This is the second of a series of posts about SQA, my pet audio project. I’m rebuilding it from the ground up, for reasons explained here.

In the last week, I created the sqa-engine crate, and got it up and running playing a WAV file with sqa-jack. Progress is being made!

This week’s commit log

Commits are given in reverse chronological order, with the most recent appearing first.

sqa-jack

  • d1068a2 on 2016-12-31: Allow registering/deregistering ports anytime

sqa-engine

  • 2a54e1f on 2017-01-02: volume, more strenuous tests
  • 064307e on 2016-12-31: use skip_n() method of bufs, cargo updates
  • 35b2f00 on 2016-12-31: added chan system & sadistic tests
  • 4a52a1d on 2016-12-31: removed warnings, added plain senders, press Enter
  • 1b59918 on 2016-12-31: Proof of concept: it plays back a WAV file!

Other crates

  • I made a pull request to the bounded_spsc_queue repo to add some functionality I’m now using in sqa-engine.

This week’s changes in detail

The big change this week, as mentioned in the title, is that we now have a sqa-engine crate in the mix. This handles all the actual audio playing - making sure that the audio arrives at its destination on time, playing at the correct volume, etc.

Consumers of this crate get the following struct to play with:

struct Sender<T> {
    position: Arc<AtomicU64>,
    active: Arc<AtomicBool>,
    alive: Arc<AtomicBool>,
    start_time: Arc<AtomicU64>,
    output_patch: Arc<AtomicUsize>,
    sync: Arc<AtomicBool>,
    volume: Arc<AtomicU32>,
    buf: T,
    sample_rate: u64,
    original: bool
}

Yup, lots of atomics! Essentially, how it works is you set start_time to when you want the audio to start playing (ideally, you do this in advance), set the output_patch to a channel number (which you can create with the main EngineContext struct), set active to true, then start feeding data into the buf (which is a bounded_spsc_queue::Producer<f32>).

Ideally, you’d spawn a dedicated spooler thread to feed data in as fast as possible. The point is, you don’t have to worry about dealing with the scary realtime audio callback (which you get with other APIs that requires you to provide data in x milliseconds or your audio glitches) - you just send your data in and it plays it on time. This will prove instrumental for SQA - and I’ll be documenting and publishing the library next week, so that other Rustaceans can make use of it.

Screenshot of the week

This week’s screenshot shows the new SQA Engine being put under a 32-channel stress test: it was playing one WAV file across 16 left and 16 right channels, to see how it would cope under the load.

JACK wiring diagram showing SQA wired into the system output

This test was conducted using a buffer size of 256 samples - meaning the SQA engine had roughly 5 ms to process each buffer. It handled this well, with there being no buffer underruns (that means no audio glitches at all) and taking only 30% of the time it had to perform this task.


That’s it for this week - stay tuned for next week’s devlog!