Mercurial > hg > beaglert
changeset 0:8a575ba3ab52
Initial commit.
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.cproject Fri Oct 31 19:10:17 2014 +0100 @@ -0,0 +1,196 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<?fileVersion 4.0.0?><cproject storage_type_id="org.eclipse.cdt.core.XmlProjectDescriptionStorage"> + <storageModule moduleId="org.eclipse.cdt.core.settings"> + <cconfiguration id="cdt.managedbuild.config.gnu.exe.debug.528876549"> + <storageModule buildSystemId="org.eclipse.cdt.managedbuilder.core.configurationDataProvider" id="cdt.managedbuild.config.gnu.exe.debug.528876549" moduleId="org.eclipse.cdt.core.settings" name="Debug"> + <externalSettings/> + <extensions> + <extension id="org.eclipse.cdt.core.ELF" point="org.eclipse.cdt.core.BinaryParser"/> + <extension id="org.eclipse.cdt.core.GmakeErrorParser" point="org.eclipse.cdt.core.ErrorParser"/> + <extension id="org.eclipse.cdt.core.CWDLocator" point="org.eclipse.cdt.core.ErrorParser"/> + <extension id="org.eclipse.cdt.core.GCCErrorParser" point="org.eclipse.cdt.core.ErrorParser"/> + <extension id="org.eclipse.cdt.core.GASErrorParser" point="org.eclipse.cdt.core.ErrorParser"/> + <extension id="org.eclipse.cdt.core.GLDErrorParser" point="org.eclipse.cdt.core.ErrorParser"/> + </extensions> + </storageModule> + <storageModule moduleId="cdtBuildSystem" version="4.0.0"> + <configuration artifactName="${ProjName}" buildArtefactType="org.eclipse.cdt.build.core.buildArtefactType.exe" buildProperties="org.eclipse.cdt.build.core.buildType=org.eclipse.cdt.build.core.buildType.debug,org.eclipse.cdt.build.core.buildArtefactType=org.eclipse.cdt.build.core.buildArtefactType.exe" cleanCommand="rm -rf" description="" id="cdt.managedbuild.config.gnu.exe.debug.528876549" name="Debug" parent="cdt.managedbuild.config.gnu.exe.debug"> + <folderInfo id="cdt.managedbuild.config.gnu.exe.debug.528876549." name="/" resourcePath=""> + <toolChain id="cdt.managedbuild.toolchain.gnu.exe.debug.681872250" name="Linux GCC" superClass="cdt.managedbuild.toolchain.gnu.exe.debug"> + <targetPlatform id="cdt.managedbuild.target.gnu.platform.exe.debug.295375065" name="Debug Platform" superClass="cdt.managedbuild.target.gnu.platform.exe.debug"/> + <builder buildPath="${workspace_loc:/BBB_audio+input/Debug}" id="cdt.managedbuild.target.gnu.builder.exe.debug.26322421" keepEnvironmentInBuildfile="false" managedBuildOn="true" name="Gnu Make Builder" superClass="cdt.managedbuild.target.gnu.builder.exe.debug"> + <outputEntries> + <entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="outputPath" name="Debug"/> + <entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="outputPath" name="Release"/> + </outputEntries> + </builder> + <tool id="cdt.managedbuild.tool.gnu.archiver.base.1542380883" name="GCC Archiver" superClass="cdt.managedbuild.tool.gnu.archiver.base"/> + <tool command="arm-linux-gnueabihf-g++" id="cdt.managedbuild.tool.gnu.cpp.compiler.exe.debug.2030825480" name="GCC C++ Compiler" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.exe.debug"> + <option id="gnu.cpp.compiler.exe.debug.option.optimization.level.2006448218" name="Optimization Level" superClass="gnu.cpp.compiler.exe.debug.option.optimization.level" value="gnu.cpp.compiler.optimization.level.more" valueType="enumerated"/> + <option id="gnu.cpp.compiler.exe.debug.option.debugging.level.1267706246" name="Debug Level" superClass="gnu.cpp.compiler.exe.debug.option.debugging.level" value="gnu.cpp.compiler.debugging.level.max" valueType="enumerated"/> + <option id="gnu.cpp.compiler.option.include.paths.2031219124" name="Include paths (-I)" superClass="gnu.cpp.compiler.option.include.paths" valueType="includePath"> + <listOptionValue builtIn="false" value="/usr/arm-linux-gnueabihf/include/xenomai/include"/> + <listOptionValue builtIn="false" value="/usr/arm-linux-gnueabihf/include/ne10"/> + </option> + <option id="gnu.cpp.compiler.option.other.other.1516989263" name="Other flags" superClass="gnu.cpp.compiler.option.other.other" value="-c -fmessage-length=0 -Wpointer-arith -Wunused-result -D_GNU_SOURCE -D_REENTRANT -D__XENO__" valueType="string"/> + <option id="gnu.cpp.compiler.option.warnings.allwarn.1392851614" name="All warnings (-Wall)" superClass="gnu.cpp.compiler.option.warnings.allwarn" value="true" valueType="boolean"/> + <inputType id="cdt.managedbuild.tool.gnu.cpp.compiler.input.1166892316" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.input"/> + </tool> + <tool command="arm-linux-gnueabihf-gcc" id="cdt.managedbuild.tool.gnu.c.compiler.exe.debug.917207395" name="GCC C Compiler" superClass="cdt.managedbuild.tool.gnu.c.compiler.exe.debug"> + <option defaultValue="gnu.c.optimization.level.none" id="gnu.c.compiler.exe.debug.option.optimization.level.326761664" name="Optimization Level" superClass="gnu.c.compiler.exe.debug.option.optimization.level" value="gnu.c.optimization.level.more" valueType="enumerated"/> + <option id="gnu.c.compiler.exe.debug.option.debugging.level.471732683" name="Debug Level" superClass="gnu.c.compiler.exe.debug.option.debugging.level" value="gnu.c.debugging.level.max" valueType="enumerated"/> + <option id="gnu.c.compiler.option.include.paths.358825414" name="Include paths (-I)" superClass="gnu.c.compiler.option.include.paths" valueType="includePath"> + <listOptionValue builtIn="false" value="/usr/arm-linux-gnueabihf/include/xenomai/include"/> + <listOptionValue builtIn="false" value="/usr/arm-linux-gnueabihf/include/ne10"/> + </option> + <option id="gnu.c.compiler.option.misc.other.835792865" name="Other flags" superClass="gnu.c.compiler.option.misc.other" value="-c -fmessage-length=0 -Wpointer-arith -Wunused-result -D_GNU_SOURCE -D_REENTRANT -D__XENO__ -std=gnu99" valueType="string"/> + <option id="gnu.c.compiler.option.warnings.allwarn.1145989346" name="All warnings (-Wall)" superClass="gnu.c.compiler.option.warnings.allwarn" value="true" valueType="boolean"/> + <inputType id="cdt.managedbuild.tool.gnu.c.compiler.input.308014221" superClass="cdt.managedbuild.tool.gnu.c.compiler.input"/> + </tool> + <tool id="cdt.managedbuild.tool.gnu.c.linker.exe.debug.214461086" name="GCC C Linker" superClass="cdt.managedbuild.tool.gnu.c.linker.exe.debug"/> + <tool command="arm-linux-gnueabihf-g++" id="cdt.managedbuild.tool.gnu.cpp.linker.exe.debug.1669966018" name="GCC C++ Linker" superClass="cdt.managedbuild.tool.gnu.cpp.linker.exe.debug"> + <option id="gnu.cpp.link.option.paths.462980690" name="Library search path (-L)" superClass="gnu.cpp.link.option.paths" valueType="libPaths"> + <listOptionValue builtIn="false" value="/usr/arm-linux-gnueabihf/lib"/> + <listOptionValue builtIn="false" value="/usr/arm-linux-gnueabihf/lib/xenomai"/> + <listOptionValue builtIn="false" value="/usr/lib/x86_64-linux-gnu"/> + </option> + <option id="gnu.cpp.link.option.libs.139390951" name="Libraries (-l)" superClass="gnu.cpp.link.option.libs" valueType="libs"> + <listOptionValue builtIn="false" value="rt"/> + <listOptionValue builtIn="false" value="native"/> + <listOptionValue builtIn="false" value="xenomai"/> + </option> + <option id="gnu.cpp.link.option.flags.2096887116" name="Linker flags" superClass="gnu.cpp.link.option.flags" value="-pthread -rdynamics" valueType="string"/> + <option id="gnu.cpp.link.option.userobjs.537608578" name="Other objects" superClass="gnu.cpp.link.option.userobjs" valueType="userObjs"> + <listOptionValue builtIn="false" value=""${workspace_loc:/${ProjName}/libprussdrv.a}""/> + <listOptionValue builtIn="false" value=""${workspace_loc:/${ProjName}/libNE10.a}""/> + </option> + <inputType id="cdt.managedbuild.tool.gnu.cpp.linker.input.450978479" superClass="cdt.managedbuild.tool.gnu.cpp.linker.input"> + <additionalInput kind="additionalinputdependency" paths="$(USER_OBJS)"/> + <additionalInput kind="additionalinput" paths="$(LIBS)"/> + </inputType> + </tool> + <tool command="arm-linux-gnueabihf-as" id="cdt.managedbuild.tool.gnu.assembler.exe.debug.37270610" name="GCC Assembler" superClass="cdt.managedbuild.tool.gnu.assembler.exe.debug"> + <option id="gnu.both.asm.option.include.paths.1403814918" name="Include paths (-I)" superClass="gnu.both.asm.option.include.paths" valueType="includePath"> + <listOptionValue builtIn="false" value="/usr/arm-linux-gnueabihf/include/xenomai/include"/> + <listOptionValue builtIn="false" value="/usr/arm-linux-gnueabihf/include/ne10"/> + </option> + <inputType id="cdt.managedbuild.tool.gnu.assembler.input.1788972942" superClass="cdt.managedbuild.tool.gnu.assembler.input"/> + </tool> + </toolChain> + </folderInfo> + <sourceEntries> + <entry excluding="audio_routines_old.S" flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="core"/> + <entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="include"/> + <entry excluding="d-box|basic_sensor|basic|basic_analog_output" flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="projects"/> + </sourceEntries> + </configuration> + </storageModule> + <storageModule moduleId="org.eclipse.cdt.core.externalSettings"/> + </cconfiguration> + <cconfiguration id="cdt.managedbuild.config.gnu.exe.release.1521194538"> + <storageModule buildSystemId="org.eclipse.cdt.managedbuilder.core.configurationDataProvider" id="cdt.managedbuild.config.gnu.exe.release.1521194538" moduleId="org.eclipse.cdt.core.settings" name="Release"> + <externalSettings/> + <extensions> + <extension id="org.eclipse.cdt.core.ELF" point="org.eclipse.cdt.core.BinaryParser"/> + <extension id="org.eclipse.cdt.core.GmakeErrorParser" point="org.eclipse.cdt.core.ErrorParser"/> + <extension id="org.eclipse.cdt.core.CWDLocator" point="org.eclipse.cdt.core.ErrorParser"/> + <extension id="org.eclipse.cdt.core.GCCErrorParser" point="org.eclipse.cdt.core.ErrorParser"/> + <extension id="org.eclipse.cdt.core.GASErrorParser" point="org.eclipse.cdt.core.ErrorParser"/> + <extension id="org.eclipse.cdt.core.GLDErrorParser" point="org.eclipse.cdt.core.ErrorParser"/> + </extensions> + </storageModule> + <storageModule moduleId="cdtBuildSystem" version="4.0.0"> + <configuration artifactName="${ProjName}" buildArtefactType="org.eclipse.cdt.build.core.buildArtefactType.exe" buildProperties="org.eclipse.cdt.build.core.buildType=org.eclipse.cdt.build.core.buildType.release,org.eclipse.cdt.build.core.buildArtefactType=org.eclipse.cdt.build.core.buildArtefactType.exe" cleanCommand="rm -rf" description="" id="cdt.managedbuild.config.gnu.exe.release.1521194538" name="Release" parent="cdt.managedbuild.config.gnu.exe.release"> + <folderInfo id="cdt.managedbuild.config.gnu.exe.release.1521194538." name="/" resourcePath=""> + <toolChain id="cdt.managedbuild.toolchain.gnu.exe.release.1612059942" name="Linux GCC" superClass="cdt.managedbuild.toolchain.gnu.exe.release"> + <targetPlatform id="cdt.managedbuild.target.gnu.platform.exe.release.908983575" name="Debug Platform" superClass="cdt.managedbuild.target.gnu.platform.exe.release"/> + <builder buildPath="${workspace_loc:/BBB_audio+input/Release}" id="cdt.managedbuild.target.gnu.builder.exe.release.511190290" keepEnvironmentInBuildfile="false" managedBuildOn="true" name="Gnu Make Builder" superClass="cdt.managedbuild.target.gnu.builder.exe.release"/> + <tool id="cdt.managedbuild.tool.gnu.archiver.base.810674388" name="GCC Archiver" superClass="cdt.managedbuild.tool.gnu.archiver.base"/> + <tool command="arm-linux-gnueabihf-g++" id="cdt.managedbuild.tool.gnu.cpp.compiler.exe.release.163790048" name="GCC C++ Compiler" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.exe.release"> + <option id="gnu.cpp.compiler.exe.release.option.optimization.level.2031085570" name="Optimization Level" superClass="gnu.cpp.compiler.exe.release.option.optimization.level" value="gnu.cpp.compiler.optimization.level.more" valueType="enumerated"/> + <option id="gnu.cpp.compiler.exe.release.option.debugging.level.701035863" name="Debug Level" superClass="gnu.cpp.compiler.exe.release.option.debugging.level" value="gnu.cpp.compiler.debugging.level.none" valueType="enumerated"/> + <option id="gnu.cpp.compiler.option.include.paths.823255770" name="Include paths (-I)" superClass="gnu.cpp.compiler.option.include.paths" valueType="includePath"> + <listOptionValue builtIn="false" value="/usr/arm-linux-gnueabihf/include"/> + <listOptionValue builtIn="false" value="/usr/arm-linux-gnueabihf/include/ne10"/> + <listOptionValue builtIn="false" value="/usr/arm-linux-gnueabihf/include/xenomai/include"/> + </option> + <inputType id="cdt.managedbuild.tool.gnu.cpp.compiler.input.601059736" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.input"/> + </tool> + <tool command="arm-linux-gnueabihf-gcc" commandLinePattern="${COMMAND} ${FLAGS} ${OUTPUT_FLAG} ${OUTPUT_PREFIX}${OUTPUT} ${INPUTS}" id="cdt.managedbuild.tool.gnu.c.compiler.exe.release.1302828968" name="GCC C Compiler" superClass="cdt.managedbuild.tool.gnu.c.compiler.exe.release"> + <option defaultValue="gnu.c.optimization.level.most" id="gnu.c.compiler.exe.release.option.optimization.level.1118673208" name="Optimization Level" superClass="gnu.c.compiler.exe.release.option.optimization.level" value="gnu.c.optimization.level.more" valueType="enumerated"/> + <option id="gnu.c.compiler.exe.release.option.debugging.level.1329456558" name="Debug Level" superClass="gnu.c.compiler.exe.release.option.debugging.level" value="gnu.c.debugging.level.none" valueType="enumerated"/> + <option id="gnu.c.compiler.option.include.paths.1269113471" name="Include paths (-I)" superClass="gnu.c.compiler.option.include.paths" valueType="includePath"> + <listOptionValue builtIn="false" value="/usr/arm-linux-gnueabihf/include"/> + <listOptionValue builtIn="false" value="/usr/arm-linux-gnueabihf/include/xenomai/include"/> + </option> + <inputType id="cdt.managedbuild.tool.gnu.c.compiler.input.574072828" superClass="cdt.managedbuild.tool.gnu.c.compiler.input"/> + </tool> + <tool id="cdt.managedbuild.tool.gnu.c.linker.exe.release.281634187" name="GCC C Linker" superClass="cdt.managedbuild.tool.gnu.c.linker.exe.release"/> + <tool command="arm-linux-gnueabihf-g++" id="cdt.managedbuild.tool.gnu.cpp.linker.exe.release.929570421" name="GCC C++ Linker" superClass="cdt.managedbuild.tool.gnu.cpp.linker.exe.release"> + <option id="gnu.cpp.link.option.paths.277042583" name="Library search path (-L)" superClass="gnu.cpp.link.option.paths" valueType="libPaths"> + <listOptionValue builtIn="false" value="/usr/arm-linux-gnueabihf/lib"/> + <listOptionValue builtIn="false" value="/usr/arm-linux-gnueabihf/lib/xenomai"/> + </option> + <option id="gnu.cpp.link.option.libs.1930118082" name="Libraries (-l)" superClass="gnu.cpp.link.option.libs" valueType="libs"> + <listOptionValue builtIn="false" value="rt"/> + <listOptionValue builtIn="false" value="native"/> + <listOptionValue builtIn="false" value="xenomai"/> + </option> + <option id="gnu.cpp.link.option.flags.1308374111" name="Linker flags" superClass="gnu.cpp.link.option.flags" value="-pthread -Wpointer-arith" valueType="string"/> + <option id="gnu.cpp.link.option.userobjs.1473502069" name="Other objects" superClass="gnu.cpp.link.option.userobjs" valueType="userObjs"> + <listOptionValue builtIn="false" value=""${workspace_loc:/${ProjName}/libprussdrv.a}""/> + <listOptionValue builtIn="false" value=""${workspace_loc:/${ProjName}/libNE10.a}""/> + </option> + <inputType id="cdt.managedbuild.tool.gnu.cpp.linker.input.1607203279" superClass="cdt.managedbuild.tool.gnu.cpp.linker.input"> + <additionalInput kind="additionalinputdependency" paths="$(USER_OBJS)"/> + <additionalInput kind="additionalinput" paths="$(LIBS)"/> + </inputType> + </tool> + <tool command="arm-linux-gnueabihf-as" id="cdt.managedbuild.tool.gnu.assembler.exe.release.2052985276" name="GCC Assembler" superClass="cdt.managedbuild.tool.gnu.assembler.exe.release"> + <inputType id="cdt.managedbuild.tool.gnu.assembler.input.1212659593" superClass="cdt.managedbuild.tool.gnu.assembler.input"/> + </tool> + </toolChain> + </folderInfo> + <sourceEntries> + <entry excluding="audio_routines_old.S" flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="core"/> + <entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="include"/> + <entry excluding="d-box|basic_sensor|basic|basic_analog_output" flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="projects"/> + </sourceEntries> + </configuration> + </storageModule> + <storageModule moduleId="org.eclipse.cdt.core.externalSettings"/> + </cconfiguration> + </storageModule> + <storageModule moduleId="cdtBuildSystem" version="4.0.0"> + <project id="BBB_audio+input.cdt.managedbuild.target.gnu.exe.1596461844" name="Executable" projectType="cdt.managedbuild.target.gnu.exe"/> + </storageModule> + <storageModule moduleId="refreshScope" versionNumber="2"> + <configuration configurationName="Release"> + <resource resourceType="PROJECT" workspacePath="/BBB_SimpleInstrument"/> + </configuration> + <configuration configurationName="basic"> + <resource resourceType="PROJECT" workspacePath="/BeagleRT"/> + </configuration> + <configuration configurationName="Debug"> + <resource resourceType="PROJECT" workspacePath="/BBB_SimpleInstrument"/> + </configuration> + </storageModule> + <storageModule moduleId="scannerConfiguration"> + <autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/> + <scannerConfigBuildInfo instanceId="cdt.managedbuild.config.gnu.exe.release.1521194538;cdt.managedbuild.config.gnu.exe.release.1521194538.;cdt.managedbuild.tool.gnu.cpp.compiler.exe.release.163790048;cdt.managedbuild.tool.gnu.cpp.compiler.input.601059736"> + <autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileCPP"/> + </scannerConfigBuildInfo> + <scannerConfigBuildInfo instanceId="cdt.managedbuild.config.gnu.exe.debug.528876549;cdt.managedbuild.config.gnu.exe.debug.528876549.;cdt.managedbuild.tool.gnu.cpp.compiler.exe.debug.2030825480;cdt.managedbuild.tool.gnu.cpp.compiler.input.1166892316"> + <autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileCPP"/> + </scannerConfigBuildInfo> + <scannerConfigBuildInfo instanceId="cdt.managedbuild.config.gnu.exe.debug.528876549;cdt.managedbuild.config.gnu.exe.debug.528876549.;cdt.managedbuild.tool.gnu.c.compiler.exe.debug.917207395;cdt.managedbuild.tool.gnu.c.compiler.input.308014221"> + <autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileC"/> + </scannerConfigBuildInfo> + <scannerConfigBuildInfo instanceId="cdt.managedbuild.config.gnu.exe.release.1521194538;cdt.managedbuild.config.gnu.exe.release.1521194538.;cdt.managedbuild.tool.gnu.c.compiler.exe.release.1302828968;cdt.managedbuild.tool.gnu.c.compiler.input.574072828"> + <autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileC"/> + </scannerConfigBuildInfo> + </storageModule> + <storageModule moduleId="org.eclipse.cdt.internal.ui.text.commentOwnerProjectMappings"/> + <storageModule moduleId="org.eclipse.cdt.core.LanguageSettingsProviders"/> + <storageModule moduleId="org.eclipse.cdt.make.core.buildtargets"/> +</cproject>
--- /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 -------------------------------------------
--- /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 @@ +
--- /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 @@ +
--- /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
Binary file .metadata/.plugins/org.eclipse.core.resources/.root/.indexes/properties.index has changed
--- /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
Binary file .metadata/.plugins/org.eclipse.core.resources/.safetable/org.eclipse.core.resources has changed
--- /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
--- /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
--- /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
--- /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
--- /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
--- /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 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<typeInfoHistroy/>
--- /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 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<qualifiedTypeNameHistroy/>
--- /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 @@ +<?xml version="1.0" encoding="UTF-8"?> +<section name="Workbench"> + <section name="ChooseWorkspaceDialogSettings"> + <item value="330" key="DIALOG_Y_ORIGIN"/> + <item value="0" key="DIALOG_X_ORIGIN"/> + </section> + <section name="WORKBENCH_SETTINGS"> + <list key="ENABLED_TRANSFERS"> + </list> + </section> +</section>
--- /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 @@ +<?xml version="1.0" encoding="UTF-8"?> +<section name="Workbench"> +</section>
--- /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 @@ +<?xml version="1.0" encoding="UTF-8"?> +<workbench progressCount="16" version="2.0"> +<workbenchAdvisor/> +<window height="768" maximized="true" width="1016" x="2034" y="46"> +<fastViewData fastViewLocation="1024"/> +<perspectiveBar> +<itemSize x="160"/> +</perspectiveBar> +<coolbarLayout> +<coolItem id="group.file" itemType="typeGroupMarker"/> +<coolItem id="org.eclipse.ui.workbench.file" itemType="typeToolBarContribution" x="132" y="30"/> +<coolItem id="additions" itemType="typeGroupMarker"/> +<coolItem id="org.eclipse.debug.ui.launchActionSet" itemType="typeToolBarContribution" x="48" y="30"/> +<coolItem id="org.eclipse.search.searchActionSet" itemType="typeToolBarContribution" x="48" y="30"/> +<coolItem id="group.nav" itemType="typeGroupMarker"/> +<coolItem id="org.eclipse.ui.workbench.navigate" itemType="typeToolBarContribution" x="184" y="30"/> +<coolItem id="group.editor" itemType="typeGroupMarker"/> +<coolItem id="group.help" itemType="typeGroupMarker"/> +<coolItem id="org.eclipse.ui.workbench.help" itemType="typeToolBarContribution" x="-1" y="-1"/> +</coolbarLayout> +<page aggregateWorkingSetId="Aggregate for window 1381999860394" focus="true" label="Workspace - Resource"> +<editors> +<editorArea activeWorkbook="DefaultEditorWorkbook"> +<info part="DefaultEditorWorkbook"> +<folder appearance="1" expanded="2"> +<presentation id="org.eclipse.ui.presentations.WorkbenchPresentationFactory"/> +</folder> +</info> +</editorArea> +</editors> +<views> +<view id="org.eclipse.ui.navigator.ProjectExplorer" partName="Project Explorer"> +<viewState CommonNavigator.LINKING_ENABLED="0" org.eclipse.cdt.ui.cview.groupincludes="false" org.eclipse.cdt.ui.cview.groupmacros="false" org.eclipse.cdt.ui.editor.CUChildren="true" org.eclipse.ui.navigator.resources.workingSets.showTopLevelWorkingSets="0"/> +</view> +<view id="org.eclipse.ui.views.TaskList" partName="Tasks"> +<viewState PRIMARY_SORT_FIELD="org.eclipse.ui.ide.completionField" categoryGroup="none" markerContentGenerator="org.eclipse.ui.ide.tasksGenerator" partName="Tasks"> +<columnWidths org.eclipse.ui.ide.completionField="40" org.eclipse.ui.ide.descriptionField="350" org.eclipse.ui.ide.locationField="105" org.eclipse.ui.ide.markerType="801" org.eclipse.ui.ide.pathField="140" org.eclipse.ui.ide.priorityField="35" org.eclipse.ui.ide.resourceField="105"/> +<visible IMemento.internal.id="org.eclipse.ui.ide.completionField"/> +<visible IMemento.internal.id="org.eclipse.ui.ide.priorityField"/> +<visible IMemento.internal.id="org.eclipse.ui.ide.descriptionField"/> +<visible IMemento.internal.id="org.eclipse.ui.ide.resourceField"/> +<visible IMemento.internal.id="org.eclipse.ui.ide.pathField"/> +<visible IMemento.internal.id="org.eclipse.ui.ide.locationField"/> +<visible IMemento.internal.id="org.eclipse.ui.ide.markerType"/> +</viewState> +</view> +<view id="org.eclipse.ui.views.ContentOutline" partName="Outline"> +<viewState/> +</view> +</views> +<perspectives activePart="org.eclipse.ui.navigator.ProjectExplorer" activePerspective="org.eclipse.ui.resourcePerspective"> +<perspective editorAreaTrimState="2" editorAreaVisible="1" fixed="0" version="0.016"> +<descriptor class="org.eclipse.ui.internal.ide.application.ResourcePerspective" id="org.eclipse.ui.resourcePerspective" label="Resource"/> +<alwaysOnActionSet id="org.eclipse.ui.cheatsheets.actionSet"/> +<alwaysOnActionSet id="org.eclipse.search.searchActionSet"/> +<alwaysOnActionSet id="org.eclipse.ui.edit.text.actionSet.annotationNavigation"/> +<alwaysOnActionSet id="org.eclipse.ui.edit.text.actionSet.navigation"/> +<alwaysOnActionSet id="org.eclipse.ui.edit.text.actionSet.convertLineDelimitersTo"/> +<alwaysOnActionSet id="org.eclipse.ui.externaltools.ExternalToolsSet"/> +<alwaysOnActionSet id="org.eclipse.ui.actionSet.keyBindings"/> +<alwaysOnActionSet id="org.eclipse.ui.actionSet.openFiles"/> +<alwaysOnActionSet id="org.eclipse.rse.core.search.searchActionSet"/> +<alwaysOnActionSet id="org.eclipse.ui.NavigateActionSet"/> +<show_view_action id="org.eclipse.ui.navigator.ProjectExplorer"/> +<show_view_action id="org.eclipse.ui.views.BookmarkView"/> +<show_view_action id="org.eclipse.ui.views.ContentOutline"/> +<show_view_action id="org.eclipse.ui.views.PropertySheet"/> +<show_view_action id="org.eclipse.ui.views.ProblemView"/> +<show_view_action id="org.eclipse.ui.views.ProgressView"/> +<show_view_action id="org.eclipse.ui.views.TaskList"/> +<show_view_action id="org.eclipse.ant.ui.views.AntView"/> +<show_view_action id="org.eclipse.pde.runtime.LogView"/> +<new_wizard_action id="org.eclipse.ui.wizards.new.folder"/> +<new_wizard_action id="org.eclipse.ui.wizards.new.file"/> +<new_wizard_action id="org.eclipse.ui.editors.wizards.UntitledTextFileWizard"/> +<perspective_action id="org.eclipse.cdt.ui.CPerspective"/> +<perspective_action id="org.eclipse.jdt.ui.JavaPerspective"/> +<perspective_action id="org.eclipse.jdt.ui.JavaBrowsingPerspective"/> +<perspective_action id="org.eclipse.team.cvs.ui.cvsPerspective"/> +<perspective_action id="org.eclipse.team.ui.TeamSynchronizingPerspective"/> +<hide_toolbar_item_id id="org.eclipse.jdt.ui.actions.OpenProjectWizard"/> +<hide_toolbar_item_id id="org.eclipse.ui.edit.text.toggleShowSelectedElementOnly"/> +<view id="org.eclipse.ui.navigator.ProjectExplorer"/> +<view id="org.eclipse.ui.views.ContentOutline"/> +<view id="org.eclipse.ui.views.TaskList"/> +<fastViewBars/> +<layout> +<mainWindow> +<info folder="true" part="topLeft"> +<folder activePageID="org.eclipse.ui.navigator.ProjectExplorer" appearance="2" expanded="2"> +<page content="org.eclipse.ui.navigator.ProjectExplorer" label="Project Explorer"/> +<page content="org.eclipse.ui.views.BookmarkView" label="LabelNotFound"/> +<page content="org.eclipse.ui.views.ResourceNavigator" label="LabelNotFound"/> +<page content="org.eclipse.team.ccvs.ui.RepositoriesView" label="LabelNotFound"/> +<presentation id="org.eclipse.ui.presentations.WorkbenchPresentationFactory"> +<part id="0"/> +</presentation> +</folder> +</info> +<info folder="true" part="org.eclipse.ui.internal.ViewStack@2e1ed620" ratio="0.7495049" ratioLeft="757" ratioRight="253" relationship="2" relative="topLeft"> +<folder appearance="2" expanded="2"> +<page content="org.eclipse.ui.internal.introview" label="LabelNotFound"/> +<page content="org.eclipse.help.ui.HelpView" label="LabelNotFound"/> +<page content="org.eclipse.ui.cheatsheets.views.CheatSheetView" label="LabelNotFound"/> +<presentation id="org.eclipse.ui.presentations.WorkbenchPresentationFactory"/> +</folder> +</info> +<info part="org.eclipse.ui.editorss" ratio="0.25940594" ratioLeft="262" ratioRight="748" relationship="2" relative="topLeft"/> +<info folder="true" part="bottomRight" ratio="0.6599064" ratioLeft="423" ratioRight="218" relationship="4" relative="org.eclipse.ui.editorss"> +<folder activePageID="org.eclipse.ui.views.TaskList" appearance="2" expanded="2"> +<page content="org.eclipse.ui.views.TaskList" label="Tasks"/> +<page content="org.eclipse.team.ui.GenericHistoryView" label="LabelNotFound"/> +<presentation id="org.eclipse.ui.presentations.WorkbenchPresentationFactory"> +<part id="0"/> +</presentation> +</folder> +</info> +<info folder="true" part="bottomLeft" ratio="0.49921995" ratioLeft="320" ratioRight="321" relationship="4" relative="topLeft"> +<folder activePageID="org.eclipse.ui.views.ContentOutline" appearance="2" expanded="2"> +<page content="org.eclipse.ui.views.ContentOutline" label="Outline"/> +<page content="org.eclipse.ant.ui.views.AntView" label="LabelNotFound"/> +<presentation id="org.eclipse.ui.presentations.WorkbenchPresentationFactory"> +<part id="0"/> +</presentation> +</folder> +</info> +</mainWindow> +</layout> +</perspective> +</perspectives> +<workingSets/> +<navigationHistory/> +<input factoryID="org.eclipse.ui.internal.model.ResourceFactory" path="/" type="8"/> +</page> +<workbenchWindowAdvisor/> +<actionBarAdvisor/> +<trimLayout> +<trimArea IMemento.internal.id="128"> +<trimItem IMemento.internal.id="org.eclipse.ui.internal.WorkbenchWindow.topBar"/> +</trimArea> +<trimArea IMemento.internal.id="1024"> +<trimItem IMemento.internal.id="org.eclise.ui.internal.FastViewBar"/> +<trimItem IMemento.internal.id="org.eclipse.jface.action.StatusLineManager"/> +<trimItem IMemento.internal.id="org.eclipse.ui.internal.progress.ProgressRegion"/> +</trimArea> +</trimLayout> +</window> +<mruList/> +</workbench> \ No newline at end of file
--- /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 @@ +<?xml version="1.0" encoding="UTF-8"?> +<workingSetManager> +<workingSet aggregate="true" factoryID="org.eclipse.ui.internal.WorkingSetFactory" id="1381999860395_0" label="Window Working Set" name="Aggregate for window 1381999860394"/> +</workingSetManager> \ No newline at end of file
--- /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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.project Fri Oct 31 19:10:17 2014 +0100 @@ -0,0 +1,83 @@ +<?xml version="1.0" encoding="UTF-8"?> +<projectDescription> + <name>BeagleRT</name> + <comment></comment> + <projects> + </projects> + <buildSpec> + <buildCommand> + <name>org.eclipse.cdt.managedbuilder.core.genmakebuilder</name> + <triggers>clean,full,incremental,</triggers> + <arguments> + <dictionary> + <key>?name?</key> + <value></value> + </dictionary> + <dictionary> + <key>org.eclipse.cdt.make.core.append_environment</key> + <value>true</value> + </dictionary> + <dictionary> + <key>org.eclipse.cdt.make.core.autoBuildTarget</key> + <value>all</value> + </dictionary> + <dictionary> + <key>org.eclipse.cdt.make.core.buildArguments</key> + <value></value> + </dictionary> + <dictionary> + <key>org.eclipse.cdt.make.core.buildCommand</key> + <value>make</value> + </dictionary> + <dictionary> + <key>org.eclipse.cdt.make.core.buildLocation</key> + <value>${workspace_loc:/BBB_audio+input/Debug}</value> + </dictionary> + <dictionary> + <key>org.eclipse.cdt.make.core.cleanBuildTarget</key> + <value>clean</value> + </dictionary> + <dictionary> + <key>org.eclipse.cdt.make.core.contents</key> + <value>org.eclipse.cdt.make.core.activeConfigSettings</value> + </dictionary> + <dictionary> + <key>org.eclipse.cdt.make.core.enableAutoBuild</key> + <value>false</value> + </dictionary> + <dictionary> + <key>org.eclipse.cdt.make.core.enableCleanBuild</key> + <value>true</value> + </dictionary> + <dictionary> + <key>org.eclipse.cdt.make.core.enableFullBuild</key> + <value>true</value> + </dictionary> + <dictionary> + <key>org.eclipse.cdt.make.core.fullBuildTarget</key> + <value>all</value> + </dictionary> + <dictionary> + <key>org.eclipse.cdt.make.core.stopOnError</key> + <value>true</value> + </dictionary> + <dictionary> + <key>org.eclipse.cdt.make.core.useDefaultBuildCmd</key> + <value>true</value> + </dictionary> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder</name> + <triggers>full,incremental,</triggers> + <arguments> + </arguments> + </buildCommand> + </buildSpec> + <natures> + <nature>org.eclipse.cdt.core.cnature</nature> + <nature>org.eclipse.cdt.core.ccnature</nature> + <nature>org.eclipse.cdt.managedbuilder.core.managedBuildNature</nature> + <nature>org.eclipse.cdt.managedbuilder.core.ScannerConfigNature</nature> + </natures> +</projectDescription>
--- /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
--- /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 <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <fcntl.h> +#include <poll.h> + + +/**************************************************************** + * 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; +} +
--- /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(); +} +
--- /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 <iostream> +#include <stdlib.h> +#include <cstdio> +#include <cerrno> +#include <fcntl.h> +#include <sys/mman.h> + +// Xenomai-specific includes +#include <sys/mman.h> +#include <native/task.h> +#include <native/timer.h> +#include <rtdk.h> + +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; +}
--- /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 <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <strings.h> +#include <math.h> +#include <iostream> +#include <assert.h> +#include <vector> + +// Xenomai-specific includes +#include <sys/mman.h> +#include <native/task.h> +#include <native/timer.h> +#include <rtdk.h> + +#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<InternalAuxiliaryTask*> 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<InternalAuxiliaryTask*>::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<InternalAuxiliaryTask*>::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<InternalAuxiliaryTask*>::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; +}
--- /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; +}
--- /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_ */
--- /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 <iostream> +#include <iomanip> +#include <string> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <linux/i2c.h> +#include <linux/i2c-dev.h> +#include <sys/ioctl.h> +#include <stropts.h> + +#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_ */
--- /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_ */
--- /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 <stdint.h> + +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_ */
--- /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_ */
--- /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_ */
--- /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 */ \ +} \ +
--- /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 <sys/types.h> +#include <pthread.h> + +#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
--- /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 <stdint.h> + +// 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_ */
--- /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 <iostream> +#include <cstdlib> +#include <libgen.h> +#include <signal.h> +#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; +}
--- /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 <cmath> + +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() +{ + +}
--- /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 <iostream> +#include <cstdlib> +#include <libgen.h> +#include <signal.h> +#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; +}
--- /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 <rtdk.h> +#include <cmath> + +// 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() +{ + +}
--- /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 <iostream> +#include <cstdlib> +#include <libgen.h> +#include <signal.h> +#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; +}
--- /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 <rtdk.h> +#include <cmath> + +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() +{ + +}
--- /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 <math.h> + + +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); +}
--- /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 <stdio.h> +#include <string> + +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<sampleCount; i++) + retVal = process(); + } + + return retVal; +} + +inline void ADSR::gate(int gate) { + + if (gate) + state = env_attack; + else if (state != env_idle) + state = env_release; +} + +inline int ADSR::getState() { + return state; +} + +inline void ADSR::reset() { + state = env_idle; + output = 0.0; +} + +inline float ADSR::getOutput() { + return output; +} + + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/projects/d-box/AnalogInput.cpp Fri Oct 31 19:10:17 2014 +0100 @@ -0,0 +1,163 @@ +/* + * AnalogInput.cpp + * + * Created on: Oct 17, 2013 + * Author: Victor Zappi + */ + + +#include "AnalogInput.h" + +using namespace std; + + +AnalogInput::AnalogInput() +{ + ActivateAnalogHnd = NULL; + activateAnalogPath = ""; + analogIsSet = false; + + AnalogInHnd = NULL; + analogInPath = ""; + helperNumFound = false; + + // support var for init + // these are fixed for BBB + startPath = "/sys/devices/bone_capemgr.*/slots"; + readPath = ""; + + buffer = (char*) malloc (sizeof(char)*lSize); // reading buffer + + verbose = false; +} + + +AnalogInput::~AnalogInput() +{ + free(buffer); +} + +int AnalogInput::initAnalogInputs() +{ + if(analogIsSet) + { + if(verbose) + cout << "Fine, but Analog Input already active..."<< endl; + return 0; + } + + // title + if(verbose) + cout << "Init Analog Input"<< endl; + + + // first: activate analog pins on cape manager + // cape-bone-iio > /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); + +} + + +
--- /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 <iostream> +#include <sstream> +#include <stdio.h> +#include <stdlib.h> +#include <glob.h> + +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_ */
--- /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 <math.h> +#include "Biquad.h" +#include <iostream> + +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; +}
--- /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
--- /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 <iostream> +#include <string> +#include <stdio.h> +#include <stdlib.h> + +#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_ */
--- /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<max; i++) + { + tk0_touchPosX[i] = TK0.getSliderPosition()[i]; + tk0_touchSize[i] = TK0.getSlidersize()[i]; + + tk1_touchPosX[i] = TK1.getSliderPosition()[i]; + tk1_touchSize[i] = TK1.getSlidersize()[i]; + } + tk0_touchPosY = TK0.getSliderPositionH(); + tk1_touchPosY = TK1.getSliderPositionH(); + fsr_read = (double)FSR.read(fsr_pinNum); + } + + if(gpio[0]!=-1) + { + if(gpio_read(fdDi[0], &digitalIn[0])==-1) + return 1; + } + + return 0; +} + + + +DboxSensors::DboxSensors() +{ + resetSensorsData(); +} + + + +DboxSensors::~DboxSensors() +{ + if(gpio[0]!=-1) + gpio_dismiss(fdDi[0], gpio[0]); +} + + + +//-------------------------------------------------------------------------------------------------------- +// private methods +//-------------------------------------------------------------------------------------------------------- + +// idle values +void DboxSensors::resetSensorsData() +{ + int max = 3; + if(newSensors) + max = 5; + + for(int i=0; i<max; i++) + { + tk0_touchPosX[i] = -1; + tk0_touchPosY = -1; + tk0_touchSize[i] = 0; + + tk1_touchPosX[i] = -1; + tk1_touchPosY = -1; + tk1_touchSize[i] = 0; + + fsr_read = 0; + } + + return; +} + + + + + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/projects/d-box/DboxSensors.h Fri Oct 31 19:10:17 2014 +0100 @@ -0,0 +1,128 @@ +/* + * DboxSensors.h + * + * Created on: May 19, 2014 + * Author: Victor Zappi + */ + +#ifndef DBOXSENSORS_H_ +#define DBOXSENSORS_H_ + +#include <stdio.h> +#include <sys/mount.h> // mount() +#include <string.h> // strerror() +#include <fstream> // fstream +#include <iostream> +#include <unistd.h> // usleep() +#include <glob.h> // glob() +#include <sys/time.h> // elapsed time +#include <sys/stat.h> // mkdir() +#include <algorithm> // 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_ */
--- /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 <NE10.h> + +//#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_ */
--- /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 <cstdlib> +#include <cmath> + +#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; +}
--- /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 <stdint.h> + +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
--- /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() +{} +
--- /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_ */
--- /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 <stdlib.h> + +#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<filterNum; k++) +// { +// dist = fabs(oscillatorNextNormFreq[parCnt]-filterFreqs[k]); +// if(dist<=filterQ[k]) +// { +// dmp = dist/filterQ[k]; +// parDamp *= dmp*dmp*dmp; +// } +// } + + + // each filter is a band pass notch filter + + // if at least one is active + if (filNum > 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; +}
--- /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 <string> + +#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_ */
--- /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 }; + + + +
--- /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 <cstdlib> +#include <ctime> +#include <stdlib.h> + +#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
--- /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 <iostream> +#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); +}
--- /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 <pthread.h> + +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_
--- /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
--- /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 <sys/mman.h> + +#include <native/task.h> +#include <native/timer.h> +#include <rtdk.h> +#endif + +#ifdef DBOX_USE_XENOMAI + +#define dbox_printf rt_printf + +#else + +#define dbox_printf printf + +#endif + +#endif /* DBOX_CONFIG_H */
--- /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<globbuf.gl_pathc; i++) + { + // playing with 0-9 char digits, forming a number from 0 to 9999 + logNum = (globbuf.gl_pathv[i][pathLen]-48) * 1000; // 42 to 45 are the indices of the chars forming the file index + logNum += (globbuf.gl_pathv[i][pathLen+1]-48) * 100; + logNum += (globbuf.gl_pathv[i][pathLen+2]-48) * 10; + logNum += globbuf.gl_pathv[i][pathLen+3]-48; + if(logNum > 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*>( &(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<MAX_TOUCHES; i++) + logFile << s0Touches_[i] << "\t"; // sensor 0 touch pos x + for(int i=0; i<MAX_TOUCHES; i++) + logFile << s0Size_[i] << "\t"; // sensor 0 touch size + logFile << s0LastIndex << "\t" // sensor 0 last index + << fsr_ << "\t" // sensor 0 FSR pressure + << s1TouchNum << "\t"; // sensor 1 touch count + for(int i=0; i<MAX_TOUCHES; i++) + logFile << s1Touches_[i] << "\t"; // sensor 1 touch pos x + for(int i=0; i<MAX_TOUCHES; i++) + logFile << s1Size_[i] << "\t"; // sensor 1 touch size + logFile << s1LastIndex << "\t" // sensor 1 last index + //... AND SO ON + << "\n"; + + //dbox_printf("%d\n", s0LastIndex); + //dbox_printf("s0TouchNum: %d\t s0Touches[0]: %f\t s0Size[0]: %f\t s0LastIndex: %d\n", s0TouchNum, s0Touches_[0], s0Size_[0], s0LastIndex); + +} + +void logData(unsigned long long time) +{ + // if it's time to change write-file + if(logCnt >= 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; +}
--- /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 <string.h> +#include <pthread.h> +#include <stdio.h> +#include <fstream> // file handle +#include <iostream> // stringstream +#include <sstream> // stringstream +#include <glob.h> // alternative to dirent.h to handle files in dirs +#include <iomanip> // setfill +#include <sys/time.h> // elapsed time + +#include "config.h" +#include "prio.h" + +using namespace std; + +int initLogLoop(); +void *logLoop(void *); + + +#endif /* LOGGER_H_ */
--- /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 <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <strings.h> +#include <math.h> +#include <iostream> +#include <signal.h> // interrupt handler +#include <assert.h> +#include <vector> +#include <dirent.h> // to handle files in dirs +#include <mntent.h> // to check if device is mounted +#include <sys/mount.h> // mount() +#include <sys/time.h> // elapsed time +#include <NE10.h> // neon library + +// thread priority +#include <pthread.h> +#include <sched.h> + +// get_opt_long +#include <getopt.h> + +#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<OscillatorBank*> 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 <std::string> 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; i<fileCnt; i++){ + strcpy(fullFileName, gDirName); + strcat(fullFileName, "/"); + strncat(fullFileName, files[i].c_str(), 255 - strlen(gDirName)); + dbox_printf("Loading file %s...\n", fullFileName); + OscillatorBank *bank = new OscillatorBank(fullFileName); + if(bank->initBank(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; +}
--- /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; + } +} + +//----------------------------------------------------------------------------------------------------------- + +
--- /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 <pthread.h> +#include <sched.h> +#include <iostream> + +//----------------------------------------------------------------------------------------------------------- +// set maximum real-time priority to this thread +//----------------------------------------------------------------------------------------------------------- +void set_realtime_priority(int order); +//----------------------------------------------------------------------------------------------------------- + +#endif /* PRIO_H_ */
--- /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 <assert.h> +#include <cmath> +#include <vector> + +#undef DBOX_CAPE_TEST + +#define N_OCT 4.0 // maximum number of octaves on sensor 1 + +extern vector<OscillatorBank*> 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 + +
--- /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 <stdio.h> +#include <pthread.h> +#include <unistd.h> +#include <math.h> +#include <vector> +#include "prio.h" +#include "sensors.h" +#include "OscillatorBank.h" +#include "DboxSensors.h" + + +//---------------------------------------- +// main extern variables +//---------------------------------------- +extern vector<OscillatorBank*> 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; i<MAX_TOUCHES; i++) + { + s0Touches_[i] = 0.0; + s0Size_[i] = 0.0; + + s1Touches_[i] = 0.0; + s1Size_[i] = 0.0; + } + + return 0; +} + +void sensorLoop(void *) +{ + timeval start, end; + unsigned long elapsedTime; + //float touchSize = 0; // once used for timbre + + + + float *s0Touches; + float *s0Size; + int s0PrevTouchNum = 0; + int s0SortedTouchIndices[MAX_TOUCHES]; + float s0SortedTouches[MAX_TOUCHES]; + float s0PrevSortedTouches[MAX_TOUCHES]; + + float *s1Touches; + float *s1Size; + int s1PrevTouchNum = 0; + int s1SortedTouchIndices[MAX_TOUCHES]; + float s1SortedTouches[MAX_TOUCHES]; + float s1PrevSortedTouches[MAX_TOUCHES]; + + float freqScaler = 0; + int fsrMin = 0;//50; // was 200 + int fsrMax = 1799;//1300; // was 800 + float vel = 0; + float prevVel = 0; + float filterMaxF = 0; + if(gVerbose==1) + dbox_printf("__________set Control Thread priority\n"); + + if(gVerbose==1) + dbox_printf("_________________Control Thread!\n"); + + // get freq scaler, cos freqs must be scaled according to the wavetable used in the oscillator bank + freqScaler = gOscBanks[gCurrentOscBank]->getFrequencyScaler(); + 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<MAX_TOUCHES; i++) + { + s0Touches_[i] = s0Touches[i]; + s0Size_[i] = s0Size[i]; + + s1Touches_[i] = s1Touches[i]; + s1Size_[i] = s1Size[i]; + } + + gSensor0LatestTouchNum = s0TouchNum; + if(s0TouchNum > 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; i<s0TouchNum; i++) + { + for(int p=0; p<s0PrevTouchNum; p++) + { + int index = i*s0PrevTouchNum+p; // permutation id [says between which touches we are calculating distance] + distances[index] = fabs(s0Touches[i]-s0PrevSortedTouches[p]); + ids[index] = index; + if(index>0) + { + // sort, from min to max distance + float tmp; + while(distances[index]<distances[index-1]) + { + tmp = ids[index-1]; + ids[index-1] = ids[index]; + ids[index] = tmp; + + tmp = distances[index-1]; + distances[index-1] = distances[index]; + distances[index] = tmp; + + index--; + + if(index == 0) + break; + } + } + } + } + + int sorted = 0; + bool currAssigned[MAX_TOUCHES] = {false}; + bool prevAssigned[MAX_TOUCHES] = {false}; + + // track touches assigning index according to shortest distance + for(int i=0; i<s0TouchNum*s0PrevTouchNum; i++) + { + int currentIndex = ids[i]/s0PrevTouchNum; + int prevIndex = ids[i]%s0PrevTouchNum; + // avoid double assignment + if(!currAssigned[currentIndex] && !prevAssigned[prevIndex]) + { + currAssigned[currentIndex] = true; + prevAssigned[prevIndex] = true; + s0SortedTouchIndices[currentIndex] = prevIndex; + sorted++; + } + } + // we still have to assign a free index to new touches + if(s0PrevTouchNum<s0TouchNum) + { + for(int i=0; i<s0TouchNum; i++) + { + if(!currAssigned[i]) + s0SortedTouchIndices[i] = sorted++; // assign next free index + + // update tracked value + s0SortedTouches[s0SortedTouchIndices[i]] = s0Touches[i]; + s0PrevSortedTouches[i] = s0SortedTouches[i]; + if(s0SortedTouchIndices[i]==s0TouchNum-1) + s0LastIndex = i; + + // accumulate sizes for timbre + //touchSize += s0Size[i]; + } + } + else // some touches have disappeared... + { + // ...we have to shift all indices... + for(int i=s0PrevTouchNum-1; i>=0; i--) + { + if(!prevAssigned[i]) + { + for(int j=0; j<s0TouchNum; j++) + { + // ...only if touches that disappeared were before the current one + if(s0SortedTouchIndices[j]>i) + s0SortedTouchIndices[j]--; + } + } + } + // done! now update + for(int i=0; i<s0TouchNum; i++) + { + // update tracked value + s0SortedTouches[s0SortedTouchIndices[i]] = s0Touches[i]; + s0PrevSortedTouches[i] = s0SortedTouches[i]; + if(s0SortedTouchIndices[i]==s0TouchNum-1) + s0LastIndex = i; + + // accumulate sizes for timbre + //touchSize += s0Size[i]; + } + } + } + else // nothing's changed since last round + { + for(int i=0; i<s0TouchNum; i++) + { + // update tracked value + s0SortedTouches[s0SortedTouchIndices[i]] = s0Touches[i]; + s0PrevSortedTouches[i] = s0SortedTouches[i]; + + // accumulate sizes for timbre + //touchSize += s0Size[i]; + } + } + + if(s0TouchNum == 0) + s0LastIndex = -1; + + // timbre + //touchSize = (touchSize > 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; i<gOscBanks[gCurrentOscBank]->filterNum; 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; i<gOscBanks[gCurrentOscBank]->filterNum; 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; i<s1TouchNum; i++) + { + for(int p=0; p<s1PrevTouchNum; p++) + { + int index = i*s1PrevTouchNum+p; // permutation id [says between which touches we are calculating distance] + distances[index] = fabs(s1Touches[i]-s1PrevSortedTouches[p]); + ids[index] = index; + if(index>0) + { + // sort, from min to max distance + float tmp; + while(distances[index]<distances[index-1]) + { + tmp = ids[index-1]; + ids[index-1] = ids[index]; + ids[index] = tmp; + + tmp = distances[index-1]; + distances[index-1] = distances[index]; + distances[index] = tmp; + + index--; + + if(index == 0) + break; + } + } + } + } + + int sorted = 0; + bool currAssigned[MAX_TOUCHES] = {false}; + bool prevAssigned[MAX_TOUCHES] = {false}; + + // track touches assigning index according to shortest distance + for(int i=0; i<s1TouchNum*s1PrevTouchNum; i++) + { + int currentIndex = ids[i]/s1PrevTouchNum; + int prevIndex = ids[i]%s1PrevTouchNum; + // avoid double assignment + if(!currAssigned[currentIndex] && !prevAssigned[prevIndex]) + { + currAssigned[currentIndex] = true; + prevAssigned[prevIndex] = true; + s1SortedTouchIndices[currentIndex] = prevIndex; + sorted++; + } + } + // we still have to assign a free index to new touches + if(s1PrevTouchNum<s1TouchNum) + { + for(int i=0; i<s1TouchNum; i++) + { + if(!currAssigned[i]) + s1SortedTouchIndices[i] = sorted++; // assign next free index + + // update tracked value + s1SortedTouches[s1SortedTouchIndices[i]] = s1Touches[i]; + s1PrevSortedTouches[i] = s1SortedTouches[i]; + if(s1SortedTouchIndices[i]==s1TouchNum-1) + s1LastIndex = i; + } + } + else // some touches have disappeared... + { + // ...we have to shift all indices... + for(int i=s1PrevTouchNum-1; i>=0; i--) + { + if(!prevAssigned[i]) + { + for(int j=0; j<s1TouchNum; j++) + { + // ...only if touches that disappeared were before the current one + if(s1SortedTouchIndices[j]>i) + s1SortedTouchIndices[j]--; + } + } + } + // done! now update + for(int i=0; i<s1TouchNum; i++) + { + // update tracked value + s1SortedTouches[s1SortedTouchIndices[i]] = s1Touches[i]; + s1PrevSortedTouches[i] = s1SortedTouches[i]; + if(s1SortedTouchIndices[i]==s1TouchNum-1) + s1LastIndex = i; + } + } + } + else // nothing's changed since last round + { + for(int i=0; i<s1TouchNum; i++) + { + // update tracked value + s1SortedTouches[s1SortedTouchIndices[i]] = s1Touches[i]; + s1PrevSortedTouches[i] = s1SortedTouches[i]; + } + } + } + + if(s1TouchNum > 0) + { + gSensor1LatestTouchIndex = s1LastIndex; + } + else + s1LastIndex = -1; + +/* dbox_printf("-----------------------------\nnum: %d, latest: %d\n", s1TouchNum, gSensor1LatestTouchIndex); + for(int i=0; i<s1TouchNum; i++) + dbox_printf("\t%f\n", gSensor1LatestTouchPos[i]); + dbox_printf("------\n"); + for(int i=0; i<s1TouchNum; i++) + dbox_printf("\t%f\n", s1SortedTouches[i]);*/ + + + + // update variables for both sensors + s0PrevTouchNum = s0TouchNum; + s1PrevTouchNum = s1TouchNum; + } + else + dbox_printf("Come on instrument!\n"); //break + + gettimeofday(&start, NULL); + } + + dbox_printf("sensor thread ended\n"); +} + +void *keyboardLoop(void *) +{ + if(gVerbose==1) + cout << "_________________Keyboard Control Thread!" << endl; + + char keyStroke = '.'; + cout << "Press q to quit." << endl; + + float speed; + + do + { + keyStroke = getchar(); + while(getchar()!='\n'); // to read the first stroke + + switch (keyStroke) + { + //---------------------------------------------------------------------------- + case 'a': + gOscBanks[gCurrentOscBank]->hopNumTh = 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; +}
--- /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_ */
--- /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<parNum; i++) + { + delete[] partialFrequencies[i]; + delete[] partialAmplitudes[i]; + delete[] partialFreqDelta[i]; + delete[] partialAmpDelta[i]; + + } + } + + delete[] partialFrequencies; + delete[] partialAmplitudes; + delete[] partialNumFrames; + delete[] partialFreqDelta; + delete[] partialAmpDelta; + delete[] partialFreqMean; + } + + if(activePartialNum != NULL) + { + for(unsigned int i=0; i<hopNum+1; i++) + delete[] activePartials[i]; + + delete[] activePartialNum; + delete[] activePartials ; + } +} + +void Partials::init(int parN, int hopS, bool isDBX) +{ + if(!isDBX) + { + parNum = parN; + hopSize = hopS; + + partialFrequencies = new float *[parNum]; + partialAmplitudes = new float *[parNum]; + partialNumFrames = new unsigned int[parNum]; + partialStartFrame = new unsigned int[parNum]; + partialStartSample = new unsigned int[parNum]; + partialEndSample = new unsigned int[parNum]; + partialFreqDelta = new float *[parNum]; + partialAmpDelta = new float *[parNum]; + partialFreqMean = new float[parNum]; + + + + // init in one shot + fill(partialFreqMean, partialFreqMean+parNum, 0); // mean is zero + + partialFrequencies[0] = NULL; // for free check + } + else + { + parNum = parN; + hopSize = hopS; + + partialFrequencies = new float *[parNum]; + partialAmplitudes = new float *[parNum]; + partialNumFrames = new unsigned int[parNum]; + partialStartFrame = new unsigned int[parNum]; + partialFreqDelta = new float *[parNum]; + partialAmpDelta = new float *[parNum]; + partialFreqMean = new float[parNum]; + + partialFrequencies[0] = NULL; // for free check + } +} + + + +void Partials::update(int parIndex, int frameNum) +{ + partialFrequencies[parIndex] = new float[frameNum]; + partialAmplitudes[parIndex] = new float[frameNum]; + partialFreqDelta[parIndex] = new float[frameNum]; + partialAmpDelta[parIndex] = new float[frameNum]; + + fill(partialFreqDelta[parIndex], partialFreqDelta[parIndex]+frameNum, 99999.0); // in the end, only the last one will have 99999 + fill(partialAmpDelta[parIndex], partialAmpDelta[parIndex]+frameNum, 99999.0); // in the end, only the last one will have 99999 +} + + + + + + + + + + + + + + + + + + + + +//------------------------------------------------------------------------------------------------ +// spear parser +//------------------------------------------------------------------------------------------------ +Spear_parser::Spear_parser() +{ + // some default values + hopSize = -1; + fileSampleRate = -1; +} + +Spear_parser::~Spear_parser() +{ +} + +void Spear_parser::calculateHopSize(char *filename) +{ + int index = 0; + bool prevWas_ = false; + bool found_h = false; + int n = 0; + + hopSize = 0; + + do + { + // check if '_' + if(filename[index] == '_') + prevWas_ = true; + else if( (filename[index] == 'h') && prevWas_) // if it is not, but it is 'h' and previous was '_', found "_h"! + { + found_h = true; + while(filename[index] != '\0') + { + index++; + if( (filename[index] == '.') || (filename[index] == '_')) + break; + else // i am not checking if char are digits...! + { + n = filename[index]; + hopSize = hopSize*10+(n-48); + } + } + } + else // else, nothing + prevWas_ = false; + index++; + } + while( (filename[index] != '\0') && !found_h ); + + if( !found_h || (hopSize<1) ) + hopSize = 551; // default val + +} + + +bool Spear_parser::parser(char *filename, int hopsize, int samplerate) +{ + string name = string(filename); + int len = name.length(); + // invoke correct parser according to the type of file...just checking the extension, crude but functional + if( (name[len-4]=='.') && (name[len-3]=='d') && (name[len-2]=='b') && (name[len-1]=='x') ) + return DBXparser(filename, samplerate); // .dbox + else + return TXTparser(filename, hopSize, samplerate); // .txt, or whatever +} + + +bool Spear_parser::DBXparser(char *filename, int samplerate) +{ + fileSampleRate = samplerate; + + // working vars + int parNum = 0; // total num of partials + int hopNum = 0; // total num of hops + + //---------------------------------------------------------------------------------------- + // open a file + ifstream fin; + fin.open(filename, ios::in | ios::binary); + if (!fin.good()) + { + cout << "Parser Error: file not found" << endl; // exit if file not found + return false; + } + + gettimeofday(&start, NULL); + //---------------------------------------------------------------------------------------- + // general data + + // look for partial count + fin.read((char *) &parNum, sizeof(int)); + partials.parNum = parNum; + + // look for hop count + fin.read((char *) &hopNum, sizeof(int)); + partials.setHopNum(hopNum); + + // look for hop size + fin.read((char *) &hopSize, sizeof(int)); + partials.hopSize = hopSize; // it's handy for both classes to know it + + // init partials data structure + partials.init(parNum, hopSize, true); + + // look for max active par num + fin.read((char *) &(partials.maxActiveParNum), sizeof(int)); + + + + // partial data + + // start frame of each partial + fin.read((char *) partials.partialStartFrame, sizeof(int)*parNum); + + // num of frames of each partial + fin.read((char *) partials.partialNumFrames, sizeof(int)*parNum); + + // frequency mean of each partial + fin.read((char *) partials.partialFreqMean, sizeof(int)*parNum); + + for(int par=0; par<parNum; par++) + { + int frameNum = partials.partialNumFrames[par]; + partials.update(par, frameNum); + fin.read((char *)partials.partialAmplitudes[par], sizeof(float)*frameNum); // amplitude of each partial in each frame + fin.read((char *)partials.partialFrequencies[par], sizeof(float)*frameNum); // frequency of each partial in each frame + fin.read((char *)partials.partialAmpDelta[par], sizeof(float)*frameNum); // amplitude delta of each partial in each frame + fin.read((char *)partials.partialFreqDelta[par], sizeof(float)*frameNum); // frequency delta of each partial in each frame + } + + + + + // frame data + + // number of active partial per each frame + fin.read((char *) partials.activePartialNum, sizeof(short)*(hopNum+1)); + // init array + for(int frame=0; frame<hopNum+1; frame++) + { + partials.activePartials[frame] = new unsigned int[partials.activePartialNum[frame]]; + fin.read((char *)partials.activePartials[frame], sizeof(int)*partials.activePartialNum[frame]); // active partials per each frame + } + + + + + + gettimeofday(&stop, NULL); + parserT = ( (stop.tv_sec*1000000+stop.tv_usec) - (start.tv_sec*1000000+start.tv_usec) ); + + + printf("\n-----------------------\n"); + printf("\nFile: %s\n", filename); + printf("\n-----------------------\n"); + printf("Profiler\n"); + printf("-----------------------\n"); + printf("File parser:\t\t\t%lu usec\n", parserT); + printf("\n\nTotal:\t\t%lu usec\n", parserT); + printf("-----------------------\n"); + + fin.close(); + + return true; +} + + + + +bool Spear_parser::TXTparser(char *filename, int hopsize, int samplerate) +{ + hopSize = hopsize; + fileSampleRate = samplerate; + if(hopsize<0) + { + gettimeofday(&start, NULL); + calculateHopSize(filename); + gettimeofday(&stop, NULL); + hopSizeT = ( (stop.tv_sec*1000000+stop.tv_usec) - (start.tv_sec*1000000+start.tv_usec) ); + } + else + hopSizeT = 0; + + calculateDeltaTime(); + + // working vars + char * token; // where to save single figures from file + string s = ""; // where to save lines from file + int parNum = 0; // total num of partials + int parIndex = -1; // index of current partial + int frameNum = 0; // total num of frames + int frameIndex = -1; // index of current frame + int startSample = -1; // sample value for first frame of partials + int endSample = -1; // sample value for last frame of partials + int maxSample = 0; // to calculate total number of hops in file + int missSampCnt = 0; // number of mising samples + double freq = 0; // to calculate frequency delta + double prevFreq = 0; // to calculate frequency delta + double amp = 0; // to calculate amplitude delta + double prevAmp = 0; // to calculate amplitude delta + + + //---------------------------------------------------------------------------------------- + // open a file + ifstream fin; + fin.open(filename); + if (!fin.good()) + { + cout << "Parser Error: file not found" << endl; // exit if file not found + return false; + } + + gettimeofday(&start, NULL); + //---------------------------------------------------------------------------------------- + // init partials data structure + getline(fin, s); + getline(fin, s); + getline(fin, s); // third line is the first we are interested into + + // look for partial count + token = strtok((char *)s.c_str(), " "); + // check if first token is there + if(token) + { + token = strtok(0, " "); + // check if second token is there + if(token) + parNum = atoi(token); + #ifdef DO_CHECKS + else + { + cout << "Parser Error: partial count not found, bad file format" << endl; // exit if value not found + return false; + } + #endif + } + #ifdef DO_CHECKS + else + { + cout << "Parser Error: partial count not found, bad file format" << endl; // exit if value not found + return false; + } + #endif + // from now on we take for granted that format is correct + + // init partials data structure + partials.init(parNum, hopSize); + + //---------------------------------------------------------------------------------------- + // fill in partials data structure + getline(fin, s); // get rid of intro line "partials-data" + getline(fin, s); // first important line + + while (!fin.eof()) + { + //------------------------------------- + // partial specific info + token = strtok((char *)s.c_str(), " "); + parIndex = atoi(token); // partial index + + token = strtok(0, " "); // num of frames, not used, cos we will do linear interpolation for missing frames +// frameNum = atoi(token); +// partials.partialNumFrames[parIndex] = frameNum; + + token = strtok(0, " "); // time of first frame, still char * + startSample = fromTimeToSamples(atof(token)); // convert time to samples + partials.partialStartSample[parIndex] = startSample; + + token = strtok(0, " "); // time of last frame, still char * + endSample = fromTimeToSamples(atof(token)); // convert time to samples + partials.partialEndSample[parIndex] = endSample; + + frameNum = ((endSample-startSample)/hopSize) + 1; // num of frames, including missing consecutive ones [+1 one cos we count frames, not hops] + partials.partialNumFrames[parIndex] = frameNum; + + + // check if this is the highest sample value so far + if(endSample > 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<missCnt; i++) + { + // calculate values for current missing frame + freq += freqStep; + amp += ampStep; + // save values + partials.partialFrequencies[parIndex][frame] = freq; + partials.partialAmplitudes[parIndex][frame] = amp; + partials.partialFreqMean[parIndex] += freq; // for frequency mean + // set deltas of previous frame [real or missing] + partials.setFreqDelta(parIndex, frame-1, deltaFreq); + partials.setAmpDelta(parIndex, frame-1, deltaAmp); + // move to next frame [missing or real] + sample += hopSize; + frame++; + } + + // update global values + *frameIndex = frame; + *prevFreq = freq; + *prevAmp = amp; + + return sample; // return the frame sample of the next real frame +} + + + +// for each frame, statically calculate: +// - which partial is active [and the total num of active partials] +// - at which local frame each partial is +void Spear_parser::staticCalculations() +{ + partials.maxActiveParNum = 0; // init to find maximum + + unsigned short *indices = new unsigned short[partials.parNum]; // temp array to store up to the maximum num of active partial indices + unsigned int activeCnt = 0; // counts the num of active partials in each frame + + unsigned int frameSample = 0; // current frame in samples + + char *partialStarted = new char [partials.parNum]; // index of the last local frame found per each partial + fill(partialStarted, partialStarted+partials.parNum, 0); + + for(unsigned int i=0; i<partials.hopNum+1; i++) // for each frame [not hops, this explains the +1] + { + //partials.localPartialFrames[i] = new int[partials.parNum]; // init all local frames to -1 + //fill(partials.localPartialFrames[i], partials.localPartialFrames[i]+partials.parNum, -1); + + frameSample = i*hopSize; // current frame, expressed in samples + activeCnt = 0; // reset a each frame + + for(unsigned int j=0; j<partials.parNum; j++) // for each partial + { + // check if inside active time region [expressed in samples] + if( (frameSample>=partials.partialStartSample[j]) && (frameSample<partials.partialEndSample[j]) ) // frame sample not equal to end sample, this filters out last frames and partials with one frame only + { + // activity + indices[activeCnt] = j; // save active index + activeCnt++; // increase counter + + // partial local frames + if(partialStarted[j]==0) // this partial has just started, so current local frame is first frame + { + partialStarted[j] = 1; + partials.partialStartFrame[j] = i; // here is the number of the first frame + } + } + } + + // activity + partials.activePartialNum[i] = activeCnt; // save number of active partials for this frame + partials.activePartials[i] = new unsigned int[activeCnt]; // set correct size to save all indices + + // look for maximum number of active partials at the same time + if(activeCnt > partials.maxActiveParNum) + partials.maxActiveParNum = activeCnt; + + // copy indices + for(unsigned int k=0; k<activeCnt; k++) + partials.activePartials[i][k] = indices[k]; + } + + delete[] indices; + delete[] partialStarted; + + delete[] partials.partialStartSample; + delete[] partials.partialEndSample; +} + + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/projects/d-box/spear_parser.h Fri Oct 31 19:10:17 2014 +0100 @@ -0,0 +1,188 @@ +/* + * spear_parser.h v1.2 + * + * Created on: May 6, 2014 + * Author: Victor Zappi + */ + +#ifndef SPEAR_PARSER_H_ +#define SPEAR_PARSER_H_ + +#include <iostream> +#include <fstream> +#include <cstring> +#include <string> +#include <stdlib.h> // atoi, atof +#include <math.h> +#include <algorithm> // std::fill + +#include <sys/time.h> + +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_ */
--- /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
--- /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 <iostream> +#include <cstdlib> +#include <libgen.h> +#include <signal.h> +#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; +}
--- /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 <rtdk.h> +#include <cstdlib> +#include <cmath> +#include <cstring> +#include <time.h> + +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); +}
--- /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
--- /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>; + }; + }; + +};