summaryrefslogtreecommitdiff
path: root/portaudio/src/os/win
diff options
context:
space:
mode:
authorsanine <sanine.not@pm.me>2022-08-25 14:54:53 -0500
committersanine <sanine.not@pm.me>2022-08-25 14:54:53 -0500
commit37c97e345d12f95dde44e1d1a4c2f2aadd4615bc (patch)
treee1bb25bc855883062bdd7847ff2c04290f71c840 /portaudio/src/os/win
parent5634c7b04da619669f2f29f6798c03982be05180 (diff)
add initial structure
Diffstat (limited to 'portaudio/src/os/win')
-rw-r--r--portaudio/src/os/win/pa_win_coinitialize.c148
-rw-r--r--portaudio/src/os/win/pa_win_coinitialize.h94
-rw-r--r--portaudio/src/os/win/pa_win_hostapis.c100
-rw-r--r--portaudio/src/os/win/pa_win_util.c160
-rw-r--r--portaudio/src/os/win/pa_win_waveformat.c163
-rw-r--r--portaudio/src/os/win/pa_win_wdmks_utils.c309
-rw-r--r--portaudio/src/os/win/pa_win_wdmks_utils.h65
-rw-r--r--portaudio/src/os/win/pa_x86_plain_converters.c1218
-rw-r--r--portaudio/src/os/win/pa_x86_plain_converters.h60
9 files changed, 2317 insertions, 0 deletions
diff --git a/portaudio/src/os/win/pa_win_coinitialize.c b/portaudio/src/os/win/pa_win_coinitialize.c
new file mode 100644
index 0000000..cdb1fb9
--- /dev/null
+++ b/portaudio/src/os/win/pa_win_coinitialize.c
@@ -0,0 +1,148 @@
+/*
+ * Microsoft COM initialization routines
+ * Copyright (c) 1999-2011 Ross Bencina, Dmitry Kostjuchenko
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2011 Ross Bencina, Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/*
+ * The text above constitutes the entire PortAudio license; however,
+ * the PortAudio community also makes the following non-binding requests:
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version. It is also
+ * requested that these non-binding requests be included along with the
+ * license above.
+ */
+
+/** @file
+ @ingroup win_src
+
+ @brief Microsoft COM initialization routines.
+*/
+
+#include <windows.h>
+#include <objbase.h>
+
+#include "portaudio.h"
+#include "pa_util.h"
+#include "pa_debugprint.h"
+
+#include "pa_win_coinitialize.h"
+
+
+#if (defined(WIN32) && (defined(_MSC_VER) && (_MSC_VER >= 1200))) && !defined(_WIN32_WCE) && !(defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP)) /* MSC version 6 and above */
+#pragma comment( lib, "ole32.lib" )
+#endif
+
+
+/* use some special bit patterns here to try to guard against uninitialized memory errors */
+#define PAWINUTIL_COM_INITIALIZED (0xb38f)
+#define PAWINUTIL_COM_NOT_INITIALIZED (0xf1cd)
+
+
+PaError PaWinUtil_CoInitialize( PaHostApiTypeId hostApiType, PaWinUtilComInitializationResult *comInitializationResult )
+{
+ HRESULT hr;
+
+ comInitializationResult->state = PAWINUTIL_COM_NOT_INITIALIZED;
+
+ /*
+ If COM is already initialized CoInitialize will either return
+ FALSE, or RPC_E_CHANGED_MODE if it was initialised in a different
+ threading mode. In either case we shouldn't consider it an error
+ but we need to be careful to not call CoUninitialize() if
+ RPC_E_CHANGED_MODE was returned.
+ */
+
+#if !defined(WINAPI_FAMILY) || (WINAPI_FAMILY != WINAPI_FAMILY_APP)
+ hr = CoInitialize(0); /* use legacy-safe equivalent to CoInitializeEx(NULL, COINIT_APARTMENTTHREADED) */
+#else
+ hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
+#endif
+ if( FAILED(hr) && hr != RPC_E_CHANGED_MODE )
+ {
+ PA_DEBUG(("CoInitialize(0) failed. hr=%d\n", hr));
+
+ if( hr == E_OUTOFMEMORY )
+ return paInsufficientMemory;
+
+ {
+ char *lpMsgBuf;
+ FormatMessage(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL,
+ hr,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPTSTR) &lpMsgBuf,
+ 0,
+ NULL
+ );
+ PaUtil_SetLastHostErrorInfo( hostApiType, hr, lpMsgBuf );
+ LocalFree( lpMsgBuf );
+ }
+
+ return paUnanticipatedHostError;
+ }
+
+ if( hr != RPC_E_CHANGED_MODE )
+ {
+ comInitializationResult->state = PAWINUTIL_COM_INITIALIZED;
+
+ /*
+ Memorize calling thread id and report warning on Uninitialize if
+ calling thread is different as CoInitialize must match CoUninitialize
+ in the same thread.
+ */
+ comInitializationResult->initializingThreadId = GetCurrentThreadId();
+ }
+
+ return paNoError;
+}
+
+
+void PaWinUtil_CoUninitialize( PaHostApiTypeId hostApiType, PaWinUtilComInitializationResult *comInitializationResult )
+{
+ if( comInitializationResult->state != PAWINUTIL_COM_NOT_INITIALIZED
+ && comInitializationResult->state != PAWINUTIL_COM_INITIALIZED ){
+
+ PA_DEBUG(("ERROR: PaWinUtil_CoUninitialize called without calling PaWinUtil_CoInitialize\n"));
+ }
+
+ if( comInitializationResult->state == PAWINUTIL_COM_INITIALIZED )
+ {
+ DWORD currentThreadId = GetCurrentThreadId();
+ if( comInitializationResult->initializingThreadId != currentThreadId )
+ {
+ PA_DEBUG(("ERROR: failed PaWinUtil_CoUninitialize calling thread[%lu] does not match initializing thread[%lu]\n",
+ currentThreadId, comInitializationResult->initializingThreadId));
+ }
+ else
+ {
+ CoUninitialize();
+
+ comInitializationResult->state = PAWINUTIL_COM_NOT_INITIALIZED;
+ }
+ }
+}
diff --git a/portaudio/src/os/win/pa_win_coinitialize.h b/portaudio/src/os/win/pa_win_coinitialize.h
new file mode 100644
index 0000000..deb60c3
--- /dev/null
+++ b/portaudio/src/os/win/pa_win_coinitialize.h
@@ -0,0 +1,94 @@
+/*
+ * Microsoft COM initialization routines
+ * Copyright (c) 1999-2011 Ross Bencina, Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/*
+ * The text above constitutes the entire PortAudio license; however,
+ * the PortAudio community also makes the following non-binding requests:
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version. It is also
+ * requested that these non-binding requests be included along with the
+ * license above.
+ */
+
+/** @file
+ @ingroup win_src
+
+ @brief Microsoft COM initialization routines.
+*/
+
+#ifndef PA_WIN_COINITIALIZE_H
+#define PA_WIN_COINITIALIZE_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+
+/**
+ @brief Data type used to hold the result of an attempt to initialize COM
+ using PaWinUtil_CoInitialize. Must be retained between a call to
+ PaWinUtil_CoInitialize and a matching call to PaWinUtil_CoUninitialize.
+*/
+typedef struct PaWinUtilComInitializationResult{
+ int state;
+ DWORD initializingThreadId;
+} PaWinUtilComInitializationResult;
+
+
+/**
+ @brief Initialize Microsoft COM subsystem on the current thread.
+
+ @param hostApiType the host API type id of the caller. Used for error reporting.
+
+ @param comInitializationResult An output parameter. The value pointed to by
+ this parameter stores information required by PaWinUtil_CoUninitialize
+ to correctly uninitialize COM. The value should be retained and later
+ passed to PaWinUtil_CoUninitialize.
+
+ If PaWinUtil_CoInitialize returns paNoError, the caller must later call
+ PaWinUtil_CoUninitialize once.
+*/
+PaError PaWinUtil_CoInitialize( PaHostApiTypeId hostApiType, PaWinUtilComInitializationResult *comInitializationResult );
+
+
+/**
+ @brief Uninitialize the Microsoft COM subsystem on the current thread using
+ the result of a previous call to PaWinUtil_CoInitialize. Must be called on the same
+ thread as PaWinUtil_CoInitialize.
+
+ @param hostApiType the host API type id of the caller. Used for error reporting.
+
+ @param comInitializationResult An input parameter. A pointer to a value previously
+ initialized by a call to PaWinUtil_CoInitialize.
+*/
+void PaWinUtil_CoUninitialize( PaHostApiTypeId hostApiType, PaWinUtilComInitializationResult *comInitializationResult );
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* PA_WIN_COINITIALIZE_H */
diff --git a/portaudio/src/os/win/pa_win_hostapis.c b/portaudio/src/os/win/pa_win_hostapis.c
new file mode 100644
index 0000000..f8a4651
--- /dev/null
+++ b/portaudio/src/os/win/pa_win_hostapis.c
@@ -0,0 +1,100 @@
+/*
+ * $Id$
+ * Portable Audio I/O Library Windows initialization table
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2008 Ross Bencina, Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/*
+ * The text above constitutes the entire PortAudio license; however,
+ * the PortAudio community also makes the following non-binding requests:
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version. It is also
+ * requested that these non-binding requests be included along with the
+ * license above.
+ */
+
+/** @file
+ @ingroup win_src
+
+ @brief Win32 host API initialization function table.
+*/
+
+/* This is needed to make this source file depend on CMake option changes
+ and at the same time make it transparent for clients not using CMake.
+*/
+#ifdef PORTAUDIO_CMAKE_GENERATED
+#include "options_cmake.h"
+#endif
+
+#include "pa_hostapi.h"
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+PaError PaSkeleton_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
+PaError PaWinMme_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
+PaError PaWinDs_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
+PaError PaAsio_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
+PaError PaWinWdm_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
+PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+
+PaUtilHostApiInitializer *paHostApiInitializers[] =
+ {
+
+#if PA_USE_WMME
+ PaWinMme_Initialize,
+#endif
+
+#if PA_USE_DS
+ PaWinDs_Initialize,
+#endif
+
+#if PA_USE_ASIO
+ PaAsio_Initialize,
+#endif
+
+#if PA_USE_WASAPI
+ PaWasapi_Initialize,
+#endif
+
+#if PA_USE_WDMKS
+ PaWinWdm_Initialize,
+#endif
+
+#if PA_USE_SKELETON
+ PaSkeleton_Initialize, /* just for testing. last in list so it isn't marked as default. */
+#endif
+
+ 0 /* NULL terminated array */
+ };
diff --git a/portaudio/src/os/win/pa_win_util.c b/portaudio/src/os/win/pa_win_util.c
new file mode 100644
index 0000000..b86f7af
--- /dev/null
+++ b/portaudio/src/os/win/pa_win_util.c
@@ -0,0 +1,160 @@
+/*
+ * $Id$
+ * Portable Audio I/O Library
+ * Win32 platform-specific support functions
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2008 Ross Bencina
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/*
+ * The text above constitutes the entire PortAudio license; however,
+ * the PortAudio community also makes the following non-binding requests:
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version. It is also
+ * requested that these non-binding requests be included along with the
+ * license above.
+ */
+
+/** @file
+ @ingroup win_src
+
+ @brief Win32 implementation of platform-specific PaUtil support functions.
+*/
+
+#include <windows.h>
+
+#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP)
+ #include <sys/timeb.h> /* for _ftime_s() */
+#else
+ #include <mmsystem.h> /* for timeGetTime() */
+ #if (defined(WIN32) && (defined(_MSC_VER) && (_MSC_VER >= 1200))) && !defined(_WIN32_WCE) /* MSC version 6 and above */
+ #pragma comment( lib, "winmm.lib" )
+ #endif
+#endif
+
+#include "pa_util.h"
+
+/*
+ Track memory allocations to avoid leaks.
+ */
+
+#if PA_TRACK_MEMORY
+static int numAllocations_ = 0;
+#endif
+
+
+void *PaUtil_AllocateMemory( long size )
+{
+ void *result = GlobalAlloc( GPTR, size );
+
+#if PA_TRACK_MEMORY
+ if( result != NULL ) numAllocations_ += 1;
+#endif
+ return result;
+}
+
+
+void PaUtil_FreeMemory( void *block )
+{
+ if( block != NULL )
+ {
+ GlobalFree( block );
+#if PA_TRACK_MEMORY
+ numAllocations_ -= 1;
+#endif
+
+ }
+}
+
+
+int PaUtil_CountCurrentlyAllocatedBlocks( void )
+{
+#if PA_TRACK_MEMORY
+ return numAllocations_;
+#else
+ return 0;
+#endif
+}
+
+
+void Pa_Sleep( long msec )
+{
+ Sleep( msec );
+}
+
+static int usePerformanceCounter_;
+static double secondsPerTick_;
+
+void PaUtil_InitializeClock( void )
+{
+ LARGE_INTEGER ticksPerSecond;
+
+ if( QueryPerformanceFrequency( &ticksPerSecond ) != 0 )
+ {
+ usePerformanceCounter_ = 1;
+ secondsPerTick_ = 1.0 / (double)ticksPerSecond.QuadPart;
+ }
+ else
+ {
+ usePerformanceCounter_ = 0;
+ }
+}
+
+
+double PaUtil_GetTime( void )
+{
+ LARGE_INTEGER time;
+
+ if( usePerformanceCounter_ )
+ {
+ /*
+ Note: QueryPerformanceCounter has a known issue where it can skip forward
+ by a few seconds (!) due to a hardware bug on some PCI-ISA bridge hardware.
+ This is documented here:
+ http://support.microsoft.com/default.aspx?scid=KB;EN-US;Q274323&
+
+ The work-arounds are not very paletable and involve querying GetTickCount
+ at every time step.
+
+ Using rdtsc is not a good option on multi-core systems.
+
+ For now we just use QueryPerformanceCounter(). It's good, most of the time.
+ */
+ QueryPerformanceCounter( &time );
+ return time.QuadPart * secondsPerTick_;
+ }
+ else
+ {
+#ifndef UNDER_CE
+ #if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP)
+ return GetTickCount64() * .001;
+ #else
+ return timeGetTime() * .001;
+ #endif
+#else
+ return GetTickCount() * .001;
+#endif
+ }
+}
diff --git a/portaudio/src/os/win/pa_win_waveformat.c b/portaudio/src/os/win/pa_win_waveformat.c
new file mode 100644
index 0000000..0436a39
--- /dev/null
+++ b/portaudio/src/os/win/pa_win_waveformat.c
@@ -0,0 +1,163 @@
+/*
+ * PortAudio Portable Real-Time Audio Library
+ * Windows WAVEFORMAT* data structure utilities
+ * portaudio.h should be included before this file.
+ *
+ * Copyright (c) 2007 Ross Bencina
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/*
+ * The text above constitutes the entire PortAudio license; however,
+ * the PortAudio community also makes the following non-binding requests:
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version. It is also
+ * requested that these non-binding requests be included along with the
+ * license above.
+ */
+
+#include <windows.h>
+#include <mmsystem.h>
+#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP)
+ #include <mmreg.h> /* for WAVEFORMATEX */
+#endif
+
+#include "portaudio.h"
+#include "pa_win_waveformat.h"
+
+
+#if !defined(WAVE_FORMAT_EXTENSIBLE)
+#define WAVE_FORMAT_EXTENSIBLE 0xFFFE
+#endif
+
+
+static GUID pawin_ksDataFormatSubtypeGuidBase =
+ { (USHORT)(WAVE_FORMAT_PCM), 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 };
+
+
+int PaWin_SampleFormatToLinearWaveFormatTag( PaSampleFormat sampleFormat )
+{
+ if( sampleFormat == paFloat32 )
+ return PAWIN_WAVE_FORMAT_IEEE_FLOAT;
+
+ return PAWIN_WAVE_FORMAT_PCM;
+}
+
+
+void PaWin_InitializeWaveFormatEx( PaWinWaveFormat *waveFormat,
+ int numChannels, PaSampleFormat sampleFormat, int waveFormatTag, double sampleRate )
+{
+ WAVEFORMATEX *waveFormatEx = (WAVEFORMATEX*)waveFormat;
+ int bytesPerSample = Pa_GetSampleSize(sampleFormat);
+ unsigned long bytesPerFrame = numChannels * bytesPerSample;
+
+ waveFormatEx->wFormatTag = waveFormatTag;
+ waveFormatEx->nChannels = (WORD)numChannels;
+ waveFormatEx->nSamplesPerSec = (DWORD)sampleRate;
+ waveFormatEx->nAvgBytesPerSec = waveFormatEx->nSamplesPerSec * bytesPerFrame;
+ waveFormatEx->nBlockAlign = (WORD)bytesPerFrame;
+ waveFormatEx->wBitsPerSample = bytesPerSample * 8;
+ waveFormatEx->cbSize = 0;
+}
+
+
+void PaWin_InitializeWaveFormatExtensible( PaWinWaveFormat *waveFormat,
+ int numChannels, PaSampleFormat sampleFormat, int waveFormatTag, double sampleRate,
+ PaWinWaveFormatChannelMask channelMask )
+{
+ WAVEFORMATEX *waveFormatEx = (WAVEFORMATEX*)waveFormat;
+ int bytesPerSample = Pa_GetSampleSize(sampleFormat);
+ unsigned long bytesPerFrame = numChannels * bytesPerSample;
+ GUID guid;
+
+ waveFormatEx->wFormatTag = WAVE_FORMAT_EXTENSIBLE;
+ waveFormatEx->nChannels = (WORD)numChannels;
+ waveFormatEx->nSamplesPerSec = (DWORD)sampleRate;
+ waveFormatEx->nAvgBytesPerSec = waveFormatEx->nSamplesPerSec * bytesPerFrame;
+ waveFormatEx->nBlockAlign = (WORD)bytesPerFrame;
+ waveFormatEx->wBitsPerSample = bytesPerSample * 8;
+ waveFormatEx->cbSize = 22;
+
+ memcpy(&waveFormat->fields[PAWIN_INDEXOF_WVALIDBITSPERSAMPLE],
+ &waveFormatEx->wBitsPerSample, sizeof(WORD));
+
+ memcpy(&waveFormat->fields[PAWIN_INDEXOF_DWCHANNELMASK],
+ &channelMask, sizeof(DWORD));
+
+ guid = pawin_ksDataFormatSubtypeGuidBase;
+ guid.Data1 = (USHORT)waveFormatTag;
+ memcpy(&waveFormat->fields[PAWIN_INDEXOF_SUBFORMAT], &guid, sizeof(GUID));
+}
+
+PaWinWaveFormatChannelMask PaWin_DefaultChannelMask( int numChannels )
+{
+ switch( numChannels ){
+ case 1:
+ return PAWIN_SPEAKER_MONO;
+ case 2:
+ return PAWIN_SPEAKER_STEREO;
+ case 3:
+ return PAWIN_SPEAKER_FRONT_LEFT | PAWIN_SPEAKER_FRONT_CENTER | PAWIN_SPEAKER_FRONT_RIGHT;
+ case 4:
+ return PAWIN_SPEAKER_QUAD;
+ case 5:
+ return PAWIN_SPEAKER_QUAD | PAWIN_SPEAKER_FRONT_CENTER;
+ case 6:
+ /* The meaning of the PAWIN_SPEAKER_5POINT1 flag has changed over time:
+ http://msdn2.microsoft.com/en-us/library/aa474707.aspx
+ We use PAWIN_SPEAKER_5POINT1 (not PAWIN_SPEAKER_5POINT1_SURROUND)
+ because on some cards (eg Audigy) PAWIN_SPEAKER_5POINT1_SURROUND
+ results in a virtual mixdown placing the rear output in the
+ front _and_ rear speakers.
+ */
+ return PAWIN_SPEAKER_5POINT1;
+ /* case 7: */
+ case 8:
+ /* RoBi: PAWIN_SPEAKER_7POINT1_SURROUND fits normal surround sound setups better than PAWIN_SPEAKER_7POINT1, f.i. NVidia HDMI Audio
+ output is silent on channels 5&6 with NVidia drivers, and channel 7&8 with Microsoft HD Audio driver using PAWIN_SPEAKER_7POINT1.
+ With PAWIN_SPEAKER_7POINT1_SURROUND both setups work OK. */
+ return PAWIN_SPEAKER_7POINT1_SURROUND;
+ }
+
+ /* Apparently some Audigy drivers will output silence
+ if the direct-out constant (0) is used. So this is not ideal.
+
+ RoBi 2012-12-19: Also, NVidia driver seem to output garbage instead. Again not very ideal.
+ */
+ return PAWIN_SPEAKER_DIRECTOUT;
+
+ /* Note that Alec Rogers proposed the following as an alternate method to
+ generate the default channel mask, however it doesn't seem to be an improvement
+ over the above, since some drivers will matrix outputs mapping to non-present
+ speakers across multiple physical speakers.
+
+ if(nChannels==1) {
+ pwfFormat->dwChannelMask = SPEAKER_FRONT_CENTER;
+ }
+ else {
+ pwfFormat->dwChannelMask = 0;
+ for(i=0; i<nChannels; i++)
+ pwfFormat->dwChannelMask = (pwfFormat->dwChannelMask << 1) | 0x1;
+ }
+ */
+}
diff --git a/portaudio/src/os/win/pa_win_wdmks_utils.c b/portaudio/src/os/win/pa_win_wdmks_utils.c
new file mode 100644
index 0000000..3373146
--- /dev/null
+++ b/portaudio/src/os/win/pa_win_wdmks_utils.c
@@ -0,0 +1,309 @@
+/*
+ * PortAudio Portable Real-Time Audio Library
+ * Windows WDM KS utilities
+ *
+ * Copyright (c) 1999 - 2007 Andrew Baldwin, Ross Bencina
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/*
+ * The text above constitutes the entire PortAudio license; however,
+ * the PortAudio community also makes the following non-binding requests:
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version. It is also
+ * requested that these non-binding requests be included along with the
+ * license above.
+ */
+
+#include <windows.h>
+#include <mmreg.h>
+#ifndef WAVE_FORMAT_IEEE_FLOAT
+ #define WAVE_FORMAT_IEEE_FLOAT 0x0003 // MinGW32 does not define this
+#endif
+#ifndef _WAVEFORMATEXTENSIBLE_
+ #define _WAVEFORMATEXTENSIBLE_ // MinGW32 does not define this
+#endif
+#ifndef _INC_MMREG
+ #define _INC_MMREG // for STATIC_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
+#endif
+#include <winioctl.h> // MinGW32 does not define this automatically
+
+#if defined(__GNUC__)
+
+#include "../../hostapi/wasapi/mingw-include/ks.h"
+#include "../../hostapi/wasapi/mingw-include/ksmedia.h"
+
+#else
+
+#include <ks.h>
+#include <ksmedia.h>
+
+#endif
+
+#include <stdio.h> // just for some development printfs
+
+#include "portaudio.h"
+#include "pa_util.h"
+#include "pa_win_wdmks_utils.h"
+
+
+/* PortAudio-local instances of GUIDs previously sourced from ksguid.lib */
+
+/* GUID KSDATAFORMAT_TYPE_AUDIO */
+static const GUID pa_KSDATAFORMAT_TYPE_AUDIO = { STATIC_KSDATAFORMAT_TYPE_AUDIO };
+
+/* GUID KSDATAFORMAT_SUBTYPE_IEEE_FLOAT */
+static const GUID pa_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = { STATIC_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT };
+
+/* GUID KSDATAFORMAT_SUBTYPE_PCM */
+static const GUID pa_KSDATAFORMAT_SUBTYPE_PCM = { STATIC_KSDATAFORMAT_SUBTYPE_PCM };
+
+/* GUID KSDATAFORMAT_SUBTYPE_WAVEFORMATEX */
+static const GUID pa_KSDATAFORMAT_SUBTYPE_WAVEFORMATEX = { STATIC_KSDATAFORMAT_SUBTYPE_WAVEFORMATEX };
+
+/* GUID KSMEDIUMSETID_Standard */
+static const GUID pa_KSMEDIUMSETID_Standard = { STATIC_KSMEDIUMSETID_Standard };
+
+/* GUID KSINTERFACESETID_Standard */
+static const GUID pa_KSINTERFACESETID_Standard = { STATIC_KSINTERFACESETID_Standard };
+
+/* GUID KSPROPSETID_Pin */
+static const GUID pa_KSPROPSETID_Pin = { STATIC_KSPROPSETID_Pin };
+
+#define pa_IS_VALID_WAVEFORMATEX_GUID(Guid)\
+ (!memcmp(((PUSHORT)&pa_KSDATAFORMAT_SUBTYPE_WAVEFORMATEX) + 1, ((PUSHORT)(Guid)) + 1, sizeof(GUID) - sizeof(USHORT)))
+
+
+static PaError WdmGetPinPropertySimple(
+ HANDLE handle,
+ unsigned long pinId,
+ unsigned long property,
+ void* value,
+ unsigned long valueSize )
+{
+ DWORD bytesReturned;
+ KSP_PIN ksPProp;
+ ksPProp.Property.Set = pa_KSPROPSETID_Pin;
+ ksPProp.Property.Id = property;
+ ksPProp.Property.Flags = KSPROPERTY_TYPE_GET;
+ ksPProp.PinId = pinId;
+ ksPProp.Reserved = 0;
+
+ if( DeviceIoControl( handle, IOCTL_KS_PROPERTY, &ksPProp, sizeof(KSP_PIN),
+ value, valueSize, &bytesReturned, NULL ) == 0 || bytesReturned != valueSize )
+ {
+ return paUnanticipatedHostError;
+ }
+ else
+ {
+ return paNoError;
+ }
+}
+
+
+static PaError WdmGetPinPropertyMulti(
+ HANDLE handle,
+ unsigned long pinId,
+ unsigned long property,
+ KSMULTIPLE_ITEM** ksMultipleItem)
+{
+ unsigned long multipleItemSize = 0;
+ KSP_PIN ksPProp;
+ DWORD bytesReturned;
+
+ *ksMultipleItem = 0;
+
+ ksPProp.Property.Set = pa_KSPROPSETID_Pin;
+ ksPProp.Property.Id = property;
+ ksPProp.Property.Flags = KSPROPERTY_TYPE_GET;
+ ksPProp.PinId = pinId;
+ ksPProp.Reserved = 0;
+
+ if( DeviceIoControl( handle, IOCTL_KS_PROPERTY, &ksPProp.Property,
+ sizeof(KSP_PIN), NULL, 0, &multipleItemSize, NULL ) == 0 && GetLastError() != ERROR_MORE_DATA )
+ {
+ return paUnanticipatedHostError;
+ }
+
+ *ksMultipleItem = (KSMULTIPLE_ITEM*)PaUtil_AllocateMemory( multipleItemSize );
+ if( !*ksMultipleItem )
+ {
+ return paInsufficientMemory;
+ }
+
+ if( DeviceIoControl( handle, IOCTL_KS_PROPERTY, &ksPProp, sizeof(KSP_PIN),
+ (void*)*ksMultipleItem, multipleItemSize, &bytesReturned, NULL ) == 0 || bytesReturned != multipleItemSize )
+ {
+ PaUtil_FreeMemory( ksMultipleItem );
+ return paUnanticipatedHostError;
+ }
+
+ return paNoError;
+}
+
+
+static int GetKSFilterPinCount( HANDLE deviceHandle )
+{
+ DWORD result;
+
+ if( WdmGetPinPropertySimple( deviceHandle, 0, KSPROPERTY_PIN_CTYPES, &result, sizeof(result) ) == paNoError ){
+ return result;
+ }else{
+ return 0;
+ }
+}
+
+
+static KSPIN_COMMUNICATION GetKSFilterPinPropertyCommunication( HANDLE deviceHandle, int pinId )
+{
+ KSPIN_COMMUNICATION result;
+
+ if( WdmGetPinPropertySimple( deviceHandle, pinId, KSPROPERTY_PIN_COMMUNICATION, &result, sizeof(result) ) == paNoError ){
+ return result;
+ }else{
+ return KSPIN_COMMUNICATION_NONE;
+ }
+}
+
+
+static KSPIN_DATAFLOW GetKSFilterPinPropertyDataflow( HANDLE deviceHandle, int pinId )
+{
+ KSPIN_DATAFLOW result;
+
+ if( WdmGetPinPropertySimple( deviceHandle, pinId, KSPROPERTY_PIN_DATAFLOW, &result, sizeof(result) ) == paNoError ){
+ return result;
+ }else{
+ return (KSPIN_DATAFLOW)0;
+ }
+}
+
+
+static int KSFilterPinPropertyIdentifiersInclude(
+ HANDLE deviceHandle, int pinId, unsigned long property, const GUID *identifierSet, unsigned long identifierId )
+{
+ KSMULTIPLE_ITEM* item = NULL;
+ KSIDENTIFIER* identifier;
+ int i;
+ int result = 0;
+
+ if( WdmGetPinPropertyMulti( deviceHandle, pinId, property, &item) != paNoError )
+ return 0;
+
+ identifier = (KSIDENTIFIER*)(item+1);
+
+ for( i = 0; i < (int)item->Count; i++ )
+ {
+ if( !memcmp( (void*)&identifier[i].Set, (void*)identifierSet, sizeof( GUID ) ) &&
+ ( identifier[i].Id == identifierId ) )
+ {
+ result = 1;
+ break;
+ }
+ }
+
+ PaUtil_FreeMemory( item );
+
+ return result;
+}
+
+
+/* return the maximum channel count supported by any pin on the device.
+ if isInput is non-zero we query input pins, otherwise output pins.
+*/
+int PaWin_WDMKS_QueryFilterMaximumChannelCount( void *wcharDevicePath, int isInput )
+{
+ HANDLE deviceHandle;
+ ULONG i;
+ int pinCount, pinId;
+ int result = 0;
+ KSPIN_DATAFLOW requiredDataflowDirection = (isInput ? KSPIN_DATAFLOW_OUT : KSPIN_DATAFLOW_IN );
+
+ if( !wcharDevicePath )
+ return 0;
+
+ deviceHandle = CreateFileW( (LPCWSTR)wcharDevicePath, FILE_SHARE_READ|FILE_SHARE_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL );
+ if( deviceHandle == INVALID_HANDLE_VALUE )
+ return 0;
+
+ pinCount = GetKSFilterPinCount( deviceHandle );
+ for( pinId = 0; pinId < pinCount; ++pinId )
+ {
+ KSPIN_COMMUNICATION communication = GetKSFilterPinPropertyCommunication( deviceHandle, pinId );
+ KSPIN_DATAFLOW dataflow = GetKSFilterPinPropertyDataflow( deviceHandle, pinId );
+ if( ( dataflow == requiredDataflowDirection ) &&
+ (( communication == KSPIN_COMMUNICATION_SINK) ||
+ ( communication == KSPIN_COMMUNICATION_BOTH))
+ && ( KSFilterPinPropertyIdentifiersInclude( deviceHandle, pinId,
+ KSPROPERTY_PIN_INTERFACES, &pa_KSINTERFACESETID_Standard, KSINTERFACE_STANDARD_STREAMING )
+ || KSFilterPinPropertyIdentifiersInclude( deviceHandle, pinId,
+ KSPROPERTY_PIN_INTERFACES, &pa_KSINTERFACESETID_Standard, KSINTERFACE_STANDARD_LOOPED_STREAMING ) )
+ && KSFilterPinPropertyIdentifiersInclude( deviceHandle, pinId,
+ KSPROPERTY_PIN_MEDIUMS, &pa_KSMEDIUMSETID_Standard, KSMEDIUM_STANDARD_DEVIO ) )
+ {
+ KSMULTIPLE_ITEM* item = NULL;
+ if( WdmGetPinPropertyMulti( deviceHandle, pinId, KSPROPERTY_PIN_DATARANGES, &item ) == paNoError )
+ {
+ KSDATARANGE *dataRange = (KSDATARANGE*)(item+1);
+
+ for( i=0; i < item->Count; ++i ){
+
+ if( pa_IS_VALID_WAVEFORMATEX_GUID(&dataRange->SubFormat)
+ || memcmp( (void*)&dataRange->SubFormat, (void*)&pa_KSDATAFORMAT_SUBTYPE_PCM, sizeof(GUID) ) == 0
+ || memcmp( (void*)&dataRange->SubFormat, (void*)&pa_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, sizeof(GUID) ) == 0
+ || ( ( memcmp( (void*)&dataRange->MajorFormat, (void*)&pa_KSDATAFORMAT_TYPE_AUDIO, sizeof(GUID) ) == 0 )
+ && ( memcmp( (void*)&dataRange->SubFormat, (void*)&KSDATAFORMAT_SUBTYPE_WILDCARD, sizeof(GUID) ) == 0 ) ) )
+ {
+ KSDATARANGE_AUDIO *dataRangeAudio = (KSDATARANGE_AUDIO*)dataRange;
+
+ /*
+ printf( ">>> %d %d %d %d %S\n", isInput, dataflow, communication, dataRangeAudio->MaximumChannels, devicePath );
+
+ if( memcmp((void*)&dataRange->Specifier, (void*)&KSDATAFORMAT_SPECIFIER_WAVEFORMATEX, sizeof(GUID) ) == 0 )
+ printf( "\tspecifier: KSDATAFORMAT_SPECIFIER_WAVEFORMATEX\n" );
+ else if( memcmp((void*)&dataRange->Specifier, (void*)&KSDATAFORMAT_SPECIFIER_DSOUND, sizeof(GUID) ) == 0 )
+ printf( "\tspecifier: KSDATAFORMAT_SPECIFIER_DSOUND\n" );
+ else if( memcmp((void*)&dataRange->Specifier, (void*)&KSDATAFORMAT_SPECIFIER_WILDCARD, sizeof(GUID) ) == 0 )
+ printf( "\tspecifier: KSDATAFORMAT_SPECIFIER_WILDCARD\n" );
+ else
+ printf( "\tspecifier: ?\n" );
+ */
+
+ /*
+ We assume that very high values for MaximumChannels are not useful and indicate
+ that the driver isn't prepared to tell us the real number of channels which it supports.
+ */
+ if( dataRangeAudio->MaximumChannels < 0xFFFFUL && (int)dataRangeAudio->MaximumChannels > result )
+ result = (int)dataRangeAudio->MaximumChannels;
+ }
+
+ dataRange = (KSDATARANGE*)( ((char*)dataRange) + dataRange->FormatSize);
+ }
+
+ PaUtil_FreeMemory( item );
+ }
+ }
+ }
+
+ CloseHandle( deviceHandle );
+ return result;
+}
diff --git a/portaudio/src/os/win/pa_win_wdmks_utils.h b/portaudio/src/os/win/pa_win_wdmks_utils.h
new file mode 100644
index 0000000..f524cf7
--- /dev/null
+++ b/portaudio/src/os/win/pa_win_wdmks_utils.h
@@ -0,0 +1,65 @@
+#ifndef PA_WIN_WDMKS_UTILS_H
+#define PA_WIN_WDMKS_UTILS_H
+
+/*
+ * PortAudio Portable Real-Time Audio Library
+ * Windows WDM KS utilities
+ *
+ * Copyright (c) 1999 - 2007 Ross Bencina, Andrew Baldwin
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/*
+ * The text above constitutes the entire PortAudio license; however,
+ * the PortAudio community also makes the following non-binding requests:
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version. It is also
+ * requested that these non-binding requests be included along with the
+ * license above.
+ */
+
+/** @file
+ @brief Utilities for working with the Windows WDM KS API
+*/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ Query for the maximum number of channels supported by any pin of the
+ specified device. Returns 0 if the query fails for any reason.
+
+ @param wcharDevicePath A system level PnP interface path, supplied as a WCHAR unicode string.
+ Declared as void* to avoid introducing a dependency on wchar_t here.
+
+ @param isInput A flag specifying whether to query for input (non-zero) or output (zero) channels.
+*/
+int PaWin_WDMKS_QueryFilterMaximumChannelCount( void *wcharDevicePath, int isInput );
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* PA_WIN_WDMKS_UTILS_H */
diff --git a/portaudio/src/os/win/pa_x86_plain_converters.c b/portaudio/src/os/win/pa_x86_plain_converters.c
new file mode 100644
index 0000000..1096994
--- /dev/null
+++ b/portaudio/src/os/win/pa_x86_plain_converters.c
@@ -0,0 +1,1218 @@
+/*
+ * Plain Intel IA32 assembly implementations of PortAudio sample converter functions.
+ * Copyright (c) 1999-2002 Ross Bencina, Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/*
+ * The text above constitutes the entire PortAudio license; however,
+ * the PortAudio community also makes the following non-binding requests:
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version. It is also
+ * requested that these non-binding requests be included along with the
+ * license above.
+ */
+
+/** @file
+ @ingroup win_src
+*/
+
+#include "pa_x86_plain_converters.h"
+
+#include "pa_converters.h"
+#include "pa_dither.h"
+
+/*
+ the main reason these versions are faster than the equivalent C versions
+ is that float -> int casting is expensive in C on x86 because the rounding
+ mode needs to be changed for every cast. these versions only set
+ the rounding mode once outside the loop.
+
+ small additional speed gains are made by the way that clamping is
+ implemented.
+
+TODO:
+ o- inline dither code
+ o- implement Dither only (no-clip) versions
+ o- implement int8 and uint8 versions
+ o- test thoroughly
+
+ o- the packed 24 bit functions could benefit from unrolling and avoiding
+ byte and word sized register access.
+*/
+
+/* -------------------------------------------------------------------------- */
+
+/*
+#define PA_CLIP_( val, min, max )\
+ { val = ((val) < (min)) ? (min) : (((val) > (max)) ? (max) : (val)); }
+*/
+
+/*
+ the following notes were used to determine whether a floating point
+ value should be saturated (ie >1 or <-1) by loading it into an integer
+ register. these should be rewritten so that they make sense.
+
+ an ieee floating point value
+
+ 1.xxxxxxxxxxxxxxxxxxxx?
+
+
+ is less than or equal to 1 and greater than or equal to -1 either:
+
+ if the mantissa is 0 and the unbiased exponent is 0
+
+ OR
+
+ if the unbiased exponent < 0
+
+ this translates to:
+
+ if the mantissa is 0 and the biased exponent is 7F
+
+ or
+
+ if the biased exponent is less than 7F
+
+
+ therefore the value is greater than 1 or less than -1 if
+
+ the mantissa is not 0 and the biased exponent is 7F
+
+ or
+
+ if the biased exponent is greater than 7F
+
+
+ in other words, if we mask out the sign bit, the value is
+ greater than 1 or less than -1 if its integer representation is greater than:
+
+ 0 01111111 0000 0000 0000 0000 0000 000
+
+ 0011 1111 1000 0000 0000 0000 0000 0000 => 0x3F800000
+*/
+
+#if defined(_WIN64) || defined(_WIN32_WCE)
+
+/*
+ -EMT64/AMD64 uses different asm
+ -VC2005 doesn't allow _WIN64 with inline assembly either!
+ */
+void PaUtil_InitializeX86PlainConverters( void )
+{
+}
+
+#else
+
+/* -------------------------------------------------------------------------- */
+
+static const short fpuControlWord_ = 0x033F; /*round to nearest, 64 bit precision, all exceptions masked*/
+static const double int32Scaler_ = 0x7FFFFFFF;
+static const double ditheredInt32Scaler_ = 0x7FFFFFFE;
+static const double int24Scaler_ = 0x7FFFFF;
+static const double ditheredInt24Scaler_ = 0x7FFFFE;
+static const double int16Scaler_ = 0x7FFF;
+static const double ditheredInt16Scaler_ = 0x7FFE;
+
+#define PA_DITHER_BITS_ (15)
+/* Multiply by PA_FLOAT_DITHER_SCALE_ to get a float between -2.0 and +1.99999 */
+#define PA_FLOAT_DITHER_SCALE_ (1.0F / ((1<<PA_DITHER_BITS_)-1))
+static const float const_float_dither_scale_ = PA_FLOAT_DITHER_SCALE_;
+#define PA_DITHER_SHIFT_ ((32 - PA_DITHER_BITS_) + 1)
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int32(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+/*
+ float *src = (float*)sourceBuffer;
+ signed long *dest = (signed long*)destinationBuffer;
+ (void)ditherGenerator; // unused parameter
+
+ while( count-- )
+ {
+ // REVIEW
+ double scaled = *src * 0x7FFFFFFF;
+ *dest = (signed long) scaled;
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+*/
+
+ short savedFpuControlWord;
+
+ (void) ditherGenerator; /* unused parameter */
+
+
+ __asm{
+ // esi -> source ptr
+ // eax -> source byte stride
+ // edi -> destination ptr
+ // ebx -> destination byte stride
+ // ecx -> source end ptr
+ // edx -> temp
+
+ mov esi, sourceBuffer
+
+ mov edx, 4 // sizeof float32 and int32
+ mov eax, sourceStride
+ imul eax, edx
+
+ mov ecx, count
+ imul ecx, eax
+ add ecx, esi
+
+ mov edi, destinationBuffer
+
+ mov ebx, destinationStride
+ imul ebx, edx
+
+ fwait
+ fstcw savedFpuControlWord
+ fldcw fpuControlWord_
+
+ fld int32Scaler_ // stack: (int)0x7FFFFFFF
+
+ Float32_To_Int32_loop:
+
+ // load unscaled value into st(0)
+ fld dword ptr [esi] // stack: value, (int)0x7FFFFFFF
+ add esi, eax // increment source ptr
+ //lea esi, [esi+eax]
+ fmul st(0), st(1) // st(0) *= st(1), stack: value*0x7FFFFFFF, (int)0x7FFFFFFF
+ /*
+ note: we could store to a temporary qword here which would cause
+ wraparound distortion instead of int indefinite 0x10. that would
+ be more work, and given that not enabling clipping is only advisable
+ when you know that your signal isn't going to clip it isn't worth it.
+ */
+ fistp dword ptr [edi] // pop st(0) into dest, stack: (int)0x7FFFFFFF
+
+ add edi, ebx // increment destination ptr
+ //lea edi, [edi+ebx]
+
+ cmp esi, ecx // has src ptr reached end?
+ jne Float32_To_Int32_loop
+
+ ffree st(0)
+ fincstp
+
+ fwait
+ fnclex
+ fldcw savedFpuControlWord
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int32_Clip(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+/*
+ float *src = (float*)sourceBuffer;
+ signed long *dest = (signed long*)destinationBuffer;
+ (void) ditherGenerator; // unused parameter
+
+ while( count-- )
+ {
+ // REVIEW
+ double scaled = *src * 0x7FFFFFFF;
+ PA_CLIP_( scaled, -2147483648., 2147483647. );
+ *dest = (signed long) scaled;
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+*/
+
+ short savedFpuControlWord;
+
+ (void) ditherGenerator; /* unused parameter */
+
+ __asm{
+ // esi -> source ptr
+ // eax -> source byte stride
+ // edi -> destination ptr
+ // ebx -> destination byte stride
+ // ecx -> source end ptr
+ // edx -> temp
+
+ mov esi, sourceBuffer
+
+ mov edx, 4 // sizeof float32 and int32
+ mov eax, sourceStride
+ imul eax, edx
+
+ mov ecx, count
+ imul ecx, eax
+ add ecx, esi
+
+ mov edi, destinationBuffer
+
+ mov ebx, destinationStride
+ imul ebx, edx
+
+ fwait
+ fstcw savedFpuControlWord
+ fldcw fpuControlWord_
+
+ fld int32Scaler_ // stack: (int)0x7FFFFFFF
+
+ Float32_To_Int32_Clip_loop:
+
+ mov edx, dword ptr [esi] // load floating point value into integer register
+
+ and edx, 0x7FFFFFFF // mask off sign
+ cmp edx, 0x3F800000 // greater than 1.0 or less than -1.0
+
+ jg Float32_To_Int32_Clip_clamp
+
+ // load unscaled value into st(0)
+ fld dword ptr [esi] // stack: value, (int)0x7FFFFFFF
+ add esi, eax // increment source ptr
+ //lea esi, [esi+eax]
+ fmul st(0), st(1) // st(0) *= st(1), stack: value*0x7FFFFFFF, (int)0x7FFFFFFF
+ fistp dword ptr [edi] // pop st(0) into dest, stack: (int)0x7FFFFFFF
+ jmp Float32_To_Int32_Clip_stored
+
+ Float32_To_Int32_Clip_clamp:
+ mov edx, dword ptr [esi] // load floating point value into integer register
+ shr edx, 31 // move sign bit into bit 0
+ add esi, eax // increment source ptr
+ //lea esi, [esi+eax]
+ add edx, 0x7FFFFFFF // convert to maximum range integers
+ mov dword ptr [edi], edx
+
+ Float32_To_Int32_Clip_stored:
+
+ //add edi, ebx // increment destination ptr
+ lea edi, [edi+ebx]
+
+ cmp esi, ecx // has src ptr reached end?
+ jne Float32_To_Int32_Clip_loop
+
+ ffree st(0)
+ fincstp
+
+ fwait
+ fnclex
+ fldcw savedFpuControlWord
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int32_DitherClip(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ /*
+ float *src = (float*)sourceBuffer;
+ signed long *dest = (signed long*)destinationBuffer;
+
+ while( count-- )
+ {
+ // REVIEW
+ double dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
+ // use smaller scaler to prevent overflow when we add the dither
+ double dithered = ((double)*src * (2147483646.0)) + dither;
+ PA_CLIP_( dithered, -2147483648., 2147483647. );
+ *dest = (signed long) dithered;
+
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+ */
+
+ short savedFpuControlWord;
+
+ // spill storage:
+ signed long sourceByteStride;
+ signed long highpassedDither;
+
+ // dither state:
+ unsigned long ditherPrevious = ditherGenerator->previous;
+ unsigned long ditherRandSeed1 = ditherGenerator->randSeed1;
+ unsigned long ditherRandSeed2 = ditherGenerator->randSeed2;
+
+ __asm{
+ // esi -> source ptr
+ // eax -> source byte stride
+ // edi -> destination ptr
+ // ebx -> destination byte stride
+ // ecx -> source end ptr
+ // edx -> temp
+
+ mov esi, sourceBuffer
+
+ mov edx, 4 // sizeof float32 and int32
+ mov eax, sourceStride
+ imul eax, edx
+
+ mov ecx, count
+ imul ecx, eax
+ add ecx, esi
+
+ mov edi, destinationBuffer
+
+ mov ebx, destinationStride
+ imul ebx, edx
+
+ fwait
+ fstcw savedFpuControlWord
+ fldcw fpuControlWord_
+
+ fld ditheredInt32Scaler_ // stack: int scaler
+
+ Float32_To_Int32_DitherClip_loop:
+
+ mov edx, dword ptr [esi] // load floating point value into integer register
+
+ and edx, 0x7FFFFFFF // mask off sign
+ cmp edx, 0x3F800000 // greater than 1.0 or less than -1.0
+
+ jg Float32_To_Int32_DitherClip_clamp
+
+ // load unscaled value into st(0)
+ fld dword ptr [esi] // stack: value, int scaler
+ add esi, eax // increment source ptr
+ //lea esi, [esi+eax]
+ fmul st(0), st(1) // st(0) *= st(1), stack: value*(int scaler), int scaler
+
+ /*
+ // call PaUtil_GenerateFloatTriangularDither with C calling convention
+ mov sourceByteStride, eax // save eax
+ mov sourceEnd, ecx // save ecx
+ push ditherGenerator // pass ditherGenerator parameter on stack
+ call PaUtil_GenerateFloatTriangularDither // stack: dither, value*(int scaler), int scaler
+ pop edx // clear parameter off stack
+ mov ecx, sourceEnd // restore ecx
+ mov eax, sourceByteStride // restore eax
+ */
+
+ // generate dither
+ mov sourceByteStride, eax // save eax
+ mov edx, 196314165
+ mov eax, ditherRandSeed1
+ mul edx // eax:edx = eax * 196314165
+ //add eax, 907633515
+ lea eax, [eax+907633515]
+ mov ditherRandSeed1, eax
+ mov edx, 196314165
+ mov eax, ditherRandSeed2
+ mul edx // eax:edx = eax * 196314165
+ //add eax, 907633515
+ lea eax, [eax+907633515]
+ mov edx, ditherRandSeed1
+ shr edx, PA_DITHER_SHIFT_
+ mov ditherRandSeed2, eax
+ shr eax, PA_DITHER_SHIFT_
+ //add eax, edx // eax -> current
+ lea eax, [eax+edx]
+ mov edx, ditherPrevious
+ neg edx
+ lea edx, [eax+edx] // highpass = current - previous
+ mov highpassedDither, edx
+ mov ditherPrevious, eax // previous = current
+ mov eax, sourceByteStride // restore eax
+ fild highpassedDither
+ fmul const_float_dither_scale_
+ // end generate dither, dither signal in st(0)
+
+ faddp st(1), st(0) // stack: dither + value*(int scaler), int scaler
+ fistp dword ptr [edi] // pop st(0) into dest, stack: int scaler
+ jmp Float32_To_Int32_DitherClip_stored
+
+ Float32_To_Int32_DitherClip_clamp:
+ mov edx, dword ptr [esi] // load floating point value into integer register
+ shr edx, 31 // move sign bit into bit 0
+ add esi, eax // increment source ptr
+ //lea esi, [esi+eax]
+ add edx, 0x7FFFFFFF // convert to maximum range integers
+ mov dword ptr [edi], edx
+
+ Float32_To_Int32_DitherClip_stored:
+
+ //add edi, ebx // increment destination ptr
+ lea edi, [edi+ebx]
+
+ cmp esi, ecx // has src ptr reached end?
+ jne Float32_To_Int32_DitherClip_loop
+
+ ffree st(0)
+ fincstp
+
+ fwait
+ fnclex
+ fldcw savedFpuControlWord
+ }
+
+ ditherGenerator->previous = ditherPrevious;
+ ditherGenerator->randSeed1 = ditherRandSeed1;
+ ditherGenerator->randSeed2 = ditherRandSeed2;
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int24(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+/*
+ float *src = (float*)sourceBuffer;
+ unsigned char *dest = (unsigned char*)destinationBuffer;
+ signed long temp;
+
+ (void) ditherGenerator; // unused parameter
+
+ while( count-- )
+ {
+ // convert to 32 bit and drop the low 8 bits
+ double scaled = *src * 0x7FFFFFFF;
+ temp = (signed long) scaled;
+
+ dest[0] = (unsigned char)(temp >> 8);
+ dest[1] = (unsigned char)(temp >> 16);
+ dest[2] = (unsigned char)(temp >> 24);
+
+ src += sourceStride;
+ dest += destinationStride * 3;
+ }
+*/
+
+ short savedFpuControlWord;
+
+ signed long tempInt32;
+
+ (void) ditherGenerator; /* unused parameter */
+
+ __asm{
+ // esi -> source ptr
+ // eax -> source byte stride
+ // edi -> destination ptr
+ // ebx -> destination byte stride
+ // ecx -> source end ptr
+ // edx -> temp
+
+ mov esi, sourceBuffer
+
+ mov edx, 4 // sizeof float32
+ mov eax, sourceStride
+ imul eax, edx
+
+ mov ecx, count
+ imul ecx, eax
+ add ecx, esi
+
+ mov edi, destinationBuffer
+
+ mov edx, 3 // sizeof int24
+ mov ebx, destinationStride
+ imul ebx, edx
+
+ fwait
+ fstcw savedFpuControlWord
+ fldcw fpuControlWord_
+
+ fld int24Scaler_ // stack: (int)0x7FFFFF
+
+ Float32_To_Int24_loop:
+
+ // load unscaled value into st(0)
+ fld dword ptr [esi] // stack: value, (int)0x7FFFFF
+ add esi, eax // increment source ptr
+ //lea esi, [esi+eax]
+ fmul st(0), st(1) // st(0) *= st(1), stack: value*0x7FFFFF, (int)0x7FFFFF
+ fistp tempInt32 // pop st(0) into tempInt32, stack: (int)0x7FFFFF
+ mov edx, tempInt32
+
+ mov byte ptr [edi], DL
+ shr edx, 8
+ //mov byte ptr [edi+1], DL
+ //mov byte ptr [edi+2], DH
+ mov word ptr [edi+1], DX
+
+ //add edi, ebx // increment destination ptr
+ lea edi, [edi+ebx]
+
+ cmp esi, ecx // has src ptr reached end?
+ jne Float32_To_Int24_loop
+
+ ffree st(0)
+ fincstp
+
+ fwait
+ fnclex
+ fldcw savedFpuControlWord
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int24_Clip(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+/*
+ float *src = (float*)sourceBuffer;
+ unsigned char *dest = (unsigned char*)destinationBuffer;
+ signed long temp;
+
+ (void) ditherGenerator; // unused parameter
+
+ while( count-- )
+ {
+ // convert to 32 bit and drop the low 8 bits
+ double scaled = *src * 0x7FFFFFFF;
+ PA_CLIP_( scaled, -2147483648., 2147483647. );
+ temp = (signed long) scaled;
+
+ dest[0] = (unsigned char)(temp >> 8);
+ dest[1] = (unsigned char)(temp >> 16);
+ dest[2] = (unsigned char)(temp >> 24);
+
+ src += sourceStride;
+ dest += destinationStride * 3;
+ }
+*/
+
+ short savedFpuControlWord;
+
+ signed long tempInt32;
+
+ (void) ditherGenerator; /* unused parameter */
+
+ __asm{
+ // esi -> source ptr
+ // eax -> source byte stride
+ // edi -> destination ptr
+ // ebx -> destination byte stride
+ // ecx -> source end ptr
+ // edx -> temp
+
+ mov esi, sourceBuffer
+
+ mov edx, 4 // sizeof float32
+ mov eax, sourceStride
+ imul eax, edx
+
+ mov ecx, count
+ imul ecx, eax
+ add ecx, esi
+
+ mov edi, destinationBuffer
+
+ mov edx, 3 // sizeof int24
+ mov ebx, destinationStride
+ imul ebx, edx
+
+ fwait
+ fstcw savedFpuControlWord
+ fldcw fpuControlWord_
+
+ fld int24Scaler_ // stack: (int)0x7FFFFF
+
+ Float32_To_Int24_Clip_loop:
+
+ mov edx, dword ptr [esi] // load floating point value into integer register
+
+ and edx, 0x7FFFFFFF // mask off sign
+ cmp edx, 0x3F800000 // greater than 1.0 or less than -1.0
+
+ jg Float32_To_Int24_Clip_clamp
+
+ // load unscaled value into st(0)
+ fld dword ptr [esi] // stack: value, (int)0x7FFFFF
+ add esi, eax // increment source ptr
+ //lea esi, [esi+eax]
+ fmul st(0), st(1) // st(0) *= st(1), stack: value*0x7FFFFF, (int)0x7FFFFF
+ fistp tempInt32 // pop st(0) into tempInt32, stack: (int)0x7FFFFF
+ mov edx, tempInt32
+ jmp Float32_To_Int24_Clip_store
+
+ Float32_To_Int24_Clip_clamp:
+ mov edx, dword ptr [esi] // load floating point value into integer register
+ shr edx, 31 // move sign bit into bit 0
+ add esi, eax // increment source ptr
+ //lea esi, [esi+eax]
+ add edx, 0x7FFFFF // convert to maximum range integers
+
+ Float32_To_Int24_Clip_store:
+
+ mov byte ptr [edi], DL
+ shr edx, 8
+ //mov byte ptr [edi+1], DL
+ //mov byte ptr [edi+2], DH
+ mov word ptr [edi+1], DX
+
+ //add edi, ebx // increment destination ptr
+ lea edi, [edi+ebx]
+
+ cmp esi, ecx // has src ptr reached end?
+ jne Float32_To_Int24_Clip_loop
+
+ ffree st(0)
+ fincstp
+
+ fwait
+ fnclex
+ fldcw savedFpuControlWord
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int24_DitherClip(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+/*
+ float *src = (float*)sourceBuffer;
+ unsigned char *dest = (unsigned char*)destinationBuffer;
+ signed long temp;
+
+ while( count-- )
+ {
+ // convert to 32 bit and drop the low 8 bits
+
+ // FIXME: the dither amplitude here appears to be too small by 8 bits
+ double dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
+ // use smaller scaler to prevent overflow when we add the dither
+ double dithered = ((double)*src * (2147483646.0)) + dither;
+ PA_CLIP_( dithered, -2147483648., 2147483647. );
+
+ temp = (signed long) dithered;
+
+ dest[0] = (unsigned char)(temp >> 8);
+ dest[1] = (unsigned char)(temp >> 16);
+ dest[2] = (unsigned char)(temp >> 24);
+
+ src += sourceStride;
+ dest += destinationStride * 3;
+ }
+*/
+
+ short savedFpuControlWord;
+
+ // spill storage:
+ signed long sourceByteStride;
+ signed long highpassedDither;
+
+ // dither state:
+ unsigned long ditherPrevious = ditherGenerator->previous;
+ unsigned long ditherRandSeed1 = ditherGenerator->randSeed1;
+ unsigned long ditherRandSeed2 = ditherGenerator->randSeed2;
+
+ signed long tempInt32;
+
+ __asm{
+ // esi -> source ptr
+ // eax -> source byte stride
+ // edi -> destination ptr
+ // ebx -> destination byte stride
+ // ecx -> source end ptr
+ // edx -> temp
+
+ mov esi, sourceBuffer
+
+ mov edx, 4 // sizeof float32
+ mov eax, sourceStride
+ imul eax, edx
+
+ mov ecx, count
+ imul ecx, eax
+ add ecx, esi
+
+ mov edi, destinationBuffer
+
+ mov edx, 3 // sizeof int24
+ mov ebx, destinationStride
+ imul ebx, edx
+
+ fwait
+ fstcw savedFpuControlWord
+ fldcw fpuControlWord_
+
+ fld ditheredInt24Scaler_ // stack: int scaler
+
+ Float32_To_Int24_DitherClip_loop:
+
+ mov edx, dword ptr [esi] // load floating point value into integer register
+
+ and edx, 0x7FFFFFFF // mask off sign
+ cmp edx, 0x3F800000 // greater than 1.0 or less than -1.0
+
+ jg Float32_To_Int24_DitherClip_clamp
+
+ // load unscaled value into st(0)
+ fld dword ptr [esi] // stack: value, int scaler
+ add esi, eax // increment source ptr
+ //lea esi, [esi+eax]
+ fmul st(0), st(1) // st(0) *= st(1), stack: value*(int scaler), int scaler
+
+ /*
+ // call PaUtil_GenerateFloatTriangularDither with C calling convention
+ mov sourceByteStride, eax // save eax
+ mov sourceEnd, ecx // save ecx
+ push ditherGenerator // pass ditherGenerator parameter on stack
+ call PaUtil_GenerateFloatTriangularDither // stack: dither, value*(int scaler), int scaler
+ pop edx // clear parameter off stack
+ mov ecx, sourceEnd // restore ecx
+ mov eax, sourceByteStride // restore eax
+ */
+
+ // generate dither
+ mov sourceByteStride, eax // save eax
+ mov edx, 196314165
+ mov eax, ditherRandSeed1
+ mul edx // eax:edx = eax * 196314165
+ //add eax, 907633515
+ lea eax, [eax+907633515]
+ mov ditherRandSeed1, eax
+ mov edx, 196314165
+ mov eax, ditherRandSeed2
+ mul edx // eax:edx = eax * 196314165
+ //add eax, 907633515
+ lea eax, [eax+907633515]
+ mov edx, ditherRandSeed1
+ shr edx, PA_DITHER_SHIFT_
+ mov ditherRandSeed2, eax
+ shr eax, PA_DITHER_SHIFT_
+ //add eax, edx // eax -> current
+ lea eax, [eax+edx]
+ mov edx, ditherPrevious
+ neg edx
+ lea edx, [eax+edx] // highpass = current - previous
+ mov highpassedDither, edx
+ mov ditherPrevious, eax // previous = current
+ mov eax, sourceByteStride // restore eax
+ fild highpassedDither
+ fmul const_float_dither_scale_
+ // end generate dither, dither signal in st(0)
+
+ faddp st(1), st(0) // stack: dither * value*(int scaler), int scaler
+ fistp tempInt32 // pop st(0) into tempInt32, stack: int scaler
+ mov edx, tempInt32
+ jmp Float32_To_Int24_DitherClip_store
+
+ Float32_To_Int24_DitherClip_clamp:
+ mov edx, dword ptr [esi] // load floating point value into integer register
+ shr edx, 31 // move sign bit into bit 0
+ add esi, eax // increment source ptr
+ //lea esi, [esi+eax]
+ add edx, 0x7FFFFF // convert to maximum range integers
+
+ Float32_To_Int24_DitherClip_store:
+
+ mov byte ptr [edi], DL
+ shr edx, 8
+ //mov byte ptr [edi+1], DL
+ //mov byte ptr [edi+2], DH
+ mov word ptr [edi+1], DX
+
+ //add edi, ebx // increment destination ptr
+ lea edi, [edi+ebx]
+
+ cmp esi, ecx // has src ptr reached end?
+ jne Float32_To_Int24_DitherClip_loop
+
+ ffree st(0)
+ fincstp
+
+ fwait
+ fnclex
+ fldcw savedFpuControlWord
+ }
+
+ ditherGenerator->previous = ditherPrevious;
+ ditherGenerator->randSeed1 = ditherRandSeed1;
+ ditherGenerator->randSeed2 = ditherRandSeed2;
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int16(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+/*
+ float *src = (float*)sourceBuffer;
+ signed short *dest = (signed short*)destinationBuffer;
+ (void)ditherGenerator; // unused parameter
+
+ while( count-- )
+ {
+
+ short samp = (short) (*src * (32767.0f));
+ *dest = samp;
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+*/
+
+ short savedFpuControlWord;
+
+ (void) ditherGenerator; /* unused parameter */
+
+ __asm{
+ // esi -> source ptr
+ // eax -> source byte stride
+ // edi -> destination ptr
+ // ebx -> destination byte stride
+ // ecx -> source end ptr
+ // edx -> temp
+
+ mov esi, sourceBuffer
+
+ mov edx, 4 // sizeof float32
+ mov eax, sourceStride
+ imul eax, edx // source byte stride
+
+ mov ecx, count
+ imul ecx, eax
+ add ecx, esi // source end ptr = count * source byte stride + source ptr
+
+ mov edi, destinationBuffer
+
+ mov edx, 2 // sizeof int16
+ mov ebx, destinationStride
+ imul ebx, edx // destination byte stride
+
+ fwait
+ fstcw savedFpuControlWord
+ fldcw fpuControlWord_
+
+ fld int16Scaler_ // stack: (int)0x7FFF
+
+ Float32_To_Int16_loop:
+
+ // load unscaled value into st(0)
+ fld dword ptr [esi] // stack: value, (int)0x7FFF
+ add esi, eax // increment source ptr
+ //lea esi, [esi+eax]
+ fmul st(0), st(1) // st(0) *= st(1), stack: value*0x7FFF, (int)0x7FFF
+ fistp word ptr [edi] // store scaled int into dest, stack: (int)0x7FFF
+
+ add edi, ebx // increment destination ptr
+ //lea edi, [edi+ebx]
+
+ cmp esi, ecx // has src ptr reached end?
+ jne Float32_To_Int16_loop
+
+ ffree st(0)
+ fincstp
+
+ fwait
+ fnclex
+ fldcw savedFpuControlWord
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int16_Clip(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+/*
+ float *src = (float*)sourceBuffer;
+ signed short *dest = (signed short*)destinationBuffer;
+ (void)ditherGenerator; // unused parameter
+
+ while( count-- )
+ {
+ long samp = (signed long) (*src * (32767.0f));
+ PA_CLIP_( samp, -0x8000, 0x7FFF );
+ *dest = (signed short) samp;
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+*/
+
+ short savedFpuControlWord;
+
+ (void) ditherGenerator; /* unused parameter */
+
+ __asm{
+ // esi -> source ptr
+ // eax -> source byte stride
+ // edi -> destination ptr
+ // ebx -> destination byte stride
+ // ecx -> source end ptr
+ // edx -> temp
+
+ mov esi, sourceBuffer
+
+ mov edx, 4 // sizeof float32
+ mov eax, sourceStride
+ imul eax, edx // source byte stride
+
+ mov ecx, count
+ imul ecx, eax
+ add ecx, esi // source end ptr = count * source byte stride + source ptr
+
+ mov edi, destinationBuffer
+
+ mov edx, 2 // sizeof int16
+ mov ebx, destinationStride
+ imul ebx, edx // destination byte stride
+
+ fwait
+ fstcw savedFpuControlWord
+ fldcw fpuControlWord_
+
+ fld int16Scaler_ // stack: (int)0x7FFF
+
+ Float32_To_Int16_Clip_loop:
+
+ mov edx, dword ptr [esi] // load floating point value into integer register
+
+ and edx, 0x7FFFFFFF // mask off sign
+ cmp edx, 0x3F800000 // greater than 1.0 or less than -1.0
+
+ jg Float32_To_Int16_Clip_clamp
+
+ // load unscaled value into st(0)
+ fld dword ptr [esi] // stack: value, (int)0x7FFF
+ add esi, eax // increment source ptr
+ //lea esi, [esi+eax]
+ fmul st(0), st(1) // st(0) *= st(1), stack: value*0x7FFF, (int)0x7FFF
+ fistp word ptr [edi] // store scaled int into dest, stack: (int)0x7FFF
+ jmp Float32_To_Int16_Clip_stored
+
+ Float32_To_Int16_Clip_clamp:
+ mov edx, dword ptr [esi] // load floating point value into integer register
+ shr edx, 31 // move sign bit into bit 0
+ add esi, eax // increment source ptr
+ //lea esi, [esi+eax]
+ add dx, 0x7FFF // convert to maximum range integers
+ mov word ptr [edi], dx // store clamped into into dest
+
+ Float32_To_Int16_Clip_stored:
+
+ add edi, ebx // increment destination ptr
+ //lea edi, [edi+ebx]
+
+ cmp esi, ecx // has src ptr reached end?
+ jne Float32_To_Int16_Clip_loop
+
+ ffree st(0)
+ fincstp
+
+ fwait
+ fnclex
+ fldcw savedFpuControlWord
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int16_DitherClip(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+/*
+ float *src = (float*)sourceBuffer;
+ signed short *dest = (signed short*)destinationBuffer;
+ (void)ditherGenerator; // unused parameter
+
+ while( count-- )
+ {
+
+ float dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
+ // use smaller scaler to prevent overflow when we add the dither
+ float dithered = (*src * (32766.0f)) + dither;
+ signed long samp = (signed long) dithered;
+ PA_CLIP_( samp, -0x8000, 0x7FFF );
+ *dest = (signed short) samp;
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+*/
+
+ short savedFpuControlWord;
+
+ // spill storage:
+ signed long sourceByteStride;
+ signed long highpassedDither;
+
+ // dither state:
+ unsigned long ditherPrevious = ditherGenerator->previous;
+ unsigned long ditherRandSeed1 = ditherGenerator->randSeed1;
+ unsigned long ditherRandSeed2 = ditherGenerator->randSeed2;
+
+ __asm{
+ // esi -> source ptr
+ // eax -> source byte stride
+ // edi -> destination ptr
+ // ebx -> destination byte stride
+ // ecx -> source end ptr
+ // edx -> temp
+
+ mov esi, sourceBuffer
+
+ mov edx, 4 // sizeof float32
+ mov eax, sourceStride
+ imul eax, edx // source byte stride
+
+ mov ecx, count
+ imul ecx, eax
+ add ecx, esi // source end ptr = count * source byte stride + source ptr
+
+ mov edi, destinationBuffer
+
+ mov edx, 2 // sizeof int16
+ mov ebx, destinationStride
+ imul ebx, edx // destination byte stride
+
+ fwait
+ fstcw savedFpuControlWord
+ fldcw fpuControlWord_
+
+ fld ditheredInt16Scaler_ // stack: int scaler
+
+ Float32_To_Int16_DitherClip_loop:
+
+ mov edx, dword ptr [esi] // load floating point value into integer register
+
+ and edx, 0x7FFFFFFF // mask off sign
+ cmp edx, 0x3F800000 // greater than 1.0 or less than -1.0
+
+ jg Float32_To_Int16_DitherClip_clamp
+
+ // load unscaled value into st(0)
+ fld dword ptr [esi] // stack: value, int scaler
+ add esi, eax // increment source ptr
+ //lea esi, [esi+eax]
+ fmul st(0), st(1) // st(0) *= st(1), stack: value*(int scaler), int scaler
+
+ /*
+ // call PaUtil_GenerateFloatTriangularDither with C calling convention
+ mov sourceByteStride, eax // save eax
+ mov sourceEnd, ecx // save ecx
+ push ditherGenerator // pass ditherGenerator parameter on stack
+ call PaUtil_GenerateFloatTriangularDither // stack: dither, value*(int scaler), int scaler
+ pop edx // clear parameter off stack
+ mov ecx, sourceEnd // restore ecx
+ mov eax, sourceByteStride // restore eax
+ */
+
+ // generate dither
+ mov sourceByteStride, eax // save eax
+ mov edx, 196314165
+ mov eax, ditherRandSeed1
+ mul edx // eax:edx = eax * 196314165
+ //add eax, 907633515
+ lea eax, [eax+907633515]
+ mov ditherRandSeed1, eax
+ mov edx, 196314165
+ mov eax, ditherRandSeed2
+ mul edx // eax:edx = eax * 196314165
+ //add eax, 907633515
+ lea eax, [eax+907633515]
+ mov edx, ditherRandSeed1
+ shr edx, PA_DITHER_SHIFT_
+ mov ditherRandSeed2, eax
+ shr eax, PA_DITHER_SHIFT_
+ //add eax, edx // eax -> current
+ lea eax, [eax+edx] // current = randSeed1>>x + randSeed2>>x
+ mov edx, ditherPrevious
+ neg edx
+ lea edx, [eax+edx] // highpass = current - previous
+ mov highpassedDither, edx
+ mov ditherPrevious, eax // previous = current
+ mov eax, sourceByteStride // restore eax
+ fild highpassedDither
+ fmul const_float_dither_scale_
+ // end generate dither, dither signal in st(0)
+
+ faddp st(1), st(0) // stack: dither * value*(int scaler), int scaler
+ fistp word ptr [edi] // store scaled int into dest, stack: int scaler
+ jmp Float32_To_Int16_DitherClip_stored
+
+ Float32_To_Int16_DitherClip_clamp:
+ mov edx, dword ptr [esi] // load floating point value into integer register
+ shr edx, 31 // move sign bit into bit 0
+ add esi, eax // increment source ptr
+ //lea esi, [esi+eax]
+ add dx, 0x7FFF // convert to maximum range integers
+ mov word ptr [edi], dx // store clamped into into dest
+
+ Float32_To_Int16_DitherClip_stored:
+
+ add edi, ebx // increment destination ptr
+ //lea edi, [edi+ebx]
+
+ cmp esi, ecx // has src ptr reached end?
+ jne Float32_To_Int16_DitherClip_loop
+
+ ffree st(0)
+ fincstp
+
+ fwait
+ fnclex
+ fldcw savedFpuControlWord
+ }
+
+ ditherGenerator->previous = ditherPrevious;
+ ditherGenerator->randSeed1 = ditherRandSeed1;
+ ditherGenerator->randSeed2 = ditherRandSeed2;
+}
+
+/* -------------------------------------------------------------------------- */
+
+void PaUtil_InitializeX86PlainConverters( void )
+{
+ paConverters.Float32_To_Int32 = Float32_To_Int32;
+ paConverters.Float32_To_Int32_Clip = Float32_To_Int32_Clip;
+ paConverters.Float32_To_Int32_DitherClip = Float32_To_Int32_DitherClip;
+
+ paConverters.Float32_To_Int24 = Float32_To_Int24;
+ paConverters.Float32_To_Int24_Clip = Float32_To_Int24_Clip;
+ paConverters.Float32_To_Int24_DitherClip = Float32_To_Int24_DitherClip;
+
+ paConverters.Float32_To_Int16 = Float32_To_Int16;
+ paConverters.Float32_To_Int16_Clip = Float32_To_Int16_Clip;
+ paConverters.Float32_To_Int16_DitherClip = Float32_To_Int16_DitherClip;
+}
+
+#endif
+
+/* -------------------------------------------------------------------------- */
diff --git a/portaudio/src/os/win/pa_x86_plain_converters.h b/portaudio/src/os/win/pa_x86_plain_converters.h
new file mode 100644
index 0000000..8914ed1
--- /dev/null
+++ b/portaudio/src/os/win/pa_x86_plain_converters.h
@@ -0,0 +1,60 @@
+/*
+ * Plain Intel IA32 assembly implementations of PortAudio sample converter functions.
+ * Copyright (c) 1999-2002 Ross Bencina, Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/*
+ * The text above constitutes the entire PortAudio license; however,
+ * the PortAudio community also makes the following non-binding requests:
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version. It is also
+ * requested that these non-binding requests be included along with the
+ * license above.
+ */
+
+/** @file
+ @ingroup win_src
+*/
+
+#ifndef PA_X86_PLAIN_CONVERTERS_H
+#define PA_X86_PLAIN_CONVERTERS_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+
+/**
+ @brief Install optimized converter functions suitable for all IA32 processors
+
+ It is recommended to call PaUtil_InitializeX86PlainConverters prior to calling Pa_Initialize
+*/
+void PaUtil_InitializeX86PlainConverters( void );
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* PA_X86_PLAIN_CONVERTERS_H */