diff --git a/main/medium/src/flags.rs b/main/medium/src/flags.rs index adbb2f86..16a5893c 100644 --- a/main/medium/src/flags.rs +++ b/main/medium/src/flags.rs @@ -112,7 +112,7 @@ pub enum SetTrackUiFlags { } /// Defines nudge mode in `apply_nudge` -/// +/// /// if not SetToValue — will nudge by value. #[enumflags2::bitflags] #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] @@ -120,4 +120,17 @@ pub enum SetTrackUiFlags { pub enum ApplyNudgeFlag { SetToValue = 1, Snap = 2, -} \ No newline at end of file +} + +/// 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 6e0edefa..7af36de5 100644 --- a/main/medium/src/misc_enums.rs +++ b/main/medium/src/misc_enums.rs @@ -1714,3 +1714,97 @@ impl NudgeUnits { } } } + +/// 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/reaper.rs b/main/medium/src/reaper.rs index 7b5b139d..57a33110 100644 --- a/main/medium/src/reaper.rs +++ b/main/medium/src/reaper.rs @@ -16,8 +16,8 @@ use crate::{ MidiOutputDeviceId, NativeColor, NormalizedPlayRate, NotificationBehavior, NudgeUnits, OwnedPcmSource, OwnedReaperPitchShift, OwnedReaperResample, PanMode, ParamId, PcmSource, PitchShiftMode, PitchShiftSubMode, PlaybackSpeedFactor, PluginContext, PositionInBeats, - PositionInQuarterNotes, PositionInSeconds, Progress, ProjectContext, ProjectRef, - PromptForActionResult, ReaProject, ReaperFunctionError, ReaperFunctionResult, + 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, @@ -5607,6 +5607,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