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