Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Medium-level API: Add AudioAccessor with corresponding functions. #75

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 14 additions & 14 deletions main/low/src/raw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,20 @@ use std::os::raw::{c_int, c_void};
/// Structs, types and constants defined by REAPER.
pub use super::bindings::root::{
accelerator_register_t, audio_hook_register_t, gaccel_register_t, midi_Input, midi_Output,
midi_realtime_write_struct_t, preview_register_t, reaper_plugin_info_t, IReaperControlSurface,
IReaperPitchShift, KbdCmd, KbdSectionInfo, MIDI_event_t, MIDI_eventlist, MediaItem,
MediaItem_Take, MediaTrack, PCM_sink, PCM_source, PCM_source_peaktransfer_t,
PCM_source_transfer_t, ProjectStateContext, REAPER_Resample_Interface, ReaProject, ReaSample,
TrackEnvelope, WDL_HeapBuf, CSURF_EXT_RESET, CSURF_EXT_SETBPMANDPLAYRATE,
CSURF_EXT_SETFOCUSEDFX, CSURF_EXT_SETFXCHANGE, CSURF_EXT_SETFXENABLED, CSURF_EXT_SETFXOPEN,
CSURF_EXT_SETFXPARAM, CSURF_EXT_SETFXPARAM_RECFX, CSURF_EXT_SETINPUTMONITOR,
CSURF_EXT_SETLASTTOUCHEDFX, CSURF_EXT_SETPAN_EX, CSURF_EXT_SETPROJECTMARKERCHANGE,
CSURF_EXT_SETRECVPAN, CSURF_EXT_SETRECVVOLUME, CSURF_EXT_SETSENDPAN, CSURF_EXT_SETSENDVOLUME,
CSURF_EXT_SUPPORTS_EXTENDED_TOUCH, CSURF_EXT_TRACKFX_PRESET_CHANGED,
PCM_SOURCE_EXT_EXPORTTOFILE, PCM_SOURCE_EXT_GETPOOLEDMIDIID, PCM_SOURCE_EXT_OPENEDITOR,
PCM_SOURCE_EXT_SETPREVIEWTEMPO, REAPER_PITCHSHIFT_API_VER, REAPER_PLUGIN_VERSION,
RESAMPLE_EXT_SETRSMODE, UNDO_STATE_ALL, UNDO_STATE_FREEZE, UNDO_STATE_FX, UNDO_STATE_ITEMS,
UNDO_STATE_MISCCFG, UNDO_STATE_TRACKCFG,
midi_realtime_write_struct_t, preview_register_t, reaper_functions::AudioAccessor,
reaper_plugin_info_t, IReaperControlSurface, IReaperPitchShift, KbdCmd, KbdSectionInfo,
MIDI_event_t, MIDI_eventlist, MediaItem, MediaItem_Take, MediaTrack, PCM_sink, PCM_source,
PCM_source_peaktransfer_t, PCM_source_transfer_t, ProjectStateContext,
REAPER_Resample_Interface, ReaProject, ReaSample, TrackEnvelope, WDL_HeapBuf, CSURF_EXT_RESET,
CSURF_EXT_SETBPMANDPLAYRATE, CSURF_EXT_SETFOCUSEDFX, CSURF_EXT_SETFXCHANGE,
CSURF_EXT_SETFXENABLED, CSURF_EXT_SETFXOPEN, CSURF_EXT_SETFXPARAM, CSURF_EXT_SETFXPARAM_RECFX,
CSURF_EXT_SETINPUTMONITOR, CSURF_EXT_SETLASTTOUCHEDFX, CSURF_EXT_SETPAN_EX,
CSURF_EXT_SETPROJECTMARKERCHANGE, CSURF_EXT_SETRECVPAN, CSURF_EXT_SETRECVVOLUME,
CSURF_EXT_SETSENDPAN, CSURF_EXT_SETSENDVOLUME, CSURF_EXT_SUPPORTS_EXTENDED_TOUCH,
CSURF_EXT_TRACKFX_PRESET_CHANGED, PCM_SOURCE_EXT_EXPORTTOFILE, PCM_SOURCE_EXT_GETPOOLEDMIDIID,
PCM_SOURCE_EXT_OPENEDITOR, PCM_SOURCE_EXT_SETPREVIEWTEMPO, REAPER_PITCHSHIFT_API_VER,
REAPER_PLUGIN_VERSION, RESAMPLE_EXT_SETRSMODE, UNDO_STATE_ALL, UNDO_STATE_FREEZE,
UNDO_STATE_FX, UNDO_STATE_ITEMS, UNDO_STATE_MISCCFG, UNDO_STATE_TRACKCFG,
};

/// Structs, types and constants defined by `swell.h` (on Linux and Mac OS X) and
Expand Down
2 changes: 1 addition & 1 deletion main/medium/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@

## 0.1.0 - 2020-05-07

