3 * Copyright (C) 2008-2019 Adam Williams <broadcast at earthling dot net>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 #include "bcdisplayinfo.h"
21 #include "bcsignals.h"
23 #include "comprmulti.h"
24 #include "comprmultigui.h"
30 #include "transportque.inc"
37 REGISTER_PLUGIN(ComprMultiEffect)
39 // More potential compressor algorithms:
40 // Use single reaction time parameter. Negative reaction time uses
41 // readahead. Positive reaction time uses slope.
43 // Smooth input stage if readahead.
44 // Determine slope from current smoothed sample to every sample in readahead area.
45 // Once highest slope is found, count of number of samples remaining until it is
46 // reached. Only search after this count for the next highest slope.
47 // Use highest slope to determine smoothed value.
49 // Smooth input stage if not readahead.
50 // For every sample, calculate slope needed to reach current sample from
51 // current smoothed value in the reaction time. If higher than current slope,
52 // make it the current slope and count number of samples remaining until it is
53 // reached. If this count is met and no higher slopes are found, base slope
54 // on current sample when count is met.
57 // For every sample, calculate gain from smoothed input value.
59 ComprMultiConfig::ComprMultiConfig()
60 : CompressorConfigBase(TOTAL_BANDS)
66 void ComprMultiConfig::copy_from(ComprMultiConfig &that)
68 CompressorConfigBase::copy_from(that);
70 window_size = that.window_size;
74 int ComprMultiConfig::equivalent(ComprMultiConfig &that)
76 if( !CompressorConfigBase::equivalent(that) )
79 if( !EQUIV(q, that.q) ||
80 window_size != that.window_size ) {
87 void ComprMultiConfig::interpolate(ComprMultiConfig &prev, ComprMultiConfig &next,
88 int64_t prev_frame, int64_t next_frame, int64_t current_frame)
94 ComprMultiEffect::ComprMultiEffect(PluginServer *server)
95 : PluginAClient(server)
98 for( int i = 0; i < TOTAL_BANDS; i++ )
99 band_states[i] = new BandState(this, i);
102 ComprMultiEffect::~ComprMultiEffect()
105 for( int i = 0; i < TOTAL_BANDS; i++ ) {
106 delete band_states[i];
110 void ComprMultiEffect::delete_dsp()
112 #ifndef DRAW_AFTER_BANDPASS
114 for( int i = 0; i < PluginClient::total_in_buffers; i++ )
115 delete input_buffer[i];
116 delete [] input_buffer;
123 for( int i = 0; i < PluginClient::total_in_buffers; i++ )
128 for( int i = 0; i < TOTAL_BANDS; i++ ) {
129 band_states[i]->delete_dsp();
137 void ComprMultiEffect::reset()
139 for( int i = 0; i < TOTAL_BANDS; i++ )
142 #ifndef DRAW_AFTER_BANDPASS
151 need_reconfigure = 1;
152 config.current_band = 0;
155 const char* ComprMultiEffect::plugin_title() { return N_("Compressor Multi"); }
156 int ComprMultiEffect::is_realtime() { return 1; }
157 int ComprMultiEffect::is_multichannel() { return 1; }
160 void ComprMultiEffect::read_data(KeyFrame *keyframe)
163 input.set_shared_input(keyframe->xbuf);
166 for( int i = 0; i < TOTAL_BANDS; i++ )
167 config.bands[i].levels.remove_all();
169 while( !(result = input.read_tag()) ) {
170 if( input.tag.title_is("COMPRESSOR_MULTI") ) {
171 config.trigger = input.tag.get_property("TRIGGER", config.trigger);
172 config.smoothing_only = input.tag.get_property("SMOOTHING_ONLY", config.smoothing_only);
173 config.input = input.tag.get_property("INPUT", config.input);
174 config.q = input.tag.get_property("Q", config.q);
175 config.window_size = input.tag.get_property("WINDOW_SIZE", config.window_size);
177 else if( input.tag.title_is("COMPRESSORBAND") ) {
178 int number = input.tag.get_property("NUMBER", 0);
179 config.bands[number].read_data(&input, 1);
186 void ComprMultiEffect::save_data(KeyFrame *keyframe)
189 output.set_shared_output(keyframe->xbuf);
191 output.tag.set_title("COMPRESSOR_MULTI");
192 output.tag.set_property("TRIGGER", config.trigger);
193 output.tag.set_property("SMOOTHING_ONLY", config.smoothing_only);
194 output.tag.set_property("INPUT", config.input);
195 output.tag.set_property("Q", config.q);
196 output.tag.set_property("WINDOW_SIZE", config.window_size);
198 output.append_newline();
200 for( int band = 0; band < TOTAL_BANDS; band++ ) {
201 BandConfig *band_config = &config.bands[band];
202 band_config->save_data(&output, band, 1);
206 output.tag.set_title("/COMPRESSOR_MULTI");
208 output.append_newline();
209 output.terminate_string();
212 void ComprMultiEffect::dump_frames()
214 printf("tracking %f, direction %d\n", get_tracking_position(), get_tracking_direction());
215 CompressorClientFrame *cfp = (CompressorClientFrame *)client_frames.first;
216 for( int n=0; cfp; cfp=(CompressorClientFrame *)cfp->next,++n ) {
217 switch( cfp->type ) {
218 case GAIN_COMPRESSORFRAME: {
219 CompressorGainFrame *gfp = (CompressorGainFrame *)cfp;
220 printf("%3d: band %d, gain pos=%f, gain=%f, level=%f\n", n, cfp->band,
221 gfp->position, gfp->gain, gfp->level);
223 case FREQ_COMPRESSORFRAME: {
224 CompressorFreqFrame *ffp = (CompressorFreqFrame *)cfp;
225 printf("%3d: band %d, freq pos=%f, max=%f/%f, size=%d\n", n, ffp->band,
226 ffp->position, ffp->freq_max, ffp->time_max, ffp->data_size);
232 void ComprMultiEffect::update_gui()
235 if( !thread ) return;
236 ComprMultiWindow *window = (ComprMultiWindow *)thread->window;
237 if( !window ) return;
238 // user can't change levels when loading configuration
239 window->lock_window("ComprMultiEffect::update_gui 1");
240 int reconfigured = 0;
241 // Can't update points if the user is editing
242 if( window->canvas->is_dragging() )
243 reconfigured = load_configuration();
244 //printf("ComprMultiEffect::update_gui %d %d %d\n", __LINE__, reconfigured, total_frames);
247 if( pending_gui_frame() )
248 window->update_eqcanvas();
249 window->unlock_window();
253 void ComprMultiEffect::render_stop()
255 if( !thread ) return;
256 ComprMultiWindow *window = (ComprMultiWindow*)thread->window;
257 if( !window ) return;
258 window->lock_window("ComprMultiEffect::render_stop");
260 window->gain_change->update(1, 0);
261 delete window->gain_frame; window->gain_frame = 0;
262 delete window->freq_frame; window->freq_frame = 0;
263 window->unlock_window();
267 LOAD_CONFIGURATION_MACRO(ComprMultiEffect, ComprMultiConfig)
268 NEW_WINDOW_MACRO(ComprMultiEffect, ComprMultiWindow)
271 int ComprMultiEffect::process_buffer(int64_t size, Samples **buffer,
272 int64_t start_position, int sample_rate)
274 start_pos = (double)start_position / sample_rate;
275 dir = get_direction() == PLAY_REVERSE ? -1 : 1;
276 need_reconfigure |= load_configuration();
277 int channels = PluginClient::total_in_buffers;
278 if( need_reconfigure ) {
279 need_reconfigure = 0;
280 // min_x = DB::fromdb(config.min_db); max_x = 1.0;
281 // min_y = DB::fromdb(config.min_db); max_y = 1.0;
283 if( fft && fft[0]->window_size != config.window_size ) {
284 for( int i = 0; i < channels; i++ )
291 fft = new ComprMultiFFT*[channels];
292 for( int i = 0; i < channels; i++ ) {
293 fft[i] = new ComprMultiFFT(this, i);
294 fft[i]->initialize(config.window_size, TOTAL_BANDS);
298 for( int i = 0; i < TOTAL_BANDS; i++ )
299 band_states[i]->reconfigure();
302 // reset after seeking
303 if( last_position != start_position ) {
305 for( int i = 0; i < channels; i++ )
306 if( fft[i] ) fft[i]->delete_fft();
309 #ifndef DRAW_AFTER_BANDPASS
313 input_start = start_position;
314 for( int band = 0; band < TOTAL_BANDS; band++ ) {
315 BandState *band_state = band_states[band];
316 if( band_state->engine )
317 band_state->engine->reset();
321 // process frequency domain for all bands simultaneously
322 // read enough samples ahead to process all the bands
323 int new_filtered_size = 0;
324 for( int band = 0; band < TOTAL_BANDS; band++ ) {
325 BandState *band_state = band_states[band];
326 if( !band_state->engine )
327 band_state->engine = new CompressorEngine(&config, band);
328 int attack_samples, release_samples, preview_samples;
329 band_state->engine->calculate_ranges(
330 &attack_samples, &release_samples, &preview_samples,
333 if( preview_samples > new_filtered_size )
334 new_filtered_size = preview_samples;
336 new_filtered_size += size;
337 for( int band = 0; band < TOTAL_BANDS; band++ )
338 band_states[band]->allocate_filtered(new_filtered_size);
340 // Append data to the buffers to fill the readahead area.
341 int remain = new_filtered_size - filtered_size;
344 // printf("ComprMultiEffect::process_buffer %d filtered_size=%ld remain=%d\n",
345 // __LINE__, filtered_size, remain);
346 for( int channel = 0; channel < channels; channel++ ) {
347 #ifndef DRAW_AFTER_BANDPASS
348 new_input_size = input_size;
350 // create an array of filtered buffers for the output
351 Samples *filtered_arg[TOTAL_BANDS];
352 for( int band = 0; band < TOTAL_BANDS; band++ ) {
353 new_spectrogram_frames[band] = 0;
354 filtered_arg[band] = band_states[band]->filtered_buffer[channel];
355 // temporarily set the output to the end to append data
356 filtered_arg[band]->set_offset(filtered_size);
359 // starting position of the input reads
360 int64_t start = input_start + dir * filtered_size;
361 // printf("ComprMultiEffect::process_buffer %d start=%ld remain=%d\n", __LINE__, start, remain);
362 fft[channel]->process_buffer(start, remain, filtered_arg, get_direction());
364 for( int band = 0; band < TOTAL_BANDS; band++ ) {
365 filtered_arg[band]->set_offset(0);
367 //printf("ComprMultiEffect::process_buffer %d new_input_size=%ld\n", __LINE__, new_input_size);
371 #ifndef DRAW_AFTER_BANDPASS
372 input_size = new_input_size;
374 filtered_size = new_filtered_size;
376 // process time domain for each band separately
377 for( int band = 0; band < TOTAL_BANDS; band++ ) {
378 BandState *band_state = band_states[band];
379 CompressorEngine *engine = band_state->engine;
381 engine->process(band_states[band]->filtered_buffer,
382 band_states[band]->filtered_buffer,
383 size, sample_rate, channels, start_position);
385 for( int i = 0; i < engine->gui_gains.size(); i++ ) {
386 CompressorGainFrame *frame = new CompressorGainFrame();
387 frame->position = start_pos + dir*engine->gui_offsets[i];
388 frame->gain = engine->gui_gains.get(i);
389 frame->level = engine->gui_levels.get(i);
391 add_gui_frame(frame);
395 // Add together filtered buffers + unfiltered buffer.
396 // Apply the solo here.
398 for( int band = 0; band < TOTAL_BANDS; band++ ) {
399 if( config.bands[band].solo ) {
405 for( int channel = 0; channel < channels; channel++ ) {
406 double *dst = buffer[channel]->get_data();
407 bzero(dst, size * sizeof(double));
409 for( int band = 0; band < TOTAL_BANDS; band++ ) {
410 if( !have_solo || config.bands[band].solo ) {
411 double *src = band_states[band]->filtered_buffer[channel]->get_data();
412 for( int i = 0; i < size; i++ ) {
419 // shift input buffers
420 for( int band = 0; band < TOTAL_BANDS; band++ ) {
422 for( int i = 0; i < channels; i++ ) {
423 memcpy(band_states[band]->filtered_buffer[i]->get_data(),
424 band_states[band]->filtered_buffer[i]->get_data() + size,
425 (filtered_size - size) * sizeof(double));
430 #ifndef DRAW_AFTER_BANDPASS
431 for( int i = 0; i < channels; i++ ) {
432 memcpy(input_buffer[i]->get_data(),
433 input_buffer[i]->get_data() + size,
434 (input_size - size) * sizeof(double));
439 // update the counters
440 filtered_size -= size;
441 input_start += dir * size;
442 last_position = start_position + dir * size;
446 void ComprMultiEffect::allocate_input(int new_size)
448 #ifndef DRAW_AFTER_BANDPASS
450 new_size > input_buffer[0]->get_allocated() ) {
451 Samples **new_input_buffer = new Samples*[get_total_buffers()];
453 for( int i = 0; i < get_total_buffers(); i++ ) {
454 new_input_buffer[i] = new Samples(new_size);
457 memcpy(new_input_buffer[i]->get_data(),
458 input_buffer[i]->get_data(),
459 input_buffer[i]->get_allocated() * sizeof(double));
460 delete input_buffer[i];
464 if( input_buffer ) delete [] input_buffer;
465 input_buffer = new_input_buffer;
467 #endif // !DRAW_AFTER_BANDPASS
471 void ComprMultiEffect::calculate_envelope()
473 for( int i = 0; i < TOTAL_BANDS; i++ ) {
474 band_states[i]->calculate_envelope();
479 BandState::BandState(ComprMultiEffect *plugin, int band)
481 this->plugin = plugin;
486 BandState::~BandState()
491 void BandState::delete_dsp()
495 if( filtered_buffer ) {
496 for( int i = 0; i < plugin->total_in_buffers; i++ ) {
497 delete filtered_buffer[i];
499 delete [] filtered_buffer;
507 void BandState::reset()
511 envelope_allocated = 0;
515 previous_target = 1.0;
517 target_current_sample = -1;
521 void BandState::reconfigure()
523 // Calculate linear transfer from db
526 BandConfig *config = &plugin->config.bands[band];
527 for( int i = 0; i < config->levels.total; i++ ) {
529 levels.values[i].x = DB::fromdb(config->levels.values[i].x);
530 levels.values[i].y = DB::fromdb(config->levels.values[i].y);
533 calculate_envelope();
537 void BandState::allocate_filtered(int new_size)
539 if( !filtered_buffer ||
540 new_size > filtered_buffer[0]->get_allocated() ) {
541 Samples **new_filtered_buffer = new Samples*[plugin->get_total_buffers()];
542 for( int i = 0; i < plugin->get_total_buffers(); i++ ) {
543 new_filtered_buffer[i] = new Samples(new_size);
545 if( filtered_buffer ) {
546 memcpy(new_filtered_buffer[i]->get_data(),
547 filtered_buffer[i]->get_data(),
548 filtered_buffer[i]->get_allocated() * sizeof(double));
549 delete filtered_buffer[i];
553 if( filtered_buffer ) delete [] filtered_buffer;
554 filtered_buffer = new_filtered_buffer;
559 void BandState::calculate_envelope()
561 // the window size changed
562 if( envelope && envelope_allocated < plugin->config.window_size / 2 ) {
568 envelope_allocated = plugin->config.window_size / 2;
569 envelope = new double[envelope_allocated];
572 // number of slots in the edge
573 double edge = (1.0 - plugin->config.q) * TOTALFREQS / 2;
574 int max_freq = Freq::tofreq_f(TOTALFREQS - 1);
575 int nyquist = plugin->project_sample_rate / 2;
576 int freq = plugin->config.bands[band].freq;
578 // max frequency of all previous bands is the low
580 for( int i=0; i<band; ++i ) {
581 if( plugin->config.bands[i].freq > low )
582 low = plugin->config.bands[i].freq;
585 // limit the frequencies
586 if( band < TOTAL_BANDS-1 ) high = freq;
587 if( high >= max_freq ) { high = max_freq; edge = 0; }
588 // hard edges on the lowest & highest
589 if( band == 0 && high <= 0 ) edge = 0;
590 if( low > high ) low = high;
591 // number of slots to arrive at 1/2 power
592 #ifndef LOG_CROSSOVER
594 double edge2 = edge / 2;
597 double edge2 = edge * 6 / -INFINITYGAIN;
599 double low_slot = Freq::fromfreq_f(low);
600 double high_slot = Freq::fromfreq_f(high);
601 // shift slots to allow crossover
602 if( band < TOTAL_BANDS-1 ) high_slot -= edge2;
603 if( band > 0 ) low_slot += edge2;
605 for( int i = 0; i < plugin->config.window_size / 2; i++ ) {
606 double freq = i * nyquist / (plugin->config.window_size / 2);
607 double slot = Freq::fromfreq_f(freq);
608 // sum of previous bands
610 for( int prev_band = 0; prev_band < band; prev_band++ ) {
611 double *prev_envelope = plugin->band_states[prev_band]->envelope;
612 prev_sum += prev_envelope[i];
615 if( slot < high_slot )
616 // remain of previous bands
617 envelope[i] = 1.0 - prev_sum;
618 else if( slot < high_slot + edge ) {
620 double remain = 1.0 - prev_sum;
621 #ifndef LOG_CROSSOVER
623 envelope[i] = remain - remain * (slot - high_slot) / edge;
626 envelope[i] = DB::fromdb((slot - high_slot) * INFINITYGAIN / edge);
636 void BandState::process_readbehind(int size,
637 int reaction_samples, int decay_samples, int trigger)
639 if( target_current_sample < 0 )
640 target_current_sample = reaction_samples;
641 double current_slope = (next_target - previous_target) / reaction_samples;
642 double *trigger_buffer = filtered_buffer[trigger]->get_data();
643 int channels = plugin->get_total_buffers();
644 for( int i = 0; i < size; i++ ) {
645 // Get slope required to reach current sample from smoothed sample over reaction
648 switch( plugin->config.input ) {
649 case ComprMultiConfig::MAX: {
651 for( int j = 0; j < channels; j++ ) {
652 sample = fabs(filtered_buffer[j]->get_data()[i]);
653 if( sample > max ) max = sample;
658 case ComprMultiConfig::TRIGGER:
659 sample = fabs(trigger_buffer[i]);
662 case ComprMultiConfig::SUM: {
664 for( int j = 0; j < channels; j++ ) {
665 sample = fabs(filtered_buffer[j]->get_data()[i]);
672 double new_slope = (sample - current_value) / reaction_samples;
674 // Slope greater than current slope
675 if( new_slope >= current_slope &&
676 (current_slope >= 0 ||
678 next_target = sample;
679 previous_target = current_value;
680 target_current_sample = 0;
681 target_samples = reaction_samples;
682 current_slope = new_slope;
685 if( sample > next_target && current_slope < 0 ) {
686 next_target = sample;
687 previous_target = current_value;
688 target_current_sample = 0;
689 target_samples = decay_samples;
690 current_slope = (sample - current_value) / decay_samples;
692 // Current smoothed sample came up without finding higher slope
693 if( target_current_sample >= target_samples ) {
694 next_target = sample;
695 previous_target = current_value;
696 target_current_sample = 0;
697 target_samples = decay_samples;
698 current_slope = (sample - current_value) / decay_samples;
701 // Update current value and store gain
702 current_value = (next_target * target_current_sample +
703 previous_target * (target_samples - target_current_sample)) /
706 target_current_sample++;
708 if( plugin->config.smoothing_only ) {
709 for( int j = 0; j < channels; j++ ) {
710 filtered_buffer[j]->get_data()[i] = current_value;
714 if( !plugin->config.bands[band].bypass ) {
715 double gain = plugin->config.calculate_gain(band, current_value);
716 for( int j = 0; j < channels; j++ ) {
717 filtered_buffer[j]->get_data()[i] *= gain;
723 void BandState::process_readahead(int size, int preview_samples,
724 int reaction_samples, int decay_samples, int trigger)
726 if( target_current_sample < 0 ) target_current_sample = target_samples;
727 double current_slope = (next_target - previous_target) /
729 double *trigger_buffer = filtered_buffer[trigger]->get_data();
730 int channels = plugin->get_total_buffers();
731 for( int i = 0; i < size; i++ ) {
732 // Get slope from current sample to every sample in preview_samples.
733 // Take highest one or first one after target_samples are up.
735 // For optimization, calculate the first slope we really need.
736 // Assume every slope up to the end of preview_samples has been calculated and
737 // found <= to current slope.
738 int first_slope = preview_samples - 1;
739 // Need new slope immediately
740 if( target_current_sample >= target_samples )
743 for( int j = first_slope; j < preview_samples; j++ ) {
744 int buffer_offset = i + j;
746 switch( plugin->config.input ) {
747 case ComprMultiConfig::MAX: {
749 for( int k = 0; k < channels; k++ ) {
750 sample = fabs(filtered_buffer[k]->get_data()[buffer_offset]);
751 if( sample > max ) max = sample;
756 case ComprMultiConfig::TRIGGER:
757 sample = fabs(trigger_buffer[buffer_offset]);
760 case ComprMultiConfig::SUM: {
762 for( int k = 0; k < channels; k++ ) {
763 sample = fabs(filtered_buffer[k]->get_data()[buffer_offset]);
770 double new_slope = (sample - current_value) / j;
771 // Got equal or higher slope
772 if( new_slope >= current_slope &&
773 (current_slope >= 0 ||
775 target_current_sample = 0;
777 current_slope = new_slope;
778 next_target = sample;
779 previous_target = current_value;
782 if( sample > next_target && current_slope < 0 ) {
783 target_current_sample = 0;
784 target_samples = decay_samples;
785 current_slope = (sample - current_value) /
787 next_target = sample;
788 previous_target = current_value;
791 // Hit end of current slope range without finding higher slope
792 if( target_current_sample >= target_samples ) {
793 target_current_sample = 0;
794 target_samples = decay_samples;
795 current_slope = (sample - current_value) / decay_samples;
796 next_target = sample;
797 previous_target = current_value;
801 // Update current value and multiply gain
802 current_value = (next_target * target_current_sample +
803 previous_target * (target_samples - target_current_sample)) /
806 target_current_sample++;
808 if( plugin->config.smoothing_only ) {
809 for( int j = 0; j < channels; j++ ) {
810 filtered_buffer[j]->get_data()[i] = current_value;
814 if( !plugin->config.bands[band].bypass ) {
815 double gain = plugin->config.calculate_gain(band, current_value);
816 for( int j = 0; j < channels; j++ ) {
817 filtered_buffer[j]->get_data()[i] *= gain;
824 ComprMultiFFT::ComprMultiFFT(ComprMultiEffect *plugin, int channel)
826 this->plugin = plugin;
827 this->channel = channel;
830 ComprMultiFFT::~ComprMultiFFT()
834 int ComprMultiFFT::signal_process(int band)
836 int sample_rate = plugin->PluginAClient::project_sample_rate;
837 BandState *band_state = plugin->band_states[band];
839 // Create new spectrogram frame for updating the GUI
842 #ifndef DRAW_AFTER_BANDPASS
845 ((plugin->config.input != ComprMultiConfig::TRIGGER && channel == 0) ||
846 channel == plugin->config.trigger) ) {
847 #ifndef DRAW_AFTER_BANDPASS
848 int total_data = window_size / 2;
850 int total_data = TOTAL_BANDS * window_size / 2;
853 // store all bands in the same GUI frame
854 frame = new CompressorFreqFrame();
856 frame->data_size = total_data;
857 frame->data = new double[total_data];
858 bzero(frame->data, sizeof(double) * total_data);
859 frame->nyquist = sample_rate / 2;
861 // int attack_samples, release_samples, preview_samples;
862 // band_state->engine->calculate_ranges(&attack_samples,
863 // &release_samples, &preview_samples, sample_rate);
865 // FFT advances 1/2 a window for each spectrogram frame
866 int n = plugin->new_spectrogram_frames[band]++;
867 double sample_pos = (n * window_size / 2) / sample_rate;
868 frame->position = plugin->start_pos + plugin->dir * sample_pos;
870 // printf("ComprMultiFFT::signal_process %d top_position=%ld frame->position=%ld\n",
871 // __LINE__, plugin->get_playhead_position(), frame->position);
872 // printf("ComprMultiFFT::signal_process %d band=%d preview_samples=%d frames size=%ld filtered_size=%ld\n",
873 // __LINE__, band, preview_samples, plugin->new_spectrogram_frames[band] *
874 // window_size, plugin->filtered_size);
877 //printf("ComprMultiFFT::signal_process %d channel=%d band=%d frame=%p\n", __LINE__, channel, band, frame);
878 // apply the bandpass filter
879 for( int i = 0; i < window_size / 2; i++ ) {
880 double fr = freq_real[i], fi = freq_imag[i];
881 double env = band_state->envelope[i];
882 freq_real[i] *= env; freq_imag[i] *= env;
883 double mag = sqrt(fr*fr + fi*fi);
885 // update the spectrogram with the output
886 // neglect the true average & max spectrograms, but always use the trigger
888 int offset = band * window_size / 2 + i;
889 #ifndef DRAW_AFTER_BANDPASS
890 frame->data[offset] = MAX(frame->data[offset], mag);
891 // get the maximum output in the frequency domain
892 if( mag > frame->freq_max )
893 frame->freq_max = mag;
896 frame->data[offset] = MAX(frame->data[offset], mag);
897 // get the maximum output in the frequency domain
898 if( mag > frame->freq_max )
899 frame->freq_max = mag;
904 symmetry(window_size, freq_real, freq_imag);
909 int ComprMultiFFT::post_process(int band)
912 // get the maximum output in the time domain
913 double *buffer = output_real;
914 #ifndef DRAW_AFTER_BANDPASS
915 buffer = input_buffer->get_data();
918 for( int i = 0; i<window_size; ++i ) {
919 if( fabs(buffer[i]) > time_max )
920 time_max = fabs(buffer[i]);
922 if( time_max > frame->time_max )
923 frame->time_max = time_max;
924 plugin->add_gui_frame(frame);
930 int ComprMultiFFT::read_samples(int64_t output_sample,
931 int samples, Samples *buffer)
933 int result = plugin->read_samples(buffer, channel,
934 plugin->get_samplerate(), output_sample, samples);
935 #ifndef DRAW_AFTER_BANDPASS
936 // append unprocessed samples to the input_buffer
937 int new_input_size = plugin->new_input_size + samples;
938 plugin->allocate_input(new_input_size);
939 memcpy(plugin->input_buffer[channel]->get_data() + plugin->new_input_size,
940 buffer->get_data(), samples * sizeof(double));
941 plugin->new_input_size = new_input_size;
942 #endif // !DRAW_AFTER_BANDPASS