Retired Document
Important: This sample code may not represent best practices for current development. The project may use deprecated symbols and illustrate technologies and techniques that are no longer recommended.
PCISoundInputDriver.c
/* |
File: PCISoundInputDriver.c |
Contains: Sample PCI sound input hardware driver |
Author: <MC> |
Copyright: © Copyright 1996-2001 Apple Computer, Inc. All rights reserved. |
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. |
("Apple") in consideration of your agreement to the following terms, and your |
use, installation, modification or redistribution of this Apple software |
constitutes acceptance of these terms. If you do not agree with these terms, |
please do not use, install, modify or redistribute this Apple software. |
In consideration of your agreement to abide by the following terms, and subject |
to these terms, Apple grants you a personal, non-exclusive license, under AppleÕs |
copyrights in this original Apple software (the "Apple Software"), to use, |
reproduce, modify and redistribute the Apple Software, with or without |
modifications, in source and/or binary forms; provided that if you redistribute |
the Apple Software in its entirety and without modifications, you must retain |
this notice and the following text and disclaimers in all such redistributions of |
the Apple Software. Neither the name, trademarks, service marks or logos of |
Apple Computer, Inc. may be used to endorse or promote products derived from the |
Apple Software without specific prior written permission from Apple. Except as |
expressly stated in this notice, no other rights or licenses, express or implied, |
are granted by Apple herein, including but not limited to any patent rights that |
may be infringed by your derivative works or by other works in which the Apple |
Software may be incorporated. |
The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO |
WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED |
WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN |
COMBINATION WITH YOUR PRODUCTS. |
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR |
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE |
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION |
OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT |
(INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN |
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
Change History (most recent first): |
05/02/01 JAS Fixed problems in HWIntProc. Fixed bug in DriverStausCmd:siInputSourceNames |
to return a copy of the handle to the input source names. Updated to CW Pro 6. |
8/3/1999 KG Updated for Metrowerks Codewarror Pro 2.1 |
*/ |
#include <Aliases.h> |
#include <DriverServices.h> |
#include <DriverGestalt.h> |
#include <NameRegistry.h> |
#include <Resources.h> |
#include <Sound.h> |
#include <SoundInput.h> |
#include <TextUtils.h> |
#include <Timer.h> |
#include <stdio.h> |
#include <Strings.h> |
// This is what our input parameter looks like |
#pragma options align=mac68k |
struct SoundParam { |
QElemPtr qLink; |
short qType; |
short ioTrap; |
Ptr ioCmdAddr; |
IOCompletionUPP ioCompletion; |
OSErr ioResult; |
StringPtr ioNamePtr; |
short ioVRefNum; |
short ioCRefNum; |
short csCode; |
short csParam[11]; |
}; |
typedef struct SoundParam SoundParam, *SoundParamPtr; |
#pragma options align=reset |
// tell the OS all about us |
extern DriverDescription TheDriverDescription = { |
kTheDescriptionSignature, |
kInitialDriverDescriptor, |
// DriverType |
"\pTestSoundInputDriver", |
1, 0, developStage, 1, |
// DriverOSRuntimeInfo |
0 |
| (1 * kDriverIsLoadedUponDiscovery) /* Loader runtime options */ |
| (1 * kDriverIsOpenedUponLoad) /* Opened when loaded */ |
| (0 * kDriverIsUnderExpertControl) /* No I/O expert to handle loads/opens */ |
| (0 * kDriverIsConcurrent) /* Not concurrent yet */ |
| (0 * kDriverQueuesIOPB), /* Not internally queued yet */ |
"\p.TestSoundInputDriver", /* Str31 driverName (OpenDriver param) */ |
0, 0, 0, 0, 0, 0, 0, 0, /* UInt32 driverDescReserved[8] */ |
// DriverOSService |
1, /* ServiceCount nServices */ |
// DriverServiceInfo |
kServiceCategoryNdrvDriver, /* OSType serviceCategory */ |
kNdrvTypeIsGeneric, /* OSType serviceType */ |
1, 0, developStage, 1, /* major, minor, stage, rev */ |
}; |
//For debugging information |
#define DEBUG 0 |
#define DEBUG2 0 |
#define FULLDEBUG 0 |
#define kSamplesInBuffer 1024 |
#define kSilence 128 |
#define kAmplitude 75 |
#define kSquareSource 1 |
#define kSawSource 2 |
#define kSilenceSource 3 |
#define kNumRates 3 |
#define kNumSizes 2 |
#define kNumCompressions 1 |
typedef struct myTMTask { |
TMTask theTask; |
ParmBlkPtr pb; |
} myTMTask; |
//---------------------------------------------------------------------------------- |
// Function Prototypes |
//---------------------------------------------------------------------------------- |
pascal OSErr FragInit (CFragInitBlockPtr initInfo); |
pascal void FragTerm (void); |
static OSStatus DriverKillIOCmd (ParmBlkPtr pb); |
extern OSErr DoDriverIO (AddressSpaceID addressSpaceID, IOCommandID ioCommandID, IOCommandContents ioCommandContents, IOCommandCode ioCommandCode, IOCommandKind ioCommandKind); |
static pascal void HWIntProc (QElemPtr passedPtr); |
static void MakeSquareWave (long duration, long numberSamples, UnsignedFixed sampleRate, long frequency, short sampleSize, short numChannels); |
static void MakeSawWave (long duration, long numberSamples, UnsignedFixed sampleRate, long frequency, short sampleSize, short numChannels); |
static OSErr DoOptionsDialog (void); |
static void SetHardwareToDefault (void); |
//---------------------------------------------------------------------------------- |
// Globals |
//---------------------------------------------------------------------------------- |
//Hardware specific globals |
SInt16 gMaxAmplitude, |
gContinuousOn, |
gLevelMeterOn, |
gTwosComplementOn, |
gAGCOn, |
gPlayThruVolume, |
gVoxRecordingOn, |
gVoxStoppingOn, |
gPauseState, |
gNumberChannels, |
gSampleSize, |
gVOXStartTrigger, |
gVOXStopTrigger, |
gVOXDelay, |
gCompressionFactor, |
gCurrentInputSource = kSquareSource; |
SInt32 gActiveChannels; //channels to record from 0x01 == only left, 0x02 == only right, 0x03 == both channels |
UnsignedFixed gSampleRate; |
Fixed gInputGain, |
gLeftInputGain, |
gRightInputGain; |
OSType gCompressionType, |
gRecordingQuality; |
SIInterruptUPP gUserInterruptProc = nil; |
//Globals needed to simulate our hardware |
myTMTask gSimulatedHWInterrupt; |
TimerUPP gSimulatedHWIntProc = nil; |
SInt32 gInterruptFreq = 0, |
gSamplesWritten = 0; |
SInt16 gNextSaw16Sample = -((kAmplitude * 0.01) * 32767); //starting point for saw tooth wave |
SInt8 gNextSaw8Sample = kSilence - kAmplitude; //starting point for saw tooth wave |
Ptr gSoundBuffer = nil; //our hardware's internal buffer |
//Driver specific globals |
Boolean gStopRecording = false; |
DriverRefNum gDrvrRefNum; |
Handle gDrvrIcon, |
gInputSourceNames; |
AliasHandle gAliasToDriver; |
StringHandle gDriverName; |
//---------------------------------------------------------------------------------- |
// CFM Init |
//---------------------------------------------------------------------------------- |
pascal OSErr FragInit (CFragInitBlockPtr initInfo) { |
Handle iconHandle, |
strHandle; |
StringHandle driverName; |
SInt16 resRefNum; |
OSErr err = noErr; |
if (initInfo->fragLocator.where == kDataForkCFragLocator || initInfo->fragLocator.where == kResourceCFragLocator) { |
resRefNum = FSpOpenResFile (initInfo->fragLocator.u.onDisk.fileSpec, fsRdPerm); |
err = ResError(); |
if (err != noErr) { |
#if DEBUG |
SysDebugStr ("\p!!Error returned from FSpOpenResFile!!"); |
#endif |
} |
if (resRefNum == -1) { |
#if DEBUG |
SysDebugStr ("\p!!Error resRefNum is -1!!"); |
#endif |
} |
} |
if (resRefNum != -1 && err == noErr) { |
iconHandle = Get1Resource ('ICN#', 128); |
err = ResError (); |
if (iconHandle == nil) { |
#if DEBUG |
SysDebugStr ("\p!!iconHandle is nil!!"); |
#endif |
} |
if (err != noErr) { |
#if DEBUG |
SysDebugStr ("\p!!Error from Get1Resource('ICN#', 128)!!"); |
#endif |
} |
if (iconHandle != nil) { |
gDrvrIcon = NewHandleSys (GetHandleSize (iconHandle)); |
BlockMoveData (*iconHandle, *gDrvrIcon, GetHandleSize (iconHandle)); |
ReleaseResource (iconHandle); |
} else if (err == noErr) { |
err = resNotFound; |
#if DEBUG |
SysDebugStr ("\p!!Error Get1Resource('ICN#', 128), resNotFound!!"); |
#endif |
} |
strHandle = Get1Resource ('STR#', 128); |
err = ResError (); |
if (strHandle == nil) { |
#if DEBUG |
SysDebugStr ("\p!!strHandle is nil!!"); |
#endif |
} |
if (err != noErr) { |
#if DEBUG |
SysDebugStr ("\p!!Error from Get1Resource('STR#', 128)!!"); |
#endif |
} |
if (strHandle != nil) { |
gInputSourceNames = NewHandleSys (GetHandleSize (strHandle)); |
BlockMoveData (*strHandle, *gInputSourceNames, GetHandleSize (strHandle)); |
ReleaseResource (strHandle); |
} else if (err == noErr) { |
err = resNotFound; |
#if DEBUG |
SysDebugStr ("\p!!Error Get1Resource('STR#', 128), resNotFound!!"); |
#endif |
} |
driverName = GetString (128); |
err = ResError (); |
if (driverName == nil) { |
#if DEBUG |
SysDebugStr ("\p!!driverName is nil!!"); |
#endif |
} |
if (err != noErr) { |
#if DEBUG |
SysDebugStr ("\p!!Error from GetString(128)!!"); |
#endif |
} |
if (driverName != nil) { |
gDriverName = (StringHandle)NewHandleSys (GetHandleSize ((Handle)driverName)); |
BlockMoveData (*driverName, *gDriverName, GetHandleSize ((Handle)driverName)); |
ReleaseResource ((Handle)driverName); |
} else if (err == noErr) { |
err = resNotFound; |
#if DEBUG |
SysDebugStr ("\p!!Error GetString(128), resNotFound!!"); |
#endif |
} |
CloseResFile (resRefNum); |
} |
if (err == noErr) { |
//make an alias to ourselves so that we can open our driver later if needed |
err = NewAlias (nil, initInfo->fragLocator.u.onDisk.fileSpec, &gAliasToDriver); |
} |
#if DEBUG |
if (err != noErr) { |
SysDebugStr ("\p!!Error in FragInit!!"); |
} |
#endif |
return (err); |
} |
pascal void FragTerm (void) { |
if (gDrvrIcon != nil) { |
DisposeHandle (gDrvrIcon); |
gDrvrIcon = nil; |
} |
if (gInputSourceNames != nil) { |
DisposeHandle (gInputSourceNames); |
gInputSourceNames = nil; |
} |
if (gAliasToDriver != nil) { |
DisposeHandle ((Handle)gAliasToDriver); |
gAliasToDriver = nil; |
} |
} |
//---------------------------------------------------------------------------------- |
// Driver calls |
//---------------------------------------------------------------------------------- |
/* |
Always run at task level, can allocate and move memory. |
*/ |
static OSStatus DriverInitializeCmd (AddressSpaceID addressSpaceID, DriverInitInfoPtr initialInfo) { |
#pragma unused (addressSpaceID, initialInfo) |
OSStatus err = noErr; |
#if DEBUG |
SysDebugStr ("\pin DriverInitializeCmd;g"); |
#endif |
gDrvrRefNum = initialInfo->refNum; |
#if DEBUG |
if (err != noErr) { |
SysDebugStr ("\p!!Error in DriverInitializeCmd!!"); |
} |
#if FULLDEBUG |
SysDebugStr ("\pleaving DriverInitializeCmd;g"); |
#endif |
#endif |
return (err); |
} |
/* |
Always run at task level, can allocate and move memory. |
*/ |
static OSStatus DriverFinalizeCmd (DriverFinalInfoPtr finalInfo) { |
#pragma unused (finalInfo) |
OSStatus err = noErr; |
#if DEBUG |
SysDebugStr ("\pin DriverFinalizeCmd;g"); |
#endif |
#if DEBUG |
if (err != noErr) { |
SysDebugStr ("\p!!Error in DriverFinalizeCmd!!"); |
} |
#if FULLDEBUG |
SysDebugStr ("\pleaving DriverFinalizeCmd;g"); |
#endif |
#endif |
return (err); |
} |
/* |
Always run at task level, can allocate and move memory. |
*/ |
static OSStatus DriverSupersededCmd (DriverSupersededInfoPtr supersededInfo) { |
#pragma unused (supersededInfo) |
OSErr err = noErr; |
#if DEBUG |
SysDebugStr ("\pin DriverSupersededCmd;g"); |
#endif |
#if DEBUG |
if (err != noErr) { |
SysDebugStr ("\p!!Error in DriverSupersededCmd!!"); |
} |
#if FULLDEBUG |
SysDebugStr ("\pleaving DriverSupersededCmd;g"); |
#endif |
#endif |
return (err); |
} |
/* |
Always run at task level, can allocate and move memory. |
*/ |
static OSStatus DriverReplaceCmd (AddressSpaceID addressSpaceID, DriverReplaceInfoPtr replaceInfo) { |
#pragma unused (addressSpaceID, replaceInfo) |
OSStatus err = noErr; |
#if DEBUG |
SysDebugStr ("\pin DriverReplaceCmd;g"); |
#endif |
#if DEBUG |
if (err != noErr) { |
SysDebugStr ("\p!!Error in DriverReplaceCmd!!"); |
} |
#if FULLDEBUG |
SysDebugStr ("\pleaving DriverReplaceCmd;g"); |
#endif |
#endif |
return (err); |
} |
/* |
Always run at task level, can allocate and move memory. |
*/ |
static OSStatus DriverOpenCmd (AddressSpaceID addressSpaceID, ParmBlkPtr pb) { |
#pragma unused (addressSpaceID, pb) |
OSStatus err = noErr; |
#if DEBUG |
SysDebugStr ("\pin DriverOpenCmd;g"); |
#endif |
gSoundBuffer = NewPtrSys (kSamplesInBuffer * 4); //room for 16 bit stereo samples |
if (gSoundBuffer != nil) { |
gSimulatedHWIntProc = NewTimerProc (HWIntProc); |
SetHardwareToDefault (); |
err = SPBSignInDevice (gDrvrRefNum, *gDriverName); |
} else { |
#if DEBUG |
SysDebugStr ("\p!!couldn't allocate memory for our buffer!!"); |
#endif |
err = MemError (); |
} |
#if DEBUG |
if (err != noErr) { |
SysDebugStr ("\p!!Error in DriverOpenCmd!!"); |
} |
#if FULLDEBUG |
SysDebugStr ("\pleaving DriverOpenCmd;g"); |
#endif |
#endif |
return (err); |
} |
/* |
Always run at task level, can allocate and move memory. |
*/ |
static OSStatus DriverCloseCmd (ParmBlkPtr pb) { |
#pragma unused (pb) |
OSStatus err = noErr; |
#if DEBUG |
SysDebugStr ("\pin DriverCloseCmd;g"); |
#endif |
(void)SPBSignOutDevice (gDrvrRefNum); |
DisposePtr (gSoundBuffer); |
gSoundBuffer = nil; |
DisposeRoutineDescriptor (gSimulatedHWIntProc); |
gSimulatedHWIntProc = nil; |
#if FULLDEBUG |
SysDebugStr ("\pleaving DriverCloseCmd;g"); |
#endif |
return (err); |
} |
/* |
May run at interrupt level, CANNOT allocate or move memory. |
*/ |
static OSStatus DriverControlCmd (AddressSpaceID addressSpaceID, IOCommandID ioCommandID, IOCommandKind ioCommandKind, SoundParamPtr pb) { |
#pragma unused (addressSpaceID, ioCommandID, ioCommandKind) |
OSStatus err = noErr; |
OSType selector = 0; |
#if FULLDEBUG |
SysDebugStr ("\pin DriverControlCmd;g"); |
#endif |
// if they call SPBStopRecording, it comes to us as killIO |
// so call our killIO routine |
if (pb->csCode == killCode) { |
err = DriverKillIOCmd ((ParmBlkPtr)pb); |
} |
else if (pb->csCode == 2) { |
// indicates that sound input information selector is in first 4 bytes of csParam |
selector = ((OSType*)pb->csParam)[0]; |
} |
switch (selector) { |
//Control calls that are required to be supported |
case siCompressionType: |
//Set the compression type. Some devices allow the incoming samples to be |
//compressed before being placed in your applicationÕs input buffer. The |
//infoData parameter points to a buffer of type OSType, which is the |
//compression type. |
#if DEBUG2 |
SysDebugStr ("\pcontrol: siCompressionType"); |
#endif |
if (((OSType*)pb->csParam)[1] != 'NONE') { |
err = siInvalidCompression; //we don't support compression |
} |
break; |
case siContinuous: |
//Set the state of continuous recording from this device. If recording |
//is being turned off, the driver stops recording samples to its internal buffer. |
//Only sound input device drivers that support asynchronous recording support |
//continuous recording. The infoData parameter points to an integer, which is |
//the state of continuous recording (0 is off, 1 is on). |
#if DEBUG2 |
SysDebugStr ("\pcontrol: siContinuous"); |
#endif |
if (((SInt16*)pb->csParam)[2] == 0 || ((SInt16*)pb->csParam)[2] == 1) { |
gContinuousOn = ((SInt16*)pb->csParam)[2]; |
} else { |
err = paramErr; |
} |
break; |
case siLevelMeterOnOff: |
//Set the current state of the level meter. For calls to set the level meter, |
//the infoData parameter points to an integer that indicates whether the level meter |
//is off (0) or on (1). |
#if DEBUG2 |
SysDebugStr ("\pcontrol: siLevelMeterOnOff"); |
#endif |
if (((SInt16*)pb->csParam)[2] == 0 || ((SInt16*)pb->csParam)[2] == 1) { |
gLevelMeterOn = ((SInt16*)pb->csParam)[2]; |
} else { |
err = paramErr; |
} |
break; |
case siNumberChannels: |
//Set the number of channels this device is to record. The infoData parameter points |
//to an integer, which indicates the number of channels. Note that this selector |
//determines the format of the data stream output by the driver. If the number of |
//channels is 1, the driver should output monophonic data in response to a Read call. |
//If the number of channels is 2, the driver should output interleaved stereo data. |
#if DEBUG2 |
SysDebugStr ("\pcontrol: siNumberChannels"); |
#endif |
if (((SInt16*)pb->csParam)[2] >= 1 && ((SInt16*)pb->csParam)[2] <= 2) { |
gNumberChannels = ((SInt16*)pb->csParam)[2]; |
} else { |
err = notEnoughHardwareErr; |
} |
break; |
case siRecordingQuality: |
//Set the current quality of recorded sound. The infoData parameter points to a buffer |
//of type OSType, which is the recording quality. Currently foud qualities are supported, |
//defined by these constants: |
// siCDQuality = 'cd ', /*44.1kHz, stereo, 16 bit*/ |
// siBestQuality = 'best'; /*22kHz, mono, 8 bit*/ |
// siBetterQuality = 'betr'; /*22kHz, mono, MACE 3:1*/ |
// siGoodQuality = 'good'; |
//These qualities are defined by the sound input device driver. Usually best means |
//monaural, 8-bit, 22 kHz, sound with no compression. |
#if DEBUG2 |
SysDebugStr ("\pcontrol: siRecordingQuality"); |
#endif |
switch (((OSType*)pb->csParam)[1]) { |
case siCDQuality: |
gRecordingQuality = siCDQuality; |
gSampleRate = rate44khz; |
gSampleSize = 16; |
gNumberChannels = 2; |
gCompressionType = 'NONE'; |
break; |
case siBestQuality: |
gRecordingQuality = siBestQuality; |
gSampleRate = rate22050hz; |
gSampleSize = 8; |
gNumberChannels = 1; |
gCompressionType = 'NONE'; |
break; |
case siBetterQuality: |
gRecordingQuality = siBetterQuality; |
gSampleRate = rate22050hz; |
gSampleSize = 8; |
gNumberChannels = 1; |
gCompressionType = 'NONE'; |
break; |
case siGoodQuality: |
gRecordingQuality = siGoodQuality; |
gSampleRate = rate11025hz; |
gSampleSize = 8; |
gNumberChannels = 1; |
gCompressionType = 'NONE'; |
break; |
default: |
err = siUnknownQuality; |
} |
break; |
case siSampleRate: |
//Set the sample rate to be produced by this device. The sample rate must be in the |
//range 0 to 65535.65535 Hz. The sample rate is declared as a Fixed data type. In |
//order to accommodate sample rates greater than 32 kHz, the most significant bit is |
//not treated as a sign bit; instead, that bit is interpreted as having the value 32,768. |
//The infoData parameter points to a buffer of type Fixed, which is the sample rate. |
#if DEBUG2 |
SysDebugStr ("\pcontrol: siSampleRate"); |
#endif |
if (((UnsignedFixed*)pb->csParam)[1] == rate44khz || ((UnsignedFixed*)pb->csParam)[1] == rate22050hz || ((UnsignedFixed*)pb->csParam)[1] == rate11025hz) { |
gSampleRate = ((UnsignedFixed*)pb->csParam)[1]; |
} else { |
err = siInvalidSampleRate; |
} |
break; |
case siSampleSize: |
//Set the sample size to be produced by this device. Because some compression formats |
//require specific sample sizes, this selector might return an error when compression |
//is used. The infoData parameter points to an integer, which is the sample size. |
#if DEBUG2 |
SysDebugStr ("\pcontrol: siSampleSize"); |
#endif |
if (((SInt16*)pb->csParam)[2] == 8 || ((SInt16*)pb->csParam)[2] == 16) { |
gSampleSize = ((SInt16*)pb->csParam)[2]; |
} else { |
err = siInvalidSampleSize; |
} |
break; |
case siTwosComplementOnOff: |
//Set the current state of the twoÕs complement feature. This selector only applies to |
//8-bit data. (16-bit samples are always stored in twoÕs complement format.) If on, the |
//driver stores all samples in the application buffer as twoÕs complement values |
//(that is, Ð128 to 127). Otherwise, the driver stores the samples as offset binary |
//values (that is, 0 to 255). The infoData parameter points to an integer, which is the |
//current state of the twoÕs complement feature (1 if twoÕs complement output is desired, |
//0 otherwise). |
#if DEBUG2 |
SysDebugStr ("\pcontrol: siTwosComplementOnOff"); |
#endif |
if (((SInt16*)pb->csParam)[2] == 0 || ((SInt16*)pb->csParam)[2] == 1) { |
gTwosComplementOn = ((SInt16*)pb->csParam)[2]; |
} else { |
err = paramErr; |
} |
break; |
//Sound Manager only calls |
case siCloseDriver: |
//The Sound Input Manager sends this selector when it closes a device previously |
//opened with write permission. The sound input device driver should stop any |
//recording in progress, deallocate the input hardware, and initialize local |
//variables to default settings. |
#if DEBUG2 |
SysDebugStr ("\pcontrol: siCloseDriver"); |
#endif |
//We will actually stop the hardware at the next hardware interrupt |
gStopRecording = true; |
break; |
case siInitializeDriver: |
//The Sound Input Manager sends this selector when it opens a sound input device |
//with write permission. The sound input device driver initializes local variables |
//and prepares to start recording. If possible, the driver initializes the device |
//to a sampling rate of 22 kHz, a sample size of 8 bits, mono recording, no compression, |
//automatic gain control on, and all other features off. |
#if DEBUG2 |
SysDebugStr ("\pcontrol: siInitializeDriver"); |
#endif |
SetHardwareToDefault (); |
break; |
case siPauseRecording: |
//The Sound Input Manager uses this selector to set the current pause state. |
//The sound input device driver continues recording but does not store the sampled |
//data in a buffer. The infoData parameter points to an integer, which indicates |
//the state of pausing (0 is off, 1 is on). |
#if DEBUG2 |
SysDebugStr ("\pcontrol: siPauseRecording"); |
#endif |
gPauseState = ((SInt16*)pb->csParam)[2]; |
break; |
case siUserInterruptProc: |
//The Sound Input Manager sends this selector to specify the sound input interrupt |
//routine that the sound input device driver should call. The infoData parameter |
//points to a procedure pointer, which is the address of the sound input interrupt routine. |
#if DEBUG2 |
SysDebugStr ("\pcontrol: siUserInterruptProc"); |
#endif |
gUserInterruptProc = ((SIInterruptUPP*)pb->csParam)[1]; |
break; |
//Control calls that can be optionally supported |
case siActiveChannels: |
//Set the channels to record from. When setting the active channels, the data passed |
//in is a long integer that is interpreted as a bitmap describing the channels to record |
//from. For example, if bit 0 is set, then the first channel is made active. The samples |
//for each active channel are interleaved in the applicationÕs buffer. |
#if DEBUG2 |
SysDebugStr ("\pcontrol: siActiveChannels"); |
#endif |
if (((SInt32*)pb->csParam)[1] >= 0x01 && ((SInt32*)pb->csParam)[1] <= 0x03) { |
gActiveChannels = ((SInt32*)pb->csParam)[1]; |
} else { |
err = notEnoughHardwareErr; |
} |
break; |
case siAGCOnOff: |
//Set the current state of the automatic gain control feature. The infoData parameter |
//points to an integer, which is 0 if gain control is off and 1 if it is on. |
#if DEBUG2 |
SysDebugStr ("\pcontrol: siAGCOnOff"); |
#endif |
if (((SInt16*)pb->csParam)[2] == 0 || ((SInt16*)pb->csParam)[2] == 1) { |
gAGCOn = ((SInt16*)pb->csParam)[2]; |
} else { |
err = paramErr; |
} |
break; |
case siInputGain: |
//Set the current sound input gain. If the available hardware allows adjustment of the |
//recording gain, this selector lets you get and set the gain. In response to a Control |
//call, a sound input driver sets the gain level used for all subsequent recording to the |
//specified value. The infoData parameter points to a 4-byte value of type Fixed ranging |
//from 0.5 to 1.5, where 1.5 specifies maximum gain. |
#if DEBUG2 |
SysDebugStr ("\pcontrol: siInputGain"); |
#endif |
if (((Fixed*)pb->csParam)[1] >= (Fixed)0.5 && ((Fixed*)pb->csParam)[1] <= (Fixed)1.5) { |
gInputGain = ((Fixed*)pb->csParam)[1]; |
} else { |
err = paramErr; |
} |
break; |
case siInputSource: |
//Set the current sound input source. If the available hardware allows recording from more |
//than one source, this selector lets you get and set the source. In response to a Control |
//call, a sound input driver sets the source of all subsequent recording to the value passed |
//in. If the value is less than 1 or greater than the number of input sources, the driver |
//returns paramErr; if the driver supports only one source, it returns siUnknownInfoType. The |
//infoData parameter points to an integer, which is the index of the current sound input source. |
#if DEBUG2 |
SysDebugStr ("\pcontrol: siInputSource"); |
#endif |
if (((SInt16*)pb->csParam)[2] >= 1 && ((SInt16*)pb->csParam)[2] <= 3) { |
gCurrentInputSource = ((SInt16*)pb->csParam)[2]; |
} else { |
err = notEnoughHardwareErr; |
} |
break; |
case siOptionsDialog: |
//Cause the driver to display the Options dialog box (SPBSetDeviceInfo). This dialog box |
//is designed to allow the user to configure device-specific features of the sound input |
//hardware. With SPBSetDeviceInfo, the infoData parameter is unused. |
#if DEBUG2 |
SysDebugStr ("\pcontrol: siOptionsDialog"); |
#endif |
err = DoOptionsDialog (); |
break; |
case siPlayThruOnOff: |
//Set the current play-through state and volume. The infoData parameter points to an integer, |
//which indicates the current play-through volume (1 to 7). If that integer is 0, then |
//play-through is off. |
#if DEBUG2 |
SysDebugStr ("\pcontrol: siPlayThruOnOff"); |
#endif |
err = notEnoughHardwareErr; |
// If our (fake) hardware actually did playthrough, this would be useful... |
/* |
if (((short*)pb->csParam)[2] >= 0 || ((short*)pb->csParam)[2] <= 7) { |
gPlayThruVolume = ((short*)pb->csParam)[2]; |
} else { |
err = notEnoughHardwareErr; |
} |
*/ |
break; |
case siStereoInputGain: |
//Set the current stereo sound input gain. If the available hardware allows adjustment of |
//the recording gain, this selector lets you get and set the gain for each of two channels |
//(left or right). In response to a Status call, a sound input driver should return the |
//current gain setting for the specified channel. In response to a Control call, a sound input |
//driver should set the gain level used for all subsequent recording to the specified value. |
//The infoData parameter points to two 4-byte values of type Fixed ranging from 0.5 to 1.5, |
//where 1.5 specifies maximum gain. The first of these values is equivalent to the gain for |
//the left channel and the second value is equivalent to the gain for the right channel. |
#if DEBUG2 |
SysDebugStr ("\pcontrol: siStereoInputGain"); |
#endif |
if (((Fixed*)pb->csParam)[1] <= 1.5 && ((Fixed*)pb->csParam)[1] >= 0.5 && ((Fixed*)pb->csParam)[2] <= 1.5 && ((Fixed*)pb->csParam)[2] >= 0.5) { |
gLeftInputGain = ((Fixed*)pb->csParam)[1]; |
gRightInputGain = ((Fixed*)pb->csParam)[2]; |
} else { |
err = paramErr; |
} |
break; |
case siVoxRecordInfo: |
//Set the current VOX recording parameters. The infoData parameter points to two integers. |
//The first integer indicates whether VOX recording is on or off (0 if off, 1 if on). The |
//second integer indicates the VOX record trigger value. Trigger values range from 0 to 255 |
//(0 is trigger immediately, 255 is trigger only on full volume). |
#if DEBUG2 |
SysDebugStr ("\pcontrol: siVoxRecordInfo"); |
#endif |
if (((SInt16*)pb->csParam)[2] == 0 || ((SInt16*)pb->csParam)[2] == 1) { |
gVoxRecordingOn = ((SInt16*)pb->csParam)[2]; |
} else { |
err = paramErr; |
} |
if (gVoxRecordingOn == 1 && (((SInt16*)pb->csParam)[3] <= 255 && ((SInt16*)pb->csParam)[3] >= 0)) { |
gVOXStartTrigger = ((SInt16*)pb->csParam)[3]; |
} else { |
err = paramErr; |
} |
break; |
case siVoxStopInfo: |
//Set the current VOX stopping parameters. The infoData parameter points to three integers. |
//The first integer indicates whether VOX stopping is on or off (0 if off, 1 if on). The |
//second integer indicates the VOX stop trigger value. Trigger values range from 0 to 255 |
//(255 is stop immediately, 0 is stop only on total silence). The third integer indicates |
//how many milliseconds the trigger value must be continuously valid for recording to be stopped. |
//Delay values range from 0 to 65,535. |
#if DEBUG2 |
SysDebugStr ("\pcontrol: siVoxStopInfo"); |
#endif |
if (((SInt16*)pb->csParam)[2] == 0 || ((SInt16*)pb->csParam)[2] == 1) { |
gVoxStoppingOn = ((SInt16*)pb->csParam)[2]; |
} else { |
err = paramErr; |
} |
if (gVoxStoppingOn == 1 && (((SInt16*)pb->csParam)[3] <= 255 && ((SInt16*)pb->csParam)[3] >= 0)) { |
gVOXStopTrigger = ((SInt16*)pb->csParam)[3]; |
gVOXDelay = ((SInt16*)pb->csParam)[4]; |
} else { |
err = paramErr; |
} |
break; |
//Unknown or unsupported control call |
default: |
#if DEBUG |
{ |
Str255 errString = "\pcontrol: unknown selector -> XXXX"; |
OSType* typePtr; |
typePtr = (OSType*)(&errString[30]); |
*typePtr = selector; |
SysDebugStr ( errString ); |
} |
// SysDebugStr ("\pcontrol: unknown selector"); |
#endif |
err = controlErr; |
} |
#if DEBUG |
if (err != noErr && err != controlErr) { |
unsigned char errorcode[11]; |
sprintf ((char*)errorcode, "0x%0.8X", err); |
errorcode[0] = 10; |
SysDebugStr (errorcode); |
SysDebugStr ("\p!!Error in DriverControlCmd!!"); |
} |
#if FULLDEBUG |
SysDebugStr ("\pleaving DriverControlCmd;g"); |
#endif |
#endif |
return (err); |
} |
/* |
May run at interrupt level, CANNOT allocate or move memory. |
*/ |
static OSStatus DriverStatusCmd (AddressSpaceID addressSpaceID, IOCommandID ioCommandID, IOCommandKind ioCommandKind, SoundParamPtr pb) { |
#pragma unused (addressSpaceID, ioCommandID, ioCommandKind) |
OSStatus err = noErr; |
OSType selector = 0; |
Handle iconHandle, |
compressionAvailable, |
sampleRateAvailable, |
sampleSizeAvailable; |
SoundInfoList info; |
#if FULLDEBUG |
SysDebugStr ("\pin DriverStatusCmd;g"); |
#endif |
if (pb->csCode == 2) { |
selector = ((OSType*)pb->csParam)[0]; |
} |
switch (selector) { |
//Status calls that are required to be supported |
case siAsync: |
//Determine whether the driver supports asynchronous recording functions. The infoData parameter |
//points to an integer, which is 0 if the driver supports synchronous calls only and 1 otherwise. |
//Some sound input drivers do not support asynchronous recording at all, and some might support |
//asynchronous recording only on certain hardware configurations. |
#if DEBUG2 |
SysDebugStr ("\pstatus: siAsync;g"); |
#endif |
((SInt32*)pb->csParam)[0] = 2; //number of bytes being returned |
((SInt16*)pb->csParam)[2] = 1; //we can do async recording |
break; |
case siChannelAvailable: |
//Get the maximum number of channels this device can record. The infoData parameter points to an |
//integer, which is the number of available channels. |
#if DEBUG2 |
SysDebugStr ("\pstatus: siChannelAvailable;g"); |
#endif |
((SInt32*)pb->csParam)[0] = 2; //number of bytes being returned |
((SInt16*)pb->csParam)[2] = 2; //we can do stereo (note: Sound Manager doesn't currently work with more than 2 channels) |
break; |
case siCompressionAvailable: |
//Get the number and list of compression types this device can produce. The infoData parameter |
//points to an integer, which is the number of compression types, followed by a handle. The |
//handle references a list of compression types, each of type OSType. |
#if DEBUG2 |
SysDebugStr ("\pstatus: siCompressionAvailable;g"); |
#endif |
compressionAvailable = NewHandle (sizeof (OSType) * kNumCompressions); |
if (compressionAvailable != nil) { |
((OSType*)*compressionAvailable)[0] = 'NONE'; |
info.count = kNumCompressions; |
info.infoHandle = compressionAvailable; |
((SInt32*)pb->csParam)[0] = sizeof (SoundInfoList); //number of bytes being returned |
*(SoundInfoList*)(pb->csParam+2) = info; |
} |
break; |
case siCompressionFactor: |
//Get the compression factor of the current compression type. For example, the compression factor |
//for MACE 3:1 compression is 3. If a sound input device driver supports only compression type 'NONE', |
//the returned compression type is 1. The infoData parameter points to an integer, which is the |
//compression factor. |
#if DEBUG2 |
SysDebugStr ("\pstatus: siCompressionFactor;g"); |
#endif |
if (gCompressionType == 'NONE') { |
((SInt32*)pb->csParam)[0] = 2; //number of bytes being returned |
((SInt16*)pb->csParam)[2] = 1; //no compression equals 1:1 |
} |
break; |
case siCompressionType: |
//Get the compression type. Some devices allow the incoming samples to be compressed before being |
//placed in your applicationÕs input buffer. The infoData parameter points to a buffer of type |
//OSType, which is the compression type. |
#if DEBUG2 |
SysDebugStr ("\pstatus: siCompressionType;g"); |
#endif |
((SInt32*)pb->csParam)[0] = 4; //number of bytes being returned |
((OSType*)pb->csParam)[1] = gCompressionType; |
break; |
case siContinuous: |
//Get the state of continuous recording from this device. If recording is being turned off, the |
//driver stops recording samples to its internal buffer. Only sound input device drivers that |
//support asynchronous recording support continuous recording. The infoData parameter points to an |
//integer, which is the state of continuous recording (0 is off, 1 is on). |
#if DEBUG2 |
SysDebugStr ("\pstatus: siContinuous;g"); |
#endif |
((SInt32*)pb->csParam)[0] = 2; //number of bytes being returned |
((SInt16*)pb->csParam)[2] = gContinuousOn; |
break; |
case siDeviceBufferInfo: |
//Get the size of the deviceÕs internal buffer. This information can be useful when you want to |
//modify sound input data at interrupt time. Note, however, that if a driver is recording continuously, |
//then the size of the buffer passed to your sound input interrupt routine might be greater than the |
//size this selector returns because data recorded between calls to SPBRecord as well as recorded |
//during calls to SPBRecord will be sent to your interrupt routine. The infoData parameter points to |
//a long integer, which is the size of the deviceÕs internal buffer. |
#if DEBUG2 |
SysDebugStr ("\pstatus: siDeviceBufferInfo;g"); |
#endif |
((SInt32*)pb->csParam)[0] = 4; //number of bytes being returned |
((SInt32*)pb->csParam)[1] = kSamplesInBuffer * gNumberChannels * (gSampleSize / 8); |
break; |
case siDeviceConnected: |
//Get the state of the device connection. The infoData parameter points to an integer, which is one |
//of the following constants: |
// siDeviceIsConnected = 1; |
// siDeviceNotConnected = 0; |
// siDontKnowIfConnected = -1; |
//The siDeviceIsConnected constant indicates that the device is connected and ready. The |
//siDeviceNotConnected constant indicates that the device is not connected. The siDontKnowIfConnected |
//constant indicates that the Sound Input Manager cannot determine whether the device is connected. |
#if DEBUG2 |
SysDebugStr ("\pstatus: siDeviceConnected;g"); |
#endif |
((SInt32*)pb->csParam)[0] = 2; //number of bytes being returned |
((SInt16*)pb->csParam)[2] = siDeviceIsConnected; |
break; |
case siDeviceIcon: |
//Get the deviceÕs icon and icon mask. In response to a Status call, a sound input device driver |
//should return, in the location specified by the infoData parameter, a handle to a block of memory |
//that contains the icon and its mask in the format of an 'ICN#' resource. It is the driverÕs |
//responsibility to allocate that block of memory, but it should not releasee it. The software issuing |
//this selector is responsible for disposing of the handle. As a result, a device driver should detach |
//any resource handles (by calling DetachResource) before returning them to the caller. |
#if DEBUG2 |
SysDebugStr ("\pstatus: siDeviceIcon;g"); |
#endif |
((SInt32*)pb->csParam)[0] = 4; //number of bytes being returned |
iconHandle = gDrvrIcon; |
err = HandToHand (&iconHandle); |
if (err != noErr) { |
SysDebugStr ("\pHandToHand returned an error"); |
} |
((Handle*)pb->csParam)[1] = iconHandle; |
break; |
case siDeviceName: |
//Get the name of the sound input device. Your application must pass a pointer to a buffer that will |
//be filled in with the deviceÕs name. The buffer needs to be large enough to hold a Str255 data type. |
#if DEBUG2 |
SysDebugStr ("\pstatus: siDeviceName;g"); |
#endif |
((SInt32*)pb->csParam)[0] = 0; //Sound Manager doesn't have to copy any data |
BlockMoveData (gDriverName[0], ((Handle*)pb->csParam)[1], *gDriverName[0]+1); |
break; |
case siLevelMeterOnOff: |
//Get the current state of the level meter. To get the level meter setting, the infoData parameter |
//points to two integers; the first integer indicates the state of the level meter, and the second |
//integer contains the level value of the meter. The level meter setting is an integer that ranges |
//from 0 (no volume) to 255 (full volume). |
#if DEBUG2 |
SysDebugStr ("\pstatus: siLevelMeterOnOff;g"); |
#endif |
((SInt32*)pb->csParam)[0] = 4; //number of bytes being returned |
((SInt16*)pb->csParam)[2] = gLevelMeterOn; |
((SInt16*)pb->csParam)[3] = 0; |
break; |
case siNumberChannels: |
//Get the number of channels this device is to record. The infoData parameter points to an integer, |
//which indicates the number of channels. Note that this selector determines the format of the data |
//stream output by the driver. If the number of channels is 1, the driver should output monophonic data |
//in response to a Read call. If the number of channels is 2, the driver should output interleaved |
//stereo data. |
#if DEBUG2 |
SysDebugStr ("\pstatus: siNumberChannels;g"); |
#endif |
((SInt32*)pb->csParam)[0] = 2; //number of bytes being returned |
((SInt16*)pb->csParam)[2] = gNumberChannels; |
break; |
case siRecordingQuality: |
//Get the current quality of recorded sound. The infoData parameter points to a buffer of type |
//OSType, which is the recording quality. Currently four qualities are supported, defined by |
//these constants: |
// siCDQuality = 'cd ', /*44.1kHz, stereo, 16 bit*/ |
// siBestQuality = 'best'; /*22kHz, mono, 8 bit*/ |
// siBetterQuality = 'betr'; /*22kHz, mono, MACE 3:1*/ |
// siGoodQuality = 'good'; |
//These qualities are defined by the sound input device driver. Usually best means monaural, |
//8-bit, 22 kHz, sound with no compression. |
#if DEBUG2 |
SysDebugStr ("\pstatus: siRecordingQuality;g"); |
#endif |
((SInt32*)pb->csParam)[0] = 4; //number of bytes being returned |
((OSType*)pb->csParam)[1] = gRecordingQuality; |
break; |
case siSampleRate: |
//Get the sample rate to be produced by this device. The sample rate must be in the range 0 to |
//65535.65535 Hz. The sample rate is declared as a Fixed data type. In order to accommodate sample |
//rates greater than 32 kHz, the most significant bit is not treated as a sign bit; instead, that |
//bit is interpreted as having the value 32,768. The infoData parameter points to a buffer of type |
//Fixed, which is the sample rate. |
#if DEBUG2 |
SysDebugStr ("\pstatus: siSampleRate;g"); |
#endif |
((SInt32*)pb->csParam)[0] = 4; //number of bytes being returned |
((UnsignedFixed*)pb->csParam)[1] = gSampleRate; |
break; |
case siSampleRateAvailable: |
//Get the range of sample rates this device can produce. The infoData parameter points to an integer, |
//which is the number of sample rates the device supports, followed by a handle. The handle references |
//a list of sample rates, each of type Fixed. If the device can record a range of sample rates, the |
//number of sample rates is set to 0 and the handle contains two rates, the minimum and the maximum |
//of the range of sample rates. Otherwise, a list is returned that contains the sample rates supported. |
//In order to accommodate sample rates greater than 32 kHz, the most significant bit is not treated as |
//a sign bit; instead, that bit is interpreted as having the value 32,768. |
#if DEBUG2 |
SysDebugStr ("\pstatus: siSampleRateAvailable;g"); |
#endif |
sampleRateAvailable = NewHandle (sizeof (UnsignedFixed) * kNumRates); |
if (sampleRateAvailable != nil) { |
((UnsignedFixed*)*sampleRateAvailable)[0] = rate44khz; |
((UnsignedFixed*)*sampleRateAvailable)[1] = rate22050hz; |
((UnsignedFixed*)*sampleRateAvailable)[2] = rate11025hz; |
info.count = kNumRates; |
info.infoHandle = sampleRateAvailable; |
((SInt32*)pb->csParam)[0] = sizeof (SoundInfoList); //number of bytes being returned |
*(SoundInfoList*)(pb->csParam+2) = info; |
} |
break; |
case siSampleSize: |
//Get the sample size to be produced by this device. Because some compression formats require specific |
//sample sizes, this selector might return an error when compression is used. The infoData parameter |
//points to an integer, which is the sample size. |
#if DEBUG2 |
SysDebugStr ("\pstatus: siSampleSize;g"); |
#endif |
((SInt32*)pb->csParam)[0] = 2; //number of bytes being returned |
((SInt16*)pb->csParam)[2] = gSampleSize; |
break; |
case siSampleSizeAvailable: |
//Get the range of sample sizes this device can produce. The infoData parameter points to an integer, |
//which is the number of sample sizes the device supports, followed by a handle. The handle references |
//a list of sample sizes, each of type Integer. |
#if DEBUG2 |
SysDebugStr ("\pstatus: siSampleSizeAvailable;g"); |
#endif |
sampleSizeAvailable = NewHandle (sizeof (SInt16) * kNumSizes); |
if (sampleSizeAvailable != nil) { |
((SInt16*)*sampleSizeAvailable)[0] = 8; |
((SInt16*)*sampleSizeAvailable)[1] = 16; |
info.count = kNumSizes; |
info.infoHandle = sampleSizeAvailable; |
((SInt32*)pb->csParam)[0] = sizeof (SoundInfoList); //number of bytes being returned |
*(SoundInfoList*)(pb->csParam+2) = info; |
} |
break; |
case siTwosComplementOnOff: |
//Get the current state of the twoÕs complement feature. This selector only applies to 8-bit data. |
//(16-bit samples are always stored in twoÕs complement format.) If on, the driver stores all samples |
//in the application buffer as twoÕs complement values (that is, Ð128 to 127). Otherwise, the driver |
//stores the samples as offset binary values (that is, 0 to 255). The infoData parameter points to an |
//integer, which is the current state of the twoÕs complement feature (1 if twoÕs complement output |
//is desired, 0 otherwise). |
#if DEBUG2 |
SysDebugStr ("\pstatus: siTwosComplementOnOff;g"); |
#endif |
((SInt32*)pb->csParam)[0] = 2; //number of bytes being returned |
((SInt16*)pb->csParam)[2] = gTwosComplementOn; |
break; |
//Sound Manager only call |
case siPauseRecording: |
//The Sound Input Manager uses this selector to get the current pause state. |
//The sound input device driver continues recording but does not store the sampled |
//data in a buffer. The infoData parameter points to an integer, which indicates |
//the state of pausing (0 is off, 1 is on). |
#if DEBUG2 |
SysDebugStr ("\pcontrol: siPauseRecording;g"); |
#endif |
((SInt32*)pb->csParam)[0] = 2; //number of bytes being returned |
((SInt16*)pb->csParam)[2] = gPauseState; |
break; |
//Status calls that can be optionally supported |
case siActiveChannels: |
//Get the channels to record from. When reading the active channels, the data returned is |
//a bitmap of the active channels. |
#if DEBUG2 |
SysDebugStr ("\pstatus: siActiveChannels;g"); |
#endif |
((SInt32*)pb->csParam)[0] = 4; //number of bytes being returned |
((SInt32*)pb->csParam)[1] = gActiveChannels; |
break; |
case siActiveLevels: |
//Get the current signal level for each active channel. The infoData parameter points to an array |
//of integers, the size of which depends on the number of active channels. You can determine how |
//many channels are active by calling SPBGetDeviceInfo with the siNumberChannels selector. |
#if DEBUG2 |
SysDebugStr ("\pstatus: siActiveLevels;g"); |
#endif |
if (gNumberChannels == 1) { |
((SInt32*)pb->csParam)[0] = 2; //number of bytes being returned |
((SInt16*)pb->csParam)[2] = 0; |
} else if (gNumberChannels == 2) { |
((SInt32*)pb->csParam)[0] = 4; //number of bytes being returned |
((SInt16*)pb->csParam)[2] = 0; |
((SInt16*)pb->csParam)[3] = 0; |
} |
break; |
case siAGCOnOff: |
//Get the current state of the automatic gain control feature. The infoData parameter points to an |
//integer, which is 0 if gain control is off and 1 if it is on. |
#if DEBUG2 |
SysDebugStr ("\pstatus: siAGCOnOff;g"); |
#endif |
((SInt32*)pb->csParam)[0] = 2; //number of bytes being returned |
((SInt16*)pb->csParam)[2] = gAGCOn; |
break; |
case siCompressionHeader: |
//Get a compressed sound header for the current recording settings. Your application passes in a |
//pointer to a compressed sound header and the driver fills it in. Before calling SPBGetDeviceInfo |
//with this selector, you should set the numFrames field of the compressed sound header to the number |
//of bytes in the sound. When SPBGetDeviceInfo returns successfully, that field contains the number |
//of sample frames in the sound. This selector is needed only by drivers that use compression types |
//that are not directly supported by Apple. If you call this selector after recording a sound, your |
//application can get enough information about the sound to play it or save it in a file. The infoData |
//parameter points to a compressed sound header. |
#if DEBUG2 |
SysDebugStr ("\pstatus: siCompressionHeader;g"); |
#endif |
err = siUnknownInfoType; //we don't support compression |
break; |
case siCompressionNames: |
//Get a list of names of the compression types supported by the sound input device. In response to a |
//Status call, a sound input device driver returns, in the location specified by the infoData parameter, |
//a handle to a block of memory that contains the names of all compression types supported by the driver. |
//It is the driverÕs responsibility to allocate that block of memory, but it should not release it. The |
//software issuing this selector is responsible for disposing of the handle. As a result, a device driver |
//must detach any resource handles (by calling DetachResource) before returning them to the caller. The |
//data in the handle has the same format as an 'STR#' resource: a two-byte count of the strings in the |
//resource, followed by the strings themselves. The strings should occur in the same order as the |
//compression types returned by the siCompressionAvailable selector. If the driver does not support |
//compression, it returns siUnknownInfoType. If the driver supports compression but for some reason not |
//all compression types are currently selectable, it returns a list of all available compression types. |
#if DEBUG2 |
SysDebugStr ("\pstatus: siCompressionNames;g"); |
#endif |
err = siUnknownInfoType; //we don't support compression |
break; |
case siInputGain: |
//Get the current sound input gain. If the available hardware allows adjustment of the recording gain, |
//this selector lets you get and set the gain. In response to a Status call, a sound input driver returns |
//the current gain setting. The infoData parameter points to a 4-byte value of type Fixed ranging from |
//0.5 to 1.5, where 1.5 specifies maximum gain. |
#if DEBUG2 |
SysDebugStr ("\pstatus: siInputGain;g"); |
#endif |
((SInt32*)pb->csParam)[0] = 4; //number of bytes being returned |
((Fixed*)pb->csParam)[1] = gInputGain; |
break; |
case siInputSource: |
//Get the current sound input source. If the available hardware allows recording from more than one |
//source, this selector lets you get and set the source. In response to a Status call, a sound input |
//driver returns the current source value; if the driver supports only one source, it returns |
//siUnknownInfoType. |
#if DEBUG2 |
SysDebugStr ("\pstatus: siInputSource;g"); |
#endif |
((SInt32*)pb->csParam)[0] = 2; //number of bytes being returned |
((SInt16*)pb->csParam)[2] = gCurrentInputSource; |
break; |
case siInputSourceNames: |
//Get a list of the names of all the sound input sources supported by the sound input device. In |
//response to a Status call, a sound input device driver returns, in the location specified by the |
//infoData parameter, a handle to a block of memory that contains the names of all sound sources |
//supported by the driver. It is the driverÕs responsibility to allocate that block of memory, but it |
//should not release it. The software issuing this selector is responsible for disposing of the handle. |
//As a result, a device driver must detach any resource handles (by calling DetachResource) before |
//returning them to the caller. The data in the handle has the same format as an 'STR#' resource: a |
//two-byte count of the strings in the resource, followed by the strings themselves. The strings should |
//occur in the same order as the input sources returned by the siInputSource selector. If the driver |
//supports only one source, it returns siUnknownInfoType. If the driver supports more than one source |
//but for some reason not all of them are currently selectable, it returns a list of all available |
//input sources. |
#if DEBUG2 |
SysDebugStr ("\pstatus: siInputSourceNames;g"); |
#endif |
{ |
Handle h; |
OSErr err; |
h = gInputSourceNames; // handle we want to copy |
err = HandToHand( &h ); // copy it |
if( !err ) |
{ |
((SInt32*)pb->csParam)[0] = 4; //number of bytes being returned |
((Handle*)pb->csParam)[1] = h; //handle to data |
} |
else |
((SInt32*)pb->csParam)[0] = 0; |
} |
break; |
case siOptionsDialog: |
//Determine whether the driver supports an Options dialog box (SPBGetDeviceInfo). This dialog box is |
//designed to allow the user to configure device-specific features of the sound input hardware. With |
//SPBGetDeviceInfo, the infoData parameter points to an integer, which indicates whether the driver |
//supports an Options dialog box (1 if it supports it, 0 otherwise). |
#if DEBUG2 |
SysDebugStr ("\pstatus: siOptionsDialog;g"); |
#endif |
((SInt32*)pb->csParam)[0] = 2; //number of bytes being returned |
((SInt16*)pb->csParam)[2] = 1; //we have a dialog |
break; |
case siPlayThruOnOff: |
//Get the current play-through state and volume. The infoData parameter points to an integer, |
//which indicates the current play-through volume (1 to 7). If that integer is 0, then |
//play-through is off. |
#if DEBUG2 |
SysDebugStr ("\pstatus: siPlayThruOnOff;g"); |
#endif |
((SInt32*)pb->csParam)[0] = 2; //number of bytes being returned |
((SInt16*)pb->csParam)[2] = gPlayThruVolume; |
break; |
case siStereoInputGain: |
//Get the current stereo sound input gain. If the available hardware allows adjustment of the recording |
//gain, this selector lets you get and set the gain for each of two channels (left or right). In response |
//to a Status call, a sound input driver should return the current gain setting for the specified channel. |
//The infoData parameter points to two 4-byte values of type Fixed ranging from 0.5 to 1.5, where 1.5 |
//specifies maximum gain. The first of these values is equivalent to the gain for the left channel and the |
//second value is equivalent to the gain for the right channel. |
#if DEBUG2 |
SysDebugStr ("\pstatus: siStereoInputGain;g"); |
#endif |
((SInt32*)pb->csParam)[0] = 8; //number of bytes being returned |
((Fixed*)pb->csParam)[1] = gLeftInputGain; |
((Fixed*)pb->csParam)[2] = gRightInputGain; |
break; |
case siVoxRecordInfo: |
//Get the current VOX recording parameters. The infoData parameter points to two integers. The first |
//integer indicates whether VOX recording is on or off (0 if off, 1 if on). The second integer indicates |
//the VOX record trigger value. Trigger values range from 0 to 255 (0 is trigger immediately, 255 is |
//trigger only on full volume). |
#if DEBUG2 |
SysDebugStr ("\pstatus: siVoxRecordInfo;g"); |
#endif |
((SInt32*)pb->csParam)[0] = 4; //number of bytes being returned |
((SInt16*)pb->csParam)[2] = gVoxRecordingOn; |
((SInt16*)pb->csParam)[3] = gVOXStartTrigger; |
break; |
case siVoxStopInfo: |
//Get the current VOX stopping parameters. The infoData parameter points to three integers. The |
//first integer indicates whether VOX stopping is on or off (0 if off, 1 if on). The second integer |
//indicates the VOX stop trigger value. Trigger values range from 0 to 255 (255 is stop immediately, 0 |
//is stop only on total silence). The third integer indicates how many milliseconds the trigger value must |
//be continuously valid for recording to be stopped. Delay values range from 0 to 65,535. |
#if DEBUG2 |
SysDebugStr ("\pstatus: siVoxStopInfo;g"); |
#endif |
((SInt32*)pb->csParam)[0] = 6; //number of bytes being returned |
((SInt16*)pb->csParam)[2] = gVoxStoppingOn; |
((SInt16*)pb->csParam)[3] = gVOXStopTrigger; |
((SInt16*)pb->csParam)[4] = gVOXDelay; |
break; |
//Unknown or unsupported status call |
default: |
#if DEBUG |
SysDebugStr ("\pstatus: unknown selector;g"); |
#endif |
err = statusErr; |
} |
#if DEBUG |
if (err != noErr && err != statusErr && err != siUnknownInfoType) { |
SysDebugStr ("\p!!Error in DriverStatusCmd!!"); |
} |
#if FULLDEBUG |
SysDebugStr ("\pleaving DriverStatusCmd;g"); |
#endif |
#endif |
return (err); |
} |
/* |
May run at interrupt level, CANNOT allocate or move memory. |
*/ |
static OSStatus DriverReadCmd (AddressSpaceID addressSpaceID, IOCommandID ioCommandID, IOCommandKind ioCommandKind, ParmBlkPtr pb) { |
#pragma unused (addressSpaceID, ioCommandID, ioCommandKind) |
OSStatus err = noErr; |
double samplesInBuffer = kSamplesInBuffer,temp, temp2; |
#if DEBUG |
SysDebugStr ("\pin DriverReadCmd;g"); |
#endif |
gSimulatedHWInterrupt.theTask.tmAddr = gSimulatedHWIntProc; |
gSimulatedHWInterrupt.theTask.tmWakeUp = 0; |
gSimulatedHWInterrupt.pb = pb; |
temp = ((gSampleRate >> 16) / samplesInBuffer); |
temp2 = -(1000000.0 / temp); //calculate time in microseconds |
gInterruptFreq = temp2; |
InsXTime ((QElemPtr)&gSimulatedHWInterrupt); |
PrimeTime ((QElemPtr)&gSimulatedHWInterrupt, gInterruptFreq); //wait for the time it takes to "record" one buffer |
err = ioInProgress; |
#if DEBUG |
if (err != noErr && err != ioInProgress) { |
SysDebugStr ("\p!!Error in DriverReadCmd!!"); |
} |
#if FULLDEBUG |
SysDebugStr ("\pleaving DriverReadCmd;g"); |
#endif |
#endif |
return (err); |
} |
/* |
May run at interrupt level, CANNOT allocate or move memory. |
*/ |
static OSStatus DriverWriteCmd (AddressSpaceID addressSpaceID, IOCommandID ioCommandID, IOCommandKind ioCommandKind, ParmBlkPtr pb) { |
#pragma unused (addressSpaceID, ioCommandID, ioCommandKind, pb) |
OSStatus err; |
#if DEBUG |
SysDebugStr ("\pin DriverWriteCmd;g"); |
#endif |
//Sound input drivers don't have a write command. |
err = paramErr; |
#if DEBUG |
if (err != noErr) { |
SysDebugStr ("\p!!Error in DriverWriteCmd!!"); |
} |
#if FULLDEBUG |
SysDebugStr ("\pleaving DriverWriteCmd;g"); |
#endif |
#endif |
return (err); |
} |
/* |
May run at interrupt level, CANNOT allocate or move memory. |
*/ |
static OSStatus DriverKillIOCmd (ParmBlkPtr pb) { |
#pragma unused (pb) |
OSStatus err = noErr; |
#if DEBUG |
SysDebugStr ("\pin DriverKillIOCmd"); |
#endif |
gStopRecording = true; //when our next hardware "interrupt" fires we will stop the "hardware" |
if (gSimulatedHWIntProc != nil) { |
RmvTime ((QElemPtr)&gSimulatedHWInterrupt); |
} |
#if DEBUG |
if (err != noErr) { |
SysDebugStr ("\p!!Error in DriverKillIOCmd!!"); |
} |
#if FULLDEBUG |
SysDebugStr ("\pleaving DriverKillIOCmd;g"); |
#endif |
#endif |
return (err); |
} |
/* |
May run at interrupt level, CANNOT allocate or move memory. |
*/ |
extern OSErr DoDriverIO(AddressSpaceID addressSpaceID, IOCommandID ioCommandID, IOCommandContents ioCommandContents, IOCommandCode ioCommandCode, IOCommandKind ioCommandKind) { |
OSStatus status; |
OSErr err = noErr; |
#if FULLDEBUG |
SysDebugStr ("\pin DoDriverIO;g"); |
#endif |
/* |
Note: Initialize, Open, KillIO, Close, and Finalize are either synchronous |
or immediate. Read, Write, Control, and Status may be immediate, |
synchronous, or asynchronous. |
*/ |
switch (ioCommandCode) { |
case kInitializeCommand: /* Always immediate */ |
status = DriverInitializeCmd (addressSpaceID, ioCommandContents.initialInfo); |
break; |
case kFinalizeCommand: /* Always immediate */ |
status = DriverFinalizeCmd (ioCommandContents.finalInfo); |
break; |
case kSupersededCommand: /* Always immediate */ |
status = DriverSupersededCmd (ioCommandContents.supersededInfo); |
break; |
case kReplaceCommand: /* Always immediate, replace an old driver */ |
status = DriverReplaceCmd (addressSpaceID, ioCommandContents.replaceInfo); |
break; |
case kOpenCommand: /* Always immediate */ |
status = DriverOpenCmd (addressSpaceID, ioCommandContents.pb); |
break; |
case kCloseCommand: /* Always immediate */ |
status = DriverCloseCmd (ioCommandContents.pb); |
break; |
case kControlCommand: |
status = DriverControlCmd (addressSpaceID, ioCommandID, ioCommandKind, (SoundParamPtr) ioCommandContents.pb); |
break; |
case kStatusCommand: |
status = DriverStatusCmd (addressSpaceID, ioCommandID, ioCommandKind, (SoundParamPtr) ioCommandContents.pb); |
break; |
case kReadCommand: |
status = DriverReadCmd (addressSpaceID, ioCommandID, ioCommandKind, ioCommandContents.pb); |
break; |
case kWriteCommand: |
status = DriverWriteCmd (addressSpaceID, ioCommandID, ioCommandKind, ioCommandContents.pb); |
break; |
case kKillIOCommand: /* Always immediate */ |
status = DriverKillIOCmd (ioCommandContents.pb); |
break; |
default: |
status = paramErr; |
break; |
} |
if ((ioCommandKind & kImmediateIOCommandKind) != 0) { |
/* Immediate commands return the operation status and don't call IOCommandIsComplete */ |
} else if (status == ioInProgress) { |
/* Perform the action and call IOCommandIsComplete at some later time */ |
status = noErr; |
} else { |
err = status; |
status = IOCommandIsComplete (ioCommandID, err); |
} |
#if DEBUG |
if (err == paramErr) { |
SysDebugStr ("\p!!Param Error in DoDriverIO!!"); |
} |
#if FULLDEBUG |
SysDebugStr ("\pleaving DoDriverIO;g"); |
#endif |
#endif |
return (status); |
} |
static pascal void HWIntProc (QElemPtr passedPtr) { |
UInt32 numSamples, |
numBytesLeftToRecord; |
Boolean recordingComplete = false; |
ParmBlkPtr pb = ((myTMTask*)passedPtr)->pb; |
OSErr err; |
#if DEBUG |
SysDebugStr ("\pin HWIntProc"); |
#endif |
err = noErr; |
if (gStopRecording == false) { |
if (gContinuousOn == true) { // If continuous recording is on... |
if (pb->ioParam.ioBuffer != nil) { // ...and we are given a buffer... |
// ...if the buffer passed in is larger than the number of byte we record each hardware interrupt... |
if (pb->ioParam.ioReqCount >= kSamplesInBuffer * (gSampleSize / 8) * gNumberChannels) { |
// ...calculate the number of bytes left to record. |
numBytesLeftToRecord = pb->ioParam.ioReqCount - pb->ioParam.ioActCount; |
} |
else { |
// Return an error since the continuous ring buffer will loop if you don't ask for at |
// least a hardware size buffer for each record. |
err = paramErr; |
} |
} |
else { |
// If they ask us to record into no buffer, they really just want a recording that goes on |
// infinitely until they call SPBStopRecording (which comes to us as killIO). |
// So setup to record a full hardware buffer |
numSamples = kSamplesInBuffer; |
numBytesLeftToRecord = kSamplesInBuffer * (gSampleSize / 8) * gNumberChannels; |
} |
} |
else { // If continuous recording is not on... |
if (pb->ioParam.ioBuffer != nil) { // ...and we are given a buffer |
// ...calculate number of bytes to record. |
numBytesLeftToRecord = pb->ioParam.ioReqCount - pb->ioParam.ioActCount; |
} |
else { // ...and we are NOT given a buffer, that's an error |
err = paramErr; |
} |
} |
// If the number of bytes requested is larger than our buffer, return the max number of samples that will fit in our buffer |
if (numBytesLeftToRecord > (kSamplesInBuffer * gNumberChannels * (gSampleSize / 8))) { |
numSamples = kSamplesInBuffer; |
} else { |
// otherwise figure out how many samples the requested number of bytes equals |
numSamples = numBytesLeftToRecord / (gNumberChannels * (gSampleSize / 8)); |
} |
switch (gCurrentInputSource) { |
case 1: |
// Make a square wave |
MakeSquareWave (0, numSamples, gSampleRate, 1000, gSampleSize, gNumberChannels); |
break; |
case 2: |
// Make a saw tooth wave |
MakeSawWave (0, numSamples, gSampleRate, 1000, gSampleSize, gNumberChannels); |
break; |
case 3: |
// Make silence |
MakeSquareWave (0, numSamples, gSampleRate, 0, gSampleSize, gNumberChannels); |
break; |
default: |
#if DEBUG |
SysDebugStr ("\punknown input source in HWIntProc"); |
#endif |
break; // null statement so when DEBUG is false this switch still compiles |
} |
if (gTwosComplementOn == true && gSampleSize == 8) { |
SInt32 i; |
for (i = 0; i < numSamples; i++) { |
((UInt32*)gSoundBuffer)[i] ^= 0x80808080; |
} |
} |
// If we have a client interrupt proc and we're not paused, call the proc |
if (gUserInterruptProc != nil && gPauseState == 0) { |
CallSIInterruptProc (gUserInterruptProc, pb->ioParam.ioMisc, gSoundBuffer, gMaxAmplitude, numSamples*(gSampleSize/8)*gNumberChannels); |
} |
//if we have a buffer and pausing is off, copy data to app's buffer |
if (pb->ioParam.ioBuffer != nil && gPauseState == 0) { |
BlockMoveData (gSoundBuffer, pb->ioParam.ioBuffer+pb->ioParam.ioActCount, numSamples*(gSampleSize/8)*gNumberChannels); |
} |
//If we are not paused, update the number of samples we have made |
if (gPauseState == 0) { |
pb->ioParam.ioActCount += numSamples*(gSampleSize/8)*gNumberChannels; |
} |
// If we have a buffer and actual bytes == requested bytes, recording is complete |
if (pb->ioParam.ioBuffer != nil && pb->ioParam.ioActCount == pb->ioParam.ioReqCount) { |
recordingComplete = true; |
} |
} |
// if we got an error, stop recording |
if (err != noErr) { |
gStopRecording = true; |
} |
if (recordingComplete == false && gStopRecording == false) { |
PrimeTime ((QElemPtr)passedPtr, gInterruptFreq); // more data to record |
} else { |
// Have recorded requested number of bytes, stop the hardware (if continuous recording is not on) |
// Do a software interrupt so that we can call IOCommandIsComplete (which can only be called at |
// task level and software interrupt level. |
RmvTime ((QElemPtr)passedPtr); |
if (gStopRecording == true) { |
SetHardwareToDefault (); |
} else if (gContinuousOn == false) { |
gSamplesWritten = 0; |
} |
IOCommandIsComplete ((IOCommandID)pb->ioParam.ioCmdAddr, err); |
} |
#if FULLDEBUG |
SysDebugStr ("\pleaving HWIntProc;g"); |
#endif |
} |
//Makes a square wave of the specified frequency and duration |
//duration in 1/100 of a second, i.e. 500 for a five second tone |
//frequency in samples per second, i.e. 8000 for an 8000Hz square wave (requires 16kHz sampling rate) |
static void MakeSquareWave (SInt32 duration, SInt32 numberSamples, UnsignedFixed sampleRate, SInt32 frequency, SInt16 sampleSize, SInt16 numChannels) { |
SInt32 numSamples = 0; |
if (duration != 0 && numberSamples == 0) { |
numSamples = (duration / 100.0) * (sampleRate >> 16); |
} else if (duration == 0 && numberSamples != 0) { |
numSamples = numberSamples; |
} |
if (numSamples > 0) { |
if (frequency == 0) { |
gMaxAmplitude = 0; |
//Generate silence of requested length |
if (sampleSize == 8) { |
UInt8 *buffer8 = (UInt8*)gSoundBuffer; |
UInt8 *end8 = buffer8 + (numSamples * numChannels); |
while (buffer8 < end8) { |
*buffer8++ = kSilence; |
} |
} else if (sampleSize == 16) { |
UInt16 *buffer16 = (UInt16*)gSoundBuffer; |
UInt16 *end16 = buffer16 + (numSamples * numChannels); |
while (buffer16 < end16) { |
*buffer16++ = 0; |
} |
} else { |
#if DEBUG |
SysDebugStr ("\pbad sample size in MakeSquareWave (silence)"); |
#endif |
} |
} else if ((frequency * 2) <= sampleRate) { |
//Generate a square wave |
SInt32 samplesBeforeReverse = (((sampleRate >> 16) / frequency) / 2) * numChannels; |
if (sampleSize == 8) { |
UInt8 *buffer8 = (UInt8*)gSoundBuffer; |
UInt8 *end8 = buffer8 + (numSamples * numChannels); |
gMaxAmplitude = kSilence + kAmplitude; |
while (buffer8 < end8) { |
if (gSamplesWritten < samplesBeforeReverse) { |
*buffer8++ = kSilence + kAmplitude; |
gSamplesWritten++; |
} else { |
*buffer8++ = kSilence - kAmplitude; |
if (gSamplesWritten == (samplesBeforeReverse * 2 - 1)) { |
gSamplesWritten = 0; |
} else { |
gSamplesWritten++; |
} |
} |
} |
} else if (sampleSize == 16) { |
UInt16 *buffer16 = (UInt16*)gSoundBuffer; |
UInt16 *end16 = buffer16 + (numSamples * numChannels); |
gMaxAmplitude = (kAmplitude * 0.01) * 32767; |
while (buffer16 < end16) { |
if (gSamplesWritten < samplesBeforeReverse) { |
*buffer16++ = (kAmplitude * 0.01) * 32767; |
gSamplesWritten++; |
} else { |
*buffer16++ = -((kAmplitude * 0.01) * 32767); |
if (gSamplesWritten == (samplesBeforeReverse * 2 - 1)) { |
gSamplesWritten = 0; |
} else { |
gSamplesWritten++; |
} |
} |
} |
} else { |
#if DEBUG |
SysDebugStr ("\pbad sample size in MakeSquareWave (square wave)"); |
#endif |
} |
} |
} |
} |
static void MakeSawWave (SInt32 duration, SInt32 numberSamples, UnsignedFixed sampleRate, SInt32 frequency, SInt16 sampleSize, SInt16 numChannels) { |
SInt32 numSamples = 0; |
if (duration != 0 && numberSamples == 0) { |
numSamples = (duration / 100.0) * (sampleRate >> 16); |
} else if (duration == 0 && numberSamples != 0) { |
numSamples = numberSamples; |
} |
if (numSamples > 0) { |
if ((frequency * 2) <= sampleRate) { |
//Generate a saw tooth wave |
SInt32 samplesBeforeReverse = (((sampleRate >> 16) / frequency) / 2); |
if (sampleSize == 8) { |
UInt8 *buffer8 = (UInt8*)gSoundBuffer; |
UInt8 *end8 = buffer8 + (numSamples * numChannels); |
UInt8 increment = (kAmplitude*2) / samplesBeforeReverse; |
gMaxAmplitude = kSilence + kAmplitude; |
while (buffer8 < end8) { |
if (gSamplesWritten < samplesBeforeReverse) { |
*buffer8++ = gNextSaw8Sample; |
if (numChannels == 2) { |
*buffer8++ = gNextSaw8Sample; |
} |
gNextSaw8Sample += increment; |
gSamplesWritten++; |
} else { |
*buffer8++ = gNextSaw8Sample; |
if (numChannels == 2) { |
*buffer8++ = gNextSaw8Sample; |
} |
gNextSaw8Sample -= increment; |
if (gSamplesWritten == (samplesBeforeReverse * 2 - 1)) { |
gSamplesWritten = 0; |
} else { |
gSamplesWritten++; |
} |
} |
} |
} else if (sampleSize == 16) { |
UInt16 *buffer16 = (UInt16*)gSoundBuffer; |
UInt16 *end16 = buffer16 + (numSamples * numChannels); |
UInt16 increment = (((kAmplitude * 0.01) * 32767)*2) / samplesBeforeReverse; |
gMaxAmplitude = (kAmplitude * 0.01) * 32767; |
while (buffer16 < end16) { |
if (gSamplesWritten < samplesBeforeReverse) { |
*buffer16++ = gNextSaw16Sample; |
if (numChannels == 2) { |
*buffer16++ = gNextSaw16Sample; |
} |
gNextSaw16Sample += increment; |
gSamplesWritten++; |
} else { |
*buffer16++ = gNextSaw16Sample; |
if (numChannels == 2) { |
*buffer16++ = gNextSaw16Sample; |
} |
gNextSaw16Sample -= increment; |
if (gSamplesWritten == (samplesBeforeReverse * 2 - 1)) { |
gSamplesWritten = 0; |
} else { |
gSamplesWritten++; |
} |
} |
} |
} else { |
#if DEBUG |
SysDebugStr ("\pbad sample size in MakeSawWave"); |
#endif |
} |
} |
} |
} |
static OSErr DoOptionsDialog (void) { |
FSSpec driverFSSpec; |
Rect rect; |
ControlHandle item = nil; |
DialogRef optionsDialog; |
OSErr err; |
SInt16 itemHit = 0, |
itemType, |
curRes, |
resRef = -1, |
newInputSource = gCurrentInputSource; |
Boolean wasChanged, |
done = false; |
curRes = CurResFile (); |
err = ResolveAlias (nil, gAliasToDriver, &driverFSSpec, &wasChanged); |
if (err == noErr) { |
resRef = FSpOpenResFile (&driverFSSpec, fsRdPerm); |
if (resRef != -1) { |
optionsDialog = GetNewDialog (128, nil, (GrafPtr)-1L); |
} else { |
#if DEBUG |
SysDebugStr ("\pFSpOpenResFile failed"); |
#endif |
} |
} else { |
#if DEBUG |
SysDebugStr ("\pResolveAlias failed"); |
#endif |
} |
if (optionsDialog != nil) { |
//gCurrentInputSource+5 is the item number of the corresponding radio buttons |
//so itemHit-5 is the input source number |
GetDialogItem (optionsDialog, gCurrentInputSource+5, &itemType, &(Handle)item, &rect); |
if (item != nil) { |
SetControlValue (item, 1); |
SetDialogDefaultItem (optionsDialog, 1); //put the ring around the OK button |
} else { |
#if DEBUG |
SysDebugStr ("\pitem is nil!"); |
#endif |
} |
ShowWindow (optionsDialog); |
do { |
ModalDialog (nil, &itemHit); |
switch (itemHit) { |
case 1: //OK button |
gCurrentInputSource = newInputSource; |
done = true; |
break; |
case 2: //Cancel button |
done = true; |
break; |
case 6: //Square wave |
case 7: //Saw wave |
case 8: //Silence |
newInputSource = itemHit-5; |
GetDialogItem (optionsDialog, 6, &itemType, &(Handle)item, &rect); |
SetControlValue (item, itemHit == 6); |
GetDialogItem (optionsDialog, 7, &itemType, &(Handle)item, &rect); |
SetControlValue (item, itemHit == 7); |
GetDialogItem (optionsDialog, 8, &itemType, &(Handle)item, &rect); |
SetControlValue (item, itemHit == 8); |
break; |
} |
} while (!done); |
DisposeDialog (optionsDialog); |
optionsDialog = nil; |
} else { |
#if DEBUG |
SysDebugStr ("\poptionsDialog is nil!"); |
#endif |
} |
if (resRef != -1) { |
CloseResFile (resRef); |
UseResFile (curRes); |
} |
return err; |
} |
static void SetHardwareToDefault (void) { |
gContinuousOn = 0; |
gLevelMeterOn = 0; |
gTwosComplementOn = 0; |
gAGCOn = 0; |
gPlayThruVolume = 0; |
gVoxRecordingOn = 0; |
gVoxStoppingOn = 0; |
gPauseState = 0; |
gNumberChannels = 1; |
gSampleSize = 8; |
gVOXStartTrigger = 0; |
gVOXStopTrigger = 0; |
gVOXDelay = 0; |
gCompressionFactor = 1; |
// gCurrentInputSource = 1; //don't change the current input source |
gActiveChannels = 0x00000001; |
gSampleRate = rate22050hz; |
gInputGain = 0x00010000; |
gLeftInputGain = 0x01000100; |
gRightInputGain = 0x01000100; |
gCompressionType = 'NONE'; |
gRecordingQuality = siBestQuality; |
gUserInterruptProc = nil; |
gSamplesWritten = 0; |
gNextSaw8Sample = kSilence - kAmplitude; |
gNextSaw16Sample = -((kAmplitude * 0.01) * 32767); |
gStopRecording = false; |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-03-14