diff --git a/.github/workflows/build_macOS.yaml b/.github/workflows/build_macOS.yaml index 7560ab313..e7d316942 100644 --- a/.github/workflows/build_macOS.yaml +++ b/.github/workflows/build_macOS.yaml @@ -46,8 +46,8 @@ jobs: pip install \ -r requirements.txt \ build - # Hack until https://github.com/pyinstaller/pyinstaller/issues/8936 is resolved - pip install https://github.com/rokm/pyinstaller/archive/refs/heads/macos-nested-framework-bundles.zip + # Hack until pyinstaller has a release newer than 6.11.1 - https://github.com/pyinstaller/pyinstaller/releases + pip install https://github.com/pyinstaller/pyinstaller/archive/develop.zip # \ # pyinstaller # pip uninstall pyinstaller diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c41a6f3a..423a8af70 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,9 @@ Changelog ### v7.9.0 - **Added** TIDAL support +- **Added** macOS (experimental) and Linux automated CI builds +- **Changed** portable installations now save `cache` and `user-data` directiories in `src/tauon/`, move existing directories there if necessary +- **Fixed** Windows support - **Fixed** crashes related to PipeWire [#1250](https://github.com/Taiko2k/Tauon/issues/1250) - **Fixed** audio cutting out on the PipeWire backend with specific custom quantum settings [#1245](https://github.com/Taiko2k/Tauon/issues/1245) - **Fixed** wrong encoding used for some tags in XSPF exports [#1331](https://github.com/Taiko2k/Tauon/issues/1331) @@ -17,7 +20,6 @@ Changelog - **Improved** Various changes to build system, Migrated to pyproject.toml - ***Removed*** guitar chords feature - api.guitarchords.com it partially relied on is dead, replaced by newer API that would need implementing, and the chords feature was unmaintained - ***Removed*** Spotify recommendations feature (API deprecated by Spotify) - - Many other bug fixes and code refactors [Special thanks to @C0rn3j for a lot of these] ### v7.8.3 diff --git a/extra/msyspac.txt b/extra/msyspac.txt index 15ca07499..910df927e 100644 --- a/extra/msyspac.txt +++ b/extra/msyspac.txt @@ -1,16 +1,17 @@ -mingw-w64-x86_64-SDL2 -mingw-w64-x86_64-SDL2_image mingw-w64-x86_64-flac mingw-w64-x86_64-gcc mingw-w64-x86_64-gtk3 +mingw-w64-x86_64-libgme mingw-w64-x86_64-libopenmpt mingw-w64-x86_64-libsamplerate mingw-w64-x86_64-opusfile mingw-w64-x86_64-pkgconf -mingw-w64-x86_64-wavpack mingw-w64-x86_64-python3 mingw-w64-x86_64-python3-gobject mingw-w64-x86_64-python3-pillow mingw-w64-x86_64-python3-pip mingw-w64-x86_64-python-websocket-client -mingw-w64-x86_64-libgme \ No newline at end of file +mingw-w64-x86_64-rust +mingw-w64-x86_64-SDL2 +mingw-w64-x86_64-SDL2_image +mingw-w64-x86_64-wavpack diff --git a/requirements_linux.txt b/extra/requirements_linux.txt similarity index 100% rename from requirements_linux.txt rename to extra/requirements_linux.txt diff --git a/requirements_macos.txt b/extra/requirements_macos.txt similarity index 100% rename from requirements_macos.txt rename to extra/requirements_macos.txt diff --git a/requirements_optional.txt b/extra/requirements_optional.txt similarity index 80% rename from requirements_optional.txt rename to extra/requirements_optional.txt index 222ae36fe..a00b212d4 100644 --- a/requirements_optional.txt +++ b/extra/requirements_optional.txt @@ -2,7 +2,8 @@ colored_traceback jxlpy; sys_platform != 'darwin' # macOS hates it - fails to find jxl/types.h - https://github.com/olokelo/jxlpy/issues/25#issuecomment-2547928563 #librespot - https://github.com/kokarare1212/librespot-python/pull/286 natsort -opencc +opencc; sys_platform != 'win32' +opencc-python-reimplemented; sys_platform == 'win32' #picard - picard 2.12.3 requires charset-normalizer~=3.3.2, but you have charset-normalizer 3.4.0 which is incompatible. plexapi PyChromecast diff --git a/requirements_windows.txt b/extra/requirements_windows.txt similarity index 97% rename from requirements_windows.txt rename to extra/requirements_windows.txt index caac82813..cdcc5e9ea 100644 --- a/requirements_windows.txt +++ b/extra/requirements_windows.txt @@ -6,6 +6,7 @@ musicbrainzngs mutagen natsort # optdep opencc-python-reimplemented # Windows version of openCC optdep +Pillow PlexAPI PyGObject pyinstaller diff --git a/pyproject.toml b/pyproject.toml index dba58dd22..c0001ee30 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -71,10 +71,10 @@ # https://setuptools.pypa.io/en/latest/userguide/ext_modules.html # This is terribly formatted as in TOML 1.0 it all has to be inline, can only be changed when TOML 1.1 ships - # Windows - MSVC - does not work as PyGobject, GTK, Cairo and et al won't build under MVSC + # Windows (MSVC) - does not work as PyGobject, GTK, Cairo and et al won't build under MVSC # One option would be to test https://github.com/wingtk/gvsbuild + https://stackoverflow.com/a/72737752 # ./vcpkg.exe install libsndfile:x64-windows # TODO download the necessities instead of this bundle, libflac at minimum - # ./vcpkg.exe install mpg123:x64-windows # Hack 1.29.3 in first see https://sourceforge.net/p/mpg123/bugs/374/ + # ./vcpkg.exe install mpg123:x64-windows # ./vcpkg.exe install libsamplerate:x64-windows # ./vcpkg.exe install libopenmpt:x64-windows # ./vcpkg.exe install wavpack:x64-windows @@ -85,18 +85,14 @@ # ext-modules = [ # {name = "phazor", sources = ["src/phazor/kissfft/kiss_fftr.c", "src/phazor/kissfft/kiss_fft.c", "src/phazor/phazor.c"], include-dirs = ["src/phazor/miniaudio", "C:/Users/Yeet/Tauon/src/phazor/miniaudio", "C:/Users/Yeet/Tauon/vcpkg/installed/x64-windows/include", "C:/Users/Yeet/Tauon/src/phazor/kissfft", "C:/Users/Yeet/Tauon/src/phazor/miniaudio"], libraries=["samplerate", "wavpackdll", "opusfile", "ogg", "opus", "vorbisfile", "mpg123", "FLAC", "openmpt", "pthreadVC3", "gme"], library-dirs = ["vcpkg/installed/x64-windows/lib"] },] - # Linux + macOS + # Linux + macOS + Windows (MINGW64) ext-modules = [ - {name = "phazor", sources = ["src/phazor/kissfft/kiss_fftr.c", "src/phazor/kissfft/kiss_fft.c", "src/phazor/phazor.c"], include-dirs = ["/usr/include/opus", "/opt/homebrew/include/opus", "/opt/homebrew/include"], libraries = ["samplerate", "wavpack", "opusfile", "vorbisfile", "mpg123", "FLAC", "openmpt", "gme"], library-dirs = ["/opt/homebrew/lib"] }, + {name = "phazor", sources = ["src/phazor/kissfft/kiss_fftr.c", "src/phazor/kissfft/kiss_fft.c", "src/phazor/phazor.c"], include-dirs = ["/usr/include/opus", "/opt/homebrew/include/opus", "/opt/homebrew/include", "C:/msys64/mingw64/include/opus"], libraries = ["samplerate", "wavpack", "opusfile", "vorbisfile", "mpg123", "FLAC", "openmpt", "gme"], library-dirs = ["/opt/homebrew/lib"] }, # Set as optional to allow soft-failure, it's expected not to build on Windows/macOS, but we don't want to fail the entire build on it # I have not found a better way to solve this, ideally we would somehow tag this as Linux-exclusive {name = "phazor-pw", sources = ["src/phazor/kissfft/kiss_fftr.c", "src/phazor/kissfft/kiss_fft.c", "src/phazor/phazor.c"], include-dirs = ["/usr/include/opus"], libraries = ["samplerate", "wavpack", "opusfile", "vorbisfile", "mpg123", "FLAC", "openmpt", "gme", "pipewire-0.3"], optional = true }, ] - # macOS -# ext-modules = [ -# {name = "phazor", sources = ["src/phazor/kissfft/kiss_fftr.c", "src/phazor/kissfft/kiss_fft.c", "src/phazor/phazor.c"], include-dirs = ["/opt/homebrew/include/opus", "src/phazor/kissfft", "/opt/homebrew/include"], libraries=["samplerate", "wavpack", "opusfile", "vorbisfile", "mpg123", "FLAC", "openmpt", "gme"], library-dirs = ["/opt/homebrew/lib"] },] - package-dir = {"" = "src"} # Should we care about these options or is it fine to omit them here? diff --git a/requirements.txt b/requirements.txt index 333c79370..44e065374 100644 --- a/requirements.txt +++ b/requirements.txt @@ -13,7 +13,7 @@ pysdl2-dll # Don't rely on system SDL2 https://github.com/py-sdl/py-sdl2#require comtypes; sys_platform == 'win32' infi.systray; sys_platform == 'win32' keyboard; sys_platform == 'win32' -Pillow; sys_platform != 'win32' +Pillow opencc; sys_platform != 'win32' # OPTDEP opencc-python-reimplemented; sys_platform == 'win32' # OPTDEP #pyinstaller # ; sys_platform != 'linux' # for macOS at least diff --git a/run.sh b/run.sh index 1966dd51c..fa7a78c68 100755 --- a/run.sh +++ b/run.sh @@ -4,16 +4,18 @@ set -euo pipefail win_build() { rm -rf dist/tauon + # Had to do Windows Security -> Virus & thread protection*2 -> Manage settings -> Windows Real-time protection: off # TODO(Martin): pkg_resources is deprecated, does it still need to be there? # https://setuptools.pypa.io/en/latest/pkg_resources.html pyinstaller \ + --name TauonMusicBox \ + --noconfirm \ --additional-hooks-dir='extra\pyinstaller-hooks' \ --hidden-import 'infi.systray' \ --hidden-import 'pylast' \ --hidden-import 'tekore' \ - --add-binary 'C:\msys64\mingw64\lib\girepository-1.0\Rsvg-2.0.typelib;gi_typelibs' \ - --add-binary 'lib/libphazor.so;lib' \ + --hidden-import 'phazor' \ --add-binary 'C:\msys64\mingw64\bin\libFLAC.dll;.' \ --add-binary 'C:\msys64\mingw64\bin\libmpg123-0.dll;.' \ --add-binary 'C:\msys64\mingw64\bin\libogg-0.dll;.' \ @@ -31,27 +33,40 @@ win_build() { --hidden-import 'packaging.requirements' \ --hidden-import 'pkg_resources.py2_warn' \ --hidden-import 'requests' \ - src/tauon/tauon.py \ - -w -i assets/icon.ico + src/tauon/__main__.py \ + -w -i src/tauon/assets/icon.ico - mkdir -p dist/tauon/tekore - mkdir -p dist/tauon/etc + mkdir -p dist/TauonMusicBox/tekore + mkdir -p dist/TauonMusicBox/etc #cp C:/msys64/mingw64/lib/python3.13/site-packages/tekore/VERSION dist/tauon/tekore/VERSION - cp -r src/tauon/{theme,assets,locale,templates,lib} dist/tauon/ - rm -rf dist/tauon/share/{icons,locale,tcl/tzdata} dist/tauon/tcl/tzdata - cp -r fonts dist/tauon/ || echo 'Fonts are not present!' - cp -r /mingw64/etc/fonts dist/tauon/etc - cp librespot.exe dist/tauon/ - cp TaskbarLib.tlb dist/tauon/ || echo 'TLB is not present!' + cp -r src/tauon/{theme,assets,locale,templates} dist/TauonMusicBox/ + rm -rf dist/tauon/share/{icons,locale,tcl/tzdata} dist/TauonMusicBox/tcl/tzdata + cp -r fonts dist/tauon/ || echo 'fonts directory is not present!' + cp -r /mingw64/etc/fonts dist/TauonMusicBox/etc + if [[ -e librespot.exe ]]; then + cp librespot.exe dist/TauonMusicBox/ + else + echo 'librespot.exe is not present!' + fi + if [[ -e TaskbarLib.tlb ]]; then + cp TaskbarLib.tlb dist/TauonMusicBox/ + else + echo 'TaskbarLib.tlb is not present!' + fi + echo -e "Packaged to dist/TauonMusicBox" } -dirty_venv_run() { - if ! command -v python; then +python_check() { + if ! command -v python >/dev/null; then echo -e "python executable not found? Is python installed? Debian(-based) distributions may need python-is-python3 installed via apt." exit 1 fi +} + +dirty_venv_run() { + python_check # Ensure correct cwd, for example: ~/Projects/Tauon cd "$(dirname "${0}")" export PYTHONPATH=".":"${PYTHONPATH-}" @@ -60,10 +75,7 @@ dirty_venv_run() { } clean_venv_run() { - if ! command -v python; then - echo -e "python executable not found? Is python installed? Debian(-based) distributions may need python-is-python3 installed via apt." - exit 1 - fi + python_check # Ensure correct cwd, for example: ~/Projects/Tauon cd "$(dirname "${0}")" export PYTHONPATH=".":"${PYTHONPATH-}" @@ -82,14 +94,17 @@ clean_venv_run() { python -m venv .venv source .venv/bin/activate - pip install -r requirements.txt -r requirements_optional.txt -r requirements_devel.txt build +# python -m pip install -U pip + # Necessary for Windows (MINGW64) if compiling things like Pillow + export CFLAGS="-I/mingw64/include" +# export LDFLAGS="-L/mingw64/lib" + pip install -r requirements.txt -r requirements_devel.txt build python -m compile_translations python -m build --wheel pip install --prefix ".venv" dist/*.whl --force-reinstall tauonmb # "${@}" # Passing args is broken atm } - compile_phazor() { outFile="build/libphazor.so" mkdir -p build @@ -100,7 +115,6 @@ compile_phazor() { echo "Compiled as ${outFile}!" } - compile_phazor_pipewire() { outFile="build/libphazor-pw.so" mkdir -p build @@ -111,7 +125,6 @@ compile_phazor_pipewire() { echo "Compiled as ${outFile}!" } -# Display menu show_menu() { PS3="Select a script to run: " select yn in "${answer_options[@]}"; do diff --git a/src/phazor/phazor.c b/src/phazor/phazor.c index d03976aa1..100cb0284 100644 --- a/src/phazor/phazor.c +++ b/src/phazor/phazor.c @@ -18,25 +18,26 @@ #define MINI -#ifdef _WIN32 -#define WIN -#include -#define usleep(usec) Sleep((usec) / 1000) // Convert microseconds to milliseconds +#ifdef WIN64 + #include + #ifndef __MINGW64__ + #define usleep(usec) Sleep((usec) / 1000) // Convert microseconds to milliseconds + #endif #else -#include + #include #endif #ifdef PIPE -#undef MINI + #undef MINI #endif //#define MINI #ifdef PIPE -#include -#include -#include -#include + #include + #include + #include + #include #endif #define _GNU_SOURCE @@ -49,19 +50,19 @@ #ifdef MINI -#define MINIAUDIO_IMPLEMENTATION -#define MA_NO_GENERATION -#define MA_NO_DECODING -#define MA_NO_ENCODING -#define MA_ENABLE_ONLY_SPECIFIC_BACKENDS -#define MA_ENABLE_WASAPI -#define MA_ENABLE_PULSEAUDIO -#define MA_ENABLE_COREAUDIO -#define MA_ENABLE_OSS -#define MA_ENABLE_SNDIO -#define MA_ENABLE_AUDIO4 -//#define MA_DEBUG_OUTPUT -#include "miniaudio/miniaudio.h" + #define MINIAUDIO_IMPLEMENTATION + #define MA_NO_GENERATION + #define MA_NO_DECODING + #define MA_NO_ENCODING + #define MA_ENABLE_ONLY_SPECIFIC_BACKENDS + #define MA_ENABLE_WASAPI + #define MA_ENABLE_PULSEAUDIO + #define MA_ENABLE_COREAUDIO + #define MA_ENABLE_OSS + #define MA_ENABLE_SNDIO + #define MA_ENABLE_AUDIO4 + //#define MA_DEBUG_OUTPUT + #include "miniaudio/miniaudio.h" #endif #include @@ -78,7 +79,6 @@ #include "gme/gme.h" #include - // Module method definitions (if any) static PyMethodDef PhazorMethods[] = { {NULL, NULL, 0, NULL} // Sentinel @@ -93,133 +93,136 @@ static struct PyModuleDef phazor_module = { PhazorMethods // Methods table }; -#ifdef _WIN32 -__declspec(dllexport) +#ifdef WIN64 + __declspec(dllexport) + #define EXPORT __declspec(dllexport) +#else + #define EXPORT #endif + // Entry point for the module PyMODINIT_FUNC PyInit_phazor(void) { return PyModule_Create(&phazor_module); } - #define BUFF_SIZE 240000 // Decoded data buffer size #define BUFF_SAFE 100000 // Ensure there is this much space free in the buffer #ifdef MINI -ma_context_config c_config; -ma_device_config config; -ma_device device; + ma_context_config c_config; + ma_device_config config; + ma_device device; #endif #ifdef PIPE -pthread_t pw_thread; -pthread_mutex_t pipe_devices_mutex; -struct pw_main_loop *loop; -struct pw_context *context; -struct pw_core *core; -struct pw_registry *registry; -struct spa_hook registry_listener; -struct spa_hook core_listener; -struct pw_stream *global_stream; -int enum_done = 0; -int pipe_set_samplerate = 48000; -#define MAX_DEVICES 64 -#define POD_BUFFER_SIZE 2048 -struct device_info { - uint32_t id; - char name[256]; - char description[256]; -}; -struct pipe_devices_struct { - struct device_info devices[MAX_DEVICES]; - int device_count; -}; - -struct pipe_devices_struct pipe_devices = {0}; - -static void registry_event_remove_global(void *data, uint32_t id) { - pthread_mutex_lock(&pipe_devices_mutex); - for (size_t i = 0; i < pipe_devices.device_count; i++) { - if (pipe_devices.devices[i].id == id) { // Assuming each device has a unique ID - // Shift remaining devices to fill the gap - for (size_t j = i; j < pipe_devices.device_count - 1; j++) { - pipe_devices.devices[j] = pipe_devices.devices[j + 1]; - } - pipe_devices.device_count--; - printf("Removed device with ID: %u\n", id); - break; - } - } - pthread_mutex_unlock(&pipe_devices_mutex); -} - -static void registry_event_global(void *data, uint32_t id, - uint32_t permissions, const char *type, uint32_t version, - const struct spa_dict *props) -{ - - if (props == NULL || type == NULL || !spa_streq(type, PW_TYPE_INTERFACE_Node)) - return; - - - //printf("object: id:%u type:%s/%d\n", id, type, version); - const char *media_class; - - media_class = spa_dict_lookup(props, PW_KEY_MEDIA_CLASS); - if (media_class == NULL) - return; - - if (spa_streq(media_class, "Audio/Sink")) { - - pthread_mutex_lock(&pipe_devices_mutex); - if (pipe_devices.device_count >= MAX_DEVICES){ - printf("Error: Max devices\n"); - pthread_mutex_unlock(&pipe_devices_mutex); - return; - } - const char *name = spa_dict_lookup(props, PW_KEY_NODE_NAME); - const char *description = spa_dict_lookup(props, PW_KEY_NODE_DESCRIPTION); - if (!name || !description) { - printf("Error: Missing name or description for device\n"); - pthread_mutex_unlock(&pipe_devices_mutex); - return; - } - - // Check if already added - for (size_t i = 0; i < pipe_devices.device_count; i++) { - if (pipe_devices.devices[i].id == id) { - pthread_mutex_unlock(&pipe_devices_mutex); - return; - } - } - pipe_devices.devices[pipe_devices.device_count].id = id; - snprintf(pipe_devices.devices[pipe_devices.device_count].name, sizeof(pipe_devices.devices[pipe_devices.device_count].name), "%s", name); - snprintf(pipe_devices.devices[pipe_devices.device_count].description, sizeof(pipe_devices.devices[pipe_devices.device_count].description), "%s", description); - pipe_devices.device_count++; - printf("Found audio sink: %s (%s)\n", name, description); - pthread_mutex_unlock(&pipe_devices_mutex); - - } -} - -static const struct pw_registry_events registry_events = { - PW_VERSION_REGISTRY_EVENTS, - .global = registry_event_global, - .global_remove = registry_event_remove_global, -}; - -static void on_core_done(void *userdata, uint32_t id, int seq){ - if (id == PW_ID_CORE){ - enum_done = 1; - } -} - -static const struct pw_core_events core_events = { - PW_VERSION_CORE_EVENTS, - .done = on_core_done, -}; - + pthread_t pw_thread; + pthread_mutex_t pipe_devices_mutex; + struct pw_main_loop *loop; + struct pw_context *context; + struct pw_core *core; + struct pw_registry *registry; + struct spa_hook registry_listener; + struct spa_hook core_listener; + struct pw_stream *global_stream; + int enum_done = 0; + int pipe_set_samplerate = 48000; + #define MAX_DEVICES 64 + #define POD_BUFFER_SIZE 2048 + struct device_info { + uint32_t id; + char name[256]; + char description[256]; + }; + struct pipe_devices_struct { + struct device_info devices[MAX_DEVICES]; + int device_count; + }; + + struct pipe_devices_struct pipe_devices = {0}; + + static void registry_event_remove_global(void *data, uint32_t id) { + pthread_mutex_lock(&pipe_devices_mutex); + for (size_t i = 0; i < pipe_devices.device_count; i++) { + if (pipe_devices.devices[i].id == id) { // Assuming each device has a unique ID + // Shift remaining devices to fill the gap + for (size_t j = i; j < pipe_devices.device_count - 1; j++) { + pipe_devices.devices[j] = pipe_devices.devices[j + 1]; + } + pipe_devices.device_count--; + printf("Removed device with ID: %u\n", id); + break; + } + } + pthread_mutex_unlock(&pipe_devices_mutex); + } + + static void registry_event_global( + void *data, uint32_t id, + uint32_t permissions, const char *type, uint32_t version, + const struct spa_dict *props) + { + + if (props == NULL || type == NULL || !spa_streq(type, PW_TYPE_INTERFACE_Node)) + return; + + + //printf("object: id:%u type:%s/%d\n", id, type, version); + const char *media_class; + + media_class = spa_dict_lookup(props, PW_KEY_MEDIA_CLASS); + if (media_class == NULL) + return; + + if (spa_streq(media_class, "Audio/Sink")) { + + pthread_mutex_lock(&pipe_devices_mutex); + if (pipe_devices.device_count >= MAX_DEVICES) { + printf("Error: Max devices\n"); + pthread_mutex_unlock(&pipe_devices_mutex); + return; + } + const char *name = spa_dict_lookup(props, PW_KEY_NODE_NAME); + const char *description = spa_dict_lookup(props, PW_KEY_NODE_DESCRIPTION); + if (!name || !description) { + printf("Error: Missing name or description for device\n"); + pthread_mutex_unlock(&pipe_devices_mutex); + return; + } + + // Check if already added + for (size_t i = 0; i < pipe_devices.device_count; i++) { + if (pipe_devices.devices[i].id == id) { + pthread_mutex_unlock(&pipe_devices_mutex); + return; + } + } + pipe_devices.devices[pipe_devices.device_count].id = id; + snprintf(pipe_devices.devices[pipe_devices.device_count].name, sizeof(pipe_devices.devices[pipe_devices.device_count].name), "%s", name); + snprintf(pipe_devices.devices[pipe_devices.device_count].description, sizeof(pipe_devices.devices[pipe_devices.device_count].description), "%s", description); + pipe_devices.device_count++; + printf("Found audio sink: %s (%s)\n", name, description); + pthread_mutex_unlock(&pipe_devices_mutex); + + } + } + + static const struct pw_registry_events registry_events = { + PW_VERSION_REGISTRY_EVENTS, + .global = registry_event_global, + .global_remove = registry_event_remove_global, + }; + + static void on_core_done(void *userdata, uint32_t id, int seq) { + if (id == PW_ID_CORE) { + enum_done = 1; + } + } + + static const struct pw_core_events core_events = { + PW_VERSION_CORE_EVENTS, + .done = on_core_done, + }; #endif float bfl[BUFF_SIZE]; @@ -229,23 +232,23 @@ int high = 0; int high_mark = BUFF_SIZE - BUFF_SAFE; int watermark = BUFF_SIZE - BUFF_SAFE; -int get_buff_fill(){ - if (low <= high) return high - low; - return (watermark - low) + high; +int get_buff_fill() { + if (low <= high) return high - low; + return (watermark - low) + high; } -void buff_cycle(){ - if (high > high_mark){ - watermark = high; - high = 0; - } - if (low >= watermark) low = 0; +void buff_cycle() { + if (high > high_mark) { + watermark = high; + high = 0; + } + if (low >= watermark) low = 0; } -void buff_reset(){ - low = 0; - high = 0; - watermark = high_mark; +void buff_reset() { + low = 0; + high = 0; + watermark = high_mark; } double t_start, t_end; @@ -278,8 +281,8 @@ pthread_mutex_t fade_mutex; float out_buff[2048 * 2]; //#ifdef AO -//char out_buffc[2048 * 4]; -//int32_t temp32 = 0; +// char out_buffc[2048 * 4]; +// int32_t temp32 = 0; //#endif int position_count = 0; @@ -339,42 +342,42 @@ int config_min_buffer = 30000; unsigned int test1 = 0; enum status { - PLAYING, - PAUSED, - STOPPED, - RAMP_DOWN, - ENDING, + PLAYING, + PAUSED, + STOPPED, + RAMP_DOWN, + ENDING, }; enum command_status { - NONE, - START, - LOAD, // used internally only - SEEK, - STOP, - PAUSE, - RESUME, - EXIT, + NONE, + START, + LOAD, // used internally only + SEEK, + STOP, + PAUSE, + RESUME, + EXIT, }; enum decoder_types { - UNKNOWN, - FLAC, - MPG, - VORBIS, - OPUS, - FFMPEG, - WAVE, - MPT, - FEED, - WAVPACK, - GME, + UNKNOWN, + FLAC, + MPG, + VORBIS, + OPUS, + FFMPEG, + WAVE, + MPT, + FEED, + WAVPACK, + GME, }; enum result_status_enum { - WAITING, - SUCCESS, - FAILURE + WAITING, + SUCCESS, + FAILURE }; int result_status = WAITING; @@ -391,47 +394,47 @@ FILE *d_file; // Misc ---------------------------------------------------------- float ramp_step(int sample_rate, int milliseconds) { - return 1.0 / sample_rate / (milliseconds / 1000.0); + return 1.0 / sample_rate / (milliseconds / 1000.0); } void fade_fx() { - //pthread_mutex_lock(&fade_mutex); - - if (rg_value_want != 0.0 && rg_value_want != 1.0){ - bfr[high] *= rg_value_want; - bfl[high] *= rg_value_want; - if (bfl[high] > 1) bfl[high] = 1; - if (bfl[high] < -1) bfl[high] = -1; - if (bfr[high] > 1) bfr[high] = 1; - if (bfr[high] < -1) bfr[high] = -1; - } - - if (fade_mini < 1.0){ - fade_mini += ramp_step(sample_rate_out, 10); // 10ms ramp - bfr[high] *= fade_mini; - bfl[high] *= fade_mini; - if (fade_mini > 1.0) fade_mini = 1.0; - } - if (fade_fill > 0) { - if (fade_fill == fade_position) { - fade_fill = 0; - fade_position = 0; - } else { - fade_lockout = 1; - float cross = fade_position / (float) fade_fill; - float cross_i = 1.0 - cross; - - - bfl[high] *= cross; - bfl[high] += fadefl[fade_position] * cross_i; - - bfr[high] *= cross; - bfr[high] += fadefr[fade_position] * cross_i; - fade_position++; - - } - } - //pthread_mutex_unlock(&fade_mutex); + //pthread_mutex_lock(&fade_mutex); + + if (rg_value_want != 0.0 && rg_value_want != 1.0) { + bfr[high] *= rg_value_want; + bfl[high] *= rg_value_want; + if (bfl[high] > 1) bfl[high] = 1; + if (bfl[high] < -1) bfl[high] = -1; + if (bfr[high] > 1) bfr[high] = 1; + if (bfr[high] < -1) bfr[high] = -1; + } + + if (fade_mini < 1.0) { + fade_mini += ramp_step(sample_rate_out, 10); // 10ms ramp + bfr[high] *= fade_mini; + bfl[high] *= fade_mini; + if (fade_mini > 1.0) fade_mini = 1.0; + } + if (fade_fill > 0) { + if (fade_fill == fade_position) { + fade_fill = 0; + fade_position = 0; + } else { + fade_lockout = 1; + float cross = fade_position / (float) fade_fill; + float cross_i = 1.0 - cross; + + + bfl[high] *= cross; + bfl[high] += fadefl[fade_position] * cross_i; + + bfr[high] *= cross; + bfr[high] += fadefr[fade_position] * cross_i; + fade_position++; + + } + } + //pthread_mutex_unlock(&fade_mutex); } FILE *fptr; @@ -494,49 +497,49 @@ void (*ff_close)(); void (*on_device_unavailable)(); void start_ffmpeg(char uri[], int start_ms) { - int status = ff_start(uri, start_ms, sample_rate_out); - if (status != 0){ - printf("pa: Error starting FFMPEG\n"); - return; - } + int status = ff_start(uri, start_ms, sample_rate_out); + if (status != 0) { + printf("pa: Error starting FFMPEG\n"); + return; + } - decoder_allocated = 1; - sample_rate_src = sample_rate_out; + decoder_allocated = 1; + sample_rate_src = sample_rate_out; } void stop_ffmpeg() { - ff_close(); + ff_close(); } void resample_to_buffer(int in_frames) { - src_data.data_in = re_in; - src_data.data_out = re_out; - src_data.input_frames = in_frames; - src_data.output_frames = BUFF_SIZE - BUFF_SAFE; - src_data.src_ratio = (double) sample_rate_out / (double) sample_rate_src; - src_data.end_of_input = 0; + src_data.data_in = re_in; + src_data.data_out = re_out; + src_data.input_frames = in_frames; + src_data.output_frames = BUFF_SIZE - BUFF_SAFE; + src_data.src_ratio = (double) sample_rate_out / (double) sample_rate_src; + src_data.end_of_input = 0; - src_process(src, &src_data); - //printf("pa: SRC error code: %d\n", src_result); - //printf("pa: SRC output frames: %lu\n", src_data.output_frames_gen); - //printf("pa: SRC input frames used: %lu\n", src_data.input_frames_used); - int out_frames = src_data.output_frames_gen; + src_process(src, &src_data); + //printf("pa: SRC error code: %d\n", src_result); + //printf("pa: SRC output frames: %lu\n", src_data.output_frames_gen); + //printf("pa: SRC input frames used: %lu\n", src_data.input_frames_used); + int out_frames = src_data.output_frames_gen; - int i = 0; - while (i < out_frames) { + int i = 0; + while (i < out_frames) { - bfl[high] = re_out[i * 2]; - bfr[high] = re_out[(i * 2) + 1]; + bfl[high] = re_out[i * 2]; + bfr[high] = re_out[(i * 2) + 1]; - fade_fx(); + fade_fx(); - high += 1; - i++; - } - buff_cycle(); + high += 1; + i++; + } + buff_cycle(); } @@ -553,242 +556,240 @@ int16_t wave_16 = 0; int wave_open(char *filename) { - wave_file = fopen(filename, "rb"); - - char b[16]; - int i; - - b[15] = '\0'; - fread(b, 4, 1, wave_file); - //printf("pa: mark: %s\n", b) - - fread(&i, 4, 1, wave_file); - //printf("pa: size: %d\n", i); - wave_size = i - 44; - - fread(b, 4, 1, wave_file); - //printf("pa: head: %s\n", b); - if (memcmp(b, "WAVE", 4) == 1) { - printf("pa: Invalid WAVE file\n"); - return 1; - } - - while (1) { - - // Read data block label - wave_error = fread(b, 4, 1, wave_file); - if (wave_error != 1) { - fclose(wave_file); - return 1; - } - // Read data block length - wave_error = fread(&i, 4, 1, wave_file); - if (wave_error != 1) { - fclose(wave_file); - return 1; - } - // Is audio data? - if (memcmp(b, "fmt ", 4) == 0) { - wave_start = ftell(wave_file); - wave_size = i; - break; - } - // Skip to next block - fseek(wave_file, i, SEEK_CUR); - } - - - //fread(b, 4, 1, wave_file); - //printf("pa: fmt : %s\n", b); - - //fread(&i, 4, 1, wave_file); - //printf("pa: abov: %d\n", i); - //if (i != 16) { - // printf("pa: Unsupported WAVE file\n"); - // return 1; - //} - - fread(&i, 2, 1, wave_file); - //printf("pa: type: %d\n", i); - if (i != 1) { - printf("pa: Unsupported WAVE file\n"); - return 1; - } - - fread(&i, 2, 1, wave_file); - //printf("pa: chan: %d\n", i); - if (i != 1 && i != 2) { - printf("pa: Unsupported WAVE channels\n"); - return 1; - } - wave_channels = i; - - fread(&i, 4, 1, wave_file); - //printf("pa: smpl: %d\n", i); - wave_samplerate = i; - sample_rate_src = i; - - fseek(wave_file, 6, SEEK_CUR); - - fread(&i, 2, 1, wave_file); - //printf("pa: bitd: %d\n", i); - if (i != 16) { - printf("pa: Unsupported WAVE depth\n"); - return 1; - } - wave_depth = i; - fseek(wave_file, wave_start + wave_size, SEEK_SET); - - while (1) { - - // Read data block label - wave_error = fread(b, 4, 1, wave_file); - if (wave_error != 1) { - fclose(wave_file); - return 1; - } - // Read data block length - wave_error = fread(&i, 4, 1, wave_file); - if (wave_error != 1) { - fclose(wave_file); - return 1; - } - // Is audio data? - //printf("label %s\n", b); - if (memcmp(b, "data", 4) == 0) { - wave_start = ftell(wave_file); - wave_size = i; - break; - } - // Skip to next block - fseek(wave_file, i, SEEK_CUR); - - } - - return 0; + wave_file = fopen(filename, "rb"); + + char b[16]; + int i; + + b[15] = '\0'; + fread(b, 4, 1, wave_file); + //printf("pa: mark: %s\n", b) + + fread(&i, 4, 1, wave_file); + //printf("pa: size: %d\n", i); + wave_size = i - 44; + + fread(b, 4, 1, wave_file); + //printf("pa: head: %s\n", b); + if (memcmp(b, "WAVE", 4) == 1) { + printf("pa: Invalid WAVE file\n"); + return 1; + } + + while (1) { + + // Read data block label + wave_error = fread(b, 4, 1, wave_file); + if (wave_error != 1) { + fclose(wave_file); + return 1; + } + // Read data block length + wave_error = fread(&i, 4, 1, wave_file); + if (wave_error != 1) { + fclose(wave_file); + return 1; + } + // Is audio data? + if (memcmp(b, "fmt ", 4) == 0) { + wave_start = ftell(wave_file); + wave_size = i; + break; + } + // Skip to next block + fseek(wave_file, i, SEEK_CUR); + } + + + //fread(b, 4, 1, wave_file); + //printf("pa: fmt : %s\n", b); + + //fread(&i, 4, 1, wave_file); + //printf("pa: abov: %d\n", i); + //if (i != 16) { + // printf("pa: Unsupported WAVE file\n"); + // return 1; + //} + + fread(&i, 2, 1, wave_file); + //printf("pa: type: %d\n", i); + if (i != 1) { + printf("pa: Unsupported WAVE file\n"); + return 1; + } + + fread(&i, 2, 1, wave_file); + //printf("pa: chan: %d\n", i); + if (i != 1 && i != 2) { + printf("pa: Unsupported WAVE channels\n"); + return 1; + } + wave_channels = i; + + fread(&i, 4, 1, wave_file); + //printf("pa: smpl: %d\n", i); + wave_samplerate = i; + sample_rate_src = i; + + fseek(wave_file, 6, SEEK_CUR); + + fread(&i, 2, 1, wave_file); + //printf("pa: bitd: %d\n", i); + if (i != 16) { + printf("pa: Unsupported WAVE depth\n"); + return 1; + } + wave_depth = i; + fseek(wave_file, wave_start + wave_size, SEEK_SET); + + while (1) { + // Read data block label + wave_error = fread(b, 4, 1, wave_file); + if (wave_error != 1) { + fclose(wave_file); + return 1; + } + // Read data block length + wave_error = fread(&i, 4, 1, wave_file); + if (wave_error != 1) { + fclose(wave_file); + return 1; + } + // Is audio data? + //printf("label %s\n", b); + if (memcmp(b, "data", 4) == 0) { + wave_start = ftell(wave_file); + wave_size = i; + break; + } + // Skip to next block + fseek(wave_file, i, SEEK_CUR); + } + + return 0; } int wave_decode(int read_frames) { - int frames_read = 0; - int end = 0; - int i = 0; - while (i < read_frames) { + int frames_read = 0; + int end = 0; + int i = 0; + while (i < read_frames) { - wave_error = fread(&wave_16, 2, 1, wave_file); - if (wave_error != 1) return 1; - re_in[i * 2] = wave_16 / 32768.0; + wave_error = fread(&wave_16, 2, 1, wave_file); + if (wave_error != 1) return 1; + re_in[i * 2] = wave_16 / 32768.0; - wave_error = fread(&wave_16, 2, 1, wave_file); - if (wave_error != 1) return 1; - re_in[i * 2 + 1] = wave_16 / 32768.0; + wave_error = fread(&wave_16, 2, 1, wave_file); + if (wave_error != 1) return 1; + re_in[i * 2 + 1] = wave_16 / 32768.0; - i++; - frames_read++; - if ((ftell(wave_file) - wave_start) > wave_size) { - printf("pa: End of WAVE file data\n"); - end = 1; - break; - } + i++; + frames_read++; + if ((ftell(wave_file) - wave_start) > wave_size) { + printf("pa: End of WAVE file data\n"); + end = 1; + break; + } - } + } - if (sample_rate_src != sample_rate_out){ - resample_to_buffer(frames_read); - } else { + if (sample_rate_src != sample_rate_out) { + resample_to_buffer(frames_read); + } else { - i = 0; - while (i < frames_read){ + i = 0; + while (i < frames_read) { - bfl[high] = re_in[i * 2]; - bfr[high] = re_in[i * 2 + 1]; + bfl[high] = re_in[i * 2]; + bfr[high] = re_in[i * 2 + 1]; - fade_fx(); + fade_fx(); - //buff_filled++; - high++; - samples_decoded++; - i++; - } - buff_cycle(); - } - if (end == 1) return 1; - return 0; + //buff_filled++; + high++; + samples_decoded++; + i++; + } + buff_cycle(); + } + if (end == 1) return 1; + return 0; } int wave_seek(int frame_position) { - return fseek(wave_file, (frame_position * 4) + wave_start, SEEK_SET); + return fseek(wave_file, (frame_position * 4) + wave_start, SEEK_SET); } void wave_close() { - fclose(wave_file); + fclose(wave_file); } -void read_to_buffer_24in32_fs(int32_t src[], int n_samples){ - // full samples version - int i = 0; - int f = 0; +void read_to_buffer_24in32_fs(int32_t src[], int n_samples) { + // full samples version + int i = 0; + int f = 0; - // Convert int16 to float - while (f < n_samples) { - re_in[f * 2] = (src[i]) / 8388608.0; - if (src_channels == 1) { - re_in[(f * 2) + 1] = re_in[f * 2]; - i += 1; - } else { - re_in[(f * 2) + 1] = (src[i + 1]) / 8388608.0; - i += 2; - } + // Convert int16 to float + while (f < n_samples) { + re_in[f * 2] = (src[i]) / 8388608.0; + if (src_channels == 1) { + re_in[(f * 2) + 1] = re_in[f * 2]; + i += 1; + } else { + re_in[(f * 2) + 1] = (src[i + 1]) / 8388608.0; + i += 2; + } - f++; - } + f++; + } - resample_to_buffer(f); + resample_to_buffer(f); } -void read_to_buffer_16in32_fs(int32_t src[], int n_samples){ - // full samples version - int i = 0; - int f = 0; +void read_to_buffer_16in32_fs(int32_t src[], int n_samples) { + // full samples version + int i = 0; + int f = 0; - // Convert int16 to float - while (f < n_samples) { - re_in[f * 2] = (src[i]) / 32768.0; - if (src_channels == 1) { - re_in[(f * 2) + 1] = re_in[f * 2]; - i += 1; - } else { - re_in[(f * 2) + 1] = (src[i + 1]) / 32768.0; - i += 2; - } + // Convert int16 to float + while (f < n_samples) { + re_in[f * 2] = (src[i]) / 32768.0; + if (src_channels == 1) { + re_in[(f * 2) + 1] = re_in[f * 2]; + i += 1; + } else { + re_in[(f * 2) + 1] = (src[i + 1]) / 32768.0; + i += 2; + } - f++; - } + f++; + } - resample_to_buffer(f); + resample_to_buffer(f); } void read_to_buffer_char16_resample(char src[], int n_bytes) { - int i = 0; - int f = 0; + int i = 0; + int f = 0; - // Convert bytes16 to float - while (i < n_bytes) { - re_in[f * 2] = (float)((int16_t)((src[i + 1] << 8) | (src[i + 0] & 0xFF)) / 32768.0); - if (src_channels == 1) { - re_in[(f * 2) + 1] = re_in[f * 2]; - i += 2; - } else { - re_in[(f * 2) + 1] = (float)((int16_t)((src[i + 3] << 8) | (src[i + 2] & 0xFF)) / 32768.0); - i += 4; - } + // Convert bytes16 to float + while (i < n_bytes) { + re_in[f * 2] = (float)((int16_t)((src[i + 1] << 8) | (src[i + 0] & 0xFF)) / 32768.0); + if (src_channels == 1) { + re_in[(f * 2) + 1] = re_in[f * 2]; + i += 2; + } else { + re_in[(f * 2) + 1] = (float)((int16_t)((src[i + 3] << 8) | (src[i + 2] & 0xFF)) / 32768.0); + i += 4; + } - f++; - } + f++; + } - resample_to_buffer(f); + resample_to_buffer(f); } @@ -796,107 +797,107 @@ void read_to_buffer_char16_resample(char src[], int n_bytes) { void read_to_buffer_char16(char src[], int n_bytes) { - if (sample_rate_src != sample_rate_out) { - read_to_buffer_char16_resample(src, n_bytes); - return; - } - - int i = 0; - if (src_channels == 1){ - while (i < n_bytes) { - bfl[high] = (float)((int16_t)((src[i + 1] << 8) | (src[i + 0] & 0xFF)) / 32768.0); - bfr[high] = bfl[high]; - fade_fx(); - high++; - i += 2; - } - } else { - while (i < n_bytes) { - bfl[high] = (float)((int16_t)((src[i + 1] << 8) | (src[i + 0] & 0xFF)) / 32768.0); - bfr[high] = (float)((int16_t)((src[i + 3] << 8) | (src[i + 2] & 0xFF)) / 32768.0); - fade_fx(); - high++; - i += 4; - } - } - buff_cycle(); + if (sample_rate_src != sample_rate_out) { + read_to_buffer_char16_resample(src, n_bytes); + return; + } + + int i = 0; + if (src_channels == 1) { + while (i < n_bytes) { + bfl[high] = (float)((int16_t)((src[i + 1] << 8) | (src[i + 0] & 0xFF)) / 32768.0); + bfr[high] = bfl[high]; + fade_fx(); + high++; + i += 2; + } + } else { + while (i < n_bytes) { + bfl[high] = (float)((int16_t)((src[i + 1] << 8) | (src[i + 0] & 0xFF)) / 32768.0); + bfr[high] = (float)((int16_t)((src[i + 3] << 8) | (src[i + 2] & 0xFF)) / 32768.0); + fade_fx(); + high++; + i += 4; + } + } + buff_cycle(); } void read_to_buffer_s16int_resample(int16_t src[], int n_samples) { - int i = 0; - int f = 0; + int i = 0; + int f = 0; - // Convert int16 to float - while (i < n_samples) { - re_in[f * 2] = (src[i]) / 32768.0; - if (src_channels == 1) { - re_in[(f * 2) + 1] = re_in[f * 2]; - i += 1; - } else { - re_in[(f * 2) + 1] = (src[i + 1]) / 32768.0; - i += 2; - } + // Convert int16 to float + while (i < n_samples) { + re_in[f * 2] = (src[i]) / 32768.0; + if (src_channels == 1) { + re_in[(f * 2) + 1] = re_in[f * 2]; + i += 1; + } else { + re_in[(f * 2) + 1] = (src[i + 1]) / 32768.0; + i += 2; + } - f++; - } + f++; + } - resample_to_buffer(f); + resample_to_buffer(f); } -void read_to_buffer_s16int(int16_t src[], int n_samples){ +void read_to_buffer_s16int(int16_t src[], int n_samples) { - if (sample_rate_src != sample_rate_out) { - read_to_buffer_s16int_resample(src, n_samples); - return; - } + if (sample_rate_src != sample_rate_out) { + read_to_buffer_s16int_resample(src, n_samples); + return; + } - int i = 0; - if (src_channels == 1){ - while (i < n_samples){ - bfl[high] = src[i] / 32768.0; - bfr[high] = bfl[high]; - fade_fx(); + int i = 0; + if (src_channels == 1) { + while (i < n_samples) { + bfl[high] = src[i] / 32768.0; + bfr[high] = bfl[high]; + fade_fx(); - i+=1; - //buff_filled++; - high++; - } - buff_cycle(); + i+=1; + //buff_filled++; + high++; + } + buff_cycle(); - } else { - while (i < n_samples){ - bfl[high] = src[i] / 32768.0; - bfr[high] = src[i + 1] / 32768.0; - fade_fx(); + } else { + while (i < n_samples) { + bfl[high] = src[i] / 32768.0; + bfr[high] = src[i + 1] / 32768.0; + fade_fx(); - i+=2; - high++; - } - buff_cycle(); - } + i+=2; + high++; + } + buff_cycle(); + } } // FLAC related --------------------------------------------------------------- FLAC__StreamDecoderWriteStatus f_write(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 *const buffer[], - void *client_data) { + void *client_data) { - //printf("Frame size is: %d\n", frame->header.blocksize); - //printf("Resolution is: %d\n", frame->header.bits_per_sample); - //printf("Samplerate is: %d\n", frame->header.sample_rate); - //printf("Channels is : %d\n", frame->header.channels); + //printf("Frame size is: %d\n", frame->header.blocksize); + //printf("Resolution is: %d\n", frame->header.bits_per_sample); + //printf("Samplerate is: %d\n", frame->header.sample_rate); + //printf("Channels is : %d\n", frame->header.channels); - pthread_mutex_lock(&buffer_mutex); + pthread_mutex_lock(&buffer_mutex); - /* if (frame->header.sample_rate != current_sample_rate){ */ - /* if (want_sample_rate != frame->header.sample_rate){ */ - /* want_sample_rate = frame->header.sample_rate; */ - /* sample_change_byte = (buff_filled + buff_base) % BUFF_SIZE; */ - /* } */ - /* } */ + /* if (frame->header.sample_rate != current_sample_rate) { */ + /* if (want_sample_rate != frame->header.sample_rate) { */ + /* want_sample_rate = frame->header.sample_rate; */ + /* sample_change_byte = (buff_filled + buff_base) % BUFF_SIZE; */ + /* } */ + /* } */ // if (sample_rate_out != current_sample_rate) { // if (want_sample_rate != sample_rate_out) { @@ -905,113 +906,113 @@ f_write(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC // } // } - if (current_length_count == 0) { - current_length_count = FLAC__stream_decoder_get_total_samples(decoder); - } + if (current_length_count == 0) { + current_length_count = FLAC__stream_decoder_get_total_samples(decoder); + } - unsigned int i = 0; - int resample = 0; - int old_sample_rate = sample_rate_src; - sample_rate_src = frame->header.sample_rate; - flac_got_rate = 1; - if (old_sample_rate != sample_rate_src) { - src_reset(src); - } - if (sample_rate_src != sample_rate_out && config_resample == 1) { - resample = 1; - } + unsigned int i = 0; + int resample = 0; + int old_sample_rate = sample_rate_src; + sample_rate_src = frame->header.sample_rate; + flac_got_rate = 1; + if (old_sample_rate != sample_rate_src) { + src_reset(src); + } + if (sample_rate_src != sample_rate_out && config_resample == 1) { + resample = 1; + } - if (load_target_seek > 0) { - pthread_mutex_unlock(&buffer_mutex); - return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; - } + if (load_target_seek > 0) { + pthread_mutex_unlock(&buffer_mutex); + return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; + } - if (frame->header.blocksize > (BUFF_SIZE - get_buff_fill())) { - printf("pa: critical: BUFFER OVERFLOW!"); - } + if (frame->header.blocksize > (BUFF_SIZE - get_buff_fill())) { + printf("pa: critical: BUFFER OVERFLOW!"); + } - int temp_fill = 0; + int temp_fill = 0; - if (resample == 0) { + if (resample == 0) { - // No resampling needed, transfer data to main buffer + // No resampling needed, transfer data to main buffer - while (i < frame->header.blocksize) { + while (i < frame->header.blocksize) { - // Read and handle 24bit audio - if (frame->header.bits_per_sample == 24) { + // Read and handle 24bit audio + if (frame->header.bits_per_sample == 24) { - bfl[high] = (buffer[0][i]) / 8388608.0; + bfl[high] = (buffer[0][i]) / 8388608.0; - if (frame->header.channels == 1) { - bfr[high] = bfl[high]; - } else { - bfr[high] = (buffer[1][i]) / 8388608.0; - } - } // end 24 bit audio + if (frame->header.channels == 1) { + bfr[high] = bfl[high]; + } else { + bfr[high] = (buffer[1][i]) / 8388608.0; + } + } // end 24 bit audio - // Read 16bit audio - else if (frame->header.bits_per_sample == 16) { - bfl[high] = (buffer[0][i]) / 32768.0; - if (frame->header.channels == 1) { - bfr[high] = (buffer[0][i]) / 32768.0; - } else { - bfr[high] = (buffer[1][i]) / 32768.0; - } - } else printf("ph: CRITIAL ERROR - INVALID BIT DEPTH!\n"); + // Read 16bit audio + else if (frame->header.bits_per_sample == 16) { + bfl[high] = (buffer[0][i]) / 32768.0; + if (frame->header.channels == 1) { + bfr[high] = (buffer[0][i]) / 32768.0; + } else { + bfr[high] = (buffer[1][i]) / 32768.0; + } + } else printf("ph: CRITIAL ERROR - INVALID BIT DEPTH!\n"); - fade_fx(); + fade_fx(); - high++; - i++; - } + high++; + i++; + } - buff_cycle(); + buff_cycle(); - } else { + } else { - // Transfer data to resampler for resampling + // Transfer data to resampler for resampling - while (i < frame->header.blocksize) { - // Read and handle 24bit audio - if (frame->header.bits_per_sample == 24) { + while (i < frame->header.blocksize) { + // Read and handle 24bit audio + if (frame->header.bits_per_sample == 24) { - re_in[i * 2] = (buffer[0][i]) / (8388608.0); - if (frame->header.channels == 1) re_in[(i * 2) + 1] = re_in[i * 2]; - else re_in[(i * 2) + 1] = (buffer[1][i]) / 8388608.0; + re_in[i * 2] = (buffer[0][i]) / (8388608.0); + if (frame->header.channels == 1) re_in[(i * 2) + 1] = re_in[i * 2]; + else re_in[(i * 2) + 1] = (buffer[1][i]) / 8388608.0; - } // end 24 bit audio + } // end 24 bit audio - // Read 16bit audio - else if (frame->header.bits_per_sample == 16) { + // Read 16bit audio + else if (frame->header.bits_per_sample == 16) { - re_in[i * 2] = (buffer[0][i]) / 32768.0; - if (frame->header.channels == 1) re_in[(i * 2) + 1] = re_in[i * 2]; - else re_in[(i * 2) + 1] = (buffer[1][i]) / 32768.0; + re_in[i * 2] = (buffer[0][i]) / 32768.0; + if (frame->header.channels == 1) re_in[(i * 2) + 1] = re_in[i * 2]; + else re_in[(i * 2) + 1] = (buffer[1][i]) / 32768.0; - } else printf("ph: CRITIAL ERROR - INVALID BIT DEPTH!\n"); + } else printf("ph: CRITIAL ERROR - INVALID BIT DEPTH!\n"); - temp_fill++; - i++; + temp_fill++; + i++; - } + } - resample_to_buffer(temp_fill); + resample_to_buffer(temp_fill); - } + } - pthread_mutex_unlock(&buffer_mutex); - return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; + pthread_mutex_unlock(&buffer_mutex); + return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; } void f_meta(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data) { - printf("GOT META\n"); + printf("GOT META\n"); } void f_err(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data) { - printf("GOT FLAC ERR\n"); + printf("GOT FLAC ERR\n"); } @@ -1025,1386 +1026,1344 @@ int want_reconnect = 0; void stop_decoder() { - if (decoder_allocated == 0) return; - - switch (codec) { - case OPUS: - op_free(opus_dec); - break; - case VORBIS: - ov_clear(&vf); - break; - case FLAC: - FLAC__stream_decoder_finish(dec); - break; - case WAVPACK: - WavpackCloseFile(wpc); - break; - case MPG: - mpg123_close(mh); - break; - case FFMPEG: - stop_ffmpeg(); - break; - case WAVE: - wave_close(); - break; - case MPT: - openmpt_module_destroy(mod); - break; - case GME: - gme_delete(emu); - break; - } - //src_reset(src); - decoder_allocated = 0; + if (decoder_allocated == 0) return; + + switch (codec) { + case OPUS: + op_free(opus_dec); + break; + case VORBIS: + ov_clear(&vf); + break; + case FLAC: + FLAC__stream_decoder_finish(dec); + break; + case WAVPACK: + WavpackCloseFile(wpc); + break; + case MPG: + mpg123_close(mh); + break; + case FFMPEG: + stop_ffmpeg(); + break; + case WAVE: + wave_close(); + break; + case MPT: + openmpt_module_destroy(mod); + break; + case GME: + gme_delete(emu); + break; + } + //src_reset(src); + decoder_allocated = 0; } float gate = 1.0; // Used for ramping -int get_audio(int max, float* buff){ - int b = 0; +int get_audio(int max, float* buff) { + int b = 0; - pthread_mutex_lock(&buffer_mutex); + pthread_mutex_lock(&buffer_mutex); - if (buffering == 1 && get_buff_fill() > config_min_buffer) { - buffering = 0; - printf("pa: Buffering -> Playing\n"); - } + if (buffering == 1 && get_buff_fill() > config_min_buffer) { + buffering = 0; + printf("pa: Buffering -> Playing\n"); + } - if (get_buff_fill() < 10 && loaded_target_file[0] == 'h') { + if (get_buff_fill() < 10 && loaded_target_file[0] == 'h') { - if (mode == PLAYING) { - if (buffering == 0) printf("pa: Buffering...\n"); - buffering = 1; - } else buffering = 0; - } + if (mode == PLAYING) { + if (buffering == 0) printf("pa: Buffering...\n"); + buffering = 1; + } else buffering = 0; + } -// if (get_buff_fill() < max && mode == PLAYING && decoder_allocated == 1) { -// //printf("pa: Buffer underrun\n"); -// } +// if (get_buff_fill() < max && mode == PLAYING && decoder_allocated == 1) { +// //printf("pa: Buffer underrun\n"); +// } - // Put fade buffer back - if (mode == PLAYING && fade_fill > 0 && get_buff_fill() < max && fade_lockout == 0){ - //pthread_mutex_lock(&buffer_mutex); - int i = 0; - while (fade_position < fade_fill){ - float cross = fade_position / (float) fade_fill; - float cross_i = 1.0 - cross; - bfl[high] = fadefl[fade_position] * cross_i; - bfr[high] = fadefr[fade_position] * cross_i; - fade_position++; - high++; - i++; - if (i > max) break; - } - buff_cycle(); - if (fade_position == fade_fill){ - fade_fill = 0; - fade_position = 0; - } - //pthread_mutex_unlock(&buffer_mutex); - } + // Put fade buffer back + if (mode == PLAYING && fade_fill > 0 && get_buff_fill() < max && fade_lockout == 0) { + //pthread_mutex_lock(&buffer_mutex); + int i = 0; + while (fade_position < fade_fill) { + float cross = fade_position / (float) fade_fill; + float cross_i = 1.0 - cross; + bfl[high] = fadefl[fade_position] * cross_i; + bfr[high] = fadefr[fade_position] * cross_i; + fade_position++; + high++; + i++; + if (i > max) break; + } + buff_cycle(); + if (fade_position == fade_fill) { + fade_fill = 0; + fade_position = 0; + } + //pthread_mutex_unlock(&buffer_mutex); + } - if (mode == PAUSED || (mode == PLAYING && get_buff_fill() == 0)){ + if (mode == PAUSED || (mode == PLAYING && get_buff_fill() == 0)) { - } - // Process decoded audio data and send out - else if ((mode == PLAYING || mode == RAMP_DOWN || mode == ENDING) && get_buff_fill() > 0 && buffering == 0) { + } + // Process decoded audio data and send out + else if ((mode == PLAYING || mode == RAMP_DOWN || mode == ENDING) && get_buff_fill() > 0 && buffering == 0) { - //pthread_mutex_lock(&buffer_mutex); + //pthread_mutex_lock(&buffer_mutex); - b = 0; // byte number + b = 0; // byte number - peak_roll_l = 0; - peak_roll_r = 0; + peak_roll_l = 0; + peak_roll_r = 0; - //printf("pa: Buffer is at %d\n", buff_filled); + //printf("pa: Buffer is at %d\n", buff_filled); - // Fill the out buffer... - while (get_buff_fill() > 0) { + // Fill the out buffer... + while (get_buff_fill() > 0) { - // Truncate data if gate is closed anyway - if (mode == RAMP_DOWN && gate == 0) break; + // Truncate data if gate is closed anyway + if (mode == RAMP_DOWN && gate == 0) break; -// if (want_sample_rate > 0 && sample_change_byte == buff_base) { -// //printf("pa: Set new sample rate\n"); -// connect_pulse(); -// break; -// } +// if (want_sample_rate > 0 && sample_change_byte == buff_base) { +// //printf("pa: Set new sample rate\n"); +// connect_pulse(); +// break; +// } - if (reset_set == 1 && reset_set_byte == low) { - //printf("pa: Reset position counter\n"); - reset_set = 0; - position_count = reset_set_value; - } + if (reset_set == 1 && reset_set_byte == low) { + //printf("pa: Reset position counter\n"); + reset_set = 0; + position_count = reset_set_value; + } - // Ramp control --- - if (mode == RAMP_DOWN) { - gate -= ramp_step(current_sample_rate, 5); - if (gate < 0) gate = 0; - } + // Ramp control --- + if (mode == RAMP_DOWN) { + gate -= ramp_step(current_sample_rate, 5); + if (gate < 0) gate = 0; + } - if (gate < 1 && mode == PLAYING) { - gate += ramp_step(current_sample_rate, 5); - if (gate > 1) gate = 1; - } + if (gate < 1 && mode == PLAYING) { + gate += ramp_step(current_sample_rate, 5); + if (gate > 1) gate = 1; + } - // Volume control --- - if (volume_want > volume_on) { - volume_on += ramp_step(current_sample_rate, volume_ramp_speed); + // Volume control --- + if (volume_want > volume_on) { + volume_on += ramp_step(current_sample_rate, volume_ramp_speed); - if (volume_on > volume_want) { - volume_on = volume_want; - } - } + if (volume_on > volume_want) { + volume_on = volume_want; + } + } - if (volume_want < volume_on) { - volume_on -= ramp_step(current_sample_rate, volume_ramp_speed); + if (volume_want < volume_on) { + volume_on -= ramp_step(current_sample_rate, volume_ramp_speed); - if (volume_on < volume_want) { - volume_on = volume_want; - } - } + if (volume_on < volume_want) { + volume_on = volume_want; + } + } - float l = bfl[low]; - float r = bfr[low]; + float l = bfl[low]; + float r = bfr[low]; - if (fabs(l) > peak_roll_l) peak_roll_l = fabs(l); - if (fabs(r) > peak_roll_r) peak_roll_r = fabs(r); + if (fabs(l) > peak_roll_l) peak_roll_l = fabs(l); + if (fabs(r) > peak_roll_r) peak_roll_r = fabs(r); - // Apply final volume adjustment - float final_vol = pow((gate * volume_on), config_volume_power); - l = l * final_vol; - r = r * final_vol; + // Apply final volume adjustment + float final_vol = pow((gate * volume_on), config_volume_power); + l = l * final_vol; + r = r * final_vol; - buff[b] = l; - buff[b + 1] = r; - b += 2; + buff[b] = l; + buff[b + 1] = r; + b += 2; - low += 1; - buff_cycle(); + low += 1; + buff_cycle(); - position_count++; + position_count++; - if (b >= max) break; // Buffer is now full - } + if (b >= max) break; // Buffer is now full + } - if (b > 0) { - if (peak_roll_l > peak_l) peak_l = peak_roll_l; - if (peak_roll_r > peak_r) peak_r = peak_roll_r; - pthread_mutex_unlock(&buffer_mutex); - return b; + if (b > 0) { + if (peak_roll_l > peak_l) peak_l = peak_roll_l; + if (peak_roll_r > peak_r) peak_r = peak_roll_r; + pthread_mutex_unlock(&buffer_mutex); + return b; - } // sent data + } // sent data - } // close if data - memset(buff, 0, max * sizeof(float)); - pthread_mutex_unlock(&buffer_mutex); - return max; + } // close if data + memset(buff, 0, max * sizeof(float)); + pthread_mutex_unlock(&buffer_mutex); + return max; } #ifdef PIPE - -static void on_process(void *userdata) { - //struct pw_stream *stream = userdata; - struct pw_buffer *buffer; - struct spa_buffer *buf; - //void *data; - int size; - - - if ((buffer = pw_stream_dequeue_buffer(global_stream)) == NULL) - return; - - buf = buffer->buffer; - - size = get_audio(buffer->requested * 2, buf->datas[0].data) * 4; - - buf->datas[0].chunk->size = size; - pw_stream_queue_buffer(global_stream, buffer); - -} - -static const struct pw_stream_events stream_events = { - PW_VERSION_STREAM_EVENTS, - .process = on_process, -}; - - -void *pipewire_main_loop_thread(void *thread_id) { - - printf("Begin Pipewire init...\n"); - pw_init(NULL, NULL); - - loop = pw_main_loop_new(NULL /* properties */); - if (loop == NULL) { - fprintf(stderr, "Error: Failed to create main loop\n"); - return thread_id; - } - - context = pw_context_new(pw_main_loop_get_loop(loop), - NULL /* properties */, - 0 /* user_data size */); - if (context == NULL) { - fprintf(stderr, "Error: Failed to create context\n"); - return thread_id; - } - - core = pw_context_connect(context, - NULL /* properties */, - 0 /* user_data size */); - if (core == NULL) { - fprintf(stderr, "Error: Failed to connect to PipeWire\n"); - return thread_id; - } - - registry = pw_core_get_registry(core, PW_VERSION_REGISTRY, - 0 /* user_data size */); - if (registry == NULL) { - fprintf(stderr, "Error: Failed to get registry\n"); - return thread_id; - } - - spa_zero(registry_listener); - int res; - res = pw_registry_add_listener(registry, ®istry_listener, - ®istry_events, NULL); - - if (res < 0) { - fprintf(stderr, "Error: Failed to add registry listener: %s\n", spa_strerror(res)); - return thread_id; - } - - res = pw_core_add_listener(core, &core_listener, - &core_events, NULL); - if (res < 0) { - fprintf(stderr, "Error: Failed to add core listener: %s\n", spa_strerror(res)); - return thread_id; - } - pw_core_sync(core, PW_ID_CORE, 0); - - - global_stream = pw_stream_new_simple( - pw_main_loop_get_loop(loop), - "Tauon", - pw_properties_new( - PW_KEY_MEDIA_TYPE, "Audio", - PW_KEY_MEDIA_CATEGORY, "Playback", - PW_KEY_MEDIA_ROLE, "Music", - NULL), - &stream_events, - NULL - ); - if (global_stream == NULL) { - fprintf(stderr, "Error: Failed to create stream\n"); - return thread_id; - } - //printf("Run pipewire main loop...\n"); - res = pw_main_loop_run(loop); - - if (res < 0) { - fprintf(stderr, "Error: Main loop run failed: %s\n", spa_strerror(res)); - return thread_id; - } - - - if (registry) { - pw_proxy_destroy((struct pw_proxy*)registry); - } - if (core) { - spa_hook_remove(&core_listener); - pw_core_disconnect(core); - } - if (global_stream) { - pw_stream_destroy(global_stream); - } - if (context) { - pw_context_destroy(context); - } - if (loop) { - pw_main_loop_destroy(loop); - } - pw_deinit(); - //printf("Exit pipewire main loop\n"); - return thread_id; - -} - + static void on_process(void *userdata) { + //struct pw_stream *stream = userdata; + struct pw_buffer *buffer; + struct spa_buffer *buf; + //void *data; + int size; + + + if ((buffer = pw_stream_dequeue_buffer(global_stream)) == NULL) + return; + + buf = buffer->buffer; + + size = get_audio(buffer->requested * 2, buf->datas[0].data) * 4; + + buf->datas[0].chunk->size = size; + pw_stream_queue_buffer(global_stream, buffer); + + } + + static const struct pw_stream_events stream_events = { + PW_VERSION_STREAM_EVENTS, + .process = on_process, + }; + + + void *pipewire_main_loop_thread(void *thread_id) { + + printf("Begin Pipewire init...\n"); + pw_init(NULL, NULL); + + loop = pw_main_loop_new(NULL /* properties */); + if (loop == NULL) { + fprintf(stderr, "Error: Failed to create main loop\n"); + return thread_id; + } + + context = pw_context_new( + pw_main_loop_get_loop(loop), + NULL /* properties */, + 0 /* user_data size */); + if (context == NULL) { + fprintf(stderr, "Error: Failed to create context\n"); + return thread_id; + } + + core = pw_context_connect( + context, + NULL /* properties */, + 0 /* user_data size */); + if (core == NULL) { + fprintf(stderr, "Error: Failed to connect to PipeWire\n"); + return thread_id; + } + + registry = pw_core_get_registry(core, PW_VERSION_REGISTRY, + 0 /* user_data size */); + if (registry == NULL) { + fprintf(stderr, "Error: Failed to get registry\n"); + return thread_id; + } + + spa_zero(registry_listener); + int res; + res = pw_registry_add_listener(registry, ®istry_listener, ®istry_events, NULL); + + if (res < 0) { + fprintf(stderr, "Error: Failed to add registry listener: %s\n", spa_strerror(res)); + return thread_id; + } + + res = pw_core_add_listener(core, &core_listener, &core_events, NULL); + if (res < 0) { + fprintf(stderr, "Error: Failed to add core listener: %s\n", spa_strerror(res)); + return thread_id; + } + pw_core_sync(core, PW_ID_CORE, 0); + + + global_stream = pw_stream_new_simple( + pw_main_loop_get_loop(loop), + "Tauon", + pw_properties_new( + PW_KEY_MEDIA_TYPE, "Audio", + PW_KEY_MEDIA_CATEGORY, "Playback", + PW_KEY_MEDIA_ROLE, "Music", + NULL), + &stream_events, + NULL + ); + if (global_stream == NULL) { + fprintf(stderr, "Error: Failed to create stream\n"); + return thread_id; + } + //printf("Run pipewire main loop...\n"); + res = pw_main_loop_run(loop); + + if (res < 0) { + fprintf(stderr, "Error: Main loop run failed: %s\n", spa_strerror(res)); + return thread_id; + } + + + if (registry) { + pw_proxy_destroy((struct pw_proxy*)registry); + } + if (core) { + spa_hook_remove(&core_listener); + pw_core_disconnect(core); + } + if (global_stream) { + pw_stream_destroy(global_stream); + } + if (context) { + pw_context_destroy(context); + } + if (loop) { + pw_main_loop_destroy(loop); + } + pw_deinit(); + //printf("Exit pipewire main loop\n"); + return thread_id; + } #endif #ifdef MINI -void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount){ - - get_audio(frameCount * 2, pOutput); - //if (0 < b && b < frameCount) printf("ph: Buffer underrun\n"); -} - -void notification_callback(const ma_device_notification* pNotification) { - if (pNotification->type == ma_device_notification_type_stopped) { - device_stopped = 1; - signaled_device_unavailable = 0; - } -} - -ma_device_info* pPlaybackDeviceInfos; -ma_uint32 playbackDeviceCount = 0; -ma_result result; -ma_context context; -int context_allocated = 0; -ma_uint32 iDevice; - - -int initiate_ma_context(){ - if (context_allocated == 0){ - if (ma_context_init(NULL, 0, NULL, &context) != MA_SUCCESS) { - printf("Failed to initialize context.\n"); - return -1; - } - context_allocated = 1; - } - return 1; -} - - -void my_log_callback(void* pUserData, ma_uint32 level, const char* pMessage) { - printf("Log [%u]: %s\n", level, pMessage); - // Additional logic for handling log messages can be added here -} - + void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) { + get_audio(frameCount * 2, pOutput); + //if (0 < b && b < frameCount) printf("ph: Buffer underrun\n"); + } + + void notification_callback(const ma_device_notification* pNotification) { + if (pNotification->type == ma_device_notification_type_stopped) { + device_stopped = 1; + signaled_device_unavailable = 0; + } + } + + ma_device_info* pPlaybackDeviceInfos; + ma_uint32 playbackDeviceCount = 0; + ma_result result; + ma_context context; + int context_allocated = 0; + ma_uint32 iDevice; + + int initiate_ma_context() { + if (context_allocated == 0) { + if (ma_context_init(NULL, 0, NULL, &context) != MA_SUCCESS) { + printf("Failed to initialize context.\n"); + return -1; + } + context_allocated = 1; + } + return 1; + } + + void my_log_callback(void* pUserData, ma_uint32 level, const char* pMessage) { + printf("Log [%u]: %s\n", level, pMessage); + // Additional logic for handling log messages can be added here + } #endif - - -int scan_devices(){ - - #ifdef MINI - if (initiate_ma_context() == -1) return -1; - result = ma_context_get_devices(&context, &pPlaybackDeviceInfos, &playbackDeviceCount, NULL, NULL); - if (result != MA_SUCCESS) { - printf("Failed to retrieve device information.\n"); - return -2; - } - return playbackDeviceCount; - #endif - - #ifdef PIPE - while (enum_done != 1){ - usleep(10000); - } - return pipe_devices.device_count; - #endif -} - - void decode_seek(int abs_ms, int sample_rate) { - - switch (codec) { - case FLAC: - FLAC__stream_decoder_seek_absolute(dec, (int) sample_rate * (abs_ms / 1000.0)); - break; - case OPUS: - op_pcm_seek(opus_dec, (int) sample_rate * (abs_ms / 1000.0)); - samples_decoded = sample_rate * (abs_ms / 1000.0) * 2; - break; - case VORBIS: - ov_pcm_seek(&vf, (ogg_int64_t) sample_rate * (abs_ms / 1000.0)); - break; - case WAVPACK: - WavpackSeekSample64(wpc, (int64_t) sample_rate * (abs_ms / 1000.0)); - break; - case MPG: - mpg123_seek(mh, (int) sample_rate * (abs_ms / 1000.0), SEEK_SET); - break; - case FFMPEG: - stop_ffmpeg(); - start_ffmpeg(loaded_target_file, abs_ms); - break; - case WAVE: - wave_seek((int) sample_rate * (abs_ms / 1000.0)); - break; - case MPT: - openmpt_module_set_position_seconds(mod, abs_ms / 1000.0); - break; - case GME: - gme_seek(emu, (long) abs_ms); - break; - } + switch (codec) { + case FLAC: + FLAC__stream_decoder_seek_absolute(dec, (int) sample_rate * (abs_ms / 1000.0)); + break; + case OPUS: + op_pcm_seek(opus_dec, (int) sample_rate * (abs_ms / 1000.0)); + samples_decoded = sample_rate * (abs_ms / 1000.0) * 2; + break; + case VORBIS: + ov_pcm_seek(&vf, (ogg_int64_t) sample_rate * (abs_ms / 1000.0)); + break; + case WAVPACK: + WavpackSeekSample64(wpc, (int64_t) sample_rate * (abs_ms / 1000.0)); + break; + case MPG: + mpg123_seek(mh, (int) sample_rate * (abs_ms / 1000.0), SEEK_SET); + break; + case FFMPEG: + stop_ffmpeg(); + start_ffmpeg(loaded_target_file, abs_ms); + break; + case WAVE: + wave_seek((int) sample_rate * (abs_ms / 1000.0)); + break; + case MPT: + openmpt_module_set_position_seconds(mod, abs_ms / 1000.0); + break; + case GME: + gme_seek(emu, (long) abs_ms); + break; + } } #ifdef PIPE - -static int pipe_disconnect(struct spa_loop *loop, bool async, uint32_t seq, - const void *_data, size_t size, void *user_data){ - return pw_stream_disconnect(global_stream); - } - + static int pipe_disconnect(struct spa_loop *loop, bool async, uint32_t seq, const void *_data, size_t size, void *user_data) { + return pw_stream_disconnect(global_stream); + } #endif int disconnect_pulse() { - //printf("ph: Disconnect Device\n"); - - if (pulse_connected == 1) { - #ifdef MINI - ma_device_uninit(&device); - #endif + //printf("ph: Disconnect Device\n"); - #ifdef PIPE - pw_loop_invoke(pw_main_loop_get_loop(loop), pipe_disconnect, SPA_ID_INVALID, NULL, 0, - true, NULL); + if (pulse_connected == 1) { + #ifdef MINI + ma_device_uninit(&device); + #endif - #endif + #ifdef PIPE + pw_loop_invoke(pw_main_loop_get_loop(loop), pipe_disconnect, SPA_ID_INVALID, NULL, 0, true, NULL); + #endif - } - pulse_connected = 0; - gate = 0.0; - return 0; + } + pulse_connected = 0; + gate = 0.0; + return 0; } #ifdef PIPE - -static int pipe_exit(struct spa_loop *loopo, bool async, uint32_t seq, - const void *_data, size_t size, void *user_data){ - - pw_main_loop_quit(loop); - return 0; - } - -struct pw_stream *global_stream = NULL; // Initialize appropriately - -static int pipe_connect(struct spa_loop *loop, bool async, uint32_t seq, - const void *_data, size_t size, void *user_data) { - - struct spa_pod_builder b = { 0 }; - uint8_t buffer[POD_BUFFER_SIZE]; - const struct spa_pod *params[1]; - int ret; - - // Initialize the pod builder - spa_pod_builder_init(&b, buffer, sizeof(buffer)); - - // Build audio format parameters - params[0] = spa_format_audio_raw_build(&b, SPA_PARAM_EnumFormat, - &SPA_AUDIO_INFO_RAW_INIT( - .format = SPA_AUDIO_FORMAT_F32, - .channels = 2, - .rate = pipe_set_samplerate)); - if (params[0] == NULL) { - fprintf(stderr, "Failed to build audio format parameters\n"); - return -EINVAL; - } - - // Select the appropriate device - ssize_t selected_index = -1; - - pthread_mutex_lock(&pipe_devices_mutex); - for (size_t i = 0; i < pipe_devices.device_count; i++) { - if (strcmp(pipe_devices.devices[i].description, config_output_sink) == 0) { - selected_index = i; - break; // Stop at the first match - } - } - pthread_mutex_unlock(&pipe_devices_mutex); - - // Get and copy stream properties - const struct pw_properties *props = pw_stream_get_properties(global_stream); - if (props == NULL) { - fprintf(stderr, "Failed to get stream properties\n"); - return -EINVAL; - } - - struct pw_properties *mutable_props = pw_properties_copy(props); - if (mutable_props == NULL) { - fprintf(stderr, "Failed to copy stream properties\n"); - return -ENOMEM; - } - - // Set the target device if selected - if (selected_index != -1) { - pthread_mutex_lock(&pipe_devices_mutex); - const char *device_name = pipe_devices.devices[selected_index].name; - pthread_mutex_unlock(&pipe_devices_mutex); - - if (device_name) { - pw_properties_set(mutable_props, PW_KEY_TARGET_OBJECT, device_name); - printf("Selected device index: %zu (%s)\n", selected_index, device_name); - } else { - fprintf(stderr, "Selected device has no name\n"); - pw_properties_set(mutable_props, PW_KEY_TARGET_OBJECT, ""); - } - } else { - // Optionally, handle the case where no device is selected - pw_properties_set(mutable_props, PW_KEY_TARGET_OBJECT, ""); - printf("Using default device.\n"); - } - - // Update the stream properties - ret = pw_stream_update_properties(global_stream, &mutable_props->dict); - if (ret < 0) { - fprintf(stderr, "Failed to update stream properties: %d\n", ret); - pw_properties_free(mutable_props); - return ret; - } - - pw_properties_free(mutable_props); - - // Connect the stream - ret = pw_stream_connect(global_stream, - PW_DIRECTION_OUTPUT, - PW_ID_ANY, - PW_STREAM_FLAG_AUTOCONNECT | - PW_STREAM_FLAG_MAP_BUFFERS | - PW_STREAM_FLAG_RT_PROCESS, - params, 1); - if (ret < 0) { - fprintf(stderr, "Failed to connect stream: %d\n", ret); - return ret; - } - - printf("Stream connected successfully.\n"); - return 0; // Success -} - -static int pipe_update(struct spa_loop *loop, bool async, uint32_t seq, - const void *_data, size_t size, void *user_data){ - - pw_stream_disconnect(global_stream); - return pipe_connect(loop, async, seq, _data, size, user_data); -} + static int pipe_exit( + struct spa_loop *loopo, bool async, uint32_t seq, const void *_data, size_t size, void *user_data + ) { + pw_main_loop_quit(loop); + return 0; + } + + struct pw_stream *global_stream = NULL; // Initialize appropriately + + static int pipe_connect( + struct spa_loop *loop, bool async, uint32_t seq, const void *_data, size_t size, void *user_data + ) { + + struct spa_pod_builder b = { 0 }; + uint8_t buffer[POD_BUFFER_SIZE]; + const struct spa_pod *params[1]; + int ret; + + // Initialize the pod builder + spa_pod_builder_init(&b, buffer, sizeof(buffer)); + + // Build audio format parameters + params[0] = spa_format_audio_raw_build( + &b, SPA_PARAM_EnumFormat, + &SPA_AUDIO_INFO_RAW_INIT( + .format = SPA_AUDIO_FORMAT_F32, + .channels = 2, + .rate = pipe_set_samplerate)); + if (params[0] == NULL) { + fprintf(stderr, "Failed to build audio format parameters\n"); + return -EINVAL; + } + + // Select the appropriate device + ssize_t selected_index = -1; + + pthread_mutex_lock(&pipe_devices_mutex); + for (size_t i = 0; i < pipe_devices.device_count; i++) { + if (strcmp(pipe_devices.devices[i].description, config_output_sink) == 0) { + selected_index = i; + break; // Stop at the first match + } + } + pthread_mutex_unlock(&pipe_devices_mutex); + + // Get and copy stream properties + const struct pw_properties *props = pw_stream_get_properties(global_stream); + if (props == NULL) { + fprintf(stderr, "Failed to get stream properties\n"); + return -EINVAL; + } + + struct pw_properties *mutable_props = pw_properties_copy(props); + if (mutable_props == NULL) { + fprintf(stderr, "Failed to copy stream properties\n"); + return -ENOMEM; + } + + // Set the target device if selected + if (selected_index != -1) { + pthread_mutex_lock(&pipe_devices_mutex); + const char *device_name = pipe_devices.devices[selected_index].name; + pthread_mutex_unlock(&pipe_devices_mutex); + + if (device_name) { + pw_properties_set(mutable_props, PW_KEY_TARGET_OBJECT, device_name); + printf("Selected device index: %zu (%s)\n", selected_index, device_name); + } else { + fprintf(stderr, "Selected device has no name\n"); + pw_properties_set(mutable_props, PW_KEY_TARGET_OBJECT, ""); + } + } else { + // Optionally, handle the case where no device is selected + pw_properties_set(mutable_props, PW_KEY_TARGET_OBJECT, ""); + printf("Using default device.\n"); + } + + // Update the stream properties + ret = pw_stream_update_properties(global_stream, &mutable_props->dict); + if (ret < 0) { + fprintf(stderr, "Failed to update stream properties: %d\n", ret); + pw_properties_free(mutable_props); + return ret; + } + + pw_properties_free(mutable_props); + + // Connect the stream + ret = pw_stream_connect( + global_stream, + PW_DIRECTION_OUTPUT, + PW_ID_ANY, + PW_STREAM_FLAG_AUTOCONNECT | + PW_STREAM_FLAG_MAP_BUFFERS | + PW_STREAM_FLAG_RT_PROCESS, + params, 1); + if (ret < 0) { + fprintf(stderr, "Failed to connect stream: %d\n", ret); + return ret; + } + + printf("Stream connected successfully.\n"); + return 0; // Success + } + + static int pipe_update(struct spa_loop *loop, bool async, uint32_t seq, + const void *_data, size_t size, void *user_data) { + + pw_stream_disconnect(global_stream); + return pipe_connect(loop, async, seq, _data, size, user_data); + } #endif void connect_pulse() { - if (pulse_connected == 1) { - //printf("pa: Reconnect\n"); - disconnect_pulse(); - - } - printf("ph: Connect\n"); - - #ifdef MINI - if (getenv("MA_DEBUG")) { - ma_result result; - ma_log logger; - - printf("Initialize logger.\n"); - - // Initialize the logger - result = ma_log_init(NULL, &logger); - if (result != MA_SUCCESS) { - printf("Failed to initialize logger.\n"); - return; - } - - // Create the log callback structure - ma_log_callback logCallback = ma_log_callback_init(my_log_callback, NULL); - - // Register the log callback - result = ma_log_register_callback(&logger, logCallback); - if (result != MA_SUCCESS) { - printf("Failed to register log callback.\n"); - ma_log_uninit(&logger); - return; - } - } - - int n = -1; - if (strcmp(config_output_sink, "Default") != 0){ - for (int i = 0; i < playbackDeviceCount; ++i) { - if (strcmp(pPlaybackDeviceInfos[i].name, config_output_sink) == 0){ - n = i; - } - } - } - - //printf("ph: Connect device\n"); - - c_config.pulse.pApplicationName = "Tauon Music Box"; - if (initiate_ma_context() == -1) return; - - result = ma_context_get_devices(&context, &pPlaybackDeviceInfos, &playbackDeviceCount, NULL, NULL); - if (result != MA_SUCCESS) { - printf("Failed to retrieve device information.\n"); - return; - } - - int set_samplerate = 0; - - if (sample_rate_src > 0) set_samplerate = sample_rate_src; - - ma_device_config config = ma_device_config_init(ma_device_type_playback); - if (n > -1) config.playback.pDeviceID = &pPlaybackDeviceInfos[n].id; - config.playback.format = ma_format_f32; // Set to ma_format_unknown to use the device's native format. - config.playback.channels = 2; // Set to 0 to use the device's native channel count. - config.sampleRate = set_samplerate; // Set to 0 to use the device's native sample rate. - config.dataCallback = data_callback; // This function will be called when miniaudio needs more data. - config.notificationCallback = notification_callback; - config.periodSizeInMilliseconds = config_dev_buffer / 4; - config.periods = 4; // - - ma_result result; - result = ma_device_init(&context, &config, &device); - if (result != MA_SUCCESS) { - printf("ph: Device init error\n"); - const char* description = ma_result_description(result); - printf("Result Description: %s\n", description); - mode = STOPPED; - return; // Failed to initialize the device. - } - - //dev = config_output_sink; - printf("ph: Connected using samplerate %uhz\n", device.sampleRate); - - sample_rate_out = device.sampleRate; - - #endif - - #ifdef PIPE - - int set_samplerate = 48000; - if (sample_rate_src > 0) pipe_set_samplerate = sample_rate_src; - printf("SET PIPE SAMPLERATE: %d\n", set_samplerate); - sample_rate_out = pipe_set_samplerate; - - pw_loop_invoke(pw_main_loop_get_loop(loop), pipe_connect, SPA_ID_INVALID, NULL, 0, - true, NULL); - - #endif - - if (decoder_allocated == 1 && current_sample_rate > 0 && - sample_rate_out > 0 && position_count > get_buff_fill() && - current_sample_rate != sample_rate_out && position_count > 0 && get_buff_fill() > 0){ - - src_reset(src); - printf("ph: The samplerate changed, rewinding\n"); - if (reset_set == 0){ - decode_seek(position_count / sample_rate_src * 1000, sample_rate_src); - } - - buff_reset(); - } - - current_sample_rate = sample_rate_out; - - pulse_connected = 1; - -} - -FILE *uni_fopen(char *ff){ - #ifdef WIN - wchar_t w_path[MAX_PATH]; - MultiByteToWideChar(CP_UTF8, 0, ff, -1, w_path, MAX_PATH); - FILE *file = _wfopen(w_path, L"rb"); - return file; - - #else - return fopen(ff, "rb"); - #endif + if (pulse_connected == 1) { + //printf("pa: Reconnect\n"); + disconnect_pulse(); + + } + printf("ph: Connect\n"); + + #ifdef MINI + if (getenv("MA_DEBUG")) { + ma_result result; + ma_log logger; + + printf("Initialize logger.\n"); + + // Initialize the logger + result = ma_log_init(NULL, &logger); + if (result != MA_SUCCESS) { + printf("Failed to initialize logger.\n"); + return; + } + + // Create the log callback structure + ma_log_callback logCallback = ma_log_callback_init(my_log_callback, NULL); + + // Register the log callback + result = ma_log_register_callback(&logger, logCallback); + if (result != MA_SUCCESS) { + printf("Failed to register log callback.\n"); + ma_log_uninit(&logger); + return; + } + } + + int n = -1; + if (strcmp(config_output_sink, "Default") != 0) { + for (int i = 0; i < playbackDeviceCount; ++i) { + if (strcmp(pPlaybackDeviceInfos[i].name, config_output_sink) == 0) { + n = i; + } + } + } + + //printf("ph: Connect device\n"); + + c_config.pulse.pApplicationName = "Tauon Music Box"; + if (initiate_ma_context() == -1) return; + + result = ma_context_get_devices(&context, &pPlaybackDeviceInfos, &playbackDeviceCount, NULL, NULL); + if (result != MA_SUCCESS) { + printf("Failed to retrieve device information.\n"); + return; + } + + int set_samplerate = 0; + + if (sample_rate_src > 0) set_samplerate = sample_rate_src; + + ma_device_config config = ma_device_config_init(ma_device_type_playback); + if (n > -1) config.playback.pDeviceID = &pPlaybackDeviceInfos[n].id; + config.playback.format = ma_format_f32; // Set to ma_format_unknown to use the device's native format. + config.playback.channels = 2; // Set to 0 to use the device's native channel count. + config.sampleRate = set_samplerate; // Set to 0 to use the device's native sample rate. + config.dataCallback = data_callback; // This function will be called when miniaudio needs more data. + config.notificationCallback = notification_callback; + config.periodSizeInMilliseconds = config_dev_buffer / 4; + config.periods = 4; // + + ma_result result; + result = ma_device_init(&context, &config, &device); + if (result != MA_SUCCESS) { + printf("ph: Device init error\n"); + const char* description = ma_result_description(result); + printf("Result Description: %s\n", description); + mode = STOPPED; + return; // Failed to initialize the device. + } + + //dev = config_output_sink; + printf("ph: Connected using samplerate %uhz\n", device.sampleRate); + + sample_rate_out = device.sampleRate; + #endif + + #ifdef PIPE + int set_samplerate = 48000; + if (sample_rate_src > 0) pipe_set_samplerate = sample_rate_src; + printf("SET PIPE SAMPLERATE: %d\n", set_samplerate); + sample_rate_out = pipe_set_samplerate; + + pw_loop_invoke(pw_main_loop_get_loop(loop), pipe_connect, SPA_ID_INVALID, NULL, 0, true, NULL); + #endif + + if (decoder_allocated == 1 && current_sample_rate > 0 && + sample_rate_out > 0 && position_count > get_buff_fill() && + current_sample_rate != sample_rate_out && position_count > 0 && get_buff_fill() > 0) { + + src_reset(src); + printf("ph: The samplerate changed, rewinding\n"); + if (reset_set == 0) { + decode_seek(position_count / sample_rate_src * 1000, sample_rate_src); + } + + buff_reset(); + } + + current_sample_rate = sample_rate_out; + + pulse_connected = 1; + +} + +FILE *uni_fopen(char *ff) { + #ifdef WIN64 + wchar_t w_path[MAX_PATH]; + MultiByteToWideChar(CP_UTF8, 0, ff, -1, w_path, MAX_PATH); + FILE *file = _wfopen(w_path, L"rb"); + return file; + #else + return fopen(ff, "rb"); + #endif } int load_next() { - // Function to load a file / prepare decoder - - stop_decoder(); - - strcpy(loaded_target_file, load_target_file); - - int channels; - int encoding; - long rate; - int e = 0; - int old_sample_rate = sample_rate_src; - src_channels = 2; - - char *ext; - ext = strrchr(loaded_target_file, '.'); - - codec = UNKNOWN; - current_length_count = 0; - buffering = 0; - samples_decoded = 0; - - if (loaded_target_file[0] == 'h') buffering = 1; - - rg_byte = high; - - char peak[35]; - - if (strcmp(loaded_target_file, "RAW FEED") == 0){ - codec = FEED; - load_target_seek = 0; - pthread_mutex_lock(&buffer_mutex); -// if (current_sample_rate != sample_rate_out) { -// sample_change_byte = high; -// want_sample_rate = config_feed_samplerate; -// } - sample_rate_src = config_feed_samplerate; - src_reset(src); - pthread_mutex_unlock(&buffer_mutex); - decoder_allocated = 1; - buffering = 1; - return 0; - } - - // If target is url, use FFMPEG - if (loaded_target_file[0] == 'h') { - - codec = FFMPEG; - start_ffmpeg(loaded_target_file, load_target_seek); - load_target_seek = 0; - pthread_mutex_lock(&buffer_mutex); - if (old_sample_rate != sample_rate_src) { - src_reset(src); - } - pthread_mutex_unlock(&buffer_mutex); - - return 0; - } - - - // We need to identify the file type - // Peak into file and try to detect signature - - if ((fptr = uni_fopen(loaded_target_file)) == NULL) { - printf("pa: Error opening file - 1\n"); - perror("Error"); - return 1; - } - - stat(loaded_target_file, &st); - load_file_size = st.st_size; - - fread(peak, sizeof(peak), 1, fptr); - - if (memcmp(peak, "fLaC", 4) == 0) { - codec = FLAC; - //printf("Detected flac\n"); - } else if (memcmp(peak, "RIFF", 4) == 0) { - codec = FFMPEG; //WAVE; - } else if (memcmp(peak, "OggS", 4) == 0) { - codec = VORBIS; - if (peak[28] == 'O' && peak[29] == 'p') codec = OPUS; - } else if (memcmp(peak, "\xff\xfb", 2) == 0) { - codec = MPG; - //printf("Detected mp3\n"); - } else if (memcmp(peak, "\xff\xf3", 2) == 0) { - codec = MPG; - //printf("Detected mp3\n"); - } else if (memcmp(peak, "\xff\xf2", 2) == 0) { - codec = MPG; - //printf("Detected mp3\n"); - } else if (memcmp(peak, "\0\0\0\x20" "ftypM4A", 11) == 0) { - codec = FFMPEG; - //printf("Detected m4a\n"); - } else if (memcmp(peak, "\0\0\0\x18" "ftypdash", 12) == 0) { - codec = FFMPEG; - //printf("Detected m4a\n"); - } else if (memcmp(peak, "\0\0\0\x18" "ftypiso5", 12) == 0) { - codec = FFMPEG; - //printf("Detected m4a\n"); - } else if (memcmp(peak, "\x30\x26\xb2\x75\x8e\x66\xcf\x11", 8) == 0) { - codec = FFMPEG; - //printf("Detected wma\n"); - } else if (memcmp(peak, "MAC\x20", 4) == 0) { - codec = FFMPEG; - //printf("Detected ape\n"); - } else if (memcmp(peak, "TTA1", 4) == 0) { - codec = FFMPEG; - //printf("Detected tta\n"); - } else if (memcmp(peak, "wvpk", 4) == 0) { - codec = WAVPACK; - printf("Detected wavpack\n"); - - } else if (memcmp(peak, "\x49\x44\x33", 3) == 0) { - int id3_size = (peak[6] << 21) | (peak[7] << 14) | (peak[8] << 7) | peak[9]; - fseek(fptr, id3_size + 10, SEEK_SET); - codec = MPG; - unsigned char flac_marker[4]; - if (fread(flac_marker, 1, 4, fptr) == 4 && memcmp(flac_marker, "fLaC", 4) == 0) { - codec = FLAC; - printf("Detected FLAC with ID3 header\n"); - } - } - fclose(fptr); - - // Fallback to detecting using file extension - if (codec == UNKNOWN && ext != NULL && ( - strcmp(ext, ".ape") == 0 || strcmp(ext, ".APE") == 0 || - strcmp(ext, ".m4a") == 0 || strcmp(ext, ".M4A") == 0 || - strcmp(ext, ".mp4") == 0 || strcmp(ext, ".MP4") == 0 || - strcmp(ext, ".webm") == 0 || strcmp(ext, ".WEBM") == 0 || - strcmp(ext, ".tta") == 0 || strcmp(ext, ".TTA") == 0 || - strcmp(ext, ".wma") == 0 || strcmp(ext, ".WMA") == 0 - ) - ) codec = FFMPEG; - - if (codec == UNKNOWN && ext != NULL && ( - strcmp(ext, ".xm") == 0 || strcmp(ext, ".XM") == 0 || - strcmp(ext, ".s3m") == 0 || strcmp(ext, ".S3M") == 0 || - strcmp(ext, ".it") == 0 || strcmp(ext, ".IT") == 0 || - strcmp(ext, ".mptm") == 0 || strcmp(ext, ".MPTM") == 0 || - strcmp(ext, ".mod") == 0 || strcmp(ext, ".MOD") == 0 || - strcmp(ext, ".umx") == 0 || strcmp(ext, ".UMX") == 0 || - strcmp(ext, ".okt") == 0 || strcmp(ext, ".OKT") == 0 || - strcmp(ext, ".mtm") == 0 || strcmp(ext, ".MTM") == 0 || - strcmp(ext, ".far") == 0 || strcmp(ext, ".FAR") == 0 || - strcmp(ext, ".wow") == 0 || strcmp(ext, ".WOW") == 0 || - strcmp(ext, ".dmf") == 0 || strcmp(ext, ".DMF") == 0 || - strcmp(ext, ".med") == 0 || strcmp(ext, ".MED") == 0 || - strcmp(ext, ".md2") == 0 || strcmp(ext, ".MD2") == 0 || - strcmp(ext, ".ult") == 0 || strcmp(ext, ".ULT") == 0 || - strcmp(ext, ".669") == 0 - ) - ) codec = MPT; - - if (codec == UNKNOWN && ext != NULL && ( - strcmp(ext, ".spc") == 0 || strcmp(ext, ".SPC") == 0 || - strcmp(ext, ".ay") == 0 || strcmp(ext, ".AY") == 0 || - strcmp(ext, ".gbs") == 0 || strcmp(ext, ".GBS") == 0 || - strcmp(ext, ".gym") == 0 || strcmp(ext, ".GYM") == 0 || - strcmp(ext, ".hes") == 0 || strcmp(ext, ".HES") == 0 || - strcmp(ext, ".kss") == 0 || strcmp(ext, ".KSS") == 0 || - strcmp(ext, ".nsf") == 0 || strcmp(ext, ".NSF") == 0 || - strcmp(ext, ".nsfe") == 0 || strcmp(ext, ".NSFE") == 0 || - strcmp(ext, ".sap") == 0 || strcmp(ext, ".SAP") == 0 || - strcmp(ext, ".vgm") == 0 || strcmp(ext, ".VGM") == 0 || - strcmp(ext, ".vgz") == 0 || strcmp(ext, ".VGZ") == 0 - ) - ) codec = GME; - - if (codec == UNKNOWN && ext != NULL) { - if (strcmp(ext, ".flac") == 0 || strcmp(ext, ".FLAC") == 0) { - codec = FLAC; - } - if (strcmp(ext, ".mp3") == 0 || strcmp(ext, ".MP3") == 0) { - codec = MPG; - } - if (strcmp(ext, ".ogg") == 0 || strcmp(ext, ".OGG") == 0 || - strcmp(ext, ".oga") == 0 || strcmp(ext, ".OGA") == 0) { - codec = VORBIS; - } - if (strcmp(ext, ".opus") == 0 || strcmp(ext, ".OPUS") == 0) { - codec = OPUS; - } - if (strcmp(ext, ".wv") == 0 || strcmp(ext, ".WV") == 0) { - codec = WAVPACK; - } - } - - if (codec == UNKNOWN || config_always_ffmpeg == 1) { - codec = FFMPEG; - printf("pa: Decode using FFMPEG\n"); - } - - // Start decoders - if (codec == FFMPEG){ - start_ffmpeg(loaded_target_file, load_target_seek); - load_target_seek = 0; - pthread_mutex_lock(&buffer_mutex); - if (old_sample_rate != sample_rate_src) { - src_reset(src); - } - pthread_mutex_unlock(&buffer_mutex); - if (decoder_allocated == 0) return 1; - return 0; - } - - if (codec == GME){ - - sample_rate_src = 48000; - gme_open_file(loaded_target_file, &emu, (long) sample_rate_src); - gme_start_track(emu, subtrack); - - if (load_target_seek > 0) gme_seek(emu, (long) load_target_seek); - - if (old_sample_rate != sample_rate_src) { - src_reset(src); - } - - pthread_mutex_unlock(&buffer_mutex); - decoder_allocated = 1; - - return 0; - - } - - if (codec == MPT){ - - mod_file = uni_fopen(loaded_target_file); - mod = openmpt_module_create2(openmpt_stream_get_file_callbacks2(), mod_file, NULL, NULL, NULL, NULL, NULL, NULL, NULL); - src_channels = 2; - fclose(mod_file); - pthread_mutex_lock(&buffer_mutex); - sample_rate_src = 48000; - current_length_count = openmpt_module_get_duration_seconds(mod) * 48000; - - if (old_sample_rate != sample_rate_src) { - src_reset(src); - } - - if (load_target_seek > 0) { - // printf("pa: Start at position %d\n", load_target_seek); - openmpt_module_set_position_seconds(mod, load_target_seek / 1000.0); - reset_set_value = 48000 * (load_target_seek / 1000.0); - samples_decoded = reset_set_value * 2; - reset_set = 1; - reset_set_byte = high; - load_target_seek = 0; - } - pthread_mutex_unlock(&buffer_mutex); - decoder_allocated = 1; - - return 0; - - } - - - switch (codec) { - - // Unlock the output thread mutex cause loading could take a while?.. - // and we dont wanna interrupt the output for too long. - // - case WAVE: - if (wave_open(loaded_target_file) != 0) return 1; - if (load_target_seek > 0) { - wave_seek((int) wave_samplerate * (load_target_seek / 1000.0)); - } - pthread_mutex_lock(&buffer_mutex); - if (old_sample_rate != sample_rate_src) { - src_reset(src); - } - - if (load_target_seek > 0) { - reset_set_value = (int) wave_samplerate * (load_target_seek / 1000.0); - reset_set = 1; - reset_set_byte = high; - load_target_seek = 0; - } - pthread_mutex_unlock(&buffer_mutex); - decoder_allocated = 1; - return 0; - - case OPUS: - - opus_dec = op_open_file(loaded_target_file, &e); - decoder_allocated = 1; - - if (e != 0) { - printf("pa: Error reading ogg file (expecting opus)\n"); - printf("pa: %d\n", e); - printf("pa: %s\n", loaded_target_file); - } - - if (e == 0) { - pthread_mutex_lock(&buffer_mutex); - - sample_rate_src = 48000; - src_channels = op_channel_count(opus_dec, -1); - - if (old_sample_rate != sample_rate_src) { - src_reset(src); - } - - current_length_count = op_pcm_total(opus_dec, -1); - - if (load_target_seek > 0) { - // printf("pa: Start at position %d\n", load_target_seek); - op_pcm_seek(opus_dec, (int) 48000 * (load_target_seek / 1000.0)); - reset_set_value = op_raw_tell(opus_dec); - samples_decoded = reset_set_value * 2; - reset_set = 1; - reset_set_byte = high; - load_target_seek = 0; - } - pthread_mutex_unlock(&buffer_mutex); - return 0; - } else { - decoder_allocated = 0; - return 1; - } - - break; - case VORBIS: - d_file = uni_fopen(loaded_target_file); - //e = ov_fopen(loaded_target_file, &vf); - e = ov_open(d_file, &vf, NULL, 0); - decoder_allocated = 1; - if (e != 0) { - printf("pa: Error reading ogg file (expecting vorbis)\n"); - - return 1; - } else { - - vi = *ov_info(&vf, -1); - - pthread_mutex_lock(&buffer_mutex); - //printf("pa: Vorbis samplerate is %lu\n", vi.rate); - - sample_rate_src = vi.rate; - src_channels = vi.channels; - - if (old_sample_rate != sample_rate_src) { - src_reset(src); - } - - current_length_count = ov_pcm_total(&vf, -1); - - if (load_target_seek > 0) { - //printf("pa: Start at position %d\n", load_target_seek); - ov_pcm_seek(&vf, (ogg_int64_t) vi.rate * (load_target_seek / 1000.0)); - reset_set_value = vi.rate * (load_target_seek / 1000.0); // op_pcm_tell(opus_dec); that segfaults? - //reset_set_value = 0; - reset_set = 1; - reset_set_byte = high; - load_target_seek = 0; - } - pthread_mutex_unlock(&buffer_mutex); - return 0; - - } - - break; - case FLAC: - d_file = uni_fopen(loaded_target_file); - if (FLAC__stream_decoder_init_FILE( - dec, - d_file, - &f_write, - NULL, //&f_meta, - &f_err, - 0) == FLAC__STREAM_DECODER_INIT_STATUS_OK) { - - decoder_allocated = 1; - flac_got_rate = 0; - - return 0; - - } else return 1; - - break; - - case WAVPACK: - wpc = WavpackOpenFileInput(loaded_target_file, NULL, OPEN_WVC | OPEN_2CH_MAX, 0); - if (wpc == NULL) { - printf("pa: Error loading wavpak file\n"); - WavpackCloseFile(wpc); - return 1; - } - src_channels = WavpackGetReducedChannels(wpc); - sample_rate_src = WavpackGetSampleRate(wpc); - if (old_sample_rate != sample_rate_src) { - src_reset(src); - } - wp_bit = WavpackGetBitsPerSample(wpc); - if (! (wp_bit == 16 || wp_bit == 24)){ - printf("pa: wavpak bit depth not supported\n"); - WavpackCloseFile(wpc); - return 1; - } - wp_float = 0; - if (WavpackGetMode(wpc) & MODE_FLOAT){ - wp_float = 1; - printf("pa: wavpak float mode not implemented"); - return 1; - } - - current_length_count = WavpackGetNumSamples(wpc); - return 0; - break; - - case MPG: - - mpg123_open(mh, loaded_target_file); - decoder_allocated = 1; - mpg123_getformat(mh, &rate, &channels, &encoding); - mpg123_scan(mh); - //printf("pa: %lu. / %d. / %d\n", rate, channels, encoding); - - pthread_mutex_lock(&buffer_mutex); - - sample_rate_src = rate; - src_channels = channels; - if (old_sample_rate != sample_rate_src) { - src_reset(src); - } - current_length_count = (unsigned int) mpg123_length(mh); - - if (encoding == MPG123_ENC_SIGNED_16) { - - if (load_target_seek > 0) { - //printf("pa: Start at position %d\n", load_target_seek); - mpg123_seek(mh, (int) rate * (load_target_seek / 1000.0), SEEK_SET); - reset_set_value = mpg123_tell(mh); - reset_set = 1; - reset_set_byte = high; - load_target_seek = 0; - } - pthread_mutex_unlock(&buffer_mutex); - return 0; - - } else { - // Pretty much every MP3 ive tried is S16, so we might not have - // to worry about this. - printf("pa: ERROR, encoding format not supported!\n"); - pthread_mutex_unlock(&buffer_mutex); - return 1; - } - - break; - } - return 1; + // Function to load a file / prepare decoder + + stop_decoder(); + + strcpy(loaded_target_file, load_target_file); + + int channels; + int encoding; + long rate; + int e = 0; + int old_sample_rate = sample_rate_src; + src_channels = 2; + + char *ext; + ext = strrchr(loaded_target_file, '.'); + + codec = UNKNOWN; + current_length_count = 0; + buffering = 0; + samples_decoded = 0; + + if (loaded_target_file[0] == 'h') buffering = 1; + + rg_byte = high; + + char peak[35]; + + if (strcmp(loaded_target_file, "RAW FEED") == 0) { + codec = FEED; + load_target_seek = 0; + pthread_mutex_lock(&buffer_mutex); +// if (current_sample_rate != sample_rate_out) { +// sample_change_byte = high; +// want_sample_rate = config_feed_samplerate; +// } + sample_rate_src = config_feed_samplerate; + src_reset(src); + pthread_mutex_unlock(&buffer_mutex); + decoder_allocated = 1; + buffering = 1; + return 0; + } + + // If target is url, use FFMPEG + if (loaded_target_file[0] == 'h') { + + codec = FFMPEG; + start_ffmpeg(loaded_target_file, load_target_seek); + load_target_seek = 0; + pthread_mutex_lock(&buffer_mutex); + if (old_sample_rate != sample_rate_src) { + src_reset(src); + } + pthread_mutex_unlock(&buffer_mutex); + + return 0; + } + + + // We need to identify the file type + // Peak into file and try to detect signature + + if ((fptr = uni_fopen(loaded_target_file)) == NULL) { + printf("pa: Error opening file - 1\n"); + perror("Error"); + return 1; + } + + stat(loaded_target_file, &st); + load_file_size = st.st_size; + + fread(peak, sizeof(peak), 1, fptr); + + if (memcmp(peak, "fLaC", 4) == 0) { + codec = FLAC; + //printf("Detected flac\n"); + } else if (memcmp(peak, "RIFF", 4) == 0) { + codec = FFMPEG; //WAVE; + } else if (memcmp(peak, "OggS", 4) == 0) { + codec = VORBIS; + if (peak[28] == 'O' && peak[29] == 'p') codec = OPUS; + } else if (memcmp(peak, "\xff\xfb", 2) == 0) { + codec = MPG; + //printf("Detected mp3\n"); + } else if (memcmp(peak, "\xff\xf3", 2) == 0) { + codec = MPG; + //printf("Detected mp3\n"); + } else if (memcmp(peak, "\xff\xf2", 2) == 0) { + codec = MPG; + //printf("Detected mp3\n"); + } else if (memcmp(peak, "\0\0\0\x20" "ftypM4A", 11) == 0) { + codec = FFMPEG; + //printf("Detected m4a\n"); + } else if (memcmp(peak, "\0\0\0\x18" "ftypdash", 12) == 0) { + codec = FFMPEG; + //printf("Detected m4a\n"); + } else if (memcmp(peak, "\0\0\0\x18" "ftypiso5", 12) == 0) { + codec = FFMPEG; + //printf("Detected m4a\n"); + } else if (memcmp(peak, "\x30\x26\xb2\x75\x8e\x66\xcf\x11", 8) == 0) { + codec = FFMPEG; + //printf("Detected wma\n"); + } else if (memcmp(peak, "MAC\x20", 4) == 0) { + codec = FFMPEG; + //printf("Detected ape\n"); + } else if (memcmp(peak, "TTA1", 4) == 0) { + codec = FFMPEG; + //printf("Detected tta\n"); + } else if (memcmp(peak, "wvpk", 4) == 0) { + codec = WAVPACK; + printf("Detected wavpack\n"); + + } else if (memcmp(peak, "\x49\x44\x33", 3) == 0) { + int id3_size = (peak[6] << 21) | (peak[7] << 14) | (peak[8] << 7) | peak[9]; + fseek(fptr, id3_size + 10, SEEK_SET); + codec = MPG; + unsigned char flac_marker[4]; + if (fread(flac_marker, 1, 4, fptr) == 4 && memcmp(flac_marker, "fLaC", 4) == 0) { + codec = FLAC; + printf("Detected FLAC with ID3 header\n"); + } + } + fclose(fptr); + + // Fallback to detecting using file extension + if (codec == UNKNOWN && ext != NULL && ( + strcmp(ext, ".ape") == 0 || strcmp(ext, ".APE") == 0 || + strcmp(ext, ".m4a") == 0 || strcmp(ext, ".M4A") == 0 || + strcmp(ext, ".mp4") == 0 || strcmp(ext, ".MP4") == 0 || + strcmp(ext, ".webm") == 0 || strcmp(ext, ".WEBM") == 0 || + strcmp(ext, ".tta") == 0 || strcmp(ext, ".TTA") == 0 || + strcmp(ext, ".wma") == 0 || strcmp(ext, ".WMA") == 0 + ) + ) codec = FFMPEG; + + if (codec == UNKNOWN && ext != NULL && ( + strcmp(ext, ".xm") == 0 || strcmp(ext, ".XM") == 0 || + strcmp(ext, ".s3m") == 0 || strcmp(ext, ".S3M") == 0 || + strcmp(ext, ".it") == 0 || strcmp(ext, ".IT") == 0 || + strcmp(ext, ".mptm") == 0 || strcmp(ext, ".MPTM") == 0 || + strcmp(ext, ".mod") == 0 || strcmp(ext, ".MOD") == 0 || + strcmp(ext, ".umx") == 0 || strcmp(ext, ".UMX") == 0 || + strcmp(ext, ".okt") == 0 || strcmp(ext, ".OKT") == 0 || + strcmp(ext, ".mtm") == 0 || strcmp(ext, ".MTM") == 0 || + strcmp(ext, ".far") == 0 || strcmp(ext, ".FAR") == 0 || + strcmp(ext, ".wow") == 0 || strcmp(ext, ".WOW") == 0 || + strcmp(ext, ".dmf") == 0 || strcmp(ext, ".DMF") == 0 || + strcmp(ext, ".med") == 0 || strcmp(ext, ".MED") == 0 || + strcmp(ext, ".md2") == 0 || strcmp(ext, ".MD2") == 0 || + strcmp(ext, ".ult") == 0 || strcmp(ext, ".ULT") == 0 || + strcmp(ext, ".669") == 0 + ) + ) codec = MPT; + + if (codec == UNKNOWN && ext != NULL && ( + strcmp(ext, ".spc") == 0 || strcmp(ext, ".SPC") == 0 || + strcmp(ext, ".ay") == 0 || strcmp(ext, ".AY") == 0 || + strcmp(ext, ".gbs") == 0 || strcmp(ext, ".GBS") == 0 || + strcmp(ext, ".gym") == 0 || strcmp(ext, ".GYM") == 0 || + strcmp(ext, ".hes") == 0 || strcmp(ext, ".HES") == 0 || + strcmp(ext, ".kss") == 0 || strcmp(ext, ".KSS") == 0 || + strcmp(ext, ".nsf") == 0 || strcmp(ext, ".NSF") == 0 || + strcmp(ext, ".nsfe") == 0 || strcmp(ext, ".NSFE") == 0 || + strcmp(ext, ".sap") == 0 || strcmp(ext, ".SAP") == 0 || + strcmp(ext, ".vgm") == 0 || strcmp(ext, ".VGM") == 0 || + strcmp(ext, ".vgz") == 0 || strcmp(ext, ".VGZ") == 0 + ) + ) codec = GME; + + if (codec == UNKNOWN && ext != NULL) { + if (strcmp(ext, ".flac") == 0 || strcmp(ext, ".FLAC") == 0) { + codec = FLAC; + } + if (strcmp(ext, ".mp3") == 0 || strcmp(ext, ".MP3") == 0) { + codec = MPG; + } + if (strcmp(ext, ".ogg") == 0 || strcmp(ext, ".OGG") == 0 || + strcmp(ext, ".oga") == 0 || strcmp(ext, ".OGA") == 0) { + codec = VORBIS; + } + if (strcmp(ext, ".opus") == 0 || strcmp(ext, ".OPUS") == 0) { + codec = OPUS; + } + if (strcmp(ext, ".wv") == 0 || strcmp(ext, ".WV") == 0) { + codec = WAVPACK; + } + } + + if (codec == UNKNOWN || config_always_ffmpeg == 1) { + codec = FFMPEG; + printf("pa: Decode using FFMPEG\n"); + } + + // Start decoders + if (codec == FFMPEG) { + start_ffmpeg(loaded_target_file, load_target_seek); + load_target_seek = 0; + pthread_mutex_lock(&buffer_mutex); + if (old_sample_rate != sample_rate_src) { + src_reset(src); + } + pthread_mutex_unlock(&buffer_mutex); + if (decoder_allocated == 0) return 1; + return 0; + } + + if (codec == GME) { + + sample_rate_src = 48000; + gme_open_file(loaded_target_file, &emu, (long) sample_rate_src); + gme_start_track(emu, subtrack); + + if (load_target_seek > 0) gme_seek(emu, (long) load_target_seek); + + if (old_sample_rate != sample_rate_src) { + src_reset(src); + } + + pthread_mutex_unlock(&buffer_mutex); + decoder_allocated = 1; + + return 0; + + } + + if (codec == MPT) { + mod_file = uni_fopen(loaded_target_file); + mod = openmpt_module_create2(openmpt_stream_get_file_callbacks2(), mod_file, NULL, NULL, NULL, NULL, NULL, NULL, NULL); + src_channels = 2; + fclose(mod_file); + pthread_mutex_lock(&buffer_mutex); + sample_rate_src = 48000; + current_length_count = openmpt_module_get_duration_seconds(mod) * 48000; + + if (old_sample_rate != sample_rate_src) { + src_reset(src); + } + + if (load_target_seek > 0) { + // printf("pa: Start at position %d\n", load_target_seek); + openmpt_module_set_position_seconds(mod, load_target_seek / 1000.0); + reset_set_value = 48000 * (load_target_seek / 1000.0); + samples_decoded = reset_set_value * 2; + reset_set = 1; + reset_set_byte = high; + load_target_seek = 0; + } + pthread_mutex_unlock(&buffer_mutex); + decoder_allocated = 1; + + return 0; + + } + + + switch (codec) { + + // Unlock the output thread mutex cause loading could take a while?.. + // and we dont wanna interrupt the output for too long. + // + case WAVE: + if (wave_open(loaded_target_file) != 0) return 1; + if (load_target_seek > 0) { + wave_seek((int) wave_samplerate * (load_target_seek / 1000.0)); + } + pthread_mutex_lock(&buffer_mutex); + if (old_sample_rate != sample_rate_src) { + src_reset(src); + } + + if (load_target_seek > 0) { + reset_set_value = (int) wave_samplerate * (load_target_seek / 1000.0); + reset_set = 1; + reset_set_byte = high; + load_target_seek = 0; + } + pthread_mutex_unlock(&buffer_mutex); + decoder_allocated = 1; + return 0; + + case OPUS: + + opus_dec = op_open_file(loaded_target_file, &e); + decoder_allocated = 1; + + if (e != 0) { + printf("pa: Error reading ogg file (expecting opus)\n"); + printf("pa: %d\n", e); + printf("pa: %s\n", loaded_target_file); + } + + if (e == 0) { + pthread_mutex_lock(&buffer_mutex); + + sample_rate_src = 48000; + src_channels = op_channel_count(opus_dec, -1); + + if (old_sample_rate != sample_rate_src) { + src_reset(src); + } + + current_length_count = op_pcm_total(opus_dec, -1); + + if (load_target_seek > 0) { + // printf("pa: Start at position %d\n", load_target_seek); + op_pcm_seek(opus_dec, (int) 48000 * (load_target_seek / 1000.0)); + reset_set_value = op_raw_tell(opus_dec); + samples_decoded = reset_set_value * 2; + reset_set = 1; + reset_set_byte = high; + load_target_seek = 0; + } + pthread_mutex_unlock(&buffer_mutex); + return 0; + } else { + decoder_allocated = 0; + return 1; + } + + break; + case VORBIS: + d_file = uni_fopen(loaded_target_file); + //e = ov_fopen(loaded_target_file, &vf); + e = ov_open(d_file, &vf, NULL, 0); + decoder_allocated = 1; + if (e != 0) { + printf("pa: Error reading ogg file (expecting vorbis)\n"); + + return 1; + } else { + + vi = *ov_info(&vf, -1); + + pthread_mutex_lock(&buffer_mutex); + //printf("pa: Vorbis samplerate is %lu\n", vi.rate); + + sample_rate_src = vi.rate; + src_channels = vi.channels; + + if (old_sample_rate != sample_rate_src) { + src_reset(src); + } + + current_length_count = ov_pcm_total(&vf, -1); + + if (load_target_seek > 0) { + //printf("pa: Start at position %d\n", load_target_seek); + ov_pcm_seek(&vf, (ogg_int64_t) vi.rate * (load_target_seek / 1000.0)); + reset_set_value = vi.rate * (load_target_seek / 1000.0); // op_pcm_tell(opus_dec); that segfaults? + //reset_set_value = 0; + reset_set = 1; + reset_set_byte = high; + load_target_seek = 0; + } + pthread_mutex_unlock(&buffer_mutex); + return 0; + + } + + break; + case FLAC: + d_file = uni_fopen(loaded_target_file); + if (FLAC__stream_decoder_init_FILE( + dec, + d_file, + &f_write, + NULL, //&f_meta, + &f_err, + 0) == FLAC__STREAM_DECODER_INIT_STATUS_OK) { + + decoder_allocated = 1; + flac_got_rate = 0; + + return 0; + + } else return 1; + + break; + + case WAVPACK: + wpc = WavpackOpenFileInput(loaded_target_file, NULL, OPEN_WVC | OPEN_2CH_MAX, 0); + if (wpc == NULL) { + printf("pa: Error loading wavpak file\n"); + WavpackCloseFile(wpc); + return 1; + } + src_channels = WavpackGetReducedChannels(wpc); + sample_rate_src = WavpackGetSampleRate(wpc); + if (old_sample_rate != sample_rate_src) { + src_reset(src); + } + wp_bit = WavpackGetBitsPerSample(wpc); + if (! (wp_bit == 16 || wp_bit == 24)) { + printf("pa: wavpak bit depth not supported\n"); + WavpackCloseFile(wpc); + return 1; + } + wp_float = 0; + if (WavpackGetMode(wpc) & MODE_FLOAT) { + wp_float = 1; + printf("pa: wavpak float mode not implemented"); + return 1; + } + + current_length_count = WavpackGetNumSamples(wpc); + return 0; + break; + + case MPG: + + mpg123_open(mh, loaded_target_file); + decoder_allocated = 1; + mpg123_getformat(mh, &rate, &channels, &encoding); + mpg123_scan(mh); + //printf("pa: %lu. / %d. / %d\n", rate, channels, encoding); + + pthread_mutex_lock(&buffer_mutex); + + sample_rate_src = rate; + src_channels = channels; + if (old_sample_rate != sample_rate_src) { + src_reset(src); + } + current_length_count = (unsigned int) mpg123_length(mh); + + if (encoding == MPG123_ENC_SIGNED_16) { + + if (load_target_seek > 0) { + //printf("pa: Start at position %d\n", load_target_seek); + mpg123_seek(mh, (int) rate * (load_target_seek / 1000.0), SEEK_SET); + reset_set_value = mpg123_tell(mh); + reset_set = 1; + reset_set_byte = high; + load_target_seek = 0; + } + pthread_mutex_unlock(&buffer_mutex); + return 0; + + } else { + // Pretty much every MP3 ive tried is S16, so we might not have + // to worry about this. + printf("pa: ERROR, encoding format not supported!\n"); + pthread_mutex_unlock(&buffer_mutex); + return 1; + } + + break; + } + return 1; } void end() { - // Call when buffer has run out or otherwise ready to stop and flush - stop_decoder(); - pthread_mutex_lock(&buffer_mutex); - mode = STOPPED; - command = NONE; - buff_reset(); - buffering = 0; - pthread_mutex_unlock(&buffer_mutex); + // Call when buffer has run out or otherwise ready to stop and flush + stop_decoder(); + pthread_mutex_lock(&buffer_mutex); + mode = STOPPED; + command = NONE; + buff_reset(); + buffering = 0; + pthread_mutex_unlock(&buffer_mutex); } void decoder_eos() { - // Call once current decode steam has run out - //printf("pa: End of stream\n"); - if (next_ready == 1) { - //printf("pa: Read next gapless\n"); - int result = load_next(); - if (result == 1){ - result_status = FAILURE; - } - pthread_mutex_lock(&buffer_mutex); - next_ready = 0; - reset_set_value = 0; - reset_set = 1; - reset_set_byte = high; - pthread_mutex_unlock(&buffer_mutex); + // Call once current decode steam has run out + //printf("pa: End of stream\n"); + if (next_ready == 1) { + //printf("pa: Read next gapless\n"); + int result = load_next(); + if (result == 1) { + result_status = FAILURE; + } + pthread_mutex_lock(&buffer_mutex); + next_ready = 0; + reset_set_value = 0; + reset_set = 1; + reset_set_byte = high; + pthread_mutex_unlock(&buffer_mutex); - } else mode = ENDING; + } else mode = ENDING; } -void stop_out(){ +void stop_out() { - if (out_thread_running == 1){ - called_to_stop_device = 1; - #ifdef MINI - ma_device_stop(&device); - #endif - out_thread_running = 0; - } - disconnect_pulse(); + if (out_thread_running == 1) { + called_to_stop_device = 1; + #ifdef MINI + ma_device_stop(&device); + #endif + out_thread_running = 0; + } + disconnect_pulse(); } -void start_out(){ - if (pulse_connected == 0) connect_pulse(); +void start_out() { + if (pulse_connected == 0) connect_pulse(); - if (out_thread_running == 0){ - called_to_stop_device = 0; - device_stopped = 0; - #ifdef MINI - ma_device_start(&device); - #endif - out_thread_running = 1; + if (out_thread_running == 0) { + called_to_stop_device = 0; + device_stopped = 0; + #ifdef MINI + ma_device_start(&device); + #endif + out_thread_running = 1; - #ifdef PIPE + #ifdef PIPE - #endif - } + #endif + } } void pump_decode() { - // Here we get data from the decoders to fill the main buffer - - int reconnect = 0; - if (config_resample == 0 && sample_rate_out != sample_rate_src) { - if (get_buff_fill() > 0){ - return; - } - printf("ph: Pump wrong samplerate\n"); - - #ifdef MINI - stop_out(); - fade_fill = 0; - fade_position = 0; - reset_set_value = 0; - buff_reset(); - reconnect = 1; - #endif - - #ifdef PIPE - fade_fill = 0; - fade_position = 0; - reset_set_value = 0; - buff_reset(); - pipe_set_samplerate = sample_rate_src; - sample_rate_out = pipe_set_samplerate; - pw_loop_invoke(pw_main_loop_get_loop(loop), pipe_update, SPA_ID_INVALID, NULL, 0, - true, NULL); - #endif - } - - if (codec == WAVE) { - int result; - pthread_mutex_lock(&buffer_mutex); - result = wave_decode(1024 * 2); - pthread_mutex_unlock(&buffer_mutex); - if (result == 1) { - decoder_eos(); - } - - } else if (codec == MPT) { - int count; - count = openmpt_module_read_interleaved_stereo(mod, 48000, 4096, temp16l); - if (count == 0){ - decoder_eos(); - } else { - pthread_mutex_lock(&buffer_mutex); - read_to_buffer_s16int(temp16l, count * 2); - samples_decoded += count * 2; - pthread_mutex_unlock(&buffer_mutex); - } - - } else if (codec == GME) { - - gme_play(emu, 1024, temp16l); - - pthread_mutex_lock(&buffer_mutex); - read_to_buffer_s16int(temp16l, 1024); - samples_decoded += 1024; - pthread_mutex_unlock(&buffer_mutex); - - if (gme_track_ended(emu)){ - decoder_eos(); - } - - - } else if (codec == FLAC) { - // FLAC decoding - - switch (FLAC__stream_decoder_get_state(dec)) { - case FLAC__STREAM_DECODER_END_OF_STREAM: - decoder_eos(); - break; - - default: - FLAC__stream_decoder_process_single(dec); - - } - - if (load_target_seek > 0 && flac_got_rate == 1) { - //printf("pa: Set start position %d\n", load_target_seek); - - FLAC__stream_decoder_seek_absolute(dec, (int) sample_rate_src * (load_target_seek / 1000.0)); - pthread_mutex_lock(&buffer_mutex); - reset_set = 1; - reset_set_byte = high; - load_target_seek = 0; - pthread_mutex_unlock(&buffer_mutex); - } - - } else if (codec == OPUS) { - - unsigned int done; - - if(src_channels == 1){ - done = op_read(opus_dec, opus_buffer, 4096, NULL); - } - else{ - done = op_read_stereo(opus_dec, opus_buffer, 1024 * 2) * 2; - } - - pthread_mutex_lock(&buffer_mutex); - read_to_buffer_s16int(opus_buffer, done); - samples_decoded += done; - - pthread_mutex_unlock(&buffer_mutex); - if (done == 0) { - - // Check if file was appended to... - stat(loaded_target_file, &st); - if (load_file_size != st.st_size) { - printf("pa: Ogg file size changed!\n"); - int e = 0; - op_free(opus_dec); - opus_dec = op_open_file(loaded_target_file, &e); - op_pcm_seek(opus_dec, samples_decoded / 2); - return; - } - - decoder_eos(); - } - - - } else if (codec == VORBIS) { - - unsigned int done; - int stream; - done = ov_read(&vf, parse_buffer, 2048 * 2, 0, 2, 1, &stream); - - pthread_mutex_lock(&buffer_mutex); - read_to_buffer_char16(parse_buffer, done); - pthread_mutex_unlock(&buffer_mutex); - if (done == 0) { - decoder_eos(); - } + // Here we get data from the decoders to fill the main buffer + + int reconnect = 0; + if (config_resample == 0 && sample_rate_out != sample_rate_src) { + if (get_buff_fill() > 0) { + return; + } + printf("ph: Pump wrong samplerate\n"); + + #ifdef MINI + stop_out(); + fade_fill = 0; + fade_position = 0; + reset_set_value = 0; + buff_reset(); + reconnect = 1; + #endif + + #ifdef PIPE + fade_fill = 0; + fade_position = 0; + reset_set_value = 0; + buff_reset(); + pipe_set_samplerate = sample_rate_src; + sample_rate_out = pipe_set_samplerate; + pw_loop_invoke(pw_main_loop_get_loop(loop), pipe_update, SPA_ID_INVALID, NULL, 0, true, NULL); + #endif + } + + if (codec == WAVE) { + int result; + pthread_mutex_lock(&buffer_mutex); + result = wave_decode(1024 * 2); + pthread_mutex_unlock(&buffer_mutex); + if (result == 1) { + decoder_eos(); + } + + } else if (codec == MPT) { + int count; + count = openmpt_module_read_interleaved_stereo(mod, 48000, 4096, temp16l); + if (count == 0) { + decoder_eos(); + } else { + pthread_mutex_lock(&buffer_mutex); + read_to_buffer_s16int(temp16l, count * 2); + samples_decoded += count * 2; + pthread_mutex_unlock(&buffer_mutex); + } + + } else if (codec == GME) { + + gme_play(emu, 1024, temp16l); + + pthread_mutex_lock(&buffer_mutex); + read_to_buffer_s16int(temp16l, 1024); + samples_decoded += 1024; + pthread_mutex_unlock(&buffer_mutex); + + if (gme_track_ended(emu)) { + decoder_eos(); + } + + + } else if (codec == FLAC) { + // FLAC decoding + + switch (FLAC__stream_decoder_get_state(dec)) { + case FLAC__STREAM_DECODER_END_OF_STREAM: + decoder_eos(); + break; + + default: + FLAC__stream_decoder_process_single(dec); + + } + + if (load_target_seek > 0 && flac_got_rate == 1) { + //printf("pa: Set start position %d\n", load_target_seek); + + FLAC__stream_decoder_seek_absolute(dec, (int) sample_rate_src * (load_target_seek / 1000.0)); + pthread_mutex_lock(&buffer_mutex); + reset_set = 1; + reset_set_byte = high; + load_target_seek = 0; + pthread_mutex_unlock(&buffer_mutex); + } + + } else if (codec == OPUS) { + + unsigned int done; + + if (src_channels == 1) { + done = op_read(opus_dec, opus_buffer, 4096, NULL); + } + else { + done = op_read_stereo(opus_dec, opus_buffer, 1024 * 2) * 2; + } + + pthread_mutex_lock(&buffer_mutex); + read_to_buffer_s16int(opus_buffer, done); + samples_decoded += done; + + pthread_mutex_unlock(&buffer_mutex); + if (done == 0) { + + // Check if file was appended to... + stat(loaded_target_file, &st); + if (load_file_size != st.st_size) { + printf("pa: Ogg file size changed!\n"); + int e = 0; + op_free(opus_dec); + opus_dec = op_open_file(loaded_target_file, &e); + op_pcm_seek(opus_dec, samples_decoded / 2); + return; + } + + decoder_eos(); + } + + + } else if (codec == VORBIS) { + + unsigned int done; + int stream; + done = ov_read(&vf, parse_buffer, 2048 * 2, 0, 2, 1, &stream); - } else if (codec == WAVPACK) { - int samples; - int32_t buffer[4 * 1024 * 2]; - samples = WavpackUnpackSamples(wpc, buffer, 1024); - if (wp_bit == 16){ - read_to_buffer_16in32_fs(buffer, samples); - } else if (wp_bit == 24){ - read_to_buffer_24in32_fs(buffer, samples); - } - samples_decoded += samples; + pthread_mutex_lock(&buffer_mutex); + read_to_buffer_char16(parse_buffer, done); + pthread_mutex_unlock(&buffer_mutex); + if (done == 0) { + decoder_eos(); + } - } else if (codec == MPG) { - // MP3 decoding + } else if (codec == WAVPACK) { + int samples; + int32_t buffer[4 * 1024 * 2]; + samples = WavpackUnpackSamples(wpc, buffer, 1024); + if (wp_bit == 16) { + read_to_buffer_16in32_fs(buffer, samples); + } else if (wp_bit == 24) { + read_to_buffer_24in32_fs(buffer, samples); + } + samples_decoded += samples; - size_t done; + } else if (codec == MPG) { + // MP3 decoding - mpg123_read(mh, parse_buffer, 2048 * 2, &done); + size_t done; - pthread_mutex_lock(&buffer_mutex); - read_to_buffer_char16(parse_buffer, done); - pthread_mutex_unlock(&buffer_mutex); - if (done == 0) { - decoder_eos(); - } - } else if (codec == FFMPEG) { + mpg123_read(mh, parse_buffer, 2048 * 2, &done); - int b = 0; + pthread_mutex_lock(&buffer_mutex); + read_to_buffer_char16(parse_buffer, done); + pthread_mutex_unlock(&buffer_mutex); + if (done == 0) { + decoder_eos(); + } + } else if (codec == FFMPEG) { - b = ff_read(ffm_buffer, 2048); + int b = 0; - if (b % 4 != 0) { - printf("pa: Uneven data\n"); - decoder_eos(); - return; - } + b = ff_read(ffm_buffer, 2048); - pthread_mutex_lock(&buffer_mutex); - read_to_buffer_char16(ffm_buffer, b); - pthread_mutex_unlock(&buffer_mutex); - if (b == 0) { - printf("pa: FFMPEG has finished\n"); - decoder_eos(); + if (b % 4 != 0) { + printf("pa: Uneven data\n"); + decoder_eos(); + return; + } - } - } + pthread_mutex_lock(&buffer_mutex); + read_to_buffer_char16(ffm_buffer, b); + pthread_mutex_unlock(&buffer_mutex); + if (b == 0) { + printf("pa: FFMPEG has finished\n"); + decoder_eos(); + } + } - if (reconnect == 1 && sample_rate_src > 0) start_out(); + if (reconnect == 1 && sample_rate_src > 0) start_out(); } @@ -2417,586 +2376,591 @@ int main_running = 0; void *main_loop(void *thread_id) { - rbuf = (kiss_fft_scalar*)malloc(sizeof(kiss_fft_scalar) * 2048 ); - cbuf = (kiss_fft_cpx*)malloc(sizeof(kiss_fft_cpx) * (2048/2+1) ); - ffta = kiss_fftr_alloc(2048 ,0 ,0,0 ); - - int error = 0; - - int load_result = 0; - int using_fade = 0; + rbuf = (kiss_fft_scalar*)malloc(sizeof(kiss_fft_scalar) * 2048 ); + cbuf = (kiss_fft_cpx*)malloc(sizeof(kiss_fft_cpx) * (2048/2+1) ); + ffta = kiss_fftr_alloc(2048 ,0 ,0,0 ); + + int error = 0; + + int load_result = 0; + int using_fade = 0; + + // SRC ---------------------------- + + src = src_new(config_resample_quality, 2, &error); + // printf("pa: SRC error code %d", error); + error = 0; + + // MP3 decoder -------------------------------------------------------------- + + mpg123_init(); + mh = mpg123_new(NULL, &error); + mpg123_param(mh, MPG123_ADD_FLAGS, MPG123_QUIET | MPG123_SKIP_ID3V2, 0); + mpg123_param(mh, MPG123_RESYNC_LIMIT, 10000, 0); + + // FLAC decoder ---------------------------------------------------------------- + + dec = FLAC__stream_decoder_new(); + + // --------------------------------------------- + + // PIPEWIRE ----------- + #ifdef PIPE + printf("Start pipewire thread...\n"); + enum_done = 0; + if (pthread_create(&pw_thread, NULL, pipewire_main_loop_thread, NULL) != 0) { + fprintf(stderr, "Failed to create Pipewire main loop thread\n"); + return thread_id; + } + printf("Done Pipewire prep, wait for ready event...\n"); + while (enum_done != 1) { + usleep(10000); + } + printf("Pipewire load done.\n"); + #endif + //int test1 = 0; + // Main loop --------------------------------------------------------------- + while (1) { + +// test1++; +// if (test1 > 650) { +// printf("pa: Status: mode %d, command %d, buffer %d, gate %f\n", mode, command, get_buff_fill(), gate); +// test1 = 0; +// } + + // Detect when device was unplugged or became unavailble + if (device_stopped && !called_to_stop_device && !signaled_device_unavailable) { + on_device_unavailable(); + signaled_device_unavailable = 1; + } + + if (command != NONE) { + + if (command == EXIT) { + break; + } + switch (command) { + + case PAUSE: + if (mode == PLAYING || (mode == RAMP_DOWN && gate == 0)) { + mode = PAUSED; + //stop_out(); + command = NONE; + } + + break; + case RESUME: + if (mode == PAUSED) { + start_out(); + mode = PLAYING; + } + command = NONE; + break; + case STOP: + if (mode == STOPPED) { + command = NONE; + } else if (mode == PLAYING) { + mode = RAMP_DOWN; + } + if ((mode == RAMP_DOWN && (gate == 0 || get_buff_fill() == 0)) || mode == PAUSED) { + end(); + } + break; + case START: + if (mode == PLAYING) { + mode = RAMP_DOWN; + } + if (mode == RAMP_DOWN && gate == 0) { + command = LOAD; + } else break; + + case LOAD: + + // Prepare for a crossfade if enabled and suitable + using_fade = 0; + if (config_fade_jump == 1 && mode == PLAYING) { + pthread_mutex_lock(&buffer_mutex); + if (fade_fill > 0) { + printf("pa: Fade already in progress\n"); + } + int l = current_sample_rate * (config_fade_duration / 1000.0); + int reserve = 0; //current_sample_rate / 10.0; + if (get_buff_fill() > l) { + int i = 0; + int p = low + reserve; + i = 0; + + while (i < l) { + fadefl[i] = bfl[p]; //buffl[(buff_base + i + reserve) % BUFF_SIZE]; + fadefr[i] = bfr[p]; //buffr[(buff_base + i + reserve) % BUFF_SIZE]; + i++; + p++; + if (p >= watermark) { + p = 0; + } + } + fade_position = 0; + //position_count = 0; + fade_fill = l; + high = low + reserve; + using_fade = 1; + fade_lockout = 0; + fade_mini = 0.0; + + reset_set_byte = p; + if (reset_set == 0) { + reset_set = 1; + reset_set_value = 0; + } + + } + pthread_mutex_unlock(&buffer_mutex); + } + + load_result = load_next(); + + if (using_fade == 0) { + // Jump immediately + //printf("ph: Jump\n"); + position_count = 0; + buff_reset(); + gate = 0; + sample_change_byte = 0; + reset_set = 1; + reset_set_byte = 0; + reset_set_value = 0; + } + + if (load_result == 0) { + + mode = PLAYING; + result_status = SUCCESS; + start_out(); + command = NONE; + + + } else { + printf("ph: Load file failed\n"); + result_status = FAILURE; + command = NONE; + mode = STOPPED; + } + + break; + + } // end switch + + } // end if none + + + if (command == SEEK) { + + if (mode == PLAYING) { + + mode = RAMP_DOWN; + + //if (want_sample_rate > 0) decode_seek(seek_request_ms, want_sample_rate); + decode_seek(seek_request_ms, sample_rate_src); + reset_set = 0; + + //if (want_sample_rate > 0) position_count = want_sample_rate * (seek_request_ms / 1000.0); + position_count = current_sample_rate * (seek_request_ms / 1000.0); + + } else if (mode == PAUSED) { - // SRC ---------------------------- - src = src_new(config_resample_quality, 2, &error); - // printf("pa: SRC error code %d", error); - error = 0; + //if (want_sample_rate > 0) decode_seek(seek_request_ms, want_sample_rate); + decode_seek(seek_request_ms, current_sample_rate); - // MP3 decoder -------------------------------------------------------------- + //if (want_sample_rate > 0) position_count = want_sample_rate * (seek_request_ms / 1000.0); + position_count = current_sample_rate * (seek_request_ms / 1000.0); - mpg123_init(); - mh = mpg123_new(NULL, &error); - mpg123_param(mh, MPG123_ADD_FLAGS, MPG123_QUIET | MPG123_SKIP_ID3V2, 0); - mpg123_param(mh, MPG123_RESYNC_LIMIT, 10000, 0); + pthread_mutex_lock(&buffer_mutex); - // FLAC decoder ---------------------------------------------------------------- + buff_reset(); - dec = FLAC__stream_decoder_new(); + command = NONE; - // --------------------------------------------- + pthread_mutex_unlock(&buffer_mutex); - // PIPEWIRE ----------- - #ifdef PIPE - - - printf("Start pipewire thread...\n"); - enum_done = 0; - if (pthread_create(&pw_thread, NULL, pipewire_main_loop_thread, NULL) != 0) { - fprintf(stderr, "Failed to create Pipewire main loop thread\n"); - return thread_id; - } - printf("Done Pipewire prep, wait for ready event...\n"); - while (enum_done != 1){ - usleep(10000); - } - printf("Pipewire load done.\n"); - #endif - //int test1 = 0; - // Main loop --------------------------------------------------------------- - while (1) { - -// test1++; -// if (test1 > 650){ -// printf("pa: Status: mode %d, command %d, buffer %d, gate %f\n", mode, command, get_buff_fill(), gate); -// test1 = 0; -// } + } else if (mode != RAMP_DOWN) { + printf("pa: fixme - cannot seek at this time\n"); + command = NONE; + } - // Detect when device was unplugged or became unavailble - if (device_stopped && !called_to_stop_device && !signaled_device_unavailable) { - on_device_unavailable(); - signaled_device_unavailable = 1; - } - - if (command != NONE) { - - if (command == EXIT) { - break; - } - switch (command) { - - case PAUSE: - if (mode == PLAYING || (mode == RAMP_DOWN && gate == 0)) { - mode = PAUSED; - //stop_out(); - command = NONE; - } - - break; - case RESUME: - if (mode == PAUSED) { - start_out(); - mode = PLAYING; - } - command = NONE; - break; - case STOP: - if (mode == STOPPED) { - command = NONE; - } else if (mode == PLAYING) { - mode = RAMP_DOWN; - } - if ((mode == RAMP_DOWN && (gate == 0 || get_buff_fill() == 0)) || mode == PAUSED) { - end(); - } - break; - case START: - if (mode == PLAYING) { - mode = RAMP_DOWN; - } - if (mode == RAMP_DOWN && gate == 0) { - command = LOAD; - } else break; - - case LOAD: - - // Prepare for a crossfade if enabled and suitable - using_fade = 0; - if (config_fade_jump == 1 && mode == PLAYING) { - pthread_mutex_lock(&buffer_mutex); - if (fade_fill > 0){ - printf("pa: Fade already in progress\n"); - } - int l = current_sample_rate * (config_fade_duration / 1000.0); - int reserve = 0; //current_sample_rate / 10.0; - if (get_buff_fill() > l) { - int i = 0; - int p = low + reserve; - i = 0; - - while (i < l) { - fadefl[i] = bfl[p]; //buffl[(buff_base + i + reserve) % BUFF_SIZE]; - fadefr[i] = bfr[p]; //buffr[(buff_base + i + reserve) % BUFF_SIZE]; - i++; - p++; - if (p >= watermark){ - p = 0; - } - } - fade_position = 0; - //position_count = 0; - fade_fill = l; - high = low + reserve; - using_fade = 1; - fade_lockout = 0; - fade_mini = 0.0; - - reset_set_byte = p; - if (reset_set == 0) { - reset_set = 1; - reset_set_value = 0; - } - - } - pthread_mutex_unlock(&buffer_mutex); - } - - load_result = load_next(); - - if (using_fade == 0){ - // Jump immediately - //printf("ph: Jump\n"); - position_count = 0; - buff_reset(); - gate = 0; - sample_change_byte = 0; - reset_set = 1; - reset_set_byte = 0; - reset_set_value = 0; - } - - if (load_result == 0){ - - mode = PLAYING; - result_status = SUCCESS; - start_out(); - command = NONE; - - - } else { - printf("ph: Load file failed\n"); - result_status = FAILURE; - command = NONE; - mode = STOPPED; - } - - break; - - } // end switch - - } // end if none - - - if (command == SEEK) { + if (mode == RAMP_DOWN && gate == 0) { + pthread_mutex_lock(&buffer_mutex); + buff_reset(); + mode = PLAYING; + command = NONE; + pthread_mutex_unlock(&buffer_mutex); - if (mode == PLAYING) { + } + } - mode = RAMP_DOWN; - //if (want_sample_rate > 0) decode_seek(seek_request_ms, want_sample_rate); - decode_seek(seek_request_ms, sample_rate_src); - reset_set = 0; + // Refill the buffer + if (mode == PLAYING && codec != FEED) { + while (get_buff_fill() < BUFF_SAFE && mode != ENDING) { + pump_decode(); + } + } - //if (want_sample_rate > 0) position_count = want_sample_rate * (seek_request_ms / 1000.0); - position_count = current_sample_rate * (seek_request_ms / 1000.0); + if (mode == ENDING && get_buff_fill() == 0) { + //printf("pa: Buffer ran out at end of track\n"); + end(); + } + if (mode == ENDING && next_ready == 1) { + //printf("pa: Next registered while buffer was draining\n"); + //printf("pa: -- remaining was %d\n", get_buff_fill()); + mode = PLAYING; + } - } else if (mode == PAUSED) { + usleep(5000); + } + //printf("pa: Cleanup and exit\n"); - //if (want_sample_rate > 0) decode_seek(seek_request_ms, want_sample_rate); - decode_seek(seek_request_ms, current_sample_rate); + pthread_mutex_lock(&buffer_mutex); - //if (want_sample_rate > 0) position_count = want_sample_rate * (seek_request_ms / 1000.0); - position_count = current_sample_rate * (seek_request_ms / 1000.0); + main_running = 0; - pthread_mutex_lock(&buffer_mutex); + position_count = 0; + buff_reset(); - buff_reset(); + //disconnect_pulse(); + FLAC__stream_decoder_finish(dec); + FLAC__stream_decoder_delete(dec); + mpg123_delete(mh); + src_delete(src); - command = NONE; + pthread_mutex_unlock(&buffer_mutex); - pthread_mutex_unlock(&buffer_mutex); + stop_out(); + disconnect_pulse(); + #ifdef MINI + if (context_allocated == 1) { + ma_context_uninit(&context); + context_allocated = 0; + } + #endif - } else if (mode != RAMP_DOWN) { - printf("pa: fixme - cannot seek at this time\n"); - command = NONE; - } - - if (mode == RAMP_DOWN && gate == 0) { - pthread_mutex_lock(&buffer_mutex); - buff_reset(); - mode = PLAYING; - command = NONE; - pthread_mutex_unlock(&buffer_mutex); - - } - } - - - // Refill the buffer - if (mode == PLAYING && codec != FEED) { - while (get_buff_fill() < BUFF_SAFE && mode != ENDING) { - pump_decode(); - - } - } - - if (mode == ENDING && get_buff_fill() == 0) { - //printf("pa: Buffer ran out at end of track\n"); - end(); - - } - if (mode == ENDING && next_ready == 1) { - //printf("pa: Next registered while buffer was draining\n"); - //printf("pa: -- remaining was %d\n", get_buff_fill()); - mode = PLAYING; - } - - usleep(5000); - } - - //printf("pa: Cleanup and exit\n"); - - pthread_mutex_lock(&buffer_mutex); - - main_running = 0; - - position_count = 0; - buff_reset(); - - //disconnect_pulse(); - FLAC__stream_decoder_finish(dec); - FLAC__stream_decoder_delete(dec); - mpg123_delete(mh); - src_delete(src); - - pthread_mutex_unlock(&buffer_mutex); - - stop_out(); - disconnect_pulse(); - #ifdef MINI - if (context_allocated == 1){ - - ma_context_uninit(&context); - - context_allocated = 0; - } - #endif - - #ifdef PIPE - - pw_loop_invoke(pw_main_loop_get_loop(loop), pipe_exit, SPA_ID_INVALID, NULL, 0, - true, NULL); - - - #endif - command = NONE; - printf("Exit PHAzOR\n"); - return thread_id; + #ifdef PIPE + pw_loop_invoke(pw_main_loop_get_loop(loop), pipe_exit, SPA_ID_INVALID, NULL, 0, true, NULL); + #endif + command = NONE; + printf("Exit PHAzOR\n"); + return thread_id; } // --------------------------------------------------------------------------------------- // Begin exported functions -int init() { - //printf("ph: PHAzOR starting up\n"); - if (main_running == 0) { - main_running = 1; - pthread_t main_thread_id; - pthread_create(&main_thread_id, NULL, main_loop, NULL); - } else printf("ph: Cannot init. Main loop already running!\n"); - return 0; +EXPORT int scan_devices() { + #ifdef MINI + if (initiate_ma_context() == -1) return -1; + result = ma_context_get_devices(&context, &pPlaybackDeviceInfos, &playbackDeviceCount, NULL, NULL); + if (result != MA_SUCCESS) { + printf("Failed to retrieve device information.\n"); + return -2; + } + return playbackDeviceCount; + #endif + + #ifdef PIPE + while (enum_done != 1) { + usleep(10000); + } + return pipe_devices.device_count; + #endif } -int get_status() { - return mode; +EXPORT int init() { + //printf("ph: PHAzOR starting up\n"); + if (main_running == 0) { + main_running = 1; + pthread_t main_thread_id; + pthread_create(&main_thread_id, NULL, main_loop, NULL); + } else printf("ph: Cannot init. Main loop already running!\n"); + return 0; } -int get_result() { - return result_status; +EXPORT int get_status() { + return mode; } -int start(char *filename, int start_ms, int fade, float rg) { +EXPORT int get_result() { + return result_status; +} - while (command != NONE) { - usleep(1000); - } +EXPORT int start(char *filename, int start_ms, int fade, float rg) { - result_status = WAITING; + while (command != NONE) { + usleep(1000); + } - rg_value_want = rg; - config_fade_jump = fade; + result_status = WAITING; - load_target_seek = start_ms; - strcpy(load_target_file, filename); + rg_value_want = rg; + config_fade_jump = fade; - if (mode == PLAYING) { - if (fade == 1) command = LOAD; - else command = START; - } else command = LOAD; + load_target_seek = start_ms; + strcpy(load_target_file, filename); - return 0; -} + if (mode == PLAYING) { + if (fade == 1) command = LOAD; + else command = START; + } else command = LOAD; + return 0; +} -int next(char *filename, int start_ms, float rg) { +EXPORT int next(char *filename, int start_ms, float rg) { - while (command != NONE) { - usleep(1000); - } + while (command != NONE) { + usleep(1000); + } - result_status = WAITING; + result_status = WAITING; - if (mode == STOPPED) { - start(filename, start_ms, 0, rg); - } else { - load_target_seek = start_ms; - strcpy(load_target_file, filename); - rg_value_want = rg; - next_ready = 1; - } + if (mode == STOPPED) { + start(filename, start_ms, 0, rg); + } else { + load_target_seek = start_ms; + strcpy(load_target_file, filename); + rg_value_want = rg; + next_ready = 1; + } - return 0; + return 0; } -int pause() { - while (command != NONE) { - usleep(1000); - } - if (mode == PAUSED) return 0; - if (out_thread_running && (mode == PLAYING || mode == RAMP_DOWN)) { - mode = RAMP_DOWN; - command = PAUSE; - } +EXPORT int pause() { + while (command != NONE) { + usleep(1000); + } + if (mode == PAUSED) return 0; + if (out_thread_running && (mode == PLAYING || mode == RAMP_DOWN)) { + mode = RAMP_DOWN; + command = PAUSE; + } - return 0; + return 0; } -int resume() { - while (command != NONE) { - usleep(1000); - } - if (mode == PAUSED) { - gate = 0; - } - command = RESUME; - return 0; +EXPORT int resume() { + while (command != NONE) { + usleep(1000); + } + if (mode == PAUSED) { + gate = 0; + } + command = RESUME; + return 0; } -int stop() { - while (command != NONE) { - usleep(1000); - } - command = STOP; - return 0; +EXPORT int stop() { + while (command != NONE) { + usleep(1000); + } + command = STOP; + return 0; } -void wait_for_command() { - while (command != NONE) { - usleep(1000); - } +EXPORT void wait_for_command() { + while (command != NONE) { + usleep(1000); + } } -int seek(int ms_absolute, int flag) { +EXPORT int seek(int ms_absolute, int flag) { - while (command != NONE) { - usleep(1000); - } + while (command != NONE) { + usleep(1000); + } - config_fast_seek = flag; - seek_request_ms = ms_absolute; - command = SEEK; + config_fast_seek = flag; + seek_request_ms = ms_absolute; + command = SEEK; - return 0; + return 0; } -int set_volume(int percent) { - volume_want = percent / 100.0; - volume_on = percent / 100.0; +EXPORT int set_volume(int percent) { + volume_want = percent / 100.0; + volume_on = percent / 100.0; - return 0; + return 0; } -int ramp_volume(int percent, int speed) { - volume_ramp_speed = speed; - volume_want = percent / 100.0; - return 0; +EXPORT int ramp_volume(int percent, int speed) { + volume_ramp_speed = speed; + volume_want = percent / 100.0; + return 0; } -int get_position_ms() { - if (command != START && command != LOAD && reset_set == 0 && current_sample_rate > 0) { - return (int) ((position_count / (float) current_sample_rate) * 1000.0); - } else return 0; +EXPORT int get_position_ms() { + if (command != START && command != LOAD && reset_set == 0 && current_sample_rate > 0) { + return (int) ((position_count / (float) current_sample_rate) * 1000.0); + } else return 0; } -void set_position_ms(int ms) { - position_count = ((float)(ms / 1000.0)) * current_sample_rate; +EXPORT void set_position_ms(int ms) { + position_count = ((float)(ms / 1000.0)) * current_sample_rate; } -int get_length_ms() { - if (reset_set == 0 && sample_rate_src > 0 && current_length_count > 0) { +EXPORT int get_length_ms() { + if (reset_set == 0 && sample_rate_src > 0 && current_length_count > 0) { - return (int) ((current_length_count / (float) sample_rate_src) * 1000.0); - } else return 0; + return (int) ((current_length_count / (float) sample_rate_src) * 1000.0); + } else return 0; } -void config_set_dev_buffer(int ms) { - config_dev_buffer = ms; +EXPORT void config_set_dev_buffer(int ms) { + config_dev_buffer = ms; } -void config_set_samplerate(int hz) { - sample_rate_out = hz; -} -void config_set_resample_quality(int n) { - config_resample_quality = n; +EXPORT void config_set_samplerate(int hz) { + sample_rate_out = hz; } -void config_set_resample(int n) { - config_resample = n; +EXPORT void config_set_resample_quality(int n) { + config_resample_quality = n; } -void config_set_always_ffmpeg(int n) { - config_always_ffmpeg = n; +EXPORT void config_set_resample(int n) { + config_resample = n; } -void config_set_fade_duration(int ms){ - if (ms < 200) ms = 200; - if (ms > 2000) ms = 2000; - config_fade_duration = ms; +EXPORT void config_set_always_ffmpeg(int n) { + config_always_ffmpeg = n; } -void config_set_dev_name(char *device) { - if (device == NULL) { - strcpy(config_output_sink, "Default"); - } else { - strcpy(config_output_sink, device); - } +EXPORT void config_set_fade_duration(int ms) { + if (ms < 200) ms = 200; + if (ms > 2000) ms = 2000; + config_fade_duration = ms; } -void config_set_volume_power(int n){ - config_volume_power = n; +EXPORT void config_set_dev_name(char *device) { + if (device == NULL) { + strcpy(config_output_sink, "Default"); + } else { + strcpy(config_output_sink, device); + } } -void config_set_feed_samplerate(int n){ - config_feed_samplerate = n; +EXPORT void config_set_volume_power(int n) { + config_volume_power = n; } -void config_set_min_buffer(int n){ - config_min_buffer = n; +EXPORT void config_set_feed_samplerate(int n) { + config_feed_samplerate = n; } -float get_level_peak_l() { - - float peak = peak_l; - peak_l = 0.0; - return peak; +EXPORT void config_set_min_buffer(int n) { + config_min_buffer = n; } -float get_level_peak_r() { - float peak = peak_r; - peak_r = 0.0; - return peak; +EXPORT float get_level_peak_l() { + float peak = peak_l; + peak_l = 0.0; + return peak; } -void set_callbacks(void *start, void *read, void *close, void *device_unavailable){ - ff_start = start; - ff_read = read; - ff_close = close; - on_device_unavailable = device_unavailable; +EXPORT float get_level_peak_r() { + float peak = peak_r; + peak_r = 0.0; + return peak; } - -char* get_device(int n){ - #ifdef MINI - return pPlaybackDeviceInfos[n].name; - #endif - #ifdef PIPE - return pipe_devices.devices[n].description; - #endif +EXPORT void set_callbacks(void *start, void *read, void *close, void *device_unavailable) { + ff_start = start; + ff_read = read; + ff_close = close; + on_device_unavailable = device_unavailable; } -int get_spectrum(int n_bins, float* bins) { - - int samples = 2048; - int base = low; +EXPORT char* get_device(int n) { + #ifdef MINI + return pPlaybackDeviceInfos[n].name; + #endif + #ifdef PIPE + return pipe_devices.devices[n].description; + #endif +} - int i = 0; - while (i < samples) { - if (base >= watermark){ - base = 0; - } - rbuf[i] = bfl[base] * 0.5 * (1 - cos(2*3.1415926*i/samples)); - i++; - base += 1; - } +EXPORT int get_spectrum(int n_bins, float* bins) { + int samples = 2048; + int base = low; - kiss_fftr( ffta , rbuf , cbuf ); + int i = 0; + while (i < samples) { + if (base >= watermark) { + base = 0; + } + rbuf[i] = bfl[base] * 0.5 * (1 - cos(2*3.1415926*i/samples)); + i++; + base += 1; + } - i = 0; - while (i < samples / 2) { - rbuf[i] = sqrt((cbuf[i].r * cbuf[i].r) + (cbuf[i].i * cbuf[i].i)); - i++; - } + kiss_fftr( ffta , rbuf , cbuf ); - int b0 = 0; - for (int x = 0; x < n_bins; x++) { - float peak = 0; - int b1 = pow(2, x * 10.0 / (n_bins - 1)); - if (b1 > (samples / 2) - 1) b1 = (samples / 2) - 1; - if (b1 <= b0) b1 = b0 + 1; - for (; b0 < b1; b0++) { - if (peak < rbuf[1 + b0]) peak = rbuf[1 + b0]; - } - bins[x] = sqrt(peak); - } + i = 0; + while (i < samples / 2) { + rbuf[i] = sqrt((cbuf[i].r * cbuf[i].r) + (cbuf[i].i * cbuf[i].i)); + i++; + } - return 0; + int b0 = 0; + for (int x = 0; x < n_bins; x++) { + float peak = 0; + int b1 = pow(2, x * 10.0 / (n_bins - 1)); + if (b1 > (samples / 2) - 1) b1 = (samples / 2) - 1; + if (b1 <= b0) b1 = b0 + 1; + for (; b0 < b1; b0++) { + if (peak < rbuf[1 + b0]) peak = rbuf[1 + b0]; + } + bins[x] = sqrt(peak); + } + return 0; } - -int is_buffering(){ - if (buffering == 0) return 0; - return (int) (get_buff_fill() / config_min_buffer * 100.0); +EXPORT int is_buffering() { + if (buffering == 0) return 0; + return (int) (get_buff_fill() / config_min_buffer * 100.0); } -/* int get_latency(){ */ -/* return active_latency / 1000; */ + +/* EXPORT int get_latency() { */ +/* return active_latency / 1000; */ /* } */ -int feed_ready(int request_size){ - if (mode != STOPPED && high_mark - get_buff_fill() > request_size && codec == FEED) return 1; - return 0; +EXPORT int feed_ready(int request_size) { + if (mode != STOPPED && high_mark - get_buff_fill() > request_size && codec == FEED) return 1; + return 0; } -void feed_raw(int len, char* data){ - if (feed_ready(len) == 0) return; - pthread_mutex_lock(&buffer_mutex); - read_to_buffer_char16(data, len); - pthread_mutex_unlock(&buffer_mutex); +EXPORT void feed_raw(int len, char* data) { + if (feed_ready(len) == 0) return; + pthread_mutex_lock(&buffer_mutex); + read_to_buffer_char16(data, len); + pthread_mutex_unlock(&buffer_mutex); } -void set_subtrack(int n){ - subtrack = n; +EXPORT void set_subtrack(int n) { + subtrack = n; } -void print_status(){ - printf("command is %d, mode is %d, gate is %f\n", command, mode, gate); +EXPORT void print_status() { + printf("command is %d, mode is %d, gate is %f\n", command, mode, gate); } -int phazor_shutdown() { - while (command != NONE) { - usleep(1000); - } - command = EXIT; - return 0; +EXPORT int phazor_shutdown() { + while (command != NONE) { + usleep(1000); + } + command = EXIT; + return 0; } diff --git a/src/tauon/__main__.py b/src/tauon/__main__.py index 32491aa8e..b91eb8edf 100755 --- a/src/tauon/__main__.py +++ b/src/tauon/__main__.py @@ -83,7 +83,7 @@ ], ) # INFO+ to std_err -logging.getLogger().handlers[0].setLevel(logging.DEBUG if os.path.isfile(install_directory / "debug") else logging.INFO) +logging.getLogger().handlers[0].setLevel(logging.DEBUG if (install_directory / "debug").is_file() else logging.INFO) logging.getLogger().handlers[0].setFormatter(CustomLoggingFormatter()) # https://docs.python.org/3/library/warnings.html diff --git a/src/tauon/t_modules/t_main.py b/src/tauon/t_modules/t_main.py index c8d5fe62b..a47a067c7 100644 --- a/src/tauon/t_modules/t_main.py +++ b/src/tauon/t_modules/t_main.py @@ -608,7 +608,7 @@ else: logging.info("Running from installed location") - logging.info(f" User files location: {user_directory}") + logging.info(f"User files location: {user_directory}") if not Path(Path(user_directory) / "encoder").is_dir(): os.makedirs(Path(user_directory) / "encoder") @@ -4261,28 +4261,33 @@ def load_prefs(): if msys and win_ver >= 10: #logging.info(sss.info.win.window) - try: - sm = ctypes.cdll.LoadLibrary(os.path.join(install_directory, "lib", "TauonSMTC.dll")) - - def SMTC_button_callback(button: int) -> None: - - if button == 1: - inp.media_key = "Play" - if button == 2: - inp.media_key = "Pause" - if button == 3: - inp.media_key = "Next" - if button == 4: - inp.media_key = "Previous" - if button == 5: - inp.media_key = "Stop" - gui.update += 1 - tauon.wake() + SMTC_path = Path(install_directory) / "lib" / "TauonSMTC.dll" + if SMTC_path.exists(): + try: + sm = ctypes.cdll.LoadLibrary(str(SMTC_path)) + + def SMTC_button_callback(button: int) -> None: + + if button == 1: + inp.media_key = "Play" + if button == 2: + inp.media_key = "Pause" + if button == 3: + inp.media_key = "Next" + if button == 4: + inp.media_key = "Previous" + if button == 5: + inp.media_key = "Stop" + gui.update += 1 + tauon.wake() + + close_callback = ctypes.WINFUNCTYPE(ctypes.c_void_p, ctypes.c_int)(SMTC_button_callback) + smtc = sm.init(close_callback) == 0 + except Exception: + logging.exception("Failed to load TauonSMTC.dll - Media keys will not work!") + else: + logging.warning("Failed to load TauonSMTC.dll - Media keys will not work!") - close_callback = ctypes.WINFUNCTYPE(ctypes.c_void_p, ctypes.c_int)(SMTC_button_callback) - smtc = sm.init(close_callback) == 0 - except Exception: - logging.exception("Failed to load TauonSMTC.dll") def auto_scale() -> None: diff --git a/src/tauon/t_modules/t_phazor.py b/src/tauon/t_modules/t_phazor.py index 24b8eb439..da51973cc 100644 --- a/src/tauon/t_modules/t_phazor.py +++ b/src/tauon/t_modules/t_phazor.py @@ -49,7 +49,7 @@ def find_library(libname: str) -> Path | None: - """Search for 'libname.so'. + """Search for 'libname.extension' in various formats. Return library Path loadable with ctypes.CDLL, otherwise return None """ @@ -78,20 +78,19 @@ def find_library(libname: str) -> Path | None: logging.debug(f"Lib {libname} found in {path!s}") return path -# raise OSError(f"Can't find {libname}.so. Searched at:\n" + "\n".join(str(p) for p in search_paths)) +# raise OSError(f"Can't find library '{libname}'. Searched at:\n" + "\n".join(str(p) for p in search_paths)) return None def get_phazor_path(pctl: PlayerCtl) -> Path: - """ - Locate the PHaZOR library in the specified priority order. - Tries .so, .dll, .dynlib in that order and finally uses find_library as a fallback. + """Locate the PHaZOR library in the specified priority order. - :param pctl: PlayerCtl object containing installation details - :return: Path to the library file - :raises Exception: If no library is found - """ + Tries .so, .dll, .pyd, .dynlib in that order and finally uses find_library as a fallback. + :param pctl: PlayerCtl object containing installation details + :return: Path to the library file + :raises Exception: If no library is found + """ # This is where compile-phazor.sh scrips place the dll base_path = Path(pctl.install_directory).parent.parent / "build" @@ -101,7 +100,7 @@ def get_phazor_path(pctl: PlayerCtl) -> Path: else: lib_name = "phazor" - extensions = [".so", ".dll", ".dynlib"] + extensions = [".so", ".dll", ".pyd", ".dynlib"] # Check explicitly for each file for ext in extensions: diff --git a/windows.spec b/windows.spec new file mode 100644 index 000000000..6d07340a3 --- /dev/null +++ b/windows.spec @@ -0,0 +1,65 @@ +a = Analysis( + ["src/tauon/__main__.py"], + pathex=[], + binaries=[ + ("C:\\msys64\\mingw64\\bin\\libFLAC.dll", "."), + ("C:\\msys64\\mingw64\\bin\\libmpg123-0.dll", "."), + ("C:\\msys64\\mingw64\\bin\\libogg-0.dll", "."), + ("C:\\msys64\\mingw64\\bin\\libopenmpt-0.dll", "."), + ("C:\\msys64\\mingw64\\bin\\libopus-0.dll", "."), + ("C:\\msys64\\mingw64\\bin\\libopusfile-0.dll", "."), + ("C:\\msys64\\mingw64\\bin\\libsamplerate-0.dll", "."), + ("C:\\msys64\\mingw64\\bin\\libvorbis-0.dll", "."), + ("C:\\msys64\\mingw64\\bin\\libvorbisfile-3.dll", "."), + ("C:\\msys64\\mingw64\\bin\\libwavpack-1.dll", "."), + ("C:\\msys64\\mingw64\\bin\\SDL2.dll", "."), + ("C:\\msys64\\mingw64\\bin\\SDL2_image.dll", "."), + ("C:\\msys64\\mingw64\\bin\\libgme.dll", "."), + ], + datas=[], + hiddenimports=[ + "infi.systray", + "pylast", + "tekore", + "phazor", + "pip", + "packaging.requirements", + "pkg_resources.py2_warn", + "requests", + ], + hookspath=["extra\\pyinstaller-hooks"], + hooksconfig={}, + runtime_hooks=[], + excludes=[], + noarchive=False, + optimize=0, +) +pyz = PYZ(a.pure) + +exe = EXE( + pyz, + a.scripts, + [], + exclude_binaries=True, + name="Tauon Music Box", + debug=False, + bootloader_ignore_signals=False, + strip=False, + upx=True, + console=False, + disable_windowed_traceback=False, + argv_emulation=False, + target_arch=None, + codesign_identity=None, + entitlements_file=None, + icon=["src/tauon/assets/icon.ico"], +) +coll = COLLECT( + exe, + a.binaries, + a.datas, + strip=False, + upx=True, + upx_exclude=[], + name="TauonMusicBox", +)