package miniaudio import "core:c" when ODIN_OS == .Windows { foreign import lib "lib/miniaudio.lib" } else when ODIN_OS == .Linux { foreign import lib "lib/miniaudio.a" } else { foreign import lib "system:miniaudio" } /************************************************************************************************************************************************************ Engine ************************************************************************************************************************************************************/ /* Sound flags. */ sound_flags :: enum c.int { STREAM = 0x00000001, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM */ DECODE = 0x00000002, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE */ ASYNC = 0x00000004, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC */ WAIT_INIT = 0x00000008, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT */ NO_DEFAULT_ATTACHMENT = 0x00000010, /* Do not attach to the endpoint by default. Useful for when setting up nodes in a complex graph system. */ NO_PITCH = 0x00000020, /* Disable pitch shifting with ma_sound_set_pitch() and ma_sound_group_set_pitch(). This is an optimization. */ NO_SPATIALIZATION = 0x00000040, /* Disable spatialization. */ } ENGINE_MAX_LISTENERS :: 4 LISTENER_INDEX_CLOSEST :: 255 engine_node_type :: enum c.int { sound, group, } engine_node_config :: struct { pEngine: ^engine, type: engine_node_type, channelsIn: u32, channelsOut: u32, sampleRate: u32, /* Only used when the type is set to ma_engine_node_type_sound. */ isPitchDisabled: b8, /* Pitching can be explicitly disable with MA_SOUND_FLAG_NO_PITCH to optimize processing. */ isSpatializationDisabled: b8, /* Spatialization can be explicitly disabled with MA_SOUND_FLAG_NO_SPATIALIZATION. */ pinnedListenerIndex: u8, /* The index of the listener this node should always use for spatialization. If set to MA_LISTENER_INDEX_CLOSEST the engine will use the closest listener. */ } /* Base node object for both ma_sound and ma_sound_group. */ engine_node :: struct { baseNode: node_base, /* Must be the first member for compatiblity with the ma_node API. */ pEngine: ^engine, /* A pointer to the engine. Set based on the value from the config. */ sampleRate: u32, /* The sample rate of the input data. For sounds backed by a data source, this will be the data source's sample rate. Otherwise it'll be the engine's sample rate. */ fader: fader, resampler: linear_resampler, /* For pitch shift. */ spatializer: spatializer, panner: panner, pitch: f32, /*atomic*/ oldPitch: f32, /* For determining whether or not the resampler needs to be updated to reflect the new pitch. The resampler will be updated on the mixing thread. */ oldDopplerPitch: f32, /* For determining whether or not the resampler needs to be updated to take a new doppler pitch into account. */ isPitchDisabled: b32, /*atomic*/ /* When set to true, pitching will be disabled which will allow the resampler to be bypassed to save some computation. */ isSpatializationDisabled: b32, /*atomic*/ /* Set to false by default. When set to false, will not have spatialisation applied. */ pinnedListenerIndex: u32, /*atomic*/ /* The index of the listener this node should always use for spatialization. If set to MA_LISTENER_INDEX_CLOSEST the engine will use the closest listener. */ /* Memory management. */ _ownsHeap: b8, _pHeap: rawptr, } @(default_calling_convention="c", link_prefix="ma_") foreign lib { engine_node_config_init :: proc(pEngine: ^engine, type: engine_node_type, flags: u32) -> engine_node_config --- engine_node_get_heap_size :: proc(pConfig: ^engine_node_config, pHeapSizeInBytes: ^c.size_t) -> result --- engine_node_init_preallocated :: proc(pConfig: ^engine_node_config, pHeap: rawptr, pEngineNode: ^engine_node) -> result --- engine_node_init :: proc(pConfig: ^engine_node_config, pAllocationCallbacks: ^allocation_callbacks, pEngineNode: ^engine_node) -> result --- engine_node_uninit :: proc(pEngineNode: ^engine_node, pAllocationCallbacks: ^allocation_callbacks) --- } SOUND_SOURCE_CHANNEL_COUNT :: 0xFFFFFFFF sound_config :: struct { pFilePath: cstring, /* Set this to load from the resource manager. */ pFilePathW: [^]c.wchar_t, /* Set this to load from the resource manager. */ pDataSource: ^data_source, /* Set this to load from an existing data source. */ pInitialAttachment: ^node, /* If set, the sound will be attached to an input of this node. This can be set to a ma_sound. If set to NULL, the sound will be attached directly to the endpoint unless MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT is set in `flags`. */ initialAttachmentInputBusIndex: u32, /* The index of the input bus of pInitialAttachment to attach the sound to. */ channelsIn: u32, /* Ignored if using a data source as input (the data source's channel count will be used always). Otherwise, setting to 0 will cause the engine's channel count to be used. */ channelsOut: u32, /* Set this to 0 (default) to use the engine's channel count. Set to MA_SOUND_SOURCE_CHANNEL_COUNT to use the data source's channel count (only used if using a data source as input). */ flags: u32, /* A combination of MA_SOUND_FLAG_* flags. */ initialSeekPointInPCMFrames: u64, /* Initializes the sound such that it's seeked to this location by default. */ rangeBegInPCMFrames: u64, rangeEndInPCMFrames: u64, loopPointBegInPCMFrames: u64, loopPointEndInPCMFrames: u64, isLooping: b32, pDoneFence: ^fence, /* Released when the resource manager has finished decoding the entire sound. Not used with streams. */ } sound :: struct { engineNode: engine_node, /* Must be the first member for compatibility with the ma_node API. */ pDataSource: ^data_source, seekTarget: u64, /*atomic*/ /* The PCM frame index to seek to in the mixing thread. Set to (~(ma_uint64)0) to not perform any seeking. */ atEnd: b32, /*atomic*/ ownsDataSource: b8, /* We're declaring a resource manager data source object here to save us a malloc when loading a sound via the resource manager, which I *think* will be the most common scenario. */ pResourceManagerDataSource: ^resource_manager_data_source, } /* Structure specifically for sounds played with ma_engine_play_sound(). Making this a separate structure to reduce overhead. */ sound_inlined :: struct { sound: sound, pNext: ^sound_inlined, pPrev: ^sound_inlined, } @(default_calling_convention="c", link_prefix="ma_") foreign lib { sound_config_init :: proc() -> sound_config --- sound_init_from_file :: proc(pEngine: ^engine, pFilePath: cstring, flags: u32, pGroup: ^sound_group, pDoneFence: ^fence, pSound: ^sound) -> result --- sound_init_from_file_w :: proc(pEngine: ^engine, pFilePath: [^]c.wchar_t, flags: u32, pGroup: ^sound_group, pDoneFence: ^fence, pSound: ^sound) -> result --- sound_init_copy :: proc(pEngine: ^engine, pExistingSound: ^sound, flags: u32, pGroup: ^sound_group, pSound: ^sound) -> result --- sound_init_from_data_source :: proc(pEngine: ^engine, pDataSource: ^data_source, flags: u32, pGroup: ^sound_group, pSound: ^sound) -> result --- sound_init_ex :: proc(pEngine: ^engine, pConfig: ^sound_config, pSound: ^sound) -> result --- sound_uninit :: proc(pSound: ^sound) --- sound_get_engine :: proc(pSound: ^sound) -> ^engine --- sound_get_data_source :: proc(pSound: ^sound) -> ^data_source --- sound_start :: proc(pSound: ^sound) -> result --- sound_stop :: proc(pSound: ^sound) -> result --- sound_set_volume :: proc(pSound: ^sound, volume: f32) --- sound_get_volume :: proc(pSound: ^sound) -> f32 --- sound_set_pan :: proc(pSound: ^sound, pan: f32) --- sound_get_pan :: proc(pSound: ^sound) -> f32 --- sound_set_pan_mode :: proc(pSound: ^sound, panMode: pan_mode) --- sound_get_pan_mode :: proc(pSound: ^sound) -> pan_mode --- sound_set_pitch :: proc(pSound: ^sound, pitch: f32) --- sound_get_pitch :: proc(pSound: ^sound) -> f32 --- sound_set_spatialization_enabled :: proc(pSound: ^sound, enabled: b32) --- sound_is_spatialization_enabled :: proc(pSound: ^sound) -> b32 --- sound_set_pinned_listener_index :: proc(pSound: ^sound, listenerIndex: u32) --- sound_get_pinned_listener_index :: proc(pSound: ^sound) -> u32 --- sound_get_listener_index :: proc(pSound: ^sound) -> u32 --- sound_get_direction_to_listener :: proc(pSound: ^sound) -> vec3f --- sound_set_position :: proc(pSound: ^sound, x, y, z: f32) --- sound_get_position :: proc(pSound: ^sound) -> vec3f --- sound_set_direction :: proc(pSound: ^sound, x, y, z: f32) --- sound_get_direction :: proc(pSound: ^sound) -> vec3f --- sound_set_velocity :: proc(pSound: ^sound, x, y, z: f32) --- sound_get_velocity :: proc(pSound: ^sound) -> vec3f --- sound_set_attenuation_model :: proc(pSound: ^sound, attenuationModel: attenuation_model) --- sound_get_attenuation_model :: proc(pSound: ^sound) -> attenuation_model --- sound_set_positioning :: proc(pSound: ^sound, positioning: positioning) --- sound_get_positioning :: proc(pSound: ^sound) -> positioning --- sound_set_rolloff :: proc(pSound: ^sound, rolloff: f32) --- sound_get_rolloff :: proc(pSound: ^sound) -> f32 --- sound_set_min_gain :: proc(pSound: ^sound, minGain: f32) --- sound_get_min_gain :: proc(pSound: ^sound) -> f32 --- sound_set_max_gain :: proc(pSound: ^sound, maxGain: f32) --- sound_get_max_gain :: proc(pSound: ^sound) -> f32 --- sound_set_min_distance :: proc(pSound: ^sound, minDistance: f32) --- sound_get_min_distance :: proc(pSound: ^sound) -> f32 --- sound_set_max_distance :: proc(pSound: ^sound, maxDistance: f32) --- sound_get_max_distance :: proc(pSound: ^sound) -> f32 --- sound_set_cone :: proc(pSound: ^sound, innerAngleInRadians, outerAngleInRadians, outerGain: f32) --- sound_get_cone :: proc(pSound: ^sound, pInnerAngleInRadians, pOuterAngleInRadians, pOuterGain: ^f32) --- sound_set_doppler_factor :: proc(pSound: ^sound, dopplerFactor: f32) --- sound_get_doppler_factor :: proc(pSound: ^sound) -> f32 --- sound_set_directional_attenuation_factor :: proc(pSound: ^sound, directionalAttenuationFactor: f32) --- sound_get_directional_attenuation_factor :: proc(pSound: ^sound) -> f32 --- sound_set_fade_in_pcm_frames :: proc(pSound: ^sound, volumeBeg, volumeEnd: f32, fadeLengthInFrames: u64) --- sound_set_fade_in_milliseconds :: proc(pSound: ^sound, volumeBeg, volumeEnd: f32, fadeLengthInMilliseconds: u64) --- sound_get_current_fade_volume :: proc(pSound: ^sound) -> f32 --- sound_set_start_time_in_pcm_frames :: proc(pSound: ^sound, absoluteGlobalTimeInFrames: u64) --- sound_set_start_time_in_milliseconds :: proc(pSound: ^sound, absoluteGlobalTimeInMilliseconds: u64) --- sound_set_stop_time_in_pcm_frames :: proc(pSound: ^sound, absoluteGlobalTimeInFrames: u64) --- sound_set_stop_time_in_milliseconds :: proc(pSound: ^sound, absoluteGlobalTimeInMilliseconds: u64) --- sound_is_playing :: proc(pSound: ^sound) -> b32 --- sound_get_time_in_pcm_frames :: proc(pSound: ^sound) -> u64 --- sound_set_looping :: proc(pSound: ^sound, isLooping: b32) --- sound_is_looping :: proc(pSound: ^sound) -> b32 --- sound_at_end :: proc(pSound: ^sound) -> b32 --- sound_seek_to_pcm_frame :: proc(pSound: ^sound, frameIndex: u64) -> result --- /* Just a wrapper around ma_data_source_seek_to_pcm_frame(). */ sound_get_data_format :: proc(pSound: ^sound, pFormat: ^format, pChannels, pSampleRate: ^u32, pChannelMap: ^channel, channelMapCap: c.size_t) -> result --- sound_get_cursor_in_pcm_frames :: proc(pSound: ^sound, pCursor: ^u64) -> result --- sound_get_length_in_pcm_frames :: proc(pSound: ^sound, pLength: ^u64) -> result --- sound_get_cursor_in_seconds :: proc(pSound: ^sound, pCursor: ^f32) -> result --- sound_get_length_in_seconds :: proc(pSound: ^sound, pLength: ^f32) -> result --- } /* A sound group is just a sound. */ sound_group_config :: distinct sound_config sound_group :: distinct sound @(default_calling_convention="c", link_prefix="ma_") foreign lib { sound_group_config_init :: proc() -> sound_group_config --- sound_group_init :: proc(pEngine: ^engine, flags: u32, pParentGroup, pGroup: ^sound_group) -> result --- sound_group_init_ex :: proc(pEngine: ^engine, pConfig: ^sound_group_config, pGroup: ^sound_group) -> result --- sound_group_uninit :: proc(pGroup: ^sound_group) --- sound_group_get_engine :: proc(pGroup: ^sound_group) -> ^engine --- sound_group_start :: proc(pGroup: ^sound_group) -> result --- sound_group_stop :: proc(pGroup: ^sound_group) -> result --- sound_group_set_volume :: proc(pGroup: ^sound_group, volume: f32) --- sound_group_get_volume :: proc(pGroup: ^sound_group) -> f32 --- sound_group_set_pan :: proc(pGroup: ^sound_group, pan: f32) --- sound_group_get_pan :: proc(pGroup: ^sound_group) -> f32 --- sound_group_set_pan_mode :: proc(pGroup: ^sound_group, panMode: pan_mode) --- sound_group_get_pan_mode :: proc(pGroup: ^sound_group) -> pan_mode --- sound_group_set_pitch :: proc(pGroup: ^sound_group, pitch: f32) --- sound_group_get_pitch :: proc(pGroup: ^sound_group) -> f32 --- sound_group_set_spatialization_enabled :: proc(pGroup: ^sound_group, enabled: b32) --- sound_group_is_spatialization_enabled :: proc(pGroup: ^sound_group) -> b32 --- sound_group_set_pinned_listener_index :: proc(pGroup: ^sound_group, listenerIndex: u32) --- sound_group_get_pinned_listener_index :: proc(pGroup: ^sound_group) -> u32 --- sound_group_get_listener_index :: proc(pGroup: ^sound_group) -> u32 --- sound_group_get_direction_to_listener :: proc(pGroup: ^sound_group) -> vec3f --- sound_group_set_position :: proc(pGroup: ^sound_group, x, y, z: f32) --- sound_group_get_position :: proc(pGroup: ^sound_group) -> vec3f --- sound_group_set_direction :: proc(pGroup: ^sound_group, x, y, z: f32) --- sound_group_get_direction :: proc(pGroup: ^sound_group) -> vec3f --- sound_group_set_velocity :: proc(pGroup: ^sound_group, x, y, z: f32) --- sound_group_get_velocity :: proc(pGroup: ^sound_group) -> vec3f --- sound_group_set_attenuation_model :: proc(pGroup: ^sound_group, attenuationModel: attenuation_model) --- sound_group_get_attenuation_model :: proc(pGroup: ^sound_group) -> attenuation_model --- sound_group_set_positioning :: proc(pGroup: ^sound_group, positioning: positioning) --- sound_group_get_positioning :: proc(pGroup: ^sound_group) -> positioning --- sound_group_set_rolloff :: proc(pGroup: ^sound_group, rolloff: f32) --- sound_group_get_rolloff :: proc(pGroup: ^sound_group) -> f32 --- sound_group_set_min_gain :: proc(pGroup: ^sound_group, minGain: f32) --- sound_group_get_min_gain :: proc(pGroup: ^sound_group) -> f32 --- sound_group_set_max_gain :: proc(pGroup: ^sound_group, maxGain: f32) --- sound_group_get_max_gain :: proc(pGroup: ^sound_group) -> f32 --- sound_group_set_min_distance :: proc(pGroup: ^sound_group, minDistance: f32) --- sound_group_get_min_distance :: proc(pGroup: ^sound_group) -> f32 --- sound_group_set_max_distance :: proc(pGroup: ^sound_group, maxDistance: f32) --- sound_group_get_max_distance :: proc(pGroup: ^sound_group) -> f32 --- sound_group_set_cone :: proc(pGroup: ^sound_group, innerAngleInRadians, outerAngleInRadians, outerGain: f32) --- sound_group_get_cone :: proc(pGroup: ^sound_group, pInnerAngleInRadians, pOuterAngleInRadians, pOuterGain: ^f32) --- sound_group_set_doppler_factor :: proc(pGroup: ^sound_group, dopplerFactor: f32) --- sound_group_get_doppler_factor :: proc(pGroup: ^sound_group) -> f32 --- sound_group_set_directional_attenuation_factor :: proc(pGroup: ^sound_group, directionalAttenuationFactor: f32) --- sound_group_get_directional_attenuation_factor :: proc(pGroup: ^sound_group) -> f32 --- sound_group_set_fade_in_pcm_frames :: proc(pGroup: ^sound_group, volumeBeg, volumeEnd: f32, fadeLengthInFrames: u64) --- sound_group_set_fade_in_milliseconds :: proc(pGroup: ^sound_group, volumeBeg, volumeEnd: f32, fadeLengthInMilliseconds: u64) --- sound_group_get_current_fade_volume :: proc(pGroup: ^sound_group) -> f32 --- sound_group_set_start_time_in_pcm_frames :: proc(pGroup: ^sound_group, absoluteGlobalTimeInFrames: u64) --- sound_group_set_start_time_in_milliseconds :: proc(pGroup: ^sound_group, absoluteGlobalTimeInMilliseconds: u64) --- sound_group_set_stop_time_in_pcm_frames :: proc(pGroup: ^sound_group, absoluteGlobalTimeInFrames: u64) --- sound_group_set_stop_time_in_milliseconds :: proc(pGroup: ^sound_group, absoluteGlobalTimeInMilliseconds: u64) --- sound_group_is_playing :: proc(pGroup: ^sound_group) -> b32 --- sound_group_get_time_in_pcm_frames :: proc(pGroup: ^sound_group) -> u64 --- } engine_config :: struct { pResourceManager: ^resource_manager, /* Can be null in which case a resource manager will be created for you. */ pContext: ^context_type, pDevice: ^device, /* If set, the caller is responsible for calling ma_engine_data_callback() in the device's data callback. */ pPlaybackDeviceID: ^device_id, /* The ID of the playback device to use with the default listener. */ pLog: ^log, /* When set to NULL, will use the context's log. */ listenerCount: u32, /* Must be between 1 and MA_ENGINE_MAX_LISTENERS. */ channels: u32, /* The number of channels to use when mixing and spatializing. When set to 0, will use the native channel count of the device. */ sampleRate: u32, /* The sample rate. When set to 0 will use the native channel count of the device. */ periodSizeInFrames: u32, /* If set to something other than 0, updates will always be exactly this size. The underlying device may be a different size, but from the perspective of the mixer that won't matter.*/ periodSizeInMilliseconds: u32, /* Used if periodSizeInFrames is unset. */ gainSmoothTimeInFrames: u32, /* The number of frames to interpolate the gain of spatialized sounds across. If set to 0, will use gainSmoothTimeInMilliseconds. */ gainSmoothTimeInMilliseconds: u32, /* When set to 0, gainSmoothTimeInFrames will be used. If both are set to 0, a default value will be used. */ allocationCallbacks: allocation_callbacks, noAutoStart: b32, /* When set to true, requires an explicit call to ma_engine_start(). This is false by default, meaning the engine will be started automatically in ma_engine_init(). */ noDevice: b32, /* When set to true, don't create a default device. ma_engine_read_pcm_frames() can be called manually to read data. */ monoExpansionMode: mono_expansion_mode, /* Controls how the mono channel should be expanded to other channels when spatialization is disabled on a sound. */ pResourceManagerVFS: ^vfs, /* A pointer to a pre-allocated VFS object to use with the resource manager. This is ignored if pResourceManager is not NULL. */ } engine :: struct { nodeGraph: node_graph, /* An engine is a node graph. It should be able to be plugged into any ma_node_graph API (with a cast) which means this must be the first member of this struct. */ pResourceManager: ^resource_manager, pDevice: ^device, /* Optionally set via the config, otherwise allocated by the engine in ma_engine_init(). */ pLog: ^log, sampleRate: u32, listenerCount: u32, listeners: [ENGINE_MAX_LISTENERS]spatializer_listener, allocationCallbacks: allocation_callbacks, ownsResourceManager: b8, ownsDevice: b8, inlinedSoundLock: spinlock, /* For synchronizing access so the inlined sound list. */ pInlinedSoundHead: ^sound_inlined, /* The first inlined sound. Inlined sounds are tracked in a linked list. */ inlinedSoundCount: u32, /*atomic*/ /* The total number of allocated inlined sound objects. Used for debugging. */ gainSmoothTimeInFrames: u32, /* The number of frames to interpolate the gain of spatialized sounds across. */ monoExpansionMode: mono_expansion_mode, } @(default_calling_convention="c", link_prefix="ma_") foreign lib { engine_config_init :: proc() -> engine_config --- engine_init :: proc(pConfig: ^engine_config, pEngine: ^engine) -> result --- engine_uninit :: proc(pEngine: ^engine) --- engine_read_pcm_frames :: proc(pEngine: ^engine, pFramesOut: rawptr, frameCount: u64, pFramesRead: ^u64) -> result --- engine_get_node_graph :: proc(pEngine: ^engine) -> ^node_graph --- engine_get_resource_manager :: proc(pEngine: ^engine) -> ^resource_manager --- engine_get_device :: proc(pEngine: ^engine) -> ^device --- engine_get_log :: proc(pEngine: ^engine) -> ^log --- engine_get_endpoint :: proc(pEngine: ^engine) -> ^node --- engine_get_time :: proc(pEngine: ^engine) -> u64 --- engine_set_time :: proc(pEngine: ^engine, globalTime: u64) -> result --- engine_get_channels :: proc(pEngine: ^engine) -> u32 --- engine_get_sample_rate :: proc(pEngine: ^engine) -> u32 --- engine_start :: proc(pEngine: ^engine) -> result --- engine_stop :: proc(pEngine: ^engine) -> result --- engine_set_volume :: proc(pEngine: ^engine, volume: f32) -> result --- engine_set_gain_db :: proc(pEngine: ^engine, gainDB: f32) -> result --- engine_get_listener_count :: proc(pEngine: ^engine) -> u32 --- engine_find_closest_listener :: proc(pEngine: ^engine, absolutePosX, absolutePosY, absolutePosZ: f32) -> u32 --- engine_listener_set_position :: proc(pEngine: ^engine, listenerIndex: u32, x, y, z: f32) --- engine_listener_get_position :: proc(pEngine: ^engine, listenerIndex: u32) -> vec3f --- engine_listener_set_direction :: proc(pEngine: ^engine, listenerIndex: u32, x, y, z: f32) --- engine_listener_get_direction :: proc(pEngine: ^engine, listenerIndex: u32) -> vec3f --- engine_listener_set_velocity :: proc(pEngine: ^engine, listenerIndex: u32, x, y, z: f32) --- engine_listener_get_velocity :: proc(pEngine: ^engine, listenerIndex: u32) -> vec3f --- engine_listener_set_cone :: proc(pEngine: ^engine, listenerIndex: u32, innerAngleInRadians, outerAngleInRadians, outerGain: f32) --- engine_listener_get_cone :: proc(pEngine: ^engine, listenerIndex: u32, pInnerAngleInRadians, pOuterAngleInRadians, pOuterGain: ^f32) --- engine_listener_set_world_up :: proc(pEngine: ^engine, listenerIndex: u32, x, y, z: f32) --- engine_listener_get_world_up :: proc(pEngine: ^engine, listenerIndex: u32) -> vec3f --- engine_listener_set_enabled :: proc(pEngine: ^engine, listenerIndex: u32, isEnabled: b32) --- engine_listener_is_enabled :: proc(pEngine: ^engine, listenerIndex: u32) -> b32 --- engine_play_sound_ex :: proc(pEngine: ^engine, pFilePath: cstring, pNode: ^node, nodeInputBusIndex: u32) -> result --- engine_play_sound :: proc(pEngine: ^engine, pFilePath: cstring, pGroup: ^sound_group) -> result --- /* Fire and forget. */ }