summaryrefslogtreecommitdiff
path: root/libs
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2006-06-26 20:29:45 +0000
committerDavid Robillard <d@drobilla.net>2006-06-26 20:29:45 +0000
commitb7f3a6350783ffda019c22f74dd67e7d619b387b (patch)
treee9af93f232feb417a07815c2516d18bb562f0b58 /libs
parent2336aa1a5014964050165e1e6c81b05fd1670126 (diff)
Actually added the code mentioned in my last commit. Whoops.
git-svn-id: svn://localhost/ardour2/branches/midi@643 d708f5d6-7413-0410-9779-e7cbd77b26cf
Diffstat (limited to 'libs')
-rw-r--r--libs/ardour/ardour/buffer.h143
-rw-r--r--libs/ardour/ardour/diskstream.h341
-rw-r--r--libs/ardour/ardour/midi_diskstream.h194
-rw-r--r--libs/ardour/ardour/midi_playlist.h109
-rw-r--r--libs/ardour/ardour/midi_region.h152
-rw-r--r--libs/ardour/ardour/midi_source.h93
-rw-r--r--libs/ardour/ardour/midi_track.h180
-rw-r--r--libs/ardour/ardour/smf_source.h96
-rw-r--r--libs/ardour/diskstream.cc362
-rw-r--r--libs/ardour/midi_diskstream.cc624
-rw-r--r--libs/ardour/midi_playlist.cc634
-rw-r--r--libs/ardour/midi_region.cc647
-rw-r--r--libs/ardour/midi_source.cc133
-rw-r--r--libs/ardour/midi_track.cc1167
-rw-r--r--libs/ardour/smf_source.cc406
15 files changed, 5281 insertions, 0 deletions
diff --git a/libs/ardour/ardour/buffer.h b/libs/ardour/ardour/buffer.h
new file mode 100644
index 0000000000..5171f50697
--- /dev/null
+++ b/libs/ardour/ardour/buffer.h
@@ -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__
diff --git a/libs/ardour/ardour/diskstream.h b/libs/ardour/ardour/diskstream.h
new file mode 100644
index 0000000000..f43e8273a7
--- /dev/null
+++ b/libs/ardour/ardour/diskstream.h
@@ -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__ */
diff --git a/libs/ardour/ardour/midi_diskstream.h b/libs/ardour/ardour/midi_diskstream.h
new file mode 100644
index 0000000000..b6121d1176
--- /dev/null
+++ b/libs/ardour/ardour/midi_diskstream.h
@@ -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__ */
diff --git a/libs/ardour/ardour/midi_playlist.h b/libs/ardour/ardour/midi_playlist.h
new file mode 100644
index 0000000000..da3a72a3fd
--- /dev/null
+++ b/libs/ardour/ardour/midi_playlist.h
@@ -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__ */
+
+
diff --git a/libs/ardour/ardour/midi_region.h b/libs/ardour/ardour/midi_region.h
new file mode 100644
index 0000000000..e4d8f48c6e
--- /dev/null
+++ b/libs/ardour/ardour/midi_region.h
@@ -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__ */
diff --git a/libs/ardour/ardour/midi_source.h b/libs/ardour/ardour/midi_source.h
new file mode 100644
index 0000000000..735ebba447
--- /dev/null
+++ b/libs/ardour/ardour/midi_source.h
@@ -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__ */
diff --git a/libs/ardour/ardour/midi_track.h b/libs/ardour/ardour/midi_track.h
new file mode 100644
index 0000000000..1ef9bedf8d
--- /dev/null
+++ b/libs/ardour/ardour/midi_track.h
@@ -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__ */
diff --git a/libs/ardour/ardour/smf_source.h b/libs/ardour/ardour/smf_source.h
new file mode 100644
index 0000000000..00f8cb26de
--- /dev/null
+++ b/libs/ardour/ardour/smf_source.h
@@ -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__ */
+
diff --git a/libs/ardour/diskstream.cc b/libs/ardour/diskstream.cc
new file mode 100644
index 0000000000..050f23497b
--- /dev/null
+++ b/libs/ardour/diskstream.cc
@@ -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;
+ }
+ }
+}
diff --git a/libs/ardour/midi_diskstream.cc b/libs/ardour/midi_diskstream.cc
new file mode 100644
index 0000000000..0af4af4f7c
--- /dev/null
+++ b/libs/ardour/midi_diskstream.cc
@@ -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;
+}
diff --git a/libs/ardour/midi_playlist.cc b/libs/ardour/midi_playlist.cc
new file mode 100644
index 0000000000..007856e3a7
--- /dev/null
+++ b/libs/ardour/midi_playlist.cc
@@ -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;
+}
+
diff --git a/libs/ardour/midi_region.cc b/libs/ardour/midi_region.cc
new file mode 100644
index 0000000000..e9d75e3d06
--- /dev/null
+++ b/libs/ardour/midi_region.cc
@@ -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;
+}
+
diff --git a/libs/ardour/midi_source.cc b/libs/ardour/midi_source.cc
new file mode 100644
index 0000000000..f9fc8dd8ec
--- /dev/null
+++ b/libs/ardour/midi_source.cc
@@ -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;
+ }
+}
+
diff --git a/libs/ardour/midi_track.cc b/libs/ardour/midi_track.cc
new file mode 100644
index 0000000000..d4f7be1e6f
--- /dev/null
+++ b/libs/ardour/midi_track.cc
@@ -0,0 +1,1167 @@
+/*
+ Copyright (C) 2006 Paul Davis
+ 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 <pbd/error.h>
+#include <sigc++/retype.h>
+#include <sigc++/retype_return.h>
+#include <sigc++/bind.h>
+
+#include <ardour/midi_track.h>
+#include <ardour/midi_diskstream.h>
+#include <ardour/session.h>
+#include <ardour/redirect.h>
+#include <ardour/midi_region.h>
+#include <ardour/midi_source.h>
+#include <ardour/route_group_specialized.h>
+#include <ardour/insert.h>
+#include <ardour/midi_playlist.h>
+#include <ardour/panner.h>
+#include <ardour/utils.h>
+
+#include "i18n.h"
+
+using namespace std;
+using namespace ARDOUR;
+
+MidiTrack::MidiTrack (Session& sess, string name, Route::Flag flag, TrackMode mode)
+ : Route (sess, name, 1, -1, -1, -1, flag, Buffer::MIDI),
+ diskstream (0),
+ _midi_rec_enable_control (*this, _session.midi_port())
+{
+ MidiDiskstream::Flag dflags = MidiDiskstream::Flag (0);
+
+ if (_flags & Hidden) {
+ dflags = MidiDiskstream::Flag (dflags | MidiDiskstream::Hidden);
+ } else {
+ dflags = MidiDiskstream::Flag (dflags | MidiDiskstream::Recordable);
+ }
+
+ if (mode == Destructive) {
+ dflags = MidiDiskstream::Flag (dflags | MidiDiskstream::Destructive);
+ }
+
+ MidiDiskstream* ds = new MidiDiskstream (_session, name, dflags);
+
+ _declickable = true;
+ _freeze_record.state = NoFreeze;
+ _saved_meter_point = _meter_point;
+ _mode = mode;
+
+ set_diskstream (*ds, this);
+
+ // session.SMPTEOffsetChanged.connect (mem_fun (*this, &MidiTrack::handle_smpte_offset_change));
+
+ // we do this even though Route already did it in it's init
+ reset_midi_control (_session.midi_port(), _session.get_midi_control());
+
+}
+
+MidiTrack::MidiTrack (Session& sess, const XMLNode& node)
+ : Route (sess, "to be renamed", 0, 0, -1, -1),
+ diskstream (0),
+ _midi_rec_enable_control (*this, _session.midi_port())
+{
+ _freeze_record.state = NoFreeze;
+ set_state (node);
+ _declickable = true;
+ _saved_meter_point = _meter_point;
+
+ // we do this even though Route already did it in it's init
+ reset_midi_control (_session.midi_port(), _session.get_midi_control());
+}
+
+MidiTrack::~MidiTrack ()
+{
+ if (diskstream) {
+ diskstream->unref();
+ }
+}
+
+#if 0
+void
+MidiTrack::handle_smpte_offset_change ()
+{
+ diskstream
+}
+#endif
+
+int
+MidiTrack::deprecated_use_diskstream_connections ()
+{
+ if (diskstream->deprecated_io_node == 0) {
+ return 0;
+ }
+
+ const XMLProperty* prop;
+ XMLNode& node (*diskstream->deprecated_io_node);
+
+ /* don't do this more than once. */
+
+ diskstream->deprecated_io_node = 0;
+
+ set_input_minimum (-1);
+ set_input_maximum (-1);
+ set_output_minimum (-1);
+ set_output_maximum (-1);
+
+ if ((prop = node.property ("gain")) != 0) {
+ set_gain (atof (prop->value().c_str()), this);
+ _gain = _desired_gain;
+ }
+
+ if ((prop = node.property ("input-connection")) != 0) {
+ Connection* c = _session.connection_by_name (prop->value());
+
+ if (c == 0) {
+ PBD::error << string_compose(_("Unknown connection \"%1\" listed for input of %2"), prop->value(), _name) << endmsg;
+
+ if ((c = _session.connection_by_name (_("in 1"))) == 0) {
+ PBD::error << _("No input connections available as a replacement")
+ << endmsg;
+ return -1;
+ } else {
+ PBD::info << string_compose (_("Connection %1 was not available - \"in 1\" used instead"), prop->value())
+ << endmsg;
+ }
+ }
+
+ use_input_connection (*c, this);
+
+ } else if ((prop = node.property ("inputs")) != 0) {
+ if (set_inputs (prop->value())) {
+ PBD::error << string_compose(_("improper input channel list in XML node (%1)"), prop->value()) << endmsg;
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int
+MidiTrack::set_diskstream (MidiDiskstream& ds, void *src)
+{
+ if (diskstream) {
+ diskstream->unref();
+ }
+
+ diskstream = &ds.ref();
+ diskstream->set_io (*this);
+ diskstream->set_destructive (_mode == Destructive);
+
+ if (diskstream->deprecated_io_node) {
+
+ if (!connecting_legal) {
+ ConnectingLegal.connect (mem_fun (*this, &MidiTrack::deprecated_use_diskstream_connections));
+ } else {
+ deprecated_use_diskstream_connections ();
+ }
+ }
+
+ diskstream->set_record_enabled (false, this);
+ //diskstream->monitor_input (false);
+
+ ic_connection.disconnect();
+ ic_connection = input_changed.connect (mem_fun (*diskstream, &MidiDiskstream::handle_input_change));
+
+ diskstream_changed (src); /* EMIT SIGNAL */
+
+ return 0;
+}
+
+int
+MidiTrack::use_diskstream (string name)
+{
+ /*
+ MidiDiskstream *dstream;
+
+ if ((dstream = _session.diskstream_by_name (name)) == 0) {
+ PBD::error << string_compose(_("MidiTrack: diskstream \"%1\" not known by session"), name) << endmsg;
+ return -1;
+ }
+
+ return set_diskstream (*dstream, this);
+ */
+ return 0;
+}
+
+int
+MidiTrack::use_diskstream (id_t id)
+{
+ /*
+ MidiDiskstream *dstream;
+
+ if ((dstream = _session.diskstream_by_id (id)) == 0) {
+ PBD::error << string_compose(_("MidiTrack: diskstream \"%1\" not known by session"), id) << endmsg;
+ return -1;
+ }
+
+ return set_diskstream (*dstream, this);
+ */
+ return 0;
+}
+
+bool
+MidiTrack::record_enabled () const
+{
+ return diskstream->record_enabled ();
+}
+
+void
+MidiTrack::set_record_enable (bool yn, void *src)
+{
+ if (_freeze_record.state == Frozen) {
+ return;
+ }
+#if 0
+ if (_mix_group && src != _mix_group && _mix_group->is_active()) {
+ _mix_group->apply (&MidiTrack::set_record_enable, yn, _mix_group);
+ return;
+ }
+
+ /* keep track of the meter point as it was before we rec-enabled */
+
+ if (!diskstream->record_enabled()) {
+ _saved_meter_point = _meter_point;
+ }
+
+ diskstream->set_record_enabled (yn, src);
+
+ if (diskstream->record_enabled()) {
+ set_meter_point (MeterInput, this);
+ } else {
+ set_meter_point (_saved_meter_point, this);
+ }
+
+ if (_session.get_midi_feedback()) {
+ _midi_rec_enable_control.send_feedback (record_enabled());
+ }
+#endif
+}
+
+void
+MidiTrack::set_meter_point (MeterPoint p, void *src)
+{
+ Route::set_meter_point (p, src);
+}
+
+int
+MidiTrack::set_state (const XMLNode& node)
+{
+ const XMLProperty *prop;
+ XMLNodeConstIterator iter;
+ XMLNodeList midi_kids;
+
+ if (Route::set_state (node)) {
+ return -1;
+ }
+
+ if ((prop = node.property (X_("mode"))) != 0) {
+ if (prop->value() == X_("normal")) {
+ _mode = Normal;
+ } else if (prop->value() == X_("destructive")) {
+ _mode = Destructive;
+ } else {
+ PBD::warning << string_compose ("unknown midi track mode \"%1\" seen and ignored", prop->value()) << endmsg;
+ _mode = Normal;
+ }
+ } else {
+ _mode = Normal;
+ }
+
+ midi_kids = node.children ("MIDI");
+
+ for (iter = midi_kids.begin(); iter != midi_kids.end(); ++iter) {
+
+ XMLNodeList kids;
+ XMLNodeConstIterator miter;
+ XMLNode* child;
+
+ kids = (*iter)->children ();
+
+ for (miter = kids.begin(); miter != kids.end(); ++miter) {
+
+ child =* miter;
+
+ if (child->name() == "rec_enable") {
+
+ MIDI::eventType ev = MIDI::on; /* initialize to keep gcc happy */
+ MIDI::byte additional = 0; /* ditto */
+ MIDI::channel_t chn = 0; /* ditto */
+
+ if (get_midi_node_info (child, ev, chn, additional)) {
+ _midi_rec_enable_control.set_control_type (chn, ev, additional);
+ } else {
+ PBD::error << string_compose(_("MIDI rec_enable control specification for %1 is incomplete, so it has been ignored"), _name) << endmsg;
+ }
+ }
+ }
+ }
+
+
+ if ((prop = node.property ("diskstream-id")) == 0) {
+
+ /* some old sessions use the diskstream name rather than the ID */
+
+ if ((prop = node.property ("diskstream")) == 0) {
+ PBD::fatal << _("programming error: MidiTrack given state without diskstream!") << endmsg;
+ /*NOTREACHED*/
+ return -1;
+ }
+
+ if (use_diskstream (prop->value())) {
+ return -1;
+ }
+
+ } else {
+
+ id_t id = strtoull (prop->value().c_str(), 0, 10);
+
+ if (use_diskstream (id)) {
+ return -1;
+ }
+ }
+
+
+ XMLNodeList nlist;
+ XMLNodeConstIterator niter;
+ XMLNode *child;
+
+ nlist = node.children();
+ for (niter = nlist.begin(); niter != nlist.end(); ++niter){
+ child = *niter;
+
+ if (child->name() == X_("remote_control")) {
+ if ((prop = child->property (X_("id"))) != 0) {
+ int32_t x;
+ sscanf (prop->value().c_str(), "%d", &x);
+ set_remote_control_id (x);
+ }
+ }
+ }
+
+ pending_state = const_cast<XMLNode*> (&node);
+
+ _session.StateReady.connect (mem_fun (*this, &MidiTrack::set_state_part_two));
+
+ return 0;
+}
+
+XMLNode&
+MidiTrack::get_template ()
+{
+ return state (false);
+}
+
+XMLNode&
+MidiTrack::get_state ()
+{
+ return state (true);
+}
+
+XMLNode&
+MidiTrack::state(bool full_state)
+{
+ XMLNode& root (Route::state(full_state));
+ XMLNode* freeze_node;
+ char buf[32];
+
+ if (_freeze_record.playlist) {
+ XMLNode* inode;
+
+ freeze_node = new XMLNode (X_("freeze-info"));
+ freeze_node->add_property ("playlist", _freeze_record.playlist->name());
+ snprintf (buf, sizeof (buf), "%d", (int) _freeze_record.state);
+ freeze_node->add_property ("state", buf);
+
+ for (vector<FreezeRecordInsertInfo*>::iterator i = _freeze_record.insert_info.begin(); i != _freeze_record.insert_info.end(); ++i) {
+ inode = new XMLNode (X_("insert"));
+ snprintf (buf, sizeof (buf), "%" PRIu64, (*i)->id);
+ inode->add_property (X_("id"), buf);
+ inode->add_child_copy ((*i)->state);
+
+ freeze_node->add_child_nocopy (*inode);
+ }
+
+ root.add_child_nocopy (*freeze_node);
+ }
+
+ /* Alignment: act as a proxy for the diskstream */
+
+ XMLNode* align_node = new XMLNode (X_("alignment"));
+ switch (diskstream->alignment_style()) {
+ case ExistingMaterial:
+ snprintf (buf, sizeof (buf), X_("existing"));
+ break;
+ case CaptureTime:
+ snprintf (buf, sizeof (buf), X_("capture"));
+ break;
+ }
+ align_node->add_property (X_("style"), buf);
+ root.add_child_nocopy (*align_node);
+
+ /* MIDI control */
+
+ MIDI::channel_t chn;
+ MIDI::eventType ev;
+ MIDI::byte additional;
+ XMLNode* midi_node = 0;
+ XMLNode* child;
+ XMLNodeList midikids;
+
+ midikids = root.children ("MIDI");
+ if (!midikids.empty()) {
+ midi_node = midikids.front();
+ }
+ else {
+ midi_node = root.add_child ("MIDI");
+ }
+
+ if (_midi_rec_enable_control.get_control_info (chn, ev, additional) && midi_node) {
+
+ child = midi_node->add_child ("rec_enable");
+ set_midi_node_info (child, ev, chn, additional);
+ }
+
+ XMLNode* remote_control_node = new XMLNode (X_("remote_control"));
+ snprintf (buf, sizeof (buf), "%d", _remote_control_id);
+ remote_control_node->add_property (X_("id"), buf);
+ root.add_child_nocopy (*remote_control_node);
+
+ switch (_mode) {
+ case Normal:
+ root.add_property (X_("mode"), X_("normal"));
+ break;
+ case Destructive:
+ root.add_property (X_("mode"), X_("destructive"));
+ break;
+ }
+
+ /* we don't return diskstream state because we don't
+ own the diskstream exclusively. control of the diskstream
+ state is ceded to the Session, even if we create the
+ diskstream.
+ */
+
+ snprintf (buf, sizeof (buf), "%" PRIu64, diskstream->id());
+ root.add_property ("diskstream-id", buf);
+
+ return root;
+}
+
+void
+MidiTrack::set_state_part_two ()
+{
+ XMLNode* fnode;
+ XMLProperty* prop;
+ LocaleGuard lg (X_("POSIX"));
+
+ /* This is called after all session state has been restored but before
+ have been made ports and connections are established.
+ */
+
+ if (pending_state == 0) {
+ return;
+ }
+
+ if ((fnode = find_named_node (*pending_state, X_("freeze-info"))) != 0) {
+
+
+ _freeze_record.have_mementos = false;
+ _freeze_record.state = Frozen;
+
+ for (vector<FreezeRecordInsertInfo*>::iterator i = _freeze_record.insert_info.begin(); i != _freeze_record.insert_info.end(); ++i) {
+ delete *i;
+ }
+ _freeze_record.insert_info.clear ();
+
+ if ((prop = fnode->property (X_("playlist"))) != 0) {
+ Playlist* pl = _session.playlist_by_name (prop->value());
+ if (pl) {
+ _freeze_record.playlist = dynamic_cast<MidiPlaylist*> (pl);
+ } else {
+ _freeze_record.playlist = 0;
+ _freeze_record.state = NoFreeze;
+ return;
+ }
+ }
+
+ if ((prop = fnode->property (X_("state"))) != 0) {
+ _freeze_record.state = (FreezeState) atoi (prop->value().c_str());
+ }
+
+ XMLNodeConstIterator citer;
+ XMLNodeList clist = fnode->children();
+
+ for (citer = clist.begin(); citer != clist.end(); ++citer) {
+ if ((*citer)->name() != X_("insert")) {
+ continue;
+ }
+
+ if ((prop = (*citer)->property (X_("id"))) == 0) {
+ continue;
+ }
+
+ FreezeRecordInsertInfo* frii = new FreezeRecordInsertInfo (*((*citer)->children().front()));
+ frii->insert = 0;
+ sscanf (prop->value().c_str(), "%" PRIu64, &frii->id);
+ _freeze_record.insert_info.push_back (frii);
+ }
+ }
+
+ /* Alignment: act as a proxy for the diskstream */
+
+ if ((fnode = find_named_node (*pending_state, X_("alignment"))) != 0) {
+
+ if ((prop = fnode->property (X_("style"))) != 0) {
+ if (prop->value() == "existing") {
+ diskstream->set_persistent_align_style (ExistingMaterial);
+ } else if (prop->value() == "capture") {
+ diskstream->set_persistent_align_style (CaptureTime);
+ }
+ }
+ }
+ return;
+}
+
+uint32_t
+MidiTrack::n_process_buffers ()
+{
+ return max ((uint32_t) diskstream->n_channels(), redirect_max_outs);
+}
+
+void
+MidiTrack::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 nbufs = n_process_buffers ();
+ process_output_buffers (_session.get_silent_buffers (nbufs), nbufs, start_frame, end_frame, nframes, offset, true, declick, meter);
+}
+
+int
+MidiTrack::no_roll (jack_nframes_t nframes, jack_nframes_t start_frame, jack_nframes_t end_frame, jack_nframes_t offset,
+ bool session_state_changing, bool can_record, bool rec_monitors_input)
+{
+ if (n_outputs() == 0) {
+ return 0;
+ }
+
+ if (!_active) {
+ silence (nframes, offset);
+ return 0;
+ }
+
+ if (session_state_changing) {
+
+ /* XXX is this safe to do against transport state changes? */
+
+ passthru_silence (start_frame, end_frame, nframes, offset, 0, false);
+ return 0;
+ }
+
+ diskstream->check_record_status (start_frame, nframes, can_record);
+
+ bool send_silence;
+
+ if (_have_internal_generator) {
+ /* since the instrument has no input streams,
+ there is no reason to send any signal
+ into the route.
+ */
+ send_silence = true;
+ } else {
+
+ if (_session.get_auto_input()) {
+ if (Config->get_use_sw_monitoring()) {
+ send_silence = false;
+ } else {
+ send_silence = true;
+ }
+ } else {
+ if (diskstream->record_enabled()) {
+ if (Config->get_use_sw_monitoring()) {
+ send_silence = false;
+ } else {
+ send_silence = true;
+ }
+ } else {
+ send_silence = true;
+ }
+ }
+ }
+
+ apply_gain_automation = false;
+
+ if (send_silence) {
+
+ /* if we're sending silence, but we want the meters to show levels for the signal,
+ meter right here.
+ */
+
+ if (_have_internal_generator) {
+ passthru_silence (start_frame, end_frame, nframes, offset, 0, true);
+ } else {
+ if (_meter_point == MeterInput) {
+ just_meter_input (start_frame, end_frame, nframes, offset);
+ }
+ passthru_silence (start_frame, end_frame, nframes, offset, 0, false);
+ }
+
+ } else {
+
+ /* we're sending signal, but we may still want to meter the input.
+ */
+
+ passthru (start_frame, end_frame, nframes, offset, 0, (_meter_point == MeterInput));
+ }
+
+ return 0;
+}
+
+int
+MidiTrack::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)
+{
+#if 0
+ int dret;
+ Sample* b;
+ Sample* tmpb;
+ jack_nframes_t transport_frame;
+
+ {
+ Glib::RWLock::ReaderLock lm (redirect_lock, Glib::TRY_LOCK);
+ if (lm.locked()) {
+ // automation snapshot can also be called from the non-rt context
+ // and it uses the redirect list, so we take the lock out here
+ automation_snapshot (start_frame);
+ }
+ }
+
+ if (n_outputs() == 0 && _redirects.empty()) {
+ return 0;
+ }
+
+ if (!_active) {
+ silence (nframes, offset);
+ return 0;
+ }
+
+ transport_frame = _session.transport_frame();
+
+ if ((nframes = check_initial_delay (nframes, offset, transport_frame)) == 0) {
+ /* need to do this so that the diskstream sets its
+ playback distance to zero, thus causing diskstream::commit
+ to do nothing.
+ */
+ return diskstream->process (transport_frame, 0, 0, can_record, rec_monitors_input);
+ }
+
+ _silent = false;
+ apply_gain_automation = false;
+
+ if ((dret = diskstream->process (transport_frame, nframes, offset, can_record, rec_monitors_input)) != 0) {
+
+ silence (nframes, offset);
+
+ return dret;
+ }
+
+ /* special condition applies */
+
+ if (_meter_point == MeterInput) {
+ just_meter_input (start_frame, end_frame, nframes, offset);
+ }
+
+ if (diskstream->record_enabled() && !can_record && !_session.get_auto_input()) {
+
+ /* not actually recording, but we want to hear the input material anyway,
+ at least potentially (depending on monitoring options)
+ */
+
+ passthru (start_frame, end_frame, nframes, offset, 0, true);
+
+ } else if ((b = diskstream->playback_buffer(0)) != 0) {
+
+ /*
+ XXX is it true that the earlier test on n_outputs()
+ means that we can avoid checking it again here? i think
+ so, because changing the i/o configuration of an IO
+ requires holding the AudioEngine lock, which we hold
+ while in the process() tree.
+ */
+
+
+ /* copy the diskstream data to all output buffers */
+
+ vector<Sample*>& bufs = _session.get_passthru_buffers ();
+ uint32_t limit = n_process_buffers ();
+
+ uint32_t n;
+ uint32_t i;
+
+
+ for (i = 0, n = 1; i < limit; ++i, ++n) {
+ memcpy (bufs[i], b, sizeof (Sample) * nframes);
+ if (n < diskstream->n_channels()) {
+ tmpb = diskstream->playback_buffer(n);
+ if (tmpb!=0) {
+ b = tmpb;
+ }
+ }
+ }
+
+ /* don't waste time with automation if we're recording or we've just stopped (yes it can happen) */
+
+ if (!diskstream->record_enabled() && _session.transport_rolling()) {
+ Glib::Mutex::Lock am (automation_lock, Glib::TRY_LOCK);
+
+ if (am.locked() && gain_automation_playback()) {
+ apply_gain_automation = _gain_automation_curve.rt_safe_get_vector (start_frame, end_frame, _session.gain_automation_buffer(), nframes);
+ }
+ }
+
+ process_output_buffers (bufs, limit, start_frame, end_frame, nframes, offset, (!_session.get_record_enabled() || !_session.get_do_not_record_plugins()), declick, (_meter_point != MeterInput));
+
+ } else {
+ /* problem with the diskstream; just be quiet for a bit */
+ silence (nframes, offset);
+ }
+#endif
+ return 0;
+}
+
+int
+MidiTrack::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)
+{
+ if (n_outputs() == 0 && _redirects.empty()) {
+ return 0;
+ }
+
+ if (!_active) {
+ silence (nframes, offset);
+ return 0;
+ }
+
+ _silent = true;
+ apply_gain_automation = false;
+
+ silence (nframes, offset);
+
+ return diskstream->process (_session.transport_frame() + offset, nframes, offset, can_record, rec_monitors_input);
+}
+
+void
+MidiTrack::toggle_monitor_input ()
+{
+ for (vector<Port*>::iterator i = _inputs.begin(); i != _inputs.end(); ++i) {
+ (*i)->request_monitor_input(!(*i)->monitoring_input());
+ }
+}
+
+int
+MidiTrack::set_name (string str, void *src)
+{
+ int ret;
+
+ if (record_enabled() && _session.actively_recording()) {
+ /* this messes things up if done while recording */
+ return -1;
+ }
+
+ if (diskstream->set_name (str, src)) {
+ return -1;
+ }
+
+ /* save state so that the statefile fully reflects any filename changes */
+
+ if ((ret = IO::set_name (str, src)) == 0) {
+ _session.save_state ("");
+ }
+ return ret;
+}
+
+int
+MidiTrack::export_stuff (vector<unsigned char*>& buffers, char * workbuf, uint32_t nbufs, jack_nframes_t start, jack_nframes_t nframes)
+{
+#if 0
+ gain_t gain_automation[nframes];
+ gain_t gain_buffer[nframes];
+ float mix_buffer[nframes];
+ RedirectList::iterator i;
+ bool post_fader_work = false;
+ gain_t this_gain = _gain;
+ vector<Sample*>::iterator bi;
+ Sample * b;
+
+ Glib::RWLock::ReaderLock rlock (redirect_lock);
+
+ if (diskstream->playlist()->read (buffers[0], mix_buffer, gain_buffer, workbuf, start, nframes) != nframes) {
+ return -1;
+ }
+
+ uint32_t n=1;
+ bi = buffers.begin();
+ b = buffers[0];
+ ++bi;
+ for (; bi != buffers.end(); ++bi, ++n) {
+ if (n < diskstream->n_channels()) {
+ if (diskstream->playlist()->read ((*bi), mix_buffer, gain_buffer, workbuf, start, nframes, n) != nframes) {
+ return -1;
+ }
+ b = (*bi);
+ }
+ else {
+ /* duplicate last across remaining buffers */
+ memcpy ((*bi), b, sizeof (Sample) * nframes);
+ }
+ }
+
+
+ /* note: only run inserts during export. other layers in the machinery
+ will already have checked that there are no external port inserts.
+ */
+
+ for (i = _redirects.begin(); i != _redirects.end(); ++i) {
+ Insert *insert;
+
+ if ((insert = dynamic_cast<Insert*>(*i)) != 0) {
+ switch (insert->placement()) {
+ case PreFader:
+ insert->run (buffers, nbufs, nframes, 0);
+ break;
+ case PostFader:
+ post_fader_work = true;
+ break;
+ }
+ }
+ }
+
+ if (_gain_automation_curve.automation_state() == Play) {
+
+ _gain_automation_curve.get_vector (start, start + nframes, gain_automation, nframes);
+
+ for (bi = buffers.begin(); bi != buffers.end(); ++bi) {
+ Sample *b = *bi;
+ for (jack_nframes_t n = 0; n < nframes; ++n) {
+ b[n] *= gain_automation[n];
+ }
+ }
+
+ } else {
+
+ for (bi = buffers.begin(); bi != buffers.end(); ++bi) {
+ Sample *b = *bi;
+ for (jack_nframes_t n = 0; n < nframes; ++n) {
+ b[n] *= this_gain;
+ }
+ }
+ }
+
+ if (post_fader_work) {
+
+ for (i = _redirects.begin(); i != _redirects.end(); ++i) {
+ PluginInsert *insert;
+
+ if ((insert = dynamic_cast<PluginInsert*>(*i)) != 0) {
+ switch ((*i)->placement()) {
+ case PreFader:
+ break;
+ case PostFader:
+ insert->run (buffers, nbufs, nframes, 0);
+ break;
+ }
+ }
+ }
+ }
+#endif
+ return 0;
+}
+
+void
+MidiTrack::set_latency_delay (jack_nframes_t longest_session_latency)
+{
+ Route::set_latency_delay (longest_session_latency);
+ diskstream->set_roll_delay (_roll_delay);
+}
+
+jack_nframes_t
+MidiTrack::update_total_latency ()
+{
+ _own_latency = 0;
+
+ for (RedirectList::iterator i = _redirects.begin(); i != _redirects.end(); ++i) {
+ if ((*i)->active ()) {
+ _own_latency += (*i)->latency ();
+ }
+ }
+
+ set_port_latency (_own_latency);
+
+ return _own_latency;
+}
+
+void
+MidiTrack::bounce (InterThreadInfo& itt)
+{
+ //vector<MidiSource*> srcs;
+ //_session.write_one_midi_track (*this, 0, _session.current_end_frame(), false, srcs, itt);
+}
+
+
+void
+MidiTrack::bounce_range (jack_nframes_t start, jack_nframes_t end, InterThreadInfo& itt)
+{
+ //vector<MidiSource*> srcs;
+ //_session.write_one_midi_track (*this, start, end, false, srcs, itt);
+}
+
+void
+MidiTrack::freeze (InterThreadInfo& itt)
+{
+#if 0
+ Insert* insert;
+ vector<MidiSource*> srcs;
+ string new_playlist_name;
+ Playlist* new_playlist;
+ string dir;
+ AudioRegion* region;
+ string region_name;
+
+ if ((_freeze_record.playlist = diskstream->playlist()) == 0) {
+ return;
+ }
+
+ uint32_t n = 1;
+
+ while (n < (UINT_MAX-1)) {
+
+ string candidate;
+
+ candidate = string_compose ("<F%2>%1", _freeze_record.playlist->name(), n);
+
+ if (_session.playlist_by_name (candidate) == 0) {
+ new_playlist_name = candidate;
+ break;
+ }
+
+ ++n;
+
+ }
+
+ if (n == (UINT_MAX-1)) {
+ PBD::error << string_compose (X_("There Are too many frozen versions of playlist \"%1\""
+ " to create another one"), _freeze_record.playlist->name())
+ << endmsg;
+ return;
+ }
+
+ if (_session.write_one_midi_track (*this, 0, _session.current_end_frame(), true, srcs, itt)) {
+ return;
+ }
+
+ _freeze_record.insert_info.clear ();
+ _freeze_record.have_mementos = true;
+
+ {
+ Glib::RWLock::ReaderLock lm (redirect_lock);
+
+ for (RedirectList::iterator r = _redirects.begin(); r != _redirects.end(); ++r) {
+
+ if ((insert = dynamic_cast<Insert*>(*r)) != 0) {
+
+ FreezeRecordInsertInfo* frii = new FreezeRecordInsertInfo ((*r)->get_state());
+
+ frii->insert = insert;
+ frii->id = insert->id();
+ frii->memento = (*r)->get_memento();
+
+ _freeze_record.insert_info.push_back (frii);
+
+ /* now deactivate the insert */
+
+ insert->set_active (false, this);
+ }
+ }
+ }
+
+ new_playlist = new MidiPlaylist (_session, new_playlist_name, false);
+ region_name = new_playlist_name;
+
+ /* create a new region from all filesources, keep it private */
+
+ region = new AudioRegion (srcs, 0, srcs[0]->length(),
+ region_name, 0,
+ (AudioRegion::Flag) (AudioRegion::WholeFile|AudioRegion::DefaultFlags),
+ false);
+
+ new_playlist->set_orig_diskstream_id (diskstream->id());
+ new_playlist->add_region (*region, 0);
+ new_playlist->set_frozen (true);
+ region->set_locked (true);
+
+ diskstream->use_playlist (dynamic_cast<MidiPlaylist*>(new_playlist));
+ diskstream->set_record_enabled (false, this);
+
+ _freeze_record.state = Frozen;
+ FreezeChange(); /* EMIT SIGNAL */
+}
+
+void
+MidiTrack::unfreeze ()
+{
+ if (_freeze_record.playlist) {
+ diskstream->use_playlist (_freeze_record.playlist);
+
+ if (_freeze_record.have_mementos) {
+
+ for (vector<FreezeRecordInsertInfo*>::iterator i = _freeze_record.insert_info.begin(); i != _freeze_record.insert_info.end(); ++i) {
+ (*i)->memento ();
+ }
+
+ } else {
+
+ Glib::RWLock::ReaderLock lm (redirect_lock); // should this be a write lock? jlc
+ for (RedirectList::iterator i = _redirects.begin(); i != _redirects.end(); ++i) {
+ for (vector<FreezeRecordInsertInfo*>::iterator ii = _freeze_record.insert_info.begin(); ii != _freeze_record.insert_info.end(); ++ii) {
+ if ((*ii)->id == (*i)->id()) {
+ (*i)->set_state (((*ii)->state));
+ break;
+ }
+ }
+ }
+ }
+
+ _freeze_record.playlist = 0;
+ }
+#endif
+ _freeze_record.state = UnFrozen;
+ FreezeChange (); /* EMIT SIGNAL */
+}
+
+MidiTrack::FreezeRecord::~FreezeRecord ()
+{
+ for (vector<FreezeRecordInsertInfo*>::iterator i = insert_info.begin(); i != insert_info.end(); ++i) {
+ delete *i;
+ }
+}
+
+MidiTrack::FreezeState
+MidiTrack::freeze_state() const
+{
+ return _freeze_record.state;
+}
+
+
+void
+MidiTrack::reset_midi_control (MIDI::Port* port, bool on)
+{
+ MIDI::channel_t chn;
+ MIDI::eventType ev;
+ MIDI::byte extra;
+
+ Route::reset_midi_control (port, on);
+
+ _midi_rec_enable_control.get_control_info (chn, ev, extra);
+ if (!on) {
+ chn = -1;
+ }
+ _midi_rec_enable_control.midi_rebind (port, chn);
+}
+
+void
+MidiTrack::send_all_midi_feedback ()
+{
+ if (_session.get_midi_feedback()) {
+
+ Route::send_all_midi_feedback();
+
+ _midi_rec_enable_control.send_feedback (record_enabled());
+ }
+}
+
+
+MidiTrack::MIDIRecEnableControl::MIDIRecEnableControl (MidiTrack& s, MIDI::Port* port)
+ : MIDI::Controllable (port, 0), track (s), setting(false)
+{
+ last_written = false; /* XXX need a good out of bound value */
+}
+
+void
+MidiTrack::MIDIRecEnableControl::set_value (float val)
+{
+ bool bval = ((val >= 0.5f) ? true: false);
+
+ setting = true;
+ track.set_record_enable (bval, this);
+ setting = false;
+}
+
+void
+MidiTrack::MIDIRecEnableControl::send_feedback (bool value)
+{
+
+ if (!setting && get_midi_feedback()) {
+ MIDI::byte val = (MIDI::byte) (value ? 127: 0);
+ MIDI::channel_t ch = 0;
+ MIDI::eventType ev = MIDI::none;
+ MIDI::byte additional = 0;
+ MIDI::EventTwoBytes data;
+
+ if (get_control_info (ch, ev, additional)) {
+ data.controller_number = additional;
+ data.value = val;
+
+ track._session.send_midi_message (get_port(), ev, ch, data);
+ }
+ }
+
+}
+
+MIDI::byte*
+MidiTrack::MIDIRecEnableControl::write_feedback (MIDI::byte* buf, int32_t& bufsize, bool val, bool force)
+{
+ if (get_midi_feedback()) {
+
+ MIDI::channel_t ch = 0;
+ MIDI::eventType ev = MIDI::none;
+ MIDI::byte additional = 0;
+
+ if (get_control_info (ch, ev, additional)) {
+ if (val != last_written || force) {
+ *buf++ = ev & ch;
+ *buf++ = additional; /* controller number */
+ *buf++ = (MIDI::byte) (val ? 127: 0);
+ last_written = val;
+ bufsize -= 3;
+ }
+ }
+ }
+
+ return buf;
+}
+
+void
+MidiTrack::set_mode (TrackMode m)
+{
+ if (diskstream) {
+ if (_mode != m) {
+ _mode = m;
+ diskstream->set_destructive (m == Destructive);
+ ModeChanged();
+ }
+ }
+}
diff --git a/libs/ardour/smf_source.cc b/libs/ardour/smf_source.cc
new file mode 100644
index 0000000000..10cf7ab335
--- /dev/null
+++ b/libs/ardour/smf_source.cc
@@ -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;
+}
+