use crate::composition::{PulseMidiClip, PulseSong};
use crate::error::{PulseError, PulseResult};
use crate::export::{
apply_midi_export_options, export_mixer_flac_with_options, export_mixer_wav_with_options,
export_wav_with_options, path_to_string, prepare_output_path, ExportOptions,
};
use std::path::Path;
use tunes::track::Mixer;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum ExportFormat {
Wav,
Flac,
Midi,
}
pub fn export_formats() -> &'static [&'static str] {
&["wav", "flac", "mid", "midi"]
}
#[derive(Debug, Clone)]
pub struct PulseImportedMidi {
mixer: Mixer,
}
impl PulseImportedMidi {
pub fn new(mixer: Mixer) -> Self {
Self { mixer }
}
pub(crate) fn cloned_mixer(&self) -> Mixer {
self.mixer.clone()
}
pub fn as_clip(&self) -> PulseMidiClip {
PulseMidiClip::new(self.mixer.clone())
}
pub fn export(&self, path: impl AsRef<Path>) -> PulseResult<()> {
self.export_with_options(path, ExportOptions::new())
}
pub fn export_with_options(
&self,
path: impl AsRef<Path>,
options: ExportOptions,
) -> PulseResult<()> {
match export_format(path.as_ref())? {
ExportFormat::Wav => self.export_wav_with_options(path, options),
ExportFormat::Flac => self.export_flac_with_options(path, options),
ExportFormat::Midi => self.export_midi_with_options(path, options),
}
}
pub fn export_wav(&self, path: impl AsRef<Path>) -> PulseResult<()> {
self.export_wav_with_options(path, ExportOptions::new())
}
pub fn export_wav_with_options(
&self,
path: impl AsRef<Path>,
options: ExportOptions,
) -> PulseResult<()> {
let mut mixer = self.mixer.clone();
export_mixer_wav_with_options(&mut mixer, path, options)
}
pub fn export_flac(&self, path: impl AsRef<Path>) -> PulseResult<()> {
self.export_flac_with_options(path, ExportOptions::new())
}
pub fn export_flac_with_options(
&self,
path: impl AsRef<Path>,
options: ExportOptions,
) -> PulseResult<()> {
let mut mixer = self.mixer.clone();
export_mixer_flac_with_options(&mut mixer, path, options)
}
pub fn export_midi(&self, path: impl AsRef<Path>) -> PulseResult<()> {
self.export_midi_with_options(path, ExportOptions::new())
}
pub fn export_midi_with_options(
&self,
path: impl AsRef<Path>,
options: ExportOptions,
) -> PulseResult<()> {
let path_text = prepare_output_path(path.as_ref())?;
let mut mixer = self.mixer.clone();
apply_midi_export_options(&mut mixer, options);
mixer
.export_midi(&path_text)
.map_err(|error| PulseError::MidiExportFailed {
message: error.to_string(),
})
}
}
pub fn export_flac(song: &PulseSong, path: impl AsRef<Path>) -> PulseResult<()> {
export_flac_with_options(song, path, ExportOptions::new())
}
pub fn export_flac_with_options(
song: &PulseSong,
path: impl AsRef<Path>,
options: ExportOptions,
) -> PulseResult<()> {
let mut mixer = song.to_mixer()?;
export_mixer_flac_with_options(&mut mixer, path, options)
}
pub fn export_midi(song: &PulseSong, path: impl AsRef<Path>) -> PulseResult<()> {
export_midi_with_options(song, path, ExportOptions::new())
}
pub fn export_midi_with_options(
song: &PulseSong,
path: impl AsRef<Path>,
options: ExportOptions,
) -> PulseResult<()> {
let path_text = prepare_output_path(path.as_ref())?;
let mut mixer = song.to_arranged_mixer()?;
apply_midi_export_options(&mut mixer, options);
mixer
.export_midi(&path_text)
.map_err(|error| PulseError::MidiExportFailed {
message: error.to_string(),
})
}
pub fn import_midi(path: impl AsRef<Path>) -> PulseResult<PulseImportedMidi> {
let path_text = path_to_string(path.as_ref())?;
let mixer = Mixer::import_midi(&path_text).map_err(|error| PulseError::MidiImportFailed {
message: error.to_string(),
})?;
Ok(PulseImportedMidi::new(mixer))
}
pub fn export(song: &PulseSong, path: impl AsRef<Path>) -> PulseResult<()> {
export_with_options(song, path, ExportOptions::new())
}
pub fn export_with_options(
song: &PulseSong,
path: impl AsRef<Path>,
options: ExportOptions,
) -> PulseResult<()> {
match export_format(path.as_ref())? {
ExportFormat::Wav => export_wav_with_options(song, path, options),
ExportFormat::Flac => export_flac_with_options(song, path, options),
ExportFormat::Midi => export_midi_with_options(song, path, options),
}
}
fn export_format(path: &Path) -> PulseResult<ExportFormat> {
let Some(extension) = path.extension().and_then(|value| value.to_str()) else {
return Err(PulseError::UnsupportedExportFormat {
format: "missing".to_string(),
});
};
match extension.to_ascii_lowercase().as_str() {
"wav" => Ok(ExportFormat::Wav),
"flac" => Ok(ExportFormat::Flac),
"mid" | "midi" => Ok(ExportFormat::Midi),
format => Err(PulseError::UnsupportedExportFormat {
format: format.to_string(),
}),
}
}