r134: Heroine Virtual's release 1.1.8
[cinelerra_cv/mob.git] / hvirtual / plugins / timestretch / timestretch.C
blobfa2a8f255f616f6a5b4b3092997f7cc54fd872d1
1 #include "bcdisplayinfo.h"
2 #include "clip.h"
3 #include "defaults.h"
4 #include "mainprogress.h"
5 #include "picon_png.h"
6 #include "resample.h"
7 #include "timestretch.h"
8 #include "timestretchengine.h"
9 #include "vframe.h"
11 #define WINDOW_SIZE 8192
14 PluginClient* new_plugin(PluginServer *server)
16         return new TimeStretch(server);
22 TimeStretchFraction::TimeStretchFraction(TimeStretch *plugin, int x, int y)
23  : BC_TextBox(x, y, 100, 1, (float)plugin->scale)
25         this->plugin = plugin;
28 int TimeStretchFraction::handle_event()
30         plugin->scale = atof(get_text());
31         return 1;
38 TimeStretchFreq::TimeStretchFreq(TimeStretch *plugin, 
39         TimeStretchWindow *gui, 
40         int x, 
41         int y)
42  : BC_Radial(x, y, plugin->use_fft, "Use fast fourier transform")
44         this->plugin = plugin;
45         this->gui = gui;
48 int TimeStretchFreq::handle_event()
50         plugin->use_fft = 1;
51         update(1);
52         gui->time->update(0);
60 TimeStretchTime::TimeStretchTime(TimeStretch *plugin, 
61         TimeStretchWindow *gui, 
62         int x, 
63         int y)
64  : BC_Radial(x, y, !plugin->use_fft, "Use overlapping windows")
66         this->plugin = plugin;
67         this->gui = gui;
70 int TimeStretchTime::handle_event()
72         plugin->use_fft = 0;
73         update(1);
74         gui->freq->update(0);
88 TimeStretchWindow::TimeStretchWindow(TimeStretch *plugin, int x, int y)
89  : BC_Window(PROGRAM_NAME ": Time stretch", 
90                                 x - 160,
91                                 y - 75,
92                                 320, 
93                                 150, 
94                                 320, 
95                                 150,
96                                 0,
97                                 0,
98                                 1)
100         this->plugin = plugin;
104 TimeStretchWindow::~TimeStretchWindow()
108 void TimeStretchWindow::create_objects()
110         int x = 10, y = 10;
112         add_subwindow(new BC_Title(x, y, "Fraction of original length:"));
113         y += 20;
114         add_subwindow(new TimeStretchFraction(plugin, x, y));
116         y += 30;
117         add_subwindow(freq = new TimeStretchFreq(plugin, this, x, y));
118         y += 20;
119         add_subwindow(time = new TimeStretchTime(plugin, this, x, y));
121         add_subwindow(new BC_OKButton(this));
122         add_subwindow(new BC_CancelButton(this));
123         show_window();
127         flush();
139 PitchEngine::PitchEngine(TimeStretch *plugin)
140  : CrossfadeFFT()
142         this->plugin = plugin;
146 int PitchEngine::signal_process()
149         int min_freq = 
150                 1 + (int)(20.0 / ((double)plugin->PluginAClient::project_sample_rate / window_size * 2) + 0.5);
152         if(plugin->scale < 1)
153         {
154                 for(int i = min_freq; i < window_size / 2; i++)
155                 {
156                         double destination = i * plugin->scale;
157                         int dest_i = (int)(destination + 0.5);
158                         if(dest_i != i)
159                         {
160                                 if(dest_i <= window_size / 2)
161                                 {
162                                         freq_real[dest_i] = freq_real[i];
163                                         freq_imag[dest_i] = freq_imag[i];
164                                 }
165                                 freq_real[i] = 0;
166                                 freq_imag[i] = 0;
167                         }
168                 }
169         }
170         else
171         if(plugin->scale > 1)
172         {
173 //printf("PitchEngine::signal_process 1\n");
174                 for(int i = window_size / 2 - 1; i >= min_freq; i--)
175                 {
176                         double destination = i * plugin->scale;
177                         int dest_i = (int)(destination + 0.5);
178                         if(dest_i != i)
179                         {
180                                 if(dest_i <= window_size / 2)
181                                 {
182                                         freq_real[dest_i] = freq_real[i];
183                                         freq_imag[dest_i] = freq_imag[i];
184                                 }
185                                 freq_real[i] = 0;
186                                 freq_imag[i] = 0;
187                         }
188                 }
189 //printf("PitchEngine::signal_process 1\n");
190         }
192         symmetry(window_size, freq_real, freq_imag);
193 //printf("PitchEngine::signal_process 2\n");
194         return 0;
210 TimeStretch::TimeStretch(PluginServer *server)
211  : PluginAClient(server)
213         load_defaults();
214         temp = 0;
215         pitch = 0;
216         resample = 0;
217         stretch = 0;
218         input = 0;
219         input_allocated = 0;
223 TimeStretch::~TimeStretch()
225         save_defaults();
226         delete defaults;
227         if(temp) delete [] temp;
228         if(input) delete [] input;
229         if(pitch) delete pitch;
230         if(resample) delete resample;
231         if(stretch) delete stretch;
234         
235         
236 char* TimeStretch::plugin_title()
238         return "Time stretch";
241 int TimeStretch::get_parameters()
243         BC_DisplayInfo info;
244         TimeStretchWindow window(this, info.get_abs_cursor_x(), info.get_abs_cursor_y());
245         window.create_objects();
246         int result = window.run_window();
247         
248         return result;
251 VFrame* TimeStretch::new_picon()
253         return new VFrame(picon_png);
257 int TimeStretch::start_loop()
259         if(PluginClient::interactive)
260         {
261                 char string[BCTEXTLEN];
262                 sprintf(string, "%s...", plugin_title());
263                 progress = start_progress(string, 
264                         (int64_t)((double)(PluginClient::end - PluginClient::start) * scale));
265         }
267         current_position = PluginClient::start;
268         total_written = 0;
272 // The FFT case
273         if(use_fft)
274         {
275                 pitch = new PitchEngine(this);
276                 pitch->initialize(WINDOW_SIZE);
277                 resample = new Resample(0, 1);
278         }
279         else
280 // The windowing case
281         {
282 // Must be short enough to mask beating but long enough to mask humming.
283                 stretch = new TimeStretchEngine(scale, PluginAClient::project_sample_rate);
284         }
288         
289         return 0;
292 int TimeStretch::stop_loop()
294         if(PluginClient::interactive)
295         {
296                 progress->stop_progress();
297                 delete progress;
298         }
299         return 0;
302 int TimeStretch::process_loop(double *buffer, int64_t &write_length)
304 //printf("TimeStretch::process_loop 1\n");
305         int result = 0;
306 // Length to read based on desired output size
307         int64_t size = (int64_t)((double)PluginAClient::in_buffer_size / scale);
308         int64_t predicted_total = (int64_t)((double)(PluginClient::end - PluginClient::start) * scale + 0.5);
309         int samples_rendered = 0;
311         if(input_allocated < size)
312         {
313                 if(input) delete [] input;
314                 input = new double[size];
315                 input_allocated = size;
316         }
318         read_samples(input, 0, current_position, size);
319         current_position += size;
322 // The FFT case
323         if(use_fft)
324         {
326                 resample->resample_chunk(input, 
327                         size, 
328                         1000000, 
329                         (int)(1000000.0 * scale), 
330                         0);
333                 if(resample->get_output_size(0))
334                 {
335                         int64_t output_size = resample->get_output_size(0);
336                         if(temp && temp_allocated < output_size)
337                         {
338                                 delete [] temp;
339                                 temp = 0;
340                         }
342                         if(!temp)
343                         {
344                                 temp = new double[output_size];
345                                 temp_allocated = output_size;
346                         }
347                         resample->read_output(temp, 0, output_size);
350                         samples_rendered = pitch->process_fifo(output_size, 
351                                 temp, 
352                                 buffer);
355                 }
356         }
357         else
358 // The windowing case
359         {
360 //printf("TimeStretch::process_loop 10\n");
361                 samples_rendered = stretch->process(input, size);
362 //printf("TimeStretch::process_loop 20 %d\n", samples_rendered);
363                 if(samples_rendered)
364                 {
365                         samples_rendered = MIN(samples_rendered, PluginAClient::in_buffer_size);
366                         stretch->read_output(buffer, samples_rendered);
367                 }
368 //printf("TimeStretch::process_loop 30\n");
369         }
372         if(samples_rendered)
373         {
374                 total_written += samples_rendered;
375         }
377 // Trim output to predicted length of stretched selection.
378         if(total_written > predicted_total)
379         {
380                 samples_rendered -= total_written - predicted_total;
381                 result = 1;
382         }
385         write_length = samples_rendered;
386         if(PluginClient::interactive) result = progress->update(total_written);
387 //printf("TimeStretch::process_loop 100\n");
389         return result;
394 int TimeStretch::load_defaults()
396         char directory[BCTEXTLEN];
398 // set the default directory
399         sprintf(directory, "%stimestretch.rc", BCASTDIR);
400 // load the defaults
401         defaults = new Defaults(directory);
402         defaults->load();
404         scale = defaults->get("SCALE", (double)1);
405         use_fft = defaults->get("USE_FFT", 0);
406         return 0;
409 int TimeStretch::save_defaults()
411         defaults->update("SCALE", scale);
412         defaults->update("USE_FFT", use_fft);
413         defaults->save();
414         return 0;