# ngx-audio-wave

A modern, accessible audio wave visualization component for Angular 22+ with comprehensive keyboard navigation and screen reader support.

## Features

- 🎵 **Audio Wave Visualization** - Beautiful SVG-based audio wave display
- ⌨️ **Full Keyboard Support** - Complete keyboard navigation (Space, Enter, Arrow keys, Home, End)
- ♿ **Accessibility First** - WCAG 2.1 compliant with ARIA support
- 🎨 **Highly Customizable** - Colors, dimensions, gaps, and styling options
- 📱 **Responsive Design** - Works on all screen sizes
- 🚀 **Modern Angular** - Built with Angular 22+ signals and standalone components
- 🎯 **TypeScript** - Full type safety and IntelliSense support

## Demo

**🎮 [Live Demo](https://joldibaev.github.io/ngx-audio-wave/)** - Try the interactive demo

![Audio Wave Demo](https://github.com/joldibaev/ngx-audio-wave/raw/master/public/demo3.png)

## Quick Start

### Installation

```bash
npm install ngx-audio-wave
```

### Angular 22+ (Standalone Components)

```ts
import { NgxAudioWave } from 'ngx-audio-wave';

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [NgxAudioWave],
  template: `
    <ngx-audio-wave
      ariaLabel="My audio player"
      audioSrc="path/to/your/audio.mp3"
    >
    </ngx-audio-wave>
  `,
})
export class AppComponent {}
```

If your app does not already provide Angular HTTP services, add `provideHttpClient()` to your app config.

Remote audio files must allow browser CORS access because the component downloads the file to generate waveform data.

## Basic Usage

```html
<ngx-audio-wave
  ariaLabel="My podcast episode"
  audioSrc="https://example.com/audio.mp3"
>
</ngx-audio-wave>
```

### With Progress Information

```html
<ngx-audio-wave #player audioSrc="assets/audio.mp3"></ngx-audio-wave>

<div class="audio-info">
  <p>Progress: {{ player.exactPlayedPercent() | number:'1.1-1' }}%</p>
  <p>
    Time: {{ player.exactCurrentTime() | number:'1.1-1' }}s / {{
    player.exactDuration() | number:'1.1-1' }}s
  </p>
  <p>Status: {{ player.statusText() }}</p>
</div>
```

## Examples

### Custom Styling

```html
<ngx-audio-wave
  [gap]="3"
  [height]="50"
  [rounded]="false"
  audioSrc="assets/audio.mp3"
  color="#ff6b6b"
>
</ngx-audio-wave>
```

### With Custom Accessibility Labels

```html
<ngx-audio-wave
  ariaLabel="Weekly Tech Podcast Episode 42"
  audioSrc="assets/podcast.mp3"
  pauseButtonLabel="Pause podcast"
  playButtonLabel="Start podcast"
  progressBarLabel="Podcast progress - use arrow keys to navigate"
>
</ngx-audio-wave>
```

### Hide Default Button

```html
<ngx-audio-wave [hideBtn]="true" #audioPlayer audioSrc="assets/audio.mp3">
</ngx-audio-wave>

<button (click)="audioPlayer.play()">▶️ Play</button>
<button (click)="audioPlayer.pause()">⏸️ Pause</button>
<button (click)="audioPlayer.stop()">⏹️ Stop</button>
```

### Volume Control

```html
<ngx-audio-wave #player audioSrc="assets/audio.mp3"></ngx-audio-wave>

<div class="volume-controls">
  <button (click)="player.setVolume(0)">🔇 Mute</button>
  <button (click)="player.setVolume(0.5)">50% Volume</button>
  <button (click)="player.setVolume(1)">100% Volume</button>
  <button (click)="player.setVolume(player.currentVolume() === 0 ? 1 : 0)">
    Toggle
  </button>
</div>

<p>Current volume: {{ player.currentVolume() | percent }}</p>
```

### Playback Speed Control

```html
<ngx-audio-wave #player audioSrc="assets/audio.mp3"></ngx-audio-wave>

<div class="speed-controls">
  <button (click)="player.setPlaybackRate(player.currentPlaybackRate() - 0.25)">
    ⏪ Slower
  </button>
  <button (click)="player.setPlaybackRate(1)">1x Normal</button>
  <button (click)="player.setPlaybackRate(player.currentPlaybackRate() + 0.25)">
    ⏩ Faster
  </button>
  <button (click)="player.setPlaybackRate(0.5)">0.5x</button>
  <button (click)="player.setPlaybackRate(1.5)">1.5x</button>
  <button (click)="player.setPlaybackRate(2)">2x</button>
</div>

<p>Current speed: {{ player.currentPlaybackRate() }}x</p>
```

### Loop Control

```html
<ngx-audio-wave #player audioSrc="assets/audio.mp3"></ngx-audio-wave>

<div class="loop-controls">
  <button (click)="player.setLoop(true)">🔄 Enable Loop</button>
  <button (click)="player.setLoop(false)">⏹️ Disable Loop</button>
  <button (click)="player.setLoop(!player.isLooping())">
    🔄/⏹️ Toggle Loop
  </button>
</div>

<p>Looping: {{ player.isLooping() ? 'Enabled' : 'Disabled' }}</p>

<!-- Auto-loop example -->
<ngx-audio-wave
  [loop]="true"
  ariaLabel="Background music with auto-loop"
  audioSrc="assets/background-music.mp3"
>
</ngx-audio-wave>
```

### Different Heights and Gaps

```html
<!-- Different heights -->
<ngx-audio-wave [height]="25" audioSrc="assets/audio.mp3"></ngx-audio-wave>
<ngx-audio-wave [height]="50" audioSrc="assets/audio.mp3"></ngx-audio-wave>
<ngx-audio-wave [height]="100" audioSrc="assets/audio.mp3"></ngx-audio-wave>

<!-- Different gaps -->
<ngx-audio-wave [gap]="1" audioSrc="assets/audio.mp3"></ngx-audio-wave>
<ngx-audio-wave [gap]="5" audioSrc="assets/audio.mp3"></ngx-audio-wave>
<ngx-audio-wave [gap]="10" audioSrc="assets/audio.mp3"></ngx-audio-wave>
```

## Accessibility Features

This component is built with accessibility as a core feature, ensuring it works seamlessly with assistive technologies and keyboard navigation.

### Keyboard Navigation

| Key                | Action                                   |
| ------------------ | ---------------------------------------- |
| `Space` or `Enter` | Play/Pause audio                         |
| `Arrow Left`       | Skip backward (configurable with `skip`) |
| `Arrow Right`      | Skip forward (configurable with `skip`)  |
| `Home`             | Jump to beginning                        |
| `End`              | Jump to end                              |

### Screen Reader Support

- **ARIA Labels**: All interactive elements have descriptive labels
- **Live Regions**: Status changes are announced automatically
- **Progress Information**: Detailed progress text with time and percentage
- **Semantic Roles**: Proper ARIA roles for all components
- **Focus Management**: Logical tab order and focus indicators

### Screen Reader Testing

Recommended manual test matrix before release:

- **NVDA** (Windows)
- **JAWS** (Windows)
- **VoiceOver** (macOS/iOS)
- **TalkBack** (Android)

## API Reference

### Input Properties

| Property           | Type                | Default                | Description                                |
| ------------------ | ------------------- | ---------------------- | ------------------------------------------ |
| `audioSrc`         | `string \| SafeUrl` | **Required**           | URL or SafeUrl of the audio file to play   |
| `color`            | `string`            | `'#1e90ff'`            | Color of the audio wave bars               |
| `height`           | `number`            | `25`                   | Height of the wave visualization in pixels |
| `gap`              | `number`            | `5`                    | Gap between wave bars in pixels            |
| `samples`          | `number`            | `50`                   | Number of waveform bars                    |
| `rounded`          | `boolean`           | `true`                 | Whether to round the corners of wave bars  |
| `hideBtn`          | `boolean`           | `false`                | Hide the play/pause button                 |
| `skip`             | `number`            | `5`                    | Seconds to skip when using arrow keys      |
| `volume`           | `number`            | `1`                    | Initial volume level (0-1)                 |
| `playbackRate`     | `number`            | `1`                    | Initial playback speed (0.25-4)            |
| `loop`             | `boolean`           | `false`                | Whether to loop the audio                  |
| `ariaLabel`        | `string`            | `'Audio player'`       | Main ARIA label for the component          |
| `playButtonLabel`  | `string`            | `'Play audio'`         | ARIA label for the play button             |
| `pauseButtonLabel` | `string`            | `'Pause audio'`        | ARIA label for the pause button            |
| `progressBarLabel` | `string`            | `'Audio progress bar'` | ARIA label for the progress bar            |

### Output Properties (Signals)

| Property              | Type              | Description                                     |
| --------------------- | ----------------- | ----------------------------------------------- |
| `isPaused`            | `Signal<boolean>` | Whether the audio is currently paused           |
| `isLoading`           | `Signal<boolean>` | Whether the audio is currently loading          |
| `hasError`            | `Signal<boolean>` | Whether there was an error loading the audio    |
| `currentVolume`       | `Signal<number>`  | Current volume level (0-1)                      |
| `currentPlaybackRate` | `Signal<number>`  | Current playback speed (0.25-4)                 |
| `isLooping`           | `Signal<boolean>` | Whether the audio is currently looping          |
| `exactCurrentTime`    | `Signal<number>`  | Current playback time in seconds (exact)        |
| `exactDuration`       | `Signal<number>`  | Total duration in seconds (exact)               |
| `exactPlayedPercent`  | `Signal<number>`  | Playback progress as percentage (exact)         |
| `progressText`        | `Signal<string>`  | Human-readable progress text for screen readers |
| `statusText`          | `Signal<string>`  | Current status text for screen readers          |

### Methods

| Method                   | Parameters       | Description                                                          |
| ------------------------ | ---------------- | -------------------------------------------------------------------- |
| `play(time?: number)`    | `time?: number`  | Play the audio, optionally from a specific time                      |
| `pause()`                | -                | Pause the audio                                                      |
| `stop()`                 | -                | Stop the audio and reset to beginning                                |
| `setVolume(volume)`      | `volume: number` | Set volume level (0-1)                                               |
| `mute()`                 | -                | **Deprecated** - Use `setVolume(0)`                                  |
| `unmute()`               | -                | **Deprecated** - Use `setVolume(value)`                              |
| `toggleMute()`           | -                | **Deprecated** - Use `setVolume(currentVolume() === 0 ? value : 0)`  |
| `setPlaybackRate(rate)`  | `rate: number`   | Set playback speed (0.25-4)                                          |
| `resetPlaybackRate()`    | -                | **Deprecated** - Use `setPlaybackRate(1)`                            |
| `increasePlaybackRate()` | -                | **Deprecated** - Use `setPlaybackRate(currentPlaybackRate() + 0.25)` |
| `decreasePlaybackRate()` | -                | **Deprecated** - Use `setPlaybackRate(currentPlaybackRate() - 0.25)` |
| `setLoop(loop)`          | `loop: boolean`  | Enable or disable audio looping                                      |
| `enableLoop()`           | -                | **Deprecated** - Use `setLoop(true)`                                 |
| `disableLoop()`          | -                | **Deprecated** - Use `setLoop(false)`                                |
| `toggleLoop()`           | -                | **Deprecated** - Use `setLoop(!isLooping())`                         |

## Breaking Changes in v22

- Removed deprecated rounded state aliases: `currentTime`, `duration`, and
  `playedPercent`.
- Use `exactCurrentTime`, `exactDuration`, and `exactPlayedPercent` instead.
- Deprecated convenience methods remain available in v22 and are scheduled for
  removal in v23: `mute()`, `unmute()`, `toggleMute()`,
  `resetPlaybackRate()`, `increasePlaybackRate()`, `decreasePlaybackRate()`,
  `enableLoop()`, `disableLoop()`, and `toggleLoop()`.

## Compatibility Notes

- Angular: `>=22.0.0 <23.0.0`
- Node.js: `^22.22.3 || ^24.15.0 || >=26.0.0`
- `provideHttpClient()` is required by the current waveform loader.
- Large audio files are downloaded and decoded in the browser for waveform generation. Prefer smaller preview files for long podcasts or many players on one page.

## Contributing

We welcome contributions! Please feel free to submit issues and pull requests.

## License

MIT License - see [LICENSE](LICENSE) file for details.

## Support

- 📧 Email: nurlan@joldibaev.uz
- 🐛 Issues: [GitHub Issues](https://github.com/joldibaev/ngx-audio-wave/issues)
- 💬 Discussions: [GitHub Discussions](https://github.com/joldibaev/ngx-audio-wave/discussions)

---

**Made with ❤️ by [Joldibaev Nurlan](https://github.com/joldibaev)**
