Spaces:
Running
Running
| 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); | |
| } | |
| // The user should free the returned pointers | |
| 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, 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 + 3 * 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*'); | |
| 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 | |
| } | |
| // { | |
| // text: "hello", | |
| // sid: 1, | |
| // speed: 1.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 = { | |
| lexicon: '', | |
| tokens: '', | |
| 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: './model.onnx', | |
| voices: './voices.bin', | |
| tokens: './tokens.txt', | |
| dataDir: './espeak-ng-data', | |
| lengthScale: 1.0, | |
| dictDir: './dic', | |
| lexicon: './lexicon-us-en.txt,./lexicon-zh.txt', | |
| }; | |
| 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, | |
| }; | |
| } | |