Removed hooks for prepare/unprepare/stream of WAVEHDR. These are handled
authorAndrew Greenwood <silverblade@reactos.org>
Wed, 18 Feb 2009 19:10:02 +0000 (19:10 +0000)
committerAndrew Greenwood <silverblade@reactos.org>
Wed, 18 Feb 2009 19:10:02 +0000 (19:10 +0000)
internally by MME-Buddy. Instead, it simply provides a hook to do the
actual streaming, complete with an OVERLAPPED structure and I/O completion
routine. Also started imlpementation of wdmaud.drv to begin interaction
with code janderwald is working on.

svn path=/trunk/; revision=39667

reactos/dll/win32/sndblst/sndblst.c
reactos/dll/win32/wdmaud.drv/wdmaud.c
reactos/dll/win32/wdmaud.drv/wdmaud.rbuild
reactos/include/reactos/libs/sound/mmebuddy.h
reactos/lib/drivers/sound/mmebuddy/deviceinstance.c
reactos/lib/drivers/sound/mmebuddy/mmebuddy.rbuild
reactos/lib/drivers/sound/mmebuddy/wave/header.c
reactos/lib/drivers/sound/mmebuddy/wave/streaming.c [new file with mode: 0644]
reactos/lib/drivers/sound/mmebuddy/wave/wodMessage.c
reactos/lib/drivers/sound/mment4/control.c

