part of 'command_executor.dart'; extension _CommandExecutorAudio on CommandExecutor { Future<_CommandResult> _playSound( RuntimeCommand command, _CommandContext context, RuntimeCommandHandle? handle, ) async { final audio = _audio; if (audio == null) { _emitCommandCompletion(command, context); return _CommandResult.completed; } final scope = _scopeFor(command, context, defaultTarget: false); final scopeEpoch = _scopeEpochFor(scope, context); if (!_scopeIsAlive(scope)) { return _CommandResult.cancelled; } final task = _registerTask(scope, handle); final playback = await audio.play( _requiredAudioResource(command), volume: _optionalVolume(command), ); if (_disposed || task.isCancelled || (handle?.isCancelled ?? false) || !_scopeIsAlive(scope)) { await playback?.cancel(); task.complete(_CommandResult.cancelled); return task.future; } if (playback == null) { task.complete(_CommandResult.cancelled); return task.future; } task.addCancelCallback(() { async.unawaited(playback.cancel()); }); await playback.done; if (_disposed || task.isCancelled || (handle?.isCancelled ?? false) || !_scopeIsAlive(scope)) { task.complete(_CommandResult.cancelled); return task.future; } _emitCommandCompletion( command, context.copyWith(scope: scope, scopeEpoch: scopeEpoch), ); task.complete(_CommandResult.completed); return task.future; } Future<_CommandResult> _playBgm( RuntimeCommand command, _CommandContext context, RuntimeCommandHandle? handle, ) async { final audio = _audio; if (audio == null) { _emitCommandCompletion(command, context); return _CommandResult.completed; } final scope = _scopeFor(command, context, defaultTarget: false); final scopeEpoch = _scopeEpochFor(scope, context); if (!_scopeIsAlive(scope)) { return _CommandResult.cancelled; } final channel = _audioChannel(command); final playback = await audio.playBgm( _requiredAudioResource(command), channel: channel, volume: _optionalVolume(command), loop: _optionalBool(command.payload['loop'], 'play_bgm.loop') ?? true, ); if (_disposed || (handle?.isCancelled ?? false) || !_scopeIsAlive(scope)) { await audio.stopBgm(channel: channel); return _CommandResult.cancelled; } if (playback == null) { return _CommandResult.cancelled; } _registerBgmChannel(channel: channel, scope: scope); handle?.addCancelCallback(() { _unregisterBgmChannel(channel); async.unawaited(audio.stopBgm(channel: channel)); }); _emitCommandCompletion( command, context.copyWith(scope: scope, scopeEpoch: scopeEpoch), ); return _CommandResult.completed; } Future<_CommandResult> _controlBgm( RuntimeCommand command, _CommandContext context, _BgmControl control, ) async { final audio = _audio; final channel = _audioChannel(command); if (audio != null) { switch (control) { case _BgmControl.pause: await audio.pauseBgm(channel: channel); case _BgmControl.resume: await audio.resumeBgm(channel: channel); case _BgmControl.stop: await audio.stopBgm(channel: channel); _unregisterBgmChannel(channel); } } _emitCommandCompletion(command, context); return _CommandResult.completed; } }