Compare commits
52 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d870ef0bd5 | ||
|
|
53d308dac5 | ||
|
|
89b06ae7c7 | ||
|
|
d38485a8ad | ||
|
|
834877c503 | ||
|
|
d033b79af4 | ||
|
|
daec2cc203 | ||
|
|
4e593cebab | ||
|
|
73d7701e94 | ||
|
|
24f1d7a72f | ||
|
|
6387a01d7b | ||
|
|
e838840548 | ||
|
|
6a430b441e | ||
|
|
7b977ea839 | ||
|
|
62b8521cbe | ||
|
|
308244d901 | ||
|
|
6f521183f9 | ||
|
|
76c6f7e733 | ||
|
|
80ebfbeb6b | ||
|
|
793901b319 | ||
|
|
3950df8ec9 | ||
|
|
e800b236aa | ||
|
|
4ab7871106 | ||
|
|
3de85549b6 | ||
|
|
73164f7182 | ||
|
|
004b000890 | ||
|
|
d9c703d944 | ||
|
|
364b650033 | ||
|
|
156eb874db | ||
|
|
1a28dd0311 | ||
|
|
e29b4b8609 | ||
|
|
c02997e6d9 | ||
|
|
7c9fc91af9 | ||
|
|
cf5198ac64 | ||
|
|
e76ddd6dd2 | ||
|
|
e3a4cf1cf5 | ||
|
|
5844616ea8 | ||
|
|
abeb580228 | ||
|
|
4d888dfce8 | ||
|
|
08ff6f0ede | ||
|
|
c458c27231 | ||
|
|
9821b70c38 | ||
|
|
8ab8401110 | ||
|
|
4f798c85cf | ||
|
|
1949bdb43f | ||
|
|
8cf2e6b31b | ||
|
|
b1069f9f18 | ||
|
|
fb8dfa9ae9 | ||
|
|
4402a56e94 | ||
|
|
f449808ba3 | ||
|
|
5599739433 | ||
|
|
197cf85f56 |
2
.github/FUNDING.yml
vendored
@@ -1 +1 @@
|
||||
custom: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=FRJUYV5QP6HW8
|
||||
github: jonaski
|
||||
|
||||
@@ -7,7 +7,6 @@ services:
|
||||
- docker
|
||||
compiler:
|
||||
- gcc
|
||||
- clang
|
||||
|
||||
before_install:
|
||||
- if ! [ "$DEPLOY_KEY_ENC" == "" ]; then
|
||||
|
||||
@@ -327,7 +327,7 @@ optional_component(IMOBILEDEVICE ON "Devices: iPhone, iPod Touch, iPad and Apple
|
||||
DEPENDS "libimobiledevice" LIBIMOBILEDEVICE_FOUND
|
||||
DEPENDS "libplist" LIBPLIST_FOUND
|
||||
DEPENDS "libusbmuxd" LIBUSBMUXD_FOUND
|
||||
DEPENDS "libgpod" LIBGPOD_FOUND
|
||||
DEPENDS "libgpod" HAVE_LIBGPOD
|
||||
)
|
||||
|
||||
optional_component(SPARKLE ON "Sparkle integration"
|
||||
|
||||
18
Changelog
@@ -2,6 +2,24 @@ Strawberry Music Player
|
||||
=======================
|
||||
ChangeLog
|
||||
|
||||
Version 0.6.6:
|
||||
|
||||
* Fixed lowercased album artist in playlist column
|
||||
* Fixed compiling with different optional features turned off
|
||||
* Fixed hang in stream discoverer with certain formats
|
||||
* Fixed Subsonic to correctly read disc
|
||||
* Fixed preventing system logoff or shutdown
|
||||
* Fixed correctly updating compilations
|
||||
* Simplified gstreamer pipeline code
|
||||
* Disabled showing analyzer for bit depths not supported by the analyzer
|
||||
* Made stereo balancer independent from equalizer
|
||||
* Added option to prefer album artist when sending scrobbles
|
||||
* Removed lines in settings
|
||||
* Added limit for number of tagreader processes to 2
|
||||
* Improved system theme icon option to better pick correct icons
|
||||
* Added option to automatically select current playing track
|
||||
* (Windows) Added support for WASAPI
|
||||
|
||||
Version 0.6.5:
|
||||
|
||||
* Fixed scrobbler not to send scrobbles multiple times when metadata is updated
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from jonaski/opensuse:tumbleweed
|
||||
from jonaski/opensuse:lp151
|
||||
|
||||
run mkdir -p /usr/src/app
|
||||
workdir /usr/src/app
|
||||
|
||||
@@ -25,8 +25,11 @@ Strawberry is a music player and music collection organizer. It is a fork of Cle
|
||||
* Audio analyzer
|
||||
* Audio equalizer
|
||||
* Transfer music to iPod, iPhone, MTP or mass-storage USB player
|
||||
* Streaming support for Tidal, Qobuz and Subsonic
|
||||
* Subsonic streaming support
|
||||
* Unofficial streaming support for Tidal and Qobuz
|
||||
* Scrobbler with support for Last.fm, Libre.fm and ListenBrainz
|
||||
|
||||
**Tidal and Qobuz streaming in Strawberry is unofficial. You need an official API token (or App ID/Secret) to use it, we can not provide API tokens, or help getting them. Tidal will not work with Tidal Masters (MQA), because MQA is a proprietary format in lossy quality without an open source decoder, we can't support it.**
|
||||
|
||||
It has so far been tested to work on Linux, OpenBSD, macOS and Windows.
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
set(STRAWBERRY_VERSION_MAJOR 0)
|
||||
set(STRAWBERRY_VERSION_MINOR 6)
|
||||
set(STRAWBERRY_VERSION_PATCH 5)
|
||||
set(STRAWBERRY_VERSION_PATCH 6)
|
||||
#set(STRAWBERRY_VERSION_PRERELEASE rc1)
|
||||
|
||||
set(INCLUDE_GIT_REVISION OFF)
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<file>icons/128x128/applications-internet.png</file>
|
||||
<file>icons/128x128/bluetooth.png</file>
|
||||
<file>icons/128x128/cdcase.png</file>
|
||||
<file>icons/128x128/cd.png</file>
|
||||
<file>icons/128x128/media-optical.png</file>
|
||||
<file>icons/128x128/configure.png</file>
|
||||
<file>icons/128x128/device-ipod-nano.png</file>
|
||||
<file>icons/128x128/device-ipod.png</file>
|
||||
@@ -57,9 +57,9 @@
|
||||
<file>icons/128x128/mcintosh-player.png</file>
|
||||
<file>icons/128x128/mcintosh-text.png</file>
|
||||
<file>icons/128x128/media-eject.png</file>
|
||||
<file>icons/128x128/media-pause.png</file>
|
||||
<file>icons/128x128/media-play.png</file>
|
||||
<file>icons/128x128/media-stop.png</file>
|
||||
<file>icons/128x128/media-playback-pause.png</file>
|
||||
<file>icons/128x128/media-playback-start.png</file>
|
||||
<file>icons/128x128/media-playback-stop.png</file>
|
||||
<file>icons/128x128/media-skip-forward.png</file>
|
||||
<file>icons/128x128/media-skip-backward.png</file>
|
||||
<file>icons/128x128/media-seek-forward.png</file>
|
||||
@@ -79,7 +79,7 @@
|
||||
<file>icons/128x128/view-media-playlist.png</file>
|
||||
<file>icons/128x128/view-media-visualization.png</file>
|
||||
<file>icons/128x128/view-refresh.png</file>
|
||||
<file>icons/128x128/vinyl.png</file>
|
||||
<file>icons/128x128/library-music.png</file>
|
||||
<file>icons/128x128/vlc.png</file>
|
||||
<file>icons/128x128/xine.png</file>
|
||||
<file>icons/128x128/zoom-in.png</file>
|
||||
@@ -97,7 +97,7 @@
|
||||
<file>icons/64x64/applications-internet.png</file>
|
||||
<file>icons/64x64/bluetooth.png</file>
|
||||
<file>icons/64x64/cdcase.png</file>
|
||||
<file>icons/64x64/cd.png</file>
|
||||
<file>icons/64x64/media-optical.png</file>
|
||||
<file>icons/64x64/configure.png</file>
|
||||
<file>icons/64x64/device-ipod-nano.png</file>
|
||||
<file>icons/64x64/device-ipod.png</file>
|
||||
@@ -148,9 +148,9 @@
|
||||
<file>icons/64x64/mcintosh-player.png</file>
|
||||
<file>icons/64x64/mcintosh-text.png</file>
|
||||
<file>icons/64x64/media-eject.png</file>
|
||||
<file>icons/64x64/media-pause.png</file>
|
||||
<file>icons/64x64/media-play.png</file>
|
||||
<file>icons/64x64/media-stop.png</file>
|
||||
<file>icons/64x64/media-playback-pause.png</file>
|
||||
<file>icons/64x64/media-playback-start.png</file>
|
||||
<file>icons/64x64/media-playback-stop.png</file>
|
||||
<file>icons/64x64/media-skip-forward.png</file>
|
||||
<file>icons/64x64/media-skip-backward.png</file>
|
||||
<file>icons/64x64/media-seek-forward.png</file>
|
||||
@@ -171,7 +171,7 @@
|
||||
<file>icons/64x64/view-media-playlist.png</file>
|
||||
<file>icons/64x64/view-media-visualization.png</file>
|
||||
<file>icons/64x64/view-refresh.png</file>
|
||||
<file>icons/64x64/vinyl.png</file>
|
||||
<file>icons/64x64/library-music.png</file>
|
||||
<file>icons/64x64/vlc.png</file>
|
||||
<file>icons/64x64/xine.png</file>
|
||||
<file>icons/64x64/zoom-in.png</file>
|
||||
@@ -189,7 +189,7 @@
|
||||
<file>icons/48x48/applications-internet.png</file>
|
||||
<file>icons/48x48/bluetooth.png</file>
|
||||
<file>icons/48x48/cdcase.png</file>
|
||||
<file>icons/48x48/cd.png</file>
|
||||
<file>icons/48x48/media-optical.png</file>
|
||||
<file>icons/48x48/configure.png</file>
|
||||
<file>icons/48x48/device-ipod-nano.png</file>
|
||||
<file>icons/48x48/device-ipod.png</file>
|
||||
@@ -241,11 +241,11 @@
|
||||
<file>icons/48x48/mcintosh.png</file>
|
||||
<file>icons/48x48/mcintosh-text.png</file>
|
||||
<file>icons/48x48/media-eject.png</file>
|
||||
<file>icons/48x48/media-pause.png</file>
|
||||
<file>icons/48x48/media-playback-pause.png</file>
|
||||
<file>icons/48x48/media-playlist-repeat.png</file>
|
||||
<file>icons/48x48/media-playlist-shuffle.png</file>
|
||||
<file>icons/48x48/media-play.png</file>
|
||||
<file>icons/48x48/media-stop.png</file>
|
||||
<file>icons/48x48/media-playback-start.png</file>
|
||||
<file>icons/48x48/media-playback-stop.png</file>
|
||||
<file>icons/48x48/media-skip-forward.png</file>
|
||||
<file>icons/48x48/media-skip-backward.png</file>
|
||||
<file>icons/48x48/media-seek-forward.png</file>
|
||||
@@ -266,7 +266,7 @@
|
||||
<file>icons/48x48/view-media-playlist.png</file>
|
||||
<file>icons/48x48/view-media-visualization.png</file>
|
||||
<file>icons/48x48/view-refresh.png</file>
|
||||
<file>icons/48x48/vinyl.png</file>
|
||||
<file>icons/48x48/library-music.png</file>
|
||||
<file>icons/48x48/vlc.png</file>
|
||||
<file>icons/48x48/xine.png</file>
|
||||
<file>icons/48x48/zoom-in.png</file>
|
||||
@@ -284,7 +284,7 @@
|
||||
<file>icons/32x32/applications-internet.png</file>
|
||||
<file>icons/32x32/bluetooth.png</file>
|
||||
<file>icons/32x32/cdcase.png</file>
|
||||
<file>icons/32x32/cd.png</file>
|
||||
<file>icons/32x32/media-optical.png</file>
|
||||
<file>icons/32x32/configure.png</file>
|
||||
<file>icons/32x32/device-ipod-nano.png</file>
|
||||
<file>icons/32x32/device-ipod.png</file>
|
||||
@@ -336,11 +336,11 @@
|
||||
<file>icons/32x32/mcintosh.png</file>
|
||||
<file>icons/32x32/mcintosh-text.png</file>
|
||||
<file>icons/32x32/media-eject.png</file>
|
||||
<file>icons/32x32/media-pause.png</file>
|
||||
<file>icons/32x32/media-playback-pause.png</file>
|
||||
<file>icons/32x32/media-playlist-repeat.png</file>
|
||||
<file>icons/32x32/media-playlist-shuffle.png</file>
|
||||
<file>icons/32x32/media-play.png</file>
|
||||
<file>icons/32x32/media-stop.png</file>
|
||||
<file>icons/32x32/media-playback-start.png</file>
|
||||
<file>icons/32x32/media-playback-stop.png</file>
|
||||
<file>icons/32x32/media-skip-forward.png</file>
|
||||
<file>icons/32x32/media-skip-backward.png</file>
|
||||
<file>icons/32x32/media-seek-forward.png</file>
|
||||
@@ -361,7 +361,7 @@
|
||||
<file>icons/32x32/view-media-playlist.png</file>
|
||||
<file>icons/32x32/view-media-visualization.png</file>
|
||||
<file>icons/32x32/view-refresh.png</file>
|
||||
<file>icons/32x32/vinyl.png</file>
|
||||
<file>icons/32x32/library-music.png</file>
|
||||
<file>icons/32x32/vlc.png</file>
|
||||
<file>icons/32x32/xine.png</file>
|
||||
<file>icons/32x32/zoom-in.png</file>
|
||||
@@ -379,7 +379,7 @@
|
||||
<file>icons/22x22/applications-internet.png</file>
|
||||
<file>icons/22x22/bluetooth.png</file>
|
||||
<file>icons/22x22/cdcase.png</file>
|
||||
<file>icons/22x22/cd.png</file>
|
||||
<file>icons/22x22/media-optical.png</file>
|
||||
<file>icons/22x22/configure.png</file>
|
||||
<file>icons/22x22/device-ipod-nano.png</file>
|
||||
<file>icons/22x22/device-ipod.png</file>
|
||||
@@ -431,11 +431,11 @@
|
||||
<file>icons/22x22/mcintosh.png</file>
|
||||
<file>icons/22x22/mcintosh-text.png</file>
|
||||
<file>icons/22x22/media-eject.png</file>
|
||||
<file>icons/22x22/media-pause.png</file>
|
||||
<file>icons/22x22/media-playback-pause.png</file>
|
||||
<file>icons/22x22/media-playlist-repeat.png</file>
|
||||
<file>icons/22x22/media-playlist-shuffle.png</file>
|
||||
<file>icons/22x22/media-play.png</file>
|
||||
<file>icons/22x22/media-stop.png</file>
|
||||
<file>icons/22x22/media-playback-start.png</file>
|
||||
<file>icons/22x22/media-playback-stop.png</file>
|
||||
<file>icons/22x22/media-skip-forward.png</file>
|
||||
<file>icons/22x22/media-skip-backward.png</file>
|
||||
<file>icons/22x22/media-seek-forward.png</file>
|
||||
@@ -456,7 +456,7 @@
|
||||
<file>icons/22x22/view-media-playlist.png</file>
|
||||
<file>icons/22x22/view-media-visualization.png</file>
|
||||
<file>icons/22x22/view-refresh.png</file>
|
||||
<file>icons/22x22/vinyl.png</file>
|
||||
<file>icons/22x22/library-music.png</file>
|
||||
<file>icons/22x22/vlc.png</file>
|
||||
<file>icons/22x22/xine.png</file>
|
||||
<file>icons/22x22/zoom-in.png</file>
|
||||
|
||||
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 8.9 KiB After Width: | Height: | Size: 8.9 KiB |
|
Before Width: | Height: | Size: 8.8 KiB After Width: | Height: | Size: 8.8 KiB |
|
Before Width: | Height: | Size: 8.8 KiB After Width: | Height: | Size: 8.8 KiB |
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 908 B After Width: | Height: | Size: 908 B |
|
Before Width: | Height: | Size: 921 B After Width: | Height: | Size: 921 B |
|
Before Width: | Height: | Size: 921 B After Width: | Height: | Size: 921 B |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 6.0 KiB After Width: | Height: | Size: 6.0 KiB |
|
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 162 KiB After Width: | Height: | Size: 162 KiB |
BIN
data/icons/full/love.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 1.2 MiB After Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 8.9 KiB After Width: | Height: | Size: 8.9 KiB |
|
Before Width: | Height: | Size: 8.8 KiB After Width: | Height: | Size: 8.8 KiB |
|
Before Width: | Height: | Size: 8.8 KiB After Width: | Height: | Size: 8.8 KiB |
BIN
data/icons/full/subsonic.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
114
dist/scripts/gen-icons-resource.sh
vendored
@@ -1,114 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
sizes="128x128 64x64 48x48 32x32 22x22"
|
||||
|
||||
#
|
||||
|
||||
for i in full/*
|
||||
do
|
||||
source=$i
|
||||
file=`basename $i`
|
||||
|
||||
id=`identify "$i"` || exit 1
|
||||
if [ "$id" = "" ] ; then
|
||||
echo "ERROR: Cannot determine format and geometry for image: \"$i\"."
|
||||
continue
|
||||
fi
|
||||
g=`echo $id | awk '{print $3}'` || exit 1
|
||||
if [ "$g" = "" ] ; then
|
||||
echo "ERROR: Cannot determine geometry for image: \"$i\"."
|
||||
continue
|
||||
fi
|
||||
|
||||
# Geometry can be 563x144+0+0 or 75x98
|
||||
# we need to get rid of the plus (+) and the x characters:
|
||||
w=`echo $g | sed 's/[^0-9]/ /g' | awk '{print $1}'` || exit 1
|
||||
if [ "$w" = "" ] ; then
|
||||
echo "ERROR: Cannot determine width for image: \"$x\"."
|
||||
continue
|
||||
fi
|
||||
h=`echo $g | sed 's/[^0-9]/ /g' | awk '{print $2}'` || exit 1
|
||||
if [ "$h" = "" ] ; then
|
||||
echo "ERROR: Cannot determine height for image: \"$x\"."
|
||||
continue
|
||||
fi
|
||||
|
||||
for x in $sizes
|
||||
do
|
||||
|
||||
dest="$x/$file"
|
||||
if [ -f $dest ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
x_w=$(echo $x | cut -d 'x' -f1)
|
||||
x_h=$(echo $x | cut -d 'x' -f2)
|
||||
|
||||
if [ "$w" -lt "$x_w" ] || [ "$h" -lt "$x_h" ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
echo "convert -verbose -resize $x $source $dest"
|
||||
convert -verbose -resize $x $source $dest
|
||||
|
||||
done
|
||||
done
|
||||
|
||||
|
||||
for i in $sizes
|
||||
do
|
||||
for x in $i/*
|
||||
do
|
||||
file=`basename $x`
|
||||
if ! [ -f "full/$file" ]; then
|
||||
echo "Warning: full/$file does not exist, but $x exists."
|
||||
fi
|
||||
|
||||
id=`identify "$x"` || exit 1
|
||||
if [ "$id" = "" ] ; then
|
||||
echo "ERROR: Cannot determine format and geometry for image: \"$x\"."
|
||||
continue
|
||||
fi
|
||||
g=`echo $id | awk '{print $3}'` || exit 1
|
||||
if [ "$g" = "" ] ; then
|
||||
echo "ERROR: Cannot determine geometry for image: \"$x\"."
|
||||
continue
|
||||
fi
|
||||
|
||||
# Geometry can be 563x144+0+0 or 75x98
|
||||
# we need to get rid of the plus (+) and the x characters:
|
||||
w=`echo $g | sed 's/[^0-9]/ /g' | awk '{print $1}'` || exit 1
|
||||
if [ "$w" = "" ] ; then
|
||||
echo "ERROR: Cannot determine width for image: \"$x\"."
|
||||
continue
|
||||
fi
|
||||
h=`echo $g | sed 's/[^0-9]/ /g' | awk '{print $2}'` || exit 1
|
||||
if [ "$h" = "" ] ; then
|
||||
echo "ERROR: Cannot determine height for image: \"$x\"."
|
||||
continue
|
||||
fi
|
||||
|
||||
if ! [ "${h}x${w}" = "$i" ]; then
|
||||
echo "Warning: $x is not $i, but ${h}x${w}!"
|
||||
fi
|
||||
|
||||
done
|
||||
done
|
||||
|
||||
file="../icons.qrc"
|
||||
rm -rf "$file"
|
||||
echo "<RCC>" >>$file
|
||||
echo "<qresource prefix=\"/\">" >>$file
|
||||
|
||||
for i in full $sizes
|
||||
do
|
||||
for x in $i/*
|
||||
do
|
||||
f=`basename $x`
|
||||
echo " <file>icons/$i/$f</file>" >>$file
|
||||
done
|
||||
done
|
||||
|
||||
echo "</qresource>" >>$file
|
||||
echo "</RCC>" >>$file
|
||||
|
||||
1
dist/scripts/maketarball.sh.in
vendored
@@ -24,6 +24,7 @@ tar -cJf $name-$version.tar.xz \
|
||||
--exclude=".directory" \
|
||||
--exclude="*.spec" \
|
||||
--exclude="*.nsi" \
|
||||
--exclude="$root/.github" \
|
||||
--exclude="$root/Dockerfile" \
|
||||
--exclude="$root/.travis.yml" \
|
||||
--exclude="$root/CMakeLists.txt.user" \
|
||||
|
||||
137
dist/scripts/verify-icons.sh
vendored
Executable file
@@ -0,0 +1,137 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Strawberry Music Player
|
||||
# Copyright 2019, Jonas Kvinge <jonas@jkvinge.net>
|
||||
#
|
||||
# Strawberry is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Strawberry is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
sizes="128x128 64x64 48x48 32x32 22x22"
|
||||
|
||||
#
|
||||
|
||||
#for i in full/*
|
||||
#do
|
||||
# source=$i
|
||||
# file=`basename $i`
|
||||
|
||||
# id=`identify "$i"` || exit 1
|
||||
# if [ "$id" = "" ] ; then
|
||||
# echo "ERROR: Cannot determine format and geometry for image: \"$i\"."
|
||||
# continue
|
||||
# fi
|
||||
# g=`echo $id | awk '{print $3}'` || exit 1
|
||||
# if [ "$g" = "" ] ; then
|
||||
# echo "ERROR: Cannot determine geometry for image: \"$i\"."
|
||||
# continue
|
||||
# fi
|
||||
|
||||
# Geometry can be 563x144+0+0 or 75x98
|
||||
# we need to get rid of the plus (+) and the x characters:
|
||||
# w=`echo $g | sed 's/[^0-9]/ /g' | awk '{print $1}'` || exit 1
|
||||
# if [ "$w" = "" ] ; then
|
||||
# echo "ERROR: Cannot determine width for image: \"$x\"."
|
||||
# continue
|
||||
# fi
|
||||
# h=`echo $g | sed 's/[^0-9]/ /g' | awk '{print $2}'` || exit 1
|
||||
# if [ "$h" = "" ] ; then
|
||||
# echo "ERROR: Cannot determine height for image: \"$x\"."
|
||||
# continue
|
||||
# fi
|
||||
|
||||
# for x in $sizes
|
||||
# do
|
||||
|
||||
# dest="$x/$file"
|
||||
# if [ -f $dest ]; then
|
||||
# continue
|
||||
# fi
|
||||
|
||||
# x_w=$(echo $x | cut -d 'x' -f1)
|
||||
# x_h=$(echo $x | cut -d 'x' -f2)
|
||||
|
||||
# if [ "$w" -lt "$x_w" ] || [ "$h" -lt "$x_h" ]; then
|
||||
# continue
|
||||
# fi
|
||||
|
||||
#echo "convert -verbose -resize $x $source $dest"
|
||||
#convert -verbose -resize $x $source $dest
|
||||
|
||||
# done
|
||||
#done
|
||||
|
||||
|
||||
for i in $sizes
|
||||
do
|
||||
for x in $i/*
|
||||
do
|
||||
file=`basename $x`
|
||||
|
||||
for y in $sizes
|
||||
do
|
||||
if [ "$y" = "$i" ]; then
|
||||
continue
|
||||
fi
|
||||
if ! [ -f "$y/$file" ]; then
|
||||
echo "Warning: $y/$file does not exist, but $x exists."
|
||||
fi
|
||||
done
|
||||
|
||||
id=`identify "$x"` || exit 1
|
||||
if [ "$id" = "" ] ; then
|
||||
echo "ERROR: Cannot determine format and geometry for image: \"$x\"."
|
||||
continue
|
||||
fi
|
||||
g=`echo $id | awk '{print $3}'` || exit 1
|
||||
if [ "$g" = "" ] ; then
|
||||
echo "ERROR: Cannot determine geometry for image: \"$x\"."
|
||||
continue
|
||||
fi
|
||||
|
||||
# Geometry can be 563x144+0+0 or 75x98
|
||||
# we need to get rid of the plus (+) and the x characters:
|
||||
w=`echo $g | sed 's/[^0-9]/ /g' | awk '{print $1}'` || exit 1
|
||||
if [ "$w" = "" ] ; then
|
||||
echo "ERROR: Cannot determine width for image: \"$x\"."
|
||||
continue
|
||||
fi
|
||||
h=`echo $g | sed 's/[^0-9]/ /g' | awk '{print $2}'` || exit 1
|
||||
if [ "$h" = "" ] ; then
|
||||
echo "ERROR: Cannot determine height for image: \"$x\"."
|
||||
continue
|
||||
fi
|
||||
|
||||
if ! [ "${h}x${w}" = "$i" ]; then
|
||||
echo "Warning: $x is not $i, but ${h}x${w}!"
|
||||
fi
|
||||
|
||||
done
|
||||
done
|
||||
|
||||
#file="../icons.qrc"
|
||||
#rm -rf "$file"
|
||||
#echo "<RCC>" >>$file
|
||||
#echo "<qresource prefix=\"/\">" >>$file
|
||||
|
||||
#for i in full $sizes
|
||||
#do
|
||||
# for x in $i/*
|
||||
# do
|
||||
# f=`basename $x`
|
||||
# echo " <file>icons/$i/$f</file>" >>$file
|
||||
# done
|
||||
#done
|
||||
|
||||
#echo "</qresource>" >>$file
|
||||
#echo "</RCC>" >>$file
|
||||
|
||||
6
dist/windows/strawberry.nsi.in
vendored
@@ -199,7 +199,7 @@ Section "Strawberry" Strawberry
|
||||
File "libpcre-1.dll"
|
||||
File "libpcre2-16-0.dll"
|
||||
File "libpng16-16.dll"
|
||||
File "libprotobuf-20.dll"
|
||||
File "libprotobuf-21.dll"
|
||||
File "libsoup-2.4-1.dll"
|
||||
File "libspeex-1.dll"
|
||||
File "libsqlite3-0.dll"
|
||||
@@ -318,6 +318,7 @@ Section "Gstreamer plugins" gstreamer-plugins
|
||||
File "/oname=libgsttypefindfunctions.dll" "gstreamer-plugins\libgsttypefindfunctions.dll"
|
||||
File "/oname=libgstgio.dll" "gstreamer-plugins\libgstgio.dll"
|
||||
File "/oname=libgstdirectsound.dll" "gstreamer-plugins\libgstdirectsound.dll"
|
||||
File "/oname=libgstwasapi.dll" "gstreamer-plugins\libgstwasapi.dll"
|
||||
File "/oname=libgstapetag.dll" "gstreamer-plugins\libgstapetag.dll"
|
||||
File "/oname=libgsticydemux.dll" "gstreamer-plugins\libgsticydemux.dll"
|
||||
File "/oname=libgstid3demux.dll" "gstreamer-plugins\libgstid3demux.dll"
|
||||
@@ -475,7 +476,7 @@ Section "Uninstall"
|
||||
Delete "$INSTDIR\libpcre-1.dll"
|
||||
Delete "$INSTDIR\libpcre2-16-0.dll"
|
||||
Delete "$INSTDIR\libpng16-16.dll"
|
||||
Delete "$INSTDIR\libprotobuf-20.dll"
|
||||
Delete "$INSTDIR\libprotobuf-21.dll"
|
||||
Delete "$INSTDIR\libsoup-2.4-1.dll"
|
||||
Delete "$INSTDIR\libspeex-1.dll"
|
||||
Delete "$INSTDIR\libsqlite3-0.dll"
|
||||
@@ -545,6 +546,7 @@ Section "Uninstall"
|
||||
Delete "$INSTDIR\gstreamer-plugins\libgsttypefindfunctions.dll"
|
||||
Delete "$INSTDIR\gstreamer-plugins\libgstgio.dll"
|
||||
Delete "$INSTDIR\gstreamer-plugins\libgstdirectsound.dll"
|
||||
Delete "$INSTDIR\gstreamer-plugins\libgstwasapi.dll"
|
||||
Delete "$INSTDIR\gstreamer-plugins\libgstapetag.dll"
|
||||
Delete "$INSTDIR\gstreamer-plugins\libgsticydemux.dll"
|
||||
Delete "$INSTDIR\gstreamer-plugins\libgstid3demux.dll"
|
||||
|
||||
@@ -158,11 +158,13 @@ WorkerPool<HandlerType>::WorkerPool(QObject *parent)
|
||||
: _WorkerPoolBase(parent),
|
||||
next_worker_(0),
|
||||
next_id_(0) {
|
||||
|
||||
worker_count_ = qBound(1, QThread::idealThreadCount() / 2, 2);
|
||||
local_server_name_ = qApp->applicationName().toLower();
|
||||
|
||||
if (local_server_name_.isEmpty())
|
||||
local_server_name_ = "workerpool";
|
||||
|
||||
}
|
||||
|
||||
template <typename HandlerType>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: strawberry
|
||||
version: '0.6.5+git'
|
||||
version: '0.6.6+git'
|
||||
summary: music player and collection organizer
|
||||
description: |
|
||||
Strawberry is a music player and collection organizer.
|
||||
|
||||
@@ -121,7 +121,7 @@ set(SOURCES
|
||||
|
||||
engine/enginetype.cpp
|
||||
engine/enginebase.cpp
|
||||
engine/enginedevice.cpp
|
||||
engine/devicefinders.cpp
|
||||
engine/devicefinder.cpp
|
||||
|
||||
analyzer/fht.cpp
|
||||
@@ -315,7 +315,7 @@ set(HEADERS
|
||||
core/mimedata.h
|
||||
|
||||
engine/enginebase.h
|
||||
engine/enginedevice.h
|
||||
engine/devicefinders.h
|
||||
|
||||
analyzer/analyzerbase.h
|
||||
analyzer/analyzercontainer.h
|
||||
@@ -877,6 +877,7 @@ endif()
|
||||
optional_source(WIN32
|
||||
SOURCES
|
||||
engine/directsounddevicefinder.cpp
|
||||
engine/mmdevicefinder.cpp
|
||||
widgets/osd_win.cpp
|
||||
core/windows7thumbbar.cpp
|
||||
HEADERS
|
||||
|
||||
@@ -904,28 +904,30 @@ void CollectionBackend::UpdateCompilations() {
|
||||
while (q.next()) {
|
||||
QString artist = q.value(0).toString();
|
||||
QString album = q.value(1).toString();
|
||||
QString filename = q.value(2).toString();
|
||||
QUrl url = QUrl::fromEncoded(q.value(2).toString().toUtf8());
|
||||
bool compilation_detected = q.value(3).toBool();
|
||||
|
||||
// Ignore songs that don't have an album field set
|
||||
if (album.isEmpty()) continue;
|
||||
|
||||
// Find the directory the song is in
|
||||
int last_separator = filename.lastIndexOf('/');
|
||||
if (last_separator == -1) continue;
|
||||
QString directory = url.toString(QUrl::PreferLocalFile|QUrl::RemoveFilename|QUrl::StripTrailingSlash);
|
||||
|
||||
CompilationInfo &info = compilation_info[album];
|
||||
CompilationInfo &info = compilation_info[directory + album];
|
||||
info.urls << url;
|
||||
info.directory = directory;
|
||||
info.album = album;
|
||||
info.artists.insert(artist);
|
||||
info.directories.insert(filename.left(last_separator));
|
||||
if (compilation_detected) info.has_compilation_detected = true;
|
||||
else info.has_not_compilation_detected = true;
|
||||
if (compilation_detected) info.has_compilation_detected++;
|
||||
else info.has_not_compilation_detected++;
|
||||
}
|
||||
|
||||
// Now mark the songs that we think are in compilations
|
||||
QSqlQuery update(db);
|
||||
update.prepare(QString("UPDATE %1 SET compilation_detected = :compilation_detected, compilation_effective = ((compilation OR :compilation_detected OR compilation_on) AND NOT compilation_off) + 0 WHERE album = :album AND unavailable = 0").arg(songs_table_));
|
||||
QSqlQuery find_songs(db);
|
||||
find_songs.prepare(QString("SELECT ROWID, " + Song::kColumnSpec + " FROM %1 WHERE album = :album AND compilation_detected = :compilation_detected AND unavailable = 0").arg(songs_table_));
|
||||
find_songs.prepare(QString("SELECT ROWID, " + Song::kColumnSpec + " FROM %1 WHERE url = :url AND compilation_detected = :compilation_detected AND unavailable = 0").arg(songs_table_));
|
||||
|
||||
QSqlQuery update_songs(db);
|
||||
update_songs.prepare(QString("UPDATE %1 SET compilation_detected = :compilation_detected, compilation_effective = ((compilation OR :compilation_detected OR compilation_on) AND NOT compilation_off) + 0 WHERE url = :url AND unavailable = 0").arg(songs_table_));
|
||||
|
||||
SongList deleted_songs;
|
||||
SongList added_songs;
|
||||
@@ -935,17 +937,18 @@ void CollectionBackend::UpdateCompilations() {
|
||||
QMap<QString, CompilationInfo>::const_iterator it = compilation_info.constBegin();
|
||||
for (; it != compilation_info.constEnd(); ++it) {
|
||||
const CompilationInfo &info = it.value();
|
||||
QString album(it.key());
|
||||
|
||||
// If there were more 'effective album artists' than there were directories for this album then it's a compilation.
|
||||
|
||||
if (info.artists.count() > info.directories.count()) {
|
||||
if (info.has_not_compilation_detected)
|
||||
UpdateCompilations(find_songs, update, deleted_songs, added_songs, album, 1);
|
||||
}
|
||||
else {
|
||||
if (info.has_compilation_detected)
|
||||
UpdateCompilations(find_songs, update, deleted_songs, added_songs, album, 0);
|
||||
for (const QUrl &url : info.urls) {
|
||||
if (info.artists.count() > 1) { // This directory+album is a compilation.
|
||||
if (info.has_not_compilation_detected > 0) // Run updates if any of the songs is not marked as compilations.
|
||||
UpdateCompilations(find_songs, update_songs, deleted_songs, added_songs, url, true);
|
||||
}
|
||||
else {
|
||||
if (info.has_compilation_detected > 0)
|
||||
UpdateCompilations(find_songs, update_songs, deleted_songs, added_songs, url, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -955,27 +958,28 @@ void CollectionBackend::UpdateCompilations() {
|
||||
emit SongsDeleted(deleted_songs);
|
||||
emit SongsDiscovered(added_songs);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void CollectionBackend::UpdateCompilations(QSqlQuery &find_songs, QSqlQuery &update, SongList &deleted_songs, SongList &added_songs, const QString &album, int compilation_detected) {
|
||||
void CollectionBackend::UpdateCompilations(QSqlQuery &find_songs, QSqlQuery &update_songs, SongList &deleted_songs, SongList &added_songs, const QUrl &url, const bool compilation_detected) {
|
||||
|
||||
// Get songs that were already in that album, so we can tell the model they've been updated
|
||||
find_songs.bindValue(":album", album);
|
||||
// Get song, so we can tell the model its updated
|
||||
find_songs.bindValue(":url", url.toString());
|
||||
find_songs.bindValue(":compilation_detected", int(!compilation_detected));
|
||||
find_songs.exec();
|
||||
while (find_songs.next()) {
|
||||
Song song;
|
||||
song.InitFromQuery(find_songs, true);
|
||||
deleted_songs << song;
|
||||
song.set_compilation_detected(true);
|
||||
song.set_compilation_detected(compilation_detected);
|
||||
added_songs << song;
|
||||
}
|
||||
|
||||
// Mark this album
|
||||
update.bindValue(":compilation_detected", compilation_detected);
|
||||
update.bindValue(":album", album);
|
||||
update.exec();
|
||||
db_->CheckErrors(update);
|
||||
// Update the song
|
||||
update_songs.bindValue(":compilation_detected", int(compilation_detected));
|
||||
update_songs.bindValue(":url", url);
|
||||
update_songs.exec();
|
||||
db_->CheckErrors(update_songs);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -225,16 +225,18 @@ class CollectionBackend : public CollectionBackendInterface {
|
||||
|
||||
private:
|
||||
struct CompilationInfo {
|
||||
CompilationInfo() : has_compilation_detected(false), has_not_compilation_detected(false) {}
|
||||
CompilationInfo() : has_compilation_detected(0), has_not_compilation_detected(0) {}
|
||||
|
||||
QString directory;
|
||||
QString album;
|
||||
QList<QUrl> urls;
|
||||
QSet<QString> artists;
|
||||
QSet<QString> directories;
|
||||
|
||||
bool has_compilation_detected;
|
||||
bool has_not_compilation_detected;
|
||||
int has_compilation_detected;
|
||||
int has_not_compilation_detected;
|
||||
};
|
||||
|
||||
void UpdateCompilations(QSqlQuery &find_songs, QSqlQuery &update, SongList &deleted_songs, SongList &added_songs, const QString &album, int compilation_detected);
|
||||
void UpdateCompilations(QSqlQuery &find_songs, QSqlQuery &update_songs, SongList &deleted_songs, SongList &added_songs, const QUrl &url, const bool compilation_detected);
|
||||
AlbumList GetAlbums(const QString &artist, const QString &album_artist, bool compilation = false, const QueryOptions &opt = QueryOptions());
|
||||
AlbumList GetAlbums(const QString &artist, bool compilation, const QueryOptions &opt = QueryOptions());
|
||||
SubdirectoryList SubdirsInDirectory(int id, QSqlDatabase &db);
|
||||
|
||||
@@ -449,7 +449,6 @@ void CollectionModel::SongsDeleted(const SongList &songs) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
beginRemoveRows(idx, node->row, node->row);
|
||||
node->parent->Delete(node->row);
|
||||
song_nodes_.remove(song.id());
|
||||
|
||||
@@ -330,8 +330,8 @@ void CollectionView::contextMenuEvent(QContextMenuEvent *e) {
|
||||
|
||||
if (!context_menu_) {
|
||||
context_menu_ = new QMenu(this);
|
||||
add_to_playlist_ = context_menu_->addAction(IconLoader::Load("media-play"), tr("Append to current playlist"), this, SLOT(AddToPlaylist()));
|
||||
load_ = context_menu_->addAction(IconLoader::Load("media-play"), tr("Replace current playlist"), this, SLOT(Load()));
|
||||
add_to_playlist_ = context_menu_->addAction(IconLoader::Load("media-playback-start"), tr("Append to current playlist"), this, SLOT(AddToPlaylist()));
|
||||
load_ = context_menu_->addAction(IconLoader::Load("media-playback-start"), tr("Replace current playlist"), this, SLOT(Load()));
|
||||
open_in_new_playlist_ = context_menu_->addAction(IconLoader::Load("document-new"), tr("Open in new playlist"), this, SLOT(OpenInNewPlaylist()));
|
||||
|
||||
context_menu_->addSeparator();
|
||||
|
||||
@@ -355,8 +355,8 @@ void ContextAlbumsView::contextMenuEvent(QContextMenuEvent *e) {
|
||||
if (!context_menu_) {
|
||||
context_menu_ = new QMenu(this);
|
||||
|
||||
add_to_playlist_ = context_menu_->addAction(IconLoader::Load("media-play"), tr("Append to current playlist"), this, SLOT(AddToPlaylist()));
|
||||
load_ = context_menu_->addAction(IconLoader::Load("media-play"), tr("Replace current playlist"), this, SLOT(Load()));
|
||||
add_to_playlist_ = context_menu_->addAction(IconLoader::Load("media-playback-start"), tr("Append to current playlist"), this, SLOT(AddToPlaylist()));
|
||||
load_ = context_menu_->addAction(IconLoader::Load("media-playback-start"), tr("Replace current playlist"), this, SLOT(Load()));
|
||||
open_in_new_playlist_ = context_menu_->addAction(IconLoader::Load("document-new"), tr("Open in new playlist"), this, SLOT(OpenInNewPlaylist()));
|
||||
|
||||
context_menu_->addSeparator();
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
#include "engine/engine_fwd.h"
|
||||
#include "engine/enginebase.h"
|
||||
#include "engine/enginetype.h"
|
||||
#include "engine/enginedevice.h"
|
||||
#include "engine/devicefinders.h"
|
||||
#include "engine/devicefinder.h"
|
||||
#include "collection/collection.h"
|
||||
#include "collection/collectionbackend.h"
|
||||
@@ -341,7 +341,7 @@ void ContextView::SetSong(const Song &song) {
|
||||
ui_->spacer_play_output->changeSize(20, 20, QSizePolicy::Fixed);
|
||||
|
||||
DeviceFinder::Device device;
|
||||
for (DeviceFinder *f : app_->enginedevice()->device_finders_) {
|
||||
for (DeviceFinder *f : app_->device_finders()->ListFinders()) {
|
||||
for (const DeviceFinder::Device &d : f->ListDevices()) {
|
||||
if (d.value != app_->player()->engine()->device()) continue;
|
||||
device = d;
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
#include "player.h"
|
||||
#include "appearance.h"
|
||||
|
||||
#include "engine/enginedevice.h"
|
||||
#include "engine/devicefinders.h"
|
||||
#ifndef Q_OS_WIN
|
||||
# include "device/devicemanager.h"
|
||||
#endif
|
||||
@@ -105,7 +105,7 @@ class ApplicationImpl {
|
||||
appearance_([=]() { return new Appearance(app); }),
|
||||
task_manager_([=]() { return new TaskManager(app); }),
|
||||
player_([=]() { return new Player(app, app); }),
|
||||
enginedevice_([=]() { return new EngineDevice(app); }),
|
||||
device_finders_([=]() { return new DeviceFinders(app); }),
|
||||
#ifndef Q_OS_WIN
|
||||
device_manager_([=]() { return new DeviceManager(app, app); }),
|
||||
#endif
|
||||
@@ -175,7 +175,7 @@ class ApplicationImpl {
|
||||
Lazy<Appearance> appearance_;
|
||||
Lazy<TaskManager> task_manager_;
|
||||
Lazy<Player> player_;
|
||||
Lazy<EngineDevice> enginedevice_;
|
||||
Lazy<DeviceFinders> device_finders_;
|
||||
#ifndef Q_OS_WIN
|
||||
Lazy<DeviceManager> device_manager_;
|
||||
#endif
|
||||
@@ -205,7 +205,7 @@ class ApplicationImpl {
|
||||
Application::Application(QObject *parent)
|
||||
: QObject(parent), p_(new ApplicationImpl(this)) {
|
||||
|
||||
enginedevice()->Init();
|
||||
device_finders()->Init();
|
||||
collection()->Init();
|
||||
tag_reader_client();
|
||||
|
||||
@@ -307,7 +307,7 @@ Appearance *Application::appearance() const { return p_->appearance_.get(); }
|
||||
Database *Application::database() const { return p_->database_.get(); }
|
||||
TaskManager *Application::task_manager() const { return p_->task_manager_.get(); }
|
||||
Player *Application::player() const { return p_->player_.get(); }
|
||||
EngineDevice *Application::enginedevice() const { return p_->enginedevice_.get(); }
|
||||
DeviceFinders *Application::device_finders() const { return p_->device_finders_.get(); }
|
||||
#ifndef Q_OS_WIN
|
||||
DeviceManager *Application::device_manager() const { return p_->device_manager_.get(); }
|
||||
#endif
|
||||
|
||||
@@ -41,7 +41,7 @@ class TaskManager;
|
||||
class ApplicationImpl;
|
||||
class TagReaderClient;
|
||||
class Database;
|
||||
class EngineDevice;
|
||||
class DeviceFinders;
|
||||
class Player;
|
||||
class Appearance;
|
||||
class SCollection;
|
||||
@@ -78,7 +78,7 @@ class Application : public QObject {
|
||||
Appearance *appearance() const;
|
||||
TaskManager *task_manager() const;
|
||||
Player *player() const;
|
||||
EngineDevice *enginedevice() const;
|
||||
DeviceFinders *device_finders() const;
|
||||
#ifndef Q_OS_WIN
|
||||
DeviceManager *device_manager() const;
|
||||
#endif
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include <QSettings>
|
||||
|
||||
#include "core/logging.h"
|
||||
#include "iconmapper.h"
|
||||
#include "settings/appearancesettingspage.h"
|
||||
#include "iconloader.h"
|
||||
|
||||
@@ -64,7 +65,17 @@ QIcon IconLoader::Load(const QString &name, const int size) {
|
||||
else sizes << size;
|
||||
|
||||
if (system_icons_) {
|
||||
IconMapper::IconProperties icon_prop;
|
||||
if (IconMapper::iconmapper_.contains(name)) {
|
||||
icon_prop = IconMapper::iconmapper_[name];
|
||||
}
|
||||
ret = QIcon::fromTheme(name);
|
||||
if (ret.isNull()) {
|
||||
for (QString alt_name : icon_prop.names) {
|
||||
ret = QIcon::fromTheme(alt_name);
|
||||
if (!ret.isNull()) break;
|
||||
}
|
||||
}
|
||||
if (!ret.isNull()) return ret;
|
||||
qLog(Warning) << "Couldn't load icon" << name << "from system theme icons.";
|
||||
}
|
||||
|
||||
135
src/core/iconmapper.h
Normal file
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* Strawberry Music Player
|
||||
* Copyright 2019, Jonas Kvinge <jonas@jkvinge.net>
|
||||
*
|
||||
* Strawberry is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Strawberry is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <QtGlobal>
|
||||
#include <QMap>
|
||||
|
||||
namespace IconMapper {
|
||||
|
||||
struct IconProperties {
|
||||
IconProperties() : min_size(0), max_size(0) {}
|
||||
IconProperties(const QStringList &_names, const int _min_size, const int _max_size) : names(_names), min_size(_min_size), max_size(_max_size) {}
|
||||
QStringList names;
|
||||
int min_size;
|
||||
int max_size;
|
||||
};
|
||||
|
||||
static const QMap<QString, IconProperties> iconmapper_ = {
|
||||
|
||||
{ "albums", { {"media-optical"}, 0, 0 } },
|
||||
{ "alsa", { {}, 0, 0 } },
|
||||
{ "application-exit", { {}, 0, 0 } },
|
||||
{ "applications-internet", { {}, 0, 0 } },
|
||||
{ "bluetooth", { {"preferences-system-bluetooth", "bluetooth-active"}, 0, 0 } },
|
||||
{ "cdcase", { {"cdcover", "media-optical"}, 0, 0 } },
|
||||
{ "media-optical", { {"cd"}, 0, 0 } },
|
||||
{ "configure", { {}, 0, 0 } },
|
||||
{ "device-ipod-nano", { {}, 0, 0 } },
|
||||
{ "device-ipod", { {}, 0, 0 } },
|
||||
{ "device-phone", { {}, 0, 0 } },
|
||||
{ "device", { {"drive-removable-media-usb-pendrive"}, 0, 0 } },
|
||||
{ "device-usb-drive", { {}, 0, 0 } },
|
||||
{ "device-usb-flash", { {}, 0, 0 } },
|
||||
{ "dialog-error", { {}, 0, 0 } },
|
||||
{ "dialog-information", { {}, 0, 0 } },
|
||||
{ "dialog-ok-apply", { {}, 0, 0 } },
|
||||
{ "dialog-password", { {}, 0, 0 } },
|
||||
{ "dialog-warning", { {}, 0, 0 } },
|
||||
{ "document-download", { {}, 0, 0 } },
|
||||
{ "document-new", { {}, 0, 0 } },
|
||||
{ "document-open-folder", { {}, 0, 0 } },
|
||||
{ "document-open", { {}, 0, 0 } },
|
||||
{ "document-save", { {}, 0, 0 } },
|
||||
{ "document-search", { {}, 0, 0 } },
|
||||
{ "download", { {"applications-internet", "network-workgroup"}, 0, 0 } },
|
||||
{ "edit-clear-list", { {}, 0, 0 } },
|
||||
{ "edit-clear-locationbar-ltr", { {}, 0, 0 } },
|
||||
{ "edit-copy", { {}, 0, 0 } },
|
||||
{ "edit-delete", { {}, 0, 0 } },
|
||||
{ "edit-find", { {}, 0, 0 } },
|
||||
{ "edit-redo", { {}, 0, 0 } },
|
||||
{ "edit-rename", { {}, 0, 0 } },
|
||||
{ "edit-undo", { {}, 0, 0 } },
|
||||
{ "electrocompaniet", { {}, 0, 0 } },
|
||||
{ "equalizer", { {"view-media-equalizer"}, 0, 0 } },
|
||||
{ "folder-new", { {}, 0, 0 } },
|
||||
{ "folder", { {}, 0, 0 } },
|
||||
{ "folder-sound", { {"folder-music"}, 0, 0 } },
|
||||
{ "footsteps", { {"go-jump"}, 0, 0 } },
|
||||
{ "go-down", { {}, 0, 0 } },
|
||||
{ "go-home", { {}, 0, 0 } },
|
||||
{ "go-jump", { {}, 0, 0 } },
|
||||
{ "go-next", { {}, 0, 0 } },
|
||||
{ "go-previous", { {}, 0, 0 } },
|
||||
{ "go-up", { {}, 0, 0 } },
|
||||
{ "gstreamer", { {"phonon-gstreamer"}, 0, 0 } },
|
||||
{ "headset", { {"audio-headset"}, 0, 0 } },
|
||||
{ "help-hint", { {}, 0, 0 } },
|
||||
{ "intel", { {}, 0, 0 } },
|
||||
{ "jack", { {"audio-input-line"}, 0, 0 } },
|
||||
{ "keyboard", { {"input-keyboard"}, 0, 0 } },
|
||||
{ "list-add", { {}, 0, 0 } },
|
||||
{ "list-remove", { {}, 0, 0 } },
|
||||
{ "love", { {"heart", "emblem-favorite"}, 0, 0 } },
|
||||
{ "mcintosh-player", { {}, 0, 0 } },
|
||||
{ "mcintosh", { {}, 0, 0 } },
|
||||
{ "mcintosh-text", { {}, 0, 0 } },
|
||||
{ "media-eject", { {}, 0, 0 } },
|
||||
{ "media-playback-pause", { {"media-pause"}, 0, 0 } },
|
||||
{ "media-playlist-repeat", { {}, 0, 0 } },
|
||||
{ "media-playlist-shuffle", { {""}, 0, 0 } },
|
||||
{ "media-playback-start", { {"media-play", "media-playback-playing"}, 0, 0 } },
|
||||
{ "media-seek-backward", { {}, 0, 0 } },
|
||||
{ "media-seek-forward", { {}, 0, 0 } },
|
||||
{ "media-skip-backward", { {}, 0, 0 } },
|
||||
{ "media-skip-forward", { {}, 0, 0 } },
|
||||
{ "media-playback-stop", { {"media-stop"}, 0, 0 } },
|
||||
{ "moodbar", { {"preferences-desktop-icons"}, 0, 0 } },
|
||||
{ "nvidia", { {}, 0, 0 } },
|
||||
{ "pulseaudio", { {}, 0, 0 } },
|
||||
{ "qobuz", { {}, 0, 0 } },
|
||||
{ "realtek", { {}, 0, 0 } },
|
||||
{ "scrobble-disabled", { {}, 0, 0 } },
|
||||
{ "scrobble", { {}, 0, 0 } },
|
||||
{ "search", { {}, 0, 0 } },
|
||||
{ "soundcard", { {"audiocard", "audio-card"}, 0, 0 } },
|
||||
{ "speaker", { {}, 0, 0 } },
|
||||
{ "star-grey", { {}, 0, 0 } },
|
||||
{ "star", { {}, 0, 0 } },
|
||||
{ "strawberry", { {}, 0, 0 } },
|
||||
{ "subsonic", { {}, 0, 0 } },
|
||||
{ "tidal", { {}, 0, 0 } },
|
||||
{ "tools-wizard", { {}, 0, 0 } },
|
||||
{ "view-choose", { {}, 0, 0 } },
|
||||
{ "view-fullscreen", { {}, 0, 0 } },
|
||||
{ "view-media-lyrics", { {}, 0, 0 } },
|
||||
{ "view-media-playlist", { {}, 0, 0 } },
|
||||
{ "view-media-visualization", { {"preferences-desktop-theme"}, 0, 0 } },
|
||||
{ "view-refresh", { {}, 0, 0 } },
|
||||
{ "library-music", { {"vinyl"}, 0, 0 } },
|
||||
{ "vlc", { {}, 0, 0 } },
|
||||
{ "xine", { {}, 0, 0 } },
|
||||
{ "zoom-in", { {}, 0, 0 } },
|
||||
{ "zoom-out", { {}, 0, 0 } }
|
||||
|
||||
};
|
||||
|
||||
} // namespace
|
||||
@@ -273,7 +273,7 @@ MainWindow::MainWindow(Application *app, SystemTrayIcon *tray_icon, OSD *osd, co
|
||||
|
||||
// Add tabs to the fancy tab widget
|
||||
ui_->tabs->AddTab(context_view_, "context", IconLoader::Load("strawberry"), tr("Context"));
|
||||
ui_->tabs->AddTab(collection_view_, "collection", IconLoader::Load("vinyl"), tr("Collection"));
|
||||
ui_->tabs->AddTab(collection_view_, "collection", IconLoader::Load("library-music"), tr("Collection"));
|
||||
ui_->tabs->AddTab(file_view_, "files", IconLoader::Load("document-open"), tr("Files"));
|
||||
ui_->tabs->AddTab(playlist_list_, "playlists", IconLoader::Load("view-media-playlist"), tr("Playlists"));
|
||||
ui_->tabs->AddTab(queue_view_, "queue", IconLoader::Load("footsteps"), tr("Queue"));
|
||||
@@ -346,11 +346,11 @@ MainWindow::MainWindow(Application *app, SystemTrayIcon *tray_icon, OSD *osd, co
|
||||
// Music menu
|
||||
|
||||
ui_->action_open_file->setIcon(IconLoader::Load("document-open"));
|
||||
ui_->action_open_cd->setIcon(IconLoader::Load("cd"));
|
||||
ui_->action_open_cd->setIcon(IconLoader::Load("media-optical"));
|
||||
ui_->action_previous_track->setIcon(IconLoader::Load("media-skip-backward"));
|
||||
ui_->action_play_pause->setIcon(IconLoader::Load("media-play"));
|
||||
ui_->action_stop->setIcon(IconLoader::Load("media-stop"));
|
||||
ui_->action_stop_after_this_track->setIcon(IconLoader::Load("media-stop"));
|
||||
ui_->action_play_pause->setIcon(IconLoader::Load("media-playback-start"));
|
||||
ui_->action_stop->setIcon(IconLoader::Load("media-playback-stop"));
|
||||
ui_->action_stop_after_this_track->setIcon(IconLoader::Load("media-playback-stop"));
|
||||
ui_->action_next_track->setIcon(IconLoader::Load("media-skip-forward"));
|
||||
ui_->action_quit->setIcon(IconLoader::Load("application-exit"));
|
||||
|
||||
@@ -594,7 +594,7 @@ MainWindow::MainWindow(Application *app, SystemTrayIcon *tray_icon, OSD *osd, co
|
||||
connect(playlist_menu_, SIGNAL(aboutToHide()), SLOT(PlaylistMenuHidden()));
|
||||
playlist_play_pause_ = playlist_menu_->addAction(tr("Play"), this, SLOT(PlaylistPlay()));
|
||||
playlist_menu_->addAction(ui_->action_stop);
|
||||
playlist_stop_after_ = playlist_menu_->addAction(IconLoader::Load("media-stop"), tr("Stop after this track"), this, SLOT(PlaylistStopAfter()));
|
||||
playlist_stop_after_ = playlist_menu_->addAction(IconLoader::Load("media-playback-stop"), tr("Stop after this track"), this, SLOT(PlaylistStopAfter()));
|
||||
playlist_queue_ = playlist_menu_->addAction(IconLoader::Load("go-next"), tr("Toggle queue status"), this, SLOT(PlaylistQueue()));
|
||||
playlist_queue_->setShortcut(QKeySequence("Ctrl+D"));
|
||||
ui_->playlist->addAction(playlist_queue_);
|
||||
@@ -1040,7 +1040,7 @@ void MainWindow::MediaStopped() {
|
||||
|
||||
ui_->action_stop->setEnabled(false);
|
||||
ui_->action_stop_after_this_track->setEnabled(false);
|
||||
ui_->action_play_pause->setIcon(IconLoader::Load("media-play"));
|
||||
ui_->action_play_pause->setIcon(IconLoader::Load("media-playback-start"));
|
||||
ui_->action_play_pause->setText(tr("Play"));
|
||||
|
||||
ui_->action_play_pause->setEnabled(true);
|
||||
@@ -1069,7 +1069,7 @@ void MainWindow::MediaPaused() {
|
||||
|
||||
ui_->action_stop->setEnabled(true);
|
||||
ui_->action_stop_after_this_track->setEnabled(true);
|
||||
ui_->action_play_pause->setIcon(IconLoader::Load("media-play"));
|
||||
ui_->action_play_pause->setIcon(IconLoader::Load("media-playback-start"));
|
||||
ui_->action_play_pause->setText(tr("Play"));
|
||||
|
||||
ui_->action_play_pause->setEnabled(true);
|
||||
@@ -1087,7 +1087,7 @@ void MainWindow::MediaPlaying() {
|
||||
|
||||
ui_->action_stop->setEnabled(true);
|
||||
ui_->action_stop_after_this_track->setEnabled(true);
|
||||
ui_->action_play_pause->setIcon(IconLoader::Load("media-pause"));
|
||||
ui_->action_play_pause->setIcon(IconLoader::Load("media-playback-pause"));
|
||||
ui_->action_play_pause->setText(tr("Pause"));
|
||||
|
||||
bool enable_play_pause(false);
|
||||
@@ -1257,9 +1257,11 @@ void MainWindow::PlayIndex(const QModelIndex &index) {
|
||||
|
||||
app_->playlist_manager()->SetActiveToCurrent();
|
||||
app_->player()->PlayAt(row, Engine::Manual, true);
|
||||
|
||||
}
|
||||
|
||||
void MainWindow::PlaylistDoubleClick(const QModelIndex &index) {
|
||||
|
||||
if (!index.isValid()) return;
|
||||
|
||||
int row = index.row();
|
||||
@@ -1284,6 +1286,7 @@ void MainWindow::PlaylistDoubleClick(const QModelIndex &index) {
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void MainWindow::VolumeWheelEvent(int delta) {
|
||||
@@ -1315,6 +1318,7 @@ void MainWindow::ToggleShowHide() {
|
||||
activateWindow();
|
||||
raise();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void MainWindow::StopAfterCurrent() {
|
||||
@@ -1330,12 +1334,12 @@ void MainWindow::closeEvent(QCloseEvent *event) {
|
||||
settings.endGroup();
|
||||
|
||||
if (keep_running && event->spontaneous() && QSystemTrayIcon::isSystemTrayAvailable()) {
|
||||
event->ignore();
|
||||
SetHiddenInTray(true);
|
||||
}
|
||||
else {
|
||||
Exit();
|
||||
}
|
||||
event->ignore();
|
||||
|
||||
}
|
||||
|
||||
@@ -1352,6 +1356,7 @@ void MainWindow::SetHiddenInTray(bool hidden) {
|
||||
if (was_maximized_) showMaximized();
|
||||
else show();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void MainWindow::FilePathChanged(const QString &path) {
|
||||
@@ -1526,11 +1531,11 @@ void MainWindow::PlaylistRightClick(const QPoint &global_pos, const QModelIndex
|
||||
// Is this song currently playing?
|
||||
if (app_->playlist_manager()->current()->current_row() == source_index.row() && app_->player()->GetState() == Engine::Playing) {
|
||||
playlist_play_pause_->setText(tr("Pause"));
|
||||
playlist_play_pause_->setIcon(IconLoader::Load("media-pause"));
|
||||
playlist_play_pause_->setIcon(IconLoader::Load("media-playback-pause"));
|
||||
}
|
||||
else {
|
||||
playlist_play_pause_->setText(tr("Play"));
|
||||
playlist_play_pause_->setIcon(IconLoader::Load("media-play"));
|
||||
playlist_play_pause_->setIcon(IconLoader::Load("media-playback-start"));
|
||||
}
|
||||
|
||||
// Are we allowed to pause?
|
||||
@@ -1643,7 +1648,7 @@ void MainWindow::PlaylistRightClick(const QPoint &global_pos, const QModelIndex
|
||||
else playlist_queue_->setIcon(IconLoader::Load("go-next"));
|
||||
|
||||
if (in_skipped < selected) playlist_skip_->setIcon(IconLoader::Load("media-skip-forward"));
|
||||
else playlist_skip_->setIcon(IconLoader::Load("media-play"));
|
||||
else playlist_skip_->setIcon(IconLoader::Load("media-playback-start"));
|
||||
|
||||
|
||||
if (!index.isValid()) {
|
||||
@@ -2106,9 +2111,10 @@ void MainWindow::PlaylistUndoRedoChanged(QAction *undo, QAction *redo) {
|
||||
playlist_menu_->insertAction(playlist_undoredo_, redo);
|
||||
}
|
||||
|
||||
#ifdef HAVE_GSTREAMER
|
||||
void MainWindow::AddFilesToTranscoder() {
|
||||
|
||||
#ifdef HAVE_GSTREAMER
|
||||
|
||||
QStringList filenames;
|
||||
|
||||
for (const QModelIndex &index : ui_->playlist->view()->selectionModel()->selection().indexes()) {
|
||||
@@ -2122,21 +2128,24 @@ void MainWindow::AddFilesToTranscoder() {
|
||||
transcode_dialog_->SetFilenames(filenames);
|
||||
|
||||
ShowTranscodeDialog();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
void MainWindow::ShowCollectionConfig() {
|
||||
//EnsureSettingsDialogCreated();
|
||||
settings_dialog_->OpenAtPage(SettingsDialog::Page_Collection);
|
||||
}
|
||||
|
||||
void MainWindow::TaskCountChanged(int count) {
|
||||
|
||||
if (count == 0) {
|
||||
ui_->status_bar_stack->setCurrentWidget(ui_->playlist_summary_page);
|
||||
}
|
||||
else {
|
||||
ui_->status_bar_stack->setCurrentWidget(ui_->multi_loading_indicator);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void MainWindow::PlayingWidgetPositionChanged(bool above_status_bar) {
|
||||
@@ -2339,13 +2348,13 @@ void MainWindow::ShowAboutDialog() {
|
||||
|
||||
}
|
||||
|
||||
#ifdef HAVE_GSTREAMER
|
||||
void MainWindow::ShowTranscodeDialog() {
|
||||
|
||||
#ifdef HAVE_GSTREAMER
|
||||
transcode_dialog_->show();
|
||||
#endif
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
void MainWindow::ShowErrorDialog(const QString &message) {
|
||||
error_dialog_->ShowMessage(message);
|
||||
@@ -2421,9 +2430,10 @@ bool MainWindow::nativeEvent(const QByteArray &eventType, void *message, long *r
|
||||
|
||||
}
|
||||
|
||||
#if defined(HAVE_GSTREAMER) && defined(HAVE_CHROMAPRINT)
|
||||
void MainWindow::AutoCompleteTags() {
|
||||
|
||||
#if defined(HAVE_GSTREAMER) && defined(HAVE_CHROMAPRINT)
|
||||
|
||||
// Create the tag fetching stuff if it hasn't been already
|
||||
if (!tag_fetcher_) {
|
||||
tag_fetcher_.reset(new TagFetcher);
|
||||
@@ -2456,6 +2466,9 @@ void MainWindow::AutoCompleteTags() {
|
||||
tag_fetcher_->StartFetch(songs);
|
||||
|
||||
track_selection_dialog_->show();
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
void MainWindow::AutoCompleteTagsAccepted() {
|
||||
@@ -2466,8 +2479,8 @@ void MainWindow::AutoCompleteTagsAccepted() {
|
||||
|
||||
// This is really lame but we don't know what rows have changed
|
||||
ui_->playlist->view()->update();
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
void MainWindow::HandleNotificationPreview(OSD::Behaviour type, QString line1, QString line2) {
|
||||
|
||||
|
||||
@@ -162,14 +162,10 @@ class MainWindow : public QMainWindow, public PlatformInterface {
|
||||
void RenumberTracks();
|
||||
void SelectionSetValue();
|
||||
void EditValue();
|
||||
#if defined(HAVE_GSTREAMER) && defined(HAVE_CHROMAPRINT)
|
||||
void AutoCompleteTags();
|
||||
void AutoCompleteTagsAccepted();
|
||||
#endif
|
||||
void PlaylistUndoRedoChanged(QAction *undo, QAction *redo);
|
||||
#ifdef HAVE_GSTREAMER
|
||||
void AddFilesToTranscoder();
|
||||
#endif
|
||||
|
||||
void PlaylistCopyToCollection();
|
||||
void PlaylistMoveToCollection();
|
||||
@@ -225,9 +221,7 @@ class MainWindow : public QMainWindow, public PlatformInterface {
|
||||
void ShowCoverManager();
|
||||
|
||||
void ShowAboutDialog();
|
||||
#ifdef HAVE_GSTREAMER
|
||||
void ShowTranscodeDialog();
|
||||
#endif
|
||||
void ShowErrorDialog(const QString& message);
|
||||
SettingsDialog *CreateSettingsDialog();
|
||||
EditTagDialog *CreateEditTagDialog();
|
||||
@@ -319,9 +313,7 @@ class MainWindow : public QMainWindow, public PlatformInterface {
|
||||
std::unique_ptr<TagFetcher> tag_fetcher_;
|
||||
#endif
|
||||
std::unique_ptr<TrackSelectionDialog> track_selection_dialog_;
|
||||
#if defined(HAVE_GSTREAMER) && defined(HAVE_CHROMAPRINT)
|
||||
PlaylistItemList autocomplete_tag_items_;
|
||||
#endif
|
||||
|
||||
InternetTabsView *tidal_view_;
|
||||
InternetTabsView *qobuz_view_;
|
||||
|
||||
@@ -200,14 +200,15 @@ void Player::Init() {
|
||||
connect(engine_.get(), SIGNAL(MetaData(Engine::SimpleMetaBundle)), SLOT(EngineMetadataReceived(Engine::SimpleMetaBundle)));
|
||||
|
||||
// Equalizer
|
||||
qLog(Debug) << "Creating equalizer";
|
||||
connect(equalizer_, SIGNAL(ParametersChanged(int,QList<int>)), app_->player()->engine(), SLOT(SetEqualizerParameters(int,QList<int>)));
|
||||
connect(equalizer_, SIGNAL(EnabledChanged(bool)), app_->player()->engine(), SLOT(SetEqualizerEnabled(bool)));
|
||||
connect(equalizer_, SIGNAL(StereoBalancerEnabledChanged(bool)), app_->player()->engine(), SLOT(SetStereoBalancerEnabled(bool)));
|
||||
connect(equalizer_, SIGNAL(StereoBalanceChanged(float)), app_->player()->engine(), SLOT(SetStereoBalance(float)));
|
||||
connect(equalizer_, SIGNAL(EqualizerEnabledChanged(bool)), app_->player()->engine(), SLOT(SetEqualizerEnabled(bool)));
|
||||
connect(equalizer_, SIGNAL(EqualizerParametersChanged(int, QList<int>)), app_->player()->engine(), SLOT(SetEqualizerParameters(int, QList<int>)));
|
||||
|
||||
engine_->SetEqualizerEnabled(equalizer_->is_enabled());
|
||||
engine_->SetEqualizerParameters(equalizer_->preamp_value(), equalizer_->gain_values());
|
||||
engine_->SetStereoBalancerEnabled(equalizer_->is_stereo_balancer_enabled());
|
||||
engine_->SetStereoBalance(equalizer_->stereo_balance());
|
||||
engine_->SetEqualizerEnabled(equalizer_->is_equalizer_enabled());
|
||||
engine_->SetEqualizerParameters(equalizer_->preamp_value(), equalizer_->gain_values());
|
||||
|
||||
s.beginGroup(BackendSettingsPage::kSettingsGroup);
|
||||
volume_control_ = s.value("volume_control", true).toBool();
|
||||
|
||||
@@ -190,7 +190,7 @@ void QtSystemTrayIcon::SetPaused() {
|
||||
|
||||
action_stop_->setEnabled(true);
|
||||
action_stop_after_this_track_->setEnabled(true);
|
||||
action_play_pause_->setIcon(IconLoader::Load("media-play"));
|
||||
action_play_pause_->setIcon(IconLoader::Load("media-playback-start"));
|
||||
action_play_pause_->setText(tr("Play"));
|
||||
|
||||
action_play_pause_->setEnabled(true);
|
||||
@@ -203,7 +203,7 @@ void QtSystemTrayIcon::SetPlaying(bool enable_play_pause) {
|
||||
|
||||
action_stop_->setEnabled(true);
|
||||
action_stop_after_this_track_->setEnabled(true);
|
||||
action_play_pause_->setIcon(IconLoader::Load("media-pause"));
|
||||
action_play_pause_->setIcon(IconLoader::Load("media-playback-pause"));
|
||||
action_play_pause_->setText(tr("Pause"));
|
||||
action_play_pause_->setEnabled(enable_play_pause);
|
||||
|
||||
@@ -215,7 +215,7 @@ void QtSystemTrayIcon::SetStopped() {
|
||||
|
||||
action_stop_->setEnabled(false);
|
||||
action_stop_after_this_track_->setEnabled(false);
|
||||
action_play_pause_->setIcon(IconLoader::Load("media-play"));
|
||||
action_play_pause_->setIcon(IconLoader::Load("media-playback-start"));
|
||||
action_play_pause_->setText(tr("Play"));
|
||||
|
||||
action_play_pause_->setEnabled(true);
|
||||
|
||||
@@ -296,14 +296,15 @@ const QString &Song::albumartist() const { return d->albumartist_; }
|
||||
const QString &Song::albumartist_sortable() const { return d->albumartist_sortable_; }
|
||||
const QString &Song::effective_albumartist() const { return d->albumartist_.isEmpty() ? d->artist_ : d->albumartist_; }
|
||||
const QString &Song::effective_albumartist_sortable() const { return d->albumartist_.isEmpty() ? d->artist_sortable_ : d->albumartist_sortable_; }
|
||||
const QString &Song::playlist_albumartist() const { return is_compilation() ? d->albumartist_sortable_ : effective_albumartist_sortable(); }
|
||||
const QString &Song::playlist_albumartist() const { return is_compilation() ? d->albumartist_ : effective_albumartist(); }
|
||||
const QString &Song::playlist_albumartist_sortable() const { return is_compilation() ? d->albumartist_sortable_ : effective_albumartist_sortable(); }
|
||||
int Song::track() const { return d->track_; }
|
||||
int Song::disc() const { return d->disc_; }
|
||||
int Song::year() const { return d->year_; }
|
||||
int Song::originalyear() const { return d->originalyear_; }
|
||||
int Song::effective_originalyear() const { return d->originalyear_ < 0 ? d->year_ : d->originalyear_; }
|
||||
const QString &Song::genre() const { return d->genre_; }
|
||||
bool Song::is_compilation() const { return (d->compilation_ || d->compilation_detected_ || d->compilation_on_) && ! d->compilation_off_; }
|
||||
bool Song::compilation() const { return d->compilation_; }
|
||||
const QString &Song::composer() const { return d->composer_; }
|
||||
const QString &Song::performer() const { return d->performer_; }
|
||||
const QString &Song::grouping() const { return d->grouping_; }
|
||||
@@ -331,6 +332,10 @@ int Song::playcount() const { return d->playcount_; }
|
||||
int Song::skipcount() const { return d->skipcount_; }
|
||||
int Song::lastplayed() const { return d->lastplayed_; }
|
||||
|
||||
bool Song::compilation_detected() const { return d->compilation_detected_; }
|
||||
bool Song::compilation_off() const { return d->compilation_off_; }
|
||||
bool Song::compilation_on() const { return d->compilation_on_; }
|
||||
|
||||
const QUrl &Song::art_automatic() const { return d->art_automatic_; }
|
||||
const QUrl &Song::art_manual() const { return d->art_manual_; }
|
||||
bool Song::has_manually_unset_cover() const { return d->art_manual_.path() == kManuallyUnsetCover; }
|
||||
@@ -349,6 +354,7 @@ bool Song::is_collection_song() const { return d->source_ == Source_Collection;
|
||||
bool Song::is_metadata_good() const { return !d->title_.isEmpty() && !d->album_.isEmpty() && !d->artist_.isEmpty() && !d->url_.isEmpty() && d->end_ > 0; }
|
||||
bool Song::is_stream() const { return d->source_ == Source_Stream || d->source_ == Source_Tidal || d->source_ == Source_Subsonic || d->source_ == Source_Qobuz; }
|
||||
bool Song::is_cdda() const { return d->source_ == Source_CDDA; }
|
||||
bool Song::is_compilation() const { return (d->compilation_ || d->compilation_detected_ || d->compilation_on_) && !d->compilation_off_; }
|
||||
|
||||
bool Song::art_automatic_is_valid() const {
|
||||
return (
|
||||
@@ -488,8 +494,8 @@ QIcon Song::IconForSource(Source source) {
|
||||
|
||||
switch (source) {
|
||||
case Song::Source_LocalFile: return IconLoader::Load("folder-sound");
|
||||
case Song::Source_Collection: return IconLoader::Load("vinyl");
|
||||
case Song::Source_CDDA: return IconLoader::Load("cd");
|
||||
case Song::Source_Collection: return IconLoader::Load("library-music");
|
||||
case Song::Source_CDDA: return IconLoader::Load("media-optical");
|
||||
case Song::Source_Device: return IconLoader::Load("device");
|
||||
case Song::Source_Stream: return IconLoader::Load("applications-internet");
|
||||
case Song::Source_Tidal: return IconLoader::Load("tidal");
|
||||
|
||||
@@ -200,7 +200,7 @@ class Song {
|
||||
int year() const;
|
||||
int originalyear() const;
|
||||
const QString &genre() const;
|
||||
bool is_compilation() const;
|
||||
bool compilation() const;
|
||||
const QString &composer() const;
|
||||
const QString &performer() const;
|
||||
const QString &grouping() const;
|
||||
@@ -232,6 +232,10 @@ class Song {
|
||||
int skipcount() const;
|
||||
int lastplayed() const;
|
||||
|
||||
bool compilation_detected() const;
|
||||
bool compilation_off() const;
|
||||
bool compilation_on() const;
|
||||
|
||||
const QUrl &art_automatic() const;
|
||||
const QUrl &art_manual() const;
|
||||
|
||||
@@ -249,9 +253,11 @@ class Song {
|
||||
bool is_metadata_good() const;
|
||||
bool art_automatic_is_valid() const;
|
||||
bool art_manual_is_valid() const;
|
||||
bool is_compilation() const;
|
||||
|
||||
// Playlist views are special because you don't want to fill in album artists automatically for compilations, but you do for normal albums:
|
||||
const QString &playlist_albumartist() const;
|
||||
const QString &playlist_albumartist_sortable() const;
|
||||
|
||||
// Returns true if this Song had it's cover manually unset by user.
|
||||
bool has_manually_unset_cover() const;
|
||||
|
||||
@@ -46,8 +46,9 @@ TagReaderClient::TagReaderClient(QObject *parent) : QObject(parent), worker_pool
|
||||
original_thread_ = thread();
|
||||
|
||||
worker_pool_->SetExecutableName(kWorkerExecutableName);
|
||||
worker_pool_->SetWorkerCount(QThread::idealThreadCount());
|
||||
worker_pool_->SetWorkerCount(qBound(1, QThread::idealThreadCount() / 2, 2));
|
||||
connect(worker_pool_, SIGNAL(WorkerFailedToStart()), SLOT(WorkerFailedToStart()));
|
||||
|
||||
}
|
||||
|
||||
void TagReaderClient::Start() { worker_pool_->Start(); }
|
||||
|
||||
@@ -95,8 +95,8 @@ AlbumCoverManager::AlbumCoverManager(Application *app, CollectionBackend *collec
|
||||
cover_searcher_(nullptr),
|
||||
cover_export_(nullptr),
|
||||
cover_exporter_(new AlbumCoverExporter(this)),
|
||||
artist_icon_(IconLoader::Load("folder-sound" )),
|
||||
all_artists_icon_(IconLoader::Load("vinyl" )),
|
||||
artist_icon_(IconLoader::Load("folder-sound")),
|
||||
all_artists_icon_(IconLoader::Load("library-music")),
|
||||
no_cover_icon_(":/pictures/cdcase.png"),
|
||||
no_cover_image_(GenerateNoCoverImage(no_cover_icon_)),
|
||||
no_cover_item_icon_(QPixmap::fromImage(no_cover_image_)),
|
||||
@@ -110,12 +110,12 @@ AlbumCoverManager::AlbumCoverManager(Application *app, CollectionBackend *collec
|
||||
ui_->albums->set_cover_manager(this);
|
||||
|
||||
// Icons
|
||||
ui_->action_fetch->setIcon(IconLoader::Load("download" ));
|
||||
ui_->export_covers->setIcon(IconLoader::Load("document-save" ));
|
||||
ui_->view->setIcon(IconLoader::Load("view-choose" ));
|
||||
ui_->button_fetch->setIcon(IconLoader::Load("download" ));
|
||||
ui_->action_add_to_playlist->setIcon(IconLoader::Load("media-play" ));
|
||||
ui_->action_load->setIcon(IconLoader::Load("media-play" ));
|
||||
ui_->action_fetch->setIcon(IconLoader::Load("download"));
|
||||
ui_->export_covers->setIcon(IconLoader::Load("document-save"));
|
||||
ui_->view->setIcon(IconLoader::Load("view-choose"));
|
||||
ui_->button_fetch->setIcon(IconLoader::Load("download"));
|
||||
ui_->action_add_to_playlist->setIcon(IconLoader::Load("media-playback-start"));
|
||||
ui_->action_load->setIcon(IconLoader::Load("media-playback-start"));
|
||||
|
||||
album_cover_choice_controller_->Init(app_);
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ QStringList CddaLister::DeviceUniqueIDs() { return devices_list_; }
|
||||
|
||||
QVariantList CddaLister::DeviceIcons(const QString &) {
|
||||
QVariantList icons;
|
||||
icons << QString("cd");
|
||||
icons << QString("media-optical");
|
||||
return icons;
|
||||
}
|
||||
|
||||
|
||||
@@ -252,6 +252,8 @@ QStringList DeviceLister::GuessIconForPath(const QString &path) {
|
||||
ret << model_icon.arg(model);
|
||||
}
|
||||
}
|
||||
#else
|
||||
Q_UNUSED(path)
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
|
||||
@@ -97,7 +97,7 @@ void DeviceProperties::ShowDevice(QModelIndex idx) {
|
||||
<< "device"
|
||||
<< "device-usb-drive"
|
||||
<< "device-usb-flash"
|
||||
<< "cd"
|
||||
<< "media-optical"
|
||||
<< "device-ipod"
|
||||
<< "device-ipod-nano"
|
||||
<< "device-phone";
|
||||
|
||||
@@ -225,8 +225,8 @@ void DeviceView::contextMenuEvent(QContextMenuEvent *e) {
|
||||
properties_action_ = device_menu_->addAction(IconLoader::Load("configure"), tr("Device properties..."), this, SLOT(Properties()));
|
||||
|
||||
// Collection menu
|
||||
add_to_playlist_action_ = collection_menu_->addAction(IconLoader::Load("media-play"), tr("Append to current playlist"), this, SLOT(AddToPlaylist()));
|
||||
load_action_ = collection_menu_->addAction(IconLoader::Load("media-play"), tr("Replace current playlist"), this, SLOT(Load()));
|
||||
add_to_playlist_action_ = collection_menu_->addAction(IconLoader::Load("media-playback-start"), tr("Append to current playlist"), this, SLOT(AddToPlaylist()));
|
||||
load_action_ = collection_menu_->addAction(IconLoader::Load("media-playback-start"), tr("Replace current playlist"), this, SLOT(Load()));
|
||||
open_in_new_playlist_ = collection_menu_->addAction(IconLoader::Load("document-new"), tr("Open in new playlist"), this, SLOT(OpenInNewPlaylist()));
|
||||
|
||||
collection_menu_->addSeparator();
|
||||
|
||||
@@ -755,7 +755,7 @@ QVariantList MacOsDeviceLister::DeviceIcons(const QString& serial) {
|
||||
}
|
||||
|
||||
if (IsCDDevice(serial)) {
|
||||
return QVariantList() << "cd";
|
||||
return QVariantList() << "media-optical";
|
||||
}
|
||||
|
||||
QString bsd_name = current_devices_[serial];
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
#include "core/song.h"
|
||||
|
||||
class MtpConnection : public QObject, public std::enable_shared_from_this<MtpConnection> {
|
||||
public:
|
||||
public:
|
||||
MtpConnection(const QUrl &url);
|
||||
~MtpConnection();
|
||||
|
||||
@@ -43,7 +43,7 @@ public:
|
||||
LIBMTP_mtpdevice_t *device() const { return device_; }
|
||||
bool GetSupportedFiletypes(QList<Song::FileType> *ret);
|
||||
|
||||
private:
|
||||
private:
|
||||
Q_DISABLE_COPY(MtpConnection)
|
||||
|
||||
LIBMTP_mtpdevice_t *device_;
|
||||
|
||||
@@ -46,7 +46,7 @@ About::About(QWidget *parent):QDialog(parent) {
|
||||
|
||||
strawberry_contributors_ \
|
||||
<< Person("Gavin D. Howard", "yzena.tech@gmail.com")
|
||||
<< Person("Martin Delille", "martin@lylo.tv");
|
||||
<< Person("Martin Delille", "martin@delille.org");
|
||||
|
||||
strawberry_thanks_ \
|
||||
<< Person("Robert-André Mauchin", "eclipseo@fedoraproject.org")
|
||||
|
||||
@@ -841,9 +841,10 @@ void EditTagDialog::ResetPlayCounts() {
|
||||
UpdateStatisticsTab(*song);
|
||||
}
|
||||
|
||||
#if defined(HAVE_GSTREAMER) && defined(HAVE_CHROMAPRINT)
|
||||
void EditTagDialog::FetchTag() {
|
||||
|
||||
#if defined(HAVE_GSTREAMER) && defined(HAVE_CHROMAPRINT)
|
||||
|
||||
const QModelIndexList sel = ui_->song_list->selectionModel()->selectedIndexes();
|
||||
|
||||
SongList songs;
|
||||
@@ -864,10 +865,14 @@ void EditTagDialog::FetchTag() {
|
||||
|
||||
results_dialog_->show();
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
void EditTagDialog::FetchTagSongChosen(const Song &original_song, const Song &new_metadata) {
|
||||
|
||||
#if defined(HAVE_GSTREAMER) && defined(HAVE_CHROMAPRINT)
|
||||
|
||||
const QString filename = original_song.url().toLocalFile();
|
||||
|
||||
// Find the song with this filename
|
||||
@@ -893,9 +898,13 @@ void EditTagDialog::FetchTagSongChosen(const Song &original_song, const Song &ne
|
||||
UpdateUI(sel);
|
||||
}
|
||||
|
||||
}
|
||||
#else
|
||||
Q_UNUSED(original_song)
|
||||
Q_UNUSED(new_metadata)
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
void EditTagDialog::SongSaveComplete(TagReaderReply *reply, const QString &filename, const Song &song) {
|
||||
|
||||
pending_--;
|
||||
@@ -905,9 +914,7 @@ void EditTagDialog::SongSaveComplete(TagReaderReply *reply, const QString &filen
|
||||
emit Error(message);
|
||||
}
|
||||
else if (song.directory_id() != -1) {
|
||||
SongList songs;
|
||||
songs << song;
|
||||
app_->collection_backend()->AddOrUpdateSongs(songs);
|
||||
app_->collection_backend()->AddOrUpdateSongs(SongList() << song);
|
||||
}
|
||||
|
||||
if (pending_ <= 0) AcceptFinished();
|
||||
|
||||
@@ -109,10 +109,8 @@ class EditTagDialog : public QDialog {
|
||||
void ResetField();
|
||||
void ButtonClicked(QAbstractButton *button);
|
||||
void ResetPlayCounts();
|
||||
#if defined(HAVE_GSTREAMER) && defined(HAVE_CHROMAPRINT)
|
||||
void FetchTag();
|
||||
void FetchTagSongChosen(const Song &original_song, const Song &new_metadata);
|
||||
#endif
|
||||
|
||||
void AlbumCoverLoaded(const quint64 id, const QUrl &cover_url, const QImage &scaled, const QImage &original);
|
||||
|
||||
|
||||
@@ -26,8 +26,8 @@
|
||||
#include <QtDebug>
|
||||
|
||||
#include "core/logging.h"
|
||||
#include "devicefinders.h"
|
||||
#include "devicefinder.h"
|
||||
#include "enginedevice.h"
|
||||
|
||||
#ifdef HAVE_ALSA
|
||||
# include "alsadevicefinder.h"
|
||||
@@ -43,16 +43,16 @@
|
||||
|
||||
#ifdef Q_OS_WIN32
|
||||
# include "directsounddevicefinder.h"
|
||||
# include "mmdevicefinder.h"
|
||||
#endif
|
||||
|
||||
EngineDevice::EngineDevice(QObject *parent) : QObject(parent) {
|
||||
}
|
||||
DeviceFinders::DeviceFinders(QObject *parent) : QObject(parent) {}
|
||||
|
||||
EngineDevice::~EngineDevice() {
|
||||
DeviceFinders::~DeviceFinders() {
|
||||
qDeleteAll(device_finders_);
|
||||
}
|
||||
|
||||
void EngineDevice::Init() {
|
||||
void DeviceFinders::Init() {
|
||||
|
||||
QList<DeviceFinder*> device_finders;
|
||||
|
||||
@@ -67,6 +67,7 @@ void EngineDevice::Init() {
|
||||
#endif
|
||||
#ifdef Q_OS_WIN32
|
||||
device_finders.append(new DirectSoundDeviceFinder);
|
||||
device_finders.append(new MMDeviceFinder);
|
||||
#endif
|
||||
|
||||
for (DeviceFinder *finder : device_finders) {
|
||||
@@ -17,8 +17,8 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ENGINEDEVICE_H
|
||||
#define ENGINEDEVICE_H
|
||||
#ifndef DEVICEFINDERS_H
|
||||
#define DEVICEFINDERS_H
|
||||
|
||||
#include "config.h"
|
||||
|
||||
@@ -28,21 +28,19 @@
|
||||
|
||||
class DeviceFinder;
|
||||
|
||||
class EngineDevice : public QObject {
|
||||
class DeviceFinders : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit EngineDevice(QObject *parent = nullptr);
|
||||
~EngineDevice();
|
||||
explicit DeviceFinders(QObject *parent = nullptr);
|
||||
~DeviceFinders();
|
||||
|
||||
void Init();
|
||||
|
||||
QList<DeviceFinder*> device_finders_;
|
||||
QList<DeviceFinder*> ListFinders() { return device_finders_; }
|
||||
|
||||
private:
|
||||
QString output_;
|
||||
QList<DeviceFinder*> device_finders_;
|
||||
|
||||
};
|
||||
|
||||
#endif // ENGINEDEVICE_H
|
||||
|
||||
#endif // DEVICEFINDERS_H
|
||||
@@ -52,9 +52,7 @@ BOOL DirectSoundDeviceFinder::EnumerateCallback(LPGUID guid, LPCSTR description,
|
||||
|
||||
Device dev;
|
||||
dev.description = QString::fromLatin1(description);
|
||||
//if (guid) dev.value = QUuid(*guid).toByteArray();
|
||||
if (guid) dev.value = QUuid(*guid).toString();
|
||||
else dev.value = QVariant();
|
||||
dev.iconname = GuessIconName(dev.description);
|
||||
state->devices.append(dev);
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@ bool Engine::Base::Play(const QUrl &stream_url, const QUrl &original_url, TrackC
|
||||
|
||||
}
|
||||
|
||||
void Engine::Base::SetVolume(uint value) {
|
||||
void Engine::Base::SetVolume(const uint value) {
|
||||
|
||||
volume_ = value;
|
||||
SetVolumeSW(MakeVolumeLogarithmic(value));
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
|
||||
#include "engine_fwd.h"
|
||||
#include "enginetype.h"
|
||||
#include "enginedevice.h"
|
||||
#include "devicefinders.h"
|
||||
#include "core/song.h"
|
||||
|
||||
namespace Engine {
|
||||
@@ -102,13 +102,13 @@ public:
|
||||
void SetVolume(const uint value);
|
||||
static uint MakeVolumeLogarithmic(const uint volume);
|
||||
|
||||
public slots:
|
||||
public slots:
|
||||
virtual void ReloadSettings();
|
||||
|
||||
protected:
|
||||
protected:
|
||||
void EmitAboutToEnd();
|
||||
|
||||
public:
|
||||
public:
|
||||
|
||||
// Simple accessors
|
||||
EngineType type() const { return type_; }
|
||||
@@ -126,9 +126,10 @@ public:
|
||||
QVariant device() { return device_; }
|
||||
|
||||
public slots:
|
||||
virtual void SetStereoBalancerEnabled(const bool) {}
|
||||
virtual void SetStereoBalance(const float) {}
|
||||
virtual void SetEqualizerEnabled(const bool) {}
|
||||
virtual void SetEqualizerParameters(const int preamp, const QList<int> &bandGains) { Q_UNUSED(preamp); Q_UNUSED(bandGains); }
|
||||
virtual void SetStereoBalance(float value) { Q_UNUSED(value); }
|
||||
virtual void SetEqualizerParameters(const int, const QList<int>&) {}
|
||||
|
||||
signals:
|
||||
// Emitted when crossfading is enabled and the track is crossfade_duration_ away from finishing
|
||||
@@ -199,7 +200,7 @@ public:
|
||||
qint64 fadeout_pause_duration_;
|
||||
qint64 fadeout_pause_duration_nanosec_;
|
||||
|
||||
private:
|
||||
private:
|
||||
bool about_to_end_emitted_;
|
||||
Q_DISABLE_COPY(Base)
|
||||
|
||||
@@ -214,13 +215,13 @@ struct SimpleMetaBundle {
|
||||
QString album;
|
||||
QString comment;
|
||||
QString genre;
|
||||
qlonglong length;
|
||||
qint64 length;
|
||||
int year;
|
||||
int track;
|
||||
Song::FileType filetype;
|
||||
int samplerate;
|
||||
int bitdepth;
|
||||
qlonglong bitrate;
|
||||
qint64 bitrate;
|
||||
QString lyrics;
|
||||
};
|
||||
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
|
||||
#include <gst/gstbuffer.h>
|
||||
|
||||
#include <QString>
|
||||
|
||||
class GstEnginePipeline;
|
||||
|
||||
class GstBufferConsumer {
|
||||
@@ -33,7 +35,7 @@ public:
|
||||
|
||||
// This is called in some unspecified GStreamer thread.
|
||||
// Ownership of the buffer is transferred to the BufferConsumer and it should gst_buffer_unref it.
|
||||
virtual void ConsumeBuffer(GstBuffer *buffer, int pipeline_id) = 0;
|
||||
virtual void ConsumeBuffer(GstBuffer *buffer, const int pipeline_id, const QString &format) = 0;
|
||||
};
|
||||
|
||||
#endif // GSTBUFFERCONSUMER_H
|
||||
|
||||
@@ -83,7 +83,10 @@ GstEngine::GstEngine(TaskManager *task_manager)
|
||||
task_manager_(task_manager),
|
||||
buffering_task_id_(-1),
|
||||
latest_buffer_(nullptr),
|
||||
stereo_balancer_enabled_(false),
|
||||
stereo_balance_(0.0f),
|
||||
equalizer_enabled_(false),
|
||||
equalizer_preamp_(0),
|
||||
seek_timer_(new QTimer(this)),
|
||||
timer_id_(-1),
|
||||
next_element_id_(0),
|
||||
@@ -102,8 +105,15 @@ GstEngine::GstEngine(TaskManager *task_manager)
|
||||
}
|
||||
|
||||
GstEngine::~GstEngine() {
|
||||
|
||||
EnsureInitialised();
|
||||
current_pipeline_.reset();
|
||||
|
||||
if (latest_buffer_) {
|
||||
gst_buffer_unref(latest_buffer_);
|
||||
latest_buffer_ = nullptr;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool GstEngine::Init() {
|
||||
@@ -411,20 +421,34 @@ GstElement *GstEngine::CreateElement(const QString &factoryName, GstElement *bin
|
||||
return element;
|
||||
}
|
||||
|
||||
void GstEngine::ConsumeBuffer(GstBuffer *buffer, const int pipeline_id) {
|
||||
void GstEngine::ConsumeBuffer(GstBuffer *buffer, const int pipeline_id, const QString &format) {
|
||||
|
||||
// Schedule this to run in the GUI thread. The buffer gets added to the queue and unreffed by UpdateScope.
|
||||
if (!QMetaObject::invokeMethod(this, "AddBufferToScope", Q_ARG(GstBuffer*, buffer), Q_ARG(int, pipeline_id))) {
|
||||
if (!QMetaObject::invokeMethod(this, "AddBufferToScope", Q_ARG(GstBuffer*, buffer), Q_ARG(int, pipeline_id), Q_ARG(QString, format))) {
|
||||
qLog(Warning) << "Failed to invoke AddBufferToScope on GstEngine";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void GstEngine::SetStereoBalancerEnabled(const bool enabled) {
|
||||
|
||||
stereo_balancer_enabled_ = enabled;
|
||||
if (current_pipeline_) current_pipeline_->set_stereo_balancer_enabled(enabled);
|
||||
|
||||
}
|
||||
|
||||
void GstEngine::SetStereoBalance(const float value) {
|
||||
|
||||
stereo_balance_ = value;
|
||||
if (current_pipeline_) current_pipeline_->SetStereoBalance(value);
|
||||
|
||||
}
|
||||
|
||||
void GstEngine::SetEqualizerEnabled(const bool enabled) {
|
||||
|
||||
equalizer_enabled_ = enabled;
|
||||
if (current_pipeline_) current_pipeline_->set_equalizer_enabled(enabled);
|
||||
|
||||
if (current_pipeline_) current_pipeline_->SetEqualizerEnabled(enabled);
|
||||
}
|
||||
|
||||
void GstEngine::SetEqualizerParameters(const int preamp, const QList<int> &band_gains) {
|
||||
@@ -432,27 +456,22 @@ void GstEngine::SetEqualizerParameters(const int preamp, const QList<int> &band_
|
||||
equalizer_preamp_ = preamp;
|
||||
equalizer_gains_ = band_gains;
|
||||
|
||||
if (current_pipeline_)
|
||||
current_pipeline_->SetEqualizerParams(preamp, band_gains);
|
||||
|
||||
}
|
||||
|
||||
void GstEngine::SetStereoBalance(const float value) {
|
||||
|
||||
stereo_balance_ = value;
|
||||
|
||||
if (current_pipeline_) current_pipeline_->SetStereoBalance(value);
|
||||
if (current_pipeline_) current_pipeline_->SetEqualizerParams(preamp, band_gains);
|
||||
|
||||
}
|
||||
|
||||
void GstEngine::AddBufferConsumer(GstBufferConsumer *consumer) {
|
||||
|
||||
buffer_consumers_ << consumer;
|
||||
if (current_pipeline_) current_pipeline_->AddBufferConsumer(consumer);
|
||||
|
||||
}
|
||||
|
||||
void GstEngine::RemoveBufferConsumer(GstBufferConsumer *consumer) {
|
||||
|
||||
buffer_consumers_.removeAll(consumer);
|
||||
if (current_pipeline_) current_pipeline_->RemoveBufferConsumer(consumer);
|
||||
|
||||
}
|
||||
|
||||
void GstEngine::timerEvent(QTimerEvent *e) {
|
||||
@@ -520,7 +539,7 @@ void GstEngine::NewMetaData(const int pipeline_id, const Engine::SimpleMetaBundl
|
||||
|
||||
}
|
||||
|
||||
void GstEngine::AddBufferToScope(GstBuffer *buf, const int pipeline_id) {
|
||||
void GstEngine::AddBufferToScope(GstBuffer *buf, const int pipeline_id, const QString &format) {
|
||||
|
||||
if (!current_pipeline_ || current_pipeline_->id() != pipeline_id) {
|
||||
gst_buffer_unref(buf);
|
||||
@@ -531,6 +550,7 @@ void GstEngine::AddBufferToScope(GstBuffer *buf, const int pipeline_id) {
|
||||
gst_buffer_unref(latest_buffer_);
|
||||
}
|
||||
|
||||
buffer_format_ = format;
|
||||
latest_buffer_ = buf;
|
||||
have_new_buffer_ = true;
|
||||
|
||||
@@ -739,11 +759,12 @@ shared_ptr<GstEnginePipeline> GstEngine::CreatePipeline() {
|
||||
|
||||
shared_ptr<GstEnginePipeline> ret(new GstEnginePipeline(this));
|
||||
ret->set_output_device(output_, device_);
|
||||
ret->set_volume_control(volume_control_);
|
||||
ret->set_volume_enabled(volume_control_);
|
||||
ret->set_stereo_balancer_enabled(stereo_balancer_enabled_);
|
||||
ret->set_equalizer_enabled(equalizer_enabled_);
|
||||
ret->set_replaygain(rg_enabled_, rg_mode_, rg_preamp_, rg_compression_);
|
||||
ret->set_buffer_duration_nanosec(buffer_duration_nanosec_);
|
||||
ret->set_buffer_min_fill(buffer_min_fill_);
|
||||
ret->SetEqualizerEnabled(equalizer_enabled_);
|
||||
|
||||
ret->AddBufferConsumer(this);
|
||||
for (GstBufferConsumer *consumer : buffer_consumers_) {
|
||||
@@ -806,13 +827,22 @@ void GstEngine::UpdateScope(const int chunk_length) {
|
||||
}
|
||||
|
||||
scope_chunk_++;
|
||||
memcpy(dest, source, bytes);
|
||||
|
||||
if (buffer_format_.startsWith("S16") ||
|
||||
buffer_format_.startsWith("U16") ||
|
||||
buffer_format_.startsWith("S32")) {
|
||||
memcpy(dest, source, bytes);
|
||||
}
|
||||
else {
|
||||
memset(dest, 0, bytes);
|
||||
}
|
||||
|
||||
gst_buffer_unmap(latest_buffer_, &map);
|
||||
|
||||
if (scope_chunk_ == scope_chunks_) {
|
||||
gst_buffer_unref(latest_buffer_);
|
||||
latest_buffer_ = nullptr;
|
||||
buffer_format_.clear();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -50,11 +50,6 @@
|
||||
class TaskManager;
|
||||
class GstEnginePipeline;
|
||||
|
||||
#ifdef Q_OS_MACOS
|
||||
struct _GTlsDatabase;
|
||||
typedef struct _GTlsDatabase GTlsDatabase;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @class GstEngine
|
||||
* @short GStreamer engine plugin
|
||||
@@ -95,28 +90,26 @@ class GstEngine : public Engine::Base, public GstBufferConsumer {
|
||||
void EnsureInitialised() { gst_startup_->EnsureInitialised(); }
|
||||
|
||||
GstElement *CreateElement(const QString &factoryName, GstElement *bin = nullptr, const bool showerror = true);
|
||||
void ConsumeBuffer(GstBuffer *buffer, int pipeline_id);
|
||||
void ConsumeBuffer(GstBuffer *buffer, const int pipeline_id, const QString &format);
|
||||
|
||||
public slots:
|
||||
|
||||
void ReloadSettings();
|
||||
|
||||
/** Set whether equalizer is enabled */
|
||||
// Set whether stereo balancer is enabled
|
||||
void SetStereoBalancerEnabled(const bool enabled);
|
||||
|
||||
// Set Stereo balance, range -1.0f..1.0f
|
||||
void SetStereoBalance(const float value);
|
||||
|
||||
// Set whether equalizer is enabled
|
||||
void SetEqualizerEnabled(const bool);
|
||||
|
||||
/** Set equalizer preamp and gains, range -100..100. Gains are 10 values. */
|
||||
// Set equalizer preamp and gains, range -100..100. Gains are 10 values.
|
||||
void SetEqualizerParameters(const int preamp, const QList<int> &bandGains);
|
||||
|
||||
/** Set Stereo balance, range -1.0f..1.0f */
|
||||
void SetStereoBalance(const float value);
|
||||
|
||||
void AddBufferConsumer(GstBufferConsumer *consumer);
|
||||
void RemoveBufferConsumer(GstBufferConsumer *consumer);
|
||||
|
||||
#ifdef Q_OS_MACOS
|
||||
GTlsDatabase *tls_database() const { return tls_database_; }
|
||||
#endif
|
||||
|
||||
protected:
|
||||
void timerEvent(QTimerEvent*);
|
||||
|
||||
@@ -124,7 +117,7 @@ class GstEngine : public Engine::Base, public GstBufferConsumer {
|
||||
void EndOfStreamReached(const int pipeline_id, const bool has_next_track);
|
||||
void HandlePipelineError(const int pipeline_id, const QString &message, const int domain, const int error_code);
|
||||
void NewMetaData(const int pipeline_id, const Engine::SimpleMetaBundle &bundle);
|
||||
void AddBufferToScope(GstBuffer *buf, const int pipeline_id);
|
||||
void AddBufferToScope(GstBuffer *buf, const int pipeline_id, const QString &format);
|
||||
void FadeoutFinished();
|
||||
void FadeoutPauseFinished();
|
||||
void SeekNow();
|
||||
@@ -180,9 +173,12 @@ class GstEngine : public Engine::Base, public GstBufferConsumer {
|
||||
|
||||
GstBuffer *latest_buffer_;
|
||||
|
||||
bool stereo_balancer_enabled_;
|
||||
float stereo_balance_;
|
||||
|
||||
bool equalizer_enabled_;
|
||||
int equalizer_preamp_;
|
||||
QList<int> equalizer_gains_;
|
||||
float stereo_balance_;
|
||||
|
||||
mutable bool can_decode_success_;
|
||||
mutable bool can_decode_last_;
|
||||
@@ -201,10 +197,7 @@ class GstEngine : public Engine::Base, public GstBufferConsumer {
|
||||
int scope_chunk_;
|
||||
bool have_new_buffer_;
|
||||
int scope_chunks_;
|
||||
|
||||
#ifdef Q_OS_MACOS
|
||||
GTlsDatabase* tls_database_;
|
||||
#endif
|
||||
QString buffer_format_;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -69,14 +69,15 @@ class GstEnginePipeline : public QObject {
|
||||
|
||||
// Call these setters before Init
|
||||
void set_output_device(const QString &sink, const QVariant &device);
|
||||
void set_volume_control(const bool volume_control);
|
||||
void set_volume_enabled(const bool enabled);
|
||||
void set_stereo_balancer_enabled(const bool enabled);
|
||||
void set_equalizer_enabled(const bool enabled);
|
||||
void set_replaygain(const bool enabled, const int mode, const float preamp, const bool compression);
|
||||
void set_buffer_duration_nanosec(qint64 duration_nanosec);
|
||||
void set_buffer_min_fill(int percent);
|
||||
|
||||
// Creates the pipeline, returns false on error
|
||||
bool InitFromUrl(const QByteArray &stream_url, const QUrl original_url, const qint64 end_nanosec);
|
||||
bool InitFromString(const QString &pipeline);
|
||||
|
||||
// GstBufferConsumers get fed audio data. Thread-safe.
|
||||
void AddBufferConsumer(GstBufferConsumer *consumer);
|
||||
@@ -86,10 +87,10 @@ class GstEnginePipeline : public QObject {
|
||||
// Control the music playback
|
||||
QFuture<GstStateChangeReturn> SetState(const GstState state);
|
||||
Q_INVOKABLE bool Seek(const qint64 nanosec);
|
||||
void SetEqualizerEnabled(const bool enabled);
|
||||
void SetEqualizerParams(const int preamp, const QList<int> &band_gains);
|
||||
void SetVolume(const int percent);
|
||||
void SetStereoBalance(const float value);
|
||||
void SetEqualizerParams(const int preamp, const QList<int> &band_gains);
|
||||
|
||||
void StartFader(const qint64 duration_nanosec, const QTimeLine::Direction direction = QTimeLine::Forward, const QTimeLine::CurveShape shape = QTimeLine::LinearCurve, const bool use_fudge_timer = true);
|
||||
|
||||
// If this is set then it will be loaded automatically when playback finishes for gapless playback
|
||||
@@ -102,6 +103,7 @@ class GstEnginePipeline : public QObject {
|
||||
QByteArray stream_url() const { return stream_url_; }
|
||||
QUrl original_url() const { return original_url_; }
|
||||
bool is_valid() const { return valid_; }
|
||||
|
||||
// Please note that this method (unlike GstEngine's.position()) is multiple-section media unaware.
|
||||
qint64 position() const;
|
||||
// Please note that this method (unlike GstEngine's.length()) is multiple-section media unaware.
|
||||
@@ -120,7 +122,7 @@ class GstEnginePipeline : public QObject {
|
||||
public slots:
|
||||
void SetVolumeModifier(qreal mod);
|
||||
|
||||
signals:
|
||||
signals:
|
||||
void EndOfStreamReached(const int pipeline_id, const bool has_next_track);
|
||||
void MetadataFound(const int pipeline_id, const Engine::SimpleMetaBundle &bundle);
|
||||
// This indicates an error, delegated from GStreamer, in the pipeline.
|
||||
@@ -136,16 +138,21 @@ signals:
|
||||
void timerEvent(QTimerEvent*);
|
||||
|
||||
private:
|
||||
bool InitAudioBin();
|
||||
|
||||
// Static callbacks. The GstEnginePipeline instance is passed in the last argument.
|
||||
static GstPadProbeReturn EventHandoffCallback(GstPad*, GstPadProbeInfo*, gpointer);
|
||||
static void SourceSetupCallback(GstPlayBin*, GParamSpec* pspec, gpointer);
|
||||
static void NewPadCallback(GstElement*, GstPad*, gpointer);
|
||||
static GstPadProbeReturn PlaybinProbe(GstPad*, GstPadProbeInfo*, gpointer);
|
||||
static GstPadProbeReturn HandoffCallback(GstPad*, GstPadProbeInfo*, gpointer);
|
||||
static void AboutToFinishCallback(GstPlayBin*, gpointer);
|
||||
static GstBusSyncReply BusCallbackSync(GstBus*, GstMessage*, gpointer);
|
||||
static gboolean BusCallback(GstBus*, GstMessage*, gpointer);
|
||||
static void NewPadCallback(GstElement*, GstPad*, gpointer);
|
||||
static GstPadProbeReturn HandoffCallback(GstPad*, GstPadProbeInfo*, gpointer);
|
||||
static GstPadProbeReturn EventHandoffCallback(GstPad*, GstPadProbeInfo*, gpointer);
|
||||
static void AboutToFinishCallback(GstPlayBin*, gpointer);
|
||||
static GstPadProbeReturn DecodebinProbe(GstPad*, GstPadProbeInfo*, gpointer);
|
||||
static void SourceSetupCallback(GstPlayBin*, GParamSpec* pspec, gpointer);
|
||||
static void TaskEnterCallback(GstTask*, GThread*, gpointer);
|
||||
static void StreamDiscovered(GstDiscoverer *discoverer, GstDiscovererInfo *info, GError *err, gpointer instance);
|
||||
static void StreamDiscoveryFinished(GstDiscoverer *discoverer, gpointer instance);
|
||||
static QString GSTdiscovererErrorMessage(GstDiscovererResult result);
|
||||
|
||||
void TagMessageReceived(GstMessage*);
|
||||
void ErrorMessageReceived(GstMessage*);
|
||||
@@ -158,17 +165,9 @@ signals:
|
||||
QString ParseStrTag(GstTagList *list, const char *tag) const;
|
||||
guint ParseUIntTag(GstTagList *list, const char *tag) const;
|
||||
|
||||
bool InitDecodeBin(GstElement *new_bin);
|
||||
bool InitAudioBin();
|
||||
GstElement *CreateDecodeBinFromString(const char *pipeline);
|
||||
|
||||
void UpdateVolume();
|
||||
void UpdateEqualizer();
|
||||
void UpdateStereoBalance();
|
||||
|
||||
static void StreamDiscovered(GstDiscoverer *discoverer, GstDiscovererInfo *info, GError *err, gpointer instance);
|
||||
static void StreamDiscoveryFinished(GstDiscoverer *discoverer, gpointer instance);
|
||||
static QString GSTdiscovererErrorMessage(GstDiscovererResult result);
|
||||
void UpdateEqualizer();
|
||||
|
||||
private slots:
|
||||
void FaderTimelineFinished();
|
||||
@@ -193,20 +192,21 @@ signals:
|
||||
bool valid_;
|
||||
QString output_;
|
||||
QVariant device_;
|
||||
bool volume_control_;
|
||||
|
||||
// Equalizer
|
||||
bool volume_enabled_;
|
||||
bool stereo_balancer_enabled_;
|
||||
bool eq_enabled_;
|
||||
int eq_preamp_;
|
||||
QList<int> eq_band_gains_;
|
||||
bool rg_enabled_;
|
||||
|
||||
// Stereo balance.
|
||||
// Stereo balance:
|
||||
// From -1.0 - 1.0
|
||||
// -1.0 is left, 1.0 is right.
|
||||
float stereo_balance_;
|
||||
|
||||
// Equalizer
|
||||
int eq_preamp_;
|
||||
QList<int> eq_band_gains_;
|
||||
|
||||
// ReplayGain
|
||||
bool rg_enabled_;
|
||||
int rg_mode_;
|
||||
float rg_preamp_;
|
||||
bool rg_compression_;
|
||||
@@ -247,7 +247,8 @@ signals:
|
||||
// When we need to specify the device to use as source (for CD device)
|
||||
QString source_device_;
|
||||
|
||||
// Seeking while the pipeline is in the READY state doesn't work, so we have to wait until it goes to PAUSED or PLAYING. Also we have to wait for the decodebin to be connected.
|
||||
// Seeking while the pipeline is in the READY state doesn't work, so we have to wait until it goes to PAUSED or PLAYING.
|
||||
// Also we have to wait for the playbin to be connected.
|
||||
bool pipeline_is_initialised_;
|
||||
bool pipeline_is_connected_;
|
||||
qint64 pending_seek_nanosec_;
|
||||
@@ -268,34 +269,24 @@ signals:
|
||||
bool use_fudge_timer_;
|
||||
|
||||
GstElement *pipeline_;
|
||||
|
||||
// The audiobin is either linked with a decodebin or set as sink of the playbin pipeline.
|
||||
GstElement *audiobin_;
|
||||
|
||||
// Elements in the audiobin. See comments in Init()'s definition.
|
||||
GstElement *queue_;
|
||||
GstElement *audioconvert_;
|
||||
GstElement *audioconvert2_;
|
||||
GstElement *audioscale_;
|
||||
GstElement *audiosink_;
|
||||
GstElement *audioqueue_;
|
||||
GstElement *volume_;
|
||||
GstElement *audio_panorama_;
|
||||
GstElement *equalizer_preamp_;
|
||||
GstElement *audiopanorama_;
|
||||
GstElement *equalizer_;
|
||||
GstElement *rgvolume_;
|
||||
GstElement *rglimiter_;
|
||||
GstElement *equalizer_preamp_;
|
||||
GstDiscoverer *discoverer_;
|
||||
|
||||
int about_to_finish_cb_id_;
|
||||
int pad_added_cb_id_;
|
||||
int notify_source_cb_id_;
|
||||
int about_to_finish_cb_id_;
|
||||
int bus_cb_id_;
|
||||
int discovery_finished_cb_id_;
|
||||
int discovery_discovered_cb_id_;
|
||||
|
||||
QThreadPool set_state_threadpool_;
|
||||
|
||||
GstSegment last_decodebin_segment_;
|
||||
GstSegment last_playbin_segment_;
|
||||
|
||||
};
|
||||
|
||||
|
||||
115
src/engine/mmdevicefinder.cpp
Normal file
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Strawberry Music Player
|
||||
* Copyright 2019, Jonas Kvinge <jonas@jkvinge.net>
|
||||
*
|
||||
* Strawberry is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Strawberry is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <initguid.h>
|
||||
#include <devpkey.h>
|
||||
#include <functiondiscoverykeys_devpkey.h>
|
||||
#include <mmdeviceapi.h>
|
||||
|
||||
#include <QList>
|
||||
#include <QVariant>
|
||||
#include <QString>
|
||||
|
||||
#include "mmdevicefinder.h"
|
||||
#include "core/logging.h"
|
||||
|
||||
MMDeviceFinder::MMDeviceFinder() : DeviceFinder("mmdevice", { "wasapisink" }) {}
|
||||
|
||||
QList<DeviceFinder::Device> MMDeviceFinder::ListDevices() {
|
||||
|
||||
HRESULT hr = S_OK;
|
||||
|
||||
IMMDeviceEnumerator *enumerator = nullptr;
|
||||
hr = CoCreateInstance(CLSID_MMDeviceEnumerator, nullptr, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void**)&enumerator);
|
||||
if (FAILED(hr)) {
|
||||
return QList<Device>();
|
||||
}
|
||||
|
||||
IMMDeviceCollection *collection = nullptr;
|
||||
hr = enumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &collection);
|
||||
if (FAILED(hr)) {
|
||||
enumerator->Release();
|
||||
return QList<Device>();
|
||||
}
|
||||
|
||||
UINT count;
|
||||
hr = collection->GetCount(&count);
|
||||
if (FAILED(hr)) {
|
||||
collection->Release();
|
||||
enumerator->Release();
|
||||
return QList<Device>();
|
||||
}
|
||||
|
||||
QList<Device> devices;
|
||||
Device default_device;
|
||||
default_device.description = "Default device";
|
||||
default_device.iconname = GuessIconName(default_device.description);
|
||||
devices.append(default_device);
|
||||
|
||||
for (ULONG i = 0 ; i < count ; i++) {
|
||||
|
||||
IMMDevice *endpoint = nullptr;
|
||||
hr = collection->Item(i, &endpoint);
|
||||
if (FAILED(hr)) { return devices; }
|
||||
|
||||
LPWSTR pwszid = nullptr;
|
||||
hr = endpoint->GetId(&pwszid);
|
||||
if (FAILED(hr)) {
|
||||
endpoint->Release();
|
||||
continue;
|
||||
}
|
||||
|
||||
IPropertyStore *props = nullptr;
|
||||
hr = endpoint->OpenPropertyStore(STGM_READ, &props);
|
||||
if (FAILED(hr)) {
|
||||
CoTaskMemFree(pwszid);
|
||||
endpoint->Release();
|
||||
continue;
|
||||
}
|
||||
|
||||
PROPVARIANT var_name;
|
||||
PropVariantInit(&var_name);
|
||||
hr = props->GetValue(PKEY_Device_FriendlyName, &var_name);
|
||||
if (FAILED(hr)) {
|
||||
props->Release();
|
||||
CoTaskMemFree(pwszid);
|
||||
endpoint->Release();
|
||||
continue;
|
||||
}
|
||||
|
||||
Device device;
|
||||
device.description = QString::fromWCharArray(var_name.pwszVal);
|
||||
device.iconname = GuessIconName(device.description);
|
||||
device.value = QString::fromStdWString(pwszid);
|
||||
devices.append(device);
|
||||
|
||||
PropVariantClear(&var_name);
|
||||
props->Release();
|
||||
CoTaskMemFree(pwszid);
|
||||
endpoint->Release();
|
||||
|
||||
}
|
||||
collection->Release();
|
||||
enumerator->Release();
|
||||
|
||||
return devices;
|
||||
|
||||
}
|
||||
34
src/engine/mmdevicefinder.h
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Strawberry Music Player
|
||||
* Copyright 2019, Jonas Kvinge <jonas@jkvinge.net>
|
||||
*
|
||||
* Strawberry is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Strawberry is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef MMDEVICEFINDER_H
|
||||
#define MMDEVICEFINDER_H
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "devicefinder.h"
|
||||
|
||||
class MMDeviceFinder : public DeviceFinder {
|
||||
public:
|
||||
MMDeviceFinder();
|
||||
virtual bool Initialise() { return true; }
|
||||
virtual QList<Device> ListDevices();
|
||||
};
|
||||
|
||||
#endif // MMDEVICEFINDER_H
|
||||
@@ -74,13 +74,14 @@ Equalizer::Equalizer(QWidget *parent)
|
||||
// Must be done before the signals are connected
|
||||
ReloadSettings();
|
||||
|
||||
connect(ui_->enable, SIGNAL(toggled(bool)), SIGNAL(EnabledChanged(bool)));
|
||||
connect(ui_->enable, SIGNAL(toggled(bool)), ui_->slider_container, SLOT(setEnabled(bool)));
|
||||
connect(ui_->enable, SIGNAL(toggled(bool)), SLOT(Save()));
|
||||
connect(ui_->enable_equalizer, SIGNAL(toggled(bool)), SLOT(EqualizerEnabledChangedSlot(bool)));
|
||||
|
||||
connect(ui_->preset, SIGNAL(currentIndexChanged(int)), SLOT(PresetChanged(int)));
|
||||
connect(ui_->preset_save, SIGNAL(clicked()), SLOT(SavePreset()));
|
||||
connect(ui_->preset_del, SIGNAL(clicked()), SLOT(DelPreset()));
|
||||
connect(ui_->balance_slider, SIGNAL(valueChanged(int)), SLOT(StereoSliderChanged(int)));
|
||||
|
||||
connect(ui_->enable_stereo_balancer, SIGNAL(toggled(bool)), SLOT(StereoBalancerEnabledChangedSlot(bool)));
|
||||
connect(ui_->stereo_balance_slider, SIGNAL(valueChanged(int)), SLOT(StereoBalanceSliderChanged(int)));
|
||||
|
||||
QShortcut *close = new QShortcut(QKeySequence::Close, this);
|
||||
connect(close, SIGNAL(activated()), SLOT(close()));
|
||||
@@ -116,12 +117,16 @@ void Equalizer::ReloadSettings() {
|
||||
if (selected_index != -1) ui_->preset->setCurrentIndex(selected_index);
|
||||
|
||||
// Enabled?
|
||||
ui_->enable->setChecked(s.value("enabled", false).toBool());
|
||||
ui_->slider_container->setEnabled(ui_->enable->isChecked());
|
||||
ui_->enable_equalizer->setChecked(s.value("enabled", false).toBool());
|
||||
ui_->slider_container->setEnabled(ui_->enable_equalizer->isChecked());
|
||||
|
||||
ui_->enable_stereo_balancer->setChecked(s.value("enable_stereo_balancer", false).toBool());
|
||||
ui_->slider_label_layout->setEnabled(ui_->enable_stereo_balancer->isChecked());
|
||||
ui_->stereo_balance_slider->setEnabled(ui_->enable_stereo_balancer->isChecked());
|
||||
|
||||
int stereo_balance = s.value("stereo_balance", 0).toInt();
|
||||
ui_->balance_slider->setValue(stereo_balance);
|
||||
StereoSliderChanged(stereo_balance);
|
||||
ui_->stereo_balance_slider->setValue(stereo_balance);
|
||||
StereoBalanceSliderChanged(stereo_balance);
|
||||
|
||||
PresetChanged(selected_preset);
|
||||
|
||||
@@ -184,7 +189,7 @@ void Equalizer::PresetChanged(const QString& name) {
|
||||
for (int i = 0; i < kBands; ++i) gain_[i]->set_value(p.gain[i]);
|
||||
loading_ = false;
|
||||
|
||||
ParametersChanged();
|
||||
EqualizerParametersChangedSlot();
|
||||
Save();
|
||||
|
||||
}
|
||||
@@ -234,14 +239,18 @@ EqualizerSlider *Equalizer::AddSlider(const QString &label) {
|
||||
|
||||
EqualizerSlider *ret = new EqualizerSlider(label, ui_->slider_container);
|
||||
ui_->slider_container->layout()->addWidget(ret);
|
||||
connect(ret, SIGNAL(ValueChanged(int)), SLOT(ParametersChanged()));
|
||||
connect(ret, SIGNAL(ValueChanged(int)), SLOT(EqualizerParametersChangedSlot()));
|
||||
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
bool Equalizer::is_enabled() const {
|
||||
return ui_->enable->isChecked();
|
||||
bool Equalizer::is_stereo_balancer_enabled() const {
|
||||
return ui_->enable_stereo_balancer->isChecked();
|
||||
}
|
||||
|
||||
bool Equalizer::is_equalizer_enabled() const {
|
||||
return ui_->enable_equalizer->isChecked();
|
||||
}
|
||||
|
||||
int Equalizer::preamp_value() const {
|
||||
@@ -268,13 +277,41 @@ Equalizer::Params Equalizer::current_params() const {
|
||||
}
|
||||
|
||||
float Equalizer::stereo_balance() const {
|
||||
return qBound(-1.0f, ui_->balance_slider->value() / 100.0f, 1.0f);
|
||||
return qBound(-1.0f, ui_->stereo_balance_slider->value() / 100.0f, 1.0f);
|
||||
}
|
||||
|
||||
void Equalizer::ParametersChanged() {
|
||||
if (loading_) return;
|
||||
void Equalizer::StereoBalancerEnabledChangedSlot(const bool enabled) {
|
||||
|
||||
if (!enabled) {
|
||||
ui_->stereo_balance_slider->setValue(0);
|
||||
emit StereoBalanceChanged(stereo_balance());
|
||||
}
|
||||
ui_->stereo_balance_slider->setEnabled(enabled);
|
||||
emit StereoBalancerEnabledChanged(enabled);
|
||||
Save();
|
||||
|
||||
}
|
||||
|
||||
void Equalizer::StereoBalanceSliderChanged(int) {
|
||||
|
||||
emit StereoBalanceChanged(stereo_balance());
|
||||
Save();
|
||||
|
||||
}
|
||||
|
||||
void Equalizer::EqualizerEnabledChangedSlot(const bool enabled) {
|
||||
|
||||
emit EqualizerEnabledChanged(enabled);
|
||||
ui_->slider_container->setEnabled(enabled);
|
||||
Save();
|
||||
|
||||
}
|
||||
|
||||
void Equalizer::EqualizerParametersChangedSlot() {
|
||||
|
||||
if (loading_) return;
|
||||
emit EqualizerParametersChanged(preamp_value(), gain_values());
|
||||
|
||||
emit ParametersChanged(preamp_value(), gain_values());
|
||||
}
|
||||
|
||||
void Equalizer::Save() {
|
||||
@@ -296,15 +333,14 @@ void Equalizer::Save() {
|
||||
s.setValue("selected_preset", ui_->preset->itemData(ui_->preset->currentIndex()).toString());
|
||||
|
||||
// Enabled?
|
||||
s.setValue("enabled", ui_->enable->isChecked());
|
||||
s.setValue("enabled", ui_->enable_equalizer->isChecked());
|
||||
|
||||
s.setValue("stereo_balance", ui_->balance_slider->value());
|
||||
s.setValue("enable_stereo_balancer", ui_->enable_stereo_balancer->isChecked());
|
||||
s.setValue("stereo_balance", ui_->stereo_balance_slider->value());
|
||||
|
||||
}
|
||||
|
||||
void Equalizer::closeEvent(QCloseEvent *e) {
|
||||
|
||||
Q_UNUSED(e);
|
||||
void Equalizer::closeEvent(QCloseEvent*) {
|
||||
|
||||
QString name = ui_->preset->currentText();
|
||||
if (!presets_.contains(name)) return;
|
||||
@@ -319,8 +355,7 @@ Equalizer::Params::Params() : preamp(0) {
|
||||
for (int i = 0; i < Equalizer::kBands; ++i) gain[i] = 0;
|
||||
}
|
||||
|
||||
Equalizer::Params::Params(int g0, int g1, int g2, int g3, int g4, int g5, int g6, int g7, int g8, int g9, int pre)
|
||||
: preamp(pre) {
|
||||
Equalizer::Params::Params(int g0, int g1, int g2, int g3, int g4, int g5, int g6, int g7, int g8, int g9, int pre) : preamp(pre) {
|
||||
gain[0] = g0;
|
||||
gain[1] = g1;
|
||||
gain[2] = g2;
|
||||
@@ -345,12 +380,6 @@ bool Equalizer::Params::operator !=(const Equalizer::Params& other) const {
|
||||
return ! (*this == other);
|
||||
}
|
||||
|
||||
void Equalizer::StereoSliderChanged(int value) {
|
||||
Q_UNUSED(value);
|
||||
emit StereoBalanceChanged(stereo_balance());
|
||||
Save();
|
||||
}
|
||||
|
||||
QDataStream &operator<<(QDataStream& s, const Equalizer::Params& p) {
|
||||
s << p.preamp;
|
||||
for (int i = 0; i < Equalizer::kBands; ++i) s << p.gain[i];
|
||||
|
||||
@@ -61,28 +61,32 @@ class Equalizer : public QDialog {
|
||||
int gain[kBands];
|
||||
};
|
||||
|
||||
bool is_enabled() const;
|
||||
bool is_equalizer_enabled() const;
|
||||
bool is_stereo_balancer_enabled() const;
|
||||
int preamp_value() const;
|
||||
QList<int> gain_values() const;
|
||||
Params current_params() const;
|
||||
float stereo_balance() const;
|
||||
|
||||
signals:
|
||||
void EnabledChanged(bool enabled);
|
||||
void ParametersChanged(int preamp, const QList<int> &band_gains);
|
||||
void StereoBalanceChanged(float balance);
|
||||
void StereoBalancerEnabledChanged(const bool enabled);
|
||||
void StereoBalanceChanged(const float balance);
|
||||
void EqualizerEnabledChanged(const bool enabled);
|
||||
void EqualizerParametersChanged(const int preamp, const QList<int> &band_gains);
|
||||
|
||||
protected:
|
||||
void closeEvent(QCloseEvent *);
|
||||
void closeEvent(QCloseEvent*);
|
||||
|
||||
private slots:
|
||||
void ParametersChanged();
|
||||
void StereoBalancerEnabledChangedSlot(const bool enabled);
|
||||
void StereoBalanceSliderChanged(const int value);
|
||||
void EqualizerEnabledChangedSlot(const bool enabled);
|
||||
void EqualizerParametersChangedSlot();
|
||||
void PresetChanged(const QString &name);
|
||||
void PresetChanged(int index);
|
||||
void SavePreset();
|
||||
void DelPreset();
|
||||
void Save();
|
||||
void StereoSliderChanged(int value);
|
||||
|
||||
private:
|
||||
EqualizerSlider *AddSlider(const QString &label);
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="enable">
|
||||
<widget class="QCheckBox" name="enable_equalizer">
|
||||
<property name="text">
|
||||
<string>Enable equalizer</string>
|
||||
</property>
|
||||
@@ -81,6 +81,13 @@
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2"/>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="enable_stereo_balancer">
|
||||
<property name="text">
|
||||
<string>Enable stereo balancer</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="slider_label_layout">
|
||||
<item>
|
||||
@@ -126,7 +133,7 @@
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSlider" name="balance_slider">
|
||||
<widget class="QSlider" name="stereo_balance_slider">
|
||||
<property name="minimum">
|
||||
<number>-100</number>
|
||||
</property>
|
||||
|
||||
@@ -299,8 +299,8 @@ void InternetCollectionView::contextMenuEvent(QContextMenuEvent *e) {
|
||||
|
||||
if (!context_menu_) {
|
||||
context_menu_ = new QMenu(this);
|
||||
add_to_playlist_ = context_menu_->addAction(IconLoader::Load("media-play"), tr("Append to current playlist"), this, SLOT(AddToPlaylist()));
|
||||
load_ = context_menu_->addAction(IconLoader::Load("media-play"), tr("Replace current playlist"), this, SLOT(Load()));
|
||||
add_to_playlist_ = context_menu_->addAction(IconLoader::Load("media-playback-start"), tr("Append to current playlist"), this, SLOT(AddToPlaylist()));
|
||||
load_ = context_menu_->addAction(IconLoader::Load("media-playback-start"), tr("Replace current playlist"), this, SLOT(Load()));
|
||||
open_in_new_playlist_ = context_menu_->addAction(IconLoader::Load("document-new"), tr("Open in new playlist"), this, SLOT(OpenInNewPlaylist()));
|
||||
|
||||
context_menu_->addSeparator();
|
||||
|
||||
@@ -430,8 +430,8 @@ bool InternetSearchView::SearchKeyEvent(QKeyEvent *event) {
|
||||
bool InternetSearchView::ResultsContextMenuEvent(QContextMenuEvent *event) {
|
||||
|
||||
context_menu_ = new QMenu(this);
|
||||
context_actions_ << context_menu_->addAction( IconLoader::Load("media-play"), tr("Append to current playlist"), this, SLOT(AddSelectedToPlaylist()));
|
||||
context_actions_ << context_menu_->addAction( IconLoader::Load("media-play"), tr("Replace current playlist"), this, SLOT(LoadSelected()));
|
||||
context_actions_ << context_menu_->addAction( IconLoader::Load("media-playback-start"), tr("Append to current playlist"), this, SLOT(AddSelectedToPlaylist()));
|
||||
context_actions_ << context_menu_->addAction( IconLoader::Load("media-playback-start"), tr("Replace current playlist"), this, SLOT(LoadSelected()));
|
||||
context_actions_ << context_menu_->addAction( IconLoader::Load("document-new"), tr("Open in new playlist"), this, SLOT(OpenSelectedInNewPlaylist()));
|
||||
|
||||
context_menu_->addSeparator();
|
||||
|
||||
@@ -120,10 +120,7 @@ int main(int argc, char* argv[]) {
|
||||
QCoreApplication::setApplicationVersion(STRAWBERRY_VERSION_DISPLAY);
|
||||
QCoreApplication::setOrganizationDomain("strawberrymusicplayer.org");
|
||||
|
||||
// This makes us show up nicely in gnome-volume-control
|
||||
#if !GLIB_CHECK_VERSION(2, 36, 0) // Deprecated in glib 2.36.0
|
||||
g_type_init();
|
||||
#endif
|
||||
// This makes us show up nicely in gnome-volume-control
|
||||
g_set_application_name(QCoreApplication::applicationName().toLocal8Bit());
|
||||
|
||||
RegisterMetaTypes();
|
||||
|
||||
@@ -330,7 +330,6 @@ void Organise::UpdateProgress() {
|
||||
|
||||
}
|
||||
|
||||
#ifdef HAVE_GSTREAMER
|
||||
void Organise::FileTranscoded(const QString &input, const QString &output, bool success) {
|
||||
|
||||
Q_UNUSED(output);
|
||||
@@ -348,7 +347,6 @@ void Organise::FileTranscoded(const QString &input, const QString &output, bool
|
||||
QTimer::singleShot(0, this, SLOT(ProcessSomeFiles()));
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
void Organise::timerEvent(QTimerEvent *e) {
|
||||
|
||||
|
||||
@@ -81,9 +81,7 @@ class Organise : public QObject {
|
||||
|
||||
private slots:
|
||||
void ProcessSomeFiles();
|
||||
#ifdef HAVE_GSTREAMER
|
||||
void FileTranscoded(const QString &input, const QString &output, bool success);
|
||||
#endif
|
||||
void LogLine(const QString message);
|
||||
|
||||
private:
|
||||
@@ -125,14 +123,9 @@ class Organise : public QObject {
|
||||
int task_count_;
|
||||
const QString playlist_;
|
||||
|
||||
#ifdef HAVE_GSTREAMER
|
||||
QBasicTimer transcode_progress_timer_;
|
||||
#endif
|
||||
|
||||
QList<Task> tasks_pending_;
|
||||
#ifdef HAVE_GSTREAMER
|
||||
QMap<QString, Task> tasks_transcoding_;
|
||||
#endif
|
||||
int tasks_complete_;
|
||||
|
||||
bool started_;
|
||||
|
||||
@@ -600,7 +600,11 @@ void Playlist::set_current_row(int i, bool is_stopping) {
|
||||
queue_->TakeNext();
|
||||
}
|
||||
|
||||
if (current_item_index_ == old_current_item_index) return;
|
||||
if (current_item_index_ == old_current_item_index) {
|
||||
UpdateScrobblePoint();
|
||||
nowplaying_ = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (old_current_item_index.isValid()) {
|
||||
emit dataChanged(old_current_item_index, old_current_item_index.sibling(old_current_item_index.row(), ColumnCount - 1));
|
||||
@@ -1121,7 +1125,7 @@ bool Playlist::CompareItems(int column, Qt::SortOrder order, shared_ptr<Playlist
|
||||
case Column_Year: cmp(year);
|
||||
case Column_OriginalYear: cmp(originalyear);
|
||||
case Column_Genre: strcmp(genre);
|
||||
case Column_AlbumArtist: strcmp(playlist_albumartist);
|
||||
case Column_AlbumArtist: strcmp(playlist_albumartist_sortable);
|
||||
case Column_Composer: strcmp(composer);
|
||||
case Column_Performer: strcmp(performer);
|
||||
case Column_Grouping: strcmp(grouping);
|
||||
|
||||
@@ -161,6 +161,7 @@ PlaylistView::PlaylistView(QWidget *parent)
|
||||
previous_background_image_x_(0),
|
||||
previous_background_image_y_(0),
|
||||
glow_enabled_(true),
|
||||
select_track_(false),
|
||||
currently_glowing_(false),
|
||||
glow_intensity_step_(0),
|
||||
inhibit_autoscroll_timer_(new QTimer(this)),
|
||||
@@ -1004,6 +1005,7 @@ void PlaylistView::ReloadSettings() {
|
||||
#endif
|
||||
glow_enabled_ = s.value("glow_effect", glow_effect).toBool();
|
||||
bool editmetadatainline = s.value("editmetadatainline", false).toBool();
|
||||
select_track_ = s.value("select_track", false).toBool();
|
||||
s.endGroup();
|
||||
|
||||
s.beginGroup(Playlist::kSettingsGroup);
|
||||
@@ -1228,7 +1230,15 @@ void PlaylistView::CopyCurrentSongToClipboard() const {
|
||||
}
|
||||
|
||||
void PlaylistView::SongChanged(const Song &song) {
|
||||
|
||||
song_playing_ = song;
|
||||
|
||||
if (select_track_ && playlist_) {
|
||||
clearSelection();
|
||||
QItemSelection selection(playlist_->index(playlist_->current_row(), 0), playlist_->index(playlist_->current_row(), playlist_->ColumnCount - 1));
|
||||
selectionModel()->select(selection, QItemSelectionModel::Select);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void PlaylistView::Playing() {}
|
||||
|
||||
@@ -240,6 +240,8 @@ class PlaylistView : public QTreeView {
|
||||
int previous_background_image_y_;
|
||||
|
||||
bool glow_enabled_;
|
||||
bool select_track_;
|
||||
|
||||
bool currently_glowing_;
|
||||
QBasicTimer glow_timer_;
|
||||
int glow_intensity_step_;
|
||||
|
||||
@@ -72,6 +72,7 @@ void AudioScrobbler::ReloadSettings() {
|
||||
scrobble_button_ = s.value("scrobble_button", false).toBool();
|
||||
love_button_ = s.value("love_button", false).toBool();
|
||||
submit_delay_ = s.value("submit", 0).toInt();
|
||||
prefer_albumartist_ = s.value("albumartist", false).toBool();
|
||||
s.endGroup();
|
||||
|
||||
emit ScrobblingEnabledChanged(enabled_);
|
||||
|
||||
@@ -48,6 +48,7 @@ class AudioScrobbler : public QObject {
|
||||
bool ScrobbleButton() const { return scrobble_button_; }
|
||||
bool LoveButton() const { return love_button_; }
|
||||
int SubmitDelay() const { return submit_delay_; }
|
||||
bool PreferAlbumArtist() const { return prefer_albumartist_; }
|
||||
|
||||
void UpdateNowPlaying(const Song &song);
|
||||
void ClearPlaying();
|
||||
@@ -87,6 +88,7 @@ class AudioScrobbler : public QObject {
|
||||
bool scrobble_button_;
|
||||
bool love_button_;
|
||||
int submit_delay_;
|
||||
bool prefer_albumartist_;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -35,6 +35,8 @@ class ScrobblerCacheItem : public QObject {
|
||||
explicit ScrobblerCacheItem(const QString &artist, const QString &album, const QString &song, const QString &albumartist, const int track, const qint64 duration, const quint64 ×tamp);
|
||||
~ScrobblerCacheItem();
|
||||
|
||||
QString effective_albumartist() const { return albumartist_.isEmpty() ? artist_ : albumartist_; }
|
||||
|
||||
public:
|
||||
QString artist_;
|
||||
QString album_;
|
||||
|
||||