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

Feature/chan adapter mixer

parent 50c96337
......@@ -52,3 +52,4 @@ git-clang-format.diff
tools/mkvstream
.bc_tester_utils.tmp
**/.gradle
tester/sounds/mixed_file.wav
/*
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
modify it under the terms of the GNU General Public License
......@@ -19,81 +19,151 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "mediastreamer2/msfilter.h"
#include "mediastreamer2/mschanadapter.h"
#include "mediastreamer2/msticker.h"
/*
This filter transforms stereo buffers to mono and vice versa.
*/
typedef struct AdapterState{
typedef struct AdapterState {
int inputchans;
int outputchans;
int sample_rate;
size_t buffer_size;
uint8_t *buffer1;
uint8_t *buffer2;
MSBufferizer input_buffer1;
MSBufferizer input_buffer2;
}AdapterState;
static void adapter_init(MSFilter *f){
AdapterState *s=ms_new0(AdapterState,1);
s->inputchans=1;
s->outputchans=1;
f->data=s;
static void adapter_init(MSFilter *f) {
AdapterState *s = ms_new0(AdapterState, 1);
s->inputchans = 1;
s->outputchans = 1;
s->sample_rate = 8000;
f->data = s;
}
static void adapter_uninit(MSFilter *f){
ms_free(f->data);
static void adapter_uninit(MSFilter *f) {
AdapterState *s = (AdapterState*)f->data;
ms_free(s);
}
static void adapter_process(MSFilter *f){
AdapterState *s=(AdapterState*)f->data;
mblk_t *im,*om;
static void adapter_preprocess(MSFilter *f) {
AdapterState *s = (AdapterState*)f->data;
if (s->inputchans == 2 && s->outputchans == 1) {
s->buffer_size = ((f->ticker->interval * s->sample_rate) / 1000) * 2;
s->buffer1 = ms_new(uint8_t, s->buffer_size);
s->buffer2 = ms_new(uint8_t, s->buffer_size);
ms_bufferizer_init(&s->input_buffer1);
ms_bufferizer_init(&s->input_buffer2);
}
}
static void adapter_process(MSFilter *f) {
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);
buffer_size1 = ms_bufferizer_get_avail(&s->input_buffer1);
buffer_size2 = ms_bufferizer_get_avail(&s->input_buffer2);
}
} else {
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->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){
*(int16_t*)om->b_wptr=*(int16_t*)im->b_rptr;
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);
ms_queue_put(f->outputs[0], om);
freemsg(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);
}
}
}
static int adapter_set_nchannels(MSFilter *f, void *data){
AdapterState *s=(AdapterState*)f->data;
s->inputchans=*(int*)data;
static void adapter_postprocess(MSFilter *f) {
AdapterState *s = (AdapterState*)f->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;
}
static int adapter_get_nchannels(MSFilter *f, void *data){
AdapterState *s=(AdapterState*)f->data;
*(int*)data=s->inputchans;
static int adapter_get_nchannels(MSFilter *f, void *data) {
AdapterState *s = (AdapterState*)f->data;
*(int*)data = s->inputchans;
return 0;
}
static int adapter_set_out_nchannels(MSFilter *f, void *data){
AdapterState *s=(AdapterState*)f->data;
s->outputchans=*(int*)data;
static int adapter_set_out_nchannels(MSFilter *f, void *data) {
AdapterState *s = (AdapterState*)f->data;
s->outputchans = *(int*)data;
return 0;
}
static int adapter_get_out_nchannels(MSFilter *f, void *data){
AdapterState *s=(AdapterState*)f->data;
*(int*)data=s->outputchans;
static int adapter_get_out_nchannels(MSFilter *f, void *data) {
AdapterState *s = (AdapterState*)f->data;
*(int*)data = s->outputchans;
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_GET_NCHANNELS, adapter_get_nchannels },
{ MS_CHANNEL_ADAPTER_SET_OUTPUT_NCHANNELS, adapter_set_out_nchannels },
......@@ -101,39 +171,20 @@ static MSFilterMethod methods[]={
{ 0, NULL }
};
#ifdef _MSC_VER
MSFilterDesc ms_channel_adapter_desc={
MSFilterDesc ms_channel_adapter_desc = {
MS_CHANNEL_ADAPTER_ID,
"MSChannelAdapter",
N_("A filter that converts from mono to stereo and vice versa."),
MS_FILTER_OTHER,
NULL,
1,
2,
1,
adapter_init,
NULL,
adapter_preprocess,
adapter_process,
NULL,
adapter_postprocess,
adapter_uninit,
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)
......@@ -29,6 +29,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "mediastreamer2_tester_private.h"
#include "private.h"
#include <sys/stat.h>
static MSFactory *msFactory = NULL;
static int basic_audio_tester_before_all(void) {
......@@ -532,6 +534,105 @@ static void dtmfgen_filerec_fileplay_tonedet(void) {
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_ONE_TAG("silence detection 48000", silence_detection_48000, "VAD"),
......@@ -547,7 +648,8 @@ test_t basic_audio_tests[] = {
TEST_NO_TAG("dtmfgen-enc-dec-tonedet-opus", dtmfgen_enc_dec_tonedet_opus),
#endif
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 = {
......
......@@ -36,7 +36,7 @@ else()
endif()
endif()
set(simple_executables bench ring mtudiscover tones)
set(simple_executables bench ring mtudiscover tones msaudiocmp)
if(ENABLE_VIDEO)
list(APPEND simple_executables videodisplay)
if(X11_FOUND)
......
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