major design changes: use glib event loop for MIDI thread/UI; rework design of BaseUI and AbstractUI; solo & mute are both temporarily broken; OSC control up next; may segfault during exit

git-svn-id: svn://localhost/ardour2/branches/3.0@6328 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
Paul Davis 2009-12-09 03:05:14 +00:00
parent 90f95df207
commit c38e02285f
50 changed files with 753 additions and 908 deletions

View File

@ -115,7 +115,7 @@ sigc::signal<void,nframes_t, bool, nframes_t> ARDOUR_UI::Clock;
ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[])
: Gtkmm2ext::UI (X_("Ardour"), argcp, argvp),
: Gtkmm2ext::UI (X_("gui"), argcp, argvp),
primary_clock (X_("primary"), false, X_("TransportClockDisplay"), true, true, false, true),
secondary_clock (X_("secondary"), false, X_("SecondaryClockDisplay"), true, true, false, true),

View File

@ -478,7 +478,7 @@ void
ARDOUR_UI::solo_alert_toggle ()
{
if (session) {
session->set_all_solo (!session->soloing());
session->set_solo (session->get_routes(), !session->soloing());
}
}

View File

@ -491,7 +491,7 @@ Editor::import_sndfiles (vector<ustring> paths, ImportMode mode, SrcQuality qual
(the GUI) to direct additional steps after that.
*/
pthread_create_and_store ("import", &import_status.thread, 0, _import_thread, this);
pthread_create_and_store ("import", &import_status.thread, _import_thread, this);
pthread_detach (import_status.thread);
while (!import_status.done && !import_status.cancel) {
@ -879,7 +879,6 @@ Editor::finish_bringing_in_material (boost::shared_ptr<Region> region, uint32_t
void *
Editor::_import_thread (void *arg)
{
PBD::notify_gui_about_thread_creation (pthread_self(), X_("Import"));
SessionEvent::create_per_thread_pool ("import events", 64);
Editor *ed = (Editor *) arg;

View File

@ -3653,7 +3653,6 @@ Editor::unfreeze_route ()
void*
Editor::_freeze_thread (void* arg)
{
PBD::notify_gui_about_thread_creation (pthread_self(), X_("Freeze"));
SessionEvent::create_per_thread_pool ("freeze events", 64);
return static_cast<Editor*>(arg)->freeze_thread ();
@ -3702,13 +3701,7 @@ Editor::freeze_route ()
itt.cancel = false;
itt.progress = 0.0f;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setstacksize(&attr, 500000);
pthread_create_and_store (X_("freezer"), &itt.thread, &attr, _freeze_thread, this);
pthread_attr_destroy(&attr);
pthread_create_and_store (X_("freezer"), &itt.thread, _freeze_thread, this);
track_canvas->get_window()->set_cursor (Gdk::Cursor (Gdk::WATCH));

View File

@ -225,7 +225,7 @@ Editor::time_fx (RegionSelection& regions, float val, bool pitching)
current_timefx->first_delete = current_timefx->signal_delete_event().connect
(mem_fun (current_timefx, &TimeFXDialog::delete_in_progress));
if (pthread_create_and_store ("timefx", &current_timefx->request.thread, 0, timefx_thread, current_timefx)) {
if (pthread_create_and_store ("timefx", &current_timefx->request.thread, timefx_thread, current_timefx)) {
current_timefx->hide ();
error << _("timefx cannot be started - thread creation error") << endmsg;
return -1;
@ -337,7 +337,6 @@ Editor::do_timefx (TimeFXDialog& dialog)
void*
Editor::timefx_thread (void *arg)
{
PBD::notify_gui_about_thread_creation (pthread_self(), X_("TimeFX"));
SessionEvent::create_per_thread_pool ("timefx events", 64);
TimeFXDialog* tsd = static_cast<TimeFXDialog*>(arg);

View File

@ -21,16 +21,11 @@
#define __ardour_gtk_gui_thread_h__
#include <gtkmm2ext/gtk_ui.h>
#include "pbd/crossthread.h"
#define ENSURE_GUI_THREAD(slot) \
if (!Gtkmm2ext::UI::instance()->caller_is_ui_thread()) {\
if (!Gtkmm2ext::UI::instance()->caller_is_self()) { \
Gtkmm2ext::UI::instance()->call_slot ((slot));\
return;\
}
#define GTK_SAFE(theSlot) crossthread_safe (Gtkmm2ext::UI::instance()->thread_id(),\
*Gtkmm2ext::UI::instance(), \
(theSlot))
#endif /* __ardour_gtk_gui_thread_h__ */

View File

@ -393,7 +393,8 @@ int main (int argc, char *argv[])
ui = 0;
ARDOUR::cleanup ();
pthread_cancel_all ();
// pthread_cancel ();
#ifdef HAVE_LV2
close_external_ui_windows();
#endif

View File

@ -183,6 +183,7 @@ private:
node.add_property ("mode", smod);
if (MIDI::Manager::instance()->add_port (node) != 0) {
cerr << " there are now " << MIDI::Manager::instance()->nports() << endl;
ports_changed ();
}
}

View File

@ -262,18 +262,21 @@ RouteUI::mute_press(GdkEventButton* ev)
if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
#if 0
/* Primary-Tertiary-click applies change to all routes */
_session.begin_reversible_command (_("mute change"));
Session::GlobalMuteStateCommand *cmd = new Session::GlobalMuteStateCommand(_session, this);
_session.set_all_mute (!_route->muted());
_session.set_mute (!_route->muted());
cmd->mark();
_session.add_command(cmd);
_session.commit_reversible_command ();
multiple_mute_change = true;
#endif
} else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
#if 0
/* Primary-button1 applies change to the mix group.
NOTE: Primary-button2 is MIDI learn.
*/
@ -281,15 +284,18 @@ RouteUI::mute_press(GdkEventButton* ev)
if (ev->button == 1) {
set_route_group_mute (_route, !_route->muted());
}
#endif
} else {
#if 0
/* plain click applies change to this route */
if (wait_for_release) {
_route->set_mute (!_route->muted(), this);
} else {
reversibly_apply_route_boolean ("mute change", &Route::set_mute, !_route->muted(), this);
}
#endif
}
}
}
@ -318,6 +324,18 @@ RouteUI::mute_release(GdkEventButton*)
return true;
}
void
RouteUI::post_solo_cleanup (SessionEvent* ev, bool was_not_latched)
{
ENSURE_GUI_THREAD (bind (mem_fun (*this, &RouteUI::post_solo_cleanup), ev, was_not_latched));
delete ev;
if (was_not_latched) {
Config->set_solo_latched (false);
}
}
bool
RouteUI::solo_press(GdkEventButton* ev)
{
@ -364,6 +382,7 @@ RouteUI::solo_press(GdkEventButton* ev)
/* Primary-Tertiary-click applies change to all routes */
bool was_not_latched = false;
if (!Config->get_solo_latched ()) {
was_not_latched = true;
/*
@ -373,28 +392,25 @@ RouteUI::solo_press(GdkEventButton* ev)
*/
Config->set_solo_latched (true);
}
_session.begin_reversible_command (_("solo change"));
Session::GlobalSoloStateCommand *cmd = new Session::GlobalSoloStateCommand(_session, this);
_session.set_all_solo (!_route->soloed());
cmd->mark();
_session.add_command (cmd);
_session.commit_reversible_command ();
multiple_solo_change = true;
if (was_not_latched) {
Config->set_solo_latched (false);
}
SessionEvent* ev = new SessionEvent (SessionEvent::RealTimeOperation, SessionEvent::Add, SessionEvent::Immediate, 0, 0.0);
ev->rt_slot = bind (sigc::mem_fun (_session, &Session::set_solo), _session.get_routes(), !_route->soloed());
ev->rt_return = bind (sigc::mem_fun (*this, &RouteUI::post_solo_cleanup), was_not_latched);
_session.queue_event (ev);
} else if (Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
// Primary-Secondary-click: exclusively solo this track, not a toggle */
//boost::shared_ptr<RouteList> rl (new RouteList);
//rl->push_back (route());
_session.begin_reversible_command (_("solo change"));
Session::GlobalSoloStateCommand *cmd = new Session::GlobalSoloStateCommand (_session, this);
_session.set_all_solo (false);
_route->set_solo (true, this);
cmd->mark();
_session.add_command(cmd);
_session.commit_reversible_command ();
//SessionEvent* ev = new SessionEvent (SessionEvent::RealTimeOperation, SessionEvent::Add, SessionEvent::Immediate, 0, 0.0);
// ev->rt_slot = bind (sigc::mem_fun (_session, &Session::set_just_one_solo), rl, true);
//ev->rt_return = sigc::mem_fun (*this, &RouteUI::post_rtop_cleanup);
//_session.queue_event (ev);
} else if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
@ -405,22 +421,29 @@ RouteUI::solo_press(GdkEventButton* ev)
} else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
#if 0
/* Primary-button1: solo mix group.
NOTE: Primary-button2 is MIDI learn.
*/
if (ev->button == 1) {
set_route_group_solo (_route, !_route->soloed());
queue_route_group_op (RouteGroup::Solo, &Session::set_all_solo, !_route->soloed());
}
#endif
} else {
/* click: solo this route */
if (wait_for_release) {
_route->set_solo (!_route->soloed(), this);
} else {
reversibly_apply_route_boolean ("solo change", &Route::set_solo, !_route->soloed(), this);
}
boost::shared_ptr<RouteList> rl (new RouteList);
rl->push_back (route());
SessionEvent* ev = new SessionEvent (SessionEvent::RealTimeOperation, SessionEvent::Add, SessionEvent::Immediate, 0, 0.0);
ev->rt_slot = bind (sigc::mem_fun (_session, &Session::set_solo), rl, !rec_enable_button->get_active());
ev->rt_return = sigc::mem_fun (*this, &RouteUI::post_rtop_cleanup);
_session.queue_event (ev);
}
}
}
@ -521,7 +544,7 @@ RouteUI::rec_enable_press(GdkEventButton* ev)
} else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
SessionEvent* ev = new SessionEvent (SessionEvent::RealTimeOperation, SessionEvent::Add, SessionEvent::Immediate, 0, 0.0);
ev->rt_slot = bind (sigc::mem_fun (_session, &Session::set_all_record_enable), _session.get_routes(), !rec_enable_button->get_active());
ev->rt_slot = bind (sigc::mem_fun (_session, &Session::set_record_enable), _session.get_routes(), !rec_enable_button->get_active());
ev->rt_return = sigc::mem_fun (*this, &RouteUI::post_rtop_cleanup);
_session.queue_event (ev);
@ -533,7 +556,7 @@ RouteUI::rec_enable_press(GdkEventButton* ev)
*/
if (ev->button == 1) {
queue_route_group_op (RouteGroup::RecEnable, &Session::set_all_record_enable, !rec_enable_button->get_active());
queue_route_group_op (RouteGroup::RecEnable, &Session::set_record_enable, !rec_enable_button->get_active());
}
} else if (Keyboard::is_context_menu_event (ev)) {
@ -543,9 +566,9 @@ RouteUI::rec_enable_press(GdkEventButton* ev)
} else {
boost::shared_ptr<RouteList> rl (new RouteList);
rl->push_back (route());
SessionEvent* ev = new SessionEvent (SessionEvent::RealTimeOperation, SessionEvent::Add, SessionEvent::Immediate, 0, 0.0);
ev->rt_slot = bind (sigc::mem_fun (_session, &Session::set_all_record_enable), rl, !rec_enable_button->get_active());
ev->rt_slot = bind (sigc::mem_fun (_session, &Session::set_record_enable), rl, !rec_enable_button->get_active());
ev->rt_return = sigc::mem_fun (*this, &RouteUI::post_rtop_cleanup);
_session.queue_event (ev);

View File

@ -217,6 +217,7 @@ class RouteUI : public virtual AxisView
void post_rtop_cleanup (ARDOUR::SessionEvent* ev);
void post_group_rtop_cleanup (ARDOUR::SessionEvent* ev, ARDOUR::RouteGroup*, ARDOUR::RouteGroup::Property);
void post_solo_cleanup (ARDOUR::SessionEvent* ev, bool was_not_latched);
};
#endif /* __ardour_route_ui__ */

