Skip to content

feat(audio): expose master output capture#466

Open
Slashpaf wants to merge 1 commit into
alnitak:mainfrom
Slashpaf:feature/master-output-tap
Open

feat(audio): expose master output capture#466
Slashpaf wants to merge 1 commit into
alnitak:mainfrom
Slashpaf:feature/master-output-tap

Conversation

@Slashpaf
Copy link
Copy Markdown

@Slashpaf Slashpaf commented May 17, 2026

Description

Adds an opt-in API for capturing the final SoLoud mixed output as interleaved float32 PCM.

The capture point is in the miniaudio backend immediately after soloud->mix(...), before the buffer is submitted to the playback device. This gives applications access to the same final master mix that is being played by the engine.

The implementation does not touch existing code, only new implementation that does not affect flutter_soloud if not enabled. Web returns notImplemented (not using miniaudio).

Public Dart API added:

  • SoLoud.instance.setOutputCaptureEnabled(...)
  • SoLoud.instance.readOutputCapture(maxFrames)
  • SoLoud.instance.getOutputCaptureAvailableFrames()
  • SoLoud.instance.getOutputCaptureDroppedFrames()
  • SoLoud.instance.getOutputCaptureFormat()

Use cases include local streaming, recording, remote monitoring, remote playback, integrations, and external visualization/analysis.

@Slashpaf
Copy link
Copy Markdown
Author

@alnitak Just adding a concrete use case for this PR.

In Audio Forge I use this to stream the exact final SoLoud master mix to remote listeners. The app mixes music, ambience, effects, filters, volume changes, etc. locally, then reads the final interleaved float32 PCM output and sends it to a room server for redistribution.

This is different from capturing one source or using visualization data: the app gets the same final post-mix audio that is sent to the playback device(s).

Minimal usage:

final soloud = SoLoud.instance;

final format = soloud.getOutputCaptureFormat();

soloud.setOutputCaptureEnabled(true, maxBufferedFrames: 44100 * 2);

Timer.periodic(const Duration(milliseconds: 20), (_) {
  final available = soloud.getOutputCaptureAvailableFrames();
  if (available == 0) return;

  final samples = soloud.readOutputCapture(2048); // interleaved float32 PCM
  sendToServer(samples, format.sampleRate, format.channels);
});

// Later:
soloud.setOutputCaptureEnabled(false);

This enables use cases like remote playback, recording the full app mix, monitoring, broadcast rooms, and external audio analysis/visualization, while staying fully opt-in.

@alnitak
Copy link
Copy Markdown
Owner

alnitak commented May 24, 2026

Hi @Slashpaf, sorry for the long wait! I had to think about this because it is a tricky feature.

A while back, I already thought about implementing this feat, but there were some problems to take into account. I thought about a buffer too, but I'd preferred to have a stream for listening to the new audio data (instead of a Timer.periodic for checking).

This should be a nice feature to have, there is also an issue asking for this. Please let me think what we can do about this a little more! For example would be nice to have the audio data in other formats (instead of the default SoLoud interleaved floats32), or maybe already compressed audio with the OGG format.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants