00001 #include "bcdisplayinfo.h"
00002 #include "clip.h"
00003 #include "bchash.h"
00004 #include "filexml.h"
00005 #include "language.h"
00006 #include "pitch.h"
00007 #include "picon_png.h"
00008 #include "units.h"
00009 #include "vframe.h"
00010
00011 #include <math.h>
00012 #include <string.h>
00013
00014
00015
00016 #define WINDOW_SIZE 8192
00017 #define OVERSAMPLE 8
00018
00019
00020
00021
00022 REGISTER_PLUGIN(PitchEffect);
00023
00024
00025
00026
00027
00028 PitchEffect::PitchEffect(PluginServer *server)
00029 : PluginAClient(server)
00030 {
00031 PLUGIN_CONSTRUCTOR_MACRO
00032 reset();
00033 }
00034
00035 PitchEffect::~PitchEffect()
00036 {
00037 PLUGIN_DESTRUCTOR_MACRO
00038
00039 if(fft) delete fft;
00040 }
00041
00042 char* PitchEffect::plugin_title() { return N_("Pitch shift"); }
00043 int PitchEffect::is_realtime() { return 1; }
00044
00045
00046
00047 void PitchEffect::read_data(KeyFrame *keyframe)
00048 {
00049 FileXML input;
00050 input.set_shared_string(keyframe->data, strlen(keyframe->data));
00051
00052 int result = 0;
00053 while(!result)
00054 {
00055 result = input.read_tag();
00056
00057 if(!result)
00058 {
00059 if(input.tag.title_is("PITCH"))
00060 {
00061 config.scale = input.tag.get_property("SCALE", config.scale);
00062 }
00063 }
00064 }
00065 }
00066
00067 void PitchEffect::save_data(KeyFrame *keyframe)
00068 {
00069 FileXML output;
00070 output.set_shared_string(keyframe->data, MESSAGESIZE);
00071
00072 output.tag.set_title("PITCH");
00073 output.tag.set_property("SCALE", config.scale);
00074 output.append_tag();
00075 output.tag.set_title("/PITCH");
00076 output.append_tag();
00077 output.append_newline();
00078
00079 output.terminate_string();
00080 }
00081
00082 int PitchEffect::load_defaults()
00083 {
00084 char directory[BCTEXTLEN], string[BCTEXTLEN];
00085 sprintf(directory, "%spitch.rc", BCASTDIR);
00086 defaults = new BC_Hash(directory);
00087 defaults->load();
00088
00089 config.scale = defaults->get("SCALE", config.scale);
00090 return 0;
00091 }
00092
00093 int PitchEffect::save_defaults()
00094 {
00095 char string[BCTEXTLEN];
00096
00097 defaults->update("SCALE", config.scale);
00098 defaults->save();
00099
00100 return 0;
00101 }
00102
00103
00104 LOAD_CONFIGURATION_MACRO(PitchEffect, PitchConfig)
00105
00106 SHOW_GUI_MACRO(PitchEffect, PitchThread)
00107
00108 RAISE_WINDOW_MACRO(PitchEffect)
00109
00110 SET_STRING_MACRO(PitchEffect)
00111
00112 NEW_PICON_MACRO(PitchEffect)
00113
00114
00115 void PitchEffect::reset()
00116 {
00117 fft = 0;
00118 }
00119
00120 void PitchEffect::update_gui()
00121 {
00122 if(thread)
00123 {
00124 load_configuration();
00125 thread->window->lock_window("PitchEffect::update_gui");
00126 thread->window->update();
00127 thread->window->unlock_window();
00128 }
00129 }
00130
00131
00132
00133 int PitchEffect::process_buffer(int64_t size,
00134 double *buffer,
00135 int64_t start_position,
00136 int sample_rate)
00137 {
00138 load_configuration();
00139
00140
00141 if(!fft)
00142 {
00143 fft = new PitchFFT(this);
00144 fft->initialize(WINDOW_SIZE);
00145 fft->set_oversample(OVERSAMPLE);
00146 }
00147
00148 fft->process_buffer_oversample(start_position,
00149 size,
00150 buffer,
00151 get_direction());
00152
00153 return 0;
00154 }
00155
00156
00157
00158
00159
00160
00161
00162
00163
00164 PitchFFT::PitchFFT(PitchEffect *plugin)
00165 : CrossfadeFFT()
00166 {
00167 this->plugin = plugin;
00168 last_phase = new double[WINDOW_SIZE];
00169 new_freq = new double[WINDOW_SIZE];
00170 new_magn = new double[WINDOW_SIZE];
00171 sum_phase = new double[WINDOW_SIZE];
00172 anal_magn = new double[WINDOW_SIZE];
00173 anal_freq = new double[WINDOW_SIZE];
00174
00175 }
00176
00177 PitchFFT::~PitchFFT()
00178 {
00179 delete [] last_phase;
00180 delete [] new_freq;
00181 delete [] new_magn;
00182 delete [] sum_phase;
00183 delete [] anal_magn;
00184 delete [] anal_freq;
00185 }
00186
00187
00188 int PitchFFT::signal_process_oversample(int reset)
00189 {
00190 double scale = plugin->config.scale;
00191
00192 memset(new_freq, 0, window_size * sizeof(double));
00193 memset(new_magn, 0, window_size * sizeof(double));
00194
00195 if (reset)
00196 {
00197 memset (last_phase, 0, WINDOW_SIZE * sizeof(double));
00198 memset (sum_phase, 0, WINDOW_SIZE * sizeof(double));
00199 }
00200
00201
00202 double expected_phase_diff = 2.0 * M_PI / oversample;
00203
00204 double freq_per_bin = (double)plugin->PluginAClient::project_sample_rate / window_size;
00205
00206
00207 for (int i = 0; i < window_size/2; i++)
00208 {
00209
00210 double magn = sqrt(fftw_data[i][0] * fftw_data[i][0] + fftw_data[i][1] * fftw_data[i][1]);
00211 double phase = atan2(fftw_data[i][1], fftw_data[i][0]);
00212
00213
00214 double temp = phase - last_phase[i];
00215 last_phase[i] = phase;
00216
00217
00218 temp -= (double)i * expected_phase_diff;
00219
00220
00221
00222 int qpd = (int)(temp/M_PI);
00223 if (qpd >= 0)
00224 qpd += qpd&1;
00225 else
00226 qpd -= qpd&1;
00227 temp -= M_PI*(double)qpd;
00228
00229
00230 temp = oversample * temp / (2.0 * M_PI);
00231
00232 temp = (double)(temp + i) * freq_per_bin;
00233
00234
00235
00236
00237
00238
00239 int new_bin = (int)(i * scale);
00240 if (new_bin >= 0 && new_bin < window_size/2)
00241 {
00242
00243
00244
00245 new_freq[new_bin] = temp*scale;
00246 new_magn[new_bin] += magn;
00247 }
00248
00249 }
00250
00251
00252
00253
00254
00255
00256
00257
00258
00259
00260
00261
00262
00263
00264
00265 for (int i = 0; i < window_size/2; i++)
00266 {
00267 double magn = new_magn[i];
00268 double temp = new_freq[i];
00269
00270 temp -= (double)(i) * freq_per_bin;
00271
00272
00273 temp /= freq_per_bin;
00274
00275
00276 temp = 2.0 * M_PI *temp / oversample;
00277
00278
00279 temp += (double)(i) * expected_phase_diff;
00280
00281
00282 sum_phase[i] += temp;
00283
00284 double phase = sum_phase[i];
00285
00286 fftw_data[i][0] = magn * cos(phase);
00287 fftw_data[i][1] = magn * sin(phase);
00288 }
00289
00290
00291 for (int i = window_size/2; i< window_size; i++)
00292 {
00293 fftw_data[i][0] = 0;
00294 fftw_data[i][1] = 0;
00295 }
00296
00297
00298 return 0;
00299 }
00300
00301 int PitchFFT::read_samples(int64_t output_sample,
00302 int samples,
00303 double *buffer)
00304 {
00305 return plugin->read_samples(buffer,
00306 0,
00307 plugin->get_samplerate(),
00308 output_sample,
00309 samples);
00310 }
00311
00312
00313
00314
00315
00316
00317
00318 PitchConfig::PitchConfig()
00319 {
00320 scale = 1.0;
00321 }
00322
00323 int PitchConfig::equivalent(PitchConfig &that)
00324 {
00325 return EQUIV(scale, that.scale);
00326 }
00327
00328 void PitchConfig::copy_from(PitchConfig &that)
00329 {
00330 scale = that.scale;
00331 }
00332
00333 void PitchConfig::interpolate(PitchConfig &prev,
00334 PitchConfig &next,
00335 int64_t prev_frame,
00336 int64_t next_frame,
00337 int64_t current_frame)
00338 {
00339 double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
00340 double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
00341 scale = prev.scale * prev_scale + next.scale * next_scale;
00342 }
00343
00344
00345
00346
00347
00348
00349
00350
00351 PLUGIN_THREAD_OBJECT(PitchEffect, PitchThread, PitchWindow)
00352
00353
00354
00355
00356
00357
00358
00359
00360
00361 PitchWindow::PitchWindow(PitchEffect *plugin, int x, int y)
00362 : BC_Window(plugin->gui_string,
00363 x,
00364 y,
00365 150,
00366 50,
00367 150,
00368 50,
00369 0,
00370 0,
00371 1)
00372 {
00373 this->plugin = plugin;
00374 }
00375
00376 void PitchWindow::create_objects()
00377 {
00378 int x = 10, y = 10;
00379
00380 add_subwindow(new BC_Title(x, y, _("Scale:")));
00381 x += 70;
00382 add_subwindow(scale = new PitchScale(plugin, x, y));
00383 show_window();
00384 flush();
00385 }
00386
00387 WINDOW_CLOSE_EVENT(PitchWindow)
00388
00389 void PitchWindow::update()
00390 {
00391 scale->update(plugin->config.scale);
00392 }
00393
00394
00395
00396
00397
00398
00399
00400
00401
00402
00403
00404
00405 PitchScale::PitchScale(PitchEffect *plugin, int x, int y)
00406 : BC_FPot(x, y, (float)plugin->config.scale, .5, 1.5)
00407 {
00408 this->plugin = plugin;
00409 set_precision(0.01);
00410 }
00411
00412 int PitchScale::handle_event()
00413 {
00414 plugin->config.scale = get_value();
00415 plugin->send_configure_change();
00416 return 1;
00417 }
00418
00419
00420
00421
00422
00423
00424
00425
00426
00427
00428
00429
00430
00431
00432
00433