From 431555030ead3aabad83028f359849314e95065e Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Mon, 22 Jun 2009 00:36:14 -0700 Subject: module-equalizer-sink added src/Makefile.am: added module-equalizer-sink --- src/Makefile.am | 7 + src/modules/module-equalizer-sink.c | 850 ++++++++++++++++++++++++++++++++++++ 2 files changed, 857 insertions(+) create mode 100755 src/modules/module-equalizer-sink.c (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index 6544e2aa..10f9a793 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1006,6 +1006,7 @@ modlibexec_LTLIBRARIES += \ module-combine.la \ module-remap-sink.la \ module-ladspa-sink.la \ + module-equalizer-sink.la \ module-esound-sink.la \ module-tunnel-sink.la \ module-tunnel-source.la \ @@ -1200,6 +1201,7 @@ SYMDEF_FILES = \ modules/module-combine-symdef.h \ modules/module-remap-sink-symdef.h \ modules/module-ladspa-sink-symdef.h \ + modules/module-equalizer-sink-symdef.h \ modules/module-esound-compat-spawnfd-symdef.h \ modules/module-esound-compat-spawnpid-symdef.h \ modules/module-match-symdef.h \ @@ -1380,6 +1382,11 @@ module_ladspa_sink_la_CFLAGS = -DLADSPA_PATH=\"$(libdir)/ladspa:/usr/local/lib/l module_ladspa_sink_la_LDFLAGS = $(MODULE_LDFLAGS) module_ladspa_sink_la_LIBADD = $(AM_LIBADD) $(LIBLTDL) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la +module_equalizer_sink_la_SOURCES = modules/module-equalizer-sink.c +module_equalizer_sink_la_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS) +module_equalizer_sink_la_LDFLAGS = $(MODULE_LDFLAGS) +module_equalizer_sink_la_LIBADD = $(AM_LIBADD) $(LIBLTDL) $(LIBOIL_LIBS) -lfftw3f libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la + module_match_la_SOURCES = modules/module-match.c module_match_la_LDFLAGS = $(MODULE_LDFLAGS) module_match_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c new file mode 100755 index 00000000..8b34fa0d --- /dev/null +++ b/src/modules/module-equalizer-sink.c @@ -0,0 +1,850 @@ + + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include +#include + + +#include "module-equalizer-sink-symdef.h" + +PA_MODULE_AUTHOR("Jason Newton"); +PA_MODULE_DESCRIPTION(_("General Purpose Equalizer")); +PA_MODULE_VERSION(PACKAGE_VERSION); +PA_MODULE_LOAD_ONCE(FALSE); +PA_MODULE_USAGE(_("sink= ")); + +#define MEMBLOCKQ_MAXLENGTH (16*1024*1024) + +struct userdata { + pa_core *core; + pa_module *module; + pa_sink *sink, *master; + pa_sink_input *sink_input; + + size_t channels; + size_t fft_size; //length (res) of fft + size_t window_size;//even! + size_t overlap_size; + size_t samples_gathered; + size_t n_buffered_output; + size_t max_output; + float *H;//frequency response filter (magnitude based) + float *W;//windowing function (time domain) + float *work_buffer,**input,**overlap_accum,**output_buffer; + fftwf_complex *output_window; + fftwf_plan forward_plan,inverse_plan; + + pa_memblockq *memblockq; +}; + +static const char* const valid_modargs[] = { + "sink_name", + "sink_properties", + "master", + "format", + "rate", + "channels", + "channel_map", + NULL +}; + +uint64_t time_diff(struct timespec *timeA_p, struct timespec *timeB_p) +{ + return ((timeA_p->tv_sec * 1000000000) + timeA_p->tv_nsec) - + ((timeB_p->tv_sec * 1000000000) + timeB_p->tv_nsec); +} + +void hanning_normalized_window(float *W,size_t window_size){ + //h = sqrt(2)/2 * (1+cos(t*pi)) ./ sqrt( 1+cos(t*pi).^2 ) + float c; + for(size_t i=0;iuserdata; + + switch (code) { + + case PA_SINK_MESSAGE_GET_LATENCY: { + pa_usec_t usec = 0; + pa_sample_spec *ss=&u->sink->sample_spec; + + /* Get the latency of the master sink */ + if (PA_MSGOBJECT(u->master)->process_msg(PA_MSGOBJECT(u->master), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0) + usec = 0; + + usec+=pa_bytes_to_usec(u->n_buffered_output*pa_frame_size(ss),ss); + /* Add the latency internal to our sink input on top */ + usec += pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->master->sample_spec); + *((pa_usec_t*) data) = usec; + return 0; + } + } + + return pa_sink_process_msg(o, code, data, offset, chunk); +} + + +/* Called from main context */ +static int sink_set_state(pa_sink *s, pa_sink_state_t state) { + struct userdata *u; + + pa_sink_assert_ref(s); + pa_assert_se(u = s->userdata); + + if (PA_SINK_IS_LINKED(state) && + u->sink_input && + PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(u->sink_input))) + + pa_sink_input_cork(u->sink_input, state == PA_SINK_SUSPENDED); + + return 0; +} + +/* Called from I/O thread context */ +static void sink_request_rewind(pa_sink *s) { + struct userdata *u; + + pa_sink_assert_ref(s); + pa_assert_se(u = s->userdata); + + /* Just hand this one over to the master sink */ + pa_sink_input_request_rewind(u->sink_input, s->thread_info.rewind_nbytes + pa_memblockq_get_length(u->memblockq), TRUE, FALSE, FALSE); +} + +/* Called from I/O thread context */ +static void sink_update_requested_latency(pa_sink *s) { + struct userdata *u; + + pa_sink_assert_ref(s); + pa_assert_se(u = s->userdata); + + /* Just hand this one over to the master sink */ + pa_sink_input_set_requested_latency_within_thread( + u->sink_input, + pa_sink_get_requested_latency_within_thread(s)); +} + +/* Called from I/O thread context */ +static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) { + struct userdata *u; + float *src, *dst; + size_t c; + pa_memchunk tchunk; + pa_sink_input_assert_ref(i); + pa_assert(chunk); + pa_assert_se(u = i->userdata); + size_t fs = pa_frame_size(&u->sink->sample_spec); + size_t ss=pa_sample_size(&u->sink->sample_spec); + size_t fe = fs/ss; + + if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state)) + return -1; + + //output any buffered outputs first + if(u->n_buffered_output>0){ + //pa_log("outputing %ld buffered samples",u->n_buffered_output); + chunk->index = 0; + size_t n_outputable=PA_MIN(u->n_buffered_output,nbytes/fs); + chunk->length = n_outputable*fs; + chunk->memblock = pa_memblock_new(i->sink->core->mempool, chunk->length); + pa_memblockq_drop(u->memblockq, chunk->length); + dst = (float*) pa_memblock_acquire(chunk->memblock); + for(size_t j=0;jchannels;++j){ + pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst+j, fs, u->output_buffer[j], sizeof(float),n_outputable); + memmove(u->output_buffer[j],u->output_buffer[j]+n_outputable,(u->n_buffered_output-n_outputable)*sizeof(float)); + } + u->n_buffered_output-=n_outputable; + pa_memblock_release(chunk->memblock); + return 0; + } + pa_assert_se(u->n_buffered_output==0); + + //collect the minimum number of samples + while(u->samples_gathered < (u->window_size-u->overlap_size)){ + //render some new fragments to our memblockq + //size_t desired_samples=PA_MIN(u->min_input-samples_gathered,u->max_output); + size_t desired_samples=PA_MIN((u->window_size-u->overlap_size)-u->samples_gathered,u->max_output); + while (pa_memblockq_peek(u->memblockq, &tchunk) < 0) { + pa_memchunk nchunk; + + pa_sink_render(u->sink, desired_samples*fs, &nchunk); + pa_memblockq_push(u->memblockq, &nchunk); + pa_memblock_unref(nchunk.memblock); + } + if(tchunk.length/fs!=desired_samples){ + pa_log("got %ld samples, asked for %ld",tchunk.length/fs,desired_samples); + } + size_t n_samples=PA_MIN(tchunk.length/fs,u->window_size-u->overlap_size-u->samples_gathered); + //TODO: figure out what to do with rest of the samples when there's too many (rare?) + src = (float*) ((uint8_t*) pa_memblock_acquire(tchunk.memblock) + tchunk.index); + for (size_t c=0;cchannels;c++) { + pa_sample_clamp(PA_SAMPLE_FLOAT32NE,u->input[c]+u->overlap_size+u->samples_gathered,sizeof(float), src+c, fs, n_samples); + } + + u->samples_gathered+=n_samples; + pa_memblock_release(tchunk.memblock); + pa_memblock_unref(tchunk.memblock); + } + //IT should be this guy if we're buffering like how its supposed to + //size_t n_outputable=PA_MIN(u->window_size-u->overlap_size,nbytes/fs); + //This one takes into account the actual data gathered but then the dsp + //stuff is wrong when the buffer "underruns" + size_t n_outputable=PA_MIN(u->samples_gathered,nbytes/fs); + /* + //debugging: tests if immediate release of freshly buffered data + //plays ok and prevents any other processing + chunk->index=0; + chunk->length=n_outputable*fs; + chunk->memblock = pa_memblock_new(i->sink->core->mempool, chunk->length); + pa_memblockq_drop(u->memblockq, chunk->length); + dst = (float*) pa_memblock_acquire(chunk->memblock);; + for (size_t c=0;cchannels;c++) { + pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst+c, fs, u->input[c]+u->overlap_size, sizeof(float),n_outputable); + } + u->samples_gathered=0; + pa_memblock_release(chunk->memblock); + return 0; + */ + + //pa_log("%ld dequed samples",u->samples_gathered); + + chunk->index=0; + chunk->length=n_outputable*fs; + chunk->memblock = pa_memblock_new(i->sink->core->mempool, chunk->length); + pa_memblockq_drop(u->memblockq, chunk->length); + dst = (float*) pa_memblock_acquire(chunk->memblock); + //pa_sample_clamp(PA_SAMPLE_FLOAT32NE, u->input, sizeof(float), src+c, fs, samples); + //pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst+c,fs, u->input, sizeof(float), samples); + + /* + struct timespec start, end; + uint64_t elapsed; + clock_gettime(CLOCK_MONOTONIC, &start); + */ + //use a zero-phase sliding dft and overlap-add method + + pa_assert_se(u->fft_size>=u->window_size); + //pa_assert_se(u->window_size%2==0); + pa_assert_se(u->overlap_sizewindow_size); + pa_assert_se(u->samples_gathered>=u->window_size-u->overlap_size); + size_t sample_rem=u->window_size-u->overlap_size-n_outputable; + //size_t w_mid=u->window_size/2; + //pa_log("hello world a"); + for (c=0;cchannels;c++) { + //center the data for zero phase + //zero-pad TODO: optimization if sure these zeros aren't overwritten + //memset(u->work_buffer+w_mid,0,(u->fft_size-u->window_size)*sizeof(float)); + //memset(u->work_buffer,0,u->fft_size*sizeof(float)); + /* + for(size_t j=0;jwindow_size;++j){ + u->work_buffer[j]=u->W[j]*u->input[c][j]; + u->work_buffer[j]=u->input[c][j]; + } + */ + //zero padd the data, don't worry about zerophase, shouldn't really matter + memset(u->work_buffer+u->overlap_size,0,(u->fft_size-u->overlap_size)*sizeof(float)); + //window the data + for(size_t j=0;jwindow_size;++j){ + u->work_buffer[j]=u->W[j]*u->input[c][j]; + } + /* + //recenter for zero phase + for(size_t j=0;jwork_buffer[j]; + u->work_buffer[j]=u->input[c][j+w_mid]; + u->work_buffer[j+u->fft_size-w_mid]=tmp; + } + */ + //pa_log("hello world b"); + + /* + //window and zero phase shift + for(size_t j=0;jwork_buffer[j]=u->input[c][j+w_mid]; + //u->work_buffer[j+u->fft_size-w_mid]=u->input[c][j]; + u->work_buffer[j]=u->W[j+w_mid]*u->input[c][j+w_mid]; + u->work_buffer[j+u->fft_size-w_mid]=u->W[j]*u->input[c][j]; + }*/ + //Processing is done here! + //do fft + fftwf_execute_dft_r2c(u->forward_plan,u->work_buffer,u->output_window); + //perform filtering + for(size_t j=0;jfft_size/2+1;++j){ + ////identity transform (fft size) + //u->output_window[j][0]/=u->fft_size; + //u->output_window[j][1]/=u->fft_size; + ////identity transform (window size) + //u->output_window[j][0]/=u->window_size; + //u->output_window[j][1]/=u->window_size; + //filtered + u->output_window[j][0]*=u->H[j]; + u->output_window[j][1]*=u->H[j]; + } + //inverse fft + fftwf_execute_dft_c2r(u->inverse_plan,u->output_window,u->work_buffer); + + /* + //uncenter the data + for(size_t j=0;jwork_buffer[j]; + u->work_buffer[j]=u->work_buffer[j+u->fft_size-w_mid]; + u->work_buffer[j+w_mid]=tmp; + } + */ + /* + //divide out fft gain (more stable here?) + for(size_t j=0;jwindow_size;++j){ + u->work_buffer[j]/=u->fft_size; + } + */ + /* + //debug: tests overlaping add + //and negates ALL PREVIOUS processing + //yields a perfect reconstruction if COLA is held + for(size_t j=0;jwindow_size;++j){ + u->work_buffer[j]=u->W[j]*u->input[c][j]; + } + */ + /* + //debug: tests if basic buffering works + //shouldn't modify the signal AT ALL + for(size_t j=0;jwindow_size;++j){ + u->work_buffer[j]=u->input[c][j]; + } + */ + + /* + //overlap add and preserve overlap component from this window (zero phase) + for(size_t j=0;joverlap_size;++j){ + u->work_buffer[j]+=u->overlap_accum[c][j]; + u->overlap_accum[c][j]=u->work_buffer[u->window_size-u->overlap_size+j]; + } + */ + //overlap add and preserve overlap component from this window (linear phase) + for(size_t j=0;joverlap_size;++j){ + u->work_buffer[j]+=u->overlap_accum[c][j]; + u->overlap_accum[c][j]=u->work_buffer[u->window_size-u->overlap_size+j]; + } + + //preseve the needed input for the next windows overlap + memmove(u->input[c],u->input[c]+u->overlap_size,(u->window_size-u->overlap_size)*sizeof(float)); + //output the samples that are outputable now + pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst+c, fs, u->work_buffer, sizeof(float),n_outputable); + //buffer the rest of them + memcpy(u->output_buffer[c]+u->n_buffered_output,u->work_buffer+n_outputable,sample_rem*sizeof(float)); + } + /* + clock_gettime(CLOCK_MONOTONIC, &end); + elapsed=time_diff(&end, &start); + pa_log("processed: %ld, time: %ld",u->samples_gathered,elapsed); + */ + u->n_buffered_output+=sample_rem; + u->samples_gathered=0; + + + //pa_log("%ld samples queued",u->n_buffered_output); + + pa_memblock_release(chunk->memblock); + + + return 0; +} + +/* Called from I/O thread context */ +static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) { + struct userdata *u; + size_t amount = 0; + + pa_sink_input_assert_ref(i); + pa_assert_se(u = i->userdata); + + if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state)) + return; + + if (u->sink->thread_info.rewind_nbytes > 0) { + size_t max_rewrite; + + max_rewrite = nbytes + pa_memblockq_get_length(u->memblockq); + amount = PA_MIN(u->sink->thread_info.rewind_nbytes, max_rewrite); + u->sink->thread_info.rewind_nbytes = 0; + + if (amount > 0) { + pa_memblockq_seek(u->memblockq, - (int64_t) amount, PA_SEEK_RELATIVE, TRUE); + pa_log_debug("Resetting equalizer"); + } + } + + pa_sink_process_rewind(u->sink, amount); + pa_memblockq_rewind(u->memblockq, nbytes); +} + +/* Called from I/O thread context */ +static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) { + struct userdata *u; + + pa_sink_input_assert_ref(i); + pa_assert_se(u = i->userdata); + + if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state)) + return; + + pa_memblockq_set_maxrewind(u->memblockq, nbytes); + pa_sink_set_max_rewind_within_thread(u->sink, nbytes); +} + +/* Called from I/O thread context */ +static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes) { + struct userdata *u; + + pa_sink_input_assert_ref(i); + pa_assert_se(u = i->userdata); + + if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state)) + return; + + pa_sink_set_max_request_within_thread(u->sink, nbytes); +} + +/* Called from I/O thread context */ +static void sink_input_update_sink_latency_range_cb(pa_sink_input *i) { + struct userdata *u; + + pa_sink_input_assert_ref(i); + pa_assert_se(u = i->userdata); + + if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state)) + return; + + pa_sink_set_latency_range_within_thread(u->sink, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency); +} + +/* Called from I/O thread context */ +static void sink_input_detach_cb(pa_sink_input *i) { + struct userdata *u; + + pa_sink_input_assert_ref(i); + pa_assert_se(u = i->userdata); + + if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state)) + return; + + pa_sink_detach_within_thread(u->sink); + pa_sink_set_asyncmsgq(u->sink, NULL); + pa_sink_set_rtpoll(u->sink, NULL); +} + +/* Called from I/O thread context */ +static void sink_input_attach_cb(pa_sink_input *i) { + struct userdata *u; + + pa_sink_input_assert_ref(i); + pa_assert_se(u = i->userdata); + + if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state)) + return; + + pa_sink_set_asyncmsgq(u->sink, i->sink->asyncmsgq); + pa_sink_set_rtpoll(u->sink, i->sink->rtpoll); + pa_sink_attach_within_thread(u->sink); + + pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->master->thread_info.max_latency); +} + +/* Called from main context */ +static void sink_input_kill_cb(pa_sink_input *i) { + struct userdata *u; + + pa_sink_input_assert_ref(i); + pa_assert_se(u = i->userdata); + + pa_sink_unlink(u->sink); + pa_sink_input_unlink(u->sink_input); + + pa_sink_unref(u->sink); + u->sink = NULL; + pa_sink_input_unref(u->sink_input); + u->sink_input = NULL; + + pa_module_unload_request(u->module, TRUE); +} + +/* Called from IO thread context */ +static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t state) { + struct userdata *u; + + pa_sink_input_assert_ref(i); + pa_assert_se(u = i->userdata); + + /* If we are added for the first time, ask for a rewinding so that + * we are heard right-away. */ + if (PA_SINK_INPUT_IS_LINKED(state) && + i->thread_info.state == PA_SINK_INPUT_INIT) { + pa_log_debug("Requesting rewind due to state change."); + pa_sink_input_request_rewind(i, 0, FALSE, TRUE, TRUE); + } +} + +/* Called from main context */ +static pa_bool_t sink_input_may_move_to_cb(pa_sink_input *i, pa_sink *dest) { + struct userdata *u; + + pa_sink_input_assert_ref(i); + pa_assert_se(u = i->userdata); + + return u->sink != dest; +} + +int pa__init(pa_module*m) { + struct userdata *u; + pa_sample_spec ss; + pa_channel_map map; + pa_modargs *ma; + const char *z; + pa_sink *master; + pa_sink_input_new_data sink_input_data; + pa_sink_new_data sink_data; + pa_bool_t *use_default = NULL; + size_t fs; + + pa_assert(m); + + if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { + pa_log("Failed to parse module arguments."); + goto fail; + } + + if (!(master = pa_namereg_get(m->core, pa_modargs_get_value(ma, "master", NULL), PA_NAMEREG_SINK))) { + pa_log("Master sink not found"); + goto fail; + } + + ss = master->sample_spec; + ss.format = PA_SAMPLE_FLOAT32; + map = master->channel_map; + if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) { + pa_log("Invalid sample format specification or channel map"); + goto fail; + } + fs=pa_frame_size(&ss); + + u = pa_xnew0(struct userdata, 1); + u->core = m->core; + u->module = m; + m->userdata = u; + u->master = master; + u->sink = NULL; + u->sink_input = NULL; + u->memblockq = pa_memblockq_new(0, MEMBLOCKQ_MAXLENGTH, 0, fs, 1, 1, 0, NULL); + + //u->fft_size=44100; + //u->fft_size=48000; + //u->fft_size=1024; + u->channels=ss.channels; + u->fft_size=pow(2,ceil(log(ss.rate)/log(2))); + //u->fft_size=ss.rate; + //u->fft_size=65536; + pa_log("fft size: %ld",u->fft_size); + u->window_size=8001; + u->overlap_size=(u->window_size+1)/2; + //u->overlap_size=u->window_size/2; + //u->overlap_size=0; + u->samples_gathered=0; + u->n_buffered_output=0; + u->max_output=pa_frame_align(pa_mempool_block_size_max(m->core->mempool), &ss)/pa_frame_size(&ss); + u->H=(float*) fftwf_malloc((u->fft_size/2+1)*sizeof(float)); + u->W=(float*) fftwf_malloc((u->window_size)*sizeof(float)); + u->work_buffer=(float*) fftwf_malloc(u->fft_size*sizeof(float)); + u->input=(float **)malloc(sizeof(float *)*u->channels); + u->overlap_accum=(float **)malloc(sizeof(float *)*u->channels); + u->output_buffer=(float **)malloc(sizeof(float *)*u->channels); + for(size_t c=0;cchannels;++c){ + u->input[c]=(float*) fftwf_malloc(u->window_size*sizeof(float)); + memset(u->input[c],0,u->window_size*sizeof(float)); + u->overlap_accum[c]=(float*) fftwf_malloc(u->overlap_size*sizeof(float)); + memset(u->overlap_accum[c],0,u->overlap_size*sizeof(float)); + u->output_buffer[c]=(float*) fftwf_malloc(u->window_size*sizeof(float)); + } + u->output_window = (fftwf_complex *) fftwf_malloc(sizeof(fftwf_complex) * (u->fft_size/2+1)); + u->forward_plan=fftwf_plan_dft_r2c_1d(u->fft_size, u->work_buffer, u->output_window, FFTW_ESTIMATE); + u->inverse_plan=fftwf_plan_dft_c2r_1d(u->fft_size, u->output_window, u->work_buffer, FFTW_ESTIMATE); + + /* + //rectangular window + for(size_t j=0;jwindow_size;++j){ + u->W[j]=1.0; + } + */ + //hanning_normalized_window(u->W,u->window_size); + hanning_window(u->W,u->window_size); + //sin_window(u->W,u->window_size); + array_out("/home/jason/window.txt",u->W,u->window_size); + //u->forward_plan=fftwf_plan_dft_r2c_1d(u->fft_size, u->input, u->output_window, FFTW_ESTIMATE); + //u->inverse_plan=fftwf_plan_dft_c2r_1d(u->fft_size, u->output_window, u->work_buffer, FFTW_ESTIMATE); + //u->forward_plan=fftwf_plan_dft_r2c_1d(u->fft_size, u->input, u->output, FFTW_MEASURE); + //u->inverse_plan=fftwf_plan_dft_c2r_1d(u->fft_size, u->output, u->input, FFTW_MEASURE); + const int freqs[]={0,25,50,100,200,300,400,800,1500, + 2000,3000,4000,5000,6000,7000,8000,9000,10000,11000,12000, + 13000,14000,15000,16000,17000,18000,19000,20000,21000,22000,23000,24000,INT_MAX}; + const float coefficients[]={1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}; + const size_t ncoefficients=sizeof(coefficients)/sizeof(float); + pa_assert_se(sizeof(freqs)/sizeof(int)==sizeof(coefficients)/sizeof(float)); + float *freq_translated=(float *) malloc(sizeof(float)*(ncoefficients)); + freq_translated[0]=1; + //Translate the frequencies in their natural sampling rate to the new sampling rate frequencies + for(size_t i=1;ifft_size)/ss.rate; + //pa_log("i: %ld: %d , %g",i,freqs[i],freq_translated[i]); + pa_assert_se(freq_translated[i]>=freq_translated[i-1]); + } + freq_translated[ncoefficients-1]=DBL_MAX; + //Interpolate the specified frequency band values + u->H[0]=1; + for(size_t i=1,j=0;i<(u->fft_size/2+1);++i){ + pa_assert_se(j=DBL_MAX){ + for(;i<(u->fft_size/2+1);++i){ + u->H[i]=coefficients[j]; + } + break; + } + //pa_log("i: %d, j: %d, freq: %f",i,j,freq_translated[j]); + //pa_log("interp: %0.4f %0.4f",freq_translated[j],freq_translated[j+1]); + pa_assert_se(freq_translated[j]=freq_translated[j]); + pa_assert_se(i<=freq_translated[j+1]); + //bilinear-inerpolation of coefficients specified + float c0=(i-freq_translated[j])/(freq_translated[j+1]-freq_translated[j]); + pa_assert_se(c0>=0&&c0<=1.0); + u->H[i]=((1.0f-c0)*coefficients[j]+c0*coefficients[j+1]); + pa_assert_se(u->H[i]>0); + while(i>=floor(freq_translated[j+1])){ + j++; + } + } + array_out("/home/jason/coffs.txt",u->H,u->fft_size/2+1); + //divide out the fft gain + for(int i=0;i<(u->fft_size/2+1);++i){ + u->H[i]/=u->fft_size; + } + free(freq_translated); + + /* Create sink */ + pa_sink_new_data_init(&sink_data); + sink_data.driver = __FILE__; + sink_data.module = m; + if (!(sink_data.name = pa_xstrdup(pa_modargs_get_value(ma, "sink_name", NULL)))) + sink_data.name = pa_sprintf_malloc("%s.equalizer", master->name); + sink_data.namereg_fail = FALSE; + pa_sink_new_data_set_sample_spec(&sink_data, &ss); + pa_sink_new_data_set_channel_map(&sink_data, &map); + z = pa_proplist_gets(master->proplist, PA_PROP_DEVICE_DESCRIPTION); + pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "FFT based equalizer"); + pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_MASTER_DEVICE, master->name); + pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_CLASS, "filter"); + + if (pa_modargs_get_proplist(ma, "sink_properties", sink_data.proplist, PA_UPDATE_REPLACE) < 0) { + pa_log("Invalid properties"); + pa_sink_new_data_done(&sink_data); + goto fail; + } + + u->sink = pa_sink_new(m->core, &sink_data, PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY); + pa_sink_new_data_done(&sink_data); + + if (!u->sink) { + pa_log("Failed to create sink."); + goto fail; + } + + u->sink->parent.process_msg = sink_process_msg; + u->sink->set_state = sink_set_state; + u->sink->update_requested_latency = sink_update_requested_latency; + u->sink->request_rewind = sink_request_rewind; + u->sink->userdata = u; + + pa_sink_set_asyncmsgq(u->sink, master->asyncmsgq); + pa_sink_set_rtpoll(u->sink, master->rtpoll); + + /* Create sink input */ + pa_sink_input_new_data_init(&sink_input_data); + sink_input_data.driver = __FILE__; + sink_input_data.module = m; + sink_input_data.sink = u->master; + pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_NAME, "Equalized Stream"); + pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "filter"); + pa_sink_input_new_data_set_sample_spec(&sink_input_data, &ss); + pa_sink_input_new_data_set_channel_map(&sink_input_data, &map); + + pa_sink_input_new(&u->sink_input, m->core, &sink_input_data, PA_SINK_INPUT_DONT_MOVE); + pa_sink_input_new_data_done(&sink_input_data); + + if (!u->sink_input) + goto fail; + + u->sink_input->pop = sink_input_pop_cb; + u->sink_input->process_rewind = sink_input_process_rewind_cb; + u->sink_input->update_max_rewind = sink_input_update_max_rewind_cb; + u->sink_input->update_max_request = sink_input_update_max_request_cb; + u->sink_input->update_sink_latency_range = sink_input_update_sink_latency_range_cb; + u->sink_input->kill = sink_input_kill_cb; + u->sink_input->attach = sink_input_attach_cb; + u->sink_input->detach = sink_input_detach_cb; + u->sink_input->state_change = sink_input_state_change_cb; + u->sink_input->may_move_to = sink_input_may_move_to_cb; + u->sink_input->userdata = u; + + pa_sink_put(u->sink); + pa_sink_input_put(u->sink_input); + + pa_modargs_free(ma); + + pa_xfree(use_default); + + return 0; + +fail: + if (ma) + pa_modargs_free(ma); + + pa_xfree(use_default); + + pa__done(m); + + return -1; +} + +int pa__get_n_used(pa_module *m) { + struct userdata *u; + + pa_assert(m); + pa_assert_se(u = m->userdata); + + return pa_sink_linked_by(u->sink); +} + +void pa__done(pa_module*m) { + struct userdata *u; + + pa_assert(m); + + if (!(u = m->userdata)) + return; + + if (u->sink) { + pa_sink_unlink(u->sink); + pa_sink_unref(u->sink); + } + + if (u->sink_input) { + pa_sink_input_unlink(u->sink_input); + pa_sink_input_unref(u->sink_input); + } + + if (u->memblockq) + pa_memblockq_free(u->memblockq); + + fftwf_destroy_plan(u->inverse_plan); + fftwf_destroy_plan(u->forward_plan); + fftwf_free(u->output_window); + for(size_t c=0;cchannels;++c){ + fftwf_free(u->output_buffer[c]); + fftwf_free(u->overlap_accum[c]); + fftwf_free(u->input[c]); + } + free(u->output_buffer); + free(u->overlap_accum); + free(u->input); + fftwf_free(u->work_buffer); + fftwf_free(u->W); + fftwf_free(u->H); + + pa_xfree(u); +} -- cgit From 2e119060cb6b61fa59976b636300eea913c20827 Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Wed, 15 Jul 2009 06:57:33 -0700 Subject: module-equalizer-sink: added temporary debugging output to track filter output removed dead code only a small amount of crackling remains --- src/modules/module-equalizer-sink.c | 268 ++++++++++++++---------------------- 1 file changed, 106 insertions(+), 162 deletions(-) (limited to 'src') diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c index 8b34fa0d..cabb0dc3 100755 --- a/src/modules/module-equalizer-sink.c +++ b/src/modules/module-equalizer-sink.c @@ -1,4 +1,29 @@ +/*** +This file is part of PulseAudio. +This module is based off Lennart Poettering's LADSPA sink and swaps out +LADSPA functionality for a STFT OLA based digital equalizer. All new work +is published under Pulseaudio's original license. +Copyright 2009 Jason Newton + +Original Author: +Copyright 2004-2008 Lennart Poettering + +PulseAudio is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published +by the Free Software Foundation; either version 2.1 of the License, +or (at your option) any later version. + +PulseAudio 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 Lesser General Public License +along with PulseAudio; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +USA. +***/ #ifdef HAVE_CONFIG_H #include @@ -25,9 +50,6 @@ #include #include #include -#include -#include - #include #include @@ -50,9 +72,15 @@ struct userdata { pa_sink_input *sink_input; size_t channels; - size_t fft_size; //length (res) of fft - size_t window_size;//even! - size_t overlap_size; + size_t fft_size;//length (res) of fft + size_t window_size;/* + *sliding window size + *effectively chooses R + */ + size_t R;/* the hop size between overlapping windows + * the latency of the filter, calculated from window_size + * based on constraints of COLA and window function + */ size_t samples_gathered; size_t n_buffered_output; size_t max_output; @@ -61,6 +89,7 @@ struct userdata { float *work_buffer,**input,**overlap_accum,**output_buffer; fftwf_complex *output_window; fftwf_plan forward_plan,inverse_plan; + //size_t samplings; pa_memblockq *memblockq; }; @@ -106,8 +135,9 @@ void hamming_window(float *W,size_t window_size){ m/=(window_size-1); W[i]=.54-.46*cos(2*M_PI*m); } - W[0]/=2; - W[window_size-1]/=2; + W[window_size-1]=0; + //W[0]/=2; + //W[window_size-1]/=2; } void blackman_window(float *W,size_t window_size){ //h=.42-.5*cos(2*pi*m)+.08*cos(4*pi*m), m=(0:W-1)/(W-1) @@ -132,6 +162,10 @@ void sin_window(float *W,size_t window_size){ void array_out(const char *name,float *a,size_t length){ FILE *p=fopen(name,"w"); + if(!p){ + pa_log("opening %s failed!",name); + return; + } for(size_t i=0;in_buffered_output>0){ //pa_log("outputing %ld buffered samples",u->n_buffered_output); chunk->index = 0; - size_t n_outputable=PA_MIN(u->n_buffered_output,nbytes/fs); + size_t n_outputable=PA_MIN(u->n_buffered_output,u->max_output); chunk->length = n_outputable*fs; chunk->memblock = pa_memblock_new(i->sink->core->mempool, chunk->length); pa_memblockq_drop(u->memblockq, chunk->length); @@ -245,10 +278,11 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk pa_assert_se(u->n_buffered_output==0); //collect the minimum number of samples - while(u->samples_gathered < (u->window_size-u->overlap_size)){ + //TODO figure out a better way of buffering the needed + //number of samples, this doesn't seem to work correctly + while(u->samples_gathered < u->R){ //render some new fragments to our memblockq - //size_t desired_samples=PA_MIN(u->min_input-samples_gathered,u->max_output); - size_t desired_samples=PA_MIN((u->window_size-u->overlap_size)-u->samples_gathered,u->max_output); + size_t desired_samples=PA_MIN(u->R-u->samples_gathered,u->max_output); while (pa_memblockq_peek(u->memblockq, &tchunk) < 0) { pa_memchunk nchunk; @@ -259,137 +293,80 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk if(tchunk.length/fs!=desired_samples){ pa_log("got %ld samples, asked for %ld",tchunk.length/fs,desired_samples); } - size_t n_samples=PA_MIN(tchunk.length/fs,u->window_size-u->overlap_size-u->samples_gathered); + size_t n_samples=PA_MIN(tchunk.length/fs,u->R-u->samples_gathered); //TODO: figure out what to do with rest of the samples when there's too many (rare?) src = (float*) ((uint8_t*) pa_memblock_acquire(tchunk.memblock) + tchunk.index); for (size_t c=0;cchannels;c++) { - pa_sample_clamp(PA_SAMPLE_FLOAT32NE,u->input[c]+u->overlap_size+u->samples_gathered,sizeof(float), src+c, fs, n_samples); + pa_sample_clamp(PA_SAMPLE_FLOAT32NE,u->input[c]+(u->window_size-u->R)+u->samples_gathered,sizeof(float), src+c, fs, n_samples); } - u->samples_gathered+=n_samples; pa_memblock_release(tchunk.memblock); pa_memblock_unref(tchunk.memblock); } //IT should be this guy if we're buffering like how its supposed to - //size_t n_outputable=PA_MIN(u->window_size-u->overlap_size,nbytes/fs); + //size_t n_outputable=PA_MIN(u->window_size-u->R,u->max_output); //This one takes into account the actual data gathered but then the dsp //stuff is wrong when the buffer "underruns" - size_t n_outputable=PA_MIN(u->samples_gathered,nbytes/fs); - /* - //debugging: tests if immediate release of freshly buffered data - //plays ok and prevents any other processing - chunk->index=0; - chunk->length=n_outputable*fs; - chunk->memblock = pa_memblock_new(i->sink->core->mempool, chunk->length); - pa_memblockq_drop(u->memblockq, chunk->length); - dst = (float*) pa_memblock_acquire(chunk->memblock);; - for (size_t c=0;cchannels;c++) { - pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst+c, fs, u->input[c]+u->overlap_size, sizeof(float),n_outputable); - } - u->samples_gathered=0; - pa_memblock_release(chunk->memblock); - return 0; - */ + size_t n_outputable=PA_MIN(u->R,u->max_output); - //pa_log("%ld dequed samples",u->samples_gathered); chunk->index=0; chunk->length=n_outputable*fs; chunk->memblock = pa_memblock_new(i->sink->core->mempool, chunk->length); pa_memblockq_drop(u->memblockq, chunk->length); dst = (float*) pa_memblock_acquire(chunk->memblock); - //pa_sample_clamp(PA_SAMPLE_FLOAT32NE, u->input, sizeof(float), src+c, fs, samples); - //pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst+c,fs, u->input, sizeof(float), samples); - - /* - struct timespec start, end; - uint64_t elapsed; - clock_gettime(CLOCK_MONOTONIC, &start); - */ - //use a zero-phase sliding dft and overlap-add method pa_assert_se(u->fft_size>=u->window_size); - //pa_assert_se(u->window_size%2==0); - pa_assert_se(u->overlap_sizewindow_size); - pa_assert_se(u->samples_gathered>=u->window_size-u->overlap_size); - size_t sample_rem=u->window_size-u->overlap_size-n_outputable; - //size_t w_mid=u->window_size/2; - //pa_log("hello world a"); - for (c=0;cchannels;c++) { - //center the data for zero phase - //zero-pad TODO: optimization if sure these zeros aren't overwritten - //memset(u->work_buffer+w_mid,0,(u->fft_size-u->window_size)*sizeof(float)); + pa_assert_se(u->Rwindow_size); + pa_assert_se(u->samples_gathered>=u->R); + size_t sample_rem=u->R-n_outputable; + //use a linear-phase sliding STFT and overlap-add method (for each channel) + for (size_t c=0;cchannels;c++) { + ////zero padd the data //memset(u->work_buffer,0,u->fft_size*sizeof(float)); - /* - for(size_t j=0;jwindow_size;++j){ - u->work_buffer[j]=u->W[j]*u->input[c][j]; - u->work_buffer[j]=u->input[c][j]; - } - */ - //zero padd the data, don't worry about zerophase, shouldn't really matter - memset(u->work_buffer+u->overlap_size,0,(u->fft_size-u->overlap_size)*sizeof(float)); - //window the data + memset(u->work_buffer+u->window_size,0,(u->fft_size-u->window_size)*sizeof(float)); + ////window the data for(size_t j=0;jwindow_size;++j){ u->work_buffer[j]=u->W[j]*u->input[c][j]; } - /* - //recenter for zero phase - for(size_t j=0;jwork_buffer[j]; - u->work_buffer[j]=u->input[c][j+w_mid]; - u->work_buffer[j+u->fft_size-w_mid]=tmp; - } - */ - //pa_log("hello world b"); - - /* - //window and zero phase shift - for(size_t j=0;jwork_buffer[j]=u->input[c][j+w_mid]; - //u->work_buffer[j+u->fft_size-w_mid]=u->input[c][j]; - u->work_buffer[j]=u->W[j+w_mid]*u->input[c][j+w_mid]; - u->work_buffer[j+u->fft_size-w_mid]=u->W[j]*u->input[c][j]; - }*/ //Processing is done here! //do fft + //char fname[1024]; + //if(u->samplings==200){ + // pa_assert_se(0); + //} + + //this iterations input + //sprintf(fname,"/home/jason/input%ld-%ld.txt",u->samplings+1,c); + //array_out(fname,u->input[c]+(u->window_size-u->R),u->R); + fftwf_execute_dft_r2c(u->forward_plan,u->work_buffer,u->output_window); //perform filtering for(size_t j=0;jfft_size/2+1;++j){ - ////identity transform (fft size) - //u->output_window[j][0]/=u->fft_size; - //u->output_window[j][1]/=u->fft_size; - ////identity transform (window size) - //u->output_window[j][0]/=u->window_size; - //u->output_window[j][1]/=u->window_size; - //filtered u->output_window[j][0]*=u->H[j]; u->output_window[j][1]*=u->H[j]; } - //inverse fft + ////inverse fft fftwf_execute_dft_c2r(u->inverse_plan,u->output_window,u->work_buffer); + //the output for the previous iteration's input + //sprintf(fname,"/home/jason/output%ld-%ld.txt",u->samplings,c); + //array_out(fname,u->work_buffer,u->window_size); - /* - //uncenter the data - for(size_t j=0;jwork_buffer[j]; - u->work_buffer[j]=u->work_buffer[j+u->fft_size-w_mid]; - u->work_buffer[j+w_mid]=tmp; - } - */ - /* - //divide out fft gain (more stable here?) - for(size_t j=0;jwindow_size;++j){ - u->work_buffer[j]/=u->fft_size; - } - */ - /* - //debug: tests overlaping add - //and negates ALL PREVIOUS processing - //yields a perfect reconstruction if COLA is held - for(size_t j=0;jwindow_size;++j){ - u->work_buffer[j]=u->W[j]*u->input[c][j]; + + ////debug: tests overlaping add + ////and negates ALL PREVIOUS processing + ////yields a perfect reconstruction if COLA is held + //for(size_t j=0;jwindow_size;++j){ + // u->work_buffer[j]=u->W[j]*u->input[c][j]; + //} + + //overlap add and preserve overlap component from this window (linear phase) + for(size_t j=0;jR;++j){ + u->work_buffer[j]+=u->overlap_accum[c][j]; + u->overlap_accum[c][j]=u->work_buffer[u->window_size-u->R+j]; } - */ + + /* //debug: tests if basic buffering works //shouldn't modify the signal AT ALL @@ -398,40 +375,20 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk } */ - /* - //overlap add and preserve overlap component from this window (zero phase) - for(size_t j=0;joverlap_size;++j){ - u->work_buffer[j]+=u->overlap_accum[c][j]; - u->overlap_accum[c][j]=u->work_buffer[u->window_size-u->overlap_size+j]; - } - */ - //overlap add and preserve overlap component from this window (linear phase) - for(size_t j=0;joverlap_size;++j){ - u->work_buffer[j]+=u->overlap_accum[c][j]; - u->overlap_accum[c][j]=u->work_buffer[u->window_size-u->overlap_size+j]; - } - //preseve the needed input for the next windows overlap - memmove(u->input[c],u->input[c]+u->overlap_size,(u->window_size-u->overlap_size)*sizeof(float)); + memmove(u->input[c],u->input[c]+u->R, + (u->window_size-u->R)*sizeof(float) + ); //output the samples that are outputable now pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst+c, fs, u->work_buffer, sizeof(float),n_outputable); //buffer the rest of them memcpy(u->output_buffer[c]+u->n_buffered_output,u->work_buffer+n_outputable,sample_rem*sizeof(float)); + } - /* - clock_gettime(CLOCK_MONOTONIC, &end); - elapsed=time_diff(&end, &start); - pa_log("processed: %ld, time: %ld",u->samples_gathered,elapsed); - */ + //u->samplings++; u->n_buffered_output+=sample_rem; u->samples_gathered=0; - - - //pa_log("%ld samples queued",u->n_buffered_output); - pa_memblock_release(chunk->memblock); - - return 0; } @@ -456,6 +413,8 @@ static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) { if (amount > 0) { pa_memblockq_seek(u->memblockq, - (int64_t) amount, PA_SEEK_RELATIVE, TRUE); pa_log_debug("Resetting equalizer"); + u->n_buffered_output=0; + u->samples_gathered=0; } } @@ -621,18 +580,12 @@ int pa__init(pa_module*m) { u->sink_input = NULL; u->memblockq = pa_memblockq_new(0, MEMBLOCKQ_MAXLENGTH, 0, fs, 1, 1, 0, NULL); - //u->fft_size=44100; - //u->fft_size=48000; - //u->fft_size=1024; + //u->samplings=0; u->channels=ss.channels; u->fft_size=pow(2,ceil(log(ss.rate)/log(2))); - //u->fft_size=ss.rate; - //u->fft_size=65536; pa_log("fft size: %ld",u->fft_size); - u->window_size=8001; - u->overlap_size=(u->window_size+1)/2; - //u->overlap_size=u->window_size/2; - //u->overlap_size=0; + u->window_size=7999; + u->R=(u->window_size+1)/2; u->samples_gathered=0; u->n_buffered_output=0; u->max_output=pa_frame_align(pa_mempool_block_size_max(m->core->mempool), &ss)/pa_frame_size(&ss); @@ -645,34 +598,26 @@ int pa__init(pa_module*m) { for(size_t c=0;cchannels;++c){ u->input[c]=(float*) fftwf_malloc(u->window_size*sizeof(float)); memset(u->input[c],0,u->window_size*sizeof(float)); - u->overlap_accum[c]=(float*) fftwf_malloc(u->overlap_size*sizeof(float)); - memset(u->overlap_accum[c],0,u->overlap_size*sizeof(float)); + u->overlap_accum[c]=(float*) fftwf_malloc(u->R*sizeof(float)); + memset(u->overlap_accum[c],0,u->R*sizeof(float)); u->output_buffer[c]=(float*) fftwf_malloc(u->window_size*sizeof(float)); } u->output_window = (fftwf_complex *) fftwf_malloc(sizeof(fftwf_complex) * (u->fft_size/2+1)); u->forward_plan=fftwf_plan_dft_r2c_1d(u->fft_size, u->work_buffer, u->output_window, FFTW_ESTIMATE); u->inverse_plan=fftwf_plan_dft_c2r_1d(u->fft_size, u->output_window, u->work_buffer, FFTW_ESTIMATE); - /* - //rectangular window for(size_t j=0;jwindow_size;++j){ - u->W[j]=1.0; + u->W[j]=.5; } */ - //hanning_normalized_window(u->W,u->window_size); hanning_window(u->W,u->window_size); - //sin_window(u->W,u->window_size); - array_out("/home/jason/window.txt",u->W,u->window_size); - //u->forward_plan=fftwf_plan_dft_r2c_1d(u->fft_size, u->input, u->output_window, FFTW_ESTIMATE); - //u->inverse_plan=fftwf_plan_dft_c2r_1d(u->fft_size, u->output_window, u->work_buffer, FFTW_ESTIMATE); - //u->forward_plan=fftwf_plan_dft_r2c_1d(u->fft_size, u->input, u->output, FFTW_MEASURE); - //u->inverse_plan=fftwf_plan_dft_c2r_1d(u->fft_size, u->output, u->input, FFTW_MEASURE); + const int freqs[]={0,25,50,100,200,300,400,800,1500, 2000,3000,4000,5000,6000,7000,8000,9000,10000,11000,12000, 13000,14000,15000,16000,17000,18000,19000,20000,21000,22000,23000,24000,INT_MAX}; - const float coefficients[]={1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}; + const float coefficients[]={.1,.1,.1,.1,.1,.1,.1,.1,.1,.1, + .1,.1,.1,.1,.1,.1,.1,.1, + .1,.1,.1,.1,.1,.1,.1,.1,.1,.1,.1,.1,.1,.1,.1}; const size_t ncoefficients=sizeof(coefficients)/sizeof(float); pa_assert_se(sizeof(freqs)/sizeof(int)==sizeof(coefficients)/sizeof(float)); float *freq_translated=(float *) malloc(sizeof(float)*(ncoefficients)); @@ -683,13 +628,13 @@ int pa__init(pa_module*m) { //pa_log("i: %ld: %d , %g",i,freqs[i],freq_translated[i]); pa_assert_se(freq_translated[i]>=freq_translated[i-1]); } - freq_translated[ncoefficients-1]=DBL_MAX; + freq_translated[ncoefficients-1]=FLT_MAX; //Interpolate the specified frequency band values u->H[0]=1; for(size_t i=1,j=0;i<(u->fft_size/2+1);++i){ pa_assert_se(j=DBL_MAX){ + if(freq_translated[j+1]>=FLT_MAX){ for(;i<(u->fft_size/2+1);++i){ u->H[i]=coefficients[j]; } @@ -709,9 +654,8 @@ int pa__init(pa_module*m) { j++; } } - array_out("/home/jason/coffs.txt",u->H,u->fft_size/2+1); //divide out the fft gain - for(int i=0;i<(u->fft_size/2+1);++i){ + for(size_t i=0;i<(u->fft_size/2+1);++i){ u->H[i]/=u->fft_size; } free(freq_translated); -- cgit From 182c9c7dcb3c7e6b8d273ab85e6f3e67070a4694 Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Thu, 16 Jul 2009 18:18:14 -0700 Subject: module-equalizer-sink: added more assertions to aid in debugging --- src/modules/module-equalizer-sink.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c index cabb0dc3..d4cec3cd 100755 --- a/src/modules/module-equalizer-sink.c +++ b/src/modules/module-equalizer-sink.c @@ -251,8 +251,9 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk pa_sink_input_assert_ref(i); pa_assert(chunk); pa_assert_se(u = i->userdata); - size_t fs = pa_frame_size(&u->sink->sample_spec); - size_t ss=pa_sample_size(&u->sink->sample_spec); + pa_assert_se(u->sink); + size_t fs = pa_frame_size(&(u->sink->sample_spec)); + size_t ss=pa_sample_size(&(u->sink->sample_spec)); size_t fe = fs/ss; if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state)) @@ -597,10 +598,14 @@ int pa__init(pa_module*m) { u->output_buffer=(float **)malloc(sizeof(float *)*u->channels); for(size_t c=0;cchannels;++c){ u->input[c]=(float*) fftwf_malloc(u->window_size*sizeof(float)); + pa_assert_se(u->input[c]); memset(u->input[c],0,u->window_size*sizeof(float)); + pa_assert_se(u->input[c]); u->overlap_accum[c]=(float*) fftwf_malloc(u->R*sizeof(float)); + pa_assert_se(u->overlap_accum[c]); memset(u->overlap_accum[c],0,u->R*sizeof(float)); u->output_buffer[c]=(float*) fftwf_malloc(u->window_size*sizeof(float)); + pa_assert_se(u->output_buffer[c]); } u->output_window = (fftwf_complex *) fftwf_malloc(sizeof(fftwf_complex) * (u->fft_size/2+1)); u->forward_plan=fftwf_plan_dft_r2c_1d(u->fft_size, u->work_buffer, u->output_window, FFTW_ESTIMATE); @@ -615,9 +620,9 @@ int pa__init(pa_module*m) { const int freqs[]={0,25,50,100,200,300,400,800,1500, 2000,3000,4000,5000,6000,7000,8000,9000,10000,11000,12000, 13000,14000,15000,16000,17000,18000,19000,20000,21000,22000,23000,24000,INT_MAX}; - const float coefficients[]={.1,.1,.1,.1,.1,.1,.1,.1,.1,.1, - .1,.1,.1,.1,.1,.1,.1,.1, - .1,.1,.1,.1,.1,.1,.1,.1,.1,.1,.1,.1,.1,.1,.1}; + const float coefficients[]={1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}; const size_t ncoefficients=sizeof(coefficients)/sizeof(float); pa_assert_se(sizeof(freqs)/sizeof(int)==sizeof(coefficients)/sizeof(float)); float *freq_translated=(float *) malloc(sizeof(float)*(ncoefficients)); -- cgit From d4fe5bfce988765fd51d291c61217ffef9df7698 Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Thu, 16 Jul 2009 22:00:38 -0700 Subject: module-equalizer-sink: attempt different buffering strategy --- src/modules/module-equalizer-sink.c | 51 ++++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 24 deletions(-) (limited to 'src') diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c index d4cec3cd..d6e28f3d 100755 --- a/src/modules/module-equalizer-sink.c +++ b/src/modules/module-equalizer-sink.c @@ -259,29 +259,11 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state)) return -1; - //output any buffered outputs first - if(u->n_buffered_output>0){ - //pa_log("outputing %ld buffered samples",u->n_buffered_output); - chunk->index = 0; - size_t n_outputable=PA_MIN(u->n_buffered_output,u->max_output); - chunk->length = n_outputable*fs; - chunk->memblock = pa_memblock_new(i->sink->core->mempool, chunk->length); - pa_memblockq_drop(u->memblockq, chunk->length); - dst = (float*) pa_memblock_acquire(chunk->memblock); - for(size_t j=0;jchannels;++j){ - pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst+j, fs, u->output_buffer[j], sizeof(float),n_outputable); - memmove(u->output_buffer[j],u->output_buffer[j]+n_outputable,(u->n_buffered_output-n_outputable)*sizeof(float)); - } - u->n_buffered_output-=n_outputable; - pa_memblock_release(chunk->memblock); - return 0; - } - pa_assert_se(u->n_buffered_output==0); - //collect the minimum number of samples //TODO figure out a better way of buffering the needed //number of samples, this doesn't seem to work correctly - while(u->samples_gathered < u->R){ + //most of the itme + if(u->samples_gathered < u->R){ //render some new fragments to our memblockq size_t desired_samples=PA_MIN(u->R-u->samples_gathered,u->max_output); while (pa_memblockq_peek(u->memblockq, &tchunk) < 0) { @@ -295,7 +277,7 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk pa_log("got %ld samples, asked for %ld",tchunk.length/fs,desired_samples); } size_t n_samples=PA_MIN(tchunk.length/fs,u->R-u->samples_gathered); - //TODO: figure out what to do with rest of the samples when there's too many (rare?) + pa_assert_se(n_samples<=u->R-u->samples_gathered); src = (float*) ((uint8_t*) pa_memblock_acquire(tchunk.memblock) + tchunk.index); for (size_t c=0;cchannels;c++) { pa_sample_clamp(PA_SAMPLE_FLOAT32NE,u->input[c]+(u->window_size-u->R)+u->samples_gathered,sizeof(float), src+c, fs, n_samples); @@ -304,12 +286,33 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk pa_memblock_release(tchunk.memblock); pa_memblock_unref(tchunk.memblock); } + //output any buffered outputs first + if(u->n_buffered_output>0){ + //pa_log("outputing %ld buffered samples",u->n_buffered_output); + chunk->index = 0; + size_t n_outputable=PA_MIN(u->n_buffered_output,u->max_output); + chunk->length = n_outputable*fs; + chunk->memblock = pa_memblock_new(i->sink->core->mempool, chunk->length); + pa_memblockq_drop(u->memblockq, chunk->length); + dst = (float*) pa_memblock_acquire(chunk->memblock); + for(size_t j=0;jchannels;++j){ + pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst+j, fs, u->output_buffer[j], sizeof(float),n_outputable); + memmove(u->output_buffer[j],u->output_buffer[j]+n_outputable,(u->n_buffered_output-n_outputable)*sizeof(float)); + } + u->n_buffered_output-=n_outputable; + pa_memblock_release(chunk->memblock); + return 0; + } + pa_assert_se(u->n_buffered_output==0); + + if(u->samples_gatheredR){ + return -1; + } //IT should be this guy if we're buffering like how its supposed to //size_t n_outputable=PA_MIN(u->window_size-u->R,u->max_output); //This one takes into account the actual data gathered but then the dsp //stuff is wrong when the buffer "underruns" - size_t n_outputable=PA_MIN(u->R,u->max_output); - + size_t n_outputable=PA_MIN(u->R,u->max_output)*(u->R==u->samples_gathered); chunk->index=0; chunk->length=n_outputable*fs; @@ -319,7 +322,6 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk pa_assert_se(u->fft_size>=u->window_size); pa_assert_se(u->Rwindow_size); - pa_assert_se(u->samples_gathered>=u->R); size_t sample_rem=u->R-n_outputable; //use a linear-phase sliding STFT and overlap-add method (for each channel) for (size_t c=0;cchannels;c++) { @@ -389,6 +391,7 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk //u->samplings++; u->n_buffered_output+=sample_rem; u->samples_gathered=0; +end: pa_memblock_release(chunk->memblock); return 0; } -- cgit From cf8331a0da9df5e4eff6105a002f4912be673d0a Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Sat, 18 Jul 2009 01:00:35 -0700 Subject: module-equalizer-sink: trying new buffering strategies --- src/modules/module-equalizer-sink.c | 303 +++++++++++++++++++----------------- src/pulsecore/memblock.c | 2 +- 2 files changed, 160 insertions(+), 145 deletions(-) (limited to 'src') diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c index d6e28f3d..1d4a423d 100755 --- a/src/modules/module-equalizer-sink.c +++ b/src/modules/module-equalizer-sink.c @@ -81,9 +81,10 @@ struct userdata { * the latency of the filter, calculated from window_size * based on constraints of COLA and window function */ + size_t overlap_size;//window_size-R size_t samples_gathered; - size_t n_buffered_output; size_t max_output; + size_t target_samples; float *H;//frequency response filter (magnitude based) float *W;//windowing function (time domain) float *work_buffer,**input,**overlap_accum,**output_buffer; @@ -91,7 +92,8 @@ struct userdata { fftwf_plan forward_plan,inverse_plan; //size_t samplings; - pa_memblockq *memblockq; + pa_memchunk conv_buffer; + pa_memblockq *rendered_q; }; static const char* const valid_modargs[] = { @@ -186,12 +188,14 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse case PA_SINK_MESSAGE_GET_LATENCY: { pa_usec_t usec = 0; pa_sample_spec *ss=&u->sink->sample_spec; + size_t fs=pa_frame_size(ss); /* Get the latency of the master sink */ if (PA_MSGOBJECT(u->master)->process_msg(PA_MSGOBJECT(u->master), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0) usec = 0; - usec+=pa_bytes_to_usec(u->n_buffered_output*pa_frame_size(ss),ss); + usec+=pa_bytes_to_usec(u->samples_gathered*fs,ss); + usec += pa_bytes_to_usec(pa_memblockq_get_length(u->rendered_q), ss); /* Add the latency internal to our sink input on top */ usec += pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->master->sample_spec); *((pa_usec_t*) data) = usec; @@ -227,7 +231,7 @@ static void sink_request_rewind(pa_sink *s) { pa_assert_se(u = s->userdata); /* Just hand this one over to the master sink */ - pa_sink_input_request_rewind(u->sink_input, s->thread_info.rewind_nbytes + pa_memblockq_get_length(u->memblockq), TRUE, FALSE, FALSE); + pa_sink_input_request_rewind(u->sink_input, s->thread_info.rewind_nbytes + pa_memblockq_get_length(u->rendered_q), TRUE, FALSE, FALSE); } /* Called from I/O thread context */ @@ -246,153 +250,159 @@ static void sink_update_requested_latency(pa_sink *s) { /* Called from I/O thread context */ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) { struct userdata *u; - float *src, *dst; - pa_memchunk tchunk; pa_sink_input_assert_ref(i); pa_assert(chunk); pa_assert_se(u = i->userdata); pa_assert_se(u->sink); - size_t fs = pa_frame_size(&(u->sink->sample_spec)); + size_t fs=pa_frame_size(&(u->sink->sample_spec)); size_t ss=pa_sample_size(&(u->sink->sample_spec)); size_t fe = fs/ss; + size_t samples_requested=nbytes/fs; + pa_memchunk tchunk; + chunk->memblock=NULL; + size_t buffered_samples=pa_memblockq_get_length(u->rendered_q)/fs; if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state)) return -1; - //collect the minimum number of samples - //TODO figure out a better way of buffering the needed - //number of samples, this doesn't seem to work correctly - //most of the itme - if(u->samples_gathered < u->R){ - //render some new fragments to our memblockq - size_t desired_samples=PA_MIN(u->R-u->samples_gathered,u->max_output); - while (pa_memblockq_peek(u->memblockq, &tchunk) < 0) { - pa_memchunk nchunk; - - pa_sink_render(u->sink, desired_samples*fs, &nchunk); - pa_memblockq_push(u->memblockq, &nchunk); - pa_memblock_unref(nchunk.memblock); - } - if(tchunk.length/fs!=desired_samples){ - pa_log("got %ld samples, asked for %ld",tchunk.length/fs,desired_samples); + pa_log("start output-buffered %ld, input-buffered %ld",buffered_samples,u->samples_gathered); + //collect samples + size_t buffered_remaining=pa_memblockq_get_length(u->rendered_q)/fs; + size_t buffer_missing=pa_memblockq_missing(u->rendered_q)/fs; + size_t desired_samples=(buffer_missing>=u->R)*PA_MIN(u->target_samples-u->samples_gathered,buffer_missing); + if(desired_samples>0){ + u->conv_buffer.index=0; + //if we still had buffered output, + //or can gather any more in the buffer + //politely request (optimistic) + if(buffered_samples>=samples_requested || + (u->samples_gathered/u->R)*u->R>=samples_requested){ + u->conv_buffer.length=desired_samples*fs; + pa_log("trying to buffer %ld samples",desired_samples); + pa_sink_render_into(u->sink, &u->conv_buffer); + }else{//we need it now! force it + //TODO: minimum amount or the whole buffer better? + desired_samples=u->R-u->samples_gathered%u->R; + u->conv_buffer.length=desired_samples*fs; + pa_log("force-buffer %ld samples",desired_samples); + pa_sink_render_into_full(u->sink, &u->conv_buffer); + pa_assert_se(u->conv_buffer.length==desired_samples*fs); } - size_t n_samples=PA_MIN(tchunk.length/fs,u->R-u->samples_gathered); - pa_assert_se(n_samples<=u->R-u->samples_gathered); - src = (float*) ((uint8_t*) pa_memblock_acquire(tchunk.memblock) + tchunk.index); + size_t n_samples=u->conv_buffer.length/fs; + float *src; + pa_log("received %ld samples",n_samples); + + pa_assert_se(n_samples<=u->target_samples-u->samples_gathered); + src = (float*) ((uint8_t*) pa_memblock_acquire(u->conv_buffer.memblock) + u->conv_buffer.index); for (size_t c=0;cchannels;c++) { - pa_sample_clamp(PA_SAMPLE_FLOAT32NE,u->input[c]+(u->window_size-u->R)+u->samples_gathered,sizeof(float), src+c, fs, n_samples); + //buffer with an offset after the overlap from previous + //iterations + pa_assert_se( + u->input[c]+u->overlap_size+u->samples_gathered+n_samples<=u->input[c]+u->target_samples+u->overlap_size + ); + pa_sample_clamp(PA_SAMPLE_FLOAT32NE,u->input[c]+u->overlap_size+u->samples_gathered,sizeof(float), src+c, fs, n_samples); } u->samples_gathered+=n_samples; - pa_memblock_release(tchunk.memblock); - pa_memblock_unref(tchunk.memblock); - } - //output any buffered outputs first - if(u->n_buffered_output>0){ - //pa_log("outputing %ld buffered samples",u->n_buffered_output); - chunk->index = 0; - size_t n_outputable=PA_MIN(u->n_buffered_output,u->max_output); - chunk->length = n_outputable*fs; - chunk->memblock = pa_memblock_new(i->sink->core->mempool, chunk->length); - pa_memblockq_drop(u->memblockq, chunk->length); - dst = (float*) pa_memblock_acquire(chunk->memblock); - for(size_t j=0;jchannels;++j){ - pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst+j, fs, u->output_buffer[j], sizeof(float),n_outputable); - memmove(u->output_buffer[j],u->output_buffer[j]+n_outputable,(u->n_buffered_output-n_outputable)*sizeof(float)); - } - u->n_buffered_output-=n_outputable; - pa_memblock_release(chunk->memblock); - return 0; - } - pa_assert_se(u->n_buffered_output==0); - - if(u->samples_gatheredR){ - return -1; + pa_memblock_release(u->conv_buffer.memblock); } - //IT should be this guy if we're buffering like how its supposed to - //size_t n_outputable=PA_MIN(u->window_size-u->R,u->max_output); - //This one takes into account the actual data gathered but then the dsp - //stuff is wrong when the buffer "underruns" - size_t n_outputable=PA_MIN(u->R,u->max_output)*(u->R==u->samples_gathered); - - chunk->index=0; - chunk->length=n_outputable*fs; - chunk->memblock = pa_memblock_new(i->sink->core->mempool, chunk->length); - pa_memblockq_drop(u->memblockq, chunk->length); - dst = (float*) pa_memblock_acquire(chunk->memblock); - + //pa_assert_se(u->samples_gathered>=u->R); pa_assert_se(u->fft_size>=u->window_size); pa_assert_se(u->Rwindow_size); - size_t sample_rem=u->R-n_outputable; - //use a linear-phase sliding STFT and overlap-add method (for each channel) - for (size_t c=0;cchannels;c++) { - ////zero padd the data - //memset(u->work_buffer,0,u->fft_size*sizeof(float)); - memset(u->work_buffer+u->window_size,0,(u->fft_size-u->window_size)*sizeof(float)); - ////window the data - for(size_t j=0;jwindow_size;++j){ - u->work_buffer[j]=u->W[j]*u->input[c][j]; - } - //Processing is done here! - //do fft - //char fname[1024]; - //if(u->samplings==200){ - // pa_assert_se(0); - //} - - //this iterations input - //sprintf(fname,"/home/jason/input%ld-%ld.txt",u->samplings+1,c); - //array_out(fname,u->input[c]+(u->window_size-u->R),u->R); - - fftwf_execute_dft_r2c(u->forward_plan,u->work_buffer,u->output_window); - //perform filtering - for(size_t j=0;jfft_size/2+1;++j){ - u->output_window[j][0]*=u->H[j]; - u->output_window[j][1]*=u->H[j]; - } - ////inverse fft - fftwf_execute_dft_c2r(u->inverse_plan,u->output_window,u->work_buffer); - //the output for the previous iteration's input - //sprintf(fname,"/home/jason/output%ld-%ld.txt",u->samplings,c); - //array_out(fname,u->work_buffer,u->window_size); - - - ////debug: tests overlaping add - ////and negates ALL PREVIOUS processing - ////yields a perfect reconstruction if COLA is held - //for(size_t j=0;jwindow_size;++j){ - // u->work_buffer[j]=u->W[j]*u->input[c][j]; - //} - - //overlap add and preserve overlap component from this window (linear phase) - for(size_t j=0;jR;++j){ - u->work_buffer[j]+=u->overlap_accum[c][j]; - u->overlap_accum[c][j]=u->work_buffer[u->window_size-u->R+j]; - } - - - /* - //debug: tests if basic buffering works - //shouldn't modify the signal AT ALL - for(size_t j=0;jwindow_size;++j){ - u->work_buffer[j]=u->input[c][j]; + //process every complete block on hand + while(u->samples_gathered>=u->R&&buffer_missing>=u->R){ + float *dst; + //pa_log("iter gathered: %ld",u->samples_gathered); + tchunk.index=0; + tchunk.length=u->R*fs; + tchunk.memblock=pa_memblock_new(u->core->mempool,tchunk.length); + //pa_memblockq_drop(u->rendered_q, tchunk.length); + pa_assert_se(tchunk.length==u->R*fs); + dst=(float*)pa_memblock_acquire(tchunk.memblock); + //use a linear-phase sliding STFT and overlap-add method (for each channel) + for (size_t c=0;cchannels;c++) { + //zero padd the data + memset(u->work_buffer+u->window_size,0,(u->fft_size-u->window_size)*sizeof(float)); + //window the data + for(size_t j=0;jwindow_size;++j){ + u->work_buffer[j]=u->W[j]*u->input[c][j]; + } + //Processing is done here! + //do fft + fftwf_execute_dft_r2c(u->forward_plan,u->work_buffer,u->output_window); + //perform filtering + for(size_t j=0;jfft_size/2+1;++j){ + u->output_window[j][0]*=u->H[j]; + u->output_window[j][1]*=u->H[j]; + } + //inverse fft + fftwf_execute_dft_c2r(u->inverse_plan,u->output_window,u->work_buffer); + ////debug: tests overlaping add + ////and negates ALL PREVIOUS processing + ////yields a perfect reconstruction if COLA is held + //for(size_t j=0;jwindow_size;++j){ + // u->work_buffer[j]=u->W[j]*u->input[c][j]; + //} + + //overlap add and preserve overlap component from this window (linear phase) + for(size_t j=0;jR;++j){ + u->work_buffer[j]+=u->overlap_accum[c][j]; + u->overlap_accum[c][j]=u->work_buffer[u->overlap_size+j]; + } + + //debug: tests if basic buffering works + //shouldn't modify the signal AT ALL (beyond roundoff) + for(size_t j=0;jwindow_size;++j){ + u->work_buffer[j]=u->input[c][j]; + } + + //preseve the needed input for the next window's overlap + memmove(u->input[c],u->input[c]+u->R, + (u->samples_gathered+u->overlap_size-u->R)*sizeof(float) + ); + //output the samples that are outputable now + pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst+c, fs, u->work_buffer, sizeof(float),u->R); } - */ - - //preseve the needed input for the next windows overlap - memmove(u->input[c],u->input[c]+u->R, - (u->window_size-u->R)*sizeof(float) - ); - //output the samples that are outputable now - pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst+c, fs, u->work_buffer, sizeof(float),n_outputable); - //buffer the rest of them - memcpy(u->output_buffer[c]+u->n_buffered_output,u->work_buffer+n_outputable,sample_rem*sizeof(float)); - + pa_memblock_release(tchunk.memblock); + pa_memblockq_push(u->rendered_q, &tchunk); + pa_memblock_unref(tchunk.memblock); + u->samples_gathered-=u->R; + buffer_missing-=u->R; } - //u->samplings++; - u->n_buffered_output+=sample_rem; - u->samples_gathered=0; -end: - pa_memblock_release(chunk->memblock); + //deque from renderq and output + //pa_memblockq_set_prebuf(u->rendered_q,samples_requested*fs); + pa_assert_se(pa_memblockq_peek(u->rendered_q,&tchunk)>=0); + if(tchunk.length>=nbytes){ + *chunk=tchunk; + chunk->length=samples_requested*fs; + pa_memblock_ref(chunk->memblock); + pa_memblock_unref(tchunk.memblock); + pa_memblockq_drop(u->rendered_q, chunk->length); + }else{ + size_t copied=0; + chunk->length=nbytes; + chunk->memblock=pa_memblock_new(u->core->mempool,chunk->length); + uint8_t *dst=(uint8_t*)pa_memblock_acquire(chunk->memblock); + do{ + size_t l=PA_MIN(tchunk.length-tchunk.index,nbytes-copied); + uint8_t *src=(((uint8_t*)pa_memblock_acquire(tchunk.memblock))+tchunk.index); + memmove(dst+copied,src,l); + copied+=l; + pa_memblock_release(tchunk.memblock); + pa_memblock_unref(tchunk.memblock); + pa_memblockq_drop(u->rendered_q,l); + if(copiedrendered_q)==0){ + chunk->length=copied; + break; + } + pa_memblockq_peek(u->rendered_q,&tchunk); + } + }while(copiedmemblock); + } + pa_assert_se(chunk->memblock); + pa_log("output requested %ld, gave %ld",nbytes/fs,chunk->length/fs); + //pa_log("end pop"); return 0; } @@ -410,20 +420,19 @@ static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) { if (u->sink->thread_info.rewind_nbytes > 0) { size_t max_rewrite; - max_rewrite = nbytes + pa_memblockq_get_length(u->memblockq); + max_rewrite = nbytes + pa_memblockq_get_length(u->rendered_q); amount = PA_MIN(u->sink->thread_info.rewind_nbytes, max_rewrite); u->sink->thread_info.rewind_nbytes = 0; if (amount > 0) { - pa_memblockq_seek(u->memblockq, - (int64_t) amount, PA_SEEK_RELATIVE, TRUE); + pa_memblockq_seek(u->rendered_q, - (int64_t) amount, PA_SEEK_RELATIVE, TRUE); pa_log_debug("Resetting equalizer"); - u->n_buffered_output=0; u->samples_gathered=0; } } pa_sink_process_rewind(u->sink, amount); - pa_memblockq_rewind(u->memblockq, nbytes); + pa_memblockq_rewind(u->rendered_q, nbytes); } /* Called from I/O thread context */ @@ -436,7 +445,7 @@ static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) { if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state)) return; - pa_memblockq_set_maxrewind(u->memblockq, nbytes); + pa_memblockq_set_maxrewind(u->rendered_q, nbytes); pa_sink_set_max_rewind_within_thread(u->sink, nbytes); } @@ -582,17 +591,20 @@ int pa__init(pa_module*m) { u->master = master; u->sink = NULL; u->sink_input = NULL; - u->memblockq = pa_memblockq_new(0, MEMBLOCKQ_MAXLENGTH, 0, fs, 1, 1, 0, NULL); - //u->samplings=0; u->channels=ss.channels; u->fft_size=pow(2,ceil(log(ss.rate)/log(2))); pa_log("fft size: %ld",u->fft_size); u->window_size=7999; u->R=(u->window_size+1)/2; + u->overlap_size=u->window_size-u->R; + u->target_samples=5*u->R; u->samples_gathered=0; - u->n_buffered_output=0; u->max_output=pa_frame_align(pa_mempool_block_size_max(m->core->mempool), &ss)/pa_frame_size(&ss); + u->rendered_q = pa_memblockq_new(0, MEMBLOCKQ_MAXLENGTH,u->target_samples*fs, fs, fs, 0, 0, NULL); + u->conv_buffer.memblock=pa_memblock_new(u->core->mempool,u->target_samples*fs); + + u->H=(float*) fftwf_malloc((u->fft_size/2+1)*sizeof(float)); u->W=(float*) fftwf_malloc((u->window_size)*sizeof(float)); u->work_buffer=(float*) fftwf_malloc(u->fft_size*sizeof(float)); @@ -600,9 +612,9 @@ int pa__init(pa_module*m) { u->overlap_accum=(float **)malloc(sizeof(float *)*u->channels); u->output_buffer=(float **)malloc(sizeof(float *)*u->channels); for(size_t c=0;cchannels;++c){ - u->input[c]=(float*) fftwf_malloc(u->window_size*sizeof(float)); + u->input[c]=(float*) fftwf_malloc((u->target_samples+u->overlap_size)*sizeof(float)); pa_assert_se(u->input[c]); - memset(u->input[c],0,u->window_size*sizeof(float)); + memset(u->input[c],0,(u->target_samples+u->overlap_size)*sizeof(float)); pa_assert_se(u->input[c]); u->overlap_accum[c]=(float*) fftwf_malloc(u->R*sizeof(float)); pa_assert_se(u->overlap_accum[c]); @@ -780,8 +792,11 @@ void pa__done(pa_module*m) { pa_sink_input_unref(u->sink_input); } - if (u->memblockq) - pa_memblockq_free(u->memblockq); + if(u->conv_buffer.memblock) + pa_memblock_unref(u->conv_buffer.memblock); + + if (u->rendered_q) + pa_memblockq_free(u->rendered_q); fftwf_destroy_plan(u->inverse_plan); fftwf_destroy_plan(u->forward_plan); diff --git a/src/pulsecore/memblock.c b/src/pulsecore/memblock.c index f38b17c6..eac4a59b 100644 --- a/src/pulsecore/memblock.c +++ b/src/pulsecore/memblock.c @@ -54,7 +54,7 @@ * stored in SHM and our OS does not commit the memory before we use * it for the first time. */ #define PA_MEMPOOL_SLOTS_MAX 1024 -#define PA_MEMPOOL_SLOT_SIZE (64*1024) +#define PA_MEMPOOL_SLOT_SIZE (128*1024) #define PA_MEMEXPORT_SLOTS_MAX 128 -- cgit From 09d9096069360d1eecd30b11df7b4c7d2c39ac35 Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Tue, 21 Jul 2009 03:24:57 -0700 Subject: module-equalizer-sink: simplified sink_input pop callback and introduced new variables that simplify different strategies. --- src/modules/module-equalizer-sink.c | 341 +++++++++++++++++++++--------------- 1 file changed, 198 insertions(+), 143 deletions(-) (limited to 'src') diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c index 1d4a423d..970b20d0 100755 --- a/src/modules/module-equalizer-sink.c +++ b/src/modules/module-equalizer-sink.c @@ -65,6 +65,7 @@ PA_MODULE_USAGE(_("sink= ")); #define MEMBLOCKQ_MAXLENGTH (16*1024*1024) + struct userdata { pa_core *core; pa_module *module; @@ -107,6 +108,21 @@ static const char* const valid_modargs[] = { NULL }; +uint64_t time_diff(struct timespec *timeA_p, struct timespec *timeB_p); +void hanning_normalized_window(float *W,size_t window_size); +void hanning_window(float *W,size_t window_size); +void hamming_window(float *W,size_t window_size); +void blackman_window(float *W,size_t window_size); +void sin_window(float *W,size_t window_size); +void array_out(const char *name,float *a,size_t length); + +static void dsp_logic(float *dst,struct userdata *u); +static void process_samples(struct userdata *u); +void input_buffer(struct userdata *u,pa_memchunk *in); + +#define gettime(x) clock_gettime(CLOCK_MONOTONIC,&x) +#define tdiff(x,y) time_diff(&x,&y) + uint64_t time_diff(struct timespec *timeA_p, struct timespec *timeB_p) { return ((timeA_p->tv_sec * 1000000000) + timeA_p->tv_nsec) - @@ -188,13 +204,14 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse case PA_SINK_MESSAGE_GET_LATENCY: { pa_usec_t usec = 0; pa_sample_spec *ss=&u->sink->sample_spec; - size_t fs=pa_frame_size(ss); + size_t fs=pa_frame_size(&(u->sink->sample_spec)); /* Get the latency of the master sink */ if (PA_MSGOBJECT(u->master)->process_msg(PA_MSGOBJECT(u->master), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0) usec = 0; - usec+=pa_bytes_to_usec(u->samples_gathered*fs,ss); + usec+=pa_bytes_to_usec(u->R*fs,ss); + //usec+=pa_bytes_to_usec(u->samples_gathered*fs,ss); usec += pa_bytes_to_usec(pa_memblockq_get_length(u->rendered_q), ss); /* Add the latency internal to our sink input on top */ usec += pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->master->sample_spec); @@ -247,6 +264,90 @@ static void sink_update_requested_latency(pa_sink *s) { pa_sink_get_requested_latency_within_thread(s)); } +static void process_samples(struct userdata *u){ + pa_memchunk tchunk; + size_t fs=pa_frame_size(&(u->sink->sample_spec)); + while(u->samples_gathered>=u->R){ + float *dst; + //pa_log("iter gathered: %ld",u->samples_gathered); + //pa_memblockq_drop(u->rendered_q, tchunk.length); + tchunk.index=0; + tchunk.length=u->R*fs; + tchunk.memblock=pa_memblock_new(u->core->mempool,tchunk.length); + dst=((float*)pa_memblock_acquire(tchunk.memblock)); + dsp_logic(dst,u); + pa_memblock_release(tchunk.memblock); + pa_memblockq_push(u->rendered_q, &tchunk); + pa_memblock_unref(tchunk.memblock); + u->samples_gathered-=u->R; + } +} + +static void dsp_logic(float *dst,struct userdata *u){ + size_t fs=pa_frame_size(&(u->sink->sample_spec)); + //use a linear-phase sliding STFT and overlap-add method (for each channel) + for (size_t c=0;cchannels;c++) { + //zero padd the data + memset(u->work_buffer+u->window_size,0,(u->fft_size-u->window_size)*sizeof(float)); + //window the data + for(size_t j=0;jwindow_size;++j){ + u->work_buffer[j]=u->W[j]*u->input[c][j]; + } + //Processing is done here! + //do fft + fftwf_execute_dft_r2c(u->forward_plan,u->work_buffer,u->output_window); + //perform filtering + for(size_t j=0;jfft_size/2+1;++j){ + u->output_window[j][0]*=u->H[j]; + u->output_window[j][1]*=u->H[j]; + } + //inverse fft + fftwf_execute_dft_c2r(u->inverse_plan,u->output_window,u->work_buffer); + ////debug: tests overlaping add + ////and negates ALL PREVIOUS processing + ////yields a perfect reconstruction if COLA is held + //for(size_t j=0;jwindow_size;++j){ + // u->work_buffer[j]=u->W[j]*u->input[c][j]; + //} + + //overlap add and preserve overlap component from this window (linear phase) + for(size_t j=0;jR;++j){ + u->work_buffer[j]+=u->overlap_accum[c][j]; + u->overlap_accum[c][j]=u->work_buffer[u->overlap_size+j]; + } + + //debug: tests if basic buffering works + //shouldn't modify the signal AT ALL (beyond roundoff) + for(size_t j=0;jwindow_size;++j){ + u->work_buffer[j]=u->input[c][j]; + } + + //preseve the needed input for the next window's overlap + memmove(u->input[c],u->input[c]+u->R, + (u->samples_gathered+u->overlap_size-u->R)*sizeof(float) + ); + //output the samples that are outputable now + pa_sample_clamp(PA_SAMPLE_FLOAT32NE,dst+c,fs,u->work_buffer,sizeof(float),u->R); + } +} + +void input_buffer(struct userdata *u,pa_memchunk *in){ + size_t fs=pa_frame_size(&(u->sink->sample_spec)); + size_t samples=in->length/fs; + pa_assert_se(samples<=u->target_samples-u->samples_gathered); + float *src = (float*) ((uint8_t*) pa_memblock_acquire(in->memblock) + in->index); + for (size_t c=0;cchannels;c++) { + //buffer with an offset after the overlap from previous + //iterations + pa_assert_se( + u->input[c]+u->overlap_size+u->samples_gathered+samples<=u->input[c]+u->target_samples+u->overlap_size + ); + pa_sample_clamp(PA_SAMPLE_FLOAT32NE,u->input[c]+u->overlap_size+u->samples_gathered,sizeof(float),src+c,fs,samples); + } + u->samples_gathered+=samples; + pa_memblock_release(in->memblock); +} + /* Called from I/O thread context */ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) { struct userdata *u; @@ -255,153 +356,98 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk pa_assert_se(u = i->userdata); pa_assert_se(u->sink); size_t fs=pa_frame_size(&(u->sink->sample_spec)); - size_t ss=pa_sample_size(&(u->sink->sample_spec)); - size_t fe = fs/ss; size_t samples_requested=nbytes/fs; + size_t buffered_samples=pa_memblockq_get_length(u->rendered_q)/fs; pa_memchunk tchunk; chunk->memblock=NULL; - size_t buffered_samples=pa_memblockq_get_length(u->rendered_q)/fs; - if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state)) return -1; - pa_log("start output-buffered %ld, input-buffered %ld",buffered_samples,u->samples_gathered); - //collect samples - size_t buffered_remaining=pa_memblockq_get_length(u->rendered_q)/fs; - size_t buffer_missing=pa_memblockq_missing(u->rendered_q)/fs; - size_t desired_samples=(buffer_missing>=u->R)*PA_MIN(u->target_samples-u->samples_gathered,buffer_missing); - if(desired_samples>0){ - u->conv_buffer.index=0; - //if we still had buffered output, - //or can gather any more in the buffer - //politely request (optimistic) - if(buffered_samples>=samples_requested || - (u->samples_gathered/u->R)*u->R>=samples_requested){ - u->conv_buffer.length=desired_samples*fs; - pa_log("trying to buffer %ld samples",desired_samples); - pa_sink_render_into(u->sink, &u->conv_buffer); - }else{//we need it now! force it - //TODO: minimum amount or the whole buffer better? - desired_samples=u->R-u->samples_gathered%u->R; - u->conv_buffer.length=desired_samples*fs; - pa_log("force-buffer %ld samples",desired_samples); - pa_sink_render_into_full(u->sink, &u->conv_buffer); - pa_assert_se(u->conv_buffer.length==desired_samples*fs); - } - size_t n_samples=u->conv_buffer.length/fs; - float *src; - pa_log("received %ld samples",n_samples); - - pa_assert_se(n_samples<=u->target_samples-u->samples_gathered); - src = (float*) ((uint8_t*) pa_memblock_acquire(u->conv_buffer.memblock) + u->conv_buffer.index); - for (size_t c=0;cchannels;c++) { - //buffer with an offset after the overlap from previous - //iterations - pa_assert_se( - u->input[c]+u->overlap_size+u->samples_gathered+n_samples<=u->input[c]+u->target_samples+u->overlap_size - ); - pa_sample_clamp(PA_SAMPLE_FLOAT32NE,u->input[c]+u->overlap_size+u->samples_gathered,sizeof(float), src+c, fs, n_samples); - } - u->samples_gathered+=n_samples; - pa_memblock_release(u->conv_buffer.memblock); - } - //pa_assert_se(u->samples_gathered>=u->R); - pa_assert_se(u->fft_size>=u->window_size); - pa_assert_se(u->Rwindow_size); - //process every complete block on hand - while(u->samples_gathered>=u->R&&buffer_missing>=u->R){ - float *dst; - //pa_log("iter gathered: %ld",u->samples_gathered); - tchunk.index=0; - tchunk.length=u->R*fs; - tchunk.memblock=pa_memblock_new(u->core->mempool,tchunk.length); - //pa_memblockq_drop(u->rendered_q, tchunk.length); - pa_assert_se(tchunk.length==u->R*fs); - dst=(float*)pa_memblock_acquire(tchunk.memblock); - //use a linear-phase sliding STFT and overlap-add method (for each channel) - for (size_t c=0;cchannels;c++) { - //zero padd the data - memset(u->work_buffer+u->window_size,0,(u->fft_size-u->window_size)*sizeof(float)); - //window the data - for(size_t j=0;jwindow_size;++j){ - u->work_buffer[j]=u->W[j]*u->input[c][j]; - } - //Processing is done here! - //do fft - fftwf_execute_dft_r2c(u->forward_plan,u->work_buffer,u->output_window); - //perform filtering - for(size_t j=0;jfft_size/2+1;++j){ - u->output_window[j][0]*=u->H[j]; - u->output_window[j][1]*=u->H[j]; - } - //inverse fft - fftwf_execute_dft_c2r(u->inverse_plan,u->output_window,u->work_buffer); - ////debug: tests overlaping add - ////and negates ALL PREVIOUS processing - ////yields a perfect reconstruction if COLA is held - //for(size_t j=0;jwindow_size;++j){ - // u->work_buffer[j]=u->W[j]*u->input[c][j]; - //} - - //overlap add and preserve overlap component from this window (linear phase) - for(size_t j=0;jR;++j){ - u->work_buffer[j]+=u->overlap_accum[c][j]; - u->overlap_accum[c][j]=u->work_buffer[u->overlap_size+j]; - } - - //debug: tests if basic buffering works - //shouldn't modify the signal AT ALL (beyond roundoff) - for(size_t j=0;jwindow_size;++j){ - u->work_buffer[j]=u->input[c][j]; - } - - //preseve the needed input for the next window's overlap - memmove(u->input[c],u->input[c]+u->R, - (u->samples_gathered+u->overlap_size-u->R)*sizeof(float) - ); - //output the samples that are outputable now - pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst+c, fs, u->work_buffer, sizeof(float),u->R); - } - pa_memblock_release(tchunk.memblock); - pa_memblockq_push(u->rendered_q, &tchunk); - pa_memblock_unref(tchunk.memblock); - u->samples_gathered-=u->R; - buffer_missing-=u->R; - } - //deque from renderq and output - //pa_memblockq_set_prebuf(u->rendered_q,samples_requested*fs); - pa_assert_se(pa_memblockq_peek(u->rendered_q,&tchunk)>=0); - if(tchunk.length>=nbytes){ + pa_log("start output-buffered %ld, input-buffered %ld, requested %ld",buffered_samples,u->samples_gathered,samples_requested); + struct timespec start,end; + + if(pa_memblockq_peek(u->rendered_q,&tchunk)==0){ *chunk=tchunk; - chunk->length=samples_requested*fs; - pa_memblock_ref(chunk->memblock); - pa_memblock_unref(tchunk.memblock); pa_memblockq_drop(u->rendered_q, chunk->length); - }else{ - size_t copied=0; - chunk->length=nbytes; - chunk->memblock=pa_memblock_new(u->core->mempool,chunk->length); - uint8_t *dst=(uint8_t*)pa_memblock_acquire(chunk->memblock); - do{ - size_t l=PA_MIN(tchunk.length-tchunk.index,nbytes-copied); - uint8_t *src=(((uint8_t*)pa_memblock_acquire(tchunk.memblock))+tchunk.index); - memmove(dst+copied,src,l); - copied+=l; - pa_memblock_release(tchunk.memblock); - pa_memblock_unref(tchunk.memblock); - pa_memblockq_drop(u->rendered_q,l); - if(copiedrendered_q)==0){ - chunk->length=copied; - break; - } - pa_memblockq_peek(u->rendered_q,&tchunk); - } - }while(copiedmemblock); + return 0; } + do{ + pa_memchunk *buffer; + size_t input_remaining=u->target_samples-u->samples_gathered; + pa_assert(input_remaining>0); + //collect samples + + //buffer=&u->conv_buffer; + //buffer->length=input_remaining*fs; + //buffer->index=0; + //pa_memblock_ref(buffer); + //pa_sink_render_into(u->sink,buffer); + + if(u->sink->thread_info.rewind_requested) + sink_request_rewind(u->sink); + + pa_memchunk p; + buffer=&p; + pa_sink_render(u->sink,u->R*fs,buffer); + buffer->length=PA_MIN(input_remaining*fs,buffer->length); + + //debug block + //pa_memblockq_push(u->rendered_q,buffer); + //pa_memblock_unref(buffer->memblock); + //goto END; + + pa_log("asked for %ld input samples, got %ld samples",input_remaining,buffer->length/fs); + //copy new input + gettime(start); + input_buffer(u,buffer); + gettime(end); + pa_log("Took %0.5f seconds to setup",tdiff(end,start)*1e-9); + + pa_memblock_unref(buffer->memblock); + + pa_assert_se(u->fft_size>=u->window_size); + pa_assert_se(u->Rwindow_size); + //process every complete block on hand + + gettime(start); + process_samples(u); + gettime(end); + pa_log("Took %0.5f seconds to process",tdiff(end,start)*1e-9); + + buffered_samples=pa_memblockq_get_length(u->rendered_q)/fs; + }while(buffered_samplesR); + + //deque from rendered_q and output + pa_assert_se(pa_memblockq_peek(u->rendered_q,&tchunk)==0); + *chunk=tchunk; + pa_memblockq_drop(u->rendered_q, chunk->length); + //if(tchunk.length>=nbytes){ + //chunk->length=PA_MIN(tchunk.length,nbytes); + //}else{ + // size_t copied=0; + // chunk->index=0; + // chunk->length=PA_MIN(nbytes,pa_memblockq_get_length(u->rendered_q)); + // chunk->memblock=pa_memblock_new(u->core->mempool,chunk->length); + // uint8_t *dst=(uint8_t*)pa_memblock_acquire(chunk->memblock); + // for(;;){ + // size_t l=PA_MIN(tchunk.length,nbytes-copied); + // pa_assert_se(l>0); + // uint8_t *src=(((uint8_t*)pa_memblock_acquire(tchunk.memblock))+tchunk.index); + // memmove(dst+copied,src,l); + // copied+=l; + // pa_memblock_release(tchunk.memblock); + // pa_memblock_unref(tchunk.memblock); + // pa_memblockq_drop(u->rendered_q,l); + // if(copiedlength){ + // pa_assert_se(pa_memblockq_peek(u->rendered_q,&tchunk)==0); + // }else{ + // break; + // } + // } + // pa_memblock_release(chunk->memblock); + //} pa_assert_se(chunk->memblock); - pa_log("output requested %ld, gave %ld",nbytes/fs,chunk->length/fs); + pa_log("gave %ld",chunk->length/fs); //pa_log("end pop"); return 0; } @@ -411,6 +457,7 @@ static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) { struct userdata *u; size_t amount = 0; + pa_log_debug("Rewind callback!"); pa_sink_input_assert_ref(i); pa_assert_se(u = i->userdata); @@ -425,6 +472,7 @@ static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) { u->sink->thread_info.rewind_nbytes = 0; if (amount > 0) { + //pa_sample_spec *ss=&u->sink->sample_spec; pa_memblockq_seek(u->rendered_q, - (int64_t) amount, PA_SEEK_RELATIVE, TRUE); pa_log_debug("Resetting equalizer"); u->samples_gathered=0; @@ -459,7 +507,8 @@ static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes) { if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state)) return; - pa_sink_set_max_request_within_thread(u->sink, nbytes); + size_t fs=pa_frame_size(&(u->sink->sample_spec)); + pa_sink_set_max_request_within_thread(u->sink, u->R*fs); } /* Called from I/O thread context */ @@ -472,7 +521,9 @@ static void sink_input_update_sink_latency_range_cb(pa_sink_input *i) { if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state)) return; - pa_sink_set_latency_range_within_thread(u->sink, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency); + size_t fs=pa_frame_size(&(u->sink->sample_spec)); + pa_sink_set_latency_range_within_thread(u->sink,u->R*fs ,u->R*fs ); + //pa_sink_set_latency_range_within_thread(u->sink, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency); } /* Called from I/O thread context */ @@ -504,7 +555,9 @@ static void sink_input_attach_cb(pa_sink_input *i) { pa_sink_set_rtpoll(u->sink, i->sink->rtpoll); pa_sink_attach_within_thread(u->sink); - pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->master->thread_info.max_latency); + size_t fs=pa_frame_size(&(u->sink->sample_spec)); + pa_sink_set_latency_range_within_thread(u->sink, u->R*fs, u->R*fs); + //pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->master->thread_info.max_latency); } /* Called from main context */ @@ -598,7 +651,7 @@ int pa__init(pa_module*m) { u->window_size=7999; u->R=(u->window_size+1)/2; u->overlap_size=u->window_size-u->R; - u->target_samples=5*u->R; + u->target_samples=1*u->R; u->samples_gathered=0; u->max_output=pa_frame_align(pa_mempool_block_size_max(m->core->mempool), &ss)/pa_frame_size(&ss); u->rendered_q = pa_memblockq_new(0, MEMBLOCKQ_MAXLENGTH,u->target_samples*fs, fs, fs, 0, 0, NULL); @@ -716,6 +769,8 @@ int pa__init(pa_module*m) { pa_sink_set_asyncmsgq(u->sink, master->asyncmsgq); pa_sink_set_rtpoll(u->sink, master->rtpoll); + pa_sink_set_max_request(u->sink,u->R*fs); + //pa_sink_set_fixed_latency(u->sink,pa_bytes_to_usec(u->R*fs,&ss)); /* Create sink input */ pa_sink_input_new_data_init(&sink_input_data); -- cgit From 702480a8836eefb95cb41d7d4a05d4065d6560dc Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Thu, 23 Jul 2009 03:26:35 -0700 Subject: module-equalizer-sink: first commit of a working state (cpu speed dependant) added noop processing for filter debugability --- src/modules/module-equalizer-sink.c | 56 +++++++++++++++++++------------------ 1 file changed, 29 insertions(+), 27 deletions(-) (limited to 'src') diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c index 970b20d0..4d595e1c 100755 --- a/src/modules/module-equalizer-sink.c +++ b/src/modules/module-equalizer-sink.c @@ -82,6 +82,7 @@ struct userdata { * the latency of the filter, calculated from window_size * based on constraints of COLA and window function */ + size_t latency; size_t overlap_size;//window_size-R size_t samples_gathered; size_t max_output; @@ -210,9 +211,9 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse if (PA_MSGOBJECT(u->master)->process_msg(PA_MSGOBJECT(u->master), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0) usec = 0; - usec+=pa_bytes_to_usec(u->R*fs,ss); + usec+=pa_bytes_to_usec(u->latency*fs,ss); //usec+=pa_bytes_to_usec(u->samples_gathered*fs,ss); - usec += pa_bytes_to_usec(pa_memblockq_get_length(u->rendered_q), ss); + //usec += pa_bytes_to_usec(pa_memblockq_get_length(u->rendered_q), ss); /* Add the latency internal to our sink input on top */ usec += pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->master->sample_spec); *((pa_usec_t*) data) = usec; @@ -316,11 +317,11 @@ static void dsp_logic(float *dst,struct userdata *u){ u->overlap_accum[c][j]=u->work_buffer[u->overlap_size+j]; } - //debug: tests if basic buffering works - //shouldn't modify the signal AT ALL (beyond roundoff) - for(size_t j=0;jwindow_size;++j){ - u->work_buffer[j]=u->input[c][j]; - } + ////debug: tests if basic buffering works + ////shouldn't modify the signal AT ALL (beyond roundoff) + //for(size_t j=0;jwindow_size;++j){ + // u->work_buffer[j]=u->input[c][j]; + //} //preseve the needed input for the next window's overlap memmove(u->input[c],u->input[c]+u->R, @@ -363,7 +364,7 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state)) return -1; - pa_log("start output-buffered %ld, input-buffered %ld, requested %ld",buffered_samples,u->samples_gathered,samples_requested); + //pa_log("start output-buffered %ld, input-buffered %ld, requested %ld",buffered_samples,u->samples_gathered,samples_requested); struct timespec start,end; if(pa_memblockq_peek(u->rendered_q,&tchunk)==0){ @@ -377,31 +378,31 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk pa_assert(input_remaining>0); //collect samples - //buffer=&u->conv_buffer; - //buffer->length=input_remaining*fs; - //buffer->index=0; - //pa_memblock_ref(buffer); - //pa_sink_render_into(u->sink,buffer); + buffer=&u->conv_buffer; + buffer->length=input_remaining*fs; + buffer->index=0; + pa_memblock_ref(buffer->memblock); + pa_sink_render_into(u->sink,buffer); - if(u->sink->thread_info.rewind_requested) - sink_request_rewind(u->sink); + //if(u->sink->thread_info.rewind_requested) + // sink_request_rewind(u->sink); - pa_memchunk p; - buffer=&p; - pa_sink_render(u->sink,u->R*fs,buffer); - buffer->length=PA_MIN(input_remaining*fs,buffer->length); + //pa_memchunk p; + //buffer=&p; + //pa_sink_render(u->sink,u->R*fs,buffer); + //buffer->length=PA_MIN(input_remaining*fs,buffer->length); //debug block //pa_memblockq_push(u->rendered_q,buffer); //pa_memblock_unref(buffer->memblock); //goto END; - pa_log("asked for %ld input samples, got %ld samples",input_remaining,buffer->length/fs); + //pa_log("asked for %ld input samples, got %ld samples",input_remaining,buffer->length/fs); //copy new input gettime(start); input_buffer(u,buffer); gettime(end); - pa_log("Took %0.5f seconds to setup",tdiff(end,start)*1e-9); + //pa_log("Took %0.5f seconds to setup",tdiff(end,start)*1e-9); pa_memblock_unref(buffer->memblock); @@ -412,7 +413,7 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk gettime(start); process_samples(u); gettime(end); - pa_log("Took %0.5f seconds to process",tdiff(end,start)*1e-9); + //pa_log("Took %0.5f seconds to process",tdiff(end,start)*1e-9); buffered_samples=pa_memblockq_get_length(u->rendered_q)/fs; }while(buffered_samplesR); @@ -447,7 +448,7 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk // pa_memblock_release(chunk->memblock); //} pa_assert_se(chunk->memblock); - pa_log("gave %ld",chunk->length/fs); + //pa_log("gave %ld",chunk->length/fs); //pa_log("end pop"); return 0; } @@ -522,7 +523,7 @@ static void sink_input_update_sink_latency_range_cb(pa_sink_input *i) { return; size_t fs=pa_frame_size(&(u->sink->sample_spec)); - pa_sink_set_latency_range_within_thread(u->sink,u->R*fs ,u->R*fs ); + pa_sink_set_latency_range_within_thread(u->sink,u->latency*fs ,u->latency*fs ); //pa_sink_set_latency_range_within_thread(u->sink, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency); } @@ -556,7 +557,7 @@ static void sink_input_attach_cb(pa_sink_input *i) { pa_sink_attach_within_thread(u->sink); size_t fs=pa_frame_size(&(u->sink->sample_spec)); - pa_sink_set_latency_range_within_thread(u->sink, u->R*fs, u->R*fs); + pa_sink_set_latency_range_within_thread(u->sink, u->latency*fs, u->latency*fs); //pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->master->thread_info.max_latency); } @@ -656,6 +657,7 @@ int pa__init(pa_module*m) { u->max_output=pa_frame_align(pa_mempool_block_size_max(m->core->mempool), &ss)/pa_frame_size(&ss); u->rendered_q = pa_memblockq_new(0, MEMBLOCKQ_MAXLENGTH,u->target_samples*fs, fs, fs, 0, 0, NULL); u->conv_buffer.memblock=pa_memblock_new(u->core->mempool,u->target_samples*fs); + u->latency=u->R; u->H=(float*) fftwf_malloc((u->fft_size/2+1)*sizeof(float)); @@ -676,8 +678,8 @@ int pa__init(pa_module*m) { pa_assert_se(u->output_buffer[c]); } u->output_window = (fftwf_complex *) fftwf_malloc(sizeof(fftwf_complex) * (u->fft_size/2+1)); - u->forward_plan=fftwf_plan_dft_r2c_1d(u->fft_size, u->work_buffer, u->output_window, FFTW_ESTIMATE); - u->inverse_plan=fftwf_plan_dft_c2r_1d(u->fft_size, u->output_window, u->work_buffer, FFTW_ESTIMATE); + u->forward_plan=fftwf_plan_dft_r2c_1d(u->fft_size, u->work_buffer, u->output_window, FFTW_MEASURE); + u->inverse_plan=fftwf_plan_dft_c2r_1d(u->fft_size, u->output_window, u->work_buffer, FFTW_MEASURE); /* for(size_t j=0;jwindow_size;++j){ u->W[j]=.5; -- cgit From c7fcc9cc01c807c30b6c96f9995ef2c596c74146 Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Mon, 27 Jul 2009 01:22:26 -0700 Subject: module-equalizer-sink: removed liboil added sse2 optimized dsp logic implementation cleaned up a bit --- src/Makefile.am | 4 +- src/modules/module-equalizer-sink.c | 397 ++++++++++++++++++++++-------------- 2 files changed, 244 insertions(+), 157 deletions(-) (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index 10f9a793..281bdf14 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1383,9 +1383,9 @@ module_ladspa_sink_la_LDFLAGS = $(MODULE_LDFLAGS) module_ladspa_sink_la_LIBADD = $(AM_LIBADD) $(LIBLTDL) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la module_equalizer_sink_la_SOURCES = modules/module-equalizer-sink.c -module_equalizer_sink_la_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS) +module_equalizer_sink_la_CFLAGS = $(AM_CFLAGS) module_equalizer_sink_la_LDFLAGS = $(MODULE_LDFLAGS) -module_equalizer_sink_la_LIBADD = $(AM_LIBADD) $(LIBLTDL) $(LIBOIL_LIBS) -lfftw3f libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la +module_equalizer_sink_la_LIBADD = $(AM_LIBADD) $(LIBLTDL) -lfftw3f libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la module_match_la_SOURCES = modules/module-match.c module_match_la_LDFLAGS = $(MODULE_LDFLAGS) diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c index 4d595e1c..e20e07f0 100755 --- a/src/modules/module-equalizer-sink.c +++ b/src/modules/module-equalizer-sink.c @@ -29,11 +29,13 @@ USA. #include #endif +#include #include +#include #include #include -#include - +#include +#include #include #include @@ -55,6 +57,14 @@ USA. #include +//#undef __SSE2__ +#ifdef __SSE2__ +#include +#include +#endif + + + #include "module-equalizer-sink-symdef.h" PA_MODULE_AUTHOR("Jason Newton"); @@ -82,10 +92,12 @@ struct userdata { * the latency of the filter, calculated from window_size * based on constraints of COLA and window function */ - size_t latency; + size_t latency;//Really just R but made into it's own variable + //for twiddling with pulseaudio size_t overlap_size;//window_size-R size_t samples_gathered; - size_t max_output; + size_t max_output;//max amount of samples outputable in a single + //message size_t target_samples; float *H;//frequency response filter (magnitude based) float *W;//windowing function (time domain) @@ -109,76 +121,39 @@ static const char* const valid_modargs[] = { NULL }; -uint64_t time_diff(struct timespec *timeA_p, struct timespec *timeB_p); -void hanning_normalized_window(float *W,size_t window_size); -void hanning_window(float *W,size_t window_size); -void hamming_window(float *W,size_t window_size); -void blackman_window(float *W,size_t window_size); -void sin_window(float *W,size_t window_size); -void array_out(const char *name,float *a,size_t length); - -static void dsp_logic(float *dst,struct userdata *u); +static uint64_t time_diff(struct timespec *timeA_p, struct timespec *timeB_p); +static void hanning_window(float *W,size_t window_size); +static void array_out(const char *name,float *a,size_t length); static void process_samples(struct userdata *u); -void input_buffer(struct userdata *u,pa_memchunk *in); - +static void input_buffer(struct userdata *u,pa_memchunk *in); + +void dsp_logic( + float * __restrict__ dst, + float * __restrict__ src, + float * __restrict__ overlap, + const float * __restrict__ H, + const float * __restrict__ W, + fftwf_complex * __restrict__ output_window, + struct userdata *u); + +#define v_size 4 #define gettime(x) clock_gettime(CLOCK_MONOTONIC,&x) #define tdiff(x,y) time_diff(&x,&y) +#define mround(x,y) (x%y==0?x:(x/y+1)*y) uint64_t time_diff(struct timespec *timeA_p, struct timespec *timeB_p) { - return ((timeA_p->tv_sec * 1000000000) + timeA_p->tv_nsec) - - ((timeB_p->tv_sec * 1000000000) + timeB_p->tv_nsec); + return ((timeA_p->tv_sec * 1000000000ULL) + timeA_p->tv_nsec) - + ((timeB_p->tv_sec * 1000000000ULL) + timeB_p->tv_nsec); } -void hanning_normalized_window(float *W,size_t window_size){ - //h = sqrt(2)/2 * (1+cos(t*pi)) ./ sqrt( 1+cos(t*pi).^2 ) - float c; - for(size_t i=0;imaster)->process_msg(PA_MSGOBJECT(u->master), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0) usec = 0; - usec+=pa_bytes_to_usec(u->latency*fs,ss); + //usec+=pa_bytes_to_usec(u->latency*fs,ss); //usec+=pa_bytes_to_usec(u->samples_gathered*fs,ss); - //usec += pa_bytes_to_usec(pa_memblockq_get_length(u->rendered_q), ss); + usec += pa_bytes_to_usec(pa_memblockq_get_length(u->rendered_q), ss); /* Add the latency internal to our sink input on top */ usec += pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->master->sample_spec); *((pa_usec_t*) data) = usec; @@ -276,7 +251,18 @@ static void process_samples(struct userdata *u){ tchunk.length=u->R*fs; tchunk.memblock=pa_memblock_new(u->core->mempool,tchunk.length); dst=((float*)pa_memblock_acquire(tchunk.memblock)); - dsp_logic(dst,u); + for (size_t c=0;cchannels;c++) { + dsp_logic( + u->work_buffer, + u->input[c], + u->overlap_accum[c], + u->H, + u->W, + u->output_window, + u + ); + pa_sample_clamp(PA_SAMPLE_FLOAT32NE,dst+c,fs,u->work_buffer,sizeof(float),u->R); + } pa_memblock_release(tchunk.memblock); pa_memblockq_push(u->rendered_q, &tchunk); pa_memblock_unref(tchunk.memblock); @@ -284,54 +270,166 @@ static void process_samples(struct userdata *u){ } } -static void dsp_logic(float *dst,struct userdata *u){ - size_t fs=pa_frame_size(&(u->sink->sample_spec)); - //use a linear-phase sliding STFT and overlap-add method (for each channel) - for (size_t c=0;cchannels;c++) { - //zero padd the data - memset(u->work_buffer+u->window_size,0,(u->fft_size-u->window_size)*sizeof(float)); - //window the data - for(size_t j=0;jwindow_size;++j){ - u->work_buffer[j]=u->W[j]*u->input[c][j]; - } - //Processing is done here! - //do fft - fftwf_execute_dft_r2c(u->forward_plan,u->work_buffer,u->output_window); - //perform filtering - for(size_t j=0;jfft_size/2+1;++j){ - u->output_window[j][0]*=u->H[j]; - u->output_window[j][1]*=u->H[j]; - } - //inverse fft - fftwf_execute_dft_c2r(u->inverse_plan,u->output_window,u->work_buffer); - ////debug: tests overlaping add - ////and negates ALL PREVIOUS processing - ////yields a perfect reconstruction if COLA is held - //for(size_t j=0;jwindow_size;++j){ - // u->work_buffer[j]=u->W[j]*u->input[c][j]; - //} +typedef float v4sf __attribute__ ((__aligned__(v_size*sizeof(float)))); +typedef union float_vector { + float f[v_size]; + v4sf v; +#ifdef __SSE2__ + __m128 m; +#endif +} float_vector_t; + +////reference implementation +//void dsp_logic( +// float * __restrict__ dst,//used as a temp array too, needs to be fft_length! +// float * __restrict__ src,/*input data w/ overlap at start, +// *automatically cycled in routine +// */ +// float * __restrict__ overlap,//The size of the overlap +// const float * __restrict__ H,//The freq. magnitude scalers filter +// const float * __restrict__ W,//The windowing function +// fftwf_complex * __restrict__ output_window,//The transformed window'd src +// struct userdata *u){ +// //use a linear-phase sliding STFT and overlap-add method (for each channel) +// //zero padd the data +// memset(dst+u->window_size,0,(u->fft_size-u->window_size)*sizeof(float)); +// //window the data +// for(size_t j=0;jwindow_size;++j){ +// dst[j]=W[j]*src[j]; +// } +// //Processing is done here! +// //do fft +// fftwf_execute_dft_r2c(u->forward_plan,dst,output_window); +// //perform filtering +// for(size_t j=0;jfft_size/2+1;++j){ +// u->output_window[j][0]*=u->H[j]; +// u->output_window[j][1]*=u->H[j]; +// } +// //inverse fft +// fftwf_execute_dft_c2r(u->inverse_plan,output_window,dst); +// ////debug: tests overlaping add +// ////and negates ALL PREVIOUS processing +// ////yields a perfect reconstruction if COLA is held +// //for(size_t j=0;jwindow_size;++j){ +// // u->work_buffer[j]=u->W[j]*u->input[c][j]; +// //} +// +// //overlap add and preserve overlap component from this window (linear phase) +// for(size_t j=0;joverlap_size;++j){ +// u->work_buffer[j]+=overlap[j]; +// overlap[j]=dst[u->R+j]; +// } +// ////debug: tests if basic buffering works +// ////shouldn't modify the signal AT ALL (beyond roundoff) +// //for(size_t j=0;jwindow_size;++j){ +// // u->work_buffer[j]=u->input[c][j]; +// //} +// +// //preseve the needed input for the next window's overlap +// memmove(src,src+u->R, +// (u->samples_gathered+u->overlap_size-u->R)*sizeof(float) +// ); +//} + +//regardless of sse enabled, the loops in here assume +//16 byte aligned addresses and memory allocations divisible by v_size +void dsp_logic( + float * __restrict__ dst,//used as a temp array too, needs to be fft_length! + float * __restrict__ src,/*input data w/ overlap at start, + *automatically cycled in routine + */ + float * __restrict__ overlap,//The size of the overlap + const float * __restrict__ H,//The freq. magnitude scalers filter + const float * __restrict__ W,//The windowing function + fftwf_complex * __restrict__ output_window,//The transformed window'd src + struct userdata *u){//Collection of constants + + const size_t window_size=mround(u->window_size,v_size); + const size_t fft_h=mround(u->fft_size/2+1,v_size/2); + const size_t R=mround(u->R,v_size); + const size_t overlap_size=mround(u->overlap_size,v_size); + + //assert(u->samples_gathered>=u->R); + //zero out the bit beyond the real overlap so we don't add garbage + for(size_t j=overlap_size;j>u->overlap_size;--j){ + overlap[j-1]=0; + } + //use a linear-phase sliding STFT and overlap-add method + //zero padd the data + memset(dst+u->window_size,0,(u->fft_size-u->window_size)*sizeof(float)); + //window the data + for(size_t j=0;jm=_mm_mul_ps(w->m,s->m); +#else + d->v=w->v*s->v; +#endif + } + //Processing is done here! + //do fft + fftwf_execute_dft_r2c(u->forward_plan,dst,output_window); + + + //perform filtering - purely magnitude based + for(size_t j=0;jm=_mm_mul_ps(d->m,h.m); +#else + d->v=d->v*h->v; +#endif + } - //overlap add and preserve overlap component from this window (linear phase) - for(size_t j=0;jR;++j){ - u->work_buffer[j]+=u->overlap_accum[c][j]; - u->overlap_accum[c][j]=u->work_buffer[u->overlap_size+j]; - } - ////debug: tests if basic buffering works - ////shouldn't modify the signal AT ALL (beyond roundoff) - //for(size_t j=0;jwindow_size;++j){ - // u->work_buffer[j]=u->input[c][j]; - //} + //inverse fft + fftwf_execute_dft_c2r(u->inverse_plan,output_window,dst); - //preseve the needed input for the next window's overlap - memmove(u->input[c],u->input[c]+u->R, - (u->samples_gathered+u->overlap_size-u->R)*sizeof(float) - ); - //output the samples that are outputable now - pa_sample_clamp(PA_SAMPLE_FLOAT32NE,dst+c,fs,u->work_buffer,sizeof(float),u->R); + ////debug: tests overlaping add + ////and negates ALL PREVIOUS processing + ////yields a perfect reconstruction if COLA is held + //for(size_t j=0;jwindow_size;++j){ + // dst[j]=W[j]*src[j]; + //} + + //overlap add and preserve overlap component from this window (linear phase) + for(size_t j=0;jm=_mm_add_ps(d->m,o->m); + o->m=((float_vector_t*)(dst+u->R+j))->m; +#else + d->v=d->v+o->v; + o->v=((float_vector_t*)(dst+u->R+j))->v; +#endif } + //memcpy(overlap,dst+u->R,u->overlap_size*sizeof(float)); + + //////debug: tests if basic buffering works + //////shouldn't modify the signal AT ALL (beyond roundoff) + //for(size_t j=0;jwindow_size;++j){ + // dst[j]=src[j]; + //} + + //preseve the needed input for the next window's overlap + memmove(src,src+u->R, + (u->overlap_size+u->samples_gathered-u->R)*sizeof(float) + ); } + + void input_buffer(struct userdata *u,pa_memchunk *in){ size_t fs=pa_frame_size(&(u->sink->sample_spec)); size_t samples=in->length/fs; @@ -422,31 +520,6 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk pa_assert_se(pa_memblockq_peek(u->rendered_q,&tchunk)==0); *chunk=tchunk; pa_memblockq_drop(u->rendered_q, chunk->length); - //if(tchunk.length>=nbytes){ - //chunk->length=PA_MIN(tchunk.length,nbytes); - //}else{ - // size_t copied=0; - // chunk->index=0; - // chunk->length=PA_MIN(nbytes,pa_memblockq_get_length(u->rendered_q)); - // chunk->memblock=pa_memblock_new(u->core->mempool,chunk->length); - // uint8_t *dst=(uint8_t*)pa_memblock_acquire(chunk->memblock); - // for(;;){ - // size_t l=PA_MIN(tchunk.length,nbytes-copied); - // pa_assert_se(l>0); - // uint8_t *src=(((uint8_t*)pa_memblock_acquire(tchunk.memblock))+tchunk.index); - // memmove(dst+copied,src,l); - // copied+=l; - // pa_memblock_release(tchunk.memblock); - // pa_memblock_unref(tchunk.memblock); - // pa_memblockq_drop(u->rendered_q,l); - // if(copiedlength){ - // pa_assert_se(pa_memblockq_peek(u->rendered_q,&tchunk)==0); - // }else{ - // break; - // } - // } - // pa_memblock_release(chunk->memblock); - //} pa_assert_se(chunk->memblock); //pa_log("gave %ld",chunk->length/fs); //pa_log("end pop"); @@ -509,7 +582,8 @@ static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes) { return; size_t fs=pa_frame_size(&(u->sink->sample_spec)); - pa_sink_set_max_request_within_thread(u->sink, u->R*fs); + pa_sink_set_max_request_within_thread(u->sink, nbytes); + //pa_sink_set_max_request_within_thread(u->sink, u->R*fs); } /* Called from I/O thread context */ @@ -523,7 +597,8 @@ static void sink_input_update_sink_latency_range_cb(pa_sink_input *i) { return; size_t fs=pa_frame_size(&(u->sink->sample_spec)); - pa_sink_set_latency_range_within_thread(u->sink,u->latency*fs ,u->latency*fs ); + pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->latency*fs); + //pa_sink_set_latency_range_within_thread(u->sink,u->latency*fs ,u->latency*fs ); //pa_sink_set_latency_range_within_thread(u->sink, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency); } @@ -557,7 +632,12 @@ static void sink_input_attach_cb(pa_sink_input *i) { pa_sink_attach_within_thread(u->sink); size_t fs=pa_frame_size(&(u->sink->sample_spec)); - pa_sink_set_latency_range_within_thread(u->sink, u->latency*fs, u->latency*fs); + //pa_sink_set_latency_range_within_thread(u->sink, u->latency*fs, u->latency*fs); + //pa_sink_set_latency_range_within_thread(u->sink,u->latency*fs, u->master->thread_info.max_latency); + //TODO: setting this guy minimizes drop outs but doesn't get rid + //of them completely, figure out why + pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->latency*fs); + //TODO: this guy causes dropouts constantly+rewinds, it's unusable //pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->master->thread_info.max_latency); } @@ -605,6 +685,16 @@ static pa_bool_t sink_input_may_move_to_cb(pa_sink_input *i, pa_sink *dest) { return u->sink != dest; } + +//ensure's memory allocated is a multiple of v_size +//and aligned +static void * alloc(size_t x,size_t s){ + size_t f=mround(x*s,sizeof(float)*v_size); + //printf("requested %ld floats=%ld bytes, rem=%ld\n",x,x*sizeof(float),x*sizeof(float)%16); + //printf("giving %ld floats=%ld bytes, rem=%ld\n",f,f*sizeof(float),f*sizeof(float)%16); + return fftwf_malloc(f*s); +} + int pa__init(pa_module*m) { struct userdata *u; pa_sample_spec ss; @@ -649,7 +739,7 @@ int pa__init(pa_module*m) { u->channels=ss.channels; u->fft_size=pow(2,ceil(log(ss.rate)/log(2))); pa_log("fft size: %ld",u->fft_size); - u->window_size=7999; + u->window_size=15999; u->R=(u->window_size+1)/2; u->overlap_size=u->window_size-u->R; u->target_samples=1*u->R; @@ -659,32 +749,28 @@ int pa__init(pa_module*m) { u->conv_buffer.memblock=pa_memblock_new(u->core->mempool,u->target_samples*fs); u->latency=u->R; - - u->H=(float*) fftwf_malloc((u->fft_size/2+1)*sizeof(float)); - u->W=(float*) fftwf_malloc((u->window_size)*sizeof(float)); - u->work_buffer=(float*) fftwf_malloc(u->fft_size*sizeof(float)); + u->H=alloc((u->fft_size/2+1),sizeof(fftwf_complex)); + u->W=alloc(u->window_size,sizeof(float)); + u->work_buffer=alloc(u->fft_size,sizeof(float)); + memset(u->work_buffer,0,u->fft_size*sizeof(float)); u->input=(float **)malloc(sizeof(float *)*u->channels); u->overlap_accum=(float **)malloc(sizeof(float *)*u->channels); u->output_buffer=(float **)malloc(sizeof(float *)*u->channels); for(size_t c=0;cchannels;++c){ - u->input[c]=(float*) fftwf_malloc((u->target_samples+u->overlap_size)*sizeof(float)); + u->input[c]=alloc(u->target_samples+u->overlap_size,sizeof(float)); pa_assert_se(u->input[c]); memset(u->input[c],0,(u->target_samples+u->overlap_size)*sizeof(float)); pa_assert_se(u->input[c]); - u->overlap_accum[c]=(float*) fftwf_malloc(u->R*sizeof(float)); + u->overlap_accum[c]=alloc(u->overlap_size,sizeof(float)); pa_assert_se(u->overlap_accum[c]); - memset(u->overlap_accum[c],0,u->R*sizeof(float)); - u->output_buffer[c]=(float*) fftwf_malloc(u->window_size*sizeof(float)); + memset(u->overlap_accum[c],0,u->overlap_size*sizeof(float)); + u->output_buffer[c]=alloc(u->window_size,sizeof(float)); pa_assert_se(u->output_buffer[c]); } - u->output_window = (fftwf_complex *) fftwf_malloc(sizeof(fftwf_complex) * (u->fft_size/2+1)); + u->output_window=alloc((u->fft_size/2+1),sizeof(fftwf_complex)); u->forward_plan=fftwf_plan_dft_r2c_1d(u->fft_size, u->work_buffer, u->output_window, FFTW_MEASURE); u->inverse_plan=fftwf_plan_dft_c2r_1d(u->fft_size, u->output_window, u->work_buffer, FFTW_MEASURE); - /* - for(size_t j=0;jwindow_size;++j){ - u->W[j]=.5; - } - */ + hanning_window(u->W,u->window_size); const int freqs[]={0,25,50,100,200,300,400,800,1500, @@ -735,6 +821,7 @@ int pa__init(pa_module*m) { } free(freq_translated); + /* Create sink */ pa_sink_new_data_init(&sink_data); sink_data.driver = __FILE__; @@ -857,18 +944,18 @@ void pa__done(pa_module*m) { fftwf_destroy_plan(u->inverse_plan); fftwf_destroy_plan(u->forward_plan); - fftwf_free(u->output_window); + free(u->output_window); for(size_t c=0;cchannels;++c){ - fftwf_free(u->output_buffer[c]); - fftwf_free(u->overlap_accum[c]); - fftwf_free(u->input[c]); + free(u->output_buffer[c]); + free(u->overlap_accum[c]); + free(u->input[c]); } free(u->output_buffer); free(u->overlap_accum); free(u->input); - fftwf_free(u->work_buffer); - fftwf_free(u->W); - fftwf_free(u->H); + free(u->work_buffer); + free(u->W); + free(u->H); pa_xfree(u); } -- cgit From 8934c314f6401b953b871bbf5b6810b5fe05a9ac Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Fri, 31 Jul 2009 18:10:11 -0700 Subject: module-equalizer-sink: added dbus support removed cruft from inherited from ladspa module and improved clarity switched dsp processing to reference implementation until project is more mature tsched=0 seems to help with the micro-dropouts/crackling! oh my! reformatting/spaces --- src/Makefile.am | 2 +- src/modules/module-equalizer-sink.c | 747 ++++++++++++++++++++++-------------- 2 files changed, 461 insertions(+), 288 deletions(-) (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index 281bdf14..82bc2f9c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1385,7 +1385,7 @@ module_ladspa_sink_la_LIBADD = $(AM_LIBADD) $(LIBLTDL) libpulsecore-@PA_MAJORMIN module_equalizer_sink_la_SOURCES = modules/module-equalizer-sink.c module_equalizer_sink_la_CFLAGS = $(AM_CFLAGS) module_equalizer_sink_la_LDFLAGS = $(MODULE_LDFLAGS) -module_equalizer_sink_la_LIBADD = $(AM_LIBADD) $(LIBLTDL) -lfftw3f libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la +module_equalizer_sink_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) -lfftw3f libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la module_match_la_SOURCES = modules/module-match.c module_match_la_LDFLAGS = $(MODULE_LDFLAGS) diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c index e20e07f0..d8eb5f3d 100755 --- a/src/modules/module-equalizer-sink.c +++ b/src/modules/module-equalizer-sink.c @@ -35,7 +35,6 @@ USA. #include #include #include -#include #include #include @@ -52,6 +51,8 @@ USA. #include #include #include +#include +#include #include #include @@ -101,13 +102,18 @@ struct userdata { size_t target_samples; float *H;//frequency response filter (magnitude based) float *W;//windowing function (time domain) - float *work_buffer,**input,**overlap_accum,**output_buffer; + float *work_buffer, **input, **overlap_accum; fftwf_complex *output_window; - fftwf_plan forward_plan,inverse_plan; + fftwf_plan forward_plan, inverse_plan; //size_t samplings; + float *Hs[2];//thread updatable copies + pa_aupdate *a_H; pa_memchunk conv_buffer; pa_memblockq *rendered_q; + + pa_dbus_protocol *dbus_protocol; + char *dbus_path; }; static const char* const valid_modargs[] = { @@ -122,10 +128,10 @@ static const char* const valid_modargs[] = { }; static uint64_t time_diff(struct timespec *timeA_p, struct timespec *timeB_p); -static void hanning_window(float *W,size_t window_size); -static void array_out(const char *name,float *a,size_t length); +static void hanning_window(float *W, size_t window_size); +static void array_out(const char *name, float *a, size_t length); static void process_samples(struct userdata *u); -static void input_buffer(struct userdata *u,pa_memchunk *in); +static void input_buffer(struct userdata *u, pa_memchunk *in); void dsp_logic( float * __restrict__ dst, @@ -136,10 +142,17 @@ void dsp_logic( fftwf_complex * __restrict__ output_window, struct userdata *u); +static void dbus_init(struct userdata *u); +static void dbus_done(struct userdata *u); +static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *_u); +static void get_n_coefs(DBusConnection *conn, DBusMessage *msg, void *_u); +static void get_filter(DBusConnection *conn, DBusMessage *msg, void *_u); +static void set_filter(DBusConnection *conn, DBusMessage *msg, void *_u); + #define v_size 4 -#define gettime(x) clock_gettime(CLOCK_MONOTONIC,&x) -#define tdiff(x,y) time_diff(&x,&y) -#define mround(x,y) (x%y==0?x:(x/y+1)*y) +#define gettime(x) clock_gettime(CLOCK_MONOTONIC, &x) +#define tdiff(x, y) time_diff(&x, &y) +#define mround(x, y) (x % y == 0 ? x : ( x / y + 1) * y) uint64_t time_diff(struct timespec *timeA_p, struct timespec *timeB_p) { @@ -147,26 +160,33 @@ uint64_t time_diff(struct timespec *timeA_p, struct timespec *timeB_p) ((timeB_p->tv_sec * 1000000000ULL) + timeB_p->tv_nsec); } -void hanning_window(float *W,size_t window_size){ +static void hanning_window(float *W, size_t window_size){ //h=.5*(1-cos(2*pi*j/(window_size+1)), COLA for R=(M+1)/2 - for(size_t i=0;isink->sample_spec; - size_t fs=pa_frame_size(&(u->sink->sample_spec)); + //size_t fs=pa_frame_size(&(u->sink->sample_spec)); /* Get the latency of the master sink */ if (PA_MSGOBJECT(u->master)->process_msg(PA_MSGOBJECT(u->master), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0) usec = 0; - //usec+=pa_bytes_to_usec(u->latency*fs,ss); - //usec+=pa_bytes_to_usec(u->samples_gathered*fs,ss); + //usec+=pa_bytes_to_usec(u->latency * fs, ss); + //usec+=pa_bytes_to_usec(u->samples_gathered * fs, ss); usec += pa_bytes_to_usec(pa_memblockq_get_length(u->rendered_q), ss); /* Add the latency internal to our sink input on top */ usec += pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->master->sample_spec); @@ -243,15 +263,15 @@ static void sink_update_requested_latency(pa_sink *s) { static void process_samples(struct userdata *u){ pa_memchunk tchunk; size_t fs=pa_frame_size(&(u->sink->sample_spec)); - while(u->samples_gathered>=u->R){ + while(u->samples_gathered >= u->R){ float *dst; - //pa_log("iter gathered: %ld",u->samples_gathered); + //pa_log("iter gathered: %ld", u->samples_gathered); //pa_memblockq_drop(u->rendered_q, tchunk.length); tchunk.index=0; tchunk.length=u->R*fs; - tchunk.memblock=pa_memblock_new(u->core->mempool,tchunk.length); + tchunk.memblock=pa_memblock_new(u->core->mempool, tchunk.length); dst=((float*)pa_memblock_acquire(tchunk.memblock)); - for (size_t c=0;cchannels;c++) { + for(size_t c=0;c < u->channels; c++) { dsp_logic( u->work_buffer, u->input[c], @@ -261,7 +281,7 @@ static void process_samples(struct userdata *u){ u->output_window, u ); - pa_sample_clamp(PA_SAMPLE_FLOAT32NE,dst+c,fs,u->work_buffer,sizeof(float),u->R); + pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst + c, fs, u->work_buffer, sizeof(float), u->R); } pa_memblock_release(tchunk.memblock); pa_memblockq_push(u->rendered_q, &tchunk); @@ -279,60 +299,7 @@ typedef union float_vector { #endif } float_vector_t; -////reference implementation -//void dsp_logic( -// float * __restrict__ dst,//used as a temp array too, needs to be fft_length! -// float * __restrict__ src,/*input data w/ overlap at start, -// *automatically cycled in routine -// */ -// float * __restrict__ overlap,//The size of the overlap -// const float * __restrict__ H,//The freq. magnitude scalers filter -// const float * __restrict__ W,//The windowing function -// fftwf_complex * __restrict__ output_window,//The transformed window'd src -// struct userdata *u){ -// //use a linear-phase sliding STFT and overlap-add method (for each channel) -// //zero padd the data -// memset(dst+u->window_size,0,(u->fft_size-u->window_size)*sizeof(float)); -// //window the data -// for(size_t j=0;jwindow_size;++j){ -// dst[j]=W[j]*src[j]; -// } -// //Processing is done here! -// //do fft -// fftwf_execute_dft_r2c(u->forward_plan,dst,output_window); -// //perform filtering -// for(size_t j=0;jfft_size/2+1;++j){ -// u->output_window[j][0]*=u->H[j]; -// u->output_window[j][1]*=u->H[j]; -// } -// //inverse fft -// fftwf_execute_dft_c2r(u->inverse_plan,output_window,dst); -// ////debug: tests overlaping add -// ////and negates ALL PREVIOUS processing -// ////yields a perfect reconstruction if COLA is held -// //for(size_t j=0;jwindow_size;++j){ -// // u->work_buffer[j]=u->W[j]*u->input[c][j]; -// //} -// -// //overlap add and preserve overlap component from this window (linear phase) -// for(size_t j=0;joverlap_size;++j){ -// u->work_buffer[j]+=overlap[j]; -// overlap[j]=dst[u->R+j]; -// } -// ////debug: tests if basic buffering works -// ////shouldn't modify the signal AT ALL (beyond roundoff) -// //for(size_t j=0;jwindow_size;++j){ -// // u->work_buffer[j]=u->input[c][j]; -// //} -// -// //preseve the needed input for the next window's overlap -// memmove(src,src+u->R, -// (u->samples_gathered+u->overlap_size-u->R)*sizeof(float) -// ); -//} - -//regardless of sse enabled, the loops in here assume -//16 byte aligned addresses and memory allocations divisible by v_size +//reference implementation void dsp_logic( float * __restrict__ dst,//used as a temp array too, needs to be fft_length! float * __restrict__ src,/*input data w/ overlap at start, @@ -342,106 +309,159 @@ void dsp_logic( const float * __restrict__ H,//The freq. magnitude scalers filter const float * __restrict__ W,//The windowing function fftwf_complex * __restrict__ output_window,//The transformed window'd src - struct userdata *u){//Collection of constants - - const size_t window_size=mround(u->window_size,v_size); - const size_t fft_h=mround(u->fft_size/2+1,v_size/2); - const size_t R=mround(u->R,v_size); - const size_t overlap_size=mround(u->overlap_size,v_size); - - //assert(u->samples_gathered>=u->R); - //zero out the bit beyond the real overlap so we don't add garbage - for(size_t j=overlap_size;j>u->overlap_size;--j){ - overlap[j-1]=0; - } - //use a linear-phase sliding STFT and overlap-add method + struct userdata *u){ + //use a linear-phase sliding STFT and overlap-add method (for each channel) //zero padd the data - memset(dst+u->window_size,0,(u->fft_size-u->window_size)*sizeof(float)); + memset(dst + u->window_size, 0, (u->fft_size - u->window_size) * sizeof(float)); //window the data - for(size_t j=0;jm=_mm_mul_ps(w->m,s->m); -#else - d->v=w->v*s->v; -#endif + for(size_t j = 0;j < u->window_size; ++j){ + dst[j] = W[j] * src[j]; } //Processing is done here! //do fft - fftwf_execute_dft_r2c(u->forward_plan,dst,output_window); - - - //perform filtering - purely magnitude based - for(size_t j=0;jm=_mm_mul_ps(d->m,h.m); -#else - d->v=d->v*h->v; -#endif + fftwf_execute_dft_r2c(u->forward_plan, dst, output_window); + //perform filtering + for(size_t j = 0;j < u->fft_size / 2 + 1; ++j){ + u->output_window[j][0] *= u->H[j]; + u->output_window[j][1] *= u->H[j]; } - - //inverse fft - fftwf_execute_dft_c2r(u->inverse_plan,output_window,dst); - + fftwf_execute_dft_c2r(u->inverse_plan, output_window, dst); ////debug: tests overlaping add ////and negates ALL PREVIOUS processing ////yields a perfect reconstruction if COLA is held - //for(size_t j=0;jwindow_size;++j){ - // dst[j]=W[j]*src[j]; + //for(size_t j = 0; j < u->window_size; ++j){ + // u->work_buffer[j] = u->W[j] * u->input[c][j]; //} //overlap add and preserve overlap component from this window (linear phase) - for(size_t j=0;jm=_mm_add_ps(d->m,o->m); - o->m=((float_vector_t*)(dst+u->R+j))->m; -#else - d->v=d->v+o->v; - o->v=((float_vector_t*)(dst+u->R+j))->v; -#endif + for(size_t j = 0;j < u->overlap_size; ++j){ + u->work_buffer[j] += overlap[j]; + overlap[j] = dst[u->R+j]; } - //memcpy(overlap,dst+u->R,u->overlap_size*sizeof(float)); - - //////debug: tests if basic buffering works - //////shouldn't modify the signal AT ALL (beyond roundoff) - //for(size_t j=0;jwindow_size;++j){ - // dst[j]=src[j]; + ////debug: tests if basic buffering works + ////shouldn't modify the signal AT ALL (beyond roundoff) + //for(size_t j = 0; j < u->window_size;++j){ + // u->work_buffer[j] = u->input[c][j]; //} //preseve the needed input for the next window's overlap - memmove(src,src+u->R, - (u->overlap_size+u->samples_gathered-u->R)*sizeof(float) + memmove(src, src+u->R, + ((u->overlap_size + u->samples_gathered) - u->R)*sizeof(float) ); } +////regardless of sse enabled, the loops in here assume +////16 byte aligned addresses and memory allocations divisible by v_size +//void dsp_logic( +// float * __restrict__ dst,//used as a temp array too, needs to be fft_length! +// float * __restrict__ src,/*input data w/ overlap at start, +// *automatically cycled in routine +// */ +// float * __restrict__ overlap,//The size of the overlap +// const float * __restrict__ H,//The freq. magnitude scalers filter +// const float * __restrict__ W,//The windowing function +// fftwf_complex * __restrict__ output_window,//The transformed window'd src +// struct userdata *u){//Collection of constants +// +// const size_t window_size = mround(u->window_size,v_size); +// const size_t fft_h = mround(u->fft_size / 2 + 1, v_size / 2); +// //const size_t R = mround(u->R, v_size); +// const size_t overlap_size = mround(u->overlap_size, v_size); +// +// //assert(u->samples_gathered >= u->R); +// //zero out the bit beyond the real overlap so we don't add garbage +// for(size_t j = overlap_size; j > u->overlap_size; --j){ +// overlap[j-1] = 0; +// } +// //use a linear-phase sliding STFT and overlap-add method +// //zero padd the data +// memset(dst + u->window_size, 0, (u->fft_size - u->window_size)*sizeof(float)); +// //window the data +// for(size_t j = 0; j < window_size; j += v_size){ +// //dst[j] = W[j]*src[j]; +// float_vector_t *d = (float_vector_t*) (dst+j); +// float_vector_t *w = (float_vector_t*) (W+j); +// float_vector_t *s = (float_vector_t*) (src+j); +//#if __SSE2__ +// d->m = _mm_mul_ps(w->m, s->m); +//#else +// d->v = w->v * s->v; +//#endif +// } +// //Processing is done here! +// //do fft +// fftwf_execute_dft_r2c(u->forward_plan, dst, output_window); +// +// +// //perform filtering - purely magnitude based +// for(size_t j = 0;j < fft_h; j+=v_size/2){ +// //output_window[j][0]*=H[j]; +// //output_window[j][1]*=H[j]; +// float_vector_t *d = (float_vector_t*)(output_window+j); +// float_vector_t h; +// h.f[0] = h.f[1] = H[j]; +// h.f[2] = h.f[3] = H[j+1]; +//#if __SSE2__ +// d->m = _mm_mul_ps(d->m, h.m); +//#else +// d->v = d->v*h->v; +//#endif +// } +// +// +// //inverse fft +// fftwf_execute_dft_c2r(u->inverse_plan, output_window, dst); +// +// ////debug: tests overlaping add +// ////and negates ALL PREVIOUS processing +// ////yields a perfect reconstruction if COLA is held +// //for(size_t j = 0; j < u->window_size; ++j){ +// // dst[j] = W[j]*src[j]; +// //} +// +// //overlap add and preserve overlap component from this window (linear phase) +// for(size_t j = 0; j < overlap_size; j+=v_size){ +// //dst[j]+=overlap[j]; +// //overlap[j]+=dst[j+R]; +// float_vector_t *d = (float_vector_t*)(dst+j); +// float_vector_t *o = (float_vector_t*)(overlap+j); +//#if __SSE2__ +// d->m = _mm_add_ps(d->m, o->m); +// o->m = ((float_vector_t*)(dst+u->R+j))->m; +//#else +// d->v = d->v+o->v; +// o->v = ((float_vector_t*)(dst+u->R+j))->v; +//#endif +// } +// //memcpy(overlap, dst+u->R, u->overlap_size*sizeof(float)); +// +// //////debug: tests if basic buffering works +// //////shouldn't modify the signal AT ALL (beyond roundoff) +// //for(size_t j = 0; j < u->window_size; ++j){ +// // dst[j] = src[j]; +// //} +// +// //preseve the needed input for the next window's overlap +// memmove(src, src+u->R, +// ((u->overlap_size+u->samples_gathered)+-u->R)*sizeof(float) +// ); +//} -void input_buffer(struct userdata *u,pa_memchunk *in){ - size_t fs=pa_frame_size(&(u->sink->sample_spec)); - size_t samples=in->length/fs; - pa_assert_se(samples<=u->target_samples-u->samples_gathered); + +void input_buffer(struct userdata *u, pa_memchunk *in){ + size_t fs = pa_frame_size(&(u->sink->sample_spec)); + size_t samples = in->length/fs; + pa_assert_se(samples <= u->target_samples-u->samples_gathered); float *src = (float*) ((uint8_t*) pa_memblock_acquire(in->memblock) + in->index); - for (size_t c=0;cchannels;c++) { + for(size_t c = 0; c < u->channels; c++) { //buffer with an offset after the overlap from previous //iterations pa_assert_se( - u->input[c]+u->overlap_size+u->samples_gathered+samples<=u->input[c]+u->target_samples+u->overlap_size + u->input[c]+u->overlap_size+u->samples_gathered+samples <= u->input[c]+u->overlap_size+u->target_samples ); - pa_sample_clamp(PA_SAMPLE_FLOAT32NE,u->input[c]+u->overlap_size+u->samples_gathered,sizeof(float),src+c,fs,samples); + pa_sample_clamp(PA_SAMPLE_FLOAT32NE, u->input[c]+u->overlap_size+u->samples_gathered, sizeof(float), src + c, fs, samples); } u->samples_gathered+=samples; pa_memblock_release(in->memblock); @@ -454,74 +474,81 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk pa_assert(chunk); pa_assert_se(u = i->userdata); pa_assert_se(u->sink); - size_t fs=pa_frame_size(&(u->sink->sample_spec)); - size_t samples_requested=nbytes/fs; - size_t buffered_samples=pa_memblockq_get_length(u->rendered_q)/fs; + size_t fs = pa_frame_size(&(u->sink->sample_spec)); + //size_t samples_requested = nbytes/fs; + size_t buffered_samples = pa_memblockq_get_length(u->rendered_q)/fs; pa_memchunk tchunk; - chunk->memblock=NULL; + chunk->memblock = NULL; if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state)) return -1; //pa_log("start output-buffered %ld, input-buffered %ld, requested %ld",buffered_samples,u->samples_gathered,samples_requested); - struct timespec start,end; + struct timespec start, end; - if(pa_memblockq_peek(u->rendered_q,&tchunk)==0){ - *chunk=tchunk; + if(pa_memblockq_peek(u->rendered_q, &tchunk)==0){ + *chunk = tchunk; pa_memblockq_drop(u->rendered_q, chunk->length); return 0; } + + /* + Set the H filter + */ + unsigned H_i = pa_aupdate_read_begin(u->a_H); + u->H = u->Hs[H_i]; + do{ pa_memchunk *buffer; - size_t input_remaining=u->target_samples-u->samples_gathered; + size_t input_remaining = u->target_samples-u->samples_gathered; pa_assert(input_remaining>0); //collect samples - buffer=&u->conv_buffer; - buffer->length=input_remaining*fs; - buffer->index=0; + buffer = &u->conv_buffer; + buffer->length = input_remaining*fs; + buffer->index = 0; pa_memblock_ref(buffer->memblock); - pa_sink_render_into(u->sink,buffer); + pa_sink_render_into(u->sink, buffer); //if(u->sink->thread_info.rewind_requested) // sink_request_rewind(u->sink); //pa_memchunk p; - //buffer=&p; - //pa_sink_render(u->sink,u->R*fs,buffer); - //buffer->length=PA_MIN(input_remaining*fs,buffer->length); + //buffer = &p; + //pa_sink_render(u->sink, u->R*fs, buffer); + //buffer->length = PA_MIN(input_remaining*fs, buffer->length); //debug block - //pa_memblockq_push(u->rendered_q,buffer); + //pa_memblockq_push(u->rendered_q, buffer); //pa_memblock_unref(buffer->memblock); //goto END; //pa_log("asked for %ld input samples, got %ld samples",input_remaining,buffer->length/fs); //copy new input gettime(start); - input_buffer(u,buffer); + input_buffer(u, buffer); gettime(end); - //pa_log("Took %0.5f seconds to setup",tdiff(end,start)*1e-9); + //pa_log("Took %0.5f seconds to setup", tdiff(end, start)*1e-9); pa_memblock_unref(buffer->memblock); - pa_assert_se(u->fft_size>=u->window_size); - pa_assert_se(u->Rwindow_size); + pa_assert_se(u->fft_size >= u->window_size); + pa_assert_se(u->R < u->window_size); //process every complete block on hand gettime(start); process_samples(u); gettime(end); - //pa_log("Took %0.5f seconds to process",tdiff(end,start)*1e-9); + //pa_log("Took %0.5f seconds to process", tdiff(end, start)*1e-9); - buffered_samples=pa_memblockq_get_length(u->rendered_q)/fs; - }while(buffered_samplesR); + buffered_samples = pa_memblockq_get_length(u->rendered_q)/fs; + }while(buffered_samples < u->R); //deque from rendered_q and output - pa_assert_se(pa_memblockq_peek(u->rendered_q,&tchunk)==0); - *chunk=tchunk; + pa_assert_se(pa_memblockq_peek(u->rendered_q, &tchunk)==0); + *chunk = tchunk; pa_memblockq_drop(u->rendered_q, chunk->length); pa_assert_se(chunk->memblock); - //pa_log("gave %ld",chunk->length/fs); + //pa_log("gave %ld", chunk->length/fs); //pa_log("end pop"); return 0; } @@ -546,10 +573,10 @@ static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) { u->sink->thread_info.rewind_nbytes = 0; if (amount > 0) { - //pa_sample_spec *ss=&u->sink->sample_spec; + //pa_sample_spec *ss = &u->sink->sample_spec; pa_memblockq_seek(u->rendered_q, - (int64_t) amount, PA_SEEK_RELATIVE, TRUE); pa_log_debug("Resetting equalizer"); - u->samples_gathered=0; + u->samples_gathered = 0; } } @@ -581,9 +608,9 @@ static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes) { if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state)) return; - size_t fs=pa_frame_size(&(u->sink->sample_spec)); - pa_sink_set_max_request_within_thread(u->sink, nbytes); - //pa_sink_set_max_request_within_thread(u->sink, u->R*fs); + size_t fs = pa_frame_size(&(u->sink->sample_spec)); + //pa_sink_set_max_request_within_thread(u->sink, nbytes); + pa_sink_set_max_request_within_thread(u->sink, u->R*fs); } /* Called from I/O thread context */ @@ -596,9 +623,9 @@ static void sink_input_update_sink_latency_range_cb(pa_sink_input *i) { if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state)) return; - size_t fs=pa_frame_size(&(u->sink->sample_spec)); - pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->latency*fs); - //pa_sink_set_latency_range_within_thread(u->sink,u->latency*fs ,u->latency*fs ); + size_t fs = pa_frame_size(&(u->sink->sample_spec)); + //pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->latency*fs); + pa_sink_set_latency_range_within_thread(u->sink, u->latency*fs, u->latency*fs ); //pa_sink_set_latency_range_within_thread(u->sink, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency); } @@ -631,9 +658,9 @@ static void sink_input_attach_cb(pa_sink_input *i) { pa_sink_set_rtpoll(u->sink, i->sink->rtpoll); pa_sink_attach_within_thread(u->sink); - size_t fs=pa_frame_size(&(u->sink->sample_spec)); + size_t fs = pa_frame_size(&(u->sink->sample_spec)); //pa_sink_set_latency_range_within_thread(u->sink, u->latency*fs, u->latency*fs); - //pa_sink_set_latency_range_within_thread(u->sink,u->latency*fs, u->master->thread_info.max_latency); + //pa_sink_set_latency_range_within_thread(u->sink, u->latency*fs, u->master->thread_info.max_latency); //TODO: setting this guy minimizes drop outs but doesn't get rid //of them completely, figure out why pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->latency*fs); @@ -689,10 +716,13 @@ static pa_bool_t sink_input_may_move_to_cb(pa_sink_input *i, pa_sink *dest) { //ensure's memory allocated is a multiple of v_size //and aligned static void * alloc(size_t x,size_t s){ - size_t f=mround(x*s,sizeof(float)*v_size); - //printf("requested %ld floats=%ld bytes, rem=%ld\n",x,x*sizeof(float),x*sizeof(float)%16); - //printf("giving %ld floats=%ld bytes, rem=%ld\n",f,f*sizeof(float),f*sizeof(float)%16); - return fftwf_malloc(f*s); + size_t f = mround(x*s, sizeof(float)*v_size); + pa_assert_se(f >= x*s); + //printf("requested %ld floats=%ld bytes, rem=%ld\n", x, x*sizeof(float), x*sizeof(float)%16); + //printf("giving %ld floats=%ld bytes, rem=%ld\n", f, f*sizeof(float), f*sizeof(float)%16); + float *t = fftwf_malloc(f); + memset(t, 0, f); + return t; } int pa__init(pa_module*m) { @@ -726,7 +756,7 @@ int pa__init(pa_module*m) { pa_log("Invalid sample format specification or channel map"); goto fail; } - fs=pa_frame_size(&ss); + fs = pa_frame_size(&ss); u = pa_xnew0(struct userdata, 1); u->core = m->core; @@ -736,90 +766,96 @@ int pa__init(pa_module*m) { u->sink = NULL; u->sink_input = NULL; - u->channels=ss.channels; - u->fft_size=pow(2,ceil(log(ss.rate)/log(2))); - pa_log("fft size: %ld",u->fft_size); - u->window_size=15999; - u->R=(u->window_size+1)/2; - u->overlap_size=u->window_size-u->R; - u->target_samples=1*u->R; - u->samples_gathered=0; - u->max_output=pa_frame_align(pa_mempool_block_size_max(m->core->mempool), &ss)/pa_frame_size(&ss); - u->rendered_q = pa_memblockq_new(0, MEMBLOCKQ_MAXLENGTH,u->target_samples*fs, fs, fs, 0, 0, NULL); - u->conv_buffer.memblock=pa_memblock_new(u->core->mempool,u->target_samples*fs); - u->latency=u->R; - - u->H=alloc((u->fft_size/2+1),sizeof(fftwf_complex)); - u->W=alloc(u->window_size,sizeof(float)); - u->work_buffer=alloc(u->fft_size,sizeof(float)); - memset(u->work_buffer,0,u->fft_size*sizeof(float)); - u->input=(float **)malloc(sizeof(float *)*u->channels); - u->overlap_accum=(float **)malloc(sizeof(float *)*u->channels); - u->output_buffer=(float **)malloc(sizeof(float *)*u->channels); - for(size_t c=0;cchannels;++c){ - u->input[c]=alloc(u->target_samples+u->overlap_size,sizeof(float)); + u->channels = ss.channels; + u->fft_size = pow(2, ceil(log(ss.rate)/log(2))); + pa_log("fft size: %ld", u->fft_size); + u->window_size = 7999; + u->R = (u->window_size+1)/2; + u->overlap_size = u->window_size-u->R; + u->target_samples = 1*u->R; + u->samples_gathered = 0; + u->max_output = pa_frame_align(pa_mempool_block_size_max(m->core->mempool), &ss)/pa_frame_size(&ss); + u->rendered_q = pa_memblockq_new(0, MEMBLOCKQ_MAXLENGTH, u->target_samples*fs, fs, fs, 0, 0, NULL); + u->a_H = pa_aupdate_new(); + u->conv_buffer.memblock = pa_memblock_new(u->core->mempool, u->target_samples*fs); + u->latency = u->R; + for(size_t i = 0; i < 2; ++i){ + u->Hs[i] = alloc((u->fft_size / 2 + 1), sizeof(float)); + } + u->W = alloc(u->window_size, sizeof(float)); + u->work_buffer = alloc(u->fft_size, sizeof(float)); + memset(u->work_buffer, 0, u->fft_size*sizeof(float)); + u->input = (float **)pa_xmalloc0(sizeof(float *)*u->channels); + u->overlap_accum = (float **)pa_xmalloc0(sizeof(float *)*u->channels); + for(size_t c = 0; c < u->channels; ++c){ + u->input[c] = alloc(u->overlap_size+u->target_samples, sizeof(float)); pa_assert_se(u->input[c]); - memset(u->input[c],0,(u->target_samples+u->overlap_size)*sizeof(float)); + memset(u->input[c], 0, (u->overlap_size+u->target_samples)*sizeof(float)); pa_assert_se(u->input[c]); - u->overlap_accum[c]=alloc(u->overlap_size,sizeof(float)); + u->overlap_accum[c] = alloc(u->overlap_size, sizeof(float)); pa_assert_se(u->overlap_accum[c]); - memset(u->overlap_accum[c],0,u->overlap_size*sizeof(float)); - u->output_buffer[c]=alloc(u->window_size,sizeof(float)); - pa_assert_se(u->output_buffer[c]); - } - u->output_window=alloc((u->fft_size/2+1),sizeof(fftwf_complex)); - u->forward_plan=fftwf_plan_dft_r2c_1d(u->fft_size, u->work_buffer, u->output_window, FFTW_MEASURE); - u->inverse_plan=fftwf_plan_dft_c2r_1d(u->fft_size, u->output_window, u->work_buffer, FFTW_MEASURE); - - hanning_window(u->W,u->window_size); - - const int freqs[]={0,25,50,100,200,300,400,800,1500, - 2000,3000,4000,5000,6000,7000,8000,9000,10000,11000,12000, - 13000,14000,15000,16000,17000,18000,19000,20000,21000,22000,23000,24000,INT_MAX}; - const float coefficients[]={1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}; - const size_t ncoefficients=sizeof(coefficients)/sizeof(float); - pa_assert_se(sizeof(freqs)/sizeof(int)==sizeof(coefficients)/sizeof(float)); - float *freq_translated=(float *) malloc(sizeof(float)*(ncoefficients)); - freq_translated[0]=1; - //Translate the frequencies in their natural sampling rate to the new sampling rate frequencies - for(size_t i=1;ifft_size)/ss.rate; - //pa_log("i: %ld: %d , %g",i,freqs[i],freq_translated[i]); - pa_assert_se(freq_translated[i]>=freq_translated[i-1]); + memset(u->overlap_accum[c], 0, u->overlap_size*sizeof(float)); } - freq_translated[ncoefficients-1]=FLT_MAX; - //Interpolate the specified frequency band values - u->H[0]=1; - for(size_t i=1,j=0;i<(u->fft_size/2+1);++i){ - pa_assert_se(j=FLT_MAX){ - for(;i<(u->fft_size/2+1);++i){ - u->H[i]=coefficients[j]; - } - break; - } - //pa_log("i: %d, j: %d, freq: %f",i,j,freq_translated[j]); - //pa_log("interp: %0.4f %0.4f",freq_translated[j],freq_translated[j+1]); - pa_assert_se(freq_translated[j]=freq_translated[j]); - pa_assert_se(i<=freq_translated[j+1]); - //bilinear-inerpolation of coefficients specified - float c0=(i-freq_translated[j])/(freq_translated[j+1]-freq_translated[j]); - pa_assert_se(c0>=0&&c0<=1.0); - u->H[i]=((1.0f-c0)*coefficients[j]+c0*coefficients[j+1]); - pa_assert_se(u->H[i]>0); - while(i>=floor(freq_translated[j+1])){ - j++; - } - } - //divide out the fft gain - for(size_t i=0;i<(u->fft_size/2+1);++i){ - u->H[i]/=u->fft_size; + u->output_window = alloc((u->fft_size / 2 + 1), sizeof(fftwf_complex)); + u->forward_plan = fftwf_plan_dft_r2c_1d(u->fft_size, u->work_buffer, u->output_window, FFTW_MEASURE); + u->inverse_plan = fftwf_plan_dft_c2r_1d(u->fft_size, u->output_window, u->work_buffer, FFTW_MEASURE); + + hanning_window(u->W, u->window_size); + + unsigned H_i = pa_aupdate_write_begin(u->a_H); + u->H = u->Hs[H_i]; + for(size_t i = 0; i < u->fft_size / 2 + 1; ++i){ + u->H[i] = 1.0; } - free(freq_translated); + + //TODO cut this out and leave it for the client side + //const int freqs[] = {0,25,50,100,200,300,400,800,1500, + // 2000,3000,4000,5000,6000,7000,8000,9000,10000,11000,12000, + // 13000,14000,15000,16000,17000,18000,19000,20000,21000,22000,23000,24000,INT_MAX}; + //const float coefficients[] = {1,1,1,1,1,1,1,1,1,1, + // 1,1,1,1,1,1,1,1, + // 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}; + //const size_t ncoefficients = sizeof(coefficients)/sizeof(float); + //pa_assert_se(sizeof(freqs)/sizeof(int)==sizeof(coefficients)/sizeof(float)); + //float *freq_translated = (float *) pa_xmalloc0(sizeof(float)*(ncoefficients)); + //freq_translated[0] = 1; + ////Translate the frequencies in their natural sampling rate to the new sampling rate frequencies + //for(size_t i = 1; i < ncoefficients-1; ++i){ + // freq_translated[i] = ((float)freqs[i]*u->fft_size)/ss.rate; + // //pa_log("i: %ld: %d , %g",i, freqs[i], freq_translated[i]); + // pa_assert_se(freq_translated[i] >= freq_translated[i-1]); + //} + //freq_translated[ncoefficients-1] = FLT_MAX; + // + ////Interpolate the specified frequency band values + //u->H[0] = 1; + //for(size_t i = 1, j = 0; i < (u->fft_size / 2 + 1); ++i){ + // pa_assert_se(j < ncoefficients); + // //max frequency range passed, consider the rest as one band + // if(freq_translated[j+1] >= FLT_MAX){ + // for(; i < (u->fft_size / 2 + 1); ++i){ + // u->H[i] = coefficients[j]; + // } + // break; + // } + // //pa_log("i: %d, j: %d, freq: %f", i, j, freq_translated[j]); + // //pa_log("interp: %0.4f %0.4f", freq_translated[j], freq_translated[j+1]); + // pa_assert_se(freq_translated[j] < freq_translated[j+1]); + // pa_assert_se(i >= freq_translated[j]); + // pa_assert_se(i <= freq_translated[j+1]); + // //bilinear-inerpolation of coefficients specified + // float c0 = (i-freq_translated[j])/(freq_translated[j+1]-freq_translated[j]); + // pa_assert_se(c0 >= 0&&c0 <= 1.0); + // u->H[i] = ((1.0f-c0)*coefficients[j]+c0*coefficients[j+1]); + // pa_assert_se(u->H[i]>0); + // while(i >= floor(freq_translated[j+1])){ + // j++; + // } + //} + //pa_xfree(freq_translated); + fix_filter(u->H, u->fft_size); + pa_aupdate_write_swap(u->a_H); + pa_aupdate_write_end(u->a_H); /* Create sink */ @@ -858,8 +894,8 @@ int pa__init(pa_module*m) { pa_sink_set_asyncmsgq(u->sink, master->asyncmsgq); pa_sink_set_rtpoll(u->sink, master->rtpoll); - pa_sink_set_max_request(u->sink,u->R*fs); - //pa_sink_set_fixed_latency(u->sink,pa_bytes_to_usec(u->R*fs,&ss)); + pa_sink_set_max_request(u->sink, u->R*fs); + //pa_sink_set_fixed_latency(u->sink, pa_bytes_to_usec(u->R*fs, &ss)); /* Create sink input */ pa_sink_input_new_data_init(&sink_input_data); @@ -896,6 +932,8 @@ int pa__init(pa_module*m) { pa_xfree(use_default); + dbus_init(u); + return 0; fail: @@ -925,6 +963,7 @@ void pa__done(pa_module*m) { if (!(u = m->userdata)) return; + dbus_done(u); if (u->sink) { pa_sink_unlink(u->sink); @@ -944,18 +983,152 @@ void pa__done(pa_module*m) { fftwf_destroy_plan(u->inverse_plan); fftwf_destroy_plan(u->forward_plan); - free(u->output_window); - for(size_t c=0;cchannels;++c){ - free(u->output_buffer[c]); - free(u->overlap_accum[c]); - free(u->input[c]); + pa_xfree(u->output_window); + for(size_t c=0; c < u->channels; ++c){ + pa_xfree(u->overlap_accum[c]); + pa_xfree(u->input[c]); + } + pa_xfree(u->overlap_accum); + pa_xfree(u->input); + pa_xfree(u->work_buffer); + pa_xfree(u->W); + for(size_t i = 0; i < 2; ++i){ + pa_xfree(u->Hs[i]); } - free(u->output_buffer); - free(u->overlap_accum); - free(u->input); - free(u->work_buffer); - free(u->W); - free(u->H); pa_xfree(u); } + +enum property_handler_index { + PROPERTY_HANDLER_N_COEFS, + PROPERTY_HANDLER_COEFS, + PROPERTY_HANDLER_MAX +}; + +static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX]={ + [PROPERTY_HANDLER_N_COEFS]{.property_name="n_filter_coefficients",.type="u",.get_cb=get_n_coefs,.set_cb=NULL}, + [PROPERTY_HANDLER_COEFS]{.property_name="filter_coefficients",.type="ai",.get_cb=get_filter,.set_cb=set_filter} +}; + +//static pa_dbus_arg_info new_equalizer_args[] = { { "path","o",NULL} }; +//static pa_dbus_signal_info signals[SIGNAL_MAX] = { +// [SIGNAL_NEW_EQUALIZER]={.name="NewEqualizer",.arguments=new_equalizer_args,.n_arguments=1} +//}; + +#define EXTNAME "org.PulseAudio.Ext.Equalizing1" + +static pa_dbus_interface_info interface_info={ + .name=EXTNAME ".Equalizer", + .method_handlers=NULL, + .n_method_handlers=0, + .property_handlers=property_handlers, + .n_property_handlers=PROPERTY_HANDLER_MAX, + .get_all_properties_cb=handle_get_all, + .signals=NULL, + .n_signals=0 +}; + + +void dbus_init(struct userdata *u){ + u->dbus_protocol=pa_dbus_protocol_get(u->core); + u->dbus_path=pa_sprintf_malloc("/org/pulseaudio/core1/sink%d", u->sink->index); + + pa_dbus_protocol_add_interface(u->dbus_protocol, u->dbus_path, &interface_info, u); + pa_dbus_protocol_register_extension(u->dbus_protocol, EXTNAME); +} + +void dbus_done(struct userdata *u){ + pa_dbus_protocol_unregister_extension(u->dbus_protocol, EXTNAME); + pa_dbus_protocol_remove_interface(u->dbus_protocol, u->dbus_path, EXTNAME); + + pa_xfree(u->dbus_path); + pa_dbus_protocol_unref(u->dbus_protocol); +} + +void get_n_coefs(DBusConnection *conn, DBusMessage *msg, void *_u){ + pa_assert(conn); + pa_assert(msg); + pa_assert(_u); + + struct userdata *u=(struct userdata *)_u; + + uint32_t n_coefs=(uint32_t)(u->fft_size / 2 + 1); + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &n_coefs); +} + +void get_filter(DBusConnection *conn, DBusMessage *msg, void *_u){ + pa_assert(conn); + pa_assert(msg); + pa_assert(_u); + + struct userdata *u=(struct userdata *)_u; + + unsigned n_coefs=(unsigned)(u->fft_size / 2 + 1); + double *H_=(double *)pa_xmalloc0(n_coefs*sizeof(double)); + + unsigned H_i=pa_aupdate_read_begin(u->a_H); + float *H=u->Hs[H_i]; + for(size_t i = 0;i < u->fft_size / 2 + 1; ++i){ + H_[i]=H[i]; + } + pa_aupdate_read_end(u->a_H); + pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_DOUBLE, &H_, n_coefs); + pa_xfree(H_); +} + +void set_filter(DBusConnection *conn, DBusMessage *msg, void *_u){ + pa_assert(conn); + pa_assert(msg); + pa_assert(_u); + + struct userdata *u=(struct userdata *)_u; + double *H_; + unsigned _n_coefs; + pa_dbus_get_fixed_array_set_property_arg(conn, msg, DBUS_TYPE_DOUBLE, &H_, &_n_coefs); + if(_n_coefs!=u->fft_size / 2 + 1){ + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "This filter takes exactly %ld coefficients, you gave %d", u->fft_size / 2 + 1, _n_coefs); + return; + } + unsigned H_i = pa_aupdate_write_begin(u->a_H); + float *H = u->Hs[H_i]; + for(size_t i = 0; i < u->fft_size / 2 + 1; ++i){ + H[i] = (float)H_[i]; + } + pa_aupdate_write_swap(u->a_H); + pa_aupdate_write_end(u->a_H); + + pa_dbus_send_empty_reply(conn, msg); +} + +void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *_u){ + pa_assert(conn); + pa_assert(msg); + pa_assert(_u); + + struct userdata *u = (struct userdata *)_u; + DBusMessage *reply = NULL; + DBusMessageIter msg_iter, dict_iter; + + int n_coefs=(unsigned)(u->fft_size / 2 + 1); + double *H_=(double *)pa_xmalloc0(n_coefs*sizeof(double)); + + unsigned H_i=pa_aupdate_read_begin(u->a_H); + float *H=u->Hs[H_i]; + for(size_t i = 0; i < u->fft_size / 2 + 1; ++i){ + H_[i] = H[i]; + } + pa_aupdate_read_end(u->a_H); + + pa_assert_se((reply = dbus_message_new_method_return(msg))); + dbus_message_iter_init_append(reply, &msg_iter); + pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter)); + + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_N_COEFS].property_name, DBUS_TYPE_UINT32, &n_coefs); + pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_COEFS].property_name, DBUS_TYPE_DOUBLE, H_, n_coefs); + + pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter)); + pa_assert_se(dbus_connection_send(conn, reply, NULL)); + dbus_message_unref(reply); + + pa_xfree(H_); +} -- cgit From 66a6cc693bcf441d86ed56a3b15be948008e9de7 Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Sat, 1 Aug 2009 20:23:49 -0700 Subject: module-equalizer-sink: added support for suspend/resume of filter coefficients unregister the correct dbus interface. made equalizer state file sink index dependent expanded dbus properties whitespace --- src/modules/module-equalizer-sink.c | 219 ++++++++++++++++++++++++------------ 1 file changed, 147 insertions(+), 72 deletions(-) (limited to 'src') diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c index d8eb5f3d..6ea59514 100755 --- a/src/modules/module-equalizer-sink.c +++ b/src/modules/module-equalizer-sink.c @@ -50,7 +50,7 @@ USA. #include #include #include -#include +#include #include #include @@ -114,6 +114,8 @@ struct userdata { pa_dbus_protocol *dbus_protocol; char *dbus_path; + + pa_database *database; }; static const char* const valid_modargs[] = { @@ -145,14 +147,18 @@ void dsp_logic( static void dbus_init(struct userdata *u); static void dbus_done(struct userdata *u); static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *_u); +void get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *_u); +void get_filter_rate(DBusConnection *conn, DBusMessage *msg, void *_u); static void get_n_coefs(DBusConnection *conn, DBusMessage *msg, void *_u); static void get_filter(DBusConnection *conn, DBusMessage *msg, void *_u); static void set_filter(DBusConnection *conn, DBusMessage *msg, void *_u); +static void save_state(struct userdata *u); #define v_size 4 #define gettime(x) clock_gettime(CLOCK_MONOTONIC, &x) #define tdiff(x, y) time_diff(&x, &y) #define mround(x, y) (x % y == 0 ? x : ( x / y + 1) * y) +#define COEFKEY "coefficients" uint64_t time_diff(struct timespec *timeA_p, struct timespec *timeB_p) { @@ -321,7 +327,7 @@ void dsp_logic( //do fft fftwf_execute_dft_r2c(u->forward_plan, dst, output_window); //perform filtering - for(size_t j = 0;j < u->fft_size / 2 + 1; ++j){ + for(size_t j = 0; j < u->fft_size / 2 + 1; ++j){ u->output_window[j][0] *= u->H[j]; u->output_window[j][1] *= u->H[j]; } @@ -702,6 +708,22 @@ static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t s } } +void save_state(struct userdata *u){ + const float *H = u->Hs[pa_aupdate_read_begin(u->a_H)]; + float *H_n = pa_xmalloc((u->fft_size / 2 + 1) * sizeof(float)); + for(size_t i = 0 ; i <= u->fft_size / 2 + 1; ++i){ + H_n[i] = H[i] * u->fft_size; + } + pa_aupdate_read_end(u->a_H); + pa_datum key, data; + key.data = (char *) COEFKEY; + key.size = strlen(key.data); + data.data = H_n; + data.size = (u->fft_size / 2 + 1) * sizeof(float); + pa_database_set(u->database, &key, &data, TRUE); + pa_database_sync(u->database); +} + /* Called from main context */ static pa_bool_t sink_input_may_move_to_cb(pa_sink_input *i, pa_sink *dest) { struct userdata *u; @@ -802,62 +824,6 @@ int pa__init(pa_module*m) { hanning_window(u->W, u->window_size); - unsigned H_i = pa_aupdate_write_begin(u->a_H); - u->H = u->Hs[H_i]; - for(size_t i = 0; i < u->fft_size / 2 + 1; ++i){ - u->H[i] = 1.0; - } - - //TODO cut this out and leave it for the client side - //const int freqs[] = {0,25,50,100,200,300,400,800,1500, - // 2000,3000,4000,5000,6000,7000,8000,9000,10000,11000,12000, - // 13000,14000,15000,16000,17000,18000,19000,20000,21000,22000,23000,24000,INT_MAX}; - //const float coefficients[] = {1,1,1,1,1,1,1,1,1,1, - // 1,1,1,1,1,1,1,1, - // 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}; - //const size_t ncoefficients = sizeof(coefficients)/sizeof(float); - //pa_assert_se(sizeof(freqs)/sizeof(int)==sizeof(coefficients)/sizeof(float)); - //float *freq_translated = (float *) pa_xmalloc0(sizeof(float)*(ncoefficients)); - //freq_translated[0] = 1; - ////Translate the frequencies in their natural sampling rate to the new sampling rate frequencies - //for(size_t i = 1; i < ncoefficients-1; ++i){ - // freq_translated[i] = ((float)freqs[i]*u->fft_size)/ss.rate; - // //pa_log("i: %ld: %d , %g",i, freqs[i], freq_translated[i]); - // pa_assert_se(freq_translated[i] >= freq_translated[i-1]); - //} - //freq_translated[ncoefficients-1] = FLT_MAX; - // - ////Interpolate the specified frequency band values - //u->H[0] = 1; - //for(size_t i = 1, j = 0; i < (u->fft_size / 2 + 1); ++i){ - // pa_assert_se(j < ncoefficients); - // //max frequency range passed, consider the rest as one band - // if(freq_translated[j+1] >= FLT_MAX){ - // for(; i < (u->fft_size / 2 + 1); ++i){ - // u->H[i] = coefficients[j]; - // } - // break; - // } - // //pa_log("i: %d, j: %d, freq: %f", i, j, freq_translated[j]); - // //pa_log("interp: %0.4f %0.4f", freq_translated[j], freq_translated[j+1]); - // pa_assert_se(freq_translated[j] < freq_translated[j+1]); - // pa_assert_se(i >= freq_translated[j]); - // pa_assert_se(i <= freq_translated[j+1]); - // //bilinear-inerpolation of coefficients specified - // float c0 = (i-freq_translated[j])/(freq_translated[j+1]-freq_translated[j]); - // pa_assert_se(c0 >= 0&&c0 <= 1.0); - // u->H[i] = ((1.0f-c0)*coefficients[j]+c0*coefficients[j+1]); - // pa_assert_se(u->H[i]>0); - // while(i >= floor(freq_translated[j+1])){ - // j++; - // } - //} - //pa_xfree(freq_translated); - fix_filter(u->H, u->fft_size); - pa_aupdate_write_swap(u->a_H); - pa_aupdate_write_end(u->a_H); - - /* Create sink */ pa_sink_new_data_init(&sink_data); sink_data.driver = __FILE__; @@ -932,6 +898,82 @@ int pa__init(pa_module*m) { pa_xfree(use_default); + char *dbname; + char *pref = pa_sprintf_malloc("equalizer-%s-state", u->sink->name); + pa_assert_se(dbname = pa_state_path(pref, TRUE)); + pa_xfree(pref); + pa_assert_se(u->database = pa_database_open(dbname, TRUE)); + pa_xfree(dbname); + + unsigned H_i = pa_aupdate_write_begin(u->a_H); + u->H = u->Hs[H_i]; + for(size_t i = 0; i < u->fft_size / 2 + 1; ++i){ + u->H[i] = 1.0; + } + + //TODO cut this out and leave it for the client side + //const int freqs[] = {0,25,50,100,200,300,400,800,1500, + // 2000,3000,4000,5000,6000,7000,8000,9000,10000,11000,12000, + // 13000,14000,15000,16000,17000,18000,19000,20000,21000,22000,23000,24000,INT_MAX}; + //const float coefficients[] = {1,1,1,1,1,1,1,1,1,1, + // 1,1,1,1,1,1,1,1, + // 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}; + //const size_t ncoefficients = sizeof(coefficients)/sizeof(float); + //pa_assert_se(sizeof(freqs)/sizeof(int)==sizeof(coefficients)/sizeof(float)); + //float *freq_translated = (float *) pa_xmalloc0(sizeof(float)*(ncoefficients)); + //freq_translated[0] = 1; + ////Translate the frequencies in their natural sampling rate to the new sampling rate frequencies + //for(size_t i = 1; i < ncoefficients-1; ++i){ + // freq_translated[i] = ((float)freqs[i]*u->fft_size)/ss.rate; + // //pa_log("i: %ld: %d , %g",i, freqs[i], freq_translated[i]); + // pa_assert_se(freq_translated[i] >= freq_translated[i-1]); + //} + //freq_translated[ncoefficients-1] = FLT_MAX; + // + ////Interpolate the specified frequency band values + //u->H[0] = 1; + //for(size_t i = 1, j = 0; i < (u->fft_size / 2 + 1); ++i){ + // pa_assert_se(j < ncoefficients); + // //max frequency range passed, consider the rest as one band + // if(freq_translated[j+1] >= FLT_MAX){ + // for(; i < (u->fft_size / 2 + 1); ++i){ + // u->H[i] = coefficients[j]; + // } + // break; + // } + // //pa_log("i: %d, j: %d, freq: %f", i, j, freq_translated[j]); + // //pa_log("interp: %0.4f %0.4f", freq_translated[j], freq_translated[j+1]); + // pa_assert_se(freq_translated[j] < freq_translated[j+1]); + // pa_assert_se(i >= freq_translated[j]); + // pa_assert_se(i <= freq_translated[j+1]); + // //bilinear-inerpolation of coefficients specified + // float c0 = (i-freq_translated[j])/(freq_translated[j+1]-freq_translated[j]); + // pa_assert_se(c0 >= 0&&c0 <= 1.0); + // u->H[i] = ((1.0f-c0)*coefficients[j]+c0*coefficients[j+1]); + // pa_assert_se(u->H[i]>0); + // while(i >= floor(freq_translated[j+1])){ + // j++; + // } + //} + //pa_xfree(freq_translated); + + //load old parameters + pa_datum key,value; + key.data = (char *) COEFKEY; + key.size = strlen(key.data); + if (pa_database_get(u->database, &key, &value) != NULL){ + if(value.size == (u->fft_size / 2 + 1) * sizeof(float)){ + memcpy(u->H, value.data, (u->fft_size / 2 + 1) * sizeof(float)); + } + pa_datum_free(&value); + } + + fix_filter(u->H, u->fft_size); + pa_aupdate_write_swap(u->a_H); + pa_aupdate_write_end(u->a_H); + + + dbus_init(u); return 0; @@ -963,6 +1005,10 @@ void pa__done(pa_module*m) { if (!(u = m->userdata)) return; + + save_state(u); + pa_database_close(u->database); + dbus_done(u); if (u->sink) { @@ -1000,14 +1046,18 @@ void pa__done(pa_module*m) { } enum property_handler_index { + PROPERTY_HANDLER_SAMPLERATE, + PROPERTY_HANDLER_FILTERSAMPLERATE, PROPERTY_HANDLER_N_COEFS, PROPERTY_HANDLER_COEFS, PROPERTY_HANDLER_MAX }; static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX]={ - [PROPERTY_HANDLER_N_COEFS]{.property_name="n_filter_coefficients",.type="u",.get_cb=get_n_coefs,.set_cb=NULL}, - [PROPERTY_HANDLER_COEFS]{.property_name="filter_coefficients",.type="ai",.get_cb=get_filter,.set_cb=set_filter} + [PROPERTY_HANDLER_SAMPLERATE]{.property_name="SampleRate",.type="u",.get_cb=get_sample_rate,.set_cb=NULL}, + [PROPERTY_HANDLER_FILTERSAMPLERATE]{.property_name="FilterSampleRate",.type="u",.get_cb=get_filter_rate,.set_cb=NULL}, + [PROPERTY_HANDLER_N_COEFS]{.property_name="NFilterCoefficients",.type="u",.get_cb=get_n_coefs,.set_cb=NULL}, + [PROPERTY_HANDLER_COEFS]{.property_name="FilterCoefficients",.type="ai",.get_cb=get_filter,.set_cb=set_filter} }; //static pa_dbus_arg_info new_equalizer_args[] = { { "path","o",NULL} }; @@ -1039,7 +1089,7 @@ void dbus_init(struct userdata *u){ void dbus_done(struct userdata *u){ pa_dbus_protocol_unregister_extension(u->dbus_protocol, EXTNAME); - pa_dbus_protocol_remove_interface(u->dbus_protocol, u->dbus_path, EXTNAME); + pa_dbus_protocol_remove_interface(u->dbus_protocol, u->dbus_path, interface_info.name); pa_xfree(u->dbus_path); pa_dbus_protocol_unref(u->dbus_protocol); @@ -1056,23 +1106,41 @@ void get_n_coefs(DBusConnection *conn, DBusMessage *msg, void *_u){ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &n_coefs); } +void get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *_u){ + pa_assert(conn); + pa_assert(msg); + pa_assert(_u); + + struct userdata *u=(struct userdata *) _u; + uint32_t rate=(uint32_t) u->sink->sample_spec.rate; + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &rate); +} +void get_filter_rate(DBusConnection *conn, DBusMessage *msg, void *_u){ + pa_assert(conn); + pa_assert(msg); + pa_assert(_u); + + struct userdata *u=(struct userdata *) _u; + uint32_t fft_size=(uint32_t) u->fft_size; + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &fft_size); +} + void get_filter(DBusConnection *conn, DBusMessage *msg, void *_u){ pa_assert(conn); pa_assert(msg); pa_assert(_u); - struct userdata *u=(struct userdata *)_u; + struct userdata *u = (struct userdata *)_u; - unsigned n_coefs=(unsigned)(u->fft_size / 2 + 1); - double *H_=(double *)pa_xmalloc0(n_coefs*sizeof(double)); + unsigned n_coefs = (unsigned) (u->fft_size / 2 + 1); + double *H_ = (double *) pa_xmalloc0(n_coefs * sizeof(double)); - unsigned H_i=pa_aupdate_read_begin(u->a_H); - float *H=u->Hs[H_i]; + float *H=u->Hs[pa_aupdate_read_begin(u->a_H)]; for(size_t i = 0;i < u->fft_size / 2 + 1; ++i){ - H_[i]=H[i]; + H_[i] = H[i] * u->fft_size; } pa_aupdate_read_end(u->a_H); - pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_DOUBLE, &H_, n_coefs); + pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_DOUBLE, H_, n_coefs); pa_xfree(H_); } @@ -1089,14 +1157,17 @@ void set_filter(DBusConnection *conn, DBusMessage *msg, void *_u){ pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "This filter takes exactly %ld coefficients, you gave %d", u->fft_size / 2 + 1, _n_coefs); return; } - unsigned H_i = pa_aupdate_write_begin(u->a_H); - float *H = u->Hs[H_i]; + float *H = u->Hs[pa_aupdate_write_begin(u->a_H)]; for(size_t i = 0; i < u->fft_size / 2 + 1; ++i){ - H[i] = (float)H_[i]; + H[i] = (float) H_[i]; } + fix_filter(H, u->fft_size); pa_aupdate_write_swap(u->a_H); pa_aupdate_write_end(u->a_H); + //Stupid for IO reasons? Add a save signal to dbus instead + save_state(u); + pa_dbus_send_empty_reply(conn, msg); } @@ -1109,7 +1180,9 @@ void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *_u){ DBusMessage *reply = NULL; DBusMessageIter msg_iter, dict_iter; - int n_coefs=(unsigned)(u->fft_size / 2 + 1); + uint32_t rate=(uint32_t) u->sink->sample_spec.rate; + uint32_t fft_size=(uint32_t) u->fft_size; + uint32_t n_coefs=(uint32_t)(u->fft_size / 2 + 1); double *H_=(double *)pa_xmalloc0(n_coefs*sizeof(double)); unsigned H_i=pa_aupdate_read_begin(u->a_H); @@ -1123,6 +1196,8 @@ void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *_u){ dbus_message_iter_init_append(reply, &msg_iter); pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter)); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SAMPLERATE].property_name, DBUS_TYPE_UINT32, &rate); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_FILTERSAMPLERATE].property_name, DBUS_TYPE_UINT32, &fft_size); pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_N_COEFS].property_name, DBUS_TYPE_UINT32, &n_coefs); pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_COEFS].property_name, DBUS_TYPE_DOUBLE, H_, n_coefs); -- cgit From 144f1c4f31fe3cde73a0d9ea08d2ae34cc24bdf6 Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Wed, 5 Aug 2009 00:52:16 -0700 Subject: module-equalizer-sink: dbus properties and manager so that multiple sinks can be loaded and mixers can be equalizer-sink aware functionality to seed new filters quickly (rteq guis) profile support extra checking in client->server dbus messages --- src/modules/module-equalizer-sink.c | 773 ++++++++++++++++++++++++++++-------- 1 file changed, 613 insertions(+), 160 deletions(-) (limited to 'src') diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c index 6ea59514..285fddbf 100755 --- a/src/modules/module-equalizer-sink.c +++ b/src/modules/module-equalizer-sink.c @@ -50,6 +50,8 @@ USA. #include #include #include +#include +#include #include #include #include @@ -82,6 +84,7 @@ struct userdata { pa_module *module; pa_sink *sink, *master; pa_sink_input *sink_input; + char *name; size_t channels; size_t fft_size;//length (res) of fft @@ -131,9 +134,17 @@ static const char* const valid_modargs[] = { static uint64_t time_diff(struct timespec *timeA_p, struct timespec *timeB_p); static void hanning_window(float *W, size_t window_size); +void fix_filter(float *H, size_t fft_size); +void interpolate(float *signal, size_t length, uint32_t *xs, float *ys, size_t n_points); static void array_out(const char *name, float *a, size_t length); +static int is_monotonic(uint32_t *xs, size_t length); static void process_samples(struct userdata *u); static void input_buffer(struct userdata *u, pa_memchunk *in); +static void save_profile(struct userdata *u,char *name); +static void save_state(struct userdata *u); +static void remove_profile(pa_core *u,char *name); +static const char * load_profile(struct userdata *u,char *name); +static void load_state(struct userdata *u); void dsp_logic( float * __restrict__ dst, @@ -144,21 +155,37 @@ void dsp_logic( fftwf_complex * __restrict__ output_window, struct userdata *u); + +/* + * DBus Routines and Callbacks + */ +#define EXTNAME "org.PulseAudio.Ext.Equalizing1" +#define MANAGER_PATH "/org/pulseaudio/equalizing1" static void dbus_init(struct userdata *u); static void dbus_done(struct userdata *u); -static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *_u); -void get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *_u); -void get_filter_rate(DBusConnection *conn, DBusMessage *msg, void *_u); -static void get_n_coefs(DBusConnection *conn, DBusMessage *msg, void *_u); -static void get_filter(DBusConnection *conn, DBusMessage *msg, void *_u); -static void set_filter(DBusConnection *conn, DBusMessage *msg, void *_u); -static void save_state(struct userdata *u); +static void manager_get_revision(DBusConnection *conn, DBusMessage *msg, void *_u); +static void manager_get_sinks(DBusConnection *conn, DBusMessage *msg, void *_u); +static void manager_get_profiles(DBusConnection *conn, DBusMessage *msg, void *_u); +static void manager_get_all(DBusConnection *conn, DBusMessage *msg, void *_u); +static void manager_handle_remove_profile(DBusConnection *conn, DBusMessage *msg, void *_u); +static void equalizer_get_revision(DBusConnection *conn, DBusMessage *msg, void *_u); +static void equalizer_get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *_u); +static void equalizer_get_filter_rate(DBusConnection *conn, DBusMessage *msg, void *_u); +static void equalizer_get_n_coefs(DBusConnection *conn, DBusMessage *msg, void *_u); +static void equalizer_get_filter(DBusConnection *conn, DBusMessage *msg, void *_u); +static void equalizer_get_all(DBusConnection *conn, DBusMessage *msg, void *_u); +static void equalizer_set_filter(DBusConnection *conn, DBusMessage *msg, void *_u); +static void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, void *_u); +static void equalizer_handle_get_filter_points(DBusConnection *conn, DBusMessage *msg, void *_u); +static void equalizer_handle_save_profile(DBusConnection *conn, DBusMessage *msg, void *_u); +static void equalizer_handle_load_profile(DBusConnection *conn, DBusMessage *msg, void *_u); #define v_size 4 #define gettime(x) clock_gettime(CLOCK_MONOTONIC, &x) #define tdiff(x, y) time_diff(&x, &y) #define mround(x, y) (x % y == 0 ? x : ( x / y + 1) * y) -#define COEFKEY "coefficients" +#define SINKLIST "equalized_sinklist" +#define EQDB "equalizer_db" uint64_t time_diff(struct timespec *timeA_p, struct timespec *timeB_p) { @@ -166,20 +193,43 @@ uint64_t time_diff(struct timespec *timeA_p, struct timespec *timeB_p) ((timeB_p->tv_sec * 1000000000ULL) + timeB_p->tv_nsec); } -static void hanning_window(float *W, size_t window_size){ +void hanning_window(float *W, size_t window_size){ //h=.5*(1-cos(2*pi*j/(window_size+1)), COLA for R=(M+1)/2 for(size_t i=0; i < window_size;++i){ W[i] = (float).5*(1-cos(2*M_PI*i/(window_size+1))); } } -static void fix_filter(float *H, size_t fft_size){ +void fix_filter(float *H, size_t fft_size){ //divide out the fft gain - for(size_t i = 0; i < (fft_size / 2 + 1); ++i){ + for(size_t i = 0; i < fft_size / 2 + 1; ++i){ H[i] /= fft_size; } } +void interpolate(float *signal, size_t length, uint32_t *xs, float *ys, size_t n_points){ + //Note that xs must be monotonically increasing! + pa_assert_se(n_points>=2); + pa_assert_se(xs[0] == 0); + pa_assert_se(xs[n_points - 1] == length - 1); + for(size_t x = 0, x_range_lower_i = 0; x < length-1; ++x){ + pa_assert(x_range_lower_i < n_points-1); + float x_range_lower = (float) (xs[x_range_lower_i]); + float x_range_upper = (float) (xs[x_range_lower_i+1]); + pa_assert_se(x_range_lower < x_range_upper); + pa_assert_se(x >= x_range_lower); + pa_assert_se(x <= x_range_upper); + //bilinear-interpolation of coefficients specified + float c0 = (x-x_range_lower)/(x_range_upper-x_range_lower); + pa_assert_se(c0 >= 0&&c0 <= 1.0); + signal[x] = ((1.0f - c0) * ys[x_range_lower_i] + c0 * ys[x_range_lower_i + 1]); + while(x >= xs[x_range_lower_i + 1]){ + x_range_lower_i++; + } + } + signal[length-1]=ys[n_points-1]; +} + void array_out(const char *name, float *a, size_t length){ FILE *p=fopen(name, "w"); if(!p){ @@ -195,6 +245,17 @@ void array_out(const char *name, float *a, size_t length){ fprintf(p, "\n"); fclose(p); } +static int is_monotonic(uint32_t *xs,size_t length){ + if(length<2){ + return 1; + } + for(size_t i = 1; i < length; ++i){ + if(xs[i]<=xs[i-1]){ + return 0; + } + } + return 1; +} /* Called from I/O thread context */ @@ -708,15 +769,16 @@ static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t s } } -void save_state(struct userdata *u){ - const float *H = u->Hs[pa_aupdate_read_begin(u->a_H)]; +void save_profile(struct userdata *u, char *name){ float *H_n = pa_xmalloc((u->fft_size / 2 + 1) * sizeof(float)); + const float *H = u->Hs[pa_aupdate_read_begin(u->a_H)]; for(size_t i = 0 ; i <= u->fft_size / 2 + 1; ++i){ - H_n[i] = H[i] * u->fft_size; + //H_n[i] = H[i] * u->fft_size; + H_n[i] = H[i]; } pa_aupdate_read_end(u->a_H); pa_datum key, data; - key.data = (char *) COEFKEY; + key.data=name; key.size = strlen(key.data); data.data = H_n; data.size = (u->fft_size / 2 + 1) * sizeof(float); @@ -724,6 +786,49 @@ void save_state(struct userdata *u){ pa_database_sync(u->database); } +void save_state(struct userdata *u){ + char *state_name = pa_sprintf_malloc("%s-previous-state", u->name); + save_profile(u, state_name); + pa_xfree(state_name); +} + +void remove_profile(pa_core *c,char *name){ + pa_datum key; + key.data = name; + key.size = strlen(key.data); + pa_database *database; + pa_assert_se(database = pa_shared_get(c,EQDB)); + pa_database_unset(database,&key); + pa_database_sync(database); +} + +const char* load_profile(struct userdata *u,char *name){ + pa_datum key,value; + key.data = name; + key.size = strlen(key.data); + if(pa_database_get(u->database, &key, &value) != NULL){ + if(value.size == (u->fft_size / 2 + 1) * sizeof(float)){ + float *H=u->Hs[pa_aupdate_write_begin(u->a_H)]; + memcpy(H, value.data, value.size); + pa_aupdate_write_swap(u->a_H); + pa_aupdate_write_end(u->a_H); + }else{ + return "incompatible size"; + } + pa_datum_free(&value); + }else{ + return "profile doesn't exist"; + } + return NULL; + //fix_filter(u->H, u->fft_size); +} +void load_state(struct userdata *u){ + char *state_name=pa_sprintf_malloc("%s-previous-state", u->name); + load_profile(u,state_name); + pa_xfree(state_name); +} + + /* Called from main context */ static pa_bool_t sink_input_may_move_to_cb(pa_sink_input *i, pa_sink *dest) { struct userdata *u; @@ -791,7 +896,7 @@ int pa__init(pa_module*m) { u->channels = ss.channels; u->fft_size = pow(2, ceil(log(ss.rate)/log(2))); pa_log("fft size: %ld", u->fft_size); - u->window_size = 7999; + u->window_size = 15999; u->R = (u->window_size+1)/2; u->overlap_size = u->window_size-u->R; u->target_samples = 1*u->R; @@ -851,7 +956,7 @@ int pa__init(pa_module*m) { pa_log("Failed to create sink."); goto fail; } - + u->name=pa_xstrdup(u->sink->name); u->sink->parent.process_msg = sink_process_msg; u->sink->set_state = sink_set_state; u->sink->update_requested_latency = sink_update_requested_latency; @@ -898,83 +1003,14 @@ int pa__init(pa_module*m) { pa_xfree(use_default); - char *dbname; - char *pref = pa_sprintf_malloc("equalizer-%s-state", u->sink->name); - pa_assert_se(dbname = pa_state_path(pref, TRUE)); - pa_xfree(pref); - pa_assert_se(u->database = pa_database_open(dbname, TRUE)); - pa_xfree(dbname); + dbus_init(u); - unsigned H_i = pa_aupdate_write_begin(u->a_H); - u->H = u->Hs[H_i]; + //default filter to these for(size_t i = 0; i < u->fft_size / 2 + 1; ++i){ - u->H[i] = 1.0; + u->Hs[1][i]=u->Hs[0][i] = 1.0; } - - //TODO cut this out and leave it for the client side - //const int freqs[] = {0,25,50,100,200,300,400,800,1500, - // 2000,3000,4000,5000,6000,7000,8000,9000,10000,11000,12000, - // 13000,14000,15000,16000,17000,18000,19000,20000,21000,22000,23000,24000,INT_MAX}; - //const float coefficients[] = {1,1,1,1,1,1,1,1,1,1, - // 1,1,1,1,1,1,1,1, - // 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}; - //const size_t ncoefficients = sizeof(coefficients)/sizeof(float); - //pa_assert_se(sizeof(freqs)/sizeof(int)==sizeof(coefficients)/sizeof(float)); - //float *freq_translated = (float *) pa_xmalloc0(sizeof(float)*(ncoefficients)); - //freq_translated[0] = 1; - ////Translate the frequencies in their natural sampling rate to the new sampling rate frequencies - //for(size_t i = 1; i < ncoefficients-1; ++i){ - // freq_translated[i] = ((float)freqs[i]*u->fft_size)/ss.rate; - // //pa_log("i: %ld: %d , %g",i, freqs[i], freq_translated[i]); - // pa_assert_se(freq_translated[i] >= freq_translated[i-1]); - //} - //freq_translated[ncoefficients-1] = FLT_MAX; - // - ////Interpolate the specified frequency band values - //u->H[0] = 1; - //for(size_t i = 1, j = 0; i < (u->fft_size / 2 + 1); ++i){ - // pa_assert_se(j < ncoefficients); - // //max frequency range passed, consider the rest as one band - // if(freq_translated[j+1] >= FLT_MAX){ - // for(; i < (u->fft_size / 2 + 1); ++i){ - // u->H[i] = coefficients[j]; - // } - // break; - // } - // //pa_log("i: %d, j: %d, freq: %f", i, j, freq_translated[j]); - // //pa_log("interp: %0.4f %0.4f", freq_translated[j], freq_translated[j+1]); - // pa_assert_se(freq_translated[j] < freq_translated[j+1]); - // pa_assert_se(i >= freq_translated[j]); - // pa_assert_se(i <= freq_translated[j+1]); - // //bilinear-inerpolation of coefficients specified - // float c0 = (i-freq_translated[j])/(freq_translated[j+1]-freq_translated[j]); - // pa_assert_se(c0 >= 0&&c0 <= 1.0); - // u->H[i] = ((1.0f-c0)*coefficients[j]+c0*coefficients[j+1]); - // pa_assert_se(u->H[i]>0); - // while(i >= floor(freq_translated[j+1])){ - // j++; - // } - //} - //pa_xfree(freq_translated); - //load old parameters - pa_datum key,value; - key.data = (char *) COEFKEY; - key.size = strlen(key.data); - if (pa_database_get(u->database, &key, &value) != NULL){ - if(value.size == (u->fft_size / 2 + 1) * sizeof(float)){ - memcpy(u->H, value.data, (u->fft_size / 2 + 1) * sizeof(float)); - } - pa_datum_free(&value); - } - - fix_filter(u->H, u->fft_size); - pa_aupdate_write_swap(u->a_H); - pa_aupdate_write_end(u->a_H); - - - - dbus_init(u); + load_state(u); return 0; @@ -1007,7 +1043,6 @@ void pa__done(pa_module*m) { return; save_state(u); - pa_database_close(u->database); dbus_done(u); @@ -1042,22 +1077,130 @@ void pa__done(pa_module*m) { pa_xfree(u->Hs[i]); } + pa_xfree(u->name); + pa_xfree(u); } -enum property_handler_index { - PROPERTY_HANDLER_SAMPLERATE, - PROPERTY_HANDLER_FILTERSAMPLERATE, - PROPERTY_HANDLER_N_COEFS, - PROPERTY_HANDLER_COEFS, - PROPERTY_HANDLER_MAX +enum manager_method_index { + MANAGER_METHOD_REMOVE_PROFILE, + MANAGER_METHOD_MAX +}; + +pa_dbus_arg_info remove_profile_args[]={ + {"name", "s","in"}, +}; + +static pa_dbus_method_handler manager_methods[MANAGER_METHOD_MAX]={ + [MANAGER_METHOD_REMOVE_PROFILE]{ + .method_name="RemoveProfile", + .arguments=remove_profile_args, + .n_arguments=sizeof(remove_profile_args)/sizeof(pa_dbus_arg_info), + .receive_cb=manager_handle_remove_profile} +}; + +enum manager_handler_index { + MANAGER_HANDLER_REVISION, + MANAGER_HANDLER_EQUALIZED_SINKS, + MANAGER_HANDLER_PROFILES, + MANAGER_HANDLER_MAX +}; + +static pa_dbus_property_handler manager_handlers[MANAGER_HANDLER_MAX]={ + [MANAGER_HANDLER_REVISION]={.property_name="InterfaceRevision",.type="u",.get_cb=manager_get_revision,.set_cb=NULL}, + [MANAGER_HANDLER_EQUALIZED_SINKS]={.property_name="EqualizedSinks",.type="ao",.get_cb=manager_get_sinks,.set_cb=NULL}, + [MANAGER_HANDLER_PROFILES]={.property_name="Profiles",.type="as",.get_cb=manager_get_profiles,.set_cb=NULL} +}; + +static pa_dbus_interface_info manager_info={ + .name=EXTNAME ".Manager", + .method_handlers=manager_methods, + .n_method_handlers=MANAGER_METHOD_MAX, + .property_handlers=manager_handlers, + .n_property_handlers=MANAGER_HANDLER_MAX, + .get_all_properties_cb=manager_get_all, + .signals=NULL, + .n_signals=0 +}; + + +enum equalizer_method_index { + EQUALIZER_METHOD_FILTER_POINTS, + EQUALIZER_METHOD_SEED_FILTER, + EQUALIZER_METHOD_SAVE_PROFILE, + EQUALIZER_METHOD_LOAD_PROFILE, + EQUALIZER_METHOD_SET_FILTER, + EQUALIZER_METHOD_GET_FILTER, + EQUALIZER_METHOD_MAX +}; + +enum equalizer_handler_index { + EQUALIZER_HANDLER_REVISION, + EQUALIZER_HANDLER_SAMPLERATE, + EQUALIZER_HANDLER_FILTERSAMPLERATE, + EQUALIZER_HANDLER_N_COEFS, + EQUALIZER_HANDLER_MAX +}; + +pa_dbus_arg_info filter_points_args[]={ + {"xs", "au","in"}, + {"ys", "ad","out"}, +}; +pa_dbus_arg_info seed_filter_args[]={ + {"xs", "au","in"}, + {"ys", "ad","in"}, +}; +pa_dbus_arg_info save_profile_args[]={ + {"name", "s","in"}, +}; +pa_dbus_arg_info load_profile_args[]={ + {"name", "s","in"}, +}; +pa_dbus_arg_info set_filter_args[]={ + {"coefficients", "ad","in"}, +}; +pa_dbus_arg_info get_filter_args[]={ + {"coefficients", "ad","out"}, +}; + +static pa_dbus_method_handler equalizer_methods[EQUALIZER_METHOD_MAX]={ + [EQUALIZER_METHOD_SEED_FILTER]{ + .method_name="SeedFilter", + .arguments=seed_filter_args, + .n_arguments=sizeof(seed_filter_args)/sizeof(pa_dbus_arg_info), + .receive_cb=equalizer_handle_seed_filter}, + [EQUALIZER_METHOD_FILTER_POINTS]{ + .method_name="FilterAtPoints", + .arguments=filter_points_args, + .n_arguments=sizeof(filter_points_args)/sizeof(pa_dbus_arg_info), + .receive_cb=equalizer_handle_get_filter_points}, + [EQUALIZER_METHOD_SAVE_PROFILE]{ + .method_name="SaveProfile", + .arguments=save_profile_args, + .n_arguments=sizeof(save_profile_args)/sizeof(pa_dbus_arg_info), + .receive_cb=equalizer_handle_save_profile}, + [EQUALIZER_METHOD_LOAD_PROFILE]{ + .method_name="LoadProfile", + .arguments=load_profile_args, + .n_arguments=sizeof(load_profile_args)/sizeof(pa_dbus_arg_info), + .receive_cb=equalizer_handle_load_profile}, + [EQUALIZER_METHOD_SET_FILTER]{ + .method_name="SetFilterCoefficients", + .arguments=set_filter_args, + .n_arguments=sizeof(set_filter_args)/sizeof(pa_dbus_arg_info), + .receive_cb=equalizer_set_filter}, + [EQUALIZER_METHOD_GET_FILTER]{ + .method_name="GetFilterCoefficients", + .arguments=get_filter_args, + .n_arguments=sizeof(get_filter_args)/sizeof(pa_dbus_arg_info), + .receive_cb=equalizer_get_filter} }; -static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX]={ - [PROPERTY_HANDLER_SAMPLERATE]{.property_name="SampleRate",.type="u",.get_cb=get_sample_rate,.set_cb=NULL}, - [PROPERTY_HANDLER_FILTERSAMPLERATE]{.property_name="FilterSampleRate",.type="u",.get_cb=get_filter_rate,.set_cb=NULL}, - [PROPERTY_HANDLER_N_COEFS]{.property_name="NFilterCoefficients",.type="u",.get_cb=get_n_coefs,.set_cb=NULL}, - [PROPERTY_HANDLER_COEFS]{.property_name="FilterCoefficients",.type="ai",.get_cb=get_filter,.set_cb=set_filter} +static pa_dbus_property_handler equalizer_handlers[EQUALIZER_HANDLER_MAX]={ + [EQUALIZER_HANDLER_REVISION]={.property_name="InterfaceRevision",.type="u",.get_cb=equalizer_get_revision,.set_cb=NULL}, + [EQUALIZER_HANDLER_SAMPLERATE]{.property_name="SampleRate",.type="u",.get_cb=equalizer_get_sample_rate,.set_cb=NULL}, + [EQUALIZER_HANDLER_FILTERSAMPLERATE]{.property_name="FilterSampleRate",.type="u",.get_cb=equalizer_get_filter_rate,.set_cb=NULL}, + [EQUALIZER_HANDLER_N_COEFS]{.property_name="NFilterCoefficients",.type="u",.get_cb=equalizer_get_n_coefs,.set_cb=NULL}, }; //static pa_dbus_arg_info new_equalizer_args[] = { { "path","o",NULL} }; @@ -1065,37 +1208,354 @@ static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX]={ // [SIGNAL_NEW_EQUALIZER]={.name="NewEqualizer",.arguments=new_equalizer_args,.n_arguments=1} //}; -#define EXTNAME "org.PulseAudio.Ext.Equalizing1" - -static pa_dbus_interface_info interface_info={ +static pa_dbus_interface_info equalizer_info={ .name=EXTNAME ".Equalizer", - .method_handlers=NULL, - .n_method_handlers=0, - .property_handlers=property_handlers, - .n_property_handlers=PROPERTY_HANDLER_MAX, - .get_all_properties_cb=handle_get_all, + .method_handlers=equalizer_methods, + .n_method_handlers=EQUALIZER_METHOD_MAX, + .property_handlers=equalizer_handlers, + .n_property_handlers=EQUALIZER_HANDLER_MAX, + .get_all_properties_cb=equalizer_get_all, .signals=NULL, .n_signals=0 }; - void dbus_init(struct userdata *u){ u->dbus_protocol=pa_dbus_protocol_get(u->core); u->dbus_path=pa_sprintf_malloc("/org/pulseaudio/core1/sink%d", u->sink->index); - pa_dbus_protocol_add_interface(u->dbus_protocol, u->dbus_path, &interface_info, u); - pa_dbus_protocol_register_extension(u->dbus_protocol, EXTNAME); + pa_dbus_protocol_add_interface(u->dbus_protocol, u->dbus_path, &equalizer_info, u); + pa_idxset *sink_list=pa_shared_get(u->core,SINKLIST); + u->database=pa_shared_get(u->core,EQDB); + if(sink_list==NULL){ + sink_list=pa_idxset_new(&pa_idxset_trivial_hash_func, &pa_idxset_trivial_compare_func); + pa_shared_set(u->core, SINKLIST, sink_list); + char *dbname; + pa_assert_se(dbname = pa_state_path("equalizers", TRUE)); + pa_assert_se(u->database = pa_database_open(dbname, TRUE)); + pa_xfree(dbname); + pa_shared_set(u->core,EQDB,u->database); + pa_dbus_protocol_add_interface(u->dbus_protocol, MANAGER_PATH, &manager_info, u->core); + pa_dbus_protocol_register_extension(u->dbus_protocol, EXTNAME); + } + uint32_t dummy; + pa_idxset_put(sink_list,u,&dummy); } void dbus_done(struct userdata *u){ - pa_dbus_protocol_unregister_extension(u->dbus_protocol, EXTNAME); - pa_dbus_protocol_remove_interface(u->dbus_protocol, u->dbus_path, interface_info.name); - + pa_idxset *sink_list; + uint32_t dummy; + + pa_assert_se(sink_list=pa_shared_get(u->core,SINKLIST)); + pa_idxset_remove_by_data(sink_list,u,&dummy); + if(pa_idxset_size(sink_list)==0){ + pa_dbus_protocol_unregister_extension(u->dbus_protocol, EXTNAME); + pa_dbus_protocol_remove_interface(u->dbus_protocol, MANAGER_PATH, manager_info.name); + pa_shared_remove(u->core, EQDB); + pa_database_close(u->database); + pa_shared_remove(u->core, SINKLIST); + pa_xfree(sink_list); + } + pa_dbus_protocol_remove_interface(u->dbus_protocol, u->dbus_path, equalizer_info.name); pa_xfree(u->dbus_path); pa_dbus_protocol_unref(u->dbus_protocol); } -void get_n_coefs(DBusConnection *conn, DBusMessage *msg, void *_u){ +void manager_handle_remove_profile(DBusConnection *conn, DBusMessage *msg, void *_u) { + DBusError error; + pa_core *c = (pa_core *)_u; + pa_assert(conn); + pa_assert(msg); + pa_assert(c); + dbus_error_init(&error); + char *name; + if(!dbus_message_get_args(msg, &error, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_INVALID)){ + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message); + dbus_error_free(&error); + return; + } + remove_profile(c,name); + pa_dbus_send_empty_reply(conn, msg); +} + +void manager_get_revision(DBusConnection *conn, DBusMessage *msg, void *_u){ + uint32_t rev=1; + pa_dbus_send_basic_value_reply(conn, msg, DBUS_TYPE_UINT32, &rev); +} + +void manager_get_sinks(DBusConnection *conn, DBusMessage *msg, void *_u){ + pa_assert(conn); + pa_assert(msg); + pa_assert(_u); + + pa_core *c = (pa_core *)_u; + pa_idxset *sink_list; + uint32_t dummy; + + pa_assert_se(sink_list = pa_shared_get(c, SINKLIST)); + unsigned n_sinks = (unsigned) pa_idxset_size(sink_list); + char **names = NULL; + void *iter = NULL; + struct userdata *sink_u = NULL; + pa_assert_se(names = pa_xnew0(char *,n_sinks)); + for(uint32_t i = 0; i < n_sinks; ++i){ + sink_u=(struct userdata *) pa_idxset_iterate(sink_list, &iter, &dummy); + names[i] = sink_u->dbus_path; + } + pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, names, n_sinks); + pa_xfree(names); +} + +void manager_get_profiles(DBusConnection *conn, DBusMessage *msg, void *_u){ + pa_assert(conn); + pa_assert(msg); + pa_assert(_u); + + pa_core *core=_u; + DBusMessage *reply = NULL; + DBusMessageIter msg_iter, array_iter; + + pa_assert_se((reply = dbus_message_new_method_return(msg))); + dbus_message_iter_init_append(reply, &msg_iter); + pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "s", &array_iter)); + + char *name; + pa_datum key,next_key; + int done; + pa_database *database; + pa_assert_se(database=pa_shared_get(core, EQDB)); + done = !pa_database_first(database, &key, NULL); + + while(!done){ + done = !pa_database_next(database, &key, &next_key, NULL); + name=pa_xmalloc(key.size + 1); + memcpy(name, key.data, key.size); + name[key.size] = '\0'; + pa_datum_free(&key); + dbus_message_iter_append_basic(&array_iter, DBUS_TYPE_STRING, &name); + pa_xfree(name); + key = next_key; + } + dbus_message_iter_close_container(&msg_iter, &array_iter); + pa_assert_se(dbus_connection_send(conn, reply, NULL)); + dbus_message_unref(reply); +} + +void manager_get_all(DBusConnection *conn, DBusMessage *msg, void *_u){ + pa_assert(conn); + pa_assert(msg); + pa_assert(_u); + + pa_core *u=(pa_core *) _u; + DBusMessage *reply = NULL; + DBusMessageIter msg_iter, dict_iter, sub_iter, array_iter; + pa_assert_se((reply = dbus_message_new_method_return(msg))); + dbus_message_iter_init_append(reply, &msg_iter); + pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter)); + + uint32_t rev=1; + pa_idxset *sink_list; + pa_dbus_append_basic_variant_dict_entry(&dict_iter, manager_handlers[MANAGER_HANDLER_REVISION].property_name, DBUS_TYPE_UINT32, &rev); + + pa_assert_se(sink_list = pa_shared_get(u, SINKLIST)); + unsigned n_sinks = (unsigned) pa_idxset_size(sink_list); + char **names = NULL; + void *iter = NULL; + struct userdata *sink_u = NULL; + pa_assert_se(names = pa_xnew0(char *,n_sinks)); + for(uint32_t i = 0; i < n_sinks; ++i){ + unsigned dummy; + sink_u=(struct userdata *) pa_idxset_iterate(sink_list, &iter, &dummy); + names[i] = sink_u->dbus_path; + } + pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, manager_handlers[MANAGER_HANDLER_EQUALIZED_SINKS].property_name, DBUS_TYPE_OBJECT_PATH, names, n_sinks); + pa_xfree(names); + + pa_database *database; + pa_assert_se(database=pa_shared_get(u, EQDB)); + + pa_datum key,next_key; + char *profile_name; + int done; + done = !pa_database_first(database, &key, NULL); + dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &sub_iter); + dbus_message_iter_append_basic(&sub_iter, DBUS_TYPE_STRING, &manager_handlers[MANAGER_HANDLER_PROFILES].property_name); + + dbus_message_iter_open_container(&sub_iter, DBUS_TYPE_ARRAY, "s", &array_iter); + while(!done){ + done = !pa_database_next(database, &key, &next_key, NULL); + profile_name=pa_xmalloc(key.size + 1); + memcpy(profile_name, key.data, key.size); + profile_name[key.size] = '\0'; + pa_datum_free(&key); + dbus_message_iter_append_basic(&array_iter, DBUS_TYPE_STRING, &profile_name); + pa_xfree(profile_name); + key = next_key; + } + pa_assert_se(dbus_message_iter_close_container(&sub_iter, &array_iter)); + pa_assert_se(dbus_message_iter_close_container(&dict_iter, &sub_iter)); + pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter)); + pa_assert_se(dbus_connection_send(conn, reply, NULL)); + dbus_message_unref(reply); +} + +void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, void *_u) { + struct userdata *u=(struct userdata *) _u; + DBusError error; + + pa_assert(conn); + pa_assert(msg); + pa_assert(u); + float *ys; + uint32_t *xs; + double *_ys; + unsigned x_npoints,y_npoints; + + dbus_error_init(&error); + + if(!dbus_message_get_args(msg, &error, + DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &xs, &x_npoints, + DBUS_TYPE_ARRAY, DBUS_TYPE_DOUBLE, &_ys, &y_npoints, + DBUS_TYPE_INVALID)){ + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message); + dbus_error_free(&error); + return; + } + int points_good=1; + for(size_t i = 0; i < x_npoints; ++i){ + if(xs[i] >= u->fft_size / 2 + 1){ + points_good=0; + break; + } + } + if(!is_monotonic(xs,x_npoints) || !points_good){ + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "xs must be monotonic and 0fft_size / 2); + dbus_error_free(&error); + return; + + } + else if(x_npoints != y_npoints || x_npoints < 2 || x_npoints > u->fft_size / 2 +1 ){ + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "xs and ys must be the same length and 2<=l<=%ld!", u->fft_size / 2 + 1); + dbus_error_free(&error); + return; + } + + ys = pa_xmalloc(x_npoints * sizeof(float)); + for(uint32_t i = 0; i < x_npoints; ++i){ + ys[i] = (float) _ys[i]; + } + + float *H = u->Hs[pa_aupdate_write_begin(u->a_H)]; + interpolate(H, u->fft_size / 2 + 1, xs, ys, x_npoints); + fix_filter(H, u->fft_size); + pa_aupdate_write_swap(u->a_H); + pa_aupdate_write_end(u->a_H); + pa_xfree(ys); + + //Stupid for IO reasons? Add a save signal to dbus instead + //save_state(u); + + pa_dbus_send_empty_reply(conn, msg); +} + +void equalizer_handle_get_filter_points(DBusConnection *conn, DBusMessage *msg, void *_u) { + struct userdata *u=(struct userdata *) _u; + DBusError error; + + pa_assert(conn); + pa_assert(msg); + pa_assert(u); + uint32_t *xs; + double *ys; + unsigned x_npoints; + + dbus_error_init(&error); + + if(!dbus_message_get_args(msg, &error, + DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &xs, &x_npoints, + DBUS_TYPE_INVALID)){ + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message); + dbus_error_free(&error); + return; + } + int points_good=1; + for(size_t i = 0; i < x_npoints; ++i){ + if(xs[i] >= u->fft_size / 2 + 1){ + points_good=0; + break; + } + } + + if(x_npoints > u->fft_size / 2 +1 || !points_good){ + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "xs indices/length must be <= %ld!", u->fft_size / 2 + 1); + dbus_error_free(&error); + return; + } + + ys = pa_xmalloc(x_npoints * sizeof(double)); + float *H = u->Hs[pa_aupdate_read_begin(u->a_H)]; + for(uint32_t i = 0; i < x_npoints; ++i){ + ys[i] = H[xs[i]] * u->fft_size; + } + pa_aupdate_read_end(u->a_H); + + pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_DOUBLE, ys, x_npoints); + pa_xfree(ys); +} + +void equalizer_handle_save_profile(DBusConnection *conn, DBusMessage *msg, void *_u) { + struct userdata *u=(struct userdata *) _u; + DBusError error; + pa_assert(conn); + pa_assert(msg); + pa_assert(u); + dbus_error_init(&error); + char *name; + + if(!dbus_message_get_args(msg, &error, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_INVALID)){ + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message); + dbus_error_free(&error); + return; + } + save_profile(u,name); + pa_dbus_send_empty_reply(conn, msg); +} + +void equalizer_handle_load_profile(DBusConnection *conn, DBusMessage *msg, void *_u) { + struct userdata *u=(struct userdata *) _u; + DBusError error; + + pa_assert(conn); + pa_assert(msg); + pa_assert(u); + dbus_error_init(&error); + char *name; + + if(!dbus_message_get_args(msg, &error, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_INVALID)){ + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message); + dbus_error_free(&error); + return; + } + const char *err_msg=load_profile(u,name); + if(err_msg!=NULL){ + pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, "error loading profile %s: %s", name,err_msg); + dbus_error_free(&error); + return; + } + pa_dbus_send_empty_reply(conn, msg); +} + +void equalizer_get_revision(DBusConnection *conn, DBusMessage *msg, void *_u){ + uint32_t rev=1; + pa_dbus_send_basic_value_reply(conn, msg, DBUS_TYPE_UINT32, &rev); +} + +void equalizer_get_n_coefs(DBusConnection *conn, DBusMessage *msg, void *_u){ pa_assert(conn); pa_assert(msg); pa_assert(_u); @@ -1106,7 +1566,7 @@ void get_n_coefs(DBusConnection *conn, DBusMessage *msg, void *_u){ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &n_coefs); } -void get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *_u){ +void equalizer_get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *_u){ pa_assert(conn); pa_assert(msg); pa_assert(_u); @@ -1115,7 +1575,8 @@ void get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *_u){ uint32_t rate=(uint32_t) u->sink->sample_spec.rate; pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &rate); } -void get_filter_rate(DBusConnection *conn, DBusMessage *msg, void *_u){ + +void equalizer_get_filter_rate(DBusConnection *conn, DBusMessage *msg, void *_u){ pa_assert(conn); pa_assert(msg); pa_assert(_u); @@ -1125,7 +1586,34 @@ void get_filter_rate(DBusConnection *conn, DBusMessage *msg, void *_u){ pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &fft_size); } -void get_filter(DBusConnection *conn, DBusMessage *msg, void *_u){ +void equalizer_get_all(DBusConnection *conn, DBusMessage *msg, void *_u){ + pa_assert(conn); + pa_assert(msg); + pa_assert(_u); + + struct userdata *u=(struct userdata *) _u; + DBusMessage *reply = NULL; + DBusMessageIter msg_iter, dict_iter; + uint32_t rev=1; + uint32_t n_coefs=(uint32_t)(u->fft_size / 2 + 1); + uint32_t rate=(uint32_t) u->sink->sample_spec.rate; + uint32_t fft_size=(uint32_t) u->fft_size; + + pa_assert_se((reply = dbus_message_new_method_return(msg))); + dbus_message_iter_init_append(reply, &msg_iter); + pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter)); + + pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_REVISION].property_name, DBUS_TYPE_UINT32, &rev); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_SAMPLERATE].property_name, DBUS_TYPE_UINT32, &rate); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_FILTERSAMPLERATE].property_name, DBUS_TYPE_UINT32, &fft_size); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_N_COEFS].property_name, DBUS_TYPE_UINT32, &n_coefs); + + pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter)); + pa_assert_se(dbus_connection_send(conn, reply, NULL)); + dbus_message_unref(reply); +} + +void equalizer_get_filter(DBusConnection *conn, DBusMessage *msg, void *_u){ pa_assert(conn); pa_assert(msg); pa_assert(_u); @@ -1144,7 +1632,7 @@ void get_filter(DBusConnection *conn, DBusMessage *msg, void *_u){ pa_xfree(H_); } -void set_filter(DBusConnection *conn, DBusMessage *msg, void *_u){ +void equalizer_set_filter(DBusConnection *conn, DBusMessage *msg, void *_u){ pa_assert(conn); pa_assert(msg); pa_assert(_u); @@ -1152,7 +1640,9 @@ void set_filter(DBusConnection *conn, DBusMessage *msg, void *_u){ struct userdata *u=(struct userdata *)_u; double *H_; unsigned _n_coefs; - pa_dbus_get_fixed_array_set_property_arg(conn, msg, DBUS_TYPE_DOUBLE, &H_, &_n_coefs); + if(pa_dbus_get_fixed_array_set_property_arg(conn, msg, DBUS_TYPE_DOUBLE, &H_, &_n_coefs)){ + return; + } if(_n_coefs!=u->fft_size / 2 + 1){ pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "This filter takes exactly %ld coefficients, you gave %d", u->fft_size / 2 + 1, _n_coefs); return; @@ -1166,44 +1656,7 @@ void set_filter(DBusConnection *conn, DBusMessage *msg, void *_u){ pa_aupdate_write_end(u->a_H); //Stupid for IO reasons? Add a save signal to dbus instead - save_state(u); + //save_state(u); pa_dbus_send_empty_reply(conn, msg); } - -void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *_u){ - pa_assert(conn); - pa_assert(msg); - pa_assert(_u); - - struct userdata *u = (struct userdata *)_u; - DBusMessage *reply = NULL; - DBusMessageIter msg_iter, dict_iter; - - uint32_t rate=(uint32_t) u->sink->sample_spec.rate; - uint32_t fft_size=(uint32_t) u->fft_size; - uint32_t n_coefs=(uint32_t)(u->fft_size / 2 + 1); - double *H_=(double *)pa_xmalloc0(n_coefs*sizeof(double)); - - unsigned H_i=pa_aupdate_read_begin(u->a_H); - float *H=u->Hs[H_i]; - for(size_t i = 0; i < u->fft_size / 2 + 1; ++i){ - H_[i] = H[i]; - } - pa_aupdate_read_end(u->a_H); - - pa_assert_se((reply = dbus_message_new_method_return(msg))); - dbus_message_iter_init_append(reply, &msg_iter); - pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter)); - - pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SAMPLERATE].property_name, DBUS_TYPE_UINT32, &rate); - pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_FILTERSAMPLERATE].property_name, DBUS_TYPE_UINT32, &fft_size); - pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_N_COEFS].property_name, DBUS_TYPE_UINT32, &n_coefs); - pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_COEFS].property_name, DBUS_TYPE_DOUBLE, H_, n_coefs); - - pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter)); - pa_assert_se(dbus_connection_send(conn, reply, NULL)); - dbus_message_unref(reply); - - pa_xfree(H_); -} -- cgit From 857eea062129b4b9e04950a9187ebc47fc84899f Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Fri, 7 Aug 2009 15:08:57 -0700 Subject: module-equalizer-sink: add lennard's fix for piggy-back sinks in pop_cb fixed some tsched issues --- src/modules/module-equalizer-sink.c | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c index 285fddbf..eb9d6ef6 100755 --- a/src/modules/module-equalizer-sink.c +++ b/src/modules/module-equalizer-sink.c @@ -549,6 +549,9 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state)) return -1; + /* Hmm, process any reweind request that might be queued up */ + pa_sink_process_rewind(u->sink, 0); + //pa_log("start output-buffered %ld, input-buffered %ld, requested %ld",buffered_samples,u->samples_gathered,samples_requested); struct timespec start, end; @@ -559,11 +562,11 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk } /* - Set the H filter - */ + * Set the H filter + */ unsigned H_i = pa_aupdate_read_begin(u->a_H); u->H = u->Hs[H_i]; - + do{ pa_memchunk *buffer; size_t input_remaining = u->target_samples-u->samples_gathered; @@ -641,14 +644,14 @@ static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) { if (amount > 0) { //pa_sample_spec *ss = &u->sink->sample_spec; - pa_memblockq_seek(u->rendered_q, - (int64_t) amount, PA_SEEK_RELATIVE, TRUE); + //pa_memblockq_seek(u->rendered_q, - (int64_t) amount, PA_SEEK_RELATIVE, TRUE); pa_log_debug("Resetting equalizer"); u->samples_gathered = 0; } } pa_sink_process_rewind(u->sink, amount); - pa_memblockq_rewind(u->rendered_q, nbytes); + //pa_memblockq_rewind(u->rendered_q, nbytes); } /* Called from I/O thread context */ @@ -677,7 +680,8 @@ static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes) { size_t fs = pa_frame_size(&(u->sink->sample_spec)); //pa_sink_set_max_request_within_thread(u->sink, nbytes); - pa_sink_set_max_request_within_thread(u->sink, u->R*fs); + //pa_sink_set_max_request_within_thread(u->sink, u->R*fs); + pa_sink_set_max_request_within_thread(u->sink, ((nbytes+u->R*fs-1)/(u->R*fs))*(u->R*fs)); } /* Called from I/O thread context */ @@ -692,8 +696,8 @@ static void sink_input_update_sink_latency_range_cb(pa_sink_input *i) { size_t fs = pa_frame_size(&(u->sink->sample_spec)); //pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->latency*fs); - pa_sink_set_latency_range_within_thread(u->sink, u->latency*fs, u->latency*fs ); - //pa_sink_set_latency_range_within_thread(u->sink, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency); + //pa_sink_set_latency_range_within_thread(u->sink, u->latency*fs, u->latency*fs ); + pa_sink_set_latency_range_within_thread(u->sink, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency); } /* Called from I/O thread context */ @@ -924,8 +928,8 @@ int pa__init(pa_module*m) { memset(u->overlap_accum[c], 0, u->overlap_size*sizeof(float)); } u->output_window = alloc((u->fft_size / 2 + 1), sizeof(fftwf_complex)); - u->forward_plan = fftwf_plan_dft_r2c_1d(u->fft_size, u->work_buffer, u->output_window, FFTW_MEASURE); - u->inverse_plan = fftwf_plan_dft_c2r_1d(u->fft_size, u->output_window, u->work_buffer, FFTW_MEASURE); + u->forward_plan = fftwf_plan_dft_r2c_1d(u->fft_size, u->work_buffer, u->output_window, FFTW_ESTIMATE); + u->inverse_plan = fftwf_plan_dft_c2r_1d(u->fft_size, u->output_window, u->work_buffer, FFTW_ESTIMATE); hanning_window(u->W, u->window_size); @@ -965,7 +969,9 @@ int pa__init(pa_module*m) { pa_sink_set_asyncmsgq(u->sink, master->asyncmsgq); pa_sink_set_rtpoll(u->sink, master->rtpoll); - pa_sink_set_max_request(u->sink, u->R*fs); + pa_sink_set_max_request(u->sink, + ((pa_sink_get_max_request(u->sink)+u->R*fs-1)/(u->R*fs))*(u->R*fs) + ); //pa_sink_set_fixed_latency(u->sink, pa_bytes_to_usec(u->R*fs, &ss)); /* Create sink input */ -- cgit From 4231ac444fc1492bab58fe12d439c4479ef1f648 Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Sat, 8 Aug 2009 00:53:40 -0700 Subject: module-equalizer-sink: reverted buffering logic back to how the ladspa sink did it --- src/modules/module-equalizer-sink.c | 178 +++++++++++++++--------------------- 1 file changed, 75 insertions(+), 103 deletions(-) (limited to 'src') diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c index eb9d6ef6..63111810 100755 --- a/src/modules/module-equalizer-sink.c +++ b/src/modules/module-equalizer-sink.c @@ -113,7 +113,7 @@ struct userdata { float *Hs[2];//thread updatable copies pa_aupdate *a_H; pa_memchunk conv_buffer; - pa_memblockq *rendered_q; + pa_memblockq *input_q; pa_dbus_protocol *dbus_protocol; char *dbus_path; @@ -138,7 +138,7 @@ void fix_filter(float *H, size_t fft_size); void interpolate(float *signal, size_t length, uint32_t *xs, float *ys, size_t n_points); static void array_out(const char *name, float *a, size_t length); static int is_monotonic(uint32_t *xs, size_t length); -static void process_samples(struct userdata *u); +static void process_samples(struct userdata *u, pa_memchunk *tchunk); static void input_buffer(struct userdata *u, pa_memchunk *in); static void save_profile(struct userdata *u,char *name); static void save_state(struct userdata *u); @@ -273,9 +273,9 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse if (PA_MSGOBJECT(u->master)->process_msg(PA_MSGOBJECT(u->master), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0) usec = 0; - //usec+=pa_bytes_to_usec(u->latency * fs, ss); - //usec+=pa_bytes_to_usec(u->samples_gathered * fs, ss); - usec += pa_bytes_to_usec(pa_memblockq_get_length(u->rendered_q), ss); + //usec += pa_bytes_to_usec(u->latency * fs, ss); + //usec += pa_bytes_to_usec(u->samples_gathered * fs, ss); + usec += pa_bytes_to_usec(pa_memblockq_get_length(u->input_q), ss); /* Add the latency internal to our sink input on top */ usec += pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->master->sample_spec); *((pa_usec_t*) data) = usec; @@ -310,8 +310,10 @@ static void sink_request_rewind(pa_sink *s) { pa_sink_assert_ref(s); pa_assert_se(u = s->userdata); + //pa_memblockq_drop(u->input_q,pa_memblockq_get_length(u->input_q)); + /* Just hand this one over to the master sink */ - pa_sink_input_request_rewind(u->sink_input, s->thread_info.rewind_nbytes + pa_memblockq_get_length(u->rendered_q), TRUE, FALSE, FALSE); + pa_sink_input_request_rewind(u->sink_input, s->thread_info.rewind_nbytes+pa_memblockq_get_length(u->input_q), TRUE, FALSE, FALSE); } /* Called from I/O thread context */ @@ -327,34 +329,28 @@ static void sink_update_requested_latency(pa_sink *s) { pa_sink_get_requested_latency_within_thread(s)); } -static void process_samples(struct userdata *u){ - pa_memchunk tchunk; +static void process_samples(struct userdata *u, pa_memchunk *tchunk){ size_t fs=pa_frame_size(&(u->sink->sample_spec)); - while(u->samples_gathered >= u->R){ - float *dst; - //pa_log("iter gathered: %ld", u->samples_gathered); - //pa_memblockq_drop(u->rendered_q, tchunk.length); - tchunk.index=0; - tchunk.length=u->R*fs; - tchunk.memblock=pa_memblock_new(u->core->mempool, tchunk.length); - dst=((float*)pa_memblock_acquire(tchunk.memblock)); - for(size_t c=0;c < u->channels; c++) { - dsp_logic( - u->work_buffer, - u->input[c], - u->overlap_accum[c], - u->H, - u->W, - u->output_window, - u - ); - pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst + c, fs, u->work_buffer, sizeof(float), u->R); - } - pa_memblock_release(tchunk.memblock); - pa_memblockq_push(u->rendered_q, &tchunk); - pa_memblock_unref(tchunk.memblock); - u->samples_gathered-=u->R; + pa_assert(u->samples_gathered >= u->R); + float *dst; + tchunk->index=0; + tchunk->length=u->R*fs; + tchunk->memblock=pa_memblock_new(u->core->mempool, tchunk->length); + dst=((float*)pa_memblock_acquire(tchunk->memblock)); + for(size_t c=0;c < u->channels; c++) { + dsp_logic( + u->work_buffer, + u->input[c], + u->overlap_accum[c], + u->H, + u->W, + u->output_window, + u + ); + pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst + c, fs, u->work_buffer, sizeof(float), u->R); } + pa_memblock_release(tchunk->memblock); + u->samples_gathered-=u->R; } typedef float v4sf __attribute__ ((__aligned__(v_size*sizeof(float)))); @@ -539,85 +535,59 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk struct userdata *u; pa_sink_input_assert_ref(i); pa_assert(chunk); - pa_assert_se(u = i->userdata); - pa_assert_se(u->sink); + pa_assert(u = i->userdata); + pa_assert(u->sink); size_t fs = pa_frame_size(&(u->sink->sample_spec)); - //size_t samples_requested = nbytes/fs; - size_t buffered_samples = pa_memblockq_get_length(u->rendered_q)/fs; pa_memchunk tchunk; chunk->memblock = NULL; if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state)) return -1; - /* Hmm, process any reweind request that might be queued up */ + /* Hmm, process any rewind request that might be queued up */ pa_sink_process_rewind(u->sink, 0); //pa_log("start output-buffered %ld, input-buffered %ld, requested %ld",buffered_samples,u->samples_gathered,samples_requested); struct timespec start, end; - if(pa_memblockq_peek(u->rendered_q, &tchunk)==0){ - *chunk = tchunk; - pa_memblockq_drop(u->rendered_q, chunk->length); - return 0; - } - - /* - * Set the H filter - */ - unsigned H_i = pa_aupdate_read_begin(u->a_H); - u->H = u->Hs[H_i]; - do{ - pa_memchunk *buffer; size_t input_remaining = u->target_samples-u->samples_gathered; pa_assert(input_remaining>0); //collect samples - buffer = &u->conv_buffer; - buffer->length = input_remaining*fs; - buffer->index = 0; - pa_memblock_ref(buffer->memblock); - pa_sink_render_into(u->sink, buffer); - - //if(u->sink->thread_info.rewind_requested) - // sink_request_rewind(u->sink); - - //pa_memchunk p; - //buffer = &p; - //pa_sink_render(u->sink, u->R*fs, buffer); - //buffer->length = PA_MIN(input_remaining*fs, buffer->length); - - //debug block - //pa_memblockq_push(u->rendered_q, buffer); - //pa_memblock_unref(buffer->memblock); - //goto END; - + //buffer = &u->conv_buffer; + //buffer->length = input_remaining*fs; + //buffer->index = 0; + //pa_memblock_ref(buffer->memblock); + //pa_sink_render_into(u->sink, buffer); + while(pa_memblockq_peek(u->input_q, &tchunk) < 0){ + pa_sink_render(u->sink, input_remaining*fs, &tchunk); + pa_memblockq_push(u->input_q, &tchunk); + pa_memblock_unref(tchunk.memblock); + } + tchunk.length = PA_MIN(input_remaining*fs, tchunk.length); + pa_memblockq_drop(u->input_q,tchunk.length); //pa_log("asked for %ld input samples, got %ld samples",input_remaining,buffer->length/fs); - //copy new input + /* copy new input */ gettime(start); - input_buffer(u, buffer); + input_buffer(u, &tchunk); gettime(end); //pa_log("Took %0.5f seconds to setup", tdiff(end, start)*1e-9); + pa_memblock_unref(tchunk.memblock); + }while(u->samples_gathered < u->R); - pa_memblock_unref(buffer->memblock); - - pa_assert_se(u->fft_size >= u->window_size); - pa_assert_se(u->R < u->window_size); - //process every complete block on hand - - gettime(start); - process_samples(u); - gettime(end); - //pa_log("Took %0.5f seconds to process", tdiff(end, start)*1e-9); - - buffered_samples = pa_memblockq_get_length(u->rendered_q)/fs; - }while(buffered_samples < u->R); + pa_assert(u->fft_size >= u->window_size); + pa_assert(u->R < u->window_size); + /* set the H filter */ + unsigned H_i = pa_aupdate_read_begin(u->a_H); + u->H = u->Hs[H_i]; + gettime(start); + /* process a block */ + process_samples(u,chunk); + gettime(end); + //pa_log("Took %0.5f seconds to process", tdiff(end, start)*1e-9); + pa_aupdate_read_end(u->a_H); - //deque from rendered_q and output - pa_assert_se(pa_memblockq_peek(u->rendered_q, &tchunk)==0); - *chunk = tchunk; - pa_memblockq_drop(u->rendered_q, chunk->length); - pa_assert_se(chunk->memblock); + pa_assert(chunk->memblock); //pa_log("gave %ld", chunk->length/fs); //pa_log("end pop"); return 0; @@ -638,20 +608,25 @@ static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) { if (u->sink->thread_info.rewind_nbytes > 0) { size_t max_rewrite; - max_rewrite = nbytes + pa_memblockq_get_length(u->rendered_q); + //max_rewrite = nbytes; + max_rewrite = nbytes + pa_memblockq_get_length(u->input_q); + //PA_MIN(pa_memblockq_get_length(u->input_q), nbytes); amount = PA_MIN(u->sink->thread_info.rewind_nbytes, max_rewrite); u->sink->thread_info.rewind_nbytes = 0; if (amount > 0) { //pa_sample_spec *ss = &u->sink->sample_spec; - //pa_memblockq_seek(u->rendered_q, - (int64_t) amount, PA_SEEK_RELATIVE, TRUE); + //invalidate the output q + pa_memblockq_seek(u->input_q, - (int64_t) amount, PA_SEEK_RELATIVE, TRUE); + //pa_memblockq_drop(u->input_q, pa_memblockq_get_length(u->input_q)); + //pa_memblockq_seek(u->input_q, - (int64_t) amount, PA_SEEK_RELATIVE, TRUE); pa_log_debug("Resetting equalizer"); u->samples_gathered = 0; } } pa_sink_process_rewind(u->sink, amount); - //pa_memblockq_rewind(u->rendered_q, nbytes); + pa_memblockq_rewind(u->input_q, nbytes); } /* Called from I/O thread context */ @@ -664,7 +639,7 @@ static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) { if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state)) return; - pa_memblockq_set_maxrewind(u->rendered_q, nbytes); + pa_memblockq_set_maxrewind(u->input_q, nbytes); pa_sink_set_max_rewind_within_thread(u->sink, nbytes); } @@ -694,7 +669,6 @@ static void sink_input_update_sink_latency_range_cb(pa_sink_input *i) { if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state)) return; - size_t fs = pa_frame_size(&(u->sink->sample_spec)); //pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->latency*fs); //pa_sink_set_latency_range_within_thread(u->sink, u->latency*fs, u->latency*fs ); pa_sink_set_latency_range_within_thread(u->sink, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency); @@ -729,14 +703,14 @@ static void sink_input_attach_cb(pa_sink_input *i) { pa_sink_set_rtpoll(u->sink, i->sink->rtpoll); pa_sink_attach_within_thread(u->sink); - size_t fs = pa_frame_size(&(u->sink->sample_spec)); + //size_t fs = pa_frame_size(&(u->sink->sample_spec)); //pa_sink_set_latency_range_within_thread(u->sink, u->latency*fs, u->latency*fs); //pa_sink_set_latency_range_within_thread(u->sink, u->latency*fs, u->master->thread_info.max_latency); //TODO: setting this guy minimizes drop outs but doesn't get rid //of them completely, figure out why - pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->latency*fs); + //pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->latency*fs); //TODO: this guy causes dropouts constantly+rewinds, it's unusable - //pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->master->thread_info.max_latency); + pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->master->thread_info.max_latency); } /* Called from main context */ @@ -906,7 +880,7 @@ int pa__init(pa_module*m) { u->target_samples = 1*u->R; u->samples_gathered = 0; u->max_output = pa_frame_align(pa_mempool_block_size_max(m->core->mempool), &ss)/pa_frame_size(&ss); - u->rendered_q = pa_memblockq_new(0, MEMBLOCKQ_MAXLENGTH, u->target_samples*fs, fs, fs, 0, 0, NULL); + u->input_q = pa_memblockq_new(0, MEMBLOCKQ_MAXLENGTH, 0, fs, 1, 1, 0, NULL); u->a_H = pa_aupdate_new(); u->conv_buffer.memblock = pa_memblock_new(u->core->mempool, u->target_samples*fs); u->latency = u->R; @@ -1062,11 +1036,9 @@ void pa__done(pa_module*m) { pa_sink_input_unref(u->sink_input); } - if(u->conv_buffer.memblock) - pa_memblock_unref(u->conv_buffer.memblock); - - if (u->rendered_q) - pa_memblockq_free(u->rendered_q); + pa_aupdate_free(u->a_H); + pa_memblock_unref(u->conv_buffer.memblock); + pa_memblockq_free(u->input_q); fftwf_destroy_plan(u->inverse_plan); fftwf_destroy_plan(u->forward_plan); -- cgit From 1e3c7d326f4feb2e8dca8626afa58e88bca40fcd Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Sat, 8 Aug 2009 22:20:05 -0700 Subject: module-equalizer-sink: dbus: eliminated some redundant code in dbus handlers/getall switched filter back to being a property signals for changed profiles, added/removed sinks, filter updates and sink reconfigurations fixed timing routines --- src/modules/module-equalizer-sink.c | 330 +++++++++++++++++++++--------------- 1 file changed, 194 insertions(+), 136 deletions(-) (limited to 'src') diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c index 63111810..88c10745 100755 --- a/src/modules/module-equalizer-sink.c +++ b/src/modules/module-equalizer-sink.c @@ -52,6 +52,7 @@ USA. #include #include #include +#include #include #include #include @@ -161,10 +162,14 @@ void dsp_logic( */ #define EXTNAME "org.PulseAudio.Ext.Equalizing1" #define MANAGER_PATH "/org/pulseaudio/equalizing1" +#define MANAGER_IFACE EXTNAME ".Manager" +#define EQUALIZER_IFACE EXTNAME ".Equalizer" static void dbus_init(struct userdata *u); static void dbus_done(struct userdata *u); static void manager_get_revision(DBusConnection *conn, DBusMessage *msg, void *_u); +static void get_sinks(pa_core *u, char ***names, unsigned *n_sinks); static void manager_get_sinks(DBusConnection *conn, DBusMessage *msg, void *_u); +static void get_profiles(pa_core *u, char ***names, unsigned *n_sinks); static void manager_get_profiles(DBusConnection *conn, DBusMessage *msg, void *_u); static void manager_get_all(DBusConnection *conn, DBusMessage *msg, void *_u); static void manager_handle_remove_profile(DBusConnection *conn, DBusMessage *msg, void *_u); @@ -179,6 +184,8 @@ static void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, static void equalizer_handle_get_filter_points(DBusConnection *conn, DBusMessage *msg, void *_u); static void equalizer_handle_save_profile(DBusConnection *conn, DBusMessage *msg, void *_u); static void equalizer_handle_load_profile(DBusConnection *conn, DBusMessage *msg, void *_u); +static void get_filter(struct userdata *u, double **H_); +static void set_filter(struct userdata *u, double **H_); #define v_size 4 #define gettime(x) clock_gettime(CLOCK_MONOTONIC, &x) @@ -471,8 +478,6 @@ void dsp_logic( // d->v = d->v*h->v; //#endif // } -// -// // //inverse fft // fftwf_execute_dft_c2r(u->inverse_plan, output_window, dst); // @@ -511,8 +516,6 @@ void dsp_logic( // ); //} - - void input_buffer(struct userdata *u, pa_memchunk *in){ size_t fs = pa_frame_size(&(u->sink->sample_spec)); size_t samples = in->length/fs; @@ -546,9 +549,9 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk /* Hmm, process any rewind request that might be queued up */ pa_sink_process_rewind(u->sink, 0); - //pa_log("start output-buffered %ld, input-buffered %ld, requested %ld",buffered_samples,u->samples_gathered,samples_requested); + //pa_log_debug("start output-buffered %ld, input-buffered %ld, requested %ld",buffered_samples,u->samples_gathered,samples_requested); struct timespec start, end; - + gettime(start); do{ size_t input_remaining = u->target_samples-u->samples_gathered; pa_assert(input_remaining>0); @@ -565,15 +568,17 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk pa_memblock_unref(tchunk.memblock); } tchunk.length = PA_MIN(input_remaining*fs, tchunk.length); - pa_memblockq_drop(u->input_q,tchunk.length); - //pa_log("asked for %ld input samples, got %ld samples",input_remaining,buffer->length/fs); + pa_memblockq_drop(u->input_q, tchunk.length); + //pa_log_debug("asked for %ld input samples, got %ld samples",input_remaining,buffer->length/fs); /* copy new input */ - gettime(start); + //gettime(start); input_buffer(u, &tchunk); - gettime(end); - //pa_log("Took %0.5f seconds to setup", tdiff(end, start)*1e-9); + //gettime(end); + //pa_log_debug("Took %0.5f seconds to setup", tdiff(end, start)*1e-9); pa_memblock_unref(tchunk.memblock); }while(u->samples_gathered < u->R); + gettime(end); + pa_log_debug("Took %0.6f seconds to get data", tdiff(end, start)*1e-9); pa_assert(u->fft_size >= u->window_size); pa_assert(u->R < u->window_size); @@ -584,12 +589,12 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk /* process a block */ process_samples(u,chunk); gettime(end); - //pa_log("Took %0.5f seconds to process", tdiff(end, start)*1e-9); + pa_log_debug("Took %0.6f seconds to process", tdiff(end, start)*1e-9); pa_aupdate_read_end(u->a_H); pa_assert(chunk->memblock); - //pa_log("gave %ld", chunk->length/fs); - //pa_log("end pop"); + //pa_log_debug("gave %ld", chunk->length/fs); + //pa_log_debug("end pop"); return 0; } @@ -1090,25 +1095,39 @@ static pa_dbus_property_handler manager_handlers[MANAGER_HANDLER_MAX]={ [MANAGER_HANDLER_PROFILES]={.property_name="Profiles",.type="as",.get_cb=manager_get_profiles,.set_cb=NULL} }; +pa_dbus_arg_info sink_args[]={ + {"sink", "o", NULL} +}; + +enum manager_signal_index{ + MANAGER_SIGNAL_SINK_ADDED, + MANAGER_SIGNAL_SINK_REMOVED, + MANAGER_SIGNAL_PROFILES_CHANGED, + MANAGER_SIGNAL_MAX +}; + +static pa_dbus_signal_info manager_signals[MANAGER_SIGNAL_MAX]={ + [MANAGER_SIGNAL_SINK_ADDED]={.name="SinkAdded", .arguments=sink_args, .n_arguments=sizeof(sink_args)/sizeof(pa_dbus_arg_info)}, + [MANAGER_SIGNAL_SINK_REMOVED]={.name="SinkRemoved", .arguments=sink_args, .n_arguments=sizeof(sink_args)/sizeof(pa_dbus_arg_info)}, + [MANAGER_SIGNAL_PROFILES_CHANGED]={.name="ProfilesChanged", .arguments=NULL, .n_arguments=0} +}; + static pa_dbus_interface_info manager_info={ - .name=EXTNAME ".Manager", + .name=MANAGER_IFACE, .method_handlers=manager_methods, .n_method_handlers=MANAGER_METHOD_MAX, .property_handlers=manager_handlers, .n_property_handlers=MANAGER_HANDLER_MAX, .get_all_properties_cb=manager_get_all, - .signals=NULL, - .n_signals=0 + .signals=manager_signals, + .n_signals=MANAGER_SIGNAL_MAX }; - enum equalizer_method_index { EQUALIZER_METHOD_FILTER_POINTS, EQUALIZER_METHOD_SEED_FILTER, EQUALIZER_METHOD_SAVE_PROFILE, EQUALIZER_METHOD_LOAD_PROFILE, - EQUALIZER_METHOD_SET_FILTER, - EQUALIZER_METHOD_GET_FILTER, EQUALIZER_METHOD_MAX }; @@ -1117,6 +1136,7 @@ enum equalizer_handler_index { EQUALIZER_HANDLER_SAMPLERATE, EQUALIZER_HANDLER_FILTERSAMPLERATE, EQUALIZER_HANDLER_N_COEFS, + EQUALIZER_HANDLER_FILTER, EQUALIZER_HANDLER_MAX }; @@ -1134,12 +1154,6 @@ pa_dbus_arg_info save_profile_args[]={ pa_dbus_arg_info load_profile_args[]={ {"name", "s","in"}, }; -pa_dbus_arg_info set_filter_args[]={ - {"coefficients", "ad","in"}, -}; -pa_dbus_arg_info get_filter_args[]={ - {"coefficients", "ad","out"}, -}; static pa_dbus_method_handler equalizer_methods[EQUALIZER_METHOD_MAX]={ [EQUALIZER_METHOD_SEED_FILTER]{ @@ -1162,16 +1176,6 @@ static pa_dbus_method_handler equalizer_methods[EQUALIZER_METHOD_MAX]={ .arguments=load_profile_args, .n_arguments=sizeof(load_profile_args)/sizeof(pa_dbus_arg_info), .receive_cb=equalizer_handle_load_profile}, - [EQUALIZER_METHOD_SET_FILTER]{ - .method_name="SetFilterCoefficients", - .arguments=set_filter_args, - .n_arguments=sizeof(set_filter_args)/sizeof(pa_dbus_arg_info), - .receive_cb=equalizer_set_filter}, - [EQUALIZER_METHOD_GET_FILTER]{ - .method_name="GetFilterCoefficients", - .arguments=get_filter_args, - .n_arguments=sizeof(get_filter_args)/sizeof(pa_dbus_arg_info), - .receive_cb=equalizer_get_filter} }; static pa_dbus_property_handler equalizer_handlers[EQUALIZER_HANDLER_MAX]={ @@ -1179,22 +1183,29 @@ static pa_dbus_property_handler equalizer_handlers[EQUALIZER_HANDLER_MAX]={ [EQUALIZER_HANDLER_SAMPLERATE]{.property_name="SampleRate",.type="u",.get_cb=equalizer_get_sample_rate,.set_cb=NULL}, [EQUALIZER_HANDLER_FILTERSAMPLERATE]{.property_name="FilterSampleRate",.type="u",.get_cb=equalizer_get_filter_rate,.set_cb=NULL}, [EQUALIZER_HANDLER_N_COEFS]{.property_name="NFilterCoefficients",.type="u",.get_cb=equalizer_get_n_coefs,.set_cb=NULL}, + [EQUALIZER_HANDLER_FILTER]{.property_name="Filter",.type="ad",.get_cb=equalizer_get_filter,.set_cb=equalizer_set_filter}, +}; + +enum equalizer_signal_index{ + EQUALIZER_SIGNAL_FILTER_CHANGED, + EQUALIZER_SIGNAL_SINK_RECONFIGURED, + EQUALIZER_SIGNAL_MAX }; -//static pa_dbus_arg_info new_equalizer_args[] = { { "path","o",NULL} }; -//static pa_dbus_signal_info signals[SIGNAL_MAX] = { -// [SIGNAL_NEW_EQUALIZER]={.name="NewEqualizer",.arguments=new_equalizer_args,.n_arguments=1} -//}; +static pa_dbus_signal_info equalizer_signals[EQUALIZER_SIGNAL_MAX]={ + [EQUALIZER_SIGNAL_FILTER_CHANGED]={.name="FilterChanged", .arguments=NULL, .n_arguments=0}, + [EQUALIZER_SIGNAL_SINK_RECONFIGURED]={.name="SinkReconfigured", .arguments=NULL, .n_arguments=0}, +}; static pa_dbus_interface_info equalizer_info={ - .name=EXTNAME ".Equalizer", + .name=EQUALIZER_IFACE, .method_handlers=equalizer_methods, .n_method_handlers=EQUALIZER_METHOD_MAX, .property_handlers=equalizer_handlers, .n_property_handlers=EQUALIZER_HANDLER_MAX, .get_all_properties_cb=equalizer_get_all, - .signals=NULL, - .n_signals=0 + .signals=equalizer_signals, + .n_signals=EQUALIZER_SIGNAL_MAX }; void dbus_init(struct userdata *u){ @@ -1202,8 +1213,8 @@ void dbus_init(struct userdata *u){ u->dbus_path=pa_sprintf_malloc("/org/pulseaudio/core1/sink%d", u->sink->index); pa_dbus_protocol_add_interface(u->dbus_protocol, u->dbus_path, &equalizer_info, u); - pa_idxset *sink_list=pa_shared_get(u->core,SINKLIST); - u->database=pa_shared_get(u->core,EQDB); + pa_idxset *sink_list=pa_shared_get(u->core, SINKLIST); + u->database=pa_shared_get(u->core, EQDB); if(sink_list==NULL){ sink_list=pa_idxset_new(&pa_idxset_trivial_hash_func, &pa_idxset_trivial_compare_func); pa_shared_set(u->core, SINKLIST, sink_list); @@ -1216,13 +1227,25 @@ void dbus_init(struct userdata *u){ pa_dbus_protocol_register_extension(u->dbus_protocol, EXTNAME); } uint32_t dummy; - pa_idxset_put(sink_list,u,&dummy); + pa_idxset_put(sink_list, u, &dummy); + + DBusMessage *signal = NULL; + pa_assert_se((signal = dbus_message_new_signal(MANAGER_PATH, MANAGER_IFACE, manager_signals[MANAGER_SIGNAL_SINK_ADDED].name))); + dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &u->dbus_path, DBUS_TYPE_INVALID); + pa_dbus_protocol_send_signal(u->dbus_protocol, signal); + dbus_message_unref(signal); } void dbus_done(struct userdata *u){ pa_idxset *sink_list; uint32_t dummy; + DBusMessage *signal = NULL; + pa_assert_se((signal = dbus_message_new_signal(MANAGER_PATH, MANAGER_IFACE, manager_signals[MANAGER_SIGNAL_SINK_REMOVED].name))); + dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &u->dbus_path, DBUS_TYPE_INVALID); + pa_dbus_protocol_send_signal(u->dbus_protocol, signal); + dbus_message_unref(signal); + pa_assert_se(sink_list=pa_shared_get(u->core,SINKLIST)); pa_idxset_remove_by_data(sink_list,u,&dummy); if(pa_idxset_size(sink_list)==0){ @@ -1255,6 +1278,13 @@ void manager_handle_remove_profile(DBusConnection *conn, DBusMessage *msg, void } remove_profile(c,name); pa_dbus_send_empty_reply(conn, msg); + + DBusMessage *signal = NULL; + pa_assert_se((signal = dbus_message_new_signal(MANAGER_PATH, MANAGER_IFACE, manager_signals[MANAGER_SIGNAL_PROFILES_CHANGED].name))); + pa_dbus_protocol *dbus_protocol = pa_dbus_protocol_get(c); + pa_dbus_protocol_send_signal(dbus_protocol, signal); + pa_dbus_protocol_unref(dbus_protocol); + dbus_message_unref(signal); } void manager_get_revision(DBusConnection *conn, DBusMessage *msg, void *_u){ @@ -1262,62 +1292,85 @@ void manager_get_revision(DBusConnection *conn, DBusMessage *msg, void *_u){ pa_dbus_send_basic_value_reply(conn, msg, DBUS_TYPE_UINT32, &rev); } -void manager_get_sinks(DBusConnection *conn, DBusMessage *msg, void *_u){ - pa_assert(conn); - pa_assert(msg); - pa_assert(_u); - - pa_core *c = (pa_core *)_u; - pa_idxset *sink_list; - uint32_t dummy; +void get_sinks(pa_core *u, char ***names, unsigned *n_sinks){ + pa_assert(u); + pa_assert(names); + pa_assert(n_sinks); - pa_assert_se(sink_list = pa_shared_get(c, SINKLIST)); - unsigned n_sinks = (unsigned) pa_idxset_size(sink_list); - char **names = NULL; void *iter = NULL; struct userdata *sink_u = NULL; - pa_assert_se(names = pa_xnew0(char *,n_sinks)); - for(uint32_t i = 0; i < n_sinks; ++i){ - sink_u=(struct userdata *) pa_idxset_iterate(sink_list, &iter, &dummy); - names[i] = sink_u->dbus_path; + uint32_t dummy; + pa_idxset *sink_list; + pa_assert_se(sink_list = pa_shared_get(u, SINKLIST)); + *n_sinks = (unsigned) pa_idxset_size(sink_list); + pa_assert_se(*names = pa_xnew0(char *,*n_sinks)); + for(uint32_t i = 0; i < *n_sinks; ++i){ + sink_u = (struct userdata *) pa_idxset_iterate(sink_list, &iter, &dummy); + (*names)[i] = pa_xstrdup(sink_u->dbus_path); } - pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, names, n_sinks); - pa_xfree(names); } -void manager_get_profiles(DBusConnection *conn, DBusMessage *msg, void *_u){ +void manager_get_sinks(DBusConnection *conn, DBusMessage *msg, void *_u){ pa_assert(conn); pa_assert(msg); pa_assert(_u); - pa_core *core=_u; - DBusMessage *reply = NULL; - DBusMessageIter msg_iter, array_iter; - - pa_assert_se((reply = dbus_message_new_method_return(msg))); - dbus_message_iter_init_append(reply, &msg_iter); - pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "s", &array_iter)); + unsigned n; + char **names = NULL; + get_sinks((pa_core *) _u, &names, &n); + pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, names, n); + for(unsigned i = 0; i < n; ++i){ + pa_xfree(names[i]); + } + pa_xfree(names); +} +void get_profiles(pa_core *c, char ***names, unsigned *n){ + pa_assert(c); + pa_assert(names); + pa_assert(n); char *name; - pa_datum key,next_key; - int done; pa_database *database; - pa_assert_se(database=pa_shared_get(core, EQDB)); - done = !pa_database_first(database, &key, NULL); + pa_assert_se(database = pa_shared_get(c, EQDB)); + pa_datum key, next_key; + pa_strlist *head=NULL, *iter; + int done; + done = !pa_database_first(database, &key, NULL); + *n = 0; while(!done){ done = !pa_database_next(database, &key, &next_key, NULL); name=pa_xmalloc(key.size + 1); memcpy(name, key.data, key.size); name[key.size] = '\0'; pa_datum_free(&key); - dbus_message_iter_append_basic(&array_iter, DBUS_TYPE_STRING, &name); + head = pa_strlist_prepend(head, name); pa_xfree(name); key = next_key; + (*n)++; } - dbus_message_iter_close_container(&msg_iter, &array_iter); - pa_assert_se(dbus_connection_send(conn, reply, NULL)); - dbus_message_unref(reply); + (*names) = pa_xnew0(char *, *n); + iter=head; + for(unsigned i = 0; i < *n; ++i){ + (*names)[*n-1-i]=pa_xstrdup(pa_strlist_data(iter)); + iter=pa_strlist_next(iter); + } + pa_strlist_free(head); +} + +void manager_get_profiles(DBusConnection *conn, DBusMessage *msg, void *_u){ + pa_assert(conn); + pa_assert(msg); + pa_assert(_u); + + char **names; + unsigned n; + get_profiles((pa_core *)_u, &names, &n); + pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_STRING, names, n); + for(unsigned i = 0; i < n; ++i){ + pa_xfree(names[i]); + } + pa_xfree(names); } void manager_get_all(DBusConnection *conn, DBusMessage *msg, void *_u){ @@ -1325,54 +1378,31 @@ void manager_get_all(DBusConnection *conn, DBusMessage *msg, void *_u){ pa_assert(msg); pa_assert(_u); - pa_core *u=(pa_core *) _u; + pa_core *c = (pa_core *)_u; + char **names = NULL; + unsigned n; DBusMessage *reply = NULL; - DBusMessageIter msg_iter, dict_iter, sub_iter, array_iter; + DBusMessageIter msg_iter, dict_iter; pa_assert_se((reply = dbus_message_new_method_return(msg))); dbus_message_iter_init_append(reply, &msg_iter); pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter)); uint32_t rev=1; - pa_idxset *sink_list; pa_dbus_append_basic_variant_dict_entry(&dict_iter, manager_handlers[MANAGER_HANDLER_REVISION].property_name, DBUS_TYPE_UINT32, &rev); - pa_assert_se(sink_list = pa_shared_get(u, SINKLIST)); - unsigned n_sinks = (unsigned) pa_idxset_size(sink_list); - char **names = NULL; - void *iter = NULL; - struct userdata *sink_u = NULL; - pa_assert_se(names = pa_xnew0(char *,n_sinks)); - for(uint32_t i = 0; i < n_sinks; ++i){ - unsigned dummy; - sink_u=(struct userdata *) pa_idxset_iterate(sink_list, &iter, &dummy); - names[i] = sink_u->dbus_path; + get_sinks(c, &names, &n); + pa_dbus_append_basic_array_variant_dict_entry(&dict_iter,manager_handlers[MANAGER_HANDLER_EQUALIZED_SINKS].property_name, DBUS_TYPE_OBJECT_PATH, names, n); + for(unsigned i = 0; i < n; ++i){ + pa_xfree(names[i]); } - pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, manager_handlers[MANAGER_HANDLER_EQUALIZED_SINKS].property_name, DBUS_TYPE_OBJECT_PATH, names, n_sinks); pa_xfree(names); - pa_database *database; - pa_assert_se(database=pa_shared_get(u, EQDB)); - - pa_datum key,next_key; - char *profile_name; - int done; - done = !pa_database_first(database, &key, NULL); - dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &sub_iter); - dbus_message_iter_append_basic(&sub_iter, DBUS_TYPE_STRING, &manager_handlers[MANAGER_HANDLER_PROFILES].property_name); - - dbus_message_iter_open_container(&sub_iter, DBUS_TYPE_ARRAY, "s", &array_iter); - while(!done){ - done = !pa_database_next(database, &key, &next_key, NULL); - profile_name=pa_xmalloc(key.size + 1); - memcpy(profile_name, key.data, key.size); - profile_name[key.size] = '\0'; - pa_datum_free(&key); - dbus_message_iter_append_basic(&array_iter, DBUS_TYPE_STRING, &profile_name); - pa_xfree(profile_name); - key = next_key; + get_profiles(c, &names, &n); + pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, manager_handlers[MANAGER_HANDLER_PROFILES].property_name, DBUS_TYPE_STRING, names, n); + for(unsigned i = 0; i < n; ++i){ + pa_xfree(names[i]); } - pa_assert_se(dbus_message_iter_close_container(&sub_iter, &array_iter)); - pa_assert_se(dbus_message_iter_close_container(&dict_iter, &sub_iter)); + pa_xfree(names); pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter)); pa_assert_se(dbus_connection_send(conn, reply, NULL)); dbus_message_unref(reply); @@ -1412,11 +1442,14 @@ void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, void * dbus_error_free(&error); return; - } - else if(x_npoints != y_npoints || x_npoints < 2 || x_npoints > u->fft_size / 2 +1 ){ + }else if(x_npoints != y_npoints || x_npoints < 2 || x_npoints > u->fft_size / 2 +1 ){ pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "xs and ys must be the same length and 2<=l<=%ld!", u->fft_size / 2 + 1); dbus_error_free(&error); return; + }else if(xs[0] != 0 || xs[x_npoints-1] != u->fft_size / 2){ + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "xs[0] must be 0 and xs[-1]=fft_size/2"); + dbus_error_free(&error); + return; } ys = pa_xmalloc(x_npoints * sizeof(float)); @@ -1435,6 +1468,11 @@ void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, void * //save_state(u); pa_dbus_send_empty_reply(conn, msg); + + DBusMessage *signal = NULL; + pa_assert_se((signal = dbus_message_new_signal(u->dbus_path, EQUALIZER_IFACE, equalizer_signals[EQUALIZER_SIGNAL_FILTER_CHANGED].name))); + pa_dbus_protocol_send_signal(u->dbus_protocol, signal); + dbus_message_unref(signal); } void equalizer_handle_get_filter_points(DBusConnection *conn, DBusMessage *msg, void *_u) { @@ -1500,6 +1538,11 @@ void equalizer_handle_save_profile(DBusConnection *conn, DBusMessage *msg, void } save_profile(u,name); pa_dbus_send_empty_reply(conn, msg); + + DBusMessage *signal = NULL; + pa_assert_se((signal = dbus_message_new_signal(MANAGER_PATH, MANAGER_IFACE, manager_signals[MANAGER_SIGNAL_PROFILES_CHANGED].name))); + pa_dbus_protocol_send_signal(u->dbus_protocol, signal); + dbus_message_unref(signal); } void equalizer_handle_load_profile(DBusConnection *conn, DBusMessage *msg, void *_u) { @@ -1564,6 +1607,15 @@ void equalizer_get_filter_rate(DBusConnection *conn, DBusMessage *msg, void *_u) pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &fft_size); } +void get_filter(struct userdata *u, double **H_){ + *H_ = pa_xnew0(double, u->fft_size / 2 + 1); + float *H=u->Hs[pa_aupdate_read_begin(u->a_H)]; + for(size_t i = 0;i < u->fft_size / 2 + 1; ++i){ + (*H_)[i] = H[i] * u->fft_size; + } + pa_aupdate_read_end(u->a_H); +} + void equalizer_get_all(DBusConnection *conn, DBusMessage *msg, void *_u){ pa_assert(conn); pa_assert(msg); @@ -1585,6 +1637,10 @@ void equalizer_get_all(DBusConnection *conn, DBusMessage *msg, void *_u){ pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_SAMPLERATE].property_name, DBUS_TYPE_UINT32, &rate); pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_FILTERSAMPLERATE].property_name, DBUS_TYPE_UINT32, &fft_size); pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_N_COEFS].property_name, DBUS_TYPE_UINT32, &n_coefs); + double *H; + get_filter(u, &H); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_FILTER].property_name, DBUS_TYPE_UINT32, &H); + pa_xfree(H); pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter)); pa_assert_se(dbus_connection_send(conn, reply, NULL)); @@ -1597,44 +1653,46 @@ void equalizer_get_filter(DBusConnection *conn, DBusMessage *msg, void *_u){ pa_assert(_u); struct userdata *u = (struct userdata *)_u; - - unsigned n_coefs = (unsigned) (u->fft_size / 2 + 1); - double *H_ = (double *) pa_xmalloc0(n_coefs * sizeof(double)); - - float *H=u->Hs[pa_aupdate_read_begin(u->a_H)]; - for(size_t i = 0;i < u->fft_size / 2 + 1; ++i){ - H_[i] = H[i] * u->fft_size; - } - pa_aupdate_read_end(u->a_H); + unsigned n_coefs = u->fft_size / 2 + 1; + double *H_; + get_filter(u, &H_); pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_DOUBLE, H_, n_coefs); pa_xfree(H_); } +void set_filter(struct userdata *u, double **H_){ + float *H = u->Hs[pa_aupdate_write_begin(u->a_H)]; + for(size_t i = 0; i < u->fft_size / 2 + 1; ++i){ + H[i] = (float) (*H_)[i]; + } + fix_filter(H, u->fft_size); + pa_aupdate_write_swap(u->a_H); + pa_aupdate_write_end(u->a_H); +} + void equalizer_set_filter(DBusConnection *conn, DBusMessage *msg, void *_u){ pa_assert(conn); pa_assert(msg); pa_assert(_u); struct userdata *u=(struct userdata *)_u; - double *H_; + double *H; unsigned _n_coefs; - if(pa_dbus_get_fixed_array_set_property_arg(conn, msg, DBUS_TYPE_DOUBLE, &H_, &_n_coefs)){ + if(pa_dbus_get_fixed_array_set_property_arg(conn, msg, DBUS_TYPE_DOUBLE, &H, &_n_coefs)){ return; } if(_n_coefs!=u->fft_size / 2 + 1){ pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "This filter takes exactly %ld coefficients, you gave %d", u->fft_size / 2 + 1, _n_coefs); return; } - float *H = u->Hs[pa_aupdate_write_begin(u->a_H)]; - for(size_t i = 0; i < u->fft_size / 2 + 1; ++i){ - H[i] = (float) H_[i]; - } - fix_filter(H, u->fft_size); - pa_aupdate_write_swap(u->a_H); - pa_aupdate_write_end(u->a_H); - + set_filter(u, &H); //Stupid for IO reasons? Add a save signal to dbus instead //save_state(u); pa_dbus_send_empty_reply(conn, msg); + + DBusMessage *signal = NULL; + pa_assert_se((signal = dbus_message_new_signal(u->dbus_path, EQUALIZER_IFACE, equalizer_signals[EQUALIZER_SIGNAL_FILTER_CHANGED].name))); + pa_dbus_protocol_send_signal(u->dbus_protocol, signal); + dbus_message_unref(signal); } -- cgit From 684ad6ddb7ab0e8ba11bf6df620b3cf9c1facf8a Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Mon, 10 Aug 2009 15:33:55 -0700 Subject: module-equalizer-sink: proper fix for pa_xmalloc(0) given that 0 is illegal fix coefficients in case there's no resume state loadprofile now signals filterchanged --- src/modules/module-equalizer-sink.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c index 88c10745..8049a99b 100755 --- a/src/modules/module-equalizer-sink.c +++ b/src/modules/module-equalizer-sink.c @@ -991,9 +991,13 @@ int pa__init(pa_module*m) { dbus_init(u); //default filter to these + float *H=u->Hs[pa_aupdate_write_begin(u->a_H)]; for(size_t i = 0; i < u->fft_size / 2 + 1; ++i){ - u->Hs[1][i]=u->Hs[0][i] = 1.0; + H[i] = 1.0 / sqrtf(2.0f); } + fix_filter(H, u->fft_size); + pa_aupdate_write_swap(u->a_H); + pa_aupdate_write_end(u->a_H); //load old parameters load_state(u); @@ -1303,7 +1307,7 @@ void get_sinks(pa_core *u, char ***names, unsigned *n_sinks){ pa_idxset *sink_list; pa_assert_se(sink_list = pa_shared_get(u, SINKLIST)); *n_sinks = (unsigned) pa_idxset_size(sink_list); - pa_assert_se(*names = pa_xnew0(char *,*n_sinks)); + *names = *n_sinks > 0 ? pa_xnew0(char *,*n_sinks) : NULL; for(uint32_t i = 0; i < *n_sinks; ++i){ sink_u = (struct userdata *) pa_idxset_iterate(sink_list, &iter, &dummy); (*names)[i] = pa_xstrdup(sink_u->dbus_path); @@ -1349,7 +1353,7 @@ void get_profiles(pa_core *c, char ***names, unsigned *n){ key = next_key; (*n)++; } - (*names) = pa_xnew0(char *, *n); + (*names) = *n > 0 ? pa_xnew0(char *, *n) : NULL; iter=head; for(unsigned i = 0; i < *n; ++i){ (*names)[*n-1-i]=pa_xstrdup(pa_strlist_data(iter)); @@ -1569,6 +1573,11 @@ void equalizer_handle_load_profile(DBusConnection *conn, DBusMessage *msg, void return; } pa_dbus_send_empty_reply(conn, msg); + + DBusMessage *signal = NULL; + pa_assert_se((signal = dbus_message_new_signal(u->dbus_path, EQUALIZER_IFACE, equalizer_signals[EQUALIZER_SIGNAL_FILTER_CHANGED].name))); + pa_dbus_protocol_send_signal(u->dbus_protocol, signal); + dbus_message_unref(signal); } void equalizer_get_revision(DBusConnection *conn, DBusMessage *msg, void *_u){ -- cgit From 8c2f9763df96550e06e94d8e985b24a9f6675677 Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Tue, 11 Aug 2009 03:00:28 -0700 Subject: module-equalizer-sink: fix for peek returning a null memblock pa_log -> pa_log_debug for fft size updated module description fixed a comment in dbus error for incorrect x positions --- src/modules/module-equalizer-sink.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c index 8049a99b..270e73e3 100755 --- a/src/modules/module-equalizer-sink.c +++ b/src/modules/module-equalizer-sink.c @@ -2,8 +2,8 @@ This file is part of PulseAudio. This module is based off Lennart Poettering's LADSPA sink and swaps out -LADSPA functionality for a STFT OLA based digital equalizer. All new work -is published under Pulseaudio's original license. +LADSPA functionality for a dbus-aware STFT OLA based digital equalizer. +All new work is published under Pulseaudio's original license. Copyright 2009 Jason Newton Original Author: @@ -562,11 +562,13 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk //buffer->index = 0; //pa_memblock_ref(buffer->memblock); //pa_sink_render_into(u->sink, buffer); - while(pa_memblockq_peek(u->input_q, &tchunk) < 0){ + while(pa_memblockq_peek(u->input_q, &tchunk) < 0 || tchunk.memblock == NULL){ pa_sink_render(u->sink, input_remaining*fs, &tchunk); + pa_assert(tchunk.memblock); pa_memblockq_push(u->input_q, &tchunk); pa_memblock_unref(tchunk.memblock); } + pa_assert(tchunk.memblock); tchunk.length = PA_MIN(input_remaining*fs, tchunk.length); pa_memblockq_drop(u->input_q, tchunk.length); //pa_log_debug("asked for %ld input samples, got %ld samples",input_remaining,buffer->length/fs); @@ -878,7 +880,7 @@ int pa__init(pa_module*m) { u->channels = ss.channels; u->fft_size = pow(2, ceil(log(ss.rate)/log(2))); - pa_log("fft size: %ld", u->fft_size); + pa_log_debug("fft size: %ld", u->fft_size); u->window_size = 15999; u->R = (u->window_size+1)/2; u->overlap_size = u->window_size-u->R; @@ -1442,7 +1444,7 @@ void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, void * } } if(!is_monotonic(xs,x_npoints) || !points_good){ - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "xs must be monotonic and 0fft_size / 2); + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "xs must be monotonic and 0<=x<=%ld", u->fft_size / 2); dbus_error_free(&error); return; -- cgit From 38d608ad5a0a3fa0ff7f93007dde46d8661348c3 Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Sat, 15 Aug 2009 05:22:29 -0700 Subject: module-equalizer-sink: reworked processing so we don't have input->output delay of R samples --- src/modules/module-equalizer-sink.c | 97 +++++++++++++++++++++++++------------ 1 file changed, 67 insertions(+), 30 deletions(-) (limited to 'src') diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c index 270e73e3..5f16cd65 100755 --- a/src/modules/module-equalizer-sink.c +++ b/src/modules/module-equalizer-sink.c @@ -103,7 +103,6 @@ struct userdata { size_t samples_gathered; size_t max_output;//max amount of samples outputable in a single //message - size_t target_samples; float *H;//frequency response filter (magnitude based) float *W;//windowing function (time domain) float *work_buffer, **input, **overlap_accum; @@ -115,6 +114,7 @@ struct userdata { pa_aupdate *a_H; pa_memchunk conv_buffer; pa_memblockq *input_q; + int first_iteration; pa_dbus_protocol *dbus_protocol; char *dbus_path; @@ -135,11 +135,13 @@ static const char* const valid_modargs[] = { static uint64_t time_diff(struct timespec *timeA_p, struct timespec *timeB_p); static void hanning_window(float *W, size_t window_size); -void fix_filter(float *H, size_t fft_size); -void interpolate(float *signal, size_t length, uint32_t *xs, float *ys, size_t n_points); +static void fix_filter(float *H, size_t fft_size); +static void interpolate(float *signal, size_t length, uint32_t *xs, float *ys, size_t n_points); static void array_out(const char *name, float *a, size_t length); static int is_monotonic(uint32_t *xs, size_t length); +static void reset_filter(struct userdata *u); static void process_samples(struct userdata *u, pa_memchunk *tchunk); +static void initialize_buffer(struct userdata *u, pa_memchunk *in); static void input_buffer(struct userdata *u, pa_memchunk *in); static void save_profile(struct userdata *u,char *name); static void save_state(struct userdata *u); @@ -274,13 +276,13 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse case PA_SINK_MESSAGE_GET_LATENCY: { pa_usec_t usec = 0; pa_sample_spec *ss=&u->sink->sample_spec; - //size_t fs=pa_frame_size(&(u->sink->sample_spec)); + size_t fs=pa_frame_size(&(u->sink->sample_spec)); /* Get the latency of the master sink */ if (PA_MSGOBJECT(u->master)->process_msg(PA_MSGOBJECT(u->master), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0) usec = 0; - //usec += pa_bytes_to_usec(u->latency * fs, ss); + usec += pa_bytes_to_usec(u->latency * fs, ss); //usec += pa_bytes_to_usec(u->samples_gathered * fs, ss); usec += pa_bytes_to_usec(pa_memblockq_get_length(u->input_q), ss); /* Add the latency internal to our sink input on top */ @@ -340,10 +342,10 @@ static void process_samples(struct userdata *u, pa_memchunk *tchunk){ size_t fs=pa_frame_size(&(u->sink->sample_spec)); pa_assert(u->samples_gathered >= u->R); float *dst; - tchunk->index=0; - tchunk->length=u->R*fs; - tchunk->memblock=pa_memblock_new(u->core->mempool, tchunk->length); - dst=((float*)pa_memblock_acquire(tchunk->memblock)); + tchunk->index = 0; + tchunk->length = u->R * fs; + tchunk->memblock = pa_memblock_new(u->core->mempool, tchunk->length); + dst = ((float*)pa_memblock_acquire(tchunk->memblock)); for(size_t c=0;c < u->channels; c++) { dsp_logic( u->work_buffer, @@ -354,13 +356,21 @@ static void process_samples(struct userdata *u, pa_memchunk *tchunk){ u->output_window, u ); + if(u->first_iteration){ + /* The windowing function will make the audio ramped in, as a cheap fix we can + * undo the windowing (for non-zero window values) + */ + for(size_t i = 0;i < u->overlap_size; ++i){ + u->work_buffer[i] = u->W[i] <= FLT_EPSILON ? u->W[i] : u->work_buffer[i] / u->W[i]; + } + } pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst + c, fs, u->work_buffer, sizeof(float), u->R); } pa_memblock_release(tchunk->memblock); - u->samples_gathered-=u->R; + u->samples_gathered-= u->R; } -typedef float v4sf __attribute__ ((__aligned__(v_size*sizeof(float)))); +typedef float v4sf __attribute__ ((__aligned__(v_size * sizeof(float)))); typedef union float_vector { float f[v_size]; v4sf v; @@ -516,20 +526,34 @@ void dsp_logic( // ); //} +void initialize_buffer(struct userdata *u, pa_memchunk *in){ + size_t fs = pa_frame_size(&(u->sink->sample_spec)); + size_t samples = in->length/fs; + pa_assert_se(u->samples_gathered + samples == u->window_size); + float *src = (float*) ((uint8_t*) pa_memblock_acquire(in->memblock) + in->index); + for(size_t c = 0; c < u->channels; c++) { + //buffer with an offset after the overlap from previous + //iterations + pa_sample_clamp(PA_SAMPLE_FLOAT32NE, u->input[c]+u->samples_gathered, sizeof(float), src + c, fs, samples); + } + u->samples_gathered+=samples; + pa_memblock_release(in->memblock); +} + void input_buffer(struct userdata *u, pa_memchunk *in){ size_t fs = pa_frame_size(&(u->sink->sample_spec)); size_t samples = in->length/fs; - pa_assert_se(samples <= u->target_samples-u->samples_gathered); + pa_assert_se(samples <= u->window_size - u->samples_gathered); float *src = (float*) ((uint8_t*) pa_memblock_acquire(in->memblock) + in->index); for(size_t c = 0; c < u->channels; c++) { //buffer with an offset after the overlap from previous //iterations pa_assert_se( - u->input[c]+u->overlap_size+u->samples_gathered+samples <= u->input[c]+u->overlap_size+u->target_samples + u->input[c]+u->overlap_size+u->samples_gathered+samples <= u->input[c]+u->window_size ); pa_sample_clamp(PA_SAMPLE_FLOAT32NE, u->input[c]+u->overlap_size+u->samples_gathered, sizeof(float), src + c, fs, samples); } - u->samples_gathered+=samples; + u->samples_gathered += samples; pa_memblock_release(in->memblock); } @@ -553,8 +577,8 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk struct timespec start, end; gettime(start); do{ - size_t input_remaining = u->target_samples-u->samples_gathered; - pa_assert(input_remaining>0); + size_t input_remaining = u->window_size - u->samples_gathered; + pa_assert(input_remaining > 0); //collect samples //buffer = &u->conv_buffer; @@ -563,7 +587,7 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk //pa_memblock_ref(buffer->memblock); //pa_sink_render_into(u->sink, buffer); while(pa_memblockq_peek(u->input_q, &tchunk) < 0 || tchunk.memblock == NULL){ - pa_sink_render(u->sink, input_remaining*fs, &tchunk); + pa_sink_render_full(u->sink, input_remaining*fs, &tchunk); pa_assert(tchunk.memblock); pa_memblockq_push(u->input_q, &tchunk); pa_memblock_unref(tchunk.memblock); @@ -574,11 +598,15 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk //pa_log_debug("asked for %ld input samples, got %ld samples",input_remaining,buffer->length/fs); /* copy new input */ //gettime(start); - input_buffer(u, &tchunk); + if(u->first_iteration){ + initialize_buffer(u, &tchunk); + }else{ + input_buffer(u, &tchunk); + } //gettime(end); //pa_log_debug("Took %0.5f seconds to setup", tdiff(end, start)*1e-9); pa_memblock_unref(tchunk.memblock); - }while(u->samples_gathered < u->R); + }while(u->samples_gathered <= u->window_size); gettime(end); pa_log_debug("Took %0.6f seconds to get data", tdiff(end, start)*1e-9); @@ -597,6 +625,9 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk pa_assert(chunk->memblock); //pa_log_debug("gave %ld", chunk->length/fs); //pa_log_debug("end pop"); + if(u->first_iteration){ + u->first_iteration = 0; + } return 0; } @@ -627,8 +658,8 @@ static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) { pa_memblockq_seek(u->input_q, - (int64_t) amount, PA_SEEK_RELATIVE, TRUE); //pa_memblockq_drop(u->input_q, pa_memblockq_get_length(u->input_q)); //pa_memblockq_seek(u->input_q, - (int64_t) amount, PA_SEEK_RELATIVE, TRUE); - pa_log_debug("Resetting equalizer"); - u->samples_gathered = 0; + pa_log("Resetting filter"); + reset_filter(u); } } @@ -636,6 +667,14 @@ static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) { pa_memblockq_rewind(u->input_q, nbytes); } +void reset_filter(struct userdata *u){ + u->samples_gathered = 0; + for(size_t i = 0;i < u->channels; ++i){ + memset(u->overlap_accum[i], 0, u->overlap_size * sizeof(float)); + } + u->first_iteration = 1; +} + /* Called from I/O thread context */ static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) { struct userdata *u; @@ -882,15 +921,13 @@ int pa__init(pa_module*m) { u->fft_size = pow(2, ceil(log(ss.rate)/log(2))); pa_log_debug("fft size: %ld", u->fft_size); u->window_size = 15999; - u->R = (u->window_size+1)/2; - u->overlap_size = u->window_size-u->R; - u->target_samples = 1*u->R; + u->R = (u->window_size + 1) / 2; + u->overlap_size = u->window_size - u->R; u->samples_gathered = 0; - u->max_output = pa_frame_align(pa_mempool_block_size_max(m->core->mempool), &ss)/pa_frame_size(&ss); + u->max_output = pa_frame_align(pa_mempool_block_size_max(m->core->mempool), &ss) / pa_frame_size(&ss); u->input_q = pa_memblockq_new(0, MEMBLOCKQ_MAXLENGTH, 0, fs, 1, 1, 0, NULL); u->a_H = pa_aupdate_new(); - u->conv_buffer.memblock = pa_memblock_new(u->core->mempool, u->target_samples*fs); - u->latency = u->R; + u->latency = u->window_size - u->R; for(size_t i = 0; i < 2; ++i){ u->Hs[i] = alloc((u->fft_size / 2 + 1), sizeof(float)); } @@ -900,9 +937,9 @@ int pa__init(pa_module*m) { u->input = (float **)pa_xmalloc0(sizeof(float *)*u->channels); u->overlap_accum = (float **)pa_xmalloc0(sizeof(float *)*u->channels); for(size_t c = 0; c < u->channels; ++c){ - u->input[c] = alloc(u->overlap_size+u->target_samples, sizeof(float)); + u->input[c] = alloc(u->window_size, sizeof(float)); pa_assert_se(u->input[c]); - memset(u->input[c], 0, (u->overlap_size+u->target_samples)*sizeof(float)); + memset(u->input[c], 0, (u->window_size)*sizeof(float)); pa_assert_se(u->input[c]); u->overlap_accum[c] = alloc(u->overlap_size, sizeof(float)); pa_assert_se(u->overlap_accum[c]); @@ -913,6 +950,7 @@ int pa__init(pa_module*m) { u->inverse_plan = fftwf_plan_dft_c2r_1d(u->fft_size, u->output_window, u->work_buffer, FFTW_ESTIMATE); hanning_window(u->W, u->window_size); + u->first_iteration = 1; /* Create sink */ pa_sink_new_data_init(&sink_data); @@ -1048,7 +1086,6 @@ void pa__done(pa_module*m) { } pa_aupdate_free(u->a_H); - pa_memblock_unref(u->conv_buffer.memblock); pa_memblockq_free(u->input_q); fftwf_destroy_plan(u->inverse_plan); -- cgit From 0e6711ddd0659d62613e8812b33e36d581875ba0 Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Sat, 15 Aug 2009 06:17:40 -0700 Subject: module-equalizer-sink: merging in upstream changes whitespace fix and fix for first iteration un-windowing --- src/modules/module-equalizer-sink.c | 184 ++++++++++++++++++++---------------- 1 file changed, 100 insertions(+), 84 deletions(-) (limited to 'src') diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c index 5f16cd65..944aa7ab 100755 --- a/src/modules/module-equalizer-sink.c +++ b/src/modules/module-equalizer-sink.c @@ -81,9 +81,8 @@ PA_MODULE_USAGE(_("sink= ")); struct userdata { - pa_core *core; pa_module *module; - pa_sink *sink, *master; + pa_sink *sink; pa_sink_input *sink_input; char *name; @@ -101,7 +100,6 @@ struct userdata { //for twiddling with pulseaudio size_t overlap_size;//window_size-R size_t samples_gathered; - size_t max_output;//max amount of samples outputable in a single //message float *H;//frequency response filter (magnitude based) float *W;//windowing function (time domain) @@ -274,20 +272,26 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse switch (code) { case PA_SINK_MESSAGE_GET_LATENCY: { - pa_usec_t usec = 0; - pa_sample_spec *ss=&u->sink->sample_spec; - size_t fs=pa_frame_size(&(u->sink->sample_spec)); - - /* Get the latency of the master sink */ - if (PA_MSGOBJECT(u->master)->process_msg(PA_MSGOBJECT(u->master), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0) - usec = 0; - - usec += pa_bytes_to_usec(u->latency * fs, ss); - //usec += pa_bytes_to_usec(u->samples_gathered * fs, ss); - usec += pa_bytes_to_usec(pa_memblockq_get_length(u->input_q), ss); - /* Add the latency internal to our sink input on top */ - usec += pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->master->sample_spec); - *((pa_usec_t*) data) = usec; + size_t fs=pa_frame_size(&u->sink->sample_spec); + + /* The sink is _put() before the sink input is, so let's + * make sure we don't access it in that time. Also, the + * sink input is first shut down, the sink second. */ + if (!PA_SINK_IS_LINKED(u->sink->thread_info.state) || + !PA_SINK_INPUT_IS_LINKED(u->sink_input->thread_info.state)) { + *((pa_usec_t*) data) = 0; + return 0; + } + + *((pa_usec_t*) data) = + /* Get the latency of the master sink */ + pa_sink_get_latency_within_thread(u->sink_input->sink) + + + /* Add the latency internal to our sink input on top */ + pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->sink_input->sink->sample_spec) + + pa_bytes_to_usec(u->samples_gathered * fs, &u->sink->sample_spec); + //+ pa_bytes_to_usec(u->latency * fs, ss) + //+ pa_bytes_to_usec(pa_memblockq_get_length(u->input_q), ss); return 0; } } @@ -303,12 +307,11 @@ static int sink_set_state(pa_sink *s, pa_sink_state_t state) { pa_sink_assert_ref(s); pa_assert_se(u = s->userdata); - if (PA_SINK_IS_LINKED(state) && - u->sink_input && - PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(u->sink_input))) - - pa_sink_input_cork(u->sink_input, state == PA_SINK_SUSPENDED); + if (!PA_SINK_IS_LINKED(state) || + !PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(u->sink_input))) + return 0; + pa_sink_input_cork(u->sink_input, state == PA_SINK_SUSPENDED); return 0; } @@ -319,7 +322,9 @@ static void sink_request_rewind(pa_sink *s) { pa_sink_assert_ref(s); pa_assert_se(u = s->userdata); - //pa_memblockq_drop(u->input_q,pa_memblockq_get_length(u->input_q)); + if (!PA_SINK_IS_LINKED(u->sink->thread_info.state) || + !PA_SINK_INPUT_IS_LINKED(u->sink_input->thread_info.state)) + return; /* Just hand this one over to the master sink */ pa_sink_input_request_rewind(u->sink_input, s->thread_info.rewind_nbytes+pa_memblockq_get_length(u->input_q), TRUE, FALSE, FALSE); @@ -332,6 +337,10 @@ static void sink_update_requested_latency(pa_sink *s) { pa_sink_assert_ref(s); pa_assert_se(u = s->userdata); + if (!PA_SINK_IS_LINKED(u->sink->thread_info.state) || + !PA_SINK_INPUT_IS_LINKED(u->sink_input->thread_info.state)) + return; + /* Just hand this one over to the master sink */ pa_sink_input_set_requested_latency_within_thread( u->sink_input, @@ -344,7 +353,7 @@ static void process_samples(struct userdata *u, pa_memchunk *tchunk){ float *dst; tchunk->index = 0; tchunk->length = u->R * fs; - tchunk->memblock = pa_memblock_new(u->core->mempool, tchunk->length); + tchunk->memblock = pa_memblock_new(u->sink->core->mempool, tchunk->length); dst = ((float*)pa_memblock_acquire(tchunk->memblock)); for(size_t c=0;c < u->channels; c++) { dsp_logic( @@ -361,7 +370,7 @@ static void process_samples(struct userdata *u, pa_memchunk *tchunk){ * undo the windowing (for non-zero window values) */ for(size_t i = 0;i < u->overlap_size; ++i){ - u->work_buffer[i] = u->W[i] <= FLT_EPSILON ? u->W[i] : u->work_buffer[i] / u->W[i]; + u->work_buffer[i] = u->W[i] <= FLT_EPSILON ? u->work_buffer[i] : u->work_buffer[i] / u->W[i]; } } pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst + c, fs, u->work_buffer, sizeof(float), u->R); @@ -529,7 +538,7 @@ void dsp_logic( void initialize_buffer(struct userdata *u, pa_memchunk *in){ size_t fs = pa_frame_size(&(u->sink->sample_spec)); size_t samples = in->length/fs; - pa_assert_se(u->samples_gathered + samples == u->window_size); + pa_assert_se(u->samples_gathered + samples <= u->window_size); float *src = (float*) ((uint8_t*) pa_memblock_acquire(in->memblock) + in->index); for(size_t c = 0; c < u->channels; c++) { //buffer with an offset after the overlap from previous @@ -549,7 +558,7 @@ void input_buffer(struct userdata *u, pa_memchunk *in){ //buffer with an offset after the overlap from previous //iterations pa_assert_se( - u->input[c]+u->overlap_size+u->samples_gathered+samples <= u->input[c]+u->window_size + u->input[c]+u->samples_gathered+samples <= u->input[c]+u->window_size ); pa_sample_clamp(PA_SAMPLE_FLOAT32NE, u->input[c]+u->overlap_size+u->samples_gathered, sizeof(float), src + c, fs, samples); } @@ -561,14 +570,12 @@ void input_buffer(struct userdata *u, pa_memchunk *in){ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) { struct userdata *u; pa_sink_input_assert_ref(i); + pa_assert_se(u = i->userdata); pa_assert(chunk); - pa_assert(u = i->userdata); pa_assert(u->sink); size_t fs = pa_frame_size(&(u->sink->sample_spec)); pa_memchunk tchunk; chunk->memblock = NULL; - if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state)) - return -1; /* Hmm, process any rewind request that might be queued up */ pa_sink_process_rewind(u->sink, 0); @@ -587,13 +594,13 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk //pa_memblock_ref(buffer->memblock); //pa_sink_render_into(u->sink, buffer); while(pa_memblockq_peek(u->input_q, &tchunk) < 0 || tchunk.memblock == NULL){ - pa_sink_render_full(u->sink, input_remaining*fs, &tchunk); + pa_sink_render(u->sink, input_remaining*fs, &tchunk); pa_assert(tchunk.memblock); pa_memblockq_push(u->input_q, &tchunk); pa_memblock_unref(tchunk.memblock); } pa_assert(tchunk.memblock); - tchunk.length = PA_MIN(input_remaining*fs, tchunk.length); + tchunk.length = PA_MIN(input_remaining * fs, tchunk.length); pa_memblockq_drop(u->input_q, tchunk.length); //pa_log_debug("asked for %ld input samples, got %ld samples",input_remaining,buffer->length/fs); /* copy new input */ @@ -606,7 +613,7 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk //gettime(end); //pa_log_debug("Took %0.5f seconds to setup", tdiff(end, start)*1e-9); pa_memblock_unref(tchunk.memblock); - }while(u->samples_gathered <= u->window_size); + }while(u->samples_gathered < u->window_size); gettime(end); pa_log_debug("Took %0.6f seconds to get data", tdiff(end, start)*1e-9); @@ -617,7 +624,7 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk u->H = u->Hs[H_i]; gettime(start); /* process a block */ - process_samples(u,chunk); + process_samples(u, chunk); gettime(end); pa_log_debug("Took %0.6f seconds to process", tdiff(end, start)*1e-9); pa_aupdate_read_end(u->a_H); @@ -640,9 +647,6 @@ static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) { pa_sink_input_assert_ref(i); pa_assert_se(u = i->userdata); - if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state)) - return; - if (u->sink->thread_info.rewind_nbytes > 0) { size_t max_rewrite; @@ -682,9 +686,6 @@ static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) { pa_sink_input_assert_ref(i); pa_assert_se(u = i->userdata); - if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state)) - return; - pa_memblockq_set_maxrewind(u->input_q, nbytes); pa_sink_set_max_rewind_within_thread(u->sink, nbytes); } @@ -696,9 +697,6 @@ static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes) { pa_sink_input_assert_ref(i); pa_assert_se(u = i->userdata); - if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state)) - return; - size_t fs = pa_frame_size(&(u->sink->sample_spec)); //pa_sink_set_max_request_within_thread(u->sink, nbytes); //pa_sink_set_max_request_within_thread(u->sink, u->R*fs); @@ -712,26 +710,30 @@ static void sink_input_update_sink_latency_range_cb(pa_sink_input *i) { pa_sink_input_assert_ref(i); pa_assert_se(u = i->userdata); - if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state)) - return; - //pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->latency*fs); //pa_sink_set_latency_range_within_thread(u->sink, u->latency*fs, u->latency*fs ); pa_sink_set_latency_range_within_thread(u->sink, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency); } /* Called from I/O thread context */ -static void sink_input_detach_cb(pa_sink_input *i) { +static void sink_input_update_sink_fixed_latency_cb(pa_sink_input *i) { struct userdata *u; pa_sink_input_assert_ref(i); pa_assert_se(u = i->userdata); - if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state)) - return; + pa_sink_set_fixed_latency_within_thread(u->sink, i->sink->thread_info.fixed_latency); +} + +/* Called from I/O thread context */ +static void sink_input_detach_cb(pa_sink_input *i) { + struct userdata *u; + + pa_sink_input_assert_ref(i); + pa_assert_se(u = i->userdata); pa_sink_detach_within_thread(u->sink); - pa_sink_set_asyncmsgq(u->sink, NULL); + pa_sink_set_rtpoll(u->sink, NULL); } @@ -742,11 +744,10 @@ static void sink_input_attach_cb(pa_sink_input *i) { pa_sink_input_assert_ref(i); pa_assert_se(u = i->userdata); - if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state)) - return; + pa_sink_set_rtpoll(u->sink, i->sink->thread_info.rtpoll); + pa_sink_set_latency_range_within_thread(u->sink, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency); - pa_sink_set_asyncmsgq(u->sink, i->sink->asyncmsgq); - pa_sink_set_rtpoll(u->sink, i->sink->rtpoll); + pa_sink_set_fixed_latency_within_thread(u->sink, i->sink->thread_info.fixed_latency); pa_sink_attach_within_thread(u->sink); //size_t fs = pa_frame_size(&(u->sink->sample_spec)); @@ -756,7 +757,8 @@ static void sink_input_attach_cb(pa_sink_input *i) { //of them completely, figure out why //pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->latency*fs); //TODO: this guy causes dropouts constantly+rewinds, it's unusable - pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->master->thread_info.max_latency); + //pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->master->thread_info.max_latency); + pa_sink_attach_within_thread(u->sink); } /* Called from main context */ @@ -766,14 +768,18 @@ static void sink_input_kill_cb(pa_sink_input *i) { pa_sink_input_assert_ref(i); pa_assert_se(u = i->userdata); - pa_sink_unlink(u->sink); + /* The order here matters! We first kill the sink input, followed + * by the sink. That means the sink callbacks must be protected + * against an unconnected sink input! */ pa_sink_input_unlink(u->sink_input); + pa_sink_unlink(u->sink); - pa_sink_unref(u->sink); - u->sink = NULL; pa_sink_input_unref(u->sink_input); u->sink_input = NULL; + pa_sink_unref(u->sink); + u->sink = NULL; + pa_module_unload_request(u->module, TRUE); } @@ -863,6 +869,16 @@ static pa_bool_t sink_input_may_move_to_cb(pa_sink_input *i, pa_sink *dest) { return u->sink != dest; } +/* Called from main context */ +static void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest) { + struct userdata *u; + + pa_sink_input_assert_ref(i); + pa_assert_se(u = i->userdata); + + pa_sink_set_asyncmsgq(u->sink, dest->asyncmsgq); + pa_sink_update_flags(u->sink, PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY, dest->flags); +} //ensure's memory allocated is a multiple of v_size //and aligned @@ -910,12 +926,8 @@ int pa__init(pa_module*m) { fs = pa_frame_size(&ss); u = pa_xnew0(struct userdata, 1); - u->core = m->core; u->module = m; m->userdata = u; - u->master = master; - u->sink = NULL; - u->sink_input = NULL; u->channels = ss.channels; u->fft_size = pow(2, ceil(log(ss.rate)/log(2))); @@ -924,7 +936,6 @@ int pa__init(pa_module*m) { u->R = (u->window_size + 1) / 2; u->overlap_size = u->window_size - u->R; u->samples_gathered = 0; - u->max_output = pa_frame_align(pa_mempool_block_size_max(m->core->mempool), &ss) / pa_frame_size(&ss); u->input_q = pa_memblockq_new(0, MEMBLOCKQ_MAXLENGTH, 0, fs, 1, 1, 0, NULL); u->a_H = pa_aupdate_new(); u->latency = u->window_size - u->R; @@ -958,11 +969,10 @@ int pa__init(pa_module*m) { sink_data.module = m; if (!(sink_data.name = pa_xstrdup(pa_modargs_get_value(ma, "sink_name", NULL)))) sink_data.name = pa_sprintf_malloc("%s.equalizer", master->name); - sink_data.namereg_fail = FALSE; pa_sink_new_data_set_sample_spec(&sink_data, &ss); pa_sink_new_data_set_channel_map(&sink_data, &map); z = pa_proplist_gets(master->proplist, PA_PROP_DEVICE_DESCRIPTION); - pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "FFT based equalizer"); + pa_proplist_setf(sink_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "FFT based equalizer on %s",z? z: master->name); pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_MASTER_DEVICE, master->name); pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_CLASS, "filter"); @@ -972,7 +982,7 @@ int pa__init(pa_module*m) { goto fail; } - u->sink = pa_sink_new(m->core, &sink_data, PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY); + u->sink = pa_sink_new(m->core, &sink_data, master->flags & (PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY)); pa_sink_new_data_done(&sink_data); if (!u->sink) { @@ -987,7 +997,6 @@ int pa__init(pa_module*m) { u->sink->userdata = u; pa_sink_set_asyncmsgq(u->sink, master->asyncmsgq); - pa_sink_set_rtpoll(u->sink, master->rtpoll); pa_sink_set_max_request(u->sink, ((pa_sink_get_max_request(u->sink)+u->R*fs-1)/(u->R*fs))*(u->R*fs) ); @@ -997,13 +1006,13 @@ int pa__init(pa_module*m) { pa_sink_input_new_data_init(&sink_input_data); sink_input_data.driver = __FILE__; sink_input_data.module = m; - sink_input_data.sink = u->master; + sink_input_data.sink = master; pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_NAME, "Equalized Stream"); pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "filter"); pa_sink_input_new_data_set_sample_spec(&sink_input_data, &ss); pa_sink_input_new_data_set_channel_map(&sink_input_data, &map); - pa_sink_input_new(&u->sink_input, m->core, &sink_input_data, PA_SINK_INPUT_DONT_MOVE); + pa_sink_input_new(&u->sink_input, m->core, &sink_input_data, 0); pa_sink_input_new_data_done(&sink_input_data); if (!u->sink_input) @@ -1014,11 +1023,13 @@ int pa__init(pa_module*m) { u->sink_input->update_max_rewind = sink_input_update_max_rewind_cb; u->sink_input->update_max_request = sink_input_update_max_request_cb; u->sink_input->update_sink_latency_range = sink_input_update_sink_latency_range_cb; + u->sink_input->update_sink_fixed_latency = sink_input_update_sink_fixed_latency_cb; u->sink_input->kill = sink_input_kill_cb; u->sink_input->attach = sink_input_attach_cb; u->sink_input->detach = sink_input_detach_cb; u->sink_input->state_change = sink_input_state_change_cb; u->sink_input->may_move_to = sink_input_may_move_to_cb; + u->sink_input->moving = sink_input_moving_cb; u->sink_input->userdata = u; pa_sink_put(u->sink); @@ -1075,15 +1086,20 @@ void pa__done(pa_module*m) { dbus_done(u); - if (u->sink) { - pa_sink_unlink(u->sink); - pa_sink_unref(u->sink); - } + /* See comments in sink_input_kill_cb() above regarding + * destruction order! */ - if (u->sink_input) { + if (u->sink_input) pa_sink_input_unlink(u->sink_input); + + if (u->sink) + pa_sink_unlink(u->sink); + + if (u->sink_input) pa_sink_input_unref(u->sink_input); - } + + if (u->sink) + pa_sink_unref(u->sink); pa_aupdate_free(u->a_H); pa_memblockq_free(u->input_q); @@ -1252,21 +1268,21 @@ static pa_dbus_interface_info equalizer_info={ }; void dbus_init(struct userdata *u){ - u->dbus_protocol=pa_dbus_protocol_get(u->core); + u->dbus_protocol=pa_dbus_protocol_get(u->sink->core); u->dbus_path=pa_sprintf_malloc("/org/pulseaudio/core1/sink%d", u->sink->index); pa_dbus_protocol_add_interface(u->dbus_protocol, u->dbus_path, &equalizer_info, u); - pa_idxset *sink_list=pa_shared_get(u->core, SINKLIST); - u->database=pa_shared_get(u->core, EQDB); + pa_idxset *sink_list=pa_shared_get(u->sink->core, SINKLIST); + u->database=pa_shared_get(u->sink->core, EQDB); if(sink_list==NULL){ sink_list=pa_idxset_new(&pa_idxset_trivial_hash_func, &pa_idxset_trivial_compare_func); - pa_shared_set(u->core, SINKLIST, sink_list); + pa_shared_set(u->sink->core, SINKLIST, sink_list); char *dbname; pa_assert_se(dbname = pa_state_path("equalizers", TRUE)); pa_assert_se(u->database = pa_database_open(dbname, TRUE)); pa_xfree(dbname); - pa_shared_set(u->core,EQDB,u->database); - pa_dbus_protocol_add_interface(u->dbus_protocol, MANAGER_PATH, &manager_info, u->core); + pa_shared_set(u->sink->core,EQDB,u->database); + pa_dbus_protocol_add_interface(u->dbus_protocol, MANAGER_PATH, &manager_info, u->sink->core); pa_dbus_protocol_register_extension(u->dbus_protocol, EXTNAME); } uint32_t dummy; @@ -1289,14 +1305,14 @@ void dbus_done(struct userdata *u){ pa_dbus_protocol_send_signal(u->dbus_protocol, signal); dbus_message_unref(signal); - pa_assert_se(sink_list=pa_shared_get(u->core,SINKLIST)); + pa_assert_se(sink_list=pa_shared_get(u->sink->core,SINKLIST)); pa_idxset_remove_by_data(sink_list,u,&dummy); if(pa_idxset_size(sink_list)==0){ pa_dbus_protocol_unregister_extension(u->dbus_protocol, EXTNAME); pa_dbus_protocol_remove_interface(u->dbus_protocol, MANAGER_PATH, manager_info.name); - pa_shared_remove(u->core, EQDB); + pa_shared_remove(u->sink->core, EQDB); pa_database_close(u->database); - pa_shared_remove(u->core, SINKLIST); + pa_shared_remove(u->sink->core, SINKLIST); pa_xfree(sink_list); } pa_dbus_protocol_remove_interface(u->dbus_protocol, u->dbus_path, equalizer_info.name); -- cgit From 1c1a812b32897a6aa217bbe381e9e82f3b2728fc Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Sun, 16 Aug 2009 11:38:12 -0700 Subject: module-equalizer-sink exchanged improper usage of memblockq_peek'd memchunk for silence block dropped unneeded function prototypes changed mround to be slightly more elegant __restrict__ -> restrict for c99 removed unneeded pa_aupdate_swap calls first_iteration -> pa_bool_t cleaned up some usage of pa_malloc0 where pa_new0 was more appropriate cruft removal, whitespace fixes and reordering of variables --- src/modules/module-equalizer-sink.c | 556 +++++++++++++++++------------------- 1 file changed, 257 insertions(+), 299 deletions(-) (limited to 'src') diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c index 944aa7ab..b7f61c6a 100755 --- a/src/modules/module-equalizer-sink.c +++ b/src/modules/module-equalizer-sink.c @@ -38,7 +38,9 @@ USA. #include #include +#include +#include #include #include #include @@ -112,7 +114,7 @@ struct userdata { pa_aupdate *a_H; pa_memchunk conv_buffer; pa_memblockq *input_q; - int first_iteration; + pa_bool_t first_iteration; pa_dbus_protocol *dbus_protocol; char *dbus_path; @@ -131,103 +133,43 @@ static const char* const valid_modargs[] = { NULL }; -static uint64_t time_diff(struct timespec *timeA_p, struct timespec *timeB_p); -static void hanning_window(float *W, size_t window_size); -static void fix_filter(float *H, size_t fft_size); -static void interpolate(float *signal, size_t length, uint32_t *xs, float *ys, size_t n_points); -static void array_out(const char *name, float *a, size_t length); -static int is_monotonic(uint32_t *xs, size_t length); -static void reset_filter(struct userdata *u); -static void process_samples(struct userdata *u, pa_memchunk *tchunk); -static void initialize_buffer(struct userdata *u, pa_memchunk *in); -static void input_buffer(struct userdata *u, pa_memchunk *in); -static void save_profile(struct userdata *u,char *name); -static void save_state(struct userdata *u); -static void remove_profile(pa_core *u,char *name); -static const char * load_profile(struct userdata *u,char *name); -static void load_state(struct userdata *u); - -void dsp_logic( - float * __restrict__ dst, - float * __restrict__ src, - float * __restrict__ overlap, - const float * __restrict__ H, - const float * __restrict__ W, - fftwf_complex * __restrict__ output_window, - struct userdata *u); - - -/* - * DBus Routines and Callbacks - */ -#define EXTNAME "org.PulseAudio.Ext.Equalizing1" -#define MANAGER_PATH "/org/pulseaudio/equalizing1" -#define MANAGER_IFACE EXTNAME ".Manager" -#define EQUALIZER_IFACE EXTNAME ".Equalizer" -static void dbus_init(struct userdata *u); -static void dbus_done(struct userdata *u); -static void manager_get_revision(DBusConnection *conn, DBusMessage *msg, void *_u); -static void get_sinks(pa_core *u, char ***names, unsigned *n_sinks); -static void manager_get_sinks(DBusConnection *conn, DBusMessage *msg, void *_u); -static void get_profiles(pa_core *u, char ***names, unsigned *n_sinks); -static void manager_get_profiles(DBusConnection *conn, DBusMessage *msg, void *_u); -static void manager_get_all(DBusConnection *conn, DBusMessage *msg, void *_u); -static void manager_handle_remove_profile(DBusConnection *conn, DBusMessage *msg, void *_u); -static void equalizer_get_revision(DBusConnection *conn, DBusMessage *msg, void *_u); -static void equalizer_get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *_u); -static void equalizer_get_filter_rate(DBusConnection *conn, DBusMessage *msg, void *_u); -static void equalizer_get_n_coefs(DBusConnection *conn, DBusMessage *msg, void *_u); -static void equalizer_get_filter(DBusConnection *conn, DBusMessage *msg, void *_u); -static void equalizer_get_all(DBusConnection *conn, DBusMessage *msg, void *_u); -static void equalizer_set_filter(DBusConnection *conn, DBusMessage *msg, void *_u); -static void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, void *_u); -static void equalizer_handle_get_filter_points(DBusConnection *conn, DBusMessage *msg, void *_u); -static void equalizer_handle_save_profile(DBusConnection *conn, DBusMessage *msg, void *_u); -static void equalizer_handle_load_profile(DBusConnection *conn, DBusMessage *msg, void *_u); -static void get_filter(struct userdata *u, double **H_); -static void set_filter(struct userdata *u, double **H_); #define v_size 4 -#define gettime(x) clock_gettime(CLOCK_MONOTONIC, &x) -#define tdiff(x, y) time_diff(&x, &y) -#define mround(x, y) (x % y == 0 ? x : ( x / y + 1) * y) +#define mround(x, y) ((x + y - 1) / y) * y #define SINKLIST "equalized_sinklist" #define EQDB "equalizer_db" +static void dbus_init(struct userdata *u); +static void dbus_done(struct userdata *u); -uint64_t time_diff(struct timespec *timeA_p, struct timespec *timeB_p) -{ - return ((timeA_p->tv_sec * 1000000000ULL) + timeA_p->tv_nsec) - - ((timeB_p->tv_sec * 1000000000ULL) + timeB_p->tv_nsec); -} - -void hanning_window(float *W, size_t window_size){ +static void hanning_window(float *W, size_t window_size){ //h=.5*(1-cos(2*pi*j/(window_size+1)), COLA for R=(M+1)/2 for(size_t i=0; i < window_size;++i){ W[i] = (float).5*(1-cos(2*M_PI*i/(window_size+1))); } } -void fix_filter(float *H, size_t fft_size){ +static void fix_filter(float *H, size_t fft_size){ //divide out the fft gain for(size_t i = 0; i < fft_size / 2 + 1; ++i){ H[i] /= fft_size; } } -void interpolate(float *signal, size_t length, uint32_t *xs, float *ys, size_t n_points){ +static void interpolate(float *signal, size_t length, uint32_t *xs, float *ys, size_t n_points){ //Note that xs must be monotonically increasing! + float x_range_lower, x_range_upper, c0; pa_assert_se(n_points>=2); pa_assert_se(xs[0] == 0); pa_assert_se(xs[n_points - 1] == length - 1); for(size_t x = 0, x_range_lower_i = 0; x < length-1; ++x){ pa_assert(x_range_lower_i < n_points-1); - float x_range_lower = (float) (xs[x_range_lower_i]); - float x_range_upper = (float) (xs[x_range_lower_i+1]); + x_range_lower = (float) (xs[x_range_lower_i]); + x_range_upper = (float) (xs[x_range_lower_i+1]); pa_assert_se(x_range_lower < x_range_upper); pa_assert_se(x >= x_range_lower); pa_assert_se(x <= x_range_upper); //bilinear-interpolation of coefficients specified - float c0 = (x-x_range_lower)/(x_range_upper-x_range_lower); + c0 = (x-x_range_lower)/(x_range_upper-x_range_lower); pa_assert_se(c0 >= 0&&c0 <= 1.0); signal[x] = ((1.0f - c0) * ys[x_range_lower_i] + c0 * ys[x_range_lower_i + 1]); while(x >= xs[x_range_lower_i + 1]){ @@ -237,22 +179,7 @@ void interpolate(float *signal, size_t length, uint32_t *xs, float *ys, size_t n signal[length-1]=ys[n_points-1]; } -void array_out(const char *name, float *a, size_t length){ - FILE *p=fopen(name, "w"); - if(!p){ - pa_log("opening %s failed!", name); - return; - } - for(size_t i = 0; i < length; ++i){ - fprintf(p, "%e,", a[i]); - //if(i%1000==0){ - // fprintf(p, "\n"); - //} - } - fprintf(p, "\n"); - fclose(p); -} -static int is_monotonic(uint32_t *xs,size_t length){ +static int is_monotonic(const uint32_t *xs,size_t length){ if(length<2){ return 1; } @@ -347,38 +274,6 @@ static void sink_update_requested_latency(pa_sink *s) { pa_sink_get_requested_latency_within_thread(s)); } -static void process_samples(struct userdata *u, pa_memchunk *tchunk){ - size_t fs=pa_frame_size(&(u->sink->sample_spec)); - pa_assert(u->samples_gathered >= u->R); - float *dst; - tchunk->index = 0; - tchunk->length = u->R * fs; - tchunk->memblock = pa_memblock_new(u->sink->core->mempool, tchunk->length); - dst = ((float*)pa_memblock_acquire(tchunk->memblock)); - for(size_t c=0;c < u->channels; c++) { - dsp_logic( - u->work_buffer, - u->input[c], - u->overlap_accum[c], - u->H, - u->W, - u->output_window, - u - ); - if(u->first_iteration){ - /* The windowing function will make the audio ramped in, as a cheap fix we can - * undo the windowing (for non-zero window values) - */ - for(size_t i = 0;i < u->overlap_size; ++i){ - u->work_buffer[i] = u->W[i] <= FLT_EPSILON ? u->work_buffer[i] : u->work_buffer[i] / u->W[i]; - } - } - pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst + c, fs, u->work_buffer, sizeof(float), u->R); - } - pa_memblock_release(tchunk->memblock); - u->samples_gathered-= u->R; -} - typedef float v4sf __attribute__ ((__aligned__(v_size * sizeof(float)))); typedef union float_vector { float f[v_size]; @@ -389,15 +284,15 @@ typedef union float_vector { } float_vector_t; //reference implementation -void dsp_logic( - float * __restrict__ dst,//used as a temp array too, needs to be fft_length! - float * __restrict__ src,/*input data w/ overlap at start, +static void dsp_logic( + float * restrict dst,//used as a temp array too, needs to be fft_length! + float * restrict src,/*input data w/ overlap at start, *automatically cycled in routine */ - float * __restrict__ overlap,//The size of the overlap - const float * __restrict__ H,//The freq. magnitude scalers filter - const float * __restrict__ W,//The windowing function - fftwf_complex * __restrict__ output_window,//The transformed window'd src + float * restrict overlap,//The size of the overlap + const float * restrict H,//The freq. magnitude scalers filter + const float * restrict W,//The windowing function + fftwf_complex * restrict output_window,//The transformed window'd src struct userdata *u){ //use a linear-phase sliding STFT and overlap-add method (for each channel) //zero padd the data @@ -443,14 +338,14 @@ void dsp_logic( ////regardless of sse enabled, the loops in here assume ////16 byte aligned addresses and memory allocations divisible by v_size //void dsp_logic( -// float * __restrict__ dst,//used as a temp array too, needs to be fft_length! -// float * __restrict__ src,/*input data w/ overlap at start, +// float * restrict dst,//used as a temp array too, needs to be fft_length! +// float * restrict src,/*input data w/ overlap at start, // *automatically cycled in routine // */ -// float * __restrict__ overlap,//The size of the overlap -// const float * __restrict__ H,//The freq. magnitude scalers filter -// const float * __restrict__ W,//The windowing function -// fftwf_complex * __restrict__ output_window,//The transformed window'd src +// float * restrict overlap,//The size of the overlap +// const float * restrict H,//The freq. magnitude scalers filter +// const float * restrict W,//The windowing function +// fftwf_complex * restrict output_window,//The transformed window'd src // struct userdata *u){//Collection of constants // // const size_t window_size = mround(u->window_size,v_size); @@ -535,25 +430,57 @@ void dsp_logic( // ); //} -void initialize_buffer(struct userdata *u, pa_memchunk *in){ - size_t fs = pa_frame_size(&(u->sink->sample_spec)); - size_t samples = in->length/fs; - pa_assert_se(u->samples_gathered + samples <= u->window_size); +static void process_samples(struct userdata *u, pa_memchunk *tchunk){ + size_t fs=pa_frame_size(&(u->sink->sample_spec)); + float *dst; + pa_assert(u->samples_gathered >= u->R); + tchunk->index = 0; + tchunk->length = u->R * fs; + tchunk->memblock = pa_memblock_new(u->sink->core->mempool, tchunk->length); + dst = ((float*)pa_memblock_acquire(tchunk->memblock)); + for(size_t c=0;c < u->channels; c++) { + dsp_logic( + u->work_buffer, + u->input[c], + u->overlap_accum[c], + u->H, + u->W, + u->output_window, + u + ); + if(u->first_iteration){ + /* The windowing function will make the audio ramped in, as a cheap fix we can + * undo the windowing (for non-zero window values) + */ + for(size_t i = 0;i < u->overlap_size; ++i){ + u->work_buffer[i] = u->W[i] <= FLT_EPSILON ? u->work_buffer[i] : u->work_buffer[i] / u->W[i]; + } + } + pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst + c, fs, u->work_buffer, sizeof(float), u->R); + } + pa_memblock_release(tchunk->memblock); + u->samples_gathered -= u->R; +} + +static void initialize_buffer(struct userdata *u, pa_memchunk *in){ + size_t fs = pa_frame_size(&u->sink->sample_spec); + size_t samples = in->length / fs; float *src = (float*) ((uint8_t*) pa_memblock_acquire(in->memblock) + in->index); + pa_assert_se(u->samples_gathered + samples <= u->window_size); for(size_t c = 0; c < u->channels; c++) { //buffer with an offset after the overlap from previous //iterations - pa_sample_clamp(PA_SAMPLE_FLOAT32NE, u->input[c]+u->samples_gathered, sizeof(float), src + c, fs, samples); + pa_sample_clamp(PA_SAMPLE_FLOAT32NE, u->input[c] + u->samples_gathered, sizeof(float), src + c, fs, samples); } - u->samples_gathered+=samples; + u->samples_gathered += samples; pa_memblock_release(in->memblock); } -void input_buffer(struct userdata *u, pa_memchunk *in){ +static void input_buffer(struct userdata *u, pa_memchunk *in){ size_t fs = pa_frame_size(&(u->sink->sample_spec)); size_t samples = in->length/fs; - pa_assert_se(samples <= u->window_size - u->samples_gathered); float *src = (float*) ((uint8_t*) pa_memblock_acquire(in->memblock) + in->index); + pa_assert_se(samples <= u->window_size - u->samples_gathered); for(size_t c = 0; c < u->channels; c++) { //buffer with an offset after the overlap from previous //iterations @@ -569,20 +496,21 @@ void input_buffer(struct userdata *u, pa_memchunk *in){ /* Called from I/O thread context */ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) { struct userdata *u; + size_t fs; + struct timeval start, end; + pa_memchunk tchunk; pa_sink_input_assert_ref(i); pa_assert_se(u = i->userdata); pa_assert(chunk); pa_assert(u->sink); - size_t fs = pa_frame_size(&(u->sink->sample_spec)); - pa_memchunk tchunk; + fs = pa_frame_size(&(u->sink->sample_spec)); chunk->memblock = NULL; /* Hmm, process any rewind request that might be queued up */ pa_sink_process_rewind(u->sink, 0); //pa_log_debug("start output-buffered %ld, input-buffered %ld, requested %ld",buffered_samples,u->samples_gathered,samples_requested); - struct timespec start, end; - gettime(start); + pa_timeval_load(&start); do{ size_t input_remaining = u->window_size - u->samples_gathered; pa_assert(input_remaining > 0); @@ -593,7 +521,7 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk //buffer->index = 0; //pa_memblock_ref(buffer->memblock); //pa_sink_render_into(u->sink, buffer); - while(pa_memblockq_peek(u->input_q, &tchunk) < 0 || tchunk.memblock == NULL){ + while(pa_memblockq_peek(u->input_q, &tchunk) < 0){ pa_sink_render(u->sink, input_remaining*fs, &tchunk); pa_assert(tchunk.memblock); pa_memblockq_push(u->input_q, &tchunk); @@ -604,40 +532,47 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk pa_memblockq_drop(u->input_q, tchunk.length); //pa_log_debug("asked for %ld input samples, got %ld samples",input_remaining,buffer->length/fs); /* copy new input */ - //gettime(start); + //pa_timeval_load(start); if(u->first_iteration){ initialize_buffer(u, &tchunk); }else{ input_buffer(u, &tchunk); } - //gettime(end); - //pa_log_debug("Took %0.5f seconds to setup", tdiff(end, start)*1e-9); + //pa_timeval_load(&end); + //pa_log_debug("Took %0.5f seconds to setup", pa_timeval_diff(end, start) / (double) PA_USEC_PER_SEC); pa_memblock_unref(tchunk.memblock); }while(u->samples_gathered < u->window_size); - gettime(end); - pa_log_debug("Took %0.6f seconds to get data", tdiff(end, start)*1e-9); + pa_timeval_load(&end); + pa_log_debug("Took %0.6f seconds to get data", pa_timeval_diff(&end, &start) / (double) PA_USEC_PER_SEC); pa_assert(u->fft_size >= u->window_size); pa_assert(u->R < u->window_size); /* set the H filter */ - unsigned H_i = pa_aupdate_read_begin(u->a_H); - u->H = u->Hs[H_i]; - gettime(start); + u->H = u->Hs[pa_aupdate_read_begin(u->a_H)]; + pa_timeval_load(&start); /* process a block */ process_samples(u, chunk); - gettime(end); - pa_log_debug("Took %0.6f seconds to process", tdiff(end, start)*1e-9); + pa_timeval_load(&end); + pa_log_debug("Took %0.6f seconds to process", pa_timeval_diff(&end, &start) / (double) PA_USEC_PER_SEC); pa_aupdate_read_end(u->a_H); pa_assert(chunk->memblock); //pa_log_debug("gave %ld", chunk->length/fs); //pa_log_debug("end pop"); if(u->first_iteration){ - u->first_iteration = 0; + u->first_iteration = FALSE; } return 0; } +static void reset_filter(struct userdata *u){ + u->samples_gathered = 0; + for(size_t i = 0;i < u->channels; ++i){ + memset(u->overlap_accum[i], 0, u->overlap_size * sizeof(float)); + } + u->first_iteration = TRUE; +} + /* Called from I/O thread context */ static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) { struct userdata *u; @@ -671,14 +606,6 @@ static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) { pa_memblockq_rewind(u->input_q, nbytes); } -void reset_filter(struct userdata *u){ - u->samples_gathered = 0; - for(size_t i = 0;i < u->channels; ++i){ - memset(u->overlap_accum[i], 0, u->overlap_size * sizeof(float)); - } - u->first_iteration = 1; -} - /* Called from I/O thread context */ static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) { struct userdata *u; @@ -693,11 +620,11 @@ static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) { /* Called from I/O thread context */ static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes) { struct userdata *u; - + size_t fs; pa_sink_input_assert_ref(i); pa_assert_se(u = i->userdata); - size_t fs = pa_frame_size(&(u->sink->sample_spec)); + fs = pa_frame_size(&(u->sink->sample_spec)); //pa_sink_set_max_request_within_thread(u->sink, nbytes); //pa_sink_set_max_request_within_thread(u->sink, u->R*fs); pa_sink_set_max_request_within_thread(u->sink, ((nbytes+u->R*fs-1)/(u->R*fs))*(u->R*fs)); @@ -740,7 +667,7 @@ static void sink_input_detach_cb(pa_sink_input *i) { /* Called from I/O thread context */ static void sink_input_attach_cb(pa_sink_input *i) { struct userdata *u; - + size_t fs; pa_sink_input_assert_ref(i); pa_assert_se(u = i->userdata); @@ -748,9 +675,10 @@ static void sink_input_attach_cb(pa_sink_input *i) { pa_sink_set_latency_range_within_thread(u->sink, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency); pa_sink_set_fixed_latency_within_thread(u->sink, i->sink->thread_info.fixed_latency); + fs = pa_frame_size(&(u->sink->sample_spec)); pa_sink_attach_within_thread(u->sink); + pa_sink_set_max_request_within_thread(u->sink, mround(pa_sink_get_max_request(i->sink), u->R*fs)); - //size_t fs = pa_frame_size(&(u->sink->sample_spec)); //pa_sink_set_latency_range_within_thread(u->sink, u->latency*fs, u->latency*fs); //pa_sink_set_latency_range_within_thread(u->sink, u->latency*fs, u->master->thread_info.max_latency); //TODO: setting this guy minimizes drop outs but doesn't get rid @@ -758,7 +686,6 @@ static void sink_input_attach_cb(pa_sink_input *i) { //pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->latency*fs); //TODO: this guy causes dropouts constantly+rewinds, it's unusable //pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->master->thread_info.max_latency); - pa_sink_attach_within_thread(u->sink); } /* Called from main context */ @@ -799,15 +726,15 @@ static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t s } } -void save_profile(struct userdata *u, char *name){ +static void save_profile(struct userdata *u, char *name){ float *H_n = pa_xmalloc((u->fft_size / 2 + 1) * sizeof(float)); const float *H = u->Hs[pa_aupdate_read_begin(u->a_H)]; + pa_datum key, data; for(size_t i = 0 ; i <= u->fft_size / 2 + 1; ++i){ //H_n[i] = H[i] * u->fft_size; H_n[i] = H[i]; } pa_aupdate_read_end(u->a_H); - pa_datum key, data; key.data=name; key.size = strlen(key.data); data.data = H_n; @@ -816,23 +743,23 @@ void save_profile(struct userdata *u, char *name){ pa_database_sync(u->database); } -void save_state(struct userdata *u){ +static void save_state(struct userdata *u){ char *state_name = pa_sprintf_malloc("%s-previous-state", u->name); save_profile(u, state_name); pa_xfree(state_name); } -void remove_profile(pa_core *c,char *name){ +static void remove_profile(pa_core *c, char *name){ pa_datum key; + pa_database *database; key.data = name; key.size = strlen(key.data); - pa_database *database; - pa_assert_se(database = pa_shared_get(c,EQDB)); - pa_database_unset(database,&key); + pa_assert_se(database = pa_shared_get(c, EQDB)); + pa_database_unset(database, &key); pa_database_sync(database); } -const char* load_profile(struct userdata *u,char *name){ +static const char* load_profile(struct userdata *u, char *name){ pa_datum key,value; key.data = name; key.size = strlen(key.data); @@ -840,7 +767,6 @@ const char* load_profile(struct userdata *u,char *name){ if(value.size == (u->fft_size / 2 + 1) * sizeof(float)){ float *H=u->Hs[pa_aupdate_write_begin(u->a_H)]; memcpy(H, value.data, value.size); - pa_aupdate_write_swap(u->a_H); pa_aupdate_write_end(u->a_H); }else{ return "incompatible size"; @@ -852,13 +778,13 @@ const char* load_profile(struct userdata *u,char *name){ return NULL; //fix_filter(u->H, u->fft_size); } -void load_state(struct userdata *u){ + +static void load_state(struct userdata *u){ char *state_name=pa_sprintf_malloc("%s-previous-state", u->name); load_profile(u,state_name); pa_xfree(state_name); } - /* Called from main context */ static pa_bool_t sink_input_may_move_to_cb(pa_sink_input *i, pa_sink *dest) { struct userdata *u; @@ -884,10 +810,11 @@ static void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest) { //and aligned static void * alloc(size_t x,size_t s){ size_t f = mround(x*s, sizeof(float)*v_size); + float *t; pa_assert_se(f >= x*s); //printf("requested %ld floats=%ld bytes, rem=%ld\n", x, x*sizeof(float), x*sizeof(float)%16); //printf("giving %ld floats=%ld bytes, rem=%ld\n", f, f*sizeof(float), f*sizeof(float)%16); - float *t = fftwf_malloc(f); + t = fftwf_malloc(f); memset(t, 0, f); return t; } @@ -903,6 +830,7 @@ int pa__init(pa_module*m) { pa_sink_new_data sink_data; pa_bool_t *use_default = NULL; size_t fs; + float *H; pa_assert(m); @@ -936,7 +864,6 @@ int pa__init(pa_module*m) { u->R = (u->window_size + 1) / 2; u->overlap_size = u->window_size - u->R; u->samples_gathered = 0; - u->input_q = pa_memblockq_new(0, MEMBLOCKQ_MAXLENGTH, 0, fs, 1, 1, 0, NULL); u->a_H = pa_aupdate_new(); u->latency = u->window_size - u->R; for(size_t i = 0; i < 2; ++i){ @@ -945,8 +872,8 @@ int pa__init(pa_module*m) { u->W = alloc(u->window_size, sizeof(float)); u->work_buffer = alloc(u->fft_size, sizeof(float)); memset(u->work_buffer, 0, u->fft_size*sizeof(float)); - u->input = (float **)pa_xmalloc0(sizeof(float *)*u->channels); - u->overlap_accum = (float **)pa_xmalloc0(sizeof(float *)*u->channels); + u->input = pa_xnew0(float *, u->channels); + u->overlap_accum = pa_xnew0(float *, u->channels); for(size_t c = 0; c < u->channels; ++c){ u->input[c] = alloc(u->window_size, sizeof(float)); pa_assert_se(u->input[c]); @@ -961,7 +888,7 @@ int pa__init(pa_module*m) { u->inverse_plan = fftwf_plan_dft_c2r_1d(u->fft_size, u->output_window, u->work_buffer, FFTW_ESTIMATE); hanning_window(u->W, u->window_size); - u->first_iteration = 1; + u->first_iteration = TRUE; /* Create sink */ pa_sink_new_data_init(&sink_data); @@ -995,11 +922,9 @@ int pa__init(pa_module*m) { u->sink->update_requested_latency = sink_update_requested_latency; u->sink->request_rewind = sink_request_rewind; u->sink->userdata = u; + u->input_q = pa_memblockq_new(0, MEMBLOCKQ_MAXLENGTH, 0, fs, 1, 1, 0, &u->sink->silence); pa_sink_set_asyncmsgq(u->sink, master->asyncmsgq); - pa_sink_set_max_request(u->sink, - ((pa_sink_get_max_request(u->sink)+u->R*fs-1)/(u->R*fs))*(u->R*fs) - ); //pa_sink_set_fixed_latency(u->sink, pa_bytes_to_usec(u->R*fs, &ss)); /* Create sink input */ @@ -1042,12 +967,11 @@ int pa__init(pa_module*m) { dbus_init(u); //default filter to these - float *H=u->Hs[pa_aupdate_write_begin(u->a_H)]; + H=u->Hs[pa_aupdate_write_begin(u->a_H)]; for(size_t i = 0; i < u->fft_size / 2 + 1; ++i){ H[i] = 1.0 / sqrtf(2.0f); } fix_filter(H, u->fft_size); - pa_aupdate_write_swap(u->a_H); pa_aupdate_write_end(u->a_H); //load old parameters load_state(u); @@ -1124,6 +1048,33 @@ void pa__done(pa_module*m) { pa_xfree(u); } +/* + * DBus Routines and Callbacks + */ +#define EXTNAME "org.PulseAudio.Ext.Equalizing1" +#define MANAGER_PATH "/org/pulseaudio/equalizing1" +#define MANAGER_IFACE EXTNAME ".Manager" +#define EQUALIZER_IFACE EXTNAME ".Equalizer" +static void manager_get_revision(DBusConnection *conn, DBusMessage *msg, void *_u); +static void get_sinks(pa_core *u, char ***names, unsigned *n_sinks); +static void manager_get_sinks(DBusConnection *conn, DBusMessage *msg, void *_u); +static void get_profiles(pa_core *u, char ***names, unsigned *n_sinks); +static void manager_get_profiles(DBusConnection *conn, DBusMessage *msg, void *_u); +static void manager_get_all(DBusConnection *conn, DBusMessage *msg, void *_u); +static void manager_handle_remove_profile(DBusConnection *conn, DBusMessage *msg, void *_u); +static void equalizer_get_revision(DBusConnection *conn, DBusMessage *msg, void *_u); +static void equalizer_get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *_u); +static void equalizer_get_filter_rate(DBusConnection *conn, DBusMessage *msg, void *_u); +static void equalizer_get_n_coefs(DBusConnection *conn, DBusMessage *msg, void *_u); +static void equalizer_get_filter(DBusConnection *conn, DBusMessage *msg, void *_u); +static void equalizer_get_all(DBusConnection *conn, DBusMessage *msg, void *_u); +static void equalizer_set_filter(DBusConnection *conn, DBusMessage *msg, void *_u); +static void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, void *_u); +static void equalizer_handle_get_filter_points(DBusConnection *conn, DBusMessage *msg, void *_u); +static void equalizer_handle_save_profile(DBusConnection *conn, DBusMessage *msg, void *_u); +static void equalizer_handle_load_profile(DBusConnection *conn, DBusMessage *msg, void *_u); +static void get_filter(struct userdata *u, double **H_); +static void set_filter(struct userdata *u, double **H_); enum manager_method_index { MANAGER_METHOD_REMOVE_PROFILE, MANAGER_METHOD_MAX @@ -1267,17 +1218,20 @@ static pa_dbus_interface_info equalizer_info={ .n_signals=EQUALIZER_SIGNAL_MAX }; -void dbus_init(struct userdata *u){ +static void dbus_init(struct userdata *u){ + uint32_t dummy; + DBusMessage *signal = NULL; + pa_idxset *sink_list = NULL; u->dbus_protocol=pa_dbus_protocol_get(u->sink->core); u->dbus_path=pa_sprintf_malloc("/org/pulseaudio/core1/sink%d", u->sink->index); pa_dbus_protocol_add_interface(u->dbus_protocol, u->dbus_path, &equalizer_info, u); - pa_idxset *sink_list=pa_shared_get(u->sink->core, SINKLIST); + sink_list = pa_shared_get(u->sink->core, SINKLIST); u->database=pa_shared_get(u->sink->core, EQDB); if(sink_list==NULL){ + char *dbname; sink_list=pa_idxset_new(&pa_idxset_trivial_hash_func, &pa_idxset_trivial_compare_func); pa_shared_set(u->sink->core, SINKLIST, sink_list); - char *dbname; pa_assert_se(dbname = pa_state_path("equalizers", TRUE)); pa_assert_se(u->database = pa_database_open(dbname, TRUE)); pa_xfree(dbname); @@ -1285,17 +1239,15 @@ void dbus_init(struct userdata *u){ pa_dbus_protocol_add_interface(u->dbus_protocol, MANAGER_PATH, &manager_info, u->sink->core); pa_dbus_protocol_register_extension(u->dbus_protocol, EXTNAME); } - uint32_t dummy; pa_idxset_put(sink_list, u, &dummy); - DBusMessage *signal = NULL; pa_assert_se((signal = dbus_message_new_signal(MANAGER_PATH, MANAGER_IFACE, manager_signals[MANAGER_SIGNAL_SINK_ADDED].name))); dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &u->dbus_path, DBUS_TYPE_INVALID); pa_dbus_protocol_send_signal(u->dbus_protocol, signal); dbus_message_unref(signal); } -void dbus_done(struct userdata *u){ +static void dbus_done(struct userdata *u){ pa_idxset *sink_list; uint32_t dummy; @@ -1320,14 +1272,16 @@ void dbus_done(struct userdata *u){ pa_dbus_protocol_unref(u->dbus_protocol); } -void manager_handle_remove_profile(DBusConnection *conn, DBusMessage *msg, void *_u) { +static void manager_handle_remove_profile(DBusConnection *conn, DBusMessage *msg, void *_u) { DBusError error; pa_core *c = (pa_core *)_u; + DBusMessage *signal = NULL; + pa_dbus_protocol *dbus_protocol; + char *name; pa_assert(conn); pa_assert(msg); pa_assert(c); dbus_error_init(&error); - char *name; if(!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID)){ @@ -1338,28 +1292,27 @@ void manager_handle_remove_profile(DBusConnection *conn, DBusMessage *msg, void remove_profile(c,name); pa_dbus_send_empty_reply(conn, msg); - DBusMessage *signal = NULL; pa_assert_se((signal = dbus_message_new_signal(MANAGER_PATH, MANAGER_IFACE, manager_signals[MANAGER_SIGNAL_PROFILES_CHANGED].name))); - pa_dbus_protocol *dbus_protocol = pa_dbus_protocol_get(c); + dbus_protocol = pa_dbus_protocol_get(c); pa_dbus_protocol_send_signal(dbus_protocol, signal); pa_dbus_protocol_unref(dbus_protocol); dbus_message_unref(signal); } -void manager_get_revision(DBusConnection *conn, DBusMessage *msg, void *_u){ +static void manager_get_revision(DBusConnection *conn, DBusMessage *msg, void *_u){ uint32_t rev=1; pa_dbus_send_basic_value_reply(conn, msg, DBUS_TYPE_UINT32, &rev); } -void get_sinks(pa_core *u, char ***names, unsigned *n_sinks){ - pa_assert(u); - pa_assert(names); - pa_assert(n_sinks); - +static void get_sinks(pa_core *u, char ***names, unsigned *n_sinks){ void *iter = NULL; struct userdata *sink_u = NULL; uint32_t dummy; pa_idxset *sink_list; + pa_assert(u); + pa_assert(names); + pa_assert(n_sinks); + pa_assert_se(sink_list = pa_shared_get(u, SINKLIST)); *n_sinks = (unsigned) pa_idxset_size(sink_list); *names = *n_sinks > 0 ? pa_xnew0(char *,*n_sinks) : NULL; @@ -1369,13 +1322,13 @@ void get_sinks(pa_core *u, char ***names, unsigned *n_sinks){ } } -void manager_get_sinks(DBusConnection *conn, DBusMessage *msg, void *_u){ +static void manager_get_sinks(DBusConnection *conn, DBusMessage *msg, void *_u){ + unsigned n; + char **names = NULL; pa_assert(conn); pa_assert(msg); pa_assert(_u); - unsigned n; - char **names = NULL; get_sinks((pa_core *) _u, &names, &n); pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, names, n); for(unsigned i = 0; i < n; ++i){ @@ -1384,17 +1337,17 @@ void manager_get_sinks(DBusConnection *conn, DBusMessage *msg, void *_u){ pa_xfree(names); } -void get_profiles(pa_core *c, char ***names, unsigned *n){ - pa_assert(c); - pa_assert(names); - pa_assert(n); +static void get_profiles(pa_core *c, char ***names, unsigned *n){ char *name; pa_database *database; - pa_assert_se(database = pa_shared_get(c, EQDB)); pa_datum key, next_key; pa_strlist *head=NULL, *iter; + pa_bool_t done; + pa_assert_se(database = pa_shared_get(c, EQDB)); - int done; + pa_assert(c); + pa_assert(names); + pa_assert(n); done = !pa_database_first(database, &key, NULL); *n = 0; while(!done){ @@ -1411,19 +1364,19 @@ void get_profiles(pa_core *c, char ***names, unsigned *n){ (*names) = *n > 0 ? pa_xnew0(char *, *n) : NULL; iter=head; for(unsigned i = 0; i < *n; ++i){ - (*names)[*n-1-i]=pa_xstrdup(pa_strlist_data(iter)); - iter=pa_strlist_next(iter); + (*names)[*n - 1 - i] = pa_xstrdup(pa_strlist_data(iter)); + iter = pa_strlist_next(iter); } pa_strlist_free(head); } -void manager_get_profiles(DBusConnection *conn, DBusMessage *msg, void *_u){ +static void manager_get_profiles(DBusConnection *conn, DBusMessage *msg, void *_u){ + char **names; + unsigned n; pa_assert(conn); pa_assert(msg); pa_assert(_u); - char **names; - unsigned n; get_profiles((pa_core *)_u, &names, &n); pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_STRING, names, n); for(unsigned i = 0; i < n; ++i){ @@ -1432,21 +1385,22 @@ void manager_get_profiles(DBusConnection *conn, DBusMessage *msg, void *_u){ pa_xfree(names); } -void manager_get_all(DBusConnection *conn, DBusMessage *msg, void *_u){ - pa_assert(conn); - pa_assert(msg); - pa_assert(_u); - - pa_core *c = (pa_core *)_u; +static void manager_get_all(DBusConnection *conn, DBusMessage *msg, void *_u){ + pa_core *c; char **names = NULL; unsigned n; DBusMessage *reply = NULL; DBusMessageIter msg_iter, dict_iter; + uint32_t rev; + pa_assert(conn); + pa_assert(msg); + pa_assert_se(c = _u); + pa_assert_se((reply = dbus_message_new_method_return(msg))); dbus_message_iter_init_append(reply, &msg_iter); pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter)); - uint32_t rev=1; + rev = 1; pa_dbus_append_basic_variant_dict_entry(&dict_iter, manager_handlers[MANAGER_HANDLER_REVISION].property_name, DBUS_TYPE_UINT32, &rev); get_sinks(c, &names, &n); @@ -1467,17 +1421,19 @@ void manager_get_all(DBusConnection *conn, DBusMessage *msg, void *_u){ dbus_message_unref(reply); } -void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, void *_u) { +static void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, void *_u) { struct userdata *u=(struct userdata *) _u; DBusError error; - - pa_assert(conn); - pa_assert(msg); - pa_assert(u); + DBusMessage *signal = NULL; float *ys; uint32_t *xs; double *_ys; - unsigned x_npoints,y_npoints; + unsigned x_npoints, y_npoints; + float *H; + pa_bool_t points_good = TRUE; + pa_assert(conn); + pa_assert(msg); + pa_assert(u); dbus_error_init(&error); @@ -1489,14 +1445,13 @@ void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, void * dbus_error_free(&error); return; } - int points_good=1; for(size_t i = 0; i < x_npoints; ++i){ if(xs[i] >= u->fft_size / 2 + 1){ - points_good=0; + points_good = FALSE; break; } } - if(!is_monotonic(xs,x_npoints) || !points_good){ + if(!is_monotonic(xs, x_npoints) || !points_good){ pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "xs must be monotonic and 0<=x<=%ld", u->fft_size / 2); dbus_error_free(&error); return; @@ -1505,7 +1460,7 @@ void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, void * pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "xs and ys must be the same length and 2<=l<=%ld!", u->fft_size / 2 + 1); dbus_error_free(&error); return; - }else if(xs[0] != 0 || xs[x_npoints-1] != u->fft_size / 2){ + }else if(xs[0] != 0 || xs[x_npoints - 1] != u->fft_size / 2){ pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "xs[0] must be 0 and xs[-1]=fft_size/2"); dbus_error_free(&error); return; @@ -1516,10 +1471,9 @@ void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, void * ys[i] = (float) _ys[i]; } - float *H = u->Hs[pa_aupdate_write_begin(u->a_H)]; + H = u->Hs[pa_aupdate_write_begin(u->a_H)]; interpolate(H, u->fft_size / 2 + 1, xs, ys, x_npoints); fix_filter(H, u->fft_size); - pa_aupdate_write_swap(u->a_H); pa_aupdate_write_end(u->a_H); pa_xfree(ys); @@ -1528,22 +1482,23 @@ void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, void * pa_dbus_send_empty_reply(conn, msg); - DBusMessage *signal = NULL; pa_assert_se((signal = dbus_message_new_signal(u->dbus_path, EQUALIZER_IFACE, equalizer_signals[EQUALIZER_SIGNAL_FILTER_CHANGED].name))); pa_dbus_protocol_send_signal(u->dbus_protocol, signal); dbus_message_unref(signal); } -void equalizer_handle_get_filter_points(DBusConnection *conn, DBusMessage *msg, void *_u) { - struct userdata *u=(struct userdata *) _u; +static void equalizer_handle_get_filter_points(DBusConnection *conn, DBusMessage *msg, void *_u) { + struct userdata *u = (struct userdata *) _u; DBusError error; + uint32_t *xs; + double *ys; + unsigned x_npoints; + float *H; + pa_bool_t points_good=TRUE; pa_assert(conn); pa_assert(msg); pa_assert(u); - uint32_t *xs; - double *ys; - unsigned x_npoints; dbus_error_init(&error); @@ -1554,10 +1509,9 @@ void equalizer_handle_get_filter_points(DBusConnection *conn, DBusMessage *msg, dbus_error_free(&error); return; } - int points_good=1; for(size_t i = 0; i < x_npoints; ++i){ if(xs[i] >= u->fft_size / 2 + 1){ - points_good=0; + points_good=FALSE; break; } } @@ -1569,7 +1523,7 @@ void equalizer_handle_get_filter_points(DBusConnection *conn, DBusMessage *msg, } ys = pa_xmalloc(x_npoints * sizeof(double)); - float *H = u->Hs[pa_aupdate_read_begin(u->a_H)]; + H = u->Hs[pa_aupdate_read_begin(u->a_H)]; for(uint32_t i = 0; i < x_npoints; ++i){ ys[i] = H[xs[i]] * u->fft_size; } @@ -1579,14 +1533,15 @@ void equalizer_handle_get_filter_points(DBusConnection *conn, DBusMessage *msg, pa_xfree(ys); } -void equalizer_handle_save_profile(DBusConnection *conn, DBusMessage *msg, void *_u) { - struct userdata *u=(struct userdata *) _u; +static void equalizer_handle_save_profile(DBusConnection *conn, DBusMessage *msg, void *_u) { + struct userdata *u = (struct userdata *) _u; + char *name; + DBusMessage *signal = NULL; DBusError error; pa_assert(conn); pa_assert(msg); pa_assert(u); dbus_error_init(&error); - char *name; if(!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &name, @@ -1598,21 +1553,22 @@ void equalizer_handle_save_profile(DBusConnection *conn, DBusMessage *msg, void save_profile(u,name); pa_dbus_send_empty_reply(conn, msg); - DBusMessage *signal = NULL; pa_assert_se((signal = dbus_message_new_signal(MANAGER_PATH, MANAGER_IFACE, manager_signals[MANAGER_SIGNAL_PROFILES_CHANGED].name))); pa_dbus_protocol_send_signal(u->dbus_protocol, signal); dbus_message_unref(signal); } -void equalizer_handle_load_profile(DBusConnection *conn, DBusMessage *msg, void *_u) { +static void equalizer_handle_load_profile(DBusConnection *conn, DBusMessage *msg, void *_u) { struct userdata *u=(struct userdata *) _u; + char *name; DBusError error; + const char *err_msg = NULL; + DBusMessage *signal = NULL; pa_assert(conn); pa_assert(msg); pa_assert(u); dbus_error_init(&error); - char *name; if(!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &name, @@ -1621,77 +1577,80 @@ void equalizer_handle_load_profile(DBusConnection *conn, DBusMessage *msg, void dbus_error_free(&error); return; } - const char *err_msg=load_profile(u,name); - if(err_msg!=NULL){ - pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, "error loading profile %s: %s", name,err_msg); + err_msg = load_profile(u,name); + if(err_msg != NULL){ + pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, "error loading profile %s: %s", name, err_msg); dbus_error_free(&error); return; } pa_dbus_send_empty_reply(conn, msg); - DBusMessage *signal = NULL; pa_assert_se((signal = dbus_message_new_signal(u->dbus_path, EQUALIZER_IFACE, equalizer_signals[EQUALIZER_SIGNAL_FILTER_CHANGED].name))); pa_dbus_protocol_send_signal(u->dbus_protocol, signal); dbus_message_unref(signal); } -void equalizer_get_revision(DBusConnection *conn, DBusMessage *msg, void *_u){ +static void equalizer_get_revision(DBusConnection *conn, DBusMessage *msg, void *_u){ uint32_t rev=1; pa_dbus_send_basic_value_reply(conn, msg, DBUS_TYPE_UINT32, &rev); } -void equalizer_get_n_coefs(DBusConnection *conn, DBusMessage *msg, void *_u){ +static void equalizer_get_n_coefs(DBusConnection *conn, DBusMessage *msg, void *_u){ + struct userdata *u; + uint32_t n_coefs; + pa_assert_se(u = (struct userdata *) _u); pa_assert(conn); pa_assert(msg); - pa_assert(_u); - - struct userdata *u=(struct userdata *)_u; - uint32_t n_coefs=(uint32_t)(u->fft_size / 2 + 1); + n_coefs = (uint32_t) (u->fft_size / 2 + 1); pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &n_coefs); } -void equalizer_get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *_u){ +static void equalizer_get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *_u){ + struct userdata *u; + uint32_t rate; + pa_assert_se(u = (struct userdata *) _u); pa_assert(conn); pa_assert(msg); - pa_assert(_u); - struct userdata *u=(struct userdata *) _u; - uint32_t rate=(uint32_t) u->sink->sample_spec.rate; + rate = (uint32_t) u->sink->sample_spec.rate; pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &rate); } -void equalizer_get_filter_rate(DBusConnection *conn, DBusMessage *msg, void *_u){ +static void equalizer_get_filter_rate(DBusConnection *conn, DBusMessage *msg, void *_u){ + struct userdata *u; + uint32_t fft_size; + pa_assert_se(u = (struct userdata *) _u); pa_assert(conn); pa_assert(msg); - pa_assert(_u); - struct userdata *u=(struct userdata *) _u; - uint32_t fft_size=(uint32_t) u->fft_size; + fft_size = (uint32_t) u->fft_size; pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &fft_size); } -void get_filter(struct userdata *u, double **H_){ +static void get_filter(struct userdata *u, double **H_){ + float *H; *H_ = pa_xnew0(double, u->fft_size / 2 + 1); - float *H=u->Hs[pa_aupdate_read_begin(u->a_H)]; + H = u->Hs[pa_aupdate_read_begin(u->a_H)]; for(size_t i = 0;i < u->fft_size / 2 + 1; ++i){ (*H_)[i] = H[i] * u->fft_size; } pa_aupdate_read_end(u->a_H); } -void equalizer_get_all(DBusConnection *conn, DBusMessage *msg, void *_u){ - pa_assert(conn); - pa_assert(msg); - pa_assert(_u); - - struct userdata *u=(struct userdata *) _u; +static void equalizer_get_all(DBusConnection *conn, DBusMessage *msg, void *_u){ + struct userdata *u; DBusMessage *reply = NULL; DBusMessageIter msg_iter, dict_iter; - uint32_t rev=1; - uint32_t n_coefs=(uint32_t)(u->fft_size / 2 + 1); - uint32_t rate=(uint32_t) u->sink->sample_spec.rate; - uint32_t fft_size=(uint32_t) u->fft_size; + uint32_t rev, n_coefs, rate, fft_size; + double *H; + pa_assert_se(u = (struct userdata *) _u); + pa_assert(msg); + + rev = 1; + n_coefs = (uint32_t) (u->fft_size / 2 + 1); + rate = (uint32_t) u->sink->sample_spec.rate; + fft_size = (uint32_t) u->fft_size; pa_assert_se((reply = dbus_message_new_method_return(msg))); dbus_message_iter_init_append(reply, &msg_iter); @@ -1701,7 +1660,6 @@ void equalizer_get_all(DBusConnection *conn, DBusMessage *msg, void *_u){ pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_SAMPLERATE].property_name, DBUS_TYPE_UINT32, &rate); pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_FILTERSAMPLERATE].property_name, DBUS_TYPE_UINT32, &fft_size); pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_N_COEFS].property_name, DBUS_TYPE_UINT32, &n_coefs); - double *H; get_filter(u, &H); pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_FILTER].property_name, DBUS_TYPE_UINT32, &H); pa_xfree(H); @@ -1711,37 +1669,38 @@ void equalizer_get_all(DBusConnection *conn, DBusMessage *msg, void *_u){ dbus_message_unref(reply); } -void equalizer_get_filter(DBusConnection *conn, DBusMessage *msg, void *_u){ +static void equalizer_get_filter(DBusConnection *conn, DBusMessage *msg, void *_u){ + struct userdata *u; + unsigned n_coefs; + double *H_; + pa_assert_se(u = (struct userdata *) _u); + + n_coefs = u->fft_size / 2 + 1; pa_assert(conn); pa_assert(msg); - pa_assert(_u); - - struct userdata *u = (struct userdata *)_u; - unsigned n_coefs = u->fft_size / 2 + 1; - double *H_; get_filter(u, &H_); pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_DOUBLE, H_, n_coefs); pa_xfree(H_); } -void set_filter(struct userdata *u, double **H_){ +static void set_filter(struct userdata *u, double **H_){ float *H = u->Hs[pa_aupdate_write_begin(u->a_H)]; for(size_t i = 0; i < u->fft_size / 2 + 1; ++i){ H[i] = (float) (*H_)[i]; } fix_filter(H, u->fft_size); - pa_aupdate_write_swap(u->a_H); pa_aupdate_write_end(u->a_H); } -void equalizer_set_filter(DBusConnection *conn, DBusMessage *msg, void *_u){ +static void equalizer_set_filter(DBusConnection *conn, DBusMessage *msg, void *_u){ + struct userdata *u; + double *H; + unsigned _n_coefs; + DBusMessage *signal = NULL; + pa_assert_se(u = (struct userdata *) _u); pa_assert(conn); pa_assert(msg); - pa_assert(_u); - struct userdata *u=(struct userdata *)_u; - double *H; - unsigned _n_coefs; if(pa_dbus_get_fixed_array_set_property_arg(conn, msg, DBUS_TYPE_DOUBLE, &H, &_n_coefs)){ return; } @@ -1755,7 +1714,6 @@ void equalizer_set_filter(DBusConnection *conn, DBusMessage *msg, void *_u){ pa_dbus_send_empty_reply(conn, msg); - DBusMessage *signal = NULL; pa_assert_se((signal = dbus_message_new_signal(u->dbus_path, EQUALIZER_IFACE, equalizer_signals[EQUALIZER_SIGNAL_FILTER_CHANGED].name))); pa_dbus_protocol_send_signal(u->dbus_protocol, signal); dbus_message_unref(signal); -- cgit From 07cd6a4c3d357e9406bbb0444995c8c21f3a3cab Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Sun, 16 Aug 2009 14:29:21 -0700 Subject: module-equalizer-sink.c i->sink -> i in pa_get_sink_max_request* --- src/modules/module-equalizer-sink.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c index b7f61c6a..d4707748 100755 --- a/src/modules/module-equalizer-sink.c +++ b/src/modules/module-equalizer-sink.c @@ -677,7 +677,7 @@ static void sink_input_attach_cb(pa_sink_input *i) { pa_sink_set_fixed_latency_within_thread(u->sink, i->sink->thread_info.fixed_latency); fs = pa_frame_size(&(u->sink->sample_spec)); pa_sink_attach_within_thread(u->sink); - pa_sink_set_max_request_within_thread(u->sink, mround(pa_sink_get_max_request(i->sink), u->R*fs)); + pa_sink_set_max_request_within_thread(u->sink, mround(pa_sink_input_get_max_request(i), u->R*fs)); //pa_sink_set_latency_range_within_thread(u->sink, u->latency*fs, u->latency*fs); //pa_sink_set_latency_range_within_thread(u->sink, u->latency*fs, u->master->thread_info.max_latency); @@ -811,7 +811,7 @@ static void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest) { static void * alloc(size_t x,size_t s){ size_t f = mround(x*s, sizeof(float)*v_size); float *t; - pa_assert_se(f >= x*s); + pa_assert(f >= x*s); //printf("requested %ld floats=%ld bytes, rem=%ld\n", x, x*sizeof(float), x*sizeof(float)%16); //printf("giving %ld floats=%ld bytes, rem=%ld\n", f, f*sizeof(float), f*sizeof(float)%16); t = fftwf_malloc(f); @@ -876,11 +876,8 @@ int pa__init(pa_module*m) { u->overlap_accum = pa_xnew0(float *, u->channels); for(size_t c = 0; c < u->channels; ++c){ u->input[c] = alloc(u->window_size, sizeof(float)); - pa_assert_se(u->input[c]); memset(u->input[c], 0, (u->window_size)*sizeof(float)); - pa_assert_se(u->input[c]); u->overlap_accum[c] = alloc(u->overlap_size, sizeof(float)); - pa_assert_se(u->overlap_accum[c]); memset(u->overlap_accum[c], 0, u->overlap_size*sizeof(float)); } u->output_window = alloc((u->fft_size / 2 + 1), sizeof(fftwf_complex)); -- cgit From 7bd7ce6deca125d9061ea8d29f6a17e65ba36116 Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Sun, 16 Aug 2009 15:41:40 -0700 Subject: module-equalizer-sink.c: swapped order of attach_within_thread and set_max_request within sink_input_attach_cb --- src/modules/module-equalizer-sink.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c index d4707748..9a4740f3 100755 --- a/src/modules/module-equalizer-sink.c +++ b/src/modules/module-equalizer-sink.c @@ -543,7 +543,7 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk pa_memblock_unref(tchunk.memblock); }while(u->samples_gathered < u->window_size); pa_timeval_load(&end); - pa_log_debug("Took %0.6f seconds to get data", pa_timeval_diff(&end, &start) / (double) PA_USEC_PER_SEC); + pa_log_debug("Took %0.6f seconds to get data", (double) pa_timeval_diff(&end, &start) / PA_USEC_PER_SEC); pa_assert(u->fft_size >= u->window_size); pa_assert(u->R < u->window_size); @@ -553,7 +553,7 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk /* process a block */ process_samples(u, chunk); pa_timeval_load(&end); - pa_log_debug("Took %0.6f seconds to process", pa_timeval_diff(&end, &start) / (double) PA_USEC_PER_SEC); + pa_log_debug("Took %0.6f seconds to process", (double) pa_timeval_diff(&end, &start) / PA_USEC_PER_SEC); pa_aupdate_read_end(u->a_H); pa_assert(chunk->memblock); @@ -676,7 +676,6 @@ static void sink_input_attach_cb(pa_sink_input *i) { pa_sink_set_fixed_latency_within_thread(u->sink, i->sink->thread_info.fixed_latency); fs = pa_frame_size(&(u->sink->sample_spec)); - pa_sink_attach_within_thread(u->sink); pa_sink_set_max_request_within_thread(u->sink, mround(pa_sink_input_get_max_request(i), u->R*fs)); //pa_sink_set_latency_range_within_thread(u->sink, u->latency*fs, u->latency*fs); @@ -686,6 +685,7 @@ static void sink_input_attach_cb(pa_sink_input *i) { //pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->latency*fs); //TODO: this guy causes dropouts constantly+rewinds, it's unusable //pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->master->thread_info.max_latency); + pa_sink_attach_within_thread(u->sink); } /* Called from main context */ -- cgit From ab0e20ab2c0014e495cadb9c4f8e188df125fa7f Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Sun, 16 Aug 2009 16:14:30 -0700 Subject: module-equalizer-sink: fixed timeval initialization --- src/modules/module-equalizer-sink.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c index 9a4740f3..a8ccff8b 100755 --- a/src/modules/module-equalizer-sink.c +++ b/src/modules/module-equalizer-sink.c @@ -40,6 +40,7 @@ USA. #include #include +#include #include #include #include @@ -510,7 +511,7 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk pa_sink_process_rewind(u->sink, 0); //pa_log_debug("start output-buffered %ld, input-buffered %ld, requested %ld",buffered_samples,u->samples_gathered,samples_requested); - pa_timeval_load(&start); + pa_rtclock_get(&start); do{ size_t input_remaining = u->window_size - u->samples_gathered; pa_assert(input_remaining > 0); @@ -532,27 +533,27 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk pa_memblockq_drop(u->input_q, tchunk.length); //pa_log_debug("asked for %ld input samples, got %ld samples",input_remaining,buffer->length/fs); /* copy new input */ - //pa_timeval_load(start); + //pa_rtclock_get(start); if(u->first_iteration){ initialize_buffer(u, &tchunk); }else{ input_buffer(u, &tchunk); } - //pa_timeval_load(&end); + //pa_rtclock_get(&end); //pa_log_debug("Took %0.5f seconds to setup", pa_timeval_diff(end, start) / (double) PA_USEC_PER_SEC); pa_memblock_unref(tchunk.memblock); }while(u->samples_gathered < u->window_size); - pa_timeval_load(&end); + pa_rtclock_get(&end); pa_log_debug("Took %0.6f seconds to get data", (double) pa_timeval_diff(&end, &start) / PA_USEC_PER_SEC); pa_assert(u->fft_size >= u->window_size); pa_assert(u->R < u->window_size); /* set the H filter */ u->H = u->Hs[pa_aupdate_read_begin(u->a_H)]; - pa_timeval_load(&start); + pa_rtclock_get(&start); /* process a block */ process_samples(u, chunk); - pa_timeval_load(&end); + pa_rtclock_get(&end); pa_log_debug("Took %0.6f seconds to process", (double) pa_timeval_diff(&end, &start) / PA_USEC_PER_SEC); pa_aupdate_read_end(u->a_H); -- cgit From cd54ecdc8c6b7947bd06a7db69055cfba078f1bf Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Mon, 17 Aug 2009 10:52:43 -0700 Subject: module-equalizer-sink: drop old macros for new library based ones --- src/modules/module-equalizer-sink.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c index a8ccff8b..5152b646 100755 --- a/src/modules/module-equalizer-sink.c +++ b/src/modules/module-equalizer-sink.c @@ -136,7 +136,6 @@ static const char* const valid_modargs[] = { #define v_size 4 -#define mround(x, y) ((x + y - 1) / y) * y #define SINKLIST "equalized_sinklist" #define EQDB "equalizer_db" static void dbus_init(struct userdata *u); @@ -349,10 +348,10 @@ static void dsp_logic( // fftwf_complex * restrict output_window,//The transformed window'd src // struct userdata *u){//Collection of constants // -// const size_t window_size = mround(u->window_size,v_size); -// const size_t fft_h = mround(u->fft_size / 2 + 1, v_size / 2); -// //const size_t R = mround(u->R, v_size); -// const size_t overlap_size = mround(u->overlap_size, v_size); +// const size_t window_size = PA_ROUND_UP(u->window_size,v_size); +// const size_t fft_h = PA_ROUND_UP(u->fft_size / 2 + 1, v_size / 2); +// //const size_t R = PA_ROUND_UP(u->R, v_size); +// const size_t overlap_size = PA_ROUND_UP(u->overlap_size, v_size); // // //assert(u->samples_gathered >= u->R); // //zero out the bit beyond the real overlap so we don't add garbage @@ -677,7 +676,7 @@ static void sink_input_attach_cb(pa_sink_input *i) { pa_sink_set_fixed_latency_within_thread(u->sink, i->sink->thread_info.fixed_latency); fs = pa_frame_size(&(u->sink->sample_spec)); - pa_sink_set_max_request_within_thread(u->sink, mround(pa_sink_input_get_max_request(i), u->R*fs)); + pa_sink_set_max_request_within_thread(u->sink, PA_ROUND_UP(pa_sink_input_get_max_request(i), u->R*fs)); //pa_sink_set_latency_range_within_thread(u->sink, u->latency*fs, u->latency*fs); //pa_sink_set_latency_range_within_thread(u->sink, u->latency*fs, u->master->thread_info.max_latency); @@ -810,7 +809,7 @@ static void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest) { //ensure's memory allocated is a multiple of v_size //and aligned static void * alloc(size_t x,size_t s){ - size_t f = mround(x*s, sizeof(float)*v_size); + size_t f = PA_ROUND_UP(x*s, sizeof(float)*v_size); float *t; pa_assert(f >= x*s); //printf("requested %ld floats=%ld bytes, rem=%ld\n", x, x*sizeof(float), x*sizeof(float)%16); -- cgit From 735c8ab6fbd9b673c05a2506f3f3c1d64d4d3970 Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Tue, 18 Aug 2009 22:11:37 -0700 Subject: module-equalizer-sink: added support for preamp --- src/modules/module-equalizer-sink.c | 279 ++++++++++++++++++++---------------- 1 file changed, 159 insertions(+), 120 deletions(-) (limited to 'src') diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c index 5152b646..0d82e018 100755 --- a/src/modules/module-equalizer-sink.c +++ b/src/modules/module-equalizer-sink.c @@ -104,6 +104,7 @@ struct userdata { size_t overlap_size;//window_size-R size_t samples_gathered; //message + float X; float *H;//frequency response filter (magnitude based) float *W;//windowing function (time domain) float *work_buffer, **input, **overlap_accum; @@ -111,6 +112,7 @@ struct userdata { fftwf_plan forward_plan, inverse_plan; //size_t samplings; + float Xs[2]; float *Hs[2];//thread updatable copies pa_aupdate *a_H; pa_memchunk conv_buffer; @@ -138,6 +140,8 @@ static const char* const valid_modargs[] = { #define v_size 4 #define SINKLIST "equalized_sinklist" #define EQDB "equalizer_db" +#define FILTER_SIZE (u->fft_size / 2 + 1) +#define PROFILE_SIZE (FILTER_SIZE + 1) static void dbus_init(struct userdata *u); static void dbus_done(struct userdata *u); @@ -199,7 +203,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse switch (code) { case PA_SINK_MESSAGE_GET_LATENCY: { - size_t fs=pa_frame_size(&u->sink->sample_spec); + //size_t fs=pa_frame_size(&u->sink->sample_spec); /* The sink is _put() before the sink input is, so let's * make sure we don't access it in that time. Also, the @@ -215,8 +219,8 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse pa_sink_get_latency_within_thread(u->sink_input->sink) + /* Add the latency internal to our sink input on top */ - pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->sink_input->sink->sample_spec) + - pa_bytes_to_usec(u->samples_gathered * fs, &u->sink->sample_spec); + pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->sink_input->sink->sample_spec); + // pa_bytes_to_usec(u->samples_gathered * fs, &u->sink->sample_spec); //+ pa_bytes_to_usec(u->latency * fs, ss) //+ pa_bytes_to_usec(pa_memblockq_get_length(u->input_q), ss); return 0; @@ -274,15 +278,6 @@ static void sink_update_requested_latency(pa_sink *s) { pa_sink_get_requested_latency_within_thread(s)); } -typedef float v4sf __attribute__ ((__aligned__(v_size * sizeof(float)))); -typedef union float_vector { - float f[v_size]; - v4sf v; -#ifdef __SSE2__ - __m128 m; -#endif -} float_vector_t; - //reference implementation static void dsp_logic( float * restrict dst,//used as a temp array too, needs to be fft_length! @@ -299,13 +294,13 @@ static void dsp_logic( memset(dst + u->window_size, 0, (u->fft_size - u->window_size) * sizeof(float)); //window the data for(size_t j = 0;j < u->window_size; ++j){ - dst[j] = W[j] * src[j]; + dst[j] = u->X * W[j] * src[j]; } //Processing is done here! //do fft fftwf_execute_dft_r2c(u->forward_plan, dst, output_window); //perform filtering - for(size_t j = 0; j < u->fft_size / 2 + 1; ++j){ + for(size_t j = 0; j < FILTER_SIZE; ++j){ u->output_window[j][0] *= u->H[j]; u->output_window[j][1] *= u->H[j]; } @@ -335,6 +330,15 @@ static void dsp_logic( ); } +typedef float v4sf __attribute__ ((__aligned__(v_size * sizeof(float)))); +typedef union float_vector { + float f[v_size]; + v4sf v; +#ifdef __SSE2__ + __m128 m; +#endif +} float_vector_t; + ////regardless of sse enabled, the loops in here assume ////16 byte aligned addresses and memory allocations divisible by v_size //void dsp_logic( @@ -347,11 +351,12 @@ static void dsp_logic( // const float * restrict W,//The windowing function // fftwf_complex * restrict output_window,//The transformed window'd src // struct userdata *u){//Collection of constants -// + //float_vector_t x = {u->X, u->X, u->X, u->X}; // const size_t window_size = PA_ROUND_UP(u->window_size,v_size); -// const size_t fft_h = PA_ROUND_UP(u->fft_size / 2 + 1, v_size / 2); +// const size_t fft_h = PA_ROUND_UP(FILTER_SIZE, v_size / 2); // //const size_t R = PA_ROUND_UP(u->R, v_size); // const size_t overlap_size = PA_ROUND_UP(u->overlap_size, v_size); +// overlap_size = PA_ROUND_UP(u->overlap_size, v_size); // // //assert(u->samples_gathered >= u->R); // //zero out the bit beyond the real overlap so we don't add garbage @@ -368,9 +373,9 @@ static void dsp_logic( // float_vector_t *w = (float_vector_t*) (W+j); // float_vector_t *s = (float_vector_t*) (src+j); //#if __SSE2__ -// d->m = _mm_mul_ps(w->m, s->m); +// d->m = _mm_mul_ps(x->m, _mm_mul_ps(w->m, s->m)); //#else -// d->v = w->v * s->v; +// d->v = x->v * w->v * s->v; //#endif // } // //Processing is done here! @@ -498,6 +503,7 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk struct userdata *u; size_t fs; struct timeval start, end; + unsigned a_i; pa_memchunk tchunk; pa_sink_input_assert_ref(i); pa_assert_se(u = i->userdata); @@ -548,7 +554,9 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk pa_assert(u->fft_size >= u->window_size); pa_assert(u->R < u->window_size); /* set the H filter */ - u->H = u->Hs[pa_aupdate_read_begin(u->a_H)]; + a_i = pa_aupdate_read_begin(u->a_H); + u->X = u->Xs[a_i]; + u->H = u->Hs[a_i]; pa_rtclock_get(&start); /* process a block */ process_samples(u, chunk); @@ -727,18 +735,25 @@ static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t s } static void save_profile(struct userdata *u, char *name){ - float *H_n = pa_xmalloc((u->fft_size / 2 + 1) * sizeof(float)); - const float *H = u->Hs[pa_aupdate_read_begin(u->a_H)]; + unsigned a_i; + const size_t profile_size = PROFILE_SIZE * sizeof(float); + float *H_n, *profile; + const float *H; pa_datum key, data; - for(size_t i = 0 ; i <= u->fft_size / 2 + 1; ++i){ + profile = pa_xnew0(float, profile_size); + a_i = pa_aupdate_read_begin(u->a_H); + H_n = profile + 1; + H = u->Hs[a_i]; + profile[0] = u->Xs[a_i]; + for(size_t i = 0 ; i <= FILTER_SIZE; ++i){ //H_n[i] = H[i] * u->fft_size; H_n[i] = H[i]; } pa_aupdate_read_end(u->a_H); key.data=name; key.size = strlen(key.data); - data.data = H_n; - data.size = (u->fft_size / 2 + 1) * sizeof(float); + data.data = profile; + data.size = profile_size; pa_database_set(u->database, &key, &data, TRUE); pa_database_sync(u->database); } @@ -760,13 +775,17 @@ static void remove_profile(pa_core *c, char *name){ } static const char* load_profile(struct userdata *u, char *name){ - pa_datum key,value; + unsigned a_i; + pa_datum key, value; + const size_t profile_size = PROFILE_SIZE * sizeof(float); key.data = name; key.size = strlen(key.data); if(pa_database_get(u->database, &key, &value) != NULL){ - if(value.size == (u->fft_size / 2 + 1) * sizeof(float)){ - float *H=u->Hs[pa_aupdate_write_begin(u->a_H)]; - memcpy(H, value.data, value.size); + if(value.size == profile_size){ + float *H = (float *) value.data; + a_i = pa_aupdate_write_begin(u->a_H); + u->Xs[a_i] = H[0]; + memcpy(u->Hs[a_i], H + 1, (FILTER_SIZE) * sizeof(float)); pa_aupdate_write_end(u->a_H); }else{ return "incompatible size"; @@ -831,6 +850,7 @@ int pa__init(pa_module*m) { pa_bool_t *use_default = NULL; size_t fs; float *H; + unsigned a_i; pa_assert(m); @@ -867,7 +887,7 @@ int pa__init(pa_module*m) { u->a_H = pa_aupdate_new(); u->latency = u->window_size - u->R; for(size_t i = 0; i < 2; ++i){ - u->Hs[i] = alloc((u->fft_size / 2 + 1), sizeof(float)); + u->Hs[i] = alloc((FILTER_SIZE), sizeof(float)); } u->W = alloc(u->window_size, sizeof(float)); u->work_buffer = alloc(u->fft_size, sizeof(float)); @@ -880,7 +900,7 @@ int pa__init(pa_module*m) { u->overlap_accum[c] = alloc(u->overlap_size, sizeof(float)); memset(u->overlap_accum[c], 0, u->overlap_size*sizeof(float)); } - u->output_window = alloc((u->fft_size / 2 + 1), sizeof(fftwf_complex)); + u->output_window = alloc((FILTER_SIZE), sizeof(fftwf_complex)); u->forward_plan = fftwf_plan_dft_r2c_1d(u->fft_size, u->work_buffer, u->output_window, FFTW_ESTIMATE); u->inverse_plan = fftwf_plan_dft_c2r_1d(u->fft_size, u->output_window, u->work_buffer, FFTW_ESTIMATE); @@ -964,8 +984,10 @@ int pa__init(pa_module*m) { dbus_init(u); //default filter to these - H=u->Hs[pa_aupdate_write_begin(u->a_H)]; - for(size_t i = 0; i < u->fft_size / 2 + 1; ++i){ + a_i = pa_aupdate_write_begin(u->a_H); + H = u->Hs[a_i]; + u->Xs[a_i] = 1.0f; + for(size_t i = 0; i < FILTER_SIZE; ++i){ H[i] = 1.0 / sqrtf(2.0f); } fix_filter(H, u->fft_size); @@ -1053,9 +1075,7 @@ void pa__done(pa_module*m) { #define MANAGER_IFACE EXTNAME ".Manager" #define EQUALIZER_IFACE EXTNAME ".Equalizer" static void manager_get_revision(DBusConnection *conn, DBusMessage *msg, void *_u); -static void get_sinks(pa_core *u, char ***names, unsigned *n_sinks); static void manager_get_sinks(DBusConnection *conn, DBusMessage *msg, void *_u); -static void get_profiles(pa_core *u, char ***names, unsigned *n_sinks); static void manager_get_profiles(DBusConnection *conn, DBusMessage *msg, void *_u); static void manager_get_all(DBusConnection *conn, DBusMessage *msg, void *_u); static void manager_handle_remove_profile(DBusConnection *conn, DBusMessage *msg, void *_u); @@ -1064,14 +1084,12 @@ static void equalizer_get_sample_rate(DBusConnection *conn, DBusMessage *msg, vo static void equalizer_get_filter_rate(DBusConnection *conn, DBusMessage *msg, void *_u); static void equalizer_get_n_coefs(DBusConnection *conn, DBusMessage *msg, void *_u); static void equalizer_get_filter(DBusConnection *conn, DBusMessage *msg, void *_u); -static void equalizer_get_all(DBusConnection *conn, DBusMessage *msg, void *_u); static void equalizer_set_filter(DBusConnection *conn, DBusMessage *msg, void *_u); +static void equalizer_get_all(DBusConnection *conn, DBusMessage *msg, void *_u); static void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, void *_u); static void equalizer_handle_get_filter_points(DBusConnection *conn, DBusMessage *msg, void *_u); static void equalizer_handle_save_profile(DBusConnection *conn, DBusMessage *msg, void *_u); static void equalizer_handle_load_profile(DBusConnection *conn, DBusMessage *msg, void *_u); -static void get_filter(struct userdata *u, double **H_); -static void set_filter(struct userdata *u, double **H_); enum manager_method_index { MANAGER_METHOD_REMOVE_PROFILE, MANAGER_METHOD_MAX @@ -1144,16 +1162,19 @@ enum equalizer_handler_index { EQUALIZER_HANDLER_FILTERSAMPLERATE, EQUALIZER_HANDLER_N_COEFS, EQUALIZER_HANDLER_FILTER, + EQUALIZER_HANDLER_PREAMP, EQUALIZER_HANDLER_MAX }; pa_dbus_arg_info filter_points_args[]={ {"xs", "au","in"}, {"ys", "ad","out"}, + {"preamp", "d","out"}, }; pa_dbus_arg_info seed_filter_args[]={ {"xs", "au","in"}, {"ys", "ad","in"}, + {"preamp", "d","in"}, }; pa_dbus_arg_info save_profile_args[]={ {"name", "s","in"}, @@ -1190,7 +1211,7 @@ static pa_dbus_property_handler equalizer_handlers[EQUALIZER_HANDLER_MAX]={ [EQUALIZER_HANDLER_SAMPLERATE]{.property_name="SampleRate",.type="u",.get_cb=equalizer_get_sample_rate,.set_cb=NULL}, [EQUALIZER_HANDLER_FILTERSAMPLERATE]{.property_name="FilterSampleRate",.type="u",.get_cb=equalizer_get_filter_rate,.set_cb=NULL}, [EQUALIZER_HANDLER_N_COEFS]{.property_name="NFilterCoefficients",.type="u",.get_cb=equalizer_get_n_coefs,.set_cb=NULL}, - [EQUALIZER_HANDLER_FILTER]{.property_name="Filter",.type="ad",.get_cb=equalizer_get_filter,.set_cb=equalizer_set_filter}, + [EQUALIZER_HANDLER_FILTER]{.property_name="Filter",.type="ad",.get_cb=equalizer_get_filter,.set_cb=equalizer_set_filter} }; enum equalizer_signal_index{ @@ -1215,7 +1236,7 @@ static pa_dbus_interface_info equalizer_info={ .n_signals=EQUALIZER_SIGNAL_MAX }; -static void dbus_init(struct userdata *u){ +void dbus_init(struct userdata *u){ uint32_t dummy; DBusMessage *signal = NULL; pa_idxset *sink_list = NULL; @@ -1244,7 +1265,7 @@ static void dbus_init(struct userdata *u){ dbus_message_unref(signal); } -static void dbus_done(struct userdata *u){ +void dbus_done(struct userdata *u){ pa_idxset *sink_list; uint32_t dummy; @@ -1269,7 +1290,7 @@ static void dbus_done(struct userdata *u){ pa_dbus_protocol_unref(u->dbus_protocol); } -static void manager_handle_remove_profile(DBusConnection *conn, DBusMessage *msg, void *_u) { +void manager_handle_remove_profile(DBusConnection *conn, DBusMessage *msg, void *_u) { DBusError error; pa_core *c = (pa_core *)_u; DBusMessage *signal = NULL; @@ -1296,7 +1317,7 @@ static void manager_handle_remove_profile(DBusConnection *conn, DBusMessage *msg dbus_message_unref(signal); } -static void manager_get_revision(DBusConnection *conn, DBusMessage *msg, void *_u){ +void manager_get_revision(DBusConnection *conn, DBusMessage *msg, void *_u){ uint32_t rev=1; pa_dbus_send_basic_value_reply(conn, msg, DBUS_TYPE_UINT32, &rev); } @@ -1319,7 +1340,7 @@ static void get_sinks(pa_core *u, char ***names, unsigned *n_sinks){ } } -static void manager_get_sinks(DBusConnection *conn, DBusMessage *msg, void *_u){ +void manager_get_sinks(DBusConnection *conn, DBusMessage *msg, void *_u){ unsigned n; char **names = NULL; pa_assert(conn); @@ -1367,7 +1388,7 @@ static void get_profiles(pa_core *c, char ***names, unsigned *n){ pa_strlist_free(head); } -static void manager_get_profiles(DBusConnection *conn, DBusMessage *msg, void *_u){ +void manager_get_profiles(DBusConnection *conn, DBusMessage *msg, void *_u){ char **names; unsigned n; pa_assert(conn); @@ -1382,7 +1403,7 @@ static void manager_get_profiles(DBusConnection *conn, DBusMessage *msg, void *_ pa_xfree(names); } -static void manager_get_all(DBusConnection *conn, DBusMessage *msg, void *_u){ +void manager_get_all(DBusConnection *conn, DBusMessage *msg, void *_u){ pa_core *c; char **names = NULL; unsigned n; @@ -1418,14 +1439,14 @@ static void manager_get_all(DBusConnection *conn, DBusMessage *msg, void *_u){ dbus_message_unref(reply); } -static void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, void *_u) { +void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, void *_u) { struct userdata *u=(struct userdata *) _u; DBusError error; DBusMessage *signal = NULL; float *ys; uint32_t *xs; - double *_ys; - unsigned x_npoints, y_npoints; + double *_ys, preamp; + unsigned x_npoints, y_npoints, a_i; float *H; pa_bool_t points_good = TRUE; pa_assert(conn); @@ -1437,13 +1458,14 @@ static void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, if(!dbus_message_get_args(msg, &error, DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &xs, &x_npoints, DBUS_TYPE_ARRAY, DBUS_TYPE_DOUBLE, &_ys, &y_npoints, + DBUS_TYPE_DOUBLE, &preamp, DBUS_TYPE_INVALID)){ pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message); dbus_error_free(&error); return; } for(size_t i = 0; i < x_npoints; ++i){ - if(xs[i] >= u->fft_size / 2 + 1){ + if(xs[i] >= FILTER_SIZE){ points_good = FALSE; break; } @@ -1453,8 +1475,8 @@ static void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, dbus_error_free(&error); return; - }else if(x_npoints != y_npoints || x_npoints < 2 || x_npoints > u->fft_size / 2 +1 ){ - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "xs and ys must be the same length and 2<=l<=%ld!", u->fft_size / 2 + 1); + }else if(x_npoints != y_npoints || x_npoints < 2 || x_npoints > FILTER_SIZE ){ + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "xs and ys must be the same length and 2<=l<=%ld!", FILTER_SIZE); dbus_error_free(&error); return; }else if(xs[0] != 0 || xs[x_npoints - 1] != u->fft_size / 2){ @@ -1467,9 +1489,10 @@ static void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, for(uint32_t i = 0; i < x_npoints; ++i){ ys[i] = (float) _ys[i]; } - - H = u->Hs[pa_aupdate_write_begin(u->a_H)]; - interpolate(H, u->fft_size / 2 + 1, xs, ys, x_npoints); + a_i = pa_aupdate_write_begin(u->a_H); + H = u->Hs[a_i]; + u->Xs[a_i] = preamp; + interpolate(H, FILTER_SIZE, xs, ys, x_npoints); fix_filter(H, u->fft_size); pa_aupdate_write_end(u->a_H); pa_xfree(ys); @@ -1484,14 +1507,16 @@ static void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, dbus_message_unref(signal); } -static void equalizer_handle_get_filter_points(DBusConnection *conn, DBusMessage *msg, void *_u) { +void equalizer_handle_get_filter_points(DBusConnection *conn, DBusMessage *msg, void *_u) { struct userdata *u = (struct userdata *) _u; DBusError error; uint32_t *xs; - double *ys; - unsigned x_npoints; + double *ys, preamp; + unsigned x_npoints, a_i; float *H; pa_bool_t points_good=TRUE; + DBusMessage *reply = NULL; + DBusMessageIter msg_iter; pa_assert(conn); pa_assert(msg); @@ -1507,30 +1532,39 @@ static void equalizer_handle_get_filter_points(DBusConnection *conn, DBusMessage return; } for(size_t i = 0; i < x_npoints; ++i){ - if(xs[i] >= u->fft_size / 2 + 1){ + if(xs[i] >= FILTER_SIZE){ points_good=FALSE; break; } } - if(x_npoints > u->fft_size / 2 +1 || !points_good){ - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "xs indices/length must be <= %ld!", u->fft_size / 2 + 1); + if(x_npoints > FILTER_SIZE || !points_good){ + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "xs indices/length must be <= %ld!", FILTER_SIZE); dbus_error_free(&error); return; } ys = pa_xmalloc(x_npoints * sizeof(double)); - H = u->Hs[pa_aupdate_read_begin(u->a_H)]; + a_i = pa_aupdate_read_begin(u->a_H); + H = u->Hs[a_i]; + preamp = u->Xs[a_i]; for(uint32_t i = 0; i < x_npoints; ++i){ ys[i] = H[xs[i]] * u->fft_size; } pa_aupdate_read_end(u->a_H); - pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_DOUBLE, ys, x_npoints); + pa_assert_se((reply = dbus_message_new_method_return(msg))); + dbus_message_iter_init_append(reply, &msg_iter); + + pa_dbus_append_basic_array(&msg_iter, DBUS_TYPE_DOUBLE, ys, x_npoints); + pa_dbus_append_basic_variant(&msg_iter, DBUS_TYPE_DOUBLE, &preamp); + + pa_assert_se(dbus_connection_send(conn, reply, NULL)); + dbus_message_unref(reply); pa_xfree(ys); } -static void equalizer_handle_save_profile(DBusConnection *conn, DBusMessage *msg, void *_u) { +void equalizer_handle_save_profile(DBusConnection *conn, DBusMessage *msg, void *_u) { struct userdata *u = (struct userdata *) _u; char *name; DBusMessage *signal = NULL; @@ -1555,7 +1589,7 @@ static void equalizer_handle_save_profile(DBusConnection *conn, DBusMessage *msg dbus_message_unref(signal); } -static void equalizer_handle_load_profile(DBusConnection *conn, DBusMessage *msg, void *_u) { +void equalizer_handle_load_profile(DBusConnection *conn, DBusMessage *msg, void *_u) { struct userdata *u=(struct userdata *) _u; char *name; DBusError error; @@ -1574,7 +1608,7 @@ static void equalizer_handle_load_profile(DBusConnection *conn, DBusMessage *msg dbus_error_free(&error); return; } - err_msg = load_profile(u,name); + err_msg = load_profile(u, name); if(err_msg != NULL){ pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, "error loading profile %s: %s", name, err_msg); dbus_error_free(&error); @@ -1587,23 +1621,23 @@ static void equalizer_handle_load_profile(DBusConnection *conn, DBusMessage *msg dbus_message_unref(signal); } -static void equalizer_get_revision(DBusConnection *conn, DBusMessage *msg, void *_u){ +void equalizer_get_revision(DBusConnection *conn, DBusMessage *msg, void *_u){ uint32_t rev=1; pa_dbus_send_basic_value_reply(conn, msg, DBUS_TYPE_UINT32, &rev); } -static void equalizer_get_n_coefs(DBusConnection *conn, DBusMessage *msg, void *_u){ +void equalizer_get_n_coefs(DBusConnection *conn, DBusMessage *msg, void *_u){ struct userdata *u; uint32_t n_coefs; pa_assert_se(u = (struct userdata *) _u); pa_assert(conn); pa_assert(msg); - n_coefs = (uint32_t) (u->fft_size / 2 + 1); + n_coefs = (uint32_t) PROFILE_SIZE; pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &n_coefs); } -static void equalizer_get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *_u){ +void equalizer_get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *_u){ struct userdata *u; uint32_t rate; pa_assert_se(u = (struct userdata *) _u); @@ -1614,7 +1648,7 @@ static void equalizer_get_sample_rate(DBusConnection *conn, DBusMessage *msg, vo pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &rate); } -static void equalizer_get_filter_rate(DBusConnection *conn, DBusMessage *msg, void *_u){ +void equalizer_get_filter_rate(DBusConnection *conn, DBusMessage *msg, void *_u){ struct userdata *u; uint32_t fft_size; pa_assert_se(u = (struct userdata *) _u); @@ -1625,71 +1659,47 @@ static void equalizer_get_filter_rate(DBusConnection *conn, DBusMessage *msg, vo pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &fft_size); } -static void get_filter(struct userdata *u, double **H_){ +static double * get_filter(struct userdata *u){ float *H; - *H_ = pa_xnew0(double, u->fft_size / 2 + 1); - H = u->Hs[pa_aupdate_read_begin(u->a_H)]; - for(size_t i = 0;i < u->fft_size / 2 + 1; ++i){ - (*H_)[i] = H[i] * u->fft_size; + double *H_; + unsigned a_i; + H_ = pa_xnew0(double, PROFILE_SIZE); + a_i = pa_aupdate_read_begin(u->a_H); + H = u->Hs[a_i]; + H_[0] = u->Xs[a_i]; + for(size_t i = 0;i < FILTER_SIZE; ++i){ + H_[i + 1] = H[i] * u->fft_size; } pa_aupdate_read_end(u->a_H); + return H_; } -static void equalizer_get_all(DBusConnection *conn, DBusMessage *msg, void *_u){ - struct userdata *u; - DBusMessage *reply = NULL; - DBusMessageIter msg_iter, dict_iter; - uint32_t rev, n_coefs, rate, fft_size; - double *H; - pa_assert_se(u = (struct userdata *) _u); - pa_assert(msg); - - rev = 1; - n_coefs = (uint32_t) (u->fft_size / 2 + 1); - rate = (uint32_t) u->sink->sample_spec.rate; - fft_size = (uint32_t) u->fft_size; - - pa_assert_se((reply = dbus_message_new_method_return(msg))); - dbus_message_iter_init_append(reply, &msg_iter); - pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter)); - - pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_REVISION].property_name, DBUS_TYPE_UINT32, &rev); - pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_SAMPLERATE].property_name, DBUS_TYPE_UINT32, &rate); - pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_FILTERSAMPLERATE].property_name, DBUS_TYPE_UINT32, &fft_size); - pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_N_COEFS].property_name, DBUS_TYPE_UINT32, &n_coefs); - get_filter(u, &H); - pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_FILTER].property_name, DBUS_TYPE_UINT32, &H); - pa_xfree(H); - - pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter)); - pa_assert_se(dbus_connection_send(conn, reply, NULL)); - dbus_message_unref(reply); -} - -static void equalizer_get_filter(DBusConnection *conn, DBusMessage *msg, void *_u){ +void equalizer_get_filter(DBusConnection *conn, DBusMessage *msg, void *_u){ struct userdata *u; unsigned n_coefs; double *H_; pa_assert_se(u = (struct userdata *) _u); - n_coefs = u->fft_size / 2 + 1; + n_coefs = PROFILE_SIZE; pa_assert(conn); pa_assert(msg); - get_filter(u, &H_); + H_ = get_filter(u); pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_DOUBLE, H_, n_coefs); pa_xfree(H_); } -static void set_filter(struct userdata *u, double **H_){ - float *H = u->Hs[pa_aupdate_write_begin(u->a_H)]; - for(size_t i = 0; i < u->fft_size / 2 + 1; ++i){ - H[i] = (float) (*H_)[i]; +static void set_filter(struct userdata *u, double *H_){ + unsigned a_i= pa_aupdate_write_begin(u->a_H); + float *H = u->Hs[a_i]; + u->Xs[a_i] = H_[0]; + for(size_t i = 0; i < FILTER_SIZE; ++i){ + H[i] = (float) H_[i + 1]; } - fix_filter(H, u->fft_size); + fix_filter(H + 1, u->fft_size); pa_aupdate_write_end(u->a_H); } -static void equalizer_set_filter(DBusConnection *conn, DBusMessage *msg, void *_u){ +void equalizer_set_filter(DBusConnection *conn, DBusMessage *msg, void *_u){ struct userdata *u; double *H; unsigned _n_coefs; @@ -1701,13 +1711,11 @@ static void equalizer_set_filter(DBusConnection *conn, DBusMessage *msg, void *_ if(pa_dbus_get_fixed_array_set_property_arg(conn, msg, DBUS_TYPE_DOUBLE, &H, &_n_coefs)){ return; } - if(_n_coefs!=u->fft_size / 2 + 1){ - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "This filter takes exactly %ld coefficients, you gave %d", u->fft_size / 2 + 1, _n_coefs); + if(_n_coefs != PROFILE_SIZE){ + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "This filter takes exactly %ld coefficients, you gave %d", PROFILE_SIZE, _n_coefs); return; } - set_filter(u, &H); - //Stupid for IO reasons? Add a save signal to dbus instead - //save_state(u); + set_filter(u, H); pa_dbus_send_empty_reply(conn, msg); @@ -1715,3 +1723,34 @@ static void equalizer_set_filter(DBusConnection *conn, DBusMessage *msg, void *_ pa_dbus_protocol_send_signal(u->dbus_protocol, signal); dbus_message_unref(signal); } + +void equalizer_get_all(DBusConnection *conn, DBusMessage *msg, void *_u){ + struct userdata *u; + DBusMessage *reply = NULL; + DBusMessageIter msg_iter, dict_iter; + uint32_t rev, n_coefs, rate, fft_size; + double *H; + pa_assert_se(u = (struct userdata *) _u); + pa_assert(msg); + + rev = 1; + n_coefs = (uint32_t) PROFILE_SIZE; + rate = (uint32_t) u->sink->sample_spec.rate; + fft_size = (uint32_t) u->fft_size; + + pa_assert_se((reply = dbus_message_new_method_return(msg))); + dbus_message_iter_init_append(reply, &msg_iter); + pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter)); + + pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_REVISION].property_name, DBUS_TYPE_UINT32, &rev); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_SAMPLERATE].property_name, DBUS_TYPE_UINT32, &rate); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_FILTERSAMPLERATE].property_name, DBUS_TYPE_UINT32, &fft_size); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_N_COEFS].property_name, DBUS_TYPE_UINT32, &n_coefs); + H = get_filter(u); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_FILTER].property_name, DBUS_TYPE_UINT32, &H); + pa_xfree(H); + + pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter)); + pa_assert_se(dbus_connection_send(conn, reply, NULL)); + dbus_message_unref(reply); +} -- cgit From 2f6fd32cc5726b47d332ad71c0f04f16614118ba Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Wed, 19 Aug 2009 00:30:07 -0700 Subject: module-equalizer-sink: fixed a bug w/ new zero-latency input scheme (that was an interesting/cool bug!) --- src/modules/module-equalizer-sink.c | 50 ++++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 23 deletions(-) (limited to 'src') diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c index 0d82e018..67a3d74e 100755 --- a/src/modules/module-equalizer-sink.c +++ b/src/modules/module-equalizer-sink.c @@ -104,8 +104,6 @@ struct userdata { size_t overlap_size;//window_size-R size_t samples_gathered; //message - float X; - float *H;//frequency response filter (magnitude based) float *W;//windowing function (time domain) float *work_buffer, **input, **overlap_accum; fftwf_complex *output_window; @@ -113,7 +111,7 @@ struct userdata { //size_t samplings; float Xs[2]; - float *Hs[2];//thread updatable copies + float *Hs[2];//thread updatable copies of the freq response filters (magintude based) pa_aupdate *a_H; pa_memchunk conv_buffer; pa_memblockq *input_q; @@ -284,7 +282,8 @@ static void dsp_logic( float * restrict src,/*input data w/ overlap at start, *automatically cycled in routine */ - float * restrict overlap,//The size of the overlap + float * restrict overlap, + const float X,//multipliar const float * restrict H,//The freq. magnitude scalers filter const float * restrict W,//The windowing function fftwf_complex * restrict output_window,//The transformed window'd src @@ -293,16 +292,16 @@ static void dsp_logic( //zero padd the data memset(dst + u->window_size, 0, (u->fft_size - u->window_size) * sizeof(float)); //window the data - for(size_t j = 0;j < u->window_size; ++j){ - dst[j] = u->X * W[j] * src[j]; + for(size_t j = 0; j < u->window_size; ++j){ + dst[j] = X * W[j] * src[j]; } //Processing is done here! //do fft fftwf_execute_dft_r2c(u->forward_plan, dst, output_window); //perform filtering for(size_t j = 0; j < FILTER_SIZE; ++j){ - u->output_window[j][0] *= u->H[j]; - u->output_window[j][1] *= u->H[j]; + u->output_window[j][0] *= H[j]; + u->output_window[j][1] *= H[j]; } //inverse fft fftwf_execute_dft_c2r(u->inverse_plan, output_window, dst); @@ -314,9 +313,9 @@ static void dsp_logic( //} //overlap add and preserve overlap component from this window (linear phase) - for(size_t j = 0;j < u->overlap_size; ++j){ + for(size_t j = 0; j < u->overlap_size; ++j){ u->work_buffer[j] += overlap[j]; - overlap[j] = dst[u->R+j]; + overlap[j] = dst[u->R + j]; } ////debug: tests if basic buffering works ////shouldn't modify the signal AT ALL (beyond roundoff) @@ -325,8 +324,8 @@ static void dsp_logic( //} //preseve the needed input for the next window's overlap - memmove(src, src+u->R, - ((u->overlap_size + u->samples_gathered) - u->R)*sizeof(float) + memmove(src, src + u->R, + u->overlap_size * sizeof(float) ); } @@ -347,11 +346,12 @@ typedef union float_vector { // *automatically cycled in routine // */ // float * restrict overlap,//The size of the overlap +// const float X,//multipliar // const float * restrict H,//The freq. magnitude scalers filter // const float * restrict W,//The windowing function // fftwf_complex * restrict output_window,//The transformed window'd src // struct userdata *u){//Collection of constants - //float_vector_t x = {u->X, u->X, u->X, u->X}; + //float_vector_t x = {X, X, X, X}; // const size_t window_size = PA_ROUND_UP(u->window_size,v_size); // const size_t fft_h = PA_ROUND_UP(FILTER_SIZE, v_size / 2); // //const size_t R = PA_ROUND_UP(u->R, v_size); @@ -430,25 +430,33 @@ typedef union float_vector { // //} // // //preseve the needed input for the next window's overlap -// memmove(src, src+u->R, -// ((u->overlap_size+u->samples_gathered)+-u->R)*sizeof(float) +// memmove(src, src + u->R, +// u->overlap_size * sizeof(float) // ); //} static void process_samples(struct userdata *u, pa_memchunk *tchunk){ size_t fs=pa_frame_size(&(u->sink->sample_spec)); float *dst; + unsigned a_i; + float *H, X; pa_assert(u->samples_gathered >= u->R); tchunk->index = 0; tchunk->length = u->R * fs; tchunk->memblock = pa_memblock_new(u->sink->core->mempool, tchunk->length); dst = ((float*)pa_memblock_acquire(tchunk->memblock)); + /* set the H filter */ + a_i = pa_aupdate_read_begin(u->a_H); + X = u->Xs[a_i]; + H = u->Hs[a_i]; + for(size_t c=0;c < u->channels; c++) { dsp_logic( u->work_buffer, u->input[c], u->overlap_accum[c], - u->H, + X, + H, u->W, u->output_window, u @@ -465,6 +473,8 @@ static void process_samples(struct userdata *u, pa_memchunk *tchunk){ } pa_memblock_release(tchunk->memblock); u->samples_gathered -= u->R; + + pa_aupdate_read_end(u->a_H); } static void initialize_buffer(struct userdata *u, pa_memchunk *in){ @@ -492,7 +502,7 @@ static void input_buffer(struct userdata *u, pa_memchunk *in){ pa_assert_se( u->input[c]+u->samples_gathered+samples <= u->input[c]+u->window_size ); - pa_sample_clamp(PA_SAMPLE_FLOAT32NE, u->input[c]+u->overlap_size+u->samples_gathered, sizeof(float), src + c, fs, samples); + pa_sample_clamp(PA_SAMPLE_FLOAT32NE, u->input[c]+u->samples_gathered, sizeof(float), src + c, fs, samples); } u->samples_gathered += samples; pa_memblock_release(in->memblock); @@ -503,7 +513,6 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk struct userdata *u; size_t fs; struct timeval start, end; - unsigned a_i; pa_memchunk tchunk; pa_sink_input_assert_ref(i); pa_assert_se(u = i->userdata); @@ -554,15 +563,11 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk pa_assert(u->fft_size >= u->window_size); pa_assert(u->R < u->window_size); /* set the H filter */ - a_i = pa_aupdate_read_begin(u->a_H); - u->X = u->Xs[a_i]; - u->H = u->Hs[a_i]; pa_rtclock_get(&start); /* process a block */ process_samples(u, chunk); pa_rtclock_get(&end); pa_log_debug("Took %0.6f seconds to process", (double) pa_timeval_diff(&end, &start) / PA_USEC_PER_SEC); - pa_aupdate_read_end(u->a_H); pa_assert(chunk->memblock); //pa_log_debug("gave %ld", chunk->length/fs); @@ -1162,7 +1167,6 @@ enum equalizer_handler_index { EQUALIZER_HANDLER_FILTERSAMPLERATE, EQUALIZER_HANDLER_N_COEFS, EQUALIZER_HANDLER_FILTER, - EQUALIZER_HANDLER_PREAMP, EQUALIZER_HANDLER_MAX }; -- cgit From b028e4e917441f138197a5c6a7b86b50b26bc35b Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Wed, 19 Aug 2009 23:22:33 -0700 Subject: module-equalizer-sink: per-channel filtering support + profiles, easier default configuration --- src/modules/module-equalizer-sink.c | 521 +++++++++++++++++++++++++----------- 1 file changed, 371 insertions(+), 150 deletions(-) (limited to 'src') diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c index 67a3d74e..98b6b89b 100755 --- a/src/modules/module-equalizer-sink.c +++ b/src/modules/module-equalizer-sink.c @@ -110,15 +110,16 @@ struct userdata { fftwf_plan forward_plan, inverse_plan; //size_t samplings; - float Xs[2]; - float *Hs[2];//thread updatable copies of the freq response filters (magintude based) - pa_aupdate *a_H; + float **Xs; + float ***Hs;//thread updatable copies of the freq response filters (magintude based) + pa_aupdate **a_H; pa_memchunk conv_buffer; pa_memblockq *input_q; pa_bool_t first_iteration; pa_dbus_protocol *dbus_protocol; char *dbus_path; + pa_bool_t set_default; pa_database *database; }; @@ -129,6 +130,7 @@ static const char* const valid_modargs[] = { "master", "format", "rate", + "set_default", "channels", "channel_map", NULL @@ -138,8 +140,10 @@ static const char* const valid_modargs[] = { #define v_size 4 #define SINKLIST "equalized_sinklist" #define EQDB "equalizer_db" +#define EQ_STATE_DB "equalizer-state" #define FILTER_SIZE (u->fft_size / 2 + 1) -#define PROFILE_SIZE (FILTER_SIZE + 1) +#define CHANNEL_PROFILE_SIZE (FILTER_SIZE + 1) +#define STATE_SIZE (CHANNEL_PROFILE_SIZE * u->channels) static void dbus_init(struct userdata *u); static void dbus_done(struct userdata *u); @@ -445,12 +449,11 @@ static void process_samples(struct userdata *u, pa_memchunk *tchunk){ tchunk->length = u->R * fs; tchunk->memblock = pa_memblock_new(u->sink->core->mempool, tchunk->length); dst = ((float*)pa_memblock_acquire(tchunk->memblock)); - /* set the H filter */ - a_i = pa_aupdate_read_begin(u->a_H); - X = u->Xs[a_i]; - H = u->Hs[a_i]; for(size_t c=0;c < u->channels; c++) { + a_i = pa_aupdate_read_begin(u->a_H[c]); + X = u->Xs[c][a_i]; + H = u->Hs[c][a_i]; dsp_logic( u->work_buffer, u->input[c], @@ -461,6 +464,7 @@ static void process_samples(struct userdata *u, pa_memchunk *tchunk){ u->output_window, u ); + pa_aupdate_read_end(u->a_H[c]); if(u->first_iteration){ /* The windowing function will make the audio ramped in, as a cheap fix we can * undo the windowing (for non-zero window values) @@ -473,8 +477,6 @@ static void process_samples(struct userdata *u, pa_memchunk *tchunk){ } pa_memblock_release(tchunk->memblock); u->samples_gathered -= u->R; - - pa_aupdate_read_end(u->a_H); } static void initialize_buffer(struct userdata *u, pa_memchunk *in){ @@ -538,6 +540,7 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk //pa_sink_render_into(u->sink, buffer); while(pa_memblockq_peek(u->input_q, &tchunk) < 0){ pa_sink_render(u->sink, input_remaining*fs, &tchunk); + //pa_sink_render_full(u->sink, input_remaining*fs, &tchunk); pa_assert(tchunk.memblock); pa_memblockq_push(u->input_q, &tchunk); pa_memblock_unref(tchunk.memblock); @@ -699,6 +702,9 @@ static void sink_input_attach_cb(pa_sink_input *i) { //TODO: this guy causes dropouts constantly+rewinds, it's unusable //pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->master->thread_info.max_latency); pa_sink_attach_within_thread(u->sink); + if(u->set_default){ + pa_namereg_set_default_sink(u->module->core, u->sink); + } } /* Called from main context */ @@ -739,22 +745,22 @@ static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t s } } -static void save_profile(struct userdata *u, char *name){ +static void save_profile(struct userdata *u, size_t channel, char *name){ unsigned a_i; - const size_t profile_size = PROFILE_SIZE * sizeof(float); + const size_t profile_size = CHANNEL_PROFILE_SIZE * sizeof(float); float *H_n, *profile; const float *H; pa_datum key, data; profile = pa_xnew0(float, profile_size); - a_i = pa_aupdate_read_begin(u->a_H); + a_i = pa_aupdate_read_begin(u->a_H[channel]); + profile[0] = u->Xs[a_i][channel]; + H = u->Hs[channel][a_i]; H_n = profile + 1; - H = u->Hs[a_i]; - profile[0] = u->Xs[a_i]; for(size_t i = 0 ; i <= FILTER_SIZE; ++i){ - //H_n[i] = H[i] * u->fft_size; - H_n[i] = H[i]; + H_n[i] = H[i] * u->fft_size; + //H_n[i] = H[i]; } - pa_aupdate_read_end(u->a_H); + pa_aupdate_read_end(u->a_H[channel]); key.data=name; key.size = strlen(key.data); data.data = profile; @@ -764,9 +770,38 @@ static void save_profile(struct userdata *u, char *name){ } static void save_state(struct userdata *u){ - char *state_name = pa_sprintf_malloc("%s-previous-state", u->name); - save_profile(u, state_name); - pa_xfree(state_name); + unsigned a_i; + const size_t state_size = STATE_SIZE * sizeof(float); + float *H_n, *state; + float *H; + pa_datum key, data; + pa_database *database; + char *dbname; + char *state_name = u->name; + state = pa_xnew0(float, STATE_SIZE); + + for(size_t c = 0; c < u->channels; ++c){ + a_i = pa_aupdate_read_begin(u->a_H[c]); + state[c * CHANNEL_PROFILE_SIZE] = u->Xs[a_i][c]; + H = u->Hs[c][a_i]; + H_n = state + c * CHANNEL_PROFILE_SIZE + 1; + memcpy(H_n, H, FILTER_SIZE * sizeof(float)); + pa_aupdate_read_end(u->a_H[c]); + } + + key.data = state_name; + key.size = strlen(key.data); + data.data = state; + data.size = state_size; + //thread safety for 0.9.17? + pa_assert_se(dbname = pa_state_path(EQ_STATE_DB, TRUE)); + pa_assert_se(database = pa_database_open(dbname, TRUE)); + pa_xfree(dbname); + + pa_database_set(database, &key, &data, TRUE); + pa_database_sync(database); + pa_database_close(database); + pa_xfree(state); } static void remove_profile(pa_core *c, char *name){ @@ -779,19 +814,20 @@ static void remove_profile(pa_core *c, char *name){ pa_database_sync(database); } -static const char* load_profile(struct userdata *u, char *name){ +static const char* load_profile(struct userdata *u, size_t channel, char *name){ unsigned a_i; pa_datum key, value; - const size_t profile_size = PROFILE_SIZE * sizeof(float); + const size_t profile_size = CHANNEL_PROFILE_SIZE * sizeof(float); key.data = name; key.size = strlen(key.data); if(pa_database_get(u->database, &key, &value) != NULL){ if(value.size == profile_size){ - float *H = (float *) value.data; - a_i = pa_aupdate_write_begin(u->a_H); - u->Xs[a_i] = H[0]; - memcpy(u->Hs[a_i], H + 1, (FILTER_SIZE) * sizeof(float)); - pa_aupdate_write_end(u->a_H); + float *profile = (float *) value.data; + a_i = pa_aupdate_write_begin(u->a_H[channel]); + u->Xs[channel][a_i] = profile[0]; + memcpy(u->Hs[channel][a_i], profile + 1, CHANNEL_PROFILE_SIZE * sizeof(float)); + fix_filter(u->Hs[channel][a_i], u->fft_size); + pa_aupdate_write_end(u->a_H[channel]); }else{ return "incompatible size"; } @@ -800,13 +836,39 @@ static const char* load_profile(struct userdata *u, char *name){ return "profile doesn't exist"; } return NULL; - //fix_filter(u->H, u->fft_size); } static void load_state(struct userdata *u){ - char *state_name=pa_sprintf_malloc("%s-previous-state", u->name); - load_profile(u,state_name); - pa_xfree(state_name); + unsigned a_i; + float *H; + pa_datum key, value; + pa_database *database; + char *dbname; + char *state_name = u->name; + + pa_assert_se(dbname = pa_state_path(EQ_STATE_DB, FALSE)); + database = pa_database_open(dbname, FALSE); + pa_xfree(dbname); + if(!database){ + return; + } + + key.data = state_name; + key.size = strlen(key.data); + + if(pa_database_get(database, &key, &value) != NULL){ + size_t states = PA_MIN(value.size / (CHANNEL_PROFILE_SIZE * sizeof(float)), u->channels); + float *state = (float *) value.data; + for(size_t c = 0; c < states; ++c){ + a_i = pa_aupdate_write_begin(u->a_H[c]); + H = state + c * CHANNEL_PROFILE_SIZE + 1; + u->Xs[c][a_i] = state[c * CHANNEL_PROFILE_SIZE]; + memcpy(u->Hs[c][a_i], H, FILTER_SIZE * sizeof(float)); + pa_aupdate_write_end(u->a_H[c]); + } + pa_datum_free(&value); + } + pa_database_close(database); } /* Called from main context */ @@ -865,8 +927,12 @@ int pa__init(pa_module*m) { } if (!(master = pa_namereg_get(m->core, pa_modargs_get_value(ma, "master", NULL), PA_NAMEREG_SINK))) { - pa_log("Master sink not found"); - goto fail; + pa_log("Master sink not found, trying default"); + master = pa_namereg_get_default_sink(m->core); + if(!master){ + pa_log("no default sink found!"); + goto fail; + } } ss = master->sample_spec; @@ -882,6 +948,9 @@ int pa__init(pa_module*m) { u->module = m; m->userdata = u; + u->set_default = TRUE; + u->set_default = pa_modargs_get_value_boolean(ma, "set_default", &u->set_default); + u->channels = ss.channels; u->fft_size = pow(2, ceil(log(ss.rate)/log(2))); pa_log_debug("fft size: %ld", u->fft_size); @@ -889,10 +958,16 @@ int pa__init(pa_module*m) { u->R = (u->window_size + 1) / 2; u->overlap_size = u->window_size - u->R; u->samples_gathered = 0; - u->a_H = pa_aupdate_new(); + u->a_H = pa_xnew0(pa_aupdate *, u->channels); u->latency = u->window_size - u->R; - for(size_t i = 0; i < 2; ++i){ - u->Hs[i] = alloc((FILTER_SIZE), sizeof(float)); + u->Xs = pa_xnew0(float *, u->channels); + u->Hs = pa_xnew0(float **, u->channels); + for(size_t c = 0; c < u->channels; ++c){ + u->Xs[c] = pa_xnew0(float, 2); + u->Hs[c] = pa_xnew0(float *, 2); + for(size_t i = 0; i < 2; ++i){ + u->Hs[c][i] = alloc((FILTER_SIZE), sizeof(float)); + } } u->W = alloc(u->window_size, sizeof(float)); u->work_buffer = alloc(u->fft_size, sizeof(float)); @@ -900,6 +975,7 @@ int pa__init(pa_module*m) { u->input = pa_xnew0(float *, u->channels); u->overlap_accum = pa_xnew0(float *, u->channels); for(size_t c = 0; c < u->channels; ++c){ + u->a_H[c] = pa_aupdate_new(); u->input[c] = alloc(u->window_size, sizeof(float)); memset(u->input[c], 0, (u->window_size)*sizeof(float)); u->overlap_accum[c] = alloc(u->overlap_size, sizeof(float)); @@ -989,14 +1065,16 @@ int pa__init(pa_module*m) { dbus_init(u); //default filter to these - a_i = pa_aupdate_write_begin(u->a_H); - H = u->Hs[a_i]; - u->Xs[a_i] = 1.0f; - for(size_t i = 0; i < FILTER_SIZE; ++i){ - H[i] = 1.0 / sqrtf(2.0f); + for(size_t c = 0; c< u->channels; ++c){ + a_i = pa_aupdate_write_begin(u->a_H[c]); + H = u->Hs[c][a_i]; + u->Xs[c][a_i] = 1.0f; + for(size_t i = 0; i < FILTER_SIZE; ++i){ + H[i] = 1.0 / sqrtf(2.0f); + } + fix_filter(H, u->fft_size); + pa_aupdate_write_end(u->a_H[c]); } - fix_filter(H, u->fft_size); - pa_aupdate_write_end(u->a_H); //load old parameters load_state(u); @@ -1049,23 +1127,30 @@ void pa__done(pa_module*m) { if (u->sink) pa_sink_unref(u->sink); - pa_aupdate_free(u->a_H); pa_memblockq_free(u->input_q); fftwf_destroy_plan(u->inverse_plan); fftwf_destroy_plan(u->forward_plan); pa_xfree(u->output_window); for(size_t c=0; c < u->channels; ++c){ + pa_aupdate_free(u->a_H[c]); pa_xfree(u->overlap_accum[c]); pa_xfree(u->input[c]); } + pa_xfree(u->a_H); pa_xfree(u->overlap_accum); pa_xfree(u->input); pa_xfree(u->work_buffer); pa_xfree(u->W); - for(size_t i = 0; i < 2; ++i){ - pa_xfree(u->Hs[i]); + for(size_t c = 0; c < u->channels; ++c){ + pa_xfree(u->Xs[c]); + for(size_t i = 0; i < 2; ++i){ + pa_xfree(u->Hs[c][i]); + } + pa_xfree(u->Hs[c]); } + pa_xfree(u->Xs); + pa_xfree(u->Hs); pa_xfree(u->name); @@ -1088,11 +1173,12 @@ static void equalizer_get_revision(DBusConnection *conn, DBusMessage *msg, void static void equalizer_get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *_u); static void equalizer_get_filter_rate(DBusConnection *conn, DBusMessage *msg, void *_u); static void equalizer_get_n_coefs(DBusConnection *conn, DBusMessage *msg, void *_u); -static void equalizer_get_filter(DBusConnection *conn, DBusMessage *msg, void *_u); -static void equalizer_set_filter(DBusConnection *conn, DBusMessage *msg, void *_u); +static void equalizer_get_n_channels(DBusConnection *conn, DBusMessage *msg, void *_u); static void equalizer_get_all(DBusConnection *conn, DBusMessage *msg, void *_u); static void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, void *_u); static void equalizer_handle_get_filter_points(DBusConnection *conn, DBusMessage *msg, void *_u); +static void equalizer_handle_get_filter(DBusConnection *conn, DBusMessage *msg, void *_u); +static void equalizer_handle_set_filter(DBusConnection *conn, DBusMessage *msg, void *_u); static void equalizer_handle_save_profile(DBusConnection *conn, DBusMessage *msg, void *_u); static void equalizer_handle_load_profile(DBusConnection *conn, DBusMessage *msg, void *_u); enum manager_method_index { @@ -1158,6 +1244,8 @@ enum equalizer_method_index { EQUALIZER_METHOD_SEED_FILTER, EQUALIZER_METHOD_SAVE_PROFILE, EQUALIZER_METHOD_LOAD_PROFILE, + EQUALIZER_METHOD_SET_FILTER, + EQUALIZER_METHOD_GET_FILTER, EQUALIZER_METHOD_MAX }; @@ -1166,25 +1254,41 @@ enum equalizer_handler_index { EQUALIZER_HANDLER_SAMPLERATE, EQUALIZER_HANDLER_FILTERSAMPLERATE, EQUALIZER_HANDLER_N_COEFS, - EQUALIZER_HANDLER_FILTER, + EQUALIZER_HANDLER_N_CHANNELS, EQUALIZER_HANDLER_MAX }; pa_dbus_arg_info filter_points_args[]={ + {"channel", "u","in"}, {"xs", "au","in"}, {"ys", "ad","out"}, - {"preamp", "d","out"}, + {"preamp", "d","out"} }; pa_dbus_arg_info seed_filter_args[]={ + {"channel", "u","in"}, {"xs", "au","in"}, {"ys", "ad","in"}, - {"preamp", "d","in"}, + {"preamp", "d","in"} +}; + +pa_dbus_arg_info set_filter_args[]={ + {"channel", "u","in"}, + {"ys", "ad","in"}, + {"preamp", "d","in"} +}; +pa_dbus_arg_info get_filter_args[]={ + {"channel", "u","in"}, + {"ys", "ad","out"}, + {"preamp", "d","out"} }; + pa_dbus_arg_info save_profile_args[]={ - {"name", "s","in"}, + {"channel", "u","in"}, + {"name", "s","in"} }; pa_dbus_arg_info load_profile_args[]={ - {"name", "s","in"}, + {"channel", "u","in"}, + {"name", "s","in"} }; static pa_dbus_method_handler equalizer_methods[EQUALIZER_METHOD_MAX]={ @@ -1198,6 +1302,16 @@ static pa_dbus_method_handler equalizer_methods[EQUALIZER_METHOD_MAX]={ .arguments=filter_points_args, .n_arguments=sizeof(filter_points_args)/sizeof(pa_dbus_arg_info), .receive_cb=equalizer_handle_get_filter_points}, + [EQUALIZER_METHOD_SET_FILTER]{ + .method_name="SetFilter", + .arguments=set_filter_args, + .n_arguments=sizeof(set_filter_args)/sizeof(pa_dbus_arg_info), + .receive_cb=equalizer_handle_set_filter}, + [EQUALIZER_METHOD_GET_FILTER]{ + .method_name="GetFilter", + .arguments=get_filter_args, + .n_arguments=sizeof(get_filter_args)/sizeof(pa_dbus_arg_info), + .receive_cb=equalizer_handle_get_filter}, [EQUALIZER_METHOD_SAVE_PROFILE]{ .method_name="SaveProfile", .arguments=save_profile_args, @@ -1207,7 +1321,7 @@ static pa_dbus_method_handler equalizer_methods[EQUALIZER_METHOD_MAX]={ .method_name="LoadProfile", .arguments=load_profile_args, .n_arguments=sizeof(load_profile_args)/sizeof(pa_dbus_arg_info), - .receive_cb=equalizer_handle_load_profile}, + .receive_cb=equalizer_handle_load_profile} }; static pa_dbus_property_handler equalizer_handlers[EQUALIZER_HANDLER_MAX]={ @@ -1215,7 +1329,7 @@ static pa_dbus_property_handler equalizer_handlers[EQUALIZER_HANDLER_MAX]={ [EQUALIZER_HANDLER_SAMPLERATE]{.property_name="SampleRate",.type="u",.get_cb=equalizer_get_sample_rate,.set_cb=NULL}, [EQUALIZER_HANDLER_FILTERSAMPLERATE]{.property_name="FilterSampleRate",.type="u",.get_cb=equalizer_get_filter_rate,.set_cb=NULL}, [EQUALIZER_HANDLER_N_COEFS]{.property_name="NFilterCoefficients",.type="u",.get_cb=equalizer_get_n_coefs,.set_cb=NULL}, - [EQUALIZER_HANDLER_FILTER]{.property_name="Filter",.type="ad",.get_cb=equalizer_get_filter,.set_cb=equalizer_set_filter} + [EQUALIZER_HANDLER_N_CHANNELS]{.property_name="NChannels",.type="u",.get_cb=equalizer_get_n_channels,.set_cb=NULL}, }; enum equalizer_signal_index{ @@ -1249,15 +1363,15 @@ void dbus_init(struct userdata *u){ pa_dbus_protocol_add_interface(u->dbus_protocol, u->dbus_path, &equalizer_info, u); sink_list = pa_shared_get(u->sink->core, SINKLIST); - u->database=pa_shared_get(u->sink->core, EQDB); - if(sink_list==NULL){ + u->database = pa_shared_get(u->sink->core, EQDB); + if(sink_list == NULL){ char *dbname; sink_list=pa_idxset_new(&pa_idxset_trivial_hash_func, &pa_idxset_trivial_compare_func); pa_shared_set(u->sink->core, SINKLIST, sink_list); - pa_assert_se(dbname = pa_state_path("equalizers", TRUE)); + pa_assert_se(dbname = pa_state_path("equalizer-presets", FALSE)); pa_assert_se(u->database = pa_database_open(dbname, TRUE)); pa_xfree(dbname); - pa_shared_set(u->sink->core,EQDB,u->database); + pa_shared_set(u->sink->core, EQDB, u->database); pa_dbus_protocol_add_interface(u->dbus_protocol, MANAGER_PATH, &manager_info, u->sink->core); pa_dbus_protocol_register_extension(u->dbus_protocol, EXTNAME); } @@ -1448,7 +1562,7 @@ void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, void * DBusError error; DBusMessage *signal = NULL; float *ys; - uint32_t *xs; + uint32_t *xs, channel, r_channel; double *_ys, preamp; unsigned x_npoints, y_npoints, a_i; float *H; @@ -1460,6 +1574,7 @@ void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, void * dbus_error_init(&error); if(!dbus_message_get_args(msg, &error, + DBUS_TYPE_UINT32, &channel, DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &xs, &x_npoints, DBUS_TYPE_ARRAY, DBUS_TYPE_DOUBLE, &_ys, &y_npoints, DBUS_TYPE_DOUBLE, &preamp, @@ -1468,6 +1583,11 @@ void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, void * dbus_error_free(&error); return; } + if(channel > u->channels){ + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "invalid channel: %d", channel); + dbus_error_free(&error); + return; + } for(size_t i = 0; i < x_npoints; ++i){ if(xs[i] >= FILTER_SIZE){ points_good = FALSE; @@ -1478,7 +1598,6 @@ void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, void * pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "xs must be monotonic and 0<=x<=%ld", u->fft_size / 2); dbus_error_free(&error); return; - }else if(x_npoints != y_npoints || x_npoints < 2 || x_npoints > FILTER_SIZE ){ pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "xs and ys must be the same length and 2<=l<=%ld!", FILTER_SIZE); dbus_error_free(&error); @@ -1493,14 +1612,25 @@ void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, void * for(uint32_t i = 0; i < x_npoints; ++i){ ys[i] = (float) _ys[i]; } - a_i = pa_aupdate_write_begin(u->a_H); - H = u->Hs[a_i]; - u->Xs[a_i] = preamp; + r_channel = channel == u->channels ? 0 : channel; + a_i = pa_aupdate_write_begin(u->a_H[r_channel]); + H = u->Hs[r_channel][a_i]; + u->Xs[r_channel][a_i] = preamp; interpolate(H, FILTER_SIZE, xs, ys, x_npoints); fix_filter(H, u->fft_size); - pa_aupdate_write_end(u->a_H); + if(channel == u->channels){ + for(size_t c = 1; c < u->channels; ++c){ + unsigned b_i = pa_aupdate_write_begin(u->a_H[c]); + float *H_p = u->Hs[c][b_i]; + u->Xs[c][b_i] = preamp; + memcpy(H_p, H, FILTER_SIZE * sizeof(float)); + pa_aupdate_write_end(u->a_H[c]); + } + } + pa_aupdate_write_end(u->a_H[r_channel]); pa_xfree(ys); + //Stupid for IO reasons? Add a save signal to dbus instead //save_state(u); @@ -1513,28 +1643,34 @@ void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, void * void equalizer_handle_get_filter_points(DBusConnection *conn, DBusMessage *msg, void *_u) { struct userdata *u = (struct userdata *) _u; - DBusError error; - uint32_t *xs; + uint32_t *xs, channel, r_channel; double *ys, preamp; unsigned x_npoints, a_i; float *H; pa_bool_t points_good=TRUE; DBusMessage *reply = NULL; DBusMessageIter msg_iter; + DBusError error; pa_assert(conn); pa_assert(msg); pa_assert(u); dbus_error_init(&error); - if(!dbus_message_get_args(msg, &error, + DBUS_TYPE_UINT32, &channel, DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &xs, &x_npoints, DBUS_TYPE_INVALID)){ pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message); dbus_error_free(&error); return; } + if(channel > u->channels){ + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "invalid channel: %d", channel); + dbus_error_free(&error); + return; + } + for(size_t i = 0; i < x_npoints; ++i){ if(xs[i] >= FILTER_SIZE){ points_good=FALSE; @@ -1548,14 +1684,15 @@ void equalizer_handle_get_filter_points(DBusConnection *conn, DBusMessage *msg, return; } + r_channel = channel == u->channels ? 0 : channel; ys = pa_xmalloc(x_npoints * sizeof(double)); - a_i = pa_aupdate_read_begin(u->a_H); - H = u->Hs[a_i]; - preamp = u->Xs[a_i]; + a_i = pa_aupdate_read_begin(u->a_H[r_channel]); + H = u->Hs[r_channel][a_i]; + preamp = u->Xs[r_channel][a_i]; for(uint32_t i = 0; i < x_npoints; ++i){ ys[i] = H[xs[i]] * u->fft_size; } - pa_aupdate_read_end(u->a_H); + pa_aupdate_read_end(u->a_H[r_channel]); pa_assert_se((reply = dbus_message_new_method_return(msg))); dbus_message_iter_init_append(reply, &msg_iter); @@ -1568,9 +1705,128 @@ void equalizer_handle_get_filter_points(DBusConnection *conn, DBusMessage *msg, pa_xfree(ys); } +static void get_filter(struct userdata *u, size_t channel, double **H_, double *preamp){ + float *H; + unsigned a_i; + size_t r_channel = channel == u->channels ? 0 : channel; + *H_ = pa_xnew0(double, FILTER_SIZE); + a_i = pa_aupdate_read_begin(u->a_H[r_channel]); + H = u->Hs[r_channel][a_i]; + for(size_t i = 0;i < FILTER_SIZE; ++i){ + (*H_)[i] = H[i] * u->fft_size; + } + *preamp = u->Xs[r_channel][a_i]; + + pa_aupdate_read_end(u->a_H[r_channel]); +} + +void equalizer_handle_get_filter(DBusConnection *conn, DBusMessage *msg, void *_u){ + struct userdata *u; + unsigned n_coefs; + uint32_t channel; + double *H_, preamp; + DBusMessage *reply = NULL; + DBusMessageIter msg_iter; + DBusError error; + pa_assert_se(u = (struct userdata *) _u); + pa_assert(conn); + pa_assert(msg); + + dbus_error_init(&error); + if(!dbus_message_get_args(msg, &error, + DBUS_TYPE_UINT32, &channel, + DBUS_TYPE_INVALID)){ + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message); + dbus_error_free(&error); + return; + } + if(channel > u->channels){ + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "invalid channel: %d", channel); + dbus_error_free(&error); + return; + } + + n_coefs = CHANNEL_PROFILE_SIZE; + pa_assert(conn); + pa_assert(msg); + get_filter(u, channel, &H_, &preamp); + pa_assert_se((reply = dbus_message_new_method_return(msg))); + dbus_message_iter_init_append(reply, &msg_iter); + + pa_dbus_append_basic_array(&msg_iter, DBUS_TYPE_DOUBLE, H_, n_coefs); + pa_dbus_append_basic_variant(&msg_iter, DBUS_TYPE_DOUBLE, &preamp); + + pa_assert_se(dbus_connection_send(conn, reply, NULL)); + dbus_message_unref(reply); + pa_xfree(H_); +} + +static void set_filter(struct userdata *u, size_t channel, double *H_, double preamp){ + unsigned a_i; + size_t r_channel = channel == u->channels ? 0 : channel; + float *H; + //all channels + a_i = pa_aupdate_write_begin(u->a_H[r_channel]); + u->Xs[r_channel][a_i] = (float) preamp; + H = u->Hs[r_channel][a_i]; + for(size_t i = 0; i < FILTER_SIZE; ++i){ + H[i] = (float) H_[i]; + } + fix_filter(H, u->fft_size); + if(channel == u->channels){ + for(size_t c = 1; c < u->channels; ++c){ + unsigned b_i = pa_aupdate_write_begin(u->a_H[c]); + u->Xs[c][b_i] = u->Xs[r_channel][a_i]; + memcpy(u->Hs[c][b_i], u->Hs[r_channel][a_i], FILTER_SIZE * sizeof(float)); + pa_aupdate_write_end(u->a_H[c]); + } + } + pa_aupdate_write_end(u->a_H[r_channel]); +} + +void equalizer_handle_set_filter(DBusConnection *conn, DBusMessage *msg, void *_u){ + struct userdata *u; + double *H, preamp; + uint32_t channel; + unsigned _n_coefs; + DBusMessage *signal = NULL; + DBusError error; + pa_assert_se(u = (struct userdata *) _u); + pa_assert(conn); + pa_assert(msg); + + dbus_error_init(&error); + if(!dbus_message_get_args(msg, &error, + DBUS_TYPE_UINT32, &channel, + DBUS_TYPE_ARRAY, DBUS_TYPE_DOUBLE, &H, &_n_coefs, + DBUS_TYPE_DOUBLE, &preamp, + DBUS_TYPE_INVALID)){ + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message); + dbus_error_free(&error); + return; + } + if(channel > u->channels){ + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "invalid channel: %d", channel); + dbus_error_free(&error); + return; + } + if(_n_coefs != FILTER_SIZE){ + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "This filter takes exactly %ld coefficients, you gave %d", FILTER_SIZE, _n_coefs); + return; + } + set_filter(u, channel, H, preamp); + + pa_dbus_send_empty_reply(conn, msg); + + pa_assert_se((signal = dbus_message_new_signal(u->dbus_path, EQUALIZER_IFACE, equalizer_signals[EQUALIZER_SIGNAL_FILTER_CHANGED].name))); + pa_dbus_protocol_send_signal(u->dbus_protocol, signal); + dbus_message_unref(signal); +} + void equalizer_handle_save_profile(DBusConnection *conn, DBusMessage *msg, void *_u) { struct userdata *u = (struct userdata *) _u; char *name; + uint32_t channel, r_channel; DBusMessage *signal = NULL; DBusError error; pa_assert(conn); @@ -1579,13 +1835,20 @@ void equalizer_handle_save_profile(DBusConnection *conn, DBusMessage *msg, void dbus_error_init(&error); if(!dbus_message_get_args(msg, &error, - DBUS_TYPE_STRING, &name, + DBUS_TYPE_UINT32, &channel, + DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID)){ pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message); dbus_error_free(&error); return; } - save_profile(u,name); + if(channel > u->channels){ + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "invalid channel: %d", channel); + dbus_error_free(&error); + return; + } + r_channel = channel == u->channels ? 0 : channel; + save_profile(u, r_channel, name); pa_dbus_send_empty_reply(conn, msg); pa_assert_se((signal = dbus_message_new_signal(MANAGER_PATH, MANAGER_IFACE, manager_signals[MANAGER_SIGNAL_PROFILES_CHANGED].name))); @@ -1594,9 +1857,10 @@ void equalizer_handle_save_profile(DBusConnection *conn, DBusMessage *msg, void } void equalizer_handle_load_profile(DBusConnection *conn, DBusMessage *msg, void *_u) { - struct userdata *u=(struct userdata *) _u; + struct userdata *u = (struct userdata *) _u; char *name; DBusError error; + uint32_t channel, r_channel; const char *err_msg = NULL; DBusMessage *signal = NULL; @@ -1606,18 +1870,31 @@ void equalizer_handle_load_profile(DBusConnection *conn, DBusMessage *msg, void dbus_error_init(&error); if(!dbus_message_get_args(msg, &error, - DBUS_TYPE_STRING, &name, + DBUS_TYPE_UINT32, &channel, + DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID)){ pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message); dbus_error_free(&error); return; } - err_msg = load_profile(u, name); + if(channel > u->channels){ + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "invalid channel: %d", channel); + dbus_error_free(&error); + return; + } + r_channel = channel == u->channels ? 0 : channel; + + err_msg = load_profile(u, r_channel, name); if(err_msg != NULL){ pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, "error loading profile %s: %s", name, err_msg); dbus_error_free(&error); return; } + if(channel == u->channels){ + for(uint32_t c = 1; c < u->channels; ++c){ + load_profile(u, c, name); + } + } pa_dbus_send_empty_reply(conn, msg); pa_assert_se((signal = dbus_message_new_signal(u->dbus_path, EQUALIZER_IFACE, equalizer_signals[EQUALIZER_SIGNAL_FILTER_CHANGED].name))); @@ -1630,6 +1907,17 @@ void equalizer_get_revision(DBusConnection *conn, DBusMessage *msg, void *_u){ pa_dbus_send_basic_value_reply(conn, msg, DBUS_TYPE_UINT32, &rev); } +void equalizer_get_n_channels(DBusConnection *conn, DBusMessage *msg, void *_u){ + struct userdata *u; + uint32_t channels; + pa_assert_se(u = (struct userdata *) _u); + pa_assert(conn); + pa_assert(msg); + + channels = (uint32_t) u->channels; + pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &channels); +} + void equalizer_get_n_coefs(DBusConnection *conn, DBusMessage *msg, void *_u){ struct userdata *u; uint32_t n_coefs; @@ -1637,7 +1925,7 @@ void equalizer_get_n_coefs(DBusConnection *conn, DBusMessage *msg, void *_u){ pa_assert(conn); pa_assert(msg); - n_coefs = (uint32_t) PROFILE_SIZE; + n_coefs = (uint32_t) CHANNEL_PROFILE_SIZE; pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &n_coefs); } @@ -1663,84 +1951,19 @@ void equalizer_get_filter_rate(DBusConnection *conn, DBusMessage *msg, void *_u) pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &fft_size); } -static double * get_filter(struct userdata *u){ - float *H; - double *H_; - unsigned a_i; - H_ = pa_xnew0(double, PROFILE_SIZE); - a_i = pa_aupdate_read_begin(u->a_H); - H = u->Hs[a_i]; - H_[0] = u->Xs[a_i]; - for(size_t i = 0;i < FILTER_SIZE; ++i){ - H_[i + 1] = H[i] * u->fft_size; - } - pa_aupdate_read_end(u->a_H); - return H_; -} - -void equalizer_get_filter(DBusConnection *conn, DBusMessage *msg, void *_u){ - struct userdata *u; - unsigned n_coefs; - double *H_; - pa_assert_se(u = (struct userdata *) _u); - - n_coefs = PROFILE_SIZE; - pa_assert(conn); - pa_assert(msg); - H_ = get_filter(u); - pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_DOUBLE, H_, n_coefs); - pa_xfree(H_); -} - -static void set_filter(struct userdata *u, double *H_){ - unsigned a_i= pa_aupdate_write_begin(u->a_H); - float *H = u->Hs[a_i]; - u->Xs[a_i] = H_[0]; - for(size_t i = 0; i < FILTER_SIZE; ++i){ - H[i] = (float) H_[i + 1]; - } - fix_filter(H + 1, u->fft_size); - pa_aupdate_write_end(u->a_H); -} - -void equalizer_set_filter(DBusConnection *conn, DBusMessage *msg, void *_u){ - struct userdata *u; - double *H; - unsigned _n_coefs; - DBusMessage *signal = NULL; - pa_assert_se(u = (struct userdata *) _u); - pa_assert(conn); - pa_assert(msg); - - if(pa_dbus_get_fixed_array_set_property_arg(conn, msg, DBUS_TYPE_DOUBLE, &H, &_n_coefs)){ - return; - } - if(_n_coefs != PROFILE_SIZE){ - pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "This filter takes exactly %ld coefficients, you gave %d", PROFILE_SIZE, _n_coefs); - return; - } - set_filter(u, H); - - pa_dbus_send_empty_reply(conn, msg); - - pa_assert_se((signal = dbus_message_new_signal(u->dbus_path, EQUALIZER_IFACE, equalizer_signals[EQUALIZER_SIGNAL_FILTER_CHANGED].name))); - pa_dbus_protocol_send_signal(u->dbus_protocol, signal); - dbus_message_unref(signal); -} - void equalizer_get_all(DBusConnection *conn, DBusMessage *msg, void *_u){ struct userdata *u; DBusMessage *reply = NULL; DBusMessageIter msg_iter, dict_iter; - uint32_t rev, n_coefs, rate, fft_size; - double *H; + uint32_t rev, n_coefs, rate, fft_size, channels; pa_assert_se(u = (struct userdata *) _u); pa_assert(msg); rev = 1; - n_coefs = (uint32_t) PROFILE_SIZE; + n_coefs = (uint32_t) CHANNEL_PROFILE_SIZE; rate = (uint32_t) u->sink->sample_spec.rate; fft_size = (uint32_t) u->fft_size; + channels = (uint32_t) u->channels; pa_assert_se((reply = dbus_message_new_method_return(msg))); dbus_message_iter_init_append(reply, &msg_iter); @@ -1750,9 +1973,7 @@ void equalizer_get_all(DBusConnection *conn, DBusMessage *msg, void *_u){ pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_SAMPLERATE].property_name, DBUS_TYPE_UINT32, &rate); pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_FILTERSAMPLERATE].property_name, DBUS_TYPE_UINT32, &fft_size); pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_N_COEFS].property_name, DBUS_TYPE_UINT32, &n_coefs); - H = get_filter(u); - pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_FILTER].property_name, DBUS_TYPE_UINT32, &H); - pa_xfree(H); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_N_CHANNELS].property_name, DBUS_TYPE_UINT32, &channels); pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter)); pa_assert_se(dbus_connection_send(conn, reply, NULL)); -- cgit From f5ceed8151214a95ca2192ed1c681eb31cb0bc98 Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Thu, 20 Aug 2009 18:24:19 -0700 Subject: module-equalizer-sink: added server side persistance of profile names --- src/modules/module-equalizer-sink.c | 221 +++++++++++++++++++++++++++++++----- 1 file changed, 190 insertions(+), 31 deletions(-) (limited to 'src') diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c index 98b6b89b..c531468d 100755 --- a/src/modules/module-equalizer-sink.c +++ b/src/modules/module-equalizer-sink.c @@ -122,6 +122,7 @@ struct userdata { pa_bool_t set_default; pa_database *database; + char **base_profiles; }; static const char* const valid_modargs[] = { @@ -143,7 +144,7 @@ static const char* const valid_modargs[] = { #define EQ_STATE_DB "equalizer-state" #define FILTER_SIZE (u->fft_size / 2 + 1) #define CHANNEL_PROFILE_SIZE (FILTER_SIZE + 1) -#define STATE_SIZE (CHANNEL_PROFILE_SIZE * u->channels) +#define FILTER_STATE_SIZE (CHANNEL_PROFILE_SIZE * u->channels) static void dbus_init(struct userdata *u); static void dbus_done(struct userdata *u); @@ -199,7 +200,7 @@ static int is_monotonic(const uint32_t *xs,size_t length){ /* Called from I/O thread context */ -static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) { +static int sink_process_msg_cb(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) { struct userdata *u = PA_SINK(o)->userdata; switch (code) { @@ -234,7 +235,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse /* Called from main context */ -static int sink_set_state(pa_sink *s, pa_sink_state_t state) { +static int sink_set_state_cb(pa_sink *s, pa_sink_state_t state) { struct userdata *u; pa_sink_assert_ref(s); @@ -249,7 +250,7 @@ static int sink_set_state(pa_sink *s, pa_sink_state_t state) { } /* Called from I/O thread context */ -static void sink_request_rewind(pa_sink *s) { +static void sink_request_rewind_cb(pa_sink *s) { struct userdata *u; pa_sink_assert_ref(s); @@ -264,7 +265,7 @@ static void sink_request_rewind(pa_sink *s) { } /* Called from I/O thread context */ -static void sink_update_requested_latency(pa_sink *s) { +static void sink_update_requested_latency_cb(pa_sink *s) { struct userdata *u; pa_sink_assert_ref(s); @@ -280,6 +281,35 @@ static void sink_update_requested_latency(pa_sink *s) { pa_sink_get_requested_latency_within_thread(s)); } +/* Called from main context */ +static void sink_set_volume_cb(pa_sink *s) { + struct userdata *u; + + pa_sink_assert_ref(s); + pa_assert_se(u = s->userdata); + + if (!PA_SINK_IS_LINKED(pa_sink_get_state(s)) || + !PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(u->sink_input))) + return; + + pa_sink_input_set_volume(u->sink_input, &s->real_volume, s->save_volume, TRUE); +} + +/* Called from main context */ +static void sink_set_mute_cb(pa_sink *s) { + struct userdata *u; + + pa_sink_assert_ref(s); + pa_assert_se(u = s->userdata); + + if (!PA_SINK_IS_LINKED(pa_sink_get_state(s)) || + !PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(u->sink_input))) + return; + + pa_sink_input_set_mute(u->sink_input, s->muted, s->save_muted); +} + + //reference implementation static void dsp_logic( float * restrict dst,//used as a temp array too, needs to be fft_length! @@ -581,6 +611,26 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk return 0; } +/* Called from main context */ +static void sink_input_volume_changed_cb(pa_sink_input *i) { + struct userdata *u; + + pa_sink_input_assert_ref(i); + pa_assert_se(u = i->userdata); + + pa_sink_volume_changed(u->sink, &i->volume); +} + +/* Called from main context */ +static void sink_input_mute_changed_cb(pa_sink_input *i) { + struct userdata *u; + + pa_sink_input_assert_ref(i); + pa_assert_se(u = i->userdata); + + pa_sink_mute_changed(u->sink, i->muted); +} + static void reset_filter(struct userdata *u){ u->samples_gathered = 0; for(size_t i = 0;i < u->channels; ++i){ @@ -745,6 +795,41 @@ static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t s } } +static void pack(char **strs, size_t len, char **packed, size_t *length){ + size_t t_len = 0; + size_t headers = (1+len) * sizeof(uint16_t); + size_t offset = sizeof(uint16_t); + for(size_t i = 0; i < len; ++i){ + t_len += strlen(strs[i]); + } + *length = headers + t_len; + *packed = pa_xmalloc0(*length); + ((uint16_t *) *packed)[0] = (uint16_t) len; + for(size_t i = 0; i < len; ++i){ + uint16_t l = strlen(strs[i]); + *((uint16_t *)(*packed + offset)) = l; + offset += sizeof(uint16_t); + memcpy(*packed + offset, strs[i], l); + offset += l; + } +} +static void unpack(char *str, size_t length, char ***strs, size_t *len){ + size_t offset = sizeof(uint16_t); + *len = ((uint16_t *)str)[0]; + *strs = pa_xnew(char *, *len); + for(size_t i = 0; i < *len; ++i){ + size_t l = *((uint16_t *)(str+offset)); + size_t e = PA_MIN(offset + l, length) - offset; + offset = PA_MIN(offset + sizeof(uint16_t), length); + if(e > 0){ + (*strs)[i] = pa_xnew(char, e + 1); + memcpy((*strs)[i], strs + offset, e); + (*strs)[i][e] = '\0'; + }else{ + (*strs)[i]=NULL; + } + } +} static void save_profile(struct userdata *u, size_t channel, char *name){ unsigned a_i; const size_t profile_size = CHANNEL_PROFILE_SIZE * sizeof(float); @@ -767,18 +852,26 @@ static void save_profile(struct userdata *u, size_t channel, char *name){ data.size = profile_size; pa_database_set(u->database, &key, &data, TRUE); pa_database_sync(u->database); + if(u->base_profiles[channel]){ + pa_xfree(u->base_profiles[channel]); + } + u->base_profiles[channel] = pa_xstrdup(name); } static void save_state(struct userdata *u){ unsigned a_i; - const size_t state_size = STATE_SIZE * sizeof(float); + const size_t filter_state_size = FILTER_STATE_SIZE * sizeof(float); float *H_n, *state; float *H; pa_datum key, data; pa_database *database; char *dbname; char *state_name = u->name; - state = pa_xnew0(float, STATE_SIZE); + char *packed; + size_t packed_length; + + pack(u->base_profiles, u->channels, &packed, &packed_length); + state = (float *) pa_xmalloc0(filter_state_size + packed_length); for(size_t c = 0; c < u->channels; ++c){ a_i = pa_aupdate_read_begin(u->a_H[c]); @@ -788,11 +881,13 @@ static void save_state(struct userdata *u){ memcpy(H_n, H, FILTER_SIZE * sizeof(float)); pa_aupdate_read_end(u->a_H[c]); } + memcpy(((char *)state) + filter_state_size, packed, packed_length); + pa_xfree(packed); key.data = state_name; key.size = strlen(key.data); data.data = state; - data.size = state_size; + data.size = filter_state_size + packed_length; //thread safety for 0.9.17? pa_assert_se(dbname = pa_state_path(EQ_STATE_DB, TRUE)); pa_assert_se(database = pa_database_open(dbname, TRUE)); @@ -828,6 +923,10 @@ static const char* load_profile(struct userdata *u, size_t channel, char *name){ memcpy(u->Hs[channel][a_i], profile + 1, CHANNEL_PROFILE_SIZE * sizeof(float)); fix_filter(u->Hs[channel][a_i], u->fft_size); pa_aupdate_write_end(u->a_H[channel]); + if(u->base_profiles[channel]){ + pa_xfree(u->base_profiles[channel]); + } + u->base_profiles[channel] = pa_xstrdup(name); }else{ return "incompatible size"; } @@ -845,7 +944,6 @@ static void load_state(struct userdata *u){ pa_database *database; char *dbname; char *state_name = u->name; - pa_assert_se(dbname = pa_state_path(EQ_STATE_DB, FALSE)); database = pa_database_open(dbname, FALSE); pa_xfree(dbname); @@ -857,14 +955,26 @@ static void load_state(struct userdata *u){ key.size = strlen(key.data); if(pa_database_get(database, &key, &value) != NULL){ - size_t states = PA_MIN(value.size / (CHANNEL_PROFILE_SIZE * sizeof(float)), u->channels); - float *state = (float *) value.data; - for(size_t c = 0; c < states; ++c){ - a_i = pa_aupdate_write_begin(u->a_H[c]); - H = state + c * CHANNEL_PROFILE_SIZE + 1; - u->Xs[c][a_i] = state[c * CHANNEL_PROFILE_SIZE]; - memcpy(u->Hs[c][a_i], H, FILTER_SIZE * sizeof(float)); - pa_aupdate_write_end(u->a_H[c]); + if(value.size > FILTER_STATE_SIZE * sizeof(float) + sizeof(uint16_t)){ + float *state = (float *) value.data; + size_t n_profs; + char **names; + for(size_t c = 0; c < u->channels; ++c){ + a_i = pa_aupdate_write_begin(u->a_H[c]); + H = state + c * CHANNEL_PROFILE_SIZE + 1; + u->Xs[c][a_i] = state[c * CHANNEL_PROFILE_SIZE]; + memcpy(u->Hs[c][a_i], H, FILTER_SIZE * sizeof(float)); + pa_aupdate_write_end(u->a_H[c]); + } + unpack(((char *)value.data) + FILTER_STATE_SIZE, value.size - FILTER_STATE_SIZE, &names, &n_profs); + n_profs = PA_MIN(n_profs, u->channels); + for(size_t c = 0; c < n_profs; ++c){ + if(u->base_profiles[c]){ + pa_xfree(u->base_profiles[c]); + } + u->base_profiles[c] = names[c]; + } + pa_xfree(names); } pa_datum_free(&value); } @@ -898,8 +1008,6 @@ static void * alloc(size_t x,size_t s){ size_t f = PA_ROUND_UP(x*s, sizeof(float)*v_size); float *t; pa_assert(f >= x*s); - //printf("requested %ld floats=%ld bytes, rem=%ld\n", x, x*sizeof(float), x*sizeof(float)%16); - //printf("giving %ld floats=%ld bytes, rem=%ld\n", f, f*sizeof(float), f*sizeof(float)%16); t = fftwf_malloc(f); memset(t, 0, f); return t; @@ -914,7 +1022,6 @@ int pa__init(pa_module*m) { pa_sink *master; pa_sink_input_new_data sink_input_data; pa_sink_new_data sink_data; - pa_bool_t *use_default = NULL; size_t fs; float *H; unsigned a_i; @@ -988,6 +1095,8 @@ int pa__init(pa_module*m) { hanning_window(u->W, u->window_size); u->first_iteration = TRUE; + u->base_profiles = pa_xnew0(char *, u->channels); + /* Create sink */ pa_sink_new_data_init(&sink_data); sink_data.driver = __FILE__; @@ -1007,7 +1116,9 @@ int pa__init(pa_module*m) { goto fail; } - u->sink = pa_sink_new(m->core, &sink_data, master->flags & (PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY)); + u->sink = pa_sink_new(m->core, &sink_data, + PA_SINK_HW_MUTE_CTRL|PA_SINK_HW_VOLUME_CTRL|PA_SINK_DECIBEL_VOLUME| + (master->flags & (PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY))); pa_sink_new_data_done(&sink_data); if (!u->sink) { @@ -1015,10 +1126,12 @@ int pa__init(pa_module*m) { goto fail; } u->name=pa_xstrdup(u->sink->name); - u->sink->parent.process_msg = sink_process_msg; - u->sink->set_state = sink_set_state; - u->sink->update_requested_latency = sink_update_requested_latency; - u->sink->request_rewind = sink_request_rewind; + u->sink->parent.process_msg = sink_process_msg_cb; + u->sink->set_state = sink_set_state_cb; + u->sink->update_requested_latency = sink_update_requested_latency_cb; + u->sink->request_rewind = sink_request_rewind_cb; + u->sink->set_volume = sink_set_volume_cb; + u->sink->set_mute = sink_set_mute_cb; u->sink->userdata = u; u->input_q = pa_memblockq_new(0, MEMBLOCKQ_MAXLENGTH, 0, fs, 1, 1, 0, &u->sink->silence); @@ -1053,6 +1166,9 @@ int pa__init(pa_module*m) { u->sink_input->state_change = sink_input_state_change_cb; u->sink_input->may_move_to = sink_input_may_move_to_cb; u->sink_input->moving = sink_input_moving_cb; + u->sink_input->volume_changed = sink_input_volume_changed_cb; + u->sink_input->mute_changed = sink_input_mute_changed_cb; + u->sink_input->userdata = u; pa_sink_put(u->sink); @@ -1060,7 +1176,6 @@ int pa__init(pa_module*m) { pa_modargs_free(ma); - pa_xfree(use_default); dbus_init(u); @@ -1084,7 +1199,6 @@ fail: if (ma) pa_modargs_free(ma); - pa_xfree(use_default); pa__done(m); @@ -1112,6 +1226,13 @@ void pa__done(pa_module*m) { dbus_done(u); + for(size_t c = 0; c < u->channels; ++c){ + if(u->base_profiles[c]){ + pa_xfree(u->base_profiles[c]); + } + } + pa_xfree(u->base_profiles); + /* See comments in sink_input_kill_cb() above regarding * destruction order! */ @@ -1181,6 +1302,7 @@ static void equalizer_handle_get_filter(DBusConnection *conn, DBusMessage *msg, static void equalizer_handle_set_filter(DBusConnection *conn, DBusMessage *msg, void *_u); static void equalizer_handle_save_profile(DBusConnection *conn, DBusMessage *msg, void *_u); static void equalizer_handle_load_profile(DBusConnection *conn, DBusMessage *msg, void *_u); +static void equalizer_handle_get_profile_name(DBusConnection *conn, DBusMessage *msg, void *_u); enum manager_method_index { MANAGER_METHOD_REMOVE_PROFILE, MANAGER_METHOD_MAX @@ -1246,6 +1368,7 @@ enum equalizer_method_index { EQUALIZER_METHOD_LOAD_PROFILE, EQUALIZER_METHOD_SET_FILTER, EQUALIZER_METHOD_GET_FILTER, + EQUALIZER_METHOD_GET_PROFILE_NAME, EQUALIZER_METHOD_MAX }; @@ -1290,6 +1413,10 @@ pa_dbus_arg_info load_profile_args[]={ {"channel", "u","in"}, {"name", "s","in"} }; +pa_dbus_arg_info base_profile_name_args[]={ + {"channel", "u","in"}, + {"name", "s","out"} +}; static pa_dbus_method_handler equalizer_methods[EQUALIZER_METHOD_MAX]={ [EQUALIZER_METHOD_SEED_FILTER]{ @@ -1321,7 +1448,12 @@ static pa_dbus_method_handler equalizer_methods[EQUALIZER_METHOD_MAX]={ .method_name="LoadProfile", .arguments=load_profile_args, .n_arguments=sizeof(load_profile_args)/sizeof(pa_dbus_arg_info), - .receive_cb=equalizer_handle_load_profile} + .receive_cb=equalizer_handle_load_profile}, + [EQUALIZER_METHOD_GET_PROFILE_NAME]{ + .method_name="BaseProfile", + .arguments=base_profile_name_args, + .n_arguments=sizeof(base_profile_name_args)/sizeof(pa_dbus_arg_info), + .receive_cb=equalizer_handle_get_profile_name} }; static pa_dbus_property_handler equalizer_handlers[EQUALIZER_HANDLER_MAX]={ @@ -1631,9 +1763,6 @@ void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, void * pa_xfree(ys); - //Stupid for IO reasons? Add a save signal to dbus instead - //save_state(u); - pa_dbus_send_empty_reply(conn, msg); pa_assert_se((signal = dbus_message_new_signal(u->dbus_path, EQUALIZER_IFACE, equalizer_signals[EQUALIZER_SIGNAL_FILTER_CHANGED].name))); @@ -1902,6 +2031,36 @@ void equalizer_handle_load_profile(DBusConnection *conn, DBusMessage *msg, void dbus_message_unref(signal); } +void equalizer_handle_get_profile_name(DBusConnection *conn, DBusMessage *msg, void *_u){ + struct userdata *u = (struct userdata *) _u; + DBusError error; + uint32_t channel, r_channel; + + pa_assert(conn); + pa_assert(msg); + pa_assert(u); + dbus_error_init(&error); + + if(!dbus_message_get_args(msg, &error, + DBUS_TYPE_UINT32, &channel, + DBUS_TYPE_INVALID)){ + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message); + dbus_error_free(&error); + return; + } + if(channel > u->channels){ + pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "invalid channel: %d", channel); + dbus_error_free(&error); + return; + } + r_channel = channel == u->channels ? 0 : channel; + if(u->base_profiles[r_channel]){ + pa_dbus_send_basic_value_reply(conn,msg, DBUS_TYPE_STRING, &u->base_profiles[r_channel]); + }else{ + pa_dbus_send_empty_reply(conn, msg); + } +} + void equalizer_get_revision(DBusConnection *conn, DBusMessage *msg, void *_u){ uint32_t rev=1; pa_dbus_send_basic_value_reply(conn, msg, DBUS_TYPE_UINT32, &rev); -- cgit From 263b683437b9a88722f80fd0abea9ca1998fbd36 Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Thu, 20 Aug 2009 23:55:02 -0700 Subject: module-equalizer-sink: fix improper usage of pa_modargs_get_value_boolean for u->set_default --- src/modules/module-equalizer-sink.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c index c531468d..9a79cb97 100755 --- a/src/modules/module-equalizer-sink.c +++ b/src/modules/module-equalizer-sink.c @@ -753,6 +753,7 @@ static void sink_input_attach_cb(pa_sink_input *i) { //pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->master->thread_info.max_latency); pa_sink_attach_within_thread(u->sink); if(u->set_default){ + pa_log("Setting default sink to %s", u->sink->name); pa_namereg_set_default_sink(u->module->core, u->sink); } } @@ -1056,7 +1057,7 @@ int pa__init(pa_module*m) { m->userdata = u; u->set_default = TRUE; - u->set_default = pa_modargs_get_value_boolean(ma, "set_default", &u->set_default); + pa_modargs_get_value_boolean(ma, "set_default", &u->set_default); u->channels = ss.channels; u->fft_size = pow(2, ceil(log(ss.rate)/log(2))); -- cgit From 3053badf0684e077fca8e8fddb43b4e9f2a5c30c Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Sun, 23 Aug 2009 15:49:27 -0700 Subject: module-equalizer-sink: resync with ladspa parent sink --- src/modules/module-equalizer-sink.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c index 9a79cb97..f634d5ea 100755 --- a/src/modules/module-equalizer-sink.c +++ b/src/modules/module-equalizer-sink.c @@ -998,9 +998,11 @@ static void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest) { pa_sink_input_assert_ref(i); pa_assert_se(u = i->userdata); - - pa_sink_set_asyncmsgq(u->sink, dest->asyncmsgq); - pa_sink_update_flags(u->sink, PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY, dest->flags); + if (dest) { + pa_sink_set_asyncmsgq(u->sink, dest->asyncmsgq); + pa_sink_update_flags(u->sink, PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY, dest->flags); + } else + pa_sink_set_asyncmsgq(u->sink, NULL); } //ensure's memory allocated is a multiple of v_size -- cgit From a434f4c6af1ba421f8c689fa8bf9e040268205b2 Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Wed, 26 Aug 2009 01:15:49 -0700 Subject: module-equalizer-sink: resyncing with head and fix invalid writes * pa_log->debug for default equalizer notification * partially fixed infinite rewind bug * set max_request to window_size first iteration * swap order inside ROUND_UP calls * resync pa_sink_input_new changes * change pa_sample_clamp parameters to be correct to fix invalid writes * reenable proper reset logic + proper request size --- src/modules/module-equalizer-sink.c | 213 ++++++++++++++++++------------------ 1 file changed, 108 insertions(+), 105 deletions(-) (limited to 'src') diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c index f634d5ea..f556be78 100755 --- a/src/modules/module-equalizer-sink.c +++ b/src/modules/module-equalizer-sink.c @@ -99,10 +99,10 @@ struct userdata { * the latency of the filter, calculated from window_size * based on constraints of COLA and window function */ - size_t latency;//Really just R but made into it's own variable //for twiddling with pulseaudio size_t overlap_size;//window_size-R size_t samples_gathered; + size_t input_buffer_max; //message float *W;//windowing function (time domain) float *work_buffer, **input, **overlap_accum; @@ -198,6 +198,34 @@ static int is_monotonic(const uint32_t *xs,size_t length){ return 1; } +//ensure's memory allocated is a multiple of v_size +//and aligned +static void * alloc(size_t x,size_t s){ + size_t f = PA_ROUND_UP(x*s, sizeof(float)*v_size); + float *t; + pa_assert(f >= x*s); + t = fftwf_malloc(f); + memset(t, 0, f); + return t; +} + +static void alloc_input_buffers(struct userdata *u, size_t min_buffer_length){ + if(min_buffer_length <= u->input_buffer_max){ + return; + } + pa_assert(min_buffer_length >= u->window_size); + for(size_t c = 0; c < u->channels; ++c){ + float *tmp = alloc(min_buffer_length, sizeof(float)); + if(u->input[c]){ + if(!u->first_iteration){ + memcpy(tmp, u->input[c], u->overlap_size * sizeof(float)); + } + free(u->input[c]); + } + u->input[c] = tmp; + } + u->input_buffer_max = min_buffer_length; +} /* Called from I/O thread context */ static int sink_process_msg_cb(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) { @@ -359,7 +387,7 @@ static void dsp_logic( //preseve the needed input for the next window's overlap memmove(src, src + u->R, - u->overlap_size * sizeof(float) + (u->samples_gathered - u->R) * sizeof(float) ); } @@ -470,71 +498,64 @@ typedef union float_vector { //} static void process_samples(struct userdata *u, pa_memchunk *tchunk){ - size_t fs=pa_frame_size(&(u->sink->sample_spec)); + size_t fs = pa_frame_size(&(u->sink->sample_spec)); float *dst; unsigned a_i; float *H, X; - pa_assert(u->samples_gathered >= u->R); + size_t iterations, offset; + pa_assert(u->samples_gathered >= u->window_size); + iterations = (u->samples_gathered - u->overlap_size) / u->R; tchunk->index = 0; - tchunk->length = u->R * fs; + tchunk->length = iterations * u->R * fs; tchunk->memblock = pa_memblock_new(u->sink->core->mempool, tchunk->length); - dst = ((float*)pa_memblock_acquire(tchunk->memblock)); - - for(size_t c=0;c < u->channels; c++) { - a_i = pa_aupdate_read_begin(u->a_H[c]); - X = u->Xs[c][a_i]; - H = u->Hs[c][a_i]; - dsp_logic( - u->work_buffer, - u->input[c], - u->overlap_accum[c], - X, - H, - u->W, - u->output_window, - u - ); - pa_aupdate_read_end(u->a_H[c]); - if(u->first_iteration){ - /* The windowing function will make the audio ramped in, as a cheap fix we can - * undo the windowing (for non-zero window values) - */ - for(size_t i = 0;i < u->overlap_size; ++i){ - u->work_buffer[i] = u->W[i] <= FLT_EPSILON ? u->work_buffer[i] : u->work_buffer[i] / u->W[i]; + dst = ((float*) pa_memblock_acquire(tchunk->memblock)); + for(size_t iter = 0; iter < iterations; ++iter){ + offset = iter * u->R * fs; + for(size_t c = 0;c < u->channels; c++) { + a_i = pa_aupdate_read_begin(u->a_H[c]); + X = u->Xs[c][a_i]; + H = u->Hs[c][a_i]; + dsp_logic( + u->work_buffer, + u->input[c], + u->overlap_accum[c], + X, + H, + u->W, + u->output_window, + u + ); + pa_aupdate_read_end(u->a_H[c]); + if(u->first_iteration){ + /* The windowing function will make the audio ramped in, as a cheap fix we can + * undo the windowing (for non-zero window values) + */ + for(size_t i = 0; i < u->overlap_size; ++i){ + u->work_buffer[i] = u->W[i] <= FLT_EPSILON ? u->work_buffer[i] : u->work_buffer[i] / u->W[i]; + } } + pa_sample_clamp(PA_SAMPLE_FLOAT32NE, (uint8_t *) (dst + c) + offset, fs, u->work_buffer, sizeof(float), u->R); } - pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst + c, fs, u->work_buffer, sizeof(float), u->R); + if(u->first_iteration){ + u->first_iteration = FALSE; + } + u->samples_gathered -= u->R; } pa_memblock_release(tchunk->memblock); - u->samples_gathered -= u->R; -} - -static void initialize_buffer(struct userdata *u, pa_memchunk *in){ - size_t fs = pa_frame_size(&u->sink->sample_spec); - size_t samples = in->length / fs; - float *src = (float*) ((uint8_t*) pa_memblock_acquire(in->memblock) + in->index); - pa_assert_se(u->samples_gathered + samples <= u->window_size); - for(size_t c = 0; c < u->channels; c++) { - //buffer with an offset after the overlap from previous - //iterations - pa_sample_clamp(PA_SAMPLE_FLOAT32NE, u->input[c] + u->samples_gathered, sizeof(float), src + c, fs, samples); - } - u->samples_gathered += samples; - pa_memblock_release(in->memblock); } static void input_buffer(struct userdata *u, pa_memchunk *in){ size_t fs = pa_frame_size(&(u->sink->sample_spec)); size_t samples = in->length/fs; float *src = (float*) ((uint8_t*) pa_memblock_acquire(in->memblock) + in->index); - pa_assert_se(samples <= u->window_size - u->samples_gathered); + pa_assert(u->samples_gathered + samples <= u->input_buffer_max); for(size_t c = 0; c < u->channels; c++) { //buffer with an offset after the overlap from previous //iterations pa_assert_se( - u->input[c]+u->samples_gathered+samples <= u->input[c]+u->window_size + u->input[c] + u->samples_gathered + samples <= u->input[c] + u->input_buffer_max ); - pa_sample_clamp(PA_SAMPLE_FLOAT32NE, u->input[c]+u->samples_gathered, sizeof(float), src + c, fs, samples); + pa_sample_clamp(PA_SAMPLE_FLOAT32NE, u->input[c] + u->samples_gathered, sizeof(float), src + c, fs, samples); } u->samples_gathered += samples; pa_memblock_release(in->memblock); @@ -543,7 +564,7 @@ static void input_buffer(struct userdata *u, pa_memchunk *in){ /* Called from I/O thread context */ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) { struct userdata *u; - size_t fs; + size_t fs, target_samples; struct timeval start, end; pa_memchunk tchunk; pa_sink_input_assert_ref(i); @@ -551,6 +572,16 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk pa_assert(chunk); pa_assert(u->sink); fs = pa_frame_size(&(u->sink->sample_spec)); + target_samples = PA_ROUND_UP(nbytes / fs, u->R); + if(u->first_iteration){ + //allocate request_size + target_samples = PA_MAX(target_samples, u->window_size); + }else{ + //allocate request_size + overlap + target_samples += u->overlap_size; + alloc_input_buffers(u, target_samples); + } + alloc_input_buffers(u, target_samples); chunk->memblock = NULL; /* Hmm, process any rewind request that might be queued up */ @@ -559,18 +590,11 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk //pa_log_debug("start output-buffered %ld, input-buffered %ld, requested %ld",buffered_samples,u->samples_gathered,samples_requested); pa_rtclock_get(&start); do{ - size_t input_remaining = u->window_size - u->samples_gathered; + size_t input_remaining = target_samples - u->samples_gathered; pa_assert(input_remaining > 0); - //collect samples - - //buffer = &u->conv_buffer; - //buffer->length = input_remaining*fs; - //buffer->index = 0; - //pa_memblock_ref(buffer->memblock); - //pa_sink_render_into(u->sink, buffer); while(pa_memblockq_peek(u->input_q, &tchunk) < 0){ - pa_sink_render(u->sink, input_remaining*fs, &tchunk); - //pa_sink_render_full(u->sink, input_remaining*fs, &tchunk); + //pa_sink_render(u->sink, input_remaining * fs, &tchunk); + pa_sink_render_full(u->sink, input_remaining * fs, &tchunk); pa_assert(tchunk.memblock); pa_memblockq_push(u->input_q, &tchunk); pa_memblock_unref(tchunk.memblock); @@ -581,15 +605,12 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk //pa_log_debug("asked for %ld input samples, got %ld samples",input_remaining,buffer->length/fs); /* copy new input */ //pa_rtclock_get(start); - if(u->first_iteration){ - initialize_buffer(u, &tchunk); - }else{ - input_buffer(u, &tchunk); - } + input_buffer(u, &tchunk); //pa_rtclock_get(&end); //pa_log_debug("Took %0.5f seconds to setup", pa_timeval_diff(end, start) / (double) PA_USEC_PER_SEC); pa_memblock_unref(tchunk.memblock); - }while(u->samples_gathered < u->window_size); + }while(u->samples_gathered < target_samples); + pa_rtclock_get(&end); pa_log_debug("Took %0.6f seconds to get data", (double) pa_timeval_diff(&end, &start) / PA_USEC_PER_SEC); @@ -605,9 +626,6 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk pa_assert(chunk->memblock); //pa_log_debug("gave %ld", chunk->length/fs); //pa_log_debug("end pop"); - if(u->first_iteration){ - u->first_iteration = FALSE; - } return 0; } @@ -632,11 +650,17 @@ static void sink_input_mute_changed_cb(pa_sink_input *i) { } static void reset_filter(struct userdata *u){ + size_t fs = pa_frame_size(&u->sink->sample_spec); + size_t max_request; u->samples_gathered = 0; - for(size_t i = 0;i < u->channels; ++i){ + for(size_t i = 0; i < u->channels; ++i){ memset(u->overlap_accum[i], 0, u->overlap_size * sizeof(float)); } u->first_iteration = TRUE; + //set buffer size to max request, no overlap copy + max_request = PA_ROUND_UP(pa_sink_input_get_max_request(u->sink_input) / fs , u->R); + max_request = PA_MAX(max_request, u->window_size); + pa_sink_set_max_request_within_thread(u->sink, max_request * fs); } /* Called from I/O thread context */ @@ -658,11 +682,8 @@ static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) { u->sink->thread_info.rewind_nbytes = 0; if (amount > 0) { - //pa_sample_spec *ss = &u->sink->sample_spec; //invalidate the output q pa_memblockq_seek(u->input_q, - (int64_t) amount, PA_SEEK_RELATIVE, TRUE); - //pa_memblockq_drop(u->input_q, pa_memblockq_get_length(u->input_q)); - //pa_memblockq_seek(u->input_q, - (int64_t) amount, PA_SEEK_RELATIVE, TRUE); pa_log("Resetting filter"); reset_filter(u); } @@ -689,11 +710,11 @@ static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes) { size_t fs; pa_sink_input_assert_ref(i); pa_assert_se(u = i->userdata); - + //if(u->first_iteration){ + // return; + //} fs = pa_frame_size(&(u->sink->sample_spec)); - //pa_sink_set_max_request_within_thread(u->sink, nbytes); - //pa_sink_set_max_request_within_thread(u->sink, u->R*fs); - pa_sink_set_max_request_within_thread(u->sink, ((nbytes+u->R*fs-1)/(u->R*fs))*(u->R*fs)); + pa_sink_set_max_request_within_thread(u->sink, PA_ROUND_UP(nbytes / fs, u->R) * fs); } /* Called from I/O thread context */ @@ -703,8 +724,6 @@ static void sink_input_update_sink_latency_range_cb(pa_sink_input *i) { pa_sink_input_assert_ref(i); pa_assert_se(u = i->userdata); - //pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->latency*fs); - //pa_sink_set_latency_range_within_thread(u->sink, u->latency*fs, u->latency*fs ); pa_sink_set_latency_range_within_thread(u->sink, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency); } @@ -733,7 +752,7 @@ static void sink_input_detach_cb(pa_sink_input *i) { /* Called from I/O thread context */ static void sink_input_attach_cb(pa_sink_input *i) { struct userdata *u; - size_t fs; + size_t fs, max_request; pa_sink_input_assert_ref(i); pa_assert_se(u = i->userdata); @@ -741,19 +760,15 @@ static void sink_input_attach_cb(pa_sink_input *i) { pa_sink_set_latency_range_within_thread(u->sink, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency); pa_sink_set_fixed_latency_within_thread(u->sink, i->sink->thread_info.fixed_latency); - fs = pa_frame_size(&(u->sink->sample_spec)); - pa_sink_set_max_request_within_thread(u->sink, PA_ROUND_UP(pa_sink_input_get_max_request(i), u->R*fs)); - - //pa_sink_set_latency_range_within_thread(u->sink, u->latency*fs, u->latency*fs); - //pa_sink_set_latency_range_within_thread(u->sink, u->latency*fs, u->master->thread_info.max_latency); - //TODO: setting this guy minimizes drop outs but doesn't get rid - //of them completely, figure out why - //pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->latency*fs); - //TODO: this guy causes dropouts constantly+rewinds, it's unusable - //pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->master->thread_info.max_latency); + fs = pa_frame_size(&u->sink->sample_spec); + //set buffer size to max request, no overlap copy + max_request = PA_ROUND_UP(pa_sink_input_get_max_request(u->sink_input) / fs , u->R); + max_request = PA_MAX(max_request, u->window_size); + pa_sink_set_max_request_within_thread(u->sink, max_request * fs); + pa_sink_set_max_rewind_within_thread(u->sink, pa_sink_input_get_max_rewind(i)); pa_sink_attach_within_thread(u->sink); if(u->set_default){ - pa_log("Setting default sink to %s", u->sink->name); + pa_log_debug("Setting default sink to %s", u->sink->name); pa_namereg_set_default_sink(u->module->core, u->sink); } } @@ -1005,17 +1020,6 @@ static void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest) { pa_sink_set_asyncmsgq(u->sink, NULL); } -//ensure's memory allocated is a multiple of v_size -//and aligned -static void * alloc(size_t x,size_t s){ - size_t f = PA_ROUND_UP(x*s, sizeof(float)*v_size); - float *t; - pa_assert(f >= x*s); - t = fftwf_malloc(f); - memset(t, 0, f); - return t; -} - int pa__init(pa_module*m) { struct userdata *u; pa_sample_spec ss; @@ -1068,15 +1072,15 @@ int pa__init(pa_module*m) { u->R = (u->window_size + 1) / 2; u->overlap_size = u->window_size - u->R; u->samples_gathered = 0; + u->input_buffer_max = 0; u->a_H = pa_xnew0(pa_aupdate *, u->channels); - u->latency = u->window_size - u->R; u->Xs = pa_xnew0(float *, u->channels); u->Hs = pa_xnew0(float **, u->channels); for(size_t c = 0; c < u->channels; ++c){ u->Xs[c] = pa_xnew0(float, 2); u->Hs[c] = pa_xnew0(float *, 2); for(size_t i = 0; i < 2; ++i){ - u->Hs[c][i] = alloc((FILTER_SIZE), sizeof(float)); + u->Hs[c][i] = alloc(FILTER_SIZE, sizeof(float)); } } u->W = alloc(u->window_size, sizeof(float)); @@ -1086,8 +1090,7 @@ int pa__init(pa_module*m) { u->overlap_accum = pa_xnew0(float *, u->channels); for(size_t c = 0; c < u->channels; ++c){ u->a_H[c] = pa_aupdate_new(); - u->input[c] = alloc(u->window_size, sizeof(float)); - memset(u->input[c], 0, (u->window_size)*sizeof(float)); + u->input[c] = NULL; u->overlap_accum[c] = alloc(u->overlap_size, sizeof(float)); memset(u->overlap_accum[c], 0, u->overlap_size*sizeof(float)); } @@ -1151,7 +1154,7 @@ int pa__init(pa_module*m) { pa_sink_input_new_data_set_sample_spec(&sink_input_data, &ss); pa_sink_input_new_data_set_channel_map(&sink_input_data, &map); - pa_sink_input_new(&u->sink_input, m->core, &sink_input_data, 0); + pa_sink_input_new(&u->sink_input, m->core, &sink_input_data); pa_sink_input_new_data_done(&sink_input_data); if (!u->sink_input) -- cgit From 97056d2a0e4364fad86f4be5e26625592aef3d55 Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Fri, 18 Sep 2009 05:48:01 -0700 Subject: module-equalizer-sink: *added client initiated sync support for filter state *added note of possible unstable behavior with next-power-of-2 sample rate calculation --- src/modules/module-equalizer-sink.c | 59 ++++++++++++++++++++++--------------- 1 file changed, 35 insertions(+), 24 deletions(-) (limited to 'src') diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c index f556be78..74d7497b 100755 --- a/src/modules/module-equalizer-sink.c +++ b/src/modules/module-equalizer-sink.c @@ -837,13 +837,10 @@ static void unpack(char *str, size_t length, char ***strs, size_t *len){ size_t l = *((uint16_t *)(str+offset)); size_t e = PA_MIN(offset + l, length) - offset; offset = PA_MIN(offset + sizeof(uint16_t), length); - if(e > 0){ - (*strs)[i] = pa_xnew(char, e + 1); - memcpy((*strs)[i], strs + offset, e); - (*strs)[i][e] = '\0'; - }else{ - (*strs)[i]=NULL; - } + (*strs)[i] = pa_xnew(char, e + 1); + memcpy((*strs)[i], str + offset, e); + (*strs)[i][e] = '\0'; + offset += l; } } static void save_profile(struct userdata *u, size_t channel, char *name){ @@ -905,7 +902,7 @@ static void save_state(struct userdata *u){ data.data = state; data.size = filter_state_size + packed_length; //thread safety for 0.9.17? - pa_assert_se(dbname = pa_state_path(EQ_STATE_DB, TRUE)); + pa_assert_se(dbname = pa_state_path(EQ_STATE_DB, FALSE)); pa_assert_se(database = pa_database_open(dbname, TRUE)); pa_xfree(dbname); @@ -936,12 +933,10 @@ static const char* load_profile(struct userdata *u, size_t channel, char *name){ float *profile = (float *) value.data; a_i = pa_aupdate_write_begin(u->a_H[channel]); u->Xs[channel][a_i] = profile[0]; - memcpy(u->Hs[channel][a_i], profile + 1, CHANNEL_PROFILE_SIZE * sizeof(float)); + memcpy(u->Hs[channel][a_i], profile + 1, FILTER_SIZE * sizeof(float)); fix_filter(u->Hs[channel][a_i], u->fft_size); pa_aupdate_write_end(u->a_H[channel]); - if(u->base_profiles[channel]){ - pa_xfree(u->base_profiles[channel]); - } + pa_xfree(u->base_profiles[channel]); u->base_profiles[channel] = pa_xstrdup(name); }else{ return "incompatible size"; @@ -964,6 +959,7 @@ static void load_state(struct userdata *u){ database = pa_database_open(dbname, FALSE); pa_xfree(dbname); if(!database){ + pa_log("No resume state"); return; } @@ -985,14 +981,14 @@ static void load_state(struct userdata *u){ unpack(((char *)value.data) + FILTER_STATE_SIZE, value.size - FILTER_STATE_SIZE, &names, &n_profs); n_profs = PA_MIN(n_profs, u->channels); for(size_t c = 0; c < n_profs; ++c){ - if(u->base_profiles[c]){ - pa_xfree(u->base_profiles[c]); - } + pa_xfree(u->base_profiles[c]); u->base_profiles[c] = names[c]; } pa_xfree(names); } pa_datum_free(&value); + }else{ + pa_log("resume state exists but is wrong size!"); } pa_database_close(database); } @@ -1066,7 +1062,7 @@ int pa__init(pa_module*m) { pa_modargs_get_value_boolean(ma, "set_default", &u->set_default); u->channels = ss.channels; - u->fft_size = pow(2, ceil(log(ss.rate)/log(2))); + u->fft_size = pow(2, ceil(log(ss.rate)/log(2)));//probably unstable near corner cases of powers of 2 pa_log_debug("fft size: %ld", u->fft_size); u->window_size = 15999; u->R = (u->window_size + 1) / 2; @@ -1102,6 +1098,9 @@ int pa__init(pa_module*m) { u->first_iteration = TRUE; u->base_profiles = pa_xnew0(char *, u->channels); + for(size_t c = 0; c < u->channels; ++c){ + u->base_profiles[c] = pa_xstrdup("default"); + } /* Create sink */ pa_sink_new_data_init(&sink_data); @@ -1233,9 +1232,7 @@ void pa__done(pa_module*m) { dbus_done(u); for(size_t c = 0; c < u->channels; ++c){ - if(u->base_profiles[c]){ - pa_xfree(u->base_profiles[c]); - } + pa_xfree(u->base_profiles[c]); } pa_xfree(u->base_profiles); @@ -1308,6 +1305,7 @@ static void equalizer_handle_get_filter(DBusConnection *conn, DBusMessage *msg, static void equalizer_handle_set_filter(DBusConnection *conn, DBusMessage *msg, void *_u); static void equalizer_handle_save_profile(DBusConnection *conn, DBusMessage *msg, void *_u); static void equalizer_handle_load_profile(DBusConnection *conn, DBusMessage *msg, void *_u); +static void equalizer_handle_save_state(DBusConnection *conn, DBusMessage *msg, void *_u); static void equalizer_handle_get_profile_name(DBusConnection *conn, DBusMessage *msg, void *_u); enum manager_method_index { MANAGER_METHOD_REMOVE_PROFILE, @@ -1374,6 +1372,7 @@ enum equalizer_method_index { EQUALIZER_METHOD_LOAD_PROFILE, EQUALIZER_METHOD_SET_FILTER, EQUALIZER_METHOD_GET_FILTER, + EQUALIZER_METHOD_SAVE_STATE, EQUALIZER_METHOD_GET_PROFILE_NAME, EQUALIZER_METHOD_MAX }; @@ -1455,6 +1454,11 @@ static pa_dbus_method_handler equalizer_methods[EQUALIZER_METHOD_MAX]={ .arguments=load_profile_args, .n_arguments=sizeof(load_profile_args)/sizeof(pa_dbus_arg_info), .receive_cb=equalizer_handle_load_profile}, + [EQUALIZER_METHOD_SAVE_STATE]{ + .method_name="SaveState", + .arguments=NULL, + .n_arguments=0, + .receive_cb=equalizer_handle_save_state}, [EQUALIZER_METHOD_GET_PROFILE_NAME]{ .method_name="BaseProfile", .arguments=base_profile_name_args, @@ -2037,6 +2041,16 @@ void equalizer_handle_load_profile(DBusConnection *conn, DBusMessage *msg, void dbus_message_unref(signal); } +void equalizer_handle_save_state(DBusConnection *conn, DBusMessage *msg, void *_u) { + struct userdata *u = (struct userdata *) _u; + pa_assert(conn); + pa_assert(msg); + pa_assert(u); + + save_state(u); + pa_dbus_send_empty_reply(conn, msg); +} + void equalizer_handle_get_profile_name(DBusConnection *conn, DBusMessage *msg, void *_u){ struct userdata *u = (struct userdata *) _u; DBusError error; @@ -2060,11 +2074,8 @@ void equalizer_handle_get_profile_name(DBusConnection *conn, DBusMessage *msg, v return; } r_channel = channel == u->channels ? 0 : channel; - if(u->base_profiles[r_channel]){ - pa_dbus_send_basic_value_reply(conn,msg, DBUS_TYPE_STRING, &u->base_profiles[r_channel]); - }else{ - pa_dbus_send_empty_reply(conn, msg); - } + pa_assert(u->base_profiles[r_channel]); + pa_dbus_send_basic_value_reply(conn,msg, DBUS_TYPE_STRING, &u->base_profiles[r_channel]); } void equalizer_get_revision(DBusConnection *conn, DBusMessage *msg, void *_u){ -- cgit From e8952001699317952318918835252f63c7a899f9 Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Sat, 19 Sep 2009 11:15:05 -0700 Subject: module-equalizer-sink: disable active profile name restoration as something in pack/unpack is funky and I don't have time for a proper fix --- src/modules/module-equalizer-sink.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c index 74d7497b..3a28b497 100755 --- a/src/modules/module-equalizer-sink.c +++ b/src/modules/module-equalizer-sink.c @@ -978,13 +978,13 @@ static void load_state(struct userdata *u){ memcpy(u->Hs[c][a_i], H, FILTER_SIZE * sizeof(float)); pa_aupdate_write_end(u->a_H[c]); } - unpack(((char *)value.data) + FILTER_STATE_SIZE, value.size - FILTER_STATE_SIZE, &names, &n_profs); - n_profs = PA_MIN(n_profs, u->channels); - for(size_t c = 0; c < n_profs; ++c){ - pa_xfree(u->base_profiles[c]); - u->base_profiles[c] = names[c]; - } - pa_xfree(names); + //unpack(((char *)value.data) + FILTER_STATE_SIZE, value.size - FILTER_STATE_SIZE, &names, &n_profs); + //n_profs = PA_MIN(n_profs, u->channels); + //for(size_t c = 0; c < n_profs; ++c){ + // pa_xfree(u->base_profiles[c]); + // u->base_profiles[c] = names[c]; + //} + //pa_xfree(names); } pa_datum_free(&value); }else{ -- cgit