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