UNPKG

angular-voice

Version:

Angular Voice Recorder, a standalone Angular component for recording audio with live preview and glassy UI.

1 lines 24.6 kB
{"version":3,"file":"angular-voice.mjs","sources":["../../../projects/angular-voice/src/lib/recorder-service.ts","../../../projects/angular-voice/src/lib/angular-voice.ts","../../../projects/angular-voice/src/lib/angular-voice.html","../../../projects/angular-voice/src/angular-voice.ts"],"sourcesContent":["import { Injectable } from '@angular/core';\n\nexport type RecorderFormat = 'webm' | 'wav';\n\nexport interface RecordingResult {\n blob: Blob; // raw audio data\n file: File; // audio as a File (can be uploaded/saved)\n durationMs: number; // recording duration in milliseconds\n mimeType: string; // type of audio (webm/wav)\n}\n\n/**\n * AudioRecorderService\n * --------------------\n * This service lets you record audio from the user's microphone.\n * It supports two formats:\n * - WEBM (default, smaller file size, better quality)\n * - WAV (fallback, bigger files but works everywhere)\n *\n * How it works:\n * 1. Ask the browser for microphone access.\n * 2. Start recording using MediaRecorder (WEBM) or AudioWorklet (WAV).\n * 3. Stop recording and return the audio as File + Blob.\n */\n@Injectable({ providedIn: 'root' })\nexport class AudioRecorderService {\n // MediaRecorder path (for webm)\n private mediaStream?: MediaStream;\n private mediaRecorder?: MediaRecorder;\n private chunks: Blob[] = [];\n\n // WAV path (AudioWorklet)\n private audioCtx?: AudioContext;\n private sourceNode?: MediaStreamAudioSourceNode;\n private workletNode?: AudioWorkletNode;\n private pcmBuffers: Float32Array[] = [];\n private sampleRate = 48000;\n\n private startTs = 0;\n\n /** Quick check: are we in a browser with mic support? */\n get isBrowser() {\n return typeof window !== 'undefined' && !!navigator?.mediaDevices;\n }\n\n /** Ask the user for microphone permission (runs once) */\n async prepare(): Promise<void> {\n if (!this.isBrowser) return;\n if (this.mediaStream) return; // already prepared\n\n this.mediaStream = await navigator.mediaDevices.getUserMedia({ audio: true });\n }\n\n /**\n * Start recording\n * @param format 'webm' (default) or 'wav'\n * @param desiredSampleRate optional sample rate (e.g., 44100)\n */\n async start(format: RecorderFormat = 'webm', desiredSampleRate?: number): Promise<void> {\n if (!this.isBrowser) throw new Error('Recording only works in browser.');\n await this.prepare();\n\n this.chunks = [];\n this.pcmBuffers = [];\n this.startTs = performance.now();\n\n // --- Case 1: WEBM (MediaRecorder, smaller, modern) ---\n if (format === 'webm' && this.supportsMediaRecorder('audio/webm')) {\n const mimeType = this.pickWebmMime();\n this.mediaRecorder = new MediaRecorder(this.mediaStream!, { mimeType });\n this.mediaRecorder.ondataavailable = (e) => {\n if (e.data?.size) this.chunks.push(e.data);\n };\n this.mediaRecorder.start();\n return;\n }\n\n // --- Case 2: WAV (AudioWorklet, fallback) ---\n this.audioCtx = new AudioContext({ sampleRate: desiredSampleRate ?? undefined });\n this.sampleRate = this.audioCtx.sampleRate;\n\n // Build a tiny recorder processor dynamically\n const workletCode = `\n class RecorderProcessor extends AudioWorkletProcessor {\n process(inputs) {\n const input = inputs[0];\n if (input && input[0]) {\n this.port.postMessage(input[0]); // send raw PCM data\n }\n return true;\n }\n }\n registerProcessor('recorder-processor', RecorderProcessor);\n `;\n const blob = new Blob([workletCode], { type: 'application/javascript' });\n const url = URL.createObjectURL(blob);\n await this.audioCtx.audioWorklet.addModule(url);\n\n this.sourceNode = this.audioCtx.createMediaStreamSource(this.mediaStream!);\n this.workletNode = new AudioWorkletNode(this.audioCtx, 'recorder-processor');\n this.workletNode.port.onmessage = (event) => {\n // Copy the audio data so it’s safe\n this.pcmBuffers.push(new Float32Array(event.data));\n };\n\n this.sourceNode.connect(this.workletNode);\n this.workletNode.connect(this.audioCtx.destination);\n }\n\n /**\n * Stop recording and return the result\n */\n async stop(format: RecorderFormat = 'webm'): Promise<RecordingResult> {\n const durationMs = performance.now() - this.startTs;\n\n // --- WEBM path ---\n if (format === 'webm' && this.mediaRecorder) {\n const recorder = this.mediaRecorder;\n const stopPromise = new Promise<void>((resolve) => {\n recorder.onstop = () => resolve();\n });\n if (recorder.state !== 'inactive') recorder.stop();\n await stopPromise;\n\n const blob = new Blob(this.chunks, { type: recorder.mimeType || 'audio/webm' });\n const file = new File([blob], `recording-${Date.now()}.webm`, { type: blob.type });\n this.cleanupMediaRecorder();\n return { blob, file, durationMs, mimeType: blob.type };\n }\n\n // --- WAV path ---\n const wavBlob = this.encodeWavFromPcm(this.pcmBuffers, this.sampleRate);\n const file = new File([wavBlob], `recording-${Date.now()}.wav`, { type: 'audio/wav' });\n this.cleanupWav();\n return { blob: wavBlob, file, durationMs, mimeType: 'audio/wav' };\n }\n\n /** Pause recording (if supported) */\n pause(): void {\n if (this.mediaRecorder && this.mediaRecorder.state === 'recording') {\n this.mediaRecorder.pause?.();\n }\n if (this.workletNode) {\n this.workletNode.port.onmessage = null; // stop collecting\n }\n }\n\n /** Resume recording (if supported) */\n resume(): void {\n if (this.mediaRecorder && this.mediaRecorder.state === 'paused') {\n this.mediaRecorder.resume?.();\n }\n if (this.workletNode) {\n this.workletNode.port.onmessage = (event) => {\n this.pcmBuffers.push(new Float32Array(event.data));\n };\n }\n }\n\n /** --- Helpers below --- */\n\n /** Check if MediaRecorder supports a mime type */\n private supportsMediaRecorder(mime: string): boolean {\n return typeof MediaRecorder !== 'undefined' && MediaRecorder.isTypeSupported?.(mime);\n }\n\n /** Pick the best WEBM mime type available */\n private pickWebmMime(): string {\n const candidates = [\n 'audio/webm;codecs=opus',\n 'audio/webm',\n 'audio/webm;codecs=pcm',\n ];\n for (const c of candidates) if (this.supportsMediaRecorder(c)) return c;\n return 'audio/webm';\n }\n\n /** Cleanup for webm recorder */\n private cleanupMediaRecorder() {\n this.mediaRecorder = undefined;\n this.chunks = [];\n }\n\n /** Cleanup for wav recorder */\n private cleanupWav() {\n try {\n this.workletNode?.disconnect();\n this.sourceNode?.disconnect();\n this.audioCtx?.close();\n } catch { }\n this.workletNode = undefined;\n this.sourceNode = undefined;\n this.audioCtx = undefined;\n this.pcmBuffers = [];\n }\n\n /**\n * Convert PCM float audio to a WAV Blob (mono, 16-bit PCM)\n */\n private encodeWavFromPcm(buffers: Float32Array[], sampleRate: number): Blob {\n // Join all audio chunks into one array\n const totalLength = buffers.reduce((acc, b) => acc + b.length, 0);\n const interleaved = new Float32Array(totalLength);\n let offset = 0;\n for (const b of buffers) {\n interleaved.set(b, offset);\n offset += b.length;\n }\n\n // Convert to 16-bit PCM\n const pcm16 = new Int16Array(interleaved.length);\n for (let i = 0; i < interleaved.length; i++) {\n const s = Math.max(-1, Math.min(1, interleaved[i]));\n pcm16[i] = s < 0 ? s * 0x8000 : s * 0x7fff;\n }\n\n // Build WAV file header\n const blockAlign = 1 * 16 / 8;\n const byteRate = sampleRate * blockAlign;\n const dataSize = pcm16.length * 2;\n const buffer = new ArrayBuffer(44 + dataSize);\n const view = new DataView(buffer);\n\n let p = 0;\n const writeStr = (s: string) => { for (let i = 0; i < s.length; i++) view.setUint8(p++, s.charCodeAt(i)); };\n const write16 = (v: number) => { view.setUint16(p, v, true); p += 2; };\n const write32 = (v: number) => { view.setUint32(p, v, true); p += 4; };\n\n writeStr('RIFF');\n write32(36 + dataSize);\n writeStr('WAVE');\n writeStr('fmt ');\n write32(16);\n write16(1);\n write16(1);\n write32(sampleRate);\n write32(byteRate);\n write16(blockAlign);\n write16(16);\n writeStr('data');\n write32(dataSize);\n\n // PCM samples\n let idx = 44;\n const u8 = new Uint8Array(buffer);\n for (let i = 0; i < pcm16.length; i++, idx += 2) {\n u8[idx] = pcm16[i] & 0xff;\n u8[idx + 1] = (pcm16[i] >> 8) & 0xff;\n }\n\n return new Blob([buffer], { type: 'audio/wav' });\n }\n}\n","import { Component, computed, EventEmitter, inject, input, model, Output, signal } from '@angular/core';\nimport { AudioRecorderService, RecordingResult } from './recorder-service';\n\n/**\n * AngularVoice Component\n * ----------------------\n * A reusable audio recorder UI + logic component.\n * Handles recording, stopping, previewing, and sending audio files.\n */\n@Component({\n selector: 'angular-voice',\n imports: [],\n templateUrl: './angular-voice.html',\n styleUrls: ['./angular-voice.css']\n})\nexport class AngularVoice {\n // Service that actually records audio\n private recorder = inject(AudioRecorderService);\n\n // Keeps track of the last blob URL we created (so we can clean it up)\n private previousUrl: string | null = null;\n\n // The recorded file once we have one\n protected activeRecordingFile: File | null = null;\n\n // Whether preview mode is shown\n previewRecord = model<boolean>(false);\n\n // Whether recording is active\n recording = model<boolean>(false);\n\n // Inputs to customize the component\n displayBtnsLabels = input<boolean>(true); // show/hide button labels\n startRecordingBtnLabel = input<string>('Start recording');\n recordingBtnLabel = input<string>('Recording...');\n startBtnClass = input<string>(''); // extra CSS classes for \"start\" button\n recordingBtnClass = input<string>(''); // extra CSS classes for \"recording\" button\n\n // Holds the current recording result (blob + file)\n result = signal<RecordingResult | null>(null);\n\n // Computed signal: returns an object URL for the current recording blob\n audioUrl = computed(() => {\n const blob = this.result()?.blob;\n\n // If there’s no blob, revoke old URL and return null\n if (!blob) {\n if (this.previousUrl) {\n URL.revokeObjectURL(this.previousUrl);\n this.previousUrl = null;\n }\n return null;\n }\n\n // Revoke old URL and create a new one for the new blob\n if (this.previousUrl) {\n URL.revokeObjectURL(this.previousUrl);\n }\n\n this.previousUrl = URL.createObjectURL(blob);\n return this.previousUrl;\n });\n\n // Event emitter: lets parent components know when recording is done\n @Output() recordingCompleted: EventEmitter<File | null> = new EventEmitter<File | null>();\n\n /**\n * Starts or stops recording depending on the current state.\n */\n async toggleRecord() {\n // Case 1: Not recording yet → start recording\n if (!this.recording() && !this.result()) {\n this.result.set(null);\n await this.recorder.start('webm');\n this.recording.set(true);\n this.recordingCompleted.emit(null); // notify parent that recording started\n }\n // Case 2: Already recording → stop and save\n else if (this.recording()) {\n const result = await this.recorder.stop('webm');\n this.recording.set(false);\n\n if (result) {\n this.result.set(result);\n await this.send();\n }\n }\n }\n\n /**\n * Sends the recorded file to parent component via event emitter.\n */\n async send() {\n const result = this.result();\n if (!result) return;\n\n this.recording.set(false);\n this.activeRecordingFile = result.file;\n\n // Notify parent that recording finished with a file\n this.recordingCompleted.emit(result.file);\n }\n\n /**\n * Cancels recording and resets the component state.\n */\n discard() {\n // If recording is still running, stop it\n if (this.recording()) {\n this.toggleRecord();\n }\n\n // Clean up blob URL to prevent memory leaks\n const url = this.audioUrl();\n if (url) {\n URL.revokeObjectURL(url);\n }\n\n // Reset everything\n this.result.set(null);\n this.previewRecord.set(false);\n this.activeRecordingFile = null;\n }\n}\n","@if (!recording() && !result()) {\n<button [class]=\"startBtnClass() ? startBtnClass() : 'glassy-btn'\" (click)=\"toggleRecord()\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" fill=\"currentColor\" class=\"bi bi-mic\"\n viewBox=\"0 0 16 16\">\n <path\n d=\"M3.5 6.5A.5.5 0 0 1 4 7v1a4 4 0 0 0 8 0V7a.5.5 0 0 1 1 0v1a5 5 0 0 1-4.5 4.975V15h3a.5.5 0 0 1 0 1h-7a.5.5 0 0 1 0-1h3v-2.025A5 5 0 0 1 3 8V7a.5.5 0 0 1 .5-.5\" />\n <path d=\"M10 8a2 2 0 1 1-4 0V3a2 2 0 1 1 4 0zM8 0a3 3 0 0 0-3 3v5a3 3 0 0 0 6 0V3a3 3 0 0 0-3-3\" />\n </svg>\n\n @if(displayBtnsLabels()){\n {{startRecordingBtnLabel()}}\n }\n</button>\n} @else if(recording()) {\n<button [class]=\"recordingBtnClass() ? recordingBtnClass() : 'glassy-btn recording'\" (click)=\"toggleRecord()\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" fill=\"currentColor\" class=\"bi bi-soundwave\"\n viewBox=\"0 0 16 16\">\n <path fill-rule=\"evenodd\"\n d=\"M8.5 2a.5.5 0 0 1 .5.5v11a.5.5 0 0 1-1 0v-11a.5.5 0 0 1 .5-.5m-2 2a.5.5 0 0 1 .5.5v7a.5.5 0 0 1-1 0v-7a.5.5 0 0 1 .5-.5m4 0a.5.5 0 0 1 .5.5v7a.5.5 0 0 1-1 0v-7a.5.5 0 0 1 .5-.5m-6 1.5A.5.5 0 0 1 5 6v4a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5m8 0a.5.5 0 0 1 .5.5v4a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5m-10 1A.5.5 0 0 1 3 7v2a.5.5 0 0 1-1 0V7a.5.5 0 0 1 .5-.5m12 0a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0V7a.5.5 0 0 1 .5-.5\" />\n </svg>\n\n @if(displayBtnsLabels()){\n {{recordingBtnLabel()}}\n }\n</button>\n} @else if(!recording() && result()) {\n<div class=\"preview-record glassy-card\">\n @if (audioUrl()) {\n <div class=\"record-info\">\n <span class=\"file-name\">{{ activeRecordingFile?.name }}</span>\n <audio controls class=\"record-player\">\n <source [src]=\"audioUrl()\" type=\"audio/wav\" />\n Your browser does not support the audio element.\n </audio>\n </div>\n }\n</div>\n}","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}