Skip to content

Commit

Permalink
Trigger mixdown from the UI.
Browse files Browse the repository at this point in the history
  • Loading branch information
dodecaphonic committed Mar 14, 2014
1 parent a954d56 commit f5d6506
Show file tree
Hide file tree
Showing 18 changed files with 202 additions and 23 deletions.
1 change: 1 addition & 0 deletions bin/balladina
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ require "reel"
require_relative "../lib/balladina"
require_relative "../lib/balladina/endpoint"

Balladina::Configuration.load File.expand_path("../config/application.yml", __dir__)
Balladina::Endpoint.run
4 changes: 4 additions & 0 deletions config/application.sample.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
chunks_path: /tmp
mixdowns_path: /tmp/mixdowns
public_mixdowns_path: /var/www/balladina/public/mixdowns
4 changes: 4 additions & 0 deletions lib/balladina.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
require "logger"
require "fileutils"
require "tmpdir"
require "singleton"
require "yaml"

require_relative "balladina/recorder"
require_relative "balladina/chunk_writer"
Expand All @@ -12,3 +14,5 @@
require_relative "balladina/track_coordinator"
require_relative "balladina/board"
require_relative "balladina/mixdown"
require_relative "balladina/engineer"
require_relative "balladina/configuration"
19 changes: 15 additions & 4 deletions lib/balladina/board.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,16 @@ class Board
include Celluloid::Notifications

def initialize(options = {})
@name = SecureRandom.hex
@creates_tracks = options.fetch(:creates_tracks) { Track }
@creates_coordinators = options.fetch(:creates_coordinators) { TrackCoordinator }
@tracks = Hamster.set
@ready_ids = Hamster.set
@tracks = Hamster.set
@ready_ids = Hamster.set
@engineer = Engineer.new_link(Actor.current)
end

attr_reader :tracks, :ready_ids, :creates_tracks, :creates_coordinators
private :tracks, :ready_ids, :creates_tracks, :creates_coordinators
attr_reader :tracks, :ready_ids, :creates_tracks, :creates_coordinators, :engineer, :name
private :tracks, :ready_ids, :creates_tracks, :creates_coordinators, :engineer

def add_track(track_id, control_socket, data_socket)
track = creates_tracks.new(track_id, data_socket)
Expand Down Expand Up @@ -57,6 +59,15 @@ def stop_recording
publish "stop_recording"
end

def mixdown
mixdown_ready_tracks = tracks.map { |t| t.future.prepare_mixdown }
engineer.mixdown mixdown_ready_tracks
end

def mixdown_ready(public_path)
publish "download_mixdown", File.basename(public_path)
end

private
def create_track_coordinator(control_socket, track)
creates_coordinators.new(control_socket, track, Actor.current)
Expand Down
17 changes: 17 additions & 0 deletions lib/balladina/configuration.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
module Balladina
class Configuration
include Singleton

attr_accessor :mixdowns_path, :public_mixdowns_path, :chunks_path

def self.load(config_file)
config = self.instance

YAML.load(open(config_file)).each do |key, value|
config.public_send "#{key}=", value
end

config
end
end
end
33 changes: 33 additions & 0 deletions lib/balladina/engineer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
module Balladina
class Engineer
include Celluloid
include Celluloid::Notifications
include Celluloid::Logger

def initialize(board, options = {})
@board = board
@creates_mixdowns = options.fetch(:creates_mixdowns) { Mixdown }
@mixdowns_path = options.fetch(:mixdowns_path) {
Configuration.instance.public_mixdowns_path
}
end

attr_reader :creates_mixdowns, :board, :mixdowns_path
private :creates_mixdowns, :board, :mixdowns_path

def mixdown(future_tracks)
tracks_clips = future_tracks.inject({}) { |tcm, t| tcm.merge t.value }
mixdown_path = creates_mixdowns.create_for(board.name,
with_tracks: tracks_clips)

board.async.mixdown_ready copy_to_public_path(mixdown_path)
end

def copy_to_public_path(mixdown_path)
public_path = File.join(mixdowns_path, File.basename(mixdown_path))
FileUtils.cp mixdown_path, public_path

