@@ -5,7 +5,7 @@ use crate::{
55 song:: { PlayerConfig , Song } ,
66} ;
77use std:: {
8- collections:: { HashSet , VecDeque } ,
8+ collections:: { HashMap , HashSet } ,
99 time:: { Duration , Instant } ,
1010} ;
1111
@@ -194,21 +194,24 @@ pub enum MidiEventSource {
194194 User ,
195195}
196196
197+ type NoteId = u8 ;
198+
197199#[ derive( Debug ) ]
198- struct UserPress {
200+ struct NotePress {
199201 timestamp : Instant ,
200- note_id : u8 ,
201202}
202203
203204#[ derive( Debug ) ]
204205pub struct PlayAlong {
205206 user_keyboard_range : piano_math:: KeyboardRange ,
206207
207- required_notes : HashSet < u8 > ,
208-
209- // List of user key press events that happened in last 500ms,
210- // used for play along leeway logic
211- user_pressed_recently : VecDeque < UserPress > ,
208+ /// Notes required to proggres further in the song
209+ required_notes : HashMap < NoteId , NotePress > ,
210+ /// List of user key press events that happened in last 500ms,
211+ /// used for play along leeway logic
212+ user_pressed_recently : HashMap < NoteId , NotePress > ,
213+ /// File notes that had NoteOn event, but no NoteOff yet
214+ in_proggres_file_notes : HashSet < NoteId > ,
212215}
213216
214217impl PlayAlong {
@@ -217,61 +220,51 @@ impl PlayAlong {
217220 user_keyboard_range,
218221 required_notes : Default :: default ( ) ,
219222 user_pressed_recently : Default :: default ( ) ,
223+ in_proggres_file_notes : Default :: default ( ) ,
220224 }
221225 }
222226
223227 fn update ( & mut self ) {
228+ // Instead of calling .elapsed() per item let's fetch `now` once, and subtract it ourselves
224229 let now = Instant :: now ( ) ;
225230 let threshold = Duration :: from_millis ( 500 ) ;
226231
227232 // Retain only the items that are within the threshold
228- self . user_pressed_recently . retain ( |item| {
229- let elapsed = now - item. timestamp ;
230- elapsed <= threshold
231- } ) ;
233+ self . user_pressed_recently
234+ . retain ( |_, item| now. duration_since ( item. timestamp ) <= threshold) ;
232235 }
233236
234237 fn user_press_key ( & mut self , note_id : u8 , active : bool ) {
235- if active {
236- let timestamp = Instant :: now ( ) ;
237- // Check if note_id already exists in the collection
238- if let Some ( item) = self
239- . user_pressed_recently
240- . iter_mut ( )
241- . find ( |item| item. note_id == note_id)
242- {
243- // Update the timestamp for existing note_id
244- item. timestamp = timestamp;
245- } else {
246- // Push a new UserPress
247- self . user_pressed_recently
248- . push_back ( UserPress { timestamp, note_id } ) ;
249- }
238+ let timestamp = Instant :: now ( ) ;
250239
251- // Check if note_id is in required_notes
252- if self . required_notes . contains ( & note_id) {
253- // If it's in required_notes, remove it from presed_recently to avoid skips/repeated count
240+ if active {
241+ // Check if note has already been played by a file
242+ if self . required_notes . remove ( & note_id) . is_none ( ) {
243+ // This note was not played by file yet, place it in recents
254244 self . user_pressed_recently
255- . retain ( |item| item . note_id != note_id ) ;
245+ . insert ( note_id, NotePress { timestamp } ) ;
256246 }
257- self . required_notes . remove ( & note_id) ;
258247 }
259248 }
260249
261250 fn file_press_key ( & mut self , note_id : u8 , active : bool ) {
251+ let timestamp = Instant :: now ( ) ;
262252 if active {
263- if let Some ( ( id, _) ) = self
264- . user_pressed_recently
265- . iter ( )
266- . enumerate ( )
267- . find ( |( _, item) | item. note_id == note_id)
268- {
269- self . user_pressed_recently . remove ( id) ;
270- } else {
271- self . required_notes . insert ( note_id) ;
253+ // Check if note got pressed earlier 500ms (user_pressed_recently)
254+ if self . user_pressed_recently . remove ( & note_id) . is_none ( ) {
255+ // Player never pressed that note, let it reach required_notes
256+
257+ // Ignore overlapping notes
258+ if self . in_proggres_file_notes . contains ( & note_id) {
259+ return ;
260+ }
261+
262+ self . required_notes . insert ( note_id, NotePress { timestamp } ) ;
272263 }
264+
265+ self . in_proggres_file_notes . insert ( note_id) ;
273266 } else {
274- self . required_notes . remove ( & note_id) ;
267+ self . in_proggres_file_notes . remove ( & note_id) ;
275268 }
276269 }
277270
@@ -295,7 +288,9 @@ impl PlayAlong {
295288 }
296289
297290 pub fn clear ( & mut self ) {
298- self . required_notes . clear ( )
291+ self . required_notes . clear ( ) ;
292+ self . user_pressed_recently . clear ( ) ;
293+ self . in_proggres_file_notes . clear ( ) ;
299294 }
300295
301296 pub fn are_required_keys_pressed ( & self ) -> bool {
0 commit comments