From 8dab33c609bdf588aa33cd4e34019f7319f078d6 Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Mon, 7 Dec 2009 14:18:06 +0000 Subject: [PATCH] Route::set_meter_point() is now conceptually RT safe, although it still takes a write lock on the processor list. this allows it to be called when setting rec-enable status on a route. not thoroughly tested, and still incomplete - single route rec-enables should probably use this pathway, and there is still no cross-thread cleanup from an RT route op request git-svn-id: svn://localhost/ardour2/branches/3.0@6320 d708f5d6-7413-0410-9779-e7cbd77b26cf --- libs/ardour/ardour/meter.h | 19 ++- libs/ardour/ardour/route.h | 2 +- libs/ardour/ardour/track.h | 2 - libs/ardour/meter.cc | 31 +++-- libs/ardour/route.cc | 57 +++++---- libs/ardour/session.cc | 15 ++- libs/ardour/session_events.cc | 9 +- libs/ardour/session_midi.cc | 221 ++++++++++++++++++++++++++++++++++ libs/ardour/track.cc | 14 +-- 9 files changed, 319 insertions(+), 51 deletions(-) diff --git a/libs/ardour/ardour/meter.h b/libs/ardour/ardour/meter.h index dbaba25a06..ae0a1672db 100644 --- a/libs/ardour/ardour/meter.h +++ b/libs/ardour/ardour/meter.h @@ -59,6 +59,19 @@ public: bool can_support_io_configuration (const ChanCount& in, ChanCount& out) const; bool configure_io (ChanCount in, ChanCount out); + + /* special method for meter, to ensure that it can always handle the maximum + number of streams in the route, no matter where we put it. + */ + + void reset_max_channels (const ChanCount&); + + /* tell the meter than no matter how many channels it can handle, + `in' is the number it is actually going be handling from + now on. + */ + + void reflect_inputs (const ChanCount& in); /** Compute peaks */ void run (BufferSet& bufs, sframes_t start_frame, sframes_t end_frame, nframes_t nframes, bool); @@ -80,10 +93,12 @@ public: } XMLNode& state (bool full); - + private: friend class IO; - + + uint32_t current_meters; + std::vector _peak_power; std::vector _visible_peak_power; std::vector _max_peak_power; diff --git a/libs/ardour/ardour/route.h b/libs/ardour/ardour/route.h index 984ba5fe95..8b08dded99 100644 --- a/libs/ardour/ardour/route.h +++ b/libs/ardour/ardour/route.h @@ -153,7 +153,7 @@ class Route : public SessionObject, public AutomatableControls void drop_route_group (void *); RouteGroup *route_group () const { return _route_group; } - virtual void set_meter_point (MeterPoint, void *src); + void set_meter_point (MeterPoint, void *src); MeterPoint meter_point() const { return _meter_point; } void meter (); diff --git a/libs/ardour/ardour/track.h b/libs/ardour/ardour/track.h index 82cb83ad16..5d8e2dbbd5 100644 --- a/libs/ardour/ardour/track.h +++ b/libs/ardour/ardour/track.h @@ -90,8 +90,6 @@ class Track : public Route bool record_enabled() const; void set_record_enable (bool yn, void *src); - void set_meter_point (MeterPoint, void* src); - sigc::signal DiskstreamChanged; sigc::signal FreezeChange; diff --git a/libs/ardour/meter.cc b/libs/ardour/meter.cc index 0d3e8e228a..4566ed7e02 100644 --- a/libs/ardour/meter.cc +++ b/libs/ardour/meter.cc @@ -65,6 +65,12 @@ Metering::update_meters() Meter(); /* EMIT SIGNAL */ } +PeakMeter::PeakMeter (Session& s, const XMLNode& node) + : Processor (s, node) +{ + current_meters = 0; +} + /** Get peaks from @a bufs * Input acceptance is lenient - the first n buffers from @a bufs will * be metered, where n was set by the last call to setup(), excess meters will @@ -115,11 +121,6 @@ PeakMeter::run (BufferSet& bufs, sframes_t /*start_frame*/, sframes_t /*end_fram _active = _pending_active; } -PeakMeter::PeakMeter (Session& s, const XMLNode& node) - : Processor (s, node) -{ -} - void PeakMeter::reset () { @@ -150,7 +151,21 @@ PeakMeter::configure_io (ChanCount in, ChanCount out) return false; } - uint32_t limit = in.n_total(); + current_meters = in.n_total (); + + return Processor::configure_io (in, out); +} + +void +PeakMeter::reflect_inputs (const ChanCount& in) +{ + current_meters = in.n_total (); +} + +void +PeakMeter::reset_max_channels (const ChanCount& chn) +{ + uint32_t limit = chn.n_total(); while (_peak_power.size() > limit) { _peak_power.pop_back(); @@ -167,8 +182,6 @@ PeakMeter::configure_io (ChanCount in, ChanCount out) assert(_peak_power.size() == limit); assert(_visible_peak_power.size() == limit); assert(_max_peak_power.size() == limit); - - return Processor::configure_io (in, out); } /** To be driven by the Meter signal from IO. @@ -185,7 +198,7 @@ PeakMeter::meter () assert(_visible_peak_power.size() == _peak_power.size()); - const size_t limit = _peak_power.size(); + const size_t limit = min (_peak_power.size(), (size_t) current_meters); for (size_t n = 0; n < limit; ++n) { diff --git a/libs/ardour/route.cc b/libs/ardour/route.cc index 6879eee5e5..768df38698 100644 --- a/libs/ardour/route.cc +++ b/libs/ardour/route.cc @@ -80,8 +80,8 @@ Route::Route (Session& sess, string name, Flag flg, DataType default_type) /* add standard processors other than amp (added by ::init()) */ _meter.reset (new PeakMeter (_session)); - _meter->set_display_to_user (_meter_point == MeterCustom); - add_processor (_meter, PreFader); + _meter->set_display_to_user (false); + add_processor (_meter, PostFader); if (_flags & ControlOut) { /* where we listen to tracks */ @@ -1479,6 +1479,10 @@ Route::configure_processors_unlocked (ProcessorStreams* err) out = c->second; } + if (_meter) { + _meter->reset_max_channels (processor_max_streams); + } + /* make sure we have sufficient scratch buffers to cope with the new processor configuration */ _session.ensure_buffers (n_process_buffers ()); @@ -2667,6 +2671,8 @@ Route::flush_processors () void Route::set_meter_point (MeterPoint p, void *src) { + /* CAN BE CALLED FROM PROCESS CONTEXT */ + if (_meter_point == p) { return; } @@ -2675,54 +2681,61 @@ Route::set_meter_point (MeterPoint p, void *src) { Glib::RWLock::WriterLock lm (_processor_lock); - ProcessorList as_it_was (_processors); - + if (p != MeterCustom) { // Move meter in the processors list to reflect the new position - ProcessorList::iterator loc = find(_processors.begin(), _processors.end(), _meter); + ProcessorList::iterator loc = find (_processors.begin(), _processors.end(), _meter); _processors.erase(loc); switch (p) { case MeterInput: loc = _processors.begin(); break; case MeterPreFader: - loc = find(_processors.begin(), _processors.end(), _amp); + loc = find (_processors.begin(), _processors.end(), _amp); break; case MeterPostFader: loc = _processors.end(); break; default: - break; + break; } - - _processors.insert(loc, _meter); - if (configure_processors_unlocked (0)) { - _processors = as_it_was; - configure_processors_unlocked (0); // it worked before we tried to add it ... - return; + ChanCount m_in; + + if (loc == _processors.begin()) { + m_in = _input->n_ports(); + } else { + ProcessorList::iterator before = loc; + --before; + m_in = (*before)->output_streams (); } - + + _meter->reflect_inputs (m_in); + + _processors.insert (loc, _meter); + + /* we do not need to reconfigure the processors, because the meter + (a) is always ready to handle processor_max_streams + (b) is always an N-in/N-out processor, and thus moving + it doesn't require any changes to the other processors. + */ + _meter->set_display_to_user (false); - + } else { - + // just make it visible and let the user move it - + _meter->set_display_to_user (true); } - } _meter_point = p; meter_change (src); /* EMIT SIGNAL */ - /* the meter has visibly changed if it is not visible to the user, or if it was and now isn't */ - bool const meter_visibly_changed = _meter->display_to_user() || meter_was_visible_to_user; + bool const meter_visibly_changed = (_meter->display_to_user() != meter_was_visible_to_user); processors_changed (RouteProcessorChange (RouteProcessorChange::MeterPointChange, meter_visibly_changed)); /* EMIT SIGNAL */ - - _session.set_dirty (); } void diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index 9cb4cc3284..f113bf4ebc 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -1164,8 +1164,9 @@ Session::disable_record (bool rt_context, bool force) // FIXME: timestamp correct? [DR] // FIXME FIXME FIXME: rt_context? this must be called in the process thread. // does this /need/ to be sent in all cases? - if (rt_context) + if (rt_context) { deliver_mmc (MIDI::MachineControl::cmdRecordExit, _transport_frame); + } if (Config->get_monitoring_model() == HardwareMonitoring && config.get_auto_input()) { boost::shared_ptr dsl = diskstreams.reader(); @@ -3647,13 +3648,21 @@ Session::record_enable_change_all (bool yn) void Session::do_record_enable_change_all (RouteList* rl, bool yn) { - for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) { + for (RouteList::iterator i = rl->begin(); i != rl->end(); ) { boost::shared_ptr t; if ((t = boost::dynamic_pointer_cast(*i)) != 0) { t->set_record_enable (yn, this); - } + if (t->meter_point() == MeterCustom) { + /* don't change metering for this track */ + i = rl->erase (i); + } else { + ++i; + } + } } + + set_dirty (); } void diff --git a/libs/ardour/session_events.cc b/libs/ardour/session_events.cc index 8818436ed7..7b24b1abef 100644 --- a/libs/ardour/session_events.cc +++ b/libs/ardour/session_events.cc @@ -69,7 +69,7 @@ SessionEvent::operator delete (void *ptr, size_t /*size*/) { Pool* p = pool->per_thread_pool (); SessionEvent* ev = static_cast (ptr); - + if (p == ev->own_pool) { p->release (ptr); } else { @@ -278,3 +278,10 @@ SessionEventManager::_clear_event_type (SessionEvent::Type type) set_next_event (); } +#if 0 +void +Session::process_rtop (SessionEvent* ev) +{ + ev->rt_return (ev->rt_slot ()); +} +#endif diff --git a/libs/ardour/session_midi.cc b/libs/ardour/session_midi.cc index 15777aa642..a0366dd6e8 100644 --- a/libs/ardour/session_midi.cc +++ b/libs/ardour/session_midi.cc @@ -1082,6 +1082,227 @@ Session::_midi_thread_work (void* arg) return 0; } +#if 0 +void +Session::midi_thread_work () +{ + MIDIRequest* request; + GPollFD pfd[4]; + int nfds = 0; + int timeout; + int fds_ready; + struct sched_param rtparam; + int x; + bool restart; + vector ports; + + PBD::notify_gui_about_thread_creation (pthread_self(), X_("MIDI"), 2048); + SessionEvent::create_per_thread_pool (X_("MIDI I/O"), 128); + + memset (&rtparam, 0, sizeof (rtparam)); + rtparam.sched_priority = 9; /* XXX should be relative to audio (JACK) thread */ + + if ((x = pthread_setschedparam (pthread_self(), SCHED_FIFO, &rtparam)) != 0) { + // do we care? not particularly. + } + + /* set up the port vector; 5 is the largest possible size for now */ + + ports.assign (5, (MIDI::Port*) 0); + + GMainContext* main_context = g_main_context_new (); + + while (1) { + + nfds = 0; + + gpfd[nfds].fd = midi_request_pipe[0]; + gpfd[nfds].events = POLLIN|POLLHUP|POLLERR; + nfds++; + + if (Config->get_mmc_control() && _mmc_port && _mmc_port->selectable() >= 0) { + gpfd[nfds].fd = _mmc_port->selectable(); + gpfd[nfds].events = POLLIN|POLLHUP|POLLERR; + ports[nfds] = _mmc_port; + g_main_context_add_poll (&gpfd[nfds]); + DEBUG_TRACE (DEBUG::MidiIO, string_compose ("set up port #%1 for mmc @ %2\n", nfds, _mmc_port)); + nfds++; + } + + /* if MTC is being handled on a different port from MMC + or we are not handling MMC at all, poll + the relevant port. + */ + + if (_mtc_port && (_mtc_port != _mmc_port || !Config->get_mmc_control()) && _mtc_port->selectable() >= 0) { + gpfd[nfds].fd = _mtc_port->selectable(); + gpfd[nfds].events = POLLIN|POLLHUP|POLLERR; + ports[nfds] = _mtc_port; + g_main_context_add_poll (&gpfd[nfds]); + DEBUG_TRACE (DEBUG::MidiIO, string_compose ("set up port #%1 for mtc @ %2\n", nfds, _mtc_port)); + nfds++; + } + + if (_midi_clock_port && (_midi_clock_port != _mmc_port || !Config->get_mmc_control()) && _midi_clock_port->selectable() >= 0) { + gpfd[nfds].fd = _midi_clock_port->selectable(); + gpfd[nfds].events = POLLIN|POLLHUP|POLLERR; + ports[nfds] = _midi_clock_port; + g_main_context_add_poll (&gpfd[nfds]); + DEBUG_TRACE (DEBUG::MidiIO, string_compose ("set up port #%1 for midi clock @ %2\n", nfds, _midi_clock_port)); + nfds++; + } + + /* if we are using MMC control, we obviously have to listen + the relevant port. + */ + + if (_midi_port && (_midi_port != _mmc_port || !Config->get_mmc_control()) && (_midi_port != _mtc_port) && _midi_port->selectable() >= 0) { + gpfd[nfds].fd = _midi_port->selectable(); + gpfd[nfds].events = POLLIN|POLLHUP|POLLERR; + ports[nfds] = _midi_port; + g_main_context_add_poll (&gpfd[nfds]); + DEBUG_TRACE (DEBUG::MidiIO, string_compose ("set up port #%1 for midi @ %2\n", nfds, _midi_port)); + nfds++; + } + + if (!midi_timeouts.empty()) { + timeout = 100; /* 10msecs */ + } else { + timeout = -1; /* if there is no data, we don't care */ + } + + again: + + DEBUG_TRACE (DEBUG::MidiIO, string_compose ("MIDI poll on %1 fds for %2\n", nfds, timeout)); + if (g_poll (gpfd, nfds, timeout) < 0) { + if (errno == EINTR) { + /* gdb at work, perhaps */ + goto again; + } + + error << string_compose(_("MIDI thread poll failed (%1)"), strerror (errno)) << endmsg; + + break; + } + + nframes64_t now = engine().frame_time(); + + DEBUG_TRACE (DEBUG::MidiIO, "MIDI thread awake\n"); + + fds_ready = 0; + + /* check the transport request pipe */ + + if (gpfd[0].revents & ~POLLIN) { + error << _("Error on transport thread request pipe") << endmsg; + break; + } + + if (gpfd[0].revents & POLLIN) { + + char foo[16]; + + DEBUG_TRACE (DEBUG::MidiIO, "MIDI request FIFO ready\n"); + fds_ready++; + + /* empty the pipe of all current requests */ + + while (1) { + size_t nread = read (midi_request_pipe[0], &foo, sizeof (foo)); + + if (nread > 0) { + if ((size_t) nread < sizeof (foo)) { + break; + } else { + continue; + } + } else if (nread == 0) { + break; + } else if (errno == EAGAIN) { + break; + } else { + fatal << _("Error reading from transport request pipe") << endmsg; + /*NOTREACHED*/ + } + } + + while (midi_requests.read (&request, 1) == 1) { + + switch (request->type) { + case MIDIRequest::PortChange: + /* restart poll with new ports */ + DEBUG_TRACE (DEBUG::MidiIO, "rebind\n"); + restart = true; + break; + + case MIDIRequest::Quit: + delete request; + DEBUG_TRACE (DEBUG::MidiIO, "thread quit\n"); + pthread_exit_pbd (0); + /*NOTREACHED*/ + break; + + default: + break; + } + + + delete request; + } + + } + + if (restart) { + DEBUG_TRACE (DEBUG::MidiIO, "ports changed, restart poll\n"); + restart = false; + continue; + } + + /* now read the rest of the ports */ + + for (int p = 1; p < nfds; ++p) { + + DEBUG_STR_SET(foo, "port #%1 revents = "); + DEBUG_STR(foo) << hex << pfd[p].revents << dec << endl; + DEBUG_TRACE (DEBUG::MidiIO, string_compose (DEBUG_STR(foo).str(), p)); + + if ((pfd[p].revents & ~POLLIN)) { + // error << string_compose(_("Transport: error polling MIDI port %1 (revents =%2%3%4"), p, &hex, pfd[p].revents, &dec) << endmsg; + break; + } + + if (pfd[p].revents & POLLIN) { + DEBUG_TRACE (DEBUG::MidiIO, string_compose ("MIDI fd # %1 has data ready @ %2\n", p, now)); + fds_ready++; + ports[p]->parse (now); + } + + g_main_context_remove_poll (&gpfd[p]); + } + + /* timeout driven */ + + if (fds_ready < 2 && timeout != -1) { + + DEBUG_TRACE (DEBUG::MidiIO, "Check timeouts\n"); + for (MidiTimeoutList::iterator i = midi_timeouts.begin(); i != midi_timeouts.end(); ) { + + MidiTimeoutList::iterator tmp; + tmp = i; + ++tmp; + + if (!(*i)()) { + midi_timeouts.erase (i); + } + + i = tmp; + } + } + } + +} +#endif + void Session::midi_thread_work () { diff --git a/libs/ardour/track.cc b/libs/ardour/track.cc index acf0c50e18..373829413c 100644 --- a/libs/ardour/track.cc +++ b/libs/ardour/track.cc @@ -66,12 +66,6 @@ Track::~Track () DEBUG_TRACE (DEBUG::Destruction, string_compose ("track %1 destructor\n", _name)); } -void -Track::set_meter_point (MeterPoint p, void *src) -{ - Route::set_meter_point (p, src); -} - XMLNode& Track::get_state () { @@ -192,17 +186,15 @@ Track::set_record_enable (bool yn, void *src) _diskstream->set_record_enabled (yn); -#if 0 if (_diskstream->record_enabled()) { - set_meter_point (MeterInput, this); + if (_meter_point != MeterCustom) { + set_meter_point (MeterInput, this); + } } else { set_meter_point (_saved_meter_point, this); } -#endif - cerr << "4\n"; _rec_enable_control->Changed (); - cerr << "5\n"; } -- 2.30.2