/** * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0. */ #include #include #include #include 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(CLASS_TAG); char* buf = Aws::NewArray(size, CLASS_TAG); memcpy(buf, buffer, size); waveHdr->lpData = buf; waveHdr->dwBufferLength = static_cast(size); waveHdr->dwFlags = 0; waveHdr->dwLoops = 0; waveHdr->dwUser = NULL; std::lock_guard 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 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(m_selectedCaps.channels); format.nSamplesPerSec = static_cast(m_selectedCaps.sampleRate); format.wBitsPerSample = static_cast(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(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 m(m_driverLock); m_activeDevice = device; m_selectedCaps = caps; m_isInit = false; InitDevice(); } Aws::Vector WaveOutPCMOutputDriver::EnumerateDevices() const { Aws::Vector 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; } } }