Posts Tagged 'MuttonChop'
2016-03-06

A state of semi-focused relaxation, with a smidgeon of oomph, and a smattering of zing, is just the thing to help me unwind. For some reason, the tones of The Mermen always seem to direct me towards that state. Live recordings of Instrumental Surfadelic music puts my mind in a wonderful place for coding, crafting, sleeping, driving, creating, and simply breathing.

For the most part, music listening to me means "put everything on random". While this is fine for most things, there are times when I want to listen to more than one track at a time by any given artist. Since the majority of my music listening involves using MuttonChop! for playback, and most of that playback happens on Wind (damn, that old machine needs updating), a bit of code with HTTP requests should do what I need.

the Need

What I really wanted was a script that would:

  1. take a number as an argument
  2. get a list of all Mermen tunes from Wind
  3. pick a random index in the tune list
  4. starting at the index, have Wind queue X number in a row from the list

Enter the ruby

#!/usr/bin/env ruby
require 'open-uri'
require 'json'

#how many tracks are going to be played?
num ARGV[0].to_i
num if num == 0

#what is the URL for all mermen tracks on wind?
url "http://wind:2876/audio/search/mermen"

#get the text from the url
text openurl ).read()
#parse the text to JSON data
data JSON.parse(text)
files data['files']

#base zero offset
bzo num-1

#get a random number between 1 and the number of mermen tracks - the num we want to play
queue_index rand(files.count-(num))

#loop from our random number to random number + our base zero offset
(queue_index..queue_index+bzo).each do |i|
  # get the file at this index
  file files[i]
  #get the id of the file at this index
  id file["id"]
  #create a queue url for this file
  new_url "http://wind:2876/queue/add/audio/#{id}"
  puts "#{i}#{new_url}"
  #call the url to add the file to the queue
  opennew_url )
  #don't go to fast
  sleep 0.1
end

For easier copy/paste, the code is available at http://hoof.jezra.net/snip/os

So there you have it, a nice script to do exactly what I want. Now, when I need to get into that certain place, a quick 'wind_queue_mermen.rb 5' does the trick. Why stop there? A bit of blather config editing, and queuing five Mermen tunes is as easy as picking up the Cronos microphone and saying "queue five mermen". BOOYAH!

Now quite reading, and go to https://archive.org/details/Mermen for some wonderful live recordings.

