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.
SoundInputSample.c
/* |
** Apple Macintosh Developer Technical Support |
** |
** SoundInputSample: Sample code to properly record a sound to disk and play it back. |
** |
** by Andrew Wulf, Apple Developer Technical Support |
** |
** File: SoundInputSample.c |
** |
** Copyright © 1996 Apple Computer, Inc. |
** All rights reserved. |
** |
** You may incorporate this sample code into your applications without |
** restriction, though the sample code has been provided "AS IS" and the |
** responsibility for its operation is 100% yours. However, what you are |
** not permitted to do is to redistribute the source as "DSC Sample Code" |
** after having made changes. If you're going to re-distribute the source, |
** we require that you make it clear in the source that the code was |
** descended from Apple Sample Code, but that you've made changes. |
*/ |
#include <Quickdraw.h> |
#include <Windows.h> |
#include <Dialogs.h> |
#include <Files.h> |
#include <StandardFile.h> |
#include <Packages.h> |
#include <OSEvents.h> |
#include <Memory.h> |
#include <Sound.h> |
#include <SoundInput.h> |
#include <SoundComponents.h> |
#include <OSUtils.h> |
#include <ToolUtils.h> |
#include <Types.h> |
#include <TextUtils.h> |
#include <SegLoad.h> |
#include <Gestalt.h> |
#include <AIFF.h> |
#include <stdio.h> |
#include <strings.h> |
#if PRAGMA_ALIGN_SUPPORTED |
#pragma options align=mac68k |
#endif |
typedef struct |
{ |
short numberOfRatesSupported; |
unsigned long** ratesSupported; |
} SampleRatesData; |
typedef struct |
{ |
short numberOfSizesSupported; |
short** sizesSupported; |
} SampleSizesData; |
#if PRAGMA_ALIGN_SUPPORTED |
#pragma options align=reset |
#endif |
#if ((!defined(__MWERKS__))&&(!defined(THINK_C))) |
QDGlobals qd; |
#endif |
void ShowErrAndQuit(char*text, short err); |
void ShowErr(char*text, short err); |
void main() |
{ |
short i; |
OSErr err; |
long soundAttr = 0; |
SndChannelPtr mySoundChannel; |
StandardFileReply fileReply; |
short myFRefNum; |
long mySIRefNum; |
SampleSizesData sampleSizesInfo = { 0, 0 }; |
SampleRatesData sampleRatesInfo = { 0, 0 }; |
OSType previousCompressionType; |
short previousTwosComplement; |
short previousChannels; |
unsigned long previousSampleRate; |
short previousSampleSize; |
Fixed previousGain; |
short previousVoxRecord[2]; |
short previousVoxStop[3]; |
OSType noCompression = NoneType; |
OSType compressionSetting = noCompression; |
short twosComplementSetting = 1; |
short numChannels = 1; |
unsigned long sampleRate; |
short sampleSize; |
long soundDuration = 4*1000; // in milliseconds |
Fixed inputGain = 0x00010000L; // = 1.0, can be up to 1.5 |
short voxRecord[2] = { 0, 0 }; |
short voxStop[3] = { 0, 0, 0 }; |
SPB soundParamBlock; |
ParamBlockRec paramBlock; |
DialogPtr infoDialog = 0; |
InitGraf(&qd.thePort); |
FlushEvents(everyEvent, 0); |
InitWindows(); |
InitDialogs(0); |
InitCursor(); |
// check gestalt to see if we have sound input support |
err = Gestalt(gestaltSoundAttr, &soundAttr); |
if (err || (soundAttr & (1L<<gestaltSoundIOMgrPresent))==0 |
|| (soundAttr & (1L<<gestaltBuiltInSoundInput))==0) |
ShowErrAndQuit("Insufficient sound input support", 0); |
// Make a file to save sound into |
StandardPutFile("\pPick a file to record to:", "\p", &fileReply); |
if (! fileReply.sfGood) |
ShowErrAndQuit("Goodbye", 0); |
FSpCreate(&fileReply.sfFile, 'SCPL', 'AIFC', 0); // a SoundApp creator & filetype |
err = FSpOpenDF(&fileReply.sfFile, fsRdWrPerm, &myFRefNum); |
if (err != noErr) |
ShowErrAndQuit("FSpOpenDF", err); |
// Open the default sound input device |
err = SPBOpenDevice("\p", siWritePermission, &mySIRefNum); |
if (err != noErr) |
ShowErrAndQuit("SPBOpenDevice", err); |
// Get the sample sizes available |
err = SPBGetDeviceInfo(mySIRefNum, siSampleSizeAvailable, (Ptr) &sampleSizesInfo); |
if (err != noErr) |
ShowErrAndQuit("SPBGetDeviceInfo with siSampleSizeAvailable", err); |
// Get the sample rates supported |
err = SPBGetDeviceInfo(mySIRefNum, siSampleRateAvailable, (Ptr) &sampleRatesInfo); |
if (err != noErr) |
ShowErrAndQuit("SPBGetDeviceInfo with siSampleRateAvailable", err); |
// get the current settings of the sound input device; to be friendly, we'll set them |
// back after we're through |
err = SPBGetDeviceInfo(mySIRefNum, siInputGain, (Ptr) &previousGain); |
// ignore error, input device isn't required to support this |
err = SPBGetDeviceInfo(mySIRefNum, siVoxRecordInfo, (Ptr) previousVoxRecord); |
// ignore error, input device isn't required to support this |
err = SPBGetDeviceInfo(mySIRefNum, siVoxStopInfo, (Ptr) previousVoxStop); |
// ignore error, input device isn't required to support this |
err = SPBGetDeviceInfo(mySIRefNum, siCompressionType, (Ptr) &previousCompressionType); |
if (err != noErr) |
ShowErrAndQuit("SPBGetDeviceInfo with siCompressionType", err); |
err = SPBGetDeviceInfo(mySIRefNum, siNumberChannels, (Ptr) &previousChannels); |
if (err != noErr) |
ShowErrAndQuit("SPBGetDeviceInfo with siNumberChannels", err); |
err = SPBGetDeviceInfo(mySIRefNum, siSampleRate, (Ptr) &previousSampleRate); |
if (err != noErr) |
ShowErrAndQuit("SPBGetDeviceInfo with siSampleRate", err); |
err = SPBGetDeviceInfo(mySIRefNum, siSampleSize, (Ptr) &previousSampleSize); |
if (err != noErr) |
ShowErrAndQuit("SPBGetDeviceInfo with siSampleSize", err); |
err = SPBGetDeviceInfo(mySIRefNum, siTwosComplementOnOff, (Ptr) &previousTwosComplement); |
if (err != noErr) |
ShowErrAndQuit("SPBGetDeviceInfo with siTwosComplementOnOff", err); |
// pick the biggest sample size available |
sampleSize = 0; |
for (i=0; i< sampleSizesInfo.numberOfSizesSupported; i++) |
if ((*sampleSizesInfo.sizesSupported)[i] > sampleSize) |
sampleSize = (*sampleSizesInfo.sizesSupported)[i]; |
// MACE compression only supported in 8 bit |
if ( compressionSetting == kMace3SubType || compressionSetting == kMace6SubType ) |
{ |
sampleSize = 8; |
twosComplementSetting = 0; |
} |
// now pick the best sample rate available, if the count is 0, you get a range of [low, high] |
// note the sample rate is actually treated as an "unsigned" fixed value |
if (sampleRatesInfo.numberOfRatesSupported == 0) |
sampleRate = (*sampleRatesInfo.ratesSupported)[ 1 ]; |
else |
{ |
sampleRate = 0; |
for (i=0; i< sampleRatesInfo.numberOfRatesSupported; i++) |
if ((*sampleRatesInfo.ratesSupported)[i] > sampleRate) |
sampleRate = (*sampleRatesInfo.ratesSupported)[i]; |
} |
//------------------------------------------------------------------------------- |
// now we'll set the parameters we want - this is very important to do, since you |
// won't get consistant results otherwise. Never assume you know the state! You |
// share the input driver's state with every other application. |
//------------------------------------------------------------------------------- |
// initialize the compression type to NONE, if you want compression, set it last |
// note that not all devices can compress sound during input, and many |
// compressions are not available. |
err = SPBSetDeviceInfo(mySIRefNum, siCompressionType, (Ptr) &noCompression); |
if (err != noErr) |
ShowErrAndQuit("SPBSetDeviceInfo with siCompressionType == NONE", err); |
err = SPBSetDeviceInfo(mySIRefNum, siNumberChannels, (Ptr) &numChannels); |
if (err != noErr) |
ShowErrAndQuit("SPBSetDeviceInfo with siNumberChannels", err); |
err = SPBSetDeviceInfo(mySIRefNum, siSampleSize, (Ptr) &sampleSize); |
if (err != noErr) |
ShowErrAndQuit("SPBSetDeviceInfo with siSampleSize", err); |
err = SPBSetDeviceInfo(mySIRefNum, siSampleRate, (Ptr) &sampleRate); |
if (err != noErr) |
ShowErrAndQuit("SPBSetDeviceInfo with siSampleRate", err); |
err = SPBSetDeviceInfo(mySIRefNum, siTwosComplementOnOff, (Ptr) &twosComplementSetting); |
if (err != noErr) |
ShowErrAndQuit("SPBSetDeviceInfo with siTwosComplementOnOff", err); |
// you may get err == siInvalidCompression(-223) if it's not supported for input |
if ( compressionSetting != noCompression ) |
{ |
err = SPBSetDeviceInfo(mySIRefNum, siCompressionType, (Ptr) &compressionSetting); |
if (err != noErr) |
ShowErrAndQuit("SPBSetDeviceInfo with siCompressionType", err); |
} |
err = SPBSetDeviceInfo(mySIRefNum, siInputGain, (Ptr) &inputGain); |
// ignore error, input device isn't required to support this |
err = SPBSetDeviceInfo(mySIRefNum, siVoxRecordInfo, (Ptr) voxRecord); |
// ignore error, input device isn't required to support this |
err = SPBSetDeviceInfo(mySIRefNum, siVoxStopInfo, (Ptr) voxStop); |
// ignore error, input device isn't required to support this |
// note that you could use siRecordingQuality to set the rate, sample size & compression |
// 'best', 'better' and 'good' quality are defined by the sound input device, |
// so use this if you really don't care what you get |
// now setup the AIFF file header |
err = SetupAIFFHeader(myFRefNum, numChannels, sampleRate, sampleSize, |
compressionSetting, 0, 0); |
if (err != noErr) |
ShowErrAndQuit("SetupAIFFHeader", err); |
// actually record the sound to file |
infoDialog = GetNewDialog(129, 0L, (WindowPtr)-1 ); |
ShowWindow( infoDialog ); |
DrawDialog( infoDialog ); |
soundParamBlock.inRefNum = mySIRefNum; |
soundParamBlock.count = 0; // after recording, this holds the # bytes recorded |
soundParamBlock.milliseconds = soundDuration; |
soundParamBlock.completionRoutine = 0; |
soundParamBlock.interruptRoutine = 0; |
soundParamBlock.userLong = 0; |
soundParamBlock.error = 0; |
soundParamBlock.unused1 = 0; |
err = SPBRecordToFile(myFRefNum, &soundParamBlock, false); // synchronous |
if (err != noErr) |
ShowErrAndQuit("Failure at call to SPBRecordToFile", err); |
CloseDialog( infoDialog ); |
// set the input parameters back the way they were |
err = SPBSetDeviceInfo(mySIRefNum, siCompressionType, (Ptr) &previousCompressionType); |
if (err != noErr) |
ShowErrAndQuit("SPBSetDeviceInfo resetting siCompressionType", err); |
err = SPBSetDeviceInfo(mySIRefNum, siNumberChannels, (Ptr) &previousChannels); |
if (err != noErr) |
ShowErrAndQuit("SPBSetDeviceInfo resetting siNumberChannels", err); |
err = SPBSetDeviceInfo(mySIRefNum, siSampleRate, (Ptr) &previousSampleRate); |
if (err != noErr) |
ShowErrAndQuit("SPBSetDeviceInfo resetting siSampleRate", err); |
err = SPBSetDeviceInfo(mySIRefNum, siSampleSize, (Ptr) &previousSampleSize); |
if (err != noErr) |
ShowErrAndQuit("SPBSetDeviceInfo resetting siSampleSize", err); |
err = SPBSetDeviceInfo(mySIRefNum, siTwosComplementOnOff, (Ptr) &previousTwosComplement); |
if (err != noErr) |
ShowErrAndQuit("SPBSetDeviceInfo resetting siTwosComplementOnOff", err); |
err = SPBSetDeviceInfo(mySIRefNum, siInputGain, (Ptr) &previousGain); |
// ignore error, input device isn't required to support this |
err = SPBSetDeviceInfo(mySIRefNum, siVoxRecordInfo, (Ptr) previousVoxRecord); |
// ignore error, input device isn't required to support this |
err = SPBSetDeviceInfo(mySIRefNum, siVoxStopInfo, (Ptr) previousVoxStop); |
// ignore error, input device isn't required to support this |
// We're done recording so close the device |
err = SPBCloseDevice(mySIRefNum); |
if (err != noErr) |
ShowErrAndQuit("SPBCloseDevice", err); |
// rewind sound file to start |
paramBlock.ioParam.ioCompletion = 0; |
paramBlock.ioParam.ioRefNum = myFRefNum; |
paramBlock.ioParam.ioPosMode = fsFromStart; |
paramBlock.ioParam.ioPosOffset = 0; |
err = PBSetFPos(¶mBlock, false); |
if (err != noErr) |
ShowErrAndQuit("PBSetFPos", err); |
// now resetup file header with actual count of bytes |
err = SetupAIFFHeader(myFRefNum, numChannels, sampleRate, sampleSize, |
compressionSetting, soundParamBlock.count, 0); |
if (err != noErr) |
ShowErrAndQuit("SetupAIFFHeader 2nd time", err); |
// Allocate our own sound channel |
mySoundChannel = 0; |
err = SndNewChannel(&mySoundChannel, sampledSynth, 0, 0); |
if (err != noErr) |
ShowErrAndQuit("SndNewChannel", err); |
// finally play our sound file |
err = SndStartFilePlay(mySoundChannel, myFRefNum, 0, 40000L, 0, 0, 0, false); |
if (err != noErr) |
ShowErrAndQuit("SndStartFilePlay", err); |
// dispose of the sound channel |
SndDisposeChannel(mySoundChannel, true); |
// close the file |
FSClose(myFRefNum); |
// cleanup |
if (sampleSizesInfo.sizesSupported) |
DisposeHandle((Handle)sampleSizesInfo.sizesSupported); |
if (sampleRatesInfo.ratesSupported) |
DisposeHandle((Handle)sampleRatesInfo.ratesSupported); |
} |
void ShowErr(char*text, short err) |
{ |
char s[256]; |
if (err) |
sprintf((char*)s, "Failure at %s, with error = %hd.", text, err); |
else |
sprintf((char*)s, "%s.", text); |
c2pstr(s); |
ParamText((Byte*)s, 0, 0, 0); |
NoteAlert(128, 0); |
} |
void ShowErrAndQuit(char*text, short err) |
{ |
ShowErr( text, err ); |
ExitToShell(); |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-01-14