1 //
2 // libtgvoip is free and unencumbered public domain software.
3 // For more information, see http://unlicense.org or the UNLICENSE file
4 // you should have received with this source code distribution.
5 //
6
7 #include <stdlib.h>
8 #include <stdio.h>
9 #include <assert.h>
10 #include "AudioInputWave.h"
11 #include "../../logging.h"
12 #include "../../VoIPController.h"
13
14 using namespace tgvoip::audio;
15
16 #define BUFFER_SIZE 960
17 #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;}
18
AudioInputWave(std::string deviceID)19 AudioInputWave::AudioInputWave(std::string deviceID){
20 isRecording=false;
21
22 for(int i=0;i<4;i++){
23 ZeroMemory(&buffers[i], sizeof(WAVEHDR));
24 buffers[i].dwBufferLength=960*2;
25 buffers[i].lpData=(char*)malloc(960*2);
26 }
27
28 hWaveIn=NULL;
29
30 SetCurrentDevice(deviceID);
31 }
32
~AudioInputWave()33 AudioInputWave::~AudioInputWave(){
34 for(int i=0;i<4;i++){
35 free(buffers[i].lpData);
36 }
37 waveInClose(hWaveIn);
38 }
39
Start()40 void AudioInputWave::Start(){
41 if(!isRecording){
42 isRecording=true;
43
44 MMRESULT res;
45 for(int i=0;i<4;i++){
46 res=waveInPrepareHeader(hWaveIn, &buffers[i], sizeof(WAVEHDR));
47 CHECK_ERROR(res, "waveInPrepareHeader failed");
48 res=waveInAddBuffer(hWaveIn, &buffers[i], sizeof(WAVEHDR));
49 CHECK_ERROR(res, "waveInAddBuffer failed");
50 }
51 res=waveInStart(hWaveIn);
52 CHECK_ERROR(res, "waveInStart failed");
53 }
54 }
55
Stop()56 void AudioInputWave::Stop(){
57 if(isRecording){
58 isRecording=false;
59
60 MMRESULT res=waveInStop(hWaveIn);
61 CHECK_ERROR(res, "waveInStop failed");
62 res=waveInReset(hWaveIn);
63 CHECK_ERROR(res, "waveInReset failed");
64 for(int i=0;i<4;i++){
65 res=waveInUnprepareHeader(hWaveIn, &buffers[i], sizeof(WAVEHDR));
66 CHECK_ERROR(res, "waveInUnprepareHeader failed");
67 }
68 }
69 }
70
WaveInProc(HWAVEIN hwi,UINT uMsg,DWORD_PTR dwInstance,DWORD_PTR dwParam1,DWORD_PTR dwParam2)71 void CALLBACK AudioInputWave::WaveInProc(HWAVEIN hwi, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2){
72 if(uMsg==WIM_DATA){
73 ((AudioInputWave*)dwInstance)->OnData((WAVEHDR*)dwParam1);
74 }
75 }
76
OnData(WAVEHDR * hdr)77 void AudioInputWave::OnData(WAVEHDR* hdr){
78 if(!isRecording)
79 return;
80
81 InvokeCallback((unsigned char*)hdr->lpData, hdr->dwBufferLength);
82 hdr->dwFlags&= ~WHDR_DONE;
83 MMRESULT res=waveInAddBuffer(hWaveIn, hdr, sizeof(WAVEHDR));
84 CHECK_ERROR(res, "waveInAddBuffer failed");
85 }
86
EnumerateDevices(std::vector<tgvoip::AudioInputDevice> & devs)87 void AudioInputWave::EnumerateDevices(std::vector<tgvoip::AudioInputDevice>& devs){
88 UINT num=waveInGetNumDevs();
89 WAVEINCAPSW caps;
90 char nameBuf[512];
91 for(UINT i=0;i<num;i++){
92 waveInGetDevCapsW(i, &caps, sizeof(caps));
93 AudioInputDevice dev;
94 WideCharToMultiByte(CP_UTF8, 0, caps.szPname, -1, nameBuf, sizeof(nameBuf), NULL, NULL);
95 dev.displayName=std::string(nameBuf);
96 dev.id=std::string(nameBuf);
97 devs.push_back(dev);
98 }
99 }
100
SetCurrentDevice(std::string deviceID)101 void AudioInputWave::SetCurrentDevice(std::string deviceID){
102 currentDevice=deviceID;
103
104 bool wasRecording=isRecording;
105 isRecording=false;
106 if(hWaveIn){
107 MMRESULT res;
108 if(isRecording){
109 res=waveInStop(hWaveIn);
110 CHECK_ERROR(res, "waveInStop failed");
111 res=waveInReset(hWaveIn);
112 CHECK_ERROR(res, "waveInReset failed");
113 for(int i=0;i<4;i++){
114 res=waveInUnprepareHeader(hWaveIn, &buffers[i], sizeof(WAVEHDR));
115 CHECK_ERROR(res, "waveInUnprepareHeader failed");
116 }
117 }
118 res=waveInClose(hWaveIn);
119 CHECK_ERROR(res, "waveInClose failed");
120 }
121
122 ZeroMemory(&format, sizeof(format));
123 format.cbSize=0;
124 format.wFormatTag=WAVE_FORMAT_PCM;
125 format.nSamplesPerSec=48000;
126 format.wBitsPerSample=16;
127 format.nChannels=1;
128 format.nBlockAlign=2;
129
130 LOGV("before open device %s", deviceID.c_str());
131
132 if(deviceID=="default"){
133 MMRESULT res=waveInOpen(&hWaveIn, WAVE_MAPPER, &format, (DWORD_PTR)AudioInputWave::WaveInProc, (DWORD_PTR)this, CALLBACK_FUNCTION);
134 CHECK_ERROR(res, "waveInOpen failed");
135 }else{
136 UINT num=waveInGetNumDevs();
137 WAVEINCAPSW caps;
138 char nameBuf[512];
139 hWaveIn=NULL;
140 for(UINT i=0;i<num;i++){
141 waveInGetDevCapsW(i, &caps, sizeof(caps));
142 WideCharToMultiByte(CP_UTF8, 0, caps.szPname, -1, nameBuf, sizeof(nameBuf), NULL, NULL);
143 std::string name=std::string(nameBuf);
144 if(name==deviceID){
145 MMRESULT res=waveInOpen(&hWaveIn, i, &format, (DWORD_PTR)AudioInputWave::WaveInProc, (DWORD_PTR)this, CALLBACK_FUNCTION | WAVE_MAPPED);
146 CHECK_ERROR(res, "waveInOpen failed");
147 LOGD("Opened device %s", nameBuf);
148 break;
149 }
150 }
151 if(!hWaveIn){
152 SetCurrentDevice("default");
153 return;
154 }
155 }
156
157 isRecording=wasRecording;
158
159 if(isRecording){
160 MMRESULT res;
161 for(int i=0;i<4;i++){
162 res=waveInPrepareHeader(hWaveIn, &buffers[i], sizeof(WAVEHDR));
163 CHECK_ERROR(res, "waveInPrepareHeader failed");
164 res=waveInAddBuffer(hWaveIn, &buffers[i], sizeof(WAVEHDR));
165 CHECK_ERROR(res, "waveInAddBuffer failed");
166 }
167 res=waveInStart(hWaveIn);
168 CHECK_ERROR(res, "waveInStart failed");
169 }
170 }