Author Topic: How to design the Waveform (and be the King)  (Read 5216 times)

fredvs

  • Posts: 327
Who will be the king who explain me how to design the waveform (like in Goldwave, Cakewalk, etc) just before the stream is playing.

I just know that i have to use Stream_Decode and Getdata,
to find the volume at some positions...

Then it is easy, put this values in array and zis done.

OK for position (with setposition),... But for getdata ?????
« Last Edit: 4 Mar '03 - 20:11 by fredvs »

bigjim

  • Posts: 232
Re: How to design the Waveform (and be the King)
« Reply #1 on: 4 Mar '03 - 21:27 »
Hi, I've posted an example in delphi below.
My code decodes a stream to an array zoomed to a certain amount but you get the look your after by grabbing the largest and smallest sample from each block of samples you get and storing it in the array. You can then plot the array at play-time.
The codes not commented but is fairly self explanitory. The vrThreads are a component which can be emulated by using a timer or a loop set to release to the system every so often.

Code: [Select]

unit Unit1;

interface

uses
 Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, Bass,
 StdCtrls, ExtCtrls, ComCtrls, VrControls, VrThreads, VrTrackBar,
 VrDisplay;

type
 TForm1 = class(TForm)
   Button1: TButton;
   VrThread1: TVrThread;
   Image13: TPaintBox;
   VrThread2: TVrThread;
   ProgressBar1: TProgressBar;
   Timer1: TTimer;
   TrackBar1: TVrTrackBar;
   Button2: TButton;
   Button3: TButton;
   ScrollBar1: TScrollBar;
   OpenDialog1: TOpenDialog;
   procedure FormCreate(Sender: TObject);
   procedure Button1Click(Sender: TObject);
   procedure VrThread1Execute(Sender: TObject);
   procedure VrThread2Execute(Sender: TObject);
   procedure TrackBar1MouseDown(Sender: TObject; Button: TMouseButton;
     Shift: TShiftState; X, Y: Integer);
   procedure TrackBar1MouseUp(Sender: TObject; Button: TMouseButton;
     Shift: TShiftState; X, Y: Integer);
   procedure Timer1Timer(Sender: TObject);
   procedure Button2Click(Sender: TObject);
   procedure Button3Click(Sender: TObject);
   procedure ScrollBar1Change(Sender: TObject);
   procedure FormDestroy(Sender: TObject);
 private
   { Private declarations }
 public
   { Public declarations }
 end;

var
 Form1: TForm1;
 mp3h : DWORD;
 mp3h2 : DWORD;
 decodeb : array[0..4410] of smallint;
 wavpos : integer;
 zoomwave_max : array of smallint;
 zoomwave_min : array of smallint;
 zoomtime : array of single;
 countb : integer;
 imagpos : integer;
 image1 : TBitmap;
 moving : boolean;
 zoomvert : integer;
 totalmax, totalmin : smallint;

implementation

{$R *.DFM}

procedure TForm1.FormCreate(Sender: TObject);
var
maxlength : integer;
i : integer;
begin
if opendialog1.execute = true then
begin
 moving := false;
 Bass_Init(-1 , 44100, 0, form1.handle);
 Bass_Start;
 mp3h := Bass_StreamCreateFile(FALSE, pChar(opendialog1.filename), 0, 0, BASS_STREAM_DECODE OR BASS_MP3_SETPOS);
 mp3h2 := Bass_StreamCreateFile(FALSE, pChar(opendialog1.filename), 0, 0, 0);
 image1 := TBitmap.Create;
 image1.width := image13.width;
 image1.height := image13.height;
 totalmax := 0;
 totalmin := 0;
 vrThread1.Enabled := true;
 maxlength := trunc(bass_streamgetlength(mp3h) / 4410);
 setlength(zoomwave_max, maxlength+1);
 setlength(zoomwave_min, maxlength+1);
 setlength(zoomtime, maxlength+1);
end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
widtha : integer;
begin
wavpos := 1;
bass_streamplay(mp3h2, FALSE, 0);
timer1.enabled := true;
vrThread2.Enabled := true;
end;

