From 8bb2a5b24124dcb67c771ce351c82b9dcf0b3b47 Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Fri, 9 Nov 2012 13:28:54 +0000 Subject: [PATCH] merge 13363:13398 svn+ssh://ardoursvn@subversion.ardour.org/ardour2/branches/3.0, plus further work on SG (forgot to commit right after the merge, thus confusing things a bit git-svn-id: svn://localhost/ardour2/branches/3.0-SG@13409 d708f5d6-7413-0410-9779-e7cbd77b26cf --- gtk2_ardour/ardev_common.sh.in | 2 +- gtk2_ardour/ardour_ui.cc | 164 ++++++-------- gtk2_ardour/ardour_ui_dialogs.cc | 7 + gtk2_ardour/audio_clock.cc | 74 +++++-- gtk2_ardour/engine_dialog.cc | 54 ++++- gtk2_ardour/engine_dialog.h | 5 +- gtk2_ardour/global_port_matrix.cc | 12 + gtk2_ardour/rc_option_editor.cc | 14 ++ gtk2_ardour/rc_option_editor.h | 1 + gtk2_ardour/session_option_editor.cc | 2 +- gtk2_ardour/soundgrid.mm | 2 +- gtk2_ardour/startup.cc | 206 ++++++++++-------- gtk2_ardour/startup.h | 9 +- gtk2_ardour/step_entry.cc | 2 + libs/ardour/ardour/mtdm.h | 52 ++--- libs/ardour/ardour/rc_configuration_vars.h | 1 + libs/ardour/ardour/slave.h | 3 +- libs/ardour/audio_diskstream.cc | 2 +- libs/ardour/auditioner.cc | 8 +- libs/ardour/export_graph_builder.cc | 11 +- libs/ardour/ltc_slave.cc | 44 ++-- libs/ardour/mtc_slave.cc | 34 ++- libs/ardour/mtdm.cc | 189 ++++++++-------- libs/ardour/port_insert.cc | 2 +- libs/ardour/route.cc | 4 +- libs/ardour/session.cc | 5 + libs/ardour/session_ltc.cc | 104 ++++++--- libs/ardour/sg_rack.cc | 2 + libs/ardour/wscript | 13 ++ .../audiographer/general/chunker.h | 9 +- .../audiographer/sndfile/tmp_file.h | 24 +- .../tests/general/chunker_test.cc | 31 +++ libs/timecode/src/time.cc | 26 ++- libs/timecode/timecode/time.h | 2 + 34 files changed, 696 insertions(+), 424 deletions(-) diff --git a/gtk2_ardour/ardev_common.sh.in b/gtk2_ardour/ardev_common.sh.in index 210f282e76..ae7841f74b 100644 --- a/gtk2_ardour/ardev_common.sh.in +++ b/gtk2_ardour/ardev_common.sh.in @@ -14,7 +14,7 @@ libs=$TOP/@LIBS@ export ARDOUR_PATH=$TOP/gtk2_ardour/icons:$TOP/gtk2_ardour/pixmaps:$TOP/build/gtk2_ardour:$TOP/gtk2_ardour:. export ARDOUR_SURFACES_PATH=$libs/surfaces/osc:$libs/surfaces/generic_midi:$libs/surfaces/tranzport:$libs/surfaces/powermate:$libs/surfaces/mackie export ARDOUR_PANNER_PATH=$libs/panners/2in2out:$libs/panners/1in2out:$libs/panners/vbap -export ARDOUR_DATA_PATH=$TOP/gtk2_ardour:build/gtk2_ardour:. +export ARDOUR_DATA_PATH=$TOP/build:$TOP/gtk2_ardour:$TOP/build/gtk2_ardour:. export ARDOUR_MIDIMAPS_PATH=$TOP/midi_maps:. export ARDOUR_MCP_PATH=$TOP/mcp:. export ARDOUR_EXPORT_FORMATS_PATH=$TOP/export:. diff --git a/gtk2_ardour/ardour_ui.cc b/gtk2_ardour/ardour_ui.cc index 39762a55d5..af076a33cc 100644 --- a/gtk2_ardour/ardour_ui.cc +++ b/gtk2_ardour/ardour_ui.cc @@ -330,37 +330,6 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[]) DPIReset.connect (sigc::mem_fun (*this, &ARDOUR_UI::resize_text_widgets)); } -/** @return true if a session was chosen and `apply' clicked, otherwise false if `cancel' was clicked */ -bool -ARDOUR_UI::run_startup (bool should_be_new, std::string load_template) -{ - delete _startup; - _startup = new ArdourStartup (); - - XMLNode* audio_setup = Config->extra_xml ("AudioSetup"); - - if (audio_setup && _startup->engine_control()) { - _startup->engine_control()->set_state (*audio_setup); - } - - _startup->set_new_only (should_be_new); - if (!load_template.empty()) { - _startup->set_load_template (load_template); - } - _startup->present (); - - main().run(); - - _startup->hide (); - - switch (_startup->response()) { - case RESPONSE_OK: - return true; - default: - return false; - } -} - int ARDOUR_UI::create_engine () { @@ -2467,7 +2436,7 @@ ARDOUR_UI::get_session_parameters (bool quit_on_cancel, bool should_be_new, stri while (ret != 0) { - if (!should_be_new && !ARDOUR_COMMAND_LINE::session_name.empty()) { + if (!ARDOUR_COMMAND_LINE::session_name.empty()) { /* if they named a specific statefile, use it, otherwise they are just giving a session folder, and we want to use it as is @@ -2484,74 +2453,79 @@ ARDOUR_UI::get_session_parameters (bool quit_on_cancel, bool should_be_new, stri session_path = ARDOUR_COMMAND_LINE::session_name; session_name = Glib::path_get_basename (ARDOUR_COMMAND_LINE::session_name); } + } - } else { - - bool const apply = run_startup (should_be_new, load_template); - - if (!apply) { - if (quit_on_cancel) { - if (Profile->get_soundgrid()) { - SoundGrid::instance().teardown(); - } - exit (1); - } else { - return ret; - } - } - - /* if we run the startup dialog again, offer more than just "new session" */ - - should_be_new = false; - - session_name = _startup->session_name (likely_new); - - string::size_type suffix = session_name.find (statefile_suffix); - - if (suffix != string::npos) { - session_name = session_name.substr (0, suffix); - } - - /* this shouldn't happen, but we catch it just in case it does */ - - if (session_name.empty()) { - continue; - } - - if (_startup->use_session_template()) { - template_name = _startup->session_template_name(); - _session_is_new = true; - } - - if (session_name[0] == G_DIR_SEPARATOR || - (session_name.length() > 2 && session_name[0] == '.' && session_name[1] == G_DIR_SEPARATOR) || - (session_name.length() > 3 && session_name[0] == '.' && session_name[1] == '.' && session_name[2] == G_DIR_SEPARATOR)) { - - /* absolute path or cwd-relative path specified for session name: infer session folder - from what was given. - */ - - session_path = Glib::path_get_dirname (session_name); - session_name = Glib::path_get_basename (session_name); - + delete _startup; + _startup = new ArdourStartup (should_be_new, session_name, session_path, load_template); + + if (!_startup->ready_without_display()) { + _startup->present (); + main().run(); + _startup->hide (); + } + + switch (_startup->response()) { + case RESPONSE_OK: + break; + default: + if (quit_on_cancel) { + exit (1); } else { - - session_path = _startup->session_folder(); - - char illegal = Session::session_name_is_legal (session_name); - - if (illegal) { - MessageDialog msg (*_startup, - string_compose (_("To ensure compatibility with various systems\n" - "session names may not contain a '%1' character"), - illegal)); - msg.run (); - ARDOUR_COMMAND_LINE::session_name = ""; // cancel that - continue; - } + return ret; } } + /* if we run the startup dialog again, offer more than just "new session" */ + + should_be_new = false; + + session_name = _startup->session_name (likely_new); + + string::size_type suffix = session_name.find (statefile_suffix); + + if (suffix != string::npos) { + session_name = session_name.substr (0, suffix); + } + + /* this shouldn't happen, but we catch it just in case it does */ + + if (session_name.empty()) { + continue; + } + + if (_startup->use_session_template()) { + template_name = _startup->session_template_name(); + _session_is_new = true; + } + + if (session_name[0] == G_DIR_SEPARATOR || + (session_name.length() > 2 && session_name[0] == '.' && session_name[1] == G_DIR_SEPARATOR) || + (session_name.length() > 3 && session_name[0] == '.' && session_name[1] == '.' && session_name[2] == G_DIR_SEPARATOR)) { + + /* absolute path or cwd-relative path specified for session name: infer session folder + from what was given. + */ + + session_path = Glib::path_get_dirname (session_name); + session_name = Glib::path_get_basename (session_name); + + } else { + + session_path = _startup->session_folder(); + + char illegal = Session::session_name_is_legal (session_name); + + if (illegal) { + MessageDialog msg (*_startup, + string_compose (_("To ensure compatibility with various systems\n" + "session names may not contain a '%1' character"), + illegal)); + msg.run (); + ARDOUR_COMMAND_LINE::session_name = ""; // cancel that + continue; + } + } + if (create_engine ()) { break; } diff --git a/gtk2_ardour/ardour_ui_dialogs.cc b/gtk2_ardour/ardour_ui_dialogs.cc index 0116f4e842..02c72d7230 100644 --- a/gtk2_ardour/ardour_ui_dialogs.cc +++ b/gtk2_ardour/ardour_ui_dialogs.cc @@ -62,6 +62,13 @@ ARDOUR_UI::set_session (Session *s) { SessionHandlePtr::set_session (s); + for (ARDOUR::DataType::iterator i = ARDOUR::DataType::begin(); i != ARDOUR::DataType::end(); ++i) { + GlobalPortMatrixWindow* w; + if ((w = _global_port_matrix[*i]->get()) != 0) { + w->set_session (s); + } + } + if (!_session) { return; } diff --git a/gtk2_ardour/audio_clock.cc b/gtk2_ardour/audio_clock.cc index 5df46da5b4..5e3ff631f7 100644 --- a/gtk2_ardour/audio_clock.cc +++ b/gtk2_ardour/audio_clock.cc @@ -345,7 +345,25 @@ AudioClock::render (cairo_t* cr) cairo_fill (cr); } - cairo_move_to (cr, x_leading_padding + left_rect_width + separator_height, upper_height + separator_height + ((h - info_height)/2.0)); + + if (_right_layout->get_alignment() == Pango::ALIGN_RIGHT) { + /* right-align does not work per se beacuse layout width is unset. + * Using _right_layout->set_width([value >=0]) would also enable + * word-wrapping which is not wanted here. + * The solution is to custom align the layout depending on its size. + * if it is larger than the available space it will be cropped on the + * right edge rather than override text on the left side. + */ + int x, rw, rh; + _right_layout->get_pixel_size(rw, rh); + x = get_width() - rw- separator_height - x_leading_padding; + if (x < x_leading_padding + left_rect_width + separator_height) { + x = x_leading_padding + left_rect_width + separator_height; + } + cairo_move_to (cr, x, upper_height + separator_height + ((h - info_height)/2.0)); + } else { + cairo_move_to (cr, x_leading_padding + left_rect_width + separator_height, upper_height + separator_height + ((h - info_height)/2.0)); + } pango_cairo_show_layout (cr, _right_layout->gobj()); } else { @@ -947,9 +965,15 @@ AudioClock::set (framepos_t when, bool force, framecnt_t offset) } if (!editing) { + if (_right_layout) { + _right_layout->set_alignment(Pango::ALIGN_LEFT); + } switch (_mode) { case Timecode: + if (_right_layout) { + _right_layout->set_alignment(Pango::ALIGN_RIGHT); + } set_timecode (when, force); break; @@ -1017,7 +1041,7 @@ AudioClock::set_frames (framepos_t when, bool /*force*/) if (vid_pullup == 0.0) { _right_layout->set_text (_("pullup: \u2012")); } else { - sprintf (buf, _("pullup %-6.4f"), vid_pullup); + sprintf (buf, _("%+-6.4f%%"), vid_pullup); _right_layout->set_text (buf); } } @@ -1510,17 +1534,19 @@ AudioClock::on_button_press_event (GdkEventButton *ev) y = ev->y - ((upper_height - layout_height)/2); x = ev->x - layout_x_offset; - if (_layout->xy_to_index (x * PANGO_SCALE, y * PANGO_SCALE, index, trailing)) { - drag_field = index_to_field (index); - dragging = true; - /* make absolutely sure that the pointer is grabbed */ - gdk_pointer_grab(ev->window,false , - GdkEventMask( Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK |Gdk::BUTTON_RELEASE_MASK), - NULL,NULL,ev->time); - drag_accum = 0; - drag_start_y = ev->y; - drag_y = ev->y; + if (!_layout->xy_to_index (x * PANGO_SCALE, y * PANGO_SCALE, index, trailing)) { + /* pretend it is a character on the far right */ + index = 99; } + drag_field = index_to_field (index); + dragging = true; + /* make absolutely sure that the pointer is grabbed */ + gdk_pointer_grab(ev->window,false , + GdkEventMask( Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK |Gdk::BUTTON_RELEASE_MASK), + NULL,NULL,ev->time); + drag_accum = 0; + drag_start_y = ev->y; + drag_y = ev->y; } break; @@ -2026,6 +2052,20 @@ AudioClock::set_mode (Mode m) _layout->set_text (""); if (_left_layout) { + + if (_mode == Timecode) { + _left_layout->set_attributes (small_info_attributes); + _right_layout->set_attributes (small_info_attributes); + } else { + _left_layout->set_attributes (info_attributes); + _right_layout->set_attributes (info_attributes); + } + /* adjust info_height according to font size */ + int ignored; + _left_layout->set_text (" 1234567890"); + _left_layout->get_pixel_size (ignored, info_height); + info_height += 4; + _left_layout->set_text (""); _right_layout->set_text (""); } @@ -2074,16 +2114,6 @@ AudioClock::set_mode (Mode m) break; } - if (_left_layout) { - if (_mode == Timecode) { - _left_layout->set_attributes (small_info_attributes); - _right_layout->set_attributes (small_info_attributes); - } else { - _left_layout->set_attributes (info_attributes); - _right_layout->set_attributes (info_attributes); - } - } - set (last_when, true); if (!is_transient) { diff --git a/gtk2_ardour/engine_dialog.cc b/gtk2_ardour/engine_dialog.cc index 8ed8957c9f..c245ba8843 100644 --- a/gtk2_ardour/engine_dialog.cc +++ b/gtk2_ardour/engine_dialog.cc @@ -53,6 +53,8 @@ #include "gtkmm2ext/utils.h" #include "gtkmm2ext/gtk_ui.h" +#include "ardour/rc_configuration.h" + #include "pbd/convert.h" #include "pbd/error.h" #include "pbd/pathscanner.h" @@ -420,6 +422,14 @@ EngineControl::EngineControl () set_border_width (12); pack_start (notebook); + + /* Pick up any existing audio setup configuration, if appropriate */ + + XMLNode* audio_setup = ARDOUR::Config->extra_xml ("AudioSetup"); + + if (audio_setup) { + set_state (*audio_setup); + } } EngineControl::~EngineControl () @@ -646,13 +656,55 @@ EngineControl::build_command_line (vector& cmd) } } +bool +EngineControl::soundgrid_requested () +{ + bool ret = false; + +#ifdef HAVE_SOUNDGRID + /* we have to determine if we plan to use SoundGrid and if so, initialize it. + * + * this is really crude: we need to check for a .jackdrc file, read it and + * see if it appears to involve SoundGrid. + */ + + string path = Glib::build_filename (Glib::get_home_dir(), ".jackdrc"); + + ifstream jackdrc (path.c_str()); + + if (jackdrc) { + char buf[2048]; + string str; + + jackdrc.getline (buf, sizeof (buf)); + str = buf; + + if (str.find (SoundGrid::coreaudio_device_name()) != string::npos) { + /* SoundGrid is involved */ + ret = true; + } + jackdrc.close (); + } +#endif + + return ret; +} + +bool +EngineControl::need_setup () +{ + return soundgrid_requested() || !engine_running(); +} + bool EngineControl::engine_running () { EnvironmentalProtectionAgency* global_epa = EnvironmentalProtectionAgency::get_global_epa (); boost::scoped_ptr current_epa; - /* revert all environment settings back to whatever they were when ardour started + /* revert all environment settings back to whatever they were when + * ardour started, because ardour's startup script may have reset + * something in ways that interfere with finding/starting JACK. */ if (global_epa) { diff --git a/gtk2_ardour/engine_dialog.h b/gtk2_ardour/engine_dialog.h index ba431b3319..e56f0905f7 100644 --- a/gtk2_ardour/engine_dialog.h +++ b/gtk2_ardour/engine_dialog.h @@ -41,7 +41,7 @@ class EngineControl : public Gtk::VBox { EngineControl (); ~EngineControl (); - static bool engine_running (); + static bool need_setup (); int setup_engine (); int prepare (); @@ -161,6 +161,9 @@ class EngineControl : public Gtk::VBox { bool _used; + static bool engine_running (); + static bool soundgrid_requested (); + void driver_changed (); void interface_changed (); void build_command_line (std::vector&); diff --git a/gtk2_ardour/global_port_matrix.cc b/gtk2_ardour/global_port_matrix.cc index d749990463..fcd9960e43 100644 --- a/gtk2_ardour/global_port_matrix.cc +++ b/gtk2_ardour/global_port_matrix.cc @@ -42,6 +42,10 @@ GlobalPortMatrix::GlobalPortMatrix (Gtk::Window* p, Session* s, DataType t) void GlobalPortMatrix::setup_ports (int dim) { + if (!_session) { + return; + } + _ports[dim].suspend_signals (); _ports[dim].gather (_session, type(), dim == IN, false, show_only_bundles ()); _ports[dim].resume_signals (); @@ -50,6 +54,10 @@ GlobalPortMatrix::setup_ports (int dim) void GlobalPortMatrix::set_state (BundleChannel c[2], bool s) { + if (!_session) { + return; + } + Bundle::PortList const & in_ports = c[IN].bundle->channel_ports (c[IN].channel); Bundle::PortList const & out_ports = c[OUT].bundle->channel_ports (c[OUT].channel); @@ -178,6 +186,10 @@ void GlobalPortMatrixWindow::set_session (Session* s) { _port_matrix.set_session (s); + + if (!s) { + hide (); + } } string diff --git a/gtk2_ardour/rc_option_editor.cc b/gtk2_ardour/rc_option_editor.cc index 9d0d24acfa..16c893e645 100644 --- a/gtk2_ardour/rc_option_editor.cc +++ b/gtk2_ardour/rc_option_editor.cc @@ -1087,6 +1087,18 @@ RCOptionEditor::RCOptionEditor () add_option (_("Transport"), _sync_genlock); + _sync_source_2997 = new BoolOption ( + "timecode-source-2997", + _("Lock to 29.9700 fps instead of 30000/1001"), + sigc::mem_fun (*_rc_config, &RCConfiguration::get_timecode_source_2997), + sigc::mem_fun (*_rc_config, &RCConfiguration::set_timecode_source_2997) + ); + Gtkmm2ext::UI::instance()->set_tip + (_sync_genlock->tip_widget(), + _("When enabled the external timecode source is assumed to use 29.97 fps instead of 30000/1001")); + + add_option (_("Transport"), _sync_source_2997); + _ltc_port = new ComboStringOption ( "ltc-source-port", _("LTC incoming port"), @@ -1782,10 +1794,12 @@ RCOptionEditor::parameter_changed (string const & p) case ARDOUR::LTC: _sync_genlock->set_sensitive (true); _sync_framerate->set_sensitive (true); + _sync_source_2997->set_sensitive (true); break; default: _sync_genlock->set_sensitive (false); _sync_framerate->set_sensitive (false); + _sync_source_2997->set_sensitive (false); break; } #ifdef HAVE_LTC diff --git a/gtk2_ardour/rc_option_editor.h b/gtk2_ardour/rc_option_editor.h index 392865d2d8..7b9b29c3ff 100644 --- a/gtk2_ardour/rc_option_editor.h +++ b/gtk2_ardour/rc_option_editor.h @@ -46,6 +46,7 @@ private: ComboOption* _sync_source; BoolOption* _sync_framerate; BoolOption* _sync_genlock; + BoolOption* _sync_source_2997; ComboStringOption* _ltc_port; HSliderOption* _ltc_volume_slider; Gtk::Adjustment* _ltc_volume_adjustment; diff --git a/gtk2_ardour/session_option_editor.cc b/gtk2_ardour/session_option_editor.cc index b21e76c140..5059555fff 100644 --- a/gtk2_ardour/session_option_editor.cc +++ b/gtk2_ardour/session_option_editor.cc @@ -49,7 +49,7 @@ SessionOptionEditor::SessionOptionEditor (Session* s) smf->add (timecode_23976, _("23.976")); smf->add (timecode_24, _("24")); - smf->add (timecode_24976, _("24.976")); + smf->add (timecode_24976, _("24.975")); smf->add (timecode_25, _("25")); smf->add (timecode_2997, _("29.97")); smf->add (timecode_2997drop, _("29.97 drop")); diff --git a/gtk2_ardour/soundgrid.mm b/gtk2_ardour/soundgrid.mm index 38b393f8de..31d72364e3 100644 --- a/gtk2_ardour/soundgrid.mm +++ b/gtk2_ardour/soundgrid.mm @@ -132,7 +132,7 @@ soundgrid_initialize (uint32_t max_tracks, uint32_t max_busses, /* as of early August 2012, we need to wait 5 seconds before configuring the CoreAudio driver */ Glib::signal_timeout().connect (sigc::bind (sigc::ptr_fun (soundgrid_driver_init), - max_phys_inputs, max_phys_outputs, max_tracks), 5000); + max_phys_inputs, max_phys_outputs, max_tracks), 2500); /* tell everyone/everything that we're using soundgrid */ diff --git a/gtk2_ardour/startup.cc b/gtk2_ardour/startup.cc index 20ed00bc60..fd1d216028 100644 --- a/gtk2_ardour/startup.cc +++ b/gtk2_ardour/startup.cc @@ -63,97 +63,119 @@ static string poor_mans_glob (string path) } -ArdourStartup::ArdourStartup () +ArdourStartup::ArdourStartup (bool require_new, + const std::string& session_name, + const std::string& session_path, + const std::string& template_name) : _response (RESPONSE_OK) + , config_modified (false) + , new_only (require_new) + , default_dir_chooser (0) , ic_new_session_button (_("Create a new session")) , ic_existing_session_button (_("Open an existing session")) - , monitor_via_hardware_button (_("Use an external mixer or the hardware mixer of your audio interface.\n\ -Ardour will play NO role in monitoring")) + , monitor_via_hardware_button (_("Use an external mixer or the hardware mixer of your audio interface.\n" + "Ardour will play NO role in monitoring")) , monitor_via_ardour_button (string_compose (_("Ask %1 to play back material as it is being recorded"), PROGRAM_NAME)) + , engine_dialog (0) , new_folder_chooser (FILE_CHOOSER_ACTION_SELECT_FOLDER) , more_new_session_options_button (_("I'd like more options for this session")) , _output_limit_count_adj (1, 0, 100, 1, 10, 0) , _input_limit_count_adj (1, 0, 100, 1, 10, 0) , _master_bus_channel_count_adj (2, 0, 100, 1, 10, 0) + , audio_page_index (-1) + , new_user_page_index (-1) + , default_folder_page_index (-1) + , monitoring_page_index (-1) + , session_page_index (-1) + , initial_choice_index (-1) + , final_page_index (-1) + , session_options_page_index (-1) , _existing_session_chooser_used (false) { - audio_page_index = -1; - initial_choice_index = -1; - new_user_page_index = -1; - default_folder_page_index = -1; - monitoring_page_index = -1; - session_page_index = -1; - final_page_index = -1; - session_options_page_index = -1; - new_only = false; + new_user = !Glib::file_test (been_here_before_path(), Glib::FILE_TEST_EXISTS); + need_audio_setup = EngineControl::need_setup (); + need_session_info = (session_name.empty() || require_new); - engine_dialog = 0; - config_modified = false; - default_dir_chooser = 0; + _provided_session_name = session_name; + _provided_session_path = session_path; + + if (need_audio_setup || need_session_info || new_user) { - use_template_button.set_group (session_template_group); - use_session_as_template_button.set_group (session_template_group); - - set_keep_above (true); - set_position (WIN_POS_CENTER); - set_border_width (12); - - if ((icon_pixbuf = ::get_icon ("ardour_icon_48px")) == 0) { - throw failed_constructor(); - } - - list > window_icons; - Glib::RefPtr icon; - - if ((icon = ::get_icon ("ardour_icon_16px")) != 0) { - window_icons.push_back (icon); - } - if ((icon = ::get_icon ("ardour_icon_22px")) != 0) { - window_icons.push_back (icon); - } - if ((icon = ::get_icon ("ardour_icon_32px")) != 0) { - window_icons.push_back (icon); - } - if ((icon = ::get_icon ("ardour_icon_48px")) != 0) { - window_icons.push_back (icon); - } - if (!window_icons.empty ()) { - set_default_icon_list (window_icons); - } - - new_user = !Glib::file_test(been_here_before_path(), Glib::FILE_TEST_EXISTS); - - bool need_audio_setup = !EngineControl::engine_running(); - - // setup_prerelease_page (); - - if (new_user) { + use_template_button.set_group (session_template_group); + use_session_as_template_button.set_group (session_template_group); - setup_new_user_page (); - setup_first_time_config_page (); - setup_monitoring_choice_page (); - setup_monitor_section_choice_page (); - - if (need_audio_setup) { - setup_audio_page (); + set_keep_above (true); + set_position (WIN_POS_CENTER); + set_border_width (12); + + if ((icon_pixbuf = ::get_icon ("ardour_icon_48px")) == 0) { + throw failed_constructor(); + } + + list > window_icons; + Glib::RefPtr icon; + + if ((icon = ::get_icon ("ardour_icon_16px")) != 0) { + window_icons.push_back (icon); + } + if ((icon = ::get_icon ("ardour_icon_22px")) != 0) { + window_icons.push_back (icon); + } + if ((icon = ::get_icon ("ardour_icon_32px")) != 0) { + window_icons.push_back (icon); + } + if ((icon = ::get_icon ("ardour_icon_48px")) != 0) { + window_icons.push_back (icon); + } + if (!window_icons.empty ()) { + set_default_icon_list (window_icons); + } + + // setup_prerelease_page (); + + if (new_user) { + + setup_new_user_page (); + setup_first_time_config_page (); + setup_monitoring_choice_page (); + setup_monitor_section_choice_page (); + + if (need_audio_setup) { + setup_audio_page (); + } + + ic_new_session_button.set_active (true); // always create new session on first run + + } else { + + if (need_audio_setup) { + setup_audio_page (); + } + + if (need_session_info) { + setup_initial_choice_page (); + } } - ic_new_session_button.set_active (true); // always create new session on first run + if (need_session_info) { + setup_session_page (); + setup_more_options_page (); + } - } else { - - if (need_audio_setup) { - setup_audio_page (); + if (new_user) { + setup_final_page (); } - setup_initial_choice_page (); - } + if (new_only) { + ic_vbox.hide (); + } else { + ic_vbox.show (); + } - setup_session_page (); - setup_more_options_page (); - - if (new_user) { - setup_final_page (); + if (!template_name.empty()) { + use_template_button.set_active (false); + load_template_override = template_name; + } } the_startup = this; @@ -163,6 +185,12 @@ ArdourStartup::~ArdourStartup () { } +bool +ArdourStartup::ready_without_display () const +{ + return !new_user && !need_audio_setup && !need_session_info; +} + void ArdourStartup::setup_prerelease_page () { @@ -198,25 +226,6 @@ Full information on all the above can be found on the support page at\n\ set_page_complete (*vbox, true); } -void -ArdourStartup::set_new_only (bool yn) -{ - new_only = yn; - - if (new_only) { - ic_vbox.hide (); - } else { - ic_vbox.show (); - } -} - -void -ArdourStartup::set_load_template (string load_template) -{ - use_template_button.set_active (false); - load_template_override = load_template; -} - bool ArdourStartup::use_session_template () { @@ -235,7 +244,7 @@ std::string ArdourStartup::session_template_name () { if (!load_template_override.empty()) { - string the_path(ARDOUR::user_template_directory()); + string the_path (ARDOUR::user_template_directory()); return Glib::build_filename (the_path, load_template_override + ARDOUR::template_suffix); } @@ -257,6 +266,10 @@ ArdourStartup::session_template_name () std::string ArdourStartup::session_name (bool& should_be_new) { + if (!need_session_info) { + return _provided_session_name; + } + if (ic_new_session_button.get_active()) { should_be_new = true; string val = new_name_entry.get_text (); @@ -283,6 +296,10 @@ ArdourStartup::session_name (bool& should_be_new) std::string ArdourStartup::session_folder () { + if (!need_session_info) { + return _provided_session_path; + } + if (ic_new_session_button.get_active()) { std::string legal_session_folder_name = legalize_for_path (new_name_entry.get_text()); return Glib::build_filename (new_folder_chooser.get_current_folder(), legal_session_folder_name); @@ -310,7 +327,7 @@ ArdourStartup::setup_audio_page () engine_dialog->show_all (); audio_page_index = append_page (*engine_dialog); - set_page_type (*engine_dialog, ASSISTANT_PAGE_CONTENT); + set_page_type (*engine_dialog, (need_session_info ? ASSISTANT_PAGE_CONTENT : ASSISTANT_PAGE_CONFIRM)); set_page_title (*engine_dialog, _("Audio / MIDI Setup")); /* the default parameters should work, so the page is potentially complete */ @@ -622,6 +639,11 @@ void ArdourStartup::on_apply () { if (engine_dialog) { + if (engine_dialog->prepare ()) { + /* failure - do not proceed to new page */ + set_current_page (audio_page_index); + return; + } if (engine_dialog->setup_engine ()) { set_current_page (audio_page_index); return; diff --git a/gtk2_ardour/startup.h b/gtk2_ardour/startup.h index c2de96c55f..00c0a90d39 100644 --- a/gtk2_ardour/startup.h +++ b/gtk2_ardour/startup.h @@ -45,11 +45,10 @@ class EngineControl; class ArdourStartup : public Gtk::Assistant { public: - ArdourStartup (); + ArdourStartup (bool require_new, const std::string& session_name, const std::string& session_path, const std::string& template_name); ~ArdourStartup (); - void set_new_only (bool); - void set_load_template( std::string load_template ); + bool ready_without_display () const; std::string session_name (bool& should_be_new); std::string session_folder (); @@ -83,7 +82,11 @@ class ArdourStartup : public Gtk::Assistant { gint _response; bool config_modified; bool new_user; + bool need_audio_setup; + bool need_session_info; bool new_only; + std::string _provided_session_name; + std::string _provided_session_path; std::string been_here_before_path () const; diff --git a/gtk2_ardour/step_entry.cc b/gtk2_ardour/step_entry.cc index fd53fd55ff..7769b5e054 100644 --- a/gtk2_ardour/step_entry.cc +++ b/gtk2_ardour/step_entry.cc @@ -470,6 +470,8 @@ StepEntry::StepEntry (StepEditor& seditor) r = RefPtr::cast_dynamic (act); assert (r); r->set_active (true); + + set_type_hint(Gdk::WINDOW_TYPE_HINT_DIALOG); } StepEntry::~StepEntry() diff --git a/libs/ardour/ardour/mtdm.h b/libs/ardour/ardour/mtdm.h index 59b504b821..b46e53c0a2 100644 --- a/libs/ardour/ardour/mtdm.h +++ b/libs/ardour/ardour/mtdm.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2003-2008 Fons Adriaensen + Copyright (C) 2003-2012 Fons Adriaensen 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 @@ -21,35 +21,37 @@ #include -class MTDM { +class MTDM +{ public: - MTDM (); - - int process (size_t len, float *inp, float *out); - int resolve (); - void invert () { _inv ^= 1; } - int inv () { return _inv; } - double del () { return _del; } - double err () { return _err; } + MTDM (int fsamp); + int process (size_t len, float *inp, float *out); + int resolve (void); + void invert (void) { _inv ^= 1; } + int inv (void) { return _inv; } + double del (void) { return _del; } + double err (void) { return _err; } private: - class Freq { - public: - int p; - int f; - float a; - float xa; - float ya; - float xf; - float yf; - }; + class Freq { + public: + int p; + int f; + float xa; + float ya; + float x1; + float y1; + float x2; + float y2; + }; - double _del; - double _err; - int _cnt; - int _inv; - Freq _freq [5]; + double _del; + double _err; + float _wlp; + int _cnt; + int _inv; + Freq _freq [13]; }; #endif /* __libardour_mtdm_h__ */ diff --git a/libs/ardour/ardour/rc_configuration_vars.h b/libs/ardour/ardour/rc_configuration_vars.h index 4916eeece4..35c90e61a6 100644 --- a/libs/ardour/ardour/rc_configuration_vars.h +++ b/libs/ardour/ardour/rc_configuration_vars.h @@ -50,6 +50,7 @@ CONFIG_VARIABLE (bool, first_midi_bank_is_zero, "diplay-first-midi-bank-as-zero" CONFIG_VARIABLE (int, mtc_qf_speed_tolerance, "mtc-qf-speed-tolerance", 5) CONFIG_VARIABLE (bool, timecode_sync_frame_rate, "timecode-sync-frame-rate", true) CONFIG_VARIABLE (bool, timecode_source_is_synced, "timecode-source-is-synced", true) +CONFIG_VARIABLE (bool, timecode_source_2997, "timecode-source-2997", false) CONFIG_VARIABLE (SyncSource, sync_source, "sync-source", JACK) CONFIG_VARIABLE (std::string, ltc_source_port, "ltc-source-port", "system:capture_1") CONFIG_VARIABLE (bool, send_ltc, "send-ltc", false) diff --git a/libs/ardour/ardour/slave.h b/libs/ardour/ardour/slave.h index f864bee636..de58e07e36 100644 --- a/libs/ardour/ardour/slave.h +++ b/libs/ardour/ardour/slave.h @@ -261,7 +261,7 @@ class MTC_Slave : public TimecodeSlave { void handle_locate (const MIDI::byte*); framecnt_t resolution () const; - bool requires_seekahead () const { return true; } + bool requires_seekahead () const { return false; } framecnt_t seekahead_distance() const; bool give_slave_full_control_over_transport_speed() const; @@ -356,6 +356,7 @@ public: bool detect_ltc_fps(int, bool); bool equal_ltc_frame_time(LTCFrame *a, LTCFrame *b); void reset(); + void resync_xrun(); void resync_latency(); Session& session; diff --git a/libs/ardour/audio_diskstream.cc b/libs/ardour/audio_diskstream.cc index 3345a00b42..2d2d3d252f 100644 --- a/libs/ardour/audio_diskstream.cc +++ b/libs/ardour/audio_diskstream.cc @@ -578,7 +578,7 @@ AudioDiskstream::process (framepos_t transport_frame, pframes_t nframes, framecn /* no varispeed playback if we're recording, because the output .... TBD */ if (rec_nframes == 0 && _actual_speed != 1.0f) { - necessary_samples = (framecnt_t) floor ((nframes * fabs (_actual_speed))) + 1; + necessary_samples = (framecnt_t) ceil ((nframes * fabs (_actual_speed))) + 2; } else { necessary_samples = nframes; } diff --git a/libs/ardour/auditioner.cc b/libs/ardour/auditioner.cc index ad4d8b5d16..4620bca306 100644 --- a/libs/ardour/auditioner.cc +++ b/libs/ardour/auditioner.cc @@ -55,17 +55,20 @@ Auditioner::init () return -1; } - string left = _session.config.get_auditioner_output_left(); - string right = _session.config.get_auditioner_output_right(); + string left = Config->get_auditioner_output_left(); + string right = Config->get_auditioner_output_right(); vector outputs; _session.engine().get_physical_outputs (DataType::AUDIO, outputs); + cerr << "we have " << outputs.size() << " for auditioning, L/R = " << left << " + " << right << endl; + if (left == "default") { if (_session.monitor_out()) { left = _session.monitor_out()->input()->audio (0)->name(); via_monitor = true; } else { + cerr << "here!\n"; if (outputs.size() > 0) { left = outputs[0]; } @@ -77,6 +80,7 @@ Auditioner::init () right = _session.monitor_out()->input()->audio (1)->name(); via_monitor = true; } else { + cerr << "there!\n"; if (outputs.size() > 1) { right = outputs[1]; } diff --git a/libs/ardour/export_graph_builder.cc b/libs/ardour/export_graph_builder.cc index 2be9044415..fd39aa6cd6 100644 --- a/libs/ardour/export_graph_builder.cc +++ b/libs/ardour/export_graph_builder.cc @@ -1,5 +1,7 @@ #include "ardour/export_graph_builder.h" +#include + #include "audiographer/process_context.h" #include "audiographer/general/chunker.h" #include "audiographer/general/interleaver.h" @@ -17,6 +19,7 @@ #include "ardour/export_filename.h" #include "ardour/export_format_specification.h" #include "ardour/export_timespan.h" +#include "ardour/session_directory.h" #include "ardour/sndfile_helpers.h" #include "pbd/file_utils.h" @@ -292,6 +295,12 @@ ExportGraphBuilder::SFC::operator== (FileSpec const & other_config) const ExportGraphBuilder::Normalizer::Normalizer (ExportGraphBuilder & parent, FileSpec const & new_config, framecnt_t /*max_frames*/) : parent (parent) { + std::string tmpfile_path = parent.session.session_directory().export_path(); + tmpfile_path = Glib::build_filename(tmpfile_path, "XXXXXX"); + char tmpfile_path_buf[tmpfile_path.size() + 1]; + std::copy(tmpfile_path.begin(), tmpfile_path.end(), tmpfile_path_buf); + tmpfile_path_buf[tmpfile_path.size()] = '\0'; + config = new_config; uint32_t const channels = config.channel_config->get_n_chans(); max_frames_out = 4086 - (4086 % channels); // TODO good chunk size @@ -305,7 +314,7 @@ ExportGraphBuilder::Normalizer::Normalizer (ExportGraphBuilder & parent, FileSpe normalizer->add_output (threader); int format = ExportFormatBase::F_RAW | ExportFormatBase::SF_Float; - tmp_file.reset (new TmpFile (format, channels, config.format->sample_rate())); + tmp_file.reset (new TmpFile (tmpfile_path_buf, format, channels, config.format->sample_rate())); tmp_file->FileWritten.connect_same_thread (post_processing_connection, boost::bind (&Normalizer::start_post_processing, this)); diff --git a/libs/ardour/ltc_slave.cc b/libs/ardour/ltc_slave.cc index 4f6f77ce1f..28c6331628 100644 --- a/libs/ardour/ltc_slave.cc +++ b/libs/ardour/ltc_slave.cc @@ -61,7 +61,8 @@ LTC_Slave::LTC_Slave (Session& s) decoder = ltc_decoder_create((int) frames_per_ltc_frame, 128 /*queue size*/); reset(); - session.Xrun.connect_same_thread (port_connections, boost::bind (<C_Slave::resync_latency, this)); + resync_latency(); + session.Xrun.connect_same_thread (port_connections, boost::bind (<C_Slave::resync_xrun, this)); session.engine().GraphReordered.connect_same_thread (port_connections, boost::bind (<C_Slave::resync_latency, this)); } @@ -79,7 +80,7 @@ LTC_Slave::~LTC_Slave() ARDOUR::framecnt_t LTC_Slave::resolution () const { - return (framecnt_t) (frames_per_ltc_frame); + return (framecnt_t) (session.frame_rate() / 1000); } bool @@ -94,13 +95,20 @@ LTC_Slave::ok() const return true; } +void +LTC_Slave::resync_xrun() +{ + DEBUG_TRACE (DEBUG::LTC, "LTC resync_xrun()\n"); + engine_dll_initstate = 0; +} + void LTC_Slave::resync_latency() { - DEBUG_TRACE (DEBUG::LTC, "LTC resync()\n"); + DEBUG_TRACE (DEBUG::LTC, "LTC resync_latency()\n"); engine_dll_initstate = 0; - if (session.ltc_output_io()) { /* check if Port exits */ + if (!session.deletion_in_progress() && session.ltc_output_io()) { /* check if Port exits */ boost::shared_ptr ltcport = session.ltc_input_port(); ltcport->get_connected_latency_range(ltc_slave_latency, false); } @@ -115,7 +123,6 @@ LTC_Slave::reset() transport_direction = 0; ltc_speed = 0; engine_dll_initstate = 0; - resync_latency(); } void @@ -224,6 +231,15 @@ LTC_Slave::detect_ltc_fps(int frameno, bool df) /* poll and check session TC */ TimecodeFormat tc_format = apparent_timecode_format(); TimecodeFormat cur_timecode = session.config.get_timecode_format(); + + if (Config->get_timecode_source_2997() && tc_format == Timecode::timecode_2997drop) { + tc_format = Timecode::timecode_2997000drop; + } + else + if (Config->get_timecode_source_2997() && tc_format == Timecode::timecode_2997) { + tc_format = Timecode::timecode_2997000; + } + if (Config->get_timecode_sync_frame_rate()) { /* enforce time-code */ if (!did_reset_tc_format) { @@ -371,16 +387,6 @@ LTC_Slave::speed_and_position (double& speed, framepos_t& pos) boost::shared_ptr ltcport = session.ltc_input_port(); -#if 1 - /* TODO read layency only on demand -> ::reset() - * this is already prepared. - * - * ..but first fix jack2 issue with re-computing latency - * in the correct order. Until then, querying it in the - * process-callback is the only way to get the current value - */ - ltcport->get_connected_latency_range(ltc_slave_latency, false); -#endif in = (jack_default_audio_sample_t*) jack_port_get_buffer (ltcport->jack_port(), nframes); frameoffset_t skip = now - (monotonic_cnt + nframes); @@ -416,7 +422,7 @@ LTC_Slave::speed_and_position (double& speed, framepos_t& pos) reset(); } - parse_ltc(nframes, in, now + ltc_slave_latency.max ); + parse_ltc(nframes, in, now - ltc_slave_latency.max ); process_ltc(now); } @@ -497,10 +503,10 @@ LTC_Slave::speed_and_position (double& speed, framepos_t& pos) DEBUG_TRACE (DEBUG::LTC, string_compose ("LTCsync spd: %1 pos: %2 | last-pos: %3 elapsed: %4 delta: %5\n", speed, pos, last_ltc_frame, elapsed, current_delta)); -#if 1 /* provide a .1% deadzone to lock the speed */ - if (fabs(speed - 1.0) <= 0.001) + /* provide a .1% deadzone to lock the speed */ + if (fabs(speed - 1.0) <= 0.001) { speed = 1.0; -#endif + } return true; } diff --git a/libs/ardour/mtc_slave.cc b/libs/ardour/mtc_slave.cc index c94c14b986..f637d678a2 100644 --- a/libs/ardour/mtc_slave.cc +++ b/libs/ardour/mtc_slave.cc @@ -62,8 +62,8 @@ MTC_Slave::MTC_Slave (Session& s, MIDI::Port& p) last_mtc_fps_byte = session.get_mtc_timecode_bits (); quarter_frame_duration = (double(session.frames_per_timecode_frame()) / 4.0); - mtc_timecode = timecode_60; // track changes of MTC timecode - a3e_timecode = timecode_60; // track canges of Ardour's timecode + mtc_timecode = session.config.get_timecode_format(); + a3e_timecode = session.config.get_timecode_format(); printed_timecode_warning = false; reset (true); @@ -130,7 +130,7 @@ MTC_Slave::outside_window (framepos_t pos) const bool MTC_Slave::locked () const { - return port->parser()->mtc_locked(); + return port->parser()->mtc_locked() && last_inbound_frame !=0 && engine_dll_initstate !=0; } bool @@ -261,10 +261,10 @@ MTC_Slave::update_mtc_qtr (Parser& /*p*/, int which_qtr, framepos_t now) current.speed = mtc_speed; current.guard2++; + last_inbound_frame = now; } maybe_reset (); - last_inbound_frame = now; busy_guard2++; } @@ -549,15 +549,17 @@ MTC_Slave::speed_and_position (double& speed, framepos_t& pos) //sess_pos -= session.engine().frames_since_cycle_start(); SafeTime last; - framecnt_t elapsed; + frameoffset_t elapsed; + bool engine_dll_reinitialized = false; read_current (&last); /* re-init engine DLL here when state changed (direction, first_mtc_timestamp) */ if (last.timestamp == 0) { engine_dll_initstate = 0; } - else if (engine_dll_initstate != transport_direction) { + else if (engine_dll_initstate != transport_direction && last.speed != 0) { engine_dll_initstate = transport_direction; init_engine_dll(last.position, session.engine().frames_per_cycle()); + engine_dll_reinitialized = true; } if (last.timestamp == 0) { @@ -592,15 +594,13 @@ MTC_Slave::speed_and_position (double& speed, framepos_t& pos) else { /* scale elapsed time by the current MTC speed */ - if (last.timestamp && (now > last.timestamp)) { - elapsed = (framecnt_t) rint (speed_flt * (now - last.timestamp)); - } else { - elapsed = 0; - } - if (give_slave_full_control_over_transport_speed()) { - /* there is a frame-delta engine vs MTC position - * mostly due to quantization and rounding of (speed * nframes) - * thus we use an other DLL.. + elapsed = (framecnt_t) rint (speed_flt * (now - last.timestamp)); + if (give_slave_full_control_over_transport_speed() && !engine_dll_reinitialized) { + /* there is an engine vs MTC position frame-delta. + * This mostly due to quantization and rounding of (speed * nframes) + * but can also due to the session-process not calling + * speed_and_position() every cycle under some circumstances. + * Thus we use an other DLL to align the engine and the MTC */ /* update engine DLL and calculate speed */ @@ -628,11 +628,9 @@ MTC_Slave::speed_and_position (double& speed, framepos_t& pos) queue_reset (false); } -#if 1 /* provide a .1% deadzone to lock the speed */ if (fabs(speed - 1.0) <= 0.001) speed = 1.0; -#endif DEBUG_TRACE (DEBUG::MTC, string_compose ("MTCsync spd: %1 pos: %2 | last-pos: %3 elapsed: %4 delta: %5\n", speed, pos, last.position, elapsed, pos - sess_pos)); @@ -648,7 +646,7 @@ MTC_Slave::apparent_timecode_format () const return mtc_timecode; } -std::string +std::string MTC_Slave::approximate_current_position() const { SafeTime last; diff --git a/libs/ardour/mtdm.cc b/libs/ardour/mtdm.cc index 4c27b3b600..940f0b8c25 100644 --- a/libs/ardour/mtdm.cc +++ b/libs/ardour/mtdm.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2003-2008 Fons Adriaensen + Copyright (C) 2003-2012 Fons Adriaensen 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 @@ -20,115 +20,106 @@ #include "ardour/mtdm.h" -MTDM::MTDM () - : _cnt (0) - , _inv (0) +MTDM::MTDM (int fsamp) + : _cnt (0) + , _inv (0) { - int i; - Freq *F; + int i; + Freq *F; - _freq [0].f = 4096; - _freq [1].f = 512; - _freq [2].f = 1088; - _freq [3].f = 1544; - _freq [4].f = 2049; - - _freq [0].a = 0.2f; - _freq [1].a = 0.1f; - _freq [2].a = 0.1f; - _freq [3].a = 0.1f; - _freq [4].a = 0.1f; - - for (i = 0, F = _freq; i < 5; i++, F++) { - F->p = 128; - F->xa = F->ya = 0.0f; - F->xf = F->yf = 0.0f; - } + _freq [0].f = 4096; + _freq [1].f = 2048; + _freq [2].f = 3072; + _freq [3].f = 2560; + _freq [4].f = 2304; + _freq [5].f = 2176; + _freq [6].f = 1088; + _freq [7].f = 1312; + _freq [8].f = 1552; + _freq [9].f = 1800; + _freq [10].f = 3332; + _freq [11].f = 3586; + _freq [12].f = 3841; + _wlp = 200.0f / fsamp; + for (i = 0, F = _freq; i < 13; i++, F++) + { + F->p = 128; + F->xa = F->ya = 0.0f; + F->x1 = F->y1 = 0.0f; + F->x2 = F->y2 = 0.0f; + } } -int -MTDM::process (size_t len, float *ip, float *op) -{ - int i; - float vip, vop, a, c, s; - Freq *F; - while (len--) +int MTDM::process (size_t len, float *ip, float *op) +{ + int i; + float vip, vop, a, c, s; + Freq *F; + + while (len--) + { + vop = 0.0f; + vip = *ip++; + for (i = 0, F = _freq; i < 13; i++, F++) { - vop = 0.0f; - vip = *ip++; - for (i = 0, F = _freq; i < 5; i++, F++) - { - a = 2 * (float) M_PI * (F->p & 65535) / 65536.0; - F->p += F->f; - c = cosf (a); - s = -sinf (a); - vop += F->a * s; - F->xa += s * vip; - F->ya += c * vip; - } - *op++ = vop; - if (++_cnt == 16) - { - for (i = 0, F = _freq; i < 5; i++, F++) - { - F->xf += 1e-3f * (F->xa - F->xf + 1e-20); - F->yf += 1e-3f * (F->ya - F->yf + 1e-20); - F->xa = F->ya = 0.0f; - } - _cnt = 0; - } + a = 2 * (float) M_PI * (F->p & 65535) / 65536.0; + F->p += F->f; + c = cosf (a); + s = -sinf (a); + vop += (i ? 0.01f : 0.20f) * s; + F->xa += s * vip; + F->ya += c * vip; + } + *op++ = vop; + if (++_cnt == 16) + { + for (i = 0, F = _freq; i < 13; i++, F++) + { + F->x1 += _wlp * (F->xa - F->x1 + 1e-20); + F->y1 += _wlp * (F->ya - F->y1 + 1e-20); + F->x2 += _wlp * (F->x1 - F->x2 + 1e-20); + F->y2 += _wlp * (F->y1 - F->y2 + 1e-20); + F->xa = F->ya = 0.0f; + } + _cnt = 0; } + } - return 0; + return 0; } -int -MTDM::resolve () + +int MTDM::resolve (void) { - int i, k, m; - double d, e, f0, p; - Freq *F = _freq; + int i, k, m; + double d, e, f0, p; + Freq *F = _freq; - if (hypot (F->xf, F->yf) < 0.01) { - return -1; - } + if (hypot (F->x2, F->y2) < 0.001) return -1; + d = atan2 (F->y2, F->x2) / (2 * M_PI); + if (_inv) d += 0.5; + if (d > 0.5) d -= 1.0; + f0 = _freq [0].f; + m = 1; + _err = 0.0; + for (i = 0; i < 12; i++) + { + F++; + p = atan2 (F->y2, F->x2) / (2 * M_PI) - d * F->f / f0; + if (_inv) p += 0.5; + p -= floor (p); + p *= 2; + k = (int)(floor (p + 0.5)); + e = fabs (p - k); + if (e > _err) _err = e; + if (e > 0.4) return 1; + d += m * (k & 1); + m *= 2; + } + _del = 16 * d; - d = atan2 (F->yf, F->xf) / (2 * M_PI); - - if (_inv) { - d += 0.5f; - } - - if (d > 0.5f) { - d -= 1.0f; - } - - f0 = _freq [0].f; - m = 1; - _err = 0.0; - - for (i = 0; i < 4; i++) { - F++; - p = atan2 (F->yf, F->xf) / (2 * M_PI) - d * F->f / f0; - if (_inv) { - p += 0.5f; - } - p -= floor (p); - p *= 8; - k = (int)(floor (p + 0.5)); - e = fabs (p - k); - if (e > _err) { - _err = e; - } - if (e > 0.4) { - return 1; - } - d += m * (k & 7); - m *= 8; - } - - _del = 16 * d; - - return 0; + return 0; } + + diff --git a/libs/ardour/port_insert.cc b/libs/ardour/port_insert.cc index b726b14aa5..c13927449a 100644 --- a/libs/ardour/port_insert.cc +++ b/libs/ardour/port_insert.cc @@ -63,7 +63,7 @@ void PortInsert::start_latency_detection () { delete _mtdm; - _mtdm = new MTDM; + _mtdm = new MTDM (_session.frame_rate()); _latency_flush_frames = false; _latency_detect = true; _measured_latency = 0; diff --git a/libs/ardour/route.cc b/libs/ardour/route.cc index 86ef26e05b..a26f55f6a9 100644 --- a/libs/ardour/route.cc +++ b/libs/ardour/route.cc @@ -2944,7 +2944,9 @@ Route::output_change_handler (IOChange change, void * /*src*/) if (_sg_rack) { _sg_rack->reconfigure (_output->ports().num_ports (DataType::AUDIO)); - _sg_rack->make_connections (); + if (IO::connecting_legal) { + _sg_rack->make_connections (); + } } } diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index 1757ee2660..ba61baca0f 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -848,6 +848,8 @@ Session::hookup_io () it makes its own connections to ports. */ + cerr << "CREATING AUDITIONER\n"; + try { boost::shared_ptr a (new Auditioner (*this)); if (a->init()) { @@ -858,6 +860,7 @@ Session::hookup_io () } catch (failed_constructor& err) { + cerr << _("cannot create Auditioner: no auditioning of regions possible") << endl; warning << _("cannot create Auditioner: no auditioning of regions possible") << endmsg; } } @@ -2172,6 +2175,8 @@ Session::add_routes (RouteList& new_routes, bool input_auto_connect, bool output r->output()->changed.connect_same_thread (*this, boost::bind (&Session::set_worst_io_latencies_x, this, _1, _2)); r->processors_changed.connect_same_thread (*this, boost::bind (&Session::route_processors_changed, this, _1)); + cerr << "\n\nLOADING route " << r->name() << " master ? " << r->is_master() << endl; + if (r->is_master()) { _master_out = r; } diff --git a/libs/ardour/session_ltc.cc b/libs/ardour/session_ltc.cc index b7044d28a4..cc146cbb59 100644 --- a/libs/ardour/session_ltc.cc +++ b/libs/ardour/session_ltc.cc @@ -39,6 +39,25 @@ using namespace Timecode; //#define LTC_GEN_FRAMEDBUG //#define LTC_GEN_TXDBUG +#ifndef MAX +#define MAX(a,b) ( (a) > (b) ? (a) : (b) ) +#endif +#ifndef MIN +#define MIN(a,b) ( (a) < (b) ? (a) : (b) ) +#endif + +/* LTC signal should have a rise time of 25 us +/- 5 us. + * yet with most sound-cards a square-wave of 1-2 sample + * introduces ringing and small oscillations. + * https://en.wikipedia.org/wiki/Gibbs_phenomenon + * A low-pass filter in libltc can reduce this at + * the cost of being slightly out of spec WRT to rise-time. + * + * This filter is adaptive so that fast vari-speed signals + * will not be affected by it. + */ +#define LTC_RISE_TIME(speed) MIN (100, MAX(25, (4000000 / ((speed==0)?1:speed) / engine().frame_rate()))) + void Session::ltc_tx_initialize() { @@ -50,6 +69,7 @@ Session::ltc_tx_initialize() 0); ltc_encoder_set_bufsize(ltc_encoder, nominal_frame_rate(), 23.0); + ltc_encoder_set_filter(ltc_encoder, LTC_RISE_TIME(1.0)); /* buffersize for 1 LTC frame: (1 + sample-rate / fps) bytes * usually returned by ltc_encoder_get_buffersize(encoder) @@ -58,7 +78,8 @@ Session::ltc_tx_initialize() ltc_enc_buf = (ltcsnd_sample_t*) calloc((nominal_frame_rate() / 23), sizeof(ltcsnd_sample_t)); ltc_speed = 0; ltc_tx_reset(); - Xrun.connect_same_thread (*this, boost::bind (&Session::ltc_tx_resync_latency, this)); + ltc_tx_resync_latency(); + Xrun.connect_same_thread (*this, boost::bind (&Session::ltc_tx_reset, this)); engine().GraphReordered.connect_same_thread (*this, boost::bind (&Session::ltc_tx_resync_latency, this)); } @@ -92,7 +113,6 @@ Session::ltc_tx_reset() ltc_buf_off = 0; ltc_enc_byte = 0; ltc_enc_cnt = 0; - ltc_tx_resync_latency(); } void @@ -110,7 +130,7 @@ Session::ltc_tx_recalculate_position() a3tc.drop = timecode_has_drop_frames(ltc_enc_tcformat); Timecode::timecode_to_sample (a3tc, ltc_enc_pos, true, false, - double(frame_rate()), + (double)frame_rate(), config.get_subframes_per_frame(), config.get_timecode_offset_negative(), config.get_timecode_offset() ); @@ -153,16 +173,6 @@ Session::ltc_tx_send_time_code_for_cycle (framepos_t start_frame, framepos_t end /* range from libltc (38..218) || - 128.0 -> (-90..90) */ const float ltcvol = Config->get_ltc_output_volume()/(90.0); // pow(10, db/20.0)/(90.0); -#if 1 - /* TODO read layency only on demand -> ::ltc_tx_reset() - * this is already prepared. - * - * ..but first fix jack2 issue with re-computing latency - * in the correct order. Until then, querying it in the - * process-callback is the only way to get the current value - */ - ltcport->get_connected_latency_range(ltc_out_latency, true); -#endif DEBUG_TRACE (DEBUG::LTC, string_compose("LTC TX %1 to %2 / %3 | lat: %4\n", start_frame, end_frame, nframes, ltc_out_latency.max)); /* all systems go. Now here's the plan: @@ -188,6 +198,7 @@ Session::ltc_tx_send_time_code_for_cycle (framepos_t start_frame, framepos_t end ltc_tx_cleanup(); return; } + ltc_encoder_set_filter(ltc_encoder, LTC_RISE_TIME(ltc_speed)); ltc_enc_tcformat = cur_timecode; ltc_tx_reset(); } @@ -205,23 +216,19 @@ Session::ltc_tx_send_time_code_for_cycle (framepos_t start_frame, framepos_t end #define SIGNUM(a) ( (a) < 0 ? -1 : 1) bool speed_changed = false; - /* use port latency compensation */ -#if 1 - /* The generated timecode is offset by the port-latency, + /* port latency compensation: + * The _generated timecode_ is offset by the port-latency, * therefore the offset depends on the direction of transport. */ - framepos_t cycle_start_frame = (current_speed < 0) ? (start_frame + ltc_out_latency.max) : (start_frame - ltc_out_latency.max); -#else - /* This comes in handy when testing sync - record output on an A3 track - * see also http://tracker.ardour.org/view.php?id=5073 - */ - framepos_t cycle_start_frame = start_frame; -#endif + framepos_t cycle_start_frame = (current_speed < 0) ? (start_frame - ltc_out_latency.max) : (start_frame + ltc_out_latency.max); /* cycle-start may become negative due to latency compensation */ if (cycle_start_frame < 0) { cycle_start_frame = 0; } - double new_ltc_speed = double(labs(end_frame - start_frame) * SIGNUM(current_speed)) / double(nframes); + double new_ltc_speed = (double)(labs(end_frame - start_frame) * SIGNUM(current_speed)) / (double)nframes; + if (nominal_frame_rate() != frame_rate()) { + new_ltc_speed *= (double)nominal_frame_rate() / (double)frame_rate(); + } if (SIGNUM(new_ltc_speed) != SIGNUM (ltc_speed)) { DEBUG_TRACE (DEBUG::LTC, "LTC TX2: transport changed direction\n"); @@ -240,6 +247,7 @@ Session::ltc_tx_send_time_code_for_cycle (framepos_t start_frame, framepos_t end */ DEBUG_TRACE (DEBUG::LTC, string_compose("LTC TX2: speed change old: %1 cur: %2 tgt: %3 ctd: %4\n", ltc_speed, current_speed, target_speed, fabs(current_speed) - target_speed)); speed_changed = true; + ltc_encoder_set_filter(ltc_encoder, LTC_RISE_TIME(new_ltc_speed)); } if (end_frame == start_frame || fabs(current_speed) < 0.1 ) { @@ -289,8 +297,8 @@ Session::ltc_tx_send_time_code_for_cycle (framepos_t start_frame, framepos_t end * which is left for later.. */ - double oldbuflen = double(ltc_buf_len - ltc_buf_off); - double newbuflen = double(ltc_buf_len - ltc_buf_off) * fabs(ltc_speed / new_ltc_speed); + double oldbuflen = (double)(ltc_buf_len - ltc_buf_off); + double newbuflen = (double)(ltc_buf_len - ltc_buf_off) * fabs(ltc_speed / new_ltc_speed); DEBUG_TRACE (DEBUG::LTC, string_compose("LTC TX2: bufOld %1 bufNew %2 | diff %3\n", (ltc_buf_len - ltc_buf_off), newbuflen, newbuflen - oldbuflen @@ -348,11 +356,17 @@ Session::ltc_tx_send_time_code_for_cycle (framepos_t start_frame, framepos_t end framepos_t tc_sample_start; /* calc timecode frame from current position - round down to nearest timecode */ - sample_to_timecode(cycle_start_frame, tc_start, true, false); + Timecode::sample_to_timecode(cycle_start_frame, tc_start, true, false, + timecode_frames_per_second(), + timecode_drop_frames(), + (double)frame_rate(), + config.get_subframes_per_frame(), + config.get_timecode_offset_negative(), config.get_timecode_offset() + ); /* convert timecode back to sample-position */ Timecode::timecode_to_sample (tc_start, tc_sample_start, true, false, - double(frame_rate()), + (double)frame_rate(), config.get_subframes_per_frame(), config.get_timecode_offset_negative(), config.get_timecode_offset() ); @@ -392,7 +406,9 @@ Session::ltc_tx_send_time_code_for_cycle (framepos_t start_frame, framepos_t end rint(ltc_enc_pos + ltc_enc_cnt - poff) - cycle_start_frame )); - if (ltc_speed != 0 && fabs(ceil(ltc_enc_pos + ltc_enc_cnt - poff) - cycle_start_frame) > maxdiff) { + if (ltc_enc_pos < 0 + || (ltc_speed != 0 && fabs(ceil(ltc_enc_pos + ltc_enc_cnt - poff) - cycle_start_frame) > maxdiff) + ) { // (5) re-align ltc_tx_reset(); @@ -426,7 +442,7 @@ Session::ltc_tx_send_time_code_for_cycle (framepos_t start_frame, framepos_t end if (ltc_speed < 0 ) { /* calculate the byte that starts at or after the current position */ ltc_enc_byte = floor((10.0 * soff) / (fptcf)); - ltc_enc_cnt = double(ltc_enc_byte * fptcf / 10.0); + ltc_enc_cnt = ltc_enc_byte * fptcf / 10.0; /* calculate difference between the current position and the byte to send */ cyc_off = soff- ceil(ltc_enc_cnt); @@ -434,7 +450,7 @@ Session::ltc_tx_send_time_code_for_cycle (framepos_t start_frame, framepos_t end } else { /* calculate the byte that starts at or after the current position */ ltc_enc_byte = ceil((10.0 * soff) / fptcf); - ltc_enc_cnt = double(ltc_enc_byte * fptcf / 10.0); + ltc_enc_cnt = ltc_enc_byte * fptcf / 10.0; /* calculate difference between the current position and the byte to send */ cyc_off = ceil(ltc_enc_cnt) - soff; @@ -463,6 +479,26 @@ Session::ltc_tx_send_time_code_for_cycle (framepos_t start_frame, framepos_t end DEBUG_TRACE (DEBUG::LTC, string_compose("LTC TX5 restart @ %1 + %2 - %3 | byte %4\n", ltc_enc_pos, ltc_enc_cnt, cyc_off, ltc_enc_byte)); } + else if (ltc_speed != 0 && (fptcf / ltc_speed / 80) > 3 ) { + /* reduce (low freq) jitter. + * The granularity of the LTC encoder speed is 1 byte = + * (frames-per-timecode-frame / 10) audio-samples. + * Thus, tiny speed changes [as produced by the slave] + * may not have any effect in the cycle when they occur, + * but they will add up over time. + * + * This is a linear approx to compensate for this drift + * and prempt re-sync when the drift builds up. + * + * However, for very fast speeds - when 1 LTC bit is + * <= 3 audio-sample - adjusting speed may lead to + * invalid frames. + * + * To do better than this, resampling (or a rewrite of the + * encoder) is required. + */ + ltc_speed -= ((ltc_enc_pos + ltc_enc_cnt - poff) - cycle_start_frame) / engine().frame_rate(); + } // (6) encode and output @@ -527,6 +563,12 @@ Session::ltc_tx_send_time_code_for_cycle (framepos_t start_frame, framepos_t end ltc_enc_byte = (ltc_enc_byte + 1)%10; if (ltc_enc_byte == 0 && ltc_speed != 0) { ltc_encoder_inc_timecode(ltc_encoder); +#if 0 /* force fixed parity -- scope debug */ + LTCFrame f; + ltc_encoder_get_frame(ltc_encoder, &f); + f.biphase_mark_phase_correction=0; + ltc_encoder_set_frame(ltc_encoder, &f); +#endif ltc_tx_recalculate_position(); ltc_enc_cnt = 0; } else if (ltc_enc_byte == 0) { diff --git a/libs/ardour/sg_rack.cc b/libs/ardour/sg_rack.cc index 91195f9d62..5fde915682 100644 --- a/libs/ardour/sg_rack.cc +++ b/libs/ardour/sg_rack.cc @@ -118,6 +118,8 @@ SoundGridRack::make_connections () boost::shared_ptr master_out = _route.session().master_out(); bool is_track = (dynamic_cast(&_route) != 0) && !_route.is_hidden(); + assert (master_out); + DEBUG_TRACE (DEBUG::SoundGrid, string_compose ("Mapping input for %1 (track ? %2) with %3 outputs\n", _route.name(), is_track, ports.num_ports())); diff --git a/libs/ardour/wscript b/libs/ardour/wscript index 30960e4c51..ef108858f7 100644 --- a/libs/ardour/wscript +++ b/libs/ardour/wscript @@ -343,6 +343,19 @@ int main(int argc, char **argv) { mandatory = True, errmsg = missing_jack_message) + conf.check_cc(fragment = ''' +#include +int main(int argc, char** argv) { + jack_position_t pos; + pos.valid & JackVideoFrameOffset; + return 0; +}''', + uselib= [ 'JACK' ], + msg = 'Checking for JackVideoFrameOffset', + define_name = 'HAVE_JACK_VIDEO_SUPPORT', + mandatory = False, + okmsg = 'present') + if flac_supported(): conf.define ('HAVE_FLAC', 1) if ogg_supported(): diff --git a/libs/audiographer/audiographer/general/chunker.h b/libs/audiographer/audiographer/general/chunker.h index 397d67ffc5..cc46aa4575 100644 --- a/libs/audiographer/audiographer/general/chunker.h +++ b/libs/audiographer/audiographer/general/chunker.h @@ -49,14 +49,15 @@ class Chunker framecnt_t const frames_to_copy = chunk_size - position; TypeUtils::copy (&context.data()[input_position], &buffer[position], frames_to_copy); - // Output whole buffer - ProcessContext c_out (context, buffer, chunk_size); - ListedSource::output (c_out); - // Update counters position = 0; input_position += frames_to_copy; frames_left -= frames_to_copy; + + // Output whole buffer + ProcessContext c_out (context, buffer, chunk_size); + if (frames_left) { c_out.remove_flag(ProcessContext::EndOfInput); } + ListedSource::output (c_out); } if (frames_left) { diff --git a/libs/audiographer/audiographer/sndfile/tmp_file.h b/libs/audiographer/audiographer/sndfile/tmp_file.h index a4e7b2679a..8655fd7191 100644 --- a/libs/audiographer/audiographer/sndfile/tmp_file.h +++ b/libs/audiographer/audiographer/sndfile/tmp_file.h @@ -1,6 +1,9 @@ #ifndef AUDIOGRAPHER_TMP_FILE_H #define AUDIOGRAPHER_TMP_FILE_H +#include +#include + #include "sndfile_writer.h" #include "sndfile_reader.h" @@ -12,14 +15,29 @@ template class TmpFile : public SndfileWriter, public SndfileReader { public: - + + /// \a filename_template must match the requirements for mkstemp, i.e. end in "XXXXXX" + TmpFile (char * filename_template, int format, ChannelCount channels, framecnt_t samplerate) + : SndfileHandle (mkstemp(filename_template), true, SndfileBase::ReadWrite, format, channels, samplerate) + , filename (filename_template) + {} + TmpFile (int format, ChannelCount channels, framecnt_t samplerate) : SndfileHandle (fileno (tmpfile()), true, SndfileBase::ReadWrite, format, channels, samplerate) {} - + TmpFile (TmpFile const & other) : SndfileHandle (other) {} using SndfileHandle::operator=; - + + ~TmpFile() + { + if (!filename.empty()) { + std::remove(filename.c_str()); + } + } + + private: + std::string filename; }; } // namespace diff --git a/libs/audiographer/tests/general/chunker_test.cc b/libs/audiographer/tests/general/chunker_test.cc index ea5c29a410..d3adab3691 100644 --- a/libs/audiographer/tests/general/chunker_test.cc +++ b/libs/audiographer/tests/general/chunker_test.cc @@ -14,6 +14,7 @@ class ChunkerTest : public CppUnit::TestFixture CPPUNIT_TEST (testSynchronousProcess); CPPUNIT_TEST (testAsynchronousProcess); CPPUNIT_TEST (testChoppingProcess); + CPPUNIT_TEST (testEndOfInputFlagHandling); CPPUNIT_TEST_SUITE_END (); public: @@ -136,6 +137,36 @@ class ChunkerTest : public CppUnit::TestFixture CPPUNIT_ASSERT (TestUtils::array_equals (random_data, &sink->get_array()[ 3 * frames / 2], frames / 2)); } + void testEndOfInputFlagHandling() + { + boost::shared_ptr > grabber(new ProcessContextGrabber()); + + assert (frames % 2 == 0); + chunker.reset (new Chunker(frames)); + chunker->add_output (grabber); + + ProcessContext const half_context (random_data, frames / 2, 1); + ProcessContext const context (random_data, frames, 1); + context.set_flag(ProcessContext<>::EndOfInput); + + // Process 0.5 then 1.0 + chunker->process (half_context); + chunker->process (context); + + // Should output two contexts + CPPUNIT_ASSERT_EQUAL((int)grabber->contexts.size(), 2); + ProcessContextGrabber::ContextList::iterator it = grabber->contexts.begin(); + + // first 1.0 not end of input + CPPUNIT_ASSERT_EQUAL(it->frames(), frames); + CPPUNIT_ASSERT(!it->has_flag(ProcessContext<>::EndOfInput)); + + // Then 0.5 with end of input + ++it; + CPPUNIT_ASSERT_EQUAL(it->frames(), frames / 2); + CPPUNIT_ASSERT(it->has_flag(ProcessContext<>::EndOfInput)); + } + private: boost::shared_ptr > chunker; boost::shared_ptr > sink; diff --git a/libs/timecode/src/time.cc b/libs/timecode/src/time.cc index 5a4da10691..b848db79b3 100644 --- a/libs/timecode/src/time.cc +++ b/libs/timecode/src/time.cc @@ -445,12 +445,20 @@ timecode_to_frames_per_second(TimecodeFormat t) break; case timecode_2997: - return 29.97; + return (30000.0/1001.0); //29.97; break; case timecode_2997drop: return (30000.0/1001.0); //29.97; + break; + case timecode_2997000: + return 29.97; + + break; + case timecode_2997000drop: + return 29.97; + break; case timecode_30: return 30; @@ -502,6 +510,14 @@ timecode_has_drop_frames(TimecodeFormat t) case timecode_2997drop: return true; + break; + case timecode_2997000: + return false; + + break; + case timecode_2997000drop: + return true; + break; case timecode_30: return false; @@ -554,6 +570,14 @@ timecode_format_name (TimecodeFormat const t) case timecode_2997drop: return "29.97 drop"; + break; + case timecode_2997000: + return "29.97000"; + + break; + case timecode_2997000drop: + return "29.97000 drop"; + break; case timecode_30: return "30"; diff --git a/libs/timecode/timecode/time.h b/libs/timecode/timecode/time.h index 8166ba8f39..256d8feaaf 100644 --- a/libs/timecode/timecode/time.h +++ b/libs/timecode/timecode/time.h @@ -39,6 +39,8 @@ enum TimecodeFormat { timecode_25, timecode_2997, timecode_2997drop, + timecode_2997000, + timecode_2997000drop, timecode_30, timecode_30drop, timecode_5994,