pub mod bank;
pub mod bits;
pub mod cbin;
pub mod components;
pub mod crc;
pub mod error;
pub mod fields;
pub mod formats;
pub mod layout;
pub mod types;
pub mod util;
pub mod wav;
use crate::cbin::{Cbin, RawBody};
use crate::formats::{
cn3, midi, nc2, nc2d, nd2, nd3, ne3, ne4, ne5, ne6, ne7, ng2, nl4, nla1, no3, np, np2, np3,
np4, np5, npip, npno, ns2, ns3, ns4, nsclassic, nsmp, nsmpproj, nw, nw2, sysex,
};
use std::fs::File;
use std::io::{BufReader, Read, Seek};
use std::path::Path;
use util::{peek, FileType};
use crate::error::{Error, ParseError};
#[cfg(feature = "bundle")]
#[derive(Debug)]
pub enum Bundle {
Drum2Bank(nd2::bank::Bank),
Drum3KitBank(nd3::kit_bank::KitBank),
Electro5(ne5::Bundle),
Members(Vec<(String, Cbin<RawBody>)>),
}
#[allow(clippy::large_enum_variant)]
#[derive(Debug)]
pub enum Program {
C2(Cbin<RawBody>),
C2D(Cbin<RawBody>),
Drum2(Cbin<RawBody>),
Drum3(Cbin<RawBody>),
Electro3(Cbin<RawBody>),
Electro4(Cbin<RawBody>),
Electro5(Cbin<ne5::Program>),
Electro6(Cbin<RawBody>),
Electro7(Cbin<RawBody>),
Grand(Cbin<RawBody>),
Lead4(Cbin<RawBody>),
LeadA1(Cbin<RawBody>),
Organ3(Cbin<RawBody>),
Piano1(Cbin<RawBody>),
Piano2(Cbin<RawBody>),
Piano3(Cbin<RawBody>),
Piano4(Cbin<RawBody>),
Piano5(Cbin<RawBody>),
Stage2(Cbin<ns2::Program>),
Stage3(Cbin<ns3::Program>),
Stage4(Cbin<ns4::Program>),
StageClassic(Cbin<RawBody>),
Wave(Cbin<RawBody>),
Wave2(Cbin<RawBody>),
}
#[allow(clippy::large_enum_variant)]
#[derive(Debug)]
pub enum Live {
Electro4(Cbin<RawBody>),
Electro5(Cbin<ne5::Program>),
Electro6(Cbin<RawBody>),
Electro7(Cbin<RawBody>),
Grand(Cbin<RawBody>),
Piano1(Cbin<RawBody>),
Piano2(Cbin<RawBody>),
Piano3(Cbin<RawBody>),
Piano4(Cbin<RawBody>),
Piano5(Cbin<RawBody>),
Stage2(Cbin<ns2::Program>),
Stage3(Cbin<ns3::Program>),
Stage4(Cbin<ns4::Program>),
Wave2(Cbin<RawBody>),
}
#[derive(Debug)]
pub enum Song {
Electro5(Cbin<ne5::Song>),
Stage3(Cbin<RawBody>),
}
#[derive(Debug)]
pub enum Settings {
C2(Cbin<RawBody>),
C2D(Cbin<RawBody>),
Electro4(Cbin<RawBody>),
Electro5(Cbin<ne5::Settings>),
Electro6(Cbin<RawBody>),
Electro7(Cbin<RawBody>),
Grand(Cbin<RawBody>),
Lead4(Cbin<RawBody>),
LeadA1(Cbin<RawBody>),
Organ3(Cbin<RawBody>),
Piano1(Cbin<RawBody>),
Piano2(Cbin<RawBody>),
Piano3(Cbin<RawBody>),
Piano4(Cbin<RawBody>),
Piano5(Cbin<RawBody>),
Stage2(Cbin<RawBody>),
Stage3(Cbin<RawBody>),
Stage4(Cbin<RawBody>),
Wave(Cbin<RawBody>),
Wave2(Cbin<RawBody>),
}
#[allow(clippy::large_enum_variant)]
#[derive(Debug)]
pub enum Synth {
Stage2(Cbin<RawBody>),
Stage3(Cbin<ns3::SynthPreset>),
Stage4(Cbin<ns4::synth::SynthPreset>),
StageClassic(Cbin<RawBody>),
}
#[derive(Debug)]
pub enum Performance {
Lead4(Cbin<RawBody>),
LeadA1(Cbin<RawBody>),
}
#[allow(clippy::large_enum_variant)]
#[derive(Debug)]
pub enum OrganPreset {
Electro3(Cbin<RawBody>),
Stage4(Cbin<ns4::organ_preset::OrganPreset>),
}
#[derive(Debug)]
pub enum PianoPreset {
Stage4(Cbin<ns4::piano_preset::PianoPreset>),
}
#[derive(Debug)]
pub enum Sample {
V2(Cbin<nsmp::Sample>),
V3(Cbin<nsmp::SampleV3>),
}
impl Sample {
pub fn layout(&self) -> nsmp::codec::Layout {
match self {
Sample::V2(_) => nsmp::codec::Layout::V2,
Sample::V3(s) => nsmp::codec::Layout::from_version(s.header.version),
}
}
pub fn generation(&self) -> &'static str {
match self {
Sample::V2(_) => "v2",
Sample::V3(s) if s.header.version >= 400 => "v4",
Sample::V3(_) => "v3",
}
}
pub fn zones(&self) -> Result<Vec<nsmp::ZoneAudio<'_>>, Error> {
match self {
Sample::V2(s) => {
let zones = s.zones()?;
let strokes = s.strokes()?;
zones
.iter()
.zip(&strokes)
.enumerate()
.map(|(i, (zone, stroke))| {
let (at, stream) = s.zone_stream(i)?;
Ok(nsmp::ZoneAudio {
root_key: stroke.root_key,
top_note: zone.top_note,
at,
stream,
})
})
.collect()
}
Sample::V3(s) => {
let zones = s.zones()?;
zones
.iter()
.enumerate()
.map(|(i, zone)| {
let (at, stream) = s.zone_stream(i)?;
Ok(nsmp::ZoneAudio {
root_key: zone.root_key,
top_note: zone.top_note,
at,
stream,
})
})
.collect()
}
}
}
pub fn stroke_streams(&self) -> Vec<(usize, &[u8])> {
match self {
Sample::V2(s) => s.stroke_streams(),
Sample::V3(s) => s.stroke_streams(),
}
}
}
#[allow(clippy::large_enum_variant)]
#[derive(Debug)]
pub enum Entity {
Cne3(cn3::Cne3),
Live(Live),
Midi(midi::Midi),
OrganPreset(OrganPreset),
Piano(npno::Piano),
PianoLibrary(Cbin<RawBody>),
PianoPreset(PianoPreset),
PipeLibrary(Cbin<RawBody>),
Performance(Performance),
Program(Program),
Sample(Sample),
SampleProject(nsmpproj::Project),
Settings(Settings),
Song(Song),
Synth(Synth),
Sysex(sysex::Sysex),
#[cfg(feature = "bundle")]
Bundle(Bundle),
}
pub fn from_stream(reader: &mut (impl Read + Seek + Sized)) -> Result<Entity, Error> {
let header = peek(reader)?;
match header.file_type {
#[cfg(feature = "bundle")]
FileType::Zip => read_zip(reader),
#[cfg(not(feature = "bundle"))]
FileType::Zip => {
Err(ParseError::UnknownFileType("zip (bundle feature disabled)".to_string()).into())
}
FileType::Sysex => Ok(Entity::Sysex(sysex::Sysex::read_from(reader)?)),
FileType::Midi => Ok(Entity::Midi(midi::Midi::read_from(reader)?)),
FileType::Cne3 => Ok(Entity::Cne3(cn3::Cne3::read_from(reader)?)),
FileType::SampleProject => Ok(Entity::SampleProject(nsmpproj::Project::read_from(reader)?)),
FileType::Cbin => read_cbin(reader, header.format.as_str()),
e => Err(ParseError::UnknownFileType(e.as_str().to_string()).into()),
}
}
fn read_cbin(reader: &mut (impl Read + Seek), tag: &str) -> Result<Entity, Error> {
use Entity as E;
Ok(match tag {
nsmp::FORMAT => {
let file: Cbin<nsmp::AnyBody> = cbin::read(reader, nsmp::FORMAT)?;
let header = file.header;
E::Sample(match file.body {
nsmp::AnyBody::V2(body) => Sample::V2(Cbin { header, body }),
nsmp::AnyBody::V3(body) => Sample::V3(Cbin { header, body }),
})
}
npno::FORMAT => E::Piano(npno::Piano::read_from(reader)?),
npip::pipe_library::FORMAT => E::PipeLibrary(npip::pipe_library::read_from(reader)?),
nsclassic::piano_library::FORMAT => {
E::PianoLibrary(nsclassic::piano_library::read_from(reader)?)
}
ne3::program::FORMAT => E::Program(Program::Electro3(ne3::program::read_from(reader)?)),
ne3::organ_preset::FORMAT => {
E::OrganPreset(OrganPreset::Electro3(ne3::organ_preset::read_from(reader)?))
}
ne4::program::FORMAT => E::Program(Program::Electro4(ne4::program::read_from(reader)?)),
ne4::live::FORMAT => E::Live(Live::Electro4(ne4::live::read_from(reader)?)),
ne4::settings::FORMAT => E::Settings(Settings::Electro4(ne4::settings::read_from(reader)?)),
ne5::program::FORMAT => E::Program(Program::Electro5(ne5::program::read_from(reader)?)),
ne5::live::FORMAT => E::Live(Live::Electro5(ne5::live::read_from(reader)?)),
ne5::song::FORMAT => E::Song(Song::Electro5(ne5::song::read_from(reader)?)),
ne5::settings::FORMAT => E::Settings(Settings::Electro5(ne5::settings::read_from(reader)?)),
ne6::program::FORMAT => E::Program(Program::Electro6(ne6::program::read_from(reader)?)),
ne6::live::FORMAT => E::Live(Live::Electro6(ne6::live::read_from(reader)?)),
ne6::settings::FORMAT => E::Settings(Settings::Electro6(ne6::settings::read_from(reader)?)),
ne7::program::FORMAT => E::Program(Program::Electro7(ne7::program::read_from(reader)?)),
ne7::live::FORMAT => E::Live(Live::Electro7(ne7::live::read_from(reader)?)),
ne7::settings::FORMAT => E::Settings(Settings::Electro7(ne7::settings::read_from(reader)?)),
nsclassic::program::FORMAT => E::Program(Program::StageClassic(
nsclassic::program::read_from(reader)?,
)),
nsclassic::synth::FORMAT => {
E::Synth(Synth::StageClassic(nsclassic::synth::read_from(reader)?))
}
ns2::program::FORMAT => E::Program(Program::Stage2(ns2::program::read_from(reader)?)),
ns2::live::FORMAT => E::Live(Live::Stage2(ns2::live::read_from(reader)?)),
ns2::synth::FORMAT => E::Synth(Synth::Stage2(ns2::synth::read_from(reader)?)),
ns2::settings::FORMAT => E::Settings(Settings::Stage2(ns2::settings::read_from(reader)?)),
ns3::program::FORMAT => E::Program(Program::Stage3(ns3::program::read_from(reader)?)),
ns3::live::FORMAT => E::Live(Live::Stage3(ns3::live::read_from(reader)?)),
ns3::song::FORMAT => E::Song(Song::Stage3(ns3::song::read_from(reader)?)),
ns3::synth::FORMAT => E::Synth(Synth::Stage3(ns3::synth::read_from(reader)?)),
ns3::settings::FORMAT => E::Settings(Settings::Stage3(ns3::settings::read_from(reader)?)),
ns4::program::FORMAT => E::Program(Program::Stage4(ns4::program::read_from(reader)?)),
ns4::live::FORMAT => E::Live(Live::Stage4(ns4::live::read_from(reader)?)),
ns4::synth::FORMAT => E::Synth(Synth::Stage4(ns4::synth::read_from(reader)?)),
ns4::piano_preset::FORMAT => {
E::PianoPreset(PianoPreset::Stage4(ns4::piano_preset::read_from(reader)?))
}
ns4::organ_preset::FORMAT => {
E::OrganPreset(OrganPreset::Stage4(ns4::organ_preset::read_from(reader)?))
}
ns4::settings::FORMAT => E::Settings(Settings::Stage4(ns4::settings::read_from(reader)?)),
np::program::FORMAT => E::Program(Program::Piano1(np::program::read_from(reader)?)),
np::live::FORMAT => E::Live(Live::Piano1(np::live::read_from(reader)?)),
np::settings::FORMAT => E::Settings(Settings::Piano1(np::settings::read_from(reader)?)),
np2::program::FORMAT => E::Program(Program::Piano2(np2::program::read_from(reader)?)),
np2::live::FORMAT => E::Live(Live::Piano2(np2::live::read_from(reader)?)),
np2::settings::FORMAT => E::Settings(Settings::Piano2(np2::settings::read_from(reader)?)),
np3::program::FORMAT => E::Program(Program::Piano3(np3::program::read_from(reader)?)),
np3::live::FORMAT => E::Live(Live::Piano3(np3::live::read_from(reader)?)),
np3::settings::FORMAT => E::Settings(Settings::Piano3(np3::settings::read_from(reader)?)),
np4::program::FORMAT => E::Program(Program::Piano4(np4::program::read_from(reader)?)),
np4::live::FORMAT => E::Live(Live::Piano4(np4::live::read_from(reader)?)),
np4::settings::FORMAT => E::Settings(Settings::Piano4(np4::settings::read_from(reader)?)),
np5::program::FORMAT => E::Program(Program::Piano5(np5::program::read_from(reader)?)),
np5::live::FORMAT => E::Live(Live::Piano5(np5::live::read_from(reader)?)),
np5::settings::FORMAT => E::Settings(Settings::Piano5(np5::settings::read_from(reader)?)),
ng2::program::FORMAT => E::Program(Program::Grand(ng2::program::read_from(reader)?)),
ng2::live::FORMAT => E::Live(Live::Grand(ng2::live::read_from(reader)?)),
ng2::settings::FORMAT => E::Settings(Settings::Grand(ng2::settings::read_from(reader)?)),
nw::program::FORMAT => E::Program(Program::Wave(nw::program::read_from(reader)?)),
nw::settings::FORMAT => E::Settings(Settings::Wave(nw::settings::read_from(reader)?)),
nw2::program::FORMAT => E::Program(Program::Wave2(nw2::program::read_from(reader)?)),
nw2::live::FORMAT => E::Live(Live::Wave2(nw2::live::read_from(reader)?)),
nw2::settings::FORMAT => E::Settings(Settings::Wave2(nw2::settings::read_from(reader)?)),
nc2::program::FORMAT => E::Program(Program::C2(nc2::program::read_from(reader)?)),
nc2::settings::FORMAT => E::Settings(Settings::C2(nc2::settings::read_from(reader)?)),
nc2d::program::FORMAT => E::Program(Program::C2D(nc2d::program::read_from(reader)?)),
nc2d::settings::FORMAT => E::Settings(Settings::C2D(nc2d::settings::read_from(reader)?)),
no3::program::FORMAT => E::Program(Program::Organ3(no3::program::read_from(reader)?)),
no3::settings::FORMAT => E::Settings(Settings::Organ3(no3::settings::read_from(reader)?)),
nl4::program::FORMAT => E::Program(Program::Lead4(nl4::program::read_from(reader)?)),
nl4::performance::FORMAT => {
E::Performance(Performance::Lead4(nl4::performance::read_from(reader)?))
}
nl4::settings::FORMAT => E::Settings(Settings::Lead4(nl4::settings::read_from(reader)?)),
nla1::program::FORMAT => E::Program(Program::LeadA1(nla1::program::read_from(reader)?)),
nla1::performance::FORMAT => {
E::Performance(Performance::LeadA1(nla1::performance::read_from(reader)?))
}
nla1::settings::FORMAT => E::Settings(Settings::LeadA1(nla1::settings::read_from(reader)?)),
nd2::program::FORMAT => E::Program(Program::Drum2(nd2::program::read_from(reader)?)),
nd3::kit::FORMAT => E::Program(Program::Drum3(nd3::kit::read_from(reader)?)),
e => return Err(ParseError::UnknownFormat(e.to_string()).into()),
})
}
#[cfg(feature = "bundle")]
fn read_zip(reader: &mut (impl Read + Seek)) -> Result<Entity, Error> {
let start = reader.stream_position()?;
let kind = {
let zip = zip::ZipArchive::new(&mut *reader)?;
let names: Vec<&str> = zip.file_names().collect();
if names.is_empty() {
return Err(ParseError::AssertFail("the archive holds no members".into()).into());
}
if names.iter().any(|n| {
std::path::Path::new(n)
.extension()
.is_some_and(|e| e.to_string_lossy().starts_with("ne5"))
}) {
"bundle"
} else if names.iter().all(|n| n.ends_with(".nd2p")) {
"nd2"
} else if names.iter().all(|n| n.ends_with(".nd3k")) {
"nd3"
} else {
"members"
}
};
reader.seek(std::io::SeekFrom::Start(start))?;
Ok(Entity::Bundle(match kind {
"nd2" => Bundle::Drum2Bank(nd2::bank::read_from(reader)?),
"nd3" => Bundle::Drum3KitBank(nd3::kit_bank::read_from(reader)?),
"members" => Bundle::Members(formats::zip_raw_members(reader)?),
_ => Bundle::Electro5(ne5::Bundle::read_from(reader)?),
}))
}
pub fn from_path<P: AsRef<Path>>(path: P) -> Result<Entity, Error> {
from_stream(&mut BufReader::new(File::open(path)?))
}
#[cfg(test)]
mod registry_tests {
use super::*;
#[test]
fn the_entity_registry_reads_and_writes_the_body() {
let mut entity = Entity::Program(Program::Electro5(ne5::program::new(
(0, 0).try_into().unwrap(),
)));
let before = entity.registry().unwrap().fields();
assert!(before.iter().any(|f| f.path == "center_panel.transpose"));
entity
.registry_mut()
.unwrap()
.set_field("center_panel.transpose", "-5")
.unwrap();
let after = entity.registry().unwrap().fields();
let transpose = after
.iter()
.find(|f| f.path == "center_panel.transpose")
.unwrap();
assert_eq!(transpose.value, "-5");
}
#[test]
fn a_stub_has_no_registry() {
let file = Cbin {
header: cbin::Header::new("ne6p", (0, 0), 1),
body: RawBody(vec![0; 16]),
};
let mut entity = Entity::Program(Program::Electro6(file));
assert!(entity.registry().is_none());
assert!(entity.registry_mut().is_none());
}
#[test]
fn a_song_is_not_a_registry_entity() {
let song = ne5::song::new(
(0, 0).try_into().unwrap(),
ne5::song::DEFAULT_VERSION,
[(0, 0).try_into().unwrap(); 4],
);
assert!(Entity::Song(Song::Electro5(song)).registry().is_none());
}
}
#[cfg(all(test, feature = "bundle"))]
mod bundle_tests {
use super::*;
use crate::cbin::{Cbin, Header, RawBody};
use std::io::{Cursor, Write};
fn member(tag: &str) -> Vec<u8> {
let file = Cbin {
header: Header::new(tag, (0, 0), 4),
body: RawBody(vec![0x5A; 16]),
};
let mut out = Cursor::new(Vec::new());
file.write_to(&mut out).unwrap();
out.into_inner()
}
fn archive(members: &[(&str, &[u8])]) -> Vec<u8> {
let mut zip = zip::ZipWriter::new(Cursor::new(Vec::new()));
let stored = zip::write::SimpleFileOptions::default()
.compression_method(zip::CompressionMethod::Stored);
for (name, bytes) in members {
zip.start_file(name.to_string(), stored).unwrap();
zip.write_all(bytes).unwrap();
}
zip.finish().unwrap().into_inner()
}
#[test]
fn a_zip_of_mixed_cbin_members_is_a_bundle() {
let a = member("ns3f");
let b = member("ns3y");
let bytes = archive(&[("Bank A/One.ns3f", &a), ("presets/Two.ns3y", &b)]);
let entity = from_stream(&mut Cursor::new(bytes)).unwrap();
let Entity::Bundle(Bundle::Members(members)) = entity else {
panic!("decoded to something other than a member bundle");
};
assert_eq!(members.len(), 2);
assert_eq!(members[0].0, "Bank A/One.ns3f");
assert_eq!(&members[0].1.header.tag, b"ns3f");
assert_eq!(&members[1].1.header.tag, b"ns3y");
}
#[test]
fn an_empty_zip_is_refused() {
let bytes = archive(&[]);
assert!(from_stream(&mut Cursor::new(bytes)).is_err());
}
#[test]
fn a_zip_with_a_non_cbin_member_is_refused() {
let a = member("ns3f");
let bytes = archive(&[("One.ns3f", &a), ("readme.txt", b"hello")]);
assert!(from_stream(&mut Cursor::new(bytes)).is_err());
}
#[test]
fn a_zip_is_read_from_the_callers_current_position() {
let member = member("ns3f");
let bytes = archive(&[("Bank A/One.ns3f", &member)]);
let prefix_len = 7;
let mut prefixed = vec![0xa5; prefix_len];
prefixed.extend(bytes);
let mut reader = Cursor::new(prefixed);
reader.set_position(prefix_len as u64);
let entity = from_stream(&mut reader).unwrap();
assert!(matches!(entity, Entity::Bundle(Bundle::Members(_))));
}
}
pub fn to_bytes(entity: &Entity) -> Result<Vec<u8>, Error> {
use std::io::Cursor;
let mut out = Cursor::new(Vec::new());
entity.write_to(&mut out)?;
Ok(out.into_inner())
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Identity {
pub kind: &'static str,
pub format: &'static str,
}
macro_rules! registry_bodies {
($($body:ty),* $(,)?) => {$(
impl fields::Registry for Cbin<$body> {
fn fields(&self) -> Vec<fields::Field> {
self.body.fields()
}
fn field_values(&self) -> Vec<fields::FieldValue> {
self.body.field_values()
}
fn set_field(&mut self, path: &str, value: &str) -> Result<(), fields::FieldError> {
self.body.set_field(path, value)
}
}
)*};
}
registry_bodies!(
ne5::Program,
ne5::Settings,
ns2::Program,
ns3::Program,
ns3::SynthPreset,
ns4::Program,
ns4::organ_preset::OrganPreset,
ns4::piano_preset::PianoPreset,
ns4::synth::SynthPreset,
);
macro_rules! with_registry {
($entity:expr, $($reference:tt)*) => {
match $entity {
Entity::Program(Program::Electro5(f)) | Entity::Live(Live::Electro5(f)) => {
Some(f as $($reference)* dyn fields::Registry)
}
Entity::Program(Program::Stage2(f)) | Entity::Live(Live::Stage2(f)) => {
Some(f as $($reference)* dyn fields::Registry)
}
Entity::Program(Program::Stage3(f)) | Entity::Live(Live::Stage3(f)) => {
Some(f as $($reference)* dyn fields::Registry)
}
Entity::Program(Program::Stage4(f)) | Entity::Live(Live::Stage4(f)) => {
Some(f as $($reference)* dyn fields::Registry)
}
Entity::Settings(Settings::Electro5(f)) => Some(f as $($reference)* dyn fields::Registry),
Entity::Synth(Synth::Stage3(f)) => Some(f as $($reference)* dyn fields::Registry),
Entity::Synth(Synth::Stage4(f)) => Some(f as $($reference)* dyn fields::Registry),
Entity::OrganPreset(OrganPreset::Stage4(f)) => {
Some(f as $($reference)* dyn fields::Registry)
}
Entity::PianoPreset(PianoPreset::Stage4(f)) => {
Some(f as $($reference)* dyn fields::Registry)
}
_ => None,
}
};
}
impl Entity {
pub fn raw(&self) -> Option<&Cbin<RawBody>> {
use {Live as L, OrganPreset as OP, Program as P, Settings as St, Synth as Sy};
match self {
Entity::Program(
P::C2(f)
| P::C2D(f)
| P::Drum2(f)
| P::Drum3(f)
| P::Electro3(f)
| P::Electro4(f)
| P::Electro6(f)
| P::Electro7(f)
| P::Grand(f)
| P::Lead4(f)
| P::LeadA1(f)
| P::Organ3(f)
| P::Piano1(f)
| P::Piano2(f)
| P::Piano3(f)
| P::Piano4(f)
| P::Piano5(f)
| P::StageClassic(f)
| P::Wave(f)
| P::Wave2(f),
)
| Entity::Live(
L::Electro4(f)
| L::Electro6(f)
| L::Electro7(f)
| L::Grand(f)
| L::Piano1(f)
| L::Piano2(f)
| L::Piano3(f)
| L::Piano4(f)
| L::Piano5(f)
| L::Wave2(f),
)
| Entity::Settings(
St::C2(f)
| St::C2D(f)
| St::Electro4(f)
| St::Electro6(f)
| St::Electro7(f)
| St::Grand(f)
| St::Lead4(f)
| St::LeadA1(f)
| St::Organ3(f)
| St::Piano1(f)
| St::Piano2(f)
| St::Piano3(f)
| St::Piano4(f)
| St::Piano5(f)
| St::Stage2(f)
| St::Stage3(f)
| St::Stage4(f)
| St::Wave(f)
| St::Wave2(f),
)
| Entity::Song(Song::Stage3(f))
| Entity::Synth(Sy::Stage2(f) | Sy::StageClassic(f))
| Entity::Performance(Performance::Lead4(f) | Performance::LeadA1(f))
| Entity::OrganPreset(OP::Electro3(f))
| Entity::PianoLibrary(f)
| Entity::PipeLibrary(f) => Some(f),
_ => None,
}
}
pub fn registry(&self) -> Option<&dyn fields::Registry> {
with_registry!(self, &)
}
pub fn registry_mut(&mut self) -> Option<&mut dyn fields::Registry> {
with_registry!(self, &mut)
}
pub fn identity(&self) -> Identity {
use {Live as L, Program as P, Settings as St};
let id = |kind, format| Identity { kind, format };
match self {
Entity::Program(p) => match p {
P::C2(_) => id("C2 program", nc2::program::FORMAT),
P::C2D(_) => id("C2D program", nc2d::program::FORMAT),
P::Drum2(_) => id("Drum 2 program", nd2::program::FORMAT),
P::Drum3(_) => id("Drum 3P kit", nd3::kit::FORMAT),
P::Electro3(_) => id("Electro 3 program", ne3::program::FORMAT),
P::Electro4(_) => id("Electro 4 program", ne4::program::FORMAT),
P::Electro5(_) => id("Electro 5 program", ne5::program::FORMAT),
P::Electro6(_) => id("Electro 6 program", ne6::program::FORMAT),
P::Electro7(_) => id("Electro 7 program", ne7::program::FORMAT),
P::Grand(_) => id("Grand program", ng2::program::FORMAT),
P::Lead4(_) => id("Lead 4 program", nl4::program::FORMAT),
P::LeadA1(_) => id("Lead A1 program", nla1::program::FORMAT),
P::Organ3(_) => id("no3 organ program", no3::program::FORMAT),
P::Piano1(_) => id("Piano program", np::program::FORMAT),
P::Piano2(_) => id("Piano 2 program", np2::program::FORMAT),
P::Piano3(_) => id("Piano 3 program", np3::program::FORMAT),
P::Piano4(_) => id("Piano 4 program", np4::program::FORMAT),
P::Piano5(_) => id("Piano 5 program", np5::program::FORMAT),
P::Stage2(_) => id("Stage 2 program", ns2::program::FORMAT),
P::Stage3(_) => id("Stage 3 program", ns3::program::FORMAT),
P::Stage4(_) => id("Stage 4 program", ns4::program::FORMAT),
P::StageClassic(_) => id("Stage Classic program", nsclassic::program::FORMAT),
P::Wave(_) => id("Wave program", nw::program::FORMAT),
P::Wave2(_) => id("Wave 2 program", nw2::program::FORMAT),
},
Entity::Live(l) => match l {
L::Electro4(_) => id("Electro 4 live slot", ne4::live::FORMAT),
L::Electro5(_) => id("Electro 5 live slot", ne5::live::FORMAT),
L::Electro6(_) => id("Electro 6 live slot", ne6::live::FORMAT),
L::Electro7(_) => id("Electro 7 live slot", ne7::live::FORMAT),
L::Grand(_) => id("Grand live slot", ng2::live::FORMAT),
L::Piano1(_) => id("Piano live slot", np::live::FORMAT),
L::Piano2(_) => id("Piano 2 live slot", np2::live::FORMAT),
L::Piano3(_) => id("Piano 3 live slot", np3::live::FORMAT),
L::Piano4(_) => id("Piano 4 live slot", np4::live::FORMAT),
L::Piano5(_) => id("Piano 5 live slot", np5::live::FORMAT),
L::Stage2(_) => id("Stage 2 live slot", ns2::live::FORMAT),
L::Stage3(_) => id("Stage 3 live slot", ns3::live::FORMAT),
L::Stage4(_) => id("Stage 4 live slot", ns4::live::FORMAT),
L::Wave2(_) => id("Wave 2 live slot", nw2::live::FORMAT),
},
Entity::Settings(s) => match s {
St::C2(_) => id("C2 settings", nc2::settings::FORMAT),
St::C2D(_) => id("C2D settings", nc2d::settings::FORMAT),
St::Electro4(_) => id("Electro 4 settings", ne4::settings::FORMAT),
St::Electro5(_) => id("Electro 5 settings", ne5::settings::FORMAT),
St::Electro6(_) => id("Electro 6 settings", ne6::settings::FORMAT),
St::Electro7(_) => id("Electro 7 settings", ne7::settings::FORMAT),
St::Grand(_) => id("Grand settings", ng2::settings::FORMAT),
St::Lead4(_) => id("Lead 4 settings", nl4::settings::FORMAT),
St::LeadA1(_) => id("Lead A1 settings", nla1::settings::FORMAT),
St::Organ3(_) => id("no3 organ settings", no3::settings::FORMAT),
St::Piano1(_) => id("Piano settings", np::settings::FORMAT),
St::Piano2(_) => id("Piano 2 settings", np2::settings::FORMAT),
St::Piano3(_) => id("Piano 3 settings", np3::settings::FORMAT),
St::Piano4(_) => id("Piano 4 settings", np4::settings::FORMAT),
St::Piano5(_) => id("Piano 5 settings", np5::settings::FORMAT),
St::Stage2(_) => id("Stage 2 settings", ns2::settings::FORMAT),
St::Stage3(_) => id("Stage 3 settings", ns3::settings::FORMAT),
St::Stage4(_) => id("Stage 4 settings", ns4::settings::FORMAT),
St::Wave(_) => id("Wave settings", nw::settings::FORMAT),
St::Wave2(_) => id("Wave 2 settings", nw2::settings::FORMAT),
},
Entity::Song(Song::Electro5(_)) => id("Electro 5 song / set", ne5::song::FORMAT),
Entity::Song(Song::Stage3(_)) => id("Stage 3 song", ns3::song::FORMAT),
Entity::Synth(Synth::Stage2(_)) => id("Stage 2 synth patch", ns2::synth::FORMAT),
Entity::Synth(Synth::Stage3(_)) => id("Stage 3 synth patch", ns3::synth::FORMAT),
Entity::Synth(Synth::Stage4(_)) => id("Stage 4 synth preset", ns4::synth::FORMAT),
Entity::Synth(Synth::StageClassic(_)) => {
id("Stage Classic synth patch", nsclassic::synth::FORMAT)
}
Entity::Performance(Performance::Lead4(_)) => {
id("Lead 4 performance", nl4::performance::FORMAT)
}
Entity::Performance(Performance::LeadA1(_)) => {
id("Lead A1 performance", nla1::performance::FORMAT)
}
Entity::OrganPreset(OrganPreset::Electro3(_)) => {
id("Electro 3 organ preset", ne3::organ_preset::FORMAT)
}
Entity::OrganPreset(OrganPreset::Stage4(_)) => {
id("Stage 4 organ preset", ns4::organ_preset::FORMAT)
}
Entity::PianoPreset(PianoPreset::Stage4(_)) => {
id("Stage 4 piano preset", ns4::piano_preset::FORMAT)
}
Entity::Piano(_) => id("piano library", npno::FORMAT),
Entity::PianoLibrary(_) => id(
"Stage Classic piano library",
nsclassic::piano_library::FORMAT,
),
Entity::PipeLibrary(_) => id("C2 pipe library", npip::pipe_library::FORMAT),
Entity::Sample(Sample::V2(_)) => id("sample instrument", nsmp::FORMAT),
Entity::Sample(Sample::V3(_)) => id("sample instrument (nsmp3/nsmp4)", nsmp::FORMAT),
Entity::SampleProject(_) => id("Sample Editor project", nsmpproj::FORMAT),
Entity::Sysex(_) => id("SysEx dump", "syx"),
Entity::Midi(_) => id("MIDI file", "mid"),
Entity::Cne3(_) => id("Electro 2 library", "cn3"),
#[cfg(feature = "bundle")]
Entity::Bundle(_) => id("bundle", "zip"),
}
}
pub fn write_to(&self, w: &mut (impl std::io::Write + Seek)) -> Result<(), Error> {
match self {
Entity::Cne3(f) => f.write_to(w),
Entity::Live(l) => match l {
Live::Electro4(f)
| Live::Electro6(f)
| Live::Electro7(f)
| Live::Grand(f)
| Live::Piano1(f)
| Live::Piano2(f)
| Live::Piano3(f)
| Live::Piano4(f)
| Live::Piano5(f)
| Live::Wave2(f) => f.write_to(w),
Live::Electro5(f) => f.write_to(w),
Live::Stage4(f) => f.write_to(w),
Live::Stage2(f) => f.write_to(w),
Live::Stage3(f) => f.write_to(w),
},
Entity::Midi(f) => f.write_to(w),
Entity::OrganPreset(OrganPreset::Electro3(f))
| Entity::PianoLibrary(f)
| Entity::PipeLibrary(f) => f.write_to(w),
Entity::OrganPreset(OrganPreset::Stage4(f)) => f.write_to(w),
Entity::PianoPreset(PianoPreset::Stage4(f)) => f.write_to(w),
Entity::Piano(f) => f.write_to(w),
Entity::Performance(Performance::Lead4(f))
| Entity::Performance(Performance::LeadA1(f)) => f.write_to(w),
Entity::Program(p) => match p {
Program::C2(f)
| Program::C2D(f)
| Program::Drum2(f)
| Program::Drum3(f)
| Program::Electro3(f)
| Program::Electro4(f)
| Program::Electro6(f)
| Program::Electro7(f)
| Program::Grand(f)
| Program::Lead4(f)
| Program::LeadA1(f)
| Program::Organ3(f)
| Program::Piano1(f)
| Program::Piano2(f)
| Program::Piano3(f)
| Program::Piano4(f)
| Program::Piano5(f)
| Program::StageClassic(f)
| Program::Wave(f)
| Program::Wave2(f) => f.write_to(w),
Program::Electro5(f) => f.write_to(w),
Program::Stage2(f) => f.write_to(w),
Program::Stage3(f) => f.write_to(w),
Program::Stage4(f) => f.write_to(w),
},
Entity::Sample(Sample::V2(f)) => f.write_to(w),
Entity::Sample(Sample::V3(f)) => f.write_to(w),
Entity::SampleProject(f) => f.write_to(w),
Entity::Settings(s) => match s {
Settings::C2(f)
| Settings::C2D(f)
| Settings::Electro4(f)
| Settings::Electro6(f)
| Settings::Electro7(f)
| Settings::Grand(f)
| Settings::Lead4(f)
| Settings::LeadA1(f)
| Settings::Organ3(f)
| Settings::Piano1(f)
| Settings::Piano2(f)
| Settings::Piano3(f)
| Settings::Piano4(f)
| Settings::Piano5(f)
| Settings::Stage2(f)
| Settings::Stage3(f)
| Settings::Stage4(f)
| Settings::Wave(f)
| Settings::Wave2(f) => f.write_to(w),
Settings::Electro5(f) => f.write_to(w),
},
Entity::Song(Song::Electro5(f)) => f.write_to(w),
Entity::Song(Song::Stage3(f)) => f.write_to(w),
Entity::Synth(Synth::Stage2(f)) | Entity::Synth(Synth::StageClassic(f)) => {
f.write_to(w)
}
Entity::Synth(Synth::Stage3(f)) => f.write_to(w),
Entity::Synth(Synth::Stage4(f)) => f.write_to(w),
Entity::Sysex(f) => f.write_to(w),
#[cfg(feature = "bundle")]
Entity::Bundle(_) => Err(ParseError::AssertFail(
"bundles are archives; re-encoding one is not supported".into(),
)
.into()),
}
}
}