Commit 57eca805 authored by Erwan Croze's avatar Erwan Croze 👋🏻

Feature/chan adapter mixer

parent 50c96337
...@@ -52,3 +52,4 @@ git-clang-format.diff ...@@ -52,3 +52,4 @@ git-clang-format.diff
tools/mkvstream tools/mkvstream
.bc_tester_utils.tmp .bc_tester_utils.tmp
**/.gradle **/.gradle
tester/sounds/mixed_file.wav
/* /*
mediastreamer2 library - modular sound and video processing and streaming mediastreamer2 library - modular sound and video processing and streaming
Copyright (C) 2010 Simon MORLAT (simon.morlat@linphone.org) Copyright (C) 2010-2018 Simon MORLAT (simon.morlat@linphone.org)
This program is free software; you can redistribute it and/or This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License modify it under the terms of the GNU General Public License
...@@ -19,121 +19,172 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ...@@ -19,121 +19,172 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "mediastreamer2/msfilter.h" #include "mediastreamer2/msfilter.h"
#include "mediastreamer2/mschanadapter.h" #include "mediastreamer2/mschanadapter.h"
#include "mediastreamer2/msticker.h"
/* /*
This filter transforms stereo buffers to mono and vice versa. This filter transforms stereo buffers to mono and vice versa.
*/ */
typedef struct AdapterState{ typedef struct AdapterState {
int inputchans; int inputchans;
int outputchans; int outputchans;
int sample_rate;
size_t buffer_size;
uint8_t *buffer1;
uint8_t *buffer2;
MSBufferizer input_buffer1;
MSBufferizer input_buffer2;
}AdapterState; }AdapterState;
static void adapter_init(MSFilter *f){ static void adapter_init(MSFilter *f) {
AdapterState *s=ms_new0(AdapterState,1); AdapterState *s = ms_new0(AdapterState, 1);
s->inputchans=1; s->inputchans = 1;
s->outputchans=1; s->outputchans = 1;
f->data=s; s->sample_rate = 8000;
f->data = s;
} }
static void adapter_uninit(MSFilter *f){ static void adapter_uninit(MSFilter *f) {
ms_free(f->data); AdapterState *s = (AdapterState*)f->data;
ms_free(s);
} }
static void adapter_process(MSFilter *f){ static void adapter_preprocess(MSFilter *f) {
AdapterState *s=(AdapterState*)f->data; AdapterState *s = (AdapterState*)f->data;
mblk_t *im,*om; if (s->inputchans == 2 && s->outputchans == 1) {
size_t msgsize; s->buffer_size = ((f->ticker->interval * s->sample_rate) / 1000) * 2;
s->buffer1 = ms_new(uint8_t, s->buffer_size);
while((im=ms_queue_get(f->inputs[0]))!=NULL){ s->buffer2 = ms_new(uint8_t, s->buffer_size);
if (s->inputchans==s->outputchans){ ms_bufferizer_init(&s->input_buffer1);
ms_queue_put(f->outputs[0],im); ms_bufferizer_init(&s->input_buffer2);
}else if (s->inputchans==2){ }
msgsize=msgdsize(im)/2; }
om=allocb(msgsize,0);
for (;im->b_rptr<im->b_wptr;im->b_rptr+=4,om->b_wptr+=2){ static void adapter_process(MSFilter *f) {
*(int16_t*)om->b_wptr=*(int16_t*)im->b_rptr; AdapterState *s = (AdapterState*)f->data;
// Two mono input t stereo output
if (s->inputchans == 2 && s->outputchans == 1) {
size_t buffer_size1, buffer_size2;
ms_bufferizer_put_from_queue(&s->input_buffer1, f->inputs[0]);
ms_bufferizer_put_from_queue(&s->input_buffer2, f->inputs[1]);
buffer_size1 = ms_bufferizer_get_avail(&s->input_buffer1);
buffer_size2 = ms_bufferizer_get_avail(&s->input_buffer2);
while (buffer_size1 >= s->buffer_size || buffer_size2 >= s->buffer_size) {
mblk_t *om;
if (buffer_size1 < s->buffer_size) memset(s->buffer1, 0, s->buffer_size);
if (buffer_size2 < s->buffer_size) memset(s->buffer2, 0, s->buffer_size);
ms_bufferizer_read(&s->input_buffer1, s->buffer1, s->buffer_size);
ms_bufferizer_read(&s->input_buffer2, s->buffer2, s->buffer_size);
om = allocb(s->buffer_size * 2, 0);
for (unsigned int i = 0 ; i < s->buffer_size / sizeof(int16_t) ; i++ , om->b_wptr += 4) {
((int16_t*)om->b_wptr)[0] = ((int16_t*)s->buffer1)[i];
((int16_t*)om->b_wptr)[1] = ((int16_t*)s->buffer2)[i];
} }
ms_queue_put(f->outputs[0],om);
freemsg(im); ms_queue_put(f->outputs[0], om);
}else if (s->outputchans==2){
msgsize=msgdsize(im)*2; buffer_size1 = ms_bufferizer_get_avail(&s->input_buffer1);
om=allocb(msgsize,0); buffer_size2 = ms_bufferizer_get_avail(&s->input_buffer2);
for (;im->b_rptr<im->b_wptr;im->b_rptr+=2,om->b_wptr+=4){ }
((int16_t*)om->b_wptr)[0]=*(int16_t*)im->b_rptr; } else {
((int16_t*)om->b_wptr)[1]=*(int16_t*)im->b_rptr; mblk_t *im, *om;
size_t msgsize;
while ((im = ms_queue_get(f->inputs[0])) != NULL) {
if (s->inputchans == s->outputchans) {
ms_queue_put(f->outputs[0], im);
} else if (s->outputchans == 2) {
msgsize = msgdsize(im) * 2;
om = allocb(msgsize, 0);
for (;im->b_rptr < im->b_wptr ; im->b_rptr += 2 ,om->b_wptr += 4){
((int16_t*)om->b_wptr)[0] = *(int16_t*)im->b_rptr;
((int16_t*)om->b_wptr)[1] = *(int16_t*)im->b_rptr;
}
ms_queue_put(f->outputs[0], om);
freemsg(im);
} }
ms_queue_put(f->outputs[0],om);
freemsg(im);
} }
} }
} }
static int adapter_set_nchannels(MSFilter *f, void *data){ static void adapter_postprocess(MSFilter *f) {
AdapterState *s=(AdapterState*)f->data; AdapterState *s = (AdapterState*)f->data;
s->inputchans=*(int*)data; ms_bufferizer_uninit(&s->input_buffer1);
ms_bufferizer_uninit(&s->input_buffer2);
if (s->buffer1) ms_free(s->buffer1);
if (s->buffer2) ms_free(s->buffer2);
s->buffer1 = NULL;
s->buffer2 = NULL;
}
static int adapter_set_sample_rate(MSFilter *f, void *data) {
AdapterState *s = (AdapterState*)f->data;
s->sample_rate = *(int*)data;
return 0;
}
static int adapter_get_sample_rate(MSFilter *f, void *data) {
AdapterState *s = (AdapterState*)f->data;
*(int*)data = s->sample_rate;
return 0;
}
static int adapter_set_nchannels(MSFilter *f, void *data) {
AdapterState *s = (AdapterState*)f->data;
s->inputchans = *(int*)data;
return 0; return 0;
} }
static int adapter_get_nchannels(MSFilter *f, void *data){ static int adapter_get_nchannels(MSFilter *f, void *data) {
AdapterState *s=(AdapterState*)f->data; AdapterState *s = (AdapterState*)f->data;
*(int*)data=s->inputchans; *(int*)data = s->inputchans;
return 0; return 0;
} }
static int adapter_set_out_nchannels(MSFilter *f, void *data){ static int adapter_set_out_nchannels(MSFilter *f, void *data) {
AdapterState *s=(AdapterState*)f->data; AdapterState *s = (AdapterState*)f->data;
s->outputchans=*(int*)data; s->outputchans = *(int*)data;
return 0; return 0;
} }
static int adapter_get_out_nchannels(MSFilter *f, void *data){ static int adapter_get_out_nchannels(MSFilter *f, void *data) {
AdapterState *s=(AdapterState*)f->data; AdapterState *s = (AdapterState*)f->data;
*(int*)data=s->outputchans; *(int*)data = s->outputchans;
return 0; return 0;
} }
static MSFilterMethod methods[]={ static MSFilterMethod methods[] = {
{ MS_FILTER_SET_SAMPLE_RATE, adapter_set_sample_rate},
{ MS_FILTER_GET_SAMPLE_RATE, adapter_get_sample_rate},
{ MS_FILTER_SET_NCHANNELS , adapter_set_nchannels }, { MS_FILTER_SET_NCHANNELS , adapter_set_nchannels },
{ MS_FILTER_GET_NCHANNELS, adapter_get_nchannels }, { MS_FILTER_GET_NCHANNELS, adapter_get_nchannels },
{ MS_CHANNEL_ADAPTER_SET_OUTPUT_NCHANNELS, adapter_set_out_nchannels }, { MS_CHANNEL_ADAPTER_SET_OUTPUT_NCHANNELS, adapter_set_out_nchannels },
{ MS_CHANNEL_ADAPTER_GET_OUTPUT_NCHANNELS, adapter_get_out_nchannels }, { MS_CHANNEL_ADAPTER_GET_OUTPUT_NCHANNELS, adapter_get_out_nchannels },
{ 0, NULL } { 0, NULL }
}; };
MSFilterDesc ms_channel_adapter_desc = {
#ifdef _MSC_VER
MSFilterDesc ms_channel_adapter_desc={
MS_CHANNEL_ADAPTER_ID, MS_CHANNEL_ADAPTER_ID,
"MSChannelAdapter", "MSChannelAdapter",
N_("A filter that converts from mono to stereo and vice versa."), N_("A filter that converts from mono to stereo and vice versa."),
MS_FILTER_OTHER, MS_FILTER_OTHER,
NULL, NULL,
1, 2,
1, 1,
adapter_init, adapter_init,
NULL, adapter_preprocess,
adapter_process, adapter_process,
NULL, adapter_postprocess,
adapter_uninit, adapter_uninit,
methods methods
}; };
#else
MSFilterDesc ms_channel_adapter_desc={
.id=MS_CHANNEL_ADAPTER_ID,
.name="MSChannelAdapter",
.text=N_("A filter that converts from mono to stereo and vice versa."),
.category=MS_FILTER_OTHER,
.ninputs=1,
.noutputs=1,
.init=adapter_init,
.process=adapter_process,
.uninit=adapter_uninit,
.methods=methods
};
#endif
MS_FILTER_DESC_EXPORT(ms_channel_adapter_desc) MS_FILTER_DESC_EXPORT(ms_channel_adapter_desc)
...@@ -29,6 +29,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ...@@ -29,6 +29,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "mediastreamer2_tester_private.h" #include "mediastreamer2_tester_private.h"
#include "private.h" #include "private.h"
#include <sys/stat.h>
static MSFactory *msFactory = NULL; static MSFactory *msFactory = NULL;
static int basic_audio_tester_before_all(void) { static int basic_audio_tester_before_all(void) {
...@@ -532,6 +534,105 @@ static void dtmfgen_filerec_fileplay_tonedet(void) { ...@@ -532,6 +534,105 @@ static void dtmfgen_filerec_fileplay_tonedet(void) {
free(recorded_file); free(recorded_file);
} }
#define SOUND_TEST_1 "sounds/hello8000.wav"
#define SOUND_TEST_2 "sounds/arpeggio_8000_mono.wav"
#define RECORD_SOUND "sounds/mixed_file.wav"
static void two_mono_into_one_stereo(void) {
//struct stat sound_file1, sound_file2, sound_record;
//unsigned int max_sound_size;
player_callback_data player1_data, player2_data;
MSFilter *mixer_mono, *player1, *player2;
unsigned int filter_mask = FILTER_MASK_FILEREC;
int sample_rate1, sample_rate2, nb_channels = 2;
char* played_file1 = bc_tester_res(SOUND_TEST_1);
char* played_file2 = bc_tester_res(SOUND_TEST_2);
char* recorded_file = bc_tester_res(RECORD_SOUND);
player1_data.end_of_file = FALSE;
player2_data.end_of_file = FALSE;
ms_factory_reset_statistics(msFactory);
ms_tester_create_ticker();
ms_tester_create_filters(filter_mask, msFactory);
player1 = ms_factory_create_filter(msFactory, MS_FILE_PLAYER_ID);
player2 = ms_factory_create_filter(msFactory, MS_FILE_PLAYER_ID);
mixer_mono = ms_factory_create_filter(msFactory, MS_CHANNEL_ADAPTER_ID);
ms_filter_add_notify_callback(player1, player_cb, &player1_data, TRUE);
ms_filter_add_notify_callback(player2, player_cb, &player2_data, TRUE);
ms_filter_call_method(mixer_mono, MS_FILTER_SET_NCHANNELS, &nb_channels);
ms_filter_call_method(player1, MS_FILE_PLAYER_OPEN, played_file1);
ms_filter_call_method(player2, MS_FILE_PLAYER_OPEN, played_file2);
ms_filter_call_method(player1, MS_FILTER_GET_SAMPLE_RATE, &sample_rate1);
ms_filter_call_method(player2, MS_FILTER_GET_SAMPLE_RATE, &sample_rate2);
BC_ASSERT_EQUAL(sample_rate1, sample_rate2, int, "%i");
if (sample_rate1 != sample_rate2) {
ms_error("The two sounds do not have the same sample rate");
ms_filter_call_method_noarg(player1, MS_FILE_PLAYER_CLOSE);
ms_filter_call_method_noarg(player2, MS_FILE_PLAYER_CLOSE);
goto end;
}
ms_filter_call_method(mixer_mono, MS_FILTER_SET_SAMPLE_RATE, &sample_rate1);
ms_filter_call_method(ms_tester_filerec, MS_FILE_REC_OPEN, recorded_file);
ms_filter_call_method(ms_tester_filerec, MS_FILTER_SET_SAMPLE_RATE, &sample_rate1);
ms_filter_call_method(ms_tester_filerec, MS_FILTER_SET_NCHANNELS, &nb_channels);
ms_filter_link(player1, 0, mixer_mono, 0);
ms_filter_link(player2, 0, mixer_mono, 1);
ms_filter_link(mixer_mono, 0, ms_tester_filerec, 0);
ms_filter_call_method_noarg(player1, MS_FILE_PLAYER_START);
ms_filter_call_method_noarg(player2, MS_FILE_PLAYER_START);
ms_filter_call_method_noarg(ms_tester_filerec, MS_FILE_REC_START);
ms_ticker_attach(ms_tester_ticker, player1);
BC_ASSERT_TRUE(wait_for_until(NULL, NULL, &player1_data.end_of_file, TRUE, 15000));
BC_ASSERT_TRUE(wait_for_until(NULL, NULL, &player2_data.end_of_file, TRUE, 15000));
ms_filter_call_method_noarg(player1, MS_FILE_PLAYER_CLOSE);
ms_filter_call_method_noarg(player2, MS_FILE_PLAYER_CLOSE);
ms_filter_call_method_noarg(ms_tester_filerec, MS_FILE_REC_STOP);
ms_filter_call_method_noarg(ms_tester_filerec, MS_FILE_REC_CLOSE);
ms_ticker_detach(ms_tester_ticker, player1);
ms_filter_unlink(player1, 0, mixer_mono, 0);
ms_filter_unlink(player2, 0, mixer_mono, 1);
ms_filter_unlink(mixer_mono, 0, ms_tester_filerec, 0);
/* TODO
stat(SOUND_TEST_1, &sound_file1);
stat(SOUND_TEST_2, &sound_file2);
stat(RECORD_SOUND, &sound_record);
max_sound_size = (sound_file1.st_size > sound_file2.st_size) ? sound_file1.st_size : sound_file2.st_size;
BC_ASSERT_EQUAL((unsigned long long)sound_record.st_size, ((unsigned long long)max_sound_size) * 2 - 44, unsigned long long, "%llu");
*/
end:
ms_factory_log_statistics(msFactory);
if (player1) ms_filter_destroy(player1);
if (player2) ms_filter_destroy(player2);
if (mixer_mono) ms_filter_destroy(mixer_mono);
ms_tester_destroy_filters(filter_mask);
ms_tester_destroy_ticker();
if (recorded_file) ms_free(recorded_file);
if (played_file1) ms_free(played_file1);
if (played_file2) ms_free(played_file2);
}
test_t basic_audio_tests[] = { test_t basic_audio_tests[] = {
TEST_ONE_TAG("silence detection 48000", silence_detection_48000, "VAD"), TEST_ONE_TAG("silence detection 48000", silence_detection_48000, "VAD"),
...@@ -547,7 +648,8 @@ test_t basic_audio_tests[] = { ...@@ -547,7 +648,8 @@ test_t basic_audio_tests[] = {
TEST_NO_TAG("dtmfgen-enc-dec-tonedet-opus", dtmfgen_enc_dec_tonedet_opus), TEST_NO_TAG("dtmfgen-enc-dec-tonedet-opus", dtmfgen_enc_dec_tonedet_opus),
#endif #endif
TEST_NO_TAG("dtmfgen-enc-rtp-dec-tonedet", dtmfgen_enc_rtp_dec_tonedet), TEST_NO_TAG("dtmfgen-enc-rtp-dec-tonedet", dtmfgen_enc_rtp_dec_tonedet),
TEST_NO_TAG("dtmfgen-filerec-fileplay-tonedet", dtmfgen_filerec_fileplay_tonedet) TEST_NO_TAG("dtmfgen-filerec-fileplay-tonedet", dtmfgen_filerec_fileplay_tonedet),
TEST_NO_TAG("Mixe two mono file into one stereo file", two_mono_into_one_stereo)
}; };
test_suite_t basic_audio_test_suite = { test_suite_t basic_audio_test_suite = {
......
...@@ -36,7 +36,7 @@ else() ...@@ -36,7 +36,7 @@ else()
endif() endif()
endif() endif()
set(simple_executables bench ring mtudiscover tones) set(simple_executables bench ring mtudiscover tones msaudiocmp)
if(ENABLE_VIDEO) if(ENABLE_VIDEO)
list(APPEND simple_executables videodisplay) list(APPEND simple_executables videodisplay)
if(X11_FOUND) if(X11_FOUND)
...@@ -69,7 +69,7 @@ if(PCAP_FOUND) ...@@ -69,7 +69,7 @@ if(PCAP_FOUND)
set(SOURCE_FILES_OBJC pcap_player_cocoa.m) set(SOURCE_FILES_OBJC pcap_player_cocoa.m)
endif() endif()
bc_apply_compile_flags(SOURCE_FILES_OBJC STRICT_OPTIONS_CPP STRICT_OPTIONS_OBJC) bc_apply_compile_flags(SOURCE_FILES_OBJC STRICT_OPTIONS_CPP STRICT_OPTIONS_OBJC)
set(PCAP_PLAYBACK_SOURCE_FILES pcap_playback.c common.c) set(PCAP_PLAYBACK_SOURCE_FILES pcap_playback.c common.c)
bc_apply_compile_flags(PCAP_PLAYBACK_SOURCE_FILES STRICT_OPTIONS_CPP STRICT_OPTIONS_C) bc_apply_compile_flags(PCAP_PLAYBACK_SOURCE_FILES STRICT_OPTIONS_CPP STRICT_OPTIONS_C)
add_executable(pcap_playback ${USE_BUNDLE} ${PCAP_PLAYBACK_SOURCE_FILES} ${SOURCE_FILES_OBJC}) add_executable(pcap_playback ${USE_BUNDLE} ${PCAP_PLAYBACK_SOURCE_FILES} ${SOURCE_FILES_OBJC})
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment