Jak zapisać WAV z danych PCM

0

Ktoś może podpowiedzieć jak zapisać najprostszy WAV monofoniczny w którym byłby jakiś prosty dźwięk np. 440Hz

Znalazłem opis jak zapisać prosty plik WAV
http://www.topherlee.com/software/pcm-tut-wavformat.html

The header of a WAV (RIFF) file is 44 bytes long and has the following format:
Positions Sample Value Description
1 - 4 "RIFF" Marks the file as a riff file. Characters are each 1 byte long.
5 - 8 File size (integer) Size of the overall file - 8 bytes, in bytes (32-bit integer). Typically, you'd fill this in after creation.
9 -12 "WAVE" File Type Header. For our purposes, it always equals "WAVE".
13-16 "fmt " Format chunk marker. Includes trailing null
17-20 16 Length of format data as listed above
21-22 1 Type of format (1 is PCM) - 2 byte integer
23-24 2 Number of Channels - 2 byte integer
25-28 44100 Sample Rate - 32 byte integer. Common values are 44100 (CD), 48000 (DAT). Sample Rate = Number of Samples per second, or Hertz.
29-32 176400 (Sample Rate * BitsPerSample * Channels) / 8.
33-34 4 (BitsPerSample * Channels) / 8.1 - 8 bit mono2 - 8 bit stereo/16 bit mono4 - 16 bit stereo
35-36 16 Bits per sample
37-40 "data" "data" chunk header. Marks the beginning of the data section.
41-44 File size (data) Size of the data section.
Sample values are given above for a 16-bit stereo source.

