EEF instrument generation
parent
01645a02b7
commit
873a08dc00
|
@ -41,7 +41,7 @@ fn run() -> Result<(), Box<dyn Error>> {
|
||||||
SubCommand::with_name( "soundtrack" )
|
SubCommand::with_name( "soundtrack" )
|
||||||
.about( "Generate an on-console soundtrack from a chiptune tracker module" )
|
.about( "Generate an on-console soundtrack from a chiptune tracker module" )
|
||||||
.arg_from_usage( "-i, --input=<FILE> 'Specify input module'" )
|
.arg_from_usage( "-i, --input=<FILE> 'Specify input module'" )
|
||||||
.arg_from_usage( "-o, --output=<FILE> 'Specify output file'")
|
.arg_from_usage( "-o, --output=<FILE> 'Specify output filename (may output multiple files depending on format)'")
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name( "TRACKER_FORMAT" )
|
Arg::with_name( "TRACKER_FORMAT" )
|
||||||
.short( "tf" )
|
.short( "tf" )
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::{error::Error, fs::File, io::Read, convert::TryInto, collections::HashMap};
|
use std::{error::Error, fs::File, io::Read, convert::TryInto, collections::HashMap, cmp::{min, max}};
|
||||||
use flate2::read::ZlibDecoder;
|
use flate2::read::ZlibDecoder;
|
||||||
use crate::reskit::{utility::{Either, get_string, get_u8, skip, get_u32, get_i8, get_i32, get_u16, get_i16}, soundtrack::types::{SampleRate, SampleFormat, PsgEnvelope}};
|
use crate::reskit::{utility::{get_string, get_u8, skip, get_u32, get_i8, get_i32, get_u16, get_i16, Ring, arrays_equal}, soundtrack::types::{SampleRate, SampleFormat, PsgEnvelope}};
|
||||||
|
|
||||||
use super::types::{Sample, Note, PsgSettings};
|
use super::types::{Sample, Note, PsgSettings};
|
||||||
|
|
||||||
|
@ -223,9 +223,10 @@ impl DmfModule {
|
||||||
};
|
};
|
||||||
|
|
||||||
println!( "Volume Envelope Size:\t{}", envelope.len() );
|
println!( "Volume Envelope Size:\t{}", envelope.len() );
|
||||||
|
println!( "Loop at:\t{:?}", loop_at );
|
||||||
|
|
||||||
PsgEnvelope {
|
PsgEnvelope {
|
||||||
envelope: Either::Type1( envelope ),
|
envelope,
|
||||||
loop_at,
|
loop_at,
|
||||||
settings: HashMap::new()
|
settings: HashMap::new()
|
||||||
}
|
}
|
||||||
|
@ -257,9 +258,10 @@ impl DmfModule {
|
||||||
|
|
||||||
println!( "Arpeggio Envelope Size:\t{}", envelope.len() );
|
println!( "Arpeggio Envelope Size:\t{}", envelope.len() );
|
||||||
println!( "Arpeggio Macro Fixed:\t{}", settings[ "arpeggio_macro_fixed" ] );
|
println!( "Arpeggio Macro Fixed:\t{}", settings[ "arpeggio_macro_fixed" ] );
|
||||||
|
println!( "Loop at:\t{:?}", loop_at );
|
||||||
|
|
||||||
PsgEnvelope {
|
PsgEnvelope {
|
||||||
envelope: Either::Type2( envelope ),
|
envelope,
|
||||||
loop_at,
|
loop_at,
|
||||||
settings
|
settings
|
||||||
}
|
}
|
||||||
|
@ -286,9 +288,10 @@ impl DmfModule {
|
||||||
};
|
};
|
||||||
|
|
||||||
println!( "Noise Envelope Size:\t{}", envelope.len() );
|
println!( "Noise Envelope Size:\t{}", envelope.len() );
|
||||||
|
println!( "Loop at:\t{:?}", loop_at );
|
||||||
|
|
||||||
PsgEnvelope {
|
PsgEnvelope {
|
||||||
envelope: Either::Type1( envelope ),
|
envelope,
|
||||||
loop_at,
|
loop_at,
|
||||||
settings: HashMap::new()
|
settings: HashMap::new()
|
||||||
}
|
}
|
||||||
|
@ -315,9 +318,10 @@ impl DmfModule {
|
||||||
};
|
};
|
||||||
|
|
||||||
println!( "WavTbl Envelope Size:\t{}\n", envelope.len() );
|
println!( "WavTbl Envelope Size:\t{}\n", envelope.len() );
|
||||||
|
println!( "Loop at:\t{:?}", loop_at );
|
||||||
|
|
||||||
PsgEnvelope {
|
PsgEnvelope {
|
||||||
envelope: Either::Type1( envelope ),
|
envelope,
|
||||||
loop_at,
|
loop_at,
|
||||||
settings: HashMap::new()
|
settings: HashMap::new()
|
||||||
}
|
}
|
||||||
|
@ -592,9 +596,163 @@ impl DmfModule {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_esf( self ) -> Result<Vec<u8>, Box<dyn Error>> {
|
pub fn get_eefs( &self ) -> Result<HashMap<String, Vec<u8>>, Box<dyn Error>> {
|
||||||
|
let mut eefs: HashMap<String, Vec<u8>> = HashMap::new();
|
||||||
|
|
||||||
|
for instrument in &self.instruments {
|
||||||
|
if let InstrumentType::Psg( settings ) = &instrument.instrument_type {
|
||||||
|
// Echo uses volume and arpeggio envelopes
|
||||||
|
// Noise envelopes are usable but cannot be articulated in an instrument.
|
||||||
|
// For use of the noise envelope, use ESF $0Bnn/$3Bnn and $3Ann in the stream.
|
||||||
|
|
||||||
|
// comments using example: 20 vol values, 6 arp values, both repeat from the beginning
|
||||||
|
|
||||||
|
// generate the initial portion (always). this is just an expansion
|
||||||
|
// of the envelopes, without loops, into two Vec<u8>'s.
|
||||||
|
// 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20
|
||||||
|
// 01 02 03 04 05 06 -- -- -- -- -- -- -- -- -- -- -- -- -- --
|
||||||
|
let mut volume_envelope: Vec<u8> = settings.volume.envelope.iter().map( | long | *long as u8 ).collect();
|
||||||
|
let mut arpeggio_envelope: Vec<i8> = settings.arpeggio.envelope.iter().map( | long | *long as i8 ).collect();
|
||||||
|
|
||||||
|
// now take a slice of the repeatable portion of the smaller array,
|
||||||
|
// and use it to expand the smaller array to the same size as the
|
||||||
|
// larger array. if there is no repeatable portion, take a one-element
|
||||||
|
// slice of the last element in the array.
|
||||||
|
// 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20
|
||||||
|
// 01 02 03 04 05 06 01 02 03 04 05 06 01 02 03 04 05 06 01 02
|
||||||
|
let mut volume_repeat_pattern: Ring<u8> = Ring::create(
|
||||||
|
if let Some( loop_at ) = settings.volume.loop_at {
|
||||||
|
Vec::from( &volume_envelope[ loop_at.. ] )
|
||||||
|
} else {
|
||||||
|
vec![ volume_envelope[ volume_envelope.len() - 1 ] ]
|
||||||
|
},
|
||||||
|
None
|
||||||
|
);
|
||||||
|
let mut arpeggio_repeat_pattern: Ring<i8> = Ring::create(
|
||||||
|
if let Some( loop_at ) = settings.arpeggio.loop_at {
|
||||||
|
Vec::from( &arpeggio_envelope[ loop_at.. ] )
|
||||||
|
} else {
|
||||||
|
vec![ arpeggio_envelope[ arpeggio_envelope.len() - 1 ] ]
|
||||||
|
},
|
||||||
|
None
|
||||||
|
);
|
||||||
|
|
||||||
|
if volume_envelope.len() < arpeggio_envelope.len() {
|
||||||
|
let difference = arpeggio_envelope.len() - volume_envelope.len();
|
||||||
|
for _ in 0..difference {
|
||||||
|
volume_envelope.push(
|
||||||
|
volume_repeat_pattern.next().expect( "fatal: internal error (volume_repeat_pattern)" )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else if arpeggio_envelope.len() < volume_envelope.len() {
|
||||||
|
let difference = volume_envelope.len() - arpeggio_envelope.len();
|
||||||
|
for _ in 0..difference {
|
||||||
|
arpeggio_envelope.push(
|
||||||
|
arpeggio_repeat_pattern.next().expect( "fatal: internal error (arpeggio_repeat_pattern)" )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let interval_size = max( volume_repeat_pattern.len(), arpeggio_repeat_pattern.len() );
|
||||||
|
|
||||||
|
// now, you generate the repeating portion using the two repeat_pattern iterators.
|
||||||
|
// each segment at a time is generated by the size of the larger array.
|
||||||
|
// generate it until the first repeat pattern is regenerated.
|
||||||
|
// 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 .. (volume repeats from beginning)
|
||||||
|
// 03 04 05 06 01 02 03 04 05 06 01 02 03 04 05 06 01 02 03 04 .. (arpeggio repeats up from where it left off)
|
||||||
|
|
||||||
|
// 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 ..
|
||||||
|
// 05 06 01 02 03 04 05 06 01 02 03 04 05 06 01 02 03 04 05 06 ..
|
||||||
|
|
||||||
|
// 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 ..
|
||||||
|
// 01 02 03 04 05 06 01 02 03 04 05 06 01 02 03 04 05 06 01 02 ..
|
||||||
|
let mut volume_repeat: Vec<u8> = Vec::new();
|
||||||
|
let mut arpeggio_repeat: Vec<i8> = Vec::new();
|
||||||
|
|
||||||
|
let mut initial_volume_repeat: Vec<u8> = Vec::new();
|
||||||
|
let mut initial_arpeggio_repeat: Vec<i8> = Vec::new();
|
||||||
|
for _ in 0..interval_size {
|
||||||
|
initial_volume_repeat.push( volume_repeat_pattern.next().expect( "fatal: internal error (volume_repeat_pattern)" ) );
|
||||||
|
initial_arpeggio_repeat.push( arpeggio_repeat_pattern.next().expect( "fatal: internal error (arpeggio_repeat_pattern)" ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
volume_repeat.append( &mut initial_volume_repeat );
|
||||||
|
arpeggio_repeat.append( &mut initial_arpeggio_repeat );
|
||||||
|
|
||||||
|
loop {
|
||||||
|
// Guard against OOM
|
||||||
|
if volume_repeat.len() > 65535 {
|
||||||
|
return Err( "eef error: psg instrument repeat period too long (try modifying repeating envelopes to have evenly-divisible parameters)" )?
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut volume_repeat_period: Vec<u8> = Vec::new();
|
||||||
|
let mut arpeggio_repeat_period: Vec<i8> = Vec::new();
|
||||||
|
for _ in 0..interval_size {
|
||||||
|
volume_repeat_period.push( volume_repeat_pattern.next().expect( "fatal: internal error (volume_repeat_pattern)" ) );
|
||||||
|
arpeggio_repeat_period.push( arpeggio_repeat_pattern.next().expect( "fatal: internal error (arpeggio_repeat_pattern)" ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
// End loop once the pattern begins repeating
|
||||||
|
if arrays_equal( &initial_volume_repeat, &volume_repeat_period ) && arrays_equal( &initial_arpeggio_repeat, &arpeggio_repeat_period ) {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
// If it doesn't repeat, append and generate another period
|
||||||
|
volume_repeat.append( &mut volume_repeat_period );
|
||||||
|
arpeggio_repeat.append( &mut arpeggio_repeat_period );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now generate the Echo EEF envelope from volume_envelope/volume_repeat + arpeggio_envelope/arpeggio_repeat
|
||||||
|
let mut envelope: Vec<u8> = Vec::new();
|
||||||
|
for i in 0..volume_envelope.len() {
|
||||||
|
envelope.push( get_eef_volume( volume_envelope[ i ] )? | get_eef_shift( arpeggio_envelope[ i ] )? );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Echo begin loop command
|
||||||
|
envelope.push( 0xFE );
|
||||||
|
|
||||||
|
for i in 0..volume_repeat.len() {
|
||||||
|
envelope.push( get_eef_volume( volume_repeat[ i ] )? | get_eef_shift( arpeggio_repeat[ i ] )? );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Echo end loop command
|
||||||
|
envelope.push( 0xFF );
|
||||||
|
|
||||||
|
eefs.insert( instrument.name.clone(), envelope );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok( eefs )
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_esf( &self ) -> Result<Vec<u8>, Box<dyn Error>> {
|
||||||
// TODO !!
|
// TODO !!
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_eef_volume( dmf_volume: u8 ) -> Result<u8, Box<dyn Error>> {
|
||||||
|
Ok( max( dmf_volume, 0x0F ) )
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_eef_shift( dmf_shift: i8 ) -> Result<u8, Box<dyn Error>> {
|
||||||
|
// Clamp to [-12, 12]
|
||||||
|
let as_i8: i8 = max( -12, min( dmf_shift, 12 ) );
|
||||||
|
|
||||||
|
Ok(
|
||||||
|
match as_i8 {
|
||||||
|
0 => 0x00,
|
||||||
|
|
||||||
|
1 => 0x10, -1 => 0x80,
|
||||||
|
2 => 0x20, -2 => 0x90,
|
||||||
|
3 => 0x30, -3 => 0xA0,
|
||||||
|
4 => 0x40, -4 => 0xB0,
|
||||||
|
5 | 6 => 0x50, -5 | -6 => 0xC0,
|
||||||
|
7 | 8 => 0x60, -7 | -8 => 0xD0,
|
||||||
|
9 | 10 | 11 | 12 => 0x70, -9 | -10 | -11 | -12 => 0xE0,
|
||||||
|
|
||||||
|
_ => return Err( "internal error: arpeggio shift out of defined range" )?
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
|
@ -1,22 +1,20 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use crate::reskit::utility::Either;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct PsgEnvelope {
|
pub struct PsgEnvelope< DataFormat > {
|
||||||
pub envelope: Either<Vec<u32>, Vec<i32>>,
|
pub envelope: Vec<DataFormat>,
|
||||||
pub loop_at: Option<usize>,
|
pub loop_at: Option<usize>,
|
||||||
pub settings: HashMap<&'static str, bool>
|
pub settings: HashMap<&'static str, bool>
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct PsgSettings {
|
pub struct PsgSettings {
|
||||||
pub volume: PsgEnvelope,
|
pub volume: PsgEnvelope< u32 >,
|
||||||
pub arpeggio: PsgEnvelope,
|
pub arpeggio: PsgEnvelope< i32 >,
|
||||||
pub noise: PsgEnvelope,
|
pub noise: PsgEnvelope< u32 >,
|
||||||
pub wavetable: PsgEnvelope
|
pub wavetable: PsgEnvelope< u32 >
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum SampleRate {
|
pub enum SampleRate {
|
||||||
Hz8000,
|
Hz8000,
|
||||||
|
|
|
@ -1,10 +1,56 @@
|
||||||
use std::{slice::Iter, error::Error, str::from_utf8, convert::TryInto};
|
use std::{slice::Iter, error::Error, str::from_utf8, convert::TryInto};
|
||||||
|
|
||||||
|
pub struct Ring< Type: Copy >{
|
||||||
|
data: Vec< Type >,
|
||||||
|
position: usize
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
impl< Type: Copy > Ring< Type > {
|
||||||
pub enum Either< Type1, Type2 > {
|
|
||||||
Type1( Type1 ),
|
pub fn create( data: Vec< Type >, start_position: Option<usize> ) -> Ring< Type > {
|
||||||
Type2( Type2 )
|
Ring {
|
||||||
|
data,
|
||||||
|
position: if let Some( position ) = start_position {
|
||||||
|
position
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn len( &self ) -> usize {
|
||||||
|
self.data.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
impl< Type: Copy > Iterator for Ring< Type > {
|
||||||
|
type Item = Type;
|
||||||
|
|
||||||
|
fn next( &mut self ) -> Option< Self::Item > {
|
||||||
|
let item = self.data[ self.position % self.data.len() ];
|
||||||
|
|
||||||
|
self.position += 1;
|
||||||
|
|
||||||
|
Some( item )
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn arrays_equal< Type: PartialEq >( array1: &Vec<Type>, array2: &Vec<Type> ) -> bool {
|
||||||
|
if array1.len() != array2.len() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for i1 in 0..array1.len() {
|
||||||
|
for i2 in 0..array2.len() {
|
||||||
|
if array1[ i1 ] != array2[ i2 ] {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn print_error( error_msg: &str ) {
|
pub fn print_error( error_msg: &str ) {
|
||||||
|
|
Loading…
Reference in New Issue