Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.
Very cool :)

P.S. Thank you for teaching me a little bit about Mac windowing, albeit indirectly :D This was a fun challenge to implement ... not trivial, but fun ;)

(I have also begun to look at energy usage.)
 
Last edited:
I'm adding a few more refinements to the UI:

1 - Time remaining is now shown, in addition to the time elapsed, for the playing track
2 - Playlist now has an index column that shows an animation for the row containing the currently playing track
3 - Player controls are much more compact and the enclosing controls box has been shrunk accordingly
4 - Playlist rows are not selected, unless requested by the user (i.e. empty selection is now the default)

PlayingGIF.gif
 
  • Like
Reactions: PBG4 Dude
I'm modifying the look n feel of the effects panel tab view (see button that says "EQ", and other adjacent buttons). Enough of the boring old white rectangles.

NewAural.png

[doublepost=1508059720][/doublepost]And this is what it now looks like, for tracks that don't have their own artwork

ShortDemo2.gif
 
  • Like
Reactions: sartrekid
Just wanted to tell you that this project is amazing. Really love what you've done there.
So instantly forked this and also did a pull request for swift 4 conversion.

Keep up this great work!
 
  • Like
Reactions: 0002378
Just wanted to tell you that this project is amazing. Really love what you've done there.
So instantly forked this and also did a pull request for swift 4 conversion.

Keep up this great work!

Thanks very much for the great feedback. Glad you like it. It's messages like yours that give me the motivation to keep this project going.

I'm relatively new to GitHub, so I don't understand what exactly a pull request is. I did read up on it, and I'm assuming it just means that you're going to be working on it separately ? It's more of a notification than a request, right ?

EDIT - I saw your pull request, and have uploaded the missing GIFs under /Resources. Sorry about that ! Thanks for the great work with Swift 4 !
 
Last edited:
Thanks very much for the great feedback. Glad you like it. It's messages like yours that give me the motivation to keep this project going.

I'm relatively new to GitHub, so I don't understand what exactly a pull request is. I did read up on it, and I'm assuming it just means that you're going to be working on it separately ? It's more of a notification than a request, right ?

Hey, I'm also quite new to this stuff. Actually it's my first pull request I ever did, but I also read about how git and stuff works.

Basically it's a request to merge changes into the origin. In this case you have the ability to review all those changes and decide on each whether to commit it to your own repo or not. So instead of you committing your own changes it's like you committing someone elses changes with the ability to review them beforehand (and do stuff like follow up commits). Please someone correct me if I'm wrong.
 
Wow, the UI is not very Mac'ish. Looks like Winamp from the 90s. :eek:

Haha, yes, this project was inspired by Winamp :) It is it's doppelganger in the Mac world, since there aren't any (or many) decent substitutes.

When I started using Mac, many years ago, my first aim was to find a Winamp substitute that worked just as simply and effectively. 8 years later, having still not found one, I tried to write one :)
[doublepost=1508181489][/doublepost]
Hey, I'm also quite new to this stuff. Actually it's my first pull request I ever did, but I also read about how git and stuff works.

Basically it's a request to merge changes into the origin. In this case you have the ability to review all those changes and decide on each whether to commit it to your own repo or not. So instead of you committing your own changes it's like you committing someone elses changes with the ability to review them beforehand (and do stuff like follow up commits). Please someone correct me if I'm wrong.

Ah ok. So, I wanted to clarify something, then.

I would like the master branch to remain written in Swift 3, for now, because I plan to continue development on that branch in Swift 3, till perhaps, at a later point, I decide to move to Swift 4 myself.

But, I'm definitely interested in your changes, so I want to encourage you to keep working in Swift 4 ! And, hopefully, we can find a way to somehow take advantage of each other's new changes.

That said, I could either create a new Swift4 branch under my repo and have you merge stuff there as you continue working on it, or you could just continue developing under your repo, and someday, when Swift3 development is retired, someone could pull / merge everything over from your branch and make it the master (or your repo could become the new master) ?

I've never collaborated with anyone on GitHub like this before. This is also my very first open source project, so I'm kind of clueless. What do you think ?
 
Last edited:
Part of me thinks you should have one codebase but another thinks you should keep them separate, as different people maintaining the different code bases could leak to features on one version that wouldn’t be on the other.
 
Part of me thinks you should have one codebase but another thinks you should keep them separate, as different people maintaining the different code bases could leak to features on one version that wouldn’t be on the other.

