Sources
Delphi Russian Knowledge Base
DRKB - это самая большая и удобная в использовании база знаний по Дельфи в рунете, составленная Виталием Невзоровым

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

01.01.2007

Интересно, есть ли технология преобразования 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..3] of Byte);
      T1: (lint: LongInt);
      M1: (up, dn: Integer);
  end;
var
  ChunkSize: FudgeNum;
 
procedure WriteChunkName(Name: string);
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; {InitLinearSystem}
 
begin {инициализируемый модулем код}
 
  InitLinearSystem;
end. {Unit LinearSystem}

Взято с https://delphiworld.narod.ru