home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
GEMini Atari
/
GEMini_Atari_CD-ROM_Walnut_Creek_December_1993.iso
/
files
/
tt
/
wave_20
/
wave.c
(
.txt
)
< prev
next >
Wrap
C/C++ Source or Header
|
1993-07-30
|
15KB
|
464 lines
/**************************************************************
* This program will play a "wave" file on an ATARI TT *
* This program should also run on an STe and Falcon *
* but since I only own an ST and a TT it is only *
* warranteed on a TT. *
* *
* THIS PROGRAM IS NOT MINT/MULTI-TOS FRIENDLY *
* THIS PROGRAM WILL RUN UNDER MINT/MULTI-TOS BUT *
* MULTI_TASKING WILL BE SUSPENDED WHILE PLAYING THE *
* SAMPLE. MULTI_TASKING RESUMES WHEN THE SAMPLE IS *
* FINISHED PLAYING. *
* *
* I know how to fix this and it will be implemented *
* as soon as I can get around to it. *
* *
* Algorithm: *
* open file *
* Read the header of the file. *
* if the header is not valid report that and exit *
* If file is an "RIFF" file convert to motorola integers *
* allocate memory (ST-RAM) for PCM data *
* Read the PCM data *
* close the file *
* If sample is 8 bit then convert data to ATARI format *
* play the Sample *
* wait until sample is completed *
* free memory *
* exit *
* *
* *
* Change History: *
* *
* 12/10/92 Added ability to accept filenames from stdin *
* 12/13/92 Corrected Error msgs relating to memory allocs *
* 05/20/93 Added capability to skip over undesired "chunks" *
* 07/06/93 Added capability to play partial files instead *
* of reporting a file error *
* 07/30/92 Corrected reading from stdin to handle empty lines*
* *
**************************************************************/
/*
The correct method of reading a WAV file is to
read in the RIFF/RIFX chunk id and size along with
the WAVE identifier.
while not the end of the file (WAV files can contain multiple samples)
Begin
Read in the next chunk identifier
while the identifier is not "fmt\0"
Begin (typically this section willnever be used)
report type of chunk encountered.
skip this chunk.
read in the next chunk identifier
End (ckID not "fmt\0")
read in the "fmt\0" chunk data
Read in the next chunk identifier
while the identifier is not "data"
Begin
report type of chunk encountered.
skip this chunk.
read in the next chunk identifier
End (ckID not "data")
read in the data
one second of sample at a time
Begin
re-sample the data (if needed otherwise copy)
play the 1 second
End (One second at a time)
End (Not end of file)
*/
/*
Things to be added:
1) take filenames from STDIN
2) optionally supress messages to stdout/stderr
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <tos.h>
#include "stereo.h"
#include "riff.h"
#define FALSE 0
#define TRUE 1
#define MAX_FNAME_LENGTH 132
#define FROM_START_OF_FILE 0
#define FROM_END_OF_FILE 2
void FatalError (char * id) ;
void MSC_cnvrt_int(INTEGER * i) ;
void MSC_cnvrt_long(LONG * i) ;
void Convert_MSC_ATARI_8_Bit(unsigned char * c, unsigned long i) ;
long Play_PCM_Sample() ;
void process_file(char * fname) ;
int locate_chunk(char * id_fmt, CHK_DEF * Chk, FILE * fp) ;
char cmd[132] ;
char *id_riff = "RIFF";
char *id_rifx = "RIFX";
char *id_wave = "WAVE";
char *id_fmt = "fmt ";
char *id_data = "data" ;
WAVE_DEFINITION *wave ;
PCM_Samples origSample ;
int motorola_fmt ;
main(argc, argv)
int argc ;
char *argv[] ;
{
unsigned long i ;
char fname[MAX_FNAME_LENGTH] ;
strcpy(cmd, argv[0]) ;
fname[0] = '\0' ;
if (argc > 1)
for (i = 1; i < argc; i++)
{
if (argv[i][0] != '-')
{
printf("%s -", argv[i]) ;
process_file(argv[i]) ;
}
}
else
while (fgets(fname, MAX_FNAME_LENGTH, stdin) != NULL)
{
if (strlen(fname) > 1)
process_file(fname) ;
}
return 0 ;
}
void process_file(char * fname)
{
long num_read ;
unsigned long nSamples ;
double Sample_length ;
long BytesPerSample ;
long file_size ;
long file_pos ;
PCM_Samples Dest ;
WAVE_FILE riffChk ;
CHK_DEF fmtChk ;
FILE * fp ;
motorola_fmt = FALSE ;
if (strcmp(fname,"-")==0)
{
fp = stdin;
fname = "<stdin>";
}
else
fp = fopen(fname,"rb");
fseek(fp,0l, FROM_END_OF_FILE) ;
file_size = ftell(fp) ;
fseek(fp,0l, FROM_START_OF_FILE) ;
if (fp == NULL)
FatalError("could not open file.") ;
wave = (WAVE_DEFINITION *) malloc(sizeof(WAVE_DEFINITION)) ;
if (!wave)
FatalError("not enough memory to read '*.WAV' file header");
num_read = fread(&riffChk, 1, sizeof(WAVE_FILE), fp) ;
if (num_read != sizeof(WAVE_FILE))
FatalError("WAV 'riff' header read failed");
if (strncmp(riffChk.riffStr, id_riff, 4))
{
if (strncmp(riffChk.riffStr, id_rifx, 4))
FatalError("not an RIFF/RIFX file");
else
motorola_fmt = TRUE ;
}
if (strncmp(riffChk.waveStr, id_wave, 4))
FatalError("not a WAVE file");
/*
We now are going to have to possible loop through
many "chunks" of information to locate the format
"fmt" chunk for this WAVe file.
*/
if (locate_chunk(id_fmt, &fmtChk, fp) == -1)
{
FatalError("EOF encountered before 'fmt' chunk") ;
}
num_read = fread(&(wave->hdr),1, sizeof(WAVE_HDR), fp) ;
if (num_read != sizeof(WAVE_HDR))
FatalError("WAVE header read failed");
if (locate_chunk(id_data, &(wave->data), fp) == -1)
{
FatalError("EOF encountered before 'data' chunk") ;
}
/*
because some sample wave files are incomplete the next few
lines will make adjustments to try and fix that.
*/
file_pos = ftell(fp) ;
if ((file_pos + wave->data.ckSize) > file_size)
{
wave->data.ckSize = file_size - file_pos ;
}
/* do we need to change the integer storage format to motorola? */
if (motorola_fmt == FALSE)
{
MSC_cnvrt_long(&riffChk.ckSize) ;
/*
MSC_cnvrt_long(&fmtChk.ckSize) ;
*/
MSC_cnvrt_int(&wave->hdr.formatTag) ;
MSC_cnvrt_int(&wave->hdr.nChannels) ;
MSC_cnvrt_long(&wave->hdr.nSamplesPerSec) ;
MSC_cnvrt_long(&wave->hdr.nAvgBytesPerSec) ;
MSC_cnvrt_int(&wave->hdr.nBlockAlign) ;
MSC_cnvrt_int(&wave->hdr.nBitsPerSample) ;
/*
MSC_cnvrt_long(&wave->data.ckSize) ;
*/
}
if (wave->hdr.nChannels > 2)
FatalError("can not accept more than 2 channels in file") ;
/*
Since the STe/TT only support 8 bit PCM codes
We need to determine the correct amount of memory.
We also must take into consideration that that WAVE
file may not be sampled at the same rate the STe/TT
can play it back. So we may need to peform a little
DSP to reconstruct the sample so the Atari can play it
correctly.
*/
/* Determine memory requirements */
BytesPerSample = (wave->hdr.nBitsPerSample + 7) / 8 ;
if (BytesPerSample > 2)
FatalError("can not accept more than 2 bytes per sample") ;
if ( wave->hdr.nSamplesPerSec <= SSS_RATE_50kHz )
wave->Target_SpS = SSS_RATE_50kHz ;
if ( wave->hdr.nSamplesPerSec <= SSS_RATE_25kHz )
wave->Target_SpS = SSS_RATE_25kHz ;
if ( wave->hdr.nSamplesPerSec <= SSS_RATE_12kHz )
wave->Target_SpS = SSS_RATE_12kHz ;
if ( wave->hdr.nSamplesPerSec <= SSS_RATE_6kHz )
wave->Target_SpS = SSS_RATE_6kHz ;
/*
determine the number of seconds the sample will play for
*/
nSamples = (wave->data.ckSize / wave->hdr.nChannels)/ BytesPerSample ;
Sample_length = (double)nSamples / (double)wave->hdr.nSamplesPerSec ;
printf("WAVE file contents:\n") ;
printf(" %d bit", wave->hdr.nBitsPerSample) ;
if (wave->hdr.nChannels == 2)
printf(" Stereo\n") ;
else
printf(" Mono\n") ;
printf("Sample Rate:\n Original : %ld\n Playback :%ld\n",
wave->hdr.nSamplesPerSec, wave->Target_SpS) ;
printf(" %lf seconds playing time\n", Sample_length) ;
/*
determine the number of bytes required to hold
sample at a rate that the Atari can play back.
*/
wave->required_memory = (unsigned long)((double)wave->Target_SpS * Sample_length) ;
wave->required_memory = wave->required_memory * wave->hdr.nChannels ;
/* Allocate memory for the PCM Data */
wave->PCM.Mono8 = (unsigned char *)Mxalloc((wave->required_memory + 256), 0) ;
if (!wave->PCM.Mono8)
FatalError("not enough memory to play sample") ;
if (wave->Target_SpS == wave->hdr.nSamplesPerSec)
{ /* no psuedo DSP algorithm required */
num_read = fread(wave->PCM.Mono8,1, wave->data.ckSize, fp) ;
if (num_read != wave->data.ckSize)
FatalError("error reading WAVE data") ;
}
else
/*
determine the amount of memory required to read in the
entire WAVE file. Use TT-RAM if available (mode = 3)
*/
{
origSample.Mono8 = (unsigned char *)Mxalloc(wave->data.ckSize,3) ;
if (!origSample.Mono8)
FatalError("not enough memory to read in sample");
Dest.Mono8 = wave->PCM.Mono8 ;
num_read = fread(origSample.Mono8,1, wave->data.ckSize, fp) ;
if (num_read != wave->data.ckSize)
{
FatalError("error reading source of WAVE data") ;
wave->required_memory = (long)((float)wave->required_memory
* (float)(num_read)/(float)wave->data.ckSize) ;
wave->data.ckSize = num_read ;
}
/*
If we only had object oriented programming this next section
would have only one line
*/
switch (wave->hdr.nChannels)
{
case 1 : switch ((int)BytesPerSample)
{
case 1: Resample8bitMono(&Dest, &origSample, wave->required_memory, wave->data.ckSize) ;
break ;
case 2: Resample16bitMono(&Dest, &origSample, wave->required_memory, wave->data.ckSize) ;
break ;
default:
FatalError("can not convert this # bytes per sample") ;
}
break ;
case 2 : switch ((int)BytesPerSample)
{
case 1: Resample8bitStereo(&Dest, &origSample, wave->required_memory, wave->data.ckSize) ;
break ;
case 2: Resample16bitStereo(&Dest, &origSample, wave->required_memory, wave->data.ckSize) ;
break ;
default:
FatalError("can not convert this # bytes per sample") ;
}
break ;
default :
FatalError("Incorrect # of channels") ;
}
}
Supexec(Play_PCM_Sample) ;
free(wave->PCM.Mono8) ;
free(wave) ;
free(origSample.Mono8) ;
fclose(fp) ;
}
/***********************************/
void FatalError (identifier)
char *identifier;
{
if (wave != NULL)
{
if (wave->PCM.Mono8 != NULL)
free(wave->PCM.Mono8) ;
free(wave) ;
if (origSample.Mono8 != NULL)
free(origSample.Mono8) ;
}
fprintf(stderr, "Fatal Error: %s: %s\n",cmd, identifier);
exit(-1);
return ;
}
/*
The next two routines convert an integer and a long
that is stored in Intel format to Motorola format
*/
void MSC_cnvrt_int(i)
INTEGER * i ;
{
char tmp ;
tmp = i->MSC[0] ;
i->MSC[0] = i->MSC[1] ;
i->MSC[1] = tmp ;
}
void MSC_cnvrt_long(LONG * i)
{
char tmp ;
tmp = i->MSC[0] ;
i->MSC[0] = i->MSC[3] ;
i->MSC[3] = tmp ;
tmp = i->MSC[1] ;
i->MSC[1] = i->MSC[2] ;
i->MSC[2] = tmp ;
}
/*
Since the following routine accesses the hardware registers
it must be executed in supervisory mode.
*/
long Play_PCM_Sample()
{
SOUND_REGS *hardware = (SOUND_REGS *)0xff8900l ;
unsigned int mode ;
unsigned long tmp ;
/*
hardware = (SOUND_REGS *)malloc(sizeof(SOUND_REGS)) ;
*/
switch (wave->Target_SpS)
{
case SSS_RATE_6kHz : mode = SSS_RATE_06258Hz ; /* 0x0 */
break ;
case SSS_RATE_12kHz : mode = SSS_RATE_12517Hz ; /* 0x1 */
break ;
case SSS_RATE_25kHz : mode = SSS_RATE_25033Hz ; /* 0x2 */
break ;
case SSS_RATE_50kHz : mode = SSS_RATE_50066Hz ; /* 0x3 */
break ;
default : FatalError("can not play sample rate") ;
}
if (wave->hdr.nChannels == 1)
mode = mode | SSS_MONO ;
tmp = (unsigned long)wave->PCM.Mono8 + wave->required_memory ;
hardware->Frame_base_high = ((unsigned long) wave->PCM.Mono8 >> 16) & 0x3f ;
hardware->Frame_base_med = ((unsigned long) wave->PCM.Mono8 >> 8) & 0xff ;
hardware->Frame_base_low = (unsigned long) wave->PCM.Mono8 & 0xfe ;
hardware->Frame_end_high = ((unsigned long) tmp >> 16) & 0x3f ;
hardware->Frame_end_med = ((unsigned long) tmp >> 8) & 0xff ;
hardware->Frame_end_low = (unsigned long) tmp & 0xfe ;
hardware->Mode_cntrl = mode ;
hardware->DMA_cntrl = 0x01 ;
/* wait for the sample to complete */
while(hardware->DMA_cntrl != 0) ;
return 0l ;
}
int locate_chunk(char * id_fmt, CHK_DEF * Chk, FILE * fp)
{
int good_bad ;
int num_read ;
char temp[5] ;
good_bad = 0 ; /* false */
do
{
temp[4] = '\0' ;
num_read = fread(Chk, 1, sizeof(CHK_DEF), fp) ;
if (num_read != sizeof(CHK_DEF))
return -1 ;
if (motorola_fmt == FALSE)
MSC_cnvrt_long(&Chk->ckSize) ;
strncpy(temp, Chk->ckStr, 4) ;
printf("Found Chunk of type : %s\n", temp) ;
if (strncmp(id_fmt, Chk->ckStr, 4) != 0)
{
fseek(fp, Chk->ckSize, 1) ;
}
else
good_bad = 1 ; /* true */
} while (good_bad == 0) ;
return 0 ;
}