Right. Having one codebase, in my experience (from the corporate world), requires heavy collaboration, and that can be a challenge, unless people are committed to working together on a well co-ordinated schedule to resolve merge issues or bugs and such. This difficulty can be compounded by geographic separation or time zone differences.

In other words, I (and likely also the other developer) probably don't have the motivation to commit to that kind of heavy collaboration. That can really take the fun out of developing :)

It is probably best for us to develop separately, and keep a casual eye on each other's work to see if there's something worth merging together into one melting pot that contains the best of both worlds.

Ok, so I will create a Swift4 branch under my repo. Perhaps that can serve as a melting pot, which will contain both my changes from master and Tobias Dunkel's changes from his repo.
 
Hey, I'm also quite new to this stuff. Actually it's my first pull request I ever did, but I also read about how git and stuff works.

Basically it's a request to merge changes into the origin. In this case you have the ability to review all those changes and decide on each whether to commit it to your own repo or not. So instead of you committing your own changes it's like you committing someone elses changes with the ability to review them beforehand (and do stuff like follow up commits). Please someone correct me if I'm wrong.

Tobias, if you have a moment, and if you are able to get Aural running on your machine, I would really appreciate it if you could post a screenshot or two of how the app renders. I have not been able to test my app on any machines other than mine, and would love to see how it looks on other systems. Thanks !
 
Hey, did some UI changes on a test branch I made to experiment a bit. Went for a slightly more native feel, while keeping the overall look and feel. Also completely redid autolayout which took like forever, but now UI changes should be easier. Only got like one two things that still don't look and work as intended (for example the EQ Sliders, or the resizing of the main window when hiding the EQ), but other than that it works fine without crashes and all with Swift 4.

screenshot_52.png


So, looking for some feedback on this. It has system integrated blurry background instead of normal transparency, which can also be turned of by system preferences.

Regarding different branches in my opinion a single codebase would be best, but this is up to you. New features that only change specific parts of existing code or mostly add new stuff to it can be developed on side-branches and once completely developed and tested to be stable they can easily be merged. This way multiple people can work together on the same product. But after all, this is totally up to you, I'm fine with whatever your suggestion is on that topic.

Here's the original version rendered on my Macbook as requested.
screenshot_55.png
 
  • Like
Reactions: 0002378
Hey, did some UI changes on a test branch I made to experiment a bit. Went for a slightly more native feel, while keeping the overall look and feel. Also completely redid autolayout which took like forever, but now UI changes should be easier. Only got like one two things that still don't look and work as intended (for example the EQ Sliders, or the resizing of the main window when hiding the EQ), but other than that it works fine without crashes and all with Swift 4.

View attachment 725794

So, looking for some feedback on this. It has system integrated blurry background instead of normal transparency, which can also be turned of by system preferences.

Regarding different branches in my opinion a single codebase would be best, but this is up to you. New features that only change specific parts of existing code or mostly add new stuff to it can be developed on side-branches and once completely developed and tested to be stable they can easily be merged. This way multiple people can work together on the same product. But after all, this is totally up to you, I'm fine with whatever your suggestion is on that topic.

Here's the original version rendered on my Macbook as requested.
View attachment 725796

Wow, so far, so great ! I definitely like your making it look more native. I also like that you took the playlist controls (at the very bottom) out of the black playlist box ... I had been wanting to do that myself. Great stuff !

I just have one concern ... you've added window controls (the red and green circles) to the playlist window. This might make it confusing to the end user, because the playlist window is intended to be hidden/closed through the "Show/Hide playlist" function, which has a button at the top right corner of the main window, and also a menu item under the "View" menu. And, for maximizing, there are the 3 buttons on the left side of the playlist window (the 3 white square-ish buttons).

Did you intend to add those native red/green window controls ? I just feel that it might confuse the user because now, there are two ways of closing/maximizing the playlist window. Just my opinion.

I also see that, on the main window, you've removed the custom close/hide icons and replaced them with the native red/green window controls. This, I think, is fine. Whether it's native or custom doesn't matter too much. I just wanted to make it look a bit cooler. But, either way is fine.

Otherwise, I love your changes. You've definitely spiced up the UI a bit, which it was in need of. I'm not all that imaginative with UI (being a back end developer most of my career).

About the single codebase, yeah, I'd like to keep them separate (Swift 3 and 4) just for now. I think it makes sense to have both: 1 - A stable Swift 3 codebase, with a lot of online support for Swift 3, and 2 - An experimental cutting-edge Swift 4 codebase that leverages the latest/greatest that Swift has to offer.

