Skip to content

Commit 657a8e0

Browse files
committed
remove espeaker in resource/* , was useless
1 parent 32e5bbc commit 657a8e0

4 files changed

Lines changed: 31 additions & 133 deletions

File tree

Package.swift

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,7 @@ let package = Package(
2929
"ESpeakNG",
3030
],
3131
path: "Sources/FluidAudio",
32-
exclude: [],
33-
resources: [
34-
.copy("Resources")
35-
]
32+
exclude: []
3633
),
3734
.executableTarget(
3835
name: "FluidAudioCLI",

Sources/FluidAudio/TextToSpeech/Kokoro/Assets/Lexicon/EspeakG2P.swift

Lines changed: 29 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -50,22 +50,12 @@ final class EspeakG2P {
5050
return true
5151
}
5252

53-
guard let base = try? TtsModels.cacheDirectoryURL() else {
54-
logger.warning("Unable to resolve TTS cache directory; disabling eSpeak G2P")
53+
guard let dataDir = Self.frameworkBundledDataPath() else {
54+
logger.warning("eSpeak NG data bundle not found in ESpeakNG.xcframework; disabling G2P")
5555
return false
5656
}
5757

58-
let dataDir =
59-
base
60-
.appendingPathComponent("Models/kokoro/Resources/espeak-ng/espeak-ng-data.bundle/espeak-ng-data")
61-
62-
let voicesPath = dataDir.appendingPathComponent("voices")
63-
guard FileManager.default.fileExists(atPath: voicesPath.path) else {
64-
logger.warning("eSpeak NG voices directory not found at \(dataDir.path); disabling G2P")
65-
return false
66-
}
67-
68-
logger.info("Using eSpeak NG data from: \(dataDir.path)")
58+
logger.info("Using eSpeak NG data from framework: \(dataDir.path)")
6959
let rc: Int32 = dataDir.path.withCString { espeak_Initialize(AUDIO_OUTPUT_SYNCHRONOUS, 0, $0, 0) }
7060

7161
guard rc >= 0 else {
@@ -78,11 +68,32 @@ final class EspeakG2P {
7868
return true
7969
}
8070

71+
private static func frameworkBundledDataPath() -> URL? {
72+
// ESpeakNG.xcframework has espeak-ng-data.bundle embedded
73+
guard let frameworkBundle = Bundle(identifier: "com.fluidaudio.ESpeakNG") else {
74+
return nil
75+
}
76+
77+
guard
78+
let bundleURL = frameworkBundle.url(
79+
forResource: "espeak-ng-data",
80+
withExtension: "bundle"
81+
)
82+
else {
83+
return nil
84+
}
85+
86+
let dataDir = bundleURL.appendingPathComponent("espeak-ng-data")
87+
let voicesPath = dataDir.appendingPathComponent("voices")
88+
89+
guard FileManager.default.fileExists(atPath: voicesPath.path) else {
90+
return nil
91+
}
92+
93+
return dataDir
94+
}
95+
8196
static func isDataAvailable() -> Bool {
82-
guard let base = try? TtsModels.cacheDirectoryURL() else { return false }
83-
let voices =
84-
base
85-
.appendingPathComponent("Models/kokoro/Resources/espeak-ng/espeak-ng-data.bundle/espeak-ng-data/voices")
86-
return FileManager.default.fileExists(atPath: voices.path)
97+
return frameworkBundledDataPath() != nil
8798
}
8899
}

Sources/FluidAudio/TextToSpeech/Kokoro/Assets/Lexicon/LexiconAssetManager.swift

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,7 @@ actor LexiconAssetManager {
1313
}
1414
}
1515

16-
private func ensureEspeakAssets() async {
17-
let cacheDir = try? TtsModels.cacheDirectoryURL()
18-
guard let cacheDir else { return }
19-
let modelsDirectory = cacheDir.appendingPathComponent("Models")
20-
_ = try? await TtsResourceDownloader.ensureEspeakDataBundle(in: modelsDirectory)
21-
}
22-
2316
func ensureCoreAssets() async throws {
2417
await ensureLexiconCache()
25-
await ensureEspeakAssets()
2618
}
2719
}
Lines changed: 1 addition & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,11 @@
11
import Foundation
22

3-
/// Kokoro TTS resource downloader (lexicons, voice embeddings, eSpeak data)
3+
/// Kokoro TTS resource downloader (lexicons, voice embeddings)
44
public enum TtsResourceDownloader {
55

66
private static let logger = AppLogger(category: "TtsResourceDownloader")
77
private static let kokoroBaseURL = "https://huggingface.co/\(Repo.kokoro.remotePath)/resolve/main"
88

9-
private static func packagedEspeakBundleURL() -> URL? {
10-
#if SWIFT_PACKAGE
11-
return Bundle.module.url(
12-
forResource: "espeak-ng-data",
13-
withExtension: "bundle",
14-
subdirectory: "espeak-ng"
15-
)
16-
#else
17-
return nil
18-
#endif
19-
}
20-
21-
private static func stagePackagedEspeakBundle(
22-
from bundleURL: URL,
23-
into repoPath: URL,
24-
voicesPath: URL
25-
) throws {
26-
let targetBundle = repoPath.appendingPathComponent("Resources/espeak-ng/espeak-ng-data.bundle")
27-
let parentDir = targetBundle.deletingLastPathComponent()
28-
try FileManager.default.createDirectory(at: parentDir, withIntermediateDirectories: true)
29-
if FileManager.default.fileExists(atPath: targetBundle.path) {
30-
try FileManager.default.removeItem(at: targetBundle)
31-
}
32-
do {
33-
try FileManager.default.copyItem(at: bundleURL, to: targetBundle)
34-
} catch {
35-
throw TTSError.downloadFailed(
36-
"Failed to stage packaged eSpeak NG bundle: \(error.localizedDescription)"
37-
)
38-
}
39-
40-
guard FileManager.default.fileExists(atPath: voicesPath.path) else {
41-
throw TTSError.downloadFailed(
42-
"Packaged eSpeak NG data bundle missing 'voices' directory after copy"
43-
)
44-
}
45-
}
46-
479
/// Download a voice embedding JSON file from HuggingFace
4810
public static func downloadVoiceEmbedding(voice: String) async throws -> Data {
4911
let jsonURL = "\(kokoroBaseURL)/voices/\(voice).json"
@@ -118,68 +80,4 @@ public enum TtsResourceDownloader {
11880
}
11981
}
12082

121-
/// Ensure the eSpeak NG data bundle is available locally for Kokoro G2P
122-
@available(macOS 13.0, iOS 16.0, *)
123-
public static func ensureEspeakDataBundle(in modelsDirectory: URL) async throws -> URL {
124-
let repo = Repo.kokoro
125-
let repoPath = modelsDirectory.appendingPathComponent(repo.folderName)
126-
let bundleRoot = repoPath.appendingPathComponent(
127-
"Resources/espeak-ng/espeak-ng-data.bundle/espeak-ng-data"
128-
)
129-
let voices = bundleRoot.appendingPathComponent("voices")
130-
131-
if FileManager.default.fileExists(atPath: voices.path) {
132-
return bundleRoot
133-
}
134-
135-
try FileManager.default.createDirectory(at: repoPath, withIntermediateDirectories: true)
136-
137-
if let bundledBundle = packagedEspeakBundleURL() {
138-
try stagePackagedEspeakBundle(from: bundledBundle, into: repoPath, voicesPath: voices)
139-
logger.info("Using packaged espeak-ng-data bundle shipped with FluidAudio resources")
140-
return bundleRoot
141-
}
142-
143-
#if os(macOS)
144-
logger.info("Downloading eSpeak NG data bundle from HuggingFace…")
145-
146-
let zipPath = repoPath.appendingPathComponent("espeak-ng.zip")
147-
let zipURL = URL(string: "https://huggingface.co/\(repo.remotePath)/resolve/main/espeak-ng.zip")!
148-
149-
if !FileManager.default.fileExists(atPath: zipPath.path) {
150-
let descriptor = AssetDownloader.Descriptor(
151-
description: "espeak-ng.zip",
152-
remoteURL: zipURL,
153-
destinationURL: zipPath,
154-
transferMode: .file()
155-
)
156-
_ = try await AssetDownloader.ensure(descriptor, logger: logger)
157-
}
158-
159-
let resourcesDir = repoPath.appendingPathComponent("Resources")
160-
161-
let process = Process()
162-
process.executableURL = URL(fileURLWithPath: "/usr/bin/unzip")
163-
process.arguments = ["-o", zipPath.path, "-d", resourcesDir.path]
164-
process.standardOutput = FileHandle.nullDevice
165-
process.standardError = FileHandle.nullDevice
166-
try process.run()
167-
process.waitUntilExit()
168-
169-
guard process.terminationStatus == 0 else {
170-
throw TTSError.downloadFailed(
171-
"Failed to extract eSpeak NG data bundle via unzip (status=\(process.terminationStatus))")
172-
}
173-
logger.info("Extracted espeak-ng-data successfully")
174-
#else
175-
throw TTSError.downloadFailed(
176-
"Packaged eSpeak NG data bundle missing from FluidAudio resources"
177-
)
178-
#endif
179-
180-
guard FileManager.default.fileExists(atPath: voices.path) else {
181-
throw TTSError.downloadFailed("eSpeak NG data bundle missing 'voices' after extraction")
182-
}
183-
return bundleRoot
184-
}
18583
}

0 commit comments

Comments
 (0)