From 0cfbaf6a0daa956d5bf85121b722bf0b06258649 Mon Sep 17 00:00:00 2001 From: Marco Bavagnoli Date: Fri, 12 Jul 2024 11:36:01 +0200 Subject: [PATCH 1/6] init params & voice groups #102 #100 #78, #92 --- example/tests/tests.dart | 137 ++++++++++++---------- lib/src/bindings/bindings_player.dart | 49 +++++++- lib/src/bindings/bindings_player_ffi.dart | 90 +++++++++++++- lib/src/bindings/bindings_player_web.dart | 47 +++++++- lib/src/bindings/js_extension.dart | 25 +++- lib/src/enums.dart | 18 +++ lib/src/soloud.dart | 60 +++++++++- src/bindings.cpp | 117 +++++++++++------- src/ffi_gen_tmp.h | 31 ++++- src/player.cpp | 34 +++++- src/player.h | 35 +++++- 11 files changed, 517 insertions(+), 126 deletions(-) diff --git a/example/tests/tests.dart b/example/tests/tests.dart index 5688e73..5cd3fe7 100644 --- a/example/tests/tests.dart +++ b/example/tests/tests.dart @@ -1,7 +1,9 @@ +// ignore_for_file: public_member_api_docs, sort_constructors_first import 'dart:async'; import 'dart:ui'; import 'package:flutter/material.dart'; + import 'package:flutter_soloud/flutter_soloud.dart'; import 'package:flutter_soloud/src/bindings/soloud_controller.dart'; @@ -11,12 +13,6 @@ enum TestStatus { failed, } -typedef TestFunction = ({ - String name, - Future Function() callback, - TestStatus status, -}); - /// A GUI for tests. /// /// Run this with `flutter run tests/tests.dart`. @@ -39,6 +35,17 @@ void main() { ); } +class Test { + Test({ + required this.name, + required this.callback, + this.status = TestStatus.none, + }); + final String name; + final Future Function() callback; + TestStatus status; +} + class MyHomePage extends StatefulWidget { const MyHomePage({super.key}); @@ -48,7 +55,7 @@ class MyHomePage extends StatefulWidget { class _MyHomePageState extends State { final output = StringBuffer(); - final List tests = []; + final List tests = []; final textEditingController = TextEditingController(); @override @@ -57,51 +64,17 @@ class _MyHomePageState extends State { /// Add all testing functions. tests.addAll([ - ( - name: 'testProtectVoice', - status: TestStatus.none, - callback: testProtectVoice, - ), - ( - name: 'testAllInstancesFinished', - status: TestStatus.none, - callback: testAllInstancesFinished, - ), - ( - name: 'testCreateNotes', - status: TestStatus.none, - callback: testCreateNotes, - ), - ( - name: 'testPlaySeekPause', - status: TestStatus.none, - callback: testPlaySeekPause, - ), - ( - name: 'testPan', - status: TestStatus.none, - callback: testPan, - ), - ( - name: 'testHandles', - status: TestStatus.none, - callback: testHandles, - ), - ( - name: 'loopingTests', - status: TestStatus.none, - callback: loopingTests, - ), - ( - name: 'testSynchronousDeinit', - status: TestStatus.none, - callback: testSynchronousDeinit, - ), - ( - name: 'testAsynchronousDeinit', - status: TestStatus.none, - callback: testAsynchronousDeinit, - ), + Test(name: 'testProtectVoice', callback: testProtectVoice), + Test( + name: 'testAllInstancesFinished', callback: testAllInstancesFinished), + Test(name: 'testCreateNotes', callback: testCreateNotes), + Test(name: 'testPlaySeekPause', callback: testPlaySeekPause), + Test(name: 'testPan', callback: testPan), + Test(name: 'testHandles', callback: testHandles), + Test(name: 'loopingTests', callback: loopingTests), + Test(name: 'testSynchronousDeinit', callback: testSynchronousDeinit), + Test(name: 'testAsynchronousDeinit', callback: testAsynchronousDeinit), + Test(name: 'testVoiceGroups', callback: testVoiceGroups), ]); } @@ -187,11 +160,7 @@ class _MyHomePageState extends State { ..write(await tests[index].callback()) ..write('===== PASSED! =====\n\n') ..writeln(); - tests[index] = ( - name: tests[index].name, - status: TestStatus.passed, - callback: tests[index].callback, - ); + tests[index].status = TestStatus.passed; textEditingController.text = output.toString(); debugPrint(output.toString()); if (context.mounted) setState(() {}); @@ -209,11 +178,7 @@ class _MyHomePageState extends State { ..writeln() ..writeln(); // ignore: parameter_assignments - tests[index] = ( - name: tests[index].name, - status: TestStatus.failed, - callback: tests[index].callback, - ); + tests[index].status = TestStatus.failed; textEditingController.text = output.toString(); debugPrint(output.toString()); if (context.mounted) setState(() {}); @@ -676,3 +641,51 @@ Future testSynchronousDeinit() async { return StringBuffer(); } + +/// Test voice groups. +Future testVoiceGroups() async { + await initialize(); + + final currentSound = + await SoLoud.instance.loadAsset('assets/audio/explosion.mp3'); + + /// Start playing sounds in pause state to get their handles. + final h1 = await SoLoud.instance.play(currentSound, paused: true); + final h2 = await SoLoud.instance.play(currentSound, paused: true); + final h3 = await SoLoud.instance.play(currentSound, paused: true); + final h4 = await SoLoud.instance.play(currentSound, paused: true); + + final group = SoLoud.instance.createVoiceGroup(); + assert(!group.isError, 'Failed to create voice group!'); + + var isValid = SoLoud.instance.isVoiceGroup(group); + assert(isValid, 'Voice group created but it is not valid!'); + + var isEmpty = SoLoud.instance.isVoiceGroupEmpty(group); + assert(isEmpty, 'Voice group just created but it is not empty!'); + + /// Add all voices to the group. + SoLoud.instance.addVoiceToGroup(group, [h1, h2, h3, h4]); + isEmpty = SoLoud.instance.isVoiceGroupEmpty(group); + assert(!isEmpty, 'Voices added to the group, but the group is empty!'); + + /// Start playing the voices in the group. + SoLoud.instance.setPause(group, false); + + await delay(4000); + + /// Check if group is empty after playing voices. + isEmpty = SoLoud.instance.isVoiceGroupEmpty(group); + assert( + isEmpty, + 'Voices added and finished to play, but the group is not empty!', + ); + + /// Destroy the group + SoLoud.instance.destroyVoiceGroup(group); + isValid = SoLoud.instance.isVoiceGroup(group); + assert(!isValid, 'Voice group destroy failed!'); + + deinit(); + return StringBuffer(); +} diff --git a/lib/src/bindings/bindings_player.dart b/lib/src/bindings/bindings_player.dart index 79e8b9a..7364fb1 100644 --- a/lib/src/bindings/bindings_player.dart +++ b/lib/src/bindings/bindings_player.dart @@ -52,9 +52,19 @@ abstract class FlutterSoLoud { /// Initialize the player. Must be called before any other player functions. /// + /// [sampleRate] the sample rate. Usually is 22050, 44100 (CD quality) + /// or 48000. + /// [bufferSize] the audio buffer size. Usually is 2048, but can be also + /// 512 when low latency is needed for example in games. + /// [channels] 1=mono, 2=stereo, 4=quad, 6=5.1, 8=7.1. + /// /// Returns [PlayerErrors.noError] if success. @mustBeOverridden - PlayerErrors initEngine(); + PlayerErrors initEngine( + int sampleRate, + int bufferSize, + Channels channels, + ); /// Must be called when the player is no more needed or when closing the app. @mustBeOverridden @@ -483,6 +493,43 @@ abstract class FlutterSoLoud { @mustBeOverridden void setMaxActiveVoiceCount(int maxVoiceCount); + ///////////////////////////////////////// + /// voice groups + ///////////////////////////////////////// + + /// Used to create a new voice group. Returns 0 if not successful. + SoundHandle createVoiceGroup(); + + /// Deallocates the voice group. Does not stop the voices attached to the + /// voice group. + /// + /// [handle] the group handle to destroy. + void destroyVoiceGroup(SoundHandle handle); + + /// Adds voice handle to the voice group. The voice handles can still be + /// used separate from the group. + /// [voiceGroupHandle] the group handle to add the new [voiceHandles]. + /// [voiceHandles] voice handles list to add to the [voiceGroupHandle]. + void addVoiceToGroup( + SoundHandle voiceGroupHandle, + List voiceHandles, + ); + + /// Checks if the handle is a valid voice group. Does not care if the + /// voice group is empty. + /// + /// [handle] the group handle to check. + /// Return true if [handle] is a group handle. + bool isVoiceGroup(SoundHandle handle); + + /// Checks whether a voice group is empty. SoLoud automatically trims + /// the voice groups of voices that have ended, so the group may be + /// empty even though you've added valid voice handles to it. + /// + /// [handle] group handle to check. + /// Return true if the group handle doesn't have any voices. + bool isVoiceGroupEmpty(SoundHandle handle); + // /////////////////////////////////////// // faders // /////////////////////////////////////// diff --git a/lib/src/bindings/bindings_player_ffi.dart b/lib/src/bindings/bindings_player_ffi.dart index 2a8dd3b..36cb36e 100644 --- a/lib/src/bindings/bindings_player_ffi.dart +++ b/lib/src/bindings/bindings_player_ffi.dart @@ -163,13 +163,28 @@ class FlutterSoLoudFfi extends FlutterSoLoud { _nativeFreePtr.asFunction)>(); @override - PlayerErrors initEngine() { - return PlayerErrors.values[_initEngine()]; + PlayerErrors initEngine(int sampleRate, int bufferSize, Channels channels) { + final channelsInternal = switch (channels) { + Channels.channelMono => 1, + Channels.channelStereo => 2, + Channels.channelQuad => 4, + Channels.channel51 => 6, + Channels.channel71 => 8, + }; + final ret = _initEngine( + sampleRate, + bufferSize, + channelsInternal, + ); + return PlayerErrors.values[ret]; } - late final _initEnginePtr = - _lookup>('initEngine'); - late final _initEngine = _initEnginePtr.asFunction(); + late final _initEnginePtr = _lookup< + ffi.NativeFunction< + ffi.Int32 Function(ffi.UnsignedInt, ffi.UnsignedInt, + ffi.UnsignedInt)>>('initEngine'); + late final _initEngine = + _initEnginePtr.asFunction(); @override void deinit() { @@ -837,6 +852,71 @@ class FlutterSoLoudFfi extends FlutterSoLoud { late final _setMaxActiveVoiceCount = _setMaxActiveVoiceCountPtr.asFunction(); + ///////////////////////////////////////// + /// voice groups + ///////////////////////////////////////// + + @override + SoundHandle createVoiceGroup() { + final ret = _createVoiceGroup(); + return SoundHandle(ret > 0 ? ret : -1); + } + + late final _createVoiceGroupPtr = + _lookup>( + 'createVoiceGroup'); + late final _createVoiceGroup = + _createVoiceGroupPtr.asFunction(); + + @override + void destroyVoiceGroup(SoundHandle handle) { + return _destroyVoiceGroup(handle.id); + } + + late final _destroyVoiceGroupPtr = + _lookup>( + 'destroyVoiceGroup'); + late final _destroyVoiceGroup = + _destroyVoiceGroupPtr.asFunction(); + + @override + void addVoiceToGroup( + SoundHandle voiceGroupHandle, + List voiceHandles, + ) { + for (final handle in voiceHandles) { + _addVoiceToGroup(voiceGroupHandle.id, handle.id); + } + } + + late final _addVoiceToGroupPtr = _lookup< + ffi + .NativeFunction>( + 'addVoiceToGroup'); + late final _addVoiceToGroup = + _addVoiceToGroupPtr.asFunction(); + + @override + bool isVoiceGroup(SoundHandle handle) { + return _isVoiceGroup(handle.id) == 1; + } + + late final _isVoiceGroupPtr = + _lookup>( + 'isVoiceGroup'); + late final _isVoiceGroup = _isVoiceGroupPtr.asFunction(); + + @override + bool isVoiceGroupEmpty(SoundHandle handle) { + return _isVoiceGroupEmpty(handle.id) == 1; + } + + late final _isVoiceGroupEmptyPtr = + _lookup>( + 'isVoiceGroupEmpty'); + late final _isVoiceGroupEmpty = + _isVoiceGroupEmptyPtr.asFunction(); + ///////////////////////////////////////// /// faders ///////////////////////////////////////// diff --git a/lib/src/bindings/bindings_player_web.dart b/lib/src/bindings/bindings_player_web.dart index 1bfffa2..13412c0 100644 --- a/lib/src/bindings/bindings_player_web.dart +++ b/lib/src/bindings/bindings_player_web.dart @@ -72,8 +72,16 @@ class FlutterSoLoudWeb extends FlutterSoLoud { } @override - PlayerErrors initEngine() { - return PlayerErrors.values[wasmInitEngine()]; + PlayerErrors initEngine(int sampleRate, int bufferSize, Channels channels) { + final channelsInternal = switch (channels) { + Channels.channelMono => 1, + Channels.channelStereo => 2, + Channels.channelQuad => 4, + Channels.channel51 => 6, + Channels.channel71 => 8, + }; + final ret = wasmInitEngine(sampleRate, bufferSize, channelsInternal); + return PlayerErrors.values[ret]; } @override @@ -420,6 +428,41 @@ class FlutterSoLoudWeb extends FlutterSoLoud { return wasmSetMaxActiveVoiceCount(maxVoiceCount); } + ///////////////////////////////////////// + /// voice groups + ///////////////////////////////////////// + + @override + SoundHandle createVoiceGroup() { + final ret = wasmCreateVoiceGroup(); + return SoundHandle(ret > 0 ? ret : -1); + } + + @override + void destroyVoiceGroup(SoundHandle handle) { + return wasmDestroyVoiceGroup(handle.id); + } + + @override + void addVoiceToGroup( + SoundHandle voiceGroupHandle, + List voiceHandles, + ) { + for (final handle in voiceHandles) { + wasmAddVoiceToGroup(voiceGroupHandle.id, handle.id); + } + } + + @override + bool isVoiceGroup(SoundHandle handle) { + return wasmIsVoiceGroup(handle.id) == 1; + } + + @override + bool isVoiceGroupEmpty(SoundHandle handle) { + return wasmIsVoiceGroupEmpty(handle.id) == 1; + } + // /////////////////////////////////////// // faders // /////////////////////////////////////// diff --git a/lib/src/bindings/js_extension.dart b/lib/src/bindings/js_extension.dart index 0306049..681fc45 100644 --- a/lib/src/bindings/js_extension.dart +++ b/lib/src/bindings/js_extension.dart @@ -46,7 +46,7 @@ external void wasmSendToWorker(int message, int value); external web.Worker wasmWorker; @JS('Module._initEngine') -external int wasmInitEngine(); +external int wasmInitEngine(int sampleRate, int bufferSize, int channels); @JS('Module._dispose') external void wasmDeinit(); @@ -242,6 +242,29 @@ external int wasmGetMaxActiveVoiceCount(); @JS('Module._setMaxActiveVoiceCount') external void wasmSetMaxActiveVoiceCount(int maxVoiceCount); + ///////////////////////////////////////// + /// voice groups + ///////////////////////////////////////// + +@JS('Module._createVoiceGroup') +external int wasmCreateVoiceGroup(); + +@JS('Module._destroyVoiceGroup') +external void wasmDestroyVoiceGroup(int handle); + +@JS('Module._addVoiceToGroup') +external void wasmAddVoiceToGroup(int voiceGroupHandle, int voiceHandle); + +@JS('Module._isVoiceGroup') +external int wasmIsVoiceGroup(int handle); + +@JS('Module._isVoiceGroupEmpty') +external int wasmIsVoiceGroupEmpty(int handle); + + // /////////////////////////////////////// + // faders + // /////////////////////////////////////// + @JS('Module._fadeGlobalVolume') external int wasmFadeGlobalVolume(double to, double duration); diff --git a/lib/src/enums.dart b/lib/src/enums.dart index e433c9a..95be45b 100644 --- a/lib/src/enums.dart +++ b/lib/src/enums.dart @@ -233,3 +233,21 @@ enum PlayerStateNotification { /// unlocked, } + +/// The channels to be used while initializing the player. +enum Channels { + /// One channel. + channelMono, + + /// Two channels. + channelStereo, + + /// Four channels. + channelQuad, + + /// Six channels. + channel51, + + /// Eight channels. + channel71, +} diff --git a/lib/src/soloud.dart b/lib/src/soloud.dart index 50ccdf5..f6590f4 100644 --- a/lib/src/soloud.dart +++ b/lib/src/soloud.dart @@ -207,6 +207,9 @@ interface class SoLoud { @Deprecated('timeout is not used anymore.') Duration timeout = const Duration(seconds: 10), bool automaticCleanup = false, + int sampleRate = 44100, + int bufferSize = 2048, + Channels channels = Channels.channelStereo, }) async { /// Defaults are: /// Miniaudio audio backend @@ -233,7 +236,11 @@ interface class SoLoud { // Initialize native callbacks _initializeNativeCallbacks(); - final error = _controller.soLoudFFI.initEngine(); + final error = _controller.soLoudFFI.initEngine( + sampleRate, + bufferSize, + channels, + ); _logPlayerError(error, from: 'initialize() result'); if (error == PlayerErrors.noError) { _isInitialized = true; @@ -1333,6 +1340,57 @@ interface class SoLoud { _controller.soLoudFFI.setFftSmoothing(smooth); } + ///////////////////////////////////////// + /// voice groups + ///////////////////////////////////////// + + /// Used to create a new voice group. Returns 0 if not successful. + SoundHandle createVoiceGroup() { + final ret = _controller.soLoudFFI.createVoiceGroup(); + return ret; + } + + /// Deallocates the voice group. Does not stop the voices attached to the + /// voice group. + /// + /// [handle] the group handle to destroy. + void destroyVoiceGroup(SoundHandle handle) { + return _controller.soLoudFFI.destroyVoiceGroup(handle); + } + + /// Adds voice handle to the voice group. The voice handles can still be + /// used separate from the group. + /// [voiceGroupHandle] the group handle to add the new [voiceHandles]. + /// [voiceHandles] voice handle to add to the [voiceGroupHandle]. + void addVoiceToGroup( + SoundHandle voiceGroupHandle, + List voiceHandles, + ) { + return _controller.soLoudFFI.addVoiceToGroup( + voiceGroupHandle, + voiceHandles, + ); + } + + /// Checks if the handle is a valid voice group. Does not care if the + /// voice group is empty. + /// + /// [handle] the group handle to check. + /// Return true if [handle] is a group handle. + bool isVoiceGroup(SoundHandle handle) { + return _controller.soLoudFFI.isVoiceGroup(handle); + } + + /// Checks whether a voice group is empty. SoLoud automatically trims + /// the voice groups of voices that have ended, so the group may be + /// empty even though you've added valid voice handles to it. + /// + /// [handle] group handle to check. + /// Return true if the group handle doesn't have any voices. + bool isVoiceGroupEmpty(SoundHandle handle) { + return _controller.soLoudFFI.isVoiceGroupEmpty(handle); + } + // /////////////////////////////////////// // faders // ////////////////////////////////////// diff --git a/src/bindings.cpp b/src/bindings.cpp index 7618abb..beb695f 100644 --- a/src/bindings.cpp +++ b/src/bindings.cpp @@ -43,9 +43,9 @@ extern "C" FFI_PLUGIN_EXPORT void createWorkerInWasm() { printf("CPP void createWorkerInWasm()\n"); - + EM_ASM({ - if (!Module.wasmWorker) + if (!Module.wasmWorker) { // Create a new Worker from the URI var workerUri = "assets/packages/flutter_soloud/web/worker.dart.js"; @@ -149,16 +149,24 @@ extern "C" ////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////// - /// Initialize the player.get()-> Must be called before any other player functions + /// Initialize the player. Must be called before any other player functions. /// - /// Returns [PlayerErrors.noError] if success - FFI_PLUGIN_EXPORT enum PlayerErrors initEngine() + /// [sampleRate] the sample rate. Usually is 22050, 44100 (CD quality) or 48000. + /// [bufferSize] the audio buffer size. Usually is 2048, but can be also 512 when + /// low latency is needed for example in games. + /// [channels] 1=mono, 2=stereo, 4=quad, 6=5.1, 8=7.1. + /// + /// Returns [PlayerErrors.noError] if success. + FFI_PLUGIN_EXPORT enum PlayerErrors initEngine( + unsigned int sampleRate, + unsigned int bufferSize, + unsigned int channels) { if (player.get() == nullptr) player = std::make_unique(); player.get()->setStateChangedCallback(stateChangedCallback); - PlayerErrors res = (PlayerErrors)player.get()->init(); + PlayerErrors res = (PlayerErrors)player.get()->init(sampleRate, bufferSize, channels); if (res != noError) return res; @@ -374,7 +382,7 @@ extern "C" FFI_PLUGIN_EXPORT void pauseSwitch(unsigned int handle) { if (player.get() == nullptr || !player.get()->isInited() || - !player.get()->isValidVoiceHandle(handle)) + !player.get()->isValidHandle(handle)) return; player.get()->pauseSwitch(handle); } @@ -386,7 +394,7 @@ extern "C" FFI_PLUGIN_EXPORT void setPause(unsigned int handle, bool pause) { if (player.get() == nullptr || !player.get()->isInited() || - !player.get()->isValidVoiceHandle(handle)) + !player.get()->isValidHandle(handle)) return; player.get()->setPause(handle, pause); } @@ -398,7 +406,7 @@ extern "C" FFI_PLUGIN_EXPORT int getPause(unsigned int handle) { if (player.get() == nullptr || !player.get()->isInited() || - !player.get()->isValidVoiceHandle(handle)) + !player.get()->isValidHandle(handle)) return false; return player.get()->getPause(handle) ? 1 : 0; } @@ -418,7 +426,7 @@ extern "C" FFI_PLUGIN_EXPORT void setRelativePlaySpeed(unsigned int handle, float speed) { if (player.get() == nullptr || !player.get()->isInited() || - !player.get()->isValidVoiceHandle(handle)) + !player.get()->isValidHandle(handle)) return; player.get()->setRelativePlaySpeed(handle, speed); } @@ -431,7 +439,7 @@ extern "C" FFI_PLUGIN_EXPORT float getRelativePlaySpeed(unsigned int handle) { if (player.get() == nullptr || !player.get()->isInited() || - !player.get()->isValidVoiceHandle(handle)) + !player.get()->isValidHandle(handle)) return 1; return player.get()->getRelativePlaySpeed(handle); } @@ -471,7 +479,7 @@ extern "C" FFI_PLUGIN_EXPORT void stop(unsigned int handle) { if (player.get() == nullptr || !player.get()->isInited() || - !player.get()->isValidVoiceHandle(handle)) + !player.get()->isValidHandle(handle)) return; player.get()->stop(handle); } @@ -502,7 +510,7 @@ extern "C" FFI_PLUGIN_EXPORT int getLooping(unsigned int handle) { if (player.get() == nullptr || !player.get()->isInited() || - !player.get()->isValidVoiceHandle(handle)) + !player.get()->isValidHandle(handle)) return 0; return player.get()->getLooping(handle) == 1; } @@ -515,7 +523,7 @@ extern "C" FFI_PLUGIN_EXPORT void setLooping(unsigned int handle, bool enable) { if (player.get() == nullptr || !player.get()->isInited() || - !player.get()->isValidVoiceHandle(handle)) + !player.get()->isValidHandle(handle)) return; player.get()->setLooping(handle, enable); } @@ -527,7 +535,7 @@ extern "C" FFI_PLUGIN_EXPORT double getLoopPoint(unsigned int handle) { if (player.get() == nullptr || !player.get()->isInited() || - !player.get()->isValidVoiceHandle(handle)) + !player.get()->isValidHandle(handle)) return 0; return player.get()->getLoopPoint(handle); } @@ -539,7 +547,7 @@ extern "C" FFI_PLUGIN_EXPORT void setLoopPoint(unsigned int handle, double time) { if (player.get() == nullptr || !player.get()->isInited() || - !player.get()->isValidVoiceHandle(handle)) + !player.get()->isValidHandle(handle)) return; player.get()->setLoopPoint(handle, time); } @@ -646,7 +654,8 @@ extern "C" return noError; } - FFI_PLUGIN_EXPORT float getTextureValue(int row, int column) { + FFI_PLUGIN_EXPORT float getTextureValue(int row, int column) + { return texture2D[row][column]; } @@ -692,7 +701,7 @@ extern "C" FFI_PLUGIN_EXPORT double getPosition(unsigned int handle) { if (player.get() == nullptr || !player.get()->isInited() || - !player.get()->isValidVoiceHandle(handle)) + !player.get()->isValidHandle(handle)) return 0.0; return player.get()->getPosition(handle); } @@ -724,7 +733,7 @@ extern "C" FFI_PLUGIN_EXPORT double getVolume(unsigned int handle) { if (player.get() == nullptr || !player.get()->isInited() || - !player.get()->isValidVoiceHandle(handle)) + !player.get()->isValidHandle(handle)) return 0.0; return player.get()->getVolume(handle); } @@ -735,7 +744,7 @@ extern "C" { if (player.get() == nullptr || !player.get()->isInited()) return backendNotInited; - if (!player.get()->isValidVoiceHandle(handle)) + if (!player.get()->isValidHandle(handle)) return soundHandleNotFound; player.get()->setVolume(handle, volume); return noError; @@ -787,7 +796,7 @@ extern "C" if (player.get() == nullptr || !player.get()->isInited() || player.get()->getSoundsCount() == 0) return false; - return player.get()->isValidVoiceHandle(handle) ? 1 : 0; + return player.get()->isValidHandle(handle) ? 1 : 0; } /// Returns the number of concurrent sounds that are playing at the moment. @@ -818,7 +827,7 @@ extern "C" FFI_PLUGIN_EXPORT bool getProtectVoice(unsigned int handle) { if (player.get() == nullptr || !player.get()->isInited() || - !player.get()->isValidVoiceHandle(handle)) + !player.get()->isValidHandle(handle)) return false; return player.get()->getProtectVoice(handle); } @@ -836,7 +845,7 @@ extern "C" FFI_PLUGIN_EXPORT void setProtectVoice(unsigned int handle, bool protect) { if (player.get() == nullptr || !player.get()->isInited() || - !player.get()->isValidVoiceHandle(handle)) + !player.get()->isValidHandle(handle)) return; player.get()->setProtectVoice(handle, protect); } @@ -868,11 +877,54 @@ extern "C" } ///////////////////////////////////////// - /// faders + /// voice groups ///////////////////////////////////////// - /// Smoothly change the global volume over specified time. + /// Used to create a new voice group. Returns 0 if not successful. + FFI_PLUGIN_EXPORT unsigned int createVoiceGroup() { + return player.get()->createVoiceGroup(); + } + + /// Deallocates the voice group. Does not stop the voices attached to the + /// voice group. + /// + /// [handle] the group handle to destroy. + FFI_PLUGIN_EXPORT void destroyVoiceGroup(unsigned int handle) { + player.get()->destroyVoiceGroup(handle); + } + + /// Adds voice handle to the voice group. The voice handles can still be + /// used separate from the group. + /// [voiceGroupHandle] the group handle to add the new [voiceHandle]. + /// [voiceHandle] voice handle to add to the [voiceGroupHandle]. + FFI_PLUGIN_EXPORT void addVoiceToGroup(unsigned int voiceGroupHandle, unsigned int voiceHandle) { + player.get()->addVoiceToGroup(voiceGroupHandle, voiceHandle); + } + + /// Checks if the handle is a valid voice group. Does not care if the + /// voice group is empty. /// + /// [handle] the group handle to check. + /// Return true if [handle] is a group handle. + FFI_PLUGIN_EXPORT bool isVoiceGroup(unsigned int handle) { + return player.get()->isVoiceGroup(handle); + } + + /// Checks whether a voice group is empty. SoLoud automatically trims + /// the voice groups of voices that have ended, so the group may be + /// empty even though you've added valid voice handles to it. + /// + /// [handle] group handle to check. + /// Return true if the group handle doesn't have any voices. + FFI_PLUGIN_EXPORT bool isVoiceGroupEmpty(unsigned int handle) { + return player.get()->isVoiceGroupEmpty(handle); + } + + ///////////////////////////////////////// + /// faders & oscillators + ///////////////////////////////////////// + + /// Smoothly change the global volume over specified time. FFI_PLUGIN_EXPORT enum PlayerErrors fadeGlobalVolume(float to, float time) { if (player.get() == nullptr || !player.get()->isInited()) @@ -882,7 +934,6 @@ extern "C" } /// Smoothly change a channel's volume over specified time. - /// FFI_PLUGIN_EXPORT enum PlayerErrors fadeVolume(unsigned int handle, float to, float time) { if (player.get() == nullptr || !player.get()->isInited()) @@ -892,7 +943,6 @@ extern "C" } /// Smoothly change a channel's pan setting over specified time. - /// FFI_PLUGIN_EXPORT enum PlayerErrors fadePan(unsigned int handle, float to, float time) { if (player.get() == nullptr || !player.get()->isInited()) @@ -902,7 +952,6 @@ extern "C" } /// Smoothly change a channel's relative play speed over specified time. - /// FFI_PLUGIN_EXPORT enum PlayerErrors fadeRelativePlaySpeed(unsigned int handle, float to, float time) { if (player.get() == nullptr || !player.get()->isInited()) @@ -912,7 +961,6 @@ extern "C" } /// After specified time, pause the channel. - /// FFI_PLUGIN_EXPORT enum PlayerErrors schedulePause(unsigned int handle, float time) { if (player.get() == nullptr || !player.get()->isInited()) @@ -922,7 +970,6 @@ extern "C" } /// After specified time, stop the channel. - /// FFI_PLUGIN_EXPORT enum PlayerErrors scheduleStop(unsigned int handle, float time) { if (player.get() == nullptr || !player.get()->isInited()) @@ -932,7 +979,6 @@ extern "C" } /// Set fader to oscillate the volume at specified frequency. - /// FFI_PLUGIN_EXPORT enum PlayerErrors oscillateVolume(unsigned int handle, float from, float to, float time) { if (player.get() == nullptr || !player.get()->isInited()) @@ -942,7 +988,6 @@ extern "C" } /// Set fader to oscillate the panning at specified frequency. - /// FFI_PLUGIN_EXPORT enum PlayerErrors oscillatePan(unsigned int handle, float from, float to, float time) { if (player.get() == nullptr || !player.get()->isInited()) @@ -952,7 +997,6 @@ extern "C" } /// Set fader to oscillate the relative play speed at specified frequency. - /// FFI_PLUGIN_EXPORT enum PlayerErrors oscillateRelativePlaySpeed(unsigned int handle, float from, float to, float time) { if (player.get() == nullptr || !player.get()->isInited()) @@ -962,7 +1006,6 @@ extern "C" } /// Set fader to oscillate the global volume at specified frequency. - /// FFI_PLUGIN_EXPORT enum PlayerErrors oscillateGlobalVolume(float from, float to, float time) { if (player.get() == nullptr || !player.get()->isInited()) @@ -980,7 +1023,6 @@ extern "C" /// [filterType] filter to check /// Returns [PlayerErrors.noError] if no errors and the index of /// the given filter (-1 if the filter is not active) - /// FFI_PLUGIN_EXPORT enum PlayerErrors isFilterActive(enum FilterType filterType, int *index) { *index = -1; @@ -994,7 +1036,6 @@ extern "C" /// /// [filterType] filter to get param names /// Returns [PlayerErrors.noError] if no errors and the list of param names - /// FFI_PLUGIN_EXPORT enum PlayerErrors getFilterParamNames( enum FilterType filterType, int *paramsCount, char **names) { @@ -1017,7 +1058,6 @@ extern "C" /// /// [filterType] filter to add /// Returns [PlayerErrors.noError] if no errors - /// FFI_PLUGIN_EXPORT enum PlayerErrors addGlobalFilter(enum FilterType filterType) { if (player.get() == nullptr || !player.get()->isInited()) @@ -1029,7 +1069,6 @@ extern "C" /// /// [filterType] filter to remove /// Returns [PlayerErrors.noError] if no errors - /// FFI_PLUGIN_EXPORT enum PlayerErrors removeGlobalFilter(enum FilterType filterType) { if (player.get() == nullptr || !player.get()->isInited()) @@ -1044,7 +1083,6 @@ extern "C" /// /// [filterType] filter to modify a param /// Returns [PlayerErrors.noError] if no errors - /// FFI_PLUGIN_EXPORT enum PlayerErrors setFxParams(enum FilterType filterType, int attributeId, float value) { if (player.get() == nullptr || !player.get()->isInited()) @@ -1057,7 +1095,6 @@ extern "C" /// /// [filterType] filter to modify a param /// Returns the value of param - /// FFI_PLUGIN_EXPORT float getFxParams(enum FilterType filterType, int attributeId) { if (player.get() == nullptr || !player.get()->isInited()) diff --git a/src/ffi_gen_tmp.h b/src/ffi_gen_tmp.h index fbda9cc..3afd42e 100644 --- a/src/ffi_gen_tmp.h +++ b/src/ffi_gen_tmp.h @@ -23,9 +23,28 @@ struct CaptureDevice //--------------------- copy here the new functions to generate -FFI_PLUGIN_EXPORT enum PlayerErrors loadMem( - char *uniqueName, - unsigned char *buffer, - int length, - int loadIntoMem, - unsigned int *hash); +/// Used to create a new voice group. Returns 0 if not successful. +FFI_PLUGIN_EXPORT unsigned int createVoiceGroup(); + +/// Deallocates the voice group. Does not stop the voices attached to the voice group. +/// +/// [handle] the group handle to destroy. +FFI_PLUGIN_EXPORT void destroyVoiceGroup(unsigned int handle); + +/// Adds voice handle to the voice group. The voice handles can still be used separate from the group. +/// [voiceGroupHandle] the group handle to add the new [handle]. +/// [voiceHandle] voice handle to add to the [voiceGroupHandle]. +FFI_PLUGIN_EXPORT void addVoiceToGroup(unsigned int voiceGroupHandle, unsigned int voiceHandle); + +/// Checks if the handle is a valid voice group. Does not care if the voice group is empty. +/// +/// [handle] the group handle to check. +/// Return true if [handle] is a group handle. +FFI_PLUGIN_EXPORT int isVoiceGroup(unsigned int handle); + +/// Checks whether a voice group is empty. SoLoud automatically trims the voice groups of +/// voices that have ended, so the group may be empty even though you've added valid voice handles to it. +/// +/// [handle] group handle to check. +/// Return true if the group handle doesn't have any voices. +FFI_PLUGIN_EXPORT int isVoiceGroupEmpty(unsigned int handle); diff --git a/src/player.cpp b/src/player.cpp index c46fee8..a35636c 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -32,7 +32,7 @@ void Player::setStateChangedCallback(void (*stateChangedCallback)(unsigned int)) soloud.setStateChangedCallback(stateChangedCallback); } -PlayerErrors Player::init() +PlayerErrors Player::init(unsigned int sampleRate, unsigned int bufferSize, unsigned int channels) { if (mInited) return playerAlreadyInited; @@ -42,9 +42,7 @@ PlayerErrors Player::init() // initialize SoLoud. SoLoud::result result = soloud.init( SoLoud::Soloud::CLIP_ROUNDOFF, - SoLoud::Soloud::MINIAUDIO, 44100, 2048, 2U); - // soloud.init(1U, 0U, 44100, 2048, 2U); - // SoLoud::Thread::sleep(1000); + SoLoud::Soloud::MINIAUDIO, sampleRate, bufferSize, channels); if (result == SoLoud::SO_NO_ERROR) mInited = true; else @@ -551,9 +549,9 @@ void Player::setPanAbsolute(SoLoud::handle handle, float panLeft, float panRight soloud.setPanAbsolute(handle, panLeft, panRight); } -bool Player::isValidVoiceHandle(SoLoud::handle handle) +bool Player::isValidHandle(SoLoud::handle handle) { - return soloud.isValidVoiceHandle(handle); + return soloud.isValidVoiceHandle(handle) || soloud.isVoiceGroup(handle); } unsigned int Player::getActiveVoiceCount() @@ -639,6 +637,30 @@ void Player::debug() } } +///////////////////////////////////////// +/// voice groups +///////////////////////////////////////// + +unsigned int Player::createVoiceGroup() { + return soloud.createVoiceGroup(); +} + +void Player::destroyVoiceGroup(SoLoud::handle handle) { + soloud.destroyVoiceGroup(handle); +} + +void Player::addVoiceToGroup(SoLoud::handle voiceGroupHandle, SoLoud::handle voiceHandle) { + soloud.addVoiceToGroup(voiceGroupHandle, voiceHandle); +} + +bool Player::isVoiceGroup(SoLoud::handle handle) { + return soloud.isVoiceGroup(handle); +} + +bool Player::isVoiceGroupEmpty(SoLoud::handle handle) { + return soloud.isVoiceGroupEmpty(handle); +} + ///////////////////////////////////////// /// faders ///////////////////////////////////////// diff --git a/src/player.h b/src/player.h index 0dcf786..16d752a 100644 --- a/src/player.h +++ b/src/player.h @@ -45,8 +45,12 @@ class Player ~Player(); /// @brief Initialize the player. Must be called before any other player functions. + /// @param sampleRate sample rate. Usually is 22050, 44100 (CD quality) or 48000. + /// @param bufferSize the audio buffer size. Usually is 2048, but can be also 512 when + /// low latency is needed for example in games. + /// @param channels 1)mono, 2)stereo 4)quad 6)5.1 8)7.1 /// @return Returns [PlayerErrors.SO_NO_ERROR] if success. - PlayerErrors init(); + PlayerErrors init(unsigned int sampleRate, unsigned int bufferSize, unsigned int channels); /// @brief Set a function callback triggered when a voice is stopped/ended. void setVoiceEndedCallback(void (*voiceEndedCallback)(unsigned int*)); @@ -307,7 +311,7 @@ class Player /// @brief Check if a handle is still valid. /// @param handle handle to check. /// @return true if it still exists. - bool isValidVoiceHandle(SoLoud::handle handle); + bool isValidHandle(SoLoud::handle handle); /// @brief Returns the number of concurrent sounds that are playing at the moment. unsigned int getActiveVoiceCount(); @@ -357,6 +361,33 @@ class Player void debug(); + ///////////////////////////////////////// + /// voice groups + ///////////////////////////////////////// + + /// @brief Used to create a new voice group. Returns 0 if not successful. + unsigned int createVoiceGroup(); + + /// @brief Deallocates the voice group. Does not stop the voices attached to the voice group. + /// @param handle the group handle to destroy. + void destroyVoiceGroup(SoLoud::handle handle); + + /// @brief Adds voice handle to the voice group. The voice handles can still be used separate from the group. + /// @param voiceGroupHandle the group handle to add the new [handle]. + /// @param voiceHandle voice handle to add to the [voiceGroupHandle]. + void addVoiceToGroup(SoLoud::handle voiceGroupHandle, SoLoud::handle voiceHandle); + + /// @brief Checks if the handle is a valid voice group. Does not care if the voice group is empty. + /// @param handle the group handle to check. + /// @return true if [handle] is a group handle. + bool isVoiceGroup(SoLoud::handle handle); + + /// @brief Checks whether a voice group is empty. SoLoud automatically trims the voice groups of + /// voices that have ended, so the group may be empty even though you've added valid voice handles to it. + /// @param handle group handle to check. + /// @return true if the group handle doesn't have any voices. + bool isVoiceGroupEmpty(SoLoud::handle handle); + ///////////////////////////////////////// /// faders & oscillators ///////////////////////////////////////// From 1ec040b4912b87149aaec293ff7599cca284ae5f Mon Sep 17 00:00:00 2001 From: Marco Bavagnoli Date: Sun, 14 Jul 2024 12:37:10 +0200 Subject: [PATCH 2/6] added filters to sounds #101 --- example/lib/ui/filter_fx.dart | 4 +- example/tests/tests.dart | 145 +++++++++++++- lib/src/audio_source.dart | 203 ++++++++++++++++++- lib/src/bindings/bindings_player.dart | 99 +++++++-- lib/src/bindings/bindings_player_ffi.dart | 217 +++++++++++++++----- lib/src/bindings/bindings_player_web.dart | 163 ++++++++++++--- lib/src/bindings/js_extension.dart | 63 ++++-- lib/src/soloud.dart | 176 ++++++++++++---- lib/src/sound_handle.dart | 6 +- lib/src/sound_hash.dart | 6 +- src/active_sound.h | 27 +++ src/bindings.cpp | 232 ++++++++++++++++++---- src/enums.h | 23 +++ src/ffi_gen_tmp.h | 30 +-- src/filters/filters.cpp | 107 +++++++--- src/filters/filters.h | 61 +++--- src/player.cpp | 134 ++++++------- src/player.h | 31 +-- web/libflutter_soloud_plugin.js | 2 +- web/libflutter_soloud_plugin.wasm | Bin 448515 -> 452671 bytes 20 files changed, 1350 insertions(+), 379 deletions(-) create mode 100644 src/active_sound.h diff --git a/example/lib/ui/filter_fx.dart b/example/lib/ui/filter_fx.dart index c768e18..97b9347 100644 --- a/example/lib/ui/filter_fx.dart +++ b/example/lib/ui/filter_fx.dart @@ -75,7 +75,7 @@ class _FilterFxState extends State { } for (var i = 0; i < params.length; i++) { params[i] = SoLoud.instance - .getFilterParameter(widget.filterType, i); + .getGlobalFilterParameter(widget.filterType, i); } } else { try { @@ -102,7 +102,7 @@ class _FilterFxState extends State { onChanged: (value) async { params[index] = value; SoLoud.instance - .setFilterParameter(widget.filterType, index, value); + .setGlobalFilterParameter(widget.filterType, index, value); if (mounted) setState(() {}); }, ); diff --git a/example/tests/tests.dart b/example/tests/tests.dart index 5cd3fe7..50298e9 100644 --- a/example/tests/tests.dart +++ b/example/tests/tests.dart @@ -3,7 +3,6 @@ import 'dart:async'; import 'dart:ui'; import 'package:flutter/material.dart'; - import 'package:flutter_soloud/flutter_soloud.dart'; import 'package:flutter_soloud/src/bindings/soloud_controller.dart'; @@ -66,7 +65,9 @@ class _MyHomePageState extends State { tests.addAll([ Test(name: 'testProtectVoice', callback: testProtectVoice), Test( - name: 'testAllInstancesFinished', callback: testAllInstancesFinished), + name: 'testAllInstancesFinished', + callback: testAllInstancesFinished, + ), Test(name: 'testCreateNotes', callback: testCreateNotes), Test(name: 'testPlaySeekPause', callback: testPlaySeekPause), Test(name: 'testPan', callback: testPan), @@ -75,6 +76,8 @@ class _MyHomePageState extends State { Test(name: 'testSynchronousDeinit', callback: testSynchronousDeinit), Test(name: 'testAsynchronousDeinit', callback: testAsynchronousDeinit), Test(name: 'testVoiceGroups', callback: testVoiceGroups), + Test(name: 'testSoundFilters', callback: testSoundFilters), + Test(name: 'testGlobalFilters', callback: testGlobalFilters), ]); } @@ -166,6 +169,7 @@ class _MyHomePageState extends State { if (context.mounted) setState(() {}); }, (error, stack) { + deinit(); // if (error is SoLoudInitializationStoppedByDeinitException) { // // This is to be expected in this test. // return; @@ -665,7 +669,7 @@ Future testVoiceGroups() async { assert(isEmpty, 'Voice group just created but it is not empty!'); /// Add all voices to the group. - SoLoud.instance.addVoiceToGroup(group, [h1, h2, h3, h4]); + SoLoud.instance.addVoicesToGroup(group, [h1, h2, h3, h4]); isEmpty = SoLoud.instance.isVoiceGroupEmpty(group); assert(!isEmpty, 'Voices added to the group, but the group is empty!'); @@ -689,3 +693,138 @@ Future testVoiceGroups() async { deinit(); return StringBuffer(); } + +/// Test sound filters. +Future testSoundFilters() async { + final ret = StringBuffer(); + await initialize(); + + final sound = await SoLoud.instance.loadAsset( + 'assets/audio/8_bit_mentality.mp3', + // mode: LoadMode.disk, + ); + + /// Add filter to the sound. + sound.addFilter(FilterType.echoFilter); + + /// Set a handle filter. It must be set before it starts playing. + final h1 = await SoLoud.instance.play(sound); + + /// Use the `Wet` attribute index. + const attributeId = 0; + const value = 1.2; + sound.setFilterParameter( + h1, + FilterType.echoFilter, + attributeId, + value, + ); + final g = sound.getFilterParameter(h1, FilterType.echoFilter, attributeId); + assert( + closeTo(g, value, 0.001), + 'Setting attribute to $value but optained $g', + ); + + sound.oscillateFilterParameter( + h1, + FilterType.echoFilter, + attributeId, + 0.01, + 2, + const Duration(seconds: 2), + ); + + assert( + sound.isFilterActive(FilterType.echoFilter) >= 0, + 'The filter is not active!', + ); + + await delay(6000); + + /// Remove the filter + try { + sound.removeFilter(FilterType.echoFilter); + } on Exception catch (e) { + ret + ..write(e) + ..writeln(); + } + assert( + sound.isFilterActive(FilterType.echoFilter) < 0, + 'The filter is still active after removing it!', + ); + + deinit(); + return ret; +} + +/// Test global filters. +Future testGlobalFilters() async { + final ret = StringBuffer(); + await initialize(); + + late final AudioSource sound; + try { + sound = await SoLoud.instance.loadAsset( + 'assets/audio/8_bit_mentality.mp3', + mode: LoadMode.disk, + ); + } on Exception catch (e) { + ret + ..write(e) + ..writeln(); + } + + /// Add filter to the sound. + SoLoud.instance.addGlobalFilter(FilterType.echoFilter); + + await SoLoud.instance.play(sound); + + /// Use the `Wet` attribute index. + const attributeId = 0; + const value = 1.2; + SoLoud.instance.setGlobalFilterParameter( + FilterType.echoFilter, + attributeId, + value, + ); + final g = SoLoud.instance.getGlobalFilterParameter( + FilterType.echoFilter, + attributeId, + ); + assert( + closeTo(g, value, 0.001), + 'Setting attribute to $value but optained $g', + ); + + SoLoud.instance.oscillateGlobalFilterParameter( + FilterType.echoFilter, + attributeId, + 0.01, + 2, + const Duration(seconds: 2), + ); + + assert( + SoLoud.instance.isFilterActive(FilterType.echoFilter) >= 0, + 'The filter is not active!', + ); + + await delay(6000); + + /// Remove the filter + try { + SoLoud.instance.removeGlobalFilter(FilterType.echoFilter); + } on Exception catch (e) { + ret + ..write(e) + ..writeln(); + } + assert( + SoLoud.instance.isFilterActive(FilterType.echoFilter) < 0, + 'The filter is still active after removing it!', + ); + + deinit(); + return ret; +} diff --git a/lib/src/audio_source.dart b/lib/src/audio_source.dart index 3298c24..2596a79 100644 --- a/lib/src/audio_source.dart +++ b/lib/src/audio_source.dart @@ -1,8 +1,15 @@ import 'dart:async'; import 'dart:collection'; +import 'package:flutter_soloud/src/bindings/bindings_player.dart'; +import 'package:flutter_soloud/src/bindings/soloud_controller.dart'; +import 'package:flutter_soloud/src/enums.dart'; +import 'package:flutter_soloud/src/exceptions/exceptions.dart'; +import 'package:flutter_soloud/src/filter_params.dart'; +import 'package:flutter_soloud/src/soloud.dart'; import 'package:flutter_soloud/src/sound_handle.dart'; import 'package:flutter_soloud/src/sound_hash.dart'; +import 'package:logging/logging.dart'; import 'package:meta/meta.dart'; /// the type sent back to the user when a sound event occurs @@ -37,11 +44,25 @@ enum SoundEventType { /// You can access the currently playing instances' handles via [handles]. /// /// You can listen to the broadcast stream of [soundEvents]. +/// +/// Every [AudioSource] can have their own filters. You can add, remove and +/// query wether if filter is active respectively with `addFilter()`, +/// `removeGlobalFilter()` and `isFilterActive()`. The effect must be +/// set before playing the sound and parameters can be changed after having +/// the a voice handle: +/// ``` +/// final sound = await SoLoud.instance.loadAsset('...'); +/// sound.addFilter(FilterType.echoFilter); +/// final handle = SoLoud.instance.play(sound, paused: true); +/// sound.setFilterParameter(); +/// ``` class AudioSource { /// Constructs an instance of [AudioSource]. @internal AudioSource(this.soundHash); + static final Logger _log = Logger('flutter_soloud.AudioSource'); + /// The hash uniquely identifying this loaded sound. final SoundHash soundHash; @@ -68,8 +89,6 @@ class AudioSource { @internal final Set handlesInternal = {}; - // TODO(marco): make marker keys time able to trigger an event - /// Backing controller for [soundEvents]. @internal late final StreamController soundEventsController = @@ -103,6 +122,186 @@ class AudioSource { Stream get allInstancesFinished => allInstancesFinishedController.stream; + // /////////////////////////////////////// + // / Filters for this [soundHash] + // /////////////////////////////////////// + + /// Checks whether the given [filterType] is active. + /// + /// Returns `-1` if the filter is not active. Otherwise, returns + /// the index of the given filter. + int isFilterActive(FilterType filterType) { + final ret = SoLoudController().soLoudFFI.isFilterActive( + filterType, + soundHash: soundHash, + ); + if (ret.error != PlayerErrors.noError) { + _log.severe(() => 'isFilterActive(): ${ret.error}'); + throw SoLoudCppException.fromPlayerError(ret.error); + } + return ret.index; + } + + /// Adds a [filterType] to this sound. + /// + /// Throws [SoLoudMaxFilterNumberReachedException] when the max number of + /// concurrent filter is reached (default max filter is 8). + /// Throws [SoLoudFilterAlreadyAddedException] when trying to add a filter + /// that has already been added. + PlayerErrors addFilter(FilterType filterType) { + final error = SoLoudController().soLoudFFI.addFilter( + filterType, + soundHash: soundHash, + ); + if (error != PlayerErrors.noError) { + _log.severe(() => 'addGlobalFilter(): $error'); + throw SoLoudCppException.fromPlayerError(error); + } + return error; + } + + /// Removes [filterType] from all sounds. + PlayerErrors removeFilter(FilterType filterType) { + final error = SoLoudController().soLoudFFI.removeFilter( + filterType, + soundHash: soundHash, + ); + if (error != PlayerErrors.noError) { + _log.severe(() => 'removeGlobalFilter(): $error'); + throw SoLoudCppException.fromPlayerError(error); + } + return error; + } + + /// Set the effect parameter with id [attributeId] of [filterType] + /// with [value] value. + /// + /// Specify the [attributeId] of the parameter (which you can learn from + /// [SoLoud.getFilterParamNames]), and its new [value]. + /// + /// [handle] the handle to set the filter to. If equal to 0, the filter is + /// applyed to the global filter. + /// [filterType] filter to modify a param. + /// Returns [PlayerErrors.noError] if no errors. + PlayerErrors setFilterParameter( + SoundHandle handle, + FilterType filterType, + int attributeId, + double value, + ) { + final error = SoLoudController().soLoudFFI.setFilterParams( + filterType, + attributeId, + value, + handle: handle, + ); + if (error != PlayerErrors.noError) { + _log.severe(() => 'setFxParams(): $error'); + throw SoLoudCppException.fromPlayerError(error); + } + return error; + } + + /// Get the effect parameter value with id [attributeId] of [filterType]. + /// + /// Specify the [attributeId] of the parameter (which you can learn from + /// [SoLoud.getFilterParamNames]). + /// + /// [handle] the handle to get the attribute value from. If equal to 0, + /// it gets the global filter value. + /// [filterType] the filter to modify a parameter. + /// Returns the value of the parameter. + double getFilterParameter( + SoundHandle handle, + FilterType filterType, + int attributeId, + ) { + final ret = SoLoudController().soLoudFFI.getFilterParams( + filterType, + attributeId, + handle: handle, + ); + if (ret.error == PlayerErrors.filterNotFound) { + throw const SoLoudFilterNotFoundException(); + } + if (ret.error == PlayerErrors.soundHandleNotFound) { + throw const SoLoudSoundHandleNotFoundCppException(); + } + if (ret.error != PlayerErrors.noError) { + throw SoLoudCppException.fromPlayerError(ret.error); + } + return ret.value; + } + + /// Fade a parameter of a filter. + /// + /// [handle] the handle of the voice to apply the fade. If equal to 0, + /// it fades the global filter. + /// [filterType] filter to modify a param. + /// [attributeId] the attribute index to fade. + /// [to] value the attribute should go in [time] duration. + /// [time] the fade slope duration. + /// + /// Throws [SoLoudNotInitializedException] if the engine is not initialized. + void fadeFilterParameter( + SoundHandle handle, + FilterType filterType, + int attributeId, + double to, + Duration time, + ) { + if (!SoLoud.instance.isInitialized) { + throw const SoLoudNotInitializedException(); + } + final error = SoLoudController().soLoudFFI.fadeFilterParameter( + filterType, + attributeId, + to, + time.toDouble(), + handle: handle, + ); + if (error != PlayerErrors.noError) { + _log.severe(() => 'fadeFilterParameter(): $error'); + throw SoLoudCppException.fromPlayerError(error); + } + } + + /// Oscillate a parameter of a filter. + /// + /// [handle] the handle of the voice to apply the fade. If equal to 0, + /// it fades the global filter. + /// [filterType] filter to modify a param. + /// [attributeId] the attribute index to fade. + /// [from] the starting value the attribute sould start to oscillate. + /// [to] the ending value the attribute sould end to oscillate. + /// [time] the fade slope duration. + /// + /// Throws [SoLoudNotInitializedException] if the engine is not initialized. + void oscillateFilterParameter( + SoundHandle handle, + FilterType filterType, + int attributeId, + double from, + double to, + Duration time, + ) { + if (!SoLoud.instance.isInitialized) { + throw const SoLoudNotInitializedException(); + } + final error = SoLoudController().soLoudFFI.oscillateFilterParameter( + filterType, + attributeId, + from, + to, + time.toDouble(), + handle: handle, + ); + if (error != PlayerErrors.noError) { + _log.severe(() => 'oscillateFilterParameter(): $error'); + throw SoLoudCppException.fromPlayerError(error); + } + } + @override String toString() { return 'soundHash: $soundHash has ${handles.length} active handles'; diff --git a/lib/src/bindings/bindings_player.dart b/lib/src/bindings/bindings_player.dart index 7364fb1..bf23d1c 100644 --- a/lib/src/bindings/bindings_player.dart +++ b/lib/src/bindings/bindings_player.dart @@ -510,7 +510,7 @@ abstract class FlutterSoLoud { /// used separate from the group. /// [voiceGroupHandle] the group handle to add the new [voiceHandles]. /// [voiceHandles] voice handles list to add to the [voiceGroupHandle]. - void addVoiceToGroup( + void addVoicesToGroup( SoundHandle voiceGroupHandle, List voiceHandles, ); @@ -536,31 +536,35 @@ abstract class FlutterSoLoud { /// Smoothly change the global volume over specified [duration]. @mustBeOverridden - int fadeGlobalVolume(double to, Duration duration); + PlayerErrors fadeGlobalVolume(double to, Duration duration); /// Smoothly change a channel's volume over specified [duration]. @mustBeOverridden - int fadeVolume(SoundHandle handle, double to, Duration duration); + PlayerErrors fadeVolume(SoundHandle handle, double to, Duration duration); /// Smoothly change a channel's pan setting over specified [duration]. @mustBeOverridden - int fadePan(SoundHandle handle, double to, Duration duration); + PlayerErrors fadePan(SoundHandle handle, double to, Duration duration); /// Smoothly change a channel's relative play speed over specified time. @mustBeOverridden - int fadeRelativePlaySpeed(SoundHandle handle, double to, Duration time); + PlayerErrors fadeRelativePlaySpeed( + SoundHandle handle, + double to, + Duration time, + ); /// After specified [duration], pause the channel. @mustBeOverridden - int schedulePause(SoundHandle handle, Duration duration); + PlayerErrors schedulePause(SoundHandle handle, Duration duration); /// After specified time, stop the channel. @mustBeOverridden - int scheduleStop(SoundHandle handle, Duration duration); + PlayerErrors scheduleStop(SoundHandle handle, Duration duration); /// Set fader to oscillate the volume at specified frequency. @mustBeOverridden - int oscillateVolume( + PlayerErrors oscillateVolume( SoundHandle handle, double from, double to, @@ -569,11 +573,12 @@ abstract class FlutterSoLoud { /// Set fader to oscillate the panning at specified frequency. @mustBeOverridden - int oscillatePan(SoundHandle handle, double from, double to, Duration time); + PlayerErrors oscillatePan( + SoundHandle handle, double from, double to, Duration time); /// Set fader to oscillate the relative play speed at specified frequency. @mustBeOverridden - int oscillateRelativePlaySpeed( + PlayerErrors oscillateRelativePlaySpeed( SoundHandle handle, double from, double to, @@ -582,7 +587,45 @@ abstract class FlutterSoLoud { /// Set fader to oscillate the global volume at specified frequency. @mustBeOverridden - int oscillateGlobalVolume(double from, double to, Duration time); + PlayerErrors oscillateGlobalVolume(double from, double to, Duration time); + + /// Fade a parameter of a filter. + /// + /// [handle] the handle of the voice to apply the fade. If equal to 0, + /// it fades the global filter. + /// [filterType] filter to modify a param. + /// [attributeId] the attribute index to fade. + /// [to] value the attribute should go in [time] duration. + /// [time] the fade slope duration. + /// Returns [PlayerErrors.noError] if no errors. + @mustBeOverridden + PlayerErrors fadeFilterParameter( + FilterType filterType, + int attributeId, + double to, + double time, { + SoundHandle handle = const SoundHandle(0), + }); + + /// Oscillate a parameter of a filter. + /// + /// [handle] the handle of the voice to apply the fade. If equal to 0, + /// it fades the global filter. + /// [filterType] filter to modify a param. + /// [attributeId] the attribute index to fade. + /// [from] the starting value the attribute sould start to oscillate. + /// [to] the ending value the attribute sould end to oscillate. + /// [time] the fade slope duration. + /// Returns [PlayerErrors.noError] if no errors. + @mustBeOverridden + PlayerErrors oscillateFilterParameter( + FilterType filterType, + int attributeId, + double from, + double to, + double time, { + SoundHandle handle = const SoundHandle(0), + }); // /////////////////////////////////////// // Filters @@ -594,7 +637,10 @@ abstract class FlutterSoLoud { /// Returns [PlayerErrors.noError] if no errors and the index of /// the active filter (-1 if the filter is not active). @mustBeOverridden - ({PlayerErrors error, int index}) isFilterActive(FilterType filterType); + ({PlayerErrors error, int index}) isFilterActive( + FilterType filterType, { + SoundHash soundHash = const SoundHash.invalid(), + }); /// Get parameters names of the given filter. /// @@ -616,29 +662,48 @@ abstract class FlutterSoLoud { /// [PlayerErrors.maxNumberOfFiltersReached] when the maximum number of /// filters has been reached (default is 8). @mustBeOverridden - PlayerErrors addGlobalFilter(FilterType filterType); + PlayerErrors addFilter( + FilterType filterType, { + SoundHash soundHash = const SoundHash.invalid(), + }); /// Remove the filter [filterType]. /// /// [filterType] filter to remove. /// Returns [PlayerErrors.noError] if no errors. @mustBeOverridden - int removeGlobalFilter(FilterType filterType); + PlayerErrors removeFilter( + FilterType filterType, { + SoundHash soundHash = const SoundHash.invalid(), + }); /// Set the effect parameter with id [attributeId] of [filterType] /// with [value] value. /// + /// [handle] the handle to set the filter to. If equal to 0, the filter is + /// applyed to the global filter. /// [filterType] filter to modify a param. /// Returns [PlayerErrors.noError] if no errors. @mustBeOverridden - int setFilterParams(FilterType filterType, int attributeId, double value); + PlayerErrors setFilterParams( + FilterType filterType, + int attributeId, + double value, { + SoundHandle handle = const SoundHandle.error(), + }); - /// Get the effect parameter with id [attributeId] of [filterType]. + /// Get the effect parameter value with id [attributeId] of [filterType]. /// + /// [handle] the handle to get the attribute value from. If equal to 0, + /// it gets the global filter. /// [filterType] the filter to modify a parameter. /// Returns the value of the parameter. @mustBeOverridden - double getFilterParams(FilterType filterType, int attributeId); + ({PlayerErrors error, double value}) getFilterParams( + FilterType filterType, + int attributeId, { + SoundHandle handle = const SoundHandle.error(), + }); // /////////////////////////////////////// // 3D audio methods diff --git a/lib/src/bindings/bindings_player_ffi.dart b/lib/src/bindings/bindings_player_ffi.dart index 36cb36e..7d80cf4 100644 --- a/lib/src/bindings/bindings_player_ffi.dart +++ b/lib/src/bindings/bindings_player_ffi.dart @@ -880,7 +880,7 @@ class FlutterSoLoudFfi extends FlutterSoLoud { _destroyVoiceGroupPtr.asFunction(); @override - void addVoiceToGroup( + void addVoicesToGroup( SoundHandle voiceGroupHandle, List voiceHandles, ) { @@ -922,8 +922,9 @@ class FlutterSoLoudFfi extends FlutterSoLoud { ///////////////////////////////////////// @override - int fadeGlobalVolume(double to, Duration duration) { - return _fadeGlobalVolume(to, duration.toDouble()); + PlayerErrors fadeGlobalVolume(double to, Duration duration) { + final e = _fadeGlobalVolume(to, duration.toDouble()); + return PlayerErrors.values[e]; } late final _fadeGlobalVolumePtr = @@ -933,8 +934,9 @@ class FlutterSoLoudFfi extends FlutterSoLoud { _fadeGlobalVolumePtr.asFunction(); @override - int fadeVolume(SoundHandle handle, double to, Duration duration) { - return _fadeVolume(handle.id, to, duration.toDouble()); + PlayerErrors fadeVolume(SoundHandle handle, double to, Duration duration) { + final e = _fadeVolume(handle.id, to, duration.toDouble()); + return PlayerErrors.values[e]; } late final _fadeVolumePtr = _lookup< @@ -945,8 +947,9 @@ class FlutterSoLoudFfi extends FlutterSoLoud { _fadeVolumePtr.asFunction(); @override - int fadePan(SoundHandle handle, double to, Duration duration) { - return _fadePan(handle.id, to, duration.toDouble()); + PlayerErrors fadePan(SoundHandle handle, double to, Duration duration) { + final e = _fadePan(handle.id, to, duration.toDouble()); + return PlayerErrors.values[e]; } late final _fadePanPtr = _lookup< @@ -957,8 +960,13 @@ class FlutterSoLoudFfi extends FlutterSoLoud { _fadePanPtr.asFunction(); @override - int fadeRelativePlaySpeed(SoundHandle handle, double to, Duration time) { - return _fadeRelativePlaySpeed(handle.id, to, time.toDouble()); + PlayerErrors fadeRelativePlaySpeed( + SoundHandle handle, + double to, + Duration time, + ) { + final e = _fadeRelativePlaySpeed(handle.id, to, time.toDouble()); + return PlayerErrors.values[e]; } late final _fadeRelativePlaySpeedPtr = _lookup< @@ -969,8 +977,9 @@ class FlutterSoLoudFfi extends FlutterSoLoud { _fadeRelativePlaySpeedPtr.asFunction(); @override - int schedulePause(SoundHandle handle, Duration duration) { - return _schedulePause(handle.id, duration.toDouble()); + PlayerErrors schedulePause(SoundHandle handle, Duration duration) { + final e = _schedulePause(handle.id, duration.toDouble()); + return PlayerErrors.values[e]; } late final _schedulePausePtr = _lookup< @@ -980,8 +989,9 @@ class FlutterSoLoudFfi extends FlutterSoLoud { _schedulePausePtr.asFunction(); @override - int scheduleStop(SoundHandle handle, Duration duration) { - return _scheduleStop(handle.id, duration.toDouble()); + PlayerErrors scheduleStop(SoundHandle handle, Duration duration) { + final e = _scheduleStop(handle.id, duration.toDouble()); + return PlayerErrors.values[e]; } late final _scheduleStopPtr = _lookup< @@ -991,9 +1001,10 @@ class FlutterSoLoudFfi extends FlutterSoLoud { _scheduleStopPtr.asFunction(); @override - int oscillateVolume( + PlayerErrors oscillateVolume( SoundHandle handle, double from, double to, Duration time) { - return _oscillateVolume(handle.id, from, to, time.toDouble()); + final e = _oscillateVolume(handle.id, from, to, time.toDouble()); + return PlayerErrors.values[e]; } late final _oscillateVolumePtr = _lookup< @@ -1004,8 +1015,14 @@ class FlutterSoLoudFfi extends FlutterSoLoud { .asFunction(); @override - int oscillatePan(SoundHandle handle, double from, double to, Duration time) { - return _oscillatePan(handle.id, from, to, time.toDouble()); + PlayerErrors oscillatePan( + SoundHandle handle, + double from, + double to, + Duration time, + ) { + final e = _oscillatePan(handle.id, from, to, time.toDouble()); + return PlayerErrors.values[e]; } late final _oscillatePanPtr = _lookup< @@ -1016,9 +1033,10 @@ class FlutterSoLoudFfi extends FlutterSoLoud { _oscillatePanPtr.asFunction(); @override - int oscillateRelativePlaySpeed( + PlayerErrors oscillateRelativePlaySpeed( SoundHandle handle, double from, double to, Duration time) { - return _oscillateRelativePlaySpeed(handle.id, from, to, time.toDouble()); + final e = _oscillateRelativePlaySpeed(handle.id, from, to, time.toDouble()); + return PlayerErrors.values[e]; } late final _oscillateRelativePlaySpeedPtr = _lookup< @@ -1029,8 +1047,9 @@ class FlutterSoLoudFfi extends FlutterSoLoud { .asFunction(); @override - int oscillateGlobalVolume(double from, double to, Duration time) { - return _oscillateGlobalVolume(from, to, time.toDouble()); + PlayerErrors oscillateGlobalVolume(double from, double to, Duration time) { + final e = _oscillateGlobalVolume(from, to, time.toDouble()); + return PlayerErrors.values[e]; } late final _oscillateGlobalVolumePtr = _lookup< @@ -1040,26 +1059,81 @@ class FlutterSoLoudFfi extends FlutterSoLoud { late final _oscillateGlobalVolume = _oscillateGlobalVolumePtr .asFunction(); + @override + PlayerErrors fadeFilterParameter( + FilterType filterType, + int attributeId, + double to, + double time, { + SoundHandle handle = const SoundHandle(0), + }) { + final e = _fadeFilterParameter( + handle.id, + filterType.index, + attributeId, + to, + time, + ); + return PlayerErrors.values[e]; + } + + late final _fadeFilterParameterPtr = _lookup< + ffi.NativeFunction< + ffi.Int32 Function(ffi.UnsignedInt, ffi.Int32, ffi.Int, ffi.Float, + ffi.Float)>>('fadeFilterParameter'); + late final _fadeFilterParameter = _fadeFilterParameterPtr + .asFunction(); + + @override + PlayerErrors oscillateFilterParameter( + FilterType filterType, + int attributeId, + double from, + double to, + double time, { + SoundHandle handle = const SoundHandle(0), + }) { + final e = _oscillateFilterParameter( + handle.id, + filterType.index, + attributeId, + from, + to, + time, + ); + return PlayerErrors.values[e]; + } + + late final _oscillateFilterParameterPtr = _lookup< + ffi.NativeFunction< + ffi.Int32 Function(ffi.UnsignedInt, ffi.Int32, ffi.Int, ffi.Float, + ffi.Float, ffi.Float)>>('oscillateFilterParameter'); + late final _oscillateFilterParameter = _oscillateFilterParameterPtr + .asFunction(); + // /////////////////////////////////////// // Filters // /////////////////////////////////////// @override - ({PlayerErrors error, int index}) isFilterActive(FilterType filterType) { + ({PlayerErrors error, int index}) isFilterActive( + FilterType filterType, { + SoundHash soundHash = const SoundHash.invalid(), + }) { // ignore: omit_local_variable_types final ffi.Pointer id = calloc(ffi.sizeOf()); - final e = _isFilterActive(filterType.index, id); + final e = _isFilterActive(soundHash.hash, filterType.index, id); final ret = (error: PlayerErrors.values[e], index: id.value); calloc.free(id); return ret; } late final _isFilterActivePtr = _lookup< - ffi - .NativeFunction)>>( - 'isFilterActive'); - late final _isFilterActive = - _isFilterActivePtr.asFunction)>(); + ffi.NativeFunction< + ffi.Int32 Function(ffi.UnsignedInt, ffi.Int32, + ffi.Pointer)>>('isFilterActive'); + late final _isFilterActive = _isFilterActivePtr + .asFunction)>(); @override ({PlayerErrors error, List names}) getFilterParamNames( @@ -1103,49 +1177,82 @@ class FlutterSoLoudFfi extends FlutterSoLoud { int, ffi.Pointer, ffi.Pointer>)>(); @override - PlayerErrors addGlobalFilter(FilterType filterType) { - final e = _addGlobalFilter(filterType.index); + PlayerErrors addFilter( + FilterType filterType, { + SoundHash soundHash = const SoundHash.invalid(), + }) { + final e = _addFilter(soundHash.hash, filterType.index); return PlayerErrors.values[e]; } - late final _addGlobalFilterPtr = - _lookup>( - 'addGlobalFilter'); - late final _addGlobalFilter = - _addGlobalFilterPtr.asFunction(); + late final _addFilterPtr = _lookup< + ffi.NativeFunction>( + 'addFilter'); + late final _addFilter = _addFilterPtr.asFunction(); @override - int removeGlobalFilter(FilterType filterType) { - return _removeGlobalFilter(filterType.index); + PlayerErrors removeFilter( + FilterType filterType, { + SoundHash soundHash = const SoundHash.invalid(), + }) { + final e = _removeFilter(soundHash.hash, filterType.index); + return PlayerErrors.values[e]; } - late final _removeGlobalFilterPtr = - _lookup>( - 'removeGlobalFilter'); - late final _removeGlobalFilter = - _removeGlobalFilterPtr.asFunction(); + late final _removeFilterPtr = _lookup< + ffi.NativeFunction>( + 'removeFilter'); + late final _removeFilter = + _removeFilterPtr.asFunction(); @override - int setFilterParams(FilterType filterType, int attributeId, double value) { - return _setFxParams(filterType.index, attributeId, value); + PlayerErrors setFilterParams( + FilterType filterType, + int attributeId, + double value, { + SoundHandle handle = const SoundHandle.error(), + }) { + final e = _setFilterParams( + handle.isError ? 0 : handle.id, + filterType.index, + attributeId, + value, + ); + return PlayerErrors.values[e]; } - late final _setFxParamsPtr = _lookup< + late final _setFilterParamsPtr = _lookup< ffi.NativeFunction< - ffi.Int32 Function(ffi.Int32, ffi.Int, ffi.Float)>>('setFxParams'); - late final _setFxParams = - _setFxParamsPtr.asFunction(); + ffi.Int32 Function(ffi.UnsignedInt, ffi.Int32, ffi.Int, + ffi.Float)>>('setFilterParams'); + late final _setFilterParams = + _setFilterParamsPtr.asFunction(); @override - double getFilterParams(FilterType filterType, int attributeId) { - return _getFxParams(filterType.index, attributeId); + ({PlayerErrors error, double value}) getFilterParams( + FilterType filterType, + int attributeId, { + SoundHandle handle = const SoundHandle.error(), + }) { + // ignore: omit_local_variable_types + final ffi.Pointer paramValue = calloc(); + final error = _getFilterParams( + handle.isError ? 0 : handle.id, + filterType.index, + attributeId, + paramValue, + ); + final ret = paramValue.value; + calloc.free(paramValue); + return (error: PlayerErrors.values[error], value: ret); } - late final _getFxParamsPtr = - _lookup>( - 'getFxParams'); - late final _getFxParams = - _getFxParamsPtr.asFunction(); + late final _getFilterParamsPtr = _lookup< + ffi.NativeFunction< + ffi.Int32 Function(ffi.UnsignedInt, ffi.Int32, ffi.Int, + ffi.Pointer)>>('getFilterParams'); + late final _getFilterParams = _getFilterParamsPtr + .asFunction)>(); ///////////////////////////////////////// /// 3D audio methods diff --git a/lib/src/bindings/bindings_player_web.dart b/lib/src/bindings/bindings_player_web.dart index 13412c0..98c1208 100644 --- a/lib/src/bindings/bindings_player_web.dart +++ b/lib/src/bindings/bindings_player_web.dart @@ -434,7 +434,10 @@ class FlutterSoLoudWeb extends FlutterSoLoud { @override SoundHandle createVoiceGroup() { - final ret = wasmCreateVoiceGroup(); + /// The group handle returned has the sign bit flagged. Since on the web + /// the int is a signed 32 bit, a negative number will be returned. + /// Fixing by ORing the result.< + final ret = wasmCreateVoiceGroup() | 0xfffff000; return SoundHandle(ret > 0 ? ret : -1); } @@ -444,7 +447,7 @@ class FlutterSoLoudWeb extends FlutterSoLoud { } @override - void addVoiceToGroup( + void addVoicesToGroup( SoundHandle voiceGroupHandle, List voiceHandles, ) { @@ -468,63 +471,125 @@ class FlutterSoLoudWeb extends FlutterSoLoud { // /////////////////////////////////////// @override - int fadeGlobalVolume(double to, Duration duration) { - return wasmFadeGlobalVolume(to, duration.toDouble()); + PlayerErrors fadeGlobalVolume(double to, Duration duration) { + final e = wasmFadeGlobalVolume(to, duration.toDouble()); + return PlayerErrors.values[e]; } @override - int fadeVolume(SoundHandle handle, double to, Duration duration) { - return wasmFadeVolume(handle.id, to, duration.toDouble()); + PlayerErrors fadeVolume(SoundHandle handle, double to, Duration duration) { + final e = wasmFadeVolume(handle.id, to, duration.toDouble()); + return PlayerErrors.values[e]; } @override - int fadePan(SoundHandle handle, double to, Duration duration) { - return wasmFadePan(handle.id, to, duration.toDouble()); + PlayerErrors fadePan(SoundHandle handle, double to, Duration duration) { + final e = wasmFadePan(handle.id, to, duration.toDouble()); + return PlayerErrors.values[e]; } @override - int fadeRelativePlaySpeed(SoundHandle handle, double to, Duration time) { - return wasmFadeRelativePlaySpeed(handle.id, to, time.toDouble()); + PlayerErrors fadeRelativePlaySpeed( + SoundHandle handle, + double to, + Duration time, + ) { + final e = wasmFadeRelativePlaySpeed(handle.id, to, time.toDouble()); + return PlayerErrors.values[e]; } @override - int schedulePause(SoundHandle handle, Duration duration) { - return wasmSchedulePause(handle.id, duration.toDouble()); + PlayerErrors schedulePause(SoundHandle handle, Duration duration) { + final e = wasmSchedulePause(handle.id, duration.toDouble()); + return PlayerErrors.values[e]; } @override - int scheduleStop(SoundHandle handle, Duration duration) { - return wasmScheduleStop(handle.id, duration.toDouble()); + PlayerErrors scheduleStop(SoundHandle handle, Duration duration) { + final e = wasmScheduleStop(handle.id, duration.toDouble()); + return PlayerErrors.values[e]; } @override - int oscillateVolume( + PlayerErrors oscillateVolume( SoundHandle handle, double from, double to, Duration time, ) { - return wasmOscillateVolume(handle.id, from, to, time.toDouble()); + final e = wasmOscillateVolume(handle.id, from, to, time.toDouble()); + return PlayerErrors.values[e]; } @override - int oscillatePan(SoundHandle handle, double from, double to, Duration time) { - return wasmOscillatePan(handle.id, from, to, time.toDouble()); + PlayerErrors oscillatePan( + SoundHandle handle, + double from, + double to, + Duration time, + ) { + final e = wasmOscillatePan(handle.id, from, to, time.toDouble()); + return PlayerErrors.values[e]; } @override - int oscillateRelativePlaySpeed( + PlayerErrors oscillateRelativePlaySpeed( SoundHandle handle, double from, double to, Duration time, ) { - return wasmOscillateRelativePlaySpeed(handle.id, from, to, time.toDouble()); + final e = wasmOscillateRelativePlaySpeed( + handle.id, + from, + to, + time.toDouble(), + ); + return PlayerErrors.values[e]; } @override - int oscillateGlobalVolume(double from, double to, Duration time) { - return wasmOscillateGlobalVolume(from, to, time.toDouble()); + PlayerErrors oscillateGlobalVolume(double from, double to, Duration time) { + final e = wasmOscillateGlobalVolume(from, to, time.toDouble()); + return PlayerErrors.values[e]; + } + + @override + PlayerErrors fadeFilterParameter( + FilterType filterType, + int attributeId, + double to, + double time, { + SoundHandle handle = const SoundHandle.error(), + }) { + final e = wasmFadeFilterParameter( + handle.isError ? 0 : handle.id, + filterType.index, + attributeId, + to, + time, + ); + return PlayerErrors.values[e]; + } + + @override + PlayerErrors oscillateFilterParameter( + FilterType filterType, + int attributeId, + double from, + double to, + double time, { + SoundHandle handle = const SoundHandle.error(), + }) { + final e = wasmOscillateFilterParameter( + handle.isError ? 0 : handle.id, + filterType.index, + attributeId, + from, + to, + time, + ); + return PlayerErrors.values[e]; } // /////////////////////////////////////// @@ -532,10 +597,13 @@ class FlutterSoLoudWeb extends FlutterSoLoud { // /////////////////////////////////////// @override - ({PlayerErrors error, int index}) isFilterActive(FilterType filterType) { + ({PlayerErrors error, int index}) isFilterActive( + FilterType filterType, { + SoundHash soundHash = const SoundHash.invalid(), + }) { // ignore: omit_local_variable_types final idPtr = wasmMalloc(4); // 4 bytes for an int - final e = wasmIsFilterActive(filterType.index, idPtr); + final e = wasmIsFilterActive(soundHash.hash, filterType.index, idPtr); final index = wasmGetI32Value(idPtr, 'i32'); final ret = (error: PlayerErrors.values[e], index: index); wasmFree(idPtr); @@ -569,24 +637,55 @@ class FlutterSoLoudWeb extends FlutterSoLoud { } @override - PlayerErrors addGlobalFilter(FilterType filterType) { - final e = wasmAddGlobalFilter(filterType.index); + PlayerErrors addFilter( + FilterType filterType, { + SoundHash soundHash = const SoundHash.invalid(), + }) { + final e = wasmAddFilter(soundHash.hash, filterType.index); return PlayerErrors.values[e]; } @override - int removeGlobalFilter(FilterType filterType) { - return wasmRemoveGlobalFilter(filterType.index); + PlayerErrors removeFilter( + FilterType filterType, { + SoundHash soundHash = const SoundHash.invalid(), + }) { + final e = wasmRemoveFilter(soundHash.hash, filterType.index); + return PlayerErrors.values[e]; } @override - int setFilterParams(FilterType filterType, int attributeId, double value) { - return wasmSetFxParams(filterType.index, attributeId, value); + PlayerErrors setFilterParams( + FilterType filterType, + int attributeId, + double value, { + SoundHandle handle = const SoundHandle(0), + }) { + final e = wasmSetFilterParams( + handle.id, + filterType.index, + attributeId, + value, + ); + return PlayerErrors.values[e]; } @override - double getFilterParams(FilterType filterType, int attributeId) { - return wasmGetFxParams(filterType.index, attributeId); + ({PlayerErrors error, double value}) getFilterParams( + FilterType filterType, + int attributeId, { + SoundHandle handle = const SoundHandle(0), + }) { + final paramValuePtr = wasmMalloc(4); + final error = wasmGetFilterParams( + handle.id, + filterType.index, + attributeId, + paramValuePtr, + ); + final ret = wasmGetF32Value(paramValuePtr, 'float'); + wasmFree(paramValuePtr); + return (error: PlayerErrors.values[error], value: ret); } // ////////////////////////////////////// diff --git a/lib/src/bindings/js_extension.dart b/lib/src/bindings/js_extension.dart index 681fc45..245414d 100644 --- a/lib/src/bindings/js_extension.dart +++ b/lib/src/bindings/js_extension.dart @@ -242,9 +242,9 @@ external int wasmGetMaxActiveVoiceCount(); @JS('Module._setMaxActiveVoiceCount') external void wasmSetMaxActiveVoiceCount(int maxVoiceCount); - ///////////////////////////////////////// - /// voice groups - ///////////////////////////////////////// +///////////////////////////////////////// +/// voice groups +///////////////////////////////////////// @JS('Module._createVoiceGroup') external int wasmCreateVoiceGroup(); @@ -261,9 +261,9 @@ external int wasmIsVoiceGroup(int handle); @JS('Module._isVoiceGroupEmpty') external int wasmIsVoiceGroupEmpty(int handle); - // /////////////////////////////////////// - // faders - // /////////////////////////////////////// +// /////////////////////////////////////// +// faders +// /////////////////////////////////////// @JS('Module._fadeGlobalVolume') external int wasmFadeGlobalVolume(double to, double duration); @@ -305,8 +305,31 @@ external int wasmOscillateRelativePlaySpeed( @JS('Module._oscillateGlobalVolume') external int wasmOscillateGlobalVolume(double from, double to, double time); +@JS('Module._fadeFilterParameter') +external int wasmFadeFilterParameter( + int handle, + int filterType, + int attributeId, + double to, + double time, +); + +@JS('Module._oscillateFilterParameter') +external int wasmOscillateFilterParameter( + int handle, + int filterType, + int attributeId, + double from, + double to, + double time, +); + +// /////////////////////////////////////// +// Filters +// /////////////////////////////////////// + @JS('Module._isFilterActive') -external int wasmIsFilterActive(int filterType, int idPtr); +external int wasmIsFilterActive(int soundHash, int filterType, int idPtr); @JS('Module._getFilterParamNames') external int wasmGetFilterParamNames( @@ -315,17 +338,27 @@ external int wasmGetFilterParamNames( int namesPtr, ); -@JS('Module._addGlobalFilter') -external int wasmAddGlobalFilter(int filterType); +@JS('Module._addFilter') +external int wasmAddFilter(int soundHash, int filterType); -@JS('Module._removeGlobalFilter') -external int wasmRemoveGlobalFilter(int filterType); +@JS('Module._removeFilter') +external int wasmRemoveFilter(int soundHash, int filterType); -@JS('Module._setFxParams') -external int wasmSetFxParams(int filterType, int attributeId, double value); +@JS('Module._setFilterParams') +external int wasmSetFilterParams( + int handle, + int filterType, + int attributeId, + double value, +); -@JS('Module._getFxParams') -external double wasmGetFxParams(int filterType, int attributeId); +@JS('Module._getFilterParams') +external int wasmGetFilterParams( + int handle, + int filterType, + int attributeId, + int paramValuePtr, +); @JS('Module._play3d') external int wasmPlay3d( diff --git a/lib/src/soloud.dart b/lib/src/soloud.dart index f6590f4..2d274c8 100644 --- a/lib/src/soloud.dart +++ b/lib/src/soloud.dart @@ -6,6 +6,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:flutter_soloud/src/audio_source.dart'; import 'package:flutter_soloud/src/bindings/audio_data.dart'; +import 'package:flutter_soloud/src/bindings/bindings_player.dart'; import 'package:flutter_soloud/src/bindings/soloud_controller.dart'; import 'package:flutter_soloud/src/enums.dart'; import 'package:flutter_soloud/src/exceptions/exceptions.dart'; @@ -1362,11 +1363,11 @@ interface class SoLoud { /// used separate from the group. /// [voiceGroupHandle] the group handle to add the new [voiceHandles]. /// [voiceHandles] voice handle to add to the [voiceGroupHandle]. - void addVoiceToGroup( + void addVoicesToGroup( SoundHandle voiceGroupHandle, List voiceHandles, ) { - return _controller.soLoudFFI.addVoiceToGroup( + return _controller.soLoudFFI.addVoicesToGroup( voiceGroupHandle, voiceHandles, ); @@ -1403,8 +1404,7 @@ interface class SoLoud { if (!isInitialized) { throw const SoLoudNotInitializedException(); } - final ret = _controller.soLoudFFI.fadeGlobalVolume(to, time); - final error = PlayerErrors.values[ret]; + final error = _controller.soLoudFFI.fadeGlobalVolume(to, time); if (error != PlayerErrors.noError) { _log.severe(() => 'fadeGlobalVolume(): $error'); throw SoLoudCppException.fromPlayerError(error); @@ -1421,8 +1421,7 @@ interface class SoLoud { if (!isInitialized) { throw const SoLoudNotInitializedException(); } - final ret = _controller.soLoudFFI.fadeVolume(handle, to, time); - final error = PlayerErrors.values[ret]; + final error = _controller.soLoudFFI.fadeVolume(handle, to, time); if (error != PlayerErrors.noError) { _log.severe(() => 'fadeVolume(): $error'); throw SoLoudCppException.fromPlayerError(error); @@ -1439,8 +1438,7 @@ interface class SoLoud { if (!isInitialized) { throw const SoLoudNotInitializedException(); } - final ret = _controller.soLoudFFI.fadePan(handle, to, time); - final error = PlayerErrors.values[ret]; + final error = _controller.soLoudFFI.fadePan(handle, to, time); if (error != PlayerErrors.noError) { _log.severe(() => 'fadePan(): $error'); throw SoLoudCppException.fromPlayerError(error); @@ -1457,8 +1455,7 @@ interface class SoLoud { if (!isInitialized) { throw const SoLoudNotInitializedException(); } - final ret = _controller.soLoudFFI.fadeRelativePlaySpeed(handle, to, time); - final error = PlayerErrors.values[ret]; + final error = _controller.soLoudFFI.fadeRelativePlaySpeed(handle, to, time); if (error != PlayerErrors.noError) { _log.severe(() => 'fadeRelativePlaySpeed(): $error'); throw SoLoudCppException.fromPlayerError(error); @@ -1474,8 +1471,7 @@ interface class SoLoud { if (!isInitialized) { throw const SoLoudNotInitializedException(); } - final ret = _controller.soLoudFFI.schedulePause(handle, time); - final error = PlayerErrors.values[ret]; + final error = _controller.soLoudFFI.schedulePause(handle, time); if (error != PlayerErrors.noError) { _log.severe(() => 'schedulePause(): $error'); throw SoLoudCppException.fromPlayerError(error); @@ -1491,8 +1487,7 @@ interface class SoLoud { if (!isInitialized) { throw const SoLoudNotInitializedException(); } - final ret = _controller.soLoudFFI.scheduleStop(handle, time); - final error = PlayerErrors.values[ret]; + final error = _controller.soLoudFFI.scheduleStop(handle, time); if (error != PlayerErrors.noError) { _log.severe(() => 'scheduleStop(): $error'); throw SoLoudCppException.fromPlayerError(error); @@ -1513,8 +1508,7 @@ interface class SoLoud { if (!isInitialized) { throw const SoLoudNotInitializedException(); } - final ret = _controller.soLoudFFI.oscillateVolume(handle, from, to, time); - final error = PlayerErrors.values[ret]; + final error = _controller.soLoudFFI.oscillateVolume(handle, from, to, time); if (error != PlayerErrors.noError) { _log.severe(() => 'oscillateVolume(): $error'); throw SoLoudCppException.fromPlayerError(error); @@ -1534,8 +1528,7 @@ interface class SoLoud { if (!isInitialized) { throw const SoLoudNotInitializedException(); } - final ret = _controller.soLoudFFI.oscillatePan(handle, from, to, time); - final error = PlayerErrors.values[ret]; + final error = _controller.soLoudFFI.oscillatePan(handle, from, to, time); if (error != PlayerErrors.noError) { _log.severe(() => 'oscillatePan(): $error'); throw SoLoudCppException.fromPlayerError(error); @@ -1556,9 +1549,8 @@ interface class SoLoud { if (!isInitialized) { throw const SoLoudNotInitializedException(); } - final ret = _controller.soLoudFFI + final error = _controller.soLoudFFI .oscillateRelativePlaySpeed(handle, from, to, time); - final error = PlayerErrors.values[ret]; if (error != PlayerErrors.noError) { _log.severe(() => 'oscillateRelativePlaySpeed(): $error'); throw SoLoudCppException.fromPlayerError(error); @@ -1576,16 +1568,78 @@ interface class SoLoud { if (!isInitialized) { throw const SoLoudNotInitializedException(); } - final ret = _controller.soLoudFFI.oscillateGlobalVolume(from, to, time); - final error = PlayerErrors.values[ret]; + final error = _controller.soLoudFFI.oscillateGlobalVolume(from, to, time); if (error != PlayerErrors.noError) { _log.severe(() => 'oscillateGlobalVolume(): $error'); throw SoLoudCppException.fromPlayerError(error); } } + /// Fade a parameter of a filter. + /// + /// it fades the global filter. + /// [filterType] filter to modify a param. + /// [attributeId] the attribute index to fade. + /// [to] value the attribute should go in [time] duration. + /// [time] the fade slope duration. + /// + /// Throws [SoLoudNotInitializedException] if the engine is not initialized. + void fadeGlobalFilterParameter( + FilterType filterType, + int attributeId, + double to, + Duration time, + ) { + if (!isInitialized) { + throw const SoLoudNotInitializedException(); + } + final error = _controller.soLoudFFI.fadeFilterParameter( + filterType, + attributeId, + to, + time.toDouble(), + ); + if (error != PlayerErrors.noError) { + _log.severe(() => 'fadeFilterParameter(): $error'); + throw SoLoudCppException.fromPlayerError(error); + } + } + + /// Oscillate a parameter of a filter. + /// + /// it fades the global filter. + /// [filterType] filter to modify a param. + /// [attributeId] the attribute index to fade. + /// [from] the starting value the attribute sould start to oscillate. + /// [to] the ending value the attribute sould end to oscillate. + /// [time] the fade slope duration. + /// + /// Throws [SoLoudNotInitializedException] if the engine is not initialized. + void oscillateGlobalFilterParameter( + FilterType filterType, + int attributeId, + double from, + double to, + Duration time, + ) { + if (!isInitialized) { + throw const SoLoudNotInitializedException(); + } + final error = _controller.soLoudFFI.oscillateFilterParameter( + filterType, + attributeId, + from, + to, + time.toDouble(), + ); + if (error != PlayerErrors.noError) { + _log.severe(() => 'oscillateFilterParameter(): $error'); + throw SoLoudCppException.fromPlayerError(error); + } + } + // /////////////////////////////////////// - // / Filters + // / Global filters // /////////////////////////////////////// /// Checks whether the given [filterType] is active. @@ -1601,8 +1655,6 @@ interface class SoLoud { return ret.index; } - // TODO(marco): add a method to rearrange filters order? - /// Gets parameters of the given [filterType]. /// /// Returns the list of param names. @@ -1622,48 +1674,90 @@ interface class SoLoud { /// Throws [SoLoudFilterAlreadyAddedException] when trying to add a filter /// that has already been added. void addGlobalFilter(FilterType filterType) { - final e = _controller.soLoudFFI.addGlobalFilter(filterType); - if (e != PlayerErrors.noError) { - _log.severe(() => 'addGlobalFilter(): $e'); - throw SoLoudCppException.fromPlayerError(e); + final error = _controller.soLoudFFI.addFilter(filterType); + if (error != PlayerErrors.noError) { + _log.severe(() => 'addGlobalFilter(): $error'); + throw SoLoudCppException.fromPlayerError(error); } } /// Removes [filterType] from all sounds. void removeGlobalFilter(FilterType filterType) { - final ret = _controller.soLoudFFI.removeGlobalFilter(filterType); - final error = PlayerErrors.values[ret]; + final error = _controller.soLoudFFI.removeFilter(filterType); if (error != PlayerErrors.noError) { _log.severe(() => 'removeGlobalFilter(): $error'); throw SoLoudCppException.fromPlayerError(error); } } - /// Sets a parameter of the given [filterType]. + /// Set the effect parameter with id [attributeId] of [filterType] + /// with [value] value. /// /// Specify the [attributeId] of the parameter (which you can learn from /// [getFilterParamNames]), and its new [value]. - void setFilterParameter( - FilterType filterType, int attributeId, double value) { - final ret = - _controller.soLoudFFI.setFilterParams(filterType, attributeId, value); - final error = PlayerErrors.values[ret]; + /// + /// applyed to the global filter. + /// [filterType] filter to modify a param. + /// Returns [PlayerErrors.noError] if no errors. + void setGlobalFilterParameter( + FilterType filterType, + int attributeId, + double value, + ) { + final error = _controller.soLoudFFI.setFilterParams( + filterType, + attributeId, + value, + ); if (error != PlayerErrors.noError) { _log.severe(() => 'setFxParams(): $error'); throw SoLoudCppException.fromPlayerError(error); } } - /// Gets the value of a parameter of the given [filterType]. + /// Set the effect parameter with id [attributeId] of [filterType] + /// with [value] value. + @Deprecated('Please use setGlobalFilterParameter class instead.') + void setFilterParameter( + FilterType filterType, + int attributeId, + double value, + ) => + setGlobalFilterParameter(filterType, attributeId, value); + + /// Get the effect parameter value with id [attributeId] of [filterType]. /// /// Specify the [attributeId] of the parameter (which you can learn from /// [getFilterParamNames]). /// - /// Returns the value as [double]. - double getFilterParameter(FilterType filterType, int attributeId) { - return _controller.soLoudFFI.getFilterParams(filterType, attributeId); + /// it gets the global filter value. + /// [filterType] the filter to modify a parameter. + /// Returns the value of the parameter. + double getGlobalFilterParameter( + FilterType filterType, + int attributeId, + ) { + final ret = _controller.soLoudFFI.getFilterParams( + filterType, + attributeId, + ); + + _logPlayerError(ret.error, from: 'getGlobalFilterParameter()'); + if (ret.error != PlayerErrors.noError) { + throw SoLoudCppException.fromPlayerError(ret.error); + } + return ret.value; } + /// Get the effect parameter value with id [attributeId] of [filterType]. + @Deprecated('Please use getGlobalFilterParameter class instead.') + double getFilterParameter( + FilterType filterType, + int attributeId, { + SoundHandle handle = const SoundHandle(0), + }) => + getGlobalFilterParameter(filterType, attributeId); + // //////////////////////////////////////////////// // Below all the methods implemented with FFI for the 3D audio // more info: https://solhsa.com/soloud/core3d.html diff --git a/lib/src/sound_handle.dart b/lib/src/sound_handle.dart index 3df886f..c524484 100644 --- a/lib/src/sound_handle.dart +++ b/lib/src/sound_handle.dart @@ -16,10 +16,10 @@ import 'package:meta/meta.dart'; /// /// Constructors are marked [internal] because it should not be possible /// for users to create a handle from Dart. -extension type SoundHandle._(int id) { +extension type const SoundHandle._(int id) { /// Constructs a valid handle with [id]. @internal - SoundHandle(this.id) + const SoundHandle(this.id) : assert( id >= 0, 'Handle with id<0 is being constructed. ' @@ -28,7 +28,7 @@ extension type SoundHandle._(int id) { /// Constructs an invalid handle (for APIs that need to return _some_ handle /// even during errors). @internal - SoundHandle.error() : this._(-1); + const SoundHandle.error() : this._(-1); /// Checks if the handle represents an error (it was constructed /// with [SoundHandle.error]). diff --git a/lib/src/sound_hash.dart b/lib/src/sound_hash.dart index de3b5d2..3a34741 100644 --- a/lib/src/sound_hash.dart +++ b/lib/src/sound_hash.dart @@ -17,10 +17,10 @@ import 'package:meta/meta.dart'; /// /// Constructors are marked [internal] because it should not be possible /// for users to create a sound hash from Dart. -extension type SoundHash._(int hash) { +extension type const SoundHash._(int hash) { /// Constructs a valid sound hash with [hash]. @internal - SoundHash(this.hash) + const SoundHash(this.hash) : assert( hash > 0, 'Trying to create a valid sound hash with the value 0', @@ -29,7 +29,7 @@ extension type SoundHash._(int hash) { /// Constructs an invalid sound hash /// (for APIs that need to return _some_ hash even during errors). @internal - SoundHash.invalid() : this._(0); + const SoundHash.invalid() : this._(0); /// Generate a "fake" [SoundHash] for generated (i.e. non-loaded) sounds. /// diff --git a/src/active_sound.h b/src/active_sound.h new file mode 100644 index 0000000..ce70879 --- /dev/null +++ b/src/active_sound.h @@ -0,0 +1,27 @@ +#ifndef ACTIVE_SOUND_H +#define ACTIVE_SOUND_H + +#include "enums.h" +#include "filters/filters.h" +#include "soloud.h" + +#include +#include +#include + +class Filters; + +/// The default number of concurrent voices - maximum number of "streams" - is 16, +/// but this can be adjusted at runtime +typedef struct ActiveSound +{ + std::shared_ptr sound; + SoundType soundType; + std::vector handle; + std::unique_ptr filters; + // unique identifier of this sound based on the file name + unsigned int soundHash; + std::string completeFileName; +} ActiveSound; + +#endif // ACTIVE_SOUND_H \ No newline at end of file diff --git a/src/bindings.cpp b/src/bindings.cpp index beb695f..ea780da 100644 --- a/src/bindings.cpp +++ b/src/bindings.cpp @@ -881,42 +881,58 @@ extern "C" ///////////////////////////////////////// /// Used to create a new voice group. Returns 0 if not successful. - FFI_PLUGIN_EXPORT unsigned int createVoiceGroup() { - return player.get()->createVoiceGroup(); + FFI_PLUGIN_EXPORT unsigned int createVoiceGroup() + { + if (player.get() == nullptr || !player.get()->isInited()) + return -1; + auto ret = player.get()->createVoiceGroup(); + return ret; } - /// Deallocates the voice group. Does not stop the voices attached to the + /// Deallocates the voice group. Does not stop the voices attached to the /// voice group. /// /// [handle] the group handle to destroy. - FFI_PLUGIN_EXPORT void destroyVoiceGroup(unsigned int handle) { + FFI_PLUGIN_EXPORT void destroyVoiceGroup(unsigned int handle) + { + if (player.get() == nullptr || !player.get()->isInited()) + return; player.get()->destroyVoiceGroup(handle); } - /// Adds voice handle to the voice group. The voice handles can still be + /// Adds voice handle to the voice group. The voice handles can still be /// used separate from the group. /// [voiceGroupHandle] the group handle to add the new [voiceHandle]. /// [voiceHandle] voice handle to add to the [voiceGroupHandle]. - FFI_PLUGIN_EXPORT void addVoiceToGroup(unsigned int voiceGroupHandle, unsigned int voiceHandle) { + FFI_PLUGIN_EXPORT void addVoiceToGroup(unsigned int voiceGroupHandle, unsigned int voiceHandle) + { + if (player.get() == nullptr || !player.get()->isInited()) + return; player.get()->addVoiceToGroup(voiceGroupHandle, voiceHandle); } - /// Checks if the handle is a valid voice group. Does not care if the + /// Checks if the handle is a valid voice group. Does not care if the /// voice group is empty. /// /// [handle] the group handle to check. /// Return true if [handle] is a group handle. - FFI_PLUGIN_EXPORT bool isVoiceGroup(unsigned int handle) { + FFI_PLUGIN_EXPORT bool isVoiceGroup(unsigned int handle) + { + if (player.get() == nullptr || !player.get()->isInited()) + return false; return player.get()->isVoiceGroup(handle); } - /// Checks whether a voice group is empty. SoLoud automatically trims + /// Checks whether a voice group is empty. SoLoud automatically trims /// the voice groups of voices that have ended, so the group may be /// empty even though you've added valid voice handles to it. /// /// [handle] group handle to check. /// Return true if the group handle doesn't have any voices. - FFI_PLUGIN_EXPORT bool isVoiceGroupEmpty(unsigned int handle) { + FFI_PLUGIN_EXPORT bool isVoiceGroupEmpty(unsigned int handle) + { + if (player.get() == nullptr || !player.get()->isInited()) + return false; return player.get()->isVoiceGroupEmpty(handle); } @@ -1020,22 +1036,34 @@ extern "C" /// Check if the given filter is active or not. /// - /// [filterType] filter to check + /// [soundHash] the sound to check the filter. If this is =0 this function + /// searches in the global filters. + /// [filterType] filter to check. /// Returns [PlayerErrors.noError] if no errors and the index of - /// the given filter (-1 if the filter is not active) - FFI_PLUGIN_EXPORT enum PlayerErrors isFilterActive(enum FilterType filterType, int *index) + /// the given filter (-1 if the filter is not active). + FFI_PLUGIN_EXPORT enum PlayerErrors isFilterActive(unsigned int soundHash, enum FilterType filterType, int *index) { *index = -1; if (player.get() == nullptr || !player.get()->isInited()) return backendNotInited; - *index = player.get()->mFilters.isFilterActive(filterType); + + if (soundHash == 0) + *index = player.get()->mFilters.isFilterActive(filterType); + else + { + auto const s = player.get()->findByHash(soundHash); + if (s == 0) + return soundHashNotFound; + *index = s->filters->isFilterActive(filterType); + } + return noError; } /// Get parameters names of the given filter. /// - /// [filterType] filter to get param names - /// Returns [PlayerErrors.noError] if no errors and the list of param names + /// [filterType] filter to get param names. + /// Returns [PlayerErrors.noError] if no errors and the list of param names. FFI_PLUGIN_EXPORT enum PlayerErrors getFilterParamNames( enum FilterType filterType, int *paramsCount, char **names) { @@ -1054,52 +1082,186 @@ extern "C" return noError; } - /// Add the filter [filterType] to all sounds. + /// Add the filter [filterType] to [soundHash]. If [soundHash]==0 the + /// filter is added to global filters. /// - /// [filterType] filter to add - /// Returns [PlayerErrors.noError] if no errors - FFI_PLUGIN_EXPORT enum PlayerErrors addGlobalFilter(enum FilterType filterType) + /// [soundHash] the sound to add the filter to. + /// [filterType] filter to add. + /// Returns [PlayerErrors.noError] if no errors. + FFI_PLUGIN_EXPORT enum PlayerErrors addFilter(unsigned int soundHash, enum FilterType filterType) { if (player.get() == nullptr || !player.get()->isInited()) return backendNotInited; - return player.get()->mFilters.addGlobalFilter(filterType); + if (soundHash == 0) + return player.get()->mFilters.addFilter(filterType); + + auto const s = player.get()->findByHash(soundHash); + if (s == 0) + return soundHashNotFound; + + return s->filters->addFilter(filterType); } - /// Remove the filter [filterType]. + /// Remove the filter [filterType] from [soundHash]. If [soundHash]==0 the + /// filter is removed from the global filters. /// - /// [filterType] filter to remove - /// Returns [PlayerErrors.noError] if no errors - FFI_PLUGIN_EXPORT enum PlayerErrors removeGlobalFilter(enum FilterType filterType) + /// [filterType] filter to remove. + /// Returns [PlayerErrors.noError] if no errors. + FFI_PLUGIN_EXPORT enum PlayerErrors removeFilter(unsigned int soundHash, enum FilterType filterType) { if (player.get() == nullptr || !player.get()->isInited()) return backendNotInited; - if (!player.get()->mFilters.removeGlobalFilter(filterType)) - return filterNotFound; + if (soundHash == 0) + { + if (!player.get()->mFilters.removeFilter(filterType)) + return filterNotFound; + } + else + { + auto const s = player.get()->findByHash(soundHash); + if (s == 0) + return soundHashNotFound; + if (!s->filters->removeFilter(filterType)) + return filterNotFound; + } + return noError; } /// Set the effect parameter with id [attributeId] /// of [filterType] with [value] value. /// - /// [filterType] filter to modify a param - /// Returns [PlayerErrors.noError] if no errors - FFI_PLUGIN_EXPORT enum PlayerErrors setFxParams(enum FilterType filterType, int attributeId, float value) + /// [handle] the handle to set the filter to. If equal to 0, the filter is applyed globally. + /// [filterType] filter to modify a param. + /// Returns [PlayerErrors.noError] if no errors. + FFI_PLUGIN_EXPORT enum PlayerErrors setFilterParams(unsigned int handle, enum FilterType filterType, int attributeId, float value) { if (player.get() == nullptr || !player.get()->isInited()) return backendNotInited; - player.get()->mFilters.setFxParams(filterType, attributeId, value); + /// Not important to call the [SoLoud::AudioSource].setFilterParams() here, SoLoud + /// will set the param globally or by [handle] if [handle] is not ==0. + if (handle == 0) + player.get()->mFilters.setFilterParams(handle, filterType, attributeId, value); + else + { + auto const &s = player.get()->findByHandle(handle); + if (s == 0) + { + return soundHandleNotFound; + } + else + { + s->filters.get()->setFilterParams(handle, filterType, attributeId, value); + } + } return noError; } /// Get the effect parameter with id [attributeId] of [filterType]. /// - /// [filterType] filter to modify a param - /// Returns the value of param - FFI_PLUGIN_EXPORT float getFxParams(enum FilterType filterType, int attributeId) + /// [handle] the handle to get the filter to. If equal to 0, it gets the global filter. + /// [filterType] filter to modify a param. + /// Returns the value of param or 9999.0 if the filter is not found. + FFI_PLUGIN_EXPORT enum PlayerErrors getFilterParams( + unsigned int handle, + enum FilterType filterType, + int attributeId, + float *filterValue) { + *filterValue = 9999.0f; if (player.get() == nullptr || !player.get()->isInited()) return backendNotInited; - return player.get()->mFilters.getFxParams(filterType, attributeId); + /// If [handle] == 0 get the parameter from global filters else from + /// the sound which owns [handle]. + if (handle == 0) + { + *filterValue = player.get()->mFilters.getFilterParams(handle, filterType, attributeId); + return noError; + } + else + { + auto const &s = player.get()->findByHandle(handle); + if (s == 0) + return soundHandleNotFound; + else + { + *filterValue = s->filters.get()->getFilterParams(handle, filterType, attributeId); + if (*filterValue == 9999.0f) + return filterNotFound; + return noError; + } + } + return noError; + } + + /// Fades a parameter of a filter. + /// + /// [handle] the handle of the voice to apply the fade. If equal to 0, it fades the global filter. + /// [filterType] filter to modify a param. + /// [attributeId] the attribute index to fade. + /// [to] value the attribute should go in [time] duration. + /// [time] the fade slope duration. + /// Returns [PlayerErrors.noError] if no errors. + FFI_PLUGIN_EXPORT enum PlayerErrors fadeFilterParameter( + unsigned int handle, + enum FilterType filterType, + int attributeId, + float to, + float time) + { + if (player.get() == nullptr || !player.get()->isInited()) + return backendNotInited; + if (handle == 0) + player.get()->mFilters.fadeFilterParameter(handle, filterType, attributeId, to, time); + else + { + auto const &s = player.get()->findByHandle(handle); + if (s == 0) + { + return soundHandleNotFound; + } + else + { + s->filters.get()->fadeFilterParameter(handle, filterType, attributeId, to, time); + } + } + return noError; + } + + /// Oscillate a parameter of a filter. + /// + /// [handle] the handle of the voice to apply the fade. If equal to 0, it fades the global filter. + /// [filterType] filter to modify a param. + /// [attributeId] the attribute index to fade. + /// [from] the starting value the attribute sould start to oscillate. + /// [to] the ending value the attribute sould end to oscillate. + /// [time] the fade slope duration. + /// Returns [PlayerErrors.noError] if no errors. + FFI_PLUGIN_EXPORT enum PlayerErrors oscillateFilterParameter( + unsigned int handle, + enum FilterType filterType, + int attributeId, + float from, + float to, + float time) + { + if (player.get() == nullptr || !player.get()->isInited()) + return backendNotInited; + if (handle == 0) + player.get()->mFilters.oscillateFilterParameter(handle, filterType, attributeId, from, to, time); + else + { + auto const &s = player.get()->findByHandle(handle); + if (s == 0) + { + return soundHandleNotFound; + } + else + { + s->filters.get()->oscillateFilterParameter(handle, filterType, attributeId, from, to, time); + } + } + return noError; } ///////////////////////////////////////// diff --git a/src/enums.h b/src/enums.h index 96beb7c..200a0c8 100644 --- a/src/enums.h +++ b/src/enums.h @@ -69,4 +69,27 @@ typedef enum PlayerStateEvents event_unlocked, } PlayerEvents_t; +typedef enum SoundType +{ + // using Soloud::wav + TYPE_WAV, + // using Soloud::wavStream + TYPE_WAVSTREAM, + // this sound is a waveform + TYPE_SYNTH +} SoundType_t; + +typedef enum FilterType +{ + BiquadResonantFilter, + EqFilter, + EchoFilter, + LofiFilter, + FlangerFilter, + BassboostFilter, + WaveShaperFilter, + RobotizeFilter, + FreeverbFilter +} FilterType_t; + #endif // ENUMS_H \ No newline at end of file diff --git a/src/ffi_gen_tmp.h b/src/ffi_gen_tmp.h index 3afd42e..219c217 100644 --- a/src/ffi_gen_tmp.h +++ b/src/ffi_gen_tmp.h @@ -23,28 +23,8 @@ struct CaptureDevice //--------------------- copy here the new functions to generate -/// Used to create a new voice group. Returns 0 if not successful. -FFI_PLUGIN_EXPORT unsigned int createVoiceGroup(); - -/// Deallocates the voice group. Does not stop the voices attached to the voice group. -/// -/// [handle] the group handle to destroy. -FFI_PLUGIN_EXPORT void destroyVoiceGroup(unsigned int handle); - -/// Adds voice handle to the voice group. The voice handles can still be used separate from the group. -/// [voiceGroupHandle] the group handle to add the new [handle]. -/// [voiceHandle] voice handle to add to the [voiceGroupHandle]. -FFI_PLUGIN_EXPORT void addVoiceToGroup(unsigned int voiceGroupHandle, unsigned int voiceHandle); - -/// Checks if the handle is a valid voice group. Does not care if the voice group is empty. -/// -/// [handle] the group handle to check. -/// Return true if [handle] is a group handle. -FFI_PLUGIN_EXPORT int isVoiceGroup(unsigned int handle); - -/// Checks whether a voice group is empty. SoLoud automatically trims the voice groups of -/// voices that have ended, so the group may be empty even though you've added valid voice handles to it. -/// -/// [handle] group handle to check. -/// Return true if the group handle doesn't have any voices. -FFI_PLUGIN_EXPORT int isVoiceGroupEmpty(unsigned int handle); +FFI_PLUGIN_EXPORT enum PlayerErrors getFilterParams( + unsigned int handle, + enum FilterType filterType, + int attributeId, + float *filterValue); diff --git a/src/filters/filters.cpp b/src/filters/filters.cpp index b69a564..2de459b 100644 --- a/src/filters/filters.cpp +++ b/src/filters/filters.cpp @@ -4,13 +4,10 @@ #include #include -Filters::Filters(SoLoud::Soloud *soloud) : mSoloud(soloud) -{ -} +Filters::Filters(SoLoud::Soloud *soloud, ActiveSound *sound) + : mSoloud(soloud), mSound(sound), filters({}) {} -Filters::~Filters() -{ -} +Filters::~Filters() {} int Filters::isFilterActive(FilterType filter) { @@ -127,9 +124,9 @@ std::vector Filters::getFilterParamNames(FilterType filterType) return ret; } -PlayerErrors Filters::addGlobalFilter(FilterType filterType) +PlayerErrors Filters::addFilter(FilterType filterType) { - if (filters.size() >= FILTERS_PER_STREAM) + if ((int)filters.size() >= FILTERS_PER_STREAM) return maxNumberOfFiltersReached; // Check if the new filter is already here. @@ -143,55 +140,82 @@ PlayerErrors Filters::addGlobalFilter(FilterType filterType) case BiquadResonantFilter: if (!mBiquadResonantFilter) mBiquadResonantFilter = std::make_unique(); - mSoloud->setGlobalFilter(filtersSize, mBiquadResonantFilter.get()); + if (mSound == nullptr) + mSoloud->setGlobalFilter(filtersSize, mBiquadResonantFilter.get()); + else + mSound->sound.get()->setFilter(filtersSize, mBiquadResonantFilter.get()); filters.push_back({filterType, static_cast(mBiquadResonantFilter.get())}); break; case EqFilter: if (!mEqFilter) mEqFilter = std::make_unique(); - mSoloud->setGlobalFilter(filtersSize, mEqFilter.get()); + if (mSound == nullptr) + mSoloud->setGlobalFilter(filtersSize, mEqFilter.get()); + else + mSound->sound.get()->setFilter(filtersSize, mEqFilter.get()); filters.push_back({filterType, static_cast(mEqFilter.get())}); break; case EchoFilter: if (!mEchoFilter) mEchoFilter = std::make_unique(); - mSoloud->setGlobalFilter(filtersSize, mEchoFilter.get()); + if (mSound == nullptr) + mSoloud->setGlobalFilter(filtersSize, mEchoFilter.get()); + else + mSound->sound.get()->setFilter(filtersSize, mEchoFilter.get()); filters.push_back({filterType, static_cast(mEchoFilter.get())}); break; case LofiFilter: if (!mLofiFilter) mLofiFilter = std::make_unique(); - mSoloud->setGlobalFilter(filtersSize, mLofiFilter.get()); + if (mSound == nullptr) + mSoloud->setGlobalFilter(filtersSize, mLofiFilter.get()); + else + mSound->sound.get()->setFilter(filtersSize, mLofiFilter.get()); filters.push_back({filterType, static_cast(mLofiFilter.get())}); break; case FlangerFilter: if (!mFlangerFilter) mFlangerFilter = std::make_unique(); - mSoloud->setGlobalFilter(filtersSize, mFlangerFilter.get()); + if (mSound == nullptr) + mSoloud->setGlobalFilter(filtersSize, mFlangerFilter.get()); + else + mSound->sound.get()->setFilter(filtersSize, mFlangerFilter.get()); filters.push_back({filterType, static_cast(mFlangerFilter.get())}); break; case BassboostFilter: if (!mBassboostFilter) mBassboostFilter = std::make_unique(); - mSoloud->setGlobalFilter(filtersSize, mBassboostFilter.get()); + if (mSound == nullptr) + mSoloud->setGlobalFilter(filtersSize, mBassboostFilter.get()); + else + mSound->sound.get()->setFilter(filtersSize, mBassboostFilter.get()); filters.push_back({filterType, static_cast(mBassboostFilter.get())}); break; case WaveShaperFilter: if (!mWaveShaperFilter) mWaveShaperFilter = std::make_unique(); - mSoloud->setGlobalFilter(filtersSize, mWaveShaperFilter.get()); + if (mSound == nullptr) + mSoloud->setGlobalFilter(filtersSize, mWaveShaperFilter.get()); + else + mSound->sound.get()->setFilter(filtersSize, mWaveShaperFilter.get()); filters.push_back({filterType, static_cast(mWaveShaperFilter.get())}); break; case RobotizeFilter: if (!mRobotizeFilter) mRobotizeFilter = std::make_unique(); - mSoloud->setGlobalFilter(filtersSize, mRobotizeFilter.get()); + if (mSound == nullptr) + mSoloud->setGlobalFilter(filtersSize, mRobotizeFilter.get()); + else + mSound->sound.get()->setFilter(filtersSize, mRobotizeFilter.get()); filters.push_back({filterType, static_cast(mRobotizeFilter.get())}); break; case FreeverbFilter: if (!mFreeverbFilter) mFreeverbFilter = std::make_unique(); - mSoloud->setGlobalFilter(filtersSize, mFreeverbFilter.get()); + if (mSound == nullptr) + mSoloud->setGlobalFilter(filtersSize, mFreeverbFilter.get()); + else + mSound->sound.get()->setFilter(filtersSize, mFreeverbFilter.get()); filters.push_back({filterType, static_cast(mFreeverbFilter.get())}); break; default: @@ -199,8 +223,9 @@ PlayerErrors Filters::addGlobalFilter(FilterType filterType) } return noError; } + /// TODO remove all filters FilterType.none -bool Filters::removeGlobalFilter(FilterType filterType) +bool Filters::removeFilter(FilterType filterType) { int index = isFilterActive(filterType); if (index < 0) @@ -239,11 +264,17 @@ bool Filters::removeGlobalFilter(FilterType filterType) break; } - /// shift filters down by 1 fron [index] + /// shift filters down by 1 from [index] for (int i = index; i < filters.size() - 1; i++) { - mSoloud->setGlobalFilter(i + 1, 0); - mSoloud->setGlobalFilter(i, filters[i+1].filter); + if (mSound == nullptr) + mSoloud->setGlobalFilter(i + 1, 0); + else + mSound->sound.get()->setFilter(i + 1, 0); + if (mSound == nullptr) + mSoloud->setGlobalFilter(i, filters[i + 1].filter); + else + mSound->sound.get()->setFilter(i, filters[i + 1].filter); } /// remove the filter from the list filters.erase(filters.begin() + index); @@ -251,21 +282,45 @@ bool Filters::removeGlobalFilter(FilterType filterType) return true; } -void Filters::setFxParams(FilterType filterType, int attributeId, float value) +void Filters::setFilterParams(SoLoud::handle handle, FilterType filterType, int attributeId, float value) { int index = isFilterActive(filterType); if (index < 0) return; - mSoloud->setFilterParameter(0, index, attributeId, value); + mSoloud->setFilterParameter(handle, index, attributeId, value); } -float Filters::getFxParams(FilterType filterType, int attributeId) +float Filters::getFilterParams(SoLoud::handle handle, FilterType filterType, int attributeId) { int index = isFilterActive(filterType); if (index < 0) - return 0.0f; + return 9999.0f; - float ret = mSoloud->getFilterParameter(0, index, attributeId); + float ret = mSoloud->getFilterParameter(handle, index, attributeId); return ret; } + +void Filters::fadeFilterParameter(SoLoud::handle handle, FilterType filterType, int attributeId, float to, float time) +{ + int index = isFilterActive(filterType); + if (index < 0) + return; + + mSoloud->fadeFilterParameter(handle, index, attributeId, to, time); +} + +void Filters::oscillateFilterParameter( + SoLoud::handle handle, + FilterType filterType, + int attributeId, + float from, + float to, + float time) +{ + int index = isFilterActive(filterType); + if (index < 0) + return; + + mSoloud->oscillateFilterParameter(handle, index, attributeId, from, to, time); +} diff --git a/src/filters/filters.h b/src/filters/filters.h index 4183e5f..cf6b798 100644 --- a/src/filters/filters.h +++ b/src/filters/filters.h @@ -1,6 +1,8 @@ #ifndef FILTERS_H #define FILTERS_H +#include "../active_sound.h" + #include "soloud.h" #include "soloud_filter.h" #include "soloud_biquadresonantfilter.h" @@ -16,55 +18,66 @@ #include "soloud_robotizefilter.h" #include "soloud_freeverbfilter.h" -#include "../enums.h" - #include #include #include -typedef enum FilterType +struct FilterObject { - BiquadResonantFilter, - EqFilter, - EchoFilter, - LofiFilter, - FlangerFilter, - BassboostFilter, - WaveShaperFilter, - RobotizeFilter, - FreeverbFilter -} FilterType_t; - -struct FilterObject { FilterType type; SoLoud::Filter *filter; - bool operator==(FilterType const &i) { + bool operator==(FilterType const &i) + { return (i == type); } }; -class Filters { +/// Class to manage global filters. +class Filters +{ /// TODO(marco): Soloud.setGlobalFilter() /// Sets, or clears, the global filter. /// - /// Setting the global filter to NULL will clear the global filter. - /// The default maximum number of global filters active is 4, but this + /// Setting the global filter to NULL will clear the global filter. + /// The default maximum number of global filters active is 4, but this /// can be changed in a global constant in soloud.h (and rebuilding SoLoud). public: - Filters(SoLoud::Soloud *soloud); + Filters(SoLoud::Soloud *soloud, ActiveSound *sound); ~Filters(); int isFilterActive(FilterType filter); - PlayerErrors addGlobalFilter(FilterType filterType); - bool removeGlobalFilter(FilterType filterType); + + PlayerErrors addFilter(FilterType filterType); + + bool removeFilter(FilterType filterType); + std::vector getFilterParamNames(FilterType filterType); - void setFxParams(FilterType filterType, int attributeId, float value); - float getFxParams(FilterType filterType, int attributeId); + + /// If [handle]==0 the operation is done to global filters. + void setFilterParams(SoLoud::handle handle, FilterType filterType, int attributeId, float value); + + /// If [handle]==0 the operation is done to global filters. + float getFilterParams(SoLoud::handle handle, FilterType filterType, int attributeId); + + /// If [handle]==0 the operation is done to global filters. + void fadeFilterParameter(SoLoud::handle handle, FilterType filterType, int attributeId, float to, float time); + + /// If [handle]==0 the operation is done to global filters. + void oscillateFilterParameter( + SoLoud::handle handle, + FilterType filterType, + int attributeId, + float from, + float to, + float time); private: /// main SoLoud engine, the one used by player.cpp SoLoud::Soloud *mSoloud; + /// The sound to manage filters for. If null the filters are managed globally. + ActiveSound *mSound; + std::vector filters; std::unique_ptr mBiquadResonantFilter; diff --git a/src/player.cpp b/src/player.cpp index a35636c..2879b5d 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -15,7 +15,7 @@ #include #endif -Player::Player() : mInited(false), mFilters(&soloud) {} +Player::Player() : mInited(false), mFilters(&soloud, nullptr) {} Player::~Player() { @@ -126,12 +126,9 @@ PlayerErrors Player::loadFile( unsigned int newHash = (unsigned int)std::hash{}(completeFileName); /// check if the sound has been already loaded - auto const &s = std::find_if( - sounds.begin(), sounds.end(), - [&](std::shared_ptr const &f) - { return f->soundHash == newHash; }); + auto const s = findByHash(newHash); - if (s != sounds.end()) + if (s != 0) { *hash = newHash; return fileAlreadyLoaded; @@ -158,6 +155,8 @@ PlayerErrors Player::loadFile( if (result != SoLoud::SO_NO_ERROR) { sounds.pop_back(); + } else { + sounds.back().get()->filters = std::make_unique(&soloud, sounds.back().get()); } *hash = newHash; @@ -178,12 +177,9 @@ PlayerErrors Player::loadMem( unsigned int newHash = (unsigned int)std::hash{}(uniqueName); /// check if the sound has been already loaded - auto const &s = std::find_if( - sounds.begin(), sounds.end(), - [&](std::shared_ptr const &f) - { return f->soundHash == newHash; }); + auto const s = findByHash(newHash); - if (s != sounds.end()) + if (s != 0) { hash = newHash; return fileAlreadyLoaded; @@ -210,6 +206,8 @@ PlayerErrors Player::loadMem( if (result != SoLoud::SO_NO_ERROR) { sounds.pop_back(); + } else { + sounds.back().get()->filters = std::make_unique(&soloud, sounds.back().get()); } return (PlayerErrors)result; @@ -238,68 +236,59 @@ PlayerErrors Player::loadWaveform( sounds.back().get()->soundHash = hash; sounds.back().get()->sound = std::make_shared((SoLoud::Soloud::WAVEFORM)waveform, superWave, detune, scale); sounds.back().get()->soundType = TYPE_SYNTH; + sounds.back().get()->filters = std::make_unique(&soloud, sounds.back().get()); return noError; } void Player::setWaveformScale(unsigned int soundHash, float newScale) { - auto const &s = std::find_if(sounds.begin(), sounds.end(), - [&](std::shared_ptr const &f) - { return f->soundHash == soundHash; }); + auto const s = findByHash(soundHash); - if (s == sounds.end() || s->get()->soundType != TYPE_SYNTH) + if (s == 0 || s->soundType != TYPE_SYNTH) return; - static_cast(s->get()->sound.get())->setScale(newScale); + static_cast(s->sound.get())->setScale(newScale); } void Player::setWaveformDetune(unsigned int soundHash, float newDetune) { - auto const &s = std::find_if(sounds.begin(), sounds.end(), - [&](std::shared_ptr const &f) - { return f->soundHash == soundHash; }); + auto const s = findByHash(soundHash); - if (s == sounds.end() || s->get()->soundType != TYPE_SYNTH) + if (s == 0 || s->soundType != TYPE_SYNTH) return; - static_cast(s->get()->sound.get())->setDetune(newDetune); + static_cast(s->sound.get())->setDetune(newDetune); } void Player::setWaveform(unsigned int soundHash, int newWaveform) { - auto const &s = std::find_if(sounds.begin(), sounds.end(), - [&](std::shared_ptr const &f) - { return f->soundHash == soundHash; }); + auto const s = findByHash(soundHash); - if (s == sounds.end() || s->get()->soundType != TYPE_SYNTH) + if (s == 0 || s->soundType != TYPE_SYNTH) return; - static_cast(s->get()->sound.get())->setWaveform((SoLoud::Soloud::WAVEFORM)newWaveform); + static_cast(s->sound.get())->setWaveform((SoLoud::Soloud::WAVEFORM)newWaveform); } void Player::setWaveformFreq(unsigned int soundHash, float newFreq) { - auto const &s = std::find_if(sounds.begin(), sounds.end(), - [&](std::shared_ptr const &f) - { return f->soundHash == soundHash; }); + auto const s = findByHash(soundHash); - if (s == sounds.end() || s->get()->soundType != TYPE_SYNTH) + if (s == 0 || s->soundType != TYPE_SYNTH) return; - static_cast(s->get()->sound.get())->setFreq(newFreq); + static_cast(s->sound.get())->setFreq(newFreq); } void Player::setWaveformSuperwave(unsigned int soundHash, bool superwave) { - auto const &s = std::find_if(sounds.begin(), sounds.end(), - [&](std::shared_ptr const &f) - { return f->soundHash == soundHash; }); + auto const s = findByHash(soundHash); - if (s == sounds.end() || s->get()->soundType != TYPE_SYNTH) + if (s == 0 || s->soundType != TYPE_SYNTH) return; - static_cast(s->get()->sound.get())->setSuperWave(superwave); + static_cast(s->sound.get())->setSuperWave(superwave); } void Player::pauseSwitch(unsigned int handle) @@ -337,15 +326,10 @@ unsigned int Player::play( bool looping, double loopingStartAt) { - auto const &s = std::find_if( - sounds.begin(), sounds.end(), - [&](std::shared_ptr const &f) - { return f->soundHash == soundHash; }); + ActiveSound *sound = findByHash(soundHash); - if (s == sounds.end()) - return 0; - - ActiveSound *sound = s->get(); + if (sound == 0) + return soundHashNotFound; SoLoud::handle newHandle = soloud.play( *sound->sound.get(), volume, pan, paused, 0); @@ -391,14 +375,12 @@ void Player::removeHandle(unsigned int handle) void Player::disposeSound(unsigned int soundHash) { - auto const &s = std::find_if(sounds.begin(), sounds.end(), - [&](std::shared_ptr const &f) - { return f->soundHash == soundHash; }); + ActiveSound *sound = findByHash(soundHash); - if (s == sounds.end()) + if (sound == 0) return; - s->get()->sound.get()->stop(); + sound->sound.get()->stop(); // remove the sound from the list sounds.erase(std::remove_if(sounds.begin(), sounds.end(), [soundHash](std::shared_ptr &f) @@ -442,6 +424,7 @@ PlayerErrors Player::textToSpeech(const std::string &textToSpeech, unsigned int if (result == SoLoud::SO_NO_ERROR) { handle = soloud.play(speech); + sounds.back().get()->filters = std::make_unique(&soloud, sounds.back().get()); sounds.back().get()->handle.push_back(handle); } else @@ -474,16 +457,15 @@ float *Player::getWave() // The length in seconds double Player::getLength(unsigned int soundHash) { - auto const &s = std::find_if(sounds.begin(), sounds.end(), - [&](std::shared_ptr const &f) - { return f->soundHash == soundHash; }); - if (s == sounds.end() || s->get()->soundType == TYPE_SYNTH) + auto const &s = findByHash(soundHash); + + if (s == 0 || s->soundType == TYPE_SYNTH) return 0.0; - if (s->get()->soundType == TYPE_WAV) - return static_cast(s->get()->sound.get())->getLength(); + if (s->soundType == TYPE_WAV) + return static_cast(s->sound.get())->getLength(); - // if (s->get()->soundType == TYPE_WAVSTREAM) - return static_cast(s->get()->sound.get())->getLength(); + // if (s->soundType == TYPE_WAVSTREAM) + return static_cast(s->sound.get())->getLength(); } // time in seconds @@ -561,19 +543,18 @@ unsigned int Player::getActiveVoiceCount() int Player::countAudioSource(unsigned int soundHash) { - auto const &s = std::find_if(sounds.begin(), sounds.end(), - [&](std::shared_ptr const &f) - { return f->soundHash == soundHash; }); - if (s == sounds.end() || s->get()->soundType == TYPE_SYNTH) + auto const &s = findByHash(soundHash); + + if (s == 0 || s->soundType == TYPE_SYNTH) return 0; - if (s->get()->soundType == TYPE_WAV) + if (s->soundType == TYPE_WAV) { - SoLoud::AudioSource *as = static_cast(s->get()->sound.get()); + SoLoud::AudioSource *as = static_cast(s->sound.get()); return soloud.countAudioSource(*as); } - // if (s->get()->soundType == TYPE_WAVSTREAM) - SoLoud::AudioSource *as = static_cast(s->get()->sound.get()); + // if (s->soundType == TYPE_WAVSTREAM) + SoLoud::AudioSource *as = static_cast(s->sound.get()); return soloud.countAudioSource(*as); } @@ -623,6 +604,17 @@ ActiveSound *Player::findByHandle(SoLoud::handle handle) return nullptr; } +ActiveSound *Player::findByHash(unsigned int soundHash) +{ + auto const &s = std::find_if(sounds.begin(), sounds.end(), + [&](std::shared_ptr const &f) + { return f->soundHash == soundHash; }); + if (s == sounds.end()) + return 0; + + return s->get(); +} + void Player::debug() { int n = 0; @@ -642,7 +634,8 @@ void Player::debug() ///////////////////////////////////////// unsigned int Player::createVoiceGroup() { - return soloud.createVoiceGroup(); + auto ret = soloud.createVoiceGroup(); + return ret; } void Player::destroyVoiceGroup(SoLoud::handle handle) { @@ -738,13 +731,10 @@ unsigned int Player::play3d( bool looping, double loopingStartAt) { - auto const &s = std::find_if(sounds.begin(), sounds.end(), - [&](std::shared_ptr const &f) - { return f->soundHash == soundHash; }); - if (s == sounds.end()) - return 0; + ActiveSound *sound = findByHash(soundHash); + if (sound == 0) + return soundHashNotFound; - ActiveSound *sound = s->get(); SoLoud::handle newHandle = soloud.play3d( *sound->sound.get(), posX, posY, posZ, diff --git a/src/player.h b/src/player.h index 16d752a..3a01576 100644 --- a/src/player.h +++ b/src/player.h @@ -4,9 +4,10 @@ #define PLAYER_H #include "enums.h" +#include "filters/filters.h" +#include "active_sound.h" #include "soloud.h" #include "soloud_speech.h" -#include "filters/filters.h" #include #include @@ -16,27 +17,6 @@ #include #include -typedef enum SoundType -{ - // using Soloud::wav - TYPE_WAV, - // using Soloud::wavStream - TYPE_WAVSTREAM, - // this sound is a waveform - TYPE_SYNTH -} SoundType_t; - -/// The default number of concurrent voices - maximum number of "streams" - is 16, -/// but this can be adjusted at runtime -struct ActiveSound -{ - std::shared_ptr sound; - SoundType soundType; - std::vector handle; - // unique identifier of this sound based on the file name - unsigned int soundHash; - std::string completeFileName; -}; class Player { @@ -359,6 +339,11 @@ class Player /// @return If not found, return nullptr. ActiveSound *findByHandle(SoLoud::handle handle); + /// @brief Find a sound by its handle. + /// @param handle the handle to search. + /// @return If not found, return nullptr. + ActiveSound *findByHash(unsigned int hash); + void debug(); ///////////////////////////////////////// @@ -549,7 +534,7 @@ class Player /// speech object SoLoud::Speech speech; - /// Filters + /// Global filters Filters mFilters; private: diff --git a/web/libflutter_soloud_plugin.js b/web/libflutter_soloud_plugin.js index 6a76334..8a2fee3 100644 --- a/web/libflutter_soloud_plugin.js +++ b/web/libflutter_soloud_plugin.js @@ -1 +1 @@ -var Module=typeof Module!="undefined"?Module:{};var ENVIRONMENT_IS_WEB=typeof window=="object";var ENVIRONMENT_IS_WORKER=typeof importScripts=="function";var ENVIRONMENT_IS_NODE=typeof process=="object"&&typeof process.versions=="object"&&typeof process.versions.node=="string";if(ENVIRONMENT_IS_NODE){}var moduleOverrides=Object.assign({},Module);var arguments_=[];var thisProgram="./this.program";var quit_=(status,toThrow)=>{throw toThrow};var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}return scriptDirectory+path}var read_,readAsync,readBinary;if(ENVIRONMENT_IS_NODE){var fs=require("fs");var nodePath=require("path");scriptDirectory=__dirname+"/";read_=(filename,binary)=>{filename=isFileURI(filename)?new URL(filename):nodePath.normalize(filename);return fs.readFileSync(filename,binary?undefined:"utf8")};readBinary=filename=>{var ret=read_(filename,true);if(!ret.buffer){ret=new Uint8Array(ret)}return ret};readAsync=(filename,onload,onerror,binary=true)=>{filename=isFileURI(filename)?new URL(filename):nodePath.normalize(filename);fs.readFile(filename,binary?undefined:"utf8",(err,data)=>{if(err)onerror(err);else onload(binary?data.buffer:data)})};if(!Module["thisProgram"]&&process.argv.length>1){thisProgram=process.argv[1].replace(/\\/g,"/")}arguments_=process.argv.slice(2);if(typeof module!="undefined"){module["exports"]=Module}process.on("uncaughtException",ex=>{if(ex!=="unwind"&&!(ex instanceof ExitStatus)&&!(ex.context instanceof ExitStatus)){throw ex}});quit_=(status,toThrow)=>{process.exitCode=status;throw toThrow}}else if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){if(ENVIRONMENT_IS_WORKER){scriptDirectory=self.location.href}else if(typeof document!="undefined"&&document.currentScript){scriptDirectory=document.currentScript.src}if(scriptDirectory.startsWith("blob:")){scriptDirectory=""}else{scriptDirectory=scriptDirectory.substr(0,scriptDirectory.replace(/[?#].*/,"").lastIndexOf("/")+1)}{read_=url=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.send(null);return xhr.responseText};if(ENVIRONMENT_IS_WORKER){readBinary=url=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)}}readAsync=(url,onload,onerror)=>{if(isFileURI(url)){var xhr=new XMLHttpRequest;xhr.open("GET",url,true);xhr.responseType="arraybuffer";xhr.onload=()=>{if(xhr.status==200||xhr.status==0&&xhr.response){onload(xhr.response);return}onerror()};xhr.onerror=onerror;xhr.send(null);return}fetch(url,{credentials:"same-origin"}).then(response=>{if(response.ok){return response.arrayBuffer()}return Promise.reject(new Error(response.status+" : "+response.url))}).then(onload,onerror)}}}else{}var out=Module["print"]||console.log.bind(console);var err=Module["printErr"]||console.error.bind(console);Object.assign(Module,moduleOverrides);moduleOverrides=null;if(Module["arguments"])arguments_=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"];if(Module["quit"])quit_=Module["quit"];var wasmBinary;if(Module["wasmBinary"])wasmBinary=Module["wasmBinary"];function getSafeHeapType(bytes,isFloat){switch(bytes){case 1:return"i8";case 2:return"i16";case 4:return isFloat?"float":"i32";case 8:return isFloat?"double":"i64";default:abort(`getSafeHeapType() invalid bytes=${bytes}`)}}function SAFE_HEAP_STORE(dest,value,bytes,isFloat){if(dest<=0)abort(`segmentation fault storing ${bytes} bytes to address ${dest}`);if(dest%bytes!==0)abort(`alignment error storing to address ${dest}, which was expected to be aligned to a multiple of ${bytes}`);if(runtimeInitialized){var brk=_sbrk(0);if(dest+bytes>brk)abort(`segmentation fault, exceeded the top of the available dynamic heap when storing ${bytes} bytes to address ${dest}. DYNAMICTOP=${brk}`);if(brk<_emscripten_stack_get_base())abort(`brk >= _emscripten_stack_get_base() (brk=${brk}, _emscripten_stack_get_base()=${_emscripten_stack_get_base()})`);if(brk>wasmMemory.buffer.byteLength)abort(`brk <= wasmMemory.buffer.byteLength (brk=${brk}, wasmMemory.buffer.byteLength=${wasmMemory.buffer.byteLength})`)}setValue_safe(dest,value,getSafeHeapType(bytes,isFloat));return value}function SAFE_HEAP_STORE_D(dest,value,bytes){return SAFE_HEAP_STORE(dest,value,bytes,true)}function SAFE_HEAP_LOAD(dest,bytes,unsigned,isFloat){if(dest<=0)abort(`segmentation fault loading ${bytes} bytes from address ${dest}`);if(dest%bytes!==0)abort(`alignment error loading from address ${dest}, which was expected to be aligned to a multiple of ${bytes}`);if(runtimeInitialized){var brk=_sbrk(0);if(dest+bytes>brk)abort(`segmentation fault, exceeded the top of the available dynamic heap when loading ${bytes} bytes from address ${dest}. DYNAMICTOP=${brk}`);if(brk<_emscripten_stack_get_base())abort(`brk >= _emscripten_stack_get_base() (brk=${brk}, _emscripten_stack_get_base()=${_emscripten_stack_get_base()})`);if(brk>wasmMemory.buffer.byteLength)abort(`brk <= wasmMemory.buffer.byteLength (brk=${brk}, wasmMemory.buffer.byteLength=${wasmMemory.buffer.byteLength})`)}var type=getSafeHeapType(bytes,isFloat);var ret=getValue_safe(dest,type);if(unsigned)ret=unSign(ret,parseInt(type.substr(1),10));return ret}function SAFE_HEAP_LOAD_D(dest,bytes,unsigned){return SAFE_HEAP_LOAD(dest,bytes,unsigned,true)}function segfault(){abort("segmentation fault")}function alignfault(){abort("alignment fault")}var wasmMemory;var ABORT=false;var EXITSTATUS;var HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;function updateMemoryViews(){var b=wasmMemory.buffer;Module["HEAP8"]=HEAP8=new Int8Array(b);Module["HEAP16"]=HEAP16=new Int16Array(b);Module["HEAPU8"]=HEAPU8=new Uint8Array(b);Module["HEAPU16"]=HEAPU16=new Uint16Array(b);Module["HEAP32"]=HEAP32=new Int32Array(b);Module["HEAPU32"]=HEAPU32=new Uint32Array(b);Module["HEAPF32"]=HEAPF32=new Float32Array(b);Module["HEAPF64"]=HEAPF64=new Float64Array(b)}var __ATPRERUN__=[];var __ATINIT__=[];var __ATPOSTRUN__=[];var runtimeInitialized=false;function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(__ATPRERUN__)}function initRuntime(){runtimeInitialized=true;if(!Module["noFSInit"]&&!FS.init.initialized)FS.init();FS.ignorePermissions=false;TTY.init();callRuntimeCallbacks(__ATINIT__)}function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(__ATPOSTRUN__)}function addOnPreRun(cb){__ATPRERUN__.unshift(cb)}function addOnInit(cb){__ATINIT__.unshift(cb)}function addOnPostRun(cb){__ATPOSTRUN__.unshift(cb)}var runDependencies=0;var runDependencyWatcher=null;var dependenciesFulfilled=null;function getUniqueRunDependency(id){return id}function addRunDependency(id){runDependencies++;Module["monitorRunDependencies"]?.(runDependencies)}function removeRunDependency(id){runDependencies--;Module["monitorRunDependencies"]?.(runDependencies);if(runDependencies==0){if(runDependencyWatcher!==null){clearInterval(runDependencyWatcher);runDependencyWatcher=null}if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback()}}}function abort(what){Module["onAbort"]?.(what);what="Aborted("+what+")";err(what);ABORT=true;EXITSTATUS=1;what+=". Build with -sASSERTIONS for more info.";var e=new WebAssembly.RuntimeError(what);throw e}var dataURIPrefix="data:application/octet-stream;base64,";var isDataURI=filename=>filename.startsWith(dataURIPrefix);var isFileURI=filename=>filename.startsWith("file://");function findWasmBinary(){var f="libflutter_soloud_plugin.wasm";if(!isDataURI(f)){return locateFile(f)}return f}var wasmBinaryFile;function getBinarySync(file){if(file==wasmBinaryFile&&wasmBinary){return new Uint8Array(wasmBinary)}if(readBinary){return readBinary(file)}throw"both async and sync fetching of the wasm failed"}function getBinaryPromise(binaryFile){if(!wasmBinary){return new Promise((resolve,reject)=>{readAsync(binaryFile,response=>resolve(new Uint8Array(response)),error=>{try{resolve(getBinarySync(binaryFile))}catch(e){reject(e)}})})}return Promise.resolve().then(()=>getBinarySync(binaryFile))}function instantiateArrayBuffer(binaryFile,imports,receiver){return getBinaryPromise(binaryFile).then(binary=>WebAssembly.instantiate(binary,imports)).then(receiver,reason=>{err(`failed to asynchronously prepare wasm: ${reason}`);abort(reason)})}function instantiateAsync(binary,binaryFile,imports,callback){if(!binary&&typeof WebAssembly.instantiateStreaming=="function"&&!isDataURI(binaryFile)&&!isFileURI(binaryFile)&&!ENVIRONMENT_IS_NODE&&typeof fetch=="function"){return fetch(binaryFile,{credentials:"same-origin"}).then(response=>{var result=WebAssembly.instantiateStreaming(response,imports);return result.then(callback,function(reason){err(`wasm streaming compile failed: ${reason}`);err("falling back to ArrayBuffer instantiation");return instantiateArrayBuffer(binaryFile,imports,callback)})})}return instantiateArrayBuffer(binaryFile,imports,callback)}function getWasmImports(){return{a:wasmImports}}function createWasm(){var info=getWasmImports();function receiveInstance(instance,module){wasmExports=instance.exports;wasmMemory=wasmExports["q"];updateMemoryViews();addOnInit(wasmExports["r"]);removeRunDependency("wasm-instantiate");return wasmExports}addRunDependency("wasm-instantiate");function receiveInstantiationResult(result){receiveInstance(result["instance"])}if(Module["instantiateWasm"]){try{return Module["instantiateWasm"](info,receiveInstance)}catch(e){err(`Module.instantiateWasm callback failed with error: ${e}`);return false}}if(!wasmBinaryFile)wasmBinaryFile=findWasmBinary();instantiateAsync(wasmBinary,wasmBinaryFile,info,receiveInstantiationResult);return{}}var tempDouble;var tempI64;var ASM_CONSTS={67376:($0,$1,$2,$3,$4)=>{if(typeof window==="undefined"||(window.AudioContext||window.webkitAudioContext)===undefined){return 0}if(typeof window.miniaudio==="undefined"){window.miniaudio={referenceCount:0};window.miniaudio.device_type={};window.miniaudio.device_type.playback=$0;window.miniaudio.device_type.capture=$1;window.miniaudio.device_type.duplex=$2;window.miniaudio.device_state={};window.miniaudio.device_state.stopped=$3;window.miniaudio.device_state.started=$4;let miniaudio=window.miniaudio;miniaudio.devices=[];miniaudio.track_device=function(device){for(var iDevice=0;iDevice0){if(miniaudio.devices[miniaudio.devices.length-1]==null){miniaudio.devices.pop()}else{break}}};miniaudio.untrack_device=function(device){for(var iDevice=0;iDevice{_ma_device__on_notification_unlocked(device.pDevice)},error=>{console.error("Failed to resume audiocontext",error)})}}miniaudio.unlock_event_types.map(function(event_type){document.removeEventListener(event_type,miniaudio.unlock,true)})};miniaudio.unlock_event_types.map(function(event_type){document.addEventListener(event_type,miniaudio.unlock,true)})}window.miniaudio.referenceCount+=1;return 1},69554:()=>{if(typeof window.miniaudio!=="undefined"){miniaudio.unlock_event_types.map(function(event_type){document.removeEventListener(event_type,miniaudio.unlock,true)});window.miniaudio.referenceCount-=1;if(window.miniaudio.referenceCount===0){delete window.miniaudio}}},69844:()=>navigator.mediaDevices!==undefined&&navigator.mediaDevices.getUserMedia!==undefined,69948:()=>{try{var temp=new(window.AudioContext||window.webkitAudioContext);var sampleRate=temp.sampleRate;temp.close();return sampleRate}catch(e){return 0}},70119:($0,$1,$2,$3,$4,$5)=>{var deviceType=$0;var channels=$1;var sampleRate=$2;var bufferSize=$3;var pIntermediaryBuffer=$4;var pDevice=$5;if(typeof window.miniaudio==="undefined"){return-1}var device={};var audioContextOptions={};if(deviceType==window.miniaudio.device_type.playback&&sampleRate!=0){audioContextOptions.sampleRate=sampleRate}device.webaudio=new(window.AudioContext||window.webkitAudioContext)(audioContextOptions);device.webaudio.suspend();device.state=window.miniaudio.device_state.stopped;var channelCountIn=0;var channelCountOut=channels;if(deviceType!=window.miniaudio.device_type.playback){channelCountIn=channels}device.scriptNode=device.webaudio.createScriptProcessor(bufferSize,channelCountIn,channelCountOut);device.scriptNode.onaudioprocess=function(e){if(device.intermediaryBufferView==null||device.intermediaryBufferView.length==0){device.intermediaryBufferView=new Float32Array(HEAPF32.buffer,pIntermediaryBuffer,bufferSize*channels)}if(deviceType==window.miniaudio.device_type.capture||deviceType==window.miniaudio.device_type.duplex){for(var iChannel=0;iChannelwindow.miniaudio.get_device_by_index($0).webaudio.sampleRate,73069:$0=>{var device=window.miniaudio.get_device_by_index($0);if(device.scriptNode!==undefined){device.scriptNode.onaudioprocess=function(e){};device.scriptNode.disconnect();device.scriptNode=undefined}if(device.streamNode!==undefined){device.streamNode.disconnect();device.streamNode=undefined}device.webaudio.close();device.webaudio=undefined;device.pDevice=undefined},73469:$0=>{window.miniaudio.untrack_device_by_index($0)},73519:$0=>{var device=window.miniaudio.get_device_by_index($0);device.webaudio.resume();device.state=window.miniaudio.device_state.started},73658:$0=>{var device=window.miniaudio.get_device_by_index($0);device.webaudio.suspend();device.state=window.miniaudio.device_state.stopped},73798:()=>{if(!Module.wasmWorker){var workerUri="assets/packages/flutter_soloud/web/worker.dart.js";console.log("EM_ASM creating web worker!");Module.wasmWorker=new Worker(workerUri)}else{console.log("EM_ASM web worker already created!")}},74046:($0,$1)=>{if(Module.wasmWorker){console.log("EM_ASM posting message "+UTF8ToString($0)+" with value "+$1);Module.wasmWorker.postMessage(JSON.stringify({message:UTF8ToString($0),value:$1}))}else{console.error("Worker not found.")}}};function ExitStatus(status){this.name="ExitStatus";this.message=`Program terminated with exit(${status})`;this.status=status}Module["ExitStatus"]=ExitStatus;var callRuntimeCallbacks=callbacks=>{while(callbacks.length>0){callbacks.shift()(Module)}};Module["callRuntimeCallbacks"]=callRuntimeCallbacks;function getValue(ptr,type="i8"){if(type.endsWith("*"))type="*";switch(type){case"i1":return SAFE_HEAP_LOAD(ptr,1,0);case"i8":return SAFE_HEAP_LOAD(ptr,1,0);case"i16":return SAFE_HEAP_LOAD((ptr>>1)*2,2,0);case"i32":return SAFE_HEAP_LOAD((ptr>>2)*4,4,0);case"i64":abort("to do getValue(i64) use WASM_BIGINT");case"float":return SAFE_HEAP_LOAD_D((ptr>>2)*4,4,0);case"double":return SAFE_HEAP_LOAD_D((ptr>>3)*8,8,0);case"*":return SAFE_HEAP_LOAD((ptr>>2)*4,4,1);default:abort(`invalid type for getValue: ${type}`)}}Module["getValue"]=getValue;function getValue_safe(ptr,type="i8"){if(type.endsWith("*"))type="*";switch(type){case"i1":return HEAP8[ptr];case"i8":return HEAP8[ptr];case"i16":return HEAP16[ptr>>1];case"i32":return HEAP32[ptr>>2];case"i64":abort("to do getValue(i64) use WASM_BIGINT");case"float":return HEAPF32[ptr>>2];case"double":return HEAPF64[ptr>>3];case"*":return HEAPU32[ptr>>2];default:abort(`invalid type for getValue: ${type}`)}}Module["getValue_safe"]=getValue_safe;var noExitRuntime=Module["noExitRuntime"]||true;Module["noExitRuntime"]=noExitRuntime;function setValue(ptr,value,type="i8"){if(type.endsWith("*"))type="*";switch(type){case"i1":SAFE_HEAP_STORE(ptr,value,1);break;case"i8":SAFE_HEAP_STORE(ptr,value,1);break;case"i16":SAFE_HEAP_STORE((ptr>>1)*2,value,2);break;case"i32":SAFE_HEAP_STORE((ptr>>2)*4,value,4);break;case"i64":abort("to do setValue(i64) use WASM_BIGINT");case"float":SAFE_HEAP_STORE_D((ptr>>2)*4,value,4);break;case"double":SAFE_HEAP_STORE_D((ptr>>3)*8,value,8);break;case"*":SAFE_HEAP_STORE((ptr>>2)*4,value,4);break;default:abort(`invalid type for setValue: ${type}`)}}Module["setValue"]=setValue;function setValue_safe(ptr,value,type="i8"){if(type.endsWith("*"))type="*";switch(type){case"i1":HEAP8[ptr]=value;break;case"i8":HEAP8[ptr]=value;break;case"i16":HEAP16[ptr>>1]=value;break;case"i32":HEAP32[ptr>>2]=value;break;case"i64":abort("to do setValue(i64) use WASM_BIGINT");case"float":HEAPF32[ptr>>2]=value;break;case"double":HEAPF64[ptr>>3]=value;break;case"*":HEAPU32[ptr>>2]=value;break;default:abort(`invalid type for setValue: ${type}`)}}Module["setValue_safe"]=setValue_safe;var stackRestore=val=>__emscripten_stack_restore(val);Module["stackRestore"]=stackRestore;var stackSave=()=>_emscripten_stack_get_current();Module["stackSave"]=stackSave;var unSign=(value,bits)=>{if(value>=0){return value}return bits<=32?2*Math.abs(1<{var endIdx=idx+maxBytesToRead;var endPtr=idx;while(heapOrArray[endPtr]&&!(endPtr>=endIdx))++endPtr;if(endPtr-idx>16&&heapOrArray.buffer&&UTF8Decoder){return UTF8Decoder.decode(heapOrArray.subarray(idx,endPtr))}var str="";while(idx>10,56320|ch&1023)}}return str};Module["UTF8ArrayToString"]=UTF8ArrayToString;var UTF8ToString=(ptr,maxBytesToRead)=>ptr?UTF8ArrayToString(HEAPU8,ptr,maxBytesToRead):"";Module["UTF8ToString"]=UTF8ToString;var ___assert_fail=(condition,filename,line,func)=>{abort(`Assertion failed: ${UTF8ToString(condition)}, at: `+[filename?UTF8ToString(filename):"unknown filename",line,func?UTF8ToString(func):"unknown function"])};Module["___assert_fail"]=___assert_fail;class ExceptionInfo{constructor(excPtr){this.excPtr=excPtr;this.ptr=excPtr-24}set_type(type){SAFE_HEAP_STORE((this.ptr+4>>2)*4,type,4)}get_type(){return SAFE_HEAP_LOAD((this.ptr+4>>2)*4,4,1)}set_destructor(destructor){SAFE_HEAP_STORE((this.ptr+8>>2)*4,destructor,4)}get_destructor(){return SAFE_HEAP_LOAD((this.ptr+8>>2)*4,4,1)}set_caught(caught){caught=caught?1:0;SAFE_HEAP_STORE(this.ptr+12,caught,1)}get_caught(){return SAFE_HEAP_LOAD(this.ptr+12,1,0)!=0}set_rethrown(rethrown){rethrown=rethrown?1:0;SAFE_HEAP_STORE(this.ptr+13,rethrown,1)}get_rethrown(){return SAFE_HEAP_LOAD(this.ptr+13,1,0)!=0}init(type,destructor){this.set_adjusted_ptr(0);this.set_type(type);this.set_destructor(destructor)}set_adjusted_ptr(adjustedPtr){SAFE_HEAP_STORE((this.ptr+16>>2)*4,adjustedPtr,4)}get_adjusted_ptr(){return SAFE_HEAP_LOAD((this.ptr+16>>2)*4,4,1)}get_exception_ptr(){var isPointer=___cxa_is_pointer_type(this.get_type());if(isPointer){return SAFE_HEAP_LOAD((this.excPtr>>2)*4,4,1)}var adjusted=this.get_adjusted_ptr();if(adjusted!==0)return adjusted;return this.excPtr}}Module["ExceptionInfo"]=ExceptionInfo;var exceptionLast=0;Module["exceptionLast"]=exceptionLast;var uncaughtExceptionCount=0;Module["uncaughtExceptionCount"]=uncaughtExceptionCount;var ___cxa_throw=(ptr,type,destructor)=>{var info=new ExceptionInfo(ptr);info.init(type,destructor);exceptionLast=ptr;uncaughtExceptionCount++;throw exceptionLast};Module["___cxa_throw"]=___cxa_throw;function syscallGetVarargI(){var ret=SAFE_HEAP_LOAD((+SYSCALLS.varargs>>2)*4,4,0);SYSCALLS.varargs+=4;return ret}Module["syscallGetVarargI"]=syscallGetVarargI;var syscallGetVarargP=syscallGetVarargI;Module["syscallGetVarargP"]=syscallGetVarargP;var PATH={isAbs:path=>path.charAt(0)==="/",splitPath:filename=>{var splitPathRe=/^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/;return splitPathRe.exec(filename).slice(1)},normalizeArray:(parts,allowAboveRoot)=>{var up=0;for(var i=parts.length-1;i>=0;i--){var last=parts[i];if(last==="."){parts.splice(i,1)}else if(last===".."){parts.splice(i,1);up++}else if(up){parts.splice(i,1);up--}}if(allowAboveRoot){for(;up;up--){parts.unshift("..")}}return parts},normalize:path=>{var isAbsolute=PATH.isAbs(path),trailingSlash=path.substr(-1)==="/";path=PATH.normalizeArray(path.split("/").filter(p=>!!p),!isAbsolute).join("/");if(!path&&!isAbsolute){path="."}if(path&&trailingSlash){path+="/"}return(isAbsolute?"/":"")+path},dirname:path=>{var result=PATH.splitPath(path),root=result[0],dir=result[1];if(!root&&!dir){return"."}if(dir){dir=dir.substr(0,dir.length-1)}return root+dir},basename:path=>{if(path==="/")return"/";path=PATH.normalize(path);path=path.replace(/\/$/,"");var lastSlash=path.lastIndexOf("/");if(lastSlash===-1)return path;return path.substr(lastSlash+1)},join:(...paths)=>PATH.normalize(paths.join("/")),join2:(l,r)=>PATH.normalize(l+"/"+r)};Module["PATH"]=PATH;var initRandomFill=()=>{if(typeof crypto=="object"&&typeof crypto["getRandomValues"]=="function"){return view=>crypto.getRandomValues(view)}else if(ENVIRONMENT_IS_NODE){try{var crypto_module=require("crypto");var randomFillSync=crypto_module["randomFillSync"];if(randomFillSync){return view=>crypto_module["randomFillSync"](view)}var randomBytes=crypto_module["randomBytes"];return view=>(view.set(randomBytes(view.byteLength)),view)}catch(e){}}abort("initRandomDevice")};Module["initRandomFill"]=initRandomFill;var randomFill=view=>(randomFill=initRandomFill())(view);Module["randomFill"]=randomFill;var PATH_FS={resolve:(...args)=>{var resolvedPath="",resolvedAbsolute=false;for(var i=args.length-1;i>=-1&&!resolvedAbsolute;i--){var path=i>=0?args[i]:FS.cwd();if(typeof path!="string"){throw new TypeError("Arguments to path.resolve must be strings")}else if(!path){return""}resolvedPath=path+"/"+resolvedPath;resolvedAbsolute=PATH.isAbs(path)}resolvedPath=PATH.normalizeArray(resolvedPath.split("/").filter(p=>!!p),!resolvedAbsolute).join("/");return(resolvedAbsolute?"/":"")+resolvedPath||"."},relative:(from,to)=>{from=PATH_FS.resolve(from).substr(1);to=PATH_FS.resolve(to).substr(1);function trim(arr){var start=0;for(;start=0;end--){if(arr[end]!=="")break}if(start>end)return[];return arr.slice(start,end-start+1)}var fromParts=trim(from.split("/"));var toParts=trim(to.split("/"));var length=Math.min(fromParts.length,toParts.length);var samePartsLength=length;for(var i=0;i{var len=0;for(var i=0;i=55296&&c<=57343){len+=4;++i}else{len+=3}}return len};Module["lengthBytesUTF8"]=lengthBytesUTF8;var stringToUTF8Array=(str,heap,outIdx,maxBytesToWrite)=>{if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i=55296&&u<=57343){var u1=str.charCodeAt(++i);u=65536+((u&1023)<<10)|u1&1023}if(u<=127){if(outIdx>=endIdx)break;heap[outIdx++]=u}else if(u<=2047){if(outIdx+1>=endIdx)break;heap[outIdx++]=192|u>>6;heap[outIdx++]=128|u&63}else if(u<=65535){if(outIdx+2>=endIdx)break;heap[outIdx++]=224|u>>12;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}else{if(outIdx+3>=endIdx)break;heap[outIdx++]=240|u>>18;heap[outIdx++]=128|u>>12&63;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}}heap[outIdx]=0;return outIdx-startIdx};Module["stringToUTF8Array"]=stringToUTF8Array;function intArrayFromString(stringy,dontAddNull,length){var len=length>0?length:lengthBytesUTF8(stringy)+1;var u8array=new Array(len);var numBytesWritten=stringToUTF8Array(stringy,u8array,0,u8array.length);if(dontAddNull)u8array.length=numBytesWritten;return u8array}Module["intArrayFromString"]=intArrayFromString;var FS_stdin_getChar=()=>{if(!FS_stdin_getChar_buffer.length){var result=null;if(ENVIRONMENT_IS_NODE){var BUFSIZE=256;var buf=Buffer.alloc(BUFSIZE);var bytesRead=0;var fd=process.stdin.fd;try{bytesRead=fs.readSync(fd,buf,0,BUFSIZE)}catch(e){if(e.toString().includes("EOF"))bytesRead=0;else throw e}if(bytesRead>0){result=buf.slice(0,bytesRead).toString("utf-8")}}else if(typeof window!="undefined"&&typeof window.prompt=="function"){result=window.prompt("Input: ");if(result!==null){result+="\n"}}else{}if(!result){return null}FS_stdin_getChar_buffer=intArrayFromString(result,true)}return FS_stdin_getChar_buffer.shift()};Module["FS_stdin_getChar"]=FS_stdin_getChar;var TTY={ttys:[],init(){},shutdown(){},register(dev,ops){TTY.ttys[dev]={input:[],output:[],ops:ops};FS.registerDevice(dev,TTY.stream_ops)},stream_ops:{open(stream){var tty=TTY.ttys[stream.node.rdev];if(!tty){throw new FS.ErrnoError(43)}stream.tty=tty;stream.seekable=false},close(stream){stream.tty.ops.fsync(stream.tty)},fsync(stream){stream.tty.ops.fsync(stream.tty)},read(stream,buffer,offset,length,pos){if(!stream.tty||!stream.tty.ops.get_char){throw new FS.ErrnoError(60)}var bytesRead=0;for(var i=0;i0){out(UTF8ArrayToString(tty.output,0));tty.output=[]}},ioctl_tcgets(tty){return{c_iflag:25856,c_oflag:5,c_cflag:191,c_lflag:35387,c_cc:[3,28,127,21,4,0,1,0,17,19,26,0,18,15,23,22,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}},ioctl_tcsets(tty,optional_actions,data){return 0},ioctl_tiocgwinsz(tty){return[24,80]}},default_tty1_ops:{put_char(tty,val){if(val===null||val===10){err(UTF8ArrayToString(tty.output,0));tty.output=[]}else{if(val!=0)tty.output.push(val)}},fsync(tty){if(tty.output&&tty.output.length>0){err(UTF8ArrayToString(tty.output,0));tty.output=[]}}}};Module["TTY"]=TTY;var zeroMemory=(address,size)=>{HEAPU8.fill(0,address,address+size);return address};Module["zeroMemory"]=zeroMemory;var alignMemory=(size,alignment)=>Math.ceil(size/alignment)*alignment;Module["alignMemory"]=alignMemory;var mmapAlloc=size=>{abort()};Module["mmapAlloc"]=mmapAlloc;var MEMFS={ops_table:null,mount(mount){return MEMFS.createNode(null,"/",16384|511,0)},createNode(parent,name,mode,dev){if(FS.isBlkdev(mode)||FS.isFIFO(mode)){throw new FS.ErrnoError(63)}MEMFS.ops_table||={dir:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr,lookup:MEMFS.node_ops.lookup,mknod:MEMFS.node_ops.mknod,rename:MEMFS.node_ops.rename,unlink:MEMFS.node_ops.unlink,rmdir:MEMFS.node_ops.rmdir,readdir:MEMFS.node_ops.readdir,symlink:MEMFS.node_ops.symlink},stream:{llseek:MEMFS.stream_ops.llseek}},file:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr},stream:{llseek:MEMFS.stream_ops.llseek,read:MEMFS.stream_ops.read,write:MEMFS.stream_ops.write,allocate:MEMFS.stream_ops.allocate,mmap:MEMFS.stream_ops.mmap,msync:MEMFS.stream_ops.msync}},link:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr,readlink:MEMFS.node_ops.readlink},stream:{}},chrdev:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr},stream:FS.chrdev_stream_ops}};var node=FS.createNode(parent,name,mode,dev);if(FS.isDir(node.mode)){node.node_ops=MEMFS.ops_table.dir.node;node.stream_ops=MEMFS.ops_table.dir.stream;node.contents={}}else if(FS.isFile(node.mode)){node.node_ops=MEMFS.ops_table.file.node;node.stream_ops=MEMFS.ops_table.file.stream;node.usedBytes=0;node.contents=null}else if(FS.isLink(node.mode)){node.node_ops=MEMFS.ops_table.link.node;node.stream_ops=MEMFS.ops_table.link.stream}else if(FS.isChrdev(node.mode)){node.node_ops=MEMFS.ops_table.chrdev.node;node.stream_ops=MEMFS.ops_table.chrdev.stream}node.timestamp=Date.now();if(parent){parent.contents[name]=node;parent.timestamp=node.timestamp}return node},getFileDataAsTypedArray(node){if(!node.contents)return new Uint8Array(0);if(node.contents.subarray)return node.contents.subarray(0,node.usedBytes);return new Uint8Array(node.contents)},expandFileStorage(node,newCapacity){var prevCapacity=node.contents?node.contents.length:0;if(prevCapacity>=newCapacity)return;var CAPACITY_DOUBLING_MAX=1024*1024;newCapacity=Math.max(newCapacity,prevCapacity*(prevCapacity>>0);if(prevCapacity!=0)newCapacity=Math.max(newCapacity,256);var oldContents=node.contents;node.contents=new Uint8Array(newCapacity);if(node.usedBytes>0)node.contents.set(oldContents.subarray(0,node.usedBytes),0)},resizeFileStorage(node,newSize){if(node.usedBytes==newSize)return;if(newSize==0){node.contents=null;node.usedBytes=0}else{var oldContents=node.contents;node.contents=new Uint8Array(newSize);if(oldContents){node.contents.set(oldContents.subarray(0,Math.min(newSize,node.usedBytes)))}node.usedBytes=newSize}},node_ops:{getattr(node){var attr={};attr.dev=FS.isChrdev(node.mode)?node.id:1;attr.ino=node.id;attr.mode=node.mode;attr.nlink=1;attr.uid=0;attr.gid=0;attr.rdev=node.rdev;if(FS.isDir(node.mode)){attr.size=4096}else if(FS.isFile(node.mode)){attr.size=node.usedBytes}else if(FS.isLink(node.mode)){attr.size=node.link.length}else{attr.size=0}attr.atime=new Date(node.timestamp);attr.mtime=new Date(node.timestamp);attr.ctime=new Date(node.timestamp);attr.blksize=4096;attr.blocks=Math.ceil(attr.size/attr.blksize);return attr},setattr(node,attr){if(attr.mode!==undefined){node.mode=attr.mode}if(attr.timestamp!==undefined){node.timestamp=attr.timestamp}if(attr.size!==undefined){MEMFS.resizeFileStorage(node,attr.size)}},lookup(parent,name){throw FS.genericErrors[44]},mknod(parent,name,mode,dev){return MEMFS.createNode(parent,name,mode,dev)},rename(old_node,new_dir,new_name){if(FS.isDir(old_node.mode)){var new_node;try{new_node=FS.lookupNode(new_dir,new_name)}catch(e){}if(new_node){for(var i in new_node.contents){throw new FS.ErrnoError(55)}}}delete old_node.parent.contents[old_node.name];old_node.parent.timestamp=Date.now();old_node.name=new_name;new_dir.contents[new_name]=old_node;new_dir.timestamp=old_node.parent.timestamp},unlink(parent,name){delete parent.contents[name];parent.timestamp=Date.now()},rmdir(parent,name){var node=FS.lookupNode(parent,name);for(var i in node.contents){throw new FS.ErrnoError(55)}delete parent.contents[name];parent.timestamp=Date.now()},readdir(node){var entries=[".",".."];for(var key of Object.keys(node.contents)){entries.push(key)}return entries},symlink(parent,newname,oldpath){var node=MEMFS.createNode(parent,newname,511|40960,0);node.link=oldpath;return node},readlink(node){if(!FS.isLink(node.mode)){throw new FS.ErrnoError(28)}return node.link}},stream_ops:{read(stream,buffer,offset,length,position){var contents=stream.node.contents;if(position>=stream.node.usedBytes)return 0;var size=Math.min(stream.node.usedBytes-position,length);if(size>8&&contents.subarray){buffer.set(contents.subarray(position,position+size),offset)}else{for(var i=0;i0||position+length{var dep=!noRunDep?getUniqueRunDependency(`al ${url}`):"";readAsync(url,arrayBuffer=>{onload(new Uint8Array(arrayBuffer));if(dep)removeRunDependency(dep)},event=>{if(onerror){onerror()}else{throw`Loading data file "${url}" failed.`}});if(dep)addRunDependency(dep)};Module["asyncLoad"]=asyncLoad;var FS_createDataFile=(parent,name,fileData,canRead,canWrite,canOwn)=>{FS.createDataFile(parent,name,fileData,canRead,canWrite,canOwn)};Module["FS_createDataFile"]=FS_createDataFile;var preloadPlugins=Module["preloadPlugins"]||[];Module["preloadPlugins"]=preloadPlugins;var FS_handledByPreloadPlugin=(byteArray,fullname,finish,onerror)=>{if(typeof Browser!="undefined")Browser.init();var handled=false;preloadPlugins.forEach(plugin=>{if(handled)return;if(plugin["canHandle"](fullname)){plugin["handle"](byteArray,fullname,finish,onerror);handled=true}});return handled};Module["FS_handledByPreloadPlugin"]=FS_handledByPreloadPlugin;var FS_createPreloadedFile=(parent,name,url,canRead,canWrite,onload,onerror,dontCreateFile,canOwn,preFinish)=>{var fullname=name?PATH_FS.resolve(PATH.join2(parent,name)):parent;var dep=getUniqueRunDependency(`cp ${fullname}`);function processData(byteArray){function finish(byteArray){preFinish?.();if(!dontCreateFile){FS_createDataFile(parent,name,byteArray,canRead,canWrite,canOwn)}onload?.();removeRunDependency(dep)}if(FS_handledByPreloadPlugin(byteArray,fullname,finish,()=>{onerror?.();removeRunDependency(dep)})){return}finish(byteArray)}addRunDependency(dep);if(typeof url=="string"){asyncLoad(url,processData,onerror)}else{processData(url)}};Module["FS_createPreloadedFile"]=FS_createPreloadedFile;var FS_modeStringToFlags=str=>{var flagModes={r:0,"r+":2,w:512|64|1,"w+":512|64|2,a:1024|64|1,"a+":1024|64|2};var flags=flagModes[str];if(typeof flags=="undefined"){throw new Error(`Unknown file open mode: ${str}`)}return flags};Module["FS_modeStringToFlags"]=FS_modeStringToFlags;var FS_getMode=(canRead,canWrite)=>{var mode=0;if(canRead)mode|=292|73;if(canWrite)mode|=146;return mode};Module["FS_getMode"]=FS_getMode;var FS={root:null,mounts:[],devices:{},streams:[],nextInode:1,nameTable:null,currentPath:"/",initialized:false,ignorePermissions:true,ErrnoError:class{constructor(errno){this.name="ErrnoError";this.errno=errno}},genericErrors:{},filesystems:null,syncFSRequests:0,FSStream:class{constructor(){this.shared={}}get object(){return this.node}set object(val){this.node=val}get isRead(){return(this.flags&2097155)!==1}get isWrite(){return(this.flags&2097155)!==0}get isAppend(){return this.flags&1024}get flags(){return this.shared.flags}set flags(val){this.shared.flags=val}get position(){return this.shared.position}set position(val){this.shared.position=val}},FSNode:class{constructor(parent,name,mode,rdev){if(!parent){parent=this}this.parent=parent;this.mount=parent.mount;this.mounted=null;this.id=FS.nextInode++;this.name=name;this.mode=mode;this.node_ops={};this.stream_ops={};this.rdev=rdev;this.readMode=292|73;this.writeMode=146}get read(){return(this.mode&this.readMode)===this.readMode}set read(val){val?this.mode|=this.readMode:this.mode&=~this.readMode}get write(){return(this.mode&this.writeMode)===this.writeMode}set write(val){val?this.mode|=this.writeMode:this.mode&=~this.writeMode}get isFolder(){return FS.isDir(this.mode)}get isDevice(){return FS.isChrdev(this.mode)}},lookupPath(path,opts={}){path=PATH_FS.resolve(path);if(!path)return{path:"",node:null};var defaults={follow_mount:true,recurse_count:0};opts=Object.assign(defaults,opts);if(opts.recurse_count>8){throw new FS.ErrnoError(32)}var parts=path.split("/").filter(p=>!!p);var current=FS.root;var current_path="/";for(var i=0;i40){throw new FS.ErrnoError(32)}}}}return{path:current_path,node:current}},getPath(node){var path;while(true){if(FS.isRoot(node)){var mount=node.mount.mountpoint;if(!path)return mount;return mount[mount.length-1]!=="/"?`${mount}/${path}`:mount+path}path=path?`${node.name}/${path}`:node.name;node=node.parent}},hashName(parentid,name){var hash=0;for(var i=0;i>>0)%FS.nameTable.length},hashAddNode(node){var hash=FS.hashName(node.parent.id,node.name);node.name_next=FS.nameTable[hash];FS.nameTable[hash]=node},hashRemoveNode(node){var hash=FS.hashName(node.parent.id,node.name);if(FS.nameTable[hash]===node){FS.nameTable[hash]=node.name_next}else{var current=FS.nameTable[hash];while(current){if(current.name_next===node){current.name_next=node.name_next;break}current=current.name_next}}},lookupNode(parent,name){var errCode=FS.mayLookup(parent);if(errCode){throw new FS.ErrnoError(errCode)}var hash=FS.hashName(parent.id,name);for(var node=FS.nameTable[hash];node;node=node.name_next){var nodeName=node.name;if(node.parent.id===parent.id&&nodeName===name){return node}}return FS.lookup(parent,name)},createNode(parent,name,mode,rdev){var node=new FS.FSNode(parent,name,mode,rdev);FS.hashAddNode(node);return node},destroyNode(node){FS.hashRemoveNode(node)},isRoot(node){return node===node.parent},isMountpoint(node){return!!node.mounted},isFile(mode){return(mode&61440)===32768},isDir(mode){return(mode&61440)===16384},isLink(mode){return(mode&61440)===40960},isChrdev(mode){return(mode&61440)===8192},isBlkdev(mode){return(mode&61440)===24576},isFIFO(mode){return(mode&61440)===4096},isSocket(mode){return(mode&49152)===49152},flagsToPermissionString(flag){var perms=["r","w","rw"][flag&3];if(flag&512){perms+="w"}return perms},nodePermissions(node,perms){if(FS.ignorePermissions){return 0}if(perms.includes("r")&&!(node.mode&292)){return 2}else if(perms.includes("w")&&!(node.mode&146)){return 2}else if(perms.includes("x")&&!(node.mode&73)){return 2}return 0},mayLookup(dir){if(!FS.isDir(dir.mode))return 54;var errCode=FS.nodePermissions(dir,"x");if(errCode)return errCode;if(!dir.node_ops.lookup)return 2;return 0},mayCreate(dir,name){try{var node=FS.lookupNode(dir,name);return 20}catch(e){}return FS.nodePermissions(dir,"wx")},mayDelete(dir,name,isdir){var node;try{node=FS.lookupNode(dir,name)}catch(e){return e.errno}var errCode=FS.nodePermissions(dir,"wx");if(errCode){return errCode}if(isdir){if(!FS.isDir(node.mode)){return 54}if(FS.isRoot(node)||FS.getPath(node)===FS.cwd()){return 10}}else{if(FS.isDir(node.mode)){return 31}}return 0},mayOpen(node,flags){if(!node){return 44}if(FS.isLink(node.mode)){return 32}else if(FS.isDir(node.mode)){if(FS.flagsToPermissionString(flags)!=="r"||flags&512){return 31}}return FS.nodePermissions(node,FS.flagsToPermissionString(flags))},MAX_OPEN_FDS:4096,nextfd(){for(var fd=0;fd<=FS.MAX_OPEN_FDS;fd++){if(!FS.streams[fd]){return fd}}throw new FS.ErrnoError(33)},getStreamChecked(fd){var stream=FS.getStream(fd);if(!stream){throw new FS.ErrnoError(8)}return stream},getStream:fd=>FS.streams[fd],createStream(stream,fd=-1){stream=Object.assign(new FS.FSStream,stream);if(fd==-1){fd=FS.nextfd()}stream.fd=fd;FS.streams[fd]=stream;return stream},closeStream(fd){FS.streams[fd]=null},dupStream(origStream,fd=-1){var stream=FS.createStream(origStream,fd);stream.stream_ops?.dup?.(stream);return stream},chrdev_stream_ops:{open(stream){var device=FS.getDevice(stream.node.rdev);stream.stream_ops=device.stream_ops;stream.stream_ops.open?.(stream)},llseek(){throw new FS.ErrnoError(70)}},major:dev=>dev>>8,minor:dev=>dev&255,makedev:(ma,mi)=>ma<<8|mi,registerDevice(dev,ops){FS.devices[dev]={stream_ops:ops}},getDevice:dev=>FS.devices[dev],getMounts(mount){var mounts=[];var check=[mount];while(check.length){var m=check.pop();mounts.push(m);check.push(...m.mounts)}return mounts},syncfs(populate,callback){if(typeof populate=="function"){callback=populate;populate=false}FS.syncFSRequests++;if(FS.syncFSRequests>1){err(`warning: ${FS.syncFSRequests} FS.syncfs operations in flight at once, probably just doing extra work`)}var mounts=FS.getMounts(FS.root.mount);var completed=0;function doCallback(errCode){FS.syncFSRequests--;return callback(errCode)}function done(errCode){if(errCode){if(!done.errored){done.errored=true;return doCallback(errCode)}return}if(++completed>=mounts.length){doCallback(null)}}mounts.forEach(mount=>{if(!mount.type.syncfs){return done(null)}mount.type.syncfs(mount,populate,done)})},mount(type,opts,mountpoint){var root=mountpoint==="/";var pseudo=!mountpoint;var node;if(root&&FS.root){throw new FS.ErrnoError(10)}else if(!root&&!pseudo){var lookup=FS.lookupPath(mountpoint,{follow_mount:false});mountpoint=lookup.path;node=lookup.node;if(FS.isMountpoint(node)){throw new FS.ErrnoError(10)}if(!FS.isDir(node.mode)){throw new FS.ErrnoError(54)}}var mount={type:type,opts:opts,mountpoint:mountpoint,mounts:[]};var mountRoot=type.mount(mount);mountRoot.mount=mount;mount.root=mountRoot;if(root){FS.root=mountRoot}else if(node){node.mounted=mount;if(node.mount){node.mount.mounts.push(mount)}}return mountRoot},unmount(mountpoint){var lookup=FS.lookupPath(mountpoint,{follow_mount:false});if(!FS.isMountpoint(lookup.node)){throw new FS.ErrnoError(28)}var node=lookup.node;var mount=node.mounted;var mounts=FS.getMounts(mount);Object.keys(FS.nameTable).forEach(hash=>{var current=FS.nameTable[hash];while(current){var next=current.name_next;if(mounts.includes(current.mount)){FS.destroyNode(current)}current=next}});node.mounted=null;var idx=node.mount.mounts.indexOf(mount);node.mount.mounts.splice(idx,1)},lookup(parent,name){return parent.node_ops.lookup(parent,name)},mknod(path,mode,dev){var lookup=FS.lookupPath(path,{parent:true});var parent=lookup.node;var name=PATH.basename(path);if(!name||name==="."||name===".."){throw new FS.ErrnoError(28)}var errCode=FS.mayCreate(parent,name);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.mknod){throw new FS.ErrnoError(63)}return parent.node_ops.mknod(parent,name,mode,dev)},create(path,mode){mode=mode!==undefined?mode:438;mode&=4095;mode|=32768;return FS.mknod(path,mode,0)},mkdir(path,mode){mode=mode!==undefined?mode:511;mode&=511|512;mode|=16384;return FS.mknod(path,mode,0)},mkdirTree(path,mode){var dirs=path.split("/");var d="";for(var i=0;iFS.currentPath,chdir(path){var lookup=FS.lookupPath(path,{follow:true});if(lookup.node===null){throw new FS.ErrnoError(44)}if(!FS.isDir(lookup.node.mode)){throw new FS.ErrnoError(54)}var errCode=FS.nodePermissions(lookup.node,"x");if(errCode){throw new FS.ErrnoError(errCode)}FS.currentPath=lookup.path},createDefaultDirectories(){FS.mkdir("/tmp");FS.mkdir("/home");FS.mkdir("/home/web_user")},createDefaultDevices(){FS.mkdir("/dev");FS.registerDevice(FS.makedev(1,3),{read:()=>0,write:(stream,buffer,offset,length,pos)=>length});FS.mkdev("/dev/null",FS.makedev(1,3));TTY.register(FS.makedev(5,0),TTY.default_tty_ops);TTY.register(FS.makedev(6,0),TTY.default_tty1_ops);FS.mkdev("/dev/tty",FS.makedev(5,0));FS.mkdev("/dev/tty1",FS.makedev(6,0));var randomBuffer=new Uint8Array(1024),randomLeft=0;var randomByte=()=>{if(randomLeft===0){randomLeft=randomFill(randomBuffer).byteLength}return randomBuffer[--randomLeft]};FS.createDevice("/dev","random",randomByte);FS.createDevice("/dev","urandom",randomByte);FS.mkdir("/dev/shm");FS.mkdir("/dev/shm/tmp")},createSpecialDirectories(){FS.mkdir("/proc");var proc_self=FS.mkdir("/proc/self");FS.mkdir("/proc/self/fd");FS.mount({mount(){var node=FS.createNode(proc_self,"fd",16384|511,73);node.node_ops={lookup(parent,name){var fd=+name;var stream=FS.getStreamChecked(fd);var ret={parent:null,mount:{mountpoint:"fake"},node_ops:{readlink:()=>stream.path}};ret.parent=ret;return ret}};return node}},{},"/proc/self/fd")},createStandardStreams(){if(Module["stdin"]){FS.createDevice("/dev","stdin",Module["stdin"])}else{FS.symlink("/dev/tty","/dev/stdin")}if(Module["stdout"]){FS.createDevice("/dev","stdout",null,Module["stdout"])}else{FS.symlink("/dev/tty","/dev/stdout")}if(Module["stderr"]){FS.createDevice("/dev","stderr",null,Module["stderr"])}else{FS.symlink("/dev/tty1","/dev/stderr")}var stdin=FS.open("/dev/stdin",0);var stdout=FS.open("/dev/stdout",1);var stderr=FS.open("/dev/stderr",1)},staticInit(){[44].forEach(code=>{FS.genericErrors[code]=new FS.ErrnoError(code);FS.genericErrors[code].stack=""});FS.nameTable=new Array(4096);FS.mount(MEMFS,{},"/");FS.createDefaultDirectories();FS.createDefaultDevices();FS.createSpecialDirectories();FS.filesystems={MEMFS:MEMFS}},init(input,output,error){FS.init.initialized=true;Module["stdin"]=input||Module["stdin"];Module["stdout"]=output||Module["stdout"];Module["stderr"]=error||Module["stderr"];FS.createStandardStreams()},quit(){FS.init.initialized=false;for(var i=0;ithis.length-1||idx<0){return undefined}var chunkOffset=idx%this.chunkSize;var chunkNum=idx/this.chunkSize|0;return this.getter(chunkNum)[chunkOffset]}setDataGetter(getter){this.getter=getter}cacheLength(){var xhr=new XMLHttpRequest;xhr.open("HEAD",url,false);xhr.send(null);if(!(xhr.status>=200&&xhr.status<300||xhr.status===304))throw new Error("Couldn't load "+url+". Status: "+xhr.status);var datalength=Number(xhr.getResponseHeader("Content-length"));var header;var hasByteServing=(header=xhr.getResponseHeader("Accept-Ranges"))&&header==="bytes";var usesGzip=(header=xhr.getResponseHeader("Content-Encoding"))&&header==="gzip";var chunkSize=1024*1024;if(!hasByteServing)chunkSize=datalength;var doXHR=(from,to)=>{if(from>to)throw new Error("invalid range ("+from+", "+to+") or no bytes requested!");if(to>datalength-1)throw new Error("only "+datalength+" bytes available! programmer error!");var xhr=new XMLHttpRequest;xhr.open("GET",url,false);if(datalength!==chunkSize)xhr.setRequestHeader("Range","bytes="+from+"-"+to);xhr.responseType="arraybuffer";if(xhr.overrideMimeType){xhr.overrideMimeType("text/plain; charset=x-user-defined")}xhr.send(null);if(!(xhr.status>=200&&xhr.status<300||xhr.status===304))throw new Error("Couldn't load "+url+". Status: "+xhr.status);if(xhr.response!==undefined){return new Uint8Array(xhr.response||[])}return intArrayFromString(xhr.responseText||"",true)};var lazyArray=this;lazyArray.setDataGetter(chunkNum=>{var start=chunkNum*chunkSize;var end=(chunkNum+1)*chunkSize-1;end=Math.min(end,datalength-1);if(typeof lazyArray.chunks[chunkNum]=="undefined"){lazyArray.chunks[chunkNum]=doXHR(start,end)}if(typeof lazyArray.chunks[chunkNum]=="undefined")throw new Error("doXHR failed!");return lazyArray.chunks[chunkNum]});if(usesGzip||!datalength){chunkSize=datalength=1;datalength=this.getter(0).length;chunkSize=datalength;out("LazyFiles on gzip forces download of the whole file when length is accessed")}this._length=datalength;this._chunkSize=chunkSize;this.lengthKnown=true}get length(){if(!this.lengthKnown){this.cacheLength()}return this._length}get chunkSize(){if(!this.lengthKnown){this.cacheLength()}return this._chunkSize}}if(typeof XMLHttpRequest!="undefined"){if(!ENVIRONMENT_IS_WORKER)throw"Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc";var lazyArray=new LazyUint8Array;var properties={isDevice:false,contents:lazyArray}}else{var properties={isDevice:false,url:url}}var node=FS.createFile(parent,name,properties,canRead,canWrite);if(properties.contents){node.contents=properties.contents}else if(properties.url){node.contents=null;node.url=properties.url}Object.defineProperties(node,{usedBytes:{get:function(){return this.contents.length}}});var stream_ops={};var keys=Object.keys(node.stream_ops);keys.forEach(key=>{var fn=node.stream_ops[key];stream_ops[key]=(...args)=>{FS.forceLoadFile(node);return fn(...args)}});function writeChunks(stream,buffer,offset,length,position){var contents=stream.node.contents;if(position>=contents.length)return 0;var size=Math.min(contents.length-position,length);if(contents.slice){for(var i=0;i{FS.forceLoadFile(node);return writeChunks(stream,buffer,offset,length,position)};stream_ops.mmap=(stream,length,position,prot,flags)=>{FS.forceLoadFile(node);var ptr=mmapAlloc(length);if(!ptr){throw new FS.ErrnoError(48)}writeChunks(stream,HEAP8,ptr,length,position);return{ptr:ptr,allocated:true}};node.stream_ops=stream_ops;return node}};Module["FS"]=FS;var SYSCALLS={DEFAULT_POLLMASK:5,calculateAt(dirfd,path,allowEmpty){if(PATH.isAbs(path)){return path}var dir;if(dirfd===-100){dir=FS.cwd()}else{var dirstream=SYSCALLS.getStreamFromFD(dirfd);dir=dirstream.path}if(path.length==0){if(!allowEmpty){throw new FS.ErrnoError(44)}return dir}return PATH.join2(dir,path)},doStat(func,path,buf){var stat=func(path);SAFE_HEAP_STORE((buf>>2)*4,stat.dev,4);SAFE_HEAP_STORE((buf+4>>2)*4,stat.mode,4);SAFE_HEAP_STORE((buf+8>>2)*4,stat.nlink,4);SAFE_HEAP_STORE((buf+12>>2)*4,stat.uid,4);SAFE_HEAP_STORE((buf+16>>2)*4,stat.gid,4);SAFE_HEAP_STORE((buf+20>>2)*4,stat.rdev,4);tempI64=[stat.size>>>0,(tempDouble=stat.size,+Math.abs(tempDouble)>=1?tempDouble>0?+Math.floor(tempDouble/4294967296)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],SAFE_HEAP_STORE((buf+24>>2)*4,tempI64[0],4),SAFE_HEAP_STORE((buf+28>>2)*4,tempI64[1],4);SAFE_HEAP_STORE((buf+32>>2)*4,4096,4);SAFE_HEAP_STORE((buf+36>>2)*4,stat.blocks,4);var atime=stat.atime.getTime();var mtime=stat.mtime.getTime();var ctime=stat.ctime.getTime();tempI64=[Math.floor(atime/1e3)>>>0,(tempDouble=Math.floor(atime/1e3),+Math.abs(tempDouble)>=1?tempDouble>0?+Math.floor(tempDouble/4294967296)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],SAFE_HEAP_STORE((buf+40>>2)*4,tempI64[0],4),SAFE_HEAP_STORE((buf+44>>2)*4,tempI64[1],4);SAFE_HEAP_STORE((buf+48>>2)*4,atime%1e3*1e3,4);tempI64=[Math.floor(mtime/1e3)>>>0,(tempDouble=Math.floor(mtime/1e3),+Math.abs(tempDouble)>=1?tempDouble>0?+Math.floor(tempDouble/4294967296)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],SAFE_HEAP_STORE((buf+56>>2)*4,tempI64[0],4),SAFE_HEAP_STORE((buf+60>>2)*4,tempI64[1],4);SAFE_HEAP_STORE((buf+64>>2)*4,mtime%1e3*1e3,4);tempI64=[Math.floor(ctime/1e3)>>>0,(tempDouble=Math.floor(ctime/1e3),+Math.abs(tempDouble)>=1?tempDouble>0?+Math.floor(tempDouble/4294967296)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],SAFE_HEAP_STORE((buf+72>>2)*4,tempI64[0],4),SAFE_HEAP_STORE((buf+76>>2)*4,tempI64[1],4);SAFE_HEAP_STORE((buf+80>>2)*4,ctime%1e3*1e3,4);tempI64=[stat.ino>>>0,(tempDouble=stat.ino,+Math.abs(tempDouble)>=1?tempDouble>0?+Math.floor(tempDouble/4294967296)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],SAFE_HEAP_STORE((buf+88>>2)*4,tempI64[0],4),SAFE_HEAP_STORE((buf+92>>2)*4,tempI64[1],4);return 0},doMsync(addr,stream,len,flags,offset){if(!FS.isFile(stream.node.mode)){throw new FS.ErrnoError(43)}if(flags&2){return 0}var buffer=HEAPU8.slice(addr,addr+len);FS.msync(stream,buffer,offset,len,flags)},getStreamFromFD(fd){var stream=FS.getStreamChecked(fd);return stream},varargs:undefined,getStr(ptr){var ret=UTF8ToString(ptr);return ret}};Module["SYSCALLS"]=SYSCALLS;function ___syscall_fcntl64(fd,cmd,varargs){SYSCALLS.varargs=varargs;try{var stream=SYSCALLS.getStreamFromFD(fd);switch(cmd){case 0:{var arg=syscallGetVarargI();if(arg<0){return-28}while(FS.streams[arg]){arg++}var newStream;newStream=FS.dupStream(stream,arg);return newStream.fd}case 1:case 2:return 0;case 3:return stream.flags;case 4:{var arg=syscallGetVarargI();stream.flags|=arg;return 0}case 12:{var arg=syscallGetVarargP();var offset=0;SAFE_HEAP_STORE((arg+offset>>1)*2,2,2);return 0}case 13:case 14:return 0}return-28}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}Module["___syscall_fcntl64"]=___syscall_fcntl64;function ___syscall_ioctl(fd,op,varargs){SYSCALLS.varargs=varargs;try{var stream=SYSCALLS.getStreamFromFD(fd);switch(op){case 21509:{if(!stream.tty)return-59;return 0}case 21505:{if(!stream.tty)return-59;if(stream.tty.ops.ioctl_tcgets){var termios=stream.tty.ops.ioctl_tcgets(stream);var argp=syscallGetVarargP();SAFE_HEAP_STORE((argp>>2)*4,termios.c_iflag||0,4);SAFE_HEAP_STORE((argp+4>>2)*4,termios.c_oflag||0,4);SAFE_HEAP_STORE((argp+8>>2)*4,termios.c_cflag||0,4);SAFE_HEAP_STORE((argp+12>>2)*4,termios.c_lflag||0,4);for(var i=0;i<32;i++){SAFE_HEAP_STORE(argp+i+17,termios.c_cc[i]||0,1)}return 0}return 0}case 21510:case 21511:case 21512:{if(!stream.tty)return-59;return 0}case 21506:case 21507:case 21508:{if(!stream.tty)return-59;if(stream.tty.ops.ioctl_tcsets){var argp=syscallGetVarargP();var c_iflag=SAFE_HEAP_LOAD((argp>>2)*4,4,0);var c_oflag=SAFE_HEAP_LOAD((argp+4>>2)*4,4,0);var c_cflag=SAFE_HEAP_LOAD((argp+8>>2)*4,4,0);var c_lflag=SAFE_HEAP_LOAD((argp+12>>2)*4,4,0);var c_cc=[];for(var i=0;i<32;i++){c_cc.push(SAFE_HEAP_LOAD(argp+i+17,1,0))}return stream.tty.ops.ioctl_tcsets(stream.tty,op,{c_iflag:c_iflag,c_oflag:c_oflag,c_cflag:c_cflag,c_lflag:c_lflag,c_cc:c_cc})}return 0}case 21519:{if(!stream.tty)return-59;var argp=syscallGetVarargP();SAFE_HEAP_STORE((argp>>2)*4,0,4);return 0}case 21520:{if(!stream.tty)return-59;return-28}case 21531:{var argp=syscallGetVarargP();return FS.ioctl(stream,op,argp)}case 21523:{if(!stream.tty)return-59;if(stream.tty.ops.ioctl_tiocgwinsz){var winsize=stream.tty.ops.ioctl_tiocgwinsz(stream.tty);var argp=syscallGetVarargP();SAFE_HEAP_STORE((argp>>1)*2,winsize[0],2);SAFE_HEAP_STORE((argp+2>>1)*2,winsize[1],2)}return 0}case 21524:{if(!stream.tty)return-59;return 0}case 21515:{if(!stream.tty)return-59;return 0}default:return-28}}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}Module["___syscall_ioctl"]=___syscall_ioctl;function ___syscall_openat(dirfd,path,flags,varargs){SYSCALLS.varargs=varargs;try{path=SYSCALLS.getStr(path);path=SYSCALLS.calculateAt(dirfd,path);var mode=varargs?syscallGetVarargI():0;return FS.open(path,flags,mode).fd}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}Module["___syscall_openat"]=___syscall_openat;var __abort_js=()=>{abort("")};Module["__abort_js"]=__abort_js;var __emscripten_memcpy_js=(dest,src,num)=>HEAPU8.copyWithin(dest,src,src+num);Module["__emscripten_memcpy_js"]=__emscripten_memcpy_js;var readEmAsmArgsArray=[];Module["readEmAsmArgsArray"]=readEmAsmArgsArray;var readEmAsmArgs=(sigPtr,buf)=>{readEmAsmArgsArray.length=0;var ch;while(ch=SAFE_HEAP_LOAD(sigPtr++,1,1)){var wide=ch!=105;wide&=ch!=112;buf+=wide&&buf%8?4:0;readEmAsmArgsArray.push(ch==112?SAFE_HEAP_LOAD((buf>>2)*4,4,1):ch==105?SAFE_HEAP_LOAD((buf>>2)*4,4,0):SAFE_HEAP_LOAD_D((buf>>3)*8,8,0));buf+=wide?8:4}return readEmAsmArgsArray};Module["readEmAsmArgs"]=readEmAsmArgs;var runEmAsmFunction=(code,sigPtr,argbuf)=>{var args=readEmAsmArgs(sigPtr,argbuf);return ASM_CONSTS[code](...args)};Module["runEmAsmFunction"]=runEmAsmFunction;var _emscripten_asm_const_int=(code,sigPtr,argbuf)=>runEmAsmFunction(code,sigPtr,argbuf);Module["_emscripten_asm_const_int"]=_emscripten_asm_const_int;var getHeapMax=()=>2147483648;Module["getHeapMax"]=getHeapMax;var growMemory=size=>{var b=wasmMemory.buffer;var pages=(size-b.byteLength+65535)/65536;try{wasmMemory.grow(pages);updateMemoryViews();return 1}catch(e){}};Module["growMemory"]=growMemory;var _emscripten_resize_heap=requestedSize=>{var oldSize=HEAPU8.length;requestedSize>>>=0;var maxHeapSize=getHeapMax();if(requestedSize>maxHeapSize){return false}var alignUp=(x,multiple)=>x+(multiple-x%multiple)%multiple;for(var cutDown=1;cutDown<=4;cutDown*=2){var overGrownHeapSize=oldSize*(1+.2/cutDown);overGrownHeapSize=Math.min(overGrownHeapSize,requestedSize+100663296);var newSize=Math.min(maxHeapSize,alignUp(Math.max(requestedSize,overGrownHeapSize),65536));var replacement=growMemory(newSize);if(replacement){return true}}return false};Module["_emscripten_resize_heap"]=_emscripten_resize_heap;function _fd_close(fd){try{var stream=SYSCALLS.getStreamFromFD(fd);FS.close(stream);return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}Module["_fd_close"]=_fd_close;var doReadv=(stream,iov,iovcnt,offset)=>{var ret=0;for(var i=0;i>2)*4,4,1);var len=SAFE_HEAP_LOAD((iov+4>>2)*4,4,1);iov+=8;var curr=FS.read(stream,HEAP8,ptr,len,offset);if(curr<0)return-1;ret+=curr;if(curr>2)*4,num,4);return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}Module["_fd_read"]=_fd_read;var convertI32PairToI53Checked=(lo,hi)=>hi+2097152>>>0<4194305-!!lo?(lo>>>0)+hi*4294967296:NaN;Module["convertI32PairToI53Checked"]=convertI32PairToI53Checked;function _fd_seek(fd,offset_low,offset_high,whence,newOffset){var offset=convertI32PairToI53Checked(offset_low,offset_high);try{if(isNaN(offset))return 61;var stream=SYSCALLS.getStreamFromFD(fd);FS.llseek(stream,offset,whence);tempI64=[stream.position>>>0,(tempDouble=stream.position,+Math.abs(tempDouble)>=1?tempDouble>0?+Math.floor(tempDouble/4294967296)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],SAFE_HEAP_STORE((newOffset>>2)*4,tempI64[0],4),SAFE_HEAP_STORE((newOffset+4>>2)*4,tempI64[1],4);if(stream.getdents&&offset===0&&whence===0)stream.getdents=null;return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}Module["_fd_seek"]=_fd_seek;var doWritev=(stream,iov,iovcnt,offset)=>{var ret=0;for(var i=0;i>2)*4,4,1);var len=SAFE_HEAP_LOAD((iov+4>>2)*4,4,1);iov+=8;var curr=FS.write(stream,HEAP8,ptr,len,offset);if(curr<0)return-1;ret+=curr;if(typeof offset!="undefined"){offset+=curr}}return ret};Module["doWritev"]=doWritev;function _fd_write(fd,iov,iovcnt,pnum){try{var stream=SYSCALLS.getStreamFromFD(fd);var num=doWritev(stream,iov,iovcnt);SAFE_HEAP_STORE((pnum>>2)*4,num,4);return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}Module["_fd_write"]=_fd_write;var _getentropy=(buffer,size)=>{randomFill(HEAPU8.subarray(buffer,buffer+size));return 0};Module["_getentropy"]=_getentropy;var getCFunc=ident=>{var func=Module["_"+ident];return func};Module["getCFunc"]=getCFunc;var writeArrayToMemory=(array,buffer)=>{HEAP8.set(array,buffer)};Module["writeArrayToMemory"]=writeArrayToMemory;var stringToUTF8=(str,outPtr,maxBytesToWrite)=>stringToUTF8Array(str,HEAPU8,outPtr,maxBytesToWrite);Module["stringToUTF8"]=stringToUTF8;var stackAlloc=sz=>__emscripten_stack_alloc(sz);Module["stackAlloc"]=stackAlloc;var stringToUTF8OnStack=str=>{var size=lengthBytesUTF8(str)+1;var ret=stackAlloc(size);stringToUTF8(str,ret,size);return ret};Module["stringToUTF8OnStack"]=stringToUTF8OnStack;var ccall=(ident,returnType,argTypes,args,opts)=>{var toC={string:str=>{var ret=0;if(str!==null&&str!==undefined&&str!==0){ret=stringToUTF8OnStack(str)}return ret},array:arr=>{var ret=stackAlloc(arr.length);writeArrayToMemory(arr,ret);return ret}};function convertReturnValue(ret){if(returnType==="string"){return UTF8ToString(ret)}if(returnType==="boolean")return Boolean(ret);return ret}var func=getCFunc(ident);var cArgs=[];var stack=0;if(args){for(var i=0;i{var numericArgs=!argTypes||argTypes.every(type=>type==="number"||type==="boolean");var numericRet=returnType!=="string";if(numericRet&&numericArgs&&!opts){return getCFunc(ident)}return(...args)=>ccall(ident,returnType,argTypes,args,opts)};Module["cwrap"]=cwrap;FS.createPreloadedFile=FS_createPreloadedFile;FS.staticInit();var wasmImports={a:___assert_fail,h:___cxa_throw,g:___syscall_fcntl64,j:___syscall_ioctl,k:___syscall_openat,o:__abort_js,l:__emscripten_memcpy_js,c:alignfault,d:_emscripten_asm_const_int,p:_emscripten_resize_heap,e:_fd_close,i:_fd_read,m:_fd_seek,f:_fd_write,n:_getentropy,b:segfault};var wasmExports=createWasm();var ___wasm_call_ctors=()=>(___wasm_call_ctors=wasmExports["r"])();var _malloc=Module["_malloc"]=a0=>(_malloc=Module["_malloc"]=wasmExports["t"])(a0);var _free=Module["_free"]=a0=>(_free=Module["_free"]=wasmExports["u"])(a0);var _ma_device__on_notification_unlocked=Module["_ma_device__on_notification_unlocked"]=a0=>(_ma_device__on_notification_unlocked=Module["_ma_device__on_notification_unlocked"]=wasmExports["v"])(a0);var _ma_malloc_emscripten=Module["_ma_malloc_emscripten"]=(a0,a1)=>(_ma_malloc_emscripten=Module["_ma_malloc_emscripten"]=wasmExports["w"])(a0,a1);var _ma_free_emscripten=Module["_ma_free_emscripten"]=(a0,a1)=>(_ma_free_emscripten=Module["_ma_free_emscripten"]=wasmExports["x"])(a0,a1);var _ma_device_process_pcm_frames_capture__webaudio=Module["_ma_device_process_pcm_frames_capture__webaudio"]=(a0,a1,a2)=>(_ma_device_process_pcm_frames_capture__webaudio=Module["_ma_device_process_pcm_frames_capture__webaudio"]=wasmExports["y"])(a0,a1,a2);var _ma_device_process_pcm_frames_playback__webaudio=Module["_ma_device_process_pcm_frames_playback__webaudio"]=(a0,a1,a2)=>(_ma_device_process_pcm_frames_playback__webaudio=Module["_ma_device_process_pcm_frames_playback__webaudio"]=wasmExports["z"])(a0,a1,a2);var _createWorkerInWasm=Module["_createWorkerInWasm"]=()=>(_createWorkerInWasm=Module["_createWorkerInWasm"]=wasmExports["A"])();var _sendToWorker=Module["_sendToWorker"]=(a0,a1)=>(_sendToWorker=Module["_sendToWorker"]=wasmExports["B"])(a0,a1);var _nativeFree=Module["_nativeFree"]=a0=>(_nativeFree=Module["_nativeFree"]=wasmExports["C"])(a0);var _voiceEndedCallback=Module["_voiceEndedCallback"]=a0=>(_voiceEndedCallback=Module["_voiceEndedCallback"]=wasmExports["D"])(a0);var _setDartEventCallback=Module["_setDartEventCallback"]=(a0,a1,a2)=>(_setDartEventCallback=Module["_setDartEventCallback"]=wasmExports["E"])(a0,a1,a2);var _initEngine=Module["_initEngine"]=()=>(_initEngine=Module["_initEngine"]=wasmExports["F"])();var _dispose=Module["_dispose"]=()=>(_dispose=Module["_dispose"]=wasmExports["G"])();var _isInited=Module["_isInited"]=()=>(_isInited=Module["_isInited"]=wasmExports["H"])();var _loadFile=Module["_loadFile"]=(a0,a1)=>(_loadFile=Module["_loadFile"]=wasmExports["I"])(a0,a1);var _loadMem=Module["_loadMem"]=(a0,a1,a2,a3,a4)=>(_loadMem=Module["_loadMem"]=wasmExports["J"])(a0,a1,a2,a3,a4);var _loadWaveform=Module["_loadWaveform"]=(a0,a1,a2,a3,a4)=>(_loadWaveform=Module["_loadWaveform"]=wasmExports["K"])(a0,a1,a2,a3,a4);var _setWaveformScale=Module["_setWaveformScale"]=(a0,a1)=>(_setWaveformScale=Module["_setWaveformScale"]=wasmExports["L"])(a0,a1);var _setWaveformDetune=Module["_setWaveformDetune"]=(a0,a1)=>(_setWaveformDetune=Module["_setWaveformDetune"]=wasmExports["M"])(a0,a1);var _setWaveformFreq=Module["_setWaveformFreq"]=(a0,a1)=>(_setWaveformFreq=Module["_setWaveformFreq"]=wasmExports["N"])(a0,a1);var _setSuperWave=Module["_setSuperWave"]=(a0,a1)=>(_setSuperWave=Module["_setSuperWave"]=wasmExports["O"])(a0,a1);var _setWaveform=Module["_setWaveform"]=(a0,a1)=>(_setWaveform=Module["_setWaveform"]=wasmExports["P"])(a0,a1);var _speechText=Module["_speechText"]=(a0,a1)=>(_speechText=Module["_speechText"]=wasmExports["Q"])(a0,a1);var _pauseSwitch=Module["_pauseSwitch"]=a0=>(_pauseSwitch=Module["_pauseSwitch"]=wasmExports["R"])(a0);var _setPause=Module["_setPause"]=(a0,a1)=>(_setPause=Module["_setPause"]=wasmExports["S"])(a0,a1);var _getPause=Module["_getPause"]=a0=>(_getPause=Module["_getPause"]=wasmExports["T"])(a0);var _setRelativePlaySpeed=Module["_setRelativePlaySpeed"]=(a0,a1)=>(_setRelativePlaySpeed=Module["_setRelativePlaySpeed"]=wasmExports["U"])(a0,a1);var _getRelativePlaySpeed=Module["_getRelativePlaySpeed"]=a0=>(_getRelativePlaySpeed=Module["_getRelativePlaySpeed"]=wasmExports["V"])(a0);var _play=Module["_play"]=(a0,a1,a2,a3,a4,a5,a6)=>(_play=Module["_play"]=wasmExports["W"])(a0,a1,a2,a3,a4,a5,a6);var _stop=Module["_stop"]=a0=>(_stop=Module["_stop"]=wasmExports["X"])(a0);var _disposeSound=Module["_disposeSound"]=a0=>(_disposeSound=Module["_disposeSound"]=wasmExports["Y"])(a0);var _disposeAllSound=Module["_disposeAllSound"]=()=>(_disposeAllSound=Module["_disposeAllSound"]=wasmExports["Z"])();var _getLooping=Module["_getLooping"]=a0=>(_getLooping=Module["_getLooping"]=wasmExports["_"])(a0);var _setLooping=Module["_setLooping"]=(a0,a1)=>(_setLooping=Module["_setLooping"]=wasmExports["$"])(a0,a1);var _getLoopPoint=Module["_getLoopPoint"]=a0=>(_getLoopPoint=Module["_getLoopPoint"]=wasmExports["aa"])(a0);var _setLoopPoint=Module["_setLoopPoint"]=(a0,a1)=>(_setLoopPoint=Module["_setLoopPoint"]=wasmExports["ba"])(a0,a1);var _setVisualizationEnabled=Module["_setVisualizationEnabled"]=a0=>(_setVisualizationEnabled=Module["_setVisualizationEnabled"]=wasmExports["ca"])(a0);var _getVisualizationEnabled=Module["_getVisualizationEnabled"]=()=>(_getVisualizationEnabled=Module["_getVisualizationEnabled"]=wasmExports["da"])();var _getFft=Module["_getFft"]=a0=>(_getFft=Module["_getFft"]=wasmExports["ea"])(a0);var _getWave=Module["_getWave"]=a0=>(_getWave=Module["_getWave"]=wasmExports["fa"])(a0);var _setFftSmoothing=Module["_setFftSmoothing"]=a0=>(_setFftSmoothing=Module["_setFftSmoothing"]=wasmExports["ga"])(a0);var _getAudioTexture=Module["_getAudioTexture"]=a0=>(_getAudioTexture=Module["_getAudioTexture"]=wasmExports["ha"])(a0);var _getAudioTexture2D=Module["_getAudioTexture2D"]=a0=>(_getAudioTexture2D=Module["_getAudioTexture2D"]=wasmExports["ia"])(a0);var _getTextureValue=Module["_getTextureValue"]=(a0,a1)=>(_getTextureValue=Module["_getTextureValue"]=wasmExports["ja"])(a0,a1);var _getLength=Module["_getLength"]=a0=>(_getLength=Module["_getLength"]=wasmExports["ka"])(a0);var _seek=Module["_seek"]=(a0,a1)=>(_seek=Module["_seek"]=wasmExports["la"])(a0,a1);var _getPosition=Module["_getPosition"]=a0=>(_getPosition=Module["_getPosition"]=wasmExports["ma"])(a0);var _getGlobalVolume=Module["_getGlobalVolume"]=()=>(_getGlobalVolume=Module["_getGlobalVolume"]=wasmExports["na"])();var _setGlobalVolume=Module["_setGlobalVolume"]=a0=>(_setGlobalVolume=Module["_setGlobalVolume"]=wasmExports["oa"])(a0);var _getVolume=Module["_getVolume"]=a0=>(_getVolume=Module["_getVolume"]=wasmExports["pa"])(a0);var _setVolume=Module["_setVolume"]=(a0,a1)=>(_setVolume=Module["_setVolume"]=wasmExports["qa"])(a0,a1);var _getPan=Module["_getPan"]=a0=>(_getPan=Module["_getPan"]=wasmExports["ra"])(a0);var _setPan=Module["_setPan"]=(a0,a1)=>(_setPan=Module["_setPan"]=wasmExports["sa"])(a0,a1);var _setPanAbsolute=Module["_setPanAbsolute"]=(a0,a1,a2)=>(_setPanAbsolute=Module["_setPanAbsolute"]=wasmExports["ta"])(a0,a1,a2);var _getIsValidVoiceHandle=Module["_getIsValidVoiceHandle"]=a0=>(_getIsValidVoiceHandle=Module["_getIsValidVoiceHandle"]=wasmExports["ua"])(a0);var _getActiveVoiceCount=Module["_getActiveVoiceCount"]=()=>(_getActiveVoiceCount=Module["_getActiveVoiceCount"]=wasmExports["va"])();var _countAudioSource=Module["_countAudioSource"]=a0=>(_countAudioSource=Module["_countAudioSource"]=wasmExports["wa"])(a0);var _getVoiceCount=Module["_getVoiceCount"]=()=>(_getVoiceCount=Module["_getVoiceCount"]=wasmExports["xa"])();var _getProtectVoice=Module["_getProtectVoice"]=a0=>(_getProtectVoice=Module["_getProtectVoice"]=wasmExports["ya"])(a0);var _setProtectVoice=Module["_setProtectVoice"]=(a0,a1)=>(_setProtectVoice=Module["_setProtectVoice"]=wasmExports["za"])(a0,a1);var _getMaxActiveVoiceCount=Module["_getMaxActiveVoiceCount"]=()=>(_getMaxActiveVoiceCount=Module["_getMaxActiveVoiceCount"]=wasmExports["Aa"])();var _setMaxActiveVoiceCount=Module["_setMaxActiveVoiceCount"]=a0=>(_setMaxActiveVoiceCount=Module["_setMaxActiveVoiceCount"]=wasmExports["Ba"])(a0);var _fadeGlobalVolume=Module["_fadeGlobalVolume"]=(a0,a1)=>(_fadeGlobalVolume=Module["_fadeGlobalVolume"]=wasmExports["Ca"])(a0,a1);var _fadeVolume=Module["_fadeVolume"]=(a0,a1,a2)=>(_fadeVolume=Module["_fadeVolume"]=wasmExports["Da"])(a0,a1,a2);var _fadePan=Module["_fadePan"]=(a0,a1,a2)=>(_fadePan=Module["_fadePan"]=wasmExports["Ea"])(a0,a1,a2);var _fadeRelativePlaySpeed=Module["_fadeRelativePlaySpeed"]=(a0,a1,a2)=>(_fadeRelativePlaySpeed=Module["_fadeRelativePlaySpeed"]=wasmExports["Fa"])(a0,a1,a2);var _schedulePause=Module["_schedulePause"]=(a0,a1)=>(_schedulePause=Module["_schedulePause"]=wasmExports["Ga"])(a0,a1);var _scheduleStop=Module["_scheduleStop"]=(a0,a1)=>(_scheduleStop=Module["_scheduleStop"]=wasmExports["Ha"])(a0,a1);var _oscillateVolume=Module["_oscillateVolume"]=(a0,a1,a2,a3)=>(_oscillateVolume=Module["_oscillateVolume"]=wasmExports["Ia"])(a0,a1,a2,a3);var _oscillatePan=Module["_oscillatePan"]=(a0,a1,a2,a3)=>(_oscillatePan=Module["_oscillatePan"]=wasmExports["Ja"])(a0,a1,a2,a3);var _oscillateRelativePlaySpeed=Module["_oscillateRelativePlaySpeed"]=(a0,a1,a2,a3)=>(_oscillateRelativePlaySpeed=Module["_oscillateRelativePlaySpeed"]=wasmExports["Ka"])(a0,a1,a2,a3);var _oscillateGlobalVolume=Module["_oscillateGlobalVolume"]=(a0,a1,a2)=>(_oscillateGlobalVolume=Module["_oscillateGlobalVolume"]=wasmExports["La"])(a0,a1,a2);var _isFilterActive=Module["_isFilterActive"]=(a0,a1)=>(_isFilterActive=Module["_isFilterActive"]=wasmExports["Ma"])(a0,a1);var _getFilterParamNames=Module["_getFilterParamNames"]=(a0,a1,a2)=>(_getFilterParamNames=Module["_getFilterParamNames"]=wasmExports["Na"])(a0,a1,a2);var _addGlobalFilter=Module["_addGlobalFilter"]=a0=>(_addGlobalFilter=Module["_addGlobalFilter"]=wasmExports["Oa"])(a0);var _removeGlobalFilter=Module["_removeGlobalFilter"]=a0=>(_removeGlobalFilter=Module["_removeGlobalFilter"]=wasmExports["Pa"])(a0);var _setFxParams=Module["_setFxParams"]=(a0,a1,a2)=>(_setFxParams=Module["_setFxParams"]=wasmExports["Qa"])(a0,a1,a2);var _getFxParams=Module["_getFxParams"]=(a0,a1)=>(_getFxParams=Module["_getFxParams"]=wasmExports["Ra"])(a0,a1);var _play3d=Module["_play3d"]=(a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11)=>(_play3d=Module["_play3d"]=wasmExports["Sa"])(a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11);var _set3dSoundSpeed=Module["_set3dSoundSpeed"]=a0=>(_set3dSoundSpeed=Module["_set3dSoundSpeed"]=wasmExports["Ta"])(a0);var _get3dSoundSpeed=Module["_get3dSoundSpeed"]=()=>(_get3dSoundSpeed=Module["_get3dSoundSpeed"]=wasmExports["Ua"])();var _set3dListenerParameters=Module["_set3dListenerParameters"]=(a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11)=>(_set3dListenerParameters=Module["_set3dListenerParameters"]=wasmExports["Va"])(a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11);var _set3dListenerPosition=Module["_set3dListenerPosition"]=(a0,a1,a2)=>(_set3dListenerPosition=Module["_set3dListenerPosition"]=wasmExports["Wa"])(a0,a1,a2);var _set3dListenerAt=Module["_set3dListenerAt"]=(a0,a1,a2)=>(_set3dListenerAt=Module["_set3dListenerAt"]=wasmExports["Xa"])(a0,a1,a2);var _set3dListenerUp=Module["_set3dListenerUp"]=(a0,a1,a2)=>(_set3dListenerUp=Module["_set3dListenerUp"]=wasmExports["Ya"])(a0,a1,a2);var _set3dListenerVelocity=Module["_set3dListenerVelocity"]=(a0,a1,a2)=>(_set3dListenerVelocity=Module["_set3dListenerVelocity"]=wasmExports["Za"])(a0,a1,a2);var _set3dSourceParameters=Module["_set3dSourceParameters"]=(a0,a1,a2,a3,a4,a5,a6)=>(_set3dSourceParameters=Module["_set3dSourceParameters"]=wasmExports["_a"])(a0,a1,a2,a3,a4,a5,a6);var _set3dSourcePosition=Module["_set3dSourcePosition"]=(a0,a1,a2,a3)=>(_set3dSourcePosition=Module["_set3dSourcePosition"]=wasmExports["$a"])(a0,a1,a2,a3);var _set3dSourceVelocity=Module["_set3dSourceVelocity"]=(a0,a1,a2,a3)=>(_set3dSourceVelocity=Module["_set3dSourceVelocity"]=wasmExports["ab"])(a0,a1,a2,a3);var _set3dSourceMinMaxDistance=Module["_set3dSourceMinMaxDistance"]=(a0,a1,a2)=>(_set3dSourceMinMaxDistance=Module["_set3dSourceMinMaxDistance"]=wasmExports["bb"])(a0,a1,a2);var _set3dSourceAttenuation=Module["_set3dSourceAttenuation"]=(a0,a1,a2)=>(_set3dSourceAttenuation=Module["_set3dSourceAttenuation"]=wasmExports["cb"])(a0,a1,a2);var _set3dSourceDopplerFactor=Module["_set3dSourceDopplerFactor"]=(a0,a1)=>(_set3dSourceDopplerFactor=Module["_set3dSourceDopplerFactor"]=wasmExports["db"])(a0,a1);var _js_init=Module["_js_init"]=a0=>(_js_init=Module["_js_init"]=wasmExports["eb"])(a0);var _js_load=Module["_js_load"]=()=>(_js_load=Module["_js_load"]=wasmExports["fb"])();var _js_play=Module["_js_play"]=a0=>(_js_play=Module["_js_play"]=wasmExports["gb"])(a0);var _js_dispose=Module["_js_dispose"]=()=>(_js_dispose=Module["_js_dispose"]=wasmExports["hb"])();var _listCaptureDevices=Module["_listCaptureDevices"]=(a0,a1,a2)=>(_listCaptureDevices=Module["_listCaptureDevices"]=wasmExports["ib"])(a0,a1,a2);var _freeListCaptureDevices=Module["_freeListCaptureDevices"]=(a0,a1,a2)=>(_freeListCaptureDevices=Module["_freeListCaptureDevices"]=wasmExports["jb"])(a0,a1,a2);var _initCapture=Module["_initCapture"]=a0=>(_initCapture=Module["_initCapture"]=wasmExports["kb"])(a0);var _disposeCapture=Module["_disposeCapture"]=()=>(_disposeCapture=Module["_disposeCapture"]=wasmExports["lb"])();var _isCaptureInited=Module["_isCaptureInited"]=()=>(_isCaptureInited=Module["_isCaptureInited"]=wasmExports["mb"])();var _isCaptureStarted=Module["_isCaptureStarted"]=()=>(_isCaptureStarted=Module["_isCaptureStarted"]=wasmExports["nb"])();var _startCapture=Module["_startCapture"]=()=>(_startCapture=Module["_startCapture"]=wasmExports["ob"])();var _stopCapture=Module["_stopCapture"]=()=>(_stopCapture=Module["_stopCapture"]=wasmExports["pb"])();var _getCaptureFft=Module["_getCaptureFft"]=a0=>(_getCaptureFft=Module["_getCaptureFft"]=wasmExports["qb"])(a0);var _getCaptureWave=Module["_getCaptureWave"]=a0=>(_getCaptureWave=Module["_getCaptureWave"]=wasmExports["rb"])(a0);var _getCaptureTexture=Module["_getCaptureTexture"]=a0=>(_getCaptureTexture=Module["_getCaptureTexture"]=wasmExports["sb"])(a0);var _getCaptureAudioTexture2D=Module["_getCaptureAudioTexture2D"]=a0=>(_getCaptureAudioTexture2D=Module["_getCaptureAudioTexture2D"]=wasmExports["tb"])(a0);var _getCaptureTextureValue=Module["_getCaptureTextureValue"]=(a0,a1)=>(_getCaptureTextureValue=Module["_getCaptureTextureValue"]=wasmExports["ub"])(a0,a1);var _setCaptureFftSmoothing=Module["_setCaptureFftSmoothing"]=a0=>(_setCaptureFftSmoothing=Module["_setCaptureFftSmoothing"]=wasmExports["vb"])(a0);var _emscripten_get_sbrk_ptr=()=>(_emscripten_get_sbrk_ptr=wasmExports["wb"])();var _sbrk=a0=>(_sbrk=wasmExports["xb"])(a0);var _emscripten_stack_get_base=()=>(_emscripten_stack_get_base=wasmExports["yb"])();var __emscripten_stack_restore=a0=>(__emscripten_stack_restore=wasmExports["zb"])(a0);var __emscripten_stack_alloc=a0=>(__emscripten_stack_alloc=wasmExports["Ab"])(a0);var _emscripten_stack_get_current=()=>(_emscripten_stack_get_current=wasmExports["Bb"])();var ___cxa_is_pointer_type=a0=>(___cxa_is_pointer_type=wasmExports["Cb"])(a0);var dynCall_iiiji=Module["dynCall_iiiji"]=(a0,a1,a2,a3,a4,a5)=>(dynCall_iiiji=Module["dynCall_iiiji"]=wasmExports["Db"])(a0,a1,a2,a3,a4,a5);var dynCall_jii=Module["dynCall_jii"]=(a0,a1,a2)=>(dynCall_jii=Module["dynCall_jii"]=wasmExports["Eb"])(a0,a1,a2);var dynCall_jiji=Module["dynCall_jiji"]=(a0,a1,a2,a3,a4)=>(dynCall_jiji=Module["dynCall_jiji"]=wasmExports["Fb"])(a0,a1,a2,a3,a4);Module["ccall"]=ccall;Module["cwrap"]=cwrap;var calledRun;dependenciesFulfilled=function runCaller(){if(!calledRun)run();if(!calledRun)dependenciesFulfilled=runCaller};function run(){if(runDependencies>0){return}preRun();if(runDependencies>0){return}function doRun(){if(calledRun)return;calledRun=true;Module["calledRun"]=true;if(ABORT)return;initRuntime();if(Module["onRuntimeInitialized"])Module["onRuntimeInitialized"]();postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(function(){setTimeout(function(){Module["setStatus"]("")},1);doRun()},1)}else{doRun()}}if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].pop()()}}run(); +var Module=typeof Module!="undefined"?Module:{};var ENVIRONMENT_IS_WEB=typeof window=="object";var ENVIRONMENT_IS_WORKER=typeof importScripts=="function";var ENVIRONMENT_IS_NODE=typeof process=="object"&&typeof process.versions=="object"&&typeof process.versions.node=="string";if(ENVIRONMENT_IS_NODE){}var moduleOverrides=Object.assign({},Module);var arguments_=[];var thisProgram="./this.program";var quit_=(status,toThrow)=>{throw toThrow};var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}return scriptDirectory+path}var read_,readAsync,readBinary;if(ENVIRONMENT_IS_NODE){var fs=require("fs");var nodePath=require("path");scriptDirectory=__dirname+"/";read_=(filename,binary)=>{filename=isFileURI(filename)?new URL(filename):nodePath.normalize(filename);return fs.readFileSync(filename,binary?undefined:"utf8")};readBinary=filename=>{var ret=read_(filename,true);if(!ret.buffer){ret=new Uint8Array(ret)}return ret};readAsync=(filename,onload,onerror,binary=true)=>{filename=isFileURI(filename)?new URL(filename):nodePath.normalize(filename);fs.readFile(filename,binary?undefined:"utf8",(err,data)=>{if(err)onerror(err);else onload(binary?data.buffer:data)})};if(!Module["thisProgram"]&&process.argv.length>1){thisProgram=process.argv[1].replace(/\\/g,"/")}arguments_=process.argv.slice(2);if(typeof module!="undefined"){module["exports"]=Module}process.on("uncaughtException",ex=>{if(ex!=="unwind"&&!(ex instanceof ExitStatus)&&!(ex.context instanceof ExitStatus)){throw ex}});quit_=(status,toThrow)=>{process.exitCode=status;throw toThrow}}else if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){if(ENVIRONMENT_IS_WORKER){scriptDirectory=self.location.href}else if(typeof document!="undefined"&&document.currentScript){scriptDirectory=document.currentScript.src}if(scriptDirectory.startsWith("blob:")){scriptDirectory=""}else{scriptDirectory=scriptDirectory.substr(0,scriptDirectory.replace(/[?#].*/,"").lastIndexOf("/")+1)}{read_=url=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.send(null);return xhr.responseText};if(ENVIRONMENT_IS_WORKER){readBinary=url=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)}}readAsync=(url,onload,onerror)=>{if(isFileURI(url)){var xhr=new XMLHttpRequest;xhr.open("GET",url,true);xhr.responseType="arraybuffer";xhr.onload=()=>{if(xhr.status==200||xhr.status==0&&xhr.response){onload(xhr.response);return}onerror()};xhr.onerror=onerror;xhr.send(null);return}fetch(url,{credentials:"same-origin"}).then(response=>{if(response.ok){return response.arrayBuffer()}return Promise.reject(new Error(response.status+" : "+response.url))}).then(onload,onerror)}}}else{}var out=Module["print"]||console.log.bind(console);var err=Module["printErr"]||console.error.bind(console);Object.assign(Module,moduleOverrides);moduleOverrides=null;if(Module["arguments"])arguments_=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"];if(Module["quit"])quit_=Module["quit"];var wasmBinary;if(Module["wasmBinary"])wasmBinary=Module["wasmBinary"];function getSafeHeapType(bytes,isFloat){switch(bytes){case 1:return"i8";case 2:return"i16";case 4:return isFloat?"float":"i32";case 8:return isFloat?"double":"i64";default:abort(`getSafeHeapType() invalid bytes=${bytes}`)}}function SAFE_HEAP_STORE(dest,value,bytes,isFloat){if(dest<=0)abort(`segmentation fault storing ${bytes} bytes to address ${dest}`);if(dest%bytes!==0)abort(`alignment error storing to address ${dest}, which was expected to be aligned to a multiple of ${bytes}`);if(runtimeInitialized){var brk=_sbrk(0);if(dest+bytes>brk)abort(`segmentation fault, exceeded the top of the available dynamic heap when storing ${bytes} bytes to address ${dest}. DYNAMICTOP=${brk}`);if(brk<_emscripten_stack_get_base())abort(`brk >= _emscripten_stack_get_base() (brk=${brk}, _emscripten_stack_get_base()=${_emscripten_stack_get_base()})`);if(brk>wasmMemory.buffer.byteLength)abort(`brk <= wasmMemory.buffer.byteLength (brk=${brk}, wasmMemory.buffer.byteLength=${wasmMemory.buffer.byteLength})`)}setValue_safe(dest,value,getSafeHeapType(bytes,isFloat));return value}function SAFE_HEAP_STORE_D(dest,value,bytes){return SAFE_HEAP_STORE(dest,value,bytes,true)}function SAFE_HEAP_LOAD(dest,bytes,unsigned,isFloat){if(dest<=0)abort(`segmentation fault loading ${bytes} bytes from address ${dest}`);if(dest%bytes!==0)abort(`alignment error loading from address ${dest}, which was expected to be aligned to a multiple of ${bytes}`);if(runtimeInitialized){var brk=_sbrk(0);if(dest+bytes>brk)abort(`segmentation fault, exceeded the top of the available dynamic heap when loading ${bytes} bytes from address ${dest}. DYNAMICTOP=${brk}`);if(brk<_emscripten_stack_get_base())abort(`brk >= _emscripten_stack_get_base() (brk=${brk}, _emscripten_stack_get_base()=${_emscripten_stack_get_base()})`);if(brk>wasmMemory.buffer.byteLength)abort(`brk <= wasmMemory.buffer.byteLength (brk=${brk}, wasmMemory.buffer.byteLength=${wasmMemory.buffer.byteLength})`)}var type=getSafeHeapType(bytes,isFloat);var ret=getValue_safe(dest,type);if(unsigned)ret=unSign(ret,parseInt(type.substr(1),10));return ret}function SAFE_HEAP_LOAD_D(dest,bytes,unsigned){return SAFE_HEAP_LOAD(dest,bytes,unsigned,true)}function segfault(){abort("segmentation fault")}function alignfault(){abort("alignment fault")}var wasmMemory;var ABORT=false;var EXITSTATUS;var HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;function updateMemoryViews(){var b=wasmMemory.buffer;Module["HEAP8"]=HEAP8=new Int8Array(b);Module["HEAP16"]=HEAP16=new Int16Array(b);Module["HEAPU8"]=HEAPU8=new Uint8Array(b);Module["HEAPU16"]=HEAPU16=new Uint16Array(b);Module["HEAP32"]=HEAP32=new Int32Array(b);Module["HEAPU32"]=HEAPU32=new Uint32Array(b);Module["HEAPF32"]=HEAPF32=new Float32Array(b);Module["HEAPF64"]=HEAPF64=new Float64Array(b)}var __ATPRERUN__=[];var __ATINIT__=[];var __ATPOSTRUN__=[];var runtimeInitialized=false;function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(__ATPRERUN__)}function initRuntime(){runtimeInitialized=true;if(!Module["noFSInit"]&&!FS.init.initialized)FS.init();FS.ignorePermissions=false;TTY.init();callRuntimeCallbacks(__ATINIT__)}function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(__ATPOSTRUN__)}function addOnPreRun(cb){__ATPRERUN__.unshift(cb)}function addOnInit(cb){__ATINIT__.unshift(cb)}function addOnPostRun(cb){__ATPOSTRUN__.unshift(cb)}var runDependencies=0;var runDependencyWatcher=null;var dependenciesFulfilled=null;function getUniqueRunDependency(id){return id}function addRunDependency(id){runDependencies++;Module["monitorRunDependencies"]?.(runDependencies)}function removeRunDependency(id){runDependencies--;Module["monitorRunDependencies"]?.(runDependencies);if(runDependencies==0){if(runDependencyWatcher!==null){clearInterval(runDependencyWatcher);runDependencyWatcher=null}if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback()}}}function abort(what){Module["onAbort"]?.(what);what="Aborted("+what+")";err(what);ABORT=true;EXITSTATUS=1;what+=". Build with -sASSERTIONS for more info.";var e=new WebAssembly.RuntimeError(what);throw e}var dataURIPrefix="data:application/octet-stream;base64,";var isDataURI=filename=>filename.startsWith(dataURIPrefix);var isFileURI=filename=>filename.startsWith("file://");function findWasmBinary(){var f="libflutter_soloud_plugin.wasm";if(!isDataURI(f)){return locateFile(f)}return f}var wasmBinaryFile;function getBinarySync(file){if(file==wasmBinaryFile&&wasmBinary){return new Uint8Array(wasmBinary)}if(readBinary){return readBinary(file)}throw"both async and sync fetching of the wasm failed"}function getBinaryPromise(binaryFile){if(!wasmBinary){return new Promise((resolve,reject)=>{readAsync(binaryFile,response=>resolve(new Uint8Array(response)),error=>{try{resolve(getBinarySync(binaryFile))}catch(e){reject(e)}})})}return Promise.resolve().then(()=>getBinarySync(binaryFile))}function instantiateArrayBuffer(binaryFile,imports,receiver){return getBinaryPromise(binaryFile).then(binary=>WebAssembly.instantiate(binary,imports)).then(receiver,reason=>{err(`failed to asynchronously prepare wasm: ${reason}`);abort(reason)})}function instantiateAsync(binary,binaryFile,imports,callback){if(!binary&&typeof WebAssembly.instantiateStreaming=="function"&&!isDataURI(binaryFile)&&!isFileURI(binaryFile)&&!ENVIRONMENT_IS_NODE&&typeof fetch=="function"){return fetch(binaryFile,{credentials:"same-origin"}).then(response=>{var result=WebAssembly.instantiateStreaming(response,imports);return result.then(callback,function(reason){err(`wasm streaming compile failed: ${reason}`);err("falling back to ArrayBuffer instantiation");return instantiateArrayBuffer(binaryFile,imports,callback)})})}return instantiateArrayBuffer(binaryFile,imports,callback)}function getWasmImports(){return{a:wasmImports}}function createWasm(){var info=getWasmImports();function receiveInstance(instance,module){wasmExports=instance.exports;wasmMemory=wasmExports["q"];updateMemoryViews();addOnInit(wasmExports["r"]);removeRunDependency("wasm-instantiate");return wasmExports}addRunDependency("wasm-instantiate");function receiveInstantiationResult(result){receiveInstance(result["instance"])}if(Module["instantiateWasm"]){try{return Module["instantiateWasm"](info,receiveInstance)}catch(e){err(`Module.instantiateWasm callback failed with error: ${e}`);return false}}if(!wasmBinaryFile)wasmBinaryFile=findWasmBinary();instantiateAsync(wasmBinary,wasmBinaryFile,info,receiveInstantiationResult);return{}}var tempDouble;var tempI64;var ASM_CONSTS={67296:($0,$1,$2,$3,$4)=>{if(typeof window==="undefined"||(window.AudioContext||window.webkitAudioContext)===undefined){return 0}if(typeof window.miniaudio==="undefined"){window.miniaudio={referenceCount:0};window.miniaudio.device_type={};window.miniaudio.device_type.playback=$0;window.miniaudio.device_type.capture=$1;window.miniaudio.device_type.duplex=$2;window.miniaudio.device_state={};window.miniaudio.device_state.stopped=$3;window.miniaudio.device_state.started=$4;let miniaudio=window.miniaudio;miniaudio.devices=[];miniaudio.track_device=function(device){for(var iDevice=0;iDevice0){if(miniaudio.devices[miniaudio.devices.length-1]==null){miniaudio.devices.pop()}else{break}}};miniaudio.untrack_device=function(device){for(var iDevice=0;iDevice{_ma_device__on_notification_unlocked(device.pDevice)},error=>{console.error("Failed to resume audiocontext",error)})}}miniaudio.unlock_event_types.map(function(event_type){document.removeEventListener(event_type,miniaudio.unlock,true)})};miniaudio.unlock_event_types.map(function(event_type){document.addEventListener(event_type,miniaudio.unlock,true)})}window.miniaudio.referenceCount+=1;return 1},69474:()=>{if(typeof window.miniaudio!=="undefined"){miniaudio.unlock_event_types.map(function(event_type){document.removeEventListener(event_type,miniaudio.unlock,true)});window.miniaudio.referenceCount-=1;if(window.miniaudio.referenceCount===0){delete window.miniaudio}}},69764:()=>navigator.mediaDevices!==undefined&&navigator.mediaDevices.getUserMedia!==undefined,69868:()=>{try{var temp=new(window.AudioContext||window.webkitAudioContext);var sampleRate=temp.sampleRate;temp.close();return sampleRate}catch(e){return 0}},70039:($0,$1,$2,$3,$4,$5)=>{var deviceType=$0;var channels=$1;var sampleRate=$2;var bufferSize=$3;var pIntermediaryBuffer=$4;var pDevice=$5;if(typeof window.miniaudio==="undefined"){return-1}var device={};var audioContextOptions={};if(deviceType==window.miniaudio.device_type.playback&&sampleRate!=0){audioContextOptions.sampleRate=sampleRate}device.webaudio=new(window.AudioContext||window.webkitAudioContext)(audioContextOptions);device.webaudio.suspend();device.state=window.miniaudio.device_state.stopped;var channelCountIn=0;var channelCountOut=channels;if(deviceType!=window.miniaudio.device_type.playback){channelCountIn=channels}device.scriptNode=device.webaudio.createScriptProcessor(bufferSize,channelCountIn,channelCountOut);device.scriptNode.onaudioprocess=function(e){if(device.intermediaryBufferView==null||device.intermediaryBufferView.length==0){device.intermediaryBufferView=new Float32Array(HEAPF32.buffer,pIntermediaryBuffer,bufferSize*channels)}if(deviceType==window.miniaudio.device_type.capture||deviceType==window.miniaudio.device_type.duplex){for(var iChannel=0;iChannelwindow.miniaudio.get_device_by_index($0).webaudio.sampleRate,72989:$0=>{var device=window.miniaudio.get_device_by_index($0);if(device.scriptNode!==undefined){device.scriptNode.onaudioprocess=function(e){};device.scriptNode.disconnect();device.scriptNode=undefined}if(device.streamNode!==undefined){device.streamNode.disconnect();device.streamNode=undefined}device.webaudio.close();device.webaudio=undefined;device.pDevice=undefined},73389:$0=>{window.miniaudio.untrack_device_by_index($0)},73439:$0=>{var device=window.miniaudio.get_device_by_index($0);device.webaudio.resume();device.state=window.miniaudio.device_state.started},73578:$0=>{var device=window.miniaudio.get_device_by_index($0);device.webaudio.suspend();device.state=window.miniaudio.device_state.stopped},73718:()=>{if(!Module.wasmWorker){var workerUri="assets/packages/flutter_soloud/web/worker.dart.js";console.log("EM_ASM creating web worker!");Module.wasmWorker=new Worker(workerUri)}else{console.log("EM_ASM web worker already created!")}},73966:($0,$1)=>{if(Module.wasmWorker){console.log("EM_ASM posting message "+UTF8ToString($0)+" with value "+$1);Module.wasmWorker.postMessage(JSON.stringify({message:UTF8ToString($0),value:$1}))}else{console.error("Worker not found.")}}};function ExitStatus(status){this.name="ExitStatus";this.message=`Program terminated with exit(${status})`;this.status=status}Module["ExitStatus"]=ExitStatus;var callRuntimeCallbacks=callbacks=>{while(callbacks.length>0){callbacks.shift()(Module)}};Module["callRuntimeCallbacks"]=callRuntimeCallbacks;function getValue(ptr,type="i8"){if(type.endsWith("*"))type="*";switch(type){case"i1":return SAFE_HEAP_LOAD(ptr,1,0);case"i8":return SAFE_HEAP_LOAD(ptr,1,0);case"i16":return SAFE_HEAP_LOAD((ptr>>1)*2,2,0);case"i32":return SAFE_HEAP_LOAD((ptr>>2)*4,4,0);case"i64":abort("to do getValue(i64) use WASM_BIGINT");case"float":return SAFE_HEAP_LOAD_D((ptr>>2)*4,4,0);case"double":return SAFE_HEAP_LOAD_D((ptr>>3)*8,8,0);case"*":return SAFE_HEAP_LOAD((ptr>>2)*4,4,1);default:abort(`invalid type for getValue: ${type}`)}}Module["getValue"]=getValue;function getValue_safe(ptr,type="i8"){if(type.endsWith("*"))type="*";switch(type){case"i1":return HEAP8[ptr];case"i8":return HEAP8[ptr];case"i16":return HEAP16[ptr>>1];case"i32":return HEAP32[ptr>>2];case"i64":abort("to do getValue(i64) use WASM_BIGINT");case"float":return HEAPF32[ptr>>2];case"double":return HEAPF64[ptr>>3];case"*":return HEAPU32[ptr>>2];default:abort(`invalid type for getValue: ${type}`)}}Module["getValue_safe"]=getValue_safe;var noExitRuntime=Module["noExitRuntime"]||true;Module["noExitRuntime"]=noExitRuntime;function setValue(ptr,value,type="i8"){if(type.endsWith("*"))type="*";switch(type){case"i1":SAFE_HEAP_STORE(ptr,value,1);break;case"i8":SAFE_HEAP_STORE(ptr,value,1);break;case"i16":SAFE_HEAP_STORE((ptr>>1)*2,value,2);break;case"i32":SAFE_HEAP_STORE((ptr>>2)*4,value,4);break;case"i64":abort("to do setValue(i64) use WASM_BIGINT");case"float":SAFE_HEAP_STORE_D((ptr>>2)*4,value,4);break;case"double":SAFE_HEAP_STORE_D((ptr>>3)*8,value,8);break;case"*":SAFE_HEAP_STORE((ptr>>2)*4,value,4);break;default:abort(`invalid type for setValue: ${type}`)}}Module["setValue"]=setValue;function setValue_safe(ptr,value,type="i8"){if(type.endsWith("*"))type="*";switch(type){case"i1":HEAP8[ptr]=value;break;case"i8":HEAP8[ptr]=value;break;case"i16":HEAP16[ptr>>1]=value;break;case"i32":HEAP32[ptr>>2]=value;break;case"i64":abort("to do setValue(i64) use WASM_BIGINT");case"float":HEAPF32[ptr>>2]=value;break;case"double":HEAPF64[ptr>>3]=value;break;case"*":HEAPU32[ptr>>2]=value;break;default:abort(`invalid type for setValue: ${type}`)}}Module["setValue_safe"]=setValue_safe;var stackRestore=val=>__emscripten_stack_restore(val);Module["stackRestore"]=stackRestore;var stackSave=()=>_emscripten_stack_get_current();Module["stackSave"]=stackSave;var unSign=(value,bits)=>{if(value>=0){return value}return bits<=32?2*Math.abs(1<{var endIdx=idx+maxBytesToRead;var endPtr=idx;while(heapOrArray[endPtr]&&!(endPtr>=endIdx))++endPtr;if(endPtr-idx>16&&heapOrArray.buffer&&UTF8Decoder){return UTF8Decoder.decode(heapOrArray.subarray(idx,endPtr))}var str="";while(idx>10,56320|ch&1023)}}return str};Module["UTF8ArrayToString"]=UTF8ArrayToString;var UTF8ToString=(ptr,maxBytesToRead)=>ptr?UTF8ArrayToString(HEAPU8,ptr,maxBytesToRead):"";Module["UTF8ToString"]=UTF8ToString;var ___assert_fail=(condition,filename,line,func)=>{abort(`Assertion failed: ${UTF8ToString(condition)}, at: `+[filename?UTF8ToString(filename):"unknown filename",line,func?UTF8ToString(func):"unknown function"])};Module["___assert_fail"]=___assert_fail;class ExceptionInfo{constructor(excPtr){this.excPtr=excPtr;this.ptr=excPtr-24}set_type(type){SAFE_HEAP_STORE((this.ptr+4>>2)*4,type,4)}get_type(){return SAFE_HEAP_LOAD((this.ptr+4>>2)*4,4,1)}set_destructor(destructor){SAFE_HEAP_STORE((this.ptr+8>>2)*4,destructor,4)}get_destructor(){return SAFE_HEAP_LOAD((this.ptr+8>>2)*4,4,1)}set_caught(caught){caught=caught?1:0;SAFE_HEAP_STORE(this.ptr+12,caught,1)}get_caught(){return SAFE_HEAP_LOAD(this.ptr+12,1,0)!=0}set_rethrown(rethrown){rethrown=rethrown?1:0;SAFE_HEAP_STORE(this.ptr+13,rethrown,1)}get_rethrown(){return SAFE_HEAP_LOAD(this.ptr+13,1,0)!=0}init(type,destructor){this.set_adjusted_ptr(0);this.set_type(type);this.set_destructor(destructor)}set_adjusted_ptr(adjustedPtr){SAFE_HEAP_STORE((this.ptr+16>>2)*4,adjustedPtr,4)}get_adjusted_ptr(){return SAFE_HEAP_LOAD((this.ptr+16>>2)*4,4,1)}get_exception_ptr(){var isPointer=___cxa_is_pointer_type(this.get_type());if(isPointer){return SAFE_HEAP_LOAD((this.excPtr>>2)*4,4,1)}var adjusted=this.get_adjusted_ptr();if(adjusted!==0)return adjusted;return this.excPtr}}Module["ExceptionInfo"]=ExceptionInfo;var exceptionLast=0;Module["exceptionLast"]=exceptionLast;var uncaughtExceptionCount=0;Module["uncaughtExceptionCount"]=uncaughtExceptionCount;var ___cxa_throw=(ptr,type,destructor)=>{var info=new ExceptionInfo(ptr);info.init(type,destructor);exceptionLast=ptr;uncaughtExceptionCount++;throw exceptionLast};Module["___cxa_throw"]=___cxa_throw;function syscallGetVarargI(){var ret=SAFE_HEAP_LOAD((+SYSCALLS.varargs>>2)*4,4,0);SYSCALLS.varargs+=4;return ret}Module["syscallGetVarargI"]=syscallGetVarargI;var syscallGetVarargP=syscallGetVarargI;Module["syscallGetVarargP"]=syscallGetVarargP;var PATH={isAbs:path=>path.charAt(0)==="/",splitPath:filename=>{var splitPathRe=/^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/;return splitPathRe.exec(filename).slice(1)},normalizeArray:(parts,allowAboveRoot)=>{var up=0;for(var i=parts.length-1;i>=0;i--){var last=parts[i];if(last==="."){parts.splice(i,1)}else if(last===".."){parts.splice(i,1);up++}else if(up){parts.splice(i,1);up--}}if(allowAboveRoot){for(;up;up--){parts.unshift("..")}}return parts},normalize:path=>{var isAbsolute=PATH.isAbs(path),trailingSlash=path.substr(-1)==="/";path=PATH.normalizeArray(path.split("/").filter(p=>!!p),!isAbsolute).join("/");if(!path&&!isAbsolute){path="."}if(path&&trailingSlash){path+="/"}return(isAbsolute?"/":"")+path},dirname:path=>{var result=PATH.splitPath(path),root=result[0],dir=result[1];if(!root&&!dir){return"."}if(dir){dir=dir.substr(0,dir.length-1)}return root+dir},basename:path=>{if(path==="/")return"/";path=PATH.normalize(path);path=path.replace(/\/$/,"");var lastSlash=path.lastIndexOf("/");if(lastSlash===-1)return path;return path.substr(lastSlash+1)},join:(...paths)=>PATH.normalize(paths.join("/")),join2:(l,r)=>PATH.normalize(l+"/"+r)};Module["PATH"]=PATH;var initRandomFill=()=>{if(typeof crypto=="object"&&typeof crypto["getRandomValues"]=="function"){return view=>crypto.getRandomValues(view)}else if(ENVIRONMENT_IS_NODE){try{var crypto_module=require("crypto");var randomFillSync=crypto_module["randomFillSync"];if(randomFillSync){return view=>crypto_module["randomFillSync"](view)}var randomBytes=crypto_module["randomBytes"];return view=>(view.set(randomBytes(view.byteLength)),view)}catch(e){}}abort("initRandomDevice")};Module["initRandomFill"]=initRandomFill;var randomFill=view=>(randomFill=initRandomFill())(view);Module["randomFill"]=randomFill;var PATH_FS={resolve:(...args)=>{var resolvedPath="",resolvedAbsolute=false;for(var i=args.length-1;i>=-1&&!resolvedAbsolute;i--){var path=i>=0?args[i]:FS.cwd();if(typeof path!="string"){throw new TypeError("Arguments to path.resolve must be strings")}else if(!path){return""}resolvedPath=path+"/"+resolvedPath;resolvedAbsolute=PATH.isAbs(path)}resolvedPath=PATH.normalizeArray(resolvedPath.split("/").filter(p=>!!p),!resolvedAbsolute).join("/");return(resolvedAbsolute?"/":"")+resolvedPath||"."},relative:(from,to)=>{from=PATH_FS.resolve(from).substr(1);to=PATH_FS.resolve(to).substr(1);function trim(arr){var start=0;for(;start=0;end--){if(arr[end]!=="")break}if(start>end)return[];return arr.slice(start,end-start+1)}var fromParts=trim(from.split("/"));var toParts=trim(to.split("/"));var length=Math.min(fromParts.length,toParts.length);var samePartsLength=length;for(var i=0;i{var len=0;for(var i=0;i=55296&&c<=57343){len+=4;++i}else{len+=3}}return len};Module["lengthBytesUTF8"]=lengthBytesUTF8;var stringToUTF8Array=(str,heap,outIdx,maxBytesToWrite)=>{if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i=55296&&u<=57343){var u1=str.charCodeAt(++i);u=65536+((u&1023)<<10)|u1&1023}if(u<=127){if(outIdx>=endIdx)break;heap[outIdx++]=u}else if(u<=2047){if(outIdx+1>=endIdx)break;heap[outIdx++]=192|u>>6;heap[outIdx++]=128|u&63}else if(u<=65535){if(outIdx+2>=endIdx)break;heap[outIdx++]=224|u>>12;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}else{if(outIdx+3>=endIdx)break;heap[outIdx++]=240|u>>18;heap[outIdx++]=128|u>>12&63;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}}heap[outIdx]=0;return outIdx-startIdx};Module["stringToUTF8Array"]=stringToUTF8Array;function intArrayFromString(stringy,dontAddNull,length){var len=length>0?length:lengthBytesUTF8(stringy)+1;var u8array=new Array(len);var numBytesWritten=stringToUTF8Array(stringy,u8array,0,u8array.length);if(dontAddNull)u8array.length=numBytesWritten;return u8array}Module["intArrayFromString"]=intArrayFromString;var FS_stdin_getChar=()=>{if(!FS_stdin_getChar_buffer.length){var result=null;if(ENVIRONMENT_IS_NODE){var BUFSIZE=256;var buf=Buffer.alloc(BUFSIZE);var bytesRead=0;var fd=process.stdin.fd;try{bytesRead=fs.readSync(fd,buf,0,BUFSIZE)}catch(e){if(e.toString().includes("EOF"))bytesRead=0;else throw e}if(bytesRead>0){result=buf.slice(0,bytesRead).toString("utf-8")}}else if(typeof window!="undefined"&&typeof window.prompt=="function"){result=window.prompt("Input: ");if(result!==null){result+="\n"}}else{}if(!result){return null}FS_stdin_getChar_buffer=intArrayFromString(result,true)}return FS_stdin_getChar_buffer.shift()};Module["FS_stdin_getChar"]=FS_stdin_getChar;var TTY={ttys:[],init(){},shutdown(){},register(dev,ops){TTY.ttys[dev]={input:[],output:[],ops:ops};FS.registerDevice(dev,TTY.stream_ops)},stream_ops:{open(stream){var tty=TTY.ttys[stream.node.rdev];if(!tty){throw new FS.ErrnoError(43)}stream.tty=tty;stream.seekable=false},close(stream){stream.tty.ops.fsync(stream.tty)},fsync(stream){stream.tty.ops.fsync(stream.tty)},read(stream,buffer,offset,length,pos){if(!stream.tty||!stream.tty.ops.get_char){throw new FS.ErrnoError(60)}var bytesRead=0;for(var i=0;i0){out(UTF8ArrayToString(tty.output,0));tty.output=[]}},ioctl_tcgets(tty){return{c_iflag:25856,c_oflag:5,c_cflag:191,c_lflag:35387,c_cc:[3,28,127,21,4,0,1,0,17,19,26,0,18,15,23,22,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}},ioctl_tcsets(tty,optional_actions,data){return 0},ioctl_tiocgwinsz(tty){return[24,80]}},default_tty1_ops:{put_char(tty,val){if(val===null||val===10){err(UTF8ArrayToString(tty.output,0));tty.output=[]}else{if(val!=0)tty.output.push(val)}},fsync(tty){if(tty.output&&tty.output.length>0){err(UTF8ArrayToString(tty.output,0));tty.output=[]}}}};Module["TTY"]=TTY;var zeroMemory=(address,size)=>{HEAPU8.fill(0,address,address+size);return address};Module["zeroMemory"]=zeroMemory;var alignMemory=(size,alignment)=>Math.ceil(size/alignment)*alignment;Module["alignMemory"]=alignMemory;var mmapAlloc=size=>{abort()};Module["mmapAlloc"]=mmapAlloc;var MEMFS={ops_table:null,mount(mount){return MEMFS.createNode(null,"/",16384|511,0)},createNode(parent,name,mode,dev){if(FS.isBlkdev(mode)||FS.isFIFO(mode)){throw new FS.ErrnoError(63)}MEMFS.ops_table||={dir:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr,lookup:MEMFS.node_ops.lookup,mknod:MEMFS.node_ops.mknod,rename:MEMFS.node_ops.rename,unlink:MEMFS.node_ops.unlink,rmdir:MEMFS.node_ops.rmdir,readdir:MEMFS.node_ops.readdir,symlink:MEMFS.node_ops.symlink},stream:{llseek:MEMFS.stream_ops.llseek}},file:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr},stream:{llseek:MEMFS.stream_ops.llseek,read:MEMFS.stream_ops.read,write:MEMFS.stream_ops.write,allocate:MEMFS.stream_ops.allocate,mmap:MEMFS.stream_ops.mmap,msync:MEMFS.stream_ops.msync}},link:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr,readlink:MEMFS.node_ops.readlink},stream:{}},chrdev:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr},stream:FS.chrdev_stream_ops}};var node=FS.createNode(parent,name,mode,dev);if(FS.isDir(node.mode)){node.node_ops=MEMFS.ops_table.dir.node;node.stream_ops=MEMFS.ops_table.dir.stream;node.contents={}}else if(FS.isFile(node.mode)){node.node_ops=MEMFS.ops_table.file.node;node.stream_ops=MEMFS.ops_table.file.stream;node.usedBytes=0;node.contents=null}else if(FS.isLink(node.mode)){node.node_ops=MEMFS.ops_table.link.node;node.stream_ops=MEMFS.ops_table.link.stream}else if(FS.isChrdev(node.mode)){node.node_ops=MEMFS.ops_table.chrdev.node;node.stream_ops=MEMFS.ops_table.chrdev.stream}node.timestamp=Date.now();if(parent){parent.contents[name]=node;parent.timestamp=node.timestamp}return node},getFileDataAsTypedArray(node){if(!node.contents)return new Uint8Array(0);if(node.contents.subarray)return node.contents.subarray(0,node.usedBytes);return new Uint8Array(node.contents)},expandFileStorage(node,newCapacity){var prevCapacity=node.contents?node.contents.length:0;if(prevCapacity>=newCapacity)return;var CAPACITY_DOUBLING_MAX=1024*1024;newCapacity=Math.max(newCapacity,prevCapacity*(prevCapacity>>0);if(prevCapacity!=0)newCapacity=Math.max(newCapacity,256);var oldContents=node.contents;node.contents=new Uint8Array(newCapacity);if(node.usedBytes>0)node.contents.set(oldContents.subarray(0,node.usedBytes),0)},resizeFileStorage(node,newSize){if(node.usedBytes==newSize)return;if(newSize==0){node.contents=null;node.usedBytes=0}else{var oldContents=node.contents;node.contents=new Uint8Array(newSize);if(oldContents){node.contents.set(oldContents.subarray(0,Math.min(newSize,node.usedBytes)))}node.usedBytes=newSize}},node_ops:{getattr(node){var attr={};attr.dev=FS.isChrdev(node.mode)?node.id:1;attr.ino=node.id;attr.mode=node.mode;attr.nlink=1;attr.uid=0;attr.gid=0;attr.rdev=node.rdev;if(FS.isDir(node.mode)){attr.size=4096}else if(FS.isFile(node.mode)){attr.size=node.usedBytes}else if(FS.isLink(node.mode)){attr.size=node.link.length}else{attr.size=0}attr.atime=new Date(node.timestamp);attr.mtime=new Date(node.timestamp);attr.ctime=new Date(node.timestamp);attr.blksize=4096;attr.blocks=Math.ceil(attr.size/attr.blksize);return attr},setattr(node,attr){if(attr.mode!==undefined){node.mode=attr.mode}if(attr.timestamp!==undefined){node.timestamp=attr.timestamp}if(attr.size!==undefined){MEMFS.resizeFileStorage(node,attr.size)}},lookup(parent,name){throw FS.genericErrors[44]},mknod(parent,name,mode,dev){return MEMFS.createNode(parent,name,mode,dev)},rename(old_node,new_dir,new_name){if(FS.isDir(old_node.mode)){var new_node;try{new_node=FS.lookupNode(new_dir,new_name)}catch(e){}if(new_node){for(var i in new_node.contents){throw new FS.ErrnoError(55)}}}delete old_node.parent.contents[old_node.name];old_node.parent.timestamp=Date.now();old_node.name=new_name;new_dir.contents[new_name]=old_node;new_dir.timestamp=old_node.parent.timestamp},unlink(parent,name){delete parent.contents[name];parent.timestamp=Date.now()},rmdir(parent,name){var node=FS.lookupNode(parent,name);for(var i in node.contents){throw new FS.ErrnoError(55)}delete parent.contents[name];parent.timestamp=Date.now()},readdir(node){var entries=[".",".."];for(var key of Object.keys(node.contents)){entries.push(key)}return entries},symlink(parent,newname,oldpath){var node=MEMFS.createNode(parent,newname,511|40960,0);node.link=oldpath;return node},readlink(node){if(!FS.isLink(node.mode)){throw new FS.ErrnoError(28)}return node.link}},stream_ops:{read(stream,buffer,offset,length,position){var contents=stream.node.contents;if(position>=stream.node.usedBytes)return 0;var size=Math.min(stream.node.usedBytes-position,length);if(size>8&&contents.subarray){buffer.set(contents.subarray(position,position+size),offset)}else{for(var i=0;i0||position+length{var dep=!noRunDep?getUniqueRunDependency(`al ${url}`):"";readAsync(url,arrayBuffer=>{onload(new Uint8Array(arrayBuffer));if(dep)removeRunDependency(dep)},event=>{if(onerror){onerror()}else{throw`Loading data file "${url}" failed.`}});if(dep)addRunDependency(dep)};Module["asyncLoad"]=asyncLoad;var FS_createDataFile=(parent,name,fileData,canRead,canWrite,canOwn)=>{FS.createDataFile(parent,name,fileData,canRead,canWrite,canOwn)};Module["FS_createDataFile"]=FS_createDataFile;var preloadPlugins=Module["preloadPlugins"]||[];Module["preloadPlugins"]=preloadPlugins;var FS_handledByPreloadPlugin=(byteArray,fullname,finish,onerror)=>{if(typeof Browser!="undefined")Browser.init();var handled=false;preloadPlugins.forEach(plugin=>{if(handled)return;if(plugin["canHandle"](fullname)){plugin["handle"](byteArray,fullname,finish,onerror);handled=true}});return handled};Module["FS_handledByPreloadPlugin"]=FS_handledByPreloadPlugin;var FS_createPreloadedFile=(parent,name,url,canRead,canWrite,onload,onerror,dontCreateFile,canOwn,preFinish)=>{var fullname=name?PATH_FS.resolve(PATH.join2(parent,name)):parent;var dep=getUniqueRunDependency(`cp ${fullname}`);function processData(byteArray){function finish(byteArray){preFinish?.();if(!dontCreateFile){FS_createDataFile(parent,name,byteArray,canRead,canWrite,canOwn)}onload?.();removeRunDependency(dep)}if(FS_handledByPreloadPlugin(byteArray,fullname,finish,()=>{onerror?.();removeRunDependency(dep)})){return}finish(byteArray)}addRunDependency(dep);if(typeof url=="string"){asyncLoad(url,processData,onerror)}else{processData(url)}};Module["FS_createPreloadedFile"]=FS_createPreloadedFile;var FS_modeStringToFlags=str=>{var flagModes={r:0,"r+":2,w:512|64|1,"w+":512|64|2,a:1024|64|1,"a+":1024|64|2};var flags=flagModes[str];if(typeof flags=="undefined"){throw new Error(`Unknown file open mode: ${str}`)}return flags};Module["FS_modeStringToFlags"]=FS_modeStringToFlags;var FS_getMode=(canRead,canWrite)=>{var mode=0;if(canRead)mode|=292|73;if(canWrite)mode|=146;return mode};Module["FS_getMode"]=FS_getMode;var FS={root:null,mounts:[],devices:{},streams:[],nextInode:1,nameTable:null,currentPath:"/",initialized:false,ignorePermissions:true,ErrnoError:class{constructor(errno){this.name="ErrnoError";this.errno=errno}},genericErrors:{},filesystems:null,syncFSRequests:0,FSStream:class{constructor(){this.shared={}}get object(){return this.node}set object(val){this.node=val}get isRead(){return(this.flags&2097155)!==1}get isWrite(){return(this.flags&2097155)!==0}get isAppend(){return this.flags&1024}get flags(){return this.shared.flags}set flags(val){this.shared.flags=val}get position(){return this.shared.position}set position(val){this.shared.position=val}},FSNode:class{constructor(parent,name,mode,rdev){if(!parent){parent=this}this.parent=parent;this.mount=parent.mount;this.mounted=null;this.id=FS.nextInode++;this.name=name;this.mode=mode;this.node_ops={};this.stream_ops={};this.rdev=rdev;this.readMode=292|73;this.writeMode=146}get read(){return(this.mode&this.readMode)===this.readMode}set read(val){val?this.mode|=this.readMode:this.mode&=~this.readMode}get write(){return(this.mode&this.writeMode)===this.writeMode}set write(val){val?this.mode|=this.writeMode:this.mode&=~this.writeMode}get isFolder(){return FS.isDir(this.mode)}get isDevice(){return FS.isChrdev(this.mode)}},lookupPath(path,opts={}){path=PATH_FS.resolve(path);if(!path)return{path:"",node:null};var defaults={follow_mount:true,recurse_count:0};opts=Object.assign(defaults,opts);if(opts.recurse_count>8){throw new FS.ErrnoError(32)}var parts=path.split("/").filter(p=>!!p);var current=FS.root;var current_path="/";for(var i=0;i40){throw new FS.ErrnoError(32)}}}}return{path:current_path,node:current}},getPath(node){var path;while(true){if(FS.isRoot(node)){var mount=node.mount.mountpoint;if(!path)return mount;return mount[mount.length-1]!=="/"?`${mount}/${path}`:mount+path}path=path?`${node.name}/${path}`:node.name;node=node.parent}},hashName(parentid,name){var hash=0;for(var i=0;i>>0)%FS.nameTable.length},hashAddNode(node){var hash=FS.hashName(node.parent.id,node.name);node.name_next=FS.nameTable[hash];FS.nameTable[hash]=node},hashRemoveNode(node){var hash=FS.hashName(node.parent.id,node.name);if(FS.nameTable[hash]===node){FS.nameTable[hash]=node.name_next}else{var current=FS.nameTable[hash];while(current){if(current.name_next===node){current.name_next=node.name_next;break}current=current.name_next}}},lookupNode(parent,name){var errCode=FS.mayLookup(parent);if(errCode){throw new FS.ErrnoError(errCode)}var hash=FS.hashName(parent.id,name);for(var node=FS.nameTable[hash];node;node=node.name_next){var nodeName=node.name;if(node.parent.id===parent.id&&nodeName===name){return node}}return FS.lookup(parent,name)},createNode(parent,name,mode,rdev){var node=new FS.FSNode(parent,name,mode,rdev);FS.hashAddNode(node);return node},destroyNode(node){FS.hashRemoveNode(node)},isRoot(node){return node===node.parent},isMountpoint(node){return!!node.mounted},isFile(mode){return(mode&61440)===32768},isDir(mode){return(mode&61440)===16384},isLink(mode){return(mode&61440)===40960},isChrdev(mode){return(mode&61440)===8192},isBlkdev(mode){return(mode&61440)===24576},isFIFO(mode){return(mode&61440)===4096},isSocket(mode){return(mode&49152)===49152},flagsToPermissionString(flag){var perms=["r","w","rw"][flag&3];if(flag&512){perms+="w"}return perms},nodePermissions(node,perms){if(FS.ignorePermissions){return 0}if(perms.includes("r")&&!(node.mode&292)){return 2}else if(perms.includes("w")&&!(node.mode&146)){return 2}else if(perms.includes("x")&&!(node.mode&73)){return 2}return 0},mayLookup(dir){if(!FS.isDir(dir.mode))return 54;var errCode=FS.nodePermissions(dir,"x");if(errCode)return errCode;if(!dir.node_ops.lookup)return 2;return 0},mayCreate(dir,name){try{var node=FS.lookupNode(dir,name);return 20}catch(e){}return FS.nodePermissions(dir,"wx")},mayDelete(dir,name,isdir){var node;try{node=FS.lookupNode(dir,name)}catch(e){return e.errno}var errCode=FS.nodePermissions(dir,"wx");if(errCode){return errCode}if(isdir){if(!FS.isDir(node.mode)){return 54}if(FS.isRoot(node)||FS.getPath(node)===FS.cwd()){return 10}}else{if(FS.isDir(node.mode)){return 31}}return 0},mayOpen(node,flags){if(!node){return 44}if(FS.isLink(node.mode)){return 32}else if(FS.isDir(node.mode)){if(FS.flagsToPermissionString(flags)!=="r"||flags&512){return 31}}return FS.nodePermissions(node,FS.flagsToPermissionString(flags))},MAX_OPEN_FDS:4096,nextfd(){for(var fd=0;fd<=FS.MAX_OPEN_FDS;fd++){if(!FS.streams[fd]){return fd}}throw new FS.ErrnoError(33)},getStreamChecked(fd){var stream=FS.getStream(fd);if(!stream){throw new FS.ErrnoError(8)}return stream},getStream:fd=>FS.streams[fd],createStream(stream,fd=-1){stream=Object.assign(new FS.FSStream,stream);if(fd==-1){fd=FS.nextfd()}stream.fd=fd;FS.streams[fd]=stream;return stream},closeStream(fd){FS.streams[fd]=null},dupStream(origStream,fd=-1){var stream=FS.createStream(origStream,fd);stream.stream_ops?.dup?.(stream);return stream},chrdev_stream_ops:{open(stream){var device=FS.getDevice(stream.node.rdev);stream.stream_ops=device.stream_ops;stream.stream_ops.open?.(stream)},llseek(){throw new FS.ErrnoError(70)}},major:dev=>dev>>8,minor:dev=>dev&255,makedev:(ma,mi)=>ma<<8|mi,registerDevice(dev,ops){FS.devices[dev]={stream_ops:ops}},getDevice:dev=>FS.devices[dev],getMounts(mount){var mounts=[];var check=[mount];while(check.length){var m=check.pop();mounts.push(m);check.push(...m.mounts)}return mounts},syncfs(populate,callback){if(typeof populate=="function"){callback=populate;populate=false}FS.syncFSRequests++;if(FS.syncFSRequests>1){err(`warning: ${FS.syncFSRequests} FS.syncfs operations in flight at once, probably just doing extra work`)}var mounts=FS.getMounts(FS.root.mount);var completed=0;function doCallback(errCode){FS.syncFSRequests--;return callback(errCode)}function done(errCode){if(errCode){if(!done.errored){done.errored=true;return doCallback(errCode)}return}if(++completed>=mounts.length){doCallback(null)}}mounts.forEach(mount=>{if(!mount.type.syncfs){return done(null)}mount.type.syncfs(mount,populate,done)})},mount(type,opts,mountpoint){var root=mountpoint==="/";var pseudo=!mountpoint;var node;if(root&&FS.root){throw new FS.ErrnoError(10)}else if(!root&&!pseudo){var lookup=FS.lookupPath(mountpoint,{follow_mount:false});mountpoint=lookup.path;node=lookup.node;if(FS.isMountpoint(node)){throw new FS.ErrnoError(10)}if(!FS.isDir(node.mode)){throw new FS.ErrnoError(54)}}var mount={type:type,opts:opts,mountpoint:mountpoint,mounts:[]};var mountRoot=type.mount(mount);mountRoot.mount=mount;mount.root=mountRoot;if(root){FS.root=mountRoot}else if(node){node.mounted=mount;if(node.mount){node.mount.mounts.push(mount)}}return mountRoot},unmount(mountpoint){var lookup=FS.lookupPath(mountpoint,{follow_mount:false});if(!FS.isMountpoint(lookup.node)){throw new FS.ErrnoError(28)}var node=lookup.node;var mount=node.mounted;var mounts=FS.getMounts(mount);Object.keys(FS.nameTable).forEach(hash=>{var current=FS.nameTable[hash];while(current){var next=current.name_next;if(mounts.includes(current.mount)){FS.destroyNode(current)}current=next}});node.mounted=null;var idx=node.mount.mounts.indexOf(mount);node.mount.mounts.splice(idx,1)},lookup(parent,name){return parent.node_ops.lookup(parent,name)},mknod(path,mode,dev){var lookup=FS.lookupPath(path,{parent:true});var parent=lookup.node;var name=PATH.basename(path);if(!name||name==="."||name===".."){throw new FS.ErrnoError(28)}var errCode=FS.mayCreate(parent,name);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.mknod){throw new FS.ErrnoError(63)}return parent.node_ops.mknod(parent,name,mode,dev)},create(path,mode){mode=mode!==undefined?mode:438;mode&=4095;mode|=32768;return FS.mknod(path,mode,0)},mkdir(path,mode){mode=mode!==undefined?mode:511;mode&=511|512;mode|=16384;return FS.mknod(path,mode,0)},mkdirTree(path,mode){var dirs=path.split("/");var d="";for(var i=0;iFS.currentPath,chdir(path){var lookup=FS.lookupPath(path,{follow:true});if(lookup.node===null){throw new FS.ErrnoError(44)}if(!FS.isDir(lookup.node.mode)){throw new FS.ErrnoError(54)}var errCode=FS.nodePermissions(lookup.node,"x");if(errCode){throw new FS.ErrnoError(errCode)}FS.currentPath=lookup.path},createDefaultDirectories(){FS.mkdir("/tmp");FS.mkdir("/home");FS.mkdir("/home/web_user")},createDefaultDevices(){FS.mkdir("/dev");FS.registerDevice(FS.makedev(1,3),{read:()=>0,write:(stream,buffer,offset,length,pos)=>length});FS.mkdev("/dev/null",FS.makedev(1,3));TTY.register(FS.makedev(5,0),TTY.default_tty_ops);TTY.register(FS.makedev(6,0),TTY.default_tty1_ops);FS.mkdev("/dev/tty",FS.makedev(5,0));FS.mkdev("/dev/tty1",FS.makedev(6,0));var randomBuffer=new Uint8Array(1024),randomLeft=0;var randomByte=()=>{if(randomLeft===0){randomLeft=randomFill(randomBuffer).byteLength}return randomBuffer[--randomLeft]};FS.createDevice("/dev","random",randomByte);FS.createDevice("/dev","urandom",randomByte);FS.mkdir("/dev/shm");FS.mkdir("/dev/shm/tmp")},createSpecialDirectories(){FS.mkdir("/proc");var proc_self=FS.mkdir("/proc/self");FS.mkdir("/proc/self/fd");FS.mount({mount(){var node=FS.createNode(proc_self,"fd",16384|511,73);node.node_ops={lookup(parent,name){var fd=+name;var stream=FS.getStreamChecked(fd);var ret={parent:null,mount:{mountpoint:"fake"},node_ops:{readlink:()=>stream.path}};ret.parent=ret;return ret}};return node}},{},"/proc/self/fd")},createStandardStreams(){if(Module["stdin"]){FS.createDevice("/dev","stdin",Module["stdin"])}else{FS.symlink("/dev/tty","/dev/stdin")}if(Module["stdout"]){FS.createDevice("/dev","stdout",null,Module["stdout"])}else{FS.symlink("/dev/tty","/dev/stdout")}if(Module["stderr"]){FS.createDevice("/dev","stderr",null,Module["stderr"])}else{FS.symlink("/dev/tty1","/dev/stderr")}var stdin=FS.open("/dev/stdin",0);var stdout=FS.open("/dev/stdout",1);var stderr=FS.open("/dev/stderr",1)},staticInit(){[44].forEach(code=>{FS.genericErrors[code]=new FS.ErrnoError(code);FS.genericErrors[code].stack=""});FS.nameTable=new Array(4096);FS.mount(MEMFS,{},"/");FS.createDefaultDirectories();FS.createDefaultDevices();FS.createSpecialDirectories();FS.filesystems={MEMFS:MEMFS}},init(input,output,error){FS.init.initialized=true;Module["stdin"]=input||Module["stdin"];Module["stdout"]=output||Module["stdout"];Module["stderr"]=error||Module["stderr"];FS.createStandardStreams()},quit(){FS.init.initialized=false;for(var i=0;ithis.length-1||idx<0){return undefined}var chunkOffset=idx%this.chunkSize;var chunkNum=idx/this.chunkSize|0;return this.getter(chunkNum)[chunkOffset]}setDataGetter(getter){this.getter=getter}cacheLength(){var xhr=new XMLHttpRequest;xhr.open("HEAD",url,false);xhr.send(null);if(!(xhr.status>=200&&xhr.status<300||xhr.status===304))throw new Error("Couldn't load "+url+". Status: "+xhr.status);var datalength=Number(xhr.getResponseHeader("Content-length"));var header;var hasByteServing=(header=xhr.getResponseHeader("Accept-Ranges"))&&header==="bytes";var usesGzip=(header=xhr.getResponseHeader("Content-Encoding"))&&header==="gzip";var chunkSize=1024*1024;if(!hasByteServing)chunkSize=datalength;var doXHR=(from,to)=>{if(from>to)throw new Error("invalid range ("+from+", "+to+") or no bytes requested!");if(to>datalength-1)throw new Error("only "+datalength+" bytes available! programmer error!");var xhr=new XMLHttpRequest;xhr.open("GET",url,false);if(datalength!==chunkSize)xhr.setRequestHeader("Range","bytes="+from+"-"+to);xhr.responseType="arraybuffer";if(xhr.overrideMimeType){xhr.overrideMimeType("text/plain; charset=x-user-defined")}xhr.send(null);if(!(xhr.status>=200&&xhr.status<300||xhr.status===304))throw new Error("Couldn't load "+url+". Status: "+xhr.status);if(xhr.response!==undefined){return new Uint8Array(xhr.response||[])}return intArrayFromString(xhr.responseText||"",true)};var lazyArray=this;lazyArray.setDataGetter(chunkNum=>{var start=chunkNum*chunkSize;var end=(chunkNum+1)*chunkSize-1;end=Math.min(end,datalength-1);if(typeof lazyArray.chunks[chunkNum]=="undefined"){lazyArray.chunks[chunkNum]=doXHR(start,end)}if(typeof lazyArray.chunks[chunkNum]=="undefined")throw new Error("doXHR failed!");return lazyArray.chunks[chunkNum]});if(usesGzip||!datalength){chunkSize=datalength=1;datalength=this.getter(0).length;chunkSize=datalength;out("LazyFiles on gzip forces download of the whole file when length is accessed")}this._length=datalength;this._chunkSize=chunkSize;this.lengthKnown=true}get length(){if(!this.lengthKnown){this.cacheLength()}return this._length}get chunkSize(){if(!this.lengthKnown){this.cacheLength()}return this._chunkSize}}if(typeof XMLHttpRequest!="undefined"){if(!ENVIRONMENT_IS_WORKER)throw"Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc";var lazyArray=new LazyUint8Array;var properties={isDevice:false,contents:lazyArray}}else{var properties={isDevice:false,url:url}}var node=FS.createFile(parent,name,properties,canRead,canWrite);if(properties.contents){node.contents=properties.contents}else if(properties.url){node.contents=null;node.url=properties.url}Object.defineProperties(node,{usedBytes:{get:function(){return this.contents.length}}});var stream_ops={};var keys=Object.keys(node.stream_ops);keys.forEach(key=>{var fn=node.stream_ops[key];stream_ops[key]=(...args)=>{FS.forceLoadFile(node);return fn(...args)}});function writeChunks(stream,buffer,offset,length,position){var contents=stream.node.contents;if(position>=contents.length)return 0;var size=Math.min(contents.length-position,length);if(contents.slice){for(var i=0;i{FS.forceLoadFile(node);return writeChunks(stream,buffer,offset,length,position)};stream_ops.mmap=(stream,length,position,prot,flags)=>{FS.forceLoadFile(node);var ptr=mmapAlloc(length);if(!ptr){throw new FS.ErrnoError(48)}writeChunks(stream,HEAP8,ptr,length,position);return{ptr:ptr,allocated:true}};node.stream_ops=stream_ops;return node}};Module["FS"]=FS;var SYSCALLS={DEFAULT_POLLMASK:5,calculateAt(dirfd,path,allowEmpty){if(PATH.isAbs(path)){return path}var dir;if(dirfd===-100){dir=FS.cwd()}else{var dirstream=SYSCALLS.getStreamFromFD(dirfd);dir=dirstream.path}if(path.length==0){if(!allowEmpty){throw new FS.ErrnoError(44)}return dir}return PATH.join2(dir,path)},doStat(func,path,buf){var stat=func(path);SAFE_HEAP_STORE((buf>>2)*4,stat.dev,4);SAFE_HEAP_STORE((buf+4>>2)*4,stat.mode,4);SAFE_HEAP_STORE((buf+8>>2)*4,stat.nlink,4);SAFE_HEAP_STORE((buf+12>>2)*4,stat.uid,4);SAFE_HEAP_STORE((buf+16>>2)*4,stat.gid,4);SAFE_HEAP_STORE((buf+20>>2)*4,stat.rdev,4);tempI64=[stat.size>>>0,(tempDouble=stat.size,+Math.abs(tempDouble)>=1?tempDouble>0?+Math.floor(tempDouble/4294967296)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],SAFE_HEAP_STORE((buf+24>>2)*4,tempI64[0],4),SAFE_HEAP_STORE((buf+28>>2)*4,tempI64[1],4);SAFE_HEAP_STORE((buf+32>>2)*4,4096,4);SAFE_HEAP_STORE((buf+36>>2)*4,stat.blocks,4);var atime=stat.atime.getTime();var mtime=stat.mtime.getTime();var ctime=stat.ctime.getTime();tempI64=[Math.floor(atime/1e3)>>>0,(tempDouble=Math.floor(atime/1e3),+Math.abs(tempDouble)>=1?tempDouble>0?+Math.floor(tempDouble/4294967296)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],SAFE_HEAP_STORE((buf+40>>2)*4,tempI64[0],4),SAFE_HEAP_STORE((buf+44>>2)*4,tempI64[1],4);SAFE_HEAP_STORE((buf+48>>2)*4,atime%1e3*1e3,4);tempI64=[Math.floor(mtime/1e3)>>>0,(tempDouble=Math.floor(mtime/1e3),+Math.abs(tempDouble)>=1?tempDouble>0?+Math.floor(tempDouble/4294967296)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],SAFE_HEAP_STORE((buf+56>>2)*4,tempI64[0],4),SAFE_HEAP_STORE((buf+60>>2)*4,tempI64[1],4);SAFE_HEAP_STORE((buf+64>>2)*4,mtime%1e3*1e3,4);tempI64=[Math.floor(ctime/1e3)>>>0,(tempDouble=Math.floor(ctime/1e3),+Math.abs(tempDouble)>=1?tempDouble>0?+Math.floor(tempDouble/4294967296)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],SAFE_HEAP_STORE((buf+72>>2)*4,tempI64[0],4),SAFE_HEAP_STORE((buf+76>>2)*4,tempI64[1],4);SAFE_HEAP_STORE((buf+80>>2)*4,ctime%1e3*1e3,4);tempI64=[stat.ino>>>0,(tempDouble=stat.ino,+Math.abs(tempDouble)>=1?tempDouble>0?+Math.floor(tempDouble/4294967296)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],SAFE_HEAP_STORE((buf+88>>2)*4,tempI64[0],4),SAFE_HEAP_STORE((buf+92>>2)*4,tempI64[1],4);return 0},doMsync(addr,stream,len,flags,offset){if(!FS.isFile(stream.node.mode)){throw new FS.ErrnoError(43)}if(flags&2){return 0}var buffer=HEAPU8.slice(addr,addr+len);FS.msync(stream,buffer,offset,len,flags)},getStreamFromFD(fd){var stream=FS.getStreamChecked(fd);return stream},varargs:undefined,getStr(ptr){var ret=UTF8ToString(ptr);return ret}};Module["SYSCALLS"]=SYSCALLS;function ___syscall_fcntl64(fd,cmd,varargs){SYSCALLS.varargs=varargs;try{var stream=SYSCALLS.getStreamFromFD(fd);switch(cmd){case 0:{var arg=syscallGetVarargI();if(arg<0){return-28}while(FS.streams[arg]){arg++}var newStream;newStream=FS.dupStream(stream,arg);return newStream.fd}case 1:case 2:return 0;case 3:return stream.flags;case 4:{var arg=syscallGetVarargI();stream.flags|=arg;return 0}case 12:{var arg=syscallGetVarargP();var offset=0;SAFE_HEAP_STORE((arg+offset>>1)*2,2,2);return 0}case 13:case 14:return 0}return-28}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}Module["___syscall_fcntl64"]=___syscall_fcntl64;function ___syscall_ioctl(fd,op,varargs){SYSCALLS.varargs=varargs;try{var stream=SYSCALLS.getStreamFromFD(fd);switch(op){case 21509:{if(!stream.tty)return-59;return 0}case 21505:{if(!stream.tty)return-59;if(stream.tty.ops.ioctl_tcgets){var termios=stream.tty.ops.ioctl_tcgets(stream);var argp=syscallGetVarargP();SAFE_HEAP_STORE((argp>>2)*4,termios.c_iflag||0,4);SAFE_HEAP_STORE((argp+4>>2)*4,termios.c_oflag||0,4);SAFE_HEAP_STORE((argp+8>>2)*4,termios.c_cflag||0,4);SAFE_HEAP_STORE((argp+12>>2)*4,termios.c_lflag||0,4);for(var i=0;i<32;i++){SAFE_HEAP_STORE(argp+i+17,termios.c_cc[i]||0,1)}return 0}return 0}case 21510:case 21511:case 21512:{if(!stream.tty)return-59;return 0}case 21506:case 21507:case 21508:{if(!stream.tty)return-59;if(stream.tty.ops.ioctl_tcsets){var argp=syscallGetVarargP();var c_iflag=SAFE_HEAP_LOAD((argp>>2)*4,4,0);var c_oflag=SAFE_HEAP_LOAD((argp+4>>2)*4,4,0);var c_cflag=SAFE_HEAP_LOAD((argp+8>>2)*4,4,0);var c_lflag=SAFE_HEAP_LOAD((argp+12>>2)*4,4,0);var c_cc=[];for(var i=0;i<32;i++){c_cc.push(SAFE_HEAP_LOAD(argp+i+17,1,0))}return stream.tty.ops.ioctl_tcsets(stream.tty,op,{c_iflag:c_iflag,c_oflag:c_oflag,c_cflag:c_cflag,c_lflag:c_lflag,c_cc:c_cc})}return 0}case 21519:{if(!stream.tty)return-59;var argp=syscallGetVarargP();SAFE_HEAP_STORE((argp>>2)*4,0,4);return 0}case 21520:{if(!stream.tty)return-59;return-28}case 21531:{var argp=syscallGetVarargP();return FS.ioctl(stream,op,argp)}case 21523:{if(!stream.tty)return-59;if(stream.tty.ops.ioctl_tiocgwinsz){var winsize=stream.tty.ops.ioctl_tiocgwinsz(stream.tty);var argp=syscallGetVarargP();SAFE_HEAP_STORE((argp>>1)*2,winsize[0],2);SAFE_HEAP_STORE((argp+2>>1)*2,winsize[1],2)}return 0}case 21524:{if(!stream.tty)return-59;return 0}case 21515:{if(!stream.tty)return-59;return 0}default:return-28}}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}Module["___syscall_ioctl"]=___syscall_ioctl;function ___syscall_openat(dirfd,path,flags,varargs){SYSCALLS.varargs=varargs;try{path=SYSCALLS.getStr(path);path=SYSCALLS.calculateAt(dirfd,path);var mode=varargs?syscallGetVarargI():0;return FS.open(path,flags,mode).fd}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}Module["___syscall_openat"]=___syscall_openat;var __abort_js=()=>{abort("")};Module["__abort_js"]=__abort_js;var __emscripten_memcpy_js=(dest,src,num)=>HEAPU8.copyWithin(dest,src,src+num);Module["__emscripten_memcpy_js"]=__emscripten_memcpy_js;var readEmAsmArgsArray=[];Module["readEmAsmArgsArray"]=readEmAsmArgsArray;var readEmAsmArgs=(sigPtr,buf)=>{readEmAsmArgsArray.length=0;var ch;while(ch=SAFE_HEAP_LOAD(sigPtr++,1,1)){var wide=ch!=105;wide&=ch!=112;buf+=wide&&buf%8?4:0;readEmAsmArgsArray.push(ch==112?SAFE_HEAP_LOAD((buf>>2)*4,4,1):ch==105?SAFE_HEAP_LOAD((buf>>2)*4,4,0):SAFE_HEAP_LOAD_D((buf>>3)*8,8,0));buf+=wide?8:4}return readEmAsmArgsArray};Module["readEmAsmArgs"]=readEmAsmArgs;var runEmAsmFunction=(code,sigPtr,argbuf)=>{var args=readEmAsmArgs(sigPtr,argbuf);return ASM_CONSTS[code](...args)};Module["runEmAsmFunction"]=runEmAsmFunction;var _emscripten_asm_const_int=(code,sigPtr,argbuf)=>runEmAsmFunction(code,sigPtr,argbuf);Module["_emscripten_asm_const_int"]=_emscripten_asm_const_int;var getHeapMax=()=>2147483648;Module["getHeapMax"]=getHeapMax;var growMemory=size=>{var b=wasmMemory.buffer;var pages=(size-b.byteLength+65535)/65536;try{wasmMemory.grow(pages);updateMemoryViews();return 1}catch(e){}};Module["growMemory"]=growMemory;var _emscripten_resize_heap=requestedSize=>{var oldSize=HEAPU8.length;requestedSize>>>=0;var maxHeapSize=getHeapMax();if(requestedSize>maxHeapSize){return false}var alignUp=(x,multiple)=>x+(multiple-x%multiple)%multiple;for(var cutDown=1;cutDown<=4;cutDown*=2){var overGrownHeapSize=oldSize*(1+.2/cutDown);overGrownHeapSize=Math.min(overGrownHeapSize,requestedSize+100663296);var newSize=Math.min(maxHeapSize,alignUp(Math.max(requestedSize,overGrownHeapSize),65536));var replacement=growMemory(newSize);if(replacement){return true}}return false};Module["_emscripten_resize_heap"]=_emscripten_resize_heap;function _fd_close(fd){try{var stream=SYSCALLS.getStreamFromFD(fd);FS.close(stream);return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}Module["_fd_close"]=_fd_close;var doReadv=(stream,iov,iovcnt,offset)=>{var ret=0;for(var i=0;i>2)*4,4,1);var len=SAFE_HEAP_LOAD((iov+4>>2)*4,4,1);iov+=8;var curr=FS.read(stream,HEAP8,ptr,len,offset);if(curr<0)return-1;ret+=curr;if(curr>2)*4,num,4);return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}Module["_fd_read"]=_fd_read;var convertI32PairToI53Checked=(lo,hi)=>hi+2097152>>>0<4194305-!!lo?(lo>>>0)+hi*4294967296:NaN;Module["convertI32PairToI53Checked"]=convertI32PairToI53Checked;function _fd_seek(fd,offset_low,offset_high,whence,newOffset){var offset=convertI32PairToI53Checked(offset_low,offset_high);try{if(isNaN(offset))return 61;var stream=SYSCALLS.getStreamFromFD(fd);FS.llseek(stream,offset,whence);tempI64=[stream.position>>>0,(tempDouble=stream.position,+Math.abs(tempDouble)>=1?tempDouble>0?+Math.floor(tempDouble/4294967296)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],SAFE_HEAP_STORE((newOffset>>2)*4,tempI64[0],4),SAFE_HEAP_STORE((newOffset+4>>2)*4,tempI64[1],4);if(stream.getdents&&offset===0&&whence===0)stream.getdents=null;return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}Module["_fd_seek"]=_fd_seek;var doWritev=(stream,iov,iovcnt,offset)=>{var ret=0;for(var i=0;i>2)*4,4,1);var len=SAFE_HEAP_LOAD((iov+4>>2)*4,4,1);iov+=8;var curr=FS.write(stream,HEAP8,ptr,len,offset);if(curr<0)return-1;ret+=curr;if(typeof offset!="undefined"){offset+=curr}}return ret};Module["doWritev"]=doWritev;function _fd_write(fd,iov,iovcnt,pnum){try{var stream=SYSCALLS.getStreamFromFD(fd);var num=doWritev(stream,iov,iovcnt);SAFE_HEAP_STORE((pnum>>2)*4,num,4);return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}Module["_fd_write"]=_fd_write;var _getentropy=(buffer,size)=>{randomFill(HEAPU8.subarray(buffer,buffer+size));return 0};Module["_getentropy"]=_getentropy;var getCFunc=ident=>{var func=Module["_"+ident];return func};Module["getCFunc"]=getCFunc;var writeArrayToMemory=(array,buffer)=>{HEAP8.set(array,buffer)};Module["writeArrayToMemory"]=writeArrayToMemory;var stringToUTF8=(str,outPtr,maxBytesToWrite)=>stringToUTF8Array(str,HEAPU8,outPtr,maxBytesToWrite);Module["stringToUTF8"]=stringToUTF8;var stackAlloc=sz=>__emscripten_stack_alloc(sz);Module["stackAlloc"]=stackAlloc;var stringToUTF8OnStack=str=>{var size=lengthBytesUTF8(str)+1;var ret=stackAlloc(size);stringToUTF8(str,ret,size);return ret};Module["stringToUTF8OnStack"]=stringToUTF8OnStack;var ccall=(ident,returnType,argTypes,args,opts)=>{var toC={string:str=>{var ret=0;if(str!==null&&str!==undefined&&str!==0){ret=stringToUTF8OnStack(str)}return ret},array:arr=>{var ret=stackAlloc(arr.length);writeArrayToMemory(arr,ret);return ret}};function convertReturnValue(ret){if(returnType==="string"){return UTF8ToString(ret)}if(returnType==="boolean")return Boolean(ret);return ret}var func=getCFunc(ident);var cArgs=[];var stack=0;if(args){for(var i=0;i{var numericArgs=!argTypes||argTypes.every(type=>type==="number"||type==="boolean");var numericRet=returnType!=="string";if(numericRet&&numericArgs&&!opts){return getCFunc(ident)}return(...args)=>ccall(ident,returnType,argTypes,args,opts)};Module["cwrap"]=cwrap;FS.createPreloadedFile=FS_createPreloadedFile;FS.staticInit();var wasmImports={a:___assert_fail,h:___cxa_throw,g:___syscall_fcntl64,j:___syscall_ioctl,k:___syscall_openat,o:__abort_js,l:__emscripten_memcpy_js,c:alignfault,d:_emscripten_asm_const_int,p:_emscripten_resize_heap,e:_fd_close,i:_fd_read,m:_fd_seek,f:_fd_write,n:_getentropy,b:segfault};var wasmExports=createWasm();var ___wasm_call_ctors=()=>(___wasm_call_ctors=wasmExports["r"])();var _malloc=Module["_malloc"]=a0=>(_malloc=Module["_malloc"]=wasmExports["t"])(a0);var _free=Module["_free"]=a0=>(_free=Module["_free"]=wasmExports["u"])(a0);var _ma_device__on_notification_unlocked=Module["_ma_device__on_notification_unlocked"]=a0=>(_ma_device__on_notification_unlocked=Module["_ma_device__on_notification_unlocked"]=wasmExports["v"])(a0);var _ma_malloc_emscripten=Module["_ma_malloc_emscripten"]=(a0,a1)=>(_ma_malloc_emscripten=Module["_ma_malloc_emscripten"]=wasmExports["w"])(a0,a1);var _ma_free_emscripten=Module["_ma_free_emscripten"]=(a0,a1)=>(_ma_free_emscripten=Module["_ma_free_emscripten"]=wasmExports["x"])(a0,a1);var _ma_device_process_pcm_frames_capture__webaudio=Module["_ma_device_process_pcm_frames_capture__webaudio"]=(a0,a1,a2)=>(_ma_device_process_pcm_frames_capture__webaudio=Module["_ma_device_process_pcm_frames_capture__webaudio"]=wasmExports["y"])(a0,a1,a2);var _ma_device_process_pcm_frames_playback__webaudio=Module["_ma_device_process_pcm_frames_playback__webaudio"]=(a0,a1,a2)=>(_ma_device_process_pcm_frames_playback__webaudio=Module["_ma_device_process_pcm_frames_playback__webaudio"]=wasmExports["z"])(a0,a1,a2);var _createWorkerInWasm=Module["_createWorkerInWasm"]=()=>(_createWorkerInWasm=Module["_createWorkerInWasm"]=wasmExports["A"])();var _sendToWorker=Module["_sendToWorker"]=(a0,a1)=>(_sendToWorker=Module["_sendToWorker"]=wasmExports["B"])(a0,a1);var _nativeFree=Module["_nativeFree"]=a0=>(_nativeFree=Module["_nativeFree"]=wasmExports["C"])(a0);var _voiceEndedCallback=Module["_voiceEndedCallback"]=a0=>(_voiceEndedCallback=Module["_voiceEndedCallback"]=wasmExports["D"])(a0);var _setDartEventCallback=Module["_setDartEventCallback"]=(a0,a1,a2)=>(_setDartEventCallback=Module["_setDartEventCallback"]=wasmExports["E"])(a0,a1,a2);var _initEngine=Module["_initEngine"]=(a0,a1,a2)=>(_initEngine=Module["_initEngine"]=wasmExports["F"])(a0,a1,a2);var _dispose=Module["_dispose"]=()=>(_dispose=Module["_dispose"]=wasmExports["G"])();var _isInited=Module["_isInited"]=()=>(_isInited=Module["_isInited"]=wasmExports["H"])();var _loadFile=Module["_loadFile"]=(a0,a1)=>(_loadFile=Module["_loadFile"]=wasmExports["I"])(a0,a1);var _loadMem=Module["_loadMem"]=(a0,a1,a2,a3,a4)=>(_loadMem=Module["_loadMem"]=wasmExports["J"])(a0,a1,a2,a3,a4);var _loadWaveform=Module["_loadWaveform"]=(a0,a1,a2,a3,a4)=>(_loadWaveform=Module["_loadWaveform"]=wasmExports["K"])(a0,a1,a2,a3,a4);var _setWaveformScale=Module["_setWaveformScale"]=(a0,a1)=>(_setWaveformScale=Module["_setWaveformScale"]=wasmExports["L"])(a0,a1);var _setWaveformDetune=Module["_setWaveformDetune"]=(a0,a1)=>(_setWaveformDetune=Module["_setWaveformDetune"]=wasmExports["M"])(a0,a1);var _setWaveformFreq=Module["_setWaveformFreq"]=(a0,a1)=>(_setWaveformFreq=Module["_setWaveformFreq"]=wasmExports["N"])(a0,a1);var _setSuperWave=Module["_setSuperWave"]=(a0,a1)=>(_setSuperWave=Module["_setSuperWave"]=wasmExports["O"])(a0,a1);var _setWaveform=Module["_setWaveform"]=(a0,a1)=>(_setWaveform=Module["_setWaveform"]=wasmExports["P"])(a0,a1);var _speechText=Module["_speechText"]=(a0,a1)=>(_speechText=Module["_speechText"]=wasmExports["Q"])(a0,a1);var _pauseSwitch=Module["_pauseSwitch"]=a0=>(_pauseSwitch=Module["_pauseSwitch"]=wasmExports["R"])(a0);var _setPause=Module["_setPause"]=(a0,a1)=>(_setPause=Module["_setPause"]=wasmExports["S"])(a0,a1);var _getPause=Module["_getPause"]=a0=>(_getPause=Module["_getPause"]=wasmExports["T"])(a0);var _setRelativePlaySpeed=Module["_setRelativePlaySpeed"]=(a0,a1)=>(_setRelativePlaySpeed=Module["_setRelativePlaySpeed"]=wasmExports["U"])(a0,a1);var _getRelativePlaySpeed=Module["_getRelativePlaySpeed"]=a0=>(_getRelativePlaySpeed=Module["_getRelativePlaySpeed"]=wasmExports["V"])(a0);var _play=Module["_play"]=(a0,a1,a2,a3,a4,a5,a6)=>(_play=Module["_play"]=wasmExports["W"])(a0,a1,a2,a3,a4,a5,a6);var _stop=Module["_stop"]=a0=>(_stop=Module["_stop"]=wasmExports["X"])(a0);var _disposeSound=Module["_disposeSound"]=a0=>(_disposeSound=Module["_disposeSound"]=wasmExports["Y"])(a0);var _disposeAllSound=Module["_disposeAllSound"]=()=>(_disposeAllSound=Module["_disposeAllSound"]=wasmExports["Z"])();var _getLooping=Module["_getLooping"]=a0=>(_getLooping=Module["_getLooping"]=wasmExports["_"])(a0);var _setLooping=Module["_setLooping"]=(a0,a1)=>(_setLooping=Module["_setLooping"]=wasmExports["$"])(a0,a1);var _getLoopPoint=Module["_getLoopPoint"]=a0=>(_getLoopPoint=Module["_getLoopPoint"]=wasmExports["aa"])(a0);var _setLoopPoint=Module["_setLoopPoint"]=(a0,a1)=>(_setLoopPoint=Module["_setLoopPoint"]=wasmExports["ba"])(a0,a1);var _setVisualizationEnabled=Module["_setVisualizationEnabled"]=a0=>(_setVisualizationEnabled=Module["_setVisualizationEnabled"]=wasmExports["ca"])(a0);var _getVisualizationEnabled=Module["_getVisualizationEnabled"]=()=>(_getVisualizationEnabled=Module["_getVisualizationEnabled"]=wasmExports["da"])();var _getFft=Module["_getFft"]=a0=>(_getFft=Module["_getFft"]=wasmExports["ea"])(a0);var _getWave=Module["_getWave"]=a0=>(_getWave=Module["_getWave"]=wasmExports["fa"])(a0);var _setFftSmoothing=Module["_setFftSmoothing"]=a0=>(_setFftSmoothing=Module["_setFftSmoothing"]=wasmExports["ga"])(a0);var _getAudioTexture=Module["_getAudioTexture"]=a0=>(_getAudioTexture=Module["_getAudioTexture"]=wasmExports["ha"])(a0);var _getAudioTexture2D=Module["_getAudioTexture2D"]=a0=>(_getAudioTexture2D=Module["_getAudioTexture2D"]=wasmExports["ia"])(a0);var _getTextureValue=Module["_getTextureValue"]=(a0,a1)=>(_getTextureValue=Module["_getTextureValue"]=wasmExports["ja"])(a0,a1);var _getLength=Module["_getLength"]=a0=>(_getLength=Module["_getLength"]=wasmExports["ka"])(a0);var _seek=Module["_seek"]=(a0,a1)=>(_seek=Module["_seek"]=wasmExports["la"])(a0,a1);var _getPosition=Module["_getPosition"]=a0=>(_getPosition=Module["_getPosition"]=wasmExports["ma"])(a0);var _getGlobalVolume=Module["_getGlobalVolume"]=()=>(_getGlobalVolume=Module["_getGlobalVolume"]=wasmExports["na"])();var _setGlobalVolume=Module["_setGlobalVolume"]=a0=>(_setGlobalVolume=Module["_setGlobalVolume"]=wasmExports["oa"])(a0);var _getVolume=Module["_getVolume"]=a0=>(_getVolume=Module["_getVolume"]=wasmExports["pa"])(a0);var _setVolume=Module["_setVolume"]=(a0,a1)=>(_setVolume=Module["_setVolume"]=wasmExports["qa"])(a0,a1);var _getPan=Module["_getPan"]=a0=>(_getPan=Module["_getPan"]=wasmExports["ra"])(a0);var _setPan=Module["_setPan"]=(a0,a1)=>(_setPan=Module["_setPan"]=wasmExports["sa"])(a0,a1);var _setPanAbsolute=Module["_setPanAbsolute"]=(a0,a1,a2)=>(_setPanAbsolute=Module["_setPanAbsolute"]=wasmExports["ta"])(a0,a1,a2);var _getIsValidVoiceHandle=Module["_getIsValidVoiceHandle"]=a0=>(_getIsValidVoiceHandle=Module["_getIsValidVoiceHandle"]=wasmExports["ua"])(a0);var _getActiveVoiceCount=Module["_getActiveVoiceCount"]=()=>(_getActiveVoiceCount=Module["_getActiveVoiceCount"]=wasmExports["va"])();var _countAudioSource=Module["_countAudioSource"]=a0=>(_countAudioSource=Module["_countAudioSource"]=wasmExports["wa"])(a0);var _getVoiceCount=Module["_getVoiceCount"]=()=>(_getVoiceCount=Module["_getVoiceCount"]=wasmExports["xa"])();var _getProtectVoice=Module["_getProtectVoice"]=a0=>(_getProtectVoice=Module["_getProtectVoice"]=wasmExports["ya"])(a0);var _setProtectVoice=Module["_setProtectVoice"]=(a0,a1)=>(_setProtectVoice=Module["_setProtectVoice"]=wasmExports["za"])(a0,a1);var _getMaxActiveVoiceCount=Module["_getMaxActiveVoiceCount"]=()=>(_getMaxActiveVoiceCount=Module["_getMaxActiveVoiceCount"]=wasmExports["Aa"])();var _setMaxActiveVoiceCount=Module["_setMaxActiveVoiceCount"]=a0=>(_setMaxActiveVoiceCount=Module["_setMaxActiveVoiceCount"]=wasmExports["Ba"])(a0);var _createVoiceGroup=Module["_createVoiceGroup"]=()=>(_createVoiceGroup=Module["_createVoiceGroup"]=wasmExports["Ca"])();var _destroyVoiceGroup=Module["_destroyVoiceGroup"]=a0=>(_destroyVoiceGroup=Module["_destroyVoiceGroup"]=wasmExports["Da"])(a0);var _addVoiceToGroup=Module["_addVoiceToGroup"]=(a0,a1)=>(_addVoiceToGroup=Module["_addVoiceToGroup"]=wasmExports["Ea"])(a0,a1);var _isVoiceGroup=Module["_isVoiceGroup"]=a0=>(_isVoiceGroup=Module["_isVoiceGroup"]=wasmExports["Fa"])(a0);var _isVoiceGroupEmpty=Module["_isVoiceGroupEmpty"]=a0=>(_isVoiceGroupEmpty=Module["_isVoiceGroupEmpty"]=wasmExports["Ga"])(a0);var _fadeGlobalVolume=Module["_fadeGlobalVolume"]=(a0,a1)=>(_fadeGlobalVolume=Module["_fadeGlobalVolume"]=wasmExports["Ha"])(a0,a1);var _fadeVolume=Module["_fadeVolume"]=(a0,a1,a2)=>(_fadeVolume=Module["_fadeVolume"]=wasmExports["Ia"])(a0,a1,a2);var _fadePan=Module["_fadePan"]=(a0,a1,a2)=>(_fadePan=Module["_fadePan"]=wasmExports["Ja"])(a0,a1,a2);var _fadeRelativePlaySpeed=Module["_fadeRelativePlaySpeed"]=(a0,a1,a2)=>(_fadeRelativePlaySpeed=Module["_fadeRelativePlaySpeed"]=wasmExports["Ka"])(a0,a1,a2);var _schedulePause=Module["_schedulePause"]=(a0,a1)=>(_schedulePause=Module["_schedulePause"]=wasmExports["La"])(a0,a1);var _scheduleStop=Module["_scheduleStop"]=(a0,a1)=>(_scheduleStop=Module["_scheduleStop"]=wasmExports["Ma"])(a0,a1);var _oscillateVolume=Module["_oscillateVolume"]=(a0,a1,a2,a3)=>(_oscillateVolume=Module["_oscillateVolume"]=wasmExports["Na"])(a0,a1,a2,a3);var _oscillatePan=Module["_oscillatePan"]=(a0,a1,a2,a3)=>(_oscillatePan=Module["_oscillatePan"]=wasmExports["Oa"])(a0,a1,a2,a3);var _oscillateRelativePlaySpeed=Module["_oscillateRelativePlaySpeed"]=(a0,a1,a2,a3)=>(_oscillateRelativePlaySpeed=Module["_oscillateRelativePlaySpeed"]=wasmExports["Pa"])(a0,a1,a2,a3);var _oscillateGlobalVolume=Module["_oscillateGlobalVolume"]=(a0,a1,a2)=>(_oscillateGlobalVolume=Module["_oscillateGlobalVolume"]=wasmExports["Qa"])(a0,a1,a2);var _isFilterActive=Module["_isFilterActive"]=(a0,a1,a2)=>(_isFilterActive=Module["_isFilterActive"]=wasmExports["Ra"])(a0,a1,a2);var _getFilterParamNames=Module["_getFilterParamNames"]=(a0,a1,a2)=>(_getFilterParamNames=Module["_getFilterParamNames"]=wasmExports["Sa"])(a0,a1,a2);var _addFilter=Module["_addFilter"]=(a0,a1)=>(_addFilter=Module["_addFilter"]=wasmExports["Ta"])(a0,a1);var _removeFilter=Module["_removeFilter"]=(a0,a1)=>(_removeFilter=Module["_removeFilter"]=wasmExports["Ua"])(a0,a1);var _setFilterParams=Module["_setFilterParams"]=(a0,a1,a2,a3)=>(_setFilterParams=Module["_setFilterParams"]=wasmExports["Va"])(a0,a1,a2,a3);var _getFilterParams=Module["_getFilterParams"]=(a0,a1,a2,a3)=>(_getFilterParams=Module["_getFilterParams"]=wasmExports["Wa"])(a0,a1,a2,a3);var _fadeFilterParameter=Module["_fadeFilterParameter"]=(a0,a1,a2,a3,a4)=>(_fadeFilterParameter=Module["_fadeFilterParameter"]=wasmExports["Xa"])(a0,a1,a2,a3,a4);var _oscillateFilterParameter=Module["_oscillateFilterParameter"]=(a0,a1,a2,a3,a4,a5)=>(_oscillateFilterParameter=Module["_oscillateFilterParameter"]=wasmExports["Ya"])(a0,a1,a2,a3,a4,a5);var _play3d=Module["_play3d"]=(a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11)=>(_play3d=Module["_play3d"]=wasmExports["Za"])(a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11);var _set3dSoundSpeed=Module["_set3dSoundSpeed"]=a0=>(_set3dSoundSpeed=Module["_set3dSoundSpeed"]=wasmExports["_a"])(a0);var _get3dSoundSpeed=Module["_get3dSoundSpeed"]=()=>(_get3dSoundSpeed=Module["_get3dSoundSpeed"]=wasmExports["$a"])();var _set3dListenerParameters=Module["_set3dListenerParameters"]=(a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11)=>(_set3dListenerParameters=Module["_set3dListenerParameters"]=wasmExports["ab"])(a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11);var _set3dListenerPosition=Module["_set3dListenerPosition"]=(a0,a1,a2)=>(_set3dListenerPosition=Module["_set3dListenerPosition"]=wasmExports["bb"])(a0,a1,a2);var _set3dListenerAt=Module["_set3dListenerAt"]=(a0,a1,a2)=>(_set3dListenerAt=Module["_set3dListenerAt"]=wasmExports["cb"])(a0,a1,a2);var _set3dListenerUp=Module["_set3dListenerUp"]=(a0,a1,a2)=>(_set3dListenerUp=Module["_set3dListenerUp"]=wasmExports["db"])(a0,a1,a2);var _set3dListenerVelocity=Module["_set3dListenerVelocity"]=(a0,a1,a2)=>(_set3dListenerVelocity=Module["_set3dListenerVelocity"]=wasmExports["eb"])(a0,a1,a2);var _set3dSourceParameters=Module["_set3dSourceParameters"]=(a0,a1,a2,a3,a4,a5,a6)=>(_set3dSourceParameters=Module["_set3dSourceParameters"]=wasmExports["fb"])(a0,a1,a2,a3,a4,a5,a6);var _set3dSourcePosition=Module["_set3dSourcePosition"]=(a0,a1,a2,a3)=>(_set3dSourcePosition=Module["_set3dSourcePosition"]=wasmExports["gb"])(a0,a1,a2,a3);var _set3dSourceVelocity=Module["_set3dSourceVelocity"]=(a0,a1,a2,a3)=>(_set3dSourceVelocity=Module["_set3dSourceVelocity"]=wasmExports["hb"])(a0,a1,a2,a3);var _set3dSourceMinMaxDistance=Module["_set3dSourceMinMaxDistance"]=(a0,a1,a2)=>(_set3dSourceMinMaxDistance=Module["_set3dSourceMinMaxDistance"]=wasmExports["ib"])(a0,a1,a2);var _set3dSourceAttenuation=Module["_set3dSourceAttenuation"]=(a0,a1,a2)=>(_set3dSourceAttenuation=Module["_set3dSourceAttenuation"]=wasmExports["jb"])(a0,a1,a2);var _set3dSourceDopplerFactor=Module["_set3dSourceDopplerFactor"]=(a0,a1)=>(_set3dSourceDopplerFactor=Module["_set3dSourceDopplerFactor"]=wasmExports["kb"])(a0,a1);var _listCaptureDevices=Module["_listCaptureDevices"]=(a0,a1,a2)=>(_listCaptureDevices=Module["_listCaptureDevices"]=wasmExports["lb"])(a0,a1,a2);var _freeListCaptureDevices=Module["_freeListCaptureDevices"]=(a0,a1,a2)=>(_freeListCaptureDevices=Module["_freeListCaptureDevices"]=wasmExports["mb"])(a0,a1,a2);var _initCapture=Module["_initCapture"]=a0=>(_initCapture=Module["_initCapture"]=wasmExports["nb"])(a0);var _disposeCapture=Module["_disposeCapture"]=()=>(_disposeCapture=Module["_disposeCapture"]=wasmExports["ob"])();var _isCaptureInited=Module["_isCaptureInited"]=()=>(_isCaptureInited=Module["_isCaptureInited"]=wasmExports["pb"])();var _isCaptureStarted=Module["_isCaptureStarted"]=()=>(_isCaptureStarted=Module["_isCaptureStarted"]=wasmExports["qb"])();var _startCapture=Module["_startCapture"]=()=>(_startCapture=Module["_startCapture"]=wasmExports["rb"])();var _stopCapture=Module["_stopCapture"]=()=>(_stopCapture=Module["_stopCapture"]=wasmExports["sb"])();var _getCaptureFft=Module["_getCaptureFft"]=a0=>(_getCaptureFft=Module["_getCaptureFft"]=wasmExports["tb"])(a0);var _getCaptureWave=Module["_getCaptureWave"]=a0=>(_getCaptureWave=Module["_getCaptureWave"]=wasmExports["ub"])(a0);var _getCaptureTexture=Module["_getCaptureTexture"]=a0=>(_getCaptureTexture=Module["_getCaptureTexture"]=wasmExports["vb"])(a0);var _getCaptureAudioTexture2D=Module["_getCaptureAudioTexture2D"]=a0=>(_getCaptureAudioTexture2D=Module["_getCaptureAudioTexture2D"]=wasmExports["wb"])(a0);var _getCaptureTextureValue=Module["_getCaptureTextureValue"]=(a0,a1)=>(_getCaptureTextureValue=Module["_getCaptureTextureValue"]=wasmExports["xb"])(a0,a1);var _setCaptureFftSmoothing=Module["_setCaptureFftSmoothing"]=a0=>(_setCaptureFftSmoothing=Module["_setCaptureFftSmoothing"]=wasmExports["yb"])(a0);var _emscripten_get_sbrk_ptr=()=>(_emscripten_get_sbrk_ptr=wasmExports["zb"])();var _sbrk=a0=>(_sbrk=wasmExports["Ab"])(a0);var _emscripten_stack_get_base=()=>(_emscripten_stack_get_base=wasmExports["Bb"])();var __emscripten_stack_restore=a0=>(__emscripten_stack_restore=wasmExports["Cb"])(a0);var __emscripten_stack_alloc=a0=>(__emscripten_stack_alloc=wasmExports["Db"])(a0);var _emscripten_stack_get_current=()=>(_emscripten_stack_get_current=wasmExports["Eb"])();var ___cxa_is_pointer_type=a0=>(___cxa_is_pointer_type=wasmExports["Fb"])(a0);var dynCall_iiiji=Module["dynCall_iiiji"]=(a0,a1,a2,a3,a4,a5)=>(dynCall_iiiji=Module["dynCall_iiiji"]=wasmExports["Gb"])(a0,a1,a2,a3,a4,a5);var dynCall_jii=Module["dynCall_jii"]=(a0,a1,a2)=>(dynCall_jii=Module["dynCall_jii"]=wasmExports["Hb"])(a0,a1,a2);var dynCall_jiji=Module["dynCall_jiji"]=(a0,a1,a2,a3,a4)=>(dynCall_jiji=Module["dynCall_jiji"]=wasmExports["Ib"])(a0,a1,a2,a3,a4);Module["ccall"]=ccall;Module["cwrap"]=cwrap;var calledRun;dependenciesFulfilled=function runCaller(){if(!calledRun)run();if(!calledRun)dependenciesFulfilled=runCaller};function run(){if(runDependencies>0){return}preRun();if(runDependencies>0){return}function doRun(){if(calledRun)return;calledRun=true;Module["calledRun"]=true;if(ABORT)return;initRuntime();if(Module["onRuntimeInitialized"])Module["onRuntimeInitialized"]();postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(function(){setTimeout(function(){Module["setStatus"]("")},1);doRun()},1)}else{doRun()}}if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].pop()()}}run(); diff --git a/web/libflutter_soloud_plugin.wasm b/web/libflutter_soloud_plugin.wasm index 241c5957fd8e08e60f5ae1e6058626cbcb434213..94b36545e34b1e11a5d6f9bbb6d6cb4cd0c85dda 100755 GIT binary patch delta 66351 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�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 From 9bab461dd5a842ee8a38c8d687880502c88cfa65 Mon Sep 17 00:00:00 2001 From: Marco Bavagnoli Date: Mon, 15 Jul 2024 10:37:14 +0200 Subject: [PATCH 3/6] init() docs --- example/test/widget_test.dart | 30 --------------------------- lib/src/bindings/bindings_player.dart | 12 +++++++---- lib/src/soloud.dart | 18 ++++++++++++++-- 3 files changed, 24 insertions(+), 36 deletions(-) delete mode 100644 example/test/widget_test.dart diff --git a/example/test/widget_test.dart b/example/test/widget_test.dart deleted file mode 100644 index 092d222..0000000 --- a/example/test/widget_test.dart +++ /dev/null @@ -1,30 +0,0 @@ -// This is a basic Flutter widget test. -// -// To perform an interaction with a widget in your test, use the WidgetTester -// utility in the flutter_test package. For example, you can send tap and scroll -// gestures. You can also use WidgetTester to find child widgets in the widget -// tree, read text, and verify that the values of widget properties are correct. - -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; - -import 'package:example/main.dart'; - -void main() { - testWidgets('Counter increments smoke test', (WidgetTester tester) async { - // Build our app and trigger a frame. - await tester.pumpWidget(const MyApp()); - - // Verify that our counter starts at 0. - expect(find.text('0'), findsOneWidget); - expect(find.text('1'), findsNothing); - - // Tap the '+' icon and trigger a frame. - await tester.tap(find.byIcon(Icons.add)); - await tester.pump(); - - // Verify that our counter has incremented. - expect(find.text('0'), findsNothing); - expect(find.text('1'), findsOneWidget); - }); -} diff --git a/lib/src/bindings/bindings_player.dart b/lib/src/bindings/bindings_player.dart index bf23d1c..8a7867d 100644 --- a/lib/src/bindings/bindings_player.dart +++ b/lib/src/bindings/bindings_player.dart @@ -54,9 +54,9 @@ abstract class FlutterSoLoud { /// /// [sampleRate] the sample rate. Usually is 22050, 44100 (CD quality) /// or 48000. - /// [bufferSize] the audio buffer size. Usually is 2048, but can be also - /// 512 when low latency is needed for example in games. - /// [channels] 1=mono, 2=stereo, 4=quad, 6=5.1, 8=7.1. + /// [bufferSize] the audio buffer size. Usually is 2048, but can be also be + /// lowered if less latency is needed. + /// [channels] mono, stereo, quad, 5.1, 7.1. /// /// Returns [PlayerErrors.noError] if success. @mustBeOverridden @@ -574,7 +574,11 @@ abstract class FlutterSoLoud { /// Set fader to oscillate the panning at specified frequency. @mustBeOverridden PlayerErrors oscillatePan( - SoundHandle handle, double from, double to, Duration time); + SoundHandle handle, + double from, + double to, + Duration time, + ); /// Set fader to oscillate the relative play speed at specified frequency. @mustBeOverridden diff --git a/lib/src/soloud.dart b/lib/src/soloud.dart index 2d274c8..af2f1de 100644 --- a/lib/src/soloud.dart +++ b/lib/src/soloud.dart @@ -203,6 +203,20 @@ interface class SoLoud { /// that play sounds from assets or from the file system, this is probably /// unnecessary, as the amount of data will be finite. /// The default is `false`. + /// [sampleRate] The sample rate represents the number of samples used, per + /// second. Typical sample rates are 8000Hz, 22050Hz, 44100Hz and 48000Hz. + /// Higher the sample rates mean clearer sound, but also bigger files, more + /// memory and higher processing power requirements. + /// [bufferSize] Audio latency generally means the time it takes from + /// triggering a sound to the sound actually coming out of the speakers. + /// The smaller the latency, the better. + /// Unfortunately, there's always some latency. The primary source of + /// latency (that a programmer can have any control over) is the size of + /// audio buffer. Generally speaking, the smaller the buffer, the lower the + /// latency, but at the same time, the smaller the buffer, the more likely the + /// system hits buffer underruns (ie, the play head marches on but there's no + /// data ready to be played) and the sound breaks down horribly. + /// [channels] mono, stereo, quad, 5.1, 7.1. Future init({ // TODO(filip): remove deprecation? @Deprecated('timeout is not used anymore.') @@ -850,7 +864,7 @@ interface class SoLoud { source.soundEventsController.add(( event: SoundEventType.soundDisposed, sound: source, - handle: SoundHandle.error(), + handle: const SoundHandle.error(), )); } await source.soundEventsController.close(); @@ -879,7 +893,7 @@ interface class SoLoud { sound.soundEventsController.add(( event: SoundEventType.soundDisposed, sound: sound, - handle: SoundHandle.error(), + handle: const SoundHandle.error(), )); // TODO(filiph): Close these in parallel using `Future.wait()` await sound.soundEventsController.close(); From da01da2f23a148cf5fa852d791e391b3e88b4207 Mon Sep 17 00:00:00 2001 From: Marco Bavagnoli Date: Mon, 15 Jul 2024 11:01:37 +0200 Subject: [PATCH 4/6] CHANGELOG, README update --- CHANGELOG.md | 4 ++++ README.md | 1 + pubspec.yaml | 4 ++-- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 05d1f4f..9354698 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ - added `loadMem()` to read the give audio file bytes buffer (not RAW data). Useful for the Web platform. - fixed `getFilterParamNames()`. - added `AudioData` class to manage audio samples. +- added player initialization parameters: sample rate, buffer size, number of channels (mono, stereo, quad, 5.1, 7.1). +- added voice groups. +- it's now possible to set filters not only globally, but also to single audio sources. +- fade and oscillate filter parameters. ### 2.0.2 (23 May 2024) - Fixed wrong exception raised by `setVolume()` when a handle is no more valid. diff --git a/README.md b/README.md index acb9b37..b3b9f90 100755 --- a/README.md +++ b/README.md @@ -19,6 +19,7 @@ A low-level audio plugin for Flutter. * 3D positional audio, including Doppler effect * Support for MP3, WAV, OGG, and FLAC * Audio effects such as echo, reverb, filter, equalizer +* Web support is still under testing. Your feedback is greatly appreciated. ## Overview diff --git a/pubspec.yaml b/pubspec.yaml index 4dc57d1..5254cda 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -3,7 +3,7 @@ description: >- A low-level audio plugin for Flutter, mainly meant for games and immersive apps. Based on the SoLoud (C++) audio engine. -version: 2.0.2 +version: 2.1.0 issue_tracker: https://github.com/alnitak/flutter_soloud/issues homepage: https://github.com/alnitak/flutter_soloud maintainer: Marco Bavagnoli (@lildeimos) @@ -62,7 +62,7 @@ flutter: assets: # These assets are only needed for the web platform. # Waiting for https://github.com/flutter/flutter/issues/65065 and - # https://github.com/flutter/flutter/issues/8230 to be addressed + # https://github.com/flutter/flutter/issues/8230 to be addressed. # to make a conditional build. - web/worker.dart.js - web/libflutter_soloud_plugin.js From bcfd8daff0a6b0d97cae1daa21eb6f70d9d7a81f Mon Sep 17 00:00:00 2001 From: Marco Bavagnoli Date: Mon, 15 Jul 2024 11:21:09 +0200 Subject: [PATCH 5/6] comment removed --- lib/src/soloud.dart | 5 ----- 1 file changed, 5 deletions(-) diff --git a/lib/src/soloud.dart b/lib/src/soloud.dart index af2f1de..bd1b67c 100644 --- a/lib/src/soloud.dart +++ b/lib/src/soloud.dart @@ -226,11 +226,6 @@ interface class SoLoud { int bufferSize = 2048, Channels channels = Channels.channelStereo, }) async { - /// Defaults are: - /// Miniaudio audio backend - /// sample rate 44100 - /// buffer 2048 - // TODO(marco): add engine initialization parameters _log.finest('init() called'); // if `!isInitialized` but the engine is initialized in native, therefore From 7823ec1d7ed62b658379f649c62a5f4fdfce4196 Mon Sep 17 00:00:00 2001 From: Marco Bavagnoli Date: Mon, 22 Jul 2024 17:08:47 +0200 Subject: [PATCH 6/6] review fixes --- example/pubspec.lock | 2 +- example/tests/tests.dart | 56 ++++++++++---------- lib/src/audio_source.dart | 9 ++-- lib/src/bindings/bindings_player_ffi.dart | 9 +--- lib/src/bindings/bindings_player_web.dart | 11 +--- lib/src/enums.dart | 15 ++++-- lib/src/exceptions/exceptions_from_dart.dart | 11 ++++ lib/src/soloud.dart | 3 +- src/bindings.cpp | 14 ++--- src/player.cpp | 24 ++++----- 10 files changed, 78 insertions(+), 76 deletions(-) diff --git a/example/pubspec.lock b/example/pubspec.lock index 5ca483a..a86bf3a 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -92,7 +92,7 @@ packages: path: ".." relative: true source: path - version: "2.0.2" + version: "2.1.0" flutter_test: dependency: "direct dev" description: flutter diff --git a/example/tests/tests.dart b/example/tests/tests.dart index 50298e9..3a3b86a 100644 --- a/example/tests/tests.dart +++ b/example/tests/tests.dart @@ -1,4 +1,3 @@ -// ignore_for_file: public_member_api_docs, sort_constructors_first import 'dart:async'; import 'dart:ui'; @@ -34,10 +33,11 @@ void main() { ); } -class Test { - Test({ +class _Test { + _Test({ required this.name, required this.callback, + // ignore: unused_element this.status = TestStatus.none, }); final String name; @@ -54,7 +54,7 @@ class MyHomePage extends StatefulWidget { class _MyHomePageState extends State { final output = StringBuffer(); - final List tests = []; + final List<_Test> tests = []; final textEditingController = TextEditingController(); @override @@ -63,21 +63,21 @@ class _MyHomePageState extends State { /// Add all testing functions. tests.addAll([ - Test(name: 'testProtectVoice', callback: testProtectVoice), - Test( + _Test(name: 'testProtectVoice', callback: testProtectVoice), + _Test( name: 'testAllInstancesFinished', callback: testAllInstancesFinished, ), - Test(name: 'testCreateNotes', callback: testCreateNotes), - Test(name: 'testPlaySeekPause', callback: testPlaySeekPause), - Test(name: 'testPan', callback: testPan), - Test(name: 'testHandles', callback: testHandles), - Test(name: 'loopingTests', callback: loopingTests), - Test(name: 'testSynchronousDeinit', callback: testSynchronousDeinit), - Test(name: 'testAsynchronousDeinit', callback: testAsynchronousDeinit), - Test(name: 'testVoiceGroups', callback: testVoiceGroups), - Test(name: 'testSoundFilters', callback: testSoundFilters), - Test(name: 'testGlobalFilters', callback: testGlobalFilters), + _Test(name: 'testCreateNotes', callback: testCreateNotes), + _Test(name: 'testPlaySeekPause', callback: testPlaySeekPause), + _Test(name: 'testPan', callback: testPan), + _Test(name: 'testHandles', callback: testHandles), + _Test(name: 'loopingTests', callback: loopingTests), + _Test(name: 'testSynchronousDeinit', callback: testSynchronousDeinit), + _Test(name: 'testAsynchronousDeinit', callback: testAsynchronousDeinit), + _Test(name: 'testVoiceGroups', callback: testVoiceGroups), + _Test(name: 'testSoundFilters', callback: testSoundFilters), + _Test(name: 'testGlobalFilters', callback: testGlobalFilters), ]); } @@ -280,7 +280,7 @@ Future testProtectVoice() async { /// Test allInstancesFinished stream Future testAllInstancesFinished() async { - final ret = StringBuffer(); + final strBuf = StringBuffer(); await initialize(); await SoLoud.instance.disposeAllSources(); @@ -299,14 +299,14 @@ Future testAllInstancesFinished() async { var songDisposed = false; unawaited( explosion.allInstancesFinished.first.then((_) async { - ret.write('All instances of explosion finished.\n'); + strBuf.write('All instances of explosion finished.\n'); await SoLoud.instance.disposeSource(explosion); explosionDisposed = true; }), ); unawaited( song.allInstancesFinished.first.then((_) async { - ret.write('All instances of song finished.\n'); + strBuf.write('All instances of song finished.\n'); await SoLoud.instance.disposeSource(song); songDisposed = true; }), @@ -328,7 +328,7 @@ Future testAllInstancesFinished() async { deinit(); - return ret; + return strBuf; } /// Test waveform @@ -696,7 +696,7 @@ Future testVoiceGroups() async { /// Test sound filters. Future testSoundFilters() async { - final ret = StringBuffer(); + final strBuf = StringBuffer(); await initialize(); final sound = await SoLoud.instance.loadAsset( @@ -722,7 +722,7 @@ Future testSoundFilters() async { final g = sound.getFilterParameter(h1, FilterType.echoFilter, attributeId); assert( closeTo(g, value, 0.001), - 'Setting attribute to $value but optained $g', + 'Setting attribute to $value but obtained $g', ); sound.oscillateFilterParameter( @@ -745,7 +745,7 @@ Future testSoundFilters() async { try { sound.removeFilter(FilterType.echoFilter); } on Exception catch (e) { - ret + strBuf ..write(e) ..writeln(); } @@ -755,12 +755,12 @@ Future testSoundFilters() async { ); deinit(); - return ret; + return strBuf; } /// Test global filters. Future testGlobalFilters() async { - final ret = StringBuffer(); + final strBuf = StringBuffer(); await initialize(); late final AudioSource sound; @@ -770,7 +770,7 @@ Future testGlobalFilters() async { mode: LoadMode.disk, ); } on Exception catch (e) { - ret + strBuf ..write(e) ..writeln(); } @@ -816,7 +816,7 @@ Future testGlobalFilters() async { try { SoLoud.instance.removeGlobalFilter(FilterType.echoFilter); } on Exception catch (e) { - ret + strBuf ..write(e) ..writeln(); } @@ -826,5 +826,5 @@ Future testGlobalFilters() async { ); deinit(); - return ret; + return strBuf; } diff --git a/lib/src/audio_source.dart b/lib/src/audio_source.dart index 2596a79..d276815 100644 --- a/lib/src/audio_source.dart +++ b/lib/src/audio_source.dart @@ -46,9 +46,9 @@ enum SoundEventType { /// You can listen to the broadcast stream of [soundEvents]. /// /// Every [AudioSource] can have their own filters. You can add, remove and -/// query wether if filter is active respectively with `addFilter()`, -/// `removeGlobalFilter()` and `isFilterActive()`. The effect must be -/// set before playing the sound and parameters can be changed after having +/// query wether a filter is active with `addFilter()`, +/// `removeGlobalFilter()` and `isFilterActive()`, respectively. The effect must +/// be set before playing the sound and parameters can be changed after having /// the a voice handle: /// ``` /// final sound = await SoLoud.instance.loadAsset('...'); @@ -179,8 +179,7 @@ class AudioSource { /// Specify the [attributeId] of the parameter (which you can learn from /// [SoLoud.getFilterParamNames]), and its new [value]. /// - /// [handle] the handle to set the filter to. If equal to 0, the filter is - /// applyed to the global filter. + /// [handle] the handle to set the filter to. /// [filterType] filter to modify a param. /// Returns [PlayerErrors.noError] if no errors. PlayerErrors setFilterParameter( diff --git a/lib/src/bindings/bindings_player_ffi.dart b/lib/src/bindings/bindings_player_ffi.dart index 7d80cf4..9cc28b8 100644 --- a/lib/src/bindings/bindings_player_ffi.dart +++ b/lib/src/bindings/bindings_player_ffi.dart @@ -164,17 +164,10 @@ class FlutterSoLoudFfi extends FlutterSoLoud { @override PlayerErrors initEngine(int sampleRate, int bufferSize, Channels channels) { - final channelsInternal = switch (channels) { - Channels.channelMono => 1, - Channels.channelStereo => 2, - Channels.channelQuad => 4, - Channels.channel51 => 6, - Channels.channel71 => 8, - }; final ret = _initEngine( sampleRate, bufferSize, - channelsInternal, + channels.count, ); return PlayerErrors.values[ret]; } diff --git a/lib/src/bindings/bindings_player_web.dart b/lib/src/bindings/bindings_player_web.dart index 98c1208..681d088 100644 --- a/lib/src/bindings/bindings_player_web.dart +++ b/lib/src/bindings/bindings_player_web.dart @@ -73,14 +73,7 @@ class FlutterSoLoudWeb extends FlutterSoLoud { @override PlayerErrors initEngine(int sampleRate, int bufferSize, Channels channels) { - final channelsInternal = switch (channels) { - Channels.channelMono => 1, - Channels.channelStereo => 2, - Channels.channelQuad => 4, - Channels.channel51 => 6, - Channels.channel71 => 8, - }; - final ret = wasmInitEngine(sampleRate, bufferSize, channelsInternal); + final ret = wasmInitEngine(sampleRate, bufferSize, channels.count); return PlayerErrors.values[ret]; } @@ -436,7 +429,7 @@ class FlutterSoLoudWeb extends FlutterSoLoud { SoundHandle createVoiceGroup() { /// The group handle returned has the sign bit flagged. Since on the web /// the int is a signed 32 bit, a negative number will be returned. - /// Fixing by ORing the result.< + /// Fixing by ORing the result. final ret = wasmCreateVoiceGroup() | 0xfffff000; return SoundHandle(ret > 0 ? ret : -1); } diff --git a/lib/src/enums.dart b/lib/src/enums.dart index 95be45b..9389212 100644 --- a/lib/src/enums.dart +++ b/lib/src/enums.dart @@ -237,17 +237,22 @@ enum PlayerStateNotification { /// The channels to be used while initializing the player. enum Channels { /// One channel. - channelMono, + mono(1), /// Two channels. - channelStereo, + stereo(2), /// Four channels. - channelQuad, + quad(4), /// Six channels. - channel51, + surround51(6), /// Eight channels. - channel71, + dolby71(8); + + const Channels(this.count); + + /// The channels count. + final int count; } diff --git a/lib/src/exceptions/exceptions_from_dart.dart b/lib/src/exceptions/exceptions_from_dart.dart index 884c91f..cb2b30f 100644 --- a/lib/src/exceptions/exceptions_from_dart.dart +++ b/lib/src/exceptions/exceptions_from_dart.dart @@ -87,3 +87,14 @@ class SoLoudSoundHashNotFoundDartException extends SoLoudDartException { String get description => 'The sound with specified hash is not found ' '(on the Dart side).'; } + +/// An exception that is thrown when SoLoud (Dart) tries to create a voice +/// group but something goes wrong. +class SoLoudCreateVoiceGroupDartException extends SoLoudDartException { + /// Creates a new [SoLoudCreateVoiceGroupDartException]. + const SoLoudCreateVoiceGroupDartException([super.message]); + + @override + String get description => 'SoLoud.createVoiceGroup() was not able to create ' + ' a new voice group.'; +} diff --git a/lib/src/soloud.dart b/lib/src/soloud.dart index bd1b67c..24162c5 100644 --- a/lib/src/soloud.dart +++ b/lib/src/soloud.dart @@ -224,7 +224,7 @@ interface class SoLoud { bool automaticCleanup = false, int sampleRate = 44100, int bufferSize = 2048, - Channels channels = Channels.channelStereo, + Channels channels = Channels.stereo, }) async { _log.finest('init() called'); @@ -1357,6 +1357,7 @@ interface class SoLoud { /// Used to create a new voice group. Returns 0 if not successful. SoundHandle createVoiceGroup() { final ret = _controller.soLoudFFI.createVoiceGroup(); + if (ret.isError) throw const SoLoudCreateVoiceGroupDartException(); return ret; } diff --git a/src/bindings.cpp b/src/bindings.cpp index ea780da..e1733cd 100644 --- a/src/bindings.cpp +++ b/src/bindings.cpp @@ -1052,7 +1052,7 @@ extern "C" else { auto const s = player.get()->findByHash(soundHash); - if (s == 0) + if (s == nullptr) return soundHashNotFound; *index = s->filters->isFilterActive(filterType); } @@ -1096,7 +1096,7 @@ extern "C" return player.get()->mFilters.addFilter(filterType); auto const s = player.get()->findByHash(soundHash); - if (s == 0) + if (s == nullptr) return soundHashNotFound; return s->filters->addFilter(filterType); @@ -1119,7 +1119,7 @@ extern "C" else { auto const s = player.get()->findByHash(soundHash); - if (s == 0) + if (s == nullptr) return soundHashNotFound; if (!s->filters->removeFilter(filterType)) return filterNotFound; @@ -1145,7 +1145,7 @@ extern "C" else { auto const &s = player.get()->findByHandle(handle); - if (s == 0) + if (s == nullptr) { return soundHandleNotFound; } @@ -1181,7 +1181,7 @@ extern "C" else { auto const &s = player.get()->findByHandle(handle); - if (s == 0) + if (s == nullptr) return soundHandleNotFound; else { @@ -1216,7 +1216,7 @@ extern "C" else { auto const &s = player.get()->findByHandle(handle); - if (s == 0) + if (s == nullptr) { return soundHandleNotFound; } @@ -1252,7 +1252,7 @@ extern "C" else { auto const &s = player.get()->findByHandle(handle); - if (s == 0) + if (s == nullptr) { return soundHandleNotFound; } diff --git a/src/player.cpp b/src/player.cpp index 2879b5d..4cc6d1b 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -128,7 +128,7 @@ PlayerErrors Player::loadFile( /// check if the sound has been already loaded auto const s = findByHash(newHash); - if (s != 0) + if (s != nullptr) { *hash = newHash; return fileAlreadyLoaded; @@ -179,7 +179,7 @@ PlayerErrors Player::loadMem( /// check if the sound has been already loaded auto const s = findByHash(newHash); - if (s != 0) + if (s != nullptr) { hash = newHash; return fileAlreadyLoaded; @@ -245,7 +245,7 @@ void Player::setWaveformScale(unsigned int soundHash, float newScale) { auto const s = findByHash(soundHash); - if (s == 0 || s->soundType != TYPE_SYNTH) + if (s == nullptr || s->soundType != TYPE_SYNTH) return; static_cast(s->sound.get())->setScale(newScale); @@ -255,7 +255,7 @@ void Player::setWaveformDetune(unsigned int soundHash, float newDetune) { auto const s = findByHash(soundHash); - if (s == 0 || s->soundType != TYPE_SYNTH) + if (s == nullptr || s->soundType != TYPE_SYNTH) return; static_cast(s->sound.get())->setDetune(newDetune); @@ -265,7 +265,7 @@ void Player::setWaveform(unsigned int soundHash, int newWaveform) { auto const s = findByHash(soundHash); - if (s == 0 || s->soundType != TYPE_SYNTH) + if (s == nullptr || s->soundType != TYPE_SYNTH) return; static_cast(s->sound.get())->setWaveform((SoLoud::Soloud::WAVEFORM)newWaveform); @@ -275,7 +275,7 @@ void Player::setWaveformFreq(unsigned int soundHash, float newFreq) { auto const s = findByHash(soundHash); - if (s == 0 || s->soundType != TYPE_SYNTH) + if (s == nullptr || s->soundType != TYPE_SYNTH) return; static_cast(s->sound.get())->setFreq(newFreq); @@ -285,7 +285,7 @@ void Player::setWaveformSuperwave(unsigned int soundHash, bool superwave) { auto const s = findByHash(soundHash); - if (s == 0 || s->soundType != TYPE_SYNTH) + if (s == nullptr || s->soundType != TYPE_SYNTH) return; static_cast(s->sound.get())->setSuperWave(superwave); @@ -328,7 +328,7 @@ unsigned int Player::play( { ActiveSound *sound = findByHash(soundHash); - if (sound == 0) + if (sound == nullptr) return soundHashNotFound; SoLoud::handle newHandle = soloud.play( @@ -377,7 +377,7 @@ void Player::disposeSound(unsigned int soundHash) { ActiveSound *sound = findByHash(soundHash); - if (sound == 0) + if (sound == nullptr) return; sound->sound.get()->stop(); @@ -459,7 +459,7 @@ double Player::getLength(unsigned int soundHash) { auto const &s = findByHash(soundHash); - if (s == 0 || s->soundType == TYPE_SYNTH) + if (s == nullptr || s->soundType == TYPE_SYNTH) return 0.0; if (s->soundType == TYPE_WAV) return static_cast(s->sound.get())->getLength(); @@ -545,7 +545,7 @@ int Player::countAudioSource(unsigned int soundHash) { auto const &s = findByHash(soundHash); - if (s == 0 || s->soundType == TYPE_SYNTH) + if (s == nullptr || s->soundType == TYPE_SYNTH) return 0; if (s->soundType == TYPE_WAV) { @@ -610,7 +610,7 @@ ActiveSound *Player::findByHash(unsigned int soundHash) [&](std::shared_ptr const &f) { return f->soundHash == soundHash; }); if (s == sounds.end()) - return 0; + return nullptr; return s->get(); }