X-Git-Url: http://git.cinelerra-gg.org/git/?a=blobdiff_plain;f=cinelerra-5.1%2Fcinelerra%2Frecordengine.C;fp=cinelerra-5.1%2Fcinelerra%2Frecordengine.C;h=25b881191f648e5dac51bbfa8189ed055ebd5cb9;hb=30bdb85eb33a8ee7ba675038a86c6be59c43d7bd;hp=0000000000000000000000000000000000000000;hpb=52fcc46226f9df46f9ce9d0566dc568455a7db0b;p=goodguy%2Fhistory.git diff --git a/cinelerra-5.1/cinelerra/recordengine.C b/cinelerra-5.1/cinelerra/recordengine.C new file mode 100644 index 00000000..25b88119 --- /dev/null +++ b/cinelerra-5.1/cinelerra/recordengine.C @@ -0,0 +1,791 @@ + +/* + * CINELERRA + * Copyright (C) 2008 Adam Williams + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "assets.h" +#include "audioconfig.h" +#include "audiodevice.h" +#include "file.h" +#include "filexml.h" +#include "mwindow.h" +#include "patchbay.h" +#include "playbackengine.h" +#include "preferences.h" +#include "recconfirmdelete.h" +#include "record.h" +#include "recordengine.h" +#include "recordgui.h" +#include "recordlabel.h" +#include "recordpreview.h" +#include "recordthread.h" +#include "recordmonitor.h" +#include "units.h" +#include "videodevice.h" + +#include + +#include +#define _(String) gettext(String) +#define gettext_noop(String) String +#define N_(String) gettext_noop (String) + +RecordEngine::RecordEngine(MWindow *mwindow, Record *record) +{ + this->mwindow = mwindow; + this->record = record; +} + + + + + +RecordEngine::RecordEngine(MWindow *mwindow, + Record *record, + File *file, + Asset *asset, + RecordLabels *labels) +{ + this->mwindow = mwindow; + this->record = record; + this->file = file; + this->labels = labels; + this->asset = asset; + is_saving = 0; + is_previewing = 0; + is_duplexing = 0; + is_monitoring = 0; + prev_label = -1; + next_label = -1; + + if(record->do_audio) + adevice = new AudioDevice; + else + adevice = 0; + + if(record->do_video) + vdevice = new VideoDevice(mwindow); + else + vdevice = 0; +} + +RecordEngine::~RecordEngine() +{ + delete monitor_thread; + delete record_thread; + delete preview_thread; + if(adevice) delete adevice; + if(vdevice) delete vdevice; +} + +int RecordEngine::initialize() +{ +// monitor_thread = new RecordThread(mwindow, record, this); + monitor_thread->create_objects(); + +// record_thread = new RecordThread(mwindow, record, this); + record_thread->create_objects(); + + preview_thread = new RecordPreview(record, this); + preview_thread->initialize(); + +// put at end of file + total_length = -1; + if(record->do_audio) current_position = file->get_audio_length(); + else + if(record->do_video) current_position = Units::tosamples((float)(file->get_video_length(record->get_framerate())), record->get_samplerate(), record->get_framerate()); + + file->seek_end(); + + duplex_thread = mwindow->playback_engine; + +// initialize seek buttons + jump_delay[0] = 100; + jump_delay[1] = 50; + jump_delay[2] = 25; + jump_delay[3] = 10; + jump_delay[4] = 5; + + current_jump_jumps[0] = 20; + current_jump_jumps[1] = 40; + current_jump_jumps[2] = 60; + current_jump_jumps[3] = 80; + current_jump_jumps[4] = 100; +} + +int RecordEngine::run_script(FileXML *script) +{ + int result = 0, script_result = 0; + char string[1024]; + + while(!result && !script_result) + { + result = script->read_tag(); + + if(!result) + { + if(script->tag.title_is("set_mode")) + { + set_record_mode(script->tag.get_property_text(0)); + mode_to_text(string, get_record_mode()); + gui->rec_mode_menu->set_text(string); + } + else + if(script->tag.title_is("set_duration")) + { + record->set_loop_duration((long)record->get_samplerate() * script->tag.get_property_int(0)); + gui->update_duration_boxes(); + } + else + if(script->tag.title_is("start_recording")) + { + gui->unlock_window(); + start_saving(); + gui->lock_window(); + } + else + if(script->tag.title_is("set_monitor_video")) + { + set_monitor_video(script->tag.get_property_int(0)); + if(!script->tag.get_property_int(0) && record->video_window_open) + { + record->video_window_open = 0; + gui->monitor_video_window->window->hide_window(); + } + } + else + if(script->tag.title_is("set_monitor_audio")) + { + set_monitor_audio(script->tag.get_property_int(0)); + } + else + if(script->tag.title_is("quit_when_completed")) + { + record_thread->quit_when_completed = 1; + } + else + if(script->tag.title_is("ok")) + { + script_result = 1; + } + } + } + return script_result; +} + +// ============================================= accounting + +long RecordEngine::get_dc_offset(int offset) +{ + return record->dc_offset[offset]; +} + +int RecordEngine::set_dc_offset(long new_offset, int number) +{ + adevice->set_dc_offset(new_offset, number); +} + +long int RecordEngine::get_dc_offset(long *dc_offset, RecordGUIDCOffsetText **dc_offset_text) +{ + return adevice->get_dc_offset(dc_offset, dc_offset_text); +} + +int RecordEngine::set_gui(RecordGUI *gui) +{ + this->gui = gui; + update_position(current_position); +} + +int RecordEngine::get_duplex_enable() +{ + return record->enable_duplex(); +} + + + +// ================================================ operations + +int RecordEngine::open_input_devices(int duplex) +{ + int audio_opened = 0; + int video_opened = 0; + AudioConfig *aconfig /* = mwindow->preferences->aconfig */; + +// Initialize sharing + if(record->do_audio && record->do_video) + { + vdevice->set_adevice(adevice); + adevice->set_vdevice(vdevice); + } + +// Initialize audio + if(record->do_audio) + { + if(record->get_software_positioning()) + adevice->set_software_positioning(); + + for(int i = 0; i < asset->channels; i++) + { + adevice->set_dc_offset(record->dc_offset[i], i); + } + } + + +// Duplex is only needed if the timeline and the recording have audio + if(duplex && + record->do_audio && + mwindow->patches->total_playable_atracks()) + { +// duplex device is identical to input device + if(aconfig->audio_in_driver == aconfig->audio_duplex_driver && + !strcmp(aconfig->oss_in_device, aconfig->oss_duplex_device) && + aconfig->oss_in_bits == aconfig->oss_duplex_bits && + aconfig->oss_in_channels == aconfig->oss_duplex_channels) + { +// adevice->open_duplex(mwindow->preferences->aconfig, +// record->get_samplerate(), +// get_in_length()); + audio_opened = 1; + } + else +// two separate devices + { +// adevice->open_output(mwindow->preferences->aconfig, +// record->get_samplerate(), +// record->get_out_length()); + } + } + + if(record->do_audio && !audio_opened) + { +// adevice->open_input(mwindow->preferences->aconfig, +// record->get_samplerate(), +// get_in_length()); + } + +// Initialize video + if(record->do_video) + { +// vdevice->open_input(mwindow->preferences->vconfig, +// record->frame_w, +// record->frame_h, +// record->video_x, +// record->video_y, +// record->video_zoom, +// get_frame_rate()); +// vdevice->set_field_order(record->reverse_interlace); +// if(record->get_current_channel()) +// vdevice->set_channel(record->get_current_channel()); +// set_video_picture(); + } + + return 0; +} + + +int RecordEngine::close_input_devices() +{ + if(record->do_audio) + adevice->close_all(); + if(record->do_video) + vdevice->close_all(); + + return 0; +} + +int RecordEngine::start_monitor() +{ + monitor_timer.update(); + open_input_devices(0); + monitor_thread->start_recording(0, 0); + is_monitoring = 1; + return 0; +} + +int RecordEngine::stop_monitor() +{ +// if(is_monitoring) +// { +// is_monitoring = 0; +// monitor_thread->stop_recording(); +// } + return 0; +} + +int RecordEngine::pause_monitor() +{ + if(is_monitoring) + { + is_monitoring = 0; + monitor_thread->pause_recording(); + } + return 0; +} + +int RecordEngine::resume_monitor() +{ + if(!is_monitoring) + { + is_monitoring = 1; + monitor_timer.update(); + open_input_devices(0); + monitor_thread->resume_recording(); + } + return 0; +} + +int RecordEngine::start_saving(int duplex) +{ + if(!is_saving) + { + pause_monitor(); + record_timer.update(); + open_input_devices(duplex); + + duplex = record->enable_duplex() && duplex; + +// start the duplex engine if necessary +// OSS < 3.9 crashes if recording starts before playback +// OSS >= 3.9 crashes if playback starts before recording + if(duplex) + { + long start, end; + record->get_duplex_range(&start, &end); + duplex_thread->reset_parameters(); + duplex_thread->arm_playback(0, 0, 1, adevice); + duplex_thread->start_playback(); + is_duplexing = 1; + } + +// record_thread->start_recording(); + + is_saving = 1; + } + return 0; +} + +int RecordEngine::save_frame() +{ + if(!is_saving) + { + pause_monitor(); + record_timer.update(); + record->do_audio = 0; + open_input_devices(0); + +// start the duplex engine if necessary + record_thread->start_recording(0, 0); + is_saving = 1; + } + return 0; +} + +int RecordEngine::stop_saving(int no_monitor) +{ + if(is_saving) + { +// automatically stops duplex here and resumes monitor + record_thread->stop_recording(no_monitor); + } + return 0; +} + +int RecordEngine::stop_duplex() +{ + if(is_duplexing) + { + is_duplexing = 0; + duplex_thread->stop_playback(0); +// OSS can't resume recording if buffers underrun +// so stop playback first + } + return 0; +} + +int RecordEngine::start_preview() +{ + if(!is_previewing) + { + stop_operation(); + pause_monitor(); + + preview_timer.update(); + open_output_devices(); + preview_thread->start_preview(current_position, file); + + is_previewing = 1; + } + return 0; +} + +int RecordEngine::stop_preview(int no_monitor) +{ + if(is_previewing) + { + preview_thread->stop_preview(no_monitor); +// preview engine automatically triggers monitor when finished + } + return 0; +} + +int RecordEngine::stop_operation(int no_monitor) +{ +// Resumes monitoring after stopping + if(is_saving) stop_saving(no_monitor); + else + if(is_previewing) stop_preview(no_monitor); + return 0; +} + +int RecordEngine::set_video_picture() +{ + if(record->do_video && vdevice) + vdevice->set_picture(record->video_brightness, + record->video_hue, + record->video_color, + record->video_contrast, + record->video_whiteness); + return 0; +} + +int RecordEngine::open_output_devices() +{ + if(record->do_audio) + { +// adevice->open_output(mwindow->preferences->aconfig, +// record->get_samplerate(), +// record->get_out_length()); + if(record->get_software_positioning()) adevice->set_software_positioning(); + } + +// Video is already open for monitoring + return 0; +} + +int RecordEngine::close_output_devices() +{ + if(record->do_audio) + adevice->close_all(); +// Video is already open for monitoring + return 0; +} + + + +int RecordEngine::lock_window() +{ + gui->lock_window(); +} + +int RecordEngine::unlock_window() +{ + gui->unlock_window(); +} + +int RecordEngine::update_position(long new_position) +{ + if(new_position < 0) new_position = 0; // fread error in filepcm + current_position = new_position; + + gui->update_position(new_position); + + if(new_position > total_length) + { + total_length = new_position; +// gui->update_total_length(new_position); + } + + if(prev_label != labels->get_prev_label(new_position)) + { + prev_label = labels->get_prev_label(new_position); + gui->update_prev_label(prev_label); + } + + if(next_label != labels->get_next_label(new_position)) + { + next_label = labels->get_next_label(new_position); + + gui->update_next_label(next_label); + } +} + +int RecordEngine::goto_prev_label() +{ + if(!is_saving) + { + stop_operation(); + long new_position; + + new_position = labels->goto_prev_label(current_position); + if(new_position != -1) + { +// if(record->do_audio) file->set_audio_position(new_position); + if(record->do_video) file->set_video_position(Units::toframes(new_position, record->get_samplerate(), record->get_framerate()), record->get_framerate()); + update_position(new_position); + } + } +} + +int RecordEngine::goto_next_label() +{ + if(!is_saving) + { + stop_operation(); + long new_position; + + new_position = labels->goto_next_label(current_position); + if(new_position != -1 && new_position <= total_length) + { +// if(record->do_audio) file->set_audio_position(new_position); + if(record->do_video) file->set_video_position(Units::toframes(new_position, record->get_samplerate(), record->get_framerate()), record->get_framerate()); + update_position(new_position); + } + } + return 0; +} + +int RecordEngine::toggle_label() +{ + labels->toggle_label(current_position); + update_position(current_position); + return 0; +} + +int RecordEngine::calibrate_dc_offset() +{ + if(record->do_audio) + { + get_dc_offset(record->dc_offset, gui->dc_offset_text); + } + return 0; +} + +int RecordEngine::calibrate_dc_offset(long new_value, int channel) +{ + if(record->do_audio) + { + set_dc_offset(new_value, channel); + record->dc_offset[channel] = new_value; + } + return 0; +} + +int RecordEngine::reset_over() +{ +} + +int RecordEngine::set_done(int value) +{ + stop_operation(1); + stop_monitor(); + gui->set_done(value); +} + +int RecordEngine::start_over() +{ + if((record->do_audio && file->get_audio_length() > 0) || + (record->do_video && file->get_video_length(record->get_framerate()) > 0)) + { + RecConfirmDelete dialog(mwindow); + dialog.create_objects(_("start over")); + int result = dialog.run_window(); + if(!result) + { + stop_operation(); +// remove file + file->close_file(); + remove(asset->path); + + +// start the engine over + labels->delete_new_labels(); + update_position(0); + total_length = 0; +// gui->update_total_length(0); + + record->startsource_sample = 0; + record->startsource_frame = 0; + } + } +} + +int RecordEngine::change_channel(Channel *channel) +{ + if(record->do_video && vdevice) + return vdevice->set_channel(channel); + else + return 0; +} + +ArrayList* RecordEngine::get_video_inputs() +{ + if(record->do_video && vdevice) + return vdevice->get_inputs(); + else + return 0; +} + +int RecordEngine::get_vu_format() { return record->get_vu_format(); } +int RecordEngine::get_dither() { return record->default_asset->dither * record->default_asset->bits; } +int RecordEngine::get_input_channels() { return asset->channels; } +int RecordEngine::get_format(char *string) +{ + File file; + strcpy(string, file.formattostr(mwindow->plugindb, asset->format)); +} +int RecordEngine::get_samplerate() { return asset->rate; } +int RecordEngine::get_bits() { return asset->bits; } +int RecordEngine::get_time_format() { return record->get_time_format(); } +float RecordEngine::get_frame_rate() { return record->get_frame_rate(); } +int RecordEngine::get_loop_hr() { return record->loop_duration / asset->rate / 3600; } +int RecordEngine::get_loop_min() { return record->loop_duration / asset->rate / 60 - (long)get_loop_hr() * 60; } +int RecordEngine::get_loop_sec() { return record->loop_duration / asset->rate - (long)get_loop_hr() * 3600 - (long)get_loop_min() * 60; } +long RecordEngine::get_loop_duration() { return record->loop_duration; } +float RecordEngine::get_min_db() { return record->get_min_db(); } +int RecordEngine::get_meter_over_hold(int divisions) { return divisions * 15; } +int RecordEngine::get_meter_peak_hold(int divisions) { return divisions * 2; } +int RecordEngine::get_meter_speed() { return record->get_meter_speed(); } +float RecordEngine::get_frames_per_foot() { /* return mwindow->preferences->frames_per_foot; */ } + +int RecordEngine::set_monitor_video(int value) +{ +} + +int RecordEngine::set_monitor_audio(int value) +{ +} + +int RecordEngine::set_record_mode(char *text) +{ + record->record_mode = text_to_mode(text); +} + +int RecordEngine::get_record_mode(char *text) +{ + mode_to_text(text, record->record_mode); +} + +int RecordEngine::get_record_mode() +{ + return record->record_mode; +} + +int RecordEngine::mode_to_text(char *string, int mode) +{ + switch(mode) + { + case 0: sprintf(string, _("Untimed")); break; + case 1: sprintf(string, _("Timed")); break; + case 2: sprintf(string, _("Loop")); break; + } +} + +int RecordEngine::text_to_mode(char *string) +{ + if(!strcasecmp(string, _("Untimed"))) return 0; + if(!strcasecmp(string, _("Timed"))) return 1; + if(!strcasecmp(string, _("Loop"))) return 2; +} + +long RecordEngine::get_current_delay() +{ + if(current_jump_jump > 0) current_jump_jump--; + if(current_jump_jump == 0 && current_jump_delay < /*JUMP_DELAYS*/ 1) + { + current_jump_delay++; + current_jump_jump = current_jump_jumps[current_jump_delay]; + } + return jump_delay[current_jump_delay]; +} + +int RecordEngine::reset_current_delay() +{ + current_jump_delay = 0; + current_jump_jump = current_jump_jumps[current_jump_delay]; +} + +int RecordEngine::set_loop_duration() +{ + 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)); +} + + +// Remember to change meters if you change this. +// Return the size of the fragments to read from the audio device. +int RecordEngine::get_in_length() +{ + long fragment_size = 1; + while(fragment_size < asset->rate / record->get_meter_speed()) fragment_size *= 2; + fragment_size /= 2; + return fragment_size; +} + +// Different absolute positions are defined for each operation so threads +// can end at different times without screwing up the frame synchronization. +long RecordEngine::absolute_monitor_position() +{ + if(is_monitoring) + { + if(record->do_audio) + { +// return monitor_thread->absolute_position(); + } + else + { + return (long)((float)monitor_timer.get_difference() / 1000 * record->get_samplerate()); + } + } + else + return -1; +} + +long RecordEngine::absolute_preview_position() +{ + if(is_previewing) + { + if(record->do_audio) + { + return preview_thread->absolute_position(); + } + else + { + return (long)((float)preview_timer.get_difference() / 1000 * record->get_samplerate()); + } + } + else + return -1; +} + +long RecordEngine::absolute_record_position() +{ + if(is_saving) + { + if(record->do_audio) + { +// return record_thread->absolute_position(); + } + else + { + return (long)((float)record_timer.get_difference() / 1000 * record->get_samplerate()); + } + } + else + return -1; +} +