Spaces:
Runtime error
Runtime error
/* | |
* PortAudio Portable Real-Time Audio Library | |
* Latest Version at: http://www.portaudio.com | |
* | |
* Copyright (c) 1999-2010 Phil Burk and Ross Bencina | |
* | |
* Permission is hereby granted, free of charge, to any person obtaining | |
* a copy of this software and associated documentation files | |
* (the "Software"), to deal in the Software without restriction, | |
* including without limitation the rights to use, copy, modify, merge, | |
* publish, distribute, sublicense, and/or sell copies of the Software, | |
* and to permit persons to whom the Software is furnished to do so, | |
* subject to the following conditions: | |
* | |
* The above copyright notice and this permission notice shall be | |
* included in all copies or substantial portions of the Software. | |
* | |
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | |
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR | |
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF | |
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
*/ | |
/* | |
* The text above constitutes the entire PortAudio license; however, | |
* the PortAudio community also makes the following non-binding requests: | |
* | |
* Any person wishing to distribute modifications to the Software is | |
* requested to send the modifications to the original developer so that | |
* they can be incorporated into the canonical version. It is also | |
* requested that these non-binding requests be included along with the | |
* license above. | |
*/ | |
/** | |
* Very simple WAV file writer for saving captured audio. | |
*/ | |
/* Write long word data to a little endian format byte array. */ | |
static void WriteLongLE( unsigned char **addrPtr, unsigned long data ) | |
{ | |
unsigned char *addr = *addrPtr; | |
*addr++ = (unsigned char) data; | |
*addr++ = (unsigned char) (data>>8); | |
*addr++ = (unsigned char) (data>>16); | |
*addr++ = (unsigned char) (data>>24); | |
*addrPtr = addr; | |
} | |
/* Write short word data to a little endian format byte array. */ | |
static void WriteShortLE( unsigned char **addrPtr, unsigned short data ) | |
{ | |
unsigned char *addr = *addrPtr; | |
*addr++ = (unsigned char) data; | |
*addr++ = (unsigned char) (data>>8); | |
*addrPtr = addr; | |
} | |
/* Write IFF ChunkType data to a byte array. */ | |
static void WriteChunkType( unsigned char **addrPtr, unsigned long cktyp ) | |
{ | |
unsigned char *addr = *addrPtr; | |
*addr++ = (unsigned char) (cktyp>>24); | |
*addr++ = (unsigned char) (cktyp>>16); | |
*addr++ = (unsigned char) (cktyp>>8); | |
*addr++ = (unsigned char) cktyp; | |
*addrPtr = addr; | |
} | |
/********************************************************************************* | |
* Open named file and write WAV header to the file. | |
* The header includes the DATA chunk type and size. | |
* Returns number of bytes written to file or negative error code. | |
*/ | |
long Audio_WAV_OpenWriter( WAV_Writer *writer, const char *fileName, int frameRate, int samplesPerFrame ) | |
{ | |
unsigned int bytesPerSecond; | |
unsigned char header[ WAV_HEADER_SIZE ]; | |
unsigned char *addr = header; | |
int numWritten; | |
writer->dataSize = 0; | |
writer->dataSizeOffset = 0; | |
writer->fid = fopen( fileName, "wb" ); | |
if( writer->fid == NULL ) | |
{ | |
return -1; | |
} | |
/* Write RIFF header. */ | |
WriteChunkType( &addr, RIFF_ID ); | |
/* Write RIFF size as zero for now. Will patch later. */ | |
WriteLongLE( &addr, 0 ); | |
/* Write WAVE form ID. */ | |
WriteChunkType( &addr, WAVE_ID ); | |
/* Write format chunk based on AudioSample structure. */ | |
WriteChunkType( &addr, FMT_ID ); | |
WriteLongLE( &addr, 16 ); | |
WriteShortLE( &addr, WAVE_FORMAT_PCM ); | |
bytesPerSecond = frameRate * samplesPerFrame * sizeof( short); | |
WriteShortLE( &addr, (short) samplesPerFrame ); | |
WriteLongLE( &addr, frameRate ); | |
WriteLongLE( &addr, bytesPerSecond ); | |
WriteShortLE( &addr, (short) (samplesPerFrame * sizeof( short)) ); /* bytesPerBlock */ | |
WriteShortLE( &addr, (short) 16 ); /* bits per sample */ | |
/* Write ID and size for 'data' chunk. */ | |
WriteChunkType( &addr, DATA_ID ); | |
/* Save offset so we can patch it later. */ | |
writer->dataSizeOffset = (int) (addr - header); | |
WriteLongLE( &addr, 0 ); | |
numWritten = fwrite( header, 1, sizeof(header), writer->fid ); | |
if( numWritten != sizeof(header) ) return -1; | |
return (int) numWritten; | |
} | |
/********************************************************************************* | |
* Write to the data chunk portion of a WAV file. | |
* Returns bytes written or negative error code. | |
*/ | |
long Audio_WAV_WriteShorts( WAV_Writer *writer, | |
short *samples, | |
int numSamples | |
) | |
{ | |
unsigned char buffer[2]; | |
unsigned char *bufferPtr; | |
int i; | |
short *p = samples; | |
int numWritten; | |
int bytesWritten; | |
if( numSamples <= 0 ) | |
{ | |
return -1; | |
} | |
for( i=0; i<numSamples; i++ ) | |
{ | |
bufferPtr = buffer; | |
WriteShortLE( &bufferPtr, *p++ ); | |
numWritten = fwrite( buffer, 1, sizeof( buffer), writer->fid ); | |
if( numWritten != sizeof(buffer) ) return -1; | |
} | |
bytesWritten = numSamples * sizeof(short); | |
writer->dataSize += bytesWritten; | |
return (int) bytesWritten; | |
} | |
/********************************************************************************* | |
* Close WAV file. | |
* Update chunk sizes so it can be read by audio applications. | |
*/ | |
long Audio_WAV_CloseWriter( WAV_Writer *writer ) | |
{ | |
unsigned char buffer[4]; | |
unsigned char *bufferPtr; | |
int numWritten; | |
int riffSize; | |
/* Go back to beginning of file and update DATA size */ | |
int result = fseek( writer->fid, writer->dataSizeOffset, SEEK_SET ); | |
if( result < 0 ) return result; | |
bufferPtr = buffer; | |
WriteLongLE( &bufferPtr, writer->dataSize ); | |
numWritten = fwrite( buffer, 1, sizeof( buffer), writer->fid ); | |
if( numWritten != sizeof(buffer) ) return -1; | |
/* Update RIFF size */ | |
result = fseek( writer->fid, 4, SEEK_SET ); | |
if( result < 0 ) return result; | |
riffSize = writer->dataSize + (WAV_HEADER_SIZE - 8); | |
bufferPtr = buffer; | |
WriteLongLE( &bufferPtr, riffSize ); | |
numWritten = fwrite( buffer, 1, sizeof( buffer), writer->fid ); | |
if( numWritten != sizeof(buffer) ) return -1; | |
fclose( writer->fid ); | |
writer->fid = NULL; | |
return writer->dataSize; | |
} | |
/********************************************************************************* | |
* Simple test that write a sawtooth waveform to a file. | |
*/ | |
int main( void ) | |
{ | |
int i; | |
WAV_Writer writer; | |
int result; | |
short data[NUM_SAMPLES]; | |
short saw = 0; | |
for( i=0; i<NUM_SAMPLES; i++ ) | |
{ | |
data[i] = saw; | |
saw += 293; | |
} | |
result = Audio_WAV_OpenWriter( &writer, "rendered_midi.wav", 44100, 1 ); | |
if( result < 0 ) goto error; | |
for( i=0; i<15; i++ ) | |
{ | |
result = Audio_WAV_WriteShorts( &writer, data, NUM_SAMPLES ); | |
if( result < 0 ) goto error; | |
} | |
result = Audio_WAV_CloseWriter( &writer ); | |
if( result < 0 ) goto error; | |
return 0; | |
error: | |
printf("ERROR: result = %d\n", result ); | |
return result; | |
} | |