[Delphi] Wegschrijven wav file met pauzes na pauze "ruis"

Pagina: 1
Acties:
  • 157 views sinds 30-01-2008
  • Reageer

  • Jeroennl
  • Registratie: Januari 2004
  • Laatst online: 09:07
Ik lees eerst een WAV file en wil deze dan weer wegschrijven met pauzes erin. Nu gaat het goed als ik geen pauzes wegschrijf. De file wordt dan goed. Alleen bij pauzes komt er bij het stuk na de pauze een enorme ruis er overheen. Dit gebeurd tevens alleen bij de even stukken.

VB: Temp.wav

Delphi: code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
  while j < ARepeatCount  do
  begin
    while (i < ReadStream.Size) and (i < data) do
    begin
      ReadStream.Read(buf, 1);
      RecStream.Write(buf, 1);
      i := i + 1;
    end;

    i := 0;

    while l < pause do
    begin
      SoundValue := 0;
      RecStream.Write(SoundValue, 1);
      l := l + 1;
    end;

    l := 0;
    j := j + 1;
  end;


buf en SoundValue zijn beide van het type BYTE.
Pause geeft aan hoelang de pause moet zijn. En data is er alleen om een aantal seconden maar in te lezen en niet de hele file.

De uiteindelijke output is dus goed op het moment dat ik de regel RecStream.Write(SoundValue, 1); commentaar maak. Alleen ontbreek dan dus mijn pauze. Als hij wel gewoon wordt uitgevoerd krijg ik dus die ruis zoals in het voorbeeld.

  • Icelus
  • Registratie: Januari 2004
  • Niet online
Weet je zeker dat de waarde 0 ook het nulpunt is? Vaak wordt een 8-bit signed waarde gebruikt waardoor 0 de laagste waarde is en 128 het nulpunt.

Verander regel 13 'ns in:
Delphi:
1
SoundValue := 128;

Developer Accused Of Unreadable Code Refuses To Comment


  • Jeroennl
  • Registratie: Januari 2004
  • Laatst online: 09:07
Dit maakt helaas geen verschil de "ruis" blijft aanwezig.

  • Tomatoman
  • Registratie: November 2000
  • Laatst online: 01-12 21:29

Tomatoman

Fulltime prutser

Je gebruikt allerlei verschillende tellertjes, waardoor je weinig overzicht hebt. Waarom gebruik je niet gewoon een paar for lussen?

Delphi: code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
uses
  Math;

[...]

const
  SoundValue: Byte = 0;
var
  j, l: Integer;
begin
  for j := 0 to Min(ARepeatCount, Data) -1 do
  begin
    RecStream.CopyFrom(ReadStream, ReadStream.Size);
    for l := 0 to Pause -1 do
      RecStream.Write(SoundValue, 1);
  end;
end;
Maar dat is niet je werkelijke probleem. De implementatie van de Write method hangt af van het welke TStream descendant je gebruikt. Controleer eens of SoundValue wel echt een Byte moet zijn. Verwacht RecStream geen integer?

Een goede grap mag vrienden kosten.


  • Icelus
  • Registratie: Januari 2004
  • Niet online
Weet je zeker dat het om een 8-bit audio formaat gaat en niet om bijvoorbeeld een 16-bit formaat?

Edit:
Zie dat het bestand 16-bit stereo PCM is, weet niet of het om little of big-endian gaat.

Wat je snel zou kunnen proberen is regel 14 te vervangen door:
Delphi:
1
RecStream.Write( 0, 1 ); RecStream.Write( 128, 1);
óf
Delphi:
1
RecStream.Write( 128, 1 ); RecStream.Write( 0, 1);

[ Voor 68% gewijzigd door Icelus op 27-02-2007 16:13 ]

Developer Accused Of Unreadable Code Refuses To Comment


  • Jeroennl
  • Registratie: Januari 2004
  • Laatst online: 09:07
tomatoman schreef op dinsdag 27 februari 2007 @ 16:07:
Je gebruikt allerlei verschillende tellertjes, waardoor je weinig overzicht hebt. Waarom gebruik je niet gewoon een paar for lussen?

Delphi: code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
uses
  Math;

[...]

const
  SoundValue: Byte = 0;
var
  j, l: Integer;
