.WAV as BLOBs in Paradox tables - wav-db.txt [01/01]

Someone asked how to save .WAV files in a Paradox table, but I fumbled the
message.  Here is the info I have.  I am writing an app that will be saving
dozens of sound bites in a Paradox table, and I am using .WAV files to record
the sounds originally and to play them back.

BEGIN -- Cut Here -- cut here
This file describes how to create a blank .WAV file so that
you can use TMediaPlayer to record sound.  Further, it tells
how to save those sounds to TBlobFields in a Paradox table.

I used the Delphi newsgroups to get this information during
the summer of '96, and I'm answering you because it looked
like (in your post) that you wanted this or similar
information.  

I am including the response I received to my post first,
followed by the unit I used that information in.  I expect
this unit to be in daily use when the application I'm
writing is finished.  It has worked fine so far.

------------------------------------------------------------

Hi Rick,

Here's a reply I gave some time ago, it works for all the WAV formats
that I have tried, although someone in the DSP newsgroup pointed out
that it wouldn't work correctly for his obscure sound card because his
wasn't PCM, but oi say {*word*81}'im :}

Here goes...

The WAV file has basically two bits, a header and the actual sample
data...

THE WAV FILE HEADER RECORD
--------------------------
I had a quick check in the WAV file specs and derived the following
record which represents the header of a WAV file, don't worry too much
about the variables in the record...

