generowanie dźwięku i jego nagrywanie

0

Witam chciałem wygenerować dźwięk a dokładniej mam tablice z pojedynczymi próbkami, które poddaje różnego typu algorytmom następnie chce je odtworzyć do tego celu używałem bardzo fajnej procedury, która znajduje się na tej stronie Pod adresem http://4programmers.net/faq.php?id=260 jej zaletom jest to ze bardzo szybko mogę przepisać daną tablice i ja odtworzyć, jednak wadą jest to ze chodzi na 8 bitach a chciałem to zrobić na 16 a kiedy zmienię wpis

wBitsPerSample := 8

na 16 to zmienia mi się próbkowanie na dwa razy większe niż jest podane w wpisie nSamplesPerSec :=


  czego efektem jest dwukrotnie mniejszy czas <ort>odważania </ort>sygnału i co za tym idzie odtwarzana częstotliwość jest dwa razy większa. 

No i teraz pytanie mam takie:
Dlaczego tak się dzieje mogę to niby łatwo skorygować przez wydłużenie dwukrotne tablicy, ale przypuszczam jednak ze cos jest nie tak w procedurze (czy ona nie pasuje pod 16bit?)??

Następna sprawa jest taka chciałbym w momencie, gdy karta muzyczna odtwarza sygnał od razu go nagrywać przez wejście karty szukam czegoś podobnego do tej procedury odtwarzania, czyli prostego i żeby moc od razu to szybciutko przepisać na tablice będę bardzo wdzięczny za podsuniecie jakiegoś kodu i jakąkolwiek pomoc.
0
Pieli napisał(a)

No i teraz pytanie mam takie:
Dlaczego tak się dzieje mogę to niby łatwo skorygować przez wydłużenie dwukrotne tablicy, ale przypuszczam jednak ze cos jest nie tak w procedurze (czy ona nie pasuje pod 16bit?)??

Jak zmieniasz wBitsPerSample musisz także zmienić nBlockAlign i nAvgBytesPerSec. Tu masz przykład w C/C++ ale myśle, że zrozumiesz o co chodzi ;)

wfx.nBlockAlign=wfx.nChannels*(wfx.wBitsPerSample/8);
wfx.nAvgBytesPerSec=wfx.nBlockAlign*wfx.nSamplesPerSec;

//EDIT

W tym artykule też to jest:

nBlockAlign := (nChannels * wBitsPerSample) div 8;
nAvgBytesPerSec := nSamplesPerSec * nBlockAlign;
0

No właśnie tez to jest i tez to wpisuje do siebie, czyli tak jak poniżej jest skonfigurowane powinno być niby poprawnie, jednak niestety nie jest, bo sygnał jest odtwarzany dwa razy szybciej niż powinien jeżeli zmienię na 16 bit a wiec jak niby to ma być.

 with WaveFormatEx do
  begin
    wFormatTag := WAVE_FORMAT_PCM;
    nChannels := 1;
    nSamplesPerSec := 44100;
    wBitsPerSample := 16;
    nBlockAlign := (nChannels * wBitsPerSample) div 8;
    nAvgBytesPerSec := nSamplesPerSec * nBlockAlign;
    cbSize := 0;
  end;
0

No jeżeli struktura WAVEFORMATEX jest poprawnie wypełniona to szukałbym przyczyny gdzie indziej. Czym wypełniasz buffory??? Generujesz dźwięk czy sample są z pliku audio???

0

Podaje program z problemem wystarczy wkleić cały tekst i wstawić na formę dwa Przyciski
Po przyciśnięciu pierwszego powinien niby zostać odtworzony sinus o częstotliwości 1000Hz i trwający 5 sekund A tak naprawdę przez ten błąd trwa 2,5 sekundy i odtwarzana częstotliwość to 2000Hz

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs,Mmsystem, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    procedure init;
    Procedure DoPamieci;
    Procedure GenerujDoTablicySinus;
    Procedure GenerujDoTablicySzum;
    Procedure TablicaDoPamieciPlay;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  WaveFormatEx: TWaveFormatEx;
  ZapisDoPamieci: TMemoryStream;

  i, TempInt, IleProbekWsygnale, RiffCount: integer;
  CzasSygnalu:longword;    //calkowity czas trwania sygnalu w ms
  Sygnal:array of word;    //tablica z wygenerowanym sygnalem
  Volume:integer = 50;     //Głosnosc
