Remove XAudio2
Audio on Windows is provided by the cubeb (shared mode) and WASAPI (exclusive mode) backends, both of which exhibit lower latency.
This commit is contained in:
parent
828dc0f204
commit
0f3695a14b
File diff suppressed because it is too large
Load Diff
|
@ -1,263 +0,0 @@
|
||||||
/***************************************************************************
|
|
||||||
*
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
*
|
|
||||||
* File: audiodefs.h
|
|
||||||
* Content: Basic constants and data types for audio work.
|
|
||||||
*
|
|
||||||
* Remarks: This header file defines all of the audio format constants and
|
|
||||||
* structures required for XAudio2 and XACT work. Providing these
|
|
||||||
* in a single location avoids certain dependency problems in the
|
|
||||||
* legacy audio headers (mmreg.h, mmsystem.h, ksmedia.h).
|
|
||||||
*
|
|
||||||
* NOTE: Including the legacy headers after this one may cause a
|
|
||||||
* compilation error, because they define some of the same types
|
|
||||||
* defined here without preprocessor guards to avoid multiple
|
|
||||||
* definitions. If a source file needs one of the old headers,
|
|
||||||
* it must include it before including audiodefs.h.
|
|
||||||
*
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
#ifndef __AUDIODEFS_INCLUDED__
|
|
||||||
#define __AUDIODEFS_INCLUDED__
|
|
||||||
|
|
||||||
#include <windef.h> // For WORD, DWORD, etc.
|
|
||||||
|
|
||||||
#pragma pack(push, 1) // Pack structures to 1-byte boundaries
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
*
|
|
||||||
* WAVEFORMATEX: Base structure for many audio formats. Format-specific
|
|
||||||
* extensions can be defined for particular formats by using a non-zero
|
|
||||||
* cbSize value and adding extra fields to the end of this structure.
|
|
||||||
*
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
#ifndef _WAVEFORMATEX_
|
|
||||||
|
|
||||||
#define _WAVEFORMATEX_
|
|
||||||
typedef struct tWAVEFORMATEX
|
|
||||||
{
|
|
||||||
WORD wFormatTag; // Integer identifier of the format
|
|
||||||
WORD nChannels; // Number of audio channels
|
|
||||||
DWORD nSamplesPerSec; // Audio sample rate
|
|
||||||
DWORD nAvgBytesPerSec; // Bytes per second (possibly approximate)
|
|
||||||
WORD nBlockAlign; // Size in bytes of a sample block (all channels)
|
|
||||||
WORD wBitsPerSample; // Size in bits of a single per-channel sample
|
|
||||||
WORD cbSize; // Bytes of extra data appended to this struct
|
|
||||||
} WAVEFORMATEX;
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Defining pointer types outside of the #if block to make sure they are
|
|
||||||
// defined even if mmreg.h or mmsystem.h is #included before this file
|
|
||||||
|
|
||||||
typedef WAVEFORMATEX *PWAVEFORMATEX, *NPWAVEFORMATEX, *LPWAVEFORMATEX;
|
|
||||||
typedef const WAVEFORMATEX *PCWAVEFORMATEX, *LPCWAVEFORMATEX;
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
*
|
|
||||||
* WAVEFORMATEXTENSIBLE: Extended version of WAVEFORMATEX that should be
|
|
||||||
* used as a basis for all new audio formats. The format tag is replaced
|
|
||||||
* with a GUID, allowing new formats to be defined without registering a
|
|
||||||
* format tag with Microsoft. There are also new fields that can be used
|
|
||||||
* to specify the spatial positions for each channel and the bit packing
|
|
||||||
* used for wide samples (e.g. 24-bit PCM samples in 32-bit containers).
|
|
||||||
*
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
#ifndef _WAVEFORMATEXTENSIBLE_
|
|
||||||
|
|
||||||
#define _WAVEFORMATEXTENSIBLE_
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
WAVEFORMATEX Format; // Base WAVEFORMATEX data
|
|
||||||
union
|
|
||||||
{
|
|
||||||
WORD wValidBitsPerSample; // Valid bits in each sample container
|
|
||||||
WORD wSamplesPerBlock; // Samples per block of audio data; valid
|
|
||||||
// if wBitsPerSample=0 (but rarely used).
|
|
||||||
WORD wReserved; // Zero if neither case above applies.
|
|
||||||
} Samples;
|
|
||||||
DWORD dwChannelMask; // Positions of the audio channels
|
|
||||||
GUID SubFormat; // Format identifier GUID
|
|
||||||
} WAVEFORMATEXTENSIBLE;
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef WAVEFORMATEXTENSIBLE *PWAVEFORMATEXTENSIBLE, *LPWAVEFORMATEXTENSIBLE;
|
|
||||||
typedef const WAVEFORMATEXTENSIBLE *PCWAVEFORMATEXTENSIBLE, *LPCWAVEFORMATEXTENSIBLE;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
*
|
|
||||||
* Define the most common wave format tags used in WAVEFORMATEX formats.
|
|
||||||
*
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
#ifndef WAVE_FORMAT_PCM // Pulse Code Modulation
|
|
||||||
|
|
||||||
// If WAVE_FORMAT_PCM is not defined, we need to define some legacy types
|
|
||||||
// for compatibility with the Windows mmreg.h / mmsystem.h header files.
|
|
||||||
|
|
||||||
// Old general format structure (information common to all formats)
|
|
||||||
typedef struct waveformat_tag
|
|
||||||
{
|
|
||||||
WORD wFormatTag;
|
|
||||||
WORD nChannels;
|
|
||||||
DWORD nSamplesPerSec;
|
|
||||||
DWORD nAvgBytesPerSec;
|
|
||||||
WORD nBlockAlign;
|
|
||||||
} WAVEFORMAT, *PWAVEFORMAT, NEAR *NPWAVEFORMAT, FAR *LPWAVEFORMAT;
|
|
||||||
|
|
||||||
// Specific format structure for PCM data
|
|
||||||
typedef struct pcmwaveformat_tag
|
|
||||||
{
|
|
||||||
WAVEFORMAT wf;
|
|
||||||
WORD wBitsPerSample;
|
|
||||||
} PCMWAVEFORMAT, *PPCMWAVEFORMAT, NEAR *NPPCMWAVEFORMAT, FAR *LPPCMWAVEFORMAT;
|
|
||||||
|
|
||||||
#define WAVE_FORMAT_PCM 0x0001
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef WAVE_FORMAT_ADPCM // Microsoft Adaptive Differental PCM
|
|
||||||
|
|
||||||
// Replicate the Microsoft ADPCM type definitions from mmreg.h.
|
|
||||||
|
|
||||||
typedef struct adpcmcoef_tag
|
|
||||||
{
|
|
||||||
short iCoef1;
|
|
||||||
short iCoef2;
|
|
||||||
} ADPCMCOEFSET;
|
|
||||||
|
|
||||||
#pragma warning(push)
|
|
||||||
#pragma warning(disable:4200) // Disable zero-sized array warnings
|
|
||||||
|
|
||||||
typedef struct adpcmwaveformat_tag {
|
|
||||||
WAVEFORMATEX wfx;
|
|
||||||
WORD wSamplesPerBlock;
|
|
||||||
WORD wNumCoef;
|
|
||||||
ADPCMCOEFSET aCoef[]; // Always 7 coefficient pairs for MS ADPCM
|
|
||||||
} ADPCMWAVEFORMAT;
|
|
||||||
|
|
||||||
#pragma warning(pop)
|
|
||||||
|
|
||||||
#define WAVE_FORMAT_ADPCM 0x0002
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Other frequently used format tags
|
|
||||||
|
|
||||||
#ifndef WAVE_FORMAT_UNKNOWN
|
|
||||||
#define WAVE_FORMAT_UNKNOWN 0x0000 // Unknown or invalid format tag
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef WAVE_FORMAT_IEEE_FLOAT
|
|
||||||
#define WAVE_FORMAT_IEEE_FLOAT 0x0003 // 32-bit floating-point
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef WAVE_FORMAT_MPEGLAYER3
|
|
||||||
#define WAVE_FORMAT_MPEGLAYER3 0x0055 // ISO/MPEG Layer3
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef WAVE_FORMAT_DOLBY_AC3_SPDIF
|
|
||||||
#define WAVE_FORMAT_DOLBY_AC3_SPDIF 0x0092 // Dolby Audio Codec 3 over S/PDIF
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef WAVE_FORMAT_WMAUDIO2
|
|
||||||
#define WAVE_FORMAT_WMAUDIO2 0x0161 // Windows Media Audio
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef WAVE_FORMAT_WMAUDIO3
|
|
||||||
#define WAVE_FORMAT_WMAUDIO3 0x0162 // Windows Media Audio Pro
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef WAVE_FORMAT_WMASPDIF
|
|
||||||
#define WAVE_FORMAT_WMASPDIF 0x0164 // Windows Media Audio over S/PDIF
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef WAVE_FORMAT_EXTENSIBLE
|
|
||||||
#define WAVE_FORMAT_EXTENSIBLE 0xFFFE // All WAVEFORMATEXTENSIBLE formats
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
*
|
|
||||||
* Define the most common wave format GUIDs used in WAVEFORMATEXTENSIBLE
|
|
||||||
* formats. Note that including the Windows ksmedia.h header after this
|
|
||||||
* one will cause build problems; this cannot be avoided, since ksmedia.h
|
|
||||||
* defines these macros without preprocessor guards.
|
|
||||||
*
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
#ifdef __cplusplus // uuid() and __uuidof() are only available in C++
|
|
||||||
|
|
||||||
#ifndef KSDATAFORMAT_SUBTYPE_PCM
|
|
||||||
struct __declspec(uuid("00000001-0000-0010-8000-00aa00389b71")) KSDATAFORMAT_SUBTYPE_PCM_STRUCT;
|
|
||||||
#define KSDATAFORMAT_SUBTYPE_PCM __uuidof(KSDATAFORMAT_SUBTYPE_PCM_STRUCT)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef KSDATAFORMAT_SUBTYPE_ADPCM
|
|
||||||
struct __declspec(uuid("00000002-0000-0010-8000-00aa00389b71")) KSDATAFORMAT_SUBTYPE_ADPCM_STRUCT;
|
|
||||||
#define KSDATAFORMAT_SUBTYPE_ADPCM __uuidof(KSDATAFORMAT_SUBTYPE_ADPCM_STRUCT)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
|
|
||||||
struct __declspec(uuid("00000003-0000-0010-8000-00aa00389b71")) KSDATAFORMAT_SUBTYPE_IEEE_FLOAT_STRUCT;
|
|
||||||
#define KSDATAFORMAT_SUBTYPE_IEEE_FLOAT __uuidof(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT_STRUCT)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
*
|
|
||||||
* Speaker positions used in the WAVEFORMATEXTENSIBLE dwChannelMask field.
|
|
||||||
*
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
#ifndef SPEAKER_FRONT_LEFT
|
|
||||||
#define SPEAKER_FRONT_LEFT 0x00000001
|
|
||||||
#define SPEAKER_FRONT_RIGHT 0x00000002
|
|
||||||
#define SPEAKER_FRONT_CENTER 0x00000004
|
|
||||||
#define SPEAKER_LOW_FREQUENCY 0x00000008
|
|
||||||
#define SPEAKER_BACK_LEFT 0x00000010
|
|
||||||
#define SPEAKER_BACK_RIGHT 0x00000020
|
|
||||||
#define SPEAKER_FRONT_LEFT_OF_CENTER 0x00000040
|
|
||||||
#define SPEAKER_FRONT_RIGHT_OF_CENTER 0x00000080
|
|
||||||
#define SPEAKER_BACK_CENTER 0x00000100
|
|
||||||
#define SPEAKER_SIDE_LEFT 0x00000200
|
|
||||||
#define SPEAKER_SIDE_RIGHT 0x00000400
|
|
||||||
#define SPEAKER_TOP_CENTER 0x00000800
|
|
||||||
#define SPEAKER_TOP_FRONT_LEFT 0x00001000
|
|
||||||
#define SPEAKER_TOP_FRONT_CENTER 0x00002000
|
|
||||||
#define SPEAKER_TOP_FRONT_RIGHT 0x00004000
|
|
||||||
#define SPEAKER_TOP_BACK_LEFT 0x00008000
|
|
||||||
#define SPEAKER_TOP_BACK_CENTER 0x00010000
|
|
||||||
#define SPEAKER_TOP_BACK_RIGHT 0x00020000
|
|
||||||
#define SPEAKER_RESERVED 0x7FFC0000
|
|
||||||
#define SPEAKER_ALL 0x80000000
|
|
||||||
#define _SPEAKER_POSITIONS_
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef SPEAKER_STEREO
|
|
||||||
#define SPEAKER_MONO (SPEAKER_FRONT_CENTER)
|
|
||||||
#define SPEAKER_STEREO (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT)
|
|
||||||
#define SPEAKER_2POINT1 (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_LOW_FREQUENCY)
|
|
||||||
#define SPEAKER_SURROUND (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_BACK_CENTER)
|
|
||||||
#define SPEAKER_QUAD (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT)
|
|
||||||
#define SPEAKER_4POINT1 (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT)
|
|
||||||
#define SPEAKER_5POINT1 (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT)
|
|
||||||
#define SPEAKER_7POINT1 (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_FRONT_LEFT_OF_CENTER | SPEAKER_FRONT_RIGHT_OF_CENTER)
|
|
||||||
#define SPEAKER_5POINT1_SURROUND (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT)
|
|
||||||
#define SPEAKER_7POINT1_SURROUND (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#pragma pack(pop)
|
|
||||||
|
|
||||||
#endif // #ifndef __AUDIODEFS_INCLUDED__
|
|
|
@ -1,59 +0,0 @@
|
||||||
// comdecl.h: Macros to facilitate COM interface and GUID declarations.
|
|
||||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
|
|
||||||
#ifndef _COMDECL_H_
|
|
||||||
#define _COMDECL_H_
|
|
||||||
|
|
||||||
#ifndef _XBOX
|
|
||||||
#include <basetyps.h> // For standard COM interface macros
|
|
||||||
#else
|
|
||||||
#pragma warning(push)
|
|
||||||
#pragma warning(disable:4061)
|
|
||||||
#include <xtl.h> // Required by xobjbase.h
|
|
||||||
#include <xobjbase.h> // Special definitions for Xbox build
|
|
||||||
#pragma warning(pop)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// The DEFINE_CLSID() and DEFINE_IID() macros defined below allow COM GUIDs to
|
|
||||||
// be declared and defined in such a way that clients can obtain the GUIDs using
|
|
||||||
// either the __uuidof() extension or the old-style CLSID_Foo / IID_IFoo names.
|
|
||||||
// If using the latter approach, the client can also choose whether to get the
|
|
||||||
// GUID definitions by defining the INITGUID preprocessor constant or by linking
|
|
||||||
// to a GUID library. This works in either C or C++.
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
|
|
||||||
#define DECLSPEC_UUID_WRAPPER(x) __declspec(uuid(#x))
|
|
||||||
#ifdef INITGUID
|
|
||||||
|
|
||||||
#define DEFINE_CLSID(className, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
|
|
||||||
class DECLSPEC_UUID_WRAPPER(l##-##w1##-##w2##-##b1##b2##-##b3##b4##b5##b6##b7##b8) className; \
|
|
||||||
EXTERN_C const GUID DECLSPEC_SELECTANY CLSID_##className = __uuidof(className)
|
|
||||||
|
|
||||||
#define DEFINE_IID(interfaceName, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
|
|
||||||
interface DECLSPEC_UUID_WRAPPER(l##-##w1##-##w2##-##b1##b2##-##b3##b4##b5##b6##b7##b8) interfaceName; \
|
|
||||||
EXTERN_C const GUID DECLSPEC_SELECTANY IID_##interfaceName = __uuidof(interfaceName)
|
|
||||||
|
|
||||||
#else // INITGUID
|
|
||||||
|
|
||||||
#define DEFINE_CLSID(className, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
|
|
||||||
class DECLSPEC_UUID_WRAPPER(l##-##w1##-##w2##-##b1##b2##-##b3##b4##b5##b6##b7##b8) className; \
|
|
||||||
EXTERN_C const GUID CLSID_##className
|
|
||||||
|
|
||||||
#define DEFINE_IID(interfaceName, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
|
|
||||||
interface DECLSPEC_UUID_WRAPPER(l##-##w1##-##w2##-##b1##b2##-##b3##b4##b5##b6##b7##b8) interfaceName; \
|
|
||||||
EXTERN_C const GUID IID_##interfaceName
|
|
||||||
|
|
||||||
#endif // INITGUID
|
|
||||||
|
|
||||||
#else // __cplusplus
|
|
||||||
|
|
||||||
#define DEFINE_CLSID(className, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
|
|
||||||
DEFINE_GUID(CLSID_##className, 0x##l, 0x##w1, 0x##w2, 0x##b1, 0x##b2, 0x##b3, 0x##b4, 0x##b5, 0x##b6, 0x##b7, 0x##b8)
|
|
||||||
|
|
||||||
#define DEFINE_IID(interfaceName, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
|
|
||||||
DEFINE_GUID(IID_##interfaceName, 0x##l, 0x##w1, 0x##w2, 0x##b1, 0x##b2, 0x##b3, 0x##b4, 0x##b5, 0x##b6, 0x##b7, 0x##b8)
|
|
||||||
|
|
||||||
#endif // __cplusplus
|
|
||||||
|
|
||||||
#endif // #ifndef _COMDECL_H_
|
|
|
@ -1,18 +0,0 @@
|
||||||
/*==========================================================================;
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* File: dxsdkver.h
|
|
||||||
* Content: DirectX SDK Version Include File
|
|
||||||
*
|
|
||||||
****************************************************************************/
|
|
||||||
|
|
||||||
#ifndef _DXSDKVER_H_
|
|
||||||
#define _DXSDKVER_H_
|
|
||||||
|
|
||||||
#define _DXSDK_PRODUCT_MAJOR 9
|
|
||||||
#define _DXSDK_PRODUCT_MINOR 29
|
|
||||||
#define _DXSDK_BUILD_MAJOR 1962
|
|
||||||
#define _DXSDK_BUILD_MINOR 0
|
|
||||||
|
|
||||||
#endif // _DXSDKVER_H_
|
|
||||||
|
|
|
@ -1,718 +0,0 @@
|
||||||
/***************************************************************************
|
|
||||||
*
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
*
|
|
||||||
* File: xma2defs.h
|
|
||||||
* Content: Constants, data types and functions for XMA2 compressed audio.
|
|
||||||
*
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
#ifndef __XMA2DEFS_INCLUDED__
|
|
||||||
#define __XMA2DEFS_INCLUDED__
|
|
||||||
|
|
||||||
#include <sal.h> // Markers for documenting API semantics
|
|
||||||
#include <winerror.h> // For S_OK, E_FAIL
|
|
||||||
#include <audiodefs.h> // Basic data types and constants for audio work
|
|
||||||
|
|
||||||
|
|
||||||
/***************************************************************************
|
|
||||||
* Overview
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
// A typical XMA2 file contains these RIFF chunks:
|
|
||||||
//
|
|
||||||
// 'fmt' or 'XMA2' chunk (or both): A description of the XMA data's structure
|
|
||||||
// and characteristics (length, channels, sample rate, loops, block size, etc).
|
|
||||||
//
|
|
||||||
// 'seek' chunk: A seek table to help navigate the XMA data.
|
|
||||||
//
|
|
||||||
// 'data' chunk: The encoded XMA2 data.
|
|
||||||
//
|
|
||||||
// The encoded XMA2 data is structured as a set of BLOCKS, which contain PACKETS,
|
|
||||||
// which contain FRAMES, which contain SUBFRAMES (roughly speaking). The frames
|
|
||||||
// in a file may also be divided into several subsets, called STREAMS.
|
|
||||||
//
|
|
||||||
// FRAME: A variable-sized segment of XMA data that decodes to exactly 512 mono
|
|
||||||
// or stereo PCM samples. This is the smallest unit of XMA data that can
|
|
||||||
// be decoded in isolation. Frames are an arbitrary number of bits in
|
|
||||||
// length, and need not be byte-aligned. See "XMA frame structure" below.
|
|
||||||
//
|
|
||||||
// SUBFRAME: A region of bits in an XMA frame that decodes to 128 mono or stereo
|
|
||||||
// samples. The XMA decoder cannot decode a subframe in isolation; it needs
|
|
||||||
// a whole frame to work with. However, it can begin emitting the frame's
|
|
||||||
// decoded samples at any one of the four subframe boundaries. Subframes
|
|
||||||
// can be addressed for seeking and looping purposes.
|
|
||||||
//
|
|
||||||
// PACKET: A 2Kb region containing a 32-bit header and some XMA frames. Frames
|
|
||||||
// can (and usually do) span packets. A packet's header includes the offset
|
|
||||||
// in bits of the first frame that begins within that packet. All of the
|
|
||||||
// frames that begin in a given packet belong to the same "stream" (see the
|
|
||||||
// Multichannel Audio section below).
|
|
||||||
//
|
|
||||||
// STREAM: A set of packets within an XMA file that all contain data for the
|
|
||||||
// same mono or stereo component of a PCM file with more than two channels.
|
|
||||||
// The packets comprising a given stream may be interleaved with each other
|
|
||||||
// more or less arbitrarily; see Multichannel Audio.
|
|
||||||
//
|
|
||||||
// BLOCK: An array of XMA packets; or, to break it down differently, a series of
|
|
||||||
// consecutive XMA frames, padded at the end with reserved data. A block
|
|
||||||
// must contain at least one 2Kb packet per stream, and it can hold up to
|
|
||||||
// 4095 packets (8190Kb), but its size is typically in the 32Kb-128Kb range.
|
|
||||||
// (The size chosen involves a trade-off between memory use and efficiency
|
|
||||||
// of reading from permanent storage.)
|
|
||||||
//
|
|
||||||
// XMA frames do not span blocks, so a block is guaranteed to begin with a
|
|
||||||
// set of complete frames, one per stream. Also, a block in a multi-stream
|
|
||||||
// XMA2 file always contains the same number of samples for each stream;
|
|
||||||
// see Multichannel Audio.
|
|
||||||
//
|
|
||||||
// The 'data' chunk in an XMA2 file is an array of XMA2WAVEFORMAT.BlockCount XMA
|
|
||||||
// blocks, all the same size (as specified in XMA2WAVEFORMAT.BlockSizeInBytes)
|
|
||||||
// except for the last one, which may be shorter.
|
|
||||||
|
|
||||||
|
|
||||||
// MULTICHANNEL AUDIO: the XMA decoder can only decode raw XMA data into either
|
|
||||||
// mono or stereo PCM data. In order to encode a 6-channel file (say), the file
|
|
||||||
// must be deinterleaved into 3 stereo streams that are encoded independently,
|
|
||||||
// producing 3 encoded XMA data streams. Then the packets in these 3 streams
|
|
||||||
// are interleaved to produce a single XMA2 file, and some information is added
|
|
||||||
// to the file so that the original 6-channel audio can be reconstructed at
|
|
||||||
// decode time. This works using the concept of an XMA stream (see above).
|
|
||||||
//
|
|
||||||
// The frames for all the streams in an XMA file are interleaved in an arbitrary
|
|
||||||
// order. To locate a frame that belongs to a given stream in a given XMA block,
|
|
||||||
// you must examine the first few packets in the block. Here (and only here) the
|
|
||||||
// packets are guaranteed to be presented in stream order, so that all frames
|
|
||||||
// beginning in packet 0 belong to stream 0 (the first stereo pair), etc.
|
|
||||||
//
|
|
||||||
// (This means that when decoding multi-stream XMA files, only entire XMA blocks
|
|
||||||
// should be submitted to the decoder; otherwise it cannot know which frames
|
|
||||||
// belong to which stream.)
|
|
||||||
//
|
|
||||||
// Once you have one frame that belongs to a given stream, you can find the next
|
|
||||||
// one by looking at the frame's 'NextFrameOffsetBits' value (which is stored in
|
|
||||||
// its first 15 bits; see XMAFRAME below). The GetXmaFrameBitPosition function
|
|
||||||
// uses this technique.
|
|
||||||
|
|
||||||
|
|
||||||
// SEEKING IN XMA2 FILES: Here is some pseudocode to find the byte position and
|
|
||||||
// subframe in an XMA2 file which will contain sample S when decoded.
|
|
||||||
//
|
|
||||||
// 1. Traverse the seek table to find the XMA2 block containing sample S. The
|
|
||||||
// seek table is an array of big-endian DWORDs, one per block in the file.
|
|
||||||
// The Nth DWORD is the total number of PCM samples that would be obtained
|
|
||||||
// by decoding the entire XMA file up to the end of block N. Hence, the
|
|
||||||
// block we want is the first one whose seek table entry is greater than S.
|
|
||||||
// (See the GetXmaBlockContainingSample helper function.)
|
|
||||||
//
|
|
||||||
// 2. Calculate which frame F within the block found above contains sample S.
|
|
||||||
// Since each frame decodes to 512 samples, this is straightforward. The
|
|
||||||
// first frame in the block produces samples X to X + 512, where X is the
|
|
||||||
// seek table entry for the prior block. So F is (S - X) / 512.
|
|
||||||
//
|
|
||||||
// 3. Find the bit offset within the block where frame F starts. Since frames
|
|
||||||
// are variable-sized, this can only be done by traversing all the frames in
|
|
||||||
// the block until we reach frame F. (See GetXmaFrameBitPosition.)
|
|
||||||
//
|
|
||||||
// 4. Frame F has four 128-sample subframes. To find the subframe containing S,
|
|
||||||
// we can use the formula (S % 512) / 128.
|
|
||||||
//
|
|
||||||
// In the case of multi-stream XMA files, sample S is a multichannel sample with
|
|
||||||
// parts coming from several frames, one per stream. To find all these frames,
|
|
||||||
// steps 2-4 need to be repeated for each stream N, using the knowledge that the
|
|
||||||
// first packets in a block are presented in stream order. The frame traversal
|
|
||||||
// in step 3 must be started at the first frame in the Nth packet of the block,
|
|
||||||
// which will be the first frame for stream N. (And the packet header will tell
|
|
||||||
// you the first frame's start position within the packet.)
|
|
||||||
//
|
|
||||||
// Step 1 can be performed using the GetXmaBlockContainingSample function below,
|
|
||||||
// and steps 2-4 by calling GetXmaDecodePositionForSample once for each stream.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/***************************************************************************
|
|
||||||
* XMA constants
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
// Size of the PCM samples produced by the XMA decoder
|
|
||||||
#define XMA_OUTPUT_SAMPLE_BYTES 2u
|
|
||||||
#define XMA_OUTPUT_SAMPLE_BITS (XMA_OUTPUT_SAMPLE_BYTES * 8u)
|
|
||||||
|
|
||||||
// Size of an XMA packet
|
|
||||||
#define XMA_BYTES_PER_PACKET 2048u
|
|
||||||
#define XMA_BITS_PER_PACKET (XMA_BYTES_PER_PACKET * 8u)
|
|
||||||
|
|
||||||
// Size of an XMA packet header
|
|
||||||
#define XMA_PACKET_HEADER_BYTES 4u
|
|
||||||
#define XMA_PACKET_HEADER_BITS (XMA_PACKET_HEADER_BYTES * 8u)
|
|
||||||
|
|
||||||
// Sample blocks in a decoded XMA frame
|
|
||||||
#define XMA_SAMPLES_PER_FRAME 512u
|
|
||||||
|
|
||||||
// Sample blocks in a decoded XMA subframe
|
|
||||||
#define XMA_SAMPLES_PER_SUBFRAME 128u
|
|
||||||
|
|
||||||
// Maximum encoded data that can be submitted to the XMA decoder at a time
|
|
||||||
#define XMA_READBUFFER_MAX_PACKETS 4095u
|
|
||||||
#define XMA_READBUFFER_MAX_BYTES (XMA_READBUFFER_MAX_PACKETS * XMA_BYTES_PER_PACKET)
|
|
||||||
|
|
||||||
// Maximum size allowed for the XMA decoder's output buffers
|
|
||||||
#define XMA_WRITEBUFFER_MAX_BYTES (31u * 256u)
|
|
||||||
|
|
||||||
// Required byte alignment of the XMA decoder's output buffers
|
|
||||||
#define XMA_WRITEBUFFER_BYTE_ALIGNMENT 256u
|
|
||||||
|
|
||||||
// Decode chunk sizes for the XMA_PLAYBACK_INIT.subframesToDecode field
|
|
||||||
#define XMA_MIN_SUBFRAMES_TO_DECODE 1u
|
|
||||||
#define XMA_MAX_SUBFRAMES_TO_DECODE 8u
|
|
||||||
#define XMA_OPTIMAL_SUBFRAMES_TO_DECODE 4u
|
|
||||||
|
|
||||||
// LoopCount<255 means finite repetitions; LoopCount=255 means infinite looping
|
|
||||||
#define XMA_MAX_LOOPCOUNT 254u
|
|
||||||
#define XMA_INFINITE_LOOP 255u
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/***************************************************************************
|
|
||||||
* XMA format structures
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
// The currently recommended way to express format information for XMA2 files
|
|
||||||
// is the XMA2WAVEFORMATEX structure. This structure is fully compliant with
|
|
||||||
// the WAVEFORMATEX standard and contains all the information needed to parse
|
|
||||||
// and manage XMA2 files in a compact way.
|
|
||||||
|
|
||||||
#define WAVE_FORMAT_XMA2 0x166
|
|
||||||
|
|
||||||
typedef struct XMA2WAVEFORMATEX
|
|
||||||
{
|
|
||||||
WAVEFORMATEX wfx;
|
|
||||||
// Meaning of the WAVEFORMATEX fields here:
|
|
||||||
// wFormatTag; // Audio format type; always WAVE_FORMAT_XMA2
|
|
||||||
// nChannels; // Channel count of the decoded audio
|
|
||||||
// nSamplesPerSec; // Sample rate of the decoded audio
|
|
||||||
// nAvgBytesPerSec; // Used internally by the XMA encoder
|
|
||||||
// nBlockAlign; // Decoded sample size; channels * wBitsPerSample / 8
|
|
||||||
// wBitsPerSample; // Bits per decoded mono sample; always 16 for XMA
|
|
||||||
// cbSize; // Size in bytes of the rest of this structure (34)
|
|
||||||
|
|
||||||
WORD NumStreams; // Number of audio streams (1 or 2 channels each)
|
|
||||||
DWORD ChannelMask; // Spatial positions of the channels in this file,
|
|
||||||
// stored as SPEAKER_xxx values (see audiodefs.h)
|
|
||||||
DWORD SamplesEncoded; // Total number of PCM samples the file decodes to
|
|
||||||
DWORD BytesPerBlock; // XMA block size (but the last one may be shorter)
|
|
||||||
DWORD PlayBegin; // First valid sample in the decoded audio
|
|
||||||
DWORD PlayLength; // Length of the valid part of the decoded audio
|
|
||||||
DWORD LoopBegin; // Beginning of the loop region in decoded sample terms
|
|
||||||
DWORD LoopLength; // Length of the loop region in decoded sample terms
|
|
||||||
BYTE LoopCount; // Number of loop repetitions; 255 = infinite
|
|
||||||
BYTE EncoderVersion; // Version of XMA encoder that generated the file
|
|
||||||
WORD BlockCount; // XMA blocks in file (and entries in its seek table)
|
|
||||||
} XMA2WAVEFORMATEX, *PXMA2WAVEFORMATEX;
|
|
||||||
|
|
||||||
|
|
||||||
// The legacy XMA format structures are described here for reference, but they
|
|
||||||
// should not be used in new content. XMAWAVEFORMAT was the structure used in
|
|
||||||
// XMA version 1 files. XMA2WAVEFORMAT was used in early XMA2 files; it is not
|
|
||||||
// placed in the usual 'fmt' RIFF chunk but in its own 'XMA2' chunk.
|
|
||||||
|
|
||||||
#ifndef WAVE_FORMAT_XMA
|
|
||||||
#define WAVE_FORMAT_XMA 0x0165
|
|
||||||
|
|
||||||
// Values used in the ChannelMask fields below. Similar to the SPEAKER_xxx
|
|
||||||
// values defined in audiodefs.h, but modified to fit in a single byte.
|
|
||||||
#ifndef XMA_SPEAKER_LEFT
|
|
||||||
#define XMA_SPEAKER_LEFT 0x01
|
|
||||||
#define XMA_SPEAKER_RIGHT 0x02
|
|
||||||
#define XMA_SPEAKER_CENTER 0x04
|
|
||||||
#define XMA_SPEAKER_LFE 0x08
|
|
||||||
#define XMA_SPEAKER_LEFT_SURROUND 0x10
|
|
||||||
#define XMA_SPEAKER_RIGHT_SURROUND 0x20
|
|
||||||
#define XMA_SPEAKER_LEFT_BACK 0x40
|
|
||||||
#define XMA_SPEAKER_RIGHT_BACK 0x80
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
// Used in XMAWAVEFORMAT for per-stream data
|
|
||||||
typedef struct XMASTREAMFORMAT
|
|
||||||
{
|
|
||||||
DWORD PsuedoBytesPerSec; // Used by the XMA encoder (typo preserved for legacy reasons)
|
|
||||||
DWORD SampleRate; // The stream's decoded sample rate (in XMA2 files,
|
|
||||||
// this is the same for all streams in the file).
|
|
||||||
DWORD LoopStart; // Bit offset of the frame containing the loop start
|
|
||||||
// point, relative to the beginning of the stream.
|
|
||||||
DWORD LoopEnd; // Bit offset of the frame containing the loop end.
|
|
||||||
BYTE SubframeData; // Two 4-bit numbers specifying the exact location of
|
|
||||||
// the loop points within the frames that contain them.
|
|
||||||
// SubframeEnd: Subframe of the loop end frame where
|
|
||||||
// the loop ends. Ranges from 0 to 3.
|
|
||||||
// SubframeSkip: Subframes to skip in the start frame to
|
|
||||||
// reach the loop. Ranges from 0 to 4.
|
|
||||||
BYTE Channels; // Number of channels in the stream (1 or 2)
|
|
||||||
WORD ChannelMask; // Spatial positions of the channels in the stream
|
|
||||||
} XMASTREAMFORMAT;
|
|
||||||
|
|
||||||
// Legacy XMA1 format structure
|
|
||||||
typedef struct XMAWAVEFORMAT
|
|
||||||
{
|
|
||||||
WORD FormatTag; // Audio format type (always WAVE_FORMAT_XMA)
|
|
||||||
WORD BitsPerSample; // Bit depth (currently required to be 16)
|
|
||||||
WORD EncodeOptions; // Options for XMA encoder/decoder
|
|
||||||
WORD LargestSkip; // Largest skip used in interleaving streams
|
|
||||||
WORD NumStreams; // Number of interleaved audio streams
|
|
||||||
BYTE LoopCount; // Number of loop repetitions; 255 = infinite
|
|
||||||
BYTE Version; // XMA encoder version that generated the file.
|
|
||||||
// Always 3 or higher for XMA2 files.
|
|
||||||
XMASTREAMFORMAT XmaStreams[1]; // Per-stream format information; the actual
|
|
||||||
// array length is in the NumStreams field.
|
|
||||||
} XMAWAVEFORMAT;
|
|
||||||
|
|
||||||
|
|
||||||
// Used in XMA2WAVEFORMAT for per-stream data
|
|
||||||
typedef struct XMA2STREAMFORMAT
|
|
||||||
{
|
|
||||||
BYTE Channels; // Number of channels in the stream (1 or 2)
|
|
||||||
BYTE RESERVED; // Reserved for future use
|
|
||||||
WORD ChannelMask; // Spatial positions of the channels in the stream
|
|
||||||
} XMA2STREAMFORMAT;
|
|
||||||
|
|
||||||
// Legacy XMA2 format structure (big-endian byte ordering)
|
|
||||||
typedef struct XMA2WAVEFORMAT
|
|
||||||
{
|
|
||||||
BYTE Version; // XMA encoder version that generated the file.
|
|
||||||
// Always 3 or higher for XMA2 files.
|
|
||||||
BYTE NumStreams; // Number of interleaved audio streams
|
|
||||||
BYTE RESERVED; // Reserved for future use
|
|
||||||
BYTE LoopCount; // Number of loop repetitions; 255 = infinite
|
|
||||||
DWORD LoopBegin; // Loop begin point, in samples
|
|
||||||
DWORD LoopEnd; // Loop end point, in samples
|
|
||||||
DWORD SampleRate; // The file's decoded sample rate
|
|
||||||
DWORD EncodeOptions; // Options for the XMA encoder/decoder
|
|
||||||
DWORD PsuedoBytesPerSec; // Used internally by the XMA encoder
|
|
||||||
DWORD BlockSizeInBytes; // Size in bytes of this file's XMA blocks (except
|
|
||||||
// possibly the last one). Always a multiple of
|
|
||||||
// 2Kb, since XMA blocks are arrays of 2Kb packets.
|
|
||||||
DWORD SamplesEncoded; // Total number of PCM samples encoded in this file
|
|
||||||
DWORD SamplesInSource; // Actual number of PCM samples in the source
|
|
||||||
// material used to generate this file
|
|
||||||
DWORD BlockCount; // Number of XMA blocks in this file (and hence
|
|
||||||
// also the number of entries in its seek table)
|
|
||||||
XMA2STREAMFORMAT Streams[1]; // Per-stream format information; the actual
|
|
||||||
// array length is in the NumStreams field.
|
|
||||||
} XMA2WAVEFORMAT;
|
|
||||||
|
|
||||||
#endif // #ifndef WAVE_FORMAT_XMA
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/***************************************************************************
|
|
||||||
* XMA packet structure (in big-endian form)
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
typedef struct XMA2PACKET
|
|
||||||
{
|
|
||||||
int FrameCount : 6; // Number of XMA frames that begin in this packet
|
|
||||||
int FrameOffsetInBits : 15; // Bit of XmaData where the first complete frame begins
|
|
||||||
int PacketMetaData : 3; // Metadata stored in the packet (always 1 for XMA2)
|
|
||||||
int PacketSkipCount : 8; // How many packets belonging to other streams must be
|
|
||||||
// skipped to find the next packet belonging to this one
|
|
||||||
BYTE XmaData[XMA_BYTES_PER_PACKET - sizeof(DWORD)]; // XMA encoded data
|
|
||||||
} XMA2PACKET;
|
|
||||||
|
|
||||||
// E.g. if the first DWORD of a packet is 0x30107902:
|
|
||||||
//
|
|
||||||
// 001100 000001000001111 001 00000010
|
|
||||||
// | | | |____ Skip 2 packets to find the next one for this stream
|
|
||||||
// | | |___________ XMA2 signature (always 001)
|
|
||||||
// | |_____________________ First frame starts 527 bits into packet
|
|
||||||
// |________________________________ Packet contains 12 frames
|
|
||||||
|
|
||||||
|
|
||||||
// Helper functions to extract the fields above from an XMA packet. (Note that
|
|
||||||
// the bitfields cannot be read directly on little-endian architectures such as
|
|
||||||
// the Intel x86, as they are laid out in big-endian form.)
|
|
||||||
|
|
||||||
__inline DWORD GetXmaPacketFrameCount(__in_bcount(1) const BYTE* pPacket)
|
|
||||||
{
|
|
||||||
return (DWORD)(pPacket[0] >> 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
__inline DWORD GetXmaPacketFirstFrameOffsetInBits(__in_bcount(3) const BYTE* pPacket)
|
|
||||||
{
|
|
||||||
return ((DWORD)(pPacket[0] & 0x3) << 13) |
|
|
||||||
((DWORD)(pPacket[1]) << 5) |
|
|
||||||
((DWORD)(pPacket[2]) >> 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
__inline DWORD GetXmaPacketMetadata(__in_bcount(3) const BYTE* pPacket)
|
|
||||||
{
|
|
||||||
return (DWORD)(pPacket[2] & 0x7);
|
|
||||||
}
|
|
||||||
|
|
||||||
__inline DWORD GetXmaPacketSkipCount(__in_bcount(4) const BYTE* pPacket)
|
|
||||||
{
|
|
||||||
return (DWORD)(pPacket[3]);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/***************************************************************************
|
|
||||||
* XMA frame structure
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
// There is no way to represent the XMA frame as a C struct, since it is a
|
|
||||||
// variable-sized string of bits that need not be stored at a byte-aligned
|
|
||||||
// position in memory. This is the layout:
|
|
||||||
//
|
|
||||||
// XMAFRAME
|
|
||||||
// {
|
|
||||||
// LengthInBits: A 15-bit number representing the length of this frame.
|
|
||||||
// XmaData: Encoded XMA data; its size in bits is (LengthInBits - 15).
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Size in bits of the frame's initial LengthInBits field
|
|
||||||
#define XMA_BITS_IN_FRAME_LENGTH_FIELD 15
|
|
||||||
|
|
||||||
// Special LengthInBits value that marks an invalid final frame
|
|
||||||
#define XMA_FINAL_FRAME_MARKER 0x7FFF
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/***************************************************************************
|
|
||||||
* XMA helper functions
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
// We define a local ASSERT macro to equal the global one if it exists.
|
|
||||||
// You can define XMA2DEFS_ASSERT in advance to override this default.
|
|
||||||
#ifndef XMA2DEFS_ASSERT
|
|
||||||
#ifdef ASSERT
|
|
||||||
#define XMA2DEFS_ASSERT ASSERT
|
|
||||||
#else
|
|
||||||
#define XMA2DEFS_ASSERT(a) /* No-op by default */
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
// GetXmaBlockContainingSample: Use a given seek table to find the XMA block
|
|
||||||
// containing a given decoded sample. Note that the seek table entries in an
|
|
||||||
// XMA file are stored in big-endian form and may need to be converted prior
|
|
||||||
// to calling this function.
|
|
||||||
|
|
||||||
__inline HRESULT GetXmaBlockContainingSample
|
|
||||||
(
|
|
||||||
DWORD nBlockCount, // Blocks in the file (= seek table entries)
|
|
||||||
__in_ecount(nBlockCount) const DWORD* pSeekTable, // Pointer to the seek table data
|
|
||||||
DWORD nDesiredSample, // Decoded sample to locate
|
|
||||||
__out DWORD* pnBlockContainingSample, // Index of the block containing the sample
|
|
||||||
__out DWORD* pnSampleOffsetWithinBlock // Position of the sample in this block
|
|
||||||
)
|
|
||||||
{
|
|
||||||
DWORD nPreviousTotalSamples = 0;
|
|
||||||
DWORD nBlock;
|
|
||||||
DWORD nTotalSamplesSoFar;
|
|
||||||
|
|
||||||
XMA2DEFS_ASSERT(pSeekTable);
|
|
||||||
XMA2DEFS_ASSERT(pnBlockContainingSample);
|
|
||||||
XMA2DEFS_ASSERT(pnSampleOffsetWithinBlock);
|
|
||||||
|
|
||||||
for (nBlock = 0; nBlock < nBlockCount; ++nBlock)
|
|
||||||
{
|
|
||||||
nTotalSamplesSoFar = pSeekTable[nBlock];
|
|
||||||
if (nTotalSamplesSoFar > nDesiredSample)
|
|
||||||
{
|
|
||||||
*pnBlockContainingSample = nBlock;
|
|
||||||
*pnSampleOffsetWithinBlock = nDesiredSample - nPreviousTotalSamples;
|
|
||||||
return S_OK;
|
|
||||||
}
|
|
||||||
nPreviousTotalSamples = nTotalSamplesSoFar;
|
|
||||||
}
|
|
||||||
|
|
||||||
return E_FAIL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// GetXmaFrameLengthInBits: Reads a given frame's LengthInBits field.
|
|
||||||
|
|
||||||
__inline DWORD GetXmaFrameLengthInBits
|
|
||||||
(
|
|
||||||
__in_bcount(nBitPosition / 8 + 3)
|
|
||||||
__in const BYTE* pPacket, // Pointer to XMA packet[s] containing the frame
|
|
||||||
DWORD nBitPosition // Bit offset of the frame within this packet
|
|
||||||
)
|
|
||||||
{
|
|
||||||
DWORD nRegion;
|
|
||||||
DWORD nBytePosition = nBitPosition / 8;
|
|
||||||
DWORD nBitOffset = nBitPosition % 8;
|
|
||||||
|
|
||||||
if (nBitOffset < 2) // Only need to read 2 bytes (and might not be safe to read more)
|
|
||||||
{
|
|
||||||
nRegion = (DWORD)(pPacket[nBytePosition+0]) << 8 |
|
|
||||||
(DWORD)(pPacket[nBytePosition+1]);
|
|
||||||
return (nRegion >> (1 - nBitOffset)) & 0x7FFF; // Last 15 bits
|
|
||||||
}
|
|
||||||
else // Need to read 3 bytes
|
|
||||||
{
|
|
||||||
nRegion = (DWORD)(pPacket[nBytePosition+0]) << 16 |
|
|
||||||
(DWORD)(pPacket[nBytePosition+1]) << 8 |
|
|
||||||
(DWORD)(pPacket[nBytePosition+2]);
|
|
||||||
return (nRegion >> (9 - nBitOffset)) & 0x7FFF; // Last 15 bits
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// GetXmaFrameBitPosition: Calculates the bit offset of a given frame within
|
|
||||||
// an XMA block or set of blocks. Returns 0 on failure.
|
|
||||||
|
|
||||||
__inline DWORD GetXmaFrameBitPosition
|
|
||||||
(
|
|
||||||
__in_bcount(nXmaDataBytes) const BYTE* pXmaData, // Pointer to XMA block[s]
|
|
||||||
DWORD nXmaDataBytes, // Size of pXmaData in bytes
|
|
||||||
DWORD nStreamIndex, // Stream within which to seek
|
|
||||||
DWORD nDesiredFrame // Frame sought
|
|
||||||
)
|
|
||||||
{
|
|
||||||
const BYTE* pCurrentPacket;
|
|
||||||
DWORD nPacketsExamined = 0;
|
|
||||||
DWORD nFrameCountSoFar = 0;
|
|
||||||
DWORD nFramesToSkip;
|
|
||||||
DWORD nFrameBitOffset;
|
|
||||||
|
|
||||||
XMA2DEFS_ASSERT(pXmaData);
|
|
||||||
XMA2DEFS_ASSERT(nXmaDataBytes % XMA_BYTES_PER_PACKET == 0);
|
|
||||||
|
|
||||||
// Get the first XMA packet belonging to the desired stream, relying on the
|
|
||||||
// fact that the first packets for each stream are in consecutive order at
|
|
||||||
// the beginning of an XMA block.
|
|
||||||
|
|
||||||
pCurrentPacket = pXmaData + nStreamIndex * XMA_BYTES_PER_PACKET;
|
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
// If we have exceeded the size of the XMA data, return failure
|
|
||||||
if (pCurrentPacket + XMA_BYTES_PER_PACKET > pXmaData + nXmaDataBytes)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the current packet contains the frame we are looking for...
|
|
||||||
if (nFrameCountSoFar + GetXmaPacketFrameCount(pCurrentPacket) > nDesiredFrame)
|
|
||||||
{
|
|
||||||
// See how many frames in this packet we need to skip to get to it
|
|
||||||
XMA2DEFS_ASSERT(nDesiredFrame >= nFrameCountSoFar);
|
|
||||||
nFramesToSkip = nDesiredFrame - nFrameCountSoFar;
|
|
||||||
|
|
||||||
// Get the bit offset of the first frame in this packet
|
|
||||||
nFrameBitOffset = XMA_PACKET_HEADER_BITS + GetXmaPacketFirstFrameOffsetInBits(pCurrentPacket);
|
|
||||||
|
|
||||||
// Advance nFrameBitOffset to the frame of interest
|
|
||||||
while (nFramesToSkip--)
|
|
||||||
{
|
|
||||||
nFrameBitOffset += GetXmaFrameLengthInBits(pCurrentPacket, nFrameBitOffset);
|
|
||||||
}
|
|
||||||
|
|
||||||
// The bit offset to return is the number of bits from pXmaData to
|
|
||||||
// pCurrentPacket plus the bit offset of the frame of interest
|
|
||||||
return (DWORD)(pCurrentPacket - pXmaData) * 8 + nFrameBitOffset;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we haven't found the right packet yet, advance our counters
|
|
||||||
++nPacketsExamined;
|
|
||||||
nFrameCountSoFar += GetXmaPacketFrameCount(pCurrentPacket);
|
|
||||||
|
|
||||||
// And skip to the next packet belonging to the same stream
|
|
||||||
pCurrentPacket += XMA_BYTES_PER_PACKET * (GetXmaPacketSkipCount(pCurrentPacket) + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// GetLastXmaFrameBitPosition: Calculates the bit offset of the last complete
|
|
||||||
// frame in an XMA block or set of blocks.
|
|
||||||
|
|
||||||
__inline DWORD GetLastXmaFrameBitPosition
|
|
||||||
(
|
|
||||||
__in_bcount(nXmaDataBytes) const BYTE* pXmaData, // Pointer to XMA block[s]
|
|
||||||
DWORD nXmaDataBytes, // Size of pXmaData in bytes
|
|
||||||
DWORD nStreamIndex // Stream within which to seek
|
|
||||||
)
|
|
||||||
{
|
|
||||||
const BYTE* pLastPacket;
|
|
||||||
DWORD nBytesToNextPacket;
|
|
||||||
DWORD nFrameBitOffset;
|
|
||||||
DWORD nFramesInLastPacket;
|
|
||||||
|
|
||||||
XMA2DEFS_ASSERT(pXmaData);
|
|
||||||
XMA2DEFS_ASSERT(nXmaDataBytes % XMA_BYTES_PER_PACKET == 0);
|
|
||||||
XMA2DEFS_ASSERT(nXmaDataBytes >= XMA_BYTES_PER_PACKET * (nStreamIndex + 1));
|
|
||||||
|
|
||||||
// Get the first XMA packet belonging to the desired stream, relying on the
|
|
||||||
// fact that the first packets for each stream are in consecutive order at
|
|
||||||
// the beginning of an XMA block.
|
|
||||||
pLastPacket = pXmaData + nStreamIndex * XMA_BYTES_PER_PACKET;
|
|
||||||
|
|
||||||
// Search for the last packet belonging to the desired stream
|
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
nBytesToNextPacket = XMA_BYTES_PER_PACKET * (GetXmaPacketSkipCount(pLastPacket) + 1);
|
|
||||||
XMA2DEFS_ASSERT(nBytesToNextPacket);
|
|
||||||
if (pLastPacket + nBytesToNextPacket + XMA_BYTES_PER_PACKET > pXmaData + nXmaDataBytes)
|
|
||||||
{
|
|
||||||
break; // The next packet would extend beyond the end of pXmaData
|
|
||||||
}
|
|
||||||
pLastPacket += nBytesToNextPacket;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The last packet can sometimes have no seekable frames, in which case we
|
|
||||||
// have to use the previous one
|
|
||||||
if (GetXmaPacketFrameCount(pLastPacket) == 0)
|
|
||||||
{
|
|
||||||
pLastPacket -= nBytesToNextPacket;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Found the last packet. Get the bit offset of its first frame.
|
|
||||||
nFrameBitOffset = XMA_PACKET_HEADER_BITS + GetXmaPacketFirstFrameOffsetInBits(pLastPacket);
|
|
||||||
|
|
||||||
// Traverse frames until we reach the last one
|
|
||||||
nFramesInLastPacket = GetXmaPacketFrameCount(pLastPacket);
|
|
||||||
while (--nFramesInLastPacket)
|
|
||||||
{
|
|
||||||
nFrameBitOffset += GetXmaFrameLengthInBits(pLastPacket, nFrameBitOffset);
|
|
||||||
}
|
|
||||||
|
|
||||||
// The bit offset to return is the number of bits from pXmaData to
|
|
||||||
// pLastPacket plus the offset of the last frame in this packet.
|
|
||||||
return (DWORD)(pLastPacket - pXmaData) * 8 + nFrameBitOffset;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// GetXmaDecodePositionForSample: Obtains the information needed to make the
|
|
||||||
// decoder generate audio starting at a given sample position relative to the
|
|
||||||
// beginning of the given XMA block: the bit offset of the appropriate frame,
|
|
||||||
// and the right subframe within that frame. This data can be passed directly
|
|
||||||
// to the XMAPlaybackSetDecodePosition function.
|
|
||||||
|
|
||||||
__inline HRESULT GetXmaDecodePositionForSample
|
|
||||||
(
|
|
||||||
__in_bcount(nXmaDataBytes) const BYTE* pXmaData, // Pointer to XMA block[s]
|
|
||||||
DWORD nXmaDataBytes, // Size of pXmaData in bytes
|
|
||||||
DWORD nStreamIndex, // Stream within which to seek
|
|
||||||
DWORD nDesiredSample, // Sample sought
|
|
||||||
__out DWORD* pnBitOffset, // Returns the bit offset within pXmaData of
|
|
||||||
// the frame containing the sample sought
|
|
||||||
__out DWORD* pnSubFrame // Returns the subframe containing the sample
|
|
||||||
)
|
|
||||||
{
|
|
||||||
DWORD nDesiredFrame = nDesiredSample / XMA_SAMPLES_PER_FRAME;
|
|
||||||
DWORD nSubFrame = (nDesiredSample % XMA_SAMPLES_PER_FRAME) / XMA_SAMPLES_PER_SUBFRAME;
|
|
||||||
DWORD nBitOffset = GetXmaFrameBitPosition(pXmaData, nXmaDataBytes, nStreamIndex, nDesiredFrame);
|
|
||||||
|
|
||||||
XMA2DEFS_ASSERT(pnBitOffset);
|
|
||||||
XMA2DEFS_ASSERT(pnSubFrame);
|
|
||||||
|
|
||||||
if (nBitOffset)
|
|
||||||
{
|
|
||||||
*pnBitOffset = nBitOffset;
|
|
||||||
*pnSubFrame = nSubFrame;
|
|
||||||
return S_OK;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return E_FAIL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// GetXmaSampleRate: Obtains the legal XMA sample rate (24, 32, 44.1 or 48Khz)
|
|
||||||
// corresponding to a generic sample rate.
|
|
||||||
|
|
||||||
__inline DWORD GetXmaSampleRate(DWORD dwGeneralRate)
|
|
||||||
{
|
|
||||||
DWORD dwXmaRate = 48000; // Default XMA rate for all rates above 44100Hz
|
|
||||||
|
|
||||||
if (dwGeneralRate <= 24000) dwXmaRate = 24000;
|
|
||||||
else if (dwGeneralRate <= 32000) dwXmaRate = 32000;
|
|
||||||
else if (dwGeneralRate <= 44100) dwXmaRate = 44100;
|
|
||||||
|
|
||||||
return dwXmaRate;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Functions to convert between WAVEFORMATEXTENSIBLE channel masks (combinations
|
|
||||||
// of the SPEAKER_xxx flags defined in audiodefs.h) and XMA channel masks (which
|
|
||||||
// are limited to eight possible speaker positions: left, right, center, low
|
|
||||||
// frequency, side left, side right, back left and back right).
|
|
||||||
|
|
||||||
__inline DWORD GetStandardChannelMaskFromXmaMask(BYTE bXmaMask)
|
|
||||||
{
|
|
||||||
DWORD dwStandardMask = 0;
|
|
||||||
|
|
||||||
if (bXmaMask & XMA_SPEAKER_LEFT) dwStandardMask |= SPEAKER_FRONT_LEFT;
|
|
||||||
if (bXmaMask & XMA_SPEAKER_RIGHT) dwStandardMask |= SPEAKER_FRONT_RIGHT;
|
|
||||||
if (bXmaMask & XMA_SPEAKER_CENTER) dwStandardMask |= SPEAKER_FRONT_CENTER;
|
|
||||||
if (bXmaMask & XMA_SPEAKER_LFE) dwStandardMask |= SPEAKER_LOW_FREQUENCY;
|
|
||||||
if (bXmaMask & XMA_SPEAKER_LEFT_SURROUND) dwStandardMask |= SPEAKER_SIDE_LEFT;
|
|
||||||
if (bXmaMask & XMA_SPEAKER_RIGHT_SURROUND) dwStandardMask |= SPEAKER_SIDE_RIGHT;
|
|
||||||
if (bXmaMask & XMA_SPEAKER_LEFT_BACK) dwStandardMask |= SPEAKER_BACK_LEFT;
|
|
||||||
if (bXmaMask & XMA_SPEAKER_RIGHT_BACK) dwStandardMask |= SPEAKER_BACK_RIGHT;
|
|
||||||
|
|
||||||
return dwStandardMask;
|
|
||||||
}
|
|
||||||
|
|
||||||
__inline BYTE GetXmaChannelMaskFromStandardMask(DWORD dwStandardMask)
|
|
||||||
{
|
|
||||||
BYTE bXmaMask = 0;
|
|
||||||
|
|
||||||
if (dwStandardMask & SPEAKER_FRONT_LEFT) bXmaMask |= XMA_SPEAKER_LEFT;
|
|
||||||
if (dwStandardMask & SPEAKER_FRONT_RIGHT) bXmaMask |= XMA_SPEAKER_RIGHT;
|
|
||||||
if (dwStandardMask & SPEAKER_FRONT_CENTER) bXmaMask |= XMA_SPEAKER_CENTER;
|
|
||||||
if (dwStandardMask & SPEAKER_LOW_FREQUENCY) bXmaMask |= XMA_SPEAKER_LFE;
|
|
||||||
if (dwStandardMask & SPEAKER_SIDE_LEFT) bXmaMask |= XMA_SPEAKER_LEFT_SURROUND;
|
|
||||||
if (dwStandardMask & SPEAKER_SIDE_RIGHT) bXmaMask |= XMA_SPEAKER_RIGHT_SURROUND;
|
|
||||||
if (dwStandardMask & SPEAKER_BACK_LEFT) bXmaMask |= XMA_SPEAKER_LEFT_BACK;
|
|
||||||
if (dwStandardMask & SPEAKER_BACK_RIGHT) bXmaMask |= XMA_SPEAKER_RIGHT_BACK;
|
|
||||||
|
|
||||||
return bXmaMask;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// LocalizeXma2Format: Modifies a XMA2WAVEFORMATEX structure in place to comply
|
|
||||||
// with the current platform's byte-ordering rules (little- or big-endian).
|
|
||||||
|
|
||||||
__inline HRESULT LocalizeXma2Format(__inout XMA2WAVEFORMATEX* pXma2Format)
|
|
||||||
{
|
|
||||||
#define XMASWAP2BYTES(n) ((WORD)(((n) >> 8) | (((n) & 0xff) << 8)))
|
|
||||||
#define XMASWAP4BYTES(n) ((DWORD)((n) >> 24 | (n) << 24 | ((n) & 0xff00) << 8 | ((n) & 0xff0000) >> 8))
|
|
||||||
|
|
||||||
if (pXma2Format->wfx.wFormatTag == WAVE_FORMAT_XMA2)
|
|
||||||
{
|
|
||||||
return S_OK;
|
|
||||||
}
|
|
||||||
else if (XMASWAP2BYTES(pXma2Format->wfx.wFormatTag) == WAVE_FORMAT_XMA2)
|
|
||||||
{
|
|
||||||
pXma2Format->wfx.wFormatTag = XMASWAP2BYTES(pXma2Format->wfx.wFormatTag);
|
|
||||||
pXma2Format->wfx.nChannels = XMASWAP2BYTES(pXma2Format->wfx.nChannels);
|
|
||||||
pXma2Format->wfx.nSamplesPerSec = XMASWAP4BYTES(pXma2Format->wfx.nSamplesPerSec);
|
|
||||||
pXma2Format->wfx.nAvgBytesPerSec = XMASWAP4BYTES(pXma2Format->wfx.nAvgBytesPerSec);
|
|
||||||
pXma2Format->wfx.nBlockAlign = XMASWAP2BYTES(pXma2Format->wfx.nBlockAlign);
|
|
||||||
pXma2Format->wfx.wBitsPerSample = XMASWAP2BYTES(pXma2Format->wfx.wBitsPerSample);
|
|
||||||
pXma2Format->wfx.cbSize = XMASWAP2BYTES(pXma2Format->wfx.cbSize);
|
|
||||||
pXma2Format->NumStreams = XMASWAP2BYTES(pXma2Format->NumStreams);
|
|
||||||
pXma2Format->ChannelMask = XMASWAP4BYTES(pXma2Format->ChannelMask);
|
|
||||||
pXma2Format->SamplesEncoded = XMASWAP4BYTES(pXma2Format->SamplesEncoded);
|
|
||||||
pXma2Format->BytesPerBlock = XMASWAP4BYTES(pXma2Format->BytesPerBlock);
|
|
||||||
pXma2Format->PlayBegin = XMASWAP4BYTES(pXma2Format->PlayBegin);
|
|
||||||
pXma2Format->PlayLength = XMASWAP4BYTES(pXma2Format->PlayLength);
|
|
||||||
pXma2Format->LoopBegin = XMASWAP4BYTES(pXma2Format->LoopBegin);
|
|
||||||
pXma2Format->LoopLength = XMASWAP4BYTES(pXma2Format->LoopLength);
|
|
||||||
pXma2Format->BlockCount = XMASWAP2BYTES(pXma2Format->BlockCount);
|
|
||||||
return S_OK;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return E_FAIL; // Not a recognizable XMA2 format
|
|
||||||
}
|
|
||||||
|
|
||||||
#undef XMASWAP2BYTES
|
|
||||||
#undef XMASWAP4BYTES
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#endif // #ifndef __XMA2DEFS_INCLUDED__
|
|
|
@ -60,8 +60,6 @@ Dolphin includes or links code of the following third-party software projects:
|
||||||
[LGPLv2+](http://www.surina.net/soundtouch/license.html)
|
[LGPLv2+](http://www.surina.net/soundtouch/license.html)
|
||||||
- [TAP-Windows](https://openvpn.net/):
|
- [TAP-Windows](https://openvpn.net/):
|
||||||
header only
|
header only
|
||||||
- [XAudio2](http://msdn.microsoft.com/en-us/library/windows/desktop/hh405049.aspx):
|
|
||||||
headers only
|
|
||||||
- [xxHash](https://github.com/Cyan4973/xxHash):
|
- [xxHash](https://github.com/Cyan4973/xxHash):
|
||||||
[2-clause BSD](https://github.com/Cyan4973/xxHash/blob/master/LICENSE)
|
[2-clause BSD](https://github.com/Cyan4973/xxHash/blob/master/LICENSE)
|
||||||
- [zlib](http://www.zlib.net/):
|
- [zlib](http://www.zlib.net/):
|
||||||
|
|
|
@ -11,8 +11,6 @@
|
||||||
#include "AudioCommon/OpenSLESStream.h"
|
#include "AudioCommon/OpenSLESStream.h"
|
||||||
#include "AudioCommon/PulseAudioStream.h"
|
#include "AudioCommon/PulseAudioStream.h"
|
||||||
#include "AudioCommon/WASAPIStream.h"
|
#include "AudioCommon/WASAPIStream.h"
|
||||||
#include "AudioCommon/XAudio2Stream.h"
|
|
||||||
#include "AudioCommon/XAudio2_7Stream.h"
|
|
||||||
#include "Common/Common.h"
|
#include "Common/Common.h"
|
||||||
#include "Common/FileUtil.h"
|
#include "Common/FileUtil.h"
|
||||||
#include "Common/Logging/Log.h"
|
#include "Common/Logging/Log.h"
|
||||||
|
@ -38,13 +36,6 @@ void InitSoundStream()
|
||||||
g_sound_stream = std::make_unique<OpenALStream>();
|
g_sound_stream = std::make_unique<OpenALStream>();
|
||||||
else if (backend == BACKEND_NULLSOUND)
|
else if (backend == BACKEND_NULLSOUND)
|
||||||
g_sound_stream = std::make_unique<NullSound>();
|
g_sound_stream = std::make_unique<NullSound>();
|
||||||
else if (backend == BACKEND_XAUDIO2)
|
|
||||||
{
|
|
||||||
if (XAudio2::isValid())
|
|
||||||
g_sound_stream = std::make_unique<XAudio2>();
|
|
||||||
else if (XAudio2_7::isValid())
|
|
||||||
g_sound_stream = std::make_unique<XAudio2_7>();
|
|
||||||
}
|
|
||||||
else if (backend == BACKEND_ALSA && AlsaSound::isValid())
|
else if (backend == BACKEND_ALSA && AlsaSound::isValid())
|
||||||
g_sound_stream = std::make_unique<AlsaSound>();
|
g_sound_stream = std::make_unique<AlsaSound>();
|
||||||
else if (backend == BACKEND_PULSEAUDIO && PulseAudio::isValid())
|
else if (backend == BACKEND_PULSEAUDIO && PulseAudio::isValid())
|
||||||
|
@ -101,8 +92,6 @@ std::vector<std::string> GetSoundBackends()
|
||||||
|
|
||||||
backends.emplace_back(BACKEND_NULLSOUND);
|
backends.emplace_back(BACKEND_NULLSOUND);
|
||||||
backends.emplace_back(BACKEND_CUBEB);
|
backends.emplace_back(BACKEND_CUBEB);
|
||||||
if (XAudio2_7::isValid() || XAudio2::isValid())
|
|
||||||
backends.emplace_back(BACKEND_XAUDIO2);
|
|
||||||
if (AlsaSound::isValid())
|
if (AlsaSound::isValid())
|
||||||
backends.emplace_back(BACKEND_ALSA);
|
backends.emplace_back(BACKEND_ALSA);
|
||||||
if (PulseAudio::isValid())
|
if (PulseAudio::isValid())
|
||||||
|
@ -127,8 +116,6 @@ bool SupportsDPL2Decoder(std::string_view backend)
|
||||||
return true;
|
return true;
|
||||||
if (backend == BACKEND_PULSEAUDIO)
|
if (backend == BACKEND_PULSEAUDIO)
|
||||||
return true;
|
return true;
|
||||||
if (backend == BACKEND_XAUDIO2)
|
|
||||||
return true;
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,8 +129,7 @@ bool SupportsVolumeChanges(std::string_view backend)
|
||||||
// FIXME: this one should ask the backend whether it supports it.
|
// FIXME: this one should ask the backend whether it supports it.
|
||||||
// but getting the backend from string etc. is probably
|
// but getting the backend from string etc. is probably
|
||||||
// too much just to enable/disable a stupid slider...
|
// too much just to enable/disable a stupid slider...
|
||||||
return backend == BACKEND_CUBEB || backend == BACKEND_OPENAL || backend == BACKEND_XAUDIO2 ||
|
return backend == BACKEND_CUBEB || backend == BACKEND_OPENAL || backend == BACKEND_WASAPI;
|
||||||
backend == BACKEND_WASAPI;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void UpdateSoundStream()
|
void UpdateSoundStream()
|
||||||
|
|
|
@ -46,10 +46,6 @@
|
||||||
<ClCompile Include="WASAPIStream.cpp" />
|
<ClCompile Include="WASAPIStream.cpp" />
|
||||||
<ClCompile Include="SurroundDecoder.cpp" />
|
<ClCompile Include="SurroundDecoder.cpp" />
|
||||||
<ClCompile Include="WaveFile.cpp" />
|
<ClCompile Include="WaveFile.cpp" />
|
||||||
<ClCompile Include="XAudio2Stream.cpp" />
|
|
||||||
<ClCompile Include="XAudio2_7Stream.cpp">
|
|
||||||
<AdditionalIncludeDirectories>$(ExternalsDir)XAudio2_7;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
|
||||||
</ClCompile>
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="AlsaSoundStream.h" />
|
<ClInclude Include="AlsaSoundStream.h" />
|
||||||
|
@ -66,8 +62,6 @@
|
||||||
<ClInclude Include="WASAPIStream.h" />
|
<ClInclude Include="WASAPIStream.h" />
|
||||||
<ClInclude Include="SurroundDecoder.h" />
|
<ClInclude Include="SurroundDecoder.h" />
|
||||||
<ClInclude Include="WaveFile.h" />
|
<ClInclude Include="WaveFile.h" />
|
||||||
<ClInclude Include="XAudio2Stream.h" />
|
|
||||||
<ClInclude Include="XAudio2_7Stream.h" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Text Include="CMakeLists.txt" />
|
<Text Include="CMakeLists.txt" />
|
||||||
|
|
|
@ -17,12 +17,6 @@
|
||||||
<ClCompile Include="OpenALStream.cpp">
|
<ClCompile Include="OpenALStream.cpp">
|
||||||
<Filter>SoundStreams</Filter>
|
<Filter>SoundStreams</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="XAudio2Stream.cpp">
|
|
||||||
<Filter>SoundStreams</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="XAudio2_7Stream.cpp">
|
|
||||||
<Filter>SoundStreams</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="CubebStream.cpp">
|
<ClCompile Include="CubebStream.cpp">
|
||||||
<Filter>SoundStreams</Filter>
|
<Filter>SoundStreams</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
@ -43,12 +37,6 @@
|
||||||
<ClInclude Include="OpenALStream.h">
|
<ClInclude Include="OpenALStream.h">
|
||||||
<Filter>SoundStreams</Filter>
|
<Filter>SoundStreams</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="XAudio2Stream.h">
|
|
||||||
<Filter>SoundStreams</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="XAudio2_7Stream.h">
|
|
||||||
<Filter>SoundStreams</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="PulseAudioStream.h">
|
<ClInclude Include="PulseAudioStream.h">
|
||||||
<Filter>SoundStreams</Filter>
|
<Filter>SoundStreams</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
|
|
@ -71,19 +71,7 @@ if(WIN32)
|
||||||
|
|
||||||
WASAPIStream.cpp
|
WASAPIStream.cpp
|
||||||
WASAPIStream.h
|
WASAPIStream.h
|
||||||
XAudio2Stream.cpp
|
|
||||||
XAudio2Stream.h
|
|
||||||
)
|
)
|
||||||
|
|
||||||
add_library(audiocommon_xaudio27
|
|
||||||
XAudio2_7Stream.cpp
|
|
||||||
XAudio2_7Stream.h
|
|
||||||
)
|
|
||||||
target_include_directories(audiocommon_xaudio27 PRIVATE
|
|
||||||
${PROJECT_SOURCE_DIR}/Externals
|
|
||||||
${PROJECT_SOURCE_DIR}/Externals/XAudio2_7
|
|
||||||
)
|
|
||||||
target_link_libraries(audiocommon PRIVATE audiocommon_xaudio27)
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
target_link_libraries(audiocommon PRIVATE cubeb SoundTouch FreeSurround)
|
target_link_libraries(audiocommon PRIVATE cubeb SoundTouch FreeSurround)
|
||||||
|
|
|
@ -1,309 +0,0 @@
|
||||||
// Copyright 2010 Dolphin Emulator Project
|
|
||||||
// Licensed under GPLv2+
|
|
||||||
// Refer to the license.txt file included.
|
|
||||||
|
|
||||||
#include <variant>
|
|
||||||
|
|
||||||
#include <xaudio2.h>
|
|
||||||
#include "AudioCommon/AudioCommon.h"
|
|
||||||
#include "AudioCommon/XAudio2Stream.h"
|
|
||||||
#include "Common/Event.h"
|
|
||||||
#include "Common/Logging/Log.h"
|
|
||||||
#include "Common/MsgHandler.h"
|
|
||||||
#include "Core/ConfigManager.h"
|
|
||||||
|
|
||||||
#ifndef XAUDIO2_DLL
|
|
||||||
#error You are building this module against the wrong version of DirectX. You probably need to remove DXSDK_DIR from your include path.
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct StreamingVoiceContext : public IXAudio2VoiceCallback
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
Mixer* const m_mixer;
|
|
||||||
Common::Event& m_sound_sync_event;
|
|
||||||
IXAudio2SourceVoice* m_source_voice;
|
|
||||||
std::variant<std::unique_ptr<short[]>, std::unique_ptr<float[]>> xaudio_buffer;
|
|
||||||
|
|
||||||
void SubmitBuffer(PBYTE buf_data);
|
|
||||||
|
|
||||||
bool m_use_surround = SConfig::GetInstance().bDPL2Decoder;
|
|
||||||
|
|
||||||
public:
|
|
||||||
StreamingVoiceContext(IXAudio2* pXAudio2, Mixer* pMixer, Common::Event& pSyncEvent);
|
|
||||||
virtual ~StreamingVoiceContext();
|
|
||||||
|
|
||||||
void Stop();
|
|
||||||
void Play();
|
|
||||||
|
|
||||||
STDMETHOD_(void, OnVoiceError)(THIS_ void* pBufferContext, HRESULT Error) {}
|
|
||||||
STDMETHOD_(void, OnVoiceProcessingPassStart)(UINT32) {}
|
|
||||||
STDMETHOD_(void, OnVoiceProcessingPassEnd)() {}
|
|
||||||
STDMETHOD_(void, OnBufferStart)(void*) {}
|
|
||||||
STDMETHOD_(void, OnLoopEnd)(void*) {}
|
|
||||||
STDMETHOD_(void, OnStreamEnd)() {}
|
|
||||||
STDMETHOD_(void, OnBufferEnd)(void* context);
|
|
||||||
};
|
|
||||||
|
|
||||||
const int NUM_BUFFERS = 3;
|
|
||||||
const int SAMPLES_PER_BUFFER = 96;
|
|
||||||
const int SAMPLES_PER_BUFFER_SURROUND = 256;
|
|
||||||
|
|
||||||
const int NUM_CHANNELS = 2;
|
|
||||||
const int BUFFER_SIZE = SAMPLES_PER_BUFFER * NUM_CHANNELS;
|
|
||||||
const int BUFFER_SIZE_BYTES = BUFFER_SIZE * sizeof(s16);
|
|
||||||
|
|
||||||
const int NUM_CHANNELS_SURROUND = 6;
|
|
||||||
const int BUFFER_SIZE_SURROUND = SAMPLES_PER_BUFFER_SURROUND * NUM_CHANNELS_SURROUND;
|
|
||||||
const int BUFFER_SIZE_BYTES_SURROUND = BUFFER_SIZE_SURROUND * sizeof(float);
|
|
||||||
void StreamingVoiceContext::SubmitBuffer(PBYTE buf_data)
|
|
||||||
{
|
|
||||||
XAUDIO2_BUFFER buf = {};
|
|
||||||
if (m_use_surround)
|
|
||||||
{
|
|
||||||
buf.AudioBytes = BUFFER_SIZE_BYTES_SURROUND;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
buf.AudioBytes = BUFFER_SIZE_BYTES;
|
|
||||||
}
|
|
||||||
buf.pContext = buf_data;
|
|
||||||
buf.pAudioData = buf_data;
|
|
||||||
|
|
||||||
m_source_voice->SubmitSourceBuffer(&buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
StreamingVoiceContext::StreamingVoiceContext(IXAudio2* pXAudio2, Mixer* pMixer,
|
|
||||||
Common::Event& pSyncEvent)
|
|
||||||
: m_mixer(pMixer), m_sound_sync_event(pSyncEvent)
|
|
||||||
{
|
|
||||||
WAVEFORMATEXTENSIBLE wfx = {};
|
|
||||||
wfx.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
|
|
||||||
wfx.Format.nSamplesPerSec = m_mixer->GetSampleRate();
|
|
||||||
|
|
||||||
// More information about these values:
|
|
||||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/dd390971(v=vs.85).aspx
|
|
||||||
if (m_use_surround)
|
|
||||||
{
|
|
||||||
xaudio_buffer = std::make_unique<float[]>(NUM_BUFFERS * BUFFER_SIZE_SURROUND);
|
|
||||||
|
|
||||||
wfx.Format.nChannels = 6;
|
|
||||||
wfx.Format.wBitsPerSample = 32;
|
|
||||||
wfx.Samples.wValidBitsPerSample = 32;
|
|
||||||
wfx.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER |
|
|
||||||
SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT;
|
|
||||||
wfx.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
|
|
||||||
wfx.Format.cbSize = sizeof(wfx.Samples) + sizeof(wfx.dwChannelMask) + sizeof(wfx.SubFormat);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
xaudio_buffer = std::make_unique<short[]>(NUM_BUFFERS * BUFFER_SIZE);
|
|
||||||
|
|
||||||
wfx.Format.nChannels = 2;
|
|
||||||
wfx.Format.wBitsPerSample = 16;
|
|
||||||
wfx.Samples.wValidBitsPerSample = 16;
|
|
||||||
wfx.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
|
|
||||||
wfx.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
|
|
||||||
wfx.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
|
|
||||||
}
|
|
||||||
|
|
||||||
wfx.Format.nBlockAlign = wfx.Format.nChannels * wfx.Format.wBitsPerSample / 8;
|
|
||||||
wfx.Format.nAvgBytesPerSec = wfx.Format.nSamplesPerSec * wfx.Format.nBlockAlign;
|
|
||||||
|
|
||||||
// create source voice
|
|
||||||
HRESULT hr;
|
|
||||||
if (FAILED(hr = pXAudio2->CreateSourceVoice(&m_source_voice, &wfx.Format, XAUDIO2_VOICE_NOSRC,
|
|
||||||
1.0f, this)))
|
|
||||||
{
|
|
||||||
PanicAlert("XAudio2 CreateSourceVoice failed: %#X", hr);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_source_voice->Start();
|
|
||||||
|
|
||||||
// start buffers with silence
|
|
||||||
if (m_use_surround)
|
|
||||||
{
|
|
||||||
for (int i = 0; i != NUM_BUFFERS; ++i)
|
|
||||||
{
|
|
||||||
SubmitBuffer(
|
|
||||||
reinterpret_cast<BYTE*>(std::get<std::unique_ptr<float[]>>(xaudio_buffer).get()) +
|
|
||||||
(i * BUFFER_SIZE_BYTES_SURROUND));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
for (int i = 0; i != NUM_BUFFERS; ++i)
|
|
||||||
{
|
|
||||||
SubmitBuffer(
|
|
||||||
reinterpret_cast<BYTE*>(std::get<std::unique_ptr<short[]>>(xaudio_buffer).get()) +
|
|
||||||
(i * BUFFER_SIZE_BYTES));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
StreamingVoiceContext::~StreamingVoiceContext()
|
|
||||||
{
|
|
||||||
if (m_source_voice)
|
|
||||||
{
|
|
||||||
m_source_voice->Stop();
|
|
||||||
m_source_voice->DestroyVoice();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void StreamingVoiceContext::Stop()
|
|
||||||
{
|
|
||||||
if (m_source_voice)
|
|
||||||
m_source_voice->Stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
void StreamingVoiceContext::Play()
|
|
||||||
{
|
|
||||||
if (m_source_voice)
|
|
||||||
m_source_voice->Start();
|
|
||||||
}
|
|
||||||
|
|
||||||
void StreamingVoiceContext::OnBufferEnd(void* context)
|
|
||||||
{
|
|
||||||
// buffer end callback; gets SAMPLES_PER_BUFFER samples for a new buffer
|
|
||||||
|
|
||||||
if (!m_source_voice || !context)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// m_sound_sync_event->Wait(); // sync
|
|
||||||
// m_sound_sync_event->Spin(); // or tight sync
|
|
||||||
|
|
||||||
if (m_use_surround)
|
|
||||||
{
|
|
||||||
m_mixer->MixSurround(static_cast<float*>(context), SAMPLES_PER_BUFFER_SURROUND);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_mixer->Mix(static_cast<short*>(context), SAMPLES_PER_BUFFER);
|
|
||||||
}
|
|
||||||
SubmitBuffer(reinterpret_cast<BYTE*>(context));
|
|
||||||
}
|
|
||||||
|
|
||||||
HMODULE XAudio2::m_xaudio2_dll = nullptr;
|
|
||||||
typedef decltype(&XAudio2Create) XAudio2Create_t;
|
|
||||||
void* XAudio2::PXAudio2Create = nullptr;
|
|
||||||
|
|
||||||
bool XAudio2::InitLibrary()
|
|
||||||
{
|
|
||||||
if (m_xaudio2_dll)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_xaudio2_dll = ::LoadLibrary(XAUDIO2_DLL);
|
|
||||||
if (!m_xaudio2_dll)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!PXAudio2Create)
|
|
||||||
{
|
|
||||||
PXAudio2Create = (XAudio2Create_t)::GetProcAddress(m_xaudio2_dll, "XAudio2Create");
|
|
||||||
if (!PXAudio2Create)
|
|
||||||
{
|
|
||||||
::FreeLibrary(m_xaudio2_dll);
|
|
||||||
m_xaudio2_dll = nullptr;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
XAudio2::XAudio2()
|
|
||||||
: m_mastering_voice(nullptr), m_volume(1.0f),
|
|
||||||
m_cleanup_com(SUCCEEDED(CoInitializeEx(nullptr, COINIT_MULTITHREADED)))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
XAudio2::~XAudio2()
|
|
||||||
{
|
|
||||||
Stop();
|
|
||||||
if (m_cleanup_com)
|
|
||||||
CoUninitialize();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool XAudio2::Init()
|
|
||||||
{
|
|
||||||
HRESULT hr;
|
|
||||||
|
|
||||||
// callback doesn't seem to run on a specific CPU anyways
|
|
||||||
IXAudio2* xaudptr;
|
|
||||||
if (FAILED(hr = ((XAudio2Create_t)PXAudio2Create)(&xaudptr, 0, XAUDIO2_DEFAULT_PROCESSOR)))
|
|
||||||
{
|
|
||||||
PanicAlert("XAudio2 init failed: %#X", hr);
|
|
||||||
Stop();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
m_xaudio2 = std::unique_ptr<IXAudio2, Releaser>(xaudptr);
|
|
||||||
|
|
||||||
// XAudio2 master voice
|
|
||||||
// XAUDIO2_DEFAULT_CHANNELS instead of 2 for expansion?
|
|
||||||
uint channels = NUM_CHANNELS;
|
|
||||||
if (SConfig::GetInstance().bDPL2Decoder)
|
|
||||||
channels = NUM_CHANNELS_SURROUND;
|
|
||||||
|
|
||||||
if (FAILED(hr = m_xaudio2->CreateMasteringVoice(&m_mastering_voice, channels,
|
|
||||||
m_mixer->GetSampleRate())))
|
|
||||||
{
|
|
||||||
PanicAlert("XAudio2 master voice creation failed: %#X", hr);
|
|
||||||
Stop();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Volume
|
|
||||||
m_mastering_voice->SetVolume(m_volume);
|
|
||||||
|
|
||||||
m_voice_context = std::unique_ptr<StreamingVoiceContext>(
|
|
||||||
new StreamingVoiceContext(m_xaudio2.get(), m_mixer.get(), m_sound_sync_event));
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void XAudio2::SetVolume(int volume)
|
|
||||||
{
|
|
||||||
// linear 1- .01
|
|
||||||
m_volume = (float)volume / 100.f;
|
|
||||||
|
|
||||||
if (m_mastering_voice)
|
|
||||||
m_mastering_voice->SetVolume(m_volume);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool XAudio2::SetRunning(bool running)
|
|
||||||
{
|
|
||||||
if (!m_voice_context)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (running)
|
|
||||||
m_voice_context->Play();
|
|
||||||
else
|
|
||||||
m_voice_context->Stop();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void XAudio2::Stop()
|
|
||||||
{
|
|
||||||
// m_sound_sync_event.Set();
|
|
||||||
|
|
||||||
m_voice_context.reset();
|
|
||||||
|
|
||||||
if (m_mastering_voice)
|
|
||||||
{
|
|
||||||
m_mastering_voice->DestroyVoice();
|
|
||||||
m_mastering_voice = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_xaudio2.reset(); // release interface
|
|
||||||
|
|
||||||
if (m_xaudio2_dll)
|
|
||||||
{
|
|
||||||
::FreeLibrary(m_xaudio2_dll);
|
|
||||||
m_xaudio2_dll = nullptr;
|
|
||||||
PXAudio2Create = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,67 +0,0 @@
|
||||||
// Copyright 2008 Dolphin Emulator Project
|
|
||||||
// Licensed under GPLv2+
|
|
||||||
// Refer to the license.txt file included.
|
|
||||||
|
|
||||||
// This audio backend uses XAudio2 via XAUDIO2_DLL
|
|
||||||
// It works on Windows 8+, where it is included as an OS component.
|
|
||||||
// This backend is always compiled, but only available if running on Win8+
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
#include "AudioCommon/SoundStream.h"
|
|
||||||
#include "Common/Event.h"
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
|
|
||||||
#include <windows.h>
|
|
||||||
|
|
||||||
struct StreamingVoiceContext;
|
|
||||||
struct IXAudio2;
|
|
||||||
struct IXAudio2MasteringVoice;
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
class XAudio2 final : public SoundStream
|
|
||||||
{
|
|
||||||
#ifdef _WIN32
|
|
||||||
|
|
||||||
private:
|
|
||||||
class Releaser
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
template <typename R>
|
|
||||||
void operator()(R* ptr)
|
|
||||||
{
|
|
||||||
ptr->Release();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
std::unique_ptr<IXAudio2, Releaser> m_xaudio2;
|
|
||||||
std::unique_ptr<StreamingVoiceContext> m_voice_context;
|
|
||||||
IXAudio2MasteringVoice* m_mastering_voice;
|
|
||||||
|
|
||||||
Common::Event m_sound_sync_event;
|
|
||||||
float m_volume;
|
|
||||||
|
|
||||||
const bool m_cleanup_com;
|
|
||||||
|
|
||||||
static HMODULE m_xaudio2_dll;
|
|
||||||
static void* PXAudio2Create;
|
|
||||||
|
|
||||||
static bool InitLibrary();
|
|
||||||
void Stop();
|
|
||||||
|
|
||||||
public:
|
|
||||||
XAudio2();
|
|
||||||
~XAudio2() override;
|
|
||||||
|
|
||||||
bool Init() override;
|
|
||||||
|
|
||||||
bool SetRunning(bool running) override;
|
|
||||||
void SetVolume(int volume) override;
|
|
||||||
|
|
||||||
static bool isValid() { return InitLibrary(); }
|
|
||||||
#endif
|
|
||||||
};
|
|
|
@ -1,295 +0,0 @@
|
||||||
// Copyright 2013 Dolphin Emulator Project
|
|
||||||
// Licensed under GPLv2+
|
|
||||||
// Refer to the license.txt file included.
|
|
||||||
|
|
||||||
// Note that this file *and this file only* must include XAudio2.h from the old
|
|
||||||
// DXSDK instead of other possible places.
|
|
||||||
|
|
||||||
#include <XAudio2_7/XAudio2.h>
|
|
||||||
#include <variant>
|
|
||||||
|
|
||||||
#include "AudioCommon/AudioCommon.h"
|
|
||||||
#include "AudioCommon/XAudio2_7Stream.h"
|
|
||||||
#include "Common/Event.h"
|
|
||||||
#include "Common/Logging/Log.h"
|
|
||||||
#include "Common/MsgHandler.h"
|
|
||||||
#include "Core/ConfigManager.h"
|
|
||||||
|
|
||||||
struct StreamingVoiceContext2_7 : public IXAudio2VoiceCallback
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
Mixer* const m_mixer;
|
|
||||||
Common::Event& m_sound_sync_event;
|
|
||||||
IXAudio2SourceVoice* m_source_voice;
|
|
||||||
std::variant<std::unique_ptr<short[]>, std::unique_ptr<float[]>> xaudio_buffer;
|
|
||||||
|
|
||||||
void SubmitBuffer(PBYTE buf_data);
|
|
||||||
|
|
||||||
bool m_use_surround = SConfig::GetInstance().bDPL2Decoder;
|
|
||||||
|
|
||||||
public:
|
|
||||||
StreamingVoiceContext2_7(IXAudio2* pXAudio2, Mixer* pMixer, Common::Event& pSyncEvent);
|
|
||||||
virtual ~StreamingVoiceContext2_7();
|
|
||||||
|
|
||||||
void Stop();
|
|
||||||
void Play();
|
|
||||||
|
|
||||||
STDMETHOD_(void, OnVoiceError)(THIS_ void* pBufferContext, HRESULT Error) {}
|
|
||||||
STDMETHOD_(void, OnVoiceProcessingPassStart)(UINT32) {}
|
|
||||||
STDMETHOD_(void, OnVoiceProcessingPassEnd)() {}
|
|
||||||
STDMETHOD_(void, OnBufferStart)(void*) {}
|
|
||||||
STDMETHOD_(void, OnLoopEnd)(void*) {}
|
|
||||||
STDMETHOD_(void, OnStreamEnd)() {}
|
|
||||||
STDMETHOD_(void, OnBufferEnd)(void* context);
|
|
||||||
};
|
|
||||||
|
|
||||||
const int NUM_BUFFERS = 3;
|
|
||||||
const int SAMPLES_PER_BUFFER = 96;
|
|
||||||
const int SAMPLES_PER_BUFFER_SURROUND = 256;
|
|
||||||
|
|
||||||
const int NUM_CHANNELS = 2;
|
|
||||||
const int BUFFER_SIZE = SAMPLES_PER_BUFFER * NUM_CHANNELS;
|
|
||||||
const int BUFFER_SIZE_BYTES = BUFFER_SIZE * sizeof(s16);
|
|
||||||
|
|
||||||
const int NUM_CHANNELS_SURROUND = 6;
|
|
||||||
const int BUFFER_SIZE_SURROUND = SAMPLES_PER_BUFFER_SURROUND * NUM_CHANNELS_SURROUND;
|
|
||||||
const int BUFFER_SIZE_BYTES_SURROUND = BUFFER_SIZE_SURROUND * sizeof(float);
|
|
||||||
void StreamingVoiceContext2_7::SubmitBuffer(PBYTE buf_data)
|
|
||||||
{
|
|
||||||
XAUDIO2_BUFFER buf = {};
|
|
||||||
if (m_use_surround)
|
|
||||||
{
|
|
||||||
buf.AudioBytes = BUFFER_SIZE_BYTES_SURROUND;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
buf.AudioBytes = BUFFER_SIZE_BYTES;
|
|
||||||
}
|
|
||||||
buf.pContext = buf_data;
|
|
||||||
buf.pAudioData = buf_data;
|
|
||||||
|
|
||||||
m_source_voice->SubmitSourceBuffer(&buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
StreamingVoiceContext2_7::StreamingVoiceContext2_7(IXAudio2* pXAudio2, Mixer* pMixer,
|
|
||||||
Common::Event& pSyncEvent)
|
|
||||||
: m_mixer(pMixer), m_sound_sync_event(pSyncEvent)
|
|
||||||
{
|
|
||||||
WAVEFORMATEXTENSIBLE wfx = {};
|
|
||||||
wfx.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
|
|
||||||
wfx.Format.nSamplesPerSec = m_mixer->GetSampleRate();
|
|
||||||
|
|
||||||
// More information about these values:
|
|
||||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/dd390971(v=vs.85).aspx
|
|
||||||
if (m_use_surround)
|
|
||||||
{
|
|
||||||
xaudio_buffer = std::make_unique<float[]>(NUM_BUFFERS * BUFFER_SIZE_SURROUND);
|
|
||||||
|
|
||||||
wfx.Format.nChannels = 6;
|
|
||||||
wfx.Format.wBitsPerSample = 32;
|
|
||||||
wfx.Samples.wValidBitsPerSample = 32;
|
|
||||||
wfx.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER |
|
|
||||||
SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT;
|
|
||||||
wfx.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
|
|
||||||
wfx.Format.cbSize = sizeof(wfx.Samples) + sizeof(wfx.dwChannelMask) + sizeof(wfx.SubFormat);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
xaudio_buffer = std::make_unique<short[]>(NUM_BUFFERS * BUFFER_SIZE);
|
|
||||||
|
|
||||||
wfx.Format.nChannels = 2;
|
|
||||||
wfx.Format.wBitsPerSample = 16;
|
|
||||||
wfx.Samples.wValidBitsPerSample = 16;
|
|
||||||
wfx.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
|
|
||||||
wfx.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
|
|
||||||
wfx.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
|
|
||||||
}
|
|
||||||
|
|
||||||
wfx.Format.nBlockAlign = wfx.Format.nChannels * wfx.Format.wBitsPerSample / 8;
|
|
||||||
wfx.Format.nAvgBytesPerSec = wfx.Format.nSamplesPerSec * wfx.Format.nBlockAlign;
|
|
||||||
|
|
||||||
// create source voice
|
|
||||||
HRESULT hr;
|
|
||||||
if (FAILED(hr = pXAudio2->CreateSourceVoice(&m_source_voice, &wfx.Format, XAUDIO2_VOICE_NOSRC,
|
|
||||||
1.0f, this)))
|
|
||||||
{
|
|
||||||
PanicAlert("XAudio2_7 CreateSourceVoice failed: %#X", hr);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_source_voice->Start();
|
|
||||||
|
|
||||||
// start buffers with silence
|
|
||||||
if (m_use_surround)
|
|
||||||
{
|
|
||||||
for (int i = 0; i != NUM_BUFFERS; ++i)
|
|
||||||
{
|
|
||||||
SubmitBuffer(
|
|
||||||
reinterpret_cast<BYTE*>(std::get<std::unique_ptr<float[]>>(xaudio_buffer).get()) +
|
|
||||||
(i * BUFFER_SIZE_BYTES_SURROUND));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
for (int i = 0; i != NUM_BUFFERS; ++i)
|
|
||||||
{
|
|
||||||
SubmitBuffer(
|
|
||||||
reinterpret_cast<BYTE*>(std::get<std::unique_ptr<short[]>>(xaudio_buffer).get()) +
|
|
||||||
(i * BUFFER_SIZE_BYTES));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
StreamingVoiceContext2_7::~StreamingVoiceContext2_7()
|
|
||||||
{
|
|
||||||
if (m_source_voice)
|
|
||||||
{
|
|
||||||
m_source_voice->Stop();
|
|
||||||
m_source_voice->DestroyVoice();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void StreamingVoiceContext2_7::Stop()
|
|
||||||
{
|
|
||||||
if (m_source_voice)
|
|
||||||
m_source_voice->Stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
void StreamingVoiceContext2_7::Play()
|
|
||||||
{
|
|
||||||
if (m_source_voice)
|
|
||||||
m_source_voice->Start();
|
|
||||||
}
|
|
||||||
|
|
||||||
void StreamingVoiceContext2_7::OnBufferEnd(void* context)
|
|
||||||
{
|
|
||||||
// buffer end callback; gets SAMPLES_PER_BUFFER samples for a new buffer
|
|
||||||
|
|
||||||
if (!m_source_voice || !context)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// m_sound_sync_event->Wait(); // sync
|
|
||||||
// m_sound_sync_event->Spin(); // or tight sync
|
|
||||||
|
|
||||||
if (m_use_surround)
|
|
||||||
{
|
|
||||||
m_mixer->MixSurround(static_cast<float*>(context), SAMPLES_PER_BUFFER_SURROUND);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_mixer->Mix(static_cast<short*>(context), SAMPLES_PER_BUFFER);
|
|
||||||
}
|
|
||||||
SubmitBuffer(reinterpret_cast<BYTE*>(context));
|
|
||||||
}
|
|
||||||
|
|
||||||
HMODULE XAudio2_7::m_xaudio2_dll = nullptr;
|
|
||||||
|
|
||||||
void XAudio2_7::ReleaseIXAudio2(IXAudio2* ptr)
|
|
||||||
{
|
|
||||||
ptr->Release();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool XAudio2_7::InitLibrary()
|
|
||||||
{
|
|
||||||
if (m_xaudio2_dll)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_xaudio2_dll = ::LoadLibrary(TEXT("xaudio2_7.dll"));
|
|
||||||
|
|
||||||
return m_xaudio2_dll != nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
XAudio2_7::XAudio2_7()
|
|
||||||
: m_mastering_voice(nullptr), m_volume(1.0f),
|
|
||||||
m_cleanup_com(SUCCEEDED(CoInitializeEx(nullptr, COINIT_MULTITHREADED)))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
XAudio2_7::~XAudio2_7()
|
|
||||||
{
|
|
||||||
Stop();
|
|
||||||
if (m_cleanup_com)
|
|
||||||
CoUninitialize();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool XAudio2_7::Init()
|
|
||||||
{
|
|
||||||
HRESULT hr;
|
|
||||||
|
|
||||||
// callback doesn't seem to run on a specific CPU anyways
|
|
||||||
IXAudio2* xaudptr;
|
|
||||||
if (FAILED(hr = XAudio2Create(&xaudptr, 0, XAUDIO2_DEFAULT_PROCESSOR)))
|
|
||||||
{
|
|
||||||
PanicAlert("XAudio2_7 init failed: %#X", hr);
|
|
||||||
Stop();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
m_xaudio2 = std::unique_ptr<IXAudio2, Releaser>(xaudptr);
|
|
||||||
|
|
||||||
// XAudio2 master voice
|
|
||||||
// XAUDIO2_DEFAULT_CHANNELS instead of 2 for expansion?
|
|
||||||
uint channels = NUM_CHANNELS;
|
|
||||||
if (SConfig::GetInstance().bDPL2Decoder)
|
|
||||||
channels = NUM_CHANNELS_SURROUND;
|
|
||||||
|
|
||||||
if (FAILED(hr = m_xaudio2->CreateMasteringVoice(&m_mastering_voice, channels,
|
|
||||||
m_mixer->GetSampleRate())))
|
|
||||||
{
|
|
||||||
PanicAlert("XAudio2_7 master voice creation failed: %#X", hr);
|
|
||||||
Stop();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Volume
|
|
||||||
m_mastering_voice->SetVolume(m_volume);
|
|
||||||
|
|
||||||
m_voice_context = std::unique_ptr<StreamingVoiceContext2_7>(
|
|
||||||
new StreamingVoiceContext2_7(m_xaudio2.get(), m_mixer.get(), m_sound_sync_event));
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void XAudio2_7::SetVolume(int volume)
|
|
||||||
{
|
|
||||||
// linear 1- .01
|
|
||||||
m_volume = (float)volume / 100.f;
|
|
||||||
|
|
||||||
if (m_mastering_voice)
|
|
||||||
m_mastering_voice->SetVolume(m_volume);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool XAudio2_7::SetRunning(bool running)
|
|
||||||
{
|
|
||||||
if (!m_voice_context)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (running)
|
|
||||||
m_voice_context->Play();
|
|
||||||
else
|
|
||||||
m_voice_context->Stop();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void XAudio2_7::Stop()
|
|
||||||
{
|
|
||||||
// m_sound_sync_event.Set();
|
|
||||||
|
|
||||||
m_voice_context.reset();
|
|
||||||
|
|
||||||
if (m_mastering_voice)
|
|
||||||
{
|
|
||||||
m_mastering_voice->DestroyVoice();
|
|
||||||
m_mastering_voice = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_xaudio2.reset(); // release interface
|
|
||||||
|
|
||||||
if (m_xaudio2_dll)
|
|
||||||
{
|
|
||||||
::FreeLibrary(m_xaudio2_dll);
|
|
||||||
m_xaudio2_dll = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,72 +0,0 @@
|
||||||
// Copyright 2013 Dolphin Emulator Project
|
|
||||||
// Licensed under GPLv2+
|
|
||||||
// Refer to the license.txt file included.
|
|
||||||
|
|
||||||
// This audio backend uses XAudio2 via XAudio2_7.dll
|
|
||||||
// This version of the library is included in the June 2010 DirectX SDK and
|
|
||||||
// works on all versions of Windows, however the SDK and/or redist must be
|
|
||||||
// separately installed.
|
|
||||||
// Therefore this backend is available iff:
|
|
||||||
// * SDK is available at compile-time
|
|
||||||
// * runtime dll is available at runtime
|
|
||||||
// Dolphin ships the relevant SDK headers in Externals, so it's always available.
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include "AudioCommon/SoundStream.h"
|
|
||||||
#include "Common/Event.h"
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
|
|
||||||
#include <Windows.h>
|
|
||||||
|
|
||||||
struct StreamingVoiceContext2_7;
|
|
||||||
struct IXAudio2;
|
|
||||||
struct IXAudio2MasteringVoice;
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
class XAudio2_7 final : public SoundStream
|
|
||||||
{
|
|
||||||
#ifdef _WIN32
|
|
||||||
|
|
||||||
private:
|
|
||||||
static void ReleaseIXAudio2(IXAudio2* ptr);
|
|
||||||
|
|
||||||
class Releaser
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
template <typename R>
|
|
||||||
void operator()(R* ptr)
|
|
||||||
{
|
|
||||||
ReleaseIXAudio2(ptr);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
std::unique_ptr<IXAudio2, Releaser> m_xaudio2;
|
|
||||||
std::unique_ptr<StreamingVoiceContext2_7> m_voice_context;
|
|
||||||
IXAudio2MasteringVoice* m_mastering_voice;
|
|
||||||
|
|
||||||
Common::Event m_sound_sync_event;
|
|
||||||
float m_volume;
|
|
||||||
|
|
||||||
const bool m_cleanup_com;
|
|
||||||
|
|
||||||
static HMODULE m_xaudio2_dll;
|
|
||||||
|
|
||||||
static bool InitLibrary();
|
|
||||||
void Stop();
|
|
||||||
|
|
||||||
public:
|
|
||||||
XAudio2_7();
|
|
||||||
~XAudio2_7() override;
|
|
||||||
|
|
||||||
bool Init() override;
|
|
||||||
|
|
||||||
bool SetRunning(bool running) override;
|
|
||||||
void SetVolume(int volume) override;
|
|
||||||
|
|
||||||
static bool isValid() { return InitLibrary(); }
|
|
||||||
#endif
|
|
||||||
};
|
|
|
@ -53,7 +53,6 @@ struct BootParameters;
|
||||||
#define BACKEND_CUBEB "Cubeb"
|
#define BACKEND_CUBEB "Cubeb"
|
||||||
#define BACKEND_OPENAL "OpenAL"
|
#define BACKEND_OPENAL "OpenAL"
|
||||||
#define BACKEND_PULSEAUDIO "Pulse"
|
#define BACKEND_PULSEAUDIO "Pulse"
|
||||||
#define BACKEND_XAUDIO2 "XAudio2"
|
|
||||||
#define BACKEND_OPENSLES "OpenSLES"
|
#define BACKEND_OPENSLES "OpenSLES"
|
||||||
#define BACKEND_WASAPI _trans("WASAPI (Exclusive Mode)")
|
#define BACKEND_WASAPI _trans("WASAPI (Exclusive Mode)")
|
||||||
|
|
||||||
|
|
|
@ -73,10 +73,10 @@
|
||||||
This numeral indicates the "minimum system required" to run the resulting
|
This numeral indicates the "minimum system required" to run the resulting
|
||||||
program. Dolphin targets Vista+, so it should be 0x0600. However in practice,
|
program. Dolphin targets Vista+, so it should be 0x0600. However in practice,
|
||||||
_WIN32_WINNT just removes up-level API declarations from headers. This is a
|
_WIN32_WINNT just removes up-level API declarations from headers. This is a
|
||||||
problem for XAudio2 and XInput, where Dolphin expects to compile against the
|
problem for XInput, where Dolphin expects to compile against the Win8+
|
||||||
Win8+ versions of their headers. So while we really need Vista+ level of
|
versions of the headers. So while we really need Vista+ level of support,
|
||||||
support, we declare Win8+ here globally. If this becomes a problem, the
|
we declare Win8+ here globally. If this becomes a problem, the higher
|
||||||
higher declaration can be contained to just the XAudio2/XInput related code.
|
declaration can be contained to just the XInput related code.
|
||||||
-->
|
-->
|
||||||
<PreprocessorDefinitions>_WIN32_WINNT=0x0602;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>_WIN32_WINNT=0x0602;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<WarningLevel>Level4</WarningLevel>
|
<WarningLevel>Level4</WarningLevel>
|
||||||
|
|
Loading…
Reference in New Issue