use rivet_core::*; fn held() -> Phrase { Phrase { length: PPQ, notes: vec![ Note { start: 0, duration: PPQ / 2, pitch: 60, velocity: 90, ..Default::default() }, Note { start: PPQ / 2, duration: PPQ / 2, pitch: 60, velocity: 70, ..Default::default() }, ], } } #[test] fn role_handoffs_preserve_event_identity_and_respect_locks() { let mut source = demo_motif(); source.notes[1].pitch_locked = true; source.notes[0].locked = true; for mode in RoleMode::ALL { let mut s = PerformanceSettings { mode, ..Default::default() }; s.voices[0].transpose = -12; s.voices[1].transpose = 12; s.voices[0].velocity = 50; s.voices[1].velocity = 50; let p = Performance::compile(&source, &s, 140., false, Meter::default()).unwrap(); let on: Vec<_> = p .events .iter() .filter(|e| e.message.kind == MessageKind::On && !e.next_cycle) .collect(); assert_eq!(on.len(), source.notes.len()); for (i, n) in source.notes.iter().enumerate() { let role = mode.role(i, n, Meter::default()); let actual = on.iter().find(|e| e.tick == n.start as i32).unwrap(); assert_eq!(actual.message.channel, s.voices[role].channel); assert_eq!( actual.message.key, if n.keeps_pitch() { n.pitch } else { (n.pitch as i16 + s.voices[role].transpose as i16) as u8 } ); } assert_eq!(on[0].message.value, 90); } } #[test] fn controller_interpolation_and_validation_are_explicit() { let mut curve = ControllerCurve::default(); assert_eq!(curve.value_at(500), 64); curve.shape(2); assert_eq!(curve.value_at(500), 127); assert_eq!(curve.value_at(1000), 0); curve.cc = 64; assert!(curve.validate().is_err()); let mut s = PerformanceSettings::default(); s.voices[0].controller.enabled = true; s.voices[0].controller.cc = 20; let p = Performance::compile(&held(), &s, 120., false, Meter::default()).unwrap(); assert!(p .events .iter() .any(|e| e.message.kind == MessageKind::Control && e.message.key == 20 && e.message.value == 127)); s.voices[0].controller.points[1].position = 0; assert!(s.validate().is_err()); } #[test] fn attack_compensation_warms_up_once_and_wraps_without_cutting_the_next_note() { let mut s = PerformanceSettings::default(); s.voices[0].attack_ms = 100; let performance = Performance::compile(&held(), &s, 120., true, Meter::default()).unwrap(); assert_eq!(performance.lead_in, 192); assert!(performance .events .iter() .any(|e| e.tick == -192 && e.message.kind == MessageKind::On && !e.next_cycle)); let mut player = PerformancePlayer::default(); player.set_sample_rate(8000.).unwrap(); player.set_looping(true); player.play(performance.clip().unwrap()); let mut ons = Vec::new(); for frame in 0..4802 { let (_, events) = player.next_frame(120.); for e in events.events[..events.len].iter().flatten() { if e.kind == MessageKind::On { ons.push(frame); } } } assert_eq!(ons.len(), 3); assert_eq!(ons[0], 0); assert!((ons[2] as i32 - 4000).abs() <= 1); assert!(player .current_messages() .events .iter() .flatten() .any(|e| e.kind == MessageKind::On && e.key == 60)); player.set_looping(false); player.play(performance.clip().unwrap()); let mut count = 0; while player.is_playing() { let (_, events) = player.next_frame(120.); count += events .events .iter() .flatten() .filter(|e| e.kind == MessageKind::On) .count(); } assert_eq!(count, 2); } #[test] fn public_clip_validation_rejects_bad_roles_and_dense_events() { let mut p = Performance::compile( &held(), &PerformanceSettings::default(), 120., false, Meter::default(), ) .unwrap(); p.events[0].role = 9; assert!(p.clip().is_err()); let event = TimedMessage { tick: 0, role: 0, message: MidiMessage { kind: MessageKind::On, channel: 0, key: 60, value: 90, rest: 0, }, next_cycle: false, }; let p = Performance { length: PPQ, lead_in: 0, events: vec![event; 17], }; assert!(p.clip().is_err()); }