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

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

unix.superglobalmegacorp.com

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