add auto zoombar/status color, fix 3 batchrender boobies, rotate plugin tweaks, add...
[goodguy/cinelerra.git] / cinelerra-5.1 / plugins / audioscope / audioscope.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 1997-2011 Adam Williams <broadcast at earthling dot net>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  */
21
22 #include "audioscope.h"
23 #include "bcdisplayinfo.h"
24 #include "bcsignals.h"
25 #include "clip.h"
26 #include "cursors.h"
27 #include "bchash.h"
28 #include "filexml.h"
29 #include "language.h"
30 #include "bccolors.h"
31 #include "samples.h"
32 #include "theme.h"
33 #include "transportque.inc"
34 #include "units.h"
35 #include "vframe.h"
36
37
38 #include <string.h>
39
40
41
42 REGISTER_PLUGIN(AudioScope)
43
44
45 AudioScopeConfig::AudioScopeConfig()
46 {
47         window_size = MAX_WINDOW;
48         history_size = 4;
49         mode = XY_MODE;
50         trigger_level = 0;
51 }
52
53 int AudioScopeConfig::equivalent(AudioScopeConfig &that)
54 {
55         return window_size == that.window_size &&
56                 history_size == that.history_size &&
57                 mode == that.mode &&
58                 EQUIV(trigger_level, that.trigger_level);
59 }
60
61 void AudioScopeConfig::copy_from(AudioScopeConfig &that)
62 {
63         window_size = that.window_size;
64         history_size = that.history_size;
65         mode = that.mode;
66         trigger_level = that.trigger_level;
67 }
68
69 void AudioScopeConfig::interpolate(AudioScopeConfig &prev,
70         AudioScopeConfig &next,
71         int64_t prev_frame,
72         int64_t next_frame,
73         int64_t current_frame)
74 {
75         copy_from(prev);
76
77         CLAMP(history_size, MIN_HISTORY, MAX_HISTORY - 1);
78 }
79
80
81
82 AudioScopeFrame::AudioScopeFrame(int data_size, int channels)
83 {
84         this->size = data_size;
85         this->channels = channels;
86         for(int i = 0; i < CHANNELS; i++)
87                 data[i] = new float[data_size];
88         force = 0;
89 }
90
91 AudioScopeFrame::~AudioScopeFrame()
92 {
93         for(int i = 0; i < CHANNELS; i++)
94                 delete [] data[i];
95 }
96
97
98
99
100
101
102
103 AudioScopeHistory::AudioScopeHistory(AudioScope *plugin,
104         int x,
105         int y)
106  : BC_IPot(x,
107         y,
108         plugin->config.history_size,
109         MIN_HISTORY,
110         MAX_HISTORY)
111 {
112         this->plugin = plugin;
113 }
114
115 int AudioScopeHistory::handle_event()
116 {
117         plugin->config.history_size = get_value();
118         plugin->send_configure_change();
119         return 1;
120 }
121
122
123
124
125
126
127
128
129 AudioScopeWindowSize::AudioScopeWindowSize(AudioScope *plugin,
130         int x,
131         int y,
132         char *text)
133  : BC_PopupMenu(x,
134         y,
135         80,
136         text)
137 {
138         this->plugin = plugin;
139 }
140
141 int AudioScopeWindowSize::handle_event()
142 {
143         plugin->config.window_size = atoi(get_text());
144         plugin->send_configure_change();
145         return 1;
146 }
147
148
149 AudioScopeWindowSizeTumbler::AudioScopeWindowSizeTumbler(AudioScope *plugin, int x, int y)
150  : BC_Tumbler(x,
151         y)
152 {
153         this->plugin = plugin;
154 }
155
156 int AudioScopeWindowSizeTumbler::handle_up_event()
157 {
158         plugin->config.window_size *= 2;
159         if(plugin->config.window_size > MAX_WINDOW)
160                 plugin->config.window_size = MAX_WINDOW;
161         char string[BCTEXTLEN];
162         sprintf(string, "%d", plugin->config.window_size);
163         ((AudioScopeWindow*)plugin->get_thread()->get_window())->window_size->set_text(string);
164         plugin->send_configure_change();
165         return 1;
166 }
167
168 int AudioScopeWindowSizeTumbler::handle_down_event()
169 {
170         plugin->config.window_size /= 2;
171         if(plugin->config.window_size < MIN_WINDOW)
172                 plugin->config.window_size = MIN_WINDOW;
173         char string[BCTEXTLEN];
174         sprintf(string, "%d", plugin->config.window_size);
175         ((AudioScopeWindow*)plugin->get_thread()->get_window())->window_size->set_text(string);
176         plugin->send_configure_change();
177         return 1;
178 }
179
180
181
182
183
184 AudioScopeCanvas::AudioScopeCanvas(AudioScope *plugin,
185         int x,
186         int y,
187         int w,
188         int h)
189  : BC_SubWindow(x, y, w, h, BLACK)
190 {
191         this->plugin = plugin;
192         current_operation = NONE;
193 }
194
195 int AudioScopeCanvas::button_press_event()
196 {
197         if(is_event_win() && cursor_inside())
198         {
199                 calculate_point();
200                 current_operation = DRAG;
201                 plugin->send_configure_change();
202                 return 1;
203         }
204         return 0;
205 }
206
207 int AudioScopeCanvas::button_release_event()
208 {
209         if(current_operation == DRAG)
210         {
211                 current_operation = NONE;
212                 return 1;
213         }
214         return 0;
215 }
216
217 int AudioScopeCanvas::cursor_motion_event()
218 {
219         if(current_operation == DRAG)
220         {
221                 calculate_point();
222                 return 1;
223         }
224         return 0;
225 }
226
227
228 void AudioScopeCanvas::calculate_point()
229 {
230         int x = get_cursor_x();
231         int y = get_cursor_y();
232         CLAMP(x, 0, get_w() - 1);
233         CLAMP(y, 0, get_h() - 1);
234
235         ((AudioScopeWindow*)plugin->thread->window)->calculate_probe(
236                 x,
237                 y,
238                 1);
239
240 //printf("AudioScopeCanvas::calculate_point %d %d\n", __LINE__, Freq::tofreq(freq_index));
241 }
242
243
244
245
246
247
248
249 AudioScopeTriggerLevel::AudioScopeTriggerLevel(AudioScope *plugin, int x, int y)
250  : BC_FPot(x, y, plugin->config.trigger_level, (float)-1.0, (float)1.0)
251 {
252         this->plugin = plugin;
253         set_precision(0.01);
254 }
255
256 int AudioScopeTriggerLevel::handle_event()
257 {
258         AudioScopeWindow *window = (AudioScopeWindow*)plugin->thread->window;
259         window->draw_trigger();
260         plugin->config.trigger_level = get_value();
261         plugin->send_configure_change();
262         window->draw_trigger();
263         window->canvas->flash();
264         return 1;
265 }
266
267
268
269
270
271 AudioScopeMode::AudioScopeMode(AudioScope *plugin,
272         int x,
273         int y)
274  : BC_PopupMenu(x,
275         y,
276         180,
277         mode_to_text(plugin->config.mode))
278 {
279         this->plugin = plugin;
280 }
281
282 void AudioScopeMode::create_objects()
283 {
284         add_item(new BC_MenuItem(mode_to_text(XY_MODE)));
285         add_item(new BC_MenuItem(mode_to_text(WAVEFORM_NO_TRIGGER)));
286         add_item(new BC_MenuItem(mode_to_text(WAVEFORM_RISING_TRIGGER)));
287         add_item(new BC_MenuItem(mode_to_text(WAVEFORM_FALLING_TRIGGER)));
288 }
289
290 int AudioScopeMode::handle_event()
291 {
292         if(plugin->config.mode != text_to_mode(get_text()))
293         {
294                 AudioScopeWindow *window = (AudioScopeWindow*)plugin->thread->window;
295                 window->probe_x = -1;
296                 window->probe_y = -1;
297                 plugin->config.mode = text_to_mode(get_text());
298                 plugin->send_configure_change();
299
300                 window->canvas->clear_box(0, 0, window->canvas->get_w(), window->canvas->get_h());
301                 window->draw_overlay();
302                 window->canvas->flash();
303         }
304         return 1;
305 }
306
307 const char* AudioScopeMode::mode_to_text(int mode)
308 {
309         switch(mode)
310         {
311                 case XY_MODE:
312                         return _("XY Mode");
313                 case WAVEFORM_NO_TRIGGER:
314                         return _("Waveform");
315                 case WAVEFORM_RISING_TRIGGER:
316                         return _("Rising Trigger");
317                 case WAVEFORM_FALLING_TRIGGER:
318                 default:
319                         return _("Falling Trigger");
320         }
321 }
322
323 int AudioScopeMode::text_to_mode(const char *text)
324 {
325         if(!strcmp(mode_to_text(XY_MODE), text)) return XY_MODE;
326         if(!strcmp(mode_to_text(WAVEFORM_NO_TRIGGER), text)) return WAVEFORM_NO_TRIGGER;
327         if(!strcmp(mode_to_text(WAVEFORM_RISING_TRIGGER), text)) return WAVEFORM_RISING_TRIGGER;
328         if(!strcmp(mode_to_text(WAVEFORM_FALLING_TRIGGER), text)) return WAVEFORM_FALLING_TRIGGER;
329         return XY_MODE;
330 }
331
332
333
334
335
336
337
338
339
340 AudioScopeWindow::AudioScopeWindow(AudioScope *plugin)
341  : PluginClientWindow(plugin,
342         plugin->w,
343         plugin->h,
344         320,
345         320,
346         1)
347 {
348         this->plugin = plugin;
349 }
350
351 AudioScopeWindow::~AudioScopeWindow()
352 {
353 }
354
355 void AudioScopeWindow::create_objects()
356 {
357         int x = plugin->get_theme()->widget_border;
358         int y = 0;
359         char string[BCTEXTLEN];
360
361         add_subwindow(canvas = new AudioScopeCanvas(plugin,
362                 0,
363                 0,
364                 get_w(),
365                 get_h() -
366                         plugin->get_theme()->widget_border * 4 -
367                         BC_Title::calculate_h(this, "X") * 3));
368         canvas->set_cursor(CROSS_CURSOR, 0, 0);
369         draw_overlay();
370
371         y += canvas->get_h() + plugin->get_theme()->widget_border;
372
373         add_subwindow(history_size_title = new BC_Title(x, y, _("History Size:")));
374         x += history_size_title->get_w() + plugin->get_theme()->widget_border;
375         add_subwindow(history_size = new AudioScopeHistory(plugin,
376                 x,
377                 y));
378         x += history_size->get_w() + plugin->get_theme()->widget_border;
379
380
381         int x1 = x;
382         sprintf(string, "%d", plugin->config.window_size);
383         add_subwindow(window_size_title = new BC_Title(x, y, _("Window Size:")));
384
385         x += window_size_title->get_w() + plugin->get_theme()->widget_border;
386         add_subwindow(window_size = new AudioScopeWindowSize(plugin, x, y, string));
387         x += window_size->get_w();
388         add_subwindow(window_size_tumbler = new AudioScopeWindowSizeTumbler(plugin, x, y));
389
390         for(int i = MIN_WINDOW; i <= MAX_WINDOW; i *= 2)
391         {
392                 sprintf(string, "%d", i);
393                 window_size->add_item(new BC_MenuItem(string));
394         }
395
396         x += window_size_tumbler->get_w() + plugin->get_theme()->widget_border;
397
398         int y1 = y;
399         x = x1;
400         y += window_size->get_h() + plugin->get_theme()->widget_border;
401         add_subwindow(mode_title = new BC_Title(x, y, _("Mode:")));
402
403         x += mode_title->get_w() + plugin->get_theme()->widget_border;
404         add_subwindow(mode = new AudioScopeMode(plugin, x, y));
405         mode->create_objects();
406
407         x += mode->get_w() + plugin->get_theme()->widget_border;
408         y = y1;
409         add_subwindow(trigger_level_title = new BC_Title(x, y, _("Trigger level:")));
410
411         x += trigger_level_title->get_w() + plugin->get_theme()->widget_border;
412         add_subwindow(trigger_level = new AudioScopeTriggerLevel(plugin, x, y));
413
414
415         x += trigger_level->get_w() + plugin->get_theme()->widget_border;
416         y = y1;
417         add_subwindow(probe_sample = new BC_Title(x, y, _("Sample: 0")));
418         y += probe_sample->get_h() + plugin->get_theme()->widget_border;
419         add_subwindow(probe_level0 = new BC_Title(x, y, _("Level 0: 0"), MEDIUMFONT, CHANNEL0_COLOR));
420         y += probe_level0->get_h() + plugin->get_theme()->widget_border;
421         add_subwindow(probe_level1 = new BC_Title(x, y, _("Level 1: 0"), MEDIUMFONT, CHANNEL1_COLOR));
422         y += probe_level1->get_h() + plugin->get_theme()->widget_border;
423
424
425
426         show_window();
427 }
428
429 int AudioScopeWindow::resize_event(int w, int h)
430 {
431         int y_diff = h - get_h();
432
433         int canvas_factor = get_h() - canvas->get_h();
434         canvas->reposition_window(0,
435                 0,
436                 w,
437                 h - canvas_factor);
438         canvas->clear_box(0, 0, canvas->get_w(), canvas->get_h());
439         draw_overlay();
440         canvas->flash(0);
441
442
443 // Remove all columns which may be a different size.
444         plugin->frame_buffer.remove_all_objects();
445
446         window_size_title->reposition_window(window_size_title->get_x(),
447                 window_size_title->get_y() + y_diff);
448         window_size->reposition_window(window_size->get_x(),
449                 window_size->get_y() + y_diff);
450         window_size_tumbler->reposition_window(window_size_tumbler->get_x(),
451                 window_size_tumbler->get_y() + y_diff);
452
453
454
455         history_size_title->reposition_window(history_size_title->get_x(),
456                 history_size_title->get_y() + y_diff);
457         history_size->reposition_window(history_size->get_x(),
458                 history_size->get_y() + y_diff);
459
460         mode_title->reposition_window(mode_title->get_x(),
461                 mode_title->get_y() + y_diff);
462         mode->reposition_window(mode->get_x(),
463                 mode->get_y() + y_diff);
464
465
466         trigger_level_title->reposition_window(trigger_level_title->get_x(),
467                 trigger_level_title->get_y() + y_diff);
468         trigger_level->reposition_window(trigger_level->get_x(),
469                 trigger_level->get_y() + y_diff);
470
471
472         probe_sample->reposition_window(probe_sample->get_x(),
473                 probe_sample->get_y() + y_diff);
474         probe_level0->reposition_window(probe_level0->get_x(),
475                 probe_level0->get_y() + y_diff);
476         probe_level1->reposition_window(probe_level1->get_x(),
477                 probe_level1->get_y() + y_diff);
478
479         flush();
480
481         plugin->w = w;
482         plugin->h = h;
483         return 0;
484 }
485
486 void AudioScopeWindow::draw_overlay()
487 {
488         canvas->set_color(GREEN);
489
490         canvas->draw_line(0,
491                 canvas->get_h() / 2,
492                 canvas->get_w(),
493                 canvas->get_h() / 2);
494         if(plugin->config.mode == XY_MODE)
495         {
496                 canvas->draw_line(canvas->get_w() / 2,
497                         0,
498                         canvas->get_w() / 2,
499                         canvas->get_h());
500         }
501
502         draw_trigger();
503         draw_probe();
504 //printf("AudioScopeWindow::draw_overlay %d\n", __LINE__);
505 }
506
507 void AudioScopeWindow::draw_trigger()
508 {
509         if(plugin->config.mode == WAVEFORM_RISING_TRIGGER ||
510                 plugin->config.mode == WAVEFORM_FALLING_TRIGGER)
511         {
512                 int y = (int)(-plugin->config.trigger_level *
513                         canvas->get_h() / 2 +
514                         canvas->get_h() / 2);
515                 CLAMP(y, 0, canvas->get_h() - 1);
516                 canvas->set_inverse();
517                 canvas->set_color(RED);
518                 canvas->draw_line(0, y, canvas->get_w(), y);
519                 canvas->set_opaque();
520         }
521 }
522
523 void AudioScopeWindow::draw_probe()
524 {
525         if(probe_x >= 0 || probe_y >= 0)
526         {
527                 canvas->set_color(GREEN);
528                 canvas->set_inverse();
529
530                 if(plugin->config.mode == XY_MODE)
531                 {
532                         canvas->draw_line(0, probe_y, get_w(), probe_y);
533                         canvas->draw_line(probe_x, 0, probe_x, get_h());
534                 }
535                 else
536                 {
537                         canvas->draw_line(probe_x, 0, probe_x, get_h());
538                 }
539
540                 canvas->set_opaque();
541         }
542 }
543
544
545
546
547 void AudioScopeWindow::calculate_probe(int x, int y, int do_overlay)
548 {
549         if(x < 0 && y < 0) return;
550
551 // Clear previous overlay
552         if(do_overlay) draw_probe();
553
554 // New probe position
555         probe_x = x;
556         probe_y = y;
557
558         int sample = 0;
559         double channel0 = 0;
560         double channel1 = 0;
561         if(plugin->config.mode == XY_MODE)
562         {
563                 channel0 = (float)(probe_x - canvas->get_w() / 2) / (canvas->get_w() / 2);
564                 channel1 = (float)(canvas->get_h() / 2 - probe_y) / (canvas->get_h() / 2);
565         }
566         else
567         if(plugin->current_frame)
568         {
569                 sample = probe_x * plugin->current_frame->size / canvas->get_w();
570                 CLAMP(sample, 0, plugin->current_frame->size - 1);
571                 channel0 = plugin->current_frame->data[0][sample];
572                 channel1 = plugin->current_frame->data[1][sample];
573         }
574
575
576         char string[BCTEXTLEN];
577         sprintf(string, _("Sample: %d"), sample);
578         probe_sample->update(string);
579
580         sprintf(string, _("Level 0: %.2f"), channel0);
581         probe_level0->update(string);
582
583         sprintf(string, _("Level 1: %.2f"), channel1);
584         probe_level1->update(string);
585
586
587         if(do_overlay)
588         {
589                 draw_probe();
590                 canvas->flash();
591         }
592 }
593
594
595
596
597
598 void AudioScopeWindow::update_gui()
599 {
600         char string[BCTEXTLEN];
601         sprintf(string, "%d", plugin->config.window_size);
602         window_size->set_text(string);
603         history_size->update(plugin->config.history_size);
604 }
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630 AudioScope::AudioScope(PluginServer *server)
631  : PluginAClient(server)
632 {
633         reset();
634         timer = new Timer;
635         w = 640;
636         h = 480;
637 }
638
639 AudioScope::~AudioScope()
640 {
641         delete [] data;
642         for(int i = 0; i < CHANNELS; i++)
643                 delete audio_buffer[i];
644         delete timer;
645         frame_buffer.remove_all_objects();
646         frame_history.remove_all_objects();
647 }
648
649
650 void AudioScope::reset()
651 {
652         thread = 0;
653         data = 0;
654         for(int i = 0; i < CHANNELS; i++)
655                 audio_buffer[i] = 0;
656         allocated_data = 0;
657         total_windows = 0;
658         buffer_size = 0;
659         bzero(&header, sizeof(data_header_t));
660         current_frame = 0;
661 }
662
663
664 const char* AudioScope::plugin_title() { return N_("AudioScope"); }
665 int AudioScope::is_realtime() { return 1; }
666 int AudioScope::is_multichannel() { return 1; }
667
668 int AudioScope::process_buffer(int64_t size,
669                 Samples **buffer,
670                 int64_t start_position,
671                 int sample_rate)
672 {
673         int channels = MIN(get_total_buffers(), CHANNELS);
674
675 // Pass through
676         for(int i = 0; i < get_total_buffers(); i++)
677         {
678                 read_samples(buffer[i],
679                         i,
680                         sample_rate,
681                         start_position,
682                         size);
683         }
684
685         load_configuration();
686
687 // Reset audio buffer
688         if(window_size != config.window_size)
689         {
690                 render_stop();
691                 window_size = config.window_size;
692         }
693
694         if(!data)
695         {
696                 data = new unsigned char[sizeof(data_header_t)];
697                 allocated_data = 0;
698         }
699
700         if(!audio_buffer[0])
701         {
702                 for(int i = 0; i < CHANNELS; i++)
703                 {
704                         audio_buffer[i] = new Samples(MAX_WINDOW);
705                 }
706         }
707
708 // Accumulate audio
709         int needed = buffer_size + size;
710         if(audio_buffer[0]->get_allocated() < needed)
711         {
712                 for(int i = 0; i < CHANNELS; i++)
713                 {
714                         Samples *new_samples = new Samples(needed);
715                         memcpy(new_samples->get_data(),
716                                 audio_buffer[i]->get_data(),
717                                 sizeof(double) * buffer_size);
718                         delete audio_buffer[i];
719                         audio_buffer[i] = new_samples;
720                 }
721         }
722
723         for(int i = 0; i < CHANNELS; i++)
724         {
725                 memcpy(audio_buffer[i]->get_data() + buffer_size,
726                         buffer[MIN(i, channels - 1)]->get_data(),
727                         sizeof(double) * size);
728         }
729         buffer_size += size;
730
731
732 // Append a windows to end of GUI buffer
733         total_windows = 0;
734         while(buffer_size >= window_size)
735         {
736                 if(allocated_data < (total_windows + 1) * window_size * CHANNELS)
737                 {
738                         int new_allocation = (total_windows + 1) *
739                                 window_size *
740                                 CHANNELS;
741                         unsigned char *new_data = new unsigned char[sizeof(data_header_t) +
742                                 sizeof(float) * new_allocation];
743                         data_header_t *new_header = (data_header_t*)new_data;
744                         data_header_t *old_header = (data_header_t*)data;
745                         memcpy(new_header->samples,
746                                 old_header->samples,
747                                 sizeof(float) * allocated_data);
748                         delete [] data;
749                         data = new_data;
750                         allocated_data = new_allocation;
751                 }
752
753 // Search for trigger
754                 int need_trigger = 0;
755                 int got_trigger = 0;
756                 int trigger_sample = 0;
757 //printf("AudioScope::process_buffer %d\n", __LINE__);
758                 if(config.mode == WAVEFORM_RISING_TRIGGER)
759                 {
760                         need_trigger = 1;
761                         double *trigger_data = audio_buffer[0]->get_data();
762                         for(int i = 1; i < buffer_size - window_size; i++)
763                         {
764                                 if(trigger_data[i - 1] < config.trigger_level &&
765                                         trigger_data[i] >= config.trigger_level)
766                                 {
767                                         got_trigger = 1;
768                                         trigger_sample = i;
769                                         break;
770                                 }
771                         }
772                 }
773                 else
774                 if(config.mode == WAVEFORM_FALLING_TRIGGER)
775                 {
776                         need_trigger = 1;
777                         double *trigger_data = audio_buffer[0]->get_data();
778                         for(int i = 1; i < buffer_size - window_size; i++)
779                         {
780                                 if(trigger_data[i - 1] > config.trigger_level &&
781                                         trigger_data[i] <= config.trigger_level)
782                                 {
783                                         got_trigger = 1;
784                                         trigger_sample = i;
785                                         break;
786                                 }
787                         }
788                 }
789
790 //printf("AudioScope::process_buffer %d\n", __LINE__);
791 // Flush buffer
792                 if(need_trigger && !got_trigger)
793                 {
794 //printf("AudioScope::process_buffer %d\n", __LINE__);
795                         for(int j = 0; j < CHANNELS; j++)
796                         {
797                                 double *sample_input = audio_buffer[j]->get_data();
798                                 memcpy(sample_input,
799                                         sample_input + buffer_size - window_size,
800                                         sizeof(double) * window_size);
801                         }
802
803                         buffer_size = window_size;
804                         if(buffer_size == window_size) break;
805 //printf("AudioScope::process_buffer %d\n", __LINE__);
806                 }
807                 else
808                 {
809                         data_header_t *header = (data_header_t*)data;
810 //printf("AudioScope::process_buffer %d\n", __LINE__);
811                         for(int j = 0; j < CHANNELS; j++)
812                         {
813                                 float *sample_output = header->samples +
814                                         total_windows * CHANNELS * window_size +
815                                         window_size * j;
816                                 double *sample_input = audio_buffer[j]->get_data();
817                                 for(int i = 0; i < window_size; i++)
818                                 {
819                                         sample_output[i] = sample_input[i + trigger_sample];
820                                 }
821
822 // Shift accumulation buffer
823 //printf("AudioScope::process_buffer %d\n", __LINE__);
824                                 memcpy(sample_input,
825                                         sample_input + trigger_sample + window_size,
826                                         sizeof(double) * (buffer_size - trigger_sample - window_size));
827 //printf("AudioScope::process_buffer %d\n", __LINE__);
828                         }
829
830 //printf("AudioScope::process_buffer %d\n", __LINE__);
831
832                         total_windows++;
833                         buffer_size -= window_size + trigger_sample;
834 //printf("AudioScope::process_buffer %d\n", __LINE__);
835                 }
836         }
837
838         data_header_t *header = (data_header_t*)data;
839         header->window_size = window_size;
840         header->sample_rate = sample_rate;
841         header->channels = channels;
842         header->total_windows = total_windows;
843
844         send_render_gui(data,
845                 sizeof(data_header_t) +
846                         sizeof(float) * total_windows * window_size * CHANNELS);
847
848         return 0;
849 }
850
851 void AudioScope::render_stop()
852 {
853         buffer_size = 0;
854         frame_buffer.remove_all_objects();
855 }
856
857
858
859
860 NEW_WINDOW_MACRO(AudioScope, AudioScopeWindow)
861
862 void AudioScope::update_gui()
863 {
864         if(thread)
865         {
866                 int result = load_configuration();
867                 AudioScopeWindow *window = (AudioScopeWindow*)thread->get_window();
868                 window->lock_window("AudioScope::update_gui");
869                 if(result) window->update_gui();
870
871 // Shift in accumulated frames
872                 if(frame_buffer.size())
873                 {
874                         BC_SubWindow *canvas = window->canvas;
875 // Frames to draw in this iteration
876                         int total_frames = timer->get_difference() *
877                                 header.sample_rate /
878                                 header.window_size /
879                                 1000;
880                         if(total_frames) timer->subtract(total_frames *
881                                 header.window_size *
882                                 1000 /
883                                 header.sample_rate);
884
885 // Add forced frame drawing
886                         for(int i = 0; i < frame_buffer.size(); i++)
887                                 if(frame_buffer.get(i)->force) total_frames++;
888                         total_frames = MIN(frame_buffer.size(), total_frames);
889 //printf("AudioScope::update_gui %d %d\n", __LINE__, total_frames);
890
891
892
893 // Shift frames into history
894                         for(int frame = 0; frame < total_frames; frame++)
895                         {
896                                 if(frame_history.size() >= config.history_size)
897                                         frame_history.remove_object_number(0);
898
899                                 frame_history.append(frame_buffer.get(0));
900                                 frame_buffer.remove_number(0);
901                         }
902
903 // Reduce history size
904                         while(frame_history.size() > config.history_size)
905                                 frame_history.remove_object_number(0);
906
907 // Point probe data at history
908                         current_frame = frame_history.get(frame_history.size() - 1);
909
910 // Draw frames from history
911                         canvas->clear_box(0, 0, canvas->get_w(), canvas->get_h());
912                         for(int frame = 0; frame < frame_history.size(); frame++)
913                         {
914                                 AudioScopeFrame *ptr = frame_history.get(frame);
915
916                                 int luma = (frame + 1) * 0x80 / frame_history.size();
917                                 if(frame == frame_history.size() - 1)
918                                 {
919                                         canvas->set_color(WHITE);
920                                         canvas->set_line_width(2);
921                                 }
922                                 else
923                                 {
924                                         canvas->set_color((luma << 16) |
925                                                 (luma << 8) |
926                                                 luma);
927                                 }
928
929                                 int x1 = 0;
930                                 int y1 = 0;
931                                 int w = canvas->get_w();
932                                 int h = canvas->get_h();
933                                 float *channel0 = ptr->data[0];
934                                 float *channel1 = ptr->data[1];
935
936                                 if(config.mode == XY_MODE)
937                                 {
938
939                                         int number = 0;
940                                         for(int point = 0; point < ptr->size; point++)
941                                         {
942                                                 int x2 = (int)(channel0[point] * w / 2 + w / 2);
943                                                 int y2 = (int)(-channel1[point] * h / 2 + h / 2);
944                                                 CLAMP(x2, 0, w - 1);
945                                                 CLAMP(y2, 0, h - 1);
946
947                                                 if(number)
948                                                 {
949                                                         canvas->draw_line(x1, y1, x2, y2);
950                                                 }
951                                                 else
952                                                 {
953                                                         number++;
954                                                 }
955
956                                                 x1 = x2;
957                                                 y1 = y2;
958                                         }
959                                 }
960                                 else
961                                 {
962                                         for(int channel = ptr->channels - 1; channel >= 0; channel--)
963                                         {
964                                                 if(channel > 0)
965                                                 {
966                                                         if(frame == frame_history.size() - 1)
967                                                         {
968                                                                 canvas->set_color(PINK);
969                                                                 canvas->set_line_width(2);
970                                                         }
971                                                         else
972                                                                 canvas->set_color(((luma * 0xff / 0xff) << 16) |
973                                                                         ((luma * 0x80 / 0xff) << 8) |
974                                                                         ((luma * 0x80 / 0xff)));
975                                                 }
976                                                 else
977                                                 {
978                                                         if(frame == frame_history.size() - 1)
979                                                                 canvas->set_color(WHITE);
980                                                         else
981                                                                 canvas->set_color((luma << 16) |
982                                                                         (luma << 8) |
983                                                                         luma);
984                                                 }
985
986                                                 if(ptr->size < w)
987                                                 {
988                                                         for(int point = 0; point < ptr->size; point++)
989                                                         {
990                                                                 int x2 = point * w / ptr->size;
991                                                                 int y2 = (int)(-ptr->data[channel][point] * h / 2 + h / 2);
992                                                                 CLAMP(y2, 0, h - 1);
993                                                                 if(point > 0)
994                                                                 {
995                                                                         canvas->draw_line(x1, y1, x2, y2);
996                                                                 }
997
998                                                                 x1 = x2;
999                                                                 y1 = y2;
1000                                                         }
1001                                                 }
1002                                                 else
1003                                                 {
1004                                                         for(int x2 = 0; x2 < w; x2++)
1005                                                         {
1006                                                                 int sample1 = x2 * ptr->size / w;
1007                                                                 int sample2 = (x2 + 1) * ptr->size / w;
1008                                                                 double min = ptr->data[channel][sample1];
1009                                                                 double max = ptr->data[channel][sample1];
1010                                                                 for(int i = sample1 + 1; i < sample2; i++)
1011                                                                 {
1012                                                                         double value = ptr->data[channel][i];
1013                                                                         if(value < min) min = value;
1014                                                                         if(value > max) max = value;
1015                                                                 }
1016
1017                                                                 int min2 = (int)(-min * h / 2 + h / 2);
1018                                                                 int max2 = (int)(-max * h / 2 + h / 2);
1019                                                                 CLAMP(min2, 0, h - 1);
1020                                                                 CLAMP(max2, 0, h - 1);
1021                                                                 int y2 = (min2 + max2) / 2;
1022                                                                 canvas->draw_line(x2, min2, x2, max2);
1023                                                                 if(x2 > 0)
1024                                                                 {
1025                                                                         canvas->draw_line(x2, y1, x2, y2);
1026                                                                 }
1027
1028                                                                 y1 = y2;
1029                                                         }
1030                                                 }
1031                                         }
1032                                 }
1033
1034                                 canvas->set_line_width(1);
1035                         }
1036
1037 // Recompute probe level
1038                         window->calculate_probe(window->probe_x, window->probe_y, 0);
1039
1040
1041 // Draw overlay
1042                         window->draw_overlay();
1043                         canvas->flash();
1044                 }
1045
1046
1047
1048                 while(frame_buffer.size() > MAX_COLUMNS)
1049                         frame_buffer.remove_object_number(0);
1050
1051                 thread->get_window()->unlock_window();
1052         }
1053 }
1054
1055 void AudioScope::render_gui(void *data, int size)
1056 {
1057         if(thread)
1058         {
1059                 thread->get_window()->lock_window("AudioScope::render_gui");
1060                 data_header_t *header = (data_header_t*)data;
1061                 memcpy(&this->header, header, sizeof(data_header_t));
1062                 //BC_SubWindow *canvas = ((AudioScopeWindow*)thread->get_window())->canvas;
1063                 //int h = canvas->get_h();
1064
1065 // Set all previous frames to draw immediately
1066                 for(int i = 0; i < frame_buffer.size(); i++)
1067                         frame_buffer.get(i)->force = 1;
1068
1069                 for(int current_fragment = 0;
1070                         current_fragment < header->total_windows;
1071                         current_fragment++)
1072                 {
1073                         float *in_frame = header->samples +
1074                                 current_fragment * header->window_size * CHANNELS;
1075                         AudioScopeFrame *out_frame = new AudioScopeFrame(
1076                                 header->window_size,
1077                                 header->channels);
1078
1079 // Copy the window to the frame
1080                         for(int j = 0; j < CHANNELS; j++)
1081                         {
1082                                 for(int i = 0; i < header->window_size; i++)
1083                                 {
1084                                         out_frame->data[j][i] = in_frame[header->window_size * j + i];
1085                                 }
1086                         }
1087
1088                         frame_buffer.append(out_frame);
1089                         total_windows++;
1090                 }
1091
1092                 timer->update();
1093                 thread->get_window()->unlock_window();
1094         }
1095 }
1096
1097
1098
1099
1100
1101
1102
1103
1104 LOAD_CONFIGURATION_MACRO(AudioScope, AudioScopeConfig)
1105
1106 void AudioScope::read_data(KeyFrame *keyframe)
1107 {
1108         FileXML input;
1109         input.set_shared_input(keyframe->xbuf);
1110
1111         int result = 0;
1112         while(!result)
1113         {
1114                 result = input.read_tag();
1115
1116                 if(!result)
1117                 {
1118                         if(input.tag.title_is("AUDIOSCOPE"))
1119                         {
1120                                 if(is_defaults())
1121                                 {
1122                                         w = input.tag.get_property("W", w);
1123                                         h = input.tag.get_property("H", h);
1124                                 }
1125
1126                                 config.history_size = input.tag.get_property("HISTORY_SIZE", config.history_size);
1127                                 config.window_size = input.tag.get_property("WINDOW_SIZE", config.window_size);
1128                                 config.mode = input.tag.get_property("MODE", config.mode);
1129                                 config.trigger_level = input.tag.get_property("TRIGGER_LEVEL", config.trigger_level);
1130                         }
1131                 }
1132         }
1133 }
1134
1135 void AudioScope::save_data(KeyFrame *keyframe)
1136 {
1137         FileXML output;
1138         output.set_shared_output(keyframe->xbuf);
1139
1140 //printf("AudioScope::save_data %d %d %d\n", __LINE__, config.w, config.h);
1141         output.tag.set_title("AUDIOSCOPE");
1142         output.tag.set_property("W", w);
1143         output.tag.set_property("H", h);
1144         output.tag.set_property("HISTORY_SIZE", config.history_size);
1145         output.tag.set_property("WINDOW_SIZE", (int)config.window_size);
1146         output.tag.set_property("MODE", (int)config.mode);
1147         output.tag.set_property("TRIGGER_LEVEL", (float)config.trigger_level);
1148         output.append_tag();
1149         output.tag.set_title("/AUDIOSCOPE");
1150         output.append_tag();
1151         output.append_newline();
1152         output.terminate_string();
1153 }
1154
1155
1156
1157
1158