Class: DTAS::Sink

Inherits:
Object
  • Object
show all
Includes:
Command, Process, Serialize, WritableIter
Defined in:
lib/dtas/sink.rb

Overview

this is a sink (endpoint, audio enters but never leaves)

Constant Summary collapse

SINK_DEFAULTS =
COMMAND_DEFAULTS.merge({
  "name" => nil, # order matters, this is first
  "command" => "exec play -q $SOXFMT -",
  "prio" => 0,
  "nonblock" => false,
  "pipe_size" => nil,
  "active" => false,
  "respawn" => false,
})
DEVFD_RE =
%r{/dev/fd/([a-zA-Z]\w*)\b}
SIVS =

order matters for Ruby 1.9+, this defines to_hsh serialization so we can make the state file human-friendly

%w(name env command prio nonblock pipe_size active)

Constants included from Process

Process::PIDS

Constants included from Command

Command::COMMAND_DEFAULTS

Instance Attribute Summary collapse

Attributes included from WritableIter

#on_writable

Attributes included from Command

#command, #env, #pid, #spawn_at, #to_io

Class Method Summary collapse

Instance Method Summary collapse

Methods included from WritableIter

#writable_iter, #writable_iter_init

Methods included from Serialize

#ivars_to_hash

Methods included from Process

#dtas_spawn, #qx, reaper, #xs

Methods included from Command

#command_init, #command_string, #kill

Constructor Details

#initializeSink

Returns a new instance of Sink.



42
43
44
45
46
# File 'lib/dtas/sink.rb', line 42

def initialize
  command_init(SINK_DEFAULTS)
  writable_iter_init
  @sink = self
end

Instance Attribute Details

#activeObject

boolean



16
17
18
# File 'lib/dtas/sink.rb', line 16

def active
  @active
end

#nameObject

Returns the value of attribute name.



17
18
19
# File 'lib/dtas/sink.rb', line 17

def name
  @name
end

#nonblockObject

Returns the value of attribute nonblock.



18
19
20
# File 'lib/dtas/sink.rb', line 18

def nonblock
  @nonblock
end

#prioObject

:nodoc:



15
16
17
# File 'lib/dtas/sink.rb', line 15

def prio
  @prio
end

#respawnObject

Returns the value of attribute respawn.



19
20
21
# File 'lib/dtas/sink.rb', line 19

def respawn
  @respawn
end

Class Method Details

.load(hash) ⇒ Object



54
55
56
57
58
59
60
61
62
# File 'lib/dtas/sink.rb', line 54

def self.load(hash)
  sink = new
  return sink unless hash
  (SIVS & hash.keys).each do |k|
    sink.instance_variable_set("@#{k}", hash[k])
  end
  sink.valid_name?(sink.name) or raise ArgumentError, "invalid sink name"
  sink
end

Instance Method Details

#on_death(status) ⇒ Object



70
71
72
# File 'lib/dtas/sink.rb', line 70

def on_death(status)
  super
end

#parse(str) ⇒ Object



64
65
66
67
68
# File 'lib/dtas/sink.rb', line 64

def parse(str)
  inputs = {}
  str.scan(DEVFD_RE) { |w| inputs[w[0]] = nil }
  inputs
end

#spawn(format, opts = {}) ⇒ Object



74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/dtas/sink.rb', line 74

def spawn(format, opts = {})
  raise "BUG: #{self.inspect}#spawn called twice" if @pid
  rv = []

  pclass = @nonblock ? DTAS::PipeNB : DTAS::Pipe

  cmd = command_string
  inputs = parse(cmd)

  if inputs.empty?
    # /dev/fd/* not specified in the command, assume one input for stdin
    r, w = pclass.new
    w.pipe_size = @pipe_size if @pipe_size
    inputs[:in] = opts[:in] = r
    w.sink = self
    rv << w
  else
    # multiple inputs, fun!, we'll tee to them
    inputs.each_key do |name|
      r, w = pclass.new
      w.pipe_size = @pipe_size if @pipe_size
      inputs[name] = r
      w.sink = self
      rv << w
    end
    opts[:in] = "/dev/null"

    # map to real /dev/fd/* values and setup proper redirects
    cmd = cmd.gsub(DEVFD_RE) do
      read_fd = inputs[$1].fileno
      opts[read_fd] = read_fd # do not close-on-exec
      "/dev/fd/#{read_fd}"
    end
  end

  @pid = dtas_spawn(format.to_env.merge!(@env), cmd, opts)
  inputs.each_value { |rpipe| rpipe.close }
  rv
end

#to_hashObject



114
115
116
# File 'lib/dtas/sink.rb', line 114

def to_hash
  ivars_to_hash(SIVS)
end

#to_hshObject



118
119
120
# File 'lib/dtas/sink.rb', line 118

def to_hsh
  to_hash.delete_if { |k,v| v == SINK_DEFAULTS[k] }
end

#valid_name?(s) ⇒ Boolean

allow things that look like audio device names (“hw:1,0” , “/dev/dsp”) or variable names.

Returns:

  • (Boolean)


50
51
52
# File 'lib/dtas/sink.rb', line 50

def valid_name?(s)
  !!(s =~ %r{\A[\w:,/-]+\z})
end