DELPHI FAQ: Декомпилляция звукового файла формата Wave и получение звуковых данных


Декомпилляция звукового файла формата Wave и получение звуковых данных
Previous  Home  Next



Интересно, есть ли технология преобразования Wave-формата в обычный набор звуковых данных? К примеру, мне необходимо удалить заголовок и механизм (метод) сжатия, которые могут компилироваться и сохраняться вместе с Wave-файлами.

У меня есть программа под D1/D2, которая читает WAV-файлы и вытаскивает исходные данные, но она не может их восстанавить, используя зашитый алгоритм сжатия.

unit
 LinearSystem;

interface


{============== Тип, описывающий формат WAV ==================}

type

  WAVHeader = record


    nChannels: Word;
    nBitsPerSample: LongInt;
    nSamplesPerSec: LongInt;
    nAvgBytesPerSec: LongInt;
    RIFFSize: LongInt;
    fmtSize: LongInt;
    formatTag: Word;
    nBlockAlign: LongInt;
    DataSize: LongInt;
  end
;

  {============== Поток данных сэмпла ========================}

const

  MaxN = 300
{ максимальное значение величины сэмпла }
type

  SampleIndex = 0
..MaxN + 3;
type

  DataStream = array
[SampleIndex] of Real;

var

  N: SampleIndex;

  {============== Переменные сопровождения ======================}

type

  Observation = record


    Name
string[40]; {Имя данного сопровождения}
    yyy: DataStream; {Массив указателей на данные}

    WAV: WAVHeader; {Спецификация WAV для сопровождения}

    Last: SampleIndex; {Последний доступный индекс yyy}

    MinO, MaxO: Real; {Диапазон значений yyy}

  end
;

var

  K0R, K1R, K2R, K3R: Observation;

  K0B, K1B, K2B, K3B: Observation;

  {================== Переменные имени файла ===================}

var

  StandardDatabase: string
[80];

  BaseFileName: string
[80];
  StandardOutput: string
[80];
  StandardInput: string
[80];

  {=============== Объявления процедур ==================}

procedure
 ReadWAVFile(var Ki, Kj: Observation);
procedure
 WriteWAVFile(var Ki, Kj: Observation);
procedure
 ScaleData(var Kk: Observation);
procedure
 InitAllSignals;
procedure
 InitLinearSystem;

implementation

{$R *.DFM}

uses
 VarGraph, SysUtils;

{================== Стандартный формат WAV-файла ===================}

const

  MaxDataSize: LongInt = (MaxN + 1
) * 2 * 2;
const

  MaxRIFFSize: LongInt = (MaxN + 1
) * 2 * 2 + 36;
const

  StandardWAV: WAVHeader = (

    nChannels: Word(2
);
    nBitsPerSample: LongInt(16
);
    nSamplesPerSec: LongInt(8000
);
    nAvgBytesPerSec: LongInt(32000
);
    RIFFSize: LongInt((MaxN + 1
) * 2 * 2 + 36);
    fmtSize: LongInt(16
);
    formatTag: Word(1
);
    nBlockAlign: LongInt(4
);
    DataSize: LongInt((MaxN + 1
) * 2 * 2)
    );

  {================== Сканирование переменных сопровождения ===================}


procedure
 ScaleData(var Kk: Observation);
var

  I: SampleIndex;
begin


  {Инициализация переменных сканирования}

  Kk.MaxO := Kk.yyy[0
];
  Kk.MinO := Kk.yyy[0
];

  {Сканирование для получения максимального и минимального значения}

  for
 I := 1 to Kk.Last do
  begin

    if
 Kk.MaxO < Kk.yyy[I] then
      Kk.MaxO := Kk.yyy[I];
    if
 Kk.MinO > Kk.yyy[I] then
      Kk.MinO := Kk.yyy[I];
  end
;
end
{ ScaleData }

procedure
 ScaleAllData;
