Skip to content

Tag Archives: music

Senior Project, Application Launcher, System Customizations

11-Jan-10

It has been a while since my last update, and since then we have gotten completely usable two-glove audio output, without accelerometer-based articulations. It is very cool (we can kind of play the Tetris A Theme, but no one is very good at using the gloves yet). Just today we got the accelerometer all working; we have rotation and vertical motion output fit to the correct ranges. Despite having the software all written, we don't have the gloves working with full articulations due to the issue of actually mounting the accelerometers on the gloves and the Arduinos on the wristbands. We have already bought most of the components we'll need to do this, so I expect to have the entire thing working by the end of this weekend. When we finish, I'll post the final parts list, pictures, and latest code, along with instructions for constructing your own pair of musical gloves as a DIY project. Next!

I haven't been very motivated to finish it since the current version works fine, but I'm almost done with a re-write of my application launcher. The new version will by extensible via plugins and will be able to run in the background (as opposed to starting up every time you summon it). Aside from the rewrite, I added some neat shorthand directory launching support, which allows me to open "/home/ifx/Code/Launcher/final" by typing "/lau/f" and pressing enter. Regex is pretty neat.

Finally, I think I'm going to make a post about all the interesting customizations I have made to my Arch Linux install. These would include my mouse gestures (I have some that actually do useful, non-obvious things), my Conky configuration, and, of course, my .bashrc.

Things That Have Happened, Things That Have Not, and Senior Project Status

12-Nov-09

As of the time of posting, I have ten Google Wave invites. I've been playing with Wave a bit, and it is pretty neat, but not extremely impressive. I have seen real-time collaborative document editing before. Using Wave for chat is fairly cumbersome (no send on Enter). I have also gotten a few crashes, and it isn't incredibly snappy.

All that aside, it is very cool and I do see how it can be useful. Even if just discussing a random topic, the fact that you can reply to related comments and have the whole discussion appear in a tree promotes organization. I am using a neat LaTeX extension that allows me to embed editable LaTeX markup which is automatically rendered and displayed inline. Related to this, I am using Wave with the math teacher to plan for my third trimester class "Seminar in Independent Mathematical Research" (I'm very excited, I might post about my topic ideas later).

I started learning Go recently. For practice, I'm re-writing my Python solutions for Project Euler in Go. Here is Problem 259:

package main
 
import (
    "fmt";
    "strconv";
    "strings";
    "./fraction";
    "time";
)
 
func simplify(nums []*fraction.Fraction) map[string]*fraction.Fraction {
	out := make(map[string]*fraction.Fraction);
	var r *fraction.Fraction;
	if len(nums) == 1 {
		out[nums[0].String()] = nums[0];
		return out;
	}
	for offset := 1; offset < len(nums); offset++ {
		lefts := simplify(nums[0:offset]);
		rights := simplify(nums[offset:len(nums)]);
		for _, left := range lefts {
			for _, right := range rights {
				r = left.Add(right);
				out[r.String()] = r;
				r = left.Sub(right);
				out[r.String()] = r;
				r = left.Mul(right);
				out[r.String()] = r;
				if right.IsZero() != true {
					r = left.Quo(right);
					out[r.String()] = r;
				}
			}
		}
	}
	return out;
}
 
func groups(symbols string) ([]string, int) {
	out := new([512]string);
	out[0] = symbols;
	c := 1;
	for i := 1; i < len(symbols); i++ {
		gout, gcount := groups(symbols[i:len(symbols)]);
		for g := 0; g < gcount; g++ {
			group := gout[g];
			out[c] = symbols[0:i] + "|" + group;
			c += 1;
		}
	}
	return out, c
}
 
func numlist(s string) []*fraction.Fraction {
	vals := strings.Split(s, "|", 0);
	out := make([]*fraction.Fraction, len(vals));
	for i := 0; i < len(vals); i++ {
		t, _ := strconv.Atoi64(vals[i]);
		out[i] = fraction.New(t, 1);
	}
	return out;
}
 
func simplify_nums(nums string) map[string]*fraction.Fraction {
	return simplify(numlist(nums));
}
 
/*
func simplify_nums(nums string, out chan map[string]*fraction.Fraction) {
	out <- simplify(numlist(nums));
}
*/
func main() {
	start := time.Seconds();
	reachable := make(map[uint64]bool);
	seqs, nseqs := groups("123456789");
	for i := 0; i < nseqs; i++ {
		reps := simplify_nums(seqs[i]);
		for _, k := range reps {
			if k.IsPos() && k.IsInt() {
				reachable[uint64(k.Num())] = false;
			}
		}
	}
	/*
	channels := make([]chan map[string]*fraction.Fraction, nseqs);
	for i := 0; i < nseqs; i++ {
		channels[i] = make(chan map[string]*fraction.Fraction);
		go simplify_nums(seqs[i], channels[i]);
	}
	for i := 0; i < nseqs; i++ {
		reps := <-channels[i];
		for _, k := range reps {
			if k.IsPos() && k.IsInt() {
				reachable[uint64(k.Num())] = false;
			}
		}
	}
	*/
	var total uint64 = 0;
	for k, _ := range reachable {
		total += k;
	}
	fmt.Printf("Sum: %d\n", total);
	fmt.Printf("Total Time: %d\n", time.Seconds() - start);
}

If anyone wants "fraction.go", I can upload it.
I tried using goroutines (commented in the code above) to speed things up via concurrency, but it didn't seem to have any effect on the speed (though memory usage increased) and CPU usage only spiked on one core. Not sure what the deal is with that; I'll look into it later.

So far as I have played with it, I like the language. Go has a respectable standard library, which is something many new programming languages (that I have seen) lack. If I code anything interesting enough, I'll make a post about it.

About my Application Launcher: I'll be splitting it up into a daemon process and a GUI process which will communicate using D-Bus (probably, haven't decided yet). I will be adding filesystem searching using Glimpse and locate. Glimpse is pretty damn impressive. It provides very fast searching of file contents, support for fuzzy matching, and the index is quite small (for everything under my home directory the index is ~34 MB).

