2002-05-30 11:53:51 +00:00
|
|
|
#include "config.h"
|
|
|
|
|
2001-06-12 11:00:15 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
2002-05-30 11:53:51 +00:00
|
|
|
#include <string.h>
|
2001-06-12 11:00:15 +00:00
|
|
|
|
2002-04-26 13:07:03 +00:00
|
|
|
#include "bswap.h"
|
|
|
|
#include "afmt.h"
|
2001-06-12 11:00:15 +00:00
|
|
|
#include "audio_out.h"
|
|
|
|
#include "audio_out_internal.h"
|
|
|
|
|
|
|
|
static ao_info_t info =
|
|
|
|
{
|
2001-06-12 14:24:26 +00:00
|
|
|
"RAW PCM/WAVE file writer audio output",
|
2001-06-12 11:00:15 +00:00
|
|
|
"pcm",
|
|
|
|
"Atmosfear",
|
|
|
|
""
|
|
|
|
};
|
|
|
|
|
|
|
|
LIBAO_EXTERN(pcm)
|
|
|
|
|
2002-03-02 18:54:58 +00:00
|
|
|
extern int vo_pts;
|
|
|
|
|
2001-06-12 14:24:26 +00:00
|
|
|
char *ao_outputfilename = NULL;
|
|
|
|
int ao_pcm_waveheader = 1;
|
|
|
|
|
|
|
|
#define WAV_ID_RIFF 0x46464952 /* "RIFF" */
|
|
|
|
#define WAV_ID_WAVE 0x45564157 /* "WAVE" */
|
|
|
|
#define WAV_ID_FMT 0x20746d66 /* "fmt " */
|
|
|
|
#define WAV_ID_DATA 0x61746164 /* "data" */
|
|
|
|
#define WAV_ID_PCM 0x0001
|
|
|
|
|
|
|
|
struct WaveHeader
|
|
|
|
{
|
|
|
|
unsigned long riff;
|
|
|
|
unsigned long file_length;
|
|
|
|
unsigned long wave;
|
|
|
|
unsigned long fmt;
|
|
|
|
unsigned long fmt_length;
|
|
|
|
short fmt_tag;
|
|
|
|
short channels;
|
|
|
|
unsigned long sample_rate;
|
|
|
|
unsigned long bytes_per_second;
|
|
|
|
short block_align;
|
|
|
|
short bits;
|
|
|
|
unsigned long data;
|
|
|
|
unsigned long data_length;
|
|
|
|
};
|
|
|
|
|
2002-04-26 13:07:03 +00:00
|
|
|
/* init with default values */
|
2001-06-12 14:24:26 +00:00
|
|
|
static struct WaveHeader wavhdr = {
|
2002-04-26 13:07:03 +00:00
|
|
|
le2me_32(WAV_ID_RIFF),
|
2003-01-19 16:49:02 +00:00
|
|
|
/* same conventions than in sox/wav.c/wavwritehdr() */
|
2003-02-02 16:34:54 +00:00
|
|
|
0, //le2me_32(0x7ffff024),
|
2002-04-26 13:07:03 +00:00
|
|
|
le2me_32(WAV_ID_WAVE),
|
|
|
|
le2me_32(WAV_ID_FMT),
|
|
|
|
le2me_32(16),
|
|
|
|
le2me_16(WAV_ID_PCM),
|
|
|
|
le2me_16(2),
|
|
|
|
le2me_32(44100),
|
|
|
|
le2me_32(192000),
|
|
|
|
le2me_16(4),
|
|
|
|
le2me_16(16),
|
|
|
|
le2me_32(WAV_ID_DATA),
|
2003-02-02 16:34:54 +00:00
|
|
|
0, //le2me_32(0x7ffff000)
|
2001-06-12 14:24:26 +00:00
|
|
|
};
|
|
|
|
|
2001-06-12 11:00:15 +00:00
|
|
|
static FILE *fp = NULL;
|
|
|
|
|
|
|
|
// to set/get/query special features/parameters
|
2003-03-21 16:42:50 +00:00
|
|
|
static int control(int cmd,void *arg){
|
2001-06-12 11:00:15 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// open & setup audio device
|
|
|
|
// return: 1=success 0=fail
|
|
|
|
static int init(int rate,int channels,int format,int flags){
|
2002-04-26 13:07:03 +00:00
|
|
|
int bits;
|
2001-06-12 14:24:26 +00:00
|
|
|
if(!ao_outputfilename) {
|
2003-01-29 12:22:00 +00:00
|
|
|
ao_outputfilename = strdup(ao_pcm_waveheader ? "audiodump.wav" : "audiodump.pcm");
|
2001-06-12 14:24:26 +00:00
|
|
|
}
|
2002-10-07 19:51:00 +00:00
|
|
|
|
2002-04-26 13:07:03 +00:00
|
|
|
/* bits is only equal to format if (format == 8) or (format == 16);
|
|
|
|
this means that the following "if" is a kludge and should
|
|
|
|
really be a switch to be correct in all cases */
|
|
|
|
|
2002-10-07 19:51:00 +00:00
|
|
|
bits=8;
|
|
|
|
switch(format){
|
|
|
|
case AFMT_S8:
|
|
|
|
format=AFMT_U8;
|
|
|
|
case AFMT_U8:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
format=AFMT_S16_LE;
|
|
|
|
bits=16;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
ao_data.outburst = 65536;
|
|
|
|
ao_data.buffersize= 2*65536;
|
|
|
|
ao_data.channels=channels;
|
|
|
|
ao_data.samplerate=rate;
|
|
|
|
ao_data.format=format;
|
|
|
|
ao_data.bps=channels*rate*(bits/8);
|
|
|
|
|
|
|
|
wavhdr.channels = le2me_16(ao_data.channels);
|
|
|
|
wavhdr.sample_rate = le2me_32(ao_data.samplerate);
|
|
|
|
wavhdr.bytes_per_second = le2me_32(ao_data.bps);
|
2002-04-26 13:07:03 +00:00
|
|
|
wavhdr.bits = le2me_16(bits);
|
2003-02-04 18:22:43 +00:00
|
|
|
|
|
|
|
wavhdr.data_length=le2me_32(0x7ffff000);
|
|
|
|
wavhdr.file_length = wavhdr.data_length + sizeof(wavhdr) - 8;
|
2002-04-26 13:07:03 +00:00
|
|
|
|
|
|
|
printf("PCM: File: %s (%s)\n"
|
|
|
|
"PCM: Samplerate: %iHz Channels: %s Format %s\n",
|
|
|
|
ao_outputfilename, (ao_pcm_waveheader?"WAVE":"RAW PCM"), rate,
|
|
|
|
(channels > 1) ? "Stereo" : "Mono", audio_out_format_name(format));
|
2003-05-05 23:43:30 +00:00
|
|
|
printf("PCM: Info: fastest dumping is achieved with -vc dummy -vo null\n"
|
2002-04-26 13:07:03 +00:00
|
|
|
"PCM: Info: to write WAVE files use -waveheader (default); "
|
|
|
|
"for RAW PCM -nowaveheader.\n");
|
|
|
|
|
2001-06-12 14:24:26 +00:00
|
|
|
fp = fopen(ao_outputfilename, "wb");
|
|
|
|
if(fp) {
|
2003-02-04 18:22:43 +00:00
|
|
|
if(ao_pcm_waveheader){ /* Reserve space for wave header */
|
2002-06-22 12:10:02 +00:00
|
|
|
fwrite(&wavhdr,sizeof(wavhdr),1,fp);
|
2003-02-04 18:22:43 +00:00
|
|
|
wavhdr.file_length=wavhdr.data_length=0;
|
|
|
|
}
|
2001-06-12 14:24:26 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
printf("PCM: Failed to open %s for writing!\n", ao_outputfilename);
|
2001-06-12 11:00:15 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// close audio device
|
|
|
|
static void uninit(){
|
2001-06-12 14:24:26 +00:00
|
|
|
|
2002-06-22 12:10:02 +00:00
|
|
|
if(ao_pcm_waveheader && fseek(fp, 0, SEEK_SET) == 0){ /* Write wave header */
|
2002-03-02 18:54:58 +00:00
|
|
|
wavhdr.file_length = wavhdr.data_length + sizeof(wavhdr) - 8;
|
2002-04-26 13:07:03 +00:00
|
|
|
wavhdr.file_length = le2me_32(wavhdr.file_length);
|
2002-07-21 12:13:40 +00:00
|
|
|
wavhdr.data_length = le2me_32(wavhdr.data_length);
|
2001-06-12 14:24:26 +00:00
|
|
|
fwrite(&wavhdr,sizeof(wavhdr),1,fp);
|
|
|
|
}
|
2001-06-12 11:00:15 +00:00
|
|
|
fclose(fp);
|
|
|
|
}
|
|
|
|
|
|
|
|
// stop playing and empty buffers (for seeking/pause)
|
|
|
|
static void reset(){
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// stop playing, keep buffers (for pause)
|
|
|
|
static void audio_pause()
|
|
|
|
{
|
|
|
|
// for now, just call reset();
|
|
|
|
reset();
|
|
|
|
}
|
|
|
|
|
|
|
|
// resume playing, after audio_pause()
|
|
|
|
static void audio_resume()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
// return: how many bytes can be played without blocking
|
|
|
|
static int get_space(){
|
|
|
|
|
2002-04-26 13:07:03 +00:00
|
|
|
if(vo_pts)
|
|
|
|
return ao_data.pts < vo_pts ? ao_data.outburst : 0;
|
|
|
|
return ao_data.outburst;
|
2001-06-12 11:00:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// plays 'len' bytes of 'data'
|
|
|
|
// it should round it down to outburst*n
|
|
|
|
// return: number of bytes played
|
|
|
|
static int play(void* data,int len,int flags){
|
|
|
|
|
2002-10-07 19:51:00 +00:00
|
|
|
// let libaf to do the conversion...
|
|
|
|
#if 0
|
|
|
|
//#ifdef WORDS_BIGENDIAN
|
|
|
|
if (ao_data.format == AFMT_S16_LE) {
|
|
|
|
unsigned short *buffer = (unsigned short *) data;
|
|
|
|
register int i;
|
2002-04-26 13:07:03 +00:00
|
|
|
for(i = 0; i < len/2; ++i) {
|
|
|
|
buffer[i] = le2me_16(buffer[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
2002-10-07 19:51:00 +00:00
|
|
|
|
2001-06-12 11:00:15 +00:00
|
|
|
//printf("PCM: Writing chunk!\n");
|
|
|
|
fwrite(data,len,1,fp);
|
2002-04-26 13:07:03 +00:00
|
|
|
|
2001-06-12 14:24:26 +00:00
|
|
|
if(ao_pcm_waveheader)
|
|
|
|
wavhdr.data_length += len;
|
|
|
|
|
2001-06-12 11:00:15 +00:00
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
2001-11-24 05:21:22 +00:00
|
|
|
// return: delay in seconds between first and last sample in buffer
|
|
|
|
static float get_delay(){
|
2001-06-12 11:00:15 +00:00
|
|
|
|
2001-11-24 05:21:22 +00:00
|
|
|
return 0.0;
|
2001-06-12 11:00:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|