begin
  for j := 0 to Min(ARepeatCount, Data) -1 do
  begin
    RecStream.CopyFrom(ReadStream, ReadStream.Size);
    for l := 0 to Pause -1 do
      RecStream.Write(SoundValue, 1);
  end;
end;
Maar dat is niet je werkelijke probleem. De implementatie van de Write method hangt af van het welke TStream descendant je gebruikt. Controleer eens of SoundValue wel echt een Byte moet zijn. Verwacht RecStream geen integer?
Nope, RecStream gebruikt geen integer. Anders zou ik natuurlijk bij het wegschrijven van de sound zelf ook al problemen hebben. Het treed alleen op bij het toevoegen van de pauze.
Icelus schreef op dinsdag 27 februari 2007 @ 16:08:
Weet je zeker dat het om een 8-bit audio formaat gaat en niet om bijvoorbeeld een 16-bit formaat?

Edit:
Zie dat het bestand 16-bit stereo PCM is, weet niet of het om little of big-endian gaat.

Wat je snel zou kunnen proberen is regel 14 te vervangen door:
Delphi:
1
RecStream.Write( 0, 1 ); RecStream.Write( 128, 1);
óf
Delphi:
1
RecStream.Write( 128, 1 ); RecStream.Write( 0, 1);
Dat maakt niet uit want het gaat alleen op die ene regel mis. Anders zou ik natuurlijk ook een probleem krijgen bij het wegschrijven van de sound zelf.

Als ik dat probeer zegt Delphi: Variable required.

[ Voor 99% gewijzigd door Jeroennl op 27-02-2007 16:17 ]


  • Jeroennl
  • Registratie: Januari 2004
  • Laatst online: 09:07
Edit: foutje dubbelpost.

[ Voor 97% gewijzigd door Jeroennl op 27-02-2007 16:13 ]


  • Tomatoman
  • Registratie: November 2000
  • Laatst online: 01-12 21:29

Tomatoman

Fulltime prutser

Als het 16-bit PCM is, zou je het eens met de waarde $8000 kunnen proberen. Dat is precies het midden voor een little-endian getal. Voor het geval het big-endian is, kun je $0080 proberen. In beide gevallen gaat een om een Word (=16 bits).

Een goede grap mag vrienden kosten.


  • Jeroennl
  • Registratie: Januari 2004
  • Laatst online: 09:07
Dat maakt ook geen verschil als ik het zo doe. Snap ook niet waar het aan ligt.

  • Tomatoman
  • Registratie: November 2000
  • Laatst online: 01-12 21:29

Tomatoman

Fulltime prutser

Je hebt wel het dollarteken ervoor gezet om aan te geven dat het een hexadecimale waarde is?

Een goede grap mag vrienden kosten.


  • Jeroennl
  • Registratie: Januari 2004
  • Laatst online: 09:07
Vanzelfsprekend.

  • Icelus
  • Registratie: Januari 2004
  • Niet online
Zet bij de variabelen de waarde:
Delphi:
1
const pause_val:word = $8000; // of $0080
Vervang vervolgens regel 14 door:
Delphi:
1
RecStream.Write( pause_val, 2 );

Developer Accused Of Unreadable Code Refuses To Comment


  • Tomatoman
  • Registratie: November 2000
  • Laatst online: 01-12 21:29

Tomatoman

Fulltime prutser

Dan maar eens in de specificatie van WAVE-files kijken. Ik zie dat het een little-endian bestandsformaat is (zoals je dat op de x86 gewend bent). Daar word je niet vrolijk van - het is allemaal niet bepaald straightforward. Een citaat uit de specificatie:
A sample point is a value representing a sample of a sound at a given moment in time. For waveforms with greater than 8-bit resolution, each sample point is stored as a linear, 2's-complement value which may be from 9 to 32 bits wide (as determined by the wBitsPerSample field in the Format Chunk, assuming PCM format -- an uncompressed format). For example, each sample point of a 16-bit waveform would be a 16-bit word (ie, two 8-bit bytes) where 32767 (0x7FFF) is the highest value and -32768 (0x8000) is the lowest value. For 8-bit (or less) waveforms, each sample point is a linear, unsigned byte where 255 is the highest value and 0 is the lowest value. Obviously, this signed/unsigned sample point discrepancy between 8-bit and larger resolution waveforms was one of those "oops" scenarios where some Microsoft employee decided to change the sign sometime after 8-bit wave files were common but 16-bit wave files hadn't yet appeared.
Volgens dit verhaal moet je toch echt $8000 gebruiken.

