Exploring the Mediastreamer2 VoIP engine. Part 2

The material of the article is taken from my zen channel.

Exploring the Mediastreamer2 VoIP engine. Part 2

Create a tone generator

In the previous article we installed the media streamer library, development tools and tested their functionality by building a trial application.

Today we will create an application that will be able to read a tone signal on a sound card. To solve this problem, we need to connect the filters in the sound generator circuit shown below:

Exploring the Mediastreamer2 VoIP engine. Part 2

We read the diagram from left to right, it is in this direction that the data flow is moving. The arrows point to this too. Rectangles indicate filters that process blocks of data and expose the result to the output. Inside the rectangle, its role is indicated and a little lower, in capital letters, the filter type. The arrows connecting the rectangles are the data queues that carry blocks of data from filter to filter. In general, a filter can have many inputs and outputs.

It all starts with the clock source, which sets the pace at which data is calculated in the filters. According to its cycle, each filter processes all data blocks that are at its input. And exposes blocks with the result to the output - to the queue. First, it calculates the filter closest to the clock source, then the filters connected to its outputs (there can be many outputs), and so on. After the last filter in the chain has finished counting, execution stops until a new clock arrives. Clocks, by default, follow with an interval of 10 milliseconds.

Let's return to our scheme. The cycles are fed to the input of the source of silence, this is a filter, which generates a block of data containing zeros at its output for each cycle. If we consider this block as a block of sound samples, then this is nothing but silence. At first glance, it seems strange to generate data blocks with silence - because it cannot be heard, but these blocks are necessary for the sound signal generator to work. 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 forwards the input blocks to the output. Thus, silence blocks pass unchanged through the entire circuit from left to right, getting into the sound card. Which silently picks up blocks from the queue connected to its input.

But everything changes if the generator is given a command to play the sound, it starts generating sound samples and replaces the samples in the input blocks with them and sets the changed blocks to the output. The sound card starts playing sound. Below is a program that implements the above scheme of work:

/* Π€Π°ΠΉΠ» 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 the media streamer is initialized, three filters are created: voidsource, dtmfgen, snd_card_write. A clock source is created.

Then you need to connect the filters in accordance with our circuit, and the clock source must be connected last, since after that the circuit will immediately begin to work. If you connect a clock source to an incomplete circuit, it may happen that the media streamer will crash if it detects at least one filter in the chain with all inputs or all outputs "hanging in the air" (not connected).

Filters are connected 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 source output number (note that inputs and outputs are numbered starting from zero). The third argument is a pointer to the receiver filter, the fourth is the receiver's input number.

All filters are connected and the clock source is connected last (hereinafter we will simply call it a ticker). After that, our sound circuit starts working, but nothing is heard in the computer dynamics yet - the sound generator is turned off and simply forwards the input data blocks through itself with silence. To start tone generation, you need to execute the generator filter method.

We will generate a two-tone (DTMF) signal corresponding to pressing the "1" button on the phone. To do this, we use the function ms_filter_call_method() we call the MS_DTMF_GEN_PLAY method, passing it as an argument a pointer to the code to which the reproduced signal should correspond.

It remains to compile the program:

$ gcc mstest2.c -o mstest2 `pkg-config mediastreamer --libs --cflags`

And run:

$ ./mstest2

After starting the program, you will hear a short beep consisting of two tones in the computer speaker.

We built and launched our first sound circuit. We saw how to instantiate filters, how to connect and how to call their methods. While rejoicing at the first success, we still need to pay attention to the fact that our program does not free the allocated memory before terminating. Next article we learn to clean up after ourselves.

Source: habr.com

Add a comment