mirror of
				https://gitlab.freedesktop.org/pipewire/pipewire.git
				synced 2025-10-29 05:40:27 -04: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
 | |
| 
 | |
| */
 | 
