From b82ce4e111e63475563f0c2a9a679730351d34a4 Mon Sep 17 00:00:00 2001 From: PieKing1215 Date: Tue, 27 Feb 2024 02:13:34 -0500 Subject: [PATCH] Add data for PTV envelope & support PTV release in rust impl --- src/pxtone/interface/woice.rs | 16 +++++++++ src/pxtone/og_impl/error.rs | 2 ++ src/pxtone/og_impl/woice.rs | 40 ++++++++++++++++++---- src/pxtone/rust_impl/io.rs | 63 +++++++++++++++++++++++------------ src/pxtone/rust_impl/moo.rs | 49 ++++++++++++++++++++++++--- src/pxtone/rust_impl/woice.rs | 47 +++++++++++++++++++++++--- 6 files changed, 181 insertions(+), 36 deletions(-) diff --git a/src/pxtone/interface/woice.rs b/src/pxtone/interface/woice.rs index 775cf4e..e0edfeb 100644 --- a/src/pxtone/interface/woice.rs +++ b/src/pxtone/interface/woice.rs @@ -191,11 +191,27 @@ pub enum PTVWaveType<'a, C: PTVCoordinateWave, O: PTVOvertoneWave> { Overtone(&'a O), } +pub trait PTVEnvelope { + type EnvelopePoint: PTNEnvelopePoint; + + fn fps(&self) -> u32; + + fn head_num(&self) -> u32; + + fn body_num(&self) -> u32; + + fn tail_num(&self) -> u32; + + fn points(&self) -> Vec<&Self::EnvelopePoint>; +} + pub trait VoicePTV: Voice { type CoordinateWave: PTVCoordinateWave; type OvertoneWave: PTVOvertoneWave; + type Envelope: PTVEnvelope; fn wave(&self) -> PTVWaveType; + fn envelope(&self) -> &Self::Envelope; } #[repr(u8)] diff --git a/src/pxtone/og_impl/error.rs b/src/pxtone/og_impl/error.rs index ec7b48f..9c0b3be 100644 --- a/src/pxtone/og_impl/error.rs +++ b/src/pxtone/og_impl/error.rs @@ -73,6 +73,8 @@ impl Error { } pub fn from_raw(value: pxtnERR) -> Result<(), Error> { + // iirc this cast was needed on linux because pxtnERR was compiled as u32 (?) + #[allow(clippy::unnecessary_cast)] Self::from_i32(value as i32).map_or(Ok(()), Err) } } diff --git a/src/pxtone/og_impl/woice.rs b/src/pxtone/og_impl/woice.rs index 66932e8..25a3a19 100644 --- a/src/pxtone/og_impl/woice.rs +++ b/src/pxtone/og_impl/woice.rs @@ -1,18 +1,14 @@ use std::{ffi::CString, slice, io::Read}; use pxtone_sys::{ - pxNOISEDESIGN_OSCILLATOR, pxNOISEDESIGN_UNIT, pxtnPOINT, pxtnVOICEUNIT, pxtnVOICEWAVE, - pxtnWoice, pxtnDescriptor, pxtnWOICETYPE_pxtnWOICE_PCM, pxtnWOICETYPE, pxtnWOICETYPE_pxtnWOICE_PTN, pxtnWOICETYPE_pxtnWOICE_PTV, pxtnWOICETYPE_pxtnWOICE_OGGV, + pxNOISEDESIGN_OSCILLATOR, pxNOISEDESIGN_UNIT, pxtnDescriptor, pxtnPOINT, pxtnVOICEENVELOPE, pxtnVOICEUNIT, pxtnVOICEWAVE, pxtnWOICETYPE, pxtnWOICETYPE_pxtnWOICE_OGGV, pxtnWOICETYPE_pxtnWOICE_PCM, pxtnWOICETYPE_pxtnWOICE_PTN, pxtnWOICETYPE_pxtnWOICE_PTV, pxtnWoice }; use crate::{ interface::{ service::InvalidText, woice::{ - HasWoices, PTNEnvelopePoint, PTNOscillator, PTNUnit, PTNWaveType, PTVCoordinateWave, - PTVCoordinateWavePoint, PTVOvertoneWave, PTVOvertoneWaveTone, PTVWaveType, SingleVoice, - Voice, VoiceOGGV, VoicePCM, VoicePTN, VoicePTV, Woice, WoiceOGGV, WoicePCM, WoicePTN, - WoicePTV, WoiceType, WoiceTypeMut, WoiceTypeRef, Woices, WoicesMut, + HasWoices, PTNEnvelopePoint, PTNOscillator, PTNUnit, PTNWaveType, PTVCoordinateWave, PTVCoordinateWavePoint, PTVEnvelope, PTVOvertoneWave, PTVOvertoneWaveTone, PTVWaveType, SingleVoice, Voice, VoiceOGGV, VoicePCM, VoicePTN, VoicePTV, Woice, WoiceOGGV, WoicePCM, WoicePTN, WoicePTV, WoiceType, WoiceTypeMut, WoiceTypeRef, Woices, WoicesMut }, }, pxtone::util::{BoxOrMut, BoxOrRef}, @@ -223,9 +219,37 @@ impl PTVOvertoneWave for pxtnVOICEWAVE { } } +impl PTVEnvelope for pxtnVOICEENVELOPE { + type EnvelopePoint = pxtnPOINT; + + fn fps(&self) -> u32 { + self.fps as _ + } + + fn head_num(&self) -> u32 { + self.head_num as _ + } + + fn body_num(&self) -> u32 { + self.body_num as _ + } + + fn tail_num(&self) -> u32 { + self.tail_num as _ + } + + fn points(&self) -> Vec<&Self::EnvelopePoint> { + let num = self.head_num + self.body_num + self.tail_num; + let slice = unsafe { slice::from_raw_parts(self.points, num as usize) }; + + slice.iter().collect() + } +} + impl VoicePTV for pxtnVOICEUNIT { type CoordinateWave = pxtnVOICEWAVE; type OvertoneWave = pxtnVOICEWAVE; + type Envelope = pxtnVOICEENVELOPE; fn wave(&self) -> PTVWaveType { if self.type_ == pxtone_sys::pxtnVOICETYPE_pxtnVOICE_Coodinate { @@ -234,6 +258,10 @@ impl VoicePTV for pxtnVOICEUNIT { PTVWaveType::Overtone(&self.wave) } } + + fn envelope(&self) -> &Self::Envelope { + todo!() + } } impl PTNEnvelopePoint for pxtnPOINT { diff --git a/src/pxtone/rust_impl/io.rs b/src/pxtone/rust_impl/io.rs index 191bbf4..3683d28 100644 --- a/src/pxtone/rust_impl/io.rs +++ b/src/pxtone/rust_impl/io.rs @@ -12,9 +12,7 @@ use crate::{ rust_impl::{ unit::RPxToneUnit, woice::{ - RPxTonePTVCoordinatePoint, RPxTonePTVCoordinateWave, RPxTonePTVOvertoneWave, - RPxTonePTVOvertoneWaveTone, RPxTonePTVWaveType, RPxToneVoicePCM, RPxToneVoicePCMError, - RPxToneVoicePTV, RPxToneWoice, RPxToneWoicePCM, RPxToneWoiceType, + RPXTonePTVEnvelope, RPxTonePTNEnvelopePoint, RPxTonePTVCoordinatePoint, RPxTonePTVCoordinateWave, RPxTonePTVOvertoneWave, RPxTonePTVOvertoneWaveTone, RPxTonePTVWaveType, RPxToneVoicePCM, RPxToneVoicePCMError, RPxToneVoicePTV, RPxToneWoice, RPxToneWoicePCM, RPxToneWoiceType }, }, }; @@ -336,34 +334,57 @@ impl PxToneServiceIO for RPxTone { _ => panic!("Invalid wave type"), }; - let voice = RPxToneVoicePTV::new( - basic_key as _, - volume as _, - pan as _, - f32::from_le_bytes(tuning.to_le_bytes()), - wave, - ); - - if data_flags & 0x2 != 0 { - // TODO: envelope - - let _fps = v_r(&mut c).unwrap(); + #[allow(clippy::if_not_else)] + let envelope = if data_flags & 0x2 != 0 { + let fps = v_r(&mut c).unwrap(); let head_num = v_r(&mut c).unwrap(); let body_num = v_r(&mut c).unwrap(); let tail_num = v_r(&mut c).unwrap(); - println!("{_fps}, {head_num}, {body_num}, {tail_num}"); + println!("{fps}, {head_num}, {body_num}, {tail_num}"); assert_eq!(body_num, 0); assert_eq!(tail_num, 1); let num = head_num + body_num + tail_num; - (0..num).map(|_| { - let _x = v_r(&mut c).unwrap(); - let _y = v_r(&mut c).unwrap(); - }).for_each(drop); - } + let points: Vec<_> = (0..num).map(|_| { + let x = v_r(&mut c).unwrap(); + let y = v_r(&mut c).unwrap(); + + RPxTonePTNEnvelopePoint { + x, + y: y as _, + } + }).collect(); + + println!("points = {points:?}"); + let env_release = if tail_num > 0 { + points[head_num as usize].x * 44100 / fps + } else { + 0 + }; + println!("env_release = {env_release}"); + + RPXTonePTVEnvelope { + fps, + head_num, + body_num, + tail_num, + points, + } + } else { + RPXTonePTVEnvelope::default() + }; + + let voice = RPxToneVoicePTV::new( + basic_key as _, + volume as _, + pan as _, + f32::from_le_bytes(tuning.to_le_bytes()), + wave, + envelope + ); return Some(voice); } diff --git a/src/pxtone/rust_impl/moo.rs b/src/pxtone/rust_impl/moo.rs index c2e9e32..dd78744 100644 --- a/src/pxtone/rust_impl/moo.rs +++ b/src/pxtone/rust_impl/moo.rs @@ -8,7 +8,7 @@ use crate::{ }, moo::{AsMooRef, Moo}, service::PxTone, - woice::{VoicePCM, Woice, WoiceType}, + woice::{VoicePCM, VoicePTV, Woice, WoiceType}, }, util::{BoxOrMut, ZeroToOneF32}, }; @@ -213,10 +213,6 @@ impl<'a> Moo<'a> for RPxToneMoo<'a> { #[allow(clippy::for_kv_map)] for (_unit, data) in &mut self.unit_data { if let Some(on) = &mut data.on { - if clock_ticks > (on.start + on.length) as f32 { - data.on = None; - continue; - } // let on_ticks = clock_ticks - on.start as f32; // let on_secs = on_ticks / ticks_per_sec; @@ -264,6 +260,12 @@ impl<'a> Moo<'a> for RPxToneMoo<'a> { #[allow(clippy::single_match)] match woice.woice_type() { WoiceType::PCM(pcm) => { + + if clock_ticks > (on.start + on.length) as f32 { + data.on = None; + continue; + } + for (ch, v) in v.iter_mut().enumerate() { let mut val = pcm.voice.sample(cycle, ch as _); @@ -281,6 +283,12 @@ impl<'a> Moo<'a> for RPxToneMoo<'a> { } }, WoiceType::OGGV(oggv) => { + + if clock_ticks > (on.start + on.length) as f32 { + data.on = None; + continue; + } + for (ch, v) in v.iter_mut().enumerate() { let mut val = oggv.voice.sample(cycle, ch as _); @@ -298,7 +306,34 @@ impl<'a> Moo<'a> for RPxToneMoo<'a> { } }, WoiceType::PTV(ptv) => { + + let max_env_release_samples = ptv.voices.iter().map(|v| { + if v.envelope.tail_num > 0 { + v.envelope.points[v.envelope.head_num as usize].x * self.sample_rate / v.envelope.fps + } else { + 0 + } + }).max().unwrap_or(0); + + // samples / samples/sec * ticks/seconds = ticks + let max_env_release_ticks = (max_env_release_samples as f32 / self.sample_rate as f32 * ticks_per_sec) as u32; + + if clock_ticks > (on.start + on.length + max_env_release_ticks) as f32 { + data.on = None; + continue; + } + for voice in &ptv.voices { + + let env_release_samples = if voice.envelope.tail_num > 0 { + voice.envelope.points[voice.envelope.head_num as usize].x * self.sample_rate / voice.envelope.fps + } else { + 0 + }; + + // samples / samples/sec * ticks/seconds = ticks + let env_release_ticks = env_release_samples as f32 / self.sample_rate as f32 * ticks_per_sec; + for (ch, v) in v.iter_mut().enumerate() { let mut val = voice.sample(cycle, ch as _); @@ -309,6 +344,10 @@ impl<'a> Moo<'a> for RPxToneMoo<'a> { val *= (cycle * 44100.0) / smooth_smps as f32; } + if clock_ticks > (on.start + on.length) as f32 { + val *= (1.0 - (clock_ticks - (on.start + on.length) as f32) / env_release_ticks).clamp(0.0, 1.0); + } + *v += val * *data.volume * *data.velocity diff --git a/src/pxtone/rust_impl/woice.rs b/src/pxtone/rust_impl/woice.rs index a0eb4c6..c108bfa 100644 --- a/src/pxtone/rust_impl/woice.rs +++ b/src/pxtone/rust_impl/woice.rs @@ -6,10 +6,7 @@ use crate::{ interface::{ service::InvalidText, woice::{ - HasWoices, PTNEnvelopePoint, PTNOscillator, PTNUnit, PTNWaveType, PTVCoordinateWave, - PTVCoordinateWavePoint, PTVOvertoneWave, PTVOvertoneWaveTone, PTVWaveType, SingleVoice, - Voice, VoiceOGGV, VoicePCM, VoicePTN, VoicePTV, Woice, WoiceOGGV, WoicePCM, WoicePTN, - WoicePTV, WoiceTypeMut, WoiceTypeRef, Woices, WoicesMut, + HasWoices, PTNEnvelopePoint, PTNOscillator, PTNUnit, PTNWaveType, PTVCoordinateWave, PTVCoordinateWavePoint, PTVEnvelope, PTVOvertoneWave, PTVOvertoneWaveTone, PTVWaveType, SingleVoice, Voice, VoiceOGGV, VoicePCM, VoicePTN, VoicePTV, Woice, WoiceOGGV, WoicePCM, WoicePTN, WoicePTV, WoiceTypeMut, WoiceTypeRef, Woices, WoicesMut }, }, util::{BoxOrMut, BoxOrRef}, @@ -233,6 +230,7 @@ pub struct RPxToneVoicePTV { pub(crate) tuning: f32, pub(crate) wave: RPxTonePTVWaveType, + pub(crate) envelope: RPXTonePTVEnvelope, pub(crate) samples: Vec, pub(crate) ratio_to_a: f32, @@ -246,6 +244,7 @@ impl RPxToneVoicePTV { pan: i32, tuning: f32, wave: RPxTonePTVWaveType, + envelope: RPXTonePTVEnvelope, ) -> Self { let sample_num = 400; let channels = 2; @@ -279,6 +278,7 @@ impl RPxToneVoicePTV { pan, tuning, wave, + envelope, samples, ratio_to_a, } @@ -327,6 +327,7 @@ impl Voice for RPxToneVoicePTV { impl VoicePTV for RPxToneVoicePTV { type CoordinateWave = RPxTonePTVCoordinateWave; type OvertoneWave = RPxTonePTVOvertoneWave; + type Envelope = RPXTonePTVEnvelope; fn wave(&self) -> PTVWaveType { match &self.wave { @@ -334,6 +335,10 @@ impl VoicePTV for RPxToneVoicePTV { RPxTonePTVWaveType::Overtone(o) => PTVWaveType::Overtone(o), } } + + fn envelope(&self) -> &Self::Envelope { + &self.envelope + } } impl VoicePCM for RPxToneVoicePTV { @@ -476,6 +481,39 @@ impl PTVOvertoneWaveTone for RPxTonePTVOvertoneWaveTone { } } +#[derive(Default)] +pub struct RPXTonePTVEnvelope { + pub(crate) fps: u32, + pub(crate) head_num: u32, + pub(crate) body_num: u32, + pub(crate) tail_num: u32, + pub(crate) points: Vec, +} + +impl PTVEnvelope for RPXTonePTVEnvelope { + type EnvelopePoint = RPxTonePTNEnvelopePoint; + + fn fps(&self) -> u32 { + self.fps + } + + fn head_num(&self) -> u32 { + self.head_num + } + + fn body_num(&self) -> u32 { + self.body_num + } + + fn tail_num(&self) -> u32 { + self.tail_num + } + + fn points(&self) -> Vec<&Self::EnvelopePoint> { + self.points.iter().collect() + } +} + pub struct RPxToneWoicePTN { pub(crate) voice: RPxToneVoicePTN, } @@ -626,6 +664,7 @@ impl PTNUnit for RPxTonePTNUnit { } } +#[derive(Debug)] pub struct RPxTonePTNEnvelopePoint { pub(crate) x: u32, pub(crate) y: u8,