> ## Documentation Index
> Fetch the complete documentation index at: https://docs.node-webcodecs.com/llms.txt
> Use this file to discover all available pages before exploring further.

# AudioEncoder

> Encode AudioData objects into EncodedAudioChunk for AAC, Opus, MP3, and more

# AudioEncoder

The `AudioEncoder` class encodes raw `AudioData` objects into compressed `EncodedAudioChunk` objects. It supports common audio codecs like AAC, MP3, Opus, FLAC, and Vorbis through FFmpeg.

<Info>
  This class follows the [W3C WebCodecs AudioEncoder specification](https://www.w3.org/TR/webcodecs/#audioencoder-interface).
</Info>

## Quick Example

```typescript theme={null}
import { AudioEncoder, AudioData } from 'node-webcodecs';

// Create an AAC encoder
const encoder = new AudioEncoder({
  output: (chunk, metadata) => {
    console.log(`Encoded ${chunk.byteLength} bytes at ${chunk.timestamp}μs`);

    // First chunk includes decoder config
    if (metadata?.decoderConfig) {
      console.log('Decoder config:', metadata.decoderConfig);
    }
  },
  error: (err) => console.error('Encoding error:', err)
});

// Configure for AAC-LC at 128kbps
encoder.configure({
  codec: 'mp4a.40.2',      // AAC-LC
  sampleRate: 48000,
  numberOfChannels: 2,
  bitrate: 128_000
});

// Create audio data from raw samples
const audioData = new AudioData({
  format: 'f32-planar',
  sampleRate: 48000,
  numberOfFrames: 1024,
  numberOfChannels: 2,
  timestamp: 0,
  data: audioSamples
});

// Encode and close the source data
encoder.encode(audioData);
audioData.close();  // Important: prevent memory leaks

// Wait for encoding to complete
await encoder.flush();
encoder.close();
```

<Warning>
  Always call `audioData.close()` after passing it to `encode()`. Failing to close AudioData objects will cause memory leaks as the underlying buffers are not automatically released.
</Warning>

***

## Supported Audio Codecs

| Codec  | WebCodecs String | Typical Bitrate |
| ------ | ---------------- | --------------- |
| AAC-LC | mp4a.40.2        | 128-256 kbps    |
| MP3    | mp3              | 128-320 kbps    |
| Opus   | opus             | 64-256 kbps     |
| FLAC   | flac             | Lossless        |
| Vorbis | vorbis           | 128-320 kbps    |

***

## Constructor

Creates a new `AudioEncoder` with output and error callbacks.

```typescript theme={null}
new AudioEncoder(init: AudioEncoderInit)
```

<ParamField body="init" type="AudioEncoderInit" required>
  Initialization callbacks for the encoder.

  <Expandable title="AudioEncoderInit properties">
    <ParamField body="output" type="(chunk: EncodedAudioChunk, metadata?: AudioEncoderOutputMetadata) => void" required>
      Callback invoked when an encoded audio chunk is ready. The first chunk typically
      includes decoder configuration in the metadata.
    </ParamField>

    <ParamField body="error" type="(error: DOMException) => void" required>
      Callback invoked when an encoding error occurs. The encoder transitions to
      the `'closed'` state after an error.
    </ParamField>
  </Expandable>
</ParamField>

**Throws:** `TypeError` if callbacks are not functions.

<Accordion title="Example: Creating an encoder">
  ```typescript theme={null}
  const encoder = new AudioEncoder({
    output: (chunk, metadata) => {
      // Handle encoded chunk
      const data = new Uint8Array(chunk.byteLength);
      chunk.copyTo(data);

      // Check for decoder config (usually on first chunk)
      if (metadata?.decoderConfig) {
        console.log('Codec:', metadata.decoderConfig.codec);
        console.log('Sample rate:', metadata.decoderConfig.sampleRate);
        console.log('Channels:', metadata.decoderConfig.numberOfChannels);
      }
    },
    error: (err) => {
      console.error('Encoding failed:', err.message);
    }
  });
  ```
</Accordion>

***

## Properties

<ResponseField name="state" type="CodecState" required>
  Current encoder state. One of:

  * `'unconfigured'` - Not configured yet, or `reset()` was called
  * `'configured'` - Ready to encode audio data
  * `'closed'` - Encoder has been closed and cannot be used
</ResponseField>

<ResponseField name="encodeQueueSize" type="number" required>
  Number of pending encode operations. Useful for backpressure management
  when encoding large amounts of audio data.
</ResponseField>

***

## Static Methods

### isConfigSupported()

Check if an audio encoder configuration is supported on this platform.

```typescript theme={null}
static isConfigSupported(config: AudioEncoderConfig): Promise<AudioEncoderSupport>
```

<ParamField body="config" type="AudioEncoderConfig" required>
  Configuration to test for support.
</ParamField>

**Returns:** `Promise<AudioEncoderSupport>` - Resolves with support status and normalized config.

<Accordion title="Example: Checking codec support">
  ```typescript theme={null}
  const aacSupport = await AudioEncoder.isConfigSupported({
    codec: 'mp4a.40.2',
    sampleRate: 48000,
    numberOfChannels: 2,
    bitrate: 128_000
  });

  if (aacSupport.supported) {
    console.log('AAC encoding is supported');
    encoder.configure(aacSupport.config);
  } else {
    console.log('AAC encoding is NOT supported');
  }
  ```
</Accordion>

<Accordion title="Example: Fallback codec selection">
  ```typescript theme={null}
  // Try preferred codecs in order
  const codecs = ['mp4a.40.2', 'opus', 'mp3'];

  for (const codec of codecs) {
    const support = await AudioEncoder.isConfigSupported({
      codec,
      sampleRate: 48000,
      numberOfChannels: 2,
      bitrate: 128_000
    });

    if (support.supported) {
      console.log(`Using codec: ${codec}`);
      encoder.configure(support.config);
      break;
    }
  }
  ```
</Accordion>

***

## Instance Methods

### configure()

Configure the encoder with codec parameters. Must be called before encoding.

```typescript theme={null}
configure(config: AudioEncoderConfig): void
```

<ParamField body="config" type="AudioEncoderConfig" required>
  Encoder configuration specifying codec and audio parameters.

  <Expandable title="AudioEncoderConfig properties">
    <ParamField body="codec" type="string" required>
      Audio codec string. Common values:

      * `'mp4a.40.2'` - AAC-LC (most compatible)
      * `'opus'` - Opus (best quality/size ratio)
      * `'mp3'` - MP3 (legacy compatibility)
      * `'flac'` - FLAC (lossless)
      * `'vorbis'` - Vorbis (open source)
    </ParamField>

    <ParamField body="sampleRate" type="number" required>
      Sample rate in Hz. Common values: 44100, 48000, 96000.
    </ParamField>

    <ParamField body="numberOfChannels" type="number" required>
      Number of audio channels. Typically 1 (mono) or 2 (stereo).
    </ParamField>

    <ParamField body="bitrate" type="number">
      Target bitrate in bits per second. For example, `128_000` for 128 kbps.
      Not used for lossless codecs like FLAC.
    </ParamField>

    <ParamField body="bitrateMode" type="AudioBitrateMode">
      Bitrate control mode:

      * `'constant'` - Constant bitrate (CBR)
      * `'variable'` - Variable bitrate (VBR, default)
    </ParamField>
  </Expandable>
</ParamField>

**Throws:**

* `InvalidStateError` if encoder is closed
* `NotSupportedError` if codec is not supported

<Accordion title="Example: Configuring for AAC">
  ```typescript theme={null}
  encoder.configure({
    codec: 'mp4a.40.2',
    sampleRate: 48000,
    numberOfChannels: 2,
    bitrate: 256_000,
    bitrateMode: 'variable'
  });
  ```
</Accordion>

<Accordion title="Example: Configuring for Opus">
  ```typescript theme={null}
  encoder.configure({
    codec: 'opus',
    sampleRate: 48000,
    numberOfChannels: 2,
    bitrate: 96_000  // Opus is very efficient
  });
  ```
</Accordion>

***

### encode()

Queue an `AudioData` object for encoding.

```typescript theme={null}
encode(data: AudioData): void
```

<ParamField body="data" type="AudioData" required>
  The raw audio data to encode. Must have a valid format and timestamp.
</ParamField>

**Throws:**

* `InvalidStateError` if encoder is not configured
* `TypeError` if data is invalid

<Warning>
  Always call `audioData.close()` after encoding to prevent memory leaks. The encoder makes an internal copy of the data, so closing the source is safe.
</Warning>

<Accordion title="Example: Encoding audio frames">
  ```typescript theme={null}
  // Encode multiple audio frames
  for (let i = 0; i < audioFrames.length; i++) {
    const audioData = new AudioData({
      format: 'f32-planar',
      sampleRate: 48000,
      numberOfFrames: 1024,
      numberOfChannels: 2,
      timestamp: i * 21333,  // ~21ms per AAC frame
      data: audioFrames[i]
    });

    encoder.encode(audioData);
    audioData.close();  // Always close after encode
  }
  ```
</Accordion>

<Accordion title="Example: Backpressure handling">
  ```typescript theme={null}
  for (const samples of audioSamples) {
    const audioData = new AudioData({
      format: 'f32-planar',
      sampleRate: 48000,
      numberOfFrames: 1024,
      numberOfChannels: 2,
      timestamp,
      data: samples
    });

    encoder.encode(audioData);
    audioData.close();

    // Wait if queue gets too large
    if (encoder.encodeQueueSize > 10) {
      await new Promise(resolve => {
        encoder.addEventListener('dequeue', resolve, { once: true });
      });
    }
  }
  ```
</Accordion>

***

### addEventListener()

Add an event listener for encoder events.

```typescript theme={null}
addEventListener(type: string, listener: () => void, options?: { once?: boolean }): void
```

<ParamField body="type" type="string" required>
  Event type. Currently supports `'dequeue'` which fires when the encode queue size decreases.
</ParamField>

<ParamField body="listener" type="() => void" required>
  Callback function to invoke when the event fires.
</ParamField>

<ParamField body="options" type="object">
  Event listener options.

  <Expandable title="Options">
    <ParamField body="once" type="boolean">
      If `true`, the listener is automatically removed after being invoked once.
    </ParamField>
  </Expandable>
</ParamField>

<Accordion title="Example: Listening for dequeue events">
  ```typescript theme={null}
  // Monitor queue size
  encoder.addEventListener('dequeue', () => {
    console.log('Queue size:', encoder.encodeQueueSize);
  });

  // One-time listener for flow control
  await new Promise(resolve => {
    encoder.addEventListener('dequeue', resolve, { once: true });
  });
  ```
</Accordion>

***

### removeEventListener()

Remove a previously added event listener.

```typescript theme={null}
removeEventListener(type: string, listener: () => void): void
```

<ParamField body="type" type="string" required>
  Event type (e.g., `'dequeue'`).
</ParamField>

<ParamField body="listener" type="() => void" required>
  The callback function to remove. Must be the same function reference that was added.
</ParamField>

<Accordion title="Example: Removing a listener">
  ```typescript theme={null}
  const onDequeue = () => {
    console.log('Dequeue event fired');
  };

  encoder.addEventListener('dequeue', onDequeue);

  // Later, remove the listener
  encoder.removeEventListener('dequeue', onDequeue);
  ```
</Accordion>

***

### flush()

Wait for all pending encode operations to complete.

```typescript theme={null}
flush(): Promise<void>
```

**Returns:** `Promise<void>` - Resolves when all queued audio data has been encoded.

**Throws:**

* `InvalidStateError` if encoder is not configured
* Rejects if encoding fails

<Accordion title="Example: Flushing the encoder">
  ```typescript theme={null}
  // Encode all audio data
  for (const frame of audioFrames) {
    encoder.encode(frame);
    frame.close();
  }

  // Wait for all encoding to complete
  await encoder.flush();
  console.log('All audio encoded!');

  // Now safe to close
  encoder.close();
  ```
</Accordion>

***

### reset()

Reset the encoder to the unconfigured state.

```typescript theme={null}
reset(): void
```

Aborts all pending encode operations and resets to the `'unconfigured'` state.
You must call `configure()` again before encoding more audio.

**Throws:** `InvalidStateError` if encoder is closed

<Accordion title="Example: Resetting and reconfiguring">
  ```typescript theme={null}
  encoder.configure({
    codec: 'mp4a.40.2',
    sampleRate: 48000,
    numberOfChannels: 2
  });

  encoder.encode(audioData);

  // Abort and reconfigure with different settings
  encoder.reset();

  encoder.configure({
    codec: 'opus',
    sampleRate: 48000,
    numberOfChannels: 2
  });
  ```
</Accordion>

***

### close()

Close the encoder and release all resources.

```typescript theme={null}
close(): void
```

The encoder cannot be used after calling `close()`. Any pending encode operations
are aborted and the state transitions to `'closed'`.

<Accordion title="Example: Proper cleanup">
  ```typescript theme={null}
  // Encode all audio
  for (const frame of audioFrames) {
    encoder.encode(frame);
    frame.close();
  }

  // Wait for encoding to complete
  await encoder.flush();

  // Release resources
  encoder.close();

  // encoder.encode() will now throw InvalidStateError
  ```
</Accordion>

***

## Type Definitions

### AudioEncoderConfig

Configuration options for the audio encoder.

```typescript theme={null}
interface AudioEncoderConfig {
  codec: string;                     // Required: codec identifier
  sampleRate: number;                // Required: sample rate in Hz
  numberOfChannels: number;          // Required: channel count
  bitrate?: number;                  // Optional: target bitrate (bps)
  bitrateMode?: AudioBitrateMode;    // Optional: 'constant' | 'variable'
}
```

### AudioEncoderInit

Initialization callbacks for creating an AudioEncoder.

```typescript theme={null}
interface AudioEncoderInit {
  output: (chunk: EncodedAudioChunk, metadata?: AudioEncoderOutputMetadata) => void;
  error: (error: DOMException) => void;
}
```

### AudioEncoderOutputMetadata

Metadata provided with encoded audio chunks.

```typescript theme={null}
interface AudioEncoderOutputMetadata {
  decoderConfig?: {
    codec: string;              // Codec string for decoder
    sampleRate: number;         // Sample rate in Hz
    numberOfChannels: number;   // Channel count
    description?: ArrayBuffer;  // Codec-specific data (e.g., AudioSpecificConfig)
  };
}
```

### AudioEncoderSupport

Result from `isConfigSupported()` indicating codec support.

```typescript theme={null}
interface AudioEncoderSupport {
  supported: boolean;           // Whether the config is supported
  config: AudioEncoderConfig;   // The tested (possibly normalized) config
}
```

### AudioBitrateMode

Bitrate control mode for audio encoding.

```typescript theme={null}
type AudioBitrateMode = 'constant' | 'variable';
```

| Value        | Description                                                                   |
| ------------ | ----------------------------------------------------------------------------- |
| `'constant'` | Constant bitrate (CBR) - consistent file size, may waste bits on silence      |
| `'variable'` | Variable bitrate (VBR) - better quality/size ratio, recommended for most uses |

***

## See Also

<CardGroup cols={3}>
  <Card title="AudioData" icon="waveform" href="/api-reference/AudioData">
    Raw audio sample data for encoding
  </Card>

  <Card title="AudioDecoder" icon="volume-high" href="/api-reference/AudioDecoder">
    Decode EncodedAudioChunks back to AudioData
  </Card>

  <Card title="EncodedAudioChunk" icon="music" href="/api-reference/EncodedAudioChunk">
    Encoded audio chunk output
  </Card>
</CardGroup>
