use std::fs::File;
use std::io::{Error, prelude::*};
use std::path::{Path, PathBuf};
use std::result::Result;
use std::fmt::Debug;
use logos::Logos;
use crate::sfz::{Group, Header, Opcode, Region, SfzToken};
use crate::sfz::types::OpcodeMap;
#[derive(Debug)]
pub struct Instrument {
global: OpcodeMap, groups: Vec<Group>, regions: Vec<Region>,
default_path: PathBuf,
}
impl Instrument {
pub fn from_file(sfz_path: &Path) -> Result<Self, Error> {
let mut sfz_file = File::open(&sfz_path)?;
let mut sfz_text = String::new();
sfz_file.read_to_string(&mut sfz_text)?;
Self::from_sfz(&sfz_text, sfz_path.parent().unwrap())
}
pub fn from_sfz(sfz: &str, sfz_path: &Path) -> Result<Self, Error> {
let mut instrument = Instrument {
global: OpcodeMap::new(),
groups: Vec::<Group>::new(),
regions: Vec::<Region>::new(),
default_path: sfz_path.to_path_buf(),
};
let mut status = InstrumentParsingStatus::init();
let lex = SfzToken::lexer(&sfz);
for t in lex { match &t {
SfzToken::Header(h) => {
match h {
Header::Group => {
status.new_group();
instrument.groups.push(Group::new());
},
Header::Region => {
status.new_region();
instrument.regions.push(Region::new());
},
Header::Control => {
status.new_control();
},
Header::Global => {
status.new_global();
},
Header::Curve => println!("\n<curve>"),
Header::Effect => println!("\n<effect>"),
_ => ()
}
},
SfzToken::Opcode(o) => {
if status.is_header_global {
instrument.global.insert(o.str_name(), o.clone());
} else if status.is_header_control {
match o {
Opcode::default_path(p) => {
instrument.default_path.push(p)
},
_ => ()
}
} else {
if status.are_regions() {
instrument.add_region_opcode(&status, o);
} else if status.are_groups() {
instrument.add_group_opcode(&status, o);
} else {
unreachable!();
}
}
},
_ => (),
}}
Ok(instrument)
}
fn add_region_opcode(&mut self, status: &InstrumentParsingStatus, opcode: &Opcode) {
self.regions[status.region_counter.unwrap()].add(opcode, status.group_counter);
}
fn add_group_opcode(&mut self, status: &InstrumentParsingStatus, opcode: &Opcode) {
self.groups[status.group_counter.unwrap()].add(opcode);
}
}
#[derive(Debug)]
struct InstrumentParsingStatus {
is_header_control: bool,
is_header_global: bool,
group_counter: Option<usize>,
region_counter: Option<usize>,
}
impl InstrumentParsingStatus {
pub fn init() -> Self {
Self {
is_header_control: false,
is_header_global: false,
group_counter: None,
region_counter: None,
}
}
pub fn new_group(&mut self) {
self.is_header_control = false;
self.is_header_global = false;
self.region_reset();
self.group_increment();
}
pub fn new_region(&mut self) {
self.is_header_control = false;
self.is_header_global = false;
self.region_increment();
}
pub fn new_control(&mut self) {
if !self.is_header_control
&& !self.is_header_global
&& self.group_counter == None
&& self.region_counter == None {
self.is_header_control = true;
}
}
pub fn new_global(&mut self) {
if !self.is_header_global
&& self.group_counter == None
&& self.region_counter == None {
self.is_header_control = false;
self.is_header_global = true;
}
}
fn region_increment(&mut self) {
match self.region_counter {
Some(c) => self.region_counter = Some(c+1),
None => self.region_counter = Some(0),
}
}
fn region_reset(&mut self) {
self.region_counter = None;
}
fn group_increment(&mut self) {
match self.region_counter {
Some(c) => self.group_counter = Some(c+1),
None => self.group_counter = Some(0),
}
}
pub fn are_groups(&self) -> bool {
if self.group_counter == None {
return false;
}
true
}
pub fn are_regions(&self) -> bool {
if self.region_counter == None {
return false;
}
true
}
}