Remove pointless Byte typedef that didn't really match any other typedef in ardour...
[ardour.git] / libs / ardour / ardour / midi_ring_buffer.h
index 6c57c78c3ca9e135b28a697d97658a542b32416d..ff0be5c9978b5927168cdf9761a3628e3529f0a7 100644 (file)
@@ -75,7 +75,7 @@ public:
                
                if (w > r) {
                        return ((r - w + _size) % _size) - 1;
-               } else if(w < r) {
+               } else if (w < r) {
                        return (r - w) - 1;
                } else {
                        return _size - 1;
@@ -101,15 +101,17 @@ public:
 
        size_t read(size_t size, T* dst);
        bool   full_read(size_t size, T* dst);
+
+       bool   skip(size_t size);
        
        void   write(size_t size, const T* src);
 
 protected:
-       mutable gint _write_ptr;
-       mutable gint _read_ptr;
+       mutable int _write_ptr;
+       mutable int _read_ptr;
        
        size_t _size; ///< Size (capacity) in bytes
-       T*  _buf;  ///< size, event, size, event...
+       T*     _buf;  ///< size, event, size, event...
 };
 
 
@@ -130,7 +132,7 @@ MidiRingBufferBase<T>::peek(size_t size, T* dst)
                        : _size - priv_read_ptr;
        
        memcpy(dst, &_buf[priv_read_ptr], read_size);
-        
+
        return read_size;
 }
 
@@ -139,13 +141,15 @@ template<typename T>
 bool
 MidiRingBufferBase<T>::full_peek(size_t size, T* dst)
 {
-       if (read_space() < size)
+       if (read_space() < size) {
                return false;
+       }
 
        const size_t read_size = peek(size, dst);
        
-       if (read_size < size)
+       if (read_size < size) {
                peek(size - read_size, dst + read_size);
+       }
 
        return true;
 }
@@ -179,13 +183,31 @@ template<typename T>
 bool
 MidiRingBufferBase<T>::full_read(size_t size, T* dst)
 {
-       if (read_space() < size)
+       if (read_space() < size) {
                return false;
+       }
 
        const size_t read_size = read(size, dst);
        
-       if (read_size < size)
+       if (read_size < size) {
                read(size - read_size, dst + read_size);
+       }
+
+       return true;
+}
+
+
+template<typename T>
+bool
+MidiRingBufferBase<T>::skip(size_t size)
+{
+       if (read_space() < size) {
+               std::cerr << "WARNING: Attempt to skip past end of MIDI ring buffer" << std::endl;
+               return false;
+       }
+       
+       const size_t priv_read_ptr = g_atomic_int_get(&_read_ptr);
+       g_atomic_int_set(&_read_ptr, (priv_read_ptr + size) % _size);
 
        return true;
 }
@@ -199,14 +221,14 @@ MidiRingBufferBase<T>::write(size_t size, const T* src)
        
        if (priv_write_ptr + size <= _size) {
                memcpy(&_buf[priv_write_ptr], src, size);
-        g_atomic_int_set(&_write_ptr, (priv_write_ptr + size) % _size);
+               g_atomic_int_set(&_write_ptr, (priv_write_ptr + size) % _size);
        } else {
                const size_t this_size = _size - priv_write_ptr;
                assert(this_size < size);
                assert(priv_write_ptr + this_size <= _size);
                memcpy(&_buf[priv_write_ptr], src, this_size);
                memcpy(&_buf[0], src+this_size, size - this_size);
-        g_atomic_int_set(&_write_ptr, size - this_size);
+               g_atomic_int_set(&_write_ptr, size - this_size);
        }
 }
 
@@ -221,51 +243,134 @@ MidiRingBufferBase<T>::write(size_t size, const T* src)
  *
  * [timestamp][size][size bytes of raw MIDI][timestamp][size][etc..]
  */
