mirror of
https://github.com/Ardour/ardour.git
synced 2025-12-15 19:16:40 +01:00
Actually added the code mentioned in my last commit. Whoops.
git-svn-id: svn://localhost/ardour2/branches/midi@643 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
parent
2336aa1a50
commit
b7f3a63507
15 changed files with 5281 additions and 0 deletions
143
libs/ardour/ardour/buffer.h
Normal file
143
libs/ardour/ardour/buffer.h
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
/*
|
||||
Copyright (C) 2006 Paul Davis
|
||||
Written by Dave Robillard, 2006
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
#ifndef __ardour_buffer_h__
|
||||
#define __ardour_buffer_h__
|
||||
|
||||
#define _XOPEN_SOURCE 600
|
||||
#include <cstdlib> // for posix_memalign
|
||||
#include <cassert>
|
||||
#include <ardour/types.h>
|
||||
#include <jack/jack.h>
|
||||
|
||||
namespace ARDOUR {
|
||||
|
||||
|
||||
/** A buffer of recordable/playable data.
|
||||
*
|
||||
* This is a datatype-agnostic base class for all buffers (there are no
|
||||
* methods to actually access the data). This provides a way for code that
|
||||
* doesn't care about the data type to still deal with buffers (which is
|
||||
* why the base class can't be a template).
|
||||
*
|
||||
* To actually read/write buffer contents, use the appropriate derived class.
|
||||
*/
|
||||
class Buffer
|
||||
{
|
||||
public:
|
||||
/** Unfortunately using RTTI and dynamic_cast to find the type of the
|
||||
* buffer is just too slow, this is done in very performance critical
|
||||
* bits of the code. */
|
||||
enum Type { NIL = 0, AUDIO, MIDI };
|
||||
|
||||
Buffer(Type type, size_t capacity)
|
||||
: _type(type), _capacity(capacity), _size(0)
|
||||
{}
|
||||
|
||||
virtual ~Buffer() {}
|
||||
|
||||
/** Maximum capacity of buffer.
|
||||
* Note in some cases the entire buffer may not contain valid data, use size. */
|
||||
size_t capacity() const { return _capacity; }
|
||||
|
||||
/** Amount of valid data in buffer. Use this over capacity almost always. */
|
||||
size_t size() const { return _size; }
|
||||
|
||||
/** Type of this buffer.
|
||||
* Based on this you can cast a Buffer* to the desired type. */
|
||||
virtual Type type() const { return _type; }
|
||||
|
||||
/** Jack type (eg JACK_DEFAULT_AUDIO_TYPE) */
|
||||
const char* jack_type() const { return type_to_jack_type(type()); }
|
||||
|
||||
/** Separate for creating ports (before a buffer exists to call jack_type on) */
|
||||
static const char* type_to_jack_type(Type t) {
|
||||
switch (t) {
|
||||
case AUDIO: return JACK_DEFAULT_AUDIO_TYPE;
|
||||
case MIDI: return JACK_DEFAULT_MIDI_TYPE;
|
||||
default: return "";
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
Type _type;
|
||||
size_t _capacity;
|
||||
size_t _size;
|
||||
};
|
||||
|
||||
|
||||
/* Since we only have two types, templates aren't worth it, yet.. */
|
||||
|
||||
|
||||
/** Buffer containing 32-bit floating point (audio) data. */
|
||||
class AudioBuffer : public Buffer
|
||||
{
|
||||
public:
|
||||
AudioBuffer(size_t capacity)
|
||||
: Buffer(AUDIO, capacity)
|
||||
, _data(NULL)
|
||||
{
|
||||
_size = capacity; // For audio buffers, size = capacity always
|
||||
posix_memalign((void**)_data, 16, sizeof(Sample) * capacity);
|
||||
assert(_data);
|
||||
memset(_data, 0, sizeof(Sample) * capacity);
|
||||
}
|
||||
|
||||
const Sample* data() const { return _data; }
|
||||
Sample* data() { return _data; }
|
||||
|
||||
private:
|
||||
// These are undefined (prevent copies)
|
||||
AudioBuffer(const AudioBuffer& copy);
|
||||
AudioBuffer& operator=(const AudioBuffer& copy);
|
||||
|
||||
Sample* const _data; ///< Actual buffer contents
|
||||
};
|
||||
|
||||
|
||||
|
||||
/** Buffer containing 8-bit unsigned char (MIDI) data. */
|
||||
class MidiBuffer : public Buffer
|
||||
{
|
||||
public:
|
||||
MidiBuffer(size_t capacity)
|
||||
: Buffer(MIDI, capacity)
|
||||
, _data(NULL)
|
||||
{
|
||||
posix_memalign((void**)_data, 16, sizeof(RawMidi) * capacity);
|
||||
assert(_data);
|
||||
assert(_size == 0);
|
||||
memset(_data, 0, sizeof(Sample) * capacity);
|
||||
}
|
||||
|
||||
const RawMidi* data() const { return _data; }
|
||||
RawMidi* data() { return _data; }
|
||||
|
||||
private:
|
||||
// These are undefined (prevent copies)
|
||||
MidiBuffer(const MidiBuffer& copy);
|
||||
MidiBuffer& operator=(const MidiBuffer& copy);
|
||||
|
||||
RawMidi* const _data; ///< Actual buffer contents
|
||||
};
|
||||
|
||||
|
||||
} // namespace ARDOUR
|
||||
|
||||
#endif // __ardour_buffer_h__
|
||||
341
libs/ardour/ardour/diskstream.h
Normal file
341
libs/ardour/ardour/diskstream.h
Normal file
|
|
@ -0,0 +1,341 @@
|
|||
/*
|
||||
Copyright (C) 2000 Paul Davis
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
$Id: diskstream.h 579 2006-06-12 19:56:37Z essej $
|
||||
*/
|
||||
|
||||
#ifndef __ardour_diskstream_h__
|
||||
#define __ardour_diskstream_h__
|
||||
|
||||
#include <sigc++/signal.h>
|
||||
|
||||
#include <cmath>
|
||||
#include <string>
|
||||
#include <queue>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#include <pbd/fastlog.h>
|
||||
#include <pbd/ringbufferNPT.h>
|
||||
|
||||
|
||||
#include <ardour/ardour.h>
|
||||
#include <ardour/configuration.h>
|
||||
#include <ardour/session.h>
|
||||
#include <ardour/route_group.h>
|
||||
#include <ardour/route.h>
|
||||
#include <ardour/port.h>
|
||||
#include <ardour/utils.h>
|
||||
#include <ardour/stateful.h>
|
||||
|
||||
struct tm;
|
||||
|
||||
namespace ARDOUR {
|
||||
|
||||
class AudioEngine;
|
||||
class Send;
|
||||
class Session;
|
||||
class Playlist;
|
||||
//class FileSource;
|
||||
class IO;
|
||||
|
||||
/* FIXME: There are (obviously) far too many virtual functions in this ATM.
|
||||
* Just to get things off the ground, they'll be removed. */
|
||||
|
||||
class Diskstream : public Stateful, public sigc::trackable
|
||||
{
|
||||
public:
|
||||
enum Flag {
|
||||
Recordable = 0x1,
|
||||
Hidden = 0x2,
|
||||
Destructive = 0x4
|
||||
};
|
||||
|
||||
Diskstream (Session &, const string& name, Flag f = Recordable);
|
||||
Diskstream (Session &, const XMLNode&);
|
||||
|
||||
string name () const { return _name; }
|
||||
virtual int set_name (string str, void* src);
|
||||
|
||||
ARDOUR::IO* io() const { return _io; }
|
||||
virtual void set_io (ARDOUR::IO& io) = 0;
|
||||
|
||||
virtual Diskstream& ref() { _refcnt++; return *this; }
|
||||
void unref() { if (_refcnt) _refcnt--; if (_refcnt == 0) delete this; }
|
||||
uint32_t refcnt() const { return _refcnt; }
|
||||
|
||||
void set_flag (Flag f) { _flags |= f; }
|
||||
void unset_flag (Flag f) { _flags &= ~f; }
|
||||
|
||||
AlignStyle alignment_style() const { return _alignment_style; }
|
||||
void set_align_style (AlignStyle);
|
||||
void set_persistent_align_style (AlignStyle a) { _persistent_alignment_style = a; }
|
||||
|
||||
jack_nframes_t roll_delay() const { return _roll_delay; }
|
||||
void set_roll_delay (jack_nframes_t);
|
||||
|
||||
bool record_enabled() const { return g_atomic_int_get (&_record_enabled); }
|
||||
virtual void set_record_enabled (bool yn, void *src) = 0;
|
||||
|
||||
bool destructive() const { return _flags & Destructive; }
|
||||
virtual void set_destructive (bool yn);
|
||||
|
||||
id_t id() const { return _id; }
|
||||
bool hidden() const { return _flags & Hidden; }
|
||||
bool recordable() const { return _flags & Recordable; }
|
||||
bool reversed() const { return _actual_speed < 0.0f; }
|
||||
double speed() const { return _visible_speed; }
|
||||
|
||||
virtual void punch_in() {}
|
||||
virtual void punch_out() {}
|
||||
|
||||
virtual void set_speed (double);
|
||||
|
||||
virtual Playlist *playlist () = 0;
|
||||
virtual int use_new_playlist () = 0;
|
||||
virtual int use_playlist (Playlist *) = 0;
|
||||
virtual int use_copy_playlist () = 0;
|
||||
|
||||
virtual void start_scrub (jack_nframes_t where) = 0;
|
||||
virtual void end_scrub () = 0;
|
||||
|
||||
jack_nframes_t current_capture_start() const { return capture_start_frame; }
|
||||
jack_nframes_t current_capture_end() const { return capture_start_frame + capture_captured; }
|
||||
jack_nframes_t get_capture_start_frame (uint32_t n=0);
|
||||
jack_nframes_t get_captured_frames (uint32_t n=0);
|
||||
|
||||
uint32_t n_channels() { return _n_channels; }
|
||||
|
||||
static jack_nframes_t disk_io_frames() { return disk_io_chunk_frames; }
|
||||
static void set_disk_io_chunk_frames (uint32_t n) { disk_io_chunk_frames = n; }
|
||||
|
||||
/* Stateful */
|
||||
virtual XMLNode& get_state(void) = 0;
|
||||
virtual int set_state(const XMLNode& node) = 0;
|
||||
|
||||
jack_nframes_t capture_offset() const { return _capture_offset; }
|
||||
virtual void set_capture_offset ();
|
||||
|
||||
bool slaved() const { return _slaved; }
|
||||
void set_slaved(bool yn) { _slaved = yn; }
|
||||
|
||||
virtual int set_loop (Location *loc);
|
||||
sigc::signal<void,Location *> LoopSet;
|
||||
|
||||
std::list<Region*>& last_capture_regions () { return _last_capture_regions; }
|
||||
|
||||
virtual void handle_input_change (IOChange, void *src);
|
||||
|
||||
sigc::signal<void,void*> record_enable_changed;
|
||||
sigc::signal<void> speed_changed;
|
||||
sigc::signal<void,void*> reverse_changed;
|
||||
sigc::signal<void> PlaylistChanged;
|
||||
sigc::signal<void> AlignmentStyleChanged;
|
||||
|
||||
static sigc::signal<void> DiskOverrun;
|
||||
static sigc::signal<void> DiskUnderrun;
|
||||
static sigc::signal<void,Diskstream*> DiskstreamCreated; // XXX use a ref with sigc2
|
||||
//static sigc::signal<void,list<Source*>*> DeleteSources;
|
||||
|
||||
XMLNode* deprecated_io_node;
|
||||
|
||||
protected:
|
||||
friend class Session;
|
||||
|
||||
/* the Session is the only point of access for these
|
||||
because they require that the Session is "inactive"
|
||||
while they are called.
|
||||
*/
|
||||
|
||||
virtual void set_pending_overwrite (bool) = 0;
|
||||
virtual int overwrite_existing_buffers () = 0;
|
||||
virtual void reverse_scrub_buffer (bool to_forward) = 0;
|
||||
//void set_block_size (jack_nframes_t);
|
||||
virtual int internal_playback_seek (jack_nframes_t distance) = 0;
|
||||
virtual int can_internal_playback_seek (jack_nframes_t distance) = 0;
|
||||
virtual int rename_write_sources () = 0;
|
||||
virtual void reset_write_sources (bool, bool force = false) = 0;
|
||||
virtual void non_realtime_input_change () = 0;
|
||||
|
||||
uint32_t read_data_count() const { return _read_data_count; }
|
||||
uint32_t write_data_count() const { return _write_data_count; }
|
||||
|
||||
protected:
|
||||
friend class Auditioner;
|
||||
virtual int seek (jack_nframes_t which_sample, bool complete_refill = false) = 0;
|
||||
|
||||
protected:
|
||||
friend class Track;
|
||||
|
||||
virtual void prepare ();
|
||||
virtual int process (jack_nframes_t transport_frame, jack_nframes_t nframes, jack_nframes_t offset, bool can_record, bool rec_monitors_input) = 0;
|
||||
virtual bool commit (jack_nframes_t nframes) = 0;
|
||||
virtual void recover (); /* called if commit will not be called, but process was */
|
||||
|
||||
//private:
|
||||
|
||||
/* use unref() to destroy a diskstream */
|
||||
virtual ~Diskstream();
|
||||
|
||||
enum TransitionType {
|
||||
CaptureStart = 0,
|
||||
CaptureEnd
|
||||
};
|
||||
|
||||
struct CaptureTransition {
|
||||
TransitionType type;
|
||||
// the start or end file frame pos
|
||||
jack_nframes_t capture_val;
|
||||
};
|
||||
|
||||
/* the two central butler operations */
|
||||
|
||||
virtual int do_flush (char * workbuf, bool force = false) = 0;
|
||||
//int do_refill (Sample *mixdown_buffer, float *gain_buffer, char *workbuf);
|
||||
|
||||
virtual int non_realtime_do_refill() = 0;
|
||||
|
||||
//int read (Sample* buf, Sample* mixdown_buffer, float* gain_buffer, char * workbuf, jack_nframes_t& start, jack_nframes_t cnt,
|
||||
// ChannelInfo& channel_info, int channel, bool reversed);
|
||||
|
||||
/* XXX fix this redundancy ... */
|
||||
|
||||
virtual void playlist_changed (Change);
|
||||
virtual void playlist_modified ();
|
||||
virtual void playlist_deleted (Playlist*) = 0;
|
||||
virtual void session_controls_changed (Session::ControlType) = 0;
|
||||
|
||||
virtual void finish_capture (bool rec_monitors_input) = 0;
|
||||
virtual void clean_up_capture (struct tm&, time_t, bool abort) = 0;
|
||||
virtual void transport_stopped (struct tm&, time_t, bool abort) = 0;
|
||||
|
||||
struct CaptureInfo {
|
||||
uint32_t start;
|
||||
uint32_t frames;
|
||||
};
|
||||
|
||||
virtual void init (Flag);
|
||||
|
||||
//void init_channel (ChannelInfo &chan);
|
||||
//void destroy_channel (ChannelInfo &chan);
|
||||
|
||||
virtual int use_new_write_source (uint32_t n=0) = 0;
|
||||
virtual int use_new_fade_source (uint32_t n=0) = 0;
|
||||
|
||||
virtual int find_and_use_playlist (const string&) = 0;
|
||||
|
||||
//void allocate_temporary_buffers ();
|
||||
|
||||
virtual int create_input_port () = 0;
|
||||
virtual int connect_input_port () = 0;
|
||||
virtual int seek_unlocked (jack_nframes_t which_sample) = 0;
|
||||
|
||||
virtual int ports_created () = 0;
|
||||
|
||||
virtual bool realtime_set_speed (double, bool global_change);
|
||||
//void non_realtime_set_speed ();
|
||||
|
||||
std::list<Region*> _last_capture_regions;
|
||||
//std::vector<FileSource*> capturing_sources;
|
||||
virtual int use_pending_capture_data (XMLNode& node) = 0;
|
||||
|
||||
virtual void get_input_sources () = 0;
|
||||
virtual void check_record_status (jack_nframes_t transport_frame, jack_nframes_t nframes, bool can_record) = 0;
|
||||
//void set_align_style_from_io();
|
||||
virtual void setup_destructive_playlist () = 0;
|
||||
//void use_destructive_playlist ();
|
||||
|
||||
// Wouldn't hurt for this thing to do on a diet:
|
||||
|
||||
static jack_nframes_t disk_io_chunk_frames;
|
||||
vector<CaptureInfo*> capture_info;
|
||||
Glib::Mutex capture_info_lock;
|
||||
|
||||
uint32_t i_am_the_modifier;
|
||||
|
||||
string _name;
|
||||
ARDOUR::Session& _session;
|
||||
ARDOUR::IO* _io;
|
||||
uint32_t _n_channels;
|
||||
id_t _id;
|
||||
|
||||
mutable gint _record_enabled;
|
||||
double _visible_speed;
|
||||
double _actual_speed;
|
||||
/* items needed for speed change logic */
|
||||
bool _buffer_reallocation_required;
|
||||
bool _seek_required;
|
||||
|
||||
bool force_refill;
|
||||
jack_nframes_t capture_start_frame;
|
||||
jack_nframes_t capture_captured;
|
||||
bool was_recording;
|
||||
jack_nframes_t adjust_capture_position;
|
||||
jack_nframes_t _capture_offset;
|
||||
jack_nframes_t _roll_delay;
|
||||
jack_nframes_t first_recordable_frame;
|
||||
jack_nframes_t last_recordable_frame;
|
||||
int last_possibly_recording;
|
||||
AlignStyle _alignment_style;
|
||||
bool _scrubbing;
|
||||
bool _slaved;
|
||||
bool _processed;
|
||||
Location* loop_location;
|
||||
jack_nframes_t overwrite_frame;
|
||||
off_t overwrite_offset;
|
||||
bool pending_overwrite;
|
||||
bool overwrite_queued;
|
||||
IOChange input_change_pending;
|
||||
jack_nframes_t wrap_buffer_size;
|
||||
jack_nframes_t speed_buffer_size;
|
||||
|
||||
uint64_t last_phase;
|
||||
uint64_t phi;
|
||||
|
||||
jack_nframes_t file_frame;
|
||||
jack_nframes_t playback_sample;
|
||||
jack_nframes_t playback_distance;
|
||||
|
||||
uint32_t _read_data_count;
|
||||
uint32_t _write_data_count;
|
||||
|
||||
bool in_set_state;
|
||||
AlignStyle _persistent_alignment_style;
|
||||
bool first_input_change;
|
||||
|
||||
Glib::Mutex state_lock;
|
||||
|
||||
jack_nframes_t scrub_start;
|
||||
jack_nframes_t scrub_buffer_size;
|
||||
jack_nframes_t scrub_offset;
|
||||
|
||||
uint32_t _refcnt;
|
||||
|
||||
sigc::connection ports_created_c;
|
||||
sigc::connection plmod_connection;
|
||||
sigc::connection plstate_connection;
|
||||
sigc::connection plgone_connection;
|
||||
|
||||
unsigned char _flags;
|
||||
|
||||
};
|
||||
|
||||
}; /* namespace ARDOUR */
|
||||
|
||||
#endif /* __ardour_diskstream_h__ */
|
||||
194
libs/ardour/ardour/midi_diskstream.h
Normal file
194
libs/ardour/ardour/midi_diskstream.h
Normal file
|
|
@ -0,0 +1,194 @@
|
|||
/*
|
||||
Copyright (C) 2000 Paul Davis
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
$Id: diskstream.h 579 2006-06-12 19:56:37Z essej $
|
||||
*/
|
||||
|
||||
#ifndef __ardour_midi_diskstream_h__
|
||||
#define __ardour_midi_diskstream_h__
|
||||
|
||||
#include <sigc++/signal.h>
|
||||
|
||||
#include <cmath>
|
||||
#include <string>
|
||||
#include <queue>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#include <pbd/fastlog.h>
|
||||
#include <pbd/ringbufferNPT.h>
|
||||
|
||||
|
||||
#include <ardour/ardour.h>
|
||||
#include <ardour/configuration.h>
|
||||
#include <ardour/session.h>
|
||||
#include <ardour/route_group.h>
|
||||
#include <ardour/route.h>
|
||||
#include <ardour/port.h>
|
||||
#include <ardour/utils.h>
|
||||
#include <ardour/diskstream.h>
|
||||
#include <ardour/midi_playlist.h>
|
||||
struct tm;
|
||||
|
||||
namespace ARDOUR {
|
||||
|
||||
class MidiEngine;
|
||||
class Send;
|
||||
class Session;
|
||||
class MidiPlaylist;
|
||||
class SMFSource;
|
||||
class IO;
|
||||
|
||||
class MidiDiskstream : public Diskstream
|
||||
{
|
||||
public:
|
||||
MidiDiskstream (Session &, const string& name, Diskstream::Flag f = Recordable);
|
||||
MidiDiskstream (Session &, const XMLNode&);
|
||||
|
||||
void set_io (ARDOUR::IO& io);
|
||||
|
||||
MidiDiskstream& ref() { _refcnt++; return *this; }
|
||||
//void unref() { if (_refcnt) _refcnt--; if (_refcnt == 0) delete this; }
|
||||
//uint32_t refcnt() const { return _refcnt; }
|
||||
|
||||
float playback_buffer_load() const;
|
||||
float capture_buffer_load() const;
|
||||
|
||||
//void set_align_style (AlignStyle);
|
||||
//void set_persistent_align_style (AlignStyle);
|
||||
|
||||
void set_record_enabled (bool yn, void *src);
|
||||
//void set_speed (double);
|
||||
|
||||
int use_playlist (Playlist *);
|
||||
int use_new_playlist ();
|
||||
int use_copy_playlist ();
|
||||
|
||||
void start_scrub (jack_nframes_t where) {} // FIXME?
|
||||
void end_scrub () {} // FIXME?
|
||||
|
||||
Playlist *playlist () { return _playlist; }
|
||||
|
||||
static sigc::signal<void,list<SMFSource*>*> DeleteSources;
|
||||
|
||||
/* stateful */
|
||||
|
||||
XMLNode& get_state(void);
|
||||
int set_state(const XMLNode& node);
|
||||
|
||||
void monitor_input (bool);
|
||||
|
||||
//void handle_input_change (IOChange, void *src);
|
||||
|
||||
protected:
|
||||
friend class Session;
|
||||
|
||||
/* the Session is the only point of access for these
|
||||
because they require that the Session is "inactive"
|
||||
while they are called.
|
||||
*/
|
||||
|
||||
void set_pending_overwrite(bool);
|
||||
int overwrite_existing_buffers ();
|
||||
void reverse_scrub_buffer (bool to_forward) {} // FIXME?
|
||||
void set_block_size (jack_nframes_t);
|
||||
int internal_playback_seek (jack_nframes_t distance);
|
||||
int can_internal_playback_seek (jack_nframes_t distance);
|
||||
int rename_write_sources ();
|
||||
void reset_write_sources (bool, bool force = false);
|
||||
void non_realtime_input_change ();
|
||||
|
||||
uint32_t read_data_count() const { return _read_data_count; }
|
||||
uint32_t write_data_count() const { return _write_data_count; }
|
||||
|
||||
protected:
|
||||
friend class Auditioner;
|
||||
int seek (jack_nframes_t which_sample, bool complete_refill = false);
|
||||
|
||||
protected:
|
||||
friend class MidiTrack;
|
||||
|
||||
int process (jack_nframes_t transport_frame, jack_nframes_t nframes, jack_nframes_t offset, bool can_record, bool rec_monitors_input);
|
||||
bool commit (jack_nframes_t nframes);
|
||||
|
||||
private:
|
||||
|
||||
/* use unref() to destroy a diskstream */
|
||||
~MidiDiskstream();
|
||||
|
||||
MidiPlaylist* _playlist;
|
||||
|
||||
/* the two central butler operations */
|
||||
|
||||
int do_flush (char * workbuf, bool force = false);
|
||||
int do_refill (RawMidi *mixdown_buffer, float *gain_buffer, char *workbuf);
|
||||
|
||||
virtual int non_realtime_do_refill() { return do_refill(0, 0, 0); }
|
||||
|
||||
int read (RawMidi* buf, RawMidi* mixdown_buffer, char * workbuf, jack_nframes_t& start, jack_nframes_t cnt, bool reversed);
|
||||
|
||||
/* XXX fix this redundancy ... */
|
||||
|
||||
//void playlist_changed (Change);
|
||||
//void playlist_modified ();
|
||||
void playlist_deleted (Playlist*);
|
||||
void session_controls_changed (Session::ControlType) {} // FIXME?
|
||||
|
||||
void finish_capture (bool rec_monitors_input);
|
||||
void clean_up_capture (struct tm&, time_t, bool abort) {} // FIXME?
|
||||
void transport_stopped (struct tm&, time_t, bool abort);
|
||||
|
||||
struct CaptureInfo {
|
||||
uint32_t start;
|
||||
uint32_t frames;
|
||||
};
|
||||
|
||||
void init (Diskstream::Flag);
|
||||
|
||||
int use_new_write_source (uint32_t n=0);
|
||||
int use_new_fade_source (uint32_t n=0) { return 0; } // FIXME?
|
||||
|
||||
int find_and_use_playlist (const string&);
|
||||
|
||||
void allocate_temporary_buffers ();
|
||||
|
||||
int create_input_port () { return 0; } // FIXME?
|
||||
int connect_input_port () { return 0; } // FIXME?
|
||||
int seek_unlocked (jack_nframes_t which_sample) { return 0; } // FIXME?
|
||||
|
||||
int ports_created () { return 0; } // FIXME?
|
||||
|
||||
//bool realtime_set_speed (double, bool global_change);
|
||||
void non_realtime_set_speed ();
|
||||
|
||||
int use_pending_capture_data (XMLNode& node);
|
||||
|
||||
void get_input_sources ();
|
||||
void check_record_status (jack_nframes_t transport_frame, jack_nframes_t nframes, bool can_record);
|
||||
void set_align_style_from_io();
|
||||
void setup_destructive_playlist ();
|
||||
void use_destructive_playlist ();
|
||||
|
||||
std::list<Region*> _last_capture_regions;
|
||||
std::vector<SMFSource*> _capturing_sources;
|
||||
};
|
||||
|
||||
}; /* namespace ARDOUR */
|
||||
|
||||
#endif /* __ardour_midi_diskstream_h__ */
|
||||
109
libs/ardour/ardour/midi_playlist.h
Normal file
109
libs/ardour/ardour/midi_playlist.h
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
/*
|
||||
Copyright (C) 2006 Paul Davis
|
||||
Written by Dave Robillard, 2006
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
#ifndef __ardour_midi_playlist_h__
|
||||
#define __ardour_midi_playlist_h__
|
||||
|
||||
#include <vector>
|
||||
#include <list>
|
||||
|
||||
#include <ardour/ardour.h>
|
||||
#include <ardour/playlist.h>
|
||||
|
||||
namespace ARDOUR
|
||||
{
|
||||
|
||||
class Session;
|
||||
class Region;
|
||||
class MidiRegion;
|
||||
class Source;
|
||||
|
||||
class MidiPlaylist : public ARDOUR::Playlist
|
||||
{
|
||||
private:
|
||||
|
||||
struct State : public ARDOUR::StateManager::State
|
||||
{
|
||||
RegionList regions;
|
||||
std::list<UndoAction> region_states;
|
||||
|
||||
State (std::string why) : ARDOUR::StateManager::State (why)
|
||||
{}
|
||||
~State ();
|
||||
};
|
||||
|
||||
public:
|
||||
MidiPlaylist (Session&, const XMLNode&, bool hidden = false);
|
||||
MidiPlaylist (Session&, string name, bool hidden = false);
|
||||
MidiPlaylist (const MidiPlaylist&, string name, bool hidden = false);
|
||||
MidiPlaylist (const MidiPlaylist&, jack_nframes_t start, jack_nframes_t cnt,
|
||||
string name, bool hidden = false);
|
||||
|
||||
jack_nframes_t read (unsigned char *dst, unsigned char *mixdown,
|
||||
char * workbuf, jack_nframes_t start, jack_nframes_t cnt, uint32_t chan_n=0);
|
||||
|
||||
int set_state (const XMLNode&);
|
||||
UndoAction get_memento() const;
|
||||
|
||||
template<class T>
|
||||
void apply_to_history (T& obj, void (T::*method)(const ARDOUR::StateManager::StateMap&, state_id_t))
|
||||
{
|
||||
RegionLock rlock (this);
|
||||
(obj.*method) (states, _current_state_id);
|
||||
}
|
||||
|
||||
bool destroy_region (Region*);
|
||||
|
||||
void get_equivalent_regions (const MidiRegion&, std::vector<MidiRegion*>&);
|
||||
void get_region_list_equivalent_regions (const MidiRegion&, std::vector<MidiRegion*>&);
|
||||
|
||||
void drop_all_states ();
|
||||
|
||||
protected:
|
||||
|
||||
/* state management */
|
||||
|
||||
StateManager::State* state_factory (std::string) const;
|
||||
Change restore_state (StateManager::State&);
|
||||
void send_state_change (Change);
|
||||
|
||||
/* playlist "callbacks" */
|
||||
void flush_notifications ();
|
||||
|
||||
void finalize_split_region (Region *orig, Region *left, Region *right);
|
||||
|
||||
void refresh_dependents (Region& region);
|
||||
void check_dependents (Region& region, bool norefresh);
|
||||
void remove_dependents (Region& region);
|
||||
|
||||
protected:
|
||||
~MidiPlaylist (); /* public should use unref() */
|
||||
|
||||
private:
|
||||
XMLNode& state (bool full_state);
|
||||
void dump () const;
|
||||
|
||||
bool region_changed (Change, Region*);
|
||||
};
|
||||
|
||||
} /* namespace ARDOUR */
|
||||
|
||||
#endif /* __ardour_midi_playlist_h__ */
|
||||
|
||||
|
||||
152
libs/ardour/ardour/midi_region.h
Normal file
152
libs/ardour/ardour/midi_region.h
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
/*
|
||||
Copyright (C) 2006 Paul Davis
|
||||
Written by Dave Robillard, 2006
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
#ifndef __ardour_midi_region_h__
|
||||
#define __ardour_midi_region_h__
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <pbd/fastlog.h>
|
||||
#include <pbd/undo.h>
|
||||
|
||||
#include <ardour/ardour.h>
|
||||
#include <ardour/region.h>
|
||||
#include <ardour/export.h>
|
||||
|
||||
class XMLNode;
|
||||
|
||||
namespace ARDOUR {
|
||||
|
||||
class Route;
|
||||
class Playlist;
|
||||
class Session;
|
||||
class MidiFilter;
|
||||
class MidiSource;
|
||||
|
||||
struct MidiRegionState : public RegionState
|
||||
{
|
||||
MidiRegionState (std::string why);
|
||||
|
||||
};
|
||||
|
||||
class MidiRegion : public Region
|
||||
{
|
||||
public:
|
||||
typedef vector<MidiSource *> SourceList;
|
||||
|
||||
MidiRegion (MidiSource&, jack_nframes_t start, jack_nframes_t length, bool announce = true);
|
||||
MidiRegion (MidiSource&, jack_nframes_t start, jack_nframes_t length, const string& name, layer_t = 0, Region::Flag flags = Region::DefaultFlags, bool announce = true);
|
||||
MidiRegion (SourceList &, jack_nframes_t start, jack_nframes_t length, const string& name, layer_t = 0, Region::Flag flags = Region::DefaultFlags, bool announce = true);
|
||||
MidiRegion (const MidiRegion&, jack_nframes_t start, jack_nframes_t length, const string& name, layer_t = 0, Region::Flag flags = Region::DefaultFlags, bool announce = true);
|
||||
MidiRegion (const MidiRegion&);
|
||||
MidiRegion (MidiSource&, const XMLNode&);
|
||||
MidiRegion (SourceList &, const XMLNode&);
|
||||
~MidiRegion();
|
||||
|
||||
bool region_list_equivalent (const MidiRegion&) const ;
|
||||
bool source_equivalent (const MidiRegion&) const;
|
||||
bool equivalent (const MidiRegion&) const;
|
||||
bool size_equivalent (const MidiRegion&) const;
|
||||
bool overlap_equivalent (const MidiRegion&) const;
|
||||
|
||||
bool speed_mismatch (float) const;
|
||||
|
||||
void lock_sources ();
|
||||
void unlock_sources ();
|
||||
MidiSource& source (uint32_t n=0) const { if (n < sources.size()) return *sources[n]; else return *sources[0]; }
|
||||
|
||||
uint32_t n_channels() { return sources.size(); }
|
||||
vector<string> master_source_names();
|
||||
|
||||
bool captured() const { return !(_flags & (Region::Flag (Region::Import|Region::External))); }
|
||||
|
||||
virtual jack_nframes_t read_at (unsigned char *buf, unsigned char *mixdown_buffer,
|
||||
char * workbuf, jack_nframes_t position, jack_nframes_t cnt,
|
||||
uint32_t chan_n = 0,
|
||||
jack_nframes_t read_frames = 0,
|
||||
jack_nframes_t skip_frames = 0) const;
|
||||
|
||||
jack_nframes_t master_read_at (unsigned char *buf, unsigned char *mixdown_buffer,
|
||||
char * workbuf, jack_nframes_t position, jack_nframes_t cnt, uint32_t chan_n=0) const;
|
||||
|
||||
|
||||
XMLNode& state (bool);
|
||||
XMLNode& get_state ();
|
||||
int set_state (const XMLNode&);
|
||||
|
||||
enum FadeShape {
|
||||
Linear,
|
||||
Fast,
|
||||
Slow,
|
||||
LogA,
|
||||
LogB,
|
||||
|
||||
};
|
||||
|
||||
int separate_by_channel (ARDOUR::Session&, vector<MidiRegion*>&) const;
|
||||
|
||||
uint32_t read_data_count() const { return _read_data_count; }
|
||||
|
||||
ARDOUR::Playlist* playlist() const { return _playlist; }
|
||||
|
||||
UndoAction get_memento() const;
|
||||
|
||||
/* export */
|
||||
|
||||
//int exportme (ARDOUR::Session&, ARDOUR::AudioExportSpecification&);
|
||||
|
||||
Region* get_parent();
|
||||
|
||||
private:
|
||||
friend class Playlist;
|
||||
|
||||
private:
|
||||
SourceList sources;
|
||||
SourceList master_sources; /* used when timefx are applied, so
|
||||
we can always use the original
|
||||
source.
|
||||
*/
|
||||
StateManager::State* state_factory (std::string why) const;
|
||||
Change restore_state (StateManager::State&);
|
||||
|
||||
bool copied() const { return _flags & Copied; }
|
||||
void maybe_uncopy ();
|
||||
void rename_after_first_edit ();
|
||||
|
||||
jack_nframes_t _read_at (const SourceList&, unsigned char *buf, unsigned char *mixdown_buffer,
|
||||
char * workbuf, jack_nframes_t position, jack_nframes_t cnt,
|
||||
uint32_t chan_n = 0,
|
||||
jack_nframes_t read_frames = 0,
|
||||
jack_nframes_t skip_frames = 0) const;
|
||||
|
||||
bool verify_start (jack_nframes_t position);
|
||||
bool verify_length (jack_nframes_t position);
|
||||
bool verify_start_mutable (jack_nframes_t& start);
|
||||
bool verify_start_and_length (jack_nframes_t start, jack_nframes_t length);
|
||||
|
||||
void recompute_at_start() {}
|
||||
void recompute_at_end() {}
|
||||
|
||||
void source_deleted (Source*);
|
||||
};
|
||||
|
||||
} /* namespace ARDOUR */
|
||||
|
||||
|
||||
#endif /* __ardour_midi_region_h__ */
|
||||
93
libs/ardour/ardour/midi_source.h
Normal file
93
libs/ardour/ardour/midi_source.h
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
Copyright (C) 2006 Paul Davis
|
||||
Written by Dave Robillard, 2006
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
#ifndef __ardour_midi_source_h__
|
||||
#define __ardour_midi_source_h__
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#include <glibmm/thread.h>
|
||||
|
||||
#include <sigc++/signal.h>
|
||||
|
||||
#include <ardour/source.h>
|
||||
#include <ardour/ardour.h>
|
||||
#include <ardour/stateful.h>
|
||||
#include <pbd/xml++.h>
|
||||
|
||||
using std::string;
|
||||
|
||||
namespace ARDOUR {
|
||||
|
||||
/** Source for raw MIDI data */
|
||||
class MidiSource : public Source
|
||||
{
|
||||
public:
|
||||
MidiSource (string name);
|
||||
MidiSource (const XMLNode&);
|
||||
virtual ~MidiSource ();
|
||||
|
||||
/* returns the number of items in this `midi_source' */
|
||||
|
||||
// Applicable to MIDI? With what unit? [DR]
|
||||
virtual jack_nframes_t length() const {
|
||||
return _length;
|
||||
}
|
||||
|
||||
virtual jack_nframes_t read (unsigned char *dst, jack_nframes_t start, jack_nframes_t cnt, char * workbuf) const;
|
||||
virtual jack_nframes_t write (unsigned char *src, jack_nframes_t cnt, char * workbuf);
|
||||
|
||||
virtual void mark_for_remove() = 0;
|
||||
virtual void mark_streaming_write_completed () {}
|
||||
|
||||
void set_captured_for (string str) { _captured_for = str; }
|
||||
string captured_for() const { return _captured_for; }
|
||||
|
||||
uint32_t read_data_count() const { return _read_data_count; }
|
||||
uint32_t write_data_count() const { return _write_data_count; }
|
||||
|
||||
static sigc::signal<void,MidiSource*> MidiSourceCreated;
|
||||
|
||||
mutable sigc::signal<void> PeaksReady;
|
||||
mutable sigc::signal<void,jack_nframes_t,jack_nframes_t> PeakRangeReady;
|
||||
|
||||
XMLNode& get_state ();
|
||||
int set_state (const XMLNode&);
|
||||
|
||||
protected:
|
||||
jack_nframes_t _length;
|
||||
string _captured_for;
|
||||
|
||||
mutable uint32_t _read_data_count; // modified in read()
|
||||
mutable uint32_t _write_data_count; // modified in write()
|
||||
|
||||
virtual jack_nframes_t read_unlocked (unsigned char *dst, jack_nframes_t start, jack_nframes_t cnt, char * workbuf) const = 0;
|
||||
virtual jack_nframes_t write_unlocked (unsigned char *dst, jack_nframes_t cnt, char * workbuf) = 0;
|
||||
|
||||
void update_length (jack_nframes_t pos, jack_nframes_t cnt);
|
||||
|
||||
private:
|
||||
bool file_changed (string path);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* __ardour_midi_source_h__ */
|
||||
180
libs/ardour/ardour/midi_track.h
Normal file
180
libs/ardour/ardour/midi_track.h
Normal file
|
|
@ -0,0 +1,180 @@
|
|||
/*
|
||||
Copyright (C) 2006 Paul Davis
|
||||
Written by Dave Robillard
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
#ifndef __ardour_midi_track_h__
|
||||
#define __ardour_midi_track_h__
|
||||
|
||||
#include <ardour/route.h>
|
||||
|
||||
namespace ARDOUR
|
||||
{
|
||||
|
||||
class Session;
|
||||
class MidiDiskstream;
|
||||
class MidiPlaylist;
|
||||
class RouteGroup;
|
||||
|
||||
class MidiTrack : public Route
|
||||
{
|
||||
public:
|
||||
MidiTrack (Session&, string name, Route::Flag f = Route::Flag (0), TrackMode m = Normal);
|
||||
MidiTrack (Session&, const XMLNode&);
|
||||
~MidiTrack ();
|
||||
|
||||
int set_name (string str, void *src);
|
||||
|
||||
int roll (jack_nframes_t nframes, jack_nframes_t start_frame, jack_nframes_t end_frame,
|
||||
jack_nframes_t offset, int declick, bool can_record, bool rec_monitors_input);
|
||||
|
||||
int no_roll (jack_nframes_t nframes, jack_nframes_t start_frame, jack_nframes_t end_frame,
|
||||
jack_nframes_t offset, bool state_changing, bool can_record, bool rec_monitors_input);
|
||||
|
||||
int silent_roll (jack_nframes_t nframes, jack_nframes_t start_frame, jack_nframes_t end_frame,
|
||||
jack_nframes_t offset, bool can_record, bool rec_monitors_input);
|
||||
|
||||
void toggle_monitor_input ();
|
||||
|
||||
bool can_record() const { return true; }
|
||||
|
||||
void set_record_enable (bool yn, void *src);
|
||||
|
||||
MidiDiskstream& disk_stream() const { return *diskstream; }
|
||||
|
||||
int set_diskstream (MidiDiskstream&, void *);
|
||||
int use_diskstream (string name);
|
||||
int use_diskstream (id_t id);
|
||||
|
||||
TrackMode mode() const { return _mode; }
|
||||
|
||||
void set_mode (TrackMode m);
|
||||
sigc::signal<void> ModeChanged;
|
||||
|
||||
jack_nframes_t update_total_latency();
|
||||
void set_latency_delay (jack_nframes_t);
|
||||
|
||||
int export_stuff (vector<unsigned char*>& buffers, char * workbuf, uint32_t nbufs,
|
||||
jack_nframes_t nframes, jack_nframes_t end_frame);
|
||||
|
||||
sigc::signal<void,void*> diskstream_changed;
|
||||
|
||||
enum FreezeState {
|
||||
NoFreeze,
|
||||
Frozen,
|
||||
UnFrozen
|
||||
};
|
||||
|
||||
FreezeState freeze_state() const;
|
||||
|
||||
sigc::signal<void> FreezeChange;
|
||||
|
||||
void freeze (InterThreadInfo&);
|
||||
void unfreeze ();
|
||||
|
||||
void bounce (InterThreadInfo&);
|
||||
void bounce_range (jack_nframes_t start, jack_nframes_t end, InterThreadInfo&);
|
||||
|
||||
XMLNode& get_state();
|
||||
XMLNode& get_template();
|
||||
int set_state(const XMLNode& node);
|
||||
|
||||
MIDI::Controllable& midi_rec_enable_control() { return _midi_rec_enable_control; }
|
||||
|
||||
void reset_midi_control (MIDI::Port*, bool);
|
||||
void send_all_midi_feedback ();
|
||||
|
||||
bool record_enabled() const;
|
||||
void set_meter_point (MeterPoint, void* src);
|
||||
|
||||
protected:
|
||||
MidiDiskstream *diskstream;
|
||||
MeterPoint _saved_meter_point;
|
||||
TrackMode _mode;
|
||||
|
||||
XMLNode& state (bool full);
|
||||
|
||||
void passthru_silence (jack_nframes_t start_frame, jack_nframes_t end_frame,
|
||||
jack_nframes_t nframes, jack_nframes_t offset, int declick,
|
||||
bool meter);
|
||||
|
||||
uint32_t n_process_buffers ();
|
||||
|
||||
private:
|
||||
struct FreezeRecordInsertInfo
|
||||
{
|
||||
FreezeRecordInsertInfo(XMLNode& st)
|
||||
: state (st), insert (0)
|
||||
{}
|
||||
|
||||
XMLNode state;
|
||||
Insert* insert;
|
||||
id_t id;
|
||||
UndoAction memento;
|
||||
};
|
||||
|
||||
struct FreezeRecord
|
||||
{
|
||||
FreezeRecord()
|
||||
{
|
||||
playlist = 0;
|
||||
have_mementos = false;
|
||||
}
|
||||
|
||||
~FreezeRecord();
|
||||
|
||||
MidiPlaylist* playlist;
|
||||
vector<FreezeRecordInsertInfo*> insert_info;
|
||||
bool have_mementos;
|
||||
FreezeState state;
|
||||
};
|
||||
|
||||
FreezeRecord _freeze_record;
|
||||
XMLNode* pending_state;
|
||||
|
||||
void diskstream_record_enable_changed (void *src);
|
||||
void diskstream_input_channel_changed (void *src);
|
||||
|
||||
void input_change_handler (void *src);
|
||||
|
||||
sigc::connection recenable_connection;
|
||||
sigc::connection ic_connection;
|
||||
|
||||
int deprecated_use_diskstream_connections ();
|
||||
void set_state_part_two ();
|
||||
void set_state_part_three ();
|
||||
|
||||
struct MIDIRecEnableControl : public MIDI::Controllable
|
||||
{
|
||||
MIDIRecEnableControl (MidiTrack&, MIDI::Port *);
|
||||
void set_value (float);
|
||||
void send_feedback (bool);
|
||||
MIDI::byte* write_feedback (MIDI::byte* buf, int32_t& bufsize, bool val, bool force = false);
|
||||
MidiTrack& track;
|
||||
bool setting;
|
||||
bool last_written;
|
||||
};
|
||||
|
||||
MIDIRecEnableControl _midi_rec_enable_control;
|
||||
|
||||
bool _destructive;
|
||||
};
|
||||
|
||||
}
|
||||
; /* namespace ARDOUR*/
|
||||
|
||||
#endif /* __ardour_midi_track_h__ */
|
||||
96
libs/ardour/ardour/smf_source.h
Normal file
96
libs/ardour/ardour/smf_source.h
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
Copyright (C) 2006 Paul Davis
|
||||
Written by Dave Robillard, 2006
|
||||
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef __ardour_smf_filesource_h__
|
||||
#define __ardour_smf_filesource_h__
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#include <ardour/midi_source.h>
|
||||
|
||||
namespace ARDOUR {
|
||||
|
||||
/** Standard Midi File (Type 0) Source */
|
||||
class SMFSource : public MidiSource {
|
||||
public:
|
||||
enum Flag {
|
||||
Writable = 0x1,
|
||||
CanRename = 0x2,
|
||||
Broadcast = 0x4,
|
||||
Removable = 0x8,
|
||||
RemovableIfEmpty = 0x10,
|
||||
RemoveAtDestroy = 0x20,
|
||||
BuildPeaks = 0x40
|
||||
};
|
||||
|
||||
/** Constructor for existing external-to-session files */
|
||||
SMFSource (std::string path, Flag flags);
|
||||
|
||||
/* Constructor for existing in-session files */
|
||||
SMFSource (const XMLNode&);
|
||||
|
||||
virtual ~SMFSource ();
|
||||
|
||||
int set_name (string newname, bool destructive);
|
||||
|
||||
string path() const { return _path; }
|
||||
|
||||
void set_allow_remove_if_empty (bool yn);
|
||||
void mark_for_remove();
|
||||
|
||||
virtual int update_header (jack_nframes_t when, struct tm&, time_t) = 0;
|
||||
virtual int flush_header () = 0;
|
||||
|
||||
int move_to_trash (const string trash_dir_name);
|
||||
|
||||
static bool is_empty (string path);
|
||||
void mark_streaming_write_completed ();
|
||||
|
||||
void mark_take (string);
|
||||
string take_id() const { return _take_id; }
|
||||
|
||||
static void set_search_path (string);
|
||||
static void set_header_position_offset (jack_nframes_t offset, bool negative);
|
||||
|
||||
XMLNode& get_state ();
|
||||
int set_state (const XMLNode&);
|
||||
|
||||
protected:
|
||||
|
||||
int init (string idstr, bool must_exist);
|
||||
|
||||
bool find (std::string path, bool must_exist, bool& is_new);
|
||||
bool removable() const;
|
||||
bool writable() const { return _flags & Writable; }
|
||||
|
||||
uint16_t _channel;
|
||||
string _path;
|
||||
Flag _flags;
|
||||
string _take_id;
|
||||
bool _allow_remove_if_empty;
|
||||
uint64_t _timeline_position;
|
||||
|
||||
static string _search_path;
|
||||
};
|
||||
|
||||
}; /* namespace ARDOUR */
|
||||
|
||||
#endif /* __ardour_smf_filesource_h__ */
|
||||
|
||||
362
libs/ardour/diskstream.cc
Normal file
362
libs/ardour/diskstream.cc
Normal file
|
|
@ -0,0 +1,362 @@
|
|||
/*
|
||||
Copyright (C) 2000-2003 Paul Davis
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
$Id: diskstream.cc 567 2006-06-07 14:54:12Z trutkin $
|
||||
*/
|
||||
|
||||
#include <fstream>
|
||||
#include <cstdio>
|
||||
#include <unistd.h>
|
||||
#include <cmath>
|
||||
#include <cerrno>
|
||||
#include <string>
|
||||
#include <climits>
|
||||
#include <fcntl.h>
|
||||
#include <cstdlib>
|
||||
#include <ctime>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include <pbd/error.h>
|
||||
#include <pbd/basename.h>
|
||||
#include <glibmm/thread.h>
|
||||
#include <pbd/xml++.h>
|
||||
|
||||
#include <ardour/ardour.h>
|
||||
#include <ardour/audioengine.h>
|
||||
#include <ardour/diskstream.h>
|
||||
#include <ardour/utils.h>
|
||||
#include <ardour/configuration.h>
|
||||
#include <ardour/audiofilesource.h>
|
||||
#include <ardour/destructive_filesource.h>
|
||||
#include <ardour/send.h>
|
||||
#include <ardour/playlist.h>
|
||||
#include <ardour/cycle_timer.h>
|
||||
#include <ardour/region.h>
|
||||
|
||||
#include "i18n.h"
|
||||
#include <locale.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace ARDOUR;
|
||||
using namespace PBD;
|
||||
|
||||
jack_nframes_t Diskstream::disk_io_chunk_frames;
|
||||
|
||||
sigc::signal<void,Diskstream*> Diskstream::DiskstreamCreated;
|
||||
//sigc::signal<void,list<AudioFileSource*>*> Diskstream::DeleteSources;
|
||||
sigc::signal<void> Diskstream::DiskOverrun;
|
||||
sigc::signal<void> Diskstream::DiskUnderrun;
|
||||
|
||||
Diskstream::Diskstream (Session &sess, const string &name, Flag flag)
|
||||
: _name (name)
|
||||
, _session (sess)
|
||||
{
|
||||
#if 0
|
||||
/* prevent any write sources from being created */
|
||||
|
||||
in_set_state = true;
|
||||
|
||||
init (flag);
|
||||
//use_new_playlist ();
|
||||
|
||||
in_set_state = false;
|
||||
DiskstreamCreated (this); /* EMIT SIGNAL */
|
||||
#endif
|
||||
}
|
||||
|
||||
Diskstream::Diskstream (Session& sess, const XMLNode& node)
|
||||
: _session (sess)
|
||||
|
||||
{
|
||||
#if 0
|
||||
in_set_state = true;
|
||||
init (Recordable);
|
||||
|
||||
/*if (set_state (node)) {
|
||||
in_set_state = false;
|
||||
throw failed_constructor();
|
||||
}*/
|
||||
|
||||
in_set_state = false;
|
||||
|
||||
//if (destructive()) {
|
||||
// use_destructive_playlist ();
|
||||
//}
|
||||
DiskstreamCreated (this); /* EMIT SIGNAL */
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
Diskstream::init (Flag f)
|
||||
{
|
||||
_id = new_id();
|
||||
_refcnt = 0;
|
||||
_flags = f;
|
||||
_io = 0;
|
||||
_alignment_style = ExistingMaterial;
|
||||
_persistent_alignment_style = ExistingMaterial;
|
||||
first_input_change = true;
|
||||
i_am_the_modifier = 0;
|
||||
g_atomic_int_set (&_record_enabled, 0);
|
||||
was_recording = false;
|
||||
capture_start_frame = 0;
|
||||
capture_captured = 0;
|
||||
_visible_speed = 1.0f;
|
||||
_actual_speed = 1.0f;
|
||||
_buffer_reallocation_required = false;
|
||||
_seek_required = false;
|
||||
first_recordable_frame = max_frames;
|
||||
last_recordable_frame = max_frames;
|
||||
_roll_delay = 0;
|
||||
_capture_offset = 0;
|
||||
_processed = false;
|
||||
_slaved = false;
|
||||
adjust_capture_position = 0;
|
||||
last_possibly_recording = 0;
|
||||
loop_location = 0;
|
||||
wrap_buffer_size = 0;
|
||||
speed_buffer_size = 0;
|
||||
last_phase = 0;
|
||||
phi = (uint64_t) (0x1000000);
|
||||
file_frame = 0;
|
||||
playback_sample = 0;
|
||||
playback_distance = 0;
|
||||
_read_data_count = 0;
|
||||
_write_data_count = 0;
|
||||
deprecated_io_node = 0;
|
||||
|
||||
/* 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 ();
|
||||
|
||||
pending_overwrite = false;
|
||||
overwrite_frame = 0;
|
||||
overwrite_queued = false;
|
||||
input_change_pending = NoChange;
|
||||
|
||||
//add_channel ();
|
||||
_n_channels = 0;//1;
|
||||
}
|
||||
|
||||
Diskstream::~Diskstream ()
|
||||
{
|
||||
// Taken by child.. assure lock?
|
||||
//Glib::Mutex::Lock lm (state_lock);
|
||||
|
||||
//if (_playlist) {
|
||||
// _playlist->unref ();
|
||||
//}
|
||||
|
||||
//for (ChannelList::iterator chan = channels.begin(); chan != channels.end(); ++chan) {
|
||||
// destroy_channel((*chan));
|
||||
//}
|
||||
|
||||
//channels.clear();
|
||||
}
|
||||
|
||||
void
|
||||
Diskstream::handle_input_change (IOChange change, void *src)
|
||||
{
|
||||
Glib::Mutex::Lock lm (state_lock);
|
||||
|
||||
if (!(input_change_pending & change)) {
|
||||
input_change_pending = IOChange (input_change_pending|change);
|
||||
_session.request_input_change_handling ();
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
Diskstream::realtime_set_speed (double sp, bool global)
|
||||
{
|
||||
bool changed = false;
|
||||
double new_speed = sp * _session.transport_speed();
|
||||
|
||||
if (_visible_speed != sp) {
|
||||
_visible_speed = sp;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (new_speed != _actual_speed) {
|
||||
|
||||
jack_nframes_t required_wrap_size = (jack_nframes_t) floor (_session.get_block_size() *
|
||||
fabs (new_speed)) + 1;
|
||||
|
||||
if (required_wrap_size > wrap_buffer_size) {
|
||||
_buffer_reallocation_required = true;
|
||||
}
|
||||
|
||||
_actual_speed = new_speed;
|
||||
phi = (uint64_t) (0x1000000 * fabs(_actual_speed));
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
if (!global) {
|
||||
_seek_required = true;
|
||||
}
|
||||
speed_changed (); /* EMIT SIGNAL */
|
||||
}
|
||||
|
||||
return _buffer_reallocation_required || _seek_required;
|
||||
}
|
||||
|
||||
void
|
||||
Diskstream::prepare ()
|
||||
{
|
||||
_processed = false;
|
||||
playback_distance = 0;
|
||||
}
|
||||
|
||||
void
|
||||
Diskstream::recover ()
|
||||
{
|
||||
state_lock.unlock();
|
||||
_processed = false;
|
||||
}
|
||||
|
||||
void
|
||||
Diskstream::set_capture_offset ()
|
||||
{
|
||||
if (_io == 0) {
|
||||
/* can't capture, so forget it */
|
||||
return;
|
||||
}
|
||||
|
||||
_capture_offset = _io->input_latency();
|
||||
}
|
||||
|
||||
void
|
||||
Diskstream::set_align_style (AlignStyle a)
|
||||
{
|
||||
if (record_enabled() && _session.actively_recording()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (a != _alignment_style) {
|
||||
_alignment_style = a;
|
||||
AlignmentStyleChanged ();
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
Diskstream::set_loop (Location *location)
|
||||
{
|
||||
if (location) {
|
||||
if (location->start() >= location->end()) {
|
||||
error << string_compose(_("Location \"%1\" not valid for track loop (start >= end)"), location->name()) << endl;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
loop_location = location;
|
||||
|
||||
LoopSet (location); /* EMIT SIGNAL */
|
||||
return 0;
|
||||
}
|
||||
|
||||
jack_nframes_t
|
||||
Diskstream::get_capture_start_frame (uint32_t n)
|
||||
{
|
||||
Glib::Mutex::Lock lm (capture_info_lock);
|
||||
|
||||
if (capture_info.size() > n) {
|
||||
return capture_info[n]->start;
|
||||
}
|
||||
else {
|
||||
return capture_start_frame;
|
||||
}
|
||||
}
|
||||
|
||||
jack_nframes_t
|
||||
Diskstream::get_captured_frames (uint32_t n)
|
||||
{
|
||||
Glib::Mutex::Lock lm (capture_info_lock);
|
||||
|
||||
if (capture_info.size() > n) {
|
||||
return capture_info[n]->frames;
|
||||
}
|
||||
else {
|
||||
return capture_captured;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Diskstream::set_roll_delay (jack_nframes_t nframes)
|
||||
{
|
||||
_roll_delay = nframes;
|
||||
}
|
||||
|
||||
void
|
||||
Diskstream::set_speed (double sp)
|
||||
{
|
||||
_session.request_diskstream_speed (*this, sp);
|
||||
|
||||
/* to force a rebuffering at the right place */
|
||||
playlist_modified();
|
||||
}
|
||||
|
||||
void
|
||||
Diskstream::playlist_changed (Change ignored)
|
||||
{
|
||||
playlist_modified ();
|
||||
}
|
||||
|
||||
void
|
||||
Diskstream::playlist_modified ()
|
||||
{
|
||||
if (!i_am_the_modifier && !overwrite_queued) {
|
||||
_session.request_overwrite_buffer (this);
|
||||
overwrite_queued = true;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
Diskstream::set_name (string str, void *src)
|
||||
{
|
||||
if (str != _name) {
|
||||
assert(playlist());
|
||||
playlist()->set_name (str);
|
||||
_name = str;
|
||||
|
||||
if (!in_set_state && recordable()) {
|
||||
/* rename existing capture files so that they have the correct name */
|
||||
return rename_write_sources ();
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
Diskstream::set_destructive (bool yn)
|
||||
{
|
||||
if (yn != destructive()) {
|
||||
reset_write_sources (true, true);
|
||||
if (yn) {
|
||||
_flags |= Destructive;
|
||||
} else {
|
||||
_flags &= ~Destructive;
|
||||
}
|
||||
}
|
||||
}
|
||||
624
libs/ardour/midi_diskstream.cc
Normal file
624
libs/ardour/midi_diskstream.cc
Normal file
|
|
@ -0,0 +1,624 @@
|
|||
/*
|
||||
Copyright (C) 2000-2003 Paul Davis
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
$Id: diskstream.cc 567 2006-06-07 14:54:12Z trutkin $
|
||||
*/
|
||||
|
||||
#include <fstream>
|
||||
#include <cstdio>
|
||||
#include <unistd.h>
|
||||
#include <cmath>
|
||||
#include <cerrno>
|
||||
#include <string>
|
||||
#include <climits>
|
||||
#include <fcntl.h>
|
||||
#include <cstdlib>
|
||||
#include <ctime>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include <pbd/error.h>
|
||||
#include <pbd/basename.h>
|
||||
#include <glibmm/thread.h>
|
||||
#include <pbd/xml++.h>
|
||||
|
||||
#include <ardour/ardour.h>
|
||||
#include <ardour/audioengine.h>
|
||||
#include <ardour/midi_diskstream.h>
|
||||
#include <ardour/utils.h>
|
||||
#include <ardour/configuration.h>
|
||||
#include <ardour/smf_source.h>
|
||||
#include <ardour/destructive_filesource.h>
|
||||
#include <ardour/send.h>
|
||||
#include <ardour/midi_playlist.h>
|
||||
#include <ardour/cycle_timer.h>
|
||||
#include <ardour/midi_region.h>
|
||||
|
||||
#include "i18n.h"
|
||||
#include <locale.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace ARDOUR;
|
||||
using namespace PBD;
|
||||
|
||||
//sigc::signal<void,MidiDiskstream*> MidiDiskstream::MidiDiskstreamCreated;
|
||||
sigc::signal<void,list<SMFSource*>*> MidiDiskstream::DeleteSources;
|
||||
//sigc::signal<void> MidiDiskstream::DiskOverrun;
|
||||
//sigc::signal<void> MidiDiskstream::DiskUnderrun;
|
||||
|
||||
MidiDiskstream::MidiDiskstream (Session &sess, const string &name, Diskstream::Flag flag)
|
||||
: Diskstream(sess, name, flag)
|
||||
, _playlist(NULL)
|
||||
{
|
||||
/* prevent any write sources from being created */
|
||||
|
||||
in_set_state = true;
|
||||
|
||||
init (flag);
|
||||
use_new_playlist ();
|
||||
|
||||
in_set_state = false;
|
||||
|
||||
DiskstreamCreated (this); /* EMIT SIGNAL */
|
||||
}
|
||||
|
||||
MidiDiskstream::MidiDiskstream (Session& sess, const XMLNode& node)
|
||||
: Diskstream(sess, node)
|
||||
, _playlist(NULL)
|
||||
{
|
||||
in_set_state = true;
|
||||
init (Recordable);
|
||||
|
||||
if (set_state (node)) {
|
||||
in_set_state = false;
|
||||
throw failed_constructor();
|
||||
}
|
||||
|
||||
in_set_state = false;
|
||||
|
||||
if (destructive()) {
|
||||
use_destructive_playlist ();
|
||||
}
|
||||
|
||||
DiskstreamCreated (this); /* EMIT SIGNAL */
|
||||
}
|
||||
|
||||
void
|
||||
MidiDiskstream::init (Diskstream::Flag f)
|
||||
{
|
||||
Diskstream::init(f);
|
||||
|
||||
/* 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 ();
|
||||
|
||||
/* FIXME: this is now done before the above. OK? */
|
||||
/*pending_overwrite = false;
|
||||
overwrite_frame = 0;
|
||||
overwrite_queued = false;
|
||||
input_change_pending = NoChange;*/
|
||||
|
||||
_n_channels = 1;
|
||||
}
|
||||
|
||||
MidiDiskstream::~MidiDiskstream ()
|
||||
{
|
||||
Glib::Mutex::Lock lm (state_lock);
|
||||
|
||||
if (_playlist)
|
||||
_playlist->unref ();
|
||||
}
|
||||
/*
|
||||
void
|
||||
MidiDiskstream::handle_input_change (IOChange change, void *src)
|
||||
{
|
||||
Glib::Mutex::Lock lm (state_lock);
|
||||
|
||||
if (!(input_change_pending & change)) {
|
||||
input_change_pending = IOChange (input_change_pending|change);
|
||||
_session.request_input_change_handling ();
|
||||
}
|
||||
}
|
||||
*/
|
||||
void
|
||||
MidiDiskstream::non_realtime_input_change ()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
MidiDiskstream::get_input_sources ()
|
||||
{
|
||||
}
|
||||
|
||||
int
|
||||
MidiDiskstream::find_and_use_playlist (const string& name)
|
||||
{
|
||||
Playlist* pl;
|
||||
MidiPlaylist* playlist;
|
||||
|
||||
if ((pl = _session.get_playlist (name)) == 0) {
|
||||
error << string_compose(_("MidiDiskstream: Session doesn't know about a Playlist called \"%1\""), name) << endmsg;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((playlist = dynamic_cast<MidiPlaylist*> (pl)) == 0) {
|
||||
error << string_compose(_("MidiDiskstream: Playlist \"%1\" isn't an midi playlist"), name) << endmsg;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return use_playlist (playlist);
|
||||
}
|
||||
|
||||
int
|
||||
MidiDiskstream::use_playlist (Playlist* playlist)
|
||||
{
|
||||
assert(dynamic_cast<MidiPlaylist*>(playlist));
|
||||
|
||||
{
|
||||
Glib::Mutex::Lock lm (state_lock);
|
||||
|
||||
if (playlist == _playlist) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
plstate_connection.disconnect();
|
||||
plmod_connection.disconnect ();
|
||||
plgone_connection.disconnect ();
|
||||
|
||||
if (_playlist) {
|
||||
_playlist->unref();
|
||||
}
|
||||
|
||||
_playlist = dynamic_cast<MidiPlaylist*>(playlist);
|
||||
_playlist->ref();
|
||||
|
||||
if (!in_set_state && recordable()) {
|
||||
reset_write_sources (false);
|
||||
}
|
||||
|
||||
plstate_connection = _playlist->StateChanged.connect (mem_fun (*this, &MidiDiskstream::playlist_changed));
|
||||
plmod_connection = _playlist->Modified.connect (mem_fun (*this, &MidiDiskstream::playlist_modified));
|
||||
plgone_connection = _playlist->GoingAway.connect (mem_fun (*this, &MidiDiskstream::playlist_deleted));
|
||||
}
|
||||
|
||||
if (!overwrite_queued) {
|
||||
_session.request_overwrite_buffer (this);
|
||||
overwrite_queued = true;
|
||||
}
|
||||
|
||||
PlaylistChanged (); /* EMIT SIGNAL */
|
||||
_session.set_dirty ();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
MidiDiskstream::use_new_playlist ()
|
||||
{
|
||||
string newname;
|
||||
MidiPlaylist* playlist;
|
||||
|
||||
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 = new MidiPlaylist (_session, newname, hidden())) != 0) {
|
||||
playlist->set_orig_diskstream_id (id());
|
||||
return use_playlist (playlist);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
MidiDiskstream::use_copy_playlist ()
|
||||
{
|
||||
if (destructive()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (_playlist == 0) {
|
||||
error << string_compose(_("MidiDiskstream %1: there is no existing playlist to make a copy of!"), _name) << endmsg;
|
||||
return -1;
|
||||
}
|
||||
|
||||
string newname;
|
||||
MidiPlaylist* playlist;
|
||||
|
||||
newname = Playlist::bump_name (_playlist->name(), _session);
|
||||
|
||||
if ((playlist = new MidiPlaylist (*_playlist, newname)) != 0) {
|
||||
playlist->set_orig_diskstream_id (id());
|
||||
return use_playlist (playlist);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
MidiDiskstream::playlist_deleted (Playlist* pl)
|
||||
{
|
||||
/* this catches an ordering issue with session destruction. playlists
|
||||
are destroyed before diskstreams. we have to invalidate any handles
|
||||
we have to the playlist.
|
||||
*/
|
||||
|
||||
_playlist = 0;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
MidiDiskstream::setup_destructive_playlist ()
|
||||
{
|
||||
/* a single full-sized region */
|
||||
|
||||
//MidiRegion* region = new MidiRegion (srcs, 0, max_frames, _name);
|
||||
//_playlist->add_region (*region, 0);
|
||||
}
|
||||
|
||||
void
|
||||
MidiDiskstream::use_destructive_playlist ()
|
||||
{
|
||||
/* use the sources associated with the single full-extent region */
|
||||
|
||||
Playlist::RegionList* rl = _playlist->regions_at (0);
|
||||
|
||||
if (rl->empty()) {
|
||||
reset_write_sources (false, true);
|
||||
return;
|
||||
}
|
||||
|
||||
MidiRegion* region = dynamic_cast<MidiRegion*> (rl->front());
|
||||
|
||||
if (region == 0) {
|
||||
throw failed_constructor();
|
||||
}
|
||||
|
||||
delete rl;
|
||||
|
||||
/* the source list will never be reset for a destructive track */
|
||||
}
|
||||
|
||||
void
|
||||
MidiDiskstream::set_io (IO& io)
|
||||
{
|
||||
_io = &io;
|
||||
set_align_style_from_io ();
|
||||
}
|
||||
|
||||
void
|
||||
MidiDiskstream::non_realtime_set_speed ()
|
||||
{
|
||||
if (_buffer_reallocation_required)
|
||||
{
|
||||
Glib::Mutex::Lock lm (state_lock);
|
||||
allocate_temporary_buffers ();
|
||||
|
||||
_buffer_reallocation_required = false;
|
||||
}
|
||||
|
||||
if (_seek_required) {
|
||||
if (speed() != 1.0f || speed() != -1.0f) {
|
||||
seek ((jack_nframes_t) (_session.transport_frame() * (double) speed()), true);
|
||||
}
|
||||
else {
|
||||
seek (_session.transport_frame(), true);
|
||||
}
|
||||
|
||||
_seek_required = false;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MidiDiskstream::check_record_status (jack_nframes_t transport_frame, jack_nframes_t nframes, bool can_record)
|
||||
{
|
||||
}
|
||||
|
||||
int
|
||||
MidiDiskstream::process (jack_nframes_t transport_frame, jack_nframes_t nframes, jack_nframes_t offset, bool can_record, bool rec_monitors_input)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool
|
||||
MidiDiskstream::commit (jack_nframes_t nframes)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
MidiDiskstream::set_pending_overwrite (bool yn)
|
||||
{
|
||||
/* called from audio thread, so we can use the read ptr and playback sample as we wish */
|
||||
|
||||
pending_overwrite = yn;
|
||||
|
||||
overwrite_frame = playback_sample;
|
||||
//overwrite_offset = channels.front().playback_buf->get_read_ptr();
|
||||
}
|
||||
|
||||
int
|
||||
MidiDiskstream::overwrite_existing_buffers ()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
MidiDiskstream::seek (jack_nframes_t frame, bool complete_refill)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
MidiDiskstream::can_internal_playback_seek (jack_nframes_t distance)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
MidiDiskstream::internal_playback_seek (jack_nframes_t distance)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
MidiDiskstream::read (RawMidi* buf, RawMidi* mixdown_buffer, char * workbuf, jack_nframes_t& start, jack_nframes_t cnt, bool reversed)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
MidiDiskstream::do_refill (RawMidi* mixdown_buffer, float* gain_buffer, char * workbuf)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
MidiDiskstream::do_flush (char * workbuf, bool force_flush)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
MidiDiskstream::transport_stopped (struct tm& when, time_t twhen, bool abort_capture)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
MidiDiskstream::finish_capture (bool rec_monitors_input)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
MidiDiskstream::set_record_enabled (bool yn, void* src)
|
||||
{
|
||||
}
|
||||
|
||||
XMLNode&
|
||||
MidiDiskstream::get_state ()
|
||||
{
|
||||
XMLNode* node = new XMLNode ("MidiDiskstream");
|
||||
char buf[64];
|
||||
LocaleGuard lg (X_("POSIX"));
|
||||
|
||||
snprintf (buf, sizeof(buf), "0x%x", _flags);
|
||||
node->add_property ("flags", buf);
|
||||
|
||||
node->add_property ("playlist", _playlist->name());
|
||||
|
||||
snprintf (buf, sizeof(buf), "%f", _visible_speed);
|
||||
node->add_property ("speed", buf);
|
||||
|
||||
node->add_property("name", _name);
|
||||
snprintf (buf, sizeof(buf), "%" PRIu64, id());
|
||||
node->add_property("id", buf);
|
||||
|
||||
if (!_capturing_sources.empty() && _session.get_record_enabled()) {
|
||||
|
||||
XMLNode* cs_child = new XMLNode (X_("CapturingSources"));
|
||||
XMLNode* cs_grandchild;
|
||||
|
||||
for (vector<SMFSource*>::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;
|
||||
|
||||
if (_session.get_punch_in() && ((pi = _session.locations()->auto_punch_location()) != 0)) {
|
||||
snprintf (buf, sizeof (buf), "%" PRIu32, pi->start());
|
||||
} else {
|
||||
snprintf (buf, sizeof (buf), "%" PRIu32, _session.transport_frame());
|
||||
}
|
||||
|
||||
cs_child->add_property (X_("at"), buf);
|
||||
node->add_child_nocopy (*cs_child);
|
||||
}
|
||||
|
||||
if (_extra_xml) {
|
||||
node->add_child_copy (*_extra_xml);
|
||||
}
|
||||
|
||||
return* node;
|
||||
}
|
||||
|
||||
int
|
||||
MidiDiskstream::set_state (const XMLNode& node)
|
||||
{
|
||||
const XMLProperty* prop;
|
||||
XMLNodeList nlist = node.children();
|
||||
XMLNodeIterator niter;
|
||||
uint32_t nchans = 1;
|
||||
XMLNode* capture_pending_node = 0;
|
||||
LocaleGuard lg (X_("POSIX"));
|
||||
|
||||
in_set_state = true;
|
||||
|
||||
for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
|
||||
if ((*niter)->name() == IO::state_node_name) {
|
||||
deprecated_io_node = new XMLNode (**niter);
|
||||
}
|
||||
|
||||
if ((*niter)->name() == X_("CapturingSources")) {
|
||||
capture_pending_node = *niter;
|
||||
}
|
||||
}
|
||||
|
||||
/* prevent write sources from being created */
|
||||
|
||||
in_set_state = true;
|
||||
|
||||
if ((prop = node.property ("name")) != 0) {
|
||||
_name = prop->value();
|
||||
}
|
||||
|
||||
if (deprecated_io_node) {
|
||||
if ((prop = deprecated_io_node->property ("id")) != 0) {
|
||||
sscanf (prop->value().c_str(), "%" PRIu64, &_id);
|
||||
}
|
||||
} else {
|
||||
if ((prop = node.property ("id")) != 0) {
|
||||
sscanf (prop->value().c_str(), "%" PRIu64, &_id);
|
||||
}
|
||||
}
|
||||
|
||||
if ((prop = node.property ("flags")) != 0) {
|
||||
_flags = strtol (prop->value().c_str(), 0, 0);
|
||||
}
|
||||
|
||||
if ((prop = node.property ("channels")) != 0) {
|
||||
nchans = atoi (prop->value().c_str());
|
||||
}
|
||||
|
||||
if ((prop = node.property ("playlist")) == 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
{
|
||||
bool had_playlist = (_playlist != 0);
|
||||
|
||||
if (find_and_use_playlist (prop->value())) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!had_playlist) {
|
||||
_playlist->set_orig_diskstream_id (_id);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ((prop = node.property ("speed")) != 0) {
|
||||
double sp = atof (prop->value().c_str());
|
||||
|
||||
if (realtime_set_speed (sp, false)) {
|
||||
non_realtime_set_speed ();
|
||||
}
|
||||
}
|
||||
|
||||
in_set_state = false;
|
||||
|
||||
/* make sure this is clear before we do anything else */
|
||||
|
||||
_capturing_sources.clear ();
|
||||
|
||||
/* write sources are handled when we handle the input set
|
||||
up of the IO that owns this DS (::non_realtime_input_change())
|
||||
*/
|
||||
|
||||
in_set_state = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
MidiDiskstream::use_new_write_source (uint32_t n)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
MidiDiskstream::reset_write_sources (bool mark_write_complete, bool force)
|
||||
{
|
||||
}
|
||||
|
||||
int
|
||||
MidiDiskstream::rename_write_sources ()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
MidiDiskstream::set_block_size (jack_nframes_t nframes)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
MidiDiskstream::allocate_temporary_buffers ()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
MidiDiskstream::monitor_input (bool yn)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
MidiDiskstream::set_align_style_from_io ()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
float
|
||||
MidiDiskstream::playback_buffer_load () const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
float
|
||||
MidiDiskstream::capture_buffer_load () const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
MidiDiskstream::use_pending_capture_data (XMLNode& node)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
634
libs/ardour/midi_playlist.cc
Normal file
634
libs/ardour/midi_playlist.cc
Normal file
|
|
@ -0,0 +1,634 @@
|
|||
/*
|
||||
Copyright (C) 2006 Paul Davis
|
||||
Written by Dave Robillard, 2006
|
||||
|
||||
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 <cassert>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <sigc++/bind.h>
|
||||
|
||||
#include <ardour/types.h>
|
||||
#include <ardour/configuration.h>
|
||||
#include <ardour/midi_playlist.h>
|
||||
#include <ardour/midi_region.h>
|
||||
#include <ardour/session.h>
|
||||
|
||||
#include <pbd/error.h>
|
||||
|
||||
#include "i18n.h"
|
||||
|
||||
using namespace ARDOUR;
|
||||
using namespace sigc;
|
||||
using namespace std;
|
||||
|
||||
MidiPlaylist::State::~State ()
|
||||
{}
|
||||
|
||||
MidiPlaylist::MidiPlaylist (Session& session, const XMLNode& node, bool hidden)
|
||||
: Playlist (session, node, hidden)
|
||||
{
|
||||
in_set_state = true;
|
||||
set_state (node);
|
||||
in_set_state = false;
|
||||
|
||||
save_state (_("initial state"));
|
||||
|
||||
if (!hidden) {
|
||||
PlaylistCreated (this); /* EMIT SIGNAL */
|
||||
}
|
||||
}
|
||||
|
||||
MidiPlaylist::MidiPlaylist (Session& session, string name, bool hidden)
|
||||
: Playlist (session, name, hidden)
|
||||
{
|
||||
save_state (_("initial state"));
|
||||
|
||||
if (!hidden) {
|
||||
PlaylistCreated (this); /* EMIT SIGNAL */
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
MidiPlaylist::MidiPlaylist (const MidiPlaylist& other, string name, bool hidden)
|
||||
: Playlist (other, name, hidden)
|
||||
{
|
||||
save_state (_("initial state"));
|
||||
|
||||
/*
|
||||
list<Region*>::const_iterator in_o = other.regions.begin();
|
||||
list<Region*>::iterator in_n = regions.begin();
|
||||
|
||||
while (in_o != other.regions.end()) {
|
||||
MidiRegion *ar = dynamic_cast<MidiRegion *>( (*in_o) );
|
||||
|
||||
for (list<Crossfade *>::const_iterator xfades = other._crossfades.begin(); xfades != other._crossfades.end(); ++xfades) {
|
||||
if ( &(*xfades)->in() == ar) {
|
||||
// We found one! Now copy it!
|
||||
|
||||
list<Region*>::const_iterator out_o = other.regions.begin();
|
||||
list<Region*>::const_iterator out_n = regions.begin();
|
||||
|
||||
while (out_o != other.regions.end()) {
|
||||
|
||||
MidiRegion *ar2 = dynamic_cast<MidiRegion *>( (*out_o) );
|
||||
|
||||
if ( &(*xfades)->out() == ar2) {
|
||||
MidiRegion *in = dynamic_cast<MidiRegion*>( (*in_n) );
|
||||
MidiRegion *out = dynamic_cast<MidiRegion*>( (*out_n) );
|
||||
Crossfade *new_fade = new Crossfade( *(*xfades), in, out);
|
||||
add_crossfade(*new_fade);
|
||||
break;
|
||||
}
|
||||
|
||||
out_o++;
|
||||
out_n++;
|
||||
}
|
||||
// cerr << "HUH!? second region in the crossfade not found!" << endl;
|
||||
}
|
||||
}
|
||||
|
||||
in_o++;
|
||||
in_n++;
|
||||
}
|
||||
*/
|
||||
if (!hidden) {
|
||||
PlaylistCreated (this); /* EMIT SIGNAL */
|
||||
}
|
||||
}
|
||||
|
||||
MidiPlaylist::MidiPlaylist (const MidiPlaylist& other, jack_nframes_t start, jack_nframes_t cnt, string name, bool hidden)
|
||||
: Playlist (other, start, cnt, name, hidden)
|
||||
{
|
||||
save_state (_("initial state"));
|
||||
|
||||
/* this constructor does NOT notify others (session) */
|
||||
}
|
||||
|
||||
MidiPlaylist::~MidiPlaylist ()
|
||||
{
|
||||
set <Region*> all_regions;
|
||||
|
||||
GoingAway (this);
|
||||
|
||||
/* find every region we've ever used, and add it to the set of
|
||||
all regions.
|
||||
*/
|
||||
|
||||
for (RegionList::iterator x = regions.begin(); x != regions.end(); ++x) {
|
||||
all_regions.insert (*x);
|
||||
}
|
||||
|
||||
for (StateMap::iterator i = states.begin(); i != states.end(); ++i) {
|
||||
|
||||
MidiPlaylist::State* apstate = dynamic_cast<MidiPlaylist::State*> (*i);
|
||||
|
||||
for (RegionList::iterator r = apstate->regions.begin(); r != apstate->regions.end(); ++r) {
|
||||
all_regions.insert (*r);
|
||||
}
|
||||
|
||||
delete apstate;
|
||||
}
|
||||
|
||||
/* delete every region */
|
||||
|
||||
for (set<Region *>::iterator ar = all_regions.begin(); ar != all_regions.end(); ++ar) {
|
||||
(*ar)->unlock_sources ();
|
||||
delete *ar;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
struct RegionSortByLayer
|
||||
{
|
||||
bool operator() (Region *a, Region *b)
|
||||
{
|
||||
return a->layer() < b->layer();
|
||||
}
|
||||
};
|
||||
|
||||
jack_nframes_t
|
||||
MidiPlaylist::read (unsigned char *buf, unsigned char *mixdown_buffer, char * workbuf, jack_nframes_t start,
|
||||
jack_nframes_t cnt, unsigned chan_n)
|
||||
{
|
||||
jack_nframes_t ret = cnt;
|
||||
jack_nframes_t end;
|
||||
jack_nframes_t read_frames;
|
||||
jack_nframes_t skip_frames;
|
||||
|
||||
/* optimizing this memset() away involves a lot of conditionals
|
||||
that may well cause more of a hit due to cache misses
|
||||
and related stuff than just doing this here.
|
||||
|
||||
it would be great if someone could measure this
|
||||
at some point.
|
||||
|
||||
one way or another, parts of the requested area
|
||||
that are not written to by Region::region_at()
|
||||
for all Regions that cover the area need to be
|
||||
zeroed.
|
||||
*/
|
||||
|
||||
memset (buf, 0, sizeof (unsigned char) * cnt);
|
||||
|
||||
/* this function is never called from a realtime thread, so
|
||||
its OK to block (for short intervals).
|
||||
*/
|
||||
|
||||
Glib::Mutex::Lock rm (region_lock);
|
||||
|
||||
end = start + cnt - 1;
|
||||
|
||||
read_frames = 0;
|
||||
skip_frames = 0;
|
||||
_read_data_count = 0;
|
||||
|
||||
map<uint32_t,vector<Region*> > relevant_regions;
|
||||
vector<uint32_t> relevant_layers;
|
||||
|
||||
for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
|
||||
if ((*i)->coverage (start, end) != OverlapNone) {
|
||||
|
||||
relevant_regions[(*i)->layer()].push_back (*i);
|
||||
relevant_layers.push_back ((*i)->layer());
|
||||
}
|
||||
}
|
||||
|
||||
// RegionSortByLayer layer_cmp;
|
||||
// relevant_regions.sort (layer_cmp);
|
||||
|
||||
|
||||
for (vector<uint32_t>::iterator l = relevant_layers.begin(); l != relevant_layers.end(); ++l) {
|
||||
|
||||
// FIXME: Should be vector<MidiRegion*>
|
||||
vector<Region*>& r (relevant_regions[*l]);
|
||||
|
||||
for (vector<Region*>::iterator i = r.begin(); i != r.end(); ++i) {
|
||||
MidiRegion* const mr = dynamic_cast<MidiRegion*>(*i);
|
||||
assert(mr);
|
||||
mr->read_at (buf, mixdown_buffer, workbuf, start, cnt, chan_n, read_frames, skip_frames);
|
||||
_read_data_count += mr->read_data_count();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
MidiPlaylist::remove_dependents (Region& region)
|
||||
{
|
||||
MidiRegion* r = dynamic_cast<MidiRegion*> (®ion);
|
||||
|
||||
if (r == 0) {
|
||||
PBD::fatal << _("programming error: non-midi Region passed to remove_overlap in midi playlist")
|
||||
<< endmsg;
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
MidiPlaylist::flush_notifications ()
|
||||
{
|
||||
Playlist::flush_notifications();
|
||||
|
||||
if (in_flush) {
|
||||
return;
|
||||
}
|
||||
|
||||
in_flush = true;
|
||||
|
||||
in_flush = false;
|
||||
}
|
||||
|
||||
void
|
||||
MidiPlaylist::refresh_dependents (Region& r)
|
||||
{
|
||||
MidiRegion* ar = dynamic_cast<MidiRegion*>(&r);
|
||||
|
||||
if (ar == 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MidiPlaylist::finalize_split_region (Region *o, Region *l, Region *r)
|
||||
{
|
||||
/*
|
||||
MidiRegion *orig = dynamic_cast<MidiRegion*>(o);
|
||||
MidiRegion *left = dynamic_cast<MidiRegion*>(l);
|
||||
MidiRegion *right = dynamic_cast<MidiRegion*>(r);
|
||||
|
||||
for (Crossfades::iterator x = _crossfades.begin(); x != _crossfades.end();) {
|
||||
Crossfades::iterator tmp;
|
||||
tmp = x;
|
||||
++tmp;
|
||||
|
||||
Crossfade *fade = 0;
|
||||
|
||||
if ((*x)->_in == orig) {
|
||||
if (! (*x)->covers(right->position())) {
|
||||
fade = new Crossfade( *(*x), left, (*x)->_out);
|
||||
} else {
|
||||
// Overlap, the crossfade is copied on the left side of the right region instead
|
||||
fade = new Crossfade( *(*x), right, (*x)->_out);
|
||||
}
|
||||
}
|
||||
|
||||
if ((*x)->_out == orig) {
|
||||
if (! (*x)->covers(right->position())) {
|
||||
fade = new Crossfade( *(*x), (*x)->_in, right);
|
||||
} else {
|
||||
// Overlap, the crossfade is copied on the right side of the left region instead
|
||||
fade = new Crossfade( *(*x), (*x)->_in, left);
|
||||
}
|
||||
}
|
||||
|
||||
if (fade) {
|
||||
_crossfades.remove( (*x) );
|
||||
add_crossfade (*fade);
|
||||
}
|
||||
x = tmp;
|
||||
}*/
|
||||
}
|
||||
|
||||
void
|
||||
MidiPlaylist::check_dependents (Region& r, bool norefresh)
|
||||
{
|
||||
MidiRegion* other;
|
||||
MidiRegion* region;
|
||||
MidiRegion* top;
|
||||
MidiRegion* bottom;
|
||||
|
||||
if (in_set_state || in_partition) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ((region = dynamic_cast<MidiRegion*> (&r)) == 0) {
|
||||
PBD::fatal << _("programming error: non-midi Region tested for overlap in midi playlist")
|
||||
<< endmsg;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!norefresh) {
|
||||
refresh_dependents (r);
|
||||
}
|
||||
|
||||
for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
|
||||
|
||||
other = dynamic_cast<MidiRegion*> (*i);
|
||||
|
||||
if (other == region) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (other->muted() || region->muted()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (other->layer() < region->layer()) {
|
||||
top = region;
|
||||
bottom = other;
|
||||
} else {
|
||||
top = other;
|
||||
bottom = region;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
MidiPlaylist::set_state (const XMLNode& node)
|
||||
{
|
||||
/*
|
||||
XMLNode *child;
|
||||
XMLNodeList nlist;
|
||||
XMLNodeConstIterator niter;
|
||||
|
||||
if (!in_set_state) {
|
||||
Playlist::set_state (node);
|
||||
}
|
||||
|
||||
nlist = node.children();
|
||||
|
||||
for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
|
||||
|
||||
child = *niter;
|
||||
|
||||
}*/
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
MidiPlaylist::drop_all_states ()
|
||||
{
|
||||
set<Region*> all_regions;
|
||||
|
||||
/* find every region we've ever used, and add it to the set of
|
||||
all regions. same for xfades;
|
||||
*/
|
||||
|
||||
for (StateMap::iterator i = states.begin(); i != states.end(); ++i) {
|
||||
|
||||
MidiPlaylist::State* apstate = dynamic_cast<MidiPlaylist::State*> (*i);
|
||||
|
||||
for (RegionList::iterator r = apstate->regions.begin(); r != apstate->regions.end(); ++r) {
|
||||
all_regions.insert (*r);
|
||||
}
|
||||
}
|
||||
|
||||
/* now remove from the "all" lists every region that is in the current list. */
|
||||
|
||||
for (list<Region*>::iterator i = regions.begin(); i != regions.end(); ++i) {
|
||||
set
|
||||
<Region*>::iterator x = all_regions.find (*i);
|
||||
if (x != all_regions.end()) {
|
||||
all_regions.erase (x);
|
||||
}
|
||||
}
|
||||
|
||||
/* delete every region that is left - these are all things that are part of our "history" */
|
||||
|
||||
for (set
|
||||
<Region *>::iterator ar = all_regions.begin(); ar != all_regions.end(); ++ar) {
|
||||
(*ar)->unlock_sources ();
|
||||
delete *ar;
|
||||
}
|
||||
|
||||
/* Now do the generic thing ... */
|
||||
|
||||
StateManager::drop_all_states ();
|
||||
}
|
||||
|
||||
StateManager::State*
|
||||
MidiPlaylist::state_factory (std::string why) const
|
||||
{
|
||||
State* state = new State (why);
|
||||
|
||||
state->regions = regions;
|
||||
state->region_states.clear ();
|
||||
for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
|
||||
state->region_states.push_back ((*i)->get_memento());
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
Change
|
||||
MidiPlaylist::restore_state (StateManager::State& state)
|
||||
{
|
||||
{
|
||||
RegionLock rlock (this);
|
||||
State* apstate = dynamic_cast<State*> (&state);
|
||||
|
||||
in_set_state = true;
|
||||
|
||||
regions = apstate->regions;
|
||||
|
||||
for (list<UndoAction>::iterator s = apstate->
|
||||
region_states.begin();
|
||||
s != apstate->region_states.end();
|
||||
++s) {
|
||||
(*s) ();
|
||||
}
|
||||
|
||||
in_set_state = false;
|
||||
}
|
||||
|
||||
notify_length_changed ();
|
||||
return Change (~0);
|
||||
}
|
||||
|
||||
UndoAction
|
||||
MidiPlaylist::get_memento () const
|
||||
{
|
||||
return sigc::bind (mem_fun (*(const_cast<MidiPlaylist*> (this)), &StateManager::use_state), _current_state_id);
|
||||
}
|
||||
|
||||
|
||||
XMLNode&
|
||||
MidiPlaylist::state (bool full_state)
|
||||
{
|
||||
XMLNode& node = Playlist::state (full_state);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
void
|
||||
MidiPlaylist::dump () const
|
||||
{
|
||||
Region *r;
|
||||
|
||||
cerr << "Playlist \"" << _name << "\" " << endl
|
||||
<< regions.size() << " regions "
|
||||
<< endl;
|
||||
|
||||
for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
|
||||
r = *i;
|
||||
cerr << " " << r->name() << " @ " << r << " ["
|
||||
<< r->start() << "+" << r->length()
|
||||
<< "] at "
|
||||
<< r->position()
|
||||
<< " on layer "
|
||||
<< r->layer ()
|
||||
<< endl;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
MidiPlaylist::destroy_region (Region* region)
|
||||
{
|
||||
MidiRegion* r = dynamic_cast<MidiRegion*> (region);
|
||||
bool changed = false;
|
||||
|
||||
if (r == 0) {
|
||||
PBD::fatal << _("programming error: non-midi Region passed to remove_overlap in midi playlist")
|
||||
<< endmsg;
|
||||
/*NOTREACHED*/
|
||||
return false;
|
||||
}
|
||||
|
||||
{
|
||||
RegionLock rlock (this);
|
||||
RegionList::iterator i;
|
||||
RegionList::iterator tmp;
|
||||
|
||||
for (i = regions.begin(); i != regions.end(); ) {
|
||||
|
||||
tmp = i;
|
||||
++tmp;
|
||||
|
||||
if ((*i) == region) {
|
||||
(*i)->unlock_sources ();
|
||||
regions.erase (i);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
i = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
for (StateMap::iterator s = states.begin(); s != states.end(); ) {
|
||||
StateMap::iterator tmp;
|
||||
|
||||
tmp = s;
|
||||
++tmp;
|
||||
|
||||
State* astate = dynamic_cast<State*> (*s);
|
||||
|
||||
list<UndoAction>::iterator rsi, rsitmp;
|
||||
RegionList::iterator ri, ritmp;
|
||||
|
||||
for (ri = astate->regions.begin(), rsi = astate->region_states.begin();
|
||||
ri != astate->regions.end() && rsi != astate->region_states.end();) {
|
||||
|
||||
|
||||
ritmp = ri;
|
||||
++ritmp;
|
||||
|
||||
rsitmp = rsi;
|
||||
++rsitmp;
|
||||
|
||||
if (region == (*ri)) {
|
||||
astate->regions.erase (ri);
|
||||
astate->region_states.erase (rsi);
|
||||
}
|
||||
|
||||
ri = ritmp;
|
||||
rsi = rsitmp;
|
||||
}
|
||||
|
||||
s = tmp;
|
||||
}
|
||||
|
||||
|
||||
if (changed) {
|
||||
/* overload this, it normally means "removed", not destroyed */
|
||||
notify_region_removed (region);
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
MidiPlaylist::get_equivalent_regions (const MidiRegion& other, vector<MidiRegion*>& results)
|
||||
{
|
||||
for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
|
||||
|
||||
MidiRegion* ar = dynamic_cast<MidiRegion*> (*i);
|
||||
|
||||
if (ar) {
|
||||
if (Config->get_use_overlap_equivalency()) {
|
||||
if (ar->overlap_equivalent (other)) {
|
||||
results.push_back (ar);
|
||||
} else if (ar->equivalent (other)) {
|
||||
results.push_back (ar);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MidiPlaylist::get_region_list_equivalent_regions (const MidiRegion& other, vector<MidiRegion*>& results)
|
||||
{
|
||||
for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
|
||||
|
||||
MidiRegion* ar = dynamic_cast<MidiRegion*> (*i);
|
||||
|
||||
if (ar && ar->region_list_equivalent (other)) {
|
||||
results.push_back (ar);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
MidiPlaylist::region_changed (Change what_changed, Region* region)
|
||||
{
|
||||
if (in_flush || in_set_state) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Change our_interests = Change (/*MidiRegion::FadeInChanged|
|
||||
MidiRegion::FadeOutChanged|
|
||||
MidiRegion::FadeInActiveChanged|
|
||||
MidiRegion::FadeOutActiveChanged|
|
||||
MidiRegion::EnvelopeActiveChanged|
|
||||
MidiRegion::ScaleAmplitudeChanged|
|
||||
MidiRegion::EnvelopeChanged*/);
|
||||
bool parent_wants_notify;
|
||||
|
||||
parent_wants_notify = Playlist::region_changed (what_changed, region);
|
||||
|
||||
maybe_save_state (_("region modified"));
|
||||
|
||||
if ((parent_wants_notify || (what_changed & our_interests))) {
|
||||
notify_modified ();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
647
libs/ardour/midi_region.cc
Normal file
647
libs/ardour/midi_region.cc
Normal file
|
|
@ -0,0 +1,647 @@
|
|||
/*
|
||||
Copyright (C) 2006 Paul Davis
|
||||
Written by Dave Robillard, 2006
|
||||
|
||||
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 <cmath>
|
||||
#include <climits>
|
||||
#include <cfloat>
|
||||
|
||||
#include <set>
|
||||
|
||||
#include <sigc++/bind.h>
|
||||
#include <sigc++/class_slot.h>
|
||||
|
||||
#include <glibmm/thread.h>
|
||||
|
||||
#include <pbd/basename.h>
|
||||
#include <pbd/xml++.h>
|
||||
|
||||
#include <ardour/midi_region.h>
|
||||
#include <ardour/session.h>
|
||||
#include <ardour/gain.h>
|
||||
#include <ardour/dB.h>
|
||||
#include <ardour/playlist.h>
|
||||
#include <ardour/midi_source.h>
|
||||
|
||||
#include "i18n.h"
|
||||
#include <locale.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace ARDOUR;
|
||||
|
||||
MidiRegionState::MidiRegionState (string why)
|
||||
: RegionState (why)
|
||||
{
|
||||
}
|
||||
|
||||
MidiRegion::MidiRegion (MidiSource& src, jack_nframes_t start, jack_nframes_t length, bool announce)
|
||||
: Region (start, length, PBD::basename_nosuffix(src.name()), 0, Region::Flag(Region::DefaultFlags|Region::External))
|
||||
{
|
||||
/* basic MidiRegion constructor */
|
||||
|
||||
sources.push_back (&src);
|
||||
master_sources.push_back (&src);
|
||||
src.GoingAway.connect (mem_fun (*this, &MidiRegion::source_deleted));
|
||||
|
||||
save_state ("initial state");
|
||||
|
||||
if (announce) {
|
||||
CheckNewRegion (this); /* EMIT SIGNAL */
|
||||
}
|
||||
}
|
||||
|
||||
MidiRegion::MidiRegion (MidiSource& src, jack_nframes_t start, jack_nframes_t length, const string& name, layer_t layer, Flag flags, bool announce)
|
||||
: Region (start, length, name, layer, flags)
|
||||
{
|
||||
/* basic MidiRegion constructor */
|
||||
|
||||
sources.push_back (&src);
|
||||
master_sources.push_back (&src);
|
||||
src.GoingAway.connect (mem_fun (*this, &MidiRegion::source_deleted));
|
||||
|
||||
save_state ("initial state");
|
||||
|
||||
if (announce) {
|
||||
CheckNewRegion (this); /* EMIT SIGNAL */
|
||||
}
|
||||
}
|
||||
|
||||
MidiRegion::MidiRegion (SourceList& srcs, jack_nframes_t start, jack_nframes_t length, const string& name, layer_t layer, Flag flags, bool announce)
|
||||
: Region (start, length, name, layer, flags)
|
||||
{
|
||||
/* basic MidiRegion constructor */
|
||||
#if 0
|
||||
for (SourceList::iterator i=srcs.begin(); i != srcs.end(); ++i) {
|
||||
sources.push_back (*i);
|
||||
master_sources.push_back (*i);
|
||||
(*i)->GoingAway.connect (mem_fun (*this, &MidiRegion::source_deleted));
|
||||
}
|
||||
|
||||
{
|
||||
/* create a new MidiRegion, that is part of an existing one */
|
||||
|
||||
set<MidiSource*> unique_srcs;
|
||||
|
||||
for (SourceList::const_iterator i= other.sources.begin(); i != other.sources.end(); ++i) {
|
||||
sources.push_back (*i);
|
||||
(*i)->GoingAway.connect (mem_fun (*this, &MidiRegion::source_deleted));
|
||||
unique_srcs.insert (*i);
|
||||
}
|
||||
|
||||
for (SourceList::const_iterator i = other.master_sources.begin(); i != other.master_sources.end(); ++i) {
|
||||
if (unique_srcs.find (*i) == unique_srcs.end()) {
|
||||
(*i)->GoingAway.connect (mem_fun (*this, &MidiRegion::source_deleted));
|
||||
}
|
||||
master_sources.push_back (*i);
|
||||
}
|
||||
|
||||
save_state ("initial state");
|
||||
|
||||
if (announce) {
|
||||
CheckNewRegion (this); /* EMIT SIGNAL */
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
MidiRegion::MidiRegion (const MidiRegion &other)
|
||||
: Region (other)
|
||||
{
|
||||
/* Pure copy constructor */
|
||||
|
||||
set<MidiSource*> unique_srcs;
|
||||
|
||||
for (SourceList::const_iterator i = other.sources.begin(); i != other.sources.end(); ++i) {
|
||||
sources.push_back (*i);
|
||||
(*i)->GoingAway.connect (mem_fun (*this, &MidiRegion::source_deleted));
|
||||
unique_srcs.insert (*i);
|
||||
}
|
||||
|
||||
for (SourceList::const_iterator i = other.master_sources.begin(); i != other.master_sources.end(); ++i) {
|
||||
master_sources.push_back (*i);
|
||||
if (unique_srcs.find (*i) == unique_srcs.end()) {
|
||||
(*i)->GoingAway.connect (mem_fun (*this, &MidiRegion::source_deleted));
|
||||
}
|
||||
}
|
||||
|
||||
save_state ("initial state");
|
||||
|
||||
/* NOTE: no CheckNewRegion signal emitted here. This is the copy constructor */
|
||||
}
|
||||
|
||||
MidiRegion::MidiRegion (MidiSource& src, const XMLNode& node)
|
||||
: Region (node)
|
||||
{
|
||||
sources.push_back (&src);
|
||||
master_sources.push_back (&src);
|
||||
src.GoingAway.connect (mem_fun (*this, &MidiRegion::source_deleted));
|
||||
|
||||
if (set_state (node)) {
|
||||
throw failed_constructor();
|
||||
}
|
||||
|
||||
save_state ("initial state");
|
||||
|
||||
CheckNewRegion (this); /* EMIT SIGNAL */
|
||||
}
|
||||
|
||||
MidiRegion::MidiRegion (SourceList& srcs, const XMLNode& node)
|
||||
: Region (node)
|
||||
{
|
||||
/* basic MidiRegion constructor */
|
||||
|
||||
set<MidiSource*> unique_srcs;
|
||||
|
||||
for (SourceList::iterator i=srcs.begin(); i != srcs.end(); ++i) {
|
||||
sources.push_back (*i);
|
||||
(*i)->GoingAway.connect (mem_fun (*this, &MidiRegion::source_deleted));
|
||||
unique_srcs.insert (*i);
|
||||
}
|
||||
|
||||
for (SourceList::iterator i = srcs.begin(); i != srcs.end(); ++i) {
|
||||
master_sources.push_back (*i);
|
||||
if (unique_srcs.find (*i) == unique_srcs.end()) {
|
||||
(*i)->GoingAway.connect (mem_fun (*this, &MidiRegion::source_deleted));
|
||||
}
|
||||
}
|
||||
|
||||
if (set_state (node)) {
|
||||
throw failed_constructor();
|
||||
}
|
||||
|
||||
save_state ("initial state");
|
||||
|
||||
CheckNewRegion (this); /* EMIT SIGNAL */
|
||||
}
|
||||
|
||||
MidiRegion::~MidiRegion ()
|
||||
{
|
||||
GoingAway (this);
|
||||
}
|
||||
|
||||
StateManager::State*
|
||||
MidiRegion::state_factory (std::string why) const
|
||||
{
|
||||
MidiRegionState* state = new MidiRegionState (why);
|
||||
|
||||
Region::store_state (*state);
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
Change
|
||||
MidiRegion::restore_state (StateManager::State& sstate)
|
||||
{
|
||||
MidiRegionState* state = dynamic_cast<MidiRegionState*> (&sstate);
|
||||
|
||||
Change what_changed = Region::restore_and_return_flags (*state);
|
||||
|
||||
if (_flags != Flag (state->_flags)) {
|
||||
|
||||
//uint32_t old_flags = _flags;
|
||||
|
||||
_flags = Flag (state->_flags);
|
||||
|
||||
}
|
||||
|
||||
/* XXX need a way to test stored state versus current for envelopes */
|
||||
|
||||
what_changed = Change (what_changed);
|
||||
|
||||
return what_changed;
|
||||
}
|
||||
|
||||
UndoAction
|
||||
MidiRegion::get_memento() const
|
||||
{
|
||||
return sigc::bind (mem_fun (*(const_cast<MidiRegion *> (this)), &StateManager::use_state), _current_state_id);
|
||||
}
|
||||
|
||||
bool
|
||||
MidiRegion::verify_length (jack_nframes_t len)
|
||||
{
|
||||
for (uint32_t n=0; n < sources.size(); ++n) {
|
||||
if (_start > sources[n]->length() - len) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
MidiRegion::verify_start_and_length (jack_nframes_t new_start, jack_nframes_t new_length)
|
||||
{
|
||||
for (uint32_t n=0; n < sources.size(); ++n) {
|
||||
if (new_length > sources[n]->length() - new_start) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool
|
||||
MidiRegion::verify_start (jack_nframes_t pos)
|
||||
{
|
||||
for (uint32_t n=0; n < sources.size(); ++n) {
|
||||
if (pos > sources[n]->length() - _length) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
MidiRegion::verify_start_mutable (jack_nframes_t& new_start)
|
||||
{
|
||||
for (uint32_t n=0; n < sources.size(); ++n) {
|
||||
if (new_start > sources[n]->length() - _length) {
|
||||
new_start = sources[n]->length() - _length;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
jack_nframes_t
|
||||
MidiRegion::read_at (unsigned char *buf, unsigned char *mixdown_buffer, char * workbuf, jack_nframes_t position,
|
||||
jack_nframes_t cnt,
|
||||
uint32_t chan_n, jack_nframes_t read_frames, jack_nframes_t skip_frames) const
|
||||
{
|
||||
return _read_at (sources, buf, mixdown_buffer, workbuf, position, cnt, chan_n, read_frames, skip_frames);
|
||||
}
|
||||
|
||||
jack_nframes_t
|
||||
MidiRegion::master_read_at (unsigned char *buf, unsigned char *mixdown_buffer, char * workbuf, jack_nframes_t position,
|
||||
jack_nframes_t cnt, uint32_t chan_n) const
|
||||
{
|
||||
return _read_at (master_sources, buf, mixdown_buffer, workbuf, position, cnt, chan_n, 0, 0);
|
||||
}
|
||||
|
||||
jack_nframes_t
|
||||
MidiRegion::_read_at (const SourceList& srcs, unsigned char *buf, unsigned char *mixdown_buffer, char * workbuf,
|
||||
jack_nframes_t position, jack_nframes_t cnt,
|
||||
uint32_t chan_n, jack_nframes_t read_frames, jack_nframes_t skip_frames) const
|
||||
{
|
||||
jack_nframes_t internal_offset;
|
||||
jack_nframes_t buf_offset;
|
||||
jack_nframes_t to_read;
|
||||
|
||||
/* precondition: caller has verified that we cover the desired section */
|
||||
|
||||
if (chan_n >= sources.size()) {
|
||||
return 0; /* read nothing */
|
||||
}
|
||||
|
||||
if (position < _position) {
|
||||
internal_offset = 0;
|
||||
buf_offset = _position - position;
|
||||
cnt -= buf_offset;
|
||||
} else {
|
||||
internal_offset = position - _position;
|
||||
buf_offset = 0;
|
||||
}
|
||||
|
||||
if (internal_offset >= _length) {
|
||||
return 0; /* read nothing */
|
||||
}
|
||||
|
||||
|
||||
if ((to_read = min (cnt, _length - internal_offset)) == 0) {
|
||||
return 0; /* read nothing */
|
||||
}
|
||||
|
||||
if (opaque()) {
|
||||
/* overwrite whatever is there */
|
||||
mixdown_buffer = buf + buf_offset;
|
||||
} else {
|
||||
mixdown_buffer += buf_offset;
|
||||
}
|
||||
|
||||
if (muted()) {
|
||||
return 0; /* read nothing */
|
||||
}
|
||||
|
||||
_read_data_count = 0;
|
||||
|
||||
if (srcs[chan_n]->read (mixdown_buffer, _start + internal_offset, to_read, workbuf) != to_read) {
|
||||
return 0; /* "read nothing" */
|
||||
}
|
||||
|
||||
_read_data_count += srcs[chan_n]->read_data_count();
|
||||
|
||||
if (!opaque()) {
|
||||
|
||||
/* gack. the things we do for users.
|
||||
*/
|
||||
|
||||
buf += buf_offset;
|
||||
|
||||
for (jack_nframes_t n = 0; n < to_read; ++n) {
|
||||
buf[n] += mixdown_buffer[n];
|
||||
}
|
||||
}
|
||||
|
||||
return to_read;
|
||||
}
|
||||
|
||||
XMLNode&
|
||||
MidiRegion::get_state ()
|
||||
{
|
||||
return state (true);
|
||||
}
|
||||
|
||||
XMLNode&
|
||||
MidiRegion::state (bool full)
|
||||
{
|
||||
XMLNode& node (Region::state (full));
|
||||
//XMLNode *child;
|
||||
char buf[64];
|
||||
char buf2[64];
|
||||
LocaleGuard lg (X_("POSIX"));
|
||||
|
||||
snprintf (buf, sizeof (buf), "0x%x", (int) _flags);
|
||||
node.add_property ("flags", buf);
|
||||
|
||||
for (uint32_t n=0; n < sources.size(); ++n) {
|
||||
snprintf (buf2, sizeof(buf2), "source-%d", n);
|
||||
snprintf (buf, sizeof(buf), "%" PRIu64, sources[n]->id());
|
||||
node.add_property (buf2, buf);
|
||||
}
|
||||
|
||||
snprintf (buf, sizeof (buf), "%u", (uint32_t) sources.size());
|
||||
node.add_property ("channels", buf);
|
||||
|
||||
if (full && _extra_xml) {
|
||||
node.add_child_copy (*_extra_xml);
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
int
|
||||
MidiRegion::set_state (const XMLNode& node)
|
||||
{
|
||||
const XMLNodeList& nlist = node.children();
|
||||
const XMLProperty *prop;
|
||||
LocaleGuard lg (X_("POSIX"));
|
||||
|
||||
Region::set_state (node);
|
||||
|
||||
if ((prop = node.property ("flags")) != 0) {
|
||||
_flags = Flag (strtol (prop->value().c_str(), (char **) 0, 16));
|
||||
|
||||
_flags = Flag (_flags & ~Region::LeftOfSplit);
|
||||
_flags = Flag (_flags & ~Region::RightOfSplit);
|
||||
}
|
||||
|
||||
/* Now find envelope description and other misc child items */
|
||||
|
||||
for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) {
|
||||
|
||||
XMLNode *child;
|
||||
//XMLProperty *prop;
|
||||
|
||||
child = (*niter);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
MidiRegion::separate_by_channel (Session& session, vector<MidiRegion*>& v) const
|
||||
{
|
||||
SourceList srcs;
|
||||
string new_name;
|
||||
|
||||
for (SourceList::const_iterator i = master_sources.begin(); i != master_sources.end(); ++i) {
|
||||
|
||||
srcs.clear ();
|
||||
srcs.push_back (*i);
|
||||
|
||||
/* generate a new name */
|
||||
|
||||
if (session.region_name (new_name, _name)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* create a copy with just one source */
|
||||
|
||||
v.push_back (new MidiRegion (srcs, _start, _length, new_name, _layer, _flags));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
MidiRegion::source_deleted (Source* ignored)
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
void
|
||||
MidiRegion::lock_sources ()
|
||||
{
|
||||
SourceList::iterator i;
|
||||
set<MidiSource*> unique_srcs;
|
||||
|
||||
for (i = sources.begin(); i != sources.end(); ++i) {
|
||||
unique_srcs.insert (*i);
|
||||
(*i)->use ();
|
||||
}
|
||||
|
||||
for (i = master_sources.begin(); i != master_sources.end(); ++i) {
|
||||
if (unique_srcs.find (*i) == unique_srcs.end()) {
|
||||
(*i)->use ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MidiRegion::unlock_sources ()
|
||||
{
|
||||
SourceList::iterator i;
|
||||
set<MidiSource*> unique_srcs;
|
||||
|
||||
for (i = sources.begin(); i != sources.end(); ++i) {
|
||||
unique_srcs.insert (*i);
|
||||
(*i)->release ();
|
||||
}
|
||||
|
||||
for (i = master_sources.begin(); i != master_sources.end(); ++i) {
|
||||
if (unique_srcs.find (*i) == unique_srcs.end()) {
|
||||
(*i)->release ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vector<string>
|
||||
MidiRegion::master_source_names ()
|
||||
{
|
||||
SourceList::iterator i;
|
||||
|
||||
vector<string> names;
|
||||
for (i = master_sources.begin(); i != master_sources.end(); ++i) {
|
||||
names.push_back((*i)->name());
|
||||
}
|
||||
|
||||
return names;
|
||||
}
|
||||
|
||||
bool
|
||||
MidiRegion::region_list_equivalent (const MidiRegion& other) const
|
||||
{
|
||||
return size_equivalent (other) && source_equivalent (other) && _name == other._name;
|
||||
}
|
||||
|
||||
bool
|
||||
MidiRegion::source_equivalent (const MidiRegion& other) const
|
||||
{
|
||||
SourceList::const_iterator i;
|
||||
SourceList::const_iterator io;
|
||||
|
||||
for (i = sources.begin(), io = other.sources.begin(); i != sources.end() && io != other.sources.end(); ++i, ++io) {
|
||||
if ((*i)->id() != (*io)->id()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = master_sources.begin(), io = other.master_sources.begin(); i != master_sources.end() && io != other.master_sources.end(); ++i, ++io) {
|
||||
if ((*i)->id() != (*io)->id()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
MidiRegion::overlap_equivalent (const MidiRegion& other) const
|
||||
{
|
||||
return coverage (other.first_frame(), other.last_frame()) != OverlapNone;
|
||||
}
|
||||
|
||||
bool
|
||||
MidiRegion::equivalent (const MidiRegion& other) const
|
||||
{
|
||||
return _start == other._start &&
|
||||
_position == other._position &&
|
||||
_length == other._length;
|
||||
}
|
||||
|
||||
bool
|
||||
MidiRegion::size_equivalent (const MidiRegion& other) const
|
||||
{
|
||||
return _start == other._start &&
|
||||
_length == other._length;
|
||||
}
|
||||
|
||||
#if 0
|
||||
int
|
||||
MidiRegion::exportme (Session& session, AudioExportSpecification& spec)
|
||||
{
|
||||
const jack_nframes_t blocksize = 4096;
|
||||
jack_nframes_t to_read;
|
||||
int status = -1;
|
||||
|
||||
spec.channels = sources.size();
|
||||
|
||||
if (spec.prepare (blocksize, session.frame_rate())) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
spec.pos = 0;
|
||||
spec.total_frames = _length;
|
||||
|
||||
while (spec.pos < _length && !spec.stop) {
|
||||
|
||||
|
||||
/* step 1: interleave */
|
||||
|
||||
to_read = min (_length - spec.pos, blocksize);
|
||||
|
||||
if (spec.channels == 1) {
|
||||
|
||||
if (sources.front()->read (spec.dataF, _start + spec.pos, to_read, 0) != to_read) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
Sample buf[blocksize];
|
||||
|
||||
for (uint32_t chan = 0; chan < spec.channels; ++chan) {
|
||||
|
||||
if (sources[chan]->read (buf, _start + spec.pos, to_read, 0) != to_read) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (jack_nframes_t x = 0; x < to_read; ++x) {
|
||||
spec.dataF[chan+(x*spec.channels)] = buf[x];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (spec.process (to_read)) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
spec.pos += to_read;
|
||||
spec.progress = (double) spec.pos /_length;
|
||||
|
||||
}
|
||||
|
||||
status = 0;
|
||||
|
||||
out:
|
||||
spec.running = false;
|
||||
spec.status = status;
|
||||
spec.clear();
|
||||
|
||||
return status;
|
||||
}
|
||||
#endif
|
||||
|
||||
Region*
|
||||
MidiRegion::get_parent()
|
||||
{
|
||||
#if 0
|
||||
Region* r = 0;
|
||||
|
||||
if (_playlist) {
|
||||
r = _playlist->session().find_whole_file_parent (*this);
|
||||
}
|
||||
|
||||
return r;
|
||||
#endif
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
MidiRegion::speed_mismatch (float sr) const
|
||||
{
|
||||
#if 0
|
||||
if (sources.empty()) {
|
||||
/* impossible, but ... */
|
||||
return false;
|
||||
}
|
||||
|
||||
float fsr = sources.front()->sample_rate();
|
||||
|
||||
return fsr == sr;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
133
libs/ardour/midi_source.cc
Normal file
133
libs/ardour/midi_source.cc
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
/*
|
||||
Copyright (C) 2006 Paul Davis
|
||||
Written by Dave Robillard, 2006
|
||||
|
||||
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 <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <poll.h>
|
||||
#include <float.h>
|
||||
#include <cerrno>
|
||||
#include <ctime>
|
||||
#include <cmath>
|
||||
#include <iomanip>
|
||||
#include <algorithm>
|
||||
|
||||
#include <pbd/xml++.h>
|
||||
#include <pbd/pthread_utils.h>
|
||||
|
||||
#include <ardour/midi_source.h>
|
||||
|
||||
#include "i18n.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace ARDOUR;
|
||||
using namespace PBD;
|
||||
|
||||
sigc::signal<void,MidiSource *> MidiSource::MidiSourceCreated;
|
||||
|
||||
MidiSource::MidiSource (string name)
|
||||
: Source (name)
|
||||
{
|
||||
_read_data_count = 0;
|
||||
_write_data_count = 0;
|
||||
}
|
||||
|
||||
MidiSource::MidiSource (const XMLNode& node)
|
||||
: Source (node)
|
||||
{
|
||||
_read_data_count = 0;
|
||||
_write_data_count = 0;
|
||||
|
||||
if (set_state (node)) {
|
||||
throw failed_constructor();
|
||||
}
|
||||
}
|
||||
|
||||
MidiSource::~MidiSource ()
|
||||
{
|
||||
}
|
||||
|
||||
XMLNode&
|
||||
MidiSource::get_state ()
|
||||
{
|
||||
XMLNode& node (Source::get_state());
|
||||
|
||||
if (_captured_for.length()) {
|
||||
node.add_property ("captured-for", _captured_for);
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
int
|
||||
MidiSource::set_state (const XMLNode& node)
|
||||
{
|
||||
const XMLProperty* prop;
|
||||
|
||||
Source::set_state (node);
|
||||
|
||||
if ((prop = node.property ("captured-for")) != 0) {
|
||||
_captured_for = prop->value();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
jack_nframes_t
|
||||
MidiSource::read (unsigned char *dst, jack_nframes_t start, jack_nframes_t cnt, char * workbuf) const
|
||||
{
|
||||
//Glib::Mutex::Lock lm (_lock);
|
||||
//return read_unlocked (dst, start, cnt, workbuf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
jack_nframes_t
|
||||
MidiSource::write (unsigned char *dst, jack_nframes_t cnt, char * workbuf)
|
||||
{
|
||||
//Glib::Mutex::Lock lm (_lock);
|
||||
//return write_unlocked (dst, cnt, workbuf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
MidiSource::file_changed (string path)
|
||||
{
|
||||
struct stat stat_file;
|
||||
//struct stat stat_peak;
|
||||
|
||||
int e1 = stat (path.c_str(), &stat_file);
|
||||
//int e2 = stat (peak_path(path).c_str(), &stat_peak);
|
||||
|
||||
if (!e1){//&& !e2 && stat_file.st_mtime > stat_peak.st_mtime){
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
MidiSource::update_length (jack_nframes_t pos, jack_nframes_t cnt)
|
||||
{
|
||||
if (pos + cnt > _length) {
|
||||
_length = pos+cnt;
|
||||
}
|
||||
}
|
||||
|
||||
1167
libs/ardour/midi_track.cc
Normal file
1167
libs/ardour/midi_track.cc
Normal file
File diff suppressed because it is too large
Load diff
406
libs/ardour/smf_source.cc
Normal file
406
libs/ardour/smf_source.cc
Normal file
|
|
@ -0,0 +1,406 @@
|
|||
/*
|
||||
Copyright (C) 2006 Paul Davis
|
||||
Written by Dave Robillard, 2006
|
||||
|
||||
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 <vector>
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <pbd/mountpoint.h>
|
||||
#include <pbd/pathscanner.h>
|
||||
#include <pbd/stl_delete.h>
|
||||
#include <pbd/strsplit.h>
|
||||
|
||||
#include <glibmm/miscutils.h>
|
||||
|
||||
#include <ardour/smf_source.h>
|
||||
#include <ardour/session.h>
|
||||
|
||||
#include "i18n.h"
|
||||
|
||||
using namespace ARDOUR;
|
||||
|
||||
string SMFSource::_search_path;
|
||||
|
||||
/*sigc::signal<void,struct tm*, time_t> SMFSource::HeaderPositionOffsetChanged;
|
||||
bool SMFSource::header_position_negative;
|
||||
uint64_t SMFSource::header_position_offset;
|
||||
*/
|
||||
|
||||
SMFSource::SMFSource (std::string path, Flag flags)
|
||||
: MidiSource (path), _flags (flags)
|
||||
{
|
||||
/* constructor used for new internal-to-session files. file cannot exist */
|
||||
|
||||
if (init (path, false)) {
|
||||
throw failed_constructor ();
|
||||
}
|
||||
}
|
||||
|
||||
SMFSource::SMFSource (const XMLNode& node)
|
||||
: MidiSource (node), _flags (Flag (Writable|CanRename))
|
||||
{
|
||||
/* constructor used for existing internal-to-session files. file must exist */
|
||||
|
||||
if (set_state (node)) {
|
||||
throw failed_constructor ();
|
||||
}
|
||||
|
||||
if (init (_name, true)) {
|
||||
throw failed_constructor ();
|
||||
}
|
||||
}
|
||||
|
||||
SMFSource::~SMFSource ()
|
||||
{
|
||||
if (removable()) {
|
||||
unlink (_path.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
SMFSource::removable () const
|
||||
{
|
||||
return (_flags & Removable) && ((_flags & RemoveAtDestroy) ||
|
||||
((_flags & RemovableIfEmpty) && is_empty (_path)));
|
||||
}
|
||||
|
||||
int
|
||||
SMFSource::init (string pathstr, bool must_exist)
|
||||
{
|
||||
bool is_new = false;
|
||||
|
||||
_length = 0;
|
||||
|
||||
if (!find (pathstr, must_exist, is_new)) {
|
||||
cerr << "cannot find " << pathstr << " with me = " << must_exist << endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (is_new && must_exist) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
XMLNode&
|
||||
SMFSource::get_state ()
|
||||
{
|
||||
XMLNode& root (MidiSource::get_state());
|
||||
char buf[16];
|
||||
snprintf (buf, sizeof (buf), "0x%x", (int)_flags);
|
||||
root.add_property ("flags", buf);
|
||||
return root;
|
||||
}
|
||||
|
||||
int
|
||||
SMFSource::set_state (const XMLNode& node)
|
||||
{
|
||||
const XMLProperty* prop;
|
||||
|
||||
if (MidiSource::set_state (node)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((prop = node.property (X_("flags"))) != 0) {
|
||||
|
||||
int ival;
|
||||
sscanf (prop->value().c_str(), "0x%x", &ival);
|
||||
_flags = Flag (ival);
|
||||
|
||||
} else {
|
||||
|
||||
_flags = Flag (0);
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
SMFSource::mark_for_remove ()
|
||||
{
|
||||
if (!writable()) {
|
||||
return;
|
||||
}
|
||||
_flags = Flag (_flags | RemoveAtDestroy);
|
||||
}
|
||||
|
||||
void
|
||||
SMFSource::mark_streaming_write_completed ()
|
||||
{
|
||||
if (!writable()) {
|
||||
return;
|
||||
}
|
||||
#if 0
|
||||
Glib::Mutex::Lock lm (_lock);
|
||||
|
||||
|
||||
next_peak_clear_should_notify = true;
|
||||
|
||||
if (_peaks_built || pending_peak_builds.empty()) {
|
||||
_peaks_built = true;
|
||||
PeaksReady (); /* EMIT SIGNAL */
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
SMFSource::mark_take (string id)
|
||||
{
|
||||
if (writable()) {
|
||||
_take_id = id;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
SMFSource::move_to_trash (const string trash_dir_name)
|
||||
{
|
||||
string newpath;
|
||||
|
||||
if (!writable()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* don't move the file across filesystems, just
|
||||
stick it in the `trash_dir_name' directory
|
||||
on whichever filesystem it was already on.
|
||||
*/
|
||||
|
||||
newpath = Glib::path_get_dirname (_path);
|
||||
newpath = Glib::path_get_dirname (newpath);
|
||||
|
||||
newpath += '/';
|
||||
newpath += trash_dir_name;
|
||||
newpath += '/';
|
||||
newpath += Glib::path_get_basename (_path);
|
||||
|
||||
if (access (newpath.c_str(), F_OK) == 0) {
|
||||
|
||||
/* the new path already exists, try versioning */
|
||||
|
||||
char buf[PATH_MAX+1];
|
||||
int version = 1;
|
||||
string newpath_v;
|
||||
|
||||
snprintf (buf, sizeof (buf), "%s.%d", newpath.c_str(), version);
|
||||
newpath_v = buf;
|
||||
|
||||
while (access (newpath_v.c_str(), F_OK) == 0 && version < 999) {
|
||||
snprintf (buf, sizeof (buf), "%s.%d", newpath.c_str(), ++version);
|
||||
newpath_v = buf;
|
||||
}
|
||||
|
||||
if (version == 999) {
|
||||
PBD::error << string_compose (_("there are already 1000 files with names like %1; versioning discontinued"),
|
||||
newpath)
|
||||
<< endmsg;
|
||||
} else {
|
||||
newpath = newpath_v;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
/* it doesn't exist, or we can't read it or something */
|
||||
|
||||
}
|
||||
|
||||
if (::rename (_path.c_str(), newpath.c_str()) != 0) {
|
||||
PBD::error << string_compose (_("cannot rename audio file source from %1 to %2 (%3)"),
|
||||
_path, newpath, strerror (errno))
|
||||
<< endmsg;
|
||||
return -1;
|
||||
}
|
||||
#if 0
|
||||
if (::unlink (peakpath.c_str()) != 0) {
|
||||
PBD::error << string_compose (_("cannot remove peakfile %1 for %2 (%3)"),
|
||||
peakpath, _path, strerror (errno))
|
||||
<< endmsg;
|
||||
/* try to back out */
|
||||
rename (newpath.c_str(), _path.c_str());
|
||||
return -1;
|
||||
}
|
||||
|
||||
_path = newpath;
|
||||
peakpath = "";
|
||||
#endif
|
||||
/* file can not be removed twice, since the operation is not idempotent */
|
||||
|
||||
_flags = Flag (_flags & ~(RemoveAtDestroy|Removable|RemovableIfEmpty));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// FIXME: Merge this with audiofilesource somehow (make a generic filesource?)
|
||||
bool
|
||||
SMFSource::find (string pathstr, bool must_exist, bool& isnew)
|
||||
{
|
||||
string::size_type pos;
|
||||
bool ret = false;
|
||||
|
||||
isnew = false;
|
||||
|
||||
/* clean up PATH:CHANNEL notation so that we are looking for the correct path */
|
||||
|
||||
if ((pos = pathstr.find_last_of (':')) == string::npos) {
|
||||
pathstr = pathstr;
|
||||
} else {
|
||||
pathstr = pathstr.substr (0, pos);
|
||||
}
|
||||
|
||||
if (pathstr[0] != '/') {
|
||||
|
||||
/* non-absolute pathname: find pathstr in search path */
|
||||
|
||||
vector<string> dirs;
|
||||
int cnt;
|
||||
string fullpath;
|
||||
string keeppath;
|
||||
|
||||
if (_search_path.length() == 0) {
|
||||
PBD::error << _("FileSource: search path not set") << endmsg;
|
||||
goto out;
|
||||
}
|
||||
|
||||
split (_search_path, dirs, ':');
|
||||
|
||||
cnt = 0;
|
||||
|
||||
for (vector<string>::iterator i = dirs.begin(); i != dirs.end(); ++i) {
|
||||
|
||||
fullpath = *i;
|
||||
if (fullpath[fullpath.length()-1] != '/') {
|
||||
fullpath += '/';
|
||||
}
|
||||
fullpath += pathstr;
|
||||
|
||||
if (access (fullpath.c_str(), R_OK) == 0) {
|
||||
keeppath = fullpath;
|
||||
++cnt;
|
||||
}
|
||||
}
|
||||
|
||||
if (cnt > 1) {
|
||||
|
||||
PBD::error << string_compose (_("FileSource: \"%1\" is ambigous when searching %2\n\t"), pathstr, _search_path) << endmsg;
|
||||
goto out;
|
||||
|
||||
} else if (cnt == 0) {
|
||||
|
||||
if (must_exist) {
|
||||
PBD::error << string_compose(_("Filesource: cannot find required file (%1): while searching %2"), pathstr, _search_path) << endmsg;
|
||||
goto out;
|
||||
} else {
|
||||
isnew = true;
|
||||
}
|
||||
}
|
||||
|
||||
_name = pathstr;
|
||||
_path = keeppath;
|
||||
ret = true;
|
||||
|
||||
} else {
|
||||
|
||||
/* external files and/or very very old style sessions include full paths */
|
||||
|
||||
_path = pathstr;
|
||||
_name = pathstr.substr (pathstr.find_last_of ('/') + 1);
|
||||
|
||||
if (access (_path.c_str(), R_OK) != 0) {
|
||||
|
||||
/* file does not exist or we cannot read it */
|
||||
|
||||
if (must_exist) {
|
||||
PBD::error << string_compose(_("Filesource: cannot find required file (%1): %2"), _path, strerror (errno)) << endmsg;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (errno != ENOENT) {
|
||||
PBD::error << string_compose(_("Filesource: cannot check for existing file (%1): %2"), _path, strerror (errno)) << endmsg;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* a new file */
|
||||
|
||||
isnew = true;
|
||||
ret = true;
|
||||
|
||||
} else {
|
||||
|
||||
/* already exists */
|
||||
|
||||
ret = true;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
SMFSource::set_search_path (string p)
|
||||
{
|
||||
_search_path = p;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SMFSource::set_allow_remove_if_empty (bool yn)
|
||||
{
|
||||
if (writable()) {
|
||||
_allow_remove_if_empty = yn;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
SMFSource::set_name (string newname, bool destructive)
|
||||
{
|
||||
//Glib::Mutex::Lock lm (_lock); FIXME
|
||||
string oldpath = _path;
|
||||
string newpath = Session::change_audio_path_by_name (oldpath, _name, newname, destructive);
|
||||
|
||||
if (newpath.empty()) {
|
||||
PBD::error << string_compose (_("programming error: %1"), "cannot generate a changed audio path") << endmsg;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (rename (oldpath.c_str(), newpath.c_str()) != 0) {
|
||||
PBD::error << string_compose (_("cannot rename audio file for %1 to %2"), _name, newpath) << endmsg;
|
||||
return -1;
|
||||
}
|
||||
|
||||
_name = Glib::path_get_basename (newpath);
|
||||
_path = newpath;
|
||||
|
||||
return 0;//rename_peakfile (peak_path (_path));
|
||||
}
|
||||
|
||||
bool
|
||||
SMFSource::is_empty (string path)
|
||||
{
|
||||
/* XXX fix me */
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue