source: trunk/src/winmm/waveindart.cpp

Last change on this file was 21916, checked in by dmik, 14 years ago

Merge branch gcc-kmk to trunk.

File size: 19.8 KB
Line 
1/* $Id: waveindart.cpp,v 1.7 2003-07-28 11:30:44 sandervl Exp $ */
2
3/*
4 * Wave record class
5 *
6 * Copyright 2001 Sander van Leeuwen (sandervl@xs4all.nl)
7 *
8 * TODO: mulaw, alaw & adpcm
9 *
10 * Project Odin Software License can be found in LICENSE.TXT
11 *
12 */
13
14/******************************************************************************/
15// Includes
16/******************************************************************************/
17
18#define INCL_BASE
19#define INCL_OS2MM
20#include <os2wrap.h> //Odin32 OS/2 api wrappers
21#include <os2mewrap.h> //Odin32 OS/2 MMPM/2 api wrappers
22#include <stdlib.h>
23#include <string.h>
24#define OS2_ONLY
25#include <win32api.h>
26#include <wprocess.h>
27
28#include "misc.h"
29#include "waveindart.h"
30#include "initterm.h"
31
32#define DBG_LOCALLOG DBG_waveindart
33#include "dbglocal.h"
34
35#ifndef min
36#define min(a, b) ((a > b) ? b : a)
37#endif
38
39LONG APIENTRY WaveInHandler(ULONG ulStatus, PMCI_MIX_BUFFER pBuffer, ULONG ulFlags);
40
41/******************************************************************************/
42/******************************************************************************/
43
44static BOOL fwaveInFixedBuffers = FALSE;
45
46/******************************************************************************/
47/******************************************************************************/
48// ODIN_waveInSetFixedBuffers
49//
50// Tell WINMM to use DART buffers of the same size as the first buffer delivered
51// by waveInAddBuffer
52
53void WIN32API ODIN_waveInSetFixedBuffers()
54{
55 fwaveInFixedBuffers = TRUE;
56}
57/******************************************************************************/
58/******************************************************************************/
59DartWaveIn::DartWaveIn(LPWAVEFORMATEX pwfx, ULONG fdwOpen, ULONG nCallback, ULONG dwInstance)
60 : WaveInOut(pwfx, fdwOpen, nCallback, dwInstance)
61{
62 DeviceId = 0;
63 ulBufSize = DART_BUFSIZE_REC;
64 fMixerSetup = FALSE;
65 fOverrun = FALSE;
66 mixHandle = 0;
67 pmixReadProc = NULL;
68 MixBuffer = NULL;
69 BufferParms = NULL;
70
71 dprintf(("DartWaveIn: samplerate %d, numChan %d bps %d (%d), format %x",
72 SampleRate, nChannels, BitsPerSample, pwfx->nBlockAlign, pwfx->wFormatTag));
73}
74
75/******************************************************************************/
76/******************************************************************************/
77MMRESULT DartWaveIn::open()
78{
79 MCI_AMP_OPEN_PARMS AmpOpenParms;
80 MCI_GENERIC_PARMS GenericParms = {0};
81 MCI_MIXSETUP_PARMS MixSetupParms;
82 APIRET rc;
83
84 MixBuffer = (MCI_MIX_BUFFER *)malloc(PREFILLBUF_DART_REC*sizeof(MCI_MIX_BUFFER));
85 BufferParms = (MCI_BUFFER_PARMS *)malloc(sizeof(MCI_BUFFER_PARMS));
86 if(!MixBuffer || !BufferParms) {
87 dprintf(("ERROR: malloc failed!!"));
88 return MMSYSERR_NOMEM;
89 }
90
91 // Setup the open structure, pass the playlist and tell MCI_OPEN to use it
92 memset(&AmpOpenParms, 0, sizeof(AmpOpenParms));
93 AmpOpenParms.usDeviceID = 0;
94 AmpOpenParms.pszDeviceType = (PSZ)MCI_DEVTYPE_AUDIO_AMPMIX;
95
96 rc = mymciSendCommand(0, MCI_OPEN,
97 MCI_WAIT | MCI_OPEN_TYPE_ID | MCI_OPEN_SHAREABLE,
98 (PVOID) &AmpOpenParms, 0);
99 if(rc) {
100 dprintf(("MCI_OPEN failed\n"));
101 mciError(rc);
102 return MMSYSERR_NODRIVER;
103 }
104
105 DeviceId = AmpOpenParms.usDeviceID;
106
107 //Grab exclusive rights to device instance (NOT entire device)
108 rc = mymciSendCommand(DeviceId, MCI_ACQUIREDEVICE,
109 MCI_EXCLUSIVE_INSTANCE,
110 (PVOID)&GenericParms, 0);
111 if(rc) {
112 dprintf(("MCI_ACQUIREDEVICE failed\n"));
113 mciError(rc);
114 return MMSYSERR_NOTENABLED;
115 }
116
117 dprintf(("device acquired\n"));
118 dprintf(("bps %d, sps %d chan %d\n", BitsPerSample, SampleRate, nChannels));
119
120 // Set the MixSetupParms data structure
121 memset(&MixSetupParms, 0, sizeof(MCI_MIXSETUP_PARMS));
122 MixSetupParms.ulBitsPerSample = BitsPerSample;
123 MixSetupParms.ulSamplesPerSec = SampleRate;
124 MixSetupParms.ulFormatTag = MCI_WAVE_FORMAT_PCM;
125 MixSetupParms.ulChannels = nChannels;
126 MixSetupParms.ulFormatMode = MCI_RECORD;
127 MixSetupParms.ulDeviceType = MCI_DEVTYPE_WAVEFORM_AUDIO;
128 MixSetupParms.pmixEvent = WaveInHandler;
129
130 rc = mymciSendCommand(DeviceId, MCI_MIXSETUP,
131 MCI_WAIT | MCI_MIXSETUP_INIT,
132 (PVOID)&MixSetupParms, 0);
133 if ( rc != MCIERR_SUCCESS ) {
134 mymciSendCommand(DeviceId, MCI_RELEASEDEVICE,
135 MCI_WAIT,
136 (PVOID)&GenericParms, 0);
137 mciError(rc);
138 return MMSYSERR_NOTSUPPORTED;
139 }
140
141 // Save the mixer handle & the ptr to the read proc.
142 mixHandle = MixSetupParms.ulMixHandle;
143 pmixReadProc = MixSetupParms.pmixRead;
144
145
146 callback(WIM_OPEN, 0, 0);
147
148 return MMSYSERR_NOERROR;
149}
150/******************************************************************************/
151/******************************************************************************/
152DartWaveIn::~DartWaveIn()
153{
154 MCI_GENERIC_PARMS GenericParms = {0};
155
156 State = STATE_STOPPED;
157
158 if (DeviceId)
159 {
160 // Stop recording.
161 mymciSendCommand(DeviceId, MCI_STOP,
162 MCI_WAIT, (PVOID)&GenericParms, 0);
163
164 if (fMixerSetup)
165 mymciSendCommand(DeviceId, MCI_BUFFER,
166 MCI_WAIT | MCI_DEALLOCATE_MEMORY,
167 (PVOID)&BufferParms, 0);
168
169 // Close the device
170 mymciSendCommand(DeviceId, MCI_CLOSE,
171 MCI_WAIT,
172 (PVOID)&GenericParms, 0);
173
174 callback(WIM_CLOSE, 0, 0);
175 }
176
177 if(MixBuffer)
178 free(MixBuffer);
179 if(BufferParms)
180 free(BufferParms);
181}
182/******************************************************************************/
183/******************************************************************************/
184MMRESULT DartWaveIn::start()
185{
186 MCI_GENERIC_PARMS GenericParms = {0};
187 MCI_AMP_OPEN_PARMS AmpOpenParms;
188 MCI_CONNECTOR_PARMS ConnectorParms;
189 MCI_AMP_SET_PARMS AmpSetParms ;
190 APIRET rc;
191 int i, curbuf, buflength;
192
193 dprintf(("DartWaveIn::start"));
194 if(State == STATE_RECORDING)
195 return(MMSYSERR_NOERROR);
196
197 //if the app hasn't yet given us any buffers, then we can't possibly determine the
198 //right buffer size for DART recording, so postpone recording until the
199 //first buffer has been added
200 if(wavehdr == NULL) {
201 wmutex.enter();
202 State = STATE_POSTPONE_RECORDING;
203 wmutex.leave();
204 return MMSYSERR_NOERROR;
205 }
206
207 if(fMixerSetup == FALSE)
208 {
209 /*
210 * Set up the BufferParms data structure and allocate
211 * device buffers from the Amp-Mixer
212 */
213
214 int consumerate = getAvgBytesPerSecond();
215 //SvL: Recording usually isn't as sensitive to timing as playback
216 // Dividing by 8 instead of 32 here. Change if necessary.
217 int minbufsize = consumerate/8;
218
219 LPWAVEHDR pwh = wavehdr;
220 if(pwh) {
221 if(fwaveInFixedBuffers) {
222 ulBufSize = pwh->dwBufferLength;
223 }
224 else
225 {
226 dprintf(("mix setup %d, %d\n", pwh->dwBufferLength, pwh->dwBufferLength));
227 ulBufSize = pwh->dwBufferLength/2;
228 }
229 if(ulBufSize > minbufsize) {
230 dprintf(("set buffer size to %d bytes (org size = %d)", minbufsize, pwh->dwBufferLength));
231 ulBufSize = minbufsize;
232 }
233 }
234 else ulBufSize = minbufsize;
235
236 BufferParms->ulNumBuffers = PREFILLBUF_DART_REC;
237 BufferParms->ulBufferSize = ulBufSize;
238 BufferParms->pBufList = MixBuffer;
239
240 for(i=0;i<PREFILLBUF_DART_REC;i++) {
241 MixBuffer[i].ulUserParm = (ULONG)this;
242 }
243
244 rc = mymciSendCommand(DeviceId, MCI_BUFFER,
245 MCI_WAIT | MCI_ALLOCATE_MEMORY,
246 (PVOID)BufferParms, 0);
247
248 if(ULONG_LOWD(rc) != MCIERR_SUCCESS) {
249 mciError(rc);
250 mymciSendCommand(DeviceId, MCI_RELEASEDEVICE,
251 MCI_WAIT,
252 (PVOID)&GenericParms, 0);
253 return MMSYSERR_NOTSUPPORTED;
254 }
255
256 //TODO: don't do this here. Select line or mic depending on aux settings
257 //(until we really support the mixer extensions anyway)
258 /* Set the connector to 'microphone' */
259 memset(&ConnectorParms, 0, sizeof(MCI_CONNECTOR_PARMS));
260 ConnectorParms.ulConnectorType = MCI_MICROPHONE_CONNECTOR;
261 rc = mymciSendCommand(DeviceId, MCI_CONNECTOR,
262 MCI_WAIT | MCI_ENABLE_CONNECTOR | MCI_CONNECTOR_TYPE,
263 (PVOID)&ConnectorParms, 0 );
264
265 /* Allow the user to hear what is being recorded
266 * by turning the monitor on
267 */
268 memset(&AmpSetParms, 0, sizeof( MCI_AMP_SET_PARMS ) );
269 AmpSetParms.ulItem = MCI_AMP_SET_MONITOR;
270 rc = mymciSendCommand(DeviceId, MCI_SET,
271 MCI_WAIT | MCI_SET_ON | MCI_SET_ITEM,
272 (PVOID)&AmpSetParms, 0);
273
274 wmutex.enter();
275 fMixerSetup = TRUE;
276 }
277
278 for(i=0; i< PREFILLBUF_DART_REC; i++) {
279 memset(MixBuffer[i].pBuffer, 0, MixBuffer[i].ulBufferLength);
280 }
281 dprintf(("Dart opened, bufsize = %d\n", MixBuffer[0].ulBufferLength));
282
283 State = STATE_RECORDING;
284 fOverrun = FALSE;
285 wmutex.leave();
286
287 dprintf(("Dart recording"));
288
289 // Start recording.
290 USHORT selTIB = RestoreOS2FS(); // save current FS selector
291 pmixReadProc(mixHandle, &MixBuffer[0], PREFILLBUF_DART_REC);
292 SetFS(selTIB); // switch back to the saved FS selector
293
294 return(MMSYSERR_NOERROR);
295}
296/******************************************************************************/
297/******************************************************************************/
298MMRESULT DartWaveIn::stop()
299{
300 MCI_GENERIC_PARMS Params;
301
302 wmutex.enter();
303 if(State != STATE_RECORDING) {
304 dprintf(("Not recording!!"));
305 State = STATE_STOPPED;
306 wmutex.leave();
307 return(MMSYSERR_NOERROR);
308 }
309
310 State = STATE_STOPPED;
311 wmutex.leave();
312
313 memset(&Params, 0, sizeof(Params));
314
315 // Stop recording.
316 mymciSendCommand(DeviceId, MCI_STOP, MCI_WAIT, (PVOID)&Params, 0);
317
318 return(MMSYSERR_NOERROR);
319}
320/******************************************************************************/
321/******************************************************************************/
322MMRESULT DartWaveIn::reset()
323{
324 MCI_GENERIC_PARMS Params;
325 LPWAVEHDR tmpwavehdr;
326
327 dprintf(("DartWaveIn::reset %s", (State == STATE_RECORDING) ? "recording" : "stopped"));
328
329 wmutex.enter();
330 if(State == STATE_POSTPONE_RECORDING) {
331 dprintf(("reset postponed recording"));
332 State = STATE_STOPPED;
333 wmutex.leave();
334 return MMSYSERR_NOERROR;
335 }
336 wmutex.leave();
337
338 memset(&Params, 0, sizeof(Params));
339
340 // Stop recording
341 mymciSendCommand(DeviceId, MCI_STOP, MCI_WAIT, (PVOID)&Params, 0);
342
343 wmutex.enter();
344 while(wavehdr)
345 {
346 wavehdr->dwFlags |= WHDR_DONE;
347 wavehdr->dwFlags &= ~WHDR_INQUEUE;
348 tmpwavehdr = wavehdr;
349 wavehdr = wavehdr->lpNext;
350 tmpwavehdr->lpNext = NULL;
351
352 wmutex.leave();
353
354 callback(WIM_DATA, (ULONG)tmpwavehdr, 0);
355
356 wmutex.enter();
357 }
358 wavehdr = NULL;
359 State = STATE_STOPPED;
360 fOverrun = FALSE;
361
362 wmutex.leave();
363 return(MMSYSERR_NOERROR);
364}
365/******************************************************************************/
366/******************************************************************************/
367MMRESULT DartWaveIn::addBuffer(LPWAVEHDR pwh, UINT cbwh)
368{
369 int i;
370
371 wmutex.enter();
372 pwh->lpNext = NULL;
373 pwh->dwBytesRecorded = 0;
374 if(wavehdr) {
375 WAVEHDR *chdr = wavehdr;
376 while(chdr->lpNext) {
377 chdr = chdr->lpNext;
378 }
379 chdr->lpNext = pwh;
380 }
381 else wavehdr = pwh;
382 wmutex.leave();
383
384 if(State == STATE_POSTPONE_RECORDING) {
385 //if recording was postponed due to lack of buffers, then start now
386 start();
387 }
388 return(MMSYSERR_NOERROR);
389}
390/******************************************************************************/
391/******************************************************************************/
392ULONG DartWaveIn::getPosition()
393{
394 MCI_STATUS_PARMS mciStatus = {0};
395 ULONG rc, nrbytes;
396
397 if(State == STATE_STOPPED) {
398 dprintf(("Not recording; return 0 position"));
399 return 0;
400 }
401
402 mciStatus.ulItem = MCI_STATUS_POSITION;
403 rc = mymciSendCommand(DeviceId, MCI_STATUS, MCI_STATUS_ITEM|MCI_WAIT, (PVOID)&mciStatus, 0);
404 if((rc & 0xFFFF) == MCIERR_SUCCESS) {
405 nrbytes = (((double)mciStatus.ulReturn * (double)getAvgBytesPerSecond())/1000.0);
406 return nrbytes;;
407 }
408 mciError(rc);
409 return 0xFFFFFFFF;
410}
411/******************************************************************************/
412/******************************************************************************/
413int DartWaveIn::getNumDevices()
414{
415 MCI_GENERIC_PARMS GenericParms;
416 MCI_AMP_OPEN_PARMS AmpOpenParms;
417 APIRET rc;
418
419 // Setup the open structure, pass the playlist and tell MCI_OPEN to use it
420 memset(&AmpOpenParms,0,sizeof(AmpOpenParms));
421
422 AmpOpenParms.usDeviceID = ( USHORT ) 0;
423 AmpOpenParms.pszDeviceType = ( PSZ ) MCI_DEVTYPE_AUDIO_AMPMIX;
424
425 rc = mymciSendCommand(0, MCI_OPEN,
426 MCI_WAIT | MCI_OPEN_TYPE_ID | MCI_OPEN_SHAREABLE,
427 (PVOID) &AmpOpenParms,
428 0);
429
430 if(rc) {
431 return 0; //no devices present
432 }
433
434 // Generic parameters
435 GenericParms.hwndCallback = 0; //hwndFrame
436
437 // Close the device
438 mymciSendCommand(AmpOpenParms.usDeviceID, MCI_CLOSE, MCI_WAIT, (PVOID)&GenericParms, 0);
439
440 return 1;
441}
442/******************************************************************************/
443/******************************************************************************/
444BOOL DartWaveIn::queryFormat(ULONG formatTag, ULONG nChannels,
445 ULONG nSamplesPerSec, ULONG wBitsPerSample)
446{
447 MCI_WAVE_GETDEVCAPS_PARMS mciAudioCaps;
448 MCI_GENERIC_PARMS GenericParms;
449 MCI_OPEN_PARMS mciOpenParms; /* open parms for MCI_OPEN */
450 int i, freqbits = 0;
451 ULONG rc, DeviceId;
452 BOOL winrc;
453
454 dprintf(("DartWaveIn::queryFormat %x srate=%d, nchan=%d, bps=%d", formatTag, nSamplesPerSec, nChannels, wBitsPerSample));
455
456 memset(&mciOpenParms, /* Object to fill with zeros. */
457 0, /* Value to place into the object. */
458 sizeof( mciOpenParms ) ); /* How many zero's to use. */
459
460 mciOpenParms.pszDeviceType = (PSZ)MCI_DEVTYPE_WAVEFORM_AUDIO;
461
462 rc = mymciSendCommand( (USHORT) 0,
463 MCI_OPEN,
464 MCI_WAIT | MCI_OPEN_TYPE_ID,
465 (PVOID) &mciOpenParms,
466 0);
467 if((rc & 0xFFFF) != MCIERR_SUCCESS) {
468 mciError(rc);
469 return(FALSE);
470 }
471 DeviceId = mciOpenParms.usDeviceID;
472
473 memset( &mciAudioCaps , 0, sizeof(MCI_WAVE_GETDEVCAPS_PARMS));
474
475 mciAudioCaps.ulBitsPerSample = wBitsPerSample;
476 mciAudioCaps.ulFormatTag = DATATYPE_WAVEFORM;
477 mciAudioCaps.ulSamplesPerSec = nSamplesPerSec;
478 mciAudioCaps.ulChannels = nChannels;
479 mciAudioCaps.ulFormatMode = MCI_RECORD;
480 mciAudioCaps.ulItem = MCI_GETDEVCAPS_WAVE_FORMAT;
481
482 rc = mymciSendCommand(DeviceId, /* Device ID */
483 MCI_GETDEVCAPS,
484 MCI_WAIT | MCI_GETDEVCAPS_EXTENDED | MCI_GETDEVCAPS_ITEM,
485 (PVOID) &mciAudioCaps,
486 0);
487 if((rc & 0xFFFF) != MCIERR_SUCCESS) {
488 mciError(rc);
489 winrc = FALSE;
490 }
491 else winrc = TRUE;
492
493 // Close the device
494 mymciSendCommand(DeviceId,MCI_CLOSE,MCI_WAIT,(PVOID)&GenericParms,0);
495 return(winrc);
496}
497/******************************************************************************/
498/******************************************************************************/
499void DartWaveIn::mciError(ULONG rc)
500{
501#ifdef DEBUG
502 char szError[256] = "";
503
504 mymciGetErrorString(rc, szError, sizeof(szError));
505 dprintf(("WINMM: DartWaveIn: %s\n", szError));
506#endif
507}
508/******************************************************************************/
509//Simple implementation of recording. We fill up buffers queued by the application
510//until we run out of data or buffers. If we run out of buffers, the recorded data
511//is thrown away. We will continue when when the applications adds more buffers.
512/******************************************************************************/
513void DartWaveIn::handler(ULONG ulStatus, PMCI_MIX_BUFFER pBuffer, ULONG ulFlags)
514{
515 ULONG buflength, bufpos, bytestocopy;
516
517 dprintf2(("WINMM: DartWaveIn handler %x\n", pBuffer));
518 if(ulFlags == MIX_STREAM_ERROR) {
519 if(ulStatus == ERROR_DEVICE_OVERRUN) {
520 dprintf(("WINMM: ERROR: WaveIn handler ERROR_DEVICE_OVERRUN!\n"));
521 if(State == STATE_RECORDING) {
522 fOverrun = TRUE;
523 stop(); //out of buffers, so stop playback
524 }
525 return;
526 }
527 dprintf(("WINMM: WaveIn handler, Unknown error %X\n", ulStatus));
528 return;
529 }
530 wmutex.enter();
531
532 if(wavehdr == NULL) {
533 wmutex.leave();
534 //last buffer recorded -> no new ones -> overrun
535 //Windows doesn't care -> just continue
536 dprintf(("WINMM: WARNING: WaveIn handler OVERRUN!\n"));
537 return;
538 }
539
540 buflength = pBuffer->ulBufferLength;
541 bufpos = 0;
542 while(buflength) {
543 if(wavehdr) {
544 dprintf2(("WINMM: DartWaveIn handler: bytes recorded %d, buffer length %d, room %d", buflength, wavehdr->dwBufferLength, wavehdr->dwBytesRecorded));
545 bytestocopy = min(buflength, wavehdr->dwBufferLength - wavehdr->dwBytesRecorded);
546 if(bytestocopy) {
547 memcpy(wavehdr->lpData + wavehdr->dwBytesRecorded, (char *)pBuffer->pBuffer + bufpos, bytestocopy);
548 }
549 else DebugInt3(); //should never happen
550
551 bufpos += bytestocopy;
552 wavehdr->dwBytesRecorded += bytestocopy;
553 buflength -= bytestocopy;
554
555 if(wavehdr->dwBytesRecorded == wavehdr->dwBufferLength)
556 {
557 WAVEHDR *whdr = wavehdr;
558
559 dprintf2(("WINMM: DartWaveIn handler buf %X done\n", whdr));
560 whdr->dwFlags |= WHDR_DONE;
561 whdr->dwFlags &= ~WHDR_INQUEUE;
562
563 wavehdr = whdr->lpNext;
564 whdr->lpNext = NULL;
565 wmutex.leave();
566
567 callback(WIM_DATA, (ULONG)whdr, whdr->dwBytesRecorded);
568
569 wmutex.enter();
570 }
571 }
572 else break;
573 }
574 wmutex.leave();
575
576 //Transfer buffer to DART
577 // MCI_MIXSETUP_PARMS->pMixWrite does alter FS: selector!
578 USHORT selTIB = RestoreOS2FS(); // save current FS selector
579 pmixReadProc(mixHandle, pBuffer, 1);
580 SetFS(selTIB); // switch back to the saved FS selector
581}
582/******************************************************************************/
583/******************************************************************************/
584LONG APIENTRY WaveInHandler(ULONG ulStatus,
585 PMCI_MIX_BUFFER pBuffer,
586 ULONG ulFlags)
587{
588 DartWaveIn *dwave;
589
590 if(pBuffer && pBuffer->ulUserParm)
591 {
592 dwave = (DartWaveIn *)pBuffer->ulUserParm;
593 dwave->handler(ulStatus, pBuffer, ulFlags);
594 }
595 return(TRUE);
596}
597/******************************************************************************/
598/******************************************************************************/
599
Note: See TracBrowser for help on using the repository browser.