Showing posts with label linux. Show all posts
Showing posts with label linux. Show all posts

Tuesday, November 3, 2015

Sooperlooper


I have been playing with SooperLooper, a pretty cool and simple looper application for Linux and Mac. I use ubuntu and installation there is simple, it is available in the software center if you use the gui or with sudo apt-get install sooperlooper if you are more hardcore. The application has 2 parts, a GUI and the server which communicate with each other using OSC, the standard aiming to replace Midi. You can run the server only with the command sooperlooper and you can run the GUI+server with slgui

My first tests were done with the GUI. You need to run slgui and make the right connections in JACK. I use QJackCtl and this are the connections I set up for redirecting audio input to slgui capture and its output to the audio output:



I am interested on running the looper headless, so I have been investigating on how to send OSC commands from the command line. I installed pyliblo-utils: sudo apt-get install pyliblo-utils which adds a tool called send_osc and dump_osc to send and receive OSC messages. Once I did that I opened 3 terminals running the following things on each:

Terminal 1: run sooperlooper (without GUI), this is listening OSC commands in port 9951 by default
Terminal 2: run dump_osc to receive OSC commands at port 9952: dump_osc 9952
Terminal 3: this is where things get interesting:
First I run the ping command:
send_osc 9951 /ping osc.udp://localhost:9952/ osc.udp://localhost:9952/
And I get the following in Terminal 2:
osc.udp://localhost:9952/ ,ssi osc.udp://berenjena:9951/ 1.7.0 1

then I tried other commands from the sooperlooper documentation here.