public_path
end
end
end
10 changes: 8 additions & 2 deletions lib/balladina/mixdown.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,17 @@ module Balladina
class Mixdown
def initialize(name, options = {})
@name = name
@target_path = options.fetch(:target_path) { Configuration.mixdowns_path }
@target_path = options.fetch(:target_path) {
Configuration.instance.mixdowns_path
}
end

attr_reader :name, :target_path

def self.create_for(board_name, with_tracks: {})
self.new(board_name).perform_on with_tracks
end

def perform_on(tracks_clips)
create_temp_mixdown_path

Expand All @@ -23,7 +29,7 @@ def perform_on(tracks_clips)

def join_clips(track_name, clips)
track_path = File.join(temp_mixdown_path, "#{track_name}.wav")
clip_paths = clips.join(" ")
clip_paths = clips.to_a.join(" ")

`sox #{clip_paths} #{track_path}`

Expand Down
6 changes: 4 additions & 2 deletions lib/balladina/recorder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ def initialize(track, socket, options = {})
@track = track
@socket = socket
writes_chunks = options.fetch(:writes_chunks) { ChunkWriter }
@writer = writes_chunks.supervise(track,
Dir.tmpdir + "/balladina/#{track.id}")
chunks_path = options.fetch(:chunks_path) {
Configuration.instance.chunks_path
}
@writer = writes_chunks.supervise(track, File.join(chunks_path,String(track.id)))
end

attr_reader :socket, :track, :writer
Expand Down
25 changes: 17 additions & 8 deletions lib/balladina/track.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,36 +8,45 @@ class Track
def initialize(id, socket, options = {})
@id = id
@socket = socket
@chunks = []
@chunks = Hamster.vector
@creates_recorders = options.fetch(:creates_recorders) { Recorder }
@leader = options.fetch(:leader, false)
@recorder = creates_recorders.new_link(Actor.current, socket)
@is_recording = false

@recorder.async.record
end

attr_reader :chunks, :socket, :id, :creates_recorders
private :socket, :creates_recorders

def start_recording
@recorder = creates_recorders.new_link(Actor.current, socket)
info "Track \##{id}: starting to record"
@recorder.async.record
@is_recording = true
end

def stop_recording
@recorder.terminate if @recorder
info "Track \##{id}: stopping recording"
@recorder = nil
@is_recording = false
end

def leader?; @leader; end

def recording?; !!@recorder; end
def recording?; @is_recording; end

def on_chunk(chunk_path)
chunks << chunk_path
if recording?
@chunks = @chunks << chunk_path
end
end

def prepare_mixdown
{ id => chunks }
end

def recorder_died(actor, reason)
@recorder = nil
@recorder = nil
@is_recording = false
end
end
end
8 changes: 8 additions & 0 deletions lib/balladina/track_coordinator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ def initialize(control_socket, track, board, options = {})
subscribe "peers_online", :notify_peers
subscribe "start_recording", :control_recording
subscribe "stop_recording", :control_recording
subscribe "download_mixdown", :download_mixdown
end

attr_reader :track, :board, :control_socket
Expand All @@ -31,6 +32,8 @@ def on_message(message)
board.async.notify_ready track
when "promote_leader"
board.async.promote_leader message["data"]
when "mixdown"
board.async.mixdown
end
end

Expand All @@ -43,6 +46,11 @@ def control_recording(msg)
track.async.public_send msg
end

def download_mixdown(msg, public_path)
info "==== DOWNLOAD MY MIX #{public_path}"
control_socket << { command: msg, data: public_path }.to_json
end

private
def remove_track_from_board
board.async.remove_track track
Expand Down
5 changes: 4 additions & 1 deletion lib/balladina_web/public/css/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,12 @@ body {
cursor: pointer;
}

#controls .icon {
line-height: 1.3em;
}

#controls .toggle-recording .icon {
color: #ff3a31;
line-height: 1.3em;
}

#controls .toggle-recording.recording {
Expand Down
3 changes: 3 additions & 0 deletions lib/balladina_web/public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@
<div class="toggle-recording button">
<i class="fa fa-circle icon"></i>
</div>
<div class="start-mixdown button">
<i class="fa fa-cloud-download icon"></i>
</div>
</script>