I moj kawałę kodu który powinien zapisac plik WAV tylko niczym nie mozna go otworzyć :(


procedure TForm68.Button1Click(Sender: TObject);
const
  FILE_SIZE = 1024*64;
var
  s: ansistring;
  buf: TMemoryStream;
  i: Integer;
  w: Word;
  si:SmallInt;
begin
  buf:= TMemoryStream.Create;

  s := 'RIFF';
  buf.Write(s[1],4);

  i := FILE_SIZE-8;
  buf.Write(i,4);

  s  := 'WAVE';
  buf.Write(s[1],4);

  s := 'fmt'#0;
  buf.Write(s[1],4);

  i := 16;
  buf.Write(i,4);

  w := 1;

  buf.Write(w,2);

  w := 2;
  buf.Write(w,2);

  i := 44100;
  buf.Write(i,4);

  i := 176400;
  buf.Write(i,4);

  w := 4;
  buf.Write(w,2);

  w := 16;
  buf.Write(w,2);

  s := 'data';
  buf.Write(s[1],4);

  i := FILE_SIZE-44;
  buf.Write(i,4);

  while buf.Size <= FILE_SIZE do
  begin
    si := Random(32000)-16000; //random data !!!
    buf.Write(si,2);
  end;


  buf.SaveToFile('1.wav');
  buf.Free;
 end;

1

Moja wina !
13-16 "fmt " to tam jednak jest "SPACJA" a nie NULL i wtedy działa OK

0

Ogarniete w prosta klase

unit uWAV_raw;

interface

uses Classes, Windows;

{
http://mathmatrix.narod.ru/Wavefmt.html

 Wave file format specification
It seems very simple to write WAV files by yourself, but when it comes to be implemented... So, here is Microsoft Wave file format header:
Field name 	                |Size and format 	|Meaning
-------------------------------------------------------
File description header 	   4 bytes (DWord) 	The ASCII text string "RIFF" The procedure converiting 4-symbol string into dword is included in unit Wave
Size of file 	               4 bytes (DWord) 	The file size not including the "RIFF" description (4 bytes) and file description (4 bytes). This is file size - 8.
WAV description header 	     4 bytes (DWord) 	The ASCII text string "WAVE"
Format description header 	 4 bytes (DWord) 	The ASCII text string "fmt "(The space is also included)
Size of WAVE section chunck  4 bytes (DWord) 	The size of the WAVE type format (2 bytes) + mono/stereo flag (2 bytes) + sample rate (4 bytes) + bytes per sec (4 bytes) + block alignment (2 bytes) + bits per sample (2 bytes). This is usually 16.
WAVE type format 	           2 bytes (Word) 	Type of WAVE format. This is a PCM header = $01 (linear quntization). Other values indicates some forms of compression.
Number of channels 	         2 bytes (Word) 	mono ($01) or stereo ($02). You may try more channels...
Samples per second 	         4 bytes (DWord) 	The frequency of quantization (usually 44100 Hz, 22050 Hz, ...)
Bytes per second 	           4 bytes (DWord) 	Speed of data stream = Number_of_channels*Samples_per_second*Bits_per_Sample/8
Block alignment 	           2 bytes (Word) 	Number of bytes in elementary quantization = Number_of_channels*Bits_per_Sample/8
Bits per sample 	           2 bytes (Word) 	Digits of quantization (usually 32, 24, 16, 8). I wonder if this is, for example, 5 ..?
Data description header 	   4 bytes (DWord) 	The ASCII text string "data".
Size of data 	               4 bytes (DWord) 	Number of bytes of data is included in the data section.
Data 	-?- 	Wave stream data itself. Be careful with format of stereo (see below)

}

type
  TWav_Header = packed record
    str_RIFF: array [0..3] of Byte;
    file_size: Dword;
    str_WAVE: array [0..3] of Byte;
    str_fmt: array [0..3] of Byte;
    size_wave_section: DWord;
    wave_type: word;
    chanells: word;
    samples_per_second: dword;
    bytes_per_second: dword;
    block_alligned: word;
    bits_per_sample: word;
    str_data: array [0..3] of Byte;
    size_of_data: DWORD;
  end;

  TWAV_raw = class
  private
    header: TWav_Header;
    procedure UpdateHeader;
  public
    buf: TmemorySTream;
    constructor create;
    destructor Destroy; override;
    procedure SaveToFile(p_name: string);
  end;

implementation

{ TWAV_raw }

constructor TWAV_raw.create;
begin
  buf:=TmemorySTream.Create;
end;

destructor TWAV_raw.Destroy;
begin
  buf.Free;

  inherited;
end;

procedure TWAV_raw.SaveToFile(p_name: string);
var
  f:TMemoryStream;
begin
  UpdateHeader;

  f:=TMemoryStream.Create;
  f.Write(header,SizeOf(header));
  f.Write(pbyte(buf.Memory)^, buf.Size );
  f.SaveToFile(p_name);
  f.Free;
end;

procedure TWAV_raw.UpdateHeader;
var
  s: AnsiString;
  file_size: integer;
begin
  file_size := buf.Size + 44;

  s := 'RIFF';
  Move(s[1],header.str_RIFF[0],4);

  header.file_size :=  file_size - 8;

  s := 'WAVE';
  Move(s[1],header.str_WAVE[0],4);

  s := 'fmt ';
  Move(s[1],header.str_fmt[0],4);

  header.size_wave_section := 16;

  header.wave_type := 1;

  header.chanells := 1;

  header.samples_per_second := 1000;

  header.bits_per_sample := 16;

  header.bytes_per_second :=  header.chanells * header.samples_per_second * header.bits_per_sample div 8;

  header.block_alligned :=  header.chanells * header.bits_per_sample div 8;

  s := 'data';
  Move(s[1],header.str_data[0],4);

  header.size_of_data := buf.Size;

end;



end.
1
  1. buf: TmemorySTream; - ktoś Ci zrobi WAV_raw.buf := nil i cały misterny plan w pizdu... Jak już to powinna to myć właściwość read only
  2. f:TMemoryStream; czemu nie zapisujesz od razu do filestream?
0

IMO ta struktura jest zbędna, skoro i tak opakowujesz to w klasę.

Wszystkie dane zadeklaruj jako pola w klasie, ustal do nich dostęp (gettery i settery), w konstruktorze klasy inicjalizuj wartości tych pól. Dodaj sensowne i wygodne w obsłudze właściwości, pozwalające modyfikować konkretne fragmenty nagłówka. To tak na wstępie.

Przy czym nie używaj notacji z C do nazywania pól – przyjęło się, że snake_case używa się do nazywania stałych.


Edit: natomiast jeśli chodzi o zawartość metody UpdateHeader, to zamiast hardkodowanych wartości dla pól nagłówka, możesz sobie zadeklarować stałą rekordową z domyślnymi wartościami i ją po prostu przypisać do zmiennej. Wyjdzie ładniej, czytelniej.

Żeby łatwiej było to zrobić, zadeklaruj pola str_RIFF, str_WAVE, str_fmt i str_data jako macierze jednobajtowych znaków. Wtedy będziesz mógł w deklaracji stałej skorzystać bezpośrednio z literałów łańcuchowych.

Edit2: masz literówkę w nazwie pola chanells – ma być channels.

1 użytkowników online, w tym zalogowanych: 0, gości: 1