ΠΠ°ΡΠ΅ΡΠΈΠ°Π» ΡΡΠ°ΡΡΠΈ Π²Π·ΡΡ Ρ ΠΌΠΎΠ΅Π³ΠΎ
Π‘ΠΎΠ·Π΄Π°Π΅ΠΌ ΠΈΠ·ΠΌΠ΅ΡΠΈΡΠ΅Π»Ρ ΡΡΠΎΠ²Π½Ρ ΡΠΈΠ³Π½Π°Π»Π°
Π ΠΏΡΠΎΡΠ»ΠΎΠΉ
Π ΡΡΠΎΠΉ ΡΡΠ°ΡΡΠ΅ ΠΌΡ ΡΠΎΠ±Π΅ΡΠ΅ΠΌ ΡΡ Π΅ΠΌΡ ΠΈΠ·ΠΌΠ΅ΡΠΈΡΠ΅Π»Ρ ΡΡΠΎΠ²Π½Ρ ΡΠΈΠ³Π½Π°Π»Π° ΠΈ Π½Π°ΡΡΠΈΠΌΡΡ ΡΠΈΡΠ°ΡΡ ΡΠ΅Π·ΡΠ»ΡΡΠ°Ρ ΠΈΠ·ΠΌΠ΅ΡΠ΅Π½ΠΈΡ ΠΈΠ· ΡΠΈΠ»ΡΡΡΠ°. ΠΡΠ΅Π½ΠΈΠΌ ΡΠΎΡΠ½ΠΎΡΡΡ ΠΈΠ·ΠΌΠ΅ΡΠ΅Π½ΠΈΡ.
Π Π½Π°Π±ΠΎΡΠ΅ ΡΠΈΠ»ΡΡΡΠΎΠ², ΠΏΡΠ΅Π΄ΠΎΡΡΠ°Π²Π»ΡΠ΅ΠΌΡΡ ΠΌΠ΅Π΄ΠΈΠ°ΡΡΡΠΈΠΌΠ΅ΡΠΎΠΌ Π΅ΡΡΡ ΡΠΈΠ»ΡΡΡ, MS_VOLUME, ΠΊΠΎΡΠΎΡΡΠΉ ΡΠΏΠΎΡΠΎΠ±Π΅Π½ ΠΈΠ·ΠΌΠ΅ΡΡΡΡ ΡΡΠ΅Π΄Π½Π΅ΠΊΠ²Π°Π΄ΡΠ°ΡΠΈΡΠ΅ΡΠΊΠΈΠΉ ΡΡΠΎΠ²Π΅Π½Ρ ΠΏΡΠΎΡ ΠΎΠ΄ΡΡΠ΅Π³ΠΎ ΡΠ΅ΡΠ΅Π· Π½Π΅Π³ΠΎ ΡΠΈΠ³Π½Π°Π»Π°, ΠΎΡΠ»Π°Π±Π»ΡΡΡ ΡΠΈΠ³Π½Π°Π» ΠΈ Π²ΡΠΏΠΎΠ»Π½ΡΡΡ ΠΌΠ°ΡΡΡ ΠΏΠΎΠ»Π΅Π·Π½ΡΡ ΠΈ Π½Π΅ΠΎΠΆΠΈΠ΄Π°Π½Π½ΡΡ ΡΡΠ½ΠΊΡΠΈΠΉ. ΠΠΎΠ·Π΄Π½Π΅Π΅ ΠΌΡ ΠΏΠΎΡΠ²Π΅ΡΠΈΠΌ ΡΡΠΎΠΌΡ ΡΠΈΠ»ΡΡΡΡ ΡΠ΅Π»ΡΡ ΡΡΠ°ΡΡΡ. ΠΠΎ ΡΠ΅ΠΉΡΠ°Ρ ΠΌΡ Π±ΡΠ΄Π΅ΠΌ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ Π΅Π³ΠΎ ΠΊΠ°ΠΊ ΠΈΠ·ΠΌΠ΅ΡΠΈΡΠ΅Π»Ρ.
Π ΠΊΠ°ΡΠ΅ΡΡΠ²Π΅ ΠΈΡΡΠΎΡΠ½ΠΈΠΊΠ° ΡΠΈΠ³Π½Π°Π»Π° Π±ΡΠ΄Π΅ΠΌ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ ΡΠΎΠ½Π°Π»ΡΠ½ΡΠΉ Π³Π΅Π½Π΅ΡΠ°ΡΠΎΡ, ΡΠΈΠ³Π½Π°Π» Ρ ΠΊΠΎΡΠΎΡΠΎΠ³ΠΎ Π½Π°ΠΏΡΠ°Π²ΠΈΠΌ Π½Π° ΡΠΈΠ»ΡΡΡ MS_VOLUME, ΠΊ Π²ΡΡ ΠΎΠ΄Ρ ΠΊΠΎΡΠΎΡΠΎΠ³ΠΎ ΠΏΠΎΠ΄ΠΊΠ»ΡΡΠ΅Π½Π° Π·Π²ΡΠΊΠΎΠ²Π°Ρ ΠΊΠ°ΡΡΠ°.
Π Π΄Π°Π½Π½ΠΎΠΌ ΠΏΡΠΈΠΌΠ΅ΡΠ΅ ΡΠΈΠ»ΡΡΡ Π³Π΅Π½Π΅ΡΠ°ΡΠΎΡΠ° ΠΌΡ Π±ΡΠ΄Π΅ΠΌ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ Π² Π½Π΅ΡΠΊΠΎΠ»ΡΠΊΠΎ Π΄ΡΡΠ³ΠΎΠΌ ΡΠ΅ΠΆΠΈΠΌΠ΅ β ΠΎΠ½ Π±ΡΠ΄Π΅Ρ Π³Π΅Π½Π΅ΡΠΈΡΠΎΠ²Π°ΡΡ Π΄Π»Ρ Π½Π°Ρ ΠΎΠ΄Π½ΠΎΡΠΎΠ½Π°Π»ΡΠ½ΡΠΉ ΡΠΈΠ³Π½Π°Π», Ρ.Π΅. ΡΠΈΠ³Π½Π°Π» ΡΠΎΠ΄Π΅ΡΠΆΠ°ΡΠΈΠΉ ΡΠΎΠ»ΡΠΊΠΎ ΠΎΠ΄Π½ΠΎ ΡΠΈΠ½ΡΡΠΎΠΈΠ΄Π°Π»ΡΠ½ΠΎΠ΅ ΠΊΠΎΠ»Π΅Π±Π°Π½ΠΈΠ΅.
ΠΠΎΠΌΠΈΠΌΠΎ ΡΠ°ΡΡΠΎΡΡ ΠΈ Π°ΠΌΠΏΠ»ΠΈΡΡΠ΄Ρ Π½Π°ΠΌ ΠΏΠΎΠ½Π°Π΄ΠΎΠ±ΠΈΡΡΡ Π·Π°Π΄Π°ΡΡ Π²ΡΠ΅ΠΌΡ, Π² ΡΠ΅ΡΠ΅Π½ΠΈΠ΅ ΠΊΠΎΡΠΎΡΠΎΠ³ΠΎ Π±ΡΠ΄Π΅Ρ Π³Π΅Π½Π΅ΡΠΈΡΠΎΠ²Π°ΡΡΡΡ ΡΠΈΠ³Π½Π°Π», ΠΎΠ½ΠΎ Π΄ΠΎΠ»ΠΆΠ½ΠΎ Π±ΡΡΡ Π΄ΠΎΡΡΠ°ΡΠΎΡΠ½ΡΠΌ, ΡΡΠΎΠ±Ρ ΡΠ΅ΡΠ΅Π· ΡΠΈΠ»ΡΡΡ MS_VOLUME ΠΏΡΠΎΡΠ»ΠΎ Π΄ΠΎΡΡΠ°ΡΠΎΡΠ½ΠΎΠ΅ Π΄Π»Ρ ΠΈΠ·ΠΌΠ΅ΡΠ΅Π½ΠΈΠ΅ ΠΊΠΎΠ»ΠΈΡΠ΅ΡΡΠ²ΠΎ ΠΎΡΡΡΠ΅ΡΠΎΠ². ΠΠ»Ρ ΠΏΠ΅ΡΠ΅Π΄Π°ΡΠΈ Π³Π΅Π½Π΅ΡΠ°ΡΠΎΡΡ Π½Π°ΡΡΡΠΎΠ΅ΠΊ ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΡΡΡ ΡΡΡΡΠΊΡΡΡΠ° MSDtmfGenCustomTone:
struct _MSDtmfGenCustomTone{
char tone_name[8]; /* Π’Π΅ΠΊΡΡΠΎΠ²ΠΎΠ΅ Π½Π°Π·Π²Π°Π½ΠΈΠ΅ ΡΠΈΠ³Π½Π°Π»Π° ΠΈΠ· 8 Π±ΡΠΊΠ².*/
int duration; /* ΠΠ»ΠΈΡΠ΅Π»ΡΠ½ΠΎΡΡΡ ΡΠΈΠ³Π½Π°Π»Π° Π² ΠΌΠΈΠ»Π»ΠΈΡΠ΅ΠΊΡΠ½Π΄Π°Ρ
.*/
int frequencies[2]; /* ΠΠ°ΡΠ° ΡΠ°ΡΡΠΎΡ ΠΈΠ· ΠΊΠΎΡΠΎΡΡΡ
Π΄ΠΎΠ»ΠΆΠ΅Π½ ΡΠΎΡΡΠΎΡΡΡ Π²ΡΡ
ΠΎΠ΄Π½ΠΎΠΉ ΡΠΈΠ³Π½Π°Π». */
float amplitude; /* ΠΠΌΠΏΠ»ΠΈΡΡΠ΄Π° ΡΠΎΠ½ΠΎΠ², 1.0 ΡΠΎΠΎΡΠ²Π΅ΡΡΡΠ²ΡΠ΅Ρ ΡΡΠΎΠ²Π½Ρ 0 Π΄Π ΠΎΡ ΠΌΠΈΠ»Π»ΠΈΠ²Π°ΡΡΠ° Π½Π° Π½Π°Π³ΡΡΠ·ΠΊΠ΅ 600 ΠΠΌ.*/
int interval; /* ΠΠ°ΡΠ·Π° Π² ΠΌΠΈΠ»Π»ΠΈΡΠ΅ΠΊΡΠ½Π΄Π°Ρ
ΠΏΠ΅ΡΠ΅Π΄ Π½Π°ΡΠ°Π»ΠΎΠΌ ΠΏΠΎΠ²ΡΠΎΡΠ½ΠΎΠ³ΠΎ ΠΏΡΠΎΠΈΠ³ΡΡΠ²Π°Π½ΠΈΡ ΡΠΈΠ³Π½Π°Π»Π°.*/
int repeat_count; /* ΠΠΎΠ»ΠΈΡΠ΅ΡΡΠ²ΠΎ ΠΏΠΎΠ²ΡΠΎΡΠΎΠ².*/
};
typedef struct _MSDtmfGenCustomTone MSDtmfGenCustomTone;
Π§ΡΠΎΠ±Ρ Π·Π°ΠΏΡΡΡΠΈΡΡ Π³Π΅Π½Π΅ΡΠ°ΡΠΎΡ Π² ΡΠ°Π±ΠΎΡΡ, Π±ΡΠ΄Π΅ΠΌ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ Π΅Π³ΠΎ ΠΌΠ΅ΡΠΎΠ΄ MS_DTMF_GEN_PLAY_CUSTOM.
Π‘ΡΡΡΠΊΡΡΡΠ½Π°Ρ ΡΡ Π΅ΠΌΠ° ΠΎΠ±ΡΠ°Π±ΠΎΡΠΊΠΈ ΡΠΈΠ³Π½Π°Π»Π°:
ΠΠΎΠ΄ ΠΏΡΠΎΠ³ΡΠ°ΠΌΠΌΡ, ΡΠ΅Π°Π»ΠΈΠ·ΡΡΡΠΈΠΉ ΡΡΡ ΡΡ Π΅ΠΌΡ ΠΏΠΎΠΊΠ°Π·Π°Π½ Π½ΠΈΠΆΠ΅.
/* Π€Π°ΠΉΠ» mstest3.c */
#include <mediastreamer2/msfilter.h>
#include <mediastreamer2/msticker.h>
#include <mediastreamer2/dtmfgen.h>
#include <mediastreamer2/mssndcard.h>
#include <mediastreamer2/msvolume.h>
int main()
{
ms_init();
/* Π‘ΠΎΠ·Π΄Π°Π΅ΠΌ ΡΠΊΠ·Π΅ΠΌΠΏΠ»ΡΡΡ ΡΠΈΠ»ΡΡΡΠΎΠ². */
MSFilter *voidsource=ms_filter_new(MS_VOID_SOURCE_ID);
MSFilter *dtmfgen=ms_filter_new(MS_DTMF_GEN_ID);
MSFilter *volume=ms_filter_new(MS_VOLUME_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, volume, 0);
ms_filter_link(volume, 0, snd_card_write, 0);
/* ΠΠΎΠ΄ΠΊΠ»ΡΡΠ°Π΅ΠΌ ΠΈΡΡΠΎΡΠ½ΠΈΠΊ ΡΠ°ΠΊΡΠΎΠ². */
ms_ticker_attach(ticker,voidsource);
MSDtmfGenCustomTone dtmf_cfg;
/* Π£ΡΡΠ°Π½Π°Π²Π»ΠΈΠ²Π°Π΅ΠΌ ΠΈΠΌΡ Π½Π°ΡΠ΅Π³ΠΎ ΡΠΈΠ³Π½Π°Π»Π°, ΠΏΠΎΠΌΠ½Ρ ΠΎ ΡΠΎΠΌ, ΡΡΠΎ Π² ΠΌΠ°ΡΡΠΈΠ²Π΅ ΠΌΡ Π΄ΠΎΠ»ΠΆΠ½Ρ
ΠΎΡΡΠ°Π²ΠΈΡΡ ΠΌΠ΅ΡΡΠΎ Π΄Π»Ρ Π½ΡΠ»Ρ, ΠΊΠΎΡΠΎΡΡΠΉ ΠΎΠ±ΠΎΠ·Π½Π°ΡΠ°Π΅Ρ ΠΊΠΎΠ½Π΅Ρ ΡΡΡΠΎΠΊΠΈ. */
strncpy(dtmf_cfg.tone_name, "busy", sizeof(dtmf_cfg.tone_name));
dtmf_cfg.duration=1000;
dtmf_cfg.frequencies[0]=440; /* ΠΡΠ΄Π΅ΠΌ Π³Π΅Π½Π΅ΡΠΈΡΠΎΠ²Π°ΡΡ ΠΎΠ΄ΠΈΠ½ ΡΠΎΠ½, ΡΠ°ΡΡΠΎΡΡ Π²ΡΠΎΡΠΎΠ³ΠΎ ΡΠΎΠ½Π° ΡΡΡΠ°Π½ΠΎΠ²ΠΈΠΌ Π² 0.*/
dtmf_cfg.frequencies[1]=0;
dtmf_cfg.amplitude=1.0; /* Π’Π°ΠΊΠΎΠΉ Π°ΠΌΠΏΠ»ΠΈΡΡΠ΄Π΅ ΡΠΈΠ½ΡΡΠ° Π΄ΠΎΠ»ΠΆΠ΅Π½ ΡΠΎΠΎΡΠ²Π΅ΡΡΡΠ²ΠΎΠ²Π°ΡΡ ΡΠ΅Π·ΡΠ»ΡΡΠ°Ρ ΠΈΠ·ΠΌΠ΅ΡΠ΅Π½ΠΈΡ 0.707.*/
dtmf_cfg.interval=0.;
dtmf_cfg.repeat_count=0.;
/* ΠΠΊΠ»ΡΡΠ°Π΅ΠΌ Π·Π²ΡΠΊΠΎΠ²ΠΎΠΉ Π³Π΅Π½Π΅ΡΠ°ΡΠΎΡ. */
ms_filter_call_method(dtmfgen, MS_DTMF_GEN_PLAY_CUSTOM, (void*)&dtmf_cfg);
/* ΠΠ°Π΅ΠΌ, Π²ΡΠ΅ΠΌΡ ΠΏΠΎΠ»ΠΎΠ²ΠΈΠ½Ρ ΡΠ΅ΠΊΡΠ½Π΄Ρ, ΡΡΠΎΠ±Ρ ΠΈΠ·ΠΌΠ΅ΡΠΈΡΠ΅Π»Ρ Π½Π°ΠΊΠΎΠΏΠΈΠ» Π΄Π°Π½Π½ΡΠ΅. */
ms_usleep(500000);
/* Π§ΠΈΡΠ°Π΅ΠΌ ΡΠ΅Π·ΡΠ»ΡΡΠ°Ρ ΠΈΠ·ΠΌΠ΅ΡΠ΅Π½ΠΈΡ. */
float level=0;
ms_filter_call_method(volume, MS_VOLUME_GET_LINEAR,&level);
printf("ΠΠΌΠΏΠ»ΠΈΡΡΠ΄Π΅ ΡΠΈΠ½ΡΡΠ° %f Π²ΠΎΠ»ΡΡ ΡΠΎΠΎΡΠ²Π΅ΡΡΡΠ²ΡΠ΅Ρ ΡΡΠ΅Π΄Π½Π΅ΠΊΠ²Π°Π΄ΡΠ°ΡΠΈΡΠ΅ΡΠΊΠΎΠ΅ Π·Π½Π°ΡΠ΅Π½ΠΈΠ΅ %f Π²ΠΎΠ»ΡΡ.n", dtmf_cfg.amplitude, level);
}
ΠΠΎΠΌΠΏΠΈΠ»ΠΈΡΡΠ΅ΠΌ Π½Π°Ρ ΠΏΡΠΈΠΌΠ΅Ρ, ΡΠ°ΠΊΠΆΠ΅ ΠΊΠ°ΠΊ ΠΌΡ Π΄Π΅Π»Π°Π»ΠΈ Π΄ΠΎ ΡΡΠΎΠ³ΠΎ, ΡΠΎΠ»ΡΠΊΠΎ ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΡ Π½Π°Π·Π²Π°Π½ΠΈΠ΅ ΡΠ°ΠΉΠ»Π° mstest3. ΠΠ°ΠΏΡΡΠΊΠ°Π΅ΠΌ Π½Π° Π²ΡΠΏΠΎΠ»Π½Π΅Π½ΠΈΠ΅ ΠΈ ΠΏΠΎΠ»ΡΡΠΈΠΌ ΡΠ΅Π·ΡΠ»ΡΡΠ°Ρ:
ΠΠΌΠΏΠ»ΠΈΡΡΠ΄Π΅ ΡΠΈΠ½ΡΡΠ° 1.000000 Π²ΠΎΠ»ΡΡ ΡΠΎΠΎΡΠ²Π΅ΡΡΡΠ²ΡΠ΅Ρ ΡΡΠ΅Π΄Π½Π΅ΠΊΠ²Π°Π΄ΡΠ°ΡΠΈΡΠ΅ΡΠΊΠΎΠ΅ Π·Π½Π°ΡΠ΅Π½ΠΈΠ΅ 0.707733 Π²ΠΎΠ»ΡΡ.
ΠΠ°ΠΊ Π²ΠΈΠ΄ΠΈΡΠ΅, ΡΠ΅Π·ΡΠ»ΡΡΠ°Ρ ΠΈΠ·ΠΌΠ΅ΡΠ΅Π½ΠΈΡ ΡΠΎΠ²ΠΏΠ°Π» Π΄ΠΎ ΡΡΠ΅ΡΡΠ΅Π³ΠΎ Π·Π½Π°ΠΊΠ° ΠΏΠΎΡΠ»Π΅ Π·Π°ΠΏΡΡΠΎΠΉ Ρ ΡΠ΅ΠΎΡΠ΅ΡΠΈΡΠ΅ΡΠΊΠΈΠΌ Π·Π½Π°ΡΠ΅Π½ΠΈΠ΅ΠΌ ΡΠ°Π²Π½ΡΠΌ ΠΊΠ²Π°Π΄ΡΠ°ΡΠ½ΠΎΠΌΡ ΠΊΠΎΡΠ½Ρ ΠΈΠ· Π΄Π²ΠΎΠΉΠΊΠΈ ΠΏΠΎΠ΄Π΅Π»Π΅Π½Π½ΠΎΠΌΡ ΠΏΠΎΠΏΠΎΠ»Π°ΠΌ: sqr(2)/2=0,7071067811865475
ΠΡΠ½ΠΎΡΠΈΡΠ΅Π»ΡΠ½ΠΎΠ΅ ΠΎΡΠΊΠ»ΠΎΠ½Π΅Π½ΠΈΠ΅ ΡΠ΅Π·ΡΠ»ΡΡΠ°ΡΠ° ΠΎΡ ΠΈΡΡΠΈΠ½Π½ΠΎΠ³ΠΎ Π·Π½Π°ΡΠ΅Π½ΠΈΡ ΡΠΎΡΡΠ°Π²ΠΈΠ»ΠΎ 0.1%. ΠΡ ΡΠ΄Π΅Π»Π°Π»ΠΈ ΠΎΡΠ΅Π½ΠΊΡ ΠΏΠΎΠ³ΡΠ΅ΡΠ½ΠΎΡΡΠΈ ΠΈΠ·ΠΌΠ΅ΡΠ΅Π½ΠΈΡ ΠΏΡΠΈ ΠΌΠ°ΠΊΡΠΈΠΌΠ°Π»ΡΠ½ΠΎΠΌ ΡΡΠΎΠ²Π½Π΅ ΡΠΈΠ³Π½Π°Π»Π°. Π‘ΠΎΠΎΡΠ²Π΅ΡΡΡΠ²Π΅Π½Π½ΠΎ, ΠΏΡΠΈ ΡΠ½ΠΈΠΆΠ΅Π½ΠΈΠΈ ΡΡΠΎΠ²Π½Ρ ΠΏΠΎΠ³ΡΠ΅ΡΠ½ΠΎΡΡΡ Π΄ΠΎΠ»ΠΆΠ½Π° Π²ΠΎΠ·ΡΠ°ΡΡΠ°ΡΡ. ΠΡΠ΅Π΄Π»Π°Π³Π°Ρ Π²Π°ΠΌ ΡΠ°ΠΌΠΎΡΡΠΎΡΡΠ΅Π»ΡΠ½ΠΎ ΠΎΡΠ΅Π½ΠΈΡΡ Π΅Ρ Π΄Π»Ρ ΠΌΠ°Π»ΡΡ ΡΡΠΎΠ²Π½Π΅ΠΉ ΡΠΈΠ³Π½Π°Π»Π°.
Π ΡΠ»Π΅Π΄ΡΡΡΠ΅ΠΉ ΡΡΠ°ΡΡΠ΅ ΠΌΡ ΡΠΎΠ±Π΅ΡΠ΅ΠΌ ΡΡ Π΅ΠΌΡ ΠΎΠ±Π½Π°ΡΡΠΆΠΈΠ²Π°ΡΡΡΡ ΠΏΡΠΈΡΡΡΡΡΠ²ΠΈΠ΅ Π½Π° Π²Ρ ΠΎΠ΄Π΅ ΡΠΎΠ½Π°Π»ΡΠ½ΠΎΠ³ΠΎ ΡΠΈΠ³Π½Π°Π»Π° Π·Π°Π΄Π°Π½Π½ΠΎΠΉ ΡΠ°ΡΡΠΎΡΡ Ρ ΠΏΠΎΠΌΠΎΡΡΡ Π΄Π΅ΡΠ΅ΠΊΡΠΎΡΠ° ΡΠΎΠ½ΠΎΠ². Π ΡΠ°ΠΊΠΆΠ΅ Π½Π°ΡΡΠΈΠΌΡΡ ΠΎΠ±ΡΠ°Π±Π°ΡΡΠ²Π°ΡΡ ΡΠΎΠ±ΡΡΠΈΡ Π³Π΅Π½Π΅ΡΠΈΡΡΠ΅ΠΌΡΠ΅ ΡΠΈΠ»ΡΡΡΠ°ΠΌΠΈ.
ΠΡΡΠΎΡΠ½ΠΈΠΊ: habr.com