summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBent Bisballe Nyeng <deva@aasimon.org>2014-06-24 08:13:32 +0200
committerBent Bisballe Nyeng <deva@aasimon.org>2014-06-24 08:13:32 +0200
commit0c835dce3bf01dc31b33311378043b3799150a64 (patch)
tree65197c9aa5eec4cedf9bbf9156b9165532d257df /src
Import from shared project.
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am30
-rw-r--r--src/airecord.cc146
-rw-r--r--src/audioin.cc443
-rw-r--r--src/audioin.h130
-rw-r--r--src/compat.h20
-rw-r--r--src/test_audio.h112
6 files changed, 881 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..8677324
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,30 @@
+bin_PROGRAMS = airecord
+lib_LTLIBRARIES = libaudioin.la
+
+libaudioin_la_LIBADD = $(ALSA_LIBS)
+libaudioin_la_CXXFLAGS = $(ALSA_CFLAGS)
+
+libaudioin_la_SOURCES = \
+ audioin.cc
+
+airecord_LDADD = libaudioin.la
+airecord_SOURCES = \
+ airecord.cc
+
+include_HEADERS = \
+ audioin.h
+
+EXTRA_DIST = \
+ compat.h \
+ test_audio.h
+
+################
+# Test Section #
+################
+
+TEST_SOURCE_DEPS=${libaudioin_la_SOURCES} ${EXTRA_DIST}
+TEST_SCRIPT_DIR=$(top_srcdir)/tools
+
+include $(top_srcdir)/tools/Makefile.am.test
+
+include Makefile.am.test
diff --git a/src/airecord.cc b/src/airecord.cc
new file mode 100644
index 0000000..8a40edc
--- /dev/null
+++ b/src/airecord.cc
@@ -0,0 +1,146 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/***************************************************************************
+ * audioin.h
+ *
+ * Fri Apr 8 15:37:21 CEST 2011
+ * Copyright 2011 Bent Bisballe Nyeng
+ * deva@aasimon.org
+ ****************************************************************************/
+
+/*
+ * This file is part of libaudioin.
+ *
+ * libaudioin is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * libaudioin is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with libaudioin; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "audioin.h"
+
+int main(int argc, char *argv[])
+{
+ int err = 0;
+
+ if(argc < 4) {
+ printf("Usage %s samplerate num_channels output_file\n", argv[0]);
+ return 1;
+ }
+
+ int samplerate = atoi(argv[1]);
+ int channels = atoi(argv[2]);
+ char *fname = argv[3];
+
+ struct ai_t *ai = ai_init(&err, "default", "Capture", samplerate, channels);
+ if(ai == NULL || err) printf("Error: %d\n", err);
+
+ if(!ai) {
+ printf("Trying alternative mixer interface (not running on an InterCom)\n");
+ //ai = ai_init(&err, "default", "H/W Multi", samplerate, channels);
+ ai = ai_init(&err, "default", "Capture", samplerate, channels);
+ if(ai == NULL || err) printf("Error: %d\n", err);
+ }
+
+ /** Chip: IDT 92HD83C1C5
+Simple mixer control 'Master',0
+Simple mixer control 'Headphone',0
+Simple mixer control 'Speaker',0
+Simple mixer control 'Front',0
+Simple mixer control 'Front Mic Jack Mode',0
+Simple mixer control 'Line',0
+Simple mixer control 'Line Jack Mode',0
+Simple mixer control 'Line',1
+Simple mixer control 'Mic',0
+Simple mixer control 'IEC958',0
+Simple mixer control 'IEC958 Default PCM',0
+Simple mixer control 'Capture',0
+Simple mixer control 'Capture',1
+Simple mixer control 'Input Source',0
+Simple mixer control 'Input Source',1
+Simple mixer control 'Internal Mic',0
+ **/
+
+ /** Chip: Realtek ALC662 rev1
+Simple mixer control 'Master',0
+Simple mixer control 'Headphone',0
+Simple mixer control 'Front',0
+Simple mixer control 'Front Mic',0
+Simple mixer control 'Front Mic Boost',0
+Simple mixer control 'Surround',0
+Simple mixer control 'Center',0
+Simple mixer control 'LFE',0
+Simple mixer control 'Line',0
+Simple mixer control 'IEC958',0
+Simple mixer control 'IEC958 Default PCM',0
+Simple mixer control 'Capture',0
+Simple mixer control 'Capture',1
+Simple mixer control 'Auto-Mute Mode',0
+Simple mixer control 'Input Source',0
+Simple mixer control 'Input Source',1
+Simple mixer control 'Rear Mic',0
+Simple mixer control 'Rear Mic Boost',0
+ **/
+ // 'Capture' record enabled, Input Source: 'Line'
+
+
+
+
+ ai_set_mixer_level(&err, ai, 0, 1.0);
+ ai_set_mixer_level(&err, ai, 1, 1.0);
+
+ short pcm[4096 * 10];
+ FILE *fp = fopen(fname, "w");
+ if(!fp) {
+ printf("Could not write %s\n", fname);
+ return 1;
+ }
+
+ for(size_t i = 0; i < 500; i++) {
+ memset(pcm, 0, sizeof(pcm));
+ int r = ai_read(&err, ai, pcm, sizeof(pcm));
+ if(r < 0 || err) printf("Error: %d\n", err);
+
+ // for(int j = 0; j < 20; j++) pcm[j] = 0;
+
+ int bufsz = r / (sizeof(short) * channels) ;
+ int offset = 160 / (1024 / bufsz);
+ int len = 16;
+ short start[] = {
+ pcm[ (bufsz - offset - 2) * 2],
+ pcm[ (bufsz - offset - 2) * 2 + 1]
+ };
+ short stop[] = {
+ pcm[ (bufsz - offset + len + 1) * 2],
+ pcm[ (bufsz - offset + len + 1) * 2 + 1]
+ };
+ for(int j = 0; j < len; j++) {
+ float d = (float)j / (float)len;
+ pcm[(bufsz - offset + j - 1) * 2] = start[0] * (1 - d) + stop[0] * d;
+ pcm[(bufsz - offset + j - 1) * 2 + 1] = start[1] * (1 - d) + stop[1] * d;
+ }
+ /*
+ short m = 32000;
+ pcm[(bufsz - offset) * 2] = m;
+ pcm[(bufsz - offset) * 2 + 1] = m;
+ pcm[(bufsz - offset + len) * 2] = m;
+ pcm[(bufsz - offset + len) * 2 + 1] = m;
+ */
+ if(fwrite(pcm, r, 1, fp)) {}
+ }
+ fclose(fp);
+ ai_close(&err, ai);
+
+ return 0;
+}
diff --git a/src/audioin.cc b/src/audioin.cc
new file mode 100644
index 0000000..8ed8eac
--- /dev/null
+++ b/src/audioin.cc
@@ -0,0 +1,443 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/***************************************************************************
+ * audioin.cc
+ *
+ * Wed Sep 30 11:36:18 CEST 2009
+ * Copyright 2011 Bent Bisballe Nyeng
+ * deva@aasimon.org
+ ****************************************************************************/
+
+/*
+ * This file is part of libaudioin.
+ *
+ * libaudioin is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * libaudioin is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with libaudioin; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+#include "audioin.h"
+
+// Use the newer ALSA API
+#define ALSA_PCM_NEW_HW_PARAMS_API
+
+#include <asoundlib.h>
+#include <exception>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string>
+
+class AudioIn {
+public:
+ class CouldNotOpenPCMDevice : public std::exception {};
+ class UnableToSetHWParams : public std::exception {};
+ class PcmBufferTooSmall : public std::exception {};
+ class OverRun : public std::exception {};
+ class ReadError : public std::exception {};
+ class ShortRead : public std::exception {};
+ class CouldNotInitialiseParams : public std::exception {};
+ class CouldNotSetAccessMode : public std::exception {};
+ class CouldNotSetFormat : public std::exception {};
+ class CouldNotSetChannelNumber : public std::exception {};
+ class UnableToSetSampleRate : public std::exception {};
+ class UnableToSetPeriodSize : public std::exception {};
+ class MixerInitilisationFailed : public std::exception {};
+ class MixerNotInitialised : public std::exception {};
+ class InvalidMixerLevel : public std::exception {};
+ class IllegalChannelNumber : public std::exception {};
+ class CouldNotSetMixerLevel : public std::exception {};
+
+ AudioIn(std::string device, std::string mixer, unsigned int srate, int ch);
+ ~AudioIn();
+
+ /**
+ * Reads a number of samples from the soundcard.
+ * The returned signal is a 16bit mono pcm signal.
+ */
+ size_t read(void *buf, size_t size);
+
+ int set_level(unsigned int channel, float level);
+
+ unsigned int get_samplerate();
+
+ void set_enable_noise_fix(bool fixit);
+
+private:
+ bool noisefix;
+ unsigned int samplerate;
+ int channels;
+ snd_pcm_t *handle;
+ snd_pcm_hw_params_t *params;
+ unsigned int val;
+ int dir;
+ snd_pcm_uframes_t frames;
+
+ snd_mixer_t *mixhnd;
+ snd_mixer_elem_t *elem;
+ long lvl_min, lvl_max;
+};
+
+AudioIn::AudioIn(std::string device, std::string mixer_interface,
+ unsigned int srate, int ch)
+{
+ int open_mode = 0;
+ //open_mode |= SND_PCM_NONBLOCK;
+ //open_mode |= SND_PCM_NO_AUTO_RESAMPLE;
+ //open_mode |= SND_PCM_NO_AUTO_CHANNELS;
+ //open_mode |= SND_PCM_NO_AUTO_FORMAT;
+ //open_mode |= SND_PCM_NO_SOFTVOL;
+ elem = NULL;
+ mixhnd = NULL;
+
+ noisefix = false;
+
+ samplerate = 0;
+
+ int rc;
+
+ // Open PCM device for recording (capture).
+ rc = snd_pcm_open(&handle, device.c_str(), SND_PCM_STREAM_CAPTURE, open_mode);
+ if (rc < 0) throw CouldNotOpenPCMDevice();
+
+ // Allocate a hardware parameters object.
+ snd_pcm_hw_params_alloca(&params);
+
+ // Fill it in with default values.
+ rc = snd_pcm_hw_params_any(handle, params);
+ if (rc < 0) throw CouldNotInitialiseParams();
+
+ // Set the desired hardware parameters.
+
+ // Interleaved mode
+ rc = snd_pcm_hw_params_set_access(handle, params,
+ SND_PCM_ACCESS_RW_INTERLEAVED);
+ if (rc < 0) throw CouldNotSetAccessMode();
+
+ // Signed 16-bit little-endian format
+ rc = snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE);
+ if (rc < 0) throw CouldNotSetFormat();
+
+ // Set channels (stereo/mono)
+ rc = snd_pcm_hw_params_set_channels(handle, params, ch);
+ channels = ch;
+ if (rc < 0) throw CouldNotSetChannelNumber();
+
+ // Set sampling rate
+ samplerate = srate;
+ rc = snd_pcm_hw_params_set_rate_near(handle, params, &samplerate, &dir);
+ if(rc < 0) throw UnableToSetSampleRate();
+ //if(samplerate != srate) throw UnableToSetSampleRate();
+
+ // NOTE: Setting period size to 32 frames will force use of lowest possible value.
+ frames = 512;
+ rc = snd_pcm_hw_params_set_period_size_near(handle, params, &frames, &dir);
+ if(rc < 0) throw UnableToSetPeriodSize();
+
+ // Write the parameters to the driver
+ rc = snd_pcm_hw_params(handle, params);
+ if (rc < 0) throw UnableToSetHWParams();
+
+ //
+ // Set up mixer
+ //
+ snd_mixer_selem_id_t *mixer;
+
+ // Open mixer device
+ if(snd_mixer_open(&mixhnd, 0) != 0) throw MixerInitilisationFailed();
+
+ if(snd_mixer_attach(mixhnd, device.c_str()) != 0)
+ throw MixerInitilisationFailed();
+
+ if(snd_mixer_selem_register(mixhnd, NULL, NULL) < 0)
+ throw MixerInitilisationFailed();
+
+ if(snd_mixer_load(mixhnd) < 0)
+ throw MixerInitilisationFailed();
+
+ // Obtain mixer element
+ snd_mixer_selem_id_alloca(&mixer);
+ if(mixer == NULL) throw MixerInitilisationFailed();
+ snd_mixer_selem_id_set_name(mixer, mixer_interface.c_str());
+
+ elem = snd_mixer_find_selem(mixhnd, mixer);
+ if(elem == NULL) throw MixerInitilisationFailed();
+
+ if(snd_mixer_selem_get_capture_volume_range(elem, &lvl_min, &lvl_max) != 0) {
+ throw MixerInitilisationFailed();
+ }
+
+ if(snd_mixer_selem_set_capture_switch(elem,
+ SND_MIXER_SCHN_FRONT_LEFT, 1) != 0) {
+ throw MixerInitilisationFailed();
+ }
+
+ if(snd_mixer_selem_set_capture_switch(elem,
+ SND_MIXER_SCHN_FRONT_RIGHT, 1) != 0) {
+ throw MixerInitilisationFailed();
+ }
+
+ // Obtain mixer enumeration element "Input Source" and set it to 2 (line).
+ snd_mixer_selem_id_set_name(mixer, "Input Source");
+
+ snd_mixer_elem_t *iselem = snd_mixer_find_selem(mixhnd, mixer);
+ if(iselem == NULL) return;
+
+ if(snd_mixer_selem_is_enumerated(iselem)) {
+ // Set to line-in
+ for(int i = 0; i < 3; i++) {
+ char name[16];
+ snd_mixer_selem_get_enum_item_name(iselem, i, sizeof(name), name);
+ if(std::string(name) == "Line") {
+ snd_mixer_selem_set_enum_item(iselem, SND_MIXER_SCHN_MONO, i);
+ }
+ }
+ }
+}
+
+AudioIn::~AudioIn()
+{
+ if(handle) {
+ snd_pcm_drain(handle);
+ snd_pcm_close(handle);
+ }
+ if(mixhnd) snd_mixer_close(mixhnd);
+}
+
+size_t AudioIn::read(void *buf, size_t size)
+{
+ int rc;
+
+ if(size < frames * sizeof(short) * channels) {
+ throw PcmBufferTooSmall();
+ }
+
+ rc = snd_pcm_readi(handle, buf, frames);
+ if (rc == -EPIPE) {
+ // EPIPE means overrun
+ snd_pcm_prepare(handle);
+ throw OverRun();
+ return 0;
+ } else if (rc < 0) {
+ throw ReadError();
+ } else if (rc != (int)frames) {
+ throw ShortRead();
+ }
+
+ return rc * sizeof(short) * channels;
+}
+
+unsigned int AudioIn::get_samplerate()
+{
+ return samplerate;
+}
+
+int AudioIn::set_level(unsigned int channel, float level)
+{
+
+ if(!elem) throw MixerNotInitialised();
+
+ if(level > 1.0 || level < 0.0) throw InvalidMixerLevel();
+
+ long lvl = ((lvl_max - lvl_min) * level) + lvl_min;
+
+ snd_mixer_selem_channel_id_t ch;
+ switch(channel) {
+ case 0:
+ ch = SND_MIXER_SCHN_FRONT_LEFT;
+ break;
+ case 1:
+ ch = SND_MIXER_SCHN_FRONT_RIGHT;
+ break;
+ default:
+ throw IllegalChannelNumber();
+ break;
+ }
+ if(snd_mixer_selem_set_capture_volume(elem, ch, lvl) != 0) {
+ throw CouldNotSetMixerLevel();
+ }
+ return 0;
+}
+
+
+#define MAGIC 0xdeadbeef
+
+struct ai_t {
+ unsigned int magic;
+ AudioIn *ai;
+};
+
+struct ai_t *ai_init(int *err, const char *device, const char *mixer,
+ unsigned int srate, unsigned int ch)
+{
+ *err = NO_ERROR;
+
+ struct ai_t *handle = new ai_t;
+ handle->magic = MAGIC;
+
+ if(handle == NULL) {
+ *err = OUT_OF_MEMORY;
+ return NULL;
+ }
+
+ try {
+ handle->ai = new AudioIn(device, mixer, srate, ch);
+ } catch(AudioIn::CouldNotOpenPCMDevice &e) { *err = COULD_NOT_OPEN_DEVICE;
+ } catch(AudioIn::UnableToSetHWParams &e) { *err = COULD_NOT_SET_HW_PARAMS;
+ } catch(AudioIn::CouldNotInitialiseParams &e) { *err = COULD_NOT_INIT_PARAMS;
+ } catch(AudioIn::CouldNotSetAccessMode &e) { *err = COULD_NOT_SET_ACCESS_MODE;
+ } catch(AudioIn::CouldNotSetFormat &e) { *err = COULD_NOT_SET_FORMAT;
+ } catch(AudioIn::CouldNotSetChannelNumber &e) { *err = COULD_NOT_SET_CHANNELS;
+ } catch(AudioIn::UnableToSetSampleRate &e) { *err = COULD_NOT_SET_SAMPLE_RATE;
+ } catch(AudioIn::UnableToSetPeriodSize &e) { *err = COULD_NOT_SET_PERIOD_SIZE;
+ } catch(AudioIn::MixerInitilisationFailed &e) { *err = MIXER_INIT_FAILED;
+ }
+
+ if(*err != NO_ERROR) {
+ handle->magic = 0;
+ delete handle;
+ return NULL;
+ }
+
+ return handle;
+}
+
+int ai_read(int *err, struct ai_t *handle, void *pcm, unsigned int maxsize)
+{
+ if(*err == 42) { // Magic debug function
+ short *p = (short*)pcm;
+ for(size_t i = 0; i < maxsize / sizeof(short); i++) {
+ p[i] = i;
+ }
+ *err = NO_ERROR;
+ return maxsize;
+ }
+
+ *err = NO_ERROR;
+
+ if(handle == NULL || handle->magic != MAGIC || handle->ai == NULL) {
+ *err = MISSING_HANDLE;
+ return -1;
+ }
+
+ try {
+ return handle->ai->read(pcm, maxsize);
+ } catch(AudioIn::PcmBufferTooSmall &e) { *err = BUFFER_TOO_SMALL;
+ } catch(AudioIn::OverRun &e) { *err = BUFFER_OVERRUN;
+ } catch(AudioIn::ReadError &e) { *err = READ_ERROR;
+ } catch(AudioIn::ShortRead &e) { *err = SHORT_READ;
+ }
+
+ return -1;
+}
+
+int ai_get_samplerate(int *err, struct ai_t *handle)
+{
+ *err = NO_ERROR;
+
+ if(handle == NULL || handle->magic != MAGIC || handle->ai == NULL) {
+ *err = MISSING_HANDLE;
+ return -1;
+ }
+
+ return (int)handle->ai->get_samplerate();
+}
+
+
+int ai_set_mixer_level(int *err, struct ai_t *handle, unsigned int channel,
+ float level)
+{
+ *err = NO_ERROR;
+
+ if(handle == NULL || handle->magic != MAGIC || handle->ai == NULL) {
+ *err = MISSING_HANDLE;
+ return -1;
+ }
+
+ try {
+ return handle->ai->set_level(channel, level);
+ } catch(AudioIn::MixerNotInitialised &e) { *err = MIXER_NOT_INITIALISED;
+ } catch(AudioIn::InvalidMixerLevel &e) { *err = INVALID_MIXER_LEVEL;
+ } catch(AudioIn::IllegalChannelNumber &e) { *err = INVALID_CHANNEL_NUMBER;
+ } catch(AudioIn::CouldNotSetMixerLevel &e) { *err = COULD_NOT_SET_MIXER_LEVEL;
+ }
+
+ return -1;
+}
+
+void ai_close(int *err, struct ai_t *handle)
+{
+ *err = NO_ERROR;
+
+ if(handle == NULL || handle->magic != MAGIC || handle->ai == NULL) {
+ *err = MISSING_HANDLE;
+ return;
+ }
+
+ delete handle->ai;
+ handle->ai = NULL;
+ handle->magic = 0;
+ delete handle;
+}
+
+#ifdef TEST_AUDIOIN
+//deps:
+//cflags: $(ALSA_CFLAGS)
+//libs: $(ALSA_LIBS)
+
+#include <test.h>
+#include "test_audio.h"
+#include <string.h>
+
+TEST_BEGIN;
+int err;
+struct ai_t *h;
+
+h = ai_init(&err, "default", "H/W Multi", 44100, 2);
+TEST_EQUAL_INT(err, 0, "Check for errors.");
+TEST_NOTEQUAL(h, NULL, "Check handle.");
+
+ai_close(&err, h);
+TEST_EQUAL_INT(err, 0, "Check for errors.");
+
+h = ai_init(&err, "default", "H/W Multi", 22050, 1);
+TEST_EQUAL_INT(err, 0, "Check for errors.");
+TEST_NOTEQUAL(h, NULL, "Check handle.");
+
+char pcm[1880]; // Hold exactly one frame.
+int r;
+
+r = ai_read(&err, h, pcm, 0);
+TEST_EQUAL_INT(r, -1, "We should read something.");
+TEST_EQUAL_INT(err, BUFFER_TOO_SMALL, "Check for errors.");
+
+for(int i = 0; i < 100; i++) {
+ r = ai_read(&err, h, pcm, sizeof(pcm));
+}
+TEST_EQUAL_INT(err, 0, "Check for errors.");
+TEST_NOTEQUAL_INT(r, 0, "We should read something.");
+
+char ref[sizeof(pcm)];
+memset(ref, 0, sizeof(ref));
+double diff = compareBuffers(r/sizeof(short), 1, pcm, ref);
+TEST_LESS_THAN_FLOAT(diff, 1000.0, "Compare buffers (with no mic on the soundcard)");
+
+short p[1024];
+err = 42;
+ai_read(&err, h, p, sizeof(p));
+for(size_t i = 0; i < sizeof(p) / sizeof(short); i++) {
+ TEST_EQUAL_INT(p[i], i, "Compare slide.");
+}
+
+ai_close(&err, h);
+
+TEST_END;
+
+#endif/*TEST_AUDIOIN*/
diff --git a/src/audioin.h b/src/audioin.h
new file mode 100644
index 0000000..fe12fa4
--- /dev/null
+++ b/src/audioin.h
@@ -0,0 +1,130 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/***************************************************************************
+ * audioin.h
+ *
+ * Wed Sep 30 11:36:18 CEST 2009
+ * Copyright 2011 Bent Bisballe Nyeng
+ * deva@aasimon.org
+ ****************************************************************************/
+
+/*
+ * This file is part of libaudioin.
+ *
+ * libaudioin is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * libaudioin is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with libaudioin; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+#ifndef __LIBAUDIOIN_AUDIOIN_H__
+#define __LIBAUDIOIN_AUDIOIN_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define NO_ERROR 0
+
+#define OUT_OF_MEMORY 101
+#define MISSING_HANDLE 102
+
+#define COULD_NOT_OPEN_DEVICE 203
+#define COULD_NOT_SET_HW_PARAMS 204
+#define COULD_NOT_INIT_PARAMS 205
+#define COULD_NOT_SET_ACCESS_MODE 206
+#define COULD_NOT_SET_FORMAT 207
+#define COULD_NOT_SET_CHANNELS 208
+#define COULD_NOT_SET_SAMPLE_RATE 209
+#define COULD_NOT_SET_PERIOD_SIZE 210
+
+#define BUFFER_TOO_SMALL 305
+#define BUFFER_OVERRUN 306
+#define READ_ERROR 307
+#define SHORT_READ 308
+
+#define MIXER_INIT_FAILED 400
+
+#define MIXER_NOT_INITIALISED 401
+#define INVALID_MIXER_LEVEL 402
+#define INVALID_CHANNEL_NUMBER 403
+#define COULD_NOT_SET_MIXER_LEVEL 404
+
+struct ai_t;
+
+/**
+ * Initalise the AudioIn library and connect to a soundcard and a mixer.
+ * @param err An int pointer containing error value if function is unsuccessful.
+ * @param device A string containing the device name to connect to. A list
+ * possible devices can be seen with the 'aplay -L' command. The device
+ * "default" are usually the one needed.
+ * @param mixer A string containing the mixer interface to connect to. A list
+ * of possible interfaces can be seen with the 'amixer scontrols' command.
+ * Usually the interface "Capture" is the one needed.
+ * @param samplerate An unsigned integer containing the desired samplerate in
+ * Hertz.
+ * @param channels An unsigned integer containing the desired number of
+ * channels. The channel samples will be interleaved in the data stream whe
+ * ai_read is called.
+ * @return A pointer to the newly created handle or NULL on failure.
+ */
+struct ai_t *ai_init(int *err, const char *device, const char *mixer,
+ unsigned int samplerate, unsigned int channels);
+
+/**
+ * Read samples from the soundcard.
+ * @param err An int pointer containing error value if function is unsuccessful.
+ * @param handle A pointer to the handle to be used.
+ * @param maxsize The maximum number of bytes (not samples) to read.
+ * @return Returns the number of bytes (not samples) read or -1 on error.
+ * NOTE: to get the number of samples read, devide the return value with the
+ * sample width (2 bytes / 16 bits) and the number of interleaved channels
+ * (usually 1 or 2).
+ */
+int ai_read(int *err, struct ai_t *handle, void *pcm, unsigned int maxsize);
+
+/**
+ * Adjust channel mixer levels.
+ * @param err An int pointer containing error value if function is unsuccessful.
+ * @param handle A pointer to the handle to be used.
+ * @param channel The channel number to set mixer level of. 0 or 1 should be
+ * used for the two channels of a stereo interface.
+ * @param level The mixer level to set, ranging from 0.0 to 1.0 where 0.0 is
+ * mute and 1.0 is maximum gain.
+ * @return Returns -1 on error 0 otherwise.
+ */
+int ai_set_mixer_level(int *err, struct ai_t *handle, unsigned int channel,
+ float level);
+
+/**
+ * Get actual samplerate.
+ * The samplerate set in ai_init may or may not match a possible samplerate for
+ * the audio hardware and therefore the ALSA library might decide to use another
+ * samplerate than the one specified.
+ * This function can be called to obtain the actual samplerate that the hardware
+ * has been configured to use.
+ * @param err An int pointer containing error value if function is unsuccessful.
+ * @param handle A pointer to the handle to be used.
+ * @return The samplerate as an integer or -1 on error.
+ */
+int ai_get_samplerate(int *err, struct ai_t *handle);
+
+/**
+ * Close and free the handle.
+ * @param err An int pointer containing error value if function is unsuccessful.
+ * @param handle A pointer to the handle to be closed.
+ */
+void ai_close(int *err, struct ai_t *handle);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif/*__LIBAUDIOIN_AUDIOIN_H__*/
diff --git a/src/compat.h b/src/compat.h
new file mode 100644
index 0000000..868a729
--- /dev/null
+++ b/src/compat.h
@@ -0,0 +1,20 @@
+/* -*- mode: c++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/***************************************************************************
+ * File: compat.h
+ * This file belongs to the Bifrost project.
+ * Macros for throw compatability.
+ * Date: Tue Feb 16 14:14:30 CET 2010
+ * Author: Bent Bisballe Nyeng
+ * Copyright: 2010
+ * Email: deva@aasimon.org
+ ****************************************************************************/
+#ifndef __LIBAUDIOIN_COMPAT_H__
+#define __LIBAUDIOIN_COMPAT_H__
+
+#ifdef WIN32
+#define _throw(...)
+#else
+#define _throw(fmt...) throw(fmt)
+#endif
+
+#endif/*__LIBAUDIOIN_COMPAT_H__*/
diff --git a/src/test_audio.h b/src/test_audio.h
new file mode 100644
index 0000000..68dac67
--- /dev/null
+++ b/src/test_audio.h
@@ -0,0 +1,112 @@
+/* -*- mode: c++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/***************************************************************************
+ * File: test_audio.h
+ * This file belongs to the Bifrost project.
+ * [FILL IN DESCRIPTION HERE]
+ * Date: Wed Feb 3 08:04:33 CET 2010
+ * Author: Bent Bisballe Nyeng
+ * Copyright: 2010
+ * Email: deva@aasimon.org
+ ****************************************************************************/
+#ifndef __BIFROST_TEST_AUDIO_H__
+#define __BIFROST_TEST_AUDIO_H__
+
+#include <math.h>
+#include <time.h>
+#include <stdlib.h>
+
+#ifdef VERBOSE
+#include <stdio.h>
+#endif
+
+static inline size_t getBufferSize(size_t samples, size_t channels)
+{
+ return samples * channels * sizeof(short);
+}
+
+static inline char *getBuffer(size_t samples, size_t channels)
+{
+ return (char*)calloc(getBufferSize(samples, channels), 1);
+}
+
+static inline char *getSineBuffer(size_t samples, size_t channels,
+ size_t srate, size_t freq)
+{
+ short *s = (short*)getBuffer(samples, channels);
+ for(size_t i = 0; i < samples; i++) {
+ double x = (double)i / (double)srate;
+ double val = sin(x * 2 * M_PI * freq);
+
+ for(size_t c = 0; c < channels; c++) {
+ s[(i * channels) + c] = val * 32500;
+ }
+
+ }
+ return (char*)s;
+}
+
+static inline char *getNoiseBuffer(size_t samples, size_t channels)
+{
+ srand(time(NULL));
+ short *s = (short*)getBuffer(samples, channels);
+ for(size_t i = 0; i < samples; i++) {
+ for(size_t c = 0; c < channels; c++) {
+ s[(i * channels) + c] = (rand() % 65000) - 32500;
+ }
+ }
+ return (char*)s;
+}
+
+static inline void normaliseBuffer(size_t samples, size_t channels, char *buf)
+{
+ int max = 0;
+
+ short *s = (short*)buf;
+ for(size_t i = 0; i < samples; i++) {
+ for(size_t c = 0; c < channels; c++) {
+ if(max < abs(s[(i * channels) + c])) max = abs(s[(i * channels) + c]);
+ }
+ }
+
+ for(size_t i = 0; i < samples; i++) {
+ for(size_t c = 0; c < channels; c++) {
+ s[(i * channels) + c] *= 32500/max;
+ }
+ }
+
+}
+
+static inline double compareBuffers(size_t samples, size_t channels, char *buf1, char *buf2)
+{
+ double diff = 0.0;
+
+#ifdef NORM
+ normaliseBuffer(samples, channels, buf1);
+ normaliseBuffer(samples, channels, buf2);
+#endif
+
+ short *s1 = (short*)buf1;
+ short *s2 = (short*)buf2;
+
+ for(size_t i = 0; i < samples; i++) {
+ for(size_t c = 0; c < channels; c++) {
+ diff += (double)abs(s1[(i * channels) + c] - s2[(i * channels) + c]) / (double)(samples*channels);
+#ifdef VERBOSE
+ if(c == 0) fprintf(stderr, "%d\t%d\t~%d\t: %f\n",
+ s1[(i * channels) + c], s2[(i * channels) + c],
+ s1[(i * channels) + c] - s2[(i * channels) + c],
+ diff);
+#endif
+
+ }
+ }
+
+#ifdef VERBOSE
+ fprintf(stderr, "Diff: %f\n", diff);
+#endif
+
+ return diff;
+}
+
+
+#endif/*__BIFROST_TEST_AUDIO_H__*/