2007. november 28., szerda

Getting the length of a Wav file


Problem/Question/Abstract:

How do I get the length of a Wav file without using a TMediaPlayer to open the file?

Answer:

Getting the length is possible using the MCI_SENDSTRING API call, but that does get involved. However, a better method has been suggested that accesses the file directly and interprets its own internal data to obtain the information.

Here is the function:

function GetWaveLength(WaveFile: string): Double;
var
  ��groupID: array[0..3] of char;
  ��riffType: array[0..3] of char;
  ��BytesPerSec: Integer;
  ��Stream: TFileStream;
  ��dataSize: Integer;
  // chunk seeking function,
  // -1 means: chunk not found

  function GotoChunk(ID: string): Integer;
  var
    ��chunkID: array[0..3] of char;
    ��chunkSize: Integer;
  begin
    ��Result := -1;

    with Stream do
      ��begin
        ���� // index of first chunk
      ����Position := 12;
    ����repeat
      ������ // read next chunk
    ������Read(chunkID, 4);
    ������Read(chunkSize, 4);
    �������if chunkID <> ID then
      ������ // skip chunk
    �����Position := Position + chunkSize;
    ������until(chunkID = ID) or (Position >= Size);
    ������if chunkID = ID then
      �������� // chunk found,
    �������� // return chunk size
    ��������Result := chunkSize;
    ����end;
  end;

begin
  ��Result := -1;
  ��Stream := TFileStream.Create(WaveFile, fmOpenRead or fmShareDenyNone);
  ��with Stream do
    ����try
    ������Read(groupID, 4);
  ������Position := Position + 4; // skip four bytes (file size)
  ������Read(riffType, 4);

  ������if(groupID = 'RIFF') and (riffType = 'WAVE') then
    �������begin
    ��������� // search for format chunk
  ���������if GotoChunk('fmt') <> -1 then
    ����������begin
    ����������� // found it
  ������������Position := Position + 8;
  ������������Read(BytesPerSec, 4);
  �������������� //search for data chunk
  ��������������dataSize := GotoChunk('data');

  ��������������if dataSize <> -1 then
    ���������������� // found it
  ����������������Result := dataSize / BytesPerSec
    ������������end
    ��������end
    ����finally
    ������Free;
  ����end;
end;

This returns the number of seconds as a floating point number, which is not necessarily the most helpful format. Far better to return it as a string representing the time in hours, minutes and seconds. The following function achieves this based on the number of seconds as an integer:

function SecondsToTimeStr(RemainingSeconds: Integer): string;
var
  ��Hours, Minutes, Seconds: Integer;
  ��HourString, MinuteString, SecondString: string;
begin
  �� // Calculate Minutes
  ��Seconds := RemainingSeconds mod 60;
  ��Minutes := RemainingSeconds div 60;
  ��Hours := Minutes div 60;
  ��Minutes := Minutes - (Hours * 60);

  ��if Hours < 10 then
    ���HourString := '0' + IntToStr(Hours) + ':'
    �else
    ���HourString := IntToStr(Hours) + ':';

  ��if Minutes < 10 then
    ����MinuteString := '0' + IntToStr(Minutes) + ':'
    ��else
    ����MinuteString := IntToStr(Minutes) + ':';

  ��if Seconds < 10 then
    ����SecondString := '0' + IntToStr(Seconds)
    ��else
    ����SecondString := IntToStr(Seconds);
  ��Result := HourString + MinuteString + SecondString;
end;

Having created these functions you can call them from any relevant event - for example a button click:

procedure TForm1.Button1Click(Sender: TObject);
var
  �Seconds: Integer;
begin
  ��Seconds := Trunc(GetWaveLength(Edit1.Text));
    //gets only the Integer part of the length
  ��Label1.Caption := SecondsToTimeStr(Seconds);
end;

You can even reduce this to a single line of code if you prefer:

procedure TForm1.Button1Click(Sender: TObject);
begin
  ��Label1.Caption := SecondsToTimeStr(Trunc(GetWaveLength(Edit1.Text)));
end;

Nincsenek megjegyzések:

Megjegyzés küldése