fix: single flat model dropdown, linacodec dep, quiet sidecar script

- Combine engine + model size into one flat dropdown (Qwen3-TTS 1.7B,
  Qwen3-TTS 0.6B, LuxTTS) in both FloatingGenerateBox and GenerationForm
- Add linacodec git dep to requirements.txt (uv-only source, pip can't
  resolve it from Zipvoice's pyproject.toml)
- Remove redundant transitive deps from requirements.txt
- Quiet the sidecar setup script (was printing misleading instructions)
This commit is contained in:
James Pine
2026-03-13 00:21:43 -07:00
parent e1ad7a6e73
commit 163528bf69
4 changed files with 331 additions and 130 deletions
@@ -316,7 +316,7 @@ export function FloatingGenerateBox({
</span> </span>
</div> </div>
<AnimatePresence> <AnimatePresence>
{isExpanded && ( {isExpanded && form.watch('engine') !== 'luxtts' && (
<motion.div <motion.div
initial={{ opacity: 0, scale: 0.8 }} initial={{ opacity: 0, scale: 0.8 }}
animate={{ opacity: 1, scale: 1 }} animate={{ opacity: 1, scale: 1 }}
@@ -402,30 +402,36 @@ export function FloatingGenerateBox({
)} )}
/> />
<FormField
control={form.control}
name="modelSize"
render={({ field }) => (
<FormItem className="flex-1 space-y-0"> <FormItem className="flex-1 space-y-0">
<Select onValueChange={field.onChange} defaultValue={field.value}> <Select
value={`${form.watch('engine') || 'qwen'}:${form.watch('modelSize') || '1.7B'}`}
onValueChange={(value) => {
const [engine, modelSize] = value.split(':');
form.setValue('engine', engine as 'qwen' | 'luxtts');
if (modelSize) form.setValue('modelSize', modelSize as '1.7B' | '0.6B');
}}
>
<FormControl> <FormControl>
<SelectTrigger className="h-8 text-xs bg-card border-border rounded-full hover:bg-background/50 transition-all"> <SelectTrigger className="h-8 text-xs bg-card border-border rounded-full hover:bg-background/50 transition-all">
<SelectValue /> <SelectValue />
</SelectTrigger> </SelectTrigger>
</FormControl> </FormControl>
<SelectContent> <SelectContent>
<SelectItem value="1.7B" className="text-xs text-muted-foreground"> <SelectItem value="qwen:1.7B" className="text-xs text-muted-foreground">
Qwen3-TTS 1.7B Qwen3-TTS 1.7B
</SelectItem> </SelectItem>
<SelectItem value="0.6B" className="text-xs text-muted-foreground"> <SelectItem value="qwen:0.6B" className="text-xs text-muted-foreground">
Qwen3-TTS 0.6B Qwen3-TTS 0.6B
</SelectItem> </SelectItem>
<SelectItem
value="luxtts:default"
className="text-xs text-muted-foreground"
>
LuxTTS
</SelectItem>
</SelectContent> </SelectContent>
</Select> </Select>
<FormMessage className="text-xs" />
</FormItem> </FormItem>
)}
/>
</div> </div>
</motion.div> </motion.div>
</AnimatePresence> </AnimatePresence>
@@ -101,30 +101,33 @@ export function GenerationForm() {
)} )}
<div className="grid gap-4 md:grid-cols-3"> <div className="grid gap-4 md:grid-cols-3">
<FormField
control={form.control}
name="engine"
render={({ field }) => (
<FormItem> <FormItem>
<FormLabel>TTS Engine</FormLabel> <FormLabel>Model</FormLabel>
<Select onValueChange={field.onChange} defaultValue={field.value}> <Select
value={`${form.watch('engine') || 'qwen'}:${form.watch('modelSize') || '1.7B'}`}
onValueChange={(value) => {
const [engine, modelSize] = value.split(':');
form.setValue('engine', engine as 'qwen' | 'luxtts');
if (modelSize) form.setValue('modelSize', modelSize as '1.7B' | '0.6B');
}}
>
<FormControl> <FormControl>
<SelectTrigger> <SelectTrigger>
<SelectValue /> <SelectValue />
</SelectTrigger> </SelectTrigger>
</FormControl> </FormControl>
<SelectContent> <SelectContent>
<SelectItem value="qwen">Qwen TTS</SelectItem> <SelectItem value="qwen:1.7B">Qwen3-TTS 1.7B</SelectItem>
<SelectItem value="luxtts">LuxTTS</SelectItem> <SelectItem value="qwen:0.6B">Qwen3-TTS 0.6B</SelectItem>
<SelectItem value="luxtts:default">LuxTTS</SelectItem>
</SelectContent> </SelectContent>
</Select> </Select>
<FormDescription> <FormDescription>
{field.value === 'luxtts' ? 'Fast, English-focused' : 'Multi-language'} {form.watch('engine') === 'luxtts'
? 'Fast, English-focused'
: 'Multi-language, two sizes'}
</FormDescription> </FormDescription>
<FormMessage />
</FormItem> </FormItem>
)}
/>
<FormField <FormField
control={form.control} control={form.control}
@@ -174,31 +177,6 @@ export function GenerationForm() {
/> />
</div> </div>
{form.watch('engine') !== 'luxtts' && (
<FormField
control={form.control}
name="modelSize"
render={({ field }) => (
<FormItem>
<FormLabel>Model Size</FormLabel>
<Select onValueChange={field.onChange} defaultValue={field.value}>
<FormControl>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
</FormControl>
<SelectContent>
<SelectItem value="1.7B">Qwen TTS 1.7B (Higher Quality)</SelectItem>
<SelectItem value="0.6B">Qwen TTS 0.6B (Faster)</SelectItem>
</SelectContent>
</Select>
<FormDescription>Larger models produce better quality</FormDescription>
<FormMessage />
</FormItem>
)}
/>
)}
<Button type="submit" className="w-full" disabled={isPending || !selectedProfileId}> <Button type="submit" className="w-full" disabled={isPending || !selectedProfileId}>
{isPending ? ( {isPending ? (
<> <>
+2
View File
@@ -17,6 +17,8 @@ qwen-tts>=0.0.5
# LuxTTS (voice cloning engine) # LuxTTS (voice cloning engine)
# piper-phonemize needs custom index (no PyPI wheels) # piper-phonemize needs custom index (no PyPI wheels)
--find-links https://k2-fsa.github.io/icefall/piper_phonemize.html --find-links https://k2-fsa.github.io/icefall/piper_phonemize.html
# linacodec is a git-only dep of Zipvoice (uv-only source, pip can't resolve it)
linacodec @ git+https://github.com/ysharma3501/LinaCodec.git
Zipvoice @ git+https://github.com/ysharma3501/LuxTTS.git Zipvoice @ git+https://github.com/ysharma3501/LuxTTS.git
# Audio processing # Audio processing
+271 -56
View File
@@ -1,4 +1,5 @@
#!/usr/bin/env node #!/usr/bin/env node
/** /**
* Creates placeholder sidecar binaries for development mode. * Creates placeholder sidecar binaries for development mode.
* *
@@ -9,10 +10,10 @@
* The actual server should be started separately with `bun run dev:server`. * The actual server should be started separately with `bun run dev:server`.
*/ */
import { existsSync, mkdirSync, writeFileSync, statSync } from 'fs';
import { join, dirname } from 'path';
import { fileURLToPath } from 'url';
import { execSync } from 'child_process'; import { execSync } from 'child_process';
import { existsSync, mkdirSync, statSync, writeFileSync } from 'fs';
import { dirname, join } from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url); const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename); const __dirname = dirname(__filename);
@@ -55,7 +56,9 @@ function createPlaceholderBinary(targetTriple) {
try { try {
const stats = statSync(binaryPath); const stats = statSync(binaryPath);
if (stats.size > MIN_REAL_BINARY_SIZE) { if (stats.size > MIN_REAL_BINARY_SIZE) {
console.log(`Real binary already exists: ${binaryName} (${(stats.size / 1024 / 1024).toFixed(1)} MB)`); console.log(
`Real binary already exists: ${binaryName} (${(stats.size / 1024 / 1024).toFixed(1)} MB)`,
);
return; return;
} }
} catch { } catch {
@@ -73,52 +76,275 @@ function createPlaceholderBinary(targetTriple) {
// This is the smallest valid PE that Windows will accept // This is the smallest valid PE that Windows will accept
const minimalPE = Buffer.from([ const minimalPE = Buffer.from([
// DOS Header // DOS Header
0x4D, 0x5A, 0x90, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x4d,
0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5a,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00,
0x03,
0x00,
0x00,
0x00,
0x04,
0x00,
0x00,
0x00,
0xff,
0xff,
0x00,
0x00,
0xb8,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x40,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x80,
0x00,
0x00,
0x00,
// DOS Stub // DOS Stub
0x0E, 0x1F, 0xBA, 0x0E, 0x00, 0xB4, 0x09, 0xCD, 0x21, 0xB8, 0x01, 0x4C, 0xCD, 0x21, 0x54, 0x68, 0x0e,
0x69, 0x73, 0x20, 0x70, 0x72, 0x6F, 0x67, 0x72, 0x61, 0x6D, 0x20, 0x63, 0x61, 0x6E, 0x6E, 0x6F, 0x1f,
0x74, 0x20, 0x62, 0x65, 0x20, 0x72, 0x75, 0x6E, 0x20, 0x69, 0x6E, 0x20, 0x44, 0x4F, 0x53, 0x20, 0xba,
0x6D, 0x6F, 0x64, 0x65, 0x2E, 0x0D, 0x0D, 0x0A, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e,
0x00,
0xb4,
0x09,
0xcd,
0x21,
0xb8,
0x01,
0x4c,
0xcd,
0x21,
0x54,
0x68,
0x69,
0x73,
0x20,
0x70,
0x72,
0x6f,
0x67,
0x72,
0x61,
0x6d,
0x20,
0x63,
0x61,
0x6e,
0x6e,
0x6f,
0x74,
0x20,
0x62,
0x65,
0x20,
0x72,
0x75,
0x6e,
0x20,
0x69,
0x6e,
0x20,
0x44,
0x4f,
0x53,
0x20,
0x6d,
0x6f,
0x64,
0x65,
0x2e,
0x0d,
0x0d,
0x0a,
0x24,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
// PE Signature // PE Signature
0x50, 0x45, 0x00, 0x00, 0x50,
0x45,
0x00,
0x00,
// COFF Header (x64) // COFF Header (x64)
0x64, 0x86, // Machine: AMD64 0x64,
0x01, 0x00, // NumberOfSections: 1 0x86, // Machine: AMD64
0x00, 0x00, 0x00, 0x00, // TimeDateStamp 0x01,
0x00, 0x00, 0x00, 0x00, // PointerToSymbolTable 0x00, // NumberOfSections: 1
0x00, 0x00, 0x00, 0x00, // NumberOfSymbols 0x00,
0xF0, 0x00, // SizeOfOptionalHeader 0x00,
0x22, 0x00, // Characteristics: EXECUTABLE_IMAGE | LARGE_ADDRESS_AWARE 0x00,
0x00, // TimeDateStamp
0x00,
0x00,
0x00,
0x00, // PointerToSymbolTable
0x00,
0x00,
0x00,
0x00, // NumberOfSymbols
0xf0,
0x00, // SizeOfOptionalHeader
0x22,
0x00, // Characteristics: EXECUTABLE_IMAGE | LARGE_ADDRESS_AWARE
// Optional Header (PE32+) // Optional Header (PE32+)
0x0B, 0x02, // Magic: PE32+ 0x0b,
0x00, 0x00, // Linker version 0x02, // Magic: PE32+
0x00, 0x00, 0x00, 0x00, // SizeOfCode 0x00,
0x00, 0x00, 0x00, 0x00, // SizeOfInitializedData 0x00, // Linker version
0x00, 0x00, 0x00, 0x00, // SizeOfUninitializedData 0x00,
0x00, 0x10, 0x00, 0x00, // AddressOfEntryPoint 0x00,
0x00, 0x00, 0x00, 0x00, // BaseOfCode 0x00,
0x00, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 0x00, // ImageBase 0x00, // SizeOfCode
0x00, 0x10, 0x00, 0x00, // SectionAlignment 0x00,
0x00, 0x02, 0x00, 0x00, // FileAlignment 0x00,
0x06, 0x00, 0x00, 0x00, // OS version 0x00,
0x00, 0x00, 0x00, 0x00, // Image version 0x00, // SizeOfInitializedData
0x06, 0x00, 0x00, 0x00, // Subsystem version 0x00,
0x00, 0x00, 0x00, 0x00, // Win32VersionValue 0x00,
0x00, 0x20, 0x00, 0x00, // SizeOfImage 0x00,
0x00, 0x02, 0x00, 0x00, // SizeOfHeaders 0x00, // SizeOfUninitializedData
0x00, 0x00, 0x00, 0x00, // CheckSum 0x00,
0x03, 0x00, // Subsystem: CONSOLE 0x10,
0x60, 0x01, // DllCharacteristics 0x00,
0x00, // AddressOfEntryPoint
0x00,
0x00,
0x00,
0x00, // BaseOfCode
0x00,
0x00,
0x00,
0x40,
0x01,
0x00,
0x00,
0x00, // ImageBase
0x00,
0x10,
0x00,
0x00, // SectionAlignment
0x00,
0x02,
0x00,
0x00, // FileAlignment
0x06,
0x00,
0x00,
0x00, // OS version
0x00,
0x00,
0x00,
0x00, // Image version
0x06,
0x00,
0x00,
0x00, // Subsystem version
0x00,
0x00,
0x00,
0x00, // Win32VersionValue
0x00,
0x20,
0x00,
0x00, // SizeOfImage
0x00,
0x02,
0x00,
0x00, // SizeOfHeaders
0x00,
0x00,
0x00,
0x00, // CheckSum
0x03,
0x00, // Subsystem: CONSOLE
0x60,
0x01, // DllCharacteristics
// Stack/Heap sizes (8 bytes each for PE32+) // Stack/Heap sizes (8 bytes each for PE32+)
0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, // LoaderFlags 0x00,
0x10, 0x00, 0x00, 0x00, // NumberOfRvaAndSizes 0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x10,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00, // LoaderFlags
0x10,
0x00,
0x00,
0x00, // NumberOfRvaAndSizes
]); ]);
// Pad to 512 bytes minimum for valid PE // Pad to 512 bytes minimum for valid PE
@@ -138,19 +364,8 @@ exit 1
} }
function main() { function main() {
console.log('Setting up development sidecar...');
console.log('');
const targetTriple = getTargetTriple(); const targetTriple = getTargetTriple();
console.log(`Platform: ${targetTriple}`);
createPlaceholderBinary(targetTriple); createPlaceholderBinary(targetTriple);
console.log('');
console.log('Sidecar setup complete.');
console.log('For development, start the Python server in a separate terminal:');
console.log(' bun run dev:server');
console.log('');
} }
main(); main();