Related to this, I am going to be looking into Fuzzy Matching once more (see music on the command line). Already existing for Python, I found this. Haven't tested it yet. I downloaded a few papers on Damerau-Levenshtein distance and Levenshtein automata, but probably won't start implementing anything for the next week or two.

Finally, my Senior Project. By the end of this trimester (~1 week) Nate and I plan to have audible note output using data from the contact sensors. We will integrate the accelerometers next trimester, and should have plenty of time to debug and test afterwards. At the moment, though, due to a combination of college applications, homework, and sickness, we are a bit rushed. I don't expect it to be an issue: a good, solid work day this weekend should be enough to catch up.

As a last note, I'll mention the album I've been listening to while writing this up: "Suzumiya Haruhi no Gensou". Orchestral versions of many songs from Suzumiya Haruhi no Yuutsu. Surprisingly good.

Senior Project – Musical Gloves

16-Sep-09

Nate (a friend of mine) and I decided to work together for our Senior Projects a while ago. After some brainstorming, we thought an awesome idea would be to build a motion capture or gesture input glove. We discussed various ideas for how we might accomplish this, including cameras, resistive bend sensors, stretch sensors, and optical fiber (I hoped that as the fiber was bent, less light would remain trapped by total internal reflection such that a photosenor, an LED, and some calibration would allow us to measure bend. As it turns out, our mentor used a similar technique in the construction of a similar glove at the company for which he worked.).

We eventually scrapped all these ideas in favor of hall effect sensors. Although I only considered them because of an early idea for a glove which would only be used for typing, we decided that hall effect sensors would be perfect for the musical application which had become the main goal of the project.

The new idea is to design the glove specifically for music. Ideas for the interface can be found here, and our parts list (which might not be kept up to date during the project, but will probably be updated at the end to serve as a reference for others) is available here.

I'll be adding a section to the blog for this project, and probably a page for projects in general. We plan to blog the project as we go, so to my maybe one or two readers, check back!

Managing Music from the Command Line

30-Jun-09

Okay. I've been curious for a while about trying to manage my music from the command line. All the media managers I have used so far have been a bit too bloated for my tastes. Because of this I decided to try out MPD (Music Player Daemon). It looked pretty cool, but I didn't have a good way to quickly queue up a bunch of songs I wanted to listen to. After trying out a few solutions by others, I wrote two of my own solutions.

The first was a fuzzy matching system, and the second was a "blob" matching system. Both allow me to queue up an entire album by typing something short.

Because I haven't finished tweaking the first solution, and the second one is working impressively, I'll be posting about it only.

What You'll Need:

  • mpd
  • mpc
  • Python 2.6

Here is the code:

?View Code PYTHON
#!/usr/bin/python
from subprocess import Popen, PIPE
import sys
 
