- name
- utilities
- description
- Overview of all shared utility helpers, data structures, and DSP primitives available in react-native-audio-api. Covers C++ utilities in common/cpp/audioapi/utils/, common/cpp/audioapi/core/utils/, common/cpp/audioapi/dsp/, and TypeScript utilities in src/utils/. Use this skill before writing new math, buffer management, or cross-thread code — check if a utility already exists. Trigger phrases: "add a new utility", "what helpers are available", "cross-thread communication", "audio buffer", "DSP math", "circular buffer", "lock-free queue", "off-thread", "SIMD buffer", "time conversion", "parameter automation", "audio constants", "TypeScript utils".
# Skill: Utilities
## `common/cpp/audioapi/utils/` — Core data structures & primitives
### `AudioArray.h` — float audio array (read header for full API)
Single-channel float buffer. The foundational data type. Use for any per-channel audio data.
Key operations: `zero()`, `sum(source, gain)`, `multiply(source)`, `copy(source)`, `copyReverse(...)`, `copyTo(float*)`, `copyWithin(...)`, `scale(float)`, `normalize()`, `getMaxAbsValue()`, `computeConvolution(kernel)`.
Access: `operator[]`, `begin()`/`end()`, `span()`, `subSpan(length, offset)`.
Constructors copy data — `AudioArray` always owns its buffer.
---
### `AudioBuffer.h` — multi-channel container (read header for full API)
Holds N channels of `AudioArrayBuffer`. Handles up/down-mixing automatically on `sum()` and `copy()`.
Key operations: `getChannel(index)`, `getSharedChannel(index)`, `zero()`, `sum(source, interpretation)`, `copy(source)`, `deinterleaveFrom(float*, frames)`, `interleaveTo(float*, frames)`, `normalize()`, `scale(float)`.
Channel layout constants: `ChannelMono=0`, `ChannelLeft=0`, `ChannelRight=1`, `ChannelCenter=2`, `ChannelLFE=3`, `ChannelSurroundLeft=4`, `ChannelSurroundRight=5`.
`getChannel()` returns a non-owning `AudioArray*`. `getSharedChannel()` returns `shared_ptr<AudioArrayBuffer>` (use for JSI transfer).
---
### `AudioArrayBuffer.hpp` — JSI-transferable audio array
`AudioArray` + `jsi::MutableBuffer`. Allows zero-copy transfer of audio data to JS as an `ArrayBuffer`. Use in `getChannelData()` patterns.
```cpp
// Typical usage in HostObject — no copy, JS sees the native memory
auto audioArrayBuffer = audioBuffer_->getSharedChannel(channel);
auto arrayBuffer = jsi::ArrayBuffer(runtime, audioArrayBuffer);
auto float32Array = runtime.global()
.getPropertyAsFunction(runtime, "Float32Array")
.callAsConstructor(runtime, arrayBuffer)
.getObject(runtime);
float32Array.setExternalMemoryPressure(runtime, audioArrayBuffer->size());
```
---
### `CircularAudioArray.h` — circular float buffer (read header)
`AudioArray` subclass acting as a circular queue for streaming audio. **Not thread-safe** — use only from one thread.
Key operations:
- `push_back(AudioArray&, size)` / `push_back(float*, size)` — write frames
- `pop_front(AudioArray&, size)` / `pop_front(float*, size)` — read oldest frames
- `pop_back(AudioArray&, size, offset)` — read newest frames
- `getNumberOfAvailableFrames()` — how many frames ready to read
Used in delay lines and streaming buffers.
---
### `CircularOverflowableAudioArray.h` — overwritable circular buffer
Like `CircularAudioArray` but overwrites oldest data when full instead of rejecting. Use for recording input where you always want the latest data, not the oldest.
---
### `SpscChannel.hpp` — lock-free SPSC channel
Bounded single-producer, single-consumer queue built on aligned atomics. **Do not use directly — prefer `CrossThreadEventScheduler` or `TaskOffloader`** unless you need fine-grained control.
For full API see [api.md](api.md#spscchannelhpp--lock-free-spsc-channel).
---
### `CrossThreadEventScheduler.hpp` — JS→audio event queue
High-level wrapper over `SpscChannel` for scheduling lambdas from the JS thread to be executed on the audio thread. This is **the standard way** to send updates from JS to audio.
For full API see [api.md](api.md#crossthreadeventschedulerhpp--jsaudio-event-queue).
---
### `AlignedAllocator.hpp` — aligned STL allocator
STL-compatible allocator that guarantees `N`-byte alignment (default 16 bytes, SIMD-friendly). Use when creating buffers that will be processed by SIMD code in `VectorMath`.
For full API see [api.md](api.md#alignedallocatorhpp--aligned-stl-allocator).
---
### `MoveOnlyFunction.hpp` — non-copyable function wrapper
Backport of C++23 `std::move_only_function`. Use instead of `std::function` when the callable captures a move-only type (e.g. a `unique_ptr`).
For full API see [api.md](api.md#moveonlyfunctionhpp--non-copyable-function-wrapper).
---
### `Result.hpp` — Rust-style Result<T,E>
Represents either success (`Ok`) or error (`Err`). Use at API boundaries (e.g. `AudioRecorder::start()`). Use `NoneType` / `None` for void variants.
For full API see [api.md](api.md#resulthpp--rust-style-resultte).
---
### `BoundedPriorityQueue.hpp` — fixed-capacity sorted queue
Non-thread-safe `std::multiset` backed by an in-object block pool. Fixed compile-time capacity, node recycling, zero heap allocation — safe to use on the audio thread. `push` returns `false` when full instead of growing. Used by `ParamQueueBase` (AudioParam automation) and `DeferredEventQueue`.
**Do not back this with `std::pmr`.** A `monotonic_buffer_resource` + `unsynchronized_pool_resource` needs a buffer whose size depends on the standard library's private bookkeeping, and libstdc++ needs far more of it than libc++ — a buffer sized against libc++ threw `std::bad_alloc` short of capacity on the Linux CI runner while passing locally on macOS. The block pool needs exactly `Capacity * SLOT_SIZE` bytes everywhere.
For full API see [api.md](api.md#boundedpriorityqueuehpp--fixed-capacity-sorted-queue).
---
### `TaskOffloader.hpp` — worker thread with SPSC input
Spawns a dedicated worker thread that processes items from a SPSC channel. Use when you need to offload a recurring task (e.g. file writing, decoding) to a dedicated thread.
For full API see [api.md](api.md#taskoffloaderhpp--worker-thread-with-spsc-input).
---
### `Benchmark.hpp` — timing utilities (dev/debug only)
Use `getExecutionTime()` for one-shot nanosecond timing. **Do not leave `logAvgExecutionTime` in production code.**
For full API see [api.md](api.md#benchmarkhpp--timing-utilities-devdebug-only).
---
### `Path.h` — path strings and file:// URLs
```cpp
audioapi::path::lowercaseExtension(path) // "wav" for "/tmp/Take.WAV", "" when none
audioapi::path::hasExtension(path, {"m4a", "mp4"}) // lowercase, no leading dot
audioapi::path::hasNonFileProtocol(path) // http://, content://, ...
audioapi::path::normalizeFilePath(pathOrFileUrl) // strips file:// and percent-decodes
```
Pure string work, never touches the disk (that is `FileSystem.hpp`). The extension comes from the file name only, so a dot in a directory name is not mistaken for one. `decoding::pathHasExtension` is a different, older suffix match used by the decoder.
### `FileSystem.hpp` — path queries without `stat`
```cpp
audioapi::file_system::fileExists(path) // false also when the path cannot be inspected
audioapi::file_system::fileSizeBytes(path) // 0 when missing or unreadable
audioapi::file_system::removeFile(path) // no-op when missing
```
Both wrap `std::filesystem` with an `error_code`, so they never throw. Use them instead of a local `::stat` helper; `fstat` on an fd you already opened is a different job and stays inline.
### `UnitConversion.h` — byte unit constants
```cpp
audioapi::KB_IN_BYTES // 1024.0
audioapi::MB_IN_BYTES // 1024 * 1024.0
audioapi::GB_IN_BYTES // 1024^3.0
```
### `AudioLayout.h` — sample rate + channel count
`AudioLayout { float sampleRate; int channelCount; }` with a defaulted `operator==`. It is the file stream in `AudioFileProperties::stream` / `EncoderSettings::stream` and the `layout` of a `StreamFormat` (`encoding/StreamFormat.h`, which adds `maxFramesPerBuffer`). Hold a new pair of these as one `AudioLayout` and compare whole layouts (`inputLayout != outputLayout_`) rather than two fields. Storage typed for a native API stays native: the remux `TrackInfo` structs (`int32_t` from `AMediaFormat_getInt32`, `UInt32` from an ASBD) and the integer rates the Android encoder backends write into WAV headers and `MediaCodec` formats.
---
## `common/cpp/audioapi/core/utils/` — Node and context utilities
### `Constants.h` — global audio constants
```cpp
RENDER_QUANTUM_SIZE = 128 // frames per render block — never hardcode 128
MAX_FFT_SIZE = 32768
MAX_CHANNEL_COUNT = 32
OCTAVE_RANGE = 1200 // cents per octave
PI = std::numbers::pi_v<float>
MOST_POSITIVE_SINGLE_FLOAT / MOST_NEGATIVE_SINGLE_FLOAT
PROMISE_VENDOR_THREAD_POOL_WORKER_COUNT = 4
```
---
### `AudioDestructor.hpp` — off-thread destruction
Offloads `shared_ptr` destruction to a dedicated worker thread. Use for any object whose destructor may block or deallocate large buffers — both are forbidden on the audio thread.
For full API see [api.md](api.md#audiodestructorhpp--off-thread-destruction).
---
### `ParamChangeEvent.hpp` — AudioParam automation event
Represents a single Web Audio API automation command (`setValueAtTime`, `linearRampToValueAtTime`, etc.). Move-only. Used exclusively within `AudioParamEventQueue` — do not construct outside of `AudioParam` scheduling methods.
For full API see [api.md](api.md#paramchangeeventhpp--audioparam-automation-event).
---
### `AudioParamEventQueue.h` — sorted automation event queue
Stores and processes `ParamChangeEvent` objects in time order on the audio thread. Read the header for full API.
---
### `AudioGraphManager.h` — thread-safe graph mutation queue
Queues connect/disconnect operations from the JS thread for application before each render pass. Do not call its methods directly — go through `AudioNode::connect()`/`disconnect()`. Read the header for implementation details.
---
### `Locker.h` — nullable mutex RAII wrapper
RAII mutex wrapper that can hold `nullptr` (no-op). Supports `Locker::tryLock(mutex)` for non-blocking acquisition. **Do not use on the audio thread.** Locks are forbidden in `processNode()`.
---
### Other `core/utils/` classes
- **`AudioDecoder.h`** — decodes audio files to `AudioBuffer` (FFmpeg, conditional). Read the header.
- **`AudioFileWriter.h`** — writes PCM to audio files. Read the header.
- **`AudioRecorderCallback.h`** — callback adapter for the platform recorder. Internal.
- **`worklets/WorkletsRunner.h`** — manages JS worklet execution on the audio thread. Internal.
---
## `common/cpp/audioapi/dsp/` — DSP helpers
### `AudioUtils.h` — inline DSP math
Provides `timeToSampleFrame()`, `sampleFrameToTime()`, `linearInterpolate()`, `linearToDecibels()`, `decibelsToLinear()`, `uint8ToFloat()` (LE int16 bytes → float).
For full API see [api.md](api.md#audioutilshpp--inline-dsp-math).
---
### `AudioBufferPool.hpp` — preallocated planar buffers by pointer
`AudioBufferPool<N>` owns N `AudioBuffer`s and hands them out as `AudioBufferLease`s, a `unique_ptr` whose deleter returns the buffer to the pool through a lock-free `SlotFreeList` (any thread, never blocks, no allocation). Use it wherever the audio thread fills a buffer for a worker (`AudioFileWriter`, `AudioRecorderCallback`): the lease travels through the `TaskOffloader` message by move, dropping it anywhere returns the buffer, and a null lease is the shutdown message, so the message struct needs a defaulted `operator==`. Not `shared_ptr`: its control block would allocate on the audio thread.
---
### `VectorMath.h` — SIMD-optimized vector math
SIMD-accelerated array operations (Apple Accelerate/vDSP when `HAVE_ACCELERATE` is set by the podspec, otherwise ARM NEON / x86 SSE2). Use for per-channel hot-path processing. Read the header for available functions before writing manual loops. `interleave`/`deinterleave` handle any channel count (planar pointers <-> channel-interleaved); on Accelerate stereo goes through `vDSP_ctoz`/`vDSP_ztoc` and N channels through one strided `vDSP_vsadd` per channel, so platform code should call these rather than hand-roll a repack. The recorder pipeline is planar end to end (`AudioRecorder::onAudioFrames`, `AudioFileWriter`, `AudioRecorderCallback`, `AudioEncoder::encode` all take one pointer per channel); the only repacks are Oboe's interleaved input on Android and the encoder backends' fused interleave-while-quantize.
---
### `FFT.h` / `Convolver.h` / `Resampler.h` / `WaveShaper.h` / `Windows.hpp`
Higher-level DSP blocks. Read each header before use.
---
### `SpectrumAnalyser.h` — shared windowed-FFT magnitude spectrum
Owns FFT scratch state (Blackman window, temp array, complex scratch, magnitude
output) for the windowed-FFT → linear-magnitude → exponential-smoothing pipeline.
Shared by `AnalyserNode` (`core/analysis/`) and `WorkletNode`
(`react-native-audio-worklets`, frequency-domain mode) to avoid duplicating that
math — each node keeps its own input buffering/threading and just calls
`analyze(timeDomain, smoothingTimeConstant)`, then reads `getMagnitudeData()`.
Exported via `StableAPI.h` for the worklets extension package. Not thread-safe;
call `analyze()`/`setFFTSize()` from a single thread.
---
## `src/utils/` — TypeScript utilities
### `index.ts`
```ts
import { clamp, toFloat32Array, assertFiniteSequence } from './utils';
clamp(value, min, max) // clamp a number to [min, max]
toFloat32Array(values) // number[] → Float32Array (passthrough if already)
toFloat32Array(undefined) // → undefined (overload)
assertFiniteSequence(values, errorMessage) // throws TypeError if any value is non-finite
```
Use `toFloat32Array` when accepting `number[] | Float32Array` options. Use `assertFiniteSequence` in options validators (e.g. PeriodicWave `real`/`imag`).
### `paths.ts`
```ts
import { isRemoteSource, isBase64Source, isDataBlobString } from './utils/paths';
isRemoteSource(url) // true if starts with http:// or https://
isBase64Source(data) // true if 'data:audio/...;base64,...'
isDataBlobString(data) // true if starts with 'blob:'
```
Use before passing a URL/path to decoder or streaming APIs to determine the source type.
---
### `filePresets.ts`
```ts
import FilePreset from './utils/filePresets';
GitHub에서 보기