Преглед изворни кода

Implement note fallback to the mode module. Never set voices to None during note_on or note_off methods as this will be done when requesting frames for each voice from the Instrument type.

mitchmindtree пре 8 година
родитељ
комит
ff20c3adf6
1 измењених фајлова са 59 додато и 45 уклоњено
  1. 59 45
      src/mode.rs

+ 59 - 45
src/mode.rs

@@ -1,8 +1,10 @@
 pub use instrument::mode::{Mono, MonoKind, Poly, Dynamic};
+use instrument;
 use map::{self, Map};
 use pitch;
 use sample::Frame;
 use sampler::PlayingSample;
+use std;
 use Velocity;
 
 /// The "mode" with which the Sampler will handle notes.
@@ -54,12 +56,31 @@ impl Mode for Mono {
                   voices: &mut [Option<PlayingSample<A>>])
         where A: map::Audio,
     {
-        let sample = match play_sample(note_hz, note_vel, map) {
-            Some(sample) => sample,
-            None => return,
+        let Mono(ref kind, ref note_stack) = *self;
+
+        // If we're in `Legato` mode, begin the note from the same index as the previous note's
+        // current state if there is one.
+        let sample = if let instrument::mode::MonoKind::Legato = *kind {
+            note_stack.last()
+                .and_then(|&last_hz| {
+                    voices.iter()
+                        .filter_map(|v| v.as_ref())
+                        .find(|sample| instrument::mode::does_hz_match(sample.note_on_hz.hz(), last_hz))
+                        .and_then(|sample| {
+                            let idx = sample.rate_converter.source().idx;
+                            play_sample_from_playhead_idx(idx, note_hz, note_vel, map)
+                        })
+                })
+                .or_else(|| play_sample(note_hz, note_vel, map))
+        // Otherwise, we're in `Retrigger` mode, so start from the beginning of the sample.
+        } else {
+            play_sample(note_hz, note_vel, map)
         };
-        for voice in voices {
-            *voice = Some(sample.clone());
+
+        if let Some(sample) = sample {
+            for voice in voices {
+                *voice = Some(sample.clone());
+            }
         }
     }
 
@@ -71,32 +92,33 @@ impl Mode for Mono {
     {
         let Mono(kind, ref note_stack) = *self;
 
-        let should_reset = voices.iter().next()
-            .and_then(|v| v.as_ref().map(|v| v.note_on_hz == note_hz))
-            .unwrap_or(false);
+        let should_reset = voices.iter()
+            .filter_map(|v| v.as_ref())
+            .any(|v| instrument::mode::does_hz_match(v.note_on_hz.hz(), note_hz.hz()));
+
+        if !should_reset {
+            return;
+        }
 
-        if should_reset {
-            let maybe_fallback_note_hz = note_stack.iter().last();
+        // If there is some note to fall back to, do so.
+        if let Some(&fallback_note_hz) = note_stack.last() {
+            let hz = fallback_note_hz.into();
             for voice in voices {
-                // If there's some fallback note in the note stack, play it.
                 if let Some(ref mut playing_sample) = *voice {
-                    if let Some(&hz) = maybe_fallback_note_hz {
-                        let hz = pitch::Hz(hz.into());
-                        let idx = match kind {
-                            MonoKind::Legato => playing_sample.rate_converter.source().idx,
-                            MonoKind::Retrigger => 0,
-                        };
-                        let vel = playing_sample.note_on_vel;
-                        if let Some(sample) = play_sample_from_playhead_idx(idx, hz, vel, map) {
-                            *playing_sample = sample;
-                            continue;
-                        }
+                    let idx = match kind {
+                        MonoKind::Retrigger => 0,
+                        MonoKind::Legato => playing_sample.rate_converter.source().idx,
+                    };
+                    let vel = playing_sample.note_on_vel;
+                    if let Some(sample) = play_sample_from_playhead_idx(idx, hz, vel, map) {
+                        *playing_sample = sample;
                     }
                 }
-                // Otherwise, set the voices to `None`.
-                *voice = None;
             }
         }
+
+        // No need to manually set voices to `None` as this will be done when frames yielded by
+        // `instrument` run out.
     }
 
 }
@@ -117,20 +139,16 @@ impl Mode for Poly {
 
         // Find the right voice to play the note.
         let mut oldest = None;
-        let mut max_sample_count = 0;
+        let mut oldest_time_of_note_on = std::time::Instant::now();
         for voice in voices.iter_mut() {
-            match *voice {
-                None => {
-                    *voice = Some(sample);
-                    return;
-                },
-                Some(ref mut playing_sample) => {
-                    let playhead = playing_sample.rate_converter.source().idx;
-                    if playhead >= max_sample_count {
-                        max_sample_count = playhead;
-                        oldest = Some(playing_sample);
-                    }
-                },
+            if let None = *voice {
+                *voice = Some(sample);
+                return;
+            }
+            let time_of_note_on = voice.as_ref().unwrap().time_of_note_on;
+            if time_of_note_on < oldest_time_of_note_on {
+                oldest_time_of_note_on = time_of_note_on;
+                oldest = voice.as_mut();
             }
         }
         if let Some(voice) = oldest {
@@ -139,17 +157,13 @@ impl Mode for Poly {
     }
 
     fn note_off<A>(&self,
-                   note_hz: pitch::Hz,
+                   _note_hz: pitch::Hz,
                    _map: &Map<A>,
-                   voices: &mut [Option<PlayingSample<A>>])
+                   _voices: &mut [Option<PlayingSample<A>>])
         where A: map::Audio,
     {
-        for voice in voices {
-            let should_reset = voice.as_ref().map(|v| v.note_on_hz == note_hz).unwrap_or(false);
-            if should_reset {
-                *voice = None;
-            }
-        }
+        // No need to do anything here as voices will be set to `None` when frames yielded by
+        // `instrument` run out.
     }
 
 }