Использование обложек PortAudio в рубине для записи звука в .wav

Недавно я играл с рубином, и я решил запустить простой проект для записи ruby ​​ script, который записывает линейный звук в файл .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

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

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

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

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