View File

@ -733,7 +733,6 @@ SoundFileBrowser::found_search_clicked ()
void*
freesound_search_thread_entry (void* arg)
{
PBD::notify_gui_about_thread_creation (pthread_self(), X_("Freesound Search"));
SessionEvent::create_per_thread_pool ("freesound events", 64);
static_cast<SoundFileBrowser*>(arg)->freesound_search_thread ();
@ -757,7 +756,7 @@ SoundFileBrowser::freesound_search_clicked ()
searching = true;
freesound_search_btn.set_label(_("Cancel"));
pthread_t freesound_thr;
pthread_create_and_store ("freesound_search", &freesound_thr, 0, freesound_search_thread_entry, this);
pthread_create_and_store ("freesound_search", &freesound_thr, freesound_search_thread_entry, this);
}
}

View File

@ -76,7 +76,6 @@ Analyser::queue_source_for_analysis (boost::shared_ptr<Source> src, bool force)
void
Analyser::work ()
{
PBD::notify_gui_about_thread_creation (pthread_self(), string ("analyser-") + to_string (pthread_self(), std::dec));
SessionEvent::create_per_thread_pool ("Analyser", 64);
while (true) {

View File

@ -0,0 +1,52 @@
#ifndef __libardour_midi_ui_h__
#define __libardour_midi_ui_h__
#include <list>
#include "pbd/abstract_ui.h"
namespace MIDI {
class port;
}
namespace ARDOUR {
class Session;
/* this is mostly a placeholder because I suspect that at some
point we will want to add more members to accomodate
certain types of requests to the MIDI UI
*/
struct MidiUIRequest : public BaseUI::BaseRequestObject {
public:
MidiUIRequest () {}
~MidiUIRequest() {}
};
class MidiControlUI : public AbstractUI<MidiUIRequest>
{
public:
MidiControlUI (Session& s);
~MidiControlUI ();
static BaseUI::RequestType PortChange;
void change_midi_ports ();
protected:
void thread_init ();
void do_request (MidiUIRequest*);
private:
typedef std::list<Glib::RefPtr<Glib::IOSource> > PortSources;
PortSources port_sources;
ARDOUR::Session& _session;
bool midi_input_handler (Glib::IOCondition, MIDI::Port*);
void reset_ports ();
void clear_ports ();
};
}
#endif /* __libardour_midi_ui_h__ */

View File

@ -97,6 +97,7 @@ class MidiDiskstream;
class MidiRegion;
class MidiSource;
class MidiTrack;
class MidiControlUI;
class NamedSelection;
class Playlist;
class PluginInsert;
@ -616,14 +617,14 @@ class Session : public PBD::StatefulDestructible, public SessionEventManager, pu
bool soloing() const { return _non_soloed_outs_muted; }
bool listening() const { return _listen_cnt > 0; }
void set_all_solo (bool);
void set_all_mute (bool);
void set_all_listen (bool);
void set_solo (boost::shared_ptr<RouteList>, bool);
void set_mute (boost::shared_ptr<RouteList>, bool);
void set_listen (boost::shared_ptr<RouteList>, bool);
sigc::signal<void,bool> SoloActive;
sigc::signal<void> SoloChanged;
void set_all_record_enable (boost::shared_ptr<RouteList>, bool);
void set_record_enable (boost::shared_ptr<RouteList>, bool);
/* control/master out */
@ -1253,25 +1254,11 @@ class Session : public PBD::StatefulDestructible, public SessionEventManager, pu
bool non_realtime_work_pending() const { return static_cast<bool>(post_transport_work()); }
bool process_can_proceed() const { return !(post_transport_work() & ProcessCannotProceedMask); }
struct MIDIRequest {
enum Type {
PortChange,
Quit
};
Type type;
};
Glib::Mutex midi_lock;
pthread_t midi_thread;
int midi_request_pipe[2];
RingBuffer<MIDIRequest*> midi_requests;
MidiControlUI* midi_control_ui;
int start_midi_thread ();
void terminate_midi_thread ();
void poke_midi_thread ();
static void *_midi_thread_work (void *arg);
void midi_thread_work ();
void change_midi_ports ();
int use_config_midi_ports ();
void set_play_loop (bool yn);

View File

@ -135,7 +135,9 @@ _thread_init_callback (void * /*arg*/)
knows about it.
*/
PBD::notify_gui_about_thread_creation (pthread_self(), X_("Audioengine"), 4096);
PBD::notify_gui_about_thread_creation ("gui", pthread_self(), X_("Audioengine"), 4096);
PBD::notify_gui_about_thread_creation ("midiui", pthread_self(), X_("Audioengine"), 128);
SessionEvent::create_per_thread_pool (X_("Audioengine"), 512);
MIDI::JACK_MidiPort::set_process_thread (pthread_self());

View File

@ -89,7 +89,7 @@ Butler::start_thread()
return -1;
}
if (pthread_create_and_store ("disk butler", &thread, 0, _thread_work, this)) {
if (pthread_create_and_store ("disk butler", &thread, _thread_work, this)) {
error << _("Session: could not create butler thread") << endmsg;
return -1;
}
@ -113,7 +113,6 @@ Butler::terminate_thread ()
void *
Butler::_thread_work (void* arg)
{
PBD::notify_gui_about_thread_creation (pthread_self(), X_("Butler"));
SessionEvent::create_per_thread_pool ("butler events", 64);
return ((Butler *) arg)->thread_work ();
}

View File

@ -176,7 +176,6 @@ ExportChannelConfiguration::write_file ()
void *
ExportChannelConfiguration::_write_files (void *arg)
{
notify_gui_about_thread_creation (pthread_self(), "Export post-processing");
SessionEvent::create_per_thread_pool ("exporter events", 64);
// cc can be trated like 'this'

150
libs/ardour/midi_ui.cc Normal file
View File

@ -0,0 +1,150 @@
/*
Copyright (C) 2009 Paul Davis
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <cstdlib>
#include "pbd/pthread_utils.h"
#include "midi++/manager.h"
#include "midi++/port.h"
#include "ardour/debug.h"
#include "ardour/audioengine.h"
#include "ardour/midi_ui.h"
#include "ardour/session.h"
#include "ardour/session_event.h"
#include "ardour/types.h"
using namespace std;
using namespace ARDOUR;
using namespace Glib;
#include "i18n.h"
BaseUI::RequestType MidiControlUI::PortChange = BaseUI::new_request_type();
#include "pbd/abstract_ui.cc" /* instantiate the template */
MidiControlUI::MidiControlUI (Session& s)
: AbstractUI<MidiUIRequest> (_("midiui"))
, _session (s)
{
MIDI::Manager::instance()->PortsChanged.connect (mem_fun (*this, &MidiControlUI::change_midi_ports));
}
MidiControlUI::~MidiControlUI ()
{
clear_ports ();
}
void
MidiControlUI::do_request (MidiUIRequest* req)
{
if (req->type == PortChange) {
/* restart event loop with new ports */
DEBUG_TRACE (DEBUG::MidiIO, "reset ports\n");
reset_ports ();
} else if (req->type == CallSlot) {
req->the_slot ();
}
}
void
MidiControlUI::change_midi_ports ()
{
MidiUIRequest* req = get_request (PortChange);
if (req == 0) {
return;
}
send_request (req);
}
bool
MidiControlUI::midi_input_handler (IOCondition ioc, MIDI::Port* port)
{
if (ioc & ~IO_IN) {
return false;
}
if (ioc & IO_IN) {
if (port->must_drain_selectable()) {
CrossThreadChannel::drain (port->selectable());
}
DEBUG_TRACE (DEBUG::MidiIO, string_compose ("data available on %1\n", port->name()));
nframes64_t now = _session.engine().frame_time();
port->parse (now);
}
return true;
}
void
MidiControlUI::clear_ports ()
{
for (PortSources::iterator i = port_sources.begin(); i != port_sources.end(); ++i) {
/* remove existing sources from the event loop */
(*i)->destroy ();
}
port_sources.clear ();
}
void
MidiControlUI::reset_ports ()
{
clear_ports ();
MIDI::Manager::PortList plist = MIDI::Manager::instance()->get_midi_ports ();
for (MIDI::Manager::PortList::iterator i = plist.begin(); i != plist.end(); ++i) {
int fd;
if ((fd = (*i)->selectable ()) >= 0) {
Glib::RefPtr<IOSource> psrc = IOSource::create (fd, IO_IN|IO_HUP|IO_ERR);
psrc->connect (bind (mem_fun (*this, &MidiControlUI::midi_input_handler), (*i)));
port_sources.push_back (psrc);
}
}
for (PortSources::iterator i = port_sources.begin(); i != port_sources.end(); ++i) {
(*i)->attach (_main_loop->get_context());
}
}
void
MidiControlUI::thread_init ()
{
struct sched_param rtparam;
PBD::notify_gui_about_thread_creation (X_("gui"), 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 (pthread_setschedparam (pthread_self(), SCHED_FIFO, &rtparam) != 0) {
// do we care? not particularly.
}
reset_ports ();
}

View File

@ -71,6 +71,7 @@
#include "ardour/midi_playlist.h"
#include "ardour/midi_region.h"
#include "ardour/midi_track.h"
#include "ardour/midi_ui.h"
#include "ardour/named_selection.h"
#include "ardour/playlist.h"
#include "ardour/plugin_insert.h"
@ -137,8 +138,6 @@ Session::Session (AudioEngine &eng,
_butler (new Butler (this)),
_post_transport_work (0),
_send_timecode_update (false),
midi_thread (pthread_t (0)),
midi_requests (128), // the size of this should match the midi request pool size
diskstreams (new DiskstreamList),
routes (new RouteList),
_total_free_4k_blocks (0),
@ -224,8 +223,6 @@ Session::Session (AudioEngine &eng,
_butler (new Butler (this)),
_post_transport_work (0),
_send_timecode_update (false),
midi_thread (pthread_t (0)),
midi_requests (16),
diskstreams (new DiskstreamList),
routes (new RouteList),
_total_free_4k_blocks (0),
@ -366,7 +363,8 @@ Session::destroy ()
Stateful::loading_state_version = 0;
_butler->terminate_thread ();
//terminate_midi_thread ();
delete midi_control_ui;
if (click_data != default_click) {
delete [] click_data;
@ -3518,10 +3516,8 @@ Session::is_auditioning () const
}
void
Session::set_all_solo (bool yn)
Session::set_solo (boost::shared_ptr<RouteList> r, bool yn)
{
shared_ptr<RouteList> r = routes.reader ();
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
if (!(*i)->is_hidden()) {
(*i)->set_solo (yn, this);
@ -3532,10 +3528,8 @@ Session::set_all_solo (bool yn)
}
void
Session::set_all_listen (bool yn)
Session::set_listen (boost::shared_ptr<RouteList> r, bool yn)
{
shared_ptr<RouteList> r = routes.reader ();
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
if (!(*i)->is_hidden()) {
(*i)->set_listen (yn, this);
@ -3546,10 +3540,8 @@ Session::set_all_listen (bool yn)
}
void
Session::set_all_mute (bool yn)
Session::set_mute (boost::shared_ptr<RouteList> r, bool yn)
{
shared_ptr<RouteList> r = routes.reader ();
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
if (!(*i)->is_hidden()) {
(*i)->set_mute (yn, this);
@ -3605,7 +3597,7 @@ Session::graph_reordered ()
}
void
Session::set_all_record_enable (boost::shared_ptr<RouteList> rl, bool yn)
Session::set_record_enable (boost::shared_ptr<RouteList> rl, bool yn)
{
if (!writable()) {
return;
@ -4037,8 +4029,7 @@ Session::write_one_track (AudioTrack& track, nframes_t start, nframes_t end,
return result;
}
// any bigger than this seems to cause stack overflows in called functions
const nframes_t chunk_size = (128 * 1024)/4;
const nframes_t chunk_size = (256 * 1024)/4;
// block all process callback handling
@ -4323,9 +4314,9 @@ Session::solo_control_mode_changed ()
/* cancel all solo or all listen when solo control mode changes */
if (Config->get_solo_control_is_listen_control()) {
set_all_solo (false);
set_solo (routes.reader(), false);
} else {
set_all_listen (false);
set_listen (routes.reader(), false);
}
}

View File

@ -28,6 +28,7 @@
#include <boost/shared_ptr.hpp>
#include <glibmm/main.h>
#include "midi++/mmc.h"
#include "midi++/port.h"
@ -41,6 +42,7 @@
#include "ardour/session.h"
#include "ardour/audio_track.h"
#include "ardour/midi_track.h"
#include "ardour/midi_ui.h"
#include "ardour/audio_diskstream.h"
#include "ardour/slave.h"
#include "ardour/cycles.h"
@ -52,6 +54,7 @@ using namespace std;
using namespace ARDOUR;
using namespace PBD;
using namespace MIDI;
using namespace Glib;
MachineControl::CommandSignature MMC_CommandSignature;
MachineControl::ResponseSignature MMC_ResponseSignature;
@ -146,7 +149,6 @@ Session::set_mtc_port (string port_tag)
out:
MTC_PortChanged(); /* EMIT SIGNAL */
change_midi_ports ();
set_dirty();
return 0;
}
@ -244,7 +246,6 @@ Session::set_mmc_port (string port_tag)
out:
MMC_PortChanged(); /* EMIT SIGNAL */
change_midi_ports ();
set_dirty();
return 0;
}
@ -278,7 +279,6 @@ Session::set_midi_port (string /*port_tag*/)
out:
#endif
MIDI_PortChanged(); /* EMIT SIGNAL */
change_midi_ports ();
set_dirty();
return 0;
}
@ -320,7 +320,6 @@ Session::set_midi_clock_port (string port_tag)
out:
MIDIClock_PortChanged(); /* EMIT SIGNAL */
change_midi_ports ();
set_dirty();
return 0;
}
@ -640,8 +639,12 @@ Session::mmc_step (MIDI::MachineControl &/*mmc*/, int steps)
last_mmc_step = now;
if (!step_queued) {
midi_timeouts.push_back (mem_fun (*this, &Session::mmc_step_timeout));
step_queued = true;
if (midi_control_ui) {
RefPtr<TimeoutSource> tsrc = TimeoutSource::create (100);
tsrc->connect (mem_fun (*this, &Session::mmc_step_timeout));
tsrc->attach (midi_control_ui->main_loop()->get_context());
step_queued = true;
}
}
}
@ -741,16 +744,6 @@ Session::mmc_record_enable (MIDI::MachineControl &mmc, size_t trk, bool enabled)
}
}
void
Session::change_midi_ports ()
{
MIDIRequest* request = new MIDIRequest;
request->type = MIDIRequest::PortChange;
midi_requests.write (&request, 1);
poke_midi_thread ();
}
/** Send MTC Full Frame message (complete Timecode time) for the start of this cycle.
* This resets the MTC code, the next quarter frame message that is sent will be
* the first one with the beginning of this cycle as the new start point.
@ -1022,495 +1015,17 @@ Session::mmc_step_timeout ()
int
Session::start_midi_thread ()
{
if (pipe (midi_request_pipe)) {
error << string_compose(_("Cannot create transport request signal pipe (%1)"), strerror (errno)) << endmsg;
return -1;
}
if (fcntl (midi_request_pipe[0], F_SETFL, O_NONBLOCK)) {
error << string_compose(_("UI: cannot set O_NONBLOCK on " "signal read pipe (%1)"), strerror (errno)) << endmsg;
return -1;
}
if (fcntl (midi_request_pipe[1], F_SETFL, O_NONBLOCK)) {
error << string_compose(_("UI: cannot set O_NONBLOCK on " "signal write pipe (%1)"), strerror (errno)) << endmsg;
return -1;
}
if (pthread_create_and_store ("transport", &midi_thread, 0, _midi_thread_work, this)) {
error << _("Session: could not create transport thread") << endmsg;
return -1;
}
midi_control_ui = new MidiControlUI (*this);
midi_control_ui->run ();
return 0;
}
void
Session::terminate_midi_thread ()
{
if (midi_thread) {
MIDIRequest* request = new MIDIRequest;
void* status;
request->type = MIDIRequest::Quit;
midi_requests.write (&request, 1);
poke_midi_thread ();
pthread_join (midi_thread, &status);
if (midi_control_ui) {
midi_control_ui->quit ();
}
}
void
Session::poke_midi_thread ()
{
static char c = 0;
if (write (midi_request_pipe[1], &c, 1) != 1) {
error << string_compose(_("cannot send signal to midi thread! (%1)"), strerror (errno)) << endmsg;
}
}
void *
Session::_midi_thread_work (void* arg)
{
pthread_setcancelstate (PTHREAD_CANCEL_ENABLE, 0);
pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, 0);
((Session *) arg)->midi_thread_work ();
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<MIDI::Port*> 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 ()
{
MIDIRequest* request;
struct pollfd pfd[4];
int nfds = 0;
int timeout;
int fds_ready;
struct sched_param rtparam;
int x;
bool restart;
vector<MIDI::Port*> 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);
while (1) {
nfds = 0;
pfd[nfds].fd = midi_request_pipe[0];
pfd[nfds].events = POLLIN|POLLHUP|POLLERR;
nfds++;
if (Config->get_mmc_control() && _mmc_port && _mmc_port->selectable() >= 0) {
pfd[nfds].fd = _mmc_port->selectable();
pfd[nfds].events = POLLIN|POLLHUP|POLLERR;
ports[nfds] = _mmc_port;
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) {
pfd[nfds].fd = _mtc_port->selectable();
pfd[nfds].events = POLLIN|POLLHUP|POLLERR;
ports[nfds] = _mtc_port;
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) {
pfd[nfds].fd = _midi_clock_port->selectable();
pfd[nfds].events = POLLIN|POLLHUP|POLLERR;
ports[nfds] = _midi_clock_port;
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) {
pfd[nfds].fd = _midi_port->selectable();
pfd[nfds].events = POLLIN|POLLHUP|POLLERR;
ports[nfds] = _midi_port;
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 (poll (pfd, 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 (pfd[0].revents & ~POLLIN) {
error << _("Error on transport thread request pipe") << endmsg;
break;
}
if (pfd[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) {
#ifndef NDEBUG
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));
#endif
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);
}
}
/* 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;
}
}
}
}

View File

@ -51,10 +51,6 @@ using namespace std;
void
Session::process (nframes_t nframes)
{
// This is no more the appropriate place to call cycle
// start. cycle_start needs to be called at the Route::roll()
// where the signals which we want to mixdown have been calculated.
//
MIDI::Manager::instance()->cycle_start(nframes);
_silent = false;

View File

@ -219,7 +219,7 @@ Session::first_stage_init (string fullpath, string snapshot_name)
first_file_data_format_reset = true;
first_file_header_format_reset = true;
post_export_sync = false;
//midi_thread = (pthread_t) 0;
midi_control_ui = 0;
AudioDiskstream::allocate_working_buffers();

View File

@ -58,7 +58,6 @@ std::list<boost::weak_ptr<AudioSource> > SourceFactory::files_with_peaks;
static void
peak_thread_work ()
{
PBD::notify_gui_about_thread_creation (pthread_self(), string ("peakbuilder-") + to_string (pthread_self(), std::dec));
SessionEvent::create_per_thread_pool (X_("PeakFile Builder "), 64);
while (true) {

View File

@ -124,6 +124,7 @@ libardour_sources = [
'midi_state_tracker.cc',
'midi_stretch.cc',
'midi_track.cc',
'midi_ui.cc',
'mix.cc',
'mtc_slave.cc',
'mtdm.cc',

View File

@ -47,7 +47,6 @@ using namespace Glib;
using namespace PBD;
using std::map;
pthread_t UI::gui_thread;
UI *UI::theGtkUI = 0;
BaseUI::RequestType Gtkmm2ext::ErrorMessage = BaseUI::new_request_type();
@ -58,11 +57,10 @@ BaseUI::RequestType Gtkmm2ext::SetTip = BaseUI::new_request_type();
BaseUI::RequestType Gtkmm2ext::AddIdle = BaseUI::new_request_type();
BaseUI::RequestType Gtkmm2ext::AddTimeout = BaseUI::new_request_type();
#include <pbd/abstract_ui.cc> /* instantiate the template */
#include "pbd/abstract_ui.cc" /* instantiate the template */
UI::UI (string namestr, int *argc, char ***argv)
: AbstractUI<UIRequest> (namestr, true)
: AbstractUI<UIRequest> (namestr)
{
theMain = new Main (argc, argv);
#ifndef GTK_NEW_TOOLTIP_API
@ -73,18 +71,20 @@ UI::UI (string namestr, int *argc, char ***argv)
if (!theGtkUI) {
theGtkUI = this;
gui_thread = pthread_self ();
} else {
fatal << "duplicate UI requested" << endmsg;
/* NOTREACHED */
}
/* add the pipe to the select/poll loop that GDK does */
/* the GUI event loop runs in the main thread of the app,
which is assumed to have called this.
*/
gdk_input_add (signal_pipe[0],
GDK_INPUT_READ,
UI::signal_pipe_callback,
this);
run_loop_thread = Thread::self();
/* attach our request source to the default main context */
request_channel.ios()->attach (MainContext::get_default());
errors = new TextViewer (800,600);
errors->text().set_editable (false);
@ -100,8 +100,6 @@ UI::UI (string namestr, int *argc, char ***argv)
errors->signal_delete_event().connect (bind (sigc::ptr_fun (just_hide_it), (Window *) errors));
errors->set_type_hint (Gdk::WINDOW_TYPE_HINT_UTILITY);
register_thread (pthread_self(), X_("GUI"));
//load_rcfile (rcfile);
}
@ -113,7 +111,7 @@ UI::~UI ()
bool
UI::caller_is_ui_thread ()
{
return pthread_equal (gui_thread, pthread_self());
return Thread::self() == run_loop_thread;
}
int
@ -122,7 +120,9 @@ UI::load_rcfile (string path, bool themechange)
/* Yes, pointers to Glib::RefPtr. If these are not kept around,
* a segfault somewhere deep in the wonderfully robust glib will result.
* This does not occur if wiget.get_style is used instead of rc.get_style below,
* except that doesn't actually work... */
* except that doesn't actually work...
*/
static Glib::RefPtr<Style>* fatal_style = 0;
static Glib::RefPtr<Style>* error_style = 0;
static Glib::RefPtr<Style>* warning_style = 0;
@ -250,14 +250,6 @@ UI::running ()
return _active;
}
void
UI::kill ()
{
if (_active) {
pthread_kill (gui_thread, SIGKILL);
}
}
void
UI::quit ()
{
@ -348,18 +340,6 @@ UI::idle_add (int (*func)(void *), void *arg)
/* END abstract_ui interfaces */
void
UI::signal_pipe_callback (void *arg, int fd, GdkInputCondition /*cond*/)
{
char buf[256];
/* flush (nonblocking) pipe */
while (read (fd, buf, 256) > 0) {}
((UI *) arg)->handle_ui_requests ();
}
void
UI::do_request (UIRequest* req)
{
@ -375,7 +355,7 @@ UI::do_request (UIRequest* req)
} else if (req->type == CallSlot) {
req->slot ();
req->the_slot ();
} else if (req->type == TouchDisplay) {
@ -550,10 +530,9 @@ UI::handle_fatal (const char *message)
win.set_default_size (400, 100);
string title;
title = name();
WindowTitle title(Glib::get_application_name());
title += ": Fatal Error";
win.set_title (title);
win.set_title (title.get_string());
win.set_position (WIN_POS_MOUSE);
win.set_border_width (12);

View File

@ -26,6 +26,9 @@
#include <stdint.h>
#include <setjmp.h>
#include <pthread.h>
#include <glibmm/thread.h>
#include <gtkmm/widget.h>
#include <gtkmm/style.h>
#ifndef GTK_NEW_TOOLTIP_API
@ -72,7 +75,6 @@ struct UIRequest : public BaseUI::BaseRequestObject {
Transmitter::Channel chn;
void *arg;
const char *msg2;
sigc::slot<void> slot;
~UIRequest () {
if (type == ErrorMessage && msg) {
@ -80,7 +82,7 @@ struct UIRequest : public BaseUI::BaseRequestObject {
free ((char *)msg);
}
}
};
};
class UI : public Receiver, public AbstractUI<UIRequest>
{
@ -98,13 +100,12 @@ class UI : public Receiver, public AbstractUI<UIRequest>
bool caller_is_ui_thread ();
static pthread_t thread_id() { return gui_thread; }
static Glib::Thread* thread_id() { return gui_thread; }
/* Gtk-UI specific interfaces */
bool running ();
void quit ();
void kill ();
int load_rcfile (std::string, bool themechange = false);
void run (Receiver &old_receiver);
@ -136,7 +137,7 @@ class UI : public Receiver, public AbstractUI<UIRequest>
static bool just_hide_it (GdkEventAny *, Gtk::Window *);
static pthread_t the_gui_thread() { return gui_thread; }
static Glib::Thread* the_gui_thread() { return gui_thread; }
protected:
virtual void handle_fatal (const char *);
@ -146,7 +147,7 @@ class UI : public Receiver, public AbstractUI<UIRequest>
private:
static UI *theGtkUI;
static pthread_t gui_thread;
static Glib::Thread* gui_thread;
bool _active;
Gtk::Main *theMain;
#ifndef GTK_NEW_TOOLTIP_API

View File

@ -42,7 +42,7 @@ class StateButton
bool _is_realized;
virtual std::string get_widget_name() const = 0;
virtual void set_widget_name (std::string) = 0;
virtual void set_widget_name (const std::string&) = 0;
virtual int get_widget_state() = 0;
};
@ -59,7 +59,7 @@ class StatefulToggleButton : public StateButton, public Gtk::ToggleButton
void on_toggled ();
std::string get_widget_name() const { return get_name(); }
void set_widget_name (std::string name) { set_name (name); get_child()->set_name (name); }
void set_widget_name (const std::string& name);
int get_widget_state() { return get_state(); }
};
@ -74,7 +74,7 @@ class StatefulButton : public StateButton, public Gtk::Button
void on_realize ();
std::string get_widget_name() const { return get_name(); }
void set_widget_name (std::string name) { set_name (name); get_child()->set_name (name); }
void set_widget_name (const std::string& name);
int get_widget_state() { return get_state(); }
};

View File

@ -22,6 +22,8 @@
#include <gtkmm/main.h>
#include "pbd/stacktrace.h"
#include <gtkmm2ext/stateful_button.h>
using namespace Gtk;
@ -99,3 +101,31 @@ StatefulToggleButton::on_toggled ()
}
}
}
void
StatefulToggleButton::set_widget_name (const std::string& name)
{
set_name (name);
Widget* w = get_child();
if (w) {
w->set_name (name);
} else {
cerr << "Statefull TOggle button - no child\n";
PBD::stacktrace (cerr, 20);
}
}
void
StatefulButton::set_widget_name (const std::string& name)
{
set_name (name);
Widget* w = get_child();
if (w) {
w->set_name (name);
} else {
cerr << "Stateful button - no child\n";
PBD::stacktrace (cerr, 20);
}
}

