mirror of
https://github.com/cemu-project/Cemu.git
synced 2025-07-07 07:21:18 +12:00
Add all the files
This commit is contained in:
parent
e3db07a16a
commit
d60742f52b
1445 changed files with 430238 additions and 0 deletions
42
src/audio/CMakeLists.txt
Normal file
42
src/audio/CMakeLists.txt
Normal file
|
@ -0,0 +1,42 @@
|
|||
project(CemuAudio)
|
||||
|
||||
add_library(CemuAudio
|
||||
IAudioAPI.cpp
|
||||
IAudioAPI.h
|
||||
)
|
||||
|
||||
set_property(TARGET CemuAudio PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
|
||||
|
||||
# move these to UI folder
|
||||
target_sources(CemuAudio PRIVATE
|
||||
audioDebuggerWindow.cpp
|
||||
audioDebuggerWindow.h
|
||||
)
|
||||
|
||||
if(WIN32)
|
||||
target_sources(CemuAudio PRIVATE
|
||||
DirectSoundAPI.cpp
|
||||
DirectSoundAPI.h
|
||||
XAudio2API.cpp
|
||||
XAudio2API.h
|
||||
XAudio27API.cpp
|
||||
XAudio27API.h
|
||||
)
|
||||
endif()
|
||||
|
||||
if(ENABLE_CUBEB)
|
||||
target_sources(CemuAudio PRIVATE
|
||||
CubebAPI.cpp
|
||||
CubebAPI.h
|
||||
)
|
||||
#add_definitions(HAS_CUBEB)
|
||||
endif()
|
||||
|
||||
target_precompile_headers(CemuAudio PRIVATE ../Common/precompiled.h)
|
||||
|
||||
#target_link_libraries(CemuAudio CemuCommon CemuGui CemuPlatform)
|
||||
target_include_directories(CemuAudio PRIVATE ../)
|
||||
|
||||
if(ENABLE_CUBEB)
|
||||
target_link_libraries(CemuAudio cubeb)
|
||||
endif()
|
225
src/audio/CubebAPI.cpp
Normal file
225
src/audio/CubebAPI.cpp
Normal file
|
@ -0,0 +1,225 @@
|
|||
#include "CubebAPI.h"
|
||||
|
||||
#if BOOST_OS_WINDOWS
|
||||
#include <combaseapi.h>
|
||||
#include <mmreg.h>
|
||||
#include <mmsystem.h>
|
||||
#pragma comment(lib, "Avrt.lib")
|
||||
#pragma comment(lib, "ksuser.lib")
|
||||
#endif
|
||||
|
||||
|
||||
void state_cb(cubeb_stream* stream, void* user, cubeb_state state)
|
||||
{
|
||||
if (!stream)
|
||||
return;
|
||||
|
||||
/*switch (state)
|
||||
{
|
||||
case CUBEB_STATE_STARTED:
|
||||
fprintf(stderr, "stream started\n");
|
||||
break;
|
||||
case CUBEB_STATE_STOPPED:
|
||||
fprintf(stderr, "stream stopped\n");
|
||||
break;
|
||||
case CUBEB_STATE_DRAINED:
|
||||
fprintf(stderr, "stream drained\n");
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "unknown stream state %d\n", state);
|
||||
}*/
|
||||
}
|
||||
|
||||
long CubebAPI::data_cb(cubeb_stream* stream, void* user, const void* inputbuffer, void* outputbuffer, long nframes)
|
||||
{
|
||||
auto* thisptr = (CubebAPI*)user;
|
||||
//const auto size = (size_t)thisptr->m_bytesPerBlock; // (size_t)nframes* thisptr->m_channels;
|
||||
|
||||
// m_bytesPerBlock = samples_per_block * channels * (bits_per_sample / 8);
|
||||
const auto size = (size_t)nframes * thisptr->m_channels * (thisptr->m_bitsPerSample/8);
|
||||
|
||||
std::unique_lock lock(thisptr->m_mutex);
|
||||
if (thisptr->m_buffer.empty())
|
||||
{
|
||||
// we got no data, just write silence
|
||||
memset(outputbuffer, 0x00, size);
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto copied = std::min(thisptr->m_buffer.size(), size);
|
||||
memcpy(outputbuffer, thisptr->m_buffer.data(), copied);
|
||||
thisptr->m_buffer.erase(thisptr->m_buffer.begin(), std::next(thisptr->m_buffer.begin(), copied));
|
||||
lock.unlock();
|
||||
// fill rest with silence
|
||||
if (copied != size)
|
||||
memset((uint8*)outputbuffer + copied, 0x00, size - copied);
|
||||
}
|
||||
|
||||
return nframes;
|
||||
}
|
||||
|
||||
CubebAPI::CubebAPI(cubeb_devid devid, uint32 samplerate, uint32 channels, uint32 samples_per_block,
|
||||
uint32 bits_per_sample)
|
||||
: IAudioAPI(samplerate, channels, samples_per_block, bits_per_sample)
|
||||
{
|
||||
cubeb_stream_params output_params;
|
||||
|
||||
output_params.format = CUBEB_SAMPLE_S16LE;
|
||||
output_params.rate = samplerate;
|
||||
output_params.channels = channels;
|
||||
output_params.prefs = CUBEB_STREAM_PREF_NONE;
|
||||
|
||||
switch (channels)
|
||||
{
|
||||
case 8:
|
||||
output_params.layout = CUBEB_LAYOUT_3F4_LFE;
|
||||
break;
|
||||
case 6:
|
||||
output_params.layout = CUBEB_LAYOUT_QUAD_LFE | CHANNEL_FRONT_CENTER;
|
||||
break;
|
||||
case 4:
|
||||
output_params.layout = CUBEB_LAYOUT_QUAD;
|
||||
break;
|
||||
case 2:
|
||||
output_params.layout = CUBEB_LAYOUT_STEREO;
|
||||
break;
|
||||
default:
|
||||
output_params.layout = CUBEB_LAYOUT_MONO;
|
||||
break;
|
||||
}
|
||||
|
||||
uint32 latency = 1;
|
||||
cubeb_get_min_latency(s_context, &output_params, &latency);
|
||||
|
||||
m_buffer.reserve((size_t)m_bytesPerBlock * kBlockCount);
|
||||
|
||||
if (cubeb_stream_init(s_context, &m_stream, "Cemu Cubeb output",
|
||||
nullptr, nullptr,
|
||||
devid, &output_params,
|
||||
latency, data_cb, state_cb, this) != CUBEB_OK)
|
||||
{
|
||||
throw std::runtime_error("can't initialize cubeb device");
|
||||
}
|
||||
}
|
||||
|
||||
CubebAPI::~CubebAPI()
|
||||
{
|
||||
if (m_stream)
|
||||
{
|
||||
Stop();
|
||||
cubeb_stream_destroy(m_stream);
|
||||
}
|
||||
}
|
||||
|
||||
bool CubebAPI::NeedAdditionalBlocks() const
|
||||
{
|
||||
std::shared_lock lock(m_mutex);
|
||||
return m_buffer.size() < s_audioDelay * m_bytesPerBlock;
|
||||
}
|
||||
|
||||
bool CubebAPI::FeedBlock(sint16* data)
|
||||
{
|
||||
std::unique_lock lock(m_mutex);
|
||||
if (m_buffer.capacity() <= m_buffer.size() + m_bytesPerBlock)
|
||||
{
|
||||
forceLogDebug_printf("dropped direct sound block since too many buffers are queued");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_buffer.insert(m_buffer.end(), (uint8*)data, (uint8*)data + m_bytesPerBlock);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CubebAPI::Play()
|
||||
{
|
||||
if (m_is_playing)
|
||||
return true;
|
||||
|
||||
if (cubeb_stream_start(m_stream) == CUBEB_OK)
|
||||
{
|
||||
m_is_playing = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CubebAPI::Stop()
|
||||
{
|
||||
if (!m_is_playing)
|
||||
return true;
|
||||
|
||||
if (cubeb_stream_stop(m_stream) == CUBEB_OK)
|
||||
{
|
||||
m_is_playing = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void CubebAPI::SetVolume(sint32 volume)
|
||||
{
|
||||
IAudioAPI::SetVolume(volume);
|
||||
cubeb_stream_set_volume(m_stream, (float)volume / 100.0f);
|
||||
}
|
||||
|
||||
|
||||
bool CubebAPI::InitializeStatic()
|
||||
{
|
||||
#if BOOST_OS_WINDOWS
|
||||
s_com_initialized = (SUCCEEDED(CoInitializeEx(nullptr, COINIT_MULTITHREADED)));
|
||||
#endif
|
||||
|
||||
if (cubeb_init(&s_context, "Cemu Cubeb", nullptr))
|
||||
{
|
||||
cemuLog_force("can't create cubeb audio api");
|
||||
|
||||
#if BOOST_OS_WINDOWS
|
||||
if (s_com_initialized)
|
||||
{
|
||||
CoUninitialize();
|
||||
s_com_initialized = false;
|
||||
}
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CubebAPI::Destroy()
|
||||
{
|
||||
if (s_context)
|
||||
cubeb_destroy(s_context);
|
||||
#if BOOST_OS_WINDOWS
|
||||
if (s_com_initialized)
|
||||
CoUninitialize();
|
||||
#endif
|
||||
}
|
||||
|
||||
std::vector<IAudioAPI::DeviceDescriptionPtr> CubebAPI::GetDevices()
|
||||
{
|
||||
cubeb_device_collection devices;
|
||||
if (cubeb_enumerate_devices(s_context, CUBEB_DEVICE_TYPE_OUTPUT, &devices) != CUBEB_OK)
|
||||
return {};
|
||||
|
||||
std::vector<DeviceDescriptionPtr> result;
|
||||
result.reserve(devices.count);
|
||||
for (size_t i = 0; i < devices.count; ++i)
|
||||
{
|
||||
//const auto& device = devices.device[i];
|
||||
if (devices.device[i].state == CUBEB_DEVICE_STATE_ENABLED)
|
||||
{
|
||||
auto device = std::make_shared<CubebDeviceDescription>(devices.device[i].devid, devices.device[i].device_id,
|
||||
boost::nowide::widen(
|
||||
devices.device[i].friendly_name));
|
||||
result.emplace_back(device);
|
||||
}
|
||||
}
|
||||
|
||||
cubeb_device_collection_destroy(s_context, &devices);
|
||||
|
||||
return result;
|
||||
}
|
53
src/audio/CubebAPI.h
Normal file
53
src/audio/CubebAPI.h
Normal file
|
@ -0,0 +1,53 @@
|
|||
#pragma once
|
||||
|
||||
#include "IAudioAPI.h"
|
||||
|
||||
#include <cubeb/cubeb.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
class CubebAPI : public IAudioAPI
|
||||
{
|
||||
public:
|
||||
class CubebDeviceDescription : public DeviceDescription
|
||||
{
|
||||
public:
|
||||
CubebDeviceDescription(cubeb_devid devid, std::string device_id, const std::wstring& name)
|
||||
: DeviceDescription(name), m_devid(devid), m_device_id(std::move(device_id)) { }
|
||||
|
||||
std::wstring GetIdentifier() const override { return boost::nowide::widen(m_device_id); }
|
||||
cubeb_devid GetDeviceId() const { return m_devid; }
|
||||
|
||||
private:
|
||||
cubeb_devid m_devid;
|
||||
std::string m_device_id;
|
||||
};
|
||||
|
||||
using CubebDeviceDescriptionPtr = std::shared_ptr<CubebDeviceDescription>;
|
||||
|
||||
CubebAPI(cubeb_devid devid, uint32 samplerate, uint32 channels, uint32 samples_per_block, uint32 bits_per_sample);
|
||||
~CubebAPI();
|
||||
|
||||
AudioAPI GetType() const override { return Cubeb; }
|
||||
bool NeedAdditionalBlocks() const override;
|
||||
bool FeedBlock(sint16* data) override;
|
||||
bool Play() override;
|
||||
bool Stop() override;
|
||||
void SetVolume(sint32 volume) override;
|
||||
|
||||
static std::vector<DeviceDescriptionPtr> GetDevices();
|
||||
|
||||
static bool InitializeStatic();
|
||||
static void Destroy();
|
||||
|
||||
private:
|
||||
inline static bool s_com_initialized = false;
|
||||
inline static cubeb* s_context = nullptr;
|
||||
|
||||
cubeb_stream* m_stream = nullptr;
|
||||
bool m_is_playing = false;
|
||||
|
||||
mutable std::shared_mutex m_mutex;
|
||||
std::vector<uint8> m_buffer;
|
||||
static long data_cb(cubeb_stream* stream, void* user, const void* inputbuffer, void* outputbuffer, long nframes);
|
||||
};
|
246
src/audio/DirectSoundAPI.cpp
Normal file
246
src/audio/DirectSoundAPI.cpp
Normal file
|
@ -0,0 +1,246 @@
|
|||
#include "DirectSoundAPI.h"
|
||||
|
||||
#include "gui/wxgui.h"
|
||||
|
||||
#include "util/helpers/helpers.h"
|
||||
#include "gui/guiWrapper.h"
|
||||
|
||||
#pragma comment(lib, "Dsound.lib")
|
||||
|
||||
std::wstring DirectSoundAPI::DirectSoundDeviceDescription::GetIdentifier() const
|
||||
{
|
||||
return m_guid ? WStringFromGUID(*m_guid) : L"default";
|
||||
}
|
||||
|
||||
DirectSoundAPI::DirectSoundAPI(GUID* guid, sint32 samplerate, sint32 channels, sint32 samples_per_block, sint32 bits_per_sample)
|
||||
: IAudioAPI(samplerate, channels, samples_per_block, bits_per_sample)
|
||||
{
|
||||
LPDIRECTSOUND8 direct_sound;
|
||||
if (DirectSoundCreate8(guid, &direct_sound, nullptr) != DS_OK)
|
||||
throw std::runtime_error("can't create directsound device");
|
||||
|
||||
m_direct_sound = decltype(m_direct_sound)(direct_sound);
|
||||
|
||||
if (FAILED(m_direct_sound->SetCooperativeLevel(gui_getWindowInfo().window_main.hwnd, DSSCL_PRIORITY)))
|
||||
throw std::runtime_error("can't set directsound priority");
|
||||
|
||||
DSBUFFERDESC bd{};
|
||||
bd.dwSize = sizeof(DSBUFFERDESC);
|
||||
bd.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_CTRLVOLUME | DSBCAPS_GLOBALFOCUS | DSBCAPS_CTRLPOSITIONNOTIFY;
|
||||
bd.dwBufferBytes = kBufferCount * m_bytesPerBlock; // kBlockCount * (samples_per_block * channels * (bits_per_sample / 8));
|
||||
bd.lpwfxFormat = (LPWAVEFORMATEX)&m_wfx;
|
||||
|
||||
LPDIRECTSOUNDBUFFER sound_buffer;
|
||||
if (FAILED(m_direct_sound->CreateSoundBuffer(&bd, &sound_buffer, nullptr)))
|
||||
throw std::runtime_error("can't create directsound soundbuffer");
|
||||
|
||||
DSBCAPS caps{};
|
||||
caps.dwSize = sizeof(DSBCAPS);
|
||||
if (FAILED(sound_buffer->GetCaps(&caps)))
|
||||
throw std::runtime_error("can't get directsound soundbuffer size");
|
||||
|
||||
m_sound_buffer_size = caps.dwBufferBytes;
|
||||
|
||||
LPDIRECTSOUNDBUFFER8 sound_buffer8;
|
||||
LPDIRECTSOUNDNOTIFY8 notify8;
|
||||
sound_buffer->QueryInterface(IID_IDirectSoundBuffer8, (void**)&sound_buffer8);
|
||||
|
||||
if (!sound_buffer8)
|
||||
{
|
||||
sound_buffer->Release();
|
||||
throw std::runtime_error("can't get directsound buffer interface");
|
||||
}
|
||||
|
||||
m_sound_buffer = decltype(m_sound_buffer)(sound_buffer8);
|
||||
|
||||
sound_buffer->QueryInterface(IID_IDirectSoundNotify8, (void**)¬ify8);
|
||||
if (!notify8)
|
||||
{
|
||||
sound_buffer->Release();
|
||||
throw std::runtime_error("can't get directsound notify interface");
|
||||
}
|
||||
m_notify = decltype(m_notify)(notify8);
|
||||
|
||||
sound_buffer->Release();
|
||||
|
||||
{ // initialize sound buffer
|
||||
void *ptr1, *ptr2;
|
||||
DWORD bytes1, bytes2;
|
||||
m_sound_buffer->Lock(0, m_sound_buffer_size, &ptr1, &bytes1, &ptr2, &bytes2, 0);
|
||||
memset(ptr1, 0x00, bytes1);
|
||||
if (ptr2 && bytes2 > 0)
|
||||
memset(ptr2, 0x00, bytes2);
|
||||
m_sound_buffer->Unlock(ptr1, bytes1, ptr2, bytes2);
|
||||
}
|
||||
|
||||
m_sound_buffer->SetCurrentPosition(0);
|
||||
|
||||
DSBPOSITIONNOTIFY notify[kBufferCount]{};
|
||||
for (size_t i = 0; i < kBufferCount; ++i)
|
||||
{
|
||||
m_notify_event[i] = CreateEvent(nullptr, FALSE, FALSE, nullptr);
|
||||
|
||||
notify[i].hEventNotify = m_notify_event[i];
|
||||
//notify[i].dwOffset = ((i*2) + 1) * (m_bytes_per_block / 2);
|
||||
notify[i].dwOffset = (i * m_bytesPerBlock);
|
||||
}
|
||||
|
||||
if (FAILED(m_notify->SetNotificationPositions(kBufferCount, notify)))
|
||||
throw std::runtime_error("can't set directsound notify positions");
|
||||
|
||||
m_running = true;
|
||||
m_thread = std::thread(&DirectSoundAPI::AudioThread, this);
|
||||
#if BOOST_OS_WINDOWS
|
||||
SetThreadPriority(m_thread.native_handle(), THREAD_PRIORITY_TIME_CRITICAL);
|
||||
#endif
|
||||
}
|
||||
|
||||
void DirectSoundAPI::AudioThread()
|
||||
{
|
||||
while (m_running)
|
||||
{
|
||||
HRESULT hr = WaitForMultipleObjects(m_notify_event.size(), m_notify_event.data(), FALSE, 10);
|
||||
if (WAIT_OBJECT_0 <= hr && hr <= WAIT_OBJECT_0 + kBufferCount)
|
||||
{
|
||||
// write to the following buffer
|
||||
const sint32 position = (hr - WAIT_OBJECT_0 + 1) % kBufferCount;
|
||||
|
||||
void *ptr1, *ptr2;
|
||||
DWORD bytes1, bytes2;
|
||||
hr = m_sound_buffer->Lock(position * m_bytesPerBlock, m_bytesPerBlock, &ptr1, &bytes1, &ptr2, &bytes2, 0);
|
||||
if (hr == DSERR_BUFFERLOST)
|
||||
{
|
||||
m_sound_buffer->Restore();
|
||||
hr = m_sound_buffer->Lock(position * m_bytesPerBlock, m_bytesPerBlock, &ptr1, &bytes1, &ptr2, &bytes2, 0);
|
||||
}
|
||||
|
||||
if (FAILED(hr))
|
||||
{
|
||||
forceLog_printf("DirectSound: Dropped audio block due to locking failure");
|
||||
continue;
|
||||
}
|
||||
|
||||
{
|
||||
std::unique_lock lock(m_mutex);
|
||||
if (m_buffer.empty())
|
||||
{
|
||||
//forceLogDebug_printf("DirectSound: writing silence");
|
||||
|
||||
// we got no data, just write silence
|
||||
memset(ptr1, 0x00, bytes1);
|
||||
if (ptr2)
|
||||
memset(ptr2, 0x00, bytes2);
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto& buffer = m_buffer.front();
|
||||
memcpy(ptr1, buffer.get(), bytes1);
|
||||
if (ptr2)
|
||||
memcpy(ptr2, buffer.get() + bytes1, bytes2);
|
||||
|
||||
m_buffer.pop();
|
||||
}
|
||||
}
|
||||
|
||||
m_sound_buffer->Unlock(ptr1, bytes1, ptr2, bytes2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DirectSoundAPI::~DirectSoundAPI()
|
||||
{
|
||||
m_running = false;
|
||||
DirectSoundAPI::Stop();
|
||||
|
||||
if(m_thread.joinable())
|
||||
m_thread.join();
|
||||
|
||||
m_notify.reset();
|
||||
m_sound_buffer.reset();
|
||||
m_direct_sound.reset();
|
||||
|
||||
for(auto entry : m_notify_event)
|
||||
{
|
||||
if (entry)
|
||||
CloseHandle(entry);
|
||||
}
|
||||
}
|
||||
|
||||
bool DirectSoundAPI::Play()
|
||||
{
|
||||
if (m_playing)
|
||||
return true;
|
||||
|
||||
m_playing = SUCCEEDED(m_sound_buffer->Play(0, 0, DSBPLAY_LOOPING));
|
||||
return m_playing;
|
||||
}
|
||||
|
||||
bool DirectSoundAPI::Stop()
|
||||
{
|
||||
if (!m_playing)
|
||||
return true;
|
||||
|
||||
m_playing = FAILED(m_sound_buffer->Stop());
|
||||
return m_playing;
|
||||
}
|
||||
|
||||
bool DirectSoundAPI::FeedBlock(sint16* data)
|
||||
{
|
||||
std::unique_lock lock(m_mutex);
|
||||
if (m_buffer.size() > kBlockCount)
|
||||
{
|
||||
forceLogDebug_printf("dropped direct sound block since too many buffers are queued");
|
||||
return false;
|
||||
}
|
||||
|
||||
auto tmp = std::make_unique<uint8[]>(m_bytesPerBlock);
|
||||
memcpy(tmp.get(), data, m_bytesPerBlock);
|
||||
m_buffer.emplace(std::move(tmp));
|
||||
return true;
|
||||
}
|
||||
|
||||
void DirectSoundAPI::SetVolume(sint32 volume)
|
||||
{
|
||||
IAudioAPI::SetVolume(volume);
|
||||
|
||||
const LONG value = pow((float)volume / 100.0f, 0.20f) * (DSBVOLUME_MAX - DSBVOLUME_MIN) + DSBVOLUME_MIN;
|
||||
m_sound_buffer->SetVolume(value);
|
||||
}
|
||||
|
||||
bool DirectSoundAPI::NeedAdditionalBlocks() const
|
||||
{
|
||||
std::shared_lock lock(m_mutex);
|
||||
return m_buffer.size() < s_audioDelay;
|
||||
}
|
||||
|
||||
std::vector<DirectSoundAPI::DeviceDescriptionPtr> DirectSoundAPI::GetDevices()
|
||||
{
|
||||
std::vector<DeviceDescriptionPtr> result;
|
||||
|
||||
DirectSoundEnumerateW(
|
||||
[](LPGUID lpGuid, LPCWSTR lpcstrDescription, LPCWSTR lpcstrModule, LPVOID lpContext) -> BOOL
|
||||
{
|
||||
auto results = (std::vector<DeviceDescriptionPtr>*)lpContext;
|
||||
auto obj = std::make_shared<DirectSoundDeviceDescription>(lpcstrDescription, lpGuid);
|
||||
results->emplace_back(obj);
|
||||
return TRUE;
|
||||
}, &result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<DirectSoundAPI::DeviceDescriptionPtr> DirectSoundAPI::GetInputDevices()
|
||||
{
|
||||
std::vector<DeviceDescriptionPtr> result;
|
||||
|
||||
DirectSoundCaptureEnumerateW(
|
||||
[](LPGUID lpGuid, LPCWSTR lpcstrDescription, LPCWSTR lpcstrModule, LPVOID lpContext) -> BOOL
|
||||
{
|
||||
auto results = (std::vector<DirectSoundDeviceDescriptionPtr>*)lpContext;
|
||||
auto obj = std::make_shared<DirectSoundDeviceDescription>(lpcstrDescription, lpGuid);
|
||||
results->emplace_back(obj);
|
||||
return TRUE;
|
||||
}, &result);
|
||||
|
||||
return result;
|
||||
}
|
66
src/audio/DirectSoundAPI.h
Normal file
66
src/audio/DirectSoundAPI.h
Normal file
|
@ -0,0 +1,66 @@
|
|||
#pragma once
|
||||
|
||||
#define DIRECTSOUND_VERSION 0x0800
|
||||
#include <mmsystem.h>
|
||||
//#include <mmreg.h>
|
||||
#include <dsound.h>
|
||||
|
||||
#include "IAudioAPI.h"
|
||||
|
||||
class DirectSoundAPI : public IAudioAPI
|
||||
{
|
||||
public:
|
||||
class DirectSoundDeviceDescription : public DeviceDescription
|
||||
{
|
||||
public:
|
||||
DirectSoundDeviceDescription(const std::wstring& name, GUID* guid)
|
||||
: DeviceDescription(name), m_guid(guid) { }
|
||||
|
||||
std::wstring GetIdentifier() const override;
|
||||
GUID* GetGUID() const { return m_guid; }
|
||||
|
||||
private:
|
||||
GUID* m_guid;
|
||||
};
|
||||
|
||||
using DirectSoundDeviceDescriptionPtr = std::shared_ptr<DirectSoundDeviceDescription>;
|
||||
|
||||
// output
|
||||
DirectSoundAPI(GUID* guid, sint32 samplerate, sint32 channels, sint32 samples_per_block, sint32 bits_per_sample);
|
||||
~DirectSoundAPI();
|
||||
|
||||
AudioAPI GetType() const override { return DirectSound; }
|
||||
|
||||
bool Play() override;
|
||||
bool Stop() override;
|
||||
bool FeedBlock(sint16* data) override;
|
||||
void SetVolume(sint32 volume) override;
|
||||
bool NeedAdditionalBlocks() const override;
|
||||
|
||||
static std::vector<DeviceDescriptionPtr> GetDevices();
|
||||
static std::vector<DeviceDescriptionPtr> GetInputDevices();
|
||||
|
||||
private:
|
||||
struct DirectSoundDeleter
|
||||
{
|
||||
void operator()(IUnknown* ptr) const { if (ptr) ptr->Release(); }
|
||||
};
|
||||
|
||||
std::unique_ptr<IDirectSound8, DirectSoundDeleter> m_direct_sound;
|
||||
//std::unique_ptr<IDirectSoundCapture8, DirectSoundDeleter> m_direct_sound_capture;
|
||||
std::unique_ptr<IDirectSoundBuffer8, DirectSoundDeleter> m_sound_buffer;
|
||||
std::unique_ptr<IDirectSoundNotify8, DirectSoundDeleter> m_notify;
|
||||
|
||||
DWORD m_sound_buffer_size = 0;
|
||||
uint32_t m_offset = 0;
|
||||
bool m_data_written = false;
|
||||
|
||||
static const uint32 kBufferCount = 4;
|
||||
std::array<HANDLE, kBufferCount> m_notify_event{};
|
||||
mutable std::shared_mutex m_mutex;
|
||||
|
||||
std::queue<std::unique_ptr<uint8[]>> m_buffer;
|
||||
std::thread m_thread;
|
||||
std::atomic_bool m_running = false;
|
||||
void AudioThread();
|
||||
};
|
160
src/audio/IAudioAPI.cpp
Normal file
160
src/audio/IAudioAPI.cpp
Normal file
|
@ -0,0 +1,160 @@
|
|||
#include "IAudioAPI.h"
|
||||
|
||||
#if BOOST_OS_WINDOWS > 0
|
||||
#include "XAudio2API.h"
|
||||
#include "XAudio27API.h"
|
||||
#include "DirectSoundAPI.h"
|
||||
#endif
|
||||
#include "config/CemuConfig.h"
|
||||
#include "CubebAPI.h"
|
||||
|
||||
std::shared_mutex g_audioMutex;
|
||||
AudioAPIPtr g_tvAudio;
|
||||
AudioAPIPtr g_padAudio;
|
||||
std::atomic_int32_t g_padVolume = 0;
|
||||
|
||||
uint32 IAudioAPI::s_audioDelay = 2;
|
||||
std::array<bool, IAudioAPI::AudioAPIEnd> IAudioAPI::s_availableApis{};
|
||||
|
||||
IAudioAPI::IAudioAPI(uint32 samplerate, uint32 channels, uint32 samples_per_block, uint32 bits_per_sample)
|
||||
: m_samplerate(samplerate), m_channels(channels), m_samplesPerBlock(samples_per_block), m_bitsPerSample(bits_per_sample)
|
||||
{
|
||||
m_bytesPerBlock = samples_per_block * channels * (bits_per_sample / 8);
|
||||
InitWFX(m_samplerate, m_channels, m_bitsPerSample);
|
||||
}
|
||||
|
||||
void IAudioAPI::PrintLogging()
|
||||
{
|
||||
forceLog_printf("------- Init Audio backend -------");
|
||||
forceLog_printf("DirectSound: %s", s_availableApis[DirectSound] ? "available" : "not supported");
|
||||
forceLog_printf("XAudio 2.8: %s", s_availableApis[XAudio2] ? "available" : "not supported");
|
||||
if (!s_availableApis[XAudio2])
|
||||
{
|
||||
forceLog_printf("XAudio 2.7: %s", s_availableApis[XAudio27] ? "available" : "not supported")
|
||||
}
|
||||
forceLog_printf("Cubeb: %s", s_availableApis[Cubeb] ? "available" : "not supported");
|
||||
}
|
||||
|
||||
void IAudioAPI::InitWFX(sint32 samplerate, sint32 channels, sint32 bits_per_sample)
|
||||
{
|
||||
#if BOOST_OS_WINDOWS > 0
|
||||
// move this to Windows-specific audio API implementations and use a cross-platform format here
|
||||
m_wfx.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
|
||||
m_wfx.Format.nChannels = channels;
|
||||
m_wfx.Format.nSamplesPerSec = samplerate;
|
||||
m_wfx.Format.wBitsPerSample = bits_per_sample;
|
||||
m_wfx.Format.nBlockAlign = (m_wfx.Format.nChannels * m_wfx.Format.wBitsPerSample) / 8; // must equal (nChannels <20> wBitsPerSample) / 8
|
||||
m_wfx.Format.nAvgBytesPerSec = m_wfx.Format.nSamplesPerSec * m_wfx.Format.nBlockAlign; // must equal nSamplesPerSec <20> nBlockAlign.
|
||||
m_wfx.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
|
||||
|
||||
m_wfx.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
|
||||
m_wfx.Samples.wValidBitsPerSample = bits_per_sample;
|
||||
switch (channels)
|
||||
{
|
||||
case 8:
|
||||
m_wfx.dwChannelMask |= (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);
|
||||
break;
|
||||
case 6:
|
||||
m_wfx.dwChannelMask |= (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT);
|
||||
break;
|
||||
case 4:
|
||||
m_wfx.dwChannelMask |= (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT);
|
||||
break;
|
||||
case 2:
|
||||
m_wfx.dwChannelMask |= (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT);
|
||||
break;
|
||||
default:
|
||||
m_wfx.dwChannelMask = 0;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void IAudioAPI::InitializeStatic()
|
||||
{
|
||||
s_audioDelay = GetConfig().audio_delay;
|
||||
|
||||
#if BOOST_OS_WINDOWS
|
||||
s_availableApis[DirectSound] = true;
|
||||
s_availableApis[XAudio2] = XAudio2API::InitializeStatic();
|
||||
if(!s_availableApis[XAudio2]) // don't try to initialize the older lib if the newer version is available
|
||||
s_availableApis[XAudio27] = XAudio27API::InitializeStatic();
|
||||
#endif
|
||||
s_availableApis[Cubeb] = CubebAPI::InitializeStatic();
|
||||
}
|
||||
|
||||
bool IAudioAPI::IsAudioAPIAvailable(AudioAPI api)
|
||||
{
|
||||
if ((size_t)api < s_availableApis.size())
|
||||
return s_availableApis[api];
|
||||
|
||||
cemu_assert_debug(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
AudioAPIPtr IAudioAPI::CreateDevice(AudioAPI api, const DeviceDescriptionPtr& device, sint32 samplerate, sint32 channels, sint32 samples_per_block, sint32 bits_per_sample)
|
||||
{
|
||||
if (!IsAudioAPIAvailable(api))
|
||||
return {};
|
||||
|
||||
switch(api)
|
||||
{
|
||||
#if BOOST_OS_WINDOWS
|
||||
case DirectSound:
|
||||
{
|
||||
const auto tmp = std::dynamic_pointer_cast<DirectSoundAPI::DirectSoundDeviceDescription>(device);
|
||||
return std::make_unique<DirectSoundAPI>(tmp->GetGUID(), samplerate, channels, samples_per_block, bits_per_sample);
|
||||
}
|
||||
case XAudio27:
|
||||
{
|
||||
const auto tmp = std::dynamic_pointer_cast<XAudio27API::XAudio27DeviceDescription>(device);
|
||||
return std::make_unique<XAudio27API>(tmp->GetDeviceId(), samplerate, channels, samples_per_block, bits_per_sample);
|
||||
}
|
||||
case XAudio2:
|
||||
{
|
||||
const auto tmp = std::dynamic_pointer_cast<XAudio2API::XAudio2DeviceDescription>(device);
|
||||
return std::make_unique<XAudio2API>(tmp->GetDeviceId(), samplerate, channels, samples_per_block, bits_per_sample);
|
||||
}
|
||||
#endif
|
||||
case Cubeb:
|
||||
{
|
||||
const auto tmp = std::dynamic_pointer_cast<CubebAPI::CubebDeviceDescription>(device);
|
||||
return std::make_unique<CubebAPI>(tmp->GetDeviceId(), samplerate, channels, samples_per_block, bits_per_sample);
|
||||
}
|
||||
default:
|
||||
throw std::runtime_error(fmt::format("invalid audio api: {}", api));
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<IAudioAPI::DeviceDescriptionPtr> IAudioAPI::GetDevices(AudioAPI api)
|
||||
{
|
||||
if (!IsAudioAPIAvailable(api))
|
||||
return {};
|
||||
|
||||
switch(api)
|
||||
{
|
||||
#if BOOST_OS_WINDOWS
|
||||
case DirectSound:
|
||||
{
|
||||
return DirectSoundAPI::GetDevices();
|
||||
}
|
||||
case XAudio27:
|
||||
{
|
||||
return XAudio27API::GetDevices();
|
||||
}
|
||||
case XAudio2:
|
||||
{
|
||||
return XAudio2API::GetDevices();
|
||||
}
|
||||
#endif
|
||||
case Cubeb:
|
||||
{
|
||||
return CubebAPI::GetDevices();
|
||||
}
|
||||
default:
|
||||
throw std::runtime_error(fmt::format("invalid audio api: {}", api));
|
||||
}
|
||||
}
|
||||
|
90
src/audio/IAudioAPI.h
Normal file
90
src/audio/IAudioAPI.h
Normal file
|
@ -0,0 +1,90 @@
|
|||
#pragma once
|
||||
|
||||
#if BOOST_OS_WINDOWS > 0
|
||||
#include <mmreg.h>
|
||||
#endif
|
||||
|
||||
class IAudioAPI
|
||||
{
|
||||
friend class GeneralSettings2;
|
||||
|
||||
public:
|
||||
class DeviceDescription
|
||||
{
|
||||
public:
|
||||
explicit DeviceDescription(std::wstring name)
|
||||
: m_name(std::move(name)) { }
|
||||
|
||||
virtual ~DeviceDescription() = default;
|
||||
virtual std::wstring GetIdentifier() const = 0;
|
||||
const std::wstring& GetName() const { return m_name; }
|
||||
|
||||
bool operator==(const DeviceDescription& o) const
|
||||
{
|
||||
return GetIdentifier() == o.GetIdentifier();
|
||||
}
|
||||
|
||||
private:
|
||||
std::wstring m_name;
|
||||
};
|
||||
|
||||
using DeviceDescriptionPtr = std::shared_ptr<DeviceDescription>;
|
||||
|
||||
enum AudioAPI
|
||||
{
|
||||
DirectSound = 0,
|
||||
XAudio27,
|
||||
XAudio2,
|
||||
Cubeb,
|
||||
|
||||
AudioAPIEnd,
|
||||
};
|
||||
static constexpr uint32 kBlockCount = 24;
|
||||
|
||||
IAudioAPI(uint32 samplerate, uint32 channels, uint32 samples_per_block, uint32 bits_per_sample);
|
||||
virtual ~IAudioAPI() = default;
|
||||
virtual AudioAPI GetType() const = 0;
|
||||
|
||||
sint32 GetChannels() const { return m_channels; }
|
||||
|
||||
virtual sint32 GetVolume() const { return m_volume; }
|
||||
virtual void SetVolume(sint32 volume) { m_volume = volume; }
|
||||
virtual void SetInputVolume(sint32 volume) { m_inputVolume = volume; }
|
||||
|
||||
virtual bool NeedAdditionalBlocks() const = 0;
|
||||
virtual bool FeedBlock(sint16* data) = 0;
|
||||
virtual bool Play() = 0;
|
||||
virtual bool Stop() = 0;
|
||||
|
||||
static void PrintLogging();
|
||||
static void InitializeStatic();
|
||||
static bool IsAudioAPIAvailable(AudioAPI api);
|
||||
|
||||
static std::unique_ptr<IAudioAPI> CreateDevice(AudioAPI api, const DeviceDescriptionPtr& device, sint32 samplerate, sint32 channels, sint32 samples_per_block, sint32 bits_per_sample);
|
||||
static std::vector<DeviceDescriptionPtr> GetDevices(AudioAPI api);
|
||||
|
||||
protected:
|
||||
#if BOOST_OS_WINDOWS > 0
|
||||
WAVEFORMATEXTENSIBLE m_wfx{};
|
||||
#endif
|
||||
|
||||
uint32 m_samplerate, m_channels, m_samplesPerBlock, m_bitsPerSample;
|
||||
uint32 m_bytesPerBlock;
|
||||
|
||||
sint32 m_volume = 0, m_inputVolume = 0;
|
||||
bool m_playing = false;
|
||||
|
||||
static std::array<bool, AudioAPIEnd> s_availableApis;
|
||||
static uint32 s_audioDelay;
|
||||
|
||||
private:
|
||||
void InitWFX(sint32 samplerate, sint32 channels, sint32 bits_per_sample);
|
||||
|
||||
};
|
||||
|
||||
using AudioAPIPtr = std::unique_ptr<IAudioAPI>;
|
||||
extern std::shared_mutex g_audioMutex;
|
||||
extern AudioAPIPtr g_tvAudio;
|
||||
|
||||
extern AudioAPIPtr g_padAudio;
|
||||
extern std::atomic_int32_t g_padVolume;
|
237
src/audio/XAudio27API.cpp
Normal file
237
src/audio/XAudio27API.cpp
Normal file
|
@ -0,0 +1,237 @@
|
|||
#include "XAudio27API.h"
|
||||
|
||||
#include "../dependencies/DirectX_2010/XAudio2.h"
|
||||
|
||||
static_assert(IAudioAPI::kBlockCount < XAUDIO2_MAX_QUEUED_BUFFERS, "too many xaudio2 buffers");
|
||||
|
||||
HMODULE XAudio27API::s_xaudio_dll = nullptr;
|
||||
bool XAudio27API::s_com_initialized = false;
|
||||
std::unique_ptr<IXAudio2, XAudio27API::XAudioDeleter> XAudio27API::s_xaudio;
|
||||
|
||||
XAudio27API::XAudio27API(uint32 device_id, uint32 samplerate, uint32 channels, uint32 samples_per_block, uint32 bits_per_sample)
|
||||
: IAudioAPI(samplerate, channels, samples_per_block, bits_per_sample)
|
||||
{
|
||||
if (!s_xaudio)
|
||||
throw std::runtime_error("xaudio 2.7 not initialized!");
|
||||
|
||||
// we use -1 for always selecting the primary device, which is the first one
|
||||
if (device_id == -1)
|
||||
device_id = 0;
|
||||
|
||||
HRESULT hres;
|
||||
IXAudio2* xaudio;
|
||||
if (FAILED((hres = XAudio2Create(&xaudio, 0, XAUDIO2_DEFAULT_PROCESSOR))))
|
||||
throw std::runtime_error(fmt::format("can't create xaudio device (hres: {:#x})", hres));
|
||||
m_xaudio = decltype(m_xaudio)(xaudio);
|
||||
|
||||
IXAudio2MasteringVoice* mastering_voice;
|
||||
if (FAILED((hres = m_xaudio->CreateMasteringVoice(&mastering_voice, channels, samplerate, 0, device_id))))
|
||||
throw std::runtime_error(fmt::format("can't create xaudio mastering voice (hres: {:#x})", hres));
|
||||
|
||||
m_mastering_voice = decltype(m_mastering_voice)(mastering_voice);
|
||||
|
||||
m_wfx.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
|
||||
m_wfx.Format.nChannels = channels;
|
||||
m_wfx.Format.nSamplesPerSec = samplerate;
|
||||
m_wfx.Format.wBitsPerSample = bits_per_sample;
|
||||
m_wfx.Format.nBlockAlign = (m_wfx.Format.nChannels * m_wfx.Format.wBitsPerSample) / 8; // must equal (nChannels × wBitsPerSample) / 8
|
||||
m_wfx.Format.nAvgBytesPerSec = m_wfx.Format.nSamplesPerSec * m_wfx.Format.nBlockAlign; // must equal nSamplesPerSec × nBlockAlign.
|
||||
m_wfx.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
|
||||
|
||||
m_wfx.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
|
||||
m_wfx.Samples.wValidBitsPerSample = bits_per_sample;
|
||||
switch (channels)
|
||||
{
|
||||
case 8:
|
||||
m_wfx.dwChannelMask |= (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);
|
||||
break;
|
||||
case 6:
|
||||
m_wfx.dwChannelMask |= (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT);
|
||||
break;
|
||||
case 4:
|
||||
m_wfx.dwChannelMask |= (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT);
|
||||
break;
|
||||
case 2:
|
||||
m_wfx.dwChannelMask |= (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT);
|
||||
break;
|
||||
default:
|
||||
m_wfx.dwChannelMask = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
IXAudio2SourceVoice* source_voice;
|
||||
if (FAILED((hres = m_xaudio->CreateSourceVoice(&source_voice, &m_wfx.Format, 0, 1.0f))))
|
||||
throw std::runtime_error(fmt::format("can't create xaudio source voice (hres: {:#x})", hres));
|
||||
m_source_voice = decltype(m_source_voice)(source_voice);
|
||||
|
||||
m_sound_buffer_size = kBlockCount * (samples_per_block * channels * (bits_per_sample / 8));
|
||||
|
||||
for (uint32 i = 0; i < kBlockCount; ++i)
|
||||
m_audio_buffer[i] = std::make_unique<uint8[]>(m_bytesPerBlock);
|
||||
|
||||
m_xaudio->StartEngine();
|
||||
}
|
||||
|
||||
XAudio27API::~XAudio27API()
|
||||
{
|
||||
if(m_xaudio)
|
||||
m_xaudio->StopEngine();
|
||||
|
||||
XAudio27API::Stop();
|
||||
|
||||
m_source_voice.reset();
|
||||
m_mastering_voice.reset();
|
||||
m_xaudio.reset();
|
||||
}
|
||||
|
||||
void XAudio27API::SetVolume(sint32 volume)
|
||||
{
|
||||
IAudioAPI::SetVolume(volume);
|
||||
m_mastering_voice->SetVolume((float)volume / 100.0f);
|
||||
}
|
||||
|
||||
bool XAudio27API::Play()
|
||||
{
|
||||
if (m_playing)
|
||||
return true;
|
||||
|
||||
m_playing = SUCCEEDED(m_source_voice->Start());
|
||||
return m_playing;
|
||||
}
|
||||
|
||||
bool XAudio27API::Stop()
|
||||
{
|
||||
if (!m_playing)
|
||||
return true;
|
||||
|
||||
m_playing = FAILED(m_source_voice->Stop());
|
||||
m_source_voice->FlushSourceBuffers();
|
||||
|
||||
return m_playing;
|
||||
}
|
||||
|
||||
bool XAudio27API::InitializeStatic()
|
||||
{
|
||||
if (s_xaudio)
|
||||
return true;
|
||||
|
||||
s_com_initialized = (SUCCEEDED(CoInitializeEx(nullptr, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE)));
|
||||
|
||||
#ifdef _DEBUG
|
||||
s_xaudio_dll = LoadLibraryExW(L"XAudioD2_7.DLL", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32);
|
||||
if(!s_xaudio_dll)
|
||||
s_xaudio_dll = LoadLibraryExW(L"XAudio2_7.DLL", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32);
|
||||
#else
|
||||
s_xaudio_dll = LoadLibraryExW(L"XAudio2_7.DLL", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32);
|
||||
#endif
|
||||
|
||||
try
|
||||
{
|
||||
if (!s_xaudio_dll)
|
||||
throw std::exception();
|
||||
|
||||
IXAudio2* xaudio;
|
||||
if (FAILED(XAudio2Create(&xaudio, 0, XAUDIO2_DEFAULT_PROCESSOR)))
|
||||
throw std::exception();
|
||||
|
||||
s_xaudio = decltype(s_xaudio)(xaudio);
|
||||
return true;
|
||||
}
|
||||
catch (const std::exception&)
|
||||
{
|
||||
if (s_xaudio_dll)
|
||||
FreeLibrary(s_xaudio_dll);
|
||||
|
||||
if (s_com_initialized)
|
||||
CoUninitialize();
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void XAudio27API::Destroy()
|
||||
{
|
||||
s_xaudio.reset();
|
||||
|
||||
if (s_xaudio_dll)
|
||||
FreeLibrary(s_xaudio_dll);
|
||||
|
||||
if (s_com_initialized)
|
||||
CoUninitialize();
|
||||
}
|
||||
|
||||
std::vector<XAudio27API::DeviceDescriptionPtr> XAudio27API::GetDevices()
|
||||
{
|
||||
std::vector<DeviceDescriptionPtr> result;
|
||||
|
||||
// always add the default device
|
||||
auto default_device = std::make_shared<XAudio27DeviceDescription>(L"Primary Sound Driver", L"", -1);
|
||||
result.emplace_back(default_device);
|
||||
|
||||
uint32 count = 0;
|
||||
if (FAILED(s_xaudio->GetDeviceCount(&count)))
|
||||
return result;
|
||||
|
||||
if (!count)
|
||||
return result;
|
||||
|
||||
result.reserve(count + 1);
|
||||
|
||||
// first device is always the primary device
|
||||
for (uint32 id = 0; id < count; ++id)
|
||||
{
|
||||
XAUDIO2_DEVICE_DETAILS details;
|
||||
if (SUCCEEDED(s_xaudio->GetDeviceDetails(id, &details)))
|
||||
{
|
||||
auto device = std::make_shared<XAudio27DeviceDescription>(details.DisplayName, details.DeviceID, id);
|
||||
result.emplace_back(device);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void XAudio27API::XAudioDeleter::operator()(IXAudio2* ptr) const
|
||||
{
|
||||
if (ptr)
|
||||
ptr->Release();
|
||||
}
|
||||
|
||||
void XAudio27API::VoiceDeleter::operator()(IXAudio2Voice* ptr) const
|
||||
{
|
||||
if (ptr)
|
||||
ptr->DestroyVoice();
|
||||
}
|
||||
|
||||
bool XAudio27API::FeedBlock(sint16* data)
|
||||
{
|
||||
// check if we queued too many blocks
|
||||
if(m_blocks_queued >= kBlockCount)
|
||||
{
|
||||
XAUDIO2_VOICE_STATE state{};
|
||||
m_source_voice->GetState(&state);
|
||||
m_blocks_queued = state.BuffersQueued;
|
||||
|
||||
if (m_blocks_queued >= kBlockCount)
|
||||
{
|
||||
forceLogDebug_printf("dropped xaudio2 block since too many buffers are queued");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(m_audio_buffer[m_offset].get(), data, m_bytesPerBlock);
|
||||
|
||||
XAUDIO2_BUFFER buffer{};
|
||||
buffer.AudioBytes = m_bytesPerBlock;
|
||||
buffer.pAudioData = m_audio_buffer[m_offset].get();
|
||||
m_source_voice->SubmitSourceBuffer(&buffer);
|
||||
|
||||
m_offset = (m_offset + 1) % kBlockCount;
|
||||
m_blocks_queued++;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool XAudio27API::NeedAdditionalBlocks() const
|
||||
{
|
||||
return m_blocks_queued < s_audioDelay;
|
||||
}
|
73
src/audio/XAudio27API.h
Normal file
73
src/audio/XAudio27API.h
Normal file
|
@ -0,0 +1,73 @@
|
|||
#pragma once
|
||||
|
||||
#define DIRECTSOUND_VERSION 0x0800
|
||||
#include <mmsystem.h>
|
||||
#include <mmreg.h>
|
||||
#include <dsound.h>
|
||||
|
||||
#include "IAudioAPI.h"
|
||||
|
||||
struct IXAudio2;
|
||||
struct IXAudio2Voice;
|
||||
struct IXAudio2MasteringVoice;
|
||||
struct IXAudio2SourceVoice;
|
||||
|
||||
class XAudio27API : public IAudioAPI
|
||||
{
|
||||
public:
|
||||
class XAudio27DeviceDescription : public DeviceDescription
|
||||
{
|
||||
public:
|
||||
XAudio27DeviceDescription(const std::wstring& name, std::wstring device_id, uint32 id)
|
||||
: DeviceDescription(name), m_device_id(std::move(device_id)), m_id(id) { }
|
||||
|
||||
std::wstring GetIdentifier() const override { return m_device_id.empty() ? L"default" : m_device_id; }
|
||||
uint32 GetDeviceId() const { return m_id; }
|
||||
|
||||
private:
|
||||
std::wstring m_device_id;
|
||||
const uint32 m_id;
|
||||
};
|
||||
|
||||
using XAudio2DeviceDescriptionPtr = std::shared_ptr<XAudio27DeviceDescription>;
|
||||
|
||||
AudioAPI GetType() const override { return XAudio27; }
|
||||
|
||||
XAudio27API(uint32 device_id, uint32 samplerate, uint32 channels, uint32 samples_per_block, uint32 bits_per_sample);
|
||||
~XAudio27API();
|
||||
void SetVolume(sint32 volume) override;
|
||||
|
||||
bool Play() override;
|
||||
bool Stop() override;
|
||||
bool FeedBlock(sint16* data) override;
|
||||
bool NeedAdditionalBlocks() const override;
|
||||
|
||||
static bool InitializeStatic();
|
||||
static void Destroy();
|
||||
static std::vector<DeviceDescriptionPtr> GetDevices();
|
||||
|
||||
private:
|
||||
struct XAudioDeleter
|
||||
{
|
||||
void operator()(IXAudio2* ptr) const;
|
||||
};
|
||||
|
||||
struct VoiceDeleter
|
||||
{
|
||||
void operator()(IXAudio2Voice* ptr) const;
|
||||
};
|
||||
|
||||
static HMODULE s_xaudio_dll;
|
||||
static bool s_com_initialized;
|
||||
static std::unique_ptr<IXAudio2, XAudioDeleter> s_xaudio;
|
||||
|
||||
std::unique_ptr<IXAudio2, XAudioDeleter> m_xaudio;
|
||||
std::wstring m_device_id;
|
||||
std::unique_ptr<IXAudio2MasteringVoice, VoiceDeleter> m_mastering_voice;
|
||||
std::unique_ptr<IXAudio2SourceVoice, VoiceDeleter> m_source_voice;
|
||||
|
||||
std::unique_ptr<uint8[]> m_audio_buffer[kBlockCount];
|
||||
DWORD m_sound_buffer_size = 0;
|
||||
uint32_t m_offset = 0;
|
||||
uint32_t m_blocks_queued = 0;
|
||||
};
|
310
src/audio/XAudio2API.cpp
Normal file
310
src/audio/XAudio2API.cpp
Normal file
|
@ -0,0 +1,310 @@
|
|||
|
||||
|
||||
//#if (_WIN32_WINNT >= 0x0602 /*_WIN32_WINNT_WIN8*/)
|
||||
|
||||
#include <xaudio2.h>
|
||||
|
||||
#ifndef XAUDIO2_DLL
|
||||
#error wrong <xaudio2.h> included!
|
||||
#endif
|
||||
|
||||
#include "XAudio2API.h"
|
||||
#include "util/helpers/helpers.h"
|
||||
|
||||
#include <WbemCli.h>
|
||||
#include <OleAuto.h>
|
||||
#include <system_error>
|
||||
|
||||
// guid from mmdeviceapi.h
|
||||
static const GUID DEVINTERFACE_AUDIO_RENDER_GUID = { 0xe6327cad, 0xdcec, 0x4949, 0xae, 0x8a, 0x99, 0x1e, 0x97, 0x6a, 0x79, 0xd2 };
|
||||
|
||||
#pragma comment(lib, "wbemuuid.lib")
|
||||
|
||||
static_assert(IAudioAPI::kBlockCount < XAUDIO2_MAX_QUEUED_BUFFERS, "too many xaudio2 buffers");
|
||||
|
||||
HMODULE XAudio2API::s_xaudio_dll = nullptr;
|
||||
bool XAudio2API::s_com_initialized = false;
|
||||
std::vector<XAudio2API::DeviceDescriptionPtr> XAudio2API::s_devices;
|
||||
|
||||
XAudio2API::XAudio2API(std::wstring device_id, uint32 samplerate, uint32 channels, uint32 samples_per_block, uint32 bits_per_sample)
|
||||
: IAudioAPI(samplerate, channels, samples_per_block, bits_per_sample), m_device_id(std::move(device_id))
|
||||
{
|
||||
const auto _XAudio2Create = (decltype(&XAudio2Create))GetProcAddress(s_xaudio_dll, "XAudio2Create");
|
||||
if (!_XAudio2Create)
|
||||
throw std::runtime_error("can't find XAudio2Create import");
|
||||
|
||||
HRESULT hres;
|
||||
IXAudio2* xaudio;
|
||||
if (FAILED((hres = _XAudio2Create(&xaudio, 0, XAUDIO2_DEFAULT_PROCESSOR))))
|
||||
throw std::runtime_error(fmt::format("can't create xaudio device (hres: {:#x})", hres));
|
||||
|
||||
m_xaudio = decltype(m_xaudio)(xaudio);
|
||||
|
||||
IXAudio2MasteringVoice* mastering_voice;
|
||||
if (FAILED((hres = m_xaudio->CreateMasteringVoice(&mastering_voice, channels, samplerate, 0, m_device_id.empty() ? nullptr : m_device_id.c_str()))))
|
||||
throw std::runtime_error(fmt::format("can't create xaudio mastering voice (hres: {:#x})", hres));
|
||||
|
||||
m_mastering_voice = decltype(m_mastering_voice)(mastering_voice);
|
||||
|
||||
m_wfx.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
|
||||
m_wfx.Format.nChannels = channels;
|
||||
m_wfx.Format.nSamplesPerSec = samplerate;
|
||||
m_wfx.Format.wBitsPerSample = bits_per_sample;
|
||||
m_wfx.Format.nBlockAlign = (m_wfx.Format.nChannels * m_wfx.Format.wBitsPerSample) / 8; // must equal (nChannels × wBitsPerSample) / 8
|
||||
m_wfx.Format.nAvgBytesPerSec = m_wfx.Format.nSamplesPerSec * m_wfx.Format.nBlockAlign; // must equal nSamplesPerSec × nBlockAlign.
|
||||
m_wfx.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
|
||||
|
||||
m_wfx.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
|
||||
m_wfx.Samples.wValidBitsPerSample = bits_per_sample;
|
||||
switch(channels)
|
||||
{
|
||||
case 8:
|
||||
m_wfx.dwChannelMask |= (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);
|
||||
break;
|
||||
case 6:
|
||||
m_wfx.dwChannelMask |= (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT);
|
||||
break;
|
||||
case 4:
|
||||
m_wfx.dwChannelMask |= (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT);
|
||||
break;
|
||||
case 2:
|
||||
m_wfx.dwChannelMask |= (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT);
|
||||
break;
|
||||
default:
|
||||
m_wfx.dwChannelMask = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
IXAudio2SourceVoice* source_voice;
|
||||
if (FAILED((hres = m_xaudio->CreateSourceVoice(&source_voice, &m_wfx.Format, 0, 1.0f))))
|
||||
throw std::runtime_error(fmt::format("can't create xaudio source voice (hres: {:#x})", hres));
|
||||
|
||||
m_source_voice = decltype(m_source_voice)(source_voice);
|
||||
|
||||
m_sound_buffer_size = kBlockCount * (samples_per_block * channels * (bits_per_sample / 8));
|
||||
|
||||
for (uint32 i = 0; i < kBlockCount; ++i)
|
||||
m_audio_buffer[i] = std::make_unique<uint8[]>(m_bytesPerBlock);
|
||||
|
||||
m_xaudio->StartEngine();
|
||||
}
|
||||
|
||||
void XAudio2API::XAudioDeleter::operator()(IXAudio2* ptr) const
|
||||
{
|
||||
if (ptr)
|
||||
ptr->Release();
|
||||
}
|
||||
|
||||
void XAudio2API::VoiceDeleter::operator()(IXAudio2Voice* ptr) const
|
||||
{
|
||||
if (ptr)
|
||||
ptr->DestroyVoice();
|
||||
}
|
||||
|
||||
XAudio2API::~XAudio2API()
|
||||
{
|
||||
if(m_xaudio)
|
||||
m_xaudio->StopEngine();
|
||||
|
||||
XAudio2API::Stop();
|
||||
|
||||
m_source_voice.reset();
|
||||
m_mastering_voice.reset();
|
||||
m_xaudio.reset();
|
||||
}
|
||||
|
||||
void XAudio2API::SetVolume(sint32 volume)
|
||||
{
|
||||
IAudioAPI::SetVolume(volume);
|
||||
m_mastering_voice->SetVolume((float)volume / 100.0f);
|
||||
}
|
||||
|
||||
bool XAudio2API::Play()
|
||||
{
|
||||
if (m_playing)
|
||||
return true;
|
||||
|
||||
m_playing = SUCCEEDED(m_source_voice->Start());
|
||||
return m_playing;
|
||||
}
|
||||
|
||||
bool XAudio2API::Stop()
|
||||
{
|
||||
if (!m_playing)
|
||||
return true;
|
||||
|
||||
m_playing = FAILED(m_source_voice->Stop());
|
||||
m_source_voice->FlushSourceBuffers();
|
||||
|
||||
return m_playing;
|
||||
}
|
||||
|
||||
bool XAudio2API::InitializeStatic()
|
||||
{
|
||||
if (s_xaudio_dll)
|
||||
return true;
|
||||
|
||||
s_com_initialized = (SUCCEEDED(CoInitializeEx(nullptr, COINIT_MULTITHREADED)));
|
||||
|
||||
// win 10
|
||||
s_xaudio_dll = LoadLibraryEx(XAUDIO2_DLL, nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32);
|
||||
|
||||
try
|
||||
{
|
||||
if (!s_xaudio_dll)
|
||||
throw std::exception();
|
||||
|
||||
const auto _XAudio2Create = (decltype(&XAudio2Create))GetProcAddress(s_xaudio_dll, "XAudio2Create");
|
||||
if (!_XAudio2Create)
|
||||
throw std::exception();
|
||||
|
||||
RefreshDevices();
|
||||
return true;
|
||||
}
|
||||
catch (const std::exception&)
|
||||
{
|
||||
if (s_xaudio_dll)
|
||||
FreeLibrary(s_xaudio_dll);
|
||||
|
||||
if (s_com_initialized)
|
||||
CoUninitialize();
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void XAudio2API::Destroy()
|
||||
{
|
||||
if (s_xaudio_dll)
|
||||
FreeLibrary(s_xaudio_dll);
|
||||
|
||||
if (s_com_initialized)
|
||||
CoUninitialize();
|
||||
}
|
||||
|
||||
const std::vector<XAudio2API::DeviceDescriptionPtr>& XAudio2API::RefreshDevices()
|
||||
{
|
||||
// this function must be called from the same thread as we called CoInitializeEx
|
||||
s_devices.clear();
|
||||
|
||||
// always add the default device
|
||||
auto default_device = std::make_shared<XAudio2DeviceDescription>(L"Primary Sound Driver", L"");
|
||||
s_devices.emplace_back(default_device);
|
||||
|
||||
if (FAILED(CoInitializeEx(nullptr, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE)))
|
||||
return s_devices;
|
||||
|
||||
try
|
||||
{
|
||||
struct IWbemLocator *wbem_locator = nullptr;
|
||||
|
||||
HRESULT hres = CoCreateInstance(__uuidof(WbemLocator), nullptr, CLSCTX_INPROC_SERVER, __uuidof(IWbemLocator), (LPVOID*)&wbem_locator);
|
||||
if (FAILED(hres) || !wbem_locator)
|
||||
throw std::system_error(hres, std::system_category());
|
||||
|
||||
std::shared_ptr<OLECHAR> path(SysAllocString(LR"(\\.\root\cimv2)"), SysFreeString);
|
||||
std::shared_ptr<OLECHAR> language(SysAllocString(L"WQL"), SysFreeString);
|
||||
std::shared_ptr<OLECHAR> query(SysAllocString(LR"(SELECT Name,DeviceID FROM Win32_PNPEntity WHERE PNPClass = "AudioEndpoint")"), SysFreeString);
|
||||
std::shared_ptr<OLECHAR> name_row(SysAllocString(L"Name"), SysFreeString);
|
||||
std::shared_ptr<OLECHAR> device_id_row(SysAllocString(L"DeviceID"), SysFreeString);
|
||||
|
||||
IWbemServices *wbem_services = nullptr;
|
||||
hres = wbem_locator->ConnectServer(path.get(), nullptr, nullptr, nullptr, 0, nullptr, nullptr, &wbem_services);
|
||||
wbem_locator->Release(); // Free memory resources.
|
||||
|
||||
if (FAILED(hres) || !wbem_services)
|
||||
throw std::system_error(hres, std::system_category());
|
||||
|
||||
hres = CoSetProxyBlanket(wbem_services, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, nullptr, RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, nullptr, EOAC_NONE);
|
||||
if (FAILED(hres))
|
||||
throw std::system_error(hres, std::system_category());
|
||||
|
||||
IEnumWbemClassObject* wbem_enum = nullptr;
|
||||
hres = wbem_services->ExecQuery(language.get(), query.get(), WBEM_FLAG_RETURN_WBEM_COMPLETE | WBEM_FLAG_FORWARD_ONLY, nullptr, &wbem_enum);
|
||||
if (FAILED(hres) || !wbem_enum)
|
||||
throw std::system_error(hres, std::system_category());
|
||||
|
||||
ULONG returned;
|
||||
IWbemClassObject* object[20];
|
||||
// WBEM_S_TIMEDOUT
|
||||
while (SUCCEEDED(hres = wbem_enum->Next(100, 20, object, &returned)) && returned > 0)
|
||||
{
|
||||
for (ULONG i = 0; i < returned; ++i)
|
||||
{
|
||||
std::wstring name, device_id;
|
||||
|
||||
VARIANT var;
|
||||
if (SUCCEEDED(object[i]->Get(name_row.get(), 0L, &var, NULL, NULL)) && var.vt == VT_BSTR && var.bstrVal)
|
||||
{
|
||||
name = var.bstrVal;
|
||||
if (SUCCEEDED(object[i]->Get(device_id_row.get(), 0L, &var, NULL, NULL)) && var.vt == VT_BSTR && var.bstrVal)
|
||||
{
|
||||
std::wstring id = var.bstrVal;
|
||||
|
||||
if(id.find(L"{0.0.0.00000000}") == std::wstring::npos)
|
||||
{
|
||||
object[i]->Release();
|
||||
continue;
|
||||
}
|
||||
|
||||
std::replace(id.begin(), id.end(), L'\\', L'#'); // xaudio devices have "#" instead of backslashes
|
||||
|
||||
std::wstringstream tmp;
|
||||
tmp << L"\\\\?\\" << id << L"#{" << WStringFromGUID(DEVINTERFACE_AUDIO_RENDER_GUID) << L"}";
|
||||
device_id = tmp.str();
|
||||
}
|
||||
}
|
||||
|
||||
auto device = std::make_shared<XAudio2DeviceDescription>(name,device_id);
|
||||
s_devices.emplace_back(device);
|
||||
|
||||
object[i]->Release();
|
||||
}
|
||||
}
|
||||
|
||||
wbem_enum->Release();
|
||||
|
||||
// Clean up
|
||||
wbem_services->Release();
|
||||
}
|
||||
catch (const std::system_error& ex)
|
||||
{
|
||||
forceLog_printf("XAudio2API::RefreshDevices: error while refreshing device list (%s - code: 0x%08x)", ex.what(), ex.code().value());
|
||||
}
|
||||
|
||||
CoUninitialize();
|
||||
return s_devices;
|
||||
}
|
||||
|
||||
bool XAudio2API::FeedBlock(sint16* data)
|
||||
{
|
||||
// check if we queued too many blocks
|
||||
if (m_blocks_queued >= kBlockCount)
|
||||
{
|
||||
XAUDIO2_VOICE_STATE state{};
|
||||
m_source_voice->GetState(&state);
|
||||
m_blocks_queued = state.BuffersQueued;
|
||||
|
||||
if (m_blocks_queued >= kBlockCount)
|
||||
{
|
||||
forceLogDebug_printf("dropped xaudio2 block since too many buffers are queued");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(m_audio_buffer[m_offset].get(), data, m_bytesPerBlock);
|
||||
|
||||
XAUDIO2_BUFFER buffer{};
|
||||
buffer.AudioBytes = m_bytesPerBlock;
|
||||
buffer.pAudioData = m_audio_buffer[m_offset].get();
|
||||
m_source_voice->SubmitSourceBuffer(&buffer);
|
||||
|
||||
m_offset = (m_offset + 1) % kBlockCount;
|
||||
m_blocks_queued++;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool XAudio2API::NeedAdditionalBlocks() const
|
||||
{
|
||||
return m_blocks_queued < s_audioDelay;
|
||||
}
|
74
src/audio/XAudio2API.h
Normal file
74
src/audio/XAudio2API.h
Normal file
|
@ -0,0 +1,74 @@
|
|||
#pragma once
|
||||
|
||||
#define DIRECTSOUND_VERSION 0x0800
|
||||
#include <mmsystem.h>
|
||||
#include <mmreg.h>
|
||||
#include <dsound.h>
|
||||
|
||||
#include "IAudioAPI.h"
|
||||
|
||||
struct IXAudio2;
|
||||
struct IXAudio2Voice;
|
||||
struct IXAudio2MasteringVoice;
|
||||
struct IXAudio2SourceVoice;
|
||||
|
||||
class XAudio2API : public IAudioAPI
|
||||
{
|
||||
public:
|
||||
class XAudio2DeviceDescription : public DeviceDescription
|
||||
{
|
||||
public:
|
||||
XAudio2DeviceDescription(const std::wstring& name, std::wstring device_id)
|
||||
: DeviceDescription(name), m_device_id(std::move(device_id)) { }
|
||||
|
||||
std::wstring GetIdentifier() const override { return m_device_id.empty() ? L"default" : m_device_id; }
|
||||
const std::wstring& GetDeviceId() const { return m_device_id; }
|
||||
|
||||
private:
|
||||
std::wstring m_device_id;
|
||||
};
|
||||
|
||||
using XAudio2DeviceDescriptionPtr = std::shared_ptr<XAudio2DeviceDescription>;
|
||||
|
||||
AudioAPI GetType() const override { return XAudio27; }
|
||||
|
||||
XAudio2API(std::wstring device_id, uint32 samplerate, uint32 channels, uint32 samples_per_block, uint32 bits_per_sample);
|
||||
~XAudio2API();
|
||||
void SetVolume(sint32 volume) override;
|
||||
|
||||
bool Play() override;
|
||||
bool Stop() override;
|
||||
bool FeedBlock(sint16* data) override;
|
||||
bool NeedAdditionalBlocks() const override;
|
||||
|
||||
static bool InitializeStatic();
|
||||
static void Destroy();
|
||||
static const std::vector<DeviceDescriptionPtr>& GetDevices() { return s_devices; }
|
||||
|
||||
private:
|
||||
static const std::vector<DeviceDescriptionPtr>& RefreshDevices();
|
||||
|
||||
struct XAudioDeleter
|
||||
{
|
||||
void operator()(IXAudio2* ptr) const;
|
||||
};
|
||||
|
||||
struct VoiceDeleter
|
||||
{
|
||||
void operator()(IXAudio2Voice* ptr) const;
|
||||
};
|
||||
|
||||
static HMODULE s_xaudio_dll;
|
||||
static bool s_com_initialized;
|
||||
static std::vector<DeviceDescriptionPtr> s_devices;
|
||||
|
||||
std::unique_ptr<IXAudio2, XAudioDeleter> m_xaudio;
|
||||
std::wstring m_device_id;
|
||||
std::unique_ptr<IXAudio2MasteringVoice, VoiceDeleter> m_mastering_voice;
|
||||
std::unique_ptr<IXAudio2SourceVoice, VoiceDeleter> m_source_voice;
|
||||
|
||||
std::unique_ptr<uint8[]> m_audio_buffer[kBlockCount];
|
||||
DWORD m_sound_buffer_size = 0;
|
||||
uint32_t m_offset = 0;
|
||||
uint32_t m_blocks_queued = 0;
|
||||
};
|
331
src/audio/audioDebuggerWindow.cpp
Normal file
331
src/audio/audioDebuggerWindow.cpp
Normal file
|
@ -0,0 +1,331 @@
|
|||
#include "gui/wxgui.h"
|
||||
#include "audioDebuggerWindow.h"
|
||||
|
||||
#include "Cafe/OS/libs/snd_core/ax.h"
|
||||
#include "Cafe/OS/libs/snd_core/ax_internal.h"
|
||||
|
||||
enum
|
||||
{
|
||||
// options
|
||||
REFRESH_ID,
|
||||
CLOSE_ID,
|
||||
VOICELIST_ID,
|
||||
REFRESH_TIMER_ID,
|
||||
};
|
||||
|
||||
wxBEGIN_EVENT_TABLE(AudioDebuggerWindow, wxFrame)
|
||||
EVT_BUTTON(CLOSE_ID, AudioDebuggerWindow::OnCloseButton)
|
||||
EVT_BUTTON(REFRESH_ID, AudioDebuggerWindow::OnRefreshButton)
|
||||
EVT_TIMER(REFRESH_TIMER_ID, AudioDebuggerWindow::OnRefreshTimer)
|
||||
|
||||
EVT_CLOSE(AudioDebuggerWindow::OnClose)
|
||||
wxEND_EVENT_TABLE()
|
||||
|
||||
AudioDebuggerWindow::AudioDebuggerWindow(wxFrame& parent)
|
||||
: wxFrame(&parent, wxID_ANY, wxT("AX voice viewer"), wxDefaultPosition, wxSize(1126, 580), wxCLOSE_BOX | wxCLIP_CHILDREN | wxCAPTION | wxRESIZE_BORDER)
|
||||
{
|
||||
|
||||
wxPanel* mainPane = new wxPanel(this);
|
||||
wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
voiceListbox = new wxListCtrl(mainPane, VOICELIST_ID, wxPoint(0, 0), wxSize(1126, 570), wxLC_REPORT);
|
||||
voiceListbox->SetFont(wxFont(8, wxFONTFAMILY_MODERN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, "Courier New"));
|
||||
// add columns
|
||||
wxListItem col0;
|
||||
col0.SetId(0);
|
||||
col0.SetText(wxT("idx"));
|
||||
col0.SetWidth(40);
|
||||
voiceListbox->InsertColumn(0, col0);
|
||||
wxListItem col1;
|
||||
col1.SetId(1);
|
||||
col1.SetText(wxT("state"));
|
||||
col1.SetWidth(48);
|
||||
voiceListbox->InsertColumn(1, col1);
|
||||
//wxListItem col2;
|
||||
// format
|
||||
col1.SetId(2);
|
||||
col1.SetText(wxT("fmt"));
|
||||
col1.SetWidth(52);
|
||||
voiceListbox->InsertColumn(2, col1);
|
||||
// sample base addr
|
||||
col1.SetId(3);
|
||||
col1.SetText(wxT("base"));
|
||||
col1.SetWidth(70);
|
||||
voiceListbox->InsertColumn(3, col1);
|
||||
// current offset
|
||||
col1.SetId(4);
|
||||
col1.SetText(wxT("current"));
|
||||
col1.SetWidth(70);
|
||||
voiceListbox->InsertColumn(4, col1);
|
||||
// loop offset
|
||||
col1.SetId(5);
|
||||
col1.SetText(wxT("loop"));
|
||||
col1.SetWidth(70);
|
||||
voiceListbox->InsertColumn(5, col1);
|
||||
// end offset
|
||||
col1.SetId(6);
|
||||
col1.SetText(wxT("end"));
|
||||
col1.SetWidth(70);
|
||||
voiceListbox->InsertColumn(6, col1);
|
||||
// volume
|
||||
col1.SetId(7);
|
||||
col1.SetText(wxT("vol"));
|
||||
col1.SetWidth(46);
|
||||
voiceListbox->InsertColumn(7, col1);
|
||||
// volume delta
|
||||
col1.SetId(8);
|
||||
col1.SetText(wxT("volD"));
|
||||
col1.SetWidth(46);
|
||||
voiceListbox->InsertColumn(8, col1);
|
||||
// src
|
||||
col1.SetId(9);
|
||||
col1.SetText(wxT("src"));
|
||||
col1.SetWidth(70);
|
||||
voiceListbox->InsertColumn(9, col1);
|
||||
// low-pass filter coef a0
|
||||
col1.SetId(10);
|
||||
col1.SetText(wxT("lpa0"));
|
||||
col1.SetWidth(46);
|
||||
voiceListbox->InsertColumn(10, col1);
|
||||
// low-pass filter coef b0
|
||||
col1.SetId(11);
|
||||
col1.SetText(wxT("lpb0"));
|
||||
col1.SetWidth(46);
|
||||
voiceListbox->InsertColumn(11, col1);
|
||||
// biquad filter coef b0
|
||||
col1.SetId(12);
|
||||
col1.SetText(wxT("bqb0"));
|
||||
col1.SetWidth(46);
|
||||
voiceListbox->InsertColumn(12, col1);
|
||||
// biquad filter coef b0
|
||||
col1.SetId(13);
|
||||
col1.SetText(wxT("bqb1"));
|
||||
col1.SetWidth(46);
|
||||
voiceListbox->InsertColumn(13, col1);
|
||||
// biquad filter coef b0
|
||||
col1.SetId(14);
|
||||
col1.SetText(wxT("bqb2"));
|
||||
col1.SetWidth(46);
|
||||
voiceListbox->InsertColumn(14, col1);
|
||||
// biquad filter coef a0
|
||||
col1.SetId(15);
|
||||
col1.SetText(wxT("bqa1"));
|
||||
col1.SetWidth(46);
|
||||
voiceListbox->InsertColumn(15, col1);
|
||||
// biquad filter coef a1
|
||||
col1.SetId(16);
|
||||
col1.SetText(wxT("bqa2"));
|
||||
col1.SetWidth(46);
|
||||
voiceListbox->InsertColumn(16, col1);
|
||||
// device mix
|
||||
col1.SetId(17);
|
||||
col1.SetText(wxT("deviceMix"));
|
||||
col1.SetWidth(186);
|
||||
voiceListbox->InsertColumn(17, col1);
|
||||
|
||||
sizer->Add(voiceListbox, 1, wxEXPAND | wxBOTTOM, 0);
|
||||
|
||||
voiceListbox->Connect(wxEVT_RIGHT_DOWN, wxMouseEventHandler(AudioDebuggerWindow::OnVoiceListRightClick), NULL, this);
|
||||
|
||||
mainPane->SetSizer(sizer);
|
||||
|
||||
// add empty entries
|
||||
for (sint32 i = 0; i < snd_core::AX_MAX_VOICES; i++)
|
||||
{
|
||||
wxListItem item;
|
||||
item.SetId(i);
|
||||
char tempStr[32];
|
||||
sprintf(tempStr, "%d", snd_core::AX_MAX_VOICES-i-1);
|
||||
item.SetText(wxString(tempStr));
|
||||
voiceListbox->InsertItem(item);
|
||||
}
|
||||
RefreshVoiceList();
|
||||
|
||||
wxFrame::SetBackgroundColour(*wxWHITE);
|
||||
|
||||
// start refresh timer
|
||||
static const int INTERVAL = 100; // milliseconds
|
||||
refreshTimer = new wxTimer(this, REFRESH_TIMER_ID);
|
||||
refreshTimer->Start(INTERVAL);
|
||||
}
|
||||
|
||||
void AudioDebuggerWindow::OnRefreshTimer(wxTimerEvent& event)
|
||||
{
|
||||
RefreshVoiceList();
|
||||
}
|
||||
|
||||
void AudioDebuggerWindow::OnCloseButton(wxCommandEvent& event)
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
void AudioDebuggerWindow::OnRefreshButton(wxCommandEvent& event)
|
||||
{
|
||||
RefreshVoiceList();
|
||||
}
|
||||
|
||||
void AudioDebuggerWindow::OnClose(wxCloseEvent& event)
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
#define _r(__idx) _swapEndianU32(threadItrBE->context.gpr[__idx])
|
||||
|
||||
void AudioDebuggerWindow::RefreshVoiceList_sndgeneric()
|
||||
{
|
||||
if (snd_core::__AXVPBInternalVoiceArray == nullptr || snd_core::__AXVPBArrayPtr == nullptr)
|
||||
return;
|
||||
|
||||
snd_core::AXVPB tempVoiceArray[snd_core::AX_MAX_VOICES];
|
||||
memcpy(tempVoiceArray, snd_core::__AXVPBArrayPtr, sizeof(snd_core::AXVPB)*snd_core::AX_MAX_VOICES);
|
||||
|
||||
voiceListbox->Freeze();
|
||||
|
||||
char tempStr[64];
|
||||
for (sint32 i = 0; i < snd_core::AX_MAX_VOICES; i++)
|
||||
{
|
||||
sint32 voiceIndex = snd_core::AX_MAX_VOICES - 1 - i;
|
||||
snd_core::AXVPBInternal_t* internal = snd_core::__AXVPBInternalVoiceArray + voiceIndex;
|
||||
// index
|
||||
sprintf(tempStr, "%d", (sint32)tempVoiceArray[voiceIndex].index);
|
||||
voiceListbox->SetItem(i, 0, tempStr);
|
||||
// state
|
||||
uint16 playbackState = _swapEndianU16(internal->playbackState);
|
||||
if (playbackState)
|
||||
strcpy(tempStr, "on");
|
||||
else
|
||||
strcpy(tempStr, "off");
|
||||
voiceListbox->SetItem(i, 1, tempStr);
|
||||
// if voice index is invalid then stop updating here to prevent crashes
|
||||
if (voiceIndex < 0 || playbackState == 0)
|
||||
{
|
||||
voiceListbox->SetItem(i, 0, "");
|
||||
voiceListbox->SetItem(i, 1, "");
|
||||
voiceListbox->SetItem(i, 2, "");
|
||||
voiceListbox->SetItem(i, 3, "");
|
||||
voiceListbox->SetItem(i, 4, "");
|
||||
voiceListbox->SetItem(i, 5, "");
|
||||
voiceListbox->SetItem(i, 6, "");
|
||||
voiceListbox->SetItem(i, 7, "");
|
||||
voiceListbox->SetItem(i, 8, "");
|
||||
voiceListbox->SetItem(i, 9, "");
|
||||
voiceListbox->SetItem(i, 10, "");
|
||||
voiceListbox->SetItem(i, 11, "");
|
||||
voiceListbox->SetItem(i, 12, "");
|
||||
voiceListbox->SetItem(i, 13, "");
|
||||
voiceListbox->SetItem(i, 14, "");
|
||||
voiceListbox->SetItem(i, 15, "");
|
||||
voiceListbox->SetItem(i, 16, "");
|
||||
voiceListbox->SetItem(i, 17, "");
|
||||
continue;
|
||||
}
|
||||
// format
|
||||
if (tempVoiceArray[voiceIndex].offsets.format == _swapEndianU16(snd_core::AX_FORMAT_ADPCM))
|
||||
strcpy(tempStr, "adpcm");
|
||||
else if (tempVoiceArray[voiceIndex].offsets.format == _swapEndianU16(snd_core::AX_FORMAT_PCM16))
|
||||
strcpy(tempStr, "pcm16");
|
||||
else if (tempVoiceArray[voiceIndex].offsets.format == _swapEndianU16(snd_core::AX_FORMAT_PCM8))
|
||||
strcpy(tempStr, "pcm8");
|
||||
else
|
||||
strcpy(tempStr, "ukn");
|
||||
voiceListbox->SetItem(i, 2, tempStr);
|
||||
// update offsets
|
||||
snd_core::AXPBOFFSET_t tempOffsets;
|
||||
snd_core::AXGetVoiceOffsets(tempVoiceArray + voiceIndex, &tempOffsets);
|
||||
// sample base
|
||||
sprintf(tempStr, "%08x", _swapEndianU32(tempOffsets.samples));
|
||||
voiceListbox->SetItem(i, 3, tempStr);
|
||||
// current offset
|
||||
sprintf(tempStr, "%08x", _swapEndianU32(tempOffsets.currentOffset));
|
||||
voiceListbox->SetItem(i, 4, tempStr);
|
||||
// loop offset
|
||||
if (tempOffsets.loopFlag)
|
||||
sprintf(tempStr, "%08x", _swapEndianU32(tempOffsets.loopOffset));
|
||||
else
|
||||
sprintf(tempStr, "");
|
||||
voiceListbox->SetItem(i, 5, tempStr);
|
||||
// end offset
|
||||
sprintf(tempStr, "%08x", _swapEndianU32(tempOffsets.endOffset));
|
||||
voiceListbox->SetItem(i, 6, tempStr);
|
||||
// volume
|
||||
sprintf(tempStr, "%04x", (uint16)internal->veVolume);
|
||||
voiceListbox->SetItem(i, 7, tempStr);
|
||||
// volume delta
|
||||
sprintf(tempStr, "%04x", (uint16)internal->veDelta);
|
||||
voiceListbox->SetItem(i, 8, tempStr);
|
||||
// src
|
||||
sprintf(tempStr, "%04x%04x", _swapEndianU16(internal->src.ratioHigh), _swapEndianU16(internal->src.ratioLow));
|
||||
voiceListbox->SetItem(i, 9, tempStr);
|
||||
// lpf
|
||||
if (internal->lpf.on)
|
||||
{
|
||||
sprintf(tempStr, "%04x", _swapEndianU16(internal->lpf.a0));
|
||||
voiceListbox->SetItem(i, 10, tempStr);
|
||||
sprintf(tempStr, "%04x", _swapEndianU16(internal->lpf.b0));
|
||||
voiceListbox->SetItem(i, 11, tempStr);
|
||||
}
|
||||
else
|
||||
{
|
||||
voiceListbox->SetItem(i, 10, "");
|
||||
voiceListbox->SetItem(i, 11, "");
|
||||
}
|
||||
// biquad
|
||||
if (internal->biquad.on)
|
||||
{
|
||||
sprintf(tempStr, "%04x", _swapEndianU16(internal->biquad.b0));
|
||||
voiceListbox->SetItem(i, 12, tempStr);
|
||||
sprintf(tempStr, "%04x", _swapEndianU16(internal->biquad.b1));
|
||||
voiceListbox->SetItem(i, 13, tempStr);
|
||||
sprintf(tempStr, "%04x", _swapEndianU16(internal->biquad.b2));
|
||||
voiceListbox->SetItem(i, 14, tempStr);
|
||||
sprintf(tempStr, "%04x", _swapEndianU16(internal->biquad.a1));
|
||||
voiceListbox->SetItem(i, 15, tempStr);
|
||||
sprintf(tempStr, "%04x", _swapEndianU16(internal->biquad.a2));
|
||||
voiceListbox->SetItem(i, 16, tempStr);
|
||||
}
|
||||
else
|
||||
{
|
||||
voiceListbox->SetItem(i, 12, "");
|
||||
voiceListbox->SetItem(i, 13, "");
|
||||
voiceListbox->SetItem(i, 14, "");
|
||||
voiceListbox->SetItem(i, 15, "");
|
||||
voiceListbox->SetItem(i, 16, "");
|
||||
}
|
||||
// device mix
|
||||
for (uint32 f = 0; f < snd_core::AX_TV_CHANNEL_COUNT*snd_core::AX_MAX_NUM_BUS; f++)
|
||||
{
|
||||
sint32 busIndex = f% snd_core::AX_MAX_NUM_BUS;
|
||||
sint32 channelIndex = f / snd_core::AX_MAX_NUM_BUS;
|
||||
|
||||
//debug_printf("DeviceMix TV Voice %08x b%02d/c%02d vol %04x delta %04x\n", hCPU->gpr[3], busIndex, channelIndex, _swapEndianU16(mixArrayBE[f].vol), _swapEndianU16(mixArrayBE[f].volDelta));
|
||||
uint32 mixVol = internal->deviceMixTV[channelIndex * 4 + busIndex].vol;
|
||||
mixVol = (mixVol + 0x0FFF) >> (12);
|
||||
sprintf(tempStr + f, "%x", mixVol);
|
||||
//ax.voiceInternal[voiceIndex].deviceMixTVChannel[channelIndex].bus[busIndex].vol = _swapEndianU16(mixArrayBE[f].vol);
|
||||
}
|
||||
voiceListbox->SetItem(i, 17, tempStr);
|
||||
}
|
||||
voiceListbox->Thaw();
|
||||
}
|
||||
|
||||
void AudioDebuggerWindow::RefreshVoiceList()
|
||||
{
|
||||
RefreshVoiceList_sndgeneric();
|
||||
}
|
||||
|
||||
void AudioDebuggerWindow::OnVoiceListPopupClick(wxCommandEvent &evt)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void AudioDebuggerWindow::OnVoiceListRightClick(wxMouseEvent& event)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void AudioDebuggerWindow::Close()
|
||||
{
|
||||
// this->MakeModal(false);
|
||||
refreshTimer->Stop();
|
||||
this->Destroy();
|
||||
}
|
28
src/audio/audioDebuggerWindow.h
Normal file
28
src/audio/audioDebuggerWindow.h
Normal file
|
@ -0,0 +1,28 @@
|
|||
#pragma once
|
||||
|
||||
#include <wx/wx.h>
|
||||
|
||||
class AudioDebuggerWindow : public wxFrame
|
||||
{
|
||||
public:
|
||||
AudioDebuggerWindow(wxFrame& parent);
|
||||
|
||||
void OnCloseButton(wxCommandEvent& event);
|
||||
void OnRefreshButton(wxCommandEvent& event);
|
||||
void OnClose(wxCloseEvent& event);
|
||||
void RefreshVoiceList_sndgeneric();
|
||||
void RefreshVoiceList();
|
||||
void OnRefreshTimer(wxTimerEvent& event);
|
||||
void OnVoiceListPopupClick(wxCommandEvent &evt);
|
||||
void OnVoiceListRightClick(wxMouseEvent& event);
|
||||
|
||||
void Close();
|
||||
|
||||
private:
|
||||
wxListCtrl* voiceListbox;
|
||||
wxTimer* refreshTimer;
|
||||
|
||||
wxDECLARE_EVENT_TABLE();
|
||||
|
||||
|
||||
};
|
263
src/audio/xaudio2_7/audiodefs.h
Normal file
263
src/audio/xaudio2_7/audiodefs.h
Normal file
|
@ -0,0 +1,263 @@
|
|||
/***************************************************************************
|
||||
*
|
||||
* 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__
|
59
src/audio/xaudio2_7/comdecl.h
Normal file
59
src/audio/xaudio2_7/comdecl.h
Normal file
|
@ -0,0 +1,59 @@
|
|||
// 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_
|
18
src/audio/xaudio2_7/dxsdkver.h
Normal file
18
src/audio/xaudio2_7/dxsdkver.h
Normal file
|
@ -0,0 +1,18 @@
|
|||
/*==========================================================================;
|
||||
*
|
||||
*
|
||||
* 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_
|
||||
|
718
src/audio/xaudio2_7/xma2defs.h
Normal file
718
src/audio/xaudio2_7/xma2defs.h
Normal file
|
@ -0,0 +1,718 @@
|
|||
/***************************************************************************
|
||||
*
|
||||
* 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__
|
Loading…
Add table
Add a link
Reference in a new issue