The article material is taken from my .

Creating a Tone Generator
In the previous we installed the Mediastreamer library, development tools, and tested their functionality by building a sample application.
Today, we will create an application that can emit a tone signal on the sound card. To achieve this, we need to connect the filters according to the sound generator scheme shown below:
![]()
We read the scheme from left to right; this is the direction in which our data flows. The arrows indicate this as well. The rectangles represent filters that process blocks of data and output the results. Inside each rectangle is its role, with the type of filter indicated in uppercase letters just below it. The arrows connecting the rectangles represent data queues through which data blocks are delivered from one filter to another. In general, a filter can have multiple inputs and outputs.
It all starts with a clock source that sets the tempo at which data is processed in the filters. With each clock pulse, every filter processes all the data blocks at its input and outputs the result blocks into the queue. First, the closest filter to the clock source processes, followed by filters connected to its outputs (there can be many outputs), and so on. After the last filter in the chain finishes processing, the execution stops until a new clock pulse arrives. By default, clock pulses occur at intervals of 10 milliseconds.
Let's return to our scheme. The pulses are fed into the silence source, which is a filter that generates a block of data containing zeros at each pulse on its output. If we consider this block as a block of sound samples, it is nothing other than silence. At first glance, it seems strange to generate data blocks of silence—after all, it cannot be heard—but these blocks are necessary for the operation of the sound signal generator. The generator uses these blocks as a blank sheet of paper, writing sound samples into them. In its normal state, the generator is turned off and simply passes the input blocks to the output. Thus, the silence blocks pass unchanged through the entire scheme from left to right, reaching the sound card, which silently takes the blocks from the queue connected to its input.
But everything changes when the generator receives a command to play sound; it starts generating sound samples, replacing them in the input blocks and outputting the modified blocks. The sound card begins to produce sound. Below is the program that implements the scheme described above:
/* Файл mstest2.c */
#include <mediastreamer2/msfilter.h>
#include <mediastreamer2/msticker.h>
#include <mediastreamer2/dtmfgen.h>
#include <mediastreamer2/mssndcard.h>
int main()
{
ms_init();
/* Создаем экземпляры фильтров. */
MSFilter *voidsource = ms_filter_new(MS_VOID_SOURCE_ID);
MSFilter *dtmfgen = ms_filter_new(MS_DTMF_GEN_ID);
MSSndCard *card_playback = ms_snd_card_manager_get_default_card(ms_snd_card_manager_get());
MSFilter *snd_card_write = ms_snd_card_create_writer(card_playback);
/* Создаем тикер. */
MSTicker *ticker = ms_ticker_new();
/* Соединяем фильтры в цепочку. */
ms_filter_link(voidsource, 0, dtmfgen, 0);
ms_filter_link(dtmfgen, 0, snd_card_write, 0);
/* Подключаем источник тактов. */
ms_ticker_attach(ticker, voidsource);
/* Включаем звуковой генератор. */
char key='1';
ms_filter_call_method(dtmfgen, MS_DTMF_GEN_PLAY, (void*)&key);
/* Даем, время, чтобы все блоки данных были получены звуковой картой.*/
ms_sleep(2);
}After initializing the media streamer, three filters are created: voidsource, dtmfgen, snd_card_write. A clock signal source is created.
Next, we need to connect the filters according to our scheme, with the clock source being connected last, as this will immediately start the operation of the scheme. If the clock source is connected to an unfinished scheme, it may happen that the media streamer terminates unexpectedly if it detects at least one filter in the chain with all its inputs or all outputs "hanging in the air" (not connected).
Connecting the filters is done using the function
ms_filter_link(src, src_out, dst, dst_in)where the first argument is a pointer to the source filter, the second argument is the output number of the source (note that inputs and outputs are numbered starting from zero). The third argument is a pointer to the receiving filter, and the fourth is the input number of the receiver.
All filters are connected and the clock source is the last to connect (hereafter we will simply call it the ticker). After that, our audio scheme is activated, but there is still no sound from the computer's speakers — the sound generator is off and simply passes the input data blocks with silence. To start generating a tonal signal, the generator's filter method needs to be executed.
We will generate a dual-tone (DTMF) signal corresponding to pressing the '1' button on the phone. To do this, we will use the function ms_filter_call_method() to call the method MS_DTMF_GEN_PLAY, passing as an argument a pointer to the code corresponding to the signal to be played.
Now it's time to compile the program:
$ gcc mstest2.c -o mstest2 `pkg-config mediastreamer --libs --cflags`And run it:
$ ./mstest2After starting the program, you will hear a short sound signal consisting of two tones through the computer's speakers.
We constructed and launched our first audio scheme. We saw how to create filter instances, how to connect them, and how to call their methods. Having celebrated our first success, we still need to pay attention to the fact that our program does not free the allocated memory before finishing. In the next we will learn to clean up after ourselves.
Source: habr.com
