source: trunk/src/winmm/waveindart.cpp@ 8572

Last change on this file since 8572 was 8572, checked in by sandervl, 23 years ago

Postpone recording when waveInStart called without any buffers in the queue. Start in during next waveInAddBuffer call

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