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[]) 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), primary_clock (X_("primary"), false, X_("TransportClockDisplay"), true, true, false, true),
secondary_clock (X_("secondary"), false, X_("SecondaryClockDisplay"), 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 () ARDOUR_UI::solo_alert_toggle ()
{ {
if (session) { 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. (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); pthread_detach (import_status.thread);
while (!import_status.done && !import_status.cancel) { while (!import_status.done && !import_status.cancel) {
@ -879,7 +879,6 @@ Editor::finish_bringing_in_material (boost::shared_ptr<Region> region, uint32_t
void * void *
Editor::_import_thread (void *arg) Editor::_import_thread (void *arg)
{ {
PBD::notify_gui_about_thread_creation (pthread_self(), X_("Import"));
SessionEvent::create_per_thread_pool ("import events", 64); SessionEvent::create_per_thread_pool ("import events", 64);
Editor *ed = (Editor *) arg; Editor *ed = (Editor *) arg;

View file

@ -3653,7 +3653,6 @@ Editor::unfreeze_route ()
void* void*
Editor::_freeze_thread (void* arg) Editor::_freeze_thread (void* arg)
{ {
PBD::notify_gui_about_thread_creation (pthread_self(), X_("Freeze"));
SessionEvent::create_per_thread_pool ("freeze events", 64); SessionEvent::create_per_thread_pool ("freeze events", 64);
return static_cast<Editor*>(arg)->freeze_thread (); return static_cast<Editor*>(arg)->freeze_thread ();
@ -3702,13 +3701,7 @@ Editor::freeze_route ()
itt.cancel = false; itt.cancel = false;
itt.progress = 0.0f; itt.progress = 0.0f;
pthread_attr_t attr; pthread_create_and_store (X_("freezer"), &itt.thread, _freeze_thread, this);
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);
track_canvas->get_window()->set_cursor (Gdk::Cursor (Gdk::WATCH)); 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 current_timefx->first_delete = current_timefx->signal_delete_event().connect
(mem_fun (current_timefx, &TimeFXDialog::delete_in_progress)); (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 (); current_timefx->hide ();
error << _("timefx cannot be started - thread creation error") << endmsg; error << _("timefx cannot be started - thread creation error") << endmsg;
return -1; return -1;
@ -337,7 +337,6 @@ Editor::do_timefx (TimeFXDialog& dialog)
void* void*
Editor::timefx_thread (void *arg) Editor::timefx_thread (void *arg)
{ {
PBD::notify_gui_about_thread_creation (pthread_self(), X_("TimeFX"));
SessionEvent::create_per_thread_pool ("timefx events", 64); SessionEvent::create_per_thread_pool ("timefx events", 64);
TimeFXDialog* tsd = static_cast<TimeFXDialog*>(arg); TimeFXDialog* tsd = static_cast<TimeFXDialog*>(arg);

View file

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

View file

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

View file

@ -183,6 +183,7 @@ private:
node.add_property ("mode", smod); node.add_property ("mode", smod);
if (MIDI::Manager::instance()->add_port (node) != 0) { if (MIDI::Manager::instance()->add_port (node) != 0) {
cerr << " there are now " << MIDI::Manager::instance()->nports() << endl;
ports_changed (); 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 (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
#if 0
/* Primary-Tertiary-click applies change to all routes */ /* Primary-Tertiary-click applies change to all routes */
_session.begin_reversible_command (_("mute change")); _session.begin_reversible_command (_("mute change"));
Session::GlobalMuteStateCommand *cmd = new Session::GlobalMuteStateCommand(_session, this); Session::GlobalMuteStateCommand *cmd = new Session::GlobalMuteStateCommand(_session, this);
_session.set_all_mute (!_route->muted()); _session.set_mute (!_route->muted());
cmd->mark(); cmd->mark();
_session.add_command(cmd); _session.add_command(cmd);
_session.commit_reversible_command (); _session.commit_reversible_command ();
multiple_mute_change = true; multiple_mute_change = true;
#endif
} else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) { } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
#if 0
/* Primary-button1 applies change to the mix group. /* Primary-button1 applies change to the mix group.
NOTE: Primary-button2 is MIDI learn. NOTE: Primary-button2 is MIDI learn.
*/ */
@ -281,15 +284,18 @@ RouteUI::mute_press(GdkEventButton* ev)
if (ev->button == 1) { if (ev->button == 1) {
set_route_group_mute (_route, !_route->muted()); set_route_group_mute (_route, !_route->muted());
} }
#endif
} else { } else {
#if 0
/* plain click applies change to this route */ /* plain click applies change to this route */
if (wait_for_release) { if (wait_for_release) {
_route->set_mute (!_route->muted(), this); _route->set_mute (!_route->muted(), this);
} else { } else {
reversibly_apply_route_boolean ("mute change", &Route::set_mute, !_route->muted(), this); reversibly_apply_route_boolean ("mute change", &Route::set_mute, !_route->muted(), this);
} }
#endif
} }
} }
} }
@ -318,6 +324,18 @@ RouteUI::mute_release(GdkEventButton*)
return true; 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 bool
RouteUI::solo_press(GdkEventButton* ev) RouteUI::solo_press(GdkEventButton* ev)
{ {
@ -364,6 +382,7 @@ RouteUI::solo_press(GdkEventButton* ev)
/* Primary-Tertiary-click applies change to all routes */ /* Primary-Tertiary-click applies change to all routes */
bool was_not_latched = false; bool was_not_latched = false;
if (!Config->get_solo_latched ()) { if (!Config->get_solo_latched ()) {
was_not_latched = true; was_not_latched = true;
/* /*
@ -373,28 +392,25 @@ RouteUI::solo_press(GdkEventButton* ev)
*/ */
Config->set_solo_latched (true); Config->set_solo_latched (true);
} }
_session.begin_reversible_command (_("solo change"));
Session::GlobalSoloStateCommand *cmd = new Session::GlobalSoloStateCommand(_session, this); SessionEvent* ev = new SessionEvent (SessionEvent::RealTimeOperation, SessionEvent::Add, SessionEvent::Immediate, 0, 0.0);
_session.set_all_solo (!_route->soloed()); ev->rt_slot = bind (sigc::mem_fun (_session, &Session::set_solo), _session.get_routes(), !_route->soloed());
cmd->mark(); ev->rt_return = bind (sigc::mem_fun (*this, &RouteUI::post_solo_cleanup), was_not_latched);
_session.add_command (cmd);
_session.commit_reversible_command (); _session.queue_event (ev);
multiple_solo_change = true;
if (was_not_latched) {
Config->set_solo_latched (false);
}
} else if (Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) { } else if (Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
// Primary-Secondary-click: exclusively solo this track, not a toggle */ // Primary-Secondary-click: exclusively solo this track, not a toggle */
_session.begin_reversible_command (_("solo change")); //boost::shared_ptr<RouteList> rl (new RouteList);
Session::GlobalSoloStateCommand *cmd = new Session::GlobalSoloStateCommand (_session, this); //rl->push_back (route());
_session.set_all_solo (false);
_route->set_solo (true, this); //SessionEvent* ev = new SessionEvent (SessionEvent::RealTimeOperation, SessionEvent::Add, SessionEvent::Immediate, 0, 0.0);
cmd->mark(); // ev->rt_slot = bind (sigc::mem_fun (_session, &Session::set_just_one_solo), rl, true);
_session.add_command(cmd); //ev->rt_return = sigc::mem_fun (*this, &RouteUI::post_rtop_cleanup);
_session.commit_reversible_command ();
//_session.queue_event (ev);
} else if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) { } 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)) { } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
#if 0
/* Primary-button1: solo mix group. /* Primary-button1: solo mix group.
NOTE: Primary-button2 is MIDI learn. NOTE: Primary-button2 is MIDI learn.
*/ */
if (ev->button == 1) { 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 { } else {
/* click: solo this route */ /* click: solo this route */
if (wait_for_release) {
_route->set_solo (!_route->soloed(), this); boost::shared_ptr<RouteList> rl (new RouteList);
} else { rl->push_back (route());
reversibly_apply_route_boolean ("solo change", &Route::set_solo, !_route->soloed(), this);
} 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))) { } 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); 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); ev->rt_return = sigc::mem_fun (*this, &RouteUI::post_rtop_cleanup);
_session.queue_event (ev); _session.queue_event (ev);
@ -533,7 +556,7 @@ RouteUI::rec_enable_press(GdkEventButton* ev)
*/ */
if (ev->button == 1) { 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)) { } else if (Keyboard::is_context_menu_event (ev)) {
@ -545,7 +568,7 @@ RouteUI::rec_enable_press(GdkEventButton* ev)
rl->push_back (route()); rl->push_back (route());
SessionEvent* ev = new SessionEvent (SessionEvent::RealTimeOperation, SessionEvent::Add, SessionEvent::Immediate, 0, 0.0); 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); ev->rt_return = sigc::mem_fun (*this, &RouteUI::post_rtop_cleanup);
_session.queue_event (ev); _session.queue_event (ev);

View file

@ -217,6 +217,7 @@ class RouteUI : public virtual AxisView
void post_rtop_cleanup (ARDOUR::SessionEvent* ev); void post_rtop_cleanup (ARDOUR::SessionEvent* ev);
void post_group_rtop_cleanup (ARDOUR::SessionEvent* ev, ARDOUR::RouteGroup*, ARDOUR::RouteGroup::Property); 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__ */ #endif /* __ardour_route_ui__ */

View file

@ -733,7 +733,6 @@ SoundFileBrowser::found_search_clicked ()
void* void*
freesound_search_thread_entry (void* arg) 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); SessionEvent::create_per_thread_pool ("freesound events", 64);
static_cast<SoundFileBrowser*>(arg)->freesound_search_thread (); static_cast<SoundFileBrowser*>(arg)->freesound_search_thread ();
@ -757,7 +756,7 @@ SoundFileBrowser::freesound_search_clicked ()
searching = true; searching = true;
freesound_search_btn.set_label(_("Cancel")); freesound_search_btn.set_label(_("Cancel"));
pthread_t freesound_thr; 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 void
Analyser::work () 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); SessionEvent::create_per_thread_pool ("Analyser", 64);
while (true) { 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 MidiRegion;
class MidiSource; class MidiSource;
class MidiTrack; class MidiTrack;
class MidiControlUI;
class NamedSelection; class NamedSelection;
class Playlist; class Playlist;
class PluginInsert; class PluginInsert;
@ -616,14 +617,14 @@ class Session : public PBD::StatefulDestructible, public SessionEventManager, pu
bool soloing() const { return _non_soloed_outs_muted; } bool soloing() const { return _non_soloed_outs_muted; }
bool listening() const { return _listen_cnt > 0; } bool listening() const { return _listen_cnt > 0; }
void set_all_solo (bool); void set_solo (boost::shared_ptr<RouteList>, bool);
void set_all_mute (bool); void set_mute (boost::shared_ptr<RouteList>, bool);
void set_all_listen (bool); void set_listen (boost::shared_ptr<RouteList>, bool);
sigc::signal<void,bool> SoloActive; sigc::signal<void,bool> SoloActive;
sigc::signal<void> SoloChanged; 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 */ /* 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 non_realtime_work_pending() const { return static_cast<bool>(post_transport_work()); }
bool process_can_proceed() const { return !(post_transport_work() & ProcessCannotProceedMask); } bool process_can_proceed() const { return !(post_transport_work() & ProcessCannotProceedMask); }
struct MIDIRequest { MidiControlUI* midi_control_ui;
enum Type {
PortChange,
Quit
};
Type type;
};
Glib::Mutex midi_lock;
pthread_t midi_thread;
int midi_request_pipe[2];
RingBuffer<MIDIRequest*> midi_requests;
int start_midi_thread (); int start_midi_thread ();
void terminate_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 (); int use_config_midi_ports ();
void set_play_loop (bool yn); void set_play_loop (bool yn);

View file

@ -135,7 +135,9 @@ _thread_init_callback (void * /*arg*/)
knows about it. 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); SessionEvent::create_per_thread_pool (X_("Audioengine"), 512);
MIDI::JACK_MidiPort::set_process_thread (pthread_self()); MIDI::JACK_MidiPort::set_process_thread (pthread_self());

