1 #include "bcdisplayinfo.h"
5 #include "mainprogress.h"
8 #include "timestretch.h"
9 #include "timestretchengine.h"
10 #include "transportque.inc"
17 #define WINDOW_SIZE 4096
18 #define INPUT_SIZE 65536
22 REGISTER_PLUGIN(TimeStretch)
26 PitchEngine::PitchEngine(TimeStretch *plugin)
29 this->plugin = plugin;
30 last_phase = new double[WINDOW_SIZE];
31 new_freq = new double[WINDOW_SIZE];
32 new_magn = new double[WINDOW_SIZE];
33 sum_phase = new double[WINDOW_SIZE];
34 anal_magn = new double[WINDOW_SIZE];
35 anal_freq = new double[WINDOW_SIZE];
40 current_output_sample = -100000000000LL;
47 PitchEngine::~PitchEngine()
49 if(input_buffer) delete [] input_buffer;
50 if(temp) delete [] temp;
62 int PitchEngine::read_samples(int64_t output_sample,
67 // FIXME, make sure this is set at the beginning, always
68 // FIXME: we need to do backward play also
69 if (current_output_sample != output_sample)
72 double input_point = plugin->get_source_start() + (output_sample - plugin->get_source_start()) / plugin->config.scale;
73 current_input_sample = plugin->local_to_edl((int64_t)input_point);
74 current_output_sample = output_sample;
78 while(input_size < samples)
80 double scale = plugin->config.scale;
81 if(!temp) temp = new double[INPUT_SIZE];
83 plugin->read_samples(temp,
85 plugin->get_samplerate(),
88 current_input_sample +=INPUT_SIZE;
90 plugin->resample->resample_chunk(temp,
93 (int)(1000000 * scale),
96 int fragment_size = plugin->resample->get_output_size(0);
98 if(input_size + fragment_size > input_allocated)
100 int new_allocated = input_size + fragment_size;
101 double *new_buffer = new double[new_allocated];
104 memcpy(new_buffer, input_buffer, input_size * sizeof(double));
105 delete [] input_buffer;
107 input_buffer = new_buffer;
108 input_allocated = new_allocated;
112 plugin->resample->read_output(input_buffer + input_size,
115 input_size += fragment_size;
117 memcpy(buffer, input_buffer, samples * sizeof(int64_t));
119 input_buffer + samples,
120 sizeof(int64_t) * (input_size - samples));
121 input_size -= samples;
122 current_output_sample += samples;
126 int PitchEngine::signal_process_oversample(int reset)
128 double scale = plugin->config.scale;
130 memset(new_freq, 0, window_size * sizeof(double));
131 memset(new_magn, 0, window_size * sizeof(double));
135 memset (last_phase, 0, WINDOW_SIZE * sizeof(double));
136 memset (sum_phase, 0, WINDOW_SIZE * sizeof(double));
139 // new or old behaviour
142 // expected phase difference between windows
143 double expected_phase_diff = 2.0 * M_PI / oversample;
145 double freq_per_bin = (double)plugin->PluginAClient::project_sample_rate / window_size;
148 for (int i = 0; i < window_size/2; i++)
150 // Convert to magnitude and phase
151 double magn = sqrt(fftw_data[i][0] * fftw_data[i][0] + fftw_data[i][1] * fftw_data[i][1]);
152 double phase = atan2(fftw_data[i][1], fftw_data[i][0]);
154 // Remember last phase
155 double temp = phase - last_phase[i];
156 last_phase[i] = phase;
158 // Substract the expected advancement of phase
159 temp -= (double)i * expected_phase_diff;
162 // wrap temp into -/+ PI ... good trick!
163 int qpd = (int)(temp/M_PI);
168 temp -= M_PI*(double)qpd;
170 // Deviation from bin frequency
171 temp = oversample * temp / (2.0 * M_PI);
173 temp = (double)(temp + i) * freq_per_bin;
178 // Now temp is the real freq... move it!
179 // int new_bin = (int)(temp * scale / freq_per_bin + 0.5);
180 /* int new_bin = (int)(i *scale);
181 if (new_bin >= 0 && new_bin < window_size/2)
183 // double tot_magn = new_magn[new_bin] + magn;
185 // new_freq[new_bin] = (new_freq[new_bin] * new_magn[new_bin] + temp *scale* magn) / tot_magn;
186 new_freq[new_bin] = temp*scale;
187 new_magn[new_bin] += magn;
192 for (int k = 0; k <= window_size/2; k++) {
193 int index = int(k/scale);
194 if (index <= window_size/2) {
195 new_magn[k] += anal_magn[index];
196 new_freq[k] = anal_freq[index] * scale;
205 // Synthesize back the fft window
206 for (int i = 0; i < window_size/2; i++)
208 double magn = new_magn[i];
209 double temp = new_freq[i];
210 // substract the bin frequency
211 temp -= (double)(i) * freq_per_bin;
213 // get bin deviation from freq deviation
214 temp /= freq_per_bin;
217 temp = 2.0 * M_PI *temp / oversample;
219 // add back the expected phase difference (that we substracted in analysis)
220 temp += (double)(i) * expected_phase_diff;
222 // accumulate delta phase, to get bin phase
223 sum_phase[i] += temp;
225 double phase = sum_phase[i];
227 fftw_data[i][0] = magn * cos(phase);
228 fftw_data[i][1] = magn * sin(phase);
233 1 + (int)(20.0 / ((double)plugin->PluginAClient::project_sample_rate /
234 window_size * 2) + 0.5);
235 if(plugin->config.scale < 1)
237 for(int i = min_freq; i < window_size / 2; i++)
239 double destination = i * plugin->config.scale;
240 int dest_i = (int)(destination + 0.5);
243 if(dest_i <= window_size / 2)
245 fftw_data[dest_i][0] = fftw_data[i][0];
246 fftw_data[dest_i][1] = fftw_data[i][1];
254 if(plugin->config.scale > 1)
256 for(int i = window_size / 2 - 1; i >= min_freq; i--)
258 double destination = i * plugin->config.scale;
259 int dest_i = (int)(destination + 0.5);
262 if(dest_i <= window_size / 2)
264 fftw_data[dest_i][0] = fftw_data[i][0];
265 fftw_data[dest_i][1] = fftw_data[i][1];
274 //symmetry(window_size, freq_real, freq_imag);
275 for (int i = window_size/2; i< window_size; i++)
298 TimeStretch::TimeStretch(PluginServer *server)
299 : PluginAClient(server)
301 PLUGIN_CONSTRUCTOR_MACRO
312 TimeStretch::~TimeStretch()
314 PLUGIN_DESTRUCTOR_MACRO
315 if(temp) delete [] temp;
316 if(input) delete [] input;
317 if(pitch) delete pitch;
318 if(resample) delete resample;
319 if(stretch) delete stretch;
324 char* TimeStretch::plugin_title() { return N_("Time stretch"); }
325 int TimeStretch::is_realtime() { return 1; }
327 void TimeStretch::read_data(KeyFrame *keyframe)
330 input.set_shared_string(keyframe->data, strlen(keyframe->data));
335 result = input.read_tag();
339 if(input.tag.title_is("TIMESTRETCH"))
341 config.scale = input.tag.get_property("SCALE", config.scale);
347 void TimeStretch::save_data(KeyFrame *keyframe)
350 output.set_shared_string(keyframe->data, MESSAGESIZE);
352 output.tag.set_title("TIMESTRETCH");
353 output.tag.set_property("SCALE", config.scale);
355 output.append_newline();
357 output.terminate_string();
362 int TimeStretch::load_defaults()
364 char directory[BCTEXTLEN];
366 // set the default directory
367 sprintf(directory, "%stimestretch.rc", BCASTDIR);
369 defaults = new Defaults(directory);
372 config.scale = defaults->get("SCALE", (double)1);
376 int TimeStretch::save_defaults()
378 defaults->update("SCALE", config.scale);
384 TimeStretchConfig::TimeStretchConfig()
389 int TimeStretchConfig::equivalent(TimeStretchConfig &that)
391 return EQUIV(scale, that.scale);
394 void TimeStretchConfig::copy_from(TimeStretchConfig &that)
399 void TimeStretchConfig::interpolate(TimeStretchConfig &prev,
400 TimeStretchConfig &next,
403 int64_t current_frame)
405 double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
406 double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
407 scale = prev.scale * prev_scale + next.scale * next_scale;
413 LOAD_CONFIGURATION_MACRO(TimeStretch, TimeStretchConfig)
415 SHOW_GUI_MACRO(TimeStretch, TimeStretchThread)
417 RAISE_WINDOW_MACRO(TimeStretch)
419 SET_STRING_MACRO(TimeStretch)
421 NEW_PICON_MACRO(TimeStretch)
424 void TimeStretch::update_gui()
428 load_configuration();
429 thread->window->lock_window("TimeStretch::update_gui");
430 thread->window->update();
431 thread->window->unlock_window();
437 int TimeStretch::get_parameters()
440 TimeStretchWindow window(this, info.get_abs_cursor_x(), info.get_abs_cursor_y());
441 window.create_objects();
442 int result = window.run_window();
448 //int TimeStretch::process_loop(double *buffer, int64_t &write_length)
449 int TimeStretch::process_buffer(int64_t size,
451 int64_t start_position,
454 load_configuration();
460 pitch = new PitchEngine(this);
461 pitch->initialize(WINDOW_SIZE);
462 pitch->set_oversample(OVERSAMPLE);
463 resample = new Resample(0, 1);
467 pitch->process_buffer_oversample(start_position,
478 PLUGIN_THREAD_OBJECT(TimeStretch, TimeStretchThread, TimeStretchWindow)
481 TimeStretchWindow::TimeStretchWindow(TimeStretch *plugin, int x, int y)
482 : BC_Window(plugin->gui_string,
493 this->plugin = plugin;
496 void TimeStretchWindow::create_objects()
500 add_subwindow(new BC_Title(x, y, _("Scale:")));
502 add_subwindow(scale = new TimeStretchScale(plugin, x, y));
507 WINDOW_CLOSE_EVENT(TimeStretchWindow)
509 void TimeStretchWindow::update()
511 scale->update(plugin->config.scale);
516 TimeStretchScale::TimeStretchScale(TimeStretch *plugin, int x, int y)
517 : BC_FPot(x, y, (float)plugin->config.scale, .3, 2)
519 this->plugin = plugin;
520 set_precision(0.001);
523 int TimeStretchScale::handle_event()
525 plugin->config.scale = get_value();
526 plugin->send_configure_change();