Comments
Name:
not required
Email:
not required (will not be displayed)
Website:
not required (will link your name to your site)
Comment:
required
Please do not post HTML code or bbcode unless you want it to show up as code in your post. (or if you are a blog spammer, in which case, you probably aren't reading this anyway).
Prove you are human by solving a math problem! I'm sorry, but due to an increase of blog spam, I've had to implement a CAPTCHA.
Problem:
1 plus 2
Answer:
required
2014-02-26

Someone once said that I was 'addicted to noise', while I don't necessarily agree with the 'noise' part, I certainly like to listen to music... all the time. With that in mind, you can understand my desire to have a sweet sweet music player in my workshop, and now I have one (sort of). Say hello to Bonechop!

What's in a name?

Bonechop is a Beaglebone Black computer running Debian Linux and using MuttonChop for network controlled audio playing, and the whole system is put in an old AM Radio. Since the Beaglebone Black doesn't have audio out capabilities, I opted to use a hella cheapo USB audio card; and it works wonderfully.

Gather Some Supplies

Here we have:

  • a nice big late 50s or early 60s Montgomery Ward Airline AM radio gifted by a friend (thanks buddy!)
  • a screw driver
  • some awesome glue
  • white gorilla tape
  • the beaglebone (and a USB audio card)
  • a bunch of little wood screws
  • a 1" x 1" piece of poplar to use a stand offs

OK, the Poplar Didn't Work

aside from having a hell of a time just cutting the poplar into little pieces, every piece I tried to put a screw into would split before the computer was securely fastened. bummer.

Fortunately, there were some pine shims in the workshop that were left over from when my buddy framed in a door. Thanks buddy!

Here we have 3 small pieces of pine secured to the Beaglebone with little wood screws. I would have used 4 pieces, but the 4th hole is located near the micro-SD card slot and my blocky stand-off wouldn't fit properly. That's OK because 3 points make a plane.

A New Adhesive!

This is the first time I've used Weldbond, and I must say that I'm quite pleased with the results. After tipping the radio onto its side, the 3 stand-offs were glued to the body of the radio.

The next day, I took my coffee picture with the finished product.

How about a video?

As an entry to a contest by Adafruit Industries, I made a video of Bonechop playing some music.

This project is definitely not finished, but it is in a damn fine usable state. Now I need to go find some switches to wire into the GPIO.

Comments
Name:
not required
Email:
not required (will not be displayed)
Website:
not required (will link your name to your site)
Comment:
required
Please do not post HTML code or bbcode unless you want it to show up as code in your post. (or if you are a blog spammer, in which case, you probably aren't reading this anyway).
Prove you are human by solving a math problem! I'm sorry, but due to an increase of blog spam, I've had to implement a CAPTCHA.
Problem:
9 minus 3
Answer:
required
2013-12-28

The other day, while downloading the latest episode of Linux Outlaws, I wondered why I couldn't queue the episode to play first thing in the morning using MuttonChop, then I thought "Do I really hate myself enough to wake up to these guys blabbing away?" Apparently, the answer is "yes". ha!

(play the the A-Team Theme)
The plan went like this:

  1. write a script to:
    1. get the list of cast subscriptions from a MuttonChop computer
    2. find the first cast with a title that matches a given string
    3. always skip step 3
    4. find the most recent downloaded and unplayed episode of that cast
    5. queue the unplayed episode
  2. make an alarm for the Ruby Web Alarm Project that calls the script from step 1 with "Outlaws" and the search parameter
  3. always skip step 3
  4. set an alarm for the morning (fade_random_vivaldi.sh should do the trick)
  5. set the alarm from step 2 to run at the same time as fade_random_vivaldi.sh

... and that is exactly what I did.

OK, not exactly. The script defaults to search for Outlaws. ;)

For this script, I decided to use the Ruby programming language and this is the first Ruby script I've written that uses the Optparse library from the ruby standard library. The Optparse library makes handing the parsing of command line arguments quite easy. Having used an option parser in Vala and Python programming languages, I was interested to see how option parsing was handled in Ruby.

OK, that's enough jibber-jabber....

Enter the Ruby

#!/usr/bin/env ruby
require 'open-uri'
require 'json'
require 'optparse'

#define some default values for our options
options = {:host=>"localhost":cast_search_string=>"Outlaws"}

#build the options
OptionParser.new do |opts|
  opts.banner "Usage: #{File.basename(__FILE__)} [options]"
  opts.on("-s""--server server_name"String"The MuttonChop server to query"do |s|
    options[:host] = s
  end
  opts.on("-c""--cast search_string"String"The cast to search for"do |c|
    options[:cast_search_string] = c
  end
end.parse!

#show the options
puts "Host: #{options[:host]}"
puts "Search String: #{options[:cast_search_string]}"
#where do we get the list of subscribed feeds from the muttonchop server?
url "http://#{options[:host]}:2876/catcher/feeds"
puts "Feeds URL: #{url}"
#get the returned text from the url
text open(url).read()
#parse the text as json
feeds JSON::parsetext )

feed_id nil
#loop through the feeds
feeds.each do |feed|
  #does this feed's title contain our cast_search_string?
  if feed['title'].match(options[:cast_search_string])
    feed_id feed['id']
    break
  end
end

#if no feed was found, exit
if feed_id.nil?
  puts "No matching cast title"
  exit
end

#where do we find a list of the matching feed's episodes?
url "http://#{options[:host]}:2876/catcher/episodes/#{feed_id}"
puts "Matching Feed's Episodes URL: #{url}"
#read the url's returned text and parse it
text openurl ).read()
episodes JSON::parsetext )

