significant changes in code to handle import/embedding - much cleaner and less code, plus the import progress bar now works; unify response handling for Gtkmm2ext::Choice

git-svn-id: svn://localhost/trunk/ardour2@415 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
Paul Davis 2006-03-22 17:03:00 +00:00
parent 004a49b0c7
commit 3aa346b253
25 changed files with 647 additions and 647 deletions

View file

@ -93,6 +93,7 @@ default_keys.cc
editing.cc
editor.cc
editor_actions.cc
editor_audio_import.cc
editor_audiotrack.cc
editor_canvas.cc
editor_canvas_events.cc

View file

@ -13,6 +13,12 @@
<separator/>
<menuitem action='AddTrackBus'/>
<separator/>
<menu action='addExistingAudioFiles'>
<menuitem action='addExternalAudioAsRegion'/>
<menuitem action='addExternalAudioAsTrack'/>
<menuitem action='addExternalAudioToTrack'/>
</menu>
<separator/>
<menu name='Export' action='Export'>
<menuitem action='ExportSession'/>
<menuitem action='ExportSelection'/>
@ -308,6 +314,6 @@
<menuitem action='SortBySourceFilesystem'/>
</menu>
<separator/>
<menuitem action='addExternalRegion'/>
<menuitem action='addExternalAudioToRegionList'/>
</popup>
</ui>

View file

