Class: Radio::Signal::ALSA

Inherits:
Object
  • Object
show all
Defined in:
lib/radio/signals/alsa.rb

Constant Summary collapse

CP_REGEX =
/:\s*(capture|playback)\s*(\d+)\s*$/
CD_REGEX =
/^(\d+)-(\d+):\s*/

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options) ⇒ ALSA

Returns a new instance of ALSA.



74
75
76
77
78
79
80
81
# File 'lib/radio/signals/alsa.rb', line 74

def initialize options
  @stream = ::ALSA::PCM::Capture.new
  @stream.open options[:id]
  if input = options[:input]
    @channel_i = input[0]
    @channel_q = input[1]
  end
end

Class Method Details

.devicesObject



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/radio/signals/alsa.rb', line 45

def self.devices
  return {} unless defined? ::ALSA::PCM
  # Funky read to get around old Linux bug
  # http://kerneltrap.org/mailarchive/git-commits-head/2009/4/17/5510664
  pcm = ::File.open('/proc/asound/pcm') do |io|
    io.read_nonblock 32768
  end
  result = {}
  pcm.each_line do |line|
    capture = 0
    2.times do
      line.gsub! CP_REGEX, ''
      capture = $2.to_i if $1 == 'capture'
    end
    line.gsub! CD_REGEX, ''
    device = "hw:#{$1.to_i},#{$2.to_i}"
    ::ALSA::PCM::Capture.open(device) do |stream|
      params = stream.hardware_parameters
      result[device] = {
        name: line,
        rates: [params.sample_rate],
        input: params.channels,
        output: 0
      }
    end rescue nil
  end
  result
end

.statusObject



27
28
29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/radio/signals/alsa.rb', line 27

def self.status
  if defined? ::ALSA::PCM
    return "Loaded: %d devices" % devices.count
  end
  unless defined? @is_linux
    @is_linux = (`uname`.strip == 'Linux') rescue false
  end
  return "Unsupported: requires Linux" unless @is_linux
  if defined? ::ALSA
    'Unavailable: install ALSA to your OS'
  else
    'Unavailable: gem install ruby-alsa'
  end
end

Instance Method Details

#in(samples) ⇒ Object



96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/radio/signals/alsa.rb', line 96

def in samples
  out=nil
  buf_size = @stream.hw_params.buffer_size_for(samples)
  FFI::MemoryPointer.new(:char, buf_size) do |buffer|
    @stream.read_buffer buffer, samples
    out = buffer.read_string(buf_size)
    NArray.to_na(out, NArray::SINT).to_f.div! 32767
  end
  stream_channels = @stream.hardware_parameters.channels
  sample_size = buf_size / samples / stream_channels
  out = case sample_size
  when 1 then NArray.to_na(out,NArray::BYTE).to_f.collect!{|v|(v-128)/127}
  when 2 then NArray.to_na(out,NArray::SINT).to_f.div! 32767
  # when 3 then NArray.to_na(d,NArray::???).to_f.collect!{|v|(v-8388608)/8388607}
  else
    raise "Unsupported sample size: #{@sample_size}" 
  end
  return out if channels == 1 and stream_channels == 1
  out.reshape! stream_channels, out.size/stream_channels
  if channels == 1
    out[@channel_i,true]
  else
    c_out = NArray.scomplex out[0,true].size
    c_out[0..-1] = out[@channel_i,true]
    c_out.imag = out[@channel_q,true]
    c_out
  end
end

#input_channelsObject



87
88
89
90
# File 'lib/radio/signals/alsa.rb', line 87

def input_channels
  return 2 if @channel_q and @stream.hardware_parameters.channels > 1
  1
end

#output_channelsObject



92
93
94
# File 'lib/radio/signals/alsa.rb', line 92

def output_channels
  0
end

#rateObject



83
84
85
# File 'lib/radio/signals/alsa.rb', line 83

def rate
  @stream.hardware_parameters.sample_rate
end

#stopObject



125
126
127
# File 'lib/radio/signals/alsa.rb', line 125

def stop
  @stream.close
end