dtas.git  about / heads / tags
duct tape audio suite for *nix
blob c4810327eb00302e420275df16ca289527ceea04 2914 bytes (raw)
$ git show v0.16.1:lib/dtas/sink.rb	# shows this blob on the CLI

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 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
 73
 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
113
114
115
116
117
 
# Copyright (C) 2013-2016 all contributors <dtas-all@nongnu.org>
# License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
# frozen_string_literal: true
require 'yaml'
require_relative '../dtas'
require_relative 'pipe'
require_relative 'process'
require_relative 'command'
require_relative 'format'
require_relative 'serialize'

# this is a sink (endpoint, audio enters but never leaves), used by -player
class DTAS::Sink # :nodoc:
  attr_accessor :prio    # any Integer
  attr_accessor :active  # boolean
  attr_accessor :name
  attr_accessor :pipe_size
  attr_accessor :nonblock

  include DTAS::Command
  include DTAS::Process
  include DTAS::Serialize

  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,
  })

  DEVFD_RE = %r{/dev/fd/([a-zA-Z]\w*)\b}

  # order matters for Ruby 1.9+, this defines to_hsh serialization so we
  # can make the state file human-friendly
  SIVS = %w(name env command prio nonblock pipe_size active)

  def initialize
    command_init(SINK_DEFAULTS)
    @sink = self
  end

  # allow things that look like audio device names ("hw:1,0" , "/dev/dsp")
  # or variable names.
  def valid_name?(s)
    !!(s =~ %r{\A[\w:,/-]+\z})
  end

  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

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

  def on_death(status)
    super
  end

  def sink_spawn(format, opts = {})
    raise "BUG: #{self.inspect}#sink_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] = DTAS.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(&:close)
    rv
  end

  def to_hash
    ivars_to_hash(SIVS)
  end

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

git clone git://80x24.org/dtas.git
git clone https://80x24.org/dtas.git