index 28cc903..076e0ca 100644 (file)
@@ -98,9 +98,8 @@ BOOLEAN FoundDevice(
     FuncTable.SetWaveFormat = SetNt4WaveDeviceFormat;
     FuncTable.Open = OpenNt4SoundDevice;
     FuncTable.Close = CloseNt4SoundDevice;
-    FuncTable.PrepareWaveHeader = NULL;
-    FuncTable.UnprepareWaveHeader = NULL;
-    FuncTable.SubmitWaveHeader = NULL;
+    FuncTable.CommitWaveBuffer = WriteFileEx_Committer;
+    //FuncTable.SubmitWaveHeaderToDevice = SubmitWaveHeaderToDevice;
 
     SetSoundDeviceFunctionTable(SoundDevice, &FuncTable);
 
index 2a3d61d..2be4f19 100644 (file)
 /*
+ * PROJECT:     ReactOS Sound System
+ * LICENSE:     GPL - See COPYING in the top level directory
+ * FILE:        dll/win32/wdmaud.drv/wdmaud.c
  *
- * PROJECT:         ReactOS WDM Audio driver mapper
- * FILE:            dll/win32/wdmaud.drv/wdmaud.c
- * PURPOSE:         wdmaud.drv
- * PROGRAMMER:      Dmitry Chapyshev (dmitry@reactos.org)
+ * PURPOSE:     WDM Audio Driver (User-mode part)
+ * PROGRAMMERS: Andrew Greenwood (silverblade@reactos.org)
+ *
+ * NOTES:       Looking for wodMessage & co? You won't find them here. Try
+ *              the MME Buddy library, which is where these routines are
+ *              actually implemented.
  *
- * UPDATE HISTORY:
- *      25/05/2008  Created
  */
 
-#include <stdarg.h>
-
 #include <windows.h>
-#include <mmsystem.h>
+#include <ntddsnd.h>
+#include <sndtypes.h>
 #include <mmddk.h>
-#include <mmreg.h>
-#include <debug.h>
-
-DWORD APIENTRY
-mxdMessage(UINT uDevice,
-           UINT uMsg,
-           DWORD dwUser,
-           DWORD dwParam1,
-           DWORD dwParam2)
-{
-    DPRINT1("mxdMessage(%04X, %04X, %08X, %08X, %08X);\n", uDevice, uMsg, dwUser, dwParam1, dwParam2);
-
-    switch (uMsg)
-    {
-        case MXDM_INIT:
-        break;
-
-        case MXDM_GETNUMDEVS:
-        break;
-
-        case MXDM_GETDEVCAPS:
-        break;
-
-        case MXDM_OPEN:
-        break;
-
-        case MXDM_CLOSE:
-        break;
-
-        case MXDM_GETLINEINFO:
-        break;
-
-        case MXDM_GETLINECONTROLS:
-        break;
-
-        case MXDM_GETCONTROLDETAILS:
-        break;
-
-        case MXDM_SETCONTROLDETAILS:
-        break;
-    }
-
-    return MMSYSERR_NOTSUPPORTED;
-}
-
-DWORD APIENTRY
-auxMessage(UINT uDevice,
-           UINT uMsg,
-           DWORD dwUser,
-           DWORD dwParam1,
-           DWORD dwParam2)
-{
-    DPRINT1("auxMessage(%04X, %04X, %08X, %08X, %08X);\n", uDevice, uMsg, dwUser, dwParam1, dwParam2);
-
-    switch (uMsg)
-    {
-        case AUXDM_GETDEVCAPS:
-
-        break;
-
-        case AUXDM_GETNUMDEVS:
-
-        break;
-
-        case AUXDM_GETVOLUME:
-
-        break;
-
-        case AUXDM_SETVOLUME:
-
-        break;
-
-        default:
-            return MMSYSERR_NOTSUPPORTED;
-    }
-
-    return MMSYSERR_NOTSUPPORTED;
-}
-
-DWORD APIENTRY
-wodMessage(UINT uDevice,
-           UINT uMsg,
-           DWORD dwUser,
-           DWORD dwParam1,
-           DWORD dwParam2)
-{
-    DPRINT1("wodMessage(%04X, %04X, %08X, %08X, %08X);\n", uDevice, uMsg, dwUser, dwParam1, dwParam2);
-
-    switch (uMsg)
-    {
-        case WODM_GETNUMDEVS:
-        break;
-
-        case WODM_GETDEVCAPS:
-        break;
+#include <mmebuddy.h>
 
-        case WODM_OPEN:
-        break;
+#define KERNEL_DEVICE_NAME      L"\\\\Device\\wdmaud"
 
-        case WODM_CLOSE:
-        break;
+HANDLE KernelHandle = INVALID_HANDLE_VALUE;
 
-        case WODM_WRITE:
-        break;
-
-        case WODM_PAUSE:
-        break;
-
-        case WODM_RESTART:
-        break;
-
-        case WODM_RESET:
-        break;
-
-        case WODM_BREAKLOOP:
-        break;
-
-        case WODM_GETPOS:
-        break;
-
-        case WODM_SETPITCH:
-        break;
-
-        case WODM_SETVOLUME:
-        break;
-
-        case WODM_SETPLAYBACKRATE:
-        break;
-
-        case WODM_GETPITCH:
-        break;
-
-        case WODM_GETVOLUME:
-        break;
-
-        case WODM_GETPLAYBACKRATE:
-        break;
-
-        default:
-            return MMSYSERR_NOTSUPPORTED;
-    }
-
-    return MMSYSERR_NOTSUPPORTED;
-}
-
-DWORD APIENTRY
-widMessage(UINT uDevice,
-           UINT uMsg,
-           DWORD dwUser,
-           DWORD dwParam1,
-           DWORD dwParam2)
+APIENTRY LONG
+DriverProc(
+    DWORD DriverId,
+    HANDLE DriverHandle,
+    UINT Message,
+    LONG Parameter1,
+    LONG Parameter2)
 {
-    DPRINT1("widMessage(%04X, %04X, %08X, %08X, %08X);\n", uDevice, uMsg, dwUser, dwParam1, dwParam2);
+    MMRESULT Result;
 
-    switch (uMsg)
+    switch ( Message )
     {
-        case WIDM_GETNUMDEVS:
-        break;
-
-        case WIDM_GETDEVCAPS:
-        break;
-
-        case WIDM_OPEN:
-        break;
-
-        case WIDM_CLOSE:
-        break;
-
-        case WIDM_ADDBUFFER:
-        break;
-
-        case WIDM_STOP:
-        break;
-
-        case WIDM_START:
-        break;
-
-        case WIDM_RESET:
-        break;
-
-        case WIDM_GETPOS:
-        break;
-
-        default:
-            return MMSYSERR_NOTSUPPORTED;
+        case DRV_LOAD :
+        {
+            SND_TRACE(L"DRV_LOAD\n");
+
+            Result = InitEntrypointMutexes();
+
+            if ( ! MMSUCCESS(Result) )
+                return 0L;
+
+            KernelHandle = CreateFile(KERNEL_DEVICE_NAME,
+                                      GENERIC_READ | GENERIC_WRITE,
+                                      FILE_SHARE_WRITE, // ok?
+                                      NULL,
+                                      OPEN_EXISTING,
+                                      FILE_FLAG_OVERLAPPED,
+                                      NULL);
+
+            if ( KernelHandle == INVALID_HANDLE_VALUE )
+            {
+                SND_ERR(L"Failed to open %s", KERNEL_DEVICE_NAME);
+                CleanupEntrypointMutexes();
+
+                UnlistAllSoundDevices();
+
+                return 0L;
+            }
+
+            SND_TRACE(L"Initialisation complete\n");
+
+            return 1L;
+        }
+
+        case DRV_FREE :
+        {
+            SND_TRACE(L"DRV_FREE\n");
+
+            if ( KernelHandle != INVALID_HANDLE_VALUE )
+            {
+                CloseHandle(KernelHandle);
+                KernelHandle = INVALID_HANDLE_VALUE;
+            }
+
+            /* TODO: Clean up the path names! */
+            UnlistAllSoundDevices();
+
+            CleanupEntrypointMutexes();
+
+            SND_TRACE(L"Unfreed memory blocks: %d\n",
+                      GetMemoryAllocationCount());
+
+            return 1L;
+        }
+
+        case DRV_ENABLE :
+        case DRV_DISABLE :
+        {
+            SND_TRACE(L"DRV_ENABLE / DRV_DISABLE\n");
+            return 1L;
+        }
+
+        case DRV_OPEN :
+        case DRV_CLOSE :
+        {
+            SND_TRACE(L"DRV_OPEN / DRV_CLOSE\n");
+            return 1L;
+        }
+
+        case DRV_QUERYCONFIGURE :
+        {
+            SND_TRACE(L"DRV_QUERYCONFIGURE");
+            return 0L;
+        }
+        case DRV_CONFIGURE :
+            return DRVCNF_OK;
+
+        default :
+            SND_TRACE(L"Unhandled message %d\n", Message);
+            return DefDriverProc(DriverId,
+                                 DriverHandle,
+                                 Message,
+                                 Parameter1,
+                                 Parameter2);
     }
-
-    return MMSYSERR_NOTSUPPORTED;
-}
-
-DWORD APIENTRY
-modMessage(UINT uDevice,
-           UINT uMsg,
-           DWORD dwUser,
-           DWORD dwParam1,
-           DWORD dwParam2)
-{
-    DPRINT1("modMessage(%04X, %04X, %08X, %08X, %08X);\n", uDevice, uMsg, dwUser, dwParam1, dwParam2);
-
-    return MMSYSERR_NOTSUPPORTED;
 }
 
-LRESULT APIENTRY
-DriverProc(DWORD dwDriverID,
-           HDRVR hDriver,
-           UINT uiMessage,
-           LPARAM lParam1,
-           LPARAM lParam2)
-{
-    return DefDriverProc(dwDriverID, hDriver, uiMessage, lParam1, lParam2);
-}
 
-BOOL WINAPI
-DllMain(IN HINSTANCE hinstDLL,
-        IN DWORD dwReason,
-        IN LPVOID lpvReserved)
+BOOL WINAPI DllMain(
+    HINSTANCE hinstDLL,
+    DWORD fdwReason,
+    LPVOID lpvReserved)
 {
-    switch (dwReason)
+    switch ( fdwReason )
     {
-        case DLL_PROCESS_ATTACH:
-            DisableThreadLibraryCalls(hinstDLL);
+        case DLL_PROCESS_ATTACH :
+            SND_TRACE(L"WDMAUD.DRV - Process attached\n");
+            break;
+        case DLL_PROCESS_DETACH :
+            SND_TRACE(L"WDMAUD.DRV - Process detached\n");
+            break;
+        case DLL_THREAD_ATTACH :
+            SND_TRACE(L"WDMAUD.DRV - Thread attached\n");
+            break;
+        case DLL_THREAD_DETACH :
+            SND_TRACE(L"WDMAUD.DRV - Thread detached\n");
             break;
     }
 
     return TRUE;
 }
-
index b2a0640..7237b59 100644 (file)
@@ -1,12 +1,14 @@
-<module name="wdmaud.drv" type="win32dll" baseaddress="${BASEADDRESS_WDMAUD}" installbase="system32" installname="wdmaud.drv">
+<module name="wdmaud.drv" type="win32dll" baseaddress="${BASEADDRESS_WDMAUD}" installbase="system32" installname="wdmaud.drv" unicode="yes">
        <importlibrary definition="wdmaud.spec" />
        <include base="wdmaud.drv">.</include>
-       <define name="_DISABLE_TIDENTS" />
-       <library>advapi32</library>
+       <include base="ReactOS">include/reactos/libs/sound</include>
+       <define name="DEBUG_NT4" /><!-- Use custom debug routines -->
+       <library>mmebuddy</library>
+       <library>ntdll</library>
        <library>kernel32</library>
-       <library>winmm</library>
        <library>user32</library>
        <library>winmm</library>
+       <library>advapi32</library>
        <file>wdmaud.c</file>
        <file>wdmaud.rc</file>
 </module>
index c4b2291..2cf40fb 100644 (file)
@@ -32,7 +32,7 @@
         MessageBox(0, dbg_popup_msg, dbg_popup_title, MB_OK | MB_TASKMODAL); \
     }
 
