audio_stream
Overview
| This page documents q_io, the optional audio and MIDI I/O library, not q_lib. See q_io. |
audio_stream connects a processing function to an audio device. It opens the device with the channel counts asked for, and once started it calls process from the device’s own thread, once per buffer of frames samples, until stopped. process reads the input buffers and fills the output buffers; the stream does the rest.
You use it by deriving a class from audio_stream, choosing the constructor arguments for the channels you need, and overriding the one process overload that matches, of the three audio_stream_base declares:
-
process(out_channels const& out): output only, a synthesizer or a player. Construct with zero input channels. -
process(in_channels const& in): input only, an analyzer or a recorder. Construct with zero output channels. -
process(in_channels const& in, out_channels const& out): an effect.
in and out are Multi Buffer views: out[ch] is channel ch, and out.frames the range of frame indices in this buffer. Whatever process leaves in out is what the device plays, so an output-only stream must write every frame of every channel, silence included.
process runs on a real-time thread. It must not allocate, lock, block or do I/O; anything of that kind belongs on the thread that called start.
Use Case
A sine wave to the default output device: derive, fill out in process, then start, keep the program alive, and stop. Nothing plays until start, and the callbacks end when stop returns. This is the Sine Oscillator tutorial in full.
struct sin_synth : q::audio_stream
{
sin_synth(q::frequency freq)
: audio_stream(0, 2) // no input, stereo out
, phase(freq, this->sampling_rate())
{}
void process(out_channels const& out) override
{
auto left = out[0];
auto right = out[1];
for (auto frame : out.frames)
right[frame] = left[frame] = q::sin(phase++);
}
q::phase_iterator phase;
};
int main()
{
sin_synth synth{440_Hz};
if (!synth.is_valid())
return 1; // synth.error() says why
synth.start();
q::sleep(5_s); // the audio thread plays
synth.stop();
}
Declaration
class audio_stream : public audio_stream_base
{
public:
audio_stream(
std::size_t input_channels
, std::size_t output_channels
, double sps = -1
, int frames = -1
);
audio_stream(
audio_device const& device
, std::size_t input_channels
, std::size_t output_channels
, double sps = -1
, int frames = -1
);
virtual ~audio_stream();
void start();
void stop();
bool is_valid() const;
duration time() const;
double cpu_load() const;
char const* error() const;
duration input_latency() const;
duration output_latency() const;
double sampling_rate() const;
std::size_t input_channels() const;
std::size_t output_channels() const;
};
Expressions
As a subclass of audio_stream_base, audio_stream inherits all the publicly accessible member functions, member variables, and types of its base class.
In addition to valid expressions for audio_stream_base, audio_stream allows these expressions.
Notation
as-
Object of type
audio_stream. ad-
Object of type
audio_device. ic-
Number of input channels.
oc-
Number of output channels.
sps-
Floating point value for the desired sample rate. A value of -1, the default, indicates a request to use the device’s default sampling rate.
fr-
Number of frames: the buffer size per channel. A value of -1, the default, requests 256. The device may use another size; the buffers passed to
processcarry the size of each call in theirframes.
Constructors
| Expression | Semantics |
|---|---|
|
Construct an |
|
Construct an |
Take note that audio_stream_base is non-copyable, and therefore audio_stream is also non-copyable.
Accessors
| Expression | Semantics | Return Type |
|---|---|---|
|
Check if the |
|
|
Get the stream’s time in seconds: the
audio it has processed since it was
opened. It starts at 0, advances one
buffer per call to |
|
|
Get stream’s CPU load: A floating point value, typically between 0.0 and 1.0, where 1.0 indicates that the stream is consuming the maximum number of CPU cycles possible to maintain real-time operation. |
|
|
A human readable error message, or an
empty string. On |
|
|
Get the stream’s latency, or 0 if it has no inputs. |
|
|
Get the stream’s latency, or 0 if it has no outputs. |
|
|
Get the number of input channels. |
|
|
Get the number of output channels. |
|
|
The sampling rate the stream opened at, which is the device’s default unless the constructor asked for another. |
|
The audio backend reports one latency for the whole stream. On a duplex stream, input_latency() and output_latency() both return it: the input and output latencies added together.
|
Mutators
| Expression | Semantics |
|---|---|
|
Start the stream: from here |
|
Stop the stream. Returns once the device has stopped calling |
Example
An effect, with both overloads' arguments: 1 input channel (mono) and two output channels (stereo). Route the mono input to both the left and right output channels.
struct my_processor : q::audio_stream (1)
{
my_processor()
: audio_stream(1, 2) (2)
{}
void process(in_channels const& in, out_channels const& out) (3)
{
auto left = out[0];
auto right = out[1];
auto mono_in = in[0];
for (auto frame : out.frames)
{
// Get the next input sample
auto s = mono_in[frame];
// Output
left[frame] = s;
right[frame] = s;
}
}
};
| 1 | Here we declare my_processor. A user-defined class derived from audio_stream. |
| 2 | Construct the base audio_stream with 1 input channel (mono) and two output channels (stereo) using the default audio device. |
| 3 | Implement the process member function. |
See Audio Stream Client Interface for more info.