Progress bar

Mac OSX specific forum
codeit
User
User
Posts: 62
Joined: Sat Apr 15, 2017 5:53 pm
Location: Leicestershire

Progress bar

Post by codeit »

Hello i am looking for help setting up a progress bar for my Mp3 player.
I have the duration of the mp3 file but how would you go about using it?
possible math formula using current time/duration but i am useless with
maths :mrgreen: so i look to you guys for help thanks
Wolfram
Enthusiast
Enthusiast
Posts: 567
Joined: Thu May 30, 2013 4:39 pm

Re: Progress bar

Post by Wolfram »

I updated my example from yesterday to set the Progressbar to the right mx value and fixed some careless mistake.
http://www.purebasic.fr/english/viewtop ... 19&t=68791
Now you need a callback which reads the play time and set the Progessbar to that time.
This looks like a job for Wilbert ;-)
macOS Catalina 10.15.7
codeit
User
User
Posts: 62
Joined: Sat Apr 15, 2017 5:53 pm
Location: Leicestershire

Re: Progress bar

Post by codeit »

Hi Wolfram
Thanks for that but it's getting the play time i am stuck with

Code: Select all

Structure AudioItem
  title.s
  album.s
  duration.l
  fileName.s
  fileSize.q
EndStructure
Have been looking all afternoon on the net but very little on the subject :cry:
But thanks
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Progress bar

Post by wilbert »

Wolfram wrote:Now you need a callback which reads the play time and set the Progessbar to that time.
This looks like a job for Wilbert ;-)
Unfortunately NSSound doesn't have something like that.
As far as I know the only way is to use a timer and get currentTime yourself.
If you also want to add peak meters, you can consider using AVAudioPlayer which works a bit similar
http://www.purebasic.fr/english/viewtop ... 56#p459156
Unfortunately that also requires a timer to monitor progress.
codeit wrote:Thanks for that but it's getting the play time i am stuck with
I'm not sure what you mean but both NSSound and AVAudioPlayer have methods named currentTime and duration (both are double precision).
Windows (x64)
Raspberry Pi OS (Arm64)
codeit
User
User
Posts: 62
Joined: Sat Apr 15, 2017 5:53 pm
Location: Leicestershire

Re: Progress bar

Post by codeit »

Would it be possible to show me how to use currentTime and duration to give the required progress bar value
Thanks
Wolfram
Enthusiast
Enthusiast
Posts: 567
Joined: Thu May 30, 2013 4:39 pm

Re: Progress bar

Post by Wolfram »

for example…

Code: Select all

EnableExplicit

; >>> Some MetaData related things <<<

ImportC "-framework CoreServices"
  CFStringCreateWithCString(alloc, cStr.p-utf8, encoding = $8000100) 
  MDItemCopyAttribute(item, name)
  MDItemCreate(allocator, path)
EndImport

Enumeration #PB_Event_FirstCustomValue
  #ProgressBar
  
EndEnumeration

Enumeration #PB_EventType_FirstCustomValue
  #setProgressBar
EndEnumeration

UsePNGImageDecoder()

