albertz / music-player

Music player - endlessly plays your music
http://albertz.github.io/music-player/
BSD 2-Clause "Simplified" License
489 stars 58 forks source link

do you have any interest in collaborating on the backend? #42

Open andrewrk opened 10 years ago

andrewrk commented 10 years ago

I started a project which is meant to be generic music player backend library, written in pure C: https://github.com/superjoe30/libgroove

It's still a young project, but it already works quite well.

It looks like the backend for albertz/music-player and libgroove have much in common. So much, in fact, that I wonder if we might benefit from merging backends.

For me, it would look like gaining a collaborator/maintainer for libgroove. I'm sure your expertise would improve the library's quality. This is obviously a benefit for me.

For you, it would be some work to extract your backend, identify missing features in libgroove (which I would happily implement), and transition to using a different API your new backend. That's a con. On the pro side, you'd gain an extra maintainer on your backend (me). You'd be sharing backends, so the burden of maintenance is halved. The idea is that long term, this is more sustainable. As an added bonus, the boost in quality and usage to libgroove would increase the chances that it would get used for additional projects, increasing the "eyeball count" in a snowball effect.

If you're interested, have a look at src/groove.h and the examples to get a feel for the API so far.

There's also a demo Qt app which is a thin UI on top of libgroove.

libgroove is meant to be minimal and generic enough that you could built albertz/music-player on top of it with no problem.

What do you think?

albertz commented 10 years ago

Ah, this is what I thought in your closing comment in #7, i.e. how your project really differs from my project.

In my project, the backend is already extracted - it was always meant to be kept as a separate piece.

I'm a bit limited on time right now, so I only stumbled roughly through libgroove. The overall design looks quite different.

E.g., in musicplayer, the playlist is not handled by the player engine. Rather, the player engine gets a reference to an playlist-like-interface which just tells how to peek or receive the next N songs. I.e., it is more like a lazy infinite generator, not a list. It also must be infinite by definition. The playlist itself is handled in another part of the whole thing. This is a quite important thing because it was one of the main reasons I started the whole project - to be very flexible in the way I could handle the list of songs.

Otherwise, libgroove is not really more low level than the player engine in musicplayer, so I don't see how I could built the player engine on top of libgroove. The other way around, i.e. make libgroove base on the player engine of musicplayer, might make more sense, although it means that most of libgroove becomes obsolete. The only part which libgroove does more is that it manages the playlist.

Otherwise, I guess the musicplayer-playercore does more and is meant to be a bit more automatic, e.g. all the replaygain handling is done automatically under the hood. Right now, there is only a single flag to enable/disable it. In your lib, it looks like this has all done manually (by some other part of a player).

Despite that, there are a few more things done in the musicplayer-playercore:

Btw., I saw that you used SDL for audio output. I thought about different libraries for output but I found SDL too limited, e.g. for having more than 2 channels output. I have prepared my code for it, although it is not ready for it yet.

I haven't really figured out where you do the resampling. Is that done by the ffmpeg filter stuff? I'm using libswresample of FFmpeg.

andrewrk commented 10 years ago

Thanks for taking the time to respond.

It also must be infinite by definition. The playlist itself is handled in another part of the whole thing. This is a quite important thing because it was one of the main reasons I started the whole project - to be very flexible in the way I could handle the list of songs.

I agree that this is important - it's very similar to one of the features of the music player app that I want to build on top of libgroove.

The only reason that libgroove has a playlist is for gapless playback. When you add items to the list, they are opened and ready to be decoded. In a music player app on top of libgroove, there would not be a 1:1 correlation between libgroove playlist items and the music player's playlist. Realistically you might only have 2 items in the libgroove playlist at one time; you can use it as a swap buffer type thing. However, it still supports N items because it makes the API more consistent and clear.

The only part which libgroove does more is that it manages the playlist.

To be clear, libgroove does not manage the playlist. The API client manages libgroove's playlist, and the only thing libgroove manages is a pointer to the currently playing item.

Otherwise, I guess the musicplayer-playercore does more and is meant to be a bit more automatic, e.g. all the replaygain handling is done automatically under the hood. Right now, there is only a single flag to enable/disable it. In your lib, it looks like this has all done manually (by some other part of a player).

I'd love for libgroove to be able to handle all this under the hood automatically, but I reasoned:

  1. Album replaygain is sometimes desirable.
  2. To know whether you want album or track replaygain, you must have access to the entire playlist, which libgroove does not have (remember the swap buffer use case)
  3. In order to perform album replaygain scanning one must have access to the entire album, and hence the music library. libgroove does not do anything with a music library.
  4. A music player app might want to override the values from tags.

Hence, libgroove should allow the API client to decide, for each playlist item, whether replaygain is in track, album, or off mode, and even allow the API client to override the replaygain values from tags (there are cases where a music file has no metadata support but information could be saved in the music player's db).

libgroove does, however, provide replaygain scanning support, so the music player app's job is reduced to scheduling scan jobs.

How does albertz/music-player handle album replaygain?

Despite that, there are a few more things done in the musicplayer-playercore:

  • AcoustID could be added to replaygain scanning. Instead of being called a "replaygain scan" we can call it a "scan" and do both.
  • It makes sense to also add the ability to access raw buffers via the API when scanning. This way you could generate your visual preview of the song however you wanted. In addition, it is planned to be able to access raw buffers during playback, so you could add some kind of live visualization.
  • Gapless playback is handled already. In fact, it's the only mode currently supported. Crossfading is not yet implemented.

Btw., I saw that you used SDL for audio output. I thought about different libraries for output but I found SDL too limited, e.g. for having more than 2 channels output. I have prepared my code for it, although it is not ready for it yet.

SDL was the easiest thing to do for now; in the future libgroove might use something different, for exactly the same reasons as you say. What does albertz/music-player use currently?

I haven't really figured out where you do the resampling. Is that done by the ffmpeg filter stuff? I'm using libswresample of FFmpeg.

libgroove uses libswresample of ffmpeg as well, indirectly. Well, first of all libgroove currently uses the libav fork, but might switch to ffmpeg. It doesn't make too much of a difference. A filter chain is set up with libavfilter, and the "format" filter under the hood uses libswresample.

Based on this feedback so far, I think that libgroove is lacking some critical API that albertz/music-player needs, but I still think that architecture-wise, it could still fit comfortably as the backend.