Repository navigation
feat: ✨ Add audio effects support for Android recordings - #499
lavigarg-simform wants to merge 2 commits into
Conversation
8f70ccb to
01e8203
Compare
vasu-nageshri-simform
left a comment
There was a problem hiding this comment.
Thanks for this. It's opt-in and the defaults are unchanged.
What I checked
- Built it and ran the example on an API 35 arm64 emulator across all 7
AndroidAudioSourcevalues, with and without effects, plus the WAV encoder. - Invalid sample rate fails loudly with a
PlatformException, as described. flutter analyzeis clean. A small test of the Dart JSON mapping passes (included below).
What I could not check
- The emulator exposes no NS/AEC/AGC, so
isAvailable()is false. Only the "effect unavailable, skip" path ran. The attach, enable and release path, and the actual noise/echo reduction, still need a check on a real device before merge. - I did not reproduce the noise/echo from #442 / #381 on
main, and I did not hit the newSTATE_INITIALIZEDguard.
Note
- This is stacked on
fix/I488_fix_android_release_crash, so that PR needs to land first.
Unrelated to this PR, worth a separate issue: during testing, stop() for AAC recordings intermittently never returned. It also happened with the default mic and no effects, and this PR doesn't touch encoders/, so it is likely pre-existing. I did not rerun on main. The likely cause is CommonEncoder.signalToStop() only setting isEncodingComplete. If the codec has already handed out an idle input buffer (currentInputBufferIndex >= 0), no further onInputBufferAvailable fires, so end-of-stream is never queued and the completion callback never runs.
Details are in the inline comments (none blocking). The tests box in the checklist is unchecked and the repo has no test/ directory, so here is a small test that could be added as test/android_audio_source_test.dart:
android_audio_source_test.dart
import 'package:audio_waveforms/audio_waveforms.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
test('native values match MediaRecorder.AudioSource', () {
expect(AndroidAudioSource.values.map((e) => e.toNativeValue()).toList(),
[0, 1, 5, 6, 7, 9, 10]);
});
test('defaults keep legacy behaviour', () {
final j = const RecorderSettings().androidToJson(path: 'p');
expect(j['audioSource'], 1);
expect(j['useNoiseSuppressor'], false);
expect(j['useEchoCanceler'], false);
expect(j['useAutoGainControl'], false);
});
test('opt-in flags serialised', () {
final j = const RecorderSettings(
androidEncoderSettings: AndroidEncoderSettings(
audioSource: AndroidAudioSource.voiceCommunication,
useNoiseSuppressor: true,
useEchoCanceler: true,
useAutoGainControl: true,
),
).androidToJson();
expect(j['audioSource'], 7);
expect([j['useNoiseSuppressor'], j['useEchoCanceler'], j['useAutoGainControl']], [true, true, true]);
});
test('iOS json unchanged (no android keys leak)', () {
final j = const RecorderSettings().iosToJson(path: 'p');
expect(j.containsKey('audioSource'), false);
});
}- Store each audio effect before enabling it so it is always released, and share one helper between the three effects - Release existing effects at the start of attachAudioEffects so a repeated init does not leak them - Fall back to MIC instead of DEFAULT when UNPROCESSED or VOICE_PERFORMANCE are unavailable, and use the MediaRecorder constants - Use 16000 Hz in the example for the voiceCommunication source - Document the PlatformException for unsupported source/sample rate - Apply formatter line collapsing in RecorderSettings.androidToJson
01e8203 to
289d2b6
Compare
Description
On Android, recordings made with this package often contain noticeable echo and background noise compared to iOS, which sounds clean for the same environment. The root cause is that Android capture always used the raw
MICsource with no device audio processing, while iOS routes through a voice pipeline that suppresses noise and echo by default.This PR gives Android recordings access to the same kind of device-level audio processing, all opt-in so existing behavior is unchanged.
AndroidAudioSource) so callers can route capture through the device voice pipeline (e.g.voiceCommunication) instead of the raw mic.All new settings default to off /
mic, so projects that do not opt in keep their current recording behavior. Note that automatic gain control alters captured amplitude and therefore the rendered waveform, so it is best avoided for music or level-sensitive recording.Checklist
fix:,feat:,docs:etc).docsand added dartdoc comments with///.examplesordocs.Breaking Change?
Related Issues
Closes #442
Closes #381