-#ifdef DEBUG_NT4
+#ifndef NDEBUG
     #define SND_ERR(...) \
         { \
             WCHAR dbg_popup_msg[1024]; \
             } \
         }
 
+    #define DUMP_WAVEHDR_QUEUE(sound_device_instance) \
+        { \
+            PWAVEHDR CurrDumpHdr = sound_device_instance->HeadWaveHeader; \
+            SND_TRACE(L"-- Current wave header list --\n"); \
+            while ( CurrDumpHdr ) \
+            { \
+                SND_TRACE(L"%x | %d bytes | flags: %x\n", CurrDumpHdr->lpData, \
+                          CurrDumpHdr->dwBufferLength, \
+                          CurrDumpHdr->dwFlags); \
+                CurrDumpHdr = CurrDumpHdr->lpNext; \
+            } \
+        }
+
 #else
-    #define SND_ERR(...) while ( 0 ) do {}
-    #define SND_WARN(...) while ( 0 ) do {}
-    #define SND_TRACE(...) while ( 0 ) do {}
-    #define SND_ASSERT(condition) while ( 0 ) do {}
+    #define SND_ERR(...) do {} while ( 0 )
+    #define SND_WARN(...) do {} while ( 0 )
+    #define SND_TRACE(...) do {} while ( 0 )
+    #define SND_ASSERT(condition) do {} while ( 0 )
 #endif
 
 /*
@@ -157,6 +170,28 @@ DEFINE_GETCAPS_FUNCTYPE(MMGETMIDIINCAPS_FUNC,  LPMIDIINCAPS );
 struct _SOUND_DEVICE;
 struct _SOUND_DEVICE_INSTANCE;
 
+
+/*
+    By extending the OVERLAPPED structure, it becomes possible to provide the
+    I/O completion routines with additional information.
+*/
+
+typedef struct _SOUND_OVERLAPPED
+{
+    OVERLAPPED Standard;
+    struct _SOUND_DEVICE_INSTANCE* SoundDeviceInstance;
+    PWAVEHDR Header;
+} SOUND_OVERLAPPED, *PSOUND_OVERLAPPED;
+
+typedef MMRESULT (*WAVE_COMMIT_FUNC)(
+    IN  struct _SOUND_DEVICE_INSTANCE* SoundDeviceInstance,
+    IN  PVOID OffsetPtr,
+    IN  DWORD Bytes,
+    IN  PSOUND_OVERLAPPED Overlap,
+    IN  LPOVERLAPPED_COMPLETION_ROUTINE CompletionRoutine);
+
+
+
 typedef MMRESULT (*MMWAVEQUERYFORMATSUPPORT_FUNC)(
     IN  struct _SOUND_DEVICE* Device,
     IN  PWAVEFORMATEX WaveFormat,
@@ -179,6 +214,11 @@ typedef MMRESULT (*MMWAVEHEADER_FUNC)(
     IN  struct _SOUND_DEVICE_INSTANCE* SoundDeviceInstance,
     IN  PWAVEHDR WaveHeader);
 
+typedef MMRESULT (*MMBUFFER_FUNC)(
+    IN  struct _SOUND_DEVICE_INSTANCE* SoundDeviceInstance,
+    IN  PVOID Buffer,
+    IN  DWORD Length);
+
 typedef struct _MMFUNCTION_TABLE
 {
     union
@@ -196,11 +236,19 @@ typedef struct _MMFUNCTION_TABLE
     MMWAVEQUERYFORMATSUPPORT_FUNC   QueryWaveFormatSupport;
     MMWAVESETFORMAT_FUNC            SetWaveFormat;
 
-    MMWAVEHEADER_FUNC               PrepareWaveHeader;
-    MMWAVEHEADER_FUNC               UnprepareWaveHeader;
-    MMWAVEHEADER_FUNC               SubmitWaveHeader;
+    WAVE_COMMIT_FUNC                CommitWaveBuffer;
+
+    // Redundant
+    //MMWAVEHEADER_FUNC               PrepareWaveHeader;
+    //MMWAVEHEADER_FUNC               UnprepareWaveHeader;
+    //MMWAVEHEADER_FUNC               WriteWaveHeader;
+
+    //MMWAVEHEADER_FUNC               SubmitWaveHeaderToDevice;
+    //MMBUFFER_FUNC                   CompleteBuffer;
 } MMFUNCTION_TABLE, *PMMFUNCTION_TABLE;
 
+
+
 typedef MMRESULT (*SOUND_THREAD_REQUEST_HANDLER)(
     IN  struct _SOUND_DEVICE_INSTANCE* SoundDeviceInstance,
     IN  PVOID Parameter);
@@ -252,8 +300,32 @@ typedef struct _SOUND_DEVICE_INSTANCE
         DWORD ClientCallback;
         DWORD ClientCallbackInstanceData;
     } WinMM;
