diff --git a/Sources/FluidAudio/ModelNames.swift b/Sources/FluidAudio/ModelNames.swift index d60fa6198..f77123c7a 100644 --- a/Sources/FluidAudio/ModelNames.swift +++ b/Sources/FluidAudio/ModelNames.swift @@ -1571,7 +1571,10 @@ public enum ModelNames { public static let albert = "KokoroAlbert.mlmodelc" public static let postAlbert = "KokoroPostAlbert.mlmodelc" public static let alignment = "KokoroAlignment.mlmodelc" - public static let prosody = "KokoroProsody.mlmodelc" + // v2: fp32 compute. The fp16 CPU/ANE path corrupts F0/N at the start of + // the utterance for many T_a >= 400 (quiet/garbled onset). Renamed (not + // overwritten) so cached clients re-download. See issue #947. + public static let prosody = "KokoroProsody_v2.mlmodelc" // v2: atan2 phase-correction in the noise-source STFT (removes broad-spectrum // HF noise / "sharpness"). Renamed (not overwritten) so cached clients // re-download. See mobius laishere-coreml docs/trials-and-errors.md. diff --git a/Sources/FluidAudio/TTS/KokoroAne/Pipeline/KokoroAneSynthesizer+Types.swift b/Sources/FluidAudio/TTS/KokoroAne/Pipeline/KokoroAneSynthesizer+Types.swift index 3d16d4ddc..971c62b65 100644 --- a/Sources/FluidAudio/TTS/KokoroAne/Pipeline/KokoroAneSynthesizer+Types.swift +++ b/Sources/FluidAudio/TTS/KokoroAne/Pipeline/KokoroAneSynthesizer+Types.swift @@ -112,7 +112,7 @@ public enum KokoroAneStage: String, CaseIterable, Sendable { case .albert: return "KokoroAlbert.mlmodelc" case .postAlbert: return "KokoroPostAlbert.mlmodelc" case .alignment: return "KokoroAlignment.mlmodelc" - case .prosody: return "KokoroProsody.mlmodelc" + case .prosody: return "KokoroProsody_v2.mlmodelc" // v2: fp32 compute (long-utterance onset fix, #947) case .noise: return "KokoroNoise_v2.mlmodelc" // v2: atan2 phase-correction (HF-noise fix) case .vocoder: return "KokoroVocoder.mlmodelc" case .tail: return "KokoroTail_v2.mlmodelc" // v2: COLA-normalized iSTFT (level fix, #852) diff --git a/Tests/FluidAudioTests/TTS/KokoroAne/KokoroAneSynthesizerTests.swift b/Tests/FluidAudioTests/TTS/KokoroAne/KokoroAneSynthesizerTests.swift index 815f92b2f..5f4efb5cd 100644 --- a/Tests/FluidAudioTests/TTS/KokoroAne/KokoroAneSynthesizerTests.swift +++ b/Tests/FluidAudioTests/TTS/KokoroAne/KokoroAneSynthesizerTests.swift @@ -3,6 +3,21 @@ import XCTest @testable import FluidAudio +/// Stage bundle names must match the download set, including the renamed v2 bundles. +final class KokoroAneStageBundleNameTests: XCTestCase { + + func testStageBundlesMatchRequiredDownloadSet() { + let bundles = Set(KokoroAneStage.allCases.map(\.bundleName)) + XCTAssertEqual(bundles, ModelNames.KokoroAne.requiredCoreMLModels) + } + + func testProsodyUsesFp32ComputeBundle() { + // v1 fp16 Prosody corrupts F0/N at the utterance onset for many T_a >= 400 (#947). + XCTAssertEqual(KokoroAneStage.prosody.bundleName, "KokoroProsody_v2.mlmodelc") + XCTAssertEqual(ModelNames.KokoroAne.prosody, "KokoroProsody_v2.mlmodelc") + } +} + /// Lightweight tests for the pure duration-rounding helper (no models needed). final class KokoroAnePredictedDurationTests: XCTestCase {