Использование обертки PortAudio в ruby ​​для записи звука в формат .wav - PullRequest
11 голосов
/ 06 февраля 2012

Недавно я играл с ruby, и я решил начать простой проект по написанию сценария ruby, который записывает линейный звук в файл .wav. Я обнаружил, что ruby ​​не обеспечивает очень хороший доступ к аппаратным устройствам (и, вероятно, не должен), но это делает PortAudio, и я обнаружил отличную оболочку для PA здесь (это не драгоценный камень, Я думаю, потому что он использует Ruby ffi для подключения к PortAudio, а библиотека PA может быть в разных местах). Я копался в документации и примерах PortAudio, чтобы понять, как работает PA. Я не писал и не читал C годами.

У меня возникают трудности с тем, какие параметры я должен передавать потоку во время создания и в буфере во время создания. Например, что такое frame и как оно связано с другими параметрами, такими как channel и sample rate. Я совершенно новичок в аудио-программировании в целом, поэтому, если кто-нибудь может подсказать мне некоторые общие уроки и т. Д., Касающиеся звука на уровне устройства, я был бы признателен.

ruby-portaudio предоставляет один пример, который создает поток и буфер, записывает синусоидальную волну в буфер, а затем отправляет буфер в поток для воспроизведения. Некоторые из рубинов, с которыми я столкнулся в этом примере, в частности, блок цикла.

  PortAudio.init

  block_size = 1024
  sr   = 44100
  step = 1.0/sr
  time = 0.0

  stream = PortAudio::Stream.open(
             :sample_rate => sr,
             :frames => block_size,
             :output => {
               :device => PortAudio::Device.default_output,
               :channels => 1,
               :sample_format => :float32
              })

  buffer = PortAudio::SampleBuffer.new(
             :format   => :float32,
             :channels => 1,
             :frames   => block_size)

  playing = true
  Signal.trap('INT') { playing = false }
  puts "Ctrl-C to exit"

  stream.start

  loop do
    stream << buffer.fill { |frame, channel|
      time += step
      Math.cos(time * 2 * Math::PI * 440.0) * Math.cos(time * 2 * Math::PI)
    }

    break unless playing
  end

  stream.stop

Если я собираюсь записывать, я должен читать поток в буфер, затем манипулировать этим буфером и записывать его в файл, верно?

Кроме того, если я здесь лаю не на том дереве, и есть более простой способ сделать это (в рубине), было бы неплохо какое-то направление.

1 Ответ

3 голосов
/ 21 октября 2012

Давайте сначала уточнить условия, о которых вы спрашивали. Для этого я постараюсь объяснить аудио конвейер в упрощенном виде. Когда вы генерируете звук, как в вашем примере, ваша звуковая карта периодически запрашивает кадры (= буферы = блоки) из вашего кода, который вы заполняете сэмплами. Частота дискретизации определяет, сколько сэмплов вы предоставляете за секунду, и, следовательно, скорость воспроизведения сэмплов. Размер кадра (= размер буфера = размер блока) определяет, сколько сэмплов вы предоставляете за один запрос со звуковой карты. Буфер обычно довольно мал, потому что размер буфера напрямую влияет на задержку (большой буфер => высокая задержка), а большие массивы могут быть медленными (особенно медленные ruby-массивы).

Подобные вещи случаются при записи звука с вашей звуковой карты. Ваша функция вызывается время от времени, и сэмплы с микрофона обычно передаются в качестве аргумента функции (или даже просто ссылки на такой буфер). Затем вы должны обработать эти образцы, например, записав их на диск.

Я знаю, что мысль "делать все в Ruby" довольно заманчива, потому что это такой прекрасный язык. Когда вы планируете выполнять обработку звука в режиме реального времени, я бы порекомендовал переключиться на скомпилированный язык (C, C ++, Obj-C, ...). Они могут обрабатывать аудио намного лучше, потому что они гораздо ближе к оборудованию, чем Ruby, и, следовательно, в целом быстрее, что может быть проблемой при обработке звука. Вероятно, это также является причиной того, что вокруг так мало аудио библиотек Ruby, так что, возможно, Ruby просто не подходит для этой работы.

Кстати, я попробовал ruby-portaudio, ffi-portaudio, а также ruby-audio, и ни один из них не работал должным образом на моем Macbook (пытался сгенерировать синусоидальную волну), что, к сожалению, еще раз показывает, что Ruby не способен справиться с этим (пока?).

...