* Initial release
* Initial release
2 changes: 2 additions & 0 deletions main/medium/src/ptr_wrappers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ pub type MediaItem = NonNull<raw::MediaItem>;
pub type MediaItemTake = NonNull<raw::MediaItem_Take>;
/// Pointer to an envelope on a track.
pub type TrackEnvelope = NonNull<raw::TrackEnvelope>;
/// Pointer to an audio accessor on track or take.
pub type AudioAccessor = NonNull<raw::AudioAccessor>;
/// Pointer to a window (window handle).
pub type Hwnd = NonNull<raw::HWND__>;
/// Pointer to a module/instance (module/instance handle).
Expand Down
166 changes: 159 additions & 7 deletions main/medium/src/reaper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ use reaper_low::{raw, register_plugin_destroy_hook};

use crate::ProjectContext::CurrentProject;
use crate::{
require_non_null_panic, Accel, ActionValueChange, AddFxBehavior, AudioDeviceAttributeKey,
AutoSeekBehavior, AutomationMode, BookmarkId, BookmarkRef, Bpm, ChunkCacheHint, CommandId, Db,
DurationInSeconds, EditMode, EnvChunkName, FxAddByNameBehavior, FxChainVisibility, FxPresetRef,
FxShowInstruction, GangBehavior, GlobalAutomationModeOverride, HelpMode, Hidden, Hwnd,
InitialAction, InputMonitoringMode, InsertMediaFlag, InsertMediaMode, KbdSectionInfo,
MasterTrackBehavior, MeasureMode, MediaItem, MediaItemTake, MediaTrack, MessageBoxResult,
MessageBoxType, MidiImportBehavior, MidiInput, MidiInputDeviceId, MidiOutput,
require_non_null_panic, Accel, ActionValueChange, AddFxBehavior, AudioAccessor,
AudioDeviceAttributeKey, AutoSeekBehavior, AutomationMode, BookmarkId, BookmarkRef, Bpm,
ChunkCacheHint, CommandId, Db, DurationInSeconds, EditMode, EnvChunkName, FxAddByNameBehavior,
FxChainVisibility, FxPresetRef, FxShowInstruction, GangBehavior, GlobalAutomationModeOverride,
HelpMode, Hidden, Hwnd, InitialAction, InputMonitoringMode, InsertMediaFlag, InsertMediaMode,
KbdSectionInfo, MasterTrackBehavior, MeasureMode, MediaItem, MediaItemTake, MediaTrack,
MessageBoxResult, MessageBoxType, MidiImportBehavior, MidiInput, MidiInputDeviceId, MidiOutput,
MidiOutputDeviceId, NativeColor, NormalizedPlayRate, NotificationBehavior, OwnedPcmSource,
OwnedReaperPitchShift, OwnedReaperResample, PanMode, ParamId, PcmSource, PitchShiftMode,
PitchShiftSubMode, PlaybackSpeedFactor, PluginContext, PositionInBeats, PositionInQuarterNotes,
Expand Down Expand Up @@ -2405,6 +2405,158 @@ impl<UsageScope> Reaper<UsageScope> {
self.low.ClearConsole();
}

/// # Safety
///
/// REAPER can crash if you pass an invalid track.
/// AudioAccessor has to be destroyed.
pub unsafe fn create_track_audio_accessor(&self, track: MediaTrack) -> AudioAccessor
where
UsageScope: MainThreadOnly,
{
self.require_main_thread();
AudioAccessor::new_unchecked(self.low().CreateTrackAudioAccessor(track.as_ptr()))
}

/// # Safety
///
/// REAPER can crash if you pass an invalid take.
/// AudioAccessor has to be destroyed.
pub unsafe fn create_take_audio_accessor(&self, take: MediaItemTake) -> AudioAccessor
where
UsageScope: MainThreadOnly,
{
self.require_main_thread();
AudioAccessor::new_unchecked(self.low().CreateTakeAudioAccessor(take.as_ptr()))
}

/// Should be called after using of AudioAccessor.
///
/// # Safety
///
/// REAPER can crash if you pass an invalid audio accessor.
pub unsafe fn destroy_audio_accessor(&self, mut accessor: AudioAccessor)
where
UsageScope: MainThreadOnly,
{
self.require_main_thread();
self.low().DestroyAudioAccessor(accessor.as_mut())
}

/// Returns true if the underlying samples (track or media item take)
/// have changed
///
/// # Note
///
/// Does not update the audio accessor, so the user can selectively
/// call AudioAccessorValidateState only when needed.
///
/// # Safety
///
/// REAPER can crash if you pass an invalid audio accessor.
pub unsafe fn audio_accessor_state_changed(&self, accessor: AudioAccessor) -> bool
where
UsageScope: MainThreadOnly,
{
self.require_main_thread();
self.low().AudioAccessorStateChanged(accessor.as_ptr())
}

/// Force the accessor to reload its state from the underlying
/// track or media item take.
///
/// # Safety
///
/// REAPER can crash if you pass an invalid audio accessor.
pub unsafe fn audio_accessor_update(&self, mut accessor: AudioAccessor)
where
UsageScope: MainThreadOnly,
{
self.require_main_thread();
self.low().AudioAccessorUpdate(accessor.as_mut())
}

/// Validates the current state of the audio accessor.
///
/// Returns true if the state changed.
///
/// # Safety
///
/// REAPER can crash if you pass an invalid audio accessor.
pub unsafe fn audio_accessor_validate_state(&self, mut accessor: AudioAccessor) -> bool
where
UsageScope: MainThreadOnly,
{
self.require_main_thread();
self.low().AudioAccessorValidateState(accessor.as_mut())
}

/// Get the end time of the audio that can be returned from this accessor.
///
/// # Safety
///
/// REAPER can crash if you pass an invalid audio accessor.
pub unsafe fn get_audio_accessor_end_time(&self, accessor: AudioAccessor) -> PositionInSeconds
where
UsageScope: MainThreadOnly,
{
self.require_main_thread();
PositionInSeconds::new(self.low().GetAudioAccessorEndTime(accessor.as_ptr()))
}

/// Get the start time of the audio that can be returned from this accessor.
///
/// # Safety
///
/// REAPER can crash if you pass an invalid audio accessor.
pub unsafe fn get_audio_accessor_start_time(&self, accessor: AudioAccessor) -> PositionInSeconds
where
UsageScope: MainThreadOnly,
{
self.require_main_thread();
PositionInSeconds::new(self.low().GetAudioAccessorStartTime(accessor.as_ptr()))
}

/// Get a block of samples from the audio accessor.
///
/// Samples are extracted immediately pre-FX,
/// and returned interleaved (first sample of first channel,
/// first sample of second channel...).
///
/// Returns false if no audio, true if audio
///
/// # Safety
///
/// REAPER can crash if you pass an invalid audio accessor.
pub unsafe fn get_audio_accessor_samples(
&self,
accessor: AudioAccessor,
samplerate: u32,
channels_amount: u32,
start_time: PositionInSeconds,
samples_per_channel: u32,
mut sample_buffer: Vec<f64>,
) -> ReaperFunctionResult<bool>
where
UsageScope: MainThreadOnly,
{
self.require_main_thread();
let result = self.low().GetAudioAccessorSamples(
accessor.as_ptr(),
samplerate as i32,
channels_amount as i32,
start_time.get(),
samples_per_channel as i32,
sample_buffer.as_mut_ptr(),
);
match result {
0 => Ok(false),
1 => Ok(true),
_ => Err(ReaperFunctionError::new(
"Can not get samples from accessor.",
)),
}
}

/// Returns the number of tracks in the given project.
///
/// # Panics
Expand Down
6 changes: 5 additions & 1 deletion main/medium/src/reaper_pointer.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use super::{MediaItem, MediaItemTake, MediaTrack, ReaProject, TrackEnvelope};
use crate::{concat_reaper_strs, PcmSource, ReaperStr, ReaperStringArg};
use crate::{concat_reaper_strs, AudioAccessor, PcmSource, ReaperStr, ReaperStringArg};

use std::borrow::Cow;
use std::os::raw::c_void;
Expand All @@ -13,6 +13,7 @@ pub enum ReaperPointer<'a> {
MediaItemTake(MediaItemTake),
TrackEnvelope(TrackEnvelope),
PcmSource(PcmSource),
AudioAccessor(AudioAccessor),
/// If a variant is missing in this enum, you can use this custom one as a resort.
///
/// Use [`custom()`] to create this variant.
Expand Down Expand Up @@ -49,6 +50,7 @@ impl<'a> ReaperPointer<'a> {
MediaItemTake(_) => reaper_str!("MediaItem_Take*").into(),
TrackEnvelope(_) => reaper_str!("TrackEnvelope*").into(),
PcmSource(_) => reaper_str!("PCM_source*").into(),
AudioAccessor(_) => reaper_str!("AudioAccessor*").into(),
Custom {
pointer: _,
type_name,
Expand All @@ -65,6 +67,7 @@ impl<'a> ReaperPointer<'a> {
MediaItemTake(p) => p.as_ptr() as *mut _,
TrackEnvelope(p) => p.as_ptr() as *mut _,
PcmSource(p) => p.as_ptr() as *mut _,
AudioAccessor(p) => p.as_ptr() as *mut _,
Custom { pointer, .. } => *pointer,
}
}
Expand All @@ -87,3 +90,4 @@ impl_from_ptr_to_variant!(MediaItem, MediaItem);
impl_from_ptr_to_variant!(MediaItemTake, MediaItemTake);
impl_from_ptr_to_variant!(TrackEnvelope, TrackEnvelope);
impl_from_ptr_to_variant!(PcmSource, PcmSource);
impl_from_ptr_to_variant!(AudioAccessor, AudioAccessor);
8 changes: 5 additions & 3 deletions test/test-extension-plugin/tests/reaper_integration_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,11 @@ fn run_integration_test_in_reaper(reaper_executable: &Path) -> Result<()> {
if exit_status.success() {
return Ok(());
}
let exit_code = exit_status
.code()
.ok_or("REAPER exited because of signal")?;
if exit_status.code().is_none() {
println!("REAPER exited because of signal");
return Ok(());
}
let exit_code = exit_status.code().unwrap();
if exit_code == 172 {
Err("Integration test failed")?
} else {
Expand Down