Comments (51)
from tts-server-android.
其实加不加锁都一样 在写入音频那里也会阻塞。
我也忘记当初为何要加锁了
from tts-server-android.
刚测试了下 确实可以提前获取到后面文本
不过我还得想想怎么实现缓存
总之感谢你的提醒
from tts-server-android.
客户端应用不用考虑并发,搞得效率高了,微软限制就来了
from tts-server-android.
from tts-server-android.
微软又收紧限制了,下午听一个小时就不行了
from tts-server-android.
from tts-server-android.
我研究了一会 发现缓存实现没那么容易
最主要的的是 start()
和 audioAvailable()
都必须在合成线程上调用,而 audioAvailable()
写入音频这个方法还是阻塞的
官方文档:
告知服务从给定文本合成语音。此方法应阻塞直到 合成完成。在合成线程上调用。
from tts-server-android.
我也是弄不明白 既然 onSynthesizeText()
必须阻塞直到合成完成,那 TextToSpeech.speak()
的队列参数又有啥意义?
Android:
合成必须是同步的,这意味着引擎不得在方法返回后保留回调或对其调用任何方法
from tts-server-android.
这回是没戏了😟
from tts-server-android.
这回是没戏了😟
可以啊,可以把每次请求和callback提交给一个新线程,只是要注意只能在新线程中调用callback
from tts-server-android.
from tts-server-android.
但是新线程不是合成线程,就算调用start和音频写入方法也没反应
参考一下我的写法
synthesizer.synthesize(speed, volume, text, 1, new SynthesizerCallback() {
@Override
public void onBusy() {
callback.error(TextToSpeech.ERROR_SYNTHESIS);
Log.d(TAG, "onBusy: engine is busy.");
}
@Override
public void onStart() {
callback.start(16000, AudioFormat.ENCODING_PCM_16BIT, 1);
Log.d(TAG, "onStart: everything is ok.");
}
@Override
public void onFinish(String msg) {
callback.done();
Log.d(TAG, "onFinish: " + msg);
}
@Override
public void onFailed(String msg) {
Log.e(TAG, "onFailed: " + msg);
callback.error(TextToSpeech.ERROR_SERVICE);
}
@Override
public void onSynthesize(byte[] audioData, int index, int size) {
int offset = 0;
int length = audioData.length;
final int maxBufferSize = callback.getMaxBufferSize();
while (offset < length) {
int bytesToWrite = Math.min(maxBufferSize, length - offset);
callback.audioAvailable(audioData, offset, bytesToWrite);
offset += bytesToWrite;
}
}
});
synthesize方法会启动一个线程,callback 只在新线程中调用,不然播放不了。
from tts-server-android.
但是新线程不是合成线程,就算调用start和音频写入方法也没反应
只要callback的start和audioAvailable、done方法调用的是同一个线程就行,不能分开在不同线程调用
from tts-server-android.
但是新线程不是合成线程,就算调用start和音频写入方法也没反应
只要callback的start和audioAvailable、done方法调用的是同一个线程就行
emmm但为什么我已经调用了start() & audioAvailable() 但不播放呢
mScope.launch(Dispatchers.Default) {
Log.e(TAG, "current thread: ${Thread.currentThread().name}")
// 接收者
for (v in audioChannel) {
val callback = v.first
callback.start(mAudioFormat.sampleRate, AudioFormat.ENCODING_PCM_16BIT, 1)
if (v.second == null) {
Log.w(TAG, "音频为空")
continue
}
mAudioDecoder.doDecode(v.second!!, mAudioFormat.sampleRate, {
println("writeToCallBack: ${it.size}")
writeToCallBack(callback, it)
}, {})
callback.done()
}
}
from tts-server-android.
launch(Dispatchers.Default)
直接用协程 launch 不行么?
from tts-server-android.
doDecode
你的doDecode方法是不是启动别的线程了?
from tts-server-android.
没有的,
而且正常来说调用 start() 后阅读APP当前段落就会高亮,然而并无反应
from tts-server-android.
start() & audioAvailable() 返回值也是 = TextToSpeech.SUCCESS
/* 写入PCM音频到系统组件 */
private fun writeToCallBack(callback: SynthesisCallback, pcmData: ByteArray) {
try {
val maxBufferSize: Int = callback.maxBufferSize
var offset = 0
while (offset < pcmData.size) {
val bytesToWrite = maxBufferSize.coerceAtMost(pcmData.size - offset)
val ret = callback.audioAvailable(pcmData, offset, bytesToWrite)
Log.e(TAG, "audioAvailable: $ret")
offset += bytesToWrite
}
} catch (e: Exception) {
e.printStackTrace()
}
}
from tts-server-android.
新建了一个线程也不行 mScope.launch(newSingleThreadContext("new-thread-name"))
可能是callback在 onSynthesizeText() return后就不能用了?
from tts-server-android.
新建了一个线程也不行
mScope.launch(newSingleThreadContext("new-thread-name"))
可能是callback在 onSynthesizeText() return后就不能用了?
要不你试试每次callback调用打印线程编号试试?我也看不出什么问题。。。
from tts-server-android.
public abstract int audioAvailable(byte[] buffer, int offset, int length )
This method should only be called on the synthesis thread, while in TextToSpeechService.onSynthesizeText.
看来必须在 synthesis 线程上调用
from tts-server-android.
public abstract int audioAvailable(byte[] buffer, int offset, int length ) This method should only be called on the synthesis thread, while in TextToSpeechService.onSynthesizeText.
看来必须在 synthesis 线程上调用
我之前遇到过无法正常播放的问题,是callback在不同线程调用造成的。有试过直接把原来的synchronized块换成new Thread(lambda).start()吗?
from tts-server-android.
public abstract int audioAvailable(byte[] buffer, int offset, int length ) This method should only be called on the synthesis thread, while in TextToSpeechService.onSynthesizeText.
看来必须在 synthesis 线程上调用
也有可能是其他非同步方法,并发调用时的问题。。。
from tts-server-android.
测试是否必须在合成线程调用
override fun onSynthesizeText(request: SynthesisRequest?, callback: SynthesisCallback?) {
Log.e(TAG, request?.charSequenceText.toString())
Log.e(TAG, "onSynthesizeText thread: ${Thread.currentThread().id}")
GlobalScope.launch(newSingleThreadContext("thread")) {
Log.e(TAG, "callback?.start thread: ${Thread.currentThread().id}")
callback?.start(16000, AudioFormat.ENCODING_PCM_16BIT, 1)
val audio = MsTTS().getAudio("测试文本")
AudioDecoder().doDecode(audio!!, 24000, {
val maxBufferSize: Int = callback!!.maxBufferSize
var offset = 0
while (offset < it.size) {
val bytesToWrite = maxBufferSize.coerceAtMost(it.size - offset)
val ret = callback.audioAvailable(it, offset, bytesToWrite)
Log.e(TAG, "audioAvailable: $ret")
offset += bytesToWrite
}
}, {})
}
Thread.sleep(1000)
播放正常了 输出:
onSynthesizeText thread: 30951
21:18:56.315 SysTtsService E callback?.start thread: 30961
21:18:56.320 GoLog E time="2022-12-06T13:18:56Z" level=info msg="创建WebSocket连接(Edge)..."
21:18:56.320 GoLog E time="2022-12-06T13:18:56Z" level=info msg="连接到IP地址: 202.89.233.103:443"
21:18:56.850 AudioDecode D 找到音频流的index:0, mime:audio/mpeg
21:18:56.896 SysTtsService E decode thread: 30961
21:18:56.896 SysTtsService E audioAvailable: 0
21:18:56.897 SysTtsService E decode thread: 30961
21:18:56.898 SysTtsService E audioAvailable: 0
看来不一定非要在合成线程调用
from tts-server-android.
测试是否必须在合成线程调用
override fun onSynthesizeText(request: SynthesisRequest?, callback: SynthesisCallback?) { Log.e(TAG, request?.charSequenceText.toString()) Log.e(TAG, "onSynthesizeText thread: ${Thread.currentThread().id}") GlobalScope.launch(newSingleThreadContext("thread")) { Log.e(TAG, "callback?.start thread: ${Thread.currentThread().id}") callback?.start(16000, AudioFormat.ENCODING_PCM_16BIT, 1) val audio = MsTTS().getAudio("测试文本") AudioDecoder().doDecode(audio!!, 24000, { val maxBufferSize: Int = callback!!.maxBufferSize var offset = 0 while (offset < it.size) { val bytesToWrite = maxBufferSize.coerceAtMost(it.size - offset) val ret = callback.audioAvailable(it, offset, bytesToWrite) Log.e(TAG, "audioAvailable: $ret") offset += bytesToWrite } }, {}) } Thread.sleep(1000)播放正常了 输出:
onSynthesizeText thread: 30951 21:18:56.315 SysTtsService E callback?.start thread: 30961 21:18:56.320 GoLog E time="2022-12-06T13:18:56Z" level=info msg="创建WebSocket连接(Edge)..." 21:18:56.320 GoLog E time="2022-12-06T13:18:56Z" level=info msg="连接到IP地址: 202.89.233.103:443" 21:18:56.850 AudioDecode D 找到音频流的index:0, mime:audio/mpeg 21:18:56.896 SysTtsService E decode thread: 30961 21:18:56.896 SysTtsService E audioAvailable: 0 21:18:56.897 SysTtsService E decode thread: 30961 21:18:56.898 SysTtsService E audioAvailable: 0
看来不一定非要在合成线程调用
对,看了之前的问题是非同步方法并发调用的问题,可以给一些非同步方法加锁试试
from tts-server-android.
不像是这个问题 接收者是阻塞的 audioDecode.doDecode()
也会一直等待到播放完毕
我刚才测试了下callback在return后是否有效,测试下来确实是可以正常播放的
from tts-server-android.
其实单线程也行,每次调用onSynthesizeText时把请求加入处理队列后直接返回;然后用一个新线程,当队列不为空时,从队列中取出请求进行合成
from tts-server-android.
这样就不会有同步的问题了
from tts-server-android.
我用的是 Kotlin Channel 生产者消费者模式,其实也不会有线程同步问题,整个接收者都在一个单独线程内
from tts-server-android.
我用的是 Kotlin Channel 生产者消费者模式,其实也不会有线程同步问题,整个接收者都在一个单独线程内
雀食,audioAvailable方法貌似是阻塞的
from tts-server-android.
找到问题了哈哈😂 是首次请求由于没过滤空文本导致返回音频为空,然后continue时忘记 callback.done()
了就导致以后的音频callback一直阻塞。
GlobalScope.launch(newSingleThreadContext("new-thread")) {
for (v in audioChannel) {
val callback = v.first
val audio = v.second
val ret = callback.start(mAudioFormat.sampleRate, AudioFormat.ENCODING_PCM_16BIT, 1)
if (audio == null) {
Log.w(TAG, "音频为空")
callback.done()
continue
}
Log.e(TAG, "音频大小: ${audio.size}")
mAudioDecoder.doDecode(audio, mAudioFormat.sampleRate, {
println("writeToCallBack: ${it.size}")
writeToCallBack(callback, it)
}, {})
callback.done()
}
}
from tts-server-android.
又遇到问题了 =-=
onStop()
在用户暂停时不调用,导致一直播放 只能关闭朗读
经过测试发现 必须要让 onSynthesizeText()
阻塞,否则系统不调用 onStop()
from tts-server-android.
又遇到问题了 =-=
onStop()
在用户暂停时不调用,导致一直播放 只能关闭朗读 经过测试发现 必须要让onSynthesizeText()
阻塞,否则系统不调用onStop()
啊,是这样吗?那么正常暂停朗读系统是怎么处理的呢,是使正在调用的audioAvailable无效并返回异常吗?
from tts-server-android.
正常是 onSynthesizeText()
一直阻塞等待播放完毕,如果这时用户暂停,系统就会调用 onStop()
,然后就进行一些取消操作。
from tts-server-android.
但如果 onSynthesizeText()
return后,拿着它的 callback
和 request
进行合成和写入音频,这时候用户暂停, 当前callback就会失效立刻停止播放,但后面预先缓存的callback还能正常写入播放
from tts-server-android.
我还尝试通过判断返回值确定是否暂停,但就算暂停了也始终=SUCCESS
from tts-server-android.
但如果
onSynthesizeText()
return后,拿着它的callback
和request
进行合成和写入音频,这时候用户暂停, 当前callback就会失效立刻停止播放,但后面预先缓存的callback还能正常写入播放
那就在同一个线程播放,在另一个线程合成?
from tts-server-android.
生产者消费者本就是两个线程,问题在于 需要通过 onSynthesizeText()
获取后续所有的段落文本,而我又不能去阻塞它。
安卓文档也说:
Synthesis must be synchronous which means the engine must NOT hold on to the callback or call any methods on it after the method returns.
合成必须是同步的,这意味着引擎不能保留回调,也不能在方法返回后对其调用任何方法。
from tts-server-android.
虽说缓存实现了,但这不能暂停太影响体验了,实在不行只能放弃了 😶
from tts-server-android.
虽说缓存实现了,但这不能暂停太影响体验了,实在不行只能放弃了 😶
行吧,系统不让这么做放弃也行
from tts-server-android.
生产者消费者本就是两个线程,问题在于 需要通过
onSynthesizeText()
获取后续所有的段落文本,而我又不能去阻塞它。 安卓文档也说:Synthesis must be synchronous which means the engine must NOT hold on to the callback or call any methods on it after the method returns.
合成必须是同步的,这意味着引擎不能保留回调,也不能在方法返回后对其调用任何方法。
你没明白我的意思,我想说的是自己写一个生产者消费者模型,一旦某一个callback失效,就终止所有合成和后续播放
from tts-server-android.
from tts-server-android.
from tts-server-android.
问题是callback我没办法知道它是否失效
如果朗读停止后,依旧通过callback.audioAvailable写入音频数据,音频不会被播放且返回码为-1,正常为0
from tts-server-android.
问题是callback我没办法知道它是否失效
如果朗读停止后,依旧通过callback.audioAvailable写入音频数据,音频不会被播放且返回码为-1,正常为0
好吧,不阻塞时不行
from tts-server-android.
就算暂停后callback.audioAvailable()
也是返回 0 SUCCESS,所以说没法知道是否暂停
from tts-server-android.
有个不成熟的想法,如果你还想试试的话可以看看。在onSynthesizeText方法内,在方法的最后让最后一个请求线程阻塞,之前的线程返回。实现类似这样:
private volatile boolean isLast = true;
private volatile boolean isDone = false;
private final Object mLock = new Object();
protected void onSynthesizeText(SynthesisRequest request, SynthesisCallback callback) {
...
// 让之前请求的线程释放锁
isLast = false;
// 当前(最后一个)请求线程获取锁
synchronized (mLock) {
isLast = true;
// 接收到后来线程请求、或所有合成完成时释放锁
while (isLast && !isDone) {
Thread.sleep(100);
}
}
}
实在不想折腾就算了,可以理解
from tts-server-android.
就算暂停后
callback.audioAvailable()
也是返回 0 SUCCESS,所以说没法知道是否暂停
对,看来这个方法必须阻塞了
from tts-server-android.
onSynthesizeText在return后系统才会调用后续的onSynthesizeText
这行不通吧
from tts-server-android.
算了 不折腾了
from tts-server-android.
Related Issues (20)
- Persian appended to rules
- 单人朗读规则 HOT 2
- 可以加个百度的TTS插件吗?
- 最新开发版,拖动有bug, HOT 2
- Samsung a23 phone error on import config HOT 1
- Importing Unicode replacement json files HOT 3
- Voice name is Null!
- 替换规则自定义排序,无法保存排序结果。
- 想要 取消一下请求接口超时 HOT 3
- [Feature Request] sherpa-onnx tts models support HOT 4
- [FR] Accessibility - Reading Rule Editor unusable with TalkBack
- 如何使用离线语音引擎
- 阅读在GitHub找不到,大佬给个链接 HOT 1
- How to add google cloud tts sever
- 建议加入微软开放的tts HOT 1
- 英文文章无法正确发送英文句子
- 請問系統tts轉發器如何使用?若要轉給pc使用可以嗎?
- 长句分割问题
- 音频文件存放位置在哪?
- 旁白/对话规则优化建议
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from tts-server-android.