use std::{
fmt::Debug,
fs::File,
io::prelude::*,
path::{Path, PathBuf},
};
use logos::Logos;
use crate::{
error::{Error, Result},
sfz::{types::OpcodeMap, Group, Header, Opcode, Region, SfzToken},
};
#[derive(Debug)]
pub struct Instrument {
pub global: OpcodeMap,
pub groups: Vec<Group>,
pub regions: Vec<Region>,
pub default_path: PathBuf,
last_header_created: Header,
}
impl Instrument {
pub fn new() -> Instrument {
Instrument {
global: OpcodeMap::new(),
groups: Vec::<Group>::new(),
regions: Vec::<Region>::new(),
default_path: PathBuf::new(),
last_header_created: Header::Global,
}
}
pub fn from_file(sfz_path: &Path) -> Result<Self> {
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> {
#[cfg(debug)]
println!("DEBUG: Instrument::from_sfz()\n-----------------------------");
let mut instrument = Instrument {
global: OpcodeMap::new(),
groups: Vec::<Group>::new(),
regions: Vec::<Region>::new(),
default_path: sfz_path.to_path_buf(),
last_header_created: Header::Global,
};
let mut status = InstrumentParsingStatus::init();
let lex = SfzToken::lexer(&sfz);
for t in lex {
match &t {
SfzToken::Header(h) => {
match h {
Header::Group => {
#[cfg(debug)]
println!("\nFound a new group:");
status.new_group();
instrument.groups.push(Group::new());
}
Header::Region => {
#[cfg(debug)]
println!("\nFound a new 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 {
#[cfg(debug)]
println!("global OP {:?}", o);
instrument.add_opcode_global(o);
} else if status.is_header_control {
match o {
Opcode::default_path(p) => instrument.default_path.push(p),
_ => (),
}
} else {
if status.are_regions_in_current_group() {
#[cfg(debug)]
println!(
" - new region opcode: {:?} (g{} r{})",
o,
status.group_counter.unwrap(),
status.region_counter.unwrap()
);
instrument.add_opcode_to_region(o, status.region_counter.unwrap())?;
instrument.set_region_group(
status.region_counter.unwrap(),
status.group_counter,
)?;
} else if status.are_groups() {
#[cfg(debug)]
println!(" - new group opcode: {:?}", o);
instrument.add_opcode_to_group(o, status.group_counter.unwrap())?;
} else {
unreachable!();
}
}
}
_ => (),
}
}
#[cfg(debug)]
println!("-----------------------------------\n");
Ok(instrument)
}
pub fn add_opcode(&mut self, opcode: &Opcode) -> Result<()> {
match self.last_header_created {
Header::Global => Ok(self.add_opcode_global(opcode)),
Header::Group => self.add_opcode_to_group(opcode, self.groups() - 1),
Header::Region => self.add_opcode_to_region(opcode, self.regions() - 1),
_ => Err(Error::Generic),
}
}
pub fn add_opcode_global(&mut self, opcode: &Opcode) {
self.global.insert(opcode.str_name(), opcode.clone());
}
pub fn add_opcode_to_group(&mut self, opcode: &Opcode, group: usize) -> Result<()> {
if group >= self.groups() {
return Err(Error::OutOfBounds(format![
"Tried to add an Opcode to Group `{0}`, but the last group is `{1}`",
group,
self.groups() - 1
]));
}
self.groups[group].add_opcode(opcode);
Ok(())
}
pub fn add_opcode_to_region(&mut self, opcode: &Opcode, region: usize) -> Result<()> {
#[cfg(debug)]
println!(
" status.add_opcode_to_region() opcode: {:?} region: {}",
opcode, region
);
if region >= self.regions() {
return Err(Error::OutOfBounds(format![
"Tried to add an Opcode to Region `{0}`, but the last region is `{1}`",
region,
self.regions() - 1
]));
}
self.regions[region].add_opcode(opcode);
Ok(())
}
pub fn groups(&self) -> usize {
self.groups.len()
}
pub fn regions(&self) -> usize {
self.regions.len()
}
pub fn regions_in(&self, group: usize) -> Result<usize> {
if group >= self.groups() {
return Err(Error::OutOfBounds(format![
"There's no group `{0}`, the last group is `{1}`",
group,
self.groups() - 1
]));
}
let mut count = 0;
for region in self.regions.iter() {
if region.group() == Some(group) {
count += 1;
}
}
Ok(count)
}
pub fn new_group(&mut self) {
self.groups.push(Group::new());
self.last_header_created = Header::Group;
}
pub fn new_region(&mut self) {
let num_groups = self.groups();
if num_groups > 0 {
self.regions.push(Region::with_group(num_groups - 1));
} else {
self.regions.push(Region::new());
}
self.last_header_created = Header::Region;
}
pub fn set_region_group(&mut self, region: usize, group: Option<usize>) -> Result<()> {
#[cfg(debug)]
println!(
" status.set_region_group() region: {} group: {:?}",
region, group
);
if region >= self.regions() {
return Err(Error::OutOfBounds(format![
"Tried set the group of Region `{0}`, but the last region is `{1}`",
region,
self.regions() - 1
]));
}
if let Some(g) = group {
if g >= self.groups() {
return Err(Error::OutOfBounds(
format!["Tried to set Region `{0}` to have the Group `{1}`, but the last group is `{2}`",
region, g, self.groups()-1]
));
}
}
self.regions[region].set_group(group);
Ok(())
}
pub fn groups_iter(&self) -> () {}
}
#[derive(Debug)]
struct InstrumentParsingStatus {
is_header_control: bool,
is_header_global: bool,
group_counter: Option<usize>,
region_counter_in_group: 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_in_group: None,
region_counter: None,
}
}
pub fn new_group(&mut self) {
#[cfg(debug)]
println!(" status.new_group()");
self.is_header_control = false;
self.is_header_global = false;
self.region_reset_in_current_group();
self.group_increment();
}
pub fn new_region(&mut self) {
#[cfg(debug)]
println!(" status.new_region()");
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),
}
match self.region_counter_in_group {
Some(c) => self.region_counter_in_group = Some(c + 1),
None => self.region_counter_in_group = Some(0),
}
#[cfg(debug)]
println!(
" status.region_increment() g{}→rig{} (r{})",
self.group_counter.unwrap(),
self.region_counter_in_group.unwrap(),
self.region_counter.unwrap()
);
}
fn region_reset_in_current_group(&mut self) {
#[cfg(debug)]
println!(" status.region_reset_in_current_group()");
self.region_counter_in_group = None;
}
fn group_increment(&mut self) {
match self.group_counter {
Some(c) => self.group_counter = Some(c + 1),
None => self.group_counter = Some(0),
}
#[cfg(debug)]
println!(
" status.group_increment() (→g{})",
self.group_counter.unwrap()
);
}
pub fn are_groups(&self) -> bool {
if self.group_counter == None {
return false;
}
true
}
pub fn are_regions_in_current_group(&self) -> bool {
if self.region_counter_in_group == None {
return false;
}
true
}
}