@@ -7,18 +7,14 @@ use rodio::Sink;
77mod oscillator;
88pub use oscillator:: Oscillator ;
99
10- mod osc;
11- use osc:: AnalogOsc ;
12-
13- // The envelope state struct
1410struct EnvelopeState {
1511 envelope : Envelope ,
1612 start_time : Instant ,
1713 is_releasing : bool ,
1814 release_start_time : Option < Instant > ,
15+ release_start_volume : Option < f32 > , // Track starting volume for release
1916}
2017
21- // The envelope struct
2218struct Envelope {
2319 attack : f32 ,
2420 decay : f32 ,
@@ -45,9 +41,6 @@ pub struct Synth {
4541
4642impl Synth {
4743 pub fn new ( stream_handle : rodio:: OutputStreamHandle ) -> Synth {
48- // let (_stream, stream_handle) = rodio::OutputStream::try_default().unwrap();
49- // For some reason the above code would fail if it was in the new function
50-
5144 Synth {
5245 audio_sinks : HashMap :: new ( ) ,
5346 envelope_states : HashMap :: new ( ) ,
@@ -59,12 +52,13 @@ impl Synth {
5952 let sink = Sink :: try_new ( & self . stream_handle ) . expect ( "Failed to create sink" ) ;
6053 sink. append ( audio_source) ;
6154
62- let envelope = Envelope :: new ( 0.1 , 0.2 , 0.7 , 1.3 ) ; // example envelope
55+ let envelope = Envelope :: new ( 0.8 , 0.2 , 0.7 , 1.3 ) ;
6356 let envelope_state = EnvelopeState {
6457 envelope,
6558 start_time : Instant :: now ( ) ,
6659 is_releasing : false ,
6760 release_start_time : None ,
61+ release_start_volume : None ,
6862 } ;
6963
7064 self . audio_sinks . insert ( source_id, sink) ;
@@ -73,48 +67,76 @@ impl Synth {
7367
7468 pub fn release_source ( & mut self , source_id : u8 ) {
7569 if let Some ( envelope_state) = self . envelope_states . get_mut ( & source_id) {
70+ let now = Instant :: now ( ) ;
71+ let elapsed = now. duration_since ( envelope_state. start_time ) . as_secs_f32 ( ) ;
72+ let envelope = & envelope_state. envelope ;
73+
74+ // Calculate current volume at release time
75+ let current_volume = if elapsed < envelope. attack {
76+ // Attack phase
77+ elapsed / envelope. attack
78+ } else if elapsed < envelope. attack + envelope. decay {
79+ // Decay phase
80+ 1.0 - ( elapsed - envelope. attack ) / envelope. decay * ( 1.0 - envelope. sustain )
81+ } else {
82+ // Sustain phase
83+ envelope. sustain
84+ } ;
85+
7686 envelope_state. is_releasing = true ;
77- envelope_state. release_start_time = Some ( Instant :: now ( ) ) ;
87+ envelope_state. release_start_time = Some ( now) ;
88+ envelope_state. release_start_volume = Some ( current_volume) ;
7889 }
7990 }
8091
8192 pub fn update ( & mut self ) {
8293 let now = Instant :: now ( ) ;
83-
8494 let mut to_remove = Vec :: new ( ) ;
8595
8696 for ( source_id, envelope_state) in self . envelope_states . iter_mut ( ) {
87- let elapsed = now. duration_since ( envelope_state. start_time ) . as_secs_f32 ( ) ;
88-
89- let envelope = & envelope_state. envelope ;
9097 let sink = self . audio_sinks . get_mut ( source_id) . unwrap ( ) ;
98+ let envelope = & envelope_state. envelope ;
9199
92- let volume = if elapsed < envelope. attack {
93- // Attack
94- elapsed / envelope. attack
95- } else if elapsed < envelope. attack + envelope. decay {
96- // Decay
97- 1.0 - ( elapsed - envelope. attack ) / envelope. decay * ( 1.0 - envelope. sustain )
98- } else if envelope_state. is_releasing {
99- // Release
100- let elapsed_since_released = now
100+ let volume = if envelope_state. is_releasing {
101+ // Release phase - use captured start volume and release time
102+ let elapsed_release = now
101103 . duration_since ( envelope_state. release_start_time . unwrap ( ) )
102104 . as_secs_f32 ( ) ;
103- envelope. sustain - elapsed_since_released / envelope. release * envelope. sustain
105+
106+ let start_volume = envelope_state. release_start_volume . unwrap ( ) ;
107+ let t = ( elapsed_release / envelope. release ) . min ( 1.0 ) ;
108+ start_volume * ( 1.0 - t)
104109 } else {
105- // Sustain
106- envelope. sustain
110+ // Calculate based on ADSR phases
111+ let elapsed = now. duration_since ( envelope_state. start_time ) . as_secs_f32 ( ) ;
112+
113+ if elapsed < envelope. attack {
114+ // Attack phase
115+ elapsed / envelope. attack
116+ } else if elapsed < envelope. attack + envelope. decay {
117+ // Decay phase
118+ 1.0 - ( elapsed - envelope. attack ) / envelope. decay * ( 1.0 - envelope. sustain )
119+ } else {
120+ // Sustain phase
121+ envelope. sustain
122+ }
107123 } ;
108124
109125 sink. set_volume ( volume) ;
110126
111- if envelope_state. is_releasing && elapsed > envelope. release {
112- // This is done as a separate step to avoid a second mutable borrow of self.envelope_states
113- // First borrow is when .iter_mut() is called, second is when .remove() is called
114- to_remove. push ( * source_id) ;
127+ // Check if release is complete
128+ if envelope_state. is_releasing {
129+ let elapsed_release = now
130+ . duration_since ( envelope_state. release_start_time . unwrap ( ) )
131+ . as_secs_f32 ( ) ;
132+
133+ if elapsed_release >= envelope. release {
134+ to_remove. push ( * source_id) ;
135+ }
115136 }
116137 }
117138
139+ // Cleanup completed sounds
118140 for source_id in to_remove {
119141 self . envelope_states . remove ( & source_id) ;
120142 self . audio_sinks . remove ( & source_id) ;
0 commit comments