├── settings.gradle ├── app ├── src │ └── main │ │ ├── assets │ │ └── sync │ │ │ ├── models │ │ │ ├── dict │ │ │ │ ├── cmu07a.dic │ │ │ │ └── cmu07a.dic.md5 │ │ │ └── hmm │ │ │ │ └── en-us-semi │ │ │ │ ├── mdef.md5 │ │ │ │ ├── means.md5 │ │ │ │ ├── README.md5 │ │ │ │ ├── noisedict.md5 │ │ │ │ ├── sendump.md5 │ │ │ │ ├── variances.md5 │ │ │ │ ├── feat.params.md5 │ │ │ │ ├── transition_matrices.md5 │ │ │ │ ├── mdef │ │ │ │ ├── means │ │ │ │ ├── sendump │ │ │ │ ├── variances │ │ │ │ ├── transition_matrices │ │ │ │ ├── noisedict │ │ │ │ ├── feat.params │ │ │ │ └── README │ │ │ └── assets.lst │ │ ├── ic_launcher-web.png │ │ ├── res │ │ ├── drawable-hdpi │ │ │ └── ic_launcher.png │ │ ├── drawable-mdpi │ │ │ └── ic_launcher.png │ │ ├── drawable-xhdpi │ │ │ └── ic_launcher.png │ │ ├── drawable-xxhdpi │ │ │ └── ic_launcher.png │ │ ├── values │ │ │ ├── keys.xml │ │ │ ├── strings.xml │ │ │ └── arrays.xml │ │ ├── xml │ │ │ └── accessory_filter.xml │ │ └── layout │ │ │ └── activity_benson.xml │ │ ├── jniLibs │ │ └── armeabi-v7a │ │ │ └── libpocketsphinx_jni.so │ │ ├── java │ │ └── com │ │ │ └── github │ │ │ └── johnpersano │ │ │ └── benson │ │ │ ├── visualizer │ │ │ ├── AudioData.java │ │ │ ├── FFTData.java │ │ │ ├── Renderer.java │ │ │ ├── CircleRenderer.java │ │ │ └── VisualizerView.java │ │ │ ├── views │ │ │ ├── visualizer │ │ │ │ ├── AudioData.java │ │ │ │ ├── FFTData.java │ │ │ │ ├── Renderer.java │ │ │ │ ├── CircleRenderer.java │ │ │ │ └── VisualizerView.java │ │ │ └── AnimatedTextView.java │ │ │ ├── lexicon │ │ │ ├── Lexicon.java │ │ │ ├── modules │ │ │ │ ├── Time.java │ │ │ │ ├── Joke.java │ │ │ │ ├── Component.java │ │ │ │ ├── Hello.java │ │ │ │ └── HowAreYou.java │ │ │ ├── Query.java │ │ │ └── Response.java │ │ │ ├── recognition │ │ │ ├── CMUSphinxRecognition.java │ │ │ ├── CMUSphinxRecognizer.java │ │ │ └── AndroidRecognition.java │ │ │ └── ActivityBenson.java │ │ └── AndroidManifest.xml ├── libs │ ├── WolframAlpha-1.1.jar │ └── pocketsphinx-android-0.8-nolib.jar ├── proguard-rules.pro └── build.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── .gitignore ├── gradle.properties ├── sketches └── simple_sketch.ino ├── gradlew.bat ├── README.md └── gradlew /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | -------------------------------------------------------------------------------- /app/src/main/assets/sync/models/dict/cmu07a.dic: -------------------------------------------------------------------------------- 1 | benson B EH N S AH N 2 | -------------------------------------------------------------------------------- /app/src/main/assets/sync/models/dict/cmu07a.dic.md5: -------------------------------------------------------------------------------- 1 | 2faf27349ac4ecaca76db9475f1000ad 2 | -------------------------------------------------------------------------------- /app/src/main/assets/sync/models/hmm/en-us-semi/mdef.md5: -------------------------------------------------------------------------------- 1 | 2c0e0252044678a9e615cfefa93eaebf 2 | -------------------------------------------------------------------------------- /app/src/main/assets/sync/models/hmm/en-us-semi/means.md5: -------------------------------------------------------------------------------- 1 | 141637aa9305ec19ddfc623417ec2ed6 2 | -------------------------------------------------------------------------------- /app/src/main/assets/sync/models/hmm/en-us-semi/README.md5: -------------------------------------------------------------------------------- 1 | 0c97c582d21a688968144e7a6f654353 2 | -------------------------------------------------------------------------------- /app/src/main/assets/sync/models/hmm/en-us-semi/noisedict.md5: -------------------------------------------------------------------------------- 1 | 8dc6eba0389969bc34897f2437a6ea75 2 | -------------------------------------------------------------------------------- /app/src/main/assets/sync/models/hmm/en-us-semi/sendump.md5: -------------------------------------------------------------------------------- 1 | 0aa7ad8d4612854dbb221d045baf6031 2 | -------------------------------------------------------------------------------- /app/src/main/assets/sync/models/hmm/en-us-semi/variances.md5: -------------------------------------------------------------------------------- 1 | ba5dfea322d9ab26843f21813c75f604 2 | -------------------------------------------------------------------------------- /app/src/main/assets/sync/models/hmm/en-us-semi/feat.params.md5: -------------------------------------------------------------------------------- 1 | a7039af26067c37e34f0d72348f10708 2 | -------------------------------------------------------------------------------- /app/libs/WolframAlpha-1.1.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jmp-12/Benson/HEAD/app/libs/WolframAlpha-1.1.jar -------------------------------------------------------------------------------- /app/src/main/assets/sync/models/hmm/en-us-semi/transition_matrices.md5: -------------------------------------------------------------------------------- 1 | f0cb126cea30ad0f024601a65a18241f 2 | -------------------------------------------------------------------------------- /app/src/main/ic_launcher-web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jmp-12/Benson/HEAD/app/src/main/ic_launcher-web.png -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jmp-12/Benson/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /app/libs/pocketsphinx-android-0.8-nolib.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jmp-12/Benson/HEAD/app/libs/pocketsphinx-android-0.8-nolib.jar -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jmp-12/Benson/HEAD/app/src/main/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jmp-12/Benson/HEAD/app/src/main/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jmp-12/Benson/HEAD/app/src/main/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jmp-12/Benson/HEAD/app/src/main/res/drawable-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/assets/sync/models/hmm/en-us-semi/mdef: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jmp-12/Benson/HEAD/app/src/main/assets/sync/models/hmm/en-us-semi/mdef -------------------------------------------------------------------------------- /app/src/main/assets/sync/models/hmm/en-us-semi/means: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jmp-12/Benson/HEAD/app/src/main/assets/sync/models/hmm/en-us-semi/means -------------------------------------------------------------------------------- /app/src/main/assets/sync/models/hmm/en-us-semi/sendump: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jmp-12/Benson/HEAD/app/src/main/assets/sync/models/hmm/en-us-semi/sendump -------------------------------------------------------------------------------- /app/src/main/assets/sync/models/hmm/en-us-semi/variances: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jmp-12/Benson/HEAD/app/src/main/assets/sync/models/hmm/en-us-semi/variances -------------------------------------------------------------------------------- /app/src/main/jniLibs/armeabi-v7a/libpocketsphinx_jni.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jmp-12/Benson/HEAD/app/src/main/jniLibs/armeabi-v7a/libpocketsphinx_jni.so -------------------------------------------------------------------------------- /app/src/main/res/values/keys.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | YOUR_APP_KEY 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/assets/sync/models/hmm/en-us-semi/transition_matrices: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jmp-12/Benson/HEAD/app/src/main/assets/sync/models/hmm/en-us-semi/transition_matrices -------------------------------------------------------------------------------- /app/src/main/res/xml/accessory_filter.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/assets/sync/models/hmm/en-us-semi/noisedict: -------------------------------------------------------------------------------- 1 | SIL 2 | SIL 3 | SIL 4 | [BREATH] +BREATH+ 5 | [COUGH] +COUGH+ 6 | [NOISE] +NOISE+ 7 | [SMACK] +SMACK+ 8 | [UH] +UH+ 9 | [UM] +UM+ 10 | -------------------------------------------------------------------------------- /app/src/main/assets/sync/models/hmm/en-us-semi/feat.params: -------------------------------------------------------------------------------- 1 | -nfilt 25 2 | -lowerf 130 3 | -upperf 6800 4 | -feat 1s_c_d_dd 5 | -svspec 0-12/13-25/26-38 6 | -agc none 7 | -cmn current 8 | -varnorm no 9 | -transform dct 10 | -lifter 22 11 | -cmninit 40 12 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Dec 29 16:49:11 EST 2014 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip 7 | -------------------------------------------------------------------------------- /app/src/main/assets/sync/assets.lst: -------------------------------------------------------------------------------- 1 | models/dict/cmu07a.dic 2 | models/hmm/en-us-semi/README 3 | models/hmm/en-us-semi/feat.params 4 | models/hmm/en-us-semi/mdef 5 | models/hmm/en-us-semi/means 6 | models/hmm/en-us-semi/noisedict 7 | models/hmm/en-us-semi/sendump 8 | models/hmm/en-us-semi/transition_matrices 9 | models/hmm/en-us-semi/variances -------------------------------------------------------------------------------- /app/src/main/assets/sync/models/hmm/en-us-semi/README: -------------------------------------------------------------------------------- 1 | # Copyright 2013 Carnegie Mellon University. 2 | # All Rights Reserved. Use is subject to license terms. 3 | # 4 | # See the file "license.terms" for information on usage and 5 | # redistribution of this file, and for a DISCLAIMER OF ALL 6 | # WARRANTIES. 7 | 8 | This directory contains generic US english acoustic model 9 | trained with latest sphinxtrain. 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Benson 5 | 6 | Initializing Benson… 7 | 8 | Recognition error. 9 | 10 | I don\'t quite understand your response. 11 | 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | #Android generated 2 | bin 3 | gen 4 | build 5 | 6 | #Eclipse 7 | .project 8 | .classpath 9 | .settings 10 | .checkstyle 11 | 12 | #IntelliJ IDEA 13 | .idea 14 | *.iml 15 | *.ipr 16 | *.iws 17 | gen-external-apklibs 18 | 19 | #Maven 20 | target 21 | release.properties 22 | pom.xml.* 23 | 24 | #Ant 25 | build.xml 26 | ant.properties 27 | local.properties 28 | proguard.cfg 29 | proguard-project.txt 30 | 31 | #Other 32 | .DS_Store 33 | 34 | #Keys 35 | app/src/main/res/values/keys.xml -------------------------------------------------------------------------------- /app/src/main/java/com/github/johnpersano/benson/visualizer/AudioData.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011, Felix Palmer 3 | * 4 | * Licensed under the MIT license: 5 | * http://creativecommons.org/licenses/MIT/ 6 | */ 7 | 8 | package com.github.johnpersano.benson.visualizer; 9 | 10 | /* Data class to explicitly indicate that these bytes are raw audio data */ 11 | public class AudioData { 12 | 13 | public AudioData(byte[] bytes) { 14 | 15 | this.bytes = bytes; 16 | 17 | } 18 | 19 | public byte[] bytes; 20 | 21 | } 22 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/johnpersano/benson/visualizer/FFTData.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011, Felix Palmer 3 | * 4 | * Licensed under the MIT license: 5 | * http://creativecommons.org/licenses/MIT/ 6 | */ 7 | 8 | package com.github.johnpersano.benson.visualizer; 9 | 10 | /* Data class to explicitly indicate that these bytes are the FFT of audio data */ 11 | public class FFTData { 12 | 13 | public FFTData(byte[] bytes) { 14 | 15 | this.bytes = bytes; 16 | 17 | } 18 | 19 | public byte[] bytes; 20 | 21 | } 22 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/johnpersano/benson/views/visualizer/AudioData.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011, Felix Palmer 3 | * 4 | * Licensed under the MIT license: 5 | * http://creativecommons.org/licenses/MIT/ 6 | */ 7 | 8 | package com.github.johnpersano.benson.views.visualizer; 9 | 10 | /* Data class to explicitly indicate that these bytes are raw audio data */ 11 | public class AudioData { 12 | 13 | public AudioData(byte[] bytes) { 14 | 15 | this.bytes = bytes; 16 | 17 | } 18 | 19 | public byte[] bytes; 20 | 21 | } 22 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/johnpersano/benson/views/visualizer/FFTData.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011, Felix Palmer 3 | * 4 | * Licensed under the MIT license: 5 | * http://creativecommons.org/licenses/MIT/ 6 | */ 7 | 8 | package com.github.johnpersano.benson.views.visualizer; 9 | 10 | /* Data class to explicitly indicate that these bytes are the FFT of audio data */ 11 | public class FFTData { 12 | 13 | public FFTData(byte[] bytes) { 14 | 15 | this.bytes = bytes; 16 | 17 | } 18 | 19 | public byte[] bytes; 20 | 21 | } 22 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /home/john/Development/Android-Studio/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | 5 | compileSdkVersion 21 6 | buildToolsVersion '21.1.2' 7 | 8 | defaultConfig { 9 | 10 | applicationId "com.github.johnpersano.benson" 11 | minSdkVersion 16 12 | targetSdkVersion 21 13 | versionCode 1 14 | versionName "1.0" 15 | 16 | } 17 | 18 | buildTypes { 19 | 20 | release { 21 | 22 | minifyEnabled false 23 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 24 | 25 | } 26 | 27 | } 28 | 29 | } 30 | 31 | dependencies { 32 | 33 | compile fileTree(dir: 'libs', include: ['*.jar']) 34 | compile 'me.palazzetti:adktoolkit:0.3.0' 35 | 36 | } 37 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Settings specified in this file will override any Gradle settings 5 | # configured through the IDE. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_benson.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 12 | 13 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/johnpersano/benson/lexicon/Lexicon.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2014 John Persano 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | 19 | package com.github.johnpersano.benson.lexicon; 20 | 21 | import com.github.johnpersano.benson.lexicon.modules.Component; 22 | import com.github.johnpersano.benson.lexicon.modules.Time; 23 | import com.github.johnpersano.benson.lexicon.modules.Hello; 24 | import com.github.johnpersano.benson.lexicon.modules.HowAreYou; 25 | import com.github.johnpersano.benson.lexicon.modules.Joke; 26 | 27 | import java.util.Arrays; 28 | import java.util.List; 29 | 30 | /* This class holds the default lexicon for Benson. If any new modules are added, be sure to add them here as well. */ 31 | public class Lexicon { 32 | 33 | public final List lexicon = Arrays.asList(new Component(), new Hello(), new HowAreYou(), new Joke(), new Time()); 34 | 35 | } 36 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/johnpersano/benson/lexicon/modules/Time.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2014 John Persano 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | 19 | package com.github.johnpersano.benson.lexicon.modules; 20 | 21 | import android.content.Context; 22 | 23 | import com.github.johnpersano.benson.R; 24 | import com.github.johnpersano.benson.lexicon.Query; 25 | import com.github.johnpersano.benson.lexicon.Response; 26 | 27 | import java.util.Arrays; 28 | import java.util.List; 29 | 30 | import me.palazzetti.adktoolkit.AdkManager; 31 | 32 | 33 | public class Time extends Query { 34 | 35 | @Override 36 | public List getInputs() { 37 | 38 | return Arrays.asList("time"); 39 | 40 | } 41 | 42 | @Override 43 | public Response getResponse(Context context, String hypothesis, AdkManager adkManager) { 44 | 45 | return new Response() 46 | .setReply(Response.getRandomTimeReply(context.getResources().getStringArray(R.array.time_default))); 47 | 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/johnpersano/benson/lexicon/modules/Joke.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2014 John Persano 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | 19 | package com.github.johnpersano.benson.lexicon.modules; 20 | 21 | import android.content.Context; 22 | 23 | import com.github.johnpersano.benson.R; 24 | import com.github.johnpersano.benson.lexicon.Query; 25 | import com.github.johnpersano.benson.lexicon.Response; 26 | 27 | import java.util.Arrays; 28 | import java.util.List; 29 | 30 | import me.palazzetti.adktoolkit.AdkManager; 31 | 32 | 33 | public class Joke extends Query { 34 | 35 | @Override 36 | public List getInputs() { 37 | 38 | return Arrays.asList("a joke", "any jokes"); 39 | 40 | } 41 | 42 | @Override 43 | public Response getResponse(Context context, String hypothesis, AdkManager adkManager) { 44 | 45 | return new Response() 46 | .setReply(Response.getRandomReply(context.getResources().getStringArray(R.array.joke_default))); 47 | 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 18 | 19 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/johnpersano/benson/lexicon/Query.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2014 John Persano 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | 19 | package com.github.johnpersano.benson.lexicon; 20 | 21 | import android.content.Context; 22 | 23 | import java.util.List; 24 | 25 | import me.palazzetti.adktoolkit.AdkManager; 26 | 27 | /* All module classes should extend this class and override its methods. */ 28 | public class Query { 29 | 30 | /** 31 | * Get input String List for the module. 32 | * 33 | * @return {@link java.util.List} 34 | */ 35 | public List getInputs() { 36 | 37 | return null; 38 | 39 | } 40 | 41 | /** 42 | * Get response from module. 43 | * 44 | * @param context The current Context. 45 | * @param hypothesis The full string containing the user's speech. 46 | * @param adkManager The ADK Manager which communicates with the Arduino DUE. 47 | * @return {@link com.github.johnpersano.benson.lexicon.Response} 48 | */ 49 | public Response getResponse(Context context, String hypothesis, AdkManager adkManager) { 50 | 51 | return null; 52 | 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /sketches/simple_sketch.ino: -------------------------------------------------------------------------------- 1 | #include "variant.h" 2 | #include 3 | #include 4 | 5 | #define LED_PIN 13 6 | 7 | // Accessory descriptor. It's how Arduino identifies itself to Android. 8 | char descriptionName[] = "ArduinoADK_2"; 9 | char modelName[] = "UDOO_ADK"; // your Arduino Accessory name (Need to be the same defined in the Android App) 10 | char manufacturerName[] = "Aidilab"; // manufacturer (Need to be the same defined in the Android App) 11 | 12 | // Make up anything you want for these 13 | char versionNumber[] = "1.0"; // version (Need to be the same defined in the Android App) 14 | char serialNumber[] = "1"; 15 | char url[] = "http://www.udoo.org"; // If there isn't any compatible app installed, Android suggest to visit this url 16 | 17 | USBHost Usb; 18 | ADK adk(&Usb, manufacturerName, modelName, descriptionName, versionNumber, url, serialNumber); 19 | 20 | #define RCVSIZE 128 21 | uint8_t buf[RCVSIZE]; 22 | uint32_t bytesRead = 0; 23 | 24 | void setup() 25 | { 26 | Serial.begin(115200); 27 | pinMode(LED_PIN, OUTPUT); 28 | delay(500); 29 | Serial.println("UDOO ADK demo start..."); 30 | } 31 | 32 | void loop() 33 | { 34 | Usb.Task(); 35 | 36 | if (adk.isReady()) { 37 | adk.read(&bytesRead, RCVSIZE, buf);// read data into buf variable 38 | if (bytesRead > 0) { 39 | if (parseCommand(buf[0]) == 1) {// compare received data 40 | // Received "1" - turn on LED 41 | digitalWrite(LED_PIN, HIGH); 42 | } else if (parseCommand(buf[0]) == 0) { 43 | // Received "0" - turn off LED 44 | digitalWrite(LED_PIN, LOW); 45 | } 46 | } 47 | } else { 48 | digitalWrite(LED_PIN , LOW); // turn off light 49 | } 50 | 51 | delay(10); 52 | } 53 | 54 | // the characters sent to Arduino are interpreted as ASCII, we decrease 48 to return to ASCII range. 55 | uint8_t parseCommand(uint8_t received) { 56 | return received - 48; 57 | } 58 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/johnpersano/benson/recognition/CMUSphinxRecognition.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2014 John Persano 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | 19 | package com.github.johnpersano.benson.recognition; 20 | 21 | import edu.cmu.pocketsphinx.Hypothesis; 22 | import edu.cmu.pocketsphinx.RecognitionListener; 23 | 24 | 25 | public class CMUSphinxRecognition implements RecognitionListener { 26 | 27 | @SuppressWarnings("unused") 28 | private static final String TAG = "CMUSphinxRecognition"; 29 | 30 | /* Custom listener for speech recognition. */ 31 | public interface OnResultListener { 32 | 33 | public void onSpeechResult(String hypothesis); 34 | 35 | } 36 | 37 | private OnResultListener mOnResultListener; 38 | 39 | public CMUSphinxRecognition(OnResultListener onResultListener) { 40 | 41 | this.mOnResultListener = onResultListener; 42 | 43 | } 44 | 45 | @Override 46 | public void onBeginningOfSpeech() { 47 | 48 | /* Do nothing */ 49 | 50 | } 51 | 52 | @Override 53 | public void onEndOfSpeech() { 54 | 55 | /* Do nothing */ 56 | 57 | } 58 | 59 | @Override 60 | public void onPartialResult(Hypothesis hypothesis) { 61 | 62 | mOnResultListener.onSpeechResult(hypothesis.getHypstr()); 63 | 64 | } 65 | 66 | @Override 67 | public void onResult(Hypothesis hypothesis) { 68 | 69 | mOnResultListener.onSpeechResult(hypothesis.getHypstr()); 70 | 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/johnpersano/benson/recognition/CMUSphinxRecognizer.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2014 John Persano 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package com.github.johnpersano.benson.recognition; 19 | 20 | 21 | import android.content.Context; 22 | 23 | import java.io.File; 24 | import java.io.IOException; 25 | 26 | import edu.cmu.pocketsphinx.Assets; 27 | import edu.cmu.pocketsphinx.SpeechRecognizer; 28 | 29 | import static edu.cmu.pocketsphinx.SpeechRecognizerSetup.defaultSetup; 30 | 31 | public class CMUSphinxRecognizer { 32 | 33 | @SuppressWarnings("UnusedDeclaration") 34 | private static final String TAG = "CMUSphinxRecognizer"; 35 | 36 | private Context mContext; 37 | 38 | public CMUSphinxRecognizer(Context context) { 39 | 40 | this.mContext = context; 41 | 42 | } 43 | 44 | /* Create speech recognizer. This should be done in an AsyncTask */ 45 | public SpeechRecognizer getRecognizer() throws IOException { 46 | 47 | final Assets assets = new Assets(mContext); 48 | 49 | final File assetDirectory = assets.syncAssets(); 50 | final File modelsDirectory = new File(assetDirectory, "models"); 51 | 52 | return defaultSetup() 53 | .setAcousticModel(new File(modelsDirectory, "hmm/en-us-semi")) 54 | .setDictionary(new File(modelsDirectory, "dict/cmu07a.dic")) 55 | .setRawLogDir(assetDirectory).setKeywordThreshold(1e-20f) 56 | .getRecognizer(); 57 | 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/johnpersano/benson/visualizer/Renderer.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011, Felix Palmer 3 | * 4 | * Licensed under the MIT license: 5 | * http://creativecommons.org/licenses/MIT/ 6 | */ 7 | 8 | package com.github.johnpersano.benson.visualizer; 9 | 10 | import android.graphics.Canvas; 11 | import android.graphics.Rect; 12 | 13 | 14 | abstract public class Renderer 15 | { 16 | // Have these as members, so we don't have to re-create them each time 17 | protected float[] mPoints; 18 | protected float[] mFFTPoints; 19 | public Renderer() 20 | { 21 | } 22 | 23 | // As the display of raw/FFT audio will usually look different, subclasses 24 | // will typically only implement one of the below methods 25 | /** 26 | * Implement this method to render the audio data onto the canvas 27 | * @param canvas - Canvas to draw on 28 | * @param data - Data to render 29 | * @param rect - Rect to render into 30 | */ 31 | abstract public void onRender(Canvas canvas, AudioData data, Rect rect); 32 | 33 | /** 34 | * Implement this method to render the FFT audio data onto the canvas 35 | * @param canvas - Canvas to draw on 36 | * @param data - Data to render 37 | * @param rect - Rect to render into 38 | */ 39 | abstract public void onRender(Canvas canvas, FFTData data, Rect rect); 40 | 41 | 42 | // These methods should actually be called for rendering 43 | /** 44 | * Render the audio data onto the canvas 45 | * @param canvas - Canvas to draw on 46 | * @param data - Data to render 47 | * @param rect - Rect to render into 48 | */ 49 | final public void render(Canvas canvas, AudioData data, Rect rect) 50 | { 51 | if (mPoints == null || mPoints.length < data.bytes.length * 4) { 52 | mPoints = new float[data.bytes.length * 4]; 53 | } 54 | 55 | onRender(canvas, data, rect); 56 | } 57 | 58 | /** 59 | * Render the FFT data onto the canvas 60 | * @param canvas - Canvas to draw on 61 | * @param data - Data to render 62 | * @param rect - Rect to render into 63 | */ 64 | final public void render(Canvas canvas, FFTData data, Rect rect) 65 | { 66 | if (mFFTPoints == null || mFFTPoints.length < data.bytes.length * 4) { 67 | mFFTPoints = new float[data.bytes.length * 4]; 68 | } 69 | 70 | onRender(canvas, data, rect); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/johnpersano/benson/views/visualizer/Renderer.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011, Felix Palmer 3 | * 4 | * Licensed under the MIT license: 5 | * http://creativecommons.org/licenses/MIT/ 6 | */ 7 | 8 | package com.github.johnpersano.benson.views.visualizer; 9 | 10 | import android.graphics.Canvas; 11 | import android.graphics.Rect; 12 | 13 | 14 | abstract public class Renderer { 15 | 16 | protected float[] mPoints; 17 | protected float[] mFFTPoints; 18 | 19 | public Renderer() { 20 | 21 | /* Do nothing. */ 22 | 23 | } 24 | 25 | // As the display of raw/FFT audio will usually look different, subclasses 26 | // will typically only implement one of the below methods 27 | 28 | /** 29 | * Implement this method to render the audio data onto the canvas 30 | * 31 | * @param canvas - Canvas to draw on 32 | * @param data - Data to render 33 | * @param rect - Rect to render into 34 | */ 35 | abstract public void onRender(Canvas canvas, AudioData data, Rect rect); 36 | 37 | /** 38 | * Implement this method to render the FFT audio data onto the canvas 39 | * 40 | * @param canvas - Canvas to draw on 41 | * @param data - Data to render 42 | * @param rect - Rect to render into 43 | */ 44 | abstract public void onRender(Canvas canvas, FFTData data, Rect rect); 45 | 46 | /** 47 | * Render the audio data onto the canvas 48 | * 49 | * @param canvas - Canvas to draw on 50 | * @param data - Data to render 51 | * @param rect - Rect to render into 52 | */ 53 | final public void render(Canvas canvas, AudioData data, Rect rect) { 54 | 55 | if (mPoints == null || mPoints.length < data.bytes.length * 4) { 56 | 57 | mPoints = new float[data.bytes.length * 4]; 58 | 59 | } 60 | 61 | onRender(canvas, data, rect); 62 | } 63 | 64 | /** 65 | * Render the FFT data onto the canvas 66 | * 67 | * @param canvas - Canvas to draw on 68 | * @param data - Data to render 69 | * @param rect - Rect to render into 70 | */ 71 | final public void render(Canvas canvas, FFTData data, Rect rect) { 72 | 73 | if (mFFTPoints == null || mFFTPoints.length < data.bytes.length * 4) { 74 | 75 | mFFTPoints = new float[data.bytes.length * 4]; 76 | 77 | } 78 | 79 | onRender(canvas, data, rect); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/johnpersano/benson/views/AnimatedTextView.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2014 John Persano 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package com.github.johnpersano.benson.views; 19 | 20 | import android.content.Context; 21 | import android.os.Handler; 22 | import android.util.AttributeSet; 23 | import android.widget.TextView; 24 | 25 | /** 26 | * Class was modified from Stack Overflow post: 27 | * http://stackoverflow.com/questions/16895520/moving-textview-pixel-by-pixel-every-one-second-in-android 28 | */ 29 | public class AnimatedTextView extends TextView { 30 | 31 | private CharSequence mText; 32 | private int mIndex; 33 | private long mDelay = 35; 34 | 35 | @SuppressWarnings("UnusedDeclaration") 36 | public AnimatedTextView(Context context) { 37 | super(context); 38 | 39 | /* Empty constructor. */ 40 | 41 | } 42 | 43 | @SuppressWarnings("UnusedDeclaration") 44 | public AnimatedTextView(Context context, AttributeSet attrs) { 45 | super(context, attrs); 46 | 47 | /* Empty constructor. */ 48 | 49 | } 50 | 51 | private Handler mHandler = new Handler(); 52 | 53 | private Runnable characterAdder = new Runnable() { 54 | 55 | @Override 56 | public void run() { 57 | 58 | setText(mText.subSequence(0, mIndex++)); 59 | 60 | if(mIndex <= mText.length()) { 61 | 62 | mHandler.postDelayed(characterAdder, mDelay); 63 | 64 | } 65 | 66 | } 67 | 68 | }; 69 | 70 | /* This method will animate the text similar to a typewriter. */ 71 | public void animateText(CharSequence text) { 72 | 73 | mText = text; 74 | mIndex = 0; 75 | 76 | setText(""); 77 | mHandler.removeCallbacks(characterAdder); 78 | mHandler.postDelayed(characterAdder, mDelay); 79 | 80 | } 81 | 82 | } -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/johnpersano/benson/views/visualizer/CircleRenderer.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011, Felix Palmer 3 | * 4 | * Licensed under the MIT license: 5 | * http://creativecommons.org/licenses/MIT/ 6 | */ 7 | 8 | package com.github.johnpersano.benson.views.visualizer; 9 | 10 | import android.graphics.Canvas; 11 | import android.graphics.Color; 12 | import android.graphics.Paint; 13 | import android.graphics.Rect; 14 | 15 | 16 | public class CircleRenderer extends Renderer { 17 | 18 | private Paint mPaint; 19 | private float modulation = 0; 20 | 21 | /** 22 | * Renders the audio data onto a pulsing circle 23 | */ 24 | public CircleRenderer() { 25 | super(); 26 | 27 | this.mPaint = new Paint(); 28 | this.mPaint.setStrokeWidth(3f); 29 | this.mPaint.setAntiAlias(true); 30 | this.mPaint.setColor(Color.argb(255, 222, 92, 143)); 31 | 32 | } 33 | 34 | @Override 35 | public void onRender(Canvas canvas, AudioData data, Rect rect) { 36 | 37 | for (int i = 0; i < data.bytes.length - 1; i++) { 38 | 39 | float[] cartPoint = {(float) i / (data.bytes.length - 1), rect.height() / 2 + 40 | ((byte) (data.bytes[i] + 128)) * (rect.height() / 2) / 128}; 41 | 42 | float[] polarPoint = toPolar(cartPoint, rect); 43 | 44 | mPoints[i * 4] = polarPoint[0]; 45 | mPoints[i * 4 + 1] = polarPoint[1]; 46 | 47 | float[] cartPoint2 = {(float) (i + 1) / (data.bytes.length - 1), rect.height() / 2 + 48 | ((byte) (data.bytes[i + 1] + 128)) * (rect.height() / 2) / 128}; 49 | 50 | float[] polarPoint2 = toPolar(cartPoint2, rect); 51 | 52 | mPoints[i * 4 + 2] = polarPoint2[0]; 53 | mPoints[i * 4 + 3] = polarPoint2[1]; 54 | 55 | } 56 | 57 | canvas.drawLines(mPoints, mPaint); 58 | 59 | // Controls the pulsing rate 60 | modulation += 0.045; 61 | 62 | } 63 | 64 | @Override 65 | public void onRender(Canvas canvas, FFTData data, Rect rect) { 66 | 67 | /* Do nothing, we only display audio data */ 68 | 69 | } 70 | 71 | public void changeColor(int color) { 72 | 73 | mPaint.setColor(color); 74 | 75 | } 76 | 77 | private float[] toPolar(float[] cartesian, Rect rect) { 78 | 79 | double cX = rect.width() / 2; 80 | double cY = rect.height() / 2; 81 | double angle = (cartesian[0]) * 2 * Math.PI; 82 | float aggressive = 0.30f; 83 | 84 | double radius = ((rect.width() / 2) * (1 - aggressive) + aggressive * cartesian[1] / 2) * (1.2 + Math.sin(modulation)) / 2.2; 85 | radius += 25; 86 | return new float[]{ (float) (cX + radius * Math.sin(angle)), 87 | (float) (cY + radius * Math.cos(angle)) }; 88 | 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Benson 2 | ================= 3 | Benson is a voice recognition program designed for the [Udoo Quad](http://shop.udoo.org/usa/?___from_store=usa&popup=no). Benson can respond to user input with pre-programmed phrases and search the web via Wolfram API for a response to an unknown query. Pre-programmed responses can utilize native Android APIs as well as accept parameters for serial communication with the onboard DUE. The result is a sarcastic, personable voice recognition system with access to net-based knowledge and Arduino compatible electronics. 4 | 5 | ####Check out a quick demonstration on [YouTube](http://youtu.be/HjJCI1Hjb2c). 6 | 7 |

