diff --git a/acid2.rb b/acid2.rb new file mode 100755 index 0000000..d98c5cb --- /dev/null +++ b/acid2.rb @@ -0,0 +1,76 @@ +# Welcome to Sonic Pi v2.6 +# Acid +# Coded by Sam Aaron +# Hacked around by RJS & JHR + +require '/home/pi/gps/gps.rb' + +gps = Gps::Receiver.create('gpsd',:host => 'localhost', :port => 2947) + +gps.start + +sleep 2 + +puts gps.latitude + + + +use_debug false +load_sample :bd_fat + +8.times do + sample :bd_fat, amp: (line 0, 5, steps: 8).tick + sleep 0.5 +end + +live_loop :drums do + sample :bd_fat, amp: 5 + sleep 0.5 +end + +live_loop :acid do + cue :foo + 4.times do |i| + long = (gps.longitude.abs * 10**9) % 100 + use_random_seed long + 16.times do + use_synth :tb303 + play chord(:e5, :minor).choose, attack: 0, release: 0.1, cutoff: rrand_i(50, 90) + i * 10 + sleep 0.125 + end + end + + cue :bar + use_synth :tb303 + 32.times do |i| + gspeed = gps.speed.modulo(1) + puts gspeed + play chord(:b4, :minor).choose, attack: 0, release: 0.05, cutoff: rrand_i(70, 98) + i, res: gspeed + sleep 0.125 + end + + cue :baz + with_fx :reverb, mix: 0.3 do |r| + 32.times do |m| + control r, mix: 0.3 + (0.5 * (m.to_f / 32.0)) unless m == 0 if m % 8 == 0 + use_synth :prophet + play chord(:e6, :minor).choose, attack: 0, release: 0.08, cutoff: rrand_i(110, 130) + sleep 0.125 + end + end + + cue :quux + in_thread do + use_random_seed 668 + slat = (gps.latitude.abs * 10**7).modulo(1) + with_fx :slicer, mix: 0.75, wave: 3, phase: slat do + 16.times do + use_synth :tb303 + play chord(:d3, :major).choose, attack: 0, release: 0.1, cutoff: rrand(50, 100) + sleep 0.25 + end + end + end + + sleep 4 +end diff --git a/gps/gps.rb b/gps/gps.rb new file mode 100644 index 0000000..472df0b --- /dev/null +++ b/gps/gps.rb @@ -0,0 +1,21 @@ +$:.unshift File.dirname(__FILE__) + +module Gps +end + +begin + require "rubygems" +rescue LoadError +end + +if Object.const_defined?("Gem") + begin + require "gem_plugin" + rescue LoadError + end +end +require "gps/fix" +require "gps/receiver" +require "gps/receivers/gpsd" + +GemPlugin::Manager.instance.load "gps" => GemPlugin::INCLUDE if Object.const_defined?("GemPlugin") \ No newline at end of file diff --git a/gps/gps/fix.rb b/gps/gps/fix.rb new file mode 100644 index 0000000..33d933f --- /dev/null +++ b/gps/gps/fix.rb @@ -0,0 +1,13 @@ +# Module representing all data in a GPS fix. +module Gps::Fix + attr_reader :last_tag, :timestamp, :timestamp_error_estimate, :latitude, :longitude, :altitude, :horizontal_error_estimate, :vertical_error_estimate, :course, :speed, :climb, :course_error_estimate, :speed_error_estimate, :climb_error_estimate, :satellites + + def initialize(*args) + @altitude = 0 + @latitude = 0 + @longitude = 0 + @speed = 0 + @course = 0 + @satellites = 0 + end +end \ No newline at end of file diff --git a/gps/gps/receiver.rb b/gps/gps/receiver.rb new file mode 100644 index 0000000..6405771 --- /dev/null +++ b/gps/gps/receiver.rb @@ -0,0 +1,112 @@ +# Represents a provider of GPS fixes and a dispatcher of event callbacks for applications needing +# such fixes. +module Gps + class Receiver + include Fix + + def initialize(options = {}) + super + @last_latitude = @last_longitude = 0 + @position_change_threshold = 0 + @last_speed = 0 + @last_course = 0 + @last_altitude = 0 + @last_satellites = 0 + end + + # Factory for creating a +Receiver+. + # Accepts an implementation and a hash of options, both optional. If no + # implementation is provided, the first is used by default. Implementations are in + # the module +Gps::Receivers+. + def self.create(arg = nil, args = {}) + implementation = arg.respond_to?(:capitalize)? Receivers.const_get(arg.capitalize): Receivers.const_get(Receivers.constants[0]) + options = arg.kind_of?(Hash)? arg: args + implementation.new(options) + end + + # Sets the position from an array of the form [latitude, longitude], calling the + # _on_position_change_ callback if necessary. + def position=(value) + @latitude = value[0] + @longitude = value[1] + call_position_change_if_necessary + end + + # Called on position change, but only if the change is greater than +threshold+ degrees. + # The block receives the amount of change in degrees. + def on_position_change(threshold = 0, &block) + @position_change_threshold = threshold + @on_position_change = block + end + + # Called on speed change. + def on_speed_change(&block) + @on_speed_change = block + end + + # Called on course change. + def on_course_change(&block) + @on_course_change = block + end + + # Called when the altitude changes. + def on_altitude_change(&block) + @on_altitude_change = block + end + + # Called when the number of visible satellites changes. + def on_satellites_change(&block) + @on_satellites_change = block + end + + # Override this in children. Opens the connection, device, etc. + def start + @thread = Thread.new do + while true + update + end + end + end + + # Override this in children. Closes the device, connection, etc. and stops updating. + def stop + @thread.kill if @thread + @thread = nil + end + + # Returns _true_ if started, _false_ otherwise. + def started? + !@thread.nil? && @thread.alive? + end + + # Override this in children, setting fix variables from the GPS receiver. + # Here we dispatch to callbacks if necessary. + def update + call_position_change_if_necessary + @on_speed_change.call if @on_speed_change and @speed != @last_speed + @last_speed = @speed + @on_course_change.call if @on_course_change and @course != @last_course + @last_course = @course + @on_altitude_change.call if @on_altitude_change and @altitude != @last_altitude + @last_altitude = @altitude + @on_satellites_change.call if @on_satellites_change and @satellites != @last_satellites + @last_satellites = @satellites + end + + private + def call_position_change_if_necessary + if position_change > @position_change_threshold + @on_position_change.call(position_change) if @on_position_change + @last_latitude = @latitude + @last_longitude = @longitude + end + end + + def position_change + (@latitude-@last_latitude).abs+(@longitude-@last_longitude).abs + end + end + + module Receivers + end +end \ No newline at end of file diff --git a/gps/gps/receivers/gpsd.rb b/gps/gps/receivers/gpsd.rb new file mode 100644 index 0000000..e073f46 --- /dev/null +++ b/gps/gps/receivers/gpsd.rb @@ -0,0 +1,52 @@ +require "socket" +require 'json' + +# Represents a +Receiver+ that obtains information from GPSD. +module Gps::Receivers + class Gpsd < Gps::Receiver + attr_reader :host, :port + + # Accepts an options +Hash+ consisting of the following: + # * _:host_: The host to which to connect + # * _:port_: The port to which to connect + def initialize(options = {}) + super + @host ||= options[:host] ||= "localhost" + @port = options[:port] ||= 2947 + end + + def start + @socket = TCPSocket.new(@host, @port) + @socket.puts("?WATCH={\"enable\":true,\"json\": true}") + super + end + + def update + line = @socket.gets.chomp + return if !line + jline = JSON.parse(line) + #puts jline.inspect + + msgtype = jline['class'] + case msgtype + when 'TPV' + @last_tag = jline['tag'] + @timestamp = jline['time'] + @timestamp_error_estimate = jline['ept'] + @latitude = jline['lat'] + @longitude = jline['lon'] + @altitude = jline['lon'] + @horizontal_error_estimate = jline['epx'] + @vertical_error_estimate = jline['epv'] + @course = jline['track'] + @speed = jline['speed'] + @climb = jline['climb'] + @course_error_estimate = jline['epd'] + @speed_error_estimate = jline['eps'] + @climb_error_estimate = jline['epc'] + when 'SKY' + @satellites = jline['satellites'] + end + end + end +end diff --git a/gps/gps/version.rb b/gps/gps/version.rb new file mode 100644 index 0000000..3f04292 --- /dev/null +++ b/gps/gps/version.rb @@ -0,0 +1,9 @@ +module Gps #:nodoc: + module VERSION #:nodoc: + MAJOR = 0 + MINOR = 0 + TINY = 1 + + STRING = [MAJOR, MINOR, TINY].join('.') + end +end diff --git a/startup.sh b/startup.sh new file mode 100755 index 0000000..8c85c77 --- /dev/null +++ b/startup.sh @@ -0,0 +1,6 @@ +#!/bin/bash -x + +sleep 20 +amixer cset numid=3 1 +amixer sset PCM 100% +cat acid2.rb|sonic_pi