mirror of
https://github.com/clangen/musikcube.git
synced 2024-11-19 11:10:52 +00:00
Merge branch 'master' into clangen/upstream-pdcurses
This commit is contained in:
commit
23b274bce9
@ -45,7 +45,20 @@ jobs:
|
||||
steps:
|
||||
- checkout
|
||||
- run: apt-get update
|
||||
- run: apt-get install -y build-essential clang cmake libboost-thread1.67-dev libboost-system1.67-dev libboost-filesystem1.67-dev libboost-date-time1.67-dev libboost-atomic1.67-dev libboost-chrono1.67-dev libogg-dev libvorbis-dev libavutil-dev libavformat-dev libswresample-dev libncursesw5-dev libasound2-dev libpulse-dev pulseaudio libmicrohttpd-dev libmp3lame-dev libcurl4-openssl-dev libev-dev libssl-dev libtag1-dev
|
||||
- run: apt-get install -y build-essential clang cmake libboost-thread1.67-dev libboost-system1.67-dev libboost-filesystem1.67-dev libboost-date-time1.67-dev libboost-atomic1.67-dev libboost-chrono1.67-dev libogg-dev libvorbis-dev libavutil-dev libavformat-dev libswresample-dev libncursesw5-dev libasound2-dev libpulse-dev pulseaudio libmicrohttpd-dev libmp3lame-dev libcurl4-openssl-dev libev-dev libssl-dev libtag1-dev libsystemd-dev
|
||||
- run: cmake -DGENERATE_DEB=1 -DDEB_ARCHITECTURE=amd64 -DDEB_PLATFORM=ubuntu -DDEB_DISTRO=eoan -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Release .
|
||||
- run: make -j2
|
||||
- run: make package
|
||||
- run: mkdir -p /root/debs && mv /root/project/*.deb /root/debs/
|
||||
- store_artifacts:
|
||||
path: /root/debs/
|
||||
build_ubuntu_focal:
|
||||
docker:
|
||||
- image: ubuntu:focal
|
||||
steps:
|
||||
- checkout
|
||||
- run: apt-get update
|
||||
- run: apt-get install -y build-essential clang cmake libboost-thread1.71-dev libboost-system1.71-dev libboost-filesystem1.71-dev libboost-date-time1.71-dev libboost-atomic1.71-dev libboost-chrono1.71-dev libogg-dev libvorbis-dev libavutil-dev libavformat-dev libswresample-dev libncursesw5-dev libasound2-dev libpulse-dev pulseaudio libmicrohttpd-dev libmp3lame-dev libcurl4-openssl-dev libev-dev libssl-dev libtag1-dev libsystemd-dev
|
||||
- run: cmake -DGENERATE_DEB=1 -DDEB_ARCHITECTURE=amd64 -DDEB_PLATFORM=ubuntu -DDEB_DISTRO=eoan -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Release .
|
||||
- run: make -j2
|
||||
- run: make package
|
||||
@ -126,6 +139,7 @@ workflows:
|
||||
- build_ubuntu_cosmic: *workflow_filters
|
||||
- build_ubuntu_disco: *workflow_filters
|
||||
- build_ubuntu_eoan: *workflow_filters
|
||||
- build_ubuntu_focal: *workflow_filters
|
||||
- build_fedora_26: *workflow_filters
|
||||
- build_fedora_27: *workflow_filters
|
||||
- build_fedora_28: *workflow_filters
|
||||
|
@ -237,12 +237,19 @@ file(GLOB locales "src/musikcube/data/locales/*.json")
|
||||
file(COPY ${locales} DESTINATION bin/locales)
|
||||
install(FILES ${locales} DESTINATION share/musikcube/locales)
|
||||
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "Linux")
|
||||
file(GLOB_RECURSE linux_share_applications "src/musikcube/data/linux/share/applications/*")
|
||||
install(FILES ${linux_share_applications} DESTINATION share/applications/)
|
||||
file(GLOB_RECURSE linux_share_icons_48 "src/musikcube/data/linux/share/icons/hicolor/48x48/apps/*")
|
||||
install(FILES ${linux_share_icons_48} DESTINATION share/icons/hicolor/48x48/apps/)
|
||||
endif()
|
||||
|
||||
# libmusikcore sshared library
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "Darwin")
|
||||
install(FILES "bin/libmusikcore.dylib" DESTINATION share/musikcube)
|
||||
else ()
|
||||
else()
|
||||
install(FILES "bin/libmusikcore.so" DESTINATION share/musikcube)
|
||||
endif ()
|
||||
endif()
|
||||
|
||||
# executable and shell script for musikcube
|
||||
install(
|
||||
@ -307,6 +314,9 @@ if (GENERATE_DEB MATCHES "1")
|
||||
set(DEB_AVUTIL_VERSION "56")
|
||||
set(DEB_AVFORMAT_VERSION "58")
|
||||
set(DEB_SWRESAMPLE_VERSION "3")
|
||||
elseif (DEB_DISTRO MATCHES "focal")
|
||||
set(DEB_BOOST_VERSION "1.71.0")
|
||||
set(DEB_MICROHTTPD_VERSION "12")
|
||||
elseif (DEB_DISTRO MATCHES "xenial")
|
||||
set(DEB_BOOST_VERSION "1.58.0")
|
||||
set(DEB_MICROHTTPD_VERSION "10")
|
||||
@ -349,5 +359,5 @@ add_custom_command(TARGET postbuild POST_BUILD COMMAND cmake .)
|
||||
# strip binaries in release mode
|
||||
if (CMAKE_BUILD_TYPE MATCHES Release)
|
||||
message(STATUS "stripping binaries...")
|
||||
add_custom_command(TARGET postbuild POST_BUILD COMMAND ./strip-nix.sh)
|
||||
add_custom_command(TARGET postbuild POST_BUILD COMMAND "${CMAKE_SOURCE_DIR}/strip-nix.sh")
|
||||
endif()
|
||||
|
51
install-deps.sh
Executable file
51
install-deps.sh
Executable file
@ -0,0 +1,51 @@
|
||||
#!/bin/bash
|
||||
OS=`uname`
|
||||
if [ $OS == "Linux" ]; then
|
||||
echo "detected linux"
|
||||
if [ -x "$(command -v lsb_release)" ]; then
|
||||
DISTRO=`lsb_release -cs`
|
||||
echo "detected ${DISTRO}"
|
||||
case $DISTRO in
|
||||
"focal")
|
||||
sudo apt-get install build-essential clang cmake libboost-thread1.71-dev libboost-system1.71-dev libboost-filesystem1.71-dev libboost-date-time1.71-dev libboost-atomic1.71-dev libboost-chrono1.71-dev libogg-dev libvorbis-dev libavutil-dev libavformat-dev libswresample-dev libncursesw5-dev libasound2-dev libpulse-dev pulseaudio libmicrohttpd-dev libmp3lame-dev libcurl4-openssl-dev libev-dev libtag1-dev libsystemd-dev
|
||||
;;
|
||||
"cosmic" | "disco" | "eoan")
|
||||
sudo apt-get install build-essential clang cmake libboost-thread1.67-dev libboost-system1.67-dev libboost-filesystem1.67-dev libboost-date-time1.67-dev libboost-atomic1.67-dev libboost-chrono1.67-dev libogg-dev libvorbis-dev libavutil-dev libavformat-dev libswresample-dev libncursesw5-dev libasound2-dev libpulse-dev pulseaudio libmicrohttpd-dev libmp3lame-dev libcurl4-openssl-dev libev-dev libtag1-dev libsystemd-dev
|
||||
;;
|
||||
"bionic")
|
||||
sudo apt-get install build-essential clang cmake libboost-thread1.65-dev libboost-system1.65-dev libboost-filesystem1.65-dev libboost-date-time1.65-dev libboost-atomic1.65-dev libboost-chrono1.65-dev libogg-dev libvorbis-dev libavutil-dev libavformat-dev libswresample-dev libncursesw5-dev libasound2-dev libpulse-dev pulseaudio libmicrohttpd-dev libmp3lame-dev libcurl4-openssl-dev libev-dev libtag1-dev libsystemd-dev
|
||||
;;
|
||||
"artful")
|
||||
sudo apt-get install build-essential clang cmake libboost-thread1.63-dev libboost-system1.63-dev libboost-filesystem1.63-dev libboost-date-time1.63-dev libboost-atomic1.63-dev libboost-chrono1.63-dev libogg-dev libvorbis-dev libavutil-dev libavformat-dev libswresample-dev libncursesw5-dev libasound2-dev libpulse-dev pulseaudio libmicrohttpd-dev libmp3lame-dev libcurl4-openssl-dev libev-dev libtag1-dev libsystemd-dev
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
if [ -f "/etc/fedora-release" ]; then
|
||||
DISTRO=`cat /etc/fedora-release`
|
||||
echo "detected ${DISTRO}"
|
||||
sudo dnf install gcc-c++ make cmake boost-devel libogg-devel libvorbis-devel ffmpeg-devel ncurses-devel zlib-devel alsa-lib-devel pulseaudio-libs-devel libcurl-devel libmicrohttpd-devel lame-devel libev-devel taglib-devel libsystemd-devel
|
||||
fi
|
||||
if [ -f "/etc/arch-release" ] || [ -f "/etc/manjaro-release" ]; then
|
||||
sudo pacman -S libogg libvorbis libmicrohttpd ffmpeg lame cmake ncurses boost pulseaudio libpulse alsa-lib curl libev taglib
|
||||
fi
|
||||
if [ -f "/etc/SUSE-brand" ]; then
|
||||
echo "detected SUSE"
|
||||
sudo zypper install libcurl-devel libmicrohttpd-devel libboost_thread1_71_0-devel libboost_system1_71_0-devel libboost_filesystem1_71_0-devel libboost_date_time1_71_0-devel libboost_atomic1_71_0-devel libboost_chrono1_71_0-devel cmake ncurses-devel libogg-devel libvorbis-devel ffmpeg-3-libavcodec-devel ffmpeg-3-libswresample-devel ffmpeg-3-libavformat-devel ffmpeg-3-libavutil-devel libmp3lame-devel pulseaudio libpulse-devel alsa-devel zlib-devel libressl-devel libev-devel libtag-devel systemd-devel
|
||||
fi
|
||||
fi
|
||||
if [ $OS == "Darwin" ]; then
|
||||
echo "detected macos"
|
||||
brew install cmake boost libogg libvorbis ffmpeg libmicrohttpd lame libev taglib
|
||||
fi
|
||||
if [ $OS == "FreeBSD" ]; then
|
||||
echo "detected freebsd"
|
||||
pkg install boost-all curl libvorbis libogg libmicrohttpd ffmpeg alsa-lib cmake sndio libev taglib bash
|
||||
portsnap fetch
|
||||
portsnap extract
|
||||
cd /usr/ports/audio/lame
|
||||
make reinstall
|
||||
fi
|
||||
if [ $OS == "OpenBSD" ]; then
|
||||
echo "detected openbsd"
|
||||
pkg_add git cmake ffmpeg boost sndio libev libmicrohttpd taglib
|
||||
fi
|
@ -8,7 +8,7 @@ Summary: A cross-platform, terminal-based audio engine, library, player and serv
|
||||
Source0: https://github.com/clangen/musikcube/archive/%{version}.tar.gz#/%{name}-%{version}.tar.gz
|
||||
License: BSD-3-Clause
|
||||
Packager: David Muckle <dvdmuckle@dvdmuckle.xyz>
|
||||
BuildRequires: gcc-c++ cmake boost-devel libogg-devel libvorbis-devel ffmpeg-devel ncurses-devel zlib-devel alsa-lib-devel pulseaudio-libs-devel openssl-devel libcurl-devel libmicrohttpd-devel lame-devel libev-devel taglib-devel make
|
||||
BuildRequires: gcc-c++ cmake boost-devel libogg-devel libvorbis-devel ffmpeg-devel ncurses-devel zlib-devel alsa-lib-devel pulseaudio-libs-devel openssl-devel libcurl-devel libmicrohttpd-devel lame-devel libev-devel taglib-devel systemd-devel make
|
||||
Requires: boost libogg libvorbis ffmpeg-libs ncurses zlib alsa-lib pulseaudio-libs openssl libcurl libmicrohttpd lame libev taglib
|
||||
|
||||
%description
|
||||
|
13
share/applications/musikcube.desktop
Normal file
13
share/applications/musikcube.desktop
Normal file
@ -0,0 +1,13 @@
|
||||
[Desktop Entry]
|
||||
Version=1.0
|
||||
Type=Application
|
||||
Name=musikcube
|
||||
GenericName=musikcube
|
||||
Comment=terminal-based music player
|
||||
Exec=musikcube %U
|
||||
TryExec=musikcube
|
||||
Icon=musikcube
|
||||
Terminal=true
|
||||
Categories=AudioVideo;Player;Audio;
|
||||
StartupNotify=false
|
||||
StartupWMClass=musikcube
|
BIN
share/icons/hicolor/48x48/apps/musikcube.png
Normal file
BIN
share/icons/hicolor/48x48/apps/musikcube.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 801 B |
100
snap/snapcraft.yaml
Normal file
100
snap/snapcraft.yaml
Normal file
@ -0,0 +1,100 @@
|
||||
name: musikcube # you probably want to 'snapcraft register <name>'
|
||||
version: 0.85.0
|
||||
summary: a terminal-based music player, metadata indexer, and server
|
||||
description: |
|
||||
musikcube is a fully functional terminal-based music player, library, and
|
||||
streaming audio server that runs natively on Windows, macOS, and Linux. It
|
||||
also runs well on a Raspberry Pi with a custom DAC (e.g. IQaudIO DAC+,
|
||||
HiFiBerry DAC+ and others), and can output 24bit/192k audio comfortably.
|
||||
grade: stable
|
||||
confinement: strict
|
||||
base: core18
|
||||
|
||||
parts:
|
||||
desktop-glib-musikcube:
|
||||
source: https://github.com/ubuntu/snapcraft-desktop-helpers.git
|
||||
source-subdir: glib-only/
|
||||
plugin: make
|
||||
build-packages:
|
||||
- libglib2.0-dev
|
||||
stage-packages:
|
||||
- libglib2.0-bin
|
||||
|
||||
musikcube:
|
||||
after:
|
||||
- desktop-glib-musikcube
|
||||
plugin: cmake
|
||||
configflags:
|
||||
- -DCMAKE_BUILD_TYPE=Release
|
||||
source: https://github.com/clangen/musikcube.git
|
||||
build-packages:
|
||||
- build-essential
|
||||
- clang
|
||||
- cmake
|
||||
- libboost-thread1.65-dev
|
||||
- libboost-system1.65-dev
|
||||
- libboost-filesystem1.65-dev
|
||||
- libboost-date-time1.65-dev
|
||||
- libboost-atomic1.65-dev
|
||||
- libboost-chrono1.65-dev
|
||||
- libogg-dev
|
||||
- libvorbis-dev
|
||||
- libavutil-dev
|
||||
- libavformat-dev
|
||||
- libswresample-dev
|
||||
- libncursesw5-dev
|
||||
- libasound2-dev
|
||||
- libpulse-dev
|
||||
- libmicrohttpd-dev
|
||||
- libmp3lame-dev
|
||||
- libcurl4-openssl-dev
|
||||
- libev-dev
|
||||
- libtag1-dev
|
||||
- libsystemd-dev
|
||||
- libssl-dev
|
||||
stage-packages:
|
||||
- libboost-thread1.65.1
|
||||
- libboost-system1.65.1
|
||||
- libboost-chrono1.65.1
|
||||
- libboost-filesystem1.65.1
|
||||
- libboost-date-time1.65.1
|
||||
- libmicrohttpd12
|
||||
- libcurl4
|
||||
- libogg0
|
||||
- libvorbis0a
|
||||
- libvorbisfile3
|
||||
- libncursesw5
|
||||
- libasound2
|
||||
- libpulse0
|
||||
- libmp3lame0
|
||||
- libev4
|
||||
- libavcodec-extra
|
||||
- libavutil55
|
||||
- libavformat57
|
||||
- libswresample2
|
||||
- libsystemd0
|
||||
- libtag1v5
|
||||
|
||||
apps:
|
||||
musikcube:
|
||||
desktop: share/applications/musikcube.desktop
|
||||
command: desktop-launch $SNAP/share/musikcube/musikcube
|
||||
environment:
|
||||
LD_LIBRARY_PATH: $LD_LIBRARY_PATH:$SNAP/usr/lib/$SNAPCRAFT_ARCH_TRIPLET/pulseaudio
|
||||
ALSA_CONFIG_PATH: /snap/$SNAPCRAFT_PROJECT_NAME/current/usr/share/alsa/alsa.conf
|
||||
LANG: C.UTF-8
|
||||
plugs:
|
||||
- home
|
||||
- removable-media
|
||||
- pulseaudio
|
||||
- network
|
||||
- network-bind
|
||||
- audio-playback
|
||||
- alsa
|
||||
- desktop
|
||||
slots:
|
||||
- mpris
|
||||
|
||||
layout:
|
||||
/usr/share/alsa:
|
||||
bind: $SNAP/usr/share/alsa
|
@ -129,6 +129,10 @@ void DirectoryLayout::SetDirectory(const std::string& directory) {
|
||||
this->Refresh(true);
|
||||
}
|
||||
|
||||
std::string DirectoryLayout::GetDirectory() {
|
||||
return this->adapter->GetCurrentPath();
|
||||
}
|
||||
|
||||
void DirectoryLayout::OnVisibilityChanged(bool visible) {
|
||||
LayoutBase::OnVisibilityChanged(visible);
|
||||
if (visible) {
|
||||
|
@ -62,7 +62,8 @@ namespace musik {
|
||||
|
||||
virtual void OnVisibilityChanged(bool visible);
|
||||
virtual bool KeyPress(const std::string& key);
|
||||
virtual void SetDirectory(const std::string& directory);
|
||||
void SetDirectory(const std::string& directory);
|
||||
std::string GetDirectory();
|
||||
|
||||
protected:
|
||||
virtual void OnLayout();
|
||||
|
@ -159,7 +159,7 @@ void LibraryLayout::ShowBrowse(const std::string& category) {
|
||||
if (category.size()) {
|
||||
this->browseLayout->SwitchCategory(category);
|
||||
}
|
||||
|
||||
this->lastBrowseCategoryType = category;
|
||||
REMEMBER(keys::LastLibraryView, type::Browse);
|
||||
}
|
||||
|
||||
@ -167,7 +167,6 @@ void LibraryLayout::ShowCategorySearch() {
|
||||
SHOULD_REFOCUS(this->categorySearchLayout)
|
||||
? this->categorySearchLayout->FocusInput()
|
||||
: this->ChangeMainLayout(this->categorySearchLayout);
|
||||
|
||||
REMEMBER(keys::LastLibraryView, type::CategoryFilter);
|
||||
}
|
||||
|
||||
@ -180,11 +179,13 @@ void LibraryLayout::ShowTrackSearch() {
|
||||
}
|
||||
|
||||
void LibraryLayout::ShowDirectories(const std::string& directory) {
|
||||
this->directoryLayout->SetDirectory(directory);
|
||||
if (directory.size()) {
|
||||
this->directoryLayout->SetDirectory(directory);
|
||||
REMEMBER(keys::LastBrowseDirectoryRoot, directory);
|
||||
}
|
||||
this->ChangeMainLayout(this->directoryLayout);
|
||||
|
||||
this->lastBrowseCategoryType = MagicConstants::DirectoryCategoryType;
|
||||
REMEMBER(keys::LastLibraryView, type::Directory);
|
||||
REMEMBER(keys::LastBrowseDirectoryRoot, directory);
|
||||
}
|
||||
|
||||
void LibraryLayout::InitializeWindows() {
|
||||
@ -349,7 +350,7 @@ IWindowPtr LibraryLayout::GetFocus() {
|
||||
result = this->visibleLayout->GetFocus();
|
||||
}
|
||||
|
||||
return result;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool LibraryLayout::SetFocus(cursespp::IWindowPtr window) {
|
||||
@ -445,7 +446,12 @@ bool LibraryLayout::KeyPress(const std::string& key) {
|
||||
return true;
|
||||
}
|
||||
else if (Hotkeys::Is(Hotkeys::NavigateLibraryBrowse, key)) {
|
||||
this->ShowBrowse();
|
||||
if (this->lastBrowseCategoryType == MagicConstants::DirectoryCategoryType) {
|
||||
this->ShowDirectories();
|
||||
}
|
||||
else {
|
||||
this->ShowBrowse();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else if (Hotkeys::Is(Hotkeys::NavigateLibraryFilter, key)) {
|
||||
|
@ -97,7 +97,7 @@ namespace musik {
|
||||
void ShowCategorySearch();
|
||||
void ShowTrackSearch();
|
||||
void ShowDirectoryChooser();
|
||||
void ShowDirectories(const std::string& directory);
|
||||
void ShowDirectories(const std::string& directory = "");
|
||||
|
||||
void ChangeMainLayout(std::shared_ptr<cursespp::LayoutBase> newLayout);
|
||||
void OnLayoutChanged();
|
||||
@ -115,6 +115,7 @@ namespace musik {
|
||||
std::shared_ptr<TrackSearchLayout> trackSearchLayout;
|
||||
std::shared_ptr<cursespp::LayoutBase> visibleLayout;
|
||||
cursespp::ShortcutsWindow* shortcuts;
|
||||
std::string lastBrowseCategoryType;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -516,6 +516,7 @@ void PlayQueueOverlays::ShowAddCategoryOverlay(
|
||||
adapter->AddEntry(_TSTR("playqueue_overlay_add_to_start_of_queue"));
|
||||
adapter->AddEntry(_TSTR("playqueue_overlay_add_to_end_in_queue"));
|
||||
adapter->AddEntry(_TSTR("playqueue_overlay_add_as_next_in_queue"));
|
||||
adapter->AddEntry(_TSTR("playqueue_overlay_add_hotswap_queue"));
|
||||
adapter->SetSelectable(true);
|
||||
|
||||
size_t selectedIndex = 0;
|
||||
@ -542,6 +543,11 @@ void PlayQueueOverlays::ShowAddCategoryOverlay(
|
||||
showAddCategorySelectionToPlaylistOverlay(
|
||||
messageQueue, library, fieldColumn, fieldValue, fieldId);
|
||||
}
|
||||
else if (index == adapter->GetEntryCount() - 1) {
|
||||
auto tracks = queryTracksByCategory(
|
||||
library, fieldColumn, fieldValue, fieldId);
|
||||
playback.HotSwap(*tracks);
|
||||
}
|
||||
else {
|
||||
handleAddCategorySelectionToPlayQueue(
|
||||
playback, library, fieldColumn, fieldValue, fieldId, index - 1);
|
||||
|
@ -223,11 +223,12 @@ struct musik::cube::TransportDisplayCache {
|
||||
if (updatedTotal != secondsTotal) {
|
||||
secondsTotal = updatedTotal;
|
||||
if (secondsTotal <= 0 && secondsTotal != INT_MIN) {
|
||||
std::string duration =
|
||||
this->track->GetString(constants::Track::DURATION);
|
||||
|
||||
if (duration.size()) {
|
||||
secondsTotal = std::stoi(duration);
|
||||
if (this->track) {
|
||||
std::string duration =
|
||||
this->track->GetString(constants::Track::DURATION);
|
||||
if (duration.size()) {
|
||||
secondsTotal = std::stoi(duration);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,13 @@
|
||||
[Desktop Entry]
|
||||
Version=1.0
|
||||
Type=Application
|
||||
Name=musikcube
|
||||
GenericName=musikcube
|
||||
Comment=terminal-based music player
|
||||
Exec=musikcube %U
|
||||
TryExec=musikcube
|
||||
Icon=musikcube
|
||||
Terminal=true
|
||||
Categories=AudioVideo;Player;Audio;
|
||||
StartupNotify=false
|
||||
StartupWMClass=musikcube
|
Binary file not shown.
After Width: | Height: | Size: 801 B |
@ -159,6 +159,7 @@
|
||||
"playqueue_overlay_add_to_start_of_queue": "add to beginning of play queue",
|
||||
"playqueue_overlay_add_to_end_in_queue": "add to end of play queue",
|
||||
"playqueue_overlay_add_as_next_in_queue": "add as next in play queue",
|
||||
"playqueue_overlay_add_hotswap_queue": "hotswap with play queue",
|
||||
"playqueue_overlay_add_to_playlist": "add to playlist",
|
||||
"playqueue_overlay_select_playlist_title": "select a playlist",
|
||||
|
||||
|
@ -165,6 +165,7 @@ AlsaOut::~AlsaOut() {
|
||||
}
|
||||
|
||||
void AlsaOut::CloseDevice() {
|
||||
LOCK("CloseDevice()");
|
||||
if (this->pcmHandle) {
|
||||
std::cerr << "AlsaOut: closing PCM handle\n";
|
||||
snd_pcm_close(this->pcmHandle);
|
||||
@ -417,12 +418,17 @@ void AlsaOut::WriteLoop() {
|
||||
}
|
||||
}
|
||||
|
||||
WRITE_BUFFER(this->pcmHandle, next, samplesPerChannel); /* sets 'err' */
|
||||
{
|
||||
LOCK("WRITE_BUFFER()");
|
||||
if (this->pcmHandle) {
|
||||
WRITE_BUFFER(this->pcmHandle, next, samplesPerChannel); /* sets 'err' */
|
||||
|
||||
if (err == -EINTR || err == -EPIPE || err == -ESTRPIPE) {
|
||||
if (!snd_pcm_recover(this->pcmHandle, err, 1)) {
|
||||
/* try one more time... */
|
||||
WRITE_BUFFER(this->pcmHandle, next, samplesPerChannel);
|
||||
if (err == -EINTR || err == -EPIPE || err == -ESTRPIPE) {
|
||||
if (!snd_pcm_recover(this->pcmHandle, err, 1)) {
|
||||
/* try one more time... */
|
||||
WRITE_BUFFER(this->pcmHandle, next, samplesPerChannel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -493,20 +499,22 @@ void AlsaOut::SetFormat(IBuffer *buffer) {
|
||||
|
||||
this->InitDevice();
|
||||
|
||||
int err = snd_pcm_set_params(
|
||||
this->pcmHandle,
|
||||
PCM_FORMAT,
|
||||
PCM_ACCESS_TYPE,
|
||||
this->channels,
|
||||
this->rate,
|
||||
1, /* allow resampling */
|
||||
500000); /* 0.5s latency */
|
||||
if (this->pcmHandle) {
|
||||
int err = snd_pcm_set_params(
|
||||
this->pcmHandle,
|
||||
PCM_FORMAT,
|
||||
PCM_ACCESS_TYPE,
|
||||
this->channels,
|
||||
this->rate,
|
||||
1, /* allow resampling */
|
||||
500000); /* 0.5s latency */
|
||||
|
||||
if (err > 0) {
|
||||
std::cerr << "AlsaOut: set format error: " << snd_strerror(err) << std::endl;
|
||||
}
|
||||
else {
|
||||
this->SetVolume(this->volume);
|
||||
if (err > 0) {
|
||||
std::cerr << "AlsaOut: set format error: " << snd_strerror(err) << std::endl;
|
||||
}
|
||||
else {
|
||||
this->SetVolume(this->volume);
|
||||
}
|
||||
}
|
||||
|
||||
std::cerr << "AlsaOut: device format initialized from buffer\n";
|
||||
|
@ -188,6 +188,7 @@ static int get_metadata(sd_bus* bus, const char* path, const char *iface,
|
||||
// append fields
|
||||
sd_msg_append_dict(reply, "mpris:trackid", metadata.trackid.c_str(), 'o');
|
||||
sd_msg_append_dict(reply, "mpris:length", &metadata.length, 'x');
|
||||
sd_msg_append_dict(reply, "mpris:artUrl", metadata.albumArt.c_str(), 's');
|
||||
sd_msg_append_strlist_dict(reply, "xesam:artist", metadata.artist.c_str());
|
||||
sd_msg_append_dict(reply, "xesam:title", metadata.title.c_str(), 's');
|
||||
sd_msg_append_dict(reply, "xesam:album", metadata.album.c_str(), 's');
|
||||
|
@ -4,12 +4,14 @@
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <core/sdk/IEnvironment.h>
|
||||
|
||||
extern "C" {
|
||||
#include <unistd.h>
|
||||
}
|
||||
|
||||
thread_local char localBuffer[1024];
|
||||
std::string thumbnailPath;
|
||||
thread_local char localBuffer[4096];
|
||||
static MPRISRemote remote;
|
||||
|
||||
static const std::map<MPRISProperty, const std::vector<const char*>> MPRISPropertyNames =
|
||||
@ -25,6 +27,12 @@ static std::string GetMetadataString(ITrack* track, const char* key)
|
||||
return std::string(localBuffer);
|
||||
}
|
||||
|
||||
static std::string GetThumbnailPath(ITrack* track)
|
||||
{
|
||||
int64_t thumbnailId = track->GetInt64(track::ThumbnailId);
|
||||
return thumbnailPath + std::to_string(thumbnailId) + ".jpg";
|
||||
}
|
||||
|
||||
static class MPRISPlugin : public IPlugin {
|
||||
public:
|
||||
MPRISPlugin() { }
|
||||
@ -39,6 +47,13 @@ static class MPRISPlugin : public IPlugin {
|
||||
int SdkVersion() { return musik::core::sdk::SdkVersion; }
|
||||
} plugin;
|
||||
|
||||
extern "C" void SetEnvironment(IEnvironment* environment) {
|
||||
if (environment) {
|
||||
environment->GetPath(PathLibrary, localBuffer, sizeof(localBuffer));
|
||||
thumbnailPath = std::string(localBuffer) + "/thumbs/";
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" IPlugin* GetPlugin() {
|
||||
return &plugin;
|
||||
}
|
||||
@ -101,7 +116,7 @@ void MPRISRemote::MPRISDeinit() {
|
||||
|
||||
void MPRISRemote::MPRISEmitChange(MPRISProperty prop) {
|
||||
if (bus) {
|
||||
char** strv = (char**)(&MPRISPropertyNames.at(prop));
|
||||
char** strv = (char**)(MPRISPropertyNames.at(prop).data());
|
||||
std::unique_lock<decltype(sd_mutex)> lock(sd_mutex);
|
||||
sd_bus_emit_properties_changed_strv(bus, "/org/mpris/MediaPlayer2",
|
||||
"org.mpris.MediaPlayer2.Player",
|
||||
@ -181,6 +196,7 @@ struct MPRISMetadataValues MPRISRemote::MPRISGetMetadata() {
|
||||
metadata.discNumber = curTrack->GetInt32(track::DiscNum);
|
||||
metadata.trackNumber = curTrack->GetInt32(track::TrackNum);
|
||||
metadata.length = curTrack->GetInt64(track::Duration)*1000*1000;
|
||||
metadata.albumArt = GetThumbnailPath(curTrack);
|
||||
metadata.available = true;
|
||||
}
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ struct MPRISMetadataValues {
|
||||
std::string title;
|
||||
std::string album;
|
||||
std::string albumArtist;
|
||||
std::string albumArt;
|
||||
std::string genre;
|
||||
std::string comment;
|
||||
uint32_t trackNumber;
|
||||
|
@ -242,6 +242,7 @@ void PulseOut::OpenDevice(musik::core::sdk::IBuffer* buffer) {
|
||||
this->rate != buffer->SampleRate() ||
|
||||
this->channels != buffer->Channels())
|
||||
{
|
||||
int errorCode = 0;
|
||||
this->CloseDevice();
|
||||
|
||||
pa_sample_spec spec;
|
||||
@ -249,9 +250,8 @@ void PulseOut::OpenDevice(musik::core::sdk::IBuffer* buffer) {
|
||||
spec.channels = buffer->Channels();
|
||||
spec.rate = buffer->SampleRate();
|
||||
|
||||
std::cerr << "PulseOut: opening device\n";
|
||||
|
||||
std::string deviceId = this->GetPreferredDeviceId();
|
||||
std::cerr << "PulseOut: opening device " << deviceId << "\n";
|
||||
|
||||
/* output to preferred device id, as specified in prefs */
|
||||
this->audioConnection = pa_blocking_new(
|
||||
@ -263,7 +263,11 @@ void PulseOut::OpenDevice(musik::core::sdk::IBuffer* buffer) {
|
||||
&spec,
|
||||
nullptr,
|
||||
nullptr,
|
||||
0);
|
||||
&errorCode);
|
||||
|
||||
if (!this->audioConnection) {
|
||||
std::cerr << "PulseOut: failed to open device. errorCode=" << errorCode << "\n";
|
||||
}
|
||||
|
||||
if (!this->audioConnection && deviceId.size()) {
|
||||
/* fall back to default if preferred is not found */
|
||||
@ -276,7 +280,11 @@ void PulseOut::OpenDevice(musik::core::sdk::IBuffer* buffer) {
|
||||
&spec,
|
||||
nullptr,
|
||||
nullptr,
|
||||
0);
|
||||
&errorCode);
|
||||
|
||||
if (!this->audioConnection) {
|
||||
std::cerr << "PulseOut: failed to open default device. errorCode=" << errorCode << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
if (this->audioConnection) {
|
||||
|
@ -158,10 +158,15 @@ pa_blocking* pa_blocking_new(
|
||||
pa_blocking *p;
|
||||
int error = PA_ERR_INTERNAL, r;
|
||||
|
||||
fprintf(stderr, "pulse_blocking_stream: CHECK_VALIDITY server\n");
|
||||
CHECK_VALIDITY_RETURN_ANY(rerror, !server || *server, PA_ERR_INVALID, NULL);
|
||||
fprintf(stderr, "pulse_blocking_stream: CHECK_VALIDITY dir\n");
|
||||
CHECK_VALIDITY_RETURN_ANY(rerror, dir == PA_STREAM_PLAYBACK || dir == PA_STREAM_RECORD, PA_ERR_INVALID, NULL);
|
||||
fprintf(stderr, "pulse_blocking_stream: CHECK_VALIDITY device\n");
|
||||
CHECK_VALIDITY_RETURN_ANY(rerror, !dev || *dev, PA_ERR_INVALID, NULL);
|
||||
fprintf(stderr, "pulse_blocking_stream: CHECK_VALIDITY spec\n");
|
||||
CHECK_VALIDITY_RETURN_ANY(rerror, ss && pa_sample_spec_valid(ss), PA_ERR_INVALID, NULL);
|
||||
fprintf(stderr, "pulse_blocking_stream: CHECK_VALIDITY channel map\n");
|
||||
CHECK_VALIDITY_RETURN_ANY(rerror, !map || (pa_channel_map_valid(map) && map->channels == ss->channels), PA_ERR_INVALID, NULL)
|
||||
|
||||
p = pa_xnew0(pa_blocking, 1);
|
||||
@ -170,33 +175,43 @@ pa_blocking* pa_blocking_new(
|
||||
p->sink_id = -1;
|
||||
p->hw_volume = -1;
|
||||
|
||||
if (!(p->mainloop = pa_threaded_mainloop_new()))
|
||||
if (!(p->mainloop = pa_threaded_mainloop_new())) {
|
||||
fprintf(stderr, "pulse_blocking_stream: failed to create main loop\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!(p->context = pa_context_new(pa_threaded_mainloop_get_api(p->mainloop), name)))
|
||||
if (!(p->context = pa_context_new(pa_threaded_mainloop_get_api(p->mainloop), name))) {
|
||||
fprintf(stderr, "pulse_blocking_stream: failed to create context\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
pa_context_set_state_callback(p->context, context_state_cb, p);
|
||||
|
||||
if (pa_context_connect(p->context, server, 0, NULL) < 0) {
|
||||
fprintf(stderr, "pulse_blocking_stream: failed to connect context\n");
|
||||
error = pa_context_errno(p->context);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
pa_threaded_mainloop_lock(p->mainloop);
|
||||
|
||||
if (pa_threaded_mainloop_start(p->mainloop) < 0)
|
||||
if (pa_threaded_mainloop_start(p->mainloop) < 0) {
|
||||
fprintf(stderr, "pulse_blocking_stream: failed to start main loop\n");
|
||||
goto unlock_and_fail;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
pa_context_state_t state;
|
||||
|
||||
state = pa_context_get_state(p->context);
|
||||
|
||||
if (state == PA_CONTEXT_READY)
|
||||
if (state == PA_CONTEXT_READY) {
|
||||
fprintf(stderr, "pulse_blocking_stream: context is ready\n");
|
||||
break;
|
||||
}
|
||||
|
||||
if (!PA_CONTEXT_IS_GOOD(state)) {
|
||||
fprintf(stderr, "pulse_blocking_stream: context is bad\n");
|
||||
error = pa_context_errno(p->context);
|
||||
goto unlock_and_fail;
|
||||
}
|
||||
@ -206,6 +221,7 @@ pa_blocking* pa_blocking_new(
|
||||
}
|
||||
|
||||
if (!(p->stream = pa_stream_new(p->context, stream_name, ss, map))) {
|
||||
fprintf(stderr, "pulse_blocking_stream: failed to create stream\n");
|
||||
error = pa_context_errno(p->context);
|
||||
goto unlock_and_fail;
|
||||
}
|
||||
@ -227,6 +243,7 @@ pa_blocking* pa_blocking_new(
|
||||
|PA_STREAM_AUTO_TIMING_UPDATE);
|
||||
|
||||
if (r < 0) {
|
||||
fprintf(stderr, "pulse_blocking_stream: failed to connect stream to output device\n");
|
||||
error = pa_context_errno(p->context);
|
||||
goto unlock_and_fail;
|
||||
}
|
||||
@ -236,10 +253,13 @@ pa_blocking* pa_blocking_new(
|
||||
|
||||
state = pa_stream_get_state(p->stream);
|
||||
|
||||
if (state == PA_STREAM_READY)
|
||||
if (state == PA_STREAM_READY) {
|
||||
fprintf(stderr, "pulse_blocking_stream: stream is ready\n");
|
||||
break;
|
||||
}
|
||||
|
||||
if (!PA_STREAM_IS_GOOD(state)) {
|
||||
fprintf(stderr, "pulse_blocking_stream: stream is bad\n");
|
||||
error = pa_context_errno(p->context);
|
||||
goto unlock_and_fail;
|
||||
}
|
||||
@ -249,13 +269,14 @@ pa_blocking* pa_blocking_new(
|
||||
}
|
||||
|
||||
pa_threaded_mainloop_unlock(p->mainloop);
|
||||
|
||||
fflush(stderr);
|
||||
return p;
|
||||
|
||||
unlock_and_fail:
|
||||
pa_threaded_mainloop_unlock(p->mainloop);
|
||||
|
||||
fail:
|
||||
fflush(stderr);
|
||||
if (rerror)
|
||||
*rerror = error;
|
||||
pa_blocking_free(p);
|
||||
|
Loading…
Reference in New Issue
Block a user