CSL 5.2

- Main Page
- Related Pages
- Modules
- Namespaces
- Classes
- Files

- File List
- File Members

MIDIIOP.cpp
Go to the documentation of this file.
1 //
2 // MIDIIOP.cpp -- MIDI IO using PortMIDI
3 //
4 // See the copyright notice and acknowledgment of authors in the file COPYRIGHT
5 //
6
7 #include "MIDIIO.h"
8
9 //#define DEBUG_MIDIIO_READ
10 //#define DEBUG_MIDIIO_READ_INTERPRET
11
12 using namespace csl;
13
14 /******************************************************************************
15 CSL_MIDIMessage
16 ******************************************************************************/
17
18 CSL_MIDIMessage::CSL_MIDIMessage() : message(kNone), channel(0), data1(0), data2(0), time(0) { }
19
20 /******************************************************************************
21 copy_CSL_MIDIMessage
22 copies CSL_MIDIMessage
23 ******************************************************************************/
24
25 void csl::copy_CSL_MIDIMessage(CSL_MIDIMessage* source, CSL_MIDIMessage* dest ) {
26 dest->message = source->message;
27 dest->channel = source->channel;
28 dest->data1 = source->data1;
29 dest->data2 = source->data2;
30 dest->time = source->time;
31 }
32
33 /******************************************************************************
34 CSL_MIDIMessageToPmEvent
35 converts CSL_MIDIMessage to PmEvent
36 ******************************************************************************/
37 void csl::CSL_MIDIMessageToPmEvent(CSL_MIDIMessage* cslMIDI, PmEvent* event ) {
38
39 event->message = Pm_Message(Message_ChannelToStatus(cslMIDI->message, cslMIDI->channel ),
40 cslMIDI->data1,
41 cslMIDI->data2 );
42 event->timestamp = cslMIDI->time;
43 }
44
45 /******************************************************************************
46 PmEventToCSL_MIDIMessage
47 converts PmEvent to CSL_MIDIMessage
48 ******************************************************************************/
49 void csl::PmEventToCSL_MIDIMessage( PmEvent* event, CSL_MIDIMessage* cslMIDI ) {
50
51 cslMIDI->message = (CSL_MIDIMessageType)
52 ((unsigned) ((Pm_MessageStatus(event->message) & 0xF0) >> 4) );
53 cslMIDI->channel = Pm_MessageStatus(event->message) & 0x0F;
54 cslMIDI->data1 = Pm_MessageData1(event->message);
55 cslMIDI->data2 = Pm_MessageData2(event->message);
56 cslMIDI->time = event->timestamp;
57 }
58
59
60 /******************************************************************************
61 Message_ChannelTovoid
62 converts from message and channel to void byte
63 ******************************************************************************/
64 unsigned csl::Message_ChannelToStatus(CSL_MIDIMessageType message, unsigned channel ) {
65
66 return (((unsigned)message << 4) + channel );
67 }
68
69 /******************************************************************************
70 MIDIIO
71 ******************************************************************************/
72
73 // static var. definition
74
75 bool MIDIIO::mIsInitialized = false;
76 bool MIDIIO::mIsPortTimeStarted = false;
77 unsigned MIDIIO::mNumInstantiated = 0;
78
79 // Constructors
80
81 MIDIIO::MIDIIO() {
82 if( ! mIsInitialized ) {
83 Pm_Initialize();
84 mIsInitialized = true;
85 }
86 mNumInstantiated++;
87 mIsOpen = false;
88 mIsPortTimeStarted = false;
89 }
90
91 MIDIIO::~MIDIIO() {
92 mNumInstantiated--;
93 if(mNumInstantiated == 0) {
94 Pt_Stop();
95 mIsPortTimeStarted = false;
96 Pm_Terminate();
97 mIsInitialized = false;
98 }
99
100 }
101
102 bool MIDIIO::is_open() {
103 return mIsOpen;
104 }
105
106 void MIDIIO::close() {
107 PmError err;
108 err = Pm_Close(mMIDIStream );
109 if(err != pmNoError )
110 handle_error(err);
111 }
112
113 void MIDIIO::dump_device_info() {
114 int i;
115 char dev_nam[128], * nl;
116
117 printf("\nPortMidi devices:\n");
118 for (i = 0; i < Pm_CountDevices(); i++) {
119 const PmDeviceInfo *info = Pm_GetDeviceInfo(i);
120 if ((nl = index(info->name, '\n')) == NULL)
121 strcpy(dev_nam, info->name);
122 else
123 strncpy(dev_nam, info->name, (nl - info->name));
124 printf("\tDeviceID %d: %s, %s", i, info->interf, dev_nam);
125 if (info->input) printf(" (input)");
126 if (info->output) printf(" (output)");
127 printf("\n");
128 }
129 printf("\n");
130 }
131
132 void MIDIIO::dump_count_devices() {
133 printf("PortMidi device count = %d\n", Pm_CountDevices());
134 }
135
136 // thin wrappers for PortMidi functions. Made available for flexibility
137
138 int MIDIIO::count_devices() {
139 return (int) Pm_CountDevices();
140 }
141
142 int MIDIIO::get_default_input_id() {
143 return (int) Pm_GetDefaultInputDeviceID();
144 }
145
146 int MIDIIO::get_default_output_id() {
147 return (int) Pm_GetDefaultOutputDeviceID();
148 }
149
150 const PmDeviceInfo* MIDIIO::get_device_info(int deviceID ) {
151 return Pm_GetDeviceInfo((PmDeviceID) deviceID );
152 }
153
154 // PortMIDI error handler
155
156 void MIDIIO::handle_error(PmError err) {
157 Pm_Terminate();
158 logMsg(kLogError, "An error occured while using PortMIDI, Error# %d, %s",err, Pm_GetErrorText(err));
159 }
160
161 /******************************************************************************
162 MIDIIn
163 ******************************************************************************/
164
165 MIDIIn::MIDIIn() : MIDIIO() {
166 mIsInput = true;
167 mIsOutput = false;
168 mFilterFlag = 0;
169
170 mDeviceID = Pm_GetDefaultInputDeviceID();
171 mBufferSize = 100;
172 }
173
174 unsigned MIDIIn::buffer_size() {
175 return mBufferSize;
176 }
177
178 void MIDIIn::set_buffer_size(unsigned bufferSize) {
179 mBufferSize = bufferSize;
180 }
181
182 void MIDIIn::open() {
183 // checking if device with mDeviceID is an input.
184 const PmDeviceInfo* info = get_device_info(mDeviceID );
185 if( ! info->input ) {
186 return handle_error(pmInvalidDeviceId );
187 }
188 if( ! mIsPortTimeStarted )
189 TIME_START; // porttime start.
190 mIsPortTimeStarted = true;
191
192 PmError err;
193 // Pm_OpenInput(PortMidiStream**, PmDeviceID, void*, long int, PmTimestamp (*)(void*), void*)
194 err = Pm_OpenInput(&mMIDIStream, mDeviceID, DRIVER_INFO, mBufferSize,
195 (PmTimeProcPtr) TIME_PROC, TIME_INFO );
196 if(err != pmNoError )
197 return handle_error(err);
198 mFilterFlag = mFilterFlag | PM_FILT_ACTIVE; // portmidi filters active sensing by default.
199 }
200
201 void MIDIIn::open(int deviceID) {
202 mDeviceID = deviceID;
203 open();
204 }
205
206
207 bool MIDIIn::poll() {
208 PmError err;
209 err = Pm_Poll(mMIDIStream );
210 if(err == FALSE )
211 return false;
212 if(err == TRUE )
213 return true;
214 if(err != pmNoError ) {
215 handle_error(err);
216 return false; // need better error handling....
217 }
218 return false;
219 }
220
221 void MIDIIn::read() {
222 mLength = Pm_Read(mMIDIStream, mBuffer, 1);
223 #ifdef DEBUG_MIDIIO_READ
224 unsigned cmd, voice, pitch = 0, vel;
225 PmMessage msg;
226
227 if (mLength > 0) {
228 msg = mBuffer[0].message;
229 cmd = Pm_Messagevoid(msg) & 0xF0;
230 voice = Pm_Messagevoid(msg) & 0x0F;
231 pitch = Pm_MessageData1(msg);
232 vel = Pm_MessageData2(msg);
233 printf("\tGot message: time %.3f, cmd: %x midich: %d data1: %d data2: %d\n", (float) mBuffer[0].timestamp / 1000.0f, cmd, voice, pitch, vel);
234 }
235 #endif
236 if(mLength == 0 ) {
237 logMsg("in MIDIIn::read(). MIDI message not read");
238 return;
239 } else
240 if(mLength < 0 )
241 return handle_error(mLength );
242 }
243
244 void MIDIIn::read_interpret() {
245 read();
246 unsigned cmd, voice, pitch = 0, vel;
247 PmMessage msg;
248 msg = mBuffer[0].message;
249 cmd = (Pm_MessageStatus(msg) & 0xF0) >> 4;
250 voice = Pm_MessageStatus(msg) & 0x0F;
251 pitch = Pm_MessageData1(msg);
252 vel = Pm_MessageData2(msg);
253 switch(cmd) {
254 case kNoteOn:
255 if(vel > 0) {
256 mMsg.message = kNoteOn;
257 mMsg.channel = voice;
258 mMsg.data1 = pitch;
259 mMsg.data2 = vel;
260 break;
261 }
262 case kNoteOff:
263 mMsg.message = kNoteOff;
264 mMsg.channel = voice;
265 mMsg.data1 = pitch;
266 mMsg.data2 = vel;
267 break;
268 case kPolyTouch:
269 mMsg.message = kPolyTouch;
270 mMsg.channel = voice;
271 mMsg.data1 = pitch;
272 mMsg.data2 = vel;
273 case kControlChange:
274 mMsg.message = kControlChange;
275 mMsg.channel = voice;
276 mMsg.data1 = pitch;
277 mMsg.data2 = vel;
278 break;
279 case kProgramChange:
280 mMsg.message = kProgramChange;
281 mMsg.channel = voice;
282 mMsg.data1 = pitch;
283 mMsg.data2 = 0;
284 break;
285 case kAftertouch:
286 mMsg.message = kAftertouch;
287 mMsg.channel = voice;
288 mMsg.data1 = pitch;
289 mMsg.data2 = 0;
290 break;
291 case kPitchWheel:
292 mMsg.message = kPitchWheel;
293 mMsg.channel = voice;
294 mMsg.data1 = pitch;
295 mMsg.data2 = vel;
296 break;
297 case kSysEX:
298 mMsg.message = kSysEX;
299 mMsg.channel = 0;
300 mMsg.data1 = 0;
301 mMsg.data2 = 0;
302 break;
303 default:
304 printf("Unknown input: void=%d chan=%d value1=%d value2=%d", cmd, voice, pitch, vel);
305 break;
306 }
307
308 #ifdef DEBUG_MIDIIO_READ_INTERPRET
309 printf("\tGot message: time %.3f, cmd: %x midich: %d data1: %d data2: %d\n", (float) mBuffer[0].timestamp / 1000.0f, cmd, mMsg.channel, mMsg.data1, mMsg.data2);
310 #endif
311 }
312
313 void MIDIIn::dump_buffer() {
314 unsigned cmd, voice, pitch = 0, vel;
315 PmMessage msg;
316 msg = mBuffer[0].message;
317 cmd = Pm_MessageStatus(msg) & 0xF0;
318 voice = Pm_MessageStatus(msg) & 0x0F;
319 pitch = Pm_MessageData1(msg);
320 vel = Pm_MessageData2(msg);
321 printf("\tGot message: time %.3f, cmd: %x midich: %d data1: %d data2: %d\n", (float) mBuffer[0].timestamp / 1000.0f, cmd, voice, pitch, vel);
322
323 }
324 void MIDIIn::dump_CSL_MIDIMessage() {
325 printf("MIDIIn message received\n");
326 switch(mMsg.message) {
327 case kNone:
328 printf("\t message: kNone");
329 break;
330 case kNoteOff:
331 printf("\t message: kNoteOff");
332 break;
333 case kNoteOn:
334 printf("\t message: kNoteOn");
335 break;
336 case kPolyTouch:
337 printf("\t message: kPolyTouch");
338 break;
339 case kControlChange:
340 printf("\t message: kControlChange");
341 break;
342 case kProgramChange:
343 printf("\t message: kProgramChange");
344 break;
345 case kAftertouch:
346 printf("\t message: kAftertouch");
347 break;
348 case kPitchWheel:
349 printf("\t message: kPitchWheel");
350 break;
351 case kSysEX:
352 printf("\t message: kSysEX");
353 break;
354 default:
355 printf("\t message: unknown");
356 }
357 printf(" channel: %d data1: %d data2: %d time: %d\n", mMsg.channel, mMsg.data1, mMsg.data2, mMsg.time);
358 }
359
360
361 bool MIDIIn::is_NoteOn_received() {
362 return (mMsg.message == kNoteOn) ? true : false;
363 }
364
365 bool MIDIIn::is_NoteOff_received() {
366 return (mMsg.message == kNoteOff) ? true : false;
367 }
368
369 bool MIDIIn::is_PolyTouch_received() {
370 return (mMsg.message == kPolyTouch) ? true : false;
371 }
372
373 bool MIDIIn::is_ControlChange_received() {
374 return (mMsg.message == kControlChange) ? true : false;
375 }
376
377 bool MIDIIn::is_ProgramChange_received() {
378 return (mMsg.message == kProgramChange) ? true : false;
379 }
380
381 bool MIDIIn::is_Aftertouch_received() {
382 return (mMsg.message == kAftertouch) ? true : false;
383 }
384
385 bool MIDIIn::is_PitchWheel_received() {
386 return (mMsg.message == kPitchWheel) ? true : false;
387 }
388
389 bool MIDIIn::is_SysEX_received() {
390 return (mMsg.message == kSysEX) ? true :false;
391 }
392
393 unsigned MIDIIn::get_note() { return mMsg.data1; }
394 unsigned MIDIIn::get_velocity() { return mMsg.data2; }
395 unsigned MIDIIn::get_PolyAftertouch() { return mMsg.data2; }
396 unsigned MIDIIn::get_ControlChange_function() { return mMsg.data1; }
397 unsigned MIDIIn::get_ControlChange_value() { return mMsg.data2; }
398 unsigned MIDIIn::get_ProgramNumber() { return mMsg.data1; }
399 unsigned MIDIIn::get_Aftertouch() { return mMsg.data1; }
400 unsigned MIDIIn::get_PitchWheel() { return ((mMsg.data2 << 7) + mMsg.data1 ); }
401 float MIDIIn::get_frequency() { return keyToFreq(mMsg.data1 ); }
402 float MIDIIn::get_velocity_float() { return ((float) mMsg.data2 / 127.0f); }
403
404 void MIDIIn::setFilter() {
405 PmError err;
406 err = Pm_SetFilter(mMIDIStream, PM_FILT_ACTIVE | PM_FILT_CLOCK);
407 if(err != pmNoError )
408 handle_error(err);
409 }
410
411 void MIDIIn::filter_active_sensing(bool flag ) {
412 if( ! (mFilterFlag & PM_FILT_ACTIVE) && flag ) { // set filter
413 mFilterFlag = mFilterFlag | PM_FILT_ACTIVE;
414 PmError err;
415 err = Pm_SetFilter(mMIDIStream, mFilterFlag );
416 if(err != pmNoError )
417 return handle_error(err);
418 logMsg("active sensing will be filtered, mFilterFlag = %d", mFilterFlag);
419 return;
420 }
421 if((mFilterFlag & PM_FILT_ACTIVE) && !flag) { // un-set the filter
422
423 mFilterFlag = mFilterFlag ^ PM_FILT_ACTIVE; // xoring to flip just the unwanted bit.
424 PmError err;
425 err = Pm_SetFilter(mMIDIStream, mFilterFlag );
426 if(err != pmNoError )
427 return handle_error(err);
428 logMsg("active sensing will not be filtered, mFilterFlag = %d", mFilterFlag);
429 return;
430 }
431 // do nothing
432 logMsg("active sensing filter state not changed, mFilterFlag = %d", mFilterFlag);
433 }
434
435
436 void MIDIIn::filter_sysex(bool flag ) {
437 if( ! (mFilterFlag & PM_FILT_SYSEX) && flag ) { // set filter
438 mFilterFlag = mFilterFlag | PM_FILT_SYSEX;
439 PmError err;
440 err = Pm_SetFilter(mMIDIStream, mFilterFlag );
441 if(err != pmNoError )
442 return handle_error(err);
443 logMsg("sysex will be filtered, mFilterFlag = %d", mFilterFlag);
444 return;
445 }
446 if((mFilterFlag & PM_FILT_SYSEX) && !flag) { // un-set the filter
447
448 mFilterFlag = mFilterFlag ^ PM_FILT_SYSEX; // xoring to flip just the unwanted bit.
449 PmError err;
450 err = Pm_SetFilter(mMIDIStream, mFilterFlag );
451 if(err != pmNoError )
452 return handle_error(err);
453 logMsg("sysex will not be filtered, mFilterFlag = %d", mFilterFlag);
454 return;
455 }
456 // do nothing
457 logMsg("sysex filter state not changed, mFilterFlag = %d", mFilterFlag);
458 }
459
460
461 void MIDIIn::filter_clock_msg(bool flag ) {
462 if( ! (mFilterFlag & PM_FILT_CLOCK) && flag ) { // set filter
463
464 mFilterFlag = mFilterFlag | PM_FILT_CLOCK;
465 PmError err;
466 err = Pm_SetFilter(mMIDIStream, mFilterFlag );
467 if(err != pmNoError )
468 return handle_error(err);
469 logMsg("clock message will be filtered, mFilterFlag = %d", mFilterFlag);
470 return;
471 }
472 if((mFilterFlag & PM_FILT_CLOCK) && !flag) { // un-set the filter
473
474 mFilterFlag = mFilterFlag ^ PM_FILT_CLOCK; // xoring to flip just the unwanted bit.
475 PmError err;
476 err = Pm_SetFilter(mMIDIStream, mFilterFlag );
477 if(err != pmNoError )
478 return handle_error(err);
479 logMsg("clock message will not be filtered, mFilterFlag = %d", mFilterFlag);
480 return;
481 }
482 // do nothing
483 logMsg("clock message filter state not changed, mFilterFlag = %d", mFilterFlag);
484 }
485
486
487 /******************************************************************************
488 MIDIOut
489 ******************************************************************************/
490
491 MIDIOut::MIDIOut() : MIDIIO() {
492 mIsInput = false;
493 mIsOutput = true;
494 mDeviceID = Pm_GetDefaultOutputDeviceID();
495 mBufferSize = 0;
496 mLatency = 0; // do I need to set this to > 0 value for synchronization?
497 }
498
499 MIDIOut::~MIDIOut() {
500 }
501
502 unsigned MIDIOut::buffer_size() { return mBufferSize; }
503 void MIDIOut::set_buffer_size(unsigned bufferSize ) { mBufferSize = bufferSize; }
504 long MIDIOut::latency () { return mLatency; }
505 void MIDIOut::set_latency(long latency ) { mLatency = latency; }
506
507 void MIDIOut::open() {
508 // checking if device with mDeviceID is an output.
509 const PmDeviceInfo* info = get_device_info(mDeviceID );
510 if( ! info->output ) {
511 return handle_error(pmInvalidDeviceId );
512 }
513
514 if( ! mIsPortTimeStarted )
515 TIME_START;
516 mIsPortTimeStarted = false;
517
518 PmError err;
519 err = Pm_OpenOutput(&mMIDIStream, mDeviceID, DRIVER_INFO, mBufferSize,
520 (PmTimeProcPtr) TIME_PROC, TIME_INFO, mLatency );
521 if(err != pmNoError )
522 return handle_error(err );
523 }
524
525 void MIDIOut::open(int deviceID ) {
526 mDeviceID = deviceID;
527 open();
528 }
529
530 void MIDIOut::set_message(CSL_MIDIMessage msg, long when ) {
531 copy_CSL_MIDIMessage(&msg, &mMsg );
532 }
533
534 void MIDIOut::write() {
535 PmEvent event;
536 csl::CSL_MIDIMessageToPmEvent(&mMsg, &event);
537
538 long eventLength = 1; // does it need to be longer??
539 PmError err;
540 err = Pm_Write(mMIDIStream, &event, eventLength);
541 if(err != pmNoError )
542 return handle_error(err );
543 }
544
545 void MIDIOut::write(CSL_MIDIMessage* msg, long length ) {
546 int i;
547 for(i = 0; i < length; i++ ) {
548 copy_CSL_MIDIMessage(msg, &mMsg );
549 write();
550 }
551 }
552
553 void MIDIOut::write_short(CSL_MIDIMessage msg ) {
554 PmError err;
555 err = Pm_WriteShort(mMIDIStream,
556 msg.time,
557 Pm_Message(((unsigned)msg.message << 4) + msg.channel,
558 msg.data1,
559 msg.data2 )
560 );
561 if(err != pmNoError )
562 return handle_error(err );
563 }
564
565 void MIDIOut::write_SysEX(long when, unsigned char *msg ) {
566 PmError err;
567 err = Pm_WriteSysEx(mMIDIStream, when, msg);
568 if(err != pmNoError )
569 return handle_error(err );
570 }
571
572 void MIDIOut::write_NoteOn(unsigned channel, unsigned pitch, unsigned velocity ) {
573 PmEvent event;
574 event.timestamp = TIME_PROC();
575 event.message = Pm_Message(Message_ChannelToStatus(kNoteOn, channel),
576 pitch,
577 velocity );
578 PmError err;
579 err = Pm_Write(mMIDIStream, &event, 1);
580 if(err != pmNoError )
581 return handle_error(err );
582 }
583
584 void MIDIOut::write_NoteOn(unsigned channel, float frequency, float amplitude ) {
585 PmEvent event;
586 event.timestamp = TIME_PROC();
587 event.message = Pm_Message(Message_ChannelToStatus(kNoteOn, channel),
588 freqToKey(frequency),
589 (int)ceil(amplitude * 127.0f) );
590 PmError err;
591 err = Pm_Write(mMIDIStream, &event, 1);
592 if(err != pmNoError )
593 return handle_error(err );
594 }
595
596 void MIDIOut::write_NoteOff(unsigned channel, unsigned pitch, unsigned velocity ) {
597 PmEvent event;
598 event.timestamp = TIME_PROC();
599 event.message = Pm_Message(Message_ChannelToStatus(kNoteOff, channel),
600 pitch,
601 velocity );
602 PmError err;
603 err = Pm_Write(mMIDIStream, &event, 1);
604 if(err != pmNoError )
605 return handle_error(err );
606 }
607
608 void MIDIOut::write_NoteOff(unsigned channel, float frequency, float amplitude ) {
609 PmEvent event;
610 event.timestamp = TIME_PROC();
611 event.message = Pm_Message(Message_ChannelToStatus(kNoteOff, channel),
612 freqToKey(frequency),
613 (int)ceil(amplitude * 127.0f) );
614 PmError err;
615 err = Pm_Write(mMIDIStream, &event, 1);
616 if(err != pmNoError )
617 return handle_error(err );
618 }
619
620 void MIDIOut::write_PolyTouch(unsigned channel, unsigned pitch, unsigned amount ) {
621 PmEvent event;
622 event.timestamp = TIME_PROC();
623 event.message = Pm_Message(Message_ChannelToStatus(kPolyTouch, channel),
624 pitch,
625 amount );
626 PmError err;
627 err = Pm_Write(mMIDIStream, &event, 1);
628 if(err != pmNoError )
629 return handle_error(err );
630 }
631
632 void MIDIOut::write_ControlChange(unsigned channel, unsigned function, unsigned value ) {
633 PmEvent event;
634 event.timestamp = TIME_PROC();
635 event.message = Pm_Message(Message_ChannelToStatus(kControlChange, channel),
636 function,
637 value );
638 PmError err;
639 err = Pm_Write(mMIDIStream, &event, 1);
640 if(err != pmNoError )
641 return handle_error(err );
642 }
643
644 void MIDIOut::write_ProgramChange(unsigned channel, unsigned programNum ) {
645 PmEvent event;
646 event.timestamp = TIME_PROC();
647 event.message = Pm_Message(Message_ChannelToStatus(kProgramChange, channel),
648 programNum,
649 0 );
650 PmError err;
651 err = Pm_Write(mMIDIStream, &event, 1);
652 if(err != pmNoError )
653 return handle_error(err );
654 }
655
656 void MIDIOut::write_Aftertouch(unsigned channel, unsigned amount ) {
657 PmEvent event;
658 event.timestamp = TIME_PROC();
659 event.message = Pm_Message(Message_ChannelToStatus(kAftertouch, channel),
660 amount,
661 0 );
662 PmError err;
663 err = Pm_Write(mMIDIStream, &event, 1);
664 if(err != pmNoError )
665 return handle_error(err );
666 }
667
668 void MIDIOut::write_PitchWheel(unsigned channel, unsigned amount ) {
669 PmEvent event;
670 event.timestamp = TIME_PROC();
671 event.message = Pm_Message(Message_ChannelToStatus(kPitchWheel, channel),
672 amount,
673 0 );
674 PmError err;
675 err = Pm_Write(mMIDIStream, &event, 1);
676 if(err != pmNoError )
677 return handle_error(err );
678 }