And, then, merge the two once in a while. You'll find that, at this point, when you look stuff up online (and if you're still learning Swift, you'll surely need to look stuff up often), you will have trouble finding Swift 4 answers ... on StackOverflow for instance. I had this problem when developing with Swift 2. Almost all Swift docs online are for Swift 3. This is why it makes sense to keep a Swift 3 branch alive, but also have that new experimental Swift 4 branch in parallel.

Of course, I might switch to Swift 4 once I find that there's not much else I can think to add. (I'm still adding some features/refinements as we speak) I might one day get bored and say let's do it ! At that point, we can have the whole thing in Swift 4.

Great work ! You're making awesome progress, and I'm eager to see what else you have in store. Keep it up, and feel free to post screenshots here on this thread whenever you want to share something.

Thanks for the screenshots ! It renders fine, it looks like.
 
Last edited:
Hey, thanks for your detailed answer. Now that you explained why, it makes sense to have Swift 3 and Swift 4 version separate. Still, I will try to keep up with the changes that you're doing in Swift 3 and try to bring them up to Swift 4. I'm an update junkie and always have to have the latest and greatest. For me, having to figure things out on my own is also part of the deal, and it's a thing I really like.

Regarding the window controls I'm just experimenting, they are gone as easy as two clicks, so I didn't really care. But a native close button for the playlist makes total sense in my opinion. How would it make things more complicated, when using native controls? Also it's often quicker in terms of mouse movement when having a dedicated close button at the playlist window itself.

Another thing i maybe want to change in terms of UX is having the playlist resize and docking controls on the main window. Besides that there's more things I just wanna try and play with (like media keys, menu bar icon...), knowing I can always go back to how it was.

Yeah, I'm also very interested in what you're doing on your side. It is already my favorite music player on mac xD and I found out about it not even two days ago. Haven't even really dug deep into the code and already learned so much from your work.

Oh, and just want to point out that somehow your custom minimize button does not work for me. Nothing happens when I press it.
 
  • Like
Reactions: 0002378
Hey, thanks for your detailed answer. Now that you explained why, it makes sense to have Swift 3 and Swift 4 version separate. Still, I will try to keep up with the changes that you're doing in Swift 3 and try to bring them up to Swift 4. I'm an update junkie and always have to have the latest and greatest. For me, having to figure things out on my own is also part of the deal, and it's a thing I really like.

Regarding the window controls I'm just experimenting, they are gone as easy as two clicks, so I didn't really care. But a native close button for the playlist makes total sense in my opinion. How would it make things more complicated, when using native controls? Also it's often quicker in terms of mouse movement when having a dedicated close button at the playlist window itself.

Another thing i maybe want to change in terms of UX is having the playlist resize and docking controls on the main window. Besides that there's more things I just wanna try and play with (like media keys, menu bar icon...), knowing I can always go back to how it was.

Yeah, I'm also very interested in what you're doing on your side. It is already my favorite music player on mac xD and I found out about it not even two days ago. Haven't even really dug deep into the code and already learned so much from your work.

Oh, and just want to point out that somehow your custom minimize button does not work for me. Nothing happens when I press it.

You're a very fast learner !

Yes, I'm very much open to doing things a different way. Feel free to experiment with moving the window controls around as you wish ... as long as they're intuitive to the end user.

What I was trying to say about the playlist window controls was that the 3 custom resize buttons behave differently than the native green maximize control. So, this represents a disparity or inconsistency. Consistency is good from a UX perspective. When the user clicks a button, he/she should know exactly what it is going to do. If the custom maximize button expands the window only vertically and the native control expands it both vertically and horizontally, that is inconsistent behavior ... not a good user experience.

Take a look in WindowViewController (or better yet, just run the app and use the playlist maximize controls) ... the custom maximize button does not behave the same way as the native maximize button. The custom button will expand the playlist window either to the left/right of the main app window, or above or below it (depending on the relative locations of both windows), so that both are always visible and do not overlap. This is the intended maximize behavior. On the other hand, the native control does not know or care about the main app window and will maximize it over the entire screen. This is not really the intended behavior.

Also, I don't know if the OS controls will do something weird with the window, i.e. unexpected behavior, in some cases that we have not foreseen. By allowing control only through custom controls, you take the uncertainty out of the equation, and everything behaves in very predictable and consistent ways as you have programmed it. That's why I didn't like the playlist window having native controls.

But again, experiment away, and if you find that the new layout of controls is intuitive and behaves predictably, then, that's fine too. I'm open to new ways of doing things ... absolutely.