Een goede grap mag vrienden kosten.


  • Jeroennl
  • Registratie: Januari 2004
  • Laatst online: 09:07
Icelus schreef op dinsdag 27 februari 2007 @ 16:31:
Zet bij de variabelen de waarde:
Delphi:
1
const pause_val:word = $8000; // of $0080
Vervang vervolgens regel 14 door:
Delphi:
1
RecStream.Write( pause_val, 2 );
Dit deed het hem inderdaad :). Hartstikke bedankt voor de hulp ik kan weer vooruit.

Verwijderd

Misschien een fijne tip: :)
Gebruik nimmer een lowercase L als variabelnaam. In Courier 9pt o.a. is ie identiek aan het cijfer 1 en het gevaar dat daarin schuilt heb ik ooit eens aan den lijve ondervonden.

Dit stukje bijv:
code:
1
2
l := 0; 
j := j + 1;

zou met wat onachtzaam copy/paste werk zomaar van betekenis kunnen veranderen (het is zo al niet te zien of j met 1 of met L wordt verhoogd). Hoofdletter O idem dito. Het lijkt een kleinigheid maar een andere naam sluit een dergelijke bug uit en is dus per definitie te prefereren.
Je bent gewaarschuwd in elk geval :P

  • Icelus
  • Registratie: Januari 2004
  • Niet online
Houd er overigens rekening mee dat het een stereo WAV-bestand is. Als je bijvoorbeeld 500× een ‘stilte sample’ schrijft wordt dit over twee kanalen verdeeld. Effectief heb je dan maar 250 samples per kanaal geschreven.

Developer Accused Of Unreadable Code Refuses To Comment


  • Tomatoman
  • Registratie: November 2000
  • Laatst online: 01-12 21:29

Tomatoman

Fulltime prutser

En wil je een procedure schrijven die overweg kan met alle willekeurige wave-bestanden, dan zul je nog een stap verder moeten gaan. Uit de Format chunk zul je de sample rate, het aantal bits per sample en het aantal kanalen moeten lezen. Verder moet je rekening houden met de block alignment. Op basis van deze gegevens moet je besluiten hoeveel bytes en welke bytes je gaat invoegen. Niet al te moeilijk, maar wel foutgevoelig :)
The Format (fmt) chunk describes fundamental parameters of the waveform data such as sample rate, bit resolution, and how many channels of digital audio are stored in the WAVE.
C:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#define FormatID 'fmt '   /* chunkID for Format Chunk. NOTE: There */
                          /* is a space at the end of this ID. */

typedef struct {
  ID             chunkID;
  long           chunkSize;
  short          wFormatTag;
  unsigned short wChannels;
  unsigned long  dwSamplesPerSec;
  unsigned long  dwAvgBytesPerSec;
  unsigned short wBlockAlign;
  unsigned short wBitsPerSample;
/* Note: there may be additional fields here, depending upon */
/* wFormatTag. */

} FormatChunk;

Een goede grap mag vrienden kosten.


  • Jeroennl
  • Registratie: Januari 2004
  • Laatst online: 09:07
Klopt inderdaad maar dat ik had ik al gemaakt.
Verwijderd schreef op woensdag 28 februari 2007 @ 03:51:
Misschien een fijne tip: :)
Gebruik nimmer een lowercase L als variabelnaam. In Courier 9pt o.a. is ie identiek aan het cijfer 1 en het gevaar dat daarin schuilt heb ik ooit eens aan den lijve ondervonden.

Dit stukje bijv:
code:
1
2
l := 0; 
j := j + 1;

zou met wat onachtzaam copy/paste werk zomaar van betekenis kunnen veranderen (het is zo al niet te zien of j met 1 of met L wordt verhoogd). Hoofdletter O idem dito. Het lijkt een kleinigheid maar een andere naam sluit een dergelijke bug uit en is dus per definitie te prefereren.
Je bent gewaarschuwd in elk geval :P
Dat is een handige tip moeten we inderdaad niet hebben zoiets.

[ Voor 89% gewijzigd door Jeroennl op 28-02-2007 12:13 ]

Pagina: 1