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();
        }
    }
}