View file

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

View file

@ -176,7 +176,6 @@ ExportChannelConfiguration::write_file ()
void * void *
ExportChannelConfiguration::_write_files (void *arg) ExportChannelConfiguration::_write_files (void *arg)
{ {
notify_gui_about_thread_creation (pthread_self(), "Export post-processing");
SessionEvent::create_per_thread_pool ("exporter events", 64); SessionEvent::create_per_thread_pool ("exporter events", 64);
// cc can be trated like 'this' // 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_playlist.h"
#include "ardour/midi_region.h" #include "ardour/midi_region.h"
#include "ardour/midi_track.h" #include "ardour/midi_track.h"
#include "ardour/midi_ui.h"
#include "ardour/named_selection.h" #include "ardour/named_selection.h"
#include "ardour/playlist.h" #include "ardour/playlist.h"
#include "ardour/plugin_insert.h" #include "ardour/plugin_insert.h"
@ -137,8 +138,6 @@ Session::Session (AudioEngine &eng,
_butler (new Butler (this)), _butler (new Butler (this)),
_post_transport_work (0), _post_transport_work (0),
_send_timecode_update (false), _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), diskstreams (new DiskstreamList),
routes (new RouteList), routes (new RouteList),
_total_free_4k_blocks (0), _total_free_4k_blocks (0),
@ -224,8 +223,6 @@ Session::Session (AudioEngine &eng,
_butler (new Butler (this)), _butler (new Butler (this)),
_post_transport_work (0), _post_transport_work (0),
_send_timecode_update (false), _send_timecode_update (false),
midi_thread (pthread_t (0)),
midi_requests (16),
diskstreams (new DiskstreamList), diskstreams (new DiskstreamList),
routes (new RouteList), routes (new RouteList),
_total_free_4k_blocks (0), _total_free_4k_blocks (0),
@ -366,7 +363,8 @@ Session::destroy ()
Stateful::loading_state_version = 0; Stateful::loading_state_version = 0;
_butler->terminate_thread (); _butler->terminate_thread ();
//terminate_midi_thread ();
delete midi_control_ui;
if (click_data != default_click) { if (click_data != default_click) {
delete [] click_data; delete [] click_data;
@ -3518,10 +3516,8 @@ Session::is_auditioning () const
} }
void 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) { for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
if (!(*i)->is_hidden()) { if (!(*i)->is_hidden()) {
(*i)->set_solo (yn, this); (*i)->set_solo (yn, this);
@ -3532,10 +3528,8 @@ Session::set_all_solo (bool yn)
} }
void 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) { for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
if (!(*i)->is_hidden()) { if (!(*i)->is_hidden()) {
(*i)->set_listen (yn, this); (*i)->set_listen (yn, this);
@ -3546,10 +3540,8 @@ Session::set_all_listen (bool yn)
} }
void 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) { for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
if (!(*i)->is_hidden()) { if (!(*i)->is_hidden()) {
(*i)->set_mute (yn, this); (*i)->set_mute (yn, this);
@ -3605,7 +3597,7 @@ Session::graph_reordered ()
} }
void 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()) { if (!writable()) {
return; return;
@ -4037,8 +4029,7 @@ Session::write_one_track (AudioTrack& track, nframes_t start, nframes_t end,
return result; return result;
} }
// any bigger than this seems to cause stack overflows in called functions const nframes_t chunk_size = (256 * 1024)/4;
const nframes_t chunk_size = (128 * 1024)/4;
// block all process callback handling // 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 */ /* cancel all solo or all listen when solo control mode changes */
if (Config->get_solo_control_is_listen_control()) { if (Config->get_solo_control_is_listen_control()) {
set_all_solo (false); set_solo (routes.reader(), false);
} else { } else {
set_all_listen (false); set_listen (routes.reader(), false);
} }
} }

View file

@ -28,6 +28,7 @@
#include <boost/shared_ptr.hpp> #include <boost/shared_ptr.hpp>
#include <glibmm/main.h>
#include "midi++/mmc.h" #include "midi++/mmc.h"
#include "midi++/port.h" #include "midi++/port.h"
@ -41,6 +42,7 @@
#include "ardour/session.h" #include "ardour/session.h"
#include "ardour/audio_track.h" #include "ardour/audio_track.h"
#include "ardour/midi_track.h" #include "ardour/midi_track.h"
#include "ardour/midi_ui.h"
#include "ardour/audio_diskstream.h" #include "ardour/audio_diskstream.h"
#include "ardour/slave.h" #include "ardour/slave.h"
#include "ardour/cycles.h" #include "ardour/cycles.h"
@ -52,6 +54,7 @@ using namespace std;
using namespace ARDOUR; using namespace ARDOUR;
using namespace PBD; using namespace PBD;
using namespace MIDI; using namespace MIDI;
using namespace Glib;
MachineControl::CommandSignature MMC_CommandSignature; MachineControl::CommandSignature MMC_CommandSignature;
MachineControl::ResponseSignature MMC_ResponseSignature; MachineControl::ResponseSignature MMC_ResponseSignature;
@ -146,7 +149,6 @@ Session::set_mtc_port (string port_tag)
out: out:
MTC_PortChanged(); /* EMIT SIGNAL */ MTC_PortChanged(); /* EMIT SIGNAL */
change_midi_ports ();
set_dirty(); set_dirty();
return 0; return 0;
} }
@ -244,7 +246,6 @@ Session::set_mmc_port (string port_tag)
out: out:
MMC_PortChanged(); /* EMIT SIGNAL */ MMC_PortChanged(); /* EMIT SIGNAL */
change_midi_ports ();
set_dirty(); set_dirty();
return 0; return 0;
} }
@ -278,7 +279,6 @@ Session::set_midi_port (string /*port_tag*/)
out: out:
#endif #endif
MIDI_PortChanged(); /* EMIT SIGNAL */ MIDI_PortChanged(); /* EMIT SIGNAL */
change_midi_ports ();
set_dirty(); set_dirty();
return 0; return 0;
} }
@ -320,7 +320,6 @@ Session::set_midi_clock_port (string port_tag)
out: out:
MIDIClock_PortChanged(); /* EMIT SIGNAL */ MIDIClock_PortChanged(); /* EMIT SIGNAL */
change_midi_ports ();
set_dirty(); set_dirty();
return 0; return 0;
} }
@ -640,9 +639,13 @@ Session::mmc_step (MIDI::MachineControl &/*mmc*/, int steps)
last_mmc_step = now; last_mmc_step = now;
if (!step_queued) { if (!step_queued) {
midi_timeouts.push_back (mem_fun (*this, &Session::mmc_step_timeout)); 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; step_queued = true;
} }
}
} }
void void
@ -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. /** 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 * 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. * the first one with the beginning of this cycle as the new start point.
@ -1022,495 +1015,17 @@ Session::mmc_step_timeout ()
int int
Session::start_midi_thread () Session::start_midi_thread ()
{ {
if (pipe (midi_request_pipe)) { midi_control_ui = new MidiControlUI (*this);
error << string_compose(_("Cannot create transport request signal pipe (%1)"), strerror (errno)) << endmsg; midi_control_ui->run ();
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;
}
return 0; return 0;
} }
void void
Session::terminate_midi_thread () Session::terminate_midi_thread ()
{ {
if (midi_thread) { if (midi_control_ui) {
midi_control_ui->quit ();
MIDIRequest* request = new MIDIRequest;
void* status;
request->type = MIDIRequest::Quit;
midi_requests.write (&request, 1);
poke_midi_thread ();
pthread_join (midi_thread, &status);
} }
} }
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 void
Session::process (nframes_t nframes) 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); MIDI::Manager::instance()->cycle_start(nframes);
_silent = false; _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_data_format_reset = true;
first_file_header_format_reset = true; first_file_header_format_reset = true;
post_export_sync = false; post_export_sync = false;
//midi_thread = (pthread_t) 0; midi_control_ui = 0;
AudioDiskstream::allocate_working_buffers(); AudioDiskstream::allocate_working_buffers();

View file

@ -58,7 +58,6 @@ std::list<boost::weak_ptr<AudioSource> > SourceFactory::files_with_peaks;
static void static void
peak_thread_work () 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); SessionEvent::create_per_thread_pool (X_("PeakFile Builder "), 64);
while (true) { while (true) {

View file

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

View file

@ -47,7 +47,6 @@ using namespace Glib;
using namespace PBD; using namespace PBD;
using std::map; using std::map;
pthread_t UI::gui_thread;
UI *UI::theGtkUI = 0; UI *UI::theGtkUI = 0;
BaseUI::RequestType Gtkmm2ext::ErrorMessage = BaseUI::new_request_type(); 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::AddIdle = BaseUI::new_request_type();
BaseUI::RequestType Gtkmm2ext::AddTimeout = 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) UI::UI (string namestr, int *argc, char ***argv)
: AbstractUI<UIRequest> (namestr, true) : AbstractUI<UIRequest> (namestr)
{ {
theMain = new Main (argc, argv); theMain = new Main (argc, argv);
#ifndef GTK_NEW_TOOLTIP_API #ifndef GTK_NEW_TOOLTIP_API
@ -73,18 +71,20 @@ UI::UI (string namestr, int *argc, char ***argv)
if (!theGtkUI) { if (!theGtkUI) {
theGtkUI = this; theGtkUI = this;
gui_thread = pthread_self ();
} else { } else {
fatal << "duplicate UI requested" << endmsg; fatal << "duplicate UI requested" << endmsg;
/* NOTREACHED */ /* 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], run_loop_thread = Thread::self();
GDK_INPUT_READ,
UI::signal_pipe_callback, /* attach our request source to the default main context */
this);
request_channel.ios()->attach (MainContext::get_default());
errors = new TextViewer (800,600); errors = new TextViewer (800,600);
errors->text().set_editable (false); 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->signal_delete_event().connect (bind (sigc::ptr_fun (just_hide_it), (Window *) errors));
errors->set_type_hint (Gdk::WINDOW_TYPE_HINT_UTILITY); errors->set_type_hint (Gdk::WINDOW_TYPE_HINT_UTILITY);
register_thread (pthread_self(), X_("GUI"));
//load_rcfile (rcfile); //load_rcfile (rcfile);
} }
@ -113,7 +111,7 @@ UI::~UI ()
bool bool
UI::caller_is_ui_thread () UI::caller_is_ui_thread ()
{ {
return pthread_equal (gui_thread, pthread_self()); return Thread::self() == run_loop_thread;
} }
int int
@ -122,7 +120,9 @@ UI::load_rcfile (string path, bool themechange)
/* Yes, pointers to Glib::RefPtr. If these are not kept around, /* Yes, pointers to Glib::RefPtr. If these are not kept around,
* a segfault somewhere deep in the wonderfully robust glib will result. * 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, * 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>* fatal_style = 0;
static Glib::RefPtr<Style>* error_style = 0; static Glib::RefPtr<Style>* error_style = 0;
static Glib::RefPtr<Style>* warning_style = 0; static Glib::RefPtr<Style>* warning_style = 0;
@ -250,14 +250,6 @@ UI::running ()
return _active; return _active;
} }
void
UI::kill ()
{
if (_active) {
pthread_kill (gui_thread, SIGKILL);
}
}
void void
UI::quit () UI::quit ()
{ {
@ -348,18 +340,6 @@ UI::idle_add (int (*func)(void *), void *arg)
/* END abstract_ui interfaces */ /* 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 void
UI::do_request (UIRequest* req) UI::do_request (UIRequest* req)
{ {
@ -375,7 +355,7 @@ UI::do_request (UIRequest* req)
} else if (req->type == CallSlot) { } else if (req->type == CallSlot) {
req->slot (); req->the_slot ();
} else if (req->type == TouchDisplay) { } else if (req->type == TouchDisplay) {
@ -550,10 +530,9 @@ UI::handle_fatal (const char *message)
win.set_default_size (400, 100); win.set_default_size (400, 100);
string title; WindowTitle title(Glib::get_application_name());
title = name();
title += ": Fatal Error"; title += ": Fatal Error";
win.set_title (title); win.set_title (title.get_string());
win.set_position (WIN_POS_MOUSE); win.set_position (WIN_POS_MOUSE);
win.set_border_width (12); win.set_border_width (12);

View file

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

View file

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

View file

@ -22,6 +22,8 @@
#include <gtkmm/main.h> #include <gtkmm/main.h>
#include "pbd/stacktrace.h"
#include <gtkmm2ext/stateful_button.h> #include <gtkmm2ext/stateful_button.h>
using namespace Gtk; 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,7 +122,7 @@ void CoreMidi_MidiPort::read_proc (const MIDIPacketList *pktlist, void *refCon,
if (driver->firstrecv) { if (driver->firstrecv) {
driver->firstrecv = false; 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) { for (unsigned int i = 0; i < pktlist->numPackets; ++i) {
@ -130,11 +130,18 @@ void CoreMidi_MidiPort::read_proc (const MIDIPacketList *pktlist, void *refCon,
driver->bytes_read += packet->length; driver->bytes_read += packet->length;
if (driver->input_parser) { if (driver->input_parser) {
driver->input_parser->raw_preparse (*driver->input_parser, packet->data, packet->length); //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++) { for (int i = 0; i < packet->length; i++) {
driver->input_parser->scanner (packet->data[i]); driver->input_parser->scanner (packet->data[i]);
} }
driver->input_parser->raw_postparse (*driver->input_parser, packet->data, packet->length);
//driver->input_parser->raw_postparse (*driver->input_parser, packet->data, packet->length);
} }
packet = MIDIPacketNext(packet); packet = MIDIPacketNext(packet);

View file

@ -20,9 +20,11 @@
#include <fcntl.h> #include <fcntl.h>
#include <cerrno> #include <cerrno>
#include <cassert> #include <cassert>
#include <cstring>
#include <cstdlib> #include <cstdlib>
#include "pbd/error.h" #include "pbd/error.h"
#include "pbd/compose.h"
#include "midi++/types.h" #include "midi++/types.h"
#include "midi++/jack.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_input_port(NULL)
, _jack_output_port(NULL) , _jack_output_port(NULL)
, _last_read_index(0) , _last_read_index(0)
, non_process_thread_fifo (512) , output_fifo (512)
, input_fifo (1024)
{ {
int err = create_ports (node); if (!create_ports (node)) {
if (!err) {
_ok = true; _ok = true;
} }
} }
@ -82,18 +83,15 @@ JACK_MidiPort::cycle_start (nframes_t nframes)
const nframes_t event_count = jack_midi_get_event_count(jack_buffer); const nframes_t event_count = jack_midi_get_event_count(jack_buffer);
jack_midi_event_t ev; jack_midi_event_t ev;
nframes_t cycle_start_frame = jack_last_frame_time (_jack_client); timestamp_t cycle_start_frame = jack_last_frame_time (_jack_client);
for (nframes_t i=0; i < event_count; ++i) {
for (nframes_t i = 0; i < event_count; ++i) {
jack_midi_event_get (&ev, jack_buffer, i); jack_midi_event_get (&ev, jack_buffer, i);
input_fifo.write (cycle_start_frame + ev.time, (Evoral::EventType) 0, ev.size, ev.buffer);
}
if (input_parser) { if (event_count) {
for (size_t i = 0; i < ev.size; i++) { xthread.wakeup ();
input_parser->set_timestamp (cycle_start_frame + ev.time);
input_parser->scanner (ev.buffer[i]);
}
}
} }
} }
} }
@ -104,6 +102,8 @@ JACK_MidiPort::cycle_end ()
if (_jack_output_port != 0) { if (_jack_output_port != 0) {
flush (jack_port_get_buffer (_jack_output_port, _nframes_this_cycle)); flush (jack_port_get_buffer (_jack_output_port, _nframes_this_cycle));
} }
Port::cycle_end();
} }
int int
@ -113,10 +113,10 @@ JACK_MidiPort::write(byte * msg, size_t msglen, timestamp_t timestamp)
if (!is_process_thread()) { 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; 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) { if (vec.len[0] + vec.len[1] < 1) {
error << "no space in FIFO for non-process thread MIDI write" << endmsg; error << "no space in FIFO for non-process thread MIDI write" << endmsg;
@ -129,7 +129,7 @@ JACK_MidiPort::write(byte * msg, size_t msglen, timestamp_t timestamp)
vec.buf[1]->set (msg, msglen, timestamp); vec.buf[1]->set (msg, msglen, timestamp);
} }
non_process_thread_fifo.increment_write_idx (1); output_fifo.increment_write_idx (1);
ret = msglen; ret = msglen;
@ -164,11 +164,13 @@ JACK_MidiPort::write(byte * msg, size_t msglen, timestamp_t timestamp)
} }
if (ret > 0 && output_parser) { 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++) { for (int i = 0; i < ret; i++) {
output_parser->scanner (msg[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; return ret;
@ -180,7 +182,7 @@ JACK_MidiPort::flush (void* jack_port_buffer)
RingBuffer< Evoral::Event<double> >::rw_vector vec; RingBuffer< Evoral::Event<double> >::rw_vector vec;
size_t written; size_t written;
non_process_thread_fifo.get_read_vector (&vec); output_fifo.get_read_vector (&vec);
if (vec.len[0] + vec.len[1]) { if (vec.len[0] + vec.len[1]) {
// cerr << "Flush " << vec.len[0] + vec.len[1] << " events from non-process FIFO\n"; // 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) { if ((written = vec.len[0] + vec.len[1]) != 0) {
non_process_thread_fifo.increment_read_idx (written); output_fifo.increment_read_idx (written);
} }
} }
int 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"; timestamp_t time;
abort (); 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 int

View file

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

View file

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

View file

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

View file

@ -33,36 +33,24 @@
using namespace std; using namespace std;
using namespace PBD; 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::RequestType BaseUI::CallSlot = BaseUI::new_request_type();
BaseUI::BaseUI (string str, bool with_signal_pipe) BaseUI::BaseUI (const string& str)
: _name (str) : run_loop_thread (0)
, _name (str)
{ {
/* odd pseudo-singleton semantics */
base_ui_instance = this; base_ui_instance = this;
signal_pipe[0] = -1; request_channel.ios()->connect (sigc::mem_fun (*this, &BaseUI::request_handler));
signal_pipe[1] = -1;
if (with_signal_pipe) { /* derived class must set _ok */
if (setup_signal_pipe ()) {
throw failed_constructor ();
}
}
} }
BaseUI::~BaseUI() BaseUI::~BaseUI()
{ {
if (signal_pipe[0] >= 0) {
close (signal_pipe[0]);
}
if (signal_pipe[1] >= 0) {
close (signal_pipe[1]);
}
} }
BaseUI::RequestType BaseUI::RequestType
@ -78,32 +66,53 @@ BaseUI::new_request_type ()
return rt; return rt;
} }
int void
BaseUI::setup_signal_pipe () BaseUI::main_thread ()
{ {
/* setup the pipe that other threads send us notifications/requests thread_init ();
through. _main_loop->run ();
*/ }
if (pipe (signal_pipe)) { void
error << string_compose (_("%1-UI: cannot create error signal pipe (%2)"), _name, ::strerror (errno)) BaseUI::run ()
<< endmsg; {
/* to be called by UI's that need/want their own distinct, self-created event loop thread.
return -1; Derived classes should have set up a handler for IO on request_channel.ios()
} */
if (fcntl (signal_pipe[0], F_SETFL, O_NONBLOCK)) { _main_loop = MainLoop::create (MainContext::create());
error << string_compose (_("%1-UI: cannot set O_NONBLOCK on signal read pipe (%2)"), _name, ::strerror (errno)) request_channel.ios()->attach (_main_loop->get_context());
<< endmsg; run_loop_thread = Thread::create (mem_fun (*this, &BaseUI::main_thread), true);
return -1; }
}
void
if (fcntl (signal_pipe[1], F_SETFL, O_NONBLOCK)) { BaseUI::quit ()
error << string_compose (_("%1-UI: cannot set O_NONBLOCK on signal write pipe (%2)"), _name, ::strerror (errno)) {
<< endmsg; _main_loop->quit ();
return -1; run_loop_thread->join ();
} }
return 0; 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 <unistd.h>
#include "pbd/stacktrace.h"
#include "pbd/abstract_ui.h" #include "pbd/abstract_ui.h"
#include "pbd/pthread_utils.h" #include "pbd/pthread_utils.h"
#include "pbd/failed_constructor.h" #include "pbd/failed_constructor.h"
@ -9,26 +10,19 @@
using namespace std; using namespace std;
template <typename RequestObject> template <typename RequestObject>
AbstractUI<RequestObject>::AbstractUI (string name, bool with_signal_pipes) AbstractUI<RequestObject>::AbstractUI (const string& name)
: BaseUI (name, with_signal_pipes) : BaseUI (name)
{ {
if (pthread_key_create (&thread_request_buffer_key, 0)) { PBD::ThreadCreatedWithRequestSize.connect (mem_fun (*this, &AbstractUI<RequestObject>::register_thread));
cerr << _("cannot create thread request buffer key") << endl; }
throw failed_constructor();
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); RequestBuffer* b = new RequestBuffer (num_requests);
{ {
@ -36,54 +30,45 @@ AbstractUI<RequestObject>::register_thread_with_request_count (pthread_t thread_
request_buffers[thread_id] = b; request_buffers[thread_id] = b;
} }
pthread_setspecific (thread_request_buffer_key, b); per_thread_request_buffer.set (b);
} }
template <typename RequestObject> RequestObject* template <typename RequestObject> RequestObject*
AbstractUI<RequestObject>::get_request (RequestType rt) AbstractUI<RequestObject>::get_request (RequestType rt)
{ {
RequestBuffer* rbuf = static_cast<RequestBuffer*>(pthread_getspecific (thread_request_buffer_key)); RequestBuffer* rbuf = per_thread_request_buffer.get ();
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 ();
}
RequestBufferVector vec; RequestBufferVector vec;
vec.buf[0] = 0;
vec.buf[1] = 0; if (rbuf != 0) {
/* we have a per-thread FIFO, use it */
rbuf->get_write_vector (&vec); rbuf->get_write_vector (&vec);
if (vec.len[0] == 0) { 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;
return 0; return 0;
} else {
vec.buf[1]->type = rt;
return vec.buf[1];
} }
} else {
vec.buf[0]->type = rt; vec.buf[0]->type = rt;
return vec.buf[0]; return vec.buf[0];
} }
RequestObject* req = new RequestObject;
req->type = rt;
return req;
} }
template <typename RequestObject> void template <typename RequestObject> void
AbstractUI<RequestObject>::handle_ui_requests () AbstractUI<RequestObject>::handle_ui_requests ()
{ {
RequestBufferMapIterator i; RequestBufferMapIterator i;
RequestBufferVector vec;
/* per-thread buffers first */
request_buffer_map_lock.lock (); request_buffer_map_lock.lock ();
for (i = request_buffers.begin(); i != request_buffers.end(); ++i) { for (i = request_buffers.begin(); i != request_buffers.end(); ++i) {
RequestBufferVector vec;
while (true) { while (true) {
/* we must process requests 1 by 1 because /* we must process requests 1 by 1 because
@ -110,6 +95,22 @@ AbstractUI<RequestObject>::handle_ui_requests ()
} }
request_buffer_map_lock.unlock (); 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 template <typename RequestObject> void
@ -119,30 +120,40 @@ AbstractUI<RequestObject>::send_request (RequestObject *req)
return; /* XXX is this the right thing to do ? */ return; /* XXX is this the right thing to do ? */
} }
if (caller_is_ui_thread()) { if (caller_is_self ()) {
// cerr << "GUI thread sent request " << req << " type = " << req->type << endl;
do_request (req); do_request (req);
} else { } 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!
*/
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 ();
}
// cerr << "thread " << pthread_self() << " sent request " << req << " type = " << req->type << endl;
if (rbuf != 0) {
rbuf->increment_write_ptr (1); rbuf->increment_write_ptr (1);
} else {
if (signal_pipe[1] >= 0) { /* no per-thread buffer, so just use a list with a lock so that it remains
const char c = 0; single-reader/single-writer semantics
write (signal_pipe[1], &c, 1); */
Glib::Mutex::Lock lm (request_list_lock);
request_list.push_back (req);
} }
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 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 it under the terms of the GNU General Public License as published by
@ -34,38 +34,29 @@
class Touchable; class Touchable;
template <class RequestObject> template<typename RequestObject>
class AbstractUI : public BaseUI class AbstractUI : public BaseUI
{ {
public: public:
AbstractUI (std::string name, bool with_signal_pipe); AbstractUI (const std::string& name);
virtual ~AbstractUI() {} virtual ~AbstractUI() {}
virtual bool caller_is_ui_thread() = 0; void register_thread (std::string, pthread_t, std::string, uint32_t num_requests);
void call_slot (sigc::slot<void> el_slot);
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);
protected: protected:
typedef RingBufferNPT<RequestObject> RequestBuffer; typedef RingBufferNPT<RequestObject> RequestBuffer;
typedef typename RequestBuffer::rw_vector RequestBufferVector; typedef typename RequestBuffer::rw_vector RequestBufferVector;
typedef typename std::map<pthread_t,RequestBuffer*>::iterator RequestBufferMapIterator; typedef typename std::map<pthread_t,RequestBuffer*>::iterator RequestBufferMapIterator;
typedef std::map<pthread_t,RequestBuffer*> RequestBufferMap;
Glib::Mutex request_buffer_map_lock; Glib::Mutex request_buffer_map_lock;
typedef std::map<pthread_t,RequestBuffer*> RequestBufferMap;
RequestBufferMap request_buffers; 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); RequestObject* get_request (RequestType);
void handle_ui_requests (); void handle_ui_requests ();
void send_request (RequestObject *); void send_request (RequestObject *);

View file

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

View file

@ -20,38 +20,25 @@
#ifndef __pbd__crossthread_h__ #ifndef __pbd__crossthread_h__
#define __pbd__crossthread_h__ #define __pbd__crossthread_h__
#include "pbd/abstract_ui.h" #include <glibmm/main.h>
#include <sigc++/sigc++.h>
#include <pthread.h>
template<class RequestType> class CrossThreadChannel {
void public:
call_slot_from_thread_or_dispatch_it (pthread_t thread_id, AbstractUI<RequestType>& ui, sigc::slot<void> theSlot) CrossThreadChannel();
{ ~CrossThreadChannel();
/* 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.
*/
if (pthread_self() == thread_id) { void wakeup();
theSlot (); int selectable() const { return fds[0]; }
} else {
ui.call_slot (theSlot);
}
}
template<class RequestType> void drain ();
sigc::slot<void> static void drain (int fd);
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>), Glib::RefPtr<Glib::IOSource> ios();
thread_id, ui, theSlot); 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__ */ #endif /* __pbd__crossthread_h__ */

View file

@ -27,19 +27,18 @@
#include <sigc++/sigc++.h> #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_cancel_one (pthread_t thread);
void pthread_kill_all (int signum); void pthread_kill_all (int signum);
void pthread_cancel_all ();
void pthread_exit_pbd (void* status); void pthread_exit_pbd (void* status);
std::string pthread_name (); std::string pthread_name ();
namespace PBD { 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 void notify_gui_about_thread_exit (pthread_t);
extern sigc::signal<void,pthread_t> ThreadLeaving; extern sigc::signal<void,pthread_t> ThreadLeaving;
extern sigc::signal<void,pthread_t,std::string,uint32_t> ThreadCreatedWithRequestSize; extern sigc::signal<void,std::string,pthread_t,std::string,uint32_t> ThreadCreatedWithRequestSize;
} }
#endif /* __pbd_pthread_utils__ */ #endif /* __pbd_pthread_utils__ */

View file

@ -37,7 +37,7 @@ static pthread_mutex_t gui_notify_lock = PTHREAD_MUTEX_INITIALIZER;
namespace PBD { namespace PBD {
sigc::signal<void,pthread_t> ThreadLeaving; sigc::signal<void,pthread_t> ThreadLeaving;
sigc::signal<void,pthread_t,std::string,uint32_t> ThreadCreatedWithRequestSize; sigc::signal<void,std::string, pthread_t,std::string,uint32_t> ThreadCreatedWithRequestSize;
} }
using namespace PBD; using namespace PBD;
@ -52,10 +52,10 @@ static int thread_creator (pthread_t* thread_id, const pthread_attr_t* attr, voi
} }
void 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); pthread_mutex_lock (&gui_notify_lock);
ThreadCreatedWithRequestSize (thread, str, request_count); ThreadCreatedWithRequestSize (target_gui, thread, str, request_count);
pthread_mutex_unlock (&gui_notify_lock); pthread_mutex_unlock (&gui_notify_lock);
} }
@ -68,34 +68,26 @@ PBD::notify_gui_about_thread_exit (pthread_t thread)
} }
int 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)
{ {
pthread_attr_t default_attr;
int ret; int ret;
pthread_attr_t default_attr;
bool use_default_attr = (attr == NULL);
if (use_default_attr) {
// set default stack size to sensible default for memlocking // set default stack size to sensible default for memlocking
pthread_attr_init(&default_attr); pthread_attr_init(&default_attr);
pthread_attr_setstacksize(&default_attr, 500000); pthread_attr_setstacksize(&default_attr, 500000);
attr = &default_attr;
}
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; std::pair<string,pthread_t> newpair;
newpair.first = name; newpair.first = name;
newpair.second = *thread; newpair.second = *thread;
pthread_mutex_lock (&thread_map_lock); pthread_mutex_lock (&thread_map_lock);
all_threads.insert (newpair); all_threads.insert (newpair);
pthread_mutex_unlock (&thread_map_lock); pthread_mutex_unlock (&thread_map_lock);
} }
if (use_default_attr) {
pthread_attr_destroy(&default_attr); pthread_attr_destroy(&default_attr);
}
return ret; return ret;
} }
@ -131,19 +123,6 @@ pthread_kill_all (int signum)
pthread_mutex_unlock (&thread_map_lock); 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 void
pthread_cancel_one (pthread_t thread) pthread_cancel_one (pthread_t thread)
{ {

View file

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

View file

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

View file

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

View file

@ -1028,7 +1028,7 @@ TranzportControlProtocol::monitor_work ()
uint8_t offline = 0; 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_setcancelstate (PTHREAD_CANCEL_ENABLE, 0);
pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, 0); pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, 0);
next_track (); next_track ();

View file

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

View file

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

View file

@ -129,7 +129,7 @@ PowermateControlProtocol::set_active (bool inActivate)
return -1; 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; _active = true;
} else { } else {
return -1; return -1;

View file

@ -86,16 +86,16 @@ TranzportControlProtocol::set_active (bool yn)
return -1; 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; _active = true;
#if TRANZPORT_THREADS #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; _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; _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; _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; _active_process = true;
#endif #endif
} else { } else {