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 }