Installation
Copy
npm install kugelaudio
Copy
yarn add kugelaudio
# or
pnpm add kugelaudio
Quick Start
Copy
import { KugelAudio } from 'kugelaudio';
// Initialize the client
const client = new KugelAudio({ apiKey: 'your_api_key' });
// Generate speech
const audio = await client.tts.generate({
text: 'Hello, world!',
model: 'kugel-1-turbo',
});
// audio.audio is an ArrayBuffer with PCM16 data
console.log(`Duration: ${audio.durationMs}ms`);
Client Configuration
Copy
import { KugelAudio } from 'kugelaudio';
// Simple setup
const client = new KugelAudio({ apiKey: 'your_api_key' });
// With custom options
const client = new KugelAudio({
apiKey: 'your_api_key', // Required: Your API key
apiUrl: 'https://api.kugelaudio.com', // Optional: API base URL
timeout: 60000, // Optional: Request timeout in ms
});
Local Development
For local development, point directly to your TTS server:Copy
const client = new KugelAudio({
apiKey: 'your_api_key',
apiUrl: 'http://localhost:8000',
});
Copy
const client = new KugelAudio({
apiKey: 'your_api_key',
apiUrl: 'http://localhost:8001', // Backend for REST API
ttsUrl: 'http://localhost:8000', // TTS server for WebSocket streaming
});
Text-to-Speech
Basic Generation
Generate complete audio and receive it all at once:Copy
const audio = await client.tts.generate({
text: 'Hello, this is a test of the KugelAudio text-to-speech system.',
model: 'kugel-1-turbo', // 'kugel-1-turbo' (fast) or 'kugel-1' (quality)
voiceId: 123, // Optional: specific voice ID
cfgScale: 2.0, // Guidance scale (1.0-5.0)
maxNewTokens: 2048, // Maximum tokens to generate
sampleRate: 24000, // Output sample rate
speakerPrefix: true, // Add speaker prefix for better quality
});
// Audio properties
console.log(`Duration: ${audio.durationMs}ms`);
console.log(`Samples: ${audio.samples}`);
console.log(`Sample rate: ${audio.sampleRate} Hz`);
console.log(`Generation time: ${audio.generationMs}ms`);
console.log(`RTF: ${audio.rtf}`); // Real-time factor
// audio.audio is an ArrayBuffer with PCM16 data
Playing Audio in Browser
Copy
import { createWavBlob } from 'kugelaudio';
const audio = await client.tts.generate({
text: 'Hello, world!',
model: 'kugel-1-turbo',
});
// Create WAV blob for playback
const wavBlob = createWavBlob(audio.audio, audio.sampleRate);
const url = URL.createObjectURL(wavBlob);
// Play with Audio element
const audioElement = new Audio(url);
audioElement.play();
// Or with Web Audio API
const audioContext = new AudioContext();
const arrayBuffer = await wavBlob.arrayBuffer();
const audioBuffer = await audioContext.decodeAudioData(arrayBuffer);
const source = audioContext.createBufferSource();
source.buffer = audioBuffer;
source.connect(audioContext.destination);
source.start();
Streaming Audio
Receive audio chunks as they are generated for lower latency:Copy
await client.tts.stream(
{
text: 'Hello, this is streaming audio.',
model: 'kugel-1-turbo',
},
{
onOpen: () => {
console.log('WebSocket connected');
},
onChunk: (chunk) => {
console.log(`Chunk ${chunk.index}: ${chunk.samples} samples`);
// chunk.audio is base64-encoded PCM16 data
playAudioChunk(chunk);
},
onFinal: (stats) => {
console.log(`Total duration: ${stats.durationMs}ms`);
console.log(`Time to first audio: ${stats.ttfaMs}ms`);
console.log(`Generation time: ${stats.generationMs}ms`);
console.log(`RTF: ${stats.rtf}`);
},
onError: (error) => {
console.error('TTS error:', error);
},
onClose: () => {
console.log('WebSocket closed');
},
}
);
Processing Audio Chunks
Copy
import { base64ToArrayBuffer, decodePCM16 } from 'kugelaudio';
// In streaming callback:
onChunk: (chunk) => {
// Decode base64 to ArrayBuffer
const pcmBuffer = base64ToArrayBuffer(chunk.audio);
// Convert PCM16 to Float32 for Web Audio API
const float32Data = decodePCM16(chunk.audio);
// Play with Web Audio API
const audioBuffer = audioContext.createBuffer(1, float32Data.length, chunk.sampleRate);
audioBuffer.copyToChannel(float32Data, 0);
const source = audioContext.createBufferSource();
source.buffer = audioBuffer;
source.connect(audioContext.destination);
source.start();
}
Voices
List Available Voices
Copy
// List all available voices
const voices = await client.voices.list();
for (const voice of voices) {
console.log(`${voice.id}: ${voice.name}`);
console.log(` Category: ${voice.category}`);
console.log(` Languages: ${voice.supportedLanguages.join(', ')}`);
}
// Filter by language
const germanVoices = await client.voices.list({ language: 'de' });
// Get only public voices
const publicVoices = await client.voices.list({ includePublic: true });
// Limit results
const first10 = await client.voices.list({ limit: 10 });
Get a Specific Voice
Copy
const voice = await client.voices.get(123);
console.log(`Voice: ${voice.name}`);
console.log(`Sample text: ${voice.sampleText}`);
Models
List Available Models
Copy
const models = await client.models.list();
for (const model of models) {
console.log(`${model.id}: ${model.name}`);
console.log(` Description: ${model.description}`);
console.log(` Parameters: ${model.parameters}`);
console.log(` Max Input: ${model.maxInputLength} characters`);
console.log(` Sample Rate: ${model.sampleRate} Hz`);
}
Error Handling
Copy
import { KugelAudio } from 'kugelaudio';
import {
KugelAudioError,
AuthenticationError,
RateLimitError,
InsufficientCreditsError,
ValidationError,
ConnectionError,
} from 'kugelaudio';
try {
const audio = await client.tts.generate({ text: 'Hello!' });
} catch (error) {
if (error instanceof AuthenticationError) {
console.error('Invalid API key');
} else if (error instanceof RateLimitError) {
console.error('Rate limit exceeded, please wait');
} else if (error instanceof InsufficientCreditsError) {
console.error('Not enough credits, please top up');
} else if (error instanceof ValidationError) {
console.error(`Invalid request: ${error.message}`);
} else if (error instanceof ConnectionError) {
console.error('Failed to connect to server');
} else if (error instanceof KugelAudioError) {
console.error(`API error: ${error.message}`);
}
}
TypeScript Types
KugelAudioOptions
Copy
interface KugelAudioOptions {
apiKey: string; // Required
apiUrl?: string; // Default: 'https://api.kugelaudio.com'
ttsUrl?: string; // Default: same as apiUrl
timeout?: number; // Default: 60000 (ms)
}
GenerateOptions
Copy
interface GenerateOptions {
text: string; // Required: Text to synthesize
model?: string; // Default: 'kugel-1-turbo'
voiceId?: number; // Optional: Voice ID
cfgScale?: number; // Default: 2.0
maxNewTokens?: number; // Default: 2048
sampleRate?: number; // Default: 24000
speakerPrefix?: boolean; // Default: true
}
AudioChunk
Copy
interface AudioChunk {
audio: string; // Base64-encoded PCM16 audio
encoding: string; // 'pcm_s16le'
index: number; // Chunk index (0-based)
sampleRate: number; // Sample rate (24000)
samples: number; // Number of samples in chunk
}
AudioResponse
Copy
interface AudioResponse {
audio: ArrayBuffer; // Complete PCM16 audio
sampleRate: number; // Sample rate (24000)
samples: number; // Total samples
durationMs: number; // Duration in milliseconds
generationMs: number; // Generation time in milliseconds
rtf: number; // Real-time factor
}
GenerationStats
Copy
interface GenerationStats {
final: true;
chunks: number; // Number of chunks generated
totalSamples: number; // Total samples generated
durationMs: number; // Audio duration in ms
generationMs: number; // Generation time in ms
ttfaMs: number; // Time to first audio in ms
rtf: number; // Real-time factor
}
StreamCallbacks
Copy
interface StreamCallbacks {
onOpen?: () => void;
onChunk?: (chunk: AudioChunk) => void;
onFinal?: (stats: GenerationStats) => void;
onError?: (error: Error) => void;
onClose?: () => void;
}
Model
Copy
interface Model {
id: string; // 'kugel-1-turbo' or 'kugel-1'
name: string; // Human-readable name
description: string; // Model description
parameters: string; // Parameter count ('1.5B', '7B')
maxInputLength: number; // Maximum input characters
sampleRate: number; // Output sample rate
}
Voice
Copy
interface Voice {
id: number; // Voice ID
name: string; // Voice name
description?: string; // Description
category?: VoiceCategory; // 'premade' | 'cloned' | 'generated'
sex?: VoiceSex; // 'male' | 'female' | 'neutral'
age?: VoiceAge; // 'young' | 'middle_aged' | 'old'
supportedLanguages: string[]; // ['en', 'de', ...]
sampleText?: string; // Sample text for preview
avatarUrl?: string; // Avatar image URL
sampleUrl?: string; // Sample audio URL
isPublic: boolean; // Whether voice is public
verified: boolean; // Whether voice is verified
}
Utility Functions
base64ToArrayBuffer
Convert base64 string to ArrayBuffer:Copy
import { base64ToArrayBuffer } from 'kugelaudio';
const buffer = base64ToArrayBuffer(chunk.audio);
decodePCM16
Convert base64 PCM16 to Float32Array for Web Audio API:Copy
import { decodePCM16 } from 'kugelaudio';
const floatData = decodePCM16(chunk.audio);
createWavFile
Create a WAV file from PCM16 data:Copy
import { createWavFile } from 'kugelaudio';
const wavBuffer = createWavFile(pcmArrayBuffer, 24000);
createWavBlob
Create a playable Blob from PCM16 data:Copy
import { createWavBlob } from 'kugelaudio';
const blob = createWavBlob(pcmArrayBuffer, 24000);
const url = URL.createObjectURL(blob);
Complete Example
Copy
import { KugelAudio, createWavBlob, base64ToArrayBuffer } from 'kugelaudio';
async function main() {
// Initialize client
const client = new KugelAudio({ apiKey: 'your_api_key' });
// List available models
console.log('Available Models:');
const models = await client.models.list();
for (const model of models) {
console.log(` - ${model.id}: ${model.name} (${model.parameters})`);
}
// List available voices
console.log('\nAvailable Voices:');
const voices = await client.voices.list({ limit: 5 });
for (const voice of voices) {
console.log(` - ${voice.id}: ${voice.name}`);
}
// Generate audio with streaming
console.log('\nGenerating audio (streaming)...');
const chunks: ArrayBuffer[] = [];
let ttfa: number | undefined;
const startTime = Date.now();
await client.tts.stream(
{
text: 'Welcome to KugelAudio. This is an example of high-quality text-to-speech synthesis.',
model: 'kugel-1-turbo',
},
{
onChunk: (chunk) => {
if (!ttfa) {
ttfa = Date.now() - startTime;
console.log(`Time to first audio: ${ttfa}ms`);
}
chunks.push(base64ToArrayBuffer(chunk.audio));
},
onFinal: (stats) => {
console.log(`Generated ${stats.durationMs}ms of audio`);
console.log(`Generation time: ${stats.generationMs}ms`);
console.log(`RTF: ${stats.rtf}x`);
},
}
);
}
main();