Delphi7调用MS SAPI合成语音后续短语耗时逐次增加问题求解
问题成因
你观察到的合成耗时递增,本质不是SAPI语音合成本身变慢,是统计音频字节数的实现方式存在额外性能损耗:
每次循环中调用的SpMemoryStream.GetData会将内存流内的全部历史音频数据完整拷贝为OLE变体数组,随着循环执行,流内存储的音频数据越来越多,每次拷贝操作的耗时也会线性增长,最终表现为每轮循环的总耗时越来越长。
解决方案
直接读取TSpMemoryStream自带的Size属性获取当前流的总字节数,完全不需要拷贝整个流的数据:
uses ...SpeechLib_TLB... procedure TForm1.Button2Click(Sender: TObject); var SpVoice: TSpVoice; SpMemoryStream: TSpMemoryStream; SpFileStream: TSpFileStream; SavedCW: Word; i: integer; list: tstrings; time1, time2, bytes1, bytes2: dword; begin ListBox1.Clear; list:=tstringlist.create; list.loadfromfile(extractfilepath(paramstr(0)) + 'test1.txt'); SpVoice:=TSpVoice.create(nil); SpMemoryStream:=TSpMemoryStream.Create(nil); SpMemoryStream.Format.type_:=SAFT44kHz16BitMono; SpVoice.AudioOutputStream:=SpMemoryStream.DefaultInterface; SpFileStream:=TSpFilestream.Create(nil); SpFileStream.Format.type_:=SAFT44kHz16BitMono; SpFileStream.Open(ExtractFilePath(ParamStr(0))+'test1.wav', SSFMCreateForWrite, FALSE); SavedCW := Get8087CW; Set8087CW(SavedCW or $4); for i:=0 to list.count-1 do begin time1:=gettickcount; // 直接读Size属性,无需拷贝全量数据 bytes1:=SpMemoryStream.Size; SpVoice.Speak(list[i], 0); time2:=gettickcount; // 直接读Size属性,无需拷贝全量数据 bytes2:=SpMemoryStream.Size; ListBox1.items.Add(Format('%d ms %d Bytes %s', [time2-time1, bytes2-bytes1, list[i]])); Label1.caption:=format('Item %d From %d', [i+1, list.count]); Application.ProcessMessages; end; Set8087CW(SavedCW); SpFileStream.Write(SpMemoryStream.GetData); SpFileStream.Free; SpMemoryStream.Free; SpVoice.free; list.free; showmessage('All done'); end;
修改后去掉了循环内的全量内存拷贝操作,每轮循环的耗时就会回归到正常的语音合成耗时,不会再出现线性递增的情况。
内容的提问来源于stack exchange,提问作者user1581016
相关产品推荐
相关产品推荐

