LADI
/
spa
1
Fork 0
spa/doc/tutorial4.dox

159 lines
5.3 KiB
Plaintext

/** \page page_tutorial4 Tutorial - Part 4: Playing A Tone
\ref page_tutorial3 | \ref page_tutorial "Index" | \ref page_tutorial5
In this tutorial we show how to use a stream to play a tone.
Let's take a look at the code before we break it down:
\snippet tutorial4.c code
Save as tutorial4.c and compile with:
gcc -Wall tutorial4.c -o tutorial4 -lm $(pkg-config --cflags --libs libpipewire-0.3)
We start with the usual boilerplate, `pw_init()` and a `pw_main_loop_new()`.
We're going to store our objects in a structure so that we can pass them
around in callbacks later.
\code{.c}
struct data {
struct pw_main_loop *loop;
struct pw_stream *stream;
double accumulator;
};
int main(int argc, char *argv[])
{
struct data data = { 0, };
pw_init(&argc, &argv);
data.loop = pw_main_loop_new(NULL);
\endcode
Next we create a stream object. It takes the mainloop as first argument and
a stream name as the second. Next we provide some properties for the stream
and a callback + data.
\code{.c}
data.stream = pw_stream_new_simple(
pw_main_loop_get_loop(data.loop),
"audio-src",
pw_properties_new(
PW_KEY_MEDIA_TYPE, "Audio",
PW_KEY_MEDIA_CATEGORY, "Playback",
PW_KEY_MEDIA_ROLE, "Music",
NULL),
&stream_events,
&data);
\endcode
We are using `pw_stream_new_simple()` but there is also a `pw_stream_new()` that
takes an existing `struct pw_core` as the first argument and that requires you
to add the event handle manually, for more control. The `pw_stream_new_simple()`
is, as the name implies, easier to use because it creates a `struct pw_context`
and `struct pw_core` automatically.
In the properties we need to give as much information about the stream as we
can so that the session manager can make good decisions about how and where
to route this stream. There are three important properties to configure:
- `PW_KEY_MEDIA_TYPE`: The media type; like Audio, Video, MIDI.
- `PW_KEY_MEDIA_CATEGORY`: The category; like Playback, Capture, Duplex, Monitor.
- `PW_KEY_MEDIA_ROLE`: The media role; like Movie, Music, Camera, Screen,
Communication, Game, Notification, DSP, Production, Accessibility, Test.
The properties are owned by the stream and freed when the stream is destroyed
later.
This is the event structure that we use to listen for events:
\code{.c}
static const struct pw_stream_events stream_events = {
PW_VERSION_STREAM_EVENTS,
.process = on_process,
};
\endcode
We are for the moment only interested now in the `process` event. This event
is called whenever we need to produce more data. We'll see how that function
is implemented but first we need to setup the format of the stream:
\code{.c}
const struct spa_pod *params[1];
uint8_t buffer[1024];
struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
#define DEFAULT_RATE 44100
#define DEFAULT_CHANNELS 2
params[0] = spa_format_audio_raw_build(&b, SPA_PARAM_EnumFormat,
&SPA_AUDIO_INFO_RAW_INIT(
.format = SPA_AUDIO_FORMAT_S16,
.channels = DEFAULT_CHANNELS,
.rate = DEFAULT_RATE ));
\endcode
This is using a `struct spa_pod_builder` to make a `struct spa_pod *` object
in the buffer array on the stack. The parameter is of type `SPA_PARAM_EnumFormat`
which means that it enumerates the possible formats for this stream. We have
only one, a Signed 16 bit stereo format at 44.1KHz.
We use `spa_format_audio_raw_build()` which is a helper function to make the param
with the builder. See \ref page_spa_pod for more information about how to
make these POD objects.
Now we're ready to connect the stream and run the main loop:
\code{.c}
pw_stream_connect(data.stream,
PW_DIRECTION_OUTPUT,
PW_ID_ANY,
PW_STREAM_FLAG_AUTOCONNECT |
PW_STREAM_FLAG_MAP_BUFFERS |
PW_STREAM_FLAG_RT_PROCESS,
params, 1);
pw_main_loop_run(data.loop);
\endcode
To connect we specify that we have a `PW_DIRECTION_OUTPUT` stream. The third argument
is always `PW_ID_ANY`. Next we set some flags:
- `PW_STREAM_FLAG_AUTOCONNECT`: Automatically connect this stream. This instructs
the session manager to link us to some consumer.
- `PW_STREAM_FLAG_MAP_BUFFERS`: mmap the buffers for us so we can access the
memory. If you don't set these flags you have either work with the fd or mmap
yourself.
- `PW_STREAM_FLAG_RT_PROCESS`: Run the process function in the realtime thread.
Only use this if the process function only uses functions that are realtime
safe, this means no allocation or file access or any locking.
And last we pass the extra parameters for our stream. Here we only have the
allowed formats (`SPA_PARAM_EnumFormat`).
Running the mainloop will then start processing and will result in our
`process` callback to be called. Let's have a look at that function now.
The main program flow of the process function is:
- `pw_stream_dequeue_buffer()` to obtain a buffer to write into.
- Get pointers in buffer memory to write to.
- Write data into buffer.
- Adjust buffer with number of written bytes, offset, stride.
- `pw_stream_queue_buffer()` to queue the buffer for playback.
\snippet tutorial4.c on_process
Check out the docs for \ref page_spa_buffer for more information
about how to work with buffers.
Try to change the number of channels, samplerate or format; the stream
will automatically convert to the format on the server.
\ref page_tutorial3 | \ref page_tutorial "Index" | \ref page_tutorial5
*/