At the moment, I'm working on playlist drag/drop re-ordering. It is quite involved, as it turns out ! Another thing I'm looking to add is new playlist views that will group the tracks by album/artist/genre, etc, like iTunes does. I may or may not do it. The playlist could have a tab group, one for each grouped view, plus the default flat view.

BTW, my GitHub page has a "Planned Updates" section on the main page. If you're ever wondering what I'm up to, that's a great place to check :) Just FYI.

The custom minimize button calls miniaturize() which is supposed to hide/minimize the window. Weird. Maybe Swift 4 has a new way of doing that.
[doublepost=1508220218][/doublepost]

------------------------------------------------------------------------------------------

Since my developer-readme file is totally outdated at this point, and since you're actively working on the code, I just wanted to give you a few pointers into my code (although I'm aware that you like to figure things out on your own). I will also update developer-readme soon.

App layers: The app definitely implements the MVC design pattern, but I like to call them "View", "Delegate", and "Back end"

The view layer consists of several ViewController classes, one for each logical component of the app (not necessarily physical component, but logical component). For instance, all playback-related functions are handled by PlaybackViewController. And, there are several custom views, e.g. XXXSliderCell, that customize the look and feel of UI controls.

PlaylistTableViewController: This class is so important it deserves a special mention. This class provides the data for the playlist table view. It also handles drag/drop operations.

The delegate layer takes requests from the view layer, and marshals them into formats the back end can use. It does things like conversion of values from a UI format to the audio engine's format, and formatting of back end values into UI-friendly formats. E.g. volume in the UI ranges from 0-100, but the engine requires 0-1. Generally, the delegate layer provides the view layer with a simple interface for accessing the audio engine in the back end, and provides app-level operations that then get translated into audio-engine-level operations. The delegate layer consists of several protocols, that are named XXXDelegateProtocol, which provide the contracts, and of course their concrete implementations. One rule that is always followed is that the view layer never accesses the back end directly; it must always only interact with the delegate layer.

Finally, the back end, also accessed through protocols, consists of 3 main components:

1 - Audio Graph: centers around the AVAudioEngine framework. The "audio graph" refers to a graph of nodes that perform the necessary signal processing (playback, effects, mixing, etc) that sends the sound output to your audio device. The main classes here are the AudioGraph, Player, and Recorder. BufferManager does all the dirty work of scheduling audio buffers for Player.

2 - Playlist: CRUD for tracks. Also performs search/sort functions.

3 - PlaybackSequence: Contains logic for repeat and shuffle. Decides which track is going to play next.

As much as possible, code is accessed through protocols, not directly through concrete implementation classes, which is a fundamental OOP principle.

Example call chain: As an example, when the user changes the volume using the volume slider, control flows as follows: AudioGraphViewController -> AudioGraphDelegate (through AudioGraphDelegateProtocol) -> AudioGraph (through AudioGraphProtocol) -> AVAudioPlayerNode.setVolume().

View -> Delegate -> Back end

Another example: When a track is removed from the playlist, the following happens:

PlaylistViewController -> PlaylistDelegate -> PlaylistMutatorDelegate -> Playlist.removeTracks() (through PlaylistCRUDProtocol)

Object Graph: Constructs/initializes all the delegate/back end objects necessary.

AppState: Class that encapsulates all persistent app state. When you exit the app, this object is persisted to a json file, and loaded back upon startup.

Preferences: User preferences. Uses the built-in system-provided UserDefaults mechanism.

Messaging: A very important part of the app is messaging, both synchronous and asynchronous. Often, one app component does its job and needs to tell other app components that it has done something, so that the other component may do something else in response. But, it has to do this without tight coupling between the two components. For instance, when a playlist track is removed from the playlist, if the removed track was playing, the playlist view needs to tell the playback controller to stop playing the track, so it will send out a message.

Another example - When the app is about to exit, the app delegate will send out an "exiting" message. If a recording is ongoing, the recorder unit will respond with "Wait ! Don't exit, let me prompt the user to save his recording".

The two main files that hold message definitions are SyncMessages.swift and AsyncMessages.swift.

IO and Utils: The IO and Utils source groups (folders within the project) contain a set of utility classes for performing disk IO and other functions like formatting strings, performing UI computations, concurrency, file system computations, etc.

MetadataSpecs: ID3 and ITunes metadata specifications which map format-specific tags like TLEN to user-friendly descriptions like "Duration"

