From f5f4ebba56df60cc4e6fa8b3a2c79c87e01f0a7d Mon Sep 17 00:00:00 2001 From: Joshua Phillips Date: Sun, 28 Dec 2008 19:39:30 +0000 Subject: [PATCH] Initial commit. Plays a wave file. --- .gitignore | 3 ++ SConscript | 13 ++++++++ SConstruct | 31 +++++++++++++++++++ aformat.c | 7 +++++ aformat.h | 12 ++++++++ main.c | 38 +++++++++++++++++++++++ sound-ioctl.c | 45 +++++++++++++++++++++++++++ sound-ioctl.h | 10 ++++++ soundout.c | 32 +++++++++++++++++++ soundout.h | 14 +++++++++ wavefile.c | 99 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ wavefile.h | 20 ++++++++++++ 12 files changed, 324 insertions(+) create mode 100644 .gitignore create mode 100644 SConscript create mode 100644 SConstruct create mode 100644 aformat.c create mode 100644 aformat.h create mode 100644 main.c create mode 100644 sound-ioctl.c create mode 100644 sound-ioctl.h create mode 100644 soundout.c create mode 100644 soundout.h create mode 100644 wavefile.c create mode 100644 wavefile.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f06a2fa --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +build/ +scache.conf +.sconsign.dblite diff --git a/SConscript b/SConscript new file mode 100644 index 0000000..ff98143 --- /dev/null +++ b/SConscript @@ -0,0 +1,13 @@ +import os.path +Import(['env']) + +programs = { + 'main': ['main.c', 'wavefile.c', 'soundout.c', 'aformat.c', 'sound-ioctl.c'], +} + +objects = {} +for program in programs: + for source in programs[program]: + if source not in objects: + objects[source] = env.Object(source) + env.Program(program, [objects[i] for i in programs[program]]) diff --git a/SConstruct b/SConstruct new file mode 100644 index 0000000..05d4a84 --- /dev/null +++ b/SConstruct @@ -0,0 +1,31 @@ +# Build options +opts = Options('scache.conf') +opts.AddOptions( + PathOption('PREFIX', 'Set the install "prefix"', '/usr/local'), + PathOption('DESTDIR', 'Set the installation root', '/'), + ('BUILDDIR', 'Set the sub-directory to put object files in', 'build'), + ('INCLUDE_DIRS', 'Set the default include directories to build into the compiler', '/include:/usr/include:/usr/local/include:/opt/include'), + ('CC', 'Set the C compiler to use'), + ('LINK', 'Set the linker to use'), + ('CFLAGS', 'Change the flags for the compiler', '-Wall -g -ffunction-sections'), + ('LINKFLAGS', 'Change the flags for the linker', '-Wl,--gc-sections'), + BoolOption('verbose', 'Show full commands during the build process', False), +) + +env = Environment(options = opts) +Help(opts.GenerateHelpText(env)) +opts.Save('scache.conf', env) + +env['LIBS'] = ['m', 'asound'] + +# Pretty coloured output +if not env['verbose']: + env['CCCOMSTR'] = ' Compiling $TARGET' + env['LINKCOMSTR'] = ' Linking $TARGET' + env['ARCOMSTR'] = ' Archiving $TARGET' + env['RANLIBCOMSTR'] = ' Indexing $TARGET' + +SConscript('SConscript', + exports = ['env'], + build_dir = env['BUILDDIR'], + duplicate = 0) diff --git a/aformat.c b/aformat.c new file mode 100644 index 0000000..a5d642b --- /dev/null +++ b/aformat.c @@ -0,0 +1,7 @@ +#include "aformat.h" + +int aformat_get_sample_size(struct aformat *af) +{ + return (af->bits / 8) * af->channels; +} + diff --git a/aformat.h b/aformat.h new file mode 100644 index 0000000..31d759e --- /dev/null +++ b/aformat.h @@ -0,0 +1,12 @@ +#ifndef AFORMAT_H +#define AFORMAT_H + +struct aformat { + int srate; // sample rate + int bits; // number of bits per sample + int channels; // number of channels +}; + +int aformat_get_sample_size(struct aformat *af); + +#endif diff --git a/main.c b/main.c new file mode 100644 index 0000000..8f9045b --- /dev/null +++ b/main.c @@ -0,0 +1,38 @@ +#include "stdio.h" +#include "soundout.h" +#include "wavefile.h" + +int main(int argc, char **argv) +{ + struct soundout so; + struct wavefile wf; + struct aformat af; + + if (wavefile_open(&wf, "/home/aoe/reflections.wav") != 0){ + fprintf(stderr, "cannot open wave file\n"); + return 1; + } + + af.srate = 44100; + af.bits = 16; + af.channels = 2; + + if (soundout_open(&so, &af) != 0){ + fprintf(stderr, "cannot open audio output device\n"); + // wavefile_close + return 1; + } + + unsigned int pos = 0; + char buf[4410 * 4]; + for (;;){ + if (wavefile_read_at(&wf, pos, buf, 4410) != 0){ + // end of file + break; + } + soundout_write(&so, buf, 4410 * 4); + pos += 4410; + } + + return 0; +} diff --git a/sound-ioctl.c b/sound-ioctl.c new file mode 100644 index 0000000..fad10f7 --- /dev/null +++ b/sound-ioctl.c @@ -0,0 +1,45 @@ +/* Easier writing these bits in C rather + than defining the ioctls in D */ + +#include "fcntl.h" +#include "linux/soundcard.h" + +int sound_set_format(int fd, int format) +{ + int tmp; + tmp = format; + if (ioctl(fd, SNDCTL_DSP_SETFMT, &tmp) == -1 + || tmp != format) + { + return -1; + } else { + return 0; + } +} + +int sound_set_srate(int fd, int srate) +{ + int tmp; + tmp = srate; + if (ioctl(fd, SNDCTL_DSP_SPEED, &tmp) == -1 + || tmp != srate) + { + return -1; + } else { + return 0; + } +} + +int sound_set_channels(int fd, int channels) +{ + int tmp; + tmp = channels; + if (ioctl(fd, SNDCTL_DSP_CHANNELS, &tmp) == -1 + || tmp != channels) + { + return -1; + } else { + return 0; + } +} + diff --git a/sound-ioctl.h b/sound-ioctl.h new file mode 100644 index 0000000..b00aca9 --- /dev/null +++ b/sound-ioctl.h @@ -0,0 +1,10 @@ +#ifndef SOUND_IOCTL_H +#define SOUND_IOCTL_H + +///// Wrappers to OSS ioctl() calls ///// + +int sound_set_format(int fd, int format); +int sound_set_srate(int fd, int srate); +int sound_set_channels(int fd, int channels); + +#endif diff --git a/soundout.c b/soundout.c new file mode 100644 index 0000000..038574a --- /dev/null +++ b/soundout.c @@ -0,0 +1,32 @@ +#include "sound-ioctl.h" +#include "soundout.h" +#include "unistd.h" +#include "fcntl.h" + +int soundout_open(struct soundout *so, struct aformat *af) +{ + so->fd = open("/dev/dsp", O_WRONLY); + if (so->fd < 0){ + return 1; + } + + if (sound_set_format(so->fd, af->bits)){ + return 1; + } + + if (sound_set_srate(so->fd, af->srate)){ + return 1; + } + + if (sound_set_channels(so->fd, af->channels)){ + return 1; + } + + return 0; +} + +int soundout_write(struct soundout *so, void *buf, size_t len) +{ + write(so->fd, buf, len); + return 0; +} diff --git a/soundout.h b/soundout.h new file mode 100644 index 0000000..40a8a25 --- /dev/null +++ b/soundout.h @@ -0,0 +1,14 @@ +#ifndef SOUNDOUT_H +#define SOUNDOUT_H + +#include "aformat.h" +#include "stddef.h" + +struct soundout { + int fd; +}; + +int soundout_open(struct soundout *so, struct aformat *af); +int soundout_write(struct soundout *so, void *buf, size_t len); + +#endif diff --git a/wavefile.c b/wavefile.c new file mode 100644 index 0000000..82cbb1a --- /dev/null +++ b/wavefile.c @@ -0,0 +1,99 @@ +#include "wavefile.h" +#include "stdint.h" +#include "assert.h" +#include "string.h" + +struct wavehdr +{ + // http://mathmatrix.narod.ru/Wavefmt.html + char magic[4]; // "RIFF" + uint32_t size; // size of file + char wave_magic[4]; // "WAVE" + char fmt_magic[4]; // "fmt " + uint32_t wave_section_chunk_size; + uint16_t wave_fmt_type; // 1 = linear quantization + uint16_t n_channels; + uint32_t srate; + uint32_t bytes_per_second; + uint16_t block_align; + uint16_t bits_per_sample; +}; + +struct chunkhdr +{ + char name[4]; // name of chunk + uint32_t size; // size (bytes) of chunk (excluding header) +}; + +static int read_header(struct wavefile *wav) +{ + struct wavehdr whdr; + struct chunkhdr chdr; + + // read wave header + if (fread(&whdr, sizeof whdr, 1, wav->f) < 1){ + return 1; + } + + // check wave magic strings + if (strncmp(whdr.magic, "RIFF", 4) + || strncmp(whdr.wave_magic, "WAVE", 4) + || strncmp(whdr.fmt_magic, "fmt ", 4)){ + // invalid file format + return 1; + } + + // make sure it's in PCM format + if (whdr.wave_fmt_type != 1){ + return 1; + } + + // store format information + wav->format.srate = whdr.srate; + wav->format.bits = whdr.bits_per_sample; + wav->format.channels = whdr.n_channels; + + // read chunk headers + for (;;){ + if (fread(&chdr, sizeof chdr, 1, wav->f) < 1){ + // read failure + return 1; + } + if (!strncmp(chdr.name, "data", 4)){ + // found the data chunk + break; + } else { + // this is not the data. + // Next chunk! + fseek(wav->f, chdr.size, SEEK_CUR); + } + } + + wav->length = chdr.size / ((wav->format.bits / 8) * wav->format.channels); + wav->pcm_start = ftell(wav->f); + return 0; +} + +int wavefile_open(struct wavefile *wav, const char *filename) +{ + // open the wave file + wav->f = fopen(filename, "r"); + if (!wav->f){ + return 1; + } + + // read wave file header + return read_header(wav); +} + +int wavefile_read_at(struct wavefile *wav, off_t sample_start, void *buf, size_t n_samples) +{ + if (sample_start + n_samples > wav->length){ + // past the end of file + return 1; + } + + fseek(wav->f, wav->pcm_start + sample_start * aformat_get_sample_size(&wav->format), SEEK_SET); + fread(buf, 1, n_samples * aformat_get_sample_size(&wav->format), wav->f); + return 0; +} diff --git a/wavefile.h b/wavefile.h new file mode 100644 index 0000000..84d0ca5 --- /dev/null +++ b/wavefile.h @@ -0,0 +1,20 @@ +#ifndef WAVEFILE_H +#define WAVEFILE_H + +#include "aformat.h" +#include "stdio.h" +#include "sys/types.h" + +struct wavefile +{ + char *filename; + FILE *f; + struct aformat format; + long length; // length, in samples + off_t pcm_start; // file offset to start of actual audio data +}; + +int wavefile_open(struct wavefile *wav, const char *filename); +int wavefile_read_at(struct wavefile *wav, off_t sample_start, void *buf, size_t n_samples); + +#endif -- 2.11.4.GIT