diff AudioPac.cpp @ 0:a6a46af64546

first upload
author wenx <xue.wen@eecs.qmul.ac.uk>
date Wed, 10 Aug 2011 14:55:38 +0100
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/AudioPac.cpp	Wed Aug 10 14:55:38 2011 +0100
@@ -0,0 +1,1534 @@
+/*
+    Harmonic Visualiser
+
+    An audio file viewer and editor.
+    Centre for Digital Music, Queen Mary, University of London.
+    This file copyright 2011 Wen Xue.
+
+    This program is free software; you can redistribute it and/or
+    modify it under the terms of the GNU General Public License as
+    published by the Free Software Foundation; either version 2 of the
+    License, or (at your option) any later version.
+*/
+//---------------------------------------------------------------------------
+#include <vcl.h>
+#pragma hdrstop
+
+#include "AudioPac.h"
+#include <math.h>
+
+#pragma package(smart_init)
+//---------------------------------------------------------------------------
+void DoubleToInt(void* out, int BytesPerSample, double* in, int Count)
+{
+  if (BytesPerSample==1){unsigned char* out8=(unsigned char*)out; for (int k=0; k<Count; k++) *(out8++)=*(in++)+128.5;}
+  else if (BytesPerSample==2) {__int16* out16=(__int16*)out; for (int k=0; k<Count; k++) *(out16++)=floor(*(in++)+0.5);}
+  else {__pint24 out24=(__pint24)out; for (int k=0; k<Count; k++) {*(out24++)=floor(*(in++)+0.5);}}
+}
+
+void IntToDouble(double* out, void* in, int BytesPerSample, int Count)
+{
+  if (BytesPerSample==1){unsigned char* in8=(unsigned char*)in; for (int k=0; k<Count; k++) *(out++)=*(in8++)-128.0;}
+  else if (BytesPerSample==2) {__int16* in16=(__int16*)in; for (int k=0; k<Count; k++) *(out++)=*(in16++);}
+  else {__pint24 in24=(__pint24)in; for (int k=0; k<Count; k++) {*(out++)=*(in24++);}}
+}
+
+void DoubleToIntInterleave(void* out, int BytesPerSample, double* in1, double* in2, int Count)
+{
+  if (BytesPerSample==1)
+  {
+    unsigned char* out8=(unsigned char*)out;
+    for (int k=0; k<Count; k++) {*(out8++)=*(in1++)+128; *(out8++)=*(in2++)+128;}
+  }
+  else if (BytesPerSample==2)
+  {
+    __int16* out16=(__int16*)out;
+    for (int k=0; k<Count; k++) {*(out16++)=*(in1++); *(out16++)=*(in2++);}
+  }
+  else if (BytesPerSample==3)
+  {
+    __pint24 out24=(__pint24)out;
+    for (int k=0; k<Count; k++) {*(out24++)=*(in1++); *(out24++)=*(in2++); }
+  }
+}
+
+void IntToDoubleInterleave(double* out1, double* out2, void* in, int BytesPerSample, int Count)
+{
+  if (BytesPerSample==1)
+  {
+    unsigned char* in8=(unsigned char*)in;
+    for (int k=0; k<Count; k++) {*(out1++)=*(in8++)-128.0; *(out2++)=*(in8++)-128.0;}
+  }
+  else if (BytesPerSample==2)
+  {
+    __int16* in16=(__int16*)in;
+    for (int k=0; k<Count; k++) {*(out1++)=*(in16++); *(out2++)=*(in16++);}
+  }
+  else if (BytesPerSample==3)
+  {
+    __pint24 in24=(__pint24)in;
+    for (int k=0; k<Count; k++) {*(out1++)=*(in24++); *(out2++)=*(in24++);}
+  }
+}
+
+int IntToIntInterleave(void* dest, int bytesperunit, void* block1, void* block2, int Count)
+{
+  if (bytesperunit==1)
+  {
+    char* Dest=(char*)dest;
+    char* Block1=(char*)block1;
+    char* Block2=(char*)block2;
+    for (int i=0; i<Count; i++)
+    {
+      *(Dest++)=*(Block1++);
+      *(Dest++)=*(Block2++);
+    }
+    return 2*Count;
+  }
+  else if (bytesperunit==2)
+  {
+    __int16* Dest=(__int16*)dest;
+    __int16* Block1=(__int16*)block1;
+    __int16* Block2=(__int16*)block2;
+    for (int i=0; i<Count; i++)
+    {
+      *(Dest++)=*(Block1++);
+      *(Dest++)=*(Block2++);
+    }
+    return 4*Count;
+  }
+  else if (bytesperunit==3)
+  {
+    __pint24 Dest=(__pint24)dest;
+    __pint24 Block1=(__pint24)block1;
+    __pint24 Block2=(__pint24)block2;
+    for (int i=0; i<Count; i++)
+    {
+      *(Dest++)=*(Block1++);
+      *(Dest++)=*(Block2++);
+    }
+    return 6*Count;
+  }
+  else
+    return 0;
+}
+
+int IntToIntInterleave(void* dest1, void* dest2, int bytesperunit, void* block, int Count)
+{
+  if (bytesperunit==1)
+  {
+    char* Dest1=(char*)dest1;
+    char* Dest2=(char*)dest2;
+    char* Block=(char*)block;
+    for (int i=0; i<Count; i++)
+    {
+      *(Dest1++)=*(Block++);
+      *(Dest2++)=*(Block++);
+    }
+    return 2*Count;
+  }
+  else if (bytesperunit==2)
+  {
+    __int16* Dest1=(__int16*)dest1;
+    __int16* Dest2=(__int16*)dest2;
+    __int16* Block=(__int16*)block;
+    for (int i=0; i<Count; i++)
+    {
+      *(Dest1++)=*(Block++);
+      *(Dest2++)=*(Block++);
+    }
+    return 4*Count;
+  }
+  else if (bytesperunit==2)
+  {
+    __pint24 Dest1=(__pint24)dest1;
+    __pint24 Dest2=(__pint24)dest2;
+    __pint24 Block=(__pint24)block;
+    for (int i=0; i<Count; i++)
+    {
+      *(Dest1++)=*(Block++);
+      *(Dest2++)=*(Block++);
+    }
+    return 6*Count;
+  }
+  else
+    return 0;
+}
+
+int IntToIntMultiSingle(void* dest, int bytesperunit, int channels, int offset, void* block, int Count)
+{
+  if (channels==1)
+  {
+    memcpy(dest, &((char*)block)[offset*bytesperunit], bytesperunit*Count);
+    return Count;
+  }
+  else if (channels==2)
+  {
+    if (bytesperunit==1)
+    {
+      char* Dest=(char*)dest;
+      char* Block=&((char*)block)[offset];
+      for (int i=0; i<Count; i++)
+      {
+        *(Dest++)=*Block;
+        Block+=2;
+      }
+      return Count;
+    }
+    else if (bytesperunit==2)
+    {
+      __int16* Dest=(__int16*)dest;
+      __int16* Block=(__int16*)(&((char*)block)[offset*2]);
+      for (int i=0; i<Count; i++)
+      {
+        *(Dest++)=*Block;
+        Block+=2;
+      }
+      return Count*2;
+    }
+    else if (bytesperunit==3)
+    {
+      __pint24 Dest=(__pint24)dest;
+      __pint24 Block=(__pint24)(&((char*)block)[offset*3]);
+      for (int i=0; i<Count; i++)
+      {
+        *(Dest++)=*Block;
+        Block+=2;
+      }
+      return Count*3;
+    }
+    else
+      return 0;
+  }
+  else
+  {
+    if (bytesperunit==1)
+    {
+      char* Dest=(char*)dest;
+      char* Block=&((char*)block)[offset];
+      for (int i=0; i<Count; i++)
+      {
+        *(Dest++)=*Block;
+        Block+=channels;
+      }
+      return Count;
+    }
+    else if (bytesperunit==2)
+    {
+      __int16* Dest=(__int16*)dest;
+      __int16* Block=(__int16*)(&((char*)block)[offset*2]);
+      for (int i=0; i<Count; i++)
+      {
+        *(Dest++)=*Block;
+        Block+=channels;
+      }
+      return Count*2;
+    }
+    else if (bytesperunit==3)
+    {
+      __pint24 Dest=(__pint24)dest;
+      __pint24 Block=(__pint24)(&((char*)block)[offset*3]);
+      for (int i=0; i<Count; i++)
+      {
+        *(Dest++)=*Block;
+        Block+=channels;
+      }
+      return Count*3;
+    }
+    else return 0;
+  }
+}
+
+int IntToIntMultiChannel(int C, void** dest, int bytesperunit, int channels, void* block, int count)
+{
+  int tail=channels-C;
+  if (bytesperunit==1)
+  {
+    char* data=(char*)block;
+    for (int i=0; i<count; i++)
+    {
+      for (int c=0; c<C; c++) *(((char*)dest[c])++)=*(data++);
+      data=&data[tail];
+    }
+    return count;
+  }
+  else if (bytesperunit==2)
+  {
+    __int16* data=(__int16*)block;
+    for (int i=0; i<count; i++)
+    {
+      for (int c=0; c<C; c++) *(((__int16*)dest[c])++)=*(data++);
+      data=&data[tail];
+    }
+    return count*2;
+  }
+  else if (bytesperunit==3)
+  {
+    __pint24 data=(__pint24)block;
+    for (int i=0; i<count; i++)
+    {
+      for (int c=0; c<C; c++) {*(((__pint24)dest[c])++)=*(data++); }
+      data=&data[tail];
+    }
+    return count*3;
+  }
+  else
+    return 0;
+}
+
+__fastcall TAttachFileStream::TAttachFileStream(TFileStream* AFile)
+    : TStream()
+{
+  File=AFile;
+  StartOffset=0;
+  EndOffset=0;
+}
+
+__fastcall TAttachFileStream::~TAttachFileStream()
+{
+  delete File;
+}
+
+int __fastcall TAttachFileStream::Read(void *Buffer, int Count)
+{
+  return File->Read(Buffer, Count);
+}
+
+int __fastcall TAttachFileStream::Seek(int AnOffset, Word Origin)
+{
+  if (!File) return 0;
+  if (Origin==soFromBeginning)
+    return File->Seek(AnOffset+StartOffset, soFromBeginning)-StartOffset;
+  else if (Origin==soFromCurrent)
+    return File->Seek(AnOffset, soFromCurrent)-StartOffset;
+  else
+    return File->Seek(AnOffset+EndOffset, soFromEnd)-StartOffset;
+}
+
+__int64 __fastcall TAttachFileStream::Seek(const __int64 AnOffset, TSeekOrigin Origin)
+{
+  return Seek((int)AnOffset, (Word)Origin);
+}
+
+int __fastcall TAttachFileStream::Write(const void *Buffer, int Count)
+{
+  return File->Write(Buffer, Count);
+}
+
+//---------------------------------------------------------------------------
+// ValidCtrCheck is used to assure that the components created do not have
+// any pure virtual functions.
+//
+
+static inline void ValidCtrCheck(TWaveAudio *)
+{
+  new TWaveAudio(NULL);
+}
+//---------------------------------------------------------------------------
+__fastcall TWaveAudio::~TWaveAudio()
+{
+  delete FMemoryStream;
+  delete FFileStream;
+  DeallocateHWnd(HWndOut);
+  DeallocateHWnd(HWndIn);
+}
+
+//---------------------------------------------------------------------------
+void __fastcall TWaveAudio::Clear(TObject* Sender)
+{
+  if (!FUseMemoryStream)
+  {
+    FUseMemoryStream=true;
+    delete FFileStream->File;
+    FFileStream->File=0;
+    FFileName="";
+  }
+  FMemoryStream->Position=0;
+  FMemoryStream->Size=0;
+  if (FOnAudioChange) FOnAudioChange(this);
+}
+
+//---------------------------------------------------------------------------
+void __fastcall TWaveAudio::CloseFile(bool Close)
+{
+  if (UseMemoryStream) return;
+  int Position=FFileStream->Position;
+  delete FFileStream->File;
+  FFileStream->File=new TFileStream(FFileName, fmOpenWrite);
+  SaveHeader(FFileStream->File);
+  delete FFileStream->File;
+  FFileStream->File=0;
+  if (!Close)
+  {
+    FFileStream->File=new TFileStream(FFileName, fmOpenRead);
+    FFileStream->Position=Position;
+  }
+}
+
+//---------------------------------------------------------------------------
+void TWaveAudio::CopyFrom(TStream* AStream, int Count)
+{
+  WaveStream->CopyFrom(AStream, Count);
+  if (FOnAudioChange) FOnAudioChange(this);
+}
+
+void TWaveAudio::CopyFrom(TWaveAudio* AWaveAudio, int Count)
+{
+  WaveStream->CopyFrom(AWaveAudio->WaveStream, Count);
+  if (FOnAudioChange) FOnAudioChange(this);
+}
+
+//---------------------------------------------------------------------------
+void TWaveAudio::CopySamplesFrom(TStream* AStream, int Count)
+{
+  WaveStream->CopyFrom(AStream, Count*FChannels*(BitsPerSample/8));
+  if (FOnAudioChange) FOnAudioChange(this);
+}
+
+void TWaveAudio::CopySamplesFrom(TWaveAudio* AWaveAudio, int Count)
+{
+  WaveStream->CopyFrom(AWaveAudio->WaveStream, Count*FChannels*(BitsPerSample/8));
+  if (FOnAudioChange) FOnAudioChange(this);
+}
+
+//---------------------------------------------------------------------------
+void TWaveAudio::CreateFile(AnsiString FileName)
+{
+  UseMemoryStream=false;
+
+  TFileStream* OpenFile=new TFileStream(FileName, fmCreate);
+  SaveHeader(OpenFile);
+  delete OpenFile;
+
+  delete FFileStream->File;
+  FFileStream->File=new TFileStream(FileName, fmOpenReadWrite);
+  FFileStream->StartOffset=FFileStream->File->Size;
+  FFileStream->Position=0;
+  FFileName=FileName;
+}
+
+//---------------------------------------------------------------------------
+int __fastcall TWaveAudio::FillBlock(void* Block)
+{
+  return Stream->Read(Block, FBlockSize);
+}
+
+//---------------------------------------------------------------------------
+int __fastcall TWaveAudio::GetBytesPerSample()
+{
+  return FBitsPerSample/8;
+}
+
+//---------------------------------------------------------------------------
+__int32 __fastcall TWaveAudio::GetLength()
+{
+  return Stream->Size/FBlockAlign;
+}
+
+//---------------------------------------------------------------------------
+TStream* __fastcall TWaveAudio::GetStream()
+{
+  if (FUseMemoryStream) return FMemoryStream;
+  else return FFileStream;
+}
+
+//---------------------------------------------------------------------------
+void TWaveAudio::GetWaveProperties(TWaveAudio* WaveAudio1)
+{
+  FFormatTag=WaveAudio1->FormatTag;
+  SamplesPerSec=WaveAudio1->SamplesPerSec;
+  BitsPerSample=WaveAudio1->BitsPerSample;
+  Channels=WaveAudio1->Channels;
+  BlockSize=WaveAudio1->BlockSize;
+}
+
+//---------------------------------------------------------------------------
+bool __fastcall TWaveAudio::ReadHeader(TStream* AStream, __int32& length, bool allowunfinisheddata)
+{
+  bool success=true;
+  char s[10];
+  __int32 headerlen;
+  __int32 headerstart;
+
+  __int16 formattag;
+  __int16 channels;
+  __int32 samplespersec;
+  __int32 avgbytespersec;
+  __int16 blockalign;
+  __int16 bitspersample;
+
+  int rc=AStream->Read(s, 4); s[4]=0;
+  success=success&&(rc==4)&&!strcmp(s, "RIFF");
+  if (!success) return success;
+
+  rc=AStream->Read(&length, 4);
+  success=success&&(rc==4);
+
+  rc=AStream->Read(s, 4); if (rc!=4) return false;
+  s[4]=0;
+  success=success&&(rc==4)&&!strcmp(s, "WAVE");
+  if (!success) return success;
+
+  rc=AStream->Read(s, 4); s[4]=0;
+  while(rc==4 && strcmp(s, "fmt "))
+  {
+    AStream->Read(&length, 4);
+    AStream->Seek(length, soFromCurrent);
+    rc=AStream->Read(s, 4); s[4]=0;
+  }
+  success=success&&(rc==4);
+  if (!success) return success;
+
+    rc=AStream->Read(&headerlen, 4);
+    headerstart=AStream->Position;
+
+    AStream->Read(&formattag, 2);
+    AStream->Read(&channels, 2);
+    AStream->Read(&samplespersec, 4);
+    AStream->Read(&avgbytespersec, 4);
+    AStream->Read(&blockalign, 2);
+    AStream->Read(&bitspersample, 2);
+    success=success&&(formattag==1);
+    success=success&&(rc==4)&&(blockalign*8==channels*bitspersample);
+    success=success&&(avgbytespersec==samplespersec*blockalign);
+
+  if (!success) return success;
+  AStream->Seek(headerlen+headerstart, soFromBeginning);
+
+  rc=AStream->Read(s, 4); s[4]=0;
+  while(rc==4 && strcmp(s, "data"))
+  {
+    AStream->Read(&length, 4);
+    AStream->Seek(length, soFromCurrent);
+    rc=AStream->Read(s, 4); s[4]=0;
+  }
+  success=success&&(rc==4);
+  if (!success) return success;
+
+  rc=AStream->Read(&length, 4);
+  success=success&&(rc==4);
+  if (!success) return success;
+
+  if (AStream->Position+length>AStream->Size)
+  {
+    if (allowunfinisheddata) length=(AStream->Size-AStream->Position)/blockalign*blockalign;
+    else success=false;
+  }
+  if (!success) return success;
+
+  FFormatTag=formattag;
+  FChannels=channels;
+  FSamplesPerSec=samplespersec;
+  FAvgBytesPerSec=avgbytespersec;
+  FBlockAlign=blockalign;
+  FBitsPerSample=bitspersample;
+
+  return success;
+}
+
+void TWaveAudio::InsertFromFile(AnsiString FileName)
+{
+  TFileStream* OpenFile=new TFileStream(FileName, fmOpenRead|fmShareDenyNone);
+
+  __int32 length;
+  bool success=ReadHeader(OpenFile, length);
+
+  if (success)
+  {
+    FFileName=FileName;
+    if (FUseMemoryStream)
+    {
+      if (length>0)
+      {
+        FMemoryStream->Size=length;
+        OpenFile->Read(FMemoryStream->Memory, length);
+      }
+      delete OpenFile;
+    }
+    else
+    {
+      delete FFileStream->File;
+      FFileStream->File=OpenFile;
+      FFileStream->StartOffset=OpenFile->Position;
+      FFileStream->EndOffset=OpenFile->Position+length-OpenFile->Size;
+    }
+
+    if (FStreamLimit>0 && Stream->Size>=FStreamLimit && OnStreamFull) OnStreamFull(this);
+    if (FOnAudioChange) FOnAudioChange(this);
+  }
+  else
+  {
+    delete OpenFile;
+    throw EReadError("PCM error ecountered reading "+FileName);
+  }
+}
+
+//---------------------------------------------------------------------------
+bool TWaveAudio::IsValidWave(AnsiString FileName)
+{
+  bool success=true;
+  char s[10];
+  __int32 headerlen;
+  __int32 headerstart;
+
+  __int16 formattag;
+  __int16 channels;
+  __int32 samplespersec;
+  __int32 avgbytespersec;
+  __int16 blockalign;
+  __int16 bitspersample;
+  __int32 length;
+
+
+  TFileStream* OpenFile=new TFileStream(FileName,fmOpenRead|fmShareDenyNone);
+
+  OpenFile->Read(s, 4);
+  s[4]=0;
+  success=success&&!strcmp(s, "RIFF");
+
+  OpenFile->Read(&length, 4);
+  success=success&&(length+OpenFile->Position==OpenFile->Size);
+
+  OpenFile->Read(s, 8);
+  s[8]=0;
+  success=success&&!strcmp(s, "WAVEfmt ");
+
+  OpenFile->Read(&headerlen, 4);
+  headerstart=OpenFile->Position;
+
+  OpenFile->Read(&formattag, 2);
+  success=success&&(formattag==1);
+
+  OpenFile->Read(&channels, 2);
+  OpenFile->Read(&samplespersec, 4);
+  OpenFile->Read(&avgbytespersec, 4);
+  OpenFile->Read(&blockalign, 2);
+  OpenFile->Read(&bitspersample, 2);
+  success=success&&(blockalign*8==channels*bitspersample);
+  success=success&&(avgbytespersec==samplespersec*blockalign);
+
+  OpenFile->Seek(headerlen+headerstart, soFromBeginning);
+  OpenFile->Read(s, 4);
+  s[4]=0;
+  success=success&&!strcmp(s, "data");
+
+  OpenFile->Read(&length, 4);
+  success=success&&(length+OpenFile->Position==OpenFile->Size);
+
+  delete OpenFile;
+  return success;
+}
+
+//---------------------------------------------------------------------------
+void TWaveAudio::LoadFromFile(AnsiString FileName)
+{
+  if (FOnBeforeLoad) FOnBeforeLoad(this);
+  if (FAutoUseMemoryStream) UseMemoryStream=false;
+  if (FUseMemoryStream) FMemoryStream->Clear();
+  InsertFromFile(FileName);
+  if (FOnLoad) FOnLoad(this);
+}
+
+//---------------------------------------------------------------------------
+void __fastcall TWaveAudio::OnWimClose(TMessage& Message)
+{
+  waveInUnprepareHeader(WaveIn,WaveHdr1,sizeof(WAVEHDR));
+  waveInUnprepareHeader(WaveIn,WaveHdr2,sizeof(WAVEHDR));
+  delete WaveHdr1;
+  delete WaveHdr2;
+  delete[] Buffer1;
+  delete[] Buffer2;
+  FRecording=false;
+  if (ResetInStream && FOnStreamFull)
+    FOnStreamFull(this);
+  ResetInStream=false;
+  ResetInUser=false;
+  if (FOnRecordingDone) FOnRecordingDone(this);
+  if (FOnAudioChange) FOnAudioChange(this);
+}
+
+//---------------------------------------------------------------------------
+void __fastcall TWaveAudio::OnWimData(TMessage& Message)
+{
+  WAVEHDR* WaveHdr=(WAVEHDR*)Message.LParam;
+  if (!FRecording) return;
+
+  if (!ResetInStream)
+  {
+    ReleaseBlock(WaveHdr->lpData,WaveHdr->dwBytesRecorded);
+    if (FStreamLimit>0 && Stream->Size>=FStreamLimit)
+      ResetInStream=true;
+    waveInAddBuffer(WaveIn,WaveHdr,sizeof(WAVEHDR));
+    if (FOnInAddBuffer) FOnInAddBuffer(this);
+    if (ResetInUser)
+    {
+      waveInReset(WaveIn);
+      waveInClose(WaveIn);
+    }
+  }
+  else
+  {
+    waveInReset(WaveIn);
+    waveInClose(WaveIn);
+  }
+}
+
+//---------------------------------------------------------------------------
+void __fastcall TWaveAudio::OnWimOpen(TMessage& Message)
+{
+//
+}
+
+//---------------------------------------------------------------------------
+void __fastcall TWaveAudio::OnWomClose(TMessage& Message)
+{
+  waveOutUnprepareHeader(WaveOut,WaveHdr1,sizeof(WAVEHDR));
+  waveOutUnprepareHeader(WaveOut,WaveHdr2,sizeof(WAVEHDR));
+  delete WaveHdr1;
+  delete WaveHdr2;
+  delete[] Buffer1;
+  delete[] Buffer2;
+  WaveHdr1=WaveHdr2=0;
+  Buffer1=Buffer2=0;
+  FPlaying=false;
+  FPaused=false;
+  ResetOut=false;
+  ResetOutUser=false;
+  if (FOnPlaybackProgress) FOnPlaybackProgress(this, 1);
+  if (FOnPlaybackProg) FOnPlaybackProg(this, -2);
+
+  if (OnPlaybackDone) OnPlaybackDone(this);
+}
+
+//---------------------------------------------------------------------------
+void __fastcall TWaveAudio::OnWomDone(TMessage& Message)
+{
+  WAVEHDR* WaveHdr=(WAVEHDR*)Message.LParam;
+
+  if (ResetOut)
+  {
+    if  (WaveHdr->dwBufferLength!=(unsigned)FBlockSize)
+    {
+      waveOutClose(WaveOut);
+    }
+  }
+  else
+  {
+    int rc;
+
+    if (ResetOutUser) rc=0;
+    else rc=FillBlock(WaveHdr->lpData);
+
+    if (rc!=FBlockSize)
+    {
+      ResetOut=true;
+      WaveHdr->dwBufferLength=rc;
+      if (BitsPerSample==8) memset(&WaveHdr->lpData[rc],0x80,FBlockSize-rc);
+      else memset(&WaveHdr->lpData[rc],0,FBlockSize-rc);
+      waveOutUnprepareHeader(WaveOut, WaveHdr, sizeof(WAVEHDR));
+      waveOutPrepareHeader(WaveOut, WaveHdr, sizeof(WAVEHDR));
+    }
+    waveOutWrite(WaveOut,WaveHdr,sizeof(WAVEHDR));
+    if (FOnPlaybackProgress)
+      FOnPlaybackProgress(this, (Stream->Position-FBlockSize)*1.0/Stream->Size);
+    if (FOnPlaybackProg)
+      FOnPlaybackProg(this, Stream->Position-FBlockSize);
+    if (FOnOutWrite) FOnOutWrite(this);
+  }
+}
+
+//---------------------------------------------------------------------------
+void __fastcall TWaveAudio::OnWomOpen(TMessage& Message)
+{
+//
+}
+
+//---------------------------------------------------------------------------
+void __fastcall TWaveAudio::Pause(TObject* Sender)
+{
+  if (FPlaying)
+  {
+    waveOutPause(WaveOut);
+    FPaused=true;
+  }
+}
+
+//---------------------------------------------------------------------------
+void __fastcall TWaveAudio::PausePlayback(TObject* Sender)
+{
+  if (Playing) ResetOutUser=true;
+}
+
+//---------------------------------------------------------------------------
+void __fastcall TWaveAudio::PauseRecording(TObject* Sender)
+{
+  if (Recording) ResetInUser=true;
+}
+
+//---------------------------------------------------------------------------
+void __fastcall TWaveAudio::Play(TObject* Sender)
+{
+  Rewind(this);
+  StartPlayback(this);
+}
+
+void __fastcall TWaveAudio::Play(TNotifyEvent AnOnPlaybackDone)
+{
+  Rewind(this);
+  OnPlaybackDone=AnOnPlaybackDone;
+  StartPlayback(this);
+}
+
+//---------------------------------------------------------------------------
+int __fastcall TWaveAudio::Read(void* Buffer, int Count)
+{
+  return Stream->Read(Buffer, Count);
+}
+
+int __fastcall TWaveAudio::ReadSamples(void* Buffer, int Count)
+{
+  return Stream->Read(Buffer, Count*FChannels*(BitsPerSample/8));
+}
+
+int __fastcall TWaveAudio::ReadSamples(double* Buffer, int Count)
+{
+  int FBytesPerSample=FBitsPerSample/8;
+  int countavailable=(Stream->Size-Stream->Position)/FBytesPerSample;
+  if (Count>countavailable) Count=countavailable;
+  if (FUseMemoryStream)
+  {
+    int Position=FMemoryStream->Position;
+    IntToDouble(Buffer, &((char*)FMemoryStream->Memory)[Position], FBytesPerSample, Count);
+    FMemoryStream->Position=Position+Count*FBytesPerSample;
+  }
+  else
+  {
+    void *data=new char[FBlockSize];
+    double *Data=Buffer;
+    int CountPF=FBlockSize/FBytesPerSample;
+    int Fr=(Count/CountPF);
+    for (int i=0; i<Fr; i++)
+    {
+      Stream->Read(data, FBlockSize);
+      IntToDouble(Data, data, FBytesPerSample, CountPF);
+      Data=&Data[CountPF];
+    }
+    int CountLastF=Count%CountPF;
+    int lastblocksize=CountLastF*FBytesPerSample;
+    Stream->Read(data, lastblocksize);
+    IntToDouble(Data, data, FBytesPerSample, CountLastF);
+    delete[] data;
+  }
+
+  return Count*FBytesPerSample;
+}
+
+int __fastcall TWaveAudio::ReadSamplesInterleave(void* Buffer1, void* Buffer2, int Count)
+{
+  int FBytesPerSample=FBitsPerSample/8;
+  int SampleSize=FBytesPerSample*2;
+  int countavailable=(Stream->Size-Stream->Position)/SampleSize;
+  if (Count>countavailable) Count=countavailable;
+  if (FUseMemoryStream)
+  {
+    int Position=FMemoryStream->Position;
+    IntToIntInterleave(Buffer1, Buffer2, FBytesPerSample, &((char*)FMemoryStream->Memory)[Position], Count);
+    FMemoryStream->Position=Position+Count*SampleSize;
+  }
+  else
+  {
+    void *Data1=Buffer1, *Data2=Buffer2, *data=new char[FBlockSize];
+    int HBlockSize=FBlockSize/2, CountPF=FBlockSize/SampleSize;
+    int Fr=Count/CountPF;
+    for (int i=0; i<Fr; i++)
+    {
+      Stream->Read(data, FBlockSize);
+      IntToIntInterleave(Data1, Data2, FBytesPerSample, data, CountPF);
+      Data1=&((char*)Data1)[HBlockSize], Data2=&((char*)Data2)[HBlockSize];
+    }
+    int CountLastF=Count%CountPF;
+    int lastblocksize=CountLastF*SampleSize;
+    Stream->Read(data, lastblocksize);
+    IntToIntInterleave(Data1, Data2, FBytesPerSample, data, CountLastF);
+    delete[] data;
+  }
+  return Count*SampleSize;
+}
+
+int __fastcall TWaveAudio::ReadSamplesInterleave(double* Buffer1, double* Buffer2, int Count)
+{
+  int FBytesPerSample=FBitsPerSample/8;
+  int SampleSize=FBytesPerSample*2;
+  int countavailable=(Stream->Size-Stream->Position)/SampleSize;
+  if (Count>countavailable) Count=countavailable;
+  if (FUseMemoryStream)
+  {
+    int Position=FMemoryStream->Position;
+    IntToDoubleInterleave(Buffer1, Buffer2, &((char*)FMemoryStream->Memory)[Position], FBytesPerSample, Count);
+    FMemoryStream->Position=Position+Count*SampleSize;
+  }
+  else
+  {
+    double *Data1=Buffer1, *Data2=Buffer2;
+    void *data=new char[FBlockSize];
+    int CountPF=FBlockSize/SampleSize;
+    int Fr=Count/CountPF;
+    for (int i=0; i<Fr; i++)
+    {
+      Stream->Read(data, FBlockSize);
+      IntToDoubleInterleave(Data1, Data2, data, FBytesPerSample, CountPF);
+      Data1=&Data1[CountPF], Data2=&Data2[CountPF];
+    }
+    int CountLastF=Count%CountPF;
+    int lastblocksize=CountLastF*SampleSize;
+    Stream->Read(data, lastblocksize);
+    IntToDoubleInterleave(Data1, Data2, data, FBytesPerSample, CountLastF);
+    delete[] data;
+  }
+  return Count*SampleSize;
+}
+
+int __fastcall TWaveAudio::ReadSamplesMultiSingle(void* Buffer, int Channel, int Count)
+{
+  int FBytesPerSample=FBitsPerSample/8;
+  int SampleSize=FBytesPerSample*FChannels;
+  int countavailable=(Stream->Size-Stream->Position)/SampleSize;
+  if (Count>countavailable) Count=countavailable;
+  if (FUseMemoryStream)
+  {
+    int Position=FMemoryStream->Position;
+    IntToIntMultiSingle(Buffer, FBytesPerSample, FChannels, Channel, &((char*)FMemoryStream->Memory)[Position], Count);
+    FMemoryStream->Position=Position+Count*SampleSize;
+  }
+  else
+  {
+    int CountPF=FBlockSize/SampleSize;
+    int ReadSize=CountPF*SampleSize, WriteSize=CountPF*FBytesPerSample;
+    int Fr=Count/CountPF;
+    void *Data=Buffer, *data=new char[ReadSize];
+    for (int i=0; i<Fr; i++)
+    {
+      Stream->Read(data, ReadSize);
+      IntToIntMultiSingle(Data, FBytesPerSample, FChannels, Channel, data, CountPF);
+      Data=&((char*)Data)[WriteSize];
+    }
+    int CountLastF=Count%CountPF;
+    ReadSize=CountLastF*SampleSize;
+    Stream->Read(data, ReadSize);
+    IntToIntMultiSingle(Data, FBytesPerSample, FChannels, Channel, data, CountLastF);
+    delete[] data;
+  }
+  return Count*FBytesPerSample;
+}
+
+int __fastcall TWaveAudio::ReadSamplesMultiChannel(int C, void** Buffer, int Count)
+{
+  if (C>FChannels) C=FChannels;
+  int FBytesPerSample=FBitsPerSample/8;
+  int SampleSize=FBytesPerSample*FChannels;
+  int countavailable=(Stream->Size-Stream->Position)/SampleSize;
+  if (Count>countavailable) Count=countavailable;
+  if (FUseMemoryStream)
+  {
+    int Position=FMemoryStream->Position;
+    IntToIntMultiChannel(C, Buffer, FBytesPerSample, FChannels, &((char*)FMemoryStream->Memory)[Position], Count);
+    FMemoryStream->Position=Position+Count*SampleSize;
+  }
+  else
+  {
+    int CountPF=FBlockSize/SampleSize;
+    int ReadSize=CountPF*SampleSize, WriteSize=CountPF*FBytesPerSample;
+    int Fr=Count/CountPF;
+    void* data=new char[ReadSize];
+    for (int i=0; i<Fr; i++)
+    {
+      Stream->Read(data, ReadSize);
+      IntToIntMultiChannel(C, Buffer, FBytesPerSample, FChannels, data, CountPF);
+      for (int c=0; c<C; c++) Buffer[c]=&((char*)Buffer[c])[WriteSize];
+    }
+    int CountLastF=Count*CountPF;
+    ReadSize=CountLastF*SampleSize;
+    Stream->Read(data, ReadSize);
+    IntToIntMultiChannel(C, Buffer, FBytesPerSample, FChannels, data, CountLastF);
+  }
+  return Count*SampleSize;
+}
+
+//---------------------------------------------------------------------------
+void __fastcall TWaveAudio::ReleaseBlock(void* Block, int BytesRecorded)
+{
+  Stream->Write(Block, BytesRecorded);
+}
+
+//---------------------------------------------------------------------------
+void __fastcall TWaveAudio::Restart(TObject* Sender)
+{
+  if (FPlaying)
+  {
+    waveOutRestart(WaveOut);
+    FPaused=false;
+  }
+}
+
+//---------------------------------------------------------------------------
+void __fastcall TWaveAudio::Rewind(TObject* Sender)
+{
+  Stream->Seek(0,soFromBeginning);
+}
+
+//---------------------------------------------------------------------------
+void __fastcall TWaveAudio::SaveHeader(TStream* AStream)
+{
+  __int32 s32;
+  __int16 s16;
+
+  AStream->Write("RIFF",4);
+  s32=Stream->Size+38;
+  AStream->Write(&s32,4);
+  AStream->Write("WAVEfmt ",8);
+  s32=0x12;
+  AStream->Write(&s32,4);
+  s16=FFormatTag;
+  AStream->Write(&s16,2);
+  s16=FChannels;
+  AStream->Write(&s16,2);
+  s32=FSamplesPerSec;
+  AStream->Write(&s32,4);
+  s32=FAvgBytesPerSec;
+  AStream->Write(&s32,4);
+  s16=FBlockAlign;
+  AStream->Write(&s16,2);
+  s16=FBitsPerSample;
+  AStream->Write(&s16,2);
+  s16=0;
+  AStream->Write(&s16,2);
+  AStream->Write("data",4);
+  s32=Stream->Size;
+  AStream->Write(&s32,4);
+}
+
+void __fastcall TWaveAudio::SaveToFile(AnsiString FileName)
+{
+  TFileStream& SaveFile=*new TFileStream(FileName, fmCreate);
+  SaveHeader(&SaveFile);
+
+  if (FUseMemoryStream)
+    ((TMemoryStream*)Stream)->SaveToStream(&SaveFile);
+  else
+    SaveFile.CopyFrom(Stream, 0);
+  delete &SaveFile;
+}
+
+//---------------------------------------------------------------------------
+void __fastcall TWaveAudio::Seek(int Offset, WORD Origin)
+{
+  WaveStream->Seek(Offset, Origin);
+}
+
+void __fastcall TWaveAudio::SeekSamples(int Offset, WORD Origin)
+{
+  WaveStream->Seek(Offset*FChannels*(BitsPerSample/8), Origin);
+}
+
+//---------------------------------------------------------------------------
+void __fastcall TWaveAudio::SetBitsPerSample(__int16 ABitsPerSample)
+{
+  if (ABitsPerSample!=8 && ABitsPerSample!=16 && ABitsPerSample!=24) return;
+  FBitsPerSample=ABitsPerSample;
+  FAvgBytesPerSec=FSamplesPerSec*FBitsPerSample*FChannels/8;
+  FBlockAlign=FBitsPerSample*FChannels/8;
+}
+
+//---------------------------------------------------------------------------
+void __fastcall TWaveAudio::SetBlockSize(int ABlockSize)
+{
+  FBlockSize=ABlockSize;
+}
+
+//---------------------------------------------------------------------------
+void __fastcall TWaveAudio::SetChannels(__int16 AChannels)
+{
+  if (AChannels!=1 && AChannels!=2) return;
+  FChannels=AChannels;
+  FAvgBytesPerSec=FSamplesPerSec*FBitsPerSample*FChannels/8;
+  FBlockAlign=FBitsPerSample*FChannels/8;
+}
+
+//---------------------------------------------------------------------------
+void __fastcall TWaveAudio::SetLVolume(unsigned __int16 ALVolume)
+{
+  if (ALVolume!=FLVolume)
+  {
+    FLVolume=ALVolume;
+    if (FPlaying) SetVolume();
+  }
+}
+
+//---------------------------------------------------------------------------
+void __fastcall TWaveAudio::SetRVolume(unsigned __int16 ARVolume)
+{
+  if (ARVolume!=FRVolume)
+  {
+    FRVolume=ARVolume;
+    if (FPlaying) SetVolume();
+  }
+}
+
+//---------------------------------------------------------------------------
+void __fastcall TWaveAudio::SetSamplesPerSec(__int32 ASamplesPerSec)
+{
+  FSamplesPerSec=ASamplesPerSec;
+  FAvgBytesPerSec=FSamplesPerSec*FBitsPerSample*FChannels/8;
+}
+
+//---------------------------------------------------------------------------
+void __fastcall TWaveAudio::SetStreamLimit(int AStreamLimit)
+{
+  if (AStreamLimit>0 && Stream->Size>AStreamLimit)
+  {
+    if (OnStreamLimitFailure) OnStreamLimitFailure(this);
+       return;
+  }
+  FStreamLimit=AStreamLimit;
+}
+
+void __fastcall TWaveAudio::SetUseMemoryStream(bool AUseMemoryStream)
+{
+  if (FUseMemoryStream!=AUseMemoryStream)
+  {
+    FUseMemoryStream=AUseMemoryStream;
+  }
+}
+
+//---------------------------------------------------------------------------
+MMRESULT __fastcall TWaveAudio::SetVolume(void)
+{
+  unsigned __int32 Volume=FRVolume;
+  Volume=Volume<<16;
+  Volume+=FLVolume;
+  return waveOutSetVolume(WaveOut, Volume);
+}
+
+//---------------------------------------------------------------------------
+void __fastcall TWaveAudio::StartPlayback(TObject* Sender)
+{
+  if (FPlaying) return;
+
+  WAVEFORMATEX wfx;
+  wfx.wFormatTag=FFormatTag;
+  wfx.nChannels=FChannels;
+  wfx.nSamplesPerSec=FSamplesPerSec;
+  wfx.nAvgBytesPerSec=FAvgBytesPerSec;
+  wfx.nBlockAlign=FBlockAlign;
+  wfx.wBitsPerSample=FBitsPerSample;
+  wfx.cbSize=FcbSize;
+
+	if (waveOutOpen(&WaveOut,WAVE_MAPPER,&wfx,(DWORD)HWndOut, NULL,
+          CALLBACK_WINDOW)!=MMSYSERR_NOERROR)
+  {
+    throw EWriteError("TWaveAudio Err: Failed opening device WaveOut");
+  }
+
+  SetVolume();
+
+  Buffer1=new char[FBlockSize];
+  Buffer2=new char[FBlockSize];
+  WaveHdr1=new WAVEHDR;
+  WaveHdr2=new WAVEHDR;
+
+  WaveHdr1->lpData=(char*)Buffer1;
+  WaveHdr1->dwBufferLength=FBlockSize;
+  WaveHdr1->dwBytesRecorded=0;
+  WaveHdr1->dwUser=0;
+  WaveHdr1->dwFlags=0;
+  WaveHdr1->dwLoops=1;
+  WaveHdr1->lpNext=NULL;
+  WaveHdr1->reserved=0;
+
+  WaveHdr2->lpData=(char*)Buffer2;
+  WaveHdr2->dwBufferLength=FBlockSize;
+  WaveHdr2->dwBytesRecorded=0;
+  WaveHdr2->dwUser=0;
+  WaveHdr2->dwFlags=0;
+  WaveHdr2->dwLoops=1;
+  WaveHdr2->lpNext=NULL;
+  WaveHdr2->reserved=0;
+
+  int rc;
+
+  ResetOut=false;
+  ResetOutUser=false;
+
+  rc=(FillBlock(WaveHdr1->lpData));
+  if (rc!=FBlockSize)
+  {
+    if (BitsPerSample==8) memset(&WaveHdr1->lpData[rc],0x80,FBlockSize-rc);
+    else memset(&WaveHdr1->lpData[rc],0,FBlockSize-rc);
+    WaveHdr1->dwBufferLength=rc;
+    ResetOut=true;
+  }
+  waveOutPrepareHeader(WaveOut,WaveHdr1,sizeof(WAVEHDR));
+  waveOutWrite(WaveOut,WaveHdr1,sizeof(WAVEHDR));
+
+  if (FOnPlaybackProgress)
+    FOnPlaybackProgress(this, 0);
+  if (FOnPlaybackProg)
+    FOnPlaybackProg(this, 0);
+
+  if (rc==BlockSize)
+  {
+    rc=(FillBlock(WaveHdr2->lpData));
+    if (rc!=FBlockSize)
+    {
+      if (BitsPerSample==8) memset(&WaveHdr2->lpData[rc],0x80,FBlockSize-rc);
+      else memset(&WaveHdr2->lpData[rc],0,FBlockSize-rc);
+      WaveHdr2->dwBufferLength=rc;
+      ResetOut=true;
+    }
+
+    waveOutPrepareHeader(WaveOut,WaveHdr2,sizeof(WAVEHDR));
+    waveOutWrite(WaveOut,WaveHdr2,sizeof(WAVEHDR));
+
+    if (FOnPlaybackProgress)
+      FOnPlaybackProgress(this, (Stream->Position-FBlockSize)*1.0/Stream->Size);
+    if (FOnPlaybackProg)
+      FOnPlaybackProg(this, Stream->Position-FBlockSize);
+
+  }
+  FPlaying=true;
+  if (FOnStartPlayback) FOnStartPlayback(this);
+}
+
+//---------------------------------------------------------------------------
+void __fastcall TWaveAudio::StartRecording(TObject* Sender)
+{
+  if (FRecording) return;
+
+  WAVEFORMATEX wfx;
+  wfx.wFormatTag=FFormatTag;
+  wfx.nChannels=FChannels;
+  wfx.nSamplesPerSec=FSamplesPerSec;
+  wfx.nAvgBytesPerSec=FAvgBytesPerSec;
+  wfx.nBlockAlign=FBlockAlign;
+  wfx.wBitsPerSample=FBitsPerSample;
+  wfx.cbSize=FcbSize;
+
+  MMRESULT tr=waveInOpen(&WaveIn, WAVE_MAPPER, &wfx, (unsigned long)HWndIn, NULL,
+          CALLBACK_WINDOW);
+  if (tr!=MMSYSERR_NOERROR && tr!=MMSYSERR_ALLOCATED)
+  {
+      throw EReadError("TWaveAudio err: Failed opening device WaveIn");
+  }
+
+  Buffer1=new char[FBlockSize];
+  Buffer2=new char[FBlockSize];
+  WaveHdr1=new WAVEHDR;
+  WaveHdr2=new WAVEHDR;
+
+  WaveHdr1->lpData=(char*)Buffer1;
+  WaveHdr1->dwBufferLength=FBlockSize;
+  WaveHdr1->dwBytesRecorded=0;
+  WaveHdr1->dwUser=0;
+  WaveHdr1->dwFlags=0;
+  WaveHdr1->dwLoops=1;
+  WaveHdr1->lpNext=NULL;
+  WaveHdr1->reserved=0;
+
+  waveInPrepareHeader(WaveIn,WaveHdr1,sizeof(WAVEHDR));
+
+  WaveHdr2->lpData=(char*)Buffer2;
+  WaveHdr2->dwBufferLength=FBlockSize;
+  WaveHdr2->dwBytesRecorded=0;
+  WaveHdr2->dwUser=0;
+  WaveHdr2->dwFlags=0;
+  WaveHdr2->dwLoops=1;
+  WaveHdr2->lpNext=NULL;
+  WaveHdr2->reserved=0;
+
+  waveInPrepareHeader(WaveIn,WaveHdr2,sizeof(WAVEHDR));
+
+  waveInAddBuffer(WaveIn,WaveHdr1,sizeof(WAVEHDR));
+  waveInAddBuffer(WaveIn,WaveHdr2,sizeof(WAVEHDR));
+
+  ResetInStream=false;
+  ResetInUser=false;
+  FRecording=true;
+
+  waveInStart(WaveIn);
+
+  if (FOnStartRecording) FOnStartRecording(this);
+}
+
+//---------------------------------------------------------------------------
+__fastcall TWaveAudio::TWaveAudio(TComponent* Owner)
+  : TComponent(Owner)
+{
+  FBlockSize=12288;
+  FStreamLimit=0;
+  FUseMemoryStream=true;
+  FAutoUseMemoryStream=true;
+  FMemoryStream=new TMemoryStream;
+  FFileStream=new TAttachFileStream(NULL);
+
+  FPlaying=false;
+  FRecording=false;
+  FPaused=false;
+
+	FOnAudioChange=0;
+	FOnStartPlayback=0;
+  FOnStartRecording=0;
+  FOnPlaybackDone=0;
+  FOnRecordingDone=0;
+  FOnStreamLimitFailure=0;
+  FOnStreamFull=0;
+  FOnPlaybackProgress=0;
+  FOnPlaybackProg=0;
+  FOnInAddBuffer=0;
+  FOnOutWrite=0;
+
+  FSamplesPerSec=44100;
+  FBitsPerSample=16;
+  FChannels=1;
+  FFileName="";
+
+  FFormatTag=WAVE_FORMAT_PCM;
+  FcbSize=0;   //ignored for PCM
+  FAvgBytesPerSec=FSamplesPerSec*FBitsPerSample*FChannels/8;
+  FBlockAlign=FBitsPerSample*FChannels/8;
+
+  HWndOut=AllocateHWnd(WaveOutProc);
+  HWndIn=AllocateHWnd(WaveInProc);
+
+  unsigned long Volume;
+	waveOutGetVolume((void *)WAVE_MAPPER, &Volume);
+  FLVolume=Volume&0x0000FFFF;
+  FRVolume=Volume>>16;
+}
+
+
+//---------------------------------------------------------------------------
+void __fastcall TWaveAudio::WaveInProc(TMessage& Message)
+{
+  switch(Message.Msg)
+  {
+    case MM_WIM_OPEN: OnWimOpen(Message); break;
+    case MM_WIM_DATA: OnWimData(Message); break;
+    case MM_WIM_CLOSE: OnWimClose(Message); break;
+  }
+}
+
+//---------------------------------------------------------------------------
+void __fastcall TWaveAudio::WaveOutProc(TMessage& Message)
+{
+  switch(Message.Msg)
+  {
+    case MM_WOM_OPEN: OnWomOpen(Message); break;
+    case MM_WOM_DONE: OnWomDone(Message); break;
+    case MM_WOM_CLOSE: OnWomClose(Message); break;
+  }
+}
+
+//---------------------------------------------------------------------------
+int __fastcall TWaveAudio::Write(void* Buffer, int Count)
+{
+  if (!FUseMemoryStream)
+  {
+    int Position=FFileStream->Position;
+    delete FFileStream->File;
+    FFileStream->File=new TFileStream(FFileName, fmOpenWrite);
+    FFileStream->Position=Position;
+  }
+  int result=Stream->Write(Buffer, Count);
+  if (!FUseMemoryStream)
+  {
+    int Position=FFileStream->Position;
+    delete FFileStream->File;
+    FFileStream->File=new TFileStream(FFileName, fmOpenRead);
+    FFileStream->Position=Position;
+  }
+  if (FOnAudioChange) FOnAudioChange(this);
+  return result;
+}
+
+int __fastcall TWaveAudio::WriteSamples(void* Buffer, int Count)
+{
+  if (!FUseMemoryStream)
+  {
+    int Position=FFileStream->Position;
+    delete FFileStream->File;
+    FFileStream->File=new TFileStream(FFileName, fmOpenWrite);
+    FFileStream->Position=Position;
+  }
+  int result=Stream->Write(Buffer, Count*FChannels*FBitsPerSample/8);
+  if (!FUseMemoryStream)
+  {
+    int Position=FFileStream->Position;
+    delete FFileStream->File;
+    FFileStream->File=new TFileStream(FFileName, fmOpenRead);
+    FFileStream->Position=Position;
+  }
+  if (FOnAudioChange) FOnAudioChange(this);
+  return result;
+}
+
+int __fastcall TWaveAudio::WriteSamples(double* Buffer, int Count)
+{
+  int result=0;
+  if (!FUseMemoryStream)
+  {
+    int Position=FFileStream->Position;
+    delete FFileStream->File;
+    FFileStream->File=new TFileStream(FFileName, fmOpenWrite);
+    FFileStream->Position=Position;
+  }
+
+  int FBytesPerSample=FBitsPerSample/8;
+  if (FUseMemoryStream)
+  {
+    int Position=FMemoryStream->Position;
+    result=Count*FBytesPerSample;
+    if (FMemoryStream->Size<Position+result) FMemoryStream->Size=Position+result;
+    DoubleToInt(&((char*)FMemoryStream->Memory)[Position], FBytesPerSample, Buffer, Count);
+    FMemoryStream->Position=Position+result;
+  }
+  else
+  {
+    void *data=new char[FBlockSize];
+    double *Data=Buffer;
+    int CountPF=FBlockSize/FBytesPerSample;
+    int Fr=(Count/CountPF);
+    for (int i=0; i<Fr; i++)
+    {
+      DoubleToInt(data, FBytesPerSample, Data, CountPF);
+      result+=Stream->Write(data, FBlockSize);
+      Data=&Data[CountPF];
+    }
+    int CountLastF=Count%CountPF;
+    int lastblocksize=CountLastF*FBytesPerSample;
+    DoubleToInt(data, FBytesPerSample, Data, CountLastF);
+    result+=Stream->Write(data, lastblocksize);
+    delete[] data;
+  }
+
+  if (!FUseMemoryStream)
+  {
+    int Position=FFileStream->Position;
+    delete FFileStream->File;
+    FFileStream->File=new TFileStream(FFileName, fmOpenRead);
+    FFileStream->Position=Position;
+  }
+  if (FOnAudioChange) FOnAudioChange(this);
+  return result;
+}
+
+int __fastcall TWaveAudio::WriteSamplesInterleave(void* Buffer1, void* Buffer2, int Count)
+{
+  int FBytesPerSample=FBitsPerSample/8;
+  int SampleSize=FBytesPerSample*2;
+  if (FUseMemoryStream)
+  {
+    int Position=FMemoryStream->Position;
+    int countavailable=(FMemoryStream->Size-Position)/SampleSize;
+    if (countavailable<Count) FMemoryStream->Size=Position+SampleSize*Count;
+    IntToIntInterleave(&((char*)FMemoryStream->Memory)[Position], FBytesPerSample, Buffer1, Buffer2, Count);
+    FMemoryStream->Position=Position+SampleSize*Count;
+  }
+  else
+  {
+    int Position=Stream->Position;
+    delete FFileStream->File;
+    FFileStream->File=new TFileStream(FFileName, fmOpenWrite);
+    FFileStream->Position=Position;
+
+    int CountPF=FBlockSize/SampleSize, HBlockSize=FBlockSize/2;
+    int Fr=Count/CountPF;
+    void *Data1=Buffer1, *Data2=Buffer2, *data=new char[FBlockSize];
+    for (int i=0; i<Fr; i++)
+    {
+      IntToIntInterleave(data, FBytesPerSample, Data1, Data2, CountPF);
+      Stream->Write(data, FBlockSize);
+      Data1=&((char*)Data1)[HBlockSize], Data2=&((char*)Data2)[HBlockSize];
+    }
+    int CountLastF=Count%CountPF;
+    int lastblocksize=CountLastF*SampleSize;
+    IntToIntInterleave(data, FBytesPerSample, Data1, Data2, CountLastF);
+    Stream->Write(data, lastblocksize);
+
+    Position=Stream->Position;
+    delete FFileStream->File;
+    FFileStream->File=new TFileStream(FFileName, fmOpenRead);
+    FFileStream->Position=Position;
+  }
+  if (FOnAudioChange) FOnAudioChange(this);
+  return Count*SampleSize;
+}
+
+int __fastcall TWaveAudio::WriteSamplesInterleave(double* Buffer1, double* Buffer2, int Count)
+{
+  int FBytesPerSample=FBitsPerSample/8;
+  int SampleSize=FBytesPerSample*2;
+  if (FUseMemoryStream)
+  {
+    int Position=FMemoryStream->Position;
+    int countavailable=(FMemoryStream->Size-Position)/SampleSize;
+    if (countavailable<Count) FMemoryStream->Size=Position+SampleSize*Count;
+    DoubleToIntInterleave(&((char*)FMemoryStream->Memory)[Position], FBytesPerSample, Buffer1, Buffer2, Count);
+    FMemoryStream->Position=Position+SampleSize*Count;
+  }
+  else
+  {
+    int Position=Stream->Position;
+    delete FFileStream->File;
+    FFileStream->File=new TFileStream(FFileName, fmOpenWrite);
+    FFileStream->Position=Position;
+
+    int CountPF=FBlockSize/SampleSize;
+    int Fr=Count/CountPF;
+    double *Data1=Buffer1, *Data2=Buffer2;
+    void *data=new char[FBlockSize];
+    for (int i=0; i<Fr; i++)
+    {
+      DoubleToIntInterleave(data, FBytesPerSample, Data1, Data2, CountPF);
+      Stream->Write(data, FBlockSize);
+      Data1=&Data1[CountPF], Data2=&Data2[CountPF];
+    }
+    int CountLastF=Count%CountPF;
+    int lastblocksize=CountLastF*SampleSize;
+    DoubleToIntInterleave(data, FBytesPerSample, Data1, Data2, CountLastF);
+    Stream->Write(data, lastblocksize);
+
+    Position=Stream->Position;
+    delete FFileStream->File;
+    FFileStream->File=new TFileStream(FFileName, fmOpenRead);
+    FFileStream->Position=Position;
+  }
+  if (FOnAudioChange) FOnAudioChange(this);
+  return Count*SampleSize;
+}
+//---------------------------------------------------------------------------
+__fastcall TDataAudio::TDataAudio(TComponent* Owner)
+      : TWaveAudio(Owner)
+{
+  FCustomFillBlock=0;
+}
+
+int __fastcall TDataAudio::FillBlock(void* Block)
+{
+  if (FCustomFillBlock)
+    return FCustomFillBlock(Block);
+  else
+    return TWaveAudio::FillBlock(Block);
+}
+
+//---------------------------------------------------------------------------
+namespace Audiopac
+{
+  void __fastcall PACKAGE Register()
+  {
+    TComponentClass classes[1] = {__classid(TWaveAudio)};
+    RegisterComponents("Samples", classes, 0);
+  }
+}
+//---------------------------------------------------------------------------
+
+