# HG changeset patch
# User andrewm
# Date 1414779017 -3600
# Node ID 8a575ba3ab52654233aeb7c7c1613f34227f1bcd
Initial commit.
diff -r 000000000000 -r 8a575ba3ab52 .cproject
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/.cproject Fri Oct 31 19:10:17 2014 +0100
@@ -0,0 +1,196 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff -r 000000000000 -r 8a575ba3ab52 .metadata/.plugins/org.eclipse.cdt.core/.log
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/.metadata/.plugins/org.eclipse.cdt.core/.log Fri Oct 31 19:10:17 2014 +0100
@@ -0,0 +1,3 @@
+*** SESSION Oct 17, 2013 09:50:56.88 -------------------------------------------
+*** SESSION Oct 17, 2013 09:51:41.97 -------------------------------------------
+*** SESSION Jul 22, 2014 19:36:22.66 -------------------------------------------
diff -r 000000000000 -r 8a575ba3ab52 .metadata/.plugins/org.eclipse.cdt.make.core/specs.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/.metadata/.plugins/org.eclipse.cdt.make.core/specs.c Fri Oct 31 19:10:17 2014 +0100
@@ -0,0 +1,1 @@
+
diff -r 000000000000 -r 8a575ba3ab52 .metadata/.plugins/org.eclipse.cdt.make.core/specs.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/.metadata/.plugins/org.eclipse.cdt.make.core/specs.cpp Fri Oct 31 19:10:17 2014 +0100
@@ -0,0 +1,1 @@
+
diff -r 000000000000 -r 8a575ba3ab52 .metadata/.plugins/org.eclipse.core.resources/.root/.indexes/history.version
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/.metadata/.plugins/org.eclipse.core.resources/.root/.indexes/history.version Fri Oct 31 19:10:17 2014 +0100
@@ -0,0 +1,1 @@
+
\ No newline at end of file
diff -r 000000000000 -r 8a575ba3ab52 .metadata/.plugins/org.eclipse.core.resources/.root/.indexes/properties.index
Binary file .metadata/.plugins/org.eclipse.core.resources/.root/.indexes/properties.index has changed
diff -r 000000000000 -r 8a575ba3ab52 .metadata/.plugins/org.eclipse.core.resources/.root/.indexes/properties.version
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/.metadata/.plugins/org.eclipse.core.resources/.root/.indexes/properties.version Fri Oct 31 19:10:17 2014 +0100
@@ -0,0 +1,1 @@
+
\ No newline at end of file
diff -r 000000000000 -r 8a575ba3ab52 .metadata/.plugins/org.eclipse.core.resources/.root/2.tree
Binary file .metadata/.plugins/org.eclipse.core.resources/.root/2.tree has changed
diff -r 000000000000 -r 8a575ba3ab52 .metadata/.plugins/org.eclipse.core.resources/.safetable/org.eclipse.core.resources
Binary file .metadata/.plugins/org.eclipse.core.resources/.safetable/org.eclipse.core.resources has changed
diff -r 000000000000 -r 8a575ba3ab52 .metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.cdt.ui.prefs
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.cdt.ui.prefs Fri Oct 31 19:10:17 2014 +0100
@@ -0,0 +1,4 @@
+eclipse.preferences.version=1
+spelling_locale_initialized=true
+useAnnotationsPrefPage=true
+useQuickDiffPrefPage=true
diff -r 000000000000 -r 8a575ba3ab52 .metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.core.resources.prefs
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.core.resources.prefs Fri Oct 31 19:10:17 2014 +0100
@@ -0,0 +1,2 @@
+eclipse.preferences.version=1
+version=1
diff -r 000000000000 -r 8a575ba3ab52 .metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.jdt.ui.prefs
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.jdt.ui.prefs Fri Oct 31 19:10:17 2014 +0100
@@ -0,0 +1,14 @@
+content_assist_proposals_background=255,255,255
+content_assist_proposals_foreground=60,60,60
+eclipse.preferences.version=1
+fontPropagated=true
+org.eclipse.jdt.internal.ui.navigator.layout=2
+org.eclipse.jdt.ui.editor.tab.width=
+org.eclipse.jdt.ui.formatterprofiles.version=12
+org.eclipse.jdt.ui.javadoclocations.migrated=true
+org.eclipse.jface.textfont=1|Monospace|10.0|0|GTK|1|;
+proposalOrderMigrated=true
+spelling_locale_initialized=true
+tabWidthPropagated=true
+useAnnotationsPrefPage=true
+useQuickDiffPrefPage=true
diff -r 000000000000 -r 8a575ba3ab52 .metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.ui.ide.prefs
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.ui.ide.prefs Fri Oct 31 19:10:17 2014 +0100
@@ -0,0 +1,6 @@
+EXIT_PROMPT_ON_CLOSE_LAST_WINDOW=false
+TASKS_FILTERS_MIGRATE=true
+eclipse.preferences.version=1
+platformState=1380290440069
+quickStart=false
+tipsAndTricks=true
diff -r 000000000000 -r 8a575ba3ab52 .metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.ui.prefs
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.ui.prefs Fri Oct 31 19:10:17 2014 +0100
@@ -0,0 +1,2 @@
+eclipse.preferences.version=1
+showIntro=false
diff -r 000000000000 -r 8a575ba3ab52 .metadata/.plugins/org.eclipse.jdt.core/invalidArchivesCache
Binary file .metadata/.plugins/org.eclipse.jdt.core/invalidArchivesCache has changed
diff -r 000000000000 -r 8a575ba3ab52 .metadata/.plugins/org.eclipse.jdt.core/nonChainingJarsCache
Binary file .metadata/.plugins/org.eclipse.jdt.core/nonChainingJarsCache has changed
diff -r 000000000000 -r 8a575ba3ab52 .metadata/.plugins/org.eclipse.jdt.core/variablesAndContainers.dat
Binary file .metadata/.plugins/org.eclipse.jdt.core/variablesAndContainers.dat has changed
diff -r 000000000000 -r 8a575ba3ab52 .metadata/.plugins/org.eclipse.jdt.ui/OpenTypeHistory.xml
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/.metadata/.plugins/org.eclipse.jdt.ui/OpenTypeHistory.xml Fri Oct 31 19:10:17 2014 +0100
@@ -0,0 +1,2 @@
+
+
diff -r 000000000000 -r 8a575ba3ab52 .metadata/.plugins/org.eclipse.jdt.ui/QualifiedTypeNameHistory.xml
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/.metadata/.plugins/org.eclipse.jdt.ui/QualifiedTypeNameHistory.xml Fri Oct 31 19:10:17 2014 +0100
@@ -0,0 +1,2 @@
+
+
diff -r 000000000000 -r 8a575ba3ab52 .metadata/.plugins/org.eclipse.ui.ide/dialog_settings.xml
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/.metadata/.plugins/org.eclipse.ui.ide/dialog_settings.xml Fri Oct 31 19:10:17 2014 +0100
@@ -0,0 +1,11 @@
+
+
diff -r 000000000000 -r 8a575ba3ab52 .metadata/.plugins/org.eclipse.ui.workbench/dialog_settings.xml
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/.metadata/.plugins/org.eclipse.ui.workbench/dialog_settings.xml Fri Oct 31 19:10:17 2014 +0100
@@ -0,0 +1,3 @@
+
+
diff -r 000000000000 -r 8a575ba3ab52 .metadata/.plugins/org.eclipse.ui.workbench/workbench.xml
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/.metadata/.plugins/org.eclipse.ui.workbench/workbench.xml Fri Oct 31 19:10:17 2014 +0100
@@ -0,0 +1,149 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff -r 000000000000 -r 8a575ba3ab52 .metadata/.plugins/org.eclipse.ui.workbench/workingsets.xml
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/.metadata/.plugins/org.eclipse.ui.workbench/workingsets.xml Fri Oct 31 19:10:17 2014 +0100
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff -r 000000000000 -r 8a575ba3ab52 .metadata/version.ini
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/.metadata/version.ini Fri Oct 31 19:10:17 2014 +0100
@@ -0,0 +1,1 @@
+org.eclipse.core.runtime=1
\ No newline at end of file
diff -r 000000000000 -r 8a575ba3ab52 .project
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/.project Fri Oct 31 19:10:17 2014 +0100
@@ -0,0 +1,83 @@
+
+
+ BeagleRT
+
+
+
+
+
+ org.eclipse.cdt.managedbuilder.core.genmakebuilder
+ clean,full,incremental,
+
+
+ ?name?
+
+
+
+ org.eclipse.cdt.make.core.append_environment
+ true
+
+
+ org.eclipse.cdt.make.core.autoBuildTarget
+ all
+
+
+ org.eclipse.cdt.make.core.buildArguments
+
+
+
+ org.eclipse.cdt.make.core.buildCommand
+ make
+
+
+ org.eclipse.cdt.make.core.buildLocation
+ ${workspace_loc:/BBB_audio+input/Debug}
+
+
+ org.eclipse.cdt.make.core.cleanBuildTarget
+ clean
+
+
+ org.eclipse.cdt.make.core.contents
+ org.eclipse.cdt.make.core.activeConfigSettings
+
+
+ org.eclipse.cdt.make.core.enableAutoBuild
+ false
+
+
+ org.eclipse.cdt.make.core.enableCleanBuild
+ true
+
+
+ org.eclipse.cdt.make.core.enableFullBuild
+ true
+
+
+ org.eclipse.cdt.make.core.fullBuildTarget
+ all
+
+
+ org.eclipse.cdt.make.core.stopOnError
+ true
+
+
+ org.eclipse.cdt.make.core.useDefaultBuildCmd
+ true
+
+
+
+
+ org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder
+ full,incremental,
+
+
+
+
+
+ org.eclipse.cdt.core.cnature
+ org.eclipse.cdt.core.ccnature
+ org.eclipse.cdt.managedbuilder.core.managedBuildNature
+ org.eclipse.cdt.managedbuilder.core.ScannerConfigNature
+
+
diff -r 000000000000 -r 8a575ba3ab52 .settings/org.eclipse.cdt.core.prefs
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/.settings/org.eclipse.cdt.core.prefs Fri Oct 31 19:10:17 2014 +0100
@@ -0,0 +1,3 @@
+eclipse.preferences.version=1
+environment/project/cdt.managedbuild.config.gnu.exe.debug.528876549/append=true
+environment/project/cdt.managedbuild.config.gnu.exe.debug.528876549/appendContributed=true
diff -r 000000000000 -r 8a575ba3ab52 core/GPIOcontrol.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/core/GPIOcontrol.cpp Fri Oct 31 19:10:17 2014 +0100
@@ -0,0 +1,325 @@
+/*
+ * SimpleGPIO.cpp
+ *
+ * Modifications by Derek Molloy, School of Electronic Engineering, DCU
+ * www.derekmolloy.ie
+ * Almost entirely based on Software by RidgeRun:
+ *
+ * Copyright (c) 2011, RidgeRun
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the RidgeRun.
+ * 4. Neither the name of the RidgeRun nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY RIDGERUN ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL RIDGERUN BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "../include/GPIOcontrol.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+
+/****************************************************************
+ * gpio_setup
+ ****************************************************************/
+int gpio_setup(unsigned int gpio, int out_flag)
+{
+ /* Export the GPIO pins and set their direction */
+ if(gpio_export(gpio)) {
+ printf("Unable to export GPIO input pin\n");
+ return -1;
+ }
+ if(gpio_set_dir(gpio, out_flag)) {
+ printf("Unable to set GPIO input direction\n");
+ return -1;
+ }
+
+ return gpio_fd_open(gpio, O_RDWR);
+}
+
+/****************************************************************
+ * gpio_export
+ ****************************************************************/
+int gpio_export(unsigned int gpio)
+{
+ int fd, len, result = 0;
+ char buf[MAX_BUF];
+
+ fd = open(SYSFS_GPIO_DIR "/export", O_WRONLY);
+ if (fd < 0) {
+ perror("gpio/export");
+ return fd;
+ }
+
+ len = snprintf(buf, sizeof(buf), "%d", gpio);
+ if(write(fd, buf, len) < 0)
+ result = -1;
+ close(fd);
+
+ return result;
+}
+
+/****************************************************************
+ * gpio_unexport
+ ****************************************************************/
+int gpio_unexport(unsigned int gpio)
+{
+ int fd, len, result = 0;
+ char buf[MAX_BUF];
+
+ fd = open(SYSFS_GPIO_DIR "/unexport", O_WRONLY);
+ if (fd < 0) {
+ perror("gpio/export");
+ return fd;
+ }
+
+ len = snprintf(buf, sizeof(buf), "%d", gpio);
+ if(write(fd, buf, len) < 0)
+ result = -1;
+ close(fd);
+ return result;
+}
+
+/****************************************************************
+ * gpio_set_dir
+ ****************************************************************/
+int gpio_set_dir(unsigned int gpio, int out_flag)
+{
+ int fd, result = 0;
+ char buf[MAX_BUF];
+
+ snprintf(buf, sizeof(buf), SYSFS_GPIO_DIR "/gpio%d/direction", gpio);
+
+ fd = open(buf, O_WRONLY);
+ if (fd < 0) {
+ perror("gpio/direction");
+ return fd;
+ }
+
+ if (out_flag == OUTPUT_PIN) {
+ if(write(fd, "out", 4) < 0)
+ result = -1;
+ }
+ else {
+ if(write(fd, "in", 3) < 0)
+ result = -1;
+ }
+
+ close(fd);
+ return result;
+}
+
+/****************************************************************
+ * gpio_set_value
+ ****************************************************************/
+int gpio_set_value(unsigned int gpio, int value)
+{
+ int fd, result = 0;
+ char buf[MAX_BUF];
+
+ snprintf(buf, sizeof(buf), SYSFS_GPIO_DIR "/gpio%d/value", gpio);
+
+ fd = open(buf, O_WRONLY);
+ if (fd < 0) {
+ perror("gpio/set-value");
+ return fd;
+ }
+
+ if (value==LOW) {
+ if(write(fd, "0", 2) < 0)
+ result = -1;
+ }
+ else {
+ if(write(fd, "1", 2) < 0)
+ result = -1;
+ }
+
+ close(fd);
+ return result;
+}
+
+/****************************************************************
+ * gpio_get_value
+ ****************************************************************/
+int gpio_get_value(unsigned int gpio, unsigned int *value)
+{
+ int fd, result = 0;
+ char buf[MAX_BUF];
+ char ch;
+
+ snprintf(buf, sizeof(buf), SYSFS_GPIO_DIR "/gpio%d/value", gpio);
+
+ fd = open(buf, O_RDONLY);
+ if (fd < 0) {
+ perror("gpio/get-value");
+ return fd;
+ }
+
+ if(read(fd, &ch, 1) <= 0) {
+ result = -1;
+ }
+ else {
+ if (ch != '0') {
+ *value = 1;
+ } else {
+ *value = 0;
+ }
+ }
+
+ close(fd);
+ return result;
+}
+
+
+/****************************************************************
+ * gpio_set_edge
+ ****************************************************************/
+
+int gpio_set_edge(unsigned int gpio, char *edge)
+{
+ int fd, result = 0;
+ char buf[MAX_BUF];
+
+ snprintf(buf, sizeof(buf), SYSFS_GPIO_DIR "/gpio%d/edge", gpio);
+
+ fd = open(buf, O_WRONLY);
+ if (fd < 0) {
+ perror("gpio/set-edge");
+ return fd;
+ }
+
+ if(write(fd, edge, strlen(edge) + 1) < 0)
+ result = -1;
+ close(fd);
+ return result;
+}
+
+/****************************************************************
+ * gpio_fd_open
+ ****************************************************************/
+
+int gpio_fd_open(unsigned int gpio, int writeFlag)
+{
+ int fd;
+ char buf[MAX_BUF];
+
+ snprintf(buf, sizeof(buf), SYSFS_GPIO_DIR "/gpio%d/value", gpio);
+
+ fd = open(buf, writeFlag | O_NONBLOCK );
+ if (fd < 0) {
+ perror("gpio/fd_open");
+ }
+ return fd;
+}
+
+/****************************************************************
+ * gpio_fd_close
+ ****************************************************************/
+
+int gpio_fd_close(int fd)
+{
+ return close(fd);
+}
+
+/****************************************************************
+ * gpio_read
+ ****************************************************************/
+int gpio_read(int fd, unsigned int *value)
+{
+ int result = 0;
+ char ch;
+
+ if(read(fd, &ch, 1) <= 0) {
+ result = -1;
+ }
+ else {
+ if (ch != '0') {
+ *value = 1;
+ } else {
+ *value = 0;
+ }
+ }
+
+ return result;
+}
+
+/****************************************************************
+ * gpio_write
+ ****************************************************************/
+int gpio_write(int fd, int value)
+{
+ int result = 0;
+ //char buf[MAX_BUF];
+
+ if (value==LOW) {
+ if(write(fd, "0", 2) < 0)
+ result = -1;
+ }
+ else {
+ if(write(fd, "1", 2) < 0)
+ result = -1;
+ }
+
+ return result;
+}
+
+
+/****************************************************************
+ * gpio_dismiss
+ ****************************************************************/
+int gpio_dismiss(int fd, unsigned int gpio)
+{
+ close(fd);
+ gpio_unexport(gpio);
+ return 0;
+}
+
+/****************************************************************
+ * led_set_trigger
+ ****************************************************************/
+int led_set_trigger(unsigned int lednum, const char *trigger)
+{
+ // Set the trigger source for an onboard user LED
+ int fd, result = 0;
+ char buf[MAX_BUF];
+
+ snprintf(buf, sizeof(buf), SYSFS_LED_DIR "/beaglebone:green:usr%d/trigger", lednum);
+
+ fd = open(buf, O_WRONLY);
+ if (fd < 0) {
+ perror("gpio/led-set-trigger");
+ return fd;
+ }
+
+ if(write(fd, trigger, strlen(trigger) + 1) < 0)
+ result = -1;
+
+ close(fd);
+ return result;
+}
+
diff -r 000000000000 -r 8a575ba3ab52 core/I2c_Codec.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/core/I2c_Codec.cpp Fri Oct 31 19:10:17 2014 +0100
@@ -0,0 +1,286 @@
+/*
+ * I2c_Codec.cpp
+ *
+ * Handle writing the registers to the TLV320AIC310x
+ * series audio codecs, used on the BeagleBone Audio Cape.
+ * This code is designed to bypass the ALSA driver and
+ * configure the codec directly in a sensible way. It
+ * is complemented by code running on the PRU which uses
+ * the McASP serial port to transfer audio data.
+ *
+ * Created on: May 25, 2014
+ * Author: Andrew McPherson
+ */
+
+#include "../include/I2c_Codec.h"
+
+I2c_Codec::I2c_Codec()
+: running(false), dacVolumeHalfDbs(0), adcVolumeHalfDbs(0), hpVolumeHalfDbs(0)
+{}
+
+// This method initialises the audio codec to its default state
+int I2c_Codec::initCodec()
+{
+ // Write the reset register of the codec
+ if(writeRegister(0x01, 0x80)) // Software reset register
+ {
+ cout << "Failed to reset codec\n";
+ return 1;
+ }
+
+ // Wait for codec to process the reset (for safety)
+ usleep(5000);
+
+ return 0;
+}
+
+// Tell the codec to start generating audio
+// See the TLV320AIC3106 datasheet for full details of the registers
+// The dual_rate flag, when true, runs the codec at 88.2kHz; otherwise
+// it runs at 44.1kHz
+int I2c_Codec::startAudio(int dual_rate)
+{
+ if(writeRegister(0x02, 0x00)) // Codec sample rate register: fs_ref / 1
+ return 1;
+ if(writeRegister(0x03, 0x91)) // PLL register A: enable
+ return 1;
+ if(writeRegister(0x04, 0x1C)) // PLL register B
+ return 1;
+ if(writeRegister(0x05, 0x52)) // PLL register C
+ return 1;
+ if(writeRegister(0x06, 0x40)) // PLL register D
+ return 1;
+ if(dual_rate) {
+ if(writeRegister(0x07, 0xEA)) // Codec datapath register: 44.1kHz; dual rate; standard datapath
+ return 1;
+ }
+ else {
+ if(writeRegister(0x07, 0x8A)) // Codec datapath register: 44.1kHz; std rate; standard datapath
+ return 1;
+ }
+ if(writeRegister(0x08, 0xC0)) // Audio serial control register A: BLCK, WCLK outputs
+ return 1;
+ if(writeRegister(0x09, 0x40)) // Audio serial control register B: DSP mode, word len 16 bits
+ return 1;
+ if(writeRegister(0x0A, 0x00)) // Audio serial control register C: 0 bit offset
+ return 1;
+ if(writeRegister(0x0B, 0x01)) // Audio codec overflow flag register: PLL R = 1
+ return 1;
+ if(writeRegister(0x0C, 0x00)) // Digital filter register: disabled
+ return 1;
+ if(writeRegister(0x0D, 0x00)) // Headset / button press register A: disabled
+ return 1;
+ if(writeRegister(0x0E, 0x00)) // Headset / button press register B: disabled
+ return 1;
+ if(writeRegister(0x0F, 0x20)) // Left ADC PGA gain control: not muted; 0x20 = 16dB
+ return 1;
+ if(writeRegister(0x10, 0x20)) // Right ADC PGA gain control: not muted; 0x20 = 16dB
+ return 1;
+
+ if(writeRegister(0x25, 0xC0)) // DAC power/driver register: DAC power on (left and right)
+ return 1;
+ if(writeRegister(0x26, 0x04)) // High power output driver register: Enable short circuit protection
+ return 1;
+ if(writeRegister(0x28, 0x02)) // High power output stage register: disable soft stepping
+ return 1;
+
+ if(writeRegister(0x52, 0x80)) // DAC_L1 to LEFT_LOP volume control: routed, volume 0dB
+ return 1;
+ if(writeRegister(0x5C, 0x80)) // DAC_R1 to RIGHT_LOP volume control: routed, volume 0dB
+ return 1;
+
+ if(writeHPVolumeRegisters()) // Send DAC to high-power outputs
+ return 1;
+
+ if(writeRegister(0x66, 0x02)) // Clock generation control register: use MCLK, PLL N = 2
+ return 1;
+
+ if(writeRegister(0x33, 0x0D)) // HPLOUT output level control: output level = 0dB, not muted, powered up
+ return 1;
+ if(writeRegister(0x41, 0x0D)) // HPROUT output level control: output level = 0dB, not muted, powered up
+ return 1;
+ if(writeRegister(0x56, 0x09)) // LEFT_LOP output level control: 0dB, not muted, powered up
+ return 1;
+ if(writeRegister(0x5D, 0x09)) // RIGHT_LOP output level control: 0dB, not muted, powered up
+ return 1;
+
+ if(writeDACVolumeRegisters(false)) // Unmute and set volume
+ return 1;
+
+ if(writeRegister(0x65, 0x00)) // GPIO control register B: disabled; codec uses PLLDIV_OUT
+ return 1;
+
+ if(writeADCVolumeRegisters(false)) // Unmute and set ADC volume
+ return 1;
+
+ running = true;
+ return 0;
+}
+
+// Set the volume of the DAC output
+int I2c_Codec::setDACVolume(int halfDbSteps)
+{
+ dacVolumeHalfDbs = halfDbSteps;
+ if(running)
+ return writeDACVolumeRegisters(false);
+
+ return 0;
+}
+
+// Set the volume of the DAC output
+int I2c_Codec::setADCVolume(int halfDbSteps)
+{
+ adcVolumeHalfDbs = halfDbSteps;
+ if(running)
+ return writeADCVolumeRegisters(false);
+
+ return 0;
+}
+
+// Update the DAC volume control registers
+int I2c_Codec::writeDACVolumeRegisters(bool mute)
+{
+ int volumeBits = 0;
+
+ if(dacVolumeHalfDbs < 0) { // Volume is specified in half-dBs with 0 as full scale
+ volumeBits = -dacVolumeHalfDbs;
+ if(volumeBits > 127)
+ volumeBits = 127;
+ }
+
+ if(mute) {
+ if(writeRegister(0x2B, volumeBits | 0x80)) // Left DAC volume control: muted
+ return 1;
+ if(writeRegister(0x2C, volumeBits | 0x80)) // Right DAC volume control: muted
+ return 1;
+ }
+ else {
+ if(writeRegister(0x2B, volumeBits)) // Left DAC volume control: not muted
+ return 1;
+ if(writeRegister(0x2C, volumeBits)) // Right DAC volume control: not muted
+ return 1;
+ }
+
+ return 0;
+}
+
+// Update the ADC volume control registers
+int I2c_Codec::writeADCVolumeRegisters(bool mute)
+{
+ int volumeBits = 0;
+
+ // Volume is specified in half-dBs with 0 as full scale
+ // The codec uses 1.5dB steps so we divide this number by 3
+ if(adcVolumeHalfDbs < 0) {
+ volumeBits = -adcVolumeHalfDbs / 3;
+ if(volumeBits > 8)
+ volumeBits = 8;
+ }
+
+ if(mute) {
+ if(writeRegister(0x13, 0x00)) // Line1L to Left ADC control register: power down
+ return 1;
+ if(writeRegister(0x16, 0x00)) // Line1R to Right ADC control register: power down
+ return 1;
+ }
+ else {
+ if(writeRegister(0x13, 0x7C)) // Line1L disabled; left ADC powered up with soft step
+ return 1;
+ if(writeRegister(0x16, 0x7C)) // Line1R disabled; right ADC powered up with soft step
+ return 1;
+ if(writeRegister(0x11, (volumeBits << 4) | 0x0F)) // Line2L connected to left ADC
+ return 1;
+ if(writeRegister(0x12, volumeBits | 0xF0)) // Line2R connected to right ADC
+ return 1;
+ }
+
+ return 0;
+}
+
+// Set the volume of the headphone output
+int I2c_Codec::setHPVolume(int halfDbSteps)
+{
+ hpVolumeHalfDbs = halfDbSteps;
+ if(running)
+ return writeHPVolumeRegisters();
+
+ return 0;
+}
+
+
+// Update the headphone volume control registers
+int I2c_Codec::writeHPVolumeRegisters()
+{
+ int volumeBits = 0;
+
+ if(hpVolumeHalfDbs < 0) { // Volume is specified in half-dBs with 0 as full scale
+ volumeBits = -hpVolumeHalfDbs;
+ if(volumeBits > 127)
+ volumeBits = 127;
+ }
+
+ if(writeRegister(0x2F, volumeBits | 0x80)) // DAC_L1 to HPLOUT register: route to HPLOUT, volume 0dB
+ return 1;
+ if(writeRegister(0x40, volumeBits | 0x80)) // DAC_R1 to HPROUT register: route to HPROUT, volume 0dB
+ return 1;
+
+ return 0;
+}
+
+// This tells the codec to stop generating audio and mute the outputs
+int I2c_Codec::stopAudio()
+{
+ if(writeDACVolumeRegisters(true)) // Mute the DACs
+ return 1;
+ if(writeADCVolumeRegisters(true)) // Mute the ADCs
+ return 1;
+
+ usleep(10000);
+
+ if(writeRegister(0x33, 0x0C)) // HPLOUT output level register: muted
+ return 1;
+ if(writeRegister(0x41, 0x0C)) // HPROUT output level register: muted
+ return 1;
+ if(writeRegister(0x56, 0x08)) // LEFT_LOP output level control: muted
+ return 1;
+ if(writeRegister(0x5D, 0x08)) // RIGHT_LOP output level control: muted
+ return 1;
+ if(writeRegister(0x25, 0x00)) // DAC power/driver register: power off
+ return 1;
+ if(writeRegister(0x03, 0x11)) // PLL register A: disable
+ return 1;
+ if(writeRegister(0x01, 0x80)) // Reset codec to defaults
+ return 1;
+
+ running = false;
+ return 0;
+}
+
+// Write a specific register on the codec
+int I2c_Codec::writeRegister(unsigned int reg, unsigned int value)
+{
+ char buf[2] = { reg & 0xFF, value & 0xFF };
+
+ if(write(i2C_file, buf, 2) != 2)
+ {
+ cout << "Failed to write register " << reg << " on codec\n";
+ return 1;
+ }
+
+ return 0;
+}
+
+
+int I2c_Codec::readI2C()
+{
+ // Nothing to do here, we only write the registers
+ return 0;
+}
+
+
+I2c_Codec::~I2c_Codec()
+{
+ if(running)
+ stopAudio();
+}
+
diff -r 000000000000 -r 8a575ba3ab52 core/PRU.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/core/PRU.cpp Fri Oct 31 19:10:17 2014 +0100
@@ -0,0 +1,469 @@
+/*
+ * PRU.cpp
+ *
+ * Code for communicating with the Programmable Realtime Unit (PRU)
+ * on the BeagleBone AM335x series processors. The PRU loads and runs
+ * a separate code image compiled from an assembly file. Here it is
+ * used to handle audio and SPI ADC/DAC data.
+ *
+ * This code is specific to the PRU code in the assembly file; for example,
+ * it uses certain GPIO resources that correspond to that image.
+ *
+ * Created on: May 27, 2014
+ * Author: andrewm
+ */
+
+#include "../include/PRU.h"
+#include "../include/prussdrv.h"
+#include "../include/pruss_intc_mapping.h"
+#include "../include/GPIOcontrol.h"
+#include "../include/render.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+// Xenomai-specific includes
+#include
+#include
+#include
+#include
+
+using namespace std;
+
+#define PRU_MEM_MCASP_OFFSET 0x2000 // Offset within PRU-SHARED RAM
+#define PRU_MEM_MCASP_LENGTH 0x2000 // Length of McASP memory, in bytes
+#define PRU_MEM_DAC_OFFSET 0x0 // Offset within PRU0 RAM
+#define PRU_MEM_DAC_LENGTH 0x2000 // Length of ADC+DAC memory, in bytes
+#define PRU_MEM_COMM_OFFSET 0x0 // Offset within PRU-SHARED RAM
+
+#define PRU_SHOULD_STOP 0
+#define PRU_CURRENT_BUFFER 1
+#define PRU_BUFFER_FRAMES 2
+#define PRU_SHOULD_SYNC 3
+#define PRU_SYNC_ADDRESS 4
+#define PRU_SYNC_PIN_MASK 5
+#define PRU_LED_ADDRESS 6
+#define PRU_LED_PIN_MASK 7
+#define PRU_FRAME_COUNT 8
+#define PRU_USE_SPI 9
+
+#define PRU_SAMPLE_INTERVAL_NS 45351 // 22050Hz per SPI sample = 45.351us
+
+#define GPIO0_ADDRESS 0x44E07000
+#define GPIO1_ADDRESS 0x4804C000
+#define GPIO_SIZE 0x198
+#define GPIO_CLEARDATAOUT (0x190 / 4)
+#define GPIO_SETDATAOUT (0x194 / 4)
+
+#define TEST_PIN_GPIO_BASE GPIO0_ADDRESS // Use GPIO0(31) for debugging
+#define TEST_PIN_MASK (1 << 31)
+#define TEST_PIN2_MASK (1 << 26)
+
+#define USERLED3_GPIO_BASE GPIO1_ADDRESS // GPIO1(24) is user LED 3
+#define USERLED3_PIN_MASK (1 << 24)
+
+const unsigned int PRU::kPruGPIODACSyncPin = 5; // GPIO0(5); P9-17
+const unsigned int PRU::kPruGPIOADCSyncPin = 48; // GPIO1(16); P9-15
+
+const unsigned int PRU::kPruGPIOTestPin = 60; // GPIO1(28); P9-12
+const unsigned int PRU::kPruGPIOTestPin2 = 31; // GPIO0(31); P9-13
+const unsigned int PRU::kPruGPIOTestPin3 = 26; // GPIO0(26); P8-14
+
+extern int gShouldStop;
+extern int gRTAudioVerbose;
+
+// Constructor: specify a PRU number (0 or 1)
+PRU::PRU()
+: pru_number(0), running(false), spi_enabled(false), gpio_enabled(false), led_enabled(false),
+ gpio_test_pin_enabled(false), xenomai_gpio_fd(-1), xenomai_gpio(0)
+{
+
+}
+
+// Destructor
+PRU::~PRU()
+{
+ if(running)
+ disable();
+ if(gpio_enabled)
+ cleanupGPIO();
+ if(xenomai_gpio_fd >= 0)
+ close(xenomai_gpio_fd);
+}
+
+// Prepare the GPIO pins needed for the PRU
+// If include_test_pin is set, the GPIO output
+// is also prepared for an output which can be
+// viewed on a scope. If include_led is set,
+// user LED 3 on the BBB is taken over by the PRU
+// to indicate activity
+int PRU::prepareGPIO(int use_spi, int include_test_pin, int include_led)
+{
+ if(use_spi) {
+ // Prepare DAC CS/ pin: output, high to begin
+ if(gpio_export(kPruGPIODACSyncPin)) {
+ if(gRTAudioVerbose)
+ cout << "Warning: couldn't export DAC sync pin\n";
+ }
+ if(gpio_set_dir(kPruGPIODACSyncPin, OUTPUT_PIN)) {
+ if(gRTAudioVerbose)
+ cout << "Couldn't set direction on DAC sync pin\n";
+ return -1;
+ }
+ if(gpio_set_value(kPruGPIODACSyncPin, HIGH)) {
+ if(gRTAudioVerbose)
+ cout << "Couldn't set value on DAC sync pin\n";
+ return -1;
+ }
+
+ // Prepare ADC CS/ pin: output, high to begin
+ if(gpio_export(kPruGPIOADCSyncPin)) {
+ if(gRTAudioVerbose)
+ cout << "Warning: couldn't export ADC sync pin\n";
+ }
+ if(gpio_set_dir(kPruGPIOADCSyncPin, OUTPUT_PIN)) {
+ if(gRTAudioVerbose)
+ cout << "Couldn't set direction on ADC sync pin\n";
+ return -1;
+ }
+ if(gpio_set_value(kPruGPIOADCSyncPin, HIGH)) {
+ if(gRTAudioVerbose)
+ cout << "Couldn't set value on ADC sync pin\n";
+ return -1;
+ }
+
+ spi_enabled = true;
+ }
+
+ if(include_test_pin) {
+ // Prepare GPIO test output (for debugging), low to begin
+ if(gpio_export(kPruGPIOTestPin)) {
+ if(gRTAudioVerbose)
+ cout << "Warning: couldn't export GPIO test pin\n";
+ }
+ if(gpio_set_dir(kPruGPIOTestPin, OUTPUT_PIN)) {
+ if(gRTAudioVerbose)
+ cout << "Couldn't set direction on GPIO test pin\n";
+ return -1;
+ }
+ if(gpio_set_value(kPruGPIOTestPin, LOW)) {
+ if(gRTAudioVerbose)
+ cout << "Couldn't set value on GPIO test pin\n";
+ return -1;
+ }
+
+ if(gpio_export(kPruGPIOTestPin2)) {
+ if(gRTAudioVerbose)
+ cout << "Warning: couldn't export GPIO test pin 2\n";
+ }
+ if(gpio_set_dir(kPruGPIOTestPin2, OUTPUT_PIN)) {
+ if(gRTAudioVerbose)
+ cout << "Couldn't set direction on GPIO test pin 2\n";
+ return -1;
+ }
+ if(gpio_set_value(kPruGPIOTestPin2, LOW)) {
+ if(gRTAudioVerbose)
+ cout << "Couldn't set value on GPIO test pin 2\n";
+ return -1;
+ }
+
+ if(gpio_export(kPruGPIOTestPin3)) {
+ if(gRTAudioVerbose)
+ cout << "Warning: couldn't export GPIO test pin 3\n";
+ }
+ if(gpio_set_dir(kPruGPIOTestPin3, OUTPUT_PIN)) {
+ if(gRTAudioVerbose)
+ cout << "Couldn't set direction on GPIO test pin 3\n";
+ return -1;
+ }
+ if(gpio_set_value(kPruGPIOTestPin3, LOW)) {
+ if(gRTAudioVerbose)
+ cout << "Couldn't set value on GPIO test pin 3\n";
+ return -1;
+ }
+ gpio_test_pin_enabled = true;
+ }
+
+ if(include_led) {
+ // Turn off system function for LED3 so it can be reused by PRU
+ led_set_trigger(3, "none");
+ led_enabled = true;
+ }
+
+ gpio_enabled = true;
+
+ return 0;
+}
+
+// Clean up the GPIO at the end
+void PRU::cleanupGPIO()
+{
+ if(!gpio_enabled)
+ return;
+ if(spi_enabled) {
+ gpio_unexport(kPruGPIODACSyncPin);
+ gpio_unexport(kPruGPIOADCSyncPin);
+ }
+ if(gpio_test_pin_enabled) {
+ gpio_unexport(kPruGPIOTestPin);
+ gpio_unexport(kPruGPIOTestPin2);
+ gpio_unexport(kPruGPIOTestPin3);
+ }
+ if(led_enabled) {
+ // Set LED back to default eMMC status
+ // TODO: make it go back to its actual value before this program,
+ // rather than the system default
+ led_set_trigger(3, "mmc1");
+ }
+
+ gpio_enabled = gpio_test_pin_enabled = false;
+}
+
+// Initialise and open the PRU
+int PRU::initialise(int pru_num, int frames_per_buffer, bool xenomai_test_pin)
+{
+ uint32_t *pruMem = 0;
+
+ if(!gpio_enabled) {
+ rt_printf("initialise() called before GPIO enabled\n");
+ return 1;
+ }
+
+ pru_number = pru_num;
+
+ /* Initialize structure used by prussdrv_pruintc_intc */
+ /* PRUSS_INTC_INITDATA is found in pruss_intc_mapping.h */
+ tpruss_intc_initdata pruss_intc_initdata = PRUSS_INTC_INITDATA;
+
+ /* Allocate and initialize memory */
+ prussdrv_init();
+ if(prussdrv_open(PRU_EVTOUT_0)) {
+ rt_printf("Failed to open PRU driver\n");
+ return 1;
+ }
+
+ /* Map PRU's INTC */
+ prussdrv_pruintc_init(&pruss_intc_initdata);
+
+ spi_buffer_frames = frames_per_buffer;
+ audio_buffer_frames = spi_buffer_frames * 2;
+
+ /* Map PRU memory to pointers */
+ prussdrv_map_prumem (PRUSS0_SHARED_DATARAM, (void **)&pruMem);
+ pru_buffer_comm = (uint32_t *)&pruMem[PRU_MEM_COMM_OFFSET/sizeof(uint32_t)];
+ pru_buffer_audio_dac = (int16_t *)&pruMem[PRU_MEM_MCASP_OFFSET/sizeof(uint32_t)];
+
+ /* ADC memory starts 2(ch)*2(buffers)*2(samples/spi)*bufsize samples later */
+ pru_buffer_audio_adc = &pru_buffer_audio_dac[8 * spi_buffer_frames];
+
+ if(spi_enabled) {
+ prussdrv_map_prumem (pru_number == 0 ? PRUSS0_PRU0_DATARAM : PRUSS0_PRU1_DATARAM, (void **)&pruMem);
+ pru_buffer_spi_dac = (uint16_t *)&pruMem[PRU_MEM_DAC_OFFSET/sizeof(uint32_t)];
+
+ /* ADC memory starts after 8(ch)*2(buffers)*bufsize samples */
+ pru_buffer_spi_adc = &pru_buffer_spi_dac[16 * spi_buffer_frames];
+ }
+ else {
+ pru_buffer_spi_dac = pru_buffer_spi_adc = 0;
+ }
+
+ /* Set up flags */
+ pru_buffer_comm[PRU_SHOULD_STOP] = 0;
+ pru_buffer_comm[PRU_CURRENT_BUFFER] = 0;
+ pru_buffer_comm[PRU_BUFFER_FRAMES] = spi_buffer_frames;
+ pru_buffer_comm[PRU_SHOULD_SYNC] = 0;
+ pru_buffer_comm[PRU_SYNC_ADDRESS] = 0;
+ pru_buffer_comm[PRU_SYNC_PIN_MASK] = 0;
+ if(led_enabled) {
+ pru_buffer_comm[PRU_LED_ADDRESS] = USERLED3_GPIO_BASE;
+ pru_buffer_comm[PRU_LED_PIN_MASK] = USERLED3_PIN_MASK;
+ }
+ else {
+ pru_buffer_comm[PRU_LED_ADDRESS] = 0;
+ pru_buffer_comm[PRU_LED_PIN_MASK] = 0;
+ }
+ if(spi_enabled) {
+ pru_buffer_comm[PRU_USE_SPI] = 1;
+ }
+ else {
+ pru_buffer_comm[PRU_USE_SPI] = 0;
+ }
+
+ /* Clear ADC and DAC memory */
+ if(spi_enabled) {
+ for(int i = 0; i < PRU_MEM_DAC_LENGTH / 2; i++)
+ pru_buffer_spi_dac[i] = 0;
+ }
+ for(int i = 0; i < PRU_MEM_MCASP_LENGTH / 2; i++)
+ pru_buffer_audio_dac[i] = 0;
+
+ /* If using GPIO test pin for Xenomai (for debugging), initialise the pointer now */
+ if(xenomai_test_pin && xenomai_gpio_fd < 0) {
+ xenomai_gpio_fd = open("/dev/mem", O_RDWR);
+ if(xenomai_gpio_fd < 0)
+ rt_printf("Unable to open /dev/mem for GPIO test pin\n");
+ else {
+ xenomai_gpio = (uint32_t *)mmap(0, GPIO_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, xenomai_gpio_fd, TEST_PIN_GPIO_BASE);
+ if(xenomai_gpio == MAP_FAILED) {
+ rt_printf("Unable to map GPIO address for test pin\n");
+ xenomai_gpio = 0;
+ close(xenomai_gpio_fd);
+ xenomai_gpio_fd = -1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+// Run the code image in the specified file
+int PRU::start(char * const filename)
+{
+ /* Clear any old interrupt */
+ prussdrv_pru_clear_event(pru_number == 0 ? PRU0_ARM_INTERRUPT : PRU1_ARM_INTERRUPT);
+
+ /* Load and execute binary on PRU */
+ if(prussdrv_exec_program(pru_number, filename)) {
+ rt_printf("Failed to execute PRU code from %s\n", filename);
+ return 1;
+ }
+
+ running = true;
+ return 0;
+}
+
+// Main loop to read and write data from/to PRU
+void PRU::loop()
+{
+ // Polling interval is 1/4 of the period
+ RTIME sleepTime = PRU_SAMPLE_INTERVAL_NS * spi_buffer_frames / 4;
+ float *audioInBuffer, *audioOutBuffer;
+
+ audioInBuffer = (float *)malloc(2 * audio_buffer_frames * sizeof(float));
+ audioOutBuffer = (float *)malloc(2 * audio_buffer_frames * sizeof(float));
+
+ if(audioInBuffer == 0 || audioOutBuffer == 0) {
+ rt_printf("Error: couldn't allocated audio buffers\n");
+ return;
+ }
+
+ while(!gShouldStop) {
+ // Wait for PRU to move to buffer 1
+ while(pru_buffer_comm[PRU_CURRENT_BUFFER] == 0 && !gShouldStop) {
+ rt_task_sleep(sleepTime);
+ }
+ if(gShouldStop)
+ break;
+
+ if(xenomai_gpio != 0) {
+ // Set the test pin high
+ xenomai_gpio[GPIO_SETDATAOUT] = TEST_PIN_MASK;
+ }
+
+ // Render from/to buffer 0
+
+ // Convert short (16-bit) samples to float
+ for(unsigned int n = 0; n < 2 * audio_buffer_frames; n++)
+ audioInBuffer[n] = (float)pru_buffer_audio_adc[n] / 32768.0;
+
+ if(spi_enabled)
+ render(spi_buffer_frames, audio_buffer_frames, audioInBuffer, audioOutBuffer,
+ pru_buffer_spi_adc, pru_buffer_spi_dac);
+ else
+ render(0, audio_buffer_frames, audioInBuffer, audioOutBuffer, 0, 0);
+
+ // Convert float back to short
+ for(unsigned int n = 0; n < 2 * audio_buffer_frames; n++) {
+ int out = audioOutBuffer[n] * 32768.0;
+ if(out < -32768) out = -32768;
+ else if(out > 32767) out = 32767;
+ pru_buffer_audio_dac[n] = (int16_t)out;
+ }
+
+ if(xenomai_gpio != 0) {
+ // Set the test pin high
+ xenomai_gpio[GPIO_CLEARDATAOUT] = TEST_PIN_MASK;
+ }
+
+ // Wait for PRU to move to buffer 0
+ while(pru_buffer_comm[PRU_CURRENT_BUFFER] != 0 && !gShouldStop) {
+ rt_task_sleep(sleepTime);
+ }
+
+ if(gShouldStop)
+ break;
+
+ if(xenomai_gpio != 0) {
+ // Set the test pin high
+ xenomai_gpio[GPIO_SETDATAOUT] = TEST_PIN_MASK;
+ }
+
+ // Render from/to buffer 1
+
+ // Convert short (16-bit) samples to float
+ for(unsigned int n = 0; n < 2 * audio_buffer_frames; n++)
+ audioInBuffer[n] = (float)pru_buffer_audio_adc[n + audio_buffer_frames * 2] / 32768.0;
+
+ if(spi_enabled)
+ render(spi_buffer_frames, audio_buffer_frames, audioInBuffer, audioOutBuffer,
+ &pru_buffer_spi_adc[spi_buffer_frames * 8], &pru_buffer_spi_dac[spi_buffer_frames * 8]);
+ else
+ render(0, audio_buffer_frames, audioInBuffer, audioOutBuffer, 0, 0);
+
+ // Convert float back to short
+ for(unsigned int n = 0; n < 2 * audio_buffer_frames; n++) {
+ int out = audioOutBuffer[n] * 32768.0;
+ if(out < -32768) out = -32768;
+ else if(out > 32767) out = 32767;
+ pru_buffer_audio_dac[n + audio_buffer_frames * 2] = (int16_t)out;
+ }
+
+ if(xenomai_gpio != 0) {
+ // Set the test pin high
+ xenomai_gpio[GPIO_CLEARDATAOUT] = TEST_PIN_MASK;
+ }
+ }
+
+ // Tell PRU to stop
+ pru_buffer_comm[PRU_SHOULD_STOP] = 1;
+
+ free(audioInBuffer);
+ free(audioOutBuffer);
+}
+
+// Wait for an interrupt from the PRU indicate it is finished
+void PRU::waitForFinish()
+{
+ if(!running)
+ return;
+ prussdrv_pru_wait_event (PRU_EVTOUT_0);
+ prussdrv_pru_clear_event(pru_number == 0 ? PRU0_ARM_INTERRUPT : PRU1_ARM_INTERRUPT);
+}
+
+// Turn off the PRU when done
+void PRU::disable()
+{
+ /* Disable PRU and close memory mapping*/
+ prussdrv_pru_disable(pru_number);
+ prussdrv_exit();
+ running = false;
+}
+
+// Debugging
+void PRU::setGPIOTestPin()
+{
+ if(!xenomai_gpio)
+ return;
+ xenomai_gpio[GPIO_SETDATAOUT] = TEST_PIN2_MASK;
+}
+
+void PRU::clearGPIOTestPin()
+{
+ if(!xenomai_gpio)
+ return;
+ xenomai_gpio[GPIO_CLEARDATAOUT] = TEST_PIN2_MASK;
+}
diff -r 000000000000 -r 8a575ba3ab52 core/RTAudio.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/core/RTAudio.cpp Fri Oct 31 19:10:17 2014 +0100
@@ -0,0 +1,325 @@
+/*
+ * RTAudio.cpp
+ *
+ * Central control code for hard real-time audio on BeagleBone Black
+ * using PRU and Xenomai Linux extensions. This code began as part
+ * of the Hackable Instruments project (EPSRC) at Queen Mary University
+ * of London, 2013-14.
+ *
+ * (c) 2014 Victor Zappi and Andrew McPherson
+ * Queen Mary University of London
+ */
+
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+// Xenomai-specific includes
+#include
+#include
+#include
+#include
+
+#include "../include/RTAudio.h"
+#include "../include/PRU.h"
+#include "../include/I2c_Codec.h"
+#include "../include/render.h"
+#include "../include/GPIOcontrol.h"
+
+using namespace std;
+
+// Data structure to keep track of auxiliary tasks we
+// can schedule
+typedef struct {
+ RT_TASK task;
+ void (*function)(void);
+ char *name;
+ int priority;
+} InternalAuxiliaryTask;
+
+const char gRTAudioThreadName[] = "beaglert-audio";
+const char gRTCalculationThreadNameMedium[] = "dbox-calculation-medium";
+const char gRTCalculationThreadNameLow[] = "dbox-calculation-low";
+
+// Real-time tasks and objects
+RT_TASK gRTAudioThread;
+PRU *gPRU = 0;
+I2c_Codec *gAudioCodec = 0;
+
+vector gAuxTasks;
+
+// Flag which tells the audio task to stop
+bool gShouldStop = false;
+
+// general settings
+int gRTAudioVerbose = 0; // Verbosity level for debugging
+char gPRUFilename[256] = "pru_rtaudio.bin"; // path to PRU binary file
+int gAmplifierMutePin = -1;
+
+
+// initAudio() prepares the infrastructure for running PRU-based real-time
+// audio, but does not actually start the calculations.
+// periodSize indicates the number of _sensor_ frames per period: the audio period size
+// is twice this value. In total, the audio latency in frames will be 4*periodSize,
+// plus any latency inherent in the ADCs and DACs themselves.
+// useMatrix indicates whether to use the ADC and DAC or just the audio codec.
+// userData is an opaque pointer which will be passed through to the initialise_render()
+// function for application-specific use
+//
+// Returns 0 on success.
+
+int initAudio(int periodSize, int useMatrix,
+ void *userData,
+ int codecI2CAddress, int ampMutePin)
+{
+ rt_print_auto_init(1);
+ if(gRTAudioVerbose == 1)
+ rt_printf("Running with Xenomai\n");
+
+ if(gRTAudioVerbose == 1)
+ cout << "---------------->Init Audio Thread" << endl;
+
+ // Prepare GPIO pins for amplifier mute and status LED
+ if(ampMutePin >= 0) {
+ gAmplifierMutePin = ampMutePin;
+
+ if(gpio_export(ampMutePin)) {
+ if(gRTAudioVerbose)
+ cout << "Warning: couldn't export amplifier mute pin\n";
+ }
+ if(gpio_set_dir(ampMutePin, OUTPUT_PIN)) {
+ if(gRTAudioVerbose)
+ cout << "Couldn't set direction on amplifier mute pin\n";
+ return -1;
+ }
+ if(gpio_set_value(ampMutePin, LOW)) {
+ if(gRTAudioVerbose)
+ cout << "Couldn't set value on amplifier mute pin\n";
+ return -1;
+ }
+ }
+
+ // Use PRU for audio
+ gPRU = new PRU();
+ gAudioCodec = new I2c_Codec();
+
+ if(gPRU->prepareGPIO(useMatrix, 1, 1)) {
+ cout << "Error: unable to prepare GPIO for PRU audio\n";
+ return 1;
+ }
+ if(gPRU->initialise(0, periodSize, true)) {
+ cout << "Error: unable to initialise PRU\n";
+ return 1;
+ }
+ if(gAudioCodec->initI2C_RW(2, codecI2CAddress, -1)) {
+ cout << "Unable to open codec I2C\n";
+ return 1;
+ }
+ if(gAudioCodec->initCodec()) {
+ cout << "Error: unable to initialise audio codec\n";
+ return 1;
+ }
+ gAudioCodec->setDACVolume(0); // Set the DAC volume to full-scale
+ gAudioCodec->setHPVolume(-12); // Headphones 6dB down
+ gAudioCodec->setADCVolume(-12); // Set the ADC volume to 6dB down
+
+ if(!initialise_render(2, useMatrix ? periodSize : 0, periodSize * 2, 22050.0, 44100.0, userData)) {
+ cout << "Couldn't initialise audio rendering\n";
+ return 1;
+ }
+
+ return 0;
+}
+
+// audioLoop() is the main function which starts the PRU audio code
+// and then transfers control to the PRU object. The PRU object in
+// turn will call the audio render() callback function every time
+// there is new data to process.
+
+void audioLoop(void *)
+{
+ if(gRTAudioVerbose==1)
+ rt_printf("_________________Audio Thread!\n");
+
+ // PRU audio
+ assert(gAudioCodec != 0 && gPRU != 0);
+
+ if(gAudioCodec->startAudio(0)) {
+ rt_printf("Error: unable to start I2C audio codec\n");
+ gShouldStop = 1;
+ }
+ else {
+ if(gPRU->start(gPRUFilename)) {
+ rt_printf("Error: unable to start PRU from file %s\n", gPRUFilename);
+ gShouldStop = 1;
+ }
+ else {
+ // All systems go. Run the loop; it will end when gShouldStop is set to 1
+ // First unmute the amplifier
+ if(gpio_set_value(gAmplifierMutePin, HIGH)) {
+ if(gRTAudioVerbose)
+ rt_printf("Warning: couldn't set value (high) on amplifier mute pin\n");
+ }
+
+ gPRU->loop();
+
+ // Now clean up
+ // gPRU->waitForFinish();
+ gPRU->disable();
+ gAudioCodec->stopAudio();
+ gPRU->cleanupGPIO();
+ }
+ }
+
+ if(gRTAudioVerbose == 1)
+ rt_printf("audio thread ended\n");
+}
+
+// Create a calculation loop which can run independently of the audio, at a different
+// (equal or lower) priority. Audio priority is 99; priority should be generally be less than this.
+// Returns an (opaque) pointer to the created task on success; 0 on failure
+AuxiliaryTask createAuxiliaryTaskLoop(void (*functionToCall)(void), int priority, const char *name)
+{
+ InternalAuxiliaryTask *newTask = (InternalAuxiliaryTask*)malloc(sizeof(InternalAuxiliaryTask));
+
+ // Attempt to create the task
+ if(rt_task_create(&(newTask->task), name, 0, priority, T_JOINABLE | T_FPU)) {
+ cout << "Error: unable to create auxiliary task " << name << endl;
+ free(newTask);
+ return 0;
+ }
+
+ // Populate the rest of the data structure and store it in the vector
+ newTask->function = functionToCall;
+ newTask->name = strdup(name);
+ newTask->priority = priority;
+
+ gAuxTasks.push_back(newTask);
+
+ return (AuxiliaryTask)newTask;
+}
+
+// Schedule a previously created auxiliary task. It will run when the priority rules next
+// allow it to be scheduled.
+void scheduleAuxiliaryTask(AuxiliaryTask task)
+{
+ InternalAuxiliaryTask *taskToSchedule = (InternalAuxiliaryTask *)task;
+
+ rt_task_resume(&taskToSchedule->task);
+}
+
+// Calculation loop that can be used for other tasks running at a lower
+// priority than the audio thread. Simple wrapper for Xenomai calls.
+// Treat the argument as containing the task structure
+void auxiliaryTaskLoop(void *taskStruct)
+{
+ // Get function to call from the argument
+ void (*auxiliary_function)(void) = ((InternalAuxiliaryTask *)taskStruct)->function;
+ const char *name = ((InternalAuxiliaryTask *)taskStruct)->name;
+
+ // Wait for a notification
+ rt_task_suspend(NULL);
+
+ while(!gShouldStop) {
+ // Then run the calculations
+ auxiliary_function();
+
+ // Wait for a notification
+ rt_task_suspend(NULL);
+ }
+
+ if(gRTAudioVerbose == 1)
+ rt_printf("auxiliary task %s ended\n", name);
+}
+
+// startAudio() should be called only after initAudio() successfully completes.
+// It launches the real-time Xenomai task which runs the audio loop. Returns 0
+// on success.
+
+int startAudio()
+{
+ // Create audio thread with the highest priority
+ if(rt_task_create(&gRTAudioThread, gRTAudioThreadName, 0, 99, T_JOINABLE | T_FPU)) {
+ cout << "Error: unable to create Xenomai audio thread" << endl;
+ return -1;
+ }
+
+ // Start all RT threads
+ if(rt_task_start(&gRTAudioThread, &audioLoop, 0)) {
+ cout << "Error: unable to start Xenomai audio thread" << endl;
+ return -1;
+ }
+
+ // The user may have created other tasks. Start those also.
+ vector::iterator it;
+ for(it = gAuxTasks.begin(); it != gAuxTasks.end(); it++) {
+ InternalAuxiliaryTask *taskStruct = *it;
+
+ if(rt_task_start(&(taskStruct->task), &auxiliaryTaskLoop, taskStruct)) {
+ cerr << "Error: unable to start Xenomai task " << taskStruct->name << endl;
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+// Stop the PRU-based audio from running and wait
+// for the tasks to complete before returning.
+
+void stopAudio()
+{
+ // Tell audio thread to stop (if this hasn't been done already)
+ gShouldStop = true;
+
+ // Now wait for threads to respond and actually stop...
+ rt_task_join(&gRTAudioThread);
+
+ // Stop all the auxiliary threads too
+ vector::iterator it;
+ for(it = gAuxTasks.begin(); it != gAuxTasks.end(); it++) {
+ InternalAuxiliaryTask *taskStruct = *it;
+
+ // Wake up each thread and join it
+ rt_task_resume(&(taskStruct->task));
+ rt_task_join(&(taskStruct->task));
+ }
+}
+
+// Free any resources associated with PRU real-time audio
+void cleanupAudio()
+{
+ cleanup_render();
+
+ // Clean up the auxiliary tasks
+ vector::iterator it;
+ for(it = gAuxTasks.begin(); it != gAuxTasks.end(); it++) {
+ InternalAuxiliaryTask *taskStruct = *it;
+
+ // Free the name string and the struct itself
+ free(taskStruct->name);
+ free(taskStruct);
+ }
+ gAuxTasks.clear();
+
+ if(gPRU != 0)
+ delete gPRU;
+ if(gAudioCodec != 0)
+ delete gAudioCodec;
+
+ if(gAmplifierMutePin >= 0)
+ gpio_unexport(gAmplifierMutePin);
+ gAmplifierMutePin = -1;
+}
+
+// Set the verbosity level
+void setVerboseLevel(int level)
+{
+ gRTAudioVerbose = level;
+}
diff -r 000000000000 -r 8a575ba3ab52 core/Utilities.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/core/Utilities.cpp Fri Oct 31 19:10:17 2014 +0100
@@ -0,0 +1,31 @@
+/*
+ * Utilities.cpp
+ *
+ * Created on: Oct 27, 2014
+ * Author: parallels
+ */
+
+#include "../include/Utilities.h"
+
+// map()
+//
+// Scale an input value from one range to another. Works like its Wiring language equivalent.
+// x is the value to scale; in_min and in_max are the input range; out_min and out_max
+// are the output range.
+
+float map(float x, float in_min, float in_max, float out_min, float out_max)
+{
+ return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
+}
+
+// constrain()
+//
+// Clips an input value to be between two end points
+// x is the value to constrain; min_val and max_val are the range
+
+float constrain(float x, float min_val, float max_val)
+{
+ if(x < min_val) return min_val;
+ if(x > max_val) return max_val;
+ return x;
+}
diff -r 000000000000 -r 8a575ba3ab52 include/GPIOcontrol.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/include/GPIOcontrol.h Fri Oct 31 19:10:17 2014 +0100
@@ -0,0 +1,77 @@
+/*
+ * SimpleGPIO.h
+ *
+ * Copyright Derek Molloy, School of Electronic Engineering, Dublin City University
+ * www.derekmolloy.ie
+ *
+ * Based on Software by RidgeRun
+ * Copyright (c) 2011, RidgeRun
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the RidgeRun.
+ * 4. Neither the name of the RidgeRun nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY RIDGERUN ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL RIDGERUN BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef SIMPLEGPIO_H_
+#define SIMPLEGPIO_H_
+
+ /****************************************************************
+ * Constants
+ ****************************************************************/
+
+#define SYSFS_GPIO_DIR "/sys/class/gpio"
+#define SYSFS_LED_DIR "/sys/class/leds"
+#define POLL_TIMEOUT (3 * 1000) /* 3 seconds */
+#define MAX_BUF 128
+
+enum PIN_DIRECTION{
+ INPUT_PIN=0,
+ OUTPUT_PIN=1
+};
+
+enum PIN_VALUE{
+ LOW=0,
+ HIGH=1
+};
+
+/****************************************************************
+ * gpio_functions
+ ****************************************************************/
+int gpio_setup(unsigned int gpio, int out_flag);
+int gpio_export(unsigned int gpio);
+int gpio_unexport(unsigned int gpio);
+int gpio_set_dir(unsigned int gpio, int out_flag);
+int gpio_set_value(unsigned int gpio, int value);
+int gpio_get_value(unsigned int gpio, unsigned int *value);
+int gpio_set_edge(unsigned int gpio, char *edge);
+int gpio_fd_open(unsigned int gpio, int writeFlag);
+int gpio_fd_close(int fd);
+int gpio_write(int fd, int value);
+int gpio_read(int fd, unsigned int *value);
+int gpio_dismiss(int fd, unsigned int gpio);
+
+int led_set_trigger(unsigned int lednum, const char *trigger);
+
+#endif /* SIMPLEGPIO_H_ */
diff -r 000000000000 -r 8a575ba3ab52 include/I2c.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/include/I2c.h Fri Oct 31 19:10:17 2014 +0100
@@ -0,0 +1,87 @@
+/*
+ * I2c.h
+ *
+ * Created on: Oct 14, 2013
+ * Author: Victor Zappi
+ */
+
+#ifndef I2C_H_
+#define I2C_H_
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define MAX_BUF_NAME 64
+
+using namespace std;
+
+
+class I2c
+{
+
+protected:
+ int i2C_bus;
+ int i2C_address;
+ int i2C_file;
+
+public:
+ int initI2C_RW(int bus, int address, int file);
+ virtual int readI2C() = 0;
+ int closeI2C();
+
+ virtual ~I2c();
+
+};
+
+
+inline int I2c::initI2C_RW(int bus, int address, int fileHnd)
+{
+ i2C_bus = bus;
+ i2C_address = address;
+ i2C_file = fileHnd;
+
+ // open I2C device as a file
+ char namebuf[MAX_BUF_NAME];
+ snprintf(namebuf, sizeof(namebuf), "/dev/i2c-%d", i2C_bus);
+
+ if ((i2C_file = open(namebuf, O_RDWR)) < 0)
+ {
+ cout << "Failed to open " << namebuf << " I2C Bus" << endl;
+ return(1);
+ }
+
+ // target device as slave
+ if (ioctl(i2C_file, I2C_SLAVE, i2C_address) < 0){
+ cout << "I2C_SLAVE address " << i2C_address << " failed..." << endl;
+ return(2);
+ }
+
+ return 0;
+}
+
+
+
+inline int I2c::closeI2C()
+{
+ if(close(i2C_file)>0)
+ {
+ cout << "Failed to close file "<< i2C_file << endl;
+ return 1;
+ }
+ return 0;
+}
+
+
+inline I2c::~I2c(){}
+
+
+#endif /* I2C_H_ */
diff -r 000000000000 -r 8a575ba3ab52 include/I2c_Codec.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/include/I2c_Codec.h Fri Oct 31 19:10:17 2014 +0100
@@ -0,0 +1,51 @@
+/*
+ * I2c_Codec.h
+ *
+ * Handle writing the registers to the TLV320AIC310x
+ * series audio codecs, used on the BeagleBone Audio Cape.
+ * This code is designed to bypass the ALSA driver and
+ * configure the codec directly in a sensible way. It
+ * is complemented by code running on the PRU which uses
+ * the McASP serial port to transfer audio data.
+ *
+ * Created on: May 25, 2014
+ * Author: Andrew McPherson
+ */
+
+
+#ifndef I2CCODEC_H_
+#define I2CCODEC_H_
+
+#include "I2c.h"
+
+
+class I2c_Codec : public I2c
+{
+public:
+ int writeRegister(unsigned int reg, unsigned int value);
+
+ int initCodec();
+ int startAudio(int dual_rate);
+ int stopAudio();
+
+ int setDACVolume(int halfDbSteps);
+ int writeDACVolumeRegisters(bool mute);
+ int setADCVolume(int halfDbSteps);
+ int writeADCVolumeRegisters(bool mute);
+ int setHPVolume(int halfDbSteps);
+ int writeHPVolumeRegisters();
+
+ int readI2C();
+
+ I2c_Codec();
+ ~I2c_Codec();
+
+private:
+ bool running;
+ int dacVolumeHalfDbs;
+ int adcVolumeHalfDbs;
+ int hpVolumeHalfDbs;
+};
+
+
+#endif /* I2CCODEC_H_ */
diff -r 000000000000 -r 8a575ba3ab52 include/PRU.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/include/PRU.h Fri Oct 31 19:10:17 2014 +0100
@@ -0,0 +1,75 @@
+/*
+ * PRU.h
+ *
+ * Created on: May 27, 2014
+ * Author: andrewm
+ */
+
+#ifndef PRU_H_
+#define PRU_H_
+
+#include
+
+class PRU
+{
+private:
+ static const unsigned int kPruGPIODACSyncPin;
+ static const unsigned int kPruGPIOADCSyncPin;
+ static const unsigned int kPruGPIOTestPin;
+ static const unsigned int kPruGPIOTestPin2;
+ static const unsigned int kPruGPIOTestPin3;
+
+public:
+ // Constructor
+ PRU();
+
+ // Destructor
+ ~PRU();
+
+ // Prepare the GPIO pins needed for the PRU
+ int prepareGPIO(int use_spi, int include_test_pin, int include_led);
+
+ // Clean up the GPIO at the end
+ void cleanupGPIO();
+
+ // Initialise and open the PRU
+ int initialise(int pru_num, int frames_per_buffer, bool xenomai_test_pin = false);
+
+ // Run the code image in the specified file
+ int start(char * const filename);
+
+ // Loop: read and write data from the PRU
+ void loop();
+
+ // Wait for an interrupt from the PRU indicate it is finished
+ void waitForFinish();
+
+ // Turn off the PRU when done
+ void disable();
+
+ // For debugging:
+ void setGPIOTestPin();
+ void clearGPIOTestPin();
+
+private:
+ int pru_number; // Which PRU we use
+ bool running; // Whether the PRU is running
+ bool spi_enabled; // Whether SPI ADC and DAC are used
+ bool gpio_enabled; // Whether GPIO has been prepared
+ bool led_enabled; // Whether a user LED is enabled
+ bool gpio_test_pin_enabled; // Whether the test pin was also enabled
+
+ volatile uint32_t *pru_buffer_comm;
+ uint16_t *pru_buffer_spi_dac;
+ uint16_t *pru_buffer_spi_adc;
+ int16_t *pru_buffer_audio_dac;
+ int16_t *pru_buffer_audio_adc;
+ unsigned int spi_buffer_frames;
+ unsigned int audio_buffer_frames;
+
+ int xenomai_gpio_fd; // File descriptor for /dev/mem for fast GPIO
+ uint32_t *xenomai_gpio; // Pointer to GPIO registers
+};
+
+
+#endif /* PRU_H_ */
diff -r 000000000000 -r 8a575ba3ab52 include/RTAudio.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/include/RTAudio.h Fri Oct 31 19:10:17 2014 +0100
@@ -0,0 +1,49 @@
+/*
+ * RTAudio.h
+ *
+ * Central control code for hard real-time audio on BeagleBone Black
+ * using PRU and Xenomai Linux extensions. This code began as part
+ * of the Hackable Instruments project (EPSRC) at Queen Mary University
+ * of London, 2013-14.
+ *
+ * (c) 2014 Victor Zappi and Andrew McPherson
+ * Queen Mary University of London
+ */
+
+
+#ifndef RTAUDIO_H_
+#define RTAUDIO_H_
+
+#include "render.h"
+
+// Useful constants
+#define DBOX_CAPE // New custom cape
+
+#ifdef DBOX_CAPE
+#define CODEC_I2C_ADDRESS 0x18 // Address of TLV320AIC3104 codec
+#else
+#define CODEC_I2C_ADDRESS 0x1B // Address of TLV320AIC3106 codec
+#endif
+
+enum {
+ kAmplifierMutePin = 61 // P8-26 controls amplifier mute
+};
+
+typedef void* AuxiliaryTask; // Opaque data type to keep track of aux tasks
+
+// Flag that indicates when the audio will stop; can be read or
+// set by other components which should end at the same time as the audio
+extern bool gShouldStop;
+
+int initAudio(int periodSize, int useMatrix, void *userData,
+ int codecI2CAddress = CODEC_I2C_ADDRESS, int ampMutePin = kAmplifierMutePin);
+int startAudio();
+void stopAudio();
+void cleanupAudio();
+
+AuxiliaryTask createAuxiliaryTaskLoop(void (*functionToCall)(void), int priority, const char *name);
+void scheduleAuxiliaryTask(AuxiliaryTask task);
+
+void setVerboseLevel(int level);
+
+#endif /* RTAUDIO_H_ */
diff -r 000000000000 -r 8a575ba3ab52 include/Utilities.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/include/Utilities.h Fri Oct 31 19:10:17 2014 +0100
@@ -0,0 +1,14 @@
+/*
+ * Utilities.h
+ *
+ * Created on: Oct 27, 2014
+ * Author: parallels
+ */
+
+#ifndef UTILITIES_H_
+#define UTILITIES_H_
+
+float map(float x, float in_min, float in_max, float out_min, float out_max);
+float constrain(float x, float min_val, float max_val);
+
+#endif /* UTILITIES_H_ */
diff -r 000000000000 -r 8a575ba3ab52 include/pruss_intc_mapping.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/include/pruss_intc_mapping.h Fri Oct 31 19:10:17 2014 +0100
@@ -0,0 +1,104 @@
+/*
+ * pruss_intc_mapping.h
+ *
+ * Example PRUSS INTC mapping for the application
+ *
+ * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Texas Instruments Incorporated nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+*/
+
+/*
+ * ============================================================================
+ * Copyright (c) Texas Instruments Inc 2010-11
+ *
+ * Use of this software is controlled by the terms and conditions found in the
+ * license agreement under which this software has been supplied or provided.
+ * ============================================================================
+*/
+
+#define AM33XX
+#ifdef AM33XX
+#define PRU0_PRU1_INTERRUPT 17
+#define PRU1_PRU0_INTERRUPT 18
+#define PRU0_ARM_INTERRUPT 19
+#define PRU1_ARM_INTERRUPT 20
+#define ARM_PRU0_INTERRUPT 21
+#define ARM_PRU1_INTERRUPT 22
+#else
+#define PRU0_PRU1_INTERRUPT 32
+#define PRU1_PRU0_INTERRUPT 33
+#define PRU0_ARM_INTERRUPT 34
+#define PRU1_ARM_INTERRUPT 35
+#define ARM_PRU0_INTERRUPT 36
+#define ARM_PRU1_INTERRUPT 37
+#endif
+#define CHANNEL0 0
+#define CHANNEL1 1
+#define CHANNEL2 2
+#define CHANNEL3 3
+#define CHANNEL4 4
+#define CHANNEL5 5
+#define CHANNEL6 6
+#define CHANNEL7 7
+#define CHANNEL8 8
+#define CHANNEL9 9
+
+#define PRU0 0
+#define PRU1 1
+#define PRU_EVTOUT0 2
+#define PRU_EVTOUT1 3
+#define PRU_EVTOUT2 4
+#define PRU_EVTOUT3 5
+#define PRU_EVTOUT4 6
+#define PRU_EVTOUT5 7
+#define PRU_EVTOUT6 8
+#define PRU_EVTOUT7 9
+
+#define PRU0_HOSTEN_MASK 0x0001
+#define PRU1_HOSTEN_MASK 0x0002
+#define PRU_EVTOUT0_HOSTEN_MASK 0x0004
+#define PRU_EVTOUT1_HOSTEN_MASK 0x0008
+#define PRU_EVTOUT2_HOSTEN_MASK 0x0010
+#define PRU_EVTOUT3_HOSTEN_MASK 0x0020
+#define PRU_EVTOUT4_HOSTEN_MASK 0x0040
+#define PRU_EVTOUT5_HOSTEN_MASK 0x0080
+#define PRU_EVTOUT6_HOSTEN_MASK 0x0100
+#define PRU_EVTOUT7_HOSTEN_MASK 0x0200
+
+
+#define PRUSS_INTC_INITDATA { \
+{ PRU0_PRU1_INTERRUPT, PRU1_PRU0_INTERRUPT, PRU0_ARM_INTERRUPT, PRU1_ARM_INTERRUPT, ARM_PRU0_INTERRUPT, ARM_PRU1_INTERRUPT, -1 }, \
+{ {PRU0_PRU1_INTERRUPT,CHANNEL1}, {PRU1_PRU0_INTERRUPT, CHANNEL0}, {PRU0_ARM_INTERRUPT,CHANNEL2}, {PRU1_ARM_INTERRUPT, CHANNEL3}, {ARM_PRU0_INTERRUPT, CHANNEL0}, {ARM_PRU1_INTERRUPT, CHANNEL1}, {-1,-1}}, \
+ { {CHANNEL0,PRU0}, {CHANNEL1, PRU1}, {CHANNEL2, PRU_EVTOUT0}, {CHANNEL3, PRU_EVTOUT1}, {-1,-1} }, \
+ (PRU0_HOSTEN_MASK | PRU1_HOSTEN_MASK | PRU_EVTOUT0_HOSTEN_MASK | PRU_EVTOUT1_HOSTEN_MASK) /*Enable PRU0, PRU1, PRU_EVTOUT0 */ \
+} \
+
diff -r 000000000000 -r 8a575ba3ab52 include/prussdrv.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/include/prussdrv.h Fri Oct 31 19:10:17 2014 +0100
@@ -0,0 +1,160 @@
+/*
+ * prussdrv.h
+ *
+ * Describes PRUSS userspace driver for Industrial Communications
+ *
+ * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Texas Instruments Incorporated nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+*/
+
+/*
+ * ============================================================================
+ * Copyright (c) Texas Instruments Inc 2010-11
+ *
+ * Use of this software is controlled by the terms and conditions found in the
+ * license agreement under which this software has been supplied or provided.
+ * ============================================================================
+*/
+
+#ifndef _PRUSSDRV_H
+#define _PRUSSDRV_H
+
+#include
+#include
+
+#if defined (__cplusplus)
+extern "C" {
+#endif
+
+#define NUM_PRU_HOSTIRQS 8
+#define NUM_PRU_HOSTS 10
+#define NUM_PRU_CHANNELS 10
+#define NUM_PRU_SYS_EVTS 64
+
+#define PRUSS0_PRU0_DATARAM 0
+#define PRUSS0_PRU1_DATARAM 1
+#define PRUSS0_PRU0_IRAM 2
+#define PRUSS0_PRU1_IRAM 3
+
+//Available in AM33xx series - begin
+#define PRUSS0_SHARED_DATARAM 4
+#define PRUSS0_CFG 5
+#define PRUSS0_UART 6
+#define PRUSS0_IEP 7
+#define PRUSS0_ECAP 8
+#define PRUSS0_MII_RT 9
+#define PRUSS0_MDIO 10
+//Available in AM33xx series - end
+
+#define PRU_EVTOUT_0 0
+#define PRU_EVTOUT_1 1
+#define PRU_EVTOUT_2 2
+#define PRU_EVTOUT_3 3
+#define PRU_EVTOUT_4 4
+#define PRU_EVTOUT_5 5
+#define PRU_EVTOUT_6 6
+#define PRU_EVTOUT_7 7
+
+ typedef void *(*prussdrv_function_handler) (void *);
+ typedef struct __sysevt_to_channel_map {
+ short sysevt;
+ short channel;
+ } tsysevt_to_channel_map;
+ typedef struct __channel_to_host_map {
+ short channel;
+ short host;
+ } tchannel_to_host_map;
+ typedef struct __pruss_intc_initdata {
+ //Enabled SYSEVTs - Range:0..63
+ //{-1} indicates end of list
+ char sysevts_enabled[NUM_PRU_SYS_EVTS];
+ //SysEvt to Channel map. SYSEVTs - Range:0..63 Channels -Range: 0..9
+ //{-1, -1} indicates end of list
+ tsysevt_to_channel_map sysevt_to_channel_map[NUM_PRU_SYS_EVTS];
+ //Channel to Host map.Channels -Range: 0..9 HOSTs - Range:0..9
+ //{-1, -1} indicates end of list
+ tchannel_to_host_map channel_to_host_map[NUM_PRU_CHANNELS];
+ //10-bit mask - Enable Host0-Host9 {Host0/1:PRU0/1, Host2..9 : PRUEVT_OUT0..7)
+ unsigned int host_enable_bitmask;
+ } tpruss_intc_initdata;
+
+ int prussdrv_init(void);
+
+ int prussdrv_open(unsigned int pru_evtout_num);
+
+ int prussdrv_pru_reset(unsigned int prunum);
+
+ int prussdrv_pru_disable(unsigned int prunum);
+
+ int prussdrv_pru_enable(unsigned int prunum);
+
+ int prussdrv_pru_write_memory(unsigned int pru_ram_id,
+ unsigned int wordoffset,
+ unsigned int *memarea,
+ unsigned int bytelength);
+
+ int prussdrv_pruintc_init(tpruss_intc_initdata * prussintc_init_data);
+
+ int prussdrv_map_l3mem(void **address);
+
+ int prussdrv_map_extmem(void **address);
+
+ int prussdrv_map_prumem(unsigned int pru_ram_id, void **address);
+
+ int prussdrv_map_peripheral_io(unsigned int per_id, void **address);
+
+ unsigned int prussdrv_get_phys_addr(void *address);
+
+ void *prussdrv_get_virt_addr(unsigned int phyaddr);
+
+ int prussdrv_pru_wait_event(unsigned int pru_evtout_num);
+
+ int prussdrv_pru_send_event(unsigned int eventnum);
+
+ int prussdrv_pru_clear_event(unsigned int eventnum);
+
+ int prussdrv_pru_send_wait_clear_event(unsigned int send_eventnum,
+ unsigned int pru_evtout_num,
+ unsigned int ack_eventnum);
+
+ int prussdrv_exit(void);
+
+ int prussdrv_exec_program(int prunum, char *filename);
+
+ int prussdrv_start_irqthread(unsigned int pru_evtout_num, int priority,
+ prussdrv_function_handler irqhandler);
+
+
+#if defined (__cplusplus)
+}
+#endif
+#endif
diff -r 000000000000 -r 8a575ba3ab52 include/render.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/include/render.h Fri Oct 31 19:10:17 2014 +0100
@@ -0,0 +1,53 @@
+/*
+ * render.h
+ *
+ * Created on: May 28, 2014
+ * Author: Victor Zappi
+ */
+
+#ifndef RENDER_H_
+#define RENDER_H_
+
+// uint types
+#include
+
+// Mappings from pin numbers on PCB to actual DAC channels
+// This gives the DAC and ADC connectors the same effective pinout
+#define DAC_PIN0 6
+#define DAC_PIN1 4
+#define DAC_PIN2 2
+#define DAC_PIN3 0
+#define DAC_PIN4 1
+#define DAC_PIN5 3
+#define DAC_PIN6 5
+#define DAC_PIN7 7
+
+#define ADC_PIN0 0
+#define ADC_PIN1 1
+#define ADC_PIN2 2
+#define ADC_PIN3 3
+#define ADC_PIN4 4
+#define ADC_PIN5 5
+#define ADC_PIN6 6
+#define ADC_PIN7 7
+
+#define MATRIX_MAX 65535.0
+
+bool initialise_render(int numChannels, int numMatrixFramesPerPeriod,
+ int numAudioFramesPerPeriod,
+ float matrixSampleRate, float audioSampleRate,
+ void *userData);
+
+void render(int numMatrixFrames, int numAudioFrames, float *audioIn, float *audioOut,
+ uint16_t *matrixIn, uint16_t *matrixOut);
+
+void render_medium_prio();
+void render_low_prio();
+
+void schedule_render_medium_prio();
+void schedule_render_low_prio();
+
+
+void cleanup_render();
+
+#endif /* RENDER_H_ */
diff -r 000000000000 -r 8a575ba3ab52 libNE10.a
Binary file libNE10.a has changed
diff -r 000000000000 -r 8a575ba3ab52 libprussdrv.a
Binary file libprussdrv.a has changed
diff -r 000000000000 -r 8a575ba3ab52 projects/basic/main.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/projects/basic/main.cpp Fri Oct 31 19:10:17 2014 +0100
@@ -0,0 +1,114 @@
+/*
+ * main.cpp
+ *
+ * Created on: Oct 24, 2014
+ * Author: parallels
+ */
+
+#include
+#include
+#include
+#include
+#include "../../include/RTAudio.h"
+
+using namespace std;
+
+// Handle Ctrl-C by requesting that the audio rendering stop
+void interrupt_handler(int var)
+{
+ gShouldStop = true;
+}
+
+// Print usage information
+void usage(const char * processName)
+{
+ cerr << "Usage: " << processName << " [-h] [-v] [-p period] [-f frequency]" << endl;
+ cerr << " -h: Print this menu\n";
+ cerr << " -v: Enable verbose messages\n";
+ cerr << " -p period: Set the period (hardware buffer) size in sensor frames\n";
+ cerr << " -f frequency: Set the frequency of the oscillator\n";
+ cerr << " -m: Enable the matrix (ADC and DAC) as well as audio\n";
+}
+
+int main(int argc, char *argv[])
+{
+ int periodSize = 8; // Period size in sensor frames
+ int verbose = 0; // Verbose printing level
+ float frequency = 440.0; // Frequency of oscillator
+ int useMatrix = 0; // Whether to use the matrix or just audio
+
+ // Parse command-line arguments
+ while (1) {
+ int c;
+ if ((c = getopt(argc, argv, "hp:vf:m")) < 0)
+ break;
+ switch (c) {
+ case 'h':
+ usage(basename(argv[0]));
+ exit(0);
+ case 'p':
+ periodSize = atoi(optarg);
+ if(periodSize < 1)
+ periodSize = 1;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ case 'f':
+ frequency = atof(optarg);
+ break;
+ case 'm':
+ useMatrix = 1;
+ break;
+ case '?':
+ default:
+ usage(basename(argv[0]));
+ exit(1);
+ }
+ }
+
+
+ // Set verbose logging information (optional by using value > 0; default is 0)
+ setVerboseLevel(verbose);
+
+ if(verbose) {
+ cout << "Starting with period size " << periodSize << " and frequency " << frequency << endl;
+ if(useMatrix)
+ cout << "Matrix enabled\n";
+ else
+ cout << "Matrix disabled\n";
+ }
+
+ // Initialise the PRU audio device
+ if(initAudio(periodSize, useMatrix, &frequency) != 0) {
+ cout << "Error: unable to initialise audio" << endl;
+ return -1;
+ }
+
+ // Start the audio device running
+ if(startAudio()) {
+ cout << "Error: unable to start real-time audio" << endl;
+ return -1;
+ }
+
+ // Set up interrupt handler to catch Control-C
+ signal(SIGINT, interrupt_handler);
+
+ // Run until told to stop
+ while(!gShouldStop) {
+ usleep(100000);
+ }
+
+ // Stop the audio device
+ stopAudio();
+
+ if(verbose) {
+ cout << "Cleaning up..." << endl;
+ }
+
+ // Clean up any resources allocated for audio
+ cleanupAudio();
+
+ // All done!
+ return 0;
+}
diff -r 000000000000 -r 8a575ba3ab52 projects/basic/render.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/projects/basic/render.cpp Fri Oct 31 19:10:17 2014 +0100
@@ -0,0 +1,65 @@
+/*
+ * render.cpp
+ *
+ * Created on: Oct 24, 2014
+ * Author: parallels
+ */
+
+
+#include "../../include/render.h"
+#include
+
+float gFrequency;
+float gPhase;
+float gInverseSampleRate;
+int gNumChannels;
+
+// initialise_render() is called once before the audio rendering starts.
+// Use it to perform any initialisation and allocation which is dependent
+// on the period size or sample rate.
+//
+// userData holds an opaque pointer to a data structure that was passed
+// in from the call to initAudio().
+//
+// Return true on success; returning false halts the program.
+
+bool initialise_render(int numChannels, int numMatrixFramesPerPeriod,
+ int numAudioFramesPerPeriod, float matrixSampleRate,
+ float audioSampleRate, void *userData)
+{
+ // Retrieve a parameter passed in from the initAudio() call
+ gFrequency = *(float *)userData;
+
+ gNumChannels = numChannels;
+ gInverseSampleRate = 1.0 / audioSampleRate;
+ gPhase = 0.0;
+
+ return true;
+}
+
+// render() is called regularly at the highest priority by the audio engine.
+// Input and output are given from the audio hardware and the other
+// ADCs and DACs (if available). If only audio is available, numMatrixFrames
+// will be 0.
+
+void render(int numMatrixFrames, int numAudioFrames, float *audioIn, float *audioOut,
+ uint16_t *matrixIn, uint16_t *matrixOut)
+{
+ for(int n = 0; n < numAudioFrames; n++) {
+ float out = 0.8f * sinf(gPhase);
+ gPhase += 2.0 * M_PI * gFrequency * gInverseSampleRate;
+ if(gPhase > 2.0 * M_PI)
+ gPhase -= 2.0 * M_PI;
+
+ for(int channel = 0; channel < gNumChannels; channel++)
+ audioOut[n * gNumChannels + channel] = out;
+ }
+}
+
+// cleanup_render() is called once at the end, after the audio has stopped.
+// Release any resources that were allocated in initialise_render().
+
+void cleanup_render()
+{
+
+}
diff -r 000000000000 -r 8a575ba3ab52 projects/basic_analog_output/main.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/projects/basic_analog_output/main.cpp Fri Oct 31 19:10:17 2014 +0100
@@ -0,0 +1,109 @@
+/*
+ * main.cpp
+ *
+ * Created on: Oct 24, 2014
+ * Author: parallels
+ */
+
+#include
+#include
+#include
+#include
+#include "../../include/RTAudio.h"
+
+using namespace std;
+
+// Handle Ctrl-C by requesting that the audio rendering stop
+void interrupt_handler(int var)
+{
+ gShouldStop = true;
+}
+
+// Print usage information
+void usage(const char * processName)
+{
+ cerr << "Usage: " << processName << " [-h] [-v] [-p period] [-f input] [-a input]" << endl;
+ cerr << " -h: Print this menu\n";
+ cerr << " -v: Enable verbose messages\n";
+ cerr << " -p period: Set the period (hardware buffer) size in sensor frames\n";
+ cerr << " -f frequency: Set frequency of LED fade (default: 1.0)\n";
+}
+
+int main(int argc, char *argv[])
+{
+ int periodSize = 8; // Period size in sensor frames
+ int verbose = 0; // Verbose printing level
+ float frequency = 1.0; // Frequency of LED fades
+
+ // Parse command-line arguments
+ while (1) {
+ int c;
+ if ((c = getopt(argc, argv, "hp:vf:")) < 0)
+ break;
+ switch (c) {
+ case 'h':
+ usage(basename(argv[0]));
+ exit(0);
+ case 'p':
+ periodSize = atoi(optarg);
+ if(periodSize < 1)
+ periodSize = 1;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ case 'f':
+ frequency = atof(optarg);
+ if(frequency < 0)
+ frequency = 0;
+ if(frequency > 11025.0)
+ frequency = 11025.0;
+ break;
+ case '?':
+ default:
+ usage(basename(argv[0]));
+ exit(1);
+ }
+ }
+
+
+ // Set verbose logging information (optional by using value > 0; default is 0)
+ setVerboseLevel(verbose);
+
+ if(verbose) {
+ cout << "Starting with period size " << periodSize << " and frequency " << frequency << endl;
+ }
+
+ // Initialise the PRU audio device
+ if(initAudio(periodSize, 1, &frequency) != 0) {
+ cout << "Error: unable to initialise audio" << endl;
+ return -1;
+ }
+
+ // Start the audio device running
+ if(startAudio()) {
+ cout << "Error: unable to start real-time audio" << endl;
+ return -1;
+ }
+
+ // Set up interrupt handler to catch Control-C
+ signal(SIGINT, interrupt_handler);
+
+ // Run until told to stop
+ while(!gShouldStop) {
+ usleep(100000);
+ }
+
+ // Stop the audio device
+ stopAudio();
+
+ if(verbose) {
+ cout << "Cleaning up..." << endl;
+ }
+
+ // Clean up any resources allocated for audio
+ cleanupAudio();
+
+ // All done!
+ return 0;
+}
diff -r 000000000000 -r 8a575ba3ab52 projects/basic_analog_output/render.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/projects/basic_analog_output/render.cpp Fri Oct 31 19:10:17 2014 +0100
@@ -0,0 +1,83 @@
+/*
+ * render.cpp
+ *
+ * Created on: Oct 24, 2014
+ * Author: parallels
+ */
+
+
+#include "../../include/render.h"
+#include "../../include/Utilities.h"
+#include
+#include
+
+// Set range for analog outputs designed for driving LEDs
+const float kMinimumAmplitude = (1.5 / 5.0) * MATRIX_MAX;
+const float kAmplitudeRange = MATRIX_MAX - kMinimumAmplitude;
+
+float gFrequency;
+float gPhase;
+float gInverseSampleRate;
+
+// initialise_render() is called once before the audio rendering starts.
+// Use it to perform any initialisation and allocation which is dependent
+// on the period size or sample rate.
+//
+// userData holds an opaque pointer to a data structure that was passed
+// in from the call to initAudio().
+//
+// Return true on success; returning false halts the program.
+
+bool initialise_render(int numChannels, int numMatrixFramesPerPeriod,
+ int numAudioFramesPerPeriod, float matrixSampleRate,
+ float audioSampleRate, void *userData)
+{
+ // Retrieve a parameter passed in from the initAudio() call
+ gFrequency = *(float *)userData;
+
+ if(numMatrixFramesPerPeriod*2 != numAudioFramesPerPeriod) {
+ rt_printf("Error: this example needs the matrix enabled, running at half audio rate\n");
+ return false;
+ }
+
+ gInverseSampleRate = 1.0 / audioSampleRate;
+ gPhase = 0.0;
+
+ return true;
+}
+
+// render() is called regularly at the highest priority by the audio engine.
+// Input and output are given from the audio hardware and the other
+// ADCs and DACs (if available). If only audio is available, numMatrixFrames
+// will be 0.
+
+void render(int numMatrixFrames, int numAudioFrames, float *audioIn, float *audioOut,
+ uint16_t *matrixIn, uint16_t *matrixOut)
+{
+ for(int n = 0; n < numMatrixFrames; n++) {
+ // Set LED to different phase for each matrix channel
+ float relativePhase = 0.0;
+ for(int channel = 0; channel < 8; channel++) {
+ float out = kMinimumAmplitude + kAmplitudeRange * 0.5f * (1.0f + sinf(gPhase + relativePhase));
+ if(out > MATRIX_MAX)
+ out = MATRIX_MAX;
+
+ matrixOut[n * 8 + channel] = (uint16_t)out;
+
+ // Advance by pi/4 (1/8 of a full rotation) for each channel
+ relativePhase += M_PI * 0.25;
+ }
+
+ gPhase += 2.0 * M_PI * gFrequency * gInverseSampleRate;
+ if(gPhase > 2.0 * M_PI)
+ gPhase -= 2.0 * M_PI;
+ }
+}
+
+// cleanup_render() is called once at the end, after the audio has stopped.
+// Release any resources that were allocated in initialise_render().
+
+void cleanup_render()
+{
+
+}
diff -r 000000000000 -r 8a575ba3ab52 projects/basic_sensor/main.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/projects/basic_sensor/main.cpp Fri Oct 31 19:10:17 2014 +0100
@@ -0,0 +1,121 @@
+/*
+ * main.cpp
+ *
+ * Created on: Oct 24, 2014
+ * Author: parallels
+ */
+
+#include
+#include
+#include
+#include
+#include "../../include/RTAudio.h"
+
+using namespace std;
+
+int gSensorInputFrequency = 0;
+int gSensorInputAmplitude = 1;
+
+// Handle Ctrl-C by requesting that the audio rendering stop
+void interrupt_handler(int var)
+{
+ gShouldStop = true;
+}
+
+// Print usage information
+void usage(const char * processName)
+{
+ cerr << "Usage: " << processName << " [-h] [-v] [-p period] [-f input] [-a input]" << endl;
+ cerr << " -h: Print this menu\n";
+ cerr << " -v: Enable verbose messages\n";
+ cerr << " -p period: Set the period (hardware buffer) size in sensor frames\n";
+ cerr << " -f input: Choose the analog input controlling frequency (0-7; default 0)\n";
+ cerr << " -a input: Choose the analog input controlling amplitude (0-7; default 1)\n";
+}
+
+int main(int argc, char *argv[])
+{
+ int periodSize = 8; // Period size in sensor frames
+ int verbose = 0; // Verbose printing level
+
+ // Parse command-line arguments
+ while (1) {
+ int c;
+ if ((c = getopt(argc, argv, "hp:vf:a:")) < 0)
+ break;
+ switch (c) {
+ case 'h':
+ usage(basename(argv[0]));
+ exit(0);
+ case 'p':
+ periodSize = atoi(optarg);
+ if(periodSize < 1)
+ periodSize = 1;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ case 'f':
+ gSensorInputFrequency = atoi(optarg);
+ if(gSensorInputFrequency < 0 || gSensorInputFrequency > 7) {
+ usage(basename(argv[0]));
+ exit(0);
+ }
+ break;
+ case 'a':
+ gSensorInputAmplitude = atoi(optarg);
+ if(gSensorInputAmplitude < 0 || gSensorInputAmplitude > 7) {
+ usage(basename(argv[0]));
+ exit(0);
+ }
+ break;
+ case '?':
+ default:
+ usage(basename(argv[0]));
+ exit(1);
+ }
+ }
+
+
+ // Set verbose logging information (optional by using value > 0; default is 0)
+ setVerboseLevel(verbose);
+
+ if(verbose) {
+ cout << "Starting with period size " << periodSize << endl;
+ cout << "--> Frequency on input " << gSensorInputFrequency << endl;
+ cout << "--> Amplitude on input " << gSensorInputAmplitude << endl;
+ }
+
+ // Initialise the PRU audio device
+ if(initAudio(periodSize, 1, 0) != 0) {
+ cout << "Error: unable to initialise audio" << endl;
+ return -1;
+ }
+
+ // Start the audio device running
+ if(startAudio()) {
+ cout << "Error: unable to start real-time audio" << endl;
+ return -1;
+ }
+
+ // Set up interrupt handler to catch Control-C
+ signal(SIGINT, interrupt_handler);
+
+ // Run until told to stop
+ while(!gShouldStop) {
+ usleep(100000);
+ }
+
+ // Stop the audio device
+ stopAudio();
+
+ if(verbose) {
+ cout << "Cleaning up..." << endl;
+ }
+
+ // Clean up any resources allocated for audio
+ cleanupAudio();
+
+ // All done!
+ return 0;
+}
diff -r 000000000000 -r 8a575ba3ab52 projects/basic_sensor/render.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/projects/basic_sensor/render.cpp Fri Oct 31 19:10:17 2014 +0100
@@ -0,0 +1,88 @@
+/*
+ * render.cpp
+ *
+ * Created on: Oct 24, 2014
+ * Author: parallels
+ */
+
+
+#include "../../include/render.h"
+#include "../../include/Utilities.h"
+#include
+#include
+
+float gPhase;
+float gInverseSampleRate;
+int gNumChannels;
+
+// These settings are carried over from main.cpp
+// Setting global variables is an alternative approach
+// to passing a structure to userData in initialise_render()
+
+extern int gSensorInputFrequency;
+extern int gSensorInputAmplitude;
+
+// initialise_render() is called once before the audio rendering starts.
+// Use it to perform any initialisation and allocation which is dependent
+// on the period size or sample rate.
+//
+// userData holds an opaque pointer to a data structure that was passed
+// in from the call to initAudio().
+//
+// Return true on success; returning false halts the program.
+
+bool initialise_render(int numChannels, int numMatrixFramesPerPeriod,
+ int numAudioFramesPerPeriod, float matrixSampleRate,
+ float audioSampleRate, void *userData)
+{
+ if(numMatrixFramesPerPeriod*2 != numAudioFramesPerPeriod) {
+ rt_printf("Error: this example needs the matrix enabled, running at half audio rate\n");
+ return false;
+ }
+
+ gNumChannels = numChannels;
+ gInverseSampleRate = 1.0 / audioSampleRate;
+ gPhase = 0.0;
+
+ return true;
+}
+
+// render() is called regularly at the highest priority by the audio engine.
+// Input and output are given from the audio hardware and the other
+// ADCs and DACs (if available). If only audio is available, numMatrixFrames
+// will be 0.
+
+void render(int numMatrixFrames, int numAudioFrames, float *audioIn, float *audioOut,
+ uint16_t *matrixIn, uint16_t *matrixOut)
+{
+ float frequency = 0;
+ float amplitude = 0;
+
+ // There are twice as many audio frames as matrix frames since audio sample rate
+ // is twice as high
+
+ for(int n = 0; n < numAudioFrames; n++) {
+ if(!(n % 2)) {
+ // Even audio samples: update frequency and amplitude from the matrix
+ frequency = map((float)matrixIn[gSensorInputFrequency], 0, MATRIX_MAX, 100, 1000);
+ amplitude = (float)matrixIn[gSensorInputAmplitude] / MATRIX_MAX;
+ }
+
+ float out = amplitude * sinf(gPhase);
+
+ for(int channel = 0; channel < gNumChannels; channel++)
+ audioOut[n * gNumChannels + channel] = out;
+
+ gPhase += 2.0 * M_PI * frequency * gInverseSampleRate;
+ if(gPhase > 2.0 * M_PI)
+ gPhase -= 2.0 * M_PI;
+ }
+}
+
+// cleanup_render() is called once at the end, after the audio has stopped.
+// Release any resources that were allocated in initialise_render().
+
+void cleanup_render()
+{
+
+}
diff -r 000000000000 -r 8a575ba3ab52 projects/d-box/ADSR.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/projects/d-box/ADSR.cpp Fri Oct 31 19:10:17 2014 +0100
@@ -0,0 +1,76 @@
+//
+// ADSR.cpp
+//
+// Created by Nigel Redmon on 12/18/12.
+// EarLevel Engineering: earlevel.com
+// Copyright 2012 Nigel Redmon
+//
+// For a complete explanation of the ADSR envelope generator and code,
+// read the series of articles by the author, starting here:
+// http://www.earlevel.com/main/2013/06/01/envelope-generators/
+//
+// License:
+//
+// This source code is provided as is, without warranty.
+// You may copy and distribute verbatim copies of this document.
+// You may modify and use this source code to create binary code for your own purposes, free or commercial.
+//
+
+#include "ADSR.h"
+#include
+
+
+ADSR::ADSR(void) {
+ reset();
+ setAttackRate(0);
+ setDecayRate(0);
+ setReleaseRate(0);
+ setSustainLevel(1.0);
+ setTargetRatioA(0.3);
+ setTargetRatioDR(0.0001);
+}
+
+ADSR::~ADSR(void) {
+}
+
+void ADSR::setAttackRate(float rate) {
+ attackRate = rate;
+ attackCoef = calcCoef(rate, targetRatioA);
+ attackBase = (1.0 + targetRatioA) * (1.0 - attackCoef);
+}
+
+void ADSR::setDecayRate(float rate) {
+ decayRate = rate;
+ decayCoef = calcCoef(rate, targetRatioDR);
+ decayBase = (sustainLevel - targetRatioDR) * (1.0 - decayCoef);
+}
+
+void ADSR::setReleaseRate(float rate) {
+ releaseRate = rate;
+ releaseCoef = calcCoef(rate, targetRatioDR);
+ releaseBase = -targetRatioDR * (1.0 - releaseCoef);
+}
+
+float ADSR::calcCoef(float rate, float targetRatio) {
+ return exp(-log((1.0 + targetRatio) / targetRatio) / rate);
+}
+
+void ADSR::setSustainLevel(float level) {
+ sustainLevel = level;
+ decayBase = (sustainLevel - targetRatioDR) * (1.0 - decayCoef);
+}
+
+void ADSR::setTargetRatioA(float targetRatio) {
+ if (targetRatio < 0.000000001)
+ targetRatio = 0.000000001; // -180 dB
+ targetRatioA = targetRatio;
+ attackBase = (1.0 + targetRatioA) * (1.0 - attackCoef);
+}
+
+void ADSR::setTargetRatioDR(float targetRatio) {
+ if (targetRatio < 0.000000001)
+ targetRatio = 0.000000001; // -180 dB
+ targetRatioDR = targetRatio;
+ decayBase = (sustainLevel - targetRatioDR) * (1.0 - decayCoef);
+ releaseBase = -targetRatioDR * (1.0 - releaseCoef);
+}
diff -r 000000000000 -r 8a575ba3ab52 projects/d-box/ADSR.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/projects/d-box/ADSR.h Fri Oct 31 19:10:17 2014 +0100
@@ -0,0 +1,137 @@
+//
+// ADRS.h
+//
+// Created by Nigel Redmon on 12/18/12.
+// EarLevel Engineering: earlevel.com
+// Copyright 2012 Nigel Redmon
+//
+// For a complete explanation of the ADSR envelope generator and code,
+// read the series of articles by the author, starting here:
+// http://www.earlevel.com/main/2013/06/01/envelope-generators/
+//
+// License:
+//
+// This source code is provided as is, without warranty.
+// You may copy and distribute verbatim copies of this document.
+// You may modify and use this source code to create binary code for your own purposes, free or commercial.
+//
+
+#ifndef ADRS_h
+#define ADRS_h
+
+#include
+#include
+
+using namespace std;
+
+enum envState {
+ env_idle = 0,
+ env_attack,
+ env_decay,
+ env_sustain,
+ env_release
+};
+
+class ADSR {
+public:
+ ADSR(void);
+ ~ADSR(void);
+ float process(void);
+ float process(int sampleCount);
+ float getOutput(void);
+ int getState(void);
+ void gate(int on);
+ void setAttackRate(float rate);
+ void setDecayRate(float rate);
+ void setReleaseRate(float rate);
+ void setSustainLevel(float level);
+ void setTargetRatioA(float targetRatio);
+ void setTargetRatioDR(float targetRatio);
+ void reset(void);
+
+protected:
+ int state;
+ float output;
+ float attackRate;
+ float decayRate;
+ float releaseRate;
+ float attackCoef;
+ float decayCoef;
+ float releaseCoef;
+ float sustainLevel;
+ float targetRatioA;
+ float targetRatioDR;
+ float attackBase;
+ float decayBase;
+ float releaseBase;
+ string name;
+ float calcCoef(float rate, float targetRatio);
+};
+
+inline float ADSR::process() {
+ switch (state) {
+ case env_idle:
+ break;
+ case env_attack:
+ output = attackBase + output * attackCoef;
+ if (output >= 1.0) {
+ output = 1.0;
+ state = env_decay;
+ }
+ break;
+ case env_decay:
+ output = decayBase + output * decayCoef;
+ if (output <= sustainLevel) {
+ output = sustainLevel;
+ state = env_sustain;
+ }
+ break;
+ case env_sustain:
+ break;
+ case env_release:
+ output = releaseBase + output * releaseCoef;
+ if (output <= 0.0) {
+ output = 0.0;
+ state = env_idle;
+ }
+ break;
+ }
+ return output;
+}
+
+inline float ADSR::process(int sampleCount)
+{
+ float retVal = 0;
+
+ if(state != env_idle)
+ {
+ for(int i=0; i /sys/devices/bone_capemgr.*/slots
+
+ // we have to look for the semi-random number the BBB has initialized the bone_capemgr with [value of *]
+ // to reach /slots and set cape-bone-iio.
+ // to do so, we use glob lib, which translates wildcards [*] into all the values found in paths
+
+
+ glob( startPath.c_str(), 0, NULL, &globbuf);
+
+ if(globbuf.gl_pathc >0)
+ {
+ if (globbuf.gl_pathc == 1 )
+ {
+ activateAnalogPath = globbuf.gl_pathv[0];
+
+ // check if file is existing
+ if((ActivateAnalogHnd = fopen(activateAnalogPath.c_str(), "r+")) != NULL)
+ {
+ // we found that current capemgr num
+
+ fwrite("cape-bone-iio", sizeof(char), 13, ActivateAnalogHnd); // activate pins
+
+ analogIsSet = true;
+
+ if(verbose)
+ cout << "Analog Pins activated via cape-bone-iio at path " << activateAnalogPath << endl;
+
+ fclose(ActivateAnalogHnd); // close file
+ }
+ }
+ //else
+ //printf("toomany", );
+ }
+
+ globfree(&globbuf); // self freeing
+
+
+ if(!analogIsSet)
+ {
+ cout << "cannot find bone_capemgr" << endl;
+ cout << "------Init failed------" << endl;
+ return 1;
+ }
+
+
+ // build read path
+ startPath = "/sys/devices/ocp.2/helper.*";
+
+ glob( startPath.c_str(), 0, NULL, &globbuf);
+
+ if(globbuf.gl_pathc >0)
+ {
+ if (globbuf.gl_pathc == 1 )
+ {
+ analogInPath = globbuf.gl_pathv[0] + (string)"/AIN";
+ }
+ else
+ cout << "Too many analog inputs with this name! [I am puzzled...]" << endl;
+ }
+ else
+ cout << "Cannot find analog input dir...puzzled" << endl;
+
+
+ return 0;
+}
+
+
+int AnalogInput::read(int index)
+{
+ // convert int index into string
+ stringstream ss;
+ ss << index;
+
+ readPath = analogInPath + ss.str(); // create pin0 file path
+
+
+ // check if file is existing
+ if((AnalogInHnd = fopen(readPath.c_str(), "rb")) != NULL)
+ {
+ // we found that current helper num
+
+ // prepare read buffer to reading
+ fseek (AnalogInHnd , 0 , SEEK_END);
+ lSize = ftell (AnalogInHnd);
+ rewind (AnalogInHnd);
+
+ result = fread (buffer, 1, lSize, AnalogInHnd);
+
+ fclose(AnalogInHnd); // close file
+
+ helperNumFound = true;
+
+ //cout << "Analog Pins can be read at path " << analogInPath << endl;
+ //cout << "Test reading of Pin0 gives: " << buffer << endl;
+ }
+
+ if(!helperNumFound)
+ {
+ cout << "cannot find helper" << endl;
+ cout << "------Analog Read failed------" << endl;
+ return -1;
+ }
+
+ return atoi(buffer);
+
+}
+
+
+
diff -r 000000000000 -r 8a575ba3ab52 projects/d-box/AnalogInput.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/projects/d-box/AnalogInput.h Fri Oct 31 19:10:17 2014 +0100
@@ -0,0 +1,55 @@
+/*
+ * AnalogInput.h
+ *
+ * Created on: Oct 17, 2013
+ * Author: Victor Zappi
+ */
+
+#ifndef ANALOGINPUT_H_
+#define ANALOGINPUT_H_
+
+#include
+#include
+#include
+#include
+#include
+
+using namespace std;
+
+class AnalogInput
+{
+private:
+ FILE *ActivateAnalogHnd;
+ string activateAnalogPath;
+ bool analogIsSet;
+
+ FILE *AnalogInHnd;
+ string analogInPath;
+ bool helperNumFound;
+
+ // suport var for init
+ string startPath;
+ string readPath;
+
+ glob_t globbuf;
+
+ // support vars for pin reading
+ long lSize;
+ char * buffer;
+ size_t result;
+
+ bool verbose;
+
+public:
+ AnalogInput();
+ ~AnalogInput();
+
+ int initAnalogInputs();
+ int read(int index);
+
+};
+
+
+
+
+#endif /* ANALOGINPUT_H_ */
diff -r 000000000000 -r 8a575ba3ab52 projects/d-box/Biquad.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/projects/d-box/Biquad.cpp Fri Oct 31 19:10:17 2014 +0100
@@ -0,0 +1,169 @@
+//
+// Biquad.cpp
+//
+// Created by Nigel Redmon on 11/24/12
+// EarLevel Engineering: earlevel.com
+// Copyright 2012 Nigel Redmon
+//
+// For a complete explanation of the Biquad code:
+// http://www.earlevel.com/main/2012/11/26/biquad-c-source-code/
+//
+// License:
+//
+// This source code is provided as is, without warranty.
+// You may copy and distribute verbatim copies of this document.
+// You may modify and use this source code to create binary code
+// for your own purposes, free or commercial.
+//
+
+#include
+#include "Biquad.h"
+#include
+
+Biquad::Biquad() {
+ type = bq_type_lowpass;
+ a0 = 1.0;
+ a1 = a2 = b1 = b2 = 0.0;
+ Fc = 0.50;
+ Q = 0.707;
+ peakGain = 0.0;
+ z1 = z2 = 0.0;
+}
+
+Biquad::Biquad(int type, double Fc, double Q, double peakGainDB) {
+ setBiquad(type, Fc, Q, peakGainDB);
+ z1 = z2 = 0.0;
+}
+
+Biquad::~Biquad() {
+}
+
+void Biquad::setType(int type) {
+ this->type = type;
+ calcBiquad();
+}
+
+void Biquad::setQ(double Q) {
+ this->Q = Q;
+ calcBiquad();
+}
+
+void Biquad::setFc(double Fc) {
+ this->Fc = Fc;
+ calcBiquad();
+}
+
+void Biquad::setPeakGain(double peakGainDB) {
+ this->peakGain = peakGainDB;
+ calcBiquad();
+}
+
+void Biquad::setBiquad(int type, double Fc, double Q, double peakGainDB) {
+ this->type = type;
+ this->Q = Q;
+ this->Fc = Fc;
+ startFc = Fc;
+ startQ = Q;
+ startPeakGain = peakGainDB;
+ setPeakGain(peakGainDB);
+}
+
+void Biquad::calcBiquad(void) {
+ double norm;
+ double V = pow(10, fabs(peakGain) / 20.0);
+ double K = tan(M_PI * Fc);
+ switch (this->type) {
+ case bq_type_lowpass:
+ norm = 1 / (1 + K / Q + K * K);
+ a0 = K * K * norm;
+ a1 = 2 * a0;
+ a2 = a0;
+ b1 = 2 * (K * K - 1) * norm;
+ b2 = (1 - K / Q + K * K) * norm;
+ break;
+
+ case bq_type_highpass:
+ norm = 1 / (1 + K / Q + K * K);
+ a0 = 1 * norm;
+ a1 = -2 * a0;
+ a2 = a0;
+ b1 = 2 * (K * K - 1) * norm;
+ b2 = (1 - K / Q + K * K) * norm;
+ break;
+
+ case bq_type_bandpass:
+ norm = 1 / (1 + K / Q + K * K);
+ a0 = K / Q * norm;
+ a1 = 0;
+ a2 = -a0;
+ b1 = 2 * (K * K - 1) * norm;
+ b2 = (1 - K / Q + K * K) * norm;
+ break;
+
+ case bq_type_notch:
+ norm = 1 / (1 + K / Q + K * K);
+ a0 = (1 + K * K) * norm;
+ a1 = 2 * (K * K - 1) * norm;
+ a2 = a0;
+ b1 = a1;
+ b2 = (1 - K / Q + K * K) * norm;
+ break;
+
+ case bq_type_peak:
+ if (peakGain >= 0) { // boost
+ norm = 1 / (1 + 1/Q * K + K * K);
+ a0 = (1 + V/Q * K + K * K) * norm;
+ a1 = 2 * (K * K - 1) * norm;
+ a2 = (1 - V/Q * K + K * K) * norm;
+ b1 = a1;
+ b2 = (1 - 1/Q * K + K * K) * norm;
+ }
+ else { // cut
+ norm = 1 / (1 + V/Q * K + K * K);
+ a0 = (1 + 1/Q * K + K * K) * norm;
+ a1 = 2 * (K * K - 1) * norm;
+ a2 = (1 - 1/Q * K + K * K) * norm;
+ b1 = a1;
+ b2 = (1 - V/Q * K + K * K) * norm;
+ }
+ break;
+ case bq_type_lowshelf:
+ if (peakGain >= 0) { // boost
+ norm = 1 / (1 + sqrt(2) * K + K * K);
+ a0 = (1 + sqrt(2*V) * K + V * K * K) * norm;
+ a1 = 2 * (V * K * K - 1) * norm;
+ a2 = (1 - sqrt(2*V) * K + V * K * K) * norm;
+ b1 = 2 * (K * K - 1) * norm;
+ b2 = (1 - sqrt(2) * K + K * K) * norm;
+ }
+ else { // cut
+ norm = 1 / (1 + sqrt(2*V) * K + V * K * K);
+ a0 = (1 + sqrt(2) * K + K * K) * norm;
+ a1 = 2 * (K * K - 1) * norm;
+ a2 = (1 - sqrt(2) * K + K * K) * norm;
+ b1 = 2 * (V * K * K - 1) * norm;
+ b2 = (1 - sqrt(2*V) * K + V * K * K) * norm;
+ }
+ break;
+ case bq_type_highshelf:
+ if (peakGain >= 0) { // boost
+ norm = 1 / (1 + sqrt(2) * K + K * K);
+ a0 = (V + sqrt(2*V) * K + K * K) * norm;
+ a1 = 2 * (K * K - V) * norm;
+ a2 = (V - sqrt(2*V) * K + K * K) * norm;
+ b1 = 2 * (K * K - 1) * norm;
+ b2 = (1 - sqrt(2) * K + K * K) * norm;
+ }
+ else { // cut
+ norm = 1 / (V + sqrt(2*V) * K + K * K);
+ a0 = (1 + sqrt(2) * K + K * K) * norm;
+ a1 = 2 * (K * K - 1) * norm;
+ a2 = (1 - sqrt(2) * K + K * K) * norm;
+ b1 = 2 * (K * K - V) * norm;
+ b2 = (V - sqrt(2*V) * K + K * K) * norm;
+ }
+ break;
+ }
+
+ return;
+}
diff -r 000000000000 -r 8a575ba3ab52 projects/d-box/Biquad.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/projects/d-box/Biquad.h Fri Oct 31 19:10:17 2014 +0100
@@ -0,0 +1,99 @@
+//
+// Biquad.h
+//
+// Created by Nigel Redmon on 11/24/12
+// EarLevel Engineering: earlevel.com
+// Copyright 2012 Nigel Redmon
+//
+// For a complete explanation of the Biquad code:
+// http://www.earlevel.com/main/2012/11/25/biquad-c-source-code/
+//
+// License:
+//
+// This source code is provided as is, without warranty.
+// You may copy and distribute verbatim copies of this document.
+// You may modify and use this source code to create binary code
+// for your own purposes, free or commercial.
+//
+
+#ifndef Biquad_h
+#define Biquad_h
+
+enum {
+ bq_type_lowpass = 0,
+ bq_type_highpass,
+ bq_type_bandpass,
+ bq_type_notch,
+ bq_type_peak,
+ bq_type_lowshelf,
+ bq_type_highshelf
+};
+
+class Biquad {
+public:
+ Biquad();
+ Biquad(int type, double Fc, double Q, double peakGainDB);
+ ~Biquad();
+ void setType(int type);
+ void setQ(double Q);
+ void setFc(double Fc);
+ void setPeakGain(double peakGainDB);
+ void setBiquad(int type, double Fc, double Q, double peakGain);
+ float process(float in);
+
+ double getQ();
+ double getFc();
+ double getPeakGain();
+
+ double getStartingQ();
+ double getStartingFc();
+ double getStartingPeakGain();
+
+protected:
+ void calcBiquad(void);
+
+ int type;
+ double a0, a1, a2, b1, b2;
+ double Fc, Q, peakGain;
+ double startFc, startQ, startPeakGain;
+ double z1, z2;
+};
+
+inline double Biquad::getQ()
+{
+ return Q;
+}
+
+inline double Biquad::getFc()
+{
+ return Fc;
+}
+
+inline double Biquad::getPeakGain()
+{
+ return peakGain;
+}
+
+inline double Biquad::getStartingQ()
+{
+ return startQ;
+}
+
+inline double Biquad::getStartingFc()
+{
+ return startFc;
+}
+
+inline double Biquad::getStartingPeakGain()
+{
+ return startPeakGain;
+}
+
+inline float Biquad::process(float in) {
+ double out = in * a0 + z1;
+ z1 = in * a1 + z2 - b1 * out;
+ z2 = in * a2 - b2 * out;
+ return out;
+}
+
+#endif // Biquad_h
diff -r 000000000000 -r 8a575ba3ab52 projects/d-box/DBoxSynth.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/projects/d-box/DBoxSynth.h Fri Oct 31 19:10:17 2014 +0100
@@ -0,0 +1,35 @@
+/*
+ * SimpleSynth.h
+ *
+ * Created on: Oct 22, 2013
+ * Author: Victor Zappi
+ */
+
+#ifndef DBOXSYNTH_H_
+#define DBOXSYNTH_H_
+
+#include
+#include
+#include
+#include
+
+#include "Synth.h"
+
+
+class DBoxSynth : public Synth
+{
+public:
+ DBoxSynth(unsigned int rate, unsigned long buffer_size);
+ double getSample();
+ double *getBlock(int block_size);
+
+
+private:
+ Sampler *smp;
+
+};
+
+
+
+
+#endif /* DBOXSYNTH_H_ */
diff -r 000000000000 -r 8a575ba3ab52 projects/d-box/DboxSensors.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/projects/d-box/DboxSensors.cpp Fri Oct 31 19:10:17 2014 +0100
@@ -0,0 +1,157 @@
+/*
+ * DboxSensors.cpp
+ *
+ * Created on: May 19, 2014
+ * Author: Victor Zappi
+ */
+
+
+#include "DboxSensors.h"
+#include "config.h"
+
+using namespace std;
+
+
+
+int DboxSensors::initSensors(int tk0_bus, int tk0_address, int tk1_bus, int tk1_address, int tk_file, int fsr_pin, int fsrmax, bool useNewSensors, int gpio_0, int gpio_1)
+{
+ newSensors = useNewSensors;
+ // init first touch key on i2c bus
+ if(tk0_address >= 0) {
+ if(TK0.initI2C_RW(tk0_bus, tk0_address, tk_file)>0)
+ return 1;
+ if(TK0.initTouchKey(newSensors)>0)
+ return 2;
+ }
+
+ // init second touch key on i2c bus
+ if(tk1_address >= 0) {
+ if(TK1.initI2C_RW(tk1_bus, tk1_address, tk_file)>0)
+ return 1;
+ if(TK1.initTouchKey(newSensors)>0)
+ return 2;
+ }
+
+ // init fsr on analog input pin
+ fsr_pinNum = fsr_pin;
+ fsr_max = fsrmax;
+
+ if(FSR.initAnalogInputs()>0)
+ return 3;
+
+ gpio[0] = gpio_0;
+ if(gpio[0]!=-1)
+ {
+ fdDi[0] = gpio_export(gpio[0]);
+ if(fdDi[0] == -1)
+ return 4;
+ }
+ digitalIn[0] = 1;
+
+ return 0;
+}
+
+
+int DboxSensors::readSensors()
+{
+ // write data into first touch key
+ if(TK0.ready()) {
+ if(TK0.readI2C()>0)
+ return 1;
+
+ // retrieve data from first touch key
+ tk0_touchCnt = TK0.getTouchCount();
+ }
+ else
+ tk0_touchCnt = 0;
+
+ // write data into second touch key
+ if(TK1.ready()) {
+ if(TK1.readI2C()>0)
+ return 1;
+ // retrieve data from second touch key
+ tk1_touchCnt = TK1.getTouchCount();
+ }
+ else
+ tk1_touchCnt = 0;
+
+
+ int max = 3;
+ if(newSensors)
+ max = 5;
+ // if touches detected on main touch key
+ if(tk0_touchCnt == 0 && tk1_touchCnt == 0)
+ resetSensorsData();
+ else
+ {
+ for(int i=0; i
+#include // mount()
+#include // strerror()
+#include // fstream
+#include
+#include // usleep()
+#include // glob()
+#include // elapsed time
+#include // mkdir()
+#include // reverse() [string...]
+
+#include "I2c_TouchKey.h"
+#include "AnalogInput.h"
+#include "../../include/GPIOcontrol.h" // TODO wrap this into a class
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * This class retrieves data from all the connected sensors,
+ * logs them
+ * and exposes to the main only the values needed to synthesize sound
+ *
+ * The simple instrument has:
+ *
+ *
+ *
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+class DboxSensors
+{
+public:
+ int initSensors(int tk0_bus, int tk0_address, int tk1_bus, int tk1_address, int tk_file, int fsr_pin, int fsrmax, bool useNewSensors, int gpio0=-1, int gpio1=-1);
+ int readSensors();
+ int getTKTouchCount(int index);
+ float *getTKXPositions(int index);
+ float getTKYPosition(int index);
+ float *getTKTouchSize(int index);
+ double getFSRVAlue();
+ int getDigitalIn(int index);
+
+ DboxSensors();
+ ~DboxSensors();
+
+private:
+ bool newSensors;
+
+ I2c_TouchKey TK0;
+ int tk0_touchCnt;
+ float tk0_touchPosX[5];
+ float tk0_touchPosY;
+ float tk0_touchSize[5];
+
+ I2c_TouchKey TK1;
+ int tk1_touchCnt;
+ float tk1_touchPosX[5];
+ float tk1_touchPosY;
+ float tk1_touchSize[5];
+
+ AnalogInput FSR;
+ int fsr_pinNum;
+ double fsr_read;
+ int fsr_max;
+
+ unsigned int digitalIn[2];
+ int fdDi[2];
+ int gpio[2];
+
+ void resetSensorsData();
+
+};
+
+
+
+//--------------------------------------------------------------------------------
+// read interface
+inline int DboxSensors::getTKTouchCount(int index)
+{
+ if(index==0)
+ return tk0_touchCnt;
+ else
+ return tk1_touchCnt;
+}
+
+inline float *DboxSensors::getTKXPositions(int index)
+{
+ if(index==0)
+ return tk0_touchPosX;
+ else
+ return tk1_touchPosX;
+}
+
+inline float DboxSensors::getTKYPosition(int index)
+{
+ if(index==0)
+ return tk0_touchPosY;
+ else
+ return tk1_touchPosY;
+}
+
+inline float *DboxSensors::getTKTouchSize(int index)
+{
+ if(index==0)
+ return tk0_touchSize;
+ else
+ return tk1_touchSize;
+}
+
+inline double DboxSensors::getFSRVAlue()
+{
+ return fsr_read;
+}
+
+inline int DboxSensors::getDigitalIn(int index)
+{
+ return digitalIn[index];
+}
+//--------------------------------------------------------------------------------
+
+
+#endif /* DBOXSENSORS_H_ */
diff -r 000000000000 -r 8a575ba3ab52 projects/d-box/FIRfilter.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/projects/d-box/FIRfilter.h Fri Oct 31 19:10:17 2014 +0100
@@ -0,0 +1,73 @@
+/*
+ * FIRfilter.h
+ *
+ * Created on: Aug 5, 2014
+ * Author: Victor Zappi and Andrew McPherson
+ */
+
+#ifndef FIRFILTER_H_
+#define FIRFILTER_H_
+
+
+#include
+
+//#define FILTER_TAP_NUM 21
+//ne10_float32_t filterTaps[FILTER_TAP_NUM] = {
+// 0.000350,
+// 0.001133,
+// 0.002407,
+// 0.004203,
+// 0.006468,
+// 0.009057,
+// 0.011748,
+// 0.014265,
+// 0.016323,
+// 0.017671,
+// 0.018141,
+// 0.017671,
+// 0.016323,
+// 0.014265,
+// 0.011748,
+// 0.009057,
+// 0.006468,
+// 0.004203,
+// 0.002407,
+// 0.001133,
+// 0.000350
+//};
+#define FILTER_TAP_NUM 31
+ne10_float32_t filterTaps[FILTER_TAP_NUM] = {
+ 0.000018,
+ 0.000043,
+ 0.000078,
+ 0.000125,
+ 0.000183,
+ 0.000252,
+ 0.000330,
+ 0.000415,
+ 0.000504,
+ 0.000592,
+ 0.000677,
+ 0.000754,
+ 0.000818,
+ 0.000866,
+ 0.000897,
+ 0.000907,
+ 0.000897,
+ 0.000866,
+ 0.000818,
+ 0.000754,
+ 0.000677,
+ 0.000592,
+ 0.000504,
+ 0.000415,
+ 0.000330,
+ 0.000252,
+ 0.000183,
+ 0.000125,
+ 0.000078,
+ 0.000043,
+ 0.000018
+};
+
+#endif /* FIRFILTER_H_ */
diff -r 000000000000 -r 8a575ba3ab52 projects/d-box/FeedbackOscillator.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/projects/d-box/FeedbackOscillator.cpp Fri Oct 31 19:10:17 2014 +0100
@@ -0,0 +1,112 @@
+/*
+ * FeedbackOscillator.cpp
+ *
+ * Recursive phase-shift oscillator implemented
+ * on the matrix
+ *
+ * Andrew McPherson 2014
+ */
+
+#include "FeedbackOscillator.h"
+#include
+#include
+
+#define COEFF_B0 0
+#define COEFF_B1 1
+#define COEFF_A1 2
+
+FeedbackOscillator::FeedbackOscillator()
+: wavetable1(0), wavetable2(0)
+{
+
+}
+
+FeedbackOscillator::~FeedbackOscillator() {
+ if(wavetable1 != 0)
+ free(wavetable1);
+ if(wavetable2 != 0)
+ free(wavetable2);
+
+}
+
+// Initialise the settings for the feedback oscillator
+void FeedbackOscillator::initialise(int maxTableSize, float hpfCutoffFrequency, float matrixSampleRate) {
+ wavetableMaxLength = maxTableSize;
+ if(wavetable1 != 0)
+ free(wavetable1);
+ if(wavetable2 != 0)
+ free(wavetable2);
+
+ wavetable1 = (float *)malloc(maxTableSize * sizeof(float));
+ wavetable2 = (float *)malloc(maxTableSize * sizeof(float));
+
+ float omega = tan(M_PI * hpfCutoffFrequency / matrixSampleRate);
+ float n = 1.0f / (1.0f + omega);
+
+ coeffs[COEFF_A1] = (omega - 1.0f) * n;
+ coeffs[COEFF_B0] = n;
+ coeffs[COEFF_B1] = -n;
+
+ for(int n = 0; n < maxTableSize; n++)
+ wavetable1[n] = wavetable2[n] = 0;
+
+ wavetableRead = wavetable1;
+ wavetableWrite = wavetable2;
+ wavetableWritePointer = 0;
+ sampleCount = lastTriggerCount = 0;
+}
+
+// Process one sample and store the output value
+// Returns true if the wavetable needs rendering
+int FeedbackOscillator::process(uint16_t input, uint16_t *output) {
+ float inFloat = input / 65536.0;
+ float outFloat = coeffs[COEFF_B0] * inFloat + coeffs[COEFF_B1] * lastInput - coeffs[COEFF_A1] * lastOutput;
+ int requestRenderLength = 0;
+
+ //outFloat *= 2.0;
+
+ int intOut = outFloat * 65536.0 + 32768;
+ if(intOut > 65535)
+ intOut = 65535;
+ if(intOut < 0)
+ intOut = 0;
+ //intOut = (intOut & 0xFF) << 8;
+ //if(intOut > 65535)
+ // intOut = 65535;
+
+ *output = (uint16_t)intOut;
+
+ if(canTrigger && outFloat > 0 && lastOutput <= 0) {
+ triggered = true;
+ requestRenderLength = wavetableWritePointer; // How many samples stored thus far?
+ if(requestRenderLength < 4)
+ requestRenderLength = 0; // Ignore anything with fewer than 4 points
+
+ lastTriggerCount = sampleCount;
+ canTrigger = false;
+ wavetableWritePointer = 0;
+
+ // Swap buffers
+ float *temp = wavetableWrite;
+ wavetableWrite = wavetableRead;
+ wavetableRead = temp;
+ }
+
+ if(triggered) {
+ wavetableWrite[wavetableWritePointer] = outFloat;
+ if(++wavetableWritePointer >= wavetableMaxLength) {
+ triggered = false;
+ wavetableWritePointer = 0;
+ }
+ }
+
+ if(sampleCount - lastTriggerCount > 40)
+ canTrigger = true;
+
+ sampleCount++;
+
+ lastOutput = outFloat;
+ lastInput = inFloat;
+
+ return requestRenderLength;
+}
diff -r 000000000000 -r 8a575ba3ab52 projects/d-box/FeedbackOscillator.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/projects/d-box/FeedbackOscillator.h Fri Oct 31 19:10:17 2014 +0100
@@ -0,0 +1,43 @@
+/*
+ * FeedbackOscillator.h
+ *
+ * Created on: June 8, 2014
+ * Author: Andrew McPherson
+ */
+
+#ifndef FEEDBACKOSCILLATOR_H
+#define FEEDBACKOSCILLATOR_H
+
+#include
+
+class FeedbackOscillator
+{
+public:
+ FeedbackOscillator();
+ ~FeedbackOscillator();
+
+ // Initialise the settings for the feedback oscillator
+ void initialise(int maxTableSize, float hpfCutoffFrequency, float matrixSampleRate);
+
+ // Process one sample and store the output value
+ // Returns the length of table to interpolate; or 0 if nothing to process further
+ int process(uint16_t input, uint16_t *output);
+
+ float *wavetable() { return wavetableRead; }
+
+private:
+ float coeffs[3]; // Coefficients of first-order high-pass filter
+ float lastInput; // last input sample for HPF
+ float lastOutput; // last output sample of HPF
+ bool triggered; // whether we are currently saving samples
+ bool canTrigger; // whether we are able to begin saving samples
+ int wavetableMaxLength; // how long the stored wavetable can be
+ int sampleCount; // how many samples have elapsed
+ int lastTriggerCount; // sample count when we last triggered
+
+ float *wavetable1, *wavetable2; // Two wavetables where we record samples
+ float *wavetableRead, *wavetableWrite; // Pointers to the above wavetables
+ int wavetableWritePointer; // Where we are currently writing
+};
+
+#endif
diff -r 000000000000 -r 8a575ba3ab52 projects/d-box/I2c_TouchKey.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/projects/d-box/I2c_TouchKey.cpp Fri Oct 31 19:10:17 2014 +0100
@@ -0,0 +1,152 @@
+/*
+ * I2c_TouchKey.cpp
+ *
+ * Created on: Oct 14, 2013
+ * Author: Victor Zappi
+ */
+
+
+
+#include "I2c_TouchKey.h"
+
+#undef DEBUG_I2C_TOUCHKEY
+
+I2c_TouchKey::I2c_TouchKey()
+{
+ isReady = false;
+ newSensor = false;
+ touchCount = 0;
+ sliderSize[0] = sliderSize[1] = sliderSize[2] = -1;
+ sliderPosition[0] = sliderPosition[1] = sliderPosition[2] = -1;
+ sliderPositionH = -1;
+}
+
+int I2c_TouchKey::initTouchKey(bool useNewSensor)
+{
+ newSensor = useNewSensor;
+ numBytesToRead = newSensor ? NUM_BYTES_NEW : NUM_BYTES_OLD;
+
+ char buf[3] = { 0x00, 0x01, 0x00 }; // code for centroid mode
+ if(write(i2C_file, buf, 3) !=3)
+ {
+ cout << "Failed to set TouchKey in \"Centroid Mode\" " << endl;
+ return 1;
+ }
+
+ usleep(5000); // need to give TouchKey enough time to process command
+
+ char buf4[4] = { 0x00, 0x07, 0x00, 0x64}; // code for change minimum touch area
+ if(write(i2C_file, buf4, 4) !=4)
+ {
+ cout << "Failed to set TouchKey minimum touch size" << endl;
+ return 1;
+ }
+
+ usleep(5000); // need to give TouchKey enough time to process command
+
+ buf[0] = 0x06; // code for data collection
+ if(write(i2C_file, buf, 1) !=1)
+ {
+ cout << "Failed to prepare data collection " << endl;
+ return 2;
+ }
+
+ usleep(5000); // need to give TouchKey enough time to process command
+
+ isReady = true;
+
+ return 0;
+}
+
+
+int I2c_TouchKey::readI2C()
+{
+ bytesRead = read(i2C_file, dataBuffer, numBytesToRead);
+ if (bytesRead != numBytesToRead)
+ {
+ cout << "Failure to read Byte Stream" << endl;
+ return 2;
+ }
+ /*cout << NUM_BYTES << " bytes read" << endl;
+ for(int j=0; j<9; j++)
+ cout << "\t" << (int)dataBuffer[j];
+ cout << endl;
+ */
+
+ touchCount = 0;
+
+ rawSliderPosition[0] = (((dataBuffer[0] & 0xF0) << 4) + dataBuffer[1]);
+ rawSliderPosition[1] = (((dataBuffer[0] & 0x0F) << 8) + dataBuffer[2]);
+ rawSliderPosition[2] = (((dataBuffer[3] & 0xF0) << 4) + dataBuffer[4]);
+
+ // Old TouchKeys sensors have 3 touch locations plus horizontal positions
+ // New D-Box sensors have 5 touch locations but no horizontal position
+ if(newSensor)
+ {
+ rawSliderPosition[3] = (((dataBuffer[5] & 0xF0) << 4) + dataBuffer[6]);
+ rawSliderPosition[4] = (((dataBuffer[5] & 0x0F) << 8) + dataBuffer[7]);
+ rawSliderPositionH = 0x0FFF;
+ }
+ else
+ {
+ rawSliderPosition[3] = 0x0FFF;
+ rawSliderPosition[4] = 0x0FFF;
+ rawSliderPositionH = (((dataBuffer[3] & 0x0F) << 8) + dataBuffer[5]);
+ }
+
+
+ for(int i = 0; i < 5; i++)
+ {
+ if(rawSliderPosition[i] != 0x0FFF) // 0x0FFF means no touch
+ {
+ //sliderPosition[i] = (float)rawSliderPosition[i] / 1536.0; // Black keys, vertical (128 * 12)
+ //sliderPosition[i] = (float)rawSliderPosition[i] / 768.0; // Cute white key, for simple instrument
+ if(newSensor)
+ sliderPosition[i] = (float)rawSliderPosition[i] / 3200.0; // New sensors; 26 pads (128 * 25)
+ else
+ sliderPosition[i] = (float)rawSliderPosition[i] / 2432.0; // White keys, vertical (128 * 19)
+ if(sliderPosition[i]>1.0)
+ sliderPosition[i] = 1.0;
+ if(newSensor)
+ sliderSize[i] = (float)dataBuffer[i + 8] / 255.0;
+ else {
+ if(i < 3)
+ sliderSize[i] = (float)dataBuffer[i + 6] / 255.0;
+ else
+ sliderSize[i] = 0.0;
+ }
+ touchCount++;
+ }
+ else {
+ sliderPosition[i] = -1.0;
+ sliderSize[i] = 0.0;
+ }
+ }
+
+
+
+ if(rawSliderPositionH != 0x0FFF)
+ {
+ sliderPositionH = (float)rawSliderPositionH / 256.0; // White keys, horizontal (1 byte + 1 bit)
+ }
+ else
+ sliderPositionH = -1.0;
+
+#ifdef DEBUG_I2C_TOUCHKEY
+ for(int i = 0; i < bytesRead; i++) {
+ printf("%2X ", dataBuffer[i]);
+ }
+ cout << touchCount << " touches: ";
+ for(int i = 0; i < touchCount; i++) {
+ cout << "(" << sliderPosition[i] << ", " << sliderSize[i] << ") ";
+ }
+ cout << "H = " << sliderPositionH << endl;
+#endif
+
+ return 0;
+}
+
+
+I2c_TouchKey::~I2c_TouchKey()
+{}
+
diff -r 000000000000 -r 8a575ba3ab52 projects/d-box/I2c_TouchKey.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/projects/d-box/I2c_TouchKey.h Fri Oct 31 19:10:17 2014 +0100
@@ -0,0 +1,75 @@
+/*
+ * I2c.h
+ *
+ * Created on: Oct 14, 2013
+ * Author: Victor Zappi
+ */
+
+#ifndef I2CTK_H_
+#define I2CTK_H_
+
+#include "../../include/I2c.h"
+
+#define NUM_BYTES_OLD 9
+#define NUM_BYTES_NEW 13
+
+class I2c_TouchKey : public I2c
+{
+private:
+ bool isReady;
+ bool newSensor;
+ int numBytesToRead;
+
+ // read NUM_BYTES bytes, which have to be properly parsed
+ char dataBuffer[NUM_BYTES_NEW];
+ int bytesRead;
+
+ int rawSliderPosition[5];
+ int rawSliderPositionH;
+
+ int touchCount;
+ float sliderSize[5];
+ float sliderPosition[5];
+ float sliderPositionH;
+
+
+public:
+ int initTouchKey(bool useNewSensor = false);
+ int readI2C();
+ int getTouchCount();
+ float * getSlidersize();
+ float * getSliderPosition();
+ float getSliderPositionH();
+
+ bool ready() { return isReady; }
+
+ I2c_TouchKey();
+ ~I2c_TouchKey();
+
+};
+
+inline int I2c_TouchKey::getTouchCount()
+{
+ return touchCount;
+}
+
+inline float * I2c_TouchKey::getSlidersize()
+{
+ return sliderSize;
+}
+
+inline float * I2c_TouchKey::getSliderPosition()
+{
+ return sliderPosition;
+}
+
+inline float I2c_TouchKey::getSliderPositionH()
+{
+ return sliderPositionH;
+}
+
+
+
+
+
+#endif /* I2CTK_H_ */
diff -r 000000000000 -r 8a575ba3ab52 projects/d-box/OscillatorBank.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/projects/d-box/OscillatorBank.cpp Fri Oct 31 19:10:17 2014 +0100
@@ -0,0 +1,1016 @@
+/*
+ * OscillatorBank.cpp
+ *
+ * Created on: May 23, 2014
+ * Author: Victor Zappi and Andrew McPherson
+ */
+
+
+/*
+ * There is a problem with name consistency between this class and the Parser class in spear_parser files.
+ * There, a "frame" is each of the time values where partials are sampled, while a "hop" is the actual jump between frames [calculated in samples]
+ * Here, "hop" is used with the meaning of "frame", while "frame" became the local frame of a partial
+ *
+ * example
+ *
+ * frames: 0 1 2
+ * p0 p0_0 p0_1
+ * p1 p1_0 p1_1 p1_2
+ * p2 p2_0 p2_1
+ *
+ * In this case:
+ * in Parser there are 2 hops, 3 total frames and the 3 partials have respectively 2, 3 and 2 local frames
+ *
+ * here there are 3 total hops [the concept of jumps is discarded, cos not in use] and the 3 partials have respectively 2, 3 and 2 frames
+ *
+ * This must be fixed
+*/
+
+// TODO: fix hop-frame name consistency
+
+
+#include
+
+#include "OscillatorBank.h"
+
+OscillatorBank::OscillatorBank() {
+ loaded = false;
+}
+
+OscillatorBank::OscillatorBank(string filename, int hopsize, int samplerate) {
+ loaded = false;
+ loadFile(filename.c_str(), hopsize, samplerate);
+}
+
+OscillatorBank::OscillatorBank(char *filename, int hopsize, int samplerate) {
+ loaded = false;
+ loadFile(filename, hopsize, samplerate);
+}
+
+OscillatorBank::~OscillatorBank() {
+ free(oscillatorPhases);
+ free(oscillatorNextNormFreq);
+ free(oscillatorNextAmp);
+ free(oscillatorNormFrequencies);
+ free(oscillatorAmplitudes);
+ free(oscillatorNormFreqDerivatives);
+ free(oscillatorAmplitudeDerivatives);
+ free(phaseCopies);
+ free(nextNormFreqCopies);
+ free(nextAmpCopies);
+
+ delete[] oscStatNormFrequenciesMean;
+ delete[] oscStatNumHops;
+ delete[] lookupTable;
+ delete[] indicesMapping;
+ delete[] freqFixedDeltas;
+ delete[] ampFixedDeltas;
+ delete[] nyquistCut;
+}
+
+bool OscillatorBank::initBank(int oversamp) {
+ if (!loaded)
+ return false;
+
+ //---prepare look-up table
+ lookupTableSize = 1024;
+ lookupTable = new float[lookupTableSize + 1];
+ for (int n = 0; n < (lookupTableSize + 1); n++)
+ lookupTable[n] = sin(2.0 * M_PI * (float) n / (float) lookupTableSize);
+ frequencyScaler = (float) lookupTableSize / rate;
+ nyqNorm = rate / 2 * frequencyScaler;
+
+ if (oversamp < 1)
+ oversamp = 1;
+
+ //---prepare oscillators
+ partials = &(parser.partials); // pointer to paser's partials
+ partialsHopSize = parser.getHopSize();
+ lastHop = partials->getHopNum(); // last bank hop is equal to last partial frame, which is equal to partial hop num
+ overSampling = oversamp;
+ hopSize = partialsHopSize / overSampling; // if oversampling, osc bank hop num > partials hop num
+ hopSizeReminder = partialsHopSize % overSampling;
+ oscBankHopSize = hopSize;
+ numOfPartials = partials->getPartialNum();
+ numOfOscillators = partials->getMaxActivePartialNum(); // get the maximum number of active partials at the same time
+
+ // set to next multiple of 4 [NEON]
+ numOfOscillators = (numOfOscillators + 3) & ~0x3; // to be sure we can add up to 3 fake oscillators
+
+ int err;
+ //---allocate buffers
+ // alligned buffers [NEON]
+ err = posix_memalign((void**) &oscillatorPhases, 16,
+ numOfOscillators * sizeof(float));
+ err += posix_memalign((void**) &oscillatorNextNormFreq, 16,
+ numOfOscillators * sizeof(float));
+ err += posix_memalign((void**) &oscillatorNextAmp, 16,
+ numOfOscillators * sizeof(float));
+ err += posix_memalign((void**) &oscillatorNormFrequencies, 16,
+ numOfOscillators * sizeof(float));
+ err += posix_memalign((void**) &oscillatorAmplitudes, 16,
+ numOfOscillators * sizeof(float));
+ err += posix_memalign((void**) &oscillatorNormFreqDerivatives, 16,
+ numOfOscillators * sizeof(float));
+ err += posix_memalign((void**) &oscillatorAmplitudeDerivatives, 16,
+ numOfOscillators * sizeof(float));
+ err += posix_memalign((void**) &phaseCopies, 16,
+ numOfOscillators * sizeof(float));
+ err += posix_memalign((void**) &nextNormFreqCopies, 16,
+ numOfOscillators * sizeof(float));
+ err += posix_memalign((void**) &nextAmpCopies, 16,
+ numOfOscillators * sizeof(float));
+
+ // regular ones
+ oscStatNormFrequenciesMean = new float[numOfPartials];
+ oscStatNumHops = new float[numOfPartials];
+ indicesMapping = new int[numOfPartials];
+ freqFixedDeltas = new float[numOfPartials];
+ ampFixedDeltas = new float[numOfPartials];
+ nyquistCut = new bool[numOfPartials];
+
+ if (err > 0) {
+ dbox_printf("Failed memory allocations %@#!\n");
+ return false;
+ }
+
+ // copy stats [they do not change]
+ for (int n = 0; n < numOfPartials; n++) {
+ oscStatNormFrequenciesMean[n] = partials->partialFreqMean[n]
+ * frequencyScaler;
+ oscStatNumHops[n] = partials->partialNumFrames[n]; // in Parser and Partials "frames" are what we call here "hops" [see comment at top of file]
+ }
+
+ // deafult values
+ actPartNum = 0;
+ loopStartHop = 0;
+ loopEndHop = (parser.partials.getHopNum() - 2) * overSampling;
+ ampTh = 0.0001;
+ hopNumTh = 0;
+ pitchMultiplier = 1;
+ freqMovement = 1;
+ filterNum = 0;
+ note = false;
+ speed = 1;
+ nextSpeed = -1;
+ maxSpeed = 10;
+ minSpeed = 0.1;
+ jumpHop = -1;
+
+ // filter
+ filterMaxF = 22000;
+ filterAmpMinF = 10 * frequencyScaler;
+ filterAmpMaxF = 5000 * frequencyScaler;
+ filterAmpMul = 10.0;
+
+ // adsr
+ minAttackTime = .0001;
+ deltaAttackTime = 2.;
+ minReleaseTime = 1;
+ deltaReleaseTime = 2.5;
+
+ adsr.setAttackRate(minAttackTime * rate);
+ adsr.setDecayRate(.0001 * rate);
+ adsr.setSustainLevel(1);
+ adsr.setReleaseRate(minReleaseTime * rate);
+
+ state = bank_stopped;
+ return true;
+}
+
+void OscillatorBank::resetOscillators() {
+ currentHop = -1;
+ loopDir = 1;
+ loopDirShift = 0;
+ fill(nyquistCut, nyquistCut + numOfPartials, false);
+ prevAdsrVal = 0;
+ prevAmpTh = ampTh;
+ prevHopNumTh = hopNumTh;
+ prevPitchMultiplier = pitchMultiplier;
+ prevFreqMovement = freqMovement;
+ prevFilterNum = filterNum;
+ memcpy(prevFilterFreqs, filterFreqs, filterNum * sizeof(float));
+ memcpy(prevFilterQ, filterQ, filterNum * sizeof(float));
+
+ int activePNum = partials->activePartialNum[0];
+ unsigned int *activeP = partials->activePartials[0];
+ for (int i = 0; i < activePNum; i++) {
+ freqFixedDeltas[activeP[i]] = partials->partialFreqDelta[activeP[i]][0]
+ / overSampling;
+ ampFixedDeltas[activeP[i]] = partials->partialAmpDelta[activeP[i]][0]
+ / overSampling;
+ }
+ // attack!
+ adsr.gate(1);
+ note = true;
+
+ nextHop();
+
+ state = bank_playing;
+}
+
+void OscillatorBank::nextHop() {
+ hopSize = oscBankHopSize;
+
+ // copy phases, next freqs and next amps from previous frame
+ memcpy(phaseCopies, oscillatorPhases, actPartNum * sizeof(float));
+ memcpy(nextNormFreqCopies, oscillatorNextNormFreq,
+ actPartNum * sizeof(float));
+ memcpy(nextAmpCopies, oscillatorNextAmp, actPartNum * sizeof(float));
+
+ // next frame is forward or backwards, cos we could be in the loop
+ currentHop += loopDir;
+
+ checkDirection();
+
+// if((currentHop/overSampling)%100 == 0)
+// dbox_printf("currentHop %d, direction: %d\n", currentHop/overSampling, loopDir);
+
+ // if needs jump, end here this method, cos jumpToHop() will do tee rest
+ if (checkJump() == 0)
+ return;
+ // otherwise, if jump is not needed or fails, continue regular stuff
+
+ if (nextEnvState() != 0)
+ return; // release has ended!
+
+ checkSpeed();
+
+ // now let's decide how to calculate next hop
+ if (!checkOversampling())
+ nextOscBankHop();
+ else
+ nextPartialHop();
+}
+
+void OscillatorBank::nextOscBankHop() {
+ int parIndex, localHop;
+ float parDamp = 1;
+ int currentPartialHop = (currentHop / overSampling) + loopDirShift;
+
+ // if going backwards in the loop, get previous frame active partials...
+ actPartNum = partials->activePartialNum[currentPartialHop - loopDirShift];
+ actPart = partials->activePartials[currentPartialHop - loopDirShift];
+ //cout << "actPartNum: " << actPartNum << endl;
+
+ envState = adsr.getState(); // to determine what state we will be in next hop [attack, decay, sustain, release]
+
+ int parCnt = 0;
+ int currentHopReminder = currentHop % overSampling;
+ // steps to reach next bank hop from previous partial hop
+ int steps = currentHopReminder + 1;
+ if (loopDir < 0)
+ steps = overSampling - currentHopReminder + 1;
+
+ for (int i = 0; i < actPartNum; i++) {
+ // find partial and frame
+ parIndex = actPart[i];
+ //localHop = partials->localPartialFrames[currentPartialHop][parIndex];
+ localHop = currentPartialHop - partials->partialStartFrame[parIndex]; // in Parser and Partials "frames" are what we call here "hops". These particular ones are local frames [see comment at top of file]
+
+ //float delta = partials->partialFrequencies[parIndex][localHop+loopDir] - partials->partialFrequencies[parIndex][localHop];
+
+ // if this partial was over nyquist on previous hop...
+ if (nyquistCut[parIndex]) {
+ // ...restart from safe values
+ oscillatorPhases[parCnt] = 0;
+ //TODO add freqmove dependency
+ oscillatorNextNormFreq[parCnt] =
+ (partials->partialFrequencies[parIndex][localHop]
+ + freqFixedDeltas[parIndex] * (steps - 1))
+ * frequencyScaler * prevPitchMultiplier;
+ oscillatorNextAmp[parCnt] = 0;
+ } else if (loopDir == 1) // otherwise recover phase, target freq and target amp from previous frame
+ {
+ if ((localHop != 0) || (currentHopReminder != 0)) {
+ oscillatorPhases[parCnt] =
+ phaseCopies[indicesMapping[parIndex]];
+ oscillatorNextNormFreq[parCnt] =
+ nextNormFreqCopies[indicesMapping[parIndex]];
+ oscillatorNextAmp[parCnt] =
+ nextAmpCopies[indicesMapping[parIndex]];
+ } else // first oscillator hop [both for bank and partial], so no previous data are available
+ {
+ oscillatorPhases[parCnt] = 0;
+ //TODO add freqmove dependency
+ oscillatorNextNormFreq[parCnt] =
+ partials->partialFrequencies[parIndex][localHop]
+ * frequencyScaler * prevPitchMultiplier;
+ parDamp = calculateParDamping(parIndex, prevHopNumTh,
+ prevAdsrVal, oscillatorNextNormFreq[parCnt],
+ prevFilterNum, prevFilterFreqs, prevFilterQ);
+ oscillatorNextAmp[parCnt] =
+ partials->partialAmplitudes[parIndex][localHop]
+ * parDamp;
+ if(oscillatorNextAmp[parCnt] > 1)
+ oscillatorNextAmp[parCnt] = 1;
+ freqFixedDeltas[parIndex] =
+ partials->partialFreqDelta[parIndex][localHop + loopDir]
+ * loopDir / overSampling;
+ ampFixedDeltas[parIndex] =
+ partials->partialAmpDelta[parIndex][localHop + loopDir]
+ * loopDir / overSampling;
+ }
+ } else {
+ oscillatorPhases[parCnt] = phaseCopies[indicesMapping[parIndex]];
+ oscillatorNextNormFreq[parCnt] =
+ nextNormFreqCopies[indicesMapping[parIndex]];
+ oscillatorNextAmp[parCnt] = nextAmpCopies[indicesMapping[parIndex]];
+ }
+
+ // remove aliasing, skipping partial over nyquist freq
+ if (oscillatorNextNormFreq[parCnt] > nyqNorm) {
+ nyquistCut[parIndex] = true;
+ continue;
+ }
+ nyquistCut[parIndex] = false;
+
+ // first set up freq, cos filter affects amplitude damping according to freq content
+ oscillatorNormFrequencies[parCnt] = oscillatorNextNormFreq[parCnt]; // to fix any possible drifts
+ // save next values, current for next round
+ oscillatorNextNormFreq[parCnt] = (freqMovement
+ * (partials->partialFrequencies[parIndex][localHop]
+ + freqFixedDeltas[parIndex] * steps) * frequencyScaler
+ + (1 - freqMovement) * oscStatNormFrequenciesMean[parIndex])
+ * pitchMultiplier;
+ // derivatives are (next hop value*next damping) - (current hop value*current damping) ---> next hop must be available, in both directions, because of control on active partials
+ oscillatorNormFreqDerivatives[parCnt] = (oscillatorNextNormFreq[parCnt]
+ - oscillatorNormFrequencies[parCnt]) / hopCounter;
+ // this second weird passage handles dissonance control, morphing between regular and mean frequencies
+ oscillatorNormFreqDerivatives[parCnt] = freqMovement
+ * oscillatorNormFreqDerivatives[parCnt]
+ + (1 - freqMovement)
+ * ((oscStatNormFrequenciesMean[parIndex]
+ * pitchMultiplier)
+ - oscillatorNormFrequencies[parCnt])
+ / hopCounter;
+
+ parDamp = calculateParDamping(parIndex, hopNumTh, adsrVal,
+ oscillatorNextNormFreq[parCnt], filterNum, filterFreqs, filterQ);
+
+ // now amplitudes
+ oscillatorAmplitudes[parCnt] = oscillatorNextAmp[parCnt]; // to fix any possible drifts
+ // save next values, current for next round
+ //delta = partials->partialAmplitudes[parIndex][localHop+loopDir] - partials->partialAmplitudes[parIndex][localHop];
+ oscillatorNextAmp[parCnt] =
+ (partials->partialAmplitudes[parIndex][localHop]
+ + ampFixedDeltas[parIndex] * steps) * parDamp;
+ if(oscillatorNextAmp[parCnt] > 1)
+ oscillatorNextAmp[parCnt] = 1;
+ if ((loopDir == -1) && (localHop = 1) && (currentHopReminder == 1))
+ oscillatorNextAmp[parCnt] = 0;
+ // derivatives are (next hop value*next damping) - (current hop value*current damping) ---> next hop must be available, in both directions, because of control on active partials
+ oscillatorAmplitudeDerivatives[parCnt] = (oscillatorNextAmp[parCnt]
+ - oscillatorAmplitudes[parCnt]) / hopCounter;
+
+ // finally update current mapping between oscillators and partials
+ indicesMapping[parIndex] = parCnt;
+ parCnt++;
+ }
+ actPartNum = parCnt;
+ // [NEON] if not multiple of 4...
+ if (actPartNum % 4 != 0)
+ addFakeOsc();
+}
+
+void OscillatorBank::nextPartialHop() {
+ unsigned int parIndex, localHop;
+ float parDamp = 1;
+ int currentPartialHop = currentHop / overSampling;
+
+ // if going backwards in the loop, get previous frame active partials...
+ actPartNum = partials->activePartialNum[currentPartialHop - loopDirShift];
+ actPart = partials->activePartials[currentPartialHop - loopDirShift];
+
+ envState = adsr.getState(); // to determine what state we will be in next hop [attack, decay, sustain, release]
+
+ int parCnt = 0;
+ int steps = overSampling - 1; // steps to reach next hop [partial or bank] from previous partial hop
+
+ for (int i = 0; i < actPartNum; i++) {
+ // find partial and frame
+ parIndex = actPart[i];
+ //localHop = partials->localPartialFrames[currentPartialHop][parIndex];
+ localHop = currentPartialHop - partials->partialStartFrame[parIndex]; // in Parser and Partials "frames" are what we call here "hops". These particular ones are local frames [see comment at top of file]
+
+ // if this partial was over nyquist on previous hop...
+ if (nyquistCut[parIndex]) {
+ // ...restart from safe values
+ oscillatorPhases[parCnt] = 0;
+ //TODO add freqmove dependency
+ oscillatorNextNormFreq[parCnt] =
+ (partials->partialFrequencies[parIndex][localHop]
+ + freqFixedDeltas[parIndex] * steps
+ * (1 - loopDirShift)) * frequencyScaler
+ * prevPitchMultiplier;
+ oscillatorNextAmp[parCnt] = 0;
+ } else if (loopDir == 1) // otherwise recover phase, target freq and target amp from previous frame
+ {
+ if ((localHop != 0) || (overSampling > 1)) {
+ oscillatorPhases[parCnt] =
+ phaseCopies[indicesMapping[parIndex]];
+ oscillatorNextNormFreq[parCnt] =
+ nextNormFreqCopies[indicesMapping[parIndex]];
+ oscillatorNextAmp[parCnt] =
+ nextAmpCopies[indicesMapping[parIndex]];
+ } else // first oscillator hop [both for bank and partial], so no previous data are available
+ {
+ oscillatorPhases[parCnt] = 0;
+ //TODO add freqmove dependency
+ oscillatorNextNormFreq[parCnt] =
+ partials->partialFrequencies[parIndex][localHop]
+ * frequencyScaler * prevPitchMultiplier;
+ parDamp = calculateParDamping(parIndex, prevHopNumTh,
+ prevAdsrVal, oscillatorNextNormFreq[parCnt],
+ prevFilterNum, prevFilterFreqs, prevFilterQ);
+ oscillatorNextAmp[parCnt] =
+ partials->partialAmplitudes[parIndex][localHop]
+ * parDamp;
+ if(oscillatorNextAmp[parCnt] > 1)
+ oscillatorNextAmp[parCnt] = 1;
+ freqFixedDeltas[parIndex] =
+ partials->partialFreqDelta[parIndex][localHop + loopDir]
+ * loopDir / overSampling;
+ ampFixedDeltas[parIndex] =
+ partials->partialAmpDelta[parIndex][localHop + loopDir]
+ * loopDir / overSampling;
+ }
+ } else {
+ if (localHop != partials->partialNumFrames[parIndex] - 1) {
+ oscillatorPhases[parCnt] =
+ phaseCopies[indicesMapping[parIndex]];
+ oscillatorNextNormFreq[parCnt] =
+ nextNormFreqCopies[indicesMapping[parIndex]];
+ oscillatorNextAmp[parCnt] =
+ nextAmpCopies[indicesMapping[parIndex]];
+ } else // first oscillator hop [going backwards - both for bank and partial] , so no previous data are available
+ {
+ oscillatorPhases[parCnt] = 0;
+ //TODO add freqmove dependency
+ oscillatorNextNormFreq[parCnt] =
+ partials->partialFrequencies[parIndex][localHop]
+ * frequencyScaler * prevPitchMultiplier;
+ parDamp = calculateParDamping(parIndex, prevHopNumTh,
+ prevAdsrVal, oscillatorNextNormFreq[parCnt],
+ prevFilterNum, prevFilterFreqs, prevFilterQ);
+ oscillatorNextAmp[parCnt] =
+ partials->partialAmplitudes[parIndex][localHop]
+ * parDamp;
+ if(oscillatorNextAmp[parCnt] > 1)
+ oscillatorNextAmp[parCnt] = 1;
+ freqFixedDeltas[parIndex] =
+ partials->partialFreqDelta[parIndex][localHop + loopDir]
+ * loopDir / overSampling;
+ ampFixedDeltas[parIndex] =
+ partials->partialAmpDelta[parIndex][localHop + loopDir]
+ * loopDir / overSampling;
+ }
+ }
+ // remove aliasing, skipping partial over nyquist freq
+ if (oscillatorNextNormFreq[parCnt] > nyqNorm) {
+ //cout << nyqNorm << endl;
+ nyquistCut[parIndex] = true;
+ continue;
+ }
+ nyquistCut[parIndex] = false;
+
+ // first set up freq, cos filter affects amplitude damping according to freq content
+ oscillatorNormFrequencies[parCnt] = oscillatorNextNormFreq[parCnt]; // to fix any possible drifts
+ // save next values, current for next round
+ oscillatorNextNormFreq[parCnt] = (freqMovement
+ * (partials->partialFrequencies[parIndex][localHop + loopDir]
+ - freqFixedDeltas[parIndex] * steps * loopDirShift)
+ * frequencyScaler
+ + (1 - freqMovement) * oscStatNormFrequenciesMean[parIndex])
+ * pitchMultiplier;
+ // derivatives are (next hop value*next damping) - (current hop value*current damping) ---> next hop must be available, in both directions, because of control on active partials
+ oscillatorNormFreqDerivatives[parCnt] = (oscillatorNextNormFreq[parCnt]
+ - oscillatorNormFrequencies[parCnt]) / hopCounter;
+ // this second weird passage handles dissonance control, morphing between regular and mean frequencies
+ oscillatorNormFreqDerivatives[parCnt] = freqMovement
+ * oscillatorNormFreqDerivatives[parCnt]
+ + (1 - freqMovement)
+ * ((oscStatNormFrequenciesMean[parIndex]
+ * pitchMultiplier)
+ - oscillatorNormFrequencies[parCnt])
+ / hopCounter;
+
+ parDamp = calculateParDamping(parIndex, hopNumTh, adsrVal,
+ oscillatorNextNormFreq[parCnt], filterNum, filterFreqs, filterQ);
+
+ // now amplitudes
+ oscillatorAmplitudes[parCnt] = oscillatorNextAmp[parCnt]; // to fix any possible drifts
+ // save next values, current for next round
+ //delta = partials->partialAmplitudes[parIndex][localHop+loopDir] - partials->partialAmplitudes[parIndex][localHop];
+ oscillatorNextAmp[parCnt] =
+ (partials->partialAmplitudes[parIndex][localHop + loopDir]
+ - (ampFixedDeltas[parIndex]) * steps * loopDirShift)
+ * parDamp;
+ if(oscillatorNextAmp[parCnt] > 1)
+ oscillatorNextAmp[parCnt] = 1;
+
+ // to avoid bursts when transients are played backwards
+ if ((loopDir == -1) && (localHop - 1 == 0) && (overSampling == 1)) {
+ oscillatorNextAmp[parCnt] = 0;
+ }
+ // derivatives are (next hop value*next damping) - (current hop value*current damping) ---> next hop must be available, in both directions, because of control on active partials
+ oscillatorAmplitudeDerivatives[parCnt] = (oscillatorNextAmp[parCnt]
+ - oscillatorAmplitudes[parCnt]) / hopCounter;
+
+ // if next is not going to loop boundaries, get next deltas [same direction]
+ if ((((currentPartialHop + loopDir) * overSampling != loopEndHop)
+ || (loopDir == -1))
+ && (((currentPartialHop + loopDir) * overSampling + loopDir
+ != loopStartHop) || (loopDir == 1))) {
+ freqFixedDeltas[parIndex] =
+ partials->partialFreqDelta[parIndex][localHop + loopDir]
+ * loopDir / overSampling;
+ ampFixedDeltas[parIndex] =
+ partials->partialAmpDelta[parIndex][localHop + loopDir]
+ * loopDir / overSampling;
+ } else // .. otherwise, keep deltas but change sign [co swe change direction]
+ {
+ freqFixedDeltas[parIndex] = -freqFixedDeltas[parIndex];
+ ampFixedDeltas[parIndex] = -ampFixedDeltas[parIndex];
+ }
+
+ // finally update current mapping between oscillators and partials
+ indicesMapping[parIndex] = parCnt;
+ parCnt++;
+ }
+ actPartNum = parCnt;
+ // [NEON] if not multiple of 4...
+ if (actPartNum % 4 != 0)
+ addFakeOsc();
+
+ updatePrevControls();
+}
+
+void OscillatorBank::addFakeOsc() {
+ // ...calculate difference
+ int newPartNum = (actPartNum + 3) & ~0x3;
+ // ...add fake oscillators until total num is multiple of 4
+ for (int i = actPartNum; i < newPartNum; i++) {
+ oscillatorAmplitudes[i] = 0;
+ oscillatorNormFrequencies[i] = 0;
+ oscillatorAmplitudeDerivatives[i] = 0;
+ oscillatorNormFreqDerivatives[i] = 0;
+ oscillatorPhases[i] = 0;
+ }
+ // ...and update num of active partials
+ actPartNum = newPartNum;
+}
+
+void OscillatorBank::play(float vel) {
+ // set attack and release params according to velocity
+ //adsr.setAttackRate((minAttackTime + ((1 - vel) * deltaAttackTime)) * rate);
+ adsr.setAttackRate(minAttackTime * rate);
+ //adsr.setReleaseRate((minReleaseTime + (1 - vel) * deltaReleaseTime) * rate);
+ adsr.setReleaseRate(minReleaseTime * rate);
+
+ // set timbre
+ hopNumTh = log((1 - vel) + 1) / log(2) * 20000;
+
+ state = bank_toreset;
+}
+
+//---------------------------------------------------------------------------------------------------------------------------
+// private methods
+//---------------------------------------------------------------------------------------------------------------------------
+
+bool OscillatorBank::loader(char *filename, int hopsize, int samplerate) {
+ rate = samplerate;
+ loaded = parser.parseFile(filename, hopsize, samplerate);
+ return loaded;
+}
+
+int OscillatorBank::jumpToHop() {
+ int jumpGap = abs(jumpHop - currentHop / overSampling); // gaps in partial reference
+
+ // can't jump to self dude
+ if (jumpGap == 0)
+ return 1;
+
+ // direction is in general maintained with jump
+ if (jumpHop == 0)
+ setDirection(1);
+ else if (jumpHop == lastHop)
+ setDirection(-1);
+
+ dbox_printf("\tJump from %d to %d\n", currentHop / overSampling, jumpHop);
+ dbox_printf("\tdirection %d\n", loopDir);
+
+ currentHop = jumpHop * overSampling;
+
+ if (nextEnvState() != 0)
+ return 0; // release has ended!
+
+ checkSpeed();
+
+ int parIndex, localHop, targetHop;
+ float parDamp = 1;
+ int currentPartialHop = currentHop / overSampling;
+ int targetPartialHop = jumpHop;
+
+ actPartNum = partials->activePartialNum[currentPartialHop];
+ actPart = partials->activePartials[currentPartialHop];
+ int targetActParNum = partials->activePartialNum[targetPartialHop];
+ unsigned int *targetActPar = partials->activePartials[targetPartialHop];
+
+ envState = adsr.getState(); // to determine what state we will be in next hop [attack, decay, sustain, release]
+
+ int parCnt = 0;
+ int currentHopReminder = currentHop % overSampling;
+
+ // steps to walk where i am [bank of partial hop] from previous partial hop
+ int steps = currentHopReminder * (overSampling != 1); // no oversampling 0, oversampling and going ff currentHopReminder
+
+ for (int i = 0; i < actPartNum; i++) {
+ // find partial and frame
+ parIndex = actPart[i];
+ //localHop = partials->localPartialFrames[currentPartialHop][parIndex];
+ localHop = currentPartialHop - partials->partialStartFrame[parIndex]; // in Parser and Partials "frames" are what we call here "hops". These particular ones are local frames [see comment at top of file]
+
+ // if this partial was over nyquist on previous hop...
+ if (nyquistCut[parIndex]) {
+ // ...restart from safe values
+ oscillatorPhases[parCnt] = 0;
+ //TODO add freqmove dependency
+ oscillatorNextNormFreq[parCnt] =
+ (partials->partialFrequencies[parIndex][localHop]
+ + freqFixedDeltas[parIndex] * steps * loopDir)
+ * frequencyScaler * prevPitchMultiplier;
+ oscillatorNextAmp[parCnt] = 0;
+ } else if (loopDir == 1) {// otherwise recover phase, target freq and target amp from previous frame
+ if ((localHop != 0)
+ || ((overSampling > 1) && (currentHopReminder != 0))) {
+ oscillatorPhases[parCnt] =
+ phaseCopies[indicesMapping[parIndex]];
+ oscillatorNextNormFreq[parCnt] =
+ nextNormFreqCopies[indicesMapping[parIndex]];
+ oscillatorNextAmp[parCnt] =
+ nextAmpCopies[indicesMapping[parIndex]];
+ } else { // first oscillator hop [both for bank and partial], so no previous data are available
+ oscillatorPhases[parCnt] = 0;
+ //TODO add freqmove dependency
+ oscillatorNextNormFreq[parCnt] =
+ partials->partialFrequencies[parIndex][localHop]
+ * frequencyScaler * prevPitchMultiplier;
+ parDamp = calculateParDamping(parIndex, prevHopNumTh,
+ prevAdsrVal, oscillatorNextNormFreq[parCnt],
+ prevFilterNum, prevFilterFreqs, prevFilterQ);
+ oscillatorNextAmp[parCnt] =
+ partials->partialAmplitudes[parIndex][localHop]
+ * parDamp;
+ if(oscillatorNextAmp[parCnt] > 1)
+ oscillatorNextAmp[parCnt] = 1;
+ }
+ } else {
+ if (( (unsigned)localHop != partials->partialNumFrames[parIndex] - 1)
+ || ((overSampling > 1) && (currentHopReminder != 0))) {
+ oscillatorPhases[parCnt] =
+ phaseCopies[indicesMapping[parIndex]];
+ oscillatorNextNormFreq[parCnt] =
+ nextNormFreqCopies[indicesMapping[parIndex]];
+ oscillatorNextAmp[parCnt] =
+ nextAmpCopies[indicesMapping[parIndex]];
+ } else // first oscillator hop [going backwards - both for bank and partial] , so no previous data are available, so retrieve where i am
+ {
+ oscillatorPhases[parCnt] = 0;
+ //TODO add freqmove dependency
+ oscillatorNextNormFreq[parCnt] =
+ partials->partialFrequencies[parIndex][localHop]
+ * frequencyScaler * prevPitchMultiplier;
+ parDamp = calculateParDamping(parIndex, prevHopNumTh,
+ prevAdsrVal, oscillatorNextNormFreq[parCnt],
+ prevFilterNum, prevFilterFreqs, prevFilterQ);
+ oscillatorNextAmp[parCnt] =
+ partials->partialAmplitudes[parIndex][localHop]
+ * parDamp;
+ if(oscillatorNextAmp[parCnt] > 1)
+ oscillatorNextAmp[parCnt] = 1;
+ }
+ }
+ // remove aliasing, skipping partial over nyquist freq
+ if (oscillatorNextNormFreq[parCnt] > nyqNorm) {
+ //cout << nyqNorm << endl;
+ nyquistCut[parIndex] = true;
+ continue;
+ }
+ nyquistCut[parIndex] = false;
+
+ // check what happens of this partial at target hop
+ float targetFreqVal, targetAmpVal;
+ //targetHop = partials->localPartialFrames[targetPartialHop][parIndex];
+ targetHop = targetPartialHop - partials->partialStartFrame[parIndex];
+
+ if (targetHop == -1)
+ targetFreqVal = targetAmpVal = 0;
+ else {
+ targetFreqVal = partials->partialFrequencies[parIndex][targetHop]
+ * frequencyScaler; // pitch shift will be multiplied later!!!
+ targetAmpVal = partials->partialFrequencies[parIndex][targetHop]; // parDamp will be multiplied later!!!
+ }
+
+ // first set up freq, cos filter affects amplitude damping according to freq content
+ oscillatorNormFrequencies[parCnt] = oscillatorNextNormFreq[parCnt]; // to fix any possible drifts
+ // save next values, current for next round
+ oscillatorNextNormFreq[parCnt] = (freqMovement * targetFreqVal
+ + (1 - freqMovement) * oscStatNormFrequenciesMean[parIndex])
+ * pitchMultiplier;
+ // derivatives are (next hop value*next damping) - (current hop value*current damping) ---> next hop must be available, in both directions, because of control on active partials
+ oscillatorNormFreqDerivatives[parCnt] = (oscillatorNextNormFreq[parCnt]
+ - oscillatorNormFrequencies[parCnt]) / hopCounter;
+ // this second weird passage handles dissonance control, morphing between regular and mean frequencies
+ oscillatorNormFreqDerivatives[parCnt] = freqMovement
+ * oscillatorNormFreqDerivatives[parCnt]
+ + (1 - freqMovement)
+ * ((oscStatNormFrequenciesMean[parIndex]
+ * pitchMultiplier)
+ - oscillatorNormFrequencies[parCnt])
+ / hopCounter;
+
+ parDamp = calculateParDamping(parIndex, hopNumTh, adsrVal,
+ oscillatorNextNormFreq[parCnt], filterNum, filterFreqs, filterQ);
+
+ // now amplitudes
+ oscillatorAmplitudes[parCnt] = oscillatorNextAmp[parCnt]; // to fix any possible drifts
+ // save next values, current for next round
+ oscillatorNextAmp[parCnt] = targetAmpVal * parDamp;
+ if(oscillatorNextAmp[parCnt] > 1)
+ oscillatorNextAmp[parCnt] = 1;
+ // to avoid bursts when transients are played backwards
+ if ((loopDir == -1) && (targetHop == 0)
+ && ((overSampling == 1) || (currentHopReminder == 0))) {
+ oscillatorNextAmp[parCnt] = 0;
+ }
+ // derivatives are (next hop value*next damping) - (current hop value*current damping) ---> next hop must be available, in both directions, because of control on active partials
+ oscillatorAmplitudeDerivatives[parCnt] = (oscillatorNextAmp[parCnt]
+ - oscillatorAmplitudes[parCnt]) / hopCounter;
+
+ //if partial does not die at target, calculate deltas according to direction
+ if (targetHop != -1) {
+ freqFixedDeltas[parIndex] =
+ partials->partialFreqDelta[parIndex][targetHop] * loopDir
+ / overSampling;
+ ampFixedDeltas[parIndex] =
+ partials->partialAmpDelta[parIndex][targetHop] * loopDir
+ / overSampling;
+ }
+
+ // finally update current mapping between oscillators and partials
+ indicesMapping[parIndex] = parCnt;
+ parCnt++;
+ }
+ actPartNum = parCnt;
+
+ // now add the ones that start at target hop!
+ for (int i = 0; i < targetActParNum; i++) {
+ // find partial and frame
+ parIndex = targetActPar[i];
+ //targetHop = partials->localPartialFrames[targetPartialHop][parIndex];
+ targetHop = targetPartialHop - partials->partialStartFrame[parIndex]; // in Parser and Partials "frames" are what we call here "hops". These particular ones are local frames [see comment at top of file]
+
+ // check if this partials was already active before the jump
+ //localHop = partials->localPartialFrames[currentPartialHop][parIndex];
+ localHop = currentPartialHop - partials->partialStartFrame[parIndex];
+
+ // if yes, skip it
+ if (localHop != -1)
+ continue;
+
+ // otherwise add it to active bunch and calcucalte values
+
+ // first set up freq, cos filter affects amplitude damping according to freq content
+ oscillatorNormFrequencies[parCnt] = 0;
+ // save next values, current for next round
+ oscillatorNextNormFreq[parCnt] = (freqMovement
+ * partials->partialFrequencies[parIndex][targetHop]
+ * frequencyScaler
+ + (1 - freqMovement) * oscStatNormFrequenciesMean[parIndex])
+ * pitchMultiplier;
+ // derivatives are (next hop value*next damping) - (current hop value*current damping) ---> next hop must be available, in both directions, because of control on active partials
+ oscillatorNormFreqDerivatives[parCnt] = (oscillatorNextNormFreq[parCnt]
+ - oscillatorNormFrequencies[parCnt]) / hopCounter;
+ // this second weird passage handles dissonance control, morphing between regular and mean frequencies
+ oscillatorNormFreqDerivatives[parCnt] = freqMovement
+ * oscillatorNormFreqDerivatives[parCnt]
+ + (1 - freqMovement)
+ * ((oscStatNormFrequenciesMean[parIndex]
+ * pitchMultiplier)
+ - oscillatorNormFrequencies[parCnt])
+ / hopCounter;
+
+ parDamp = calculateParDamping(parIndex, hopNumTh, adsrVal,
+ oscillatorNextNormFreq[parCnt], filterNum, filterFreqs, filterQ);
+
+ // now amplitudes
+ oscillatorAmplitudes[parCnt] = 0;
+ // save next values, current for next round
+ oscillatorNextAmp[parCnt] =
+ partials->partialFrequencies[parIndex][targetHop] * parDamp;
+ if(oscillatorNextAmp[parCnt] > 1)
+ oscillatorNextAmp[parCnt] = 1;
+ // derivatives are (next hop value*next damping) - (current hop value*current damping) ---> next hop must be available, in both directions, because of control on active partials
+ oscillatorAmplitudeDerivatives[parCnt] = (oscillatorNextAmp[parCnt]
+ - oscillatorAmplitudes[parCnt]) / hopCounter;
+
+ //calculate deltas according to direction
+ freqFixedDeltas[parIndex] =
+ partials->partialFreqDelta[parIndex][targetHop] * loopDir
+ / overSampling;
+ ampFixedDeltas[parIndex] =
+ partials->partialAmpDelta[parIndex][targetHop] * loopDir
+ / overSampling;
+
+ // finally update current mapping between oscillators and partials
+ indicesMapping[parIndex] = parCnt;
+ parCnt++;
+
+ }
+ // [NEON] if not multiple of 4...
+ if (actPartNum % 4 != 0)
+ addFakeOsc();
+
+ updatePrevControls();
+
+ jumpHop = -1;
+
+ return 0;
+}
+
+int OscillatorBank::nextEnvState() {
+ /*
+ envState = Attack.getState(); // to determine what state we are in [attack, decay, sustain, release]
+
+ // osc bank is playing the tail and the tail ends...
+ if( (state == bank_playing)&&(envState == env_idle) )
+ {
+ state = bank_stopped; // ...stop bank
+ return 1; // and return immediately
+ }
+ else if( (envState == env_attack) || (envState == env_decay) )
+ {
+ // run envelopes until next frame
+ dampWeight = Attack.process(hopSize);
+ }
+ else if(envState == env_release)
+ {
+ // run envelopes until next frame
+ dampWeight = Attack.process(hopSize);
+ releaseDamp = Release.process(hopSize);
+ }*/
+
+ envState = adsr.getState();
+ // osc bank is playing the tail and the tail ends...
+ if ((state == bank_playing) && (envState == env_idle)) {
+ state = bank_stopped; // ...stop bank
+ adsrVal = 0;
+ return 1; // and return immediately
+ } else
+ adsrVal = adsr.process(hopSize);
+
+ return 0;
+}
+
+void OscillatorBank::checkDirection() {
+ // end of the loop or end of file
+ if (((currentHop >= loopEndHop) && (loopDir == 1))
+ || ((currentHop >= lastHop) && (loopDir == 1))) {
+ // move backwards
+ setDirection(-1);
+ //dbox_printf("backward from %d\n", loopEndHop);
+ } else if (((currentHop <= loopStartHop) && (loopDir == -1))
+ || ((currentHop <= 0) && (loopDir == -1))) // start of the loop or start of file
+ {
+ // move forward
+ setDirection(1);
+ //dbox_printf("forward from %d\n", loopStartHop);
+ }
+}
+
+void OscillatorBank::checkSpeed() {
+ // speed control [alike on highways, LOL]
+ if (nextSpeed > 0) {
+ nextSpeed = (nextSpeed < maxSpeed) ? nextSpeed : maxSpeed;
+ nextSpeed = (nextSpeed > minSpeed) ? nextSpeed : minSpeed;
+ speed = nextSpeed;
+ nextSpeed = -1;
+ }
+ hopCounter = hopSize / speed;
+}
+
+int OscillatorBank::checkJump() {
+ //check if has to jump somewhere
+ if (jumpHop > -1) {
+ // needs to jump!
+ if (jumpToHop() == 0)
+ return 0;
+ }
+ return 1; // no jump
+}
+
+bool OscillatorBank::checkOversampling() {
+ //TODO fix this, but need andrew to fix oversampling multiple of period size
+ // if partialsHopSize is not a multiple of oversampling, change hop size to periodically match next partial hop
+ if (hopSizeReminder > 0) {
+ // if next osc bank hop overtakes next partial hop...
+ if ((currentHop + loopDir) * hopSize > partialsHopSize) {
+ hopSize = hopSizeReminder; // ...shrink osc bank hop size to match partial hop
+ return true; // and set next hop as matching with next partial hop
+ }
+ } else if (((currentHop + (1 - loopDirShift)) % overSampling) == 0) // if next osc bank hop matches next partial hop
+ return true; // ...mark next hop as partial hop
+
+ return false; // ,otherwise mark next hop as osc bank hop
+}
+
+void OscillatorBank::updatePrevControls() {
+ prevAdsrVal = adsrVal;
+ prevAmpTh = ampTh;
+ prevHopNumTh = hopNumTh;
+ prevPitchMultiplier = pitchMultiplier;
+ prevFreqMovement = freqMovement;
+ prevFilterNum = filterNum;
+ memcpy(prevFilterFreqs, filterFreqs, filterNum * sizeof(float));
+ memcpy(prevFilterQ, filterQ, filterNum * sizeof(float));
+}
+
+float OscillatorBank::calculateParDamping(int parIndex, int hopNTh,
+ float adsrVl, float nextFreq, int filNum, float *filFreq, float *filQ) {
+ float parDamp = 1;
+
+ // timbre
+ parDamp = ((float) (oscStatNumHops[parIndex] + 1)) / (hopNTh + 1);
+ parDamp = (parDamp > 1) ? 1 : parDamp;
+ parDamp = adsrVl * parDamp;
+
+ //filters
+
+ float filterWeights[MAX_TOUCHES];
+ float filterDamp[MAX_TOUCHES];
+ float filDist;
+ float filterWeightsAcc;
+ float filDmp;
+ float filAmp;
+
+// band reject notch filter
+// float dist, dmp;
+// for(int k=0; k 0) {
+ // reset values
+ filDist = 0;
+ filterWeightsAcc = 0;
+ filDmp = 0;
+ filAmp = 0;
+ // for each filter
+ for (int k = 0; k < filNum; k++) {
+ // here are a couple of kludges to boost sound output of hi freq filters
+
+ // damping effect of filter increases with distance, but decreases with filter frequency [kludge]
+ float mul = ((filterMaxF-nextFreq)/filterMaxF) * 0.9 + 0.1 ;
+ //filDist = fabs(nextFreq - filFreq[k])*( ((exp(a*4)-1)/EXP_DENOM) * 0.9 + 0.1 );
+ filDist = fabs(nextFreq - filFreq[k])*mul;
+
+ // these to merge all filters contributions according to distance
+ filterWeights[k] = filterMaxF - filDist;
+ filterWeightsAcc += filterWeights[k];
+ // freqs very close to filter center are slightly amplified
+ // the size of this amp area and the effect of amplification increase with frequency [kludge]
+ if (filDist
+ < filterAmpMinF
+ + (filterAmpMaxF*(1-mul) - filterAmpMinF) * (1 - filQ[k]) )
+ filAmp = filQ[k] * filterAmpMul*(1-mul);
+ else
+ filAmp = 0;
+ // actual damping
+ filDmp = 1 / (filDist * filQ[k]);
+ filDmp = (filDmp > 1) ? 1 : filDmp;
+ // sum damp+amplification
+ filterDamp[k] = filDmp + filAmp;
+ }
+ // do weighted mean to merge all filters contributions
+ filDmp = 0;
+ for (int k = 0; k < filNum; k++)
+ filDmp += filterDamp[k] * filterWeights[k];
+ filDmp /= filterWeightsAcc;
+ // apply
+ parDamp *= filDmp;
+ }
+
+
+ return parDamp;
+}
diff -r 000000000000 -r 8a575ba3ab52 projects/d-box/OscillatorBank.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/projects/d-box/OscillatorBank.h Fri Oct 31 19:10:17 2014 +0100
@@ -0,0 +1,240 @@
+/*
+ * OscillatorBank.h
+ *
+ * Created on: May 23, 2014
+ * Author: Victor Zappi and Andrew McPherson
+ */
+
+#ifndef OSCILLATORBANK_H_
+#define OSCILLATORBANK_H_
+
+
+#include
+
+#include "spear_parser.h"
+#include "ADSR.h"
+#include "config.h"
+
+using namespace std;
+
+enum OscBankstates {bank_stopped, bank_playing, bank_toreset};
+
+class OscillatorBank
+{
+public:
+ OscillatorBank();
+ OscillatorBank(string filename, int hopsize=-1, int samplerate=44100);
+ OscillatorBank(char *filename, int hopsize=-1, int samplerate=44100);
+ ~OscillatorBank();
+ float *oscillatorPhases;
+ float *oscillatorNormFrequencies;
+ float *oscillatorNormFreqDerivatives;
+ float *oscillatorAmplitudes;
+ float *oscillatorAmplitudeDerivatives;
+ float *oscStatNormFrequenciesMean;
+ float *oscStatNumHops;
+ OscBankstates state;
+ bool note;
+ int actPartNum;
+ unsigned int *actPart;
+ int hopCounter;
+ int lookupTableSize;
+ float *lookupTable;
+ float ampTh;
+ int hopNumTh;
+ float pitchMultiplier;
+ float freqMovement;
+ int filterNum;
+ float filterFreqs[5];
+ float filterQ[5];
+ float filterMaxF;
+ float filterAmpMinF;
+ float filterAmpMaxF;
+ float filterAmpMul;
+
+ bool loadFile(string filename, int hopsize=-1, int samplerate=44100);
+ bool loadFile(char *filename, int hopsize=-1, int samplerate=44100);
+ bool initBank(int oversamp=1);
+ void resetOscillators();
+ int getHopSize() { return hopSize; }
+ void nextHop();
+ void setLoopHops(int start, int end);
+ void play(float vel);
+ void stop();
+ void afterTouch(float vel);
+ int getEnvelopeState();
+ float getFrequencyScaler();
+ void setSpeed(float sp);
+ float getSpeed();
+ float getMaxSpeed();
+ float getMinSpeed();
+ void setJumpHop(int hop);
+ int getLastHop();
+ int getCurrentHop() { return currentHop; }
+
+private:
+
+ bool loaded;
+ int numOfPartials;
+ int numOfOscillators;
+ int partialsHopSize;
+ int overSampling;
+ int hopSize;
+ int hopSizeReminder;
+ int oscBankHopSize;
+ float frequencyScaler;
+ float nyqNorm;
+ int lastHop;
+ int currentHop;
+ int loopDir;
+ int loopDirShift;
+ int loopStartHop;
+ int loopEndHop;
+ int *indicesMapping;
+ float *phaseCopies;
+ float *oscillatorNextNormFreq;
+ float *oscillatorNextAmp;
+ float *nextNormFreqCopies;
+ float *nextAmpCopies;
+ float *freqFixedDeltas;
+ float *ampFixedDeltas;
+ bool *nyquistCut;
+ Spear_parser parser;
+ Partials *partials;
+ ADSR adsr;
+ float minAttackTime;
+ float deltaAttackTime;
+ float minReleaseTime;
+ float deltaReleaseTime;
+ int envState;
+ int rate;
+ float speed;
+ float nextSpeed;
+ float maxSpeed;
+ float minSpeed;
+ int jumpHop;
+ float adsrVal;
+ float prevAdsrVal;
+ float prevAmpTh;
+ int prevHopNumTh;
+ float prevPitchMultiplier;
+ float prevFreqMovement;
+ int prevFilterNum;
+ float prevFilterFreqs[5];
+ float prevFilterQ[5];
+
+ bool loader(char *filename, int hopsize=-1, int samplerate=44100);
+ void addFakeOsc();
+ void nextOscBankHop();
+ void nextPartialHop();
+ int jumpToHop();
+ void setDirection(int dir);
+ int nextEnvState();
+ void checkDirection();
+ void checkSpeed();
+ int checkJump();
+ bool checkOversampling();
+ void updatePrevControls();
+ float calculateParDamping(int parIndex, int hopNTh, float adsrVl, float nextFreq,
+ int filNum, float *filFreq, float *filQ);
+};
+
+inline bool OscillatorBank::loadFile(string filename, int hopsize, int samplerate)
+{
+ return loader((char *)filename.c_str(), hopsize, samplerate);
+}
+
+inline bool OscillatorBank::loadFile(char *filename, int hopsize, int samplerate)
+{
+ return loader(filename, hopsize, samplerate);
+}
+
+inline void OscillatorBank::setLoopHops(int start, int end)
+{
+ if(start > end)
+ end = start;
+
+ if(start<0)
+ start = 0;
+ else if(start>lastHop)
+ start = 0;
+ if(end < 1)
+ end = 1;
+ end = (end<=lastHop) ? end : lastHop;
+
+ // set it, take into consideration hop oversampling
+ loopStartHop = start*overSampling;
+ loopEndHop = end*overSampling;
+}
+
+inline void OscillatorBank::stop()
+{
+ note = false;
+ adsr.gate(0);
+}
+
+inline float OscillatorBank::getFrequencyScaler()
+{
+ return frequencyScaler;
+}
+
+inline void OscillatorBank::afterTouch(float vel)
+{
+ hopNumTh = log((1-vel)+1)/log(2)*20000;
+ if(adsr.getState()==env_attack)
+ adsr.setAttackRate( (minAttackTime + ( (1-vel)*deltaAttackTime )) * rate );
+ adsr.setReleaseRate( (minReleaseTime+(1-vel)*deltaReleaseTime)* rate );
+}
+
+inline int OscillatorBank::getEnvelopeState()
+{
+ return envState;
+}
+
+inline void OscillatorBank::setSpeed(float sp)
+{
+ nextSpeed = sp;
+}
+
+inline float OscillatorBank::getSpeed()
+{
+ return speed;
+}
+
+inline float OscillatorBank::getMaxSpeed()
+{
+ return maxSpeed;
+}
+
+inline float OscillatorBank::getMinSpeed()
+{
+ return minSpeed;
+}
+
+inline void OscillatorBank::setJumpHop(int hop)
+{
+ if(hop<0)
+ return;
+ hop = (hop<=lastHop) ? hop : lastHop;
+ jumpHop = hop;
+}
+
+inline void OscillatorBank::setDirection(int dir)
+{
+ if(dir>=0)
+ {
+ loopDir = 1;
+ loopDirShift = 0;
+ }
+ else
+ {
+ loopDir = -1;
+ loopDirShift = 1;
+ }
+}
+
+inline int OscillatorBank::getLastHop()
+{
+ return lastHop;
+}
+#endif /* OSCILLATORBANK_H_ */
diff -r 000000000000 -r 8a575ba3ab52 projects/d-box/PinkNoise.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/projects/d-box/PinkNoise.cpp Fri Oct 31 19:10:17 2014 +0100
@@ -0,0 +1,16 @@
+/*
+ * PinkNoise.cpp
+ *
+ * Created on: Oct 15, 2013
+ * Author: Victor Zappi
+ */
+
+#include "PinkNoise.h"
+
+// miserable definition to init static const array members...otherwise gets error when PinkNoise.h is included into another header file
+const float PinkNoise::A[] = { 0.02109238, 0.07113478, 0.68873558 }; // rescaled by (1+P)/(1-P)
+const float PinkNoise::P[] = { 0.3190, 0.7756, 0.9613 };
+
+
+
+
diff -r 000000000000 -r 8a575ba3ab52 projects/d-box/PinkNoise.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/projects/d-box/PinkNoise.h Fri Oct 31 19:10:17 2014 +0100
@@ -0,0 +1,49 @@
+#ifndef _PinkNoise_H
+#define _PinkNoise_H
+
+// Technique by Larry "RidgeRat" Trammell 3/2006
+// http://home.earthlink.net/~ltrammell/tech/pinkalg.htm
+// implementation and optimization by David Lowenfels
+
+#include
+#include
+#include
+
+#define PINK_NOISE_NUM_STAGES 3
+
+class PinkNoise {
+public:
+ PinkNoise() {
+ srand ( time(NULL) ); // initialize random generator
+ clear();
+ }
+
+ void clear() {
+ for( size_t i=0; i< PINK_NOISE_NUM_STAGES; i++ )
+ state[ i ] = 0.0;
+ }
+
+ float tick() {
+ static const float RMI2 = 2.0 / float(RAND_MAX); // + 1.0; // change for range [0,1)
+ static const float offset = A[0] + A[1] + A[2];
+
+ // unrolled loop
+ float temp = float( rand() );
+ state[0] = P[0] * (state[0] - temp) + temp;
+ temp = float( rand() );
+ state[1] = P[1] * (state[1] - temp) + temp;
+ temp = float( rand() );
+ state[2] = P[2] * (state[2] - temp) + temp;
+ return ( A[0]*state[0] + A[1]*state[1] + A[2]*state[2] )*RMI2 - offset;
+ }
+
+protected:
+ float state[ PINK_NOISE_NUM_STAGES ];
+ static const float A[ PINK_NOISE_NUM_STAGES ];
+ static const float P[ PINK_NOISE_NUM_STAGES ];
+};
+
+//const float PinkNoise::A[] = { 0.02109238, 0.07113478, 0.68873558 }; // rescaled by (1+P)/(1-P)
+//const float PinkNoise::P[] = { 0.3190, 0.7756, 0.9613 };
+
+#endif
diff -r 000000000000 -r 8a575ba3ab52 projects/d-box/StatusLED.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/projects/d-box/StatusLED.cpp Fri Oct 31 19:10:17 2014 +0100
@@ -0,0 +1,90 @@
+/*
+ * StatusLED.cpp
+ *
+ * Routines for manipulating the status LED
+ *
+ * (c) 2014 Andrew McPherson and Victor Zappi
+ * QMUL, Centre for Digital Music
+ */
+
+#include
+#include "StatusLED.h"
+#include "../../include/GPIOcontrol.h"
+
+extern int gShouldStop;
+extern int gVerbose;
+
+using namespace std;
+
+StatusLED::StatusLED() {
+ gpio_number = -1;
+ milliseconds_on = 0;
+ milliseconds_off = 100;
+ blink_thread = -1;
+}
+
+StatusLED::~StatusLED() {
+ if(gpio_number >= 0) {
+ this_should_stop = true;
+ pthread_join(blink_thread, NULL);
+ gpio_unexport(gpio_number);
+ }
+}
+
+bool StatusLED::init(int gpio_pin) {
+ gpio_number = gpio_pin;
+ this_should_stop = false;
+
+ if(gpio_export(gpio_number)) {
+ if(gVerbose)
+ cout << "Warning: couldn't export status LED pin\n";
+ }
+ if(gpio_set_dir(gpio_number, OUTPUT_PIN)) {
+ if(gVerbose)
+ cout << "Couldn't set direction on status LED pin\n";
+ return false;
+ }
+ if(gpio_set_value(gpio_number, LOW)) {
+ if(gVerbose)
+ cout << "Couldn't set value on status LED pin\n";
+ return false;
+ }
+
+
+ if ( pthread_create(&blink_thread, NULL, static_blink_loop, this) )
+ {
+ cout << "Error:unable to create status LED thread" << endl;
+ return false;
+ }
+
+ return true;
+}
+
+void StatusLED::on() {
+ milliseconds_on = 100;
+ milliseconds_off = 0;
+}
+
+void StatusLED::off() {
+ milliseconds_on = 0;
+ milliseconds_off = 100;
+}
+
+void StatusLED::blink(int ms_on, int ms_off) {
+ milliseconds_on = ms_on;
+ milliseconds_off = ms_off;
+}
+
+void* StatusLED::blink_loop(void *) {
+ while(!gShouldStop && !this_should_stop) {
+ if(milliseconds_on != 0)
+ gpio_set_value(gpio_number, HIGH);
+ usleep(1000 * milliseconds_on);
+ if(gShouldStop)
+ break;
+ if(milliseconds_off != 0)
+ gpio_set_value(gpio_number, LOW);
+ usleep(1000 * milliseconds_off);
+ }
+ pthread_exit(NULL);
+}
diff -r 000000000000 -r 8a575ba3ab52 projects/d-box/StatusLED.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/projects/d-box/StatusLED.h Fri Oct 31 19:10:17 2014 +0100
@@ -0,0 +1,38 @@
+/*
+ * StatusLED.h
+ *
+ *
+ */
+
+#ifndef STATUSLED_H_
+#define STATUSLED_H_
+
+#include
+
+class StatusLED
+{
+public:
+ StatusLED();
+ ~StatusLED();
+
+ bool init(int gpio_pin);
+
+ void on();
+ void off();
+ void blink(int ms_on, int ms_off);
+
+ static void *static_blink_loop(void *data) {
+ ((StatusLED*)data)->blink_loop(NULL);
+ return 0;
+ }
+
+ void* blink_loop(void *);
+
+private:
+ int gpio_number;
+ int milliseconds_on, milliseconds_off;
+ bool this_should_stop;
+ pthread_t blink_thread;
+};
+
+#endif // STATUSLED_H_
diff -r 000000000000 -r 8a575ba3ab52 projects/d-box/audio_routines.S
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/projects/d-box/audio_routines.S Fri Oct 31 19:10:17 2014 +0100
@@ -0,0 +1,185 @@
+@
+@ audio_routines.S
+@
+@ NEON-based functions for time-critical audio processing
+@
+@ Andrew McPherson 2014
+@ Queen Mary University of London
+@
+
+ .syntax unified
+ .arch armv7-a
+ .fpu neon
+
+@ void oscillator_bank_neon(int numAudioFrames, float *audioOut,
+@ int activePartialNum, int lookupTableSize,
+@ float *phases, float *frequencies, float *amplitudes,
+@ float *freqDerivatives, float *ampDerivatives,
+@ float *lookupTable);
+
+@ Registers:
+@ r0: numAudioFrames How many frames to render
+@ r1: audioOut Buffer for audio output samples [stereo]
+@ r2: activePartialNum How many active partials to render
+@ r3: lookupTableSize Size of lookup table
+@ ---- other arguments start on the stack and are moved: -----
+@ r4: phases Phase of each oscillator (pointer)
+@ r5: frequencies Normalised frequency of each oscillator (pointer)
+@ r6: amplitudes Normalised amplitude of each oscillator (pointer)
+@ r7: freqDerivatives Derivative of frequency for each oscillator (pointer)
+@ r8: ampDerivatives Derivative of amplitude for each oscillator (pointer)
+@ r9: lookupTable Lookup table containing one oscillation
+@
+@ Alignment requirements:
+@ audioOut: 8-byte boundary
+@ phases: 16-byte boundary
+@ frequencies: 16-byte boundary
+@ amplitudes: 16-byte boundary
+@ freqDerivatives: 16-byte bounary
+@ ampDerivatives: 16-byte boundary
+@ lookupTable: 4-byte boundary (TODO: check this)
+
+ .align 2
+ .global oscillator_bank_neon
+ .thumb
+ .thumb_func
+ .type oscillator_bank_neon, %function
+oscillator_bank_neon:
+
+
+dSample .dn D6.F32
+qPhases .qn Q8.F32
+dPhases_0 .dn D16.F32
+dPhases_1 .dn D17.F32
+qFreqs .qn Q9.F32
+dFreqs_0 .dn D18.F32
+dFreqs_1 .dn D19.F32
+qAmps .qn Q10.F32
+dAmps_0 .dn D20.F32
+dAmps_1 .dn D21.F32
+qFreqDs .qn Q11.F32
+dFreqDs_0 .dn D22.F32
+dFreqDs_1 .dn D23.F32
+qAmpDs .qn Q12.F32
+dAmpDs_0 .dn D24.F32
+dAmpDs_1 .dn D25.F32
+
+qBaseInts .qn Q13.U32 @ Base indexes: unsigned ints x4
+dBaseInts_0 .dn D26.U32
+dBaseInts_1 .dn D27.U32
+qFractions .qn Q14.F32 @ Fraction indexes: floats x4
+qTableBase .qn Q15.U32 @ Base of lookup table
+
+ cmp r0, #0 @ Check for trivial case 1: zero frames
+ it eq
+ bxeq lr @ Return if that's the case (otherwise might have odd behaviour)
+ cmp r2, #4 @ Check for trivial case 2: zero oscillators
+ it lt
+ bxlt lr @ Return if that's the case
+
+ push {r4-r11} @ Now arguments start 32 bytes above SP
+ add r11, sp, #32 @ Pointer to 32 bytes into the stack
+ ldm r11, {r4-r9} @ Load 6 arguments into registers
+
+ vdup qTableBase, r9 @ Move lookup table base index into 4 ints
+
+ @ Outer loop: iterate over the number of oscillators, choosing 4 at a
+ @ time to work with.
+oscbank_oscillator_loop:
+ vld1 {dPhases_0, dPhases_1}, [r4] @ no increment; will store at end of sample loop
+ vld1 {dFreqs_0, dFreqs_1}, [r5]
+ vld1 {dAmps_0, dAmps_1}, [r6]
+ vld1 {dFreqDs_0, dFreqDs_1}, [r7]! @ increment; won't update at end of sample loop
+ vld1 {dAmpDs_0, dAmpDs_1}, [r8]!
+
+ push {r0-r1,r4-r8}
+ @ --- inner loop: iterate over the number of samples ---
+oscbank_sample_loop:
+ vcvt qBaseInts, qPhases @ Take floor(phases)
+ vmov q2.f32, #1.0 @ Load 1.0 into every slot of q2
+ vshl q0.U32, qBaseInts, #2 @ Shift the indexes left 2 (*4 for float addressing)
+ vcvt qFractions, qBaseInts @ int back to float
+ vadd q0.U32, q0.U32, qTableBase @ Find memory addresses
+
+ vmov r4, r5, d0 @ Move two indexes to ARM registers
+ vmov r6, r7, d1 @ Move two more indexes to ARM registers
+ vsub qFractions, qPhases, qFractions @ fraction = phase - floor(phase)
+
+ vldr.64 d0, [r4] @ Load two consecutive floats at each location
+ vldr.64 d1, [r5] @ These hold the previous and following samples in the table
+ vldr.64 d2, [r6] @ TODO: check whether these work at 4-byte alignment
+ vldr.64 d3, [r7]
+
+ @ Format at this point:
+ @ Osc0(before) Osc0(after) Osc1(before) Osc1(after) Osc2(before) Osc2(after) Osc3(before) Osc3(after)
+ @ We want:
+ @ Osc0(before) Osc1(before) Osc2(before) Osc3(before) Osc0(after) Osc1(after) Osc2(after) Osc3(after)
+
+ vuzp.32 q0, q1 @ Now q0 contains before, q1 contains after
+ vsub q2.f32, q2.f32, qFractions @ q2 = 1.0 - fraction
+ vmul q1.f32, q1.f32, qFractions @ q1 = fraction * after
+ vmul q0.f32, q0.f32, q2.f32 @ q0 = (1.0 - fraction) * before
+
+ vadd qPhases, qPhases, qFreqs @ Update phases
+ vadd qFreqs, qFreqs, qFreqDs @ Update frequencies
+
+ vadd q0.f32, q0.f32, q1.f32 @ Add two interpolated components to get the final sample
+ vdup q2.u32, r3 @ Put lookup table size into each element of q2
+ vcvt qBaseInts, qPhases @ Take floor of new phases
+ vmul q0.f32, q0.f32, qAmps @ Multiply samples by current amplitude
+
+ vld1 dSample, [r1] @ Load the current stereo samples
+ vpadd d2.f32, d0.f32, d1.f32 @ Pairwise accumulate q0 (output sample) into d2
+
+ vand q2, q2, qBaseInts @ Logical AND of new phase int leaves 1 bit set only if phase >= table size
+ vpadd d3.f32, d2.f32, d2.f32 @ Pairwise accumulate d2 into d0 --> d0[0] and d0[1] both hold total of 4 oscillators
+ vadd qAmps, qAmps, qAmpDs @ Update amplitudes
+ vcvt q0.f32, q2.u32 @ Convert int back to float after AND operation
+
+ vadd dSample, dSample, d3.f32 @ Add oscillator outputs to each channel
+
+ subs r0, r0, #1 @ numFrames--
+ vsub qPhases, qPhases, q0.f32 @ Keep phases in table range
+ vst1 dSample, [r1]! @ Store back in buffer and increment by 8
+
+ it gt
+ bgt oscbank_sample_loop @ Loop if numFrames > 0
+
+ @ --- end inner loop ---
+ pop {r0-r1,r4-r8} @ Restore registers: restores audioOut and numFrames, among others
+
+ vst1 {dPhases_0, dPhases_1}, [r4]! @ Store phases back to array
+ vst1 {dFreqs_0, dFreqs_1}, [r5]! @ Store frequencies back to array
+ vst1 {dAmps_0, dAmps_1}, [r6]! @ Store amplitudes back to array
+ @ No need to update r7, r8
+
+ subs r2, r2, #4 @ numPartials -= 4
+ it gt
+ bgt oscbank_oscillator_loop @ Loop if numPartials > 0
+
+ pop {r4-r11}
+ bx lr
+
+
+@ void wavetable_interpolate_neon(int numSamplesIn, int numSamplesOut,
+@ float *tableIn, float *tableOut);
+
+@ Registers:
+@ r0: numSamplesIn Size of the input table
+@ r1: numSamplesOut Size of the output table
+@ r2: tableIn Pointer to input table
+@ r3: tableOut Pointer to output table
+
+@ Alignment requirements:
+@ tableIn: 8-byte boundary
+@ tableOut: 8-byte boundary
+
+ .align 2
+ .global wavetable_interpolate_neon
+ .thumb
+ .thumb_func
+ .type wavetable_interpolate_neon, %function
+wavetable_interpolate_neon:
+ @ TODO
+
+ bx lr
diff -r 000000000000 -r 8a575ba3ab52 projects/d-box/config.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/projects/d-box/config.h Fri Oct 31 19:10:17 2014 +0100
@@ -0,0 +1,46 @@
+/*
+ * config.h
+ *
+ * Global settings for D-Box project
+ *
+ * Andrew McPherson and Victor Zappi 2014
+ */
+
+
+#ifndef DBOX_CONFIG_H_
+#define DBOX_CONFIG_H_
+
+
+/* Number of maximum touches used by the TouchKey sensors */
+#define MAX_TOUCHES 5
+
+// for sensor 1 filter
+#define EXP_DENOM 53.5981500331 // exp(4)-1
+
+/* Define this to use Xenomai real-time extensions */
+#define DBOX_USE_XENOMAI
+//#define OLD_OSCBANK
+
+/* Define this if the new cape is in use (changes pinouts and I2C address) */
+#define DBOX_CAPE
+
+#ifdef DBOX_USE_XENOMAI
+// Xenomai-specific includes
+#include
+
+#include
+#include
+#include
+#endif
+
+#ifdef DBOX_USE_XENOMAI
+
+#define dbox_printf rt_printf
+
+#else
+
+#define dbox_printf printf
+
+#endif
+
+#endif /* DBOX_CONFIG_H */
diff -r 000000000000 -r 8a575ba3ab52 projects/d-box/logger.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/projects/d-box/logger.cpp Fri Oct 31 19:10:17 2014 +0100
@@ -0,0 +1,188 @@
+/*
+ * logger.cpp
+ *
+ * Created on: Aug 6, 2014
+ * Author: VIctor Zappi and Andrew McPherson
+ */
+
+#include "logger.h"
+
+// main extern vars
+extern bool gShouldStop;
+extern int gVerbose;
+
+// file nanme extern vars
+extern char gId;
+extern char gGroup;
+
+
+// logged extern vars
+extern int s0TouchNum;
+extern float s0Touches_[MAX_TOUCHES];
+extern float s0Size_[MAX_TOUCHES];
+extern int s0LastIndex;
+
+extern int s1TouchNum;
+extern float s1Touches_[MAX_TOUCHES];
+extern float s1Size_[MAX_TOUCHES];
+extern int s1LastIndex;
+
+extern int fsr;
+
+
+
+string logPath = "/boot/uboot/instrumentLog";
+string logFileIncipit = "/datalog";
+string logFileName = "";
+ofstream logFile;
+timeval logTimeVal;
+unsigned long long logTimeOrig;
+int logCnt = 0; // counts how many lines so far
+int logCntSwap = 50; // how many log lines before closing and re-opening the file
+
+
+// create the log file, using incremental name convention
+int initLogLoop()
+{
+ if(gVerbose==1)
+ cout << "---------------->Init Log Thread" << endl;
+
+
+ // transform chars into strings via stringstream objs
+ stringstream id_ss, group_ss, freedom_ss;
+ id_ss << gId;
+ group_ss << gGroup;
+
+ int logNum = -1;
+ int logMax = -1;
+ int pathLen = logPath.length() + logFileIncipit.length() + 4; // + 4 is: "_", id, group, "_"
+ glob_t globbuf;
+
+ // check how many log files are already there, and choose name according to this
+ glob( (logPath + logFileIncipit + "*").c_str(), 0, NULL, &globbuf);
+
+ // cycle through all and find the highest index
+ for(unsigned int i=0; i logMax)
+ logMax = logNum;
+ }
+ logNum = logMax + 1; // new index
+
+ globfree(&globbuf);
+
+ ostringstream numString;
+ numString << setw (4) << setfill ('0') << logNum; // set integer with 4 figures
+
+ // here are the new names: PATH + DIR + INCIPIT + _ + id + group + freedom + _ + NUM (4figures) + _A.txt
+ logFileName = logPath + logFileIncipit;
+ logFileName += "_" + id_ss.str() + group_ss.str() + freedom_ss.str();
+ logFileName += "_" + numString.str(); //static_cast( &(ostringstream() << logNum) )->str();
+ logFileName += ".txt";
+
+
+ // create new files
+ FILE *fp_a = fopen(logFileName.c_str(), "wb");
+ if(!fp_a)
+ {
+ dbox_printf("Cannot create files...\n");
+ return 2;
+ }
+ fclose(fp_a);
+
+ // ready to append
+ logFile.open(logFileName.c_str(), ios::out | ios::app);
+
+ dbox_printf("Logging on file %s\n", logFileName.c_str());
+
+ return 0;
+}
+
+
+void writeData(unsigned long long time)
+{
+
+ float fsr_ = ((float)(1799-fsr)/1799.0);
+ logFile << time << "\t" // timestamp
+ << s0TouchNum << "\t"; // sensor 0 touch count
+ for(int i=0; i= logCntSwap)
+ {
+ logFile.close(); // close file, dump stream
+ logCnt = 0; // ready for another whole round
+
+ // open again, ready to append
+ logFile.open(logFileName.c_str(), ios::out | ios::app);
+ }
+
+ writeData(time);
+
+ logCnt++;
+}
+
+
+
+
+void *logLoop(void *)
+{
+ set_realtime_priority(10);
+
+ if(gVerbose==1)
+ dbox_printf("_________________Log Thread!\n");
+
+ // get time reference
+ gettimeofday(&logTimeVal, NULL);
+ logData(0);
+
+ logTimeOrig = logTimeVal.tv_usec;
+ logTimeOrig *= 0.001; // from usec to msec
+ logTimeOrig += logTimeVal.tv_sec*1000; // from sec to msec
+
+ usleep(5000);
+
+ while(!gShouldStop)
+ {
+ gettimeofday(&logTimeVal, NULL);
+ unsigned long long currentTime = logTimeVal.tv_usec;
+ currentTime *= 0.001; // from usec to msec
+ currentTime += logTimeVal.tv_sec*1000; // from sec to msec
+
+ logData(currentTime-logTimeOrig);
+
+ usleep(5000);
+ }
+
+ if(logFile!=NULL)
+ logFile.close();
+
+ dbox_printf("log thread ended\n");
+
+ return (void *)0;
+}
diff -r 000000000000 -r 8a575ba3ab52 projects/d-box/logger.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/projects/d-box/logger.h Fri Oct 31 19:10:17 2014 +0100
@@ -0,0 +1,30 @@
+/*
+ * logger.h
+ *
+ * Created on: Aug 6, 2014
+ * Author: Victor Zappi and Andrew McPherson
+ */
+
+#ifndef LOGGER_H_
+#define LOGGER_H_
+
+#include
+#include
+#include
+#include // file handle
+#include // stringstream
+#include // stringstream
+#include // alternative to dirent.h to handle files in dirs
+#include // setfill
+#include // elapsed time
+
+#include "config.h"
+#include "prio.h"
+
+using namespace std;
+
+int initLogLoop();
+void *logLoop(void *);
+
+
+#endif /* LOGGER_H_ */
diff -r 000000000000 -r 8a575ba3ab52 projects/d-box/main.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/projects/d-box/main.cpp Fri Oct 31 19:10:17 2014 +0100
@@ -0,0 +1,490 @@
+/*
+ * RTAudio.cpp
+ *
+ * Central control code for hard real-time audio on BeagleBone Black
+ * using PRU and Xenomai Linux extensions. This code began as part
+ * of the Hackable Instruments project (EPSRC) at Queen Mary University
+ * of London, 2013-14.
+ *
+ * (c) 2014 Victor Zappi and Andrew McPherson
+ * Queen Mary University of London
+ */
+
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include // interrupt handler
+#include
+#include
+#include // to handle files in dirs
+#include // to check if device is mounted
+#include // mount()
+#include // elapsed time
+#include // neon library
+
+// thread priority
+#include
+#include
+
+// get_opt_long
+#include
+
+#include "../../include/RTAudio.h"
+#include "config.h"
+#include "sensors.h"
+#include "OscillatorBank.h"
+#include "StatusLED.h"
+#include "logger.h"
+
+using namespace std;
+
+//----------------------------------------
+// main variables
+//----------------------------------------
+vector gOscBanks;
+int gCurrentOscBank = 0;
+int gNextOscBank = 0;
+int oscBnkOversampling = 1; // oscillator bank frame oversampling
+
+const int kStatusLEDPin = 30; // P9-11 controls status LED
+StatusLED gStatusLED;
+
+pthread_t keyboardThread;
+pthread_t logThread;
+
+// general settings
+int gVerbose = 0; // verbose flag
+bool forceKeyboard = true; // activate/deactivate keyboard control
+bool forceSensors = false; // activate/deactivate sensor control
+bool forceLog = true; // activate/deactivate log on boot partition
+bool useSD = true; // activate/deactivate file loading from SD [as opposed to emmc]
+bool useAudioTest = false; // activate/deactivate sensors and test audio only
+
+// audio settings
+unsigned int gPeriodSize = 8; // period size for audio
+char* gPartialFilename = 0; // name of the partials file to load
+bool gAudioIn = false; // stereo audio in status
+
+int touchSensor0Address = 0x0C; // I2C addresses of touch sensors
+int touchSensor1Address = 0x0B;
+bool useNewSensors = false;
+
+char sdPath[256] = "/dev/mmcblk0p2"; // system path of the SD, partition 2
+char mountPath[256] = "/root/d-box/usersounds"; // mount point of SD partition 2 [where user files are]
+char gUserDirName[256] = "usersounds"; // Directory in which user analysis files can be found [dir of mountPath]
+char gDefaultDirName[256] = "sounds"; // Directory in which built in analysis files can be found
+char *gDirName;
+bool gIsLoading = false;
+int fileCnt = 0;
+std::vector files;
+
+char gId = 'f'; // from 0 to 14, hexadecimal [0-d]! f means not set
+char gGroup = '2'; // 0 is no info, 1 info. 2 is not set
+
+// audio in filter
+extern ne10_float32_t *filterState[2];
+extern ne10_float32_t *filterIn[2];
+extern ne10_float32_t *filterOut[2];
+
+struct arg_data
+{
+ int argc;
+ char **argv;
+};
+
+arg_data args;
+
+
+int readFiles()
+{
+ if(useSD)
+ gDirName = gUserDirName;
+ else
+ gDirName = gDefaultDirName;
+ DIR *dir;
+ struct dirent *ent;
+
+ // From http://stackoverflow.com/questions/612097/how-can-i-get-a-list-of-files-in-a-directory-using-c-or-c
+ if ((dir = opendir (gDirName)) != NULL) {
+ /* print all the files and directories within directory */
+ while ((ent = readdir (dir)) != NULL) {
+ // Ignore dotfiles and . and .. paths
+ if(!strncmp(ent->d_name, ".", 1))
+ continue;
+
+ //dbox_printf("%s\n", ent->d_name);
+
+ // take only .dbx and .txt files
+ string name = string(ent->d_name);
+ int len = name.length();
+
+ bool dboxFile = false;
+
+ if( (name[len-4]=='.') && (name[len-3]=='d') && (name[len-2]=='b') && (name[len-1]=='x') )
+ dboxFile = true;
+ if( (name[len-4]=='.') && (name[len-3]=='t') && (name[len-2]=='x') && (name[len-1]=='t') )
+ dboxFile = true;
+
+ if(dboxFile)
+ {
+ fileCnt++;
+ //dbox_printf("%s\n", ent->d_name);
+ files.push_back( std::string( ent->d_name ) );
+ }
+ }
+ closedir (dir);
+ } else {
+ /* could not open directory */
+ printf("Could not open directory %s\n", gDirName);
+ return 1;
+ }
+
+ // order by name
+ std::sort( files.begin(), files.end() );
+
+ if(fileCnt==0)
+ {
+ printf("No .dbx or .txt files in %s!\n", gDirName);
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Load sounds from the directory */
+void loadAudioFiles(bool loadFirstFile)
+{
+ char fullFileName[256];
+
+ if(loadFirstFile) {
+ strcpy(fullFileName, gDirName);
+ strcat(fullFileName, "/");
+ strncat(fullFileName, files[0].c_str(), 255 - strlen(gDirName));
+ dbox_printf("Loading first file %s...\n", fullFileName);
+ OscillatorBank *bank = new OscillatorBank(fullFileName);
+ if(bank->initBank(oscBnkOversampling)) {
+ bank->setLoopHops(100, bank->getLastHop());
+ gOscBanks.push_back(bank);
+ }
+ }
+
+ else {
+ for(int i=1; iinitBank(oscBnkOversampling)) {
+ bank->setLoopHops(100, bank->getLastHop());
+ gOscBanks.push_back(bank);
+ }
+ }
+ }
+}
+
+// adapted from http://program-nix.blogspot.co.uk/2008/08/c-language-check-filesystem-is-mounted.html
+int checkIfMounted (char * dev_path)
+{
+ FILE * mtab = NULL;
+ struct mntent * part = NULL;
+ int is_mounted = 0;
+
+ if ( ( mtab = setmntent ("/etc/mtab", "r") ) != NULL)
+ {
+ while ( ( part = getmntent ( mtab) ) != NULL)
+ {
+ if ( ( part->mnt_fsname != NULL ) && ( strcmp ( part->mnt_fsname, dev_path ) ) == 0 )
+ is_mounted = 1;
+ }
+ endmntent(mtab);
+ }
+ return is_mounted;
+}
+
+int mountSDuserPartition()
+{
+ if(checkIfMounted(sdPath))
+ {
+ printf("device %s already mounted, fair enough, let's move on\n", sdPath);
+ return 0;
+ }
+ // if mount rootfs from SD [rootfs eMMC not used] or from eMMC via properly formatted SD [SD rootfs used as storage volume]
+ // we always use rootfs on SD as storage volume ----> "/dev/mmcblk0p2"
+ int ret = mount(sdPath, "/root/d-box/usersounds", "vfat", 0, NULL);
+ if (ret!=0)
+ {
+ printf("Error in mount...%s\n", strerror(ret));
+ return 1;
+ }
+ return 0;
+}
+
+int initSoundFiles()
+{
+ if(gVerbose==1)
+ cout << "---------------->Init Audio Thread" << endl;
+
+ if(useSD)
+ {
+ // mount the SD partition where user sounds are located
+ // [this is p2, p1 is already mounted and we will log data there]
+ if(mountSDuserPartition()!=0)
+ return -1;
+ }
+
+ gIsLoading = true;
+
+ // read files from SD and order them alphabetically
+ if(readFiles()!=0)
+ return 1;
+
+ // load first file into oscBank
+ loadAudioFiles(true);
+
+ return 0;
+}
+
+//---------------------------------------------------------------------------------------------------------
+
+// Handle Ctrl-C
+void interrupt_handler(int var)
+{
+ // kill keyboard thread mercilessly
+ if(forceKeyboard)
+ pthread_cancel(keyboardThread);
+
+ gShouldStop = true;
+}
+
+
+void parseArguments(arg_data args)
+{
+ // Default filename;
+ gPartialFilename = strdup("D-Box_sound_250_60_40_h88_2.txt");
+
+ // TODO: complete this
+ struct option long_option[] =
+ {
+ {"help", 0, NULL, 'h'},
+ {"period", 1, NULL, 'p'},
+ {"verbose", 1, NULL, 'v'},
+ {"audioin", 1, NULL, 'i'},
+ {"file", 1, NULL, 'f'},
+ {"keyboard", 1, NULL, 'k'},
+ {"audio-test", 0, NULL, 'A'},
+ {"new-sensors", 0, NULL, 'S'},
+ {"sensor0", 1, NULL, 'Q'},
+ {"sensor1", 1, NULL, 'R'},
+ {"log", 1, NULL, 'l'},
+ {"usesd", 1, NULL, 'u'},
+ {"oversamp", 1, NULL, 'o'},
+ {"boxnumber", 1, NULL, 'n'},
+ {"group", 1, NULL, 'g'},
+ {NULL, 0, NULL, 0},
+ };
+ int morehelp = 0;
+ int tmp = -1;
+
+ while (1)
+ {
+ int c;
+ if ((c = getopt_long(args.argc, args.argv, "hp:vf:ki:sAQ:R:Sl:u:o:n:g:", long_option, NULL)) < 0)
+ break;
+ switch (c)
+ {
+ case 'h':
+ morehelp++;
+ break;
+ case 'p':
+ gPeriodSize = atoi(optarg);
+ break;
+ case 'v':
+ gVerbose = 1;
+ break;
+ case 'f':
+ free(gPartialFilename);
+ gPartialFilename = strdup(optarg);
+ break;
+ case 'k':
+ forceKeyboard = true;
+ break;
+ case 'i':
+ gAudioIn = (atoi(optarg)==0) ? false : true;
+ break;
+ case 's':
+ forceSensors = true;
+ break;
+ case 'A':
+ useAudioTest = true;
+ break;
+ case 'S':
+ useNewSensors = true;
+ break;
+ case 'Q':
+ touchSensor0Address = atoi(optarg);
+ break;
+ case 'R':
+ touchSensor1Address = atoi(optarg);
+ break;
+ case 'l':
+ tmp = atoi(optarg);
+ if(tmp==0)
+ forceLog = false;
+ else if(tmp>0)
+ forceLog = true;
+ break;
+ case 'u':
+ tmp = atoi(optarg);
+ if(tmp==0)
+ useSD = false;
+ else if(tmp>0)
+ useSD = true;
+ break;
+ case 'o':
+ oscBnkOversampling = atoi(optarg);
+ break;
+ case 'n':
+ gId = *optarg;
+ cout << "-set box number to: " << gId << endl;
+ break;
+ case 'g':
+ gGroup = *optarg;
+ cout << "-set group to: " << gId << endl;
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ RT_TASK rtSensorThread;
+ const char rtSensorThreadName[] = "dbox-sensor";
+ int oscBankHopSize;
+
+ // Parse command-line arguments
+ args.argc = argc;
+ args.argv = argv;
+ parseArguments(args);
+
+ setVerboseLevel(gVerbose);
+ if(gVerbose == 1 && useAudioTest)
+ cout << "main() : running in audio test mode" << endl;
+
+ // Load sound files from directory
+ if(initSoundFiles() != 0)
+ return -1;
+
+ oscBankHopSize = gOscBanks[gCurrentOscBank]->getHopSize()/gOscBanks[gCurrentOscBank]->getMinSpeed();
+
+ // Initialise the audio device
+ if(initAudio(gPeriodSize, 1, &oscBankHopSize) != 0)
+ return -1;
+
+ // Initialise the status LED
+ if(!gStatusLED.init(kStatusLEDPin)) {
+ if(gVerbose)
+ cout << "Couldn't initialise status LED pin\n";
+ }
+
+ // Free file name string which is no longer needed
+ if(gPartialFilename != 0)
+ free(gPartialFilename);
+
+ if(!useAudioTest) {
+ if(initSensorLoop(touchSensor0Address, touchSensor1Address, useNewSensors) != 0)
+ return -1;
+ }
+
+ if(gVerbose == 1)
+ cout << "main() : creating audio thread" << endl;
+
+ if(startAudio()) {
+ cout << "Error: unable to start real-time audio" << endl;
+ return -1;
+ }
+
+ // LED on...
+ gStatusLED.on();
+
+ if(forceSensors && !useAudioTest) {
+ if(gVerbose==1)
+ cout << "main() : creating control thread" << endl;
+
+ if(rt_task_create(&rtSensorThread, rtSensorThreadName, 0, 95, T_JOINABLE | T_FPU)) {
+ cout << "Error:unable to create Xenomai control thread" << endl;
+ return -1;
+ }
+ if(rt_task_start(&rtSensorThread, &sensorLoop, 0)) {
+ cout << "Error:unable to start Xenomai control thread" << endl;
+ return -1;
+ }
+ }
+
+ if(forceKeyboard) {
+ if(gVerbose==1)
+ cout << "main() : creating keyboard thread" << endl;
+
+ if ( pthread_create(&keyboardThread, NULL, keyboardLoop, NULL) ) {
+ cout << "Error:unable to create keyboard thread" << endl;
+ return -1;
+ }
+ }
+
+ if(forceLog) {
+ if(gVerbose==1)
+ cout << "main() : creating log thread" << endl;
+
+ if(initLogLoop()!=0) {
+ cout << "Error:unable to create log thread" << endl;
+ return -1;
+ }
+
+ if ( pthread_create(&logThread, NULL, logLoop, NULL) ) {
+ cout << "Error:unable to create keyboard thread" << endl;
+ return -1;
+ }
+ }
+
+ // Set up interrupt handler to catch Control-C
+ signal(SIGINT, interrupt_handler);
+
+ // load all other files into oscBanks
+ loadAudioFiles(false);
+ cout << "Finished loading analysis files\n";
+ gIsLoading = false;
+
+ // Run until told to stop
+ while(!gShouldStop) {
+ usleep(100000);
+ }
+
+ stopAudio();
+
+ if(!useAudioTest)
+ rt_task_join(&rtSensorThread);
+
+ cleanupAudio();
+
+ pthread_join( keyboardThread, NULL);
+ pthread_join( logThread, NULL);
+
+ for(unsigned int i = 0; i < gOscBanks.size(); i++)
+ delete gOscBanks[i];
+
+ NE10_FREE(filterState[0]);
+ NE10_FREE(filterState[1]);
+ NE10_FREE(filterIn[0]);
+ NE10_FREE(filterIn[1]);
+ NE10_FREE(filterOut[0]);
+ NE10_FREE(filterOut[1]);
+
+ printf("Program ended\nBye bye\n");
+ return 0;
+}
diff -r 000000000000 -r 8a575ba3ab52 projects/d-box/prio.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/projects/d-box/prio.cpp Fri Oct 31 19:10:17 2014 +0100
@@ -0,0 +1,48 @@
+/*
+ * prio.cpp
+ *
+ * Created on: May 14, 2014
+ * Author: Victor Zappi
+ */
+
+#include "prio.h"
+using namespace std;
+//-----------------------------------------------------------------------------------------------------------
+// set wanted real-time priority to this thread
+//-----------------------------------------------------------------------------------------------------------
+void set_realtime_priority(int order)
+{
+ int ret;
+
+ // We'll operate on the currently running thread.
+ pthread_t this_thread = pthread_self();
+ // struct sched_param is used to store the scheduling priority
+ struct sched_param params;
+ // We'll set the priority to the maximum.
+ params.sched_priority = sched_get_priority_max(SCHED_FIFO) - order;
+
+ // Attempt to set thread real-time priority to the SCHED_FIFO policy
+ ret = pthread_setschedparam(this_thread, SCHED_FIFO, ¶ms);
+ if (ret != 0) {
+ // Print the error
+ cout << "Unsuccessful in setting thread realtime prio" << endl;
+ return;
+ }
+
+ // Now verify the change in thread priority
+ int policy = 0;
+ ret = pthread_getschedparam(this_thread, &policy, ¶ms);
+ if (ret != 0) {
+ cout << "Couldn't retrieve real-time scheduling parameters" << endl;
+ return;
+ }
+
+ // Check the correct policy was applied
+ if(policy != SCHED_FIFO) {
+ cout << "Scheduling is NOT SCHED_FIFO!" << endl;
+ }
+}
+
+//-----------------------------------------------------------------------------------------------------------
+
+
diff -r 000000000000 -r 8a575ba3ab52 projects/d-box/prio.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/projects/d-box/prio.h Fri Oct 31 19:10:17 2014 +0100
@@ -0,0 +1,22 @@
+/*
+ * prio.h
+ *
+ * Created on: May 14, 2014
+ * Author: Victor Zappi
+ */
+
+#ifndef PRIO_H_
+#define PRIO_H_
+
+
+#include
+#include
+#include
+
+//-----------------------------------------------------------------------------------------------------------
+// set maximum real-time priority to this thread
+//-----------------------------------------------------------------------------------------------------------
+void set_realtime_priority(int order);
+//-----------------------------------------------------------------------------------------------------------
+
+#endif /* PRIO_H_ */
diff -r 000000000000 -r 8a575ba3ab52 projects/d-box/render.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/projects/d-box/render.cpp Fri Oct 31 19:10:17 2014 +0100
@@ -0,0 +1,841 @@
+/*
+ * render.cpp
+ *
+ * Created on: May 28, 2014
+ * Author: Victor Zappi
+ */
+
+#include "../../include/RTAudio.h"
+#include "../../include/PRU.h"
+#include "StatusLED.h"
+#include "config.h"
+#include "OscillatorBank.h"
+#include "FeedbackOscillator.h"
+#include "ADSR.h"
+#include "FIRfilter.h"
+#include
+#include
+#include
+
+#undef DBOX_CAPE_TEST
+
+#define N_OCT 4.0 // maximum number of octaves on sensor 1
+
+extern vector gOscBanks;
+extern int gCurrentOscBank;
+extern int gNextOscBank;
+extern PRU *gPRU;
+extern StatusLED gStatusLED;
+extern bool gIsLoading;
+extern bool gAudioIn;
+extern int gPeriodSize;
+int gChannels = 2;
+
+float *gOscillatorBuffer1, *gOscillatorBuffer2;
+float *gOscillatorBufferRead, *gOscillatorBufferWrite;
+int gOscillatorBufferReadPointer = 0;
+int gOscillatorBufferReadCurrentSize = 0;
+int gOscillatorBufferWriteCurrentSize = 0;
+bool gOscillatorNeedsRender = false;
+
+int gMatrixSampleCount = 0; // How many samples have elapsed on the matrix
+
+// Wavetable which changes in response to an oscillator
+float *gDynamicWavetable;
+int gDynamicWavetableLength;
+bool gDynamicWavetableNeedsRender = false;
+
+// These variables handle the hysteresis oscillator used for setting the playback speed
+bool gSpeedHysteresisOscillatorRising = false;
+int gSpeedHysteresisLastTrigger = 0;
+
+// These variables handle the feedback oscillator used for controlling the wavetable
+FeedbackOscillator gFeedbackOscillator;
+float *gFeedbackOscillatorTable;
+int gFeedbackOscillatorTableLength;
+
+// This comes from sensor.cpp where it records the most recent touch location on
+// sensor 0.
+extern float gSensor0LatestTouchPos;
+extern int gSensor0LatestTouchNum;
+uint16_t gPitchLatestInput = 0;
+
+extern float gSensor1LatestTouchPos[];
+//extern float gSensor1LatestTouchSizes[];
+extern int gSensor1LatestTouchCount;
+extern int gSensor1LatestTouchIndex;
+int gSensor1LastTouchIndex = -1;
+int gSensor1InputDelayCounter = -1;
+int gSensor1InputIndex = 0;
+float gSensor1MatrixTouchPos[5] = {0};
+
+// FSR value from matrix input
+extern int gLastFSRValue;
+
+// Loop points from matrix input 4
+const int gLoopPointsInputBufferSize = 256;
+uint16_t gLoopPointsInputBuffer[gLoopPointsInputBufferSize];
+int gLoopPointsInputBufferPointer = 0;
+int gLoopPointMin = 0, gLoopPointMax = 0;
+
+// multiplier to activate or mute audio in
+int audioInStatus = 0;
+
+// xenomai timer
+SRTIME prevChangeNs = 0;
+
+// pitch vars
+float octaveSplitter;
+u_int16_t semitones[((int)N_OCT*12)+1];
+float deltaTouch = 0;
+float deltaWeightP = 0.5;
+float deltaWeightI = 0.0005;
+
+// filter vars
+ne10_fir_instance_f32_t filter[2];
+ne10_float32_t *filterIn[2];
+ne10_float32_t *filterOut[2];
+ne10_uint32_t blockSize;
+ne10_float32_t *filterState[2];
+ne10_float32_t prevFiltered[2];
+int filterGain = 80;
+ADSR PeakBurst[2];
+float peak[2];
+float peakThresh = 0.2;
+
+// Tasks for lower-priority calculation
+AuxiliaryTask gMediumPriorityRender, gLowPriorityRender;
+
+
+extern "C" {
+ // Function prototype for ARM assembly implementation of oscillator bank
+ void oscillator_bank_neon(int numAudioFrames, float *audioOut,
+ int activePartialNum, int lookupTableSize,
+ float *phases, float *frequencies, float *amplitudes,
+ float *freqDerivatives, float *ampDerivatives,
+ float *lookupTable);
+
+ void wavetable_interpolate_neon(int numSamplesIn, int numSamplesOut,
+ float *tableIn, float *tableOut);
+}
+
+void wavetable_interpolate(int numSamplesIn, int numSamplesOut,
+ float *tableIn, float *tableOut,
+ float *sineTable, float sineMix);
+
+inline uint16_t hysteresis_oscillator(uint16_t input, uint16_t risingThreshold,
+ uint16_t fallingThreshold, bool *rising);
+
+#ifdef DBOX_CAPE_TEST
+void render_capetest(int numMatrixFrames, int numAudioFrames, float *audioIn, float *audioOut,
+ uint16_t *matrixIn, uint16_t *matrixOut);
+#endif
+
+bool initialise_render(int numChannels, int numMatrixFramesPerPeriod, int numAudioFramesPerPeriod, float matrixSampleRate, float audioSampleRate, void *userData) {
+ gChannels = numChannels;
+ int oscBankHopSize = *(int *)userData;
+
+ // Allocate two buffers for rendering oscillator bank samples
+ // One will be used for writing in the background while the other is used for reading
+ // on the audio thread. 8-byte alignment needed for the NEON code.
+ if(posix_memalign((void **)&gOscillatorBuffer1, 8, oscBankHopSize * gChannels * sizeof(float))) {
+ printf("Error allocating render buffers\n");
+ return false;
+ }
+ if(posix_memalign((void **)&gOscillatorBuffer2, 8, oscBankHopSize * gChannels * sizeof(float))) {
+ printf("Error allocating render buffers\n");
+ return false;
+ }
+ gOscillatorBufferWrite = gOscillatorBuffer1;
+ gOscillatorBufferRead = gOscillatorBuffer2;
+
+ memset(gOscillatorBuffer1, 0, oscBankHopSize * gChannels * sizeof(float));
+ memset(gOscillatorBuffer2, 0, oscBankHopSize * gChannels * sizeof(float));
+
+ // Initialise the dynamic wavetable used by the oscillator bank
+ // It should match the size of the static one already allocated in the OscillatorBank object
+ // Don't forget a guard point at the end of the table
+ gDynamicWavetableLength = gOscBanks[gCurrentOscBank]->lookupTableSize;
+ if(posix_memalign((void **)&gDynamicWavetable, 8, (gDynamicWavetableLength + 1) * sizeof(float))) {
+ printf("Error allocating wavetable\n");
+ return false;
+ }
+
+ gFeedbackOscillator.initialise(8192, 10.0, matrixSampleRate);
+
+ for(int n = 0; n < gDynamicWavetableLength + 1; n++)
+ gDynamicWavetable[n] = 0;
+
+ // pitch
+ float midPos = (float)65535/2.0;
+ octaveSplitter = round((float)65535/(N_OCT));
+ int numOfSemi = 12*N_OCT;
+ int middleSemitone = 12*N_OCT/2;
+ int lastSemitone = middleSemitone+numOfSemi/2;
+ float inc = (float)65535/(N_OCT*12.0);
+ int i = -1;
+ for(int semi=middleSemitone; semi<=lastSemitone; semi++)
+ semitones[semi] = ( midPos + (++i)*inc) + 0.5;
+ i = 0;
+ for(int semi=middleSemitone-1; semi>=0; semi--)
+ semitones[semi] = ( midPos - (++i)*inc) + 0.5;
+
+ if(gAudioIn)
+ audioInStatus = 1;
+
+ // filter
+ blockSize = 2*gPeriodSize;
+ filterState[0] = (ne10_float32_t *) NE10_MALLOC ((FILTER_TAP_NUM+blockSize-1) * sizeof (ne10_float32_t));
+ filterState[1] = (ne10_float32_t *) NE10_MALLOC ((FILTER_TAP_NUM+blockSize-1) * sizeof (ne10_float32_t));
+ filterIn[0] = (ne10_float32_t *) NE10_MALLOC (blockSize * sizeof (ne10_float32_t));
+ filterIn[1] = (ne10_float32_t *) NE10_MALLOC (blockSize * sizeof (ne10_float32_t));
+ filterOut[0] = (ne10_float32_t *) NE10_MALLOC (blockSize * sizeof (ne10_float32_t));
+ filterOut[1] = (ne10_float32_t *) NE10_MALLOC (blockSize * sizeof (ne10_float32_t));
+ ne10_fir_init_float(&filter[0], FILTER_TAP_NUM, filterTaps, filterState[0], blockSize);
+ ne10_fir_init_float(&filter[1], FILTER_TAP_NUM, filterTaps, filterState[1], blockSize);
+
+ // peak outputs
+ PeakBurst[0].setAttackRate(.00001 * matrixSampleRate);
+ PeakBurst[1].setAttackRate(.00001 * matrixSampleRate);
+ PeakBurst[0].setDecayRate(.5 * matrixSampleRate);
+ PeakBurst[1].setDecayRate(.5 * matrixSampleRate);
+ PeakBurst[0].setSustainLevel(0.0);
+ PeakBurst[1].setSustainLevel(0.0);
+
+ // Initialise auxiliary tasks
+ if((gMediumPriorityRender = createAuxiliaryTaskLoop(&render_medium_prio, 90, "dbox-calculation-medium")) == 0)
+ return false;
+ if((gLowPriorityRender = createAuxiliaryTaskLoop(&render_low_prio, 85, "dbox-calculation-low")) == 0)
+ return false;
+
+ return true;
+}
+
+void render(int numMatrixFrames, int numAudioFrames, float *audioIn, float *audioOut,
+ uint16_t *matrixIn, uint16_t *matrixOut)
+{
+#ifdef DBOX_CAPE_TEST
+ render_capetest(numMatrixFrames, numAudioFrames, audioIn, audioOut, matrixIn, matrixOut);
+#else
+ if(gOscBanks[gCurrentOscBank]->state==bank_toreset)
+ gOscBanks[gCurrentOscBank]->resetOscillators();
+
+ if(gOscBanks[gCurrentOscBank]->state==bank_playing)
+ {
+ assert(gChannels == 2);
+
+#ifdef OLD_OSCBANK
+ memset(audioOut, 0, numAudioFrames * gChannels * sizeof(float));
+
+ /* Render the oscillator bank. The oscillator bank function is written in NEON assembly
+ * and it strips out all extra checks, so find out in advance whether we can render a whole
+ * block or whether the frame will increment in the middle of this buffer.
+ */
+
+ int framesRemaining = numAudioFrames;
+ float *audioOutWithOffset = audioOut;
+
+ while(framesRemaining > 0) {
+ if(gOscBanks[gCurrentOscBank]->hopCounter >= framesRemaining) {
+ /* More frames left in this hop than we need this time. Render and finish */
+ oscillator_bank_neon(framesRemaining, audioOutWithOffset,
+ gOscBanks[gCurrentOscBank]->actPartNum, gOscBanks[gCurrentOscBank]->lookupTableSize,
+ gOscBanks[gCurrentOscBank]->oscillatorPhases, gOscBanks[gCurrentOscBank]->oscillatorNormFrequencies,
+ gOscBanks[gCurrentOscBank]->oscillatorAmplitudes,
+ gOscBanks[gCurrentOscBank]->oscillatorNormFreqDerivatives,
+ gOscBanks[gCurrentOscBank]->oscillatorAmplitudeDerivatives,
+ gDynamicWavetable/*gOscBanks[gCurrentOscBank]->lookupTable*/);
+ gOscBanks[gCurrentOscBank]->hopCounter -= framesRemaining;
+ if(gOscBanks[gCurrentOscBank]->hopCounter <= 0)
+ gOscBanks[gCurrentOscBank]->nextHop();
+ framesRemaining = 0;
+ }
+ else {
+ /* More frames to render than are left in this hop. Render and decrement the
+ * number of remaining frames; then advance to the next oscillator frame.
+ */
+ oscillator_bank_neon(gOscBanks[gCurrentOscBank]->hopCounter, audioOutWithOffset,
+ gOscBanks[gCurrentOscBank]->actPartNum, gOscBanks[gCurrentOscBank]->lookupTableSize,
+ gOscBanks[gCurrentOscBank]->oscillatorPhases, gOscBanks[gCurrentOscBank]->oscillatorNormFrequencies,
+ gOscBanks[gCurrentOscBank]->oscillatorAmplitudes,
+ gOscBanks[gCurrentOscBank]->oscillatorNormFreqDerivatives,
+ gOscBanks[gCurrentOscBank]->oscillatorAmplitudeDerivatives,
+ gDynamicWavetable/*gOscBanks[gCurrentOscBank]->lookupTable*/);
+ framesRemaining -= gOscBanks[gCurrentOscBank]->hopCounter;
+ audioOutWithOffset += gChannels * gOscBanks[gCurrentOscBank]->hopCounter;
+ gOscBanks[gCurrentOscBank]->sampleCount += gOscBanks[gCurrentOscBank]->hopCounter;
+ gOscBanks[gCurrentOscBank]->nextHop();
+ }
+ }
+#else
+ for(int n = 0; n < numAudioFrames; n++) {
+ audioOut[2*n] = gOscillatorBufferRead[gOscillatorBufferReadPointer++]+audioIn[2*n]*audioInStatus;
+ audioOut[2*n + 1] = gOscillatorBufferRead[gOscillatorBufferReadPointer++]+audioIn[2*n+1]*audioInStatus;
+
+ filterIn[0][n] = fabs(audioIn[2*n]); // rectify for peak detection in 1
+ filterIn[1][n] = fabs(audioIn[2*n+1]); // rectify for peak detection in 2
+
+ /* FIXME why doesn't this work? */
+ /*
+ if(gOscillatorBufferReadPointer == gOscillatorBufferCurrentSize/2) {
+ gOscillatorNeedsRender = true;
+ scheduleAuxiliaryTask(gLowPriorityRender);
+ } */
+
+ if(gOscillatorBufferReadPointer >= gOscillatorBufferReadCurrentSize) {
+ // Finished reading from the buffer: swap to the next buffer
+ if(gOscillatorBufferRead == gOscillatorBuffer1) {
+ gOscillatorBufferRead = gOscillatorBuffer2;
+ gOscillatorBufferWrite = gOscillatorBuffer1;
+ }
+ else {
+ gOscillatorBufferRead = gOscillatorBuffer1;
+ gOscillatorBufferWrite = gOscillatorBuffer2;
+ }
+
+ // New buffer size is whatever finished writing last hop
+ gOscillatorBufferReadCurrentSize = gOscillatorBufferWriteCurrentSize;
+ gOscillatorBufferReadPointer = 0;
+
+ gOscillatorNeedsRender = true;
+ scheduleAuxiliaryTask(gMediumPriorityRender);
+ }
+ }
+#endif
+ }
+ else
+ {
+ for(int n = 0; n < numAudioFrames; n++) {
+ audioOut[2*n] = audioIn[2*n]*audioInStatus;
+ audioOut[2*n + 1] = audioIn[2*n+1]*audioInStatus;
+
+ filterIn[0][n] = fabs(audioIn[2*n]); // rectify for peak detection in 1
+ filterIn[1][n] = fabs(audioIn[2*n+1]); // rectify for peak detection in 2
+ }
+ }
+
+ // low pass filter audio in 1 and 2 for peak detection
+ ne10_fir_float_neon(&filter[0], filterIn[0], filterOut[0], blockSize);
+ ne10_fir_float_neon(&filter[1], filterIn[1], filterOut[1], blockSize);
+
+ for(int n = 0; n < numMatrixFrames; n++) {
+
+
+ /* Matrix Out 0, In 0
+ *
+ * CV loop
+ * Controls pitch of sound
+ */
+ int touchPosInt = gSensor0LatestTouchPos * 65536.0;
+ if(touchPosInt < 0) touchPosInt = 0;
+ if(touchPosInt > 65535) touchPosInt = 65535;
+ matrixOut[n*8 + DAC_PIN0] = touchPosInt;
+
+ gPitchLatestInput = matrixIn[n*8 + ADC_PIN0];
+
+
+ /* Matrix Out 7
+ *
+ * Loop feedback with Matrix In 0
+ * Controls discreet pitch
+ */
+ float deltaTarget = 0;
+ int semitoneIndex = 0;
+ if(gSensor0LatestTouchNum>0)
+ {
+ // current pitch is gPitchLatestInput, already retrieved
+ semitoneIndex = ( ( (float)gPitchLatestInput / 65535)*12*N_OCT )+0.5; // closest semitone
+ deltaTarget = (semitones[semitoneIndex]-gPitchLatestInput); // delta between pitch and target
+ deltaTouch += deltaTarget*deltaWeightI; // update feedback [previous + current]
+ }
+ else
+ deltaTouch = 0;
+
+ int nextOut = touchPosInt + deltaTarget*deltaWeightP + deltaTouch; // add feedback to touch -> next out
+ if(nextOut < 0) nextOut = 0; // clamp
+ if(nextOut > 65535) nextOut = 65535; // clamp
+ matrixOut[n*8 + DAC_PIN7] = nextOut; // send next nextOut
+
+
+ /*
+ * Matrix Out 1, In 1
+ *
+ * Hysteresis (comparator) oscillator
+ * Controls speed of playback
+ */
+ bool wasRising = gSpeedHysteresisOscillatorRising;
+ matrixOut[n*8 + DAC_PIN1] = hysteresis_oscillator(matrixIn[n*8 + ADC_PIN1], 48000, 16000, &gSpeedHysteresisOscillatorRising);
+
+ // Find interval of zero crossing
+ if(wasRising && !gSpeedHysteresisOscillatorRising) {
+ int interval = gMatrixSampleCount - gSpeedHysteresisLastTrigger;
+
+ // Interval since last trigger will be the new hop size; calculate to set speed
+ if(interval < 1)
+ interval = 1;
+ //float speed = (float)gOscBanks[gCurrentOscBank]->getHopSize() / (float)interval;
+ float speed = 144.0 / interval; // Normalise to a fixed expected speed
+ gOscBanks[gCurrentOscBank]->setSpeed(speed);
+
+ gSpeedHysteresisLastTrigger = gMatrixSampleCount;
+ }
+
+ /*
+ * Matrix Out 2, In 2
+ *
+ * Feedback (phase shift) oscillator
+ * Controls wavetable used for oscillator bank
+ */
+
+ int tableLength = gFeedbackOscillator.process(matrixIn[n*8 + ADC_PIN2], &matrixOut[n*8 + DAC_PIN2]);
+ if(tableLength != 0) {
+ gFeedbackOscillatorTableLength = tableLength;
+ gFeedbackOscillatorTable = gFeedbackOscillator.wavetable();
+ gDynamicWavetableNeedsRender = true;
+ scheduleAuxiliaryTask(gLowPriorityRender);
+ }
+
+ /*
+ * Matrix Out 3, In 3
+ *
+ * CV loop with delay for time alignment
+ * Touch positions from sensor 1
+ * Change every 32 samples (ca. 1.5 ms)
+ */
+ volatile int touchCount = gSensor1LatestTouchCount;
+ if(touchCount == 0)
+ matrixOut[n*8 + DAC_PIN3] = 0;
+ else {
+ int touchIndex = (gMatrixSampleCount >> 5) % touchCount;
+ matrixOut[n*8 + DAC_PIN3] = gSensor1LatestTouchPos[touchIndex] * 56000.0f;
+ if(touchIndex != gSensor1LastTouchIndex) {
+ // Just changed to a new touch output. Reset the counter.
+ // It will take 2*matrixFrames samples for this output to come back to the
+ // ADC input. But we also want to read near the end of the 32 sample block;
+ // let's say 24 samples into it.
+
+ // FIXME this won't work for p > 2
+ gSensor1InputDelayCounter = 24 + 2*numMatrixFrames;
+ gSensor1InputIndex = touchIndex;
+ }
+ gSensor1LastTouchIndex = touchIndex;
+ }
+
+ if(gSensor1InputDelayCounter-- >= 0 && touchCount > 0) {
+ gSensor1MatrixTouchPos[gSensor1InputIndex] = (float)matrixIn[n*8 + ADC_PIN3] / 65536.0f;
+ }
+
+ /* Matrix Out 4
+ *
+ * Sensor 1 last pos
+ */
+ touchPosInt = gSensor1LatestTouchPos[gSensor1LatestTouchIndex] * 65536.0;
+ if(touchPosInt < 0) touchPosInt = 0;
+ if(touchPosInt > 65535) touchPosInt = 65535;
+ matrixOut[n*8 + DAC_PIN4] = touchPosInt;
+
+ /* Matrix In 4
+ *
+ * Loop points selector
+ */
+ gLoopPointsInputBuffer[gLoopPointsInputBufferPointer++] = matrixIn[n*8 + ADC_PIN4];
+ if(gLoopPointsInputBufferPointer >= gLoopPointsInputBufferSize) {
+ // Find min and max values
+ uint16_t loopMax = 0, loopMin = 65535;
+ for(int i = 0; i < gLoopPointsInputBufferSize; i++) {
+ if(gLoopPointsInputBuffer[i] < loopMin)
+ loopMin = gLoopPointsInputBuffer[i];
+ if(gLoopPointsInputBuffer[i] > loopMax/* && gLoopPointsInputBuffer[i] != 65535*/)
+ loopMax = gLoopPointsInputBuffer[i];
+ }
+
+ if(loopMin >= loopMax)
+ loopMax = loopMin;
+
+ gLoopPointMax = loopMax;
+ gLoopPointMin = loopMin;
+ gLoopPointsInputBufferPointer = 0;
+ }
+
+ /* Matrix Out 5
+ *
+ * Audio In 1 peak detection and peak burst output
+ */
+
+ filterOut[0][n*2+1] *= filterGain;
+ float burstOut = PeakBurst[0].getOutput();
+ if( burstOut < 0.1)
+ {
+ if( (prevFiltered[0]>=peakThresh) && (prevFiltered[0]>=filterOut[0][n*2+1]) )
+ {
+ peak[0] = prevFiltered[0];
+ PeakBurst[0].gate(1);
+ }
+ }
+
+ PeakBurst[0].process(1);
+
+ int convAudio = burstOut*peak[0]*65535;
+ matrixOut[n*8 + DAC_PIN5] = convAudio;
+ prevFiltered[0] = filterOut[0][n*2+1];
+ if(prevFiltered[0]>1)
+ prevFiltered[0] = 1;
+
+ /* Matrix In 5
+ *
+ * Dissonance, via changing frequency motion of partials
+ */
+ float amount = (float)matrixIn[n*8 + ADC_PIN5] / 65536.0f;
+ gOscBanks[gCurrentOscBank]->freqMovement = 1-amount;
+
+
+
+
+ /* Matrix Out 6
+ *
+ * Audio In 2 peak detection and peak burst output
+ */
+
+ filterOut[1][n*2+1] *= filterGain;
+ burstOut = PeakBurst[1].getOutput();
+ if( burstOut < 0.1)
+ {
+ if( (prevFiltered[1]>=peakThresh) && (prevFiltered[1]>=filterOut[1][n*2+1]) )
+ {
+ peak[1] = prevFiltered[1];
+ PeakBurst[1].gate(1);
+ }
+ }
+
+ PeakBurst[1].process(1);
+
+ convAudio = burstOut*peak[1]*65535;
+ matrixOut[n*8 + DAC_PIN6] = convAudio;
+ prevFiltered[1] = filterOut[1][n*2+1];
+ if(prevFiltered[1]>1)
+ prevFiltered[1] = 1;
+
+ /* Matrix In 6
+ *
+ * Sound selector
+ */
+ if(!gIsLoading) {
+ // Use hysteresis to avoid jumping back and forth between sounds
+ if(gOscBanks.size() > 1) {
+ int input = matrixIn[n*8 + ADC_PIN6];
+ const int hystValue = 16000;
+
+ int upHysteresisValue = ((gCurrentOscBank + 1) * 65536 + hystValue) / gOscBanks.size();
+ int downHysteresisValue = (gCurrentOscBank * 65536 - hystValue) / gOscBanks.size();
+
+ if(input > upHysteresisValue || input < downHysteresisValue) {
+ gNextOscBank = input * gOscBanks.size() / 65536;
+ if(gNextOscBank < 0)
+ gNextOscBank = 0;
+ if((unsigned)gNextOscBank >= gOscBanks.size())
+ gNextOscBank = gOscBanks.size() - 1;
+ }
+ }
+ }
+
+ /*
+ * Matrix In 7
+ *
+ * FSR from primary touch sensor
+ * Value ranges from 0-1799
+ */
+ gLastFSRValue = matrixIn[n*8 + ADC_PIN7] * (1799.0 / 65535.0);
+ //gLastFSRValue = 1799 - matrixIn[n*8 + ADC_PIN7] * (1799.0 / 65535.0);
+ //dbox_printf("%i\n",gLastFSRValue);
+
+ gMatrixSampleCount++;
+ }
+
+#endif /* DBOX_CAPE_TEST */
+}
+
+// Medium-priority render function used for audio hop calculations
+void render_medium_prio()
+{
+
+ if(gOscillatorNeedsRender) {
+ gOscillatorNeedsRender = false;
+
+ /* Render one frame into the write buffer */
+ memset(gOscillatorBufferWrite, 0, gOscBanks[gCurrentOscBank]->hopCounter * gChannels * sizeof(float));
+
+ oscillator_bank_neon(gOscBanks[gCurrentOscBank]->hopCounter, gOscillatorBufferWrite,
+ gOscBanks[gCurrentOscBank]->actPartNum, gOscBanks[gCurrentOscBank]->lookupTableSize,
+ gOscBanks[gCurrentOscBank]->oscillatorPhases, gOscBanks[gCurrentOscBank]->oscillatorNormFrequencies,
+ gOscBanks[gCurrentOscBank]->oscillatorAmplitudes,
+ gOscBanks[gCurrentOscBank]->oscillatorNormFreqDerivatives,
+ gOscBanks[gCurrentOscBank]->oscillatorAmplitudeDerivatives,
+ /*gOscBanks[gCurrentOscBank]->lookupTable*/gDynamicWavetable);
+
+ gOscillatorBufferWriteCurrentSize = gOscBanks[gCurrentOscBank]->hopCounter * gChannels;
+
+ /* Update the pitch right before the hop
+ * Total CV range +/- N_OCT octaves
+ */
+ float pitch = (float)gPitchLatestInput / octaveSplitter - N_OCT/2;
+ //gOscBanks[gCurrentOscBank]->pitchMultiplier = powf(2.0f, pitch);
+ gOscBanks[gCurrentOscBank]->pitchMultiplier = pow(2.0f, pitch);
+
+#ifdef FIXME_LATER // This doesn't work very well yet
+ gOscBanks[gCurrentOscBank]->filterNum = gSensor1LatestTouchCount;
+ float freqScaler = gOscBanks[gCurrentOscBank]->getFrequencyScaler();
+ for(int i=0; i < gOscBanks[gCurrentOscBank]->filterNum; i++)
+ {
+ // touch pos is linear but freqs are log
+ gOscBanks[gCurrentOscBank]->filterFreqs[i] = ((expf(gSensor1MatrixTouchPos[i]*4)-1)/(expf(4)-1))*gOscBanks[gCurrentOscBank]->filterMaxF*freqScaler;
+ gOscBanks[gCurrentOscBank]->filterQ[i] = gSensor1LatestTouchSizes[i];
+ if(gOscBanks[gCurrentOscBank]->filterFreqs[i]>500*freqScaler)
+ gOscBanks[gCurrentOscBank]->filterPadding[i] = 1+100000*( (gOscBanks[gCurrentOscBank]->filterFreqs[i]-500*freqScaler)/(gOscBanks[gCurrentOscBank]->filterMaxF-500)*freqScaler );
+ else
+ gOscBanks[gCurrentOscBank]->filterPadding[i] = 1;
+ }
+#endif
+
+ RTIME ticks = rt_timer_read();
+ SRTIME ns = rt_timer_tsc2ns(ticks);
+ SRTIME delta = ns-prevChangeNs;
+
+ // switch to next bank cannot be too frequent, to avoid seg fault! [for example sef fault happens when removing both VDD and GND from breadboard]
+ if(gNextOscBank != gCurrentOscBank && delta>100000000) {
+
+ /*printf("ticks %llu\n", (unsigned long long)ticks);
+ printf("ns %llu\n", (unsigned long long)ns);
+ printf("prevChangeNs %llu\n", (unsigned long long)prevChangeNs);
+ printf("-------------------------->%llud\n", (unsigned long long)(ns-prevChangeNs));*/
+
+ prevChangeNs = ns;
+ dbox_printf("Changing to bank %d...\n", gNextOscBank);
+ if(gOscBanks[gCurrentOscBank]->state==bank_playing){
+ gOscBanks[gCurrentOscBank]->stop();
+ }
+
+ gCurrentOscBank = gNextOscBank;
+ gOscBanks[gCurrentOscBank]->hopNumTh = 0;
+ }
+ else {
+ /* Advance to the next oscillator frame */
+ gOscBanks[gCurrentOscBank]->nextHop();
+ }
+ }
+}
+
+// Lower-priority render function which performs matrix calculations
+// State should be transferred in via global variables
+void render_low_prio()
+{
+ gPRU->setGPIOTestPin();
+ if(gDynamicWavetableNeedsRender) {
+ // Find amplitude of wavetable
+ float meanAmplitude = 0;
+ float sineMix;
+
+ for(int i = 0; i < gFeedbackOscillatorTableLength; i++) {
+ //meanAmplitude += fabsf(gFeedbackOscillatorTable[i]);
+ meanAmplitude += fabs(gFeedbackOscillatorTable[i]);
+ }
+ meanAmplitude /= (float)gFeedbackOscillatorTableLength;
+
+ if(meanAmplitude > 0.35)
+ sineMix = 0;
+ else
+ sineMix = (.35 - meanAmplitude) / .35;
+
+ //dbox_printf("amp %f mix %f\n", meanAmplitude, sineMix);
+
+ // Copy to main wavetable
+ wavetable_interpolate(gFeedbackOscillatorTableLength, gDynamicWavetableLength,
+ gFeedbackOscillatorTable, gDynamicWavetable,
+ gOscBanks[gCurrentOscBank]->lookupTable, sineMix);
+ }
+
+ if(gLoopPointMin >= 60000 && gLoopPointMax >= 60000) {
+ // KLUDGE!
+ if(gCurrentOscBank == 0)
+ gOscBanks[gCurrentOscBank]->setLoopHops(50, ((float)gOscBanks[gCurrentOscBank]->getLastHop() * 0.6) - 1);
+ else
+ gOscBanks[gCurrentOscBank]->setLoopHops(5, ((float)gOscBanks[gCurrentOscBank]->getLastHop() * 0.7) - 1);
+ }
+ else {
+ float normLoopPointMin = (float)gLoopPointMin * gOscBanks[gCurrentOscBank]->getLastHop() / 65535.0;
+ float normLoopPointMax = (float)gLoopPointMax * gOscBanks[gCurrentOscBank]->getLastHop() / 65535.0;
+
+ int intLoopPointMin = normLoopPointMin;
+ if(intLoopPointMin < 1)
+ intLoopPointMin = 1;
+ int intLoopPointMax = normLoopPointMax;
+ if(intLoopPointMax <= intLoopPointMin)
+ intLoopPointMax = intLoopPointMin + 1;
+ if(intLoopPointMax > gOscBanks[gCurrentOscBank]->getLastHop() - 1)
+ intLoopPointMax = gOscBanks[gCurrentOscBank]->getLastHop() - 1;
+
+ //dbox_printf("Loop points %d-%d / %d-%d\n", gLoopPointMin, gLoopPointMax, intLoopPointMin, intLoopPointMax);
+
+ /* WORKS, jsut need to fix the glitch when jumps!
+ * *int currentHop = gOscBanks[gCurrentOscBank]->getCurrentHop();
+ if(currentHop < intLoopPointMin -1 )
+ gOscBanks[gCurrentOscBank]->setJumpHop(intLoopPointMin + 1);
+ else if(currentHop > intLoopPointMax + 1)
+ gOscBanks[gCurrentOscBank]->setJumpHop(intLoopPointMax - 1);*/
+ gOscBanks[gCurrentOscBank]->setLoopHops(intLoopPointMin, intLoopPointMax);
+ }
+
+ if(gIsLoading)
+ gStatusLED.blink(25, 75); // Blink quickly until load finished
+ else
+ gStatusLED.blink(250 / gOscBanks[gCurrentOscBank]->getSpeed(), 250 / gOscBanks[gCurrentOscBank]->getSpeed());
+ gPRU->clearGPIOTestPin();
+
+// static int counter = 32;
+// if(--counter == 0) {
+// for(int i = 0; i < gLoopPointsInputBufferSize; i++) {
+// dbox_printf("%d ", gLoopPointsInputBuffer[i]);
+// if(i % 32 == 31)
+// dbox_printf("\n");
+// }
+// dbox_printf("\n\n");
+// counter = 32;
+// }
+
+ //dbox_printf("min %d max %d\n", gLoopPointMin, gLoopPointMax);
+}
+
+// Clean up at the end of render
+void cleanup_render()
+{
+ free(gOscillatorBuffer1);
+ free(gOscillatorBuffer2);
+ free(gDynamicWavetable);
+}
+
+// Interpolate one wavetable into another. The output size
+// does not include the guard point at the end which will be identical
+// to the first point
+void wavetable_interpolate(int numSamplesIn, int numSamplesOut,
+ float *tableIn, float *tableOut,
+ float *sineTable, float sineMix)
+{
+ float fractionalScaler = (float)numSamplesIn / (float)numSamplesOut;
+
+ for(int k = 0; k < numSamplesOut; k++) {
+ float fractionalIndex = (float) k * fractionalScaler;
+ //int sB = (int)floorf(fractionalIndex);
+ int sB = (int)floor(fractionalIndex);
+ int sA = sB + 1;
+ if(sA >= numSamplesIn)
+ sA = 0;
+ float fraction = fractionalIndex - sB;
+ tableOut[k] = fraction * tableIn[sA] + (1.0f - fraction) * tableIn[sB];
+ tableOut[k] = sineMix * sineTable[k] + (1.0 - sineMix) * tableOut[k];
+ }
+
+ tableOut[numSamplesOut] = tableOut[0];
+}
+
+// Create a hysteresis oscillator with a matrix input and output
+inline uint16_t hysteresis_oscillator(uint16_t input, uint16_t risingThreshold, uint16_t fallingThreshold, bool *rising)
+{
+ uint16_t value;
+
+ if(*rising) {
+ if(input > risingThreshold) {
+ *rising = false;
+ value = 0;
+ }
+ else
+ value = 65535;
+ }
+ else {
+ if(input < fallingThreshold) {
+ *rising = true;
+ value = 65535;
+ }
+ else
+ value = 0;
+ }
+
+ return value;
+}
+
+#ifdef DBOX_CAPE_TEST
+// Test the functionality of the D-Box cape by checking each input and output
+// Loopback cable from ADC to DAC needed
+void render_capetest(int numMatrixFrames, int numAudioFrames, float *audioIn, float *audioOut,
+ uint16_t *matrixIn, uint16_t *matrixOut)
+{
+ static float phase = 0.0;
+ static int sampleCounter = 0;
+ static int invertChannel = 0;
+
+ // Play a sine wave on the audio output
+ for(int n = 0; n < numAudioFrames; n++) {
+ audioOut[2*n] = audioOut[2*n + 1] = 0.5*sinf(phase);
+ phase += 2.0 * M_PI * 440.0 / 44100.0;
+ if(phase >= 2.0 * M_PI)
+ phase -= 2.0 * M_PI;
+ }
+
+ for(int n = 0; n < numMatrixFrames; n++) {
+ // Change outputs every 512 samples
+ if(sampleCounter < 512) {
+ for(int k = 0; k < 8; k++) {
+ if(k == invertChannel)
+ matrixOut[n*8 + k] = 50000;
+ else
+ matrixOut[n*8 + k] = 0;
+ }
+ }
+ else {
+ for(int k = 0; k < 8; k++) {
+ if(k == invertChannel)
+ matrixOut[n*8 + k] = 0;
+ else
+ matrixOut[n*8 + k] = 50000;
+ }
+ }
+
+ // Read after 256 samples: input should be low
+ if(sampleCounter == 256) {
+ for(int k = 0; k < 8; k++) {
+ if(k == invertChannel) {
+ if(matrixIn[n*8 + k] < 50000) {
+ dbox_printf("FAIL channel %d -- output HIGH input %d (inverted)\n", k, matrixIn[n*8 + k]);
+ }
+ }
+ else {
+ if(matrixIn[n*8 + k] > 2048) {
+ dbox_printf("FAIL channel %d -- output LOW input %d\n", k, matrixIn[n*8 + k]);
+ }
+ }
+ }
+ }
+ else if(sampleCounter == 768) {
+ for(int k = 0; k < 8; k++) {
+ if(k == invertChannel) {
+ if(matrixIn[n*8 + k] > 2048) {
+ dbox_printf("FAIL channel %d -- output LOW input %d (inverted)\n", k, matrixIn[n*8 + k]);
+ }
+ }
+ else {
+ if(matrixIn[n*8 + k] < 50000) {
+ dbox_printf("FAIL channel %d -- output HIGH input %d\n", k, matrixIn[n*8 + k]);
+ }
+ }
+ }
+ }
+
+ if(++sampleCounter >= 1024) {
+ sampleCounter = 0;
+ invertChannel++;
+ if(invertChannel >= 8)
+ invertChannel = 0;
+ }
+ }
+}
+#endif
+
+
diff -r 000000000000 -r 8a575ba3ab52 projects/d-box/sensors.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/projects/d-box/sensors.cpp Fri Oct 31 19:10:17 2014 +0100
@@ -0,0 +1,642 @@
+/*
+ * sensors.cpp
+ *
+ * Created on: May 28, 2014
+ * Author: Victor Zappi
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include "prio.h"
+#include "sensors.h"
+#include "OscillatorBank.h"
+#include "DboxSensors.h"
+
+
+//----------------------------------------
+// main extern variables
+//----------------------------------------
+extern vector gOscBanks;
+extern int gCurrentOscBank;
+extern int gNextOscBank;
+extern bool gShouldStop;
+extern int gVerbose;
+
+float gSensor0LatestTouchPos = 0; // most recent pitch touch location [0-1] on sensor 0, used by render.cpp
+int gSensor0LatestTouchNum = 0; // most recent num of touches on sensor 0, used by render.cpp
+float gSensor1LatestTouchPos[5]; // most recent touche locations on sensor 1, used by render.cpp
+//float gSensor1LatestTouchSizes[5];
+int gSensor1LatestTouchCount; // most recent number touches on sensor 1, used by render.cpp
+int gSensor1LatestTouchIndex = 0; // index of last touch in gSensor1LatestTouchPos[5], used by render.cpp
+int gLastFSRValue = 1799; // most recent fsr value, used by render.cpp
+
+
+DboxSensors Sensors;
+
+
+//----------------------------------------
+// var shared with logger
+//----------------------------------------
+int s0TouchNum = 0;
+float s0Touches_[MAX_TOUCHES];
+float s0Size_[MAX_TOUCHES];
+int s0LastIndex;
+
+int s1TouchNum = 0;
+float s1Touches_[MAX_TOUCHES];
+float s1Size_[MAX_TOUCHES];
+int s1LastIndex;
+
+int fsr = 1799;
+
+
+
+using namespace std;
+
+int initSensorLoop(int sensorAddress0, int sensorAddress1, bool useNewSensors)
+{
+ int tk0_bus = 1;
+ int tk0_address = sensorAddress0;
+ int tk1_bus = 1;
+ int tk1_address = sensorAddress1;
+ int tk_file = 0;
+ int fsr_max = 1799;
+ int fsr_pinNum = 4;
+
+ if(gVerbose==1)
+ cout << "---------------->Init Control Thread" << endl;
+
+ if(Sensors.initSensors(tk0_bus, tk0_address, tk1_bus, tk1_address, tk_file, fsr_pinNum, fsr_max, useNewSensors)>0)
+ {
+ gShouldStop = 1;
+ cout << "control cannot start" << endl;
+ return -1;
+ }
+
+ for(int i=0; igetFrequencyScaler();
+ filterMaxF = gOscBanks[gCurrentOscBank]->filterMaxF;
+
+ // init time vals
+ gettimeofday(&start, NULL);
+
+ // here we go, sensor loop until the end of the application
+ while(!gShouldStop)
+ {
+ gettimeofday(&end, NULL);
+ elapsedTime = ( (end.tv_sec*1000000+end.tv_usec) - (start.tv_sec*1000000+start.tv_usec) );
+ if( elapsedTime<4000 )
+ usleep(4000-elapsedTime);
+ else
+ dbox_printf("%d\n", (int)elapsedTime); // this print happens when something's gone bad...
+
+ if(Sensors.readSensors()==0)
+ {
+ s0TouchNum = Sensors.getTKTouchCount(0);
+ s0Touches = Sensors.getTKXPositions(0);
+ s0Size = Sensors.getTKTouchSize(0);
+
+ s1TouchNum = Sensors.getTKTouchCount(1);
+ s1Touches = Sensors.getTKXPositions(1);
+ s1Size = Sensors.getTKTouchSize(1);
+
+ for(int i=0; i 0)
+ {
+ //-----------------------------------------------------------------------------------
+ // timbre, speed and pitch
+ //touchSize = 0; \\ once used for timbre
+
+ // if we have a number of touches different from previous round, track their order of arrival [calculated using distance comparison]
+ if(s0PrevTouchNum!=s0TouchNum)
+ {
+ float distances[MAX_TOUCHES*(MAX_TOUCHES-1)]; // maximum number of current+previous touches between rounds with different num of touches
+ int ids[MAX_TOUCHES*(MAX_TOUCHES-1)];
+ // calculate all distance permutations between previous and current touches
+ for(int i=0; i0)
+ {
+ // sort, from min to max distance
+ float tmp;
+ while(distances[index]=0; i--)
+ {
+ if(!prevAssigned[i])
+ {
+ for(int j=0; ji)
+ s0SortedTouchIndices[j]--;
+ }
+ }
+ }
+ // done! now update
+ for(int i=0; i 0.7) ? 1 : touchSize/0.7;
+ //gOscBanks[gCurrentOscBank]->hopNumTh = log((1-touchSize)+1)/log(2)*20000;
+ //gOscBanks[gCurrentOscBank]->hopNumTh = 0;
+
+
+ // pitch, controlled by last touch
+ //prevTouchPos = touch[touchIndex];
+ //touchPos = (s0SortedTouches[s0TouchNum-1]-0.5)/0.5; // from [0,1] to [-1,1]
+ gSensor0LatestTouchPos = s0SortedTouches[s0TouchNum-1];
+ //touchPos = s0Touches[0];
+ //gOscBanks[gCurrentOscBank]->pitchMultiplier = pow(2, touchPos);
+ //-----------------------------------------------------------------------------------
+
+
+
+ //-----------------------------------------------------------------------------------
+ // note on
+ //if(s0PrevTouchNum == 0)
+ // gOscBanks[gCurrentOscBank]->play();
+ // fsr = Sensors.getFSRVAlue();
+ fsr = gLastFSRValue;
+ //dbox_printf("fsr: %d\n", fsr);
+ if(!gOscBanks[gCurrentOscBank]->note)
+ {
+ vel = fsr;
+ vel /= (float)(fsrMax-fsrMin);
+
+ vel = 1-vel;
+ dbox_printf("Attack vel: %f\n", vel);
+ gOscBanks[gCurrentOscBank]->play(vel);
+ prevVel = vel;
+ }
+ else if(gOscBanks[gCurrentOscBank]->getEnvelopeState() != env_release)
+ {
+ fsr = (fsr > fsrMax) ? fsrMax : fsr;
+ vel = (fsr < fsrMin) ? fsrMin : fsr;
+ vel -= fsrMin;
+ vel /= (float)(fsrMax-fsrMin);
+ vel = 1-vel;
+ if(vel > prevVel)
+ {
+ gOscBanks[gCurrentOscBank]->afterTouch(vel);
+ prevVel = vel;
+ }
+ }
+ //-----------------------------------------------------------------------------------
+ }
+ else
+ {
+ //prevFsr = 1799;
+ //prevTouchPos = -1;
+ //-----------------------------------------------------------------------------------
+ // note off
+ if(s0PrevTouchNum > 0)
+ {
+ if(gOscBanks[gCurrentOscBank]->state==bank_playing)
+ gOscBanks[gCurrentOscBank]->stop();
+ }
+ //-----------------------------------------------------------------------------------
+ }
+
+
+
+ // sensor 2
+ //-----------------------------------------------------------------------------------
+ //filter - calculated even when no touches on first sensor, to filter also release tail
+ gOscBanks[gCurrentOscBank]->filterNum = s1TouchNum;
+
+ gSensor1LatestTouchCount = gOscBanks[gCurrentOscBank]->filterNum;
+ for(int i = 0; i < gSensor1LatestTouchCount; i++) {
+ gSensor1LatestTouchPos[i] = s1Touches[i];
+ //gSensor1LatestTouchSizes[i] = s1Size[i];
+ }
+
+/* for(int i=0; ifilterNum; i++)
+ {
+ // touch pos is linear but freqs are log
+ gOscBanks[gCurrentOscBank]->filterFreqs[i] = ((exp(s0Touches[i]*4)-1)/(exp(4)-1))*filterMaxF*freqScaler;
+ //gOscBanks[gCurrentOscBank]->filterQ[i] = size[i]*5*(1+touch[i]*1000)*freqScaler;
+ gOscBanks[gCurrentOscBank]->filterQ[i] = s0Size[i];
+ if(gOscBanks[gCurrentOscBank]->filterFreqs[i]>500*freqScaler)
+ gOscBanks[gCurrentOscBank]->filterPadding[i] = 1+100000*( (gOscBanks[gCurrentOscBank]->filterFreqs[i]-500*freqScaler)/(filterMaxF-500)*freqScaler );
+ else
+ gOscBanks[gCurrentOscBank]->filterPadding[i] = 1;
+ }*/
+
+ // each touch on sensor 2 is a notch filter, whose Q is determined by touch size
+ for(int i=0; ifilterNum; i++)
+ {
+ // map touch pos [which is linear] on freqs exponentially
+ float freq = ((exp(s1Touches[i]*4)-1)/EXP_DENOM)*filterMaxF;
+ gOscBanks[gCurrentOscBank]->filterFreqs[i] = freq*freqScaler;
+ // also size is mapped exponentially on Q
+ float siz = (exp(s1Size[i])-1)/1.71828;
+ gOscBanks[gCurrentOscBank]->filterQ[i] = siz*( (filterMaxF-freq)/filterMaxF * 0.9 + 0.1 ); // size weight on Q decreases with frequency
+ }
+ //-----------------------------------------------------------------------------------
+
+
+
+ //-----------------------------------------------------------------------------------
+ // sort touches on sensor 2
+ if(s1TouchNum > 0)
+ {
+ // if we have a number of touches different from previous round, track their order of arrival [calculated using distance comparison]
+ if(s1PrevTouchNum!=s1TouchNum)
+ {
+ float distances[MAX_TOUCHES*(MAX_TOUCHES-1)]; // maximum number of current+previous touches between rounds with different num of touches
+ int ids[MAX_TOUCHES*(MAX_TOUCHES-1)];
+ // calculate all distance permutations between previous and current touches
+ for(int i=0; i0)
+ {
+ // sort, from min to max distance
+ float tmp;
+ while(distances[index]=0; i--)
+ {
+ if(!prevAssigned[i])
+ {
+ for(int j=0; ji)
+ s1SortedTouchIndices[j]--;
+ }
+ }
+ }
+ // done! now update
+ for(int i=0; i 0)
+ {
+ gSensor1LatestTouchIndex = s1LastIndex;
+ }
+ else
+ s1LastIndex = -1;
+
+/* dbox_printf("-----------------------------\nnum: %d, latest: %d\n", s1TouchNum, gSensor1LatestTouchIndex);
+ for(int i=0; ihopNumTh = 0;
+ gOscBanks[gCurrentOscBank]->play(1);
+ //cout << "Note on" << endl;
+ break;
+ case 's':
+ if(gOscBanks[gCurrentOscBank]->state==bank_playing)
+ {
+ gOscBanks[gCurrentOscBank]->stop();
+ //cout << "Note off" << endl;
+ }
+ break;
+ //----------------------------------------------------------------------------
+ case '[':
+ gOscBanks[gCurrentOscBank]->freqMovement-=0.05;
+ if(gOscBanks[gCurrentOscBank]->freqMovement<0)
+ gOscBanks[gCurrentOscBank]->freqMovement = 0;
+ //cout << "gOscBanks[gCurrentOscBank]->FreqMov: " << gOscBanks[gCurrentOscBank]->freqMovement << endl;
+ break;
+ case ']':
+ gOscBanks[gCurrentOscBank]->freqMovement+=0.05;
+ if(gOscBanks[gCurrentOscBank]->freqMovement>1)
+ gOscBanks[gCurrentOscBank]->freqMovement = 1;
+ //cout << "gOscBanks[gCurrentOscBank]->FreqMov: " << gOscBanks[gCurrentOscBank]->freqMovement << endl;
+ break;
+ //----------------------------------------------------------------------------
+ case '<':
+ speed = gOscBanks[gCurrentOscBank]->getSpeed() - 0.1 ;
+ gOscBanks[gCurrentOscBank]->setSpeed(speed);
+ dbox_printf("Speed: %f\n", speed);
+
+ break;
+ case '>':
+ speed = gOscBanks[gCurrentOscBank]->getSpeed() + 0.1 ;
+ gOscBanks[gCurrentOscBank]->setSpeed(speed);
+ dbox_printf("Speed: %f\n", speed);
+ break;
+ case '0':
+ speed = 0.1;
+ gOscBanks[gCurrentOscBank]->setSpeed(speed);
+ dbox_printf("Speed: %f\n", speed);
+ break;
+ case '1':
+ speed = 0.5;
+ gOscBanks[gCurrentOscBank]->setSpeed(speed);
+ dbox_printf("Speed: %f\n", speed);
+ break;
+ case '2':
+ speed = 1;
+ gOscBanks[gCurrentOscBank]->setSpeed(speed);
+ dbox_printf("Speed: %f\n", speed);
+ break;
+ case '3':
+ speed = 2;
+ gOscBanks[gCurrentOscBank]->setSpeed(speed);
+ dbox_printf("Speed: %f\n", speed);
+ break;
+ case '4':
+ speed = 3;
+ gOscBanks[gCurrentOscBank]->setSpeed(speed);
+ dbox_printf("Speed: %f\n", speed);
+ break;
+ //----------------------------------------------------------------------------
+ case 'z':
+ gOscBanks[gCurrentOscBank]->setJumpHop(0);
+ break;
+ case 'x':
+ gOscBanks[gCurrentOscBank]->setJumpHop(100);
+ break;
+ case 'c':
+ gOscBanks[gCurrentOscBank]->setJumpHop(600);
+ break;
+ case 'v':
+ gOscBanks[gCurrentOscBank]->setJumpHop(1100);
+ break;
+ case 'b':
+ gOscBanks[gCurrentOscBank]->setJumpHop(2000);
+ break;
+ case 'n':
+ gOscBanks[gCurrentOscBank]->setJumpHop(gOscBanks[gCurrentOscBank]->getLastHop());
+ break;
+ //----------------------------------------------------------------------------
+ case 'q':
+ gShouldStop = true;
+ break;
+ case 'o':
+ gNextOscBank = (gCurrentOscBank + 1) % gOscBanks.size();
+ break;
+ default:
+ break;
+ //----------------------------------------------------------------------------
+ }
+ usleep(1000); /* Wait 1ms to avoid checking too quickly */
+ }
+ while (keyStroke!='q');
+
+ cout << "keyboard thread ended" << endl;
+
+ return (void *)0;
+}
diff -r 000000000000 -r 8a575ba3ab52 projects/d-box/sensors.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/projects/d-box/sensors.h Fri Oct 31 19:10:17 2014 +0100
@@ -0,0 +1,19 @@
+/*
+ * sensors.h
+ *
+ * Created on: May 28, 2014
+ * Author: Victor Zappi
+ */
+
+#ifndef SENSORS_H_
+#define SENSORS_H_
+
+#include "config.h"
+
+int initSensorLoop(int sensorAddress0, int sensorAddress1, bool useNewSensors);
+
+void sensorLoop(void *);
+void *keyboardLoop(void *);
+
+
+#endif /* SENSORS_H_ */
diff -r 000000000000 -r 8a575ba3ab52 projects/d-box/spear_parser.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/projects/d-box/spear_parser.cpp Fri Oct 31 19:10:17 2014 +0100
@@ -0,0 +1,641 @@
+/*
+ * spear_parser.cpp v1.2
+ *
+ * Created on: May 6, 2014
+ * Author: Victor Zappi
+ */
+
+#include "spear_parser.h"
+
+using namespace std;
+
+//#define DO_CHECKS
+
+//------------------------------------------------------------------------------------------------
+// partials
+//------------------------------------------------------------------------------------------------
+
+Partials::Partials()
+{
+ partialFrequencies = NULL;
+// partialAmplitudes = NULL;
+// partialNumFrames = NULL;
+// partialStartSample = NULL;
+// partialEndSample = NULL;
+// partialCurrentFrame = NULL;
+// partialFreqDelta = NULL;
+// partialAmpDelta = NULL;
+
+
+ activePartialNum = NULL;
+// activePartials = NULL;
+
+ currentSample = -1;
+}
+
+Partials::~Partials()
+{
+ if(partialFrequencies != NULL) // check on one is enough
+ {
+ if(partialFrequencies[0] != NULL) // check on one is enough
+ {
+ for(unsigned int i=0; i maxSample)
+ maxSample = endSample;
+
+ // update data structure
+ partials.update(parIndex, frameNum);
+
+
+ //-------------------------------------
+ // frames
+ getline(fin, s);
+ token = strtok((char *)s.c_str(), " "); // frame time
+ frameIndex = -1;
+
+ // unroll first iteration, so that in the following loop we save the check on the last frame to calculate increments
+ if(token) // all frames data are on one line, in groups of 3 entries
+ {
+ frameIndex++;
+
+ endSample = fromTimeToSamples(atof(token));
+
+ token = strtok(0, " "); // frame frequency
+ prevFreq = atof(token);
+ partials.partialFrequencies[parIndex][frameIndex] = (float)prevFreq;
+ partials.partialFreqMean[parIndex] += prevFreq; // for frequency mean
+
+ token = strtok(0, " "); // frame amplitude
+ prevAmp = atof(token);
+ partials.partialAmplitudes[parIndex][frameIndex] = (float)prevAmp;
+
+ token = strtok(0, " "); // next frame frequency, to be checked
+ }
+
+ // here the loop starts
+ while(token) // all frames data are on one line, in groups of 3 entries
+ {
+ frameIndex++;
+ missSampCnt = 0;
+
+ startSample = fromTimeToSamples(atof(token));
+
+ token = strtok(0, " "); // frame frequency
+ freq = atof(token);
+
+ token = strtok(0, " "); // frame amplitude
+ amp = atof(token);
+ // now we know all about the current frame, but we want to know if some frames are missing between this and the last one
+
+ // while current frame sample is farther than one hopsize...
+ while(startSample > endSample+hopSize)
+ {
+ missSampCnt++; // ...one sample is missing
+ endSample += hopSize; // move to next hop
+ }
+
+ // if frames are missing do interpolation and update indices
+ if(missSampCnt>0)
+ startSample = interpolateSamples(parIndex, &frameIndex, missSampCnt, endSample+hopSize, freq, amp, &prevFreq, &prevAmp);
+
+ partials.partialFrequencies[parIndex][frameIndex] = (float)freq;
+ partials.partialFreqMean[parIndex] += freq; // for frequency mean
+ partials.setFreqDelta(parIndex, frameIndex-1, (freq-prevFreq)/hopSize); // freq delta between prev and current frame
+ prevFreq = freq;
+
+ partials.partialAmplitudes[parIndex][frameIndex] = (float)amp;
+ partials.setAmpDelta(parIndex, frameIndex-1, (amp-prevAmp)/hopSize); // amp delta between prev and current frame
+ prevAmp = amp;
+
+ endSample = startSample;
+ token = strtok(0, " "); // next frame frequency, to be checked
+ }
+ #ifdef DO_CHECKS
+ if(frameIndex != (frameNum-1))
+ {
+ cout << "Parser Error: frame count mismatch on partial " << parIndex << ", bad file format" << endl; // exit if mismatch
+ cout << "frameIndex: " << frameIndex << endl;
+ cout << "frameNum: " << frameNum << endl;
+ return false;
+ }
+ #endif
+
+ partials.partialFreqMean[parIndex] /= partials.partialNumFrames[parIndex]; // frequency mean
+
+ getline(fin, s); // next partial line, to check
+ }
+ #ifdef DO_CHECKS
+ if(parIndex != (parNum-1))
+ {
+ cout << "Parser Error: partial count mismatch, bad file format" << endl; // exit if mismatch
+ return false;
+ }
+ #endif
+
+ partials.setHopNum(maxSample/hopSize);
+
+ gettimeofday(&stop, NULL);
+ parserT = ( (stop.tv_sec*1000000+stop.tv_usec) - (start.tv_sec*1000000+start.tv_usec) );
+
+ gettimeofday(&start, NULL);
+ staticCalculations();
+ gettimeofday(&stop, NULL);
+ staticT = ( (stop.tv_sec*1000000+stop.tv_usec) - (start.tv_sec*1000000+start.tv_usec) );
+
+ fin.close();
+
+
+ printf("\n-----------------------\n");
+ printf("\nFile: %s\n", filename);
+ printf("\n-----------------------\n");
+ printf("Profiler\n");
+ printf("-----------------------\n");
+ printf("Hop size parser:\t\t%lu usec\n", hopSizeT);
+ printf("File parser:\t\t\t%lu usec\n", parserT);
+ printf("Static calculations:\t\t%lu usec\n", staticT);
+ printf("\n\nTotal:\t\t%lu usec\n", hopSizeT+parserT+staticT);
+ printf("-----------------------\n");
+
+ return true;
+}
+
+
+int Spear_parser::interpolateSamples(int parIndex, int *frameIndex, int missCnt, int nextSample, double nextFreq, double nextAmp, double *prevFreq, double *prevAmp)
+{
+ int frame = *frameIndex; // current frame index
+ int sample = nextSample - (hopSize*(missCnt)); // move from next real frame sample to first missing frame sample
+ double freq = *prevFreq; // freq of the prev real frame
+ double freqStep = (nextFreq-*prevFreq)/(missCnt+1); // fixed freq step between hops, for missing frames [linear interpolation]
+ double deltaFreq = freqStep/hopSize; // fixed hop freq step in samples
+ double amp = *prevAmp; // same for amp...
+ double ampStep = (nextAmp-*prevAmp)/(missCnt+1);
+ double deltaAmp = ampStep/hopSize;
+
+ // for each missing frame
+ for(int i=0; i=partials.partialStartSample[j]) && (frameSample partials.maxActiveParNum)
+ partials.maxActiveParNum = activeCnt;
+
+ // copy indices
+ for(unsigned int k=0; k
+#include
+#include
+#include
+#include // atoi, atof
+#include
+#include // std::fill
+
+#include
+
+using namespace std;
+
+
+//------------------------------------------------------------------------------------------------
+// partials
+//------------------------------------------------------------------------------------------------
+
+class Spear_parser; // for class friendship
+
+class Partials
+{
+ friend class Spear_parser;
+ friend class Dbox_parser;
+
+public:
+ int **partialSamples; // sample at which each frame is
+ float **partialFrequencies; // frequencies at each frame
+ float **partialAmplitudes; // amplitudes at each frame
+ unsigned int *partialNumFrames; // Length of each partial in frames
+ unsigned int *partialStartFrame; // frame at which each partial begins
+ float **partialFreqDelta; // constant frequency slope for each partial in each frame interval
+ float **partialAmpDelta; // constant amplitude slope for each partial in each frame interval
+ float *partialFreqMean; // frequency mean for each partial, over all its frames
+
+ unsigned short *activePartialNum; // num of each active partial at each frame
+ unsigned int **activePartials; // indices of all active partials at each frame
+
+
+ int getPartialNum();
+ int getHopNum();
+ int getMaxActivePartialNum();
+
+private:
+ Partials();
+ ~Partials();
+
+ unsigned int *partialStartSample; // sample at which each partial begins
+ unsigned int *partialEndSample; // sample at which each partial ends [sample gap between 2 consecutive frames can be an integer multiple of hopSize]
+ unsigned int parNum;
+ unsigned int currentSample;
+ unsigned int hopSize;
+ unsigned int hopNum;
+ unsigned int maxActiveParNum;
+
+ void init(int parNum, int hopSize, bool isDBX=false);
+ void update(int parIndex, int frameNum);
+ void setFreqDelta(int parIndex, int frameNum, double delta);
+ void setAmpDelta(int parIndex, int frameNum, double delta);
+ void setHopNum(int hopNum);
+};
+
+inline int Partials::getPartialNum()
+{
+ return parNum;
+}
+
+inline void Partials::setHopNum(int hopN)
+{
+ hopNum = hopN;
+
+ // prepare data structures
+ activePartialNum = new unsigned short[hopNum+1]; // +1 cos total num of frames = num of hops+1
+ activePartials = new unsigned int *[hopNum+1];
+}
+
+// useful to increase current sample using a modulo on the total number of samples [easy to be deduced from the total num or hops]
+inline int Partials::getHopNum()
+{
+ return hopNum;
+}
+
+inline void Partials::setFreqDelta(int parIndex, int frameNum, double delta)
+{
+ partialFreqDelta[parIndex][frameNum] = delta;
+}
+
+inline void Partials::setAmpDelta(int parIndex, int frameNum, double delta)
+{
+ partialAmpDelta[parIndex][frameNum] = delta;
+}
+
+inline int Partials::getMaxActivePartialNum()
+{
+ return maxActiveParNum;
+}
+
+
+
+
+
+
+
+//------------------------------------------------------------------------------------------------
+// spear parser
+//------------------------------------------------------------------------------------------------
+
+class Spear_parser
+{
+public:
+ Spear_parser();
+ ~Spear_parser();
+
+ Partials partials;
+
+ bool parseFile(string filename, int hopsize=-1, int samplerate = 44100);
+ bool parseFile(char *filename, int hopsize=-1, int samplerate = 44100);
+ int getHopSize();
+ int getFileSampleRate();
+ double getDeltaTime();
+
+private:
+
+ int hopSize;
+ int fileSampleRate;
+ double deltaTime; // min time gap between consecutive frames
+
+ timeval start, stop;
+ unsigned long hopSizeT, parserT, staticT;
+
+ void calculateDeltaTime();
+ void calculateHopSize(char *filename);
+ bool parser(char *filename, int hopsize=-1, int samplerate=44100);
+ bool DBXparser(char *filename, int samplerate=44100);
+ bool TXTparser(char *filename, int hopsize=-1, int samplerate=44100);
+ int fromTimeToSamples(float time);
+ int interpolateSamples(int parIndex, int *frameIndex, int missCnt, int nextSample,
+ double nextFreq, double nextAmp, double *prevFreq, double *prevAmp);
+ void staticCalculations();
+
+};
+
+inline bool Spear_parser::parseFile(string filename, int hopsize, int samplerate)
+{
+ return parser((char *)filename.c_str(), hopsize, samplerate);
+}
+
+inline bool Spear_parser::parseFile(char *filename, int hopsize, int samplerate)
+{
+ return parser(filename, hopsize, samplerate);
+}
+
+inline void Spear_parser::calculateDeltaTime()
+{
+ deltaTime = (double)hopSize/ (double)fileSampleRate;
+}
+
+// each time value in the file is rounded, and 2 consecutive frames can differ of a time gap = i*deltaTime, where i is a positive integer
+inline int Spear_parser::fromTimeToSamples(float time)
+{
+ return round(time/deltaTime)*hopSize; // round is necessary since in the file log time values are rounded, so they do not apparently look like integer multiples of deltaTime
+}
+
+inline int Spear_parser::getHopSize()
+{
+ return hopSize;
+}
+
+inline int Spear_parser::getFileSampleRate()
+{
+ return fileSampleRate;
+}
+
+inline double Spear_parser::getDeltaTime()
+{
+ return deltaTime;
+}
+
+#endif /* SPEAR_PARSER_H_ */
diff -r 000000000000 -r 8a575ba3ab52 projects/oscillator_bank/audio_routines.S
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/projects/oscillator_bank/audio_routines.S Fri Oct 31 19:10:17 2014 +0100
@@ -0,0 +1,161 @@
+@
+@ audio_routines.S
+@
+@ NEON-based functions for time-critical audio processing
+@
+@ Andrew McPherson 2014
+@ Queen Mary University of London
+@
+
+ .syntax unified
+ .arch armv7-a
+ .fpu neon
+
+@ void oscillator_bank_neon(int numAudioFrames, float *audioOut,
+@ int activePartialNum, int lookupTableSize,
+@ float *phases, float *frequencies, float *amplitudes,
+@ float *freqDerivatives, float *ampDerivatives,
+@ float *lookupTable);
+
+@ Registers:
+@ r0: numAudioFrames How many frames to render
+@ r1: audioOut Buffer for audio output samples [stereo]
+@ r2: activePartialNum How many active partials to render
+@ r3: lookupTableSize Size of lookup table
+@ ---- other arguments start on the stack and are moved: -----
+@ r4: phases Phase of each oscillator (pointer)
+@ r5: frequencies Normalised frequency of each oscillator (pointer)
+@ r6: amplitudes Normalised amplitude of each oscillator (pointer)
+@ r7: freqDerivatives Derivative of frequency for each oscillator (pointer)
+@ r8: ampDerivatives Derivative of amplitude for each oscillator (pointer)
+@ r9: lookupTable Lookup table containing one oscillation
+@
+@ Alignment requirements:
+@ audioOut: 8-byte boundary
+@ phases: 16-byte boundary
+@ frequencies: 16-byte boundary
+@ amplitudes: 16-byte boundary
+@ freqDerivatives: 16-byte bounary
+@ ampDerivatives: 16-byte boundary
+@ lookupTable: 4-byte boundary (TODO: check this)
+
+ .align 2
+ .global oscillator_bank_neon
+ .thumb
+ .thumb_func
+ .type oscillator_bank_neon, %function
+oscillator_bank_neon:
+
+
+dSample .dn D6.F32
+qPhases .qn Q8.F32
+dPhases_0 .dn D16.F32
+dPhases_1 .dn D17.F32
+qFreqs .qn Q9.F32
+dFreqs_0 .dn D18.F32
+dFreqs_1 .dn D19.F32
+qAmps .qn Q10.F32
+dAmps_0 .dn D20.F32
+dAmps_1 .dn D21.F32
+qFreqDs .qn Q11.F32
+dFreqDs_0 .dn D22.F32
+dFreqDs_1 .dn D23.F32
+qAmpDs .qn Q12.F32
+dAmpDs_0 .dn D24.F32
+dAmpDs_1 .dn D25.F32
+
+qBaseInts .qn Q13.U32 @ Base indexes: unsigned ints x4
+dBaseInts_0 .dn D26.U32
+dBaseInts_1 .dn D27.U32
+qFractions .qn Q14.F32 @ Fraction indexes: floats x4
+qTableBase .qn Q15.U32 @ Base of lookup table
+
+ cmp r0, #0 @ Check for trivial case 1: zero frames
+ it eq
+ bxeq lr @ Return if that's the case (otherwise might have odd behaviour)
+ cmp r2, #4 @ Check for trivial case 2: zero oscillators
+ it lt
+ bxlt lr @ Return if that's the case
+
+ push {r4-r11} @ Now arguments start 32 bytes above SP
+ add r11, sp, #32 @ Pointer to 32 bytes into the stack
+ ldm r11, {r4-r9} @ Load 6 arguments into registers
+
+ vdup qTableBase, r9 @ Move lookup table base index into 4 ints
+
+ @ Outer loop: iterate over the number of oscillators, choosing 4 at a
+ @ time to work with.
+oscbank_oscillator_loop:
+ vld1 {dPhases_0, dPhases_1}, [r4] @ no increment; will store at end of sample loop
+ vld1 {dFreqs_0, dFreqs_1}, [r5]
+ vld1 {dAmps_0, dAmps_1}, [r6]
+ vld1 {dFreqDs_0, dFreqDs_1}, [r7]! @ increment; won't update at end of sample loop
+ vld1 {dAmpDs_0, dAmpDs_1}, [r8]!
+
+ push {r0-r1,r4-r8}
+ @ --- inner loop: iterate over the number of samples ---
+oscbank_sample_loop:
+ vcvt qBaseInts, qPhases @ Take floor(phases)
+ vmov q2.f32, #1.0 @ Load 1.0 into every slot of q2
+ vshl q0.U32, qBaseInts, #2 @ Shift the indexes left 2 (*4 for float addressing)
+ vcvt qFractions, qBaseInts @ int back to float
+ vadd q0.U32, q0.U32, qTableBase @ Find memory addresses
+
+ vmov r4, r5, d0 @ Move two indexes to ARM registers
+ vmov r6, r7, d1 @ Move two more indexes to ARM registers
+ vsub qFractions, qPhases, qFractions @ fraction = phase - floor(phase)
+
+ vldr.64 d0, [r4] @ Load two consecutive floats at each location
+ vldr.64 d1, [r5] @ These hold the previous and following samples in the table
+ vldr.64 d2, [r6] @ TODO: check whether these work at 4-byte alignment
+ vldr.64 d3, [r7]
+
+ @ Format at this point:
+ @ Osc0(before) Osc0(after) Osc1(before) Osc1(after) Osc2(before) Osc2(after) Osc3(before) Osc3(after)
+ @ We want:
+ @ Osc0(before) Osc1(before) Osc2(before) Osc3(before) Osc0(after) Osc1(after) Osc2(after) Osc3(after)
+
+ vuzp.32 q0, q1 @ Now q0 contains before, q1 contains after
+ vsub q2.f32, q2.f32, qFractions @ q2 = 1.0 - fraction
+ vmul q1.f32, q1.f32, qFractions @ q1 = fraction * after
+ vmul q0.f32, q0.f32, q2.f32 @ q0 = (1.0 - fraction) * before
+
+ vadd qPhases, qPhases, qFreqs @ Update phases
+ vadd qFreqs, qFreqs, qFreqDs @ Update frequencies
+
+ vadd q0.f32, q0.f32, q1.f32 @ Add two interpolated components to get the final sample
+ vdup q2.u32, r3 @ Put lookup table size into each element of q2
+ vcvt qBaseInts, qPhases @ Take floor of new phases
+ vmul q0.f32, q0.f32, qAmps @ Multiply samples by current amplitude
+
+ vld1 dSample, [r1] @ Load the current stereo samples
+ vpadd d2.f32, d0.f32, d1.f32 @ Pairwise accumulate q0 (output sample) into d2
+
+ vand q2, q2, qBaseInts @ Logical AND of new phase int leaves 1 bit set only if phase >= table size
+ vpadd d3.f32, d2.f32, d2.f32 @ Pairwise accumulate d2 into d0 --> d0[0] and d0[1] both hold total of 4 oscillators
+ vadd qAmps, qAmps, qAmpDs @ Update amplitudes
+ vcvt q0.f32, q2.u32 @ Convert int back to float after AND operation
+
+ vadd dSample, dSample, d3.f32 @ Add oscillator outputs to each channel
+
+ subs r0, r0, #1 @ numFrames--
+ vsub qPhases, qPhases, q0.f32 @ Keep phases in table range
+ vst1 dSample, [r1]! @ Store back in buffer and increment by 8
+
+ it gt
+ bgt oscbank_sample_loop @ Loop if numFrames > 0
+
+ @ --- end inner loop ---
+ pop {r0-r1,r4-r8} @ Restore registers: restores audioOut and numFrames, among others
+
+ vst1 {dPhases_0, dPhases_1}, [r4]! @ Store phases back to array
+ vst1 {dFreqs_0, dFreqs_1}, [r5]! @ Store frequencies back to array
+ vst1 {dAmps_0, dAmps_1}, [r6]! @ Store amplitudes back to array
+ @ No need to update r7, r8
+
+ subs r2, r2, #4 @ numPartials -= 4
+ it gt
+ bgt oscbank_oscillator_loop @ Loop if numPartials > 0
+
+ pop {r4-r11}
+ bx lr
diff -r 000000000000 -r 8a575ba3ab52 projects/oscillator_bank/main.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/projects/oscillator_bank/main.cpp Fri Oct 31 19:10:17 2014 +0100
@@ -0,0 +1,129 @@
+/*
+ * main.cpp
+ *
+ * Created on: Oct 24, 2014
+ * Author: parallels
+ */
+
+#include
+#include
+#include
+#include
+#include "../../include/RTAudio.h"
+
+using namespace std;
+
+int gNumOscillators = 32;
+int gWavetableLength = 1024;
+
+
+// Handle Ctrl-C by requesting that the audio rendering stop
+void interrupt_handler(int var)
+{
+ gShouldStop = true;
+}
+
+// Print usage information
+void usage(const char * processName)
+{
+ cerr << "Usage: " << processName << " [-h] [-v] [-p period] [-f input] [-a input]" << endl;
+ cerr << " -h: Print this menu\n";
+ cerr << " -v: Enable verbose messages\n";
+ cerr << " -p period: Set the period (hardware buffer) size in sensor frames\n";
+ cerr << " -n oscs: Set the number of oscillators to use (default: 32)\n";
+ cerr << " -w length: Set the wavetable length in samples (default: 1024)\n";
+ cerr << " -m: Enable the matrix (ADC and DAC) for controlling parameters\n";
+}
+
+int main(int argc, char *argv[])
+{
+ int periodSize = 8; // Period size in sensor frames
+ int verbose = 0; // Verbose printing level
+ int useMatrix = 0;
+
+ // Parse command-line arguments
+ while (1) {
+ int c;
+ if ((c = getopt(argc, argv, "hp:vn:w:m")) < 0)
+ break;
+ switch (c) {
+ case 'h':
+ usage(basename(argv[0]));
+ exit(0);
+ case 'p':
+ periodSize = atoi(optarg);
+ if(periodSize < 1)
+ periodSize = 1;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ case 'n':
+ gNumOscillators = atoi(optarg);
+ if(gNumOscillators <= 0) {
+ usage(basename(argv[0]));
+ exit(0);
+ }
+ break;
+ case 'w':
+ gWavetableLength = atoi(optarg);
+ if(gWavetableLength < 4)
+ gWavetableLength = 4;
+ if(gWavetableLength > 16384)
+ gWavetableLength = 16384;
+ break;
+ case 'm':
+ useMatrix = 1;
+ break;
+ case '?':
+ default:
+ usage(basename(argv[0]));
+ exit(1);
+ }
+ }
+
+
+ // Set verbose logging information (optional by using value > 0; default is 0)
+ setVerboseLevel(verbose);
+
+ if(verbose) {
+ cout << "Starting with period size " << periodSize << endl;
+ cout << "--> Using " << gNumOscillators << " oscillators and wavetable of " << gWavetableLength << " samples\n";
+ cout << "--> Matrix ";
+ if(useMatrix) cout << "enabled\n";
+ else cout << "disabled\n";
+ }
+
+ // Initialise the PRU audio device
+ if(initAudio(periodSize, useMatrix, 0) != 0) {
+ cout << "Error: unable to initialise audio" << endl;
+ return -1;
+ }
+
+ // Start the audio device running
+ if(startAudio()) {
+ cout << "Error: unable to start real-time audio" << endl;
+ return -1;
+ }
+
+ // Set up interrupt handler to catch Control-C
+ signal(SIGINT, interrupt_handler);
+
+ // Run until told to stop
+ while(!gShouldStop) {
+ usleep(100000);
+ }
+
+ // Stop the audio device
+ stopAudio();
+
+ if(verbose) {
+ cout << "Cleaning up..." << endl;
+ }
+
+ // Clean up any resources allocated for audio
+ cleanupAudio();
+
+ // All done!
+ return 0;
+}
diff -r 000000000000 -r 8a575ba3ab52 projects/oscillator_bank/render.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/projects/oscillator_bank/render.cpp Fri Oct 31 19:10:17 2014 +0100
@@ -0,0 +1,202 @@
+/*
+ * render.cpp
+ *
+ * Created on: Oct 24, 2014
+ * Author: parallels
+ */
+
+
+#include "../../include/RTAudio.h"
+#include "../../include/Utilities.h"
+#include
+#include
+#include
+#include
+#include
+
+const float kMinimumFrequency = 20.0f;
+const float kMaximumFrequency = 8000.0f;
+
+float *gWavetable; // Buffer holding the precalculated sine lookup table
+float *gPhases; // Buffer holding the phase of each oscillator
+float *gFrequencies; // Buffer holding the frequencies of each oscillator
+float *gAmplitudes; // Buffer holding the amplitudes of each oscillator
+float *gDFrequencies; // Buffer holding the derivatives of frequency
+float *gDAmplitudes; // Buffer holding the derivatives of amplitude
+
+float gAudioSampleRate;
+int gSampleCount; // Sample counter for indicating when to update frequencies
+float gNewMinFrequency;
+float gNewMaxFrequency;
+
+// Task for handling the update of the frequencies using the matrix
+AuxiliaryTask gFrequencyUpdateTask;
+
+// These settings are carried over from main.cpp
+// Setting global variables is an alternative approach
+// to passing a structure to userData in initialise_render()
+
+extern int gNumOscillators;
+extern int gWavetableLength;
+
+void recalculate_frequencies();
+
+extern "C" {
+ // Function prototype for ARM assembly implementation of oscillator bank
+ void oscillator_bank_neon(int numAudioFrames, float *audioOut,
+ int activePartialNum, int lookupTableSize,
+ float *phases, float *frequencies, float *amplitudes,
+ float *freqDerivatives, float *ampDerivatives,
+ float *lookupTable);
+}
+
+// initialise_render() is called once before the audio rendering starts.
+// Use it to perform any initialisation and allocation which is dependent
+// on the period size or sample rate.
+//
+// userData holds an opaque pointer to a data structure that was passed
+// in from the call to initAudio().
+//
+// Return true on success; returning false halts the program.
+
+bool initialise_render(int numChannels, int numMatrixFramesPerPeriod,
+ int numAudioFramesPerPeriod, float matrixSampleRate,
+ float audioSampleRate, void *userData)
+{
+ srandom(time(NULL));
+
+ // Initialise the sine wavetable
+ if(posix_memalign((void **)&gWavetable, 8, (gWavetableLength + 1) * sizeof(float))) {
+ rt_printf("Error allocating wavetable\n");
+ return false;
+ }
+ for(int n = 0; n < gWavetableLength + 1; n++)
+ gWavetable[n] = sinf(2.0 * M_PI * (float)n / (float)gWavetableLength);
+
+ // Allocate the other buffers
+ if(posix_memalign((void **)&gPhases, 16, gNumOscillators * sizeof(float))) {
+ rt_printf("Error allocating phase buffer\n");
+ return false;
+ }
+ if(posix_memalign((void **)&gFrequencies, 16, gNumOscillators * sizeof(float))) {
+ rt_printf("Error allocating frequency buffer\n");
+ return false;
+ }
+ if(posix_memalign((void **)&gAmplitudes, 16, gNumOscillators * sizeof(float))) {
+ rt_printf("Error allocating amplitude buffer\n");
+ return false;
+ }
+ if(posix_memalign((void **)&gDFrequencies, 16, gNumOscillators * sizeof(float))) {
+ rt_printf("Error allocating frequency derivative buffer\n");
+ return false;
+ }
+ if(posix_memalign((void **)&gDAmplitudes, 16, gNumOscillators * sizeof(float))) {
+ rt_printf("Error allocating amplitude derivative buffer\n");
+ return false;
+ }
+
+ // Initialise buffer contents
+
+ float freq = kMinimumFrequency;
+ float increment = (kMaximumFrequency - kMinimumFrequency) / (float)gNumOscillators;
+
+ for(int n = 0; n < gNumOscillators; n++) {
+ gPhases[n] = 0.0;
+
+ if(numMatrixFramesPerPeriod == 0) {
+ // Random frequencies when used without matrix
+ gFrequencies[n] = kMinimumFrequency + (kMaximumFrequency - kMinimumFrequency) * ((float)random() / (float)RAND_MAX);
+ }
+ else {
+ // Constant spread of frequencies when used with matrix
+ gFrequencies[n] = freq;
+ freq += increment;
+ }
+
+ // For efficiency, frequency is expressed in change in wavetable position per sample, not Hz or radians
+ gFrequencies[n] *= (float)gWavetableLength / audioSampleRate;
+ gAmplitudes[n] = ((float)random() / (float)RAND_MAX) / (float)gNumOscillators;
+ gDFrequencies[n] = gDAmplitudes[n] = 0.0;
+ }
+
+ // Initialise auxiliary tasks
+ if((gFrequencyUpdateTask = createAuxiliaryTaskLoop(&recalculate_frequencies, 90, "beaglert-update-frequencies")) == 0)
+ return false;
+
+ gAudioSampleRate = audioSampleRate;
+ gSampleCount = 0;
+
+ return true;
+}
+
+// render() is called regularly at the highest priority by the audio engine.
+// Input and output are given from the audio hardware and the other
+// ADCs and DACs (if available). If only audio is available, numMatrixFrames
+// will be 0.
+
+void render(int numMatrixFrames, int numAudioFrames, float *audioIn, float *audioOut,
+ uint16_t *matrixIn, uint16_t *matrixOut)
+{
+ // Initialise buffer to 0
+ memset(audioOut, 0, 2 * numAudioFrames * sizeof(float));
+
+ // Render audio frames
+ oscillator_bank_neon(numAudioFrames, audioOut,
+ gNumOscillators, gWavetableLength,
+ gPhases, gFrequencies, gAmplitudes,
+ gDFrequencies, gDAmplitudes,
+ gWavetable);
+
+ if(numMatrixFrames != 0 && (gSampleCount += numAudioFrames) >= 128) {
+ gSampleCount = 0;
+ gNewMinFrequency = map(matrixIn[0], 0, MATRIX_MAX, 20.0f, 8000.0f);
+ gNewMaxFrequency = map(matrixIn[1], 0, MATRIX_MAX, 20.0f, 8000.0f);
+
+ // Make sure max >= min
+ if(gNewMaxFrequency < gNewMinFrequency) {
+ float temp = gNewMaxFrequency;
+ gNewMaxFrequency = gNewMinFrequency;
+ gNewMinFrequency = temp;
+ }
+
+ // Request that the lower-priority task run at next opportunity
+ scheduleAuxiliaryTask(gFrequencyUpdateTask);
+ }
+}
+
+// This is a lower-priority call to update the frequencies which will happen
+// periodically when the matrix is enabled. By placing it at a lower priority,
+// it has minimal effect on the audio performance but it will take longer to
+// complete if the system is under heavy audio load.
+
+void recalculate_frequencies()
+{
+ float freq = gNewMinFrequency;
+ float increment = (gNewMaxFrequency - gNewMinFrequency) / (float)gNumOscillators;
+
+ for(int n = 0; n < gNumOscillators; n++) {
+ // Update the frequencies to a regular spread, plus a small amount of randomness
+ // to avoid weird phase effects
+ float randScale = 0.99 + .02 * (float)random() / (float)RAND_MAX;
+ float newFreq = freq * randScale;
+
+ // For efficiency, frequency is expressed in change in wavetable position per sample, not Hz or radians
+ gFrequencies[n] = newFreq * (float)gWavetableLength / gAudioSampleRate;
+
+ freq += increment;
+ }
+}
+
+
+// cleanup_render() is called once at the end, after the audio has stopped.
+// Release any resources that were allocated in initialise_render().
+
+void cleanup_render()
+{
+ free(gWavetable);
+ free(gPhases);
+ free(gFrequencies);
+ free(gAmplitudes);
+ free(gDFrequencies);
+ free(gDAmplitudes);
+}
diff -r 000000000000 -r 8a575ba3ab52 pru_rtaudio.bin
Binary file pru_rtaudio.bin has changed
diff -r 000000000000 -r 8a575ba3ab52 pru_rtaudio.p
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/pru_rtaudio.p Fri Oct 31 19:10:17 2014 +0100
@@ -0,0 +1,753 @@
+.origin 0
+.entrypoint START
+
+#define DBOX_CAPE // Define this to use new cape hardware
+
+#define CLOCK_BASE 0x44E00000
+#define CLOCK_SPI0 0x4C
+#define CLOCK_SPI1 0x50
+#define CLOCK_L4LS 0x60
+
+#define SPI0_BASE 0x48030100
+#define SPI1_BASE 0x481A0100
+#define SPI_BASE SPI0_BASE
+
+#define SPI_SYSCONFIG 0x10
+#define SPI_SYSSTATUS 0x14
+#define SPI_MODULCTRL 0x28
+#define SPI_CH0CONF 0x2C
+#define SPI_CH0STAT 0x30
+#define SPI_CH0CTRL 0x34
+#define SPI_CH0TX 0x38
+#define SPI_CH0RX 0x3C
+#define SPI_CH1CONF 0x40
+#define SPI_CH1STAT 0x44
+#define SPI_CH1CTRL 0x48
+#define SPI_CH1TX 0x4C
+#define SPI_CH1RX 0x50
+
+#define GPIO0 0x44E07000
+#define GPIO1 0x4804C000
+#define GPIO_CLEARDATAOUT 0x190
+#define GPIO_SETDATAOUT 0x194
+
+#define PRU0_ARM_INTERRUPT 19
+
+#define C_ADC_DAC_MEM C24 // PRU0 mem
+#ifdef DBOX_CAPE
+#define DAC_GPIO GPIO0
+#define DAC_CS_PIN (1<<5) // GPIO0:5 = P9 pin 17
+#else
+#define DAC_GPIO GPIO1
+#define DAC_CS_PIN (1<<16) // GPIO1:16 = P9 pin 15
+#endif
+#define DAC_TRM 0 // SPI transmit and receive
+#define DAC_WL 32 // Word length
+#define DAC_CLK_MODE 1 // SPI mode
+#define DAC_CLK_DIV 1 // Clock divider (48MHz / 2^n)
+#define DAC_DPE 1 // d0 = receive, d1 = transmit
+
+#define AD5668_COMMAND_OFFSET 24
+#define AD5668_ADDRESS_OFFSET 20
+#define AD5668_DATA_OFFSET 4
+#define AD5668_REF_OFFSET 0
+
+#ifdef DBOX_CAPE
+#define ADC_GPIO GPIO1
+#define ADC_CS_PIN (1<<16) // GPIO1:16 = P9 pin 15
+#else
+#define ADC_GPIO GPIO1
+#define ADC_CS_PIN (1<<17) // GPIO1:17 = P9 pin 23
+#endif
+#define ADC_TRM 0 // SPI transmit and receive
+#define ADC_WL 16 // Word length
+#define ADC_CLK_MODE 0 // SPI mode
+#define ADC_CLK_DIV 1 // Clock divider (48MHz / 2^n)
+#define ADC_DPE 1 // d0 = receive, d1 = transmit
+
+#define AD7699_CFG_MASK 0xF120 // Mask for config update, unipolar, full BW
+#define AD7699_CHANNEL_OFFSET 9 // 7 bits offset of a 14-bit left-justified word
+#define AD7699_SEQ_OFFSET 3 // sequencer (0 = disable, 3 = scan all)
+
+#define SHARED_COMM_MEM_BASE 0x00010000 // Location where comm flags are written
+#define COMM_SHOULD_STOP 0 // Set to be nonzero when loop should stop
+#define COMM_CURRENT_BUFFER 4 // Which buffer we are on
+#define COMM_BUFFER_FRAMES 8 // How many frames per buffer
+#define COMM_SHOULD_SYNC 12 // Whether to synchronise to an external clock
+#define COMM_SYNC_ADDRESS 16 // Which memory address to find the GPIO on
+#define COMM_SYNC_PIN_MASK 20 // Which pin to read for the sync
+#define COMM_LED_ADDRESS 24 // Which memory address to find the status LED on
+#define COMM_LED_PIN_MASK 28 // Which pin to write to change LED
+#define COMM_FRAME_COUNT 32 // How many frames have elapse since beginning
+#define COMM_USE_SPI 36 // Whether or not to use SPI ADC and DAC
+
+#define MCASP0_BASE 0x48038000
+#define MCASP1_BASE 0x4803C000
+
+#define MCASP_PWRIDLESYSCONFIG 0x04
+#define MCASP_PFUNC 0x10
+#define MCASP_PDIR 0x14
+#define MCASP_PDOUT 0x18
+#define MCASP_PDSET 0x1C
+#define MCASP_PDIN 0x1C
+#define MCASP_PDCLR 0x20
+#define MCASP_GBLCTL 0x44
+#define MCASP_AMUTE 0x48
+#define MCASP_DLBCTL 0x4C
+#define MCASP_DITCTL 0x50
+#define MCASP_RGBLCTL 0x60
+#define MCASP_RMASK 0x64
+#define MCASP_RFMT 0x68
+#define MCASP_AFSRCTL 0x6C
+#define MCASP_ACLKRCTL 0x70
+#define MCASP_AHCLKRCTL 0x74
+#define MCASP_RTDM 0x78
+#define MCASP_RINTCTL 0x7C
+#define MCASP_RSTAT 0x80
+#define MCASP_RSLOT 0x84
+#define MCASP_RCLKCHK 0x88
+#define MCASP_REVTCTL 0x8C
+#define MCASP_XGBLCTL 0xA0
+#define MCASP_XMASK 0xA4
+#define MCASP_XFMT 0xA8
+#define MCASP_AFSXCTL 0xAC
+#define MCASP_ACLKXCTL 0xB0
+#define MCASP_AHCLKXCTL 0xB4
+#define MCASP_XTDM 0xB8
+#define MCASP_XINTCTL 0xBC
+#define MCASP_XSTAT 0xC0
+#define MCASP_XSLOT 0xC4
+#define MCASP_XCLKCHK 0xC8
+#define MCASP_XEVTCTL 0xCC
+#define MCASP_SRCTL0 0x180
+#define MCASP_SRCTL1 0x184
+#define MCASP_SRCTL2 0x188
+#define MCASP_SRCTL3 0x18C
+#define MCASP_SRCTL4 0x190
+#define MCASP_SRCTL5 0x194
+#define MCASP_XBUF0 0x200
+#define MCASP_XBUF1 0x204
+#define MCASP_XBUF2 0x208
+#define MCASP_XBUF3 0x20C
+#define MCASP_XBUF4 0x210
+#define MCASP_XBUF5 0x214
+#define MCASP_RBUF0 0x280
+#define MCASP_RBUF1 0x284
+#define MCASP_RBUF2 0x288
+#define MCASP_RBUF3 0x28C
+#define MCASP_RBUF4 0x290
+#define MCASP_RBUF5 0x294
+#define MCASP_WFIFOCTL 0x1000
+#define MCASP_WFIFOSTS 0x1004
+#define MCASP_RFIFOCTL 0x1008
+#define MCASP_RFIFOSTS 0x100C
+
+#define MCASP_XSTAT_XDATA_BIT 5 // Bit to test for transmit ready
+#define MCASP_RSTAT_RDATA_BIT 5 // Bit to test for receive ready
+
+// Constants used for this particular audio setup
+#define MCASP_BASE MCASP0_BASE
+#ifdef DBOX_CAPE
+#define MCASP_SRCTL_X MCASP_SRCTL2 // Ser. 2 is transmitter
+#define MCASP_SRCTL_R MCASP_SRCTL0 // Ser. 0 is receiver
+#define MCASP_XBUF MCASP_XBUF2
+#define MCASP_RBUF MCASP_RBUF0
+#else
+#define MCASP_SRCTL_X MCASP_SRCTL3 // Ser. 3 is transmitter
+#define MCASP_SRCTL_R MCASP_SRCTL2 // Ser. 2 is receiver
+#define MCASP_XBUF MCASP_XBUF3
+#define MCASP_RBUF MCASP_RBUF2
+#endif
+
+#define MCASP_PIN_AFSX (1 << 28)
+#define MCASP_PIN_AHCLKX (1 << 27)
+#define MCASP_PIN_ACLKX (1 << 26)
+#define MCASP_PIN_AMUTE (1 << 25) // Also, 0 to 3 are XFR0 to XFR3
+
+#ifdef DBOX_CAPE
+#define MCASP_OUTPUT_PINS MCASP_PIN_AHCLKX | (1 << 2) // AHCLKX and AXR2 outputs
+#else
+#define MCASP_OUTPUT_PINS (1 << 3) // Which pins are outputs
+#endif
+
+#define MCASP_DATA_MASK 0xFFFF // 16 bit data
+#define MCASP_DATA_FORMAT 0x807C // MSB first, 0 bit delay, 16 bits, CFG bus, ROR 16bits
+
+#define C_MCASP_MEM C28 // Shared PRU mem
+
+// Flags for the flags register
+#define FLAG_BIT_BUFFER1 0
+#define FLAG_BIT_USE_SPI 1
+
+// Registers used throughout
+
+// r1, r2, r3 are used for temporary storage
+#define reg_frame_current r10 // Current frame count in SPI ADC/DAC transfer
+#define reg_frame_total r11 // Total frame count for SPI ADC/DAC
+#define reg_dac_data r12 // Current dword for SPI DAC
+#define reg_adc_data r13 // Current dword for SPI ADC
+#define reg_mcasp_dac_data r14 // Current dword for McASP DAC
+#define reg_mcasp_adc_data r15 // Current dword for McASP ADC
+#define reg_dac_buf0 r16 // Start pointer to SPI DAC buffer 0
+#define reg_dac_buf1 r17 // Start pointer to SPI DAC buffer 1
+#define reg_dac_current r18 // Pointer to current storage location of SPI DAC
+#define reg_adc_current r19 // Pointer to current storage location of SPI ADC
+#define reg_mcasp_buf0 r20 // Start pointer to McASP DAC buffer 0
+#define reg_mcasp_buf1 r21 // Start pointer to McASP DAC buffer 1
+#define reg_mcasp_dac_current r22 // Pointer to current storage location of McASP DAC
+#define reg_mcasp_adc_current r23 // Pointer to current storage location of McASP ADC
+#define reg_flags r24 // Buffer ID (0 and 1) and other flags
+#define reg_comm_addr r25 // Memory address for communicating with ARM
+#define reg_spi_addr r26 // Base address for SPI
+// r27, r28 used in macros
+#define reg_mcasp_addr r29 // Base address for McASP
+
+
+// Bring CS line low to write to DAC
+.macro DAC_CS_ASSERT
+ MOV r27, DAC_CS_PIN
+ MOV r28, DAC_GPIO + GPIO_CLEARDATAOUT
+ SBBO r27, r28, 0, 4
+.endm
+
+// Bring CS line high at end of DAC transaction
+.macro DAC_CS_UNASSERT
+ MOV r27, DAC_CS_PIN
+ MOV r28, DAC_GPIO + GPIO_SETDATAOUT
+ SBBO r27, r28, 0, 4
+.endm
+
+// Write to DAC TX register
+.macro DAC_TX
+.mparam data
+ SBBO data, reg_spi_addr, SPI_CH0TX, 4
+.endm
+
+// Wait for SPI to finish (uses RXS indicator)
+.macro DAC_WAIT_FOR_FINISH
+ LOOP:
+ LBBO r27, reg_spi_addr, SPI_CH0STAT, 4
+ QBBC LOOP, r27, 0
+.endm
+
+// Read the RX word to clear
+.macro DAC_DISCARD_RX
+ LBBO r27, reg_spi_addr, SPI_CH0RX, 4
+.endm
+
+// Complete DAC write with chip select
+.macro DAC_WRITE
+.mparam reg
+ DAC_CS_ASSERT
+ DAC_TX reg
+ DAC_WAIT_FOR_FINISH
+ DAC_CS_UNASSERT
+ DAC_DISCARD_RX
+.endm
+
+// Bring CS line low to write to ADC
+.macro ADC_CS_ASSERT
+ MOV r27, ADC_CS_PIN
+ MOV r28, ADC_GPIO + GPIO_CLEARDATAOUT
+ SBBO r27, r28, 0, 4
+.endm
+
+// Bring CS line high at end of ADC transaction
+.macro ADC_CS_UNASSERT
+ MOV r27, ADC_CS_PIN
+ MOV r28, ADC_GPIO + GPIO_SETDATAOUT
+ SBBO r27, r28, 0, 4
+.endm
+
+// Write to ADC TX register
+.macro ADC_TX
+.mparam data
+ SBBO data, reg_spi_addr, SPI_CH1TX, 4
+.endm
+
+// Wait for SPI to finish (uses RXS indicator)
+.macro ADC_WAIT_FOR_FINISH
+ LOOP:
+ LBBO r27, reg_spi_addr, SPI_CH1STAT, 4
+ QBBC LOOP, r27, 0
+.endm
+
+// Read the RX word to clear; store output
+.macro ADC_RX
+.mparam data
+ LBBO data, reg_spi_addr, SPI_CH1RX, 4
+.endm
+
+// Complete ADC write+read with chip select
+.macro ADC_WRITE
+.mparam in, out
+ ADC_CS_ASSERT
+ ADC_TX in
+ ADC_WAIT_FOR_FINISH
+ ADC_RX out
+ ADC_CS_UNASSERT
+.endm
+
+// Write a McASP register
+.macro MCASP_REG_WRITE
+.mparam reg, value
+ MOV r27, value
+ SBBO r27, reg_mcasp_addr, reg, 4
+.endm
+
+// Write a McASP register beyond the 0xFF boundary
+.macro MCASP_REG_WRITE_EXT
+.mparam reg, value
+ MOV r27, value
+ MOV r28, reg
+ ADD r28, reg_mcasp_addr, r28
+ SBBO r27, r28, 0, 4
+.endm
+
+// Read a McASP register
+.macro MCASP_REG_READ
+.mparam reg, value
+ LBBO value, reg_mcasp_addr, reg, 4
+.endm
+
+// Read a McASP register beyond the 0xFF boundary
+.macro MCASP_REG_READ_EXT
+.mparam reg, value
+ MOV r28, reg
+ ADD r28, reg_mcasp_addr, r28
+ LBBO value, r28, 0, 4
+.endm
+
+// Set a bit and wait for it to come up
+.macro MCASP_REG_SET_BIT_AND_POLL
+.mparam reg, mask
+ MOV r27, mask
+ LBBO r28, reg_mcasp_addr, reg, 4
+ OR r28, r28, r27
+ SBBO r28, reg_mcasp_addr, reg, 4
+POLL:
+ LBBO r28, reg_mcasp_addr, reg, 4
+ AND r28, r28, r27
+ QBEQ POLL, r28, 0
+.endm
+
+START:
+ // Set up c24 and c25 offsets with CTBIR register
+ // Thus C24 points to start of PRU0 RAM
+ MOV r3, 0x22020 // CTBIR0
+ MOV r2, 0
+ SBBO r2, r3, 0, 4
+
+ // Set up c28 pointer offset for shared PRU RAM
+ MOV r3, 0x22028 // CTPPR0
+ MOV r2, 0x00000120 // To get address 0x00012000
+ SBBO r2, r3, 0, 4
+
+ // Load useful registers for addressing SPI
+ MOV reg_comm_addr, SHARED_COMM_MEM_BASE
+ MOV reg_spi_addr, SPI_BASE
+ MOV reg_mcasp_addr, MCASP_BASE
+
+ // Set ARM such that PRU can write to registers
+ LBCO r0, C4, 4, 4
+ CLR r0, r0, 4
+ SBCO r0, C4, 4, 4
+
+ // Clear flags
+ MOV reg_flags, 0
+
+ // Find out whether we should use SPI ADC and DAC
+ LBBO r2, reg_comm_addr, COMM_USE_SPI, 4
+ QBEQ SPI_FLAG_CHECK_DONE, r2, 0
+ SET reg_flags, reg_flags, FLAG_BIT_USE_SPI
+
+SPI_FLAG_CHECK_DONE:
+ // If we don't use SPI, then skip all this init
+ QBBC SPI_INIT_DONE, reg_flags, FLAG_BIT_USE_SPI
+
+ // Init SPI clock
+ MOV r2, 0x02
+ MOV r3, CLOCK_BASE + CLOCK_SPI0
+ SBBO r2, r3, 0, 4
+
+ // Reset SPI and wait for finish
+ MOV r2, 0x02
+ SBBO r2, reg_spi_addr, SPI_SYSCONFIG, 4
+
+SPI_WAIT_RESET:
+ LBBO r2, reg_spi_addr, SPI_SYSSTATUS, 4
+ QBBC SPI_WAIT_RESET, r2, 0
+
+ // Turn off SPI channels
+ MOV r2, 0
+ SBBO r2, reg_spi_addr, SPI_CH0CTRL, 4
+ SBBO r2, reg_spi_addr, SPI_CH1CTRL, 4
+
+ // Set to master; chip select lines enabled (CS0 used for DAC)
+ MOV r2, 0x00
+ SBBO r2, reg_spi_addr, SPI_MODULCTRL, 4
+
+ // Configure CH0 for DAC
+ MOV r2, (3 << 27) | (DAC_DPE << 16) | (DAC_TRM << 12) | ((DAC_WL - 1) << 7) | (DAC_CLK_DIV << 2) | DAC_CLK_MODE | (1 << 6)
+ SBBO r2, reg_spi_addr, SPI_CH0CONF, 4
+
+ // Configure CH1 for ADC
+ MOV r2, (3 << 27) | (ADC_DPE << 16) | (ADC_TRM << 12) | ((ADC_WL - 1) << 7) | (ADC_CLK_DIV << 2) | ADC_CLK_MODE
+ SBBO r2, reg_spi_addr, SPI_CH1CONF, 4
+
+ // Turn on SPI channels
+ MOV r2, 0x01
+ SBBO r2, reg_spi_addr, SPI_CH0CTRL, 4
+ SBBO r2, reg_spi_addr, SPI_CH1CTRL, 4
+
+ // DAC power-on reset sequence
+ MOV r2, (0x07 << AD5668_COMMAND_OFFSET)
+ DAC_WRITE r2
+
+ // Initialise ADC
+ MOV r2, AD7699_CFG_MASK | (0 << AD7699_CHANNEL_OFFSET) | (0 << AD7699_SEQ_OFFSET)
+ ADC_WRITE r2, r2
+
+ // Enable DAC internal reference
+ MOV r2, (0x08 << AD5668_COMMAND_OFFSET) | (0x01 << AD5668_REF_OFFSET)
+ DAC_WRITE r2
+
+ // Read ADC ch0 and ch1: result is always 2 samples behind so start here
+ MOV r2, AD7699_CFG_MASK | (0x00 << AD7699_CHANNEL_OFFSET)
+ ADC_WRITE r2, r2
+
+ MOV r2, AD7699_CFG_MASK | (0x01 << AD7699_CHANNEL_OFFSET)
+ ADC_WRITE r2, r2
+SPI_INIT_DONE:
+
+// Prepare McASP0 for audio
+MCASP_REG_WRITE MCASP_GBLCTL, 0 // Disable McASP
+MCASP_REG_WRITE_EXT MCASP_SRCTL0, 0 // All serialisers off
+MCASP_REG_WRITE_EXT MCASP_SRCTL1, 0
+MCASP_REG_WRITE_EXT MCASP_SRCTL2, 0
+MCASP_REG_WRITE_EXT MCASP_SRCTL3, 0
+MCASP_REG_WRITE_EXT MCASP_SRCTL4, 0
+MCASP_REG_WRITE_EXT MCASP_SRCTL5, 0
+
+MCASP_REG_WRITE MCASP_PWRIDLESYSCONFIG, 0x02 // Power on
+MCASP_REG_WRITE MCASP_PFUNC, 0x00 // All pins are McASP
+MCASP_REG_WRITE MCASP_PDIR, MCASP_OUTPUT_PINS // Set pin direction
+MCASP_REG_WRITE MCASP_DLBCTL, 0x00
+MCASP_REG_WRITE MCASP_DITCTL, 0x00
+MCASP_REG_WRITE MCASP_RMASK, MCASP_DATA_MASK // 16 bit data receive
+MCASP_REG_WRITE MCASP_RFMT, MCASP_DATA_FORMAT // Set data format
+MCASP_REG_WRITE MCASP_AFSRCTL, 0x100 // I2S mode
+MCASP_REG_WRITE MCASP_ACLKRCTL, 0x80 // Sample on rising edge
+MCASP_REG_WRITE MCASP_AHCLKRCTL, 0x8001 // Internal clock, not inv, /2; irrelevant?
+MCASP_REG_WRITE MCASP_RTDM, 0x03 // Enable TDM slots 0 and 1
+MCASP_REG_WRITE MCASP_RINTCTL, 0x00 // No interrupts
+MCASP_REG_WRITE MCASP_XMASK, MCASP_DATA_MASK // 16 bit data transmit
+MCASP_REG_WRITE MCASP_XFMT, MCASP_DATA_FORMAT // Set data format
+MCASP_REG_WRITE MCASP_AFSXCTL, 0x100 // I2S mode
+MCASP_REG_WRITE MCASP_ACLKXCTL, 0x00 // Transmit on rising edge, sync. xmit and recv
+MCASP_REG_WRITE MCASP_AHCLKXCTL, 0x8001 // External clock from AHCLKX
+MCASP_REG_WRITE MCASP_XTDM, 0x03 // Enable TDM slots 0 and 1
+MCASP_REG_WRITE MCASP_XINTCTL, 0x00 // No interrupts
+
+MCASP_REG_WRITE_EXT MCASP_SRCTL_R, 0x02 // Set up receive serialiser
+MCASP_REG_WRITE_EXT MCASP_SRCTL_X, 0x01 // Set up transmit serialiser
+MCASP_REG_WRITE_EXT MCASP_WFIFOCTL, 0x00 // Disable FIFOs
+MCASP_REG_WRITE_EXT MCASP_RFIFOCTL, 0x00
+
+MCASP_REG_WRITE MCASP_XSTAT, 0xFF // Clear transmit errors
+MCASP_REG_WRITE MCASP_RSTAT, 0xFF // Clear receive errors
+
+MCASP_REG_SET_BIT_AND_POLL MCASP_RGBLCTL, (1 << 1) // Set RHCLKRST
+MCASP_REG_SET_BIT_AND_POLL MCASP_XGBLCTL, (1 << 9) // Set XHCLKRST
+
+// The above write sequence will have temporarily changed the AHCLKX frequency
+// The PLL needs time to settle or the sample rate will be unstable and possibly
+// cause an underrun. Give it ~1ms before going on.
+// 10ns per loop iteration = 10^-8s --> 10^5 iterations needed
+
+ MOV r2, 1 << 28
+ MOV r3, GPIO1 + GPIO_SETDATAOUT
+ SBBO r2, r3, 0, 4
+
+MOV r2, 100000
+MCASP_INIT_WAIT:
+ SUB r2, r2, 1
+ QBNE MCASP_INIT_WAIT, r2, 0
+
+ MOV r2, 1 << 28
+ MOV r3, GPIO1 + GPIO_CLEARDATAOUT
+ SBBO r2, r3, 0, 4
+
+MCASP_REG_SET_BIT_AND_POLL MCASP_RGBLCTL, (1 << 0) // Set RCLKRST
+MCASP_REG_SET_BIT_AND_POLL MCASP_XGBLCTL, (1 << 8) // Set XCLKRST
+MCASP_REG_SET_BIT_AND_POLL MCASP_RGBLCTL, (1 << 2) // Set RSRCLR
+MCASP_REG_SET_BIT_AND_POLL MCASP_XGBLCTL, (1 << 10) // Set XSRCLR
+MCASP_REG_SET_BIT_AND_POLL MCASP_RGBLCTL, (1 << 3) // Set RSMRST
+MCASP_REG_SET_BIT_AND_POLL MCASP_XGBLCTL, (1 << 11) // Set XSMRST
+
+MCASP_REG_WRITE_EXT MCASP_XBUF, 0x00 // Write to the transmit buffer to prevent underflow
+
+MCASP_REG_SET_BIT_AND_POLL MCASP_RGBLCTL, (1 << 4) // Set RFRST
+MCASP_REG_SET_BIT_AND_POLL MCASP_XGBLCTL, (1 << 12) // Set XFRST
+
+// Initialisation
+LBBO reg_frame_total, reg_comm_addr, COMM_BUFFER_FRAMES, 4 // Total frame count (SPI; 2x for McASP)
+MOV reg_dac_buf0, 0 // DAC buffer 0 start pointer
+LSL reg_dac_buf1, reg_frame_total, 4 // DAC buffer 1 start pointer = 8[ch]*2[bytes]*bufsize
+MOV reg_mcasp_buf0, 0 // McASP DAC buffer 0 start pointer
+LSL reg_mcasp_buf1, reg_frame_total, 3 // McASP DAC buffer 1 start pointer = 2[ch]*2[bytes]*2[samples/spi]*bufsize
+CLR reg_flags, reg_flags, FLAG_BIT_BUFFER1 // Bit 0 holds which buffer we are on
+MOV r2, 0
+SBBO r2, reg_comm_addr, COMM_FRAME_COUNT, 4 // Start with frame count of 0
+
+// Here we are out of sync by one TDM slot since the 0 word transmitted above will have occupied
+// the first output slot. Send one more word before jumping into the loop.
+MCASP_DAC_WAIT_BEFORE_LOOP:
+ LBBO r2, reg_mcasp_addr, MCASP_XSTAT, 4
+ QBBC MCASP_DAC_WAIT_BEFORE_LOOP, r2, MCASP_XSTAT_XDATA_BIT
+
+ MCASP_REG_WRITE_EXT MCASP_XBUF, 0x00
+
+// Likewise, read and discard the first sample we get back from the ADC. This keeps the DAC and ADC
+// in sync in terms of which TDM slot we are reading (empirically found that we should throw this away
+// rather than keep it and invert the phase)
+MCASP_ADC_WAIT_BEFORE_LOOP:
+ LBBO r2, reg_mcasp_addr, MCASP_RSTAT, 4
+ QBBC MCASP_ADC_WAIT_BEFORE_LOOP, r2, MCASP_RSTAT_RDATA_BIT
+
+ MCASP_REG_READ_EXT MCASP_RBUF, r2
+
+WRITE_ONE_BUFFER:
+ // Write a single buffer of DAC samples and read a buffer of ADC samples
+ // Load starting positions
+ MOV reg_dac_current, reg_dac_buf0 // DAC: reg_dac_current is current pointer
+ LSL reg_adc_current, reg_frame_total, 5 // 16 * 2 * bufsize
+ ADD reg_adc_current, reg_adc_current, reg_dac_current // ADC: starts 16 * 2 * bufsize beyond DAC
+ MOV reg_mcasp_dac_current, reg_mcasp_buf0 // McASP: set current DAC pointer
+ LSL reg_mcasp_adc_current, reg_frame_total, 4 // McASP ADC: starts 4*2*2*bufsize beyond DAC
+ ADC reg_mcasp_adc_current, reg_mcasp_adc_current, reg_mcasp_dac_current
+ MOV reg_frame_current, 0
+
+WRITE_LOOP:
+ // Write 8 channels to DAC from successive values in memory
+ // At the same time, read 8 channels from ADC
+ // Unrolled by a factor of 2 to get high and low words
+ MOV r1, 0
+ADC_DAC_LOOP:
+ QBBC SPI_DAC_LOAD_DONE, reg_flags, FLAG_BIT_USE_SPI
+ // Load next 2 SPI DAC samples and store zero in their place
+ LBCO reg_dac_data, C_ADC_DAC_MEM, reg_dac_current, 4
+ MOV r2, 0
+ SBCO r2, C_ADC_DAC_MEM, reg_dac_current, 4
+ ADD reg_dac_current, reg_dac_current, 4
+SPI_DAC_LOAD_DONE:
+
+ // On even iterations, load two more samples and choose the first one
+ // On odd iterations, transmit the second of the samples already loaded
+ QBBS MCASP_DAC_HIGH_WORD, r1, 1
+MCASP_DAC_LOW_WORD:
+ // Load next 2 Audio DAC samples and store zero in their place
+ LBCO reg_mcasp_dac_data, C_MCASP_MEM, reg_mcasp_dac_current, 4
+ MOV r2, 0
+ SBCO r2, C_MCASP_MEM, reg_mcasp_dac_current, 4
+ ADD reg_mcasp_dac_current, reg_mcasp_dac_current, 4
+
+ // Mask out the low word (first in little endian)
+ MOV r2, 0xFFFF
+ AND r7, reg_mcasp_dac_data, r2
+
+ QBA MCASP_WAIT_XSTAT
+MCASP_DAC_HIGH_WORD:
+ // Take the high word of the previously loaded data
+ LSR r7, reg_mcasp_dac_data, 16
+
+ // Two audio frames per SPI frame = 4 audio samples per SPI frame
+ // Therefore every 2 channels we send one audio sample; this loop already
+ // sends exactly two SPI channels.
+ // Wait for McASP XSTAT[XDATA] to set indicating we can write more data
+MCASP_WAIT_XSTAT:
+ LBBO r2, reg_mcasp_addr, MCASP_XSTAT, 4
+ QBBC MCASP_WAIT_XSTAT, r2, MCASP_XSTAT_XDATA_BIT
+
+ MCASP_REG_WRITE_EXT MCASP_XBUF, r7
+
+ // Same idea with ADC: even iterations, load the sample into the low word, odd
+ // iterations, load the sample into the high word and store
+ QBBS MCASP_ADC_HIGH_WORD, r1, 1
+MCASP_ADC_LOW_WORD:
+ // Start ADC data at 0
+ LDI reg_mcasp_adc_data, 0
+
+ // Now wait for a received word to become available from the audio ADC
+MCASP_WAIT_RSTAT_LOW:
+ LBBO r2, reg_mcasp_addr, MCASP_RSTAT, 4
+ QBBC MCASP_WAIT_RSTAT_LOW, r2, MCASP_RSTAT_RDATA_BIT
+
+ // Mask low word and store in ADC data register
+ MCASP_REG_READ_EXT MCASP_RBUF, r3
+ MOV r2, 0xFFFF
+ AND reg_mcasp_adc_data, r3, r2
+ QBA MCASP_ADC_DONE
+
+MCASP_ADC_HIGH_WORD:
+ // Wait for a received word to become available from the audio ADC
+MCASP_WAIT_RSTAT_HIGH:
+ LBBO r2, reg_mcasp_addr, MCASP_RSTAT, 4
+ QBBC MCASP_WAIT_RSTAT_HIGH, r2, MCASP_RSTAT_RDATA_BIT
+
+ // Read data and shift 16 bits to the left (into the high word)
+ MCASP_REG_READ_EXT MCASP_RBUF, r3
+ LSL r3, r3, 16
+ OR reg_mcasp_adc_data, reg_mcasp_adc_data, r3
+
+ // Now store the result and increment the pointer
+ SBCO reg_mcasp_adc_data, C_MCASP_MEM, reg_mcasp_adc_current, 4
+ ADD reg_mcasp_adc_current, reg_mcasp_adc_current, 4
+MCASP_ADC_DONE:
+ QBBC SPI_SKIP_WRITE, reg_flags, FLAG_BIT_USE_SPI
+
+ // DAC: transmit low word (first in little endian)
+ MOV r2, 0xFFFF
+ AND r7, reg_dac_data, r2
+ LSL r7, r7, AD5668_DATA_OFFSET
+ MOV r8, (0x03 << AD5668_COMMAND_OFFSET)
+ OR r7, r7, r8
+ LSL r8, r1, AD5668_ADDRESS_OFFSET
+ OR r7, r7, r8
+ DAC_WRITE r7
+
+ // Read ADC channels: result is always 2 commands behind
+ // Start by reading channel 2 (result is channel 0) and go
+ // to 10, but masking the channel number to be between 0 and 7
+ LDI reg_adc_data, 0
+ MOV r7, AD7699_CFG_MASK
+ ADD r8, r1, 2
+ AND r8, r8, 7
+ LSL r8, r8, AD7699_CHANNEL_OFFSET
+ OR r7, r7, r8
+ ADC_WRITE r7, r7
+
+ // Mask out only the relevant 16 bits and store in reg_adc_data
+ MOV r2, 0xFFFF
+ AND reg_adc_data, r7, r2
+
+ // Increment channel index
+ ADD r1, r1, 1
+
+ // DAC: transmit high word (second in little endian)
+ LSR r7, reg_dac_data, 16
+ LSL r7, r7, AD5668_DATA_OFFSET
+ MOV r8, (0x03 << AD5668_COMMAND_OFFSET)
+ OR r7, r7, r8
+ LSL r8, r1, AD5668_ADDRESS_OFFSET
+ OR r7, r7, r8
+ DAC_WRITE r7
+
+ // Read ADC channels: result is always 2 commands behind
+ // Start by reading channel 2 (result is channel 0) and go
+ // to 10, but masking the channel number to be between 0 and 7
+ MOV r7, AD7699_CFG_MASK
+ ADD r8, r1, 2
+ AND r8, r8, 7
+ LSL r8, r8, AD7699_CHANNEL_OFFSET
+ OR r7, r7, r8
+ ADC_WRITE r7, r7
+
+ // Move this result up to the 16 high bits
+ LSL r7, r7, 16
+ OR reg_adc_data, reg_adc_data, r7
+
+ // Store 2 ADC words in memory
+ SBCO reg_adc_data, C_ADC_DAC_MEM, reg_adc_current, 4
+ ADD reg_adc_current, reg_adc_current, 4
+
+ // Repeat 4 times (2 samples per loop, r1 += 1 already happened)
+ ADD r1, r1, 1
+ QBNE ADC_DAC_LOOP, r1, 8
+ QBA ADC_DAC_LOOP_DONE
+
+SPI_SKIP_WRITE:
+ // We get here only if the SPI ADC and DAC are disabled
+ // Just keep the loop going for McASP
+ ADD r1, r1, 2
+ QBNE ADC_DAC_LOOP, r1, 8
+
+ADC_DAC_LOOP_DONE:
+ // Increment number of frames, see if we have more to write
+ ADD reg_frame_current, reg_frame_current, 1
+ QBNE WRITE_LOOP, reg_frame_current, reg_frame_total
+
+WRITE_LOOP_DONE:
+ // Now done, swap the buffers and do the next one
+ // Use r2 as a temp register
+ MOV r2, reg_dac_buf0
+ MOV reg_dac_buf0, reg_dac_buf1
+ MOV reg_dac_buf1, r2
+ MOV r2, reg_mcasp_buf0
+ MOV reg_mcasp_buf0, reg_mcasp_buf1
+ MOV reg_mcasp_buf1, r2
+
+ // Notify ARM of buffer swap
+ XOR reg_flags, reg_flags, (1 << FLAG_BIT_BUFFER1)
+ AND r2, reg_flags, (1 << FLAG_BIT_BUFFER1) // Mask out every but low bit
+ SBBO r2, reg_comm_addr, COMM_CURRENT_BUFFER, 4
+
+ // Increment the frame count in the comm buffer (for status monitoring)
+ LBBO r2, reg_comm_addr, COMM_FRAME_COUNT, 4
+ ADD r2, r2, reg_frame_total
+ SBBO r2, reg_comm_addr, COMM_FRAME_COUNT, 4
+
+ // If LED blink enabled, toggle every 4096 frames
+ LBBO r3, reg_comm_addr, COMM_LED_ADDRESS, 4
+ QBEQ LED_BLINK_DONE, r3, 0
+ MOV r1, 0x1000
+ AND r2, r2, r1 // Test (frame count & 4096)
+ QBEQ LED_BLINK_OFF, r2, 0
+ LBBO r2, reg_comm_addr, COMM_LED_PIN_MASK, 4
+ MOV r1, GPIO_SETDATAOUT
+ ADD r3, r3, r1 // Address for GPIO set register
+ SBBO r2, r3, 0, 4 // Set GPIO pin
+ QBA LED_BLINK_DONE
+LED_BLINK_OFF:
+ LBBO r2, reg_comm_addr, COMM_LED_PIN_MASK, 4
+ MOV r1, GPIO_CLEARDATAOUT
+ ADD r3, r3, r1 // Address for GPIO clear register
+ SBBO r2, r3, 0, 4 // Clear GPIO pin
+LED_BLINK_DONE:
+
+ QBBC TESTLOW, reg_flags, FLAG_BIT_BUFFER1
+ MOV r2, 1 << 28
+ MOV r3, GPIO1 + GPIO_SETDATAOUT
+ SBBO r2, r3, 0, 4
+ QBA TESTDONE
+TESTLOW:
+ MOV r2, 1 << 28
+ MOV r3, GPIO1 + GPIO_CLEARDATAOUT
+ SBBO r2, r3, 0, 4
+TESTDONE:
+
+ // Check if we should finish: flag is zero as long as it should run
+ LBBO r2, reg_comm_addr, COMM_SHOULD_STOP, 4
+ QBEQ WRITE_ONE_BUFFER, r2, 0
+
+CLEANUP:
+ MCASP_REG_WRITE MCASP_GBLCTL, 0x00 // Turn off McASP
+
+ // Turn off SPI if enabled
+ QBBC SPI_CLEANUP_DONE, reg_flags, FLAG_BIT_USE_SPI
+
+ MOV r3, SPI_BASE + SPI_CH0CONF
+ LBBO r2, r3, 0, 4
+ CLR r2, r2, 13
+ CLR r2, r2, 27
+ SBBO r2, r3, 0, 4
+
+ MOV r3, SPI_BASE + SPI_CH0CTRL
+ LBBO r2, r3, 0, 4
+ CLR r2, r2, 1
+ SBBO r2, r3, 0, 4
+SPI_CLEANUP_DONE:
+
+ // Signal the ARM that we have finished
+ MOV R31.b0, PRU0_ARM_INTERRUPT + 16
+ HALT
\ No newline at end of file
diff -r 000000000000 -r 8a575ba3ab52 resources/BB-BONE-BAREAUDI-00A0.dtbo
Binary file resources/BB-BONE-BAREAUDI-00A0.dtbo has changed
diff -r 000000000000 -r 8a575ba3ab52 resources/BB-BONE-BAREAUDI-02-00A0.dts
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/resources/BB-BONE-BAREAUDI-02-00A0.dts Fri Oct 31 19:10:17 2014 +0100
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+/dts-v1/;
+/plugin/;
+
+/ {
+ compatible = "ti,beaglebone", "ti,beaglebone-black";
+
+ /* identification */
+ part-number = "BB-BONE-BAREAUDI-02";
+ version = "00A0", "A0";
+
+ /* state the resources this cape uses */
+ exclusive-use =
+ /* the pin header uses */
+ "P9.31", /* mcasp0: mcasp0_aclkx */
+ "P9.29", /* mcasp0: mcasp0_fsx */
+ "P9.28", /* mcasp0: mcasp0_axr2 */
+ "P9.25", /* mcasp0: mcasp0_ahclkx */
+ /* the hardware ip uses */
+ "gpio1_18", "gpio1_19",
+ "mcasp0";
+
+ fragment@0 {
+ target = <&am33xx_pinmux>;
+ __overlay__ {
+
+ i2c2_pins: pinmux_i2c2_pins {
+ pinctrl-single,pins = <
+ 0x150 0x72 /*spi0_scl.i2c2_sda,SLEWCTRL_SLOW | INPUT_PULLUP |MODE2*/
+ 0x154 0x72 /*spi0_d0.i2c2_scl,SLEWCTRL_SLOW | INPUT_PULLUP | MODE2*/
+ >;
+ };
+
+ bone_audio_cape_audio_pins: pinmux_bone_audio_cape_audio_pins {
+ pinctrl-single,pins = <
+ 0x1ac 0x00 /* mcasp0_ahclkx, MODE0 | INPUT */
+ 0x19c 0x22 /* mcasp0_ahclkr, */
+ 0x194 0x20 /* mcasp0_fsx, MODE0 | OUTPUT */
+ 0x190 0x20 /* mcasp0_aclkr.mcasp0_aclkx, MODE0 | OUTPUT_PULLDOWN */
+ 0x198 0x20
+ >;
+ };
+ };
+ };
+
+ fragment@1 {
+ target = <&mcasp0>;
+ __overlay__ {
+ pinctrl-names = "default";
+ pinctrl-0 = <&bone_audio_cape_audio_pins>;
+
+ status = "okay";
+
+ op-mode = <0>; /* MCASP_IIS_MODE */
+ tdm-slots = <2>;
+ num-serializer = <16>;
+ serial-dir = < /* 0: INACTIVE, 1: TX, 2: RX */
+ 2 0 1 0
+ 0 0 0 0
+ 0 0 0 0
+ 0 0 0 0
+ >;
+ tx-num-evt = <1>;
+ rx-num-evt = <1>;
+ };
+ };
+
+};