Tag: API

Stock market music in Max

Make music from the motion of stock prices.

This program gathers stock prices into a database. It generates Midi data – mapping price to pitch, and mapping trading volume to velocity and rhythmic density. It uses ancient Web technology: HTML/javascript front-end with a php back-end accessing a mysql database.

Case study: http://zproject.wikispaces.com/stock+market+music

To run this project, you will need a server (preferably linux) with the following capabilities:

  • mysql + phpmyadmin
  • php (and ability to run php over the web)
  • netcat (nc)
  • network access

All of this is pretty standard – so I won’t talk about it here. I am running it on Ubuntu Linux. There are many other ways to get the project working, by using the layout described here.



folder: stock-market




HTML/javascript web client
  • newstock3.html: (web page interface)
  • selectstock3.js:  (front-end)
php server
  • getstock3.php (back-end server to get quotes and save them to a database)
  • play3.php: (back-end server to retrieve quotes, analyze, and map to Midi sequence to send to Max)
  • udp.php: Osc library
Set execute privileges on php files so they can be run from your web server. (chmod +x)



The selectstock3.php program harvests stock quote data and stores it in a mysql database.

The database name is:  stocks – table is: quotes

Table structure:

The table is basic flat representation of a stock quote, indexed by the ticker symbol. It contains price, volume, high/low/change, timestamp, etc., For our purposes, the price, volume and timestamp are essentially all we need.

SQL to create the table:

 CREATE  TABLE  `stocks`.`quotes` (  `ticker` varchar( 12  )  NOT  NULL ,
 `price` decimal( 10, 2  )  NOT  NULL ,
 `qtime` datetime NOT  NULL ,
 `pchange` decimal( 10, 2  )  NOT  NULL ,
 `popen` decimal( 10, 2  )  NOT  NULL ,
 `phigh` decimal( 10, 2  )  NOT  NULL ,
 `plow` decimal( 10, 2  )  NOT  NULL ,
 `volume` int( 11  )  NOT  NULL ,
 `id` int( 11  )  NOT  NULL  AUTO_INCREMENT ,
 `spare` varchar( 30  )  DEFAULT NULL ,
 UNIQUE  KEY  `id` (  `id`  ) ,
 KEY  `ticker` (  `ticker`  )  ) ENGINE  =  MyISAM  DEFAULT CHARSET  = latin1 COMMENT  =  'stock quote transactions';

creating the database, user, and table

  • Log into  phpmyadmin as root
  • Create a new database called ‘stocks’
  • In privileges, add a user called: ‘webdb1′ with a password of ’34door’ (note you can change the password later)
  • In SQL, copy in the above query to create the ‘stocks’ table


Web client

The webpage control program allows you to select stocks by ticker symbol, and get either one quote or get quotes at regular time interval. Each quote is inserted into the stock table for later retrieval and analysis.


The web front end is quirky so I will describe it in terms of how you might typically use it:

market is open – and you just want to play music based on current stock prices
  1. Enter the ticker symbols for your stocks
  2. Press ‘tracking’ button – so the quotes get saved
  3. Enter the IP address of the computer running Max
  4. Press the ‘auto’ button in the upper left corner – it will run and play forever
market is closed – or you want to play historical data you have saved
  1. Enter the ticker symbols for your stocks
  2. Enter the IP address of the computer running Max
  3. set start end end dates
  4. Press the ‘play’ button to play once or press ‘loop’ to play continuously (using time interval in seconds)
  5. market is open – you just want to collect stock quote data
  6. Enter the ticker symbols for your stocks
  7. Press the tracking button so quotes will get saved
  8. Press the ‘get quotes’ button to get current quote or press ‘loop’ button (on the same line) to retrieve quotes  continuously every 30 seconds.


Max patch

  1. Make sure the IP address is set to the address of your server
  2. Select the Midi port for output
  3. Play a few test notes
  4. Select either ‘one instrument’ mode (piano) or multi instrument mode. Each time you click the multi instrument button it randomly selects a new combination 

