⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 beepmidi.c

📁 这是一个开放源代码的与WINNT/WIN2K/WIN2003兼容的操作系统
💻 C
📖 第 1 页 / 共 2 页
字号:
        }

        prev_node = node;
        node = node->next;
    }

    LeaveCriticalSection(&device_lock);

    /* Hmm, a good idea? */
#ifndef CONTINUOUS_NOTES
    ProcessPlayingNotes((PVOID) device_info);
#endif

    return MMSYSERR_NOERROR;
}


/*
    Adds a note to the playing notes list. If the note is already playing,
    the definition of ALLOW_DUPLICATE_NOTES determines if an existing note
    may be duplicated. Otherwise, duplicate notes are ignored.
*/

MMRESULT
PlayNote(
    DeviceInfo* device_info,
    UCHAR note,
    UCHAR velocity)
{
    HANDLE heap = GetProcessHeap();

    DPRINT("PlayNote\n");

    NoteNode* node;

    if ( velocity == 0 )
    {
        DPRINT("Zero velocity\n");

        /* Velocity zero is effectively a "note off" */
        StopNote(device_info, note);
    }
    else
    {
        /* Start playing the note */
        NoteNode* new_node;
        NoteNode* tail_node = NULL;

        EnterCriticalSection(&device_lock);
    
        node = device_info->note_list;
    
        while ( node != NULL )
        {
#ifndef ALLOW_DUPLICATE_NOTES
            if ( ( node->note == note ) && ( velocity > 0 ) )
            {
                /* The note is already playing - do nothing */
                DPRINT("Duplicate note playback request ignored\n");
                LeaveCriticalSection(&device_lock);
                return MMSYSERR_NOERROR;
            }
#endif

            tail_node = node;
            node = node->next;
        }

        new_node = HeapAlloc(heap, HEAP_ZERO_MEMORY, sizeof(NoteNode));

        if ( ! new_node )
        {
            LeaveCriticalSection(&device_lock);
            return MMSYSERR_NOMEM;
        }

        new_node->note = note;
        new_node->velocity = velocity;

        /*
            Prepend to the playing notes list. If exceeding polyphony,
            remove the oldest note (which will be at the tail.)
        */

        if ( device_info->note_list )
            device_info->note_list->previous = new_node;

        new_node->next = device_info->note_list;
        new_node->previous = NULL;

        device_info->note_list = new_node;
        device_info->playing_notes_count ++;

/*
        if ( device_info->playing_notes_count > POLYPHONY )
        {
            ASSERT(tail_node);

            DPRINT("Polyphony exceeded\n");

            tail_node->previous->next = NULL;

            HeapFree(heap, 0, tail_node);

            device_info->playing_notes_count --;
        }
*/

        LeaveCriticalSection(&device_lock);

        DPRINT("Note started - now playing %d notes\n", (int) device_info->playing_notes_count);
        device_info->refresh_notes = TRUE;
    }

#ifndef CONTINUOUS_NOTES
    ProcessPlayingNotes((PVOID) device_info);
#endif

    return MMSYSERR_NOERROR;
}

/*
    Decipher a short MIDI message (which is a MIDI message packed into a DWORD.)
    This will set "running status", but does not take this into account when
    processing messages (is this necessary?)
*/

MMRESULT
ProcessShortMidiMessage(
    DeviceInfo* device_info,
    DWORD message)
{
    DWORD status;

    DWORD category;
    DWORD channel;
    DWORD data1, data2;

    status = message & 0x000000FF;

    /* Deal with running status */

    if ( status < MIDI_NOTE_OFF )
    {
        status = device_info->running_status;
    }

    /* Ensure the status is sane! */

    if ( status < MIDI_NOTE_OFF )
    {
        /* It's garbage, ignore it */
        return MMSYSERR_NOERROR;
    }

    /* Figure out the message category and channel */

    category = status & 0xF0;
    channel = status & 0x0F;    /* we don't use this */

    data1 = (message & 0x0000FF00) >> 8;
    data2 = (message & 0x00FF0000) >> 16;

    DPRINT("0x%x, %d, %d\n", (int) status, (int) data1, (int) data2);

    /* Filter drums (which are *usually* on channel 10) */
    if ( channel == 10 )
    {
        return MMSYSERR_NOERROR;
    }

    /* Pass to the appropriate message handler */

    switch ( category )
    {
        case MIDI_NOTE_ON :
        {
            PlayNote(device_info, data1, data2);
            break;
        }

        case MIDI_NOTE_OFF :
        {
            StopNote(device_info, data1);
            break;
        }
    }

    return MMSYSERR_NOERROR;
}


#define PACK_MIDI(b1, b2, b3) \
    ((b3 * 65536) + (b2 * 256) + b1);


/*
    Processes a "long" MIDI message (ie, a MIDI message contained within a
    buffer.) This is intended for supporting SysEx data, or blocks of MIDI
    events. However in our case we're only interested in short MIDI messages,
    so we scan the buffer, and each time we encounter a valid status byte
    we start recording it as a new event. Once 3 bytes or a new status is
    received, the event is passed to the short message handler.
*/

