Revert "Sounds: survive an audio device that crashes the app on open"

This reverts commit c91d57633f.
This commit is contained in:
foxyz committed 2026-10-17 02:04:33 +03:00
1 parent 49fee1ca19
commit 8aa1186d29
5 files changed
+3 -92

No files matched your search

-2
View File
@@ -49,8 +49,6 @@ Future<void> main(List<String> args) async {
await AppSettings.instance.ensureLoaded();
// The composition root: every system, made once.
final graph = AppGraph.build();
// Sounds stay off after an audio-device crash (see SoundEngine.crashMark).
graph.sfx.engine.crashMark = '${dirs.support}${Platform.pathSeparator}audio-init.pending';
final ports = graph.ports;
// Close button / Alt+F4: the orderly close ([shutDownApp]), so others
// see "left", your server shuts down and no player outlives the engine.
@@ -1,5 +1,4 @@
import 'dart:async';
import 'dart:io';
import 'dart:typed_data';
import 'package:flutter_soloud/flutter_soloud.dart';
@@ -51,13 +50,6 @@ class SoundEngine {
Future<bool>? _ready;
var _disposed = false;
/// A file that exists only while the audio device is opening (set by the
/// app). Found at the next start, the last open took the app down (a
/// native crash in the driver or miniaudio, seen on Windows): sounds stay
/// off rather than crash every start, until [retry] (Settings' preview).
String? crashMark;
var _skipped = false;
/// Per slot: the loaded key and its sound (null: failed to load).
final Map<String, (String, Future<Object?>)> _slots = {};
/// Load names are unique: SoLoud hands back the old sound for a name it
@@ -69,43 +61,15 @@ class SoundEngine {
Future<bool> _init() async {
if (_disposed) return false;
final mark = crashMark == null ? null : File(crashMark!);
try {
if (mark != null && mark.existsSync()) {
_skipped = true;
_log.warning('no sounds: opening the audio device crashed the app last time '
'(${mark.path}); a sound preview in Settings tries again');
return false;
}
mark?.writeAsStringSync('opening the audio device');
} catch (_) {}
_log.info('opening the audio device');
try {
await _backend.init();
_log.info('audio device open');
return true;
} catch (e) {
_log.warning('no sounds: audio init failed: $e');
return false;
} finally {
try {
if (mark != null && mark.existsSync()) mark.deleteSync();
} catch (_) {}
}
}
/// Opens the device again after a crash mark kept sounds off (the user
/// asked for a sound). A real crash leaves the mark again.
void retry() {
if (!_skipped) return;
_skipped = false;
try {
final m = crashMark;
if (m != null) File(m).deleteSync();
} catch (_) {}
_ready = null;
}
/// Plays the sound of [slot] at [volume] (linear gain). [key] names what
/// [wav] renders: the same key reuses the loaded sound; another key, or
/// [fresh], renders and loads it again.
+2 -5
View File
@@ -37,11 +37,8 @@ class SynthSfx implements SfxService {
/// Plays [kind] even when it is switched off (Settings' preview), always
/// freshly rendered: an edited spec plays after a hot reload.
Future<void> preview(SfxKind kind, {int readyIndex = 1, SoundPreset? preset}) {
// Asked for: try the device again even after a crash kept it closed.
engine.retry();
return _play(kind, readyIndex, preset ?? presetFor(kind), fresh: true);
}
Future<void> preview(SfxKind kind, {int readyIndex = 1, SoundPreset? preset}) =>
_play(kind, readyIndex, preset ?? presetFor(kind), fresh: true);
/// One slot per sound and ready step: a new preset replaces it.
Future<void> _play(SfxKind kind, int readyIndex, SoundPreset preset, {bool fresh = false}) {
-43
View File
@@ -1,4 +1,3 @@
import 'dart:io';
import 'dart:typed_data';
import 'package:flutter_test/flutter_test.dart';
@@ -148,46 +147,4 @@ void main() {
expect(b.deinits, 1);
});
});
group('crash mark', () {
late Directory dir;
setUp(() => dir = Directory.systemTemp.createTempSync('sound_mark'));
tearDown(() => dir.deleteSync(recursive: true));
test('exists only while the device opens', () async {
final mark = '${dir.path}/audio-init.pending';
var seen = false;
final b = _MarkBackend(() => seen = File(mark).existsSync());
final e = SoundEngine(backend: b)..crashMark = mark;
expect(await e.warmUp(), isTrue);
expect(seen, isTrue, reason: 'there while opening');
expect(File(mark).existsSync(), isFalse, reason: 'gone once open');
});
test('left by a crash: sounds stay off, a preview tries again', () async {
final mark = '${dir.path}/audio-init.pending';
File(mark).writeAsStringSync('opening the audio device');
final b = _FakeBackend();
final e = SoundEngine(backend: b)..crashMark = mark;
await e.play('chat', 'k', _bytes, volume: 1);
expect(b.inits, 0, reason: 'never opened after a crash');
expect(b.plays, isEmpty);
e.retry();
await e.play('chat', 'k', _bytes, volume: 1);
expect(b.inits, 1);
expect(b.plays, hasLength(1));
expect(File(mark).existsSync(), isFalse);
});
});
}
class _MarkBackend extends _FakeBackend {
final void Function() onInit;
_MarkBackend(this.onInit);
@override
Future<void> init() async {
onInit();
await super.init();
}
}
+1 -6
View File
@@ -1946,12 +1946,7 @@ panel) and chat messages (Copy message, and a link in it).
switched off in `soundsOff`. The device opens 2 s after startup
(`warmUp`), so neither startup nor the first sound waits on it; if it
can't open, the engine logs `no sounds: audio init failed` once and drops
every play. Opening it is logged (`opening the audio device`,
`audio device open`) and leaves `<support>/audio-init.pending` until it
returns: a native crash there (a driver, miniaudio) leaves the file, and
the next start skips sounds (`no sounds: opening the audio device
crashed the app last time`) instead of crashing again; a sound preview
in Settings deletes it and tries once more. App close disposes the engine (deinit, 500 ms bound)
every play. App close disposes the engine (deinit, 500 ms bound)
before leaving the server. Settings → Notifications & sounds has the volume, the
overall sound set (`soundPreset`), and per sound a preview, its own set
(`soundPresetFor`, else the overall one) and a switch; a preview always