From 2ab3d99967d68c79351fe2a3df22c445447e3010 Mon Sep 17 00:00:00 2001 From: Michał Cichoń Date: Tue, 15 Nov 2011 22:28:48 +0100 Subject: Initial import. --- libao/src/src/ao_wav.c | 291 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 291 insertions(+) create mode 100644 libao/src/src/ao_wav.c (limited to 'libao/src/src/ao_wav.c') diff --git a/libao/src/src/ao_wav.c b/libao/src/src/ao_wav.c new file mode 100644 index 0000000..297a8ac --- /dev/null +++ b/libao/src/src/ao_wav.c @@ -0,0 +1,291 @@ +/* + * + * ao_wav.c + * + * Original Copyright (C) Aaron Holtzman - May 1999 + * Modifications Copyright (C) Stan Seibert - July 2000, July 2001 + * Copyright (C) Monty - January 2010 + * + * 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. + * + ******************************************************************** + + last mod: $Id: ao_wav.c 17718 2010-12-06 20:09:29Z xiphmont $ + + ********************************************************************/ + + +#include +#include +#include +#include +#include + +#define WAVE_FORMAT_PCM 0x0001 +#define FORMAT_MULAW 0x0101 +#define IBM_FORMAT_ALAW 0x0102 +#define IBM_FORMAT_ADPCM 0x0103 +#define WAVE_FORMAT_EXTENSIBLE 0xfffe + +#define WAV_HEADER_LEN 68 + +#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; + unsigned short cbSize; + unsigned short wValidBitsPerSample; + unsigned int dwChannelMask; + unsigned short subFormat; +}; + +struct wave_header +{ + struct riff_struct riff; + struct chunk_struct format; + struct common_struct common; + struct chunk_struct data; +}; + + +static char *ao_wav_options[] = {"matrix","verbose","quiet","debug"}; +static ao_info ao_wav_info = +{ + AO_TYPE_FILE, + "WAV file output", + "wav", + "Aaron Holtzman ", + "Sends output to a .wav file", + AO_FMT_LITTLE, + 0, + ao_wav_options, + sizeof(ao_wav_options)/sizeof(*ao_wav_options) +}; + +typedef struct ao_wav_internal +{ + struct wave_header wave; +} 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; + device->output_matrix = strdup("L,R,C,LFE,BL,BR,CL,CR,BC,SL,SR"); + device->output_matrix_order = AO_OUTPUT_MATRIX_COLLAPSIBLE; + + 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 = device->output_channels; + internal->wave.common.wBitsPerSample = ((format->bits+7)>>3)<<3; + internal->wave.common.wValidBitsPerSample = format->bits; + internal->wave.common.dwSamplesPerSec = format->rate; + + memset(buf, 0, WAV_HEADER_LEN); + + /* 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 = 40; + + internal->wave.common.wFormatTag = WAVE_FORMAT_EXTENSIBLE; + 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); + internal->wave.common.cbSize = 22; + internal->wave.common.subFormat = WAVE_FORMAT_PCM; + internal->wave.common.dwChannelMask=device->output_mask; + + strncpy(internal->wave.data.id, "data",4); + + internal->wave.data.len = size - WAV_HEADER_LEN; + + 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); + WRITE_U16(buf+36, internal->wave.common.cbSize); + WRITE_U16(buf+38, internal->wave.common.wValidBitsPerSample); + WRITE_U32(buf+40, internal->wave.common.dwChannelMask); + WRITE_U16(buf+44, internal->wave.common.subFormat); + memcpy(buf+46,"\x00\x00\x00\x00\x10\x00\x80\x00\x00\xAA\x00\x38\x9B\x71",14); + strncpy(buf+60, internal->wave.data.id, 4); + WRITE_U32(buf+64, 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; + + return 1; +} + + +/* + * 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 */ + + long size; + + /* Find how long our file is in total, including header */ + size = ftell(device->file); + + if (size < 0) { + return 0; /* Wav header corrupt */ + } + + /* Go back and set correct length info */ + + internal->wave.riff.len = size - 8; + internal->wave.data.len = size - WAV_HEADER_LEN; + + /* 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, 64, 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; + + free(internal); + device->internal=NULL; +} + + +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, + ao_wav_device_clear +}; -- cgit v1.2.3