EWF generation
parent
e55f9e3fe8
commit
34e4bbb3db
|
@ -10,4 +10,7 @@ edition = "2018"
|
||||||
image = "^0.23"
|
image = "^0.23"
|
||||||
colour = "^0.5"
|
colour = "^0.5"
|
||||||
clap = "^2.33"
|
clap = "^2.33"
|
||||||
flate2 = "1.0.26"
|
flate2 = "1.0.26"
|
||||||
|
samplerate = "0.2.4"
|
||||||
|
hound = "3.5.0"
|
||||||
|
pitch_shift = "1.0.0"
|
12
src/main.rs
12
src/main.rs
|
@ -82,20 +82,24 @@ fn run() -> Result<(), Box<dyn Error>> {
|
||||||
|
|
||||||
let result = DmfModule::from_file( &input_filename )?;
|
let result = DmfModule::from_file( &input_filename )?;
|
||||||
|
|
||||||
let eef_files = result.get_eefs()?;
|
for ( filename, data ) in result.get_eefs()? {
|
||||||
for ( filename, data ) in eef_files {
|
|
||||||
println!( "Creating EEF instrument file {}", filename );
|
println!( "Creating EEF instrument file {}", filename );
|
||||||
let mut file = File::create( filename )?;
|
let mut file = File::create( filename )?;
|
||||||
file.write_all( &data )?
|
file.write_all( &data )?
|
||||||
}
|
}
|
||||||
|
|
||||||
let eif_files = result.get_eifs()?;
|
for ( filename, data ) in result.get_eifs()? {
|
||||||
for ( filename, data ) in eif_files {
|
|
||||||
println!( "Creating EIF instrument file {}", filename );
|
println!( "Creating EIF instrument file {}", filename );
|
||||||
let mut file = File::create( filename )?;
|
let mut file = File::create( filename )?;
|
||||||
file.write_all( &data )?
|
file.write_all( &data )?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for ( filename, data ) in result.get_ewfs()? {
|
||||||
|
println!( "Creating EWF audio file {}", filename );
|
||||||
|
let mut file = File::create( filename )?;
|
||||||
|
file.write_all( &data )?
|
||||||
|
}
|
||||||
|
|
||||||
// TODO !!
|
// TODO !!
|
||||||
|
|
||||||
Ok( () )
|
Ok( () )
|
||||||
|
|
|
@ -6,4 +6,6 @@ pub trait EchoFormat {
|
||||||
|
|
||||||
fn get_eifs( &self ) -> Result<HashMap<String, Vec<u8>>, Box<dyn Error>>;
|
fn get_eifs( &self ) -> Result<HashMap<String, Vec<u8>>, Box<dyn Error>>;
|
||||||
|
|
||||||
|
fn get_ewfs( &self ) -> Result<HashMap<String, Vec<u8>>, Box<dyn Error>>;
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,11 +1,13 @@
|
||||||
use std::{error::Error, fs::File, io::Read, convert::TryInto, collections::HashMap, cmp::{min, max}};
|
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::{get_string, get_u8, skip, get_u32, get_i8, get_i32, get_u16, get_i16, Ring, print_warning}, soundtrack::{types::{SampleRate, SampleFormat, PsgEnvelope, Note, Sample, PsgSettings}, engines::echo::EchoFormat}};
|
use samplerate::convert;
|
||||||
|
use crate::reskit::{utility::{get_string, get_u8, skip, get_u32, get_i8, get_i32, get_u16, get_i16, Ring, print_warning}, soundtrack::{types::{SampleFormat, PsgEnvelope, Note, Sample, PsgSettings}, engines::echo::EchoFormat}};
|
||||||
|
|
||||||
const DMF_MAGIC_NUMBER: &'static str = ".DelekDefleMask.";
|
const DMF_MAGIC_NUMBER: &'static str = ".DelekDefleMask.";
|
||||||
const DMF_SUPPORTED_VERSION: u8 = 27;
|
const DMF_SUPPORTED_VERSION: u8 = 27;
|
||||||
const DMF_MD: u8 = 0x02;
|
const DMF_MD: u8 = 0x02;
|
||||||
const DMF_MD_ENHANCED_CH3: u8 = 0x42;
|
const DMF_MD_ENHANCED_CH3: u8 = 0x42;
|
||||||
|
const ECHO_EWF_SAMPLE_RATE: u32 = 10650;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum FrameMode {
|
pub enum FrameMode {
|
||||||
|
@ -566,11 +568,11 @@ impl DmfModule {
|
||||||
let sample_name_chars = get_u8( bytes.by_ref() )?;
|
let sample_name_chars = get_u8( bytes.by_ref() )?;
|
||||||
let name = get_string( bytes.by_ref(), sample_name_chars.into() )?;
|
let name = get_string( bytes.by_ref(), sample_name_chars.into() )?;
|
||||||
let rate = match get_u8( bytes.by_ref() )? {
|
let rate = match get_u8( bytes.by_ref() )? {
|
||||||
1 => SampleRate::Hz8000,
|
1 => 8000,
|
||||||
2 => SampleRate::Hz11025,
|
2 => 11025,
|
||||||
3 => SampleRate::Hz16000,
|
3 => 16000,
|
||||||
4 => SampleRate::Hz22050,
|
4 => 22050,
|
||||||
5 => SampleRate::Hz32000,
|
5 => 32000,
|
||||||
invalid => return Err( format!( "invalid file: invalid sample rate {}", invalid ) )?
|
invalid => return Err( format!( "invalid file: invalid sample rate {}", invalid ) )?
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -592,7 +594,7 @@ impl DmfModule {
|
||||||
println!( "{}:\t{:?}", i, sample );
|
println!( "{}:\t{:?}", i, sample );
|
||||||
|
|
||||||
for _ in 0..sample_size {
|
for _ in 0..sample_size {
|
||||||
sample.data.push( get_u16( bytes.by_ref() )? );
|
sample.data.push( get_i16( bytes.by_ref() )? );
|
||||||
}
|
}
|
||||||
|
|
||||||
samples.push( sample );
|
samples.push( sample );
|
||||||
|
@ -803,6 +805,65 @@ impl EchoFormat for DmfModule {
|
||||||
Ok( eifs )
|
Ok( eifs )
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_ewfs( &self ) -> Result<HashMap<String, Vec<u8>>, Box<dyn Error>> {
|
||||||
|
let mut ewfs: HashMap<String, Vec<u8>> = HashMap::new();
|
||||||
|
|
||||||
|
for sample in &self.samples {
|
||||||
|
// Amplify data in original sample
|
||||||
|
let data: Vec<i16> = sample.data
|
||||||
|
.iter()
|
||||||
|
.map( | pcm_sample | {
|
||||||
|
let big_pcm_sample: i32 = min( ( *pcm_sample as i32 ) + sample.amp as i32, i16::MAX as i32 );
|
||||||
|
|
||||||
|
big_pcm_sample as i16
|
||||||
|
} )
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// Convert to f32 for various library operations
|
||||||
|
let data: Vec<f32> = data
|
||||||
|
.into_iter()
|
||||||
|
.map( | pcm_sample | {
|
||||||
|
if let SampleFormat::Bits8 = sample.bitrate {
|
||||||
|
( pcm_sample as u8 ) as f32 / 255.0
|
||||||
|
} else {
|
||||||
|
pcm_sample as f32 / 32767.0
|
||||||
|
}
|
||||||
|
} )
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// TODO: Adjust pitch using crate pitch_shift
|
||||||
|
if sample.pitch > 0 {
|
||||||
|
print_warning( "pitch shift not yet implemented for pcm samples. your drums/voice samples/etc may sound a bit funny" );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use libsamplerate to resample from whatever it is now to 10650Hz, the sample rate
|
||||||
|
// required by Echo sound engine EWF samples.
|
||||||
|
let data: Vec<f32> = convert( sample.rate, ECHO_EWF_SAMPLE_RATE, 1, samplerate::ConverterType::SincBestQuality, &data )?;
|
||||||
|
|
||||||
|
// Convert from f32 back to i16, then downconvert to u8
|
||||||
|
let mut data: Vec<u8> = data
|
||||||
|
.into_iter()
|
||||||
|
.map( | pcm_sample | {
|
||||||
|
let pcm_sample = ( ( pcm_sample + 1.0 ) * 128.0 ) as u8;
|
||||||
|
|
||||||
|
// Do not end stream prematurely (EWF format uses 0xFF to end stream)
|
||||||
|
if pcm_sample == 0xFF {
|
||||||
|
0xFE
|
||||||
|
} else {
|
||||||
|
pcm_sample
|
||||||
|
}
|
||||||
|
} )
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// Terminate stream
|
||||||
|
data.push( 0xFF );
|
||||||
|
|
||||||
|
ewfs.insert( format!( "{}.ewf", sample.name ), data );
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok( ewfs )
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_eef_volume( dmf_volume: u8 ) -> Result<u8, Box<dyn Error>> {
|
fn get_eef_volume( dmf_volume: u8 ) -> Result<u8, Box<dyn Error>> {
|
||||||
|
|
|
@ -15,15 +15,6 @@ pub struct PsgSettings {
|
||||||
pub wavetable: PsgEnvelope< u32 >
|
pub wavetable: PsgEnvelope< u32 >
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum SampleRate {
|
|
||||||
Hz8000,
|
|
||||||
Hz11025,
|
|
||||||
Hz16000,
|
|
||||||
Hz22050,
|
|
||||||
Hz32000
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum SampleFormat {
|
pub enum SampleFormat {
|
||||||
Bits8,
|
Bits8,
|
||||||
|
@ -33,11 +24,11 @@ pub enum SampleFormat {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Sample {
|
pub struct Sample {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub rate: SampleRate,
|
pub rate: u32,
|
||||||
pub pitch: i8,
|
pub pitch: i8,
|
||||||
pub amp: u8,
|
pub amp: u8,
|
||||||
pub bitrate: SampleFormat,
|
pub bitrate: SampleFormat,
|
||||||
pub data: Vec<u16>
|
pub data: Vec<i16>
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
Loading…
Reference in New Issue