andrewm@0
|
1 /*
|
andrewm@0
|
2 TouchKeys: multi-touch musical keyboard control software
|
andrewm@0
|
3 Copyright (c) 2013 Andrew McPherson
|
andrewm@0
|
4
|
andrewm@0
|
5 This program is free software: you can redistribute it and/or modify
|
andrewm@0
|
6 it under the terms of the GNU General Public License as published by
|
andrewm@0
|
7 the Free Software Foundation, either version 3 of the License, or
|
andrewm@0
|
8 (at your option) any later version.
|
andrewm@0
|
9
|
andrewm@0
|
10 This program is distributed in the hope that it will be useful,
|
andrewm@0
|
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
|
andrewm@0
|
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
andrewm@0
|
13 GNU General Public License for more details.
|
andrewm@0
|
14
|
andrewm@0
|
15 You should have received a copy of the GNU General Public License
|
andrewm@0
|
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
|
andrewm@0
|
17
|
andrewm@0
|
18 =====================================================================
|
andrewm@0
|
19
|
andrewm@0
|
20 LogPlayback.cpp: basic functions for playing back a recorded TouchKeys log.
|
andrewm@0
|
21 */
|
andrewm@0
|
22
|
andrewm@0
|
23 #include "LogPlayback.h"
|
andrewm@0
|
24
|
andrewm@0
|
25 LogPlayback::LogPlayback(PianoKeyboard& keyboard, MidiInputController& midi)
|
andrewm@0
|
26 : keyboard_(keyboard), midiInputController_(midi), open_(false), playing_(false), paused_(false),
|
andrewm@0
|
27 usingTouch_(false), usingMidi_(false), playbackRate_(1.0),
|
andrewm@0
|
28 nextTouchMidiNote_(0), nextTouchTimestamp_(0), nextMidiTimestamp_(0),
|
andrewm@0
|
29 lastMidiTimestamp_(0), timestampOffset_(0)
|
andrewm@0
|
30 {
|
andrewm@0
|
31 // Create a statically bound call to the performMapping() method that
|
andrewm@0
|
32 // we use each time we schedule a new mapping
|
andrewm@0
|
33 touchAction_ = boost::bind(&LogPlayback::nextTouchEvent, this);
|
andrewm@0
|
34 midiAction_ = boost::bind(&LogPlayback::nextMidiEvent, this);
|
andrewm@0
|
35 }
|
andrewm@0
|
36
|
andrewm@0
|
37 LogPlayback::~LogPlayback()
|
andrewm@0
|
38 {
|
andrewm@0
|
39 if(open_)
|
andrewm@0
|
40 closeLogFiles();
|
andrewm@0
|
41 }
|
andrewm@0
|
42
|
andrewm@0
|
43 // File management. Open a touch and/or MIDI file. Returns true on success.
|
andrewm@0
|
44 // Pass a blank string to either one of the paths to not use that form of data capture
|
andrewm@0
|
45 bool LogPlayback::openLogFiles(string const& touchPath, string const& midiPath) {
|
andrewm@0
|
46 touchLog_.open (touchPath.c_str(), ios::in | ios::binary);
|
andrewm@0
|
47 midiLog_.open (midiPath.c_str(), ios::in | ios::binary);
|
andrewm@0
|
48
|
andrewm@0
|
49 usingTouch_ = touchLog_.is_open();
|
andrewm@0
|
50 usingMidi_ = midiLog_.is_open();
|
andrewm@0
|
51
|
andrewm@0
|
52 // Check for bad file paths
|
andrewm@0
|
53 if(!usingTouch_ && touchPath != "")
|
andrewm@0
|
54 return false;
|
andrewm@0
|
55 if(!usingMidi_ && midiPath != "")
|
andrewm@0
|
56 return false;
|
andrewm@0
|
57 if(!usingTouch_ && !usingMidi_)
|
andrewm@0
|
58 return false;
|
andrewm@0
|
59
|
andrewm@0
|
60 // Set defaults
|
andrewm@0
|
61 open_ = true;
|
andrewm@0
|
62 playing_ = paused_ = false;
|
andrewm@0
|
63 playbackRate_ = 1.0;
|
andrewm@0
|
64 return true;
|
andrewm@0
|
65 }
|
andrewm@0
|
66
|
andrewm@0
|
67 // Close the current files
|
andrewm@0
|
68 void LogPlayback::closeLogFiles() {
|
andrewm@0
|
69 if(!open_)
|
andrewm@0
|
70 return;
|
andrewm@0
|
71 if(playing_)
|
andrewm@0
|
72 stopPlayback();
|
andrewm@0
|
73 touchLog_.close();
|
andrewm@0
|
74 midiLog_.close();
|
andrewm@0
|
75 open_ = playing_ = paused_ = false;
|
andrewm@0
|
76 }
|
andrewm@0
|
77
|
andrewm@0
|
78 // Start, stop, pause, resume
|
andrewm@0
|
79 void LogPlayback::startPlayback(timestamp_type startingTimestamp) {
|
andrewm@0
|
80 if(!open_)
|
andrewm@0
|
81 return;
|
andrewm@0
|
82
|
andrewm@0
|
83 // Start the playback scheduler thread
|
andrewm@0
|
84 playbackScheduler_.start(0);
|
andrewm@0
|
85
|
andrewm@0
|
86 timestamp_type firstTouchTimestamp, firstMidiTimestamp;
|
andrewm@0
|
87
|
andrewm@0
|
88 // Register actions on the scheduler thread
|
andrewm@0
|
89 if(usingTouch_) {
|
andrewm@0
|
90 readNextTouchFrame();
|
andrewm@0
|
91 firstTouchTimestamp = nextTouchTimestamp_;
|
andrewm@0
|
92 timestampOffset_ = playbackScheduler_.currentTimestamp() - firstTouchTimestamp;
|
andrewm@0
|
93 }
|
andrewm@0
|
94 if(usingMidi_) {
|
andrewm@0
|
95 readNextMidiFrame();
|
andrewm@0
|
96 firstMidiTimestamp = nextMidiTimestamp_;
|
andrewm@0
|
97 lastMidiTimestamp_ = nextMidiTimestamp_; // First timestamp difference is 0
|
andrewm@0
|
98
|
andrewm@0
|
99 // Timestamp offset is to first MIDI event, unless there's an earlier touch event
|
andrewm@0
|
100 if(!(usingTouch_ && (firstTouchTimestamp < firstMidiTimestamp)))
|
andrewm@0
|
101 timestampOffset_ = playbackScheduler_.currentTimestamp() - firstMidiTimestamp;
|
andrewm@0
|
102 }
|
andrewm@0
|
103
|
andrewm@53
|
104 cout << "Touch " << firstTouchTimestamp << " MIDI " << firstMidiTimestamp << " offset " << timestampOffset_ << endl;
|
andrewm@53
|
105
|
andrewm@0
|
106 playing_ = true;
|
andrewm@0
|
107 paused_ = false;
|
andrewm@0
|
108
|
andrewm@0
|
109 if(usingTouch_)
|
andrewm@0
|
110 playbackScheduler_.schedule(this, touchAction_, playbackScheduler_.currentTimestamp());
|
andrewm@0
|
111 if(usingMidi_)
|
andrewm@0
|
112 playbackScheduler_.schedule(this, midiAction_, playbackScheduler_.currentTimestamp());
|
andrewm@0
|
113 }
|
andrewm@0
|
114
|
andrewm@0
|
115 void LogPlayback::stopPlayback() {
|
andrewm@0
|
116 playing_ = paused_ = false;
|
andrewm@0
|
117
|
andrewm@0
|
118 // Stop the playback scheduler thread
|
andrewm@0
|
119 playbackScheduler_.stop();
|
andrewm@0
|
120 playbackScheduler_.unschedule(this);
|
andrewm@0
|
121 }
|
andrewm@0
|
122
|
andrewm@0
|
123 // Pause a currently playing file. Save the pause time so the offset
|
andrewm@0
|
124 // can be recalculated when it resumes
|
andrewm@0
|
125 void LogPlayback::pausePlayback() {
|
andrewm@0
|
126 if(open_ && playing_) {
|
andrewm@0
|
127 playbackScheduler_.unschedule(this);
|
andrewm@0
|
128
|
andrewm@0
|
129 // TODO: consider thread safety: what happens if this comes during one of the scheduled calls?
|
andrewm@0
|
130
|
andrewm@0
|
131 paused_ = true;
|
andrewm@0
|
132 pauseTimestamp_ = playbackScheduler_.currentTimestamp();
|
andrewm@0
|
133 }
|
andrewm@0
|
134 }
|
andrewm@0
|
135
|
andrewm@0
|
136 // Resume playback after a pause
|
andrewm@0
|
137 void LogPlayback::resumePlayback() {
|
andrewm@0
|
138 if(paused_) {
|
andrewm@0
|
139 paused_ = false;
|
andrewm@0
|
140 timestamp_type resumeTimestamp = playbackScheduler_.currentTimestamp();
|
andrewm@0
|
141
|
andrewm@0
|
142 // Update the timestamp offset
|
andrewm@0
|
143 timestampOffset_ += resumeTimestamp - pauseTimestamp_;
|
andrewm@0
|
144
|
andrewm@0
|
145 // Reschedule calls
|
andrewm@0
|
146 if(usingTouch_)
|
andrewm@0
|
147 playbackScheduler_.schedule(this, touchAction_, nextTouchTimestamp_ + timestampOffset_);
|
andrewm@0
|
148 if(usingMidi_)
|
andrewm@0
|
149 playbackScheduler_.schedule(this, touchAction_, nextMidiTimestamp_ + timestampOffset_);
|
andrewm@0
|
150 }
|
andrewm@0
|
151 }
|
andrewm@0
|
152
|
andrewm@0
|
153 // Seek to a timestamp in the file
|
andrewm@0
|
154 void LogPlayback::seekPlayback(timestamp_type newTimestamp) {
|
andrewm@0
|
155 // Advance through the file until we reach the indicated timestamp
|
andrewm@0
|
156
|
andrewm@0
|
157 if(!playing_ || !open_)
|
andrewm@0
|
158 return;
|
andrewm@0
|
159
|
andrewm@0
|
160 // Remove any future actions while we perform the seek
|
andrewm@0
|
161 playbackScheduler_.unschedule(this);
|
andrewm@0
|
162 //timestamp_diff_type offset = 0;
|
andrewm@0
|
163 timestamp_type firstUpcomingTimestamp = 0;
|
andrewm@0
|
164
|
andrewm@0
|
165 if(usingTouch_) {
|
andrewm@0
|
166 //timestamp_type lastTimestamp = nextTouchTimestamp_;
|
andrewm@0
|
167
|
andrewm@0
|
168 // TODO: this assumes the seek is moving forward
|
andrewm@0
|
169 while(nextTouchTimestamp_ <= newTimestamp) {
|
andrewm@0
|
170 if(!readNextTouchFrame()) { // EOF or error
|
andrewm@0
|
171 usingTouch_ = false;
|
andrewm@0
|
172 if(!usingMidi_)
|
andrewm@0
|
173 playing_ = paused_ = false;
|
andrewm@0
|
174 break;
|
andrewm@0
|
175 }
|
andrewm@0
|
176 }
|
andrewm@0
|
177
|
andrewm@0
|
178 // Now we have the first event scheduled after the seek location
|
andrewm@0
|
179 // Update timestamp offset to continue playback from here.
|
andrewm@0
|
180
|
andrewm@0
|
181 //offset = nextTouchTimestamp_ - lastTimestamp;
|
andrewm@0
|
182 firstUpcomingTimestamp = nextTouchTimestamp_;
|
andrewm@0
|
183 }
|
andrewm@0
|
184 if(usingMidi_) {
|
andrewm@0
|
185 //timestamp_type lastTimestamp = nextMidiTimestamp_;
|
andrewm@0
|
186
|
andrewm@0
|
187 // TODO: this assumes the seek is moving forward
|
andrewm@0
|
188 while(nextMidiTimestamp_ <= newTimestamp) {
|
andrewm@0
|
189 if(!readNextMidiFrame()) { // EOF or error
|
andrewm@0
|
190 usingMidi_ = false;
|
andrewm@0
|
191 if(!usingTouch_)
|
andrewm@0
|
192 playing_ = paused_ = false;
|
andrewm@0
|
193 break;
|
andrewm@0
|
194 }
|
andrewm@0
|
195 }
|
andrewm@0
|
196
|
andrewm@0
|
197 // Now we have the first event scheduled after the seek location
|
andrewm@0
|
198 // Update timestamp offset to continue playback from here.
|
andrewm@0
|
199 // Use whichever event came first
|
andrewm@0
|
200
|
andrewm@0
|
201 //if(!(usingTouch_ && (nextMidiTimestamp_ - lastTimestamp) > offset))
|
andrewm@0
|
202 // offset = (nextMidiTimestamp_ - lastTimestamp);
|
andrewm@0
|
203 if(!usingTouch_ || nextMidiTimestamp_ < nextTouchTimestamp_);
|
andrewm@0
|
204 firstUpcomingTimestamp = nextMidiTimestamp_;
|
andrewm@0
|
205 }
|
andrewm@0
|
206
|
andrewm@0
|
207 // Update the timestamp offset
|
andrewm@0
|
208 timestampOffset_ = playbackScheduler_.currentTimestamp() - firstUpcomingTimestamp;
|
andrewm@0
|
209
|
andrewm@0
|
210 if(usingTouch_)
|
andrewm@0
|
211 playbackScheduler_.schedule(this, touchAction_, nextTouchTimestamp_ + timestampOffset_);
|
andrewm@0
|
212 if(usingMidi_)
|
andrewm@0
|
213 playbackScheduler_.schedule(this, midiAction_, nextMidiTimestamp_ + timestampOffset_);
|
andrewm@0
|
214 }
|
andrewm@0
|
215
|
andrewm@0
|
216 // Change the playback rate (1.0 being the standard speed)
|
andrewm@0
|
217 void LogPlayback::changePlaybackRate(float rate) {
|
andrewm@0
|
218 playbackRate_ = rate;
|
andrewm@0
|
219 }
|
andrewm@0
|
220
|
andrewm@0
|
221 // Events the scheduler calls when the right time elapses. Find the
|
andrewm@0
|
222 // next touch or MIDI event and play it back
|
andrewm@0
|
223 timestamp_type LogPlayback::nextTouchEvent() {
|
andrewm@0
|
224 if(!playing_ || !open_ || paused_)
|
andrewm@0
|
225 return 0;
|
andrewm@0
|
226
|
andrewm@0
|
227 // TODO: handle playback rate
|
andrewm@0
|
228
|
andrewm@0
|
229 // Play the most recent stored touch frame
|
andrewm@0
|
230 if(nextTouchMidiNote_ >= 0 && nextTouchMidiNote_ < 128) {
|
andrewm@0
|
231 // Use PianoKeyboard timestamps for the messages we send since our scheduler
|
andrewm@0
|
232 // may have a different idea of time.
|
andrewm@0
|
233
|
andrewm@0
|
234 if(nextTouch_.count == 0) {
|
andrewm@0
|
235 if(keyboard_.key(nextTouchMidiNote_) != 0)
|
andrewm@0
|
236 keyboard_.key(nextTouchMidiNote_)->touchOff(keyboard_.schedulerCurrentTimestamp());
|
andrewm@0
|
237 /*
|
andrewm@0
|
238 // Send raw OSC message if enabled
|
andrewm@0
|
239 if(sendRawOscMessages_) {
|
andrewm@0
|
240 keyboard_.sendMessage("/touchkeys/raw-off", "iii",
|
andrewm@0
|
241 octave, key, frame,
|
andrewm@0
|
242 LO_ARGS_END );
|
andrewm@0
|
243 }
|
andrewm@0
|
244 */
|
andrewm@0
|
245 }
|
andrewm@0
|
246 else {
|
andrewm@0
|
247 if(keyboard_.key(nextTouchMidiNote_) != 0)
|
andrewm@0
|
248 keyboard_.key(nextTouchMidiNote_)->touchInsertFrame(nextTouch_,
|
andrewm@0
|
249 keyboard_.schedulerCurrentTimestamp());
|
andrewm@0
|
250 /*if(sendRawOscMessages_) {
|
andrewm@0
|
251 keyboard_.sendMessage("/touchkeys/raw", "iiifffffff",
|
andrewm@0
|
252 octave, key, frame,
|
andrewm@0
|
253 sliderPosition[0],
|
andrewm@0
|
254 sliderSize[0],
|
andrewm@0
|
255 sliderPosition[1],
|
andrewm@0
|
256 sliderSize[1],
|
andrewm@0
|
257 sliderPosition[2],
|
andrewm@0
|
258 sliderSize[2],
|
andrewm@0
|
259 sliderPositionH,
|
andrewm@0
|
260 LO_ARGS_END );
|
andrewm@0
|
261 }*/
|
andrewm@0
|
262 }
|
andrewm@0
|
263 }
|
andrewm@0
|
264
|
andrewm@0
|
265 bool newTouchFound = readNextTouchFrame();
|
andrewm@0
|
266
|
andrewm@0
|
267 // Go through next touch frames and send them as long as the timestamp is not in the future
|
andrewm@0
|
268 while(newTouchFound && (nextTouchTimestamp_ + timestampOffset_) <= playbackScheduler_.currentTimestamp()) {
|
andrewm@0
|
269 if(nextTouchMidiNote_ >= 0 && nextTouchMidiNote_ < 128) {
|
andrewm@0
|
270 // Use PianoKeyboard timestamps for the messages we send since our scheduler
|
andrewm@0
|
271 // may have a different idea of time.
|
andrewm@0
|
272
|
andrewm@0
|
273 if(keyboard_.key(nextTouchMidiNote_) != 0) {
|
andrewm@0
|
274 if(nextTouch_.count == 0)
|
andrewm@0
|
275 keyboard_.key(nextTouchMidiNote_)->touchOff(keyboard_.schedulerCurrentTimestamp());
|
andrewm@0
|
276 else
|
andrewm@0
|
277 keyboard_.key(nextTouchMidiNote_)->touchInsertFrame(nextTouch_,
|
andrewm@0
|
278 keyboard_.schedulerCurrentTimestamp());
|
andrewm@0
|
279 }
|
andrewm@0
|
280 }
|
andrewm@0
|
281
|
andrewm@53
|
282 newTouchFound = readNextTouchFrame();
|
andrewm@0
|
283 }
|
andrewm@0
|
284
|
andrewm@0
|
285 if(!newTouchFound) { // EOF or error
|
andrewm@0
|
286 usingTouch_ = false;
|
andrewm@0
|
287 if(!usingMidi_)
|
andrewm@0
|
288 playing_ = paused_ = false;
|
andrewm@0
|
289 return 0;
|
andrewm@0
|
290 }
|
andrewm@0
|
291 else // Return the timestamp of the next call
|
andrewm@0
|
292 return (nextTouchTimestamp_ + timestampOffset_);
|
andrewm@0
|
293 }
|
andrewm@0
|
294
|
andrewm@0
|
295 timestamp_type LogPlayback::nextMidiEvent() {
|
andrewm@0
|
296 if(!playing_ || !open_ || paused_)
|
andrewm@0
|
297 return 0;
|
andrewm@0
|
298
|
andrewm@0
|
299 // TODO: handle playback rate
|
andrewm@0
|
300
|
andrewm@0
|
301 // Play the most recent stored touch frame
|
andrewm@53
|
302 if(nextMidi_.size() >= 3) {
|
andrewm@53
|
303 if((nextMidi_[0] & 0xF0) == 0xD0) // channel aftertouch has 2 bytes
|
andrewm@53
|
304 midiInputController_.handleIncomingMidiMessage(0, MidiMessage(nextMidi_[0], nextMidi_[1]));
|
andrewm@53
|
305 else
|
andrewm@53
|
306 midiInputController_.handleIncomingMidiMessage(0, MidiMessage(nextMidi_[0], nextMidi_[1], nextMidi_[2]));
|
andrewm@53
|
307 }
|
andrewm@0
|
308 //midiInputController_.rtMidiCallback(nextMidiTimestamp_ - lastMidiTimestamp_, &nextMidi_, 0);
|
andrewm@0
|
309 lastMidiTimestamp_ = nextMidiTimestamp_;
|
andrewm@0
|
310
|
andrewm@0
|
311 bool newMidiEventFound = readNextMidiFrame();
|
andrewm@0
|
312
|
andrewm@0
|
313 // Go through next touch frames and send them as long as the timestamp is not in the future
|
andrewm@0
|
314 while(newMidiEventFound && (nextMidiTimestamp_ + timestampOffset_) <= playbackScheduler_.currentTimestamp()) {
|
andrewm@53
|
315 if(nextMidi_.size() >= 3) {
|
andrewm@53
|
316 if((nextMidi_[0] & 0xF0) == 0xD0) // channel aftertouch has 2 bytes
|
andrewm@53
|
317 midiInputController_.handleIncomingMidiMessage(0, MidiMessage(nextMidi_[0], nextMidi_[1]));
|
andrewm@53
|
318 else
|
andrewm@53
|
319 midiInputController_.handleIncomingMidiMessage(0, MidiMessage(nextMidi_[0], nextMidi_[1], nextMidi_[2]));
|
andrewm@53
|
320 }
|
andrewm@0
|
321 //midiInputController_.rtMidiCallback(nextMidiTimestamp_ - lastMidiTimestamp_, &nextMidi_, 0);
|
andrewm@0
|
322 lastMidiTimestamp_ = nextMidiTimestamp_;
|
andrewm@0
|
323
|
andrewm@0
|
324 readNextMidiFrame();
|
andrewm@0
|
325 }
|
andrewm@0
|
326
|
andrewm@0
|
327 if(!newMidiEventFound) { // EOF or error
|
andrewm@0
|
328 usingMidi_ = false;
|
andrewm@0
|
329 if(!usingTouch_)
|
andrewm@0
|
330 playing_ = paused_ = false;
|
andrewm@0
|
331 return 0;
|
andrewm@0
|
332 }
|
andrewm@0
|
333 else // Return the timestamp of the next call
|
andrewm@0
|
334 return (nextMidiTimestamp_ + timestampOffset_);
|
andrewm@0
|
335 }
|
andrewm@0
|
336
|
andrewm@0
|
337 // Retrieve the next key touch frame from the log file
|
andrewm@0
|
338 // Return true if a touch was found, false if EOF or an error occurred
|
andrewm@0
|
339 bool LogPlayback::readNextTouchFrame() {
|
andrewm@0
|
340 int frameCounter;
|
andrewm@0
|
341
|
andrewm@0
|
342 try {
|
andrewm@0
|
343 touchLog_.read((char *)&nextTouchTimestamp_, sizeof(timestamp_type));
|
andrewm@0
|
344 touchLog_.read((char *)&frameCounter, sizeof(int));
|
andrewm@0
|
345 touchLog_.read((char *)&nextTouchMidiNote_, sizeof(int));
|
andrewm@0
|
346 touchLog_.read((char *)&nextTouch_, sizeof(KeyTouchFrame));
|
andrewm@0
|
347 }
|
andrewm@0
|
348 catch(...) {
|
andrewm@0
|
349 cout << "error reading touch\n";
|
andrewm@0
|
350 return false;
|
andrewm@0
|
351 }
|
andrewm@0
|
352 if(touchLog_.eof()) {
|
andrewm@0
|
353 cout << "Touch log playback finished\n";
|
andrewm@0
|
354 return false;
|
andrewm@0
|
355 }
|
andrewm@0
|
356
|
andrewm@0
|
357 //cout << "read touch on key " << nextTouchMidiNote_ << " timestamp " << nextTouchTimestamp_ << endl;
|
andrewm@0
|
358
|
andrewm@0
|
359 // TODO: what about frameCounter
|
andrewm@0
|
360
|
andrewm@0
|
361 return true;
|
andrewm@0
|
362 }
|
andrewm@0
|
363
|
andrewm@0
|
364 // Retrieve the next MIDI frame from the log file
|
andrewm@0
|
365 // Return true if an event was found, false if EOF or an error occurred
|
andrewm@0
|
366 bool LogPlayback::readNextMidiFrame() {
|
andrewm@0
|
367 int midi0, midi1, midi2;
|
andrewm@0
|
368
|
andrewm@0
|
369 try {
|
andrewm@0
|
370 midiLog_.read((char*)&nextMidiTimestamp_, sizeof (timestamp_type));
|
andrewm@0
|
371 midiLog_.read((char*)&midi0, sizeof (int));
|
andrewm@0
|
372 midiLog_.read((char*)&midi1, sizeof (int));
|
andrewm@0
|
373 midiLog_.read((char*)&midi2, sizeof (int));
|
andrewm@0
|
374 }
|
andrewm@0
|
375 catch(...) {
|
andrewm@0
|
376 cout << "error reading MIDI\n";
|
andrewm@0
|
377 return false;
|
andrewm@0
|
378 }
|
andrewm@0
|
379
|
andrewm@0
|
380 if(midiLog_.eof()) {
|
andrewm@0
|
381 cout << "MIDI log playback finished\n";
|
andrewm@0
|
382 return false;
|
andrewm@0
|
383 }
|
andrewm@0
|
384
|
andrewm@0
|
385 nextMidi_.clear();
|
andrewm@0
|
386 nextMidi_.push_back((unsigned char)midi0);
|
andrewm@0
|
387 nextMidi_.push_back((unsigned char)midi1);
|
andrewm@0
|
388 nextMidi_.push_back((unsigned char)midi2);
|
andrewm@0
|
389
|
andrewm@0
|
390 //cout << "read MIDI data " << (int)midi0 << " " << (int)midi1 << " " << (int)midi2 << endl;
|
andrewm@0
|
391
|
andrewm@0
|
392 return true;
|
andrewm@0
|
393 } |