Skip to content

Commit b36ed5a

Browse files
committed
freeplay: require note events for preview and save
Prevent controller-only recordings, such as sustain pedal input/only note off, from being treated as previewable or saveable takes.
1 parent 5532873 commit b36ed5a

1 file changed

Lines changed: 61 additions & 9 deletions

File tree

  • neothesia/src/scene/freeplay

neothesia/src/scene/freeplay/mod.rs

Lines changed: 61 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -119,9 +119,12 @@ impl FreeplayRecorder {
119119
matches!(self.state, RecorderState::Recording(_))
120120
}
121121

122-
fn has_events(&self) -> bool {
122+
fn has_note_events(&self) -> bool {
123123
match &self.state {
124-
RecorderState::Recorded(recorded_take) => !recorded_take.events.is_empty(),
124+
// Preview/export requires at least one played note, not just release/control data.
125+
RecorderState::Recorded(recorded_take) => recorded_take.events.iter().any(|event| {
126+
matches!(event.message, MidiMessage::NoteOn { .. })
127+
}),
125128
RecorderState::Idle | RecorderState::Recording(_) => false,
126129
}
127130
}
@@ -188,8 +191,8 @@ impl FreeplayRecorder {
188191
}
189192

190193
fn save_to_path(&self, path: &Path) -> Result<(), String> {
191-
if !self.has_events() {
192-
return Err("Nothing recorded yet".to_string());
194+
if !self.has_note_events() {
195+
return Err("No note events recorded yet".to_string());
193196
}
194197

195198
let smf = self.to_smf();
@@ -200,8 +203,8 @@ impl FreeplayRecorder {
200203
}
201204

202205
fn to_song(&self) -> Result<Song, String> {
203-
if !self.has_events() {
204-
return Err("Nothing recorded yet".to_string());
206+
if !self.has_note_events() {
207+
return Err("No note events recorded yet".to_string());
205208
}
206209

207210
let midi = midi_file::MidiFile::from_smf("freeplay-recording.mid", self.to_smf())?;
@@ -293,15 +296,64 @@ mod freeplay_recorder_tests {
293296
);
294297
recorder.stop();
295298

296-
assert!(recorder.has_events());
299+
assert!(recorder.has_note_events());
297300
assert_eq!(recorder.event_count(), 2);
298301

299302
recorder.start();
300303

301304
assert!(recorder.is_recording());
302-
assert!(!recorder.has_events());
305+
assert!(!recorder.has_note_events());
303306
assert_eq!(recorder.event_count(), 0);
304307
}
308+
309+
#[test]
310+
fn pedal_only_recording_is_rejected_for_preview_song() {
311+
let mut recorder = FreeplayRecorder::default();
312+
313+
recorder.start();
314+
recorder.push_event(
315+
0,
316+
MidiMessage::Controller {
317+
controller: 64.into(),
318+
value: 127.into(),
319+
},
320+
);
321+
recorder.push_event(
322+
0,
323+
MidiMessage::Controller {
324+
controller: 64.into(),
325+
value: 0.into(),
326+
},
327+
);
328+
recorder.stop();
329+
330+
assert!(!recorder.has_note_events());
331+
let error = recorder
332+
.to_song()
333+
.expect_err("pedal-only recordings should not create preview songs");
334+
assert_eq!(error, "No note events recorded yet");
335+
}
336+
337+
#[test]
338+
fn note_off_only_recording_is_rejected_for_preview_song() {
339+
let mut recorder = FreeplayRecorder::default();
340+
341+
recorder.start();
342+
recorder.push_event(
343+
0,
344+
MidiMessage::NoteOff {
345+
key: 60.into(),
346+
vel: 0.into(),
347+
},
348+
);
349+
recorder.stop();
350+
351+
assert!(!recorder.has_note_events());
352+
let error = recorder
353+
.to_song()
354+
.expect_err("note-off-only recordings should not create preview songs");
355+
assert_eq!(error, "No note events recorded yet");
356+
}
305357
}
306358

307359
impl FreeplayScene {
@@ -531,7 +583,7 @@ impl FreeplayScene {
531583
return;
532584
}
533585

534-
if !self.recorder.has_events() {
586+
if !self.recorder.has_note_events() {
535587
self.recorder_status = "Nothing recorded yet".to_string();
536588
return;
537589
}

0 commit comments

Comments
 (0)