8 | Gif 9 |

10 | 11 | Using this application 12 | ========= 13 | **Please remember this project is an early alpha version, bugs will be plentiful.**
14 |
15 | In order to use this application on your Udoo Quad, you must do the following. 16 | 17 | 1. Upload the Arduino [sketch](https://github.com/JohnPersano/Benson/blob/master/arduino/sketches/simple_sketch.ino) to the onboard DUE. 18 | 2. Sign up for an app key from [Wolfram Alpha](https://developer.wolframalpha.com/portal/signin.html) and place it in the [strings resource](https://github.com/JohnPersano/Benson/blob/master/app/src/main/res/values/strings.xml) file. 19 | 3. Install GAPPS on your Udoo. See Squawk003's method [here](http://www.udoo.org/forum/install-google-apps-t327-20.html). 20 | 21 | Creating new modules 22 | ========= 23 | Creating new modules for Benson is easy! Check out the [module tutorial](https://github.com/JohnPersano/Benson/wiki/Creating-a-module-guide) for information about creating modules. 24 | 25 | User/developer submissions 26 | ========= 27 | All errors, ideas for enhancements, and pull requests are welcome as long as they concern the Udoo Quad. Since I only own the Udoo Quad, I cannot personally test any code on other Udoo boards. This is a **must** before any code is included in the Benson project. 28 | 29 | Libraries used 30 | ========= 31 | [CMUSphinx](http://cmusphinx.sourceforge.net/wiki/tutorialandroid)
32 | [Wolfram Alpha Java bindings](http://products.wolframalpha.com/api/libraries.html)
33 | [Android Visualizer](https://github.com/felixpalmer/android-visualizer)
34 | [ADK Toolkit](https://github.com/palazzem/adk-toolkit)
35 | 36 | Developer 37 | ========= 38 | [John Persano](https://plus.google.com/+JohnPersano) 39 | 40 | License 41 | ======= 42 | 43 | Copyright 2014 John Persano 44 | 45 | Licensed under the Apache License, Version 2.0 (the "License"); 46 | you may not use this file except in compliance with the License. 47 | You may obtain a copy of the License at 48 | 49 | http://www.apache.org/licenses/LICENSE-2.0 50 | 51 | Unless required by applicable law or agreed to in writing, software 52 | distributed under the License is distributed on an "AS IS" BASIS, 53 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 54 | See the License for the specific language governing permissions and 55 | limitations under the License. 56 | 57 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/johnpersano/benson/visualizer/CircleRenderer.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011, Felix Palmer 3 | * 4 | * Licensed under the MIT license: 5 | * http://creativecommons.org/licenses/MIT/ 6 | */ 7 | 8 | package com.github.johnpersano.benson.visualizer; 9 | 10 | import android.graphics.Canvas; 11 | import android.graphics.Color; 12 | import android.graphics.Paint; 13 | import android.graphics.Rect; 14 | 15 | 16 | public class CircleRenderer extends Renderer { 17 | 18 | private Paint mPaint; 19 | private boolean mCycleColor; 20 | private float modulation = 0; 21 | 22 | /** 23 | * Renders the audio data onto a pulsing circle 24 | * 25 | * @param cycleColor - If true the color will change on each frame 26 | */ 27 | public CircleRenderer(boolean cycleColor) { 28 | super(); 29 | 30 | final Paint paint = new Paint(); 31 | paint.setStrokeWidth(3f); 32 | paint.setAntiAlias(true); 33 | paint.setColor(Color.argb(255, 222, 92, 143)); 34 | 35 | this.mPaint = paint; 36 | this.mCycleColor = cycleColor; 37 | 38 | } 39 | 40 | @Override 41 | public void onRender(Canvas canvas, AudioData data, Rect rect) { 42 | 43 | if (mCycleColor) { 44 | 45 | cycleColor(); 46 | 47 | } 48 | 49 | for (int i = 0; i < data.bytes.length - 1; i++) { 50 | 51 | float[] cartPoint = {(float) i / (data.bytes.length - 1), rect.height() / 2 + 52 | ((byte) (data.bytes[i] + 128)) * (rect.height() / 2) / 128}; 53 | 54 | float[] polarPoint = toPolar(cartPoint, rect); 55 | 56 | mPoints[i * 4] = polarPoint[0]; 57 | mPoints[i * 4 + 1] = polarPoint[1]; 58 | 59 | float[] cartPoint2 = {(float) (i + 1) / (data.bytes.length - 1), rect.height() / 2 + 60 | ((byte) (data.bytes[i + 1] + 128)) * (rect.height() / 2) / 128}; 61 | 62 | float[] polarPoint2 = toPolar(cartPoint2, rect); 63 | 64 | mPoints[i * 4 + 2] = polarPoint2[0]; 65 | mPoints[i * 4 + 3] = polarPoint2[1]; 66 | 67 | } 68 | 69 | canvas.drawLines(mPoints, mPaint); 70 | 71 | // Controls the pulsing rate 72 | modulation += 0.04; 73 | 74 | } 75 | 76 | @Override 77 | public void onRender(Canvas canvas, FFTData data, Rect rect) { 78 | 79 | /* Do nothing, we only display audio data */ 80 | 81 | } 82 | 83 | public void changeColor(int color) { 84 | 85 | mPaint.setColor(color); 86 | 87 | } 88 | 89 | private float[] toPolar(float[] cartesian, Rect rect) { 90 | 91 | double cX = rect.width() / 2; 92 | double cY = rect.height() / 2; 93 | double angle = (cartesian[0]) * 2 * Math.PI; 94 | float aggressive = 0.33f; 95 | 96 | double radius = ((rect.width() / 2) * (1 - aggressive) + aggressive * cartesian[1] / 2) * (1.2 + Math.sin(modulation)) / 2.2; 97 | 98 | return new float[]{ (float) (cX + radius * Math.sin(angle)), 99 | (float) (cY + radius * Math.cos(angle)) }; 100 | 101 | } 102 | 103 | private float colorCounter = 0; 104 | 105 | private void cycleColor() { 106 | 107 | int r = (int) Math.floor(128 * (Math.sin(colorCounter) + 1)); 108 | int g = (int) Math.floor(128 * (Math.sin(colorCounter + 2) + 1)); 109 | int b = (int) Math.floor(128 * (Math.sin(colorCounter + 4) + 1)); 110 | 111 | mPaint.setColor(Color.argb(128, r, g, b)); 112 | 113 | colorCounter += 0.03; 114 | 115 | } 116 | 117 | } 118 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/johnpersano/benson/recognition/AndroidRecognition.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2014 John Persano 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package com.github.johnpersano.benson.recognition; 19 | 20 | import android.os.Bundle; 21 | import android.speech.RecognitionListener; 22 | import android.speech.SpeechRecognizer; 23 | 24 | import java.util.ArrayList; 25 | 26 | 27 | public class AndroidRecognition implements RecognitionListener { 28 | 29 | @SuppressWarnings("unused") 30 | private static final String TAG = "AndroidRecognition"; 31 | 32 | private static final String RESPONSE_TIMEOUT = "How rude."; 33 | private static final String RESPONSE_BAD_SPEECH = "I have no idea what you're saying."; 34 | private static final String RESPONSE_PARTIAL_RESULTS = "I'm sorry, could you repeat that?"; 35 | 36 | /* Custom listener for speech result */ 37 | public interface OnResultListener { 38 | 39 | public void onSpeechResult(boolean resultOK, String hypothesis); 40 | 41 | } 42 | 43 | private OnResultListener mOnResultListener; 44 | 45 | public AndroidRecognition(OnResultListener onResultListener) { 46 | 47 | this.mOnResultListener = onResultListener; 48 | 49 | } 50 | 51 | @Override 52 | public void onReadyForSpeech(Bundle params) { 53 | 54 | /* Do nothing. */ 55 | 56 | } 57 | 58 | 59 | @Override 60 | public void onBeginningOfSpeech() { 61 | 62 | /* Do nothing. */ 63 | 64 | } 65 | 66 | @Override 67 | public void onRmsChanged(float rmsdB) { 68 | 69 | /* Do nothing. */ 70 | 71 | } 72 | 73 | @Override 74 | public void onBufferReceived(byte[] buffer) { 75 | 76 | /* Do nothing. */ 77 | 78 | } 79 | 80 | @Override 81 | public void onEndOfSpeech() { 82 | 83 | /* Do nothing. */ 84 | 85 | } 86 | 87 | @Override 88 | public void onError(int error) { 89 | 90 | switch (error) { 91 | 92 | case SpeechRecognizer.ERROR_SPEECH_TIMEOUT: 93 | 94 | mOnResultListener.onSpeechResult(false, RESPONSE_TIMEOUT); 95 | 96 | break; 97 | 98 | case SpeechRecognizer.ERROR_NO_MATCH: 99 | 100 | mOnResultListener.onSpeechResult(false, RESPONSE_BAD_SPEECH); 101 | 102 | break; 103 | 104 | } 105 | 106 | } 107 | 108 | @Override 109 | public void onResults(Bundle results) { 110 | 111 | final ArrayList matches = results 112 | .getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION); 113 | 114 | mOnResultListener.onSpeechResult(true, matches.get(0).toLowerCase()); 115 | 116 | } 117 | 118 | @Override 119 | public void onPartialResults(Bundle partialResults) { 120 | 121 | mOnResultListener.onSpeechResult(false, RESPONSE_PARTIAL_RESULTS); 122 | 123 | } 124 | 125 | @Override 126 | public void onEvent(int eventType, Bundle params) { 127 | 128 | /* Do nothing. */ 129 | 130 | } 131 | 132 | } 133 | 134 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/johnpersano/benson/lexicon/Response.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2014 John Persano 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | 19 | package com.github.johnpersano.benson.lexicon; 20 | 21 | import java.util.List; 22 | import java.util.Random; 23 | 24 | public class Response { 25 | 26 | /* The reply that Benson will speak. */ 27 | private String mReply; 28 | 29 | /* A nested set of queries for a response. Used for contextual conversations. */ 30 | private List mNestedLexicon; 31 | 32 | public Response() { 33 | 34 | /* Do nothing */ 35 | 36 | } 37 | 38 | /** 39 | * Set the reply Benson will speak. 40 | * 41 | * @param reply The reply Benson will speak. 42 | * @return An instance of the {@link Response} 43 | */ 44 | public Response setReply(String reply) { 45 | 46 | this.mReply = reply; 47 | 48 | return this; 49 | 50 | } 51 | 52 | /** 53 | * Returns the reply Benson will speak. 54 | * 55 | * @return The {@link String} Benson will speak. 56 | */ 57 | public String getReply() { 58 | 59 | return this.mReply; 60 | 61 | } 62 | 63 | /** 64 | * Set a {@link Query} {@link java.util.List} which 65 | * will serve as Benson's lexicon. This is useful for clarification or continuous conversation. 66 | * 67 | * @param lexicon The {@link Query} items Benson will recognize. 68 | * @return An instance of the {@link Response} 69 | */ 70 | public Response setNestedLexicon(List lexicon) { 71 | 72 | this.mNestedLexicon = lexicon; 73 | 74 | return this; 75 | 76 | } 77 | 78 | /** 79 | * Returns the {@link Query} {@link java.util.List} that 80 | * will serve as Benson's lexicon. 81 | * 82 | * @return The lexicon that Benson will use for known speech. 83 | */ 84 | public List getNestedLexicon() { 85 | 86 | return this.mNestedLexicon; 87 | 88 | } 89 | 90 | /** 91 | * Returns a random reply from a list of replies. 92 | * 93 | * @param replies Array resource. 94 | * @return Random {@link String} in the array. 95 | */ 96 | public static String getRandomReply(String[] replies) { 97 | 98 | return replies[new Random().nextInt(replies.length)]; 99 | 100 | } 101 | 102 | /** 103 | * Returns a random reply formatted with the current time from a list of replies. 104 | * 105 | * @param replies Array resource. 106 | * @return Random {@link String} in the array. 107 | */ 108 | public static String getRandomTimeReply(String[] replies) { 109 | 110 | final android.text.format.Time time = new android.text.format.Time(android.text.format.Time.getCurrentTimezone()); 111 | time.setToNow(); 112 | 113 | /* Format is (12h:minute)AM/PM. See for other options. */ 114 | return String.format(replies[new Random().nextInt(replies.length)], time.format("%l:%M%p")); 115 | 116 | } 117 | 118 | } 119 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/johnpersano/benson/lexicon/modules/Component.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2014 John Persano 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | 19 | package com.github.johnpersano.benson.lexicon.modules; 20 | 21 | import android.content.Context; 22 | 23 | import com.github.johnpersano.benson.R; 24 | import com.github.johnpersano.benson.lexicon.Query; 25 | import com.github.johnpersano.benson.lexicon.Response; 26 | 27 | import java.util.Arrays; 28 | import java.util.List; 29 | 30 | import me.palazzetti.adktoolkit.AdkManager; 31 | 32 | 33 | public class Component extends Query { 34 | 35 | /* Search the hypothesis for these words. The space is necessary. */ 36 | private static final String ON = " on"; 37 | private static final String OFF = " off"; 38 | 39 | /* These values will be sent to the Arduino via serial. */ 40 | private static final String SERIAL_ON = "1"; 41 | private static final String SERIAL_OFF = "0"; 42 | 43 | @Override 44 | public List getInputs() { 45 | 46 | return Arrays.asList("turn", "switch", "component", "exponent"); 47 | 48 | } 49 | 50 | @Override 51 | public Response getResponse(Context context, String hypothesis, AdkManager adkManager) { 52 | 53 | if (hypothesis.contains(ON)) { 54 | 55 | adkManager.write(SERIAL_ON); 56 | 57 | return new Response() 58 | .setReply(Response.getRandomReply(context.getResources().getStringArray(R.array.component_on_default))); 59 | 60 | } else if (hypothesis.contains(OFF)) { 61 | 62 | adkManager.write(SERIAL_OFF); 63 | 64 | return new Response() 65 | .setReply(Response.getRandomReply(context.getResources().getStringArray(R.array.component_off_default))); 66 | 67 | } else { 68 | 69 | return new Response() 70 | .setReply(Response.getRandomReply(context.getResources().getStringArray(R.array.component_default))) 71 | .setNestedLexicon(Arrays.asList(new ComponentOn(), new ComponentOff())); 72 | 73 | } 74 | 75 | } 76 | 77 | private class ComponentOn extends Query { 78 | 79 | @Override 80 | public List getInputs() { 81 | 82 | return Arrays.asList("on", "ron", "an", "bon"); 83 | 84 | } 85 | 86 | @Override 87 | public Response getResponse(Context context, String hypothesis, AdkManager adkManager) { 88 | 89 | adkManager.write(SERIAL_ON); 90 | 91 | return new Response() 92 | .setReply(Response.getRandomReply(context.getResources().getStringArray(R.array.component_on_default))); 93 | 94 | } 95 | 96 | } 97 | 98 | private class ComponentOff extends Query { 99 | 100 | @Override 101 | public List getInputs() { 102 | 103 | return Arrays.asList("off", "of"); 104 | 105 | } 106 | 107 | @Override 108 | public Response getResponse(Context context, String hypothesis, AdkManager adkManager) { 109 | 110 | adkManager.write(SERIAL_OFF); 111 | 112 | return new Response() 113 | .setReply(Response.getRandomReply(context.getResources().getStringArray(R.array.component_off_default))); 114 | 115 | } 116 | 117 | } 118 | 119 | } -------------------------------------------------------------------------------- /app/src/main/java/com/github/johnpersano/benson/lexicon/modules/Hello.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2014 John Persano 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | 19 | package com.github.johnpersano.benson.lexicon.modules; 20 | 21 | import android.content.Context; 22 | 23 | import com.github.johnpersano.benson.R; 24 | import com.github.johnpersano.benson.lexicon.Query; 25 | import com.github.johnpersano.benson.lexicon.Response; 26 | 27 | import java.util.Arrays; 28 | import java.util.List; 29 | 30 | import me.palazzetti.adktoolkit.AdkManager; 31 | 32 | public class Hello extends Query { 33 | 34 | @Override 35 | public List getInputs() { 36 | 37 | return Arrays.asList("hello", "hey", "hi", "howdy"); 38 | 39 | } 40 | 41 | @Override 42 | public Response getResponse(Context context, String hypothesis, AdkManager adkManager) { 43 | 44 | return new Response() 45 | .setReply(Response.getRandomReply(context.getResources().getStringArray(R.array.hello_default))) 46 | .setNestedLexicon(Arrays.asList(new PositiveStatus(), new NegativeStatus())); 47 | 48 | } 49 | 50 | private class PositiveStatus extends Query { 51 | 52 | @Override 53 | public List getInputs() { 54 | 55 | return Arrays.asList("good", "great", "excellent", "wonderful", "dandy", "super", "terrific"); 56 | 57 | } 58 | 59 | @Override 60 | public Response getResponse(Context context, String hypothesis, AdkManager adkManager) { 61 | 62 | return new Response() 63 | .setReply(Response.getRandomReply(context.getResources().getStringArray(R.array.hello_positive_status))); 64 | 65 | } 66 | 67 | } 68 | 69 | private class NegativeStatus extends Query { 70 | 71 | @Override 72 | public List getInputs() { 73 | 74 | return Arrays.asList("not good", "bad", "terrible", "horrible"); 75 | 76 | } 77 | 78 | @Override 79 | public Response getResponse(Context context, String hypothesis, AdkManager adkManager) { 80 | 81 | return new Response() 82 | .setReply(Response.getRandomReply(context.getResources().getStringArray(R.array.hello_negative_status))) 83 | .setNestedLexicon(Arrays.asList(new YesJoke(), new NoJoke())); 84 | 85 | } 86 | 87 | } 88 | 89 | private class YesJoke extends Joke { 90 | 91 | @Override 92 | public List getInputs() { 93 | 94 | return Arrays.asList("yes", "maybe", "okay", "sure", "certainly"); 95 | 96 | } 97 | 98 | @Override 99 | public Response getResponse(Context context, String hypothesis, AdkManager adkManager) { 100 | 101 | return super.getResponse(context, hypothesis, adkManager); 102 | 103 | } 104 | 105 | } 106 | 107 | private class NoJoke extends Query { 108 | 109 | @Override 110 | public List getInputs() { 111 | 112 | return Arrays.asList("no", "not now", "not at all", "absolutely not"); 113 | 114 | } 115 | 116 | @Override 117 | public Response getResponse(Context context, String hypothesis, AdkManager adkManager) { 118 | 119 | return new Response() 120 | .setReply(Response.getRandomReply(context.getResources().getStringArray(R.array.hello_no_joke))); 121 | 122 | } 123 | 124 | } 125 | 126 | } -------------------------------------------------------------------------------- /app/src/main/java/com/github/johnpersano/benson/lexicon/modules/HowAreYou.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2014 John Persano 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | 19 | package com.github.johnpersano.benson.lexicon.modules; 20 | 21 | import android.content.Context; 22 | 23 | import com.github.johnpersano.benson.R; 24 | import com.github.johnpersano.benson.lexicon.Query; 25 | import com.github.johnpersano.benson.lexicon.Response; 26 | 27 | import java.util.Arrays; 28 | import java.util.List; 29 | 30 | import me.palazzetti.adktoolkit.AdkManager; 31 | 32 | 33 | public class HowAreYou extends Query { 34 | 35 | @Override 36 | public List getInputs() { 37 | 38 | return Arrays.asList("how are you"); 39 | 40 | } 41 | 42 | @Override 43 | public Response getResponse(Context context, String hypothesis, AdkManager adkManager) { 44 | 45 | return new Response() 46 | .setReply(Response.getRandomReply(context.getResources().getStringArray(R.array.how_are_you_default))) 47 | .setNestedLexicon(Arrays.asList(new PositiveStatus(), new NegativeStatus())); 48 | 49 | } 50 | 51 | private class PositiveStatus extends Query { 52 | 53 | @Override 54 | public List getInputs() { 55 | 56 | return Arrays.asList("good", "great", "excellent", "wonderful", "dandy", "super", "terrific"); 57 | 58 | } 59 | 60 | @Override 61 | public Response getResponse(Context context, String hypothesis, AdkManager adkManager) { 62 | 63 | return new Response() 64 | .setReply(Response.getRandomReply(context.getResources().getStringArray(R.array.how_are_you_positive_status))); 65 | 66 | } 67 | 68 | } 69 | 70 | private class NegativeStatus extends Query { 71 | 72 | @Override 73 | public List getInputs() { 74 | 75 | return Arrays.asList("not good", "bad", "terrible", "horrible"); 76 | 77 | } 78 | 79 | @Override 80 | public Response getResponse(Context context, String hypothesis, AdkManager adkManager) { 81 | 82 | return new Response() 83 | .setReply(Response.getRandomReply(context.getResources().getStringArray(R.array.how_are_you_negative_status))) 84 | .setNestedLexicon(Arrays.asList(new YesJoke(), new NoJoke())); 85 | 86 | } 87 | 88 | } 89 | 90 | private class YesJoke extends Joke { 91 | 92 | @Override 93 | public List getInputs() { 94 | 95 | return Arrays.asList("yes", "maybe", "okay", "sure", "certainly"); 96 | 97 | } 98 | 99 | @Override 100 | public Response getResponse(Context context, String hypothesis, AdkManager adkManager) { 101 | 102 | return super.getResponse(context, hypothesis, adkManager); 103 | 104 | } 105 | 106 | } 107 | 108 | private class NoJoke extends Query { 109 | 110 | @Override 111 | public List getInputs() { 112 | 113 | return Arrays.asList("no", "not now", "not at all", "absolutely not"); 114 | 115 | } 116 | 117 | @Override 118 | public Response getResponse(Context context, String hypothesis, AdkManager adkManager) { 119 | 120 | return new Response() 121 | .setReply(Response.getRandomReply(context.getResources().getStringArray(R.array.how_are_you_no_joke))); 122 | 123 | } 124 | 125 | } 126 | 127 | } 128 | -------------------------------------------------------------------------------- /app/src/main/res/values/arrays.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | benson 7 | benson benson 8 | 9 | 10 | 11 | 12 | I couldn\'t find an answer to that. 13 | Sorry, I couldn\'t find anything online for that. 14 | I couldn\'t seem to find an answer to that. 15 | There\'s nothing on the internet about that. 16 | 17 | 18 | 19 | 20 | The doesn\'t seem like a valid question. 21 | That\'s not something I can look up. 22 | Sorry, I wasn\'t able to look that up. 23 | I\'m not sure what you mean by that. 24 | I wasn\'t able to look that up. 25 | 26 | 27 | 28 | 29 | Turning the component on sir. 30 | Right away sir. 31 | Sure thing sir. 32 | 33 | 34 | 35 | Turning the component off sir. 36 | Right away sir. 37 | Sure thing sir. 38 | 39 | 40 | 41 | What would you like me to do with the component sir? 42 | What do you like me to do with the component sir? 43 | 44 | 45 | 46 | 47 | Hello, how are you? 48 | Hello, how are you today sir? 49 | Greetings, how are you today? 50 | Greetings, how are you? 51 | Hi, how are you? 52 | Hi, how are you sir? 53 | 54 | 55 | 56 | Glad to hear it sir. 57 | Good to hear. 58 | I\'m glad sir. 59 | Fantastic. 60 | 61 | 62 | 63 | Sorry to hear that sir. Would a joke lighten the mood? 64 | Sorry to hear that sir. Would a joke cheer you up? 65 | Oh. Would a joke lighten the mood? 66 | Oh. Would a joke cheer you up? 67 | 68 | 69 | 70 | Okay then. I\'ll be here if you change your mind. 71 | I\'ll be here if you change your mind. 72 | Okay then. 73 | 74 | 75 | 76 | 77 | I\'m well. How are you? 78 | I\'m well today, how about you? 79 | I\'m well. How are you today? 80 | 81 | 82 | 83 | Glad to hear it sir. 84 | Good to hear. 85 | I\'m glad sir. 86 | Fantastic. 87 | 88 | 89 | 90 | Sorry to hear that sir. Would a joke lighten the mood? 91 | Sorry to hear that sir. Would a joke cheer you up? 92 | Oh. Would a joke lighten the mood? 93 | Oh. Would a joke cheer you up? 94 | 95 | 96 | 97 | Okay then. I\'ll be here if you change your mind. 98 | I\'ll be here if you change your mind. 99 | Okay then. 100 | 101 | 102 | 103 | 104 | The C language combines all the power of assembly language with all the ease-of-use of assembly language. 105 | Linux is user friendly. It\'s just very particular about who its friends are. 106 | There are 10 types of people in this world. Those who understand binary, and those who don\'t. 107 | There\'s a band called 1023 megabytes. They don\'t have any gigs yet. 108 | My software doesn\'t have any bugs. It just develops random features. 109 | My attitude isn\’t bad. It’s in beta. 110 | 111 | 112 | 113 | 114 | It\'s %1s. 115 | Right now it is %1s. 116 | It is %1s. 117 | It is currently %1s. 118 | %1s. 119 | 120 | 121 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # For Cygwin, ensure paths are in UNIX format before anything is touched. 46 | if $cygwin ; then 47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 48 | fi 49 | 50 | # Attempt to set APP_HOME 51 | # Resolve links: $0 may be a link 52 | PRG="$0" 53 | # Need this for relative symlinks. 54 | while [ -h "$PRG" ] ; do 55 | ls=`ls -ld "$PRG"` 56 | link=`expr "$ls" : '.*-> \(.*\)$'` 57 | if expr "$link" : '/.*' > /dev/null; then 58 | PRG="$link" 59 | else 60 | PRG=`dirname "$PRG"`"/$link" 61 | fi 62 | done 63 | SAVED="`pwd`" 64 | cd "`dirname \"$PRG\"`/" >&- 65 | APP_HOME="`pwd -P`" 66 | cd "$SAVED" >&- 67 | 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 69 | 70 | # Determine the Java command to use to start the JVM. 71 | if [ -n "$JAVA_HOME" ] ; then 72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 73 | # IBM's JDK on AIX uses strange locations for the executables 74 | JAVACMD="$JAVA_HOME/jre/sh/java" 75 | else 76 | JAVACMD="$JAVA_HOME/bin/java" 77 | fi 78 | if [ ! -x "$JAVACMD" ] ; then 79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 80 | 81 | Please set the JAVA_HOME variable in your environment to match the 82 | location of your Java installation." 83 | fi 84 | else 85 | JAVACMD="java" 86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 87 | 88 | Please set the JAVA_HOME variable in your environment to match the 89 | location of your Java installation." 90 | fi 91 | 92 | # Increase the maximum file descriptors if we can. 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 94 | MAX_FD_LIMIT=`ulimit -H -n` 95 | if [ $? -eq 0 ] ; then 96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 97 | MAX_FD="$MAX_FD_LIMIT" 98 | fi 99 | ulimit -n $MAX_FD 100 | if [ $? -ne 0 ] ; then 101 | warn "Could not set maximum file descriptor limit: $MAX_FD" 102 | fi 103 | else 104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 105 | fi 106 | fi 107 | 108 | # For Darwin, add options to specify how the application appears in the dock 109 | if $darwin; then 110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 111 | fi 112 | 113 | # For Cygwin, switch paths to Windows format before running java 114 | if $cygwin ; then 115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/johnpersano/benson/visualizer/VisualizerView.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011, Felix Palmer 3 | * 4 | * Licensed under the MIT license: 5 | * http://creativecommons.org/licenses/MIT/ 6 | */ 7 | 8 | package com.github.johnpersano.benson.visualizer; 9 | 10 | import android.content.Context; 11 | import android.graphics.Bitmap; 12 | import android.graphics.Bitmap.Config; 13 | import android.graphics.Canvas; 14 | import android.graphics.Color; 15 | import android.graphics.Matrix; 16 | import android.graphics.Paint; 17 | import android.graphics.PorterDuff.Mode; 18 | import android.graphics.PorterDuffXfermode; 19 | import android.graphics.Rect; 20 | import android.media.audiofx.Visualizer; 21 | import android.util.AttributeSet; 22 | import android.view.View; 23 | 24 | import java.util.HashSet; 25 | import java.util.Set; 26 | 27 | /** 28 | * A class that draws visualizations of data received from a 29 | * {@link android.media.audiofx.Visualizer.OnDataCaptureListener#onWaveFormDataCapture } and 30 | * {@link android.media.audiofx.Visualizer.OnDataCaptureListener#onFftDataCapture } 31 | */ 32 | @SuppressWarnings({"JavaDoc", "UnusedDeclaration"}) 33 | public class VisualizerView extends View { 34 | 35 | private static final String TAG = "VisualizerView"; 36 | 37 | private byte[] mBytes; 38 | private byte[] mFFTBytes; 39 | private Rect mRect = new Rect(); 40 | private Visualizer mVisualizer; 41 | 42 | private Set mRenderers; 43 | 44 | private Paint mFlashPaint = new Paint(); 45 | private Paint mFadePaint = new Paint(); 46 | 47 | private Matrix mMatrix; 48 | 49 | public VisualizerView(Context context, AttributeSet attrs, int defStyle) { 50 | super(context, attrs); 51 | 52 | mBytes = null; 53 | mFFTBytes = null; 54 | 55 | mFlashPaint.setColor(Color.argb(122, 255, 255, 255)); 56 | mFadePaint.setColor(Color.argb(238, 255, 255, 255)); // Adjust alpha to change how quickly the image fades 57 | mFadePaint.setXfermode(new PorterDuffXfermode(Mode.MULTIPLY)); 58 | 59 | mRenderers = new HashSet(); 60 | 61 | mMatrix = new Matrix(); 62 | 63 | } 64 | 65 | public VisualizerView(Context context, AttributeSet attrs) { 66 | 67 | this(context, attrs, 0); 68 | } 69 | 70 | public VisualizerView(Context context) { 71 | 72 | this(context, null, 0); 73 | 74 | } 75 | 76 | public void create() { 77 | 78 | mVisualizer = new Visualizer(0); 79 | mVisualizer.setCaptureSize(Visualizer.getCaptureSizeRange()[1]); 80 | 81 | // Pass through Visualizer data to VisualizerView 82 | Visualizer.OnDataCaptureListener captureListener = new Visualizer.OnDataCaptureListener() { 83 | 84 | @Override 85 | public void onWaveFormDataCapture(Visualizer visualizer, byte[] bytes, int samplingRate) { 86 | 87 | updateVisualizer(bytes); 88 | 89 | } 90 | 91 | @Override 92 | public void onFftDataCapture(Visualizer visualizer, byte[] bytes, int samplingRate) { 93 | 94 | updateVisualizerFFT(bytes); 95 | 96 | } 97 | 98 | }; 99 | 100 | mVisualizer.setDataCaptureListener(captureListener, Visualizer.getMaxCaptureRate() / 2, true, true); 101 | 102 | // Enabled Visualizer and disable when we're done with the stream 103 | mVisualizer.setEnabled(true); 104 | 105 | } 106 | 107 | public void addRenderer(Renderer renderer) { 108 | 109 | if (renderer != null) { 110 | 111 | mRenderers.add(renderer); 112 | 113 | } 114 | 115 | } 116 | 117 | public void clearRenderers() { 118 | 119 | mRenderers.clear(); 120 | 121 | } 122 | 123 | /** 124 | * Call to release the resources used by VisualizerView. Like with the 125 | * MediaPlayer it is good practice to call this method 126 | */ 127 | public void release() { 128 | 129 | mVisualizer.release(); 130 | 131 | } 132 | 133 | /** 134 | * Pass data to the visualizer. Typically this will be obtained from the 135 | * Android Visualizer.OnDataCaptureListener call back. See 136 | * {@link android.media.audiofx.Visualizer.OnDataCaptureListener#onWaveFormDataCapture } 137 | * 138 | * @param bytes 139 | */ 140 | public void updateVisualizer(byte[] bytes) { 141 | mBytes = bytes; 142 | invalidate(); 143 | } 144 | 145 | /** 146 | * Pass FFT data to the visualizer. Typically this will be obtained from the 147 | * Android Visualizer.OnDataCaptureListener call back. See 148 | * {@link android.media.audiofx.Visualizer.OnDataCaptureListener#onFftDataCapture } 149 | * 150 | * @param bytes 151 | */ 152 | public void updateVisualizerFFT(byte[] bytes) { 153 | mFFTBytes = bytes; 154 | invalidate(); 155 | } 156 | 157 | boolean mFlash = false; 158 | 159 | /** 160 | * Call this to make the visualizer flash. Useful for flashing at the start 161 | * of a song/loop etc... 162 | */ 163 | public void flash() { 164 | mFlash = true; 165 | invalidate(); 166 | } 167 | 168 | Bitmap mCanvasBitmap; 169 | Canvas mCanvas; 170 | 171 | 172 | @Override 173 | protected void onDraw(Canvas canvas) { 174 | super.onDraw(canvas); 175 | 176 | // Create canvas once we're ready to draw 177 | mRect.set(0, 0, getWidth(), getHeight()); 178 | 179 | if (mCanvasBitmap == null) { 180 | 181 | mCanvasBitmap = Bitmap.createBitmap(canvas.getWidth(), canvas.getHeight(), Config.ARGB_8888); 182 | 183 | } 184 | if (mCanvas == null) { 185 | 186 | mCanvas = new Canvas(mCanvasBitmap); 187 | 188 | } 189 | 190 | if (mBytes != null) { 191 | 192 | AudioData audioData = new AudioData(mBytes); 193 | 194 | for (Renderer r : mRenderers) { 195 | 196 | r.render(mCanvas, audioData, mRect); 197 | 198 | } 199 | 200 | } 201 | 202 | if (mFFTBytes != null) { 203 | // Render all FFT renderers 204 | FFTData fftData = new FFTData(mFFTBytes); 205 | for (Renderer r : mRenderers) { 206 | r.render(mCanvas, fftData, mRect); 207 | } 208 | } 209 | 210 | // Fade out old contents 211 | mCanvas.drawPaint(mFadePaint); 212 | 213 | if (mFlash) { 214 | mFlash = false; 215 | mCanvas.drawPaint(mFlashPaint); 216 | } 217 | 218 | canvas.drawBitmap(mCanvasBitmap, mMatrix, null); 219 | } 220 | } 221 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/johnpersano/benson/views/visualizer/VisualizerView.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2011, Felix Palmer 3 | * 4 | * Licensed under the MIT license: 5 | * http://creativecommons.org/licenses/MIT/ 6 | */ 7 | 8 | package com.github.johnpersano.benson.views.visualizer; 9 | 10 | import android.content.Context; 11 | import android.graphics.Bitmap; 12 | import android.graphics.Bitmap.Config; 13 | import android.graphics.Canvas; 14 | import android.graphics.Color; 15 | import android.graphics.Matrix; 16 | import android.graphics.Paint; 17 | import android.graphics.PorterDuff.Mode; 18 | import android.graphics.PorterDuffXfermode; 19 | import android.graphics.Rect; 20 | import android.media.audiofx.Visualizer; 21 | import android.util.AttributeSet; 22 | import android.view.View; 23 | 24 | import java.util.HashSet; 25 | import java.util.Set; 26 | 27 | /** 28 | * A class that draws visualizations of data received from a 29 | * {@link android.media.audiofx.Visualizer.OnDataCaptureListener#onWaveFormDataCapture } and 30 | * {@link android.media.audiofx.Visualizer.OnDataCaptureListener#onFftDataCapture } 31 | */ 32 | @SuppressWarnings({"JavaDoc", "UnusedDeclaration"}) 33 | public class VisualizerView extends View { 34 | 35 | private static final String TAG = "VisualizerView"; 36 | 37 | private byte[] mBytes; 38 | private byte[] mFFTBytes; 39 | private Rect mRect = new Rect(); 40 | private Visualizer mVisualizer; 41 | 42 | private Set mRenderers; 43 | 44 | private Paint mFlashPaint = new Paint(); 45 | private Paint mFadePaint = new Paint(); 46 | 47 | private Matrix mMatrix; 48 | 49 | public VisualizerView(Context context, AttributeSet attrs, int defStyle) { 50 | super(context, attrs); 51 | 52 | mBytes = null; 53 | mFFTBytes = null; 54 | 55 | mFlashPaint.setColor(Color.argb(122, 255, 255, 255)); 56 | mFadePaint.setColor(Color.argb(238, 255, 255, 255)); // Adjust alpha to change how quickly the image fades 57 | mFadePaint.setXfermode(new PorterDuffXfermode(Mode.MULTIPLY)); 58 | 59 | mRenderers = new HashSet(); 60 | 61 | mMatrix = new Matrix(); 62 | 63 | } 64 | 65 | public VisualizerView(Context context, AttributeSet attrs) { 66 | 67 | this(context, attrs, 0); 68 | } 69 | 70 | public VisualizerView(Context context) { 71 | 72 | this(context, null, 0); 73 | 74 | } 75 | 76 | public void create() { 77 | 78 | mVisualizer = new Visualizer(0); 79 | mVisualizer.setEnabled(false); // Illegal state exception fix 80 | mVisualizer.setCaptureSize(Visualizer.getCaptureSizeRange()[1]); 81 | 82 | // Pass through Visualizer data to VisualizerView 83 | Visualizer.OnDataCaptureListener captureListener = new Visualizer.OnDataCaptureListener() { 84 | 85 | @Override 86 | public void onWaveFormDataCapture(Visualizer visualizer, byte[] bytes, int samplingRate) { 87 | 88 | updateVisualizer(bytes); 89 | 90 | } 91 | 92 | @Override 93 | public void onFftDataCapture(Visualizer visualizer, byte[] bytes, int samplingRate) { 94 | 95 | updateVisualizerFFT(bytes); 96 | 97 | } 98 | 99 | }; 100 | 101 | mVisualizer.setDataCaptureListener(captureListener, Visualizer.getMaxCaptureRate() / 2, true, true); 102 | 103 | // Enabled Visualizer and disable when we're done with the stream 104 | mVisualizer.setEnabled(true); 105 | 106 | } 107 | 108 | public void addRenderer(Renderer renderer) { 109 | 110 | if (renderer != null) { 111 | 112 | mRenderers.add(renderer); 113 | 114 | } 115 | 116 | } 117 | 118 | public void clearRenderers() { 119 | 120 | mRenderers.clear(); 121 | 122 | } 123 | 124 | /** 125 | * Call to release the resources used by VisualizerView. Like with the 126 | * MediaPlayer it is good practice to call this method 127 | */ 128 | public void release() { 129 | 130 | mVisualizer.release(); 131 | 132 | } 133 | 134 | /** 135 | * Pass data to the visualizer. Typically this will be obtained from the 136 | * Android Visualizer.OnDataCaptureListener call back. See 137 | * {@link android.media.audiofx.Visualizer.OnDataCaptureListener#onWaveFormDataCapture } 138 | * 139 | * @param bytes 140 | */ 141 | public void updateVisualizer(byte[] bytes) { 142 | mBytes = bytes; 143 | invalidate(); 144 | } 145 | 146 | /** 147 | * Pass FFT data to the visualizer. Typically this will be obtained from the 148 | * Android Visualizer.OnDataCaptureListener call back. See 149 | * {@link android.media.audiofx.Visualizer.OnDataCaptureListener#onFftDataCapture } 150 | * 151 | * @param bytes 152 | */ 153 | public void updateVisualizerFFT(byte[] bytes) { 154 | mFFTBytes = bytes; 155 | invalidate(); 156 | } 157 | 158 | boolean mFlash = false; 159 | 160 | /** 161 | * Call this to make the visualizer flash. Useful for flashing at the start 162 | * of a song/loop etc... 163 | */ 164 | public void flash() { 165 | mFlash = true; 166 | invalidate(); 167 | } 168 | 169 | Bitmap mCanvasBitmap; 170 | Canvas mCanvas; 171 | 172 | 173 | @Override 174 | protected void onDraw(Canvas canvas) { 175 | super.onDraw(canvas); 176 | 177 | // Create canvas once we're ready to draw 178 | mRect.set(0, 0, getWidth(), getHeight()); 179 | 180 | if (mCanvasBitmap == null) { 181 | 182 | mCanvasBitmap = Bitmap.createBitmap(canvas.getWidth(), canvas.getHeight(), Config.ARGB_8888); 183 | 184 | } 185 | if (mCanvas == null) { 186 | 187 | mCanvas = new Canvas(mCanvasBitmap); 188 | 189 | } 190 | 191 | if (mBytes != null) { 192 | 193 | AudioData audioData = new AudioData(mBytes); 194 | 195 | for (Renderer r : mRenderers) { 196 | 197 | r.render(mCanvas, audioData, mRect); 198 | 199 | } 200 | 201 | } 202 | 203 | if (mFFTBytes != null) { 204 | // Render all FFT renderers 205 | FFTData fftData = new FFTData(mFFTBytes); 206 | for (Renderer r : mRenderers) { 207 | r.render(mCanvas, fftData, mRect); 208 | } 209 | } 210 | 211 | // Fade out old contents 212 | mCanvas.drawPaint(mFadePaint); 213 | 214 | if (mFlash) { 215 | mFlash = false; 216 | mCanvas.drawPaint(mFlashPaint); 217 | } 218 | 219 | canvas.drawBitmap(mCanvasBitmap, mMatrix, null); 220 | } 221 | } 222 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/johnpersano/benson/ActivityBenson.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2014 John Persano 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | 19 | package com.github.johnpersano.benson; 20 | 21 | import android.app.Activity; 22 | import android.content.Context; 23 | import android.content.Intent; 24 | import android.graphics.Color; 25 | import android.hardware.usb.UsbManager; 26 | import android.os.AsyncTask; 27 | import android.os.Bundle; 28 | import android.os.Handler; 29 | import android.speech.RecognizerIntent; 30 | import android.speech.tts.TextToSpeech; 31 | import android.speech.tts.UtteranceProgressListener; 32 | import android.util.Log; 33 | import android.view.View; 34 | 35 | import com.github.johnpersano.benson.lexicon.Query; 36 | import com.github.johnpersano.benson.lexicon.Response; 37 | import com.github.johnpersano.benson.lexicon.Lexicon; 38 | import com.github.johnpersano.benson.recognition.AndroidRecognition; 39 | import com.github.johnpersano.benson.recognition.CMUSphinxRecognition; 40 | import com.github.johnpersano.benson.views.AnimatedTextView; 41 | import com.github.johnpersano.benson.views.visualizer.CircleRenderer; 42 | import com.github.johnpersano.benson.views.visualizer.VisualizerView; 43 | 44 | import com.wolfram.alpha.WAEngine; 45 | import com.wolfram.alpha.WAException; 46 | import com.wolfram.alpha.WAPlainText; 47 | import com.wolfram.alpha.WAQuery; 48 | import com.wolfram.alpha.WAQueryResult; 49 | 50 | import java.io.File; 51 | import java.io.IOException; 52 | import java.util.Arrays; 53 | import java.util.HashMap; 54 | import java.util.List; 55 | import java.util.Locale; 56 | import java.util.Random; 57 | 58 | import edu.cmu.pocketsphinx.Assets; 59 | import edu.cmu.pocketsphinx.SpeechRecognizer; 60 | import me.palazzetti.adktoolkit.AdkManager; 61 | 62 | import static edu.cmu.pocketsphinx.SpeechRecognizerSetup.defaultSetup; 63 | 64 | 65 | public class ActivityBenson extends Activity implements AndroidRecognition.OnResultListener { 66 | 67 | @SuppressWarnings("UnusedDeclaration") 68 | private static final String TAG = "ActivityBenson"; 69 | 70 | /* CMUSphinx keyword requires a string key. For simplicity, the value is set as 'key'. */ 71 | private static final String RECOGNITION_KEY = "key"; 72 | 73 | /* CMUSphinx keyword. This is the only word CMUSphinx will listen for. */ 74 | private static final String RECOGNITION_KEYWORD = "benson"; 75 | 76 | /* Generic responses for speech recognition. */ 77 | private static final String RESPONSE_SIR = "Sir?"; 78 | private static final String RESPONSE_ONLINE = "I am online. If you require my services, I'll be here."; 79 | private static final String RESPONSE_HOLD = "Let me look that up."; 80 | 81 | /* Custom TextView that will animate text. */ 82 | private AnimatedTextView mAnimatedTextView; 83 | 84 | /* Various recognizers Benson needs to operate. */ 85 | private SpeechRecognizer mCMUSphinxRecognizer; 86 | private android.speech.SpeechRecognizer mAndroidRecognizer; 87 | private Intent mAndroidRecognizerIntent; 88 | 89 | /* Benson's voice. Try experimenting with text to speech settings to change voice. */ 90 | private TextToSpeech mTTS; 91 | 92 | /* The pulsating circles used to represent voice and status. */ 93 | private VisualizerView mVisualizerView; 94 | 95 | /* Android Adk manager to communicate with the Arduino Due. */ 96 | private AdkManager mAdkManager; 97 | 98 | /* This handler will clear on screen text and reset mood ten seconds after Benson speaks. */ 99 | private Handler mTextViewHandler; 100 | 101 | /* Default conversational vocabulary. This list is used to reset Benson's lexicon and does NOT change. */ 102 | private final List mDefaultList = new Lexicon().lexicon; 103 | 104 | /* Conversational vocabulary. This list will change depending on response clarification. */ 105 | private List mDynamicList; 106 | 107 | 108 | @Override 109 | protected void onCreate(Bundle savedInstanceState) { 110 | super.onCreate(savedInstanceState); 111 | setContentView(R.layout.activity_benson); 112 | 113 | /* Hide navigation UI for fullscreen. */ 114 | getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN 115 | | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION 116 | | View.SYSTEM_UI_FLAG_LAYOUT_STABLE 117 | | View.SYSTEM_UI_FLAG_LOW_PROFILE 118 | | View.SYSTEM_UI_FLAG_FULLSCREEN 119 | | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION); 120 | 121 | /* Open connection to the Arduino Due. */ 122 | mAdkManager = new AdkManager((UsbManager) getSystemService(Context.USB_SERVICE)); 123 | mAdkManager.open(); 124 | 125 | /* This textview serves as Benson's subtitle. */ 126 | mAnimatedTextView = (AnimatedTextView) 127 | findViewById(R.id.animated_textview); 128 | mAnimatedTextView.setText(getResources().getString(R.string.initialization)); 129 | 130 | /* The CMUSphinxRecognizer will continuously listen for the word 'benson'. */ 131 | initializeCMUSphinxRecognizer(); 132 | 133 | /* The AndroidRecognizer will be called after Benson hears his name to listen for the users speech. */ 134 | /* Create an Android speechrecognizer and set recognition listener. Used custom listener for simplicity. */ 135 | mAndroidRecognizer = android.speech.SpeechRecognizer.createSpeechRecognizer(ActivityBenson.this); 136 | mAndroidRecognizer.setRecognitionListener(new AndroidRecognition(ActivityBenson.this)); 137 | 138 | /* Intent for Android speech recognizer. Set as class member variable to avoid unnecessary object recreation. */ 139 | mAndroidRecognizerIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH); 140 | mAndroidRecognizerIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_PREFERENCE, "en"); 141 | mAndroidRecognizerIntent.putExtra(RecognizerIntent.EXTRA_CALLING_PACKAGE, ActivityBenson.this.getPackageName()); 142 | mAndroidRecognizerIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_WEB_SEARCH); 143 | mAndroidRecognizerIntent.putExtra(RecognizerIntent.EXTRA_MAX_RESULTS, 1); 144 | 145 | /* The visualizer is Benson's pulsating circle. */ 146 | mVisualizerView = (VisualizerView) findViewById(R.id.visualizer_view); 147 | mVisualizerView.create(); 148 | 149 | /* Add a circle renderer to the visualizer view. Other renderers can be used from the visualizer library. */ 150 | CircleRenderer circleRenderer = new CircleRenderer(); 151 | mVisualizerView.addRenderer(circleRenderer); 152 | circleRenderer.changeColor(Color.CYAN); 153 | 154 | /* The Android TTS service will serve as Benson's voice. */ 155 | initializeSpeech(); 156 | 157 | /* Create a handler to clear the subtitle text off of the screen. */ 158 | mTextViewHandler = new Handler(); 159 | 160 | } 161 | 162 | private void initializeCMUSphinxRecognizer() { 163 | 164 | /* Do the initialization in an AsyncTask since it takes a while. */ 165 | new AsyncTask() { 166 | 167 | @Override 168 | protected void onPreExecute() { 169 | super.onPreExecute(); 170 | 171 | mAnimatedTextView.setText(getResources().getString(R.string.initialization)); 172 | 173 | } 174 | 175 | @Override 176 | protected Exception doInBackground(Void... params) { 177 | 178 | try { 179 | 180 | final File assetDirectory = new Assets(ActivityBenson.this).syncAssets(); 181 | final File modelsDirectory = new File(assetDirectory, "models"); 182 | 183 | mCMUSphinxRecognizer = defaultSetup() 184 | .setAcousticModel(new File(modelsDirectory, "hmm/en-us-semi")) 185 | .setDictionary(new File(modelsDirectory, "dict/cmu07a.dic")) 186 | .setRawLogDir(assetDirectory).setKeywordThreshold(1e-20f) 187 | .getRecognizer(); 188 | 189 | } catch (IOException exception) { 190 | 191 | return exception; 192 | 193 | } 194 | 195 | return null; 196 | 197 | } 198 | 199 | @Override 200 | protected void onPostExecute(Exception exception) { 201 | 202 | /* If no exception was thrown during initialization, add recognition listener. */ 203 | if (exception == null) { 204 | 205 | mCMUSphinxRecognizer.addListener(new CMUSphinxRecognition(new CMUSphinxRecognition.OnResultListener() { 206 | 207 | @Override 208 | public void onSpeechResult(String hypothesis) { 209 | 210 | if (hypothesis != null) { 211 | 212 | /* Check if response contains the Benson keywords. */ 213 | if (Arrays.asList(getResources().getStringArray(R.array.cmusphinx_keywords)).contains(hypothesis)) { 214 | 215 | say(new Response().setReply(RESPONSE_SIR)); 216 | 217 | } 218 | 219 | } 220 | 221 | } 222 | 223 | })); 224 | 225 | /* Set the CMUSphinx speech recognizer to listen for the keyword 'benson'. */ 226 | mCMUSphinxRecognizer.addKeyphraseSearch(RECOGNITION_KEY, RECOGNITION_KEYWORD); 227 | 228 | /* Do not start recognition immediately. Recognition start/stop is handled by a TTS listener. */ 229 | mCMUSphinxRecognizer.stop(); 230 | 231 | } else { 232 | 233 | mAnimatedTextView.setText(getResources().getString(R.string.error_recognition)); 234 | 235 | } 236 | 237 | } 238 | 239 | }.execute(); 240 | 241 | } 242 | 243 | private void initializeSpeech() { 244 | 245 | /* Initialize the Android text to speech service and set oninitialization listener. */ 246 | mTTS = new TextToSpeech(getApplicationContext(), new TextToSpeech.OnInitListener() { 247 | 248 | @Override 249 | public void onInit(int status) { 250 | 251 | /* Set utterance listener if the text to speech service was initialized correctly. */ 252 | if (status != TextToSpeech.ERROR) { 253 | 254 | /* Give Benson an English accent. Just because. */ 255 | mTTS.setLanguage(Locale.UK); 256 | 257 | /* On first initialization tell user Benson is online. */ 258 | say(new Response().setReply(RESPONSE_ONLINE)); 259 | 260 | /* Set listener for Benson's speech. Both recognition services will start/stop based on speech progress. */ 261 | mTTS.setOnUtteranceProgressListener(new UtteranceProgressListener() { 262 | 263 | @Override 264 | public void onStart(final String utteranceId) { 265 | 266 | /* If Benson is speaking, do not listen for speech recognition. */ 267 | mCMUSphinxRecognizer.stop(); 268 | 269 | /* Show what Benson is saying in the textview. Apparently this listener does not run on UI thread. */ 270 | runOnUiThread(new Runnable() { 271 | public void run() { 272 | 273 | mAnimatedTextView.animateText(utteranceId); 274 | 275 | } 276 | }); 277 | 278 | /* Clear pending runnable that removes subtitle text. */ 279 | mTextViewHandler.removeCallbacks(mTextViewRunnable); 280 | 281 | } 282 | 283 | @Override 284 | public void onDone(String utteranceId) { 285 | 286 | /* If Benson has responded to his name or is asking a question, start Android SpeechRecognizer. */ 287 | if (utteranceId.equals(RESPONSE_SIR) || utteranceId.endsWith("?")) { 288 | 289 | runOnUiThread(new Runnable() { 290 | public void run() { 291 | 292 | mAndroidRecognizer.startListening(mAndroidRecognizerIntent); 293 | 294 | } 295 | }); 296 | 297 | /* If Benson has told the user to hold (Wolfram query) do not start listening. */ 298 | } else if (utteranceId.equals(RESPONSE_HOLD)) { 299 | 300 | /* Prevent speech recognition while Wolfram query is running. Only used to avoid the else call. */ 301 | mCMUSphinxRecognizer.stop(); 302 | 303 | } else { 304 | 305 | /* Benson has responded to the user and should start listening again. */ 306 | mCMUSphinxRecognizer.startListening(RECOGNITION_KEY); 307 | 308 | /* Clear the subtitle after twelve seconds. */ 309 | mTextViewHandler.postDelayed(mTextViewRunnable, (12 * 1000)); 310 | 311 | } 312 | 313 | } 314 | 315 | @Override 316 | public void onError(String utteranceId) { 317 | 318 | /* This has never been called during testing. Start listening just in case. */ 319 | mCMUSphinxRecognizer.startListening(RECOGNITION_KEY); 320 | 321 | } 322 | 323 | }); 324 | 325 | } 326 | 327 | } 328 | 329 | }); 330 | 331 | } 332 | 333 | 334 | @Override 335 | public void onDestroy() { 336 | 337 | if (mCMUSphinxRecognizer != null) { 338 | 339 | mCMUSphinxRecognizer.stop(); 340 | mCMUSphinxRecognizer = null; 341 | 342 | } 343 | 344 | if (mTTS != null) { 345 | 346 | mTTS.stop(); 347 | mTTS.shutdown(); 348 | 349 | } 350 | 351 | if (mAndroidRecognizer != null) { 352 | 353 | mAndroidRecognizer.stopListening(); 354 | mAndroidRecognizer.destroy(); 355 | 356 | } 357 | 358 | if (mVisualizerView != null) { 359 | 360 | mVisualizerView.release(); 361 | 362 | } 363 | 364 | super.onDestroy(); 365 | 366 | } 367 | 368 | 369 | @Override 370 | public void onSpeechResult(boolean resultOK, String hypothesis) { 371 | 372 | if (resultOK) { 373 | 374 | for (Query query : (mDynamicList != null) ? this.mDynamicList : mDefaultList) { 375 | 376 | for (String input : query.getInputs()) { 377 | 378 | if (hypothesis.contains(input)) { 379 | 380 | final Response response = query.getResponse(ActivityBenson.this, hypothesis, mAdkManager); 381 | this.mDynamicList = response.getNestedLexicon(); 382 | 383 | say(response); 384 | 385 | return; 386 | 387 | } 388 | 389 | } 390 | 391 | } 392 | 393 | if (mDynamicList != null) { 394 | 395 | say(new Response().setReply(getResources().getString(R.string.response_misunderstood_nested_lexicon))); 396 | 397 | this.mDynamicList = null; 398 | 399 | } else { 400 | 401 | /* Benson was not able to find the user's query in his vocabulary, maybe it's a query for Wolfram Alpha. */ 402 | say(new Response().setReply(RESPONSE_HOLD)); 403 | 404 | new WolframQuery().execute(hypothesis); 405 | 406 | } 407 | 408 | } else { 409 | 410 | /* Conversation context no longer applies. Reset vocabulary. */ 411 | this.mDynamicList = null; 412 | 413 | /* Error, say error response. See AndroidRecognition class. */ 414 | say(new Response().setReply(hypothesis)); 415 | 416 | } 417 | 418 | } 419 | 420 | private void say(Response response) { 421 | 422 | /* Create parameter with the text to be spoken. This is used to display subtitle. */ 423 | final HashMap params = new HashMap(); 424 | params.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, response.getReply()); 425 | 426 | mTTS.speak(response.getReply(), TextToSpeech.QUEUE_FLUSH, params); 427 | 428 | } 429 | 430 | /* This Runnable will clear any existing text off of the screen. */ 431 | private Runnable mTextViewRunnable = new Runnable() { 432 | @Override 433 | public void run() { 434 | 435 | if (mAnimatedTextView != null) { 436 | 437 | mAnimatedTextView.setText(" "); 438 | 439 | } 440 | 441 | } 442 | 443 | }; 444 | 445 | /* AsyncTask to get response from Wolfram API. */ 446 | private class WolframQuery extends AsyncTask { 447 | 448 | private static final String FORMAT = "plaintext"; 449 | 450 | @Override 451 | protected String doInBackground(String... params) { 452 | 453 | /* Go online to > http://products.wolframalpha.com/developers/ and sign up for an app key. 454 | * Once you have an app key, create a string resource with the id wolfram_key and the key for text. */ 455 | final WAEngine engine = new WAEngine(); 456 | engine.setAppID(getResources().getString(R.string.wolfram_key)); 457 | engine.addFormat(FORMAT); 458 | 459 | final WAQuery query = engine.createQuery(); 460 | query.setInput(params[0]); 461 | 462 | try { 463 | 464 | final WAQueryResult queryResult = engine.performQuery(query); 465 | 466 | if (queryResult.isError()) { 467 | 468 | return queryResult.getErrorMessage(); 469 | 470 | } else if (!queryResult.isSuccess()) { 471 | 472 | return Arrays.asList(getResources().getStringArray(R.array.wolfram_bad_query)).get(new Random() 473 | .nextInt(getResources().getStringArray(R.array.wolfram_bad_query).length)); 474 | 475 | } else { 476 | 477 | /* Very hacky way to get Wolfram API response. Needs optimization. */ 478 | for (int i = 0; i < queryResult.getNumPods(); i++) { 479 | 480 | if (i == 2) { 481 | 482 | final Object element = queryResult.getPods()[1].getSubpods()[0].getContents()[0]; 483 | 484 | return (((WAPlainText) element).getText()); 485 | 486 | } 487 | 488 | } 489 | 490 | } 491 | 492 | } catch (WAException exception) { 493 | 494 | Log.e(TAG, exception.toString()); 495 | 496 | } 497 | 498 | return Arrays.asList(getResources().getStringArray(R.array.wolfram_no_find)).get(new Random() 499 | .nextInt(getResources().getStringArray(R.array.wolfram_no_find).length)); 500 | 501 | } 502 | 503 | @Override 504 | protected void onPostExecute(String result) { 505 | 506 | say(new Response().setReply(result)); 507 | 508 | } 509 | 510 | } 511 | 512 | } 513 | 514 | 515 | --------------------------------------------------------------------------------