# audio-buffer [![test](https://github.com/audiojs/audio-buffer/actions/workflows/node.js.yml/badge.svg)](https://github.com/audiojs/audio-buffer/actions/workflows/node.js.yml) [![stable](https://img.shields.io/badge/stability-stable-brightgreen.svg)](http://github.com/badges/stability-badges)

> Audio data container with planar float32 layout.

[Web Audio API AudioBuffer](https://developer.mozilla.org/en-US/docs/Web/API/AudioBuffer) ponyfill for node, bun and other envs.

Comes with optional utils.

```js
import AudioBuffer from 'audio-buffer'
import { from, trim, normalize } from 'audio-buffer/util'

let buf = from([0, 0, 0.5, 0.8, 0.3, 0, 0])
buf = normalize(trim(buf))
```

## API

### Constructor

```js
new AudioBuffer({ length: 1024, sampleRate: 44100, numberOfChannels: 2 })
new AudioBuffer(2, 1024, 44100) // positional form
```

### Properties

All read-only.

* `length` — samples per channel
* `sampleRate` — Hz
* `duration` — seconds
* `numberOfChannels`

Iterable over channels: `for (let ch of buf)`, `let [L, R] = buf`.

### Methods

```js
buf.getChannelData(0)                           // Float32Array for channel 0
buf.copyFromChannel(dest, 0, 100)               // copy channel 0 from sample 100 into dest
buf.copyToChannel(src, 1)                       // write src into channel 1
```

## Utils

```js
import { from, slice, concat, set } from 'audio-buffer/util'
```

Same-size ops mutate and return the buffer. Size-changing ops return a new buffer.

#### `isAudioBuffer(buf) → boolean`

Check if object is an AudioBuffer instance or duck-typed compatible.

```js
isAudioBuffer(buf)                               // true
```

#### `from(source, fill?, options?) → AudioBuffer`

Create buffer from anything — number, Float32Array, Array, AudioBuffer, ArrayBuffer. Optional second arg fills samples — a number for constant value, or `fn(sample, index, channel, channelData)` to map (like `Array.from`).

```js
from([0.1, -0.3, 0.5])                       // array of samples → mono
from([left, right], { sampleRate: 48000 })   // Float32Array[] → stereo
from(existingBuffer)                         // clone
from(1024, 0.5)                              // 1024 samples buffer filled with 0.5
from(1024, (s, i) => Math.sin(i * 0.1))      // generate sine wave
from(buf, v => v * 0.5)                      // clone with half volume
from(buf, 0)                                 // same shape, zeroed (like)
from({ length: 1024, numberOfChannels: 2 })  // from options
```

#### `slice(buffer, start?, end?) → newBuffer`

Extract sample range into new buffer.

```js
slice(buf, 100, 200)                           // samples 100–199
slice(buf, -50)                                // last 50 samples
```

#### `concat(...buffers) → newBuffer`

Join buffers (same sampleRate and channels).

```js
concat(a, b)                                   // a + b end-to-end
concat(a, b, c, d)                             // join many
```

#### `set(buffer, other, offset?) → buffer`

Overwrite samples from another buffer.

```js
set(buf, patch, 1000)                          // write patch at sample 1000
```

#### `fill(buffer, value, start?, end?) → buffer`

Fill with constant or per-sample function.

```js
fill(buf, 0)                                   // silence
fill(buf, (s, i, ch) => Math.sin(i * 0.1))     // sine wave
fill(buf, () => Math.random() * 2 - 1)         // white noise
fill(buf, v => -v)                             // phase-invert
```

#### `normalize(buffer, start?, end?) → buffer`

Peak-normalize to 1.0, preserving inter-channel balance.

```js
normalize(buf)                                 // quiet recording → full scale
```

#### `trim(buffer, threshold?) → newBuffer`

Remove silence from both ends.

```js
trim(buf)                                      // remove exact zeros
trim(buf, 0.01)                                // remove near-silence
```

#### `reverse(buffer, start?, end?) → buffer`

Reverse samples.

```js
reverse(buf)                                   // full reverse
reverse(buf, 0, 100)                           // reverse first 100 samples
```

#### `mix(a, b, ratio?, offset?) → a`

Blend `b` into `a`. Ratio 0 = keep `a`, 1 = replace with `b`.

```js
mix(track, reverb, 0.3)                        // 70% dry, 30% wet
mix(a, b, (sa, sb) => Math.max(sa, sb))       // custom blend function
mix(a, b, 0.5, 1000)                           // mix starting at sample 1000
```

#### `remix(buffer, channels, interpretation?) → newBuffer`

Upmix/downmix channels per [Web Audio spec](https://www.w3.org/TR/webaudio/#channel-up-mixing-and-down-mixing) speaker rules.

```js
remix(stereo, 1)                               // stereo → mono
remix(mono, 2)                                 // mono → stereo
remix(buf, 6)                                  // → 5.1 surround
remix(buf, 4, 'discrete')                      // copy channels, silence rest
```

#### `pad(buffer, length, value?, side?) → newBuffer`

Pad to target length.

```js
pad(buf, 44100)                                // zero-pad to 1 second
pad(buf, 44100, 0, 'start')                    // pad at start
```

#### `repeat(buffer, times) → newBuffer`

Repeat N times.

```js
repeat(buf, 4)                                 // loop 4x
```

#### `rotate(buffer, offset) → buffer`

Circular shift. Positive = right.

```js
rotate(buf, 100)                               // shift right 100 samples
rotate(buf, -50)                               // shift left 50
```

#### `removeDC(buffer, start?, end?) → buffer`

Remove DC offset (subtract mean per channel).

```js
removeDC(buf)                                  // center waveform at zero
```

#### `isEqual(a, b) → boolean`

Deep equality — same shape, same samples.

```js
isEqual(buf, clone)                            // true if identical
```

## Play

```js
import play from 'audio-buffer/play'

let ctrl = await play(buf, { volume: 0.8, loop: true })
ctrl.pause()
ctrl.play()
ctrl.stop()
ctrl.currentTime  // seconds
ctrl.playing      // boolean
```

Options: `{ volume, loop, start, end, autoplay, onended }`.

Uses Web Audio API in browsers. In Node.js, install [audio-speaker](https://github.com/audiojs/audio-speaker): `npm i audio-speaker`.

`stop()` resets to start. `play()` restarts from beginning.

## Replaces

* [audio-buffer-utils](https://github.com/audiojs/audio-buffer-utils)
* [audio-buffer-from](https://github.com/audiojs/audio-buffer-from)
* [audio-buffer-remix](https://github.com/audiojs/audio-buffer-remix)
* [audio-play](https://github.com/audiojs/audio-play)

<p align=center><a href="https://github.com/krishnized/license/">🕉</a></p>