begin


  ScaleData(K0R);
  ScaleData(K0B);
  ScaleData(K1R);
  ScaleData(K1B);
  ScaleData(K2R);
  ScaleData(K2B);
  ScaleData(K3R);
  ScaleData(K3B);
end
{ScaleAllData}

{================== Считывание/запись WAV-данных ===================}


var

  InFile, OutFile: file
 of Byte;

type

  Tag = (F0, T1, M1);
type

  FudgeNum = record


    case
 X: Tag of
      F0: (chrs: array
[0..3of Byte);
      T1: (lint: LongInt);
      M1: (up, dn: Integer);
  end
;
var

  ChunkSize: FudgeNum;

procedure
 WriteChunkName(Namestring);
var

  i: Integer;

  MM: Byte;
begin


  for
 i := 1 to 4 do
  begin

    MM := ord(Name
[i]);
    write
(OutFile, MM);
  end
;
end
{WriteChunkName}

procedure
 WriteChunkSize(LL: Longint);
var

  I: integer;
begin


  ChunkSize.x := T1;
  ChunkSize.lint := LL;
  ChunkSize.x := F0;
  for
 I := 0 to 3 do
    Write
(OutFile, ChunkSize.chrs[I]);
end
;

procedure
 WriteChunkWord(WW: Word);
var

  I: integer;
begin


  ChunkSize.x := T1;
  ChunkSize.up := WW;
  ChunkSize.x := M1;
  for
 I := 0 to 1 do
    Write
(OutFile, ChunkSize.chrs[I]);
end
{WriteChunkWord}

procedure
 WriteOneDataBlock(var Ki, Kj: Observation);
var

  I: Integer;
begin


  ChunkSize.x := M1;
  with
 Ki.WAV do
  begin

    case
 nChannels of
      1
if nBitsPerSample = 16 then
        begin
 {1..2 Помещаем в буфер одноканальный 16-битный сэмпл}
          ChunkSize.up := trunc(Ki.yyy[N] + 0
.5);
          if
 N < MaxN then
            ChunkSize.dn := trunc(Ki.yyy[N + 1
] + 0.5);
          N := N + 2
;
        end

        else

        begin
 {1..4 Помещаем в буфер одноканальный 8-битный сэмпл}
          for
 I := 0 to 3 do
            ChunkSize.chrs[I]
              := trunc(Ki.yyy[N + I] + 0
.5);
          N := N + 4
;
        end
;
      2
if nBitsPerSample = 16 then
        begin
 {2 Двухканальный 16-битный сэмпл}
          ChunkSize.dn := trunc(Ki.yyy[N] + 0
.5);
          ChunkSize.up := trunc(Kj.yyy[N] + 0
.5);
          N := N + 1
;
        end

        else

        begin
 {4 Двухканальный 8-битный сэмпл}
          ChunkSize.chrs[1
] := trunc(Ki.yyy[N] + 0.5);
          ChunkSize.chrs[3
] := trunc(Ki.yyy[N + 1] + 0.5);
          ChunkSize.chrs[0
] := trunc(Kj.yyy[N] + 0.5);
          ChunkSize.chrs[2
] := trunc(Kj.yyy[N + 1] + 0.5);
          N := N + 2
;
        end
;
    end
{with WAV do begin..}
  end
{четырехбайтовая переменная "ChunkSize" теперь заполнена}

  ChunkSize.x := T1;
  WriteChunkSize(ChunkSize.lint); {помещаем 4 байта данных}

end
{WriteOneDataBlock}

procedure
 WriteWAVFile(var Ki, Kj: Observation);
var

  MM: Byte;

  I: Integer;
  OK: Boolean;
begin


  {Приготовления для записи файла данных}

  AssignFile(OutFile, StandardOutput); { Файл, выбранный в диалоговом окне }

  ReWrite(OutFile);
  with
 Ki.WAV do
  begin

    DataSize := nChannels * (nBitsPerSample div
 8) * (Ki.Last + 1);
    RIFFSize := DataSize + 36
;
    fmtSize := 16
;
  end
;

  {Записываем ChunkName "RIFF"}

  WriteChunkName('RIFF'
);

  {Записываем ChunkSize}

  WriteChunkSize(Ki.WAV.RIFFSize);

  {Записываем ChunkName "WAVE"}

  WriteChunkName('WAVE'
);

  {Записываем tag "fmt_"}

  WriteChunkName('fmt '
);

  {Записываем ChunkSize}

  Ki.WAV.fmtSize := 16
{должно быть 16-18}
  WriteChunkSize(Ki.WAV.fmtSize);

  {Записываем  formatTag, nChannels}

  WriteChunkWord(Ki.WAV.formatTag);
  WriteChunkWord(Ki.WAV.nChannels);

  {Записываем  nSamplesPerSec}

  WriteChunkSize(Ki.WAV.nSamplesPerSec);

  {Записываем  nAvgBytesPerSec}

  WriteChunkSize(Ki.WAV.nAvgBytesPerSec);

  {Записываем  nBlockAlign, nBitsPerSample}

  WriteChunkWord(Ki.WAV.nBlockAlign);
  WriteChunkWord(Ki.WAV.nBitsPerSample);

  {Записываем метку блока данных "data"}

  WriteChunkName('data'
);

  {Записываем DataSize}

  WriteChunkSize(Ki.WAV.DataSize);

  N := 0
{первая запись-позиция}
  while
 N <= Ki.Last do
    WriteOneDataBlock(Ki, Kj); {помещаем 4 байта и увеличиваем счетчик N}


  {Освобождаем буфер файла}

  CloseFile(OutFile);
end
{WriteWAVFile}

procedure
 InitSpecs;
begin

end
{ InitSpecs }

procedure
 InitSignals(var Kk: Observation);
var

  J: Integer;
begin


  for
 J := 0 to MaxN do
    Kk.yyy[J] := 0
.0;
  Kk.MinO := 0
.0;
  Kk.MaxO := 0
.0;
  Kk.Last := MaxN;
end
{InitSignals}

procedure
 InitAllSignals;
begin

  InitSignals(K0R);
  InitSignals(K0B);
  InitSignals(K1R);
  InitSignals(K1B);
  InitSignals(K2R);
  InitSignals(K2B);
  InitSignals(K3R);
  InitSignals(K3B);
end
{InitAllSignals}

var

  ChunkName: string
[4];

procedure
 ReadChunkName;
var

  I: integer;

  MM: Byte;
begin


  ChunkName[0
] := chr(4);
  for
 I := 1 to 4 do
  begin

    Read
(InFile, MM);
    ChunkName[I] := chr(MM);
  end
;
end
{ReadChunkName}

procedure
 ReadChunkSize;
var

  I: integer;

  MM: Byte;
begin


  ChunkSize.x := F0;
  ChunkSize.lint := 0
;
  for
 I := 0 to 3 do
  begin

    Read
(InFile, MM);
    ChunkSize.chrs[I] := MM;
  end
;
  ChunkSize.x := T1;
end
{ReadChunkSize}

procedure
 ReadOneDataBlock(var Ki, Kj: Observation);
var

  I: Integer;
begin


  if
 N <= MaxN then
  begin

    ReadChunkSize; {получаем 4 байта данных}

    ChunkSize.x := M1;
    with
 Ki.WAV do
      case
 nChannels of
        1
if nBitsPerSample = 16 then
          begin
 {1..2 Помещаем в буфер одноканальный 16-битный сэмпл}
            Ki.yyy[N] := 1
.0 * ChunkSize.up;
            if
 N < MaxN then
              Ki.yyy[N + 1
] := 1.0 * ChunkSize.dn;
            N := N + 2
;
          end

          else

          begin
 {1..4 Помещаем в буфер одноканальный 8-битный сэмпл}
            for
 I := 0 to 3 do
              Ki.yyy[N + I] := 1
.0 * ChunkSize.chrs[I];
            N := N + 4
;
          end
;
        2
if nBitsPerSample = 16 then
          begin
 {2 Двухканальный 16-битный сэмпл}
            Ki.yyy[N] := 1
.0 * ChunkSize.dn;
            Kj.yyy[N] := 1
.0 * ChunkSize.up;
            N := N + 1
;
          end

          else

          begin
 {4 Двухканальный 8-битный сэмпл}
            Ki.yyy[N] := 1
.0 * ChunkSize.chrs[1];
            Ki.yyy[N + 1
] := 1.0 * ChunkSize.chrs[3];
            Kj.yyy[N] := 1
.0 * ChunkSize.chrs[0];
            Kj.yyy[N + 1
] := 1.0 * ChunkSize.chrs[2];
            N := N + 2
;
          end
;
      end
;
    if
 N <= MaxN then
    begin
 {LastN    := N;}
      Ki.Last := N;
      if
 Ki.WAV.nChannels = 2 then
        Kj.Last := N;
    end

    else

    begin
 {LastN    := MaxN;}
      Ki.Last := MaxN;
      if
 Ki.WAV.nChannels = 2 then
        Kj.Last := MaxN;

    end
;
  end
;
end
{ReadOneDataBlock}

procedure
 ReadWAVFile(var Ki, Kj: Observation);
var

  MM: Byte;

  I: Integer;
  OK: Boolean;
  NoDataYet: Boolean;
  DataYet: Boolean;
  nDataBytes: LongInt;
begin


  if
 FileExists(StandardInput) then
    with
 Ki.WAV do
    begin
 { Вызов диалога открытия файла }
      OK := True; {если не изменится где-нибудь ниже}

      {Приготовления для чтения файла данных}

      AssignFile(InFile, StandardInput); { Файл, выбранный в диалоговом окне }

      Reset(InFile);

      {Считываем ChunkName "RIFF"}

      ReadChunkName;
      if
 ChunkName <> 'RIFF' then
        OK := False;

      {Считываем ChunkSize}

      ReadChunkSize;
      RIFFSize := ChunkSize.lint; {должно быть 18,678}


      {Считываем ChunkName "WAVE"}

      ReadChunkName;
      if
 ChunkName <> 'WAVE' then
        OK := False;

      {Считываем ChunkName "fmt_"}

      ReadChunkName;
      if
 ChunkName <> 'fmt ' then
        OK := False;

      {Считываем ChunkSize}

      ReadChunkSize;
      fmtSize := ChunkSize.lint; {должно быть 18}


      {Считываем  formatTag, nChannels}

      ReadChunkSize;
      ChunkSize.x := M1;
      formatTag := ChunkSize.up;
      nChannels := ChunkSize.dn;

      {Считываем  nSamplesPerSec}

      ReadChunkSize;
      nSamplesPerSec := ChunkSize.lint;

      {Считываем  nAvgBytesPerSec}

      ReadChunkSize;
      nAvgBytesPerSec := ChunkSize.lint;

      {Считываем  nBlockAlign}

      ChunkSize.x := F0;
      ChunkSize.lint := 0
;
      for
 I := 0 to 3 do
      begin

        Read
(InFile, MM);
        ChunkSize.chrs[I] := MM;
      end
;
      ChunkSize.x := M1;
      nBlockAlign := ChunkSize.up;

      {Считываем  nBitsPerSample}

      nBitsPerSample := ChunkSize.dn;
      for
 I := 17 to fmtSize do
        Read
(InFile, MM);

      NoDataYet := True;
      while
 NoDataYet do
      begin

        {Считываем метку блока данных "data"}

        ReadChunkName;

        {Считываем DataSize}

        ReadChunkSize;
        DataSize := ChunkSize.lint;

        if
 ChunkName <> 'data' then
        begin

          for
 I := 1 to DataSize do
            {пропуск данных, не относящихся к набору звуковых данных}

            Read
(InFile, MM);
        end

        else

          NoDataYet := False;
      end
;

      nDataBytes := DataSize;
      {Наконец, начинаем считывать данные для байтов nDataBytes}

      if
 nDataBytes > 0 then
        DataYet := True;
      N := 0
{чтение с первой позиции}
      while
 DataYet do
      begin

        ReadOneDataBlock(Ki, Kj); {получаем 4 байта}

        nDataBytes := nDataBytes - 4
;
        if
 nDataBytes <= 4 then
          DataYet := False;
      end
;

      ScaleData(Ki);
      if
 Ki.WAV.nChannels = 2 then
      begin

        Kj.WAV := Ki.WAV;
        ScaleData(Kj);
      end
;
      {Освобождаем буфер файла}

      CloseFile(InFile);
    end

  else

  begin

    InitSpecs; {файл не существует}

    InitSignals(Ki); {обнуляем массив "Ki"}

    InitSignals(Kj); {обнуляем массив "Kj"}

  end
;
end
{ ReadWAVFile }

{================= Операции с набором данных ====================}


const

  MaxNumberOfDataBaseItems = 360
;
type

  SignalDirectoryIndex = 0
..MaxNumberOfDataBaseItems;

var

  DataBaseFile: file
 of Observation;

  LastDataBaseItem: LongInt; {Номер текущего элемента набора данных}

  ItemNameS: array
[SignalDirectoryIndex] of string[40];

procedure
 GetDatabaseItem(Kk: Observation; N: LongInt);
begin


  if
 N <= LastDataBaseItem then
  begin

    Seek(DataBaseFile, N);
    Read
(DataBaseFile, Kk);
  end

  else

    InitSignals(Kk);
end
{GetDatabaseItem}

procedure
 PutDatabaseItem(Kk: Observation; N: LongInt);
begin


  if
 N < MaxNumberOfDataBaseItems then
    if
 N <= LastDataBaseItem then
    begin

      Seek(DataBaseFile, N);
      Write
(DataBaseFile, Kk);
      LastDataBaseItem := LastDataBaseItem + 1
;
    end

    else

      while
 LastDataBaseItem <= N do
      begin

        Seek(DataBaseFile, LastDataBaseItem);
        Write
(DataBaseFile, Kk);
        LastDataBaseItem := LastDataBaseItem + 1
;
      end

  else

    ReportError(1
); {Попытка чтения MaxNumberOfDataBaseItems}
end
{PutDatabaseItem}

procedure
 InitDataBase;
begin


  LastDataBaseItem := 0
;
  if
 FileExists(StandardDataBase) then
  begin

    Assign(DataBaseFile, StandardDataBase);
    Reset(DataBaseFile);
    while
 not EOF(DataBaseFile) do
    begin

      GetDataBaseItem(K0R, LastDataBaseItem);
      ItemNameS[LastDataBaseItem] := K0R.Name
;
      LastDataBaseItem := LastDataBaseItem + 1
;
    end
;
    if
 EOF(DataBaseFile) then
      if
 LastDataBaseItem > 0 then
        LastDataBaseItem := LastDataBaseItem - 1
;
  end
;
end
{InitDataBase}

function
 FindDataBaseName(Nstg: string): LongInt;
var

  ThisOne: LongInt;
begin


  ThisOne := 0
;
  FindDataBaseName := -1
;
  while
 ThisOne < LastDataBaseItem do
  begin

    if
 Nstg = ItemNameS[ThisOne] then
    begin

      FindDataBaseName := ThisOne;
      Exit;
    end
;
    ThisOne := ThisOne + 1
;
  end
;
end
{FindDataBaseName}

{======================= Инициализация модуля ========================}


procedure
 InitLinearSystem;
begin


  BaseFileName := '\PROGRA~1\SIGNAL~1\'
;
  StandardOutput := BaseFileName + 'K0.wav'
;
  StandardInput := BaseFileName + 'K0.wav'
;

  StandardDataBase := BaseFileName + 'Radar.sdb'
;

  InitAllSignals;
  InitDataBase;
  ReadWAVFile(K0R, K0B);
  ScaleAllData;
end