X-Git-Url: https://git.distorted.org.uk/~mdw/disorder/blobdiff_plain/f8f8039fc44104b43b94467d17a8d71fc40f219b..ce6c36be6c2df99afd01a7a602debb321322e113:/server/decode.c diff --git a/server/decode.c b/server/decode.c index 434ad00..d1be348 100644 --- a/server/decode.c +++ b/server/decode.c @@ -33,11 +33,13 @@ #include #include #include -#include +#include #include "log.h" #include "syscalls.h" #include "defs.h" +#include "wav.h" +#include "speaker-protocol.h" /** @brief Encoding lookup table type */ struct decoder { @@ -57,7 +59,7 @@ static FILE *outputfp; static const char *path; /** @brief Input buffer */ -static unsigned char buffer[1048576]; +static char buffer[1048576]; /** @brief Open the input file */ static void open_input(void) { @@ -89,19 +91,18 @@ static inline void output_16(uint16_t n) { */ static void output_header(int rate, int channels, - int bits) { - static int already_written_header; - struct ao_sample_format format; - - if(!already_written_header) { - format.rate = rate; - format.bits = bits; - format.channels = channels; - format.byte_format = AO_FMT_BIG; - if(fwrite(&format, sizeof format, 1, outputfp) < 1) - fatal(errno, "decoding %s: writing format header", path); - already_written_header = 1; - } + int bits, + int nbytes, + int endian) { + struct stream_header header; + + header.rate = rate; + header.bits = bits; + header.channels = channels; + header.endian = endian; + header.nbytes = nbytes; + if(fwrite(&header, sizeof header, 1, outputfp) < 1) + fatal(errno, "decoding %s: writing format header", path); } /** @brief Dithering state @@ -184,7 +185,9 @@ static enum mad_flow mp3_output(void attribute((unused)) *data, output_header(header->samplerate, pcm->channels, - 16); + 16, + 2 * pcm->channels * pcm->length, + ENDIAN_BIG); switch(pcm->channels) { case 1: while(n--) @@ -206,10 +209,10 @@ static enum mad_flow mp3_output(void attribute((unused)) *data, static enum mad_flow mp3_input(void attribute((unused)) *data, struct mad_stream *stream) { const size_t n = fill(); - fprintf(stderr, "n=%zu\n", n); + if(!n) return MAD_FLOW_STOP; - mad_stream_buffer(stream, buffer, n); + mad_stream_buffer(stream, (unsigned char *)buffer, n); return MAD_FLOW_CONTINUE; } @@ -218,17 +221,10 @@ static enum mad_flow mp3_input(void attribute((unused)) *data, static enum mad_flow mp3_error(void attribute((unused)) *data, struct mad_stream *stream, struct mad_frame attribute((unused)) *frame) { - error(0, "decoding %s: %s (%#04x)", - path, mad_stream_errorstr(stream), stream->error); - return MAD_FLOW_CONTINUE; -} - -/** @brief MP3 header callback */ -static enum mad_flow mp3_header(void attribute((unused)) *data, - struct mad_header const *header) { - output_header(header->samplerate, - MAD_NCHANNELS(header), - 16); + if(0) + /* Just generates pointless verbosity l-( */ + error(0, "decoding %s: %s (%#04x)", + path, mad_stream_errorstr(stream), stream->error); return MAD_FLOW_CONTINUE; } @@ -237,17 +233,74 @@ static void decode_mp3(void) { struct mad_decoder mad[1]; open_input(); - mad_decoder_init(mad, 0/*data*/, mp3_input, mp3_header, 0/*filter*/, + mad_decoder_init(mad, 0/*data*/, mp3_input, 0/*header*/, 0/*filter*/, mp3_output, mp3_error, 0/*message*/); if(mad_decoder_run(mad, MAD_DECODER_MODE_SYNC)) exit(1); mad_decoder_finish(mad); } +/** @brief OGG decoder */ +static void decode_ogg(void) { + FILE *fp; + OggVorbis_File vf[1]; + int err; + long n; + int bitstream; + vorbis_info *vi; + + if(!(fp = fopen(path, "rb"))) + fatal(errno, "cannot open %s", path); + /* There doesn't seem to be any standard function for mapping the error codes + * to strings l-( */ + if((err = ov_open(fp, vf, 0/*initial*/, 0/*ibytes*/))) + fatal(0, "ov_fopen %s: %d", path, err); + if(!(vi = ov_info(vf, 0/*link*/))) + fatal(0, "ov_info %s: failed", path); + while((n = ov_read(vf, buffer, sizeof buffer, 1/*bigendianp*/, + 2/*bytes/word*/, 1/*signed*/, &bitstream))) { + if(n < 0) + fatal(0, "ov_read %s: %ld", path, n); + if(bitstream > 0) + fatal(0, "only single-bitstream ogg files are supported"); + output_header(vi->rate, vi->channels, 16/*bits*/, n, ENDIAN_BIG); + if(fwrite(buffer, 1, n, outputfp) < (size_t)n) + fatal(errno, "decoding %s: writing sample data", path); + } +} + +/** @brief Sample data callback used by decode_wav() */ +static int wav_write(struct wavfile attribute((unused)) *f, + const char *data, + size_t nbytes, + void attribute((unused)) *u) { + if(fwrite(data, 1, nbytes, outputfp) < nbytes) + fatal(errno, "decoding %s: writing sample data", path); + return 0; +} + +/** @brief WAV file decoder */ +static void decode_wav(void) { + struct wavfile f[1]; + int err; + + if((err = wav_init(f, path))) + fatal(err, "opening %s", path); + if(f->bits % 8) + fatal(err, "%s: unsupported byte size %d", path, f->bits); + output_header(f->rate, f->channels, f->bits, f->datasize, ENDIAN_LITTLE); + if((err = wav_data(f, wav_write, 0))) + fatal(err, "error decoding %s", path); +} + /** @brief Lookup table of decoders */ static const struct decoder decoders[] = { { "*.mp3", decode_mp3 }, { "*.MP3", decode_mp3 }, + { "*.ogg", decode_ogg }, + { "*.OGG", decode_ogg }, + { "*.wav", decode_wav }, + { "*.WAV", decode_wav }, { 0, 0 } };