procedure TForm1.VrThread1Execute(Sender: TObject);
var
i,j : integer;
maxval, minval : smallint;
begin
if Bass_ChannelGetPosition(mp3h) < Bass_StreamGetLength(mp3h) then
begin
 maxval := 0;
 minval := 0;
 ProgressBar1.Position := trunc((Bass_ChannelGetPosition(mp3h) / Bass_StreamGetLength(mp3h))*100);
 Bass_ChannelGetData(mp3h, @decodeb, 4410);
 for i := 0 to 2205 do
 begin
   if decodeb[i*2] > maxval then maxval := decodeb[i*2];
   if decodeb[i*2] < minval then minval := decodeb[i*2];
 end;
if totalmax < maxval then totalmax := maxval;
if totalmin > minval then totalmin := minval;
/////////////////
/////////////////
 zoomtime[wavpos] := Bass_ChannelBytes2Seconds(mp3h, Bass_ChannelGetPosition(mp3h));
 zoomwave_max[wavpos] := maxval;
 zoomwave_min[wavpos] := minval;
/////////////////
 wavpos := wavpos + 1;
end;
if Bass_ChannelGetPosition(mp3h) = Bass_StreamGetLength(mp3h) then
begin
 zoomvert := (totalmax + (-totalmin) div 2) div ((image13.height div 3) * 2);//div 80
 progressbar1.Visible := false;
 trackbar1.MaxValue := Trunc(Bass_ChannelBytes2Seconds(mp3h, Bass_StreamGetLength(mp3h)));
 trackbar1.position := 0;
 VrThread1.Enabled := false;
end;
end;

procedure TForm1.VrThread2Execute(Sender: TObject);
var
widtha : integer;
begin
if (Bass_ChannelBytes2Seconds(mp3h2, Bass_ChannelGetPosition(mp3h2)) >= zoomtime[countb]) and (Bass_ChannelBytes2Seconds(mp3h2, Bass_ChannelGetPosition(mp3h2)) < zoomtime[countb+1]) then
begin
 countb := countb + 1;
 Image1.Canvas.Brush.Color := $007C9885;
 Image1.Canvas.Rectangle(0, 0, Image1.Width, Image1.Height);
 Image1.Canvas.Pen.Color := clBlack;
 for widtha := 1 to image1.width do
 begin
   image1.Canvas.MoveTo(widtha, image1.height div 2);
   image1.Canvas.LineTo(widtha, (image1.height div 2) - (zoomwave_max[(widtha + countb)-image1.width div 2] div (zoomvert))); //100-zoomvert
   image1.Canvas.MoveTo(widtha, image1.height div 2);
   image1.Canvas.LineTo(widtha, (image1.height div 2) + (-zoomwave_min[(widtha + countb)-image1.width div 2] div (zoomvert)));
   imagpos := imagpos + 1;
 end;
 image1.Canvas.MoveTo(0, image1.height div 2);
 image1.Canvas.LineTo(image1.width-1, image1.height div 2);
 Image1.Canvas.Pen.Color := clWhite;
 image1.Canvas.MoveTo(image1.width div 2, 0);
 image1.Canvas.LineTo(image1.width div 2, image1.height);
 Image1.Canvas.Pen.Color := clBlack;
 BitBlt(image13.canvas.Handle, 0, 0, image13.width, image13.height, image1.Canvas.Handle, 0, 0, srccopy);
end;
end;

procedure TForm1.TrackBar1MouseDown(Sender: TObject; Button: TMouseButton;
 Shift: TShiftState; X, Y: Integer);
begin
moving := true;
end;

procedure TForm1.TrackBar1MouseUp(Sender: TObject; Button: TMouseButton;
 Shift: TShiftState; X, Y: Integer);
var
oldpos : DWORD;
newpos : single;
begin
oldpos := Bass_ChannelGetPosition(mp3h2);
Bass_ChannelSetPosition(mp3h2, Bass_ChannelSeconds2Bytes(mp3h2, TrackBar1.position));
newpos := Bass_ChannelBytes2Seconds(mp3h2, Bass_ChannelGetPosition(mp3h2));
if Bass_ChannelGetPosition(mp3h2) > oldpos then
begin
 repeat
    countb := countb + 1;
 until zoomtime[countb] >= newpos;
end;
if Bass_ChannelGetPosition(mp3h2) < oldpos then
begin
 repeat
    countb := countb - 1;
 until zoomtime[countb] <= newpos;
end;
moving := false;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
if moving = false then TrackBar1.Position := Trunc(Bass_ChannelBytes2Seconds(mp3h2, Bass_ChannelGetPosition(mp3h2)));
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
Bass_ChannelPause(mp3h2);
end;

