Page 1 of 1

Progress bar

Posted: Thu Jul 20, 2017 11:04 am
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

Re: Progress bar

Posted: Thu Jul 20, 2017 6:31 pm
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 ;-)

Re: Progress bar

Posted: Thu Jul 20, 2017 7:39 pm
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

Re: Progress bar

Posted: Thu Jul 20, 2017 7:46 pm
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).

Re: Progress bar

Posted: Thu Jul 20, 2017 8:34 pm
by codeit
Would it be possible to show me how to use currentTime and duration to give the required progress bar value
Thanks

Re: Progress bar

Posted: Thu Jul 20, 2017 10:40 pm
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