Repository navigation
[Feature Request / Bug]: Support non-WAV / browser audio uploads (WebM/Opus, MP3, OGG) on /v1/audio/transcriptions for Open WebUI compatibility
#541
Replies: 28 comments 1 reply
|
I see the issue. Let me think about it. This could be part of the longer-term plan. |
thanks - another possible request, support wyoming protocol for direct compatibility with home assistant. Currently, I have to use https://github.com/roryeckel/wyoming_openai to connect any openai-compatible TTS/STT to home assistant. Having it directly incorporated somehow would be amazing! |
|
The question I’m considering is not what to support, but how to support it. The core server should stay lightweight and avoid forcing users to compile unnecessary dependencies, while features that are better suited to the frontend or can be handled there easily should be implemented as extensions in the server. That will likely require some redesign of the server architecture. That’s why MP3 support (https://github.com/0xShug0/audio.cpp/tree/ci/new-feature-mp3) was never merged. |
|
I was trying out audio.cpp with open-webui and came across this issue as well. Maybe an extension would make sense or something optional if you don't want to add it by default to the core server @0xShug0 , and at least an option in the config to support this or that format. The docs can then show that the dependency for the extension needs to be installed to support other audio file types. |
|
In the meantime I wrote a quick proxy to convert to wav, maybe that's also useful for others until the feature is added here :) |
|
As a side note, it seems that open-webui already converts webm to mp3, so audio.cpp would only need to support mp3 -> wav in this case. |
Thanks for the pointer! I'm thinking about the same direction. I feel the easier and clean solution is adding a "middleware" to the server which is opt-in,and decoupled from the server. |
|
A preliminary suggestion: add a compile option to decide whether to enable the decoder, generate a standalone file, and let audiocpp_server check at runtime whether the decoder is present and reference it if so? |
We definitely need a compile option if we go with the middleware/proxy route. But it should be transparent to the core server. I.e., the server should continue to operate on the same normalized audio input, while the optional middleware handles format detection/decoding before forwarding the request. |
|
I use the hviske-5.3 model for Danish transcription. It fully kills my DGX Spark if I throw a MP3 file (53mb) at it through the built-in webui. No error messages, just dead spark ready for power cycle. So some handling of non-wav is required. |
|
I tried to connect audio.cpp to my hermes agent and, on the reverse side for TTS, it also complained about .wav. I had it summarize the problem and need: DescriptionThe TTS endpoint This is the output-side counterpart to #313 (which is the input-side: Why it matters (concrete client: Hermes Agent)Hermes's built-in OpenAI-compatible TTS provider calls
Because the server always returns WAV:
So today, correct behavior depends on the client papering over the server's WAV-only output. The clean fix is for the server to honor Steps to Reproduce
Root CauseThe speech handler writes its PCM buffer out through the WAV writer only; there is no encoder dispatch on Suggested FixesSolution 1 (recommended): in-tree encoders, matching #313's decoder approach
Solution 2 (fallback): server-side transcode bridge
Minimum acceptable behavior (if full encoders are out of scope):
Acceptance Criteria
|
|
@ampersandru Quick side question, how did you manage to make local openai compatible TTS/STT work with Hermes / how does your config.yaml and .env look like for it? It kept ignoring them and falling back on local default when I tried. You can PM me on discord @h3ll3r_ to not spam here if you want. |
I actually just asked hermes to set it to my openai-compatible TTS server, gave it the server url, api key, model, and voice. I checked config.yaml and hermes had updated it to: i have it just output to .wav for now, and luckily discord, where I have hermes live, supports .wav uploads. I think hermes just warned about .wav only because a lot of people use Telegram to connect Hermes. |
|
Grmpf, that's what I have as well, I'll have to investigate more why it's not working. And you have STT working with a similar config? |
|
@ampersandru @arty-hlr @IIIIIllllIIIIIlllll @osos Okay I have something you can test. Check #330 This adds an opt-in in-process server frontend pipeline. The core server stays WAV/text in and WAV/text out, while selected frontend modules can pre-process requests or post-process responses. This keeps client-specific extensions out of the core server. New format converters, text normalization, request rewrites, or response adapters can be added as separate modules with their own dependencies and explicit contracts. When compiling, you can select exactly which frontend modules to include, so unused extensions and their dependencies are not built into the default server. The first modules support MP3/FLAC input for ASR and response_format=mp3 output for TTS. The frontend build logic is now isolated so future extensions are scoped to:
A new frontend module should add:
The top-level CMakeLists.txt does not need more frontend-specific logic beyond the existing include and configure call. |
|
@0xShug0, probably going a week until I can test it out. Thank you. If I understand the patch right it does not alter the api's. I would in any event suggest that the api's hard validate the input files (in STT mode) against a list of supported format - preferable with the magic bytes kind of validation, and then return a valid failure 403-ish response about the format not supported. Same goes if the requested response (in TTS mode) is formats not supported as output. |
|
I just created a Windows CPU build based on the "server-frontend-module" branch and tested it with my local Open WebUI, unfortunately I'm still getting the same "500 internal server error" message as before when using the transcription endpoint. I first modified the build_windows.ps1 to incorporate the new build arguments and then used it for the build (with preset windows-cpu-release and target audiocpp_server). In the CMakeFiles directory, there is the "audiocpp_server_frontends.dir" containing the "audio_decode.cpp.obj", so it should be included in the build. Maybe I'm just doing something wrong. In my opinion, the frontend modules should be included by default in the pre compiled release packages and Docker images (or perhaps in variants that include the frontend). While it's nice to be able to choose which modules you want to include before compilation, many people either don't know how to make their own builds, don't want to or don't have everything set up (the build environment). I think there is a significant demand for a good (more or less all in one) STT/TTS solution that also includes a server (it was hard to find). It would be a real shame if it could not be use with external applications like Open WebUI simply because the frontend is missing. |
|
@Tech-no-1 Can you confirm whether Open WebUI actually sends MP3/FLAC in your test, instead of WebM/Ogg/Opus? The current test branch only supports MP3/FLAC input and MP3 output. Codec/frontend dependencies are not core model runtime dependencies. Shipping all of them by default would make every release package carry optional dependencies that only some integrations need. However, following llama.cpp’s approach and bundling a minimal frontend module with lightweight dependencies, such as miniaudio, is probably fine. The current prebuilts mostly target UI users, where these features can be added more easily in the UI if needed. |
Oh, I think I see the problem now. If the output is also supposed to be in MP3, I probaply need to include the 'mp3_encode' module in the build as well. I am now getting the following error during build though: libmp3lame-dev appears to be a Debian package (I'm on Windows) and lame/lame.h is missing from the build environment.
Yes, I understand that and I'm aware that most users are simply using the UI (which is excellent btw), a minimal frontend for core/basic compatibility with external services via the API endpoints would be nice though. |
This is mostly just me being lazy. A better solution might be to use a small vendored library for encoding, but I haven’t started looking into it yet. Many factors to consider/test: size, portability, license, maturity, maintenance burden etc. You can see the issue here: we may need to add different dependencies that may have duplicated or unnecessary functionality just for certain fronend features. It took me some time to extract the tokenizer from llama.cpp to avoid depending on llama.cpp (and SentencePiece may be next cleanup target), so I'd rather avoid going back in the opposite direction. Another issue is that it’s really hard to guarantee that random dependencies will behave correctly across different environments and configurations. Some models do use runtime dependencies, but they are scoped to individual models and have safe, generic fallbacks. The maintenance effort should go into the framework, rather than external dependencies. I’m now leaning more toward separating the frontend module from audio.cpp entirely into its own repo. That would give users more freedom to customize and extend it based on their needs ("plugins"), while keeping the main audio.cpp repo focused and limited to a minimal set of vendored dependencies. |
|
Updated the PR: |
Okay, so first of all, the Windows build is working now. However, audiocpp_server fails to start because the "libmp3lame.dll" is missing. I simply copied the dll from mp3lame and the server started. I haven't tested whether MP3 output actually works yet, because Open WebUI accepts WAV. Transcoding still isn't working and returns the "500 internal server error". I can confirm that Open WebUI converts the audio to MP3 first and stores that file in cache. Is there a way to get a verbose/debug log, because even with --log I get nothing. |
|
@Tech-no-1 added temp logs. You should see something like. I don't know how Open WebUI works. ChatGPT said it may be because you sent a chunked MP3 without a Content-Length header. |
|
Here are the relevant parts of the logs, I've changed the file paths and URLs. Browser request to Open WebUI: Open WebUI: Audio.cpp: Part from the Open WebUI Traceback: This is a snippet from Open WebUIs 'audio.py' (transcribe_openai), seems like they include a 'file' field in the chunked request: Audio.cpp log with JSON request instead of default chunked/multipart: |
|
@Tech-no-1 The branch has been updated to handle Open WebUI no For the json failure, the issue is a schema mismatch: Open WebUI’s JSON transcription request does not match the JSON schema currently accepted by audio.cpp. |
|
Yes, transcription is working now! (with chunked upload)
By the way, in my opinion, Cohere Transcribe is one of the best transcription models currently available (very accurate), it might be worth supporting it at some point. I also tested TTS with Open WebUI, by default it requests/accepts WAV and transcodes it to MP3 afterwards. To test the MP3 output, I explicitly used "response_format": "mp3" in the request JSON and it seems to work as well. Open WebUI did not transcode the responses into MP3. I think you still need to add the "libmp3lame.dll" to the "package_windows_prebuilt" script (Copy-RequiredDll) so that it gets copied from mp3lame (at least for Windows builds). Otherwise the server won't start if you make a build with the 'mp3_encode' frontend module. |
|
Call it a day. Now that the frontend is a submodule in another repo https://github.com/0xShug0/audio.cpp-server-frontends, it's more decoupled from the main audio.cpp. I also added WebSocket support. If anyone would like to help maintain that repo, let me know. The main audio.cpp prebuilt binaries would stay as they are and would not include the frontend module. The front repo prebuilts would include all the modules and dependencies I'm not a CI expert, but I think it should be possible. Runtime LAME can be replaced with vendored lib, as I'm more comfortable letting the submodule pull in random dependencies. @Tech-no-1 Thanks for the pointer! I added the model to TODOs. |
|
Sorry, just circling back to this now, somehow I wasn't subscribed to the issue. I'll test it this week on mac and let you know. |
Uh oh!
There was an error while loading. Please reload this page.
Description
Currently, the
POST /v1/audio/transcriptionsendpoint strictly expects raw WAV PCM audio uploads (RIFF....WAVEheader).When integrating
audio.cppwith popular web clients like Open WebUI, transcription fails because modern web browsers (Chrome, Edge, Firefox, Safari) using the HTML5MediaRecorderAPI record microphone input inaudio/webm;codecs=opus(oraudio/ogg). Browsers do not support recording native WAV viaMediaRecorder.As a result, sending microphone recordings from Open WebUI to any ASR model (e.g.
parakeet_tdt,granite5asr,whisper) fails with:Error transcribing chunk: External: 500, message='Internal Server Error', url='http://:8080/v1/audio/transcriptions'In contrast, clients that buffer and convert audio to WAV beforehand (such as Home Assistant via
wyoming_openaiorcurl -F file=@sample.wav) succeed with exceptional sub-30ms latency.Steps to Reproduce
audiocpp_serverwith any ASR model (e.g.,parakeet_tdtorgranite5asr):{ "models": [ { "id": "parakeet_tdt", "family": "parakeet_tdt", "path": "/models/parakeet-tdt-0.6b-v3-q8_0.gguf", "task": "asr", "mode": "offline" } ] }Root Cause
handle_transcription_multipart in app/server/runtime.cpp checks is_wav_upload_filename and directly parses bytes using engine::audio::read_wav_f32(), rejecting .webm, .mp3, .ogg, and .m4a files.
When browsers send MediaRecorder chunks or blobs (often named audio.webm or blob), the server throws an unhandled exception or returns a 400/500 error instead of decoding the compressed audio stream.
Suggested Fixes & Solutions
Solution 1: Lightweight In-Tree Decoders (Recommended)
Embed header-only C/C++ audio decoders (similar to how ggml or llama.cpp bundle dependencies) so audio.cpp remains self-contained without external runtime dependencies:
miniaudio (Public Domain / MIT, single-file) — handles MP3, FLAC, WAV, and Vorbis decoding natively.
opusfile / libopus — decodes WebM/Ogg Opus streams directly to 16 kHz float32 PCM in memory.
Solution 2: Optional Server-Side Transcoder Bridge
If external tools are acceptable when present, allow audiocpp_server to detect and pipe incoming non-WAV multipart bytes through ffmpeg or libavcodec to convert to 16 kHz mono WAV PCM in memory before running inference.
Solution 3: Graceful Handling of Empty / Incomplete Chunks
When MediaRecorder begins recording, the initial chunk may sometimes be 0 bytes or header-incomplete. Returning {"text": ""} (HTTP 200) instead of throwing an unhandled exception ensures the client does not crash before the full audio is delivered.
All reactions