Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
PIFM_PATH=/home/pi/PiFmRds/src/pi_fm_rds
NODE_ENV=production
HOST=localhost
30 changes: 29 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,32 @@ mpd2fm tracks *tinyfm* tracks and turn them into FM modulations.
# Requirements

- a [Raspberry Pi with an antenna/metalic wire](http://makezine.com/projects/make-38-cameras-and-av/raspberry-pirate-radio/#steppers);
- a [tinyfm sandbox](https://github.com/tinyfm/sandbox)-like Pi configuration;
- a [tinyfm sandbox](https://github.com/tinyfm/sandbox)-like Pi configuration;

# Install

```bash
npm install -g git+https://github.com/tinyfm/mpd2fm.git
```

# Usage

Use `mpd2fm --help` to display help about available commands.

## init.d config

Prints out the Debian `initd` config.

```bash
mpd2fm --initd-config
```

Pretty much handy to daemonize the command-line tool on a Raspberry Pi.

## Playback events to FM

Listens to tinyfm audio playback events (backed by [mopidy](https://www.mopidy.com/)) and broadcasts them as an FM signal.

```
mpd2fm --base-dir /usr/share/tinyfm --start
```
57 changes: 57 additions & 0 deletions bin/cli.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#!/usr/bin/env node

'use strict';

var fs = require('fs');
var path = require('path');
var Mopidy = require('mopidy');
var encoder = require('../lib/encoder');

var argv = require('yargs')
.options('initd-config', {
alias: 'i',
boolean: true,
default: false,
description: 'Displays out the related Debian initd script.'
})
.options('base-dir', {
alias: 'd',
default: '/usr/share/tinyfm',
description: 'Base directory to resolve event media filepath from.'
})
.options('start', {
boolean: true,
default: false,
description: 'Starts to media playback to FM broadcasting server.'
})
.strict()
.argv;

if (argv.initdConfig) {
return fs.createReadStream(path.join(__dirname, '..', 'dist', 'init.d', 'mpd2fm'))
.pipe(process.stdout);
}

if (argv.start) {
var mopidy = new Mopidy({
callingConvention: 'by-position-or-by-name',
webSocketUrl: 'ws://' + (process.env.HOST || 'localhost') + ':6680/mopidy/ws/'
});

var broadcast = encoder({ baseDir: argv.baseDir, env: 'test' });
var currentStream;

mopidy.on("event:trackPlaybackStarted", function (event) {
// catch the uri
var filepath = event.tl_track.track.uri.split(":").pop();
console.log("-----------------------------------");
console.log("New song:", filepath);

currentStream = broadcast(filepath);
}, logErrors);
}


function logErrors(err){
console.error(err);
}
17 changes: 17 additions & 0 deletions bin/test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
'use strict';

/*
This test case demonstrates our ability to swap a stream with another one.

$ node bin/test.js audioFile1.wav audioFile2.wav

It will play an audio file and swap with a second audio file a second later.
*/

var broadcast = require('../lib/encoder')({ env: 'test' });

broadcast(process.argv[2]);

setTimeout(function(){
broadcast(process.argv[3])
}, 1000);
10 changes: 5 additions & 5 deletions dist/init.d/mpd2fm
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,17 @@
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: tinyfm audio playback to FM transmission.
# Description:
# Description: tinyfm audio playback to FM transmission.
### END INIT INFO

# Author: tinyfm

# PATH should only include /usr/* if it runs after the mountnfs.sh script
PATH=/sbin:/usr/sbin:/bin:/usr/bin
DESC="Description of the service"
PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/bin
DESC=tinyfm audio playback to FM transmission."
NAME=mpd2fm
DAEMON=/usr/local/bin/node
DAEMON_ARGS="/home/pi/mpd2fm/script.js"
DAEMON=$(which mpd2fm)
DAEMON_ARGS="--start"
PIDFILE=/var/run/$NAME.pid
SCRIPTNAME=/etc/init.d/$NAME

Expand Down
55 changes: 55 additions & 0 deletions lib/encoder.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
'use strict';

var child_process = require('child_process');
var path = require('path');
var fs = require('fs');

// path is expected to be something like /home/pi/PiFmRds/src/pi_fm_rds (on a tinyfm vanilla install)
var pifmPath = process.env.PIFM_PATH || path.join('/', 'home', 'pi', 'PiFmRds', 'src', 'pi_fm_rds');
var activeStream;

module.exports = function(options){
var resolvedOptions = options || {};
var pipeline = resolveStreamer(resolvedOptions.env || process.env.NODE_ENV || null);

return function streamFrom(filepath){
clearStream(function(){
var resolvedFilepath = path.resolve(resolvedOptions.baseDir, filepath);
var stream = fs.createReadStream(resolvedFilepath);

stream.pipe(pipeline.stdin);

return stream;
});
};
};

function clearStream(fn){
if (activeStream){
activeStream.unpipe();

activeStream.close(function(){
activeStream = fn();
});
}
else {
process.nextTick(function(){
activeStream = fn();
});
}
}

function resolveStreamer(env){
var args = [];

if (env !== 'production'){
args = ['play', ['-']];
}
else {
var pifmPath = require.resolve(pifmPath);

args = [pifmPath, ["-audio", "-"]];
}

return child_process.spawn.apply(child_process, args);
}
8 changes: 6 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@
"name": "mpd2fm",
"version": "1.0.0",
"description": "tinyfm mdp to FM transmitter bridge.",
"main": "index.js",
"main": "./lib/encoder.js",
"dependencies": {
"mopidy": "^0.4.1",
"when": "^3.6.3"
"when": "^3.6.3",
"yargs": "^1.3.3"
},
"bin": {
"mpd2fm": "./bin/cli.js"
},
"devDependencies": {},
"scripts": {
Expand Down
69 changes: 0 additions & 69 deletions script.js

This file was deleted.