diff options
author | David Robillard <d@drobilla.net> | 2008-02-09 18:23:57 +0000 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2008-02-09 18:23:57 +0000 |
commit | 81f335330c209359ef1d2b3bdedc5c19790d2eba (patch) | |
tree | 2262742e60b1360ecd66bf1f5be57e4df2b53de3 | |
parent | 43dd122ffcdb6a0b40a56777245a870e411d439c (diff) | |
download | raul-81f335330c209359ef1d2b3bdedc5c19790d2eba.tar.gz raul-81f335330c209359ef1d2b3bdedc5c19790d2eba.tar.bz2 raul-81f335330c209359ef1d2b3bdedc5c19790d2eba.zip |
Use Raul::TimeStamp (LV2 compatible typed 32:32 fixed timestamp) everywhere.
Fix initial size of Patchage messages window.
Machina disabled for now (transitioning to generic timestamps).
git-svn-id: http://svn.drobilla.net/lad/raul@1133 a436a847-0d15-0410-975c-d299462d15a1
-rw-r--r-- | raul/EventRingBuffer.hpp (renamed from raul/StampedChunkRingBuffer.hpp) | 40 | ||||
-rw-r--r-- | raul/MIDISink.hpp | 4 | ||||
-rw-r--r-- | raul/Makefile.am | 21 | ||||
-rw-r--r-- | raul/Quantizer.hpp | 9 | ||||
-rw-r--r-- | raul/SMFReader.hpp | 11 | ||||
-rw-r--r-- | raul/SMFWriter.hpp | 23 | ||||
-rw-r--r-- | raul/TimeSlice.hpp | 71 | ||||
-rw-r--r-- | raul/TimeStamp.hpp | 240 | ||||
-rw-r--r-- | raul/types.hpp | 35 | ||||
-rw-r--r-- | src/SMFReader.cpp | 10 | ||||
-rw-r--r-- | src/SMFWriter.cpp | 47 | ||||
-rw-r--r-- | src/TimeSlice.cpp | 2 | ||||
-rw-r--r-- | tests/midi_ringbuffer_test.cpp | 11 | ||||
-rw-r--r-- | tests/quantize_test.cpp | 13 | ||||
-rw-r--r-- | tests/smf_test.cpp | 8 | ||||
-rw-r--r-- | tests/time_test.cpp | 16 |
16 files changed, 405 insertions, 156 deletions
diff --git a/raul/StampedChunkRingBuffer.hpp b/raul/EventRingBuffer.hpp index b4f9371..9a28084 100644 --- a/raul/StampedChunkRingBuffer.hpp +++ b/raul/EventRingBuffer.hpp @@ -15,63 +15,63 @@ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef RAUL_STAMPED_CHUNK_RING_BUFFER_HPP -#define RAUL_STAMPED_CHUNK_RING_BUFFER_HPP +#ifndef RAUL_EVENT_RING_BUFFER_HPP +#define RAUL_EVENT_RING_BUFFER_HPP #include <cassert> #include <algorithm> #include <glib.h> -#include <raul/types.hpp> #include <raul/RingBuffer.hpp> +#include <raul/TimeStamp.hpp> namespace Raul { -/** A RingBuffer of timestamped binary "chunks". +/** A RingBuffer of events (generic time-stamped binary "blobs"). * * This packs a timestamp, size, and size bytes of data flat into the buffer. * Useful for MIDI events, OSC messages, etc. */ -class StampedChunkRingBuffer : private Raul::RingBuffer<Byte> { +class EventRingBuffer : private Raul::RingBuffer<uint8_t> { public: - /** @param size Size in bytes. + /** @param capacity Ringbuffer capacity in bytes. */ - StampedChunkRingBuffer(size_t size) - : RingBuffer<Byte>(size) + EventRingBuffer(size_t capacity) + : RingBuffer<uint8_t>(capacity) {} size_t capacity() const { return _size; } - size_t write(TickTime time, size_t size, const Byte* buf); - bool read(TickTime* time, size_t* size, Byte* buf); + size_t write(TimeStamp time, size_t size, const uint8_t* buf); + bool read(TimeStamp* time, size_t* size, uint8_t* buf); }; inline bool -StampedChunkRingBuffer::read(TickTime* time, size_t* size, Byte* buf) +EventRingBuffer::read(TimeStamp* time, size_t* size, uint8_t* buf) { - bool success = RingBuffer<Byte>::full_read(sizeof(TickTime), (Byte*)time); + bool success = RingBuffer<uint8_t>::full_read(sizeof(TimeStamp), (uint8_t*)time); if (success) - success = RingBuffer<Byte>::full_read(sizeof(size_t), (Byte*)size); + success = RingBuffer<uint8_t>::full_read(sizeof(size_t), (uint8_t*)size); if (success) - success = RingBuffer<Byte>::full_read(*size, buf); + success = RingBuffer<uint8_t>::full_read(*size, buf); return success; } inline size_t -StampedChunkRingBuffer::write(TickTime time, size_t size, const Byte* buf) +EventRingBuffer::write(TimeStamp time, size_t size, const uint8_t* buf) { assert(size > 0); - if (write_space() < (sizeof(TickTime) + sizeof(size_t) + size)) { + if (write_space() < (sizeof(TimeStamp) + sizeof(size_t) + size)) { return 0; } else { - RingBuffer<Byte>::write(sizeof(TickTime), (Byte*)&time); - RingBuffer<Byte>::write(sizeof(size_t), (Byte*)&size); - RingBuffer<Byte>::write(size, buf); + RingBuffer<uint8_t>::write(sizeof(TimeStamp), (uint8_t*)&time); + RingBuffer<uint8_t>::write(sizeof(size_t), (uint8_t*)&size); + RingBuffer<uint8_t>::write(size, buf); return size; } } @@ -79,5 +79,5 @@ StampedChunkRingBuffer::write(TickTime time, size_t size, const Byte* buf) } // namespace Raul -#endif // RAUL_STAMPED_CHUNK_RING_BUFFER_HPP +#endif // RAUL_EVENT_RING_BUFFER_HPP diff --git a/raul/MIDISink.hpp b/raul/MIDISink.hpp index e30dad2..05c8747 100644 --- a/raul/MIDISink.hpp +++ b/raul/MIDISink.hpp @@ -19,7 +19,7 @@ #define RAUL_MIDI_SINK_HPP #include <stdexcept> -#include <raul/TimeSlice.hpp> +#include <raul/TimeStamp.hpp> #include <raul/Deletable.hpp> namespace Raul { @@ -29,7 +29,7 @@ namespace Raul { */ class MIDISink : public Deletable { public: - virtual void write_event(BeatTime time, + virtual void write_event(TimeStamp time, size_t ev_size, const uint8_t* ev) throw (std::logic_error) = 0; }; diff --git a/raul/Makefile.am b/raul/Makefile.am index eeeb792..6c2661b 100644 --- a/raul/Makefile.am +++ b/raul/Makefile.am @@ -3,39 +3,40 @@ raulincludedir = $(includedir)/raul raulinclude_HEADERS = \ Array.hpp \ Atom.hpp \ - AtomLiblo.hpp \ - AtomRDF.hpp \ AtomicInt.hpp \ AtomicPtr.hpp \ + AtomLiblo.hpp \ + AtomRDF.hpp \ Command.hpp \ Deletable.hpp \ DoubleBuffer.hpp \ + EventRingBuffer.hpp \ JackDriver.hpp \ List.hpp \ ListImpl.hpp \ - MIDISink.hpp \ + lv2_event.h \ Maid.hpp \ + midi_events.h \ + MIDISink.hpp \ Path.hpp \ PathTable.hpp \ Process.hpp \ Quantizer.hpp \ RingBuffer.hpp \ + Semaphore.hpp \ + SharedPtr.hpp \ + Slave.hpp \ SMFReader.hpp \ SMFWriter.hpp \ SRMWQueue.hpp \ SRSWQueue.hpp \ - Semaphore.hpp \ - SharedPtr.hpp \ - Slave.hpp \ - StampedChunkRingBuffer.hpp \ Stateful.hpp \ Table.hpp \ TableImpl.hpp \ Thread.hpp \ TimeSlice.hpp \ - WeakPtr.hpp \ - midi_events.h \ - types.hpp + TimeStamp.hpp \ + WeakPtr.hpp if WITH_LASH raulinclude_HEADERS += LashClient.hpp LashServerInterface.hpp LashProject.hpp diff --git a/raul/Quantizer.hpp b/raul/Quantizer.hpp index 13a5cb1..0fe9640 100644 --- a/raul/Quantizer.hpp +++ b/raul/Quantizer.hpp @@ -19,14 +19,19 @@ #define RAUL_QUANTIZER_HPP #include <cmath> +#include <raul/TimeStamp.hpp> namespace Raul { class Quantizer { public: - inline static double quantize(double q, double value) { - return (q > 0) ? lrint(value / q) * q : value; + inline static TimeStamp quantize(TimeStamp q, TimeStamp t) { + assert(q.unit() == t.unit()); + // FIXME: Precision problem? Should probably stay in discrete domain + const double qd = q.to_double(); + const double td = t.to_double(); + return TimeStamp(t.unit(), (qd > 0) ? lrint(td / qd) * qd : td); } }; diff --git a/raul/SMFReader.hpp b/raul/SMFReader.hpp index 51ebdc7..7ce6f5b 100644 --- a/raul/SMFReader.hpp +++ b/raul/SMFReader.hpp @@ -21,6 +21,7 @@ #include <stdexcept> #include <string> #include <inttypes.h> +#include <raul/TimeStamp.hpp> namespace Raul { @@ -38,14 +39,15 @@ public: bool seek_to_track(unsigned track) throw (std::logic_error); + TimeUnit unit() const { return _unit; } uint16_t type() const { return _type; } uint16_t ppqn() const { return _ppqn; } size_t num_tracks() { return _num_tracks; } - int read_event(size_t buf_len, - uint8_t* buf, - uint32_t* ev_size, - uint32_t* ev_delta_time) throw (std::logic_error); + int read_event(size_t buf_len, + uint8_t* buf, + uint32_t* ev_size, + TimeStamp* ev_delta_time) throw (std::logic_error); void close(); @@ -57,6 +59,7 @@ protected: std::string _filename; FILE* _fd; + TimeUnit _unit; uint16_t _type; uint16_t _ppqn; uint16_t _num_tracks; diff --git a/raul/SMFWriter.hpp b/raul/SMFWriter.hpp index df91909..3661ffd 100644 --- a/raul/SMFWriter.hpp +++ b/raul/SMFWriter.hpp @@ -20,6 +20,7 @@ #include <stdexcept> #include <raul/MIDISink.hpp> +#include <raul/TimeStamp.hpp> namespace Raul { @@ -28,15 +29,15 @@ namespace Raul { */ class SMFWriter : public Raul::MIDISink { public: - SMFWriter(unsigned short ppqn=1920); + SMFWriter(TimeUnit unit); ~SMFWriter(); bool start(const std::string& filename, - BeatTime start_time=0) throw (std::logic_error); + TimeStamp start_time) throw (std::logic_error); - uint16_t ppqn() const { return _ppqn; } + TimeUnit unit() const { return _unit; } - void write_event(BeatTime time, + void write_event(TimeStamp time, size_t ev_size, const unsigned char* ev) throw (std::logic_error); @@ -54,13 +55,13 @@ protected: void write_chunk(const char id[4], uint32_t length, void* data); size_t write_var_len(uint32_t val); - std::string _filename; - FILE* _fd; - uint16_t _ppqn; - Raul::BeatTime _start_time; - Raul::BeatTime _last_ev_time; ///< Time last event was written relative to _start_time - uint32_t _track_size; - uint32_t _header_size; ///< size of SMF header, including MTrk chunk header + std::string _filename; + FILE* _fd; + TimeUnit _unit; + Raul::TimeStamp _start_time; + Raul::TimeStamp _last_ev_time; ///< Time last event was written relative to _start_time + uint32_t _track_size; + uint32_t _header_size; ///< size of SMF header, including MTrk chunk header }; diff --git a/raul/TimeSlice.hpp b/raul/TimeSlice.hpp index a7f445b..e8570e1 100644 --- a/raul/TimeSlice.hpp +++ b/raul/TimeSlice.hpp @@ -21,11 +21,14 @@ #include <cassert> #include <cmath> #include <boost/utility.hpp> -#include <raul/types.hpp> +#include <raul/TimeStamp.hpp> +#include <raul/lv2_event.h> namespace Raul { +/* FIXME: all the conversion here is wrong now */ + /** A duration of time, with conversion between tick time and beat time. * * This is a slice along a single timeline (ie t=0 in ticks and t=0 in beats @@ -46,14 +49,14 @@ namespace Raul { */ class TimeSlice : public boost::noncopyable { public: - TimeSlice(double tick_rate, double bpm) - : _tick_rate(tick_rate) + TimeSlice(uint32_t rate, double bpm) + : _tick_rate(rate) , _beat_rate(60.0/bpm) - , _start_ticks(0) - , _length_ticks(0) - , _start_beats(0) - , _length_beats(0) - , _offset_ticks(0) + , _start_ticks(Raul::TimeUnit(Raul::TimeUnit::FRAMES, rate), 0, 0) + , _length_ticks(TimeUnit(TimeUnit::FRAMES, rate), 0, 0) + , _start_beats(TimeUnit(TimeUnit::BEATS, LV2_EVENT_PPQN), 0, 0) + , _length_beats(TimeUnit(TimeUnit::BEATS, LV2_EVENT_PPQN), 0, 0) + , _offset_ticks(TimeUnit(TimeUnit::FRAMES, rate), 0, 0) {} @@ -62,17 +65,17 @@ public: * Note that external offset is not affected by this, don't forget to reset * the offset each cycle! */ - void set_window(TickTime start, TickCount length) { + void set_window(TimeStamp start, TimeDuration length) { _start_ticks = start; _length_ticks = length; update_beat_time(); } - void set_start(TickTime time) { _start_ticks = time; update_beat_time(); } + void set_start(TimeStamp time) { _start_ticks = time; update_beat_time(); } - void set_length(TickCount length) { _length_ticks = length; update_beat_time(); } + void set_length(TimeDuration length) { _length_ticks = length; update_beat_time(); } - bool contains(TickTime time) { + bool contains(TimeStamp time) { return (time >= start_ticks() && time < start_ticks() + length_ticks()); } @@ -90,39 +93,45 @@ public: update_beat_time(); } - inline Seconds beats_to_seconds(BeatTime beats) const { - return (beats * _beat_rate); + // FIXME + + inline TimeStamp beats_to_seconds(TimeStamp beats) const { + //return (beats * _beat_rate); + throw; } - inline TickTime beats_to_ticks(BeatTime beats) const { - return static_cast<TickTime>(floor(beats_to_seconds(beats) / _tick_rate)); + inline TimeStamp beats_to_ticks(TimeStamp beats) const { + //return static_cast<TimeStamp>(floor(beats_to_seconds(beats) / _tick_rate)); + throw; } - inline Seconds ticks_to_seconds(TickTime ticks) const { - return (ticks * _tick_rate); + inline TimeStamp ticks_to_seconds(TimeStamp ticks) const { + //return (ticks * _tick_rate); + throw; } - inline BeatTime ticks_to_beats(TickTime ticks) const { - return ticks_to_seconds(ticks) / _beat_rate; + inline TimeStamp ticks_to_beats(TimeStamp ticks) const { + //return ticks_to_seconds(ticks) / _beat_rate; + throw; } /** Start of current sub-cycle in ticks */ - inline TickTime start_ticks() const { return _start_ticks; } + inline TimeStamp start_ticks() const { return _start_ticks; } /** Length of current sub-cycle in ticks */ - inline TickCount length_ticks() const { return _length_ticks; } + inline TimeDuration length_ticks() const { return _length_ticks; } /** Start of current sub-cycle in beats */ - inline BeatTime start_beats() const { return _start_beats; } + inline TimeStamp start_beats() const { return _start_beats; } /** Length of current sub-cycle in beats */ - inline BeatCount length_beats() const { return _length_beats; } + inline TimeDuration length_beats() const { return _length_beats; } /** Set the offset between real-time and timeslice-time. */ - inline void set_offset(TickCount offset) { _offset_ticks = offset; } + inline void set_offset(TimeDuration offset) { _offset_ticks = offset; } /** Offset relative to external (e.g Jack) time */ - inline TickCount offset_ticks() const { return _offset_ticks; } + inline TimeDuration offset_ticks() const { return _offset_ticks; } private: @@ -136,12 +145,12 @@ private: double _beat_rate; ///< Beat rate in Hz // Current time - TickTime _start_ticks; ///< Current window start in ticks - TickCount _length_ticks; ///< Current window length in ticks - BeatTime _start_beats; ///< Current window start in beats - BeatCount _length_beats; ///< Current window length in beats + TimeStamp _start_ticks; ///< Current window start in ticks + TimeDuration _length_ticks; ///< Current window length in ticks + TimeStamp _start_beats; ///< Current window start in beats + TimeDuration _length_beats; ///< Current window length in beats - TickCount _offset_ticks; ///< Offset to global time (ie Jack sub-cycle offset) + TimeDuration _offset_ticks; ///< Offset to global time (ie Jack sub-cycle offset) }; diff --git a/raul/TimeStamp.hpp b/raul/TimeStamp.hpp new file mode 100644 index 0000000..6cbf807 --- /dev/null +++ b/raul/TimeStamp.hpp @@ -0,0 +1,240 @@ +/* This file is part of Raul. + * Copyright (C) 2008 Dave Robillard <http://drobilla.net> + * + * Raul 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. + * + * Raul 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 RAUL_TIME_STAMP_HPP +#define RAUL_TIME_STAMP_HPP + +#include <limits> +#include <stdint.h> +#include <math.h> +#include <cassert> +#include <iostream> + +namespace Raul { + + +/** A type of time stamp + */ +class TimeUnit { +public: + enum Type { + FRAMES, + BEATS, + SECONDS + }; + + /** @a ppt (parts per tick) is the sample rate for FRAMES, + * PPQN for BEATS, and ignored for SECONDS. + */ + inline TimeUnit(Type type, uint32_t ppt) { + _type = type; + _ppt = ppt; + } + + static inline TimeUnit frames(uint32_t srate) { return TimeUnit(FRAMES, srate); } + static inline TimeUnit beats(uint32_t ppqn) { return TimeUnit(BEATS, ppqn); } + static inline TimeUnit seconds() { return TimeUnit(BEATS, std::numeric_limits<uint32_t>::max()); } + + inline Type type() const { return _type; } + inline uint32_t ppt() const { return _ppt; } + + inline bool operator==(const TimeUnit& rhs) const { + return (_type == rhs._type && _ppt == rhs._ppt); + } + + inline bool operator!=(const TimeUnit& rhs) const { + return (_type != rhs._type || _ppt != rhs._ppt); + } + +private: + Type _type; + uint32_t _ppt; +}; + + +/** A real-time time stamp (possible units: frame, absolute (s), or beat). + * + * This is a uint32_t:uint32_t fixed point representation, capable of + * sub-sample accurate frame time, beat time (at any remotely sane + * tempo and sample rate), and absolute time. The absolute time (seconds) + * is compatible with standard OSC/NTP time stamps. + * + * \ingroup raul + */ +class TimeStamp { +public: + + inline TimeStamp(TimeUnit unit, uint32_t ticks=0, uint32_t subticks=0) + : _ticks(ticks) + , _subticks(subticks) + , _unit(unit) + {} + + inline TimeStamp(TimeUnit unit, double dec) + : _ticks((uint32_t)floor(dec)) + , _subticks((dec - floor(dec)) * unit.ppt()) + , _unit(unit) + { + assert(dec >= 0); + assert(dec <= std::numeric_limits<uint32_t>::max()); + } + + inline TimeUnit unit() const { return _unit; } + inline uint32_t ticks() const { return _ticks; } + inline uint32_t subticks() const { return _subticks; } + + inline double to_double() const { + return _ticks + (_subticks / (double)_unit.ppt()); + } + + inline bool is_zero() const { + return _ticks == 0 && _subticks == 0; + } + + inline TimeStamp& operator=(const TimeStamp& rhs) { + assert(_unit == rhs._unit); + _ticks = rhs._ticks; + _subticks = rhs._subticks; + //_unit = rhs._unit; + return *this; + } + + inline TimeStamp& operator=(uint32_t ticks) { + _ticks = ticks; + _subticks = 0; + return *this; + } + + inline bool operator==(const TimeStamp& rhs) const { + return _ticks == rhs._ticks + && _subticks == rhs._subticks + && _unit == rhs._unit; + } + + inline bool operator!=(const TimeStamp& rhs) const { + return ! operator==(rhs); + } + + inline bool operator<(const TimeStamp& rhs) const { + assert(_unit == rhs._unit); + return (_ticks < rhs._ticks + || (_ticks == rhs._ticks && _subticks < rhs._subticks)); + } + + inline bool operator<=(const TimeStamp& rhs) const { + assert(_unit == rhs._unit); + return (_ticks <= rhs._ticks + || (_ticks == rhs._ticks && _subticks <= rhs._subticks)); + } + + inline bool operator>=(const TimeStamp& rhs) const { + return ! (rhs < *this); + } + + inline bool operator>(const TimeStamp& rhs) const { + return ! (rhs <= *this); + } + + inline TimeStamp& operator+=(const TimeStamp& rhs) { + assert(_unit == rhs._unit); + _ticks += rhs._ticks; + if (_subticks + rhs._subticks <= _unit.ppt()) { + _subticks += rhs._subticks; + } else if (rhs._subticks > 0) { + ++_ticks; + _subticks = rhs._subticks + _subticks - _unit.ppt(); + } + return *this; + } + + inline TimeStamp& operator-=(const TimeStamp& rhs) { + assert(_unit == rhs._unit); + assert(rhs <= *this); + _ticks -= rhs._ticks; + if (_subticks >= rhs._subticks) { + _subticks -= rhs._subticks; + } else if (rhs._subticks > 0) { + --_ticks; + _subticks = _unit.ppt() - (rhs._subticks - _subticks); + } + return *this; + } + + inline TimeStamp operator+(const TimeStamp& rhs) const { + assert(_unit == rhs._unit); + TimeStamp result = *this; + result += rhs; + return result; + } + + inline TimeStamp operator-(const TimeStamp& rhs) const { + assert(_unit == rhs._unit); + TimeStamp result = *this; + result -= rhs; + return result; + } + +private: + uint32_t _ticks; + uint32_t _subticks; + TimeUnit _unit; +}; + + +static inline std::ostream& +operator<<(std::ostream& os, const TimeStamp& t) +{ + os << t.ticks() << ":" << t.subticks(); + switch (t.unit().type()) { + case TimeUnit::FRAMES: + os << " frames"; + break; + case TimeUnit::BEATS: + os << " beats"; + break; + case TimeUnit::SECONDS: + os << " seconds"; + break; + } + return os; +} + + +class FrameStamp : public TimeStamp { +public: + inline FrameStamp(uint32_t rate, uint32_t ticks=0, uint32_t subticks=0) + : TimeStamp(TimeUnit(TimeUnit::FRAMES, rate), ticks, subticks) + {} +}; + + +class BeatStamp : public TimeStamp { +public: + inline BeatStamp(uint32_t ppqn, uint32_t ticks=0, uint32_t subticks=0) + : TimeStamp(TimeUnit(TimeUnit::BEATS, ppqn), ticks, subticks) + {} +}; + + +/** Same thing as TimeStamp really, but makes code clearer and enforces + * correct semantics via type safety */ +typedef TimeStamp TimeDuration; + + +} // namespace Raul + +#endif // RAUL_TIME_STAMP_HPP diff --git a/raul/types.hpp b/raul/types.hpp deleted file mode 100644 index fe98e38..0000000 --- a/raul/types.hpp +++ /dev/null @@ -1,35 +0,0 @@ -/* This file is part of Raul. - * Copyright (C) 2007 Dave Robillard <http://drobilla.net> - * - * Raul 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. - * - * Raul 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 RAUL_TYPES_HPP -#define RAUL_TYPES_HPP - -#include <stdint.h> - -namespace Raul { - -typedef uint32_t TickTime; ///< absolute time in ticks -typedef uint32_t TickCount; ///< offset in ticks -typedef double BeatTime; -typedef double BeatCount; -typedef double Seconds; - -typedef unsigned char Byte; - -} - -#endif // RAUL_TYPES_HPP diff --git a/src/SMFReader.cpp b/src/SMFReader.cpp index a27939d..c21058b 100644 --- a/src/SMFReader.cpp +++ b/src/SMFReader.cpp @@ -72,6 +72,7 @@ midi_event_size(unsigned char status) SMFReader::SMFReader() : _fd(NULL) + , _unit(TimeUnit::BEATS, 192) , _ppqn(0) , _track(0) , _track_size(0) @@ -126,6 +127,7 @@ SMFReader::open(const string& filename) uint16_t ppqn_be = 0; fread(&ppqn_be, 2, 1, _fd); _ppqn = GUINT16_FROM_BE(ppqn_be); + _unit = TimeUnit::beats(_ppqn); seek_to_track(1); @@ -198,10 +200,10 @@ SMFReader::seek_to_track(unsigned track) throw (std::logic_error) * set to the actual size of the event. */ int -SMFReader::read_event(size_t buf_len, - uint8_t* buf, - uint32_t* ev_size, - uint32_t* delta_time) throw (std::logic_error) +SMFReader::read_event(size_t buf_len, + uint8_t* buf, + uint32_t* ev_size, + TimeStamp* delta_time) throw (std::logic_error) { if (_track == 0) throw logic_error("Attempt to read from unopened SMF file"); diff --git a/src/SMFWriter.cpp b/src/SMFWriter.cpp index b141e30..4ea65b0 100644 --- a/src/SMFWriter.cpp +++ b/src/SMFWriter.cpp @@ -15,7 +15,8 @@ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <cstdio> +#include <stdint.h> +#include <cassert> #include <iostream> #include <glibmm/miscutils.h> #include <raul/SMFWriter.hpp> @@ -25,13 +26,23 @@ using namespace std; namespace Raul { -SMFWriter::SMFWriter(unsigned short ppqn) +/** Create a new SMF writer. + * + * @a unit must match the time stamp of ALL events passed to write, or + * terrible things will happen. + * + * *** NOTE: Only beat time is implemented currently. + */ +SMFWriter::SMFWriter(TimeUnit unit) : _fd(NULL) - , _ppqn(ppqn) - , _last_ev_time(0) + , _unit(unit) + , _start_time(unit, 0, 0) + , _last_ev_time(unit, 0, 0) , _track_size(0) , _header_size(0) { + if (unit.type() == TimeUnit::BEATS) + assert(unit.ppt() < std::numeric_limits<uint16_t>::max()); } @@ -49,8 +60,8 @@ SMFWriter::~SMFWriter() * to write_event will have this value subtracted before writing). */ bool -SMFWriter::start(const string& filename, - Raul::BeatTime start_time) throw (logic_error) +SMFWriter::start(const string& filename, + Raul::TimeStamp start_time) throw (logic_error) { if (_fd) throw logic_error("Attempt to start new write while write in progress."); @@ -79,7 +90,7 @@ SMFWriter::start(const string& filename, * successive calls to this method. */ void -SMFWriter::write_event(Raul::BeatTime time, +SMFWriter::write_event(Raul::TimeStamp time, size_t ev_size, const unsigned char* ev) throw (logic_error) { @@ -87,27 +98,31 @@ SMFWriter::write_event(Raul::BeatTime time, throw logic_error("Event time is before file start time"); else if (time < _last_ev_time) throw logic_error("Event time not monotonically increasing"); + else if (time.unit() != _unit) + throw logic_error("Event has unexpected time unit"); - time -= _start_time; + Raul::TimeStamp delta_time = time; + delta_time -= _start_time; fseek(_fd, 0, SEEK_END); - double delta_time = (time - _last_ev_time) / (double)_ppqn; + uint64_t delta_ticks = delta_time.ticks() * _unit.ppt() + delta_time.subticks(); size_t stamp_size = 0; /* If delta time is too long (i.e. overflows), write out empty * "proprietary" events to reach the desired time. * Any SMF reading application should interpret this correctly * (by accumulating the delta time and ignoring the event) */ - while (delta_time > VAR_LEN_MAX) { + while (delta_ticks > VAR_LEN_MAX) { static unsigned char null_event[] = { 0xFF, 0x7F, 0x0 }; stamp_size = write_var_len(VAR_LEN_MAX); fwrite(null_event, 1, 3, _fd); - delta_time -= VAR_LEN_MAX; + _track_size += stamp_size + 3; + delta_ticks -= VAR_LEN_MAX; } - assert(delta_time < VAR_LEN_MAX); - stamp_size = write_var_len((uint32_t)delta_time); + assert(delta_ticks <= VAR_LEN_MAX); + stamp_size = write_var_len((uint32_t)delta_ticks); fwrite(ev, 1, ev_size, _fd); _last_ev_time = time; @@ -142,9 +157,9 @@ SMFWriter::write_header() assert(_fd); - const uint16_t type = GUINT16_TO_BE(0); // SMF Type 0 (single track) - const uint16_t ntracks = GUINT16_TO_BE(1); // Number of tracks (always 1 for Type 0) - const uint16_t division = GUINT16_TO_BE(_ppqn); // Number of tracks (always 1 for Type 0) + const uint16_t type = GUINT16_TO_BE(0); // SMF Type 0 (single track) + const uint16_t ntracks = GUINT16_TO_BE(1); // Number of tracks (always 1 for Type 0) + const uint16_t division = GUINT16_TO_BE(_unit.ppt()); // PPQN char data[6]; memcpy(data, &type, 2); diff --git a/src/TimeSlice.cpp b/src/TimeSlice.cpp index e983a33..bfc2f66 100644 --- a/src/TimeSlice.cpp +++ b/src/TimeSlice.cpp @@ -20,7 +20,7 @@ namespace Raul { -TimeSlice::TimeSlice(double tick_rate, double bpm) +TimeSlice::TimeSlice(uint32_t tick_rate, double bpm) : _tick_rate(tick_rate) , _beat_rate(60.0/bpm) , _start_ticks(0) diff --git a/tests/midi_ringbuffer_test.cpp b/tests/midi_ringbuffer_test.cpp index 706aef1..2bf138c 100644 --- a/tests/midi_ringbuffer_test.cpp +++ b/tests/midi_ringbuffer_test.cpp @@ -1,21 +1,22 @@ +#include "raul/TimeStamp.hpp" +#include "raul/EventRingBuffer.hpp" #include <iostream> -#include "raul/StampedChunkRingBuffer.hpp" #include "raul/midi_names.h" using namespace std; using namespace Raul; void -read_write_test(StampedChunkRingBuffer& rb, unsigned offset) +read_write_test(EventRingBuffer& rb, unsigned offset) { - TickTime t; + TimeStamp t(TimeUnit(TimeUnit::FRAMES, 48000), 0, 0); size_t size; unsigned char buf[5]; snprintf((char*)buf, 5, "%d", offset); size = strlen((char*)buf); - size_t written = rb.write(offset, size, buf); + size_t written = rb.write(t, size, buf); assert(written == size); for (size_t i=0; i < 4; ++i) @@ -31,7 +32,7 @@ read_write_test(StampedChunkRingBuffer& rb, unsigned offset) int main() { - StampedChunkRingBuffer rb(32); + EventRingBuffer rb(32); for (size_t i=0; i < 9999; ++i) read_write_test(rb, i); diff --git a/tests/quantize_test.cpp b/tests/quantize_test.cpp index f79d279..b4c8725 100644 --- a/tests/quantize_test.cpp +++ b/tests/quantize_test.cpp @@ -1,5 +1,5 @@ -#include <iostream> #include <raul/Quantizer.hpp> +#include <iostream> using namespace std; using namespace Raul; @@ -8,16 +8,19 @@ using namespace Raul; int main() { - double q = 0; - double beats = 0; + double in = 0; cout << "Quantization: "; - cin >> q; + cin >> in; cout << endl; + + TimeStamp q(TimeUnit(TimeUnit::BEATS, 19200), in); while (true) { cout << "Beats: "; - cin >> beats; + cin >> in; + + TimeStamp beats(TimeUnit(TimeUnit::BEATS, 19200), in); cout << "Quantized: "; cout << Quantizer::quantize(q, beats) << endl << endl; diff --git a/tests/smf_test.cpp b/tests/smf_test.cpp index 75db43a..a123a61 100644 --- a/tests/smf_test.cpp +++ b/tests/smf_test.cpp @@ -14,10 +14,10 @@ main(int argc, char** argv) if (argc < 2) { filename = "./test.mid"; - SMFWriter writer(32768); - writer.start(string(filename)); + SMFWriter writer(TimeUnit(TimeUnit::BEATS, 19200)); + writer.start(string(filename), TimeStamp(writer.unit(), 0, 0)); writer.finish(); - cout << "Wrote " << filename << " with PPQN = " << writer.ppqn() << endl; + cout << "Wrote " << filename << " with PPQN = " << writer.unit().ppt() << endl; } else { filename = argv[1]; @@ -44,7 +44,7 @@ main(int argc, char** argv) unsigned char buf[4]; uint32_t ev_size; - uint32_t ev_delta_time; + TimeStamp ev_delta_time(reader.unit()); while (reader.read_event(4, buf, &ev_size, &ev_delta_time) >= 0) { cout << "Event, size = " << ev_size << ", time = " << ev_delta_time; diff --git a/tests/time_test.cpp b/tests/time_test.cpp index c5c89b7..2f87f87 100644 --- a/tests/time_test.cpp +++ b/tests/time_test.cpp @@ -1,5 +1,6 @@ -#include <iostream> +#include <raul/TimeStamp.hpp> #include <raul/TimeSlice.hpp> +#include <iostream> using namespace std; using namespace Raul; @@ -8,19 +9,22 @@ using namespace Raul; int main() { - TimeSlice ts(1/48000.0, 120); - - double in_double = 0; + TimeUnit unit(TimeUnit::BEATS, 19200); + TimeSlice ts(48000, 120); + double in_double; cout << "Beats: "; cin >> in_double; + + TimeStamp t(unit, (uint32_t)in_double, + (uint32_t)((in_double - (uint32_t)in_double) * unit.ppt())); cout << "\tSeconds: "; - cout << ts.beats_to_seconds(in_double); + cout << ts.beats_to_seconds(t); cout << endl; cout << "\tTicks: "; - cout << ts.beats_to_ticks(in_double); + cout << ts.beats_to_ticks(t); cout << endl; return 0; |