edit drag handle cursor per track, batchrender col width fix, batchrender path errmsg...
[goodguy/cinelerra.git] / cinelerra-5.1 / cinelerra / recordengine.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2008 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 "assets.h"
23 #include "audioconfig.h"
24 #include "audiodevice.h"
25 #include "file.h"
26 #include "filexml.h"
27 #include "mwindow.h"
28 #include "patchbay.h"
29 #include "playbackengine.h"
30 #include "preferences.h"
31 #include "recconfirmdelete.h"
32 #include "record.h"
33 #include "recordengine.h"
34 #include "recordgui.h"
35 #include "recordlabel.h"
36 #include "recordpreview.h"
37 #include "recordthread.h"
38 #include "recordmonitor.h"
39 #include "units.h"
40 #include "videodevice.h"
41
42 #include <ctype.h>
43
44 #include <libintl.h>
45 #define _(String) gettext(String)
46 #define gettext_noop(String) String
47 #define N_(String) gettext_noop (String)
48
49 RecordEngine::RecordEngine(MWindow *mwindow, Record *record)
50 {
51         this->mwindow = mwindow;
52         this->record = record;
53 }
54
55
56
57
58
59 RecordEngine::RecordEngine(MWindow *mwindow,
60                         Record *record,
61                         File *file,
62                         Asset *asset,
63                         RecordLabels *labels)
64 {
65         this->mwindow = mwindow;
66         this->record = record;
67         this->file = file;
68         this->labels = labels;
69         this->asset = asset;
70         is_saving = 0;
71         is_previewing = 0;
72         is_duplexing = 0;
73         is_monitoring = 0;
74         prev_label = -1;
75         next_label = -1;
76
77         if(record->do_audio)
78                 adevice = new AudioDevice;
79         else
80                 adevice = 0;
81
82         if(record->do_video)
83                 vdevice = new VideoDevice(mwindow);
84         else
85                 vdevice = 0;
86 }
87
88 RecordEngine::~RecordEngine()
89 {
90         delete monitor_thread;
91         delete record_thread;
92         delete preview_thread;
93         if(adevice) delete adevice;
94         if(vdevice) delete vdevice;
95 }
96
97 int RecordEngine::initialize()
98 {
99 //      monitor_thread = new RecordThread(mwindow, record, this);
100         monitor_thread->create_objects();
101
102 //      record_thread = new RecordThread(mwindow, record, this);
103         record_thread->create_objects();
104
105         preview_thread = new RecordPreview(record, this);
106         preview_thread->initialize();
107
108 // put at end of file
109         total_length = -1;
110         if(record->do_audio) current_position = file->get_audio_length();
111         else
112         if(record->do_video) current_position = Units::tosamples((float)(file->get_video_length(record->get_framerate())), record->get_samplerate(), record->get_framerate());
113
114         file->seek_end();
115
116         duplex_thread = mwindow->playback_engine;
117
118 // initialize seek buttons
119         jump_delay[0] = 100;
120         jump_delay[1] = 50;
121         jump_delay[2] = 25;
122         jump_delay[3] = 10;
123         jump_delay[4] = 5;
124
125         current_jump_jumps[0] = 20;
126         current_jump_jumps[1] = 40;
127         current_jump_jumps[2] = 60;
128         current_jump_jumps[3] = 80;
129         current_jump_jumps[4] = 100;
130 }
131
132 int RecordEngine::run_script(FileXML *script)
133 {
134         int result = 0, script_result = 0;
135         char string[1024];
136
137         while(!result && !script_result)
138         {
139                 result = script->read_tag();
140
141                 if(!result)
142                 {
143                         if(script->tag.title_is("set_mode"))
144                         {
145                                 set_record_mode(script->tag.get_property_text(0));
146                                 mode_to_text(string, get_record_mode());
147                                 gui->rec_mode_menu->set_text(string);
148                         }
149                         else
150                         if(script->tag.title_is("set_duration"))
151                         {
152                                 record->set_loop_duration((long)record->get_samplerate() * script->tag.get_property_int(0));
153                                 gui->update_duration_boxes();
154                         }
155                         else
156                         if(script->tag.title_is("start_recording"))
157                         {
158                                 gui->unlock_window();
159                                 start_saving();
160                                 gui->lock_window();
161                         }
162                         else
163                         if(script->tag.title_is("set_monitor_video"))
164                         {
165                                 set_monitor_video(script->tag.get_property_int(0));
166                                 if(!script->tag.get_property_int(0) && record->video_window_open)
167                                 {
168                                         record->video_window_open = 0;
169                                         gui->monitor_video_window->window->hide_window();
170                                 }
171                         }
172                         else
173                         if(script->tag.title_is("set_monitor_audio"))
174                         {
175                                 set_monitor_audio(script->tag.get_property_int(0));
176                         }
177                         else
178                         if(script->tag.title_is("quit_when_completed"))
179                         {
180                                 record_thread->quit_when_completed = 1;
181                         }
182                         else
183                         if(script->tag.title_is("ok"))
184                         {
185                                 script_result = 1;
186                         }
187                 }
188         }
189         return script_result;
190 }
191
192 // ============================================= accounting
193
194 long RecordEngine::get_dc_offset(int offset)
195 {
196         return record->dc_offset[offset];
197 }
198
199 int RecordEngine::set_dc_offset(long new_offset, int number)
200 {
201         adevice->set_dc_offset(new_offset, number);
202 }
203
204 long int RecordEngine::get_dc_offset(long *dc_offset, RecordGUIDCOffsetText **dc_offset_text)
205 {
206         return adevice->get_dc_offset(dc_offset, dc_offset_text);
207 }
208
209 int RecordEngine::set_gui(RecordGUI *gui)
210 {
211         this->gui = gui;
212         update_position(current_position);
213 }
214
215 int RecordEngine::get_duplex_enable()
216 {
217         return record->enable_duplex();
218 }
219
220
221
222 // ================================================ operations
223
224 int RecordEngine::open_input_devices(int duplex)
225 {
226         int audio_opened = 0;
227         int video_opened = 0;
228         AudioConfig *aconfig /* = mwindow->preferences->aconfig */;
229
230 // Initialize sharing
231         if(record->do_audio && record->do_video)
232         {
233                 vdevice->set_adevice(adevice);
234                 adevice->set_vdevice(vdevice);
235         }
236
237 // Initialize audio
238         if(record->do_audio)
239         {
240                 if(record->get_software_positioning())
241                         adevice->set_software_positioning();
242
243                 for(int i = 0; i < asset->channels; i++)
244                 {
245                         adevice->set_dc_offset(record->dc_offset[i], i);
246                 }
247         }
248
249
250 // Duplex is only needed if the timeline and the recording have audio
251         if(duplex &&
252                 record->do_audio &&
253                 mwindow->patches->total_playable_atracks())
254         {
255 // duplex device is identical to input device
256                 if(aconfig->audio_in_driver == aconfig->audio_duplex_driver &&
257                         !strcmp(aconfig->oss_in_device, aconfig->oss_duplex_device) &&
258                         aconfig->oss_in_bits == aconfig->oss_duplex_bits &&
259                         aconfig->oss_in_channels == aconfig->oss_duplex_channels)
260                 {
261 //                      adevice->open_duplex(mwindow->preferences->aconfig,
262 //                                              record->get_samplerate(),
263 //                                              get_in_length());
264                         audio_opened = 1;
265                 }
266                 else
267 // two separate devices
268                 {
269 //                      adevice->open_output(mwindow->preferences->aconfig,
270 //                                      record->get_samplerate(),
271 //                                      record->get_out_length());
272                 }
273         }
274
275         if(record->do_audio && !audio_opened)
276         {
277 //              adevice->open_input(mwindow->preferences->aconfig,
278 //                              record->get_samplerate(),
279 //                              get_in_length());
280         }
281
282 // Initialize video
283         if(record->do_video)
284         {
285 //              vdevice->open_input(mwindow->preferences->vconfig,
286 //                      record->frame_w,
287 //                      record->frame_h,
288 //                      record->video_x,
289 //                      record->video_y,
290 //                      record->video_zoom,
291 //                      get_frame_rate());
292 //              vdevice->set_field_order(record->reverse_interlace);
293 //              if(record->get_current_channel())
294 //                      vdevice->set_channel(record->get_current_channel());
295 //              set_video_picture();
296         }
297
298         return 0;
299 }
300
301
302 int RecordEngine::close_input_devices()
303 {
304         if(record->do_audio)
305                 adevice->close_all();
306         if(record->do_video)
307                 vdevice->close_all();
308
309         return 0;
310 }
311
312 int RecordEngine::start_monitor()
313 {
314         monitor_timer.update();
315         open_input_devices(0);
316         monitor_thread->start_recording(0, 0);
317         is_monitoring = 1;
318         return 0;
319 }
320
321 int RecordEngine::stop_monitor()
322 {
323 //      if(is_monitoring)
324 //      {
325 //              is_monitoring = 0;
326 //              monitor_thread->stop_recording();
327 //      }
328         return 0;
329 }
330
331 int RecordEngine::pause_monitor()
332 {
333         if(is_monitoring)
334         {
335                 is_monitoring = 0;
336                 monitor_thread->pause_recording();
337         }
338         return 0;
339 }
340
341 int RecordEngine::resume_monitor()
342 {
343         if(!is_monitoring)
344         {
345                 is_monitoring = 1;
346                 monitor_timer.update();
347                 open_input_devices(0);
348                 monitor_thread->resume_recording();
349         }
350         return 0;
351 }
352
353 int RecordEngine::start_saving(int duplex)
354 {
355         if(!is_saving)
356         {
357                 pause_monitor();
358                 record_timer.update();
359                 open_input_devices(duplex);
360
361                 duplex = record->enable_duplex() && duplex;
362
363 // start the duplex engine if necessary
364 // OSS < 3.9 crashes if recording starts before playback
365 // OSS >= 3.9 crashes if playback starts before recording
366                 if(duplex)
367                 {
368                         long start, end;
369                         record->get_duplex_range(&start, &end);
370                         duplex_thread->reset_parameters();
371                         duplex_thread->arm_playback(0, 0, 1, adevice);
372                         duplex_thread->start_playback();
373                         is_duplexing = 1;
374                 }
375
376 //              record_thread->start_recording();
377
378                 is_saving = 1;
379         }
380         return 0;
381 }
382
383 int RecordEngine::save_frame()
384 {
385         if(!is_saving)
386         {
387                 pause_monitor();
388                 record_timer.update();
389                 record->do_audio = 0;
390                 open_input_devices(0);
391
392 // start the duplex engine if necessary
393                 record_thread->start_recording(0, 0);
394                 is_saving = 1;
395         }
396         return 0;
397 }
398
399 int RecordEngine::stop_saving(int no_monitor)
400 {
401         if(is_saving)
402         {
403 // automatically stops duplex here and resumes monitor
404                 record_thread->stop_recording(no_monitor);
405         }
406         return 0;
407 }
408
409 int RecordEngine::stop_duplex()
410 {
411         if(is_duplexing)
412         {
413                 is_duplexing = 0;
414                 duplex_thread->stop_playback(0);
415 // OSS can't resume recording if buffers underrun
416 // so stop playback first
417         }
418         return 0;
419 }
420
421 int RecordEngine::start_preview()
422 {
423         if(!is_previewing)
424         {
425                 stop_operation();
426                 pause_monitor();
427
428                 preview_timer.update();
429                 open_output_devices();
430                 preview_thread->start_preview(current_position, file);
431
432                 is_previewing = 1;
433         }
434         return 0;
435 }
436
437 int RecordEngine::stop_preview(int no_monitor)
438 {
439         if(is_previewing)
440         {
441                 preview_thread->stop_preview(no_monitor);
442 // preview engine automatically triggers monitor when finished
443         }
444         return 0;
445 }
446
447 int RecordEngine::stop_operation(int no_monitor)
448 {
449 // Resumes monitoring after stopping
450         if(is_saving) stop_saving(no_monitor);
451         else
452         if(is_previewing) stop_preview(no_monitor);
453         return 0;
454 }
455
456 int RecordEngine::set_video_picture()
457 {
458         if(record->do_video && vdevice)
459                 vdevice->set_picture(record->video_brightness,
460                         record->video_hue,
461                         record->video_color,
462                         record->video_contrast,
463                         record->video_whiteness);
464         return 0;
465 }
466
467 int RecordEngine::open_output_devices()
468 {
469         if(record->do_audio)
470         {
471 //              adevice->open_output(mwindow->preferences->aconfig,
472 //                              record->get_samplerate(),
473 //                              record->get_out_length());
474                 if(record->get_software_positioning()) adevice->set_software_positioning();
475         }
476
477 // Video is already open for monitoring
478         return 0;
479 }
480
481 int RecordEngine::close_output_devices()
482 {
483         if(record->do_audio)
484                 adevice->close_all();
485 // Video is already open for monitoring
486         return 0;
487 }
488
489
490
491 int RecordEngine::lock_window()
492 {
493         gui->lock_window();
494 }
495
496 int RecordEngine::unlock_window()
497 {
498         gui->unlock_window();
499 }
500
501 int RecordEngine::update_position(long new_position)
502 {
503         if(new_position < 0) new_position = 0;      // fread error in filepcm
504         current_position = new_position;
505
506         gui->update_position(new_position);
507
508         if(new_position > total_length)
509         {
510                 total_length = new_position;
511 //              gui->update_total_length(new_position);
512         }
513
514         if(prev_label != labels->get_prev_label(new_position))
515         {
516                 prev_label = labels->get_prev_label(new_position);
517                 gui->update_prev_label(prev_label);
518         }
519
520         if(next_label != labels->get_next_label(new_position))
521         {
522                 next_label = labels->get_next_label(new_position);
523
524                 gui->update_next_label(next_label);
525         }
526 }
527
528 int RecordEngine::goto_prev_label()
529 {
530         if(!is_saving)
531         {
532                 stop_operation();
533                 long new_position;
534
535                 new_position = labels->goto_prev_label(current_position);
536                 if(new_position != -1)
537                 {
538 //                      if(record->do_audio) file->set_audio_position(new_position);
539                         if(record->do_video) file->set_video_position(Units::toframes(new_position, record->get_samplerate(), record->get_framerate()), record->get_framerate());
540                         update_position(new_position);
541                 }
542         }
543 }
544
545 int RecordEngine::goto_next_label()
546 {
547         if(!is_saving)
548         {
549                 stop_operation();
550                 long new_position;
551
552                 new_position = labels->goto_next_label(current_position);
553                 if(new_position != -1 && new_position <= total_length)
554                 {
555 //                      if(record->do_audio) file->set_audio_position(new_position);
556                         if(record->do_video) file->set_video_position(Units::toframes(new_position, record->get_samplerate(), record->get_framerate()), record->get_framerate());
557                         update_position(new_position);
558                 }
559         }
560         return 0;
561 }
562
563 int RecordEngine::toggle_label()
564 {
565         labels->toggle_label(current_position);
566         update_position(current_position);
567         return 0;
568 }
569
570 int RecordEngine::calibrate_dc_offset()
571 {
572         if(record->do_audio)
573         {
574                 get_dc_offset(record->dc_offset, gui->dc_offset_text);
575         }
576         return 0;
577 }
578
579 int RecordEngine::calibrate_dc_offset(long new_value, int channel)
580 {
581         if(record->do_audio)
582         {
583                 set_dc_offset(new_value, channel);
584                 record->dc_offset[channel] = new_value;
585         }
586         return 0;
587 }
588
589 int RecordEngine::reset_over()
590 {
591 }
592
593 int RecordEngine::set_done(int value)
594 {
595         stop_operation(1);
596         stop_monitor();
597         gui->set_done(value);
598 }
599
600 int RecordEngine::start_over()
601 {
602         if((record->do_audio && file->get_audio_length() > 0) ||
603                 (record->do_video && file->get_video_length(record->get_framerate()) > 0))
604         {
605                 RecConfirmDelete dialog(mwindow);
606                 dialog.create_objects(_("start over"));
607                 int result = dialog.run_window();
608                 if(!result)
609                 {
610                         stop_operation();
611 // remove file
612                         file->close_file();
613                         remove(asset->path);
614
615
616 // start the engine over
617                         labels->delete_new_labels();
618                         update_position(0);
619                         total_length = 0;
620 //                      gui->update_total_length(0);
621
622                         record->startsource_sample = 0;
623                         record->startsource_frame = 0;
624                 }
625         }
626 }
627
628 int RecordEngine::change_channel(Channel *channel)
629 {
630         if(record->do_video && vdevice)
631                 return vdevice->set_channel(channel);
632         else
633                 return 0;
634 }
635
636 ArrayList<char*>* RecordEngine::get_video_inputs()
637 {
638         if(record->do_video && vdevice)
639                 return vdevice->get_inputs();
640         else
641                 return 0;
642 }
643
644 int RecordEngine::get_vu_format() { return record->get_vu_format(); }
645 int RecordEngine::get_dither() { return record->default_asset->dither * record->default_asset->bits; }
646 int RecordEngine::get_input_channels() { return asset->channels; }
647 int RecordEngine::get_format(char *string)
648 {
649         File file;
650         strcpy(string, file.formattostr(mwindow->plugindb, asset->format));
651 }
652 int RecordEngine::get_samplerate() { return asset->rate; }
653 int RecordEngine::get_bits() { return asset->bits; }
654 int RecordEngine::get_time_format() { return record->get_time_format(); }
655 float RecordEngine::get_frame_rate() { return record->get_frame_rate(); }
656 int RecordEngine::get_loop_hr() { return record->loop_duration / asset->rate / 3600; }
657 int RecordEngine::get_loop_min() { return record->loop_duration / asset->rate / 60 - (long)get_loop_hr() * 60; }
658 int RecordEngine::get_loop_sec() { return record->loop_duration / asset->rate - (long)get_loop_hr() * 3600 - (long)get_loop_min() * 60; }
659 long RecordEngine::get_loop_duration() { return record->loop_duration; }
660 float RecordEngine::get_min_db() { return record->get_min_db(); }
661 int RecordEngine::get_meter_over_hold(int divisions) { return divisions * 15; }
662 int RecordEngine::get_meter_peak_hold(int divisions) { return divisions * 2; }
663 int RecordEngine::get_meter_speed() { return record->get_meter_speed(); }
664 float RecordEngine::get_frames_per_foot() { /* return mwindow->preferences->frames_per_foot; */ }
665
666 int RecordEngine::set_monitor_video(int value)
667 {
668 }
669
670 int RecordEngine::set_monitor_audio(int value)
671 {
672 }
673
674 int RecordEngine::set_record_mode(char *text)
675 {
676         record->record_mode = text_to_mode(text);
677 }
678
679 int RecordEngine::get_record_mode(char *text)
680 {
681         mode_to_text(text, record->record_mode);
682 }
683
684 int RecordEngine::get_record_mode()
685 {
686         return record->record_mode;
687 }
688
689 int RecordEngine::mode_to_text(char *string, int mode)
690 {
691         switch(mode)
692         {
693                 case 0:        sprintf(string, _("Untimed"));       break;
694                 case 1:        sprintf(string, _("Timed"));         break;
695                 case 2:        sprintf(string, _("Loop"));          break;
696         }
697 }
698
699 int RecordEngine::text_to_mode(char *string)
700 {
701         if(!strcasecmp(string, _("Untimed"))) return 0;
702         if(!strcasecmp(string, _("Timed")))   return 1;
703         if(!strcasecmp(string, _("Loop")))    return 2;
704 }
705
706 long RecordEngine::get_current_delay()
707 {
708         if(current_jump_jump > 0) current_jump_jump--;
709         if(current_jump_jump == 0 && current_jump_delay < /*JUMP_DELAYS*/ 1)
710         {
711                 current_jump_delay++;
712                 current_jump_jump = current_jump_jumps[current_jump_delay];
713         }
714         return jump_delay[current_jump_delay];
715 }
716
717 int RecordEngine::reset_current_delay()
718 {
719         current_jump_delay = 0;
720         current_jump_jump = current_jump_jumps[current_jump_delay];
721 }
722
723 int RecordEngine::set_loop_duration()
724 {
725         record->set_loop_duration((long)record->get_samplerate() * (atol(gui->loop_sec->get_text()) + atol(gui->loop_min->get_text()) * 60 + atol(gui->loop_hr->get_text()) * 3600));
726 }
727
728
729 // Remember to change meters if you change this.
730 // Return the size of the fragments to read from the audio device.
731 int RecordEngine::get_in_length()
732 {
733         long fragment_size = 1;
734         while(fragment_size < asset->rate / record->get_meter_speed()) fragment_size *= 2;
735         fragment_size /= 2;
736         return fragment_size;
737 }
738
739 // Different absolute positions are defined for each operation so threads
740 // can end at different times without screwing up the frame synchronization.
741 long RecordEngine::absolute_monitor_position()
742 {
743         if(is_monitoring)
744         {
745                 if(record->do_audio)
746                 {
747 //                      return monitor_thread->absolute_position();
748                 }
749                 else
750                 {
751                         return (long)((float)monitor_timer.get_difference() / 1000 * record->get_samplerate());
752                 }
753         }
754         else
755         return -1;
756 }
757
758 long RecordEngine::absolute_preview_position()
759 {
760         if(is_previewing)
761         {
762                 if(record->do_audio)
763                 {
764                         return preview_thread->absolute_position();
765                 }
766                 else
767                 {
768                         return (long)((float)preview_timer.get_difference() / 1000 * record->get_samplerate());
769                 }
770         }
771         else
772         return -1;
773 }
774
775 long RecordEngine::absolute_record_position()
776 {
777         if(is_saving)
778         {
779                 if(record->do_audio)
780                 {
781 //                      return record_thread->absolute_position();
782                 }
783                 else
784                 {
785                         return (long)((float)record_timer.get_difference() / 1000 * record->get_samplerate());
786                 }
787         }
788         else
789         return -1;
790 }
791