Draw a waveform with Purebasic and BASS library

Just starting out? Need help? Post your questions and find answers here.
Martin Verlaan
Enthusiast
Enthusiast
Posts: 133
Joined: Sun Apr 01, 2018 11:26 am

Draw a waveform with Purebasic and BASS library

Post by Martin Verlaan »

Hi all,

Did anyone ever manage to draw a simple waveform with purebasic using the BASS library? I want to do this with a decoded channel and I know that BASS_ChannelGetLevel or BASS_ChannelGetData can be used for this. I tried some things based on examples I found on internet but so far without succes. Can anyone help me with this? I need it to improve my auto dj software ( https://www.smartmixplayer.com/ )

I have this code now, but it does not generate a waveform :(

Code: Select all

IncludeFile "include/bass.pbi"

Macro LOWORD(a) : ((a)&$FFFF) : EndMacro
Macro HIWORD(a) : (((a)>>16)&$FFFF) : EndMacro

OpenWindow(0, #PB_Ignore, #PB_Ignore, 600, 220, "waveform test", #PB_Window_MinimizeGadget|#PB_Window_ScreenCentered) 
CanvasGadget(0, 0, 0, 600, 220)


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

filename.s = "song.mp3"
channel = BASS_StreamCreateFile(0, @filename, 0, 0, #BASS_STREAM_PRESCAN|#BASS_UNICODE|#BASS_STREAM_DECODE)
 

length = BASS_ChannelBytes2Seconds(channel, #BASS_POS_BYTE) 
MaxPixelHeight = 110

x=0
While BASS_ChannelIsActive(channel)  
  level = BASS_ChannelGetLevel(channel)
  
  left  = (LOWORD(level) / 3276) * 2                 
  right = (HIWORD(level) / 3276) * 2 

  PixelYleft = (MaxPixelHeight / 2) - (left * MaxPixelHeight / 65535)
  PixelYright = (MaxPixelHeight / 2) - (right * MaxPixelHeight / 65535)  
  
  If StartDrawing(CanvasOutput(0))
    Line(x, 0, x, PixelYleft , 0)
    Line(x, 110, x, PixelYright , 0)    
    StopDrawing()
  EndIf  
 
  x+1
Wend

Repeat
Until WaitWindowEvent() = #PB_Event_CloseWindow
Thanks in advance,

Martin
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Draw a waveform with Purebasic and BASS library

Post by wilbert »

I don't have any experience with BASS (and it looks there's no free version ?).
But looking at the documentation, BASS_ChannelGetLevel won't work. The level of a channel is like a VU meter, not a waveform.
So you probably need BASS_ChannelGetData instead.
Windows (x64)
Raspberry Pi OS (Arm64)
User avatar
CELTIC88
Enthusiast
Enthusiast
Posts: 154
Joined: Thu Sep 17, 2015 3:39 pm

Re: Draw a waveform with Purebasic and BASS library

Post by CELTIC88 »

Maybe this code will interest you
https://www.purebasic.fr/french/viewtop ... =6&t=15747

8)

Code: Select all

code in my next post....
Last edited by CELTIC88 on Thu Jun 07, 2018 4:15 pm, edited 1 time in total.
interested in Cybersecurity..
User avatar
NicTheQuick
Addict
Addict
Posts: 1226
Joined: Sun Jun 22, 2003 7:43 pm
Location: Germany, Saarbrücken
Contact:

Re: Draw a waveform with Purebasic and BASS library

Post by NicTheQuick »

Instead of LOWORD and HIWORD you also could use a structre union:

Code: Select all

Structure Channel
	left.w
	right.w
EndStructure
Structure Sample
	StructureUnion
		sample.l
		channel.Channel
	EndStructureUnion
EndStructure

Define sample.Sample
sample\sample = BASS_ChannelGetLevel(channel)

Debug sample\channel\left
Debug sample\channel\right
It is more to write, but maybe you find it interesting.
The english grammar is freeware, you can use it freely - But it's not Open Source, i.e. you can not change it or publish it in altered way.
Martin Verlaan
Enthusiast
Enthusiast
Posts: 133
Joined: Sun Apr 01, 2018 11:26 am

Re: Draw a waveform with Purebasic and BASS library

Post by Martin Verlaan »

wilbert wrote:I don't have any experience with BASS (and it looks there's no free version ?).
But looking at the documentation, BASS_ChannelGetLevel won't work. The level of a channel is like a VU meter, not a waveform.
So you probably need BASS_ChannelGetData instead.
Thanks for this useful tip!
Martin Verlaan
Enthusiast
Enthusiast
Posts: 133
Joined: Sun Apr 01, 2018 11:26 am

Re: Draw a waveform with Purebasic and BASS library

Post by Martin Verlaan »

CELTIC88 wrote:Maybe this code will interest you
https://www.purebasic.fr/french/viewtop ... =6&t=15747
Thanks, but as far as I can see this is about saving a wave-file, not drawing a waveform
Martin Verlaan
Enthusiast
Enthusiast
Posts: 133
Joined: Sun Apr 01, 2018 11:26 am

Re: Draw a waveform with Purebasic and BASS library

Post by Martin Verlaan »

NicTheQuick wrote:Instead of LOWORD and HIWORD you also could use a structre union:
Very interesting, i prefer this way.
User avatar
CELTIC88
Enthusiast
Enthusiast
Posts: 154
Joined: Thu Sep 17, 2015 3:39 pm

Re: Draw a waveform with Purebasic and BASS library

Post by CELTIC88 »

test

Code: Select all

IncludeFile "bass.pbi"


CanvasGadget_width = 800
CanvasGadget_Height = 400

OpenWindow(0, #PB_Ignore, #PB_Ignore, CanvasGadget_width, CanvasGadget_Height, "waveform test", #PB_Window_MinimizeGadget|#PB_Window_ScreenCentered) 
CanvasGadget(0, 0, 0, CanvasGadget_width, CanvasGadget_Height)

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

filename.s = OpenFileRequester("","","*.*",0):If filename ="":End 1:EndIf
channel = BASS_StreamCreateFile(0, @filename,
                                0, 0, #BASS_STREAM_PRESCAN|#BASS_SAMPLE_FLOAT|#BASS_UNICODE|#BASS_STREAM_DECODE)

#BASS_LEVEL_STEREO = 2

Structure dfloat
  ff.f[2]
EndStructure
dfloat.dfloat

BASS_ChannelSetPosition(channel,0,#BASS_POS_BYTE)
len.q = BASS_ChannelGetLength(channel,#BASS_POS_BYTE)
lsec.d = BASS_ChannelBytes2Seconds(channel,len)/CanvasGadget_width; number of seconds by pixel
If lsec > 1.0 : lsec = 1.0 : EndIf
rate = BASS_ChannelSeconds2Bytes(channel,lsec)
nfrm = 1 * len / rate
pix.f = CanvasGadget_width/nfrm 
x.f
MaxPixelHeight = CanvasGadget_Height/3
PixelY = CanvasGadget_Height / 2 - MaxPixelHeight
tim = len / 10;BASS_ChannelSeconds2Bytes(channel,60.0)

StartDrawing(CanvasOutput(0))      
For i = 0 To nfrm - 1
  SetWindowTitle(0,  Str(i*100/nfrm) + "%" )
  
  BASS_ChannelGetLevelEx(channel, dfloat, lsec, #BASS_LEVEL_STEREO) ;?#BASS_LEVEL_STEREO
  
  left =  dfloat\ff[0]*100.0
  right =  dfloat\ff[1]*100.0
  
  bp = BASS_ChannelGetPosition(channel,#BASS_POS_BYTE)
  If bp - xx >= tim Or xx = 0
    DrawText(x, 0, FormatDate("%ii:%ss",BASS_ChannelBytes2Seconds(channel,bp)))
    xx =bp
    Line(x,0,1,CanvasGadget_Height,$0000FF)
  EndIf
  
  Line(x, PixelY+MaxPixelHeight -left*MaxPixelHeight /100, 1, left*MaxPixelHeight /100 , 0)
  Line(x, PixelY+MaxPixelHeight, 1, right*MaxPixelHeight /100 ,$C9D63F) 
  
  x + pix
Next
Line(0,PixelY+MaxPixelHeight,CanvasGadget_width,1,$0000FF)

StopDrawing()

Repeat
Until WaitWindowEvent() = #PB_Event_CloseWindow
interested in Cybersecurity..
Martin Verlaan
Enthusiast
Enthusiast
Posts: 133
Joined: Sun Apr 01, 2018 11:26 am

Re: Draw a waveform with Purebasic and BASS library

Post by Martin Verlaan »

Awesome CELTIC88, you are the master! Thank you so much for this example! After searching for days on Internet, I am happy to finally see a code that does (almost) what I want, and that I understand. :mrgreen: :mrgreen: :mrgreen:

I'm going to try now to make a real waveform with BASS_ChannelGetData. I need that to get a better view when zooming into the beats of a song.
Martin Verlaan
Enthusiast
Enthusiast
Posts: 133
Joined: Sun Apr 01, 2018 11:26 am

Re: Draw a waveform with Purebasic and BASS library

Post by Martin Verlaan »

Unfortunately I did not succeed to draw a waveform with BASS_ChannelGetData. I'm pretty good in programming but this is just too difficult for me. Can anyone provide an example with BASS_ChannelGetData? I am even willing to pay for it (in that case send me a PM)
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Draw a waveform with Purebasic and BASS library

Post by wilbert »

Martin Verlaan wrote:Unfortunately I did not succeed to draw a waveform with BASS_ChannelGetData
How do you want the view to look ?
Do you have any visual example (or screenshot from an audio editor you like) ?
Windows (x64)
Raspberry Pi OS (Arm64)
jmg49
New User
New User
Posts: 1
Joined: Wed Sep 13, 2017 10:04 am

Re: Draw a waveform with Purebasic and BASS library

Post by jmg49 »

hi,

perharps this one :

file.s => your file.mp3

MP3D needed but you can easily revert to native code , there were scrolltexts and rasters in this code too

Code: Select all

;credits PureBasic fr for snippets

IncludeFile "fmodex(short).pbi"

Enumeration
  #Mainform
  #Wave
  #spectrum
  #win
EndEnumeration


Global fmodsystem.i, Channel.i
i.i
j.i
Max.i
Position.i
YCurrentWave.i
YOldWave.i

EP_InitFXLib()
MP_Graphics3D (1024,768,0,1) 



CanvasGadget(#spectrum, 240, 150, 540, 380)


FMOD_System_Create(@fmodsystem)

FMOD_System_Init(fmodsystem, 32, #FMOD_INIT_NORMAL, 0)
File.s = "Gunther.mp3" ; your file

Dim WaveArray.f(1024)
FMOD_System_CreateStream(fmodsystem, @File, #FMOD_SOFTWARE, 0, @sound)
FMOD_System_PlaySound(fmodsystem, #FMOD_CHANNEL_FREE, sound, 0, @channel)

;----MainLoop---------------------------------------------------------------


Repeat
  
  event=WindowEvent()         
  
 FMOD_Channel_GetWaveData(Channel, WaveArray(), 1024, 0)
   
   
 StartDrawing(CanvasOutput(#spectrum))
    
     
  DrawingMode(#PB_2DDrawing_Transparent) 
  Box(0, 0, 1026, 500,0) 
    
    
  DrawingMode(#PB_2DDrawing_Default)
    For i=1 To 1024
      YOldWave=(WaveArray(i-1)+1)*250
      YCurrentWave=(WaveArray(i)+1)*250
      LineXY(i-1,YOldWave,i,YCurrentWave,RGB(0,200,50))
      LineXY(i-1,YOldWave+1,i,YCurrentWave+1,RGB(0,90,50))
      LineXY(i-1,YOldWave-1,i,YCurrentWave-1,RGB(0,110,50))
    Next 
    
    StopDrawing()
    
          
           
 
 
 MP_RenderWorld() 
 MP_Flip () 
 
 
 Until GetAsyncKeyState_(#VK_ESCAPE)

          
      FMOD_Channel_Stop(Channel)
      FMOD_System_Release(fmodsystem)
      End
      
 

RASHAD
PureBasic Expert
PureBasic Expert
Posts: 4659
Joined: Sun Apr 12, 2009 6:27 am

Re: Draw a waveform with Purebasic and BASS library

Post by RASHAD »

Consider it as a start
You got the sample data in the buffer now see how you want to draw the curve

Code: Select all

IncludeFile "bass.pbi"

OpenWindow(0, 0,0,800,400, "waveform test", #PB_Window_MinimizeGadget|#PB_Window_ScreenCentered)
CanvasGadget(0, 10, 10, 780,380)

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

filename.s = OpenFileRequester("","","*.*",0):If filename ="":End 1:EndIf
CompilerIf #PB_Compiler_Unicode
  *Buffer = Ascii(filename)
  sample = BASS_SampleLoad(0, *Buffer, 0, 0, 65535, #BASS_SAMPLE_SOFTWARE)
  FreeMemory(*Buffer)
CompilerElse
  sample = BASS_SampleLoad(0, @filename, 0, 0, 65535, #BASS_SAMPLE_SOFTWARE)
CompilerEndIf

;To get Channel length in bytes
; # 1 :
; info.BASS_SAMPLE
; BASS_SampleGetInfo(sample, @info)
; channels = info\chans
; length = info\length
; Debug length

; Or 
channelh = BASS_SampleGetChannel(sample,1)
length = BASS_ChannelGetLength(channelh,#BASS_POS_BYTE)
;BASS_ChannelPlay(channelh, 0)
*buffer = AllocateMemory(length)
BASS_ChannelGetData(channelh, *buffer, length)
x = 10
  For i = 10000 To length-1 Step 3
    StartVectorDrawing(CanvasVectorOutput(0))
      MovePathCursor(x,350,#PB_Path_Relative)     
      AddPathCurve(x+2,PeekB(*buffer+i)*10, x+4, PeekB(*buffer+i+1)*10, x+6, PeekB(*buffer+i+2)*10)
      VectorSourceColor(RGBA(255, 0, 0, 255))
      StrokePath(2)
      x + 3
    StopVectorDrawing()
  Next
  Delay(10)
Repeat
Until WaitWindowEvent() = #PB_Event_CloseWindow
Egypt my love
User avatar
CELTIC88
Enthusiast
Enthusiast
Posts: 154
Joined: Thu Sep 17, 2015 3:39 pm

Re: Draw a waveform with Purebasic and BASS library

Post by CELTIC88 »

you don't need 'BASS Channel GetData' to zooming in !

in my example you can show 0.001 ms of song :o

if I have more time I put an example

ps: if your sound is a wav file you can display waveform without (bassapi)


good luck :)
interested in Cybersecurity..
Martin Verlaan
Enthusiast
Enthusiast
Posts: 133
Joined: Sun Apr 01, 2018 11:26 am

Re: Draw a waveform with Purebasic and BASS library

Post by Martin Verlaan »

Thanks to all of you for your fast replies and examples! :shock: :D

@Wilbert: I like the view of Cool Edit / Adobe Audition. It doesn’t have to look beautiful with colors and a raster. I just need a simple (but clear) drawing, if possible with 2 channels (stereo)

@jmg49: I’ve modified your code a bit to make it work with BASS. So far, so good, it draws a waveform so I think I can build further on this.

@Rashad: It’s interesting that you use BASS_SampleLoad. I haven’t seen this before in other examples. However, if I run your example it takes about 2 minutes before I see the picture and it doesn’t look good
Image

@CELTIC88: I love your example and I managed to zoom in. But the problem is the zoomed view looks different then a real waveform. It’s not very useful to find a beat. In the pictures (with a zoomed view of 0.029 seconds) below you can see the difference.

Yours:
Image

Cool Edit:
Image
Post Reply