notes on stock market data

To look at historical trends, you would need access to historical stock data. To use it as a tool for short term analysis, you would need access to real-time quote data in an API. At the time, both of these cost money.

However, it doesn’t cost money to get recent quotes from Yahoo throughout the day and store them in a database – so that’s the approach I took.

If I were to do this project today, I’d look for a free online source of historical data, in machine-readable form – because the historical data provides the most interesting and organic sounds when converted into music. The instant high speeding trading data would probably make interesting sounds as well, but you still need to pay for the data.

notes on local files

xively.com feed with Arduino

Bi-directional communication from Arduino to a xively.com feed using an ethernet shield.

  • Initializes an internet connection (DHCP)
  • Connects to xively.com servers every minute
  • Stores random value in the feed using HTTP PUT
  • Retrieves current feed value using HTTP GET
  • Lights up LED when transmitting
By the way, xively used to be cosm used to be pachube… 
Arduino circuit
  • Use an ethernet shield.
  • Connect ethernet cable. (I am using a Netgear WNCE2001 ethernet to wiFi adapter)
  • LED is connected to pin 5 and ground. The shorter lead connects to ground.


xively arduino test

  • xively_test1 (Arduino sketch)
Arduino files and libraries

Copy the xively_test1/ folder to Documents/Arduino. This puts it in the Arduino sketchbook.

Notes on installing xively/cosm/pachube libraries for arduino: http://reactivemusic.net/?p=4900


  1. Connect Arduino to Macbook via USB.
  2. Open the Arduino serial monitor to initialize the ethernet connection and display the IP address.
  3. Every minute data gets send to the feed
  4. Monitor feed data here: https://xively.com/feeds/98281/workbench
Arduino sketch

