home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Frozen Fish 1: Amiga
/
FrozenFish-Apr94.iso
/
bbs
/
alib
/
d6xx
/
d684
/
playsound.lha
/
PlaySound
/
PlaySound.c
< prev
next >
Wrap
C/C++ Source or Header
|
1992-06-26
|
17KB
|
758 lines
/*
** PlaySound - Double-buffered IFF-8SVX player
**
** © Copyright 1992 by Olaf `Olsen' Barthel
** All Rights Reserved
*/
/* System includes. */
#include <libraries/iffparse.h>
#include <workbench/startup.h>
#include <graphics/gfxbase.h>
#include <exec/execbase.h>
#include <dos/dosextens.h>
#include <devices/audio.h>
#include <exec/memory.h>
/* Prototypes. */
#include <clib/iffparse_protos.h>
#include <clib/exec_protos.h>
#include <clib/alib_protos.h>
#include <clib/dos_protos.h>
/* `VHDR' header format. */
struct Voice8Header
{
ULONG oneShotHiSamples, /* # samples in the high octave 1-shot part */
repeatHiSamples, /* # samples in the high octave repeat part */
samplesPerHiCycle; /* # samples/cycle in high octave, else 0 */
UWORD samplesPerSec; /* data sampling rate */
UBYTE ctOctave, /* # of octaves of waveforms */
sCompression; /* data compression technique used */
LONG volume; /* playback nominal volume from 0 to Unity
* (full volume). Map this value into
* the output hardware's dynamic range.
*/
};
/* Audio channel indexes, including control channel. */
enum { AUDIO_LEFT, AUDIO_RIGHT, AUDIO_CONTROL, AUDIO_COUNT };
/* If run from Shell, we accept two parameters:
*
* Pattern = The files to play
* BufferSize = The replay buffer size to use.
*/
enum { ARG_PATTERN, ARG_BUFFERSIZE, ARG_COUNT };
/* Channel allocation bits. */
#define LEFT0F 1
#define RIGHT0F 2
#define RIGHT1F 4
#define LEFT1F 8
/* Channel allocation masks (to separate the left from
* the right).
*/
#define LEFT_MASK (LEFT0F | LEFT1F)
#define RIGHT_MASK (RIGHT0F | RIGHT1F)
/* Program version identifier. */
STATIC UBYTE *Version = "\0$VER: PlaySound 1.1 (27.4.92)";
/* Shared libraries. */
struct ExecBase *SysBase;
struct DosLibrary *DOSBase;
struct GfxBase *GfxBase;
struct Library *IFFParseBase;
/* Channel allocation scheme is as follows:
*
* 1) Allocate two stereo channels
* 2) Allocate any left channel
* 3) Allocate any right channel
*/
UBYTE AnyChannel[] =
{
LEFT0F | RIGHT0F,
LEFT0F | RIGHT1F,
LEFT1F | RIGHT0F,
LEFT1F | RIGHT1F,
LEFT0F, LEFT1F,
RIGHT0F,LEFT1F
};
/* Prototypes for this module. */
LONG __saveds Main(VOID);
VOID __regargs StartSound(struct IOAudio **Audio,LONG Rate,LONG Volume,APTR Data,LONG Length,BYTE Stereo);
VOID __regargs WaitSound(struct IOAudio **Audio,BYTE Stereo);
VOID __regargs StopSound(struct IOAudio **Audio,BYTE Stereo);
VOID __regargs HandleSound(STRPTR Name,struct IOAudio **Audio,APTR *AudioData,LONG BufferSize,BYTE Stereo);
BYTE __regargs PlayIt(STRPTR Name,LONG BufferSize);
/* Main():
*
* The program entry point.
*/
LONG __saveds
Main()
{
struct WBStartup *WBenchMsg;
LONG Result = RETURN_FAIL;
struct Process *ThisProcess;
LONG Pri;
LONG BufferSize = 5000;
/* Set up ExecBase */
SysBase = *(struct ExecBase **)4;
/* Pick up current process identifier. */
ThisProcess = (struct Process *)SysBase -> ThisTask;
/* If no CLI info is present, wait for Workbench
* startup message.
*/
if(!ThisProcess -> pr_CLI)
{
WaitPort(&ThisProcess -> pr_MsgPort);
WBenchMsg = (struct WBStartup *)GetMsg(&ThisProcess -> pr_MsgPort);
}
else
WBenchMsg = NULL;
/* Determine current priority. */
Pri = ThisProcess -> pr_Task . tc_Node . ln_Pri;
/* If lower than 20, set the current process priority to 20. */
if(Pri < 20)
SetTaskPri(ThisProcess,20);
/* Open dos.library. */
if(DOSBase = (struct DosLibrary *)OpenLibrary("dos.library",37))
{
/* Open graphics.library */
if(GfxBase = (struct GfxBase *)OpenLibrary("graphics.library",37))
{
/* Open iffparse.library. */
if(IFFParseBase = OpenLibrary("iffparse.library",37))
{
/* If started from Workbench, play the
* sound files passed to us via project
* icons.
*/
if(WBenchMsg)
{
LONG i;
/* Run down the list... */
for(i = 1 ; i < WBenchMsg -> sm_NumArgs ; i++)
{
/* If a lock and a name is present, change
* to the project's home directory and try
* to play the sound file.
*/
if(WBenchMsg -> sm_ArgList[i] . wa_Lock && WBenchMsg -> sm_ArgList[i] . wa_Name)
{
CurrentDir(WBenchMsg -> sm_ArgList[i] . wa_Lock);
PlayIt(WBenchMsg -> sm_ArgList[i] . wa_Name,BufferSize);
}
}
}
else
{
struct AnchorPath *Anchor;
/* Allocate memory for pattern matching buffer. */
if(Anchor = (struct AnchorPath *)AllocVec(sizeof(struct AnchorPath) + 512,MEMF_ANY | MEMF_CLEAR))
{
STRPTR Args[ARG_COUNT] = { NULL, NULL };
struct RDArgs *ArgsPtr;
/* The scanning process is to
* be aborted by pressing ^C.
*/
Anchor -> ap_BreakBits = SIGBREAKF_CTRL_C;
Anchor -> ap_Strlen = 512;
/* Read the arguments... */
if(ArgsPtr = ReadArgs("Pattern,Buffer/K/N",(LONG *)Args,NULL))
{
LONG Error = 0;
/* Did we get a pattern to look for? */
if(Args[ARG_PATTERN])
{
BYTE MatchMade = FALSE;
Result = RETURN_OK;
/* Are we to use a special replay
* buffer size?
*/
if(Args[ARG_BUFFERSIZE])
{
BufferSize = *(LONG *)Args[ARG_BUFFERSIZE];
/* 1000 bytes is the minimum. */
if(BufferSize < 1000)
BufferSize = 1000;
}
/* Look for the first file/directory to match the pattern. */
if(!MatchFirst(Args[ARG_PATTERN],Anchor))
{
STRPTR Name = Anchor -> ap_Buf;
for(;;)
{
/* Did we find a file or a directory? */
if(Anchor -> ap_Info . fib_DirEntryType < 0)
{
MatchMade = TRUE;
/* Play the sound file. */
if(!PlayIt(Name,BufferSize))
{
Printf("PlaySound: Error during channel allocation!\n");
Result = RETURN_ERROR;
break;
}
/* Check for abort signal. */
if(SetSignal(0,0) & SIGBREAKF_CTRL_C)
{
SetSignal(0,SIGBREAKF_CTRL_C);
PrintFault(ERROR_BREAK,"PlaySound");
Result = RETURN_WARN;
break;
}
}
/* Scan for the next matching file name. */
if(MatchNext(Anchor))
{
Error = IoErr();
if(Error && Error != ERROR_NO_MORE_ENTRIES)
{
PrintFault(Error,"PlaySound");
if(Error == ERROR_BREAK)
Result = RETURN_WARN;
else
Result = RETURN_ERROR;
}
break;
}
}
/* Free pattern matching auxilary data. */
MatchEnd(Anchor);
}
else
{
Error = IoErr();
if(Error && Error != ERROR_NO_MORE_ENTRIES)
{
PrintFault(Error,"PlaySound");
if(Error == ERROR_BREAK)
Result = RETURN_WARN;
else
Result = RETURN_ERROR;
}
}
/* Did we find any matching files? */
if(!MatchMade && !Error)
{
Printf("PlaySound: No matching files were found.\n");
Result = RETURN_WARN;
}
}
else
PrintFault(ERROR_REQUIRED_ARG_MISSING,"PlaySound");
/* Free argument parser data. */
FreeArgs(ArgsPtr);
}
/* Free pattern matching buffer. */
FreeVec(Anchor);
}
}
/* Clean up... */
CloseLibrary(IFFParseBase);
}
else
{
if(ThisProcess -> pr_CLI)
Printf("PlaySound: Failed to open iffparse.library!\a\n");
}
CloseLibrary(GfxBase);
}
else
{
if(ThisProcess -> pr_CLI)
Printf("PlaySound: Failed to open graphics.library!\a\n");
}
CloseLibrary(DOSBase);
}
/* If started from Workbench, reply the startup message. */
if(WBenchMsg)
{
Forbid();
ReplyMsg(&WBenchMsg -> sm_Message);
}
/* Reset the task priority. */
SetTaskPri(ThisProcess,Pri);
/* Return success. */
return(Result);
}
/* StartSound():
*
* Play a sound, play it in stereo if possible.
*/
VOID __regargs
StartSound(struct IOAudio **Audio,LONG Rate,LONG Volume,APTR Data,LONG Length,BYTE Stereo)
{
/* Set up the left channel. */
Audio[AUDIO_LEFT] -> ioa_Request . io_Command = CMD_WRITE;
Audio[AUDIO_LEFT] -> ioa_Request . io_Flags = ADIOF_PERVOL;
Audio[AUDIO_LEFT] -> ioa_Period = Rate;
Audio[AUDIO_LEFT] -> ioa_Volume = Volume;
Audio[AUDIO_LEFT] -> ioa_Cycles = 1;
Audio[AUDIO_LEFT] -> ioa_Data = Data;
Audio[AUDIO_LEFT] -> ioa_Length = Length;
/* Are we to play the sound in stereo? */
if(Stereo)
{
/* To avoid echoes or delays, we will synchronize
* both channels. To do the trick, we stop audio
* output until both audio IO requests are running
* and start them both at the same time using CMD_START.
*/
Audio[AUDIO_CONTROL] -> ioa_Request . io_Command = CMD_STOP;
DoIO(Audio[AUDIO_CONTROL]);
/* Set up the right channel. */
Audio[AUDIO_RIGHT] -> ioa_Request . io_Command = CMD_WRITE;
Audio[AUDIO_RIGHT] -> ioa_Request . io_Flags = ADIOF_PERVOL;
Audio[AUDIO_RIGHT] -> ioa_Period = Rate;
Audio[AUDIO_RIGHT] -> ioa_Volume = Volume;
Audio[AUDIO_RIGHT] -> ioa_Cycles = 1;
Audio[AUDIO_RIGHT] -> ioa_Data = Data;
Audio[AUDIO_RIGHT] -> ioa_Length = Length;
/* Start both audio IO requests. */
BeginIO(Audio[AUDIO_LEFT]);
BeginIO(Audio[AUDIO_RIGHT]);
/* Cause audio.device to play the sound on two
* stereo channels.
*/
Audio[AUDIO_CONTROL] -> ioa_Request . io_Command = CMD_START;
DoIO(Audio[AUDIO_CONTROL]);
}
else
BeginIO(Audio[AUDIO_LEFT]);
}
/* WaitSound(struct IOAudio **Audio,BYTE Stereo):
*
* Wait for sound to stop.
*/
VOID __regargs
WaitSound(struct IOAudio **Audio,BYTE Stereo)
{
WaitIO(Audio[AUDIO_LEFT]);
if(Stereo)
WaitIO(Audio[AUDIO_RIGHT]);
}
/* StopSound(struct IOAudio **Audio,BYTE Stereo):
*
* Abort any sound currently playing.
*/
VOID __regargs
StopSound(struct IOAudio **Audio,BYTE Stereo)
{
if(!CheckIO(Audio[AUDIO_LEFT]))
AbortIO(Audio[AUDIO_LEFT]);
if(Stereo)
{
if(!CheckIO(Audio[AUDIO_RIGHT]))
AbortIO(Audio[AUDIO_RIGHT]);
WaitIO(Audio[AUDIO_LEFT]);
WaitIO(Audio[AUDIO_RIGHT]);
}
else
WaitIO(Audio[AUDIO_LEFT]);
}
/* HandleSound():
*
* Play a sound using double-buffering.
*/
VOID __regargs
HandleSound(STRPTR Name,struct IOAudio **Audio,APTR *AudioData,LONG BufferSize,BYTE Stereo)
{
struct IFFHandle *Handle;
struct StoredProperty *Prop;
struct Voice8Header *VoiceHeader;
LONG Rate,
Length,
Volume;
/* Allocate an IFF handle. */
if(Handle = AllocIFF())
{
/* Open the sound file for reading. */
if(Handle -> iff_Stream = Open(Name,MODE_OLDFILE))
{
/* Say it's a plain AmigaDOS file that we
* are about to read.
*/
InitIFFasDOS(Handle);
/* Open the file for reading. */
if(!OpenIFF(Handle,IFFF_READ))
{
/* Remember the voice header chunk if encountered. */
if(!PropChunk(Handle,'8SVX','VHDR'))
{
/* Stop in front of the data body chunk. */
if(!StopChunk(Handle,'8SVX','BODY'))
{
/* Scan the file... */
if(!ParseIFF(Handle,IFFPARSE_SCAN))
{
/* Try to find the voice header chunk. */
if(Prop = FindProp(Handle,'8SVX','VHDR'))
{
VoiceHeader = (struct Voice8Header *)Prop -> sp_Data;
/* No compression and only a single octave, please! */
if(!VoiceHeader -> sCompression && VoiceHeader -> ctOctave == 1)
{
struct ContextNode *ContextNode;
/* Get information on the current chunk. */
if(ContextNode = CurrentChunk(Handle))
{
LONG Bytes = 0;
BYTE Played = FALSE,
Buffer = 0;
/* Play either the one-shot or the repeat part,
* but not both.
*/
Length = VoiceHeader -> oneShotHiSamples ? VoiceHeader -> oneShotHiSamples : VoiceHeader -> repeatHiSamples;
/* Determine replay rate. */
Rate = (GfxBase -> DisplayFlags & PAL ? 3546895 : 3579545) / VoiceHeader -> samplesPerSec;
/* Determine replay volume. */
Volume = (VoiceHeader -> volume * 64) / 0x10000;
/* Start playing... */
while(Length)
{
/* Are we to abort the playing process? */
if(SetSignal(0,0) & SIGBREAKF_CTRL_C)
{
if(Played)
{
StopSound(Audio,Stereo);
Played = FALSE;
}
break;
}
/* Are we to abort the current sound? */
if(SetSignal(0,0) & SIGBREAKF_CTRL_D)
{
SetSignal(0,SIGBREAKF_CTRL_D);
if(Played)
{
StopSound(Audio,Stereo);
Played = FALSE;
}
break;
}
/* Determine number of bytes to read. */
if(Length < BufferSize)
Bytes = Length;
else
Bytes = BufferSize;
Length -= Bytes;
/* Read the following data. */
if(ReadChunkBytes(Handle,AudioData[Buffer],Bytes) != Bytes)
break;
else
{
/* If still playing, wait until
* the previous sound has finished.
*/
if(Played)
WaitSound(Audio,Stereo);
StartSound(Audio,Rate,Volume,AudioData[Buffer],Bytes,Stereo);
/* Remember that we have been active. */
Played = TRUE;
/* Toggle the buffer to play/read. */
Buffer ^= 1;
}
}
/* Wait until the sound is finished. */
if(Played)
WaitSound(Audio,Stereo);
}
}
}
}
}
}
/* Close the IFF read handle. */
CloseIFF(Handle);
}
/* Close the file. */
Close(Handle -> iff_Stream);
}
/* Free the handle data. */
FreeIFF(Handle);
}
}
/* PlayIt(STRPTR Name,LONG BufferSize):
*
* Do the setups necessary to play a sound.
*/
BYTE __regargs
PlayIt(STRPTR Name,LONG BufferSize)
{
BYTE *AudioData[2],Result = FALSE;
/* Allocate the audio buffer. */
if(AudioData[0] = AllocVec(BufferSize * 2,MEMF_CHIP))
{
struct MsgPort *AudioPort;
/* Split the audio buffer in two. */
AudioData[1] = AudioData[0] + BufferSize;
/* Create the IO request reply port. */
if(AudioPort = CreateMsgPort())
{
struct IOAudio *Audio[AUDIO_COUNT];
/* Allocate the audio IO requests. */
if(Audio[AUDIO_LEFT] = (struct IOAudio *)AllocVec(sizeof(struct IOAudio) * AUDIO_COUNT,MEMF_PUBLIC | MEMF_CLEAR))
{
/* Cut the pie into three pieces. */
Audio[AUDIO_RIGHT] = Audio[AUDIO_LEFT] + 1;
Audio[AUDIO_CONTROL] = Audio[AUDIO_RIGHT] + 1;
/* Open audio.device, allocate the channels on the fly. */
Audio[AUDIO_LEFT] -> ioa_Request . io_Message . mn_Node . ln_Type = NT_MESSAGE;
Audio[AUDIO_LEFT] -> ioa_Request . io_Message . mn_Length = sizeof(struct IOAudio);
Audio[AUDIO_LEFT] -> ioa_Request . io_Command = ADCMD_ALLOCATE;
Audio[AUDIO_LEFT] -> ioa_Request . io_Flags = ADIOF_NOWAIT;
Audio[AUDIO_LEFT] -> ioa_Data = AnyChannel;
Audio[AUDIO_LEFT] -> ioa_Length = sizeof(AnyChannel);
Audio[AUDIO_LEFT] -> ioa_Request . io_Message . mn_ReplyPort = AudioPort;
if(!OpenDevice(AUDIONAME,0,Audio[AUDIO_LEFT],0))
{
WORD Count,i;
/* Check whether we got just a single
* or two separate stereo channels.
*/
for(i = 0 ; i < sizeof(AnyChannel) ; i++)
{
if((((LONG)Audio[AUDIO_LEFT] -> ioa_Request . io_Unit) & AnyChannel[i]) == AnyChannel[i])
{
Count = i;
break;
}
}
/* Set up the three audio IO requests. */
for(i = AUDIO_RIGHT ; i < AUDIO_COUNT ; i++)
CopyMem(Audio[AUDIO_LEFT],Audio[i],sizeof(struct IOAudio));
/* We have been successful so far. */
Result = TRUE;
/* Did we get two stereo channels? */
if(Count < 4)
{
/* Retain just the left channel. */
Audio[AUDIO_LEFT] -> ioa_Request . io_Unit = (APTR)(((ULONG)Audio[AUDIO_LEFT] -> ioa_Request . io_Unit) & LEFT_MASK);
/* Retain just the right channel. */
Audio[AUDIO_RIGHT] -> ioa_Request . io_Unit = (APTR)(((ULONG)Audio[AUDIO_RIGHT] -> ioa_Request . io_Unit) & RIGHT_MASK);
/* Play the sound. */
HandleSound(Name,Audio,(APTR *)AudioData,BufferSize,TRUE);
}
else
HandleSound(Name,Audio,(APTR *)AudioData,BufferSize,FALSE);
/* Close the device, freeing the channels. */
CloseDevice(Audio[AUDIO_CONTROL]);
}
/* Delete the audio request block. */
FreeVec(Audio[AUDIO_LEFT]);
}
/* Delete the audio IO request reply port. */
DeleteMsgPort(AudioPort);
}
/* Free the replay buffer. */
FreeVec(AudioData[0]);
}
return(Result);
}