summaryrefslogtreecommitdiffstats
path: root/doc/tutorial4.dox
diff options
context:
space:
mode:
Diffstat (limited to 'doc/tutorial4.dox')
-rw-r--r--doc/tutorial4.dox158
1 files changed, 158 insertions, 0 deletions
diff --git a/doc/tutorial4.dox b/doc/tutorial4.dox
new file mode 100644
index 0000000..b8d1707
--- /dev/null
+++ b/doc/tutorial4.dox
@@ -0,0 +1,158 @@
+/** \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
+
+*/