5/20/2014 - Arduino/xively feed interaction
Uses Ethernet Shield and and LED connected between pin D5 and ground
Sends a random value to a xively.com feed every minute
The LED lights up during data transmissions
HTTP PUT - send data to xiveyly feed and store
HTTP GET - read xively feed value
#include <SPI.h>
#include <Ethernet.h>
#include <HttpClient.h>
#include <Cosm.h>
int ledPin = 5;
int upCount = 0; // counters for number of times going up and down
#define API_KEY "96PqSh4rj7HzNif3WtTpN7GjX96SAKxrWms3SUhwaDFGUT0g" // your Cosm API key
#define FEED_ID 98281 // your Cosm feed ID
// MAC address for your Ethernet shield
byte mac[] = { 0x90, 0xA2, 0xDA, 0x0D, 0x0B, 0xCE };
// note that pins 0 and 1 are used by the Ethernet shield
unsigned long lastConnectionTime = 0; // last time we connected to Cosm
const unsigned long connectionInterval = 60000; // delay between connecting to Cosm in milliseconds
// Initialize the Cosm library
// Define the string for our datastream ID
char sensorId[] = "count";
CosmDatastream datastreams[] = {
 CosmDatastream(sensorId, strlen(sensorId), DATASTREAM_FLOAT),
// Wrap the datastream into a feed
CosmFeed feed(FEED_ID, datastreams, 1 /* number of datastreams */);
EthernetClient client;
CosmClient cosmclient(client);
void setup() {

 // initialize the detector pins 

 pinMode(ledPin, OUTPUT ); // internet transmitting indicator
 // start the Monitor (console) serial port

// display happy messages 

 Serial.println("Xively test");
// Keep trying to initialize the Internet connection
 // Note - we should eventually timeout of this and just run the stairs independently

 Serial.println("Initializing network");
 while (Ethernet.begin(mac) != 1) {
 Serial.println("Error getting IP address via DHCP, trying again...");
Serial.println("Network initialized");
 // print your local IP address:
 Serial.print("Arduino IP address: ");
 for (byte thisByte = 0; thisByte < 4; thisByte++) {
 // print the value of each byte of the IP address:
 Serial.print(Ethernet.localIP()[thisByte], DEC);

} // end of setup function
//////////////////////////// control loop ///////////////////////////
void loop() {
 // main program loop

 ////////////////////////////// Internet sending/receiving code ////////////////////////////////

 if (millis() - lastConnectionTime > connectionInterval) {

 // uncomment this to just send a random value...
 upCount = random(256);

 digitalWrite(ledPin, HIGH ); // turn on transmitter light
 // read the datastream back from Cosm - comment out to save time
 digitalWrite(ledPin, LOW );
 // update connection time so we wait before connecting again
 lastConnectionTime = millis();


 ///////////////////// end of internet send/receive code /////////////////

} // end of main loop code
/////////////////// additional functions //////////////////////////
// send the supplied value to Cosm, printing some debug information as we go
void sendData(int sensorValue) {
Serial.print("Read sensor value ");
Serial.println("Uploading to Cosm");
 int ret = cosmclient.put(feed, API_KEY);
 Serial.print("PUT return code: ");
// get the value of the datastream from Cosm, printing out the value we received
void getData() {
 Serial.println("Reading data from Cosm");
int ret = cosmclient.get(feed, API_KEY);
 Serial.print("GET return code: ");
if (ret > 0) {
 Serial.print("Datastream is: ");
Serial.print("Sensor value is: ");


Google Maps in Max

Draw points in Max by sending latitude and longitude to a Web client via Osc and web sockets.

Uses Ruby, WebSockets, Chrome, Google Maps API, Osc, Max, Jquery, and Node.js… But the Max patch is actually quite simple.

Based on this geocoding tutorial:  http://www.sitepoint.com/google-maps-api-jquery/



folder: google-maps


main Max patch
  • googlemaptest.maxpat
html and javascript for Google API
  • js/ (folder containing javascript code for map client)
  • markers.html (web client)
  • mapserver.rb (Osc and Websockets server)
node.js (optional)
  • nodeserver.js (local node.js webserver)

running node.js local web server (optional)

To run the project locally, you will either need to install node.js or have a local web server. The instructions assume that you have installed node, as well as the http package.

If you don’t want to bother with node, there is also an online version of the web client running at  http://zerokidz.com/gmap/markers/markers.html

intalling ruby gems

Running Ruby 2.0 as well as the following gems:

  • osc-ruby
  • em-websocket
  • json


1. If you are using the online Web client, go to this URL in a Google Chrome browser: http://zerokidz.com/gmap/markers/markers.html then skip to step 4.

2. In a terminal window start the node webserver

node nodeserver

3. Launch a Google Chrome web browser and type in this URL

4. In another terminal window start the ruby server for Osc and websockets

ruby mapserver.rb

5. Now in the Web Client (Chrome) press the “OSC” button underneath the map – to open the web sockets connection with the ruby server.

6. Open the Max patch:


7. Now you should be able to click on the message boxes for Bethel and Rumford in the Max patch to add location markers to the map in the browser.


Google speech API v2


update 5/17/2014: The key in the post below is now disabled. Trying this one: AIzaSyBOti4mM-6x9WDnZIjIeyEU21OpBXqWBgw 

It worked for now – but I will probably need to get a real key… and add instructions for inserting the key into the patches: robot_conversation5.maxpat and speech-to-google-text-api5.maxpat

Basic instructions are

  • edit Max patch
  • go into the sub patch: call-google-speech and replace the key string inside the curl command with the correct key.
earlier post

v1 API of Google Speech broke a few days ago.

Here is an example of how to run v2. https://github.com/gillesdemey/google-speech-v2

I have updated the Max patches in the Internet Sensors project. https://github.com/tkzic/internet-sensors

This version of the API produces malformed JSON responses.

Here’s an example using curl:

curl -v -i -X POST -H “Content-Type:audio/x-flac; rate=16000” -T /tmp/tweet.flac “https://www.google.com/speech-api/v2/recognize?xjerr=1&client=chromium&lang=en-US&maxresults=10&pfilter=0&xjerr=1&key=AIzaSyCnl6MRydhw_5fLXIdASxkLJzcJh5iX0M4”

Instructions for getting a real key…  http://www.chromium.org/developers/how-tos/api-keys

Note: Need to look at the double buffering methods in the Max patches to make sure they are handling various sample rates properly. I think they may be optimized for 44.1 KHz



Searching by image

Using Google’s search by image feature to return similar images


With Google you can search by image. But it gets really interesting when you upload an image that is not available on the internet and look at the set of similar images returned. Or if you use a common image but just view the visually similar results. For example, here is a protein molecule (http://www.kurzweilai.net/images/ferritin.jpg)

Here are similar image results returned by Google.

You can also restrict the results to faces:

A few internet images to try:

Camera images (not on the Internet until they were posted here) These will give more interesting results. For example, the woman with the flower (using face matching) returns images of Erik Prince and Brad Pitt.

More conversations with robots in Max

Using Google speech API and Pandorabots API

This project is an extension to the speech-to-text project: http://reactivemusic.net/?p=4690 You might want to try running that project first to get the Google speech API running.


  • Everything runs in one Max patch
  • voice auto detect mode
  • menu selection of chat bots and voices
  • filtering of non speakable text (like HTML tags)
  • python script now runs under current directory of patch using relative path
  • refinements to recording and chatbot engines



folder: google-speech

main Max patch
  • robot-conversation5.maxpat
abstractions and other files
  • clean-html.js
  • xml2json/xml2json.py
  • JSON-google-speech.js
  • JSON-pandorabot.js
  • autorecord-buffer2.maxpat
  • auto-record-switch.maxpat
  • pandorabots.txt
Max external objects
external programs:

sox: sox audio conversion program must be in the computer’s executable file path, ie., /usr/bin – or you can rewrite the [sprintf] input to [aka.shell] with the actual path

get sox from: http://sox.sourceforge.net


  • Open robot-converstaion.maxpat and turn on audio
  • select chatbot as destination
  • For manual record (push to talk) use the toggle 
  • For auto-record: press the + key to activate voice sensor (press – key to deactivate)
  • ask a question


The goal of this update is to get 2 or more chatbots conversing via speech through the air. This prototype is almost there, but has encountered an unexpected setback: The Google speech API is not very good at decoding synthesized speech from the built in speech synthesizer in Mac OS. Its not bad, but really only works well with the default male voice: Alex.

One idea would be to pitch shift the male voice to make it sound female. This would require some changes to audio-routing. Currently the speech output happens via the operating system. So it would need to be piped back into Max – which isn’t such a bad idea anyway – because then would could possible run two instances  on the same computer and just route the audio internally.

You may be wondering, why I didn’t just connect the two chat bots via text, skipping the speech recognition? Well, its more interesting to have devices speaking through the air.

Another thing that needs fixing: Currently the API call to Google speech causes ‘blocking’ in Max. It would be better to have the call happen using a background process that sends a message back to Max when the processing is completed. This way we could timeout if there is a bad internet connection or other network error. This could be done using a shell script.

revision history

  • 4/24/2016: need to have explicit path to sox, in the call-google-speech subpatch. In my Macports version the path is /usr/local/opt/bin/sox.
  • 6/6/2014: re-added missing pandorabots.txt (list of chatbots) – also noticed that pandorabots.com was not available. May need to look for another site.
  • 5/11/2014: The newest version requires Max 6.1.7 (for JSON parsing). Also have updated to Google Speech API v2.
  • Note: Instructions for getting a real key from Google – which will need to be inserted into the patch.  http://www.chromium.org/developers/how-tos/api-keys – so far we have been getting by with common keys from a github site (see notes in next link)

Also please see these notes about how to modify the patch with your key – until this gets resolved: http://reactivemusic.net/?p=11035