1 /*
2 * network_microphone_sdl_win32.cpp
3 * created for Marathon: Aleph One <http://source.bungie.org/>
4
5 Copyright (C) 2002 and beyond by Woody Zenfell, III
6 and the "Aleph One" developers.
7
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 This license is contained in the file "COPYING",
19 which is included with this source code; it is available online at
20 http://www.gnu.org/licenses/gpl.html
21
22 * The code in this file is licensed to you under the GNU GPL. As the copyright holder,
23 * however, I reserve the right to use this code as I see fit, without being bound by the
24 * GPL's terms. This exemption is not intended to apply to modified versions of this file -
25 * if I were to use a modified version, I would be a licensee of whomever modified it, and
26 * thus must observe the GPL terms.
27 *
28 * This is a DirectSoundCapture (Win32 DirectX)-based network microphone implementation.
29 * Other platforms will need their own implementations. A dummy implementation is
30 * provided in network_microphone_sdl_dummy.cpp for you to link against if you don't have
31 * a "real" implementation.
32 *
33 * Please factor out parts useful on other platforms rather than nearly-duplicating them.
34 *
35 * Created by woody Mar 3-8, 2002.
36 *
37 * Feb. 1, 2003 (Woody Zenfell):
38 * factored out parts useful on other platforms into network_microphone_shared.cpp
39 */
40
41 #if !defined(DISABLE_NETWORKING)
42
43 #include "cseries.h"
44 #include "network_speaker_sdl.h"
45 #include "network_microphone_shared.h"
46 #include "Logging.h"
47 #include <dsound.h>
48
49 #ifdef SPEEX
50 #include "preferences.h"
51 #include "network_speex.h"
52 #endif
53
54
55
56 static LPDIRECTSOUNDCAPTURE sDirectSoundCapture = NULL;
57 static LPDIRECTSOUNDCAPTUREBUFFER sCaptureBuffer = NULL;
58 static int sCaptureBufferSize = 0;
59 static bool sCaptureSystemReady = false;
60 static int sNextReadStartPosition = 0;
61 static int sCaptureFormatIndex = 0;
62 static bool sTransmittingAudio = false;
63
64
65
66 struct CaptureFormat {
67 uint32 mIdentifier;
68 uint32 mRate;
69 bool mStereo;
70 bool m16Bit;
71 };
72
73 // These are searched in the listed order for a format the hardware supports.
74 // List ONLY those with sample rates >= the network audio sample rate, because
75 // the format-translation routines can only downsample.
76 CaptureFormat sFormatPreferences[] = {
77 { WAVE_FORMAT_1M16, 11025, false, true },
78 { WAVE_FORMAT_1S16, 11025, true, true },
79 { WAVE_FORMAT_1M08, 11025, false, false },
80 { WAVE_FORMAT_1S08, 11025, true, false },
81 { WAVE_FORMAT_2M16, 22050, false, true },
82 { WAVE_FORMAT_2S16, 22050, true, true },
83 { WAVE_FORMAT_2M08, 22050, false, false },
84 { WAVE_FORMAT_2S08, 22050, true, false },
85 { WAVE_FORMAT_4M16, 44100, false, true },
86 { WAVE_FORMAT_4S16, 44100, true, true },
87 { WAVE_FORMAT_4M08, 44100, false, false },
88 { WAVE_FORMAT_4S08, 44100, true, false }
89 };
90
91 const int NUM_CAPTURE_FORMAT_PREFERENCES = sizeof(sFormatPreferences) / sizeof(sFormatPreferences[0]);
92
93
94
95 static void
transmit_captured_data(bool inForceSend)96 transmit_captured_data(bool inForceSend) {
97 // Find out how much data we can read
98 unsigned long theReadPosition;
99 sCaptureBuffer->GetCurrentPosition(&theReadPosition, NULL);
100
101 int32 theAmountOfDataAvailable = (sCaptureBufferSize + theReadPosition - sNextReadStartPosition) % sCaptureBufferSize;
102
103 // If we don't have a full packet's worth yet, don't send unless we're squeezing out the end.
104 if(theAmountOfDataAvailable >= get_capture_byte_count_per_packet() || inForceSend) {
105 // Lock capture buffer so we can copy audio out
106 void* theFirstChunkStart;
107 unsigned long theFirstChunkSize;
108 void* theSecondChunkStart;
109 unsigned long theSecondChunkSize;
110
111
112 sCaptureBuffer->Lock(sNextReadStartPosition, theAmountOfDataAvailable, &theFirstChunkStart, &theFirstChunkSize,
113 &theSecondChunkStart, &theSecondChunkSize, 0);
114
115 // This ought to be the same as theAmountOfDataAvailable, I'd think, but just in case...
116 int32 theAmountOfDataLocked = theFirstChunkSize + theSecondChunkSize;
117
118 // We might not actually use all of it, since we try to send in whole-packet increments.
119 int32 theAmountOfDataConsumed = copy_and_send_audio_data((uint8*) theFirstChunkStart, theFirstChunkSize,
120 (uint8*) theSecondChunkStart, theSecondChunkSize, inForceSend);
121
122 sNextReadStartPosition = (sNextReadStartPosition + theAmountOfDataConsumed) % sCaptureBufferSize;
123
124 sCaptureBuffer->Unlock(theFirstChunkStart, theFirstChunkSize, theSecondChunkStart, theSecondChunkSize);
125 }
126 }
127
128
129
130 OSErr
open_network_microphone()131 open_network_microphone() {
132 logContext("setting up microphone");
133
134 // We will probably do weird things if people open us twice without closing in between
135 assert(!sCaptureSystemReady);
136 assert(sDirectSoundCapture == NULL);
137 assert(sCaptureBuffer == NULL);
138
139 HRESULT theResult = DirectSoundCaptureCreate(NULL, &sDirectSoundCapture, NULL);
140
141 if(FAILED(theResult)) {
142 logAnomaly("DirectSoundCaptureCreate failed: %d", theResult);
143 }
144 else {
145 // See what capture formats the device supports
146 DSCCAPS theCaptureCapabilities;
147 ZeroMemory(&theCaptureCapabilities, sizeof(theCaptureCapabilities));
148 theCaptureCapabilities.dwSize = sizeof(theCaptureCapabilities);
149 sDirectSoundCapture->GetCaps(&theCaptureCapabilities);
150
151 // Find the most-preferred (earliest-listed) format the device supports
152 sCaptureFormatIndex = NONE;
153 for(int i = 0; i < NUM_CAPTURE_FORMAT_PREFERENCES; i++) {
154 if(theCaptureCapabilities.dwFormats & sFormatPreferences[i].mIdentifier) {
155 // Found a format the capture system supports
156 sCaptureFormatIndex = i;
157 break;
158 }
159 }
160
161 if(sCaptureFormatIndex == NONE) {
162 logAnomaly("No valid audio capture formats supported");
163 }
164 else {
165 CaptureFormat& theChosenFormat = sFormatPreferences[sCaptureFormatIndex];
166
167 WAVEFORMATEX theWaveFormat;
168 theWaveFormat.wFormatTag = WAVE_FORMAT_PCM;
169 theWaveFormat.nChannels = theChosenFormat.mStereo ? 2 : 1;
170 theWaveFormat.nSamplesPerSec = theChosenFormat.mRate;
171 theWaveFormat.wBitsPerSample = theChosenFormat.m16Bit ? 16 : 8;
172 theWaveFormat.nBlockAlign = theWaveFormat.nChannels * theWaveFormat.wBitsPerSample / 8;
173 theWaveFormat.nAvgBytesPerSec = theWaveFormat.nBlockAlign * theWaveFormat.nSamplesPerSec;
174 theWaveFormat.cbSize = 0;
175
176 // Let's try a half-second buffer.
177 sCaptureBufferSize = (theWaveFormat.nSamplesPerSec / 2) * theWaveFormat.nBlockAlign;
178
179 DSCBUFFERDESC theRecordingBufferDescription;
180 ZeroMemory(&theRecordingBufferDescription, sizeof(theRecordingBufferDescription));
181 theRecordingBufferDescription.dwSize = sizeof(theRecordingBufferDescription);
182 theRecordingBufferDescription.dwBufferBytes = sCaptureBufferSize;
183 theRecordingBufferDescription.lpwfxFormat = &theWaveFormat;
184
185 theResult = sDirectSoundCapture->CreateCaptureBuffer(&theRecordingBufferDescription, &sCaptureBuffer, NULL);
186
187 if(FAILED(theResult)) {
188 logAnomaly("CreateCaptureBuffer failed: %d", theResult);
189 }
190 else {
191 if(!announce_microphone_capture_format(theChosenFormat.mRate, theChosenFormat.mStereo, theChosenFormat.m16Bit)) {
192 logAnomaly("network microphone support code rejected audio format: %d samp/sec, %s, %d bits/samp",
193 theChosenFormat.mRate, theChosenFormat.mStereo ? "stereo" : "mono", theChosenFormat.m16Bit ? 16 : 8);
194 }
195 else {
196 // Initialization successful
197 sCaptureSystemReady = true;
198 }
199 }
200 }
201 }
202
203 #ifdef SPEEX
204 if (network_preferences->use_speex_encoder) {
205 init_speex_encoder();
206 }
207 #endif
208
209 if(!sCaptureSystemReady)
210 logAnomaly("Could not set up DirectSoundCapture - network microphone disabled");
211
212 // Here we _lie_, for now, to conform to the same interface the Mac code uses.
213 return 0;
214 }
215
216
217
218 static void
start_transmitting_audio()219 start_transmitting_audio() {
220 assert(sCaptureSystemReady);
221 assert(!sTransmittingAudio);
222
223 HRESULT theResult = sCaptureBuffer->Start(DSCBSTART_LOOPING);
224
225 sTransmittingAudio = (theResult == DS_OK);
226 }
227
228
229
230 static void
stop_transmitting_audio()231 stop_transmitting_audio() {
232 assert(sCaptureSystemReady);
233 assert(sTransmittingAudio);
234
235 HRESULT theResult = sCaptureBuffer->Stop();
236
237 sTransmittingAudio = !(theResult == DS_OK);
238
239 // Flush out the last chunk of stuff
240 transmit_captured_data(true);
241 }
242
243
244
245 void
close_network_microphone()246 close_network_microphone() {
247 if(sCaptureSystemReady && sTransmittingAudio)
248 stop_transmitting_audio();
249
250 if(sCaptureBuffer != NULL) {
251 sCaptureBuffer->Release();
252 sCaptureBuffer = NULL;
253 }
254
255 if(sDirectSoundCapture != NULL) {
256 sDirectSoundCapture->Release();
257 sDirectSoundCapture = NULL;
258 }
259
260 #ifdef SPEEX
261 if (network_preferences->use_speex_encoder) {
262 destroy_speex_encoder();
263 }
264 #endif
265
266 sCaptureSystemReady = false;
267 }
268
269
270
271 void
set_network_microphone_state(bool inActive)272 set_network_microphone_state(bool inActive) {
273 if(sCaptureSystemReady) {
274 if(inActive && !sTransmittingAudio)
275 start_transmitting_audio();
276 else if(!inActive && sTransmittingAudio)
277 stop_transmitting_audio();
278 }
279 }
280
281
282
283 bool
is_network_microphone_implemented()284 is_network_microphone_implemented() {
285 return true;
286 }
287
288
289
290 bool
has_sound_input_capability()291 has_sound_input_capability() {
292 // We really probably should actually _check_.
293 return true;
294 }
295
296
297
298 void
network_microphone_idle_proc()299 network_microphone_idle_proc() {
300 if(sCaptureSystemReady && sTransmittingAudio)
301 transmit_captured_data(false);
302 }
303
304 #endif // !defined(DISABLE_NETWORKING)
305