|
|
1.1 root 1: /*
1.1.1.2 root 2: Hatari - midi.c
1.1 root 3:
1.1.1.9 root 4: This file is distributed under the GNU General Public License, version 2
5: or at your option any later version. Read the file gpl.txt for details.
1.1.1.2 root 6:
7: MIDI communication.
8:
9: TODO:
10: - Most bits in the ACIA's status + control registers are currently ignored.
1.1.1.10 root 11:
12: NOTE [NP] :
13: In all accuracy, we should use a complete emulation of the acia serial line,
14: as for the ikbd. But as the MIDI's baudrate is rather high and could require
15: more resources to emulate at the bit level, we handle transfer 1 byte a time
16: instead of sending each bit one after the other.
17: This way, we only need a timer every 2560 cycles (instead of 256 cycles per bit).
18:
19: We handle a special case for the TX_EMPTY bit when reading SR : this bit should be set
20: after TDR was copied into TSR, which is approximatively when the next bit should
21: be transferred (256 cycles) (fix the program 'Notator')
1.1 root 22: */
1.1.1.6 root 23: const char Midi_fileid[] = "Hatari midi.c : " __DATE__ " " __TIME__;
1.1.1.2 root 24:
25: #include <SDL_types.h>
1.1 root 26:
27: #include "main.h"
1.1.1.2 root 28: #include "configuration.h"
1.1.1.3 root 29: #include "ioMem.h"
1.1.1.4 root 30: #include "m68000.h"
1.1.1.10 root 31: #include "memorySnapShot.h"
1.1.1.2 root 32: #include "mfp.h"
33: #include "midi.h"
1.1.1.5 root 34: #include "file.h"
1.1.1.9 root 35: #include "acia.h"
1.1.1.10 root 36: #include "screen.h"
37: #include "video.h"
1.1.1.2 root 38:
39:
40: #define ACIA_SR_INTERRUPT_REQUEST 0x80
1.1.1.6 root 41: #define ACIA_SR_TX_EMPTY 0x02
42: #define ACIA_SR_RX_FULL 0x01
1.1.1.2 root 43:
1.1.1.11 root 44: /* Delay to send/receive 1 byte through MIDI (in cpu cycles at x1, x2 or x4 speed)
1.1.1.10 root 45: * Serial line is set to 31250 bps, 1 start bit, 8 bits, 1 stop, no parity, which gives 256 cycles
46: * per bit at 8 MHz, and 2560 cycles to transfer 10 bits
47: */
1.1.1.11 root 48: #define MIDI_TRANSFER_BIT_CYCLE ( 256 << nCpuFreqShift )
49: #define MIDI_TRANSFER_BYTE_CYCLE (MIDI_TRANSFER_BIT_CYCLE * 10)
1.1.1.2 root 50:
51: static Uint8 MidiControlRegister;
52: static Uint8 MidiStatusRegister;
1.1.1.6 root 53: static Uint8 nRxDataByte;
1.1.1.10 root 54: static Uint64 TDR_Write_Time; /* Time of the last write in TDR fffc06 */
55: static Uint64 TDR_Empty_Time; /* Time when TDR will be empty after a write to fffc06 (ie when TDR is transferred to TSR) */
56: static Uint64 TSR_Complete_Time; /* Time when TSR will be completely transferred */
57:
1.1.1.2 root 58:
1.1.1.12! root 59: /*
! 60: ** Host MIDI I/O
! 61: */
! 62: static bool Midi_Host_Open(void);
! 63: static void Midi_Host_Close(void);
! 64: static int Midi_Host_ReadByte(void);
! 65: static bool Midi_Host_WriteByte(Uint8 byte);
! 66:
! 67: #ifndef HAVE_PORTMIDI
! 68: static FILE *pMidiFhIn = NULL; /* File handle used for Midi input */
! 69: static FILE *pMidiFhOut = NULL; /* File handle used for Midi output */
! 70: #else
! 71: #include "portmidi.h"
! 72: #define INPUT_BUFFER_SIZE 1024 // PortMidi handles buffering
! 73: static PmStream* midiIn = NULL; // current midi input port
! 74: static PmStream* midiOut = NULL; // current midi output port
! 75: static int numInputs = 0; // number of available input ports
! 76: static int numOutputs = 0; // number of available output ports
! 77: static const PmDeviceInfo** inports = NULL; // array of available input ports
! 78: static const PmDeviceInfo** outports = NULL; // array of available output ports
! 79:
! 80: static bool Midi_Host_SwitchPort(const char* portName, bool forInput);
! 81: static int Midi_GetDataLength(Uint8 status);
! 82: static int Midi_SplitEvent(PmEvent* midiEvent, Uint8* msg);
! 83: static PmEvent* Midi_BuildEvent(Uint8 byte);
! 84: #endif
! 85:
1.1.1.2 root 86:
1.1.1.5 root 87: /**
88: * Initialization: Open MIDI device.
89: */
1.1.1.2 root 90: void Midi_Init(void)
91: {
1.1.1.12! root 92: if (!Midi_Host_Open())
1.1.1.5 root 93: {
1.1.1.12! root 94: Log_AlertDlg(LOG_ERROR, "MIDI i/o open failed. MIDI support disabled.");
! 95: ConfigureParams.Midi.bEnableMidi = false;
1.1.1.2 root 96: }
97: }
98:
99:
1.1.1.5 root 100: /**
101: * Close MIDI device.
102: */
1.1.1.2 root 103: void Midi_UnInit(void)
104: {
1.1.1.12! root 105: Midi_Host_Close();
1.1.1.8 root 106: CycInt_RemovePendingInterrupt(INTERRUPT_MIDI);
1.1.1.2 root 107: }
108:
109:
1.1.1.5 root 110: /**
1.1.1.6 root 111: * Reset MIDI emulation.
1.1.1.5 root 112: */
1.1.1.6 root 113: void Midi_Reset(void)
1.1.1.2 root 114: {
1.1.1.10 root 115: //fprintf ( stderr , "midi reset\n" );
1.1.1.6 root 116: MidiControlRegister = 0;
117: MidiStatusRegister = ACIA_SR_TX_EMPTY;
118: nRxDataByte = 1;
1.1.1.10 root 119: TDR_Empty_Time = 0;
120: TSR_Complete_Time = 0;
1.1.1.2 root 121:
1.1.1.10 root 122: /* Set timer */
123: CycInt_AddRelativeInterrupt ( MIDI_TRANSFER_BYTE_CYCLE , INT_CPU_CYCLE , INTERRUPT_MIDI );
124: }
125:
126:
127: /**
128: * Save/Restore snapshot of local variables
129: */
130: void MIDI_MemorySnapShot_Capture(bool bSave)
131: {
132: MemorySnapShot_Store(&MidiControlRegister, sizeof(MidiControlRegister));
133: MemorySnapShot_Store(&MidiStatusRegister, sizeof(MidiStatusRegister));
134: MemorySnapShot_Store(&nRxDataByte, sizeof(nRxDataByte));
135: MemorySnapShot_Store(&TDR_Empty_Time, sizeof(TDR_Empty_Time));
136: MemorySnapShot_Store(&TSR_Complete_Time, sizeof(TSR_Complete_Time));
137: }
138:
139:
140: /*-----------------------------------------------------------------------*/
141: /**
142: * Check if the IRQ must be changed in SR.
143: * When there's a change, we must change the IRQ line too.
144: */
145: static void MIDI_UpdateIRQ ( void )
146: {
147: Uint8 irq_bit_new;
148:
149: irq_bit_new = 0;
150:
151: if ( ( ( MidiControlRegister & 0x80 ) == 0x80 ) /* Check for RX causes of interrupt */
152: && ( MidiStatusRegister & ACIA_SR_RX_FULL ) )
153: irq_bit_new = ACIA_SR_INTERRUPT_REQUEST;
154:
155: if ( ( ( MidiControlRegister & 0x60) == 0x20 ) /* Check for TX causes of interrupt */
156: && ( MidiStatusRegister & ACIA_SR_TX_EMPTY ) )
157: irq_bit_new = ACIA_SR_INTERRUPT_REQUEST;
158:
159: /* Update SR and IRQ line if a change happened */
160: if ( ( MidiStatusRegister & ACIA_SR_INTERRUPT_REQUEST ) != irq_bit_new )
161: {
162: LOG_TRACE ( TRACE_MIDI, "midi update irq irq_new=%d VBL=%d HBL=%d\n" , irq_bit_new?1:0 , nVBLs , nHBL );
163:
164: if ( irq_bit_new )
165: {
166: /* Request interrupt by setting GPIP to low/0 */
167: MFP_GPIP_Set_Line_Input ( MFP_GPIP_LINE_ACIA , MFP_GPIP_STATE_LOW );
168: MidiStatusRegister |= ACIA_SR_INTERRUPT_REQUEST;
169: }
170: else
171: {
172: /* Clear interrupt request by setting GPIP to high/1 */
173: MFP_GPIP_Set_Line_Input ( MFP_GPIP_LINE_ACIA , MFP_GPIP_STATE_HIGH );
174: MidiStatusRegister &= ~ACIA_SR_INTERRUPT_REQUEST;
175: }
176: }
1.1.1.2 root 177: }
178:
179:
1.1.1.10 root 180:
1.1.1.5 root 181: /**
1.1.1.6 root 182: * Read MIDI status register ($FFFC04).
1.1.1.5 root 183: */
1.1.1.6 root 184: void Midi_Control_ReadByte(void)
1.1.1.2 root 185: {
1.1.1.9 root 186: ACIA_AddWaitCycles (); /* Additional cycles when accessing the ACIA */
1.1.1.4 root 187:
1.1.1.10 root 188: /* Special case : if we wrote a byte into TDR, TX_EMPTY bit should be */
189: /* set approximatively after the first bit was transferred using TSR */
190: if ( ( ( MidiStatusRegister & ACIA_SR_TX_EMPTY ) == 0 )
191: && ( CyclesGlobalClockCounter > TDR_Empty_Time ) ) // OK avec 11 bits et 1 bit
192: {
193: MidiStatusRegister |= ACIA_SR_TX_EMPTY;
194:
195: /* Do we need to generate a transfer interrupt? */
196: MIDI_UpdateIRQ ();
197: }
198:
199: //fprintf ( stderr , "midi read sr %x %lld %lld\n" , MidiStatusRegister , CyclesGlobalClockCounter , TDR_Write_Time );
200:
1.1.1.6 root 201: IoMem[0xfffc04] = MidiStatusRegister;
1.1.1.10 root 202:
203: LOG_TRACE ( TRACE_MIDI, "midi read fffc04 sr=0x%02x VBL=%d HBL=%d\n" , MidiStatusRegister , nVBLs , nHBL );
1.1.1.2 root 204: }
205:
1.1 root 206:
1.1.1.5 root 207: /**
208: * Write to MIDI control register ($FFFC04).
209: */
1.1.1.3 root 210: void Midi_Control_WriteByte(void)
1.1.1.2 root 211: {
1.1.1.9 root 212: ACIA_AddWaitCycles (); /* Additional cycles when accessing the ACIA */
1.1.1.4 root 213:
1.1.1.3 root 214: MidiControlRegister = IoMem[0xfffc04];
1.1.1.2 root 215:
1.1.1.10 root 216: LOG_TRACE ( TRACE_MIDI, "midi write fffc04 cr=0x%02x VBL=%d HBL=%d\n" , MidiControlRegister , nVBLs , nHBL );
1.1.1.2 root 217:
1.1.1.10 root 218: MIDI_UpdateIRQ ();
1.1.1.2 root 219: }
220:
221:
1.1.1.6 root 222: /**
223: * Read MIDI data register ($FFFC06).
224: */
225: void Midi_Data_ReadByte(void)
226: {
1.1.1.10 root 227: LOG_TRACE ( TRACE_MIDI, "midi read fffc06 rdr=0x%02x VBL=%d HBL=%d\n" , nRxDataByte , nVBLs , nHBL );
228: //fprintf ( stderr , "midi rx %x\n" , nRxDataByte);
1.1.1.6 root 229:
1.1.1.9 root 230: ACIA_AddWaitCycles (); /* Additional cycles when accessing the ACIA */
1.1.1.6 root 231:
1.1.1.10 root 232: IoMem[0xfffc06] = nRxDataByte;
1.1.1.6 root 233:
1.1.1.10 root 234: MidiStatusRegister &= ~ACIA_SR_RX_FULL;
1.1.1.6 root 235:
1.1.1.10 root 236: MIDI_UpdateIRQ ();
1.1.1.6 root 237: }
238:
239:
1.1.1.5 root 240: /**
241: * Write to MIDI data register ($FFFC06).
1.1.1.10 root 242: * We should determine precisely when TDR will be empty and when TSR will be transferred.
243: * This is required to accurately emulate the TDRE bit in status register (fix the program 'Notator')
1.1.1.5 root 244: */
1.1.1.3 root 245: void Midi_Data_WriteByte(void)
1.1.1.2 root 246: {
1.1.1.6 root 247: Uint8 nTxDataByte;
1.1.1.12! root 248: bool ok;
! 249:
1.1.1.9 root 250: ACIA_AddWaitCycles (); /* Additional cycles when accessing the ACIA */
1.1.1.4 root 251:
1.1.1.6 root 252: nTxDataByte = IoMem[0xfffc06];
1.1.1.10 root 253: TDR_Write_Time = CyclesGlobalClockCounter;
254:
255: /* If TSR is already transferred, then TDR will be empty after 1 bit is transferred */
256: /* If TSR is not completely transferred, then TDR will be empty 1 bit after TSR is transferred */
257: if ( CyclesGlobalClockCounter >= TSR_Complete_Time )
258: {
259: TDR_Empty_Time = CyclesGlobalClockCounter + MIDI_TRANSFER_BIT_CYCLE;
260: TSR_Complete_Time = CyclesGlobalClockCounter + MIDI_TRANSFER_BYTE_CYCLE;
261: }
262: else
263: {
264: //fprintf ( stderr , "MIDI OVR %lld\n" , TSR_Complete_Time - CyclesGlobalClockCounter );
265: TDR_Empty_Time = TSR_Complete_Time + MIDI_TRANSFER_BIT_CYCLE;
266: TSR_Complete_Time += MIDI_TRANSFER_BYTE_CYCLE;
267: }
268:
269: LOG_TRACE ( TRACE_MIDI, "midi write fffc06 tdr=0x%02x VBL=%d HBL=%d\n" , nTxDataByte , nVBLs , nHBL );
270: //fprintf ( stderr , "midi tx %x sr=%x\n" , nTxDataByte , MidiStatusRegister );
1.1.1.3 root 271:
1.1.1.10 root 272: MidiStatusRegister &= ~ACIA_SR_TX_EMPTY;
1.1.1.2 root 273:
1.1.1.10 root 274: MIDI_UpdateIRQ ();
1.1.1.2 root 275:
276: if (!ConfigureParams.Midi.bEnableMidi)
277: return;
278:
1.1.1.12! root 279: ok = Midi_Host_WriteByte(nTxDataByte);
1.1.1.6 root 280:
1.1.1.12! root 281: /* If there was an error then stop the midi emulation */
! 282: if (!ok)
! 283: {
! 284: LOG_TRACE(TRACE_MIDI, "MIDI: write error -> stop MIDI\n");
! 285: Midi_UnInit();
! 286: return;
1.1.1.2 root 287: }
1.1.1.6 root 288: }
289:
290:
291: /**
292: * Read and write MIDI interface data regularly
293: */
294: void Midi_InterruptHandler_Update(void)
295: {
296: int nInChar;
297:
298: /* Remove this interrupt from list and re-order */
1.1.1.8 root 299: CycInt_AcknowledgeInterrupt();
1.1.1.6 root 300:
1.1.1.10 root 301: /* Special case : if we wrote a byte into TDR, TX_EMPTY bit should be */
302: /* set when reaching TDR_Empty_Time */
303: if ( ( ( MidiStatusRegister & ACIA_SR_TX_EMPTY ) == 0 )
304: && ( CyclesGlobalClockCounter > TDR_Empty_Time ) )
1.1.1.2 root 305: {
1.1.1.10 root 306: MidiStatusRegister |= ACIA_SR_TX_EMPTY;
307:
1.1.1.6 root 308: /* Do we need to generate a transfer interrupt? */
1.1.1.10 root 309: MIDI_UpdateIRQ ();
1.1.1.2 root 310:
1.1.1.10 root 311: /* Flush outgoing data (not necessary ?) */
1.1.1.6 root 312: // if (pMidiFhOut)
313: // fflush(pMidiFhOut);
1.1.1.2 root 314: }
1.1.1.6 root 315:
316: /* Read the bytes in, if we have any */
1.1.1.12! root 317: nInChar = Midi_Host_ReadByte();
! 318: if (nInChar != EOF)
1.1.1.6 root 319: {
1.1.1.12! root 320: LOG_TRACE(TRACE_MIDI, "MIDI: Read character -> $%x\n", nInChar);
! 321: /* Copy into our internal queue */
! 322: nRxDataByte = nInChar;
! 323: MidiStatusRegister |= ACIA_SR_RX_FULL;
! 324:
! 325: /* Do we need to generate a receive interrupt? */
! 326: MIDI_UpdateIRQ ();
! 327: }
! 328: #ifndef HAVE_PORTMIDI
! 329: else if (pMidiFhIn)
! 330: {
! 331: LOG_TRACE(TRACE_MIDI, "MIDI: read error (doesn't stop MIDI)\n");
! 332: clearerr(pMidiFhIn);
! 333: }
! 334: #endif
! 335:
! 336: /* Set timer */
! 337: CycInt_AddRelativeInterrupt ( MIDI_TRANSFER_BYTE_CYCLE , INT_CPU_CYCLE , INTERRUPT_MIDI );
! 338: }
1.1.1.10 root 339:
1.1.1.12! root 340:
! 341: // ============================================================================
! 342: // Host MIDI I/O
! 343: // ============================================================================
! 344:
! 345: /**
! 346: * open MIDI streams
! 347: * return true for no errors
! 348: */
! 349: static bool Midi_Host_Open(void)
! 350: {
! 351: #ifndef HAVE_PORTMIDI
! 352: if (!ConfigureParams.Midi.bEnableMidi)
! 353: return true;
! 354:
! 355: if (ConfigureParams.Midi.sMidiOutFileName[0])
! 356: {
! 357: /* Open MIDI output file */
! 358: pMidiFhOut = File_Open(ConfigureParams.Midi.sMidiOutFileName, "wb");
! 359: if (!pMidiFhOut)
! 360: return false;
! 361: setvbuf(pMidiFhOut, NULL, _IONBF, 0); /* No output buffering! */
! 362: LOG_TRACE(TRACE_MIDI, "MIDI: Opened file '%s' for output\n",
! 363: ConfigureParams.Midi.sMidiOutFileName);
! 364: }
! 365: if (ConfigureParams.Midi.sMidiInFileName[0])
! 366: {
! 367: /* Try to open MIDI input file */
! 368: pMidiFhIn = File_Open(ConfigureParams.Midi.sMidiInFileName, "rb");
! 369: if (!pMidiFhIn)
! 370: return false;
! 371: setvbuf(pMidiFhIn, NULL, _IONBF, 0); /* No input buffering! */
! 372: LOG_TRACE(TRACE_MIDI, "MIDI: Opened file '%s' for input\n",
! 373: ConfigureParams.Midi.sMidiInFileName);
! 374: }
! 375:
! 376: #else
! 377: /* Need to always get MIDI device info, for MIDI setup dialog */
! 378: int i;
! 379: int iindex = 0;
! 380: int oindex = 0;
! 381: int numPorts = 0;
! 382:
! 383: if (Pm_Initialize() != pmNoError)
! 384: {
! 385: LOG_TRACE(TRACE_MIDI, "MIDI: PortMidi initialization failed\n");
! 386: return false;
! 387: }
! 388: // -- get rid of earlier portmidi descriptor arrays (if allocated)
! 389: // -- the information may be stale (USB Midi etc)
! 390: if (inports)
! 391: free (inports);
! 392: if (outports)
! 393: free (outports);
! 394: inports = outports = NULL;
! 395: numInputs = numOutputs = 0;
! 396:
! 397: // -- count number of input and output ports
! 398: numPorts = Pm_CountDevices();
! 399: for (i = 0; i < numPorts; i++)
! 400: {
! 401: const PmDeviceInfo *info = Pm_GetDeviceInfo(i);
! 402: if (info->input)
! 403: numInputs++;
! 404: else if (info->output)
! 405: numOutputs++;
! 406: }
! 407:
! 408: // -- allocate descriptor arrays
! 409: inports = malloc(numInputs * sizeof(PmDeviceInfo*));
! 410: outports = malloc(numOutputs * sizeof(PmDeviceInfo*));
! 411:
! 412: // -- populate descriptor arrays
! 413: for (i = 0; i < numPorts; i++)
! 414: {
! 415: const PmDeviceInfo* info = Pm_GetDeviceInfo(i);
! 416: if (info)
! 417: {
! 418: LOG_TRACE(TRACE_MIDI, "MIDI: device %d: '%s'\n", i, info->name);
! 419: if (info->input)
! 420: inports[iindex++] = info;
! 421: if (info->output)
! 422: outports[oindex++] = info;
1.1.1.6 root 423: }
424: else
1.1.1.12! root 425: LOG_TRACE(TRACE_MIDI, "MIDI: info disappeared for device %d!\n", i);
! 426: }
! 427:
! 428: // -- open input and output ports according to configuration
! 429: if (ConfigureParams.Midi.sMidiInPortName[0])
! 430: Midi_Host_SwitchPort(ConfigureParams.Midi.sMidiInPortName, true);
! 431: if (ConfigureParams.Midi.sMidiOutPortName[0])
! 432: Midi_Host_SwitchPort(ConfigureParams.Midi.sMidiOutPortName, false);
! 433: #endif
! 434:
! 435: return true;
! 436: }
! 437:
! 438:
! 439: /* ---------------------------------------------------------------------------- */
! 440: /**
! 441: * close MIDI streams
! 442: */
! 443: static void Midi_Host_Close(void)
! 444: {
! 445: #ifndef HAVE_PORTMIDI
! 446: pMidiFhIn = File_Close(pMidiFhIn);
! 447: pMidiFhOut = File_Close(pMidiFhOut);
! 448: #else
! 449: if (midiIn)
! 450: Pm_Close(midiIn);
! 451: if (midiOut)
! 452: Pm_Close(midiOut);
! 453: midiIn = midiOut = NULL;
! 454:
! 455: // Can't terminate PM or free descriptor arrays as this gets
! 456: // called by any write errors and GUI won't then work.
! 457: // Pm_Terminate();
! 458: #endif
! 459: }
! 460:
! 461:
! 462:
! 463: /* ---------------------------------------------------------------------------- */
! 464: /**
! 465: * returns port name for input or output port at 'index'
! 466: */
! 467: const char* Midi_Host_GetPortName(int index, bool forInput)
! 468: {
! 469: #ifdef HAVE_PORTMIDI
! 470: if (forInput && index < numInputs)
! 471: return inports[index]->name;
! 472: else if (!forInput && index < numOutputs)
! 473: return outports[index]->name;
! 474: #endif
! 475:
! 476: return NULL;
! 477: }
! 478:
! 479: /* ---------------------------------------------------------------------------- */
! 480: /**
! 481: * returns port descriptor array index for input or output 'portName'
! 482: */
! 483: int Midi_Host_GetPortIndex(const char* portName, bool forInput)
! 484: {
! 485: #ifdef HAVE_PORTMIDI
! 486: int i = 0;
! 487: int numPorts = forInput ? numInputs : numOutputs;
! 488: const PmDeviceInfo** ports = forInput ? inports : outports;
! 489:
! 490: if (ports)
! 491: {
! 492: for (i = 0; i < numPorts; i++)
! 493: if (!strcmp(portName, ports[i]->name))
! 494: return i;
! 495: }
! 496: #endif
! 497:
! 498: return -1;
! 499: }
! 500:
! 501: /**
! 502: * closes current midi port (if any) and opens 'portName' if MIDI enabled
! 503: * returns true if successful, false otherwise
! 504: */
! 505: #ifdef HAVE_PORTMIDI
! 506: static bool Midi_Host_SwitchPort(const char* portName, bool forInput)
! 507: {
! 508: int i, count;
! 509: bool err;
! 510:
! 511: if (!ConfigureParams.Midi.bEnableMidi)
! 512: return false;
! 513:
! 514: // -- find PortMidi index for 'portName'
! 515: count = Pm_CountDevices();
! 516: for (i = 0; i < count; i++)
! 517: {
! 518: const PmDeviceInfo* info = Pm_GetDeviceInfo(i);
! 519: if (info)
1.1.1.6 root 520: {
1.1.1.12! root 521: if (forInput && !info->input)
! 522: continue;
! 523: else if (!forInput && info->input)
! 524: continue;
! 525: if (!strcmp(info->name, portName))
! 526: break;
1.1.1.6 root 527: }
528: }
1.1.1.12! root 529: if (i >= count)
! 530: return false;
1.1.1.6 root 531:
1.1.1.12! root 532: // -- close current port in any case, then try open new one
! 533: if (forInput == true)
! 534: {
! 535: if (midiIn) {
! 536: Pm_Close(midiIn);
! 537: midiIn = NULL;
! 538: }
! 539: err = (Pm_OpenInput(&midiIn, i, NULL, INPUT_BUFFER_SIZE, NULL, NULL) == pmNoError);
! 540: LOG_TRACE(TRACE_MIDI, "MIDI: input port %d '%s' open %s\n", i, portName, err ? "succeeded" : "failed");
! 541: return err;
! 542: }
! 543: else
! 544: {
! 545: if (midiOut) {
! 546: Pm_Close(midiOut);
! 547: midiOut = NULL;
! 548: }
! 549: err = (Pm_OpenOutput(&midiOut, i, NULL, 0, NULL, NULL, 0) == pmNoError);
! 550: LOG_TRACE(TRACE_MIDI, "MIDI: output port %d '%s' open %s\n", i, portName, err ? "succeeded" : "failed");
! 551: return err;
! 552: }
! 553:
! 554: return false;
! 555: }
! 556: #endif
! 557:
! 558: /**
! 559: * returns byte from input stream, or EOF if it is empty
! 560: */
! 561: static int Midi_Host_ReadByte(void)
! 562: {
! 563: #ifndef HAVE_PORTMIDI
! 564: if (pMidiFhIn && File_InputAvailable(pMidiFhIn))
! 565: return fgetc(pMidiFhIn);
! 566: else return EOF;
1.1.1.11 root 567: #else
1.1.1.12! root 568: static Uint8 msg[4];
! 569: static Uint8 ibyte = 0;
! 570: static int bytesAvailable = 0;
! 571:
! 572: if (midiIn)
! 573: {
! 574: // -- we have not yet returned all bytes from previous event
! 575: if (bytesAvailable > 0)
! 576: {
! 577: bytesAvailable--;
! 578: return msg[ibyte++];
! 579: }
! 580:
! 581: // -- read new event (if any)
! 582: else if (Pm_Poll(midiIn) == TRUE) {
! 583: PmEvent midiEvent;
! 584: if (Pm_Read(midiIn, &midiEvent, 1) != 1)
! 585: return EOF;
! 586: if ((bytesAvailable = Midi_SplitEvent(&midiEvent, msg)) > 0)
! 587: {
! 588: bytesAvailable--;
! 589: ibyte = 1;
! 590: return msg[0];
! 591: }
! 592: }
! 593: }
! 594:
! 595: // -- no more midi data
! 596: return EOF;
1.1.1.11 root 597: #endif
1.1.1.2 root 598: }
1.1.1.10 root 599:
1.1.1.12! root 600:
! 601: /**
! 602: * writes 'byte' into output stream, returns true on success
! 603: */
! 604: static bool Midi_Host_WriteByte(Uint8 byte)
! 605: {
! 606: #ifndef HAVE_PORTMIDI
! 607: if (pMidiFhOut)
! 608: {
! 609: /* Write the character to the output file: */
! 610: int ret = fputc(byte, pMidiFhOut);
! 611: return (ret != EOF);
! 612: }
! 613: #else
! 614: if (midiOut)
! 615: {
! 616: PmEvent* midiEvent = Midi_BuildEvent(byte);
! 617: if (midiEvent)
! 618: {
! 619: PmError error = Pm_Write(midiOut, midiEvent, 1);
! 620: if (error == pmNoError)
! 621: return true;
! 622: LOG_TRACE(TRACE_MIDI, "MIDI: PortMidi write error %d\n", error);
! 623: return false;
! 624: }
! 625: return true;
! 626: }
! 627: #endif
! 628:
! 629: return false;
! 630: }
! 631:
! 632:
! 633: #ifdef HAVE_PORTMIDI
! 634: // ============================================================================
! 635: // PortMidi utils
! 636: //
! 637: // PortMidi (as most native MIDI APIs) operate on complete MIDI messages
! 638: // we need therefore handle running status, variable number of data bytes
! 639: // and sysex correctly.
! 640: //
! 641: // ============================================================================
! 642:
! 643: /**
! 644: * return number of databytes that should accompany 'status' byte
! 645: * four bytes for sysex is a special case to simplify Midi_BuildEvent()
! 646: */
! 647: static int Midi_GetDataLength(Uint8 status)
! 648: {
! 649: static const Uint8 dataLength[] = { 2,2,2,2,1,2,2, 4,1,2,1,0,0,0,0 };
! 650:
! 651: if (status >= 0xF8 || status == 0)
! 652: return 0;
! 653: if (status >= 0xF0)
! 654: return dataLength[(status & 0x0F) + 7];
! 655: return dataLength[(status >> 4) - 8];
! 656: }
! 657:
! 658: /**
! 659: * collect bytes until valid MIDI event has been formed / four bytes of sysex data has been gathered
! 660: * returns PmEvent when done, or NULL if it needs still more data
! 661: * see MIDI 1.0 Detailed Spec 4.2, pages A-1..A-2 for discussion on running status
! 662: */
! 663: static PmEvent* Midi_BuildEvent(Uint8 byte)
! 664: {
! 665: static const Uint8 shifts[] = { 0,8,16,24 };
! 666: static PmEvent midiEvent = { 0,0 };
! 667: static Uint32 midimsg;
! 668: // static Uint8 runningStatus = 0;
! 669: static Uint8 bytesToWait = 0;
! 670: static Uint8 bytesCollected = 0;
! 671: static bool processingSysex = false;
! 672:
! 673: // -- status byte
! 674: if (byte & 0x80)
! 675: {
! 676: if (byte >= 0xF8)
! 677: {
! 678: midiEvent.message = Pm_Message(byte,0,0);
! 679: return &midiEvent;
! 680: }
! 681: else
! 682: {
! 683: processingSysex = false;
! 684: if (byte >= 0xF0)
! 685: {
! 686: // runningStatus = 0;
! 687: if (byte == 0xF0)
! 688: {
! 689: processingSysex = true;
! 690: bytesCollected = 1;
! 691: }
! 692: else if (byte == 0xF7)
! 693: {
! 694: midiEvent.message = midimsg | (((Uint32)byte) << shifts[bytesCollected]);
! 695: midimsg = bytesToWait = bytesCollected = 0;
! 696: return &midiEvent;
! 697: }
! 698: else
! 699: bytesCollected = 0;
! 700: }
! 701: else
! 702: {
! 703: // runningStatus = byte;
! 704: bytesCollected = 0;
! 705: }
! 706: }
! 707: midimsg = byte;
! 708: bytesToWait = Midi_GetDataLength(byte);
! 709: }
! 710:
! 711: // -- data byte
! 712: else
! 713: {
! 714: if (processingSysex)
! 715: midimsg |= ((Uint32)byte) << shifts[bytesCollected++];
! 716: else
! 717: midimsg |= ((Uint32)byte) << shifts[++bytesCollected];
! 718: if (bytesCollected >= bytesToWait)
! 719: {
! 720: midiEvent.message = midimsg;
! 721: midimsg = 0;
! 722: bytesCollected = 0;
! 723: bytesToWait = processingSysex ? 4 : 0;
! 724: return &midiEvent;
! 725: }
! 726: }
! 727:
! 728: return NULL;
! 729: }
! 730:
! 731:
! 732: /**
! 733: * extracts raw bytes from 'midiEvent' into 'msg'
! 734: * returns number of bytes available in 'msg'
! 735: *
! 736: * this method is required for sysex handling
! 737: * native framework has already handled running status
! 738: */
! 739: static int Midi_SplitEvent(PmEvent* midiEvent, Uint8* msg)
! 740: {
! 741: static bool processingSysex = false;
! 742: PmMessage midiMessage = midiEvent->message;
! 743: int i, bytesAvailable = 0;
! 744:
! 745: msg[0] = Pm_MessageStatus(midiMessage);
! 746:
! 747: // -- sysex start or continuation
! 748: if ((msg[0] == 0xF0) || (msg[0] < 0x80))
! 749: {
! 750: if (msg[0] == 0xF0)
! 751: processingSysex = true;
! 752:
! 753: if (processingSysex)
! 754: {
! 755: for (i = 0; i <= 3; i++)
! 756: {
! 757: msg[i] = midiMessage & 0xFF;
! 758: bytesAvailable = i;
! 759: if (msg[i] == 0xF7)
! 760: {
! 761: processingSysex = false;
! 762: break;
! 763: }
! 764: midiMessage >>= 8;
! 765: }
! 766: }
! 767: else bytesAvailable = -1;
! 768: }
! 769:
! 770: // -- non-sysex
! 771: else
! 772: {
! 773: if (msg[0] < 0xF8) // non-realtime
! 774: {
! 775: processingSysex = false;
! 776: midiMessage >>= 8;
! 777: bytesAvailable = Midi_GetDataLength(msg[0]);
! 778: for (i = 1; i <= bytesAvailable; i++)
! 779: {
! 780: msg[i] = midiMessage & 0xFF;
! 781: midiMessage >>= 8;
! 782: }
! 783: }
! 784: }
! 785:
! 786: return bytesAvailable + 1;
! 787: }
! 788: #endif
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.