@ -212,8 +212,6 @@ CrossfadeEditor::CrossfadeEditor (Session& s, Crossfade& xf, double my, double m
for (list<Preset*>::iterator i = fade_out_presets->begin(); i != fade_out_presets->end(); ++i) {
cerr << "looking for xpm " << (*i)->xpm << endl;
pxmap = manage (new Image (get_xpm((*i)->xpm)));
pbutton = manage (new Button);
pbutton->add (*pxmap);

View file

@ -13,6 +13,7 @@
#define MOUSEMODE(a) /*empty*/
#define ZOOMFOCUS(a) /*empty*/
#define DISPLAYCONTROL(a) /*empty*/
#define IMPORTMODE(a) /*empty*/
namespace Editing {
@ -124,6 +125,24 @@ DisplayControl str2displaycontrol (const std::string &);
#undef DISPLAYCONTROL
#define DISPLAYCONTROL(a) /*empty*/
// IMPORTMODE
#undef IMPORTMODE
#define IMPORTMODE(a) a,
enum ImportMode {
#include "editing_syms.h"
};
#undef IMPORTMODE
#define IMPORTMODE(s) #s,
static const char *importmodestrs[] = {
#include "editing_syms.h"
};
inline const char* enum2str(ImportMode m) {return importmodestrs[m];}
ImportMode str2importmode (const std::string &);
#undef IMPORTMODE
#define IMPORTMODE(a) /*empty*/
/////////////////////
// These don't need their state saved. yet...
enum CutCopyOp {

View file

@ -53,3 +53,7 @@ DISPLAYCONTROL(FollowPlayhead)
DISPLAYCONTROL(ShowMeasures)
DISPLAYCONTROL(ShowWaveforms)
DISPLAYCONTROL(ShowWaveformsRecording)
IMPORTMODE(ImportAsRegion)
IMPORTMODE(ImportAsTrack)
IMPORTMODE(ImportToTrack)

View file

@ -1982,14 +1982,9 @@ Editor::add_dstream_context_items (Menu_Helpers::MenuList& edit_items)
/* Adding new material */
Menu *import_menu = manage (new Menu());
MenuList& import_items = import_menu->items();
import_menu->set_name ("ArdourContextMenu");
import_items.push_back (MenuElem (_("Insert Region"), bind (mem_fun(*this, &Editor::insert_region_list_selection), 1.0f)));
import_items.push_back (MenuElem (_("Insert external sndfile"), bind (mem_fun(*this, &Editor::insert_sndfile), false)));
edit_items.push_back (MenuElem (_("Import"), *import_menu));
edit_items.push_back (SeparatorElem());
edit_items.push_back (MenuElem (_("Insert Selected Region"), bind (mem_fun(*this, &Editor::insert_region_list_selection), 1.0f)));
edit_items.push_back (MenuElem (_("Insert Existing Audio"), bind (mem_fun(*this, &Editor::add_external_audio_action), ImportToTrack)));
/* Nudge track */
@ -2905,7 +2900,7 @@ Editor::stop_canvas_autoscroll ()
}
int
Editor::convert_drop_to_paths (vector<string>& paths,
Editor::convert_drop_to_paths (vector<ustring>& paths,
const RefPtr<Gdk::DragContext>& context,
gint x,
gint y,
@ -3445,21 +3440,14 @@ Editor::edit_menu_map_handler (GdkEventAny* ev)
import_menu->set_name ("ArdourContextMenu");
MenuList& import_items = import_menu->items();
import_items.push_back (MenuElem (_("... as new track"), bind (mem_fun(*this, &Editor::import_audio), true)));
import_items.push_back (MenuElem (_("... as new region"), bind (mem_fun(*this, &Editor::import_audio), false)));
Menu* embed_menu = manage (new Menu());
embed_menu->set_name ("ArdourContextMenu");
MenuList& embed_items = embed_menu->items();
embed_items.push_back (MenuElem (_("... as new track"), bind (mem_fun(*this, &Editor::insert_sndfile), true)));
embed_items.push_back (MenuElem (_("... as new region"), mem_fun(*this, &Editor::embed_audio)));
import_items.push_back (MenuElem (_("... as new track"), bind (mem_fun(*this, &Editor::add_external_audio_action), ImportAsTrack)));
import_items.push_back (MenuElem (_("... as new region"), bind (mem_fun(*this, &Editor::add_external_audio_action), ImportAsRegion)));
edit_items.push_back (MenuElem (_("Import audio (copy)"), *import_menu));
edit_items.push_back (MenuElem (_("Embed audio (link)"), *embed_menu));
edit_items.push_back (SeparatorElem());
edit_items.push_back (MenuElem (_("Remove last capture"), mem_fun(*this, &Editor::remove_last_capture)));
if (!session->have_captured()) {
edit_items.back().set_sensitive (false);
}

View file

@ -967,18 +967,16 @@ class Editor : public PublicEditor
void insert_region_list_drag (ARDOUR::AudioRegion&, int x, int y);
void insert_region_list_selection (float times);
void insert_sndfile (bool as_tracks);
void embed_audio (); // inserts into region list
int reject_because_rate_differs (const string & path, ARDOUR::SoundFileInfo& finfo, const string & action, bool multiple_pending);
void add_external_audio_action (Editing::ImportMode);
void do_embed_sndfiles (vector<string> paths, bool split);
void embed_sndfile (string path, bool split, bool multiple_files, bool& check_sr);
void do_insert_sndfile (vector<string> path, bool multi, jack_nframes_t frame);
void insert_paths_as_new_tracks (std::vector<std::string> paths, bool multi); // inserts files as new tracks
void insert_sndfile_into (const string & path, bool multi, AudioTimeAxisView* tv, jack_nframes_t& frame, bool prompt=true);
static void* _insert_sndfile_thread (void*);
void* insert_sndfile_thread (void*);
void bring_in_external_audio (Editing::ImportMode mode, ARDOUR::AudioTrack*, jack_nframes_t& pos, bool prompt);
void do_import (vector<Glib::ustring> paths, bool split, Editing::ImportMode mode, ARDOUR::AudioTrack*, jack_nframes_t&, bool);
void do_embed (vector<Glib::ustring> paths, bool split, Editing::ImportMode mode, ARDOUR::AudioTrack*, jack_nframes_t&, bool);
void import_sndfile (Glib::ustring path, Editing::ImportMode mode, ARDOUR::AudioTrack* track, jack_nframes_t& pos);
void embed_sndfile (Glib::ustring path, bool split, bool multiple_files, bool& check_sample_rate, Editing::ImportMode mode,
ARDOUR::AudioTrack* track, jack_nframes_t& pos, bool prompt);
int finish_bringing_in_audio (ARDOUR::AudioRegion& region, uint32_t, uint32_t, ARDOUR::AudioTrack* track, jack_nframes_t& pos, Editing::ImportMode mode);
int reject_because_rate_differs (Glib::ustring path, ARDOUR::SoundFileInfo& finfo, const string & action, bool multiple_pending);
/* generic interthread progress window */
@ -1003,8 +1001,6 @@ class Editor : public PublicEditor
gint import_progress_timeout (void *);
static void *_import_thread (void *);
void* import_thread ();
void catch_new_audio_region (ARDOUR::AudioRegion*);
ARDOUR::AudioRegion* last_audio_region;
/* to support this ... */
@ -1586,7 +1582,7 @@ class Editor : public PublicEditor
/* Drag-n-Drop */
int convert_drop_to_paths (std::vector<std::string>& paths,
int convert_drop_to_paths (std::vector<Glib::ustring>& paths,
const Glib::RefPtr<Gdk::DragContext>& context,
gint x,
gint y,

View file

@ -37,6 +37,7 @@ Editor::register_actions ()
ActionManager::register_action (editor_actions, X_("Monitoring"), _("Monitoring"));
ActionManager::register_action (editor_actions, X_("Autoconnect"), _("Autoconnect"));
ActionManager::register_action (editor_actions, X_("Layering"), _("Layering"));
ActionManager::register_action (editor_actions, X_("addExistingAudioFiles"), _("Add Existing Audio"));
/* add named actions for the editor */
@ -340,7 +341,16 @@ Editor::register_actions ()
ActionManager::register_radio_action (rl_actions, sort_type_group, X_("SortBySourceFilesystem"), _("By Source Filesystem"),
bind (mem_fun(*this, &Editor::reset_region_list_sort_type), BySourceFileFS));
act = ActionManager::register_action (rl_actions, X_("addExternalAudio"), _("Embed audio (link)"), mem_fun(*this, &Editor::embed_audio));
/* the next two are duplicate items with different names for use in two different contexts */
act = ActionManager::register_action (editor_actions, X_("addExternalAudioToRegionList"), _("Add External Audio"), bind (mem_fun(*this, &Editor::add_external_audio_action), ImportAsRegion));
ActionManager::session_sensitive_actions.push_back (act);
act = ActionManager::register_action (editor_actions, X_("addExternalAudioAsRegion"), _("as Region(s)"), bind (mem_fun(*this, &Editor::add_external_audio_action), ImportAsRegion));
ActionManager::session_sensitive_actions.push_back (act);
act = ActionManager::register_action (editor_actions, X_("addExternalAudioAsTrack"), _("as Tracks"), bind (mem_fun(*this, &Editor::add_external_audio_action), ImportAsTrack));
ActionManager::session_sensitive_actions.push_back (act);
act = ActionManager::register_action (editor_actions, X_("addExternalAudioToTrack"), _("to Tracks"), bind (mem_fun(*this, &Editor::add_external_audio_action), ImportToTrack));
ActionManager::session_sensitive_actions.push_back (act);
ActionManager::register_toggle_action (editor_actions, X_("ToggleWaveformVisibility"), _("Show Waveforms"), mem_fun (*this, &Editor::toggle_waveform_visibility));

View file

@ -0,0 +1,395 @@
/*
Copyright (C) 2000-2006 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.
$Id$
*/
#include <pbd/pthread_utils.h>
#include <pbd/basename.h>
#include <gtkmm2ext/choice.h>
#include <ardour/session.h>
#include <ardour/audioplaylist.h>
#include <ardour/audioregion.h>
#include <ardour/diskstream.h>
#include <ardour/filesource.h>
#include <ardour/externalsource.h>
#include <ardour/utils.h>
#include <ardour/audio_track.h>
#include <ardour/audioplaylist.h>
#include "ardour_ui.h"
#include "editor.h"
#include "sfdb_ui.h"
#include "editing.h"
#include "audio_time_axis.h"
#include "i18n.h"
using namespace std;
using namespace ARDOUR;
using namespace sigc;
using namespace Gtk;
using namespace Editing;
/* Functions supporting the incorporation of external (non-captured) audio material into ardour */
void
Editor::add_external_audio_action (ImportMode mode)
{
jack_nframes_t& pos = edit_cursor->current_frame;
AudioTrack* track = 0;
if (!selection->tracks.empty()) {
AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(selection->tracks.front());
if (atv) {
track = atv->audio_track();
}
}
bring_in_external_audio (mode, track, pos, false);
}
void
Editor::bring_in_external_audio (ImportMode mode, AudioTrack* track, jack_nframes_t& pos, bool prompt)
{
if (session == 0) {
MessageDialog msg (0, _("You can't import or embed an audiofile until you have a session loaded."));
msg.run ();
return;
}
SoundFileOmega sfdb (_("Add existing audio to session"));
sfdb.set_mode (mode);
switch (sfdb.run()) {
case SoundFileOmega::ResponseImport:
do_import (sfdb.get_paths(), sfdb.get_split(), mode, track, pos, prompt);
break;
case SoundFileOmega::ResponseEmbed:
do_embed (sfdb.get_paths(), sfdb.get_split(), mode, track, pos, prompt);
break;
default:
break;
}
}
void
Editor::do_import (vector<Glib::ustring> paths, bool split, ImportMode mode, AudioTrack* track, jack_nframes_t& pos, bool prompt)
{
/* SFDB sets "multichan" to true to indicate "split channels"
so reverse the setting to match the way libardour
interprets it.
*/
import_status.multichan = !split;
if (interthread_progress_window == 0) {
build_interthread_progress_window ();
}
/* for each path that was selected, import it and then potentially create a new track
containing the new region as the sole contents.
*/
for (vector<Glib::ustring>::iterator i = paths.begin(); i != paths.end(); ++i ) {
import_sndfile (*i, mode, track, pos);
}
interthread_progress_window->hide_all ();
}
void
Editor::do_embed (vector<Glib::ustring> paths, bool split, ImportMode mode, AudioTrack* track, jack_nframes_t& pos, bool prompt)
{
bool multiple_files = paths.size() > 1;
bool check_sample_rate = true;
for (vector<Glib::ustring>::iterator i = paths.begin(); i != paths.end(); ++i) {
embed_sndfile (*i, split, multiple_files, check_sample_rate, mode, track, pos, prompt);
}
session->save_state ("");
}
void
Editor::import_sndfile (Glib::ustring path, ImportMode mode, AudioTrack* track, jack_nframes_t& pos)
{
interthread_progress_window->set_title (string_compose (_("ardour: importing %1"), path));
interthread_progress_window->set_position (Gtk::WIN_POS_MOUSE);
interthread_progress_window->show_all ();
interthread_progress_bar.set_fraction (0.0f);
interthread_cancel_label.set_text (_("Cancel Import"));
current_interthread_info = &import_status;
import_status.pathname = path;
import_status.done = false;
import_status.cancel = false;
import_status.freeze = false;
import_status.done = 0.0;
interthread_progress_connection = Glib::signal_timeout().connect
(bind (mem_fun(*this, &Editor::import_progress_timeout), (gpointer) 0), 100);
track_canvas.get_window()->set_cursor (Gdk::Cursor (Gdk::WATCH));
ARDOUR_UI::instance()->flush_pending ();
/* start import thread for this path. this will ultimately call Session::import_audiofile()
and if successful will add the file as a region to the session region list.
*/
pthread_create_and_store ("import", &import_status.thread, 0, _import_thread, this);
pthread_detach (import_status.thread);
while (!(import_status.done || import_status.cancel)) {
gtk_main_iteration ();
}
import_status.done = true;
interthread_progress_connection.disconnect ();
/* import thread finished - see if we should build a new track */
if (!import_status.new_regions.empty()) {
AudioRegion& region (*import_status.new_regions.front());
finish_bringing_in_audio (region, region.n_channels(), region.n_channels(), track, pos, mode);
}
track_canvas.get_window()->set_cursor (*current_canvas_cursor);
}
void
Editor::embed_sndfile (Glib::ustring path, bool split, bool multiple_files, bool& check_sample_rate, ImportMode mode,
AudioTrack* track, jack_nframes_t& pos, bool prompt)
{
ExternalSource *source = 0; /* keep g++ quiet */
AudioRegion::SourceList sources;
string idspec;
string linked_path;
SoundFileInfo finfo;
string region_name;
track_canvas.get_window()->set_cursor (Gdk::Cursor (Gdk::WATCH));
ARDOUR_UI::instance()->flush_pending ();
/* lets see if we can link it into the session */
linked_path = session->sound_dir();
linked_path += PBD::basename (path);
if (link (path.c_str(), linked_path.c_str()) == 0) {
/* there are many reasons why link(2) might have failed.
but if it succeeds, we now have a link in the
session sound dir that will protect against
unlinking of the original path. nice.
*/
path = linked_path;
}
/* note that we temporarily truncated _id at the colon */
string error_msg;
if (!ExternalSource::get_soundfile_info (path, finfo, error_msg)) {
error << string_compose(_("Editor: cannot open file \"%1\", (%2)"), selection, error_msg ) << endmsg;
return;
}
if (check_sample_rate) {
switch (reject_because_rate_differs (path, finfo, "Embed", multiple_files)) {
case 0:
break;
case 1:
return;
case -1:
check_sample_rate = false;
break;
case -2:
default:
return;
}
}
track_canvas.get_window()->set_cursor (Gdk::Cursor (Gdk::WATCH));
ARDOUR_UI::instance()->flush_pending ();
/* make the proper number of channels in the region */
for (int n = 0; n < finfo.channels; ++n)
{
idspec = path;
idspec += string_compose(":%1", n);
try {
source = ExternalSource::create (idspec.c_str());
sources.push_back(source);
}
catch (failed_constructor& err) {
error << string_compose(_("could not open %1"), path) << endmsg;
goto out;
}
ARDOUR_UI::instance()->flush_pending ();
}
if (sources.empty()) {
goto out;
}
region_name = PBD::basename_nosuffix (path);
region_name += "-0";
AudioRegion* region = new AudioRegion (sources, 0, sources[0]->length(), region_name, 0,
Region::Flag (Region::DefaultFlags|Region::WholeFile|Region::External));
uint32_t input_chan = finfo.channels;
uint32_t output_chan;
if (session->get_output_auto_connect() & Session::AutoConnectMaster) {
output_chan = (session->master_out() ? session->master_out()->n_inputs() : input_chan);
} else {
output_chan = input_chan;
}
finish_bringing_in_audio (*region, input_chan, output_chan, track, pos, mode);
out:
track_canvas.get_window()->set_cursor (*current_canvas_cursor);
}
int
Editor::finish_bringing_in_audio (AudioRegion& region, uint32_t in_chans, uint32_t out_chans, AudioTrack* track, jack_nframes_t& pos, ImportMode mode)
{
switch (mode) {
case ImportAsRegion:
/* relax, its been done */
break;
case ImportToTrack:
if (track) {
Playlist* playlist = track->disk_stream().playlist();
AudioRegion* copy = new AudioRegion (region);
begin_reversible_command (_("insert sndfile"));
session->add_undo (playlist->get_memento());
playlist->add_region (*copy, pos);
session->add_redo_no_execute (playlist->get_memento());
commit_reversible_command ();
pos += region.length();
}
break;
case ImportAsTrack:
AudioTrack* at = session->new_audio_track (in_chans, out_chans);
AudioRegion* copy = new AudioRegion (region);
at->disk_stream().playlist()->add_region (*copy, pos);
break;
}
return 0;
}
int
Editor::reject_because_rate_differs (Glib::ustring path, SoundFileInfo& finfo, const string & action, bool multiple_pending)
{
if (!session) {
return 1;
}
if (finfo.samplerate != (int) session->frame_rate()) {
vector<string> choices;
choices.push_back (string_compose (_("%1 it anyway"), action));
if (multiple_pending) {
/* XXX assumptions about sentence structure
here for translators. Sorry.
*/
choices.push_back (string_compose (_("Don't %1 it"), action));
choices.push_back (string_compose (_("%1 all without questions"), action));
choices.push_back (_("Cancel entire import"));
} else {
choices.push_back (_("Cancel"));
}
Gtkmm2ext::Choice rate_choice (
string_compose (_("%1\nThis audiofile's sample rate doesn't match the session sample rate!"), path),
choices, false);
switch (rate_choice.run()) {
case 0: /* do it anyway */
return 0;
case 1: /* don't import this one */
return 1;
case 2: /* do the rest without asking */
return -1;
case 3: /* stop a multi-file import */
default:
return -2;
}
}
return 0;
}
void *
Editor::_import_thread (void *arg)
{
PBD::ThreadCreated (pthread_self(), X_("Import"));
Editor *ed = (Editor *) arg;
return ed->import_thread ();
}
void *
Editor::import_thread ()
{
session->import_audiofile (import_status);
pthread_exit_pbd (0);
/*NOTREACHED*/
return 0;
}
gint
Editor::import_progress_timeout (void *arg)
{
interthread_progress_label.set_text (import_status.doing_what);
if (import_status.freeze) {
interthread_cancel_button.set_sensitive(false);
} else {
interthread_cancel_button.set_sensitive(true);
}
if (import_status.doing_what == "building peak files") {
interthread_progress_bar.pulse ();
return FALSE;
} else {
interthread_progress_bar.set_fraction (import_status.progress);
}
return !(import_status.done || import_status.cancel);
}

View file

@ -455,7 +455,7 @@ Editor::drop_paths (const RefPtr<Gdk::DragContext>& context,
TimeAxisView* tvp;
AudioTimeAxisView* tv;
double cy;
vector<string> paths;
vector<ustring> paths;
string spath;
GdkEvent ev;
jack_nframes_t frame;
@ -484,24 +484,20 @@ Editor::drop_paths (const RefPtr<Gdk::DragContext>& context,
if ((tvp = trackview_by_y_position (cy)) == 0) {
/* drop onto canvas background: create a new track */
insert_paths_as_new_tracks (paths, false);
/* drop onto canvas background: create new tracks */
jack_nframes_t pos = 0;
do_embed (paths, false, ImportAsTrack, 0, pos, false);
} else if ((tv = dynamic_cast<AudioTimeAxisView*>(tvp)) != 0) {
/* check that its an audio track, not a bus */
if (tv->get_diskstream()) {
for (vector<string>::iterator p = paths.begin(); p != paths.end(); ++p) {
insert_sndfile_into (*p, true, tv, frame);
do_embed (paths, false, ImportToTrack, tv->audio_track(), frame, true);
}
}
}
out:
context->drag_finish (true, false, time);
}

View file

@ -333,6 +333,7 @@ Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemTyp
break;
case StreamItem:
set_selected_track_from_click (Keyboard::selection_type (event->button.state), true, true);
break;
case AutomationTrackItem:

View file

@ -199,11 +199,7 @@ Do you really want to destroy %1 ?"),
Gtkmm2ext::Choice prompter (prompt, choices);
if (prompter.run () != RESPONSE_ACCEPT) {
return;
}
if (prompter.get_choice() != 0) {
if (prompter.run() != 0) { /* first choice */
return;
}
@ -1972,483 +1968,6 @@ Editor::interthread_cancel_clicked ()
}
}
void *
Editor::_import_thread (void *arg)
{
PBD::ThreadCreated (pthread_self(), X_("Import"));
Editor *ed = (Editor *) arg;
return ed->import_thread ();
}
void *
Editor::import_thread ()
{
session->import_audiofile (import_status);
return 0;
}
gint
Editor::import_progress_timeout (void *arg)
{
interthread_progress_label.set_text (import_status.doing_what);
if (import_status.freeze) {
interthread_cancel_button.set_sensitive(false);
} else {
interthread_cancel_button.set_sensitive(true);
}
if (import_status.doing_what == "building peak files") {
interthread_progress_bar.pulse ();
return FALSE;
} else {
interthread_progress_bar.set_fraction (import_status.progress/100);
}
return !(import_status.done || import_status.cancel);
}
void
Editor::import_audio (bool as_tracks)
{
if (session == 0) {
warning << _("You can't import an audiofile until you have a session loaded.") << endmsg;
return;
}
string str;
if (as_tracks) {
str =_("Import selected as tracks");
} else {
str = _("Import selected to region list");
}
SoundFileOmega sfdb (str);
sfdb.Imported.connect (bind (mem_fun (*this, &Editor::do_import), as_tracks));
sfdb.run();
}
void
Editor::catch_new_audio_region (AudioRegion* ar)
{
last_audio_region = ar;
}
void
Editor::do_import (vector<string> paths, bool split, bool as_tracks)
{
sigc::connection c;
/* SFDB sets "multichan" to true to indicate "split channels"
so reverse the setting to match the way libardour
interprets it.
*/
import_status.multichan = !split;
if (interthread_progress_window == 0) {
build_interthread_progress_window ();
}
interthread_progress_window->set_title (_("ardour: audio import in progress"));
interthread_progress_window->set_position (Gtk::WIN_POS_MOUSE);
interthread_progress_window->show_all ();
interthread_progress_bar.set_fraction (0.0f);
interthread_cancel_label.set_text (_("Cancel Import"));
current_interthread_info = &import_status;
c = session->AudioRegionAdded.connect (mem_fun(*this, &Editor::catch_new_audio_region));
for (vector<string>::iterator i = paths.begin(); i != paths.end(); ++i ) {
interthread_progress_window->set_title (string_compose (_("ardour: importing %1"), (*i)));
import_status.pathname = (*i);
import_status.done = false;
import_status.cancel = false;
import_status.freeze = false;
import_status.done = 0.0;
interthread_progress_connection =
Glib::signal_timeout().connect (bind (mem_fun(*this, &Editor::import_progress_timeout), (gpointer) 0), 100);
last_audio_region = 0;
pthread_create_and_store ("import", &import_status.thread, 0, _import_thread, this);
pthread_detach (import_status.thread);
while (!(import_status.done || import_status.cancel)) {
gtk_main_iteration ();
}
import_status.done = true;
interthread_progress_connection.disconnect ();
if (as_tracks && last_audio_region != 0) {
uint32_t channels = last_audio_region->n_channels();
AudioTrack* at = session->new_audio_track (channels, channels);
AudioRegion* copy = new AudioRegion (*last_audio_region);
at->disk_stream().playlist()->add_region (*copy, 0);
}
}
c.disconnect ();
interthread_progress_window->hide_all ();
}
int
Editor::reject_because_rate_differs (const string & path, SoundFileInfo& finfo, const string & action, bool multiple_pending)
{
if (!session) {
return 1;
}
if (finfo.samplerate != (int) session->frame_rate()) {
vector<string> choices;
choices.push_back (string_compose (_("%1 it anyway"), action));
if (multiple_pending) {
/* XXX assumptions about sentence structure
here for translators. Sorry.
*/
choices.push_back (string_compose (_("Don't %1 it"), action));
choices.push_back (string_compose (_("%1 all without questions"), action));
choices.push_back (_("Cancel entire import"));
} else {
choices.push_back (_("Cancel"));
}
Gtkmm2ext::Choice rate_choice (
string_compose (_("%1\nThis audiofile's sample rate doesn't match the session sample rate!"), path),
choices);
int response = rate_choice.run();
switch (response) {
case RESPONSE_ACCEPT:
break;
default:
/* stop all that might come after this */
return -2;
break;
}
switch (rate_choice.get_choice()) {
case 0: /* do it anyway */
return 0;
case 1: /* don't import this one */
return 1;
case 2: /* do the rest without asking */
return -1;
case 3: /* stop a multi-file import */
default:
return -2;
}
}
return 0;
}
void
Editor::embed_audio ()
{
if (session == 0) {
warning << _("You can't embed an audiofile until you have a session loaded.") << endmsg;
return;
}
SoundFileOmega sfdb (_("Add to External Region list"));
sfdb.Embedded.connect (mem_fun (*this, &Editor::do_embed_sndfiles));
sfdb.run ();
}
void
Editor::do_embed_sndfiles (vector<string> paths, bool split)
{
bool multiple_files = paths.size() > 1;
bool check_sample_rate = true;
for (vector<string>::iterator i = paths.begin(); i != paths.end(); ++i) {
embed_sndfile (*i, split, multiple_files, check_sample_rate);
}
session->save_state ("");
}
void
Editor::embed_sndfile (string path, bool split, bool multiple_files, bool& check_sample_rate)
{
ExternalSource *source = 0; /* keep g++ quiet */
AudioRegion::SourceList sources;
string idspec;
string linked_path;
SoundFileInfo finfo;
/* lets see if we can link it into the session */
linked_path = session->sound_dir();
linked_path += PBD::basename (path);
if (link (path.c_str(), linked_path.c_str()) == 0) {
/* there are many reasons why link(2) might have failed.
but if it succeeds, we now have a link in the
session sound dir that will protect against
unlinking of the original path. nice.
*/
path = linked_path;
}
/* note that we temporarily truncated _id at the colon */
string error_msg;
if (!ExternalSource::get_soundfile_info (path, finfo, error_msg)) {
error << string_compose(_("Editor: cannot open file \"%1\", (%2)"), selection, error_msg ) << endmsg;
return;
}
if (check_sample_rate) {
switch (reject_because_rate_differs (path, finfo, "Embed", multiple_files)) {
case 0:
break;
case 1:
return;
case -1:
check_sample_rate = false;
break;
case -2:
default:
return;
}
}
track_canvas.get_window()->set_cursor (Gdk::Cursor (Gdk::WATCH));
ARDOUR_UI::instance()->flush_pending ();
/* make the proper number of channels in the region */
for (int n=0; n < finfo.channels; ++n)
{
idspec = path;
idspec += string_compose(":%1", n);
try {
source = ExternalSource::create (idspec.c_str());
sources.push_back(source);
}
catch (failed_constructor& err) {
error << string_compose(_("could not open %1"), path) << endmsg;
goto out;
}
ARDOUR_UI::instance()->flush_pending ();
}
if (sources.size() > 0) {
string region_name = PBD::basename_nosuffix (path);
region_name += "-0";
/* The created region isn't dropped. It emits a signal
that is picked up by the session.
*/
new AudioRegion (sources, 0, sources[0]->length(), region_name, 0,
Region::Flag (Region::DefaultFlags|Region::WholeFile|Region::External));
/* make sure we can see it in the list */
/* its the second node, always */
// GTK2FIX ?? is it still always the 2nd node
TreeModel::Path path ("2");
region_list_display.expand_row (path, true);
ARDOUR_UI::instance()->flush_pending ();
}
out:
track_canvas.get_window()->set_cursor (*current_canvas_cursor);
}
void
Editor::insert_sndfile (bool as_tracks)
{
// SoundFileSelector& sfdb (ARDOUR_UI::instance()->get_sfdb_window());
sigc::connection c;
string str;
if (as_tracks) {
// c = sfdb.Action.connect (mem_fun(*this, &Editor::insert_paths_as_new_tracks));
str = _("Insert selected as new tracks");
} else {
jack_nframes_t pos;
if (clicked_audio_trackview == 0) {
return;
}
if (ensure_cursor (&pos)) {
return;
}
// c = sfdb.Action.connect (bind (mem_fun(*this, &Editor::do_insert_sndfile), pos));
str = _("Insert selected");
}
// sfdb.run (str, false);
// c.disconnect ();
}
void
Editor::insert_paths_as_new_tracks (vector<string> paths, bool split)
{
SoundFileInfo finfo;
bool multiple_files;
bool check_sample_rate = true;
string error_msg;
multiple_files = paths.size() > 1;
for (vector<string>::iterator p = paths.begin(); p != paths.end(); ++p) {
if (!ExternalSource::get_soundfile_info((*p), finfo, error_msg)) {
error << string_compose(_("Editor: cannot open file \"%1\" (%2)"), (*p), error_msg) << endmsg;
continue;
}
/* add a new track */
if (check_sample_rate) {
switch (reject_because_rate_differs (*p, finfo, "Insert", multiple_files)) {
case 0:
break;
case 1:
continue;
case -1:
check_sample_rate = false;
break;
case -2:
return;
}
}
uint32_t input_chan = finfo.channels;
uint32_t output_chan;
if (session->get_output_auto_connect() & Session::AutoConnectMaster) {
output_chan = (session->master_out() ? session->master_out()->n_inputs() : input_chan);
} else {
output_chan = input_chan;
}
(void) session->new_audio_track (input_chan, output_chan);
/* get the last (most recently added) track view */
AudioTimeAxisView* tv;
if ((tv = dynamic_cast<AudioTimeAxisView*>(track_views.back())) == 0) {
fatal << _("programming error: ")
<< X_("last trackview after new_audio_track is not an audio track!")
<< endmsg;
/*NOTREACHED*/
}
jack_nframes_t pos = 0;
insert_sndfile_into (*p, true, tv, pos, false);
}
}
void
Editor::do_insert_sndfile (vector<string> paths, bool split, jack_nframes_t pos)
{
for (vector<string>::iterator x = paths.begin(); x != paths.end(); ++x) {
insert_sndfile_into (*x, !split, clicked_audio_trackview, pos);
}
}
void
Editor::insert_sndfile_into (const string & path, bool multi, AudioTimeAxisView* tv, jack_nframes_t& pos, bool prompt)
{
ExternalSource *source = 0; /* keep g++ quiet */
AudioRegion::SourceList sources;
string idspec;
SoundFileInfo finfo;
string error_msg;
/* note that we temporarily truncated _id at the colon */
if (!ExternalSource::get_soundfile_info (path, finfo, error_msg)) {
error << string_compose(_("Editor: cannot open file \"%1\" (%2)"), path, error_msg) << endmsg;
return;
}
if (prompt && (reject_because_rate_differs (path, finfo, "Insert", false) != 0)) {
return;
}
track_canvas.get_window()->set_cursor (Gdk::Cursor (Gdk::WATCH));
ARDOUR_UI::instance()->flush_pending ();
/* make the proper number of channels in the region */
for (int n=0; n < finfo.channels; ++n)
{
idspec = path;
idspec += string_compose(":%1", n);
try {
source = ExternalSource::create (idspec.c_str());
sources.push_back(source);
}
catch (failed_constructor& err) {
error << string_compose(_("could not open %1"), path) << endmsg;
goto out;
}
ARDOUR_UI::instance()->flush_pending ();
}
if (sources.size() > 0) {
string region_name = region_name_from_path (PBD::basename (path));
AudioRegion *region = new AudioRegion (sources, 0, sources[0]->length(), region_name,
0, /* irrelevant these days */
Region::Flag (Region::DefaultFlags|Region::WholeFile|Region::External));
begin_reversible_command (_("insert sndfile"));
session->add_undo (tv->playlist()->get_memento());
tv->playlist()->add_region (*region, pos);
session->add_redo_no_execute (tv->playlist()->get_memento());
commit_reversible_command ();
pos += sources[0]->length();
ARDOUR_UI::instance()->flush_pending ();
}
out:
track_canvas.get_window()->set_cursor (*current_canvas_cursor);
return;
}
void
Editor::region_from_selection ()
{
@ -3552,11 +3071,9 @@ Editor::remove_last_capture ()
Gtkmm2ext::Choice prompter (prompt, choices);
if (prompter.run () == RESPONSE_ACCEPT) {
if (prompter.get_choice() == 0) {
if (prompter.run () == 0) {
session->remove_last_capture ();
}
}
} else {
session->remove_last_capture();

View file

@ -574,10 +574,11 @@ Editor::region_list_display_drag_data_received (const RefPtr<Gdk::DragContext>&
const SelectionData& data,
guint info, guint time)
{
vector<string> paths;
vector<ustring> paths;
if (convert_drop_to_paths (paths, context, x, y, data, info, time) == 0) {
do_embed_sndfiles (paths, false);
jack_nframes_t pos = 0;
do_embed (paths, false, ImportAsRegion, 0, pos, true);
context->drag_finish (true, false, time);
}
}

View file

@ -900,11 +900,9 @@ RedirectBox::clear_redirects()
Gtkmm2ext::Choice prompter (prompt, choices);
if (prompter.run () == RESPONSE_ACCEPT) {
if (prompter.get_choice() == 0) {
if (prompter.run () == 0) {
_route.clear_redirects (this);
}
}
}
void

View file

@ -703,13 +703,9 @@ RouteUI::remove_this_route ()
Choice prompter (prompt, choices);
prompter.show_all ();
if (prompter.run () == RESPONSE_ACCEPT) {
if (prompter.get_choice() == 0) {
if (prompter.run () == 0) {
Glib::signal_idle().connect (bind (sigc::ptr_fun (&RouteUI::idle_remove_this_route), this));
}
}
}
gint

View file

@ -22,11 +22,13 @@
#include <map>
#include <cerrno>
#include <pbd/basename.h>
#include <gtkmm/box.h>
#include <gtkmm/stock.h>
#include <pbd/basename.h>
#include <gtkmm2ext/utils.h>
#include <ardour/audio_library.h>
#include <ardour/audioregion.h>
#include <ardour/externalsource.h>
@ -34,12 +36,14 @@
#include "gui_thread.h"
#include "prompter.h"
#include "sfdb_ui.h"
#include "utils.h"
#include "i18n.h"
using namespace ARDOUR;
using namespace std;
std::string length2string (const int32_t frames, const float sample_rate);
string length2string (const int32_t frames, const float sample_rate);
SoundFileBox::SoundFileBox ()
:
@ -281,13 +285,13 @@ SoundFileBox::field_selected ()
}
}
SoundFileBrowser::SoundFileBrowser (std::string title)
:
ArdourDialog(title),
chooser(Gtk::FILE_CHOOSER_ACTION_OPEN)
SoundFileBrowser::SoundFileBrowser (string title)
: ArdourDialog (title, false),
chooser (Gtk::FILE_CHOOSER_ACTION_OPEN)
{
get_vbox()->pack_start(chooser);
chooser.set_preview_widget(preview);
chooser.set_select_multiple (true);
chooser.signal_update_preview().connect(mem_fun(*this, &SoundFileBrowser::update_preview));
}
@ -304,63 +308,117 @@ SoundFileBrowser::update_preview ()
chooser.set_preview_widget_active(preview.setup_labels(chooser.get_filename()));
}
SoundFileChooser::SoundFileChooser (std::string title)
SoundFileChooser::SoundFileChooser (string title)
:
SoundFileBrowser(title)
{
add_button (Gtk::Stock::OPEN, Gtk::RESPONSE_OK);
add_button (Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
show_all ();
}
SoundFileOmega::SoundFileOmega (std::string title)
:
SoundFileBrowser(title),
embed_btn (_("Embed")),
import_btn (_("Import")),
static const char *import_mode_strings[] = {
X_("Add to Region list"),
X_("Add as new Track(s)"),
X_("Add to selected Track(s)"),
0
};
vector<string> SoundFileOmega::mode_strings;
SoundFileOmega::SoundFileOmega (string title)
: SoundFileBrowser (title),
split_check (_("Split Channels"))
{
get_action_area()->pack_start(embed_btn);
get_action_area()->pack_start(import_btn);
if (mode_strings.empty()) {
mode_strings = internationalize (import_mode_strings);
}
add_button (_("Embed"), ResponseEmbed);
add_button (_("Import"), ResponseImport);
add_button (Gtk::Stock::CLOSE, Gtk::RESPONSE_CLOSE);
chooser.set_extra_widget(split_check);
Gtk::HBox *box = manage (new Gtk::HBox());
embed_btn.signal_clicked().connect (mem_fun (*this, &SoundFileOmega::embed_clicked));
import_btn.signal_clicked().connect (mem_fun (*this, &SoundFileOmega::import_clicked));
Gtkmm2ext::set_popdown_strings (mode_combo, mode_strings);
set_mode (Editing::ImportAsRegion);
box->pack_start (split_check);
box->pack_start (mode_combo);
mode_combo.signal_changed().connect (mem_fun (*this, &SoundFileOmega::mode_changed));
chooser.set_extra_widget (*box);
show_all ();
}
void
SoundFileOmega::embed_clicked ()
bool
SoundFileOmega::get_split ()
{
Embedded (chooser.get_filenames(), split_check.get_active());
return split_check.get_active();
}
vector<Glib::ustring>
SoundFileOmega::get_paths ()
{
return chooser.get_filenames();
}
void
SoundFileOmega::import_clicked ()
SoundFileOmega::set_mode (Editing::ImportMode mode)
{
Imported (chooser.get_filenames(), split_check.get_active());
mode_combo.set_active_text (mode_strings[(int)mode]);
switch (mode) {
case Editing::ImportAsRegion:
split_check.set_sensitive (true);
break;
case Editing::ImportAsTrack:
split_check.set_sensitive (true);
break;
case Editing::ImportToTrack:
split_check.set_sensitive (false);
break;
}
}
std::string
length2string (const int32_t frames, const float sample_rate)
Editing::ImportMode
SoundFileOmega::get_mode ()
{
int secs = (int) (frames / sample_rate);
int hrs = secs / 3600;
secs -= (hrs * 3600);
int mins = secs / 60;
secs -= (mins * 60);
vector<string>::iterator i;
uint32_t n;
string str = mode_combo.get_active_text ();
int total_secs = (hrs * 3600) + (mins * 60) + secs;
int frames_remaining = frames - (total_secs * sample_rate);
float fractional_secs = (float) frames_remaining / sample_rate;
for (n = 0, i = mode_strings.begin (); i != mode_strings.end(); ++i, ++n) {
if (str == (*i)) {
break;
}
}
char duration_str[32];
sprintf (duration_str, "%02d:%02d:%05.2f", hrs, mins, (float) secs + fractional_secs);
if (i == mode_strings.end()) {
fatal << string_compose (_("programming error: %1"), X_("unknown import mode string")) << endmsg;
/*NOTREACHED*/
}
return duration_str;
return (Editing::ImportMode) (n);
}
void
SoundFileOmega::mode_changed ()
{
Editing::ImportMode mode = get_mode();
switch (mode) {
case Editing::ImportAsRegion:
split_check.set_sensitive (true);
break;
case Editing::ImportAsTrack:
split_check.set_sensitive (true);
break;
case Editing::ImportToTrack:
split_check.set_sensitive (false);
break;
}
}

View file

@ -33,6 +33,7 @@
#include <gtkmm/dialog.h>
#include <gtkmm/entry.h>
#include <gtkmm/filechooserwidget.h>
#include <gtkmm/comboboxtext.h>
#include <gtkmm/frame.h>
#include <gtkmm/label.h>
#include <gtkmm/liststore.h>
@ -42,6 +43,7 @@
#include <ardour/externalsource.h>
#include "ardour_dialog.h"
#include "editing.h"
class SoundFileBox : public Gtk::VBox
{
@ -111,6 +113,7 @@ class SoundFileBrowser : public ArdourDialog
virtual ~SoundFileBrowser () {};
virtual void set_session (ARDOUR::Session*);
protected:
Gtk::FileChooserWidget chooser;
SoundFileBox preview;
@ -133,16 +136,27 @@ class SoundFileOmega : public SoundFileBrowser
SoundFileOmega (std::string title);
virtual ~SoundFileOmega () {};
sigc::signal<void, std::vector<std::string>, bool> Embedded;
sigc::signal<void, std::vector<std::string>, bool> Imported;
/* these are returned by the Dialog::run() method. note
that builtin GTK responses are all negative, leaving
positive values for application-defined responses.
*/
const static int ResponseImport = 1;
const static int ResponseEmbed = 2;
std::vector<Glib::ustring> get_paths ();
bool get_split ();
void set_mode (Editing::ImportMode);
Editing::ImportMode get_mode ();
protected:
Gtk::Button embed_btn;
Gtk::Button import_btn;
Gtk::CheckButton split_check;
Gtk::ComboBoxText mode_combo;
void embed_clicked ();
void import_clicked ();
void mode_changed ();
static std::vector<std::string> mode_strings;
};
#endif // __ardour_sfdb_ui_h__

View file

@ -592,3 +592,21 @@ get_xpm (std::string name)
return (xpm_map[name]);
}
string
length2string (const int32_t frames, const float sample_rate)
{
int secs = (int) (frames / sample_rate);
int hrs = secs / 3600;
secs -= (hrs * 3600);
int mins = secs / 60;
secs -= (mins * 60);
int total_secs = (hrs * 3600) + (mins * 60) + secs;
int frames_remaining = frames - (total_secs * sample_rate);
float fractional_secs = (float) frames_remaining / sample_rate;
char duration_str[32];
sprintf (duration_str, "%02d:%02d:%05.2f", hrs, mins, (float) secs + fractional_secs);
return duration_str;
}

View file

@ -80,5 +80,6 @@ bool key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev);
Glib::RefPtr<Gdk::Pixbuf> get_xpm(std::string);
static std::map<std::string, Glib::RefPtr<Gdk::Pixbuf> > xpm_map;
const char* const *get_xpm_data (std::string path);
std::string length2string (const int32_t frames, const float sample_rate);
#endif /* __ardour_gtk_utils_h__ */

View file

@ -279,15 +279,13 @@ VisualTimeAxis::remove_this_time_axis(void* src)
Gtkmm2ext::Choice prompter (prompt, choices);
if (prompter.run () == RESPONSE_ACCEPT) {
if (prompter.get_choice() == 0) {
if (prompter.run () == 0) {
/*
defer to idle loop, otherwise we'll delete this object
while we're still inside this function ...
*/
Glib::signal_idle().connect(bind(sigc::ptr_fun(&VisualTimeAxis::idle_remove_this_time_axis), this, src));
}
}
}
/**

View file

@ -647,6 +647,10 @@ class Session : public sigc::trackable, public Stateful
bool sample_convert;
volatile bool freeze;
string pathname;
/* result */
std::vector<AudioRegion*> new_regions;
};
int import_audiofile (import_status&);

View file

@ -61,11 +61,12 @@ Session::import_audiofile (import_status& status)
jack_nframes_t so_far;
char buf[PATH_MAX+1];
int ret = -1;
vector<AudioRegion *> new_regions;
vector<string> new_paths;
struct tm* now;
string tmp_convert_file;
status.new_regions.clear ();
if ((in = sf_open (status.pathname.c_str(), SFM_READ, &info)) == 0) {
error << string_compose(_("Import: cannot open input sound file \"%1\""), status.pathname) << endmsg;
return -1;
@ -214,7 +215,7 @@ Session::import_audiofile (import_status& status)
AudioRegion *r = new AudioRegion (sources, 0, newfiles[0]->length(), region_name_from_path (PBD::basename(basepath)),
0, AudioRegion::Flag (AudioRegion::DefaultFlags | AudioRegion::WholeFile));
new_regions.push_back (r);
status.new_regions.push_back (r);
} else {
for (n = 0; n < nfiles && !status.cancel; ++n) {
@ -230,7 +231,7 @@ Session::import_audiofile (import_status& status)
AudioRegion *r = new AudioRegion (*newfiles[n], 0, newfiles[n]->length(), region_name_from_path (PBD::basename (newfiles[n]->name())),
0, AudioRegion::Flag (AudioRegion::DefaultFlags | AudioRegion::WholeFile | AudioRegion::Import));
new_regions.push_back (r);
status.new_regions.push_back (r);
}
}
@ -259,7 +260,7 @@ Session::import_audiofile (import_status& status)
}
if (status.cancel) {
for (vector<AudioRegion *>::iterator i = new_regions.begin(); i != new_regions.end(); ++i) {
for (vector<AudioRegion *>::iterator i = status.new_regions.begin(); i != status.new_regions.end(); ++i) {
delete *i;
}

View file

@ -26,28 +26,30 @@ using namespace Gtkmm2ext;
using namespace sigc;
using namespace Gtk;
Choice::Choice (string prompt, vector<string> choices)
Choice::Choice (string prompt, vector<string> choices, bool center)
{
int n;
vector<string>::iterator i;
if (center) {
set_position (Gtk::WIN_POS_CENTER);
} else {
set_position (Gtk::WIN_POS_MOUSE);
}
set_name ("ChoiceWindow");
Label* label = manage (new Label (prompt));
label->show ();
get_vbox()->set_border_width (12);
get_vbox()->pack_start (*label);
set_has_separator (false);
for (n = 0, i = choices.begin(); i != choices.end(); ++i, ++n) {
Button* button;
button = add_button (*i, RESPONSE_ACCEPT);
button->signal_button_release_event().connect (bind (mem_fun (*this, &Choice::choice_made), n), false);
add_button (*i, n);
}
which_choice = -1;
}
void
@ -60,17 +62,3 @@ Choice::on_realize ()
Choice::~Choice ()
{
}
bool
Choice::choice_made (GdkEventButton* ev, int nbutton)
{
which_choice = nbutton;
response (RESPONSE_ACCEPT);
return true;
}
int
Choice::get_choice ()
{
return which_choice;
}

View file

@ -10,17 +10,11 @@ namespace Gtkmm2ext {
class Choice : public Gtk::Dialog
{
public:
Choice (std::string prompt, std::vector<std::string> choices);
Choice (std::string prompt, std::vector<std::string> choices, bool center = true);
virtual ~Choice ();
int get_choice ();
protected:
void on_realize ();
private:
int which_choice;
bool choice_made (GdkEventButton* ev, int nbutton);
};
} /* namespace */

View file

@ -26,8 +26,6 @@
#include <pbd/pthread_utils.h>
using std::string;
using std::cerr;
using std::endl;
typedef std::map<string,pthread_t> ThreadMap;
static ThreadMap all_threads;