diff --git a/.bob.json b/.bob.json
new file mode 100644
index 0000000..3e77e7c
--- /dev/null
+++ b/.bob.json
@@ -0,0 +1,21 @@
+{
+ "build": "clean lint complexity test coverage test-integration doc",
+ "lint": {
+ "type": "jshint"
+ },
+ "complexity": {
+ "type": "plato"
+ },
+ "test": {
+ "type": "buster"
+ },
+ "coverage": {
+ "type": "buster-istanbul"
+ },
+ "test-integration": {
+ "type": "buster"
+ },
+ "doc": {
+ "type": "dox-foundation"
+ }
+}
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..8bf0050
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+.bob
+.DS_Store
+node_modules
+npm-debug.log
diff --git a/.jshintrc b/.jshintrc
new file mode 100644
index 0000000..a5aaaed
--- /dev/null
+++ b/.jshintrc
@@ -0,0 +1,3 @@
+{
+ "esnext": true
+}
diff --git a/.npmignore b/.npmignore
new file mode 100644
index 0000000..f2648f6
--- /dev/null
+++ b/.npmignore
@@ -0,0 +1,14 @@
+.bob
+.bob.json
+.DS_Store
+.git
+.gitignore
+.jshintrc
+.npmignore
+.travis.yml
+avatar.jpg
+node_modules
+npm-debug.log
+screenshots
+test
+test-integration
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..ffe08d6
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,6 @@
+language: node_js
+node_js:
+ - "0.11"
+ - "0.10"
+before_install: "npm install -g bob --loglevel error"
+script: "bob build"
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..7e4d66b
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,2 @@
+### 0.0.1
+* Initial version
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..3f06d1b
--- /dev/null
+++ b/README.md
@@ -0,0 +1,23 @@
+
+
+[![Build Status](https://secure.travis-ci.org/cliffano/roombox.png?branch=master)](http://travis-ci.org/cliffano/roombox)
+[![Dependencies Status](https://david-dm.org/cliffano/roombox.png)](http://david-dm.org/cliffano/roombox)
+[![Published Version](https://badge.fury.io/js/roombox.png)](http://badge.fury.io/js/roombox)
+
+[![npm Badge](https://nodei.co/npm/roombox.png)](http://npmjs.org/package/roombox)
+
+Roombox
+-------
+
+Roombox is a Roomba boombox.
+
+This is handy when you want to turn your [Roomba vacuum cleaner](http://en.wikipedia.org/wiki/Roomba) into a boombox.
+
+Installation
+------------
+
+ npm install -g roombox
+
+Usage
+-----
+
diff --git a/lib/converters/abc.js b/lib/converters/abc.js
new file mode 100644
index 0000000..d26b40d
--- /dev/null
+++ b/lib/converters/abc.js
@@ -0,0 +1,43 @@
+var abcnode = require('abcnode');
+
+// reasonably pleasant Roomba song notes between note numbers 60 - 83
+const BASE = 60,
+ ROOMBA_NOTES = [
+ 'C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B',
+ 'c', 'c#', 'd', 'd#', 'e', 'f', 'f#', 'g', 'g#', 'a', 'a#', 'b'
+ ],
+ SANITISE = ['!', '\\\\', 'Z:.+'];
+
+function convert(data) {
+
+ SANITISE.forEach(function (pattern) {
+ data = data.replace(new RegExp(pattern), '');
+ });
+
+ var parsed = abcnode.parse(data),
+ song = {
+ title: parsed.header.title,
+ by: parsed.header.composer,
+ notes: []
+ };
+
+ parsed.song.forEach(function (x) {
+ x.forEach(function (y) {
+ y.forEach(function (z) {
+ z.chords.forEach(function (chord) {
+ chord.notes.forEach(function (note) {
+ var number = ROOMBA_NOTES.indexOf(note.note);
+ if (number !== -1) {
+ song.notes.push(number + BASE);
+ song.notes.push(note.duration);
+ }
+ });
+ });
+ });
+ });
+ });
+
+ return song;
+}
+
+exports.convert = convert;
\ No newline at end of file
diff --git a/lib/playlist.js b/lib/playlist.js
new file mode 100644
index 0000000..e69de29
diff --git a/lib/roombox.js b/lib/roombox.js
new file mode 100644
index 0000000..e69de29
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..f832e33
--- /dev/null
+++ b/package.json
@@ -0,0 +1,51 @@
+{
+ "name": "roombox",
+ "description": "Roomba boombox",
+ "keywords": [
+ "roomba",
+ "boombox",
+ "abc notation",
+ "music"
+ ],
+ "version": "0.0.1",
+ "homepage": "http://github.com/cliffano/roombox",
+ "author": "Cliffano Subagio (http://blog.cliffano.com)",
+ "contributors": [
+ "All contributors (https://github.com/cliffano/roombox/graphs/contributors)"
+ ],
+ "main": "./lib/roombox",
+ "bin": {
+ "roombox": "./bin/roombox"
+ },
+ "preferGlobal": false,
+ "repository": {
+ "type": "git",
+ "url": "http://github.com/cliffano/roombox.git"
+ },
+ "bugs": {
+ "url": "http://github.com/cliffano/roombox/issues"
+ },
+ "directories": {
+ "bin": "./bin",
+ "lib": "./lib",
+ "test": "./test"
+ },
+ "dependencies": {
+ "abcnode": "http://github.com/cliffano/abcnode/tarball/master",
+ "roomba": "~0.0.1"
+ },
+ "devDependencies": {
+ "buster-node": "~0.7.0",
+ "referee": "~1.0.1"
+ },
+ "scripts": {},
+ "engines": {
+ "node": ">= 0.8.0"
+ },
+ "licenses": [
+ {
+ "type": "MIT",
+ "url": "http://github.com/cliffano/roombox/raw/master/LICENSE"
+ }
+ ]
+}
diff --git a/test/converters/abc.js b/test/converters/abc.js
new file mode 100644
index 0000000..73f3cc9
--- /dev/null
+++ b/test/converters/abc.js
@@ -0,0 +1,57 @@
+var buster = require('buster-node'),
+ abc = require('../../lib/converters/abc'),
+ referee = require('referee'),
+ assert = referee.assert;
+
+buster.testCase('abc - convert', {
+ 'should convert abc notation to Roomba song data': function () {
+ var data = [
+ 'X:1',
+ 'T:Hey Jude',
+ 'C:The Beatles',
+ 'L:1/8',
+ 'Q:140',
+ 'M:4/4',
+ 'K:C',
+ 'z6G2|[E2C,2]C,2[zC,]E[GC,]A|[D2G,2]G,2[z2G,2][DG,]E|[F2G,2][c2G,2]G,c[BG,]G|[AF,]G/F/[E2C,2]C,2[zC,]G|[AF,]AF,A[d/F,/]cB/F,/c/A|[G2C,2]C,2[CC,]D[EC,]A|'
+ ];
+ var song = abc.convert(data.join('\n'));
+ assert.equals(song.title, 'Hey Jude');
+ assert.equals(song.by, 'The Beatles');
+ assert.equals(song.notes, [67, 64, 64, 64, 64, 32, 67, 32, 69, 32, 62, 64, 62, 32, 64, 32, 65, 64, 72, 64, 72, 32, 71, 32, 67, 32, 69, 32, 67, 16, 65, 16, 64, 64, 67, 32, 69, 32, 69, 32, 69, 32, 74, 16, 72, 32, 71, 16, 72, 16, 69, 32, 67, 64, 60, 32, 62, 32, 64, 32, 69, 32]);
+ },
+ 'should sanitise invalid ABC 1.6 data': function () {
+ var data = [
+ 'X:1',
+ 'T:Hey Jude!',
+ 'C:The Beatles',
+ 'L:1/8',
+ 'Q:140',
+ 'M:4/4',
+ 'K:C',
+ 'z6G2|[E2C,2]C,2[zC,]E[GC,]A|[D2G,2]G,2[z2G,2][DG,]E|[F2G,2][c2G,2]G,c[BG,]G|[AF,]G/F/[E2C,2]C,2[zC,]G|[AF,]AF,A[d/F,/]cB/F,/c/A|[G2C,2]C,2[CC,]D[EC,]A|\\',
+ 'Z: edited by'
+ ];
+ var song = abc.convert(data.join('\n'));
+ assert.equals(song.title, 'Hey Jude');
+ assert.equals(song.by, 'The Beatles');
+ assert.equals(song.notes, [67, 64, 64, 64, 64, 32, 67, 32, 69, 32, 62, 64, 62, 32, 64, 32, 65, 64, 72, 64, 72, 32, 71, 32, 67, 32, 69, 32, 67, 16, 65, 16, 64, 64, 67, 32, 69, 32, 69, 32, 69, 32, 74, 16, 72, 32, 71, 16, 72, 16, 69, 32, 67, 64, 60, 32, 62, 32, 64, 32, 69, 32]);
+ },
+ 'should throw error when there is invalid syntax': function () {
+ var data = [
+ 'X:1',
+ 'T:Hey Jude!',
+ 'C:The Beatles',
+ 'L:1/8',
+ 'Q:140',
+ 'M:4/4',
+ 'K:C',
+ 'z6G2|[E2C,|\\'
+ ];
+ try {
+ abc.convert(data.join('\n'));
+ } catch (e) {
+ assert.match(e, 'SyntaxError');
+ }
+ }
+});
\ No newline at end of file