|
Packit |
57f8ee |
/*
|
|
Packit |
57f8ee |
*
|
|
Packit |
57f8ee |
* ao_wav.c
|
|
Packit |
57f8ee |
*
|
|
Packit |
57f8ee |
* Original Copyright (C) Aaron Holtzman - May 1999
|
|
Packit |
57f8ee |
* Modifications Copyright (C) Stan Seibert - July 2000, July 2001
|
|
Packit |
57f8ee |
* Copyright (C) Monty - January 2010
|
|
Packit |
57f8ee |
*
|
|
Packit |
57f8ee |
* This file is part of libao, a cross-platform audio output library. See
|
|
Packit |
57f8ee |
* README for a history of this source code.
|
|
Packit |
57f8ee |
*
|
|
Packit |
57f8ee |
* libao is free software; you can redistribute it and/or modify
|
|
Packit |
57f8ee |
* it under the terms of the GNU General Public License as published by
|
|
Packit |
57f8ee |
* the Free Software Foundation; either version 2, or (at your option)
|
|
Packit |
57f8ee |
* any later version.
|
|
Packit |
57f8ee |
*
|
|
Packit |
57f8ee |
* libao is distributed in the hope that it will be useful,
|
|
Packit |
57f8ee |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
57f8ee |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
Packit |
57f8ee |
* GNU General Public License for more details.
|
|
Packit |
57f8ee |
*
|
|
Packit |
57f8ee |
* You should have received a copy of the GNU General Public License
|
|
Packit |
57f8ee |
* along with GNU Make; see the file COPYING. If not, write to
|
|
Packit |
57f8ee |
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
Packit |
57f8ee |
*
|
|
Packit |
57f8ee |
********************************************************************
|
|
Packit |
57f8ee |
|
|
Packit |
57f8ee |
last mod: $Id: ao_wav.c 18200 2012-02-14 00:44:01Z ph3-der-loewe $
|
|
Packit |
57f8ee |
|
|
Packit |
57f8ee |
********************************************************************/
|
|
Packit |
57f8ee |
|
|
Packit |
57f8ee |
|
|
Packit |
57f8ee |
#include <stdio.h>
|
|
Packit |
57f8ee |
#include <errno.h>
|
|
Packit |
57f8ee |
#include <string.h>
|
|
Packit |
57f8ee |
#include <signal.h>
|
|
Packit |
57f8ee |
#include <ao/ao.h>
|
|
Packit |
57f8ee |
|
|
Packit |
57f8ee |
#define WAVE_FORMAT_PCM 0x0001
|
|
Packit |
57f8ee |
#define FORMAT_MULAW 0x0101
|
|
Packit |
57f8ee |
#define IBM_FORMAT_ALAW 0x0102
|
|
Packit |
57f8ee |
#define IBM_FORMAT_ADPCM 0x0103
|
|
Packit |
57f8ee |
#define WAVE_FORMAT_EXTENSIBLE 0xfffe
|
|
Packit |
57f8ee |
|
|
Packit |
57f8ee |
#define WAV_HEADER_LEN 68
|
|
Packit |
57f8ee |
|
|
Packit |
57f8ee |
#define WRITE_U32(buf, x) *(buf) = (unsigned char)(x&0xff);\
|
|
Packit |
57f8ee |
*((buf)+1) = (unsigned char)((x>>8)&0xff);\
|
|
Packit |
57f8ee |
*((buf)+2) = (unsigned char)((x>>16)&0xff);\
|
|
Packit |
57f8ee |
*((buf)+3) = (unsigned char)((x>>24)&0xff);
|
|
Packit |
57f8ee |
|
|
Packit |
57f8ee |
#define WRITE_U16(buf, x) *(buf) = (unsigned char)(x&0xff);\
|
|
Packit |
57f8ee |
*((buf)+1) = (unsigned char)((x>>8)&0xff);
|
|
Packit |
57f8ee |
|
|
Packit |
57f8ee |
#define DEFAULT_SWAP_BUFFER_SIZE 2048
|
|
Packit |
57f8ee |
|
|
Packit |
57f8ee |
struct riff_struct {
|
|
Packit |
57f8ee |
unsigned char id[4]; /* RIFF */
|
|
Packit |
57f8ee |
unsigned int len;
|
|
Packit |
57f8ee |
unsigned char wave_id[4]; /* WAVE */
|
|
Packit |
57f8ee |
};
|
|
Packit |
57f8ee |
|
|
Packit |
57f8ee |
|
|
Packit |
57f8ee |
struct chunk_struct
|
|
Packit |
57f8ee |
{
|
|
Packit |
57f8ee |
unsigned char id[4];
|
|
Packit |
57f8ee |
unsigned int len;
|
|
Packit |
57f8ee |
};
|
|
Packit |
57f8ee |
|
|
Packit |
57f8ee |
struct common_struct
|
|
Packit |
57f8ee |
{
|
|
Packit |
57f8ee |
unsigned short wFormatTag;
|
|
Packit |
57f8ee |
unsigned short wChannels;
|
|
Packit |
57f8ee |
unsigned int dwSamplesPerSec;
|
|
Packit |
57f8ee |
unsigned int dwAvgBytesPerSec;
|
|
Packit |
57f8ee |
unsigned short wBlockAlign;
|
|
Packit |
57f8ee |
unsigned short wBitsPerSample;
|
|
Packit |
57f8ee |
unsigned short cbSize;
|
|
Packit |
57f8ee |
unsigned short wValidBitsPerSample;
|
|
Packit |
57f8ee |
unsigned int dwChannelMask;
|
|
Packit |
57f8ee |
unsigned short subFormat;
|
|
Packit |
57f8ee |
};
|
|
Packit |
57f8ee |
|
|
Packit |
57f8ee |
struct wave_header
|
|
Packit |
57f8ee |
{
|
|
Packit |
57f8ee |
struct riff_struct riff;
|
|
Packit |
57f8ee |
struct chunk_struct format;
|
|
Packit |
57f8ee |
struct common_struct common;
|
|
Packit |
57f8ee |
struct chunk_struct data;
|
|
Packit |
57f8ee |
};
|
|
Packit |
57f8ee |
|
|
Packit |
57f8ee |
|
|
Packit |
57f8ee |
static char *ao_wav_options[] = {"matrix","verbose","quiet","debug"};
|
|
Packit |
57f8ee |
static ao_info ao_wav_info =
|
|
Packit |
57f8ee |
{
|
|
Packit |
57f8ee |
AO_TYPE_FILE,
|
|
Packit |
57f8ee |
"WAV file output",
|
|
Packit |
57f8ee |
"wav",
|
|
Packit |
57f8ee |
"Aaron Holtzman <aholtzma@ess.engr.uvic.ca>",
|
|
Packit |
57f8ee |
"Sends output to a .wav file",
|
|
Packit |
57f8ee |
AO_FMT_LITTLE,
|
|
Packit |
57f8ee |
0,
|
|
Packit |
57f8ee |
ao_wav_options,
|
|
Packit |
57f8ee |
sizeof(ao_wav_options)/sizeof(*ao_wav_options)
|
|
Packit |
57f8ee |
};
|
|
Packit |
57f8ee |
|
|
Packit |
57f8ee |
typedef struct ao_wav_internal
|
|
Packit |
57f8ee |
{
|
|
Packit |
57f8ee |
struct wave_header wave;
|
|
Packit |
57f8ee |
} ao_wav_internal;
|
|
Packit |
57f8ee |
|
|
Packit |
57f8ee |
|
|
Packit |
57f8ee |
static int ao_wav_test(void)
|
|
Packit |
57f8ee |
{
|
|
Packit |
57f8ee |
return 1; /* File driver always works */
|
|
Packit |
57f8ee |
}
|
|
Packit |
57f8ee |
|
|
Packit |
57f8ee |
|
|
Packit |
57f8ee |
static ao_info *ao_wav_driver_info(void)
|
|
Packit |
57f8ee |
{
|
|
Packit |
57f8ee |
return &ao_wav_info;
|
|
Packit |
57f8ee |
}
|
|
Packit |
57f8ee |
|
|
Packit |
57f8ee |
|
|
Packit |
57f8ee |
static int ao_wav_device_init(ao_device *device)
|
|
Packit |
57f8ee |
{
|
|
Packit |
57f8ee |
ao_wav_internal *internal;
|
|
Packit |
57f8ee |
|
|
Packit |
57f8ee |
internal = (ao_wav_internal *) malloc(sizeof(ao_wav_internal));
|
|
Packit |
57f8ee |
|
|
Packit |
57f8ee |
if (internal == NULL)
|
|
Packit |
57f8ee |
return 0; /* Could not initialize device memory */
|
|
Packit |
57f8ee |
|
|
Packit |
57f8ee |
memset(&(internal->wave), 0, sizeof(internal->wave));
|
|
Packit |
57f8ee |
|
|
Packit |
57f8ee |
device->internal = internal;
|
|
Packit |
57f8ee |
device->output_matrix = strdup("L,R,C,LFE,BL,BR,CL,CR,BC,SL,SR");
|
|
Packit |
57f8ee |
device->output_matrix_order = AO_OUTPUT_MATRIX_COLLAPSIBLE;
|
|
Packit |
57f8ee |
|
|
Packit |
57f8ee |
return 1; /* Memory alloc successful */
|
|
Packit |
57f8ee |
}
|
|
Packit |
57f8ee |
|
|
Packit |
57f8ee |
|
|
Packit |
57f8ee |
static int ao_wav_set_option(ao_device *device, const char *key,
|
|
Packit |
57f8ee |
const char *value)
|
|
Packit |
57f8ee |
{
|
|
Packit |
57f8ee |
return 1; /* No options! */
|
|
Packit |
57f8ee |
}
|
|
Packit |
57f8ee |
|
|
Packit |
57f8ee |
static int ao_wav_open(ao_device *device, ao_sample_format *format)
|
|
Packit |
57f8ee |
{
|
|
Packit |
57f8ee |
ao_wav_internal *internal = (ao_wav_internal *) device->internal;
|
|
Packit |
57f8ee |
unsigned char buf[WAV_HEADER_LEN];
|
|
Packit |
57f8ee |
int size = 0x7fffffff; /* Use a bogus size initially */
|
|
Packit |
57f8ee |
|
|
Packit |
57f8ee |
/* Store information */
|
|
Packit |
57f8ee |
internal->wave.common.wChannels = device->output_channels;
|
|
Packit |
57f8ee |
internal->wave.common.wBitsPerSample = ((format->bits+7)>>3)<<3;
|
|
Packit |
57f8ee |
internal->wave.common.wValidBitsPerSample = format->bits;
|
|
Packit |
57f8ee |
internal->wave.common.dwSamplesPerSec = format->rate;
|
|
Packit |
57f8ee |
|
|
Packit |
57f8ee |
memset(buf, 0, WAV_HEADER_LEN);
|
|
Packit |
57f8ee |
|
|
Packit |
57f8ee |
/* Fill out our wav-header with some information. */
|
|
Packit |
57f8ee |
strncpy(internal->wave.riff.id, "RIFF",4);
|
|
Packit |
57f8ee |
internal->wave.riff.len = size - 8;
|
|
Packit |
57f8ee |
strncpy(internal->wave.riff.wave_id, "WAVE",4);
|
|
Packit |
57f8ee |
|
|
Packit |
57f8ee |
strncpy(internal->wave.format.id, "fmt ",4);
|
|
Packit |
57f8ee |
internal->wave.format.len = 40;
|
|
Packit |
57f8ee |
|
|
Packit |
57f8ee |
internal->wave.common.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
|
|
Packit |
57f8ee |
internal->wave.common.dwAvgBytesPerSec =
|
|
Packit |
57f8ee |
internal->wave.common.wChannels *
|
|
Packit |
57f8ee |
internal->wave.common.dwSamplesPerSec *
|
|
Packit |
57f8ee |
(internal->wave.common.wBitsPerSample >> 3);
|
|
Packit |
57f8ee |
|
|
Packit |
57f8ee |
internal->wave.common.wBlockAlign =
|
|
Packit |
57f8ee |
internal->wave.common.wChannels *
|
|
Packit |
57f8ee |
(internal->wave.common.wBitsPerSample >> 3);
|
|
Packit |
57f8ee |
internal->wave.common.cbSize = 22;
|
|
Packit |
57f8ee |
internal->wave.common.subFormat = WAVE_FORMAT_PCM;
|
|
Packit |
57f8ee |
internal->wave.common.dwChannelMask=device->output_mask;
|
|
Packit |
57f8ee |
|
|
Packit |
57f8ee |
strncpy(internal->wave.data.id, "data",4);
|
|
Packit |
57f8ee |
|
|
Packit |
57f8ee |
internal->wave.data.len = size - WAV_HEADER_LEN;
|
|
Packit |
57f8ee |
|
|
Packit |
57f8ee |
strncpy(buf, internal->wave.riff.id, 4);
|
|
Packit |
57f8ee |
WRITE_U32(buf+4, internal->wave.riff.len);
|
|
Packit |
57f8ee |
strncpy(buf+8, internal->wave.riff.wave_id, 4);
|
|
Packit |
57f8ee |
strncpy(buf+12, internal->wave.format.id,4);
|
|
Packit |
57f8ee |
WRITE_U32(buf+16, internal->wave.format.len);
|
|
Packit |
57f8ee |
WRITE_U16(buf+20, internal->wave.common.wFormatTag);
|
|
Packit |
57f8ee |
WRITE_U16(buf+22, internal->wave.common.wChannels);
|
|
Packit |
57f8ee |
WRITE_U32(buf+24, internal->wave.common.dwSamplesPerSec);
|
|
Packit |
57f8ee |
WRITE_U32(buf+28, internal->wave.common.dwAvgBytesPerSec);
|
|
Packit |
57f8ee |
WRITE_U16(buf+32, internal->wave.common.wBlockAlign);
|
|
Packit |
57f8ee |
WRITE_U16(buf+34, internal->wave.common.wBitsPerSample);
|
|
Packit |
57f8ee |
WRITE_U16(buf+36, internal->wave.common.cbSize);
|
|
Packit |
57f8ee |
WRITE_U16(buf+38, internal->wave.common.wValidBitsPerSample);
|
|
Packit |
57f8ee |
WRITE_U32(buf+40, internal->wave.common.dwChannelMask);
|
|
Packit |
57f8ee |
WRITE_U16(buf+44, internal->wave.common.subFormat);
|
|
Packit |
57f8ee |
memcpy(buf+46,"\x00\x00\x00\x00\x10\x00\x80\x00\x00\xAA\x00\x38\x9B\x71",14);
|
|
Packit |
57f8ee |
strncpy(buf+60, internal->wave.data.id, 4);
|
|
Packit |
57f8ee |
WRITE_U32(buf+64, internal->wave.data.len);
|
|
Packit |
57f8ee |
|
|
Packit |
57f8ee |
if (fwrite(buf, sizeof(char), WAV_HEADER_LEN, device->file)
|
|
Packit |
57f8ee |
!= WAV_HEADER_LEN) {
|
|
Packit |
57f8ee |
return 0; /* Could not write wav header */
|
|
Packit |
57f8ee |
}
|
|
Packit |
57f8ee |
|
|
Packit |
57f8ee |
device->driver_byte_format = AO_FMT_LITTLE;
|
|
Packit |
57f8ee |
|
|
Packit |
57f8ee |
return 1;
|
|
Packit |
57f8ee |
}
|
|
Packit |
57f8ee |
|
|
Packit |
57f8ee |
|
|
Packit |
57f8ee |
/*
|
|
Packit |
57f8ee |
* play the sample to the already opened file descriptor
|
|
Packit |
57f8ee |
*/
|
|
Packit |
57f8ee |
static int ao_wav_play(ao_device *device, const char *output_samples,
|
|
Packit |
57f8ee |
uint_32 num_bytes)
|
|
Packit |
57f8ee |
{
|
|
Packit |
57f8ee |
if (fwrite(output_samples, sizeof(char), num_bytes,
|
|
Packit |
57f8ee |
device->file) < num_bytes)
|
|
Packit |
57f8ee |
return 0;
|
|
Packit |
57f8ee |
else
|
|
Packit |
57f8ee |
return 1;
|
|
Packit |
57f8ee |
|
|
Packit |
57f8ee |
}
|
|
Packit |
57f8ee |
|
|
Packit |
57f8ee |
static int ao_wav_close(ao_device *device)
|
|
Packit |
57f8ee |
{
|
|
Packit |
57f8ee |
ao_wav_internal *internal = (ao_wav_internal *) device->internal;
|
|
Packit |
57f8ee |
unsigned char buf[4]; /* For holding length values */
|
|
Packit |
57f8ee |
|
|
Packit |
57f8ee |
long size;
|
|
Packit |
57f8ee |
|
|
Packit |
57f8ee |
/* Find how long our file is in total, including header */
|
|
Packit |
57f8ee |
size = ftell(device->file);
|
|
Packit |
57f8ee |
|
|
Packit |
57f8ee |
if (size < 0) {
|
|
Packit |
57f8ee |
return 0; /* Wav header corrupt */
|
|
Packit |
57f8ee |
}
|
|
Packit |
57f8ee |
|
|
Packit |
57f8ee |
/* Go back and set correct length info */
|
|
Packit |
57f8ee |
|
|
Packit |
57f8ee |
internal->wave.riff.len = size - 8;
|
|
Packit |
57f8ee |
internal->wave.data.len = size - WAV_HEADER_LEN;
|
|
Packit |
57f8ee |
|
|
Packit |
57f8ee |
/* Rewind to riff len and write it */
|
|
Packit |
57f8ee |
if (fseek(device->file, 4, SEEK_SET) < 0)
|
|
Packit |
57f8ee |
return 0; /* Wav header corrupt */
|
|
Packit |
57f8ee |
|
|
Packit |
57f8ee |
WRITE_U32(buf, internal->wave.riff.len);
|
|
Packit |
57f8ee |
if (fwrite(buf, sizeof(char), 4, device->file) < 4)
|
|
Packit |
57f8ee |
return 0; /* Wav header corrupt */
|
|
Packit |
57f8ee |
|
|
Packit |
57f8ee |
|
|
Packit |
57f8ee |
/* Rewind to data len and write it */
|
|
Packit |
57f8ee |
if (fseek(device->file, 64, SEEK_SET) < 0)
|
|
Packit |
57f8ee |
return 0; /* Wav header corrupt */
|
|
Packit |
57f8ee |
|
|
Packit |
57f8ee |
WRITE_U32(buf, internal->wave.data.len);
|
|
Packit |
57f8ee |
if (fwrite(buf, sizeof(char), 4, device->file) < 4)
|
|
Packit |
57f8ee |
return 0; /* Wav header corrupt */
|
|
Packit |
57f8ee |
|
|
Packit |
57f8ee |
|
|
Packit |
57f8ee |
return 1; /* Wav header correct */
|
|
Packit |
57f8ee |
}
|
|
Packit |
57f8ee |
|
|
Packit |
57f8ee |
static void ao_wav_device_clear(ao_device *device)
|
|
Packit |
57f8ee |
{
|
|
Packit |
57f8ee |
ao_wav_internal *internal = (ao_wav_internal *) device->internal;
|
|
Packit |
57f8ee |
|
|
Packit |
57f8ee |
free(internal);
|
|
Packit |
57f8ee |
device->internal=NULL;
|
|
Packit |
57f8ee |
}
|
|
Packit |
57f8ee |
|
|
Packit |
57f8ee |
const char *ao_wav_file_extension(void)
|
|
Packit |
57f8ee |
{
|
|
Packit |
57f8ee |
return "wav";
|
|
Packit |
57f8ee |
}
|
|
Packit |
57f8ee |
|
|
Packit |
57f8ee |
|
|
Packit |
57f8ee |
ao_functions ao_wav = {
|
|
Packit |
57f8ee |
ao_wav_test,
|
|
Packit |
57f8ee |
ao_wav_driver_info,
|
|
Packit |
57f8ee |
ao_wav_device_init,
|
|
Packit |
57f8ee |
ao_wav_set_option,
|
|
Packit |
57f8ee |
ao_wav_open,
|
|
Packit |
57f8ee |
ao_wav_play,
|
|
Packit |
57f8ee |
ao_wav_close,
|
|
Packit |
57f8ee |
ao_wav_device_clear,
|
|
Packit |
57f8ee |
ao_wav_file_extension
|
|
Packit |
57f8ee |
};
|