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/src/flags.rs b/main/medium/src/flags.rs index a8429727..16a5893c 100644 --- a/main/medium/src/flags.rs +++ b/main/medium/src/flags.rs @@ -110,3 +110,27 @@ pub enum SetTrackUiFlags { PreventTrackGrouping = 1, PreventSelectionGanging = 2, } + +/// Defines nudge mode in `apply_nudge` +/// +/// if not SetToValue — will nudge by value. +#[enumflags2::bitflags] +#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] +#[repr(u32)] +pub enum ApplyNudgeFlag { + SetToValue = 1, + Snap = 2, +} + +/// Defines how project is saved in `Reaper::save_project_ex()` +#[enumflags2::bitflags] +#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] +#[repr(u32)] +pub enum SaveProjectFlags { + /// Save as RTrackTemplate. + AsTrackTemplate = 1, + /// Include media in track template. + WithMedia = 2, + /// Include envelopes in track template. + WithEnvelopes = 4, +} diff --git a/main/medium/src/misc_enums.rs b/main/medium/src/misc_enums.rs index f34ad0fd..7af36de5 100644 --- a/main/medium/src/misc_enums.rs +++ b/main/medium/src/misc_enums.rs @@ -1664,3 +1664,147 @@ impl InsertMediaMode { bits as i32 } } + +/// Defines, in which units nudge will be applied. +#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] +pub enum NudgeUnits { + Milliseconds, + Seconds, + Grid, + Notes256, + Notes128, + Notes64, + Notes32, + Notes16, + Notes8, + Notes4, + Notes2, + NotesWhole, + /// (1.15 = 1 measure + 1.5 beats) + MeasuresBeats, + Samples, + Frames, + Pixels, + ItemLength, + ItemSelections, +} + +impl NudgeUnits { + /// Converts this value to an integer as expected by the low-level API. + pub fn to_raw(self) -> i32 { + match self { + Self::Milliseconds => 0, + Self::Seconds => 1, + Self::Grid => 2, + Self::Notes256 => 3, + Self::Notes128 => 4, + Self::Notes64 => 5, + Self::Notes32 => 6, + Self::Notes16 => 7, + Self::Notes8 => 8, + Self::Notes4 => 9, + Self::Notes2 => 10, + Self::NotesWhole => 15, + Self::MeasuresBeats => 16, + Self::Samples => 17, + Self::Frames => 18, + Self::Pixels => 19, + Self::ItemLength => 20, + Self::ItemSelections => 21, + } + } +} + +/// Selects string information, that can be set or retrieved from project. +#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] +pub enum ProjectInfoStringCategory { + /// Project file name (read-only, is_set will be ignored). + ProjectName, + + /// Title field from Project Settings/Notes dialog + ProjectTitle, + + /// Author field from Project Settings/Notes dialog + ProjectAuthor, + + /// Track group name, value should be 1..64 + TrackGroupName(u8), + + /// Get the GUID (unique ID) of the marker or region with given index, + /// index is the one, that passed to `enum_project_markers`, + /// not necessarily the displayed number. + MarkerGuid(u32), + + /// Recording directory -- may be blank or a relative path, + /// to get the effective path see get_project_path_ex() + RecordPath, + + /// Secondary recording directory. + RecordPathSecondary, + + /// Render directory + RenderFile, + + /// Render file name (may contain wildcards) + RenderPattern, + + /// The metadata saved with the project + /// (not metadata embedded in project media). + /// + /// # Example ID3 album name metadata: + /// + /// value=ReaperStringArg::from("ID3:TALB") to get + /// value=ReaperStringArg::from("ID3:TALB|my album name") to set. + /// + /// Returns as a semicolon-separated list of defined project metadata identifiers. + RenderMetadata, + + /// Semicolon separated list of files that would be written + /// if the project is rendered using the most recent render settings. + RenderTargets, + + /// (read-only) semicolon separated list of statistics + /// for the most recently rendered files. + /// Call with value=ReaperStringArg::from("XXX") to run an action + /// (for example, "42437"=dry run render selected items) before returning statistics. + RenderStats, + + /// base64-encoded sink configuration (see project files, etc). + /// + /// Callers can also pass a simple 4-byte string (non-base64-encoded), + /// e.g. "evaw" or "l3pm", to use default settings for that sink type. + /// + /// Generate ReaScriptAPI from the REAPER to see which formats are supported + /// and retrieve their strings. + /// + /// Typical are: "wave" "aiff" "caff" "iso " "ddp " "flac" "mp3l" "oggv" + /// "OggS" "FFMP" "WMF " "GIF " "LCF " "wvpk" + RenderFormat, + + /// See RenderFormat + RenderFormat2, +} + +impl<'a> ProjectInfoStringCategory { + /// Converts this value to an &str as expected by the low-level API. + pub fn to_raw(self) -> ReaperStringArg<'a> { + match self { + Self::ProjectName => ReaperStringArg::from("PROJECT_NAME"), + Self::ProjectTitle => ReaperStringArg::from("PROJECT_TITLE"), + Self::ProjectAuthor => ReaperStringArg::from("PROJECT_AUTHOR"), + Self::TrackGroupName(val) => { + ReaperStringArg::from(format!("TRACK_GROUP_NAME:{:?}", val)) + } + Self::MarkerGuid(val) => ReaperStringArg::from(format!("MARKER_GUID:{:?}", val)), + Self::RecordPath => ReaperStringArg::from("RECORD_PATH"), + Self::RecordPathSecondary => ReaperStringArg::from("RECORD_PATH_SECONDARY"), + Self::RenderFile => ReaperStringArg::from("RENDER_FILE"), + Self::RenderPattern => ReaperStringArg::from("RENDER_PATTERN"), + Self::RenderMetadata => ReaperStringArg::from("RENDER_METADATA"), + Self::RenderTargets => ReaperStringArg::from("RENDER_TARGETS"), + Self::RenderStats => ReaperStringArg::from("RENDER_STATS"), + Self::RenderFormat => ReaperStringArg::from("RENDER_FORMAT"), + Self::RenderFormat2 => ReaperStringArg::from("RENDER_FORMAT2"), + } + } +} 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 95c8e1b9..73858c94 100644 --- a/main/medium/src/reaper.rs +++ b/main/medium/src/reaper.rs @@ -6,27 +6,28 @@ 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, - MidiOutputDeviceId, NativeColor, NormalizedPlayRate, NotificationBehavior, OwnedPcmSource, - OwnedReaperPitchShift, OwnedReaperResample, PanMode, ParamId, PcmSource, PitchShiftMode, - PitchShiftSubMode, PlaybackSpeedFactor, PluginContext, PositionInBeats, PositionInQuarterNotes, - PositionInSeconds, Progress, ProjectContext, ProjectRef, PromptForActionResult, ReaProject, - ReaperFunctionError, ReaperFunctionResult, ReaperNormalizedFxParamValue, ReaperPanLikeValue, - ReaperPanValue, ReaperPointer, ReaperStr, ReaperString, ReaperStringArg, ReaperVersion, - ReaperVolumeValue, ReaperWidthValue, RecordArmMode, RecordingInput, RequiredViewMode, - ResampleMode, SectionContext, SectionId, SendTarget, SetTrackUiFlags, SoloMode, - StuffMidiMessageTarget, TakeAttributeKey, TimeModeOverride, TimeRangeType, TrackArea, - TrackAttributeKey, TrackDefaultsBehavior, TrackEnvelope, TrackFxChainType, TrackFxLocation, - TrackLocation, TrackMuteOperation, TrackMuteState, TrackPolarity, TrackPolarityOperation, - TrackRecArmOperation, TrackSendAttributeKey, TrackSendCategory, TrackSendDirection, - TrackSendRef, TrackSoloOperation, TransferBehavior, UiRefreshBehavior, UndoBehavior, UndoScope, - ValueChange, VolumeSliderValue, WindowContext, + require_non_null_panic, Accel, ActionValueChange, AddFxBehavior, ApplyNudgeFlag, 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, NudgeUnits, + OwnedPcmSource, OwnedReaperPitchShift, OwnedReaperResample, PanMode, ParamId, PcmSource, + PitchShiftMode, PitchShiftSubMode, PlaybackSpeedFactor, PluginContext, PositionInBeats, + PositionInQuarterNotes, PositionInSeconds, Progress, ProjectContext, ProjectInfoStringCategory, + ProjectRef, PromptForActionResult, ReaProject, ReaperFunctionError, ReaperFunctionResult, + ReaperNormalizedFxParamValue, ReaperPanLikeValue, ReaperPanValue, ReaperPointer, ReaperStr, + ReaperString, ReaperStringArg, ReaperVersion, ReaperVolumeValue, ReaperWidthValue, + RecordArmMode, RecordingInput, RequiredViewMode, ResampleMode, SectionContext, SectionId, + SendTarget, SetTrackUiFlags, SoloMode, StuffMidiMessageTarget, TakeAttributeKey, + TimeModeOverride, TimeRangeType, TrackArea, TrackAttributeKey, TrackDefaultsBehavior, + TrackEnvelope, TrackFxChainType, TrackFxLocation, TrackLocation, TrackMuteOperation, + TrackMuteState, TrackPolarity, TrackPolarityOperation, TrackRecArmOperation, + TrackSendAttributeKey, TrackSendCategory, TrackSendDirection, TrackSendRef, TrackSoloOperation, + TransferBehavior, UiRefreshBehavior, UndoBehavior, UndoScope, ValueChange, VolumeSliderValue, + WindowContext, }; use helgoboss_midi::ShortMessage; @@ -996,6 +997,54 @@ impl Reaper { unsafe { self.any_track_solo_unchecked(project) } } + /// True if function name exists in the REAPER API + pub fn api_exists(&self, function_name: ReaperStringArg) -> bool + where + UsageScope: MainThreadOnly, + { + unsafe { self.low().APIExists(function_name.as_ptr()) } + } + + /// Displays a message window if the API was successfully called. + pub fn api_test(&self) + where + UsageScope: MainThreadOnly, + { + self.low().APITest() + } + + /// Applies nudge. + /// + /// value depends on nudge_units. + /// + /// if reverse=true: in nudge mode + /// (e.g. !ApplyNudgeFlag::SetToValue) nudges left + /// + /// copies ignored if not in nudge duplicate mode. + pub unsafe fn apply_nudge( + &self, + project: ProjectContext, + nudge_flag: BitFlags, + nudge_what: i32, + nudge_units: NudgeUnits, + value: f64, + reverse: bool, + copies: i32, + ) -> bool + where + UsageScope: MainThreadOnly, + { + self.low().ApplyNudge( + project.to_raw(), + nudge_flag.bits() as i32, + nudge_what, + nudge_units.to_raw(), + value, + reverse, + copies, + ) + } + /// Like [`any_track_solo()`] but doesn't check if project is valid. /// /// # Safety @@ -2357,6 +2406,149 @@ 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, + { + 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, + { + 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.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: AnyThread, + { + 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.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.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: AnyThread, + { + 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: AnyThread, + { + 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: AnyThread, + { + 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 @@ -5558,6 +5750,75 @@ impl Reaper { NonNull::new(ptr) } + /// Set project information. + /// + /// For more information of possible values see `ProjectInfoStringCategory` + /// documentation. + /// + /// # Known bugs + /// + /// This function constantly returns false, but sometimes it works. + /// + /// # Safety + /// + /// REAPER can crash if you pass an invalid project. + pub unsafe fn get_set_project_info_string_set<'a, I: Into>>( + &self, + project: ProjectContext, + category: ProjectInfoStringCategory, + value: I, + ) -> bool + where + UsageScope: MainThreadOnly, + { + let val: ReaperStringArg = value.into(); + let val: CString = CString::from(val.as_reaper_str().as_c_str()); + let category_string = category.to_raw(); + self.low().GetSetProjectInfo_String( + project.to_raw(), + category_string.as_ptr(), + val.into_raw(), + true, + ) + } + + /// Get project information. + /// + /// For more information of possible values see `ProjectInfoStringCategory` + /// documentation. + /// + /// # Known bugs + /// + /// There are problems with several categories. At least, with + /// TrackGroupName(x), which makes definitely right string for category, + /// but doesn't work with reaper-rs. + /// + /// # Safety + /// + /// REAPER can crash if you pass an invalid project. + pub unsafe fn get_set_project_info_string_get( + &self, + project: ProjectContext, + category: ProjectInfoStringCategory, + ) -> ReaperFunctionResult + where + UsageScope: MainThreadOnly, + { + let max_size = 1024 * 10; + let (result, status) = with_string_buffer(max_size, |buf, _| -> bool { + self.low().GetSetProjectInfo_String( + project.to_raw(), + category.to_raw().as_ptr(), + buf, + false, + ) + }); + match status { + true => Ok(result.into_string()), + false => Err(ReaperFunctionError::new("Can not get project info.")), + } + } + /// Returns the take that is currently being edited in the given MIDI editor. /// /// # Safety diff --git a/test/test/Cargo.toml b/test/test/Cargo.toml index 61e8b4f1..939cd1fb 100644 --- a/test/test/Cargo.toml +++ b/test/test/Cargo.toml @@ -17,4 +17,5 @@ slog = "2.5.2" helgoboss-midi = "0.4.0" approx = "0.3.2" once_cell = "1.5.2" -crossbeam-channel = "0.5" \ No newline at end of file +crossbeam-channel = "0.5" +enumflags2 = "0.7" \ No newline at end of file diff --git a/test/test/src/tests.rs b/test/test/src/tests.rs index 7523966c..47b98515 100644 --- a/test/test/src/tests.rs +++ b/test/test/src/tests.rs @@ -122,6 +122,7 @@ pub fn create_test_steps() -> impl Iterator { main_section_functions(), register_and_unregister_action(), register_and_unregister_toggle_action(), + project_info_string(), ] .into_iter(); let steps_b = vec![ @@ -272,6 +273,46 @@ fn mark_project_as_dirty() -> TestStep { }) } +fn project_info_string() -> TestStep { + step(AllVersions, "Test Project info string", |rpr, _| { + // Given + let project = rpr.current_project(); + let medium = rpr.medium_reaper(); + + unsafe { + medium.get_set_project_info_string_set( + project.context(), + reaper_medium::ProjectInfoStringCategory::RenderFile, + "reaper-rs", + ); + let result = medium.get_set_project_info_string_get( + project.context(), + reaper_medium::ProjectInfoStringCategory::RenderFile, + ); + assert_eq!(result.unwrap(), "reaper-rs"); + // assert_eq!( + // reaper_medium::ProjectInfoStringCategory::TrackGroupName(2) + // .to_raw() + // .into_inner() + // .to_string(), + // String::from("TRACK_GROUP_NAME:2") + // ); + // medium.get_set_project_info_string_set( + // project.context(), + // reaper_medium::ProjectInfoStringCategory::TrackGroupName(2), + // "My group name", + // )?; + // let result = medium.get_set_project_info_string_get( + // project.context(), + // reaper_medium::ProjectInfoStringCategory::TrackGroupName(2), + // ); + // assert_eq!(result.unwrap(), "My group name"); + } + + Ok(()) + }) +} + fn get_reaper_window() -> TestStep { step(AllVersions, "Get REAPER window", |_session, _| { // Given