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/modules/module-equalizer-sink.c') 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