#include <ardour/audioengine.h>
#include <ardour/configuration.h>
#include <ardour/session.h>
-#include <ardour/audio_diskstream.h>
#include <ardour/utils.h>
+#include <ardour/audio_diskstream.h>
#include <ardour/audioplaylist.h>
#include <ardour/audioregion.h>
#include <ardour/audiofilesource.h>
+#include <ardour/midi_diskstream.h>
+#include <ardour/midi_playlist.h>
+#include <ardour/midi_region.h>
+#include <ardour/smf_source.h>
#include <ardour/auditioner.h>
#include <ardour/recent_sessions.h>
#include <ardour/redirect.h>
#include <ardour/slave.h>
#include <ardour/tempo.h>
#include <ardour/audio_track.h>
+#include <ardour/midi_track.h>
#include <ardour/cycle_timer.h>
#include <ardour/named_selection.h>
#include <ardour/crossfade.h>
#include <ardour/playlist.h>
#include <ardour/click.h>
#include <ardour/data_type.h>
+#include <ardour/buffer_set.h>
#include <ardour/source_factory.h>
#include <ardour/region_factory.h>
using namespace PBD;
using boost::shared_ptr;
+#ifdef __x86_64__
+static const int CPU_CACHE_ALIGN = 64;
+#else
+static const int CPU_CACHE_ALIGN = 16; /* arguably 32 on most arches, but it matters less */
+#endif
+
const char* Session::_template_suffix = X_(".template");
const char* Session::_statefile_suffix = X_(".ardour");
const char* Session::_pending_suffix = X_(".pending");
const char* Session::interchange_dir_name = X_("interchange");
const char* Session::export_dir_name = X_("export");
-Session::compute_peak_t Session::compute_peak = 0;
-Session::find_peaks_t Session::find_peaks = 0;
-Session::apply_gain_to_buffer_t Session::apply_gain_to_buffer = 0;
-Session::mix_buffers_with_gain_t Session::mix_buffers_with_gain = 0;
-Session::mix_buffers_no_gain_t Session::mix_buffers_no_gain = 0;
-
sigc::signal<int> Session::AskAboutPendingState;
sigc::signal<void> Session::SendFeedback;
string* mix_template)
: _engine (eng),
+ _scratch_buffers(new BufferSet()),
+ _silent_buffers(new BufferSet()),
+ _send_buffers(new BufferSet()),
_mmc_port (default_mmc_port),
_mtc_port (default_mtc_port),
_midi_port (default_midi_port),
pending_events (2048),
- midi_requests (128), // the size of this should match the midi request pool size
+ //midi_requests (128), // the size of this should match the midi request pool size
+ _send_smpte_update (false),
diskstreams (new DiskstreamList),
routes (new RouteList),
auditioner ((Auditioner*) 0),
nframes_t initial_length)
: _engine (eng),
+ _scratch_buffers(new BufferSet()),
+ _silent_buffers(new BufferSet()),
+ _send_buffers(new BufferSet()),
_mmc_port (default_mmc_port),
_mtc_port (default_mtc_port),
_midi_port (default_midi_port),
pending_events (2048),
- midi_requests (16),
+ //midi_requests (16),
+ _send_smpte_update (false),
diskstreams (new DiskstreamList),
routes (new RouteList),
main_outs (0)
}
terminate_butler_thread ();
- terminate_midi_thread ();
+ //terminate_midi_thread ();
if (click_data && click_data != default_click) {
delete [] click_data;
clear_clicks ();
- for (vector<Sample*>::iterator i = _passthru_buffers.begin(); i != _passthru_buffers.end(); ++i) {
- free(*i);
- }
-
- for (vector<Sample*>::iterator i = _silent_buffers.begin(); i != _silent_buffers.end(); ++i) {
- free(*i);
- }
-
- for (vector<Sample*>::iterator i = _send_buffers.begin(); i != _send_buffers.end(); ++i) {
- free(*i);
- }
+ delete _scratch_buffers;
+ delete _silent_buffers;
+ delete _send_buffers;
AudioDiskstream::free_working_buffers();
-
- /* this should cause deletion of the auditioner */
-
- // auditioner.reset ();
#undef TRACK_DESTRUCTION
#ifdef TRACK_DESTRUCTION
unused_playlists.clear ();
#ifdef TRACK_DESTRUCTION
- cerr << "delete audio regions\n";
+ cerr << "delete regions\n";
#endif /* TRACK_DESTRUCTION */
- for (AudioRegionList::iterator i = audio_regions.begin(); i != audio_regions.end(); ) {
- AudioRegionList::iterator tmp;
+ for (RegionList::iterator i = regions.begin(); i != regions.end(); ) {
+ RegionList::iterator tmp;
tmp = i;
++tmp;
i = tmp;
}
- audio_regions.clear ();
-
+ regions.clear ();
+
#ifdef TRACK_DESTRUCTION
cerr << "delete routes\n";
#endif /* TRACK_DESTRUCTION */
#ifdef TRACK_DESTRUCTION
cerr << "delete audio sources\n";
#endif /* TRACK_DESTRUCTION */
- for (AudioSourceList::iterator i = audio_sources.begin(); i != audio_sources.end(); ) {
- AudioSourceList::iterator tmp;
+ for (SourceMap::iterator i = sources.begin(); i != sources.end(); ) {
+ SourceMap::iterator tmp;
tmp = i;
++tmp;
i = tmp;
}
- audio_sources.clear ();
+ sources.clear ();
#ifdef TRACK_DESTRUCTION
cerr << "delete mix groups\n";
/* default state for Click */
- first_physical_output = _engine.get_nth_physical_output (0);
-
+ first_physical_output = _engine.get_nth_physical_output (DataType::AUDIO, 0);
+
if (first_physical_output.length()) {
if (_click_io->add_output_port (first_physical_output, this)) {
// relax, even though its an error
Connection* c = new OutputConnection (buf, true);
c->add_port ();
- c->add_connection (0, _engine.get_nth_physical_output (np));
+ c->add_connection (0, _engine.get_nth_physical_output (DataType::AUDIO, np));
add_connection (c);
}
Connection* c = new InputConnection (buf, true);
c->add_port ();
- c->add_connection (0, _engine.get_nth_physical_input (np));
+ c->add_connection (0, _engine.get_nth_physical_input (DataType::AUDIO, np));
add_connection (c);
}
c->add_port ();
c->add_port ();
- c->add_connection (0, _engine.get_nth_physical_output (np));
- c->add_connection (1, _engine.get_nth_physical_output (np+1));
+ c->add_connection (0, _engine.get_nth_physical_output (DataType::AUDIO, np));
+ c->add_connection (1, _engine.get_nth_physical_output (DataType::AUDIO, np+1));
add_connection (c);
}
c->add_port ();
c->add_port ();
- c->add_connection (0, _engine.get_nth_physical_input (np));
- c->add_connection (1, _engine.get_nth_physical_input (np+1));
+ c->add_connection (0, _engine.get_nth_physical_input (DataType::AUDIO, np));
+ c->add_connection (1, _engine.get_nth_physical_input (DataType::AUDIO, np+1));
add_connection (c);
}
_master_out->defer_pan_reset ();
- while ((int) _master_out->n_inputs() < _master_out->input_maximum()) {
- if (_master_out->add_input_port ("", this)) {
+ while (_master_out->n_inputs().n_audio()
+ < _master_out->input_maximum().n_audio()) {
+ if (_master_out->add_input_port ("", this, DataType::AUDIO)) {
error << _("cannot setup master inputs")
<< endmsg;
break;
}
}
n = 0;
- while ((int) _master_out->n_outputs() < _master_out->output_maximum()) {
- if (_master_out->add_output_port (_engine.get_nth_physical_output (n), this)) {
+ while (_master_out->n_outputs().n_audio()
+ < _master_out->output_maximum().n_audio()) {
+ if (_master_out->add_output_port (_engine.get_nth_physical_output (DataType::AUDIO, n), this, DataType::AUDIO)) {
error << _("cannot setup master outputs")
<< endmsg;
break;
Connection* c = new OutputConnection (_("Master Out"), true);
- for (uint32_t n = 0; n < _master_out->n_inputs (); ++n) {
+ for (uint32_t n = 0; n < _master_out->n_inputs ().get_total(); ++n) {
c->add_port ();
c->add_connection ((int) n, _master_out->input(n)->name());
}
osc->set_session (*this);
#endif
-
+
_state_of_the_state = Clean;
DirtyChanged (); /* EMIT SIGNAL */
if (_control_out) {
uint32_t n;
+ vector<string> cports;
- while ((int) _control_out->n_inputs() < _control_out->input_maximum()) {
+ while (_control_out->n_inputs().n_audio() < _control_out->input_maximum().n_audio()) {
if (_control_out->add_input_port ("", this)) {
error << _("cannot setup control inputs")
<< endmsg;
}
}
n = 0;
- while ((int) _control_out->n_outputs() < _control_out->output_maximum()) {
- if (_control_out->add_output_port (_engine.get_nth_physical_output (n), this)) {
+ while (_control_out->n_outputs().n_audio() < _control_out->output_maximum().n_audio()) {
+ if (_control_out->add_output_port (_engine.get_nth_physical_output (DataType::AUDIO, n), this)) {
error << _("cannot set up master outputs")
<< endmsg;
break;
}
n++;
}
- }
+
+
+ uint32_t ni = _control_out->n_inputs().get (DataType::AUDIO);
+
+ for (n = 0; n < ni; ++n) {
+ cports.push_back (_control_out->input(n)->name());
+ }
+
+ boost::shared_ptr<RouteList> r = routes.reader ();
+
+ for (RouteList::iterator x = r->begin(); x != r->end(); ++x) {
+ (*x)->set_control_outs (cports);
+ }
+ }
/* Tell all IO objects to connect themselves together */
if (g_atomic_int_get (&_record_status) != Recording) {
g_atomic_int_set (&_record_status, Recording);
_last_record_location = _transport_frame;
- send_mmc_in_another_thread (MIDI::MachineControl::cmdRecordStrobe);
+ deliver_mmc(MIDI::MachineControl::cmdRecordStrobe, _last_record_location);
if (Config->get_monitoring_model() == HardwareMonitoring && Config->get_auto_input()) {
boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
}
}
- send_mmc_in_another_thread (MIDI::MachineControl::cmdRecordExit);
+ // FIXME: timestamp correct? [DR]
+ // FIXME FIXME FIXME: rt_context? this must be called in the process thread.
+ // does this /need/ to be sent in all cases?
+ if (rt_context)
+ deliver_mmc (MIDI::MachineControl::cmdRecordExit, _transport_frame);
if (Config->get_monitoring_model() == HardwareMonitoring && Config->get_auto_input()) {
boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
enable_record ();
}
} else {
- send_mmc_in_another_thread (MIDI::MachineControl::cmdRecordPause);
+ deliver_mmc (MIDI::MachineControl::cmdRecordPause, _transport_frame);
RecordStateChanged (); /* EMIT SIGNAL */
}
sync_time_vars();
- Route::set_automation_interval ((jack_nframes_t) ceil ((double) frames_per_second * 0.25));
+ Route::set_automation_interval ((nframes_t) ceil ((double) frames_per_second * 0.25));
// XXX we need some equivalent to this, somehow
// SndFileSource::setup_standard_crossfades (frames_per_second);
*/
{
- vector<Sample*>::iterator i;
- uint32_t np;
current_block_size = nframes;
-
- for (np = 0, i = _passthru_buffers.begin(); i != _passthru_buffers.end(); ++i, ++np) {
- free (*i);
- }
-
- for (vector<Sample*>::iterator i = _silent_buffers.begin(); i != _silent_buffers.end(); ++i) {
- free (*i);
- }
- _passthru_buffers.clear ();
- _silent_buffers.clear ();
+ ensure_buffers(_scratch_buffers->available());
- ensure_passthru_buffers (np);
-
- for (vector<Sample*>::iterator i = _send_buffers.begin(); i != _send_buffers.end(); ++i) {
- free(*i);
-
- Sample *buf;
-#ifdef NO_POSIX_MEMALIGN
- buf = (Sample *) malloc(current_block_size * sizeof(Sample));
-#else
- posix_memalign((void **)&buf,16,current_block_size * 4);
-#endif
- *i = buf;
-
- memset (*i, 0, sizeof (Sample) * current_block_size);
- }
-
-
if (_gain_automation_buffer) {
delete [] _gain_automation_buffer;
}
for (i = r->begin(); i != r->end(); ++i) {
trace_terminal (*i, *i);
- }
-
+ }
+
RouteSorter cmp;
r->sort (cmp);
- /* don't leave dangling references to routes in Route::fed_by */
-
- for (i = r->begin(); i != r->end(); ++i) {
- (*i)->fed_by.clear ();
- }
-
#if 0
cerr << "finished route resort\n";
}
+list<boost::shared_ptr<MidiTrack> >
+Session::new_midi_track (TrackMode mode, uint32_t how_many)
+{
+ char track_name[32];
+ uint32_t track_id = 0;
+ uint32_t n = 0;
+ uint32_t channels_used = 0;
+ string port;
+ RouteList new_routes;
+ list<boost::shared_ptr<MidiTrack> > ret;
+
+ /* count existing midi tracks */
+
+ {
+ shared_ptr<RouteList> r = routes.reader ();
+
+ for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+ if (dynamic_cast<MidiTrack*>((*i).get()) != 0) {
+ if (!(*i)->hidden()) {
+ n++;
+ channels_used += (*i)->n_inputs().n_midi();
+ }
+ }
+ }
+ }
+
+ while (how_many) {
+
+ /* check for duplicate route names, since we might have pre-existing
+ routes with this name (e.g. create Midi1, Midi2, delete Midi1,
+ save, close,restart,add new route - first named route is now
+ Midi2)
+ */
+
+
+ do {
+ ++track_id;
+
+ snprintf (track_name, sizeof(track_name), "Midi %" PRIu32, track_id);
+
+ if (route_by_name (track_name) == 0) {
+ break;
+ }
+
+ } while (track_id < (UINT_MAX-1));
+
+ try {
+ shared_ptr<MidiTrack> track (new MidiTrack (*this, track_name, Route::Flag (0), mode));
+
+ if (track->ensure_io (ChanCount(DataType::MIDI, 1), ChanCount(DataType::MIDI, 1), false, this)) {
+ error << "cannot configure 1 in/1 out configuration for new midi track" << endmsg;
+ }
+
+ channels_used += track->n_inputs ().n_midi();
+
+ track->DiskstreamChanged.connect (mem_fun (this, &Session::resort_routes));
+ track->set_remote_control_id (ntracks());
+
+ new_routes.push_back (track);
+ ret.push_back (track);
+ }
+
+ catch (failed_constructor &err) {
+ error << _("Session: could not create new midi track.") << endmsg;
+ // XXX should we delete the tracks already created?
+ ret.clear ();
+ return ret;
+ }
+
+ --how_many;
+ }
+
+ if (!new_routes.empty()) {
+ add_routes (new_routes, false);
+ save_state (_current_snapshot_name);
+ }
+
+ return ret;
+}
+
list<boost::shared_ptr<AudioTrack> >
Session::new_audio_track (int input_channels, int output_channels, TrackMode mode, uint32_t how_many)
{
if (dynamic_cast<AudioTrack*>((*i).get()) != 0) {
if (!(*i)->hidden()) {
n++;
- channels_used += (*i)->n_inputs();
+ channels_used += (*i)->n_inputs().n_audio();
}
}
}
try {
track = boost::shared_ptr<AudioTrack>((new AudioTrack (*this, track_name, Route::Flag (0), mode)));
- if (track->ensure_io (input_channels, output_channels, false, this)) {
+ if (track->ensure_io (ChanCount(DataType::AUDIO, input_channels), ChanCount(DataType::AUDIO, output_channels), false, this)) {
error << string_compose (_("cannot configure %1 in/%2 out configuration for new audio track"),
input_channels, output_channels)
<< endmsg;
}
if (nphysical_in) {
- for (uint32_t x = 0; x < track->n_inputs() && x < nphysical_in; ++x) {
+ for (uint32_t x = 0; x < track->n_inputs().n_audio() && x < nphysical_in; ++x) {
port = "";
}
}
- for (uint32_t x = 0; x < track->n_outputs(); ++x) {
+ for (uint32_t x = 0; x < track->n_outputs().n_midi(); ++x) {
port = "";
port = physoutputs[(channels_used+x)%nphysical_out];
} else if (Config->get_output_auto_connect() & AutoConnectMaster) {
if (_master_out) {
- port = _master_out->input (x%_master_out->n_inputs())->name();
+ port = _master_out->input (x%_master_out->n_inputs().n_audio())->name();
}
}
}
}
- channels_used += track->n_inputs ();
+ channels_used += track->n_inputs ().n_audio();
- if (_control_out) {
- vector<string> cports;
- uint32_t ni = _control_out->n_inputs();
-
- for (n = 0; n < ni; ++n) {
- cports.push_back (_control_out->input(n)->name());
- }
-
- track->set_control_outs (cports);
- }
-
- // assert (current_thread != RT_thread)
-
track->audio_diskstream()->non_realtime_input_change();
track->DiskstreamChanged.connect (mem_fun (this, &Session::resort_routes));
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
if (dynamic_cast<AudioTrack*>((*i).get()) == 0) {
- if (!(*i)->hidden()) {
+ if (!(*i)->hidden() && (*i)->name() != _("master")) {
bus_id++;
}
}
while (how_many) {
do {
- ++bus_id;
-
snprintf (bus_name, sizeof(bus_name), "Bus %" PRIu32, bus_id);
+ bus_id++;
+
if (route_by_name (bus_name) == 0) {
break;
}
try {
shared_ptr<Route> bus (new Route (*this, bus_name, -1, -1, -1, -1, Route::Flag(0), DataType::AUDIO));
- if (bus->ensure_io (input_channels, output_channels, false, this)) {
+ if (bus->ensure_io (ChanCount(DataType::AUDIO, input_channels), ChanCount(DataType::AUDIO, output_channels), false, this)) {
error << string_compose (_("cannot configure %1 in/%2 out configuration for new audio track"),
input_channels, output_channels)
<< endmsg;
goto failure;
}
- for (uint32_t x = 0; n_physical_inputs && x < bus->n_inputs(); ++x) {
+ for (uint32_t x = 0; n_physical_inputs && x < bus->n_inputs().n_audio(); ++x) {
port = "";
}
}
- for (uint32_t x = 0; n_physical_outputs && x < bus->n_outputs(); ++x) {
+ for (uint32_t x = 0; n_physical_outputs && x < bus->n_outputs().n_audio(); ++x) {
port = "";
port = physoutputs[((n+x)%n_physical_outputs)];
} else if (Config->get_output_auto_connect() & AutoConnectMaster) {
if (_master_out) {
- port = _master_out->input (x%_master_out->n_inputs())->name();
+ port = _master_out->input (x%_master_out->n_inputs().n_audio())->name();
}
}
}
}
- if (_control_out) {
- vector<string> cports;
- uint32_t ni = _control_out->n_inputs();
-
- for (uint32_t n = 0; n < ni; ++n) {
- cports.push_back (_control_out->input(n)->name());
- }
- bus->set_control_outs (cports);
- }
-
bus->set_remote_control_id (control_id);
++control_id;
if ((*x)->control()) {
_control_out = (*x);
- }
+ }
}
+ if (_control_out && IO::connecting_legal) {
+
+ vector<string> cports;
+ uint32_t ni = _control_out->n_inputs().n_audio();
+
+ for (uint32_t n = 0; n < ni; ++n) {
+ cports.push_back (_control_out->input(n)->name());
+ }
+
+ for (RouteList::iterator x = new_routes.begin(); x != new_routes.end(); ++x) {
+ (*x)->set_control_outs (cports);
+ }
+ }
+
set_dirty();
if (save) {
Session::add_diskstream (boost::shared_ptr<Diskstream> dstream)
{
/* need to do this in case we're rolling at the time, to prevent false underruns */
- dstream->do_refill_with_alloc();
+ dstream->do_refill_with_alloc ();
- {
+ dstream->set_block_size (current_block_size);
+
+ {
RCUWriter<DiskstreamList> writer (diskstreams);
boost::shared_ptr<DiskstreamList> ds = writer.get_copy();
ds->push_back (dstream);
- }
-
- dstream->set_block_size (current_block_size);
+ /* writer goes out of scope, copies ds back to main */
+ }
dstream->PlaylistChanged.connect (sigc::bind (mem_fun (*this, &Session::diskstream_playlist_changed), dstream));
/* this will connect to future changes, and check the current length */
diskstream_playlist_changed (dstream);
dstream->prepare ();
+
}
void
/* writer goes out of scope, forces route list update */
}
- // FIXME: audio specific
- AudioTrack* at;
- boost::shared_ptr<AudioDiskstream> ds;
+ Track* t;
+ boost::shared_ptr<Diskstream> ds;
- if ((at = dynamic_cast<AudioTrack*>(route.get())) != 0) {
- ds = at->audio_diskstream();
+ if ((t = dynamic_cast<Track*>(route.get())) != 0) {
+ ds = t->diskstream();
}
if (ds) {
/* don't mess with busses */
- if (dynamic_cast<AudioTrack*>((*i).get()) == 0) {
+ if (dynamic_cast<Track*>((*i).get()) == 0) {
continue;
}
/* don't mess with tracks */
- if (dynamic_cast<AudioTrack*>((*i).get()) != 0) {
+ if (dynamic_cast<Track*>((*i).get()) != 0) {
continue;
}
}
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
if ((*i)->soloed()) {
something_soloed = true;
- if (dynamic_cast<AudioTrack*>((*i).get())) {
+ if (dynamic_cast<Track*>((*i).get())) {
if (is_track) {
same_thing_soloed = true;
break;
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
if ((*i)->soloed()) {
mute = true;
- if (dynamic_cast<AudioTrack*>((*i).get())) {
+ if (dynamic_cast<Track*>((*i).get())) {
is_track = true;
}
break;
/* only alter track solo mute */
- if (dynamic_cast<AudioTrack*>((*i).get())) {
+ if (dynamic_cast<Track*>((*i).get())) {
if ((*i)->soloed()) {
(*i)->set_solo_mute (!mute);
} else {
/* only alter bus solo mute */
- if (!dynamic_cast<AudioTrack*>((*i).get())) {
+ if (!dynamic_cast<Track*>((*i).get())) {
if ((*i)->soloed()) {
return boost::shared_ptr<Diskstream>((Diskstream*) 0);
}
-/* AudioRegion management */
+/* Region management */
string
Session::new_region_name (string old)
while (number < (UINT_MAX-1)) {
- AudioRegionList::const_iterator i;
+ RegionList::const_iterator i;
string sbuf;
number++;
snprintf (buf, len, "%s%" PRIu32, old.substr (0, last_period + 1).c_str(), number);
sbuf = buf;
- for (i = audio_regions.begin(); i != audio_regions.end(); ++i) {
+ for (i = regions.begin(); i != regions.end(); ++i) {
if (i->second->name() == sbuf) {
break;
}
}
- if (i == audio_regions.end()) {
+ if (i == regions.end()) {
break;
}
}
char buf[16];
string subbase;
+ assert(base.find("/") == string::npos);
+
if (base == "") {
Glib::Mutex::Lock lm (region_lock);
- snprintf (buf, sizeof (buf), "%d", (int)audio_regions.size() + 1);
+ snprintf (buf, sizeof (buf), "%d", (int)regions.size() + 1);
result = "region.";
name_taken = false;
- for (AudioRegionList::const_iterator i = audio_regions.begin(); i != audio_regions.end(); ++i) {
+ for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
if (i->second->name() == result) {
name_taken = true;
break;
void
Session::add_region (boost::shared_ptr<Region> region)
{
- boost::shared_ptr<AudioRegion> ar;
- boost::shared_ptr<AudioRegion> oar;
+ boost::shared_ptr<Region> other;
bool added = false;
{
Glib::Mutex::Lock lm (region_lock);
- if ((ar = boost::dynamic_pointer_cast<AudioRegion> (region)) != 0) {
+ RegionList::iterator x;
- AudioRegionList::iterator x;
+ for (x = regions.begin(); x != regions.end(); ++x) {
- for (x = audio_regions.begin(); x != audio_regions.end(); ++x) {
+ other = x->second;
- oar = boost::dynamic_pointer_cast<AudioRegion> (x->second);
-
- if (ar->region_list_equivalent (oar)) {
- break;
- }
+ if (region->region_list_equivalent (other)) {
+ break;
}
+ }
- if (x == audio_regions.end()) {
+ if (x == regions.end()) {
- pair<AudioRegionList::key_type,AudioRegionList::mapped_type> entry;
+ pair<RegionList::key_type,RegionList::mapped_type> entry;
- entry.first = region->id();
- entry.second = ar;
+ entry.first = region->id();
+ entry.second = region;
- pair<AudioRegionList::iterator,bool> x = audio_regions.insert (entry);
+ pair<RegionList::iterator,bool> x = regions.insert (entry);
-
- if (!x.second) {
- return;
- }
- added = true;
- }
-
- } else {
+ if (!x.second) {
+ return;
+ }
- fatal << _("programming error: ")
- << X_("unknown region type passed to Session::add_region()")
- << endmsg;
- /*NOTREACHED*/
+ added = true;
+ }
- }
}
/* mark dirty because something has changed even if we didn't
if (added) {
region->GoingAway.connect (sigc::bind (mem_fun (*this, &Session::remove_region), boost::weak_ptr<Region>(region)));
region->StateChanged.connect (sigc::bind (mem_fun (*this, &Session::region_changed), boost::weak_ptr<Region>(region)));
- AudioRegionAdded (ar); /* EMIT SIGNAL */
+ RegionAdded (region); /* EMIT SIGNAL */
}
}
void
Session::remove_region (boost::weak_ptr<Region> weak_region)
{
- AudioRegionList::iterator i;
+ RegionList::iterator i;
boost::shared_ptr<Region> region (weak_region.lock ());
if (!region) {
return;
}
- boost::shared_ptr<AudioRegion> ar;
bool removed = false;
{
Glib::Mutex::Lock lm (region_lock);
- if ((ar = boost::dynamic_pointer_cast<AudioRegion> (region)) != 0) {
- if ((i = audio_regions.find (region->id())) != audio_regions.end()) {
- audio_regions.erase (i);
- removed = true;
- }
-
- } else {
-
- fatal << _("programming error: ")
- << X_("unknown region type passed to Session::remove_region()")
- << endmsg;
- /*NOTREACHED*/
+ if ((i = regions.find (region->id())) != regions.end()) {
+ regions.erase (i);
+ removed = true;
}
}
set_dirty();
if (removed) {
- AudioRegionRemoved (ar); /* EMIT SIGNAL */
+ RegionRemoved(region); /* EMIT SIGNAL */
}
}
-boost::shared_ptr<AudioRegion>
-Session::find_whole_file_parent (boost::shared_ptr<AudioRegion const> child)
+boost::shared_ptr<Region>
+Session::find_whole_file_parent (boost::shared_ptr<Region const> child)
{
- AudioRegionList::iterator i;
- boost::shared_ptr<AudioRegion> region;
+ RegionList::iterator i;
+ boost::shared_ptr<Region> region;
+
Glib::Mutex::Lock lm (region_lock);
- for (i = audio_regions.begin(); i != audio_regions.end(); ++i) {
+ for (i = regions.begin(); i != regions.end(); ++i) {
region = i->second;
}
}
- return boost::shared_ptr<AudioRegion> ();
+ return boost::shared_ptr<Region> ();
}
void
}
destroy_regions (r);
+
+ save_state (_current_snapshot_name);
+
return 0;
}
}
/* Source Management */
-
void
Session::add_source (boost::shared_ptr<Source> source)
{
- boost::shared_ptr<AudioFileSource> afs;
+ pair<SourceMap::key_type, SourceMap::mapped_type> entry;
+ pair<SourceMap::iterator,bool> result;
- if ((afs = boost::dynamic_pointer_cast<AudioFileSource>(source)) != 0) {
-
- pair<AudioSourceList::key_type, AudioSourceList::mapped_type> entry;
- pair<AudioSourceList::iterator,bool> result;
-
- entry.first = source->id();
- entry.second = afs;
-
- {
- Glib::Mutex::Lock lm (audio_source_lock);
- result = audio_sources.insert (entry);
- }
-
- if (result.second) {
- source->GoingAway.connect (sigc::bind (mem_fun (this, &Session::remove_source), boost::weak_ptr<Source> (source)));
- set_dirty();
- }
+ entry.first = source->id();
+ entry.second = source;
+
+ {
+ Glib::Mutex::Lock lm (source_lock);
+ result = sources.insert (entry);
+ }
- }
+ if (result.second) {
+ source->GoingAway.connect (sigc::bind (mem_fun (this, &Session::remove_source), boost::weak_ptr<Source> (source)));
+ set_dirty();
+ }
}
void
Session::remove_source (boost::weak_ptr<Source> src)
{
- AudioSourceList::iterator i;
+ SourceMap::iterator i;
boost::shared_ptr<Source> source = src.lock();
if (!source) {
}
{
- Glib::Mutex::Lock lm (audio_source_lock);
+ Glib::Mutex::Lock lm (source_lock);
- if ((i = audio_sources.find (source->id())) != audio_sources.end()) {
- audio_sources.erase (i);
- }
+ {
+ Glib::Mutex::Lock lm (source_lock);
+
+ if ((i = sources.find (source->id())) != sources.end()) {
+ sources.erase (i);
+ }
+ }
}
if (!_state_of_the_state & InCleanup) {
boost::shared_ptr<Source>
Session::source_by_id (const PBD::ID& id)
{
- Glib::Mutex::Lock lm (audio_source_lock);
- AudioSourceList::iterator i;
+ Glib::Mutex::Lock lm (source_lock);
+ SourceMap::iterator i;
boost::shared_ptr<Source> source;
- if ((i = audio_sources.find (id)) != audio_sources.end()) {
+ if ((i = sources.find (id)) != sources.end()) {
source = i->second;
}
return source;
}
+
+boost::shared_ptr<Source>
+Session::source_by_path_and_channel (const Glib::ustring& path, uint16_t chn)
+{
+ Glib::Mutex::Lock lm (source_lock);
+
+ for (SourceMap::iterator i = sources.begin(); i != sources.end(); ++i) {
+ cerr << "comparing " << path << " with " << i->second->name() << endl;
+ boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource>(i->second);
+
+ if (afs && afs->path() == path && chn == afs->channel()) {
+ return afs;
+ }
+
+ }
+ return boost::shared_ptr<Source>();
+}
+
string
Session::peak_path_from_audio_path (string audio_path) const
{
boost::shared_ptr<AudioFileSource>
Session::create_audio_source_for_session (AudioDiskstream& ds, uint32_t chan, bool destructive)
{
- string spath = audio_path_from_name (ds.name(), ds.n_channels(), chan, destructive);
- return boost::dynamic_pointer_cast<AudioFileSource> (SourceFactory::createWritable (*this, spath, destructive, frame_rate()));
+ string spath = audio_path_from_name (ds.name(), ds.n_channels().n_audio(), chan, destructive);
+ return boost::dynamic_pointer_cast<AudioFileSource> (
+ SourceFactory::createWritable (DataType::AUDIO, *this, spath, destructive, frame_rate()));
}
+// FIXME: _terrible_ code duplication
+string
+Session::change_midi_path_by_name (string path, string oldname, string newname, bool destructive)
+{
+ string look_for;
+ string old_basename = PBD::basename_nosuffix (oldname);
+ string new_legalized = legalize_for_path (newname);
+
+ /* note: we know (or assume) the old path is already valid */
+
+ if (destructive) {
+
+ /* destructive file sources have a name of the form:
+
+ /path/to/Tnnnn-NAME(%[LR])?.wav
+
+ the task here is to replace NAME with the new name.
+ */
+
+ /* find last slash */
+
+ string dir;
+ string prefix;
+ string::size_type slash;
+ string::size_type dash;
+
+ if ((slash = path.find_last_of ('/')) == string::npos) {
+ return "";
+ }
+
+ dir = path.substr (0, slash+1);
+
+ /* '-' is not a legal character for the NAME part of the path */
+
+ if ((dash = path.find_last_of ('-')) == string::npos) {
+ return "";
+ }
+
+ prefix = path.substr (slash+1, dash-(slash+1));
+
+ path = dir;
+ path += prefix;
+ path += '-';
+ path += new_legalized;
+ path += ".mid"; /* XXX gag me with a spoon */
+
+ } else {
+
+ /* non-destructive file sources have a name of the form:
+
+ /path/to/NAME-nnnnn(%[LR])?.wav
+
+ the task here is to replace NAME with the new name.
+ */
+
+ string dir;
+ string suffix;
+ string::size_type slash;
+ string::size_type dash;
+ string::size_type postfix;
+
+ /* find last slash */
+
+ if ((slash = path.find_last_of ('/')) == string::npos) {
+ return "";
+ }
+
+ dir = path.substr (0, slash+1);
+
+ /* '-' is not a legal character for the NAME part of the path */
+
+ if ((dash = path.find_last_of ('-')) == string::npos) {
+ return "";
+ }
+
+ suffix = path.substr (dash+1);
+
+ // Suffix is now everything after the dash. Now we need to eliminate
+ // the nnnnn part, which is done by either finding a '%' or a '.'
+
+ postfix = suffix.find_last_of ("%");
+ if (postfix == string::npos) {
+ postfix = suffix.find_last_of ('.');
+ }
+
+ if (postfix != string::npos) {
+ suffix = suffix.substr (postfix);
+ } else {
+ error << "Logic error in Session::change_midi_path_by_name(), please report to the developers" << endl;
+ return "";
+ }
+
+ const uint32_t limit = 10000;
+ char buf[PATH_MAX+1];
+
+ for (uint32_t cnt = 1; cnt <= limit; ++cnt) {
+
+ snprintf (buf, sizeof(buf), "%s%s-%u%s", dir.c_str(), newname.c_str(), cnt, suffix.c_str());
+
+ if (access (buf, F_OK) != 0) {
+ path = buf;
+ break;
+ }
+ path = "";
+ }
+
+ if (path == "") {
+ error << "FATAL ERROR! Could not find a " << endl;
+ }
+
+ }
+
+ return path;
+}
+
+string
+Session::midi_path_from_name (string name)
+{
+ string spath;
+ uint32_t cnt;
+ char buf[PATH_MAX+1];
+ const uint32_t limit = 10000;
+ string legalized;
+
+ buf[0] = '\0';
+ legalized = legalize_for_path (name);
+
+ /* find a "version" of the file name that doesn't exist in
+ any of the possible directories.
+ */
+
+ for (cnt = 1; cnt <= limit; ++cnt) {
+
+ vector<space_and_path>::iterator i;
+ uint32_t existing = 0;
+
+ for (i = session_dirs.begin(); i != session_dirs.end(); ++i) {
+
+ spath = (*i).path;
+
+ // FIXME: different directory from audio?
+ spath += sound_dir(false) + "/" + legalized;
+
+ snprintf (buf, sizeof(buf), "%s-%u.mid", spath.c_str(), cnt);
+
+ if (g_file_test (buf, G_FILE_TEST_EXISTS)) {
+ existing++;
+ }
+ }
+
+ if (existing == 0) {
+ break;
+ }
+
+ if (cnt > limit) {
+ error << string_compose(_("There are already %1 recordings for %2, which I consider too many."), limit, name) << endmsg;
+ throw failed_constructor();
+ }
+ }
+
+ /* we now have a unique name for the file, but figure out where to
+ actually put it.
+ */
+
+ string foo = buf;
+
+ // FIXME: different directory than audio?
+ spath = discover_best_sound_dir ();
+ spath += '/';
+
+ string::size_type pos = foo.find_last_of ('/');
+
+ if (pos == string::npos) {
+ spath += foo;
+ } else {
+ spath += foo.substr (pos + 1);
+ }
+
+ return spath;
+}
+
+boost::shared_ptr<MidiSource>
+Session::create_midi_source_for_session (MidiDiskstream& ds)
+{
+ string spath = midi_path_from_name (ds.name());
+
+ return boost::dynamic_pointer_cast<SMFSource> (SourceFactory::createWritable (DataType::MIDI, *this, spath, false, frame_rate()));
+}
+
+
/* Playlist management */
boost::shared_ptr<Playlist>
{
PathScanner scanner;
- vector<string *>* possible_audiofiles = scanner (sound_dir(), "\\.(wav|aiff|caf|w64)$", false, true);
+ vector<string *>* possible_audiofiles = scanner (sound_dir(), "\\.(wav|aiff|caf|w64|L|R)$", false, true);
- Glib::Mutex::Lock lm (audio_source_lock);
+ Glib::Mutex::Lock lm (source_lock);
regex_t compiled_tape_track_pattern;
int err;
if (_state_of_the_state & InitialConnecting) {
return;
}
-
+
/* every track/bus asked for this to be handled but it was deferred because
we were connecting. do it now.
*/
shared_ptr<RouteList> r = routes.reader ();
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
- AudioTrack* at;
+ Track* at;
- if ((at = dynamic_cast<AudioTrack*>((*i).get())) != 0) {
+ if ((at = dynamic_cast<Track*>((*i).get())) != 0) {
at->set_record_enable (yn, this);
}
}
set_dirty ();
}
+/** Ensures that all buffers (scratch, send, silent, etc) are allocated for
+ * the given count with the current block size.
+ */
void
-Session::ensure_passthru_buffers (uint32_t howmany)
+Session::ensure_buffers (ChanCount howmany)
{
- while (howmany > _passthru_buffers.size()) {
- Sample *p;
-#ifdef NO_POSIX_MEMALIGN
- p = (Sample *) malloc(current_block_size * sizeof(Sample));
-#else
- posix_memalign((void **)&p,16,current_block_size * 4);
-#endif
- _passthru_buffers.push_back (p);
-
- *p = 0;
-
-#ifdef NO_POSIX_MEMALIGN
- p = (Sample *) malloc(current_block_size * sizeof(Sample));
-#else
- posix_memalign((void **)&p,16,current_block_size * 4);
-#endif
- memset (p, 0, sizeof (Sample) * current_block_size);
- _silent_buffers.push_back (p);
-
- *p = 0;
-
-#ifdef NO_POSIX_MEMALIGN
- p = (Sample *) malloc(current_block_size * sizeof(Sample));
-#else
- posix_memalign((void **)&p,16,current_block_size * 4);
-#endif
- memset (p, 0, sizeof (Sample) * current_block_size);
- _send_buffers.push_back (p);
-
- }
- allocate_pan_automation_buffers (current_block_size, howmany, false);
+ // FIXME: NASTY assumption (midi block size == audio block size)
+ _scratch_buffers->ensure_buffers(howmany, current_block_size);
+ _send_buffers->ensure_buffers(howmany, current_block_size);
+ _silent_buffers->ensure_buffers(howmany, current_block_size);
+
+ allocate_pan_automation_buffers (current_block_size, howmany.n_audio(), false);
}
uint32_t
for (boost::dynamic_bitset<uint32_t>::size_type n = 0; n < insert_bitset.size(); ++n) {
if (!insert_bitset[n]) {
insert_bitset[n] = true;
- cerr << "Returning " << n << " as insert ID\n";
return n;
}
for (boost::dynamic_bitset<uint32_t>::size_type n = 0; n < send_bitset.size(); ++n) {
if (!send_bitset[n]) {
send_bitset[n] = true;
- cerr << "Returning " << n << " as send ID\n";
return n;
}
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
- AudioTrack *at;
+ Track *at;
- if ((at = dynamic_cast<AudioTrack*>((*i).get())) != 0) {
+ if ((at = dynamic_cast<Track*>((*i).get())) != 0) {
/* XXX this is wrong because itt.progress will keep returning to zero at the start
of every track.
*/
int
Session::write_one_audio_track (AudioTrack& track, nframes_t start, nframes_t len,
- bool overwrite, vector<boost::shared_ptr<AudioSource> >& srcs, InterThreadInfo& itt)
+ bool overwrite, vector<boost::shared_ptr<Source> >& srcs, InterThreadInfo& itt)
{
int ret = -1;
boost::shared_ptr<Playlist> playlist;
uint32_t x;
char buf[PATH_MAX+1];
string dir;
- uint32_t nchans;
+ ChanCount nchans(track.audio_diskstream()->n_channels());
nframes_t position;
nframes_t this_chunk;
nframes_t to_do;
- vector<Sample*> buffers;
+ BufferSet buffers;
// any bigger than this seems to cause stack overflows in called functions
const nframes_t chunk_size = (128 * 1024)/4;
if (track.has_external_redirects()) {
goto out;
}
-
- nchans = track.audio_diskstream()->n_channels();
dir = discover_best_sound_dir ();
- for (uint32_t chan_n=0; chan_n < nchans; ++chan_n) {
+ for (uint32_t chan_n=0; chan_n < nchans.n_audio(); ++chan_n) {
for (x = 0; x < 99999; ++x) {
snprintf (buf, sizeof(buf), "%s/%s-%d-bounce-%" PRIu32 ".wav", dir.c_str(), playlist->name().c_str(), chan_n, x+1);
}
try {
- fsource = boost::dynamic_pointer_cast<AudioFileSource> (SourceFactory::createWritable (*this, buf, false, frame_rate()));
+ fsource = boost::dynamic_pointer_cast<AudioFileSource> (
+ SourceFactory::createWritable (DataType::AUDIO, *this, buf, false, frame_rate()));
}
catch (failed_constructor& err) {
to_do = len;
/* create a set of reasonably-sized buffers */
+ buffers.ensure_buffers(nchans, chunk_size);
+ buffers.set_count(nchans);
- for (vector<Sample*>::iterator i = _passthru_buffers.begin(); i != _passthru_buffers.end(); ++i) {
- Sample* b;
-#ifdef NO_POSIX_MEMALIGN
- b = (Sample *) malloc(chunk_size * sizeof(Sample));
-#else
- posix_memalign((void **)&b,16,chunk_size * 4);
-#endif
- buffers.push_back (b);
+ for (vector<boost::shared_ptr<Source> >::iterator src=srcs.begin(); src != srcs.end(); ++src) {
+ boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource>(*src);
+ if (afs)
+ afs->prepare_for_peakfile_writes ();
}
-
+
while (to_do && !itt.cancel) {
this_chunk = min (to_do, chunk_size);
- if (track.export_stuff (buffers, nchans, start, this_chunk)) {
+ if (track.export_stuff (buffers, start, this_chunk)) {
goto out;
}
uint32_t n = 0;
- for (vector<boost::shared_ptr<AudioSource> >::iterator src=srcs.begin(); src != srcs.end(); ++src, ++n) {
+ for (vector<boost::shared_ptr<Source> >::iterator src=srcs.begin(); src != srcs.end(); ++src, ++n) {
boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource>(*src);
if (afs) {
- if (afs->write (buffers[n], this_chunk) != this_chunk) {
+ if (afs->write (buffers.get_audio(n).data(), this_chunk) != this_chunk) {
goto out;
}
}
time (&now);
xnow = localtime (&now);
- for (vector<boost::shared_ptr<AudioSource> >::iterator src=srcs.begin(); src != srcs.end(); ++src) {
+ for (vector<boost::shared_ptr<Source> >::iterator src=srcs.begin(); src != srcs.end(); ++src) {
boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource>(*src);
-
+
if (afs) {
afs->update_header (position, *xnow, now);
afs->flush_header ();
}
}
- /* build peakfile for new source */
-
- for (vector<boost::shared_ptr<AudioSource> >::iterator src=srcs.begin(); src != srcs.end(); ++src) {
- boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource>(*src);
- if (afs) {
- afs->build_peaks ();
- }
- }
-
/* construct a region to represent the bounced material */
boost::shared_ptr<Region> aregion = RegionFactory::create (srcs, 0, srcs.front()->length(),
out:
if (ret) {
- for (vector<boost::shared_ptr<AudioSource> >::iterator src = srcs.begin(); src != srcs.end(); ++src) {
+ for (vector<boost::shared_ptr<Source> >::iterator src = srcs.begin(); src != srcs.end(); ++src) {
boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource>(*src);
if (afs) {
(*src)->drop_references ();
}
- }
- for (vector<Sample*>::iterator i = buffers.begin(); i != buffers.end(); ++i) {
- free(*i);
+ } else {
+ for (vector<boost::shared_ptr<Source> >::iterator src = srcs.begin(); src != srcs.end(); ++src) {
+ boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource>(*src);
+
+ if (afs)
+ afs->done_with_peakfile_writes ();
+ }
}
g_atomic_int_set (&processing_prohibited, 0);
- itt.done = true;
-
return ret;
}
-vector<Sample*>&
-Session::get_silent_buffers (uint32_t howmany)
+BufferSet&
+Session::get_silent_buffers (ChanCount count)
{
- for (uint32_t i = 0; i < howmany; ++i) {
- memset (_silent_buffers[i], 0, sizeof (Sample) * current_block_size);
+ assert(_silent_buffers->available() >= count);
+ _silent_buffers->set_count(count);
+
+ for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
+ for (size_t i=0; i < count.get(*t); ++i) {
+ _silent_buffers->get(*t, i).clear();
+ }
}
- return _silent_buffers;
+
+ return *_silent_buffers;
+}
+
+BufferSet&
+Session::get_scratch_buffers (ChanCount count)
+{
+ assert(_scratch_buffers->available() >= count);
+ _scratch_buffers->set_count(count);
+ return *_scratch_buffers;
+}
+
+BufferSet&
+Session::get_send_buffers (ChanCount count)
+{
+ assert(_send_buffers->available() >= count);
+ _send_buffers->set_count(count);
+ return *_send_buffers;
}
uint32_t
shared_ptr<RouteList> r = routes.reader ();
for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) {
- if (dynamic_cast<AudioTrack*> ((*i).get())) {
+ if (dynamic_cast<Track*> ((*i).get())) {
++n;
}
}
shared_ptr<RouteList> r = routes.reader ();
for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) {
- if (dynamic_cast<AudioTrack*> ((*i).get()) == 0) {
+ if (dynamic_cast<Track*> ((*i).get()) == 0) {
++n;
}
}