1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
//! Objects for asynchronous notification of audio thread events. use parking_lot::{Mutex, Condvar}; use std::sync::Arc; use std::time::{Duration, Instant}; use bounded_spsc_queue::{Producer, Consumer}; use super::CONTROL_BUFFER_SIZE; use bounded_spsc_queue; use uuid::Uuid; pub use thread::Player; /// A message from the audio thread. pub enum AudioThreadMessage { /// The player with a given `Uuid` was successfully added. PlayerAdded(Uuid), /// This player was rejected due to you exceeding `MAX_PLAYERS`. PlayerRejected(Player), /// This player was removed on account of not being `alive`. PlayerRemoved(Player), /// The player with a given `Uuid` has an invalid output patch. Playback has been stopped. /// /// To resume playback, you MUST change the output patch to a valid channel /// number, and call `set_active(true)`. PlayerInvalidOutpatch(Uuid), /// The player with a given `Uuid`'s buffer is half full. /// /// This is just to let you know, so that you can start refilling the buffer before it /// runs out. PlayerBufHalf(Uuid), /// The player with a given `Uuid`'s buffer is empty. /// /// This could simply mean that playback has ended, or it could mean that you didn't /// refill the buffer and your audio has now stopped. In the latter case, you OUGHT TO refill the /// buffer. PlayerBufEmpty(Uuid), /// The audio thread has experienced an under- or over- run. /// /// This REALLY SHOULD NOT happen under normal circumstances. If your sample rate and buffer size /// are set to reasonable values, [file a bug!](https://github.com/eeeeeta/sqa-engine). /// You MAY WISH TO inform the user that their audio just glitched, and that they should adjust /// the sample rate and buffer size to remedy the problem. Xrun } /// A commmunication channel to receive messages from the audio thread. pub struct AudioThreadHandle { inner: Arc<(Mutex<()>, Condvar)>, rx: Consumer<AudioThreadMessage> } impl AudioThreadHandle { /// This function MUST NOT be used by consumers of the library. /// It's just here because I can't help it. pub unsafe fn make() -> (AudioThreadHandle, AudioThreadSender) { let (p, c) = bounded_spsc_queue::make(CONTROL_BUFFER_SIZE); let arc = Arc::new((Mutex::new(()), Condvar::new())); (AudioThreadHandle { inner: arc.clone(), rx: c }, AudioThreadSender { inner: arc, tx: p, written_t: 0, cur_t: 1 }) } /// Attempt to receive a message from the audio thread, returning `None` if none is available. pub fn try_recv(&mut self) -> Option<AudioThreadMessage> { self.rx.try_pop() } /// Wait (forever, if necessary) until a message is available, and return it. /// /// This blocks the thread on a condition variable, consuming no CPU time whilst blocked. pub fn recv(&mut self) -> AudioThreadMessage { if let Some(x) = self.rx.try_pop() { return x; } let mut lock = self.inner.0.lock(); loop { self.inner.1.wait(&mut lock); if let Some(x) = self.rx.try_pop() { return x; } } } /// Wait until a message is available, timing out after the specified time instant. Return a /// message if obtained in the time period, otherwise `None`. /// /// The semantics of this function are equivalent to `recv()` except that the thread will be /// blocked roughly until the timeout is reached. This method should not be used for precise timing /// due to anomalies such as preemption or platform differences that may not cause the maximum /// amount of time waited to be precisely the value given. /// /// Note that the best effort is made to ensure that the time waited is measured with a monotonic /// clock, and not affected by the changes made to the system time. pub fn wait_until(&mut self, timeout: Instant) -> Option<AudioThreadMessage> { if let Some(x) = self.rx.try_pop() { return Some(x); } let mut lock = self.inner.0.lock(); self.inner.1.wait_until(&mut lock, timeout); self.rx.try_pop() } /// Wait until a message is available, timing out after a specified duration. Return a /// message if obtained in the time period, otherwise `None`. /// /// The semantics of this function are equivalent to `recv()` except that the thread will be /// blocked for roughly no longer than `timeout`. This method should not be used for precise /// timing due to anomalies such as preemption or platform differences that may not cause the /// maximum amount of time waited to be precisely `timeout`. /// /// Note that the best effort is made to ensure that the time waited is measured with a monotonic /// clock, and not affected by the changes made to the system time. pub fn wait_for(&mut self, timeout: Duration) -> Option<AudioThreadMessage> { if let Some(x) = self.rx.try_pop() { return Some(x); } let mut lock = self.inner.0.lock(); self.inner.1.wait_for(&mut lock, timeout); self.rx.try_pop() } } /// Hesssk! My `struct`! Not for you! /// /// This struct MUST NOT (and in fact, can't) be used by consumers of the library. /// It's just here because I can't help it. pub struct AudioThreadSender { inner: Arc<(Mutex<()>, Condvar)>, tx: Producer<AudioThreadMessage>, written_t: u64, cur_t: u64 } impl AudioThreadSender { #[inline(always)] pub fn init(&mut self, t: u64) { self.cur_t = t; } #[inline(always)] pub fn send(&mut self, data: AudioThreadMessage) { self.written_t = self.cur_t; if let Some(remnant) = self.tx.try_push(data) { // If we can't send the data to the main thread for deallocation, // we don't want to deallocate it in the audio thread! ::std::mem::forget(remnant); } } #[inline(always)] pub fn notify(&mut self) { if self.written_t == self.cur_t { self.inner.1.notify_one(); } } }