Skip to main content

mlua_pulse/
files.rs

1use crate::composition::{PulseMidiClip, PulseSong};
2use crate::error::{PulseError, PulseResult};
3use crate::export::{
4    apply_midi_export_options, export_mixer_flac_with_options, export_mixer_wav_with_options,
5    export_wav_with_options, path_to_string, prepare_output_path, ExportOptions,
6};
7use std::path::Path;
8use tunes::track::Mixer;
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11enum ExportFormat {
12    Wav,
13    Flac,
14    Midi,
15}
16
17/// Returns the file extensions supported by `export`.
18pub fn export_formats() -> &'static [&'static str] {
19    &["wav", "flac", "mid", "midi"]
20}
21
22/// A MIDI file imported into a `tunes` mixer.
23#[derive(Debug, Clone)]
24pub struct PulseImportedMidi {
25    mixer: Mixer,
26}
27
28impl PulseImportedMidi {
29    /// Creates an imported MIDI wrapper from a `tunes` mixer.
30    pub fn new(mixer: Mixer) -> Self {
31        Self { mixer }
32    }
33
34    pub(crate) fn cloned_mixer(&self) -> Mixer {
35        self.mixer.clone()
36    }
37
38    /// Returns this imported MIDI file as an arrangeable song or phrase clip.
39    pub fn as_clip(&self) -> PulseMidiClip {
40        PulseMidiClip::new(self.mixer.clone())
41    }
42
43    /// Exports the imported MIDI mixer based on the output file extension.
44    pub fn export(&self, path: impl AsRef<Path>) -> PulseResult<()> {
45        self.export_with_options(path, ExportOptions::new())
46    }
47
48    /// Exports the imported MIDI mixer based on the output file extension with options.
49    pub fn export_with_options(
50        &self,
51        path: impl AsRef<Path>,
52        options: ExportOptions,
53    ) -> PulseResult<()> {
54        match export_format(path.as_ref())? {
55            ExportFormat::Wav => self.export_wav_with_options(path, options),
56            ExportFormat::Flac => self.export_flac_with_options(path, options),
57            ExportFormat::Midi => self.export_midi_with_options(path, options),
58        }
59    }
60
61    /// Exports the imported MIDI mixer to WAV.
62    pub fn export_wav(&self, path: impl AsRef<Path>) -> PulseResult<()> {
63        self.export_wav_with_options(path, ExportOptions::new())
64    }
65
66    /// Exports the imported MIDI mixer to WAV with explicit export options.
67    pub fn export_wav_with_options(
68        &self,
69        path: impl AsRef<Path>,
70        options: ExportOptions,
71    ) -> PulseResult<()> {
72        let mut mixer = self.mixer.clone();
73        export_mixer_wav_with_options(&mut mixer, path, options)
74    }
75
76    /// Exports the imported MIDI mixer to FLAC.
77    pub fn export_flac(&self, path: impl AsRef<Path>) -> PulseResult<()> {
78        self.export_flac_with_options(path, ExportOptions::new())
79    }
80
81    /// Exports the imported MIDI mixer to FLAC with explicit export options.
82    pub fn export_flac_with_options(
83        &self,
84        path: impl AsRef<Path>,
85        options: ExportOptions,
86    ) -> PulseResult<()> {
87        let mut mixer = self.mixer.clone();
88        export_mixer_flac_with_options(&mut mixer, path, options)
89    }
90
91    /// Exports the imported MIDI mixer to a Standard MIDI File.
92    pub fn export_midi(&self, path: impl AsRef<Path>) -> PulseResult<()> {
93        self.export_midi_with_options(path, ExportOptions::new())
94    }
95
96    /// Exports the imported MIDI mixer to a Standard MIDI File with explicit export options.
97    pub fn export_midi_with_options(
98        &self,
99        path: impl AsRef<Path>,
100        options: ExportOptions,
101    ) -> PulseResult<()> {
102        let path_text = prepare_output_path(path.as_ref())?;
103        let mut mixer = self.mixer.clone();
104        apply_midi_export_options(&mut mixer, options);
105        mixer
106            .export_midi(&path_text)
107            .map_err(|error| PulseError::MidiExportFailed {
108                message: error.to_string(),
109            })
110    }
111}
112
113/// Exports a song to FLAC through `tunes::engine::AudioEngine::export_flac`.
114pub fn export_flac(song: &PulseSong, path: impl AsRef<Path>) -> PulseResult<()> {
115    export_flac_with_options(song, path, ExportOptions::new())
116}
117
118/// Exports a song to FLAC through `tunes::engine::AudioEngine::export_flac` with options.
119pub fn export_flac_with_options(
120    song: &PulseSong,
121    path: impl AsRef<Path>,
122    options: ExportOptions,
123) -> PulseResult<()> {
124    let mut mixer = song.to_mixer()?;
125    export_mixer_flac_with_options(&mut mixer, path, options)
126}
127
128/// Exports a song to Standard MIDI File through `tunes::track::Mixer::export_midi`.
129pub fn export_midi(song: &PulseSong, path: impl AsRef<Path>) -> PulseResult<()> {
130    export_midi_with_options(song, path, ExportOptions::new())
131}
132
133/// Exports a song to Standard MIDI File with explicit export options.
134pub fn export_midi_with_options(
135    song: &PulseSong,
136    path: impl AsRef<Path>,
137    options: ExportOptions,
138) -> PulseResult<()> {
139    let path_text = prepare_output_path(path.as_ref())?;
140    let mut mixer = song.to_arranged_mixer()?;
141    apply_midi_export_options(&mut mixer, options);
142    mixer
143        .export_midi(&path_text)
144        .map_err(|error| PulseError::MidiExportFailed {
145            message: error.to_string(),
146        })
147}
148
149/// Imports a Standard MIDI File through `tunes::track::Mixer::import_midi`.
150pub fn import_midi(path: impl AsRef<Path>) -> PulseResult<PulseImportedMidi> {
151    let path_text = path_to_string(path.as_ref())?;
152    let mixer = Mixer::import_midi(&path_text).map_err(|error| PulseError::MidiImportFailed {
153        message: error.to_string(),
154    })?;
155    Ok(PulseImportedMidi::new(mixer))
156}
157
158/// Exports a song based on the output file extension.
159pub fn export(song: &PulseSong, path: impl AsRef<Path>) -> PulseResult<()> {
160    export_with_options(song, path, ExportOptions::new())
161}
162
163/// Exports a song based on the output file extension with explicit export options.
164pub fn export_with_options(
165    song: &PulseSong,
166    path: impl AsRef<Path>,
167    options: ExportOptions,
168) -> PulseResult<()> {
169    match export_format(path.as_ref())? {
170        ExportFormat::Wav => export_wav_with_options(song, path, options),
171        ExportFormat::Flac => export_flac_with_options(song, path, options),
172        ExportFormat::Midi => export_midi_with_options(song, path, options),
173    }
174}
175
176fn export_format(path: &Path) -> PulseResult<ExportFormat> {
177    let Some(extension) = path.extension().and_then(|value| value.to_str()) else {
178        return Err(PulseError::UnsupportedExportFormat {
179            format: "missing".to_string(),
180        });
181    };
182
183    match extension.to_ascii_lowercase().as_str() {
184        "wav" => Ok(ExportFormat::Wav),
185        "flac" => Ok(ExportFormat::Flac),
186        "mid" | "midi" => Ok(ExportFormat::Midi),
187        format => Err(PulseError::UnsupportedExportFormat {
188            format: format.to_string(),
189        }),
190    }
191}