EWF generation
parent
e55f9e3fe8
commit
34e4bbb3db
|
@ -11,3 +11,6 @@ image = "^0.23"
|
|||
colour = "^0.5"
|
||||
clap = "^2.33"
|
||||
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 eef_files = result.get_eefs()?;
|
||||
for ( filename, data ) in eef_files {
|
||||
for ( filename, data ) in result.get_eefs()? {
|
||||
println!( "Creating EEF instrument file {}", filename );
|
||||
let mut file = File::create( filename )?;
|
||||
file.write_all( &data )?
|
||||
}
|
||||
|
||||
let eif_files = result.get_eifs()?;
|
||||
for ( filename, data ) in eif_files {
|
||||
for ( filename, data ) in result.get_eifs()? {
|
||||
println!( "Creating EIF instrument file {}", filename );
|
||||
let mut file = File::create( filename )?;
|
||||
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 !!
|
||||
|
||||
Ok( () )
|
||||
|
|
|
@ -6,4 +6,6 @@ pub trait EchoFormat {
|
|||
|
||||
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 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_SUPPORTED_VERSION: u8 = 27;
|
||||
const DMF_MD: u8 = 0x02;
|
||||
const DMF_MD_ENHANCED_CH3: u8 = 0x42;
|
||||
const ECHO_EWF_SAMPLE_RATE: u32 = 10650;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum FrameMode {
|
||||
|
@ -566,11 +568,11 @@ impl DmfModule {
|
|||
let sample_name_chars = get_u8( bytes.by_ref() )?;
|
||||
let name = get_string( bytes.by_ref(), sample_name_chars.into() )?;
|
||||
let rate = match get_u8( bytes.by_ref() )? {
|
||||
1 => SampleRate::Hz8000,
|
||||
2 => SampleRate::Hz11025,
|
||||
3 => SampleRate::Hz16000,
|
||||
4 => SampleRate::Hz22050,
|
||||
5 => SampleRate::Hz32000,
|
||||
1 => 8000,
|
||||
2 => 11025,
|
||||
3 => 16000,
|
||||
4 => 22050,
|
||||
5 => 32000,
|
||||
invalid => return Err( format!( "invalid file: invalid sample rate {}", invalid ) )?
|
||||
};
|
||||
|
||||
|
@ -592,7 +594,7 @@ impl DmfModule {
|
|||
println!( "{}:\t{:?}", i, sample );
|
||||
|
||||
for _ in 0..sample_size {
|
||||
sample.data.push( get_u16( bytes.by_ref() )? );
|
||||
sample.data.push( get_i16( bytes.by_ref() )? );
|
||||
}
|
||||
|
||||
samples.push( sample );
|
||||
|
@ -803,6 +805,65 @@ impl EchoFormat for DmfModule {
|
|||
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>> {
|
||||
|
|
|
@ -15,15 +15,6 @@ pub struct PsgSettings {
|
|||
pub wavetable: PsgEnvelope< u32 >
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum SampleRate {
|
||||
Hz8000,
|
||||
Hz11025,
|
||||
Hz16000,
|
||||
Hz22050,
|
||||
Hz32000
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum SampleFormat {
|
||||
Bits8,
|
||||
|
@ -33,11 +24,11 @@ pub enum SampleFormat {
|
|||
#[derive(Debug)]
|
||||
pub struct Sample {
|
||||
pub name: String,
|
||||
pub rate: SampleRate,
|
||||
pub rate: u32,
|
||||
pub pitch: i8,
|
||||
pub amp: u8,
|
||||
pub bitrate: SampleFormat,
|
||||
pub data: Vec<u16>
|
||||
pub data: Vec<i16>
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
Loading…
Reference in New Issue