Track lazy loading: One important thing to note is that track info is lazily loaded for optimal performance. When you add files to the playlist, no info whatsoever is read from the track. Then, other info (duration, artist, title, art) are read asynchronously, and the playlist updated. Finally, when you actually play the track, or prepare the track for playback (which is done eagerly), it's audio track is actually read. TrackIO is the class that handles the loading of track info.

Hope this helps !
 
Last edited:
  • Like
Reactions: Tobias_Dunkel
Haha, yes, this project was inspired by Winamp :) It is it's doppelganger in the Mac world, since there aren't any (or many) decent substitutes.

When I started using Mac, many years ago, my first aim was to find a Winamp substitute that worked just as simply and effectively. 8 years later, having still not found one, I tried to write one :)
It makes my eyes bleed so I won't use it. But kudos to you! So many times I've been in a situation where I can't find an app for my needs, but unfortunately I never learned how to code.
 
You're a very fast learner !

Yes, I'm very much open to doing things a different way. Feel free to experiment with moving the window controls around as you wish ... as long as they're intuitive to the end user.

What I was trying to say about the playlist window controls was that the 3 custom resize buttons behave differently than the native green maximize control. So, this represents a disparity or inconsistency. Consistency is good from a UX perspective. When the user clicks a button, he/she should know exactly what it is going to do. If the custom maximize button expands the window only vertically and the native control expands it both vertically and horizontally, that is inconsistent behavior ... not a good user experience.

Take a look in WindowViewController (or better yet, just run the app and use the playlist maximize controls) ... the custom maximize button does not behave the same way as the native maximize button. The custom button will expand the playlist window either to the left/right of the main app window, or above or below it (depending on the relative locations of both windows), so that both are always visible and do not overlap. This is the intended maximize behavior. On the other hand, the native control does not know or care about the main app window and will maximize it over the entire screen. This is not really the intended behavior.

Also, I don't know if the OS controls will do something weird with the window, i.e. unexpected behavior, in some cases that we have not foreseen. By allowing control only through custom controls, you take the uncertainty out of the equation, and everything behaves in very predictable and consistent ways as you have programmed it. That's why I didn't like the playlist window having native controls.

But again, experiment away, and if you find that the new layout of controls is intuitive and behaves predictably, then, that's fine too. I'm open to new ways of doing things ... absolutely.

At the moment, I'm working on playlist drag/drop re-ordering. It is quite involved, as it turns out ! Another thing I'm looking to add is new playlist views that will group the tracks by album/artist/genre, etc, like iTunes does. I may or may not do it. The playlist could have a tab group, one for each grouped view, plus the default flat view.

BTW, my GitHub page has a "Planned Updates" section on the main page. If you're ever wondering what I'm up to, that's a great place to check :) Just FYI.

The custom minimize button calls miniaturize() which is supposed to hide/minimize the window. Weird. Maybe Swift 4 has a new way of doing that.
[doublepost=1508220218][/doublepost]

------------------------------------------------------------------------------------------

Since my developer-readme file is totally outdated at this point, and since you're actively working on the code, I just wanted to give you a few pointers into my code (although I'm aware that you like to figure things out on your own). I will also update developer-readme soon.

App layers: The app definitely implements the MVC design pattern, but I like to call them "View", "Delegate", and "Back end"

The view layer consists of several ViewController classes, one for each logical component of the app (not necessarily physical component, but logical component). For instance, all playback-related functions are handled by PlaybackViewController. And, there are several custom views, e.g. XXXSliderCell, that customize the look and feel of UI controls.

PlaylistTableViewController: This class is so important it deserves a special mention. This class provides the data for the playlist table view. It also handles drag/drop operations.

The delegate layer takes requests from the view layer, and marshals them into formats the back end can use. It does things like conversion of values from a UI format to the audio engine's format, and formatting of back end values into UI-friendly formats. E.g. volume in the UI ranges from 0-100, but the engine requires 0-1. Generally, the delegate layer provides the view layer with a simple interface for accessing the audio engine in the back end, and provides app-level operations that then get translated into audio-engine-level operations. The delegate layer consists of several protocols, that are named XXXDelegateProtocol, which provide the contracts, and of course their concrete implementations. One rule that is always followed is that the view layer never accesses the back end directly; it must always only interact with the delegate layer.

Finally, the back end, also accessed through protocols, consists of 3 main components:

1 - Audio Graph: centers around the AVAudioEngine framework. The "audio graph" refers to a graph of nodes that perform the necessary signal processing (playback, effects, mixing, etc) that sends the sound output to your audio device. The main classes here are the AudioGraph, Player, and Recorder. BufferManager does all the dirty work of scheduling audio buffers for Player.

