-
-/*
- * CINELERRA
- * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- */
-
-#include "bcdisplayinfo.h"
-#include "clip.h"
-#include "bchash.h"
-#include "filexml.h"
-#include "language.h"
-#include "pitch.h"
-#include "samples.h"
-#include "theme.h"
-#include "units.h"
-#include "vframe.h"
-
-#include <math.h>
-#include <string.h>
-
-
-// It needs to be at least 40Hz yet high enough to have enough precision
-#define WINDOW_SIZE 2048
-
-
-//#define WINDOW_SIZE 131072
-
-REGISTER_PLUGIN(PitchEffect);
-
-
-
-
-
-PitchEffect::PitchEffect(PluginServer *server)
- : PluginAClient(server)
-{
-
- reset();
-}
-
-PitchEffect::~PitchEffect()
-{
-
-
- if(fft) delete fft;
-}
-
-const char* PitchEffect::plugin_title() { return _("Pitch shift"); }
-int PitchEffect::is_realtime() { return 1; }
-
-
-
-void PitchEffect::read_data(KeyFrame *keyframe)
-{
- FileXML input;
- input.set_shared_input(keyframe->get_data(), strlen(keyframe->get_data()));
-
- int result = 0;
- while(!result)
- {
- result = input.read_tag();
-
- if(!result)
- {
- if(input.tag.title_is("PITCH"))
- {
- config.scale = input.tag.get_property("SCALE", config.scale);
- config.size = input.tag.get_property("SIZE", config.size);
- }
- }
- }
-}
-
-void PitchEffect::save_data(KeyFrame *keyframe)
-{
- FileXML output;
- output.set_shared_output(keyframe->get_data(), MESSAGESIZE);
-
- output.tag.set_title("PITCH");
- output.tag.set_property("SCALE", config.scale);
- output.tag.set_property("SIZE", config.size);
- output.append_tag();
- output.tag.set_title("/PITCH");
- output.append_tag();
- output.append_newline();
- output.terminate_string();
-}
-
-
-
-LOAD_CONFIGURATION_MACRO(PitchEffect, PitchConfig)
-
-NEW_WINDOW_MACRO(PitchEffect, PitchWindow)
-
-
-
-void PitchEffect::reset()
-{
- fft = 0;
-}
-
-void PitchEffect::update_gui()
-{
- if(thread)
- {
- if(load_configuration())
- {
- thread->window->lock_window("PitchEffect::update_gui");
- ((PitchWindow*)thread->window)->update();
- thread->window->unlock_window();
- }
- }
-}
-
-
-
-int PitchEffect::process_buffer(int64_t size,
- Samples *buffer,
- int64_t start_position,
- int sample_rate)
-{
-//printf("PitchEffect::process_buffer %d\n", __LINE__);
- load_configuration();
-
- if(fft && config.size != fft->window_size)
- {
- delete fft;
- fft = 0;
- }
-
- if(!fft)
- {
- fft = new PitchFFT(this);
- fft->initialize(config.size);
- }
-
- fft->process_buffer(start_position,
- size,
- buffer,
- get_direction());
-
- return 0;
-}
-
-
-
-
-
-
-
-
-
-PitchFFT::PitchFFT(PitchEffect *plugin)
- : CrossfadeFFT()
-{
- this->plugin = plugin;
- last_phase = 0;
- new_freq = 0;
- new_magn = 0;
- sum_phase = 0;
- anal_magn = 0;
- anal_freq = 0;
-}
-
-PitchFFT::~PitchFFT()
-{
- delete [] last_phase;
- delete [] new_freq;
- delete [] new_magn;
- delete [] sum_phase;
- delete [] anal_magn;
- delete [] anal_freq;
-}
-
-
-int PitchFFT::signal_process()
-{
-#if 0
- double scale = plugin->config.scale;
- double oversample = 1.0;
-
- if(!new_freq)
- {
- last_phase = new double[window_size];
- new_freq = new double[window_size];
- new_magn = new double[window_size];
- sum_phase = new double[window_size];
- anal_magn = new double[window_size];
- anal_freq = new double[window_size];
- memset (last_phase, 0, window_size * sizeof(double));
- memset (sum_phase, 0, window_size * sizeof(double));
- }
-
- memset(new_freq, 0, window_size * sizeof(double));
- memset(new_magn, 0, window_size * sizeof(double));
-
-// expected phase difference between windows
- double expected_phase_diff = 2.0 * M_PI / oversample;
-// frequency per bin
- double freq_per_bin = (double)plugin->PluginAClient::project_sample_rate / window_size;
-
- for (int i = 0; i < window_size / 2; i++)
- {
-// Convert to magnitude and phase
- double magn = sqrt(freq_real[i] * freq_real[i] + freq_imag[i] * freq_imag[i]);
- double phase = atan2(freq_imag[i], freq_real[i]);
-
-// Remember last phase
- double temp = phase - last_phase[i];
- last_phase[i] = phase;
-
-// Substract the expected advancement of phase
- temp -= (double)i * expected_phase_diff;
-
-
-// wrap temp into -/+ PI ... good trick!
- int qpd = (int)(temp/M_PI);
- if (qpd >= 0)
- qpd += qpd & 1;
- else
- qpd -= qpd & 1;
- temp -= M_PI * (double)qpd;
-
-// Deviation from bin frequency
- temp = oversample * temp / (2.0 * M_PI);
-
- temp = (double)(temp + i) * freq_per_bin;
-
-// Now temp is the real freq... move it!
- int new_bin = (int)(i * scale);
- if (new_bin >= 0 && new_bin < window_size / 2)
- {
- new_freq[new_bin] = temp*scale;
- new_magn[new_bin] += magn;
- }
-
- }
-
-
-
-
-// Synthesize back the fft window
- for (int i = 0; i < window_size / 2; i++)
- {
- double magn = new_magn[i];
- double temp = new_freq[i];
-// substract the bin frequency
- temp -= (double)(i) * freq_per_bin;
-
-// get bin deviation from freq deviation
- temp /= freq_per_bin;
-
-// oversample
- temp = 2.0 * M_PI * temp / oversample;
-
-// add back the expected phase difference (that we substracted in analysis)
- temp += (double)(i) * expected_phase_diff;
-
-// accumulate delta phase, to get bin phase
- sum_phase[i] += temp;
-
- double phase = sum_phase[i];
-
- freq_real[i] = magn * cos(phase);
- freq_imag[i] = magn * sin(phase);
- }
-
- for (int i = window_size / 2; i < window_size; i++)
- {
- freq_real[i] = 0;
- freq_imag[i] = 0;
- }
-#endif
-
-
-#if 1
- int min_freq =
- 1 + (int)(20.0 / ((double)plugin->PluginAClient::project_sample_rate /
- window_size * 2) + 0.5);
- if(plugin->config.scale < 1)
- {
- for(int i = min_freq; i < window_size / 2; i++)
- {
- double destination = i * plugin->config.scale;
- int dest_i = (int)(destination + 0.5);
- if(dest_i != i)
- {
- if(dest_i <= window_size / 2)
- {
- freq_real[dest_i] = freq_real[i];
- freq_imag[dest_i] = freq_imag[i];
- }
- freq_real[i] = 0;
- freq_imag[i] = 0;
- }
- }
- }
- else
- if(plugin->config.scale > 1)
- {
- for(int i = window_size / 2 - 1; i >= min_freq; i--)
- {
- double destination = i * plugin->config.scale;
- int dest_i = (int)(destination + 0.5);
- if(dest_i != i)
- {
- if(dest_i <= window_size / 2)
- {
- freq_real[dest_i] = freq_real[i];
- freq_imag[dest_i] = freq_imag[i];
- }
- freq_real[i] = 0;
- freq_imag[i] = 0;
- }
- }
- }
-
- symmetry(window_size, freq_real, freq_imag);
-#endif
-
-
- return 0;
-}
-
-int PitchFFT::read_samples(int64_t output_sample,
- int samples,
- Samples *buffer)
-{
- return plugin->read_samples(buffer,
- 0,
- plugin->get_samplerate(),
- output_sample,
- samples);
-}
-
-
-
-
-
-
-
-PitchConfig::PitchConfig()
-{
- scale = 1.0;
- size = 2048;
-}
-
-int PitchConfig::equivalent(PitchConfig &that)
-{
- return EQUIV(scale, that.scale) && size == that.size;
-}
-
-void PitchConfig::copy_from(PitchConfig &that)
-{
- scale = that.scale;
- size = that.size;
-}
-
-void PitchConfig::interpolate(PitchConfig &prev,
- PitchConfig &next,
- int64_t prev_frame,
- int64_t next_frame,
- int64_t current_frame)
-{
- double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
- double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
- scale = prev.scale * prev_scale + next.scale * next_scale;
- size = prev.size;
-}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-PitchWindow::PitchWindow(PitchEffect *plugin)
- : PluginClientWindow(plugin,
- 150,
- 100,
- 150,
- 100,
- 0)
-{
- this->plugin = plugin;
-}
-
-void PitchWindow::create_objects()
-{
- int x1 = 10, x = 10, y = 10;
-
- BC_Title *title;
- add_subwindow(title = new BC_Title(x, y, _("Scale:")));
- x += title->get_w() + plugin->get_theme()->widget_border;
- add_subwindow(scale = new PitchScale(plugin, x, y));
- x = x1;
- y += scale->get_h() + plugin->get_theme()->widget_border;
- add_subwindow(title = new BC_Title(x, y, _("Window Size:")));
- y += title->get_h() + plugin->get_theme()->widget_border;
- add_subwindow(size = new PitchSize(this, plugin, x, y));
- size->create_objects();
- size->update(plugin->config.size);
- show_window();
- flush();
-}
-
-
-
-void PitchWindow::update()
-{
- scale->update(plugin->config.scale);
- size->update(plugin->config.size);
-}
-
-
-
-
-
-
-
-
-
-
-
-
-PitchScale::PitchScale(PitchEffect *plugin, int x, int y)
- : BC_FPot(x, y, (float)plugin->config.scale, .5, 1.5)
-{
- this->plugin = plugin;
- set_precision(0.01);
-}
-
-int PitchScale::handle_event()
-{
- plugin->config.scale = get_value();
- plugin->send_configure_change();
- return 1;
-}
-
-
-
-PitchSize::PitchSize(PitchWindow *window, PitchEffect *plugin, int x, int y)
- : BC_PopupMenu(x, y, 100, "4096", 1)
-{
- this->plugin = plugin;
-}
-
-int PitchSize::handle_event()
-{
- plugin->config.size = atoi(get_text());
- plugin->send_configure_change();
- return 1;
-}
-
-void PitchSize::create_objects()
-{
- add_item(new BC_MenuItem("2048"));
- add_item(new BC_MenuItem("4096"));
- add_item(new BC_MenuItem("8192"));
- add_item(new BC_MenuItem("16384"));
- add_item(new BC_MenuItem("32768"));
- add_item(new BC_MenuItem("65536"));
- add_item(new BC_MenuItem("131072"));
- add_item(new BC_MenuItem("262144"));
-}
-
-void PitchSize::update(int size)
-{
- char string[BCTEXTLEN];
- sprintf(string, "%d", size);
- set_text(string);
-}
-
-
-
-
-
-
-
-
-
-
-
-
-
-