This program is very simple, I made it to play OGG files - my default Windows media player doesn't like them. So I had a little fun in an afternoon knocking this up.
Code is over-commented, good for newbies? (Or not?)
` **************************************************
`
` AUDIO BULLY
` by viberunner
` viberunner@gmail.com
` viberunner.wordpress.com
`
` DLL Compilation Requirements:
`
` AUDIO EAP (main file - plays OGG, etc.)
` DarkSideDeskTop (desktop info)
` Gogas Free Windows (file open, message box)
` Matrix1Util_15 (Windows-friendly waits)
` Matrix1Util_16 (string manipulation)
`
` You also need the 'Webdings' font installed.
`
` **************************************************
` **************************************************
` DOCUMENTATION
` This is my first Windows application in DBP.
` It is Freeware Public Domain.
`
` DBP is not first-choice for Windows development,
` but it is feasable. Just. I needed a method of
` playing Ogg Vorbis audio files, so I spent an
` afternoon coding this, drinking iced and sliced
` gin and tonics (Hendriks), all while listening
` to the excellent podcast "Starship Sofa".
`
` Newer programmers might be interested in seeing
` how the UDT (User Defined Type) arrays are used
` to make control, clickable buttons.
`
` I realise this code requires five (!!) user-made
` DLL plug-ins, but they are all excellent and are
` heartily recommended for all DBP fans.
` **************************************************
` Escape key disabled, we will trap it manually
#CONSTANT font_size 36
SET TEXT OPAQUE
DISABLE ESCAPEKEY
SYNC ON
SYNC RATE 10
` I had all manner of grief with this. If you have
` trouble running the program look in the Project
` Settings, select "Windowed" mode radio button,
` then click the PICK button to the right of it,
` click "Use Custom Values", enter "414x36",
` without the quotes, and click OK. When that's
` done the program should show "Windowed" as
` selected and "(414x36)" next to it. NOW click
` the "Hidden" radio button. The program should
` load invisibly and correct size & place the window.
SET WINDOW SIZE 414,36
SET WINDOW LAYOUT 0,0,0
font_half AS BYTE
font_half = font_size / 2
` Very handy constants for Boolean checks
` I also have True/False added as Editor keywords
#CONSTANT TRUE 1
#CONSTANT FALSE 0
` The window grid.
` A multi-line program would have y1 y2 too.
` Ding is the character to display, if you
` use icons the image number goe there instead
` (and extend the variable from byte to word or dword)
TYPE Grid_TYPE
x1
x2
ding AS BYTE
active AS BOOLEAN
ENDTYPE
` Set the window grid array up
DIM Grid(10) AS Grid_TYPE
wx1 = (DarkSide_Get_Desktop_Width()/2) - (GET WINDOW WIDTH()/2)
wy1 = 0
wx2 = wx1 + GET WINDOW WIDTH() - 1
wy2 = wy1 + GET WINDOW HEIGHT() - 1
SET WINDOW POSITION wx1,wy1
` The various buttons
#CONSTANT BUTTON_LOAD 0
#CONSTANT BUTTON_PLAY 1
#CONSTANT BUTTON_PAUSE 2
#CONSTANT BUTTON_STOP 3
#CONSTANT BUTTON_LOOP 4
#CONSTANT BUTTON_REV 5
#CONSTANT BUTTON_FWD 6
#CONSTANT BUTTON_PITCH 7
#CONSTANT BUTTON_INFO 8
#CONSTANT BUTTON_MIN 9
#CONSTANT BUTTON_CLOSE 10
` ASCII character of the Webding
grid(BUTTON_LOAD).ding = 205
grid(BUTTON_PLAY).ding = 52
grid(BUTTON_PAUSE).ding = 59
grid(BUTTON_STOP).ding = 60
grid(BUTTON_LOOP).ding = 113
grid(BUTTON_REV).ding = 55
grid(BUTTON_FWD).ding = 56
grid(BUTTON_PITCH).ding = 175
grid(BUTTON_INFO).ding = 115
grid(BUTTON_MIN).ding = 48
grid(BUTTON_CLOSE).ding = 114
` Load the grid X/Y co-ordinates
grid(0).x1 = 0
grid(0).x2 = grid(0).x1 + font_size
grid(0).active = FALSE
FOR i = 1 TO ARRAY COUNT(grid(0))
grid(i).x1 = grid(i-1).x2 + 1
grid(i).x2 = grid(i).x1 + font_size
grid(i).active = FALSE
aa_x1 = grid(i).x1
aa_x2 = grid(i).x2
NEXT i
` Used to draw the positional line
show_audio_frame AS BOOLEAN
audio_frame AS DWORD
audio_frame_ratio# AS DOUBLE FLOAT
show_audio_frame = FALSE
audio_frame = 0
audio_frame_ratio# = 0.0
` Store the required colours, this is slightly
` quicker than using RGB() many times
` Red is highlight (selected)
` Blue is normal (selectable)
` Navy is ghosted (unselectable)
ink_red AS DWORD
ink_blue AS DWORD
ink_navy AS DWORD
ink_gold AS DWORD
ink_red = RGB(255,128,128)
ink_blue = RGB(128,128,255)
ink_navy = RGB(64,64,128)
ink_gold = RGB(128,128,96)
` File name (starts blank)
name$ = "No File Loaded"
` Clear the screen
INK rgb(255,255,255),0
CLS
` See if a Command Line exists
` if so try to load that file
CL_File$ = CL$()
IF (FAST LEN(CL_File$) <> 0)
quote$=chr$(34)
file$ = REMOVE ALL$(CL_File$,quote$)
GOSUB LoadFile
ENDIF
` *****************
` START OF PROGRAM
` *****************
DO
` Update the window display
GOSUB Display
` Check if a mouse pressed
` it sets the "button_pressed" variable
GOSUB Click
` If a button pressed, perform that action
SELECT button_pressed
CASE BUTTON_LOAD : GOSUB Load : ENDCASE
CASE BUTTON_PLAY : GOSUB Play : ENDCASE
CASE BUTTON_PAUSE : GOSUB Pause : ENDCASE
CASE BUTTON_STOP : GOSUB Stop : ENDCASE
CASE BUTTON_LOOP : GOSUB Loopz : ENDCASE
CASE BUTTON_REV : GOSUB Rev : ENDCASE
CASE BUTTON_FWD : GOSUB Fwd : ENDCASE
CASE BUTTON_PITCH : GOSUB Pitch : ENDCASE
CASE BUTTON_INFO : GOSUB Info : ENDCASE
CASE BUTTON_MIN : GOSUB Minim : ENDCASE
CASE BUTTON_CLOSE : GOSUB Close : ENDCASE
ENDSELECT
LOOP
` ***************
` END OF PROGRAM
` ***************
` Program *shouldn't* reach herem, as we trap all exists...
` ... but just in case it does
GOSUB Close
END
Load:
` Set the button active (so it appears red)
` Display it - and wait a little time for windows to display it
` The Nice Wait is more windows-friendly than the normal wait
Grid(BUTTON_LOAD).active = TRUE
GOSUB Display
NICE WAIT 100
` File load dialogue box
dir$ = GET DIR$()
title$ = "Audio Bully - Load Sound"
filter$ = "All Audio Files"
filter$ = filter$ + "|*.Ogg;*.mp3;*.flac;*.wav;*.aiff;*.mod;*.s3m;*.xm;*.it"
filter$ = filter$ + "|Ogg (*.Ogg)|*.Ogg"
filter$ = filter$ + "|MP3 (*.mp3)|*.mp3"
filter$ = filter$ + "|FLAC (*.flac)|*.flac"
filter$ = filter$ + "|WAV (*.wav)|*.wav"
filter$ = filter$ + "|AIFF (*.aiff)|*.aiff"
filter$ = filter$ + "|MOD (*.mod)|*.mod"
filter$ = filter$ + "|S3M (*.s3m)|*.s3m"
filter$ = filter$ + "|XM (*.xm)|*.xm"
filter$ = filter$ + "|IT (*.it)|*.it"
file$ = OpenFileBox(dir$,title$,filter$)
` If CANCEL pressed, return to main program
IF (file$ = "-1")
Grid(BUTTON_LOAD).active = FALSE
RETURN
ENDIF
` Before we load a new file, make sure we delete
` the old one (e.g. if this is the second file loaded)
IF (AUDIO EXIST(1) = TRUE) : DELETE AUDIO 1 : ENDIF
GOSUB LoadFile
RETURN
LoadFile:
` Requires "file$" has been loaded with the file to load
LOAD AUDIO 1,file$
` Determine if the file is seekable (rewind/fast forward)
` If it is find the size of the file and scale it to 400
` (the length of the status bar)
show_audio_frame = AUDIO SEEKABLE(1)
IF (show_audio_frame = TRUE)
audio_frame_ratio# = AUDIO LENGTH(1) / 400.0
audio_frame = AUDIO POSITION(1) / audio_frame_ratio#
ELSE
audio_frame = 0
audio_frame_ratio# = 0.0
ENDIF
` Very quick way of extracting the file name from the path
` UPDATE: DBP Forums have problems dislaying backslash char,
` so construct it from it's ASCII value.
backslash$ = CHR$(92)
SPLIT STRING file$,backslash$
name$ = GET SPLIT WORD$(SPLIT COUNT())
` Set the button back to not-active
Grid(BUTTON_LOAD).active = FALSE
` Make sure if the Play, Pause, or Loop, are active, deactivate
Grid(BUTTON_PLAY).active = FALSE
Grid(BUTTON_LOOP).active = FALSE
Grid(BUTTON_PAUSE).active = FALSE
` And play the newly-loaded file
GOSUB Play
RETURN
Play:
` Nothing to do if no file loaded
IF (AUDIO EXIST(1) = FALSE) : RETURN : ENDIF
` If file already playing, nothing to do
IF (AUDIO PLAYING(1) = TRUE) : RETURN : ENDIF
` Play and Loop are different, so if file is played
` Then deactivate Loop button
Grid(BUTTON_LOOP).active = FALSE
` Note we do NOT activate the Play button here,
` we COULD, but the DISPLAY form checks for that
` and activates it if a file is playing. That
` takes care of the situation of a file playing
` and getting to the end and stopping.
` Play the file
PLAY AUDIO 1
RETURN
Pause:
` Nothing to do if no file loaded
IF (AUDIO EXIST(1) = FALSE) : RETURN : ENDIF
` If file is not playing and not paused then nothing to do
IF ((AUDIO PLAYING(1) = FALSE) AND (Grid(BUTTON_PAUSE).active = FALSE)) : RETURN : ENDIF
` If file is not paused, then pause it and set button
IF (Grid(BUTTON_PAUSE).active = FALSE)
PAUSE AUDIO 1
Grid(BUTTON_PAUSE).active = TRUE
ELSE
` File is already paused, so un-pause it
RESUME AUDIO 1
Grid(BUTTON_PAUSE).active = FALSE
ENDIF
RETURN
Stop:
` Nothing to do if no file loaded
IF (AUDIO EXIST(1) = FALSE) : RETURN : ENDIF
` Stops the file being played,
STOP AUDIO 1
` De-activate all play/pause/loop buttons
Grid(BUTTON_PLAY).active = FALSE
Grid(BUTTON_PAUSE).active = FALSE
Grid(BUTTON_LOOP).active = FALSE
` Flick on the stop button for a few secs, then off again
Grid(BUTTON_STOP).active = TRUE
GOSUB Display
NICE WAIT 100
Grid(BUTTON_STOP).active = FALSE
RETURN
Loopz:
` Nothing to do if no file loaded
IF (AUDIO EXIST(1) = FALSE) : RETURN : ENDIF
`Loops the music, set loop button on
Grid(BUTTON_LOOP).active = TRUE
LOOP AUDIO 1
RETURN
Rev:
` Nothing to do if no file loaded
IF (AUDIO EXIST(1) = FALSE) : RETURN : ENDIF
` Don't do anything if it's a non-scrollable sound
IF (show_audio_frame = FALSE) : RETURN : ENDIF
` Axctivate the button
grid(BUTTON_REV).active = TRUE
` If the file isn't playing, or pause, still do the scroll
` but set the pause button on
IF ((AUDIO PLAYING(1) = FALSE) AND (grid(BUTTON_PAUSE).active = FALSE))
PAUSE AUDIO 1
grid(BUTTON_PAUSE).active = TRUE
ENDIF
` While the mouse is held down, decrease the position by one percent
` And update the display
WHILE (MOUSECLICK() = 1)
Frame = AUDIO POSITION(1) - (AUDIO LENGTH(1) / 100)
IF (Frame < 0) : Frame = 0 : ENDIF
AUDIO SET POSITION 1,Frame
GOSUB Display
ENDWHILE
` Reset button
grid(BUTTON_REV).active = FALSE
RETURN
Fwd:
` Nothing to do if no file loaded
IF (AUDIO EXIST(1) = FALSE) : RETURN : ENDIF
` Don't do anything if it's a non-scrollable sound
IF (show_audio_frame = FALSE) : RETURN : ENDIF
` Axctivate the button
grid(BUTTON_FWD).active = TRUE
` If the file isn't playing, or pause, still do the scroll
` but set the pause button on
IF ((AUDIO PLAYING(1) = FALSE) AND (grid(BUTTON_PAUSE).active = FALSE))
PAUSE AUDIO 1
grid(BUTTON_PAUSE).active = TRUE
ENDIF
` While the mouse is held down, increase the position by one percent
` And update the display
WHILE (MOUSECLICK() = 1)
Frame = AUDIO POSITION(1) + (AUDIO LENGTH(1) / 100)
IF (Frame > AUDIO LENGTH(1)) : Frame = AUDIO LENGTH(1) : ENDIF
AUDIO SET POSITION 1,Frame
GOSUB Display
ENDWHILE
` Reset button
grid(BUTTON_FWD).active = FALSE
RETURN
Pitch:
` Nothing to do if no file loaded
IF (AUDIO EXIST(1) = FALSE) : RETURN : ENDIF
` Set the button on for a while
Grid(BUTTON_PITCH).active = TRUE
GOSUB Display
NICE WAIT 100
` Get user-input of amount to shift the pitch
title$ = "Audio Bully - Pitch Shift"
prompt$ = "Suggested Range 0.1 - 4.5"
pitch# = GET AUDIO PITCH SHIFT(1)
filter$ = STR$(pitch#)
pitch$ = InputBox(title$, prompt$, filter$)
pitch# = VAL(pitch$)
` Do the pitch shift
AUDIO PITCH SHIFT 1,pitch#
` Turn the button off again
Grid(BUTTON_PITCH).active = FALSE
RETURN
Info:
` Turn button on
Grid(BUTTON_INFO).active = TRUE
GOSUB Display
NICE WAIT 100
` Get the current pitch (if a file is loaded)
IF (AUDIO EXIST(1) = TRUE)
pitch$ = STR$(GET AUDIO PITCH SHIFT(1))
ELSE
pitch$ = "n/a"
ENDIF
` Display the info message
Message$ = " Audio Bully " + chr$(13)
Message$ = Message$ + " viberunner " + chr$(13) + chr$(13)
Message$ = Message$ + name$ + chr$(13)+ chr$(13)
IF (AUDIO EXIST(1) = TRUE)
IF (AUDIO SEEKABLE(1) = TRUE) : Yes_No$ = "Yes" : ELSE : Yes_No$ = "No" : ENDIF
` The status bar is based on 0-400, so divide by 4 for actual percentage
IF (audio_frame > 0) : percentage = audio_frame / 4 : ELSE : percentage = 0 : ENDIF
Message$ = Message$ + "Pitch: " + pitch$ + chr$(13)
Message$ = Message$ + "Seekable: " + Yes_No$ + chr$(13)
Message$ = Message$ + "Length: " + STR$(AUDIO LENGTH(1)) + chr$(13)
Message$ = Message$ + "Position: " + STR$(AUDIO POSITION(1)) + chr$(13)
Message$ = Message$ + "Percent: " + STR$(percentage) + chr$(13)
ENDIF
MsgBox Message$,4,1
NICE WAIT 100
` Button off again
Grid(BUTTON_INFO).active = FALSE
RETURN
Minim:
` Turn the button on, minimise, and button off again
Grid(BUTTON_MIN).active = TRUE
GOSUB Display
NICE WAIT 100
MINIMIZE WINDOW
Grid(BUTTON_MIN).active = FALSE
RETURN
Click:
` There is no button 255, if 255 is returned then
` main program won't be doing much
button_pressed = 255
` Don't bother trying to work if window doesn't have focus
IF (HAS FOCUS() = FALSE) : RETURN : ENDIF
` ScanCode 1 is the Escape Key, if pressed exit program
IF (SCANCODE()=1) : button_pressed = BUTTON_CLOSE : RETURN : ENDIF
` If the mouse is outside the display box, then don't do anyting
IF ((DarkSide_Get_Desktop_MouseX() < wx1) OR _
(DarkSide_Get_Desktop_MouseX() > wx2) OR _
(DarkSide_Get_Desktop_MouseY() < wy1) OR _
(DarkSide_Get_Desktop_MouseY() > wy2))
RETURN
ENDIF
` If the left mouse not clicked, exit
IF (MOUSECLICK() <> 1) : RETURN : ENDIF
FOR i = 0 TO ARRAY COUNT(grid(0))
` Find which of the buttons has been pressed
IF ((MOUSEX() >= grid(i).x1) AND (MOUSEX() <= grid(i).x2))
button_pressed = i
ENDIF
NEXT i
` If no valid button pressed, exit
IF (button_pressed = 255) : RETURN : ENDIF
` Scrollable buttons, exit immediately to start scrolling
IF ((button_pressed = BUTTON_REV) OR (button_pressed = BUTTON_FWD)) : RETURN : ENDIF
` Pauses until button is released
NICE WAIT NO MOUSE
` If the mouse has moved away from the button then don't do anything
` (this lets the user change their mind after a mouse click)
IF ((MOUSEX() < grid(button_pressed).x1) OR _
(MOUSEX() > grid(button_pressed).x2) OR _
(DarkSide_Get_Desktop_MouseY() < wy1) OR _
(DarkSide_Get_Desktop_MouseY() > wy2))
button_pressed = 255
ENDIF
RETURN
Display:
` Note the second parameter of Font Webdings (charset)
SET TEXT FONT "Webdings",2
SET TEXT SIZE font_size
` Clear the screen
CLS
` This code turns off the play button if the sound has ended
IF (AUDIO EXIST(1) = TRUE)
` If it is still playing, make sure play button is on
IF (AUDIO PLAYING(1) = TRUE)
Grid(BUTTON_PLAY).active = TRUE
Grid(BUTTON_PAUSE).active = FALSE
ELSE
` File not playing, stop play button
Grid(BUTTON_PLAY).active = FALSE
ENDIF
ENDIF
` Code to display the icons
FOR i = 0 TO ARRAY COUNT(grid(0))
` By default try to make selectable all buttons
INK ink_blue,0
` If the button is active, make it red
IF (grid(i).active = TRUE)
INK ink_red,0
ENDIF
` If no file has been loaded, ghost the relevant buttons
IF (AUDIO EXIST(1) = FALSE)
IF ((i = BUTTON_PLAY) OR _
(i = BUTTON_PAUSE) OR _
(i = BUTTON_STOP) OR _
(i = BUTTON_LOOP) OR _
(i = BUTTON_REV) OR _
(i = BUTTON_FWD) OR _
(i = BUTTON_PITCH))
INK ink_navy,0
ENDIF
ENDIF
` A sound is loaded, but it's not seachable, so ghost scroll buttons
IF ((AUDIO EXIST(1) = TRUE) AND (show_audio_frame = FALSE))
IF ((i = BUTTON_REV) OR (i = BUTTON_FWD))
INK ink_navy,0
ENDIF
ENDIF
` Print the Wingding character to the screen
CENTER TEXT (grid(i).x1 + font_half), 0, CHR$(grid(i).ding)
NEXT i
` Draw the progrss bar
SET TEXT FONT "Verdana",1
SET TEXT SIZE font_half
INK ink_gold,0
IF (AUDIO EXIST(1) = TRUE)
IF ((AUDIO PLAYING(1) = TRUE) OR (grid(BUTTON_PAUSE).active = TRUE))
IF (show_audio_frame = TRUE)
audio_frame = AUDIO POSITION(1) / audio_frame_ratio#
LINE 7,font_size-5,audio_frame+7,font_size-5
ENDIF
ENDIF
ENDIF
` Makes program more windows-friendly
SYNC SLEEP 1
SYNC
RETURN
Close:
` Actdivate the close button
Grid(BUTTON_CLOSE).active = TRUE
GOSUB Display
NICE WAIT 100
` Delete the audio if it exists
IF (AUDIO EXIST(1) = TRUE) : DELETE AUDIO 1 : ENDIF
` Program End
END
RETURN
I'd be grateful for any tips for improving this - other than just add functionality like playlists and saving INI settings, etc.
UPDATE II. Changed the SPLIT STRING command so it doesn't use a hard-coded backslash but an ASCII generated one - more TGC forum-friendly.