#include <sys/mman.h>
#include <pbd/error.h>
-#include <pbd/basename.h>
#include <glibmm/thread.h>
#include <pbd/xml++.h>
#include <pbd/memento_command.h>
#include <ardour/ardour.h>
#include <ardour/audioengine.h>
+#include <ardour/analyser.h>
#include <ardour/audio_diskstream.h>
#include <ardour/utils.h>
#include <ardour/configuration.h>
#include <ardour/playlist_factory.h>
#include <ardour/cycle_timer.h>
#include <ardour/audioregion.h>
+#include <ardour/audio_port.h>
#include <ardour/source_factory.h>
#include "i18n.h"
allocate_temporary_buffers ();
add_channel (1);
- assert(_n_channels == 1);
+ assert(_n_channels == ChanCount(DataType::AUDIO, 1));
}
AudioDiskstream::~AudioDiskstream ()
RCUWriter<ChannelList> writer (channels);
boost::shared_ptr<ChannelList> c = writer.get_copy();
- _n_channels = c->size();
+ _n_channels.set(DataType::AUDIO, c->size());
- if (_io->n_inputs() > _n_channels) {
- add_channel_to (c, _io->n_inputs() - _n_channels);
- } else if (_io->n_inputs() < _n_channels) {
- remove_channel_from (c, _n_channels - _io->n_inputs());
+ if (_io->n_inputs().n_audio() > _n_channels.n_audio()) {
+ add_channel_to (c, _io->n_inputs().n_audio() - _n_channels.n_audio());
+ } else if (_io->n_inputs().n_audio() < _n_channels.n_audio()) {
+ remove_channel_from (c, _n_channels.n_audio() - _io->n_inputs().n_audio());
}
}
uint32_t n;
ChannelList::iterator chan;
- uint32_t ni = _io->n_inputs();
+ uint32_t ni = _io->n_inputs().n_audio();
+ vector<string> connections;
for (n = 0, chan = c->begin(); chan != c->end() && n < ni; ++chan, ++n) {
- const char **connections = _io->input(n)->get_connections ();
+ connections.clear ();
+
+ if (_io->input(n)->get_connections (connections) == 0) {
- if (connections == 0 || connections[0] == 0) {
-
if ((*chan)->source) {
// _source->disable_metering ();
}
(*chan)->source = 0;
} else {
- (*chan)->source = _session.engine().get_port_by_name (connections[0]);
- }
-
- if (connections) {
- free (connections);
+ (*chan)->source = dynamic_cast<AudioPort*>(_session.engine().get_port_by_name (connections[0]) );
}
}
}
boost::shared_ptr<AudioPlaylist> playlist;
if ((playlist = boost::dynamic_pointer_cast<AudioPlaylist> (_session.playlist_by_name (name))) == 0) {
- playlist = boost::dynamic_pointer_cast<AudioPlaylist> (PlaylistFactory::create (_session, name));
+ playlist = boost::dynamic_pointer_cast<AudioPlaylist> (PlaylistFactory::create (DataType::AUDIO, _session, name));
}
if (!playlist) {
{
string newname;
boost::shared_ptr<AudioPlaylist> playlist;
-
+
if (!in_set_state && destructive()) {
return 0;
}
newname = Playlist::bump_name (_name, _session);
}
- if ((playlist = boost::dynamic_pointer_cast<AudioPlaylist> (PlaylistFactory::create (_session, newname, hidden()))) != 0) {
+ if ((playlist = boost::dynamic_pointer_cast<AudioPlaylist> (PlaylistFactory::create (DataType::AUDIO, _session, newname, hidden()))) != 0) {
playlist->set_orig_diskstream_id (id());
return use_playlist (playlist);
/* if per-track or global rec-enable turned on while the other was already on, we've started recording */
- if ((change & track_rec_enabled) && record_enabled() && (!(change & global_rec_enabled) && can_record) ||
- ((change & global_rec_enabled) && can_record && (!(change & track_rec_enabled) && record_enabled()))) {
+ if (((change & track_rec_enabled) && record_enabled() && (!(change & global_rec_enabled) && can_record)) ||
+ (((change & global_rec_enabled) && can_record && (!(change & track_rec_enabled) && record_enabled())))) {
/* starting to record: compute first+last frames */
}
- if (_flags & Recordable) {
+ if (recordable() && destructive()) {
boost::shared_ptr<ChannelList> c = channels.reader();
for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
commit_should_unlock = false;
+ if (!_io->active()) {
+ _processed = true;
+ return 0;
+ }
+
check_record_status (transport_frame, nframes, can_record);
nominally_recording = (can_record && re);
for (chan = c->begin(); chan != c->end(); ++chan) {
(*chan)->current_capture_buffer = 0;
- (*chan)->current_playback_buffer = 0;
+ (*chan)->current_playback_buffer = 0;
}
if (nominally_recording || (_session.get_record_enabled() && Config->get_punch_in())) {
OverlapType ot;
+ // Safeguard against situations where process() goes haywire when autopunching and last_recordable_frame < first_recordable_frame
+ if (last_recordable_frame < first_recordable_frame) {
+ last_recordable_frame = max_frames;
+ }
+
ot = coverage (first_recordable_frame, last_recordable_frame, transport_frame, transport_frame + nframes);
switch (ot) {
/* |--------| recrange
-------------- transrange
*/
- rec_nframes = last_recordable_frame - last_recordable_frame;
+ rec_nframes = last_recordable_frame - first_recordable_frame;
rec_offset = first_recordable_frame - transport_frame;
break;
}
if (nominally_recording || rec_nframes) {
- uint32_t limit = _io->n_inputs ();
+ uint32_t limit = _io->n_inputs ().n_audio();
/* one or more ports could already have been removed from _io, but our
channel setup hasn't yet been updated. prevent us from trying to
rec_offset
*/
- memcpy (chaninfo->current_capture_buffer, _io->input(n)->get_buffer (rec_nframes) + offset + rec_offset, sizeof (Sample) * rec_nframes);
+ AudioPort* const ap = _io->audio_input(n);
+ assert(ap);
+ assert(rec_nframes <= ap->get_audio_buffer().capacity());
+ memcpy (chaninfo->current_capture_buffer, ap->get_audio_buffer().data(rec_nframes, offset + rec_offset), sizeof (Sample) * rec_nframes);
} else {
goto out;
}
- Sample* buf = _io->input (n)->get_buffer (nframes) + offset;
+ AudioPort* const ap = _io->audio_input(n);
+ assert(ap);
+
+ Sample* buf = ap->get_audio_buffer().data(nframes, offset);
nframes_t first = chaninfo->capture_vector.len[0];
memcpy (chaninfo->capture_wrap_buffer, buf, sizeof (Sample) * first);
nframes_t total = chaninfo->playback_vector.len[0] + chaninfo->playback_vector.len[1];
if (necessary_samples > total) {
+ cerr << "underrun for " << _name << endl;
DiskUnderrun ();
goto out;
if (rec_nframes == 0 && _actual_speed != 1.0f && _actual_speed != -1.0f) {
uint64_t phase = last_phase;
+ int64_t phi_delta;
nframes_t i = 0;
// Linearly interpolate into the alt buffer
// using 40.24 fixp maths (swh)
+ if (phi != target_phi) {
+ phi_delta = ((int64_t)(target_phi - phi)) / nframes;
+ } else {
+ phi_delta = 0;
+ }
+
for (chan = c->begin(); chan != c->end(); ++chan) {
float fr;
chaninfo->speed_buffer[outsample] =
chaninfo->current_playback_buffer[i] * (1.0f - fr) +
chaninfo->current_playback_buffer[i+1] * fr;
- phase += phi;
+ phase += phi + phi_delta;
}
chaninfo->current_playback_buffer = chaninfo->speed_buffer;
}
- playback_distance = i + 1;
+ playback_distance = i; // + 1;
last_phase = (phase & 0xFFFFFF);
} else {
playback_distance = nframes;
}
+ phi = target_phi;
+
}
ret = 0;
{
bool need_butler = false;
+ if (!_io->active()) {
+ return false;
+ }
+
if (_actual_speed < 0.0) {
playback_sample -= playback_distance;
} else {
}
if (_slaved) {
- need_butler = c->front()->playback_buf->write_space() >= c->front()->playback_buf->bufsize() / 2;
+ /*if (_io && _io->active()) {*/
+ need_butler = c->front()->playback_buf->write_space() >= c->front()->playback_buf->bufsize() / 2;
+ /*} else {
+ need_butler = false;
+ }*/
} else {
- need_butler = c->front()->playback_buf->write_space() >= disk_io_chunk_frames
- || c->front()->capture_buf->read_space() >= disk_io_chunk_frames;
+ /*if (_io && _io->active()) {*/
+ need_butler = c->front()->playback_buf->write_space() >= disk_io_chunk_frames
+ || c->front()->capture_buf->read_space() >= disk_io_chunk_frames;
+ /*} else {
+ need_butler = c->front()->capture_buf->read_space() >= disk_io_chunk_frames;
+ }*/
}
if (commit_should_unlock) {
AudioDiskstream::seek (nframes_t frame, bool complete_refill)
{
uint32_t n;
- int ret;
+ int ret = -1;
ChannelList::iterator chan;
boost::shared_ptr<ChannelList> c = channels.reader();
s->update_header (capture_info.front()->start, when, twhen);
s->set_captured_for (_name);
s->mark_immutable ();
+ if (Config->get_auto_analyse_audio()) {
+ Analyser::queue_source_for_analysis (s, true);
+ }
}
}
// cerr << _name << ": there are " << capture_info.size() << " capture_info records\n";
- XMLNode &before = _playlist->get_state();
+ XMLNode &before = _playlist->get_state();
_playlist->freeze ();
for (buffer_position = c->front()->write_source->last_capture_start_frame(), ci = capture_info.begin(); ci != capture_info.end(); ++ci) {
}
_playlist->thaw ();
- XMLNode &after = _playlist->get_state();
+ XMLNode &after = _playlist->get_state();
_session.add_command (new MementoCommand<Playlist>(*_playlist, &before, &after));
}
capture_start_frame = 0;
}
+void
+AudioDiskstream::transport_looped (nframes_t transport_frame)
+{
+ if (was_recording) {
+ // all we need to do is finish this capture, with modified capture length
+ boost::shared_ptr<ChannelList> c = channels.reader();
+
+ // adjust the capture length knowing that the data will be recorded to disk
+ // only necessary after the first loop where we're recording
+ if (capture_info.size() == 0) {
+ capture_captured += _capture_offset;
+
+ if (_alignment_style == ExistingMaterial) {
+ capture_captured += _session.worst_output_latency();
+ } else {
+ capture_captured += _roll_delay;
+ }
+ }
+
+ finish_capture (true, c);
+
+ // the next region will start recording via the normal mechanism
+ // we'll set the start position to the current transport pos
+ // no latency adjustment or capture offset needs to be made, as that already happened the first time
+ capture_start_frame = transport_frame;
+ first_recordable_frame = transport_frame; // mild lie
+ last_recordable_frame = max_frames;
+ was_recording = true;
+
+ if (recordable() && destructive()) {
+ for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
+
+ RingBufferNPT<CaptureTransition>::rw_vector transvec;
+ (*chan)->capture_transition_buf->get_write_vector(&transvec);
+
+ if (transvec.len[0] > 0) {
+ transvec.buf[0]->type = CaptureStart;
+ transvec.buf[0]->capture_val = capture_start_frame;
+ (*chan)->capture_transition_buf->increment_write_ptr(1);
+ }
+ else {
+ // bad!
+ fatal << X_("programming error: capture_transition_buf is full on rec loop! inconceivable!")
+ << endmsg;
+ }
+ }
+ }
+
+ }
+}
+
void
AudioDiskstream::finish_capture (bool rec_monitors_input, boost::shared_ptr<ChannelList> c)
{
RingBufferNPT<CaptureTransition>::rw_vector transvec;
(*chan)->capture_transition_buf->get_write_vector(&transvec);
-
if (transvec.len[0] > 0) {
transvec.buf[0]->type = CaptureEnd;
transvec.buf[0]->capture_val = capture_captured;
capture_info.push_back (ci);
capture_captured = 0;
+
+ /* now we've finished a capture, reset first_recordable_frame for next time */
+ first_recordable_frame = max_frames;
}
void
AudioDiskstream::set_record_enabled (bool yn)
{
- if (!recordable() || !_session.record_enabling_legal() || _io->n_inputs() == 0) {
+ if (!recordable() || !_session.record_enabling_legal() || _io->n_inputs().n_audio() == 0) {
return;
}
(*chan)->source->ensure_monitor_input (!(Config->get_auto_input() && rolling));
}
capturing_sources.push_back ((*chan)->write_source);
+ (*chan)->write_source->mark_streaming_write_started ();
}
} else {
for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
capturing_sources.push_back ((*chan)->write_source);
+ (*chan)->write_source->mark_streaming_write_started ();
}
}
{
g_atomic_int_set (&_record_enabled, 0);
boost::shared_ptr<ChannelList> c = channels.reader();
- for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
- if (Config->get_monitoring_model() == HardwareMonitoring) {
+ if (Config->get_monitoring_model() == HardwareMonitoring) {
+ for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
if ((*chan)->source) {
(*chan)->source->ensure_monitor_input (false);
}
// create necessary extra channels
// we are always constructed with one and we always need one
- _n_channels = channels.reader()->size();
+ _n_channels.set(DataType::AUDIO, channels.reader()->size());
- if (nchans > _n_channels) {
+ if (nchans > _n_channels.n_audio()) {
- add_channel (nchans - _n_channels);
+ add_channel (nchans - _n_channels.n_audio());
+ IO::PortCountChanged(_n_channels);
- } else if (nchans < _n_channels) {
+ } else if (nchans < _n_channels.n_audio()) {
- remove_channel (_n_channels - nchans);
+ remove_channel (_n_channels.n_audio() - nchans);
}
if ((prop = node.property ("playlist")) == 0) {
up of the IO that owns this DS (::non_realtime_input_change())
*/
- in_set_state = false;
-
return 0;
}
for (chan = c->begin(), n = 0; chan != c->end(); ++chan, ++n) {
if ((*chan)->write_source != 0) {
- (*chan)->write_source->set_name (_name, destructive());
+ (*chan)->write_source->set_source_name (_name, destructive());
/* XXX what to do if one of them fails ? */
}
}
c->push_back (new ChannelInfo(_session.diskstream_buffer_size(), speed_buffer_size, wrap_buffer_size));
}
- _n_channels = c->size();
+ _n_channels.set(DataType::AUDIO, c->size());
return 0;
}
int
AudioDiskstream::remove_channel_from (boost::shared_ptr<ChannelList> c, uint32_t how_many)
{
- while (--how_many && !c->empty()) {
+ while (how_many-- && !c->empty()) {
delete c->back();
c->pop_back();
}
- _n_channels = c->size();
+ _n_channels.set(DataType::AUDIO, c->size());
return 0;
}
continue;
}
+ // This protects sessions from errant CapturingSources in stored sessions
+ struct stat sbuf;
+ if (stat (prop->value().c_str(), &sbuf)) {
+ continue;
+ }
+
try {
- fs = boost::dynamic_pointer_cast<AudioFileSource> (SourceFactory::createWritable (_session, prop->value(), false, _session.frame_rate()));
+ fs = boost::dynamic_pointer_cast<AudioFileSource> (
+ SourceFactory::createWritable (DataType::AUDIO, _session, prop->value(), false, _session.frame_rate()));
}
catch (failed_constructor& err) {
return 1;
}
- if (pending_sources.size() != _n_channels) {
+ if (pending_sources.size() != _n_channels.n_audio()) {
error << string_compose (_("%1: incorrect number of pending sources listed - ignoring them all"), _name)
<< endmsg;
return -1;
playback_buf = new RingBufferNPT<Sample> (bufsize);
capture_buf = new RingBufferNPT<Sample> (bufsize);
- capture_transition_buf = new RingBufferNPT<CaptureTransition> (128);
+ capture_transition_buf = new RingBufferNPT<CaptureTransition> (256);
/* touch the ringbuffer buffers, which will cause
them to be mapped into locked physical RAM if