<script type="text/template" id="track-template">
Expand Down
25 changes: 23 additions & 2 deletions lib/balladina_web/public/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ var Balladina = {
PROMOTE_DATA: { command: "promote_to_data", data: -1 },
PROMOTE_CONTROL: { command: "promote_to_control", data: -1 },
START_RECORDING: { command: "start_recording" },
STOP_RECORDING: { command: "stop_recording" }
STOP_RECORDING: { command: "stop_recording" },
START_MIXDOWN: { command: "mixdown" }
}
};

Expand Down Expand Up @@ -107,6 +108,7 @@ Balladina.models.SignalingChannel = Backbone.Model.extend({
};

this.attributes.control.on("change:signalRecording", this._signalRecording, this);
this.attributes.control.on("change:mixdown", this._startMixdown, this);
},

_updateTracks: function(clientIds) {
Expand Down Expand Up @@ -159,6 +161,12 @@ Balladina.models.SignalingChannel = Backbone.Model.extend({
{ silent: true});
},

_startMixdown: function(c, shouldMixdown) {
if (shouldMixdown) {
this.socket.send(JSON.stringify(Balladina.messages.START_MIXDOWN));
}
},

actOn: function(message) {
switch (message.command) {
case "peers_online":
Expand All @@ -170,15 +178,24 @@ Balladina.models.SignalingChannel = Backbone.Model.extend({
case "stop_recording":
this._stopRecording();
break;
case "download_mixdown":
this._downloadMixdown(message.data);
break;
}
},

_downloadMixdown: function(publicPath) {
console.log(publicPath);
this.attributes.control.set({ mixdown: false }, { silent: true });
}
});

Balladina.views.Controls = Backbone.View.extend({
el: "#controls",

events: {
"click .toggle-recording": "_signalRecording"
"click .toggle-recording": "_signalRecording",
"click .start-mixdown": "_startMixdown"
},

initialize: function() {
Expand All @@ -191,6 +208,10 @@ Balladina.views.Controls = Backbone.View.extend({
return this;
},

_startMixdown: function() {
this.model.set("mixdown", true);
},

_toggleRecording: function(control, isRecording) {
var uiToggle = this.$el.find(".toggle-recording");

Expand Down
Empty file.
43 changes: 43 additions & 0 deletions spec/balladina/engineer_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
require_relative "../spec_helper"
require "tempfile"

describe Balladina::Engineer, actor_system: :global do
let(:creates_mixdowns) { double("Mixdown") }
let(:board) { double("board", name: "my-crazy-board") }
let(:temp_path) { File.join(Dir.tmpdir, "balladina-engineer") }

before do
@engineer = Balladina::Engineer.new(board, creates_mixdowns: creates_mixdowns,
mixdowns_path: temp_path)
FileUtils.mkdir_p temp_path
end

after do
@engineer.terminate
FileUtils.rm_rf temp_path
end

describe "coordinating mixdowns" do
let(:track1) { { "track-1" => [:clip] } }
let(:track2) { { "track-2" => [:clip] } }
let(:future_tracks) { [double(value: track1), double(value: track2)] }

before do
@temp_path = Tempfile.new("a_mixdown")
board.should_receive(:async).and_return board
creates_mixdowns.should_receive(:create_for)
.with("my-crazy-board", with_tracks: track1.merge(track2))
.and_return @temp_path.path
end

after do
@temp_path = nil
end

it "starts a Mixdown and notifies Board when it's finished" do
filename = File.basename(@temp_path.path)
board.should_receive(:mixdown_ready).with File.join(temp_path, filename)
@engineer.mixdown future_tracks
end
end
end
3 changes: 2 additions & 1 deletion spec/balladina/recorder_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@

before do
@recorder = Balladina::Recorder.new(track, socket,
writes_chunks: MockWriter)
writes_chunks: MockWriter,
chunks_path: Dir.tmpdir)
end

after do
Expand Down
Loading

0 comments on commit f5d6506

Please sign in to comment.