00001 #include "bcdisplayinfo.h"
00002 #include "clip.h"
00003 #include "bchash.h"
00004 #include "language.h"
00005 #include "mainprogress.h"
00006 #include "picon_png.h"
00007 #include "resample.h"
00008 #include "timestretch.h"
00009 #include "timestretchengine.h"
00010 #include "transportque.inc"
00011 #include "vframe.h"
00012 #include "filexml.h"
00013
00014 #include <string.h>
00015
00016
00017 #define WINDOW_SIZE 4096
00018 #define INPUT_SIZE 65536
00019 #define OVERSAMPLE 8
00020
00021
00022 REGISTER_PLUGIN(TimeStretch)
00023
00024
00025
00026 PitchEngine::PitchEngine(TimeStretch *plugin)
00027 : CrossfadeFFT()
00028 {
00029 this->plugin = plugin;
00030 last_phase = new double[WINDOW_SIZE];
00031 new_freq = new double[WINDOW_SIZE];
00032 new_magn = new double[WINDOW_SIZE];
00033 sum_phase = new double[WINDOW_SIZE];
00034 anal_magn = new double[WINDOW_SIZE];
00035 anal_freq = new double[WINDOW_SIZE];
00036
00037 input_buffer = 0;
00038 input_size = 0;
00039 input_allocated = 0;
00040 current_output_sample = -100000000000LL;
00041 temp = 0;
00042 }
00043
00044 PitchEngine::~PitchEngine()
00045 {
00046 if(input_buffer) delete [] input_buffer;
00047 if(temp) delete [] temp;
00048 delete [] last_phase;
00049 delete [] new_freq;
00050 delete [] new_magn;
00051 delete [] sum_phase;
00052 delete [] anal_magn;
00053 delete [] anal_freq;
00054 }
00055
00056 int PitchEngine::read_samples(int64_t output_sample,
00057 int samples,
00058 double *buffer)
00059 {
00060
00061
00062
00063 if (current_output_sample != output_sample)
00064 {
00065 input_size = 0;
00066 double input_point = plugin->get_source_start() + (output_sample - plugin->get_source_start()) / plugin->config.scale;
00067 current_input_sample = plugin->local_to_edl((int64_t)input_point);
00068 current_output_sample = output_sample;
00069
00070 }
00071
00072 while(input_size < samples)
00073 {
00074 double scale = plugin->config.scale;
00075 if(!temp) temp = new double[INPUT_SIZE];
00076
00077 plugin->read_samples(temp,
00078 0,
00079 plugin->get_samplerate(),
00080 current_input_sample,
00081 INPUT_SIZE);
00082 current_input_sample +=INPUT_SIZE;
00083
00084 plugin->resample->resample_chunk(temp,
00085 INPUT_SIZE,
00086 1000000,
00087 (int)(1000000 * scale),
00088 0);
00089
00090 int fragment_size = plugin->resample->get_output_size(0);
00091
00092 if(input_size + fragment_size > input_allocated)
00093 {
00094 int new_allocated = input_size + fragment_size;
00095 double *new_buffer = new double[new_allocated];
00096 if(input_buffer)
00097 {
00098 memcpy(new_buffer, input_buffer, input_size * sizeof(double));
00099 delete [] input_buffer;
00100 }
00101 input_buffer = new_buffer;
00102 input_allocated = new_allocated;
00103 }
00104
00105
00106 plugin->resample->read_output(input_buffer + input_size,
00107 0,
00108 fragment_size);
00109 input_size += fragment_size;
00110 }
00111 memcpy(buffer, input_buffer, samples * sizeof(int64_t));
00112 memcpy(input_buffer,
00113 input_buffer + samples,
00114 sizeof(int64_t) * (input_size - samples));
00115 input_size -= samples;
00116 current_output_sample += samples;
00117 return 0;
00118 }
00119
00120 int PitchEngine::signal_process_oversample(int reset)
00121 {
00122 double scale = plugin->config.scale;
00123
00124 memset(new_freq, 0, window_size * sizeof(double));
00125 memset(new_magn, 0, window_size * sizeof(double));
00126
00127 if (reset)
00128 {
00129 memset (last_phase, 0, WINDOW_SIZE * sizeof(double));
00130 memset (sum_phase, 0, WINDOW_SIZE * sizeof(double));
00131 }
00132
00133
00134 if (1)
00135 {
00136
00137 double expected_phase_diff = 2.0 * M_PI / oversample;
00138
00139 double freq_per_bin = (double)plugin->PluginAClient::project_sample_rate / window_size;
00140
00141
00142 for (int i = 0; i < window_size/2; i++)
00143 {
00144
00145 double magn = sqrt(fftw_data[i][0] * fftw_data[i][0] + fftw_data[i][1] * fftw_data[i][1]);
00146 double phase = atan2(fftw_data[i][1], fftw_data[i][0]);
00147
00148
00149 double temp = phase - last_phase[i];
00150 last_phase[i] = phase;
00151
00152
00153 temp -= (double)i * expected_phase_diff;
00154
00155
00156
00157 int qpd = (int)(temp/M_PI);
00158 if (qpd >= 0)
00159 qpd += qpd&1;
00160 else
00161 qpd -= qpd&1;
00162 temp -= M_PI*(double)qpd;
00163
00164
00165 temp = oversample * temp / (2.0 * M_PI);
00166
00167 temp = (double)(temp + i) * freq_per_bin;
00168
00169 anal_magn[i] = magn;
00170 anal_freq[i] = temp;
00171
00172
00173
00174
00175
00176
00177
00178
00179
00180
00181
00182
00183
00184 }
00185
00186 for (int k = 0; k <= window_size/2; k++) {
00187 int index = int(k/scale);
00188 if (index <= window_size/2) {
00189 new_magn[k] += anal_magn[index];
00190 new_freq[k] = anal_freq[index] * scale;
00191 } else{
00192
00193 new_magn[k] = 0;
00194 new_freq[k] = 0;
00195 }
00196 }
00197
00198
00199
00200 for (int i = 0; i < window_size/2; i++)
00201 {
00202 double magn = new_magn[i];
00203 double temp = new_freq[i];
00204
00205 temp -= (double)(i) * freq_per_bin;
00206
00207
00208 temp /= freq_per_bin;
00209
00210
00211 temp = 2.0 * M_PI *temp / oversample;
00212
00213
00214 temp += (double)(i) * expected_phase_diff;
00215
00216
00217 sum_phase[i] += temp;
00218
00219 double phase = sum_phase[i];
00220
00221 fftw_data[i][0] = magn * cos(phase);
00222 fftw_data[i][1] = magn * sin(phase);
00223 }
00224 } else
00225 {
00226 int min_freq =
00227 1 + (int)(20.0 / ((double)plugin->PluginAClient::project_sample_rate /
00228 window_size * 2) + 0.5);
00229 if(plugin->config.scale < 1)
00230 {
00231 for(int i = min_freq; i < window_size / 2; i++)
00232 {
00233 double destination = i * plugin->config.scale;
00234 int dest_i = (int)(destination + 0.5);
00235 if(dest_i != i)
00236 {
00237 if(dest_i <= window_size / 2)
00238 {
00239 fftw_data[dest_i][0] = fftw_data[i][0];
00240 fftw_data[dest_i][1] = fftw_data[i][1];
00241 }
00242 fftw_data[i][0] = 0;
00243 fftw_data[i][1] = 0;
00244 }
00245 }
00246 }
00247 else
00248 if(plugin->config.scale > 1)
00249 {
00250 for(int i = window_size / 2 - 1; i >= min_freq; i--)
00251 {
00252 double destination = i * plugin->config.scale;
00253 int dest_i = (int)(destination + 0.5);
00254 if(dest_i != i)
00255 {
00256 if(dest_i <= window_size / 2)
00257 {
00258 fftw_data[dest_i][0] = fftw_data[i][0];
00259 fftw_data[dest_i][1] = fftw_data[i][1];
00260 }
00261 fftw_data[i][0] = 0;
00262 fftw_data[i][1] = 0;
00263 }
00264 }
00265 }
00266 }
00267
00268
00269 for (int i = window_size/2; i< window_size; i++)
00270 {
00271 fftw_data[i][0] = 0;
00272 fftw_data[i][1] = 0;
00273 }
00274
00275
00276 return 0;
00277 }
00278
00279
00280
00281
00282
00283
00284
00285
00286
00287
00288
00289
00290
00291
00292 TimeStretch::TimeStretch(PluginServer *server)
00293 : PluginAClient(server)
00294 {
00295 PLUGIN_CONSTRUCTOR_MACRO
00296 load_defaults();
00297 temp = 0;
00298 pitch = 0;
00299 resample = 0;
00300 stretch = 0;
00301 input = 0;
00302 input_allocated = 0;
00303 }
00304
00305
00306 TimeStretch::~TimeStretch()
00307 {
00308 PLUGIN_DESTRUCTOR_MACRO
00309 if(temp) delete [] temp;
00310 if(input) delete [] input;
00311 if(pitch) delete pitch;
00312 if(resample) delete resample;
00313 if(stretch) delete stretch;
00314 }
00315
00316
00317
00318 char* TimeStretch::plugin_title() { return N_("Time stretch"); }
00319 int TimeStretch::is_realtime() { return 1; }
00320
00321 void TimeStretch::read_data(KeyFrame *keyframe)
00322 {
00323 FileXML input;
00324 input.set_shared_string(keyframe->data, strlen(keyframe->data));
00325
00326 int result = 0;
00327 while(!result)
00328 {
00329 result = input.read_tag();
00330
00331 if(!result)
00332 {
00333 if(input.tag.title_is("TIMESTRETCH"))
00334 {
00335 config.scale = input.tag.get_property("SCALE", config.scale);
00336 }
00337 }
00338 }
00339 }
00340
00341 void TimeStretch::save_data(KeyFrame *keyframe)
00342 {
00343 FileXML output;
00344 output.set_shared_string(keyframe->data, MESSAGESIZE);
00345
00346 output.tag.set_title("TIMESTRETCH");
00347 output.tag.set_property("SCALE", config.scale);
00348 output.append_tag();
00349 output.tag.set_title("/TIMESTRETCH");
00350 output.append_tag();
00351 output.append_newline();
00352
00353 output.terminate_string();
00354 }
00355
00356
00357
00358 int TimeStretch::load_defaults()
00359 {
00360 char directory[BCTEXTLEN];
00361
00362
00363 sprintf(directory, "%stimestretch.rc", BCASTDIR);
00364
00365 defaults = new BC_Hash(directory);
00366 defaults->load();
00367
00368 config.scale = defaults->get("SCALE", (double)1);
00369 return 0;
00370 }
00371
00372 int TimeStretch::save_defaults()
00373 {
00374 defaults->update("SCALE", config.scale);
00375 defaults->save();
00376 return 0;
00377 }
00378
00379
00380 TimeStretchConfig::TimeStretchConfig()
00381 {
00382 scale = 1.0;
00383 }
00384
00385 int TimeStretchConfig::equivalent(TimeStretchConfig &that)
00386 {
00387 return EQUIV(scale, that.scale);
00388 }
00389
00390 void TimeStretchConfig::copy_from(TimeStretchConfig &that)
00391 {
00392 scale = that.scale;
00393 }
00394
00395 void TimeStretchConfig::interpolate(TimeStretchConfig &prev,
00396 TimeStretchConfig &next,
00397 int64_t prev_frame,
00398 int64_t next_frame,
00399 int64_t current_frame)
00400 {
00401 double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
00402 double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
00403 scale = prev.scale * prev_scale + next.scale * next_scale;
00404 }
00405
00406
00407
00408
00409 LOAD_CONFIGURATION_MACRO(TimeStretch, TimeStretchConfig)
00410
00411 SHOW_GUI_MACRO(TimeStretch, TimeStretchThread)
00412
00413 RAISE_WINDOW_MACRO(TimeStretch)
00414
00415 SET_STRING_MACRO(TimeStretch)
00416
00417 NEW_PICON_MACRO(TimeStretch)
00418
00419
00420 void TimeStretch::update_gui()
00421 {
00422 if(thread)
00423 {
00424 load_configuration();
00425 thread->window->lock_window("TimeStretch::update_gui");
00426 thread->window->update();
00427 thread->window->unlock_window();
00428 }
00429 }
00430
00431
00432
00433 int TimeStretch::get_parameters()
00434 {
00435 BC_DisplayInfo info;
00436 TimeStretchWindow window(this, info.get_abs_cursor_x(), info.get_abs_cursor_y());
00437 window.create_objects();
00438 int result = window.run_window();
00439
00440 return result;
00441 }
00442
00443
00444
00445 int TimeStretch::process_buffer(int64_t size,
00446 double *buffer,
00447 int64_t start_position,
00448 int sample_rate)
00449 {
00450 load_configuration();
00451
00452 int result = 0;
00453
00454 if(!pitch)
00455 {
00456 pitch = new PitchEngine(this);
00457 pitch->initialize(WINDOW_SIZE);
00458 pitch->set_oversample(OVERSAMPLE);
00459 resample = new Resample(0, 1);
00460
00461 }
00462
00463 pitch->process_buffer_oversample(start_position,
00464 size,
00465 buffer,
00466 get_direction());
00467
00468
00469 return result;
00470 }
00471
00472
00473
00474 PLUGIN_THREAD_OBJECT(TimeStretch, TimeStretchThread, TimeStretchWindow)
00475
00476
00477 TimeStretchWindow::TimeStretchWindow(TimeStretch *plugin, int x, int y)
00478 : BC_Window(plugin->gui_string,
00479 x,
00480 y,
00481 150,
00482 50,
00483 150,
00484 50,
00485 0,
00486 0,
00487 1)
00488 {
00489 this->plugin = plugin;
00490 }
00491
00492 void TimeStretchWindow::create_objects()
00493 {
00494 int x = 10, y = 10;
00495
00496 add_subwindow(new BC_Title(x, y, _("Scale:")));
00497 x += 70;
00498 add_subwindow(scale = new TimeStretchScale(plugin, x, y));
00499 show_window();
00500 flush();
00501 }
00502
00503 WINDOW_CLOSE_EVENT(TimeStretchWindow)
00504
00505 void TimeStretchWindow::update()
00506 {
00507 scale->update(plugin->config.scale);
00508 }
00509
00510
00511
00512 TimeStretchScale::TimeStretchScale(TimeStretch *plugin, int x, int y)
00513 : BC_FPot(x, y, (float)plugin->config.scale, .3, 2)
00514 {
00515 this->plugin = plugin;
00516 set_precision(0.001);
00517 }
00518
00519 int TimeStretchScale::handle_event()
00520 {
00521 plugin->config.scale = get_value();
00522 plugin->send_configure_change();
00523 return 1;
00524 }
00525
00526
00527
00528
00529
00530
00531
00532
00533