View File

@ -122,22 +122,29 @@ void CoreMidi_MidiPort::read_proc (const MIDIPacketList *pktlist, void *refCon,
if (driver->firstrecv) {
driver->firstrecv = false;
PBD::notify_gui_about_thread_creation (pthread_self(), "COREMIDI");
PBD::notify_gui_about_thread_creation ("gui", pthread_self(), "COREMIDI", 256);
}
for (unsigned int i = 0; i < pktlist->numPackets; ++i) {
driver->bytes_read += packet->length;
driver->bytes_read += packet->length;
if (driver->input_parser) {
driver->input_parser->raw_preparse (*driver->input_parser, packet->data, packet->length);
for (int i = 0; i < packet->length; i++) {
driver->input_parser->scanner (packet->data[i]);
}
driver->input_parser->raw_postparse (*driver->input_parser, packet->data, packet->length);
}
packet = MIDIPacketNext(packet);
//driver->input_parser->raw_preparse (*driver->input_parser, packet->data, packet->length);
/* XXX This is technically the wrong timebase, since it is based on
host time.
*/
driver->input_parser->set_timestamp (packet->timestamp);
for (int i = 0; i < packet->length; i++) {
driver->input_parser->scanner (packet->data[i]);
}
//driver->input_parser->raw_postparse (*driver->input_parser, packet->data, packet->length);
}
packet = MIDIPacketNext(packet);
}
}

