2008-06-02 21:41:35 +00:00
|
|
|
/*
|
2009-10-14 16:10:01 +00:00
|
|
|
Copyright (C) 2000-2006 Paul Davis
|
2008-06-02 21:41:35 +00:00
|
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
|
along with this program; if not, write to the Free Software
|
|
|
|
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <fstream>
|
|
|
|
|
#include <cstdio>
|
|
|
|
|
#include <unistd.h>
|
|
|
|
|
#include <cmath>
|
|
|
|
|
#include <cerrno>
|
|
|
|
|
#include <cassert>
|
|
|
|
|
#include <string>
|
|
|
|
|
#include <climits>
|
|
|
|
|
#include <fcntl.h>
|
|
|
|
|
#include <cstdlib>
|
|
|
|
|
#include <ctime>
|
|
|
|
|
|
2009-02-25 18:26:51 +00:00
|
|
|
#include "pbd/error.h"
|
|
|
|
|
#include "pbd/xml++.h"
|
|
|
|
|
#include "pbd/memento_command.h"
|
|
|
|
|
#include "pbd/enumwriter.h"
|
2010-03-02 18:05:26 +00:00
|
|
|
#include "pbd/stateful_diff_command.h"
|
2008-06-02 21:41:35 +00:00
|
|
|
|
2009-02-25 18:26:51 +00:00
|
|
|
#include "ardour/analyser.h"
|
2009-10-24 00:39:28 +00:00
|
|
|
#include "ardour/audio_buffer.h"
|
2009-02-25 18:26:51 +00:00
|
|
|
#include "ardour/audio_diskstream.h"
|
2009-10-24 00:39:28 +00:00
|
|
|
#include "ardour/audio_port.h"
|
|
|
|
|
#include "ardour/audioengine.h"
|
2009-02-25 18:26:51 +00:00
|
|
|
#include "ardour/audiofilesource.h"
|
|
|
|
|
#include "ardour/audioplaylist.h"
|
|
|
|
|
#include "ardour/audioregion.h"
|
2009-10-24 00:39:28 +00:00
|
|
|
#include "ardour/butler.h"
|
2009-11-28 00:49:04 +00:00
|
|
|
#include "ardour/debug.h"
|
2009-02-25 18:26:51 +00:00
|
|
|
#include "ardour/io.h"
|
2009-10-24 00:39:28 +00:00
|
|
|
#include "ardour/playlist_factory.h"
|
|
|
|
|
#include "ardour/region_factory.h"
|
|
|
|
|
#include "ardour/session.h"
|
2012-05-24 06:09:29 +00:00
|
|
|
#include "ardour/session_playlists.h"
|
2009-10-24 00:39:28 +00:00
|
|
|
#include "ardour/source_factory.h"
|
2011-10-20 19:20:44 +00:00
|
|
|
#include "ardour/track.h"
|
2012-05-24 06:09:29 +00:00
|
|
|
#include "ardour/types.h"
|
2009-10-24 00:39:28 +00:00
|
|
|
#include "ardour/utils.h"
|
2008-06-02 21:41:35 +00:00
|
|
|
|
|
|
|
|
#include "i18n.h"
|
|
|
|
|
#include <locale.h>
|
|
|
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
|
using namespace ARDOUR;
|
|
|
|
|
using namespace PBD;
|
|
|
|
|
|
|
|
|
|
size_t AudioDiskstream::_working_buffers_size = 0;
|
|
|
|
|
Sample* AudioDiskstream::_mixdown_buffer = 0;
|
|
|
|
|
gain_t* AudioDiskstream::_gain_buffer = 0;
|
|
|
|
|
|
|
|
|
|
AudioDiskstream::AudioDiskstream (Session &sess, const string &name, Diskstream::Flag flag)
|
|
|
|
|
: Diskstream(sess, name, flag)
|
|
|
|
|
, channels (new ChannelList)
|
|
|
|
|
{
|
|
|
|
|
/* prevent any write sources from being created */
|
|
|
|
|
|
|
|
|
|
in_set_state = true;
|
|
|
|
|
use_new_playlist ();
|
|
|
|
|
in_set_state = false;
|
|
|
|
|
}
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
AudioDiskstream::AudioDiskstream (Session& sess, const XMLNode& node)
|
|
|
|
|
: Diskstream(sess, node)
|
|
|
|
|
, channels (new ChannelList)
|
|
|
|
|
{
|
|
|
|
|
in_set_state = true;
|
2010-04-01 01:24:13 +00:00
|
|
|
init ();
|
2008-06-02 21:41:35 +00:00
|
|
|
|
2009-10-15 18:56:11 +00:00
|
|
|
if (set_state (node, Stateful::loading_state_version)) {
|
2008-06-02 21:41:35 +00:00
|
|
|
in_set_state = false;
|
|
|
|
|
throw failed_constructor();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
in_set_state = false;
|
|
|
|
|
|
|
|
|
|
if (destructive()) {
|
|
|
|
|
use_destructive_playlist ();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2010-04-01 01:24:13 +00:00
|
|
|
AudioDiskstream::init ()
|
2008-06-02 21:41:35 +00:00
|
|
|
{
|
|
|
|
|
/* there are no channels at this point, so these
|
|
|
|
|
two calls just get speed_buffer_size and wrap_buffer
|
|
|
|
|
size setup without duplicating their code.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
set_block_size (_session.get_block_size());
|
|
|
|
|
allocate_temporary_buffers ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AudioDiskstream::~AudioDiskstream ()
|
|
|
|
|
{
|
2009-11-28 00:49:04 +00:00
|
|
|
DEBUG_TRACE (DEBUG::Destruction, string_compose ("Audio Diskstream %1 destructor\n", _name));
|
2009-11-25 14:37:20 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
{
|
|
|
|
|
RCUWriter<ChannelList> writer (channels);
|
|
|
|
|
boost::shared_ptr<ChannelList> c = writer.get_copy();
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
|
|
|
|
|
delete *chan;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
c->clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
channels.flush ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
AudioDiskstream::allocate_working_buffers()
|
|
|
|
|
{
|
|
|
|
|
assert(disk_io_frames() > 0);
|
|
|
|
|
|
|
|
|
|
_working_buffers_size = disk_io_frames();
|
|
|
|
|
_mixdown_buffer = new Sample[_working_buffers_size];
|
|
|
|
|
_gain_buffer = new gain_t[_working_buffers_size];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
AudioDiskstream::free_working_buffers()
|
|
|
|
|
{
|
|
|
|
|
delete [] _mixdown_buffer;
|
|
|
|
|
delete [] _gain_buffer;
|
|
|
|
|
_working_buffers_size = 0;
|
|
|
|
|
_mixdown_buffer = 0;
|
|
|
|
|
_gain_buffer = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
AudioDiskstream::non_realtime_input_change ()
|
|
|
|
|
{
|
2014-07-09 10:09:14 -04:00
|
|
|
bool need_new_write_sources = false;
|
|
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
{
|
2012-07-25 17:48:55 +00:00
|
|
|
Glib::Threads::Mutex::Lock lm (state_lock);
|
2008-06-02 21:41:35 +00:00
|
|
|
|
2010-08-30 22:34:21 +00:00
|
|
|
if (input_change_pending.type == IOChange::NoChange) {
|
2008-06-02 21:41:35 +00:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2011-04-04 23:00:48 +00:00
|
|
|
if (input_change_pending.type == IOChange::ConfigurationChanged) {
|
2008-06-02 21:41:35 +00:00
|
|
|
RCUWriter<ChannelList> writer (channels);
|
|
|
|
|
boost::shared_ptr<ChannelList> c = writer.get_copy();
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
_n_channels.set(DataType::AUDIO, c->size());
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
if (_io->n_ports().n_audio() > _n_channels.n_audio()) {
|
|
|
|
|
add_channel_to (c, _io->n_ports().n_audio() - _n_channels.n_audio());
|
|
|
|
|
} else if (_io->n_ports().n_audio() < _n_channels.n_audio()) {
|
|
|
|
|
remove_channel_from (c, _n_channels.n_audio() - _io->n_ports().n_audio());
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
2014-07-09 10:09:14 -04:00
|
|
|
|
|
|
|
|
need_new_write_sources = true;
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
2011-03-14 21:53:10 +00:00
|
|
|
|
2011-04-04 23:00:48 +00:00
|
|
|
if (input_change_pending.type & IOChange::ConnectionsChanged) {
|
|
|
|
|
get_input_sources ();
|
|
|
|
|
set_capture_offset ();
|
|
|
|
|
set_align_style_from_io ();
|
|
|
|
|
}
|
2011-03-14 21:53:10 +00:00
|
|
|
|
2010-08-30 22:34:21 +00:00
|
|
|
input_change_pending = IOChange::NoChange;
|
2008-06-02 21:41:35 +00:00
|
|
|
|
|
|
|
|
/* implicit unlock */
|
|
|
|
|
}
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2014-07-09 10:09:14 -04:00
|
|
|
if (need_new_write_sources) {
|
|
|
|
|
reset_write_sources (false);
|
|
|
|
|
}
|
2008-06-02 21:41:35 +00:00
|
|
|
|
|
|
|
|
/* now refill channel buffers */
|
|
|
|
|
|
|
|
|
|
if (speed() != 1.0f || speed() != -1.0f) {
|
2010-09-17 16:24:22 +00:00
|
|
|
seek ((framepos_t) (_session.transport_frame() * (double) speed()));
|
2008-06-02 21:41:35 +00:00
|
|
|
} else {
|
|
|
|
|
seek (_session.transport_frame());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2008-10-07 17:33:05 +00:00
|
|
|
void
|
2010-09-17 16:24:22 +00:00
|
|
|
AudioDiskstream::non_realtime_locate (framepos_t location)
|
2008-10-07 17:33:05 +00:00
|
|
|
{
|
|
|
|
|
/* now refill channel buffers */
|
|
|
|
|
|
|
|
|
|
if (speed() != 1.0f || speed() != -1.0f) {
|
2010-09-17 16:24:22 +00:00
|
|
|
seek ((framepos_t) (location * (double) speed()));
|
2008-10-07 17:33:05 +00:00
|
|
|
} else {
|
|
|
|
|
seek (location);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
void
|
|
|
|
|
AudioDiskstream::get_input_sources ()
|
|
|
|
|
{
|
|
|
|
|
boost::shared_ptr<ChannelList> c = channels.reader();
|
|
|
|
|
|
|
|
|
|
uint32_t n;
|
|
|
|
|
ChannelList::iterator chan;
|
2009-06-09 20:21:19 +00:00
|
|
|
uint32_t ni = _io->n_ports().n_audio();
|
2008-06-02 21:41:35 +00:00
|
|
|
vector<string> connections;
|
|
|
|
|
|
|
|
|
|
for (n = 0, chan = c->begin(); chan != c->end() && n < ni; ++chan, ++n) {
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
connections.clear ();
|
|
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
if (_io->nth (n)->get_connections (connections) == 0) {
|
2011-03-15 19:32:21 +00:00
|
|
|
if (!(*chan)->source.name.empty()) {
|
2008-06-02 21:41:35 +00:00
|
|
|
// _source->disable_metering ();
|
|
|
|
|
}
|
2011-03-15 19:32:21 +00:00
|
|
|
(*chan)->source.name = string();
|
2008-06-02 21:41:35 +00:00
|
|
|
} else {
|
2011-03-15 19:32:21 +00:00
|
|
|
(*chan)->source.name = connections[0];
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
|
|
|
|
}
|
2009-10-14 16:10:01 +00:00
|
|
|
}
|
2008-06-02 21:41:35 +00:00
|
|
|
|
|
|
|
|
int
|
|
|
|
|
AudioDiskstream::find_and_use_playlist (const string& name)
|
|
|
|
|
{
|
|
|
|
|
boost::shared_ptr<AudioPlaylist> playlist;
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2009-12-04 19:09:08 +00:00
|
|
|
if ((playlist = boost::dynamic_pointer_cast<AudioPlaylist> (_session.playlists->by_name (name))) == 0) {
|
2008-06-02 21:41:35 +00:00
|
|
|
playlist = boost::dynamic_pointer_cast<AudioPlaylist> (PlaylistFactory::create (DataType::AUDIO, _session, name));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!playlist) {
|
|
|
|
|
error << string_compose(_("AudioDiskstream: Playlist \"%1\" isn't an audio playlist"), name) << endmsg;
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return use_playlist (playlist);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
AudioDiskstream::use_playlist (boost::shared_ptr<Playlist> playlist)
|
|
|
|
|
{
|
|
|
|
|
assert(boost::dynamic_pointer_cast<AudioPlaylist>(playlist));
|
|
|
|
|
|
|
|
|
|
Diskstream::use_playlist(playlist);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
AudioDiskstream::use_new_playlist ()
|
|
|
|
|
{
|
|
|
|
|
string newname;
|
|
|
|
|
boost::shared_ptr<AudioPlaylist> playlist;
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
if (!in_set_state && destructive()) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (_playlist) {
|
|
|
|
|
newname = Playlist::bump_name (_playlist->name(), _session);
|
|
|
|
|
} else {
|
|
|
|
|
newname = Playlist::bump_name (_name, _session);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((playlist = boost::dynamic_pointer_cast<AudioPlaylist> (PlaylistFactory::create (DataType::AUDIO, _session, newname, hidden()))) != 0) {
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
return use_playlist (playlist);
|
|
|
|
|
|
2009-10-14 16:10:01 +00:00
|
|
|
} else {
|
2008-06-02 21:41:35 +00:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
AudioDiskstream::use_copy_playlist ()
|
|
|
|
|
{
|
|
|
|
|
assert(audio_playlist());
|
|
|
|
|
|
|
|
|
|
if (destructive()) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (_playlist == 0) {
|
|
|
|
|
error << string_compose(_("AudioDiskstream %1: there is no existing playlist to make a copy of!"), _name) << endmsg;
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string newname;
|
|
|
|
|
boost::shared_ptr<AudioPlaylist> playlist;
|
|
|
|
|
|
|
|
|
|
newname = Playlist::bump_name (_playlist->name(), _session);
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2010-04-21 20:42:22 +00:00
|
|
|
if ((playlist = boost::dynamic_pointer_cast<AudioPlaylist>(PlaylistFactory::create (audio_playlist(), newname))) != 0) {
|
2008-06-02 21:41:35 +00:00
|
|
|
return use_playlist (playlist);
|
2009-10-14 16:10:01 +00:00
|
|
|
} else {
|
2008-06-02 21:41:35 +00:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
AudioDiskstream::setup_destructive_playlist ()
|
|
|
|
|
{
|
|
|
|
|
SourceList srcs;
|
|
|
|
|
boost::shared_ptr<ChannelList> c = channels.reader();
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
|
|
|
|
|
srcs.push_back ((*chan)->write_source);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* a single full-sized region */
|
|
|
|
|
|
2010-03-22 22:14:54 +00:00
|
|
|
assert (!srcs.empty ());
|
|
|
|
|
|
2010-02-18 13:59:49 +00:00
|
|
|
PropertyList plist;
|
|
|
|
|
plist.add (Properties::name, _name.val());
|
|
|
|
|
plist.add (Properties::start, 0);
|
2010-09-17 16:24:22 +00:00
|
|
|
plist.add (Properties::length, max_framepos - (max_framepos - srcs.front()->natural_position()));
|
2010-02-18 13:59:49 +00:00
|
|
|
|
|
|
|
|
boost::shared_ptr<Region> region (RegionFactory::create (srcs, plist));
|
2009-10-14 16:10:01 +00:00
|
|
|
_playlist->add_region (region, srcs.front()->natural_position());
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
AudioDiskstream::use_destructive_playlist ()
|
|
|
|
|
{
|
|
|
|
|
/* this is called from the XML-based constructor or ::set_destructive. when called,
|
|
|
|
|
we already have a playlist and a region, but we need to
|
2009-10-14 16:10:01 +00:00
|
|
|
set up our sources for write. we use the sources associated
|
2008-06-02 21:41:35 +00:00
|
|
|
with the (presumed single, full-extent) region.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
boost::shared_ptr<Region> rp = _playlist->find_next_region (_session.current_start_frame(), Start, 1);
|
|
|
|
|
|
|
|
|
|
if (!rp) {
|
|
|
|
|
reset_write_sources (false, true);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (rp);
|
|
|
|
|
|
|
|
|
|
if (region == 0) {
|
|
|
|
|
throw failed_constructor();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* be sure to stretch the region out to the maximum length */
|
|
|
|
|
|
2011-05-30 13:54:44 +00:00
|
|
|
region->set_length (max_framepos - region->position());
|
2008-06-02 21:41:35 +00:00
|
|
|
|
|
|
|
|
uint32_t n;
|
|
|
|
|
ChannelList::iterator chan;
|
|
|
|
|
boost::shared_ptr<ChannelList> c = channels.reader();
|
|
|
|
|
|
|
|
|
|
for (n = 0, chan = c->begin(); chan != c->end(); ++chan, ++n) {
|
|
|
|
|
(*chan)->write_source = boost::dynamic_pointer_cast<AudioFileSource>(region->source (n));
|
|
|
|
|
assert((*chan)->write_source);
|
|
|
|
|
(*chan)->write_source->set_allow_remove_if_empty (false);
|
|
|
|
|
|
|
|
|
|
/* this might be false if we switched modes, so force it */
|
|
|
|
|
|
|
|
|
|
(*chan)->write_source->set_destructive (true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* the source list will never be reset for a destructive track */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2010-09-17 16:24:22 +00:00
|
|
|
AudioDiskstream::prepare_record_status(framepos_t capture_start_frame)
|
2008-06-02 21:41:35 +00:00
|
|
|
{
|
2009-10-20 02:01:26 +00:00
|
|
|
if (recordable() && destructive()) {
|
|
|
|
|
boost::shared_ptr<ChannelList> c = channels.reader();
|
|
|
|
|
for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
|
2008-06-02 21:41:35 +00:00
|
|
|
|
2011-03-11 02:55:52 +00:00
|
|
|
RingBufferNPT<CaptureTransition>::rw_vector transitions;
|
|
|
|
|
(*chan)->capture_transition_buf->get_write_vector (&transitions);
|
2008-06-02 21:41:35 +00:00
|
|
|
|
2011-03-11 02:55:52 +00:00
|
|
|
if (transitions.len[0] > 0) {
|
|
|
|
|
transitions.buf[0]->type = CaptureStart;
|
|
|
|
|
transitions.buf[0]->capture_val = capture_start_frame;
|
2009-10-20 02:01:26 +00:00
|
|
|
(*chan)->capture_transition_buf->increment_write_ptr(1);
|
2011-01-02 12:45:20 +00:00
|
|
|
} else {
|
2009-10-20 02:01:26 +00:00
|
|
|
// bad!
|
|
|
|
|
fatal << X_("programming error: capture_transition_buf is full on rec start! inconceivable!")
|
|
|
|
|
<< endmsg;
|
2009-10-14 16:10:01 +00:00
|
|
|
}
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-10-31 21:16:44 +00:00
|
|
|
|
|
|
|
|
/** Do some record stuff [not described in this comment!]
|
|
|
|
|
*
|
|
|
|
|
* Also:
|
|
|
|
|
* - Setup playback_distance with the nframes, or nframes adjusted
|
|
|
|
|
* for current varispeed, if appropriate.
|
|
|
|
|
* - Setup current_playback_buffer in each ChannelInfo to point to data
|
|
|
|
|
* that someone can read playback_distance worth of data from.
|
|
|
|
|
*/
|
2008-06-02 21:41:35 +00:00
|
|
|
int
|
2013-03-27 21:50:18 -04:00
|
|
|
AudioDiskstream::process (BufferSet& bufs, framepos_t transport_frame, pframes_t nframes, framecnt_t& playback_distance, bool need_disk_signal)
|
2008-06-02 21:41:35 +00:00
|
|
|
{
|
|
|
|
|
uint32_t n;
|
|
|
|
|
boost::shared_ptr<ChannelList> c = channels.reader();
|
|
|
|
|
ChannelList::iterator chan;
|
2010-12-03 22:26:29 +00:00
|
|
|
framecnt_t rec_offset = 0;
|
|
|
|
|
framecnt_t rec_nframes = 0;
|
2008-06-02 21:41:35 +00:00
|
|
|
bool collect_playback = false;
|
2011-10-21 15:05:33 +00:00
|
|
|
bool can_record = _session.actively_recording ();
|
2008-06-02 21:41:35 +00:00
|
|
|
|
2011-10-31 21:17:08 +00:00
|
|
|
playback_distance = 0;
|
2008-06-02 21:41:35 +00:00
|
|
|
|
2008-09-10 15:03:30 +00:00
|
|
|
if (!_io || !_io->active()) {
|
2008-06-02 21:41:35 +00:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2010-10-02 00:06:11 +00:00
|
|
|
check_record_status (transport_frame, can_record);
|
2008-06-02 21:41:35 +00:00
|
|
|
|
|
|
|
|
if (nframes == 0) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2012-07-25 17:48:55 +00:00
|
|
|
Glib::Threads::Mutex::Lock sm (state_lock, Glib::Threads::TRY_LOCK);
|
2008-06-02 21:41:35 +00:00
|
|
|
|
2011-03-14 21:53:10 +00:00
|
|
|
if (!sm.locked()) {
|
2008-06-02 21:41:35 +00:00
|
|
|
return 1;
|
2009-10-14 16:10:01 +00:00
|
|
|
}
|
2010-03-27 14:41:24 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
adjust_capture_position = 0;
|
|
|
|
|
|
|
|
|
|
for (chan = c->begin(); chan != c->end(); ++chan) {
|
|
|
|
|
(*chan)->current_capture_buffer = 0;
|
|
|
|
|
(*chan)->current_playback_buffer = 0;
|
|
|
|
|
}
|
|
|
|
|
|
2011-06-01 16:50:12 +00:00
|
|
|
// Safeguard against situations where process() goes haywire when autopunching
|
2011-03-14 21:53:10 +00:00
|
|
|
// and last_recordable_frame < first_recordable_frame
|
|
|
|
|
|
|
|
|
|
if (last_recordable_frame < first_recordable_frame) {
|
|
|
|
|
last_recordable_frame = max_framepos;
|
|
|
|
|
}
|
|
|
|
|
|
2011-04-04 23:00:48 +00:00
|
|
|
if (record_enabled()) {
|
2010-08-30 18:40:36 +00:00
|
|
|
|
2012-04-16 16:32:22 +00:00
|
|
|
Evoral::OverlapType ot = Evoral::coverage (first_recordable_frame, last_recordable_frame, transport_frame, transport_frame + nframes);
|
2011-04-04 23:00:48 +00:00
|
|
|
calculate_record_range (ot, transport_frame, nframes, rec_nframes, rec_offset);
|
2011-06-01 16:50:12 +00:00
|
|
|
|
2011-04-04 23:00:48 +00:00
|
|
|
if (rec_nframes && !was_recording) {
|
|
|
|
|
capture_captured = 0;
|
|
|
|
|
was_recording = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
2008-06-02 21:41:35 +00:00
|
|
|
|
2010-06-23 20:14:07 +00:00
|
|
|
if (can_record && !_last_capture_sources.empty()) {
|
|
|
|
|
_last_capture_sources.clear ();
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
|
|
|
|
|
2010-04-12 22:35:06 +00:00
|
|
|
if (rec_nframes) {
|
2008-06-02 21:41:35 +00:00
|
|
|
|
2009-06-09 20:21:19 +00:00
|
|
|
uint32_t limit = _io->n_ports ().n_audio();
|
2008-06-02 21:41:35 +00:00
|
|
|
|
|
|
|
|
/* 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
|
|
|
|
|
use channels that correspond to missing ports. note that the
|
|
|
|
|
process callback (from which this is called) is always atomic
|
|
|
|
|
with respect to port removal/addition.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
for (n = 0, chan = c->begin(); chan != c->end() && n < limit; ++chan, ++n) {
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
ChannelInfo* chaninfo (*chan);
|
|
|
|
|
|
|
|
|
|
chaninfo->capture_buf->get_write_vector (&chaninfo->capture_vector);
|
|
|
|
|
|
2010-12-03 23:15:53 +00:00
|
|
|
if (rec_nframes <= (framecnt_t) chaninfo->capture_vector.len[0]) {
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
chaninfo->current_capture_buffer = chaninfo->capture_vector.buf[0];
|
|
|
|
|
|
2009-05-01 17:32:48 +00:00
|
|
|
/* note: grab the entire port buffer, but only copy what we were supposed to
|
|
|
|
|
for recording, and use rec_offset
|
2008-06-02 21:41:35 +00:00
|
|
|
*/
|
|
|
|
|
|
2011-10-28 17:04:09 +00:00
|
|
|
boost::shared_ptr<AudioPort> const ap = _io->audio (n);
|
2008-06-02 21:41:35 +00:00
|
|
|
assert(ap);
|
2010-12-03 23:15:53 +00:00
|
|
|
assert(rec_nframes <= (framecnt_t) ap->get_audio_buffer(nframes).capacity());
|
2011-09-20 20:29:47 +00:00
|
|
|
|
2013-03-27 21:50:18 -04:00
|
|
|
Sample *buf = bufs.get_audio (n).data(rec_offset);
|
2011-09-25 20:08:48 +00:00
|
|
|
memcpy (chaninfo->current_capture_buffer, buf, sizeof (Sample) * rec_nframes);
|
2013-03-27 21:50:18 -04:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
} else {
|
|
|
|
|
|
2010-12-03 22:26:29 +00:00
|
|
|
framecnt_t total = chaninfo->capture_vector.len[0] + chaninfo->capture_vector.len[1];
|
2008-06-02 21:41:35 +00:00
|
|
|
|
|
|
|
|
if (rec_nframes > total) {
|
|
|
|
|
DiskOverrun ();
|
2011-10-31 21:17:08 +00:00
|
|
|
return -1;
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
|
|
|
|
|
2011-10-28 17:04:09 +00:00
|
|
|
boost::shared_ptr<AudioPort> const ap = _io->audio (n);
|
2008-06-02 21:41:35 +00:00
|
|
|
assert(ap);
|
|
|
|
|
|
2013-03-27 21:50:18 -04:00
|
|
|
Sample *buf = bufs.get_audio (n).data(rec_offset);
|
2010-12-03 22:26:29 +00:00
|
|
|
framecnt_t first = chaninfo->capture_vector.len[0];
|
2008-06-02 21:41:35 +00:00
|
|
|
|
|
|
|
|
memcpy (chaninfo->capture_wrap_buffer, buf, sizeof (Sample) * first);
|
|
|
|
|
memcpy (chaninfo->capture_vector.buf[0], buf, sizeof (Sample) * first);
|
|
|
|
|
memcpy (chaninfo->capture_wrap_buffer+first, buf + first, sizeof (Sample) * (rec_nframes - first));
|
|
|
|
|
memcpy (chaninfo->capture_vector.buf[1], buf + first, sizeof (Sample) * (rec_nframes - first));
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
chaninfo->current_capture_buffer = chaninfo->capture_wrap_buffer;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
|
|
if (was_recording) {
|
2011-09-18 19:49:26 +00:00
|
|
|
finish_capture (c);
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
if (rec_nframes) {
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
/* data will be written to disk */
|
|
|
|
|
|
|
|
|
|
if (rec_nframes == nframes && rec_offset == 0) {
|
|
|
|
|
|
|
|
|
|
for (chan = c->begin(); chan != c->end(); ++chan) {
|
|
|
|
|
(*chan)->current_playback_buffer = (*chan)->current_capture_buffer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
playback_distance = nframes;
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* we can't use the capture buffer as the playback buffer, because
|
|
|
|
|
we recorded only a part of the current process' cycle data
|
|
|
|
|
for capture.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
collect_playback = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
adjust_capture_position = rec_nframes;
|
|
|
|
|
|
2010-04-12 22:35:06 +00:00
|
|
|
} else if (can_record && record_enabled()) {
|
2008-06-02 21:41:35 +00:00
|
|
|
|
|
|
|
|
/* can't do actual capture yet - waiting for latency effects to finish before we start*/
|
|
|
|
|
|
|
|
|
|
for (chan = c->begin(); chan != c->end(); ++chan) {
|
|
|
|
|
(*chan)->current_playback_buffer = (*chan)->current_capture_buffer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
playback_distance = nframes;
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
|
|
collect_playback = true;
|
|
|
|
|
}
|
|
|
|
|
|
2011-10-21 15:05:33 +00:00
|
|
|
if ((_track->monitoring_state () & MonitoringDisk) || collect_playback) {
|
|
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
/* we're doing playback */
|
|
|
|
|
|
2010-12-03 22:26:29 +00:00
|
|
|
framecnt_t necessary_samples;
|
2008-06-02 21:41:35 +00:00
|
|
|
|
|
|
|
|
/* no varispeed playback if we're recording, because the output .... TBD */
|
|
|
|
|
|
|
|
|
|
if (rec_nframes == 0 && _actual_speed != 1.0f) {
|
2012-11-07 03:17:55 +00:00
|
|
|
necessary_samples = (framecnt_t) ceil ((nframes * fabs (_actual_speed))) + 2;
|
2008-06-02 21:41:35 +00:00
|
|
|
} else {
|
|
|
|
|
necessary_samples = nframes;
|
|
|
|
|
}
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
for (chan = c->begin(); chan != c->end(); ++chan) {
|
|
|
|
|
(*chan)->playback_buf->get_read_vector (&(*chan)->playback_vector);
|
|
|
|
|
}
|
|
|
|
|
|
2009-10-14 16:10:01 +00:00
|
|
|
n = 0;
|
2008-06-02 21:41:35 +00:00
|
|
|
|
2011-10-31 21:16:44 +00:00
|
|
|
/* Setup current_playback_buffer in each ChannelInfo to point to data that someone
|
|
|
|
|
can read necessary_samples (== nframes at a transport speed of 1) worth of data
|
|
|
|
|
from right now.
|
|
|
|
|
*/
|
|
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
for (chan = c->begin(); chan != c->end(); ++chan, ++n) {
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
ChannelInfo* chaninfo (*chan);
|
|
|
|
|
|
2010-12-03 23:15:53 +00:00
|
|
|
if (necessary_samples <= (framecnt_t) chaninfo->playback_vector.len[0]) {
|
2011-10-31 21:16:44 +00:00
|
|
|
/* There are enough samples in the first part of the ringbuffer */
|
2008-06-02 21:41:35 +00:00
|
|
|
chaninfo->current_playback_buffer = chaninfo->playback_vector.buf[0];
|
|
|
|
|
|
|
|
|
|
} else {
|
2010-12-03 22:26:29 +00:00
|
|
|
framecnt_t total = chaninfo->playback_vector.len[0] + chaninfo->playback_vector.len[1];
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
if (necessary_samples > total) {
|
2009-11-08 16:28:21 +00:00
|
|
|
cerr << _name << " Need " << necessary_samples << " total = " << total << endl;
|
2008-06-02 21:41:35 +00:00
|
|
|
cerr << "underrun for " << _name << endl;
|
|
|
|
|
DiskUnderrun ();
|
2011-10-31 21:17:08 +00:00
|
|
|
return -1;
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
} else {
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2011-10-31 21:16:44 +00:00
|
|
|
/* We have enough samples, but not in one lump. Coalesce the two parts
|
|
|
|
|
into one in playback_wrap_buffer in our ChannelInfo, and specify that
|
|
|
|
|
as our current_playback_buffer.
|
|
|
|
|
*/
|
|
|
|
|
|
2012-12-23 14:25:12 +00:00
|
|
|
assert(wrap_buffer_size >= necessary_samples);
|
2012-12-23 11:58:08 +00:00
|
|
|
|
2011-10-31 21:16:44 +00:00
|
|
|
/* Copy buf[0] from playback_buf */
|
2009-05-04 21:26:46 +00:00
|
|
|
memcpy ((char *) chaninfo->playback_wrap_buffer,
|
|
|
|
|
chaninfo->playback_vector.buf[0],
|
|
|
|
|
chaninfo->playback_vector.len[0] * sizeof (Sample));
|
2011-10-31 21:16:44 +00:00
|
|
|
|
|
|
|
|
/* Copy buf[1] from playback_buf */
|
2009-05-04 21:26:46 +00:00
|
|
|
memcpy (chaninfo->playback_wrap_buffer + chaninfo->playback_vector.len[0],
|
2009-10-14 16:10:01 +00:00
|
|
|
chaninfo->playback_vector.buf[1],
|
2009-05-04 21:26:46 +00:00
|
|
|
(necessary_samples - chaninfo->playback_vector.len[0])
|
|
|
|
|
* sizeof (Sample));
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
chaninfo->current_playback_buffer = chaninfo->playback_wrap_buffer;
|
|
|
|
|
}
|
|
|
|
|
}
|
2009-10-14 16:10:01 +00:00
|
|
|
}
|
2008-06-02 21:41:35 +00:00
|
|
|
|
|
|
|
|
if (rec_nframes == 0 && _actual_speed != 1.0f && _actual_speed != -1.0f) {
|
2011-10-31 21:16:54 +00:00
|
|
|
|
|
|
|
|
interpolation.set_speed (_target_speed);
|
|
|
|
|
|
|
|
|
|
int channel = 0;
|
|
|
|
|
for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan, ++channel) {
|
|
|
|
|
ChannelInfo* chaninfo (*chan);
|
|
|
|
|
|
|
|
|
|
playback_distance = interpolation.interpolate (
|
|
|
|
|
channel, nframes, chaninfo->current_playback_buffer, chaninfo->speed_buffer);
|
|
|
|
|
|
|
|
|
|
chaninfo->current_playback_buffer = chaninfo->speed_buffer;
|
|
|
|
|
}
|
|
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
} else {
|
|
|
|
|
playback_distance = nframes;
|
|
|
|
|
}
|
|
|
|
|
|
2009-06-10 00:03:47 +00:00
|
|
|
_speed = _target_speed;
|
2011-06-01 16:50:12 +00:00
|
|
|
}
|
2008-06-02 21:41:35 +00:00
|
|
|
|
2013-03-27 21:50:18 -04:00
|
|
|
if (need_disk_signal) {
|
|
|
|
|
|
|
|
|
|
/* copy data over to buffer set */
|
|
|
|
|
|
|
|
|
|
size_t n_buffers = bufs.count().n_audio();
|
|
|
|
|
size_t n_chans = c->size();
|
|
|
|
|
gain_t scaling = 1.0f;
|
|
|
|
|
|
|
|
|
|
if (n_chans > n_buffers) {
|
|
|
|
|
scaling = ((float) n_buffers)/n_chans;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (n = 0, chan = c->begin(); chan != c->end(); ++chan, ++n) {
|
|
|
|
|
|
|
|
|
|
AudioBuffer& buf (bufs.get_audio (n%n_buffers));
|
|
|
|
|
ChannelInfo* chaninfo (*chan);
|
|
|
|
|
|
|
|
|
|
if (n < n_chans) {
|
|
|
|
|
if (scaling != 1.0f) {
|
|
|
|
|
buf.read_from_with_gain (chaninfo->current_playback_buffer, nframes, scaling);
|
|
|
|
|
} else {
|
|
|
|
|
buf.read_from (chaninfo->current_playback_buffer, nframes);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (scaling != 1.0f) {
|
|
|
|
|
buf.accumulate_with_gain_from (chaninfo->current_playback_buffer, nframes, scaling);
|
|
|
|
|
} else {
|
|
|
|
|
buf.accumulate_from (chaninfo->current_playback_buffer, nframes);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* leave the MIDI count alone */
|
|
|
|
|
ChanCount cnt (DataType::AUDIO, n_chans);
|
|
|
|
|
cnt.set (DataType::MIDI, bufs.count().n_midi());
|
|
|
|
|
bufs.set_count (cnt);
|
|
|
|
|
|
|
|
|
|
/* extra buffers will already be silent, so leave them alone */
|
|
|
|
|
}
|
|
|
|
|
|
2011-10-31 21:17:08 +00:00
|
|
|
return 0;
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
|
|
|
|
|
2013-08-03 21:35:19 +02:00
|
|
|
frameoffset_t
|
|
|
|
|
AudioDiskstream::calculate_playback_distance (pframes_t nframes)
|
|
|
|
|
{
|
|
|
|
|
frameoffset_t playback_distance = nframes;
|
|
|
|
|
|
|
|
|
|
if (record_enabled()) {
|
|
|
|
|
playback_distance = nframes;
|
|
|
|
|
} else if (_actual_speed != 1.0f && _actual_speed != -1.0f) {
|
|
|
|
|
interpolation.set_speed (_target_speed);
|
|
|
|
|
boost::shared_ptr<ChannelList> c = channels.reader();
|
|
|
|
|
int channel = 0;
|
|
|
|
|
for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan, ++channel) {
|
|
|
|
|
playback_distance = interpolation.interpolate (channel, nframes, NULL, NULL);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
playback_distance = nframes;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (_actual_speed < 0.0) {
|
|
|
|
|
return -playback_distance;
|
|
|
|
|
} else {
|
|
|
|
|
return playback_distance;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-10-31 21:17:02 +00:00
|
|
|
/** Update various things including playback_sample, read pointer on each channel's playback_buf
|
|
|
|
|
* and write pointer on each channel's capture_buf. Also wout whether the butler is needed.
|
|
|
|
|
* @return true if the butler is required.
|
|
|
|
|
*/
|
2008-06-02 21:41:35 +00:00
|
|
|
bool
|
2011-10-31 21:16:54 +00:00
|
|
|
AudioDiskstream::commit (framecnt_t playback_distance)
|
2008-06-02 21:41:35 +00:00
|
|
|
{
|
|
|
|
|
bool need_butler = false;
|
|
|
|
|
|
2008-09-10 15:03:30 +00:00
|
|
|
if (!_io || !_io->active()) {
|
2008-06-02 21:41:35 +00:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (_actual_speed < 0.0) {
|
|
|
|
|
playback_sample -= playback_distance;
|
|
|
|
|
} else {
|
|
|
|
|
playback_sample += playback_distance;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
boost::shared_ptr<ChannelList> c = channels.reader();
|
|
|
|
|
for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
|
|
|
|
|
|
|
|
|
|
(*chan)->playback_buf->increment_read_ptr (playback_distance);
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
if (adjust_capture_position) {
|
|
|
|
|
(*chan)->capture_buf->increment_write_ptr (adjust_capture_position);
|
|
|
|
|
}
|
|
|
|
|
}
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
if (adjust_capture_position != 0) {
|
|
|
|
|
capture_captured += adjust_capture_position;
|
|
|
|
|
adjust_capture_position = 0;
|
|
|
|
|
}
|
2011-06-01 16:50:12 +00:00
|
|
|
|
2011-01-02 12:45:20 +00:00
|
|
|
if (c->empty()) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2011-06-01 16:50:12 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
if (_slaved) {
|
|
|
|
|
if (_io && _io->active()) {
|
|
|
|
|
need_butler = c->front()->playback_buf->write_space() >= c->front()->playback_buf->bufsize() / 2;
|
|
|
|
|
} else {
|
|
|
|
|
need_butler = false;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (_io && _io->active()) {
|
2010-12-03 23:15:53 +00:00
|
|
|
need_butler = ((framecnt_t) c->front()->playback_buf->write_space() >= disk_io_chunk_frames)
|
|
|
|
|
|| ((framecnt_t) c->front()->capture_buf->read_space() >= disk_io_chunk_frames);
|
2008-06-02 21:41:35 +00:00
|
|
|
} else {
|
2010-12-03 23:15:53 +00:00
|
|
|
need_butler = ((framecnt_t) c->front()->capture_buf->read_space() >= disk_io_chunk_frames);
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return need_butler;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
AudioDiskstream::set_pending_overwrite (bool yn)
|
|
|
|
|
{
|
|
|
|
|
/* called from audio thread, so we can use the read ptr and playback sample as we wish */
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2010-04-21 20:42:22 +00:00
|
|
|
_pending_overwrite = yn;
|
2008-06-02 21:41:35 +00:00
|
|
|
|
|
|
|
|
overwrite_frame = playback_sample;
|
2011-01-02 12:45:20 +00:00
|
|
|
|
|
|
|
|
boost::shared_ptr<ChannelList> c = channels.reader ();
|
|
|
|
|
if (!c->empty ()) {
|
|
|
|
|
overwrite_offset = c->front()->playback_buf->get_read_ptr();
|
|
|
|
|
}
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
AudioDiskstream::overwrite_existing_buffers ()
|
|
|
|
|
{
|
|
|
|
|
boost::shared_ptr<ChannelList> c = channels.reader();
|
2011-01-02 12:45:20 +00:00
|
|
|
if (c->empty ()) {
|
|
|
|
|
_pending_overwrite = false;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2011-06-01 16:50:12 +00:00
|
|
|
|
2009-10-14 16:10:01 +00:00
|
|
|
Sample* mixdown_buffer;
|
|
|
|
|
float* gain_buffer;
|
|
|
|
|
int ret = -1;
|
2008-06-02 21:41:35 +00:00
|
|
|
bool reversed = (_visible_speed * _session.transport_speed()) < 0.0f;
|
|
|
|
|
|
|
|
|
|
overwrite_queued = false;
|
|
|
|
|
|
|
|
|
|
/* assume all are the same size */
|
2010-12-03 22:26:29 +00:00
|
|
|
framecnt_t size = c->front()->playback_buf->bufsize();
|
2009-10-14 16:10:01 +00:00
|
|
|
|
|
|
|
|
mixdown_buffer = new Sample[size];
|
|
|
|
|
gain_buffer = new float[size];
|
|
|
|
|
|
2011-04-04 21:48:17 +00:00
|
|
|
/* reduce size so that we can fill the buffer correctly (ringbuffers
|
2011-04-04 23:00:48 +00:00
|
|
|
can only handle size-1, otherwise they appear to be empty)
|
|
|
|
|
*/
|
2008-06-02 21:41:35 +00:00
|
|
|
size--;
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
uint32_t n=0;
|
2010-09-17 16:24:22 +00:00
|
|
|
framepos_t start;
|
2008-06-02 21:41:35 +00:00
|
|
|
|
|
|
|
|
for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan, ++n) {
|
|
|
|
|
|
|
|
|
|
start = overwrite_frame;
|
2010-12-03 22:26:29 +00:00
|
|
|
framecnt_t cnt = size;
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
/* to fill the buffer without resetting the playback sample, we need to
|
|
|
|
|
do it one or two chunks (normally two).
|
|
|
|
|
|
|
|
|
|
|----------------------------------------------------------------------|
|
|
|
|
|
|
|
|
|
|
^
|
|
|
|
|
overwrite_offset
|
|
|
|
|
|<- second chunk->||<----------------- first chunk ------------------>|
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
*/
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2010-12-03 22:26:29 +00:00
|
|
|
framecnt_t to_read = size - overwrite_offset;
|
2008-06-02 21:41:35 +00:00
|
|
|
|
2011-10-31 21:16:19 +00:00
|
|
|
if (read ((*chan)->playback_buf->buffer() + overwrite_offset, mixdown_buffer, gain_buffer, start, to_read, n, reversed)) {
|
2008-06-02 21:41:35 +00:00
|
|
|
error << string_compose(_("AudioDiskstream %1: when refilling, cannot read %2 from playlist at frame %3"),
|
2011-10-18 13:18:47 +00:00
|
|
|
id(), size, playback_sample) << endmsg;
|
2008-06-02 21:41:35 +00:00
|
|
|
goto out;
|
|
|
|
|
}
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
if (cnt > to_read) {
|
|
|
|
|
|
|
|
|
|
cnt -= to_read;
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2011-10-31 21:16:19 +00:00
|
|
|
if (read ((*chan)->playback_buf->buffer(), mixdown_buffer, gain_buffer, start, cnt, n, reversed)) {
|
2008-06-02 21:41:35 +00:00
|
|
|
error << string_compose(_("AudioDiskstream %1: when refilling, cannot read %2 from playlist at frame %3"),
|
2011-10-18 13:18:47 +00:00
|
|
|
id(), size, playback_sample) << endmsg;
|
2008-06-02 21:41:35 +00:00
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ret = 0;
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
out:
|
2010-04-21 20:42:22 +00:00
|
|
|
_pending_overwrite = false;
|
2009-10-14 16:10:01 +00:00
|
|
|
delete [] gain_buffer;
|
|
|
|
|
delete [] mixdown_buffer;
|
|
|
|
|
return ret;
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
2010-09-17 16:24:22 +00:00
|
|
|
AudioDiskstream::seek (framepos_t frame, bool complete_refill)
|
2008-06-02 21:41:35 +00:00
|
|
|
{
|
|
|
|
|
uint32_t n;
|
|
|
|
|
int ret = -1;
|
|
|
|
|
ChannelList::iterator chan;
|
|
|
|
|
boost::shared_ptr<ChannelList> c = channels.reader();
|
|
|
|
|
|
2012-07-25 17:48:55 +00:00
|
|
|
Glib::Threads::Mutex::Lock lm (state_lock);
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
for (n = 0, chan = c->begin(); chan != c->end(); ++chan, ++n) {
|
|
|
|
|
(*chan)->playback_buf->reset ();
|
|
|
|
|
(*chan)->capture_buf->reset ();
|
|
|
|
|
}
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
/* can't rec-enable in destructive mode if transport is before start */
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
if (destructive() && record_enabled() && frame < _session.current_start_frame()) {
|
|
|
|
|
disengage_record_enable ();
|
|
|
|
|
}
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
playback_sample = frame;
|
|
|
|
|
file_frame = frame;
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
if (complete_refill) {
|
|
|
|
|
while ((ret = do_refill_with_alloc ()) > 0) ;
|
|
|
|
|
} else {
|
|
|
|
|
ret = do_refill_with_alloc ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
2010-09-17 16:24:22 +00:00
|
|
|
AudioDiskstream::can_internal_playback_seek (framecnt_t distance)
|
2008-06-02 21:41:35 +00:00
|
|
|
{
|
|
|
|
|
ChannelList::iterator chan;
|
|
|
|
|
boost::shared_ptr<ChannelList> c = channels.reader();
|
|
|
|
|
|
|
|
|
|
for (chan = c->begin(); chan != c->end(); ++chan) {
|
2010-09-17 16:24:22 +00:00
|
|
|
if ((*chan)->playback_buf->read_space() < (size_t) distance) {
|
2008-06-02 21:41:35 +00:00
|
|
|
return false;
|
2009-10-14 16:10:01 +00:00
|
|
|
}
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
2010-09-17 16:24:22 +00:00
|
|
|
AudioDiskstream::internal_playback_seek (framecnt_t distance)
|
2008-06-02 21:41:35 +00:00
|
|
|
{
|
|
|
|
|
ChannelList::iterator chan;
|
|
|
|
|
boost::shared_ptr<ChannelList> c = channels.reader();
|
|
|
|
|
|
|
|
|
|
for (chan = c->begin(); chan != c->end(); ++chan) {
|
2013-09-05 10:10:45 +01:00
|
|
|
(*chan)->playback_buf->increment_read_ptr (llabs(distance));
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
|
|
|
|
|
2010-10-02 00:06:23 +00:00
|
|
|
if (first_recordable_frame < max_framepos) {
|
|
|
|
|
first_recordable_frame += distance;
|
|
|
|
|
}
|
2008-06-02 21:41:35 +00:00
|
|
|
playback_sample += distance;
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2011-10-31 21:16:44 +00:00
|
|
|
/** Read some data for 1 channel from our playlist into a buffer.
|
|
|
|
|
* @param buf Buffer to write to.
|
|
|
|
|
* @param start Session frame to start reading from; updated to where we end up
|
|
|
|
|
* after the read.
|
|
|
|
|
* @param cnt Count of samples to read.
|
|
|
|
|
* @param reversed true if we are running backwards, otherwise false.
|
|
|
|
|
*/
|
2008-06-02 21:41:35 +00:00
|
|
|
int
|
2011-06-01 16:50:12 +00:00
|
|
|
AudioDiskstream::read (Sample* buf, Sample* mixdown_buffer, float* gain_buffer,
|
2010-12-03 22:26:29 +00:00
|
|
|
framepos_t& start, framecnt_t cnt,
|
2011-10-31 21:16:19 +00:00
|
|
|
int channel, bool reversed)
|
2008-06-02 21:41:35 +00:00
|
|
|
{
|
2010-12-03 22:26:29 +00:00
|
|
|
framecnt_t this_read = 0;
|
2008-06-02 21:41:35 +00:00
|
|
|
bool reloop = false;
|
2010-09-17 16:24:22 +00:00
|
|
|
framepos_t loop_end = 0;
|
|
|
|
|
framepos_t loop_start = 0;
|
2010-12-03 22:26:29 +00:00
|
|
|
framecnt_t offset = 0;
|
2008-06-02 21:41:35 +00:00
|
|
|
Location *loc = 0;
|
|
|
|
|
|
|
|
|
|
/* XXX we don't currently play loops in reverse. not sure why */
|
|
|
|
|
|
|
|
|
|
if (!reversed) {
|
|
|
|
|
|
2010-09-17 16:24:22 +00:00
|
|
|
framecnt_t loop_length = 0;
|
2010-02-23 20:25:53 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
/* Make the use of a Location atomic for this read operation.
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
Note: Locations don't get deleted, so all we care about
|
|
|
|
|
when I say "atomic" is that we are always pointing to
|
|
|
|
|
the same one and using a start/length values obtained
|
|
|
|
|
just once.
|
|
|
|
|
*/
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
if ((loc = loop_location) != 0) {
|
|
|
|
|
loop_start = loc->start();
|
|
|
|
|
loop_end = loc->end();
|
|
|
|
|
loop_length = loop_end - loop_start;
|
|
|
|
|
}
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
/* if we are looping, ensure that the first frame we read is at the correct
|
|
|
|
|
position within the loop.
|
|
|
|
|
*/
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
if (loc && start >= loop_end) {
|
|
|
|
|
start = loop_start + ((start - loop_start) % loop_length);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-02-06 18:32:05 +00:00
|
|
|
if (reversed) {
|
|
|
|
|
start -= cnt;
|
|
|
|
|
}
|
2011-06-01 16:50:12 +00:00
|
|
|
|
2011-10-31 21:16:44 +00:00
|
|
|
/* We need this while loop in case we hit a loop boundary, in which case our read from
|
|
|
|
|
the playlist must be split into more than one section.
|
|
|
|
|
*/
|
|
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
while (cnt) {
|
|
|
|
|
|
|
|
|
|
/* take any loop into account. we can't read past the end of the loop. */
|
|
|
|
|
|
|
|
|
|
if (loc && (loop_end - start < cnt)) {
|
|
|
|
|
this_read = loop_end - start;
|
|
|
|
|
reloop = true;
|
|
|
|
|
} else {
|
|
|
|
|
reloop = false;
|
|
|
|
|
this_read = cnt;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (this_read == 0) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this_read = min(cnt,this_read);
|
|
|
|
|
|
|
|
|
|
if (audio_playlist()->read (buf+offset, mixdown_buffer, gain_buffer, start, this_read, channel) != this_read) {
|
2011-10-18 13:18:47 +00:00
|
|
|
error << string_compose(_("AudioDiskstream %1: cannot read %2 from playlist at frame %3"), id(), this_read,
|
2008-06-02 21:41:35 +00:00
|
|
|
start) << endmsg;
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (reversed) {
|
|
|
|
|
|
|
|
|
|
swap_by_ptr (buf, buf + this_read - 1);
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
} else {
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
/* if we read to the end of the loop, go back to the beginning */
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
if (reloop) {
|
|
|
|
|
start = loop_start;
|
|
|
|
|
} else {
|
|
|
|
|
start += this_read;
|
|
|
|
|
}
|
2009-10-14 16:10:01 +00:00
|
|
|
}
|
2008-06-02 21:41:35 +00:00
|
|
|
|
|
|
|
|
cnt -= this_read;
|
|
|
|
|
offset += this_read;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
AudioDiskstream::do_refill_with_alloc ()
|
|
|
|
|
{
|
|
|
|
|
Sample* mix_buf = new Sample[disk_io_chunk_frames];
|
|
|
|
|
float* gain_buf = new float[disk_io_chunk_frames];
|
|
|
|
|
|
|
|
|
|
int ret = _do_refill(mix_buf, gain_buf);
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
delete [] mix_buf;
|
|
|
|
|
delete [] gain_buf;
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2011-10-31 21:16:44 +00:00
|
|
|
/** Get some more data from disk and put it in our channels' playback_bufs,
|
|
|
|
|
* if there is suitable space in them.
|
|
|
|
|
*/
|
2008-06-02 21:41:35 +00:00
|
|
|
int
|
|
|
|
|
AudioDiskstream::_do_refill (Sample* mixdown_buffer, float* gain_buffer)
|
|
|
|
|
{
|
|
|
|
|
int32_t ret = 0;
|
2010-12-03 22:26:29 +00:00
|
|
|
framecnt_t to_read;
|
2008-06-02 21:41:35 +00:00
|
|
|
RingBufferNPT<Sample>::rw_vector vector;
|
2011-10-31 21:16:44 +00:00
|
|
|
bool const reversed = (_visible_speed * _session.transport_speed()) < 0.0f;
|
2010-12-03 22:26:29 +00:00
|
|
|
framecnt_t total_space;
|
|
|
|
|
framecnt_t zero_fill;
|
2008-06-02 21:41:35 +00:00
|
|
|
uint32_t chan_n;
|
|
|
|
|
ChannelList::iterator i;
|
|
|
|
|
boost::shared_ptr<ChannelList> c = channels.reader();
|
2010-12-03 22:26:29 +00:00
|
|
|
framecnt_t ts;
|
2008-06-02 21:41:35 +00:00
|
|
|
|
|
|
|
|
if (c->empty()) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
assert(mixdown_buffer);
|
|
|
|
|
assert(gain_buffer);
|
|
|
|
|
|
|
|
|
|
vector.buf[0] = 0;
|
|
|
|
|
vector.len[0] = 0;
|
|
|
|
|
vector.buf[1] = 0;
|
|
|
|
|
vector.len[1] = 0;
|
|
|
|
|
|
|
|
|
|
c->front()->playback_buf->get_write_vector (&vector);
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
if ((total_space = vector.len[0] + vector.len[1]) == 0) {
|
2011-10-31 21:16:44 +00:00
|
|
|
/* nowhere to write to */
|
2008-06-02 21:41:35 +00:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* if there are 2+ chunks of disk i/o possible for
|
|
|
|
|
this track, let the caller know so that it can arrange
|
|
|
|
|
for us to be called again, ASAP.
|
|
|
|
|
*/
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2011-10-31 21:16:44 +00:00
|
|
|
if (total_space >= (_slaved ? 3 : 2) * disk_io_chunk_frames) {
|
2008-06-02 21:41:35 +00:00
|
|
|
ret = 1;
|
|
|
|
|
}
|
2009-10-14 16:10:01 +00:00
|
|
|
|
|
|
|
|
/* if we're running close to normal speed and there isn't enough
|
|
|
|
|
space to do disk_io_chunk_frames of I/O, then don't bother.
|
|
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
at higher speeds, just do it because the sync between butler
|
|
|
|
|
and audio thread may not be good enough.
|
2011-09-14 15:55:27 +00:00
|
|
|
|
|
|
|
|
Note: it is a design assumption that disk_io_chunk_frames is smaller
|
|
|
|
|
than the playback buffer size, so this check should never trip when
|
|
|
|
|
the playback buffer is empty.
|
2008-06-02 21:41:35 +00:00
|
|
|
*/
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
if ((total_space < disk_io_chunk_frames) && fabs (_actual_speed) < 2.0f) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
/* when slaved, don't try to get too close to the read pointer. this
|
|
|
|
|
leaves space for the buffer reversal to have something useful to
|
|
|
|
|
work with.
|
|
|
|
|
*/
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2010-12-03 23:15:53 +00:00
|
|
|
if (_slaved && total_space < (framecnt_t) (c->front()->playback_buf->bufsize() / 2)) {
|
2008-06-02 21:41:35 +00:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* never do more than disk_io_chunk_frames worth of disk input per call (limit doesn't apply for memset) */
|
|
|
|
|
|
|
|
|
|
total_space = min (disk_io_chunk_frames, total_space);
|
|
|
|
|
|
|
|
|
|
if (reversed) {
|
|
|
|
|
|
|
|
|
|
if (file_frame == 0) {
|
|
|
|
|
|
|
|
|
|
/* at start: nothing to do but fill with silence */
|
|
|
|
|
|
|
|
|
|
for (chan_n = 0, i = c->begin(); i != c->end(); ++i, ++chan_n) {
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
ChannelInfo* chan (*i);
|
|
|
|
|
chan->playback_buf->get_write_vector (&vector);
|
|
|
|
|
memset (vector.buf[0], 0, sizeof(Sample) * vector.len[0]);
|
|
|
|
|
if (vector.len[1]) {
|
|
|
|
|
memset (vector.buf[1], 0, sizeof(Sample) * vector.len[1]);
|
|
|
|
|
}
|
|
|
|
|
chan->playback_buf->increment_write_ptr (vector.len[0] + vector.len[1]);
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (file_frame < total_space) {
|
|
|
|
|
|
2009-10-14 16:10:01 +00:00
|
|
|
/* too close to the start: read what we can,
|
|
|
|
|
and then zero fill the rest
|
2008-06-02 21:41:35 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
zero_fill = total_space - file_frame;
|
|
|
|
|
total_space = file_frame;
|
|
|
|
|
|
|
|
|
|
} else {
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
zero_fill = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
2010-09-17 16:24:22 +00:00
|
|
|
if (file_frame == max_framepos) {
|
2008-06-02 21:41:35 +00:00
|
|
|
|
|
|
|
|
/* at end: nothing to do but fill with silence */
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
for (chan_n = 0, i = c->begin(); i != c->end(); ++i, ++chan_n) {
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
ChannelInfo* chan (*i);
|
|
|
|
|
chan->playback_buf->get_write_vector (&vector);
|
|
|
|
|
memset (vector.buf[0], 0, sizeof(Sample) * vector.len[0]);
|
|
|
|
|
if (vector.len[1]) {
|
|
|
|
|
memset (vector.buf[1], 0, sizeof(Sample) * vector.len[1]);
|
|
|
|
|
}
|
|
|
|
|
chan->playback_buf->increment_write_ptr (vector.len[0] + vector.len[1]);
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2010-09-17 16:24:22 +00:00
|
|
|
if (file_frame > max_framepos - total_space) {
|
2008-06-02 21:41:35 +00:00
|
|
|
|
|
|
|
|
/* to close to the end: read what we can, and zero fill the rest */
|
|
|
|
|
|
2010-09-17 16:24:22 +00:00
|
|
|
zero_fill = total_space - (max_framepos - file_frame);
|
|
|
|
|
total_space = max_framepos - file_frame;
|
2008-06-02 21:41:35 +00:00
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
zero_fill = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2010-09-17 16:24:22 +00:00
|
|
|
framepos_t file_frame_tmp = 0;
|
2008-06-02 21:41:35 +00:00
|
|
|
|
|
|
|
|
for (chan_n = 0, i = c->begin(); i != c->end(); ++i, ++chan_n) {
|
|
|
|
|
|
|
|
|
|
ChannelInfo* chan (*i);
|
|
|
|
|
Sample* buf1;
|
|
|
|
|
Sample* buf2;
|
2010-12-03 22:26:29 +00:00
|
|
|
framecnt_t len1, len2;
|
2008-06-02 21:41:35 +00:00
|
|
|
|
|
|
|
|
chan->playback_buf->get_write_vector (&vector);
|
|
|
|
|
|
2010-12-03 23:15:53 +00:00
|
|
|
if ((framecnt_t) vector.len[0] > disk_io_chunk_frames) {
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
/* we're not going to fill the first chunk, so certainly do not bother with the
|
|
|
|
|
other part. it won't be connected with the part we do fill, as in:
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
.... => writable space
|
|
|
|
|
++++ => readable space
|
|
|
|
|
^^^^ => 1 x disk_io_chunk_frames that would be filled
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
|......|+++++++++++++|...............................|
|
|
|
|
|
buf1 buf0
|
|
|
|
|
^^^^^^^^^^^^^^^
|
2009-10-14 16:10:01 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
So, just pretend that the buf1 part isn't there.
|
|
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
*/
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
vector.buf[1] = 0;
|
|
|
|
|
vector.len[1] = 0;
|
2009-10-14 16:10:01 +00:00
|
|
|
|
|
|
|
|
}
|
2008-06-02 21:41:35 +00:00
|
|
|
|
|
|
|
|
ts = total_space;
|
|
|
|
|
file_frame_tmp = file_frame;
|
|
|
|
|
|
|
|
|
|
buf1 = vector.buf[0];
|
|
|
|
|
len1 = vector.len[0];
|
|
|
|
|
buf2 = vector.buf[1];
|
|
|
|
|
len2 = vector.len[1];
|
|
|
|
|
|
|
|
|
|
to_read = min (ts, len1);
|
|
|
|
|
to_read = min (to_read, disk_io_chunk_frames);
|
|
|
|
|
|
2011-02-06 18:32:22 +00:00
|
|
|
assert (to_read >= 0);
|
|
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
if (to_read) {
|
|
|
|
|
|
2011-10-31 21:16:19 +00:00
|
|
|
if (read (buf1, mixdown_buffer, gain_buffer, file_frame_tmp, to_read, chan_n, reversed)) {
|
2008-06-02 21:41:35 +00:00
|
|
|
ret = -1;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
chan->playback_buf->increment_write_ptr (to_read);
|
|
|
|
|
ts -= to_read;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
to_read = min (ts, len2);
|
|
|
|
|
|
|
|
|
|
if (to_read) {
|
|
|
|
|
|
|
|
|
|
/* we read all of vector.len[0], but it wasn't an entire disk_io_chunk_frames of data,
|
|
|
|
|
so read some or all of vector.len[1] as well.
|
|
|
|
|
*/
|
|
|
|
|
|
2011-10-31 21:16:19 +00:00
|
|
|
if (read (buf2, mixdown_buffer, gain_buffer, file_frame_tmp, to_read, chan_n, reversed)) {
|
2008-06-02 21:41:35 +00:00
|
|
|
ret = -1;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
chan->playback_buf->increment_write_ptr (to_read);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (zero_fill) {
|
2011-10-31 21:16:44 +00:00
|
|
|
/* XXX: do something */
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
file_frame = file_frame_tmp;
|
2011-02-06 18:32:22 +00:00
|
|
|
assert (file_frame >= 0);
|
2011-06-01 16:50:12 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
out:
|
|
|
|
|
|
|
|
|
|
return ret;
|
2009-10-14 16:10:01 +00:00
|
|
|
}
|
2008-06-02 21:41:35 +00:00
|
|
|
|
|
|
|
|
/** Flush pending data to disk.
|
|
|
|
|
*
|
|
|
|
|
* Important note: this function will write *AT MOST* disk_io_chunk_frames
|
|
|
|
|
* of data to disk. it will never write more than that. If it writes that
|
|
|
|
|
* much and there is more than that waiting to be written, it will return 1,
|
|
|
|
|
* otherwise 0 on success or -1 on failure.
|
2009-10-14 16:10:01 +00:00
|
|
|
*
|
2008-06-02 21:41:35 +00:00
|
|
|
* If there is less than disk_io_chunk_frames to be written, no data will be
|
|
|
|
|
* written at all unless @a force_flush is true.
|
|
|
|
|
*/
|
|
|
|
|
int
|
2009-07-21 15:55:17 +00:00
|
|
|
AudioDiskstream::do_flush (RunContext /*context*/, bool force_flush)
|
2008-06-02 21:41:35 +00:00
|
|
|
{
|
|
|
|
|
uint32_t to_write;
|
|
|
|
|
int32_t ret = 0;
|
|
|
|
|
RingBufferNPT<Sample>::rw_vector vector;
|
|
|
|
|
RingBufferNPT<CaptureTransition>::rw_vector transvec;
|
2010-12-03 22:26:29 +00:00
|
|
|
framecnt_t total;
|
2008-06-02 21:41:35 +00:00
|
|
|
|
|
|
|
|
transvec.buf[0] = 0;
|
|
|
|
|
transvec.buf[1] = 0;
|
|
|
|
|
vector.buf[0] = 0;
|
|
|
|
|
vector.buf[1] = 0;
|
|
|
|
|
|
|
|
|
|
boost::shared_ptr<ChannelList> c = channels.reader();
|
|
|
|
|
for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
(*chan)->capture_buf->get_read_vector (&vector);
|
|
|
|
|
|
|
|
|
|
total = vector.len[0] + vector.len[1];
|
|
|
|
|
|
|
|
|
|
if (total == 0 || (total < disk_io_chunk_frames && !force_flush && was_recording)) {
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* if there are 2+ chunks of disk i/o possible for
|
|
|
|
|
this track, let the caller know so that it can arrange
|
|
|
|
|
for us to be called again, ASAP.
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
if we are forcing a flush, then if there is* any* extra
|
|
|
|
|
work, let the caller know.
|
|
|
|
|
|
|
|
|
|
if we are no longer recording and there is any extra work,
|
|
|
|
|
let the caller know too.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
if (total >= 2 * disk_io_chunk_frames || ((force_flush || !was_recording) && total > disk_io_chunk_frames)) {
|
|
|
|
|
ret = 1;
|
2009-10-14 16:10:01 +00:00
|
|
|
}
|
2008-06-02 21:41:35 +00:00
|
|
|
|
2010-12-03 22:26:29 +00:00
|
|
|
to_write = min (disk_io_chunk_frames, (framecnt_t) vector.len[0]);
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
// check the transition buffer when recording destructive
|
|
|
|
|
// important that we get this after the capture buf
|
|
|
|
|
|
|
|
|
|
if (destructive()) {
|
|
|
|
|
(*chan)->capture_transition_buf->get_read_vector(&transvec);
|
|
|
|
|
size_t transcount = transvec.len[0] + transvec.len[1];
|
|
|
|
|
size_t ti;
|
|
|
|
|
|
|
|
|
|
for (ti=0; ti < transcount; ++ti) {
|
|
|
|
|
CaptureTransition & captrans = (ti < transvec.len[0]) ? transvec.buf[0][ti] : transvec.buf[1][ti-transvec.len[0]];
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
if (captrans.type == CaptureStart) {
|
|
|
|
|
// by definition, the first data we got above represents the given capture pos
|
|
|
|
|
|
|
|
|
|
(*chan)->write_source->mark_capture_start (captrans.capture_val);
|
|
|
|
|
(*chan)->curr_capture_cnt = 0;
|
|
|
|
|
|
2011-07-02 17:16:46 +00:00
|
|
|
} else if (captrans.type == CaptureEnd) {
|
2008-06-02 21:41:35 +00:00
|
|
|
|
|
|
|
|
// capture end, the capture_val represents total frames in capture
|
|
|
|
|
|
|
|
|
|
if (captrans.capture_val <= (*chan)->curr_capture_cnt + to_write) {
|
|
|
|
|
|
|
|
|
|
// shorten to make the write a perfect fit
|
2009-10-14 16:10:01 +00:00
|
|
|
uint32_t nto_write = (captrans.capture_val - (*chan)->curr_capture_cnt);
|
2008-06-02 21:41:35 +00:00
|
|
|
|
|
|
|
|
if (nto_write < to_write) {
|
|
|
|
|
ret = 1; // should we?
|
|
|
|
|
}
|
|
|
|
|
to_write = nto_write;
|
|
|
|
|
|
|
|
|
|
(*chan)->write_source->mark_capture_end ();
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
// increment past this transition, but go no further
|
|
|
|
|
++ti;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
// actually ends just beyond this chunk, so force more work
|
|
|
|
|
ret = 1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ti > 0) {
|
|
|
|
|
(*chan)->capture_transition_buf->increment_read_ptr(ti);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((!(*chan)->write_source) || (*chan)->write_source->write (vector.buf[0], to_write) != to_write) {
|
2011-10-18 13:18:47 +00:00
|
|
|
error << string_compose(_("AudioDiskstream %1: cannot write to disk"), id()) << endmsg;
|
2008-06-02 21:41:35 +00:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
(*chan)->capture_buf->increment_read_ptr (to_write);
|
|
|
|
|
(*chan)->curr_capture_cnt += to_write;
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
if ((to_write == vector.len[0]) && (total > to_write) && (to_write < disk_io_chunk_frames) && !destructive()) {
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
/* we wrote all of vector.len[0] but it wasn't an entire
|
2009-10-14 16:10:01 +00:00
|
|
|
disk_io_chunk_frames of data, so arrange for some part
|
2008-06-02 21:41:35 +00:00
|
|
|
of vector.len[1] to be flushed to disk as well.
|
|
|
|
|
*/
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2010-12-03 22:26:29 +00:00
|
|
|
to_write = min ((framecnt_t)(disk_io_chunk_frames - to_write), (framecnt_t) vector.len[1]);
|
2008-06-02 21:41:35 +00:00
|
|
|
|
|
|
|
|
if ((*chan)->write_source->write (vector.buf[1], to_write) != to_write) {
|
2011-10-18 13:18:47 +00:00
|
|
|
error << string_compose(_("AudioDiskstream %1: cannot write to disk"), id()) << endmsg;
|
2008-06-02 21:41:35 +00:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
(*chan)->capture_buf->increment_read_ptr (to_write);
|
|
|
|
|
(*chan)->curr_capture_cnt += to_write;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2010-04-21 20:42:22 +00:00
|
|
|
AudioDiskstream::transport_stopped_wallclock (struct tm& when, time_t twhen, bool abort_capture)
|
2008-06-02 21:41:35 +00:00
|
|
|
{
|
|
|
|
|
uint32_t buffer_position;
|
|
|
|
|
bool more_work = true;
|
|
|
|
|
int err = 0;
|
|
|
|
|
boost::shared_ptr<AudioRegion> region;
|
2010-12-03 22:26:29 +00:00
|
|
|
framecnt_t total_capture;
|
2008-06-02 21:41:35 +00:00
|
|
|
SourceList srcs;
|
|
|
|
|
SourceList::iterator src;
|
|
|
|
|
ChannelList::iterator chan;
|
|
|
|
|
vector<CaptureInfo*>::iterator ci;
|
|
|
|
|
boost::shared_ptr<ChannelList> c = channels.reader();
|
2009-10-14 16:10:01 +00:00
|
|
|
uint32_t n = 0;
|
2008-06-02 21:41:35 +00:00
|
|
|
bool mark_write_completed = false;
|
|
|
|
|
|
2011-09-18 19:49:26 +00:00
|
|
|
finish_capture (c);
|
2008-06-02 21:41:35 +00:00
|
|
|
|
2009-10-14 16:10:01 +00:00
|
|
|
/* butler is already stopped, but there may be work to do
|
2008-06-02 21:41:35 +00:00
|
|
|
to flush remaining data to disk.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
while (more_work && !err) {
|
2009-01-30 20:18:31 +00:00
|
|
|
switch (do_flush (TransportContext, true)) {
|
2008-06-02 21:41:35 +00:00
|
|
|
case 0:
|
|
|
|
|
more_work = false;
|
|
|
|
|
break;
|
|
|
|
|
case 1:
|
|
|
|
|
break;
|
|
|
|
|
case -1:
|
|
|
|
|
error << string_compose(_("AudioDiskstream \"%1\": cannot flush captured data to disk!"), _name) << endmsg;
|
|
|
|
|
err++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* XXX is there anything we can do if err != 0 ? */
|
2012-07-25 17:48:55 +00:00
|
|
|
Glib::Threads::Mutex::Lock lm (capture_info_lock);
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
if (capture_info.empty()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (abort_capture) {
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
if (destructive()) {
|
|
|
|
|
goto outout;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
|
|
|
|
|
|
|
|
|
|
if ((*chan)->write_source) {
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
(*chan)->write_source->mark_for_remove ();
|
|
|
|
|
(*chan)->write_source->drop_references ();
|
|
|
|
|
(*chan)->write_source.reset ();
|
|
|
|
|
}
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
/* new source set up in "out" below */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
goto out;
|
2009-10-14 16:10:01 +00:00
|
|
|
}
|
2008-06-02 21:41:35 +00:00
|
|
|
|
|
|
|
|
for (total_capture = 0, ci = capture_info.begin(); ci != capture_info.end(); ++ci) {
|
|
|
|
|
total_capture += (*ci)->frames;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* figure out the name for this take */
|
|
|
|
|
|
|
|
|
|
for (n = 0, chan = c->begin(); chan != c->end(); ++chan, ++n) {
|
|
|
|
|
|
|
|
|
|
boost::shared_ptr<AudioFileSource> s = (*chan)->write_source;
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
if (s) {
|
|
|
|
|
srcs.push_back (s);
|
|
|
|
|
s->update_header (capture_info.front()->start, when, twhen);
|
2010-02-11 23:10:29 +00:00
|
|
|
s->set_captured_for (_name.val());
|
2008-06-02 21:41:35 +00:00
|
|
|
s->mark_immutable ();
|
2010-07-16 14:55:11 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
if (Config->get_auto_analyse_audio()) {
|
|
|
|
|
Analyser::queue_source_for_analysis (s, true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* destructive tracks have a single, never changing region */
|
|
|
|
|
|
|
|
|
|
if (destructive()) {
|
|
|
|
|
|
2009-10-14 16:10:01 +00:00
|
|
|
/* send a signal that any UI can pick up to do the right thing. there is
|
2008-06-02 21:41:35 +00:00
|
|
|
a small problem here in that a UI may need the peak data to be ready
|
|
|
|
|
for the data that was recorded and this isn't interlocked with that
|
|
|
|
|
process. this problem is deferred to the UI.
|
|
|
|
|
*/
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2010-01-25 20:34:09 +00:00
|
|
|
_playlist->LayeringChanged(); // XXX this may not get the UI to do the right thing
|
2008-06-02 21:41:35 +00:00
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
|
|
string whole_file_region_name;
|
|
|
|
|
whole_file_region_name = region_name_from_path (c->front()->write_source->name(), true);
|
|
|
|
|
|
|
|
|
|
/* Register a new region with the Session that
|
|
|
|
|
describes the entire source. Do this first
|
|
|
|
|
so that any sub-regions will obviously be
|
|
|
|
|
children of this one (later!)
|
|
|
|
|
*/
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
try {
|
2010-02-18 13:59:49 +00:00
|
|
|
PropertyList plist;
|
|
|
|
|
|
|
|
|
|
plist.add (Properties::start, c->front()->write_source->last_capture_start_frame());
|
|
|
|
|
plist.add (Properties::length, total_capture);
|
|
|
|
|
plist.add (Properties::name, whole_file_region_name);
|
|
|
|
|
boost::shared_ptr<Region> rx (RegionFactory::create (srcs, plist));
|
|
|
|
|
rx->set_automatic (true);
|
|
|
|
|
rx->set_whole_file (true);
|
2008-06-02 21:41:35 +00:00
|
|
|
|
|
|
|
|
region = boost::dynamic_pointer_cast<AudioRegion> (rx);
|
|
|
|
|
region->special_set_position (capture_info.front()->start);
|
|
|
|
|
}
|
2009-10-14 16:10:01 +00:00
|
|
|
|
|
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
catch (failed_constructor& err) {
|
|
|
|
|
error << string_compose(_("%1: could not create region for complete audio file"), _name) << endmsg;
|
|
|
|
|
/* XXX what now? */
|
|
|
|
|
}
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2010-06-23 20:14:07 +00:00
|
|
|
_last_capture_sources.insert (_last_capture_sources.end(), srcs.begin(), srcs.end());
|
2008-06-02 21:41:35 +00:00
|
|
|
|
|
|
|
|
// cerr << _name << ": there are " << capture_info.size() << " capture_info records\n";
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2011-03-14 21:53:10 +00:00
|
|
|
_playlist->clear_changes ();
|
2012-05-24 22:03:19 +00:00
|
|
|
_playlist->set_capture_insertion_in_progress (true);
|
2008-06-02 21:41:35 +00:00
|
|
|
_playlist->freeze ();
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
for (buffer_position = c->front()->write_source->last_capture_start_frame(), ci = capture_info.begin(); ci != capture_info.end(); ++ci) {
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
string region_name;
|
|
|
|
|
|
2010-03-30 23:40:28 +00:00
|
|
|
RegionFactory::region_name (region_name, whole_file_region_name, false);
|
2011-06-01 16:50:12 +00:00
|
|
|
|
2011-04-04 23:00:48 +00:00
|
|
|
DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("%1 capture start @ %2 length %3 add new region %4\n",
|
|
|
|
|
_name, (*ci)->start, (*ci)->frames, region_name));
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
try {
|
2010-02-18 13:59:49 +00:00
|
|
|
|
|
|
|
|
PropertyList plist;
|
2011-06-01 16:50:12 +00:00
|
|
|
|
2010-02-18 13:59:49 +00:00
|
|
|
plist.add (Properties::start, buffer_position);
|
|
|
|
|
plist.add (Properties::length, (*ci)->frames);
|
|
|
|
|
plist.add (Properties::name, region_name);
|
2011-06-01 16:50:12 +00:00
|
|
|
|
2010-02-18 13:59:49 +00:00
|
|
|
boost::shared_ptr<Region> rx (RegionFactory::create (srcs, plist));
|
2008-06-02 21:41:35 +00:00
|
|
|
region = boost::dynamic_pointer_cast<AudioRegion> (rx);
|
|
|
|
|
}
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
catch (failed_constructor& err) {
|
|
|
|
|
error << _("AudioDiskstream: could not create region for captured audio!") << endmsg;
|
|
|
|
|
continue; /* XXX is this OK? */
|
|
|
|
|
}
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
i_am_the_modifier++;
|
2011-12-29 22:14:15 +00:00
|
|
|
|
2009-04-20 21:02:46 +00:00
|
|
|
_playlist->add_region (region, (*ci)->start, 1, non_layered());
|
2011-12-30 20:05:48 +00:00
|
|
|
_playlist->set_layer (region, DBL_MAX);
|
2008-06-02 21:41:35 +00:00
|
|
|
i_am_the_modifier--;
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
buffer_position += (*ci)->frames;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_playlist->thaw ();
|
2012-05-24 22:03:19 +00:00
|
|
|
_playlist->set_capture_insertion_in_progress (false);
|
2010-03-02 18:05:26 +00:00
|
|
|
_session.add_command (new StatefulDiffCommand (_playlist));
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mark_write_completed = true;
|
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
reset_write_sources (mark_write_completed);
|
|
|
|
|
|
|
|
|
|
outout:
|
|
|
|
|
|
|
|
|
|
for (ci = capture_info.begin(); ci != capture_info.end(); ++ci) {
|
|
|
|
|
delete *ci;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
capture_info.clear ();
|
|
|
|
|
capture_start_frame = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2010-09-17 16:24:22 +00:00
|
|
|
AudioDiskstream::transport_looped (framepos_t transport_frame)
|
2008-06-02 21:41:35 +00:00
|
|
|
{
|
|
|
|
|
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) {
|
2011-03-11 02:55:52 +00:00
|
|
|
capture_captured += _session.worst_output_latency();
|
2008-06-02 21:41:35 +00:00
|
|
|
} else {
|
|
|
|
|
capture_captured += _roll_delay;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-09-18 19:49:26 +00:00
|
|
|
finish_capture (c);
|
2008-06-02 21:41:35 +00:00
|
|
|
|
|
|
|
|
// 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
|
2010-09-17 16:24:22 +00:00
|
|
|
last_recordable_frame = max_framepos;
|
2008-06-02 21:41:35 +00:00
|
|
|
was_recording = true;
|
|
|
|
|
|
|
|
|
|
if (recordable() && destructive()) {
|
|
|
|
|
for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
RingBufferNPT<CaptureTransition>::rw_vector transvec;
|
|
|
|
|
(*chan)->capture_transition_buf->get_write_vector(&transvec);
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
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!
|
2009-10-14 16:10:01 +00:00
|
|
|
fatal << X_("programming error: capture_transition_buf is full on rec loop! inconceivable!")
|
2008-06-02 21:41:35 +00:00
|
|
|
<< endmsg;
|
|
|
|
|
}
|
2009-10-14 16:10:01 +00:00
|
|
|
}
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2011-09-18 19:49:26 +00:00
|
|
|
AudioDiskstream::finish_capture (boost::shared_ptr<ChannelList> c)
|
2008-06-02 21:41:35 +00:00
|
|
|
{
|
|
|
|
|
was_recording = false;
|
2011-03-14 21:53:10 +00:00
|
|
|
first_recordable_frame = max_framepos;
|
|
|
|
|
last_recordable_frame = max_framepos;
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
if (capture_captured == 0) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (recordable() && destructive()) {
|
|
|
|
|
for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
RingBufferNPT<CaptureTransition>::rw_vector transvec;
|
|
|
|
|
(*chan)->capture_transition_buf->get_write_vector(&transvec);
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
if (transvec.len[0] > 0) {
|
|
|
|
|
transvec.buf[0]->type = CaptureEnd;
|
|
|
|
|
transvec.buf[0]->capture_val = capture_captured;
|
|
|
|
|
(*chan)->capture_transition_buf->increment_write_ptr(1);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
// bad!
|
|
|
|
|
fatal << string_compose (_("programmer error: %1"), X_("capture_transition_buf is full when stopping record! inconceivable!")) << endmsg;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2009-10-14 16:10:01 +00:00
|
|
|
|
|
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
CaptureInfo* ci = new CaptureInfo;
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
ci->start = capture_start_frame;
|
|
|
|
|
ci->frames = capture_captured;
|
2009-10-14 16:10:01 +00:00
|
|
|
|
|
|
|
|
/* XXX theoretical race condition here. Need atomic exchange ?
|
|
|
|
|
However, the circumstances when this is called right
|
2008-06-02 21:41:35 +00:00
|
|
|
now (either on record-disable or transport_stopped)
|
|
|
|
|
mean that no actual race exists. I think ...
|
|
|
|
|
We now have a capture_info_lock, but it is only to be used
|
|
|
|
|
to synchronize in the transport_stop and the capture info
|
|
|
|
|
accessors, so that invalidation will not occur (both non-realtime).
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
// cerr << "Finish capture, add new CI, " << ci->start << '+' << ci->frames << endl;
|
|
|
|
|
|
|
|
|
|
capture_info.push_back (ci);
|
|
|
|
|
capture_captured = 0;
|
|
|
|
|
|
|
|
|
|
/* now we've finished a capture, reset first_recordable_frame for next time */
|
2010-09-17 16:24:22 +00:00
|
|
|
first_recordable_frame = max_framepos;
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
AudioDiskstream::set_record_enabled (bool yn)
|
|
|
|
|
{
|
2009-06-09 20:21:19 +00:00
|
|
|
if (!recordable() || !_session.record_enabling_legal() || _io->n_ports().n_audio() == 0) {
|
2008-06-02 21:41:35 +00:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* can't rec-enable in destructive mode if transport is before start */
|
|
|
|
|
|
|
|
|
|
if (destructive() && yn && _session.transport_frame() < _session.current_start_frame()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* yes, i know that this not proof against race conditions, but its
|
|
|
|
|
good enough. i think.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
if (record_enabled() != yn) {
|
|
|
|
|
if (yn) {
|
|
|
|
|
engage_record_enable ();
|
|
|
|
|
} else {
|
|
|
|
|
disengage_record_enable ();
|
|
|
|
|
}
|
2012-12-07 19:35:38 +00:00
|
|
|
|
|
|
|
|
RecordEnableChanged (); /* EMIT SIGNAL */
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-12-07 15:48:38 +00:00
|
|
|
bool
|
|
|
|
|
AudioDiskstream::prep_record_enable ()
|
2008-06-02 21:41:35 +00:00
|
|
|
{
|
2012-12-07 15:48:38 +00:00
|
|
|
if (!recordable() || !_session.record_enabling_legal() || _io->n_ports().n_audio() == 0) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* can't rec-enable in destructive mode if transport is before start */
|
|
|
|
|
|
|
|
|
|
if (destructive() && _session.transport_frame() < _session.current_start_frame()) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
bool rolling = _session.transport_speed() != 0.0f;
|
|
|
|
|
boost::shared_ptr<ChannelList> c = channels.reader();
|
|
|
|
|
|
|
|
|
|
capturing_sources.clear ();
|
|
|
|
|
|
|
|
|
|
if (Config->get_monitoring_model() == HardwareMonitoring) {
|
|
|
|
|
|
|
|
|
|
for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
|
2013-07-24 19:29:45 -04:00
|
|
|
(*chan)->source.request_input_monitoring (!(_session.config.get_auto_input() && rolling));
|
2008-06-02 21:41:35 +00:00
|
|
|
capturing_sources.push_back ((*chan)->write_source);
|
|
|
|
|
(*chan)->write_source->mark_streaming_write_started ();
|
|
|
|
|
}
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
} 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 ();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-12-07 15:48:38 +00:00
|
|
|
return true;
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
|
|
|
|
|
2012-12-07 15:48:38 +00:00
|
|
|
bool
|
|
|
|
|
AudioDiskstream::prep_record_disable ()
|
2008-06-02 21:41:35 +00:00
|
|
|
{
|
|
|
|
|
boost::shared_ptr<ChannelList> c = channels.reader();
|
|
|
|
|
if (Config->get_monitoring_model() == HardwareMonitoring) {
|
|
|
|
|
for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
|
2013-07-24 19:29:45 -04:00
|
|
|
(*chan)->source.request_input_monitoring (false);
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
capturing_sources.clear ();
|
2012-12-07 15:48:38 +00:00
|
|
|
|
|
|
|
|
return true;
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
XMLNode&
|
|
|
|
|
AudioDiskstream::get_state ()
|
|
|
|
|
{
|
2011-03-08 20:12:40 +00:00
|
|
|
XMLNode& node (Diskstream::get_state());
|
2008-06-02 21:41:35 +00:00
|
|
|
char buf[64] = "";
|
|
|
|
|
LocaleGuard lg (X_("POSIX"));
|
|
|
|
|
|
2011-03-08 20:12:40 +00:00
|
|
|
boost::shared_ptr<ChannelList> c = channels.reader();
|
2014-01-10 17:11:10 -05:00
|
|
|
snprintf (buf, sizeof(buf), "%u", (unsigned int) c->size());
|
2011-03-08 20:12:40 +00:00
|
|
|
node.add_property ("channels", buf);
|
2011-03-14 21:53:10 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
if (!capturing_sources.empty() && _session.get_record_enabled()) {
|
|
|
|
|
|
|
|
|
|
XMLNode* cs_child = new XMLNode (X_("CapturingSources"));
|
|
|
|
|
XMLNode* cs_grandchild;
|
|
|
|
|
|
|
|
|
|
for (vector<boost::shared_ptr<AudioFileSource> >::iterator i = capturing_sources.begin(); i != capturing_sources.end(); ++i) {
|
|
|
|
|
cs_grandchild = new XMLNode (X_("file"));
|
|
|
|
|
cs_grandchild->add_property (X_("path"), (*i)->path());
|
|
|
|
|
cs_child->add_child_nocopy (*cs_grandchild);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* store the location where capture will start */
|
|
|
|
|
|
|
|
|
|
Location* pi;
|
|
|
|
|
|
2009-05-14 00:13:27 +00:00
|
|
|
if (_session.config.get_punch_in() && ((pi = _session.locations()->auto_punch_location()) != 0)) {
|
2009-07-17 22:12:21 +00:00
|
|
|
snprintf (buf, sizeof (buf), "%" PRId64, pi->start());
|
2008-06-02 21:41:35 +00:00
|
|
|
} else {
|
2009-11-08 16:28:21 +00:00
|
|
|
snprintf (buf, sizeof (buf), "%" PRId64, _session.transport_frame());
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cs_child->add_property (X_("at"), buf);
|
2011-03-08 20:12:40 +00:00
|
|
|
node.add_child_nocopy (*cs_child);
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
|
|
|
|
|
2011-03-08 20:12:40 +00:00
|
|
|
return node;
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
2011-03-08 20:12:40 +00:00
|
|
|
AudioDiskstream::set_state (const XMLNode& node, int version)
|
2008-06-02 21:41:35 +00:00
|
|
|
{
|
|
|
|
|
const XMLProperty* prop;
|
|
|
|
|
XMLNodeList nlist = node.children();
|
|
|
|
|
XMLNodeIterator niter;
|
|
|
|
|
uint32_t nchans = 1;
|
|
|
|
|
XMLNode* capture_pending_node = 0;
|
|
|
|
|
LocaleGuard lg (X_("POSIX"));
|
|
|
|
|
|
2011-03-08 20:12:40 +00:00
|
|
|
/* prevent write sources from being created */
|
|
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
in_set_state = true;
|
|
|
|
|
|
2009-10-14 16:10:01 +00:00
|
|
|
for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
|
|
|
|
|
if ((*niter)->name() == IO::state_node_name) {
|
2008-06-02 21:41:35 +00:00
|
|
|
deprecated_io_node = new XMLNode (**niter);
|
2009-10-14 16:10:01 +00:00
|
|
|
}
|
2008-06-02 21:41:35 +00:00
|
|
|
|
|
|
|
|
if ((*niter)->name() == X_("CapturingSources")) {
|
|
|
|
|
capture_pending_node = *niter;
|
|
|
|
|
}
|
2009-10-14 16:10:01 +00:00
|
|
|
}
|
2008-06-02 21:41:35 +00:00
|
|
|
|
2011-03-14 21:53:10 +00:00
|
|
|
if (Diskstream::set_state (node, version)) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
2008-06-02 21:41:35 +00:00
|
|
|
|
|
|
|
|
if ((prop = node.property ("channels")) != 0) {
|
|
|
|
|
nchans = atoi (prop->value().c_str());
|
|
|
|
|
}
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
// create necessary extra channels
|
|
|
|
|
// we are always constructed with one and we always need one
|
|
|
|
|
|
|
|
|
|
_n_channels.set(DataType::AUDIO, channels.reader()->size());
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
if (nchans > _n_channels.n_audio()) {
|
|
|
|
|
|
|
|
|
|
add_channel (nchans - _n_channels.n_audio());
|
|
|
|
|
IO::PortCountChanged(_n_channels);
|
|
|
|
|
|
|
|
|
|
} else if (nchans < _n_channels.n_audio()) {
|
|
|
|
|
|
|
|
|
|
remove_channel (_n_channels.n_audio() - nchans);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2011-03-14 21:53:10 +00:00
|
|
|
|
|
|
|
|
if (!destructive() && capture_pending_node) {
|
|
|
|
|
/* destructive streams have one and only one source per channel,
|
|
|
|
|
and so they never end up in pending capture in any useful
|
|
|
|
|
sense.
|
|
|
|
|
*/
|
|
|
|
|
use_pending_capture_data (*capture_pending_node);
|
|
|
|
|
}
|
2008-06-02 21:41:35 +00:00
|
|
|
|
|
|
|
|
in_set_state = false;
|
|
|
|
|
|
|
|
|
|
/* make sure this is clear before we do anything else */
|
|
|
|
|
|
|
|
|
|
capturing_sources.clear ();
|
|
|
|
|
|
2009-10-14 16:10:01 +00:00
|
|
|
/* write sources are handled when we handle the input set
|
2008-06-02 21:41:35 +00:00
|
|
|
up of the IO that owns this DS (::non_realtime_input_change())
|
|
|
|
|
*/
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
AudioDiskstream::use_new_write_source (uint32_t n)
|
|
|
|
|
{
|
|
|
|
|
boost::shared_ptr<ChannelList> c = channels.reader();
|
|
|
|
|
|
|
|
|
|
if (!recordable()) {
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (n >= c->size()) {
|
|
|
|
|
error << string_compose (_("AudioDiskstream: channel %1 out of range"), n) << endmsg;
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ChannelInfo* chan = (*c)[n];
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
try {
|
2011-03-14 21:53:10 +00:00
|
|
|
if ((chan->write_source = _session.create_audio_source_for_session (
|
2014-06-26 19:04:06 +02:00
|
|
|
n_channels().n_audio(), write_source_name(), n, destructive())) == 0) {
|
2008-06-02 21:41:35 +00:00
|
|
|
throw failed_constructor();
|
|
|
|
|
}
|
2009-10-14 16:10:01 +00:00
|
|
|
}
|
2008-06-02 21:41:35 +00:00
|
|
|
|
|
|
|
|
catch (failed_constructor &err) {
|
|
|
|
|
error << string_compose (_("%1:%2 new capture file not initialized correctly"), _name, n) << endmsg;
|
|
|
|
|
chan->write_source.reset ();
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* do not remove destructive files even if they are empty */
|
|
|
|
|
|
|
|
|
|
chan->write_source->set_allow_remove_if_empty (!destructive());
|
2011-06-01 16:50:12 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2009-07-21 15:55:17 +00:00
|
|
|
AudioDiskstream::reset_write_sources (bool mark_write_complete, bool /*force*/)
|
2008-06-02 21:41:35 +00:00
|
|
|
{
|
|
|
|
|
ChannelList::iterator chan;
|
|
|
|
|
boost::shared_ptr<ChannelList> c = channels.reader();
|
|
|
|
|
uint32_t n;
|
|
|
|
|
|
2009-10-13 20:43:28 +00:00
|
|
|
if (!_session.writable() || !recordable()) {
|
2008-06-02 21:41:35 +00:00
|
|
|
return;
|
|
|
|
|
}
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
capturing_sources.clear ();
|
|
|
|
|
|
|
|
|
|
for (chan = c->begin(), n = 0; chan != c->end(); ++chan, ++n) {
|
2010-07-22 14:52:05 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
if (!destructive()) {
|
|
|
|
|
|
2010-08-04 00:01:55 +00:00
|
|
|
if ((*chan)->write_source) {
|
|
|
|
|
|
|
|
|
|
if (mark_write_complete) {
|
|
|
|
|
(*chan)->write_source->mark_streaming_write_completed ();
|
|
|
|
|
(*chan)->write_source->done_with_peakfile_writes ();
|
|
|
|
|
}
|
2010-07-22 14:52:05 +00:00
|
|
|
|
2011-03-14 21:53:10 +00:00
|
|
|
if ((*chan)->write_source->removable()) {
|
|
|
|
|
(*chan)->write_source->mark_for_remove ();
|
|
|
|
|
(*chan)->write_source->drop_references ();
|
|
|
|
|
}
|
2011-06-01 16:50:12 +00:00
|
|
|
|
2011-03-14 21:53:10 +00:00
|
|
|
(*chan)->write_source.reset ();
|
|
|
|
|
}
|
2010-07-22 14:52:05 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
use_new_write_source (n);
|
|
|
|
|
|
|
|
|
|
if (record_enabled()) {
|
|
|
|
|
capturing_sources.push_back ((*chan)->write_source);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} else {
|
2010-07-22 14:52:05 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
if ((*chan)->write_source == 0) {
|
|
|
|
|
use_new_write_source (n);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2010-03-22 22:14:54 +00:00
|
|
|
if (destructive() && !c->empty ()) {
|
2008-06-02 21:41:35 +00:00
|
|
|
|
|
|
|
|
/* we now have all our write sources set up, so create the
|
|
|
|
|
playlist's single region.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
if (_playlist->empty()) {
|
|
|
|
|
setup_destructive_playlist ();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2010-12-03 22:26:29 +00:00
|
|
|
AudioDiskstream::set_block_size (pframes_t /*nframes*/)
|
2008-06-02 21:41:35 +00:00
|
|
|
{
|
|
|
|
|
if (_session.get_block_size() > speed_buffer_size) {
|
|
|
|
|
speed_buffer_size = _session.get_block_size();
|
|
|
|
|
boost::shared_ptr<ChannelList> c = channels.reader();
|
|
|
|
|
|
|
|
|
|
for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
|
The great audio processing overhaul.
The vast majority of Route signal processing is now simply in the list of
processors. There are definitely regressions here, but there's also
a lot of things fixed. It's far too much work to let diverge anymore
regardless, so here it is.
The basic model is: A route has a fixed set of input channels (matching
its JACK input ports and diskstream). The first processor takes this
as input. The next processor is configured using the first processor's
output as input, and is allowed to choose whatever output it wants
given that input... and so on, and so on. Finally, the last processor's
requested output is used to set up the panner and create whatever Jack
ports are needed to output the data.
All 'special' internal processors (meter, fader, amp, insert, send) are
currently transparent: they read any input, and return the same set
of channels back (unmodified, except for amp).
User visible changes:
* LV2 Instrument support (tracks with both MIDI and audio channels)
* MIDI in/out plugin support
* Generic plugin replication (for MIDI plugins, MIDI/audio plugins)
* Movable meter point
Known Bugs:
* Things seem to get weird on loaded sessions
* Output delivery is sketchy
* 2.0 session loading was probably already broken...
but it's definitely broken now :)
Please test this and file bugs if you have any time...
git-svn-id: svn://localhost/ardour2/branches/3.0@5055 d708f5d6-7413-0410-9779-e7cbd77b26cf
2009-05-07 06:30:50 +00:00
|
|
|
if ((*chan)->speed_buffer)
|
|
|
|
|
delete [] (*chan)->speed_buffer;
|
2008-06-02 21:41:35 +00:00
|
|
|
(*chan)->speed_buffer = new Sample[speed_buffer_size];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
allocate_temporary_buffers ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
AudioDiskstream::allocate_temporary_buffers ()
|
|
|
|
|
{
|
|
|
|
|
/* make sure the wrap buffer is at least large enough to deal
|
|
|
|
|
with the speeds up to 1.2, to allow for micro-variation
|
2009-10-26 14:38:58 +00:00
|
|
|
when slaving to MTC, Timecode etc.
|
2008-06-02 21:41:35 +00:00
|
|
|
*/
|
|
|
|
|
|
2010-04-21 20:42:22 +00:00
|
|
|
double const sp = max (fabsf (_actual_speed), 1.2f);
|
2012-12-23 14:25:12 +00:00
|
|
|
framecnt_t required_wrap_size = (framecnt_t) ceil (_session.get_block_size() * sp) + 2;
|
2008-06-02 21:41:35 +00:00
|
|
|
|
|
|
|
|
if (required_wrap_size > wrap_buffer_size) {
|
|
|
|
|
|
|
|
|
|
boost::shared_ptr<ChannelList> c = channels.reader();
|
|
|
|
|
|
|
|
|
|
for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
|
2011-10-31 21:16:44 +00:00
|
|
|
if ((*chan)->playback_wrap_buffer) {
|
The great audio processing overhaul.
The vast majority of Route signal processing is now simply in the list of
processors. There are definitely regressions here, but there's also
a lot of things fixed. It's far too much work to let diverge anymore
regardless, so here it is.
The basic model is: A route has a fixed set of input channels (matching
its JACK input ports and diskstream). The first processor takes this
as input. The next processor is configured using the first processor's
output as input, and is allowed to choose whatever output it wants
given that input... and so on, and so on. Finally, the last processor's
requested output is used to set up the panner and create whatever Jack
ports are needed to output the data.
All 'special' internal processors (meter, fader, amp, insert, send) are
currently transparent: they read any input, and return the same set
of channels back (unmodified, except for amp).
User visible changes:
* LV2 Instrument support (tracks with both MIDI and audio channels)
* MIDI in/out plugin support
* Generic plugin replication (for MIDI plugins, MIDI/audio plugins)
* Movable meter point
Known Bugs:
* Things seem to get weird on loaded sessions
* Output delivery is sketchy
* 2.0 session loading was probably already broken...
but it's definitely broken now :)
Please test this and file bugs if you have any time...
git-svn-id: svn://localhost/ardour2/branches/3.0@5055 d708f5d6-7413-0410-9779-e7cbd77b26cf
2009-05-07 06:30:50 +00:00
|
|
|
delete [] (*chan)->playback_wrap_buffer;
|
2011-10-31 21:16:44 +00:00
|
|
|
}
|
2009-10-14 16:10:01 +00:00
|
|
|
(*chan)->playback_wrap_buffer = new Sample[required_wrap_size];
|
2011-10-31 21:16:44 +00:00
|
|
|
if ((*chan)->capture_wrap_buffer) {
|
The great audio processing overhaul.
The vast majority of Route signal processing is now simply in the list of
processors. There are definitely regressions here, but there's also
a lot of things fixed. It's far too much work to let diverge anymore
regardless, so here it is.
The basic model is: A route has a fixed set of input channels (matching
its JACK input ports and diskstream). The first processor takes this
as input. The next processor is configured using the first processor's
output as input, and is allowed to choose whatever output it wants
given that input... and so on, and so on. Finally, the last processor's
requested output is used to set up the panner and create whatever Jack
ports are needed to output the data.
All 'special' internal processors (meter, fader, amp, insert, send) are
currently transparent: they read any input, and return the same set
of channels back (unmodified, except for amp).
User visible changes:
* LV2 Instrument support (tracks with both MIDI and audio channels)
* MIDI in/out plugin support
* Generic plugin replication (for MIDI plugins, MIDI/audio plugins)
* Movable meter point
Known Bugs:
* Things seem to get weird on loaded sessions
* Output delivery is sketchy
* 2.0 session loading was probably already broken...
but it's definitely broken now :)
Please test this and file bugs if you have any time...
git-svn-id: svn://localhost/ardour2/branches/3.0@5055 d708f5d6-7413-0410-9779-e7cbd77b26cf
2009-05-07 06:30:50 +00:00
|
|
|
delete [] (*chan)->capture_wrap_buffer;
|
2011-10-31 21:16:44 +00:00
|
|
|
}
|
2009-10-14 16:10:01 +00:00
|
|
|
(*chan)->capture_wrap_buffer = new Sample[required_wrap_size];
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
wrap_buffer_size = required_wrap_size;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2013-07-24 19:29:45 -04:00
|
|
|
AudioDiskstream::request_input_monitoring (bool yn)
|
2008-06-02 21:41:35 +00:00
|
|
|
{
|
|
|
|
|
boost::shared_ptr<ChannelList> c = channels.reader();
|
|
|
|
|
|
|
|
|
|
for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
|
2013-07-24 19:29:45 -04:00
|
|
|
(*chan)->source.request_input_monitoring (yn);
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
AudioDiskstream::set_align_style_from_io ()
|
|
|
|
|
{
|
|
|
|
|
bool have_physical = false;
|
|
|
|
|
|
2011-03-14 21:53:10 +00:00
|
|
|
if (_alignment_choice != Automatic) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2011-03-09 05:19:44 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
if (_io == 0) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
get_input_sources ();
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
boost::shared_ptr<ChannelList> c = channels.reader();
|
|
|
|
|
|
|
|
|
|
for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
|
2011-03-15 19:32:21 +00:00
|
|
|
if ((*chan)->source.is_physical ()) {
|
2008-06-02 21:41:35 +00:00
|
|
|
have_physical = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (have_physical) {
|
|
|
|
|
set_align_style (ExistingMaterial);
|
|
|
|
|
} else {
|
|
|
|
|
set_align_style (CaptureTime);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
AudioDiskstream::add_channel_to (boost::shared_ptr<ChannelList> c, uint32_t how_many)
|
|
|
|
|
{
|
|
|
|
|
while (how_many--) {
|
2011-03-14 21:53:10 +00:00
|
|
|
c->push_back (new ChannelInfo(
|
2011-06-01 16:50:12 +00:00
|
|
|
_session.butler()->audio_diskstream_playback_buffer_size(),
|
2011-03-14 21:53:10 +00:00
|
|
|
_session.butler()->audio_diskstream_capture_buffer_size(),
|
|
|
|
|
speed_buffer_size, wrap_buffer_size));
|
|
|
|
|
interpolation.add_channel_to (
|
|
|
|
|
_session.butler()->audio_diskstream_playback_buffer_size(),
|
|
|
|
|
speed_buffer_size);
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_n_channels.set(DataType::AUDIO, c->size());
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
AudioDiskstream::add_channel (uint32_t how_many)
|
|
|
|
|
{
|
|
|
|
|
RCUWriter<ChannelList> writer (channels);
|
|
|
|
|
boost::shared_ptr<ChannelList> c = writer.get_copy();
|
|
|
|
|
|
|
|
|
|
return add_channel_to (c, how_many);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
AudioDiskstream::remove_channel_from (boost::shared_ptr<ChannelList> c, uint32_t how_many)
|
|
|
|
|
{
|
|
|
|
|
while (how_many-- && !c->empty()) {
|
2010-03-30 15:18:43 +00:00
|
|
|
delete c->back();
|
2008-06-02 21:41:35 +00:00
|
|
|
c->pop_back();
|
2009-06-23 09:50:02 +00:00
|
|
|
interpolation.remove_channel_from ();
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_n_channels.set(DataType::AUDIO, c->size());
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
AudioDiskstream::remove_channel (uint32_t how_many)
|
|
|
|
|
{
|
|
|
|
|
RCUWriter<ChannelList> writer (channels);
|
|
|
|
|
boost::shared_ptr<ChannelList> c = writer.get_copy();
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
return remove_channel_from (c, how_many);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float
|
|
|
|
|
AudioDiskstream::playback_buffer_load () const
|
|
|
|
|
{
|
|
|
|
|
boost::shared_ptr<ChannelList> c = channels.reader();
|
|
|
|
|
|
2011-01-02 12:45:20 +00:00
|
|
|
if (c->empty ()) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
return (float) ((double) c->front()->playback_buf->read_space()/
|
|
|
|
|
(double) c->front()->playback_buf->bufsize());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float
|
|
|
|
|
AudioDiskstream::capture_buffer_load () const
|
|
|
|
|
{
|
|
|
|
|
boost::shared_ptr<ChannelList> c = channels.reader();
|
|
|
|
|
|
2011-01-02 12:45:20 +00:00
|
|
|
if (c->empty ()) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2011-06-01 16:50:12 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
return (float) ((double) c->front()->capture_buf->write_space()/
|
|
|
|
|
(double) c->front()->capture_buf->bufsize());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
AudioDiskstream::use_pending_capture_data (XMLNode& node)
|
|
|
|
|
{
|
|
|
|
|
const XMLProperty* prop;
|
|
|
|
|
XMLNodeList nlist = node.children();
|
|
|
|
|
XMLNodeIterator niter;
|
|
|
|
|
boost::shared_ptr<AudioFileSource> fs;
|
|
|
|
|
boost::shared_ptr<AudioFileSource> first_fs;
|
|
|
|
|
SourceList pending_sources;
|
2010-09-17 16:24:22 +00:00
|
|
|
framepos_t position;
|
2008-06-02 21:41:35 +00:00
|
|
|
|
|
|
|
|
if ((prop = node.property (X_("at"))) == 0) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2010-09-17 16:24:22 +00:00
|
|
|
if (sscanf (prop->value().c_str(), "%" PRIu64, &position) != 1) {
|
2008-06-02 21:41:35 +00:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
|
|
|
|
|
if ((*niter)->name() == X_("file")) {
|
|
|
|
|
|
|
|
|
|
if ((prop = (*niter)->property (X_("path"))) == 0) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// This protects sessions from errant CapturingSources in stored sessions
|
|
|
|
|
struct stat sbuf;
|
|
|
|
|
if (stat (prop->value().c_str(), &sbuf)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2014-06-10 10:07:04 -04:00
|
|
|
/* XXX as of June 2014, we always record to mono
|
|
|
|
|
files. Since this Source is being created as part of
|
|
|
|
|
crash recovery, we know that we need the first
|
|
|
|
|
channel (the final argument to the SourceFactory
|
|
|
|
|
call below). If we ever support non-mono files for
|
|
|
|
|
capture, this will need rethinking.
|
|
|
|
|
*/
|
|
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
try {
|
2014-06-10 10:07:04 -04:00
|
|
|
fs = boost::dynamic_pointer_cast<AudioFileSource> (SourceFactory::createForRecovery (DataType::AUDIO, _session, prop->value(), 0));
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
catch (failed_constructor& err) {
|
|
|
|
|
error << string_compose (_("%1: cannot restore pending capture source file %2"),
|
|
|
|
|
_name, prop->value())
|
|
|
|
|
<< endmsg;
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pending_sources.push_back (fs);
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
if (first_fs == 0) {
|
|
|
|
|
first_fs = fs;
|
|
|
|
|
}
|
|
|
|
|
|
2010-02-11 23:10:29 +00:00
|
|
|
fs->set_captured_for (_name.val());
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pending_sources.size() == 0) {
|
|
|
|
|
/* nothing can be done */
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
2011-06-01 16:50:12 +00:00
|
|
|
|
2014-06-10 10:07:04 -04:00
|
|
|
boost::shared_ptr<AudioRegion> wf_region;
|
|
|
|
|
boost::shared_ptr<AudioRegion> region;
|
|
|
|
|
|
|
|
|
|
/* First create the whole file region */
|
2011-06-01 16:50:12 +00:00
|
|
|
|
2014-06-10 10:07:04 -04:00
|
|
|
PropertyList plist;
|
|
|
|
|
|
2010-02-18 13:59:49 +00:00
|
|
|
plist.add (Properties::start, 0);
|
|
|
|
|
plist.add (Properties::length, first_fs->length (first_fs->timeline_position()));
|
|
|
|
|
plist.add (Properties::name, region_name_from_path (first_fs->name(), true));
|
|
|
|
|
|
2014-06-10 10:07:04 -04:00
|
|
|
wf_region = boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (pending_sources, plist));
|
|
|
|
|
|
|
|
|
|
wf_region->set_automatic (true);
|
|
|
|
|
wf_region->set_whole_file (true);
|
|
|
|
|
wf_region->special_set_position (position);
|
2010-02-18 13:59:49 +00:00
|
|
|
|
2014-06-10 10:07:04 -04:00
|
|
|
/* Now create a region that isn't the whole file for adding to
|
|
|
|
|
* the playlist */
|
|
|
|
|
|
|
|
|
|
region = boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (pending_sources, plist));
|
|
|
|
|
|
|
|
|
|
_playlist->add_region (region, position);
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
catch (failed_constructor& err) {
|
2009-02-17 02:11:49 +00:00
|
|
|
error << string_compose (
|
|
|
|
|
_("%1: cannot create whole-file region from pending capture sources"),
|
|
|
|
|
_name) << endmsg;
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2009-04-20 21:02:46 +00:00
|
|
|
int
|
|
|
|
|
AudioDiskstream::set_non_layered (bool yn)
|
|
|
|
|
{
|
|
|
|
|
if (yn != non_layered()) {
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2009-04-20 21:02:46 +00:00
|
|
|
if (yn) {
|
|
|
|
|
_flags = Flag (_flags | NonLayered);
|
|
|
|
|
} else {
|
|
|
|
|
_flags = Flag (_flags & ~NonLayered);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
int
|
|
|
|
|
AudioDiskstream::set_destructive (bool yn)
|
|
|
|
|
{
|
|
|
|
|
if (yn != destructive()) {
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
if (yn) {
|
2010-02-23 20:25:53 +00:00
|
|
|
bool bounce_ignored;
|
2008-06-02 21:41:35 +00:00
|
|
|
/* requestor should already have checked this and
|
2009-10-14 16:10:01 +00:00
|
|
|
bounced if necessary and desired
|
2008-06-02 21:41:35 +00:00
|
|
|
*/
|
|
|
|
|
if (!can_become_destructive (bounce_ignored)) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
_flags = Flag (_flags | Destructive);
|
|
|
|
|
use_destructive_playlist ();
|
|
|
|
|
} else {
|
|
|
|
|
_flags = Flag (_flags & ~Destructive);
|
|
|
|
|
reset_write_sources (true, true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
AudioDiskstream::can_become_destructive (bool& requires_bounce) const
|
|
|
|
|
{
|
|
|
|
|
if (!_playlist) {
|
|
|
|
|
requires_bounce = false;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* is there only one region ? */
|
|
|
|
|
|
|
|
|
|
if (_playlist->n_regions() != 1) {
|
|
|
|
|
requires_bounce = true;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
boost::shared_ptr<Region> first = _playlist->find_next_region (_session.current_start_frame(), Start, 1);
|
2010-03-23 13:11:14 +00:00
|
|
|
if (!first) {
|
|
|
|
|
requires_bounce = false;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2008-06-02 21:41:35 +00:00
|
|
|
|
|
|
|
|
/* do the source(s) for the region cover the session start position ? */
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
if (first->position() != _session.current_start_frame()) {
|
|
|
|
|
if (first->start() > _session.current_start_frame()) {
|
|
|
|
|
requires_bounce = true;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* is the source used by only 1 playlist ? */
|
|
|
|
|
|
|
|
|
|
boost::shared_ptr<AudioRegion> afirst = boost::dynamic_pointer_cast<AudioRegion> (first);
|
|
|
|
|
|
|
|
|
|
assert (afirst);
|
|
|
|
|
|
2009-12-04 19:09:08 +00:00
|
|
|
if (_session.playlists->source_use_count (afirst->source()) > 1) {
|
2009-10-14 16:10:01 +00:00
|
|
|
requires_bounce = true;
|
2008-06-02 21:41:35 +00:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
requires_bounce = false;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2011-06-01 16:50:12 +00:00
|
|
|
void
|
2010-06-09 17:24:07 +00:00
|
|
|
AudioDiskstream::adjust_playback_buffering ()
|
|
|
|
|
{
|
|
|
|
|
boost::shared_ptr<ChannelList> c = channels.reader();
|
|
|
|
|
|
|
|
|
|
for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
|
2011-03-14 21:53:10 +00:00
|
|
|
(*chan)->resize_playback (_session.butler()->audio_diskstream_playback_buffer_size());
|
|
|
|
|
}
|
2010-06-09 17:24:07 +00:00
|
|
|
}
|
|
|
|
|
|
2011-06-01 16:50:12 +00:00
|
|
|
void
|
2010-06-09 17:24:07 +00:00
|
|
|
AudioDiskstream::adjust_capture_buffering ()
|
|
|
|
|
{
|
|
|
|
|
boost::shared_ptr<ChannelList> c = channels.reader();
|
|
|
|
|
|
|
|
|
|
for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
|
2011-03-14 21:53:10 +00:00
|
|
|
(*chan)->resize_capture (_session.butler()->audio_diskstream_capture_buffer_size());
|
|
|
|
|
}
|
2010-06-09 17:24:07 +00:00
|
|
|
}
|
|
|
|
|
|
2011-03-15 19:32:21 +00:00
|
|
|
bool
|
|
|
|
|
AudioDiskstream::ChannelSource::is_physical () const
|
|
|
|
|
{
|
2011-04-04 23:00:48 +00:00
|
|
|
if (name.empty()) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2011-03-15 19:32:21 +00:00
|
|
|
|
2011-04-04 23:00:48 +00:00
|
|
|
return AudioEngine::instance()->port_is_physical (name);
|
2011-03-15 19:32:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2013-07-24 19:29:45 -04:00
|
|
|
AudioDiskstream::ChannelSource::request_input_monitoring (bool yn) const
|
2011-03-15 19:32:21 +00:00
|
|
|
{
|
2011-04-04 23:00:48 +00:00
|
|
|
if (name.empty()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2011-03-15 19:32:21 +00:00
|
|
|
|
2013-07-24 19:29:45 -04:00
|
|
|
return AudioEngine::instance()->request_input_monitoring (name, yn);
|
2011-03-15 19:32:21 +00:00
|
|
|
}
|
|
|
|
|
|
2010-12-03 22:26:29 +00:00
|
|
|
AudioDiskstream::ChannelInfo::ChannelInfo (framecnt_t playback_bufsize, framecnt_t capture_bufsize, framecnt_t speed_size, framecnt_t wrap_size)
|
2008-06-02 21:41:35 +00:00
|
|
|
{
|
|
|
|
|
current_capture_buffer = 0;
|
|
|
|
|
current_playback_buffer = 0;
|
|
|
|
|
curr_capture_cnt = 0;
|
|
|
|
|
|
|
|
|
|
speed_buffer = new Sample[speed_size];
|
|
|
|
|
playback_wrap_buffer = new Sample[wrap_size];
|
|
|
|
|
capture_wrap_buffer = new Sample[wrap_size];
|
|
|
|
|
|
2010-06-09 14:11:46 +00:00
|
|
|
playback_buf = new RingBufferNPT<Sample> (playback_bufsize);
|
|
|
|
|
capture_buf = new RingBufferNPT<Sample> (capture_bufsize);
|
2008-06-02 21:41:35 +00:00
|
|
|
capture_transition_buf = new RingBufferNPT<CaptureTransition> (256);
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
/* touch the ringbuffer buffers, which will cause
|
|
|
|
|
them to be mapped into locked physical RAM if
|
|
|
|
|
we're running with mlockall(). this doesn't do
|
2009-10-14 16:10:01 +00:00
|
|
|
much if we're not.
|
2008-06-02 21:41:35 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
memset (playback_buf->buffer(), 0, sizeof (Sample) * playback_buf->bufsize());
|
|
|
|
|
memset (capture_buf->buffer(), 0, sizeof (Sample) * capture_buf->bufsize());
|
|
|
|
|
memset (capture_transition_buf->buffer(), 0, sizeof (CaptureTransition) * capture_transition_buf->bufsize());
|
|
|
|
|
}
|
|
|
|
|
|
2010-06-09 17:24:07 +00:00
|
|
|
void
|
2010-12-03 22:26:29 +00:00
|
|
|
AudioDiskstream::ChannelInfo::resize_playback (framecnt_t playback_bufsize)
|
2010-06-09 17:24:07 +00:00
|
|
|
{
|
2011-03-14 21:53:10 +00:00
|
|
|
delete playback_buf;
|
2010-06-09 17:24:07 +00:00
|
|
|
playback_buf = new RingBufferNPT<Sample> (playback_bufsize);
|
|
|
|
|
memset (playback_buf->buffer(), 0, sizeof (Sample) * playback_buf->bufsize());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2010-12-03 22:26:29 +00:00
|
|
|
AudioDiskstream::ChannelInfo::resize_capture (framecnt_t capture_bufsize)
|
2010-06-09 17:24:07 +00:00
|
|
|
{
|
2011-03-14 21:53:10 +00:00
|
|
|
delete capture_buf;
|
2010-06-23 20:14:07 +00:00
|
|
|
|
2010-06-09 17:24:07 +00:00
|
|
|
capture_buf = new RingBufferNPT<Sample> (capture_bufsize);
|
|
|
|
|
memset (capture_buf->buffer(), 0, sizeof (Sample) * capture_buf->bufsize());
|
|
|
|
|
}
|
|
|
|
|
|
2008-06-02 21:41:35 +00:00
|
|
|
AudioDiskstream::ChannelInfo::~ChannelInfo ()
|
|
|
|
|
{
|
2011-03-14 21:53:10 +00:00
|
|
|
write_source.reset ();
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-12-18 19:31:00 +00:00
|
|
|
delete [] speed_buffer;
|
|
|
|
|
speed_buffer = 0;
|
2008-06-02 21:41:35 +00:00
|
|
|
|
2008-12-18 19:31:00 +00:00
|
|
|
delete [] playback_wrap_buffer;
|
|
|
|
|
playback_wrap_buffer = 0;
|
2008-06-02 21:41:35 +00:00
|
|
|
|
2008-12-18 19:31:00 +00:00
|
|
|
delete [] capture_wrap_buffer;
|
|
|
|
|
capture_wrap_buffer = 0;
|
2009-10-14 16:10:01 +00:00
|
|
|
|
2008-12-18 19:31:00 +00:00
|
|
|
delete playback_buf;
|
|
|
|
|
playback_buf = 0;
|
2008-06-02 21:41:35 +00:00
|
|
|
|
2008-12-18 19:31:00 +00:00
|
|
|
delete capture_buf;
|
|
|
|
|
capture_buf = 0;
|
2008-06-02 21:41:35 +00:00
|
|
|
|
2008-12-18 19:31:00 +00:00
|
|
|
delete capture_transition_buf;
|
|
|
|
|
capture_transition_buf = 0;
|
2008-06-02 21:41:35 +00:00
|
|
|
}
|
2010-06-09 17:24:07 +00:00
|
|
|
|
2011-05-08 23:45:26 +00:00
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
AudioDiskstream::set_name (string const & name)
|
|
|
|
|
{
|
2014-06-26 19:04:06 +02:00
|
|
|
if (_name == name) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2011-05-08 23:45:26 +00:00
|
|
|
Diskstream::set_name (name);
|
|
|
|
|
|
|
|
|
|
/* get a new write source so that its name reflects the new diskstream name */
|
|
|
|
|
|
|
|
|
|
boost::shared_ptr<ChannelList> c = channels.reader();
|
|
|
|
|
ChannelList::iterator i;
|
|
|
|
|
int n = 0;
|
2011-06-01 16:50:12 +00:00
|
|
|
|
2011-05-08 23:45:26 +00:00
|
|
|
for (n = 0, i = c->begin(); i != c->end(); ++i, ++n) {
|
|
|
|
|
use_new_write_source (n);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2014-06-26 19:04:06 +02:00
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
AudioDiskstream::set_write_source_name (const std::string& str) {
|
|
|
|
|
if (_write_source_name == str) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Diskstream::set_write_source_name (str);
|
|
|
|
|
|
|
|
|
|
if (_write_source_name == name()) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
boost::shared_ptr<ChannelList> c = channels.reader();
|
|
|
|
|
ChannelList::iterator i;
|
|
|
|
|
int n = 0;
|
|
|
|
|
|
|
|
|
|
for (n = 0, i = c->begin(); i != c->end(); ++i, ++n) {
|
|
|
|
|
use_new_write_source (n);
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|