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
17pub fn export_formats() -> &'static [&'static str] {
19 &["wav", "flac", "mid", "midi"]
20}
21
22#[derive(Debug, Clone)]
24pub struct PulseImportedMidi {
25 mixer: Mixer,
26}
27
28impl PulseImportedMidi {
29 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 pub fn as_clip(&self) -> PulseMidiClip {
40 PulseMidiClip::new(self.mixer.clone())
41 }
42
43 pub fn export(&self, path: impl AsRef<Path>) -> PulseResult<()> {
45 self.export_with_options(path, ExportOptions::new())
46 }
47
48 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 pub fn export_wav(&self, path: impl AsRef<Path>) -> PulseResult<()> {
63 self.export_wav_with_options(path, ExportOptions::new())
64 }
65
66 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 pub fn export_flac(&self, path: impl AsRef<Path>) -> PulseResult<()> {
78 self.export_flac_with_options(path, ExportOptions::new())
79 }
80
81 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 pub fn export_midi(&self, path: impl AsRef<Path>) -> PulseResult<()> {
93 self.export_midi_with_options(path, ExportOptions::new())
94 }
95
96 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
113pub fn export_flac(song: &PulseSong, path: impl AsRef<Path>) -> PulseResult<()> {
115 export_flac_with_options(song, path, ExportOptions::new())
116}
117
118pub 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
128pub fn export_midi(song: &PulseSong, path: impl AsRef<Path>) -> PulseResult<()> {
130 export_midi_with_options(song, path, ExportOptions::new())
131}
132
133pub 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
149pub 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
158pub fn export(song: &PulseSong, path: impl AsRef<Path>) -> PulseResult<()> {
160 export_with_options(song, path, ExportOptions::new())
161}
162
163pub 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}