Page 2 of 5

Re: Draw a waveform with Purebasic and BASS library

Posted: Wed Jun 13, 2018 12:08 am
by RASHAD
Hi Martin
The data are the curve peaks
Search the forum for how to draw Spline between points
Good luck

Re: Draw a waveform with Purebasic and BASS library

Posted: Wed Jun 13, 2018 6:58 am
by wilbert
Here's a very simple example just to show how you can draw the wave from sample data.
It does no checks at all at the moment so it assumes the audio is 16 bit stereo, it assumes there's enough memory to load the entire song and it assumes the song is long enough to fill the entire width of the CanvasGadget.
The idea is simply to get the min and max values of a number of samples and use those values to connect the dots.

Code: Select all

Structure Sample
  l.w
  r.w
EndStructure

BASS_Init(-1, 44100, 0, 0, #Null)

Channel.l = BASS_StreamCreateFile(#False, @"Test.mp3", 0, 0, #BASS_STREAM_PRESCAN|#BASS_STREAM_DECODE|#BASS_UNICODE)
Length.q = BASS_ChannelGetLength(Channel, #BASS_POS_BYTE)


SamplesPerPoint = 8

Dim Buffer.Sample(Length >> 2)
BASS_ChannelGetData(Channel, @Buffer(), Length)

OpenWindow(0, 0, 0, 620, 320, "Waveform", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
CanvasGadget(0, 10, 10, 600, 300)

StartDrawing(CanvasOutput(0))
Box(0, 0, 600, 300, $e0e0e0)
PrevMinL.w = 0 : PrevMaxL.w = 0
PrevMinR.w = 0 : PrevMaxR.w = 0
For i = 0 To 599
  MinL.w = Buffer(i*SamplesPerPoint)\l : MaxL.w = MinL
  MinR.w = Buffer(i*SamplesPerPoint)\r : MaxR.w = MinR
  j = 1
  While j < SamplesPerPoint
    If Buffer(i*SamplesPerPoint + j)\l < MinL : MinL = Buffer(i*SamplesPerPoint + j)\l : EndIf
    If Buffer(i*SamplesPerPoint + j)\l > MaxL : MaxL = Buffer(i*SamplesPerPoint + j)\l : EndIf
    If Buffer(i*SamplesPerPoint + j)\r < MinR : MinR = Buffer(i*SamplesPerPoint + j)\r : EndIf
    If Buffer(i*SamplesPerPoint + j)\r > MaxR : MaxR = Buffer(i*SamplesPerPoint + j)\r : EndIf
    j + 1
  Wend
  If PrevMinL > MaxL : LineXY(i, 75 - PrevMinL>>9, i, 75 - MaxL>>9, $ff8000) : EndIf 
  If PrevMaxL < MinL : LineXY(i, 75 - PrevMaxL>>9, i, 75 - MinL>>9, $ff8000) : EndIf 
  If PrevMinR > MaxR : LineXY(i, 225 - PrevMinR>>9, i, 225 - MaxR>>9, $ff8000) : EndIf 
  If PrevMaxR < MinR : LineXY(i, 225 - PrevMaxR>>9, i, 225 - MinR>>9, $ff8000) : EndIf 
  LineXY(i, 75 - MinL>>9, i, 75 - MaxL>>9, $ff8000)  
  LineXY(i, 225 - MinR>>9, i, 225 - MaxR>>9, $ff8000)  
  PrevMinL = MinL : PrevMaxL = MaxL
  PrevMinR = MinR : PrevMaxR = MaxR
Next  
StopDrawing()

Repeat
  Event = WaitWindowEvent()
Until Event = #PB_Event_CloseWindow

Re: Draw a waveform with Purebasic and BASS library

Posted: Wed Jun 13, 2018 8:30 am
by Martin Verlaan
Hi Wilbert,

Whoohoo, this is what I need! If I run your example I see a zoomed view. I noticed that I can change the length of the waveform with SamplesPerPoint. What formula should I use to show the complete song as a waveform? And how can I zoom in to a specific start position and length?

It's cool to see that more people here working with the BASS Library. I am curious what kind of audio software has been made by Purebasic developers.

wilbert wrote:Here's a very simple example just to show how you can draw the wave from sample data.
It does no checks at all at the moment so it assumes the audio is 16 bit stereo, it assumes there's enough memory to load the entire song and it assumes the song is long enough to fill the entire width of the CanvasGadget.
The idea is simply to get the min and max values of a number of samples and use those values to connect the dots.

Code: Select all

Structure Sample
  l.w
  r.w
EndStructure

BASS_Init(-1, 44100, 0, 0, #Null)

Channel.l = BASS_StreamCreateFile(#False, @"Test.mp3", 0, 0, #BASS_STREAM_PRESCAN|#BASS_STREAM_DECODE|#BASS_UNICODE)
Length.q = BASS_ChannelGetLength(Channel, #BASS_POS_BYTE)


SamplesPerPoint = 8

Dim Buffer.Sample(Length >> 2)
BASS_ChannelGetData(Channel, @Buffer(), Length)

OpenWindow(0, 0, 0, 620, 320, "Waveform", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
CanvasGadget(0, 10, 10, 600, 300)

StartDrawing(CanvasOutput(0))
Box(0, 0, 600, 300, $e0e0e0)
PrevMinL.w = 0 : PrevMaxL.w = 0
PrevMinR.w = 0 : PrevMaxR.w = 0
For i = 0 To 599
  MinL.w = Buffer(i*SamplesPerPoint)\l : MaxL.w = MinL
  MinR.w = Buffer(i*SamplesPerPoint)\r : MaxR.w = MinR
  j = 1
  While j < SamplesPerPoint
    If Buffer(i*SamplesPerPoint + j)\l < MinL : MinL = Buffer(i*SamplesPerPoint + j)\l : EndIf
    If Buffer(i*SamplesPerPoint + j)\l > MaxL : MaxL = Buffer(i*SamplesPerPoint + j)\l : EndIf
    If Buffer(i*SamplesPerPoint + j)\r < MinR : MinR = Buffer(i*SamplesPerPoint + j)\r : EndIf
    If Buffer(i*SamplesPerPoint + j)\r > MaxR : MaxR = Buffer(i*SamplesPerPoint + j)\r : EndIf
    j + 1
  Wend
  If PrevMinL > MaxL : LineXY(i, 75 - PrevMinL>>9, i, 75 - MaxL>>9, $ff8000) : EndIf 
  If PrevMaxL < MinL : LineXY(i, 75 - PrevMaxL>>9, i, 75 - MinL>>9, $ff8000) : EndIf 
  If PrevMinR > MaxR : LineXY(i, 225 - PrevMinR>>9, i, 225 - MaxR>>9, $ff8000) : EndIf 
  If PrevMaxR < MinR : LineXY(i, 225 - PrevMaxR>>9, i, 225 - MinR>>9, $ff8000) : EndIf 
  LineXY(i, 75 - MinL>>9, i, 75 - MaxL>>9, $ff8000)  
  LineXY(i, 225 - MinR>>9, i, 225 - MaxR>>9, $ff8000)  
  PrevMinL = MinL : PrevMaxL = MaxL
  PrevMinR = MinR : PrevMaxR = MaxR
Next  
StopDrawing()

Repeat
  Event = WaitWindowEvent()
Until Event = #PB_Event_CloseWindow

Re: Draw a waveform with Purebasic and BASS library

Posted: Wed Jun 13, 2018 10:27 am
by wilbert
Martin Verlaan wrote:Whoohoo, this is what I need! If I run your example I see a zoomed view. I noticed that I can change the length of the waveform with SamplesPerPoint. What formula should I use to show the complete song as a waveform? And how can I zoom in to a specific start position and length?
You can try something like this

Code: Select all

Structure Sample
  l.w
  r.w
EndStructure

Procedure UpdateWaveImage(PBImage, *SampleData.Sample, SampleCount)
  Protected.i Width, Height, X, VOffsetL, VOffsetR, Mul
  Protected.i SamplesPerPixel, Sample
  Protected.w MinL, MaxL, MinR, MaxR
  Protected.w MinL_, MaxL_, MinR_, MaxR_
  If *SampleData And SampleCount
    StartDrawing(ImageOutput(PBImage))
    Width = OutputWidth() : Height = OutputHeight()
    VOffsetL = Height >> 2 : VOffsetR = VOffsetL + Height >> 1
    Mul = (VOffsetL * $19999) >> 16
    Box(0, 0, Width, Height, $ffe0e0e0)
    Line(0, VOffsetL, Width, 1, $ffa0a0a0)
    Line(0, VOffsetR, Width, 1, $ffa0a0a0)
    SamplesPerPixel = (SampleCount + Width - 1) / Width
    MinL = $7fff : MaxL = $8000 : MinR = $7fff : MaxR = $8000
    MinL_ = $8000 : MaxL_ = $7fff : MinR_ = $8000 : MaxR_ = $7fff
    While SampleCount
      If *SampleData\l < MinL : MinL = *SampleData\l : ElseIf *SampleData\l > MaxL : MaxL = *SampleData\l : EndIf
      If *SampleData\r < MinR : MinR = *SampleData\r : ElseIf *SampleData\r > MaxR : MaxR = *SampleData\r : EndIf
      *SampleData + 4
      Sample + 1 : SampleCount - 1
      If Sample = SamplesPerPixel Or SampleCount = 0
        MinL = (MinL * Mul) >> 16 : MaxL = (MaxL * Mul) >> 16
        MinR = (MinR * Mul) >> 16 : MaxR = (MaxR * Mul) >> 16
        If MinL_ > MaxL : LineXY(X, VOffsetL - MinL_, X, VOffsetL - MaxL, $ffff8000) : EndIf 
        If MaxL_ < MinL : LineXY(X, VOffsetL - MaxL_, X, VOffsetL - MinL, $ffff8000) : EndIf 
        If MinR_ > MaxR : LineXY(X, VOffsetR - MinR_, X, VOffsetR - MaxR, $ffff8000) : EndIf 
        If MaxR_ < MinR : LineXY(X, VOffsetR - MaxR_, X, VOffsetR - MinR, $ffff8000) : EndIf 
        LineXY(X, VOffsetL - MinL, X, VOffsetL - MaxL, $ffff8000)
        LineXY(X, VOffsetR - MinR, X, VOffsetR - MaxR, $ffff8000)
        MinL_ = MinL : MaxL_ = MaxL : MinR_ = MinR : MaxR_ = MaxR
        MinL = $7fff : MaxL = $8000 : MinR = $7fff : MaxR = $8000
        X + 1 : Sample = 0
      EndIf
    Wend
    StopDrawing()
  EndIf  
EndProcedure


BASS_Init(-1, 44100, 0, 0, #Null)

Channel.l = BASS_StreamCreateFile(#False, @"Test.mp3", 0, 0, #BASS_STREAM_PRESCAN|#BASS_STREAM_DECODE|#BASS_UNICODE)
Length.q = BASS_ChannelGetLength(Channel, #BASS_POS_BYTE)

Dim Buffer.Sample(Length >> 2)
BASS_ChannelGetData(Channel, @Buffer(), Length)

OpenWindow(0, 0, 0, 620, 320, "Waveform", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)

WaveImage = CreateImage(#PB_Any, 8192, 256)
UpdateWaveImage(WaveImage, @Buffer(), Length >> 2)

ScrollAreaGadget(0, 10, 10, 600, 300, ImageWidth(WaveImage), ImageHeight(WaveImage), 10, #PB_ScrollArea_Center)
ImageGadget(1, 0, 0, 600, 300, ImageID(WaveImage))
CloseGadgetList()

Repeat
  Event = WaitWindowEvent()
Until Event = #PB_Event_CloseWindow
Make sure to compile with debugger disabled or it will be slow.
It could be made much faster using asm but not everyone likes that. So far it's PB code only.
By Creating a different size image, or altering the starting point and sample count for the UpdateWaveImage procedure, you can change the output.

Re: Draw a waveform with Purebasic and BASS library

Posted: Wed Jun 13, 2018 11:23 am
by Martin Verlaan
Thanks a lot for your help Wilbert :!: :!:

Re: Draw a waveform with Purebasic and BASS library

Posted: Fri Jun 15, 2018 3:53 pm
by CELTIC88
thank you DR @wilbert,

very good code .

But the picture of waveform appears wrong

Image

Re: Draw a waveform with Purebasic and BASS library

Posted: Fri Jun 15, 2018 4:17 pm
by RASHAD
Hi CELTIC88
Can you post the name of the sound file you been testing?

Re: Draw a waveform with Purebasic and BASS library

Posted: Fri Jun 15, 2018 4:29 pm
by CELTIC88
hi @RASHAD :D

Egypt lost vs Uruguay :( , good luck in next match

https://a.uguu.se/k8ErarzJCDUZ_notify.wav

Re: Draw a waveform with Purebasic and BASS library

Posted: Fri Jun 15, 2018 4:43 pm
by wilbert
The problem is that it's a mono file while my code is expecting a stereo file.
I'm not very familiar with BASS. It would be nice if there would be an option that converts mono automatically to two channels so it reads as stereo but I don't know if BASS offers that. :?

Re: Draw a waveform with Purebasic and BASS library

Posted: Fri Jun 15, 2018 5:12 pm
by CELTIC88
AH OK :)

you can use "BASS_ChannelGetInfo" to detect number of channel ?

Re: Draw a waveform with Purebasic and BASS library

Posted: Fri Jun 15, 2018 6:45 pm
by Martin Verlaan

Code: Select all

  
Define Info.BASS_CHANNELINFO
Bass_ChannelGetInfo(Channel, @Info)    
Debug Info\chans ; 1=mono, 2=stereo, etc. 

Re: Draw a waveform with Purebasic and BASS library

Posted: Sun Jun 17, 2018 8:20 pm
by Martin Verlaan
@Wilbert, your code works great but when I zoom in a lot, the waveform looks distorted. It happens with all my files. Any idea what could be the reason of this? This is a view of 0.145 seconds
Image

Re: Draw a waveform with Purebasic and BASS library

Posted: Mon Jun 18, 2018 5:49 am
by wilbert
Martin Verlaan wrote:@Wilbert, your code works great but when I zoom in a lot, the waveform looks distorted. It happens with all my files. Any idea what could be the reason of this? This is a view of 0.145 seconds
I'm not sure what is causing it.
You could try to remove the lines below and see what is happening then.

Code: Select all

If MinL_ > MaxL : LineXY(X, VOffsetL - MinL_, X, VOffsetL - MaxL, $ffff8000) : EndIf 
If MaxL_ < MinL : LineXY(X, VOffsetL - MaxL_, X, VOffsetL - MinL, $ffff8000) : EndIf 
If MinR_ > MaxR : LineXY(X, VOffsetR - MinR_, X, VOffsetR - MaxR, $ffff8000) : EndIf 
If MaxR_ < MinR : LineXY(X, VOffsetR - MaxR_, X, VOffsetR - MinR, $ffff8000) : EndIf 
Those lines are to connect the previous and current line if there's a gap so you might end up with gaps if you remove those lines but it's just to see if the other problem goes away.

Re: Draw a waveform with Purebasic and BASS library

Posted: Mon Jun 18, 2018 8:47 am
by Martin Verlaan
Unfortunately that does not help. Maybe it's a limitation or BASS, I will ask on the BASS forum.

Re: Draw a waveform with Purebasic and BASS library

Posted: Mon Jun 18, 2018 9:22 am
by wilbert
Martin Verlaan wrote:Unfortunately that does not help. Maybe it's a limitation or BASS, I will ask on the BASS forum.
Can you try with this updated procedure ?

Code: Select all

Procedure UpdateWaveImage(PBImage, *SampleData.Sample, SampleCount)
  Protected.i Width, Height, X, VOffsetL, VOffsetR, Mul
  Protected.i SamplesPerPixel, Sample
  Protected.w MinL, MaxL, MinR, MaxR
  If *SampleData And SampleCount
    StartDrawing(ImageOutput(PBImage))
    Width = OutputWidth() : Height = OutputHeight()
    VOffsetL = Height >> 2 : VOffsetR = VOffsetL + Height >> 1
    Mul = (VOffsetL * $19999) >> 16
    Box(0, 0, Width, Height, $ffe0e0e0)
    Line(0, VOffsetL, Width, 1, $ffa0a0a0)
    Line(0, VOffsetR, Width, 1, $ffa0a0a0)
    SamplesPerPixel = (SampleCount + Width - 1) / Width
    If SampleCount
      MinL = *SampleData\l : MinR = *SampleData\r : MaxL = MinL : MaxR = MinR      
      While SampleCount
        If *SampleData\l < MinL : MinL = *SampleData\l : ElseIf *SampleData\l > MaxL : MaxL = *SampleData\l : EndIf
        If *SampleData\r < MinR : MinR = *SampleData\r : ElseIf *SampleData\r > MaxR : MaxR = *SampleData\r : EndIf
        Sample + 1 : SampleCount - 1
        If Sample = SamplesPerPixel Or SampleCount = 0
          MinL = (MinL * Mul) >> 16 : MaxL = (MaxL * Mul) >> 16
          MinR = (MinR * Mul) >> 16 : MaxR = (MaxR * Mul) >> 16
          LineXY(X, VOffsetL - MinL, X, VOffsetL - MaxL, $ffff8000)
          LineXY(X, VOffsetR - MinR, X, VOffsetR - MaxR, $ffff8000)
          MinL = *SampleData\l : MinR = *SampleData\r : MaxL = MinL : MaxR = MinR
          X + 1 : Sample = 0
        EndIf
        *SampleData + 4
      Wend
    EndIf
    StopDrawing()
  EndIf  
EndProcedure