|
|
| function freeConfig(config, Module) { |
| if ('buffer' in config) { |
| Module._free(config.buffer); |
| } |
|
|
| if ('config' in config) { |
| freeConfig(config.config, Module) |
| } |
|
|
| if ('matcha' in config) { |
| freeConfig(config.matcha, Module) |
| } |
|
|
| if ('kokoro' in config) { |
| freeConfig(config.kokoro, Module) |
| } |
|
|
| Module._free(config.ptr); |
| } |
|
|
| |
| function initSherpaOnnxOfflineTtsVitsModelConfig(config, Module) { |
| const modelLen = Module.lengthBytesUTF8(config.model || '') + 1; |
| const lexiconLen = Module.lengthBytesUTF8(config.lexicon || '') + 1; |
| const tokensLen = Module.lengthBytesUTF8(config.tokens || '') + 1; |
| const dataDirLen = Module.lengthBytesUTF8(config.dataDir || '') + 1; |
| const dictDirLen = Module.lengthBytesUTF8(config.dictDir || '') + 1; |
|
|
| const n = modelLen + lexiconLen + tokensLen + dataDirLen + dictDirLen; |
|
|
| const buffer = Module._malloc(n); |
|
|
| const len = 8 * 4; |
| const ptr = Module._malloc(len); |
|
|
| let offset = 0; |
| Module.stringToUTF8(config.model || '', buffer + offset, modelLen); |
| offset += modelLen; |
|
|
| Module.stringToUTF8(config.lexicon || '', buffer + offset, lexiconLen); |
| offset += lexiconLen; |
|
|
| Module.stringToUTF8(config.tokens || '', buffer + offset, tokensLen); |
| offset += tokensLen; |
|
|
| Module.stringToUTF8(config.dataDir || '', buffer + offset, dataDirLen); |
| offset += dataDirLen; |
|
|
| Module.stringToUTF8(config.dictDir || '', buffer + offset, dictDirLen); |
| offset += dictDirLen; |
|
|
| offset = 0; |
| Module.setValue(ptr, buffer + offset, 'i8*'); |
| offset += modelLen; |
|
|
| Module.setValue(ptr + 4, buffer + offset, 'i8*'); |
| offset += lexiconLen; |
|
|
| Module.setValue(ptr + 8, buffer + offset, 'i8*'); |
| offset += tokensLen; |
|
|
| Module.setValue(ptr + 12, buffer + offset, 'i8*'); |
| offset += dataDirLen; |
|
|
| Module.setValue(ptr + 16, config.noiseScale || 0.667, 'float'); |
| Module.setValue(ptr + 20, config.noiseScaleW || 0.8, 'float'); |
| Module.setValue(ptr + 24, config.lengthScale || 1.0, 'float'); |
| Module.setValue(ptr + 28, buffer + offset, 'i8*'); |
| offset += dictDirLen; |
|
|
| return { |
| buffer: buffer, ptr: ptr, len: len, |
| } |
| } |
|
|
| function initSherpaOnnxOfflineTtsMatchaModelConfig(config, Module) { |
| const acousticModelLen = Module.lengthBytesUTF8(config.acousticModel) + 1; |
| const vocoderLen = Module.lengthBytesUTF8(config.vocoder) + 1; |
| const lexiconLen = Module.lengthBytesUTF8(config.lexicon || '') + 1; |
| const tokensLen = Module.lengthBytesUTF8(config.tokens || '') + 1; |
| const dataDirLen = Module.lengthBytesUTF8(config.dataDir || '') + 1; |
| const dictDirLen = Module.lengthBytesUTF8(config.dictDir || '') + 1; |
|
|
| const n = acousticModelLen + vocoderLen + lexiconLen + tokensLen + |
| dataDirLen + dictDirLen; |
|
|
| const buffer = Module._malloc(n); |
|
|
| const len = 8 * 4; |
| const ptr = Module._malloc(len); |
|
|
| let offset = 0; |
| Module.stringToUTF8( |
| config.acousticModel || '', buffer + offset, acousticModelLen); |
| offset += acousticModelLen; |
|
|
| Module.stringToUTF8(config.vocoder || '', buffer + offset, vocoderLen); |
| offset += vocoderLen; |
|
|
| Module.stringToUTF8(config.lexicon || '', buffer + offset, lexiconLen); |
| offset += lexiconLen; |
|
|
| Module.stringToUTF8(config.tokens || '', buffer + offset, tokensLen); |
| offset += tokensLen; |
|
|
| Module.stringToUTF8(config.dataDir || '', buffer + offset, dataDirLen); |
| offset += dataDirLen; |
|
|
| Module.stringToUTF8(config.dictDir || '', buffer + offset, dictDirLen); |
| offset += dictDirLen; |
|
|
| offset = 0; |
| Module.setValue(ptr, buffer + offset, 'i8*'); |
| offset += acousticModelLen; |
|
|
| Module.setValue(ptr + 4, buffer + offset, 'i8*'); |
| offset += vocoderLen; |
|
|
| Module.setValue(ptr + 8, buffer + offset, 'i8*'); |
| offset += lexiconLen; |
|
|
| Module.setValue(ptr + 12, buffer + offset, 'i8*'); |
| offset += tokensLen; |
|
|
| Module.setValue(ptr + 16, buffer + offset, 'i8*'); |
| offset += dataDirLen; |
|
|
| Module.setValue(ptr + 20, config.noiseScale || 0.667, 'float'); |
| Module.setValue(ptr + 24, config.lengthScale || 1.0, 'float'); |
| Module.setValue(ptr + 28, buffer + offset, 'i8*'); |
| offset += dictDirLen; |
|
|
| return { |
| buffer: buffer, ptr: ptr, len: len, |
| } |
| } |
|
|
| function initSherpaOnnxOfflineTtsKokoroModelConfig(config, Module) { |
| const modelLen = Module.lengthBytesUTF8(config.model) + 1; |
| const voicesLen = Module.lengthBytesUTF8(config.voices) + 1; |
| const tokensLen = Module.lengthBytesUTF8(config.tokens || '') + 1; |
| const dataDirLen = Module.lengthBytesUTF8(config.dataDir || '') + 1; |
| const dictDirLen = Module.lengthBytesUTF8(config.dictDir || '') + 1; |
| const lexiconLen = Module.lengthBytesUTF8(config.lexicon || '') + 1; |
|
|
| const n = |
| modelLen + voicesLen + tokensLen + dataDirLen + dictDirLen + lexiconLen; |
|
|
| const buffer = Module._malloc(n); |
|
|
| const len = 7 * 4; |
| const ptr = Module._malloc(len); |
|
|
| let offset = 0; |
| Module.stringToUTF8(config.model || '', buffer + offset, modelLen); |
| offset += modelLen; |
|
|
| Module.stringToUTF8(config.voices || '', buffer + offset, voicesLen); |
| offset += voicesLen; |
|
|
| Module.stringToUTF8(config.tokens || '', buffer + offset, tokensLen); |
| offset += tokensLen; |
|
|
| Module.stringToUTF8(config.dataDir || '', buffer + offset, dataDirLen); |
| offset += dataDirLen; |
|
|
| Module.stringToUTF8(config.dictDir || '', buffer + offset, dictDirLen); |
| offset += dictDirLen; |
|
|
| Module.stringToUTF8(config.lexicon || '', buffer + offset, lexiconLen); |
| offset += lexiconLen; |
|
|
| offset = 0; |
| Module.setValue(ptr, buffer + offset, 'i8*'); |
| offset += modelLen; |
|
|
| Module.setValue(ptr + 4, buffer + offset, 'i8*'); |
| offset += voicesLen; |
|
|
| Module.setValue(ptr + 8, buffer + offset, 'i8*'); |
| offset += tokensLen; |
|
|
| Module.setValue(ptr + 12, buffer + offset, 'i8*'); |
| offset += dataDirLen; |
|
|
| Module.setValue(ptr + 16, config.lengthScale || 1.0, 'float'); |
|
|
| Module.setValue(ptr + 20, buffer + offset, 'i8*'); |
| offset += dictDirLen; |
|
|
| Module.setValue(ptr + 24, buffer + offset, 'i8*'); |
| offset += lexiconLen; |
|
|
| return { |
| buffer: buffer, ptr: ptr, len: len, |
| } |
| } |
|
|
| function initSherpaOnnxOfflineTtsModelConfig(config, Module) { |
| if (!('offlineTtsVitsModelConfig' in config)) { |
| config.offlineTtsVitsModelConfig = { |
| model: '', |
| lexicon: '', |
| tokens: '', |
| noiseScale: 0.667, |
| noiseScaleW: 0.8, |
| lengthScale: 1.0, |
| dataDir: '', |
| dictDir: '', |
| }; |
| } |
|
|
| if (!('offlineTtsMatchaModelConfig' in config)) { |
| config.offlineTtsMatchaModelConfig = { |
| acousticModel: '', |
| vocoder: '', |
| lexicon: '', |
| tokens: '', |
| noiseScale: 0.667, |
| lengthScale: 1.0, |
| dataDir: '', |
| dictDir: '', |
| }; |
| } |
|
|
| if (!('offlineTtsKokoroModelConfig' in config)) { |
| config.offlineTtsKokoroModelConfig = { |
| model: '', |
| voices: '', |
| tokens: '', |
| lengthScale: 1.0, |
| dataDir: '', |
| dictDir: '', |
| lexicon: '', |
| }; |
| } |
|
|
|
|
| const vitsModelConfig = initSherpaOnnxOfflineTtsVitsModelConfig( |
| config.offlineTtsVitsModelConfig, Module); |
|
|
| const matchaModelConfig = initSherpaOnnxOfflineTtsMatchaModelConfig( |
| config.offlineTtsMatchaModelConfig, Module); |
|
|
| const kokoroModelConfig = initSherpaOnnxOfflineTtsKokoroModelConfig( |
| config.offlineTtsKokoroModelConfig, Module); |
|
|
| const len = vitsModelConfig.len + matchaModelConfig.len + |
| kokoroModelConfig.len + 3 * 4; |
|
|
| const ptr = Module._malloc(len); |
|
|
| let offset = 0; |
| Module._CopyHeap(vitsModelConfig.ptr, vitsModelConfig.len, ptr + offset); |
| offset += vitsModelConfig.len; |
|
|
| Module.setValue(ptr + offset, config.numThreads || 1, 'i32'); |
| offset += 4; |
|
|
| Module.setValue(ptr + offset, config.debug || 0, 'i32'); |
| offset += 4; |
|
|
| const providerLen = Module.lengthBytesUTF8(config.provider || 'cpu') + 1; |
| const buffer = Module._malloc(providerLen); |
| Module.stringToUTF8(config.provider || 'cpu', buffer, providerLen); |
| Module.setValue(ptr + offset, buffer, 'i8*'); |
| offset += 4; |
|
|
| Module._CopyHeap(matchaModelConfig.ptr, matchaModelConfig.len, ptr + offset); |
| offset += matchaModelConfig.len; |
|
|
| Module._CopyHeap(kokoroModelConfig.ptr, kokoroModelConfig.len, ptr + offset); |
| offset += kokoroModelConfig.len; |
|
|
| return { |
| buffer: buffer, ptr: ptr, len: len, config: vitsModelConfig, |
| matcha: matchaModelConfig, kokoro: kokoroModelConfig, |
| } |
| } |
|
|
| function initSherpaOnnxOfflineTtsConfig(config, Module) { |
| const modelConfig = |
| initSherpaOnnxOfflineTtsModelConfig(config.offlineTtsModelConfig, Module); |
| const len = modelConfig.len + 4 * 4; |
| const ptr = Module._malloc(len); |
|
|
| let offset = 0; |
| Module._CopyHeap(modelConfig.ptr, modelConfig.len, ptr + offset); |
| offset += modelConfig.len; |
|
|
| const ruleFstsLen = Module.lengthBytesUTF8(config.ruleFsts || '') + 1; |
| const ruleFarsLen = Module.lengthBytesUTF8(config.ruleFars || '') + 1; |
|
|
| const buffer = Module._malloc(ruleFstsLen + ruleFarsLen); |
| Module.stringToUTF8(config.ruleFsts || '', buffer, ruleFstsLen); |
| Module.stringToUTF8(config.ruleFars || '', buffer + ruleFstsLen, ruleFarsLen); |
|
|
| Module.setValue(ptr + offset, buffer, 'i8*'); |
| offset += 4; |
|
|
| Module.setValue(ptr + offset, config.maxNumSentences || 1, 'i32'); |
| offset += 4; |
|
|
| Module.setValue(ptr + offset, buffer + ruleFstsLen, 'i8*'); |
| offset += 4; |
|
|
| Module.setValue(ptr + offset, config.silenceScale || 0.2, 'float'); |
| offset += 4; |
|
|
| return { |
| buffer: buffer, ptr: ptr, len: len, config: modelConfig, |
| } |
| } |
|
|
| class OfflineTts { |
| constructor(configObj, Module) { |
| console.log(configObj) |
| const config = initSherpaOnnxOfflineTtsConfig(configObj, Module) |
| const handle = Module._SherpaOnnxCreateOfflineTts(config.ptr); |
|
|
| freeConfig(config, Module); |
|
|
| this.handle = handle; |
| this.sampleRate = Module._SherpaOnnxOfflineTtsSampleRate(this.handle); |
| this.numSpeakers = Module._SherpaOnnxOfflineTtsNumSpeakers(this.handle); |
| this.Module = Module |
| } |
|
|
| free() { |
| this.Module._SherpaOnnxDestroyOfflineTts(this.handle); |
| this.handle = 0 |
| } |
|
|
| |
| |
| |
| |
| |
| generate(config) { |
| const textLen = this.Module.lengthBytesUTF8(config.text) + 1; |
| const textPtr = this.Module._malloc(textLen); |
| this.Module.stringToUTF8(config.text, textPtr, textLen); |
|
|
| const h = this.Module._SherpaOnnxOfflineTtsGenerate( |
| this.handle, textPtr, config.sid, config.speed); |
|
|
| const numSamples = this.Module.HEAP32[h / 4 + 1]; |
| const sampleRate = this.Module.HEAP32[h / 4 + 2]; |
|
|
| const samplesPtr = this.Module.HEAP32[h / 4] / 4; |
| const samples = new Float32Array(numSamples); |
| for (let i = 0; i < numSamples; i++) { |
| samples[i] = this.Module.HEAPF32[samplesPtr + i]; |
| } |
|
|
| this.Module._SherpaOnnxDestroyOfflineTtsGeneratedAudio(h); |
| return {samples: samples, sampleRate: sampleRate}; |
| } |
| save(filename, audio) { |
| const samples = audio.samples; |
| const sampleRate = audio.sampleRate; |
| const ptr = this.Module._malloc(samples.length * 4); |
| for (let i = 0; i < samples.length; i++) { |
| this.Module.HEAPF32[ptr / 4 + i] = samples[i]; |
| } |
|
|
| const filenameLen = this.Module.lengthBytesUTF8(filename) + 1; |
| const buffer = this.Module._malloc(filenameLen); |
| this.Module.stringToUTF8(filename, buffer, filenameLen); |
| this.Module._SherpaOnnxWriteWave(ptr, samples.length, sampleRate, buffer); |
| this.Module._free(buffer); |
| this.Module._free(ptr); |
| } |
| } |
|
|
| function createOfflineTts(Module, myConfig) { |
| const offlineTtsVitsModelConfig = { |
| model: './model.onnx', |
| lexicon: '', |
| tokens: './tokens.txt', |
| dataDir: './espeak-ng-data', |
| dictDir: '', |
| noiseScale: 0.667, |
| noiseScaleW: 0.8, |
| lengthScale: 1.0, |
| }; |
|
|
| const offlineTtsMatchaModelConfig = { |
| acousticModel: '', |
| vocoder: '', |
| lexicon: '', |
| tokens: '', |
| dataDir: '', |
| dictDir: '', |
| noiseScale: 0.667, |
| lengthScale: 1.0, |
| }; |
|
|
| const offlineTtsKokoroModelConfig = { |
| model: '', |
| voices: '', |
| tokens: '', |
| dataDir: '', |
| lengthScale: 1.0, |
| dictDir: '', |
| lexicon: '', |
| }; |
|
|
| const offlineTtsModelConfig = { |
| offlineTtsVitsModelConfig: offlineTtsVitsModelConfig, |
| offlineTtsMatchaModelConfig: offlineTtsMatchaModelConfig, |
| offlineTtsKokoroModelConfig: offlineTtsKokoroModelConfig, |
| numThreads: 1, |
| debug: 1, |
| provider: 'cpu', |
| }; |
|
|
| let offlineTtsConfig = { |
| offlineTtsModelConfig: offlineTtsModelConfig, |
| ruleFsts: '', |
| ruleFars: '', |
| maxNumSentences: 1, |
| } |
|
|
| if (myConfig) { |
| offlineTtsConfig = myConfig; |
| } |
|
|
| return new OfflineTts(offlineTtsConfig, Module); |
| } |
|
|
| if (typeof process == 'object' && typeof process.versions == 'object' && |
| typeof process.versions.node == 'string') { |
| module.exports = { |
| createOfflineTts, |
| }; |
| } |
|
|