2 - Playlist: CRUD for tracks. Also performs search/sort functions.

3 - PlaybackSequence: Contains logic for repeat and shuffle. Decides which track is going to play next.

As much as possible, code is accessed through protocols, not directly through concrete implementation classes, which is a fundamental OOP principle.

Example call chain: As an example, when the user changes the volume using the volume slider, control flows as follows: AudioGraphViewController -> AudioGraphDelegate (through AudioGraphDelegateProtocol) -> AudioGraph (through AudioGraphProtocol) -> AVAudioPlayerNode.setVolume().

View -> Delegate -> Back end

Another example: When a track is removed from the playlist, the following happens:

PlaylistViewController -> PlaylistDelegate -> PlaylistMutatorDelegate -> Playlist.removeTracks() (through PlaylistCRUDProtocol)

Object Graph: Constructs/initializes all the delegate/back end objects necessary.

AppState: Class that encapsulates all persistent app state. When you exit the app, this object is persisted to a json file, and loaded back upon startup.

Preferences: User preferences. Uses the built-in system-provided UserDefaults mechanism.

Messaging: A very important part of the app is messaging, both synchronous and asynchronous. Often, one app component does its job and needs to tell other app components that it has done something, so that the other component may do something else in response. But, it has to do this without tight coupling between the two components. For instance, when a playlist track is removed from the playlist, if the removed track was playing, the playlist view needs to tell the playback controller to stop playing the track, so it will send out a message.

Another example - When the app is about to exit, the app delegate will send out an "exiting" message. If a recording is ongoing, the recorder unit will respond with "Wait ! Don't exit, let me prompt the user to save his recording".

The two main files that hold message definitions are SyncMessages.swift and AsyncMessages.swift.

IO and Utils: The IO and Utils source groups (folders within the project) contain a set of utility classes for performing disk IO and other functions like formatting strings, performing UI computations, concurrency, file system computations, etc.

MetadataSpecs: ID3 and ITunes metadata specifications which map format-specific tags like TLEN to user-friendly descriptions like "Duration"

Track lazy loading: One important thing to note is that track info is lazily loaded for optimal performance. When you add files to the playlist, no info whatsoever is read from the track. Then, other info (duration, artist, title, art) are read asynchronously, and the playlist updated. Finally, when you actually play the track, or prepare the track for playback (which is done eagerly), it's audio track is actually read. TrackIO is the class that handles the loading of track info.

Hope this helps !

Wow, thanks for the detailed explanation of the code structure, this really helps. I'm kinda used to a different structure, more like the media player called IINA has. (https://github.com/lhc70000/iina)

Especially the approach on structuring the UI is better on that, in my opinion. There every Window gets its own xib file and Controller, plus they all are in the Views folder, without having so many subfolders. Also having the interface elements external can be really useful. You can have the main window which sets the overall layout and consists of just placeholder. Those placeholder can then easily be populated with interface elements. Which means stuff is way more flexible and easier to handle. This would also open the possibility for a adaptive layout, or a single window mode, with playlist in the main window. Will do some more research in that direction and give more feedback soon.

Other than that, I have made some progress fine tuning the UI and fixing some bugs I introduced.

Only problem I have is, when first opening the application the playlist window overlaps slightly the main window. When pressing the 'dock to bottm' button it somehow works just fine. I wonder what's the difference here on launch?

Also i want the app be able to reopen it's main window after clicking the dock icon, which somehow doesn't work and i couldn't figure out why, since I implemented the correct method in the AppDelegate.

Another thing i changed was removing all the .gif usage. These introduced a hugh amount of CPU load, like without animated gifs i get less than 4% CPU on idle, compared to over 20% with animated gifs.

That's it for now. If you want to try out my latest version, you can find it on my GitHub under the 'test' branch.
[doublepost=1508302886][/doublepost]
It makes my eyes bleed so I won't use it. But kudos to you! So many times I've been in a situation where I can't find an app for my needs, but unfortunately I never learned how to code.

Well i think the app looks good as it is. It's just the close button that looks outdated.
But if you want a more native look you can try out my fork. But expect some bugs and glitches here and there.

On the other hand, if some people would test it and give feedback on what works and what doesn't as well as feedback on how to improve the app, this would help a lot.

You can get a test release here:
https://github.com/Dunkeeel/aural-player/releases/tag/0.1.0

And this is how it looks right now:

screenshot_57.png
 
  • Like
