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

Last change on this file since 10185 was 10185, checked in by sandervl, 22 years ago

Updates

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