Skip to content

Commit

Permalink
fix fatal error on lack of device on linux
Browse files Browse the repository at this point in the history
  • Loading branch information
domgetter committed Aug 13, 2014
1 parent 765f881 commit da4a734
Show file tree
Hide file tree
Showing 8 changed files with 89 additions and 40 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@ branches:
- master
- io
before_install:
- sudo apt-get install libasound2 libasound2-dev alsa-utils
- sudo apt-get install libasound2 libasound2-dev
1 change: 0 additions & 1 deletion Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,3 @@ if defined? RSpec
end
end
task default: :spec
task test: :spec
34 changes: 34 additions & 0 deletions doc/overview.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
Device
Abstracts physical devices
it can be opened
it can be closed
it can be written to
in that you write to its buffer
it can be flushed
in that its buffer is flushed
it can be played
in that its buffer is written to and immediately flushed
you can write any Data to it
it has a identification

Data
Abstracts real world sound data
it can have various formats
it can be writte to a device
it can have data which corresponds to its format

Format
Abstracts real world data interpretation
it can be PCM or MIDI
if it's PCM
it has a sample rate
it is has some number of channels
it has a bit depth per sample
it has a bit rate per second
if it's MIDI
I have no idea what MIDI looks like


Consider extending corresponding class to platform in lib/sound.rb
so that in lib/sound/device.rb I can write open(args) instead of Win32::PCM.open
I like this because it takes away platfrom decision logic from Sound::Device.
4 changes: 4 additions & 0 deletions lib/sound.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
if OS.windows?
require 'sound/win32/sound'
elsif OS.linux?
libasound_present = `which aplay`.eql? ""
unless libasound_present
warn("warning: sound output requires libasound2 and libasound2-dev packages")
end
require 'sound/linux/sound'
else
warn("warning: Sound output not yet implemented for this platform: #{OS.os}")
Expand Down
74 changes: 39 additions & 35 deletions lib/sound/device.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,16 @@
module Sound

@verbose = false
@testing = false
@no_device = false

class << self
attr_accessor :verbose, :testing
def testing?
testing
end
attr_accessor :verbose, :no_device
end

WAVE_MAPPER = -1

class NoDeviceError < RuntimeError; end

class Device

class Handle
Expand Down Expand Up @@ -44,7 +43,6 @@ class Buffer < Array; end
attr_accessor :closed, :id, :handle, :format

def initialize(direction = "w", id = nil, &block)

if OS.windows?
id ||= WAVE_MAPPER
elsif OS.linux?
Expand Down Expand Up @@ -105,9 +103,7 @@ def write(data = "beep boop")
@queue << Thread.new do
Thread.current[:async] = false
Thread.current[:data] = data
unless Sound.testing?
write_thread
end
write_thread
end
@mutex.unlock
puts "writing to queue of device '#{id}': #{data}" if Sound.verbose
Expand Down Expand Up @@ -154,19 +150,17 @@ def close
def flush
until @queue.empty?
output = @queue.shift
unless Sound.testing?
if output.kind_of? Thread
output[:stop] = false
puts "writing to device '#{id}': #{output[:data].class}" if Sound.verbose
output.run.join
else
output.each do |thread|
thread[:stop] = false
puts "writing to device '#{id}': #{thread[:data].class}" if Sound.verbose
thread.run
end
output.last.join if output.last.alive?
if output.kind_of? Thread
output[:stop] = false
puts "writing to device '#{id}': #{output[:data].class}" if Sound.verbose
output.run.join
else
output.each do |thread|
thread[:stop] = false
puts "writing to device '#{id}': #{thread[:data].class}" if Sound.verbose
thread.run
end
output.last.join if output.last.alive?
end
end
end
Expand All @@ -181,10 +175,14 @@ def write_thread
Thread.current[:stop] = true if Thread.current[:stop].nil?
if OS.windows? || OS.linux?
open_device
prepare_buffer
unless Sound.no_device
prepare_buffer
end
Thread.stop if Thread.current[:stop]
write_to_device
close_device
unless Sound.no_device
write_to_device
close_device
end
else
warn("warning: playback is not yet supported on this platform")
end
Expand All @@ -194,7 +192,11 @@ def open_device
if OS.windows?
Win32::Sound.waveOutOpen(handle.pointer, id, data.format.pointer, 0, 0, 0)
elsif OS.linux?
ALSA::PCM.open(handle.pointer, id, 0, ALSA::PCM::ASYNC)
begin
ALSA::PCM.open(handle.pointer, id, 0, ALSA::PCM::ASYNC)
rescue NoDeviceError
Sound.no_device = true
end
end
end

