Thanks so much for your help, I sort-of got it working in a LiveBook notebook now using this code
Mix.install([{:membrane_ffmpeg_swresample_plugin, "~> 0.16.1"}])
defmodule Resampling.Pipeline do
use Membrane.Pipeline
alias Membrane.FFmpeg.SWResample.Converter
alias Membrane.{File, RawAudio}
@impl true
def handle_init(_ctx, _opts) do
structure = [
child(:file_src, %File.Source{location: "/tmp/elixir_converted_from_twilio.pcm"})
|> child(:converter, %Converter{
input_stream_format: %RawAudio{channels: 1, sample_format: :s16le, sample_rate: 8_000},
output_stream_format: %RawAudio{channels: 1, sample_format: :f32le, sample_rate: 16_000}
})
|> child(:file_sink, %File.Sink{location: "/tmp/elixir_converted_from_twilio_16khz_f32.pcm"})
]
{[spec: structure, playback: :playing], nil}
end
@impl true
def handle_element_end_of_stream(:file_sink, _pad, _ctx_, _state) do
{[playback: :stopped], nil}
end
end
{:ok, pid, pid2} = Resampling.Pipeline.start_link()
It creates the output file with the correct format and I can use it as input for bumblebee. However, I still have a couple of questions
-
Is there an equivalent to the file plugin that lets me input and output a binary? I’m sorry if I missed this in the membrane docs somewhere
-
This minimal notebook above somehow ends up with each cell in “aborted” state, even though the output file is successfully created and the output of the cell looks good:
21:38:46.614 [debug] Pipeline start link: module: Resampling.Pipeline,
pipeline options: nil,
process options: []
21:38:46.630 [debug] <0.282.0>/ New spec #Reference<0.3194694823.2069889028.48896>
structure: [%Membrane.ChildrenSpec.StructureBuilder{children: [{:file_sink, %Membrane.File.Sink{location: "/tmp/elixir_converted_from_twilio_16khz_f32.pcm"}, %{get_if_exists: false}}, {:converter, %Membrane.FFmpeg.SWResample.Converter{input_stream_format: %Membrane.RawAudio{channels: 1, sample_rate: 8000, sample_format: :s16le}, output_stream_format: %Membrane.RawAudio{channels: 1, sample_rate: 16000, sample_format: :f32le}}, %{get_if_exists: false}}, {:file_src, %Membrane.File.Source{location: "/tmp/elixir_converted_from_twilio.pcm", chunk_size: 2048}, %{get_if_exists: false}}], links: [%{from: :converter, from_pad: :output, from_pad_props: %{options: []}, to: :file_sink, to_pad: :input, to_pad_props: %{auto_demand_size: nil, min_demand_factor: nil, options: [], target_queue_size: nil, throttling_factor: 1, toilet_capacity: nil}}, %{from: :file_src, from_pad: :output, from_pad_props: %{options: []}, to: :converter, to_pad: :input, to_pad_props: %{auto_demand_size: nil, min_demand_factor: nil, options: [], target_queue_size: nil, throttling_factor: 1, toilet_capacity: nil}}], status: :done, from_pad: :output, from_pad_props: %{options: []}, to_pad: :input, to_pad_props: %{auto_demand_size: nil, min_demand_factor: nil, options: [], target_queue_size: nil, throttling_factor: 1, toilet_capacity: nil}, link_starting_child: :file_sink}]
21:38:46.632 [debug] <0.282.0>/ Starting children: [%Membrane.ChildEntry{name: :file_src, module: Membrane.File.Source, options: %Membrane.File.Source{location: "/tmp/elixir_converted_from_twilio.pcm", chunk_size: 2048}, component_type: :element, pid: nil, clock: nil, sync: nil, spec_ref: #Reference<0.3194694823.2069889028.48896>, initialized?: false, ready?: false, terminating?: false}, %Membrane.ChildEntry{name: :converter, module: Membrane.FFmpeg.SWResample.Converter, options: %Membrane.FFmpeg.SWResample.Converter{input_stream_format: %Membrane.RawAudio{channels: 1, sample_rate: 8000, sample_format: :s16le}, output_stream_format: %Membrane.RawAudio{channels: 1, sample_rate: 16000, sample_format: :f32le}}, component_type: :element, pid: nil, clock: nil, sync: nil, spec_ref: #Reference<0.3194694823.2069889028.48896>, initialized?: false, ready?: false, terminating?: false}, %Membrane.ChildEntry{name: :file_sink, module: Membrane.File.Sink, options: %Membrane.File.Sink{location: "/tmp/elixir_converted_from_twilio_16khz_f32.pcm"}, component_type: :element, pid: nil, clock: nil, sync: nil, spec_ref: #Reference<0.3194694823.2069889028.48896>, initialized?: false, ready?: false, terminating?: false}]
21:38:46.632 [debug] <0.282.0>/ Starting child: name: :file_src, module: Membrane.File.Source
21:38:46.634 [debug] <0.282.0>/ subprocess supervisor Element start: :file_src
node: ,
module: Membrane.File.Source,
element options: %Membrane.File.Source{location: "/tmp/elixir_converted_from_twilio.pcm", chunk_size: 2048},
method: start
21:38:46.635 [debug] <0.282.0>/:file_src/ Initializing element: Membrane.File.Source, options: %Membrane.File.Source{location: "/tmp/elixir_converted_from_twilio.pcm", chunk_size: 2048}
21:38:46.637 [debug] <0.282.0>/ Starting child: name: :converter, module: Membrane.FFmpeg.SWResample.Converter
21:38:46.637 [debug] <0.282.0>/ subprocess supervisor Element start: :converter
node: ,
module: Membrane.FFmpeg.SWResample.Converter,
element options: %Membrane.FFmpeg.SWResample.Converter{input_stream_format: %Membrane.RawAudio{channels: 1, sample_rate: 8000, sample_format: :s16le}, output_stream_format: %Membrane.RawAudio{channels: 1, sample_rate: 16000, sample_format: :f32le}},
method: start
21:38:46.637 [debug] <0.282.0>/:converter/ Initializing element: Membrane.FFmpeg.SWResample.Converter, options: %Membrane.FFmpeg.SWResample.Converter{input_stream_format: %Membrane.RawAudio{channels: 1, sample_rate: 8000, sample_format: :s16le}, output_stream_format: %Membrane.RawAudio{channels: 1, sample_rate: 16000, sample_format: :f32le}}
21:38:46.637 [debug] <0.282.0>/ Starting child: name: :file_sink, module: Membrane.File.Sink
21:38:46.637 [debug] <0.282.0>/ subprocess supervisor Element start: :file_sink
node: ,
module: Membrane.File.Sink,
element options: %Membrane.File.Sink{location: "/tmp/elixir_converted_from_twilio_16khz_f32.pcm"},
method: start
21:38:46.637 [debug] <0.282.0>/:file_sink/ Initializing element: Membrane.File.Sink, options: %Membrane.File.Sink{location: "/tmp/elixir_converted_from_twilio_16khz_f32.pcm"}
21:38:46.637 [debug] <0.282.0>/:file_src/ Element initialized
21:38:46.638 [debug] <0.282.0>/:file_sink/ Element initialized
21:38:46.644 [debug] <0.282.0>/ Proceeding spec #Reference<0.3194694823.2069889028.48896> startup: initializing, dependent specs: MapSet.new([])
21:38:46.644 [debug] <0.282.0>/ Playing request, :stopped
21:38:46.645 [debug] <0.282.0>/ Setup
21:38:46.647 [debug] <0.282.0>/ Parent play
21:38:46.647 [debug] <0.282.0>/ Proceeding spec #Reference<0.3194694823.2069889028.48896> startup: initializing, dependent specs: MapSet.new([])
21:38:46.647 [debug] <0.282.0>/ Proceeding spec #Reference<0.3194694823.2069889028.48896> startup: initializing, dependent specs: MapSet.new([])
21:38:46.656 [debug] <0.282.0>/:converter/ Element initialized
21:38:46.656 [debug] <0.282.0>/ Proceeding spec #Reference<0.3194694823.2069889028.48896> startup: initializing, dependent specs: MapSet.new([])
21:38:46.656 [debug] <0.282.0>/ Spec #Reference<0.3194694823.2069889028.48896> status changed to initialized
21:38:46.656 [debug] <0.282.0>/ Spec #Reference<0.3194694823.2069889028.48896> status changed to linking internally
21:38:46.658 [debug] <0.282.0>/:file_src/ Element handle link on pad :output with pad :input of child :converter
21:38:46.659 [debug] <0.282.0>/:converter/ Element handle link on pad :input with pad :output of child :file_src
- Why are two pids returned? And why do I not need to call
.play()
on the pipeline as the membrane docs suggest here?
Thanks so much for your help, I really appreciate it <3