paqad-ai
Version:
Spec-driven development framework — AI agents that think before they type
63 lines (57 loc) • 1.87 kB
text/typescript
const RATE = 8000;
const NOTES: ReadonlyArray<{ hz: number; seconds: number }> = [
{ hz: 880, seconds: 0.12 },
{ hz: 1320, seconds: 0.22 },
];
const ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
export function toBase64(bytes: Uint8Array): string {
let out = '';
for (let i = 0; i < bytes.length; i += 3) {
const a = bytes[i]!;
const b = bytes[i + 1];
const c = bytes[i + 2];
const n = (a << 16) | ((b ?? 0) << 8) | (c ?? 0);
out += ALPHABET[(n >> 18) & 63]! + ALPHABET[(n >> 12) & 63]!;
out += b === undefined ? '=' : ALPHABET[(n >> 6) & 63]!;
out += c === undefined ? '=' : ALPHABET[n & 63]!;
}
return out;
}
function samples(): Uint8Array {
const out: number[] = [];
for (const note of NOTES) {
const count = Math.round(RATE * note.seconds);
for (let i = 0; i < count; i += 1) {
const fade = 1 - i / count;
const wave = Math.sin((2 * Math.PI * note.hz * i) / RATE);
out.push(Math.round(128 + 80 * fade * wave));
}
}
return Uint8Array.from(out);
}
export function chimeWav(): Uint8Array {
const data = samples();
const bytes = new Uint8Array(44 + data.length);
const view = new DataView(bytes.buffer);
const text = (offset: number, value: string) => {
for (let i = 0; i < value.length; i += 1) bytes[offset + i] = value.charCodeAt(i);
};
text(0, 'RIFF');
view.setUint32(4, 36 + data.length, true);
text(8, 'WAVE');
text(12, 'fmt ');
view.setUint32(16, 16, true);
view.setUint16(20, 1, true);
view.setUint16(22, 1, true);
view.setUint32(24, RATE, true);
view.setUint32(28, RATE, true);
view.setUint16(32, 1, true);
view.setUint16(34, 8, true);
text(36, 'data');
view.setUint32(40, data.length, true);
bytes.set(data, 44);
return bytes;
}
export function chimeBase64(): string {
return toBase64(chimeWav());
}