-class MidiRingBuffer : public MidiRingBufferBase<Byte> {
+class MidiRingBuffer : public MidiRingBufferBase<uint8_t> {
 public:
-
        /** @param size Size in bytes.
         */
        MidiRingBuffer(size_t size)
-               : MidiRingBufferBase<Byte>(size)
+               : MidiRingBufferBase<uint8_t>(size), _channel_mask(0x0000FFFF)
        {}
 
-       size_t write(double time, size_t size, const Byte* buf);
-       bool   read(double* time, size_t* size, Byte* buf);
+       size_t write(double time, size_t size, const uint8_t* buf);
+       bool   read(double* time, size_t* size, uint8_t* buf);
+
+       bool   read_prefix(double* time, size_t* size);
+       bool   read_contents(size_t size, uint8_t* buf);
 
        size_t read(MidiBuffer& dst, nframes_t start, nframes_t end, nframes_t offset=0);
+       
+       /** Set the channel filtering mode.
+        * @param mask If mode is FilterChannels, each bit represents a midi channel:
+        *     bit 0 = channel 0, bit 1 = channel 1 etc. the read and write methods will only
+        *     process events whose channel bit is 1.
+        *     If mode is ForceChannel, mask is simply a channel number which all events will
+        *     be forced to while reading.
+        */
+       void set_channel_mode(ChannelMode mode, uint16_t mask) {
+               g_atomic_int_set(&_channel_mask, ((uint16_t)mode << 16) | mask);
+       }
+
+       ChannelMode get_channel_mode() const {
+               return static_cast<ChannelMode>((g_atomic_int_get(&_channel_mask) & 0xFFFF0000) >> 16);
+       }
+       
+       uint16_t get_channel_mask() const {
+               return static_cast<ChannelMode>((g_atomic_int_get(&_channel_mask) & 0x0000FFFF));
+       }
+       
+protected:
+       inline bool is_channel_event(uint8_t event_type_byte) {
+               // mask out channel information
+               event_type_byte &= 0xF0;
+               // midi channel events range from 0x80 to 0xE0
+               return (0x80 <= event_type_byte) && (event_type_byte <= 0xE0);
+       }
+       
+private:
+       volatile uint32_t _channel_mask; // 16 bits mode, 16 bits mask
 };
 
 
 inline bool
-MidiRingBuffer::read(double* time, size_t* size, Byte* buf)
+MidiRingBuffer::read(double* time, size_t* size, uint8_t* buf)
+{
+       bool success = MidiRingBufferBase<uint8_t>::full_read(sizeof(double), (uint8_t*)time);
+       
+       if (success) {
+               success = MidiRingBufferBase<uint8_t>::full_read(sizeof(size_t), (uint8_t*)size);
+       }
+       if (success) {
+               success = MidiRingBufferBase<uint8_t>::full_read(*size, buf);
+       }
+       
+       return success;
+}
+
+
+/** Read the time and size of an event.  This call MUST be immediately proceeded
+ * by a call to read_contents (or the read pointer will be garabage).
+ */
+inline bool
+MidiRingBuffer::read_prefix(double* time, size_t* size)
 {
-       bool success = MidiRingBufferBase<Byte>::full_read(sizeof(double), (Byte*)time);
-       if (success)
-               success = MidiRingBufferBase<Byte>::full_read(sizeof(size_t), (Byte*)size);
-       if (success)
-               success = MidiRingBufferBase<Byte>::full_read(*size, buf);
+       bool success = MidiRingBufferBase<uint8_t>::full_read(sizeof(double), (uint8_t*)time);
+       if (success) {
+               success = MidiRingBufferBase<uint8_t>::full_read(sizeof(size_t), (uint8_t*)size);
+       }
 
        return success;
 }
 
 
-inline size_t
-MidiRingBuffer::write(double time, size_t size, const Byte* buf)
+/** Read the contenst of an event.  This call MUST be immediately preceeded
+ * by a call to read_prefix (or the returned even will be garabage).
+ */
+inline bool
+MidiRingBuffer::read_contents(size_t size, uint8_t* buf)
 {
-       //printf("MRB - write %#X %d %d with time %lf\n",
-       //              buf[0], buf[1], buf[2], time);
+       return MidiRingBufferBase<uint8_t>::full_read(size, buf);
+}
 
+
+inline size_t
+MidiRingBuffer::write(double time, size_t size, const uint8_t* buf)
+{
+       /*fprintf(stderr, "MRB %p write (t = %f) ", this, time);
+       for (size_t i = 0; i < size; ++i)
+               fprintf(stderr, "%X", (char)buf[i]);
+       fprintf(stderr, "\n");*/
+       
        assert(size > 0);
+       
+       // Don't write event if it doesn't match channel filter
+       if (is_channel_event(buf[0]) && get_channel_mode() == FilterChannels) {
+               uint8_t channel = buf[0] & 0x0F;
+               if ( !(get_channel_mask() & (1L << channel)) ) {
+                       return 0;
+               }
+       }
 
        if (write_space() < (sizeof(double) + sizeof(size_t) + size)) {
                return 0;
        } else {
-               MidiRingBufferBase<Byte>::write(sizeof(double), (Byte*)&time);
-               MidiRingBufferBase<Byte>::write(sizeof(size_t), (Byte*)&size);
-               MidiRingBufferBase<Byte>::write(size, buf);
+               MidiRingBufferBase<uint8_t>::write(sizeof(double), (uint8_t*)&time);
+               MidiRingBufferBase<uint8_t>::write(sizeof(size_t), (uint8_t*)&size);
+               if (is_channel_event(buf[0]) && get_channel_mode() == ForceChannel) {
+                       assert(size == 2 || size == 3);
+                       uint8_t tmp_buf[3];
+                       // Force event to channel
+                       tmp_buf[0] = (buf[0] & 0xF0) | (get_channel_mask() & 0x0F);
+                       tmp_buf[1] = buf[1];
+                       if (size == 3) {
+                               tmp_buf[2] = buf[2];
+                       }
+                       MidiRingBufferBase<uint8_t>::write(size, tmp_buf);
+               } else {
+                       MidiRingBufferBase<uint8_t>::write(size, buf);
+               }
                return size;
        }
+
 }
 
 
@@ -280,42 +385,72 @@ MidiRingBuffer::read(MidiBuffer& dst, nframes_t start, nframes_t end, nframes_t
        if (read_space() == 0)
                return 0;
 
-       MidiEvent ev;
+       double   ev_time;
+       uint32_t ev_size;
 
        size_t count = 0;
 
-       //printf("MRB - read %u .. %u + %u\n", start, end, offset);
+       //printf("---- MRB read %u .. %u + %u\n", start, end, offset);
 
        while (read_space() > sizeof(double) + sizeof(size_t)) {
        
-               full_peek(sizeof(double), (Byte*)&ev.time);
+               full_peek(sizeof(double), (uint8_t*)&ev_time);
        
-               if (ev.time > end)
+               if (ev_time > end) {
                        break;
-
-               bool success = MidiRingBufferBase<Byte>::full_read(sizeof(double), (Byte*)&ev.time);
-               if (success)
-                       success = MidiRingBufferBase<Byte>::full_read(sizeof(size_t), (Byte*)&ev.size);
+               }
                
+               bool success = MidiRingBufferBase<uint8_t>::full_read(sizeof(double), (uint8_t*)&ev_time);
+               if (success) {
+                       success = MidiRingBufferBase<uint8_t>::full_read(sizeof(size_t), (uint8_t*)&ev_size);
+               }
+
                if (!success) {
                        std::cerr << "MRB: READ ERROR (time/size)" << std::endl;
                        continue;
                }
+               
+               uint8_t status;
+               success = full_peek(sizeof(uint8_t), &status);
+               assert(success); // If this failed, buffer is corrupt, all hope is lost
+               
+               // Ignore event if it doesn't match channel filter
+               if (is_channel_event(status) && get_channel_mode() == FilterChannels) {
+                       const uint8_t channel = status & 0x0F;
+                       if ( !(get_channel_mask() & (1L << channel)) ) {
+                               skip(ev_size); // Advance read pointer to next event
+                               continue;
+                       }
+               }
+
+               if (ev_time >= start) {
 
-               if (ev.time >= start) {
-                       ev.time -= start;
-                       Byte* write_loc = dst.reserve(ev.time, ev.size);
-                       success = MidiRingBufferBase<Byte>::full_read(ev.size, write_loc);
+                       /*std::cerr << "MRB " << this << " - Reading event, time = "
+                               << ev_time << " - " << start << " => " << ev_time - start
+                               << ", size = " << ev_size << std::endl;*/
+                       
+                       ev_time -= start;
+                       
+                       uint8_t* write_loc = dst.reserve(ev_time, ev_size);
+                       if (write_loc == NULL) {
+                               std::cerr << "MRB: Unable to reserve space in buffer, event skipped";
+                               continue;
+                       }
+                       
+                       success = MidiRingBufferBase<uint8_t>::full_read(ev_size, write_loc);
                
                        if (success) {
+                               if (is_channel_event(status) && get_channel_mode() == ForceChannel) {
+                                       write_loc[0] = (write_loc[0] & 0xF0) | (get_channel_mask() & 0x0F);
+                               }
                                ++count;
-                               //printf("MRB - read event at time %lf\n", ev.time);
+                               //printf("MRB - read event at time %lf\n", ev_time);
                        } else {
                                std::cerr << "MRB: READ ERROR (data)" << std::endl;
                        }
                        
                } else {
-                       printf("MRB - SKIPPING EVENT (with time %f)\n", ev.time);
+                       printf("MRB (start %u) - Skipping event at (too early) time %f\n", start, ev_time);
                }
        }