Next steps: I might think of a way to interface sooperlooper from a keyboard by converting keystrokes to OSC. I also would like to see how it performs in a raspberry pi (EDIT: Seems like someone has been having similar thoughts: http://journeytounknownsoundscapes.blogspot.com.es/2015/05/a-new-looper-in-town-raspberry-pi-with.html?view=classic )

Sunday, November 23, 2014

Reverse engineering SJCAM4000 WiFi... well... kind of...



I got a so called Action Camera. Not enought interest on the whole video recording thing to spend a fortune on a GoPro, so I went for the Chinese option and bought a SJCAM 4000 wifi. I will not get into the whole review and unboxing discussion... that is not the purpose of this blog, you have plenty of pages already discussing that... google that...

Anyway, so the camera comes with a wifi option. How does that work? the camera opens an access point, you connect with an iOS or android app and you can configure and control the camera, see live video or download recordings (videos or pictured). That is great feature, I think, it saves you from having to extract the microSD card from the camera, which makes things easier, but I prefer using my laptop for downloading the pictures and videos than my handset, but no ubuntu app... grrrr.... had to figure out the protocol used in wifi mode...

I connected my laptop to the camera access point. I had to figure out the ip address of the camera:

arp -a

? (192.168.1.254) at 18:83:bf:XX:XX:XX [ether] on wlan0

Then I tried connecting to that IP with telnet and ssh without luck. Lastly I tried opening the webpage in that IP: http://192.168.1.254

Bingo!!! It opened a web page that allowed browsing the microSD card contents and even upload files, but no sight of configuration options or live video feed. That was really all I was looking for, but hell... I want the whole enchilada.

I installed an app in my android phone that allows capturing tcpdumps. I used tPacketCapture, which does not require having a rooted phone. If your phone is rooted, you can just use tcpdump from busybox. I captured a connection with my android app while I was doing some changes in the settings to inspect it later in the PC with wireshark. So here is what I found:

  1. The live video feed is just an RTSP connection, pretty standard and no encryption... cool... So connected the laptop back to the camera, opened VLC, Media->Open Network Stream and typed the URL I saw in my tcpdump pcap file: rtsp://192.168.1.254/sjcam.mov voila... live video from the camera
  2. The pcap file also had some http get requests. I could distinguish 2 types of requests, those with a parameter and those with out it. I assume the ones with no parameter are requests for info and the ones with a parameter are request for changing a setting. See the examples bellow (red is from laptop to camera, blue is from camera to laptop)
with no parameter:


GET /?custom=1&cmd=3016 HTTP/1.1
accept: */*
User-Agent: Dalvik/1.6.0 (Linux; U; Android 4.4.2; -------------------)
Host: 192.168.1.254
Connection: Keep-Alive
Accept-Encoding: gzip

HTTP/1.1 200 OK
Server: eCos/1.0
Cache-Control:no-store, no-cache, must-revalidate
Pragma: no-cache
Accept-Ranges: bytes
Content-length: 97
Content-type: text/xml
Connection: close

<?xml version="1.0" encoding="UTF-8" ?>
<Function>
<Cmd>3016</Cmd>
<Status>1</Status>
</Function>


with no parameter:


GET /?custom=1&cmd=2002&par=2 HTTP/1.1
accept: */*
User-Agent: Dalvik/1.6.0 (Linux; U; Android 4.4.2; -------------------)
Host: 192.168.1.254
Connection: Keep-Alive
Accept-Encoding: gzip

HTTP/1.1 200 OK
Server: eCos/1.0
Cache-Control:no-store, no-cache, must-revalidate
Pragma: no-cache
Accept-Ranges: bytes
Content-length: 97
Content-type: text/xml
Connection: close

<?xml version="1.0" encoding="UTF-8" ?>
<Function>
<Cmd>2002</Cmd>
<Status>0</Status>
</Function>

I did not dig further in this, but assume that each of those cmd numbers corresponds to a camera setting, and the parameter (par) is the value to set, but still need more research to figure out cmd and settings. I hope to make a python library to control the camera at some point, but will need some time to reverse engineer all the commands and parameters.

The last thing I did on my first weekend with the camera was try to watch the videos in my laptop and mobile phone, well, turns out the format of the recorded videos is ".MOV" and both mobile and laptop had issues with that format, even VLC which seems to play everything had issues: video was stopping and audio was out of sync. I converted the ".MOV" file to ".mp4" using avconv:

avconv -i input_file.MOV -vcodec copy -acodec mp2 output_file.mp4

What this does is generate an mp4 file in which the video is unaltered (no reencoding done, the video from the mov, h.264 compressed, is extracted and muxed again in the mp4 file without alteration). I have never been too keen on recompressing the video if it is already good from the source, unless there is a real need for it (reduce the file size). The audio gets compressed in mpeg layer 2 (audio codec in the ".MOV" is lpcm, no compression). Mp4 file format does not seem to support lpcm audio and I don't mind that much compressing the audio.

Sunday, November 9, 2014

cloudClock: Force an update of the time with ntpd

Whenever ntpd detects a huge different between the system time and the ntp server time, ntpd does not update the time. I should investigate further on why it behaves that way, but I think I know enough about ntpd already for now... so I investigated how to make ntpd to force the update and this is what I found:

To force a large NTP update, do the following:

Stop the NTPD service ("/etc/init.c/ntpd stop")
run ntpd -gnqd
Start the NTPD service again ("/etc/init.c/ntpd start")

Monday, October 20, 2014

cloudClock: ntpstat

In order to find out the status of the ntp daemon, there seem to be 2 possibilities: ntpq or ntpstat.
ntpq is by far the one that gives more info, really too much for my liking, I just want to find out if the clock is in sync or not!! so I went for ntpstat. As I mentioned in my previous post, there is no ntpstat in linino, so I had to compile it from scratch. This was easy. I got the ntpstat source code from here and compiled it doing:

../linino/trunk/staging_dir/toolchain-mips_r2_gcc-4.6-linaro_uClibc-0.9.33.2/bin/mips-openwrt-linux-gcc ntpstat.c -o ntpstat

Monday, October 13, 2014

cloudClock: time keeping

Part of the beauty of the cloudClock is the fact that it does not require having to set the time. For that I will use ntpd to keep linino on time. Turns out that linino already has ntpd running:

/usr/sbin/ntpd -n -p 0.openwrt.pool.ntp.org -p 1.openwrt.pool.ntp.org -p 2.openwrt.pool.ntp.org -p 3.open

First thing I thought was that I would like to change the ntp servers being used and select some servers near by. I also wanted to the change the timezone to my current timezone. Turns out both those things are configured in the same file: /etc/config/system

Original file was:

config system
option hostname 'Arduino'
option timezone 'UTC'
option timezone_desc 'Rest of the World (UTC)'

config timeserver 'ntp'
list server '0.openwrt.pool.ntp.org'
list server '1.openwrt.pool.ntp.org'
list server '2.openwrt.pool.ntp.org'
list server '3.openwrt.pool.ntp.org'
option enable_server '0'

And my modified file is now:

config system
option hostname 'Arduino'
option timezone 'CET-1CEST,M3.5.0,M10.5.0/3'
option timezone_desc 'Europe/Madrid'

config timeserver 'ntp'
list server '0.pool.ntp.org'
list server '1.pool.ntp.org'
list server '2.pool.ntp.org'
list server '3.pool.ntp.org'
option enable_server '0'

I got the timezone to set from this list: http://wiki.openwrt.org/doc/uci/system#time.zones

I need a way to find out if ntpd is working correctly. There seems to be 2 possibilities: ntpstat and ntpq, but I could not find ntpstat in linino, so tried with ntpq. Unfortunately ntpq was not working either. My conclusion was that it was using busybox ntpd, rather than a fully featured ntpd, so I installed ntpd doing:

opkg update
opkg install ntpd
/etc/init.d/sysntpd disable
/etc/init.d/ntpd enable
/etc/init.d/ntpd start

Now ntpq works!!! just need to figure out how do I parse the output of ntpq to get something meaningful.

On the arduino side, I bought an RTC chip module to make sure the clock is on time, even when there is no internet connection. I will get back to that whenever the RTC module arrives.


Friday, January 17, 2014

Using REST for communication between AVR and Linux

I changed my code on my previous post to use REST as a way to communicate between Arduino and Linux. REST is basically a web server running on the linux side which allows access to key/value pairs via web or through serial from Arduino. In particular I am using this now for sending the info about the song that is playing. From my python script I am calling curl to POST the info to the REST website and from Arduino I am doing Bridge.get calls to receive the data.

Here is the new python script:


from __future__ import print_function
import urllib
import urllib2
import subprocess
import sys

from grooveshark import Client
from grooveshark.classes import Radio

client = Client()
client.init()
cmdargs = str(sys.argv)

url_name='http://localhost/data/put/songName/'
url_artist='http://localhost/data/put/songArtist/'
url_album='http://localhost/data/put/songAlbum/'

for song in client.radio(sys.argv[1]):
    
    song_name = unicode(song.name).encode("utf-8")
    song_artist_name = unicode(song.artist.name).encode("utf-8")
    song_album_name = unicode(song.album.name).encode("utf-8")


    print(song_name)
    print(song_artist_name)
    print(song_album_name)

    url_name_curl = url_name + urllib.quote(song_name)
    urllib2.urlopen(url_name_curl)
    url_artist_curl = url_artist + urllib.quote(song_artist_name)
    urllib2.urlopen(url_artist_curl)
    url_album_curl = url_album + urllib.quote(song_album_name)
    urllib2.urlopen(url_album_curl)

    sys.stdout.flush()
    subprocess.call(['mpg123', song.stream.url])


UPDATE:
There is a pycurl library that I could have used instead (and there seem to be other alternatives, I am just not a python expert), but this seemed quicker to implement at the time. Will find an alternative... 
I am using urllib2 to do the POST requests to the REST website. Originally I was using curl (running it in a subprocess) but urllib2 avoids calling a separate process.
Important thing is to remember calling urllib.quote on the text to POST, as you will need to convert special characters to "%XX" to make it a valid URL.

On the Arduino side I am only getting the song name, but it is enough to get the idea of how this is done. This is how my sketch looks like now:


#include <FileIO.h>

#define    GENRE_NONE             0
#define    GENRE_ROCK             12
#define    GENRE_BLUES            230
#define    GENRE_ELECTRONICA      67
#define    GENRE_CLASSICROCK      3529
#define    GENRE_INDIE            136
#define    GENRE_METAL            17


int stations[] = { GENRE_NONE,
    GENRE_ROCK,
    GENRE_BLUES,
    GENRE_ELECTRONICA,
    GENRE_CLASSICROCK,
    GENRE_INDIE,
    GENRE_METAL };

int genres;
int val = 0;
int prevVal = 0;
Process radio_process;
String label;

void setup() {
  genres = sizeof(stations)/sizeof(int);
  Bridge.begin();
  Serial.begin(9600);

  while(!Serial);  // wait for Serial port to connect.
  Serial.println("Grooveshark Radio example");

  start_radio(val);  
}

void loop() {
  val = analogRead(3);
  val = (val*genres)/1023;

  
  if(val != prevVal) {
    String text;
    text = "Value: ";
    text += val;
    Serial.println(text);

    start_radio(val);
    
    prevVal = val;
  }
// removed old code to print the output of the script: 
/*
  while(radio_process.available() > 0){
    char c = radio_process.read();
    Serial.print(c);
  }
*/
  char labelbuffer[256];
  Bridge.get("songName", labelbuffer, 256); 

  if (String(labelbuffer).length() > 0 && label != String(labelbuffer)){
    label = String(labelbuffer);
    
    Serial.println(label);

  }
  
  
  delay(400);
}

void start_radio(int val) {
    String parameter = "";
    radio_process.close();
    Process killer;
    killer.begin("killall");
    killer.addParameter("mpg123");
    killer.run();
    if(stations[val] != 0) {
      radio_process.begin("python");
      radio_process.addParameter("/root/examples-pygrooveshark/radio_mpg123.py");
      parameter+= stations[val];
      radio_process.addParameter(parameter);
      radio_process.runAsynchronously();
    }
    else {
      radio_process.begin("echo");
      radio_process.addParameter("Do Nothing!!!");
      radio_process.runAsynchronously();
    }
}

Wednesday, January 15, 2014

Arduino Yun Grooveshark Radio


I got an Arduino Yun as Christmas preset this year. After a few days playing around with it, I decided that I wanted to turn it into an internet audio playback device. I found there is a cool python library to access Grooveshark, which is even cooler than traditional internet radios... I will explain here the steps to get it to work. I assume basic Linux and programming knowledge in my explanation.

The first step was to get USB audio working as per the explanations in here... easy...

Next step was to get some audio player running on the Linux side which would play an mp3 from a URL (rather than from file as madplay). I checked on the available packages and there was no mplayer, no vlc, no mpg123... crap... so I checked on how to compile my own packages. The first step was to compile linino image and packages:

git clone https://github.com/arduino/linino.git
cd linino/trunk
./scripts/feeds uninstall -a  
rm -rf feeds
./scripts/feeds update -a
./scripts/feeds install -a
rm -f .config
git checkout .config
make oldconfig
make V=s

During this process I found some compile issues, in fact I did not get to build all the packages, but seems like it was enough to later build mpg123.

In order to build the mpg123 package, I got the makefile from here, copied it inside my linino tree in linino/trunk/package/feeds/packages/mpg123/ and then did:

make package/mpg123/compile V=s

Somehow that did not create a .ipk file to install in the yun, as I was expecting, but everything from the ipk package that should have been created was available in linino/trunk/build_dir/target-mips_r2_uClibc-0.9.33.2/mpg123-1.13.2/ipkg-install/, so I just had to copy that folder into a pen drive and from there into the yun filesystem.

When I tried to run mpg123 I faced another issue, libltdl shared library was missing. Seems like that library was built during the linino image compilation here: linino/trunk/staging_dir/target-mips_r2_uClibc-0.9.33.2/usr/lib/libltdl.so.7.3.0. As with mpg123 before, I copied it into a pen drive and into the filesystem of the yun. Et voila... mpg123 was working.

Unfortunately, sound quality alternated between OK and very bad (with stuttering sound). In  order to fix this, I created the file /etc/asound.conf with the following contents:

pcm.dmixer {
    type dmix
    ipc_key 1024
    ipc_key_add_uid false
    ipc_perm 0666
    slave {
        pcm "hw:0,0"
        ### WARNING: do NOT add period_time, period_size or buffer_size here!!! ###
        rate 44100
    }
    bindings {
        0 0
        1 1
    }
}

pcm.dsp0 {
    type plug
    slave.pcm "dmixer"
}

pcm.!default {
    type plug
    slave.pcm "dmixer"
}

pcm.default {
   type plug
   slave.pcm "dmixer"
}

ctl.mixer0 {
    type hw
    card 0
}

This came from here. That seemed to fix the issues.

OK, so we now have mpg123 working, yuhuuu... next step was pygrooveshark. I downloaded it from here, put it into a pendrive and did:

python setup.py install

Based on the pygrooveshark radio example, I made this python script in /root/examples-pygrooveshark/radio_mpg123.py which takes a number representing the radio genre as parameter (17 is for METAL, if you want to try it from the command line):

from __future__ import print_function

import subprocess
import sys

from grooveshark import Client
from grooveshark.classes import Radio

client = Client()
client.init()
cmdargs = str(sys.argv)

for song in client.radio(sys.argv[1]):
    print(song.name)
    print(song.artist.name)
    print(song.album.name)
    sys.stdout.flush()
    subprocess.call(['mpg123', song.stream.url])

Tried running it but found another issue. Do not remember the exact error, but basically python on the yun was built without SSL support. The solution was to copy the file linino/trunk/staging_dir/target-mips_r2_uClibc-0.9.33.2/root-ar71xx/usr/lib/python2.7/lib-dynload/_ssl.so in /usr/lib/python2.7/lib-dynload/_ssl.so in the yun filesystem. Once that was done, I was able to run pygrooveshark scripts from the yun linux terminal.

Last step was to get some way of launching the different radio genres from the arduino/AVR side. Here is the hardware setup:



And here the sketch running on the AVR side:


#include <FileIO.h>

#define    GENRE_NONE             0
#define    GENRE_ROCK             12
#define    GENRE_BLUES            230
#define    GENRE_ELECTRONICA      67
#define    GENRE_CLASSICROCK      3529
#define    GENRE_INDIE            136
#define    GENRE_METAL            17


int stations[] = { GENRE_NONE,
    GENRE_ROCK,
    GENRE_BLUES,
    GENRE_ELECTRONICA,
    GENRE_CLASSICROCK,
    GENRE_INDIE,
    GENRE_METAL };

int genres;
int val = 0;
int prevVal = 0;
Process radio_process;

void setup() {
  genres = sizeof(stations)/sizeof(int);
  Bridge.begin();
  Serial.begin(9600);

  while(!Serial);  // wait for Serial port to connect.
  Serial.println("Grooveshark Radio example");

  start_radio(val);  
}

void loop() {
  val = analogRead(3);
  val = (val*genres)/1023;

  
  if(val != prevVal) {
    String text;
    text = "Value: ";
    text += val;
    Serial.println(text);

    start_radio(val);
    
    prevVal = val;
  } 

  while(radio_process.available() > 0){
    char c = radio_process.read();
    Serial.print(c);
  }
  delay(400);
}

void start_radio(int val) {
    String parameter = "";
    radio_process.close();
    Process killer;
    killer.begin("killall");
    killer.addParameter("mpg123");
    killer.run();
    if(stations[val] != 0) {
      radio_process.begin("python");
      radio_process.addParameter("/root/examples-pygrooveshark/radio_mpg123.py");
      parameter+= stations[val];
      radio_process.addParameter(parameter);
      radio_process.runAsynchronously();
    }
    else {
      radio_process.begin("echo");
      radio_process.addParameter("Do Nothing!!!");
      radio_process.runAsynchronously();
    }
}

I can change grooveshark radios by moving the potentiometer.

What is next? I have ordered an LCD screen and a rotary encoder for user interface. My plan is to allow playback of grooveshark user favourites and playlists, but that requires a lot more changes in both the AVR and Linux.