episode_id nil
#loop through each episode
episodes.each do |e|
  #has this episode been downloaded? does a file exist? has it not been played?
  if e["downloaded"and e["exists"and not e["played"]
    episode_id e['id']
    break
  end
end

unless episode_id.nil?
  #what url is needed to queue the unplayed episode?
  new_url "http://#{options[:host]}:2876/queue/add/cast/#{episode_id}"
  puts "Episode Queue URL: #{new_url}"
  opennew_url ).read
else
  puts "No unplayed episodes"
end

For easier copy/paste, this code is also available at http://hoof.jezra.net/snip/oh.

While the script certainly gets the job done, there should really be some error handling for when the host doesn't exist and it isn't possible to open and read a URL, but hey, it certainly suits my needs very well.

Now quit reading, and go listen to something.

Comments
Name:
not required
Email:
not required (will not be displayed)
Website:
not required (will link your name to your site)
Comment:
required
Please do not post HTML code or bbcode unless you want it to show up as code in your post. (or if you are a blog spammer, in which case, you probably aren't reading this anyway).
Prove you are human by solving a math problem! I'm sorry, but due to an increase of blog spam, I've had to implement a CAPTCHA.
Problem:
2 plus 5
Answer:
required
2013-08-04

A few weeks ago, while being entranced by a software bug, I failed to keep an eye on the clock and thus I missed a work meeting. Do you know when that tragedy is going to happen again? never. Obviously, I need to set some sort of alarm...

In my minds eye, I had a vision of what I wanted the alarm to do: play an audio file and flash some lights. Fortunately, the software I've been writing could almost do what I wanted to do. almost. A bit of code hacking needed to be done.

For audio playback, MuttonChop media player was installed on the toaster(which has an amplifier and speaker). A Star Trek Alert was selected as the audio to play when the alarm goes off. However, before the alarm would work as I wanted, I needed to add a bit of code to MuttonChop to make a playing file loop over and over. In the MuttonChop API, this resulted in the following new API calls:

  1. HOST:PORT/player/loop
    retrieves the loop state of the player
  2. HOST:PORT/player/loop/true
    set the player loop state to 'true'
  3. HOST:PORT/player/loop/false
    set the player loop state to 'false' (to be fair, anything that is not 'true' will result in 'false')

Rad, that takes care of the audio aspect of the alarm. On to the lights!

GLMR(the GLRM Light Manipulation Regulator) installed on a Raspberry Pi affectionately named "shitbird", will handle the light needs. However, after running some tests I quickly realized that I would need a quick and easy way to revert the lights to their "pre-alarm" state. A bit of code caking later and two new API paths were added to GLMR:

  1. HOST:PORT/color/previous
    set the LEDs to their previous set color
  2. HOST:PORT/mode/previous
    set the LEDs to their previous set mode

Booyah! Now it is simply a matter of creating an alarm for the Ruby Web Alarm project.

enter the shell script

#!/bin/sh
# call the API to play audio file with ID of 1
curl fruity:2876/audio/play/1
# set the looping to 'true'
curl fruity:2876/player/loop/true
#crank up the volume
curl fruity:2876/player/volume/100

#set the glmr mode to 'twinkle'
curl shitbird:4567/mode/twinkle
#set the LEDs to full red
curl shitbird:4567/color/ff0000

...And finally, there needs to be a way to turn off the alarm. To keep shit sweet, I decided to use the 'Cancel' button on the face of the toaster to run a 'cancel alarm' ruby script.

Enter the Ruby

#!/usr/bin/env ruby
require 'json'
require 'open-uri'

#get the status of muttonchop on fruity
url "http://fruity:2876/player/status"
openurl do |f|
  status JSON.parsef.read() )
  #is the machine playing an alert?
  if status['state']=="PLAYING" && status['album']=='Alert'
    #stop the muttonchop player
    open("http://fruity:2876/player/stop")
  end
end

#check the glmr status on shitbird
open("http://shitbird:4567/status"do |f|
  status JSON.parse(f.read() )
  # if the alarm is running
  if status['color']=='ff0000' && status['mode']=='twinkle'
    #revert the color
    open("http://shitbird:4567/color/previous")
    #revert the mode
    open("http://shitbird:4567/mode/previous")
  end
end

Alrighty! The alarm goes on, and the alarm goes off; but what does it look/sound like?

It may not be the greatest alarm ever, but it is highly effective. Now quite reading, and go set an alarm.

Comments
2013-08-05 Alison Chaiken:
Okay, I admit it's cooler than the Pebble watch.
Name:
not required
Email:
not required (will not be displayed)
Website:
not required (will link your name to your site)
Comment:
required
Please do not post HTML code or bbcode unless you want it to show up as code in your post. (or if you are a blog spammer, in which case, you probably aren't reading this anyway).
Prove you are human by solving a math problem! I'm sorry, but due to an increase of blog spam, I've had to implement a CAPTCHA.
Problem:
6 plus 8
Answer:
required
2013-02-13

After the most recent flashing of my Tizen developer device, the web browser was no longer launching from the desktop and I had no way to test the mobile web interfaces I've been making for various projects on the Tizen device. However, since Tizen applications are supposed to be created using HTML5, javascript and CSS, I figured it was time to create a stand-alone UI for MuttonChop.

Why not just reflash the device? Good question. Unfortunately, the device is no longer recognized when I run the proprietary flashing software provided by Tizen.

What is a Widget?

For the most part, Tizen html5 applications are W3C Widgets, which is really nothing more than zip file containing HTML, JavaScript, and CSS files. Within the zip file with the application code (the zip file will have the .wgt extension by the way), the W3C specification for a widget calls for an XML file named configuration.xml that will contain information about the widget.

That being said, a widget created for Tizen needs to have a bit of extra data entered into the configuration file or Tizen won't launch the app. (At least that has been my experience).

Just add some configuration

Because the mobile web interface for MuttonChop was already written, there wasn't too much that needed to be done in order to convert the UI into a stand-alone widget. Normally, the UI would be served from the MuttonChop server, but as an independent application, the user will need some way to enter (and store) the host name and port of the MuttonChop server.

Adding a simple configuration form to the app covered the input needs, and a very little bit of JavaScript was need to store the server information locally. The aptly named localStorage JavaScript object was used for storage. The code looks like:

    //get data from the form
    host = $("#config_host").val();
    port = $("#config_port").val();
    //record the data in the local store
    localStorage.setItem("host",host);
    localStorage.setItem("port",port);

localStorage is a nice and simple way to keep trac of small amounts of data on the client side.

After creating the configuration, everything was running fairly smoothly. A few css changes later, and it was time to create a zip file and install the new app on my Tizen device.

The install process goes like this:

  1. connect Tizen device to laptop with USB cable
  2. put Device into USB developer mode
  3. use 'scp' to copy the widget to the device
  4. 'ssh' to the device
  5. run command to install software

What really bothers me about the process is that I have to connect the device by USB in order to use scp or ssh over a wireless connection. On the bright side, I was able to design, develop, and install an application on the Tizen device without having to use the monstrous Tizen SDK on Ubuntu. Debugging on the Tizen device is still problematic.

On a side note, the application runs just fine in the Firefox OS simulator (which is an extension for Firefox)

KITT : KARR : Cylon

In the UI for this app, there is a place where track information is displayed and quite often the information is wider than the screen. This would normally cause the text to wrap and I found the wrapping to be aesthetically annoying, and a bit of scrolling back and forth was implemented. It looks like the following:

Annoyance

In the previous video, there is a non-usable horizontal slider for the volume. That is how webkit mobile renders an HTML5 range element. On the desktop webkit, the range element works just fine, and on Firefox, the range element is rendered as a numeric text input. It is possible to replace the HTML5 range element with a fancy JavaScript slider, but I prefer to stick to native HTML5 elements and then apply styles as needed. Hopefully, one of the many companies that uses mobile webkit for their browser will implement a working range element, or they will completely disable the range element altogether.

Vallejo

Mariano Guadalupe Vallejo. Did you see that picture on wikipedia? Of all the great pictures of Vallejo, why did they choose the one where he isn't rocking some awesome muttonchops? Obviously there are some anti-sideburn jerks working at wikipedia.

Anyway, I've decided to name my MuttonChop related projects after historic Californians with sideburns.

Head over to gitorious if you want to check out the Vallejo code.

Rock on, and rock out!

Comments
Name:
not required
Email:
not required (will not be displayed)
Website:
not required (will link your name to your site)
Comment:
required
Please do not post HTML code or bbcode unless you want it to show up as code in your post. (or if you are a blog spammer, in which case, you probably aren't reading this anyway).
Prove you are human by solving a math problem! I'm sorry, but due to an increase of blog spam, I've had to implement a CAPTCHA.
Problem:
0 plus 6
Answer:
required
2012-05-14

While working on MuttonChop, I was unhappy with the way that the Web UI updated based on the state of of the player. The reason for my displeasure, was the use of polling to determine the player state.

In pseudo-code, the polling is something like the following:

function check_player_state
    //get the player state
    state = get_player_state
    //parse the state and update the UI accordingly
    update_web_ui ( state )
    //sleep for 2 seconds
    sleep 2
    //check the state again
    check_player_state

Every 2 seconds, the Web UI checks the player state and then updates the UI.

Obviously I could shorten the polling time, but this leads to two issues that I'd rather not deal with.

  1. Constant polling creates far too much network traffic for my tastes.
  2. The steady amount of pulled data forces the client to work to much. If the client is running on a mobile device, this will cause unnecessary battery usage.

The solution, at least for me while coding MuttonChop, was to use a push technology known as Server Sent Events, which are part of the HTML5 specification.

In a nutshell, Server Sent Events work as follows:

  1. a client makes a connection to a server
  2. when the server has new data for the client, the server sends the data over the connection for the client to process

When not processing events, the client can be idle and save battery life (hopefully).

A Real World Scenario

While listening to music, when the track changes, the client interface should show the new track title and artist.

With polling, it may take 2 seconds for the UI to display the change.
With Server Sent Events, the update is almost in realtime.

The usage of Server Sent Events can make a sluggish polling based UI much more responsive to user interaction, and when dealing with Web apps, this speed up in responsiveness is very noticeable.

Client Support

Not all clients support Server Sent Events (in JavaScript terms, it is called EventSource). For example, the browser on my n900 doesn't support EventSource, and apparently the next version Internet Explorer doesn't support it either. fancy that.

In the case of MuttonChop, both polling and Server Sent Events are used. When the Web UI is first accessed, a line of JavaScript detects if the client can use EventSource. If the client can not use EventSource, the UI falls back to using polling.

Since the connection for using Server Sent Events uses HTTP and the data is transferred as text, it should be fairly easy to write programs to process the events using any language that has an HTTP library.

To see what the events look like for MuttonChop (if you are interested in such things), open the command line and curl the "events" url of a playing muttonchop machine.

curl MUTTONCHOP_HOST:2876/events

Taking this a step further, open a few connections to a MuttonChop server using a web browser or the curl method mentioned above, and change the volume on one client; the other clients should update the volume bar almost instantaneously. sweet sauce!

A great introduction to coding using Server Sent Events is available at http://www.html5rocks.com/en/tutorials/eventsource/basics/

Until later, happy hacking!

Comments
Name:
not required
Email:
not required (will not be displayed)
Website:
not required (will link your name to your site)
Comment:
required
Please do not post HTML code or bbcode unless you want it to show up as code in your post. (or if you are a blog spammer, in which case, you probably aren't reading this anyway).
Prove you are human by solving a math problem! I'm sorry, but due to an increase of blog spam, I've had to implement a CAPTCHA.
Problem:
9 minus 1
Answer:
required
2012-02-20

Making it (almost) Official

Hello, let me introduce you to MuttonChop a media player for *nix with a Web UI and a JSON API.

What?

Once configured, MuttonChop will allow one to browse and play audio and video files on a computer. The browsing and control of playback is handled by a fairly basic API and MuttonChop listens for API calls, and serves the web interface, through port 2876. Yea, the port number is B-U-R-N on a telephone. Have I mentioned the sideburn theme here?

Why?

Multiple reasons; most importantly because MuttonChop didn't exist and I wanted a media player on my home network that could be controlled from any networked device with a standard compliant browser; such as a laptop, netbook, tablet, smart phone, or pocket computer.

Rant: I hate remote controls with a lot of buttons. Give me a web interface so I can use my many devices as a remote control.

How?

MuttonChop uses a very minimal web server to listen for API requests. Similarly, MuttonChop also serves a web based UI that utilizes the API via AJAX calls using the jquip javascript library.

A Tangent: Many Moons ago, I wrote a custom interface for MPD in Python, using the PyGame library, so that I could control my music playback with a joystick. If it wasn't for MPD's well documented API, I wouldn't have been able to write the interface (which was my first foray into Python).
Had it not been for MPD's API, I might not have had such a great learning experience with Python. That being said, I can only hope that someone learning to code might try their hand at making a client for MuttonChop; I know I certainly plan on making a client or two.

Back to Coding

Well that's about it for the introduction. Once I get MuttonChop to download audcasts, I'll start making point releases. Until then, the code will be in https://launchpad.net/muttonchop/trunk

Comments
2012-02-20 x1101:
I will have to watch this. This sounds fun, and I might even commit some code. Is it GPLv3 or AGPL? (I am actually going to look this up now, but thought you might want to mention that in your post?)
2012-02-20 jezra:
Technically, MuttonChop is two projects: the Server and the web client. The server is GPLv3, and I haven't really thought much about the licensing of the current web interface. I'll have to figure that out by the time of the first point release.
Name:
not required
Email:
not required (will not be displayed)
Website:
not required (will link your name to your site)
Comment:
required
Please do not post HTML code or bbcode unless you want it to show up as code in your post. (or if you are a blog spammer, in which case, you probably aren't reading this anyway).
Prove you are human by solving a math problem! I'm sorry, but due to an increase of blog spam, I've had to implement a CAPTCHA.
Problem:
4 minus 4
Answer:
required
subscribe
 
2016
2015
2014
2013
2012
2011
2010
December
November
October
September
August
July
June
May
April
March
February
January
2009
December
November
October
September
August
July
June
May
April
March
February
January
2008