/* This is an ao2 plugin to do simple decoding of matrixed surround sound. This will provide a (basic) surround-sound effect from audio encoded for Dolby Surround, Pro Logic etc. * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. Original author: Steve Davies */ /* The principle: Make rear channels by extracting anti-phase data from the front channels, delay by 20msec and feed to rear in anti-phase */ // SPLITREAR: Define to decode two distinct rear channels - // this doesn't work so well in practice because // separation in a passive matrix is not high. // C (dialogue) to Ls and Rs 14dB or so - // so dialogue leaks to the rear. // Still - give it a try and send feedback. // comment this define for old behaviour of a single // surround sent to rear in anti-phase #define SPLITREAR #include #include #include #include #include "af.h" #include "dsp.h" // instance data typedef struct af_surround_s { float msecs; // Rear channel delay in milliseconds float* Ls_delaybuf; // circular buffer to be used for delaying Ls audio float* Rs_delaybuf; // circular buffer to be used for delaying Rs audio int delaybuf_len; // delaybuf buffer length in samples int delaybuf_rpos; // offset in buffer where we are reading int delaybuf_wpos; // offset in buffer where we are writing float filter_coefs_surround[32]; // FIR filter coefficients for surround sound 7kHz lowpass } af_surround_t; // Initialization and runtime control static int control(struct af_instance_s* af, int cmd, void* arg) { af_surround_t *instance=af->setup; switch(cmd){ case AF_CONTROL_REINIT:{ float cutoff; af->data->rate = ((af_data_t*)arg)->rate; af->data->nch = ((af_data_t*)arg)->nch*2; af->data->format = ((af_data_t*)arg)->format; af->data->bps = ((af_data_t*)arg)->bps; af_msg(AF_MSG_DEBUG0, "[surround]: rear delay=%0.2fms.\n", instance->msecs); if (af->data->nch != 4){ af_msg(AF_MSG_ERROR,"Only Stereo input is supported, filter disabled.\n"); return AF_DETACH; } // Figure out buffer space (in int16_ts) needed for the 15msec delay // Extra 31 samples allow for lowpass filter delay (taps-1) // Double size to make virtual ringbuffer easier instance->delaybuf_len = ((af->data->rate * instance->msecs / 1000)+31)*2; // Free old buffers if (instance->Ls_delaybuf != NULL) free(instance->Ls_delaybuf); if (instance->Rs_delaybuf != NULL) free(instance->Rs_delaybuf); // Allocate new buffers instance->Ls_delaybuf=(void*)calloc(instance->delaybuf_len,sizeof(*instance->Ls_delaybuf)); instance->Rs_delaybuf=(void*)calloc(instance->delaybuf_len,sizeof(*instance->Rs_delaybuf)); af_msg(AF_MSG_DEBUG1, "Delay buffers are %d samples each.\n", instance->delaybuf_len); instance->delaybuf_wpos = 0; instance->delaybuf_rpos = 32; // compensate for fir delay // Surround filer coefficients cutoff = af->data->rate/7000; if (-1 == design_fir(32, instance->filter_coefs_surround, &cutoff, LP|KAISER, 10.0)) { af_msg(AF_MSG_ERROR,"[surround] Unable to design prototype filter.\n"); return AF_ERROR; } return AF_OK; } case AF_CONTROL_COMMAND_LINE:{ float d = 0; sscanf((char*)arg,"%f",&d); if (d<0){ af_msg(AF_MSG_ERROR,"Error setting rear delay length in af_surround. Delay has to be positive.\n"); return AF_ERROR; } instance->msecs=d; return AF_OK; } } return AF_UNKNOWN; } // Deallocate memory static void uninit(struct af_instance_s* af) { af_surround_t *instance=af->setup; if(af->data->audio) free(af->data->audio); if(af->data) free(af->data); if(instance->Ls_delaybuf) free(instance->Ls_delaybuf); if(instance->Rs_delaybuf) free(instance->Rs_delaybuf); free(af->setup); } // The beginnings of an active matrix... static double steering_matrix[][12] = { // LL RL LR RR LS RS // LLs RLs LRs RRs LC RC {.707, .0, .0, .707, .5, -.5, .5878, -.3928, .3928, -.5878, .5, .5}, }; // Experimental moving average dominances //static int amp_L = 0, amp_R = 0, amp_C = 0, amp_S = 0; // Filter data through filter static af_data_t* play(struct af_instance_s* af, af_data_t* data){ af_surround_t* instance = (af_surround_t*)af->setup; int16_t* in = data->audio; int16_t* out; int i, samples; double *matrix = steering_matrix[0]; // later we'll index based on detected dominance if (AF_OK != RESIZE_LOCAL_BUFFER(af, data)) return NULL; out = af->data->audio; // fprintf(stderr, "pl_surround: play %d bytes, %d samples\n", ao_plugin_data.len, samples); samples = data->len / (data->nch * sizeof(int16_t)); // Testing - place a 1kHz tone on Lt and Rt in anti-phase: should decode in S //sinewave(in, samples, pl_surround.input_channels, 1000, 0.0, pl_surround.rate); //sinewave(&in[1], samples, pl_surround.input_channels, 1000, PI, pl_surround.rate); for (i=0; ifilter_coefs_surround, &instance->Ls_delaybuf[instance->delaybuf_rpos + instance->delaybuf_len/2]); #ifdef SPLITREAR out[3] = fir(32, instance->filter_coefs_surround, &instance->Rs_delaybuf[instance->delaybuf_rpos + instance->delaybuf_len/2]); #else out[3] = -out[2]; #endif // calculate and save surround for 20msecs time #ifdef SPLITREAR instance->Ls_delaybuf[instance->delaybuf_wpos] = instance->Ls_delaybuf[instance->delaybuf_wpos + instance->delaybuf_len/2] = matrix[6]*in[0] + matrix[7]*in[1]; instance->Rs_delaybuf[instance->delaybuf_wpos] = instance->Rs_delaybuf[instance->delaybuf_wpos++ + instance->delaybuf_len/2] = matrix[8]*in[0] + matrix[9]*in[1]; #else instance->Ls_delaybuf[instance->delaybuf_wpos] = instance->Ls_delaybuf[instance->delaybuf_wpos++ + instance->delaybuf_len/2] = matrix[4]*in[0] + matrix[5]*in[1]; #endif instance->delaybuf_rpos++; instance->delaybuf_wpos %= instance->delaybuf_len/2; instance->delaybuf_rpos %= instance->delaybuf_len/2; // next samples... in = &in[data->nch]; out = &out[af->data->nch]; } // Show some state //printf("\npl_surround: delaybuf_pos=%d, samples=%d\r\033[A", pl_surround.delaybuf_pos, samples); // Set output data data->audio = af->data->audio; data->len = (data->len*af->mul.n)/af->mul.d; data->nch = af->data->nch; return data; } static int open(af_instance_t* af){ af_surround_t *pl_surround; af->control=control; af->uninit=uninit; af->play=play; af->mul.n=2; af->mul.d=1; af->data=calloc(1,sizeof(af_data_t)); af->setup=pl_surround=calloc(1,sizeof(af_surround_t)); pl_surround->msecs=20; if(af->data == NULL || af->setup == NULL) return AF_ERROR; return AF_OK; } af_info_t af_info_surround = { "Surround decoder filter", "surround", "Steve Davies ", "", AF_FLAGS_REENTRANT, open };