class Database:
	def __init__(self, terms=None):
		self.terms = set()
		self.tree = (set(), dict())
		if terms is not None:
			self.populate(terms)
 
	def populate(self, terms):
		for term in terms:
			self.add(term)
 
	def variations(self, word):
		result = set([word])
		if word.startswith("0") and word != "0":
			#This allows "02" to be found as "2"
			result.add(word)
		elif word in frozenset(["i", "ii", "iii"]):
			#This allows "II" to be found as "2"
			result.add(str(len(word)))
		return result
 
	def words(self, term):
		term = term.lower()
		words = set()
		buf = ""
		for char in term:
			if char.isalnum():
				buf += char
			else:
				if buf != "":
					words = words.union(self.variations(buf))
					buf = ""
		if buf != "":
			words = words.union(self.variations(buf))
		return words
 
	def add(self, term):
		self.terms.add(term)
		for word in self.words(term):
			current = self.tree
			for char in word:
				if char not in current[1]:
					current[1][char] = (set(), dict())
				current = current[1][char]
			current[0].add(term)
 
	def search(self, blob, union=False):
		#This is for excluding words from a search
		if "~" in blob:
			#Excludes titles with ANY words from exclude
			blob, remove = blob.lower().split("~")
			mode = True
		elif "#" in blob:
			#Excludes titles with ALL words from exclude
			blob, remove = blob.lower().split("#")
			mode = False
		else:
			remove = None
			blob = blob.lower()
		found = dict()
		current = self.tree
		i = 0
		for char in blob:
			if char in current[1]:
				current = current[1][char]
				i += 1
				if len(current[0]) > 0:
					found[blob[i:]] = current[0]
			else:
				break
		final = set()
		for k, v in found.items():
			if k == "":
				final |= v
			else:
				if union:
					final |= v | self.search(k)
				else:
					final |= v & self.search(k)
		if remove is not None:
			remove = self.search(remove, mode)
			final -= remove
		return final
 
	def __contains__(self, other):
		return other in self.terms
 
#Initialize and populate album database
db = Database()
#with open("/var/lib/mpd/tag_cache") as f:
#	for line in f:
#		if line.startswith("Album: "):
#			album = line[7:-1]
#			if album not in db:
#				db.add(album)
#We'll get the album list from mpc instead of the tag_cache file. I haven't done a benchmark.
p1 = Popen(["mpc", "list", "album"], stdout=PIPE)
p1.wait()
with p1.stdout as f:
	for line in f:
		album = line[:-1]
		if album != "":
			db.add(album)
 
#Parse arguments
if len(sys.argv) == 2:
	searches = [sys.argv[1]]
else:
	print "No album specified. Exiting..."
	sys.exit()
 
#Perform search
r = db.search(searches[0])
r = list(r)
 
#If more than one album is returned, ask the user to choose which one we will play.
if len(r) > 1:
	for i in xrange(len(r)):
		print " " + str(i+1) + ")", r[i]
	r = r[int(raw_input("Enter a number: ")) - 1]
elif len(r) == 1:
	r = r[0]
else:
	print "No albums found. Exiting..."
	sys.exit()
 
print "Playing album '" + r + "'"
 
#Find the tracks in the album
p1 = Popen(['mpc', 'find', 'album', r], stdout=PIPE)
p1.wait()
 
#Clear the current playlist
Popen(['mpc', 'clear'], stdout=PIPE).wait()
 
#Queue up all the tracks in the album
with p1.stdout as f:
	for line in f:
		Popen(['mpc', 'add', line[:-1]], stdout=PIPE).wait()
 
#Let MPC print a list of the tracks we have queued
Popen(['mpc', 'playlist']).wait()
 
#Start playback
Popen(['mpc', 'play'], stdout=PIPE).wait()

Save that as "album" or whatever you would like. Put it in your home directory or wherever you would like. Finally, open ~/.bashrc (for Ubuntu users, at least), and add an alias pointing to the script. I use "alias album='~/album'".

Make sure you set the script to executable.

Finally, because I enjoy having my listening history available on last.fm, I also installed lastmp to scrobble songs played with MPD.

Oh yeah. I almost forgot usage instructions. With the setup described above, one could just type "album wolfsrain" to play the Wolf's Rain OST (the script would prompt you to select either the first or second volume) or "album fiction" to play Yuki Kajiura's solo album "Fiction".

I call this album search method a "blob search" because you can type any words from an album title, in any order and without spaces, and still find the album. For instance, both "album wishyou" and "album hereyouwish" will start the playback of Wish You Were Here in my library.