View File

@ -20,9 +20,11 @@
#include <fcntl.h>
#include <cerrno>
#include <cassert>
#include <cstring>
#include <cstdlib>
#include "pbd/error.h"
#include "pbd/compose.h"
#include "midi++/types.h"
#include "midi++/jack.h"
@ -39,11 +41,10 @@ JACK_MidiPort::JACK_MidiPort(const XMLNode& node, jack_client_t* jack_client)
, _jack_input_port(NULL)
, _jack_output_port(NULL)
, _last_read_index(0)
, non_process_thread_fifo (512)
, output_fifo (512)
, input_fifo (1024)
{
int err = create_ports (node);
if (!err) {
if (!create_ports (node)) {
_ok = true;
}
}
@ -82,19 +83,16 @@ JACK_MidiPort::cycle_start (nframes_t nframes)
const nframes_t event_count = jack_midi_get_event_count(jack_buffer);
jack_midi_event_t ev;
nframes_t cycle_start_frame = jack_last_frame_time (_jack_client);
for (nframes_t i=0; i < event_count; ++i) {
timestamp_t cycle_start_frame = jack_last_frame_time (_jack_client);
for (nframes_t i = 0; i < event_count; ++i) {
jack_midi_event_get (&ev, jack_buffer, i);
if (input_parser) {
for (size_t i = 0; i < ev.size; i++) {
input_parser->set_timestamp (cycle_start_frame + ev.time);
input_parser->scanner (ev.buffer[i]);
}
}
input_fifo.write (cycle_start_frame + ev.time, (Evoral::EventType) 0, ev.size, ev.buffer);
}
if (event_count) {
xthread.wakeup ();
}
}
}
@ -104,6 +102,8 @@ JACK_MidiPort::cycle_end ()
if (_jack_output_port != 0) {
flush (jack_port_get_buffer (_jack_output_port, _nframes_this_cycle));
}
Port::cycle_end();
}
int
@ -113,10 +113,10 @@ JACK_MidiPort::write(byte * msg, size_t msglen, timestamp_t timestamp)
if (!is_process_thread()) {
Glib::Mutex::Lock lm (non_process_thread_fifo_lock);
Glib::Mutex::Lock lm (output_fifo_lock);
RingBuffer< Evoral::Event<double> >::rw_vector vec;
non_process_thread_fifo.get_write_vector (&vec);
output_fifo.get_write_vector (&vec);
if (vec.len[0] + vec.len[1] < 1) {
error << "no space in FIFO for non-process thread MIDI write" << endmsg;
@ -129,10 +129,10 @@ JACK_MidiPort::write(byte * msg, size_t msglen, timestamp_t timestamp)
vec.buf[1]->set (msg, msglen, timestamp);
}
non_process_thread_fifo.increment_write_idx (1);
output_fifo.increment_write_idx (1);
ret = msglen;
} else {
assert(_jack_output_port);
@ -164,11 +164,13 @@ JACK_MidiPort::write(byte * msg, size_t msglen, timestamp_t timestamp)
}
if (ret > 0 && output_parser) {
output_parser->raw_preparse (*output_parser, msg, ret);
// ardour doesn't care about this and neither should your app, probably
// output_parser->raw_preparse (*output_parser, msg, ret);
for (int i = 0; i < ret; i++) {
output_parser->scanner (msg[i]);
}
output_parser->raw_postparse (*output_parser, msg, ret);
// ardour doesn't care about this and neither should your app, probably
// output_parser->raw_postparse (*output_parser, msg, ret);
}
return ret;
@ -180,7 +182,7 @@ JACK_MidiPort::flush (void* jack_port_buffer)
RingBuffer< Evoral::Event<double> >::rw_vector vec;
size_t written;
non_process_thread_fifo.get_read_vector (&vec);
output_fifo.get_read_vector (&vec);
if (vec.len[0] + vec.len[1]) {
// cerr << "Flush " << vec.len[0] + vec.len[1] << " events from non-process FIFO\n";
@ -205,15 +207,28 @@ JACK_MidiPort::flush (void* jack_port_buffer)
}
if ((written = vec.len[0] + vec.len[1]) != 0) {
non_process_thread_fifo.increment_read_idx (written);
output_fifo.increment_read_idx (written);
}
}
int
JACK_MidiPort::read(byte * buf, size_t bufsize)
JACK_MidiPort::read (byte * buf, size_t bufsize)
{
cerr << "This program is improperly written. JACK_MidiPort::read() should never be called\n";
abort ();
timestamp_t time;
Evoral::EventType type;
uint32_t size;
byte buffer[input_fifo.capacity()];
while (input_fifo.read (&time, &type, &size, buffer)) {
if (input_parser) {
input_parser->set_timestamp (time);
for (uint32_t i = 0; i < size; ++i) {
input_parser->scanner (buffer[i]);
}
}
}
return 0;
}
int

