play-pcm
v1.0.3
Published
A highly customizable PCM audio player with React component support. Play, manipulate, and export PCM audio with pitch control, speed adjustment, seeking, waveform visualization, and more.
Maintainers
Readme
play-pcm
A highly customizable PCM audio player with React component support. Play, manipulate, and export PCM audio with pitch control, speed adjustment, seeking, waveform visualization, and more.
Features
- 🎵 Play PCM Audio - Play Float32Array audio samples directly
- ⏯️ Full Playback Controls - Play, pause, stop, seek, skip forward/backward
- 🎚️ Volume Control - Adjust volume with mute/unmute support
- ⚡ Tempo/Speed Control - Adjust playback speed (0.25x to 4x) without affecting pitch
- 🎼 Pitch Control - Adjust pitch in semitones (-12 to +12) without affecting speed
- 🎹 Fine Pitch Control - Adjust pitch in cents for precise tuning
- 🔁 Loop Mode - Enable/disable looping
- 🌊 Waveform Visualization - Generate and display audio waveforms
- 📊 Audio Analysis - Real-time frequency and time-domain analysis
- 🎛️ Equalizer - Multi-band equalizer support
- 📁 Export Audio - Export to WAV, WebM, or OGG formats
- ⬇️ Download - Download audio files directly
- 🎨 Customizable UI - Fully themeable React component
- ⌨️ Keyboard Shortcuts - Built-in keyboard controls
- ♿ Accessible - ARIA labels and keyboard navigation
- 📱 Responsive - Mobile-friendly design
- 🔧 Audio Utilities - Comprehensive audio processing functions
- 📦 Tree-shakeable - Import only what you need
- 💪 TypeScript - Full TypeScript support with comprehensive types
Installation
npm install play-pcmyarn add play-pcmpnpm add play-pcmQuick Start
Simple Playback (No React)
import { playPCM } from 'play-pcm';
// Play PCM audio with default settings
const samples = new Float32Array([...]); // Your PCM samples
const player = playPCM(samples);
// Or with options
const player = playPCM(samples, {
sampleRate: 16000,
playbackRate: 1.3,
volume: 0.8,
});Using the PCMPlayer Class
import { PCMPlayer, createPCMPlayer } from 'play-pcm';
// Create a player instance
const player = createPCMPlayer({
sampleRate: 16000,
volume: 1,
playbackRate: 1,
});
// Load samples
player.loadSamples(samples);
// Control playback
await player.play();
player.pause();
player.stop();
// Seek
player.seek(5); // Seek to 5 seconds
player.seekPercent(50); // Seek to 50%
// Volume
player.setVolume(0.5);
player.mute();
player.unmute();
player.toggleMute();
// Speed/Tempo (without affecting pitch)
player.setTempo(1.5); // 1.5x speed
// Pitch (without affecting speed)
player.setPitch(2); // +2 semitones higher
player.setPitchCents(50); // Fine-tune by +50 cents
// Loop
player.setLoop(true);
player.toggleLoop();
// Events
player.on('timeUpdate', ({ currentTime, duration, progress }) => {
console.log(`Progress: ${progress * 100}%`);
});
player.on('ended', () => {
console.log('Playback ended');
});
// Export audio
const blob = await player.exportAudio({ format: 'wav' });
await player.downloadAudio('my-audio.wav');
// Cleanup
player.destroy();Speed and Pitch Control
The player provides independent control over speed (tempo) and pitch:
// Speed/Tempo - changes how fast audio plays WITHOUT affecting pitch
player.setTempo(1.5); // 1.5x speed, same pitch
player.setTempo(0.5); // 0.5x speed (slower), same pitch
// Pitch - changes pitch WITHOUT affecting speed
player.setPitch(3); // +3 semitones (higher pitch)
player.setPitch(-2); // -2 semitones (lower pitch)
// Fine-grained pitch control in cents (100 cents = 1 semitone)
player.setPitchCents(50); // +50 cents (half a semitone)
// Combined: slow tempo + higher pitch
player.setTempo(0.75);
player.setPitch(2);
// Get current values
const tempo = player.getTempo();
const pitch = player.getPitch();
const cents = player.getPitchCents();
const totalCents = player.getTotalPitchCents(); // semitones * 100 + centsNote: playbackRate and tempo are aliases when preservePitch is true. When preservePitch is false, changing playback rate also changes pitch (like vinyl speed change).
React Component
import { PCMPlayerComponent } from 'play-pcm';
function App() {
const samples = new Float32Array([...]); // Your PCM samples
return (
<PCMPlayerComponent
samples={samples}
options={{
sampleRate: 16000,
tempo: 1, // Speed without pitch change
pitchSemitones: 0, // Pitch adjustment
}}
showWaveform={true}
showSpeedControl={true}
showVolumeControl={true}
showPitchControl={true}
showDownloadButton={true}
title="My Audio"
artist="Artist Name"
onEnded={() => console.log('Ended')}
onTimeUpdate={(time, duration) => console.log(time, duration)}
/>
);
}React Hook
import { usePCMPlayer } from 'play-pcm';
function CustomPlayer({ samples }: { samples: Float32Array }) {
const {
state,
currentTime,
duration,
progress,
volume,
tempo, // Current tempo
pitch, // Current pitch in semitones
pitchCents, // Current pitch cents
isReady,
play,
pause,
toggle,
seek,
setVolume,
setTempo, // Set tempo (speed without pitch change)
setPlaybackRate, // Alias for setTempo
setPitch, // Set pitch in semitones
setPitchCents, // Set fine-grained pitch in cents
downloadAudio,
waveformData,
} = usePCMPlayer(samples, {
sampleRate: 16000,
tempo: 1,
pitchSemitones: 0,
});
return (
<div>
<button onClick={toggle}>
{state === 'playing' ? 'Pause' : 'Play'}
</button>
{/* Tempo slider */}
<label>
Speed: {tempo}x
<input
type="range"
min={0.25}
max={2}
step={0.1}
value={tempo}
onChange={(e) => setTempo(parseFloat(e.target.value))}
/>
</label>
{/* Pitch slider */}
<label>
Pitch: {pitch > 0 ? '+' : ''}{pitch} semitones
<input
type="range"
min={-12}
max={12}
step={1}
value={pitch}
onChange={(e) => setPitch(parseInt(e.target.value))}
/>
</label>
<span>{currentTime.toFixed(1)}s / {duration.toFixed(1)}s</span>
</div>
);
}API Reference
PCMPlayer Options
interface PCMPlayerOptions {
sampleRate?: number; // Sample rate in Hz (default: 16000)
channels?: number; // Number of channels (default: 1)
volume?: number; // Initial volume 0-1 (default: 1)
playbackRate?: number; // Playback speed (default: 1)
tempo?: number; // Tempo/speed without pitch change (default: 1)
pitchSemitones?: number; // Pitch in semitones -12 to +12 (default: 0)
pitchCents?: number; // Fine pitch in cents -1200 to +1200 (default: 0)
preservePitch?: boolean; // Preserve pitch when changing speed (default: true)
loop?: boolean; // Enable looping (default: false)
autoPlay?: boolean; // Auto-play on load (default: false)
fadeInDuration?: number; // Fade in duration in seconds (default: 0)
fadeOutDuration?: number; // Fade out duration in seconds (default: 0)
preGain?: number; // Pre-amplification in dB (default: 0)
}PCMPlayer Methods
Playback Controls
| Method | Description |
|--------|-------------|
| play() | Start playback (returns Promise) |
| pause() | Pause playback |
| stop() | Stop playback and reset position |
| toggle() | Toggle play/pause |
Seeking
| Method | Description |
|--------|-------------|
| seek(time: number) | Seek to specific time in seconds |
| seekPercent(percent: number) | Seek to percentage (0-100) |
| skipForward(seconds?: number) | Skip forward (default: 10s) |
| skipBackward(seconds?: number) | Skip backward (default: 10s) |
Volume
| Method | Description |
|--------|-------------|
| setVolume(volume: number) | Set volume (0-1) |
| getVolume() | Get current volume |
| mute() | Mute audio |
| unmute() | Unmute audio |
| toggleMute() | Toggle mute state |
| isMuted() | Check if muted |
Speed & Pitch
| Method | Description |
|--------|-------------|
| setPlaybackRate(rate: number) | Set playback speed (0.25-4) |
| getPlaybackRate() | Get current playback rate |
| setPitch(semitones: number) | Set pitch (-12 to +12 semitones) |
| getPitch() | Get current pitch |
Loop
| Method | Description |
|--------|-------------|
| setLoop(loop: boolean) | Enable/disable looping |
| isLooping() | Check if looping is enabled |
| toggleLoop() | Toggle loop mode |
State
| Method | Description |
|--------|-------------|
| getState() | Get current player state |
| getCurrentTime() | Get current playback time |
| getDuration() | Get total duration |
| getProgress() | Get playback progress (0-1) |
| getBufferInfo() | Get audio buffer information |
Events
| Method | Description |
|--------|-------------|
| on(event, handler) | Subscribe to an event |
| off(event, handler) | Unsubscribe from an event |
| once(event, handler) | Subscribe to an event once |
Audio Manipulation
| Method | Description |
|--------|-------------|
| loadSamples(samples, options?) | Load audio samples |
| getWaveformData(options?) | Generate waveform data |
| getAnalysisData() | Get real-time audio analysis |
Effects
| Method | Description |
|--------|-------------|
| setEqualizer(bands) | Set equalizer bands |
| setFadeIn(duration) | Set fade in duration |
| setFadeOut(duration) | Set fade out duration |
| setPreGain(gainDb) | Set pre-amplification |
Export
| Method | Description |
|--------|-------------|
| exportAudio(options?) | Export as Blob |
| downloadAudio(filename?, options?) | Download audio file |
Lifecycle
| Method | Description |
|--------|-------------|
| destroy() | Clean up resources |
| getSnapshot() | Get state snapshot |
| restoreSnapshot(snapshot) | Restore from snapshot |
Events
| Event | Payload | Description |
|-------|---------|-------------|
| stateChange | { state, previousState } | Player state changed |
| timeUpdate | { currentTime, duration, progress } | Playback time updated |
| volumeChange | { volume, muted } | Volume or mute changed |
| playbackRateChange | { playbackRate } | Playback rate changed |
| pitchChange | { pitchSemitones } | Pitch changed |
| seek | { currentTime, duration } | Seeked to position |
| ended | { looped } | Playback ended |
| error | { error, message } | Error occurred |
| loaded | { duration, sampleRate, channels } | Audio loaded |
| play | { currentTime } | Playback started |
| pause | { currentTime } | Playback paused |
| stop | {} | Playback stopped |
| loopChange | { loop } | Loop mode changed |
React Component Props
interface PCMPlayerComponentProps {
samples: Float32Array | Float32Array[];
options?: PCMPlayerOptions;
theme?: PCMPlayerTheme;
showWaveform?: boolean; // default: true
showSpeedControl?: boolean; // default: true
showVolumeControl?: boolean; // default: true
showTimeDisplay?: boolean; // default: true
showDownloadButton?: boolean; // default: true
showLoopButton?: boolean; // default: true
showPitchControl?: boolean; // default: false
className?: string;
style?: React.CSSProperties;
width?: string | number; // default: '100%'
height?: string | number; // default: 'auto'
onReady?: (player) => void;
onEnded?: () => void;
onTimeUpdate?: (currentTime, duration) => void;
onStateChange?: (state) => void;
onError?: (error) => void;
title?: string;
artist?: string;
coverImage?: string;
minPlaybackRate?: number; // default: 0.25
maxPlaybackRate?: number; // default: 4
keyboardShortcuts?: boolean; // default: true
ariaLabel?: string;
}Theme Customization
interface PCMPlayerTheme {
primaryColor?: string; // default: '#3b82f6'
secondaryColor?: string; // default: '#e5e7eb'
textColor?: string; // default: '#1f2937'
backgroundColor?: string; // default: '#ffffff'
progressColor?: string; // default: '#3b82f6'
progressBackgroundColor?: string; // default: '#e5e7eb'
borderRadius?: string; // default: '8px'
fontFamily?: string;
fontSize?: string; // default: '14px'
buttonSize?: string; // default: '40px'
spacing?: string; // default: '12px'
boxShadow?: string;
}Audio Utilities
import {
// Conversion
int16ToFloat32,
float32ToInt16,
uint8ToFloat32,
arrayBufferToFloat32,
// Channel manipulation
mergeChannels,
splitChannels,
interleaveChannels,
// Audio processing
normalize,
applyGain,
fadeIn,
fadeOut,
trimSilence,
resample,
reverse,
concat,
slice,
mix,
// Analysis
generateWaveform,
calculateRMS,
calculatePeak,
getDuration,
// Export
createWavBlob,
createAudioURL,
downloadAudio,
// Generation
generateSilence,
generateSineWave,
generateWhiteNoise,
} from 'play-pcm';Examples
Converting PCM from Different Sources
import { int16ToFloat32, uint8ToFloat32, arrayBufferToFloat32 } from 'play-pcm';
// From Int16Array (common for Web Audio API)
const int16Data = new Int16Array([...]);
const samples = int16ToFloat32(int16Data);
// From raw bytes (e.g., WebSocket)
const rawBytes = new Uint8Array([...]);
const samples = uint8ToFloat32(rawBytes, 2); // 2 bytes per sample (16-bit)
// From ArrayBuffer (e.g., fetch response)
const response = await fetch('audio.pcm');
const buffer = await response.arrayBuffer();
const samples = arrayBufferToFloat32(buffer);Audio Processing
import { normalize, fadeIn, fadeOut, concat, resample } from 'play-pcm';
// Normalize audio
const normalized = normalize(samples, 0.9); // 90% peak
// Add fade effects
const withFadeIn = fadeIn(samples, 44100); // 1 second fade at 44.1kHz
const withFadeOut = fadeOut(samples, 44100);
// Concatenate audio
const combined = concat(samples1, samples2, samples3);
// Resample to different rate
const resampled = resample(samples, 16000, 44100); // 16kHz to 44.1kHzCustom Waveform Visualization
import { usePCMPlayer, useAudioVisualization } from 'play-pcm';
function WaveformVisualizer({ samples }: { samples: Float32Array }) {
const canvasRef = useRef<HTMLCanvasElement>(null);
const { player } = usePCMPlayer(samples);
useAudioVisualization(player, canvasRef, {
type: 'bars', // 'bars' | 'waveform' | 'circle'
color: '#3b82f6',
backgroundColor: 'transparent',
barWidth: 3,
barGap: 1,
});
return <canvas ref={canvasRef} width={300} height={100} />;
}Dark Theme
<PCMPlayerComponent
samples={samples}
theme={{
primaryColor: '#6366f1',
secondaryColor: '#374151',
textColor: '#f9fafb',
backgroundColor: '#1f2937',
progressColor: '#6366f1',
progressBackgroundColor: '#374151',
}}
/>Export and Download
const player = createPCMPlayer();
player.loadSamples(samples);
// Export as WAV blob
const wavBlob = await player.exportAudio({
format: 'wav',
bitDepth: 16,
sampleRate: 44100,
normalize: true,
});
// Export specific portion
const portion = await player.exportAudio({
startTime: 5,
endTime: 10,
});
// Direct download
await player.downloadAudio('my-audio.wav', {
format: 'wav',
bitDepth: 24,
});Keyboard Shortcuts
When keyboardShortcuts is enabled (default):
| Key | Action | |-----|--------| | Space | Play/Pause | | ← | Skip back 5 seconds | | → | Skip forward 5 seconds | | ↑ | Volume up 10% | | ↓ | Volume down 10% | | M | Toggle mute | | L | Toggle loop |
Browser Support
- Chrome 66+
- Firefox 57+
- Safari 14.1+
- Edge 79+
TypeScript Support
Full TypeScript support with comprehensive type definitions. Import types directly:
import type {
PCMPlayerOptions,
PCMPlayerInstance,
PlayerState,
PlayerEventType,
WaveformData,
AudioExportOptions,
PCMPlayerTheme,
PCMPlayerComponentProps,
UsePCMPlayerReturn,
} from 'play-pcm';License
MIT © 2026
Contributing
Contributions are welcome! Please read our contributing guidelines before submitting a PR.
Changelog
See CHANGELOG.md for release history.