Expand All @@ -205,18 +207,16 @@ def prepare_buffer

buffer_length

params = FFI::MemoryPointer.new(:pointer)
ALSA::PCM.params_malloc(params_handle.pointer)
ALSA::PCM.params_any(handle.id, params_handle.id)

ALSA::PCM.params_malloc(params)
ALSA::PCM.params_any(handle.id, params.read_pointer)
ALSA::PCM.set_access(handle.id, params_handle.id, ALSA::PCM::SND_PCM_ACCESS_RW_INTERLEAVED)
ALSA::PCM.set_format(handle.id, params_handle.id, ALSA::PCM::SND_PCM_FORMAT_S16_LE)
ALSA::PCM.set_rate(handle.id, params_handle.id, data.format.sample_rate, 0)
ALSA::PCM.set_channels(handle.id, params_handle.id, 1)

ALSA::PCM.set_access(handle.id, params.read_pointer, ALSA::PCM::SND_PCM_ACCESS_RW_INTERLEAVED)
ALSA::PCM.set_format(handle.id, params.read_pointer, ALSA::PCM::SND_PCM_FORMAT_S16_LE)
ALSA::PCM.set_rate(handle.id, params.read_pointer, data.format.sample_rate, 0)
ALSA::PCM.set_channels(handle.id, params.read_pointer, 1)

ALSA::PCM.save_params(handle.id, params.read_pointer)
ALSA::PCM.free_params(params.read_pointer)
ALSA::PCM.save_params(handle.id, params_handle.id)
ALSA::PCM.free_params(params_handle.id)

ALSA::PCM.prepare(handle.id)

Expand Down Expand Up @@ -244,6 +244,10 @@ def handle
Thread.current[:handle] ||= Handle.new
end

def params_handle
Thread.current[:params_handle] ||= Handle.new
end

def data
Thread.current[:data]
end
Expand Down
10 changes: 9 additions & 1 deletion lib/sound/linux/sound.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class PCM
ffi_lib 'asound'
ffi_convention :stdcall

attach_function :open, :snd_pcm_open, [:pointer, :string, :int, :int], :int
attach_function :snd_pcm_open, [:pointer, :string, :int, :int], :int
attach_function :close, :snd_pcm_close, [:pointer], :int
attach_function :drain, :snd_pcm_drain, [:pointer], :int
attach_function :prepare, :snd_pcm_prepare, [:pointer], :int
Expand Down Expand Up @@ -124,6 +124,14 @@ class PCM
SND_PCM_ACCESS_RW_NONINTERLEAVED = 4
SND_PCM_ACCESS_LAST = SND_PCM_ACCESS_RW_NONINTERLEAVED

def self.open(*args)
output = `aplay -l 2>&1`
if output.match(/no soundcard/m)
raise NoDeviceError, "No sound devices present"
else
snd_pcm_open(args)
end
end
end
end
end
1 change: 1 addition & 0 deletions lib/sound/win32/sound.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ class Sound
attach_function :waveOutWrite, [:hwaveout, :pointer, :uint], :mmresult
attach_function :waveOutUnprepareHeader, [:hwaveout, :pointer, :uint], :mmresult
attach_function :waveOutClose, [:hwaveout], :mmresult

end

WAVE_FORMAT_PCM = 1
Expand Down
3 changes: 1 addition & 2 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
require 'sound'
Sound.testing = true
require 'sound'

0 comments on commit da4a734

Please sign in to comment.