summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2009-02-16 02:51:16 +0000
committerDavid Robillard <d@drobilla.net>2009-02-16 02:51:16 +0000
commit3963d2b0b224e79fdf8e852e39fc3a765fa1431b (patch)
tree0c6c8a9d519d46437ffe9cb96f09ca337f33faff
parentbeb3eea62bf217d0a7b2a86a96d5c375329df10a (diff)
Move all beats <-> frames time conversion into a single object that can be passed around.
This has 3 main benefits: - All conversion code is in one place (less duplication, potential bugs) - The conversion method can be passed to things that are ignorant of the actual time units involved, information required, etc. (In the future it would be nice to have user selectable tempo/frame time) - It should be relatively simple now to support tempo changes part-way through a MIDI region (at least architecturally speaking) git-svn-id: svn://localhost/ardour2/branches/3.0@4594 d708f5d6-7413-0410-9779-e7cbd77b26cf
-rw-r--r--gtk2_ardour/editor.h2
-rw-r--r--gtk2_ardour/midi_region_view.cc11
-rw-r--r--libs/ardour/SConscript7
-rw-r--r--libs/ardour/ardour/beats_frames_converter.h52
-rw-r--r--libs/ardour/ardour/midi_source.h21
-rw-r--r--libs/ardour/ardour/session.h6
-rw-r--r--libs/ardour/audio_region_importer.cc2
-rw-r--r--libs/ardour/beats_frames_converter.cc54
-rw-r--r--libs/ardour/import.cc24
-rw-r--r--libs/ardour/midi_source.cc22
-rw-r--r--libs/ardour/quantize.cc13
-rw-r--r--libs/ardour/smf_source.cc29
-rw-r--r--libs/evoral/evoral/TimeConverter.hpp43
13 files changed, 206 insertions, 80 deletions
diff --git a/gtk2_ardour/editor.h b/gtk2_ardour/editor.h
index 379716ed36..f0e28677f7 100644
--- a/gtk2_ardour/editor.h
+++ b/gtk2_ardour/editor.h
@@ -1305,7 +1305,7 @@ class Editor : public PublicEditor
/* import specific info */
- struct EditorImportStatus : public ARDOUR::Session::import_status {
+ struct EditorImportStatus : public ARDOUR::Session::ImportStatus {
Editing::ImportMode mode;
nframes64_t pos;
int target_tracks;
diff --git a/gtk2_ardour/midi_region_view.cc b/gtk2_ardour/midi_region_view.cc
index 5339131c8e..7a4acb560d 100644
--- a/gtk2_ardour/midi_region_view.cc
+++ b/gtk2_ardour/midi_region_view.cc
@@ -641,9 +641,6 @@ MidiRegionView::display_program_change_flags()
}
}
break;
- } else if (control->first.type() == MidiCCAutomation) {
- //cerr << " found CC Automation of channel " << int(control->first.channel())
- // << " and id " << control->first.id() << endl;
}
}
}
@@ -1391,17 +1388,13 @@ MidiRegionView::get_position_pixels()
nframes64_t
MidiRegionView::beats_to_frames(double beats) const
{
- const Meter& m = trackview.session().tempo_map().meter_at(_region->position());
- const Tempo& t = trackview.session().tempo_map().tempo_at(_region->position());
- return lrint(beats * m.frames_per_bar(t, trackview.session().frame_rate()) / m.beats_per_bar());
+ return midi_region()->midi_source()->converter().to(beats);
}
double
MidiRegionView::frames_to_beats(nframes64_t frames) const
{
- const Meter& m = trackview.session().tempo_map().meter_at(_region->position());
- const Tempo& t = trackview.session().tempo_map().tempo_at(_region->position());
- return frames / m.frames_per_bar(t, trackview.session().frame_rate()) * m.beats_per_bar();
+ return midi_region()->midi_source()->converter().from(frames);
}
void
diff --git a/libs/ardour/SConscript b/libs/ardour/SConscript
index 11a5b36566..83b7d659b5 100644
--- a/libs/ardour/SConscript
+++ b/libs/ardour/SConscript
@@ -38,6 +38,7 @@ audio_library.cc
audio_playlist.cc
audio_playlist_importer.cc
audio_port.cc
+audio_region_importer.cc
audio_track.cc
audio_track_importer.cc
audioanalyser.cc
@@ -45,13 +46,13 @@ audioengine.cc
audiofile_tagger.cc
audiofilesource.cc
audioregion.cc
-audio_region_importer.cc
audiosource.cc
auditioner.cc
automatable.cc
automation.cc
automation_control.cc
automation_list.cc
+beats_frames_converter.cc
broadcast_info.cc
buffer.cc
buffer_set.cc
@@ -65,8 +66,8 @@ cycle_timer.cc
default_click.cc
directory_names.cc
diskstream.cc
-element_importer.cc
element_import_handler.cc
+element_importer.cc
enums.cc
event_type_map.cc
export_channel.cc
@@ -162,8 +163,8 @@ svn_revision.cc
tape_file_matcher.cc
template_utils.cc
tempo.cc
-ticker.cc
tempo_map_importer.cc
+ticker.cc
track.cc
transient_detector.cc
user_bundle.cc
diff --git a/libs/ardour/ardour/beats_frames_converter.h b/libs/ardour/ardour/beats_frames_converter.h
new file mode 100644
index 0000000000..79972cb4c5
--- /dev/null
+++ b/libs/ardour/ardour/beats_frames_converter.h
@@ -0,0 +1,52 @@
+/*
+ Copyright (C) 2009 Paul Davis
+ Author: 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.
+
+ $Id: midiregion.h 733 2006-08-01 17:19:38Z drobilla $
+*/
+
+#include <evoral/TimeConverter.hpp>
+#include <ardour/types.h>
+
+#ifndef __ardour_beats_frames_converter_h__
+#define __ardour_beats_frames_converter_h__
+
+namespace ARDOUR {
+
+class Session;
+
+class BeatsFramesConverter : public Evoral::TimeConverter<double,nframes_t> {
+public:
+ BeatsFramesConverter(Session& session, nframes_t origin)
+ : _session(session)
+ , _origin(origin)
+ {}
+
+ nframes_t to(double beats) const;
+ double from(nframes_t frames) const;
+
+ nframes_t origin() const { return _origin; }
+ void set_origin(nframes_t origin) { _origin = origin; }
+
+private:
+ Session& _session;
+ nframes_t _origin;
+};
+
+} /* namespace ARDOUR */
+
+#endif /* __ardour_beats_frames_converter_h__ */
diff --git a/libs/ardour/ardour/midi_source.h b/libs/ardour/ardour/midi_source.h
index 3f8ed82310..3a28cde8c6 100644
--- a/libs/ardour/ardour/midi_source.h
+++ b/libs/ardour/ardour/midi_source.h
@@ -30,8 +30,7 @@
#include <ardour/ardour.h>
#include <ardour/buffer.h>
#include <ardour/source.h>
-
-using std::string;
+#include <ardour/beats_frames_converter.h>
namespace ARDOUR {
@@ -44,7 +43,7 @@ class MidiSource : public Source
public:
typedef double TimeType;
- MidiSource (Session& session, string name);
+ MidiSource (Session& session, std::string name);
MidiSource (Session& session, const XMLNode&);
virtual ~MidiSource ();
@@ -66,13 +65,13 @@ class MidiSource : public Source
virtual void mark_streaming_write_started ();
virtual void mark_streaming_write_completed ();
- uint64_t timeline_position () { return _timeline_position; }
- void set_timeline_position (nframes_t when) { _timeline_position = when; }
+ uint64_t timeline_position () { return _timeline_position; }
+ void set_timeline_position (nframes_t when);
virtual void session_saved();
- string captured_for() const { return _captured_for; }
- void set_captured_for (string str) { _captured_for = str; }
+ std::string captured_for() const { return _captured_for; }
+ void set_captured_for (std::string str) { _captured_for = str; }
uint32_t read_data_count() const { return _read_data_count; }
uint32_t write_data_count() const { return _write_data_count; }
@@ -96,6 +95,8 @@ class MidiSource : public Source
void set_model(boost::shared_ptr<MidiModel> m) { _model = m; }
void drop_model() { _model.reset(); }
+ BeatsFramesConverter& converter() { return _converter; }
+
protected:
virtual void flush_midi() = 0;
@@ -104,10 +105,12 @@ class MidiSource : public Source
virtual nframes_t write_unlocked (MidiRingBuffer<nframes_t>& dst, nframes_t cnt) = 0;
mutable Glib::Mutex _lock;
- string _captured_for;
+ std::string _captured_for;
uint64_t _timeline_position;
mutable uint32_t _read_data_count; ///< modified in read()
mutable uint32_t _write_data_count; ///< modified in write()
+
+ BeatsFramesConverter _converter;
boost::shared_ptr<MidiModel> _model;
bool _writing;
@@ -116,7 +119,7 @@ class MidiSource : public Source
mutable nframes_t _last_read_end;
private:
- bool file_changed (string path);
+ bool file_changed (std::string path);
};
}
diff --git a/libs/ardour/ardour/session.h b/libs/ardour/ardour/session.h
index 8de0e1c523..ef25ebaea3 100644
--- a/libs/ardour/ardour/session.h
+++ b/libs/ardour/ardour/session.h
@@ -610,7 +610,7 @@ class Session : public PBD::StatefulDestructible
/* source management */
- struct import_status : public InterThreadInfo {
+ struct ImportStatus : public InterThreadInfo {
string doing_what;
/* control info */
@@ -624,8 +624,8 @@ class Session : public PBD::StatefulDestructible
SourceList sources;
};
- void import_audiofiles (import_status&);
- bool sample_rate_convert (import_status&, string infile, string& outfile);
+ void import_audiofiles (ImportStatus&);
+ bool sample_rate_convert (ImportStatus&, string infile, string& outfile);
string build_tmp_convert_name (string file);
boost::shared_ptr<ExportHandler> get_export_handler ();
diff --git a/libs/ardour/audio_region_importer.cc b/libs/ardour/audio_region_importer.cc
index 714446bfe2..23f3bfd6c6 100644
--- a/libs/ardour/audio_region_importer.cc
+++ b/libs/ardour/audio_region_importer.cc
@@ -336,7 +336,7 @@ AudioRegionImporter::prepare_sources ()
return;
}
- Session::import_status status;
+ Session::ImportStatus status;
// Get sources that still need to be imported
for (std::list<string>::iterator it = filenames.begin(); it != filenames.end(); ++it) {
diff --git a/libs/ardour/beats_frames_converter.cc b/libs/ardour/beats_frames_converter.cc
new file mode 100644
index 0000000000..736068e690
--- /dev/null
+++ b/libs/ardour/beats_frames_converter.cc
@@ -0,0 +1,54 @@
+/*
+ Copyright (C) 2009 Paul Davis
+ Author: 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.
+
+ $Id: midiregion.h 733 2006-08-01 17:19:38Z drobilla $
+*/
+
+#include <ardour/audioengine.h>
+#include <ardour/beats_frames_converter.h>
+#include <ardour/session.h>
+#include <ardour/tempo.h>
+
+namespace ARDOUR {
+
+nframes_t
+BeatsFramesConverter::to(double beats) const
+{
+ // FIXME: assumes tempo never changes after origin
+ const Tempo& tempo = _session.tempo_map().tempo_at(_origin);
+ const double frames_per_beat = tempo.frames_per_beat(
+ _session.engine().frame_rate(),
+ _session.tempo_map().meter_at(_origin));
+
+ return lrint(beats * frames_per_beat);
+}
+
+double
+BeatsFramesConverter::from(nframes_t frames) const
+{
+ // FIXME: assumes tempo never changes after origin
+ const Tempo& tempo = _session.tempo_map().tempo_at(_origin);
+ const double frames_per_beat = tempo.frames_per_beat(
+ _session.engine().frame_rate(),
+ _session.tempo_map().meter_at(_origin));
+
+ return frames / frames_per_beat;
+}
+
+} /* namespace ARDOUR */
+
diff --git a/libs/ardour/import.cc b/libs/ardour/import.cc
index 824cdb43f7..0e2cfb9988 100644
--- a/libs/ardour/import.cc
+++ b/libs/ardour/import.cc
@@ -257,7 +257,7 @@ compose_status_message (const string& path,
}
static void
-write_audio_data_to_new_files (ImportableSource* source, Session::import_status& status,
+write_audio_data_to_new_files (ImportableSource* source, Session::ImportStatus& status,
vector<boost::shared_ptr<Source> >& newfiles)
{
const nframes_t nframes = ResampledImportableSource::blocksize;
@@ -309,8 +309,8 @@ write_audio_data_to_new_files (ImportableSource* source, Session::import_status&
}
static void
-write_midi_data_to_new_files (Evoral::SMF* source, Session::import_status& status,
- vector<boost::shared_ptr<Source> >& newfiles)
+write_midi_data_to_new_files (Evoral::SMF* source, Session::ImportStatus& status,
+ vector<boost::shared_ptr<Source> >& newfiles)
{
uint32_t buf_size = 4;
uint8_t* buf = (uint8_t*)malloc(buf_size);
@@ -354,20 +354,14 @@ write_midi_data_to_new_files (Evoral::SMF* source, Session::import_status& statu
if (status.progress < 0.99)
status.progress += 0.01;
}
-
- nframes_t timeline_position = 0; // FIXME: ?
- // FIXME: kluuuuudge: assumes tempo never changes after start
- const double frames_per_beat = smfs->session().tempo_map().tempo_at(
- timeline_position).frames_per_beat(
- smfs->session().engine().frame_rate(),
- smfs->session().tempo_map().meter_at(timeline_position));
-
- smfs->update_length(0, (nframes_t) ceil ((t / (double)source->ppqn()) * frames_per_beat));
+ const double length_beats = ceil(t / (double)source->ppqn());
+ smfs->update_length(0, smfs->converter().to(length_beats));
smfs->end_write();
- if (status.cancel)
+ if (status.cancel) {
break;
+ }
}
} catch (...) {
@@ -382,11 +376,11 @@ remove_file_source (boost::shared_ptr<Source> source)
}
// This function is still unable to cleanly update an existing source, even though
-// it is possible to set the import_status flag accordingly. The functinality
+// it is possible to set the ImportStatus flag accordingly. The functinality
// is disabled at the GUI until the Source implementations are able to provide
// the necessary API.
void
-Session::import_audiofiles (import_status& status)
+Session::import_audiofiles (ImportStatus& status)
{
uint32_t cnt = 1;
typedef vector<boost::shared_ptr<Source> > Sources;
diff --git a/libs/ardour/midi_source.cc b/libs/ardour/midi_source.cc
index 70abb99e56..ece0d9b104 100644
--- a/libs/ardour/midi_source.cc
+++ b/libs/ardour/midi_source.cc
@@ -52,16 +52,20 @@ sigc::signal<void,MidiSource *> MidiSource::MidiSourceCreated;
MidiSource::MidiSource (Session& s, string name)
: Source (s, name, DataType::MIDI)
, _timeline_position(0)
+ , _read_data_count(0)
+ , _write_data_count(0)
+ , _converter(s, _timeline_position)
, _writing (false)
, _last_read_end(0)
{
- _read_data_count = 0;
- _write_data_count = 0;
}
MidiSource::MidiSource (Session& s, const XMLNode& node)
: Source (s, node)
, _timeline_position(0)
+ , _read_data_count(0)
+ , _write_data_count(0)
+ , _converter(s, _timeline_position)
, _writing (false)
, _last_read_end(0)
{
@@ -110,12 +114,7 @@ MidiSource::midi_read (MidiRingBuffer<nframes_t>& dst, nframes_t start, nframes_
Glib::Mutex::Lock lm (_lock);
if (_model) {
- // FIXME: assumes tempo never changes after start
- const Tempo& tempo = _session.tempo_map().tempo_at(_timeline_position);
- const double frames_per_beat = tempo.frames_per_beat(
- _session.engine().frame_rate(),
- _session.tempo_map().meter_at(_timeline_position));
-#define BEATS_TO_FRAMES(t) (((t) * frames_per_beat) + stamp_offset - negative_stamp_offset)
+#define BEATS_TO_FRAMES(t) (_converter.to(t) + stamp_offset - negative_stamp_offset)
Evoral::Sequence<double>::const_iterator& i = _model_iter;
@@ -162,6 +161,13 @@ MidiSource::file_changed (string path)
}
void
+MidiSource::set_timeline_position (nframes_t when)
+{
+ _timeline_position = when;
+ _converter.set_origin(when);
+}
+
+void
MidiSource::mark_streaming_midi_write_started (NoteMode mode, nframes_t start_frame)
{
set_timeline_position(start_frame); // why do I have a feeling this can break somehow...
diff --git a/libs/ardour/quantize.cc b/libs/ardour/quantize.cc
index e4a2809277..76eb924612 100644
--- a/libs/ardour/quantize.cc
+++ b/libs/ardour/quantize.cc
@@ -62,19 +62,12 @@ Quantize::run (boost::shared_ptr<Region> r)
boost::shared_ptr<MidiModel> model = src->model();
- // FIXME: Model really needs to be switched to beat time (double) ASAP
-
- const Tempo& t = session.tempo_map().tempo_at(r->start());
- const Meter& m = session.tempo_map().meter_at(r->start());
-
- double q_frames = _q * (m.frames_per_bar(t, session.frame_rate()) / (double)m.beats_per_bar());
-
for (Evoral::Sequence<MidiModel::TimeType>::Notes::iterator i = model->notes().begin();
i != model->notes().end(); ++i) {
- const double new_time = lrint((*i)->time() / q_frames) * q_frames;
- double new_dur = lrint((*i)->length() / q_frames) * q_frames;
+ const double new_time = lrint((*i)->time() / _q) * _q;
+ double new_dur = lrint((*i)->length() / _q) * _q;
if (new_dur == 0.0)
- new_dur = q_frames;
+ new_dur = _q;
(*i)->set_time(new_time);
(*i)->set_length(new_dur);
diff --git a/libs/ardour/smf_source.cc b/libs/ardour/smf_source.cc
index 5fd09ef1bb..ab0c290191 100644
--- a/libs/ardour/smf_source.cc
+++ b/libs/ardour/smf_source.cc
@@ -41,7 +41,6 @@
#include <ardour/midi_ring_buffer.h>
#include <ardour/session.h>
#include <ardour/smf_source.h>
-#include <ardour/tempo.h>
#include "i18n.h"
@@ -145,13 +144,7 @@ SMFSource::read_unlocked (MidiRingBuffer<nframes_t>& dst, nframes_t start, nfram
size_t scratch_size = 0; // keep track of scratch to minimize reallocs
- // FIXME: assumes tempo never changes after start
- const Tempo& tempo = _session.tempo_map().tempo_at(_timeline_position);
- const double frames_per_beat = tempo.frames_per_beat(
- _session.engine().frame_rate(),
- _session.tempo_map().meter_at(_timeline_position));
-
- const uint64_t start_ticks = (uint64_t)((start / frames_per_beat) * ppqn());
+ const uint64_t start_ticks = (uint64_t)(_converter.from(start) * ppqn());
if (_last_read_end == 0 || start != _last_read_end) {
cerr << "SMFSource::read_unlocked seeking to " << start << endl;
@@ -183,8 +176,7 @@ SMFSource::read_unlocked (MidiRingBuffer<nframes_t>& dst, nframes_t start, nfram
ev_type = EventTypeMap::instance().midi_event_type(ev_buffer[0]);
assert(time >= start_ticks);
- const nframes_t ev_frame_time = (nframes_t)(
- ((time / (double)ppqn()) * frames_per_beat)) + stamp_offset;
+ const nframes_t ev_frame_time = _converter.to(time / (double)ppqn()) + stamp_offset;
if (ev_frame_time < start + dur) {
dst.write(ev_frame_time - negative_stamp_offset, ev_type, ev_size, ev_buffer);
@@ -323,24 +315,19 @@ SMFSource::append_event_unlocked_frames(const Evoral::Event<nframes_t>& ev)
return;
}
- // FIXME: assumes tempo never changes after start
- const Tempo& tempo = _session.tempo_map().tempo_at(_timeline_position);
- const double frames_per_beat = tempo.frames_per_beat(
- _session.engine().frame_rate(),
- _session.tempo_map().meter_at(_timeline_position));
-
- uint32_t delta_time = (uint32_t)((ev.time() - _last_ev_time_frames)
- / frames_per_beat * (double)ppqn());
+ const nframes_t delta_time_frames = ev.time() - _last_ev_time_frames;
+ const double delta_time_beats = _converter.from(delta_time_frames);
+ const uint32_t delta_time_ticks = (uint32_t)(lrint(delta_time_beats * (double)ppqn()));
- Evoral::SMF::append_event_delta(delta_time, ev.size(), ev.buffer());
+ Evoral::SMF::append_event_delta(delta_time_ticks, ev.size(), ev.buffer());
_last_ev_time_frames = ev.time();
_write_data_count += ev.size();
if (_model) {
- double beat_time = ev.time() / frames_per_beat;
+ const double ev_time_beats = _converter.from(ev.time());
const Evoral::Event<double> beat_ev(
- ev.event_type(), beat_time, ev.size(), (uint8_t*)ev.buffer());
+ ev.event_type(), ev_time_beats, ev.size(), (uint8_t*)ev.buffer());
_model->append(beat_ev);
}
}
diff --git a/libs/evoral/evoral/TimeConverter.hpp b/libs/evoral/evoral/TimeConverter.hpp
new file mode 100644
index 0000000000..3f434d9dd7
--- /dev/null
+++ b/libs/evoral/evoral/TimeConverter.hpp
@@ -0,0 +1,43 @@
+/* This file is part of Evoral.
+ * Copyright (C) 2009 Dave Robillard <http://drobilla.net>
+ * Copyright (C) 2009 Paul Davis
+ *
+ * Evoral 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.
+ *
+ * Evoral 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 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.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef EVORAL_TIME_CONVERTER_HPP
+#define EVORAL_TIME_CONVERTER_HPP
+
+namespace Evoral {
+
+/** A bidirectional converter between two different time units.
+ *
+ * Think of the conversion method names as if they are written in-between
+ * the two template parameters (i.e. "A <name> B").
+ */
+template<typename A, typename B>
+class TimeConverter {
+public:
+ virtual ~TimeConverter() {}
+
+ /** Convert A time to B time (A to B) */
+ virtual B to(A a) const = 0;
+
+ /** Convert B time to A time (A from B) */
+ virtual A from(B b) const = 0;
+};
+
+} // namespace Evoral
+
+#endif // EVORAL_TIME_CONVERTER_HPP