procedure TForm1.Button3Click(Sender: TObject);
begin
Bass_ChannelResume(mp3h2);
end;

procedure TForm1.ScrollBar1Change(Sender: TObject);
begin
Bass_ChannelSetAttributes(mp3h2, 88201-Scrollbar1.position, -1, -101);
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
vrthread2.Enabled := false;
zoomtime := nil;
zoomwave_max := nil;
zoomwave_min := nil;
image1.Destroy;
Bass_Free;
end;

end.

fredvs

  • Posts: 327
Re: How to design the Waveform (and be the King)
« Reply #2 on: 4 Mar '03 - 21:37 »
Sorry Jim, but you are already the Emperor (thanks to your beat-counter).
Then now you become the King of the Emperors. Congratulations... :-* :-*


Thank youuuuuuuuuuu Jim  :-*, you are the best (like bass).  


Ps : if i have well understand :
1 position is 4410 bytes, to know the volume, you search for the max and min value of each 2205 * i bytes (who represent a % volume of some left freq) and you get the general volume at that position  :idea:...

« Last Edit: 4 Mar '03 - 23:08 by fredvs »

fredvs

  • Posts: 327
Re: How to design the Waveform (and be the King)
« Reply #3 on: 5 Mar '03 - 04:06 »
Jim, it works like a charm.

I have 2 waves-forms quick (1 for the stream playing, 1 for the cued), its perfect. (will see zoom and change position of/with trackbar later...)


With your :
Bass_StreamCreateFile(FALSE, f, 0, 0, BASS_STREAM_DECODE OR BASS_MP3_SETPOS)...;

Plus your :
BASS_ChannelSetPosition... ;

And your superb :
Bass_ChannelGetData(strform, @decodeb, 4410);
for i := 0 to 2205 do
begin
  if decodeb[i*2] > maxval then maxval := decodeb[i*2];
  if decodeb[i*2] < minval then minval := decodeb[i*2];
end;...

I have all what i wanted, my prog works like bass, perfectly,

Humm, the loop works better with a timer on 1 than with a do while... :-/

Thanks a lot Jim  :-*
« Last Edit: 5 Mar '03 - 04:26 by fredvs »

Ingolf2

  • Guest
Re: How to design the Waveform (and be the King)
« Reply #4 on: 5 Mar '03 - 08:54 »
I have done the same, only store it in an array of signed bytes. That way you can zoom in and out on all the data that you have. Don't know how the above code in Delphi does this, didn't read it, but I saw some constant values, zo I guess there is no dynamic zooming there. Maybe someone is interested:

Code: [Select]

var
 FScaleVal: Integer = 4096;


FScaleVal specifies the samples to skip...

Code: [Select]
procedure TMixerForm.BuildWavePeaks(var Channel: TWaveChannel);
var
 J, C, Read, ToRead: Integer;
 R: Extended;
begin
 with FWaveData, Channel do begin
   R := High(ShortInt) / BitValues[BitDepth];
   Data.Seek(0, soBeginning);
   ToRead := ReadBufSize*ChSize;
   SetLength(Peaks, Samples);
   C := 0;
   repeat
     Read := Data.Read(DataBuf, ToRead) div ChSize;
     for J := 0 to Read-1 do begin
       Peaks[C] := Round(DataBuf[J] * R);
       Inc(C);
     end;
   until (Read <> ReadBufSize) or (C = Samples);
 end;
end;


... to build my peaks array, and then this to view it zoomed...

Code: [Select]
procedure TMixerForm.UpdateWave;
var
 I, P: Integer;
 R: Single;

 procedure DrawPeaks(var Channel: TWaveChannel; cY: Integer);
 var
   aP, aN: ShortInt;
   I, J: Integer;
   C: Single;
 begin
   with Channel, FWaveBmp.Canvas do begin
     if Length(Peaks) > 0 then begin
       C := 0;
       MoveTo(0,cY);
       for I := 0 to Round(Length(Peaks) / FScaleVal) do begin
         if I < FWaveBmp.Width then begin
           aP := 0;
           aN := 0;
           for J := Round(C) to Round(C+FScaleVal)-1 do begin
             if J < Length(Peaks) then begin
               if Peaks[J] < 0 then begin
                 if Peaks[J] < aN then aN := Peaks[J];
               end
               else begin
                 if Peaks[J] > aP then aP := Peaks[J];
               end;
             end;
           end;
           if (aP = 0) and (aN = 0) then LineTo(I,cY)
           else begin
             if aP <> 0 then LineTo(I,Round(cY-(aP*R)));
             if aN <> 0 then LineTo(I,Round(cY-(aN*R)));
           end;
           C := C + FScaleVal;
         end;
       end;
     end;
   end;
 end;

