kevinschweikert
Hey folks,
i am trying to get Membrane and Whisper working together. For now, the goal is, to use Mebrane so stream a file into Whisper. I worked on the following Livebook but cannot figure out, why Whisper is not correctly transcribing.
When i pipe the output of the converter into portaudio it is very noisy. When i set the converter to s16le the PortAudio audio sounds fine! But when i use :f32le and write it to a PCM file and import it into Audacity, it sounds fine again, despite the PortAudio Sink is not.
Either way, Whisper is not translating correctly. Can you help me out, where the issue could be?
Mix.install(
[
{:membrane_core, "~> 0.12.7"},
{:membrane_file_plugin, "~> 0.15.0"},
{:membrane_mp4_plugin, "~> 0.27.0"},
{:membrane_fake_plugin, "~> 0.10.0"},
{:membrane_raw_audio_format, "~> 0.11.0"},
{:membrane_ffmpeg_swresample_plugin,
git: "https://github.com/membraneframework/membrane_ffmpeg_swresample_plugin",
branch: "master"},
{:membrane_aac_plugin, "~> 0.15.0"},
{:membrane_aac_fdk_plugin, "~> 0.15.1"},
{:membrane_portaudio_plugin, "~> 0.16.1"},
{:kino_bumblebee, "~> 0.3.0"},
{:exla, "~> 0.5.1"}
],
config: [nx: [default_backend: EXLA.Backend], membrane_core: [logger: [verbose: false]]]
)
#dependecies:
brew install fdk-aac
brew install portaudio
model = "tiny"
{:ok, model_info} = Bumblebee.load_model({:hf, "openai/whisper-#{model}"})
{:ok, featurizer} = Bumblebee.load_featurizer({:hf, "openai/whisper-#{model}"})
{:ok, tokenizer} = Bumblebee.load_tokenizer({:hf, "openai/whisper-#{model}"})
{:ok, generation_config} = Bumblebee.load_generation_config({:hf, "openai/whisper-#{model}"})
generation_config = Bumblebee.configure(generation_config, max_new_tokens: 100)
generation_config = %{
generation_config
| forced_token_ids: [
# tell whisper that spoken text is german
{1, Bumblebee.Tokenizer.token_to_id(tokenizer, "<|de|>")},
{2, Bumblebee.Tokenizer.token_to_id(tokenizer, "<|transcribe|>")}
]
}
serving =
Bumblebee.Audio.speech_to_text(model_info, featurizer, tokenizer, generation_config,
compile: [batch_size: 1],
defn_options: [compiler: EXLA]
)
Nx.Serving.start_link(serving: serving, name: WhisperServing)
defmodule WhisperSink do
use Membrane.Sink
require Logger
def_input_pad(:input, demand_unit: :buffers, accepted_format: _any)
@impl true
def handle_playing(_ctx, state) do
{[demand: :input], state}
end
@impl true
def handle_write_list(:input, buffers, _ctx, state) do
for buffer <- buffers do
data =
buffer.payload
|> Nx.from_binary(:f32)
Logger.info(inspect(Nx.Serving.batched_run(WhisperServing, data)))
end
{[demand: {:input, length(buffers)}], state}
end
end
defmodule Pipeline do
use Membrane.Pipeline
@in_file "/path/to/video.mp4"
@impl true
def handle_init(_context, _opts) do
structure = [
child(:file_source, %Membrane.File.Source{location: @in_file, seekable?: true})
|> child(:demuxer, %Membrane.MP4.Demuxer.ISOM{optimize_for_non_fast_start?: true})
|> via_out(Pad.ref(:output, 2))
|> child(:depayloader_audio, Membrane.MP4.Depayloader.AAC)
|> child(:audio_parser, %Membrane.AAC.Parser{
in_encapsulation: :none,
out_encapsulation: :ADTS
})
|> child(:aac_decoder, Membrane.AAC.FDK.Decoder)
|> child(:converter, %Membrane.FFmpeg.SWResample.Converter{
output_stream_format: %Membrane.RawAudio{
sample_format: :f32le,
sample_rate: 16_000,
channels: 1
}
})
# |> child(:speaker, Membrane.PortAudio.Sink),
|> child(:whisper, WhisperSink),
get_child(:demuxer)
|> via_out(Pad.ref(:output, 1))
|> child(:fake, Membrane.Fake.Sink.Buffers)
]
{[spec: structure, playback: :playing], %{}}
end
@impl true
def handle_element_end_of_stream(:file_sink, _pad, _ctx_, _state) do
IO.inspect("terminate")
{[terminate: :normal], nil}
end
@impl true
def handle_element_end_of_stream(_child, _pad, _ctx, _state) do
{[], nil}
end
def handle_child_notification(notification, _element, _context, state) do
# dbg(notification)
{[], state}
end
end
{:ok, _, _} = Pipeline.start_link()
Trending in Questions
Hello!
Suppose you are building workflow (order / task / payment) processing system with the following requirements:
Each workflow con...
New
I’m aware of 2 such projects, are there others?
git: GitHub - team-alembic/realworld: A fullstack Phoenix LiveView application with back...
New
Hi all,
I’m working on a Phoenix project where I need to make external HTTP requests and I’m trying to choose between Req and HTTPoison....
New
How do I tell Hologram not to escape HTML when substituting into templates?
{Textile.render(@product.long_description)}
escapes the HTM...
New
I’d like to create a file with 600 permissions (rw-------), but there’s a race condition if I create it and then change the permissions. ...
New
Hi,
we run different LLM models across a cluster, with each node loading a configurable subset. Model-specific queues let nodes consume ...
New
When I use Mix.install with a git dependency, using git@github.com:... how do I change the SSH identity used?
I’ve got two Github accoun...
New
Other Trending Topics
Hey there,
I haven’t seen a list of useful Elixir specific skills for the popular AI tools yet so I’m starting one here with my first.
...
New
Hi everyone :waving_hand:
I wanted to share something I’ve been building — NowBlind, a random chat app designed to help people discover ...
New
I fully migrated to my own harness from Anthropic/Gemini and I think it’s time to share it. Welcome DSH, the DeepSeek Harness, fully writ...
New
Hey. Is there anyone here who creates agents in their apps? Not talking about using agents, but creating them. I’m finding it pretty diff...
New
(I just needed to vent somewhere and LinkedIn is full of hope, or hype, I’m not sure which exactly)
AI dream has many faces, but general...
New
Localize is the next generation localisation library for Elixir. Think of it as ex_cldr version 3.0. The first version will be released ...
New
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #library
- #deployment
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #elixirconf
- #channels
- #exunit
- #code-sync
- #discussion
- #podcasts
- #javascript
- #onsite
- #dialyzer
- #docker
- #authentication
- #ai
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #blog-post
- #elixirconf-us
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #graphql
- #genstage
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #security
- #elixirconf-eu
- #api
- #forms
- #performance










Showing Posts 1 to 4- Show Best Posts
- Show All (oldest first)
- Show All (newest first)
mat-hek
Hi there, I’ll look into the converter & port audio issue. Regarding speech-to-text, have you tried this example?
kevinschweikert
Hi @mat-hek,
oh wow, thanks for the LiveBook example! I did not find it. After some trial and error and the membrane_transcription repo from @lawik i found out, that i obviously need to create a buffer, otherwise the length of the
Membrane.Buffer.payloadis to small for Whisper. The example would have saved me some timeI currently got this to work with a Whisper Serving startet in the Supervision tree and used manual demand. Otherwise the buffer would be too big for the Whisper model, because it can only handle a maximum chunk of 30s.
Again, thank you for your help!
mat-hek
Yeah, we’ll make those livebooks more discoverable. If the payload is too small/big, you can just concat/split it in the WhisperSink as well
However, to use automatic demands, you’d also have to move the logic from the task to the element or 
awaiton the task - otherwise, you can be flooded with dataBTW, you can use functions from Membrane.RawAudio — Membrane: raw audio format v0.12.3 instead of hardcoding sample rate & friends. Happy hacking
mat-hek
PortAudio bug fixed in Release v0.16.2 · membraneframework/membrane_portaudio_plugin · GitHub