aboutsummaryrefslogtreecommitdiff
path: root/wave.c
diff options
context:
space:
mode:
authorBill Cox <bill@viasic.com>2011-10-21 03:02:47 -0400
committerBill Cox <bill@viasic.com>2011-10-21 03:02:47 -0400
commitec23ae004aa4dbe018a443b60cd57791d883be6c (patch)
tree5f35f19b286e07ea3475b76a96028d8a7a6a17c4 /wave.c
parent1a154185ef47ab3d58e03ba5e18c085144df40b2 (diff)
downloadsonic-ec23ae004aa4dbe018a443b60cd57791d883be6c.tar.gz
Removed dependence on libsndfile, which complicated compiling on Windows and confused some people who thought using sonic requires this library. Also changed from .flac format to wav file format for samples
Diffstat (limited to 'wave.c')
-rw-r--r--wave.c337
1 files changed, 296 insertions, 41 deletions
diff --git a/wave.c b/wave.c
index db589d7..3b9ed90 100644
--- a/wave.c
+++ b/wave.c
@@ -21,92 +21,337 @@
/*
This file supports read/write wave files.
*/
+#include <stdio.h>
#include <stdlib.h>
-#include <limits.h>
#include <string.h>
-#include <sndfile.h>
#include "wave.h"
+#define WAVE_BUF_LEN 4096
+
struct waveFileStruct {
- SNDFILE *soundFile;
int numChannels;
+ int sampleRate;
+ FILE *soundFile;
+ int bytesWritten; /* The number of bytes written so far, including header */
+ int failed;
+ int isInput;
};
-/* Open the file for reading. Also determine it's sample rate. */
+/* Write a string to a file. */
+static void writeBytes(
+ waveFile file,
+ void *bytes,
+ int length)
+{
+ size_t bytesWritten;
+
+ if(file->failed) {
+ return;
+ }
+ bytesWritten = fwrite(bytes, sizeof(char), length, file->soundFile);
+ if(bytesWritten != length) {
+ fprintf(stderr, "Unable to write to output file");
+ file->failed = 1;
+ }
+ file->bytesWritten += bytesWritten;
+}
+
+/* Write a string to a file. */
+static void writeString(
+ waveFile file,
+ char *string)
+{
+ writeBytes(file, string, strlen(string));
+}
+
+/* Write an integer to a file in little endian order. */
+static void writeInt(
+ waveFile file,
+ int value)
+{
+ char bytes[4];
+ int i;
+
+ for(i = 0; i < 4; i++) {
+ bytes[i] = value;
+ value >>= 8;
+ }
+ writeBytes(file, bytes, 4);
+}
+
+/* Write a short integer to a file in little endian order. */
+static void writeShort(
+ waveFile file,
+ short value)
+{
+ char bytes[2];
+ int i;
+
+ for(i = 0; i < 2; i++) {
+ bytes[i] = value;
+ value >>= 8;
+ }
+ writeBytes(file, bytes, 2);
+}
+
+/* Read bytes from the input file. Return the number of bytes actually read. */
+static int readBytes(
+ waveFile file,
+ void *bytes,
+ int length)
+{
+ if(file->failed) {
+ return 0;
+ }
+ return fread(bytes, sizeof(char), length, file->soundFile);
+}
+
+/* Read an exact number of bytes from the input file. */
+static void readExactBytes(
+ waveFile file,
+ void *bytes,
+ int length)
+{
+ int numRead;
+
+ if(file->failed) {
+ return;
+ }
+ numRead = fread(bytes, sizeof(char), length, file->soundFile);
+ if(numRead != length) {
+ fprintf(stderr, "Failed to read requested bytes from input file\n");
+ file->failed = 1;
+ }
+}
+
+/* Read an integer from the input file */
+static int readInt(
+ waveFile file)
+{
+ unsigned char bytes[4];
+ int value = 0, i;
+
+ readExactBytes(file, bytes, 4);
+ for(i = 3; i >= 0; i--) {
+ value <<= 8;
+ value |= bytes[i];
+ }
+ return value;
+}
+
+/* Read a short from the input file */
+static int readShort(
+ waveFile file)
+{
+ unsigned char bytes[2];
+ int value = 0, i;
+
+ readExactBytes(file, bytes, 2);
+ for(i = 1; i >= 0; i--) {
+ value <<= 8;
+ value |= bytes[i];
+ }
+ return value;
+}
+
+/* Read a string from the input and compare it to an expected string. */
+static void expectString(
+ waveFile file,
+ char *expectedString)
+{
+ char buf[11]; /* Be sure that we never call with a longer string */
+ int length = strlen(expectedString);
+
+ if(length > 10) {
+ fprintf(stderr, "Internal error: expected string too long\n");
+ file->failed = 1;
+ } else {
+ readExactBytes(file, buf, length);
+ buf[length] = '\0';
+ if(strcmp(expectedString, buf)) {
+ fprintf(stderr, "Unsupported wave file format\n");
+ file->failed = 1;
+ }
+ }
+}
+
+/* Write the header of the wave file. */
+static void writeHeader(
+ waveFile file,
+ int sampleRate)
+{
+ /* write the wav file per the wav file format */
+ writeString(file, "RIFF"); /* 00 - RIFF */
+ /* We have to fseek and overwrite this later when we close the file because */
+ /* we don't know how big it is until then. */
+ writeInt(file, 36 /* + dataLength */); /* 04 - how big is the rest of this file? */
+ writeString(file, "WAVE"); /* 08 - WAVE */
+ writeString(file, "fmt "); /* 12 - fmt */
+ writeInt(file, 16); /* 16 - size of this chunk */
+ writeShort(file, 1); /* 20 - what is the audio format? 1 for PCM = Pulse Code Modulation */
+ writeShort(file, 1); /* 22 - mono or stereo? 1 or 2? (or 5 or ???) */
+ writeInt(file, sampleRate); /* 24 - samples per second (numbers per second) */
+ writeInt(file, sampleRate * 2); /* 28 - bytes per second */
+ writeShort(file, 2); /* 32 - # of bytes in one sample, for all channels */
+ writeShort(file, 16); /* 34 - how many bits in a sample(number)? usually 16 or 24 */
+ writeString(file, "data"); /* 36 - data */
+ writeInt(file, 0); /* 40 - how big is this data chunk */
+}
+
+/* Read the header of the wave file. */
+static int readHeader(
+ waveFile file)
+{
+ int data;
+
+ expectString(file, "RIFF");
+ data = readInt(file); /* 04 - how big is the rest of this file? */
+ expectString(file, "WAVE"); /* 08 - WAVE */
+ expectString(file, "fmt "); /* 12 - fmt */
+ data = readInt(file); /* 16 - size of this chunk */
+ if(data != 16) {
+ fprintf(stderr, "Only basic wave files are supported\n");
+ return 0;
+ }
+ data = readShort(file); /* 20 - what is the audio format? 1 for PCM = Pulse Code Modulation */
+ if(data != 1) {
+ fprintf(stderr, "Only PCM wave files are supported\n");
+ return 0;
+ }
+ file->numChannels = readShort(file); /* 22 - mono or stereo? 1 or 2? (or 5 or ???) */
+ file->sampleRate = readInt(file); /* 24 - samples per second (numbers per second) */
+ readInt(file); /* 28 - bytes per second */
+ readShort(file); /* 32 - # of bytes in one sample, for all channels */
+ data = readShort(file); /* 34 - how many bits in a sample(number)? usually 16 or 24 */
+ if(data != 16) {
+ fprintf(stderr, "Only 16 bit PCM wave files are supported\n");
+ return 0;
+ }
+ expectString(file, "data"); /* 36 - data */
+ readInt(file); /* 40 - how big is this data chunk */
+ return 1;
+}
+
+/* Close the input or output file and free the waveFile. */
+static void closeFile(
+ waveFile file)
+{
+ FILE *soundFile = file->soundFile;
+
+ if(soundFile != NULL) {
+ fclose(soundFile);
+ file->soundFile = NULL;
+ }
+ free(file);
+}
+
+/* Open a 16-bit little-endian wav file for reading. It may be mono or stereo. */
waveFile openInputWaveFile(
char *fileName,
int *sampleRate,
int *numChannels)
{
- SF_INFO info;
- SNDFILE *soundFile;
waveFile file;
+ FILE *soundFile = fopen(fileName, "rb");
- info.format = 0;
- soundFile = sf_open(fileName, SFM_READ, &info);
if(soundFile == NULL) {
- fprintf(stderr, "Unable to open wave file %s: %s\n", fileName, sf_strerror(NULL));
+ fprintf(stderr, "Unable to open wave file %s for reading\n", fileName);
return NULL;
}
file = (waveFile)calloc(1, sizeof(struct waveFileStruct));
file->soundFile = soundFile;
- file->numChannels = info.channels;
- *sampleRate = info.samplerate;
- *numChannels = info.channels;
- printf("Frames = %ld, sample rate = %d, channels = %d, format = %d\n",
- info.frames, info.samplerate, info.channels, info.format);
+ file->isInput = 1;
+ if(!readHeader(file)) {
+ closeFile(file);
+ return NULL;
+ }
+ *sampleRate = file->sampleRate;
+ *numChannels = file->numChannels;
return file;
}
-/* Open the file for reading. */
+/* Open a 16-bit little-endian wav file for writing. It may be mono or stereo. */
waveFile openOutputWaveFile(
char *fileName,
int sampleRate,
int numChannels)
{
- SF_INFO info;
- SNDFILE *soundFile;
waveFile file;
+ FILE *soundFile = fopen(fileName, "wb");
- info.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16;
- info.samplerate = sampleRate;
- info.channels = numChannels;
- soundFile = sf_open(fileName, SFM_WRITE, &info);
if(soundFile == NULL) {
- fprintf(stderr, "Unable to open wave file %s: %s\n", fileName, sf_strerror(NULL));
+ fprintf(stderr, "Unable to open wave file %s for writing\n", fileName);
return NULL;
}
file = (waveFile)calloc(1, sizeof(struct waveFileStruct));
file->soundFile = soundFile;
+ file->sampleRate = sampleRate;
file->numChannels = numChannels;
+ writeHeader(file, sampleRate);
+ if(file->failed) {
+ closeFile(file);
+ return NULL;
+ }
return file;
}
/* Close the sound file. */
-void closeWaveFile(
+int closeWaveFile(
waveFile file)
{
- SNDFILE *soundFile = file->soundFile;
+ FILE *soundFile = file->soundFile;
+ int passed = 1;
- sf_close(soundFile);
- free(file);
+ if(!file->isInput) {
+ if(fseek(soundFile, 4, SEEK_SET) != 0) {
+ fprintf(stderr, "Failed to seek on input file.\n");
+ passed = 0;
+ } else {
+ /* Now update the file to have the correct size. */
+ writeInt(file, file->bytesWritten - 8);
+ if(file->failed) {
+ fprintf(stderr, "Failed to write wave file size.\n");
+ passed = 0;
+ }
+ if(fseek(soundFile, 40, SEEK_SET) != 0) {
+ fprintf(stderr, "Failed to seek on input file.\n");
+ passed = 0;
+ } else {
+ /* Now update the file to have the correct size. */
+ writeInt(file, file->bytesWritten - 48);
+ if(file->failed) {
+ fprintf(stderr, "Failed to write wave file size.\n");
+ passed = 0;
+ }
+ }
+ }
+ }
+ closeFile(file);
+ return passed;
}
-/* Read from the wave file. */
+/* Read from the wave file. Return the number of samples read. */
int readFromWaveFile(
waveFile file,
short *buffer,
int maxSamples)
{
- SNDFILE *soundFile = file->soundFile;
- int samplesRead;
- int numChannels = file->numChannels;
+ int i, bytesRead, samplesRead;
+ int bytePos = 0;
+ unsigned char bytes[WAVE_BUF_LEN];
+ short sample;
- samplesRead = sf_read_short(soundFile, buffer, maxSamples*numChannels);
- if(samplesRead <= 0) {
- return 0;
+ if(maxSamples*file->numChannels*2 > WAVE_BUF_LEN) {
+ maxSamples = WAVE_BUF_LEN/(file->numChannels*2);
+ }
+ bytesRead = readBytes(file, bytes, maxSamples*file->numChannels*2);
+ samplesRead = bytesRead/(file->numChannels*2);
+ for(i = 0; i < samplesRead*file->numChannels; i++) {
+ sample = bytes[bytePos++];
+ sample |= (unsigned int)bytes[bytePos++] << 8;
+ *buffer++ = sample;
}
- return samplesRead/numChannels;
+ return samplesRead;
}
/* Write to the wave file. */
@@ -115,13 +360,23 @@ int writeToWaveFile(
short *buffer,
int numSamples)
{
- SNDFILE *soundFile = file->soundFile;
- int numWritten;
+ int i;
+ int bytePos = 0;
+ unsigned char bytes[WAVE_BUF_LEN];
+ short sample;
+ int total = numSamples*file->numChannels;
- numWritten = sf_write_short(soundFile, buffer, numSamples*file->numChannels);
- if(numWritten != numSamples*file->numChannels) {
- fprintf(stderr, "Unable to write wave file.\n");
- return 0;
+ for(i = 0; i < total; i++) {
+ if(bytePos == WAVE_BUF_LEN) {
+ writeBytes(file, bytes, bytePos);
+ bytePos = 0;
+ }
+ sample = buffer[i];
+ bytes[bytePos++] = sample;
+ bytes[bytePos++] = sample >> 8;
}
- return 1;
+ if(bytePos != 0) {
+ writeBytes(file, bytes, bytePos);
+ }
+ return file->failed;
}