View File

@ -115,6 +115,8 @@ Manager::add_port (const XMLNode& node)
outputPort = port;
}
PortsChanged (); /* EMIT SIGNAL */
return port;
}
@ -124,11 +126,16 @@ Manager::remove_port (Port* port)
if (inputPort == port) {
inputPort = 0;
}
if (outputPort == port) {
outputPort = 0;
}
_ports.remove (port);
delete port;
PortsChanged (); /* EMIT SIGNAL */
return 0;
}

View File

@ -30,9 +30,13 @@
#include <glibmm/thread.h>
#include "pbd/ringbuffer.h"
#include <jack/jack.h>
#include <jack/midiport.h>
#include "pbd/ringbuffer.h"
#include "pbd/crossthread.h"
#include "evoral/EventRingBuffer.hpp"
#include "midi++/port.h"
#include "midi++/event.h"
@ -49,16 +53,16 @@ public:
int write(byte *msg, size_t msglen, timestamp_t timestamp);
int read(byte *buf, size_t max);
/* No select(2)/poll(2)-based I/O */
virtual int selectable() const { return -1; }
int selectable() const { return xthread.selectable(); }
bool must_drain_selectable() const { return true; }
virtual void cycle_start(nframes_t nframes);
virtual void cycle_end();
void cycle_start(nframes_t nframes);
void cycle_end();
static std::string typestring;
virtual XMLNode& get_state () const;
virtual void set_state (const XMLNode&);
XMLNode& get_state () const;
void set_state (const XMLNode&);
static void set_process_thread (pthread_t);
static pthread_t get_process_thread () { return _process_thread; }
@ -79,13 +83,16 @@ private:
jack_port_t* _jack_output_port;
nframes_t _last_read_index;
timestamp_t _last_write_timestamp;
CrossThreadChannel xthread;
void flush (void* jack_port_buffer);
static pthread_t _process_thread;
RingBuffer< Evoral::Event<double> > non_process_thread_fifo;
Glib::Mutex non_process_thread_fifo_lock;
RingBuffer< Evoral::Event<double> > output_fifo;
Evoral::EventRingBuffer<timestamp_t> input_fifo;
Glib::Mutex output_fifo_lock;
};