Reactions: 0002378
Only problem I have is, when first opening the application the playlist window overlaps slightly the main window. When pressing the 'dock to bottm' button it somehow works just fine. I wonder what's the difference here on launch?

And this is how it looks right now:

View attachment 725972

Thanks for pointing me to IINA. Will take a look.

About the playlist window overlap, I'm guessing you must have changed the window size without updating the constants declared in UIConstants. Look at UIConstants. If you change window dimensions in Interface Builder, make sure to update these constants as well. There is some duplication/redundancy here, I know, but it leads to the simplest code when dealing with toggling views.

Code:
// Window width (never changes)
    static let windowWidth: CGFloat = 415
    static let minPlaylistWidth: CGFloat = 415
    static let minPlaylistHeight: CGFloat = 150
 
    // Window heights for different views
    static let windowHeight_compact: CGFloat = 208
    static let windowHeight_playlistAndEffects: CGFloat = 381
    static let windowHeight_playlistOnly: CGFloat = 196
    static let windowHeight_effectsOnly: CGFloat = 393

Your test build is looking great !

If you're not going to show the GIF, how about showing a static image of a play button for the playing track row ? Or, just show the row index, if you don't like that idea either.

Another idea: Make it configurable so that the user can enable/disable the animations based on his preferences and system resources ?

EDIT - Yeah, I saw the CPU/Energy spike when showing the GIFs. Good catch ! I think we should make it configurable, because Quad core desktop users may not care about it as much as Macbook users.
 
Last edited:
  • Like
Reactions: Tobias_Dunkel
Regarding the indicator for the playlist I will go for the IINA route and just insert a text symbol like this: ▶︎
Looks good and is easy on the system resources.

Thanks for pointing me to those UIConstants, totally forgot these. But are these really neccessary? I mean the 'dock playlist' functions seem to work with the actual current window size. So when the main window already exists it should dock at the right position by calculating the coordinates.

I would only consider making them configurable when there's a better way to implement them, without increasing CPU load THAT much. Static placeholders should be fine for now IMO and animated ones should have low priority. I found them even a bit annoying after a while. But that's just my opinion :)

EDIT: Forgot to mention, but I'm also in the process of inserting comment marks throughout the whole code, so it's a LOT easier to navigate.
 
Regarding the indicator for the playlist I will go for the IINA route and just insert a text symbol like this: ▶︎
Looks good and is easy on the system resources.

Thanks for pointing me to those UIConstants, totally forgot these. But are these really neccessary? I mean the 'dock playlist' functions seem to work with the actual current window size. So when the main window already exists it should dock at the right position by calculating the coordinates.

I would only consider making them configurable when there's a better way to implement them, without increasing CPU load THAT much. Static placeholders should be fine for now IMO and animated ones should have low priority. I found them even a bit annoying after a while. But that's just my opinion :)

EDIT: Forgot to mention, but I'm also in the process of inserting comment marks throughout the whole code, so it's a LOT easier to navigate.

The static text for the playing track display is great for now. I was surprised at how hard the GIF is on the system ! Wow ! Never suspected that it would be so bad.

The UIConstants are not strictly necessary, but they simplify the code a LOT ! I remember this from when I implemented that piece of code. The code becomes much more concise and readable when it doesn't have to calculate window height everytime something is toggled. Just pick up the constant value and set the height.

Besides, those window heights will never change. For a given view, the main window height will always be X. Only the playlist is resizable by the user. The main window has a fixed set of possible heights (depending on the views shown).

I guess it's a tradeoff - code simplicity vs duplication. I err on the side of simplicity in this case.
 
The static text for the playing track display is great for now. I was surprised at how hard the GIF is on the system ! Wow ! Never suspected that it would be so bad.

The UIConstants are not strictly necessary, but they simplify the code a LOT ! I remember this from when I implemented that piece of code. The code becomes much more concise and readable when it doesn't have to calculate window height everytime something is toggled. Just pick up the constant value and set the height.

Besides, those window heights will never change. For a given view, the main window height will always be X. Only the playlist is resizable by the user. The main window has a fixed set of possible heights (depending on the views shown).

I guess it's a tradeoff - code simplicity vs duplication. I err on the side of simplicity in this case.

Ok, fixed the docking issue. Now it's totally independent to what size the main window has. It just docks like it should even on application launch.

All three dockPlaylist functions got merged into this single one, which takes the type (bottom, left, right) as parameter.
Saves a lot of space this way.

screenshot_58.png


I will also add a gap constant, because i like it better when there's a small gap between the docked windows.
 
