2016-10-29 02:02:21 +03:00
|
|
|
//--------------------------------------------------------------------------------------
|
|
|
|
// File: WAVFileReader.cpp
|
|
|
|
//
|
|
|
|
// Functions for loading WAV audio files
|
|
|
|
//
|
2021-02-27 10:01:15 +03:00
|
|
|
// Copyright (c) Microsoft Corporation.
|
2018-02-24 09:08:23 +03:00
|
|
|
// Licensed under the MIT License.
|
2016-10-29 02:02:21 +03:00
|
|
|
//
|
|
|
|
// http://go.microsoft.com/fwlink/?LinkId=248929
|
2018-02-24 09:08:23 +03:00
|
|
|
// http://go.microsoft.com/fwlink/?LinkID=615561
|
2016-10-29 02:02:21 +03:00
|
|
|
//-------------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
#include "pch.h"
|
|
|
|
#include "PlatformHelpers.h"
|
|
|
|
#include "WAVFileReader.h"
|
|
|
|
|
|
|
|
using namespace DirectX;
|
|
|
|
|
|
|
|
|
|
|
|
namespace
|
|
|
|
{
|
2018-05-02 05:13:50 +03:00
|
|
|
//---------------------------------------------------------------------------------
|
|
|
|
// .WAV files
|
|
|
|
//---------------------------------------------------------------------------------
|
2020-03-17 09:19:38 +03:00
|
|
|
constexpr uint32_t FOURCC_RIFF_TAG = MAKEFOURCC('R', 'I', 'F', 'F');
|
|
|
|
constexpr uint32_t FOURCC_FORMAT_TAG = MAKEFOURCC('f', 'm', 't', ' ');
|
|
|
|
constexpr uint32_t FOURCC_DATA_TAG = MAKEFOURCC('d', 'a', 't', 'a');
|
|
|
|
constexpr uint32_t FOURCC_WAVE_FILE_TAG = MAKEFOURCC('W', 'A', 'V', 'E');
|
|
|
|
constexpr uint32_t FOURCC_XWMA_FILE_TAG = MAKEFOURCC('X', 'W', 'M', 'A');
|
|
|
|
constexpr uint32_t FOURCC_DLS_SAMPLE = MAKEFOURCC('w', 's', 'm', 'p');
|
|
|
|
constexpr uint32_t FOURCC_MIDI_SAMPLE = MAKEFOURCC('s', 'm', 'p', 'l');
|
|
|
|
constexpr uint32_t FOURCC_XWMA_DPDS = MAKEFOURCC('d', 'p', 'd', 's');
|
|
|
|
constexpr uint32_t FOURCC_XMA_SEEK = MAKEFOURCC('s', 'e', 'e', 'k');
|
2016-10-29 02:02:21 +03:00
|
|
|
|
|
|
|
#pragma pack(push,1)
|
2018-03-16 22:03:02 +03:00
|
|
|
struct RIFFChunk
|
|
|
|
{
|
|
|
|
uint32_t tag;
|
|
|
|
uint32_t size;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct RIFFChunkHeader
|
|
|
|
{
|
|
|
|
uint32_t tag;
|
|
|
|
uint32_t size;
|
|
|
|
uint32_t riff;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct DLSLoop
|
|
|
|
{
|
2021-07-31 23:06:06 +03:00
|
|
|
static constexpr uint32_t LOOP_TYPE_FORWARD = 0x00000000;
|
|
|
|
static constexpr uint32_t LOOP_TYPE_RELEASE = 0x00000001;
|
2018-03-16 22:03:02 +03:00
|
|
|
|
|
|
|
uint32_t size;
|
|
|
|
uint32_t loopType;
|
|
|
|
uint32_t loopStart;
|
|
|
|
uint32_t loopLength;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct RIFFDLSSample
|
|
|
|
{
|
2021-07-31 23:06:06 +03:00
|
|
|
static constexpr uint32_t OPTIONS_NOTRUNCATION = 0x00000001;
|
|
|
|
static constexpr uint32_t OPTIONS_NOCOMPRESSION = 0x00000002;
|
2018-03-16 22:03:02 +03:00
|
|
|
|
|
|
|
uint32_t size;
|
|
|
|
uint16_t unityNote;
|
|
|
|
int16_t fineTune;
|
|
|
|
int32_t gain;
|
|
|
|
uint32_t options;
|
|
|
|
uint32_t loopCount;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct MIDILoop
|
|
|
|
{
|
2021-07-31 23:06:06 +03:00
|
|
|
static constexpr uint32_t LOOP_TYPE_FORWARD = 0x00000000;
|
|
|
|
static constexpr uint32_t LOOP_TYPE_ALTERNATING = 0x00000001;
|
|
|
|
static constexpr uint32_t LOOP_TYPE_BACKWARD = 0x00000002;
|
2018-03-16 22:03:02 +03:00
|
|
|
|
|
|
|
uint32_t cuePointId;
|
|
|
|
uint32_t type;
|
|
|
|
uint32_t start;
|
|
|
|
uint32_t end;
|
|
|
|
uint32_t fraction;
|
|
|
|
uint32_t playCount;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct RIFFMIDISample
|
|
|
|
{
|
|
|
|
uint32_t manufacturerId;
|
|
|
|
uint32_t productId;
|
|
|
|
uint32_t samplePeriod;
|
|
|
|
uint32_t unityNode;
|
|
|
|
uint32_t pitchFraction;
|
|
|
|
uint32_t SMPTEFormat;
|
|
|
|
uint32_t SMPTEOffset;
|
|
|
|
uint32_t loopCount;
|
|
|
|
uint32_t samplerData;
|
|
|
|
};
|
2016-10-29 02:02:21 +03:00
|
|
|
#pragma pack(pop)
|
|
|
|
|
2018-03-16 22:03:02 +03:00
|
|
|
static_assert(sizeof(RIFFChunk) == 8, "structure size mismatch");
|
|
|
|
static_assert(sizeof(RIFFChunkHeader) == 12, "structure size mismatch");
|
|
|
|
static_assert(sizeof(DLSLoop) == 16, "structure size mismatch");
|
|
|
|
static_assert(sizeof(RIFFDLSSample) == 20, "structure size mismatch");
|
|
|
|
static_assert(sizeof(MIDILoop) == 24, "structure size mismatch");
|
|
|
|
static_assert(sizeof(RIFFMIDISample) == 36, "structure size mismatch");
|
2016-10-29 02:02:21 +03:00
|
|
|
|
2018-05-02 05:13:50 +03:00
|
|
|
//---------------------------------------------------------------------------------
|
|
|
|
const RIFFChunk* FindChunk(
|
|
|
|
_In_reads_bytes_(sizeBytes) const uint8_t* data,
|
|
|
|
_In_ size_t sizeBytes,
|
2019-12-11 05:33:53 +03:00
|
|
|
_In_ uint32_t tag) noexcept
|
2016-10-29 02:02:21 +03:00
|
|
|
{
|
2018-05-02 05:13:50 +03:00
|
|
|
if (!data)
|
|
|
|
return nullptr;
|
2016-10-29 02:02:21 +03:00
|
|
|
|
2018-05-02 05:13:50 +03:00
|
|
|
const uint8_t* ptr = data;
|
|
|
|
const uint8_t* end = data + sizeBytes;
|
2016-10-29 02:02:21 +03:00
|
|
|
|
2018-05-02 05:13:50 +03:00
|
|
|
while (end > (ptr + sizeof(RIFFChunk)))
|
|
|
|
{
|
|
|
|
auto header = reinterpret_cast<const RIFFChunk*>(ptr);
|
|
|
|
if (header->tag == tag)
|
|
|
|
return header;
|
2016-10-29 02:02:21 +03:00
|
|
|
|
2019-05-25 02:36:18 +03:00
|
|
|
auto offset = header->size + sizeof(RIFFChunk);
|
2018-05-02 05:13:50 +03:00
|
|
|
ptr += offset;
|
|
|
|
}
|
2016-10-29 02:02:21 +03:00
|
|
|
|
2018-05-02 05:13:50 +03:00
|
|
|
return nullptr;
|
2016-10-29 02:02:21 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-05-02 05:13:50 +03:00
|
|
|
//---------------------------------------------------------------------------------
|
|
|
|
HRESULT WaveFindFormatAndData(
|
|
|
|
_In_reads_bytes_(wavDataSize) const uint8_t* wavData,
|
|
|
|
_In_ size_t wavDataSize,
|
|
|
|
_Outptr_ const WAVEFORMATEX** pwfx,
|
|
|
|
_Outptr_ const uint8_t** pdata,
|
|
|
|
_Out_ uint32_t* dataSize,
|
|
|
|
_Out_ bool& dpds,
|
2019-12-11 05:33:53 +03:00
|
|
|
_Out_ bool& seek) noexcept
|
2016-10-29 02:02:21 +03:00
|
|
|
{
|
2018-05-02 05:13:50 +03:00
|
|
|
if (!wavData || !pwfx)
|
|
|
|
return E_POINTER;
|
2016-10-29 02:02:21 +03:00
|
|
|
|
2018-05-02 05:13:50 +03:00
|
|
|
dpds = seek = false;
|
2016-10-29 02:02:21 +03:00
|
|
|
|
2018-05-02 05:13:50 +03:00
|
|
|
if (wavDataSize < (sizeof(RIFFChunk) * 2 + sizeof(uint32_t) + sizeof(WAVEFORMAT)))
|
|
|
|
{
|
|
|
|
return E_FAIL;
|
|
|
|
}
|
2016-10-29 02:02:21 +03:00
|
|
|
|
2018-05-02 05:13:50 +03:00
|
|
|
const uint8_t* wavEnd = wavData + wavDataSize;
|
2016-10-29 02:02:21 +03:00
|
|
|
|
2018-05-02 05:13:50 +03:00
|
|
|
// Locate RIFF 'WAVE'
|
|
|
|
auto riffChunk = FindChunk(wavData, wavDataSize, FOURCC_RIFF_TAG);
|
|
|
|
if (!riffChunk || riffChunk->size < 4)
|
|
|
|
{
|
|
|
|
return E_FAIL;
|
|
|
|
}
|
2016-10-29 02:02:21 +03:00
|
|
|
|
2018-05-02 05:13:50 +03:00
|
|
|
auto riffHeader = reinterpret_cast<const RIFFChunkHeader*>(riffChunk);
|
|
|
|
if (riffHeader->riff != FOURCC_WAVE_FILE_TAG && riffHeader->riff != FOURCC_XWMA_FILE_TAG)
|
|
|
|
{
|
|
|
|
return E_FAIL;
|
|
|
|
}
|
2016-10-29 02:02:21 +03:00
|
|
|
|
2018-05-02 05:13:50 +03:00
|
|
|
// Locate 'fmt '
|
|
|
|
auto ptr = reinterpret_cast<const uint8_t*>(riffHeader) + sizeof(RIFFChunkHeader);
|
|
|
|
if ((ptr + sizeof(RIFFChunk)) > wavEnd)
|
|
|
|
{
|
|
|
|
return HRESULT_FROM_WIN32(ERROR_HANDLE_EOF);
|
|
|
|
}
|
2016-10-29 02:02:21 +03:00
|
|
|
|
2018-05-02 05:13:50 +03:00
|
|
|
auto fmtChunk = FindChunk(ptr, riffHeader->size, FOURCC_FORMAT_TAG);
|
|
|
|
if (!fmtChunk || fmtChunk->size < sizeof(PCMWAVEFORMAT))
|
2016-10-29 02:02:21 +03:00
|
|
|
{
|
2018-05-02 05:13:50 +03:00
|
|
|
return E_FAIL;
|
|
|
|
}
|
2016-10-29 02:02:21 +03:00
|
|
|
|
2018-05-02 05:13:50 +03:00
|
|
|
ptr = reinterpret_cast<const uint8_t*>(fmtChunk) + sizeof(RIFFChunk);
|
|
|
|
if (ptr + fmtChunk->size > wavEnd)
|
|
|
|
{
|
|
|
|
return HRESULT_FROM_WIN32(ERROR_HANDLE_EOF);
|
|
|
|
}
|
2016-10-29 02:02:21 +03:00
|
|
|
|
2018-05-02 05:13:50 +03:00
|
|
|
auto wf = reinterpret_cast<const WAVEFORMAT*>(ptr);
|
2016-10-29 02:02:21 +03:00
|
|
|
|
2018-05-02 05:13:50 +03:00
|
|
|
// Validate WAVEFORMAT (focused on chunk size and format tag, not other data that XAUDIO2 will validate)
|
|
|
|
switch (wf->wFormatTag)
|
|
|
|
{
|
|
|
|
case WAVE_FORMAT_PCM:
|
|
|
|
case WAVE_FORMAT_IEEE_FLOAT:
|
2018-11-10 03:00:14 +03:00
|
|
|
// Can be a PCMWAVEFORMAT (16 bytes) or WAVEFORMATEX (18 bytes)
|
2018-05-02 05:13:50 +03:00
|
|
|
// We validiated chunk as at least sizeof(PCMWAVEFORMAT) above
|
|
|
|
break;
|
2016-10-29 02:02:21 +03:00
|
|
|
|
2018-05-02 05:13:50 +03:00
|
|
|
default:
|
|
|
|
{
|
|
|
|
if (fmtChunk->size < sizeof(WAVEFORMATEX))
|
|
|
|
{
|
|
|
|
return E_FAIL;
|
|
|
|
}
|
2016-10-29 02:02:21 +03:00
|
|
|
|
2018-05-02 05:13:50 +03:00
|
|
|
auto wfx = reinterpret_cast<const WAVEFORMATEX*>(ptr);
|
2016-10-29 02:02:21 +03:00
|
|
|
|
2018-05-02 05:13:50 +03:00
|
|
|
if (fmtChunk->size < (sizeof(WAVEFORMATEX) + wfx->cbSize))
|
|
|
|
{
|
|
|
|
return E_FAIL;
|
|
|
|
}
|
2016-10-29 02:02:21 +03:00
|
|
|
|
2018-05-02 05:13:50 +03:00
|
|
|
switch (wfx->wFormatTag)
|
|
|
|
{
|
|
|
|
case WAVE_FORMAT_WMAUDIO2:
|
|
|
|
case WAVE_FORMAT_WMAUDIO3:
|
|
|
|
dpds = true;
|
|
|
|
break;
|
2016-10-29 02:02:21 +03:00
|
|
|
|
2018-05-02 05:13:50 +03:00
|
|
|
case 0x166 /*WAVE_FORMAT_XMA2*/: // XMA2 is supported by Xbox One
|
|
|
|
if ((fmtChunk->size < 52 /*sizeof(XMA2WAVEFORMATEX)*/) || (wfx->cbSize < 34 /*( sizeof(XMA2WAVEFORMATEX) - sizeof(WAVEFORMATEX) )*/))
|
2018-03-16 22:03:02 +03:00
|
|
|
{
|
2018-05-02 05:13:50 +03:00
|
|
|
return E_FAIL;
|
2018-03-16 22:03:02 +03:00
|
|
|
}
|
2018-05-02 05:13:50 +03:00
|
|
|
seek = true;
|
|
|
|
break;
|
2016-10-29 02:02:21 +03:00
|
|
|
|
2018-05-02 05:13:50 +03:00
|
|
|
case WAVE_FORMAT_ADPCM:
|
|
|
|
if ((fmtChunk->size < (sizeof(WAVEFORMATEX) + 32)) || (wfx->cbSize < 32 /*MSADPCM_FORMAT_EXTRA_BYTES*/))
|
2018-03-16 22:03:02 +03:00
|
|
|
{
|
2018-05-02 05:13:50 +03:00
|
|
|
return E_FAIL;
|
|
|
|
}
|
|
|
|
break;
|
2016-10-29 02:02:21 +03:00
|
|
|
|
2018-05-02 05:13:50 +03:00
|
|
|
case WAVE_FORMAT_EXTENSIBLE:
|
|
|
|
if ((fmtChunk->size < sizeof(WAVEFORMATEXTENSIBLE)) || (wfx->cbSize < (sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX))))
|
|
|
|
{
|
|
|
|
return E_FAIL;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2018-06-07 04:37:54 +03:00
|
|
|
static const GUID s_wfexBase = { 0x00000000, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71 } };
|
2016-10-29 02:02:21 +03:00
|
|
|
|
2018-05-02 05:13:50 +03:00
|
|
|
auto wfex = reinterpret_cast<const WAVEFORMATEXTENSIBLE*>(ptr);
|
2018-03-16 22:03:02 +03:00
|
|
|
|
2018-05-02 05:13:50 +03:00
|
|
|
if (memcmp(reinterpret_cast<const BYTE*>(&wfex->SubFormat) + sizeof(DWORD),
|
|
|
|
reinterpret_cast<const BYTE*>(&s_wfexBase) + sizeof(DWORD), sizeof(GUID) - sizeof(DWORD)) != 0)
|
|
|
|
{
|
2018-03-16 22:03:02 +03:00
|
|
|
return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
|
2018-05-02 05:13:50 +03:00
|
|
|
}
|
2018-03-16 22:03:02 +03:00
|
|
|
|
2018-05-02 05:13:50 +03:00
|
|
|
switch (wfex->SubFormat.Data1)
|
|
|
|
{
|
|
|
|
case WAVE_FORMAT_PCM:
|
|
|
|
case WAVE_FORMAT_IEEE_FLOAT:
|
|
|
|
break;
|
|
|
|
|
|
|
|
// MS-ADPCM and XMA2 are not supported as WAVEFORMATEXTENSIBLE
|
|
|
|
|
|
|
|
case WAVE_FORMAT_WMAUDIO2:
|
|
|
|
case WAVE_FORMAT_WMAUDIO3:
|
|
|
|
dpds = true;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
|
|
|
|
}
|
2018-03-16 22:03:02 +03:00
|
|
|
|
2018-05-02 05:13:50 +03:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
|
|
|
|
}
|
2016-10-29 02:02:21 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-02 05:13:50 +03:00
|
|
|
// Locate 'data'
|
|
|
|
ptr = reinterpret_cast<const uint8_t*>(riffHeader) + sizeof(RIFFChunkHeader);
|
|
|
|
if ((ptr + sizeof(RIFFChunk)) > wavEnd)
|
|
|
|
{
|
|
|
|
return HRESULT_FROM_WIN32(ERROR_HANDLE_EOF);
|
|
|
|
}
|
2016-10-29 02:02:21 +03:00
|
|
|
|
2018-05-02 05:13:50 +03:00
|
|
|
auto dataChunk = FindChunk(ptr, riffChunk->size, FOURCC_DATA_TAG);
|
|
|
|
if (!dataChunk || !dataChunk->size)
|
|
|
|
{
|
|
|
|
return HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
|
|
|
|
}
|
2016-10-29 02:02:21 +03:00
|
|
|
|
2018-05-02 05:13:50 +03:00
|
|
|
ptr = reinterpret_cast<const uint8_t*>(dataChunk) + sizeof(RIFFChunk);
|
|
|
|
if (ptr + dataChunk->size > wavEnd)
|
|
|
|
{
|
|
|
|
return HRESULT_FROM_WIN32(ERROR_HANDLE_EOF);
|
|
|
|
}
|
2016-10-29 02:02:21 +03:00
|
|
|
|
2018-05-02 05:13:50 +03:00
|
|
|
*pwfx = reinterpret_cast<const WAVEFORMATEX*>(wf);
|
|
|
|
*pdata = ptr;
|
|
|
|
*dataSize = dataChunk->size;
|
|
|
|
return S_OK;
|
|
|
|
}
|
2016-10-29 02:02:21 +03:00
|
|
|
|
|
|
|
|
2018-05-02 05:13:50 +03:00
|
|
|
//---------------------------------------------------------------------------------
|
|
|
|
HRESULT WaveFindLoopInfo(
|
|
|
|
_In_reads_bytes_(wavDataSize) const uint8_t* wavData,
|
|
|
|
_In_ size_t wavDataSize,
|
|
|
|
_Out_ uint32_t* pLoopStart,
|
2019-12-11 05:33:53 +03:00
|
|
|
_Out_ uint32_t* pLoopLength) noexcept
|
2016-10-29 02:02:21 +03:00
|
|
|
{
|
2018-05-02 05:13:50 +03:00
|
|
|
if (!wavData || !pLoopStart || !pLoopLength)
|
|
|
|
return E_POINTER;
|
2016-10-29 02:02:21 +03:00
|
|
|
|
2018-05-02 05:13:50 +03:00
|
|
|
if (wavDataSize < (sizeof(RIFFChunk) + sizeof(uint32_t)))
|
|
|
|
{
|
|
|
|
return E_FAIL;
|
|
|
|
}
|
2016-10-29 02:02:21 +03:00
|
|
|
|
2018-05-02 05:13:50 +03:00
|
|
|
*pLoopStart = 0;
|
|
|
|
*pLoopLength = 0;
|
2016-10-29 02:02:21 +03:00
|
|
|
|
2018-05-02 05:13:50 +03:00
|
|
|
const uint8_t* wavEnd = wavData + wavDataSize;
|
2016-10-29 02:02:21 +03:00
|
|
|
|
2018-05-02 05:13:50 +03:00
|
|
|
// Locate RIFF 'WAVE'
|
|
|
|
auto riffChunk = FindChunk(wavData, wavDataSize, FOURCC_RIFF_TAG);
|
|
|
|
if (!riffChunk || riffChunk->size < 4)
|
|
|
|
{
|
|
|
|
return E_FAIL;
|
|
|
|
}
|
2016-10-29 02:02:21 +03:00
|
|
|
|
2018-05-02 05:13:50 +03:00
|
|
|
auto riffHeader = reinterpret_cast<const RIFFChunkHeader*>(riffChunk);
|
|
|
|
if (riffHeader->riff == FOURCC_XWMA_FILE_TAG)
|
|
|
|
{
|
|
|
|
// xWMA files do not contain loop information
|
|
|
|
return S_OK;
|
|
|
|
}
|
2016-10-29 02:02:21 +03:00
|
|
|
|
2018-05-02 05:13:50 +03:00
|
|
|
if (riffHeader->riff != FOURCC_WAVE_FILE_TAG)
|
|
|
|
{
|
|
|
|
return E_FAIL;
|
|
|
|
}
|
2016-10-29 02:02:21 +03:00
|
|
|
|
2018-05-02 05:13:50 +03:00
|
|
|
// Locate 'wsmp' (DLS Chunk)
|
|
|
|
auto ptr = reinterpret_cast<const uint8_t*>(riffHeader) + sizeof(RIFFChunkHeader);
|
|
|
|
if ((ptr + sizeof(RIFFChunk)) > wavEnd)
|
2016-10-29 02:02:21 +03:00
|
|
|
{
|
2018-03-16 22:03:02 +03:00
|
|
|
return HRESULT_FROM_WIN32(ERROR_HANDLE_EOF);
|
2016-10-29 02:02:21 +03:00
|
|
|
}
|
|
|
|
|
2018-05-02 05:13:50 +03:00
|
|
|
auto dlsChunk = FindChunk(ptr, riffChunk->size, FOURCC_DLS_SAMPLE);
|
|
|
|
if (dlsChunk)
|
2016-10-29 02:02:21 +03:00
|
|
|
{
|
2018-05-02 05:13:50 +03:00
|
|
|
ptr = reinterpret_cast<const uint8_t*>(dlsChunk) + sizeof(RIFFChunk);
|
|
|
|
if (ptr + dlsChunk->size > wavEnd)
|
|
|
|
{
|
|
|
|
return HRESULT_FROM_WIN32(ERROR_HANDLE_EOF);
|
|
|
|
}
|
2016-10-29 02:02:21 +03:00
|
|
|
|
2018-05-02 05:13:50 +03:00
|
|
|
if (dlsChunk->size >= sizeof(RIFFDLSSample))
|
2016-10-29 02:02:21 +03:00
|
|
|
{
|
2018-05-02 05:13:50 +03:00
|
|
|
auto dlsSample = reinterpret_cast<const RIFFDLSSample*>(ptr);
|
|
|
|
|
|
|
|
if (dlsChunk->size >= (dlsSample->size + dlsSample->loopCount * sizeof(DLSLoop)))
|
2016-10-29 02:02:21 +03:00
|
|
|
{
|
2018-05-02 05:13:50 +03:00
|
|
|
auto loops = reinterpret_cast<const DLSLoop*>(ptr + dlsSample->size);
|
|
|
|
for (uint32_t j = 0; j < dlsSample->loopCount; ++j)
|
2016-10-29 02:02:21 +03:00
|
|
|
{
|
2018-05-02 05:13:50 +03:00
|
|
|
if ((loops[j].loopType == DLSLoop::LOOP_TYPE_FORWARD || loops[j].loopType == DLSLoop::LOOP_TYPE_RELEASE))
|
|
|
|
{
|
|
|
|
// Return 'forward' loop
|
|
|
|
*pLoopStart = loops[j].loopStart;
|
|
|
|
*pLoopLength = loops[j].loopLength;
|
|
|
|
return S_OK;
|
|
|
|
}
|
2016-10-29 02:02:21 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-02 05:13:50 +03:00
|
|
|
// Locate 'smpl' (Sample Chunk)
|
|
|
|
auto midiChunk = FindChunk(ptr, riffChunk->size, FOURCC_MIDI_SAMPLE);
|
|
|
|
if (midiChunk)
|
2016-10-29 02:02:21 +03:00
|
|
|
{
|
2018-05-02 05:13:50 +03:00
|
|
|
ptr = reinterpret_cast<const uint8_t*>(midiChunk) + sizeof(RIFFChunk);
|
|
|
|
if (ptr + midiChunk->size > wavEnd)
|
|
|
|
{
|
|
|
|
return HRESULT_FROM_WIN32(ERROR_HANDLE_EOF);
|
|
|
|
}
|
2016-10-29 02:02:21 +03:00
|
|
|
|
2018-05-02 05:13:50 +03:00
|
|
|
if (midiChunk->size >= sizeof(RIFFMIDISample))
|
2016-10-29 02:02:21 +03:00
|
|
|
{
|
2018-05-02 05:13:50 +03:00
|
|
|
auto midiSample = reinterpret_cast<const RIFFMIDISample*>(ptr);
|
|
|
|
|
|
|
|
if (midiChunk->size >= (sizeof(RIFFMIDISample) + midiSample->loopCount * sizeof(MIDILoop)))
|
2016-10-29 02:02:21 +03:00
|
|
|
{
|
2018-05-02 05:13:50 +03:00
|
|
|
auto loops = reinterpret_cast<const MIDILoop*>(ptr + sizeof(RIFFMIDISample));
|
|
|
|
for (uint32_t j = 0; j < midiSample->loopCount; ++j)
|
2016-10-29 02:02:21 +03:00
|
|
|
{
|
2018-05-02 05:13:50 +03:00
|
|
|
if (loops[j].type == MIDILoop::LOOP_TYPE_FORWARD)
|
|
|
|
{
|
|
|
|
// Return 'forward' loop
|
|
|
|
*pLoopStart = loops[j].start;
|
2020-09-28 10:47:30 +03:00
|
|
|
*pLoopLength = loops[j].end - loops[j].start + 1;
|
2018-05-02 05:13:50 +03:00
|
|
|
return S_OK;
|
|
|
|
}
|
2016-10-29 02:02:21 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-02 05:13:50 +03:00
|
|
|
return S_OK;
|
|
|
|
}
|
2016-10-29 02:02:21 +03:00
|
|
|
|
|
|
|
|
2018-05-02 05:13:50 +03:00
|
|
|
//---------------------------------------------------------------------------------
|
|
|
|
HRESULT WaveFindTable(
|
|
|
|
_In_reads_bytes_(wavDataSize) const uint8_t* wavData,
|
|
|
|
_In_ size_t wavDataSize,
|
|
|
|
_In_ uint32_t tag,
|
|
|
|
_Outptr_result_maybenull_ const uint32_t** pData,
|
2019-12-11 05:33:53 +03:00
|
|
|
_Out_ uint32_t* dataCount) noexcept
|
2016-10-29 02:02:21 +03:00
|
|
|
{
|
2018-05-02 05:13:50 +03:00
|
|
|
if (!wavData || !pData || !dataCount)
|
|
|
|
return E_POINTER;
|
2016-10-29 02:02:21 +03:00
|
|
|
|
2018-05-02 05:13:50 +03:00
|
|
|
if (wavDataSize < (sizeof(RIFFChunk) + sizeof(uint32_t)))
|
|
|
|
{
|
|
|
|
return E_FAIL;
|
|
|
|
}
|
2016-10-29 02:02:21 +03:00
|
|
|
|
2018-05-02 05:13:50 +03:00
|
|
|
*pData = nullptr;
|
|
|
|
*dataCount = 0;
|
2016-10-29 02:02:21 +03:00
|
|
|
|
2018-05-02 05:13:50 +03:00
|
|
|
const uint8_t* wavEnd = wavData + wavDataSize;
|
2016-10-29 02:02:21 +03:00
|
|
|
|
2018-05-02 05:13:50 +03:00
|
|
|
// Locate RIFF 'WAVE'
|
|
|
|
auto riffChunk = FindChunk(wavData, wavDataSize, FOURCC_RIFF_TAG);
|
|
|
|
if (!riffChunk || riffChunk->size < 4)
|
|
|
|
{
|
|
|
|
return E_FAIL;
|
|
|
|
}
|
2016-10-29 02:02:21 +03:00
|
|
|
|
2018-05-02 05:13:50 +03:00
|
|
|
auto riffHeader = reinterpret_cast<const RIFFChunkHeader*>(riffChunk);
|
|
|
|
if (riffHeader->riff != FOURCC_WAVE_FILE_TAG && riffHeader->riff != FOURCC_XWMA_FILE_TAG)
|
|
|
|
{
|
|
|
|
return E_FAIL;
|
|
|
|
}
|
2016-10-29 02:02:21 +03:00
|
|
|
|
2018-05-02 05:13:50 +03:00
|
|
|
// Locate tag
|
|
|
|
auto ptr = reinterpret_cast<const uint8_t*>(riffHeader) + sizeof(RIFFChunkHeader);
|
|
|
|
if ((ptr + sizeof(RIFFChunk)) > wavEnd)
|
2016-10-29 02:02:21 +03:00
|
|
|
{
|
2018-03-16 22:03:02 +03:00
|
|
|
return HRESULT_FROM_WIN32(ERROR_HANDLE_EOF);
|
2016-10-29 02:02:21 +03:00
|
|
|
}
|
|
|
|
|
2018-05-02 05:13:50 +03:00
|
|
|
auto tableChunk = FindChunk(ptr, riffChunk->size, tag);
|
|
|
|
if (tableChunk)
|
2016-10-29 02:02:21 +03:00
|
|
|
{
|
2018-05-02 05:13:50 +03:00
|
|
|
ptr = reinterpret_cast<const uint8_t*>(tableChunk) + sizeof(RIFFChunk);
|
|
|
|
if (ptr + tableChunk->size > wavEnd)
|
|
|
|
{
|
|
|
|
return HRESULT_FROM_WIN32(ERROR_HANDLE_EOF);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((tableChunk->size % sizeof(uint32_t)) != 0)
|
|
|
|
{
|
|
|
|
return E_FAIL;
|
|
|
|
}
|
|
|
|
|
|
|
|
*pData = reinterpret_cast<const uint32_t*>(ptr);
|
|
|
|
*dataCount = tableChunk->size / 4;
|
2016-10-29 02:02:21 +03:00
|
|
|
}
|
|
|
|
|
2018-05-02 05:13:50 +03:00
|
|
|
return S_OK;
|
2016-10-29 02:02:21 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-05-02 05:13:50 +03:00
|
|
|
//---------------------------------------------------------------------------------
|
|
|
|
HRESULT LoadAudioFromFile(
|
|
|
|
_In_z_ const wchar_t* szFileName,
|
|
|
|
_Inout_ std::unique_ptr<uint8_t[]>& wavData,
|
2019-12-11 05:33:53 +03:00
|
|
|
_Out_ DWORD* bytesRead) noexcept
|
2018-05-02 05:13:50 +03:00
|
|
|
{
|
|
|
|
if (!szFileName)
|
|
|
|
return E_INVALIDARG;
|
|
|
|
|
|
|
|
// open the file
|
|
|
|
#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8)
|
|
|
|
ScopedHandle hFile(safe_handle(CreateFile2(szFileName,
|
|
|
|
GENERIC_READ,
|
|
|
|
FILE_SHARE_READ,
|
|
|
|
OPEN_EXISTING,
|
|
|
|
nullptr)));
|
|
|
|
#else
|
|
|
|
ScopedHandle hFile(safe_handle(CreateFileW(szFileName,
|
|
|
|
GENERIC_READ,
|
|
|
|
FILE_SHARE_READ,
|
|
|
|
nullptr,
|
|
|
|
OPEN_EXISTING,
|
|
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
|
|
nullptr)));
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (!hFile)
|
|
|
|
{
|
|
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
|
|
}
|
2016-10-29 02:02:21 +03:00
|
|
|
|
2018-05-02 05:13:50 +03:00
|
|
|
// Get the file size
|
|
|
|
FILE_STANDARD_INFO fileInfo;
|
|
|
|
if (!GetFileInformationByHandleEx(hFile.get(), FileStandardInfo, &fileInfo, sizeof(fileInfo)))
|
|
|
|
{
|
|
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
|
|
}
|
2016-10-29 02:02:21 +03:00
|
|
|
|
2018-05-02 05:13:50 +03:00
|
|
|
// File is too big for 32-bit allocation, so reject read
|
|
|
|
if (fileInfo.EndOfFile.HighPart > 0)
|
|
|
|
{
|
|
|
|
return E_FAIL;
|
|
|
|
}
|
2016-10-29 02:02:21 +03:00
|
|
|
|
2018-05-02 05:13:50 +03:00
|
|
|
// Need at least enough data to have a valid minimal WAV file
|
|
|
|
if (fileInfo.EndOfFile.LowPart < (sizeof(RIFFChunk) * 2 + sizeof(DWORD) + sizeof(WAVEFORMAT)))
|
|
|
|
{
|
|
|
|
return E_FAIL;
|
|
|
|
}
|
2016-10-29 02:02:21 +03:00
|
|
|
|
2018-05-02 05:13:50 +03:00
|
|
|
// create enough space for the file data
|
|
|
|
wavData.reset(new (std::nothrow) uint8_t[fileInfo.EndOfFile.LowPart]);
|
|
|
|
if (!wavData)
|
|
|
|
{
|
|
|
|
return E_OUTOFMEMORY;
|
|
|
|
}
|
2016-10-29 02:02:21 +03:00
|
|
|
|
2018-05-02 05:13:50 +03:00
|
|
|
// read the data in
|
|
|
|
if (!ReadFile(hFile.get(),
|
|
|
|
wavData.get(),
|
|
|
|
fileInfo.EndOfFile.LowPart,
|
|
|
|
bytesRead,
|
|
|
|
nullptr
|
|
|
|
))
|
|
|
|
{
|
|
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
|
|
}
|
2016-10-29 02:02:21 +03:00
|
|
|
|
2018-05-02 05:13:50 +03:00
|
|
|
return (*bytesRead < fileInfo.EndOfFile.LowPart) ? E_FAIL : S_OK;
|
2016-10-29 02:02:21 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-02 05:13:50 +03:00
|
|
|
//-------------------------------------------------------------------------------------
|
2016-10-29 02:02:21 +03:00
|
|
|
_Use_decl_annotations_
|
2018-05-02 05:13:50 +03:00
|
|
|
HRESULT DirectX::LoadWAVAudioInMemory(
|
|
|
|
const uint8_t* wavData,
|
|
|
|
size_t wavDataSize,
|
|
|
|
const WAVEFORMATEX** wfx,
|
|
|
|
const uint8_t** startAudio,
|
2019-12-11 05:33:53 +03:00
|
|
|
uint32_t* audioBytes) noexcept
|
2016-10-29 02:02:21 +03:00
|
|
|
{
|
2018-03-16 22:03:02 +03:00
|
|
|
if (!wavData || !wfx || !startAudio || !audioBytes)
|
2016-10-29 02:02:21 +03:00
|
|
|
return E_INVALIDARG;
|
|
|
|
|
|
|
|
*wfx = nullptr;
|
|
|
|
*startAudio = nullptr;
|
|
|
|
*audioBytes = 0;
|
|
|
|
|
|
|
|
// Need at least enough data to have a valid minimal WAV file
|
2018-03-16 22:03:02 +03:00
|
|
|
if (wavDataSize < (sizeof(RIFFChunk) * 2 + sizeof(DWORD) + sizeof(WAVEFORMAT)))
|
2016-10-29 02:02:21 +03:00
|
|
|
{
|
|
|
|
return E_FAIL;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool dpds, seek;
|
2018-03-16 22:03:02 +03:00
|
|
|
HRESULT hr = WaveFindFormatAndData(wavData, wavDataSize, wfx, startAudio, audioBytes, dpds, seek);
|
|
|
|
if (FAILED(hr))
|
2016-10-29 02:02:21 +03:00
|
|
|
return hr;
|
|
|
|
|
|
|
|
return (dpds || seek) ? E_FAIL : S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-05-02 05:13:50 +03:00
|
|
|
//-------------------------------------------------------------------------------------
|
2016-10-29 02:02:21 +03:00
|
|
|
_Use_decl_annotations_
|
2018-05-02 05:13:50 +03:00
|
|
|
HRESULT DirectX::LoadWAVAudioFromFile(
|
|
|
|
const wchar_t* szFileName,
|
|
|
|
std::unique_ptr<uint8_t[]>& wavData,
|
|
|
|
const WAVEFORMATEX** wfx,
|
|
|
|
const uint8_t** startAudio,
|
2019-12-11 05:33:53 +03:00
|
|
|
uint32_t* audioBytes) noexcept
|
2016-10-29 02:02:21 +03:00
|
|
|
{
|
2018-03-16 22:03:02 +03:00
|
|
|
if (!szFileName || !wfx || !startAudio || !audioBytes)
|
2016-10-29 02:02:21 +03:00
|
|
|
return E_INVALIDARG;
|
|
|
|
|
|
|
|
*wfx = nullptr;
|
|
|
|
*startAudio = nullptr;
|
|
|
|
*audioBytes = 0;
|
|
|
|
|
|
|
|
DWORD bytesRead = 0;
|
2018-03-16 22:03:02 +03:00
|
|
|
HRESULT hr = LoadAudioFromFile(szFileName, wavData, &bytesRead);
|
|
|
|
if (FAILED(hr))
|
2016-10-29 02:02:21 +03:00
|
|
|
{
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool dpds, seek;
|
2018-03-16 22:03:02 +03:00
|
|
|
hr = WaveFindFormatAndData(wavData.get(), bytesRead, wfx, startAudio, audioBytes, dpds, seek);
|
|
|
|
if (FAILED(hr))
|
2016-10-29 02:02:21 +03:00
|
|
|
return hr;
|
|
|
|
|
|
|
|
return (dpds || seek) ? E_FAIL : S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-05-02 05:13:50 +03:00
|
|
|
//-------------------------------------------------------------------------------------
|
2016-10-29 02:02:21 +03:00
|
|
|
_Use_decl_annotations_
|
2018-05-02 05:13:50 +03:00
|
|
|
HRESULT DirectX::LoadWAVAudioInMemoryEx(
|
|
|
|
const uint8_t* wavData,
|
|
|
|
size_t wavDataSize,
|
2019-12-11 05:33:53 +03:00
|
|
|
DirectX::WAVData& result) noexcept
|
2016-10-29 02:02:21 +03:00
|
|
|
{
|
2018-03-16 22:03:02 +03:00
|
|
|
if (!wavData)
|
2016-10-29 02:02:21 +03:00
|
|
|
return E_INVALIDARG;
|
|
|
|
|
2018-03-16 22:03:02 +03:00
|
|
|
memset(&result, 0, sizeof(result));
|
2016-10-29 02:02:21 +03:00
|
|
|
|
|
|
|
// Need at least enough data to have a valid minimal WAV file
|
2018-03-16 22:03:02 +03:00
|
|
|
if (wavDataSize < (sizeof(RIFFChunk) * 2 + sizeof(DWORD) + sizeof(WAVEFORMAT)))
|
2016-10-29 02:02:21 +03:00
|
|
|
{
|
|
|
|
return E_FAIL;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool dpds, seek;
|
2018-03-16 22:03:02 +03:00
|
|
|
HRESULT hr = WaveFindFormatAndData(wavData, wavDataSize, &result.wfx, &result.startAudio, &result.audioBytes, dpds, seek);
|
|
|
|
if (FAILED(hr))
|
2016-10-29 02:02:21 +03:00
|
|
|
return hr;
|
|
|
|
|
2018-03-16 22:03:02 +03:00
|
|
|
hr = WaveFindLoopInfo(wavData, wavDataSize, &result.loopStart, &result.loopLength);
|
|
|
|
if (FAILED(hr))
|
2016-10-29 02:02:21 +03:00
|
|
|
return hr;
|
|
|
|
|
2018-03-16 22:03:02 +03:00
|
|
|
if (dpds)
|
2016-10-29 02:02:21 +03:00
|
|
|
{
|
2018-03-16 22:03:02 +03:00
|
|
|
hr = WaveFindTable(wavData, wavDataSize, FOURCC_XWMA_DPDS, &result.seek, &result.seekCount);
|
|
|
|
if (FAILED(hr))
|
2016-10-29 02:02:21 +03:00
|
|
|
return hr;
|
|
|
|
}
|
2018-03-16 22:03:02 +03:00
|
|
|
else if (seek)
|
2016-10-29 02:02:21 +03:00
|
|
|
{
|
2018-03-16 22:03:02 +03:00
|
|
|
hr = WaveFindTable(wavData, wavDataSize, FOURCC_XMA_SEEK, &result.seek, &result.seekCount);
|
|
|
|
if (FAILED(hr))
|
2016-10-29 02:02:21 +03:00
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-05-02 05:13:50 +03:00
|
|
|
//-------------------------------------------------------------------------------------
|
2016-10-29 02:02:21 +03:00
|
|
|
_Use_decl_annotations_
|
2018-05-02 05:13:50 +03:00
|
|
|
HRESULT DirectX::LoadWAVAudioFromFileEx(
|
|
|
|
const wchar_t* szFileName,
|
|
|
|
std::unique_ptr<uint8_t[]>& wavData,
|
2019-12-11 05:33:53 +03:00
|
|
|
DirectX::WAVData& result) noexcept
|
2016-10-29 02:02:21 +03:00
|
|
|
{
|
2018-03-16 22:03:02 +03:00
|
|
|
if (!szFileName)
|
2016-10-29 02:02:21 +03:00
|
|
|
return E_INVALIDARG;
|
|
|
|
|
2018-03-16 22:03:02 +03:00
|
|
|
memset(&result, 0, sizeof(result));
|
2016-10-29 02:02:21 +03:00
|
|
|
|
|
|
|
DWORD bytesRead = 0;
|
2018-03-16 22:03:02 +03:00
|
|
|
HRESULT hr = LoadAudioFromFile(szFileName, wavData, &bytesRead);
|
|
|
|
if (FAILED(hr))
|
2016-10-29 02:02:21 +03:00
|
|
|
{
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool dpds, seek;
|
2018-03-16 22:03:02 +03:00
|
|
|
hr = WaveFindFormatAndData(wavData.get(), bytesRead, &result.wfx, &result.startAudio, &result.audioBytes, dpds, seek);
|
|
|
|
if (FAILED(hr))
|
2016-10-29 02:02:21 +03:00
|
|
|
return hr;
|
|
|
|
|
2018-03-16 22:03:02 +03:00
|
|
|
hr = WaveFindLoopInfo(wavData.get(), bytesRead, &result.loopStart, &result.loopLength);
|
|
|
|
if (FAILED(hr))
|
2016-10-29 02:02:21 +03:00
|
|
|
return hr;
|
|
|
|
|
2018-03-16 22:03:02 +03:00
|
|
|
if (dpds)
|
2016-10-29 02:02:21 +03:00
|
|
|
{
|
2018-03-16 22:03:02 +03:00
|
|
|
hr = WaveFindTable(wavData.get(), bytesRead, FOURCC_XWMA_DPDS, &result.seek, &result.seekCount);
|
|
|
|
if (FAILED(hr))
|
2016-10-29 02:02:21 +03:00
|
|
|
return hr;
|
|
|
|
}
|
2018-03-16 22:03:02 +03:00
|
|
|
else if (seek)
|
2016-10-29 02:02:21 +03:00
|
|
|
{
|
2018-03-16 22:03:02 +03:00
|
|
|
hr = WaveFindTable(wavData.get(), bytesRead, FOURCC_XMA_SEEK, &result.seek, &result.seekCount);
|
|
|
|
if (FAILED(hr))
|
2016-10-29 02:02:21 +03:00
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
return S_OK;
|
|
|
|
}
|