Professional Audio Recording & Transcription Toolkit for Node.js
Voice-driven development workflows made simple
Features β’ Installation β’ Quick Start β’ Examples β’ API Reference β’ Documentation
@grunnverk/audio-tools is a comprehensive TypeScript library for recording, transcribing, and managing audio in Node.js applications. Built for developers who want to integrate voice-driven workflows, voice notes, audio documentation, or AI-powered transcription into their projects.
- ποΈ High-Quality Recording - Capture audio from any input device with configurable settings
- π¬ Visual Countdown Timers - Professional recording countdowns with ANSI colors and beeps
- π Device Management - List, select, and configure audio input devices
- π€ AI Transcription - Powered by OpenAI's Whisper API for accurate transcription
- π¦ Archive Management - Timestamped archiving of audio files and transcripts
- π§ Fully Typed - Complete TypeScript definitions for excellent IDE support
- πͺ΅ Flexible Logging - Optional Winston logger integration
- π Zero Config - Sensible defaults, works out of the box
- β Cross-platform support (macOS, Linux, Windows)
- β Device selection and configuration
- β Configurable sample rates and formats (WAV, MP3, FLAC)
- β Duration limits and manual stop controls
- β Custom output paths
- β Real-time recording status
- β ANSI color-coded terminal display
- β In-place updating (no screen clutter)
- β Audio beeps at configurable intervals
- β Warning colors when time is low
- β Callback support for custom behaviors
- β Graceful cleanup and process handling
- β OpenAI Whisper API integration
- β Automatic file archiving with timestamps
- β Transcript preservation in Markdown format
- β Batch processing support
- β Error recovery and retry logic
npm install @grunnverk/audio-toolsThe library has optional peer dependencies for enhanced functionality:
# For logging (recommended)
npm install winston
# For shared utilities (optional)
npm install @grunnverk/shared- Node.js: 18.x or later
- Operating System: macOS, Linux, or Windows
- Audio Input: Microphone or audio input device
import { recordAudio } from '@grunnverk/audio-tools';
// Record up to 60 seconds of audio
const result = await recordAudio({
duration: 60,
countdownDelay: 3,
});
console.log('Audio recorded:', result.filePath);
console.log('Duration:', result.duration, 'seconds');
console.log('File size:', result.fileSize, 'bytes');import { recordAudio, transcribeAudio, archiveAudio } from '@grunnverk/audio-tools';
// Record audio
const recording = await recordAudio({ duration: 120 });
// Transcribe with OpenAI Whisper
const transcript = await transcribeAudio(recording.filePath);
// Archive both audio and transcript with timestamps
const archive = await archiveAudio(
recording.filePath,
transcript,
'output/recordings'
);
console.log('Transcript:', transcript);
console.log('Archived to:', archive.audioPath);import { selectDeviceInteractive, recordAudio } from '@grunnverk/audio-tools';
// Let user select audio device interactively
const device = await selectDeviceInteractive();
// Record with selected device
const result = await recordAudio({ duration: 60 });import { CountdownTimer } from '@grunnverk/audio-tools';
const timer = new CountdownTimer({
durationSeconds: 30,
beepAt30Seconds: true,
redAt30Seconds: true,
onTick: (remaining) => {
console.log(`${remaining} seconds remaining`);
},
onComplete: () => {
console.log('Time\'s up!');
}
});
await timer.start();Comprehensive documentation is available:
- Getting Started Guide - Step-by-step tutorial for beginners
- Quick Reference - One-page cheat sheet
- CLI Examples - Build command-line tools
- FAQ - Frequently asked questions
- Architecture - Design and internals
- Contributing Guide - How to contribute
- Documentation Index - Complete documentation map
The examples/ directory contains comprehensive, runnable examples:
| Example | Description | File |
|---|---|---|
| Basic Recording | Simple audio recording with default settings | basic-recording.ts |
| Record & Transcribe | Complete workflow with transcription and archiving | record-and-transcribe.ts |
| Countdown Demo | Visual countdown timer demonstrations | countdown-demo.ts |
| Device Selection | List and select audio input devices | device-selection.ts |
| Custom Output | Specify custom paths and filenames | custom-output-path.ts |
cd examples
npm install
npm run basic # Basic recording
npm run transcribe # Record and transcribe (requires OPENAI_API_KEY)
npm run countdown # Countdown timer demos
npm run devices # Device selection
npm run custom-path # Custom output pathsRecord audio from an input device.
Options:
interface RecordingOptions {
device?: AudioDevice | string; // Audio device (optional, uses default)
duration?: number; // Max duration in seconds (optional)
outputPath?: string; // Output file path (optional, generates temp)
countdownDelay?: number; // Countdown delay (default: 3)
sampleRate?: number; // Sample rate in Hz (default: 44100)
format?: 'wav' | 'mp3' | 'flac'; // Audio format (default: 'wav')
}Returns:
interface RecordingResult {
filePath: string; // Path to recorded file
duration: number; // Recording duration in seconds
fileSize: number; // File size in bytes
}Example:
const result = await recordAudio({
duration: 60,
sampleRate: 48000,
format: 'wav',
countdownDelay: 3
});Archive audio file with its transcription.
Parameters:
audioPath: Path to the original audio filetranscript: Transcribed text contentoutputDir: Directory to save archived files (default: 'output')
Returns:
{
audioPath: string; // Path to archived audio file
transcriptPath: string; // Path to archived transcript
}Example:
const archive = await archiveAudio(
'recording.wav',
'This is the transcribed text...',
'output/archives'
);
// Creates files like:
// - output/archives/250701-1430-review-audio.wav
// - output/archives/250701-1430-review-transcript.mdDelete an audio file safely.
Example:
await deleteAudio('temp-recording.wav');Get the duration of an audio file (currently returns null, planned feature).
List all available audio input devices.
Returns:
interface AudioDevice {
id: string;
name: string;
isDefault: boolean;
}Example:
const devices = await listAudioDevices();
devices.forEach(device => {
console.log(`${device.name} (${device.id})`);
if (device.isDefault) {
console.log(' β Default device');
}
});Get the system's default audio input device.
Example:
const device = await getDefaultDevice();
if (device) {
console.log('Default device:', device.name);
}Find a device by its ID or name.
Example:
const device = await findDevice('MacBook Pro Microphone');
if (device) {
console.log('Found device:', device.id);
}Present an interactive menu to select an audio device.
Example:
const deviceId = await selectDeviceInteractive();
console.log('Selected device:', deviceId);Transcribe an audio file using OpenAI's Whisper API.
Requirements:
OPENAI_API_KEYenvironment variable must be set- Audio file must be in a supported format (WAV, MP3, FLAC, etc.)
Example:
const transcript = await transcribeAudio('recording.wav');
console.log('Transcription:', transcript);A visual countdown timer with customizable behavior.
Constructor Options:
interface CountdownOptions {
durationSeconds: number; // Duration in seconds
beepAt30Seconds?: boolean; // Beep at 30s (default: true)
redAt30Seconds?: boolean; // Red color at 30s (default: true)
onTick?: (remaining: number) => void; // Called every second
onComplete?: () => void; // Called when complete
clearOnComplete?: boolean; // Clear display when done (default: false)
}Methods:
start(): Promise<void>- Start the countdownstop(): void- Stop the countdowngetRemainingSeconds(): number- Get remaining timedestroy(): void- Clean up resourcesisTimerDestroyed(): boolean- Check if destroyed
Example:
const timer = new CountdownTimer({
durationSeconds: 60,
beepAt30Seconds: true,
redAt30Seconds: true,
onTick: (remaining) => {
if (remaining % 10 === 0) {
console.log(`${remaining} seconds left`);
}
},
onComplete: () => {
console.log('Recording complete!');
}
});
await timer.start();Convenience function to create and start a countdown timer.
Example:
await startCountdown({
durationSeconds: 30,
onComplete: () => console.log('Done!')
});Create a countdown timer with sensible defaults for audio recording.
Example:
const timer = createAudioRecordingCountdown(120);
await timer.start();Generate a timestamped filename for archived audio.
Example:
const filename = getTimestampedArchivedAudioFilename('.mp3');
// Returns: "250701-1430-review-audio.mp3"Generate a timestamped filename for archived transcripts.
Example:
const filename = getTimestampedArchivedTranscriptFilename();
// Returns: "250701-1430-review-transcript.md"Set a custom Winston logger instance.
Example:
import { setLogger } from '@grunnverk/audio-tools';
import { createLogger, format, transports } from 'winston';
const logger = createLogger({
level: 'debug',
format: format.combine(
format.timestamp(),
format.colorize(),
format.simple()
),
transports: [
new transports.Console(),
new transports.File({ filename: 'audio-tools.log' })
]
});
setLogger(logger);Get the current logger instance.
Example:
import { getLogger } from '@grunnverk/audio-tools';
const logger = getLogger();
logger.info('Starting recording...');Here's a complete example showing a typical workflow:
import {
recordAudio,
transcribeAudio,
archiveAudio,
deleteAudio,
CountdownTimer,
setLogger
} from '@grunnverk/audio-tools';
import { createLogger, format, transports } from 'winston';
import { config } from 'dotenv';
// Load environment variables
config();
// Configure logging
const logger = createLogger({
level: 'info',
format: format.combine(
format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
format.colorize(),
format.printf(({ timestamp, level, message }) =>
`${timestamp} [${level}]: ${message}`
)
),
transports: [
new transports.Console(),
new transports.File({ filename: 'audio-tools.log' })
]
});
setLogger(logger);
async function recordAndTranscribeVoiceNote() {
let audioPath: string | null = null;
try {
console.log('ποΈ Voice Note Recorder');
console.log('======================\n');
// Step 1: Countdown
console.log('Get ready to speak...\n');
const countdown = new CountdownTimer({
durationSeconds: 3,
beepAt30Seconds: false,
clearOnComplete: true
});
await countdown.start();
// Step 2: Record
console.log('π΄ Recording... (Press ENTER to stop)\n');
const recording = await recordAudio({
duration: 300, // 5 minutes max
sampleRate: 48000,
format: 'wav'
});
audioPath = recording.filePath;
logger.info(`Recorded ${recording.duration.toFixed(2)}s of audio`);
// Step 3: Transcribe
console.log('\nπ Transcribing...');
const transcript = await transcribeAudio(audioPath);
console.log('\nβ
Transcript:\n');
console.log('β'.repeat(60));
console.log(transcript);
console.log('β'.repeat(60));
// Step 4: Archive
console.log('\nπΎ Archiving...');
const archive = await archiveAudio(
audioPath,
transcript,
'output/voice-notes'
);
logger.info(`Archived to: ${archive.audioPath}`);
logger.info(`Transcript saved: ${archive.transcriptPath}`);
// Step 5: Cleanup
await deleteAudio(audioPath);
logger.info('Temporary file deleted');
console.log('\nβ
Voice note saved successfully!');
} catch (error) {
logger.error('Failed to process voice note:', error);
// Cleanup on error
if (audioPath) {
try {
await deleteAudio(audioPath);
} catch {
// Ignore cleanup errors
}
}
throw error;
}
}
// Run the example
recordAndTranscribeVoiceNote().catch(error => {
console.error('\nβ Error:', error.message);
process.exit(1);
});OPENAI_API_KEY- Required for transcription functionalityNO_COLOR- Disable ANSI colors in terminal outputTERM- Terminal type detection for ANSI support
The library uses @utilarium/unplayable which stores audio device preferences in:
~/.unplayable/audio-preferences.json
You can manually edit this file to set default devices.
- @utilarium/unplayable - Cross-platform audio recording
- @grunnverk/ai-service - OpenAI Whisper integration
- @grunnverk/shared - Optional shared utilities
- winston - Optional structured logging
| Platform | Status | Backend |
|---|---|---|
| macOS | β Supported | CoreAudio |
| Linux | β Supported | ALSA/PulseAudio |
| Windows | β Supported | WASAPI |
The library includes comprehensive test coverage:
# Run tests
npm test
# Run tests with coverage
npm run test
# Watch mode
npm run test -- --watch# Clone the repository
git clone https://github.com/grunnverk/audio-tools.git
cd audio-tools
# Install dependencies
npm install
# Build
npm run build
# Run tests
npm test
# Lint
npm run lintaudio-tools/
βββ src/
β βββ countdown.ts # Countdown timer utilities
β βββ devices.ts # Audio device management
β βββ recording.ts # Recording and archiving
β βββ transcription.ts # Transcription wrapper
β βββ types.ts # TypeScript definitions
β βββ index.ts # Main exports
βββ tests/ # Test files
βββ examples/ # Usage examples
βββ dist/ # Compiled output
βββ package.json
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch:
git checkout -b feature/my-feature - Make your changes with tests
- Run tests:
npm test - Commit with clear messages
- Push and open a Pull Request
Apache-2.0 License - see LICENSE file for details.
- @grunnverk/ai-service - AI services including transcription
- @grunnverk/shared - Shared utilities
- @utilarium/unplayable - Cross-platform audio library
- π Getting Started: Tutorial Guide
- π Documentation: Complete Docs
- π Issues: GitHub Issues
- π‘ Discussions: GitHub Discussions
- β FAQ: Frequently Asked Questions
See RELEASE_NOTES.md for version history and changes.
Made with β€οΈ by Tim O'Brien
β Star this repo if you find it useful!