205 lines
5.9 KiB
C++
205 lines
5.9 KiB
C++
//
|
|
// libtgvoip is free and unencumbered public domain software.
|
|
// For more information, see http://unlicense.org or the UNLICENSE file
|
|
// you should have received with this source code distribution.
|
|
//
|
|
|
|
#include "AudioInputWave.h"
|
|
#include "../../VoIPController.h"
|
|
#include "../../logging.h"
|
|
#include <cassert>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
|
|
using namespace tgvoip::audio;
|
|
|
|
#define BUFFER_SIZE 960
|
|
#define CHECK_ERROR(res, msg) \
|
|
if (res != MMSYSERR_NOERROR) \
|
|
{ \
|
|
wchar_t _buf[1024]; \
|
|
waveInGetErrorTextW(res, _buf, 1024); \
|
|
LOGE(msg ": %ws (MMRESULT=0x%08X)", _buf, res); \
|
|
failed = true; \
|
|
}
|
|
|
|
AudioInputWave::AudioInputWave(std::string deviceID)
|
|
{
|
|
isRecording = false;
|
|
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
ZeroMemory(&buffers[i], sizeof(WAVEHDR));
|
|
buffers[i].dwBufferLength = 960 * 2;
|
|
buffers[i].lpData = reinterpret_cast<char*>(std::malloc(960 * 2));
|
|
}
|
|
|
|
hWaveIn = nullptr;
|
|
|
|
SetCurrentDevice(deviceID);
|
|
}
|
|
|
|
AudioInputWave::~AudioInputWave()
|
|
{
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
std::free(buffers[i].lpData);
|
|
}
|
|
waveInClose(hWaveIn);
|
|
}
|
|
|
|
void AudioInputWave::Start()
|
|
{
|
|
if (!isRecording)
|
|
{
|
|
isRecording = true;
|
|
|
|
MMRESULT res;
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
res = waveInPrepareHeader(hWaveIn, &buffers[i], sizeof(WAVEHDR));
|
|
CHECK_ERROR(res, "waveInPrepareHeader failed");
|
|
res = waveInAddBuffer(hWaveIn, &buffers[i], sizeof(WAVEHDR));
|
|
CHECK_ERROR(res, "waveInAddBuffer failed");
|
|
}
|
|
res = waveInStart(hWaveIn);
|
|
CHECK_ERROR(res, "waveInStart failed");
|
|
}
|
|
}
|
|
|
|
void AudioInputWave::Stop()
|
|
{
|
|
if (isRecording)
|
|
{
|
|
isRecording = false;
|
|
|
|
MMRESULT res = waveInStop(hWaveIn);
|
|
CHECK_ERROR(res, "waveInStop failed");
|
|
res = waveInReset(hWaveIn);
|
|
CHECK_ERROR(res, "waveInReset failed");
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
res = waveInUnprepareHeader(hWaveIn, &buffers[i], sizeof(WAVEHDR));
|
|
CHECK_ERROR(res, "waveInUnprepareHeader failed");
|
|
}
|
|
}
|
|
}
|
|
|
|
void CALLBACK AudioInputWave::WaveInProc(HWAVEIN hwi, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
|
|
{
|
|
if (uMsg == WIM_DATA)
|
|
{
|
|
((AudioInputWave*)dwInstance)->OnData((WAVEHDR*)dwParam1);
|
|
}
|
|
}
|
|
|
|
void AudioInputWave::OnData(WAVEHDR* hdr)
|
|
{
|
|
if (!isRecording)
|
|
return;
|
|
|
|
InvokeCallback(reinterpret_cast<std::uint8_t*>(hdr->lpData), hdr->dwBufferLength);
|
|
hdr->dwFlags &= ~WHDR_DONE;
|
|
MMRESULT res = waveInAddBuffer(hWaveIn, hdr, sizeof(WAVEHDR));
|
|
CHECK_ERROR(res, "waveInAddBuffer failed");
|
|
}
|
|
|
|
void AudioInputWave::EnumerateDevices(std::vector<tgvoip::AudioInputDevice>& devs)
|
|
{
|
|
UINT num = waveInGetNumDevs();
|
|
WAVEINCAPSW caps;
|
|
char nameBuf[512];
|
|
for (UINT i = 0; i < num; i++)
|
|
{
|
|
waveInGetDevCapsW(i, &caps, sizeof(caps));
|
|
AudioInputDevice dev;
|
|
WideCharToMultiByte(CP_UTF8, 0, caps.szPname, -1, nameBuf, sizeof(nameBuf), nullptr, nullptr);
|
|
dev.displayName = std::string(nameBuf);
|
|
dev.id = std::string(nameBuf);
|
|
devs.emplace_back(dev);
|
|
}
|
|
}
|
|
|
|
void AudioInputWave::SetCurrentDevice(std::string deviceID)
|
|
{
|
|
m_currentDevice = deviceID;
|
|
|
|
bool wasRecording = isRecording;
|
|
isRecording = false;
|
|
if (hWaveIn)
|
|
{
|
|
MMRESULT res;
|
|
if (isRecording)
|
|
{
|
|
res = waveInStop(hWaveIn);
|
|
CHECK_ERROR(res, "waveInStop failed");
|
|
res = waveInReset(hWaveIn);
|
|
CHECK_ERROR(res, "waveInReset failed");
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
res = waveInUnprepareHeader(hWaveIn, &buffers[i], sizeof(WAVEHDR));
|
|
CHECK_ERROR(res, "waveInUnprepareHeader failed");
|
|
}
|
|
}
|
|
res = waveInClose(hWaveIn);
|
|
CHECK_ERROR(res, "waveInClose failed");
|
|
}
|
|
|
|
ZeroMemory(&format, sizeof(format));
|
|
format.cbSize = 0;
|
|
format.wFormatTag = WAVE_FORMAT_PCM;
|
|
format.nSamplesPerSec = 48000;
|
|
format.wBitsPerSample = 16;
|
|
format.nChannels = 1;
|
|
format.nBlockAlign = 2;
|
|
|
|
LOGV("before open device %s", deviceID.c_str());
|
|
|
|
if (deviceID == "default")
|
|
{
|
|
MMRESULT res = waveInOpen(&hWaveIn, WAVE_MAPPER, &format, (DWORD_PTR)AudioInputWave::WaveInProc, (DWORD_PTR)this, CALLBACK_FUNCTION);
|
|
CHECK_ERROR(res, "waveInOpen failed");
|
|
}
|
|
else
|
|
{
|
|
UINT num = waveInGetNumDevs();
|
|
WAVEINCAPSW caps;
|
|
char nameBuf[512];
|
|
hWaveIn = nullptr;
|
|
for (UINT i = 0; i < num; i++)
|
|
{
|
|
waveInGetDevCapsW(i, &caps, sizeof(caps));
|
|
WideCharToMultiByte(CP_UTF8, 0, caps.szPname, -1, nameBuf, sizeof(nameBuf), nullptr, nullptr);
|
|
std::string name = std::string(nameBuf);
|
|
if (name == deviceID)
|
|
{
|
|
MMRESULT res = waveInOpen(&hWaveIn, i, &format, (DWORD_PTR)AudioInputWave::WaveInProc, (DWORD_PTR)this, CALLBACK_FUNCTION | WAVE_MAPPED);
|
|
CHECK_ERROR(res, "waveInOpen failed");
|
|
LOGD("Opened device %s", nameBuf);
|
|
break;
|
|
}
|
|
}
|
|
if (!hWaveIn)
|
|
{
|
|
SetCurrentDevice("default");
|
|
return;
|
|
}
|
|
}
|
|
|
|
isRecording = wasRecording;
|
|
|
|
if (isRecording)
|
|
{
|
|
MMRESULT res;
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
res = waveInPrepareHeader(hWaveIn, &buffers[i], sizeof(WAVEHDR));
|
|
CHECK_ERROR(res, "waveInPrepareHeader failed");
|
|
res = waveInAddBuffer(hWaveIn, &buffers[i], sizeof(WAVEHDR));
|
|
CHECK_ERROR(res, "waveInAddBuffer failed");
|
|
}
|
|
res = waveInStart(hWaveIn);
|
|
CHECK_ERROR(res, "waveInStart failed");
|
|
}
|
|
}
|