const
  RiffId: string = 'RIFF';
  WaveId: string = 'WAVE';
  FmtId: string = 'fmt ';
  DataId: string = 'data';

  implementation

{$R *.dfm}
Procedure tform1.GenerujDoTablicySinus;
begin
 setlength(sygnal,IleProbekWsygnale);
 for i:=0 to IleProbekWsygnale -1 do
  sygnal[i]:=32767 + trunc(Volume * sin(i * 2 * pi * 1000{Hz} / 48000{SamplePerSec}));
end;

Procedure tform1.GenerujDoTablicySzum;
begin
 setlength(sygnal,IleProbekWsygnale);
 randomize;
 for i:=0 to IleProbekWsygnale -1 do
  sygnal[i]:=32767 + trunc(Volume * random(10));
end;

///Procedurka inicjująca Waveformatex///
procedure tform1.init;
begin
  with WaveFormatEx do
  begin
    wFormatTag := WAVE_FORMAT_PCM;
    nChannels := 1;
    nSamplesPerSec := 48000;
    wBitsPerSample := 16;
    nBlockAlign := (nChannels * wBitsPerSample) div 8;
    nAvgBytesPerSec := nSamplesPerSec * nBlockAlign;
    cbSize := 0;
  end;
end;

Procedure tform1.DoPamieci;
begin

 ZapisDoPamieci := TMemoryStream.Create;

  with ZapisDoPamieci do
  begin
    IleProbekWsygnale := (CzasSygnalu * 48000) div 1000;
    RiffCount := Length(WaveId) + Length(FmtId) + SizeOf(DWORD) +
                 SizeOf(TWaveFormatEx) + Length(DataId) + SizeOf(DWORD) + IleProbekWsygnale; //

    Write(RiffId[1], 4); // 'RIFF'
    Write(RiffCount, SizeOf(DWORD));
    Write(WaveId[1], Length(WaveId)); // 'WAVE'
    Write(FmtId[1], Length(FmtId)); // 'fmt '
    TempInt := SizeOf(TWaveFormatEx);
    Write(TempInt, SizeOf(DWORD));
    Write(WaveFormatEx, SizeOf(TWaveFormatEx));
    Write(DataId[1], Length(DataId)); // 'data'
    Write(IleProbekWsygnale, SizeOf(DWORD));
  end;
end;

Procedure tform1.TablicaDoPamieciPlay;
begin
    ///Przepisanie tablicy do pamieci
  with ZapisDoPamieci do
   begin
    for i := 0 to IleProbekWsygnale - 1 do
      Write(sygnal[i], SizeOf(Byte));

    //////Odtworzenie sygnalu przez karte muzyczną/////
    sndPlaySound(ZapisDoPamieci.Memory, SND_MEMORY or SND_SYNC);
    ZapisDoPamieci.Free;
  end;
end;

////////////////////////////////////////////
////NO I OD TĄD DWA PRZYKLADY WYKOZYSTANIA
////////////////////////////////////////////
procedure TForm1.Button1Click(Sender: TObject);
begin
init;
CzasSygnalu:=5000{ms} ;  //czas sygnalu ustawiony na 5 sekund
DoPamieci;
GenerujDoTablicySinus;
TablicaDoPamieciPlay;
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
init;
CzasSygnalu:=5000{ms} ;  //czas sygnalu ustawiony na 5 sekund
DoPamieci;
GenerujDoTablicySzum;
TablicaDoPamieciPlay;
end;

end.
0

Tu masz błąd ;) Zakres 16 bitowych sampli to ? 32768(min) do 32767 (max). W tym artykule jest 127 (a powinno być 128 ;P) ale dlatego, że to jest mid-point wartości 8 bitowego sampla (inne kodowanie). Dla 16 bitów mid-pointem jest 0 - czyli typowe kodowanie U2 ;)

