GithubHelp home page GithubHelp logo

joshbarrass / deadbeef_gsfdecoder Goto Github PK

View Code? Open in Web Editor NEW
3.0 3.0 0.0 194 KB

GSF Decoder plugin for the DeaDBeeF media player, based on viogsf/VBA-M

License: GNU General Public License v2.0

CMake 1.58% C 74.89% C++ 23.53%
audio-decoder deadbeef-plugin gba gsf hacktoberfest cplusplus cpp

deadbeef_gsfdecoder's Introduction

GSF Decoder

A GSF decoder plugin for DeaDBeeF, based on viogsf/VBA-M.


Building From Source

Ensure that when you clone this repository, you clone recursively to download the necessary submodules. The project can then be built the same way as any other CMake project. To fully clone and build this plugin:

git clone --recursive https://github.com/joshbarrass/deadbeef_GSFdecoder
cd deadbeef_GSFdecoder
mkdir build && cd build
cmake -DCMAKE_BUILD_TYPE=Release ..
make

By defualt, running make install will then install the plugin to /opt/deadbeef/lib/deadbeef (global install). If this is not correct, replace the fourth line above with:

cmake -DCMAKE_INSTALL_PREFIX=<path> -DCMAKE_BUILD_TYPE=Release .. 

where <path> is the target path you want to install to. If you wish to install the plugin for your user only, you can set this to $HOME/.local/lib/deadbeef/. You should be able to run this command at any time without having to completely recompile the plugin.

If you wish to install the plugin manually, simply copy the GSFdecoder.so to one of DeaDBeeF's library paths (probably either /opt/deadbeef/lib/deadbeef or $HOME/.local/lib/deadbeef/).

Enabling Logging

If you are encountering any errors with the plugin, it may be helpful to enable logging to figure out what error, if any, is occurring. Currently, logging can only be toggled at compile time. To do so, add the -DENABLE_LOGGING=ON switch to your cmake command and recompile.

Debug Builds

If you want to compile a debug build, just change the CMake build type to Debug. Be warned, however, that debug builds have considerably more verbose logging, which is ordinarily disabled in a regular build.

Credit

This plugin would not have been possible without extensive reference to audiodecoder.gsf, the GSF decoder for Kodi. This project has been instrumental in understanding the GSF decoding process, particularly how to use the viogsf and psflib libraries that this project depends on.

The viogsf library itself has been extremely useful for developing this plugin, packaging a stripped-down version of VBA-M into a convenient library. This has saved me a significant amount of time, as I have not needed to isolate this from Caitsith2/Zoopd's original player myself.

deadbeef_gsfdecoder's People

Contributors

joshbarrass avatar

Stargazers

 avatar  avatar

Watchers

 avatar  avatar  avatar

deadbeef_gsfdecoder's Issues

Metadata Writing

Metadata is currently read-only. In general this isn't too much of an issue, as most GSFs tend to be tagged, but can be inconvenient if you come across one that isn't tagged. Adding the metadata manually isn't particularly difficult, but it can be tedious, especially if you want to add ReplayGain data. It would be good to implement the proper DeaDBeeF functions for writing metadata back to these files.

Track sometimes plays longer than the length due to over-reading

As there is no hard limit on the number of samples than can be obtained from the emulator, it is possible for more sound data to be generated than there is file length. This is because the read function will always return either as many bytes as was requested, even if that number bytes would exceed the defined length, or will return zero on the next call when the readpos exceeds the length.

To fix this, the number of bytes returned should be reduced if the number of bytes available would exceed the file length, but ONLY if looping is not required.

Can the functions passed to psflib be modified such that the plugin still works for e.g. zip files?

DeaDBeeF has a built-in plugin that allows music to be played directly from a zip file, rather than having to extract that file. This works fine for conventional, self-contained formats, but breaks for this format, I believe due to us being unable to open the psflib file. Is there any way that we could modify the functions we pass to psflib for opening files, etc. to fix this?

I'm not 100% on how this would be done, as I don't fully understand the workings of psflib or DeaDBeeF's file APIs, but I expect it may require us to extract the protocol, directory, etc. manually and have our file opening function prepend this to files that don't specify any. This way, we may be able to modify psflib's request to open the psflib file (e.g. calling open for file "lib.psflib") to add in the necessary bits ourselves before we pass this on to DeaDBeeF. i.e.:

  1. psflib requests we open "lib.psflib"
  2. As there is no directory, protocol, etc., we add this ourselves based on what we know (perhaps we can obtain this from the last URI opened, or could maybe use some higher order function magic to curry this into the function call -- the exact workings of this are to be determined)
  3. This modified URI, e.g. "zipfile:///lib.psflib" is passed to DeaDBeeF's open function, which handles opening of the file as normal.
  4. "Normal" requests (e.g. opening regular files) should not be broken by this. Perhaps we could attempt to open the file without any intervention, and then only modify the URI if this fails? If it then fails again, even after our intervention, we report an error as usual.

If there are any external ideas on whether this would actually work to enable this functionality, suggestions on how this could be implemented, or anything else, let me know. Otherwise I will probably just work on this myself as a low-priority issue when I get a chance.

Logarithmic Fadeout

