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:
David Robillard 2006-06-26 20:29:45 +00:00
parent 2336aa1a50
commit b7f3a63507
15 changed files with 5281 additions and 0 deletions

143
libs/ardour/ardour/buffer.h Normal file
View 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__

View 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__ */

View 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__ */

View 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__ */

View 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__ */

View 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__ */

View 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__ */

View 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
View 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;
}
}
}

View 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;
}

View 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*> (&region);
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
View 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
View 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

File diff suppressed because it is too large Load diff

406
libs/ardour/smf_source.cc Normal file
View 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;
}