Annotation of hatari/src/midi.c, revision 1.1.1.12

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

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.