Newer
Older
/*
*
* ao_wav.c
*
* Original Copyright (C) Aaron Holtzman - May 1999
* Modifications Copyright (C) Stan Seibert - July 2000, July 2001
* This file is part of libao, a cross-platform audio output library. See
* README for a history of this source code.
*
* libao is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* libao is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Make; see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <signal.h>

Kenneth C. Arnold
committed
#include <ao/ao.h>
#define WAVE_FORMAT_PCM 0x0001
#define FORMAT_MULAW 0x0101
#define IBM_FORMAT_ALAW 0x0102
#define IBM_FORMAT_ADPCM 0x0103
#define WAV_HEADER_LEN 44
#define WRITE_U32(buf, x) *(buf) = (unsigned char)(x&0xff);\
*((buf)+1) = (unsigned char)((x>>8)&0xff);\
*((buf)+2) = (unsigned char)((x>>16)&0xff);\
*((buf)+3) = (unsigned char)((x>>24)&0xff);
#define WRITE_U16(buf, x) *(buf) = (unsigned char)(x&0xff);\
*((buf)+1) = (unsigned char)((x>>8)&0xff);
#define DEFAULT_SWAP_BUFFER_SIZE 2048
struct riff_struct {
unsigned char id[4]; /* RIFF */
unsigned int len;
unsigned char wave_id[4]; /* WAVE */
};
struct chunk_struct
{
unsigned char id[4];
unsigned int len;
};
struct common_struct
{
unsigned short wFormatTag;
unsigned short wChannels;
unsigned int dwSamplesPerSec;
unsigned int dwAvgBytesPerSec;
unsigned short wBlockAlign;
unsigned short wBitsPerSample; /* Only for PCM */
};
struct wave_header
{
struct riff_struct riff;
struct chunk_struct format;
struct common_struct common;
struct chunk_struct data;
};
static ao_info ao_wav_info =
"WAV file output",
"wav",
"Aaron Holtzman <aholtzma@ess.engr.uvic.ca>",
"Sends output to a .wav file",
AO_FMT_LITTLE,
0,
NULL, /* No options */
0
typedef struct ao_wav_internal
static int ao_wav_test(void)
return 1; /* File driver always works */
}
static ao_info *ao_wav_driver_info(void)
{
return &ao_wav_info;
static int ao_wav_device_init(ao_device *device)
ao_wav_internal *internal;
internal = (ao_wav_internal *) malloc(sizeof(ao_wav_internal));
if (internal == NULL)
return 0; /* Could not initialize device memory */
memset(&(internal->wave), 0, sizeof(internal->wave));
device->internal = internal;
return 1; /* Memory alloc successful */
}
static int ao_wav_set_option(ao_device *device, const char *key,
const char *value)
{
return 1; /* No options! */
}
static int ao_wav_open(ao_device *device, ao_sample_format *format)
{
ao_wav_internal *internal = (ao_wav_internal *) device->internal;
unsigned char buf[WAV_HEADER_LEN];
int size = 0x7fffffff; /* Use a bogus size initially */
/* Store information */
internal->wave.common.wChannels = format->channels;
internal->wave.common.wBitsPerSample = format->bits;
internal->wave.common.dwSamplesPerSec = format->rate;
memset(buf, 0, WAV_HEADER_LEN);
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
/* Fill out our wav-header with some information. */
strncpy(internal->wave.riff.id, "RIFF",4);
internal->wave.riff.len = size - 8;
strncpy(internal->wave.riff.wave_id, "WAVE",4);
strncpy(internal->wave.format.id, "fmt ",4);
internal->wave.format.len = 16;
internal->wave.common.wFormatTag = WAVE_FORMAT_PCM;
internal->wave.common.dwAvgBytesPerSec =
internal->wave.common.wChannels *
internal->wave.common.dwSamplesPerSec *
(internal->wave.common.wBitsPerSample >> 3);
internal->wave.common.wBlockAlign =
internal->wave.common.wChannels *
(internal->wave.common.wBitsPerSample >> 3);
strncpy(internal->wave.data.id, "data",4);
internal->wave.data.len = size - 44;
strncpy(buf, internal->wave.riff.id, 4);
WRITE_U32(buf+4, internal->wave.riff.len);
strncpy(buf+8, internal->wave.riff.wave_id, 4);
strncpy(buf+12, internal->wave.format.id,4);
WRITE_U32(buf+16, internal->wave.format.len);
WRITE_U16(buf+20, internal->wave.common.wFormatTag);
WRITE_U16(buf+22, internal->wave.common.wChannels);
WRITE_U32(buf+24, internal->wave.common.dwSamplesPerSec);
WRITE_U32(buf+28, internal->wave.common.dwAvgBytesPerSec);
WRITE_U16(buf+32, internal->wave.common.wBlockAlign);
WRITE_U16(buf+34, internal->wave.common.wBitsPerSample);
strncpy(buf+36, internal->wave.data.id, 4);
WRITE_U32(buf+40, internal->wave.data.len);
if (fwrite(buf, sizeof(char), WAV_HEADER_LEN, device->file)
!= WAV_HEADER_LEN) {
return 0; /* Could not write wav header */
device->driver_byte_format = AO_FMT_LITTLE;
}
/*
* play the sample to the already opened file descriptor
*/
static int ao_wav_play(ao_device *device, const char *output_samples,
uint_32 num_bytes)
if (fwrite(output_samples, sizeof(char), num_bytes,
device->file) < num_bytes)
return 0;
else
return 1;
static int ao_wav_close(ao_device *device)
ao_wav_internal *internal = (ao_wav_internal *) device->internal;
unsigned char buf[4]; /* For holding length values */
/* Find how long our file is in total, including header */
size = ftell(device->file);
return 0; /* Wav header corrupt */
/* Go back and set correct length info */
internal->wave.riff.len = size - 8;
internal->wave.data.len = size - 44;
/* Rewind to riff len and write it */
if (fseek(device->file, 4, SEEK_SET) < 0)
return 0; /* Wav header corrupt */
WRITE_U32(buf, internal->wave.riff.len);
if (fwrite(buf, sizeof(char), 4, device->file) < 4)
return 0; /* Wav header corrupt */
/* Rewind to data len and write it */
if (fseek(device->file, 40, SEEK_SET) < 0)
return 0; /* Wav header corrupt */
WRITE_U32(buf, internal->wave.data.len);
if (fwrite(buf, sizeof(char), 4, device->file) < 4)
return 0; /* Wav header corrupt */
return 1; /* Wav header correct */
static void ao_wav_device_clear(ao_device *device)
ao_wav_internal *internal = (ao_wav_internal *) device->internal;
ao_functions ao_wav = {
ao_wav_test,
ao_wav_driver_info,
ao_wav_device_init,
ao_wav_set_option,
ao_wav_open,
ao_wav_play,
ao_wav_close,