From 7bd41538d951c3e476655df741adfbebbb990bde Mon Sep 17 00:00:00 2001 From: David Robillard Date: Tue, 19 Sep 2006 03:29:16 +0000 Subject: Merged with trunk R920. git-svn-id: svn://localhost/ardour2/branches/midi@921 d708f5d6-7413-0410-9779-e7cbd77b26cf --- SConstruct | 11 - gtk2_ardour/actions.cc | 8 +- gtk2_ardour/ardev | 2 +- gtk2_ardour/ardour.menus | 24 + gtk2_ardour/ardour_ui.cc | 144 +++- gtk2_ardour/ardour_ui_dependents.cc | 4 + gtk2_ardour/ardour_ui_dialogs.cc | 4 - gtk2_ardour/ardour_ui_options.cc | 2 +- gtk2_ardour/audio_streamview.cc | 23 +- gtk2_ardour/automation_line.h | 3 +- gtk2_ardour/canvas-simplerect.c | 54 +- gtk2_ardour/editing_syms.h | 9 +- gtk2_ardour/editor.cc | 129 ++- gtk2_ardour/editor.h | 23 +- gtk2_ardour/editor_actions.cc | 171 ++++ gtk2_ardour/editor_audio_import.cc | 2 +- gtk2_ardour/editor_canvas.cc | 56 +- gtk2_ardour/editor_export_audio.cc | 4 +- gtk2_ardour/editor_ops.cc | 16 +- gtk2_ardour/editor_rulers.cc | 166 ++-- gtk2_ardour/editor_tempodisplay.cc | 126 +-- gtk2_ardour/midi_streamview.cc | 13 +- gtk2_ardour/new_session_dialog.cc | 8 +- gtk2_ardour/option_editor.cc | 57 -- gtk2_ardour/option_editor.h | 2 - gtk2_ardour/region_gain_line.h | 4 - gtk2_ardour/route_time_axis.cc | 51 +- gtk2_ardour/route_time_axis.h | 3 +- gtk2_ardour/route_ui.cc | 14 +- gtk2_ardour/route_ui.h | 10 +- gtk2_ardour/sfdb_ui.cc | 19 +- gtk2_ardour/streamview.cc | 3 + gtk2_ardour/streamview.h | 2 +- libs/appleutility/CAAudioFile.cpp | 1241 +++++++++++++++++++++++++++ libs/appleutility/CAAudioFile.h | 439 ++++++++++ libs/appleutility/CABufferList.cpp | 179 ++++ libs/appleutility/CABufferList.h | 300 +++++++ libs/appleutility/CAXException.cpp | 45 + libs/appleutility/CAXException.h | 158 ++++ libs/ardour/SConscript | 28 +- libs/ardour/ardour/audio_diskstream.h | 2 - libs/ardour/ardour/audiofilesource.h | 9 +- libs/ardour/ardour/audioplaylist.h | 2 + libs/ardour/ardour/audioregion.h | 1 + libs/ardour/ardour/audiosource.h | 4 +- libs/ardour/ardour/automation_event.h | 4 +- libs/ardour/ardour/coreaudiosource.h | 9 +- libs/ardour/ardour/destructive_filesource.h | 6 +- libs/ardour/ardour/diskstream.h | 2 - libs/ardour/ardour/io.h | 3 - libs/ardour/ardour/location.h | 6 - libs/ardour/ardour/midi_source.h | 4 +- libs/ardour/ardour/playlist.h | 3 - libs/ardour/ardour/port.h | 2 +- libs/ardour/ardour/region.h | 3 - libs/ardour/ardour/session.h | 9 +- libs/ardour/ardour/smf_source.h | 4 +- libs/ardour/ardour/sndfilesource.h | 6 +- libs/ardour/ardour/source.h | 11 +- libs/ardour/ardour/source_factory.h | 9 +- libs/ardour/ardour/tempo.h | 3 - libs/ardour/audio_diskstream.cc | 7 +- libs/ardour/audio_playlist.cc | 1 + libs/ardour/audioengine.cc | 5 + libs/ardour/audiofilesource.cc | 24 +- libs/ardour/audiofilter.cc | 2 +- libs/ardour/audioregion.cc | 23 +- libs/ardour/audiosource.cc | 8 +- libs/ardour/coreaudiosource.cc | 157 ++-- libs/ardour/destructive_filesource.cc | 12 +- libs/ardour/globals.cc | 4 +- libs/ardour/import.cc | 2 +- libs/ardour/io.cc | 5 +- libs/ardour/location.cc | 5 + libs/ardour/midi_source.cc | 8 +- libs/ardour/panner.cc | 2 +- libs/ardour/plugin.cc | 4 +- libs/ardour/route.cc | 14 +- libs/ardour/session.cc | 74 +- libs/ardour/session_state.cc | 175 ++-- libs/ardour/session_time.cc | 4 +- libs/ardour/session_timefx.cc | 3 +- libs/ardour/smf_source.cc | 8 +- libs/ardour/sndfilesource.cc | 16 +- libs/ardour/source.cc | 13 +- libs/ardour/source_factory.cc | 81 +- libs/ardour/tempo.cc | 30 + libs/ardour/utils.cc | 2 +- libs/ardour/vst_plugin.cc | 4 +- libs/gtkmm2ext/dndtreeview.cc | 7 +- libs/pbd/pbd/stateful.h | 2 +- 91 files changed, 3495 insertions(+), 866 deletions(-) create mode 100644 libs/appleutility/CAAudioFile.cpp create mode 100644 libs/appleutility/CAAudioFile.h create mode 100644 libs/appleutility/CABufferList.cpp create mode 100644 libs/appleutility/CABufferList.h create mode 100644 libs/appleutility/CAXException.cpp create mode 100644 libs/appleutility/CAXException.h diff --git a/SConstruct b/SConstruct index be4b6ce0b8..73aca5a4d9 100644 --- a/SConstruct +++ b/SConstruct @@ -468,17 +468,6 @@ if conf.CheckHeader ('boost/shared_ptr.hpp', language='CXX') == 0: libraries['boost'] = conf.Finish () -conf = env.Configure () - -# jack_port_ensure_monitor available - -if conf.CheckFunc('jack_port_ensure_monitor'): - env.Append(CCFLAGS='-DWITH_JACK_PORT_ENSURE_MONITOR') -else: - print '\nWARNING: You need at least svn revision 985 of jack for hardware monitoring to work correctly.\n' - -env = conf.Finish() - # # Check for liblo diff --git a/gtk2_ardour/actions.cc b/gtk2_ardour/actions.cc index 2197d4dc00..b2408620a5 100644 --- a/gtk2_ardour/actions.cc +++ b/gtk2_ardour/actions.cc @@ -73,13 +73,13 @@ ActionManager::init () ui_manager->add_ui_from_file (ui_file); loaded = true; } catch (Glib::MarkupError& err) { - error << "badly formatted UI definition file" << endmsg; + error << _("badly formatted UI definition file") << endmsg; } catch (...) { - error << "Ardour menu definition file not found" << endmsg; + error << _("Ardour menu definition file not found") << endmsg; } if (!loaded) { - error << "ardour will not work without a valid ardour.menus file" << endmsg; + error << _("ardour will not work without a valid ardour.menus file") << endmsg; exit(1); } } @@ -277,7 +277,7 @@ ActionManager::uncheck_toggleaction (const char * name) RefPtr tact = RefPtr::cast_dynamic(act); tact->set_active (false); } else { - error << "Unknown action name: " << name << endmsg; + error << string_compose (_("Unknown action name: %1"), name) << endmsg; } delete [] group_name; diff --git a/gtk2_ardour/ardev b/gtk2_ardour/ardev index 751557d634..d3d44e55fe 100755 --- a/gtk2_ardour/ardev +++ b/gtk2_ardour/ardev @@ -1,3 +1,3 @@ #!/bin/sh source `dirname "$0"`/ardev_common.sh -exec gtk2_ardour/ardour.bin --novst "$*" +exec gtk2_ardour/ardour.bin --novst $* diff --git a/gtk2_ardour/ardour.menus b/gtk2_ardour/ardour.menus index 0f1f741c30..d213b4a53c 100644 --- a/gtk2_ardour/ardour.menus +++ b/gtk2_ardour/ardour.menus @@ -214,6 +214,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gtk2_ardour/ardour_ui.cc b/gtk2_ardour/ardour_ui.cc index dae6c9f751..f0848e6f89 100644 --- a/gtk2_ardour/ardour_ui.cc +++ b/gtk2_ardour/ardour_ui.cc @@ -1651,6 +1651,9 @@ ARDOUR_UI::save_template () void ARDOUR_UI::new_session (bool startup, std::string predetermined_path) { + string session_name; + string session_path; + int response = Gtk::RESPONSE_NONE; new_session_dialog->set_modal(true); @@ -1672,14 +1675,28 @@ ARDOUR_UI::new_session (bool startup, std::string predetermined_path) new_session_dialog->reset(); } else if (response == Gtk::RESPONSE_YES) { - /* YES == OPEN, but there's no enum for that */ - std::string session_name = new_session_dialog->session_name(); - std::string session_path = new_session_dialog->session_folder(); - load_session (session_path, session_name); + /* YES == OPEN, but there's no enum for that */ + session_name = new_session_dialog->session_name(); + + if (session_name.empty()) { + response = Gtk::RESPONSE_NONE; + cerr << "session name is empty\n"; + continue; + } + + if (session_name[0] == '/' || + (session_name.length() > 2 && session_name[0] == '.' && session_name[1] == '/') || + (session_name.length() > 3 && session_name[0] == '.' && session_name[1] == '.' && session_name[2] == '/')) { + load_session (Glib::path_get_dirname (session_name), session_name); + } else { + session_path = new_session_dialog->session_folder(); + load_session (session_path, session_name); + } } else if (response == Gtk::RESPONSE_OK) { + if (new_session_dialog->get_current_page() == 1) { /* XXX this is a bit of a hack.. @@ -1687,93 +1704,121 @@ ARDOUR_UI::new_session (bool startup, std::string predetermined_path) if we're on page 1 (the load page) Unfortunately i can't see how atm.. */ - - std::string session_name = new_session_dialog->session_name(); - std::string session_path = new_session_dialog->session_folder(); - load_session (session_path, session_name); - + + if (session_name.empty()) { + response = Gtk::RESPONSE_NONE; + cerr << "session name is empty 2\n"; + continue; + } + + if (session_name[0] == '/' || + (session_name.length() > 2 && session_name[0] == '.' && session_name[1] == '/') || + (session_name.length() > 3 && session_name[0] == '.' && session_name[1] == '.' && session_name[2] == '/')) { + load_session (Glib::path_get_dirname (session_name), session_name); + } else { + session_path = new_session_dialog->session_folder(); + load_session (session_path, session_name); + } + } else { _session_is_new = true; - std::string session_name = new_session_dialog->session_name(); - std::string session_path = new_session_dialog->session_folder(); + session_name = new_session_dialog->session_name(); + if (session_name.empty()) { + response = Gtk::RESPONSE_NONE; + cerr << "session name is empty 3\n"; + continue; + } + + if (session_name[0] == '/' || + (session_name.length() > 2 && session_name[0] == '.' && session_name[1] == '/') || + (session_name.length() > 3 && session_name[0] == '.' && session_name[1] == '.' && session_name[2] == '/')) { + + session_path = Glib::path_get_dirname (session_name); + session_name = Glib::path_get_basename (session_name); + + } else { + + std::string session_path = new_session_dialog->session_folder(); + + } //XXX This is needed because session constructor wants a //non-existant path. hopefully this will be fixed at some point. - - session_path = Glib::build_filename(session_path, session_name); - + + session_path = Glib::build_filename (session_path, session_name); + std::string template_name = new_session_dialog->session_template_name(); - + if (new_session_dialog->use_session_template()) { - - load_session (session_path, session_name, &template_name); + + load_session (session_path, session_name, &template_name); } else { - - uint32_t cchns; + + uint32_t cchns; uint32_t mchns; Session::AutoConnectOption iconnect; Session::AutoConnectOption oconnect; - + if (new_session_dialog->create_control_bus()) { - cchns = (uint32_t) new_session_dialog->control_channel_count(); + cchns = (uint32_t) new_session_dialog->control_channel_count(); } else { - cchns = 0; + cchns = 0; } - + if (new_session_dialog->create_master_bus()) { - mchns = (uint32_t) new_session_dialog->master_channel_count(); + mchns = (uint32_t) new_session_dialog->master_channel_count(); } else { - mchns = 0; + mchns = 0; } - + if (new_session_dialog->connect_inputs()) { - iconnect = Session::AutoConnectPhysical; + iconnect = Session::AutoConnectPhysical; } else { - iconnect = Session::AutoConnectOption (0); + iconnect = Session::AutoConnectOption (0); } - + /// @todo some minor tweaks. - + if (new_session_dialog->connect_outs_to_master()) { - oconnect = Session::AutoConnectMaster; + oconnect = Session::AutoConnectMaster; } else if (new_session_dialog->connect_outs_to_physical()) { - oconnect = Session::AutoConnectPhysical; + oconnect = Session::AutoConnectPhysical; } else { - oconnect = Session::AutoConnectOption (0); + oconnect = Session::AutoConnectOption (0); } - + uint32_t nphysin = (uint32_t) new_session_dialog->input_limit_count(); uint32_t nphysout = (uint32_t) new_session_dialog->output_limit_count(); - + build_session (session_path, - session_name, - cchns, - mchns, - iconnect, - oconnect, - nphysin, - nphysout, - engine->frame_rate() * 60 * 5); + session_name, + cchns, + mchns, + iconnect, + oconnect, + nphysin, + nphysout, + engine->frame_rate() * 60 * 5); } - } + } } } while (response == Gtk::RESPONSE_NONE); + show(); new_session_dialog->get_window()->set_cursor(); - new_session_dialog->hide(); } void ARDOUR_UI::close_session() { - unload_session(); - new_session (); + unload_session(); + new_session (); } int @@ -1873,6 +1918,11 @@ ARDOUR_UI::show () { if (editor) { editor->show_window (); + + if (!shown_flag) { + editor->present (); + } + shown_flag = true; } diff --git a/gtk2_ardour/ardour_ui_dependents.cc b/gtk2_ardour/ardour_ui_dependents.cc index 0603fc6baa..bf096f86c1 100644 --- a/gtk2_ardour/ardour_ui_dependents.cc +++ b/gtk2_ardour/ardour_ui_dependents.cc @@ -79,6 +79,10 @@ ARDOUR_UI::connect_dependents_to_session (ARDOUR::Session *s) { editor->connect_to_session (s); mixer->connect_to_session (s); + + /* its safe to do this now */ + + s->restore_history (s->snap_name()); } void diff --git a/gtk2_ardour/ardour_ui_dialogs.cc b/gtk2_ardour/ardour_ui_dialogs.cc index c90480c1dd..fe5963c535 100644 --- a/gtk2_ardour/ardour_ui_dialogs.cc +++ b/gtk2_ardour/ardour_ui_dialogs.cc @@ -131,10 +131,6 @@ ARDOUR_UI::connect_to_session (Session *s) start_clocking (); start_blinking (); - if (editor) { - editor->present(); - } - transport_stopped (); second_connection = Glib::signal_timeout().connect (mem_fun(*this, &ARDOUR_UI::every_second), 1000); diff --git a/gtk2_ardour/ardour_ui_options.cc b/gtk2_ardour/ardour_ui_options.cc index 205180a344..21afbcde21 100644 --- a/gtk2_ardour/ardour_ui_options.cc +++ b/gtk2_ardour/ardour_ui_options.cc @@ -463,6 +463,7 @@ ARDOUR_UI::setup_session_options () session_control_changed (Session::AutoReturn); session_control_changed (Session::AutoInput); session_control_changed (Session::Clicking); + session_control_changed (Session::SmpteMode); session->ControlChanged.connect (mem_fun (*this, &ARDOUR_UI::queue_session_control_changed)); } @@ -555,7 +556,6 @@ ARDOUR_UI::session_control_changed (Session::ControlType t) case Session::CrossfadingModel: break; - case Session::AutoPlay: map_some_session_state ("Transport", "ToggleAutoPlay", &Session::get_auto_play); break; diff --git a/gtk2_ardour/audio_streamview.cc b/gtk2_ardour/audio_streamview.cc index 2b9cd23567..85ba517f8e 100644 --- a/gtk2_ardour/audio_streamview.cc +++ b/gtk2_ardour/audio_streamview.cc @@ -68,6 +68,7 @@ AudioStreamView::AudioStreamView (AudioTimeAxisView& tv) _amplitude_above_axis = 1.0; use_rec_regions = tv.editor.show_waveforms_recording (); + } AudioStreamView::~AudioStreamView () @@ -215,9 +216,7 @@ AudioStreamView::playlist_modified () StreamView::playlist_modified(); - /* if the playlist is modified, make sure xfades are on top and all the regionviews are stacked - correctly. - */ + /* make sure xfades are on top and all the regionviews are stacked correctly. */ for (list::iterator i = crossfade_views.begin(); i != crossfade_views.end(); ++i) { (*i)->get_canvas_group()->raise_to_top(); @@ -419,10 +418,11 @@ AudioStreamView::setup_rec_box () boost::shared_ptr region (boost::dynamic_pointer_cast (RegionFactory::create (sources, start, 1 , "", 0, (Region::Flag)(Region::DefaultFlags | Region::DoNotSaveState), false))); + assert(region); region->set_position (_trackview.session().transport_frame(), this); rec_regions.push_back (region); - /* catch it if it goes away */ - region->GoingAway.connect (bind (mem_fun (*this, &AudioStreamView::remove_rec_region), region)); + + // rec regions are destroyed in setup_rec_box /* we add the region later */ } @@ -504,6 +504,14 @@ AudioStreamView::setup_rec_box () /* remove temp regions */ + for (list >::iterator iter = rec_regions.begin(); iter != rec_regions.end();) { + list >::iterator tmp; + tmp = iter; + ++tmp; + (*iter)->drop_references (); + iter = tmp; + } + rec_regions.clear(); // cerr << "\tclear " << rec_rects.size() << " rec rects\n"; @@ -567,9 +575,10 @@ AudioStreamView::update_rec_regions () continue; } - // FIXME boost::shared_ptr region = boost::dynamic_pointer_cast(*iter); - assert(region); + if (!region) { + continue; + } jack_nframes_t origlen = region->length(); diff --git a/gtk2_ardour/automation_line.h b/gtk2_ardour/automation_line.h index f8262d2a2d..8311025b02 100644 --- a/gtk2_ardour/automation_line.h +++ b/gtk2_ardour/automation_line.h @@ -163,9 +163,8 @@ class AutomationLine : public sigc::trackable, public PBD::StatefulDestructible XMLNode& get_state (void); int set_state (const XMLNode&); - PBD::ID id() { return _id; } protected: - PBD::ID _id; + string _name; guint32 _height; uint32_t _line_color; diff --git a/gtk2_ardour/canvas-simplerect.c b/gtk2_ardour/canvas-simplerect.c index abddf5756b..d59096e68b 100644 --- a/gtk2_ardour/canvas-simplerect.c +++ b/gtk2_ardour/canvas-simplerect.c @@ -263,7 +263,7 @@ gnome_canvas_simplerect_reset_bounds (GnomeCanvasItem *item) GnomeCanvasSimpleRect* simplerect; double x1, x2, y1, y2; double old_x1, old_x2, old_y1, old_y2; - double a, b; + double a, b, c, d; old_x1 = item->x1; old_y1 = item->y1; @@ -287,42 +287,24 @@ gnome_canvas_simplerect_reset_bounds (GnomeCanvasItem *item) gnome_canvas_w2c (GNOME_CANVAS(item->canvas), x2, y2, &simplerect->bbox_lrx, &simplerect->bbox_lry); /* now queue redraws for changed areas */ - - if (item->x1 != old_x1) { - - /* left edge changed. redraw the area that altered */ a = MIN(item->x1, old_x1); b = MAX(item->x1, old_x1); - gnome_canvas_request_redraw (item->canvas, a - 1, item->y1, b + 1, item->y2); - } - - if (item->x2 != old_x2) { - - /* right edge changed. redraw the area that altered */ - - a = MIN(item->x2, old_x2); - b = MAX(item->x2, old_x2); - gnome_canvas_request_redraw (item->canvas, a - 1, item->y1, b + 1, item->y2); - } - - if (item->y1 != old_y1) { - - /* top edge changed. redraw the area that altered */ - - a = MIN(item->y1, old_y1); - b = MAX(item->y1, old_y1); - gnome_canvas_request_redraw (item->canvas, item->x1, a - 1, item->x2, b + 1); - } - - if (item->y2 != old_y2) { - - /* lower edge changed. redraw the area that altered */ - - a = MIN(item->y2, old_y2); - b = MAX(item->y2, old_y2); - gnome_canvas_request_redraw (item->canvas, item->x1, a - 1, item->x2, b + 1); - } + + a = MIN(a, item->x2); + a = MIN(a, old_x2); + b = MAX(b, item->x2); + b = MAX(b, old_x2); + + c = MIN(item->y1, old_y1); + d = MAX(item->y1, old_y1); + + c = MIN(c,item->y2); + c = MIN(c, old_y2); + d = MAX(d,item->y2); + d = MAX(d, old_y2); + + gnome_canvas_request_redraw (item->canvas, a, c, b + 0.5, d + 0.5); } /* @@ -494,8 +476,8 @@ gnome_canvas_simplerect_update (GnomeCanvasItem *item, double *affine, ArtSVP *c gnome_canvas_request_redraw (item->canvas, simplerect->bbox_ulx, simplerect->bbox_uly, - simplerect->bbox_lrx+1, - simplerect->bbox_lry+1); + simplerect->bbox_lrx+0.5, + simplerect->bbox_lry+0.5); simplerect->full_draw_on_update = FALSE; } diff --git a/gtk2_ardour/editing_syms.h b/gtk2_ardour/editing_syms.h index 654ddc8852..3a503e2cb5 100644 --- a/gtk2_ardour/editing_syms.h +++ b/gtk2_ardour/editing_syms.h @@ -54,7 +54,8 @@ DISPLAYCONTROL(ShowMeasures) DISPLAYCONTROL(ShowWaveforms) DISPLAYCONTROL(ShowWaveformsRecording) -IMPORTMODE(ImportAsRegion) -IMPORTMODE(ImportAsTrack) -IMPORTMODE(ImportAsTapeTrack) -IMPORTMODE(ImportToTrack) +// if this is changed, remember to update the string table in sfdb_ui.cc +IMPORTMODE(ImportAsRegion=0) +IMPORTMODE(ImportToTrack=1) +IMPORTMODE(ImportAsTrack=2) +IMPORTMODE(ImportAsTapeTrack=3) diff --git a/gtk2_ardour/editor.cc b/gtk2_ardour/editor.cc index 933db72bab..0fc9810c70 100644 --- a/gtk2_ardour/editor.cc +++ b/gtk2_ardour/editor.cc @@ -643,7 +643,7 @@ Editor::Editor (AudioEngine& eng) edit_pane.pack1 (edit_packer, true, true); edit_pane.pack2 (the_notebook, false, true); - edit_pane.signal_size_allocate().connect_notify (bind (mem_fun(*this, &Editor::pane_allocation_handler), static_cast (&edit_pane))); + edit_pane.signal_size_allocate().connect (bind (mem_fun(*this, &Editor::pane_allocation_handler), static_cast (&edit_pane))); top_hbox.pack_start (toolbar_frame, true, true); @@ -700,6 +700,7 @@ Editor::Editor (AudioEngine& eng) ControlProtocol::ZoomIn.connect (bind (mem_fun (*this, &Editor::temporal_zoom_step), false)); ControlProtocol::ZoomOut.connect (bind (mem_fun (*this, &Editor::temporal_zoom_step), true)); ControlProtocol::ScrollTimeline.connect (mem_fun (*this, &Editor::control_scroll)); + constructed = true; instant_save (); @@ -882,6 +883,9 @@ Editor::reposition_x_origin (jack_nframes_t frame) } horizontal_adjustment.set_value (frame/frames_per_unit); + } else { + update_fixed_rulers(); + tempo_map_changed (Change (0)); } } @@ -965,20 +969,10 @@ void Editor::canvas_horizontally_scrolled () { - Glib::signal_idle().connect (mem_fun(*this, &Editor::lazy_canvas_horizontally_scrolled)); - -} - -bool -Editor::lazy_canvas_horizontally_scrolled () -{ - - leftmost_frame = (jack_nframes_t) floor (horizontal_adjustment.get_value() * frames_per_unit); - + leftmost_frame = (jack_nframes_t) floor (horizontal_adjustment.get_value() * frames_per_unit); update_fixed_rulers (); tempo_map_changed (Change (0)); - return false; } void @@ -996,7 +990,6 @@ Editor::deferred_reposition_and_zoom (jack_nframes_t frame, double nfpu) set_frames_per_unit (nfpu); reposition_x_origin (frame); - repos_zoom_queued = false; return FALSE; @@ -1033,6 +1026,10 @@ Editor::session_control_changed (Session::ControlType t) update_layering_model (); break; + case Session::SmpteMode: + update_smpte_mode (); + break; + default: break; } @@ -1179,6 +1176,9 @@ Editor::connect_to_session (Session *t) session_connections.push_back (session->SMPTEOffsetChanged.connect (mem_fun(*this, &Editor::update_just_smpte))); session_connections.push_back (session->SMPTETypeChanged.connect (mem_fun(*this, &Editor::update_just_smpte))); + session_connections.push_back (session->SMPTETypeChanged.connect (mem_fun(*this, &Editor::update_smpte_mode))); + session_connections.push_back (session->PullupChanged.connect (mem_fun(*this, &Editor::update_video_pullup))); + session_connections.push_back (session->tempo_map().StateChanged.connect (mem_fun(*this, &Editor::tempo_map_changed))); edit_groups_changed (); @@ -1266,9 +1266,12 @@ Editor::connect_to_session (Session *t) } /* xfade visibility state set from editor::set_state() */ - - update_crossfade_model (); - update_layering_model (); + + update_crossfade_model(); + update_layering_model(); + + update_smpte_mode(); + update_video_pullup(); handle_new_duration (); @@ -1292,7 +1295,7 @@ Editor::connect_to_session (Session *t) horizontal_adjustment.set_value (0); restore_ruler_visibility (); - tempo_map_changed (Change (0)); + //tempo_map_changed (Change (0)); session->tempo_map().apply_with_metrics (*this, &Editor::draw_metric_marks); edit_cursor->set_position (0); @@ -1328,6 +1331,7 @@ Editor::connect_to_session (Session *t) } /* register for undo history */ + session->register_with_memento_command_factory(_id, this); } @@ -2110,6 +2114,9 @@ Editor::set_state (const XMLNode& node) int x, y, xoff, yoff; Gdk::Geometry g; + if ((prop = node.property ("id")) != 0) { + _id = prop->value (); + } if ((geometry = find_named_node (node, "geometry")) == 0) { @@ -2241,6 +2248,9 @@ Editor::get_state () XMLNode* node = new XMLNode ("Editor"); char buf[32]; + _id.print (buf); + node->add_property ("id", buf); + if (is_realized()) { Glib::RefPtr win = get_window(); @@ -2250,7 +2260,7 @@ Editor::get_state () win->get_size(width, height); XMLNode* geometry = new XMLNode ("geometry"); - char buf[32]; + snprintf(buf, sizeof(buf), "%d", width); geometry->add_property("x_size", string(buf)); snprintf(buf, sizeof(buf), "%d", height); @@ -4085,6 +4095,7 @@ Editor::restore_editing_space () { mouse_mode_tearoff->set_visible (true); tools_tearoff->set_visible (true); + edit_pane.set_position (pre_maximal_pane_position); unfullscreen(); @@ -4138,6 +4149,87 @@ Editor::on_key_press_event (GdkEventKey* ev) return key_press_focus_accelerator_handler (*this, ev); } +void +Editor::update_smpte_mode () +{ + ENSURE_GUI_THREAD(mem_fun(*this, &Editor::update_smpte_mode)); + + RefPtr act; + + float frames = session->smpte_frames_per_second; + bool drop = session->smpte_drop_frames; + + if ((frames < 23.976 * 1.0005) && !drop) + act = ActionManager::get_action (X_("Editor"), X_("Smpte23976")); + else if ((frames < 24 * 1.0005) && !drop) + act = ActionManager::get_action (X_("Editor"), X_("Smpte24")); + else if ((frames < 24.976 * 1.0005) && !drop) + act = ActionManager::get_action (X_("Editor"), X_("Smpte24976")); + else if ((frames < 25 * 1.0005) && !drop) + act = ActionManager::get_action (X_("Editor"), X_("Smpte25")); + else if ((frames < 29.97 * 1.0005) && !drop) + act = ActionManager::get_action (X_("Editor"), X_("Smpte2997")); + else if ((frames < 29.97 * 1.0005) && drop) + act = ActionManager::get_action (X_("Editor"), X_("Smpte2997drop")); + else if ((frames < 30 * 1.0005) && !drop) + act = ActionManager::get_action (X_("Editor"), X_("Smpte30")); + else if ((frames < 30 * 1.0005) && drop) + act = ActionManager::get_action (X_("Editor"), X_("Smpte30drop")); + else if ((frames < 59.94 * 1.0005) && !drop) + act = ActionManager::get_action (X_("Editor"), X_("Smpte5994")); + else if ((frames < 60 * 1.0005) && !drop) + act = ActionManager::get_action (X_("Editor"), X_("Smpte60")); + else + cerr << "Unexpected SMPTE value (" << frames << (drop ? "drop" : "") << ") in update_smpte_mode. Menu is probably wrong\n" << endl; + + + if (act) { + RefPtr ract = RefPtr::cast_dynamic(act); + if (ract && !ract->get_active()) { + ract->set_active (true); + } + } +} + +void +Editor::update_video_pullup () +{ + ENSURE_GUI_THREAD (mem_fun(*this, &Editor::update_video_pullup)); + + RefPtr act; + + float pullup = session->video_pullup; + + if ( pullup < (-4.1667 - 0.1) * 0.99) { + act = ActionManager::get_action (X_("Editor"), X_("PullupMinus4Minus1")); + } else if ( pullup < (-4.1667) * 0.99 ) { + act = ActionManager::get_action (X_("Editor"), X_("PullupMinus4")); + } else if ( pullup < (-4.1667 + 0.1) * 0.99 ) { + act = ActionManager::get_action (X_("Editor"), X_("PullupMinus4Plus1")); + } else if ( pullup < (-0.1) * 0.99 ) { + act = ActionManager::get_action (X_("Editor"), X_("PullupMinus1")); + } else if (pullup > (4.1667 + 0.1) * 0.99 ) { + act = ActionManager::get_action (X_("Editor"), X_("PullupPlus4Plus1")); + } else if ( pullup > (4.1667) * 0.99 ) { + act = ActionManager::get_action (X_("Editor"), X_("PullupPlus4")); + } else if ( pullup > (4.1667 - 0.1) * 0.99) { + act = ActionManager::get_action (X_("Editor"), X_("PullupPlus4Minus1")); + } else if ( pullup > (0.1) * 0.99 ) { + act = ActionManager::get_action (X_("Editor"), X_("PullupPlus1")); + } else + act = ActionManager::get_action (X_("Editor"), X_("PullupNone")); + + + if (act) { + RefPtr ract = RefPtr::cast_dynamic(act); + if (ract && !ract->get_active()) { + ract->set_active (true); + } + } + +} + + void Editor::update_layering_model () { @@ -4163,7 +4255,6 @@ Editor::update_layering_model () } } - void Editor::update_crossfade_model () { diff --git a/gtk2_ardour/editor.h b/gtk2_ardour/editor.h index 239bd6307d..fe5d47df0c 100644 --- a/gtk2_ardour/editor.h +++ b/gtk2_ardour/editor.h @@ -143,8 +143,6 @@ class Editor : public PublicEditor XMLNode& get_state (); int set_state (const XMLNode& ); - PBD::ID id() { return _id; } - void set_mouse_mode (Editing::MouseMode, bool force=true); void step_mouse_mode (bool next); Editing::MouseMode current_mouse_mode () { return mouse_mode; } @@ -231,6 +229,7 @@ class Editor : public PublicEditor void set_show_measures (bool yn); bool show_measures () const { return _show_measures; } + bool initial_ruler_update_required; #ifdef FFT_ANALYSIS /* analysis window */ @@ -302,6 +301,14 @@ class Editor : public PublicEditor void set_meter_falloff (int); void set_meter_hold (int32_t); + /* SMPTE timecode & video sync */ + + void smpte_fps_chosen (ARDOUR::Session::SmpteFormat format); + void video_pullup_chosen (ARDOUR::Session::PullupFormat pullup); + + void update_smpte_mode(); + void update_video_pullup(); + /* xfades */ void toggle_auto_xfade (); @@ -311,8 +318,8 @@ class Editor : public PublicEditor void update_crossfade_model (); void set_crossfade_model (ARDOUR::CrossfadeModel); - /* layers */ + /* layers */ void set_layer_model (ARDOUR::Session::LayerModel); void update_layering_model (); @@ -349,8 +356,6 @@ class Editor : public PublicEditor ARDOUR::AudioEngine& engine; bool constructed; - PBD::ID _id; - PlaylistSelector* _playlist_selector; void set_frames_per_unit (double); @@ -683,7 +688,7 @@ class Editor : public PublicEditor void tie_vertical_scrolling (); void canvas_horizontally_scrolled (); - bool lazy_canvas_horizontally_scrolled (); + void reposition_and_zoom (jack_nframes_t sample, double fpu); gint deferred_reposition_and_zoom (jack_nframes_t sample, double fpu); void end_location_changed (ARDOUR::Location*); @@ -1185,8 +1190,6 @@ class Editor : public PublicEditor bool _follow_playhead; bool _show_waveforms_recording; - void add_bbt_marks (ARDOUR::TempoMap::BBTPointList&); - ARDOUR::TempoMap::BBTPointList *current_bbt_points; typedef vector TimeLineList; @@ -1197,7 +1200,7 @@ class Editor : public PublicEditor ArdourCanvas::SimpleLine* get_time_line (); void hide_measures (); void draw_measures (); - void draw_time_bars (); + bool lazy_hide_and_draw_measures (); void new_tempo_section (); @@ -1506,7 +1509,7 @@ class Editor : public PublicEditor jack_nframes_t autoscroll_distance; static gint _autoscroll_canvas (void *); - gint autoscroll_canvas (); + bool autoscroll_canvas (); void start_canvas_autoscroll (int direction); void stop_canvas_autoscroll (); void maybe_autoscroll (GdkEvent*); diff --git a/gtk2_ardour/editor_actions.cc b/gtk2_ardour/editor_actions.cc index 65195ca822..f1b9ef4b83 100644 --- a/gtk2_ardour/editor_actions.cc +++ b/gtk2_ardour/editor_actions.cc @@ -39,6 +39,8 @@ Editor::register_actions () ActionManager::register_action (editor_actions, X_("Monitoring"), _("Monitoring")); ActionManager::register_action (editor_actions, X_("Autoconnect"), _("Autoconnect")); ActionManager::register_action (editor_actions, X_("Layering"), _("Layering")); + ActionManager::register_action (editor_actions, X_("SMPTE"), _("SMPTE fps")); + ActionManager::register_action (editor_actions, X_("Pullup"), _("Pullup / Pulldown")); ActionManager::register_action (editor_actions, X_("Metering"), _("Metering")); ActionManager::register_action (editor_actions, X_("MeteringFallOffRate"), _("Fall off rate")); ActionManager::register_action (editor_actions, X_("MeteringHoldTime"), _("Hold Time")); @@ -393,6 +395,31 @@ Editor::register_actions () ActionManager::register_radio_action (editor_actions, layer_model_group, X_("LayerMoveAddHigher"), _("Most Recently Moved/Added is Higher"), bind (mem_fun (*this, &Editor::set_layer_model), Session::MoveAddHigher)); ActionManager::register_radio_action (editor_actions, layer_model_group, X_("LayerAddHigher"), _("Most Recently Added is Higher"), bind (mem_fun (*this, &Editor::set_layer_model), Session::AddHigher)); + RadioAction::Group smpte_group; + + ActionManager::register_radio_action (editor_actions, smpte_group, X_("Smpte23976"), _("23.976"), bind (mem_fun (*this, &Editor::smpte_fps_chosen), Session::smpte_23976)); + ActionManager::register_radio_action (editor_actions, smpte_group, X_("Smpte24"), _("24"), bind (mem_fun (*this, &Editor::smpte_fps_chosen), Session::smpte_24)); + ActionManager::register_radio_action (editor_actions, smpte_group, X_("Smpte24976"), _("24.976"), bind (mem_fun (*this, &Editor::smpte_fps_chosen), Session::smpte_24976)); + ActionManager::register_radio_action (editor_actions, smpte_group, X_("Smpte25"), _("25"), bind (mem_fun (*this, &Editor::smpte_fps_chosen), Session::smpte_25)); + ActionManager::register_radio_action (editor_actions, smpte_group, X_("Smpte2997"), _("29.97"), bind (mem_fun (*this, &Editor::smpte_fps_chosen), Session::smpte_2997)); + ActionManager::register_radio_action (editor_actions, smpte_group, X_("Smpte2997drop"), _("29.97 drop"), bind (mem_fun (*this, &Editor::smpte_fps_chosen), Session::smpte_2997drop)); + ActionManager::register_radio_action (editor_actions, smpte_group, X_("Smpte30"), _("30"), bind (mem_fun (*this, &Editor::smpte_fps_chosen), Session::smpte_30)); + ActionManager::register_radio_action (editor_actions, smpte_group, X_("Smpte30drop"), _("30 drop"), bind (mem_fun (*this, &Editor::smpte_fps_chosen), Session::smpte_30drop)); + ActionManager::register_radio_action (editor_actions, smpte_group, X_("Smpte5994"), _("59.94"), bind (mem_fun (*this, &Editor::smpte_fps_chosen), Session::smpte_5994)); + ActionManager::register_radio_action (editor_actions, smpte_group, X_("Smpte60"), _("60"), bind (mem_fun (*this, &Editor::smpte_fps_chosen), Session::smpte_60)); + + RadioAction::Group pullup_group; + + ActionManager::register_radio_action (editor_actions, pullup_group, X_("PullupPlus4Plus1"), _("+4.1667% + 0.1%"), bind (mem_fun (*this, &Editor::video_pullup_chosen), Session::pullup_Plus4Plus1)); + ActionManager::register_radio_action (editor_actions, pullup_group, X_("PullupPlus4"), _("+4.1667%"), bind (mem_fun (*this, &Editor::video_pullup_chosen), Session::pullup_Plus4)); + ActionManager::register_radio_action (editor_actions, pullup_group, X_("PullupPlus4Minus1"), _("+4.1667% - 0.1%"), bind (mem_fun (*this, &Editor::video_pullup_chosen), Session::pullup_Plus4Minus1)); + ActionManager::register_radio_action (editor_actions, pullup_group, X_("PullupPlus1"), _("+ 0.1%"), bind (mem_fun (*this, &Editor::video_pullup_chosen), Session::pullup_Plus1)); + ActionManager::register_radio_action (editor_actions, pullup_group, X_("PullupNone"), _("None"), bind (mem_fun (*this, &Editor::video_pullup_chosen), Session::pullup_None)); + ActionManager::register_radio_action (editor_actions, pullup_group, X_("PullupMinus1"), _("- 0.1%"), bind (mem_fun (*this, &Editor::video_pullup_chosen), Session::pullup_Minus1)); + ActionManager::register_radio_action (editor_actions, pullup_group, X_("PullupMinus4Plus1"), _("-4.1667% + 0.1%"), bind (mem_fun (*this, &Editor::video_pullup_chosen), Session::pullup_Minus4Plus1)); + ActionManager::register_radio_action (editor_actions, pullup_group, X_("PullupMinus4"), _("-4.1667%"), bind (mem_fun (*this, &Editor::video_pullup_chosen), Session::pullup_Minus4)); + ActionManager::register_radio_action (editor_actions, pullup_group, X_("PullupMinus4Minus1"), _("-4.1667% - 0.1%"), bind (mem_fun (*this, &Editor::video_pullup_chosen), Session::pullup_Minus4Minus1)); + ActionManager::add_action_group (rl_actions); ActionManager::add_action_group (zoom_actions); ActionManager::add_action_group (mouse_mode_actions); @@ -492,6 +519,150 @@ Editor::set_layer_model (Session::LayerModel model) } } +void +Editor::smpte_fps_chosen (Session::SmpteFormat format) +{ + /* this is driven by a toggle on a radio group, and so is invoked twice, + once for the item that became inactive and once for the one that became + active. + */ + + if (session) { + + float fps = 10; + bool drop = false; + + RefPtr act; + + switch (format) { + case Session::smpte_23976: { + fps=23.976; + drop = false; + act = ActionManager::get_action (X_("Editor"), X_("Smpte23976")); + } break; + case Session::smpte_24: { + fps=24; + drop = false; + act = ActionManager::get_action (X_("Editor"), X_("Smpte24")); + } break; + case Session::smpte_24976: { + fps=24.976; + drop = false; + act = ActionManager::get_action (X_("Editor"), X_("Smpte24976")); + } break; + case Session::smpte_25: { + fps=25; + drop = false; + act = ActionManager::get_action (X_("Editor"), X_("Smpte25")); + } break; + case Session::smpte_2997: { + fps=29.97; + drop = false; + act = ActionManager::get_action (X_("Editor"), X_("Smpte2997")); + } break; + case Session::smpte_2997drop: { + fps=29.97; + drop = true; + act = ActionManager::get_action (X_("Editor"), X_("Smpte2997drop")); + } break; + case Session::smpte_30: { + fps=30; + drop = false; + act = ActionManager::get_action (X_("Editor"), X_("Smpte30")); + } break; + case Session::smpte_30drop: { + fps=30; + drop = true; + act = ActionManager::get_action (X_("Editor"), X_("Smpte30drop")); + } break; + case Session::smpte_5994: { + fps=59.94; + drop = false; + act = ActionManager::get_action (X_("Editor"), X_("Smpte5994")); + } break; + case Session::smpte_60: { + fps=60; + drop = false; + act = ActionManager::get_action (X_("Editor"), X_("Smpte60")); + } break; + default: + cerr << "Editor received unexpected smpte type" << endl; + } + + if (act) { + RefPtr ract = RefPtr::cast_dynamic(act); + if (ract && ract->get_active()) { + session->set_smpte_type (fps, drop); + } + } + } +} + +void +Editor::video_pullup_chosen (Session::PullupFormat pullup) +{ + /* this is driven by a toggle on a radio group, and so is invoked twice, + once for the item that became inactive and once for the one that became + active. + */ + + if (session) { + + RefPtr act; + + float pull = 0.0; + + switch (pullup) { + case Session::pullup_Plus4Plus1:{ + pull = 4.1667 + 0.1; + act = ActionManager::get_action (X_("Editor"), X_("PullupPlus4Plus1")); + } break; + case Session::pullup_Plus4:{ + pull = 4.1667; + act = ActionManager::get_action (X_("Editor"), X_("PullupPlus4")); + } break; + case Session::pullup_Plus4Minus1:{ + pull = 4.1667 - 0.1; + act = ActionManager::get_action (X_("Editor"), X_("PullupPlus4Minus1")); + } break; + case Session::pullup_Plus1:{ + pull = 0.1; + act = ActionManager::get_action (X_("Editor"), X_("PullupPlus1")); + } break; + case Session::pullup_None:{ + pull = 0.0; + act = ActionManager::get_action (X_("Editor"), X_("PullupNone")); + } break; + case Session::pullup_Minus1:{ + pull = -0.1; + act = ActionManager::get_action (X_("Editor"), X_("PullupMinus1")); + } break; + case Session::pullup_Minus4Plus1:{ + pull = -4.1667 + 0.1; + act = ActionManager::get_action (X_("Editor"), X_("PullupMinus4Plus1")); + } break; + case Session::pullup_Minus4:{ + pull = -4.1667; + act = ActionManager::get_action (X_("Editor"), X_("PullupMinus4")); + } break; + case Session::pullup_Minus4Minus1:{ + pull = -4.1667 - 0.1; + act = ActionManager::get_action (X_("Editor"), X_("PullupMinus4Minus1")); + } break; + default: + cerr << "Session received unexpected pullup type" << endl; + } + + if (act) { + RefPtr ract = RefPtr::cast_dynamic(act); + if (ract && ract->get_active()) { + session->set_video_pullup ( pull ); + } + } else cerr << "Editor::video_pullup_chosen could not find action to match pullup." << endl; + } +} + + void Editor::set_crossfade_model (CrossfadeModel model) { diff --git a/gtk2_ardour/editor_audio_import.cc b/gtk2_ardour/editor_audio_import.cc index fdd680beb2..e38dee5953 100644 --- a/gtk2_ardour/editor_audio_import.cc +++ b/gtk2_ardour/editor_audio_import.cc @@ -270,7 +270,7 @@ Editor::embed_sndfile (Glib::ustring path, bool split, bool multiple_files, bool idspec += string_compose(":%1", n); try { - source = boost::dynamic_pointer_cast (SourceFactory::createReadable (DataType::AUDIO, idspec, (mode == ImportAsTrack ? AudioFileSource::Destructive : AudioFileSource::Flag (0)))); + source = boost::dynamic_pointer_cast (SourceFactory::createReadable (DataType::AUDIO, *session, idspec, (mode == ImportAsTrack ? AudioFileSource::Destructive : AudioFileSource::Flag (0)))); sources.push_back(source); } diff --git a/gtk2_ardour/editor_canvas.cc b/gtk2_ardour/editor_canvas.cc index d8eebf1c4b..e2e04326ca 100644 --- a/gtk2_ardour/editor_canvas.cc +++ b/gtk2_ardour/editor_canvas.cc @@ -266,14 +266,15 @@ Editor::initialize_canvas () edit_cursor = new Cursor (*this, "blue", &Editor::canvas_edit_cursor_event); playhead_cursor = new Cursor (*this, "red", &Editor::canvas_playhead_cursor_event); - + + initial_ruler_update_required = true; track_canvas.signal_size_allocate().connect (mem_fun(*this, &Editor::track_canvas_allocate)); + } void Editor::track_canvas_allocate (Gtk::Allocation alloc) { - static bool first_time = true; canvas_width = alloc.get_width(); canvas_height = alloc.get_height(); @@ -318,14 +319,16 @@ Editor::track_canvas_allocate (Gtk::Allocation alloc) transport_punchout_line->property_y2() = canvas_height; } - update_fixed_rulers (); - - if (is_visible() && first_time) { + if (is_visible() && initial_ruler_update_required) { + /* + this is really dumb, but signal_size_allocate() gets emitted intermittently + depending on whether the canvas contents are visible or not. + we only want to do this once + */ + update_fixed_rulers(); tempo_map_changed (Change (0)); - first_time = false; - } else { - redisplay_tempo (); - } + initial_ruler_update_required = false; + } Resized (); /* EMIT_SIGNAL */ } @@ -414,6 +417,8 @@ Editor::track_canvas_drag_data_received (const RefPtr& context const SelectionData& data, guint info, guint time) { + cerr << "dropping, target = " << data.get_target() << endl; + if (data.get_target() == "regions") { drop_regions (context, x, y, data, info, time); } else { @@ -528,7 +533,7 @@ Editor::maybe_autoscroll (GdkEvent* event) } - if (autoscroll_direction != last_autoscroll_direction) { + if ((autoscroll_direction != last_autoscroll_direction) || (leftmost_frame < frame < rightmost_frame)) { stop_canvas_autoscroll (); } @@ -542,14 +547,13 @@ Editor::maybe_autoscroll (GdkEvent* event) gint Editor::_autoscroll_canvas (void *arg) { - return ((Editor *) arg)->autoscroll_canvas (); + return ((Editor *) arg)->autoscroll_canvas (); } -gint +bool Editor::autoscroll_canvas () { jack_nframes_t new_frame; - bool keep_calling = true; jack_nframes_t limit = max_frames - current_page_frames(); GdkEventMotion ev; jack_nframes_t target_frame; @@ -570,10 +574,6 @@ Editor::autoscroll_canvas () target_frame = drag_info.current_pointer_frame + autoscroll_distance; } - if (new_frame != leftmost_frame) { - reposition_x_origin (new_frame); - } - /* now fake a motion event to get the object that is being dragged to move too */ ev.type = GDK_MOTION_NOTIFY; @@ -593,20 +593,26 @@ Editor::autoscroll_canvas () /* connect the timeout so that we get called repeatedly */ - autoscroll_timeout_tag = g_timeout_add (20, _autoscroll_canvas, this); - keep_calling = false; + autoscroll_timeout_tag = g_idle_add ( _autoscroll_canvas, this); + return false; + + } - } else if (autoscroll_cnt == 50) { /* 0.5 seconds */ + if (new_frame != leftmost_frame) { + reposition_x_origin (new_frame); + } + + if (autoscroll_cnt == 50) { /* 0.5 seconds */ /* after about a while, speed up a bit by changing the timeout interval */ - autoscroll_distance = (jack_nframes_t) floor (current_page_frames()/50.0f); + autoscroll_distance = (jack_nframes_t) floor (current_page_frames()/30.0f); - } else if (autoscroll_cnt == 75) { /* 1.0 seconds */ + } else if (autoscroll_cnt == 150) { /* 1.0 seconds */ autoscroll_distance = (jack_nframes_t) floor (current_page_frames()/20.0f); - } else if (autoscroll_cnt == 100) { /* 1.5 seconds */ + } else if (autoscroll_cnt == 300) { /* 1.5 seconds */ /* after about another while, speed up by increasing the shift per callback */ @@ -614,7 +620,7 @@ Editor::autoscroll_canvas () } - return keep_calling; + return true; } void @@ -631,7 +637,7 @@ Editor::start_canvas_autoscroll (int dir) autoscroll_cnt = 0; /* do it right now, which will start the repeated callbacks */ - + autoscroll_canvas (); } diff --git a/gtk2_ardour/editor_export_audio.cc b/gtk2_ardour/editor_export_audio.cc index 6c67a8fb0e..1b0308b080 100644 --- a/gtk2_ardour/editor_export_audio.cc +++ b/gtk2_ardour/editor_export_audio.cc @@ -210,7 +210,7 @@ Editor::write_region (string path, boost::shared_ptr region) try { - fs = boost::dynamic_pointer_cast (SourceFactory::createReadable (DataType::AUDIO, path, AudioFileSource::Flag (0))); + fs = boost::dynamic_pointer_cast (SourceFactory::createReadable (DataType::AUDIO, *session, path, AudioFileSource::Flag (0))); } catch (failed_constructor& err) { @@ -343,7 +343,7 @@ Editor::write_audio_range (AudioPlaylist& playlist, const ChanCount& count, list path = s; try { - fs = boost::dynamic_pointer_cast (SourceFactory::createReadable (DataType::AUDIO, path, AudioFileSource::Flag (0))); + fs = boost::dynamic_pointer_cast (SourceFactory::createReadable (DataType::AUDIO, *session, path, AudioFileSource::Flag (0))); } catch (failed_constructor& err) { diff --git a/gtk2_ardour/editor_ops.cc b/gtk2_ardour/editor_ops.cc index 7d262503f8..f44b433caa 100644 --- a/gtk2_ardour/editor_ops.cc +++ b/gtk2_ardour/editor_ops.cc @@ -1110,9 +1110,9 @@ Editor::temporal_zoom_step (bool coarser) nfpu = frames_per_unit; if (coarser) { - nfpu *= 2.0; + nfpu *= 1.61803399; } else { - nfpu = max(1.0,(nfpu/2.0)); + nfpu = max(1.0,(nfpu/1.61803399)); } temporal_zoom (nfpu); @@ -1251,22 +1251,22 @@ Editor::temporal_zoom_to_frame (bool coarser, jack_nframes_t frame) { if (!session) return; - jack_nframes_t range_before = frame - leftmost_frame; + double range_before = frame - leftmost_frame; double new_fpu; new_fpu = frames_per_unit; if (coarser) { - new_fpu *= 2.0; - range_before *= 2; + new_fpu *= 1.61803399; + range_before *= 1.61803399; } else { - new_fpu = max(1.0,(new_fpu/2.0)); - range_before /= 2; + new_fpu = max(1.0,(new_fpu/1.61803399)); + range_before /= 1.61803399; } if (new_fpu == frames_per_unit) return; - jack_nframes_t new_leftmost = frame - range_before; + jack_nframes_t new_leftmost = frame - (jack_nframes_t)range_before; if (new_leftmost > frame) new_leftmost = 0; diff --git a/gtk2_ardour/editor_rulers.cc b/gtk2_ardour/editor_rulers.cc index 47641655e7..224e779bf9 100644 --- a/gtk2_ardour/editor_rulers.cc +++ b/gtk2_ardour/editor_rulers.cc @@ -819,9 +819,9 @@ Editor::metric_get_smpte (GtkCustomRulerMark **marks, gdouble lower, gdouble upp if (lower > (spacer = (jack_nframes_t)(128 * Editor::get_current_zoom ()))) { lower = lower - spacer; } else { - upper = upper + spacer - lower; lower = 0; } + upper = upper + spacer; range = (jack_nframes_t) floor (upper - lower); if (range < (2 * session->frames_per_smpte_frame())) { /* 0 - 2 frames */ @@ -1035,114 +1035,87 @@ Editor::metric_get_bbt (GtkCustomRulerMark **marks, gdouble lower, gdouble upper return 0; } - TempoMap::BBTPointList::iterator i; - TempoMap::BBTPointList *zoomed_bbt_points; + TempoMap::BBTPointList::iterator i; + uint32_t beats = 0; uint32_t bars = 0; - uint32_t tick = 0; - uint32_t skip; - uint32_t t; - uint32_t zoomed_beats = 0; - uint32_t zoomed_bars = 0; uint32_t desirable_marks; uint32_t magic_accent_number = 1; gint nmarks; char buf[64]; gint n; jack_nframes_t pos; - jack_nframes_t frame_one_beats_worth; - jack_nframes_t frame_skip; - double frame_skip_error; - double accumulated_error; - bool bar_helper_on = true; - + bool bar_helper_on = true; - BBT_Time previous_beat; BBT_Time next_beat; jack_nframes_t next_beat_pos; jack_nframes_t ilower = (jack_nframes_t) floor (lower); - jack_nframes_t iupper = (jack_nframes_t) floor (upper); - if ((desirable_marks = maxchars / 6) == 0) { + if ((desirable_marks = maxchars / 7) == 0) { return 0; } /* align the tick marks to whatever we're snapping to... */ - - if (snap_type == SnapToAThirdBeat) { + + switch (snap_type) { + case SnapToAThirdBeat: bbt_beat_subdivision = 3; - } else if (snap_type == SnapToAQuarterBeat) { + break; + case SnapToAQuarterBeat: bbt_beat_subdivision = 4; - } else if (snap_type == SnapToAEighthBeat) { + break; + case SnapToAEighthBeat: bbt_beat_subdivision = 8; magic_accent_number = 2; - } else if (snap_type == SnapToASixteenthBeat) { + break; + case SnapToASixteenthBeat: bbt_beat_subdivision = 16; magic_accent_number = 4; - } else if (snap_type == SnapToAThirtysecondBeat) { + break; + case SnapToAThirtysecondBeat: bbt_beat_subdivision = 32; magic_accent_number = 8; - } else { + break; + default: bbt_beat_subdivision = 4; + break; } - /* First find what a beat's distance is, so we can start plotting stuff before the beginning of the ruler */ - - session->bbt_time(ilower,previous_beat); - previous_beat.ticks = 0; - next_beat = previous_beat; - - if (session->tempo_map().meter_at(ilower).beats_per_bar() < (next_beat.beats + 1)) { - next_beat.bars += 1; - next_beat.beats = 1; - } else { - next_beat.beats += 1; - } - - frame_one_beats_worth = session->tempo_map().frame_time(next_beat) - session->tempo_map().frame_time(previous_beat); - - - zoomed_bbt_points = session->tempo_map().get_points((ilower >= frame_one_beats_worth) ? ilower - frame_one_beats_worth : 0, iupper); - - if (current_bbt_points == 0 || zoomed_bbt_points == 0 || zoomed_bbt_points->empty()) { + if (current_bbt_points == 0 || current_bbt_points->empty()) { return 0; } - for (i = current_bbt_points->begin(); i != current_bbt_points->end(); i++) { - if ((*i).type == TempoMap::Beat) { - beats++; - } else if ((*i).type == TempoMap::Bar) { - bars++; - } - } + i = current_bbt_points->end(); + i--; + bars = (*i).bar - (*current_bbt_points->begin()).bar; + beats = current_bbt_points->size() - bars; + /*Only show the bar helper if there aren't many bars on the screen */ - if (bars > 1) { + if (bars > 2) { bar_helper_on = false; } - for (i = zoomed_bbt_points->begin(); i != zoomed_bbt_points->end(); i++) { - if ((*i).type == TempoMap::Beat) { - zoomed_beats++; - } else if ((*i).type == TempoMap::Bar) { - zoomed_bars++; - } - } - if (desirable_marks > (beats / 4)) { /* we're in beat land...*/ + uint32_t tick = 0; + uint32_t skip; + uint32_t t; + jack_nframes_t frame_skip; + double frame_skip_error; + double accumulated_error; double position_of_helper; bool i_am_accented = false; bool we_need_ticks = false; position_of_helper = ilower + (30 * Editor::get_current_zoom ()); - if (desirable_marks >= (beats * 2)) { - nmarks = (zoomed_beats * bbt_beat_subdivision) + 1; + if (desirable_marks >= (beats)) { + nmarks = ((beats + 1) * bbt_beat_subdivision) + 1; we_need_ticks = true; } else { - nmarks = zoomed_beats + 1; + nmarks = beats + 1; } *marks = (GtkCustomRulerMark *) g_malloc (sizeof(GtkCustomRulerMark) * nmarks); @@ -1151,17 +1124,14 @@ Editor::metric_get_bbt (GtkCustomRulerMark **marks, gdouble lower, gdouble upper (*marks)[0].position = ilower; (*marks)[0].style = GtkCustomRulerMarkMicro; - for (n = 1, i = zoomed_bbt_points->begin(); i != zoomed_bbt_points->end() && n < nmarks; ++i) { - - if ((*i).frame <= ilower && (bar_helper_on)) { - + for (n = 1, i = current_bbt_points->begin(); n < nmarks && i != current_bbt_points->end(); i++) { + + if ((*i).frame < ilower && (bar_helper_on)) { snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat); (*marks)[0].label = g_strdup (buf); } else { - if ((*i).type == TempoMap::Bar) { - tick = 0; (((*i).frame < position_of_helper) && bar_helper_on) ? snprintf (buf, sizeof(buf), " ") : snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar); (*marks)[n].label = g_strdup (buf); @@ -1170,7 +1140,6 @@ Editor::metric_get_bbt (GtkCustomRulerMark **marks, gdouble lower, gdouble upper n++; } else if (((*i).type == TempoMap::Beat) && ((*i).beat > 1)) { - tick = 0; ((((*i).frame < position_of_helper) && bar_helper_on) || !we_need_ticks) ? snprintf (buf, sizeof(buf), " ") : snprintf (buf, sizeof(buf), "%" PRIu32, (*i).beat); if (((*i).beat % 2 == 1) || we_need_ticks) { @@ -1184,33 +1153,36 @@ Editor::metric_get_bbt (GtkCustomRulerMark **marks, gdouble lower, gdouble upper } } - /* Find the next beat */ - - session->bbt_time((*i).frame, next_beat); - if (session->tempo_map().meter_at((*i).frame).beats_per_bar() > (next_beat.beats + 1)) { - next_beat.beats += 1; - } else { - next_beat.bars += 1; - next_beat.beats = 1; - } - - next_beat_pos = session->tempo_map().frame_time(next_beat); /* Add the tick marks */ - if (we_need_ticks) { + if (we_need_ticks && (*i).type != TempoMap::Bar) { + + /* Find the next beat */ + + next_beat.beats = (*i).beat; + next_beat.bars = (*i).bar; + + if ((*i).meter->beats_per_bar() > (next_beat.beats + 1)) { + next_beat.beats += 1; + } else { + next_beat.bars += 1; + next_beat.beats = 1; + } + + next_beat_pos = session->tempo_map().frame_time(next_beat); - frame_skip = (jack_nframes_t) floor ((session->frame_rate() * 60) / (bbt_beat_subdivision * (*i).tempo->beats_per_minute())); - frame_skip_error = ((session->frame_rate() * 60.0f) / (bbt_beat_subdivision * (*i).tempo->beats_per_minute())) - frame_skip; + frame_skip = (jack_nframes_t) floor (frame_skip_error = (session->frame_rate() * 60) / (bbt_beat_subdivision * (*i).tempo->beats_per_minute())); + frame_skip_error -= frame_skip; skip = (uint32_t) (Meter::ticks_per_beat / bbt_beat_subdivision); pos = (*i).frame + frame_skip; accumulated_error = frame_skip_error; - tick += skip; + tick = skip; - for (t = 0; tick < Meter::ticks_per_beat && pos <= next_beat_pos ; pos += frame_skip, tick += skip, ++t) { + for (t = 0; (tick < Meter::ticks_per_beat) && (n < nmarks) && (pos < next_beat_pos) ; pos += frame_skip, tick += skip, ++t) { if (t % magic_accent_number == (magic_accent_number - 1)) { i_am_accented = true; @@ -1247,23 +1219,22 @@ Editor::metric_get_bbt (GtkCustomRulerMark **marks, gdouble lower, gdouble upper } } } - delete zoomed_bbt_points; return n; //return the actual number of marks made, since we might have skipped some fro fractional time signatures } else { /* we're in bar land */ - if (desirable_marks < (uint32_t) (zoomed_bars / 256)) { + if (desirable_marks < (uint32_t) (bars / 256)) { nmarks = 1; *marks = (GtkCustomRulerMark *) g_malloc (sizeof(GtkCustomRulerMark) * nmarks); - snprintf (buf, sizeof(buf), "too many bars... (currently %" PRIu32 ")", zoomed_bars ); + snprintf (buf, sizeof(buf), "too many bars... (currently %" PRIu32 ")", bars ); (*marks)[0].style = GtkCustomRulerMarkMajor; (*marks)[0].label = g_strdup (buf); (*marks)[0].position = ilower; - } else if (desirable_marks < (uint32_t) (nmarks = (gint) (zoomed_bars / 64))) { + } else if (desirable_marks < (uint32_t) (nmarks = (gint) (bars / 64))) { *marks = (GtkCustomRulerMark *) g_malloc (sizeof(GtkCustomRulerMark) * nmarks); - for (n = 0, i = zoomed_bbt_points->begin(); i != zoomed_bbt_points->end() && n < nmarks; i++) { + for (n = 0, i = current_bbt_points->begin(); i != current_bbt_points->end() && n < nmarks; i++) { if ((*i).type == TempoMap::Bar) { if ((*i).bar % 64 == 1) { if ((*i).bar % 256 == 1) { @@ -1283,9 +1254,9 @@ Editor::metric_get_bbt (GtkCustomRulerMark **marks, gdouble lower, gdouble upper } } } - } else if (desirable_marks < (uint32_t) (nmarks = (gint)(zoomed_bars / 16))) { + } else if (desirable_marks < (uint32_t) (nmarks = (gint)(bars / 16))) { *marks = (GtkCustomRulerMark *) g_malloc (sizeof(GtkCustomRulerMark) * nmarks); - for (n = 0, i = zoomed_bbt_points->begin(); i != zoomed_bbt_points->end() && n < nmarks; i++) { + for (n = 0, i = current_bbt_points->begin(); i != current_bbt_points->end() && n < nmarks; i++) { if ((*i).type == TempoMap::Bar) { if ((*i).bar % 16 == 1) { if ((*i).bar % 64 == 1) { @@ -1305,9 +1276,9 @@ Editor::metric_get_bbt (GtkCustomRulerMark **marks, gdouble lower, gdouble upper } } } - } else if (desirable_marks < (uint32_t) (nmarks = (gint)(zoomed_bars / 4))){ + } else if (desirable_marks < (uint32_t) (nmarks = (gint)(bars / 4))){ *marks = (GtkCustomRulerMark *) g_malloc (sizeof(GtkCustomRulerMark) * nmarks); - for (n = 0, i = zoomed_bbt_points->begin(); i != zoomed_bbt_points->end() && n < nmarks; ++i) { + for (n = 0, i = current_bbt_points->begin(); i != current_bbt_points->end() && n < nmarks; ++i) { if ((*i).type == TempoMap::Bar) { if ((*i).bar % 4 == 1) { if ((*i).bar % 16 == 1) { @@ -1328,9 +1299,9 @@ Editor::metric_get_bbt (GtkCustomRulerMark **marks, gdouble lower, gdouble upper } } } else { - nmarks = zoomed_bars; + nmarks = bars; *marks = (GtkCustomRulerMark *) g_malloc (sizeof(GtkCustomRulerMark) * nmarks); - for (n = 0, i = zoomed_bbt_points->begin(); i != zoomed_bbt_points->end() && n < nmarks; i++) { + for (n = 0, i = current_bbt_points->begin(); i != current_bbt_points->end() && n < nmarks; i++) { if ((*i).type == TempoMap::Bar) { if ((*i).bar % 4 == 1) { snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar); @@ -1349,7 +1320,6 @@ Editor::metric_get_bbt (GtkCustomRulerMark **marks, gdouble lower, gdouble upper } } } - delete zoomed_bbt_points; return nmarks; } } @@ -1448,9 +1418,9 @@ Editor::metric_get_minsec (GtkCustomRulerMark **marks, gdouble lower, gdouble up if (lower > (spacer = (jack_nframes_t)(128 * Editor::get_current_zoom ()))) { lower = lower - spacer; } else { - upper = upper + spacer; lower = 0; } + upper = upper + spacer; range = iupper - ilower; if (range < (fr / 50)) { diff --git a/gtk2_ardour/editor_tempodisplay.cc b/gtk2_ardour/editor_tempodisplay.cc index db1ee5e34e..e101da196f 100644 --- a/gtk2_ardour/editor_tempodisplay.cc +++ b/gtk2_ardour/editor_tempodisplay.cc @@ -96,14 +96,36 @@ void Editor::tempo_map_changed (Change ignored) { ENSURE_GUI_THREAD(bind (mem_fun(*this, &Editor::tempo_map_changed), ignored)); - + + BBT_Time previous_beat, next_beat; // the beats previous to the leftmost frame and after the rightmost frame + + session->bbt_time(leftmost_frame, previous_beat); + session->bbt_time(leftmost_frame + current_page_frames(), next_beat); + + if (previous_beat.beats > 1) { + previous_beat.beats -= 1; + } else if (previous_beat.bars > 1) { + previous_beat.bars--; + previous_beat.beats += 1; + } + previous_beat.ticks = 0; + + if (session->tempo_map().meter_at(leftmost_frame + current_page_frames()).beats_per_bar () > next_beat.beats + 1) { + next_beat.beats += 1; + } else { + next_beat.bars += 1; + next_beat.beats = 1; + } + next_beat.ticks = 0; + if (current_bbt_points) { - delete current_bbt_points; + delete current_bbt_points; current_bbt_points = 0; } if (session) { - current_bbt_points = session->tempo_map().get_points (leftmost_frame, leftmost_frame + current_page_frames()); + current_bbt_points = session->tempo_map().get_points (session->tempo_map().frame_time (previous_beat), session->tempo_map().frame_time (next_beat)); + update_tempo_based_rulers (); } else { current_bbt_points = 0; } @@ -114,11 +136,11 @@ Editor::tempo_map_changed (Change ignored) void Editor::redisplay_tempo () { - hide_measures (); if (session && current_bbt_points) { - draw_measures (); - update_tempo_based_rulers (); + Glib::signal_idle().connect (mem_fun (*this, &Editor::lazy_hide_and_draw_measures)); + } else { + hide_measures (); } } @@ -126,7 +148,7 @@ void Editor::hide_measures () { for (TimeLineList::iterator i = used_measure_lines.begin(); i != used_measure_lines.end(); ++i) { - (*i)->hide(); + (*i)->hide(); free_measure_lines.push_back (*i); } used_measure_lines.clear (); @@ -149,6 +171,14 @@ Editor::get_time_line () return line; } +bool +Editor::lazy_hide_and_draw_measures () +{ + hide_measures (); + draw_measures (); + return false; +} + void Editor::draw_measures () { @@ -156,86 +186,63 @@ Editor::draw_measures () return; } - TempoMap::BBTPointList::iterator i = current_bbt_points->begin(); - TempoMap::BBTPoint& p = (*i); + TempoMap::BBTPointList::iterator i; ArdourCanvas::SimpleLine *line; - gdouble xpos, last_xpos; - uint32_t cnt; + gdouble xpos; + double x1, x2, y1, y2, beat_density; + + uint32_t beats = 0; + uint32_t bars = 0; uint32_t color; if (current_bbt_points == 0 || current_bbt_points->empty()) { return; } - cnt = 0; - last_xpos = 0; + track_canvas.get_scroll_region (x1, y1, x2, y2); /* get the first bar spacing */ - gdouble last_beat = DBL_MAX; - gdouble beat_spacing = 0; - - for (i = current_bbt_points->begin(); i != current_bbt_points->end() && beat_spacing == 0; ++i) { - TempoMap::BBTPoint& p = (*i); + i = current_bbt_points->end(); + i--; + bars = (*i).bar - (*current_bbt_points->begin()).bar; + beats = current_bbt_points->size() - bars; - switch (p.type) { - case TempoMap::Bar: - break; + beat_density = (beats * 10.0f) / track_canvas.get_width (); - case TempoMap::Beat: - xpos = frame_to_unit (p.frame); - if (last_beat < xpos) { - beat_spacing = xpos - last_beat; - } - last_beat = xpos; - } - } - - if (beat_spacing < 3.0) { - /* if the lines are too close together, they become useless */ + if (beat_density > 2.0f) { + /* if the lines are too close together, they become useless */ return; } - - double x1, x2, y1, y2; - track_canvas.get_scroll_region (x1, y1, x2, y2); - y2 = 1000000000.0f; - + for (i = current_bbt_points->begin(); i != current_bbt_points->end(); ++i) { - p = (*i); - - switch (p.type) { + switch ((*i).type) { case TempoMap::Bar: break; case TempoMap::Beat: - xpos = frame_to_unit (p.frame); - if (p.beat == 1) { + if ((*i).beat == 1) { color = color_map[cMeasureLineBeat]; } else { color = color_map[cMeasureLineBar]; - /* only draw beat lines if the gaps between beats - are large. - */ + /* only draw beat lines if the gaps between beats are large. */ - if (beat_spacing < 4.0) { - break; + if (beat_density > 0.25) { + break; } } - - if (cnt == 0 || xpos - last_xpos > 4.0) { - line = get_time_line (); - line->property_x1() = xpos; - line->property_x2() = xpos; - line->property_y2() = y2; - line->property_color_rgba() = color; - line->raise_to_top(); - line->show(); - last_xpos = xpos; - ++cnt; - } + + xpos = frame_to_unit ((*i).frame); + line = get_time_line (); + line->property_x1() = xpos; + line->property_x2() = xpos; + line->property_y2() = y2; + line->property_color_rgba() = color; + //line->raise_to_top(); + line->show(); break; } } @@ -244,6 +251,7 @@ Editor::draw_measures () cursor_group->raise_to_top(); time_line_group->lower_to_bottom(); + return; } void diff --git a/gtk2_ardour/midi_streamview.cc b/gtk2_ardour/midi_streamview.cc index 94d2a397cf..d718cc9802 100644 --- a/gtk2_ardour/midi_streamview.cc +++ b/gtk2_ardour/midi_streamview.cc @@ -183,8 +183,8 @@ MidiStreamView::setup_rec_box () assert(region); region->set_position (_trackview.session().transport_frame(), this); rec_regions.push_back (region); - /* catch it if it goes away */ - region->GoingAway.connect (bind (mem_fun (*this, &MidiStreamView::remove_rec_region), region)); + + // rec regions are destroyed in setup_rec_box /* we add the region later */ } @@ -252,6 +252,15 @@ MidiStreamView::setup_rec_box () last_rec_data_frame = 0; /* remove temp regions */ + + for (list >::iterator iter = rec_regions.begin(); iter != rec_regions.end();) { + list >::iterator tmp; + tmp = iter; + ++tmp; + (*iter)->drop_references (); + iter = tmp; + } + rec_regions.clear(); // cerr << "\tclear " << rec_rects.size() << " rec rects\n"; diff --git a/gtk2_ardour/new_session_dialog.cc b/gtk2_ardour/new_session_dialog.cc index 462fb57711..4a533137a6 100644 --- a/gtk2_ardour/new_session_dialog.cc +++ b/gtk2_ardour/new_session_dialog.cc @@ -657,8 +657,8 @@ NewSessionDialog::reset_template() void NewSessionDialog::reset_recent() { - /* Shamelessly ripped from ardour_ui.cc */ - std::vector *sessions; + /* Shamelessly ripped from ardour_ui.cc */ + std::vector *sessions; std::vector::iterator i; RecentSessionsSorter cmp; @@ -677,7 +677,7 @@ NewSessionDialog::reset_recent() for (i = sessions->begin(); i != sessions->end(); ++i) { - std::vector* states; + std::vector* states; std::vector item; std::string fullpath = *(*i); @@ -699,7 +699,7 @@ NewSessionDialog::reset_recent() row[recent_columns.visible_name] = Glib::path_get_basename (fullpath); row[recent_columns.fullpath] = fullpath; - if (states->size() > 1) { + if (states->size()) { /* add the children */ diff --git a/gtk2_ardour/option_editor.cc b/gtk2_ardour/option_editor.cc index b7fc7e746e..013761a86a 100644 --- a/gtk2_ardour/option_editor.cc +++ b/gtk2_ardour/option_editor.cc @@ -152,7 +152,6 @@ OptionEditor::set_session (Session *s) click_emphasis_path_entry.set_sensitive (false); session_raid_entry.set_sensitive (false); - smpte_fps_combo.set_sensitive (false); short_xfade_slider.set_sensitive (false); smpte_offset_negative_button.set_sensitive (false); @@ -165,28 +164,9 @@ OptionEditor::set_session (Session *s) click_path_entry.set_sensitive (true); click_emphasis_path_entry.set_sensitive (true); session_raid_entry.set_sensitive (true); - smpte_fps_combo.set_sensitive (true); short_xfade_slider.set_sensitive (true); smpte_offset_negative_button.set_sensitive (true); - if (!s->smpte_drop_frames) { - // non-drop frames - if (s->smpte_frames_per_second == 24.0) - smpte_fps_combo.set_active_text (_("24 FPS")); - else if (s->smpte_frames_per_second == 25.0) - smpte_fps_combo.set_active_text (_("25 FPS")); - else if (s->smpte_frames_per_second == 30.0) - smpte_fps_combo.set_active_text (_("30 FPS")); - else - smpte_fps_combo.set_active_text (_("???")); - } else { - // drop frames - if (floor(s->smpte_frames_per_second) == 29.0) - smpte_fps_combo.set_active_text (_("30 FPS drop")); - else - smpte_fps_combo.set_active_text (_("???")); - } - smpte_offset_clock.set_session (s); smpte_offset_clock.set (s->smpte_offset (), true); @@ -352,15 +332,6 @@ OptionEditor::setup_sync_options () HBox* hbox; vector dumb; - dumb.clear (); - dumb.push_back (X_("24 FPS")); - dumb.push_back (X_("25 FPS")); - dumb.push_back (X_("30 FPS drop")); - dumb.push_back (X_("30 FPS non-drop")); - - set_popdown_strings (smpte_fps_combo, dumb); - smpte_fps_combo.signal_changed().connect (mem_fun(*this, &OptionEditor::smpte_fps_chosen)); - smpte_offset_clock.set_mode (AudioClock::SMPTE); smpte_offset_clock.ValueChanged.connect (mem_fun(*this, &OptionEditor::smpte_offset_chosen)); @@ -368,19 +339,9 @@ OptionEditor::setup_sync_options () smpte_offset_negative_button.unset_flags (Gtk::CAN_FOCUS); - Label *smpte_fps_label = manage (new Label (_("SMPTE Frames/second"))); Label *smpte_offset_label = manage (new Label (_("SMPTE Offset"))); - smpte_fps_label->set_name("OptionsLabel"); smpte_offset_label->set_name("OptionsLabel"); - hbox = manage (new HBox); - hbox->set_border_width (5); - hbox->set_spacing (10); - hbox->pack_start (*smpte_fps_label, false, false); - hbox->pack_start (smpte_fps_combo, false, false); - - sync_packer.pack_start (*hbox, false, false); - hbox = manage (new HBox); hbox->set_border_width (5); hbox->set_spacing (10); @@ -401,24 +362,6 @@ OptionEditor::smpte_offset_negative_clicked () } } -void -OptionEditor::smpte_fps_chosen () -{ - if (session) { - string str = smpte_fps_combo.get_active_text(); - - if (str == X_("24 FPS")) { - session->set_smpte_type (24.0, false); - } else if (str == X_("25 FPS")) { - session->set_smpte_type (25.0, false); - } else if (str == X_("30 FPS drop")) { - session->set_smpte_type (29.97, true); - } else if (str == X_("30 FPS non-drop")) { - session->set_smpte_type (30.0, false); - } - } -} - void OptionEditor::smpte_offset_chosen() { diff --git a/gtk2_ardour/option_editor.h b/gtk2_ardour/option_editor.h index 1331d3126e..d3235164a6 100644 --- a/gtk2_ardour/option_editor.h +++ b/gtk2_ardour/option_editor.h @@ -100,13 +100,11 @@ class OptionEditor : public Gtk::Dialog Gtk::VBox sync_packer; Gtk::ComboBoxText slave_type_combo; - Gtk::ComboBoxText smpte_fps_combo; AudioClock smpte_offset_clock; Gtk::CheckButton smpte_offset_negative_button; void setup_sync_options (); - void smpte_fps_chosen (); void smpte_offset_chosen (); void smpte_offset_negative_clicked (); diff --git a/gtk2_ardour/region_gain_line.h b/gtk2_ardour/region_gain_line.h index 02340c8bae..3781fe60bb 100644 --- a/gtk2_ardour/region_gain_line.h +++ b/gtk2_ardour/region_gain_line.h @@ -26,16 +26,12 @@ class AudioRegionGainLine : public AutomationLine void remove_point (ControlPoint&); - PBD::ID id() { return _id; } - private: ARDOUR::Session& session; AudioRegionView& rv; UndoAction get_memento(); - - PBD::ID _id; }; diff --git a/gtk2_ardour/route_time_axis.cc b/gtk2_ardour/route_time_axis.cc index 27d1739fd4..470a6d0b88 100644 --- a/gtk2_ardour/route_time_axis.cc +++ b/gtk2_ardour/route_time_axis.cc @@ -118,18 +118,6 @@ RouteTimeAxisView::RouteTimeAxisView (PublicEditor& ed, Session& sess, boost::sh hide_button.add (*(manage (new Image (get_xpm("small_x.xpm"))))); - solo_button->signal_button_press_event().connect (mem_fun (*this, &RouteTimeAxisView::select_me), false); - mute_button->signal_button_press_event().connect (mem_fun (*this, &RouteTimeAxisView::select_me), false); - playlist_button.signal_button_press_event().connect (mem_fun (*this, &RouteTimeAxisView::select_me), false); - automation_button.signal_button_press_event().connect (mem_fun (*this, &RouteTimeAxisView::select_me), false); - size_button.signal_button_press_event().connect (mem_fun (*this, &RouteTimeAxisView::select_me), false); - visual_button.signal_button_press_event().connect (mem_fun (*this, &RouteTimeAxisView::select_me), false); - hide_button.signal_button_press_event().connect (mem_fun (*this, &RouteTimeAxisView::select_me), false); - - solo_button->signal_button_press_event().connect (mem_fun(*this, &RouteUI::solo_press), false); - solo_button->signal_button_release_event().connect (mem_fun(*this, &RouteUI::solo_release), false); - mute_button->signal_button_press_event().connect (mem_fun(*this, &RouteUI::mute_press), false); - mute_button->signal_button_release_event().connect (mem_fun(*this, &RouteUI::mute_release), false); edit_group_button.signal_button_release_event().connect (mem_fun(*this, &RouteTimeAxisView::edit_click), false); playlist_button.signal_clicked().connect (mem_fun(*this, &RouteTimeAxisView::playlist_click)); automation_button.signal_clicked().connect (mem_fun(*this, &RouteTimeAxisView::automation_click)); @@ -137,10 +125,14 @@ RouteTimeAxisView::RouteTimeAxisView (PublicEditor& ed, Session& sess, boost::sh visual_button.signal_clicked().connect (mem_fun(*this, &RouteTimeAxisView::visual_click)); hide_button.signal_clicked().connect (mem_fun(*this, &RouteTimeAxisView::hide_click)); + solo_button->signal_button_press_event().connect (mem_fun(*this, &RouteUI::solo_press), false); + solo_button->signal_button_release_event().connect (mem_fun(*this, &RouteUI::solo_release), false); + mute_button->signal_button_press_event().connect (mem_fun(*this, &RouteUI::mute_press), false); + mute_button->signal_button_release_event().connect (mem_fun(*this, &RouteUI::mute_release), false); + if (is_track()) { rec_enable_button->set_active (false); rec_enable_button->set_name ("TrackRecordEnableButton"); - rec_enable_button->signal_button_press_event().connect (mem_fun (*this, &RouteTimeAxisView::select_me), false); rec_enable_button->signal_button_press_event().connect (mem_fun(*this, &RouteUI::rec_enable_press)); controls_table.attach (*rec_enable_button, 5, 6, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0); ARDOUR_UI::instance()->tooltips().set_tip(*rec_enable_button, _("Record")); @@ -197,6 +189,7 @@ RouteTimeAxisView::RouteTimeAxisView (PublicEditor& ed, Session& sess, boost::sh _route->name_changed.connect (mem_fun(*this, &RouteTimeAxisView::route_name_changed)); _route->solo_safe_changed.connect (mem_fun(*this, &RouteUI::solo_changed)); + if (is_track()) { track()->FreezeChange.connect (mem_fun(*this, &RouteTimeAxisView::map_frozen)); @@ -215,6 +208,7 @@ RouteTimeAxisView::RouteTimeAxisView (PublicEditor& ed, Session& sess, boost::sh editor.ZoomChanged.connect (mem_fun(*this, &RouteTimeAxisView::reset_samples_per_unit)); ColorChanged.connect (mem_fun (*this, &RouteTimeAxisView::color_handler)); + } RouteTimeAxisView::~RouteTimeAxisView () @@ -605,19 +599,19 @@ RouteTimeAxisView::set_height (TrackHeight h) show_name_entry (); hide_name_label (); - mute_button->show_all(); - solo_button->show_all(); + mute_button->show(); + solo_button->show(); if (rec_enable_button) - rec_enable_button->show_all(); + rec_enable_button->show(); - edit_group_button.show_all(); - hide_button.show_all(); - visual_button.show_all(); - size_button.show_all(); - automation_button.show_all(); + edit_group_button.show(); + hide_button.show(); + visual_button.show(); + size_button.show(); + automation_button.show(); if (is_track() && track()->mode() == ARDOUR::Normal) { - playlist_button.show_all(); + playlist_button.show(); } break; @@ -625,10 +619,10 @@ RouteTimeAxisView::set_height (TrackHeight h) show_name_entry (); hide_name_label (); - mute_button->show_all(); - solo_button->show_all(); + mute_button->show(); + solo_button->show(); if (rec_enable_button) - rec_enable_button->show_all(); + rec_enable_button->show(); edit_group_button.hide (); hide_button.hide (); @@ -1193,13 +1187,6 @@ RouteTimeAxisView::color_handler (ColorID id, uint32_t val) } } -bool -RouteTimeAxisView::select_me (GdkEventButton* ev) -{ - editor.get_selection().add (this); - return false; -} - void RouteTimeAxisView::show_all_automation () { diff --git a/gtk2_ardour/route_time_axis.h b/gtk2_ardour/route_time_axis.h index f3643de68b..7489dc84fe 100644 --- a/gtk2_ardour/route_time_axis.h +++ b/gtk2_ardour/route_time_axis.h @@ -214,8 +214,7 @@ protected: void map_frozen (); void color_handler (ColorID, uint32_t); - bool select_me (GdkEventButton*); - + void region_view_added (RegionView*); void add_ghost_to_redirect (RegionView*, AutomationTimeAxisView*); diff --git a/gtk2_ardour/route_ui.cc b/gtk2_ardour/route_ui.cc index 3ede0bc1c7..040dac6e0e 100644 --- a/gtk2_ardour/route_ui.cc +++ b/gtk2_ardour/route_ui.cc @@ -86,10 +86,10 @@ RouteUI::RouteUI (boost::shared_ptr rt, ARDOUR::Session& sess, co update_rec_display (); } - + mute_button->unset_flags (Gtk::CAN_FOCUS); solo_button->unset_flags (Gtk::CAN_FOCUS); - + /* map the current state */ map_frozen (); @@ -100,7 +100,7 @@ RouteUI::~RouteUI() delete mute_menu; } -gint +bool RouteUI::mute_press(GdkEventButton* ev) { if (!ignore_toggle) { @@ -161,7 +161,7 @@ RouteUI::mute_press(GdkEventButton* ev) return true; } -gint +bool RouteUI::mute_release(GdkEventButton* ev) { if (!ignore_toggle) { @@ -175,7 +175,7 @@ RouteUI::mute_release(GdkEventButton* ev) return true; } -gint +bool RouteUI::solo_press(GdkEventButton* ev) { if (!ignore_toggle) { @@ -255,7 +255,7 @@ RouteUI::solo_press(GdkEventButton* ev) return true; } -gint +bool RouteUI::solo_release(GdkEventButton* ev) { if (!ignore_toggle) { @@ -271,7 +271,7 @@ RouteUI::solo_release(GdkEventButton* ev) return true; } -gint +bool RouteUI::rec_enable_press(GdkEventButton* ev) { if (!ignore_toggle && is_track() && rec_enable_button) { diff --git a/gtk2_ardour/route_ui.h b/gtk2_ardour/route_ui.h index b01d7d41cb..00b3e0a313 100644 --- a/gtk2_ardour/route_ui.h +++ b/gtk2_ardour/route_ui.h @@ -90,11 +90,11 @@ class RouteUI : public virtual AxisView XMLNode* get_child_xml_node (const string & childname); - gint mute_press(GdkEventButton*); - gint mute_release(GdkEventButton*); - gint solo_press(GdkEventButton*); - gint solo_release(GdkEventButton*); - gint rec_enable_press(GdkEventButton*); + bool mute_press(GdkEventButton*); + bool mute_release(GdkEventButton*); + bool solo_press(GdkEventButton*); + bool solo_release(GdkEventButton*); + bool rec_enable_press(GdkEventButton*); void solo_changed(void*); void mute_changed(void*); diff --git a/gtk2_ardour/sfdb_ui.cc b/gtk2_ardour/sfdb_ui.cc index b0601704c8..f434463500 100644 --- a/gtk2_ardour/sfdb_ui.cc +++ b/gtk2_ardour/sfdb_ui.cc @@ -36,6 +36,7 @@ #include #include "ardour_ui.h" +#include "editing.h" #include "gui_thread.h" #include "prompter.h" #include "sfdb_ui.h" @@ -197,7 +198,7 @@ SoundFileBox::play_btn_clicked () for (int n = 0; n < sf_info.channels; ++n) { try { - afs = boost::dynamic_pointer_cast (SourceFactory::createReadable (DataType::AUDIO, path+":"+string_compose("%1", n), AudioFileSource::Flag (0))); + afs = boost::dynamic_pointer_cast (SourceFactory::createReadable (DataType::AUDIO, *_session, path+":"+string_compose("%1", n), AudioFileSource::Flag (0))); srclist.push_back(afs); } catch (failed_constructor& err) { @@ -305,6 +306,15 @@ SoundFileBox::field_selected () } } +// this needs to be kept in sync with the ImportMode enum defined in editing.h and editing_syms.h. +static const char *import_mode_strings[] = { + X_("Add to Region list"), + X_("Add to selected Track(s)"), + X_("Add as new Track(s)"), + X_("Add as new Tape Track(s)"), + 0 +}; + SoundFileBrowser::SoundFileBrowser (string title, ARDOUR::Session* s) : ArdourDialog (title, false), chooser (Gtk::FILE_CHOOSER_ACTION_OPEN) @@ -339,13 +349,6 @@ SoundFileChooser::SoundFileChooser (string title, ARDOUR::Session* s) show_all (); } -static const char *import_mode_strings[] = { - X_("Add to Region list"), - X_("Add as new Track(s)"), - X_("Add to selected Track(s)"), - 0 -}; - vector SoundFileOmega::mode_strings; SoundFileOmega::SoundFileOmega (string title, ARDOUR::Session* s) diff --git a/gtk2_ardour/streamview.cc b/gtk2_ardour/streamview.cc index 3a1237f882..3a15d84982 100644 --- a/gtk2_ardour/streamview.cc +++ b/gtk2_ardour/streamview.cc @@ -178,6 +178,8 @@ StreamView::remove_region_view (boost::shared_ptr r) } } +#if 0 +(unused) void StreamView::remove_rec_region (boost::shared_ptr r) { @@ -195,6 +197,7 @@ StreamView::remove_rec_region (boost::shared_ptr r) } } } +#endif void StreamView::undisplay_diskstream () diff --git a/gtk2_ardour/streamview.h b/gtk2_ardour/streamview.h index 6f5e37869a..e965fc7c94 100644 --- a/gtk2_ardour/streamview.h +++ b/gtk2_ardour/streamview.h @@ -109,7 +109,7 @@ protected: virtual void add_region_view_internal (boost::shared_ptr, bool wait_for_waves) = 0; virtual void remove_region_view (boost::shared_ptr ); - void remove_rec_region (boost::shared_ptr); + //void remove_rec_region (boost::shared_ptr); (unused) void display_diskstream (boost::shared_ptr); virtual void undisplay_diskstream (); diff --git a/libs/appleutility/CAAudioFile.cpp b/libs/appleutility/CAAudioFile.cpp new file mode 100644 index 0000000000..e1e39b0ec9 --- /dev/null +++ b/libs/appleutility/CAAudioFile.cpp @@ -0,0 +1,1241 @@ +/* Copyright: © Copyright 2005 Apple Computer, Inc. All rights reserved. + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + ("Apple") in consideration of your agreement to the following terms, and your + use, installation, modification or redistribution of this Apple software + constitutes acceptance of these terms. If you do not agree with these terms, + please do not use, install, modify or redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and subject + to these terms, Apple grants you a personal, non-exclusive license, under AppleÕs + copyrights in this original Apple software (the "Apple Software"), to use, + reproduce, modify and redistribute the Apple Software, with or without + modifications, in source and/or binary forms; provided that if you redistribute + the Apple Software in its entirety and without modifications, you must retain + this notice and the following text and disclaimers in all such redistributions of + the Apple Software. Neither the name, trademarks, service marks or logos of + Apple Computer, Inc. may be used to endorse or promote products derived from the + Apple Software without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or implied, + are granted by Apple herein, including but not limited to any patent rights that + may be infringed by your derivative works or by other works in which the Apple + Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION + OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT + (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/*============================================================================= + CAAudioFile.cpp + +=============================================================================*/ + +#include "CAAudioFile.h" + +#if !CAAF_USE_EXTAUDIOFILE + +#include "CAXException.h" +#include +#include "CAHostTimeBase.h" +#include "CADebugMacros.h" + +#if !defined(__COREAUDIO_USE_FLAT_INCLUDES__) + #include +#else + #include +#endif + +#if DEBUG + //#define VERBOSE_IO 1 + //#define VERBOSE_CONVERTER 1 + //#define VERBOSE_CHANNELMAP 1 + //#define LOG_FUNCTION_ENTRIES 1 + + #if VERBOSE_CHANNELMAP + #include "CAChannelLayouts.h" // this is in Source/Tests/AudioFileTools/Utility + #endif +#endif + +#if LOG_FUNCTION_ENTRIES + class FunctionLogger { + public: + FunctionLogger(const char *name, const char *fmt=NULL, ...) : mName(name) { + Indent(); + printf("-> %s ", name); + if (fmt) { + va_list args; + va_start(args, fmt); + vprintf(fmt, args); + va_end(args); + } + printf("\n"); + ++sIndent; + } + ~FunctionLogger() { + --sIndent; + Indent(); + printf("<- %s\n", mName); + if (sIndent == 0) + printf("\n"); + } + + static void Indent() { + for (int i = sIndent; --i >= 0; ) { + putchar(' '); putchar(' '); + } + } + + const char *mName; + static int sIndent; + }; + int FunctionLogger::sIndent = 0; + + #define LOG_FUNCTION(name, format, ...) FunctionLogger _flog(name, format, ## __VA_ARGS__); +#else + #define LOG_FUNCTION(name, format, foo) +#endif + +static const UInt32 kDefaultIOBufferSizeBytes = 0x10000; + +#if CAAUDIOFILE_PROFILE + #define StartTiming(af, starttime) UInt64 starttime = af->mProfiling ? CAHostTimeBase::GetTheCurrentTime() : 0 + #define ElapsedTime(af, starttime, counter) if (af->mProfiling) counter += (CAHostTimeBase::GetTheCurrentTime() - starttime) +#else + #define StartTiming(af, starttime) + #define ElapsedTime(af, starttime, counter) +#endif + +#define kNoMoreInputRightNow 'nein' + +// _______________________________________________________________________________________ +// +CAAudioFile::CAAudioFile() : + mAudioFile(0), + mUseCache(false), + mFinishingEncoding(false), + mMode(kClosed), + mFileDataOffset(-1), + mFramesToSkipFollowingSeek(0), + + mClientOwnsIOBuffer(false), + mPacketDescs(NULL), + mNumPacketDescs(0), + mConverter(NULL), + mMagicCookie(NULL), + mWriteBufferList(NULL) +#if CAAUDIOFILE_PROFILE + , + mProfiling(false), + mTicksInConverter(0), + mTicksInReadInConverter(0), + mTicksInIO(0), + mInConverter(false) +#endif +{ + mIOBufferList.mBuffers[0].mData = NULL; + mIOBufferList.mBuffers[0].mDataByteSize = 0; + mClientMaxPacketSize = 0; + mIOBufferSizeBytes = kDefaultIOBufferSizeBytes; +} + +// _______________________________________________________________________________________ +// +CAAudioFile::~CAAudioFile() +{ + Close(); +} + +// _______________________________________________________________________________________ +// +void CAAudioFile::Close() +{ + LOG_FUNCTION("CAAudioFile::Close", NULL, NULL); + if (mMode == kClosed) + return; + if (mMode == kWriting) + FlushEncoder(); + CloseConverter(); + if (mAudioFile != 0 && mOwnOpenFile) { + AudioFileClose(mAudioFile); + mAudioFile = 0; + } + if (!mClientOwnsIOBuffer) { + delete[] (Byte *)mIOBufferList.mBuffers[0].mData; + mIOBufferList.mBuffers[0].mData = NULL; + mIOBufferList.mBuffers[0].mDataByteSize = 0; + } + delete[] mPacketDescs; mPacketDescs = NULL; mNumPacketDescs = 0; + delete[] mMagicCookie; mMagicCookie = NULL; + delete mWriteBufferList; mWriteBufferList = NULL; + mMode = kClosed; +} + +// _______________________________________________________________________________________ +// +void CAAudioFile::CloseConverter() +{ + if (mConverter) { +#if VERBOSE_CONVERTER + printf("CAAudioFile %p : CloseConverter\n", this); +#endif + AudioConverterDispose(mConverter); + mConverter = NULL; + } +} + +// ======================================================================================= + +// _______________________________________________________________________________________ +// +void CAAudioFile::Open(const FSRef &fsref) +{ + LOG_FUNCTION("CAAudioFile::Open", "%p", this); + XThrowIf(mMode != kClosed, kExtAudioFileError_InvalidOperationOrder, "file already open"); + mFSRef = fsref; + XThrowIfError(AudioFileOpen(&mFSRef, fsRdPerm, 0, &mAudioFile), "open audio file"); + mOwnOpenFile = true; + mMode = kReading; + GetExistingFileInfo(); +} + +// _______________________________________________________________________________________ +// +void CAAudioFile::Wrap(AudioFileID fileID, bool forWriting) +{ + LOG_FUNCTION("CAAudioFile::Wrap", "%p", this); + XThrowIf(mMode != kClosed, kExtAudioFileError_InvalidOperationOrder, "file already open"); + + mAudioFile = fileID; + mOwnOpenFile = false; + mMode = forWriting ? kPreparingToWrite : kReading; + GetExistingFileInfo(); + if (forWriting) + FileFormatChanged(); +} + +// _______________________________________________________________________________________ +// +void CAAudioFile::CreateNew(const FSRef &parentDir, CFStringRef filename, AudioFileTypeID filetype, const AudioStreamBasicDescription &dataFormat, const AudioChannelLayout *layout) +{ + LOG_FUNCTION("CAAudioFile::CreateNew", "%p", this); + XThrowIf(mMode != kClosed, kExtAudioFileError_InvalidOperationOrder, "file already open"); + + mFileDataFormat = dataFormat; + if (layout) { + mFileChannelLayout = layout; +#if VERBOSE_CHANNELMAP + printf("PrepareNew passed channel layout: %s\n", CAChannelLayouts::ConstantToString(mFileChannelLayout.Tag())); +#endif + } + mMode = kPreparingToCreate; + FileFormatChanged(&parentDir, filename, filetype); +} + +// _______________________________________________________________________________________ +// +// called to create the file -- or update its format/channel layout/properties based on an encoder +// setting change +void CAAudioFile::FileFormatChanged(const FSRef *parentDir, CFStringRef filename, AudioFileTypeID filetype) +{ + LOG_FUNCTION("CAAudioFile::FileFormatChanged", "%p", this); + XThrowIf(mMode != kPreparingToCreate && mMode != kPreparingToWrite, kExtAudioFileError_InvalidOperationOrder, "new file not prepared"); + + UInt32 propertySize; + OSStatus err; + AudioStreamBasicDescription saveFileDataFormat = mFileDataFormat; + +#if VERBOSE_CONVERTER + mFileDataFormat.PrintFormat(stdout, "", "Specified file data format"); +#endif + + // Find out the actual format the converter will produce. This is necessary in + // case the bitrate has forced a lower sample rate, which needs to be set correctly + // in the stream description passed to AudioFileCreate. + if (mConverter != NULL) { + propertySize = sizeof(AudioStreamBasicDescription); + Float64 origSampleRate = mFileDataFormat.mSampleRate; + XThrowIfError(AudioConverterGetProperty(mConverter, kAudioConverterCurrentOutputStreamDescription, &propertySize, &mFileDataFormat), "get audio converter's output stream description"); + // do the same for the channel layout being output by the converter +#if VERBOSE_CONVERTER + mFileDataFormat.PrintFormat(stdout, "", "Converter output"); +#endif + if (fiszero(mFileDataFormat.mSampleRate)) + mFileDataFormat.mSampleRate = origSampleRate; + err = AudioConverterGetPropertyInfo(mConverter, kAudioConverterOutputChannelLayout, &propertySize, NULL); + if (err == noErr && propertySize > 0) { + AudioChannelLayout *layout = static_cast(malloc(propertySize)); + err = AudioConverterGetProperty(mConverter, kAudioConverterOutputChannelLayout, &propertySize, layout); + if (err) { + free(layout); + XThrow(err, "couldn't get audio converter's output channel layout"); + } + mFileChannelLayout = layout; +#if VERBOSE_CHANNELMAP + printf("got new file's channel layout from converter: %s\n", CAChannelLayouts::ConstantToString(mFileChannelLayout.Tag())); +#endif + free(layout); + } + } + + // create the output file + if (mMode == kPreparingToCreate) { + CAStreamBasicDescription newFileDataFormat = mFileDataFormat; + if (fiszero(newFileDataFormat.mSampleRate)) + newFileDataFormat.mSampleRate = 44100; // just make something up for now +#if VERBOSE_CONVERTER + newFileDataFormat.PrintFormat(stdout, "", "Applied to new file"); +#endif + XThrowIfError(AudioFileCreate(parentDir, filename, filetype, &newFileDataFormat, 0, &mFSRef, &mAudioFile), "create audio file"); + mMode = kPreparingToWrite; + mOwnOpenFile = true; + } else if (saveFileDataFormat != mFileDataFormat || fnotequal(saveFileDataFormat.mSampleRate, mFileDataFormat.mSampleRate)) { + // second check must be explicit since operator== on ASBD treats SR of zero as "don't care" + if (fiszero(mFileDataFormat.mSampleRate)) + mFileDataFormat.mSampleRate = mClientDataFormat.mSampleRate; +#if VERBOSE_CONVERTER + mFileDataFormat.PrintFormat(stdout, "", "Applied to new file"); +#endif + XThrowIf(fiszero(mFileDataFormat.mSampleRate), kExtAudioFileError_InvalidDataFormat, "file's sample rate is 0"); + XThrowIfError(AudioFileSetProperty(mAudioFile, kAudioFilePropertyDataFormat, sizeof(AudioStreamBasicDescription), &mFileDataFormat), "couldn't update file's data format"); + } + + UInt32 deferSizeUpdates = 1; + err = AudioFileSetProperty(mAudioFile, kAudioFilePropertyDeferSizeUpdates, sizeof(UInt32), &deferSizeUpdates); + + if (mConverter != NULL) { + // encoder + // get the magic cookie, if any, from the converter + delete[] mMagicCookie; mMagicCookie = NULL; + mMagicCookieSize = 0; + + err = AudioConverterGetPropertyInfo(mConverter, kAudioConverterCompressionMagicCookie, &propertySize, NULL); + + // we can get a noErr result and also a propertySize == 0 + // -- if the file format does support magic cookies, but this file doesn't have one. + if (err == noErr && propertySize > 0) { + mMagicCookie = new Byte[propertySize]; + XThrowIfError(AudioConverterGetProperty(mConverter, kAudioConverterCompressionMagicCookie, &propertySize, mMagicCookie), "get audio converter's magic cookie"); + mMagicCookieSize = propertySize; // the converter lies and tell us the wrong size + // now set the magic cookie on the output file + UInt32 willEatTheCookie = false; + // the converter wants to give us one; will the file take it? + err = AudioFileGetPropertyInfo(mAudioFile, kAudioFilePropertyMagicCookieData, + NULL, &willEatTheCookie); + if (err == noErr && willEatTheCookie) { +#if VERBOSE_CONVERTER + printf("Setting cookie on encoded file\n"); +#endif + XThrowIfError(AudioFileSetProperty(mAudioFile, kAudioFilePropertyMagicCookieData, mMagicCookieSize, mMagicCookie), "set audio file's magic cookie"); + } + } + + // get maximum packet size + propertySize = sizeof(UInt32); + XThrowIfError(AudioConverterGetProperty(mConverter, kAudioConverterPropertyMaximumOutputPacketSize, &propertySize, &mFileMaxPacketSize), "get audio converter's maximum output packet size"); + + AllocateBuffers(true /* okToFail */); + } else { + InitFileMaxPacketSize(); + } + + if (mFileChannelLayout.IsValid() && mFileChannelLayout.NumberChannels() > 2) { + // don't bother tagging mono/stereo files + UInt32 isWritable; + err = AudioFileGetPropertyInfo(mAudioFile, kAudioFilePropertyChannelLayout, NULL, &isWritable); + if (!err && isWritable) { +#if VERBOSE_CHANNELMAP + printf("writing file's channel layout: %s\n", CAChannelLayouts::ConstantToString(mFileChannelLayout.Tag())); +#endif + err = AudioFileSetProperty(mAudioFile, kAudioFilePropertyChannelLayout, + mFileChannelLayout.Size(), &mFileChannelLayout.Layout()); + if (err) + CAXException::Warning("could not set the file's channel layout", err); + } else { +#if VERBOSE_CHANNELMAP + printf("file won't accept a channel layout (write)\n"); +#endif + } + } + + UpdateClientMaxPacketSize(); // also sets mFrame0Offset + mPacketMark = 0; + mFrameMark = 0; +} + +// _______________________________________________________________________________________ +// +void CAAudioFile::InitFileMaxPacketSize() +{ + LOG_FUNCTION("CAAudioFile::InitFileMaxPacketSize", "%p", this); + UInt32 propertySize = sizeof(UInt32); + OSStatus err = AudioFileGetProperty(mAudioFile, kAudioFilePropertyMaximumPacketSize, + &propertySize, &mFileMaxPacketSize); + if (err) { + // workaround for 3361377: not all file formats' maximum packet sizes are supported + if (!mFileDataFormat.IsPCM()) + XThrowIfError(err, "get audio file's maximum packet size"); + mFileMaxPacketSize = mFileDataFormat.mBytesPerFrame; + } + AllocateBuffers(true /* okToFail */); +} + + +// _______________________________________________________________________________________ +// +SInt64 CAAudioFile::FileDataOffset() +{ + if (mFileDataOffset < 0) { + UInt32 propertySize = sizeof(SInt64); + XThrowIfError(AudioFileGetProperty(mAudioFile, kAudioFilePropertyDataOffset, &propertySize, &mFileDataOffset), "couldn't get file's data offset"); + } + return mFileDataOffset; +} + +// _______________________________________________________________________________________ +// +SInt64 CAAudioFile::GetNumberFrames() const +{ + AudioFilePacketTableInfo pti; + UInt32 propertySize = sizeof(pti); + OSStatus err = AudioFileGetProperty(mAudioFile, kAudioFilePropertyPacketTableInfo, &propertySize, &pti); + if (err == noErr) + return pti.mNumberValidFrames; + return mFileDataFormat.mFramesPerPacket * GetNumberPackets() - mFrame0Offset; +} + +// _______________________________________________________________________________________ +// +void CAAudioFile::SetNumberFrames(SInt64 nFrames) +{ + XThrowIf(mFileDataFormat.mFramesPerPacket != 1, kExtAudioFileError_InvalidDataFormat, "SetNumberFrames only supported for PCM"); + XThrowIfError(AudioFileSetProperty(mAudioFile, kAudioFilePropertyAudioDataPacketCount, sizeof(SInt64), &nFrames), "Couldn't set number of packets on audio file"); +} + +// _______________________________________________________________________________________ +// +// call for existing file, NOT new one - from Open() or Wrap() +void CAAudioFile::GetExistingFileInfo() +{ + LOG_FUNCTION("CAAudioFile::GetExistingFileInfo", "%p", this); + UInt32 propertySize; + OSStatus err; + + // get mFileDataFormat + propertySize = sizeof(AudioStreamBasicDescription); + XThrowIfError(AudioFileGetProperty(mAudioFile, kAudioFilePropertyDataFormat, &propertySize, &mFileDataFormat), "get audio file's data format"); + + // get mFileChannelLayout + err = AudioFileGetPropertyInfo(mAudioFile, kAudioFilePropertyChannelLayout, &propertySize, NULL); + if (err == noErr && propertySize > 0) { + AudioChannelLayout *layout = static_cast(malloc(propertySize)); + err = AudioFileGetProperty(mAudioFile, kAudioFilePropertyChannelLayout, &propertySize, layout); + if (err == noErr) { + mFileChannelLayout = layout; +#if VERBOSE_CHANNELMAP + printf("existing file's channel layout: %s\n", CAChannelLayouts::ConstantToString(mFileChannelLayout.Tag())); +#endif + } + free(layout); + XThrowIfError(err, "get audio file's channel layout"); + } + if (mMode != kReading) + return; + +#if 0 + // get mNumberPackets + propertySize = sizeof(mNumberPackets); + XThrowIfError(AudioFileGetProperty(mAudioFile, kAudioFilePropertyAudioDataPacketCount, &propertySize, &mNumberPackets), "get audio file's packet count"); +#if VERBOSE_IO + printf("CAAudioFile::GetExistingFileInfo: %qd packets\n", mNumberPackets); +#endif +#endif + + // get mMagicCookie + err = AudioFileGetPropertyInfo(mAudioFile, kAudioFilePropertyMagicCookieData, &propertySize, NULL); + if (err == noErr && propertySize > 0) { + mMagicCookie = new Byte[propertySize]; + mMagicCookieSize = propertySize; + XThrowIfError(AudioFileGetProperty(mAudioFile, kAudioFilePropertyMagicCookieData, &propertySize, mMagicCookie), "get audio file's magic cookie"); + } + InitFileMaxPacketSize(); + mPacketMark = 0; + mFrameMark = 0; + + UpdateClientMaxPacketSize(); +} + +// ======================================================================================= + +// _______________________________________________________________________________________ +// +void CAAudioFile::SetFileChannelLayout(const CAAudioChannelLayout &layout) +{ + LOG_FUNCTION("CAAudioFile::SetFileChannelLayout", "%p", this); + mFileChannelLayout = layout; +#if VERBOSE_CHANNELMAP + printf("file channel layout set explicitly (%s): %s\n", mMode == kReading ? "read" : "write", CAChannelLayouts::ConstantToString(mFileChannelLayout.Tag())); +#endif + if (mMode != kReading) + FileFormatChanged(); +} + +// _______________________________________________________________________________________ +// +void CAAudioFile::SetClientFormat(const CAStreamBasicDescription &dataFormat, const CAAudioChannelLayout *layout) +{ + LOG_FUNCTION("CAAudioFile::SetClientFormat", "%p", this); + XThrowIf(!dataFormat.IsPCM(), kExtAudioFileError_NonPCMClientFormat, "non-PCM client format on audio file"); + + bool dataFormatChanging = (mClientDataFormat.mFormatID == 0 || mClientDataFormat != dataFormat); + + if (dataFormatChanging) { + CloseConverter(); + if (mWriteBufferList) { + delete mWriteBufferList; + mWriteBufferList = NULL; + } + mClientDataFormat = dataFormat; + } + + if (layout && layout->IsValid()) { + XThrowIf(layout->NumberChannels() != mClientDataFormat.NumberChannels(), kExtAudioFileError_InvalidChannelMap, "inappropriate channel map"); + mClientChannelLayout = *layout; + } + + bool differentLayouts; + if (mClientChannelLayout.IsValid()) { + if (mFileChannelLayout.IsValid()) { + differentLayouts = mClientChannelLayout.Tag() != mFileChannelLayout.Tag(); +#if VERBOSE_CHANNELMAP + printf("two valid layouts, %s\n", differentLayouts ? "different" : "same"); +#endif + } else { + differentLayouts = false; +#if VERBOSE_CHANNELMAP + printf("valid client layout, unknown file layout\n"); +#endif + } + } else { + differentLayouts = false; +#if VERBOSE_CHANNELMAP + if (mFileChannelLayout.IsValid()) + printf("valid file layout, unknown client layout\n"); + else + printf("two invalid layouts\n"); +#endif + } + + if (mClientDataFormat != mFileDataFormat || differentLayouts) { + // We need an AudioConverter. + if (mMode == kReading) { + // file -> client (decode) +//mFileDataFormat.PrintFormat( stdout, "", "File: "); +//mClientDataFormat.PrintFormat(stdout, "", "Client: "); + + if (mConverter == NULL) + XThrowIfError(AudioConverterNew(&mFileDataFormat, &mClientDataFormat, &mConverter), + "create audio converter"); + +#if VERBOSE_CONVERTER + printf("CAAudioFile %p -- created converter\n", this); + CAShow(mConverter); +#endif + // set the magic cookie, if any (for decode) + if (mMagicCookie) + SetConverterProperty(kAudioConverterDecompressionMagicCookie, mMagicCookieSize, mMagicCookie, mFileDataFormat.IsPCM()); + // we get cookies from some AIFF's but the converter barfs on them, + // so we set canFail to true for PCM + + SetConverterChannelLayout(false, mFileChannelLayout); + SetConverterChannelLayout(true, mClientChannelLayout); + + // propagate leading/trailing frame counts + if (mFileDataFormat.mBitsPerChannel == 0) { + UInt32 propertySize; + OSStatus err; + AudioFilePacketTableInfo pti; + propertySize = sizeof(pti); + err = AudioFileGetProperty(mAudioFile, kAudioFilePropertyPacketTableInfo, &propertySize, &pti); + if (err == noErr && (pti.mPrimingFrames > 0 || pti.mRemainderFrames > 0)) { + AudioConverterPrimeInfo primeInfo; + primeInfo.leadingFrames = pti.mPrimingFrames; + primeInfo.trailingFrames = pti.mRemainderFrames; + /* ignore any error. better to play it at all than not. */ + /*err = */AudioConverterSetProperty(mConverter, kAudioConverterPrimeInfo, sizeof(primeInfo), &primeInfo); + //XThrowIfError(err, "couldn't set prime info on converter"); + } + } + } else if (mMode == kPreparingToCreate || mMode == kPreparingToWrite) { + // client -> file (encode) + if (mConverter == NULL) + XThrowIfError(AudioConverterNew(&mClientDataFormat, &mFileDataFormat, &mConverter), "create audio converter"); + mWriteBufferList = CABufferList::New("", mClientDataFormat); + SetConverterChannelLayout(false, mClientChannelLayout); + SetConverterChannelLayout(true, mFileChannelLayout); + if (mMode == kPreparingToWrite) + FileFormatChanged(); + } else + XThrowIfError(kExtAudioFileError_InvalidOperationOrder, "audio file format not yet known"); + } + UpdateClientMaxPacketSize(); +} + +// _______________________________________________________________________________________ +// +OSStatus CAAudioFile::SetConverterProperty( + AudioConverterPropertyID inPropertyID, + UInt32 inPropertyDataSize, + const void* inPropertyData, + bool inCanFail) +{ + OSStatus err = noErr; + //LOG_FUNCTION("ExtAudioFile::SetConverterProperty", "%p %-4.4s", this, (char *)&inPropertyID); + if (inPropertyID == kAudioConverterPropertySettings && *(CFPropertyListRef *)inPropertyData == NULL) + ; + else { + err = AudioConverterSetProperty(mConverter, inPropertyID, inPropertyDataSize, inPropertyData); + if (!inCanFail) { + XThrowIfError(err, "set audio converter property"); + } + } + UpdateClientMaxPacketSize(); + if (mMode == kPreparingToWrite) + FileFormatChanged(); + return err; +} + +// _______________________________________________________________________________________ +// +void CAAudioFile::SetConverterChannelLayout(bool output, const CAAudioChannelLayout &layout) +{ + LOG_FUNCTION("CAAudioFile::SetConverterChannelLayout", "%p", this); + OSStatus err; + + if (layout.IsValid()) { +#if VERBOSE_CHANNELMAP + printf("Setting converter's %s channel layout: %s\n", output ? "output" : "input", + CAChannelLayouts::ConstantToString(mFileChannelLayout.Tag())); +#endif + if (output) { + err = AudioConverterSetProperty(mConverter, kAudioConverterOutputChannelLayout, + layout.Size(), &layout.Layout()); + XThrowIf(err && err != kAudioConverterErr_OperationNotSupported, err, "couldn't set converter's output channel layout"); + } else { + err = AudioConverterSetProperty(mConverter, kAudioConverterInputChannelLayout, + layout.Size(), &layout.Layout()); + XThrowIf(err && err != kAudioConverterErr_OperationNotSupported, err, "couldn't set converter's input channel layout"); + } + if (mMode == kPreparingToWrite) + FileFormatChanged(); + } +} + +// _______________________________________________________________________________________ +// +CFArrayRef CAAudioFile::GetConverterConfig() +{ + CFArrayRef plist; + UInt32 propertySize = sizeof(plist); + XThrowIfError(AudioConverterGetProperty(mConverter, kAudioConverterPropertySettings, &propertySize, &plist), "get converter property settings"); + return plist; +} + +// _______________________________________________________________________________________ +// +void CAAudioFile::UpdateClientMaxPacketSize() +{ + LOG_FUNCTION("CAAudioFile::UpdateClientMaxPacketSize", "%p", this); + mFrame0Offset = 0; + if (mConverter != NULL) { + AudioConverterPropertyID property = (mMode == kReading) ? + kAudioConverterPropertyMaximumOutputPacketSize : + kAudioConverterPropertyMaximumInputPacketSize; + + UInt32 propertySize = sizeof(UInt32); + XThrowIfError(AudioConverterGetProperty(mConverter, property, &propertySize, &mClientMaxPacketSize), + "get audio converter's maximum packet size"); + + if (mFileDataFormat.mBitsPerChannel == 0) { + AudioConverterPrimeInfo primeInfo; + propertySize = sizeof(primeInfo); + OSStatus err = AudioConverterGetProperty(mConverter, kAudioConverterPrimeInfo, &propertySize, &primeInfo); + if (err == noErr) + mFrame0Offset = primeInfo.leadingFrames; +#if VERBOSE_CONVERTER + printf("kAudioConverterPrimeInfo: err = %ld, leadingFrames = %ld\n", err, mFrame0Offset); +#endif + } + } else { + mClientMaxPacketSize = mFileMaxPacketSize; + } +} + +// _______________________________________________________________________________________ +// Allocates: mIOBufferList, mIOBufferSizePackets, mPacketDescs +// Dependent on: mFileMaxPacketSize, mIOBufferSizeBytes +void CAAudioFile::AllocateBuffers(bool okToFail) +{ + LOG_FUNCTION("CAAudioFile::AllocateBuffers", "%p", this); + if (mFileMaxPacketSize == 0) { + if (okToFail) + return; + XThrowIf(true, kExtAudioFileError_MaxPacketSizeUnknown, "file's maximum packet size is 0"); + } + UInt32 bufferSizeBytes = mIOBufferSizeBytes = std::max(mIOBufferSizeBytes, mFileMaxPacketSize); + // must be big enough for at least one maximum size packet + + if (mIOBufferList.mBuffers[0].mDataByteSize != bufferSizeBytes) { + mIOBufferList.mNumberBuffers = 1; + mIOBufferList.mBuffers[0].mNumberChannels = mFileDataFormat.mChannelsPerFrame; + if (!mClientOwnsIOBuffer) { + //printf("reallocating I/O buffer\n"); + delete[] (Byte *)mIOBufferList.mBuffers[0].mData; + mIOBufferList.mBuffers[0].mData = new Byte[bufferSizeBytes]; + } + mIOBufferList.mBuffers[0].mDataByteSize = bufferSizeBytes; + mIOBufferSizePackets = bufferSizeBytes / mFileMaxPacketSize; + } + + UInt32 propertySize = sizeof(UInt32); + UInt32 externallyFramed; + XThrowIfError(AudioFormatGetProperty(kAudioFormatProperty_FormatIsExternallyFramed, + sizeof(AudioStreamBasicDescription), &mFileDataFormat, &propertySize, &externallyFramed), + "is format externally framed"); + if (mNumPacketDescs != (externallyFramed ? mIOBufferSizePackets : 0)) { + delete[] mPacketDescs; + mPacketDescs = NULL; + mNumPacketDescs = 0; + + if (externallyFramed) { + //printf("reallocating packet descs\n"); + mPacketDescs = new AudioStreamPacketDescription[mIOBufferSizePackets]; + mNumPacketDescs = mIOBufferSizePackets; + } + } +} + +// _______________________________________________________________________________________ +// +void CAAudioFile::SetIOBuffer(void *buf) +{ + if (!mClientOwnsIOBuffer) + delete[] (Byte *)mIOBufferList.mBuffers[0].mData; + mIOBufferList.mBuffers[0].mData = buf; + + if (buf == NULL) { + mClientOwnsIOBuffer = false; + SetIOBufferSizeBytes(mIOBufferSizeBytes); + } else { + mClientOwnsIOBuffer = true; + AllocateBuffers(); + } +// printf("CAAudioFile::SetIOBuffer %p: %p, 0x%lx bytes, mClientOwns = %d\n", this, mIOBufferList.mBuffers[0].mData, mIOBufferSizeBytes, mClientOwnsIOBuffer); +} + +// =============================================================================== + +/* +For Tiger: +added kAudioFilePropertyPacketToFrame and kAudioFilePropertyFrameToPacket. +You pass in an AudioFramePacketTranslation struct, with the appropriate field filled in, to AudioFileGetProperty. + + kAudioFilePropertyPacketToFrame = 'pkfr', + // pass a AudioFramePacketTranslation with mPacket filled out and get mFrame back. mFrameOffsetInPacket is ignored. + kAudioFilePropertyFrameToPacket = 'frpk', + // pass a AudioFramePacketTranslation with mFrame filled out and get mPacket and mFrameOffsetInPacket back. + +struct AudioFramePacketTranslation +{ + SInt64 mFrame; + SInt64 mPacket; + UInt32 mFrameOffsetInPacket; +}; +*/ + +SInt64 CAAudioFile::PacketToFrame(SInt64 packet) const +{ + AudioFramePacketTranslation trans; + UInt32 propertySize; + + switch (mFileDataFormat.mFramesPerPacket) { + case 1: + return packet; + case 0: + trans.mPacket = packet; + propertySize = sizeof(trans); + XThrowIfError(AudioFileGetProperty(mAudioFile, kAudioFilePropertyPacketToFrame, &propertySize, &trans), + "packet <-> frame translation unimplemented for format with variable frames/packet"); + return trans.mFrame; + } + return packet * mFileDataFormat.mFramesPerPacket; +} + +SInt64 CAAudioFile::FrameToPacket(SInt64 inFrame) const +{ + AudioFramePacketTranslation trans; + UInt32 propertySize; + + switch (mFileDataFormat.mFramesPerPacket) { + case 1: + return inFrame; + case 0: + trans.mFrame = inFrame; + propertySize = sizeof(trans); + XThrowIfError(AudioFileGetProperty(mAudioFile, kAudioFilePropertyFrameToPacket, &propertySize, &trans), + "packet <-> frame translation unimplemented for format with variable frames/packet"); + return trans.mPacket; + } + return inFrame / mFileDataFormat.mFramesPerPacket; +} + +// _______________________________________________________________________________________ +// + +SInt64 CAAudioFile::Tell() const // frameNumber +{ + return mFrameMark - mFrame0Offset; +} + +void CAAudioFile::SeekToPacket(SInt64 packetNumber) +{ +#if VERBOSE_IO + printf("CAAudioFile::SeekToPacket: %qd\n", packetNumber); +#endif + XThrowIf(mMode != kReading || packetNumber < 0 /*|| packetNumber >= mNumberPackets*/ , kExtAudioFileError_InvalidSeek, "seek to packet in audio file"); + if (mPacketMark == packetNumber) + return; // already there! don't reset converter + mPacketMark = packetNumber; + + mFrameMark = PacketToFrame(packetNumber) - mFrame0Offset; + mFramesToSkipFollowingSeek = 0; + if (mConverter) + // must reset -- if we reached end of stream. converter will no longer work otherwise + AudioConverterReset(mConverter); +} + +/* + Example: AAC, 1024 frames/packet, 2112 frame offset + + 2112 + | + Absolute frames: 0 1024 2048 | 3072 + +---------+---------+--|------+---------+---------+ + Packets: | 0 | 1 | | 2 | 3 | 4 | + +---------+---------+--|------+---------+---------+ + Client frames: -2112 -1088 -64 | 960 SeekToFrame, TellFrame + | + 0 + + * Offset between absolute and client frames is mFrame0Offset. + *** mFrameMark is in client frames *** + + Examples: + clientFrame 0 960 1000 1024 + absoluteFrame 2112 3072 3112 3136 + packet 0 0 0 1 + tempFrameMark* -2112 -2112 -2112 -1088 + mFramesToSkipFollowingSeek 2112 3072 3112 2112 +*/ +void CAAudioFile::Seek(SInt64 clientFrame) +{ + if (clientFrame == mFrameMark) + return; // already there! don't reset converter + + //SInt64 absoluteFrame = clientFrame + mFrame0Offset; + XThrowIf(mMode != kReading || clientFrame < 0 || !mClientDataFormat.IsPCM(), kExtAudioFileError_InvalidSeek, "seek to frame in audio file"); + +#if VERBOSE_IO + SInt64 prevFrameMark = mFrameMark; +#endif + + SInt64 packet; + packet = FrameToPacket(clientFrame); + if (packet < 0) + packet = 0; + SeekToPacket(packet); + // this will have backed up mFrameMark to match the beginning of the packet + mFramesToSkipFollowingSeek = std::max(UInt32(clientFrame - mFrameMark), UInt32(0)); + mFrameMark = clientFrame; + +#if VERBOSE_IO + printf("CAAudioFile::SeekToFrame: frame %qd (from %qd), packet %qd, skip %ld frames\n", mFrameMark, prevFrameMark, packet, mFramesToSkipFollowingSeek); +#endif +} + +// _______________________________________________________________________________________ +// +void CAAudioFile::Read(UInt32 &ioNumPackets, AudioBufferList *ioData) + // May read fewer packets than requested if: + // buffer is not big enough + // file does not contain that many more packets + // Note that eofErr is not fatal, just results in 0 packets returned + // ioData's buffer sizes may be shortened +{ + XThrowIf(mClientMaxPacketSize == 0, kExtAudioFileError_MaxPacketSizeUnknown, "client maximum packet size is 0"); + if (mIOBufferList.mBuffers[0].mData == NULL) { +#if DEBUG + printf("warning: CAAudioFile::AllocateBuffers called from ReadPackets\n"); +#endif + AllocateBuffers(); + } + UInt32 bufferSizeBytes = ioData->mBuffers[0].mDataByteSize; + UInt32 maxNumPackets = bufferSizeBytes / mClientMaxPacketSize; + // older versions of AudioConverterFillComplexBuffer don't do this, so do our own sanity check + UInt32 nPackets = std::min(ioNumPackets, maxNumPackets); + + mMaxPacketsToRead = ~0UL; + + if (mClientDataFormat.mFramesPerPacket == 1) { // PCM or equivalent + while (mFramesToSkipFollowingSeek > 0) { + UInt32 skipFrames = std::min(mFramesToSkipFollowingSeek, maxNumPackets); + UInt32 framesPerPacket; + if ((framesPerPacket=mFileDataFormat.mFramesPerPacket) > 0) + mMaxPacketsToRead = (skipFrames + framesPerPacket - 1) / framesPerPacket; + + if (mConverter == NULL) { + XThrowIfError(ReadInputProc(NULL, &skipFrames, ioData, NULL, this), "read audio file"); + } else { +#if CAAUDIOFILE_PROFILE + mInConverter = true; +#endif + StartTiming(this, fill); + XThrowIfError(AudioConverterFillComplexBuffer(mConverter, ReadInputProc, this, &skipFrames, ioData, NULL), "convert audio packets (pcm read)"); + ElapsedTime(this, fill, mTicksInConverter); +#if CAAUDIOFILE_PROFILE + mInConverter = false; +#endif + } + if (skipFrames == 0) { // hit EOF + ioNumPackets = 0; + return; + } + mFrameMark += skipFrames; +#if VERBOSE_IO + printf("CAAudioFile::ReadPackets: skipped %ld frames\n", skipFrames); +#endif + + mFramesToSkipFollowingSeek -= skipFrames; + + // restore mDataByteSize + for (int i = ioData->mNumberBuffers; --i >= 0 ; ) + ioData->mBuffers[i].mDataByteSize = bufferSizeBytes; + } + } + + if (mFileDataFormat.mFramesPerPacket > 0) + // don't read more packets than we are being asked to produce + mMaxPacketsToRead = nPackets / mFileDataFormat.mFramesPerPacket + 1; + if (mConverter == NULL) { + XThrowIfError(ReadInputProc(NULL, &nPackets, ioData, NULL, this), "read audio file"); + } else { +#if CAAUDIOFILE_PROFILE + mInConverter = true; +#endif + StartTiming(this, fill); + XThrowIfError(AudioConverterFillComplexBuffer(mConverter, ReadInputProc, this, &nPackets, ioData, NULL), "convert audio packets (read)"); + ElapsedTime(this, fill, mTicksInConverter); +#if CAAUDIOFILE_PROFILE + mInConverter = false; +#endif + } + if (mClientDataFormat.mFramesPerPacket == 1) + mFrameMark += nPackets; + + ioNumPackets = nPackets; +} + +// _______________________________________________________________________________________ +// +OSStatus CAAudioFile::ReadInputProc( AudioConverterRef inAudioConverter, + UInt32* ioNumberDataPackets, + AudioBufferList* ioData, + AudioStreamPacketDescription** outDataPacketDescription, + void* inUserData) +{ + CAAudioFile *This = static_cast(inUserData); + +#if 0 + SInt64 remainingPacketsInFile = This->mNumberPackets - This->mPacketMark; + if (remainingPacketsInFile <= 0) { + *ioNumberDataPackets = 0; + ioData->mBuffers[0].mDataByteSize = 0; + if (outDataPacketDescription) + *outDataPacketDescription = This->mPacketDescs; +#if VERBOSE_IO + printf("CAAudioFile::ReadInputProc: EOF\n"); +#endif + return noErr; // not eofErr; EOF is signified by 0 packets/0 bytes + } +#endif + + // determine how much to read + AudioBufferList *readBuffer; + UInt32 readPackets; + if (inAudioConverter != NULL) { + // getting called from converter, need to use our I/O buffer + readBuffer = &This->mIOBufferList; + readPackets = This->mIOBufferSizePackets; + } else { + // getting called directly from ReadPackets, use supplied buffer + if (This->mFileMaxPacketSize == 0) + return kExtAudioFileError_MaxPacketSizeUnknown; + readBuffer = ioData; + readPackets = std::min(*ioNumberDataPackets, readBuffer->mBuffers[0].mDataByteSize / This->mFileMaxPacketSize); + // don't attempt to read more packets than will fit in the buffer + } + // don't try to read past EOF +// if (readPackets > remainingPacketsInFile) +// readPackets = remainingPacketsInFile; + // don't read more packets than necessary to produce the requested amount of converted data + if (readPackets > This->mMaxPacketsToRead) { +#if VERBOSE_IO + printf("CAAudioFile::ReadInputProc: limiting read to %ld packets (from %ld)\n", This->mMaxPacketsToRead, readPackets); +#endif + readPackets = This->mMaxPacketsToRead; + } + + // read + UInt32 bytesRead; + OSStatus err; + + StartTiming(This, read); + StartTiming(This, readinconv); + err = AudioFileReadPackets(This->mAudioFile, This->mUseCache, &bytesRead, This->mPacketDescs, This->mPacketMark, &readPackets, readBuffer->mBuffers[0].mData); +#if CAAUDIOFILE_PROFILE + if (This->mInConverter) ElapsedTime(This, readinconv, This->mTicksInReadInConverter); +#endif + ElapsedTime(This, read, This->mTicksInIO); + + if (err) { + DebugMessageN1("Error %ld from AudioFileReadPackets!!!\n", err); + return err; + } + +#if VERBOSE_IO + printf("CAAudioFile::ReadInputProc: read %ld packets (%qd-%qd), %ld bytes, err %ld\n", readPackets, This->mPacketMark, This->mPacketMark + readPackets, bytesRead, err); +#if VERBOSE_IO >= 2 + if (This->mPacketDescs) { + for (UInt32 i = 0; i < readPackets; ++i) { + printf(" read packet %qd : offset %qd, length %ld\n", This->mPacketMark + i, This->mPacketDescs[i].mStartOffset, This->mPacketDescs[i].mDataByteSize); + } + } + printf(" read buffer:"); CAShowAudioBufferList(readBuffer, 0, 4); +#endif +#endif + if (readPackets == 0) { + *ioNumberDataPackets = 0; + ioData->mBuffers[0].mDataByteSize = 0; + return noErr; + } + + if (outDataPacketDescription) + *outDataPacketDescription = This->mPacketDescs; + ioData->mBuffers[0].mDataByteSize = bytesRead; + ioData->mBuffers[0].mData = readBuffer->mBuffers[0].mData; + + This->mPacketMark += readPackets; + if (This->mClientDataFormat.mFramesPerPacket != 1) { // for PCM client formats we update in Read + // but for non-PCM client format (weird case) we must update here/now + if (This->mFileDataFormat.mFramesPerPacket > 0) + This->mFrameMark += readPackets * This->mFileDataFormat.mFramesPerPacket; + else { + for (UInt32 i = 0; i < readPackets; ++i) + This->mFrameMark += This->mPacketDescs[i].mVariableFramesInPacket; + } + } + *ioNumberDataPackets = readPackets; + return noErr; +} + +// _______________________________________________________________________________________ +// +void CAAudioFile::Write(UInt32 numPackets, const AudioBufferList *data) +{ + if (mIOBufferList.mBuffers[0].mData == NULL) { +#if DEBUG + printf("warning: CAAudioFile::AllocateBuffers called from WritePackets\n"); +#endif + AllocateBuffers(); + } + + if (mMode == kPreparingToWrite) + mMode = kWriting; + else + XThrowIf(mMode != kWriting, kExtAudioFileError_InvalidOperationOrder, "can't write to this file"); + if (mConverter != NULL) { + mWritePackets = numPackets; + mWriteBufferList->SetFrom(data); + WritePacketsFromCallback(WriteInputProc, this); + } else { + StartTiming(this, write); + XThrowIfError(AudioFileWritePackets(mAudioFile, mUseCache, data->mBuffers[0].mDataByteSize, + NULL, mPacketMark, &numPackets, data->mBuffers[0].mData), + "write audio file"); + ElapsedTime(this, write, mTicksInIO); +#if VERBOSE_IO + printf("CAAudioFile::WritePackets: wrote %ld packets at %qd, %ld bytes\n", numPackets, mPacketMark, data->mBuffers[0].mDataByteSize); +#endif + //mNumberPackets = + mPacketMark += numPackets; + if (mFileDataFormat.mFramesPerPacket > 0) + mFrameMark += numPackets * mFileDataFormat.mFramesPerPacket; + // else: shouldn't happen since we're only called when there's no converter + } +} + +// _______________________________________________________________________________________ +// +void CAAudioFile::FlushEncoder() +{ + if (mConverter != NULL) { + mFinishingEncoding = true; + WritePacketsFromCallback(WriteInputProc, this); + mFinishingEncoding = false; + + // get priming info from converter, set it on the file + if (mFileDataFormat.mBitsPerChannel == 0) { + UInt32 propertySize; + OSStatus err; + AudioConverterPrimeInfo primeInfo; + propertySize = sizeof(primeInfo); + + err = AudioConverterGetProperty(mConverter, kAudioConverterPrimeInfo, &propertySize, &primeInfo); + if (err == noErr) { + AudioFilePacketTableInfo pti; + propertySize = sizeof(pti); + err = AudioFileGetProperty(mAudioFile, kAudioFilePropertyPacketTableInfo, &propertySize, &pti); + if (err == noErr) { +//printf("old packet table info: %qd valid, %ld priming, %ld remainder\n", pti.mNumberValidFrames, pti.mPrimingFrames, pti.mRemainderFrames); + UInt64 totalFrames = pti.mNumberValidFrames + pti.mPrimingFrames + pti.mRemainderFrames; + pti.mPrimingFrames = primeInfo.leadingFrames; + pti.mRemainderFrames = primeInfo.trailingFrames; + pti.mNumberValidFrames = totalFrames - pti.mPrimingFrames - pti.mRemainderFrames; +//printf("new packet table info: %qd valid, %ld priming, %ld remainder\n", pti.mNumberValidFrames, pti.mPrimingFrames, pti.mRemainderFrames); + XThrowIfError(AudioFileSetProperty(mAudioFile, kAudioFilePropertyPacketTableInfo, sizeof(pti), &pti), "couldn't set packet table info on audio file"); + } + } + } + } +} + +// _______________________________________________________________________________________ +// +OSStatus CAAudioFile::WriteInputProc( AudioConverterRef /*inAudioConverter*/, + UInt32 * ioNumberDataPackets, + AudioBufferList* ioData, + AudioStreamPacketDescription ** outDataPacketDescription, + void* inUserData) +{ + CAAudioFile *This = static_cast(inUserData); + if (This->mFinishingEncoding) { + *ioNumberDataPackets = 0; + ioData->mBuffers[0].mDataByteSize = 0; + ioData->mBuffers[0].mData = NULL; + if (outDataPacketDescription) + *outDataPacketDescription = NULL; + return noErr; + } + UInt32 numPackets = This->mWritePackets; + if (numPackets == 0) { + return kNoMoreInputRightNow; + } + This->mWriteBufferList->ToAudioBufferList(ioData); + This->mWriteBufferList->BytesConsumed(numPackets * This->mClientDataFormat.mBytesPerFrame); + *ioNumberDataPackets = numPackets; + if (outDataPacketDescription) + *outDataPacketDescription = NULL; + This->mWritePackets -= numPackets; + return noErr; +} + +// _______________________________________________________________________________________ +// +#if VERBOSE_IO +static void hexdump(const void *addr, long len) +{ + const Byte *p = (Byte *)addr; + UInt32 offset = 0; + + if (len > 0x400) len = 0x400; + + while (len > 0) { + int n = len > 16 ? 16 : len; + printf("%08lX: ", offset); + for (int i = 0; i < 16; ++i) + if (i < n) + printf("%02X ", p[i]); + else printf(" "); + for (int i = 0; i < 16; ++i) + if (i < n) + putchar(p[i] >= ' ' && p[i] < 127 ? p[i] : '.'); + else putchar(' '); + putchar('\n'); + p += 16; + len -= 16; + offset += 16; + } +} +#endif + +// _______________________________________________________________________________________ +// +void CAAudioFile::WritePacketsFromCallback( + AudioConverterComplexInputDataProc inInputDataProc, + void * inInputDataProcUserData) +{ + while (true) { + // keep writing until we exhaust the input (temporary stop), or produce no output (EOF) + UInt32 numEncodedPackets = mIOBufferSizePackets; + mIOBufferList.mBuffers[0].mDataByteSize = mIOBufferSizeBytes; +#if CAAUDIOFILE_PROFILE + mInConverter = true; +#endif + StartTiming(this, fill); + OSStatus err = AudioConverterFillComplexBuffer(mConverter, inInputDataProc, inInputDataProcUserData, + &numEncodedPackets, &mIOBufferList, mPacketDescs); + ElapsedTime(this, fill, mTicksInConverter); +#if CAAUDIOFILE_PROFILE + mInConverter = false; +#endif + XThrowIf(err != 0 && err != kNoMoreInputRightNow, err, "convert audio packets (write)"); + if (numEncodedPackets == 0) + break; + Byte *buf = (Byte *)mIOBufferList.mBuffers[0].mData; +#if VERBOSE_IO + printf("CAAudioFile::WritePacketsFromCallback: wrote %ld packets, %ld bytes\n", numEncodedPackets, mIOBufferList.mBuffers[0].mDataByteSize); + if (mPacketDescs) { + for (UInt32 i = 0; i < numEncodedPackets; ++i) { + printf(" write packet %qd : offset %qd, length %ld\n", mPacketMark + i, mPacketDescs[i].mStartOffset, mPacketDescs[i].mDataByteSize); +#if VERBOSE_IO >= 2 + hexdump(buf + mPacketDescs[i].mStartOffset, mPacketDescs[i].mDataByteSize); +#endif + } + } +#endif + StartTiming(this, write); + XThrowIfError(AudioFileWritePackets(mAudioFile, mUseCache, mIOBufferList.mBuffers[0].mDataByteSize, mPacketDescs, mPacketMark, &numEncodedPackets, buf), "write audio file"); + ElapsedTime(this, write, mTicksInIO); + mPacketMark += numEncodedPackets; + //mNumberPackets += numEncodedPackets; + if (mFileDataFormat.mFramesPerPacket > 0) + mFrameMark += numEncodedPackets * mFileDataFormat.mFramesPerPacket; + else { + for (UInt32 i = 0; i < numEncodedPackets; ++i) + mFrameMark += mPacketDescs[i].mVariableFramesInPacket; + } + if (err == kNoMoreInputRightNow) + break; + } +} + +#endif // !CAAF_USE_EXTAUDIOFILE diff --git a/libs/appleutility/CAAudioFile.h b/libs/appleutility/CAAudioFile.h new file mode 100644 index 0000000000..2cfb4f3031 --- /dev/null +++ b/libs/appleutility/CAAudioFile.h @@ -0,0 +1,439 @@ +/* Copyright: © Copyright 2005 Apple Computer, Inc. All rights reserved. + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + ("Apple") in consideration of your agreement to the following terms, and your + use, installation, modification or redistribution of this Apple software + constitutes acceptance of these terms. If you do not agree with these terms, + please do not use, install, modify or redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and subject + to these terms, Apple grants you a personal, non-exclusive license, under AppleÕs + copyrights in this original Apple software (the "Apple Software"), to use, + reproduce, modify and redistribute the Apple Software, with or without + modifications, in source and/or binary forms; provided that if you redistribute + the Apple Software in its entirety and without modifications, you must retain + this notice and the following text and disclaimers in all such redistributions of + the Apple Software. Neither the name, trademarks, service marks or logos of + Apple Computer, Inc. may be used to endorse or promote products derived from the + Apple Software without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or implied, + are granted by Apple herein, including but not limited to any patent rights that + may be infringed by your derivative works or by other works in which the Apple + Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION + OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT + (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/*============================================================================= + CAAudioFile.h + +=============================================================================*/ + +#ifndef __CAAudioFile_h__ +#define __CAAudioFile_h__ + +#include + +#if !defined(__COREAUDIO_USE_FLAT_INCLUDES__) + #include +#else + #include +#endif + +#include "CAStreamBasicDescription.h" +#include "CABufferList.h" +#include "CAAudioChannelLayout.h" +#include "CAXException.h" +#include "CAMath.h" + +#ifndef CAAF_USE_EXTAUDIOFILE +// option: use AudioToolbox/ExtAudioFile.h? Only available on Tiger. + #if MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_3 + // we are building software that must be deployable on Panther or earlier + #define CAAF_USE_EXTAUDIOFILE 0 + #else + // else we require Tiger and can use the API + #define CAAF_USE_EXTAUDIOFILE 1 + #endif +#endif + +#ifndef MAC_OS_X_VERSION_10_4 + // we have pre-Tiger headers; add our own declarations + typedef UInt32 AudioFileTypeID; + enum { + kExtAudioFileError_InvalidProperty = -66561, + kExtAudioFileError_InvalidPropertySize = -66562, + kExtAudioFileError_NonPCMClientFormat = -66563, + kExtAudioFileError_InvalidChannelMap = -66564, // number of channels doesn't match format + kExtAudioFileError_InvalidOperationOrder = -66565, + kExtAudioFileError_InvalidDataFormat = -66566, + kExtAudioFileError_MaxPacketSizeUnknown = -66567, + kExtAudioFileError_InvalidSeek = -66568, // writing, or offset out of bounds + kExtAudioFileError_AsyncWriteTooLarge = -66569, + kExtAudioFileError_AsyncWriteBufferOverflow = -66570 // an async write could not be completed in time + }; +#else + #if !defined(__COREAUDIO_USE_FLAT_INCLUDES__) + #include + #else + #include "ExtendedAudioFile.h" + #endif +#endif + +// _______________________________________________________________________________________ +// Wrapper class for an AudioFile, supporting encode/decode to/from a PCM client format +class CAAudioFile { +public: + // implementation-independent helpers + void Open(const char *filePath) { + FSRef fsref; + XThrowIfError(FSPathMakeRef((UInt8 *)filePath, &fsref, NULL), "locate audio file"); + Open(fsref); + } + + bool HasConverter() const { return GetConverter() != NULL; } + + double GetDurationSeconds() { + double sr = GetFileDataFormat().mSampleRate; + return fnonzero(sr) ? GetNumberFrames() / sr : 0.; + } + // will be 0 if the file's frames/packet is 0 (variable) + // or the file's sample rate is 0 (unknown) + +#if CAAF_USE_EXTAUDIOFILE +public: + CAAudioFile() : mExtAF(NULL) { } + virtual ~CAAudioFile() { if (mExtAF) Close(); } + + void Open(const FSRef &fsref) { + // open an existing file + XThrowIfError(ExtAudioFileOpen(&fsref, &mExtAF), "ExtAudioFileOpen failed"); + } + + void CreateNew(const FSRef &inParentDir, CFStringRef inFileName, AudioFileTypeID inFileType, const AudioStreamBasicDescription &inStreamDesc, const AudioChannelLayout *inChannelLayout=NULL) { + XThrowIfError(ExtAudioFileCreateNew(&inParentDir, inFileName, inFileType, &inStreamDesc, inChannelLayout, &mExtAF), "ExtAudioFileCreateNew failed"); + } + + void Wrap(AudioFileID fileID, bool forWriting) { + // use this to wrap an AudioFileID opened externally + XThrowIfError(ExtAudioFileWrapAudioFileID(fileID, forWriting, &mExtAF), "ExtAudioFileWrapAudioFileID failed"); + } + + void Close() { + XThrowIfError(ExtAudioFileDispose(mExtAF), "ExtAudioFileClose failed"); + mExtAF = NULL; + } + + const CAStreamBasicDescription &GetFileDataFormat() { + UInt32 size = sizeof(mFileDataFormat); + XThrowIfError(ExtAudioFileGetProperty(mExtAF, kExtAudioFileProperty_FileDataFormat, &size, &mFileDataFormat), "Couldn't get file's data format"); + return mFileDataFormat; + } + + const CAAudioChannelLayout & GetFileChannelLayout() { + return FetchChannelLayout(mFileChannelLayout, kExtAudioFileProperty_FileChannelLayout); + } + + void SetFileChannelLayout(const CAAudioChannelLayout &layout) { + XThrowIfError(ExtAudioFileSetProperty(mExtAF, kExtAudioFileProperty_FileChannelLayout, layout.Size(), &layout.Layout()), "Couldn't set file's channel layout"); + mFileChannelLayout = layout; + } + + const CAStreamBasicDescription &GetClientDataFormat() { + UInt32 size = sizeof(mClientDataFormat); + XThrowIfError(ExtAudioFileGetProperty(mExtAF, kExtAudioFileProperty_ClientDataFormat, &size, &mClientDataFormat), "Couldn't get client data format"); + return mClientDataFormat; + } + + const CAAudioChannelLayout & GetClientChannelLayout() { + return FetchChannelLayout(mClientChannelLayout, kExtAudioFileProperty_ClientChannelLayout); + } + + void SetClientFormat(const CAStreamBasicDescription &dataFormat, const CAAudioChannelLayout *layout=NULL) { + XThrowIfError(ExtAudioFileSetProperty(mExtAF, kExtAudioFileProperty_ClientDataFormat, sizeof(dataFormat), &dataFormat), "Couldn't set client format"); + if (layout) + SetClientChannelLayout(*layout); + } + + void SetClientChannelLayout(const CAAudioChannelLayout &layout) { + XThrowIfError(ExtAudioFileSetProperty(mExtAF, kExtAudioFileProperty_ClientChannelLayout, layout.Size(), &layout.Layout()), "Couldn't set client channel layout"); + } + + AudioConverterRef GetConverter() const { + UInt32 size = sizeof(AudioConverterRef); + AudioConverterRef converter; + XThrowIfError(ExtAudioFileGetProperty(mExtAF, kExtAudioFileProperty_AudioConverter, &size, &converter), "Couldn't get file's AudioConverter"); + return converter; + } + + OSStatus SetConverterProperty(AudioConverterPropertyID inPropertyID, UInt32 inPropertyDataSize, const void *inPropertyData, bool inCanFail=false) + { + OSStatus err = AudioConverterSetProperty(GetConverter(), inPropertyID, inPropertyDataSize, inPropertyData); + if (!inCanFail) + XThrowIfError(err, "Couldn't set audio converter property"); + if (!err) { + // must tell the file that we have changed the converter; a NULL converter config is sufficient + CFPropertyListRef config = NULL; + XThrowIfError(ExtAudioFileSetProperty(mExtAF, kExtAudioFileProperty_ConverterConfig, sizeof(CFPropertyListRef), &config), "couldn't signal the file that the converter has changed"); + } + return err; + } + + SInt64 GetNumberFrames() { + SInt64 length; + UInt32 size = sizeof(SInt64); + XThrowIfError(ExtAudioFileGetProperty(mExtAF, kExtAudioFileProperty_FileLengthFrames, &size, &length), "Couldn't get file's length"); + return length; + } + + void SetNumberFrames(SInt64 length) { + XThrowIfError(ExtAudioFileSetProperty(mExtAF, kExtAudioFileProperty_FileLengthFrames, sizeof(SInt64), &length), "Couldn't set file's length"); + } + + void Seek(SInt64 pos) { + XThrowIfError(ExtAudioFileSeek(mExtAF, pos), "Couldn't seek in audio file"); + } + + SInt64 Tell() { + SInt64 pos; + XThrowIfError(ExtAudioFileTell(mExtAF, &pos), "Couldn't get file's mark"); + return pos; + } + + void Read(UInt32 &ioFrames, AudioBufferList *ioData) { + XThrowIfError(ExtAudioFileRead(mExtAF, &ioFrames, ioData), "Couldn't read audio file"); + } + + void Write(UInt32 inFrames, const AudioBufferList *inData) { + XThrowIfError(ExtAudioFileWrite(mExtAF, inFrames, inData), "Couldn't write audio file"); + } + + void SetIOBufferSizeBytes(UInt32 bufferSizeBytes) { + XThrowIfError(ExtAudioFileSetProperty(mExtAF, kExtAudioFileProperty_IOBufferSizeBytes, sizeof(UInt32), &bufferSizeBytes), "Couldn't set audio file's I/O buffer size"); + } + +private: + const CAAudioChannelLayout & FetchChannelLayout(CAAudioChannelLayout &layoutObj, ExtAudioFilePropertyID propID) { + UInt32 size; + XThrowIfError(ExtAudioFileGetPropertyInfo(mExtAF, propID, &size, NULL), "Couldn't get info about channel layout"); + AudioChannelLayout *layout = (AudioChannelLayout *)malloc(size); + OSStatus err = ExtAudioFileGetProperty(mExtAF, propID, &size, layout); + if (err) { + free(layout); + XThrowIfError(err, "Couldn't get channel layout"); + } + layoutObj = layout; + free(layout); + return layoutObj; + } + + +private: + ExtAudioFileRef mExtAF; + + CAStreamBasicDescription mFileDataFormat; + CAAudioChannelLayout mFileChannelLayout; + + CAStreamBasicDescription mClientDataFormat; + CAAudioChannelLayout mClientChannelLayout; +#endif + +#if !CAAF_USE_EXTAUDIOFILE + CAAudioFile(); + virtual ~CAAudioFile(); + + // --- second-stage initializers --- + // Use exactly one of the following: + // - Open + // - PrepareNew followed by Create + // - Wrap + + void Open(const FSRef &fsref); + // open an existing file + + void CreateNew(const FSRef &inParentDir, CFStringRef inFileName, AudioFileTypeID inFileType, const AudioStreamBasicDescription &inStreamDesc, const AudioChannelLayout *inChannelLayout=NULL); + + void Wrap(AudioFileID fileID, bool forWriting); + // use this to wrap an AudioFileID opened externally + + // --- + + void Close(); + // In case you want to close the file before the destructor executes + + // --- Data formats --- + + // Allow specifying the file's channel layout. Must be called before SetClientFormat. + // When writing, the specified channel layout is written to the file (if the file format supports + // the channel layout). When reading, the specified layout overrides the one read from the file, + // if any. + void SetFileChannelLayout(const CAAudioChannelLayout &layout); + + // This specifies the data format which the client will use for reading/writing the file, + // which may be different from the file's format. An AudioConverter is created if necessary. + // The client format must be linear PCM. + void SetClientFormat(const CAStreamBasicDescription &dataFormat, const CAAudioChannelLayout *layout=NULL); + void SetClientDataFormat(const CAStreamBasicDescription &dataFormat) { SetClientFormat(dataFormat, NULL); } + void SetClientChannelLayout(const CAAudioChannelLayout &layout) { SetClientFormat(mClientDataFormat, &layout); } + + // Wrapping the underlying converter, if there is one + OSStatus SetConverterProperty(AudioConverterPropertyID inPropertyID, + UInt32 inPropertyDataSize, + const void * inPropertyData, + bool inCanFail = false); + void SetConverterConfig(CFArrayRef config) { + SetConverterProperty(kAudioConverterPropertySettings, sizeof(config), &config); } + CFArrayRef GetConverterConfig(); + + // --- I/O --- + // All I/O is sequential, but you can seek to an arbitrary position when reading. + // SeekToPacket and TellPacket's packet numbers are in the file's data format, not the client's. + // However, ReadPackets/WritePackets use packet counts in the client data format. + + void Read(UInt32 &ioNumFrames, AudioBufferList *ioData); + void Write(UInt32 numFrames, const AudioBufferList *data); + + // These can fail for files without a constant mFramesPerPacket + void Seek(SInt64 frameNumber); + SInt64 Tell() const; // frameNumber + + // --- Accessors --- + // note: client parameters only valid if SetClientFormat has been called + AudioFileID GetAudioFileID() const { return mAudioFile; } + const CAStreamBasicDescription &GetFileDataFormat() const { return mFileDataFormat; } + const CAStreamBasicDescription &GetClientDataFormat() const { return mClientDataFormat; } + const CAAudioChannelLayout & GetFileChannelLayout() const { return mFileChannelLayout; } + const CAAudioChannelLayout & GetClientChannelLayout() const { return mClientChannelLayout; } + AudioConverterRef GetConverter() const { return mConverter; } + + UInt32 GetFileMaxPacketSize() const { return mFileMaxPacketSize; } + UInt32 GetClientMaxPacketSize() const { return mClientMaxPacketSize; } + SInt64 GetNumberPackets() const { + SInt64 npackets; + UInt32 propertySize = sizeof(npackets); + XThrowIfError(AudioFileGetProperty(mAudioFile, kAudioFilePropertyAudioDataPacketCount, &propertySize, &npackets), "get audio file's packet count"); + return npackets; + } + SInt64 GetNumberFrames() const; + // will be 0 if the file's frames/packet is 0 (variable) + void SetNumberFrames(SInt64 length); // should only be set on a PCM file + + // --- Tunable performance parameters --- + void SetUseCache(bool b) { mUseCache = b; } + void SetIOBufferSizeBytes(UInt32 bufferSizeBytes) { mIOBufferSizeBytes = bufferSizeBytes; } + UInt32 GetIOBufferSizeBytes() { return mIOBufferSizeBytes; } + void * GetIOBuffer() { return mIOBufferList.mBuffers[0].mData; } + void SetIOBuffer(void *buf); + + // -- Profiling --- +#if CAAUDIOFILE_PROFILE + void EnableProfiling(bool b) { mProfiling = b; } + UInt64 TicksInConverter() const { return (mTicksInConverter > 0) ? (mTicksInConverter - mTicksInReadInConverter) : 0; } + UInt64 TicksInIO() const { return mTicksInIO; } +#endif + +// _______________________________________________________________________________________ +private: + SInt64 FileDataOffset(); + void SeekToPacket(SInt64 packetNumber); + SInt64 TellPacket() const { return mPacketMark; } // will be imprecise if SeekToFrame was called + + void SetConverterChannelLayout(bool output, const CAAudioChannelLayout &layout); + void WritePacketsFromCallback( + AudioConverterComplexInputDataProc inInputDataProc, + void * inInputDataProcUserData); + // will use I/O buffer size + void InitFileMaxPacketSize(); + void FileFormatChanged(const FSRef *parentDir=0, CFStringRef filename=0, AudioFileTypeID filetype=0); + + void GetExistingFileInfo(); + void FlushEncoder(); + void CloseConverter(); + void UpdateClientMaxPacketSize(); + void AllocateBuffers(bool okToFail=false); + SInt64 PacketToFrame(SInt64 packet) const; + SInt64 FrameToPacket(SInt64 inFrame) const; + + static OSStatus ReadInputProc( AudioConverterRef inAudioConverter, + UInt32* ioNumberDataPackets, + AudioBufferList* ioData, + AudioStreamPacketDescription** outDataPacketDescription, + void* inUserData); + + static OSStatus WriteInputProc( AudioConverterRef inAudioConverter, + UInt32* ioNumberDataPackets, + AudioBufferList* ioData, + AudioStreamPacketDescription** outDataPacketDescription, + void* inUserData); +// _______________________________________________________________________________________ +private: + + // the file + FSRef mFSRef; + AudioFileID mAudioFile; + bool mOwnOpenFile; + bool mUseCache; + bool mFinishingEncoding; + enum { kClosed, kReading, kPreparingToCreate, kPreparingToWrite, kWriting } mMode; + +// SInt64 mNumberPackets; // in file's format + SInt64 mFileDataOffset; + SInt64 mPacketMark; // in file's format + SInt64 mFrameMark; // this may be offset from the start of the file + // by the codec's latency; i.e. our frame 0 could + // lie at frame 2112 of a decoded AAC file + SInt32 mFrame0Offset; + UInt32 mFramesToSkipFollowingSeek; + + // buffers + UInt32 mIOBufferSizeBytes; + UInt32 mIOBufferSizePackets; + AudioBufferList mIOBufferList; // only one buffer -- USE ACCESSOR so it can be lazily initialized + bool mClientOwnsIOBuffer; + AudioStreamPacketDescription *mPacketDescs; + UInt32 mNumPacketDescs; + + // formats/conversion + AudioConverterRef mConverter; + CAStreamBasicDescription mFileDataFormat; + CAStreamBasicDescription mClientDataFormat; + CAAudioChannelLayout mFileChannelLayout; + CAAudioChannelLayout mClientChannelLayout; + UInt32 mFileMaxPacketSize; + UInt32 mClientMaxPacketSize; + + // cookie + Byte * mMagicCookie; + UInt32 mMagicCookieSize; + + // for ReadPackets + UInt32 mMaxPacketsToRead; + + // for WritePackets + UInt32 mWritePackets; + CABufferList * mWriteBufferList; + +#if CAAUDIOFILE_PROFILE + // performance + bool mProfiling; + UInt64 mTicksInConverter; + UInt64 mTicksInReadInConverter; + UInt64 mTicksInIO; + bool mInConverter; +#endif + +#endif // CAAF_USE_EXTAUDIOFILE +}; + +#endif // __CAAudioFile_h__ diff --git a/libs/appleutility/CABufferList.cpp b/libs/appleutility/CABufferList.cpp new file mode 100644 index 0000000000..81298f918a --- /dev/null +++ b/libs/appleutility/CABufferList.cpp @@ -0,0 +1,179 @@ +/* Copyright: © Copyright 2005 Apple Computer, Inc. All rights reserved. + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + ("Apple") in consideration of your agreement to the following terms, and your + use, installation, modification or redistribution of this Apple software + constitutes acceptance of these terms. If you do not agree with these terms, + please do not use, install, modify or redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and subject + to these terms, Apple grants you a personal, non-exclusive license, under AppleÕs + copyrights in this original Apple software (the "Apple Software"), to use, + reproduce, modify and redistribute the Apple Software, with or without + modifications, in source and/or binary forms; provided that if you redistribute + the Apple Software in its entirety and without modifications, you must retain + this notice and the following text and disclaimers in all such redistributions of + the Apple Software. Neither the name, trademarks, service marks or logos of + Apple Computer, Inc. may be used to endorse or promote products derived from the + Apple Software without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or implied, + are granted by Apple herein, including but not limited to any patent rights that + may be infringed by your derivative works or by other works in which the Apple + Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION + OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT + (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/*============================================================================= + CABufferList.cpp + +=============================================================================*/ + +#include "CABufferList.h" +#if !defined(__COREAUDIO_USE_FLAT_INCLUDES__) + #include +#else + #include +#endif + +void CABufferList::AllocateBuffers(UInt32 nBytes) +{ + if (nBytes <= GetNumBytes()) return; + + if (mNumberBuffers > 1) + // align successive buffers for Altivec and to take alternating + // cache line hits by spacing them by odd multiples of 16 + nBytes = (nBytes + (0x10 - (nBytes & 0xF))) | 0x10; + UInt32 memorySize = nBytes * mNumberBuffers; + Byte *newMemory = new Byte[memorySize], *p = newMemory; + memset(newMemory, 0, memorySize); // get page faults now, not later + + AudioBuffer *buf = mBuffers; + for (UInt32 i = mNumberBuffers; i--; ++buf) { + if (buf->mData != NULL && buf->mDataByteSize > 0) + // preserve existing buffer contents + memcpy(p, buf->mData, buf->mDataByteSize); + buf->mDataByteSize = nBytes; + buf->mData = p; + p += nBytes; + } + Byte *oldMemory = mBufferMemory; + mBufferMemory = newMemory; + delete[] oldMemory; +} + +void CABufferList::AllocateBuffersAndCopyFrom(UInt32 nBytes, CABufferList *inSrcList, CABufferList *inSetPtrList) +{ + if (mNumberBuffers != inSrcList->mNumberBuffers) return; + if (mNumberBuffers != inSetPtrList->mNumberBuffers) return; + if (nBytes <= GetNumBytes()) { + CopyAllFrom(inSrcList, inSetPtrList); + return; + } + inSetPtrList->VerifyNotTrashingOwnedBuffer(); + UInt32 fromByteSize = inSrcList->GetNumBytes(); + + if (mNumberBuffers > 1) + // align successive buffers for Altivec and to take alternating + // cache line hits by spacing them by odd multiples of 16 + nBytes = (nBytes + (0x10 - (nBytes & 0xF))) | 0x10; + UInt32 memorySize = nBytes * mNumberBuffers; + Byte *newMemory = new Byte[memorySize], *p = newMemory; + memset(newMemory, 0, memorySize); // make buffer "hot" + + AudioBuffer *buf = mBuffers; + AudioBuffer *ptrBuf = inSetPtrList->mBuffers; + AudioBuffer *srcBuf = inSrcList->mBuffers; + for (UInt32 i = mNumberBuffers; i--; ++buf, ++ptrBuf, ++srcBuf) { + if (srcBuf->mData != NULL && srcBuf->mDataByteSize > 0) + // preserve existing buffer contents + memmove(p, srcBuf->mData, srcBuf->mDataByteSize); + buf->mDataByteSize = nBytes; + buf->mData = p; + ptrBuf->mDataByteSize = srcBuf->mDataByteSize; + ptrBuf->mData = p; + p += nBytes; + } + Byte *oldMemory = mBufferMemory; + mBufferMemory = newMemory; + if (inSrcList != inSetPtrList) + inSrcList->BytesConsumed(fromByteSize); + delete[] oldMemory; +} + +void CABufferList::DeallocateBuffers() +{ + AudioBuffer *buf = mBuffers; + for (UInt32 i = mNumberBuffers; i--; ++buf) { + buf->mData = NULL; + buf->mDataByteSize = 0; + } + if (mBufferMemory != NULL) { + delete[] mBufferMemory; + mBufferMemory = NULL; + } + +} + +extern "C" void CAShowAudioBufferList(const AudioBufferList *abl, int framesToPrint, int wordSize) +{ + printf("AudioBufferList @ %p:\n", abl); + const AudioBuffer *buf = abl->mBuffers; + for (UInt32 i = 0; i < abl->mNumberBuffers; ++i, ++buf) { + printf(" [%2ld]: %2ldch, %5ld bytes @ %8p", + i, buf->mNumberChannels, buf->mDataByteSize, buf->mData); + if (framesToPrint) { + printf(":"); + Byte *p = (Byte *)buf->mData; + for (int j = framesToPrint * buf->mNumberChannels; --j >= 0; ) + switch (wordSize) { + case 0: + printf(" %6.3f", *(Float32 *)p); + p += sizeof(Float32); + break; + case 1: + case -1: + printf(" %02X", *p); + p += 1; + break; + case 2: + printf(" %04X", EndianU16_BtoN(*(UInt16 *)p)); + p += 2; + break; + case 3: + printf(" %06X", (p[0] << 16) | (p[1] << 8) | p[2]); + p += 3; + break; + case 4: + printf(" %08lX", EndianU32_BtoN(*(UInt32 *)p)); + p += 4; + break; + case -2: + printf(" %04X", EndianU16_LtoN(*(UInt16 *)p)); + p += 2; + break; + case -3: + printf(" %06X", (p[2] << 16) | (p[1] << 8) | p[0]); + p += 3; + break; + case -4: + printf(" %08lX", EndianU32_LtoN(*(UInt32 *)p)); + p += 4; + break; + } + } + printf("\n"); + } +} + diff --git a/libs/appleutility/CABufferList.h b/libs/appleutility/CABufferList.h new file mode 100644 index 0000000000..3b0cef9a52 --- /dev/null +++ b/libs/appleutility/CABufferList.h @@ -0,0 +1,300 @@ +/* Copyright: © Copyright 2005 Apple Computer, Inc. All rights reserved. + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + ("Apple") in consideration of your agreement to the following terms, and your + use, installation, modification or redistribution of this Apple software + constitutes acceptance of these terms. If you do not agree with these terms, + please do not use, install, modify or redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and subject + to these terms, Apple grants you a personal, non-exclusive license, under AppleÕs + copyrights in this original Apple software (the "Apple Software"), to use, + reproduce, modify and redistribute the Apple Software, with or without + modifications, in source and/or binary forms; provided that if you redistribute + the Apple Software in its entirety and without modifications, you must retain + this notice and the following text and disclaimers in all such redistributions of + the Apple Software. Neither the name, trademarks, service marks or logos of + Apple Computer, Inc. may be used to endorse or promote products derived from the + Apple Software without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or implied, + are granted by Apple herein, including but not limited to any patent rights that + may be infringed by your derivative works or by other works in which the Apple + Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION + OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT + (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/*============================================================================= + CABufferList.h + +=============================================================================*/ + +#ifndef __CABufferList_h__ +#define __CABufferList_h__ + +#include +#include "CAStreamBasicDescription.h" +#if !defined(__COREAUDIO_USE_FLAT_INCLUDES__) + #include +#else + #include +#endif + +extern "C" void CAShowAudioBufferList(const AudioBufferList *abl, int framesToPrint, int wordSize); + // wordSize: 0 = float32, else integer word size, negative if little-endian + +/* ____________________________________________________________________________ +// CABufferList - variable length buffer list + + This class is designed for use in non-simplistic cases. For AudioUnits, AUBufferList + is preferred. + + CABufferList can be used in one of two ways: + - as mutable pointers into non-owned memory + - as an immutable array of buffers (owns its own memory). + + All buffers are assumed to have the same format (number of channels, word size), so that + we can assume their mDataByteSizes are all the same. +____________________________________________________________________________ */ +class CABufferList { +public: + void * operator new(size_t /*size*/, int nBuffers) { + return ::operator new(sizeof(CABufferList) + (nBuffers-1) * sizeof(AudioBuffer)); + } + static CABufferList * New(const char *name, const CAStreamBasicDescription &format) + { + UInt32 numBuffers = format.NumberChannelStreams(), channelsPerBuffer = format.NumberInterleavedChannels(); + return new(numBuffers) CABufferList(name, numBuffers, channelsPerBuffer); + } + +protected: + CABufferList(const char *name, UInt32 numBuffers, UInt32 channelsPerBuffer) : + mName(name), + mBufferMemory(NULL) + { + check(numBuffers > 0 /*&& channelsPerBuffer > 0*/); + mNumberBuffers = numBuffers; + AudioBuffer *buf = mBuffers; + for (UInt32 i = mNumberBuffers; i--; ++buf) { + buf->mNumberChannels = channelsPerBuffer; + buf->mDataByteSize = 0; + buf->mData = NULL; + } + } + +public: + ~CABufferList() + { + if (mBufferMemory) + delete[] mBufferMemory; + } + + const char * Name() { return mName; } + + const AudioBufferList & GetBufferList() const { return *(AudioBufferList *)&mNumberBuffers; } + + AudioBufferList & GetModifiableBufferList() + { + VerifyNotTrashingOwnedBuffer(); + return _GetBufferList(); + } + + UInt32 GetNumBytes() const + { + return mBuffers[0].mDataByteSize; + } + + void SetBytes(UInt32 nBytes, void *data) + { + VerifyNotTrashingOwnedBuffer(); + check(mNumberBuffers == 1); + mBuffers[0].mDataByteSize = nBytes; + mBuffers[0].mData = data; + } + + void CopyAllFrom(CABufferList *srcbl, CABufferList *ptrbl) + // copies bytes from srcbl + // make ptrbl reflect the length copied + // note that srcbl may be same as ptrbl! + { + // Note that this buffer *can* own memory and its pointers/lengths are not + // altered; only its buffer contents, which are copied from srcbl. + // The pointers/lengths in ptrbl are updated to reflect the addresses/lengths + // of the copied data, and srcbl's contents are consumed. + ptrbl->VerifyNotTrashingOwnedBuffer(); + UInt32 nBytes = srcbl->GetNumBytes(); + AudioBuffer *mybuf = mBuffers, *srcbuf = srcbl->mBuffers, + *ptrbuf = ptrbl->mBuffers; + for (UInt32 i = mNumberBuffers; i--; ++mybuf, ++srcbuf, ++ptrbuf) { + memmove(mybuf->mData, srcbuf->mData, srcbuf->mDataByteSize); + ptrbuf->mData = mybuf->mData; + ptrbuf->mDataByteSize = srcbuf->mDataByteSize; + } + if (srcbl != ptrbl) + srcbl->BytesConsumed(nBytes); + } + + void AppendFrom(CABufferList *blp, UInt32 nBytes) + { + VerifyNotTrashingOwnedBuffer(); + AudioBuffer *mybuf = mBuffers, *srcbuf = blp->mBuffers; + for (UInt32 i = mNumberBuffers; i--; ++mybuf, ++srcbuf) { + check(nBytes <= srcbuf->mDataByteSize); + memcpy((Byte *)mybuf->mData + mybuf->mDataByteSize, srcbuf->mData, nBytes); + mybuf->mDataByteSize += nBytes; + } + blp->BytesConsumed(nBytes); + } + + void PadWithZeroes(UInt32 desiredBufferSize) + // for cases where an algorithm (e.g. SRC) requires some + // padding to create silence following end-of-file + { + VerifyNotTrashingOwnedBuffer(); + if (GetNumBytes() > desiredBufferSize) return; + AudioBuffer *buf = mBuffers; + for (UInt32 i = mNumberBuffers; i--; ++buf) { + memset((Byte *)buf->mData + buf->mDataByteSize, 0, desiredBufferSize - buf->mDataByteSize); + buf->mDataByteSize = desiredBufferSize; + } + } + + void SetToZeroes(UInt32 nBytes) + { + VerifyNotTrashingOwnedBuffer(); + AudioBuffer *buf = mBuffers; + for (UInt32 i = mNumberBuffers; i--; ++buf) { + memset((Byte *)buf->mData, 0, nBytes); + buf->mDataByteSize = nBytes; + } + } + + void Reset() + { + DeallocateBuffers(); + } + + Boolean SameDataAs(const CABufferList* anotherBufferList) + { + // check to see if two buffer lists point to the same memory. + if (mNumberBuffers != anotherBufferList->mNumberBuffers) return false; + + for (UInt32 i = 0; i < mNumberBuffers; ++i) { + if (mBuffers[i].mData != anotherBufferList->mBuffers[i].mData) return false; + } + return true; + } + + void BytesConsumed(UInt32 nBytes) + // advance buffer pointers, decrease buffer sizes + { + VerifyNotTrashingOwnedBuffer(); + AudioBuffer *buf = mBuffers; + for (UInt32 i = mNumberBuffers; i--; ++buf) { + check(nBytes <= buf->mDataByteSize); + buf->mData = (Byte *)buf->mData + nBytes; + buf->mDataByteSize -= nBytes; + } + } + + void SetFrom(const AudioBufferList *abl) + { + VerifyNotTrashingOwnedBuffer(); + memcpy(&_GetBufferList(), abl, (char *)&abl->mBuffers[abl->mNumberBuffers] - (char *)abl); + } + + void SetFrom(const CABufferList *blp) + { + SetFrom(&blp->GetBufferList()); + } + + void SetFrom(const AudioBufferList *abl, UInt32 nBytes) + { + VerifyNotTrashingOwnedBuffer(); + AudioBuffer *mybuf = mBuffers; + const AudioBuffer *srcbuf = abl->mBuffers; + for (UInt32 i = mNumberBuffers; i--; ++mybuf, ++srcbuf) { + mybuf->mNumberChannels = srcbuf->mNumberChannels; + mybuf->mDataByteSize = nBytes; + mybuf->mData = srcbuf->mData; + } + } + + void SetFrom(const CABufferList *blp, UInt32 nBytes) + { + SetFrom(&blp->GetBufferList(), nBytes); + } + + AudioBufferList * ToAudioBufferList(AudioBufferList *abl) const + { + memcpy(abl, &GetBufferList(), (char *)&abl->mBuffers[mNumberBuffers] - (char *)abl); + return abl; + } + + void AllocateBuffers(UInt32 nBytes); + void AllocateBuffersAndCopyFrom(UInt32 nBytes, CABufferList *inCopyFromList, CABufferList *inSetPtrList); + + void DeallocateBuffers(); + + void UseExternalBuffer(Byte *ptr, UInt32 nBytes); + + void AdvanceBufferPointers(UInt32 nBytes) + // this is for bufferlists that function simply as + // an array of pointers into another bufferlist, being advanced, + // as in RenderOutput implementations + { + VerifyNotTrashingOwnedBuffer(); + AudioBuffer *buf = mBuffers; + for (UInt32 i = mNumberBuffers; i--; ++buf) { + buf->mData = (Byte *)buf->mData + nBytes; + buf->mDataByteSize -= nBytes; + } + } + + void SetNumBytes(UInt32 nBytes) + { + VerifyNotTrashingOwnedBuffer(); + AudioBuffer *buf = mBuffers; + for (UInt32 i = mNumberBuffers; i--; ++buf) + buf->mDataByteSize = nBytes; + } + + void Print(const char *label=NULL, int nframes=0, int wordSize=0) const + { + if (label == NULL) + label = mName; + printf("%s - ", label); + CAShowAudioBufferList(&GetBufferList(), nframes, wordSize); + if (mBufferMemory) + printf(" owned memory @ 0x%p:\n", mBufferMemory); + } + +protected: + AudioBufferList & _GetBufferList() { return *(AudioBufferList *)&mNumberBuffers; } // use with care + // if we make this public, then we lose ability to call VerifyNotTrashingOwnedBuffer + void VerifyNotTrashingOwnedBuffer() + { + // This needs to be called from places where we are modifying the buffer list. + // It's an error to modify the buffer pointers or lengths if we own the buffer memory. + check(mBufferMemory == NULL); + } + + const char * mName; // for debugging + Byte * mBufferMemory; + // the rest must exactly mirror the structure of AudioBufferList + UInt32 mNumberBuffers; + AudioBuffer mBuffers[1]; +}; + +#endif // __CABufferList_h__ diff --git a/libs/appleutility/CAXException.cpp b/libs/appleutility/CAXException.cpp new file mode 100644 index 0000000000..088575f041 --- /dev/null +++ b/libs/appleutility/CAXException.cpp @@ -0,0 +1,45 @@ +/* Copyright: © Copyright 2005 Apple Computer, Inc. All rights reserved. + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + ("Apple") in consideration of your agreement to the following terms, and your + use, installation, modification or redistribution of this Apple software + constitutes acceptance of these terms. If you do not agree with these terms, + please do not use, install, modify or redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and subject + to these terms, Apple grants you a personal, non-exclusive license, under AppleÕs + copyrights in this original Apple software (the "Apple Software"), to use, + reproduce, modify and redistribute the Apple Software, with or without + modifications, in source and/or binary forms; provided that if you redistribute + the Apple Software in its entirety and without modifications, you must retain + this notice and the following text and disclaimers in all such redistributions of + the Apple Software. Neither the name, trademarks, service marks or logos of + Apple Computer, Inc. may be used to endorse or promote products derived from the + Apple Software without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or implied, + are granted by Apple herein, including but not limited to any patent rights that + may be infringed by your derivative works or by other works in which the Apple + Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION + OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT + (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/*============================================================================= + CAXException.cpp + +=============================================================================*/ + +#include "CAXException.h" + +CAXException::WarningHandler CAXException::sWarningHandler = NULL; diff --git a/libs/appleutility/CAXException.h b/libs/appleutility/CAXException.h new file mode 100644 index 0000000000..796119763d --- /dev/null +++ b/libs/appleutility/CAXException.h @@ -0,0 +1,158 @@ +/* Copyright: © Copyright 2005 Apple Computer, Inc. All rights reserved. + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + ("Apple") in consideration of your agreement to the following terms, and your + use, installation, modification or redistribution of this Apple software + constitutes acceptance of these terms. If you do not agree with these terms, + please do not use, install, modify or redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and subject + to these terms, Apple grants you a personal, non-exclusive license, under AppleÕs + copyrights in this original Apple software (the "Apple Software"), to use, + reproduce, modify and redistribute the Apple Software, with or without + modifications, in source and/or binary forms; provided that if you redistribute + the Apple Software in its entirety and without modifications, you must retain + this notice and the following text and disclaimers in all such redistributions of + the Apple Software. Neither the name, trademarks, service marks or logos of + Apple Computer, Inc. may be used to endorse or promote products derived from the + Apple Software without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or implied, + are granted by Apple herein, including but not limited to any patent rights that + may be infringed by your derivative works or by other works in which the Apple + Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION + OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT + (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/*============================================================================= + CAXException.h + +=============================================================================*/ + +#ifndef __CAXException_h__ +#define __CAXException_h__ + +#if !defined(__COREAUDIO_USE_FLAT_INCLUDES__) + #include +#else + #include + #include +#endif +#include "CADebugMacros.h" +#include +#include +#include + +// An extended exception class that includes the name of the failed operation +class CAXException { +public: + CAXException(const char *operation, OSStatus err) : + mError(err) + { + if (operation == NULL) + mOperation[0] = '\0'; + else if (strlen(operation) >= sizeof(mOperation)) { + memcpy(mOperation, operation, sizeof(mOperation) - 1); + mOperation[sizeof(mOperation) - 1] = '\0'; + } else + strcpy(mOperation, operation); + } + + char *FormatError(char *str) const + { + return FormatError(str, mError); + } + + char mOperation[256]; + const OSStatus mError; + + // ------------------------------------------------- + + typedef void (*WarningHandler)(const char *msg, OSStatus err); + + /*static void Throw(const char *operation, OSStatus err) + { + throw CAXException(operation, err); + }*/ + + static char *FormatError(char *str, OSStatus error) + { + // see if it appears to be a 4-char-code + *(UInt32 *)(str + 1) = EndianU32_NtoB(error); + if (isprint(str[1]) && isprint(str[2]) && isprint(str[3]) && isprint(str[4])) { + str[0] = str[5] = '\''; + str[6] = '\0'; + } else + // no, format it as an integer + sprintf(str, "%ld", error); + return str; + } + + static void Warning(const char *s, OSStatus error) + { + if (sWarningHandler) + (*sWarningHandler)(s, error); + } + + static void SetWarningHandler(WarningHandler f) { sWarningHandler = f; } +private: + static WarningHandler sWarningHandler; +}; + +#if DEBUG || CoreAudio_Debug + #define XThrowIfError(error, operation) \ + do { \ + OSStatus __err = error; \ + if (__err) { \ + char __buf[12]; \ + DebugMessageN2("error %s: %4s\n", CAXException::FormatError(__buf, __err), operation);\ + STOP; \ + throw CAXException(operation, __err); \ + } \ + } while (0) + + #define XThrowIf(condition, error, operation) \ + do { \ + if (condition) { \ + OSStatus __err = error; \ + char __buf[12]; \ + DebugMessageN2("error %s: %4s\n", CAXException::FormatError(__buf, __err), operation);\ + STOP; \ + throw CAXException(operation, __err); \ + } \ + } while (0) + +#else + #define XThrowIfError(error, operation) \ + do { \ + OSStatus __err = error; \ + if (__err) { \ + throw CAXException(operation, __err); \ + } \ + } while (0) + + #define XThrowIf(condition, error, operation) \ + do { \ + if (condition) { \ + OSStatus __err = error; \ + throw CAXException(operation, __err); \ + } \ + } while (0) + +#endif + +#define XThrow(error, operation) XThrowIf(true, error, operation) +#define XThrowIfErr(error) XThrowIfError(error, #error) + +#endif // __CAXException_h__ diff --git a/libs/ardour/SConscript b/libs/ardour/SConscript index c15897d8be..2e5253ec25 100644 --- a/libs/ardour/SConscript +++ b/libs/ardour/SConscript @@ -171,9 +171,27 @@ def CheckJackRecomputeLatencies(context): context.Result(result) return result +jack_video_frame_offset_test = """ +#include +int main(int argc, char** argv) +{ + jack_position_t pos; + + pos.valid & JackVideoFrameOffset; + return 0; +} +""" + +def CheckJackVideoFrameOffset(context): + context.Message('Checking for JackVideoFrameOffset in jack_position_bits_t enum...') + result = context.TryLink(jack_video_frame_offset_test, '.c') + context.Result(result) + return result + conf = Configure(ardour, custom_tests = { 'CheckJackClientOpen' : CheckJackClientOpen, - 'CheckJackRecomputeLatencies' : CheckJackRecomputeLatencies + 'CheckJackRecomputeLatencies' : CheckJackRecomputeLatencies, + 'CheckJackVideoFrameOffset' : CheckJackVideoFrameOffset }) if conf.CheckJackClientOpen(): @@ -182,6 +200,14 @@ if conf.CheckJackClientOpen(): if conf.CheckJackRecomputeLatencies(): ardour.Append(CXXFLAGS="-DHAVE_JACK_RECOMPUTE_LATENCIES") +if conf.CheckJackVideoFrameOffset(): + ardour.Append(CXXFLAGS="-DHAVE_JACK_VIDEO_SUPPORT") + +if conf.CheckFunc('jack_port_ensure_monitor'): + env.Append(CCFLAGS='-DHAVE_JACK_PORT_ENSURE_MONITOR') +else: + print '\nWARNING: You need at least svn revision 985 of jack for hardware monitoring to work correctly.\n' + # # Optional header files # diff --git a/libs/ardour/ardour/audio_diskstream.h b/libs/ardour/ardour/audio_diskstream.h index 8588c9660d..81206f2bb0 100644 --- a/libs/ardour/ardour/audio_diskstream.h +++ b/libs/ardour/ardour/audio_diskstream.h @@ -61,8 +61,6 @@ class AudioDiskstream : public Diskstream AudioDiskstream (Session &, const XMLNode&); ~AudioDiskstream(); - const PBD::ID& id() const { return _id; } - float playback_buffer_load() const; float capture_buffer_load() const; diff --git a/libs/ardour/ardour/audiofilesource.h b/libs/ardour/ardour/audiofilesource.h index b1ffab0944..0cab328fba 100644 --- a/libs/ardour/ardour/audiofilesource.h +++ b/libs/ardour/ardour/audiofilesource.h @@ -75,7 +75,7 @@ class AudioFileSource : public AudioSource { int move_to_trash (const string trash_dir_name); - static bool is_empty (string path); + static bool is_empty (Session&, string path); void mark_streaming_write_completed (); void mark_take (string); @@ -104,16 +104,16 @@ class AudioFileSource : public AudioSource { /* constructor to be called for existing external-to-session files */ - AudioFileSource (std::string path, Flag flags); + AudioFileSource (Session&, std::string path, Flag flags); /* constructor to be called for new in-session files */ - AudioFileSource (std::string path, Flag flags, + AudioFileSource (Session&, std::string path, Flag flags, SampleFormat samp_format, HeaderFormat hdr_format); /* constructor to be called for existing in-session files */ - AudioFileSource (const XMLNode&); + AudioFileSource (Session&, const XMLNode&); int init (string idstr, bool must_exist); @@ -121,7 +121,6 @@ class AudioFileSource : public AudioSource { string _path; Flag _flags; string _take_id; - bool allow_remove_if_empty; uint64_t timeline_position; static string peak_dir; diff --git a/libs/ardour/ardour/audioplaylist.h b/libs/ardour/ardour/audioplaylist.h index 0426208ba1..6a52f1c16f 100644 --- a/libs/ardour/ardour/audioplaylist.h +++ b/libs/ardour/ardour/audioplaylist.h @@ -111,6 +111,8 @@ class AudioPlaylist : public ARDOUR::Playlist bool region_changed (Change, boost::shared_ptr); void crossfade_changed (Change); void add_crossfade (Crossfade&); + + void source_offset_changed (boost::shared_ptr region); }; } /* namespace ARDOUR */ diff --git a/libs/ardour/ardour/audioregion.h b/libs/ardour/ardour/audioregion.h index 9b97a88bc0..71a66e52a0 100644 --- a/libs/ardour/ardour/audioregion.h +++ b/libs/ardour/ardour/audioregion.h @@ -174,6 +174,7 @@ class AudioRegion : public Region void recompute_at_end (); void envelope_changed (Change); + void source_offset_changed (); mutable Curve _fade_in; FadeShape _fade_in_shape; diff --git a/libs/ardour/ardour/audiosource.h b/libs/ardour/ardour/audiosource.h index 751213ee8e..6a0a20d4b8 100644 --- a/libs/ardour/ardour/audiosource.h +++ b/libs/ardour/ardour/audiosource.h @@ -47,8 +47,8 @@ const jack_nframes_t frames_per_peak = 256; class AudioSource : public Source { public: - AudioSource (string name); - AudioSource (const XMLNode&); + AudioSource (Session&, string name); + AudioSource (Session&, const XMLNode&); virtual ~AudioSource (); virtual jack_nframes_t available_peaks (double zoom) const; diff --git a/libs/ardour/ardour/automation_event.h b/libs/ardour/ardour/automation_event.h index a3b84289c1..22ab706f82 100644 --- a/libs/ardour/ardour/automation_event.h +++ b/libs/ardour/ardour/automation_event.h @@ -159,8 +159,6 @@ struct ControlEvent { XMLNode &get_state(void); int set_state (const XMLNode &s); - PBD::ID id() { return _id; } - void set_max_xval (double); double get_max_xval() const { return max_xval; } @@ -189,7 +187,7 @@ struct ControlEvent { static sigc::signal AutomationListCreated; protected: - PBD::ID _id; + struct State : public ARDOUR::StateManager::State { AutomationEventList events; diff --git a/libs/ardour/ardour/coreaudiosource.h b/libs/ardour/ardour/coreaudiosource.h index cf25c466ee..49009caf53 100644 --- a/libs/ardour/ardour/coreaudiosource.h +++ b/libs/ardour/ardour/coreaudiosource.h @@ -20,15 +20,16 @@ #ifndef __coreaudio_source_h__ #define __coreaudio_source_h__ +#include + #include -#include namespace ARDOUR { class CoreAudioSource : public AudioFileSource { public: - CoreAudioSource (const XMLNode&); - CoreAudioSource (const string& path_plus_channel, Flag); + CoreAudioSource (ARDOUR::Session&, const XMLNode&); + CoreAudioSource (ARDOUR::Session&, const string& path_plus_channel, Flag); ~CoreAudioSource (); float sample_rate() const; @@ -45,7 +46,7 @@ class CoreAudioSource : public AudioFileSource { private: - ExtAudioFileRef af; + mutable CAAudioFile af; uint16_t n_channels; mutable float *tmpbuf; diff --git a/libs/ardour/ardour/destructive_filesource.h b/libs/ardour/ardour/destructive_filesource.h index fb2a3be47b..fed84217e7 100644 --- a/libs/ardour/ardour/destructive_filesource.h +++ b/libs/ardour/ardour/destructive_filesource.h @@ -31,12 +31,12 @@ namespace ARDOUR { class DestructiveFileSource : public SndFileSource { public: - DestructiveFileSource (std::string path, SampleFormat samp_format, HeaderFormat hdr_format, jack_nframes_t rate, + DestructiveFileSource (Session&, std::string path, SampleFormat samp_format, HeaderFormat hdr_format, jack_nframes_t rate, Flag flags = AudioFileSource::Flag (AudioFileSource::Writable)); - DestructiveFileSource (std::string path, Flag flags); + DestructiveFileSource (Session&, std::string path, Flag flags); - DestructiveFileSource (const XMLNode&); + DestructiveFileSource (Session&, const XMLNode&); ~DestructiveFileSource (); jack_nframes_t last_capture_start_frame() const; diff --git a/libs/ardour/ardour/diskstream.h b/libs/ardour/ardour/diskstream.h index 2bce6a424f..048e9df90f 100644 --- a/libs/ardour/ardour/diskstream.h +++ b/libs/ardour/ardour/diskstream.h @@ -92,7 +92,6 @@ class IO; bool destructive() const { return _flags & Destructive; } virtual void set_destructive (bool yn); - const PBD::ID& id() const { return _id; } bool hidden() const { return _flags & Hidden; } bool recordable() const { return _flags & Recordable; } bool reversed() const { return _actual_speed < 0.0f; } @@ -243,7 +242,6 @@ class IO; ARDOUR::Session& _session; ARDOUR::IO* _io; ChanCount _n_channels; - PBD::ID _id; Playlist* _playlist; mutable gint _record_enabled; diff --git a/libs/ardour/ardour/io.h b/libs/ardour/ardour/io.h index ef0ab6c465..6074376291 100644 --- a/libs/ardour/ardour/io.h +++ b/libs/ardour/ardour/io.h @@ -263,8 +263,6 @@ public: void start_pan_touch (uint32_t which); void end_pan_touch (uint32_t which); - const PBD::ID& id() const { return _id; } - void defer_pan_reset (); void allow_pan_reset (); @@ -292,7 +290,6 @@ public: string _name; Connection* _input_connection; Connection* _output_connection; - PBD::ID _id; bool no_panner_reset; bool _phase_invert; XMLNode* deferred_state; diff --git a/libs/ardour/ardour/location.h b/libs/ardour/ardour/location.h index 1052b74bd4..96fb1b1bcf 100644 --- a/libs/ardour/ardour/location.h +++ b/libs/ardour/ardour/location.h @@ -121,10 +121,7 @@ class Location : public sigc::trackable, public PBD::StatefulDestructible XMLNode& get_state (void); int set_state (const XMLNode&); - PBD::ID id() { return _id; } - private: - PBD::ID _id; string _name; jack_nframes_t _start; jack_nframes_t _end; @@ -150,7 +147,6 @@ class Locations : public StateManager, public PBD::StatefulDestructible XMLNode& get_state (void); int set_state (const XMLNode&); - PBD::ID id() { return _id; } Location *get_location_by_id(PBD::ID); Location* auto_loop_location () const; @@ -204,8 +200,6 @@ class Locations : public StateManager, public PBD::StatefulDestructible Change restore_state (StateManager::State&); StateManager::State* state_factory (std::string why) const; - - PBD::ID _id; }; } // namespace ARDOUR diff --git a/libs/ardour/ardour/midi_source.h b/libs/ardour/ardour/midi_source.h index 757e33f70e..a035bf683e 100644 --- a/libs/ardour/ardour/midi_source.h +++ b/libs/ardour/ardour/midi_source.h @@ -43,8 +43,8 @@ class MidiRingBuffer; class MidiSource : public Source { public: - MidiSource (string name); - MidiSource (const XMLNode&); + MidiSource (Session& session, string name); + MidiSource (Session& session, const XMLNode&); virtual ~MidiSource (); virtual jack_nframes_t read (MidiRingBuffer& dst, jack_nframes_t start, jack_nframes_t cnt, jack_nframes_t stamp_offset) const; diff --git a/libs/ardour/ardour/playlist.h b/libs/ardour/ardour/playlist.h index c04b59286f..4249007fff 100644 --- a/libs/ardour/ardour/playlist.h +++ b/libs/ardour/ardour/playlist.h @@ -81,7 +81,6 @@ class Playlist : public StateManager, public PBD::StatefulDestructible { EditMode get_edit_mode() const { return _edit_mode; } void set_edit_mode (EditMode); - PBD::ID id() { return _id; } /* Editing operations */ void add_region (boost::shared_ptr, jack_nframes_t position, float times = 1, bool with_save = true); @@ -279,8 +278,6 @@ class Playlist : public StateManager, public PBD::StatefulDestructible { void unset_freeze_child (Playlist*); void timestamp_layer_op (boost::shared_ptr); - - PBD::ID _id; }; } /* namespace ARDOUR */ diff --git a/libs/ardour/ardour/port.h b/libs/ardour/ardour/port.h index 2a99bdcc16..d6dcd55645 100644 --- a/libs/ardour/ardour/port.h +++ b/libs/ardour/ardour/port.h @@ -103,7 +103,7 @@ class Port : public sigc::trackable { void ensure_monitor_input (bool yn) { -#ifdef WITH_JACK_PORT_ENSURE_MONITOR +#ifdef HAVE_JACK_PORT_ENSURE_MONITOR jack_port_ensure_monitor (_port, yn); #else jack_port_request_monitor(_port, yn); diff --git a/libs/ardour/ardour/region.h b/libs/ardour/ardour/region.h index c3e93fc7ae..821927f8c2 100644 --- a/libs/ardour/ardour/region.h +++ b/libs/ardour/ardour/region.h @@ -37,7 +37,6 @@ class XMLNode; namespace ARDOUR { class Playlist; -class Source; enum RegionEditState { EditChangesNothing = 0, @@ -99,8 +98,6 @@ class Region : public PBD::StatefulDestructible, public StateManager, public boo virtual ~Region(); - const PBD::ID& id() const { return _id; } - /* Note: changing the name of a Region does not constitute an edit */ string name() const { return _name; } diff --git a/libs/ardour/ardour/session.h b/libs/ardour/ardour/session.h index 09fd01baec..99616f9729 100644 --- a/libs/ardour/ardour/session.h +++ b/libs/ardour/ardour/session.h @@ -267,8 +267,7 @@ class Session : public sigc::trackable, public PBD::StatefulDestructible bool dirty() const { return _state_of_the_state & Dirty; } sigc::signal DirtyChanged; - std::string sound_dir () const; - std::string tape_dir () const; + std::string sound_dir (bool with_path = true) const; std::string peak_dir () const; std::string dead_sound_dir () const; std::string automation_dir () const; @@ -281,7 +280,8 @@ class Session : public sigc::trackable, public PBD::StatefulDestructible static string change_audio_path_by_name (string oldpath, string oldname, string newname, bool destructive); static string change_midi_path_by_name (string oldpath, string oldname, string newname, bool destructive); - static string peak_path_from_audio_path (string); + + string peak_path_from_audio_path (string) const; string audio_path_from_name (string, uint32_t nchans, uint32_t chan, bool destructive); string midi_path_from_name (string); @@ -1713,9 +1713,10 @@ class Session : public sigc::trackable, public PBD::StatefulDestructible uint32_t _total_free_4k_blocks; Glib::Mutex space_lock; + static const char* old_sound_dir_name; static const char* sound_dir_name; - static const char* tape_dir_name; static const char* dead_sound_dir_name; + static const char* interchange_dir_name; static const char* peak_dir_name; string discover_best_sound_dir (bool destructive = false); diff --git a/libs/ardour/ardour/smf_source.h b/libs/ardour/ardour/smf_source.h index 5c3fdcfec9..98e78e3802 100644 --- a/libs/ardour/ardour/smf_source.h +++ b/libs/ardour/ardour/smf_source.h @@ -44,10 +44,10 @@ class SMFSource : public MidiSource { }; /** Constructor for existing external-to-session files */ - SMFSource (std::string path, Flag flags = Flag(0)); + SMFSource (Session& session, std::string path, Flag flags = Flag(0)); /* Constructor for existing in-session files */ - SMFSource (const XMLNode&); + SMFSource (Session& session, const XMLNode&); virtual ~SMFSource (); diff --git a/libs/ardour/ardour/sndfilesource.h b/libs/ardour/ardour/sndfilesource.h index ab3e61eb29..cb6bd2e920 100644 --- a/libs/ardour/ardour/sndfilesource.h +++ b/libs/ardour/ardour/sndfilesource.h @@ -31,11 +31,11 @@ class SndFileSource : public AudioFileSource { public: /* constructor to be called for existing external-to-session files */ - SndFileSource (std::string path, Flag flags); + SndFileSource (Session&, std::string path, Flag flags); /* constructor to be called for new in-session files */ - SndFileSource (std::string path, SampleFormat samp_format, HeaderFormat hdr_format, jack_nframes_t rate, + SndFileSource (Session&, std::string path, SampleFormat samp_format, HeaderFormat hdr_format, jack_nframes_t rate, Flag flags = AudioFileSource::Flag (AudioFileSource::Writable| AudioFileSource::Removable| AudioFileSource::RemovableIfEmpty| @@ -43,7 +43,7 @@ class SndFileSource : public AudioFileSource { /* constructor to be called for existing in-session files */ - SndFileSource (const XMLNode&); + SndFileSource (Session&, const XMLNode&); ~SndFileSource (); diff --git a/libs/ardour/ardour/source.h b/libs/ardour/ardour/source.h index a18250fff2..591e7181a1 100644 --- a/libs/ardour/ardour/source.h +++ b/libs/ardour/ardour/source.h @@ -32,11 +32,14 @@ namespace ARDOUR { +class Session; + class Source : public PBD::StatefulDestructible, public sigc::trackable { public: - Source (std::string name, DataType type); - Source (const XMLNode&); + Source (Session&, std::string name, DataType type); + Source (Session&, const XMLNode&); + virtual ~Source (); std::string name() const { return _name; } @@ -44,8 +47,6 @@ class Source : public PBD::StatefulDestructible, public sigc::trackable DataType type() { return _type; } - const PBD::ID& id() const { return _id; } - time_t timestamp() const { return _timestamp; } void stamp (time_t when) { _timestamp = when; } @@ -65,13 +66,13 @@ class Source : public PBD::StatefulDestructible, public sigc::trackable protected: void update_length (jack_nframes_t pos, jack_nframes_t cnt); + Session& _session; string _name; DataType _type; time_t _timestamp; jack_nframes_t _length; private: - PBD::ID _id; }; } diff --git a/libs/ardour/ardour/source_factory.h b/libs/ardour/ardour/source_factory.h index 2b25752a0d..073532c6ab 100644 --- a/libs/ardour/ardour/source_factory.h +++ b/libs/ardour/ardour/source_factory.h @@ -13,14 +13,17 @@ class XMLNode; namespace ARDOUR { +class Session; + class SourceFactory { public: static sigc::signal > SourceCreated; - static boost::shared_ptr create (const XMLNode& node); + static boost::shared_ptr create (Session&, const XMLNode& node); - static boost::shared_ptr createReadable (DataType type, std::string idstr, AudioFileSource::Flag flags, bool announce = true); - static boost::shared_ptr createWritable (DataType type, std::string name, bool destructive, jack_nframes_t rate, bool announce = true); + // MIDI sources will have to be hacked in here somehow + static boost::shared_ptr createReadable (DataType type, Session&, std::string idstr, AudioFileSource::Flag flags, bool announce = true); + static boost::shared_ptr createWritable (DataType type, Session&, std::string name, bool destructive, jack_nframes_t rate, bool announce = true); }; } diff --git a/libs/ardour/ardour/tempo.h b/libs/ardour/ardour/tempo.h index 9111aeba68..0b37579ecb 100644 --- a/libs/ardour/ardour/tempo.h +++ b/libs/ardour/ardour/tempo.h @@ -242,7 +242,6 @@ class TempoMap : public StateManager, public PBD::StatefulDestructible XMLNode& get_state (void); int set_state (const XMLNode&); - PBD::ID id() { return _id; } void dump (std::ostream&) const; void clear (); @@ -320,8 +319,6 @@ class TempoMap : public StateManager, public PBD::StatefulDestructible void save_state (std::string why); - PBD::ID _id; - }; }; /* namespace ARDOUR */ diff --git a/libs/ardour/audio_diskstream.cc b/libs/ardour/audio_diskstream.cc index 7b77aad8f5..9572297ac5 100644 --- a/libs/ardour/audio_diskstream.cc +++ b/libs/ardour/audio_diskstream.cc @@ -1590,7 +1590,7 @@ AudioDiskstream::transport_stopped (struct tm& when, time_t twhen, bool abort_ca string region_name; _session.region_name (region_name, channels[0].write_source->name(), false); - cerr << _name << ": based on ci of " << (*ci)->start << " for " << (*ci)->frames << " add region " << region_name << endl; + // cerr << _name << ": based on ci of " << (*ci)->start << " for " << (*ci)->frames << " add region " << region_name << endl; try { boost::shared_ptr rx (RegionFactory::create (srcs, buffer_position, (*ci)->frames, region_name)); @@ -1948,7 +1948,7 @@ AudioDiskstream::use_new_write_source (uint32_t n) if (chan.write_source) { - if (AudioFileSource::is_empty (chan.write_source->path())) { + if (AudioFileSource::is_empty (_session, chan.write_source->path())) { chan.write_source->mark_for_remove (); chan.write_source.reset (); } else { @@ -2185,7 +2185,8 @@ AudioDiskstream::use_pending_capture_data (XMLNode& node) } try { - fs = boost::dynamic_pointer_cast (SourceFactory::createWritable (DataType::AUDIO, prop->value(), false, _session.frame_rate())); + fs = boost::dynamic_pointer_cast ( + SourceFactory::createWritable (DataType::AUDIO, _session, prop->value(), false, _session.frame_rate())); } catch (failed_constructor& err) { diff --git a/libs/ardour/audio_playlist.cc b/libs/ardour/audio_playlist.cc index 7fc29f84b8..bd09b1e6b3 100644 --- a/libs/ardour/audio_playlist.cc +++ b/libs/ardour/audio_playlist.cc @@ -899,3 +899,4 @@ AudioPlaylist::crossfades_at (jack_nframes_t frame, Crossfades& clist) } } } + diff --git a/libs/ardour/audioengine.cc b/libs/ardour/audioengine.cc index ddb835c78f..b2a1fb6a0f 100644 --- a/libs/ardour/audioengine.cc +++ b/libs/ardour/audioengine.cc @@ -167,6 +167,9 @@ AudioEngine::stop () bool AudioEngine::get_sync_offset (jack_nframes_t& offset) const { + +#ifdef HAVE_JACK_VIDEO_SUPPORT + jack_position_t pos; (void) jack_transport_query (_jack, &pos); @@ -176,6 +179,8 @@ AudioEngine::get_sync_offset (jack_nframes_t& offset) const return true; } +#endif + return false; } diff --git a/libs/ardour/audiofilesource.cc b/libs/ardour/audiofilesource.cc index 963a2274df..adfd352d12 100644 --- a/libs/ardour/audiofilesource.cc +++ b/libs/ardour/audiofilesource.cc @@ -63,8 +63,8 @@ char AudioFileSource::bwf_country_code[3] = "US"; char AudioFileSource::bwf_organization_code[4] = "LAS"; char AudioFileSource::bwf_serial_number[13] = "000000000000"; -AudioFileSource::AudioFileSource (string idstr, Flag flags) - : AudioSource (idstr), _flags (flags) +AudioFileSource::AudioFileSource (Session& s, string idstr, Flag flags) + : AudioSource (s, idstr), _flags (flags) { /* constructor used for existing external to session files. file must exist already */ @@ -74,8 +74,8 @@ AudioFileSource::AudioFileSource (string idstr, Flag flags) } -AudioFileSource::AudioFileSource (std::string path, Flag flags, SampleFormat samp_format, HeaderFormat hdr_format) - : AudioSource (path), _flags (flags) +AudioFileSource::AudioFileSource (Session& s, std::string path, Flag flags, SampleFormat samp_format, HeaderFormat hdr_format) + : AudioSource (s, path), _flags (flags) { /* constructor used for new internal-to-session files. file cannot exist */ @@ -84,8 +84,8 @@ AudioFileSource::AudioFileSource (std::string path, Flag flags, SampleFormat sam } } -AudioFileSource::AudioFileSource (const XMLNode& node) - : AudioSource (node), _flags (Flag (Writable|CanRename)) +AudioFileSource::AudioFileSource (Session& s, const XMLNode& node) + : AudioSource (s, node), _flags (Flag (Writable|CanRename)) { /* constructor used for existing internal-to-session files. file must exist */ @@ -101,6 +101,7 @@ AudioFileSource::AudioFileSource (const XMLNode& node) AudioFileSource::~AudioFileSource () { if (removable()) { + cerr << "Removing file " << _path << " because its removable\n"; unlink (_path.c_str()); unlink (peakpath.c_str()); } @@ -109,7 +110,7 @@ AudioFileSource::~AudioFileSource () bool AudioFileSource::removable () const { - return (_flags & Removable) && ((_flags & RemoveAtDestroy) || ((_flags & RemovableIfEmpty) && is_empty (_path))); + return (_flags & Removable) && ((_flags & RemoveAtDestroy) || ((_flags & RemovableIfEmpty) && length() == 0)); } int @@ -135,7 +136,7 @@ AudioFileSource::init (string pathstr, bool must_exist) string AudioFileSource::peak_path (string audio_path) { - return Session::peak_path_from_audio_path (audio_path); + return _session.peak_path_from_audio_path (audio_path); } string @@ -527,7 +528,7 @@ void AudioFileSource::set_allow_remove_if_empty (bool yn) { if (writable()) { - allow_remove_if_empty = yn; + _flags = Flag (_flags | RemovableIfEmpty); } } @@ -561,11 +562,12 @@ AudioFileSource::set_name (string newname, bool destructive) } bool -AudioFileSource::is_empty (string path) +AudioFileSource::is_empty (Session& s, string path) { bool ret = false; + boost::shared_ptr afs = boost::dynamic_pointer_cast ( - SourceFactory::createReadable (DataType::AUDIO, path, NoPeakFile, false)); + SourceFactory::createReadable (DataType::AUDIO, s, path, NoPeakFile, false)); if (afs) { ret = (afs->length() == 0); diff --git a/libs/ardour/audiofilter.cc b/libs/ardour/audiofilter.cc index 0a630f1e25..4c38ecec20 100644 --- a/libs/ardour/audiofilter.cc +++ b/libs/ardour/audiofilter.cc @@ -51,7 +51,7 @@ AudioFilter::make_new_sources (boost::shared_ptr region, SourceList try { nsrcs.push_back (boost::dynamic_pointer_cast ( - SourceFactory::createWritable (DataType::AUDIO, path, false, session.frame_rate()))); + SourceFactory::createWritable (DataType::AUDIO, session, path, false, session.frame_rate()))); } catch (failed_constructor& err) { diff --git a/libs/ardour/audioregion.cc b/libs/ardour/audioregion.cc index 362ab305db..959177d0cf 100644 --- a/libs/ardour/audioregion.cc +++ b/libs/ardour/audioregion.cc @@ -38,7 +38,7 @@ #include #include #include -#include +#include #include "i18n.h" #include @@ -71,6 +71,11 @@ AudioRegion::AudioRegion (boost::shared_ptr src, jack_nframes_t sta _fade_out (0.0, 2.0, 1.0, false), _envelope (0.0, 2.0, 1.0, false) { + boost::shared_ptr afs = boost::dynamic_pointer_cast (src); + if (afs) { + afs->HeaderPositionOffsetChanged.connect (mem_fun (*this, &AudioRegion::source_offset_changed)); + } + _scale_amplitude = 1.0; set_default_fades (); @@ -88,6 +93,11 @@ AudioRegion::AudioRegion (boost::shared_ptr src, jack_nframes_t sta , _fade_out (0.0, 2.0, 1.0, false) , _envelope (0.0, 2.0, 1.0, false) { + boost::shared_ptr afs = boost::dynamic_pointer_cast (src); + if (afs) { + afs->HeaderPositionOffsetChanged.connect (mem_fun (*this, &AudioRegion::source_offset_changed)); + } + _scale_amplitude = 1.0; set_default_fades (); @@ -180,6 +190,11 @@ AudioRegion::AudioRegion (boost::shared_ptr src, const XMLNode& nod , _fade_out (0.0, 2.0, 1.0, false) , _envelope (0.0, 2.0, 1.0, false) { + boost::shared_ptr afs = boost::dynamic_pointer_cast (src); + if (afs) { + afs->HeaderPositionOffsetChanged.connect (mem_fun (*this, &AudioRegion::source_offset_changed)); + } + set_default_fades (); if (set_state (node)) { @@ -1165,6 +1180,12 @@ AudioRegion::speed_mismatch (float sr) const return fsr != sr; } +void +AudioRegion::source_offset_changed () +{ + set_position (source()->natural_position() + start(), this); +} + boost::shared_ptr AudioRegion::audio_source (uint32_t n) const { diff --git a/libs/ardour/audiosource.cc b/libs/ardour/audiosource.cc index d74a38097c..a8fe2a7c73 100644 --- a/libs/ardour/audiosource.cc +++ b/libs/ardour/audiosource.cc @@ -50,8 +50,8 @@ int AudioSource::peak_request_pipe[2]; bool AudioSource::_build_missing_peakfiles = false; bool AudioSource::_build_peakfiles = false; -AudioSource::AudioSource (string name) - : Source (name, DataType::AUDIO) +AudioSource::AudioSource (Session& s, string name) + : Source (s, name, DataType::AUDIO) { if (pending_peak_sources_lock == 0) { pending_peak_sources_lock = new Glib::Mutex; @@ -63,8 +63,8 @@ AudioSource::AudioSource (string name) _write_data_count = 0; } -AudioSource::AudioSource (const XMLNode& node) - : Source (node) +AudioSource::AudioSource (Session& s, const XMLNode& node) + : Source (s, node) { if (pending_peak_sources_lock == 0) { pending_peak_sources_lock = new Glib::Mutex; diff --git a/libs/ardour/coreaudiosource.cc b/libs/ardour/coreaudiosource.cc index 0d7e690a25..049b5aabbe 100644 --- a/libs/ardour/coreaudiosource.cc +++ b/libs/ardour/coreaudiosource.cc @@ -20,6 +20,9 @@ #include #include +#include +#include + #include "i18n.h" #include @@ -27,14 +30,14 @@ using namespace ARDOUR; using namespace PBD; -CoreAudioSource::CoreAudioSource (const XMLNode& node) - : AudioFileSource (node) +CoreAudioSource::CoreAudioSource (Session& s, const XMLNode& node) + : AudioFileSource (s, node) { init (_name); } -CoreAudioSource::CoreAudioSource (const string& idstr, Flag flags) - : AudioFileSource(idstr, flags) +CoreAudioSource::CoreAudioSource (Session& s, const string& idstr, Flag flags) + : AudioFileSource(s, idstr, flags) { init (idstr); } @@ -43,93 +46,48 @@ void CoreAudioSource::init (const string& idstr) { string::size_type pos; - string file; tmpbuf = 0; tmpbufsize = 0; - af = 0; - OSStatus err = noErr; _name = idstr; if ((pos = idstr.find_last_of (':')) == string::npos) { channel = 0; - file = idstr; + _path = idstr; } else { channel = atoi (idstr.substr (pos+1).c_str()); - file = idstr.substr (0, pos); - } - - /* note that we temporarily truncated _id at the colon */ - FSRef fsr; - err = FSPathMakeRef ((UInt8*)file.c_str(), &fsr, 0); - if (err != noErr) { - error << string_compose (_("Could not make reference to file: %1"), name()) << endmsg; - throw failed_constructor(); - } - - err = ExtAudioFileOpen (&fsr, &af); - if (err != noErr) { - error << string_compose (_("Could not open file: %1"), name()) << endmsg; - ExtAudioFileDispose (af); - throw failed_constructor(); + _path = idstr.substr (0, pos); } - AudioStreamBasicDescription file_asbd; - memset(&file_asbd, 0, sizeof(AudioStreamBasicDescription)); - size_t asbd_size = sizeof(AudioStreamBasicDescription); - err = ExtAudioFileGetProperty(af, - kExtAudioFileProperty_FileDataFormat, &asbd_size, &file_asbd); - if (err != noErr) { - error << string_compose (_("Could not get file data format for file: %1"), name()) << endmsg; - ExtAudioFileDispose (af); - throw failed_constructor(); - } - n_channels = file_asbd.mChannelsPerFrame; - - cerr << "number of channels: " << n_channels << endl; + cerr << "CoreAudioSource::init() " << name() << endl; - if (channel >= n_channels) { - error << string_compose(_("CoreAudioSource: file only contains %1 channels; %2 is invalid as a channel number"), n_channels, channel) << endmsg; - ExtAudioFileDispose (af); - throw failed_constructor(); - } + /* note that we temporarily truncated _id at the colon */ + try { + af.Open(_path.c_str()); - int64_t ca_frames; - size_t prop_size = sizeof(int64_t); + CAStreamBasicDescription file_asbd (af.GetFileDataFormat()); + n_channels = file_asbd.NumberChannels(); + cerr << "number of channels: " << n_channels << endl; + + if (channel >= n_channels) { + error << string_compose("CoreAudioSource: file only contains %1 channels; %2 is invalid as a channel number (%3)", n_channels, channel, name()) << endmsg; + throw failed_constructor(); + } - err = ExtAudioFileGetProperty(af, kExtAudioFileProperty_FileLengthFrames, &prop_size, &ca_frames); - if (err != noErr) { - error << string_compose (_("Could not get file length for file: %1"), name()) << endmsg; - ExtAudioFileDispose (af); - throw failed_constructor(); - } + _length = af.GetNumberFrames(); - _length = ca_frames; - _path = file; - - AudioStreamBasicDescription client_asbd; - memset(&client_asbd, 0, sizeof(AudioStreamBasicDescription)); - client_asbd.mSampleRate = file_asbd.mSampleRate; - client_asbd.mFormatID = kAudioFormatLinearPCM; - client_asbd.mFormatFlags = kLinearPCMFormatFlagIsFloat; - client_asbd.mBytesPerPacket = file_asbd.mChannelsPerFrame * 4; - client_asbd.mFramesPerPacket = 1; - client_asbd.mBytesPerFrame = client_asbd.mBytesPerPacket; - client_asbd.mChannelsPerFrame = file_asbd.mChannelsPerFrame; - client_asbd.mBitsPerChannel = 32; - - err = ExtAudioFileSetProperty (af, kExtAudioFileProperty_ClientDataFormat, asbd_size, &client_asbd); - if (err != noErr) { - error << string_compose (_("Could not set client data format for file: %1"), name()) << endmsg; - ExtAudioFileDispose (af); + CAStreamBasicDescription client_asbd(file_asbd); + client_asbd.SetCanonical(client_asbd.NumberChannels(), false); + af.SetClientFormat (client_asbd); + } catch (CAXException& cax) { + error << string_compose ("CoreAudioSource: %1 (%2)", cax.mOperation, name()) << endmsg; throw failed_constructor (); } if (_build_peakfiles) { - if (initialize_peakfile (false, file)) { - error << string_compose(_("initialize peakfile failed for file %1"), name()) << endmsg; - ExtAudioFileDispose (af); + if (initialize_peakfile (false, _path)) { + error << string_compose("CoreAudioSource: initialize peakfile failed (%1)", name()) << endmsg; throw failed_constructor (); } } @@ -137,41 +95,44 @@ CoreAudioSource::init (const string& idstr) CoreAudioSource::~CoreAudioSource () { + cerr << "CoreAudioSource::~CoreAudioSource() " << name() << endl; GoingAway (); /* EMIT SIGNAL */ - if (af) { - ExtAudioFileDispose (af); - } - if (tmpbuf) { delete [] tmpbuf; } + + cerr << "deletion done" << endl; } jack_nframes_t CoreAudioSource::read_unlocked (Sample *dst, jack_nframes_t start, jack_nframes_t cnt) const { - OSStatus err = noErr; - - err = ExtAudioFileSeek(af, start); - if (err != noErr) { - error << string_compose(_("CoreAudioSource: could not seek to frame %1 within %2 (%3)"), start, _name.substr (1), err) << endmsg; + try { + af.Seek (start); + } catch (CAXException& cax) { + error << string_compose("CoreAudioSource: %1 to %2 (%3)", cax.mOperation, start, _name.substr (1)) << endmsg; return 0; } AudioBufferList abl; abl.mNumberBuffers = 1; abl.mBuffers[0].mNumberChannels = n_channels; - abl.mBuffers[0].mDataByteSize = cnt * sizeof(Sample); - abl.mBuffers[0].mData = dst; + UInt32 new_cnt = cnt; if (n_channels == 1) { - err = ExtAudioFileRead(af, (UInt32*) &cnt, &abl); - _read_data_count = cnt * sizeof(float); - return cnt; + abl.mBuffers[0].mDataByteSize = cnt * sizeof(Sample); + abl.mBuffers[0].mData = dst; + try { + af.Read (new_cnt, &abl); + } catch (CAXException& cax) { + error << string_compose("CoreAudioSource: %1 (%2)", cax.mOperation, _name); + } + _read_data_count = new_cnt * sizeof(float); + return new_cnt; } - uint32_t real_cnt = cnt * n_channels; + UInt32 real_cnt = cnt * n_channels; { Glib::Mutex::Lock lm (_tmpbuf_lock); @@ -185,10 +146,16 @@ CoreAudioSource::read_unlocked (Sample *dst, jack_nframes_t start, jack_nframes_ tmpbuf = new float[tmpbufsize]; } - abl.mBuffers[0].mDataByteSize = real_cnt * sizeof(Sample); + abl.mBuffers[0].mDataByteSize = tmpbufsize * sizeof(Sample); abl.mBuffers[0].mData = tmpbuf; + + cerr << "channel: " << channel << endl; - err = ExtAudioFileRead(af, (UInt32*) &real_cnt, &abl); + try { + af.Read (real_cnt, &abl); + } catch (CAXException& cax) { + error << string_compose("CoreAudioSource: %1 (%2)", cax.mOperation, _name); + } float *ptr = tmpbuf + channel; real_cnt /= n_channels; @@ -208,15 +175,12 @@ CoreAudioSource::read_unlocked (Sample *dst, jack_nframes_t start, jack_nframes_ float CoreAudioSource::sample_rate() const { - AudioStreamBasicDescription client_asbd; - memset(&client_asbd, 0, sizeof(AudioStreamBasicDescription)); + CAStreamBasicDescription client_asbd; - OSStatus err = noErr; - size_t asbd_size = sizeof(AudioStreamBasicDescription); - - err = ExtAudioFileSetProperty (af, kExtAudioFileProperty_ClientDataFormat, asbd_size, &client_asbd); - if (err != noErr) { - error << string_compose(_("Could not detect samplerate for: %1"), name()) << endmsg; + try { + client_asbd = af.GetClientDataFormat (); + } catch (CAXException& cax) { + error << string_compose("CoreAudioSource: %1 (%2)", cax.mOperation, _name); return 0.0; } @@ -228,4 +192,3 @@ CoreAudioSource::update_header (jack_nframes_t when, struct tm&, time_t) { return 0; } - diff --git a/libs/ardour/destructive_filesource.cc b/libs/ardour/destructive_filesource.cc index bf16b40005..1e57d88d70 100644 --- a/libs/ardour/destructive_filesource.cc +++ b/libs/ardour/destructive_filesource.cc @@ -68,21 +68,21 @@ gain_t* DestructiveFileSource::out_coefficient = 0; gain_t* DestructiveFileSource::in_coefficient = 0; jack_nframes_t DestructiveFileSource::xfade_frames = 64; -DestructiveFileSource::DestructiveFileSource (string path, SampleFormat samp_format, HeaderFormat hdr_format, jack_nframes_t rate, Flag flags) - : SndFileSource (path, samp_format, hdr_format, rate, flags) +DestructiveFileSource::DestructiveFileSource (Session& s, string path, SampleFormat samp_format, HeaderFormat hdr_format, jack_nframes_t rate, Flag flags) + : SndFileSource (s, path, samp_format, hdr_format, rate, flags) { init (); } -DestructiveFileSource::DestructiveFileSource (string path, Flag flags) - : SndFileSource (path, flags) +DestructiveFileSource::DestructiveFileSource (Session& s, string path, Flag flags) + : SndFileSource (s, path, flags) { init (); } -DestructiveFileSource::DestructiveFileSource (const XMLNode& node) - : SndFileSource (node) +DestructiveFileSource::DestructiveFileSource (Session& s, const XMLNode& node) + : SndFileSource (s, node) { init (); } diff --git a/libs/ardour/globals.cc b/libs/ardour/globals.cc index 6f3b772ece..cc92529d88 100644 --- a/libs/ardour/globals.cc +++ b/libs/ardour/globals.cc @@ -375,7 +375,9 @@ ARDOUR::get_user_ardour_path () /* create it if necessary */ - mkdir (path.c_str (), 0755); + if (g_mkdir_with_parents (path.c_str (), 0755)) { + throw exception (); + } return path; } diff --git a/libs/ardour/import.cc b/libs/ardour/import.cc index 01c7182e14..b10f76424b 100644 --- a/libs/ardour/import.cc +++ b/libs/ardour/import.cc @@ -140,7 +140,7 @@ Session::import_audiofile (import_status& status) try { newfiles[n] = boost::dynamic_pointer_cast ( - SourceFactory::createWritable (DataType::AUDIO, buf, false, frame_rate())); + SourceFactory::createWritable (DataType::AUDIO, *this, buf, false, frame_rate())); } catch (failed_constructor& err) { diff --git a/libs/ardour/io.cc b/libs/ardour/io.cc index a30881b4da..57a89b2310 100644 --- a/libs/ardour/io.cc +++ b/libs/ardour/io.cc @@ -20,6 +20,7 @@ #include #include #include +#include #include @@ -2143,8 +2144,8 @@ IO::load_automation (const string& path) fullpath += path; in.open (fullpath.c_str()); if (!in) { - error << string_compose(_("%1: cannot open automation event file \"%2\""), _name, fullpath) << endmsg; - return -1; + error << string_compose(_("%1: cannot open automation event file \"%2\" (%2)"), _name, fullpath, strerror (errno)) << endmsg; + return -1; } } diff --git a/libs/ardour/location.cc b/libs/ardour/location.cc index 30b28c6abe..6b3ea6f220 100644 --- a/libs/ardour/location.cc +++ b/libs/ardour/location.cc @@ -81,13 +81,18 @@ Location::set_start (jack_nframes_t s) { if (is_mark()) { if (_start != s) { + _start = s; _end = s; + start_changed(this); /* EMIT SIGNAL */ + if ( is_start() ) { + Session::StartTimeChanged (); /* EMIT SIGNAL */ AudioFileSource::set_header_position_offset ( s ); } + if ( is_end() ) { Session::EndTimeChanged (); /* EMIT SIGNAL */ } diff --git a/libs/ardour/midi_source.cc b/libs/ardour/midi_source.cc index c4cfe3c71d..de2f6b1c5d 100644 --- a/libs/ardour/midi_source.cc +++ b/libs/ardour/midi_source.cc @@ -42,15 +42,15 @@ using namespace PBD; sigc::signal MidiSource::MidiSourceCreated; -MidiSource::MidiSource (string name) - : Source (name, DataType::MIDI) +MidiSource::MidiSource (Session& s, string name) + : Source (s, name, DataType::MIDI) { _read_data_count = 0; _write_data_count = 0; } -MidiSource::MidiSource (const XMLNode& node) - : Source (node) +MidiSource::MidiSource (Session& s, const XMLNode& node) + : Source (s, node) { _read_data_count = 0; _write_data_count = 0; diff --git a/libs/ardour/panner.cc b/libs/ardour/panner.cc index 1ced0faf89..0c3b0a291f 100644 --- a/libs/ardour/panner.cc +++ b/libs/ardour/panner.cc @@ -1090,7 +1090,7 @@ Panner::save () const ofstream out (automation_path.c_str()); if (!out) { - error << string_compose (_("cannot open pan automation file \"%1\" for saving (%s)"), automation_path, strerror (errno)) + error << string_compose (_("cannot open pan automation file \"%1\" for saving (%2)"), automation_path, strerror (errno)) << endmsg; return -1; } diff --git a/libs/ardour/plugin.cc b/libs/ardour/plugin.cc index 8d200b0ee4..9a82c3bbab 100644 --- a/libs/ardour/plugin.cc +++ b/libs/ardour/plugin.cc @@ -224,13 +224,13 @@ Plugin::save_preset (string name, string domain) free(lrdf_add_preset(source.c_str(), name.c_str(), unique_id(), &defaults)); string path = string_compose("%1/.%2", envvar, domain); - if (mkdir(path.c_str(), 0775) && errno != EEXIST) { + if (g_mkdir_with_parents (path.c_str(), 0775)) { warning << string_compose(_("Could not create %1. Preset not saved. (%2)"), path, strerror(errno)) << endmsg; return false; } path += "/rdf"; - if (mkdir(path.c_str(), 0775) && errno != EEXIST) { + if (g_mkdir_with_parents (path.c_str(), 0775)) { warning << string_compose(_("Could not create %1. Preset not saved. (%2)"), path, strerror(errno)) << endmsg; return false; } diff --git a/libs/ardour/route.cc b/libs/ardour/route.cc index 672e0692cb..398df02179 100644 --- a/libs/ardour/route.cc +++ b/libs/ardour/route.cc @@ -277,17 +277,17 @@ Route::process_output_buffers (BufferSet& bufs, -------------------------------------------------------------------------------------------------- */ if (declick > 0) { - Amp::run (bufs, nframes, 0.0, 1.0, _phase_invert); + Amp::run (bufs, nframes, 0.0, 1.0, false); _pending_declick = 0; } else if (declick < 0) { - Amp::run (bufs, nframes, 1.0, 0.0, _phase_invert); + Amp::run (bufs, nframes, 1.0, 0.0, false); _pending_declick = 0; } else { /* no global declick */ if (solo_gain != dsg) { - Amp::run (bufs, nframes, solo_gain, dsg, _phase_invert); + Amp::run (bufs, nframes, solo_gain, dsg, false); solo_gain = dsg; } } @@ -302,7 +302,7 @@ Route::process_output_buffers (BufferSet& bufs, } if (!_soloed && _mute_affects_pre_fader && (mute_gain != dmg)) { - Amp::run (bufs, nframes, mute_gain, dmg, _phase_invert); + Amp::run (bufs, nframes, mute_gain, dmg, false); mute_gain = dmg; mute_declick_applied = true; } @@ -379,7 +379,7 @@ Route::process_output_buffers (BufferSet& bufs, if (!_soloed && (mute_gain != dmg) && !mute_declick_applied && _mute_affects_post_fader) { - Amp::run (bufs, nframes, mute_gain, dmg, _phase_invert); + Amp::run (bufs, nframes, mute_gain, dmg, false); mute_gain = dmg; mute_declick_applied = true; } @@ -543,7 +543,7 @@ Route::process_output_buffers (BufferSet& bufs, } if (!_soloed && (mute_gain != dmg) && !mute_declick_applied && _mute_affects_control_outs) { - Amp::run (bufs, nframes, mute_gain, dmg, _phase_invert); + Amp::run (bufs, nframes, mute_gain, dmg, false); mute_gain = dmg; mute_declick_applied = true; } @@ -588,7 +588,7 @@ Route::process_output_buffers (BufferSet& bufs, ----------------------------------------------------------------------*/ if (!_soloed && (mute_gain != dmg) && !mute_declick_applied && _mute_affects_main_outs) { - Amp::run (bufs, nframes, mute_gain, dmg, _phase_invert); + Amp::run (bufs, nframes, mute_gain, dmg, false); mute_gain = dmg; mute_declick_applied = true; } diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index ebdbea1fc4..b670afb1ba 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -87,10 +87,11 @@ using boost::shared_ptr; const char* Session::_template_suffix = X_(".template"); const char* Session::_statefile_suffix = X_(".ardour"); const char* Session::_pending_suffix = X_(".pending"); -const char* Session::sound_dir_name = X_("sounds"); -const char* Session::tape_dir_name = X_("tapes"); +const char* Session::old_sound_dir_name = X_("sounds"); +const char* Session::sound_dir_name = X_("audiofiles"); const char* Session::peak_dir_name = X_("peaks"); const char* Session::dead_sound_dir_name = X_("dead_sounds"); +const char* Session::interchange_dir_name = X_("interchange"); Session::compute_peak_t Session::compute_peak = 0; Session::apply_gain_to_buffer_t Session::apply_gain_to_buffer = 0; @@ -2836,17 +2837,11 @@ Session::source_by_id (const PBD::ID& id) } string -Session::peak_path_from_audio_path (string audio_path) +Session::peak_path_from_audio_path (string audio_path) const { - /* XXX hardly bombproof! fix me */ - string res; - res = Glib::path_get_dirname (audio_path); - res = Glib::path_get_dirname (res); - res += '/'; - res += peak_dir_name; - res += '/'; + res = peak_dir (); res += PBD::basename_nosuffix (audio_path); res += ".peak"; @@ -2992,11 +2987,7 @@ Session::audio_path_from_name (string name, uint32_t nchan, uint32_t chan, bool spath = (*i).path; - if (destructive) { - spath += tape_dir_name; - } else { - spath += sound_dir_name; - } + spath += sound_dir (false); if (destructive) { if (nchan < 2) { @@ -3032,9 +3023,10 @@ Session::audio_path_from_name (string name, uint32_t nchan, uint32_t chan, bool } } - if (access (buf, F_OK) == 0) { + if (g_file_test (buf, G_FILE_TEST_EXISTS)) { existing++; - } + } + } if (existing == 0) { @@ -3053,11 +3045,7 @@ Session::audio_path_from_name (string name, uint32_t nchan, uint32_t chan, bool string foo = buf; - if (destructive) { - spath = tape_dir (); - } else { - spath = discover_best_sound_dir (); - } + spath = discover_best_sound_dir (); string::size_type pos = foo.find_last_of ('/'); @@ -3074,7 +3062,8 @@ boost::shared_ptr Session::create_audio_source_for_session (AudioDiskstream& ds, uint32_t chan, bool destructive) { string spath = audio_path_from_name (ds.name(), ds.n_channels().get(DataType::AUDIO), chan, destructive); - return boost::dynamic_pointer_cast (SourceFactory::createWritable (DataType::AUDIO, spath, destructive, frame_rate())); + return boost::dynamic_pointer_cast ( + SourceFactory::createWritable (DataType::AUDIO, *this, spath, destructive, frame_rate())); } // FIXME: _terrible_ code duplication @@ -3260,7 +3249,7 @@ Session::create_midi_source_for_session (MidiDiskstream& ds) { string spath = midi_path_from_name (ds.name()); - return boost::dynamic_pointer_cast (SourceFactory::createWritable (DataType::MIDI, spath, false, frame_rate())); + return boost::dynamic_pointer_cast (SourceFactory::createWritable (DataType::MIDI, *this, spath, false, frame_rate())); } @@ -3420,17 +3409,35 @@ Session::RoutePublicOrderSorter::operator() (boost::shared_ptr a, boost:: void Session::remove_empty_sounds () { - PathScanner scanner; - string dir; - - dir = sound_dir (); - vector* possible_audiofiles = scanner (dir, "\\.wav$", false, true); + vector* possible_audiofiles = scanner (sound_dir(), "\\.(wav|aiff|caf|w64)$", false, true); + Glib::Mutex::Lock lm (source_lock); + + regex_t compiled_tape_track_pattern; + int err; + + if ((err = regcomp (&compiled_tape_track_pattern, "/T[0-9][0-9][0-9][0-9]-", REG_EXTENDED|REG_NOSUB))) { + + char msg[256]; + + regerror (err, &compiled_tape_track_pattern, msg, sizeof (msg)); + + error << string_compose (_("Cannot compile tape track regexp for use (%1)"), msg) << endmsg; + return; + } + for (vector::iterator i = possible_audiofiles->begin(); i != possible_audiofiles->end(); ++i) { + + /* never remove files that appear to be a tape track */ - if (AudioFileSource::is_empty (*(*i))) { + if (regexec (&compiled_tape_track_pattern, (*i)->c_str(), 0, 0, 0) == 0) { + delete *i; + continue; + } + + if (AudioFileSource::is_empty (*this, *(*i))) { unlink ((*i)->c_str()); @@ -3610,7 +3617,7 @@ jack_nframes_t Session::available_capture_duration () { const double scale = 4096.0 / sizeof (Sample); - + if (_total_free_4k_blocks * scale > (double) max_frames) { return max_frames; } @@ -3914,7 +3921,8 @@ Session::write_one_audio_track (AudioTrack& track, jack_nframes_t start, jack_nf } try { - fsource = boost::dynamic_pointer_cast (SourceFactory::createWritable (DataType::AUDIO, buf, false, frame_rate())); + fsource = boost::dynamic_pointer_cast ( + SourceFactory::createWritable (DataType::AUDIO, *this, buf, false, frame_rate())); } catch (failed_constructor& err) { @@ -3931,7 +3939,7 @@ Session::write_one_audio_track (AudioTrack& track, jack_nframes_t start, jack_nf to_do = len; /* create a set of reasonably-sized buffers */ - buffers.ensure_buffers(nchans, chunk_size); +buffers.ensure_buffers(nchans, chunk_size); buffers.set_count(nchans); while (to_do && !itt.cancel) { diff --git a/libs/ardour/session_state.cc b/libs/ardour/session_state.cc index eaaa723a0c..a8b2804ff0 100644 --- a/libs/ardour/session_state.cc +++ b/libs/ardour/session_state.cc @@ -309,7 +309,6 @@ Session::second_stage_init (bool new_session) if (!new_session) { if (load_state (_current_snapshot_name)) { - cerr << "load state failed\n"; return -1; } remove_empty_sounds (); @@ -371,7 +370,6 @@ Session::second_stage_init (bool new_session) _end_location_is_free = false; } - restore_history(_current_snapshot_name); return 0; } @@ -436,16 +434,7 @@ Session::setup_raid_path (string path) if (fspath[fspath.length()-1] != '/') { fspath += '/'; } - fspath += sound_dir_name; - fspath += ':'; - - /* tape dir */ - - fspath += sp.path; - if (fspath[fspath.length()-1] != '/') { - fspath += '/'; - } - fspath += tape_dir_name; + fspath += sound_dir (false); AudioFileSource::set_search_path (fspath); SMFSource::set_search_path (fspath); // FIXME: should be different @@ -467,16 +456,7 @@ Session::setup_raid_path (string path) if (fspath[fspath.length()-1] != '/') { fspath += '/'; } - fspath += sound_dir_name; - fspath += ':'; - - /* add tape dir to file search path */ - - fspath += sp.path; - if (fspath[fspath.length()-1] != '/') { - fspath += '/'; - } - fspath += tape_dir_name; + fspath += sound_dir (false); fspath += ':'; remaining = remaining.substr (colon+1); @@ -492,15 +472,9 @@ Session::setup_raid_path (string path) if (fspath[fspath.length()-1] != '/') { fspath += '/'; } - fspath += sound_dir_name; + fspath += sound_dir (false); fspath += ':'; - fspath += sp.path; - if (fspath[fspath.length()-1] != '/') { - fspath += '/'; - } - fspath += tape_dir_name; - session_dirs.push_back (sp); } @@ -518,61 +492,40 @@ int Session::create (bool& new_session, string* mix_template, jack_nframes_t initial_length) { string dir; + + new_session = !g_file_test (_path.c_str(), GFileTest (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)); - if (mkdir (_path.c_str(), 0755) < 0) { - if (errno == EEXIST) { - new_session = false; - } else { - error << string_compose(_("Session: cannot create session dir \"%1\" (%2)"), _path, strerror (errno)) << endmsg; - return -1; - } - } else { - new_session = true; + if (g_mkdir_with_parents (_path.c_str(), 0755) < 0) { + error << string_compose(_("Session: cannot create session dir \"%1\" (%2)"), _path, strerror (errno)) << endmsg; + return -1; } dir = peak_dir (); - if (mkdir (dir.c_str(), 0755) < 0) { - if (errno != EEXIST) { - error << string_compose(_("Session: cannot create session peakfile dir \"%1\" (%2)"), dir, strerror (errno)) << endmsg; - return -1; - } + if (g_mkdir_with_parents (dir.c_str(), 0755) < 0) { + error << string_compose(_("Session: cannot create session peakfile dir \"%1\" (%2)"), dir, strerror (errno)) << endmsg; + return -1; } dir = sound_dir (); - if (mkdir (dir.c_str(), 0755) < 0) { - if (errno != EEXIST) { - error << string_compose(_("Session: cannot create session sounds dir \"%1\" (%2)"), dir, strerror (errno)) << endmsg; - return -1; - } - } - - dir = tape_dir (); - - if (mkdir (dir.c_str(), 0755) < 0) { - if (errno != EEXIST) { - error << string_compose(_("Session: cannot create session tape dir \"%1\" (%2)"), dir, strerror (errno)) << endmsg; - return -1; - } + if (g_mkdir_with_parents (dir.c_str(), 0755) < 0) { + error << string_compose(_("Session: cannot create session sounds dir \"%1\" (%2)"), dir, strerror (errno)) << endmsg; + return -1; } dir = dead_sound_dir (); - if (mkdir (dir.c_str(), 0755) < 0) { - if (errno != EEXIST) { - error << string_compose(_("Session: cannot create session dead sounds dir \"%1\" (%2)"), dir, strerror (errno)) << endmsg; - return -1; - } + if (g_mkdir_with_parents (dir.c_str(), 0755) < 0) { + error << string_compose(_("Session: cannot create session dead sounds dir \"%1\" (%2)"), dir, strerror (errno)) << endmsg; + return -1; } dir = automation_dir (); - if (mkdir (dir.c_str(), 0755) < 0) { - if (errno != EEXIST) { - error << string_compose(_("Session: cannot create session automation dir \"%1\" (%2)"), dir, strerror (errno)) << endmsg; - return -1; - } + if (g_mkdir_with_parents (dir.c_str(), 0755) < 0) { + error << string_compose(_("Session: cannot create session automation dir \"%1\" (%2)"), dir, strerror (errno)) << endmsg; + return -1; } @@ -725,6 +678,8 @@ Session::save_state (string snapshot_name, bool pending) } + cerr << "actually writing state\n"; + if (!tree.write (xml_path)) { error << string_compose (_("state could not be saved to %1"), xml_path) << endmsg; @@ -2031,13 +1986,12 @@ Session::load_sources (const XMLNode& node) boost::shared_ptr Session::XMLSourceFactory (const XMLNode& node) { - if (node.name() != "Source") { return boost::shared_ptr(); } try { - return SourceFactory::create (node); + return SourceFactory::create (*this, node); } catch (failed_constructor& err) { @@ -2062,7 +2016,7 @@ Session::save_template (string template_name) if ((dp = opendir (dir.c_str()))) { closedir (dp); } else { - if (mkdir (dir.c_str(), S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)<0) { + if (g_mkdir_with_parents (dir.c_str(), S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)) { error << string_compose(_("Could not create mix templates directory \"%1\" (%2)"), dir, strerror (errno)) << endmsg; return -1; } @@ -2142,11 +2096,9 @@ Session::ensure_sound_dir (string path, string& result) /* Ensure that the parent directory exists */ - if (mkdir (path.c_str(), 0775)) { - if (errno != EEXIST) { - error << string_compose(_("cannot create session directory \"%1\"; ignored"), path) << endmsg; - return -1; - } + if (g_mkdir_with_parents (path.c_str(), 0775)) { + error << string_compose(_("cannot create session directory \"%1\"; ignored"), path) << endmsg; + return -1; } /* Ensure that the sounds directory exists */ @@ -2155,33 +2107,27 @@ Session::ensure_sound_dir (string path, string& result) result += '/'; result += sound_dir_name; - if (mkdir (result.c_str(), 0775)) { - if (errno != EEXIST) { - error << string_compose(_("cannot create sounds directory \"%1\"; ignored"), result) << endmsg; - return -1; - } + if (g_mkdir_with_parents (result.c_str(), 0775)) { + error << string_compose(_("cannot create sounds directory \"%1\"; ignored"), result) << endmsg; + return -1; } dead = path; dead += '/'; dead += dead_sound_dir_name; - if (mkdir (dead.c_str(), 0775)) { - if (errno != EEXIST) { - error << string_compose(_("cannot create dead sounds directory \"%1\"; ignored"), dead) << endmsg; - return -1; - } + if (g_mkdir_with_parents (dead.c_str(), 0775)) { + error << string_compose(_("cannot create dead sounds directory \"%1\"; ignored"), dead) << endmsg; + return -1; } peak = path; peak += '/'; peak += peak_dir_name; - if (mkdir (peak.c_str(), 0775)) { - if (errno != EEXIST) { - error << string_compose(_("cannot create peak file directory \"%1\"; ignored"), peak) << endmsg; - return -1; - } + if (g_mkdir_with_parents (peak.c_str(), 0775)) { + error << string_compose(_("cannot create peak file directory \"%1\"; ignored"), peak) << endmsg; + return -1; } /* callers expect this to be terminated ... */ @@ -2196,12 +2142,6 @@ Session::discover_best_sound_dir (bool destructive) vector::iterator i; string result; - /* destructive files all go into the same place */ - - if (destructive) { - return tape_dir(); - } - /* handle common case without system calls */ if (session_dirs.size() == 1) { @@ -2418,20 +2358,37 @@ Session::dead_sound_dir () const } string -Session::sound_dir () const +Session::sound_dir (bool with_path) const { - string res = _path; + /* support old session structure */ + + struct stat statbuf; + string old; + + if (with_path) { + old = _path; + } + + old += sound_dir_name; + old += '/'; + + if (stat (old.c_str(), &statbuf) == 0) { + return old; + } + + string res; + + if (with_path) { + res = _path; + } + + res += interchange_dir_name; + res += '/'; + res += legalize_for_path (_name); + res += '/'; res += sound_dir_name; res += '/'; - return res; -} -string -Session::tape_dir () const -{ - string res = _path; - res += tape_dir_name; - res += '/'; return res; } @@ -3427,8 +3384,7 @@ Session::save_history (string snapshot_name) XMLTree tree; string xml_path; string bak_path; - - + tree.set_root (&history.get_state()); if (snapshot_name.empty()) { @@ -3436,7 +3392,6 @@ Session::save_history (string snapshot_name) } xml_path = _path + snapshot_name + ".history"; - cerr << "Saving history to " << xml_path << endmsg; bak_path = xml_path + ".bak"; @@ -3447,6 +3402,8 @@ Session::save_history (string snapshot_name) return -1; } + cerr << "actually writing history\n"; + if (!tree.write (xml_path)) { error << string_compose (_("history could not be saved to %1"), xml_path) << endmsg; diff --git a/libs/ardour/session_time.cc b/libs/ardour/session_time.cc index c74d3021cb..887a9fa6c4 100644 --- a/libs/ardour/session_time.cc +++ b/libs/ardour/session_time.cc @@ -51,7 +51,7 @@ Session::bbt_time (jack_nframes_t when, BBT_Time& bbt) void Session::sync_time_vars () { - _current_frame_rate = _base_frame_rate * (1.0 + (video_pullup/100.0) ); + _current_frame_rate = (jack_nframes_t) round (_base_frame_rate * (1.0 + (video_pullup/100.0))); _frames_per_hour = _current_frame_rate * 3600; _frames_per_smpte_frame = (double) _current_frame_rate / (double) smpte_frames_per_second; _smpte_frames_per_hour = (unsigned long) (smpte_frames_per_second * 3600.0); @@ -432,9 +432,11 @@ Session::jack_timebase_callback (jack_transport_state_t state, pos->valid = jack_position_bits_t (pos->valid | JackPositionBBT); } +#ifdef HAVE_JACK_VIDEO_SUPPORT //poke audio video ratio so Ardour can track Video Sync pos->audio_frames_per_video_frame = frame_rate() / smpte_frames_per_second; pos->valid = jack_position_bits_t (pos->valid | JackAudioVideoRatio); +#endif #if 0 /* SMPTE info */ diff --git a/libs/ardour/session_timefx.cc b/libs/ardour/session_timefx.cc index 4ba8d42d9e..b5b143514c 100644 --- a/libs/ardour/session_timefx.cc +++ b/libs/ardour/session_timefx.cc @@ -82,8 +82,7 @@ Session::tempoize_region (TimeStretchRequest& tsr) } try { - sources.push_back (boost::dynamic_pointer_cast ( - SourceFactory::createWritable (DataType::AUDIO, path, false, frame_rate()))); + sources.push_back (boost::dynamic_pointer_cast (SourceFactory::createWritable (DataType::AUDIO, *this, path, false, frame_rate()))); } catch (failed_constructor& err) { error << string_compose (_("tempoize: error creating new audio file %1 (%2)"), path, strerror (errno)) << endmsg; diff --git a/libs/ardour/smf_source.cc b/libs/ardour/smf_source.cc index e1845a009f..0e3064503a 100644 --- a/libs/ardour/smf_source.cc +++ b/libs/ardour/smf_source.cc @@ -48,8 +48,8 @@ bool SMFSource::header_position_negative; uint64_t SMFSource::header_position_offset; */ -SMFSource::SMFSource (std::string path, Flag flags) - : MidiSource (region_name_from_path(path)) +SMFSource::SMFSource (Session& s, std::string path, Flag flags) + : MidiSource (s, region_name_from_path(path)) , _channel(0) , _flags (Flag(flags | Writable)) // FIXME: this needs to be writable for now , _allow_remove_if_empty(true) @@ -72,8 +72,8 @@ SMFSource::SMFSource (std::string path, Flag flags) assert(_name.find("/") == string::npos); } -SMFSource::SMFSource (const XMLNode& node) - : MidiSource (node) +SMFSource::SMFSource (Session& s, const XMLNode& node) + : MidiSource (s, node) , _channel(0) , _flags (Flag (Writable|CanRename)) , _allow_remove_if_empty(true) diff --git a/libs/ardour/sndfilesource.cc b/libs/ardour/sndfilesource.cc index b6ded6d617..0e80dee714 100644 --- a/libs/ardour/sndfilesource.cc +++ b/libs/ardour/sndfilesource.cc @@ -34,8 +34,8 @@ using namespace std; using namespace ARDOUR; using namespace PBD; -SndFileSource::SndFileSource (const XMLNode& node) - : AudioFileSource (node) +SndFileSource::SndFileSource (Session& s, const XMLNode& node) + : AudioFileSource (s, node) { init (_name); @@ -52,9 +52,9 @@ SndFileSource::SndFileSource (const XMLNode& node) } } -SndFileSource::SndFileSource (string idstr, Flag flags) +SndFileSource::SndFileSource (Session& s, string idstr, Flag flags) /* files created this way are never writable or removable */ - : AudioFileSource (idstr, Flag (flags & ~(Writable|Removable|RemovableIfEmpty|RemoveAtDestroy))) + : AudioFileSource (s, idstr, Flag (flags & ~(Writable|Removable|RemovableIfEmpty|RemoveAtDestroy))) { init (idstr); @@ -71,8 +71,8 @@ SndFileSource::SndFileSource (string idstr, Flag flags) } } -SndFileSource::SndFileSource (string idstr, SampleFormat sfmt, HeaderFormat hf, jack_nframes_t rate, Flag flags) - : AudioFileSource(idstr, flags, sfmt, hf) +SndFileSource::SndFileSource (Session& s, string idstr, SampleFormat sfmt, HeaderFormat hf, jack_nframes_t rate, Flag flags) + : AudioFileSource (s, idstr, flags, sfmt, hf) { int fmt = 0; @@ -226,7 +226,6 @@ SndFileSource::open () _length = _info.frames; - _broadcast_info = new SF_BROADCAST_INFO; memset (_broadcast_info, 0, sizeof (*_broadcast_info)); @@ -507,6 +506,9 @@ SndFileSource::set_header_timeline_position () delete _broadcast_info; _broadcast_info = 0; } + + + } jack_nframes_t diff --git a/libs/ardour/source.cc b/libs/ardour/source.cc index d218e2cf94..977aea6efd 100644 --- a/libs/ardour/source.cc +++ b/libs/ardour/source.cc @@ -42,11 +42,9 @@ using std::max; using namespace ARDOUR; -sigc::signal Source::SourceCreated; - - -Source::Source (string name, DataType type) - : _type(type) +Source::Source (Session& s, string name, DataType type) + : _session (s) + , _type(type) { assert(_name.find("/") == string::npos); @@ -55,8 +53,9 @@ Source::Source (string name, DataType type) _length = 0; } -Source::Source (const XMLNode& node) - : _type(DataType::AUDIO) +Source::Source (Session& s, const XMLNode& node) + : _session (s) + , _type(DataType::AUDIO) { _timestamp = 0; _length = 0; diff --git a/libs/ardour/source_factory.cc b/libs/ardour/source_factory.cc index 61a7659a5e..29cd5166ff 100644 --- a/libs/ardour/source_factory.cc +++ b/libs/ardour/source_factory.cc @@ -37,7 +37,7 @@ sigc::signal > SourceFactory::SourceCreated; #ifdef HAVE_COREAUDIO boost::shared_ptr -SourceFactory::create (const XMLNode& node) +SourceFactory::create (Session& s, const XMLNode& node) { DataType type = DataType::AUDIO; const XMLProperty* prop = node.property("type"); @@ -49,11 +49,18 @@ SourceFactory::create (const XMLNode& node) if (node.property (X_("destructive")) != 0) { - boost::shared_ptr ret (new DestructiveFileSource (node)); + boost::shared_ptr ret (new DestructiveFileSource (s, node)); SourceCreated (ret); return ret; } else { + + try { + boost::shared_ptr ret (new CoreAudioSource (s, node)); + SourceCreated (ret); + return ret; + + } catch (failed_constructor& err) { try { boost::shared_ptr ret (new CoreAudioSource (node)); @@ -83,7 +90,7 @@ SourceFactory::create (const XMLNode& node) #else boost::shared_ptr -SourceFactory::create (const XMLNode& node) +SourceFactory::create (Session& s, const XMLNode& node) { DataType type = DataType::AUDIO; const XMLProperty* prop = node.property("type"); @@ -95,20 +102,20 @@ SourceFactory::create (const XMLNode& node) if (node.property (X_("destructive")) != 0) { - boost::shared_ptr ret (new DestructiveFileSource (node)); + boost::shared_ptr ret (new DestructiveFileSource (s, node)); SourceCreated (ret); return ret; } else { - boost::shared_ptr ret (new SndFileSource (node)); + boost::shared_ptr ret (new SndFileSource (s, node)); SourceCreated (ret); return ret; } } else if (type == DataType::MIDI) { - - boost::shared_ptr ret (new SMFSource (node)); + + boost::shared_ptr ret (new SMFSource (s, node)); SourceCreated (ret); return ret; @@ -121,35 +128,34 @@ SourceFactory::create (const XMLNode& node) #ifdef HAVE_COREAUDIO boost::shared_ptr -SourceFactory::createReadable (string idstr, AudioFileSource::Flag flags, bool announce) +SourceFactory::createReadable (DataType type, Session& s, string idstr, AudioFileSource::Flag flags, bool announce) { if (type == DataType::AUDIO) { if (flags & Destructive) { - boost::shared_ptr ret (new DestructiveFileSource (idstr, flags)); + boost::shared_ptr ret (new DestructiveFileSource (s, idstr, flags)); if (announce) { SourceCreated (ret); } return ret; - } - - try { - boost::shared_ptr ret (new CoreAudioSource (idstr, flags)); - if (announce) { - SourceCreated (ret); + + try { + boost::shared_ptr ret (new CoreAudioSource (s, idstr, flags)); + if (announce) { + SourceCreated (ret); + } + return ret; + + } catch (failed_constructor& err) { + boost::shared_ptr ret (new SndFileSource (s, idstr, flags)); + if (announce) { + SourceCreated (ret); + } + return ret; } - return ret; - } - catch (failed_constructor& err) { - boost::shared_ptr ret (new SndFileSource (idstr, flags)); - if (announce) { - SourceCreated (ret); - } - return ret; - } } else if (type == DataType::MIDI) { - boost::shared_ptr ret (new SMFSource (node)); + boost::shared_ptr ret (new SMFSource (s, node)); SourceCreated (ret); return ret; @@ -161,10 +167,11 @@ SourceFactory::createReadable (string idstr, AudioFileSource::Flag flags, bool a #else boost::shared_ptr -SourceFactory::createReadable (DataType type, string idstr, AudioFileSource::Flag flags, bool announce) +SourceFactory::createReadable (DataType type, Session& s, string idstr, AudioFileSource::Flag flags, bool announce) { if (type == DataType::AUDIO) { - boost::shared_ptr ret (new SndFileSource (idstr, flags)); + + boost::shared_ptr ret (new SndFileSource (s, idstr, flags)); if (announce) { SourceCreated (ret); } @@ -172,8 +179,10 @@ SourceFactory::createReadable (DataType type, string idstr, AudioFileSource::Fla } else if (type == DataType::MIDI) { - boost::shared_ptr ret (new SMFSource (idstr, SMFSource::Flag(0))); // FIXME: flags? - SourceCreated (ret); + boost::shared_ptr ret (new SMFSource (s, idstr, SMFSource::Flag(0))); // FIXME: flags? + if (announce) { + SourceCreated (ret); + } return ret; } @@ -184,13 +193,14 @@ SourceFactory::createReadable (DataType type, string idstr, AudioFileSource::Fla #endif // HAVE_COREAUDIO boost::shared_ptr -SourceFactory::createWritable (DataType type, std::string path, bool destructive, jack_nframes_t rate, bool announce) +SourceFactory::createWritable (DataType type, Session& s, std::string path, bool destructive, jack_nframes_t rate, bool announce) { /* this might throw failed_constructor(), which is OK */ if (type == DataType::AUDIO) { if (destructive) { - boost::shared_ptr ret (new DestructiveFileSource (path, + + boost::shared_ptr ret (new DestructiveFileSource (s, path, Config->get_native_file_data_format(), Config->get_native_file_header_format(), rate)); @@ -200,7 +210,8 @@ SourceFactory::createWritable (DataType type, std::string path, bool destructive return ret; } else { - boost::shared_ptr ret (new SndFileSource (path, + + boost::shared_ptr ret (new SndFileSource (s, path, Config->get_native_file_data_format(), Config->get_native_file_header_format(), rate)); @@ -208,13 +219,15 @@ SourceFactory::createWritable (DataType type, std::string path, bool destructive SourceCreated (ret); } return ret; + } + } else if (type == DataType::MIDI) { - boost::shared_ptr ret (new SMFSource (path)); + boost::shared_ptr ret (new SMFSource (s, path)); SourceCreated (ret); return ret; - + } return boost::shared_ptr (); diff --git a/libs/ardour/tempo.cc b/libs/ardour/tempo.cc index 02884f062b..c2ff4f9a3a 100644 --- a/libs/ardour/tempo.cc +++ b/libs/ardour/tempo.cc @@ -919,6 +919,33 @@ jack_nframes_t TempoMap::round_to_beat_subdivision (jack_nframes_t fr, int sub_num) { + + BBT_Time the_beat; + uint32_t ticks_one_half_subdivisions_worth; + uint32_t ticks_one_subdivisions_worth; + + bbt_time(fr, the_beat); + + ticks_one_subdivisions_worth = (uint32_t)Meter::ticks_per_beat / sub_num; + ticks_one_half_subdivisions_worth = ticks_one_subdivisions_worth / 2; + + if (the_beat.ticks % ticks_one_subdivisions_worth > ticks_one_half_subdivisions_worth) { + uint32_t difference = ticks_one_subdivisions_worth - (the_beat.ticks % ticks_one_subdivisions_worth); + if (the_beat.ticks + difference >= (uint32_t)Meter::ticks_per_beat) { + the_beat.beats++; + the_beat.ticks += difference; + the_beat.ticks -= (uint32_t)Meter::ticks_per_beat; + } else { + the_beat.ticks += difference; + } + } else { + the_beat.ticks -= the_beat.ticks % ticks_one_subdivisions_worth; + } + + return frame_time (the_beat); + + /* XXX just keeping this for reference + TempoMap::BBTPointList::iterator i; TempoMap::BBTPointList *more_zoomed_bbt_points; jack_nframes_t frame_one_beats_worth; @@ -970,6 +997,9 @@ TempoMap::round_to_beat_subdivision (jack_nframes_t fr, int sub_num) delete more_zoomed_bbt_points; return fr ; + + */ + } jack_nframes_t diff --git a/libs/ardour/utils.cc b/libs/ardour/utils.cc index 8d9d799a44..ce4f4accd3 100644 --- a/libs/ardour/utils.cc +++ b/libs/ardour/utils.cc @@ -287,7 +287,7 @@ compute_equal_power_fades (jack_nframes_t nframes, float* in, float* out) in[0] = 0.0f; - for (int i = 1; i < nframes - 1; ++i) { + for (jack_nframes_t i = 1; i < nframes - 1; ++i) { in[i] = in[i-1] + step; } diff --git a/libs/ardour/vst_plugin.cc b/libs/ardour/vst_plugin.cc index dbb7547052..5dd32f2d51 100644 --- a/libs/ardour/vst_plugin.cc +++ b/libs/ardour/vst_plugin.cc @@ -174,9 +174,9 @@ VSTPlugin::get_state() if (stat (path.c_str(), &sbuf)) { if (errno == ENOENT) { - if (mkdir (path.c_str(), 0600)) { + if (g_mkdir_with_parents (path.c_str(), 0600)) { error << string_compose (_("cannot create VST chunk directory: %1"), - strerror (errno)) + strerror (errno)) << endmsg; return *root; } diff --git a/libs/gtkmm2ext/dndtreeview.cc b/libs/gtkmm2ext/dndtreeview.cc index d7ee5a7b27..83955861cf 100644 --- a/libs/gtkmm2ext/dndtreeview.cc +++ b/libs/gtkmm2ext/dndtreeview.cc @@ -48,6 +48,8 @@ DnDTreeView::serialize_pointers (RefPtr model, TreeSelection::ListHan uint32_t cnt = selection->size(); uint32_t sz = (sizeof (void*) * cnt) + sizeof (SerializedObjectPointers); + cerr << "lets plan to serialize " << cnt << " from selection\n"; + char* buf = new char[sz]; SerializedObjectPointers* sr = new (buf) SerializedObjectPointers; @@ -59,10 +61,12 @@ DnDTreeView::serialize_pointers (RefPtr model, TreeSelection::ListHan cnt = 0; for (TreeSelection::ListHandle_Path::iterator x = selection->begin(); x != selection->end(); ++x, ++cnt) { + cerr << "getting next item\n"; TreeModel::Row row = *(model->get_iter (*x)); row.get_value (data_column, sr->ptr[cnt]); } - + + cerr << "returning an SR with size = " << sr->size << endl; return sr; } @@ -79,6 +83,7 @@ DnDTreeView::on_drag_data_get(const RefPtr& context, SelectionData& SerializedObjectPointers* sr = serialize_pointers (get_model(), &selection, selection_data.get_target()); selection_data.set (8, (guchar*)sr, sr->size); + cerr << "selection data set to contain " << sr->size << endl; } } diff --git a/libs/pbd/pbd/stateful.h b/libs/pbd/pbd/stateful.h index 5adddfc1c0..5fbac11e5c 100644 --- a/libs/pbd/pbd/stateful.h +++ b/libs/pbd/pbd/stateful.h @@ -42,7 +42,7 @@ class Stateful { virtual void add_instant_xml (XMLNode&, const std::string& dir); XMLNode *instant_xml (const std::string& str, const std::string& dir); - PBD::ID id() { return _id; } + const PBD::ID& id() const { return _id; } protected: XMLNode *_extra_xml; -- cgit v1.2.3