include this in the types section of your project

 { format of WAV file header }
 TWavHeader = record         { parameter description }
  rId             : longint; { 'RIFF'  4 characters }
  rLen            : longint; { length of DATA + FORMAT chunk }
  { FORMAT CHUNK }
  wId             : longint; { 'WAVE' }
  fId             : longint; { 'fmt ' }
  fLen            : longint; { length of FORMAT DATA = 16 }
  { format data }
  wFormatTag      : word;    { $01 = PCM }
  nChannels       : word;    { 1 = mono, 2 = stereo }
  nSamplesPerSec  : longint; { Sample frequency ie 11025}
  nAvgBytesPerSec : longint; { = nChannels * nSamplesPerSec *
                                (nBitsPerSample/8) }
  nBlockAlign     : word;    { = nChannels * (nBitsPerSAmple / 8 }
  wBitsPerSample  : word;    { 8 or 16 }
  { DATA CHUNK }
  dId             : longint; { 'data' }
  wSampleLength   : longint; { length of SAMPLE DATA }
  { sample data : offset 44 }
   { for 8 bit mono = s[0],s[1]... :byte}
   { for 8 bit stereo = sleft[0],sright[0],sleft[1],sright[1]... :byte}
   { for 16 bit mono = s[0],s[1]... :word}
   { for 16 bit stereo = sleft[0],sright[0],sleft[1],sright[1]... :word}
 end;

THE CREATE NEW WAV FILE PROCEDURE
---------------------------------
OK, now that we have defined the header, here is a routine which will
create a blank wav file with the specified parameters...
 channels   : 1=mono, 2=stereo
 resolution : 8 or 16 bits
 rate       : sample rate ie 11025, 44100 etc
 fn         : name of new file

include this in the implementation section

{ create new wav file }
procedure CreateWav( channels   : word;    { 1 or 2      }
                     resolution : word;    { 8 or 16     }
                     rate       : longint; { sample rate }
                     fn         : string   { file name   } );
var
 wf : file of TWavHeader;
 wh : TWavHeader;
begin
 wh.rId             := $46464952; { 'RIFF' }
 wh.rLen            := 36;        { length of sample + format }
 wh.wId             := $45564157; { 'WAVE' }
 wh.fId             := $20746d66; { 'fmt ' }
 wh.fLen            := 16;        { length of format chunk }
 wh.wFormatTag      := 1;         { PCM data }
 wh.nChannels       := channels;  { mono/stereo }
 wh.nSamplesPerSec  := rate;      { sample rate }
 wh.nAvgBytesPerSec := channels*rate*(resolution div 8);
 wh.nBlockAlign     := channels*(resolution div 8);
 wh.wBitsPerSample  := resolution;{ resolution 8/16 }
 wh.dId             := $61746164; { 'data' }
 wh.wSampleLength   := 0;         { sample size }

 assignfile(wf,fn);               { assign file }
 rewrite(wf);                     { open file for writing }
 write(wf,wh);                    { write header }
 closefile(wf);                   { close file }
end;

NOTE : this routine is not perfect in that it should have some form of
error checking to make sure that the file was actually created.

USING THE ROUTINE - AN EXAMPLE
------------------------------
To create a new wav file call BOBBY.WAV in 8 bit stereo at 11KHz...

 CreateWav(1,8,11025,'bobby.wav');

Once this has been done, you can the open the mediaplayer with...

 mediaplayer1.filename:='bobby.wav';
 mediaplayer1.open;

Then if you start recording with...

 mediaplayer1.startrecording;

The blank file will be opened to determine the sampling parameters and
recording will begin INTO A MEMORY BUFFER !!!! until you say...

 mediaplayer1.stop;

*** THEN YOU MUST SAY... ***

 mediaplayer1.save;

TO SAVE THE NEW RECORDING BACK INTO THE FILE !!!

OK
--

I tried this last night with various rates, resolutions etc and it
seemed to work fine. I checked the recorded wav files in a PROPER sound
editor and it read/ played them ok. So I am assuming I have done this
correctly, if not efficiently !

Have much fun..............

/\        Andy Clark         /\
\/  Up to his neck as usual  \/

============================================================
============================================================

Here is the unit I am using the .WAV procedures in.  I am using
my own buttons in place of the MediaPlayer buttons, but I
think that you should be able to see where I am going with
this.

I hope this file has been of some help.  Please let me know
if I can answer any further questions.

Rick

--------------------------------------------------------------

unit Main;

interface

uses
  SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
  Forms, Dialogs, MPlayer, StdCtrls, DB, DBTables, ExtCtrls, DBCtrls,
  Grids, DBGrids;

type
  TForm1 = class(TForm)
    RecordButton: TButton;
    PlayButton: TButton;
    StopButton: TButton;
    Table1: TTable;
    RadioGroup1: TRadioGroup;
    MediaPlayer1: TMediaPlayer;
    DataSource1: TDataSource;
    DBCheckBoxWord: TDBCheckBox;
    DBCheckBoxSentence: TDBCheckBox;
    DBCheckBoxSpelling: TDBCheckBox;
    DBGrid1: TDBGrid;
    DBNavigator1: TDBNavigator;
    Table1List: TStringField;
    Table1Date: TDateField;
    Table1Word: TStringField;
    Table1Spoken: TBlobField;
    Table1Spoken2: TBooleanField;
    Table1Sentence: TBlobField;
    Table1Sentence2: TBooleanField;
    Table1Spelled: TBlobField;
    Table1Spelled2: TBooleanField;
    procedure StopButtonClick(Sender: TObject);
    procedure RecordButtonClick(Sender: TObject);
    procedure PlayButtonClick(Sender: TObject);
    procedure MediaPlayer1Notify(Sender: TObject);
    procedure Table1BeforeEdit(DataSet: TDataset);
    procedure Table1AfterPost(DataSet: TDataset);
    procedure DBGrid1DblClick(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

   { format of WAV file header }
 TWavHeader = record         { parameter description }
  rId             : longint; { 'RIFF'  4 characters }
  rLen            : longint; { length of DATA + FORMAT chunk }
  { FORMAT CHUNK }
  wId             : longint; { 'WAVE' }
  fId             : longint; { 'fmt ' }
  fLen            : longint; { length of FORMAT DATA = 16 }
  { format data }
  wFormatTag      : word;    { $01 = PCM }
  nChannels       : word;    { 1 = mono, 2 = stereo }
  nSamplesPerSec  : longint; { Sample frequency ie 11025}
  nAvgBytesPerSec : longint; { = nChannels * nSamplesPerSec *
                                (nBitsPerSample/8) }
  nBlockAlign     : word;    { = nChannels * (nBitsPerSAmple / 8 }
  wBitsPerSample  : word;    { 8 or 16 }
  { DATA CHUNK }
  dId             : longint; { 'data' }
  wSampleLength   : longint; { length of SAMPLE DATA }
  { sample data : offset 44 }
   { for 8 bit mono = s[0],s[1]... :byte}
   { for 8 bit stereo = sleft[0],sright[0],sleft[1],sright[1]... :byte}
   { for 16 bit mono = s[0],s[1]... :word}
   { for 16 bit stereo = sleft[0],sright[0],sleft[1],sright[1]... :word}
 end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

{ create new wav file }
procedure CreateWav( channels   : word;    { 1 or 2      }
                     resolution : word;    { 8 or 16     }
                     rate       : longint; { sample rate }
                     fn         : string   { file name   } );
var
 wf : file of TWavHeader;
 wh : TWavHeader;
begin
 wh.rId             := $46464952; { 'RIFF' }
 wh.rLen            := 36;        { length of sample + format }
 wh.wId             := $45564157; { 'WAVE' }
 wh.fId             := $20746d66; { 'fmt ' }
 wh.fLen            := 16;        { length of format chunk }
 wh.wFormatTag      := 1;         { PCM data }
 wh.nChannels       := channels;  { mono/stereo }
 wh.nSamplesPerSec  := rate;      { sample rate }
 wh.nAvgBytesPerSec := channels*rate*(resolution div 8);
 wh.nBlockAlign     := channels*(resolution div 8);
 wh.wBitsPerSample  := resolution;{ resolution 8/16 }
 wh.dId             := $61746164; { 'data' }
 wh.wSampleLength   := 0;         { sample size }

 assignfile(wf,fn);               { assign file }
 {$I-}
  Reset(wf);
 {$I+}
 if IOResult = 0 then erase(wf);
 rewrite(wf);                     { open file for writing }
 write(wf,wh);                    { write header }
 closefile(wf);                   { close file }
end;

procedure TForm1.RecordButtonClick(Sender: TObject);
begin
 CreateWav(1,8,11025,'~temp~.wav');
 MediaPlayer1.Filename := '~temp~.wav';
 MediaPlayer1.Open;
 if mpCanRecord in MediaPlayer1.Capabilities then begin
  RecordButton.Caption := 'Begin';
  RecordButton.Enabled := False;
  PlayButton.Enabled := False;
  StopButton.Enabled := True;
  StopButton.SetFocus;
  end;
 MediaPlayer1.Enabled := True;
 MediaPlayer1.StartRecording;
end;

procedure TForm1.StopButtonClick(Sender: TObject);
begin
 MediaPlayer1.Stop;
 StopButton.Enabled := False;
 RecordButton.Enabled := True;
 RecordButton.Caption := 'Record';
 PlayButton.Enabled := True;
 PlayButton.SetFocus;
 MediaPlayer1.Enabled := False;
 MediaPlayer1.Save;
 MediaPlayer1.Close;
 Table1.Edit;
 If RadioGroup1.ItemIndex = 0 then begin
    Table1Spoken.LoadFromFile('~temp~.wav');
    Table1.FieldByName('Spoken?').AsBoolean := True;
    end;
 If RadioGroup1.ItemIndex = 1 then begin
    Table1Sentence.LoadFromFile('~temp~.wav');
    Table1.FieldByName('Sentence?').AsBoolean := True;
    end;
 If RadioGroup1.ItemIndex = 2 then begin
    Table1Spelled.LoadFromFile('~temp~.wav');
    Table1.FieldByName('Spelled?').AsBoolean := True;
    end;
 Table1.Post;
end;

procedure TForm1.PlayButtonClick(Sender: TObject);
begin
 CreateWav(1,8,11025,'~temp~.wav');
 MediaPlayer1.Filename := '~temp~.wav';
 If RadioGroup1.ItemIndex = 0 then
    Table1Spoken.SaveToFile('~temp~.wav');
 If RadioGroup1.ItemIndex = 1 then
    Table1Sentence.SaveToFile('~temp~.wav');
 If RadioGroup1.ItemIndex = 2 then
    Table1Spelled.SaveToFile('~temp~.wav');
 MediaPlayer1.Open;
 MediaPlayer1.Rewind;
 MediaPlayer1.Enabled := True;
 MediaPlayer1.Play;
 RecordButton.Enabled := False;
 MediaPlayer1.Notify := True;
end;

procedure TForm1.MediaPlayer1Notify(Sender: TObject);
begin
 If MediaPlayer1.Enabled then begin
 MediaPlayer1.Notify := False;
 MediaPlayer1.Enabled := False;
 MediaPlayer1.Close;
 end; {If MediaPlayer1.Enabled}
end;

procedure TForm1.Table1BeforeEdit(DataSet: TDataset);
begin
 RecordButton.Enabled := True;
 RecordButton.SetFocus;
end;

procedure TForm1.Table1AfterPost(DataSet: TDataset);
begin
 RecordButton.Enabled := False;
end;

procedure TForm1.DBGrid1DblClick(Sender: TObject);
begin
 Table1.Edit;
end;

end.

END -- Cut Here -- cut here

Rick Dubbs, CNA
Technology Coordinator
Monroe-Gregg School District
Monrovia (near Indianapolis), Indiana, USA