From de7ae02da0744c9302fe2fc18e33b98de0f13ecd Mon Sep 17 00:00:00 2001 From: Phyks Date: Thu, 5 Dec 2013 12:11:16 +0100 Subject: [PATCH] Working on chorizo player, will be there soon --- .gitignore | 1 + 3d_view.py => 3D_view/3d_view.py | 0 Chorizo player/LICENSE.md | 28 ++++++++ Chorizo player/chorizo.py | 115 +++++++++++++++++++++++++++++++ Chorizo player/sound4python.py | 94 +++++++++++++++++++++++++ Chorizo player/test_player.py | 100 +++++++++++++++++++++++++++ color.py => Color/color.py | 4 +- README.md | 6 +- chorizo.py | 61 ---------------- 9 files changed, 343 insertions(+), 66 deletions(-) create mode 100644 .gitignore rename 3d_view.py => 3D_view/3d_view.py (100%) create mode 100644 Chorizo player/LICENSE.md create mode 100755 Chorizo player/chorizo.py create mode 100644 Chorizo player/sound4python.py create mode 100755 Chorizo player/test_player.py rename color.py => Color/color.py (97%) delete mode 100755 chorizo.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ff1b3ea --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*__pycache* diff --git a/3d_view.py b/3D_view/3d_view.py similarity index 100% rename from 3d_view.py rename to 3D_view/3d_view.py diff --git a/Chorizo player/LICENSE.md b/Chorizo player/LICENSE.md new file mode 100644 index 0000000..46827dd --- /dev/null +++ b/Chorizo player/LICENSE.md @@ -0,0 +1,28 @@ +Chorizo.py : +==== + +This script allows you to play music using a chorizo (or whatever food you +like) keyboard ! See README for more info and links. + +As all the other scripts in this repository, I release it under a very +permissive license. To make a long story short : do whatever you want with +this script (but try to have fun :), I don't mind. It would be cool to quote +the origin of the script if you reuse it, but you don't have to. I'd like to +be noticed of what you did cool with it (if you think it's worth). :) +Ah, I almost forgot : If by chance we ever meet and you think this script is +worth, you can buy me a soda :) + + Phyks + + +Sound4Python.py : https://github.com/standarddeviant/sound4python +==== +Note : The version of sound4python.py in this project is slightly modified (some print statements commented and a bugfix). + +Copyright (C) 2013 dave.crist@gmail.com + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/Chorizo player/chorizo.py b/Chorizo player/chorizo.py new file mode 100755 index 0000000..9ebbfa6 --- /dev/null +++ b/Chorizo player/chorizo.py @@ -0,0 +1,115 @@ +#!/usr/bin/env python3 + +# ============================================================================ +# This script allows you to play music using a chorizo (or whatever food you +# like) keyboard ! See README for more info and links. + +# As all the other scripts in this repository, I release it under a very +# permissive license. To make a long story short : do whatever you want with +# this script (but try to have fun :), I don't mind. It would be cool to quote +# the origin of the script if you reuse it, but you don't have to. I'd like to +# be noticed of what you did cool with it (if you think it's worth). :) +# Ah, I almost forgot : If by chance we ever meet and you think this script is +# worth, you can buy me a soda :) +# +# Phyks +# ============================================================================= + +from sound4python import sound +from multiprocessing oimport Process +import math +import serial +import sys +import getopt + + +def play_wave(frequency=440, nb_secs=1): + sine_wave = [] + for i in range(nb_secs*framerate): + sine_wave.append(int(16384*math.sin(2*math.pi*frequency*i/framerate))) + + sound(sine_wave) + +serial_port = "/dev/ttyACM0" + +try: + opts, args = getopt.getopt(sys.argv[1:], "hs:", ["help", "serial="]) + + for opt, arg in opts: + if opt in ("-h", "--help"): + print("Play music with chorizos !") + print("\nUsage : "+sys.argv[0]+" [OPTIONS]") + print("\nOptions :") + print("\t-h (--help) \t display this help message") + print("\t-s (--serial=) \t change serial port (default is " + + "/dev/tty/ACM0") + sys.exit(0) + elif opt in ("-s", "--serial"): + serial_port = arg +except getopt.GetoptError: + pass + +try: + ser = serial.Serial(serial_port, 115200) +except Exception as e: + sys.exit("Error with serial port :"+str(e)) + +framerate = 16000 +frequencies = [440, 600, 880] +gap_times = 10 +p = [] +for i in range(3): + processes.append(Process(target=play_wave, args=(frequencies[i], 1))) + processes[-1].start() + +try: + ser.open() +except Exception as e: + sys.exit("Error while opening serial port : "+str(e)) + +if ser.isOpen(): + try: + ser.flushInput() + ser.flushOutput() + + measures = [[], [], []] + + print("Calibration :") + print("Touch each chorizo electrodes before starting") + + for i in range(10000): + line = ser.readline() + line = line.decode().strip("\r\n") + line = line.split(" ") + line = [int(j or 0) for j in line] + + for j in range(3): + measures[j][i] = line[j] + + + # Get threshold + threshold = [-1, -1, -1] + for i in range(3): + gap = -1 + for j in range(1, 10000): + if measures[i][j] - measures[i][j-1] > gap: + gap = measures[i][j] - measures[i][j-1] + threshold[i] = (measures[i][j]+measures[i][j-1])/2 + + print("Running...") + running = True + while running: + # Read line from serial + line = ser.readline() + line = line.decode().strip("\r\n") + line = line.split(" ") + line = [int(j or 0) for j in line] + + for i in range(3): + if line[i] > threshold[i]: + print("Playing") + p[i].start() + + p[i].join() + except Exception as e: + sys.exit("Error while handling data from serial : "+str(e)) diff --git a/Chorizo player/sound4python.py b/Chorizo player/sound4python.py new file mode 100644 index 0000000..57f9112 --- /dev/null +++ b/Chorizo player/sound4python.py @@ -0,0 +1,94 @@ + +try: import tempfile, wave, subprocess, os, signal, struct +except: + print("E: sound4python is unable to import a combination of %s"% + ("tempfile, wave, subprocess, os, signal, struct")) + +FNULL = open(os.devnull,'w') +def launchWithoutConsole(args,output=False): + """Launches args windowless and waits until finished""" + startupinfo = None + if( 'STARTUPINFO' in dir(subprocess) ): + startupinfo = subprocess.STARTUPINFO() + startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW + if( output ): + return subprocess.Popen(args, stdin=subprocess.PIPE, + stdout=subprocess.PIPE,startupinfo=startupinfo, ) + else: + return subprocess.Popen(args, stdin=subprocess.PIPE, + stdout=FNULL ,stderr=FNULL, startupinfo=startupinfo) + +# sound4python +def sound(itr,samprate=16000,autoscale=True,output=False): + try: import numpy as np; foundNumpy=True; + except: + foundNumpy=False; + + #for now, assume 1-D iterable + mult = 1 + if( autoscale ): + mult = 32767.0 / max(itr) + #mult = 128.0 / max(itr) + + #create file in memory + #with tempfile.SpooledTemporaryFile() as memFile: + memFile = tempfile.SpooledTemporaryFile() + + #create wave write objection pointing to memFile + waveWrite = wave.open(memFile,'wb') + waveWrite.setsampwidth(2) # int16 default + waveWrite.setnchannels(1) # mono default + waveWrite.setframerate(samprate) # 8kHz default + + wroteFrames=False + #Let's try to create sound from NumPy vector + if( foundNumpy ): + if( type(itr)==np.array ): + if( itr.ndim == 1 or itr.shape.count(1) == itr.ndim - 1 ): + waveWrite.writeframes( (mult*itr.flatten()).astype(np.int16).tostring() ) + wroteFrames=True + else: #we have np, but the iterable isn't a vector + waveWrite.writeframes( (mult*np.array(itr)).astype(np.int16).tostring() ) + wroteFrames=True + if( not wroteFrames and not foundNumpy ): + #python w/o np doesn't have "short"/"int16", "@h" is "native,aligned short" + waveWrite.writeframes( struct.pack(len(itr)*"@h",[int(mult*itm) for itm in itr]) ) + wroteFrames=True + + if( not wroteFrames ): + print("E: Unable to create sound. Only 1D numpy arrays and numerical lists are supported.") + waveWrite.close() + return None + + #configure the file object, memFile, as if it has just been opened for reading + memFile.seek(0) + + try: + # getting here means wroteFrames == True + #print("\nAttempting to play a mono audio stream of length") + #print(" %.2f seconds (%.3f thousand samples at sample rate of %.3f kHz)"% + # ( 1.0*len(itr)/samprate , len(itr)/1000. , int(samprate)/1000.) ) + p=launchWithoutConsole(['sox','-','-d']) + except: + print("E: Unable to launch sox.") + print("E: Please ensure that sox is installed and on the path.") + print("E: Try 'sox -h' to test sox installation.") + waveWrite.close() + return None + + try: + p.communicate(memFile.read()) + p.wait() + except: + print("E: Unable to send in-memory wave file to stdin of sox subprocess.") + waveWrite.close() + return None + #os.kill(p.pid,signal.CTRL_C_EVENT) +#end def sound(itr,samprate=8000,autoscale=True) + + + + + + + diff --git a/Chorizo player/test_player.py b/Chorizo player/test_player.py new file mode 100755 index 0000000..4eacbe0 --- /dev/null +++ b/Chorizo player/test_player.py @@ -0,0 +1,100 @@ +#!/usr/bin/env python3 + +# ============================================================================ +# This script allows you to play music using a chorizo (or whatever food you +# like) keyboard ! See README for more info and links. + +# As all the other scripts in this repository, I release it under a very +# permissive license. To make a long story short : do whatever you want with +# this script (but try to have fun :), I don't mind. It would be cool to quote +# the origin of the script if you reuse it, but you don't have to. I'd like to +# be noticed of what you did cool with it (if you think it's worth). :) +# Ah, I almost forgot : If by chance we ever meet and you think this script is +# worth, you can buy me a soda :) +# +# Phyks +# ============================================================================= + +from sound4python import sound +from multiprocessing import Process +import math + +class _Getch: + """Gets a single character from standard input. Does not echo to the +screen.""" + def __init__(self): + try: + self.impl = _GetchWindows() + except ImportError: + self.impl = _GetchUnix() + + def __call__(self): return self.impl() + + +class _GetchUnix: + def __init__(self): + import tty, sys + + def __call__(self): + import sys, tty, termios + fd = sys.stdin.fileno() + old_settings = termios.tcgetattr(fd) + try: + tty.setraw(sys.stdin.fileno()) + ch = sys.stdin.read(1) + finally: + termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) + return ch + + +class _GetchWindows: + def __init__(self): + import msvcrt + + def __call__(self): + import msvcrt + return msvcrt.getch() + +getch = _Getch() + +def play_wave(frequency=440, nb_secs=1.): + sine_wave = [] + for i in range(math.ceil(nb_secs*framerate)+1): + sine_wave.append(int(16384*math.sin(2*math.pi*frequency*i/framerate))) + + sound(sine_wave) + +framerate = 16000 +processes = [] +frequency = 440 + +print("Running... Press q to quit.") +running = True +while running: + char = getch() + if char == "q": + running = False + continue + elif char == "a": + frequency = 440 + elif char == "b": + frequency = 493.88 + elif char == "c": + frequency = 523.25 + elif char == "d": + frequency = 587.33 + elif char == "e": + frequency = 659.26 + elif char == "f": + frequency = 698.46 + elif char == "g": + frequency = 783.99 + else: + continue + + print("Playing "+char.upper()) + processes.append(Process(target=play_wave, args=(frequency, 0.2))) + processes[-1].start() + +for i in processes: + i.join() diff --git a/color.py b/Color/color.py similarity index 97% rename from color.py rename to Color/color.py index 1b45bc4..645c767 100755 --- a/color.py +++ b/Color/color.py @@ -46,14 +46,14 @@ try: opts, args = getopt.getopt(sys.argv[1:], "hs:", ["help", "serial="]) for opt, arg in opts: - if opt == "-h" or opt == "-help": + if opt in ("-h", "--help"): print("Touchless 3D tracking with color mapping") print("\nUsage : "+sys.argv[0]+" [OPTIONS]") print("\nTrack the position of your hand in 3D and map it " + "in RGB space") print("\nOptions :") print("\t-h (--help) \t display this help message") - print("\t-s (--serial) \t change serial port (default is " + + print("\t-s (--serial=) \t change serial port (default is " + "/dev/tty/ACM0") sys.exit(0) elif opt in ("-s", "--serial"): diff --git a/README.md b/README.md index 5703414..2cee173 100644 --- a/README.md +++ b/README.md @@ -25,17 +25,17 @@ where `value1`, `value2`, `value3` are values directly proportional to the disch You'll also find some basic Python scripts as examples on possible use of the setup. -### Colors.py +### Colors This script represents the position of your hand in the RGB space. You can then pick a color by placing your hand at a specific point in the box of the electrodes. -### Chorizo.py +### Chorizo player __(working on)__ A script to play music using chorizo pads as a musical keyboard. See [our website](http://hackens.org/Projets/TouchlessTracking) (french, but vids are available, more coming soon) for infos and demos. -### 3d_view.py +### 3d view __(working on)__ diff --git a/chorizo.py b/chorizo.py deleted file mode 100755 index a693590..0000000 --- a/chorizo.py +++ /dev/null @@ -1,61 +0,0 @@ -#!/usr/bin/env python3 - -# ============================================================================ -# This script allows you to play music using a chorizo (or whatever food you -# like) keyboard ! See README for more info and links. - -# As all the other scripts in this repository, I release it under a very -# permissive license. To make a long story short : do whatever you want with -# this script (but try to have fun :), I don't mind. It would be cool to quote -# the origin of the script if you reuse it, but you don't have to. I'd like to -# be noticed of what you did cool with it (if you think it's worth). :) -# Ah, I almost forgot : If by chance we ever meet and you think this script is -# worth, you can buy me a soda :) -# -# Phyks -# ============================================================================= - -# TODO - -import wave -import math -import pyaudio -import sys - - -# Params -# ====== -nChannels = 1 # Mono -sample_size = 2 # Size of a sample -> 1 = 8 bits -framerate = 44100 # Sampling frequency -length = 2 # Length in seconds -frequency = 440 -level = 1 -# ===== - -if level < 0.0 or level > 1.0: - sys.exit(1) - -filename = "temp" -w = wave.open(filename, 'w') - -# Computed params -nFrames = int(length*framerate) -max_amplitude = int(2**(sample_size * 8 - 1) - 1) -amplitude = max_amplitude*level - -w.setparams((nChannels, sample_size, framerate, nFrames, - 'NONE', 'not compressed')) - -sine_wave = [] -for i in range(min(framerate, nFrames)): - sine_wave.append(int(max_amplitude + - amplitude*math.sin(2*math.pi*frequency*i/framerate))) - -for i in range(nFrames): - sine_wave = int(amplitude*math.sin(2*math.pi*frequency*i/framerate)) - data = wave.struct.pack('h', sine_wave) - # ^ h is for "short" so each value can go from -2**15 to 2**15 - w.writeframesraw(data) - -w.close()