├── .editorconfig ├── .github ├── FUNDING.yml └── workflows │ ├── appimage_alpine.yml │ ├── arm_macos.yml │ └── ci.yml ├── .gitignore ├── AUTHORS.md ├── CHANGELOG.md ├── CONTRIBUTING.md ├── DEVELOPERS.md ├── LICENSE ├── Makefile ├── README.md ├── SECURITY.md ├── appdir ├── kew.desktop └── usr │ └── share │ ├── applications │ └── kew.desktop │ └── icons │ └── hicolor │ ├── 128x128 │ └── apps │ │ └── kew.png │ ├── 256x256 │ └── apps │ │ └── kew.png │ └── 64x64 │ └── apps │ └── kew.png ├── docs ├── kew-manpage.mdoc └── kew.1 ├── images └── kew-screenshot.png ├── include ├── alac │ ├── LICENSE │ └── codec │ │ ├── ALACAudioTypes.h │ │ ├── ALACBitUtilities.c │ │ ├── ALACBitUtilities.h │ │ ├── ALACDecoder.cpp │ │ ├── ALACDecoder.h │ │ ├── APPLE_LICENSE.txt │ │ ├── EndianPortable.c │ │ ├── EndianPortable.h │ │ ├── ag_dec.c │ │ ├── aglib.h │ │ ├── alac_wrapper.cpp │ │ ├── alac_wrapper.h │ │ ├── dp_dec.c │ │ ├── dplib.h │ │ ├── matrix_dec.c │ │ └── matrixlib.h ├── miniaudio │ ├── miniaudio.h │ ├── miniaudio_libopus.h │ └── miniaudio_libvorbis.h ├── minimp4 │ └── minimp4.h ├── nestegg │ ├── LICENSE │ ├── nestegg.c │ └── nestegg.h └── stb_image │ ├── stb_image.h │ └── stb_image_resize2.h └── src ├── appstate.h ├── cache.c ├── cache.h ├── common.c ├── common.h ├── common_ui.c ├── common_ui.h ├── directorytree.c ├── directorytree.h ├── events.h ├── file.c ├── file.h ├── imgfunc.c ├── imgfunc.h ├── kew.c ├── m4a.c ├── m4a.h ├── mpris.c ├── mpris.h ├── notifications.c ├── notifications.h ├── player_ui.c ├── player_ui.h ├── playerops.c ├── playerops.h ├── playlist.c ├── playlist.h ├── playlist_ui.c ├── playlist_ui.h ├── search_ui.c ├── search_ui.h ├── settings.c ├── settings.h ├── songloader.c ├── songloader.h ├── sound.c ├── sound.h ├── soundbuiltin.c ├── soundbuiltin.h ├── soundcommon.c ├── soundcommon.h ├── tagLibWrapper.cpp ├── tagLibWrapper.h ├── term.c ├── term.h ├── utils.c ├── utils.h ├── visuals.c ├── visuals.h └── webm.h /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: https://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | [*] 7 | indent_style = space 8 | indent_size = 8 9 | end_of_line = lf 10 | charset = utf-8 11 | trim_trailing_whitespace = true 12 | insert_final_newline = true -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: ravachol 2 | -------------------------------------------------------------------------------- /.github/workflows/appimage_alpine.yml: -------------------------------------------------------------------------------- 1 | name: Alpine-appimage 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | build-and-create-appimage: 8 | runs-on: ubuntu-latest 9 | container: 10 | image: alpine:latest 11 | 12 | steps: 13 | - name: Checkout code 14 | uses: actions/checkout@v2 15 | 16 | - name: Install build essentials and dependencies 17 | run: | 18 | apk update 19 | apk add --no-cache \ 20 | build-base \ 21 | taglib-dev \ 22 | fftw-dev \ 23 | chafa-dev \ 24 | opus-dev \ 25 | opusfile-dev \ 26 | libvorbis-dev \ 27 | libogg-dev \ 28 | glib-dev \ 29 | wget git desktop-file-utils \ 30 | squashfs-tools \ 31 | patchelf \ 32 | musl \ 33 | musl-dev \ 34 | gcompat \ 35 | curl-dev 36 | 37 | - name: Build code with static linking 38 | run: | 39 | # export CC=musl-gcc 40 | # export LDFLAGS="-static -Wl,-z,relro,-lz" 41 | make 42 | 43 | - name: Prepare AppDir 44 | run: | 45 | mkdir -p appdir/usr/bin 46 | chmod +x ./kew 47 | mv ./kew appdir/usr/bin/ 48 | mkdir -p appdir/usr/lib 49 | 50 | - name: Download uploadtool 51 | run: | 52 | wget -q https://github.com/probonopd/uploadtool/raw/master/upload.sh 53 | chmod +x upload.sh 54 | mv upload.sh /usr/local/bin/uploadtool 55 | 56 | - name: Download and prepare appimagetool 57 | run: | 58 | wget -O appimagetool-x86_64.AppImage -c https://github.com/$(wget -q https://github.com/probonopd/go-appimage/releases/expanded_assets/continuous -O - | grep "appimagetool-.*-x86_64.AppImage" | head -n 1 | cut -d '"' -f 2) 59 | if [ ! -f appimagetool-*.AppImage ]; then 60 | echo "appimagetool download failed"; exit 1; 61 | fi 62 | chmod +x appimagetool-x86_64.AppImage 63 | 64 | - name: Use appimagetool with --appimage-extract-and-run 65 | run: | 66 | ./appimagetool-x86_64.AppImage --appimage-extract-and-run deploy appdir/usr/share/applications/kew.desktop 67 | 68 | - name: Create AppImage 69 | run: | 70 | mkdir -p output 71 | APPIMAGE_EXTRACT_AND_RUN=1 \ 72 | ARCH=$(uname -m) \ 73 | VERSION=$(./appdir/usr/bin/kew --version | awk -F": " 'FNR==6 {printf $NF}') \ 74 | ./appimagetool-*.AppImage ./appdir 75 | 76 | - name: Move and Rename kew AppImage 77 | run: | 78 | mv kew*.AppImage output/kew 79 | chmod +x output/kew 80 | 81 | - name: Release 82 | uses: marvinpinto/action-automatic-releases@latest 83 | with: 84 | title: kew appImage (musl systems) 85 | automatic_release_tag: stable-musl 86 | prerelease: false 87 | draft: true 88 | files: | 89 | output/kew 90 | repo_token: ${{ secrets.GITHUB_TOKEN }} 91 | -------------------------------------------------------------------------------- /.github/workflows/arm_macos.yml: -------------------------------------------------------------------------------- 1 | name: Build Check macOS 2 | 3 | on: 4 | pull_request: 5 | push: 6 | paths: 7 | - 'Makefile' 8 | workflow_dispatch: 9 | 10 | jobs: 11 | macos-build-check: 12 | name: macOS Build Check 13 | runs-on: macos-latest 14 | 15 | steps: 16 | - name: Checkout code 17 | uses: actions/checkout@v2 18 | 19 | - name: Check system architecture 20 | run: uname -m 21 | 22 | - name: Install Homebrew 23 | run: | 24 | if ! command -v brew &> /dev/null; then 25 | /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" 26 | fi 27 | 28 | - name: Update Homebrew and install dependencies 29 | run: | 30 | brew update 31 | brew install faad2 taglib chafa fftw opus opusfile libogg libvorbis make curl 32 | 33 | - name: Build code 34 | run: make 35 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Build Check 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: 7 | - main 8 | 9 | jobs: 10 | ubuntu-build-check: 11 | name: Ubuntu Build Check 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - name: Checkout code 16 | uses: actions/checkout@v2 17 | 18 | - name: Install build essentials 19 | run: sudo apt-get update && sudo apt-get install -y build-essential 20 | 21 | - name: Install dependencies 22 | run: sudo apt install -y libcurl4-openssl-dev libtag1-dev libfaad-dev libogg-dev libfftw3-dev libopus-dev libopusfile-dev libvorbis-dev libchafa-dev libavformat-dev libstb-dev libglib2.0-dev 23 | 24 | - name: Build code 25 | run: make 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | kew 35 | .vscode/ 36 | error.log 37 | valgrind-out.txt 38 | -------------------------------------------------------------------------------- /AUTHORS.md: -------------------------------------------------------------------------------- 1 | # AUTHORS 2 | 3 | ## Maintainers 4 | * **Ravachol** @ravachol 5 | * Founder and Main Author 6 | 7 | ## Contributors (in alphabetical order) 8 | 9 | * Chromium-3-Oxide 10 | * Davis @kazemaksOG 11 | * DNEGEL3125 @DNEGEL3125 12 | * Hans Petter Jansson @hpjansson 13 | * John Lakeman @j-lakeman 14 | * Matthias Geiger @werdahias 15 | * Ravachol @ravachol 16 | * Rowan Shi @rowanxshi 17 | * Rui Chen @chenrui333 18 | * Ruoyu Zhong @ZhongRuoyu 19 | * Ryouji @soryu-ryouji 20 | * Samuel @Samueru-sama 21 | * Vafone @vafone 22 | * Xplshn @xplshn 23 | * Zane Godden @mechatour 24 | 25 | ## Testers 26 | 27 | * Vafone @vafone 28 | * Nicolas F 29 | * Ravachol @ravachol 30 | 31 | ## Special Thanks 32 | 33 | We would like to extend our gratitude to the following individuals who have contributed significantly to kew: 34 | 35 | * Xplshn @xplshn 36 | * David Reid @mackron (author of Miniaudio, used for playing music) 37 | * Hans Petter Jansson @hpjansson (author of Chafa, used for displaying images) 38 | * Matthias Geiger @werdahias 39 | * Yuri @yurivict yuri@freebsd.org 40 | * Joey Schaff @jaoheah 41 | * Agustin Ballesteros @agus-balles 42 | * Mateo @malteo 43 | * Hampa @hampa 44 | * Markmark1 @markmark1 45 | * VDawg @vdawg-git 46 | * INIROBO @inirobo 47 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # CONTRIBUTING 2 | 3 | ## Welcome to kew contributing guide 4 | 5 | Thank you for your interest in contributing to kew! 6 | 7 | ### Goal of the project 8 | 9 | The goal of kew is to provide a quick and easy way for people to listen to music with the absolute minimum of inconvenience. 10 | It's a small app, limited in scope and it shouldn't be everything to all people. It should continue to be a very light weight app. 11 | For instance, it's not imagined as a software for dj'ing or as a busy music file manager with all the features. 12 | 13 | We want to keep the codebase easy to manage and free of bloat, so might reject a feature out of that reason only. 14 | 15 | ### Bugs 16 | 17 | Please report any bugs directly on github, with as much relevant detail as possible. 18 | If there's a crash or stability issue, the audio file details are interesting, but also the details of the previous and next file on the playlist. You can extract these details by running: 19 | ffprobe -i AUDIO FILE -show_streams -select_streams a:0 -v quiet -print_format json 20 | 21 | ### Create a pull request 22 | 23 | After making any changes, open a pull request on Github. 24 | 25 | - Please contact me (kew-music-player@proton.me) before doing a big change, or risk the whole thing getting rejected. 26 | 27 | - Try to keep commits fairly small so that they are easy to review. 28 | 29 | - If you're fixing a particular bug in the issue list, please explicitly say "Fixes #" in your description". 30 | 31 | Once your PR has been reviewed and merged, you will be proudly listed as a contributor in the [contributor chart](https://github.com/ravachol/kew/graphs/contributors)! 32 | 33 | ### Issue assignment 34 | 35 | We don't have a process for assigning issues to contributors. Please feel free to jump into any issues in this repo that you are able to help with. Our intention is to encourage anyone to help without feeling burdened by an assigned task. Life can sometimes get in the way, and we don't want to leave contributors feeling obligated to complete issues when they may have limited time or unexpected commitments. 36 | 37 | We also recognize that not having a process could lead to competing or duplicate PRs. There's no perfect solution here. We encourage you to communicate early and often on an Issue to indicate that you're actively working on it. If you see that an Issue already has a PR, try working with that author instead of drafting your own. 38 | -------------------------------------------------------------------------------- /DEVELOPERS.md: -------------------------------------------------------------------------------- 1 | # DEVELOPERS 2 | 3 | ## Getting started 4 | 5 | This document will help you setup your development environment. 6 | 7 | ### Prerequisites 8 | 9 | Before contributing, ensure you have the following tools installed on your development machine: 10 | 11 | - [GCC](https://gcc.gnu.org/) (or another compatible C/C++ compiler) 12 | - [Make](https://www.gnu.org/software/make/) 13 | - [Git](https://git-scm.com/) 14 | - [Valgrind](http://valgrind.org/) (optional, for memory debugging and profiling) 15 | - [VSCode](https://code.visualstudio.com/) (or other debugger) 16 | 17 | ### Building the Project 18 | 19 | 1. Clone the repository: 20 | ``` 21 | git clone https://github.com/ravachol/kew.git 22 | cd kew 23 | ``` 24 | 25 | 2. To enable debugging symbols, run make with DEBUG=1 26 | 27 | 3. Build the project: 28 | ``` 29 | make DEBUG=1 -j$(nproc) # Use all available processor cores for faster builds 30 | ``` 31 | 32 | ### Debugging with Visual Studio Code 33 | 34 | To enable debugging in VSCode, you'll need to create a `launch.json` file that configures the debugger. Follow these steps: 35 | 36 | 1. Open your project's folder in VSCode. 37 | 38 | 2. Press `F5` or go to the "Run and Debug" sidebar (`Ctrl+Shift+D` on Windows/Linux, `Cmd+Shift+D` on macOS), then click on the gear icon to create a new launch configuration file. 39 | 40 | 3. Select "C++ (GDB/LLDB)" as the debugger type, and choose your platform (e.g., x64-linux, x86-win32, etc.). 41 | 42 | 4. Replace the contents of the generated `launch.json` file with the following, adjusting paths and arguments as needed: 43 | 44 | ```json 45 | { 46 | "version": "0.2.0", 47 | "configurations": [ 48 | 49 | 50 | { 51 | "name": "kew", 52 | "type": "cppdbg", 53 | "request": "launch", 54 | "program": "${workspaceFolder}/kew", 55 | //"args": ["artist or song name"], 56 | "stopAtEntry": false, 57 | "cwd": "${workspaceFolder}", 58 | "environment": [], 59 | "externalConsole": true, 60 | "MIMode": "gdb", 61 | "setupCommands": [ 62 | { 63 | "description": "Enable pretty-printing for gdb", 64 | "text": "-enable-pretty-printing", 65 | "ignoreFailures": true 66 | } 67 | ] 68 | } 69 | ] 70 | } 71 | ``` 72 | 73 | 5. Save the `launch.json` file. 74 | 75 | 6. Create a c_cpp_properties.json file in the same folder (.vscode) with the following contents adjusting paths and arguments as needed: 76 | 77 | ```json 78 | { 79 | "configurations": [ 80 | { 81 | "name": "linux-gcc-x64", 82 | "includePath": [ 83 | "${workspaceFolder}/include/miniaudio", 84 | "${workspaceFolder}/include/nestegg", 85 | "${workspaceFolder}/**", 86 | "/usr/include", 87 | "/usr/include/opus", 88 | "/usr/include/vorbis", 89 | "/usr/include/chafa/", 90 | "/lib/chafa/include", 91 | "/usr/include/glib-2.0", 92 | "/usr/lib/glib-2.0/include", 93 | "/usr/include/libmount", 94 | "/usr/include/blkid", 95 | "/usr/include/sysprof-6", 96 | "/usr/include/glib-2.0/gio", 97 | "/usr/include/glib-2.0", 98 | "${workspaceFolder}/include" 99 | ], 100 | "browse": { 101 | "path": [ 102 | "${workspaceFolder}/include/miniaudio", 103 | "${workspaceFolder}/src", 104 | "${workspaceFolder}/include", 105 | "${workspaceFolder}/**" 106 | ], 107 | "limitSymbolsToIncludedHeaders": true 108 | }, 109 | "defines": [ 110 | "_POSIX_C_SOURCE=200809L" 111 | ], 112 | "compilerPath": "/usr/bin/gcc", 113 | "cStandard": "${default}", 114 | "cppStandard": "${default}", 115 | "intelliSenseMode": "linux-gcc-x64" 116 | } 117 | ], 118 | "version": 4 119 | } 120 | 121 | ``` 122 | 123 | 7. Add the extensions C/C++, C/C++ Extension pack, C/C++ Themes (optional). 124 | 125 | 8. Now you can use VSCode's debugger to step through your code, inspect variables, and analyze any issues: 126 | 127 | * Set breakpoints in your source code by placing your cursor on the desired line number, then press `F9`. 128 | * Press `F5` or click on the "Start Debugging" button (or go to the "Run and Debug" sidebar) to start the debugger. 129 | * When the execution reaches a breakpoint, VSCode will pause, allowing you to use its built-in features for debugging. 130 | 131 | 132 | #### Finding where libs are located 133 | 134 | If the paths in c_cpp_properties.json are wrong for your OS, to find the folder where for instance Chafa library is installed, you can use one of the following methods: 135 | 136 | 1. **Using `pkg-config`**: 137 | 138 | The `pkg-config` tool is a helper tool used to determine compiler flags and linker flags for libraries. You can use it to find the location of Chafa's include directory. Open your terminal and run the following command: 139 | 140 | ``` 141 | pkg-config --cflags chafa 142 | ``` 143 | 144 | This should display the `-I` flags required to include Chafa's headers, which in turn will reveal the installation prefix (e.g., `/usr/include/chafa/`). The folder containing the library files itself is typically located under `lib` or `lib64`, so you can find it by looking for a folder named `chafa` within those directories. 145 | 146 | 2. **Using `brew` (for macOS)**: 147 | 148 | If you installed Chafa using Homebrew, you can find its installation prefix with the following command: 149 | 150 | ``` 151 | brew --prefix chafa 152 | ``` 153 | 154 | This will display the installation prefix for Chafa (e.g., `/usr/local/opt/chafa`). 155 | 156 | 3. **Manually searching**: 157 | 158 | Alternatively, you can search your file system manually for the `chafa` folder or library files. On Unix-based systems like Linux and macOS, libraries are typically installed under `/usr`, `/usr/local`, or within the user's home directory (e.g., `~/.local`). You can use the `find` command to search for the folder: 159 | 160 | ``` 161 | find /usr /usr/local ~/.local -name chafa 162 | ``` 163 | 164 | This should display the location of the Chafa installation, revealing both the include and library folders. 165 | 166 | ### Valgrind 167 | 168 | To use Valgrind for memory debugging and profiling: 169 | 170 | 1. Build your project with debug symbols (`-g`) and position-independent executables (`-no-pie`): 171 | 172 | 2. Run Valgrind on your binary: 173 | ``` 174 | valgrind --leak-check=full --track-origins=yes --show-leak-kinds=all --log-file=valgrind-out.txt ./kew 175 | ``` 176 | 177 | ### Editorconfig 178 | 179 | - If you can, use EditorConfig for VS Code Extension. There is a file with settings for it: .editorconfig. 180 | 181 | ### Contributing 182 | 183 | For further information on how to contribute, see the file CONTRIBUTING.md. 184 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CC ?= gcc 2 | CXX ?= g++ 3 | PKG_CONFIG ?= pkg-config 4 | 5 | # To enable debugging, run: 6 | # make DEBUG=1 7 | # To disable DBUS notifications, run: 8 | # make USE_DBUS=0 9 | # To disable faad2, run: 10 | # make USE_FAAD=0 11 | 12 | # Detect system and architecture 13 | UNAME_S := $(shell uname -s) 14 | ARCH := $(shell uname -m) 15 | 16 | # Default USE_DBUS to auto-detect if not set by user 17 | ifeq ($(origin USE_DBUS), undefined) 18 | ifeq ($(UNAME_S), Darwin) 19 | USE_DBUS = 0 20 | else 21 | USE_DBUS = 1 22 | endif 23 | endif 24 | 25 | # Adjust the PREFIX for macOS and Linux 26 | ifeq ($(UNAME_S), Darwin) 27 | ifeq ($(ARCH), arm64) 28 | PREFIX ?= /usr/local 29 | PKG_CONFIG_PATH := /opt/homebrew/lib/pkgconfig:/opt/homebrew/share/pkgconfig:$(PKG_CONFIG_PATH) 30 | else 31 | PREFIX ?= /usr/local 32 | PKG_CONFIG_PATH := /usr/local/lib/pkgconfig:/usr/local/share/pkgconfig:$(PKG_CONFIG_PATH) 33 | endif 34 | else 35 | PREFIX ?= /usr/local 36 | PKG_CONFIG_PATH := /usr/lib/x86_64-linux-gnu/pkgconfig:/usr/lib/pkgconfig:/usr/share/pkgconfig:$(PKG_CONFIG_PATH) 37 | endif 38 | 39 | # Default USE_FAAD to auto-detect if not set by user 40 | ifeq ($(origin USE_FAAD), undefined) 41 | 42 | USE_FAAD = $(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) $(PKG_CONFIG) --exists faad && echo 1 || echo 0) 43 | 44 | ifeq ($(USE_FAAD), 0) 45 | # If pkg-config fails, try to find libfaad dynamically in common paths 46 | USE_FAAD = $(shell [ -f /usr/lib/libfaad.so ] || [ -f /usr/local/lib/libfaad.so ] || \ 47 | [ -f /opt/local/lib/libfaad.so ] || [ -f /opt/homebrew/lib/libfaad.dylib ] || \ 48 | [ -f /opt/homebrew/opt/faad2/lib/libfaad.dylib ] || \ 49 | [ -f /usr/local/lib/libfaad.dylib ] || [ -f /lib/x86_64-linux-gnu/libfaad.so.2 ] && echo 1 || echo 0) 50 | endif 51 | endif 52 | 53 | # Compiler flags 54 | COMMONFLAGS = -I/usr/include -I/opt/homebrew/include -I/usr/local/include -I/usr/lib -Iinclude/minimp4 \ 55 | -I/usr/include/chafa -I/usr/lib/chafa/include -I/usr/include/ogg -I/usr/include/opus \ 56 | -I/usr/include/stb -Iinclude/stb_image -Iinclude/alac/codec -I/usr/include/glib-2.0 \ 57 | -I/usr/lib/glib-2.0/include -Iinclude/miniaudio -Iinclude -Iinclude/nestegg -I/usr/include/gdk-pixbuf-2.0 58 | 59 | ifeq ($(DEBUG), 1) 60 | COMMONFLAGS += -g -DDEBUG 61 | else 62 | COMMONFLAGS += -O2 63 | endif 64 | 65 | COMMONFLAGS += $(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) $(PKG_CONFIG) --cflags gio-2.0 chafa fftw3f opus opusfile vorbis ogg glib-2.0 taglib) 66 | COMMONFLAGS += -fstack-protector-strong -Wformat -Werror=format-security -fPIE -D_FORTIFY_SOURCE=2 67 | COMMONFLAGS += -Wall -Wextra -Wpointer-arith -flto 68 | CFLAGS = $(COMMONFLAGS) 69 | 70 | # Compiler flags for C++ code 71 | CXXFLAGS = $(COMMONFLAGS) -std=c++11 72 | 73 | # Libraries 74 | LIBS = -L/usr/lib -lm -lopusfile -lglib-2.0 -lpthread $(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) $(PKG_CONFIG) --libs gio-2.0 chafa fftw3f opus opusfile ogg vorbis vorbisfile glib-2.0 taglib) 75 | LIBS += -lstdc++ 76 | 77 | LDFLAGS = -logg -lz -flto 78 | 79 | ifeq ($(UNAME_S), Linux) 80 | CFLAGS += -fPIE -fstack-clash-protection 81 | CXXFLAGS += -fPIE -fstack-clash-protection 82 | LDFLAGS += -pie -Wl,-z,relro 83 | ifneq (,$(filter $(ARCH), x86_64 i386)) 84 | CFLAGS += -fcf-protection 85 | CXXFLAGS += -fcf-protection 86 | endif 87 | ifneq ($(DEBUG), 1) 88 | LDFLAGS += -s 89 | endif 90 | else ifeq ($(UNAME_S), Darwin) 91 | LIBS += -framework CoreAudio -framework CoreFoundation 92 | endif 93 | 94 | # Conditionally add USE_DBUS is enabled 95 | ifeq ($(USE_DBUS), 1) 96 | DEFINES += -DUSE_DBUS 97 | endif 98 | 99 | # Conditionally add faad2 support if USE_FAAD is enabled 100 | ifeq ($(USE_FAAD), 1) 101 | ifeq ($(ARCH), arm64) 102 | CFLAGS += -I/opt/homebrew/opt/faad2/include 103 | LIBS += -L/opt/homebrew/opt/faad2/lib -lfaad 104 | else 105 | CFLAGS += -I/usr/local/include 106 | LIBS += -L/usr/local/lib -lfaad 107 | endif 108 | DEFINES += -DUSE_FAAD 109 | endif 110 | 111 | ifeq ($(origin CC),default) 112 | CC := gcc 113 | endif 114 | 115 | ifneq ($(findstring gcc,$(CC)),) 116 | ifeq ($(UNAME_S), Linux) 117 | LIBS += -latomic 118 | endif 119 | endif 120 | 121 | OBJDIR = src/obj 122 | 123 | SRCS = src/common_ui.c src/common.c src/sound.c src/directorytree.c src/notifications.c \ 124 | src/soundcommon.c src/m4a.c src/search_ui.c src/playlist_ui.c \ 125 | src/player_ui.c src/soundbuiltin.c src/mpris.c src/playerops.c \ 126 | src/utils.c src/file.c src/imgfunc.c src/cache.c src/songloader.c \ 127 | src/playlist.c src/term.c src/settings.c src/visuals.c src/kew.c 128 | 129 | # TagLib wrapper 130 | WRAPPER_SRC = src/tagLibWrapper.cpp 131 | WRAPPER_OBJ = $(OBJDIR)/tagLibWrapper.o 132 | 133 | MAN_PAGE = kew.1 134 | MAN_DIR ?= $(PREFIX)/share/man 135 | 136 | all: kew 137 | 138 | # ALAC codec sources 139 | ALAC_SRCS_CPP = include/alac/codec/ALACDecoder.cpp include/alac/codec/alac_wrapper.cpp 140 | ALAC_SRCS_C = include/alac/codec/ag_dec.c include/alac/codec/ALACBitUtilities.c \ 141 | include/alac/codec/dp_dec.c include/alac/codec/EndianPortable.c \ 142 | include/alac/codec/matrix_dec.c 143 | 144 | # Generate object lists 145 | OBJS_C = $(SRCS:src/%.c=$(OBJDIR)/%.o) $(ALAC_SRCS_C:include/alac/codec/%.c=$(OBJDIR)/alac/%.o) 146 | OBJS_CPP = $(ALAC_SRCS_CPP:include/alac/codec/%.cpp=$(OBJDIR)/alac/%.o) 147 | 148 | NESTEGG_SRCS = include/nestegg/nestegg.c 149 | NESTEGG_OBJS = $(NESTEGG_SRCS:include/nestegg/%.c=$(OBJDIR)/nestegg/%.o) 150 | 151 | # All objects together 152 | OBJS = $(OBJS_C) $(OBJS_CPP) $(NESTEGG_OBJS) 153 | 154 | # Create object directories 155 | $(OBJDIR): 156 | mkdir -p $(OBJDIR) $(OBJDIR)/alac 157 | 158 | ## Compile C sources 159 | $(OBJDIR)/%.o: src/%.c Makefile | $(OBJDIR) 160 | @mkdir -p $(dir $@) 161 | $(CC) $(CFLAGS) $(DEFINES) -c -o $@ $< 162 | 163 | # Compile ALAC C files 164 | $(OBJDIR)/alac/%.o: include/alac/codec/%.c Makefile | $(OBJDIR) 165 | @mkdir -p $(dir $@) 166 | $(CC) $(CFLAGS) $(DEFINES) -c -o $@ $< 167 | 168 | # Compile ALAC C++ files 169 | $(OBJDIR)/alac/%.o: include/alac/codec/%.cpp Makefile | $(OBJDIR) 170 | @mkdir -p $(dir $@) 171 | $(CXX) $(CXXFLAGS) $(DEFINES) -c -o $@ $< 172 | 173 | # Compile explicit C++ sources in src/ 174 | $(OBJDIR)/%.o: src/%.cpp Makefile | $(OBJDIR) 175 | @mkdir -p $(dir $@) 176 | $(CXX) $(CXXFLAGS) $(DEFINES) -c -o $@ $< 177 | 178 | # Compile TagLib wrapper C++ source 179 | $(WRAPPER_OBJ): $(WRAPPER_SRC) Makefile | $(OBJDIR) 180 | @mkdir -p $(dir $@) 181 | $(CXX) $(CXXFLAGS) $(DEFINES) -c $< -o $@ 182 | 183 | # Compile C files in include/nestegg 184 | $(OBJDIR)/nestegg/%.o: include/nestegg/%.c Makefile | $(OBJDIR) 185 | @mkdir -p $(dir $@) 186 | $(CC) $(CFLAGS) $(DEFINES) -c -o $@ $< 187 | 188 | # Link all objects safely together using C++ linker 189 | kew: $(OBJS) $(WRAPPER_OBJ) Makefile 190 | $(CXX) -o kew $(OBJS) $(WRAPPER_OBJ) $(LIBS) $(LDFLAGS) 191 | 192 | .PHONY: install 193 | install: all 194 | mkdir -p $(DESTDIR)$(MAN_DIR)/man1 195 | mkdir -p $(DESTDIR)$(PREFIX)/bin 196 | install -m 0755 kew $(DESTDIR)$(PREFIX)/bin/kew 197 | install -m 0644 docs/kew.1 $(DESTDIR)$(MAN_DIR)/man1/kew.1 198 | 199 | .PHONY: uninstall 200 | uninstall: 201 | rm -f $(DESTDIR)$(PREFIX)/bin/kew 202 | rm -f $(DESTDIR)$(MAN_DIR)/man1/kew.1 203 | 204 | .PHONY: clean 205 | clean: 206 | rm -rf $(OBJDIR) kew 207 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # kew 2 | [![GitHub license](https://img.shields.io/github/license/ravachol/kew?color=333333&style=for-the-badge)](https://github.com/ravachol/kew/blob/master/LICENSE) 3 | 4 | Listen to music in the terminal. 5 | 6 | ![Example screenshot](images/kew-screenshot.png)
7 | *Example screenshot running in Konsole: [Jenova 7: Lost Sci-Fi Movie Themes](https://jenova7.bandcamp.com/album/lost-sci-fi-movie-themes).* 8 | 9 | 10 | 11 | 12 | 13 | kew (/kjuː/) is a terminal music player. 14 | 15 | ## Features 16 | 17 | * Search a music library with partial titles. 18 | * Creates a playlist based on a matched directory. 19 | * Control the player with previous, next and pause. 20 | * Edit the playlist by adding and removing songs. 21 | * Gapless playback (between files of the same format and type). 22 | * Supports MP3, FLAC, MPEG-4/M4A (AAC, ALAC), OPUS, OGG, Webm and WAV audio. 23 | * Supports desktop events through MPRIS. 24 | * Private, no data is collected by kew. 25 | 26 | ## Installing 27 | 28 | Packaging status 29 | 30 | 31 | ### Installing with package managers 32 | 33 | kew is available from Ubuntu 24.04. 34 | 35 | ```bash 36 | sudo apt install kew (Debian, Ubuntu) 37 | yay -S kew (Arch Linux, Manjaro) 38 | yay -S kew-git (Arch Linux, Manjaro) 39 | sudo zypper install kew (OpenSUSE) 40 | sudo pkg install kew (FreeBSD) 41 | brew install kew (macOS, Linux) 42 | apk add kew (Alpine Linux) 43 | ``` 44 | 45 | ### Building the project manually 46 | 47 | kew dependencies are: 48 | 49 | * FFTW 50 | * Chafa 51 | * libopus 52 | * opusfile 53 | * libvorbis 54 | * TagLib 55 | * faad2 (optional) 56 | * libogg 57 | * pkg-config 58 | * glib2.0 59 | 60 | Install these dependencies using your distro's package manager. Below are some examples. 61 | 62 | #### For Debian/Ubuntu: 63 | 64 | ```bash 65 | sudo apt install -y pkg-config libfaad-dev libtag1-dev libfftw3-dev libopus-dev libopusfile-dev libvorbis-dev libogg-dev git gcc make libchafa-dev libglib2.0-dev 66 | ``` 67 | 68 | #### For Arch Linux: 69 | 70 | ```bash 71 | sudo pacman -Syu --noconfirm --needed pkg-config faad2 taglib fftw git gcc make chafa glib2 opus opusfile libvorbis libogg 72 | ``` 73 | 74 | #### For macOS: 75 | 76 | Install git: 77 | 78 | ```bash 79 | xcode-select --install 80 | ``` 81 | 82 | Install dependencies: 83 | 84 | ```bash 85 | brew install gettext faad2 taglib chafa fftw opus opusfile libvorbis libogg glib pkg-config make 86 | ``` 87 | Notes for mac users: 88 | 1) A sixel-capable terminal like kitty or WezTerm is recommended for macOS. 89 | 2) The visualizer and album colors are disabled by default on macOS, because the default terminal doesn't handle them too well. To enable press v and i respectively. 90 | 91 | #### For Fedora: 92 | 93 | ```bash 94 | sudo dnf install -y pkg-config taglib-devel fftw-devel opus-devel opusfile-devel libvorbis-devel libogg-devel git gcc make chafa-devel libatomic gcc-c++ glib2-devel 95 | ``` 96 | Option: add faad2-devel for AAC,M4A support (Requires RPM-fusion to be enabled). 97 | 98 | #### For OpenSUSE: 99 | 100 | ```bash 101 | sudo zypper install -y pkg-config taglib-devel fftw-devel opus-devel opusfile-devel libvorbis-devel libogg-devel git chafa-devel gcc make glib2-devel 102 | ``` 103 | Option: add libfaad-devel for AAC,M4A support (Requires Packman to be enabled). 104 | 105 | #### For CentOS/RHEL: 106 | 107 | ```bash 108 | sudo yum install -y pkgconfig taglib-devel fftw-devel opus-devel opusfile-devel libvorbis-devel libogg-devel git gcc make chafa-devel glib2-devel 109 | ``` 110 | Option: add libfaad2-devel for AAC,M4A support (Probably requires EPEL to be enabled). 111 | 112 | #### For Solus: 113 | 114 | ```bash 115 | sudo eopkg install -y pkg-config faad2-devel taglib-devel fftw-devel opus-devel opusfile-devel libvorbis-devel libogg-devel git gcc make chafa-devel glib2-devel 116 | ``` 117 | 118 | #### For Guix: 119 | 120 | ```bash 121 | guix install pkg-config faad2 taglib fftw git gcc make chafa opus opusfile libvorbis libogg glib 122 | ``` 123 | 124 | #### For Void Linux: 125 | 126 | ```bash 127 | sudo xbps-install -y pkg-config faad2 taglib taglib-devel fftw-devel git gcc make chafa chafa-devel opus opusfile opusfile-devel libvorbis-devel libogg glib-devel 128 | ``` 129 | 130 | #### For Alpine Linux: 131 | 132 | ```bash 133 | sudo apk add pkgconfig faad2 faad2-dev taglib-dev fftw-dev opus opusfile libvorbis-dev libogg-dev git build-base chafa-dev glib-dev 134 | ``` 135 | 136 | #### For Gentoo Linux: 137 | ```bash 138 | sudo emerge --ask pkgconf faad2 taglib fftw opus opusfile libvorbis libogg chafa dev-libs/glib 139 | ``` 140 | 141 | Then run this (either git clone or unzip a release zip into a folder of your choice): 142 | 143 | ```bash 144 | git clone https://github.com/ravachol/kew.git 145 | ``` 146 | ```bash 147 | cd kew 148 | ``` 149 | ```bash 150 | make -j4 151 | ``` 152 | ```bash 153 | sudo make install 154 | ``` 155 | 156 | #### For Windows (WSL): 157 | 158 | 1) Install Windows Subsystem for Linux (WSL). 159 | 160 | 2) Install kew using the instructions for Ubuntu. 161 | 162 | 3) If you are running Windows 11, Pulseaudio should work out of the box, but if you are running Windows 10, use the instructions below for installing PulseAudio: 163 | https://www.reddit.com/r/bashonubuntuonwindows/comments/hrn1lz/wsl_sound_through_pulseaudio_solved/ 164 | 165 | 4) To install Pulseaudio as a service on Windows 10, follow the instructions at the bottom in this guide: https://www.linuxuprising.com/2021/03/how-to-get-sound-pulseaudio-to-work-on.html 166 | 167 | 168 | ### Uninstalling 169 | 170 | If you installed kew manually, simply run: 171 | 172 | ```bash 173 | sudo make uninstall 174 | ``` 175 | 176 | #### Faad2 is optional 177 | 178 | By default, the build system will automatically detect if `faad2` is available and includes it if found. 179 | 180 | 181 | ### Terminals 182 | 183 | A sixel (or equivalent) capable terminal is recommended, like Konsole or kitty, to display images properly. 184 | 185 | For a complete list of capable terminals, see this page: [Sixels in Terminal](https://www.arewesixelyet.com/). 186 | 187 | 188 | ## Usage 189 | 190 | Run kew. It will first help you set the path to your music folder, then show you that folder's contents. 191 | 192 | kew can also be told to play a certain music from the command line. It automatically creates a playlist based on a partial name of a track or directory: 193 | 194 | ```bash 195 | kew cure great 196 | ``` 197 | 198 | This command plays all songs from "The Cure Greatest Hits" directory, provided it's in your music library. 199 | 200 | kew returns the first directory or file whose name matches the string you provide. It works best when your music library is organized in this way: artist folder->album folder(s)->track(s). 201 | 202 | #### Some Examples: 203 | 204 | ``` 205 | kew (starting kew with no arguments opens the library view where you can choose what to play) 206 | 207 | kew all (plays all songs, up to 20 000, in your library, shuffled) 208 | 209 | kew albums (plays all albums, up to 2000, randomly one after the other) 210 | 211 | kew moonlight son (finds and plays moonlight sonata) 212 | 213 | kew moon (finds and plays moonlight sonata) 214 | 215 | kew beet (finds and plays all music files under "beethoven" directory) 216 | 217 | kew dir (sometimes, if names collide, it's necessary to specify it's a directory you want) 218 | 219 | kew song (or a song) 220 | 221 | kew list (or a playlist) 222 | 223 | kew shuffle (shuffles the playlist. shuffle needs to come first.) 224 | 225 | kew artistA:artistB:artistC (plays all three artists, shuffled) 226 | 227 | kew --help, -? or -h 228 | 229 | kew --version or -v 230 | 231 | kew --nocover 232 | 233 | kew --noui (completely hides the UI) 234 | 235 | kew -q , --quitonstop (exits after finishing playing the playlist) 236 | 237 | kew -e , --exact (specifies you want an exact (but not case sensitive) match, of for instance an album) 238 | 239 | kew . loads kew.m3u 240 | 241 | kew path "/home/joe/Musik/" (changes the path) 242 | 243 | ``` 244 | 245 | Put single-quotes inside quotes "guns n' roses". 246 | 247 | #### Views 248 | 249 | Add songs to the playlist in Library View F3. 250 | 251 | See the playlist and select songs in Playlist View F2. 252 | 253 | See the song info and cover in Track View F4. 254 | 255 | Search music in Search View F5. 256 | 257 | See help in Help View F7. 258 | 259 | You can select all music by pressing the - MUSIC LIBRARY - header at the top of Library View. 260 | 261 | #### Key Bindings 262 | * Enter to select or replay a song. 263 | * Use + (or =), - keys to adjust the volume. 264 | * Use , or h, l keys to switch tracks. 265 | * Space, p or right mouse to play or pause. 266 | * Alt+s to stop. 267 | * F2 or Shift+z (macOS) to show/hide playlist view. 268 | * F3 or Shift+x (macOS) to show/hide library view. 269 | * F4 or Shift+c (macOS) to show/hide track view. 270 | * F5 or Shift+v (macOS) to show/hide search view. 271 | * F6 or Shift+b (macOS) to show/hide key bindings view. 272 | * u to update the library. 273 | * v to toggle the spectrum visualizer. 274 | * i to switch between using your regular color scheme or colors derived from the track cover. 275 | * b to toggle album covers drawn in ascii or as a normal image. 276 | * r to repeat the current song after playing. 277 | * s to shuffle the playlist. 278 | * a to seek back. 279 | * d to seek forward. 280 | * x to save the currently loaded playlist to a m3u file in your music folder. 281 | * Tab to switch to next view. 282 | * Shift+Tab to switch to previous view. 283 | * Backspace to clear the playlist. 284 | * Delete to remove a single playlist entry. 285 | * t, g to move songs up or down the playlist. 286 | * number + G or Enter to go to specific song number in the playlist. 287 | * . to add current song to kew.m3u (run with "kew ."). 288 | * Esc to quit. 289 | 290 | ## Configuration 291 | 292 | kew will create a config file, kewrc, in a kew folder in your default config directory for instance ~/.config/kew or ~/Library/Preferences/kew on macOS. There you can change some settings like key bindings and the default colors of the app. To edit this file please make sure you quit kew first. 293 | 294 | ## Nerd Fonts 295 | 296 | kew looks better with Nerd Fonts: https://www.nerdfonts.com/. 297 | 298 | ## License 299 | 300 | Licensed under GPL. [See LICENSE for more information](https://github.com/ravachol/kew/blob/main/LICENSE). 301 | 302 | ## Attributions 303 | 304 | kew makes use of the following great open source projects: 305 | 306 | Alac by Apple - https://github.com/macosforge/alac 307 | 308 | Chafa by Hans Petter Jansson - https://hpjansson.org/chafa/ 309 | 310 | TagLib by TagLib Team - https://taglib.org/ 311 | 312 | faad2 by fabian_deb, knik, menno - https://sourceforge.net/projects/faac/ 313 | 314 | FFTW by Matteo Frigo and Steven G. Johnson - https://www.fftw.org/ 315 | 316 | Libopus by Opus - https://opus-codec.org/ 317 | 318 | Libvorbis by Xiph.org - https://xiph.org/ 319 | 320 | Miniaudio by David Reid - https://github.com/mackron/miniaudio 321 | 322 | Minimp4 by Lieff - https://github.com/lieff/minimp4 323 | 324 | Nestegg by Mozilla - https://github.com/mozilla/nestegg 325 | 326 | Img_To_Txt by Danny Burrows - https://github.com/danny-burrows/img_to_txt 327 | 328 | 329 | Comments? Suggestions? Send mail to kew-music-player@proton.me. 330 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # SECURITY 2 | 3 | ## Reporting a Bug 4 | 5 | If you find a security related issue, please contact us at kew-music-player@proton.me. 6 | 7 | When a fix is published, you will receive credit under your real name or bug 8 | tracker handle in GitHub. If you prefer to remain anonymous or pseudonymous, 9 | you should mention this in your e-mail. 10 | 11 | ## Disclosure Policy 12 | 13 | The maintainer will coordinate the fix and release process, involving the 14 | following steps: 15 | 16 | * Confirm the problem and determine the affected versions. 17 | * Audit code to find any potential similar problems. 18 | * Prepare fix for the latest release. This fix will be 19 | released as fast as possible. 20 | 21 | You may be asked to provide further information in pursuit of a fix. 22 | -------------------------------------------------------------------------------- /appdir/kew.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Name=kew 3 | Exec=kew 4 | Icon=kew 5 | Comment=Listen to music in the terminal. 6 | Terminal=true 7 | Type=Application 8 | Categories=Audio; 9 | -------------------------------------------------------------------------------- /appdir/usr/share/applications/kew.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Name=kew 3 | Exec=kew 4 | Icon=kew 5 | Comment=Listen to music in the terminal. 6 | Terminal=true 7 | Type=Application 8 | Categories=Audio; -------------------------------------------------------------------------------- /appdir/usr/share/icons/hicolor/128x128/apps/kew.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ravachol/kew/40cfa6b6af62819921132cc60daf6d400ded681d/appdir/usr/share/icons/hicolor/128x128/apps/kew.png -------------------------------------------------------------------------------- /appdir/usr/share/icons/hicolor/256x256/apps/kew.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ravachol/kew/40cfa6b6af62819921132cc60daf6d400ded681d/appdir/usr/share/icons/hicolor/256x256/apps/kew.png -------------------------------------------------------------------------------- /appdir/usr/share/icons/hicolor/64x64/apps/kew.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ravachol/kew/40cfa6b6af62819921132cc60daf6d400ded681d/appdir/usr/share/icons/hicolor/64x64/apps/kew.png -------------------------------------------------------------------------------- /docs/kew-manpage.mdoc: -------------------------------------------------------------------------------- 1 | .Dd 9/3/23 \" DATE 2 | .Dt kew 1 \" Program name and manual section number 3 | .Os Linux 4 | .Sh NAME \" Section Header - required - don't modify 5 | .Nm kew 6 | , 7 | .\" The following lines are read in generating the apropos(man -k) database. Use only key 8 | .\" words here as the database is built based on the words here and in the .ND line. 9 | .Nm kew music command 10 | .\" Use .Nm macro to designate other names for the documented program. 11 | .Nd A terminal music player. 12 | .Sh SYNOPSIS \" Section Header - required - don't modify 13 | .Nm 14 | .Op OPTIONS \" 15 | .Op Ar PARTIAL FILE OR DIRECTORY NAME \" [file] 16 | .Sh DESCRIPTION \" Section Header - required - don't modify 17 | .Nm 18 | plays audio from your music folder when given a partial (or whole) file or directory name. 19 | A playlist is created when finding more than one file. 20 | It supports gapless playback, 24-bit/192khz audio and MPRIS. 21 | .El 22 | .Pp 23 | Typical use: 24 | .Bl 25 | .It 26 | .Nm 27 | artist, album or song name 28 | .El 29 | .Pp 30 | .Nm 31 | returns results from the location of the first match, it doesn't return every result possible. 32 | .Pp 33 | .Sh OPTIONS 34 | .Pp 35 | .Bl -tag -width -indent 36 | .It Fl h, -help 37 | Displays help. 38 | .It Fl v, -version 39 | Displays version info. 40 | .It path 41 | Changes the path to the music library. 42 | .It Fl -nocover 43 | Hides the cover. 44 | .It Fl -noui 45 | Completely hides the UI. 46 | .It Fl q, --quitonstop 47 | Exits after playing the whole playlist. 48 | .It Fl e, --exact 49 | Specifies you want an exact (but not case sensitive) match, of for instance an album. 50 | .It shuffle 51 | Shuffles the playlist before starting to play it. 52 | .It dir 53 | Plays the directory not the song. 54 | .It song 55 | Plays the song not the directory. 56 | .It list 57 | Searches for a (.m3u) playlist. These are normally not included in searches. 58 | .El 59 | .Sh EXAMPLES 60 | .Pp 61 | .Bl -tag -width -indent 62 | .It kew 63 | Start 64 | .Nm 65 | in library view. 66 | .It kew all 67 | Start 68 | .Nm 69 | with all songs loaded into a playlist. 70 | .It kew albums 71 | Start 72 | .Nm 73 | with all albums randomly added one after the other in the playlist. 74 | .It kew moonlight son 75 | Play moonlight sonata. 76 | .It kew moon 77 | Play moonlight sonata. 78 | .It kew nirv 79 | Play all music under Nirvana folder shuffled. 80 | .It kew neverm 81 | Play Nevermind album in alphabetical order. 82 | .It kew shuffle neverm 83 | Play Nevermind album shuffled. 84 | .It kew list mix 85 | Play the mix.m3u playlist. 86 | .It kew :: 87 | Play the first match (whole directory or file) found under A, B, and C, shuffled. Searches are separated by a colon ':' character. 88 | .El \" Ends the list 89 | .Sh KEY BINDINGS 90 | .Pp 91 | .Bl -tag -width -indent 92 | .It +, - 93 | Adjusts volume. 94 | .It Left-right arrows/h,l 95 | Change song. 96 | .It Space 97 | Pause. 98 | .It Shift+s 99 | Stop. 100 | .It F2 or Shift+z 101 | Show playlist view 102 | .It F3 or Shift+x 103 | Show library view 104 | .It F4 or Shift+c 105 | Show track view 106 | .It F5 or Shift+v 107 | Show search view 108 | .It F6 or Shift+b 109 | Show radio search view. 110 | .It F7 or Shift+n 111 | Show key bindings view 112 | .It u 113 | Update the libarary. 114 | .It i 115 | Toggle colors derived from album cover or from color theme. 116 | .It v 117 | Toggle spectrum visualizer. 118 | .It b 119 | Switch between ascii and image album cover. 120 | .It r 121 | Repeat current song after playing. 122 | .It s 123 | Shuffles the playlist. 124 | .It a 125 | Seek Backward. 126 | .It d 127 | Seek Forward. 128 | .It x 129 | Save currently loaded playlist to a .m3u file in the music folder. 130 | .It Tab 131 | Switch to next view. 132 | .It Shift+Tab 133 | Switch to previous view. 134 | .It Backspace 135 | Clear the playlist. 136 | .It Delete 137 | Remove a single playlist entry. 138 | .It t,g 139 | Move a song up or down the playlist. 140 | .It number + G or Enter 141 | Go to specific song number in the playlist. 142 | .It Esc or q 143 | Quit 144 | .Nm . 145 | .El 146 | .Sh FILES 147 | .Bl -tag -width -compact 148 | .It Pa "~//kewrc" 149 | Config file. 150 | .It Pa "~//kew/kewlibrary" 151 | Music library directory tree cache. 152 | .It Pa "//kew.m3u" 153 | The 154 | .Nm 155 | playlist. Add to it by pressing '.' during playback of any song. This playlist is saved before q exits. 156 | .El 157 | .Sh COPYRIGHT 158 | Copyright © 2023 Ravachol. License GPLv2+: GNU GPL version 2 or later . 159 | This is free software: you are free to change and redistribute it. 160 | There is NO WARRANTY, to the extent permitted by law. 161 | .Sh SEE ALSO 162 | .Bl -tag -width -compact 163 | Project home page: 164 | .It Pa . 165 | .El \" Ends the list 166 | -------------------------------------------------------------------------------- /docs/kew.1: -------------------------------------------------------------------------------- 1 | .\" Automatically generated from an mdoc input file. Do not edit. 2 | .\" DATE 3 | .TH "kew" "1" "9/3/23" "Linux" "General Commands Manual" 4 | .nh 5 | .if n .ad l 6 | .SH "NAME" 7 | \fBkew\fR 8 | , 9 | \fBkew music command\fR 10 | \- A terminal music player. 11 | .SH "SYNOPSIS" 12 | .HP 4n 13 | \fBkew\fR 14 | [OPTIONS] 15 | [\fIPARTIAL\ FILE\ OR\ DIRECTORY\ NAME\fR] 16 | .SH "DESCRIPTION" 17 | \fBkew\fR 18 | plays audio from your music folder when given a partial (or whole) file or directory name. 19 | A playlist is created when finding more than one file. 20 | It supports gapless playback, 24-bit/192khz audio and MPRIS. 21 | .PP 22 | Typical use: 23 | .PP 24 | \fBkew\fR 25 | artist, album or song name 26 | .PP 27 | \fBkew\fR 28 | returns results from the location of the first match, it doesn't return every result possible. 29 | .SH "OPTIONS" 30 | .TP 9n 31 | \fB\-h,\fR \fB\--help\fR 32 | Displays help. 33 | .TP 9n 34 | \fB\-v,\fR \fB\--version\fR 35 | Displays version info. 36 | .TP 9n 37 | \fB path ,\fR 38 | Sets the path to the music library. 39 | .TP 9n 40 | \fB\--nocover\fR 41 | Hides the cover. 42 | .TP 9n 43 | \fB\--noui\fR 44 | Completely hides the UI. 45 | .TP 9n 46 | \fB\-q,\fR \fB\--quitonstop\fR 47 | Exits after playing the whole playlist. 48 | .TP 9n 49 | \fB\-e,\fR \fB\--exact, 50 | Specifies you want an exact (but not case sensitive) match, of for instance an album. 51 | .TP 9n 52 | shuffle 53 | Shuffles the playlist before starting to play it. 54 | .TP 9n 55 | dir 56 | Plays the directory not the song. 57 | .TP 9n 58 | song 59 | Plays the song not the directory. 60 | .TP 9n 61 | list 62 | Searches for a (.m3u) playlist. These are normally not included in searches. 63 | .SH "EXAMPLES" 64 | .TP 9n 65 | kew 66 | Start 67 | \fBkew\fR 68 | in library view. 69 | .TP 9n 70 | kew all 71 | Start 72 | \fBkew\fR 73 | with all songs loaded into a playlist. 74 | .TP 9n 75 | kew albums 76 | Start 77 | \fBkew\fR 78 | with all albums randomly added one after the other in the playlist. 79 | .TP 9n 80 | kew moonlight son 81 | Play moonlight sonata. 82 | .TP 9n 83 | kew moon 84 | .br 85 | Play moonlight sonata. 86 | .TP 9n 87 | kew nirv 88 | .br 89 | Play all music under Nirvana folder shuffled. 90 | .TP 9n 91 | kew neverm 92 | Play Nevermind album in alphabetical order. 93 | .TP 9n 94 | kew shuffle neverm 95 | Play Nevermind album shuffled. 96 | .TP 9n 97 | kew list mix 98 | Play the mix.m3u playlist. 99 | .TP 9n 100 | kew :: 101 | Play the first match (whole directory or file) found under A, B, and C, shuffled. Searches are separated by a colon ':' character. 102 | .SH "KEY BINDINGS" 103 | .TP 9n 104 | +, - 105 | Adjusts volume. 106 | .TP 9n 107 | Left-right arrows/h,l 108 | Change song. 109 | .TP 9n 110 | Space 111 | Play, Pause. 112 | .TP 9n 113 | Shift+s 114 | Stop. 115 | .TP 9n 116 | F2 or Shift+z 117 | Show playlist view. 118 | .TP 9n 119 | F3 or Shift+x 120 | Show library view. 121 | .TP 9n 122 | F4 or Shift+c 123 | Show track view. 124 | .TP 9n 125 | F5 or Shift+v 126 | Show search view. 127 | .TP 9n 128 | F6 or Shift+b 129 | Show radio search view. 130 | .TP 9n 131 | F7 or Shift+n 132 | Show key bindings view. 133 | .TP 9n 134 | u 135 | Update the library. 136 | .TP 9n 137 | i 138 | Toggle colors derived from album cover or from color theme. 139 | .TP 9n 140 | v 141 | Toggle spectrum visualizer. 142 | .TP 9n 143 | b 144 | Switch between ascii and image album cover. 145 | .TP 9n 146 | r 147 | Repeat current song after playing. 148 | .TP 9n 149 | s 150 | Shuffles the playlist. 151 | .TP 9n 152 | a 153 | Seek Backward. 154 | .TP 9n 155 | d 156 | Seek Forward. 157 | .TP 9n 158 | x 159 | Save currently loaded playlist to a .m3u file in the music folder. 160 | .TP 9n 161 | Tab 162 | Switch to next view. 163 | .TP 9n 164 | Shift+Tab 165 | Switch to previous view. 166 | .TP 9n 167 | Backspace 168 | Clear the playlist. 169 | .TP 9n 170 | Delete 171 | Remove a single playlist entry. 172 | .TP 9n 173 | t,g 174 | Move a song up or down the playlist. 175 | .TP 9n 176 | number + G or Enter 177 | Go to specific song number in the playlist. 178 | .TP 9n 179 | Esc or q 180 | Quit 181 | \fBkew\fR. 182 | .SH "FILES" 183 | .TP 10n 184 | \fI~//kew/kewrc\fR 185 | Config file. 186 | .TP 10n 187 | \fI~//kew/kewlibrary\fR 188 | Music library directory tree cache. 189 | .TP 10n 190 | \fI//kew.m3u\fR 191 | The 192 | \fBkew\fR 193 | playlist. Add to it by pressing '.' during playback of any song. This playlist is saved before q exits. 194 | .SH "COPYRIGHT" 195 | Copyright \[u00A9] 2023 Ravachol. License GPLv2+: GNU GPL version 2 or later . 196 | This is free software: you are free to change and redistribute it. 197 | There is NO WARRANTY, to the extent permitted by law. 198 | .SH "SEE ALSO" 199 | Project home page: 200 | .TP 10n 201 | \fI.\fR 202 | -------------------------------------------------------------------------------- /images/kew-screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ravachol/kew/40cfa6b6af62819921132cc60daf6d400ded681d/images/kew-screenshot.png -------------------------------------------------------------------------------- /include/alac/LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | 3 | Version 2.0, January 2004 4 | 5 | http://www.apache.org/licenses/ 6 | 7 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 8 | 9 | 1. Definitions. 10 | 11 | "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. 16 | 17 | "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. 18 | 19 | "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. 20 | 21 | "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. 22 | 23 | "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). 24 | 25 | "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. 26 | 27 | "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." 28 | 29 | "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 30 | 31 | 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 32 | 33 | 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 34 | 35 | 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: 36 | 37 | You must give any other recipients of the Work or Derivative Works a copy of this License; and 38 | 39 | You must cause any modified files to carry prominent notices stating that You changed the files; and 40 | 41 | You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and 42 | 43 | If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 44 | 45 | 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 46 | 47 | 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 48 | 49 | 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 50 | 51 | 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 52 | 53 | 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. 54 | -------------------------------------------------------------------------------- /include/alac/codec/ALACAudioTypes.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011 Apple Inc. All rights reserved. 3 | * 4 | * @APPLE_APACHE_LICENSE_HEADER_START@ 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * @APPLE_APACHE_LICENSE_HEADER_END@ 19 | */ 20 | 21 | /* File changed from the original to supress some warnings */ 22 | 23 | /* 24 | File: ALACAudioTypes.h 25 | */ 26 | 27 | #ifndef ALACAUDIOTYPES_H 28 | #define ALACAUDIOTYPES_H 29 | 30 | #if PRAGMA_ONCE 31 | #pragma once 32 | #endif 33 | 34 | #ifdef __cplusplus 35 | extern "C" { 36 | #endif 37 | 38 | #if PRAGMA_STRUCT_ALIGN 39 | #pragma options align=mac68k 40 | #elif PRAGMA_STRUCT_PACKPUSH 41 | #pragma pack(push, 2) 42 | #elif PRAGMA_STRUCT_PACK 43 | #pragma pack(2) 44 | #endif 45 | 46 | #include 47 | 48 | #if defined(__ppc__) 49 | #define TARGET_RT_BIG_ENDIAN 1 50 | #elif defined(__ppc64__) 51 | #define TARGET_RT_BIG_ENDIAN 1 52 | #endif 53 | 54 | #define kChannelAtomSize 12 55 | 56 | #pragma GCC diagnostic push 57 | #pragma GCC diagnostic ignored "-Wmultichar" 58 | 59 | 60 | enum 61 | { 62 | kALAC_UnimplementedError = -4, 63 | kALAC_FileNotFoundError = -43, 64 | kALAC_ParamError = -50, 65 | kALAC_MemFullError = -108 66 | }; 67 | 68 | enum 69 | { 70 | kALACFormatAppleLossless = 'alac', 71 | kALACFormatLinearPCM = 'lpcm' 72 | }; 73 | 74 | enum 75 | { 76 | kALACMaxChannels = 8, 77 | kALACMaxEscapeHeaderBytes = 8, 78 | kALACMaxSearches = 16, 79 | kALACMaxCoefs = 16, 80 | kALACDefaultFramesPerPacket = 4096 81 | }; 82 | 83 | typedef uint32_t ALACChannelLayoutTag; 84 | 85 | enum 86 | { 87 | kALACFormatFlagIsFloat = (1 << 0), // 0x1 88 | kALACFormatFlagIsBigEndian = (1 << 1), // 0x2 89 | kALACFormatFlagIsSignedInteger = (1 << 2), // 0x4 90 | kALACFormatFlagIsPacked = (1 << 3), // 0x8 91 | kALACFormatFlagIsAlignedHigh = (1 << 4), // 0x10 92 | }; 93 | 94 | enum 95 | { 96 | #if TARGET_RT_BIG_ENDIAN 97 | kALACFormatFlagsNativeEndian = kALACFormatFlagIsBigEndian 98 | #else 99 | kALACFormatFlagsNativeEndian = 0 100 | #endif 101 | }; 102 | 103 | // this is required to be an IEEE 64bit float 104 | typedef double alac_float64_t; 105 | 106 | // These are the Channel Layout Tags used in the Channel Layout Info portion of the ALAC magic cookie 107 | enum 108 | { 109 | kALACChannelLayoutTag_Mono = (100<<16) | 1, // C 110 | kALACChannelLayoutTag_Stereo = (101<<16) | 2, // L R 111 | kALACChannelLayoutTag_MPEG_3_0_B = (113<<16) | 3, // C L R 112 | kALACChannelLayoutTag_MPEG_4_0_B = (116<<16) | 4, // C L R Cs 113 | kALACChannelLayoutTag_MPEG_5_0_D = (120<<16) | 5, // C L R Ls Rs 114 | kALACChannelLayoutTag_MPEG_5_1_D = (124<<16) | 6, // C L R Ls Rs LFE 115 | kALACChannelLayoutTag_AAC_6_1 = (142<<16) | 7, // C L R Ls Rs Cs LFE 116 | kALACChannelLayoutTag_MPEG_7_1_B = (127<<16) | 8 // C Lc Rc L R Ls Rs LFE (doc: IS-13818-7 MPEG2-AAC) 117 | }; 118 | 119 | // ALAC currently only utilizes these channels layouts. There is a one for one correspondance between a 120 | // given number of channels and one of these layout tags 121 | static const ALACChannelLayoutTag ALACChannelLayoutTags[kALACMaxChannels] = 122 | { 123 | kALACChannelLayoutTag_Mono, // C 124 | kALACChannelLayoutTag_Stereo, // L R 125 | kALACChannelLayoutTag_MPEG_3_0_B, // C L R 126 | kALACChannelLayoutTag_MPEG_4_0_B, // C L R Cs 127 | kALACChannelLayoutTag_MPEG_5_0_D, // C L R Ls Rs 128 | kALACChannelLayoutTag_MPEG_5_1_D, // C L R Ls Rs LFE 129 | kALACChannelLayoutTag_AAC_6_1, // C L R Ls Rs Cs LFE 130 | kALACChannelLayoutTag_MPEG_7_1_B // C Lc Rc L R Ls Rs LFE (doc: IS-13818-7 MPEG2-AAC) 131 | }; 132 | 133 | // AudioChannelLayout from CoreAudioTypes.h. We never need the AudioChannelDescription so we remove it 134 | struct ALACAudioChannelLayout 135 | { 136 | ALACChannelLayoutTag mChannelLayoutTag; 137 | uint32_t mChannelBitmap; 138 | uint32_t mNumberChannelDescriptions; 139 | }; 140 | typedef struct ALACAudioChannelLayout ALACAudioChannelLayout; 141 | 142 | struct AudioFormatDescription 143 | { 144 | alac_float64_t mSampleRate; 145 | uint32_t mFormatID; 146 | uint32_t mFormatFlags; 147 | uint32_t mBytesPerPacket; 148 | uint32_t mFramesPerPacket; 149 | uint32_t mBytesPerFrame; 150 | uint32_t mChannelsPerFrame; 151 | uint32_t mBitsPerChannel; 152 | uint32_t mReserved; 153 | }; 154 | typedef struct AudioFormatDescription AudioFormatDescription; 155 | 156 | /* Lossless Definitions */ 157 | 158 | enum 159 | { 160 | kALACCodecFormat = 'alac', 161 | kALACVersion = 0, 162 | kALACCompatibleVersion = kALACVersion, 163 | kALACDefaultFrameSize = 4096 164 | }; 165 | 166 | // note: this struct is wrapped in an 'alac' atom in the sample description extension area 167 | // note: in QT movies, it will be further wrapped in a 'wave' atom surrounded by 'frma' and 'term' atoms 168 | typedef struct ALACSpecificConfig 169 | { 170 | uint32_t frameLength; 171 | uint8_t compatibleVersion; 172 | uint8_t bitDepth; // max 32 173 | uint8_t pb; // 0 <= pb <= 255 174 | uint8_t mb; 175 | uint8_t kb; 176 | uint8_t numChannels; 177 | uint16_t maxRun; 178 | uint32_t maxFrameBytes; 179 | uint32_t avgBitRate; 180 | uint32_t sampleRate; 181 | 182 | } ALACSpecificConfig; 183 | 184 | 185 | // The AudioChannelLayout atom type is not exposed yet so define it here 186 | enum 187 | { 188 | AudioChannelLayoutAID = 'chan' 189 | }; 190 | 191 | #pragma GCC diagnostic pop 192 | 193 | #if PRAGMA_STRUCT_ALIGN 194 | #pragma options align=reset 195 | #elif PRAGMA_STRUCT_PACKPUSH 196 | #pragma pack(pop) 197 | #elif PRAGMA_STRUCT_PACK 198 | #pragma pack() 199 | #endif 200 | 201 | #ifdef __cplusplus 202 | } 203 | #endif 204 | 205 | #endif /* ALACAUDIOTYPES_H */ 206 | -------------------------------------------------------------------------------- /include/alac/codec/ALACBitUtilities.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011 Apple Inc. All rights reserved. 3 | * 4 | * @APPLE_APACHE_LICENSE_HEADER_START@ 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * @APPLE_APACHE_LICENSE_HEADER_END@ 19 | */ 20 | 21 | /*============================================================================= 22 | File: ALACBitUtilities.c 23 | 24 | $NoKeywords: $ 25 | =============================================================================*/ 26 | 27 | #include 28 | #include "ALACBitUtilities.h" 29 | 30 | // BitBufferInit 31 | // 32 | void BitBufferInit( BitBuffer * bits, uint8_t * buffer, uint32_t byteSize ) 33 | { 34 | bits->cur = buffer; 35 | bits->end = bits->cur + byteSize; 36 | bits->bitIndex = 0; 37 | bits->byteSize = byteSize; 38 | } 39 | 40 | // BitBufferRead 41 | // 42 | uint32_t BitBufferRead( BitBuffer * bits, uint8_t numBits ) 43 | { 44 | uint32_t returnBits; 45 | 46 | //Assert( numBits <= 16 ); 47 | 48 | returnBits = ((uint32_t)bits->cur[0] << 16) | ((uint32_t)bits->cur[1] << 8) | ((uint32_t)bits->cur[2]); 49 | returnBits = returnBits << bits->bitIndex; 50 | returnBits &= 0x00FFFFFF; 51 | 52 | bits->bitIndex += numBits; 53 | 54 | returnBits = returnBits >> (24 - numBits); 55 | 56 | bits->cur += (bits->bitIndex >> 3); 57 | bits->bitIndex &= 7; 58 | 59 | //Assert( bits->cur <= bits->end ); 60 | 61 | return returnBits; 62 | } 63 | 64 | // BitBufferReadSmall 65 | // 66 | // Reads up to 8 bits 67 | uint8_t BitBufferReadSmall( BitBuffer * bits, uint8_t numBits ) 68 | { 69 | uint16_t returnBits; 70 | 71 | //Assert( numBits <= 8 ); 72 | 73 | returnBits = (bits->cur[0] << 8) | bits->cur[1]; 74 | returnBits = returnBits << bits->bitIndex; 75 | 76 | bits->bitIndex += numBits; 77 | 78 | returnBits = returnBits >> (16 - numBits); 79 | 80 | bits->cur += (bits->bitIndex >> 3); 81 | bits->bitIndex &= 7; 82 | 83 | //Assert( bits->cur <= bits->end ); 84 | 85 | return (uint8_t)returnBits; 86 | } 87 | 88 | // BitBufferReadOne 89 | // 90 | // Reads one byte 91 | uint8_t BitBufferReadOne( BitBuffer * bits ) 92 | { 93 | uint8_t returnBits; 94 | 95 | returnBits = (bits->cur[0] >> (7 - bits->bitIndex)) & 1; 96 | 97 | bits->bitIndex++; 98 | 99 | bits->cur += (bits->bitIndex >> 3); 100 | bits->bitIndex &= 7; 101 | 102 | //Assert( bits->cur <= bits->end ); 103 | 104 | return returnBits; 105 | } 106 | 107 | // BitBufferPeek 108 | // 109 | uint32_t BitBufferPeek( BitBuffer * bits, uint8_t numBits ) 110 | { 111 | return ((((((uint32_t) bits->cur[0] << 16) | ((uint32_t) bits->cur[1] << 8) | 112 | ((uint32_t) bits->cur[2])) << bits->bitIndex) & 0x00FFFFFF) >> (24 - numBits)); 113 | } 114 | 115 | // BitBufferPeekOne 116 | // 117 | uint32_t BitBufferPeekOne( BitBuffer * bits ) 118 | { 119 | return ((bits->cur[0] >> (7 - bits->bitIndex)) & 1); 120 | } 121 | 122 | // BitBufferUnpackBERSize 123 | // 124 | uint32_t BitBufferUnpackBERSize( BitBuffer * bits ) 125 | { 126 | uint32_t size; 127 | uint8_t tmp; 128 | 129 | for ( size = 0, tmp = 0x80u; tmp &= 0x80u; size = (size << 7u) | (tmp & 0x7fu) ) 130 | tmp = (uint8_t) BitBufferReadSmall( bits, 8 ); 131 | 132 | return size; 133 | } 134 | 135 | // BitBufferGetPosition 136 | // 137 | uint32_t BitBufferGetPosition( BitBuffer * bits ) 138 | { 139 | uint8_t * begin; 140 | 141 | begin = bits->end - bits->byteSize; 142 | 143 | return ((uint32_t)(bits->cur - begin) * 8) + bits->bitIndex; 144 | } 145 | 146 | // BitBufferByteAlign 147 | // 148 | void BitBufferByteAlign( BitBuffer * bits, int32_t addZeros ) 149 | { 150 | // align bit buffer to next byte boundary, writing zeros if requested 151 | if ( bits->bitIndex == 0 ) 152 | return; 153 | 154 | if ( addZeros ) 155 | BitBufferWrite( bits, 0, 8 - bits->bitIndex ); 156 | else 157 | BitBufferAdvance( bits, 8 - bits->bitIndex ); 158 | } 159 | 160 | // BitBufferAdvance 161 | // 162 | void BitBufferAdvance( BitBuffer * bits, uint32_t numBits ) 163 | { 164 | if ( numBits ) 165 | { 166 | bits->bitIndex += numBits; 167 | bits->cur += (bits->bitIndex >> 3); 168 | bits->bitIndex &= 7; 169 | } 170 | } 171 | 172 | // BitBufferRewind 173 | // 174 | void BitBufferRewind( BitBuffer * bits, uint32_t numBits ) 175 | { 176 | uint32_t numBytes; 177 | 178 | if ( numBits == 0 ) 179 | return; 180 | 181 | if ( bits->bitIndex >= numBits ) 182 | { 183 | bits->bitIndex -= numBits; 184 | return; 185 | } 186 | 187 | numBits -= bits->bitIndex; 188 | bits->bitIndex = 0; 189 | 190 | numBytes = numBits / 8; 191 | numBits = numBits % 8; 192 | 193 | bits->cur -= numBytes; 194 | 195 | if ( numBits > 0 ) 196 | { 197 | bits->bitIndex = 8 - numBits; 198 | bits->cur--; 199 | } 200 | 201 | if ( bits->cur < (bits->end - bits->byteSize) ) 202 | { 203 | //DebugCMsg("BitBufferRewind: Rewound too far."); 204 | 205 | bits->cur = (bits->end - bits->byteSize); 206 | bits->bitIndex = 0; 207 | } 208 | } 209 | 210 | // BitBufferWrite 211 | // 212 | void BitBufferWrite( BitBuffer * bits, uint32_t bitValues, uint32_t numBits ) 213 | { 214 | uint32_t invBitIndex; 215 | 216 | RequireAction( bits != nil, return; ); 217 | RequireActionSilent( numBits > 0, return; ); 218 | 219 | invBitIndex = 8 - bits->bitIndex; 220 | 221 | while ( numBits > 0 ) 222 | { 223 | uint32_t tmp; 224 | uint8_t shift; 225 | uint8_t mask; 226 | uint32_t curNum; 227 | 228 | curNum = MIN( invBitIndex, numBits ); 229 | 230 | tmp = bitValues >> (numBits - curNum); 231 | 232 | shift = (uint8_t)(invBitIndex - curNum); 233 | mask = 0xffu >> (8 - curNum); // must be done in two steps to avoid compiler sequencing ambiguity 234 | mask <<= shift; 235 | 236 | bits->cur[0] = (bits->cur[0] & ~mask) | (((uint8_t) tmp << shift) & mask); 237 | numBits -= curNum; 238 | 239 | // increment to next byte if need be 240 | invBitIndex -= curNum; 241 | if ( invBitIndex == 0 ) 242 | { 243 | invBitIndex = 8; 244 | bits->cur++; 245 | } 246 | } 247 | 248 | bits->bitIndex = 8 - invBitIndex; 249 | } 250 | 251 | void BitBufferReset( BitBuffer * bits ) 252 | //void BitBufferInit( BitBuffer * bits, uint8_t * buffer, uint32_t byteSize ) 253 | { 254 | bits->cur = bits->end - bits->byteSize; 255 | bits->bitIndex = 0; 256 | } 257 | 258 | #if PRAGMA_MARK 259 | #pragma mark - 260 | #endif 261 | -------------------------------------------------------------------------------- /include/alac/codec/ALACBitUtilities.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011 Apple Inc. All rights reserved. 3 | * 4 | * @APPLE_APACHE_LICENSE_HEADER_START@ 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * @APPLE_APACHE_LICENSE_HEADER_END@ 19 | */ 20 | 21 | /*============================================================================= 22 | File: ALACBitUtilities.h 23 | 24 | $NoKeywords: $ 25 | =============================================================================*/ 26 | 27 | #ifndef __ALACBITUTILITIES_H 28 | #define __ALACBITUTILITIES_H 29 | 30 | #include 31 | 32 | #ifndef MIN 33 | #define MIN(x, y) ( (x)<(y) ?(x) :(y) ) 34 | #endif //MIN 35 | #ifndef MAX 36 | #define MAX(x, y) ( (x)>(y) ?(x): (y) ) 37 | #endif //MAX 38 | 39 | #ifndef nil 40 | #define nil NULL 41 | #endif 42 | 43 | #define RequireAction(condition, action) if (!(condition)) { action } 44 | #define RequireActionSilent(condition, action) if (!(condition)) { action } 45 | #define RequireNoErr(condition, action) if ((condition)) { action } 46 | 47 | #ifdef __cplusplus 48 | extern "C" { 49 | #endif 50 | 51 | enum 52 | { 53 | ALAC_noErr = 0 54 | }; 55 | 56 | 57 | typedef enum 58 | { 59 | 60 | ID_SCE = 0, /* Single Channel Element */ 61 | ID_CPE = 1, /* Channel Pair Element */ 62 | ID_CCE = 2, /* Coupling Channel Element */ 63 | ID_LFE = 3, /* LFE Channel Element */ 64 | ID_DSE = 4, /* not yet supported */ 65 | ID_PCE = 5, 66 | ID_FIL = 6, 67 | ID_END = 7 68 | } ELEMENT_TYPE; 69 | 70 | // types 71 | typedef struct BitBuffer 72 | { 73 | uint8_t * cur; 74 | uint8_t * end; 75 | uint32_t bitIndex; 76 | uint32_t byteSize; 77 | 78 | } BitBuffer; 79 | 80 | /* 81 | BitBuffer routines 82 | - these routines take a fixed size buffer and read/write to it 83 | - bounds checking must be done by the client 84 | */ 85 | void BitBufferInit( BitBuffer * bits, uint8_t * buffer, uint32_t byteSize ); 86 | uint32_t BitBufferRead( BitBuffer * bits, uint8_t numBits ); // note: cannot read more than 16 bits at a time 87 | uint8_t BitBufferReadSmall( BitBuffer * bits, uint8_t numBits ); 88 | uint8_t BitBufferReadOne( BitBuffer * bits ); 89 | uint32_t BitBufferPeek( BitBuffer * bits, uint8_t numBits ); // note: cannot read more than 16 bits at a time 90 | uint32_t BitBufferPeekOne( BitBuffer * bits ); 91 | uint32_t BitBufferUnpackBERSize( BitBuffer * bits ); 92 | uint32_t BitBufferGetPosition( BitBuffer * bits ); 93 | void BitBufferByteAlign( BitBuffer * bits, int32_t addZeros ); 94 | void BitBufferAdvance( BitBuffer * bits, uint32_t numBits ); 95 | void BitBufferRewind( BitBuffer * bits, uint32_t numBits ); 96 | void BitBufferWrite( BitBuffer * bits, uint32_t value, uint32_t numBits ); 97 | void BitBufferReset( BitBuffer * bits); 98 | 99 | 100 | #ifdef __cplusplus 101 | } 102 | #endif 103 | 104 | #endif /* __BITUTILITIES_H */ 105 | -------------------------------------------------------------------------------- /include/alac/codec/ALACDecoder.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011 Apple Inc. All rights reserved. 3 | * 4 | * @APPLE_APACHE_LICENSE_HEADER_START@ 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * @APPLE_APACHE_LICENSE_HEADER_END@ 19 | */ 20 | 21 | /* 22 | File: ALACDecoder.h 23 | */ 24 | 25 | #ifndef _ALACDECODER_H 26 | #define _ALACDECODER_H 27 | 28 | #if PRAGMA_ONCE 29 | #pragma once 30 | #endif 31 | 32 | #include 33 | 34 | #include "ALACAudioTypes.h" 35 | 36 | struct BitBuffer; 37 | 38 | class ALACDecoder 39 | { 40 | public: 41 | ALACDecoder(); 42 | ~ALACDecoder(); 43 | 44 | int32_t Init( void * inMagicCookie, uint32_t inMagicCookieSize ); 45 | int32_t Decode( struct BitBuffer * bits, uint8_t * sampleBuffer, uint32_t numSamples, uint32_t numChannels, uint32_t * outNumSamples ); 46 | 47 | public: 48 | // decoding parameters (public for use in the analyzer) 49 | ALACSpecificConfig mConfig; 50 | 51 | protected: 52 | int32_t FillElement( struct BitBuffer * bits ); 53 | int32_t DataStreamElement( struct BitBuffer * bits ); 54 | 55 | uint16_t mActiveElements; 56 | 57 | // decoding buffers 58 | int32_t * mMixBufferU; 59 | int32_t * mMixBufferV; 60 | int32_t * mPredictor; 61 | uint16_t * mShiftBuffer; // note: this points to mPredictor's memory but different 62 | // variable for clarity and type difference 63 | }; 64 | 65 | #endif /* _ALACDECODER_H */ 66 | -------------------------------------------------------------------------------- /include/alac/codec/EndianPortable.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011 Apple Inc. All rights reserved. 3 | * 4 | * @APPLE_APACHE_LICENSE_HEADER_START@ 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * @APPLE_APACHE_LICENSE_HEADER_END@ 19 | */ 20 | 21 | // 22 | // EndianPortable.c 23 | // 24 | // Copyright 2011 Apple Inc. All rights reserved. 25 | // 26 | 27 | #include 28 | #include "EndianPortable.h" 29 | 30 | #define BSWAP16(x) (((x << 8) | ((x >> 8) & 0x00ff))) 31 | #define BSWAP32(x) (((x << 24) | ((x << 8) & 0x00ff0000) | ((x >> 8) & 0x0000ff00) | ((x >> 24) & 0x000000ff))) 32 | #define BSWAP64(x) ((((int64_t)x << 56) | (((int64_t)x << 40) & 0x00ff000000000000LL) | \ 33 | (((int64_t)x << 24) & 0x0000ff0000000000LL) | (((int64_t)x << 8) & 0x000000ff00000000LL) | \ 34 | (((int64_t)x >> 8) & 0x00000000ff000000LL) | (((int64_t)x >> 24) & 0x0000000000ff0000LL) | \ 35 | (((int64_t)x >> 40) & 0x000000000000ff00LL) | (((int64_t)x >> 56) & 0x00000000000000ffLL))) 36 | 37 | #if defined(__i386__) 38 | #define TARGET_RT_LITTLE_ENDIAN 1 39 | #elif defined(__x86_64__) 40 | #define TARGET_RT_LITTLE_ENDIAN 1 41 | #elif defined (TARGET_OS_WIN32) 42 | #define TARGET_RT_LITTLE_ENDIAN 1 43 | #endif 44 | 45 | uint16_t Swap16NtoB(uint16_t inUInt16) 46 | { 47 | #if TARGET_RT_LITTLE_ENDIAN 48 | return BSWAP16(inUInt16); 49 | #else 50 | return inUInt16; 51 | #endif 52 | } 53 | 54 | uint16_t Swap16BtoN(uint16_t inUInt16) 55 | { 56 | #if TARGET_RT_LITTLE_ENDIAN 57 | return BSWAP16(inUInt16); 58 | #else 59 | return inUInt16; 60 | #endif 61 | } 62 | 63 | uint32_t Swap32NtoB(uint32_t inUInt32) 64 | { 65 | #if TARGET_RT_LITTLE_ENDIAN 66 | return BSWAP32(inUInt32); 67 | #else 68 | return inUInt32; 69 | #endif 70 | } 71 | 72 | uint32_t Swap32BtoN(uint32_t inUInt32) 73 | { 74 | #if TARGET_RT_LITTLE_ENDIAN 75 | return BSWAP32(inUInt32); 76 | #else 77 | return inUInt32; 78 | #endif 79 | } 80 | 81 | uint64_t Swap64BtoN(uint64_t inUInt64) 82 | { 83 | #if TARGET_RT_LITTLE_ENDIAN 84 | return BSWAP64(inUInt64); 85 | #else 86 | return inUInt64; 87 | #endif 88 | } 89 | 90 | uint64_t Swap64NtoB(uint64_t inUInt64) 91 | { 92 | #if TARGET_RT_LITTLE_ENDIAN 93 | return BSWAP64(inUInt64); 94 | #else 95 | return inUInt64; 96 | #endif 97 | } 98 | 99 | float SwapFloat32BtoN(float in) 100 | { 101 | #if TARGET_RT_LITTLE_ENDIAN 102 | union { 103 | float f; 104 | int32_t i; 105 | } x; 106 | x.f = in; 107 | x.i = BSWAP32(x.i); 108 | return x.f; 109 | #else 110 | return in; 111 | #endif 112 | } 113 | 114 | float SwapFloat32NtoB(float in) 115 | { 116 | #if TARGET_RT_LITTLE_ENDIAN 117 | union { 118 | float f; 119 | int32_t i; 120 | } x; 121 | x.f = in; 122 | x.i = BSWAP32(x.i); 123 | return x.f; 124 | #else 125 | return in; 126 | #endif 127 | } 128 | 129 | double SwapFloat64BtoN(double in) 130 | { 131 | #if TARGET_RT_LITTLE_ENDIAN 132 | union { 133 | double f; 134 | int64_t i; 135 | } x; 136 | x.f = in; 137 | x.i = BSWAP64(x.i); 138 | return x.f; 139 | #else 140 | return in; 141 | #endif 142 | } 143 | 144 | double SwapFloat64NtoB(double in) 145 | { 146 | #if TARGET_RT_LITTLE_ENDIAN 147 | union { 148 | double f; 149 | int64_t i; 150 | } x; 151 | x.f = in; 152 | x.i = BSWAP64(x.i); 153 | return x.f; 154 | #else 155 | return in; 156 | #endif 157 | } 158 | 159 | void Swap16(uint16_t * inUInt16) 160 | { 161 | *inUInt16 = BSWAP16(*inUInt16); 162 | } 163 | 164 | void Swap24(uint8_t * inUInt24) 165 | { 166 | uint8_t tempVal = inUInt24[0]; 167 | inUInt24[0] = inUInt24[2]; 168 | inUInt24[2] = tempVal; 169 | } 170 | 171 | void Swap32(uint32_t * inUInt32) 172 | { 173 | *inUInt32 = BSWAP32(*inUInt32); 174 | } 175 | 176 | -------------------------------------------------------------------------------- /include/alac/codec/EndianPortable.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011 Apple Inc. All rights reserved. 3 | * 4 | * @APPLE_APACHE_LICENSE_HEADER_START@ 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * @APPLE_APACHE_LICENSE_HEADER_END@ 19 | */ 20 | 21 | // 22 | // EndianPortable.h 23 | // 24 | // Copyright 2011 Apple Inc. All rights reserved. 25 | // 26 | 27 | #ifndef _EndianPortable_h 28 | #define _EndianPortable_h 29 | 30 | #include 31 | 32 | #ifdef __cplusplus 33 | extern "C" { 34 | #endif 35 | 36 | uint16_t Swap16NtoB(uint16_t inUInt16); 37 | uint16_t Swap16BtoN(uint16_t inUInt16); 38 | 39 | uint32_t Swap32NtoB(uint32_t inUInt32); 40 | uint32_t Swap32BtoN(uint32_t inUInt32); 41 | 42 | uint64_t Swap64BtoN(uint64_t inUInt64); 43 | uint64_t Swap64NtoB(uint64_t inUInt64); 44 | 45 | float SwapFloat32BtoN(float in); 46 | float SwapFloat32NtoB(float in); 47 | 48 | double SwapFloat64BtoN(double in); 49 | double SwapFloat64NtoB(double in); 50 | 51 | void Swap16(uint16_t * inUInt16); 52 | void Swap24(uint8_t * inUInt24); 53 | void Swap32(uint32_t * inUInt32); 54 | 55 | #ifdef __cplusplus 56 | } 57 | #endif 58 | 59 | #endif 60 | -------------------------------------------------------------------------------- /include/alac/codec/ag_dec.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011 Apple Inc. All rights reserved. 3 | * 4 | * @APPLE_APACHE_LICENSE_HEADER_START@ 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * @APPLE_APACHE_LICENSE_HEADER_END@ 19 | */ 20 | 21 | /* File changed from the original to supress some warnings */ 22 | 23 | /* 24 | File: ag_dec.c 25 | 26 | Contains: Adaptive Golomb decode routines. 27 | 28 | Copyright: (c) 2001-2011 Apple, Inc. 29 | */ 30 | 31 | #include "aglib.h" 32 | #include "ALACBitUtilities.h" 33 | #include "ALACAudioTypes.h" 34 | 35 | #include 36 | #include 37 | #include 38 | #include 39 | #if __GNUC__ && TARGET_OS_MAC 40 | #if __POWERPC__ 41 | #include 42 | #else 43 | #include 44 | #endif 45 | #endif 46 | 47 | #pragma GCC diagnostic push 48 | #pragma GCC diagnostic ignored "-Wsign-compare" 49 | 50 | #define CODE_TO_LONG_MAXBITS 32 51 | #define N_MAX_MEAN_CLAMP 0xffff 52 | #define N_MEAN_CLAMP_VAL 0xffff 53 | #define REPORT_VAL 40 54 | 55 | #if __GNUC__ 56 | #define ALWAYS_INLINE __attribute__((always_inline)) 57 | #else 58 | #define ALWAYS_INLINE 59 | #endif 60 | 61 | /* And on the subject of the CodeWarrior x86 compiler and inlining, I reworked a lot of this 62 | to help the compiler out. In many cases this required manual inlining or a macro. Sorry 63 | if it is ugly but the performance gains are well worth it. 64 | - WSK 5/19/04 65 | */ 66 | 67 | void set_standard_ag_params(AGParamRecPtr params, uint32_t fullwidth, uint32_t sectorwidth) 68 | { 69 | /* Use 70 | fullwidth = sectorwidth = numOfSamples, for analog 1-dimensional type-short data, 71 | but use 72 | fullwidth = full image width, sectorwidth = sector (patch) width 73 | for such as image (2-dim.) data. 74 | */ 75 | set_ag_params( params, MB0, PB0, KB0, fullwidth, sectorwidth, MAX_RUN_DEFAULT ); 76 | } 77 | 78 | void set_ag_params(AGParamRecPtr params, uint32_t m, uint32_t p, uint32_t k, uint32_t f, uint32_t s, uint32_t maxrun) 79 | { 80 | params->mb = params->mb0 = m; 81 | params->pb = p; 82 | params->kb = k; 83 | params->wb = (1u<kb)-1; 84 | params->qb = QB-params->pb; 85 | params->fw = f; 86 | params->sw = s; 87 | params->maxrun = maxrun; 88 | } 89 | 90 | #if PRAGMA_MARK 91 | #pragma mark - 92 | #endif 93 | 94 | 95 | // note: implementing this with some kind of "count leading zeros" assembly is a big performance win 96 | static inline int32_t lead( int32_t m ) 97 | { 98 | long j; 99 | unsigned long c = (1ul << 31); 100 | 101 | for(j=0; j < 32; j++) 102 | { 103 | if((c & m) != 0) 104 | break; 105 | c >>= 1; 106 | } 107 | return (j); 108 | } 109 | 110 | #define arithmin(a, b) ((a) < (b) ? (a) : (b)) 111 | 112 | static inline int32_t ALWAYS_INLINE lg3a( int32_t x) 113 | { 114 | int32_t result; 115 | 116 | x += 3; 117 | result = lead(x); 118 | 119 | return 31 - result; 120 | } 121 | 122 | static inline uint32_t ALWAYS_INLINE read32bit( uint8_t * buffer ) 123 | { 124 | // embedded CPUs typically can't read unaligned 32-bit words so just read the bytes 125 | uint32_t value; 126 | 127 | value = ((uint32_t)buffer[0] << 24) | ((uint32_t)buffer[1] << 16) | 128 | ((uint32_t)buffer[2] << 8) | (uint32_t)buffer[3]; 129 | return value; 130 | 131 | } 132 | 133 | #if PRAGMA_MARK 134 | #pragma mark - 135 | #endif 136 | 137 | #define get_next_fromlong(inlong, suff) ((inlong) >> (32 - (suff))) 138 | 139 | 140 | static inline uint32_t ALWAYS_INLINE 141 | getstreambits( uint8_t *in, int32_t bitoffset, int32_t numbits ) 142 | { 143 | uint32_t load1, load2; 144 | uint32_t byteoffset = bitoffset / 8; 145 | uint32_t result; 146 | 147 | //Assert( numbits <= 32 ); 148 | 149 | load1 = read32bit( in + byteoffset ); 150 | 151 | if ( (numbits + (bitoffset & 0x7)) > 32) 152 | { 153 | int32_t load2shift; 154 | 155 | result = load1 << (bitoffset & 0x7); 156 | load2 = (uint32_t) in[byteoffset+4]; 157 | load2shift = (8-(numbits + (bitoffset & 0x7)-32)); 158 | load2 >>= load2shift; 159 | result >>= (32-numbits); 160 | result |= load2; 161 | } 162 | else 163 | { 164 | result = load1 >> (32-numbits-(bitoffset & 7)); 165 | } 166 | 167 | // a shift of >= "the number of bits in the type of the value being shifted" results in undefined 168 | // behavior so don't try to shift by 32 169 | if ( numbits != (sizeof(result) * 8) ) 170 | result &= ~(0xfffffffful << numbits); 171 | 172 | return result; 173 | } 174 | 175 | 176 | static inline int32_t dyn_get(unsigned char *in, uint32_t *bitPos, uint32_t m, uint32_t k) 177 | { 178 | uint32_t tempbits = *bitPos; 179 | uint32_t result; 180 | uint32_t pre = 0, v; 181 | uint32_t streamlong; 182 | 183 | streamlong = read32bit( in + (tempbits >> 3) ); 184 | streamlong <<= (tempbits & 7); 185 | 186 | /* find the number of bits in the prefix */ 187 | { 188 | uint32_t notI = ~streamlong; 189 | pre = lead( notI); 190 | } 191 | 192 | if(pre >= MAX_PREFIX_16) 193 | { 194 | pre = MAX_PREFIX_16; 195 | tempbits += pre; 196 | streamlong <<= pre; 197 | result = get_next_fromlong(streamlong,MAX_DATATYPE_BITS_16); 198 | tempbits += MAX_DATATYPE_BITS_16; 199 | 200 | } 201 | else 202 | { 203 | // all of the bits must fit within the long we have loaded 204 | //Assert(pre+1+k <= 32); 205 | 206 | tempbits += pre; 207 | tempbits += 1; 208 | streamlong <<= pre+1; 209 | v = get_next_fromlong(streamlong, k); 210 | tempbits += k; 211 | 212 | result = pre*m + v-1; 213 | 214 | if(v<2) { 215 | result -= (v-1); 216 | tempbits -= 1; 217 | } 218 | } 219 | 220 | *bitPos = tempbits; 221 | return result; 222 | } 223 | 224 | 225 | static inline int32_t dyn_get_32bit( uint8_t * in, uint32_t * bitPos, int32_t m, int32_t k, int32_t maxbits ) 226 | { 227 | uint32_t tempbits = *bitPos; 228 | uint32_t v; 229 | uint32_t streamlong; 230 | uint32_t result; 231 | 232 | streamlong = read32bit( in + (tempbits >> 3) ); 233 | streamlong <<= (tempbits & 7); 234 | 235 | /* find the number of bits in the prefix */ 236 | { 237 | uint32_t notI = ~streamlong; 238 | result = lead( notI); 239 | } 240 | 241 | if(result >= MAX_PREFIX_32) 242 | { 243 | result = getstreambits(in, tempbits+MAX_PREFIX_32, maxbits); 244 | tempbits += MAX_PREFIX_32 + maxbits; 245 | } 246 | else 247 | { 248 | /* all of the bits must fit within the long we have loaded*/ 249 | //Assert(k<=14); 250 | //Assert(result=2) 265 | { 266 | result += (v-1); 267 | tempbits += 1; 268 | } 269 | } 270 | } 271 | 272 | *bitPos = tempbits; 273 | 274 | return result; 275 | } 276 | 277 | int32_t dyn_decomp( AGParamRecPtr params, BitBuffer * bitstream, int32_t * pc, int32_t numSamples, int32_t maxSize, uint32_t * outNumBits ) 278 | { 279 | uint8_t *in; 280 | int32_t *outPtr = pc; 281 | uint32_t bitPos, startPos, maxPos; 282 | uint32_t j, m, k, n, c, mz; 283 | int32_t del, zmode; 284 | uint32_t mb; 285 | uint32_t pb_local = params->pb; 286 | uint32_t kb_local = params->kb; 287 | uint32_t wb_local = params->wb; 288 | int32_t status; 289 | 290 | RequireAction( (bitstream != nil) && (pc != nil) && (outNumBits != nil), return kALAC_ParamError; ); 291 | *outNumBits = 0; 292 | 293 | in = bitstream->cur; 294 | startPos = bitstream->bitIndex; 295 | maxPos = bitstream->byteSize * 8; 296 | bitPos = startPos; 297 | 298 | mb = params->mb0; 299 | zmode = 0; 300 | 301 | c = 0; 302 | status = ALAC_noErr; 303 | 304 | while (c < numSamples) 305 | { 306 | // bail if we've run off the end of the buffer 307 | RequireAction( bitPos < maxPos, status = kALAC_ParamError; goto Exit; ); 308 | 309 | m = (mb)>>QBSHIFT; 310 | k = lg3a(m); 311 | 312 | k = arithmin(k, kb_local); 313 | m = (1<> 1) * (multiplier); 324 | } 325 | 326 | *outPtr++ = del; 327 | 328 | c++; 329 | 330 | mb = pb_local*(n+zmode) + mb - ((pb_local*mb)>>QBSHIFT); 331 | 332 | // update mean tracking 333 | if (n > N_MAX_MEAN_CLAMP) 334 | mb = N_MEAN_CLAMP_VAL; 335 | 336 | zmode = 0; 337 | 338 | if (((mb << MMULSHIFT) < QB) && (c < numSamples)) 339 | { 340 | zmode = 1; 341 | k = lead(mb) - BITOFF+((mb+MOFF)>>MDENSHIFT); 342 | mz = ((1<= 65535) 355 | zmode = 0; 356 | 357 | mb = 0; 358 | } 359 | } 360 | 361 | Exit: 362 | *outNumBits = (bitPos - startPos); 363 | BitBufferAdvance( bitstream, *outNumBits ); 364 | RequireAction( bitstream->cur <= bitstream->end, status = kALAC_ParamError; ); 365 | 366 | return status; 367 | } 368 | 369 | #pragma GCC diagnostic pop 370 | -------------------------------------------------------------------------------- /include/alac/codec/aglib.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011 Apple Inc. All rights reserved. 3 | * 4 | * @APPLE_APACHE_LICENSE_HEADER_START@ 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * @APPLE_APACHE_LICENSE_HEADER_END@ 19 | */ 20 | 21 | /* 22 | File: aglib.h 23 | 24 | Copyright: (C) 2001-2011 Apple, Inc. 25 | */ 26 | 27 | #ifndef AGLIB_H 28 | #define AGLIB_H 29 | 30 | #include 31 | 32 | #ifdef __cplusplus 33 | extern "C" { 34 | #endif 35 | 36 | #define QBSHIFT 9 37 | #define QB (1< 4 | #include 5 | #include 6 | #include "ALACDecoder.h" 7 | #include "ALACBitUtilities.h" 8 | #include "EndianPortable.h" 9 | #include "ALACAudioTypes.h" 10 | #include 11 | 12 | // Opaque struct 13 | typedef struct 14 | { 15 | ALACDecoder decoder; 16 | uint32_t channels; 17 | uint32_t bit_depth; 18 | uint32_t frame_length; 19 | } alac_decoder_t; 20 | 21 | extern "C" alac_decoder_t *alac_decoder_init_from_config(const ALACSpecificConfig *parsedConfig) 22 | { 23 | alac_decoder_t *ctx = new alac_decoder_t(); 24 | if (!ctx) 25 | return NULL; 26 | 27 | ctx->channels = parsedConfig->numChannels; 28 | ctx->bit_depth = parsedConfig->bitDepth; 29 | ctx->frame_length = parsedConfig->frameLength; 30 | 31 | ALACSpecificConfig config_to_decoder = *parsedConfig; 32 | config_to_decoder.frameLength = Swap32NtoB(parsedConfig->frameLength); 33 | config_to_decoder.maxRun = Swap16NtoB(parsedConfig->maxRun); 34 | config_to_decoder.maxFrameBytes = Swap32NtoB(parsedConfig->maxFrameBytes); 35 | config_to_decoder.avgBitRate = Swap32NtoB(parsedConfig->avgBitRate); 36 | config_to_decoder.sampleRate = Swap32NtoB(parsedConfig->sampleRate); 37 | 38 | if (ctx->decoder.Init(&config_to_decoder, sizeof(config_to_decoder)) != 0) 39 | { 40 | delete ctx; 41 | return NULL; 42 | } 43 | 44 | return ctx; 45 | } 46 | 47 | extern "C" int alac_decoder_decode(alac_decoder_t *ctx, uint8_t *inbuffer, uint32_t inbuffer_size, 48 | int32_t *outbuffer, uint32_t *samples_decoded) 49 | { 50 | if (!ctx || !samples_decoded) 51 | return -1; 52 | 53 | BitBuffer bits; 54 | BitBufferInit(&bits, inbuffer, inbuffer_size); 55 | BitBufferByteAlign(&bits, true); 56 | 57 | int32_t ret = ctx->decoder.Decode(&bits, 58 | (uint8_t *)outbuffer, 59 | ctx->frame_length, 60 | ctx->channels, 61 | samples_decoded); 62 | 63 | return ret; 64 | } 65 | 66 | extern "C" void alac_decoder_free(alac_decoder_t *ctx) 67 | { 68 | if (ctx) 69 | delete ctx; 70 | } 71 | -------------------------------------------------------------------------------- /include/alac/codec/alac_wrapper.h: -------------------------------------------------------------------------------- 1 | #ifndef ALAC_WRAPPER_H 2 | #define ALAC_WRAPPER_H 3 | 4 | #include 5 | #include 6 | 7 | #ifdef __cplusplus 8 | extern "C" 9 | { 10 | #endif 11 | 12 | typedef struct 13 | { 14 | uint32_t frameLength; 15 | uint8_t compatibleVersion; 16 | uint8_t bitDepth; 17 | uint8_t pb; 18 | uint8_t mb; 19 | uint8_t kb; 20 | uint8_t numChannels; 21 | uint16_t maxRun; 22 | uint32_t maxFrameBytes; 23 | uint32_t avgBitRate; 24 | uint32_t sampleRate; 25 | } ALACSpecificConfig; 26 | 27 | typedef struct alac_decoder_t alac_decoder_t; 28 | 29 | alac_decoder_t *alac_decoder_init_from_config(const ALACSpecificConfig *parsedConfig); 30 | int alac_decoder_decode(alac_decoder_t *ctx, uint8_t *inbuffer, uint32_t inbuffer_size, 31 | int32_t *outbuffer, uint32_t *samples_decoded); 32 | void alac_decoder_free(alac_decoder_t *decoder); 33 | 34 | #ifdef __cplusplus 35 | } 36 | #endif 37 | 38 | #endif // ALAC_WRAPPER_H 39 | -------------------------------------------------------------------------------- /include/alac/codec/dp_dec.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011 Apple Inc. All rights reserved. 3 | * 4 | * @APPLE_APACHE_LICENSE_HEADER_START@ 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * @APPLE_APACHE_LICENSE_HEADER_END@ 19 | */ 20 | 21 | /* 22 | File: dp_dec.c 23 | 24 | Contains: Dynamic Predictor decode routines 25 | 26 | Copyright: (c) 2001-2011 Apple, Inc. 27 | */ 28 | 29 | 30 | #include "dplib.h" 31 | #include 32 | 33 | #if __GNUC__ 34 | #define ALWAYS_INLINE __attribute__((always_inline)) 35 | #else 36 | #define ALWAYS_INLINE 37 | #endif 38 | 39 | #if TARGET_CPU_PPC && (__MWERKS__ >= 0x3200) 40 | // align loops to a 16 byte boundary to make the G5 happy 41 | #pragma function_align 16 42 | #define LOOP_ALIGN asm { align 16 } 43 | #else 44 | #define LOOP_ALIGN 45 | #endif 46 | 47 | static inline int32_t ALWAYS_INLINE sign_of_int( int32_t i ) 48 | { 49 | int32_t negishift; 50 | 51 | negishift = ((uint32_t)-i) >> 31; 52 | return negishift | (i >> 31); 53 | } 54 | 55 | void unpc_block( int32_t * pc1, int32_t * out, int32_t num, int16_t * coefs, int32_t numactive, uint32_t chanbits, uint32_t denshift ) 56 | { 57 | register int16_t a0, a1, a2, a3; 58 | register int32_t b0, b1, b2, b3; 59 | int32_t j, k, lim; 60 | int32_t sum1, sg, sgn, top, dd; 61 | int32_t * pout; 62 | int32_t del, del0; 63 | uint32_t chanshift = 32 - chanbits; 64 | int32_t denhalf = 1<<(denshift-1); 65 | 66 | out[0] = pc1[0]; 67 | if ( numactive == 0 ) 68 | { 69 | // just copy if numactive == 0 (but don't bother if in/out pointers the same) 70 | if ( (num > 1) && (pc1 != out) ) 71 | memcpy( &out[1], &pc1[1], (num - 1) * sizeof(int32_t) ); 72 | return; 73 | } 74 | if ( numactive == 31 ) 75 | { 76 | // short-circuit if numactive == 31 77 | int32_t prev; 78 | 79 | /* this code is written such that the in/out buffers can be the same 80 | to conserve buffer space on embedded devices like the iPod 81 | 82 | (original code) 83 | for ( j = 1; j < num; j++ ) 84 | del = pc1[j] + out[j-1]; 85 | out[j] = (del << chanshift) >> chanshift; 86 | */ 87 | prev = out[0]; 88 | for ( j = 1; j < num; j++ ) 89 | { 90 | del = pc1[j] + prev; 91 | prev = (del << chanshift) >> chanshift; 92 | out[j] = prev; 93 | } 94 | return; 95 | } 96 | 97 | for ( j = 1; j <= numactive; j++ ) 98 | { 99 | del = pc1[j] + out[j-1]; 100 | out[j] = (del << chanshift) >> chanshift; 101 | } 102 | 103 | lim = numactive + 1; 104 | 105 | if ( numactive == 4 ) 106 | { 107 | // optimization for numactive == 4 108 | register int16_t a0, a1, a2, a3; 109 | register int32_t b0, b1, b2, b3; 110 | 111 | a0 = coefs[0]; 112 | a1 = coefs[1]; 113 | a2 = coefs[2]; 114 | a3 = coefs[3]; 115 | 116 | for ( j = lim; j < num; j++ ) 117 | { 118 | LOOP_ALIGN 119 | 120 | top = out[j - lim]; 121 | pout = out + j - 1; 122 | 123 | b0 = top - pout[0]; 124 | b1 = top - pout[-1]; 125 | b2 = top - pout[-2]; 126 | b3 = top - pout[-3]; 127 | 128 | sum1 = (denhalf - a0 * b0 - a1 * b1 - a2 * b2 - a3 * b3) >> denshift; 129 | 130 | del = pc1[j]; 131 | del0 = del; 132 | sg = sign_of_int(del); 133 | del += top + sum1; 134 | 135 | out[j] = (del << chanshift) >> chanshift; 136 | 137 | if ( sg > 0 ) 138 | { 139 | sgn = sign_of_int( b3 ); 140 | a3 -= sgn; 141 | del0 -= (4 - 3) * ((sgn * b3) >> denshift); 142 | if ( del0 <= 0 ) 143 | continue; 144 | 145 | sgn = sign_of_int( b2 ); 146 | a2 -= sgn; 147 | del0 -= (4 - 2) * ((sgn * b2) >> denshift); 148 | if ( del0 <= 0 ) 149 | continue; 150 | 151 | sgn = sign_of_int( b1 ); 152 | a1 -= sgn; 153 | del0 -= (4 - 1) * ((sgn * b1) >> denshift); 154 | if ( del0 <= 0 ) 155 | continue; 156 | 157 | a0 -= sign_of_int( b0 ); 158 | } 159 | else if ( sg < 0 ) 160 | { 161 | // note: to avoid unnecessary negations, we flip the value of "sgn" 162 | sgn = -sign_of_int( b3 ); 163 | a3 -= sgn; 164 | del0 -= (4 - 3) * ((sgn * b3) >> denshift); 165 | if ( del0 >= 0 ) 166 | continue; 167 | 168 | sgn = -sign_of_int( b2 ); 169 | a2 -= sgn; 170 | del0 -= (4 - 2) * ((sgn * b2) >> denshift); 171 | if ( del0 >= 0 ) 172 | continue; 173 | 174 | sgn = -sign_of_int( b1 ); 175 | a1 -= sgn; 176 | del0 -= (4 - 1) * ((sgn * b1) >> denshift); 177 | if ( del0 >= 0 ) 178 | continue; 179 | 180 | a0 += sign_of_int( b0 ); 181 | } 182 | } 183 | 184 | coefs[0] = a0; 185 | coefs[1] = a1; 186 | coefs[2] = a2; 187 | coefs[3] = a3; 188 | } 189 | else if ( numactive == 8 ) 190 | { 191 | register int16_t a4, a5, a6, a7; 192 | register int32_t b4, b5, b6, b7; 193 | 194 | // optimization for numactive == 8 195 | a0 = coefs[0]; 196 | a1 = coefs[1]; 197 | a2 = coefs[2]; 198 | a3 = coefs[3]; 199 | a4 = coefs[4]; 200 | a5 = coefs[5]; 201 | a6 = coefs[6]; 202 | a7 = coefs[7]; 203 | 204 | for ( j = lim; j < num; j++ ) 205 | { 206 | LOOP_ALIGN 207 | 208 | top = out[j - lim]; 209 | pout = out + j - 1; 210 | 211 | b0 = top - (*pout--); 212 | b1 = top - (*pout--); 213 | b2 = top - (*pout--); 214 | b3 = top - (*pout--); 215 | b4 = top - (*pout--); 216 | b5 = top - (*pout--); 217 | b6 = top - (*pout--); 218 | b7 = top - (*pout); 219 | pout += 8; 220 | 221 | sum1 = (denhalf - a0 * b0 - a1 * b1 - a2 * b2 - a3 * b3 222 | - a4 * b4 - a5 * b5 - a6 * b6 - a7 * b7) >> denshift; 223 | 224 | del = pc1[j]; 225 | del0 = del; 226 | sg = sign_of_int(del); 227 | del += top + sum1; 228 | 229 | out[j] = (del << chanshift) >> chanshift; 230 | 231 | if ( sg > 0 ) 232 | { 233 | sgn = sign_of_int( b7 ); 234 | a7 -= sgn; 235 | del0 -= 1 * ((sgn * b7) >> denshift); 236 | if ( del0 <= 0 ) 237 | continue; 238 | 239 | sgn = sign_of_int( b6 ); 240 | a6 -= sgn; 241 | del0 -= 2 * ((sgn * b6) >> denshift); 242 | if ( del0 <= 0 ) 243 | continue; 244 | 245 | sgn = sign_of_int( b5 ); 246 | a5 -= sgn; 247 | del0 -= 3 * ((sgn * b5) >> denshift); 248 | if ( del0 <= 0 ) 249 | continue; 250 | 251 | sgn = sign_of_int( b4 ); 252 | a4 -= sgn; 253 | del0 -= 4 * ((sgn * b4) >> denshift); 254 | if ( del0 <= 0 ) 255 | continue; 256 | 257 | sgn = sign_of_int( b3 ); 258 | a3 -= sgn; 259 | del0 -= 5 * ((sgn * b3) >> denshift); 260 | if ( del0 <= 0 ) 261 | continue; 262 | 263 | sgn = sign_of_int( b2 ); 264 | a2 -= sgn; 265 | del0 -= 6 * ((sgn * b2) >> denshift); 266 | if ( del0 <= 0 ) 267 | continue; 268 | 269 | sgn = sign_of_int( b1 ); 270 | a1 -= sgn; 271 | del0 -= 7 * ((sgn * b1) >> denshift); 272 | if ( del0 <= 0 ) 273 | continue; 274 | 275 | a0 -= sign_of_int( b0 ); 276 | } 277 | else if ( sg < 0 ) 278 | { 279 | // note: to avoid unnecessary negations, we flip the value of "sgn" 280 | sgn = -sign_of_int( b7 ); 281 | a7 -= sgn; 282 | del0 -= 1 * ((sgn * b7) >> denshift); 283 | if ( del0 >= 0 ) 284 | continue; 285 | 286 | sgn = -sign_of_int( b6 ); 287 | a6 -= sgn; 288 | del0 -= 2 * ((sgn * b6) >> denshift); 289 | if ( del0 >= 0 ) 290 | continue; 291 | 292 | sgn = -sign_of_int( b5 ); 293 | a5 -= sgn; 294 | del0 -= 3 * ((sgn * b5) >> denshift); 295 | if ( del0 >= 0 ) 296 | continue; 297 | 298 | sgn = -sign_of_int( b4 ); 299 | a4 -= sgn; 300 | del0 -= 4 * ((sgn * b4) >> denshift); 301 | if ( del0 >= 0 ) 302 | continue; 303 | 304 | sgn = -sign_of_int( b3 ); 305 | a3 -= sgn; 306 | del0 -= 5 * ((sgn * b3) >> denshift); 307 | if ( del0 >= 0 ) 308 | continue; 309 | 310 | sgn = -sign_of_int( b2 ); 311 | a2 -= sgn; 312 | del0 -= 6 * ((sgn * b2) >> denshift); 313 | if ( del0 >= 0 ) 314 | continue; 315 | 316 | sgn = -sign_of_int( b1 ); 317 | a1 -= sgn; 318 | del0 -= 7 * ((sgn * b1) >> denshift); 319 | if ( del0 >= 0 ) 320 | continue; 321 | 322 | a0 += sign_of_int( b0 ); 323 | } 324 | } 325 | 326 | coefs[0] = a0; 327 | coefs[1] = a1; 328 | coefs[2] = a2; 329 | coefs[3] = a3; 330 | coefs[4] = a4; 331 | coefs[5] = a5; 332 | coefs[6] = a6; 333 | coefs[7] = a7; 334 | } 335 | else 336 | { 337 | // general case 338 | for ( j = lim; j < num; j++ ) 339 | { 340 | LOOP_ALIGN 341 | 342 | sum1 = 0; 343 | pout = out + j - 1; 344 | top = out[j-lim]; 345 | 346 | for ( k = 0; k < numactive; k++ ) 347 | sum1 += coefs[k] * (pout[-k] - top); 348 | 349 | del = pc1[j]; 350 | del0 = del; 351 | sg = sign_of_int( del ); 352 | del += top + ((sum1 + denhalf) >> denshift); 353 | out[j] = (del << chanshift) >> chanshift; 354 | 355 | if ( sg > 0 ) 356 | { 357 | for ( k = (numactive - 1); k >= 0; k-- ) 358 | { 359 | dd = top - pout[-k]; 360 | sgn = sign_of_int( dd ); 361 | coefs[k] -= sgn; 362 | del0 -= (numactive - k) * ((sgn * dd) >> denshift); 363 | if ( del0 <= 0 ) 364 | break; 365 | } 366 | } 367 | else if ( sg < 0 ) 368 | { 369 | for ( k = (numactive - 1); k >= 0; k-- ) 370 | { 371 | dd = top - pout[-k]; 372 | sgn = sign_of_int( dd ); 373 | coefs[k] += sgn; 374 | del0 -= (numactive - k) * ((-sgn * dd) >> denshift); 375 | if ( del0 >= 0 ) 376 | break; 377 | } 378 | } 379 | } 380 | } 381 | } 382 | -------------------------------------------------------------------------------- /include/alac/codec/dplib.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011 Apple Inc. All rights reserved. 3 | * 4 | * @APPLE_APACHE_LICENSE_HEADER_START@ 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * @APPLE_APACHE_LICENSE_HEADER_END@ 19 | */ 20 | 21 | /* 22 | File: dplib.h 23 | 24 | Contains: Dynamic Predictor routines 25 | 26 | Copyright: Copyright (C) 2001-2011 Apple, Inc. 27 | */ 28 | 29 | #ifndef __DPLIB_H__ 30 | #define __DPLIB_H__ 31 | 32 | #include 33 | 34 | #ifdef __cplusplus 35 | extern "C" { 36 | #endif 37 | 38 | // defines 39 | 40 | #define DENSHIFT_MAX 15 41 | #define DENSHIFT_DEFAULT 9 42 | #define AINIT 38 43 | #define BINIT (-29) 44 | #define CINIT (-2) 45 | #define NUMCOEPAIRS 16 46 | 47 | // prototypes 48 | 49 | void init_coefs( int16_t * coefs, uint32_t denshift, int32_t numPairs ); 50 | void copy_coefs( int16_t * srcCoefs, int16_t * dstCoefs, int32_t numPairs ); 51 | 52 | // NOTE: these routines read at least "numactive" samples so the i/o buffers must be at least that big 53 | 54 | void pc_block( int32_t * in, int32_t * pc, int32_t num, int16_t * coefs, int32_t numactive, uint32_t chanbits, uint32_t denshift ); 55 | void unpc_block( int32_t * pc, int32_t * out, int32_t num, int16_t * coefs, int32_t numactive, uint32_t chanbits, uint32_t denshift ); 56 | 57 | #ifdef __cplusplus 58 | } 59 | #endif 60 | 61 | #endif /* __DPLIB_H__ */ 62 | -------------------------------------------------------------------------------- /include/alac/codec/matrix_dec.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011 Apple Inc. All rights reserved. 3 | * 4 | * @APPLE_APACHE_LICENSE_HEADER_START@ 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * @APPLE_APACHE_LICENSE_HEADER_END@ 19 | */ 20 | 21 | /* 22 | File: matrix_dec.c 23 | 24 | Contains: ALAC mixing/matrixing decode routines. 25 | 26 | Copyright: (c) 2004-2011 Apple, Inc. 27 | */ 28 | 29 | #include "matrixlib.h" 30 | #include "ALACAudioTypes.h" 31 | 32 | // up to 24-bit "offset" macros for the individual bytes of a 20/24-bit word 33 | #if TARGET_RT_BIG_ENDIAN 34 | #define LBYTE 2 35 | #define MBYTE 1 36 | #define HBYTE 0 37 | #else 38 | #define LBYTE 0 39 | #define MBYTE 1 40 | #define HBYTE 2 41 | #endif 42 | 43 | /* 44 | There is no plain middle-side option; instead there are various mixing 45 | modes including middle-side, each lossless, as embodied in the mix() 46 | and unmix() functions. These functions exploit a generalized middle-side 47 | transformation: 48 | 49 | u := [(rL + (m-r)R)/m]; 50 | v := L - R; 51 | 52 | where [ ] denotes integer floor. The (lossless) inverse is 53 | 54 | L = u + v - [rV/m]; 55 | R = L - v; 56 | */ 57 | 58 | // 16-bit routines 59 | 60 | void unmix16( int32_t * u, int32_t * v, int16_t * out, uint32_t stride, int32_t numSamples, int32_t mixbits, int32_t mixres ) 61 | { 62 | int16_t * op = out; 63 | int32_t j; 64 | 65 | if ( mixres != 0 ) 66 | { 67 | /* matrixed stereo */ 68 | for ( j = 0; j < numSamples; j++ ) 69 | { 70 | int32_t l, r; 71 | 72 | l = u[j] + v[j] - ((mixres * v[j]) >> mixbits); 73 | r = l - v[j]; 74 | 75 | op[0] = (int16_t) l; 76 | op[1] = (int16_t) r; 77 | op += stride; 78 | } 79 | } 80 | else 81 | { 82 | /* Conventional separated stereo. */ 83 | for ( j = 0; j < numSamples; j++ ) 84 | { 85 | op[0] = (int16_t) u[j]; 86 | op[1] = (int16_t) v[j]; 87 | op += stride; 88 | } 89 | } 90 | } 91 | 92 | // 20-bit routines 93 | // - the 20 bits of data are left-justified in 3 bytes of storage but right-aligned for input/output predictor buffers 94 | 95 | void unmix20( int32_t * u, int32_t * v, uint8_t * out, uint32_t stride, int32_t numSamples, int32_t mixbits, int32_t mixres ) 96 | { 97 | uint8_t * op = out; 98 | int32_t j; 99 | 100 | if ( mixres != 0 ) 101 | { 102 | /* matrixed stereo */ 103 | for ( j = 0; j < numSamples; j++ ) 104 | { 105 | int32_t l, r; 106 | 107 | l = u[j] + v[j] - ((mixres * v[j]) >> mixbits); 108 | r = l - v[j]; 109 | 110 | l <<= 4; 111 | r <<= 4; 112 | 113 | op[HBYTE] = (uint8_t)((l >> 16) & 0xffu); 114 | op[MBYTE] = (uint8_t)((l >> 8) & 0xffu); 115 | op[LBYTE] = (uint8_t)((l >> 0) & 0xffu); 116 | op += 3; 117 | 118 | op[HBYTE] = (uint8_t)((r >> 16) & 0xffu); 119 | op[MBYTE] = (uint8_t)((r >> 8) & 0xffu); 120 | op[LBYTE] = (uint8_t)((r >> 0) & 0xffu); 121 | 122 | op += (stride - 1) * 3; 123 | } 124 | } 125 | else 126 | { 127 | /* Conventional separated stereo. */ 128 | for ( j = 0; j < numSamples; j++ ) 129 | { 130 | int32_t val; 131 | 132 | val = u[j] << 4; 133 | op[HBYTE] = (uint8_t)((val >> 16) & 0xffu); 134 | op[MBYTE] = (uint8_t)((val >> 8) & 0xffu); 135 | op[LBYTE] = (uint8_t)((val >> 0) & 0xffu); 136 | op += 3; 137 | 138 | val = v[j] << 4; 139 | op[HBYTE] = (uint8_t)((val >> 16) & 0xffu); 140 | op[MBYTE] = (uint8_t)((val >> 8) & 0xffu); 141 | op[LBYTE] = (uint8_t)((val >> 0) & 0xffu); 142 | 143 | op += (stride - 1) * 3; 144 | } 145 | } 146 | } 147 | 148 | // 24-bit routines 149 | // - the 24 bits of data are right-justified in the input/output predictor buffers 150 | 151 | void unmix24( int32_t * u, int32_t * v, uint8_t * out, uint32_t stride, int32_t numSamples, 152 | int32_t mixbits, int32_t mixres, uint16_t * shiftUV, int32_t bytesShifted ) 153 | { 154 | uint8_t * op = out; 155 | int32_t shift = bytesShifted * 8; 156 | int32_t l, r; 157 | int32_t j, k; 158 | 159 | if ( mixres != 0 ) 160 | { 161 | /* matrixed stereo */ 162 | if ( bytesShifted != 0 ) 163 | { 164 | for ( j = 0, k = 0; j < numSamples; j++, k += 2 ) 165 | { 166 | l = u[j] + v[j] - ((mixres * v[j]) >> mixbits); 167 | r = l - v[j]; 168 | 169 | l = (l << shift) | (uint32_t) shiftUV[k + 0]; 170 | r = (r << shift) | (uint32_t) shiftUV[k + 1]; 171 | 172 | op[HBYTE] = (uint8_t)((l >> 16) & 0xffu); 173 | op[MBYTE] = (uint8_t)((l >> 8) & 0xffu); 174 | op[LBYTE] = (uint8_t)((l >> 0) & 0xffu); 175 | op += 3; 176 | 177 | op[HBYTE] = (uint8_t)((r >> 16) & 0xffu); 178 | op[MBYTE] = (uint8_t)((r >> 8) & 0xffu); 179 | op[LBYTE] = (uint8_t)((r >> 0) & 0xffu); 180 | 181 | op += (stride - 1) * 3; 182 | } 183 | } 184 | else 185 | { 186 | for ( j = 0; j < numSamples; j++ ) 187 | { 188 | l = u[j] + v[j] - ((mixres * v[j]) >> mixbits); 189 | r = l - v[j]; 190 | 191 | op[HBYTE] = (uint8_t)((l >> 16) & 0xffu); 192 | op[MBYTE] = (uint8_t)((l >> 8) & 0xffu); 193 | op[LBYTE] = (uint8_t)((l >> 0) & 0xffu); 194 | op += 3; 195 | 196 | op[HBYTE] = (uint8_t)((r >> 16) & 0xffu); 197 | op[MBYTE] = (uint8_t)((r >> 8) & 0xffu); 198 | op[LBYTE] = (uint8_t)((r >> 0) & 0xffu); 199 | 200 | op += (stride - 1) * 3; 201 | } 202 | } 203 | } 204 | else 205 | { 206 | /* Conventional separated stereo. */ 207 | if ( bytesShifted != 0 ) 208 | { 209 | for ( j = 0, k = 0; j < numSamples; j++, k += 2 ) 210 | { 211 | l = u[j]; 212 | r = v[j]; 213 | 214 | l = (l << shift) | (uint32_t) shiftUV[k + 0]; 215 | r = (r << shift) | (uint32_t) shiftUV[k + 1]; 216 | 217 | op[HBYTE] = (uint8_t)((l >> 16) & 0xffu); 218 | op[MBYTE] = (uint8_t)((l >> 8) & 0xffu); 219 | op[LBYTE] = (uint8_t)((l >> 0) & 0xffu); 220 | op += 3; 221 | 222 | op[HBYTE] = (uint8_t)((r >> 16) & 0xffu); 223 | op[MBYTE] = (uint8_t)((r >> 8) & 0xffu); 224 | op[LBYTE] = (uint8_t)((r >> 0) & 0xffu); 225 | 226 | op += (stride - 1) * 3; 227 | } 228 | } 229 | else 230 | { 231 | for ( j = 0; j < numSamples; j++ ) 232 | { 233 | int32_t val; 234 | 235 | val = u[j]; 236 | op[HBYTE] = (uint8_t)((val >> 16) & 0xffu); 237 | op[MBYTE] = (uint8_t)((val >> 8) & 0xffu); 238 | op[LBYTE] = (uint8_t)((val >> 0) & 0xffu); 239 | op += 3; 240 | 241 | val = v[j]; 242 | op[HBYTE] = (uint8_t)((val >> 16) & 0xffu); 243 | op[MBYTE] = (uint8_t)((val >> 8) & 0xffu); 244 | op[LBYTE] = (uint8_t)((val >> 0) & 0xffu); 245 | 246 | op += (stride - 1) * 3; 247 | } 248 | } 249 | } 250 | } 251 | 252 | // 32-bit routines 253 | // - note that these really expect the internal data width to be < 32 but the arrays are 32-bit 254 | // - otherwise, the calculations might overflow into the 33rd bit and be lost 255 | // - therefore, these routines deal with the specified "unused lower" bytes in the "shift" buffers 256 | 257 | void unmix32( int32_t * u, int32_t * v, int32_t * out, uint32_t stride, int32_t numSamples, 258 | int32_t mixbits, int32_t mixres, uint16_t * shiftUV, int32_t bytesShifted ) 259 | { 260 | int32_t * op = out; 261 | int32_t shift = bytesShifted * 8; 262 | int32_t l, r; 263 | int32_t j, k; 264 | 265 | if ( mixres != 0 ) 266 | { 267 | //Assert( bytesShifted != 0 ); 268 | 269 | /* matrixed stereo with shift */ 270 | for ( j = 0, k = 0; j < numSamples; j++, k += 2 ) 271 | { 272 | int32_t lt, rt; 273 | 274 | lt = u[j]; 275 | rt = v[j]; 276 | 277 | l = lt + rt - ((mixres * rt) >> mixbits); 278 | r = l - rt; 279 | 280 | op[0] = (l << shift) | (uint32_t) shiftUV[k + 0]; 281 | op[1] = (r << shift) | (uint32_t) shiftUV[k + 1]; 282 | op += stride; 283 | } 284 | } 285 | else 286 | { 287 | if ( bytesShifted == 0 ) 288 | { 289 | /* interleaving w/o shift */ 290 | for ( j = 0; j < numSamples; j++ ) 291 | { 292 | op[0] = u[j]; 293 | op[1] = v[j]; 294 | op += stride; 295 | } 296 | } 297 | else 298 | { 299 | /* interleaving with shift */ 300 | for ( j = 0, k = 0; j < numSamples; j++, k += 2 ) 301 | { 302 | op[0] = (u[j] << shift) | (uint32_t) shiftUV[k + 0]; 303 | op[1] = (v[j] << shift) | (uint32_t) shiftUV[k + 1]; 304 | op += stride; 305 | } 306 | } 307 | } 308 | } 309 | 310 | // 20/24-bit <-> 32-bit helper routines (not really matrixing but convenient to put here) 311 | 312 | void copyPredictorTo24( int32_t * in, uint8_t * out, uint32_t stride, int32_t numSamples ) 313 | { 314 | uint8_t * op = out; 315 | int32_t j; 316 | 317 | for ( j = 0; j < numSamples; j++ ) 318 | { 319 | int32_t val = in[j]; 320 | 321 | op[HBYTE] = (uint8_t)((val >> 16) & 0xffu); 322 | op[MBYTE] = (uint8_t)((val >> 8) & 0xffu); 323 | op[LBYTE] = (uint8_t)((val >> 0) & 0xffu); 324 | op += (stride * 3); 325 | } 326 | } 327 | 328 | void copyPredictorTo24Shift( int32_t * in, uint16_t * shift, uint8_t * out, uint32_t stride, int32_t numSamples, int32_t bytesShifted ) 329 | { 330 | uint8_t * op = out; 331 | int32_t shiftVal = bytesShifted * 8; 332 | int32_t j; 333 | 334 | //Assert( bytesShifted != 0 ); 335 | 336 | for ( j = 0; j < numSamples; j++ ) 337 | { 338 | int32_t val = in[j]; 339 | 340 | val = (val << shiftVal) | (uint32_t) shift[j]; 341 | 342 | op[HBYTE] = (uint8_t)((val >> 16) & 0xffu); 343 | op[MBYTE] = (uint8_t)((val >> 8) & 0xffu); 344 | op[LBYTE] = (uint8_t)((val >> 0) & 0xffu); 345 | op += (stride * 3); 346 | } 347 | } 348 | 349 | void copyPredictorTo20( int32_t * in, uint8_t * out, uint32_t stride, int32_t numSamples ) 350 | { 351 | uint8_t * op = out; 352 | int32_t j; 353 | 354 | // 32-bit predictor values are right-aligned but 20-bit output values should be left-aligned 355 | // in the 24-bit output buffer 356 | for ( j = 0; j < numSamples; j++ ) 357 | { 358 | int32_t val = in[j]; 359 | 360 | op[HBYTE] = (uint8_t)((val >> 12) & 0xffu); 361 | op[MBYTE] = (uint8_t)((val >> 4) & 0xffu); 362 | op[LBYTE] = (uint8_t)((val << 4) & 0xffu); 363 | op += (stride * 3); 364 | } 365 | } 366 | 367 | void copyPredictorTo32( int32_t * in, int32_t * out, uint32_t stride, int32_t numSamples ) 368 | { 369 | int32_t i, j; 370 | 371 | // this is only a subroutine to abstract the "iPod can only output 16-bit data" problem 372 | for ( i = 0, j = 0; i < numSamples; i++, j += stride ) 373 | out[j] = in[i]; 374 | } 375 | 376 | void copyPredictorTo32Shift( int32_t * in, uint16_t * shift, int32_t * out, uint32_t stride, int32_t numSamples, int32_t bytesShifted ) 377 | { 378 | int32_t * op = out; 379 | uint32_t shiftVal = bytesShifted * 8; 380 | int32_t j; 381 | 382 | //Assert( bytesShifted != 0 ); 383 | 384 | // this is only a subroutine to abstract the "iPod can only output 16-bit data" problem 385 | for ( j = 0; j < numSamples; j++ ) 386 | { 387 | op[0] = (in[j] << shiftVal) | (uint32_t) shift[j]; 388 | op += stride; 389 | } 390 | } 391 | -------------------------------------------------------------------------------- /include/alac/codec/matrixlib.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011 Apple Inc. All rights reserved. 3 | * 4 | * @APPLE_APACHE_LICENSE_HEADER_START@ 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * @APPLE_APACHE_LICENSE_HEADER_END@ 19 | */ 20 | 21 | /* 22 | File: matrixlib.h 23 | 24 | Contains: ALAC mixing/matrixing routines to/from 32-bit predictor buffers. 25 | 26 | Copyright: Copyright (C) 2004 to 2011 Apple, Inc. 27 | */ 28 | 29 | #ifndef __MATRIXLIB_H 30 | #define __MATRIXLIB_H 31 | 32 | #pragma once 33 | 34 | #include 35 | 36 | #ifdef __cplusplus 37 | extern "C" { 38 | #endif 39 | 40 | // 16-bit routines 41 | void mix16( int16_t * in, uint32_t stride, int32_t * u, int32_t * v, int32_t numSamples, int32_t mixbits, int32_t mixres ); 42 | void unmix16( int32_t * u, int32_t * v, int16_t * out, uint32_t stride, int32_t numSamples, int32_t mixbits, int32_t mixres ); 43 | 44 | // 20-bit routines 45 | void mix20( uint8_t * in, uint32_t stride, int32_t * u, int32_t * v, int32_t numSamples, int32_t mixbits, int32_t mixres ); 46 | void unmix20( int32_t * u, int32_t * v, uint8_t * out, uint32_t stride, int32_t numSamples, int32_t mixbits, int32_t mixres ); 47 | 48 | // 24-bit routines 49 | // - 24-bit data sometimes compresses better by shifting off the bottom byte so these routines deal with 50 | // the specified "unused lower bytes" in the combined "shift" buffer 51 | void mix24( uint8_t * in, uint32_t stride, int32_t * u, int32_t * v, int32_t numSamples, 52 | int32_t mixbits, int32_t mixres, uint16_t * shiftUV, int32_t bytesShifted ); 53 | void unmix24( int32_t * u, int32_t * v, uint8_t * out, uint32_t stride, int32_t numSamples, 54 | int32_t mixbits, int32_t mixres, uint16_t * shiftUV, int32_t bytesShifted ); 55 | 56 | // 32-bit routines 57 | // - note that these really expect the internal data width to be < 32-bit but the arrays are 32-bit 58 | // - otherwise, the calculations might overflow into the 33rd bit and be lost 59 | // - therefore, these routines deal with the specified "unused lower" bytes in the combined "shift" buffer 60 | void mix32( int32_t * in, uint32_t stride, int32_t * u, int32_t * v, int32_t numSamples, 61 | int32_t mixbits, int32_t mixres, uint16_t * shiftUV, int32_t bytesShifted ); 62 | void unmix32( int32_t * u, int32_t * v, int32_t * out, uint32_t stride, int32_t numSamples, 63 | int32_t mixbits, int32_t mixres, uint16_t * shiftUV, int32_t bytesShifted ); 64 | 65 | // 20/24/32-bit <-> 32-bit helper routines (not really matrixing but convenient to put here) 66 | void copy20ToPredictor( uint8_t * in, uint32_t stride, int32_t * out, int32_t numSamples ); 67 | void copy24ToPredictor( uint8_t * in, uint32_t stride, int32_t * out, int32_t numSamples ); 68 | 69 | void copyPredictorTo24( int32_t * in, uint8_t * out, uint32_t stride, int32_t numSamples ); 70 | void copyPredictorTo24Shift( int32_t * in, uint16_t * shift, uint8_t * out, uint32_t stride, int32_t numSamples, int32_t bytesShifted ); 71 | void copyPredictorTo20( int32_t * in, uint8_t * out, uint32_t stride, int32_t numSamples ); 72 | 73 | void copyPredictorTo32( int32_t * in, int32_t * out, uint32_t stride, int32_t numSamples ); 74 | void copyPredictorTo32Shift( int32_t * in, uint16_t * shift, int32_t * out, uint32_t stride, int32_t numSamples, int32_t bytesShifted ); 75 | 76 | #ifdef __cplusplus 77 | } 78 | #endif 79 | 80 | #endif /* __MATRIXLIB_H */ 81 | -------------------------------------------------------------------------------- /include/nestegg/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright © 2010 Mozilla Foundation 2 | 3 | Permission to use, copy, modify, and distribute this software for any 4 | purpose with or without fee is hereby granted, provided that the above 5 | copyright notice and this permission notice appear in all copies. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | -------------------------------------------------------------------------------- /src/appstate.h: -------------------------------------------------------------------------------- 1 | #ifndef APPSTATE_H 2 | #define APPSTATE_H 3 | 4 | #include "cache.h" 5 | 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | #ifndef MAXPATHLEN 12 | #define MAXPATHLEN 4096 13 | #endif 14 | 15 | #ifndef PIXELDATA_STRUCT 16 | #define PIXELDATA_STRUCT 17 | 18 | typedef struct 19 | { 20 | unsigned char r; 21 | unsigned char g; 22 | unsigned char b; 23 | } PixelData; 24 | #endif 25 | 26 | typedef enum 27 | { 28 | TRACK_VIEW, 29 | KEYBINDINGS_VIEW, 30 | PLAYLIST_VIEW, 31 | LIBRARY_VIEW, 32 | SEARCH_VIEW 33 | } ViewState; 34 | 35 | typedef struct 36 | { 37 | int mainColor; // Main terminal color, when using config colors 38 | int titleColor; // Color of the title, when using config colors 39 | int artistColor; // Artist color, when using config colors 40 | int enqueuedColor; // Color of enqueued files, when using config colors 41 | bool mouseEnabled; // Accept mouse input or not 42 | int mouseLeftClickAction; // Left mouse action 43 | int mouseMiddleClickAction; // Middle mouse action 44 | int mouseRightClickAction; // Right mouse action 45 | int mouseScrollUpAction; // Mouse scroll up action 46 | int mouseScrollDownAction; // Mouse scroll down action 47 | int mouseAltScrollUpAction; // Mouse scroll up + alt action 48 | int mouseAltScrollDownAction; // Mouse scroll down + alt action 49 | PixelData color; // The current color, when using album derived colors 50 | bool useConfigColors; // Use colors stored in config file or use an album derived color 51 | bool coverEnabled; // Show covers or not 52 | bool uiEnabled; // Show ui or not 53 | bool coverAnsi; // Show chafa cover (picture perfect in the right terminal), or ascii/ansi typ cover 54 | bool visualizerEnabled; // Show spectrum visualizer 55 | bool hideLogo; // No kew text at top 56 | bool hideHelp; // No help text at top 57 | bool allowNotifications; // Send desktop notifications or not 58 | int visualizerHeight; // Height in characters of the spectrum visualizer 59 | int visualizerColorType; // How colors are laid out in the spectrum visualizer 60 | bool visualizerBrailleMode; // Display the visualizer using braille characteres 61 | int titleDelay; // Delay when drawing title in track view 62 | int cacheLibrary; // Cache the library or not 63 | bool quitAfterStopping; // Exit kew when the music stops or not 64 | bool hideGlimmeringText; // Glimmering text on the bottom row 65 | time_t lastTimeAppRan; // When did this app run last, used for updating the cached library if it has been modified since that time 66 | int visualizerBarWidth; // 0=Thin bars, 1=Bars twice the width or 2=Auto (Depends on window size, default) 67 | int replayGainCheckFirst; // Prioritize track or album replay gain setting 68 | bool saveRepeatShuffleSettings; // Save repeat and shuffle settings between sessions. Default on. 69 | int repeatState; // 0=disabled,1=repeat track ,2=repeat list 70 | bool shuffleEnabled; 71 | } UISettings; 72 | 73 | typedef struct 74 | { 75 | int chosenNodeId; // The id of the tree node that is chosen in library view 76 | bool allowChooseSongs; // In library view, has the user entered a folder that contains songs 77 | bool openedSubDir; // Opening a directory in an open directory. 78 | int numSongsAboveSubDir; // How many rows do we need to jump up if we close the parent directory and open one within 79 | int numDirectoryTreeEntries; // The number of entries in directory tree in library view 80 | int numProgressBars; // The number of progress dots at the bottom of track view 81 | volatile sig_atomic_t resizeFlag; // Is the user resizing the terminal window 82 | bool resetPlaylistDisplay; // Should the playlist be reset, ie drawn starting from playing song 83 | bool doNotifyMPRISSwitched; // Emit mpris song switched signal 84 | bool doNotifyMPRISPlaying; // Emit mpris music is playing signal 85 | bool collapseView; // Signal that ui needs to collapse the view 86 | bool miniMode; 87 | } UIState; 88 | 89 | typedef struct 90 | { 91 | Cache *tmpCache; // Cache for temporary files 92 | ViewState currentView; // The current view (playlist, library, track) that kew is on 93 | UIState uiState; 94 | UISettings uiSettings; 95 | } AppState; 96 | 97 | static const unsigned char defaultColor = 150; 98 | 99 | #ifndef KEYVALUEPAIR_STRUCT 100 | #define KEYVALUEPAIR_STRUCT 101 | 102 | typedef struct 103 | { 104 | char *key; 105 | char *value; 106 | } KeyValuePair; 107 | 108 | #endif 109 | 110 | #ifndef APPSETTINGS_STRUCT 111 | 112 | typedef struct 113 | { 114 | char path[MAXPATHLEN]; 115 | char coverEnabled[2]; 116 | char coverAnsi[2]; 117 | char useConfigColors[2]; 118 | char visualizerEnabled[2]; 119 | char visualizerHeight[6]; 120 | char visualizerColorType[2]; 121 | char titleDelay[6]; 122 | char togglePlaylist[6]; 123 | char toggleBindings[6]; 124 | char volumeUp[6]; 125 | char volumeUpAlt[6]; 126 | char volumeDown[6]; 127 | char previousTrackAlt[6]; 128 | char nextTrackAlt[6]; 129 | char scrollUpAlt[6]; 130 | char scrollDownAlt[6]; 131 | char switchNumberedSong[6]; 132 | char switchNumberedSongAlt[6]; 133 | char switchNumberedSongAlt2[6]; 134 | char togglePause[6]; 135 | char toggleColorsDerivedFrom[6]; 136 | char toggleVisualizer[6]; 137 | char toggleAscii[6]; 138 | char toggleRepeat[6]; 139 | char toggleShuffle[6]; 140 | char seekBackward[6]; 141 | char seekForward[6]; 142 | char savePlaylist[6]; 143 | char addToMainPlaylist[6]; 144 | char updateLibrary[6]; 145 | char quit[6]; 146 | char altQuit[6]; 147 | char hardSwitchNumberedSong[6]; 148 | char hardPlayPause[6]; 149 | char hardPrev[6]; 150 | char hardNext[6]; 151 | char hardScrollUp[6]; 152 | char hardScrollDown[6]; 153 | char hardShowPlaylist[6]; 154 | char hardShowPlaylistAlt[6]; 155 | char showPlaylistAlt[6]; 156 | char hardShowKeys[6]; 157 | char hardShowKeysAlt[6]; 158 | char showKeysAlt[6]; 159 | char hardEndOfPlaylist[6]; 160 | char hardShowLibrary[6]; 161 | char hardShowLibraryAlt[6]; 162 | char showLibraryAlt[6]; 163 | char hardShowSearch[6]; 164 | char hardShowSearchAlt[6]; 165 | char showSearchAlt[6]; 166 | char hardShowTrack[6]; 167 | char hardShowTrackAlt[6]; 168 | char showTrackAlt[6]; 169 | char nextPage[6]; 170 | char prevPage[6]; 171 | char hardRemove[6]; 172 | char hardRemove2[6]; 173 | char mouseLeftClick[12]; 174 | char mouseMiddleClick[12]; 175 | char mouseRightClick[12]; 176 | char mouseScrollUp[12]; 177 | char mouseScrollDown[12]; 178 | char mouseAltScrollUp[12]; 179 | char mouseAltScrollDown[12]; 180 | char lastVolume[12]; 181 | char allowNotifications[2]; 182 | char color[2]; 183 | char artistColor[2]; 184 | char enqueuedColor[2]; 185 | char titleColor[2]; 186 | char mouseEnabled[2]; 187 | char mouseLeftClickAction[3]; 188 | char mouseMiddleClickAction[3]; 189 | char mouseRightClickAction[3]; 190 | char mouseScrollUpAction[3]; 191 | char mouseScrollDownAction[3]; 192 | char mouseAltScrollUpAction[3]; 193 | char mouseAltScrollDownAction[3]; 194 | char hideLogo[2]; 195 | char hideHelp[2]; 196 | char cacheLibrary[6]; 197 | char quitAfterStopping[2]; 198 | char hideGlimmeringText[2]; 199 | char nextView[6]; 200 | char prevView[6]; 201 | char hardClearPlaylist[6]; 202 | char moveSongUp[6]; 203 | char moveSongDown[6]; 204 | char enqueueAndPlay[6]; 205 | char hardStop[6]; 206 | char sortLibrary[6]; 207 | char visualizerBrailleMode[2]; 208 | char progressBarElapsedEvenChar[12]; 209 | char progressBarElapsedOddChar[12]; 210 | char progressBarApproachingEvenChar[12]; 211 | char progressBarApproachingOddChar[12]; 212 | char progressBarCurrentEvenChar[12]; 213 | char progressBarCurrentOddChar[12]; 214 | char visualizerBarWidth[2]; 215 | char replayGainCheckFirst[2]; 216 | char saveRepeatShuffleSettings[2]; 217 | char repeatState[2]; 218 | char shuffleEnabled[2]; 219 | } AppSettings; 220 | 221 | #endif 222 | 223 | #endif 224 | -------------------------------------------------------------------------------- /src/cache.c: -------------------------------------------------------------------------------- 1 | #define _XOPEN_SOURCE 700 2 | 3 | #include 4 | #include 5 | #include 6 | #include "cache.h" 7 | /* 8 | 9 | cache.c 10 | 11 | Related to cache which contains paths to cached files. 12 | 13 | */ 14 | 15 | Cache *createCache() 16 | { 17 | Cache *cache = malloc(sizeof(Cache)); 18 | if (cache == NULL) 19 | { 20 | perror("malloc"); 21 | return NULL; 22 | } 23 | cache->head = NULL; 24 | return cache; 25 | } 26 | 27 | void addToCache(Cache *cache, const char *filePath) 28 | { 29 | if (cache == NULL) 30 | { 31 | fprintf(stderr, "Cache is null."); 32 | return; 33 | } 34 | CacheNode *newNode = malloc(sizeof(CacheNode)); 35 | if (newNode == NULL) 36 | { 37 | perror("malloc"); 38 | return; 39 | } 40 | newNode->filePath = strdup(filePath); 41 | newNode->next = cache->head; 42 | cache->head = newNode; 43 | } 44 | 45 | void deleteCache(Cache *cache) 46 | { 47 | if (cache == NULL) 48 | { 49 | fprintf(stderr, "Cache is null."); 50 | return; 51 | } 52 | CacheNode *current = cache->head; 53 | while (current != NULL) 54 | { 55 | CacheNode *tmp = current; 56 | current = current->next; 57 | free(tmp->filePath); 58 | free(tmp); 59 | } 60 | free(cache); 61 | } 62 | 63 | bool existsInCache(Cache *cache, char *filePath) 64 | { 65 | if (cache == NULL) 66 | { 67 | fprintf(stderr, "Cache is null."); 68 | return false; 69 | } 70 | CacheNode *current = cache->head; 71 | while (current != NULL) 72 | { 73 | if (strcmp(filePath, current->filePath) == 0) 74 | { 75 | return true; 76 | } 77 | 78 | current = current->next; 79 | } 80 | return false; 81 | } 82 | -------------------------------------------------------------------------------- /src/cache.h: -------------------------------------------------------------------------------- 1 | #ifndef CACHE_H 2 | #define CACHE_H 3 | 4 | #include 5 | 6 | typedef struct CacheNode 7 | { 8 | char *filePath; 9 | struct CacheNode *next; 10 | } CacheNode; 11 | 12 | typedef struct Cache 13 | { 14 | CacheNode *head; 15 | } Cache; 16 | 17 | Cache *createCache(void); 18 | 19 | void addToCache(Cache *cache, const char *filePath); 20 | 21 | void deleteCache(Cache *cache); 22 | 23 | bool existsInCache(Cache *cache, char *filePath); 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /src/common.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "common.h" 5 | 6 | const char VERSION[] = "3.3.3"; 7 | 8 | const char LAST_ROW[] = "[F2 Playlist|F3 Library|F4 Track|F5 Search|F6 Help]"; 9 | 10 | double pauseSeconds = 0.0; 11 | double totalPauseSeconds = 0.0; 12 | double seekAccumulatedSeconds = 0.0; 13 | 14 | #define ERROR_MESSAGE_LENGTH 256 15 | 16 | char currentErrorMessage[ERROR_MESSAGE_LENGTH]; 17 | 18 | bool hasPrintedError = true; 19 | 20 | volatile bool refresh = true; // Should the whole view be refreshed next time it redraws 21 | 22 | void setErrorMessage(const char *message) 23 | { 24 | strncpy(currentErrorMessage, message, ERROR_MESSAGE_LENGTH - 1); 25 | currentErrorMessage[ERROR_MESSAGE_LENGTH - 1] = '\0'; 26 | hasPrintedError = false; 27 | refresh = true; 28 | } 29 | 30 | bool hasErrorMessage() 31 | { 32 | return (currentErrorMessage[0] != '\0'); 33 | } 34 | 35 | char *getErrorMessage() 36 | { 37 | return currentErrorMessage; 38 | } 39 | 40 | void clearErrorMessage() 41 | { 42 | currentErrorMessage[0] = '\0'; 43 | } 44 | -------------------------------------------------------------------------------- /src/common.h: -------------------------------------------------------------------------------- 1 | #ifndef COMMON_H 2 | #define COMMON_H 3 | 4 | #include 5 | 6 | #ifndef MAXPATHLEN 7 | #define MAXPATHLEN 4096 8 | #endif 9 | 10 | typedef enum 11 | { 12 | k_unknown = 0, 13 | k_aac = 1, 14 | k_rawAAC = 2, // Raw aac (.aac file) decoding is included here for convenience although they are not .m4a files 15 | k_ALAC = 3, 16 | k_FLAC = 4 17 | } k_m4adec_filetype; 18 | 19 | extern volatile bool refresh; 20 | 21 | extern double pauseSeconds; 22 | extern double totalPauseSeconds; 23 | extern double seekAccumulatedSeconds; 24 | 25 | extern const char VERSION[]; 26 | 27 | extern const char LAST_ROW[]; 28 | 29 | extern bool hasPrintedError; 30 | 31 | void setErrorMessage(const char *message); 32 | 33 | bool hasErrorMessage(); 34 | 35 | char *getErrorMessage(); 36 | 37 | void clearErrorMessage(); 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /src/common_ui.h: -------------------------------------------------------------------------------- 1 | #ifndef COMMON_UI_H 2 | #define COMMON_UI_H 3 | #include 4 | #include "appstate.h" 5 | 6 | extern unsigned int updateCounter; 7 | 8 | extern const int scrollingInterval; 9 | extern bool isSameNameAsLastTime; 10 | 11 | void setTextColorRGB2(int r, int g, int b, const UISettings *ui); 12 | 13 | void setColor(UISettings *ui); 14 | 15 | void setColorAndWeight(int bold, PixelData color, int useConfigColors); 16 | 17 | void processNameScroll(const char *name, char *output, int maxWidth, bool isSameNameAsLastTime); 18 | 19 | void resetNameScroll(); 20 | 21 | bool getIsLongName(); 22 | 23 | void processName(const char *name, char *output, int maxWidth); 24 | 25 | PixelData increaseLuminosity(PixelData pixel, int amount); 26 | 27 | PixelData decreaseLuminosityPct(PixelData base, float pct); 28 | 29 | PixelData getGradientColor(PixelData baseColor, int row, int maxListSize, int startGradient, float minPct); 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /src/directorytree.h: -------------------------------------------------------------------------------- 1 | #ifndef DIRECTORYTREE_H 2 | #define DIRECTORYTREE_H 3 | 4 | #include 5 | 6 | 7 | #ifndef PATH_MAX 8 | #define PATH_MAX 4096 9 | #endif 10 | #ifndef FILE_SYSTEM_ENTRY 11 | #define FILE_SYSTEM_ENTRY 12 | typedef struct FileSystemEntry 13 | { 14 | int id; 15 | char *name; 16 | char *fullPath; 17 | int isDirectory; // 1 for directory, 0 for file 18 | int isEnqueued; 19 | int parentId; 20 | struct FileSystemEntry *parent; 21 | struct FileSystemEntry *children; 22 | struct FileSystemEntry *next; // For siblings (next node in the same directory) 23 | } FileSystemEntry; 24 | #endif 25 | 26 | #ifndef SLOWLOADING_CALLBACK 27 | #define SLOWLOADING_CALLBACK 28 | typedef void (*SlowloadingCallback)(void); 29 | #endif 30 | 31 | FileSystemEntry *createDirectoryTree(const char *startPath, int *numEntries); 32 | 33 | void freeTree(FileSystemEntry *root); 34 | 35 | void freeAndWriteTree(FileSystemEntry *root, const char *filename); 36 | 37 | FileSystemEntry *reconstructTreeFromFile(const char *filename, const char *startMusicPath, int *numDirectoryEntries); 38 | 39 | void fuzzySearchRecursive(FileSystemEntry *node, const char *searchTerm, int threshold, void (*callback)(FileSystemEntry *, int)); 40 | 41 | void copyIsEnqueued(FileSystemEntry *library, FileSystemEntry *tmp); 42 | 43 | void sortFileSystemTree(FileSystemEntry *root, int (*comparator)(const void *, const void *)); 44 | 45 | int compareFoldersByAgeFilesAlphabetically(const void *a, const void *b); 46 | 47 | int compareLibEntries(const struct dirent **a, const struct dirent **b); 48 | 49 | int compareLibEntriesReversed(const struct dirent **a, const struct dirent **b); 50 | 51 | int compareEntryNaturalReversed(const void *a, const void *b); 52 | 53 | int compareEntryNatural(const void *a, const void *b); 54 | 55 | #endif 56 | -------------------------------------------------------------------------------- /src/events.h: -------------------------------------------------------------------------------- 1 | #ifndef EVENTS_H 2 | #define EVENTS_H 3 | 4 | #define MAX_SEQ_LEN 1024 // Maximum length of sequence buffer 5 | 6 | enum EventType 7 | { 8 | EVENT_NONE, 9 | EVENT_PLAY_PAUSE, 10 | EVENT_VOLUME_UP, 11 | EVENT_VOLUME_DOWN, 12 | EVENT_NEXT, 13 | EVENT_PREV, 14 | EVENT_QUIT, 15 | EVENT_TOGGLEREPEAT, 16 | EVENT_TOGGLEVISUALIZER, 17 | EVENT_TOGGLEASCII, 18 | EVENT_ADDTOMAINPLAYLIST, 19 | EVENT_DELETEFROMMAINPLAYLIST, 20 | EVENT_EXPORTPLAYLIST, 21 | EVENT_UPDATELIBRARY, 22 | EVENT_SHUFFLE, 23 | EVENT_KEY_PRESS, 24 | EVENT_SHOWKEYBINDINGS, 25 | EVENT_SHOWPLAYLIST, 26 | EVENT_SHOWSEARCH, 27 | EVENT_GOTOSONG, 28 | EVENT_GOTOBEGINNINGOFPLAYLIST, 29 | EVENT_GOTOENDOFPLAYLIST, 30 | EVENT_TOGGLEPROFILECOLORS, 31 | EVENT_SCROLLNEXT, 32 | EVENT_SCROLLPREV, 33 | EVENT_SEEKBACK, 34 | EVENT_SEEKFORWARD, 35 | EVENT_SHOWLIBRARY, 36 | EVENT_SHOWTRACK, 37 | EVENT_NEXTPAGE, 38 | EVENT_PREVPAGE, 39 | EVENT_REMOVE, 40 | EVENT_SEARCH, 41 | EVENT_NEXTVIEW, 42 | EVENT_PREVVIEW, 43 | EVENT_CLEARPLAYLIST, 44 | EVENT_MOVESONGUP, 45 | EVENT_MOVESONGDOWN, 46 | EVENT_ENQUEUEANDPLAY, 47 | EVENT_STOP, 48 | EVENT_SORTLIBRARY 49 | }; 50 | 51 | struct Event 52 | { 53 | enum EventType type; 54 | char key[MAX_SEQ_LEN]; // To store multi-byte characters 55 | }; 56 | 57 | typedef struct 58 | { 59 | char *seq; 60 | enum EventType eventType; 61 | } EventMapping; 62 | 63 | #endif 64 | -------------------------------------------------------------------------------- /src/file.h: -------------------------------------------------------------------------------- 1 | #ifndef FILE_H 2 | #define FILE_H 3 | 4 | #include 5 | 6 | #define __USE_GNU 7 | 8 | #ifndef MAXPATHLEN 9 | #define MAXPATHLEN 4096 10 | #endif 11 | 12 | #ifndef AUDIO_EXTENSIONS 13 | #define AUDIO_EXTENSIONS "(m4a|aac|mp3|ogg|flac|wav|opus|webm)$" 14 | #endif 15 | 16 | enum SearchType 17 | { 18 | SearchAny = 0, 19 | DirOnly = 1, 20 | FileOnly = 2, 21 | SearchPlayList = 3, 22 | ReturnAllSongs = 4 23 | }; 24 | 25 | void getDirectoryFromPath(const char *path, char *directory); 26 | 27 | int isDirectory(const char *path); 28 | 29 | /* Traverse a directory tree and search for a given file or directory */ 30 | int walker(const char *startPath, const char *searching, char *result, 31 | const char *allowedExtensions, enum SearchType searchType, bool exactSearch); 32 | 33 | int expandPath(const char *inputPath, char *expandedPath); 34 | 35 | int createDirectory(const char *path); 36 | 37 | int deleteFile(const char *filePath); 38 | 39 | void generateTempFilePath(char *filePath, const char *prefix, const char *suffix); 40 | 41 | int isInTempDir(const char *path); 42 | 43 | int existsFile(const char *fname); 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /src/imgfunc.h: -------------------------------------------------------------------------------- 1 | #ifndef IMGFUNC_H 2 | #define IMGFUNC_H 3 | 4 | #include 5 | #include 6 | #ifndef PIXELDATA_STRUCT 7 | #define PIXELDATA_STRUCT 8 | typedef struct 9 | { 10 | unsigned char r; 11 | unsigned char g; 12 | unsigned char b; 13 | } PixelData; 14 | #endif 15 | 16 | int printInAscii(const char *pathToImgFile, int height); 17 | 18 | float calcAspectRatio(void); 19 | 20 | unsigned char *getBitmap(const char *image_path, int *width, int *height); 21 | 22 | void printSquareBitmapCentered(unsigned char *pixels, int width, int height, int baseHeight); 23 | 24 | int getCoverColor(unsigned char *pixels, int width, int height, unsigned char *r, unsigned char *g, unsigned char *b); 25 | 26 | #ifdef CHAFA_VERSION_1_16 27 | gboolean retire_passthrough_workarounds_tmux(void); 28 | #endif 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /src/m4a.c: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | This implements a data source that handles m4a streams via minimp4 and faad2 4 | 5 | This object can be plugged into any `ma_data_source_*()` API and can also be used as a custom 6 | decoding backend. See the custom_decoder example. 7 | 8 | You need to include this file after miniaudio.h. 9 | */ 10 | 11 | #define MINIMP4_IMPLEMENTATION 12 | #include "../include/minimp4/minimp4.h" 13 | 14 | #include 15 | #ifdef USE_FAAD 16 | #include "m4a.h" 17 | #endif 18 | -------------------------------------------------------------------------------- /src/mpris.h: -------------------------------------------------------------------------------- 1 | #ifndef MPRIS_H 2 | #define MPRIS_H 3 | 4 | #include 5 | #include "playlist.h" 6 | 7 | void initMpris(void); 8 | 9 | void emitStringPropertyChanged(const gchar *propertyName, const gchar *newValue); 10 | 11 | void emitBooleanPropertyChanged(const gchar *propertyName, gboolean newValue); 12 | 13 | void emitVolumeChanged(void); 14 | 15 | void emitShuffleChanged(void); 16 | 17 | void emitMetadataChanged(const gchar *title, const gchar *artist, const gchar *album, const gchar *coverArtPath, const gchar *trackId, Node *currentSong, gint64 length); 18 | 19 | void emitStartPlayingMpris(void); 20 | 21 | void emitPlaybackStoppedMpris(void); 22 | 23 | void cleanupMpris(void); 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /src/notifications.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef NOTIFICATIONS_H 3 | #define NOTIFICATIONS_H 4 | 5 | #include "appstate.h" 6 | 7 | #ifndef PATH_MAX 8 | #define PATH_MAX 4096 9 | #endif 10 | 11 | #ifdef USE_DBUS 12 | 13 | int displaySongNotification(const char *artist, const char *title, const char *cover, UISettings *ui); 14 | 15 | #endif 16 | 17 | void freeLastCover(void); 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /src/player_ui.h: -------------------------------------------------------------------------------- 1 | #ifndef PLAYER_H 2 | #define PLAYER_H 3 | 4 | #include 5 | #include "appstate.h" 6 | #include "common.h" 7 | #include "directorytree.h" 8 | #include "soundcommon.h" 9 | 10 | 11 | #ifndef TAGSETTINGS_STRUCT 12 | #define TAGSETTINGS_STRUCT 13 | 14 | #define METADATA_MAX_LENGTH 256 15 | 16 | typedef struct 17 | { 18 | char title[METADATA_MAX_LENGTH]; 19 | char artist[METADATA_MAX_LENGTH]; 20 | char album_artist[METADATA_MAX_LENGTH]; 21 | char album[METADATA_MAX_LENGTH]; 22 | char date[METADATA_MAX_LENGTH]; 23 | double replaygainTrack; 24 | double replaygainAlbum; 25 | } TagSettings; 26 | 27 | #endif 28 | 29 | #ifndef SONGDATA_STRUCT 30 | #define SONGDATA_STRUCT 31 | typedef struct 32 | { 33 | gchar *trackId; 34 | char filePath[MAXPATHLEN]; 35 | char coverArtPath[MAXPATHLEN]; 36 | unsigned char red; 37 | unsigned char green; 38 | unsigned char blue; 39 | TagSettings *metadata; 40 | unsigned char *cover; 41 | int avgBitRate; 42 | int coverWidth; 43 | int coverHeight; 44 | double duration; 45 | bool hasErrors; 46 | } SongData; 47 | #endif 48 | 49 | 50 | extern int numProgressBars; 51 | 52 | extern bool fastForwarding; 53 | extern bool rewinding; 54 | 55 | extern FileSystemEntry *library; 56 | 57 | int printPlayer(SongData *songdata, double elapsedSeconds, AppSettings *settings, AppState *appState); 58 | 59 | void flipNextPage(void); 60 | 61 | void flipPrevPage(void); 62 | 63 | void showHelp(void); 64 | 65 | void setChosenDir(FileSystemEntry *entry); 66 | 67 | int getIndent(); 68 | 69 | int printAbout(SongData *songdata, UISettings *ui); 70 | 71 | FileSystemEntry *getCurrentLibEntry(void); 72 | 73 | FileSystemEntry *getChosenDir(void); 74 | 75 | FileSystemEntry *getLibrary(void); 76 | 77 | void scrollNext(void); 78 | 79 | void scrollPrev(void); 80 | 81 | void setCurrentAsChosenDir(void); 82 | 83 | void toggleShowView(ViewState VIEW_TO_SHOW); 84 | 85 | void showTrack(void); 86 | 87 | void freeMainDirectoryTree(AppState *state); 88 | 89 | char *getLibraryFilePath(void); 90 | 91 | void resetChosenDir(void); 92 | 93 | void switchToNextView(void); 94 | 95 | void switchToPreviousView(void); 96 | 97 | void resetSearchResult(void); 98 | 99 | int getChosenRow(void); 100 | 101 | void setChosenRow(int row); 102 | 103 | #endif 104 | -------------------------------------------------------------------------------- /src/playerops.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef PLAYEROPS_H 3 | #define PLAYEROPS_H 4 | 5 | 6 | #include "appstate.h" 7 | #include "playlist.h" 8 | #include "sound.h" 9 | #include "soundcommon.h" 10 | 11 | #ifdef USE_FAAD 12 | #include "m4a.h" 13 | #endif 14 | #ifndef CLOCK_MONOTONIC 15 | #define CLOCK_MONOTONIC 1 16 | #endif 17 | 18 | #ifndef MAXPATHLEN 19 | #define MAXPATHLEN 4096 20 | #endif 21 | 22 | typedef struct 23 | { 24 | char filePath[MAXPATHLEN]; 25 | SongData *songdataA; 26 | SongData *songdataB; 27 | bool loadA; 28 | bool loadingFirstDecoder; 29 | pthread_mutex_t mutex; 30 | } LoadingThreadData; 31 | 32 | extern GDBusConnection *connection; 33 | extern GMainContext *global_main_context; 34 | extern LoadingThreadData loadingdata; 35 | extern struct timespec start_time; 36 | extern struct timespec pause_time; 37 | extern volatile bool loadedNextSong; 38 | extern bool nextSongNeedsRebuilding; 39 | extern bool waitingForPlaylist; 40 | extern bool waitingForNext; 41 | extern bool usingSongDataA; 42 | extern Node *nextSong; 43 | extern Node *songToStartFrom; 44 | extern int lastPlayedId; 45 | extern bool songHasErrors; 46 | extern volatile bool clearingErrors; 47 | extern volatile bool songLoading; 48 | extern bool skipping; 49 | extern bool skipOutOfOrder; 50 | extern Node *tryNextSong; 51 | extern bool skipFromStopped; 52 | extern UserData userData; 53 | 54 | SongData *getCurrentSongData(void); 55 | 56 | Node *getNextSong(void); 57 | 58 | void handleRemove(void); 59 | 60 | FileSystemEntry *enqueueSongs(FileSystemEntry *entry, UIState *uis); 61 | 62 | void updateLastSongSwitchTime(void); 63 | 64 | void playbackPause(struct timespec *pause_time); 65 | 66 | void playbackPlay(double *totalPauseSeconds, double *pauseSeconds); 67 | 68 | void togglePause(double *totalPauseSeconds, double *pauseSeconds, struct timespec *pause_time); 69 | 70 | void stop(void); 71 | 72 | void toggleRepeat(UISettings *ui); 73 | 74 | void toggleShuffle(UISettings *ui); 75 | 76 | void toggleAscii(AppSettings *settings, UISettings *ui); 77 | 78 | void toggleColors(AppSettings *settings, UISettings *ui); 79 | 80 | void toggleVisualizer(AppSettings *settings, UISettings *ui); 81 | 82 | void quit(void); 83 | 84 | void calcElapsedTime(void); 85 | 86 | Node *getSongByNumber(PlayList *playlist, int songNumber); 87 | 88 | void skipToNextSong(AppState *state); 89 | 90 | void skipToPrevSong(AppState *state); 91 | 92 | void skipToSong(int id, bool startPlaying); 93 | 94 | void seekForward(UIState *uis); 95 | 96 | void seekBack(UIState *uis); 97 | 98 | void skipToNumberedSong(int songNumber); 99 | 100 | void skipToLastSong(void); 101 | 102 | void loadSong(Node *song, LoadingThreadData *loadingdata); 103 | 104 | int loadFirst(Node *song, AppState *state); 105 | 106 | void flushSeek(void); 107 | 108 | Node *findSelectedEntryById(PlayList *playlist, int id); 109 | 110 | void emitSeekedSignal(double newPositionSeconds); 111 | 112 | void rebuildNextSong(Node *song); 113 | 114 | void updateLibrary(char *path); 115 | 116 | void askIfCacheLibrary(UISettings *ui); 117 | 118 | void unloadSongA(AppState *state); 119 | 120 | void unloadSongB(AppState *state); 121 | 122 | void unloadPreviousSong(AppState *state); 123 | 124 | void createLibrary(AppSettings *settings, AppState *state); 125 | 126 | void resetClock(void); 127 | 128 | void loadNextSong(void); 129 | 130 | void setCurrentSongToNext(void); 131 | 132 | void finishLoading(void); 133 | 134 | void resetTimeCount(void); 135 | 136 | bool setPosition(gint64 newPosition); 137 | 138 | bool seekPosition(gint64 offset); 139 | 140 | void silentSwitchToNext(bool loadSong, AppState *state); 141 | 142 | void reshufflePlaylist(void); 143 | 144 | bool determineCurrentSongData(SongData **currentSongData); 145 | 146 | void updateLibraryIfChangedDetected(void); 147 | 148 | double getCurrentSongDuration(void); 149 | 150 | void updatePlaylistToPlayingSong(void); 151 | 152 | void moveSongUp(); 153 | 154 | void moveSongDown(); 155 | 156 | void play(Node *song); 157 | 158 | void repeatList(); 159 | 160 | void skipToBegginningOfSong(void); 161 | 162 | void sortLibrary(void); 163 | 164 | void markListAsEnqueued(FileSystemEntry *root, PlayList *playlist); 165 | 166 | bool isContainedWithin(FileSystemEntry *entry, FileSystemEntry *containingEntry); 167 | 168 | #endif 169 | -------------------------------------------------------------------------------- /src/playlist.h: -------------------------------------------------------------------------------- 1 | #ifndef _DEFAULT_SOURCE 2 | #define _DEFAULT_SOURCE 3 | #endif 4 | #ifndef __USE_XOPEN_EXTENDED 5 | #define __USE_XOPEN_EXTENDED 6 | #endif 7 | 8 | #include 9 | #include 10 | #include "directorytree.h" 11 | 12 | #define MAX_FILES 10000 13 | 14 | #ifndef PLAYLIST_STRUCT 15 | #define PLAYLIST_STRUCT 16 | 17 | typedef struct 18 | { 19 | char *filePath; 20 | double duration; 21 | } SongInfo; 22 | 23 | typedef struct Node 24 | { 25 | int id; 26 | SongInfo song; 27 | struct Node *next; 28 | struct Node *prev; 29 | } Node; 30 | 31 | typedef struct 32 | { 33 | Node *head; 34 | Node *tail; 35 | int count; 36 | pthread_mutex_t mutex; 37 | } PlayList; 38 | 39 | extern Node *currentSong; 40 | 41 | #endif 42 | 43 | extern PlayList playlist; 44 | extern PlayList *originalPlaylist; 45 | 46 | extern int nodeIdCounter; 47 | 48 | Node *getListNext(Node *node); 49 | 50 | Node *getListPrev(Node *node); 51 | 52 | void createNode(Node **node, const char *directoryPath, int id); 53 | 54 | void addToList(PlayList *list, Node *newNode); 55 | 56 | Node *deleteFromList(PlayList *list, Node *node); 57 | 58 | void deletePlaylist(PlayList *playlist); 59 | 60 | void shufflePlaylist(PlayList *playlist); 61 | 62 | void shufflePlaylistStartingFromSong(PlayList *playlist, Node *song); 63 | 64 | int makePlaylist(int argc, char *argv[], bool exactSearch, const char *path); 65 | 66 | void writeCurrentPlaylistToM3UFile(PlayList *playlist); 67 | 68 | void writeM3UFile(const char *filename, const PlayList *playlist); 69 | 70 | void saveNamedPlaylist(const char *directory, const char *playlistName, const PlayList *playlist); 71 | 72 | void exportCurrentPlaylist(const char *path); 73 | 74 | void saveLastUsedPlaylist(void); 75 | 76 | void loadLastUsedPlaylist(void); 77 | 78 | PlayList deepCopyPlayList(PlayList *originalList); 79 | 80 | void deepCopyPlayListOntoList(PlayList *originalList, PlayList *newList); 81 | 82 | Node *findPathInPlaylist(const char *path, PlayList *playlist); 83 | 84 | Node *findLastPathInPlaylist(const char *path, PlayList *playlist); 85 | 86 | int findNodeInList(PlayList *list, int id, Node **foundNode); 87 | 88 | void createPlayListFromFileSystemEntry(FileSystemEntry *root, PlayList *list, int playlistMax); 89 | 90 | void addShuffledAlbumsToPlayList(FileSystemEntry *root, PlayList *list, int playlistMax); 91 | 92 | void moveUpList(PlayList *list, Node *node); 93 | 94 | void moveDownList(PlayList *list, Node *node); 95 | -------------------------------------------------------------------------------- /src/playlist_ui.c: -------------------------------------------------------------------------------- 1 | #include "common_ui.h" 2 | #include "playlist_ui.h" 3 | #include "songloader.h" 4 | #include "term.h" 5 | #include "utils.h" 6 | 7 | /* 8 | 9 | playlist_ui.c 10 | 11 | Playlist UI functions. 12 | 13 | */ 14 | 15 | int startIter = 0; 16 | int previousChosenSong = 0; 17 | 18 | Node *determineStartNode(Node *head, int *foundAt, bool *startFromCurrent, int listSize) 19 | { 20 | Node *node = head; 21 | Node *foundNode = NULL; 22 | int numSongs = 0; 23 | *foundAt = -1; 24 | 25 | while (node != NULL && numSongs <= listSize) 26 | { 27 | if (currentSong != NULL && currentSong->id == node->id) 28 | { 29 | *foundAt = numSongs; 30 | foundNode = node; 31 | break; 32 | } 33 | node = node->next; 34 | numSongs++; 35 | } 36 | 37 | *startFromCurrent = (*foundAt > -1) ? true : false; 38 | return foundNode ? foundNode : head; 39 | } 40 | 41 | void preparePlaylistString(Node *node, char *buffer, int bufferSize) 42 | { 43 | if (node == NULL || buffer == NULL) 44 | { 45 | buffer[0] = '\0'; 46 | return; 47 | } 48 | 49 | char filePath[MAXPATHLEN]; 50 | c_strcpy(filePath, node->song.filePath, sizeof(filePath)); 51 | char *lastSlash = strrchr(filePath, '/'); 52 | size_t len = strnlen(filePath, sizeof(filePath)); 53 | 54 | if (lastSlash != NULL) 55 | { 56 | int nameLength = filePath + len - (lastSlash + 1); // Length of the filename 57 | nameLength = (nameLength < bufferSize - 1) ? nameLength : bufferSize - 1; 58 | 59 | c_strcpy(buffer, lastSlash + 1, nameLength + 1); 60 | buffer[nameLength] = '\0'; 61 | } 62 | else 63 | { 64 | buffer[0] = '\0'; 65 | } 66 | } 67 | 68 | int displayPlaylistItems(Node *startNode, int startIter, int maxListSize, int termWidth, int indent, int chosenSong, int *chosenNodeId, UISettings *ui) 69 | { 70 | int numPrintedRows = 0; 71 | Node *node = startNode; 72 | 73 | int bufferSize = termWidth - indent - 12; 74 | 75 | PixelData rowColor; 76 | rowColor.r = defaultColor; 77 | rowColor.g = defaultColor; 78 | rowColor.b = defaultColor; 79 | 80 | for (int i = startIter; node != NULL && i < startIter + maxListSize; i++) 81 | { 82 | if (!(ui->color.r == defaultColor && ui->color.g == defaultColor && ui->color.b == defaultColor)) 83 | rowColor = getGradientColor(ui->color, i - startIter, maxListSize, maxListSize / 2, 0.7f); 84 | 85 | char *buffer = (char *)malloc(MAXPATHLEN * sizeof(char)); 86 | char *filename = (char *)malloc(MAXPATHLEN * sizeof(char) + 1); 87 | 88 | if (!buffer || !filename) 89 | { 90 | 91 | return 0; 92 | } 93 | preparePlaylistString(node, buffer, MAXPATHLEN); 94 | 95 | if (buffer[0] != '\0') 96 | { 97 | if (ui->useConfigColors) 98 | setTextColor(ui->artistColor); 99 | else 100 | setColorAndWeight(0, rowColor, ui->useConfigColors); 101 | 102 | printBlankSpaces(indent); 103 | 104 | printf(" %d. ", i + 1); 105 | 106 | setDefaultTextColor(); 107 | 108 | isSameNameAsLastTime = (previousChosenSong == chosenSong); 109 | 110 | if (!isSameNameAsLastTime) 111 | { 112 | resetNameScroll(); 113 | } 114 | 115 | filename[0] = '\0'; 116 | 117 | if (i == chosenSong) 118 | { 119 | previousChosenSong = chosenSong; 120 | 121 | *chosenNodeId = node->id; 122 | 123 | processNameScroll(buffer, filename, bufferSize, isSameNameAsLastTime); 124 | 125 | printf("\x1b[7m"); 126 | } 127 | else 128 | { 129 | processName(buffer, filename, bufferSize); 130 | } 131 | 132 | if (i + 1 < 10) 133 | printf(" "); 134 | 135 | if (currentSong != NULL && currentSong->id == node->id) 136 | { 137 | printf("\e[4m\e[1m"); 138 | } 139 | 140 | printf("%s\n", filename); 141 | 142 | numPrintedRows++; 143 | } 144 | 145 | free(buffer); 146 | free(filename); 147 | 148 | node = node->next; 149 | } 150 | 151 | return numPrintedRows; 152 | } 153 | 154 | int displayPlaylist(PlayList *list, int maxListSize, int indent, int *chosenSong, int *chosenNodeId, bool reset, AppState *state) 155 | { 156 | int termWidth, termHeight; 157 | getTermSize(&termWidth, &termHeight); 158 | 159 | UISettings *ui = &(state->uiSettings); 160 | 161 | int foundAt = -1; 162 | bool startFromCurrent = false; 163 | Node *startNode = determineStartNode(list->head, &foundAt, &startFromCurrent, list->count); 164 | 165 | // Determine chosen song 166 | if (*chosenSong >= list->count) 167 | { 168 | *chosenSong = list->count - 1; 169 | } 170 | 171 | if (*chosenSong < 0) 172 | { 173 | *chosenSong = 0; 174 | } 175 | 176 | int startIter = 0; 177 | 178 | // Determine where to start iterating 179 | startIter = (startFromCurrent && (foundAt < startIter || foundAt > startIter + maxListSize)) ? foundAt : startIter; 180 | 181 | if (*chosenSong < startIter) 182 | { 183 | startIter = *chosenSong; 184 | } 185 | 186 | if (*chosenSong > startIter + maxListSize - round(maxListSize / 2)) 187 | { 188 | startIter = *chosenSong - maxListSize + round(maxListSize / 2); 189 | } 190 | 191 | if (reset && !audioData.endOfListReached) 192 | { 193 | startIter = *chosenSong = foundAt; 194 | } 195 | 196 | // Go up to find the starting node 197 | for (int i = foundAt; i > startIter; i--) 198 | { 199 | if (i > 0 && startNode->prev != NULL) 200 | startNode = startNode->prev; 201 | } 202 | 203 | // Go down to adjust the startNode 204 | for (int i = (foundAt == -1) ? 0 : foundAt; i < startIter; i++) 205 | { 206 | if (startNode->next != NULL) 207 | startNode = startNode->next; 208 | } 209 | 210 | int printedRows = displayPlaylistItems(startNode, startIter, maxListSize, termWidth, indent, *chosenSong, chosenNodeId, ui); 211 | 212 | while (printedRows < maxListSize) 213 | { 214 | printf("\n"); 215 | printedRows++; 216 | } 217 | 218 | return printedRows; 219 | } 220 | -------------------------------------------------------------------------------- /src/playlist_ui.h: -------------------------------------------------------------------------------- 1 | #ifndef PLAYLIST_UI_H 2 | #define PLAYLIST_UI_H 3 | 4 | #include "appstate.h" 5 | #include "playlist.h" 6 | #include "soundcommon.h" 7 | 8 | int displayPlaylist(PlayList *list, int maxListSize, int indent, int *chosenSong, int *chosenNodeId, bool reset, AppState *state); 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /src/search_ui.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "soundcommon.h" 4 | #include "term.h" 5 | #include "common_ui.h" 6 | #include "common.h" 7 | #include "search_ui.h" 8 | 9 | /* 10 | 11 | search_ui.c 12 | 13 | Search UI functions. 14 | 15 | */ 16 | 17 | #define MAX_SEARCH_LEN 32 18 | 19 | int numSearchLetters = 0; 20 | int numSearchBytes = 0; 21 | 22 | typedef struct SearchResult 23 | { 24 | FileSystemEntry *entry; 25 | int distance; 26 | } SearchResult; 27 | 28 | // Global variables to store results 29 | SearchResult *results = NULL; 30 | size_t resultsCount = 0; 31 | size_t resultsCapacity = 0; 32 | 33 | int minSearchLetters = 1; 34 | FileSystemEntry *currentSearchEntry = NULL; 35 | 36 | char searchText[MAX_SEARCH_LEN * 4 + 1]; // Unicode can be 4 characters 37 | 38 | FileSystemEntry *getCurrentSearchEntry(void) 39 | { 40 | return currentSearchEntry; 41 | } 42 | 43 | int getSearchResultsCount(void) 44 | { 45 | return resultsCount; 46 | } 47 | 48 | // Function to add a result to the global array 49 | void addResult(FileSystemEntry *entry, int distance) 50 | { 51 | if (resultsCount >= resultsCapacity) 52 | { 53 | resultsCapacity = resultsCapacity == 0 ? 10 : resultsCapacity * 2; 54 | results = realloc(results, resultsCapacity * sizeof(SearchResult)); 55 | } 56 | results[resultsCount].distance = distance; 57 | results[resultsCount].entry = entry; 58 | resultsCount++; 59 | } 60 | 61 | // Callback function to collect results 62 | void collectResult(FileSystemEntry *entry, int distance) 63 | { 64 | addResult(entry, distance); 65 | } 66 | 67 | // Free allocated memory from previous search 68 | void freeSearchResults(void) 69 | { 70 | if (results != NULL) 71 | { 72 | free(results); 73 | results = NULL; 74 | } 75 | 76 | if (currentSearchEntry != NULL) 77 | currentSearchEntry = NULL; 78 | 79 | resultsCapacity = 0; 80 | resultsCount = 0; 81 | } 82 | 83 | void fuzzySearch(FileSystemEntry *root, int threshold) 84 | { 85 | freeSearchResults(); 86 | 87 | if (numSearchLetters > minSearchLetters) 88 | { 89 | fuzzySearchRecursive(root, searchText, threshold, collectResult); 90 | } 91 | refresh = true; 92 | } 93 | 94 | int compareResults(const void *a, const void *b) 95 | { 96 | SearchResult *resultA = (SearchResult *)a; 97 | SearchResult *resultB = (SearchResult *)b; 98 | return resultA->distance - resultB->distance; 99 | } 100 | 101 | void sortResults(void) 102 | { 103 | qsort(results, resultsCount, sizeof(SearchResult), compareResults); 104 | } 105 | 106 | int displaySearchBox(int indent, UISettings *ui) 107 | { 108 | if (ui->useConfigColors) 109 | setTextColor(ui->mainColor); 110 | else 111 | setColor(ui); 112 | 113 | printBlankSpaces(indent); 114 | printf(" [Search]: "); 115 | setDefaultTextColor(); 116 | // Save cursor position 117 | printf("%s", searchText); 118 | printf("\033[s"); 119 | printf("█\n"); 120 | 121 | return 0; 122 | } 123 | 124 | int addToSearchText(const char *str) 125 | { 126 | if (str == NULL) 127 | { 128 | return -1; 129 | } 130 | 131 | size_t len = strnlen(str, MAX_SEARCH_LEN); 132 | 133 | // Check if the string can fit into the search text buffer 134 | if (numSearchLetters + len > MAX_SEARCH_LEN) 135 | { 136 | return 0; // Not enough space 137 | } 138 | 139 | // Restore cursor position 140 | printf("\033[u"); 141 | 142 | // Print the string 143 | printf("%s", str); 144 | 145 | // Save cursor position 146 | printf("\033[s"); 147 | 148 | printf("█\n"); 149 | 150 | // Add the string to the search text buffer 151 | for (size_t i = 0; i < len; i++) 152 | { 153 | searchText[numSearchBytes++] = str[i]; 154 | } 155 | 156 | searchText[numSearchBytes] = '\0'; // Null-terminate the buffer 157 | 158 | numSearchLetters++; 159 | 160 | return 0; 161 | } 162 | 163 | // Determine the number of bytes in the last UTF-8 character 164 | int getLastCharBytes(const char *str, int len) 165 | { 166 | if (len == 0) 167 | return 0; 168 | 169 | int i = len - 1; 170 | while (i >= 0 && (str[i] & 0xC0) == 0x80) 171 | { 172 | i--; 173 | } 174 | return len - i; 175 | } 176 | 177 | // Remove the preceding character from the search text 178 | int removeFromSearchText(void) 179 | { 180 | if (numSearchLetters == 0) 181 | return 0; 182 | 183 | // Determine the number of bytes to remove for the last character 184 | int lastCharBytes = getLastCharBytes(searchText, numSearchBytes); 185 | if (lastCharBytes == 0) 186 | return 0; 187 | 188 | // Restore cursor position 189 | printf("\033[u"); 190 | 191 | // Move cursor back one step 192 | printf("\033[D"); 193 | 194 | // Overwrite the character with spaces 195 | for (int i = 0; i < lastCharBytes; i++) 196 | { 197 | printf(" "); 198 | } 199 | 200 | // Move cursor back again to the original position 201 | for (int i = 0; i < lastCharBytes; i++) 202 | { 203 | printf("\033[D"); 204 | } 205 | 206 | // Save cursor position 207 | printf("\033[s"); 208 | 209 | // Print a block character to represent the cursor 210 | printf("█"); 211 | 212 | // Clear the end of the line 213 | printf("\033[K"); 214 | 215 | fflush(stdout); 216 | 217 | // Remove the character from the buffer 218 | numSearchBytes -= lastCharBytes; 219 | searchText[numSearchBytes] = '\0'; 220 | 221 | numSearchLetters--; 222 | 223 | return 0; 224 | } 225 | 226 | int displaySearchResults(int maxListSize, int indent, int *chosenRow, int startSearchIter, UISettings *ui) 227 | { 228 | int term_w, term_h; 229 | getTermSize(&term_w, &term_h); 230 | 231 | int maxNameWidth = term_w - indent - 5; 232 | char name[maxNameWidth + 1]; 233 | int printedRows = 0; 234 | 235 | sortResults(); 236 | 237 | if (*chosenRow >= (int)resultsCount - 1) 238 | { 239 | *chosenRow = resultsCount - 1; 240 | } 241 | 242 | if (startSearchIter < 0) 243 | startSearchIter = 0; 244 | 245 | if (*chosenRow > startSearchIter + round(maxListSize / 2)) 246 | { 247 | startSearchIter = *chosenRow - round(maxListSize / 2) + 1; 248 | } 249 | 250 | if (*chosenRow < startSearchIter) 251 | startSearchIter = *chosenRow; 252 | 253 | if (*chosenRow < 0) 254 | startSearchIter = *chosenRow = 0; 255 | 256 | printf("\n"); 257 | printedRows++; 258 | 259 | // Print the sorted results 260 | for (size_t i = startSearchIter; i < resultsCount; i++) 261 | { 262 | if (numSearchLetters < minSearchLetters) 263 | break; 264 | 265 | if ((int)i >= maxListSize + startSearchIter - 1) 266 | break; 267 | 268 | setDefaultTextColor(); 269 | 270 | printBlankSpaces(indent); 271 | 272 | if (*chosenRow == (int)i) 273 | { 274 | currentSearchEntry = results[i].entry; 275 | 276 | if (results[i].entry->isEnqueued) 277 | { 278 | if (ui->useConfigColors) 279 | setTextColor(ui->enqueuedColor); 280 | else 281 | setColor(ui); 282 | printf("\x1b[7m * "); 283 | } 284 | else 285 | { 286 | printf(" \x1b[7m "); 287 | } 288 | } 289 | else 290 | { 291 | if (results[i].entry->isEnqueued) 292 | { 293 | if (ui->useConfigColors) 294 | setTextColor(ui->enqueuedColor); 295 | else 296 | setColor(ui); 297 | printf(" * "); 298 | } 299 | else 300 | printf(" "); 301 | } 302 | 303 | 304 | name[0] = '\0'; 305 | if (results[i].entry->isDirectory) 306 | { 307 | if (results[i].entry->parent != NULL && strcmp(results[i].entry->parent->name, "root") != 0) 308 | snprintf(name, maxNameWidth + 1, "[%s] (%s)", results[i].entry->name, results[i].entry->parent->name); 309 | else 310 | snprintf(name, maxNameWidth + 1, "[%s]", results[i].entry->name); 311 | 312 | } 313 | else 314 | { 315 | if (results[i].entry->parent != NULL && strcmp(results[i].entry->parent->name, "root") != 0) 316 | snprintf(name, maxNameWidth + 1, "%s (%s)", results[i].entry->name, results[i].entry->parent->name); 317 | else 318 | snprintf(name, maxNameWidth + 1, "%s", results[i].entry->name); 319 | } 320 | printf("%s\n", name); 321 | printedRows++; 322 | } 323 | 324 | while (printedRows < maxListSize) 325 | { 326 | printf("\n"); 327 | printedRows++; 328 | } 329 | 330 | return 0; 331 | } 332 | 333 | int displaySearch(int maxListSize, int indent, int *chosenRow, int startSearchIter, UISettings *ui) 334 | { 335 | displaySearchBox(indent, ui); 336 | displaySearchResults(maxListSize, indent, chosenRow, startSearchIter, ui); 337 | 338 | return 0; 339 | } 340 | -------------------------------------------------------------------------------- /src/search_ui.h: -------------------------------------------------------------------------------- 1 | #include "appstate.h" 2 | #include "directorytree.h" 3 | 4 | int displaySearch(int maxListSize, int indent, int *chosenRow, int startSearchIter, UISettings *ui); 5 | 6 | int addToSearchText(const char *str); 7 | 8 | int removeFromSearchText(void); 9 | 10 | int getSearchResultsCount(void); 11 | 12 | void fuzzySearch(FileSystemEntry *root, int threshold); 13 | 14 | void freeSearchResults(void); 15 | 16 | FileSystemEntry *getCurrentSearchEntry(void); 17 | -------------------------------------------------------------------------------- /src/settings.h: -------------------------------------------------------------------------------- 1 | #ifndef SETTINGS_H 2 | #define SETTINGS_H 3 | 4 | #include "events.h" 5 | #include "appstate.h" 6 | 7 | 8 | #ifndef MAXPATHLEN 9 | #define MAXPATHLEN 4096 10 | #endif 11 | 12 | #ifndef NUM_KEY_MAPPINGS 13 | #define NUM_KEY_MAPPINGS 61 14 | #endif 15 | 16 | extern AppSettings settings; 17 | 18 | void getConfig(AppSettings *settings, UISettings *ui); 19 | 20 | void setConfig(AppSettings *settings, UISettings *ui); 21 | 22 | void mapSettingsToKeys(AppSettings *settings, UISettings *ui, EventMapping *mappings); 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /src/songloader.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "tagLibWrapper.h" 6 | #include "cache.h" 7 | #include "imgfunc.h" 8 | #include "file.h" 9 | #include "sound.h" 10 | #include "soundcommon.h" 11 | #include "utils.h" 12 | #include "songloader.h" 13 | #include "stb_image.h" 14 | /* 15 | 16 | songloader.c 17 | 18 | This file should contain only functions related to loading song data. 19 | 20 | */ 21 | 22 | #ifndef MAXPATHLEN 23 | #define MAXPATHLEN 4096 24 | #endif 25 | 26 | static guint track_counter = 0; 27 | 28 | char *findLargestImageFile(const char *directoryPath, char *largestImageFile, off_t *largestFileSize) 29 | { 30 | DIR *directory = opendir(directoryPath); 31 | struct dirent *entry; 32 | struct stat fileStats; 33 | 34 | if (directory == NULL) 35 | { 36 | fprintf(stderr, "Failed to open directory: %s\n", directoryPath); 37 | return largestImageFile; 38 | } 39 | 40 | while ((entry = readdir(directory)) != NULL) 41 | { 42 | char filePath[MAXPATHLEN]; 43 | 44 | if (directoryPath[strnlen(directoryPath, MAXPATHLEN) - 1] == '/') 45 | { 46 | snprintf(filePath, sizeof(filePath), "%s%s", directoryPath, entry->d_name); 47 | } 48 | else 49 | { 50 | snprintf(filePath, sizeof(filePath), "%s/%s", directoryPath, entry->d_name); 51 | } 52 | 53 | if (stat(filePath, &fileStats) == -1) 54 | { 55 | continue; 56 | } 57 | 58 | if (S_ISREG(fileStats.st_mode)) 59 | { 60 | // Check if the entry is an image file and has a larger size than the current largest image file 61 | char *extension = strrchr(entry->d_name, '.'); 62 | if (extension != NULL && (strcasecmp(extension, ".jpg") == 0 || strcasecmp(extension, ".jpeg") == 0 || 63 | strcasecmp(extension, ".png") == 0 || strcasecmp(extension, ".gif") == 0)) 64 | { 65 | if (fileStats.st_size > *largestFileSize) 66 | { 67 | *largestFileSize = fileStats.st_size; 68 | if (largestImageFile != NULL) 69 | { 70 | free(largestImageFile); 71 | } 72 | largestImageFile = strdup(filePath); 73 | } 74 | } 75 | } 76 | } 77 | 78 | closedir(directory); 79 | return largestImageFile; 80 | } 81 | 82 | // Generate a new track ID 83 | gchar *generateTrackId(void) 84 | { 85 | gchar *trackId = g_strdup_printf("/org/kew/tracklist/track%d", track_counter); 86 | track_counter++; 87 | return trackId; 88 | } 89 | 90 | void loadColor(SongData *songdata) 91 | { 92 | getCoverColor(songdata->cover, songdata->coverWidth, songdata->coverHeight, &(songdata->red), &(songdata->green), &(songdata->blue)); 93 | } 94 | 95 | void loadMetaData(SongData *songdata, AppState *state) 96 | { 97 | char path[MAXPATHLEN]; 98 | 99 | songdata->metadata = malloc(sizeof(TagSettings)); 100 | songdata->metadata->replaygainTrack = 0.0; 101 | songdata->metadata->replaygainAlbum = 0.0; 102 | 103 | generateTempFilePath(songdata->coverArtPath, "cover", ".jpg"); 104 | 105 | int res = extractTags(songdata->filePath, songdata->metadata, &(songdata->duration), songdata->coverArtPath); 106 | 107 | if (res == -2) 108 | { 109 | songdata->hasErrors = true; 110 | return; 111 | } 112 | else if (res == -1) 113 | { 114 | getDirectoryFromPath(songdata->filePath, path); 115 | char *tmp = NULL; 116 | off_t size = 0; 117 | tmp = findLargestImageFile(path, tmp, &size); 118 | 119 | if (tmp != NULL) 120 | { 121 | c_strcpy(songdata->coverArtPath, tmp, sizeof(songdata->coverArtPath)); 122 | free(tmp); 123 | tmp = NULL; 124 | } 125 | else 126 | c_strcpy(songdata->coverArtPath, "", sizeof(songdata->coverArtPath)); 127 | } 128 | else 129 | { 130 | addToCache(state->tmpCache, songdata->coverArtPath); 131 | } 132 | 133 | songdata->cover = getBitmap(songdata->coverArtPath, &(songdata->coverWidth), &(songdata->coverHeight)); 134 | } 135 | 136 | SongData *loadSongData(char *filePath, AppState *state) 137 | { 138 | SongData *songdata = NULL; 139 | songdata = malloc(sizeof(SongData)); 140 | songdata->trackId = generateTrackId(); 141 | songdata->hasErrors = false; 142 | c_strcpy(songdata->filePath, "", sizeof(songdata->filePath)); 143 | c_strcpy(songdata->coverArtPath, "", sizeof(songdata->coverArtPath)); 144 | songdata->red = defaultColor; 145 | songdata->green = defaultColor; 146 | songdata->blue = defaultColor; 147 | songdata->metadata = NULL; 148 | songdata->cover = NULL; 149 | songdata->duration = 0.0; 150 | songdata->avgBitRate = 0; 151 | c_strcpy(songdata->filePath, filePath, sizeof(songdata->filePath)); 152 | loadMetaData(songdata, state); 153 | loadColor(songdata); 154 | return songdata; 155 | } 156 | 157 | void unloadSongData(SongData **songdata, AppState *state) 158 | { 159 | if (*songdata == NULL) 160 | return; 161 | 162 | SongData *data = *songdata; 163 | 164 | if (data->cover != NULL) 165 | { 166 | stbi_image_free(data->cover); 167 | data->cover = NULL; 168 | } 169 | 170 | if (existsInCache(state->tmpCache, data->coverArtPath) && isInTempDir(data->coverArtPath)) 171 | { 172 | deleteFile(data->coverArtPath); 173 | } 174 | 175 | free(data->metadata); 176 | free(data->trackId); 177 | 178 | data->cover = NULL; 179 | data->metadata = NULL; 180 | 181 | data->trackId = NULL; 182 | 183 | free(*songdata); 184 | *songdata = NULL; 185 | } 186 | -------------------------------------------------------------------------------- /src/songloader.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include "appstate.h" 3 | 4 | #ifndef MAXPATHLEN 5 | #define MAXPATHLEN 4096 6 | #endif 7 | 8 | #ifndef KEYVALUEPAIR_STRUCT 9 | #define KEYVALUEPAIR_STRUCT 10 | 11 | typedef struct 12 | { 13 | char *key; 14 | char *value; 15 | } KeyValuePair; 16 | 17 | #endif 18 | 19 | #ifndef TAGSETTINGS_STRUCT 20 | #define TAGSETTINGS_STRUCT 21 | 22 | #define METADATA_MAX_LENGTH 256 23 | 24 | typedef struct 25 | { 26 | char title[METADATA_MAX_LENGTH]; 27 | char artist[METADATA_MAX_LENGTH]; 28 | char album_artist[METADATA_MAX_LENGTH]; 29 | char album[METADATA_MAX_LENGTH]; 30 | char date[METADATA_MAX_LENGTH]; 31 | double replaygainTrack; 32 | double replaygainAlbum; 33 | } TagSettings; 34 | 35 | #endif 36 | 37 | #ifndef SONGDATA_STRUCT 38 | #define SONGDATA_STRUCT 39 | 40 | typedef struct 41 | { 42 | gchar *trackId; 43 | char filePath[MAXPATHLEN]; 44 | char coverArtPath[MAXPATHLEN]; 45 | unsigned char red; 46 | unsigned char green; 47 | unsigned char blue; 48 | TagSettings *metadata; 49 | unsigned char *cover; 50 | int avgBitRate; 51 | int coverWidth; 52 | int coverHeight; 53 | double duration; 54 | bool hasErrors; 55 | } SongData; 56 | 57 | #endif 58 | 59 | SongData *loadSongData(char *filePath, AppState *state); 60 | 61 | void unloadSongData(SongData **songdata, AppState *state); 62 | -------------------------------------------------------------------------------- /src/sound.h: -------------------------------------------------------------------------------- 1 | #ifndef SOUND_H 2 | #define SOUND_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include "file.h" 16 | #include "songloader.h" 17 | #include "soundbuiltin.h" 18 | #include "soundcommon.h" 19 | #include "common.h" 20 | 21 | #ifndef USERDATA_STRUCT 22 | #define USERDATA_STRUCT 23 | typedef struct 24 | { 25 | SongData *songdataA; 26 | SongData *songdataB; 27 | bool songdataADeleted; 28 | bool songdataBDeleted; 29 | SongData *currentSongData; 30 | ma_uint32 currentPCMFrame; 31 | } UserData; 32 | #endif 33 | 34 | #ifndef AUDIODATA_STRUCT 35 | #define AUDIODATA_STRUCT 36 | typedef struct 37 | { 38 | ma_data_source_base base; 39 | UserData *pUserData; 40 | ma_format format; 41 | ma_uint32 channels; 42 | ma_uint32 sampleRate; 43 | ma_uint32 currentPCMFrame; 44 | ma_uint32 avgBitRate; 45 | bool switchFiles; 46 | int currentFileIndex; 47 | ma_uint64 totalFrames; 48 | bool endOfListReached; 49 | bool restart; 50 | } AudioData; 51 | #endif 52 | 53 | extern UserData userData; 54 | 55 | extern bool isContextInitialized; 56 | 57 | int createAudioDevice(); 58 | 59 | int switchAudioImplementation(void); 60 | 61 | void resumePlayback(void); 62 | 63 | void cleanupAudioContext(void); 64 | 65 | #endif 66 | -------------------------------------------------------------------------------- /src/soundbuiltin.h: -------------------------------------------------------------------------------- 1 | #ifndef SOUNDBUILTIN_H 2 | #define SOUNDBUILTIN_H 3 | 4 | #include "soundcommon.h" 5 | 6 | extern ma_data_source_vtable builtin_file_data_source_vtable; 7 | 8 | void builtin_read_pcm_frames(ma_data_source *pDataSource, void *pFramesOut, ma_uint64 frameCount, ma_uint64 *pFramesRead); 9 | 10 | void builtin_on_audio_frames(ma_device *pDevice, void *pFramesOut, const void *pFramesIn, ma_uint32 frameCount); 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /src/soundcommon.h: -------------------------------------------------------------------------------- 1 | #ifndef SOUND_COMMON_H 2 | #define SOUND_COMMON_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #ifdef USE_FAAD 14 | #include "m4a.h" 15 | #endif 16 | 17 | #include 18 | #include 19 | #include 20 | #include "appstate.h" 21 | #include "file.h" 22 | #include "utils.h" 23 | #include "webm.h" 24 | 25 | #ifndef MAXPATHLEN 26 | #define MAXPATHLEN 4096 27 | #endif 28 | 29 | #ifndef MAX_BUFFER_SIZE 30 | #define MAX_BUFFER_SIZE 32768 31 | #endif 32 | 33 | #ifndef MAX_DECODERS 34 | #define MAX_DECODERS 2 35 | #endif 36 | 37 | #ifndef TAGSETTINGS_STRUCT 38 | #define TAGSETTINGS_STRUCT 39 | 40 | #define METADATA_MAX_LENGTH 256 41 | 42 | typedef struct 43 | { 44 | char title[METADATA_MAX_LENGTH]; 45 | char artist[METADATA_MAX_LENGTH]; 46 | char album_artist[METADATA_MAX_LENGTH]; 47 | char album[METADATA_MAX_LENGTH]; 48 | char date[METADATA_MAX_LENGTH]; 49 | double replaygainTrack; 50 | double replaygainAlbum; 51 | } TagSettings; 52 | 53 | #endif 54 | 55 | #ifndef SONGDATA_STRUCT 56 | #define SONGDATA_STRUCT 57 | typedef struct 58 | { 59 | gchar *trackId; 60 | char filePath[MAXPATHLEN]; 61 | char coverArtPath[MAXPATHLEN]; 62 | unsigned char red; 63 | unsigned char green; 64 | unsigned char blue; 65 | TagSettings *metadata; 66 | unsigned char *cover; 67 | int avgBitRate; 68 | int coverWidth; 69 | int coverHeight; 70 | double duration; 71 | bool hasErrors; 72 | } SongData; 73 | #endif 74 | 75 | #ifndef USERDATA_STRUCT 76 | #define USERDATA_STRUCT 77 | typedef struct 78 | { 79 | SongData *songdataA; 80 | SongData *songdataB; 81 | bool songdataADeleted; 82 | bool songdataBDeleted; 83 | int replayGainCheckFirst; 84 | SongData *currentSongData; 85 | ma_uint32 currentPCMFrame; 86 | } UserData; 87 | #endif 88 | 89 | #ifndef AUDIODATA_STRUCT 90 | #define AUDIODATA_STRUCT 91 | typedef struct 92 | { 93 | ma_data_source_base base; 94 | UserData *pUserData; 95 | ma_format format; 96 | ma_uint32 channels; 97 | ma_uint32 sampleRate; 98 | ma_uint32 currentPCMFrame; 99 | ma_uint32 avgBitRate; 100 | bool switchFiles; 101 | int currentFileIndex; 102 | ma_uint64 totalFrames; 103 | bool endOfListReached; 104 | bool restart; 105 | } AudioData; 106 | #endif 107 | 108 | enum AudioImplementation 109 | { 110 | PCM, 111 | BUILTIN, 112 | VORBIS, 113 | OPUS, 114 | M4A, 115 | WEBM, 116 | NONE 117 | }; 118 | 119 | struct m4a_decoder; 120 | typedef struct m4a_decoder m4a_decoder; 121 | 122 | extern int hopSize; 123 | extern int fftSize; 124 | extern int prevFftSize; 125 | 126 | typedef void (*uninit_func)(void *decoder); 127 | 128 | extern AppState appState; 129 | 130 | extern AudioData audioData; 131 | 132 | extern bool bufferReady; 133 | 134 | extern double elapsedSeconds; 135 | 136 | extern bool hasSilentlySwitched; 137 | 138 | extern pthread_mutex_t dataSourceMutex; 139 | 140 | extern pthread_mutex_t switchMutex; 141 | 142 | extern bool paused; 143 | 144 | extern bool stopped; 145 | 146 | extern ma_device device; 147 | 148 | enum AudioImplementation getCurrentImplementationType(); 149 | 150 | void setCurrentImplementationType(enum AudioImplementation value); 151 | 152 | int getBufferSize(void); 153 | 154 | void setBufferSize(int value); 155 | 156 | void setPlayingStatus(bool playing); 157 | 158 | bool isPlaying(void); 159 | 160 | ma_decoder *getFirstDecoder(void); 161 | 162 | ma_decoder *getCurrentBuiltinDecoder(void); 163 | 164 | ma_decoder *getPreviousDecoder(void); 165 | 166 | void getCurrentFormatAndSampleRate(ma_format *format, ma_uint32 *sampleRate); 167 | 168 | void resetAllDecoders(); 169 | 170 | ma_libopus *getCurrentOpusDecoder(void); 171 | 172 | #ifdef USE_FAAD 173 | m4a_decoder *getCurrentM4aDecoder(void); 174 | 175 | m4a_decoder *getFirstM4aDecoder(void); 176 | 177 | void getM4aFileInfo(const char *filename, ma_format *format, ma_uint32 *channels, ma_uint32 *sampleRate, ma_channel *channelMap, int *avgBitRate, k_m4adec_filetype *fileType); 178 | #endif 179 | 180 | ma_libopus *getFirstOpusDecoder(void); 181 | 182 | ma_libvorbis *getFirstVorbisDecoder(void); 183 | 184 | void getVorbisFileInfo(const char *filename, ma_format *format, ma_uint32 *channels, ma_uint32 *sampleRate, ma_channel *channelMap); 185 | 186 | void getOpusFileInfo(const char *filename, ma_format *format, ma_uint32 *channels, ma_uint32 *sampleRate, ma_channel *channelMap); 187 | 188 | ma_libvorbis *getCurrentVorbisDecoder(void); 189 | 190 | void switchVorbisDecoder(void); 191 | 192 | int prepareNextDecoder(char *filepath); 193 | 194 | int prepareNextOpusDecoder(char *filepath); 195 | 196 | int prepareNextVorbisDecoder(char *filepath); 197 | 198 | int prepareNextM4aDecoder(SongData *songData); 199 | 200 | ma_libvorbis *getFirstVorbisDecoder(void); 201 | 202 | void getFileInfo(const char *filename, ma_uint32 *sampleRate, ma_uint32 *channels, ma_format *format); 203 | 204 | void initAudioBuffer(void); 205 | 206 | void *getAudioBuffer(void); 207 | 208 | void setAudioBuffer(void *buf, int numSamples, ma_uint32 sampleRate, ma_uint32 channels, ma_format format); 209 | 210 | int32_t unpack_s24(const ma_uint8* p); 211 | 212 | void resetAudioBuffer(void); 213 | 214 | void freeAudioBuffer(void); 215 | 216 | bool isRepeatEnabled(void); 217 | 218 | void setRepeatEnabled(bool value); 219 | 220 | bool isRepeatListEnabled(void); 221 | 222 | void setRepeatListEnabled(bool value); 223 | 224 | bool isShuffleEnabled(void); 225 | 226 | void setShuffleEnabled(bool value); 227 | 228 | bool isSkipToNext(void); 229 | 230 | void setSkipToNext(bool value); 231 | 232 | double getSeekElapsed(void); 233 | 234 | void setSeekElapsed(double value); 235 | 236 | bool isEOFReached(void); 237 | 238 | void setEOFReached(void); 239 | 240 | void setEOFNotReached(void); 241 | 242 | bool isImplSwitchReached(void); 243 | 244 | void setImplSwitchReached(void); 245 | 246 | void setImplSwitchNotReached(void); 247 | 248 | bool isPlaybackDone(void); 249 | 250 | float getSeekPercentage(void); 251 | 252 | bool isSeekRequested(void); 253 | 254 | void setSeekRequested(bool value); 255 | 256 | void seekPercentage(float percent); 257 | 258 | void resumePlayback(void); 259 | 260 | void stopPlayback(void); 261 | 262 | void pausePlayback(void); 263 | 264 | void cleanupPlaybackDevice(void); 265 | 266 | void togglePausePlayback(void); 267 | 268 | bool isPaused(void); 269 | 270 | bool isStopped(void); 271 | 272 | ma_device *getDevice(void); 273 | 274 | bool hasBuiltinDecoder(char *filePath); 275 | 276 | void setCurrentFileIndex(AudioData *pAudioData, int index); 277 | 278 | void activateSwitch(AudioData *pPCMDataSource); 279 | 280 | void executeSwitch(AudioData *pPCMDataSource); 281 | 282 | gint64 getLengthInMicroSec(double duration); 283 | 284 | int getCurrentVolume(void); 285 | 286 | void setVolume(int volume); 287 | 288 | int adjustVolumePercent(int volumeChange); 289 | 290 | void m4a_on_audio_frames(ma_device *pDevice, void *pFramesOut, const void *pFramesIn, ma_uint32 frameCount); 291 | 292 | void opus_on_audio_frames(ma_device *pDevice, void *pFramesOut, const void *pFramesIn, ma_uint32 frameCount); 293 | 294 | void vorbis_on_audio_frames(ma_device *pDevice, void *pFramesOut, const void *pFramesIn, ma_uint32 frameCount); 295 | 296 | void logTime(const char *message); 297 | 298 | void clearCurrentTrack(void); 299 | 300 | void cleanupDbusConnection(); 301 | 302 | void freeLastCover(void); 303 | 304 | void getWebmFileInfo(const char *filename, ma_format *format, ma_uint32 *channels, ma_uint32 *sampleRate, ma_channel *channelMap); 305 | 306 | int prepareNextWebmDecoder(SongData *songData); 307 | 308 | ma_webm *getCurrentWebmDecoder(void); 309 | 310 | ma_webm *getFirstWebmDecoder(void); 311 | 312 | void webm_on_audio_frames(ma_device *pDevice, void *pFramesOut, const void *pFramesIn, ma_uint32 frameCount); 313 | 314 | ma_result callReadPCMFrames( 315 | ma_data_source *pDataSource, 316 | ma_format format, 317 | void *pFramesOut, 318 | ma_uint64 framesRead, 319 | ma_uint32 channels, 320 | ma_uint64 remainingFrames, 321 | ma_uint64 *pFramesToRead); 322 | 323 | bool doesOSallowVolumeControl(); 324 | 325 | #endif 326 | -------------------------------------------------------------------------------- /src/tagLibWrapper.h: -------------------------------------------------------------------------------- 1 | // taglib_wrapper.h 2 | #ifndef TAGLIB_WRAPPER_H 3 | #define TAGLIB_WRAPPER_H 4 | 5 | #ifdef __cplusplus 6 | extern "C" 7 | { 8 | #endif 9 | 10 | #include "utils.h" 11 | 12 | #ifndef TAGSETTINGS_STRUCT 13 | #define METADATA_MAX_LENGTH 256 14 | #define TAGSETTINGS_STRUCT 15 | typedef struct 16 | { 17 | char title[METADATA_MAX_LENGTH]; 18 | char artist[METADATA_MAX_LENGTH]; 19 | char album_artist[METADATA_MAX_LENGTH]; 20 | char album[METADATA_MAX_LENGTH]; 21 | char date[METADATA_MAX_LENGTH]; 22 | double replaygainTrack; 23 | double replaygainAlbum; 24 | } TagSettings; 25 | #endif 26 | int extractTags(const char *input_file, TagSettings *tag_settings, double *duration, const char *coverFilePath); 27 | 28 | #ifdef __cplusplus 29 | } 30 | #endif 31 | 32 | #endif // TAGLIB_WRAPPER_H 33 | -------------------------------------------------------------------------------- /src/term.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "utils.h" 14 | #include "term.h" 15 | 16 | /* 17 | 18 | term.c 19 | 20 | This file should contain only simple utility functions related to the terminal. 21 | They should work independently and be as decoupled from the application as possible. 22 | 23 | */ 24 | 25 | void setTextColor(int color) 26 | { 27 | /* 28 | - 0: Black 29 | - 1: Red 30 | - 2: Green 31 | - 3: Yellow 32 | - 4: Blue 33 | - 5: Magenta 34 | - 6: Cyan 35 | - 7: White 36 | */ 37 | printf("\033[0;3%dm", color); 38 | } 39 | 40 | void setTextColorRGB(int r, int g, int b) 41 | { 42 | printf("\033[0;38;2;%03u;%03u;%03um", (unsigned int)r, (unsigned int)g, (unsigned int)b); 43 | } 44 | 45 | void getTermSize(int *width, int *height) 46 | { 47 | struct winsize w; 48 | ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); 49 | 50 | *height = (int)w.ws_row; 51 | *width = (int)w.ws_col; 52 | } 53 | 54 | void setNonblockingMode() 55 | { 56 | struct termios ttystate; 57 | tcgetattr(STDIN_FILENO, &ttystate); 58 | ttystate.c_lflag &= ~ICANON; 59 | ttystate.c_cc[VMIN] = 0; 60 | ttystate.c_cc[VTIME] = 0; 61 | tcsetattr(STDIN_FILENO, TCSANOW, &ttystate); 62 | } 63 | 64 | void restoreTerminalMode() 65 | { 66 | struct termios ttystate; 67 | tcgetattr(STDIN_FILENO, &ttystate); 68 | ttystate.c_lflag |= ICANON; 69 | tcsetattr(STDIN_FILENO, TCSANOW, &ttystate); 70 | } 71 | 72 | void saveCursorPosition() 73 | { 74 | printf("\033[s"); 75 | } 76 | 77 | void restoreCursorPosition() 78 | { 79 | printf("\033[u"); 80 | } 81 | 82 | void setDefaultTextColor() 83 | { 84 | printf("\033[0m"); 85 | } 86 | 87 | int isInputAvailable() 88 | { 89 | fd_set fds; 90 | FD_ZERO(&fds); 91 | FD_SET(STDIN_FILENO, &fds); 92 | struct timeval tv; 93 | tv.tv_sec = 0; 94 | tv.tv_usec = 0; 95 | 96 | int ret = select(STDIN_FILENO + 1, &fds, NULL, NULL, &tv); 97 | 98 | if (ret < 0) 99 | { 100 | return 0; 101 | } 102 | int result = (ret > 0) && (FD_ISSET(STDIN_FILENO, &fds)); 103 | return result; 104 | } 105 | 106 | void hideCursor() 107 | { 108 | printf("\033[?25l"); 109 | fflush(stdout); 110 | } 111 | 112 | void showCursor() 113 | { 114 | printf("\033[?25h"); 115 | fflush(stdout); 116 | } 117 | 118 | void resetConsole() 119 | { 120 | // Print ANSI escape codes to reset terminal, clear screen, and move cursor to top-left 121 | printf("\033\143"); // Reset to Initial State (RIS) 122 | printf("\033[3J"); // Clear scrollback buffer 123 | printf("\033[H\033[J"); // Move cursor to top-left and clear screen 124 | 125 | fflush(stdout); 126 | } 127 | 128 | void clearRestOfScreen() 129 | { 130 | printf("\033[J"); 131 | } 132 | 133 | void clearScreen() 134 | { 135 | printf("\033[3J"); // Clear scrollback buffer 136 | printf("\033[2J\033[3J\033[H"); // Move cursor to top-left and clear screen and scrollback buffer 137 | } 138 | 139 | void enableScrolling() 140 | { 141 | printf("\033[?7h"); 142 | } 143 | 144 | void disableInputBuffering(void) 145 | { 146 | struct termios term; 147 | tcgetattr(STDIN_FILENO, &term); 148 | term.c_lflag &= ~(ICANON | ECHO); 149 | tcsetattr(STDIN_FILENO, TCSAFLUSH, &term); 150 | } 151 | 152 | void enableInputBuffering() 153 | { 154 | struct termios term; 155 | tcgetattr(STDIN_FILENO, &term); 156 | term.c_lflag |= ICANON | ECHO; 157 | tcsetattr(STDIN_FILENO, TCSAFLUSH, &term); 158 | } 159 | 160 | void cursorJump(int numRows) 161 | { 162 | printf("\033[%dA", numRows); 163 | printf("\033[0m"); 164 | } 165 | 166 | void cursorJumpDown(int numRows) 167 | { 168 | printf("\033[%dB", numRows); 169 | } 170 | 171 | int readInputSequence(char *seq, size_t seqSize) 172 | { 173 | char c; 174 | ssize_t bytesRead; 175 | seq[0] = '\0'; 176 | bytesRead = read(STDIN_FILENO, &c, 1); 177 | if (bytesRead <= 0) 178 | { 179 | return 0; 180 | } 181 | 182 | // If it's a single-byte ASCII character, return it 183 | if ((c & 0x80) == 0) 184 | { 185 | seq[0] = c; 186 | seq[1] = '\0'; 187 | return 1; 188 | } 189 | 190 | // Determine the length of the UTF-8 sequence 191 | int additionalBytes; 192 | if ((c & 0xE0) == 0xC0) 193 | { 194 | additionalBytes = 1; // 2-byte sequence 195 | } 196 | else if ((c & 0xF0) == 0xE0) 197 | { 198 | additionalBytes = 2; // 3-byte sequence 199 | } 200 | else if ((c & 0xF8) == 0xF0) 201 | { 202 | additionalBytes = 3; // 4-byte sequence 203 | } 204 | else 205 | { 206 | // Invalid UTF-8 start byte 207 | return 0; 208 | } 209 | 210 | // Ensure there's enough space in the buffer 211 | if ((size_t)additionalBytes + 1 >= seqSize) 212 | { 213 | return 0; 214 | } 215 | 216 | // Read the remaining bytes of the UTF-8 sequence 217 | seq[0] = c; 218 | bytesRead = read(STDIN_FILENO, &seq[1], additionalBytes); 219 | if (bytesRead != additionalBytes) 220 | { 221 | return 0; 222 | } 223 | 224 | seq[additionalBytes + 1] = '\0'; 225 | return additionalBytes + 1; 226 | } 227 | 228 | int getIndentation(int terminalWidth) 229 | { 230 | int term_w, term_h; 231 | getTermSize(&term_w, &term_h); 232 | int indent = ((term_w - terminalWidth) / 2) + 1; 233 | return (indent > 0) ? indent : 0; 234 | } 235 | 236 | void enterAlternateScreenBuffer() 237 | { 238 | // Enter alternate screen buffer 239 | printf("\033[?1049h"); 240 | } 241 | 242 | void exitAlternateScreenBuffer() 243 | { 244 | // Exit alternate screen buffer 245 | printf("\033[?1049l"); 246 | } 247 | 248 | void enableTerminalMouseButtons() 249 | { 250 | // Enable program to accept mouse input as codes 251 | printf("\033[?1002h\033[?1006h"); 252 | } 253 | 254 | void disableTerminalMouseButtons() 255 | { 256 | // Disable program to accept mouse input as codes 257 | printf("\033[?1002l\033[?1006l"); 258 | } 259 | -------------------------------------------------------------------------------- /src/term.h: -------------------------------------------------------------------------------- 1 | #ifndef TERM_H 2 | #include 3 | 4 | #define TERM_H 5 | #ifndef __USE_POSIX 6 | #define __USE_POSIX 7 | #endif 8 | 9 | #ifdef __GNU__ 10 | # define _BSD_SOURCE 11 | #endif 12 | 13 | void setTextColor(int color); 14 | 15 | void setTextColorRGB(int r, int g, int b); 16 | 17 | void getTermSize(int *width, int *height); 18 | 19 | int getIndentation(int terminalWidth); 20 | 21 | void setNonblockingMode(void); 22 | 23 | void restoreTerminalMode(void); 24 | 25 | void setDefaultTextColor(void); 26 | 27 | int isInputAvailable(void); 28 | 29 | void resetConsole(void); 30 | 31 | void saveCursorPosition(void); 32 | 33 | void restoreCursorPosition(void); 34 | 35 | void hideCursor(void); 36 | 37 | void showCursor(void); 38 | 39 | void clearRestOfScreen(void); 40 | 41 | void enableScrolling(void); 42 | 43 | void initResize(void); 44 | 45 | void disableInputBuffering(void); 46 | 47 | void enableInputBuffering(void); 48 | 49 | void cursorJump(int numRows); 50 | 51 | void cursorJumpDown(int numRows); 52 | 53 | void clearScreen(void); 54 | 55 | int readInputSequence(char *seq, size_t seqSize); 56 | 57 | void enterAlternateScreenBuffer(void); 58 | 59 | void exitAlternateScreenBuffer(void); 60 | 61 | void enableTerminalMouseButtons(void); 62 | 63 | void disableTerminalMouseButtons(void); 64 | 65 | #endif 66 | -------------------------------------------------------------------------------- /src/utils.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include "utils.h" 16 | 17 | /* 18 | 19 | utils.c 20 | 21 | Utility functions for instance for replacing some standard functions with safer alternatives. 22 | 23 | */ 24 | 25 | #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) 26 | #include // For arc4random 27 | #include // For uint32_t 28 | 29 | uint32_t arc4random_uniform(uint32_t upper_bound); 30 | 31 | int getRandomNumber(int min, int max) 32 | { 33 | return min + arc4random_uniform(max - min + 1); 34 | } 35 | 36 | #else 37 | #include // For getrandom 38 | 39 | int getRandomNumber(int min, int max) 40 | { 41 | unsigned int random_value; 42 | if (getrandom(&random_value, sizeof(random_value), 0) != sizeof(random_value)) 43 | { 44 | return min; 45 | } 46 | return min + (random_value % (max - min + 1)); 47 | } 48 | 49 | #endif 50 | 51 | void c_sleep(int milliseconds) 52 | { 53 | struct timespec ts; 54 | ts.tv_sec = milliseconds / 1000; 55 | ts.tv_nsec = milliseconds % 1000 * 1000000; 56 | nanosleep(&ts, NULL); 57 | } 58 | 59 | void c_usleep(int microseconds) 60 | { 61 | struct timespec ts; 62 | ts.tv_sec = microseconds / 1000000; 63 | ts.tv_nsec = microseconds % 1000; 64 | nanosleep(&ts, NULL); 65 | } 66 | 67 | void c_strcpy(char *dest, const char *src, size_t dest_size) 68 | { 69 | if (dest && dest_size > 0 && src) 70 | { 71 | size_t src_length = strnlen(src, dest_size - 1); 72 | memcpy(dest, src, src_length); 73 | dest[src_length] = '\0'; 74 | } 75 | } 76 | 77 | char *stringToLower(const char *str) 78 | { 79 | return g_utf8_strdown(str, -1); 80 | } 81 | 82 | char *stringToUpper(const char *str) 83 | { 84 | return g_utf8_strup(str, -1); 85 | } 86 | 87 | char *c_strcasestr(const char *haystack, const char *needle, int maxScanLen) 88 | { 89 | if (!haystack || !needle) 90 | return NULL; 91 | 92 | size_t needleLen = strnlen(needle, maxScanLen); 93 | size_t haystackLen = strnlen(haystack, maxScanLen); 94 | 95 | if (needleLen > haystackLen) 96 | return NULL; 97 | 98 | for (size_t i = 0; i <= haystackLen - needleLen; i++) 99 | { 100 | if (strncasecmp(&haystack[i], needle, needleLen) == 0) 101 | { 102 | return (char *)(haystack + i); 103 | } 104 | } 105 | 106 | return NULL; 107 | } 108 | 109 | int match_regex(const regex_t *regex, const char *ext) 110 | { 111 | if (regex == NULL || ext == NULL) 112 | { 113 | fprintf(stderr, "Invalid arguments\n"); 114 | return 1; 115 | } 116 | regmatch_t pmatch[1]; // Array to store match results 117 | int ret = regexec(regex, ext, 1, pmatch, 0); 118 | if (ret == REG_NOMATCH) 119 | { 120 | return 1; 121 | } 122 | else if (ret == 0) 123 | { 124 | return 0; 125 | } 126 | else 127 | { 128 | char errorBuf[100]; 129 | regerror(ret, regex, errorBuf, sizeof(errorBuf)); 130 | fprintf(stderr, "Regex match failed: %s\n", errorBuf); 131 | return 1; 132 | } 133 | } 134 | 135 | void extractExtension(const char *filename, size_t ext_size, char *ext) 136 | { 137 | size_t length = strnlen(filename, MAXPATHLEN); 138 | 139 | // Find the last '.' character in the filename 140 | const char *dot = NULL; 141 | for (size_t i = 0; i < length; i++) 142 | { 143 | if (filename[i] == '.') 144 | { 145 | dot = &filename[i]; 146 | } 147 | } 148 | 149 | // If no dot was found, there's no extension 150 | if (!dot || dot == filename + length - 1) 151 | { 152 | ext[0] = '\0'; // No extension found 153 | return; 154 | } 155 | 156 | size_t i = 0, j = 0; 157 | while (dot[i + 1] != '\0' && j < ext_size - 1) 158 | { 159 | unsigned char c = dot[i + 1]; 160 | 161 | // Determine the number of bytes for the current UTF-8 character 162 | size_t charSize; 163 | if (c < 0x80) 164 | { 165 | charSize = 1; // 1-byte character (ASCII) 166 | } 167 | else if ((c & 0xE0) == 0xC0) 168 | { 169 | charSize = 2; // 2-byte character 170 | } 171 | else if ((c & 0xF0) == 0xE0) 172 | { 173 | charSize = 3; // 3-byte character 174 | } 175 | else if ((c & 0xF8) == 0xF0) 176 | { 177 | charSize = 4; // 4-byte character 178 | } 179 | else 180 | { 181 | // Invalid UTF-8 byte sequence, stop copying 182 | break; 183 | } 184 | 185 | // Make sure we don't copy past the buffer 186 | if (j + charSize >= ext_size) 187 | { 188 | break; 189 | } 190 | 191 | // Copy the UTF-8 character 192 | memcpy(ext + j, dot + 1 + i, charSize); 193 | j += charSize; 194 | i += charSize; 195 | } 196 | 197 | // Null-terminate the result 198 | ext[j] = '\0'; 199 | } 200 | 201 | int pathEndsWith(const char *str, const char *suffix) 202 | { 203 | size_t length = strnlen(str, MAXPATHLEN); 204 | size_t suffixLength = strnlen(suffix, MAXPATHLEN); 205 | 206 | if (suffixLength > length) 207 | { 208 | return 0; 209 | } 210 | 211 | const char *strSuffix = str + (length - suffixLength); 212 | return strcmp(strSuffix, suffix) == 0; 213 | } 214 | 215 | int pathStartsWith(const char *str, const char *prefix) 216 | { 217 | size_t length = strnlen(str, MAXPATHLEN); 218 | size_t prefixLength = strnlen(prefix, MAXPATHLEN); 219 | 220 | if (prefixLength > length) 221 | { 222 | return 0; 223 | } 224 | 225 | return strncmp(str, prefix, prefixLength) == 0; 226 | } 227 | 228 | void trim(char *str, int maxLen) 229 | { 230 | char *start = str; 231 | while (*start && isspace(*start)) 232 | { 233 | start++; 234 | } 235 | char *end = str + strnlen(str, maxLen) - 1; 236 | while (end > start && isspace(*end)) 237 | { 238 | end--; 239 | } 240 | *(end + 1) = '\0'; 241 | 242 | if (str != start) 243 | { 244 | memmove(str, start, end - start + 2); 245 | } 246 | } 247 | 248 | const char *getHomePath(void) 249 | { 250 | struct passwd *pw = getpwuid(getuid()); 251 | if (pw && pw->pw_dir) 252 | { 253 | return pw->pw_dir; 254 | } 255 | return NULL; 256 | } 257 | 258 | char *getConfigPath(void) 259 | { 260 | char *configPath = malloc(MAXPATHLEN); 261 | if (!configPath) 262 | return NULL; 263 | 264 | const char *xdgConfig = getenv("XDG_CONFIG_HOME"); 265 | 266 | if (xdgConfig) 267 | { 268 | snprintf(configPath, MAXPATHLEN, "%s/kew", xdgConfig); 269 | } 270 | else 271 | { 272 | const char *home = getHomePath(); 273 | if (home) 274 | { 275 | #ifdef __APPLE__ 276 | snprintf(configPath, MAXPATHLEN, "%s/Library/Preferences/kew", home); 277 | #else 278 | snprintf(configPath, MAXPATHLEN, "%s/.config/kew", home); 279 | #endif 280 | } 281 | else 282 | { 283 | struct passwd *pw = getpwuid(getuid()); 284 | if (pw) 285 | { 286 | #ifdef __APPLE__ 287 | snprintf(configPath, MAXPATHLEN, "%s/Library/Preferences/kew", pw->pw_dir); 288 | #else 289 | snprintf(configPath, MAXPATHLEN, "%s/.config/kew", pw->pw_dir); 290 | #endif 291 | } 292 | else 293 | { 294 | free(configPath); 295 | return NULL; 296 | } 297 | } 298 | } 299 | 300 | return configPath; 301 | } 302 | 303 | char *getFilePath(const char *filename) 304 | { 305 | if (filename == NULL) 306 | { 307 | return NULL; 308 | } 309 | 310 | char *configdir = getConfigPath(); 311 | if (configdir == NULL) 312 | { 313 | return NULL; 314 | } 315 | 316 | size_t configdir_length = strnlen(configdir, MAXPATHLEN); 317 | size_t filename_length = strnlen(filename, MAXPATHLEN); 318 | 319 | size_t filepath_length = configdir_length + 1 + filename_length + 1; 320 | 321 | if (filepath_length > MAXPATHLEN) 322 | { 323 | free(configdir); 324 | return NULL; 325 | } 326 | 327 | char *filepath = (char *)malloc(filepath_length); 328 | if (filepath == NULL) 329 | { 330 | free(configdir); 331 | return NULL; 332 | } 333 | 334 | snprintf(filepath, filepath_length, "%s/%s", configdir, filename); 335 | 336 | free(configdir); 337 | return filepath; 338 | } 339 | 340 | void removeUnneededChars(char *str, int length) 341 | { 342 | // Do not remove characters if filename only contains digits 343 | bool stringContainsLetters = false; 344 | for (int i = 0; str[i] != '\0'; i++) 345 | { 346 | if (!isdigit(str[i])) 347 | { 348 | stringContainsLetters = true; 349 | } 350 | } 351 | if (!stringContainsLetters) 352 | { 353 | return; 354 | } 355 | 356 | for (int i = 0; i < 3 && str[i] != '\0' && str[i] != ' '; i++) 357 | { 358 | if (isdigit(str[i]) || str[i] == '.' || str[i] == '-' || str[i] == ' ') 359 | { 360 | int j; 361 | for (j = i; str[j] != '\0'; j++) 362 | { 363 | str[j] = str[j + 1]; 364 | } 365 | str[j] = '\0'; 366 | i--; // Decrement i to re-check the current index 367 | length--; 368 | } 369 | } 370 | 371 | // Remove hyphens and underscores from filename 372 | for (int i = 0; str[i] != '\0'; i++) 373 | { 374 | // Only remove if there are no spaces around 375 | if ((str[i] == '-' || str[i] == '_') && (i > 0 && i < length && str[i - 1] != ' ' && str[i + 1] != ' ')) 376 | { 377 | str[i] = ' '; 378 | } 379 | } 380 | } 381 | 382 | void shortenString(char *str, size_t maxLength) 383 | { 384 | size_t length = strnlen(str, maxLength + 2); 385 | 386 | if (length > maxLength) 387 | { 388 | str[maxLength] = '\0'; 389 | } 390 | } 391 | 392 | void printBlankSpaces(int numSpaces) 393 | { 394 | if (numSpaces < 1) 395 | return; 396 | printf("%*s", numSpaces, " "); 397 | } 398 | 399 | int getNumber(const char *str) 400 | { 401 | char *endptr; 402 | long value = strtol(str, &endptr, 10); 403 | 404 | if (value < INT_MIN || value > INT_MAX) 405 | { 406 | return 0; 407 | } 408 | 409 | return (int)value; 410 | } 411 | 412 | float getFloat(const char *str) 413 | { 414 | char *endptr; 415 | float value = strtof(str, &endptr); 416 | 417 | if (str == endptr) 418 | { 419 | return 0.0f; 420 | } 421 | 422 | if (isnan(value) || isinf(value) || value < -FLT_MAX || value > FLT_MAX) 423 | { 424 | return 0.0f; 425 | } 426 | 427 | return value; 428 | } 429 | -------------------------------------------------------------------------------- /src/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef UTILS_H 2 | #define UTILS_H 3 | #ifndef _POSIX_C_SOURCE 4 | #define _POSIX_C_SOURCE 200809L 5 | #endif 6 | #ifndef __USE_POSIX 7 | #define __USE_POSIX 8 | #endif 9 | 10 | #include 11 | 12 | #ifndef MAXPATHLEN 13 | #define MAXPATHLEN 4096 14 | #endif 15 | 16 | int getRandomNumber(int min, int max); 17 | 18 | void c_sleep(int milliseconds); 19 | 20 | void c_usleep(int microseconds); 21 | 22 | void c_strcpy(char *dest, const char *src, size_t dest_size); 23 | 24 | char *stringToUpper(const char *str); 25 | 26 | char *stringToLower(const char *str); 27 | 28 | char *utf8_strstr(const char *haystack, const char *needle); 29 | 30 | char *c_strcasestr(const char *haystack, const char *needle, int maxScanLen); 31 | 32 | int match_regex(const regex_t *regex, const char *ext); 33 | 34 | void extractExtension(const char *filename, size_t numChars, char *ext); 35 | 36 | int pathEndsWith(const char *str, const char *suffix); 37 | 38 | int pathStartsWith(const char *str, const char *prefix); 39 | 40 | void trim(char *str, int maxLen); 41 | 42 | const char *getHomePath(void); 43 | 44 | char *getConfigPath(void); 45 | 46 | void removeUnneededChars(char *str, int length); 47 | 48 | void shortenString(char *str, size_t maxLength); 49 | 50 | void printBlankSpaces(int numSpaces); 51 | 52 | int getNumber(const char *str); 53 | 54 | char *getFilePath(const char *filename); 55 | 56 | float getFloat(const char *str); 57 | 58 | #endif 59 | -------------------------------------------------------------------------------- /src/visuals.h: -------------------------------------------------------------------------------- 1 | 2 | 3 | #include "appstate.h" 4 | 5 | #ifndef PIXELDATA_STRUCT 6 | #define PIXELDATA_STRUCT 7 | typedef struct 8 | { 9 | unsigned char r; 10 | unsigned char g; 11 | unsigned char b; 12 | } PixelData; 13 | #endif 14 | 15 | void initVisuals(void); 16 | 17 | void freeVisuals(void); 18 | 19 | void drawSpectrumVisualizer(AppState *state, int indentation); 20 | --------------------------------------------------------------------------------