Author: silverblade
Date: Fri Jan 19 02:26:30 2007
New Revision: 25520
URL:
http://svn.reactos.org/svn/reactos?rev=25520&view=rev
Log:
PC speaker MIDI driver (see README.TXT for info)
Tested on Windows XP but should be compatible with ReactOS
Added:
trunk/reactos/dll/win32/beepmidi/
trunk/reactos/dll/win32/beepmidi/beepmidi.c
trunk/reactos/dll/win32/beepmidi/beepmidi.def
trunk/reactos/dll/win32/beepmidi/beepmidi.rbuild
trunk/reactos/dll/win32/beepmidi/readme.txt
Added: trunk/reactos/dll/win32/beepmidi/beepmidi.c
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/dll/win32/beepmidi/beepmid…
==============================================================================
--- trunk/reactos/dll/win32/beepmidi/beepmidi.c (added)
+++ trunk/reactos/dll/win32/beepmidi/beepmidi.c Fri Jan 19 02:26:30 2007
@@ -1,0 +1,903 @@
+/*
+ BeepMidi :: beep.sys MIDI player
+
+ (c) Andrew Greenwood, 2007.
+
+ Released as open-source software. You may copy, re-distribute and modify
+ this software, provided this copyright notice remains intact.
+
+ Please see the included README.TXT for more information
+
+ HISTORY :
+ 16th January 2007 Started
+ 17th January 2007 Polyphony support and threading added
+ 18th January 2007 Made threading optional, added comments
+*/
+
+/* The timeslice to allocate for all playing notes (in milliseconds) */
+#define TIMESLICE_SIZE 60
+
+/*
+ If this is defined, notes are added to the playing list, even if
+ they already exist. As a result, the note will sound twice during
+ each timeslice. Also each note on will require a corresponding note
+ off event.
+*/
+#define ALLOW_DUPLICATE_NOTES
+
+/*
+ The maximum number of notes that may be playing at any one time.
+ Higher values result in a messier sound as all the frequencies get
+ mashed together. Do not set this below 2. Recommended = 4
+*/
+#define POLYPHONY 3
+
+/*
+ Define CONTINUOUS_NOTES to perform note playback in a separate thread.
+ This was originally the intended behaviour, but after experimentation
+ doesn't sound as good for MIDI files which have a lot going on. If not
+ defined, all playing notes are output in sequence as a new note starts.
+*/
+#define CONTINUOUS_NOTES
+
+#define WIN32_NO_STATUS
+#define NTOS_MODE_USER
+#include <windows.h>
+#include <ndk/ntndk.h>
+#include <stdio.h>
+#include <ntddbeep.h>
+#include <math.h>
+
+#include <mmddk.h>
+#include <mmsystem.h>
+
+#define DPRINT printf
+//#define DPRINT //
+
+/* A few MIDI command categories */
+#define MIDI_NOTE_OFF 0x80
+#define MIDI_NOTE_ON 0x90
+#define MIDI_CONTROL_CHANGE 0xB0
+#define MIDI_PROGRAM 0xC0
+#define MIDI_PITCH_BEND 0xE0
+#define MIDI_SYSTEM 0xFF
+
+/* Specific commands */
+#define MIDI_RESET 0xFF
+
+
+typedef struct _NoteNode
+{
+ struct _NoteNode* next;
+ struct _NoteNode* previous;
+
+ UCHAR note;
+ UCHAR velocity; /* 0 is note-off */
+} NoteNode;
+
+typedef struct _DeviceInfo
+{
+ HDRVR mme_handle;
+ HANDLE kernel_device;
+
+ DWORD callback;
+ DWORD instance;
+ DWORD flags;
+
+ UCHAR running_status;
+
+ DWORD playing_notes_count;
+ NoteNode* note_list;
+ BOOL refresh_notes;
+
+ HANDLE thread_handle;
+ BOOL terminate_thread;
+ HANDLE thread_termination_complete;
+} DeviceInfo;
+
+DeviceInfo* the_device;
+CRITICAL_SECTION device_lock;
+
+void
+FakePrintf(char* str, ...)
+{
+ /* Just to shut the compiler up */
+}
+
+
+/*
+ This is designed to be treated as a thread, however it behaves as a
+ normal function if CONTINUOUS_NOTES is not defined.
+*/
+
+DWORD WINAPI
+ProcessPlayingNotes(
+ LPVOID parameter)
+{
+ DeviceInfo* device_info = (DeviceInfo*) parameter;
+ NTSTATUS status;
+ IO_STATUS_BLOCK io_status_block;
+ DWORD arp_notes;
+
+ DPRINT("Note processing started\n");
+
+ /* We lock the note list only while accessing it */
+
+#ifdef CONTINUOUS_NOTES
+ while ( ! device_info->terminate_thread )
+#endif
+ {
+ NoteNode* node;
+
+ /* Number of notes being arpeggiated */
+ arp_notes = 1;
+
+ EnterCriticalSection(&device_lock);
+
+ /* Calculate how much time to allocate to each playing note */
+
+ DPRINT("%d notes active\n", (int)
device_info->playing_notes_count);
+
+ node = device_info->note_list;
+
+ while ( ( node != NULL ) && ( arp_notes <= POLYPHONY ) )
+ {
+ DPRINT("playing..\n");
+ BEEP_SET_PARAMETERS beep_data;
+ DWORD actually_playing = 0;
+
+ double frequency = node->note;
+ frequency = frequency / 12;
+ frequency = pow(2, frequency);
+ frequency = 8.1758 * frequency;
+
+ if (device_info->playing_notes_count > POLYPHONY)
+ actually_playing = POLYPHONY;
+ else
+ actually_playing = device_info->playing_notes_count;
+
+ DPRINT("Frequency %f\n", frequency);
+
+ // TODO
+ beep_data.Frequency = (DWORD) frequency;
+ beep_data.Duration = TIMESLICE_SIZE / actually_playing; /*
device_info->playing_notes_count; */
+
+ status = NtDeviceIoControlFile(device_info->kernel_device,
+ NULL,
+ NULL,
+ NULL,
+ &io_status_block,
+ IOCTL_BEEP_SET,
+ &beep_data,
+ sizeof(BEEP_SET_PARAMETERS),
+ NULL,
+ 0);
+
+ if ( ! NT_SUCCESS(status) )
+ {
+ DPRINT("ERROR %d\n", (int) GetLastError());
+ }
+
+ SleepEx(beep_data.Duration, TRUE);
+
+ if ( device_info->refresh_notes )
+ {
+ device_info->refresh_notes = FALSE;
+ break;
+ }
+
+ arp_notes ++;
+ node = node->next;
+ }
+
+ LeaveCriticalSection(&device_lock);
+ }
+
+#ifdef CONTINUOUS_NOTES
+ SetEvent(device_info->thread_termination_complete);
+#endif
+
+ return 0;
+}
+
+
+/*
+ Fills a MIDIOUTCAPS structure with information about our device.
+*/
+
+MMRESULT
+GetDeviceCapabilities(
+ MIDIOUTCAPS* caps)
+{
+ /* These are ignored for now */
+ caps->wMid = 0;
+ caps->wPid = 0;
+
+ caps->vDriverVersion = 0x0100;
+
+ memset(caps->szPname, 0, sizeof(caps->szPname));
+ memcpy(caps->szPname, L"PC speaker\0", strlen("PC speaker\0")
* 2);
+
+ caps->wTechnology = MOD_SQSYNTH;
+
+ caps->wVoices = 1; /* We only have one voice */
+ caps->wNotes = POLYPHONY;
+ caps->wChannelMask = 0xFFBF; /* Ignore channel 10 */
+
+ caps->dwSupport = 0;
+
+ return MMSYSERR_NOERROR;
+}
+
+
+/*
+ Helper function that just simplifies calling the application making use
+ of us.
+*/
+
+BOOL
+CallClient(
+ DeviceInfo* device_info,
+ DWORD message,
+ DWORD parameter1,
+ DWORD parameter2)
+{
+ DPRINT("Calling client - callback 0x%x mmhandle 0x%x\n", (int)
device_info->callback, (int) device_info->mme_handle);
+ return DriverCallback(device_info->callback,
+ HIWORD(device_info->flags),
+ device_info->mme_handle,
+ message,
+ device_info->instance,
+ parameter1,
+ parameter2);
+
+}
+
+
+/*
+ Open the kernel-mode device and allocate resources. This opens the
+ BEEP.SYS kernel device.
+*/
+
+MMRESULT
+OpenDevice(
+ DeviceInfo** private_data,
+ MIDIOPENDESC* open_desc,
+ DWORD flags)
+{
+ NTSTATUS status;
+ HANDLE heap;
+ HANDLE kernel_device;
+ UNICODE_STRING beep_device_name;
+ OBJECT_ATTRIBUTES attribs;
+ IO_STATUS_BLOCK status_block;
+
+ /* One at a time.. */
+ if ( the_device )
+ {
+ DPRINT("Already allocated\n");
+ return MMSYSERR_ALLOCATED;
+ }
+
+ /* Make the device name into a unicode string and open it */
+
+ RtlInitUnicodeString(&beep_device_name,
+ L"\\Device\\Beep");
+
+ InitializeObjectAttributes(&attribs,
+ &beep_device_name,
+ 0,
+ NULL,
+ NULL);
+
+ status = NtCreateFile(&kernel_device,
+ FILE_READ_DATA | FILE_WRITE_DATA,
+ &attribs,
+ &status_block,
+ NULL,
+ 0,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ FILE_OPEN_IF,
+ 0,
+ NULL,
+ 0);
+
+ if ( ! NT_SUCCESS(status) )
+ {
+ DPRINT("Could not connect to BEEP device - %d\n", (int)
GetLastError());
+ return MMSYSERR_ERROR;
+ }
+
+ DPRINT("Opened!\n");
+
+ /* Allocate and initialize the device info */
+
+ heap = GetProcessHeap();
+
+ the_device = HeapAlloc(heap, HEAP_ZERO_MEMORY, sizeof(DeviceInfo));
+
+ if ( ! the_device )
+ {
+ DPRINT("Out of memory\n");
+ return MMSYSERR_NOMEM;
+ }
+
+ /* Initialize */
+ the_device->kernel_device = kernel_device;
+ the_device->playing_notes_count = 0;
+ the_device->note_list = NULL;
+ the_device->thread_handle = 0;
+ the_device->terminate_thread = FALSE;
+ the_device->running_status = 0;
+
+ // TODO
+ the_device->mme_handle = (HDRVR) open_desc->hMidi;
+ the_device->callback = open_desc->dwCallback;
+ the_device->instance = open_desc->dwInstance;
+ the_device->flags = flags;
+
+ /* Store the pointer in the user data */
+ *private_data = the_device;
+
+ /* This is threading-related code */
+#ifdef CONTINUOUS_NOTES
+ the_device->thread_termination_complete = CreateEvent(NULL, FALSE, FALSE, NULL);
+
+ if ( ! the_device->thread_termination_complete )
+ {
+ DPRINT("CreateEvent failed\n");
+ HeapFree(heap, 0, the_device);
+ return MMSYSERR_NOMEM;
+ }
+
+ the_device->thread_handle = CreateThread(NULL,
+ 0,
+ ProcessPlayingNotes,
+ (PVOID) the_device,
+ 0,
+ NULL);
+
+ if ( ! the_device->thread_handle )
+ {
+ DPRINT("CreateThread failed\n");
+ CloseHandle(the_device->thread_termination_complete);
+ HeapFree(heap, 0, the_device);
+ return MMSYSERR_NOMEM;
+ }
+#endif
+
+ /* Now we call the client application to say the device is open */
+ DPRINT("Sending MOM_OPEN\n");
+ DPRINT("Success? %d\n", (int) CallClient(the_device, MOM_OPEN, 0, 0));
+
+ return MMSYSERR_NOERROR;
+}
+
+
+/*
+ Close the kernel-mode device.
+*/
+
+MMRESULT
+CloseDevice(DeviceInfo* device_info)
+{
+ HANDLE heap = GetProcessHeap();
+
+ /* If we're working in threaded mode we need to wait for thread to die */
+#ifdef CONTINUOUS_NOTES
+ the_device->terminate_thread = TRUE;
+
+ WaitForSingleObject(the_device->thread_termination_complete, INFINITE);
+
+ CloseHandle(the_device->thread_termination_complete);
+#endif
+
+ /* Let the client application know the device is closing */
+ DPRINT("Sending MOM_CLOSE\n");
+ CallClient(device_info, MOM_CLOSE, 0, 0);
+
+ NtClose(device_info->kernel_device);
+
+ /* Free resources */
+ HeapFree(heap, 0, device_info);
+
+ the_device = NULL;
+
+ return MMSYSERR_NOERROR;
+}
+
+
+/*
+ Removes a note from the playing notes list. If the note is not playing,
+ we just pretend nothing happened.
+*/
+
+MMRESULT
+StopNote(
+ DeviceInfo* device_info,
+ UCHAR note)
+{
+ HANDLE heap = GetProcessHeap();
+ NoteNode* node;
+ NoteNode* prev_node = NULL;
+
+ DPRINT("StopNote\n");
+
+ EnterCriticalSection(&device_lock);
+
+ node = device_info->note_list;
+
+ while ( node != NULL )
+ {
+ if ( node->note == note )
+ {
+ /* Found the note - just remove the node from the list */
+
+ DPRINT("Stopping note %d\n", (int) node->note);
+
+ if ( prev_node != NULL )
+ prev_node->next = node->next;
+ else
+ device_info->note_list = node->next;
+
+ HeapFree(heap, 0, node);
+
+ device_info->playing_notes_count --;
+
+ DPRINT("Note stopped - now playing %d notes\n", (int)
device_info->playing_notes_count);
+
+ LeaveCriticalSection(&device_lock);
+ device_info->refresh_notes = TRUE;
+
+ return MMSYSERR_NOERROR;
+ }
+
+ 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);
+}
Added: trunk/reactos/dll/win32/beepmidi/beepmidi.def
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/dll/win32/beepmidi/beepmid…
==============================================================================
--- trunk/reactos/dll/win32/beepmidi/beepmidi.def (added)
+++ trunk/reactos/dll/win32/beepmidi/beepmidi.def Fri Jan 19 02:26:30 2007
@@ -1,0 +1,12 @@
+; beepmidi.def
+;
+; BeepMidi driver by Andrew Greenwood
+;
+; For ReactOS Operating System
+;
+
+LIBRARY beepmidi.dll
+
+EXPORTS
+DriverProc@20
+modMessage@20
Added: trunk/reactos/dll/win32/beepmidi/beepmidi.rbuild
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/dll/win32/beepmidi/beepmid…
==============================================================================
--- trunk/reactos/dll/win32/beepmidi/beepmidi.rbuild (added)
+++ trunk/reactos/dll/win32/beepmidi/beepmidi.rbuild Fri Jan 19 02:26:30 2007
@@ -1,0 +1,12 @@
+<module name="beepmidi" type="win32dll"
installbase="system32" installname="beepmidi.dll">
+ <importlibrary definition="beepmidi.def" />
+ <include base="beepmidi">.</include>
+ <define name="__USE_W32API" />
+ <define name="UNICODE" />
+ <define name="_UNICODE" />
+ <library>ntdll</library>
+ <library>kernel32</library>
+ <library>user32</library>
+ <library>winmm</library>
+ <file>beepmidi.c</file>
+</module>
Added: trunk/reactos/dll/win32/beepmidi/readme.txt
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/dll/win32/beepmidi/readme.…
==============================================================================
--- trunk/reactos/dll/win32/beepmidi/readme.txt (added)
+++ trunk/reactos/dll/win32/beepmidi/readme.txt Fri Jan 19 02:26:30 2007
@@ -1,0 +1,51 @@
+BEEPMIDI :: BEEP.SYS MIDI DRIVER
+(c) Andrew Greenwood, 2007.
+
+http://www.silverblade.co.uk
+
+Released as open-source software. You may copy, re-distribute and modify
+this software, provided this copyright notice remains intact.
+
+WHAT'S THIS ?
+ BeepMidi is a MME MIDI driver for NT-compatible operating systems,
+ which uses BEEP.SYS (the kernel-mode PC speaker driver) to play
+ MIDI data. It installs as a standard MIDI output device and can even
+ be selected as your default MIDI output device. The fundamental
+ code for interacting with BEEP.SYS was taken from ReactOS' kernel32
+ module.
+
+WHY WAS THIS WRITTEN ?
+ Primarily for educational reasons - in the process, I've learned more
+ about the driver side of the MME API and how to interact with kernel
+ device drivers. It aids as a good starting point from which to
+ move on to bigger and better things :)
+
+HOW TO INSTALL :
+ Copy the file to C:\WINDOWS\SYSTEM32\BEEPMIDI.DLL
+
+ Go into HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\
+ Drivers32 in RegEdit and look for the "midi" entries on the right hand
+ side. Find the highest numbered one (eg: midi1) and create a new STRING
+ value. Give it another midi name, but one above the current highest
+ entry present (eg: midi2.)
+
+ You'll now see a "PC Speaker" entry in Sound & Audio Devices.
+
+TWEAKING:
+ See the comments toward the top of beepmidi.c for tweakable driver
+ parameters. These can only be adjusted in the source code at present.
+
+FEATURES :
+ * Supports note-on and note-off messages on channels 1-9 and 11-16
+ (channel 10 is rhythm, which is not supported.)
+ * Fake polyphony (actually just arpeggiates playing notes!)
+ * Threaded design for continuous playback (optional.)
+
+ROOM FOR IMPROVEMENT :
+ * Pitch bend is not supported
+ * Velocity could determine timeslice
+ * Should wait for timeslice to complete before adding/removing notes
+ * Would be nice to allow configuration of polyphony etc. via Control Panel
+
+BUGS :
+ * Crashes when used with Windows Media Player (mplayer2 is fine though)