Merge branch 'patches' of https://github.com/jdekozak/ardour
[ardour.git] / libs / ardour / midi_port.cc
1 /*
2     Copyright (C) 2006 Paul Davis
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 #include <cassert>
20 #include <iostream>
21
22 #include "ardour/midi_port.h"
23 #include "ardour/data_type.h"
24 #include "ardour/audioengine.h"
25
26 using namespace ARDOUR;
27 using namespace std;
28
29 MidiPort::MidiPort (const std::string& name, Flags flags)
30         : Port (name, DataType::MIDI, flags)
31         , _has_been_mixed_down (false)
32         , _resolve_required (false)
33         , _input_active (true)
34 {
35         _buffer = new MidiBuffer (AudioEngine::instance()->raw_buffer_size (DataType::MIDI));
36 }
37
38 MidiPort::~MidiPort()
39 {
40         delete _buffer;
41 }
42
43 void
44 MidiPort::cycle_start (pframes_t nframes)
45 {
46         Port::cycle_start (nframes);
47
48         _buffer->clear ();
49
50         if (sends_output ()) {
51                 jack_midi_clear_buffer (jack_port_get_buffer (_jack_port, nframes));
52         }
53 }
54
55 MidiBuffer &
56 MidiPort::get_midi_buffer (pframes_t nframes)
57 {
58         if (_has_been_mixed_down) {
59                 return *_buffer;
60         }
61
62         if (receives_input ()) {
63
64                 if (_input_active) {
65
66                         void* jack_buffer = jack_port_get_buffer (_jack_port, nframes);
67                         const pframes_t event_count = jack_midi_get_event_count (jack_buffer);
68                         
69                         /* suck all relevant MIDI events from the JACK MIDI port buffer
70                            into our MidiBuffer
71                         */
72                         
73                         for (pframes_t i = 0; i < event_count; ++i) {
74                                 
75                                 jack_midi_event_t ev;
76                                 
77                                 jack_midi_event_get (&ev, jack_buffer, i);
78                                 
79                                 if (ev.buffer[0] == 0xfe) {
80                                         /* throw away active sensing */
81                                         continue;
82                                 }
83                                 
84                                 /* check that the event is in the acceptable time range */
85                                 
86                                 if ((ev.time >= (_global_port_buffer_offset + _port_buffer_offset)) &&
87                                     (ev.time < (_global_port_buffer_offset + _port_buffer_offset + nframes))) {
88                                         _buffer->push_back (ev);
89                                 } else {
90                                         cerr << "Dropping incoming MIDI at time " << ev.time << "; offset="
91                                              << _global_port_buffer_offset << " limit="
92                                              << (_global_port_buffer_offset + _port_buffer_offset + nframes) << "\n";
93                                 }
94                         } 
95
96                 } else {
97                         _buffer->silence (nframes);
98                 }
99
100         } else {
101                 _buffer->silence (nframes);
102         }
103
104         if (nframes) {
105                 _has_been_mixed_down = true;
106         }
107
108         return *_buffer;
109 }
110
111 void
112 MidiPort::cycle_end (pframes_t /*nframes*/)
113 {
114         _has_been_mixed_down = false;
115 }
116
117 void
118 MidiPort::cycle_split ()
119 {
120         _has_been_mixed_down = false;
121 }
122
123 void
124 MidiPort::resolve_notes (void* jack_buffer, MidiBuffer::TimeType when)
125 {
126         for (uint8_t channel = 0; channel <= 0xF; channel++) {
127
128                 uint8_t ev[3] = { ((uint8_t) (MIDI_CMD_CONTROL | channel)), MIDI_CTL_SUSTAIN, 0 };
129
130                 /* we need to send all notes off AND turn the
131                  * sustain/damper pedal off to handle synths
132                  * that prioritize sustain over AllNotesOff
133                  */
134
135                 if (jack_midi_event_write (jack_buffer, when, ev, 3) != 0) {
136                         cerr << "failed to deliver sustain-zero on channel " << channel << " on port " << name() << endl;
137                 }
138
139                 ev[1] = MIDI_CTL_ALL_NOTES_OFF;
140
141                 if (jack_midi_event_write (jack_buffer, 0, ev, 3) != 0) {
142                         cerr << "failed to deliver ALL NOTES OFF on channel " << channel << " on port " << name() << endl;
143                 }
144         }
145 }
146
147 void
148 MidiPort::flush_buffers (pframes_t nframes)
149 {
150         if (sends_output ()) {
151
152                 void* jack_buffer = jack_port_get_buffer (_jack_port, nframes);
153
154                 if (_resolve_required) {
155                         /* resolve all notes at the start of the buffer */
156                         resolve_notes (jack_buffer, 0);
157                         _resolve_required = false;
158                 }
159
160                 for (MidiBuffer::iterator i = _buffer->begin(); i != _buffer->end(); ++i) {
161
162                         const Evoral::MIDIEvent<MidiBuffer::TimeType> ev (*i, false);
163
164                         // event times are in frames, relative to cycle start
165
166                         assert (ev.time() < (nframes + _global_port_buffer_offset + _port_buffer_offset));
167
168                         if (ev.time() >= _global_port_buffer_offset + _port_buffer_offset) {
169                                 if (jack_midi_event_write (jack_buffer, (jack_nframes_t) ev.time(), ev.buffer(), ev.size()) != 0) {
170                                         cerr << "write failed, drop flushed note off on the floor, time "
171                                              << ev.time() << " > " << _global_port_buffer_offset + _port_buffer_offset << endl;
172                                 }
173                         } else {
174                                 cerr << "drop flushed event on the floor, time " << ev
175                                      << " to early for " << _global_port_buffer_offset 
176                                      << " + " << _port_buffer_offset << endl;
177                         }
178                 }
179         }
180 }
181
182 void
183 MidiPort::require_resolve ()
184 {
185         _resolve_required = true;
186 }
187
188 void
189 MidiPort::transport_stopped ()
190 {
191         _resolve_required = true;
192 }
193
194 void
195 MidiPort::realtime_locate ()
196 {
197         _resolve_required = true;
198 }
199
200 void
201 MidiPort::reset ()
202 {
203         Port::reset ();
204         delete _buffer;
205         _buffer = new MidiBuffer (AudioEngine::instance()->raw_buffer_size (DataType::MIDI));
206 }
207
208 void
209 MidiPort::set_input_active (bool yn)
210 {
211         _input_active = yn;
212 }