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;
44 PitchEngine::~PitchEngine()
46 if(input_buffer) delete [] input_buffer;
47 if(temp) delete [] temp;
56 int PitchEngine::read_samples(int64_t output_sample,
61 // FIXME, make sure this is set at the beginning, always
62 // FIXME: we need to do backward play also
63 if (current_output_sample != output_sample)
66 double input_point = plugin->get_source_start() + (output_sample - plugin->get_source_start()) / plugin->config.scale;
67 current_input_sample = plugin->local_to_edl((int64_t)input_point);
68 current_output_sample = output_sample;
72 while(input_size < samples)
74 double scale = plugin->config.scale;
75 if(!temp) temp = new double[INPUT_SIZE];
77 plugin->read_samples(temp,
79 plugin->get_samplerate(),
82 current_input_sample +=INPUT_SIZE;
84 plugin->resample->resample_chunk(temp,
87 (int)(1000000 * scale),
90 int fragment_size = plugin->resample->get_output_size(0);
92 if(input_size + fragment_size > input_allocated)
94 int new_allocated = input_size + fragment_size;
95 double *new_buffer = new double[new_allocated];
98 memcpy(new_buffer, input_buffer, input_size * sizeof(double));
99 delete [] input_buffer;
101 input_buffer = new_buffer;
102 input_allocated = new_allocated;
106 plugin->resample->read_output(input_buffer + input_size,
109 input_size += fragment_size;
111 memcpy(buffer, input_buffer, samples * sizeof(int64_t));
113 input_buffer + samples,
114 sizeof(int64_t) * (input_size - samples));
115 input_size -= samples;
116 current_output_sample += samples;
120 int PitchEngine::signal_process_oversample(int reset)
122 double scale = plugin->config.scale;
124 memset(new_freq, 0, window_size * sizeof(double));
125 memset(new_magn, 0, window_size * sizeof(double));
129 memset (last_phase, 0, WINDOW_SIZE * sizeof(double));
130 memset (sum_phase, 0, WINDOW_SIZE * sizeof(double));
133 // new or old behaviour
136 // expected phase difference between windows
137 double expected_phase_diff = 2.0 * M_PI / oversample;
139 double freq_per_bin = (double)plugin->PluginAClient::project_sample_rate / window_size;
142 for (int i = 0; i < window_size/2; i++)
144 // Convert to magnitude and phase
145 double magn = sqrt(fftw_data[i][0] * fftw_data[i][0] + fftw_data[i][1] * fftw_data[i][1]);
146 double phase = atan2(fftw_data[i][1], fftw_data[i][0]);
148 // Remember last phase
149 double temp = phase - last_phase[i];
150 last_phase[i] = phase;
152 // Substract the expected advancement of phase
153 temp -= (double)i * expected_phase_diff;
156 // wrap temp into -/+ PI ... good trick!
157 int qpd = (int)(temp/M_PI);
162 temp -= M_PI*(double)qpd;
164 // Deviation from bin frequency
165 temp = oversample * temp / (2.0 * M_PI);
167 temp = (double)(temp + i) * freq_per_bin;
172 // Now temp is the real freq... move it!
173 // int new_bin = (int)(temp * scale / freq_per_bin + 0.5);
174 /* int new_bin = (int)(i *scale);
175 if (new_bin >= 0 && new_bin < window_size/2)
177 // double tot_magn = new_magn[new_bin] + magn;
179 // new_freq[new_bin] = (new_freq[new_bin] * new_magn[new_bin] + temp *scale* magn) / tot_magn;
180 new_freq[new_bin] = temp*scale;
181 new_magn[new_bin] += magn;
186 for (int k = 0; k <= window_size/2; k++) {
187 int index = int(k/scale);
188 if (index <= window_size/2) {
189 new_magn[k] += anal_magn[index];
190 new_freq[k] = anal_freq[index] * scale;
199 // Synthesize back the fft window
200 for (int i = 0; i < window_size/2; i++)
202 double magn = new_magn[i];
203 double temp = new_freq[i];
204 // substract the bin frequency
205 temp -= (double)(i) * freq_per_bin;
207 // get bin deviation from freq deviation
208 temp /= freq_per_bin;
211 temp = 2.0 * M_PI *temp / oversample;
213 // add back the expected phase difference (that we substracted in analysis)
214 temp += (double)(i) * expected_phase_diff;
216 // accumulate delta phase, to get bin phase
217 sum_phase[i] += temp;
219 double phase = sum_phase[i];
221 fftw_data[i][0] = magn * cos(phase);
222 fftw_data[i][1] = magn * sin(phase);
227 1 + (int)(20.0 / ((double)plugin->PluginAClient::project_sample_rate /
228 window_size * 2) + 0.5);
229 if(plugin->config.scale < 1)
231 for(int i = min_freq; i < window_size / 2; i++)
233 double destination = i * plugin->config.scale;
234 int dest_i = (int)(destination + 0.5);
237 if(dest_i <= window_size / 2)
239 fftw_data[dest_i][0] = fftw_data[i][0];
240 fftw_data[dest_i][1] = fftw_data[i][1];
248 if(plugin->config.scale > 1)
250 for(int i = window_size / 2 - 1; i >= min_freq; i--)
252 double destination = i * plugin->config.scale;
253 int dest_i = (int)(destination + 0.5);
256 if(dest_i <= window_size / 2)
258 fftw_data[dest_i][0] = fftw_data[i][0];
259 fftw_data[dest_i][1] = fftw_data[i][1];
268 //symmetry(window_size, freq_real, freq_imag);
269 for (int i = window_size/2; i< window_size; i++)
292 TimeStretch::TimeStretch(PluginServer *server)
293 : PluginAClient(server)
295 PLUGIN_CONSTRUCTOR_MACRO
306 TimeStretch::~TimeStretch()
308 PLUGIN_DESTRUCTOR_MACRO
309 if(temp) delete [] temp;
310 if(input) delete [] input;
311 if(pitch) delete pitch;
312 if(resample) delete resample;
313 if(stretch) delete stretch;
318 char* TimeStretch::plugin_title() { return N_("Time stretch"); }
319 int TimeStretch::is_realtime() { return 1; }
321 void TimeStretch::read_data(KeyFrame *keyframe)
324 input.set_shared_string(keyframe->data, strlen(keyframe->data));
329 result = input.read_tag();
333 if(input.tag.title_is("TIMESTRETCH"))
335 config.scale = input.tag.get_property("SCALE", config.scale);
341 void TimeStretch::save_data(KeyFrame *keyframe)
344 output.set_shared_string(keyframe->data, MESSAGESIZE);
346 output.tag.set_title("TIMESTRETCH");
347 output.tag.set_property("SCALE", config.scale);
349 output.append_newline();
351 output.terminate_string();
356 int TimeStretch::load_defaults()
358 char directory[BCTEXTLEN];
360 // set the default directory
361 sprintf(directory, "%stimestretch.rc", BCASTDIR);
363 defaults = new BC_Hash(directory);
366 config.scale = defaults->get("SCALE", (double)1);
370 int TimeStretch::save_defaults()
372 defaults->update("SCALE", config.scale);
378 TimeStretchConfig::TimeStretchConfig()
383 int TimeStretchConfig::equivalent(TimeStretchConfig &that)
385 return EQUIV(scale, that.scale);
388 void TimeStretchConfig::copy_from(TimeStretchConfig &that)
393 void TimeStretchConfig::interpolate(TimeStretchConfig &prev,
394 TimeStretchConfig &next,
397 int64_t current_frame)
399 double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
400 double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
401 scale = prev.scale * prev_scale + next.scale * next_scale;
407 LOAD_CONFIGURATION_MACRO(TimeStretch, TimeStretchConfig)
409 SHOW_GUI_MACRO(TimeStretch, TimeStretchThread)
411 RAISE_WINDOW_MACRO(TimeStretch)
413 SET_STRING_MACRO(TimeStretch)
415 NEW_PICON_MACRO(TimeStretch)
418 void TimeStretch::update_gui()
422 load_configuration();
423 thread->window->lock_window("TimeStretch::update_gui");
424 thread->window->update();
425 thread->window->unlock_window();
431 int TimeStretch::get_parameters()
434 TimeStretchWindow window(this, info.get_abs_cursor_x(), info.get_abs_cursor_y());
435 window.create_objects();
436 int result = window.run_window();
442 //int TimeStretch::process_loop(double *buffer, int64_t &write_length)
443 int TimeStretch::process_buffer(int64_t size,
445 int64_t start_position,
448 load_configuration();
454 pitch = new PitchEngine(this);
455 pitch->initialize(WINDOW_SIZE);
456 pitch->set_oversample(OVERSAMPLE);
457 resample = new Resample(0, 1);
461 pitch->process_buffer_oversample(start_position,
472 PLUGIN_THREAD_OBJECT(TimeStretch, TimeStretchThread, TimeStretchWindow)
475 TimeStretchWindow::TimeStretchWindow(TimeStretch *plugin, int x, int y)
476 : BC_Window(plugin->gui_string,
487 this->plugin = plugin;
490 void TimeStretchWindow::create_objects()
494 add_subwindow(new BC_Title(x, y, _("Scale:")));
496 add_subwindow(scale = new TimeStretchScale(plugin, x, y));
501 WINDOW_CLOSE_EVENT(TimeStretchWindow)
503 void TimeStretchWindow::update()
505 scale->update(plugin->config.scale);
510 TimeStretchScale::TimeStretchScale(TimeStretch *plugin, int x, int y)
511 : BC_FPot(x, y, (float)plugin->config.scale, .3, 2)
513 this->plugin = plugin;
514 set_precision(0.001);
517 int TimeStretchScale::handle_event()
519 plugin->config.scale = get_value();
520 plugin->send_configure_change();