Ok, fixed the docking issue. Now it's totally independent to what size the main window has. It just docks like it should even on application launch.

All three dockPlaylist functions got merged into this single one, which takes the type (bottom, left, right) as parameter.
Saves a lot of space this way.

View attachment 726012

I will also add a gap constant, because i like it better when there's a small gap between the docked windows.

Bist du Deutscher ?

This is a good time to tell you that I will possibly be moving onto another project soon ... a writing project. So, I'm trying to wrap up my planned changes soon, so that I can move on.

If I do move on, you will likely be the chief developer for this project, and I will take a backseat :) I will come back to it occasionally, more than likely, but I am anticipating being on that other project almost full-time.
 
Bist du Deutscher ?

This is a good time to tell you that I will possibly be moving onto another project soon ... a writing project. So, I'm trying to wrap up my planned changes soon, so that I can move on.

If I do move on, you will likely be the chief developer for this project, and I will take a backseat :) I will come back to it occasionally, more than likely, but I am anticipating being on that other project almost full-time.

Ja bin ich, du etwa auch?

Oh, sad to hear you're leaving this project. Still looking forward to those changes.
Thanks for entrusting me the development of this app. Yet, I'm not sure if i can handle a project of this size alone. Will take some time for sure until I can get a stable release out there. But i will keep doing test builds until im satisfied to merge them with the Swift 4 branch.

I wish you all the best for your new project, and will be looking forward to hearing from you every now and then.
 
Ja bin ich, du etwa auch?

Oh, sad to hear you're leaving this project. Still looking forward to those changes.
Thanks for entrusting me the development of this app. Yet, I'm not sure if i can handle a project of this size alone. Will take some time for sure until I can get a stable release out there. But i will keep doing test builds until im satisfied to merge them with the Swift 4 branch.

I wish you all the best for your new project, and will be looking forward to hearing from you every now and then.

Nein, ich bin Amerikaner, aber ich liebe alles und jeder Deutsch :)

BTW, there is no pressure for you to finish anything. I just meant that once I take a backseat, you will probably be doing more work on it than me, but it doesn't mean I will be expecting you to do something with it. Do it only if/when you feel like it.

To me, that's the best part about this project - it is all for fun, and there are no corporate deadlines or demands ... no pressure ... work at my own pace and only if I feel like doing so. So, the same applies to you :)

Yesterday, I wrapped up drag/drop reordering and move up/down for multiple tracks (as opposed to only one before). Here's what else I have planned (this is also in the developer-TODOs file that is under /Documentation):

- EASY Show tooltips for playlist tracks whose names have been truncated, when hovering over them
- Add a status label showing number/indexes of tracks selected, show playing track index: 5/12 playing
- BIG - New playlist views with track groups - In addition to the current flat view, new views will be added to the playlist, which group tracks by album, artist, genre, etc.

- Make GIF animations configurable (high CPU/Energy usage)
- Make playlist docking configurable through prefs (default docking location on startup / remember from last app launch ???)

- Figure out high energy usage (reported by forum member robvas)
- Figure out why so many open files/fonts (compare with VOX/iTunes)

- Update user guide / Github readme
- Complete developer readme

Bonus tasks:

BONUS: play/repeat a clip of a file
BONUS: Stop track with fade out
BONUS: Let the user save "bookmarks" - for a given track, save a certain playback position ... useful for large files like audiobooks, or to jump to a favorite part of a track. For ex. "Grimes - Genesis" 1:27.
BONUS: Visualization

I may not finish all of it, but ...

I wanted to mention a (bonus) task that may be of great interest to you. I spent a lot of time on it earlier, and was able to implement it but wasn't able to get it to work as well as I would want - Visualization (spectrogram). I really wanted to implement this for Aural Player, so I was disheartened when I discovered the following ...

There is a fundamental slowness to Swift (and perhaps with the AVAudioEngine) that makes it impossible to collect (and subsequently examine) audio samples as they're being played, real-time. There is a lag of about 0.5 seconds. This makes the visualization laggy (half a second behind). I read that in order to get it working, it needs to be implemented in C, and with AudioUnit / AUGraph (not AVAudioEngine). I honestly don't know enough about this stuff to get it working, and I haven't had the time/motivation to research it. If you're interested, that could someday be a fun research/implementation project für dich.

I will still be on it for at least a couple more weeks (maybe longer), as I finish up the pending features and update the documentation. Since the UI is changing, I don't want to update the docs now ... coz I'll have to redo the docs when the UI is done changing.
 
Last edited:
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.