begin
 with FWaveBmp.Canvas, FWaveData do begin
   if TryLock then begin
     try
       Brush.Color := BackColor;
       FillRect(ClipRect);
       if Length(Channels) > 0 then P := FWaveBmp.Height div Length(Channels)
       else P := FWaveBmp.Height;
       R := ((P / 2)-1) / High(ShortInt);
       for I := 0 to High(Channels) do begin
         if I > 0 then begin
           Pen.Color := ChannelSplitColor;
           MoveTo(0,P*I);
           LineTo(FWaveBmp.Width,P*I);
         end;
         Pen.Color := ChannelCenterColor;
         MoveTo(0,(P*I)+(P div 2));
         LineTo(FWaveBmp.Width,(P*I)+(P div 2));
         Pen.Color := ChannelPartColor;
         MoveTo(0,(P*I)+(P div 4));
         LineTo(FWaveBmp.Width,(P*I)+(P div 4));
         Pen.Color := ChannelPartColor;
         MoveTo(0,(P*(I+1))-(P div 4));
         LineTo(FWaveBmp.Width,(P*(I+1))-(P div 4));
         Pen.Color := WaveColor;
         DrawPeaks(Channels[I], (P*I)+(P div 2));
       end;
     finally
       Unlock;
     end;
   end;
 end;
end;

fredvs

  • Posts: 327
Re: How to design the Waveform (and be the King)
« Reply #5 on: 6 Mar '03 - 05:27 »
Hello Folks.

With the codes of Jim, a quick way to design...

I use a canvas of 1000 * 200,
/////////////////////////////////////////////////////
Pulic var
strima : HSTREAM;
lenimage , x, maxval : longint ;
volume : array[1..1000] of integer; // for a canvas of length 1000 pixels (x)
decodeb : array[0..4410] of smallint;
//////////////////////////////////////////////////////
procedure Tmixk.Button1Click(Sender: TObject); // the first  procedure..
begin
strima :=  Bass_StreamCreateFile(FALSE, 'test.mp3', 0, 0, BASS_STREAM_DECODE OR BASS_MP3_SETPOS);
lenimage := round(BASS_StreamGetLength(strima)) ;
form1.PaintBox1.canvas.pen.width := 1 ;
x := 0; //or else for zoom
timer1.Enabled := true ; //start the loop
end;
end;
////////////////////////////////////////////////////////////
procedure Tmixk.Timer1Timer(Sender: TObject); // Timer1 on 1 msec, i can do it with do while, but with my amd 1800 it is not so quick... ???  
var
i : integer ;
begin
Timer1.Enabled := false ;
if x > 1000  then
     begin
      BASS_ChannelStop(strima);
       BASS_StreamFree(strima);
 end
 else
begin
BASS_ChannelSetPosition(strima, round((x *  lenimage ) /1000)) ;  // /if you zoom change value of  lenimage
Bass_ChannelGetData(strima, @decodeb, 4410);
for i := 0 to 2205 do
if decodeb[i*2] > maxval then maxval := decodeb[i*2];
maxval := maxval div 300 ;/// i use a canvas of 1000 * 200, then maxval := maxval div 300 (it looks good)...
form1.PaintBox1.Canvas.PolyLine([Point(x,(maxval)div 2), Point(x,(maxval + (maxval)div 2))]); /// design the wave
volume[x] :=  maxval ; // array for refresh or zoom later
application.ProcessMessages; // Yield to system briefly
x1 := x + 1 ;
Timer1.Enabled := true ;
end;
end;
/////////////////////////////////////////////////////
procedure TForm1..Button2Click(Sender: TObject); // for refresh or zoom...
var
i : integer ;
begin
for i := 0 to 1000 do
form1.PaintBox1.Canvas.PolyLine([Point(i,(volume)div 2), Point(i,(volume + (volume)div 2))]);
end;



« Last Edit: 6 Mar '03 - 06:23 by fredvs »

Richard_b

  • Posts: 28
Re: How to design the Waveform (and be the King)
« Reply #6 on: 8 Mar '03 - 17:59 »
I do VB..does anyone have a VB example..not Delphi?