📄 unaaudiofeedback.pas
字号:
(*
----------------------------------------------
unaAudioFeedback.pas
Voice Communicator components version 2.5
Audio Feedabck routines
----------------------------------------------
This source code cannot be used without
proper permission granted to you as a private
person or an entity by the Lake of Soft, Ltd
Visit http://lakeofsoft.com/ for details.
Copyright (c) 2001, 2005 Lake of Soft, Ltd
All rights reserved
----------------------------------------------
created by:
Lake, 11 Aug 2005
modified by:
Lake, Aug 2005
Lake, Nov 2005
----------------------------------------------
*)
{$I unaDef.inc}
unit
unaAudioFeedback;
interface
uses
Windows, unaTypes, unaClasses, MMSystem, unaMsAcmClasses;
const
// feedback class status
c_stat_afStopped = 0; // feedback was stopped or not started
c_stat_afActive = 1; // feedback is running OK
c_stat_afErrorIn = 2; // some error occured with waveIn device, see errorCode for delails
c_stat_afErrorOut = 3; // some error occured with waveOut device, see errorCode for delails
//
c_recordWaveCmd_start = 1; // starts recording into a file
c_recordWaveCmd_stop = 2; // stops recording
type
// -- --
myWaveOutDevice = class(unaWaveOutDevice)
private
f_inProgress: int;
protected
function onHeaderDone(header: unaWaveHeader; wakeUpByHeaderDone: bool): bool; override;
end;
// -- --
myWaveHeader = class(unaWaveHeader)
// this class is only needed to get access to protected methods of unaWaveHeader class
end;
//
// -- audio feedback class --
//
unaAudioFeedbackClass = class(unaThread)
private
f_errorCode: int;
f_status: int;
f_delay: unsigned;
f_delayAsCount: int;
//
f_waveFile: unaRiffStream;
//
f_waveIn: unaWaveInDevice;
f_waveOut: myWaveOutDevice;
//
f_waveOutHeaders: array[byte] of myWaveHeader;
f_silence: pointer;
//
f_waveChunksThrownAway: int64;
f_waveOutLastHeaderIndex: int;
f_onDA: unaWaveDataEvent;
//
procedure setDelay(delay: unsigned);
function getWaveFormat(): pWAVEFORMATEX;
function getWaveOut(): unaWaveOutDevice;
//
procedure onWaveDataAvailable(sender: tObject; data: pointer; len: unsigned);
protected
procedure startIn(); override;
procedure startOut(); override;
function execute(threadId: unsigned): int; override;
public
procedure AfterConstruction(); override;
procedure BeforeDestruction(); override;
//
function setup(delay: unsigned = 40; inDeviceId: int = int(WAVE_MAPPER); outDeviceId: int = int(WAVE_MAPPER); format: pWAVEFORMATEX = nil): HRESULT;
function recordWaveCmd(cmd: int; const fileName: wideString = ''): HRESULT;
//
property status: int read f_status;
property errorCode: int read f_errorCode;
property delay: unsigned read f_delay write setDelay;
//
property waveFormat: pWAVEFORMATEX read getWaveFormat;
//
property waveIn: unaWaveInDevice read f_waveIn;
property waveOut: unaWaveOutDevice read getWaveOut; // stupid Delphi compiler canot directly cast f_waveOut as unaWaveOutDevice.
//
property onDataAvailable: unaWaveDataEvent read f_onDA write f_onDA;
end;
implementation
uses
unaUtils;
{ myWaveOutDevice }
// -- --
function myWaveOutDevice.onHeaderDone(header: unaWaveHeader; wakeUpByHeaderDone: bool): bool;
begin
if (wakeUpByHeaderDone) then
dec(f_inProgress);
//
result := true; // no need to call overitten method
end;
{ unaAudioFeedbackClass }
// -- --
procedure unaAudioFeedbackClass.afterConstruction();
begin
f_status := c_stat_afStopped;
f_errorCode := 0;
//
f_waveIn := unaWaveInDevice.create();
f_waveOut := myWaveOutDevice.create();
//
f_waveIn.assignStream(false, nil); // remove outgoing stream
//
waveIn.onDataAvailable := onWaveDataAvailable;
//
inherited;
//
setup(); // assign default values
end;
// -- --
procedure unaAudioFeedbackClass.beforeDestruction();
begin
inherited; // should stop thread and close devices
//
freeAndNil(f_waveIn);
freeAndNil(f_waveOut);
end;
// -- --
function unaAudioFeedbackClass.execute(threadId: unsigned): int;
begin
if (0 = f_errorCode) then
f_status := c_stat_afActive;
//
while (not shouldStop) do begin
//
sleep(100);
end;
//
result := 0;
end;
// -- --
function unaAudioFeedbackClass.getWaveFormat(): pWAVEFORMATEX;
begin
result := waveIn.dstFormat;
end;
// -- --
function unaAudioFeedbackClass.getWaveOut(): unaWaveOutDevice;
begin
result := f_waveOut;
end;
// -- --
procedure unaAudioFeedbackClass.onWaveDataAvailable(sender: tObject; data: pointer; len: unsigned);
var
header: myWaveHeader;
res: MMRESULT;
begin
// check if we can feed outbuffer now
if (f_waveOut.f_inProgress <= f_delayAsCount) then begin
//
// locate unused header
inc(f_waveOutLastHeaderIndex);
if (f_waveOutLastHeaderIndex > high(f_waveOutHeaders)) then
f_waveOutLastHeaderIndex := low(f_waveOutHeaders); // dirty hack, assuming we will never run out of headers
//
header := f_waveOutHeaders[f_waveOutLastHeaderIndex];
//
// send audio chunk to waveOut device
header.rePrepare();
header.setData(data, len);
//
res := f_waveOut.addHeader(header);
if (mmNoError(res)) then
inc(f_waveOut.f_inProgress);
//
end
else
inc(f_waveChunksThrownAway);
//
// check if we need to increase the delay
while (not shouldStop and (f_waveOut.f_inProgress < f_delayAsCount - 2)) do begin
//
// send silence to waveOut
onWaveDataAvailable(sender, f_silence, waveIn.chunkSize);
end;
//
if (enter(20)) then begin
//
try
if (nil <> f_waveFile) then
f_waveFile.write(data, len);
//
finally
leave();
end;
end;
//
if assigned(f_onDA) then
f_onDA(self, data, len);
end;
// -- --
function unaAudioFeedbackClass.recordWaveCmd(cmd: int; const fileName: wideString): HRESULT;
begin
result := HRESULT(-2);
//
if (enter(100)) then begin
//
try
//
case (cmd) of
c_recordWaveCmd_start: begin
//
if ((nil <> f_waveFile) and sameStringW(f_waveFile.fileName, fileName)) then begin
//
result := S_OK; // already recording into this file
end
else begin
//
freeAndNil(f_waveFile);
f_waveFile := unaRiffStream.createNew(fileName, waveIn.dstFormat^);
//
f_waveFile.open();
//
result := S_OK;
end;
end;
c_recordWaveCmd_stop: begin
//
freeAndNil(f_waveFile);
//
result := S_OK;
end;
else
result := HRESULT(-1);
end;
//
finally
leave();
end;
end;
end;
// -- --
procedure unaAudioFeedbackClass.setDelay(delay: unsigned);
begin
f_delay := delay;
f_delayAsCount := delay div (1000 div c_defChunksPerSecond);
end;
// -- --
function unaAudioFeedbackClass.setup(delay: unsigned; inDeviceId, outDeviceId: int; format: pWAVEFORMATEX): HRESULT;
begin
stop(); // just in case
//
self.delay := delay;
waveIn.deviceId := unsigned(inDeviceId);
waveOut.deviceId := unsigned(outDeviceId);
//
if (nil <> format) then
waveIn.setSampling(format^)
else
waveIn.setSampling(22050, 16, 1);
//
waveOut.setSampling(waveIn.dstFormat^);
//
result := 0;
end;
// -- --
procedure unaAudioFeedbackClass.startIn();
var
i: int;
res: MMRESULT;
begin
// try opening waveIn/Out devices
res := waveOut.open();
if (mmNoError(res)) then begin
//
// create waveOut headers (need to do that _before_ activating the waveIn
for i := low(f_waveOutHeaders) to high(f_waveOutHeaders) do begin
//
f_waveOutHeaders[i] := myWaveHeader.create(waveOut, waveOut.chunkSize);
f_waveOutHeaders[i].prepare();
end;
//
f_silence := malloc(waveIn.chunkSize, true, choice(8 = waveIn.dstFormat.wBitsPerSample, $80, int(0)));
//
f_waveChunksThrownAway := 0;
//
res := waveIn.open();
if (mmNoError(res)) then begin
//
// looks like all devices were activated normally
f_errorCode := 0;
end
else begin
//
f_status := c_stat_afErrorIn;
f_errorCode := res;
end;
end
else begin
//
f_status := c_stat_afErrorOut;
f_errorCode := res;
end;
//
inherited;
end;
// -- --
procedure unaAudioFeedbackClass.startOut();
var
i: int;
begin
// close all devices
waveIn.close();
//
// release waveOut headers (need to do that _before_ activating the waveIn
for i := low(f_waveOutHeaders) to high(f_waveOutHeaders) do begin
//
f_waveOutHeaders[i].unprepare();
freeAndNil(f_waveOutHeaders[i]);
end;
//
waveOut.close();
//
mrealloc(f_silence);
f_waveOut.f_inProgress := 0;
//
f_status := c_stat_afStopped;
//
recordWaveCmd(c_recordWaveCmd_stop);
//
inherited;
end;
end.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -