aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/base/ULog.pas97
-rw-r--r--src/base/URecord.pas66
2 files changed, 125 insertions, 38 deletions
diff --git a/src/base/ULog.pas b/src/base/ULog.pas
index e4ff4862..ea3cda4e 100644
--- a/src/base/ULog.pas
+++ b/src/base/ULog.pas
@@ -130,7 +130,8 @@ uses
SysUtils,
DateUtils,
URecord,
- UMain,
+ UMain,
+ UMusic,
UTime,
UCommon,
UCommandLine,
@@ -398,26 +399,102 @@ begin
LogMsg(Msg, Context, LOG_LEVEL_CRITICAL);
end;
+type
+ TRiffChunkID = array[0..3] of byte;
+
+ TRiffChunk = packed record
+ ID: TRiffChunkID;
+ DataSize: cardinal;
+ end;
+
+ TRiffHeader = packed record
+ ChunkInfo: TRiffChunk;
+ RiffType: TRiffChunkID;
+ end;
+
+ TWaveFmtChunk = packed record
+ ChunkInfo: TRiffChunk;
+ FormatTag: word;
+ NumChannels: word;
+ SamplesPerSec: cardinal;
+ AvgBytesPerSec: cardinal;
+ BlockAlign: word;
+ BitsPerSample: word;
+ end;
+
procedure TLog.LogVoice(SoundNr: integer);
var
- FS: TBinaryFileStream;
- Prefix: string;
- FileName: IPath;
- Num: integer;
+ Stream: TBinaryFileStream;
+ Prefix: string;
+ FileName: IPath;
+ Num: integer;
+ CaptureBuffer: TCaptureBuffer;
+ Buffer: TMemoryStream;
+ FormatInfo: TAudioFormatInfo;
+ WaveHdr: TRiffHeader;
+ WaveFmt: TWaveFmtChunk;
+ DataChunk: TRiffChunk;
+ UseWavFile: boolean;
+ FileExt: string;
+const
+ Channels = 1;
+ SampleRate = 44100;
+ RIFF_CHUNK_HDR: TRiffChunkID = (Ord('R'), Ord('I'), Ord('F'), Ord('F'));
+ RIFF_CHUNK_FMT: TRiffChunkID = (Ord('f'), Ord('m'), Ord('t'), Ord(' '));
+ RIFF_CHUNK_DATA: TRiffChunkID = (Ord('d'), Ord('a'), Ord('t'), Ord('a'));
+ RIFF_TYPE_WAVE: TRiffChunkID = (Ord('W'), Ord('A'), Ord('V'), Ord('E'));
+ WAVE_FORMAT_PCM = 1; // PCM (uncompressed)
begin
+ CaptureBuffer := AudioInputProcessor.Sound[SoundNr];
+ Buffer := CaptureBuffer.LogBuffer;
+ FormatInfo := CaptureBuffer.AudioFormat;
+
+ // not all formats can be stored in a wav-file
+ UseWavFile := (FormatInfo.Format in [asfU8, asfS16, asfS16LSB]);
+
+ // create output filename
for Num := 1 to 9999 do begin
Prefix := Format('Voice%.4d', [Num]);
- FileName := LogPath.Append(Prefix + '.raw');
+ if (UseWavFile) then
+ FileExt := '.wav'
+ else
+ FileExt := '.raw';
+ FileName := LogPath.Append(Prefix + FileExt);
if not FileName.Exists() then
break
end;
- FS := TBinaryFileStream.Create(FileName, fmCreate);
+ // open output file
+ Stream := TBinaryFileStream.Create(FileName, fmCreate);
+
+ // write wav-file header
+ if (UseWavFile) then
+ begin
+ WaveHdr.ChunkInfo.ID := RIFF_CHUNK_HDR;
+ WaveHdr.ChunkInfo.DataSize := (SizeOf(TRiffHeader) - 8) +
+ SizeOf(TWaveFmtChunk) + SizeOf(TRiffChunk) + Buffer.Size;
+ WaveHdr.RiffType := RIFF_TYPE_WAVE;
+ Stream.Write(WaveHdr, SizeOf(TRiffHeader));
+
+ WaveFmt.ChunkInfo.ID := RIFF_CHUNK_FMT;
+ WaveFmt.ChunkInfo.DataSize := SizeOf(TWaveFmtChunk) - 8;
+ WaveFmt.FormatTag := WAVE_FORMAT_PCM;
+ WaveFmt.NumChannels := FormatInfo.Channels;
+ WaveFmt.SamplesPerSec := Round(FormatInfo.SampleRate);
+ WaveFmt.AvgBytesPerSec := Round(FormatInfo.BytesPerSec);
+ WaveFmt.BlockAlign := FormatInfo.FrameSize;
+ WaveFmt.BitsPerSample := FormatInfo.SampleSize * 8;
+ Stream.Write(WaveFmt, SizeOf(TWaveFmtChunk));
+
+ DataChunk.ID := RIFF_CHUNK_DATA;
+ DataChunk.DataSize := Buffer.Size;
+ Stream.Write(DataChunk, SizeOf(TRiffChunk));
+ end;
- AudioInputProcessor.Sound[SoundNr].LogBuffer.Seek(0, soBeginning);
- FS.CopyFrom(AudioInputProcessor.Sound[SoundNr].LogBuffer, AudioInputProcessor.Sound[SoundNr].LogBuffer.Size);
+ Buffer.Seek(0, soBeginning);
+ Stream.CopyFrom(Buffer, Buffer.Size);
- FS.Free;
+ Stream.Free;
end;
procedure TLog.LogBuffer(const buf: Pointer; const bufLength: Integer; const filename: IPath);
diff --git a/src/base/URecord.pas b/src/base/URecord.pas
index c4b08211..d3886f3d 100644
--- a/src/base/URecord.pas
+++ b/src/base/URecord.pas
@@ -49,14 +49,18 @@ const
type
TCaptureBuffer = class
private
- VoiceStream: TAudioVoiceStream; // stream for voice passthrough
- AnalysisBufferLock: PSDL_Mutex;
+ fVoiceStream: TAudioVoiceStream; // stream for voice passthrough
+ fAnalysisBufferLock: PSDL_Mutex;
+ fAudioFormat: TAudioFormatInfo;
function GetToneString: string; // converts a tone to its string represenatation;
procedure BoostBuffer(Buffer: PByteArray; Size: integer);
procedure ProcessNewBuffer(Buffer: PByteArray; BufferSize: integer);
+ procedure StartCapture(Format: TAudioFormatInfo);
+ procedure StopCapture();
+
// we call it to analyze sound by checking Autocorrelation
procedure AnalyzeByAutocorrelation;
// use this to check one frequency by Autocorrelation
@@ -67,8 +71,6 @@ type
LogBuffer: TMemoryStream; // full buffer
- AudioFormat: TAudioFormatInfo;
-
// pitch detection
// TODO: remove ToneValid, set Tone/ToneAbs=-1 if invalid instead
ToneValid: boolean; // true if Tone contains a valid value (otherwise it contains noise)
@@ -88,6 +90,7 @@ type
function MaxSampleVolume: single;
property ToneString: string READ GetToneString;
+ property AudioFormat: TAudioFormatInfo READ fAudioFormat;
end;
const
@@ -220,7 +223,6 @@ end;
procedure TAudioInputDevice.LinkCaptureBuffer(ChannelIndex: integer; Sound: TCaptureBuffer);
var
- DeviceCfg: PInputDeviceConfig;
OldSound: TCaptureBuffer;
begin
// check bounds
@@ -231,26 +233,13 @@ begin
OldSound := CaptureChannel[ChannelIndex];
if (OldSound <> nil) then
begin
- // close voice stream
- FreeAndNil(OldSound.VoiceStream);
- // free old audio-format info
- FreeAndNil(OldSound.AudioFormat);
+ OldSound.StopCapture();
end;
// set audio-format of new capture-buffer
if (Sound <> nil) then
begin
- // copy the input-device audio-format ...
- Sound.AudioFormat := AudioFormat.Copy;
- // and adjust it because capture buffers are always mono
- Sound.AudioFormat.Channels := 1;
- DeviceCfg := @Ini.InputDeviceConfig[CfgIndex];
-
- if (Ini.VoicePassthrough = 1) then
- begin
- // TODO: map odd players to the left and even players to the right speaker
- Sound.VoiceStream := AudioPlayback.CreateVoiceStream(CHANNELMAP_FRONT, AudioFormat);
- end;
+ Sound.StartCapture(AudioFormat);
end;
// replace old with new buffer (Note: Sound might be nil)
@@ -263,27 +252,27 @@ constructor TCaptureBuffer.Create;
begin
inherited;
LogBuffer := TMemoryStream.Create;
- AnalysisBufferLock := SDL_CreateMutex();
+ fAnalysisBufferLock := SDL_CreateMutex();
AnalysisBufferSize := Length(AnalysisBuffer);
end;
destructor TCaptureBuffer.Destroy;
begin
FreeAndNil(LogBuffer);
- FreeAndNil(VoiceStream);
- FreeAndNil(AudioFormat);
- SDL_DestroyMutex(AnalysisBufferLock);
+ FreeAndNil(fVoiceStream);
+ FreeAndNil(fAudioFormat);
+ SDL_DestroyMutex(fAnalysisBufferLock);
inherited;
end;
procedure TCaptureBuffer.LockAnalysisBuffer();
begin
- SDL_mutexP(AnalysisBufferLock);
+ SDL_mutexP(fAnalysisBufferLock);
end;
procedure TCaptureBuffer.UnlockAnalysisBuffer();
begin
- SDL_mutexV(AnalysisBufferLock);
+ SDL_mutexV(fAnalysisBufferLock);
end;
procedure TCaptureBuffer.Clear;
@@ -305,8 +294,8 @@ begin
BoostBuffer(Buffer, BufferSize);
// voice passthrough (send data to playback-device)
- if (assigned(VoiceStream)) then
- VoiceStream.WriteData(Buffer, BufferSize);
+ if (assigned(fVoiceStream)) then
+ fVoiceStream.WriteData(Buffer, BufferSize);
// we assume that samples are in S16Int format
// TODO: support float too
@@ -529,6 +518,27 @@ begin
end;
end;
+procedure TCaptureBuffer.StartCapture(Format: TAudioFormatInfo);
+begin
+ // free old audio-format info
+ FreeAndNil(fAudioFormat);
+ // copy the new input-device audio-format ...
+ fAudioFormat := Format.Copy;
+ // and adjust it because capture buffers are always mono
+ fAudioFormat.Channels := 1;
+
+ if (Ini.VoicePassthrough = 1) then
+ begin
+ // TODO: map odd players to the left and even players to the right speaker
+ fVoiceStream := AudioPlayback.CreateVoiceStream(CHANNELMAP_FRONT, fAudioFormat);
+ end;
+end;
+
+procedure TCaptureBuffer.StopCapture();
+begin
+ FreeAndNil(fVoiceStream);
+end;
+
{ TAudioInputProcessor }
constructor TAudioInputProcessor.Create;