View File

@ -83,6 +83,8 @@ class Manager {
int get_known_ports (std::vector<PortSet>&);
sigc::signal<void> PortsChanged;
private:
/* This is a SINGLETON pattern */

View File

@ -112,6 +112,7 @@ class Port : public sigc::trackable {
* @return File descriptor, or -1 if not selectable.
*/
virtual int selectable() const = 0;
virtual bool must_drain_selectable() const { return false; }
static void gtk_read_callback (void *ptr, int fd, int cond);
static void write_callback (byte *msg, unsigned int len, void *);

View File

@ -33,36 +33,24 @@
using namespace std;
using namespace PBD;
using namespace Glib;
uint32_t BaseUI::rt_bit = 1;
uint64_t BaseUI::rt_bit = 1;
BaseUI::RequestType BaseUI::CallSlot = BaseUI::new_request_type();
BaseUI::BaseUI (string str, bool with_signal_pipe)
: _name (str)
BaseUI::BaseUI (const string& str)
: run_loop_thread (0)
, _name (str)
{
/* odd pseudo-singleton semantics */
base_ui_instance = this;
signal_pipe[0] = -1;
signal_pipe[1] = -1;
request_channel.ios()->connect (sigc::mem_fun (*this, &BaseUI::request_handler));
if (with_signal_pipe) {
if (setup_signal_pipe ()) {
throw failed_constructor ();
}
}
/* derived class must set _ok */
}
BaseUI::~BaseUI()
{
if (signal_pipe[0] >= 0) {
close (signal_pipe[0]);
}
if (signal_pipe[1] >= 0) {
close (signal_pipe[1]);
}
}
BaseUI::RequestType
@ -78,32 +66,53 @@ BaseUI::new_request_type ()
return rt;
}
int
BaseUI::setup_signal_pipe ()
void
BaseUI::main_thread ()
{
/* setup the pipe that other threads send us notifications/requests
through.
*/
if (pipe (signal_pipe)) {
error << string_compose (_("%1-UI: cannot create error signal pipe (%2)"), _name, ::strerror (errno))
<< endmsg;
return -1;
}
if (fcntl (signal_pipe[0], F_SETFL, O_NONBLOCK)) {
error << string_compose (_("%1-UI: cannot set O_NONBLOCK on signal read pipe (%2)"), _name, ::strerror (errno))
<< endmsg;
return -1;
}
if (fcntl (signal_pipe[1], F_SETFL, O_NONBLOCK)) {
error << string_compose (_("%1-UI: cannot set O_NONBLOCK on signal write pipe (%2)"), _name, ::strerror (errno))
<< endmsg;
return -1;
}
return 0;
thread_init ();
_main_loop->run ();
}
void
BaseUI::run ()
{
/* to be called by UI's that need/want their own distinct, self-created event loop thread.
Derived classes should have set up a handler for IO on request_channel.ios()
*/
_main_loop = MainLoop::create (MainContext::create());
request_channel.ios()->attach (_main_loop->get_context());
run_loop_thread = Thread::create (mem_fun (*this, &BaseUI::main_thread), true);
}
void
BaseUI::quit ()
{
_main_loop->quit ();
run_loop_thread->join ();
}
bool
BaseUI::request_handler (Glib::IOCondition ioc)
{
/* check the transport request pipe */
if (ioc & ~IO_IN) {
_main_loop->quit ();
}
if (ioc & IO_IN) {
request_channel.drain ();
/* there may been an error. we'd rather handle requests first,
and then get IO_HUP or IO_ERR on the next loop.
*/
/* handle requests */
handle_ui_requests ();
}
return true;
}

98
libs/pbd/crossthread.cc Normal file
View File

@ -0,0 +1,98 @@
/*
Copyright (C) 2009 Paul Davis
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <cstdlib>
#include <cerrno>
#include <cstring>
#include <fcntl.h>
#include <unistd.h>
#include "pbd/error.h"
#include "pbd/crossthread.h"
using namespace std;
using namespace PBD;
using namespace Glib;
CrossThreadChannel::CrossThreadChannel ()
{
fds[0] = -1;
fds[1] = -1;
if (pipe (fds)) {
error << "cannot create x-thread pipe for read (%2)" << ::strerror (errno) << endmsg;
return;
}
if (fcntl (fds[0], F_SETFL, O_NONBLOCK)) {
error << "cannot set non-blocking mode for x-thread pipe (read) (" << ::strerror (errno) << ')' << endmsg;
return;
}
if (fcntl (fds[1], F_SETFL, O_NONBLOCK)) {
error << "cannot set non-blocking mode for x-thread pipe (write) (%2)" << ::strerror (errno) << ')' << endmsg;
return;
}
}
CrossThreadChannel::~CrossThreadChannel ()
{
_ios->destroy ();
if (fds[0] >= 0) {
close (fds[0]);
fds[0] = -1;
}
if (fds[1] >= 0) {
close (fds[1]);
fds[1] = -1;
}
}
void
CrossThreadChannel::wakeup ()
{
char c = 0;
::write (fds[1], &c, 1);
}
RefPtr<IOSource>
CrossThreadChannel::ios ()
{
if (!_ios) {
_ios = IOSource::create (fds[0], IOCondition(IO_IN|IO_PRI|IO_ERR|IO_HUP|IO_NVAL));
}
return _ios;
}
void
CrossThreadChannel::drain ()
{
drain (fds[0]);
}
void
CrossThreadChannel::drain (int fd)
{
/* drain selectable fd */
char buf[64];
while (::read (fd, buf, sizeof (buf)) > 0);
}

View File

@ -1,5 +1,6 @@
#include <unistd.h>
#include "pbd/stacktrace.h"
#include "pbd/abstract_ui.h"
#include "pbd/pthread_utils.h"
#include "pbd/failed_constructor.h"
@ -9,81 +10,65 @@
using namespace std;
template <typename RequestObject>
AbstractUI<RequestObject>::AbstractUI (string name, bool with_signal_pipes)
: BaseUI (name, with_signal_pipes)
AbstractUI<RequestObject>::AbstractUI (const string& name)
: BaseUI (name)
{
if (pthread_key_create (&thread_request_buffer_key, 0)) {
cerr << _("cannot create thread request buffer key") << endl;
throw failed_constructor();
PBD::ThreadCreatedWithRequestSize.connect (mem_fun (*this, &AbstractUI<RequestObject>::register_thread));
}
template <typename RequestObject> void
AbstractUI<RequestObject>::register_thread (string target_gui, pthread_t thread_id, string /*thread_name*/, uint32_t num_requests)
{
if (target_gui != name()) {
return;
}
PBD::ThreadCreatedWithRequestSize.connect (mem_fun (*this, &AbstractUI<RequestObject>::register_thread_with_request_count));
}
template <typename RequestObject> void
AbstractUI<RequestObject>::register_thread (pthread_t thread_id, string name)
{
register_thread_with_request_count (thread_id, name, 256);
}
template <typename RequestObject> void
AbstractUI<RequestObject>::register_thread_with_request_count (pthread_t thread_id, string /*thread_name*/, uint32_t num_requests)
{
RequestBuffer* b = new RequestBuffer (num_requests);
{
Glib::Mutex::Lock lm (request_buffer_map_lock);
Glib::Mutex::Lock lm (request_buffer_map_lock);
request_buffers[thread_id] = b;
}
pthread_setspecific (thread_request_buffer_key, b);
per_thread_request_buffer.set (b);
}
template <typename RequestObject> RequestObject*
AbstractUI<RequestObject>::get_request (RequestType rt)
{
RequestBuffer* rbuf = static_cast<RequestBuffer*>(pthread_getspecific (thread_request_buffer_key));
if (rbuf == 0) {
/* Cannot happen, but if it does we can't use the error reporting mechanism */
cerr << _("programming error: ")
<< string_compose ("no %1-UI request buffer found for thread %2", name(), pthread_name())
<< endl;
abort ();
}
RequestBuffer* rbuf = per_thread_request_buffer.get ();
RequestBufferVector vec;
vec.buf[0] = 0;
vec.buf[1] = 0;
rbuf->get_write_vector (&vec);
if (vec.len[0] == 0) {
if (vec.len[1] == 0) {
cerr << string_compose ("no space in %1-UI request buffer for thread %2", name(), pthread_name())
<< endl;
if (rbuf != 0) {
/* we have a per-thread FIFO, use it */
rbuf->get_write_vector (&vec);
if (vec.len[0] == 0) {
return 0;
} else {
vec.buf[1]->type = rt;
return vec.buf[1];
}
} else {
vec.buf[0]->type = rt;
return vec.buf[0];
}
RequestObject* req = new RequestObject;
req->type = rt;
return req;
}
template <typename RequestObject> void
AbstractUI<RequestObject>::handle_ui_requests ()
{
RequestBufferMapIterator i;
RequestBufferVector vec;
/* per-thread buffers first */
request_buffer_map_lock.lock ();
for (i = request_buffers.begin(); i != request_buffers.end(); ++i) {
RequestBufferVector vec;
while (true) {
/* we must process requests 1 by 1 because
@ -110,6 +95,22 @@ AbstractUI<RequestObject>::handle_ui_requests ()
}
request_buffer_map_lock.unlock ();
/* and now, the generic request buffer. same rules as above apply */
Glib::Mutex::Lock lm (request_list_lock);
while (!request_list.empty()) {
RequestObject* req = request_list.front ();
request_list.pop_front ();
lm.release ();
do_request (req);
delete req;
lm.acquire();
}
}
template <typename RequestObject> void
@ -118,31 +119,41 @@ AbstractUI<RequestObject>::send_request (RequestObject *req)
if (base_instance() == 0) {
return; /* XXX is this the right thing to do ? */
}
if (caller_is_ui_thread()) {
// cerr << "GUI thread sent request " << req << " type = " << req->type << endl;
if (caller_is_self ()) {
do_request (req);
} else {
RequestBuffer* rbuf = static_cast<RequestBuffer*> (pthread_getspecific (thread_request_buffer_key));
RequestBuffer* rbuf = per_thread_request_buffer.get ();
if (rbuf == 0) {
/* can't use the error system to report this, because this
thread isn't registered!
if (rbuf != 0) {
rbuf->increment_write_ptr (1);
} else {
/* no per-thread buffer, so just use a list with a lock so that it remains
single-reader/single-writer semantics
*/
cerr << _("programming error: ")
<< string_compose ("AbstractUI::send_request() called from %1 (%2), but no request buffer exists for that thread", name(), pthread_name())
<< endl;
abort ();
Glib::Mutex::Lock lm (request_list_lock);
request_list.push_back (req);
}
// cerr << "thread " << pthread_self() << " sent request " << req << " type = " << req->type << endl;
rbuf->increment_write_ptr (1);
if (signal_pipe[1] >= 0) {
const char c = 0;
write (signal_pipe[1], &c, 1);
}
request_channel.wakeup ();
}
}
template<typename RequestObject> void
AbstractUI<RequestObject>::call_slot (sigc::slot<void> elSlot)
{
if (caller_is_self()) {
elSlot ();
return;
}
RequestObject *req = get_request (BaseUI::CallSlot);
if (req == 0) {
return;
}
req->the_slot = elSlot;
send_request (req);
}

View File

@ -1,5 +1,5 @@
/*
Copyright (C) 1998-99 Paul Barton-Davis
Copyright (C) 1998-2009 Paul Davis
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -34,38 +34,29 @@
class Touchable;
template <class RequestObject>
template<typename RequestObject>
class AbstractUI : public BaseUI
{
public:
AbstractUI (std::string name, bool with_signal_pipe);
AbstractUI (const std::string& name);
virtual ~AbstractUI() {}
virtual bool caller_is_ui_thread() = 0;
void call_slot (sigc::slot<void> el_slot) {
RequestObject *req = get_request (BaseUI::CallSlot);
if (req == 0) {
return;
}
req->slot = el_slot;
send_request (req);
}
void register_thread (pthread_t, std::string);
void register_thread_with_request_count (pthread_t, std::string, uint32_t num_requests);
void register_thread (std::string, pthread_t, std::string, uint32_t num_requests);
void call_slot (sigc::slot<void> el_slot);
protected:
typedef RingBufferNPT<RequestObject> RequestBuffer;
typedef typename RequestBuffer::rw_vector RequestBufferVector;
typedef typename std::map<pthread_t,RequestBuffer*>::iterator RequestBufferMapIterator;
Glib::Mutex request_buffer_map_lock;
typedef std::map<pthread_t,RequestBuffer*> RequestBufferMap;
Glib::Mutex request_buffer_map_lock;
RequestBufferMap request_buffers;
pthread_key_t thread_request_buffer_key;
Glib::Private<RequestBuffer> per_thread_request_buffer;
Glib::Mutex request_list_lock;
std::list<RequestObject*> request_list;
RequestObject* get_request (RequestType);
void handle_ui_requests ();
void send_request (RequestObject *);

View File

@ -26,13 +26,22 @@
#include <sigc++/slot.h>
#include <sigc++/trackable.h>
#include <glibmm/thread.h>
#include <glibmm/main.h>
#include "pbd/crossthread.h"
class BaseUI : virtual public sigc::trackable {
public:
BaseUI (std::string name, bool with_signal_pipes);
BaseUI (const std::string& name);
virtual ~BaseUI();
BaseUI* base_instance() { return base_ui_instance; }
Glib::RefPtr<Glib::MainLoop> main_loop() const { return _main_loop; }
Glib::Thread* event_loop_thread() const { return run_loop_thread; }
bool caller_is_self () const { return Glib::Thread::self() == run_loop_thread; }
std::string name() const { return _name; }
bool ok() const { return _ok; }
@ -49,17 +58,31 @@ class BaseUI : virtual public sigc::trackable {
static RequestType new_request_type();
static RequestType CallSlot;
void run ();
void quit ();
virtual void call_slot (sigc::slot<void> theSlot) = 0;
protected:
int signal_pipe[2];
CrossThreadChannel request_channel;
bool _ok;
Glib::RefPtr<Glib::MainLoop> _main_loop;
Glib::Thread* run_loop_thread;
virtual void thread_init () {};
bool request_handler (Glib::IOCondition);
virtual void handle_ui_requests () = 0;
private:
std::string _name;
BaseUI* base_ui_instance;
static uint64_t rt_bit;
static uint32_t rt_bit;
int setup_signal_pipe ();
int setup_request_pipe ();
void main_thread ();
};
#endif /* __pbd_base_ui_h__ */

View File

@ -20,38 +20,25 @@
#ifndef __pbd__crossthread_h__
#define __pbd__crossthread_h__
#include "pbd/abstract_ui.h"
#include <sigc++/sigc++.h>
#include <pthread.h>
#include <glibmm/main.h>
template<class RequestType>
void
call_slot_from_thread_or_dispatch_it (pthread_t thread_id, AbstractUI<RequestType>& ui, sigc::slot<void> theSlot)
{
/* when called, this function will determine whether the calling thread
is the same as thread specified by the first argument. if it is,
the we execute the slot. if not, we ask the interface given by the second
argument to call the slot.
*/
class CrossThreadChannel {
public:
CrossThreadChannel();
~CrossThreadChannel();
void wakeup();
int selectable() const { return fds[0]; }
if (pthread_self() == thread_id) {
theSlot ();
} else {
ui.call_slot (theSlot);
}
}
void drain ();
static void drain (int fd);
template<class RequestType>
sigc::slot<void>
crossthread_safe (pthread_t thread_id, AbstractUI<RequestType>& ui, sigc::slot<void> theSlot)
{
/* this function returns a slot that will ensure that theSlot is either
called by the specified thread or passed to the interface via
AbstractUI::call_slot().
*/
return sigc::bind (sigc::ptr_fun (call_slot_from_thread_or_dispatch_it<RequestType>),
thread_id, ui, theSlot);
}
Glib::RefPtr<Glib::IOSource> ios();
bool ok() const { return fds[0] >= 0 && fds[1] >= 0; }
private:
Glib::RefPtr<Glib::IOSource> _ios; // lazily constructed
int fds[2];
};
#endif /* __pbd__crossthread_h__ */

View File

@ -27,19 +27,18 @@
#include <sigc++/sigc++.h>
int pthread_create_and_store (std::string name, pthread_t *thread, pthread_attr_t *attr, void * (*start_routine)(void *), void * arg);
int pthread_create_and_store (std::string name, pthread_t *thread, void * (*start_routine)(void *), void * arg);
void pthread_cancel_one (pthread_t thread);
void pthread_kill_all (int signum);
void pthread_cancel_all ();
void pthread_exit_pbd (void* status);
std::string pthread_name ();
namespace PBD {
extern void notify_gui_about_thread_creation (pthread_t, std::string, int requests = 256);
extern void notify_gui_about_thread_creation (std::string, pthread_t, std::string, int requests = 256);
extern void notify_gui_about_thread_exit (pthread_t);
extern sigc::signal<void,pthread_t> ThreadLeaving;
extern sigc::signal<void,pthread_t,std::string,uint32_t> ThreadCreatedWithRequestSize;
extern sigc::signal<void,pthread_t> ThreadLeaving;
extern sigc::signal<void,std::string,pthread_t,std::string,uint32_t> ThreadCreatedWithRequestSize;
}
#endif /* __pbd_pthread_utils__ */

View File

@ -36,8 +36,8 @@ static pthread_mutex_t thread_map_lock = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t gui_notify_lock = PTHREAD_MUTEX_INITIALIZER;
namespace PBD {
sigc::signal<void,pthread_t> ThreadLeaving;
sigc::signal<void,pthread_t,std::string,uint32_t> ThreadCreatedWithRequestSize;
sigc::signal<void,pthread_t> ThreadLeaving;
sigc::signal<void,std::string, pthread_t,std::string,uint32_t> ThreadCreatedWithRequestSize;
}
using namespace PBD;
@ -52,10 +52,10 @@ static int thread_creator (pthread_t* thread_id, const pthread_attr_t* attr, voi
}
void
PBD::notify_gui_about_thread_creation (pthread_t thread, std::string str, int request_count)
PBD::notify_gui_about_thread_creation (std::string target_gui, pthread_t thread, std::string str, int request_count)
{
pthread_mutex_lock (&gui_notify_lock);
ThreadCreatedWithRequestSize (thread, str, request_count);
ThreadCreatedWithRequestSize (target_gui, thread, str, request_count);
pthread_mutex_unlock (&gui_notify_lock);
}
@ -68,35 +68,27 @@ PBD::notify_gui_about_thread_exit (pthread_t thread)
}
int
pthread_create_and_store (string name, pthread_t *thread, pthread_attr_t *attr, void * (*start_routine)(void *), void * arg)
pthread_create_and_store (string name, pthread_t *thread, void * (*start_routine)(void *), void * arg)
{
int ret;
pthread_attr_t default_attr;
bool use_default_attr = (attr == NULL);
int ret;
if (use_default_attr) {
// set default stack size to sensible default for memlocking
pthread_attr_init(&default_attr);
pthread_attr_setstacksize(&default_attr, 500000);
attr = &default_attr;
}
// set default stack size to sensible default for memlocking
pthread_attr_init(&default_attr);
pthread_attr_setstacksize(&default_attr, 500000);
if ((ret = thread_creator (thread, attr, start_routine, arg)) == 0) {
if ((ret = thread_creator (thread, &default_attr, start_routine, arg)) == 0) {
std::pair<string,pthread_t> newpair;
newpair.first = name;
newpair.second = *thread;
pthread_mutex_lock (&thread_map_lock);
all_threads.insert (newpair);
pthread_mutex_unlock (&thread_map_lock);
}
if (use_default_attr) {
pthread_attr_destroy(&default_attr);
}
pthread_attr_destroy(&default_attr);
return ret;
}
@ -131,19 +123,6 @@ pthread_kill_all (int signum)
pthread_mutex_unlock (&thread_map_lock);
}
void
pthread_cancel_all ()
{
pthread_mutex_lock (&thread_map_lock);
for (ThreadMap::iterator i = all_threads.begin(); i != all_threads.end(); ++i) {
if (i->second != pthread_self()) {
pthread_cancel (i->second);
}
}
all_threads.clear();
pthread_mutex_unlock (&thread_map_lock);
}
void
pthread_cancel_one (pthread_t thread)
{

View File

@ -57,6 +57,7 @@ def build(bld):
command.cc
convert.cc
controllable.cc
crossthread.cc
enumwriter.cc
dmalloc.cc
error.cc

View File

@ -53,7 +53,6 @@ BasicUI::register_thread (std::string name)
std::string pool_name = name;
pool_name += " events";
PBD::notify_gui_about_thread_creation (pthread_self(), name);
SessionEvent::create_per_thread_pool (pool_name, 64);
}

View File

@ -23,7 +23,9 @@
#include <string>
#include <jack/types.h>
#include "control_protocol/timecode.h"
namespace ARDOUR {

View File

@ -1028,7 +1028,7 @@ TranzportControlProtocol::monitor_work ()
uint8_t offline = 0;
PBD::notify_gui_about_thread_creation (pthread_self(), X_("Tranzport"));
PBD::notify_gui_about_thread_creation ("gui", pthread_self(), X_("Tranzport"));
pthread_setcancelstate (PTHREAD_CANCEL_ENABLE, 0);
pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, 0);
next_track ();

View File

@ -87,7 +87,7 @@ MackieControlProtocol::MackieControlProtocol (Session& session)
cout << "MackieControlProtocol::MackieControlProtocol" << endl;
#endif
// will start reading from ports, as soon as there are some
pthread_create_and_store (X_("mackie monitor"), &thread, 0, _monitor_work, this);
pthread_create_and_store (X_("mackie monitor"), &thread, _monitor_work, this);
}
MackieControlProtocol::~MackieControlProtocol()
@ -1423,7 +1423,7 @@ LedState MackieControlProtocol::clicking_release (Button &)
LedState MackieControlProtocol::global_solo_press (Button &)
{
bool state = !session->soloing();
session->set_all_solo ( state );
session->set_solo (session->get_routes(), state);
return state;
}

View File

@ -302,15 +302,11 @@ OSC::init_osc_thread ()
return false;
}
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setstacksize(&attr, 500000);
pthread_create_and_store (X_("OSC"), &_osc_thread, &OSC::_osc_receiver, this);
pthread_create_and_store (X_("OSC"), &_osc_thread, &attr, &OSC::_osc_receiver, this);
if (!_osc_thread) {
return false;
}
pthread_attr_destroy(&attr);
//pthread_detach (_osc_thread);
return true;

View File

@ -129,7 +129,7 @@ PowermateControlProtocol::set_active (bool inActivate)
return -1;
}
if (pthread_create_and_store ("Powermate", &mThread, 0, SerialThreadEntry, this) == 0) {
if (pthread_create_and_store ("Powermate", &mThread, SerialThreadEntry, this) == 0) {
_active = true;
} else {
return -1;

View File

@ -86,16 +86,16 @@ TranzportControlProtocol::set_active (bool yn)
return -1;
}
if (pthread_create_and_store (X_("tranzport monitor"), &thread, 0, _monitor_work, this) == 0) {
if (pthread_create_and_store (X_("tranzport monitor"), &thread, _monitor_work, this) == 0) {
_active = true;
#if TRANZPORT_THREADS
if (pthread_create_and_store (X_("tranzport read"), &thread_read, 0, _read_work, this) == 0) {
if (pthread_create_and_store (X_("tranzport read"), &thread_read, _read_work, this) == 0) {
_active_read = true;
if (pthread_create_and_store (X_("tranzport write"), &thread_write, 0, _write_work, this) == 0) {
if (pthread_create_and_store (X_("tranzport write"), &thread_write, _write_work, this) == 0) {
_active_write = true;
if (pthread_create_and_store (X_("tranzport process"), &thread_process, 0, _process_work, this) == 0) {
if (pthread_create_and_store (X_("tranzport process"), &thread_process, _process_work, this) == 0) {
_active_process = true;
if (pthread_create_and_store (X_("tranzport timer"), &thread_timer, 0, _process_timer, this) == 0) {
if (pthread_create_and_store (X_("tranzport timer"), &thread_timer, _process_timer, this) == 0) {
_active_process = true;
#endif
} else {