Procedure.s GetCFString(CFString.i)
  Protected CFStringLength.i = CFStringGetLength_(CFString)
  Protected Dim CharacterData.u(CFStringLength)
  CFStringGetCString_(CFString, @CharacterData(), CFStringLength + 1, #kCFStringEncodingUTF8)
  ProcedureReturn PeekS(@CharacterData(), -1, #PB_UTF8)
EndProcedure

Global kMDItemDurationSeconds.i = CFStringCreateWithCString(0, "kMDItemDurationSeconds")
Global kMDItemTitle.i = CFStringCreateWithCString(0, "kMDItemTitle")
Global kMDItemAlbum.i = CFStringCreateWithCString(0, "kMDItemAlbum")
Global kMDItemMusicalGenre.i = CFStringCreateWithCString(0, "kMDItemMusicalGenre")

Global Img0 = LoadImage(#PB_Any,"Resources/ys-backward-3btns.png")
Global Img1 = LoadImage(#PB_Any,"Resources/ys-play.png")
Global Img2 = LoadImage(#PB_Any,"Resources/ys-forward-3btns.png")
Global Img3 = LoadImage(#PB_Any,"Resources/ys-stop.png")
Global Img4 = LoadImage(#PB_Any,"Resources/ys-playlist-1btn.png")
Global Img5 = LoadImage(#PB_Any,"Resources/ys-playlist-1btn.png")

Global Exp.i, PlayList.i, Play.i, Stop.i, progressBar.i
Global BackWard.i, ForWard.i, Hide.i

; >>> AudioItem structure <<<

Structure AudioItem
  title.s 
  album.s
  duration.l
  fileName.s
  fileSize.q
EndStructure


; >>> Procedure to get meta data <<<

Procedure GetMetaData(*Item.AudioItem, AudioFileName.s)
  Protected.i Path, MDItem, Attribute
  Path = CFStringCreateWithCString(0, AudioFileName)
  MDItem = MDItemCreate(0, Path)
  If MDItem
   
    ; get title
    Attribute = MDItemCopyAttribute(MDItem, kMDItemTitle)
    If Attribute
      *Item\title = GetCFString(Attribute)
      CFRelease_(Attribute)
    EndIf
   
    ; get album
    Attribute = MDItemCopyAttribute(MDItem, kMDItemAlbum)
    If Attribute
      *Item\album = GetCFString(Attribute)
      CFRelease_(Attribute)
    EndIf
   
    ; get duration
    Attribute = MDItemCopyAttribute(MDItem, kMDItemDurationSeconds)
    If Attribute
      CFNumberGetValue_(Attribute, #kCFNumberSInt32Type, @*Item\duration)
      CFRelease_(Attribute)
    EndIf
   
    CFRelease_(MDItem)
    CFRelease_(Path)
  EndIf
EndProcedure


; >>> Procedure to add audio to playlist <<<

Procedure AddAudio(List PlayList.AudioItem(), FileOrDirectory.s, Recurse = #False)
  Protected NewList Directory.s()
  Protected *Item.AudioItem
  Protected.i d, Size, DirectoryName.s, Extension.s
 
  Size = FileSize(FileOrDirectory)
  If Size = -2
    AddElement(Directory())
    Directory() = FileOrDirectory
  ElseIf Size > 0
    *Item = AddElement(PlayList())
    GetMetaData(*Item, FileOrDirectory)
    *Item\fileName = FileOrDirectory
    *Item\fileSize = Size
  EndIf
 
  While FirstElement(Directory())
    DirectoryName = Directory()
    DeleteElement(Directory())
    d = ExamineDirectory(#PB_Any, DirectoryName, "*.*")
    If d
      While NextDirectoryEntry(d)
        ; exclude files and directories starting with '.'
        If Asc(DirectoryEntryName(d)) <> '.'
          FileOrDirectory = DirectoryName + DirectoryEntryName(d)
          Extension = LCase(GetExtensionPart(FileOrDirectory))
          Size = FileSize(FileOrDirectory)
          If Size = -2
            ; exclude macOS apps which are actually directories
            If Recurse And Extension <> "app"
              AddElement(Directory())
              Directory() = FileOrDirectory + "/"
            EndIf
          ElseIf Size > 0
            If Extension = "mp3" Or Extension = "wav"
              *Item = AddElement(PlayList())
              GetMetaData(*Item, FileOrDirectory)
              *Item\fileName = FileOrDirectory
              *Item\fileSize = Size
            EndIf 
          EndIf
        EndIf
      Wend
    EndIf
    FinishDirectory(d)
  Wend
 
EndProcedure



; >>> Main code <<<

Global NewList PlayList.AudioItem()
Global.i NSSound, delegateClass, soundDelegate, time.d

Declare ItemSelectedCallback()

ProcedureC DidFinishPlayingCallback(obj,sel,sound,flag)
  Protected.i i
  If flag = #YES
    i = GetGadgetState(PlayList) + 1; get next list index
    If i < CountGadgetItems(PlayList)
      SetGadgetState(PlayList, i)
      ItemSelectedCallback()
    Else
      SetGadgetState(PlayList, -1)
    EndIf     
  EndIf
EndProcedure

delegateClass = objc_allocateClassPair_(objc_getClass_("NSObject"), "SoundDelegateClass",0)
class_addMethod_(delegateClass,sel_registerName_("sound:didFinishPlaying:"), @DidFinishPlayingCallback(), "v@:@c")
objc_registerClassPair_(delegateClass)
soundDelegate = class_createInstance_(delegateClass, 0)


Procedure whilePlaying(soundObject)

  While CocoaMessage(0, soundObject, "isPlaying")
    
    CocoaMessage(@time, soundObject, "currentTime", 0)
    Delay(1)
    
    PostEvent(#ProgressBar)
  Wend 
  
EndProcedure

Procedure ItemSelectedCallback()
  Protected *Item.AudioItem
  *Item = GetGadgetItemData(PlayList, GetGadgetState(PlayList))
  If NSSound
    CocoaMessage(0, NSSound, "stop")
    CocoaMessage(0, NSSound, "release")
  EndIf
  NSSound = CocoaMessage(0, CocoaMessage(0, 0, "NSSound alloc"),
                         "initWithContentsOfFile:$", @*Item\fileName, "byReference:", #YES)
  CocoaMessage(0, NSSound, "setDelegate:", soundDelegate)
  
  ;-set Progersbar max value
  SetGadgetAttribute(progressBar, #PB_ProgressBar_Maximum, *Item\duration)
  Debug *Item\duration
  CocoaMessage(0, NSSound, "play")
  
  CreateThread(@whilePlaying(), NSSound)
  
EndProcedure

Procedure RefreshList()
  Protected s.s, i.i, *Item.AudioItem
  ClearGadgetItems(PlayList)
  ForEach PlayList()
    *Item = PlayList()
    s.s = *Item\title + #LF$
    s   + *Item\album + #LF$
    s   + FormatDate("%hh:%ii:%ss", *Item\duration) + #LF$
    s   + GetFilePart(*Item\fileName) + #LF$
    s   + FormatNumber(*Item\fileSize * 1e-6, 2) + #LF$
    AddGadgetItem(Playlist, i, s)
    SetGadgetItemData(Playlist, i, *Item)
    i + 1
  Next
  SetGadgetState(Playlist, -1)
EndProcedure

Procedure readDirectory(folderPath$) ;<---

  If ExamineDirectory(0, folderPath$, "*.mp3")
    
    While NextDirectoryEntry(0)
      If DirectoryEntryType(0) = #PB_DirectoryEntry_File
        AddAudio(PlayList(), folderPath$ +DirectoryEntryName(0), #False)
        
      EndIf
    Wend
    
    FinishDirectory(0)
    RefreshList()
  EndIf
  
EndProcedure

Define.i i, Event, FileCount, Files.s, *Item.AudioItem, addDirectory, clearList


OpenWindow(0, 0, 0, 826, 550, "Mp3 Player 0.1", #PB_Window_SystemMenu | #PB_Window_MinimizeGadget | #PB_Window_MaximizeGadget | #PB_Window_ScreenCentered | #PB_Window_WindowCentered)
SetWindowColor(0, RGB(213,213,213))

ExplorerListGadget(1, 0, 0, 200, 520, GetHomeDirectory() + "Music/", #PB_Explorer_BorderLess | #PB_Explorer_AlwaysShowSelection)

PlayList =ListIconGadget(#PB_Any, 200, 20, 625, 500, "Title", 130, #PB_ListIcon_GridLines | #PB_ListIcon_FullRowSelect | #PB_ListIcon_AlwaysShowSelection)
EnableGadgetDrop(PlayList, #PB_Drop_Files, #PB_Drag_Copy)
AddGadgetColumn(PlayList, 1, "Album", 160)
AddGadgetColumn(PlayList, 2, "Duration", 80)
AddGadgetColumn(PlayList, 3, "FileName", 160)
AddGadgetColumn(PlayList, 4, "FileSize (MB)", 80)
BindEvent(#PB_Event_Gadget, @ItemSelectedCallback(), 0, PlayList, #PB_EventType_LeftClick)

AddAudio(PlayList(), GetHomeDirectory() + "Music/", #True)
RefreshList()

; ImageGadget(2, 5, 525, 29, 23, ImageID(Img0))
; GadgetToolTip(2, "Backward")
; 
; ImageGadget(3, 34, 525, 27, 23, ImageID(Img1))
; GadgetToolTip(3, "Play/Pause")
; 
; ImageGadget(4, 60, 525, 29, 23, ImageID(Img2))
; GadgetToolTip(4, "Forward")
; 
; ImageGadget(5, 93, 525, 29, 23, ImageID(Img3))
; GadgetToolTip(5, "Stop")
; 
; ImageGadget(6, 127, 525, 29, 23, ImageID(Img4))
; GadgetToolTip(6, "Show/Hide Playlits")

addDirectory =ButtonGadget(#PB_Any, 150, 525, 55, 25, "add")
clearList  =ButtonGadget(#PB_Any, 215, 525, 55, 25, "clear")

progressBar =ProgressBarGadget(#PB_Any, 280, 530, 470, 15, 0, 100)
ContainerGadget(8, 200, 0, 625, 20, #PB_Container_Flat)
SetGadgetColor(8, #PB_Gadget_FrontColor,RGB(94,94,94))

Procedure setTime()
  SetGadgetState(progressBar, PeekD(@time) )
EndProcedure

BindEvent(#ProgressBar, @setTime())

Repeat
  Event = WaitWindowEvent()
Select Event
  Case  #PB_Event_GadgetDrop
    Files = EventDropFiles()
    FileCount = CountString(files, #LF$) + 1
    For i = 1 To FileCount
      AddAudio(PlayList(), StringField(Files, i, #LF$), #False)
    Next
    RefreshList()
  Case #PB_Event_Gadget
    Select EventGadget()
      Case addDirectory
        readDirectory( GetGadgetText(1) )
      Case clearList
        ClearList(PlayList())
        RefreshList()
    EndSelect

    
  EndSelect
Until Event = #PB_Event_CloseWindow

If NSSound
  CocoaMessage(0, NSSound, "stop")
  CocoaMessage(0, NSSound, "release")
EndIf
macOS Catalina 10.15.7
Post Reply