ALSA backend: tweak midi parser (fix start mid sequence)
[ardour.git] / libs / backends / alsa / alsa_rawmidi.h
1 /*
2  * Copyright (C) 2014 Robin Gareus <robin@gareus.org>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18
19 #ifndef __libbackend_alsa_rawmidi_h__
20 #define __libbackend_alsa_rawmidi_h__
21
22 #include <stdint.h>
23 #include <poll.h>
24 #include <pthread.h>
25
26 #include <alsa/asoundlib.h>
27
28 #include "pbd/ringbuffer.h"
29 #include "ardour/types.h"
30
31 namespace ARDOUR {
32
33 class AlsaRawMidiIO {
34 public:
35         AlsaRawMidiIO (const char *device, const bool input);
36         virtual ~AlsaRawMidiIO ();
37
38         int state (void) const { return _state; }
39         int start ();
40         int stop ();
41
42         void setup_timing (const size_t samples_per_period, const float samplerate);
43         void sync_time(uint64_t);
44
45         virtual void* main_process_thread () = 0;
46
47 protected:
48         pthread_t _main_thread;
49         pthread_mutex_t _notify_mutex;
50         pthread_cond_t _notify_ready;
51
52         int  _state;
53         bool  _running;
54
55         snd_rawmidi_t *_device;
56         int _npfds;
57         struct pollfd *_pfds;
58
59         double _sample_length_us;
60         double _period_length_us;
61         size_t _samples_per_period;
62         uint64_t _clock_monotonic;
63
64         struct MidiEventHeader {
65                 uint64_t time;
66                 size_t size;
67                 MidiEventHeader(const uint64_t t, const size_t s)
68                         : time(t)
69                         , size(s) {}
70         };
71
72         RingBuffer<uint8_t>* _rb;
73
74 private:
75         void init (const char *device_name, const bool input);
76
77 };
78
79 class AlsaRawMidiOut : public AlsaRawMidiIO
80 {
81 public:
82         AlsaRawMidiOut (const char *device);
83
84         void* main_process_thread ();
85         int send_event (const pframes_t, const uint8_t *, const size_t);
86 };
87
88 class AlsaRawMidiIn : public AlsaRawMidiIO
89 {
90 public:
91         AlsaRawMidiIn (const char *device);
92
93         void* main_process_thread ();
94
95         size_t recv_event (pframes_t &, uint8_t *, size_t &);
96
97 private:
98         int queue_event (const uint64_t, const uint8_t *, const size_t);
99         void parse_events (const uint64_t, const uint8_t *, const size_t);
100         bool process_byte (const uint64_t, const uint8_t);
101
102         void record_byte(uint8_t byte) {
103                 if (_total_bytes < sizeof(_parser_buffer)) {
104                         _parser_buffer[_total_bytes] = byte;
105                 } else {
106                         ++_unbuffered_bytes;
107                 }
108                 ++_total_bytes;
109         }
110
111         void prepare_byte_event(const uint64_t time, const uint8_t byte) {
112                 _parser_buffer[0] = byte;
113                 _event.prepare(time, 1);
114         }
115
116         bool prepare_buffered_event(const uint64_t time) {
117                 const bool result = !_unbuffered_bytes;
118                 if (result) {
119                         _event.prepare(time, _total_bytes);
120                 }
121                 _total_bytes = 0;
122                 _unbuffered_bytes = 0;
123                 if (_status_byte >= 0xf0) {
124                         _expected_bytes = 0;
125                         _status_byte = 0;
126                 }
127                 return result;
128         }
129
130         struct ParserEvent {
131                 uint64_t _time;
132                 size_t _size;
133                 bool _pending;
134                 ParserEvent (const uint64_t time, const size_t size)
135                         : _time(time)
136                         , _size(size)
137                         , _pending(false) {}
138
139                 void prepare(const uint64_t time, const size_t size) {
140                         _time = time;
141                         _size = size;
142                         _pending = true;
143                 }
144         } _event;
145
146         bool    _first_time;
147         size_t  _unbuffered_bytes;
148         size_t  _total_bytes;
149         size_t  _expected_bytes;
150         uint8_t _status_byte;
151         uint8_t _parser_buffer[1024];
152 };
153
154 } // namespace
155
156 #endif