sygnal[i]:=32767 + trunc(Volume * sin(i * 2 * pi * 1000{Hz} / 48000{SamplePerSec}));

i to:

  write(FmtId[1], Length(FmtId)); // 'fmt '
  TempInt := SizeOf(TWaveFormatEx)-4; // bez WAVEFORMATEX::cbSize
  write(TempInt, SizeOf(DWORD));
  write(WaveFormatEx,TempInt);

PS. nie programuję w Delphi więc mogłem coś schrzanić ;)

0

A wiec, jeżeli chodzi o pierwszy błąd to poprawiłem to i nic to samo jednak pomyślałem ze według tego, co mówisz to przecież wcześniej powinienem mieć niezłe przesterowanie praktycznie obcięta jedna połówka sinusoidy a tego nie było i wtedy zajrzałem do procedury przepisywania tablicy do pamięci była tam następująca linijka w pętli

Write(sygnal[i], SizeOf(byte));

Zakres ort! 0..255 a przecież dla 16 bit musi być -32768..32765 A wiec zmieniłem na <ort>smallint</ort>32768..32765)
No i jeszcze zmienną od głośności ort! musiałem zwiększyć o kilkaset razy, bo nie było nic słychać
Po odpaleniu programu częstotliwość zrobiła się taka jak należy jednak ze czas pozostał bez zmian, czyli o połowę krótszy i dalej nie wiem gdzie go obcina.

Dla pewności podłączyłem oscyloskop pod wyjście karty, aby sprawdzić próbkowanie i generowałem nią sygnał z impulsami, czyli 1próbka=1, 2próbka =0, 3próbka =1, 4próbka =0,itd. No i na 100% jest takie jak skonfigurowałem, czyli 48000 bo na oscyloskopie w jednej mili sekundzie mieściło się 48 próbek

Także wniosek z tego taki ze odgrywa sygnał tylko do połowy a reszta gdzieś ginie

Jeżeli chodzi o drugą Twoją wskazówkę to po zastosowaniu na wyjściu karty robi się cisza, brak sygnału.

Tak w ogóle to dzięki za cierpliwość i pomoc, bo pewnie bez pierwszej podpowiedzi chyba miałbym problem z wychwyceniem tego błędu no i mam nadzieje ze pomożesz mi jeszcze sprawę doprowadzić do końca :-)

0

To też miałem wypisać ale....

...
RiffCount := Length(WaveId) + Length(FmtId) + SizeOf(DWORD) +
                 SizeOf(TWaveFormatEx) + Length(DataId) + SizeOf(DWORD) + IleProbekWsygnale; 
...
...
write(DataId[1], Length(DataId)); // 'data'
write(IleProbekWsygnale, SizeOf(DWORD));
...

IleProbekWsygnale musi być w bajtach (czyli x2) ale tylko w tych przypadkach.

0

Fajnie, działa teraz bez zarzutu dzięki wielkie:-)
A na temat nagrywania dźwięku możesz mi coś powiedzieć chciałbym to zrobić w podobnym stylu jak odtwarzanie tylko ze odwrotnie, czyli jak najszybciej zgrać nagrany sygnał bezpośrednio z pamięci na tablice. A następnie połączyć żeby uruchomiło się jednocześnie nagrywanie i odtwarzanie. Może masz jakiś ciekawy przykładzik takiego nagrywania.

0

Nie rozumiem za bardzo o co ci chodzi??? ;) Mówisz o nagrywaniu do pliku przy jednoczesnym odtwarzaniu z innego, czy o nagrywaniu sampli do pamięci (bufforów), przetwarzaniu ich i puszczeniu z powrotem na wyjście karty??? Tak czy siak zainteresuj się tymi funkcjami - dają większą kontrolę nad odtwarzaniem/nagrywaniem:


waveInAddBuffer
waveInClose
waveInOpen
waveInStart
waveInStop
waveInUnprepareHeader
waveInPrepareHeader

waveOutClose
waveOutOpen
waveOutPrepareHeader
waveOutUnprepareHeader
waveOutWrite

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