+
+    /* DO NOT TOUCH THESE OUTSIDE OF THE SOUND THREAD */
+
+    union
+    {
+        PWAVEHDR HeadWaveHeader;
+    };
+
+    union
+    {
+        PWAVEHDR TailWaveHeader;
+    };
+
+    PWAVEHDR WaveLoopStart;
+    PWAVEHDR CurrentWaveHeader;
+    DWORD OutstandingBuffers;
 } SOUND_DEVICE_INSTANCE, *PSOUND_DEVICE_INSTANCE;
 
+/* This lives in WAVEHDR.reserved */
+typedef struct _WAVEHDR_EXTENSION
+{
+    DWORD BytesCommitted;
+    DWORD BytesCompleted;
+} WAVEHDR_EXTENSION, *PWAVEHDR_EXTENSION;
+
+
 /*
     reentrancy.c
 */
@@ -308,8 +380,8 @@ MmeCloseDevice(
 #define MmeUnprepareWaveHeader(private_handle, header) \
     UnprepareWaveHeader((PSOUND_DEVICE_INSTANCE)private_handle, (PWAVEHDR)header)
 
-#define MmeEnqueueWaveHeader(private_handle, header) \
-    EnqueueWaveHeader((PSOUND_DEVICE_INSTANCE)private_handle, (PWAVEHDR)header)
+#define MmeWriteWaveHeader(private_handle, header) \
+    WriteWaveHeader((PSOUND_DEVICE_INSTANCE)private_handle, (PWAVEHDR)header)
 
 
 /*
@@ -493,6 +565,16 @@ SetWaveDeviceFormat(
     wave/header.c
 */
 
+MMRESULT
+EnqueueWaveHeader(
+    PSOUND_DEVICE_INSTANCE SoundDeviceInstance,
+    IN  PVOID Parameter);
+
+VOID
+CompleteWaveHeader(
+    IN  PSOUND_DEVICE_INSTANCE SoundDeviceInstance,
+    IN  PWAVEHDR Header);
+
 MMRESULT
 PrepareWaveHeader(
     IN  PSOUND_DEVICE_INSTANCE SoundDeviceInstance,
@@ -504,11 +586,40 @@ UnprepareWaveHeader(
     IN  PWAVEHDR Header);
 
 MMRESULT
-EnqueueWaveHeader(
+WriteWaveHeader(
     IN  PSOUND_DEVICE_INSTANCE SoundDeviceInstance,
     IN  PWAVEHDR Header);
 
 
+/*
+    wave/streaming.c
+*/
+
+MMRESULT
+DoWaveStreaming(
+    IN  PSOUND_DEVICE_INSTANCE SoundDeviceInstance);
+
+VOID CALLBACK
+CompleteIO(
+    IN  DWORD dwErrorCode,
+    IN  DWORD dwNumberOfBytesTransferred,
+    IN  LPOVERLAPPED lpOverlapped);
+
+MMRESULT
+CommitWaveHeaderToKernelDevice(
+    IN  PSOUND_DEVICE_INSTANCE SoundDeviceInstance,
+    IN  PWAVEHDR Header,
+    IN  WAVE_COMMIT_FUNC CommitFunction);
+
+MMRESULT
+WriteFileEx_Committer(
+    IN  PSOUND_DEVICE_INSTANCE SoundDeviceInstance,
+    IN  PVOID OffsetPtr,
+    IN  DWORD Length,
+    IN  PSOUND_OVERLAPPED Overlap,
+    IN  LPOVERLAPPED_COMPLETION_ROUTINE CompletionRoutine);
+
+
 /*
     kernel.c
 */
index 64dcbff..922ed1d 100644 (file)
@@ -186,6 +186,14 @@ CreateSoundDeviceInstance(
     (*SoundDeviceInstance)->WinMM.ClientCallbackInstanceData = 0;
     (*SoundDeviceInstance)->WinMM.Flags = 0;
 
+    /* Initialise the members of the struct used by the sound thread */
+    (*SoundDeviceInstance)->HeadWaveHeader = NULL;
+    (*SoundDeviceInstance)->TailWaveHeader = NULL;
+
+    (*SoundDeviceInstance)->CurrentWaveHeader = NULL;
+    (*SoundDeviceInstance)->OutstandingBuffers = 0;
+    // TODO: Loop
+
     /* Create the streaming thread (TODO - is this for wave only?) */
     Result = CreateSoundThread(&(*SoundDeviceInstance)->Thread);
     if ( ! MMSUCCESS(Result) )
index fe19ac5..b228b84 100644 (file)
@@ -16,7 +16,8 @@
                <file>widMessage.c</file>
                <file>wodMessage.c</file>
                <file>format.c</file>
-        <file>header.c</file>
+               <file>header.c</file>
+               <file>streaming.c</file>
        </directory>
        <directory name="midi">
                <file>midMessage.c</file>
index fbb0060..0cfb553 100644 (file)
@@ -1,9 +1,9 @@
 /*
  * PROJECT:     ReactOS Sound System "MME Buddy" Library
  * LICENSE:     GPL - See COPYING in the top level directory
- * FILE:        lib/drivers/sound/mmebuddy/header.c
+ * FILE:        lib/drivers/sound/mmebuddy/wave/header.c
  *
- * PURPOSE:     Wave header preparation routines
+ * PURPOSE:     Wave header preparation and submission routines
  *
  * PROGRAMMERS: Andrew Greenwood (silverblade@reactos.org)
 */
@@ -13,6 +13,7 @@
 #include <mmddk.h>
 #include <ntddsnd.h>
 #include <mmebuddy.h>
+#include <sndtypes.h>
 
 
 /*
@@ -59,6 +60,25 @@ WaveHeaderOperation(
 }
 
 
+/*
+    SanitizeWaveHeader
+        Clean up a header / reinitialize
+*/
+
+VOID
+SanitizeWaveHeader(
+    PWAVEHDR Header)
+{
+    PWAVEHDR_EXTENSION Extension = (PWAVEHDR_EXTENSION) Header->reserved;
+    SND_ASSERT( Extension );
+
+    Header->dwBytesRecorded = 0;
+
+    Extension->BytesCommitted = 0;
+    Extension->BytesCompleted = 0;
+}
+
+
 /*
     The following routines are basically handlers for:
     - WODM_PREPARE
@@ -78,6 +98,7 @@ PrepareWaveHeader(
     MMRESULT Result;
     PSOUND_DEVICE SoundDevice;
     PMMFUNCTION_TABLE FunctionTable;
+    PWAVEHDR_EXTENSION Extension;
 
     VALIDATE_MMSYS_PARAMETER( IsValidSoundDeviceInstance(SoundDeviceInstance) );
     VALIDATE_MMSYS_PARAMETER( Header );
@@ -92,12 +113,18 @@ PrepareWaveHeader(
     if ( ! MMSUCCESS(Result) )
         return TranslateInternalMmResult(Result);
 
-    if ( ! FunctionTable->PrepareWaveHeader )
-        return MMSYSERR_NOTSUPPORTED;
+    Extension = AllocateStruct(WAVEHDR_EXTENSION);
+    if ( ! Extension )
+        return MMSYSERR_NOMEM;
+
+    Header->reserved = (DWORD_PTR) Extension;
+    Extension->BytesCommitted = 0;
+    Extension->BytesCompleted = 0;
+
+    /* Configure the flags */
+    Header->dwFlags |= WHDR_PREPARED;
 
-    return WaveHeaderOperation(FunctionTable->PrepareWaveHeader,
-                               SoundDeviceInstance,
-                               Header);
+    return MMSYSERR_NOERROR;
 }
 
 MMRESULT
@@ -108,6 +135,7 @@ UnprepareWaveHeader(
     MMRESULT Result;
     PSOUND_DEVICE SoundDevice;
     PMMFUNCTION_TABLE FunctionTable;
+    PWAVEHDR_EXTENSION Extension;
 
     VALIDATE_MMSYS_PARAMETER( IsValidSoundDeviceInstance(SoundDeviceInstance) );
     VALIDATE_MMSYS_PARAMETER( Header );
@@ -122,16 +150,18 @@ UnprepareWaveHeader(
     if ( ! MMSUCCESS(Result) )
         return TranslateInternalMmResult(Result);
 
-    if ( ! FunctionTable->UnprepareWaveHeader )
-        return MMSYSERR_NOTSUPPORTED;
+    SND_ASSERT( Header->reserved );
+    Extension = (PWAVEHDR_EXTENSION) Header->reserved;
+    FreeMemory(Extension);
 
-    return WaveHeaderOperation(FunctionTable->UnprepareWaveHeader,
-                               SoundDeviceInstance,
-                               Header);
+    /* Configure the flags */
+    Header->dwFlags &= ~WHDR_PREPARED;
+
+    return MMSYSERR_NOERROR;
 }
 
 MMRESULT
-EnqueueWaveHeader(
+WriteWaveHeader(
     IN  PSOUND_DEVICE_INSTANCE SoundDeviceInstance,
     IN  PWAVEHDR Header)
 {
@@ -152,7 +182,7 @@ EnqueueWaveHeader(
     if ( ! MMSUCCESS(Result) )
         return TranslateInternalMmResult(Result);
 
-    if ( ! FunctionTable->SubmitWaveHeader )
+    if ( ! FunctionTable->CommitWaveBuffer )
         return MMSYSERR_NOTSUPPORTED;
 
     /*
@@ -164,20 +194,160 @@ EnqueueWaveHeader(
     VALIDATE_MMSYS_PARAMETER( Header->dwFlags & WHDR_PREPARED );
     VALIDATE_MMSYS_PARAMETER( ! (Header->dwFlags & WHDR_INQUEUE) );
 
+    SanitizeWaveHeader(Header);
+
     /* Clear the "done" flag for the buffer */
     Header->dwFlags &= ~WHDR_DONE;
 
-    Result =  WaveHeaderOperation(FunctionTable->SubmitWaveHeader,
-                                  SoundDeviceInstance,
-                                  Header);
+    Result = CallSoundThread(SoundDeviceInstance,
+                             EnqueueWaveHeader,
+                             Header);
 
-    if ( ! MMSUCCESS(Result) )
+    return Result;
+}
+
+
+/*
+    EnqueueWaveHeader
+        Put the header in the record/playback queue. This is performed within
+        the context of the sound thread, it must NEVER be called from another
+        thread.
+
+    CompleteWaveHeader
+        Set the header information to indicate that it has finished playing,
+        and return it to the client application. This again must be called
+        within the context of the sound thread.
+*/
+
+MMRESULT
+EnqueueWaveHeader(
+    PSOUND_DEVICE_INSTANCE SoundDeviceInstance,
+    IN  PVOID Parameter)
+{
+    VALIDATE_MMSYS_PARAMETER( SoundDeviceInstance );
+    VALIDATE_MMSYS_PARAMETER( Parameter );
+
+    PWAVEHDR WaveHeader = (PWAVEHDR) Parameter;
+
+    /* Initialise */
+    WaveHeader->lpNext = NULL;
+
+    /* Set the "in queue" flag */
+    WaveHeader->dwFlags |= WHDR_INQUEUE;
+
+    if ( ! SoundDeviceInstance->TailWaveHeader )
+    {
+        /* This is the first header in the queue */
+        SND_TRACE(L"Enqueued first wave header\n");
+        SoundDeviceInstance->HeadWaveHeader = WaveHeader;
+        SoundDeviceInstance->TailWaveHeader = WaveHeader;
+
+        DoWaveStreaming(SoundDeviceInstance);
+    }
+    else
     {
-        return Result;
+        /* There are already queued headers - make this one the tail */
+        SND_TRACE(L"Enqueued next wave header\n");
+        SoundDeviceInstance->TailWaveHeader->lpNext = WaveHeader;
+        SoundDeviceInstance->TailWaveHeader = WaveHeader;
+
+        DoWaveStreaming(SoundDeviceInstance);
     }
 
-    /* Set the "in queue" flag if everything was OK */
-    Header->dwFlags |= WHDR_INQUEUE;
+    DUMP_WAVEHDR_QUEUE(SoundDeviceInstance);
 
     return MMSYSERR_NOERROR;
 }
+
+VOID
+CompleteWaveHeader(
+    IN  PSOUND_DEVICE_INSTANCE SoundDeviceInstance,
+    IN  PWAVEHDR Header)
+{
+    PWAVEHDR PrevHdr = NULL, CurrHdr = NULL;
+    PWAVEHDR_EXTENSION Extension;
+    PSOUND_DEVICE SoundDevice;
+    MMDEVICE_TYPE DeviceType;
+    MMRESULT Result;
+
+    SND_TRACE(L"BUFFER COMPLETE :)\n");
+
+    // TODO: Set header flags?
+    // TODO: Call client
+    // TODO: Streaming
+
+    //DoWaveStreaming(SoundDeviceInstance);
+
+    Result = GetSoundDeviceFromInstance(SoundDeviceInstance, &SoundDevice);
+    SND_ASSERT( MMSUCCESS(Result) );
+    Result = GetSoundDeviceType(SoundDevice, &DeviceType);
+    SND_ASSERT( MMSUCCESS(Result) );
+
+    Extension = (PWAVEHDR_EXTENSION)Header->reserved;
+    SND_ASSERT( Extension );
+
+    /* Remove the header from the queue, like so */
+    if ( SoundDeviceInstance->HeadWaveHeader == Header )
+    {
+        SoundDeviceInstance->HeadWaveHeader = Header->lpNext;
+
+        SND_TRACE(L"Dropping head node\n");
+
+        /* If nothing after the head, then there is no tail */
+        if ( Header->lpNext == NULL )
+        {
+            SND_TRACE(L"Dropping tail node\n");
+            SoundDeviceInstance->TailWaveHeader = NULL;
+        }
+    }
+    else
+    {
+        PrevHdr = NULL;
+        CurrHdr = SoundDeviceInstance->HeadWaveHeader;
+
+        SND_TRACE(L"Relinking nodes\n");
+
+        while ( CurrHdr != Header )
+        {
+            PrevHdr = CurrHdr;
+            CurrHdr = CurrHdr->lpNext;
+            SND_ASSERT( CurrHdr );
+        }
+
+        SND_ASSERT( PrevHdr );
+
+        PrevHdr->lpNext = CurrHdr->lpNext;
+
+        /* If this is the tail node, update the tail */
+        if ( Header->lpNext == NULL )
+        {
+            SND_TRACE(L"Updating tail node\n");
+            SoundDeviceInstance->TailWaveHeader = PrevHdr;
+        }
+    }
+
+    /* Make sure we're not using this as the current buffer any more, either! */
+    if ( SoundDeviceInstance->CurrentWaveHeader == Header )
+    {
+        SoundDeviceInstance->CurrentWaveHeader = Header->lpNext;
+    }
+
+    DUMP_WAVEHDR_QUEUE(SoundDeviceInstance);
+
+    SND_TRACE(L"Returning buffer to client...\n");
+
+    /* Update the header */
+    Header->dwFlags &= ~WHDR_INQUEUE;
+    Header->dwFlags |= WHDR_DONE;
+
+    if ( DeviceType == WAVE_IN_DEVICE_TYPE )
+    {
+        // FIXME: We won't be called on incomplete buffer!
+        Header->dwBytesRecorded = Extension->BytesCompleted;
+    }
+
+    /* Safe to do this without thread protection, as we're done with the header */
+    NotifyMmeClient(SoundDeviceInstance,
+                    DeviceType == WAVE_OUT_DEVICE_TYPE ? WOM_DONE : WIM_DATA,
+                    (DWORD) Header);
+}
diff --git a/reactos/lib/drivers/sound/mmebuddy/wave/streaming.c b/reactos/lib/drivers/sound/mmebuddy/wave/streaming.c
new file mode 100644 (file)
index 0000000..5fdf495
--- /dev/null
@@ -0,0 +1,229 @@
+/*
+ * PROJECT:     ReactOS Sound System "MME Buddy" Library
+ * LICENSE:     GPL - See COPYING in the top level directory
+ * FILE:        lib/drivers/sound/mmebuddy/wave/streaming.c
+ *
+ * PURPOSE:     Wave streaming
+ *
+ * PROGRAMMERS: Andrew Greenwood (silverblade@reactos.org)
+*/
+
+#include <windows.h>
+#include <mmsystem.h>
+#include <mmddk.h>
+#include <ntddsnd.h>
+#include <mmebuddy.h>
+#include <sndtypes.h>
+
+
+/*
+    Restrain ourselves from flooding the kernel device!
+*/
+
+#define SOUND_KERNEL_BUFFER_COUNT       10
+#define SOUND_KERNEL_BUFFER_SIZE        200000
+
+
+/*
+    DoWaveStreaming
+        Check if there is streaming to be done, and if so, do it.
+*/
+
+MMRESULT
+DoWaveStreaming(
+    IN  PSOUND_DEVICE_INSTANCE SoundDeviceInstance)
+{
+    MMRESULT Result;
+    PSOUND_DEVICE SoundDevice;
+    PMMFUNCTION_TABLE FunctionTable;
+
+    Result = GetSoundDeviceFromInstance(SoundDeviceInstance, &SoundDevice);
+    SND_ASSERT( MMSUCCESS(Result) );
+
+    Result = GetSoundDeviceFunctionTable(SoundDevice, &FunctionTable);
+    SND_ASSERT( MMSUCCESS(Result) );
+    SND_ASSERT( FunctionTable );
+    SND_ASSERT( FunctionTable->CommitWaveBuffer );
+
+    // HACK
+    SND_TRACE(L"Calling buffer submit routine\n");
+
+    if ( ! SoundDeviceInstance->CurrentWaveHeader )
+    {
+        /* Start from the beginning (always a good idea) */
+        SoundDeviceInstance->CurrentWaveHeader = SoundDeviceInstance->HeadWaveHeader;
+    }
+
+    if ( SoundDeviceInstance->CurrentWaveHeader )
+    {
+        /* Stream or continue streaming this header */
+
+        Result = CommitWaveHeaderToKernelDevice(SoundDeviceInstance,
+                                                SoundDeviceInstance->CurrentWaveHeader,
+                                                FunctionTable->CommitWaveBuffer);
+    }
+    else
+    {
+        SND_TRACE(L"NOTHING TO DO - REC/PLAY STOPPED\n");
+    }
+
+    return Result;
+}
+
+
+/*
+    CompleteIO
+        An APC called as a result of a call to CommitWaveHeaderToKernelDevice.
+        This will count up the number of bytes which have been dealt with,
+        and when the entire wave header has been dealt with, will call
+        CompleteWaveHeader to have the wave header returned to the client.
+
+    CommitWaveHeaderToKernelDevice
+        Sends portions of the buffer described by the wave header to a kernel
+        device. This must only be called from within the context of the sound
+        thread. The caller supplies either their own commit routine, or uses
+        WriteFileEx_Committer. The committer is called with portions of the
+        buffer specified in the wave header.
+
+    WriteFileEx_Committer
+        Commit buffers using the WriteFileEx API.
+*/
+
+VOID CALLBACK
+CompleteIO(
+    IN  DWORD dwErrorCode,
+    IN  DWORD dwNumberOfBytesTransferred,
+    IN  LPOVERLAPPED lpOverlapped)
+{
+    PSOUND_DEVICE_INSTANCE SoundDeviceInstance;
+    PSOUND_OVERLAPPED SoundOverlapped = (PSOUND_OVERLAPPED) lpOverlapped;
+    PWAVEHDR WaveHdr;
+    PWAVEHDR_EXTENSION HdrExtension;
+
+    WaveHdr = (PWAVEHDR) SoundOverlapped->Header;
+    SND_ASSERT( WaveHdr );
+
+    HdrExtension = (PWAVEHDR_EXTENSION) WaveHdr->reserved;
+    SND_ASSERT( HdrExtension );
+
+    SoundDeviceInstance = SoundOverlapped->SoundDeviceInstance;
+
+    HdrExtension->BytesCompleted += dwNumberOfBytesTransferred;
+    SND_TRACE(L"%d/%d bytes of wavehdr completed\n", HdrExtension->BytesCompleted, WaveHdr->dwBufferLength);
+
+    /* We have an available buffer now */
+    -- SoundDeviceInstance->OutstandingBuffers;
+
+    if ( HdrExtension->BytesCompleted == WaveHdr->dwBufferLength )
+    {
+        CompleteWaveHeader(SoundDeviceInstance, WaveHdr);
+    }
+
+    DoWaveStreaming(SoundDeviceInstance);
+
+    //CompleteWavePortion(SoundDeviceInstance, dwNumberOfBytesTransferred);
+
+    FreeMemory(lpOverlapped);
+}
+
+MMRESULT
+CommitWaveHeaderToKernelDevice(
+    IN  PSOUND_DEVICE_INSTANCE SoundDeviceInstance,
+    IN  PWAVEHDR Header,
+    IN  WAVE_COMMIT_FUNC CommitFunction)
+{
+    PSOUND_OVERLAPPED Overlap;
+    DWORD BytesToWrite, BytesRemaining;
+    PWAVEHDR_EXTENSION HdrExtension;
+    LPVOID Offset;
+
+    VALIDATE_MMSYS_PARAMETER( SoundDeviceInstance );
+    VALIDATE_MMSYS_PARAMETER( Header );
+    VALIDATE_MMSYS_PARAMETER( CommitFunction );
+
+    HdrExtension = (PWAVEHDR_EXTENSION) Header->reserved;
+    VALIDATE_MMSYS_PARAMETER( HdrExtension );
+
+    /* Loop whilst there is data and sufficient available buffers */
+    while ( ( SoundDeviceInstance->OutstandingBuffers < SOUND_KERNEL_BUFFER_COUNT ) &&
+            ( HdrExtension->BytesCommitted < Header->dwBufferLength ) )
+    {
+        /* Is this the start of a loop? */
+        SoundDeviceInstance->WaveLoopStart = Header;
+
+        /* Where to start pulling the data from within the buffer */
+        Offset = Header->lpData + HdrExtension->BytesCommitted;
+
+        /* How much of this header is not committed? */
+        BytesRemaining = Header->dwBufferLength - HdrExtension->BytesCommitted;
+
+        /* We can write anything up to the buffer size limit */
+        BytesToWrite = BytesRemaining > SOUND_KERNEL_BUFFER_SIZE ?
+                       SOUND_KERNEL_BUFFER_SIZE :
+                       BytesRemaining;
+
+        /* If there's nothing left in the current header, move to the next */
+        if ( BytesToWrite == 0 )
+        {
+            Header = Header->lpNext;
+            HdrExtension = (PWAVEHDR_EXTENSION) Header->reserved;
+            SND_ASSERT( HdrExtension );
+            SND_ASSERT( HdrExtension->BytesCommitted == 0 );
+            SND_ASSERT( HdrExtension->BytesCompleted == 0 );
+            continue;
+        }
+
+        HdrExtension->BytesCommitted += BytesToWrite;
+
+        /* We're using up a buffer so update this */
+        ++ SoundDeviceInstance->OutstandingBuffers;
+
+        SND_TRACE(L"COMMIT: Offset 0x%x amount %d remain %d totalcommit %d",
+                  Offset, BytesToWrite, BytesRemaining, HdrExtension->BytesCommitted);
+
+        /* We need a new overlapped info structure for each buffer */
+        Overlap = AllocateStruct(SOUND_OVERLAPPED);
+
+        if ( Overlap )
+        {
+            ZeroMemory(Overlap, sizeof(SOUND_OVERLAPPED));
+            Overlap->SoundDeviceInstance = SoundDeviceInstance;
+            Overlap->Header = Header;
+
+
+            if ( ! MMSUCCESS(CommitFunction(SoundDeviceInstance, Offset, BytesToWrite, Overlap, CompleteIO)) )
+            {
+                /* Just pretend it played if we fail... Show must go on, etc. etc. */
+                SND_WARN(L"FAILED\n");
+                HdrExtension->BytesCompleted += BytesToWrite;
+            }
+        }
+    }
+
+    return MMSYSERR_NOERROR;
+}
+
+MMRESULT
+WriteFileEx_Committer(
+    IN  PSOUND_DEVICE_INSTANCE SoundDeviceInstance,
+    IN  PVOID OffsetPtr,
+    IN  DWORD Length,
+    IN  PSOUND_OVERLAPPED Overlap,
+    IN  LPOVERLAPPED_COMPLETION_ROUTINE CompletionRoutine)
+{
+    HANDLE Handle;
+
+    VALIDATE_MMSYS_PARAMETER( SoundDeviceInstance );
+    VALIDATE_MMSYS_PARAMETER( OffsetPtr );
+    VALIDATE_MMSYS_PARAMETER( Overlap );
+    VALIDATE_MMSYS_PARAMETER( CompletionRoutine );
+
+    GetSoundDeviceInstanceHandle(SoundDeviceInstance, &Handle);
+
+    if ( ! WriteFileEx(Handle, OffsetPtr, Length, (LPOVERLAPPED)Overlap, CompletionRoutine) )
+    {
+        // TODO
+    }
+
+    return MMSYSERR_NOERROR;
+}
index 754b8c3..051f240 100644 (file)
@@ -94,7 +94,7 @@ wodMessage(
 
         case WODM_WRITE :
         {
-            Result = MmeEnqueueWaveHeader(PrivateHandle, Parameter1);
+            Result = MmeWriteWaveHeader(PrivateHandle, Parameter1);
             break;
         }
 
index 6289187..0d32d2e 100644 (file)
@@ -8,6 +8,8 @@
  * PROGRAMMERS: Andrew Greenwood (silverblade@reactos.org)
 */
 
+#define NDEBUG
+
 #include <windows.h>
 #include <mmsystem.h>
 #include <mmddk.h>