MMRESULT
ProcessLongMidiMessage(
    DeviceInfo* device_info,
    MIDIHDR* header)
{
    int index = 0;
    UCHAR* midi_bytes = (UCHAR*) header->lpData;

    int msg_index = 0;
    UCHAR msg[3];

    /* Initialize the buffer */
    msg[0] = msg[1] = msg[2] = 0;

    if ( ! ( header->dwFlags & MHDR_PREPARED ) )
    {
        DPRINT("Not prepared!\n");
        return MIDIERR_UNPREPARED;
    }

    DPRINT("Processing %d bytes of MIDI\n", (int) header->dwBufferLength);

    while ( index < header->dwBufferLength )
    {
        /* New status byte? ( = new event) */
        if ( midi_bytes[index] & 0x80 )
        {
            DWORD short_msg;

            /* Deal with the existing event */

            if ( msg[0] & 0x80 )
            {
                short_msg = PACK_MIDI(msg[0], msg[1], msg[2]);

                DPRINT("Complete msg is 0x%x %d %d\n", (int) msg[0], (int) msg[1], (int) msg[2]);
                ProcessShortMidiMessage(device_info, short_msg);
            }

            /* Set new running status and start recording the event */
            DPRINT("Set new running status\n");
            device_info->running_status = midi_bytes[index];
            msg[0] = midi_bytes[index];
            msg_index = 1;
        }

        /* Unexpected data byte? ( = re-use previous status) */
        else if ( msg_index == 0 )
        {
            if ( device_info->running_status & 0x80 )
            {
                DPRINT("Retrieving running status\n");
                msg[0] = device_info->running_status;
                msg[1] = midi_bytes[index];
                msg_index = 2;
            }
            else
                DPRINT("garbage\n");
        }

        /* Expected data ( = append to message until buffer full) */
        else
        {
            DPRINT("Next byte...\n");
            msg[msg_index] = midi_bytes[index];
            msg_index ++;

            if ( msg_index > 2 )
            {
                DWORD short_msg;

                short_msg = PACK_MIDI(msg[0], msg[1], msg[2]);

                DPRINT("Complete msg is 0x%x %d %d\n", (int) msg[0], (int) msg[1], (int) msg[2]);
                ProcessShortMidiMessage(device_info, short_msg);

                /* Reinit */
                msg_index = 0;
                msg[0] = msg[1] = msg[2] = 0;
            }
        }

        index ++;
    }

    /*
        We're meant to clear MHDR_DONE and set MHDR_INQUEUE but since we
        deal with everything here and now we might as well just say so.
    */
    header->dwFlags |= MHDR_DONE;
    header->dwFlags &= ~ MHDR_INQUEUE;

    DPRINT("Success? %d\n", (int) CallClient(the_device, MOM_DONE, (DWORD) header, 0));

    return MMSYSERR_NOERROR;
}


/*
    Exported function that receives messages from WINMM (the MME API.)
*/

FAR PASCAL
MMRESULT
modMessage(
    UINT device_id,
    UINT message,
    DWORD private_data,
    DWORD parameter1,
    DWORD parameter2)
{
    switch ( message )
    {
        case MODM_GETNUMDEVS :
        {
            /* Only one internal PC speaker device (and even that's too much) */
            DPRINT("MODM_GETNUMDEVS\n");
            return 1;
        }

        case MODM_GETDEVCAPS :
        {
            DPRINT("MODM_GETDEVCAPS\n");
            return GetDeviceCapabilities((MIDIOUTCAPS*) parameter1);
        }

        case MODM_OPEN :
        {
            DPRINT("MODM_OPEN\n");

            return OpenDevice((DeviceInfo**) private_data,
                              (MIDIOPENDESC*) parameter1,
                              parameter2);
        }

        case MODM_CLOSE :
        {
            DPRINT("MODM_CLOSE\n");
            return CloseDevice((DeviceInfo*) private_data);
        }

        case MODM_DATA :
        {
            return ProcessShortMidiMessage((DeviceInfo*) private_data, parameter1);
        }

        case MODM_PREPARE :
        {
            /* We don't bother with this */
            MIDIHDR* hdr = (MIDIHDR*) parameter1;
            hdr->dwFlags |= MHDR_PREPARED;
            return MMSYSERR_NOERROR;
        }

        case MODM_UNPREPARE :
        {
            MIDIHDR* hdr = (MIDIHDR*) parameter1;
            hdr->dwFlags &= ~MHDR_PREPARED;
            return MMSYSERR_NOERROR;
        }

        case MODM_LONGDATA :
        {
            DPRINT("LONGDATA\n");
            return ProcessLongMidiMessage((DeviceInfo*) private_data, (MIDIHDR*) parameter1);
        }

        case MODM_RESET :
        {
            /* TODO */
            break;
        }
    }

    DPRINT("Not supported %d\n", message);

    return MMSYSERR_NOTSUPPORTED;
}


/*
    Driver entrypoint.
*/

FAR PASCAL LONG
DriverProc(
    DWORD driver_id,
    HDRVR driver_handle,
    UINT message,
    LONG parameter1,
    LONG parameter2)
{
    switch ( message )
    {
        case DRV_LOAD :
            DPRINT("DRV_LOAD\n");
            the_device = NULL;
            return 1L;

        case DRV_FREE :
            DPRINT("DRV_FREE\n");
            return 1L;

        case DRV_OPEN :
            DPRINT("DRV_OPEN\n");
            InitializeCriticalSection(&device_lock);
            return 1L;

        case DRV_CLOSE :
            DPRINT("DRV_CLOSE\n");
            return 1L;

        case DRV_ENABLE :
            DPRINT("DRV_ENABLE\n");
            return 1L;

        case DRV_DISABLE :
            DPRINT("DRV_DISABLE\n");
            return 1L;

        /*
            We don't provide configuration capabilities. This used to be
            for things like I/O port, IRQ, DMA settings, etc.
        */

        case DRV_QUERYCONFIGURE :
            DPRINT("DRV_QUERYCONFIGURE\n");
            return 0L;

        case DRV_CONFIGURE :
            DPRINT("DRV_CONFIGURE\n");
            return 0L;

        case DRV_INSTALL :
            DPRINT("DRV_INSTALL\n");
            return DRVCNF_RESTART;
    };

    DPRINT("???\n");

    return DefDriverProc(driver_id,
                         driver_handle,
                         message,
                         parameter1,
                         parameter2);
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -