#include <assert.h>
#include <fnmatch.h>
#include <mad.h>
-#include <ao/ao.h>
+#include <vorbis/vorbisfile.h>
#include "log.h"
#include "syscalls.h"
#include "defs.h"
+#include "wav.h"
+#include "speaker-protocol.h"
/** @brief Encoding lookup table type */
struct decoder {
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) {
*/
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
output_header(header->samplerate,
pcm->channels,
- 16);
+ 16,
+ 2 * pcm->channels * pcm->length,
+ ENDIAN_BIG);
switch(pcm->channels) {
case 1:
while(n--)
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;
}
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;
}
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 }
};