diff --git a/main/low/src/raw.rs b/main/low/src/raw.rs index 5ae4ed69..b509b0ff 100644 --- a/main/low/src/raw.rs +++ b/main/low/src/raw.rs @@ -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 diff --git a/main/medium/CHANGELOG.md b/main/medium/CHANGELOG.md index dbdb66e3..5bcfe01b 100644 --- a/main/medium/CHANGELOG.md +++ b/main/medium/CHANGELOG.md @@ -16,4 +16,4 @@ ## 0.1.0 - 2020-05-07 -* Initial release \ No newline at end of file +* Initial release diff --git a/main/medium/src/ptr_wrappers.rs b/main/medium/src/ptr_wrappers.rs index eaf1160b..24f4e4f9 100644 --- a/main/medium/src/ptr_wrappers.rs +++ b/main/medium/src/ptr_wrappers.rs @@ -91,6 +91,8 @@ pub type MediaItem = NonNull; pub type MediaItemTake = NonNull; /// Pointer to an envelope on a track. pub type TrackEnvelope = NonNull; +/// Pointer to an audio accessor on track or take. +pub type AudioAccessor = NonNull; /// Pointer to a window (window handle). pub type Hwnd = NonNull; /// Pointer to a module/instance (module/instance handle). diff --git a/main/medium/src/reaper.rs b/main/medium/src/reaper.rs index 58a85191..3eb78df5 100644 --- a/main/medium/src/reaper.rs +++ b/main/medium/src/reaper.rs @@ -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, @@ -2405,6 +2405,158 @@ impl Reaper { 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, + ) -> ReaperFunctionResult + 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 diff --git a/main/medium/src/reaper_pointer.rs b/main/medium/src/reaper_pointer.rs index 00abdcfc..d3139678 100644 --- a/main/medium/src/reaper_pointer.rs +++ b/main/medium/src/reaper_pointer.rs @@ -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; @@ -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. @@ -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, @@ -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, } } @@ -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); diff --git a/test/test-extension-plugin/tests/reaper_integration_test.rs b/test/test-extension-plugin/tests/reaper_integration_test.rs index 11207c28..80735289 100644 --- a/test/test-extension-plugin/tests/reaper_integration_test.rs +++ b/test/test-extension-plugin/tests/reaper_integration_test.rs @@ -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 {