mirror of
				https://gitlab.freedesktop.org/pipewire/pipewire.git
				synced 2025-11-03 09:01:54 -05:00 
			
		
		
		
	
		
			
				
	
	
		
			160 lines
		
	
	
	
		
			5.4 KiB
		
	
	
	
		
			Text
		
	
	
	
	
	
			
		
		
	
	
			160 lines
		
	
	
	
		
			5.4 KiB
		
	
	
	
		
			Text
		
	
	
	
	
	
/** \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 3 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. `PW_ID_ANY`
 | 
						|
means that we are ok with connecting to any consumer. 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
 | 
						|
 | 
						|
*/
 |