Paso 8: ¿Cansado de hacer la gota beat? Vamos a añadir más sensores y sonidos.
La alegría de hacer un tambor bajo del retroceso de una prensa del botón físico se desvanece muy rápidamente, así que vamos a hacer un poco más interesante. Podemos usar Cylon para interfaz con una gran cantidad de diferentes sensores y dispositivos. Hasta ahora, hemos pegado al uso de un evento de pulsar botón único para el tambor del retroceso. Ahora, vamos a refactorizar el código app.js para hacerla un poco más fácil de añadir más botones, así como añadir algunos sensores analógicos.
Antes de realizar cambios en el código, vamos a añadir unos cuantos más dispositivos para trabajar con.
Para este instructable, opté por conectar un total de 4 botones, un potenciómetro y una célula fotoeléctrica. Esto nos permitirá controlar más parámetros de instrumentos de Timbre.js con una amplia gama de valores, en lugar de sólo los valores de encendido y apagado de nuestros botones.
Ahora, vamos a refactorizar el código para apoyar a más dispositivos. Queremos ser capaces de permitir a nuestros clientes a escuchar pulsar un botón específico y las lecturas de sensor específico, en lugar de utilizar un evento genérico 'botón'. Vamos a enviar dos eventos diferentes, 'botón' y 'sensor', con un hash que contenga un id de nuestro dispositivo, así como el valor que queremos enviar. Para los botones, esperamos un 0 o un 1 y para sensores analógicos, que esperamos sea cual sea el valor de que la lectura del sensor es. Realice los cambios siguientes al archivo 'app.js'.
var express = require('express') var app = express() var server = require('http').Server(app) var cylon = require('cylon') var io = require('socket.io')(server) app.use(express.static(__dirname + '/public')) server.listen(8080) var socket = io .of('/soundsocket') .on('connection', function (socket) { console.log('client connected') }) var cylonReady = function(my) { io .of('/soundsocket') .on('connection', function (socket) { registerSocketHandlers(my, socket); }) } // this will be called each time a socket is opened, so each client will receive their own events when buttons are pushed. var registerSocketHandlers = function(my, socket) { buttons = [my.button0, my.button1, my.button2, my.button3] for (var i = 0; i < buttons.length; i++) { var button = buttons[i]; registerButtonHandler(socket, button, i); } analogSensors = [my.potentiometer, my.photocell] for (var i = 0; i < analogSensors.length; i++) { var sensor = analogSensors[i]; registerAnalogSensorHandler(socket, sensor, i); } } // convenience for button specific events var registerButtonHandler = function(socket, button, buttonID) { // on push, send button ID and value of 1 button.on('push', function() { socket.emit('button', { 'id': buttonID, 'value': 1 }) }) // on push, send button ID and value of 0 button.on('release', function() { socket.emit('button', { 'id': buttonID, 'value': 0 }) }) } // convenience for listening to analog read events and emitting sensor data messages var registerAnalogSensorHandler = function(socket, analogSensor, analogSensorID) { // on new data, send sensor ID and value of sensor reading analogSensor.on('analogRead', function() { // ask the sensor for it's value sensorValue = analogSensor.analogRead() socket.emit('sensor', { 'id': analogSensorID, 'value': sensorValue }) }) } // tell Cylon which devices we will be interfacing with var getDevices = function() { return { button0: { driver: 'button', pin: 2 }, button1: { driver: 'button', pin: 3 }, button2: { driver: 'button', pin: 4 }, button3: { driver: 'button', pin: 5 }, potentiometer: { driver: 'analogSensor', pin: 0, lowerLimit: 100, upperLimit: 900 }, photocell: { driver: 'analogSensor', pin: 1, lowerLimit: 100, upperLimit: 900 } } } // tell Cylon how we will be connecting to our devices var getConnections = function() { return { edison: { adaptor: 'intel-iot' } } } cylon.robot({ connections: getConnections(), // use our getConnections function to clean this up. devices: getDevices() // use our getDevices function to clean this up. }).on('ready', cylonReady) cylon.start()
Nota: Asegúrese de que sus sensores analógicos están conectados a los puertos Analógicos en de su Edison.
Para consumir estos cambios en nuestro cliente, realice los siguientes cambios para que su 'playsounds.js' refleja el siguiente código:
var BD var SD var HH1 var HH2 var CYM var drum var lead var env var arp var delay var inv T("audio").load("./drumkit.wav", function() { BD = this.slice( 0, 500).set({bang:false}) SD = this.slice( 500, 1000).set({bang:false}) HH1 = this.slice(1000, 1500).set({bang:false, mul:0.2}) HH2 = this.slice(1500, 2000).set({bang:false, mul:0.2}) CYM = this.slice(2000).set({bang:false, mul:0.2}) var scale = new sc.Scale([0,1,3,7,8], 12, "Pelog") var P1 = [ [BD, HH1], [HH1], [HH2], [], [BD, SD, HH1], [HH1], [HH2], [SD], ].wrapExtend(128) var P2 = sc.series(16) drum = T("lowshelf", {freq:110, gain:8, mul:0.6}, BD, SD, HH1, HH2, CYM).play() lead = T("saw", {freq:T("param")}) env = T("perc", {r:100}) arp = T("OscGen", {wave:"sin(15)", env:env, mul:0.5}) delay = T("delay", {time:"BPM128 L4", fb:0.65, mix:0.35}, T("pan", {pos:T("tri", {freq:"BPM64 L1", mul:0.8}).kr()}, arp) ).play(); inv = T("interval", {interval:"BPM128 L16"}, function(count) { var i = count % P1.length if (i === 0) CYM.bang() P1[i].forEach(function(p) { p.bang() }) if (Math.random() < 0.015) { var j = (Math.random() * P1.length)|0; P1.wrapSwap(i, j); P2.wrapSwap(i, j); } var noteNum = scale.wrapAt(P2.wrapAt(count)) + 60; if (i % 2 === 0) { lead.freq.linTo(noteNum.midicps() * 2, "100ms"); } arp.noteOn(noteNum + 24, 60) }).start() }) var socketConnection = io.connect('http://0.0.0.0:8080/soundsocket') socketConnection.on('connect', function () { console.log('connected to socket') }) socketConnection.on('button', function (data) { buttonID = data['id'] value = data['value'] switch (buttonID) { case 0: if (value == 1) { BD.bang() } break; case 1: if (value == 1) { CYM.bang() } break; case 2: value == 1 ? HH1.set({mul: 1}) : HH1.set({mul: 0.2}) break; case 3: value == 1 ? HH2.set({mul: 1}) : HH2.set({mul: 0.2}) break; default: } }) socketConnection.on('sensor', function (data) { sensorID = data['id'] value = data['value'] switch (sensorID) { case 0: inv.set({interval: value * 2 }) break; case 1: arp.set({mul: 10 / value }) break; default: } })
Cometer y empujar los cambios, tire abajo a Edisony ejecutar su aplicación. Si todo funciona como se esperaba, debe ser capaz de usar los botones para golpear instrumentos específicos y poder modificar el retraso y nota compensa usando sus sensores analógicos.
Esto le da un ejemplo de cómo consumir diferentes tipos de datos y cualquier juego o modificar los parámetros de los instrumentos dentro de Timbre.js.