The current fadeout implementation added with #17 is a linear fadeout, i.e. the amplitude of the sample is reduced to 0 linearly over the fadeout range. However, human perception of volume is non-linear. My understanding is that reducing the amplitude by 10dB should sound, to humans, like halving the loudness. As such, it may sound more natural to implement a logarithmic fadeout. However, this is a little more mathematically-complex than the simple linear fadeout, and does not ever actually reach 0 amplitude, only tends to it asymptotically (at least in theory; that is not strictly true for integer-precision samples).

Nonetheless, I think this would be a cool thing to add. Eventually I think it would be worthwhile to add a config GUI where the fadeout method can be switched based on what each user likes the most.

Global state -> Per file state

Currently, the plugin tracks its state via a global state object (PluginState). Other such plugins (e.g. the built-in PSF plugin) use a state object that stores the built-in DB_fileinfo_t object at the start, and extend this object to store the rest of the plugin's state. The plugin state can then be tracked for each individual file, rather than globally, as the DB_fileinfo_t object is passed to each function and can simply be cast into the PluginState type (example). This is both a more elegant solution, and will hopefully prevents bugs such as #1 and #2.

Documentation of GSF format would be helpful for the future

A lot of what I've learned about the format has been by example, rather than from clear documentation. As far as I know, there isn't a comprehensive documentation of the format, how it works, and how the libraries I've used to decode it work. This would be good information to write down in the wiki somewhere before I forget it all, and could be useful in the future for anyone else who wants to write a decoder for this format.

Fade out

Many GSF files specify a fadeout time. The track length should be extended by this amount, and the volume gradually reduced over this length of time.

Document new compile-time options

Since the initial documentation writing, a number of other compile-time parameters have been added, namely STDERR_DEBUGGING, LOG_FADE, and SAMPLE_RATE. These should be documented for users who want to compile the plugin themselves to enable any of these options.

Segfault when converting a track if the plugin has already initialised

Steps to reproduce:

  1. Add one or more GSF files to the playlist
  2. Begin playing one of the GSF files
  3. Right click on a GSF file in the playlist and select convert
  4. Click OK to convert

Expected behaviour:

  • Track is converted without error

Actual behaviour:

  • Segmentation fault

Workaround:

  • The segfault only appears to happen if another GSF file is playing at the same time. If you press STOP (not PAUSE) before converting, the file should convert without issue. You can also restart DeaDBeeF before attempting the conversion if the issue still persists.

Conversion sometimes fails when sample rate is 44100Hz

When the sample rate is set to 44.1kHz (the default), using deadbeef to convert from GSF to FLAC (for example) can sometimes lead to a failure, with flac complaining in stderr about receiving a partial sample.

I have not encountered this bug when the sample rate is 96kHz.

Seeking very slow

At the moment, seeking is very slow. This is somewhat to be expected, as seeking is performed by running the emulator as fast as possible and binning any sound data that isn't needed. However, it feels a lot slower than I would expect it to be. I expect a lot of this is because the emulator only generates ~1000 (I think it should be 960 on average, but would have to double check) samples at a time when run for 250,000 cycles, so we spend a lot of time incrementing various counters and resetting the buffer to empty. It would be better to either:

  1. Run the emulator several times (either at the current 250,000 samples, or a larger value if it is shown to improve performance -- maybe this could be an argument to the seeking function) until the buffer either a) reaches some arbitrary size, or b) we hit the sample we were seeking for (or, in theory, both; the buffer will continue to hold the data and subsequent read requests will just pull from the already buffered data). Then we a) empty the buffer, or b) empty up to the sample we want.
  2. Scale the number of cycles we run the emulator for based on the number of samples we need to skip. This way, we can jump to approximately the right location straight away and do more fine-grained adjustment afterwards.

Option 1 is probably going to be the slowest, but will give much more control over the memory usage of the plugin. Option 2 would be the fastest as it should involve very little logic in the plugin itself, but the buffer will likely grow quite large as it needs to accommodate most of the track. Vectors are being used under the hood, so this isn't a big deal as the buffer can grow as it needs, but the buffer's size is never reduced at present (the buffer is actually, at the time of this edit, being resized at the start of every write). On most PCs, this will probably never be an issue, but it seems a bit pointless to hold open a very large buffer for no reason; I'd want to add in some way of automatically reducing the buffer size again.

Finally, both options could actually be implemented, with some logic at the start that decides which option is going to be the best. If we aren't seeking by many samples, then we could use option 2 quite happily without needing a huge buffer, but if we're seeking e.g. from the start of a several minute track to the end, we could use option 1 to keep the buffer size low.

I recommend not attempting to solve this issue until #11 has been implemented.

Type used for number of samples is inconsistent

In some places (e.g. readsamples in the state object), samples are defined as an int. Elsewhere, they are defined as 16-bit integers or 64-bit integers. We should figure out which type is going to be reasonable and make it consistent throughout the codebase.

Sample Seeking

Should implement the function for deadbeef to seek to a particular sample. The most elegant way to do this would be to use the sample tracking code added with #10 and modify the existing seek function to be the new sample seeking function. Seeking to a timestamp can then be re-added as a wrapper around the sample seeking function.

Load all available metadata tags, not just a fixed set

Currently, only a fixed set of metadata tags ("length", "fade", "title", "artist", "year", "game", "comment") are loaded. This prevents ReplayGain from being used here. In addition to this fixed set of tags, any detected tags should be loaded into some other data structure (an unordered_map<std::string>std::string perhaps?) and made visible on the playlist screen.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.