193 lines
6.5 KiB
C++
193 lines
6.5 KiB
C++
/**
|
|
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
* SPDX-License-Identifier: Apache-2.0.
|
|
*/
|
|
|
|
#include <aws/text-to-speech/windows/WaveOutPCMOutputDriver.h>
|
|
#include <aws/core/utils/StringUtils.h>
|
|
#include <aws/core/utils/logging/LogMacros.h>
|
|
|
|
#include <windows.h>
|
|
|
|
using namespace Aws::Utils;
|
|
|
|
static void CALLBACK waveOutProc(HWAVEOUT waveOut, UINT uMsg, DWORD_PTR, DWORD_PTR dwParam1, DWORD_PTR)
|
|
{
|
|
switch (uMsg)
|
|
{
|
|
case WOM_CLOSE:
|
|
return;
|
|
case WOM_DONE:
|
|
waveOutUnprepareHeader(waveOut, (WAVEHDR*)dwParam1, sizeof(WAVEHDR));
|
|
Aws::DeleteArray(((WAVEHDR*)dwParam1)->lpData);
|
|
Aws::Delete((WAVEHDR*)dwParam1);
|
|
return;
|
|
case WOM_OPEN:
|
|
return;
|
|
default:
|
|
return;
|
|
|
|
}
|
|
}
|
|
|
|
namespace Aws
|
|
{
|
|
namespace TextToSpeech
|
|
{
|
|
static const char* CLASS_TAG = "WaveOutPCMOutputDriver";
|
|
|
|
WaveOutPCMOutputDriver::WaveOutPCMOutputDriver() : m_waveOut(nullptr) {}
|
|
|
|
WaveOutPCMOutputDriver::~WaveOutPCMOutputDriver()
|
|
{
|
|
if (m_waveOut)
|
|
{
|
|
waveOutClose(m_waveOut);
|
|
m_waveOut = nullptr;
|
|
}
|
|
}
|
|
|
|
bool WaveOutPCMOutputDriver::WriteBufferToDevice(const unsigned char* buffer, size_t size)
|
|
{
|
|
InitDevice();
|
|
|
|
WAVEHDR* waveHdr = Aws::New<WAVEHDR>(CLASS_TAG);
|
|
char* buf = Aws::NewArray<char>(size, CLASS_TAG);
|
|
memcpy(buf, buffer, size);
|
|
waveHdr->lpData = buf;
|
|
waveHdr->dwBufferLength = static_cast<DWORD>(size);
|
|
waveHdr->dwFlags = 0;
|
|
waveHdr->dwLoops = 0;
|
|
waveHdr->dwUser = NULL;
|
|
|
|
std::lock_guard<std::recursive_mutex> m(m_driverLock);
|
|
if (m_waveOut)
|
|
{
|
|
auto res = waveOutPrepareHeader(m_waveOut, waveHdr, sizeof(WAVEHDR));
|
|
if (res != MMSYSERR_NOERROR)
|
|
{
|
|
AWS_LOGSTREAM_ERROR(CLASS_TAG, "Error code " << res << " returned from waveOutPrepareHeader");
|
|
return false;
|
|
}
|
|
|
|
res = waveOutWrite(m_waveOut, waveHdr, sizeof(WAVEHDR));
|
|
if (res != MMSYSERR_NOERROR)
|
|
{
|
|
AWS_LOGSTREAM_ERROR(CLASS_TAG, "Error code " << res << " returned from waveOutWrite");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
const char* WaveOutPCMOutputDriver::GetName() const
|
|
{
|
|
return "Win32 WaveOut";
|
|
}
|
|
|
|
void WaveOutPCMOutputDriver::InitDevice()
|
|
{
|
|
if (!m_isInit)
|
|
{
|
|
AWS_LOGSTREAM_INFO(CLASS_TAG, "Initializing device " << m_activeDevice.deviceName);
|
|
std::lock_guard<std::recursive_mutex> m(m_driverLock);
|
|
if (m_waveOut)
|
|
{
|
|
AWS_LOGSTREAM_TRACE(CLASS_TAG, "Cleaning up current device ");
|
|
waveOutClose(m_waveOut);
|
|
m_waveOut = nullptr;
|
|
}
|
|
|
|
WAVEFORMATEX format;
|
|
format.nChannels = static_cast<WORD>(m_selectedCaps.channels);
|
|
format.nSamplesPerSec = static_cast<DWORD>(m_selectedCaps.sampleRate);
|
|
format.wBitsPerSample = static_cast<WORD>(m_selectedCaps.sampleWidthBits);
|
|
format.wFormatTag = WAVE_FORMAT_PCM;
|
|
format.nBlockAlign = (format.nChannels * format.wBitsPerSample) / 8;
|
|
format.cbSize = 0;
|
|
format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign;
|
|
|
|
if (m_activeDevice.deviceId.empty())
|
|
{
|
|
AWS_LOGSTREAM_INFO(CLASS_TAG, "No device configured, letting windows figure out the best default.");
|
|
|
|
auto res = waveOutOpen(&m_waveOut, WAVE_MAPPER, &format, (DWORD_PTR)&waveOutProc, NULL,
|
|
CALLBACK_FUNCTION | WAVE_ALLOWSYNC | WAVE_MAPPED_DEFAULT_COMMUNICATION_DEVICE);
|
|
m_isInit = !res;
|
|
}
|
|
else
|
|
{
|
|
unsigned id = static_cast<unsigned>(StringUtils::ConvertToInt32(m_activeDevice.deviceId.c_str()));
|
|
auto res = waveOutOpen(&m_waveOut, id, &format, (DWORD_PTR)&waveOutProc, NULL,
|
|
CALLBACK_FUNCTION | WAVE_ALLOWSYNC);
|
|
m_isInit = !res;
|
|
}
|
|
|
|
if (!m_isInit)
|
|
{
|
|
AWS_LOGSTREAM_ERROR(CLASS_TAG, "Failed to initialize device");
|
|
}
|
|
}
|
|
}
|
|
|
|
void WaveOutPCMOutputDriver::SetActiveDevice(const DeviceInfo& device, const CapabilityInfo& caps)
|
|
{
|
|
std::lock_guard<std::recursive_mutex> m(m_driverLock);
|
|
m_activeDevice = device;
|
|
m_selectedCaps = caps;
|
|
m_isInit = false;
|
|
InitDevice();
|
|
}
|
|
|
|
Aws::Vector<DeviceInfo> WaveOutPCMOutputDriver::EnumerateDevices() const
|
|
{
|
|
Aws::Vector<DeviceInfo> devices;
|
|
|
|
auto deviceCount = waveOutGetNumDevs();
|
|
|
|
for (UINT i = 0; i < deviceCount; ++i)
|
|
{
|
|
WAVEOUTCAPSA waveoutCaps;
|
|
auto res = waveOutGetDevCapsA(i, &waveoutCaps, sizeof(WAVEOUTCAPSA));
|
|
|
|
if (!res)
|
|
{
|
|
DeviceInfo devInfo;
|
|
devInfo.deviceId = StringUtils::to_string(i);
|
|
devInfo.deviceName = waveoutCaps.szPname;
|
|
|
|
if ((waveoutCaps.dwFormats & WAVE_FORMAT_1M16) == WAVE_FORMAT_1M16)
|
|
{
|
|
CapabilityInfo capsInfo;
|
|
capsInfo.channels = MONO;
|
|
capsInfo.sampleRate = KHZ_8;
|
|
capsInfo.sampleWidthBits = BIT_WIDTH_16;
|
|
|
|
devInfo.capabilities.push_back(capsInfo);
|
|
|
|
capsInfo.channels = MONO;
|
|
capsInfo.sampleRate = KHZ_16;
|
|
capsInfo.sampleWidthBits = BIT_WIDTH_16;
|
|
|
|
devInfo.capabilities.push_back(capsInfo);
|
|
}
|
|
|
|
if ((waveoutCaps.dwFormats & WAVE_FORMAT_2M16) == WAVE_FORMAT_2M16)
|
|
{
|
|
CapabilityInfo capsInfo;
|
|
capsInfo.channels = MONO;
|
|
capsInfo.sampleRate = KHZ_22_5;
|
|
capsInfo.sampleWidthBits = BIT_WIDTH_16;
|
|
|
|
devInfo.capabilities.push_back(capsInfo);
|
|
}
|
|
|
|
devices.push_back(devInfo);
|
|
}
|
|
}
|
|
|
|
return devices;
|
|
}
|
|
}
|
|
} |