Reformat all non-3rd-party C/C++/Objective-C++.
Command line: find src ext -regex '.*\.\(h\|cpp\|mm\)' -exec clang-format -i -style='{BasedOnStyle: Google, DerivePointerBinding: false}' {} \;
This commit is contained in:
parent
acfc7e6d21
commit
bebd781fdf
|
@ -18,7 +18,6 @@
|
||||||
// it is used by the Spotify blob which links against libspotify and is not GPL
|
// it is used by the Spotify blob which links against libspotify and is not GPL
|
||||||
// compatible.
|
// compatible.
|
||||||
|
|
||||||
|
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
|
|
||||||
|
|
|
@ -31,9 +31,7 @@ MediaPipeline::MediaPipeline(int port, quint64 length_msec)
|
||||||
pipeline_(nullptr),
|
pipeline_(nullptr),
|
||||||
appsrc_(nullptr),
|
appsrc_(nullptr),
|
||||||
byte_rate_(1),
|
byte_rate_(1),
|
||||||
offset_bytes_(0)
|
offset_bytes_(0) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
MediaPipeline::~MediaPipeline() {
|
MediaPipeline::~MediaPipeline() {
|
||||||
if (pipeline_) {
|
if (pipeline_) {
|
||||||
|
@ -43,8 +41,7 @@ MediaPipeline::~MediaPipeline() {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MediaPipeline::Init(int sample_rate, int channels) {
|
bool MediaPipeline::Init(int sample_rate, int channels) {
|
||||||
if (is_initialised())
|
if (is_initialised()) return false;
|
||||||
return false;
|
|
||||||
|
|
||||||
pipeline_ = gst_pipeline_new("pipeline");
|
pipeline_ = gst_pipeline_new("pipeline");
|
||||||
|
|
||||||
|
@ -54,10 +51,21 @@ bool MediaPipeline::Init(int sample_rate, int channels) {
|
||||||
tcpsink_ = gst_element_factory_make("tcpclientsink", nullptr);
|
tcpsink_ = gst_element_factory_make("tcpclientsink", nullptr);
|
||||||
|
|
||||||
if (!pipeline_ || !appsrc_ || !tcpsink_) {
|
if (!pipeline_ || !appsrc_ || !tcpsink_) {
|
||||||
if (pipeline_) { gst_object_unref(GST_OBJECT(pipeline_)); pipeline_ = nullptr; }
|
if (pipeline_) {
|
||||||
if (appsrc_) { gst_object_unref(GST_OBJECT(appsrc_)); appsrc_ = nullptr; }
|
gst_object_unref(GST_OBJECT(pipeline_));
|
||||||
if (gdppay) { gst_object_unref(GST_OBJECT(gdppay)); }
|
pipeline_ = nullptr;
|
||||||
if (tcpsink_) { gst_object_unref(GST_OBJECT(tcpsink_)); tcpsink_ = nullptr; }
|
}
|
||||||
|
if (appsrc_) {
|
||||||
|
gst_object_unref(GST_OBJECT(appsrc_));
|
||||||
|
appsrc_ = nullptr;
|
||||||
|
}
|
||||||
|
if (gdppay) {
|
||||||
|
gst_object_unref(GST_OBJECT(gdppay));
|
||||||
|
}
|
||||||
|
if (tcpsink_) {
|
||||||
|
gst_object_unref(GST_OBJECT(tcpsink_));
|
||||||
|
tcpsink_ = nullptr;
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,7 +81,8 @@ bool MediaPipeline::Init(int sample_rate, int channels) {
|
||||||
|
|
||||||
// Try to send 5 seconds of audio in advance to initially fill Clementine's
|
// Try to send 5 seconds of audio in advance to initially fill Clementine's
|
||||||
// buffer.
|
// buffer.
|
||||||
g_object_set(G_OBJECT(tcpsink_), "ts-offset", qint64(-5 * kNsecPerSec), nullptr);
|
g_object_set(G_OBJECT(tcpsink_), "ts-offset", qint64(-5 * kNsecPerSec),
|
||||||
|
nullptr);
|
||||||
|
|
||||||
// We know the time of each buffer
|
// We know the time of each buffer
|
||||||
g_object_set(G_OBJECT(appsrc_), "format", GST_FORMAT_TIME, nullptr);
|
g_object_set(G_OBJECT(appsrc_), "format", GST_FORMAT_TIME, nullptr);
|
||||||
|
@ -97,13 +106,10 @@ bool MediaPipeline::Init(int sample_rate, int channels) {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Set caps
|
// Set caps
|
||||||
GstCaps* caps = gst_caps_new_simple("audio/x-raw-int",
|
GstCaps* caps = gst_caps_new_simple(
|
||||||
"endianness", G_TYPE_INT, endianness,
|
"audio/x-raw-int", "endianness", G_TYPE_INT, endianness, "signed",
|
||||||
"signed", G_TYPE_BOOLEAN, TRUE,
|
G_TYPE_BOOLEAN, TRUE, "width", G_TYPE_INT, 16, "depth", G_TYPE_INT, 16,
|
||||||
"width", G_TYPE_INT, 16,
|
"rate", G_TYPE_INT, sample_rate, "channels", G_TYPE_INT, channels,
|
||||||
"depth", G_TYPE_INT, 16,
|
|
||||||
"rate", G_TYPE_INT, sample_rate,
|
|
||||||
"channels", G_TYPE_INT, channels,
|
|
||||||
nullptr);
|
nullptr);
|
||||||
|
|
||||||
gst_app_src_set_caps(appsrc_, caps);
|
gst_app_src_set_caps(appsrc_, caps);
|
||||||
|
@ -115,12 +121,12 @@ bool MediaPipeline::Init(int sample_rate, int channels) {
|
||||||
gst_app_src_set_size(appsrc_, bytes);
|
gst_app_src_set_size(appsrc_, bytes);
|
||||||
|
|
||||||
// Ready to go
|
// Ready to go
|
||||||
return gst_element_set_state(pipeline_, GST_STATE_PLAYING) != GST_STATE_CHANGE_FAILURE;
|
return gst_element_set_state(pipeline_, GST_STATE_PLAYING) !=
|
||||||
|
GST_STATE_CHANGE_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MediaPipeline::WriteData(const char* data, qint64 length) {
|
void MediaPipeline::WriteData(const char* data, qint64 length) {
|
||||||
if (!is_initialised())
|
if (!is_initialised()) return;
|
||||||
return;
|
|
||||||
|
|
||||||
GstBuffer* buffer = gst_buffer_new_and_alloc(length);
|
GstBuffer* buffer = gst_buffer_new_and_alloc(length);
|
||||||
|
|
||||||
|
@ -137,8 +143,7 @@ void MediaPipeline::WriteData(const char* data, qint64 length) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void MediaPipeline::EndStream() {
|
void MediaPipeline::EndStream() {
|
||||||
if (!is_initialised())
|
if (!is_initialised()) return;
|
||||||
return;
|
|
||||||
|
|
||||||
gst_app_src_end_of_stream(appsrc_);
|
gst_app_src_end_of_stream(appsrc_);
|
||||||
}
|
}
|
||||||
|
@ -153,7 +158,8 @@ void MediaPipeline::EnoughDataCallback(GstAppSrc* src, void* data) {
|
||||||
me->accepting_data_ = false;
|
me->accepting_data_ = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
gboolean MediaPipeline::SeekDataCallback(GstAppSrc* src, guint64 offset, void * data) {
|
gboolean MediaPipeline::SeekDataCallback(GstAppSrc* src, guint64 offset,
|
||||||
|
void* data) {
|
||||||
// MediaPipeline* me = reinterpret_cast<MediaPipeline*>(data);
|
// MediaPipeline* me = reinterpret_cast<MediaPipeline*>(data);
|
||||||
|
|
||||||
qLog(Debug) << "Gstreamer wants seek to" << offset;
|
qLog(Debug) << "Gstreamer wants seek to" << offset;
|
||||||
|
|
|
@ -31,7 +31,8 @@ namespace utilities {
|
||||||
|
|
||||||
QString GetCacheDirectory() {
|
QString GetCacheDirectory() {
|
||||||
QString user_cache = GetUserDataDirectory();
|
QString user_cache = GetUserDataDirectory();
|
||||||
return user_cache + "/" + QCoreApplication::applicationName() + "/spotify-cache";
|
return user_cache + "/" + QCoreApplication::applicationName() +
|
||||||
|
"/spotify-cache";
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef Q_OS_DARWIN // See spotify_utilities.mm for Mac implementation.
|
#ifndef Q_OS_DARWIN // See spotify_utilities.mm for Mac implementation.
|
||||||
|
@ -47,7 +48,8 @@ QString GetSettingsDirectory() {
|
||||||
QString ret;
|
QString ret;
|
||||||
|
|
||||||
#ifdef Q_OS_WIN32
|
#ifdef Q_OS_WIN32
|
||||||
ret = GetUserDataDirectory() + "/" + QCoreApplication::applicationName() + "/spotify-settings";
|
ret = GetUserDataDirectory() + "/" + QCoreApplication::applicationName() +
|
||||||
|
"/spotify-settings";
|
||||||
#else
|
#else
|
||||||
ret = QFileInfo(QSettings().fileName()).absolutePath() + "/spotify-settings";
|
ret = QFileInfo(QSettings().fileName()).absolutePath() + "/spotify-settings";
|
||||||
#endif // Q_OS_WIN32
|
#endif // Q_OS_WIN32
|
||||||
|
|
|
@ -32,7 +32,6 @@ QString GetUserDataDirectory();
|
||||||
QString GetCacheDirectory();
|
QString GetCacheDirectory();
|
||||||
|
|
||||||
QString GetSettingsDirectory();
|
QString GetSettingsDirectory();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -10,10 +10,8 @@ QString GetUserDataDirectory() {
|
||||||
NSAutoreleasePool* pool = [NSAutoreleasePool alloc];
|
NSAutoreleasePool* pool = [NSAutoreleasePool alloc];
|
||||||
[pool init];
|
[pool init];
|
||||||
|
|
||||||
NSArray* paths = NSSearchPathForDirectoriesInDomains(
|
NSArray* paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory,
|
||||||
NSCachesDirectory,
|
NSUserDomainMask, YES);
|
||||||
NSUserDomainMask,
|
|
||||||
YES);
|
|
||||||
QString ret;
|
QString ret;
|
||||||
if ([paths count] > 0) {
|
if ([paths count] > 0) {
|
||||||
NSString* user_path = [paths objectAtIndex:0];
|
NSString* user_path = [paths objectAtIndex:0];
|
||||||
|
@ -28,9 +26,7 @@ QString GetUserDataDirectory() {
|
||||||
QString GetSettingsDirectory() {
|
QString GetSettingsDirectory() {
|
||||||
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
|
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
|
||||||
NSArray* paths = NSSearchPathForDirectoriesInDomains(
|
NSArray* paths = NSSearchPathForDirectoriesInDomains(
|
||||||
NSApplicationSupportDirectory,
|
NSApplicationSupportDirectory, NSUserDomainMask, YES);
|
||||||
NSUserDomainMask,
|
|
||||||
YES);
|
|
||||||
NSString* ret;
|
NSString* ret;
|
||||||
if ([paths count] > 0) {
|
if ([paths count] > 0) {
|
||||||
ret = [paths objectAtIndex:0];
|
ret = [paths objectAtIndex:0];
|
||||||
|
@ -40,8 +36,7 @@ QString GetSettingsDirectory() {
|
||||||
ret = [ret stringByAppendingString:@"/Clementine/spotify-settings"];
|
ret = [ret stringByAppendingString:@"/Clementine/spotify-settings"];
|
||||||
|
|
||||||
NSFileManager* file_manager = [NSFileManager defaultManager];
|
NSFileManager* file_manager = [NSFileManager defaultManager];
|
||||||
[file_manager createDirectoryAtPath:
|
[file_manager createDirectoryAtPath:ret
|
||||||
ret
|
|
||||||
withIntermediateDirectories:YES
|
withIntermediateDirectories:YES
|
||||||
attributes:nil
|
attributes:nil
|
||||||
error:nil];
|
error:nil];
|
||||||
|
@ -50,5 +45,4 @@ QString GetSettingsDirectory() {
|
||||||
[pool drain];
|
[pool drain];
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,6 @@
|
||||||
const int SpotifyClient::kSpotifyImageIDSize = 20;
|
const int SpotifyClient::kSpotifyImageIDSize = 20;
|
||||||
const int SpotifyClient::kWaveHeaderSize = 44;
|
const int SpotifyClient::kWaveHeaderSize = 44;
|
||||||
|
|
||||||
|
|
||||||
SpotifyClient::SpotifyClient(QObject* parent)
|
SpotifyClient::SpotifyClient(QObject* parent)
|
||||||
: AbstractMessageHandler<pb::spotify::Message>(nullptr, parent),
|
: AbstractMessageHandler<pb::spotify::Message>(nullptr, parent),
|
||||||
api_key_(QByteArray::fromBase64(kSpotifyApiKey)),
|
api_key_(QByteArray::fromBase64(kSpotifyApiKey)),
|
||||||
|
@ -47,7 +46,8 @@ SpotifyClient::SpotifyClient(QObject* parent)
|
||||||
|
|
||||||
memset(&spotify_callbacks_, 0, sizeof(spotify_callbacks_));
|
memset(&spotify_callbacks_, 0, sizeof(spotify_callbacks_));
|
||||||
memset(&spotify_config_, 0, sizeof(spotify_config_));
|
memset(&spotify_config_, 0, sizeof(spotify_config_));
|
||||||
memset(&playlistcontainer_callbacks_, 0, sizeof(playlistcontainer_callbacks_));
|
memset(&playlistcontainer_callbacks_, 0,
|
||||||
|
sizeof(playlistcontainer_callbacks_));
|
||||||
memset(&get_playlists_callbacks_, 0, sizeof(get_playlists_callbacks_));
|
memset(&get_playlists_callbacks_, 0, sizeof(get_playlists_callbacks_));
|
||||||
memset(&load_playlist_callbacks_, 0, sizeof(load_playlist_callbacks_));
|
memset(&load_playlist_callbacks_, 0, sizeof(load_playlist_callbacks_));
|
||||||
|
|
||||||
|
@ -64,15 +64,17 @@ SpotifyClient::SpotifyClient(QObject* parent)
|
||||||
spotify_callbacks_.start_playback = &StartPlaybackCallback;
|
spotify_callbacks_.start_playback = &StartPlaybackCallback;
|
||||||
spotify_callbacks_.stop_playback = &StopPlaybackCallback;
|
spotify_callbacks_.stop_playback = &StopPlaybackCallback;
|
||||||
|
|
||||||
|
playlistcontainer_callbacks_.container_loaded =
|
||||||
playlistcontainer_callbacks_.container_loaded = &PlaylistContainerLoadedCallback;
|
&PlaylistContainerLoadedCallback;
|
||||||
playlistcontainer_callbacks_.playlist_added = &PlaylistAddedCallback;
|
playlistcontainer_callbacks_.playlist_added = &PlaylistAddedCallback;
|
||||||
playlistcontainer_callbacks_.playlist_moved = &PlaylistMovedCallback;
|
playlistcontainer_callbacks_.playlist_moved = &PlaylistMovedCallback;
|
||||||
playlistcontainer_callbacks_.playlist_removed = &PlaylistRemovedCallback;
|
playlistcontainer_callbacks_.playlist_removed = &PlaylistRemovedCallback;
|
||||||
|
|
||||||
get_playlists_callbacks_.playlist_state_changed = &PlaylistStateChangedForGetPlaylists;
|
get_playlists_callbacks_.playlist_state_changed =
|
||||||
|
&PlaylistStateChangedForGetPlaylists;
|
||||||
|
|
||||||
load_playlist_callbacks_.playlist_state_changed = &PlaylistStateChangedForLoadPlaylist;
|
load_playlist_callbacks_.playlist_state_changed =
|
||||||
|
&PlaylistStateChangedForLoadPlaylist;
|
||||||
|
|
||||||
QString cache = utilities::GetCacheDirectory();
|
QString cache = utilities::GetCacheDirectory();
|
||||||
qLog(Debug) << "Using:" << cache << "for Spotify cache";
|
qLog(Debug) << "Using:" << cache << "for Spotify cache";
|
||||||
|
@ -111,9 +113,11 @@ void SpotifyClient::Init(quint16 port) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpotifyClient::LoggedInCallback(sp_session* session, sp_error error) {
|
void SpotifyClient::LoggedInCallback(sp_session* session, sp_error error) {
|
||||||
SpotifyClient* me = reinterpret_cast<SpotifyClient*>(sp_session_userdata(session));
|
SpotifyClient* me =
|
||||||
|
reinterpret_cast<SpotifyClient*>(sp_session_userdata(session));
|
||||||
const bool success = error == SP_ERROR_OK;
|
const bool success = error == SP_ERROR_OK;
|
||||||
pb::spotify::LoginResponse_Error error_code = pb::spotify::LoginResponse_Error_Other;
|
pb::spotify::LoginResponse_Error error_code =
|
||||||
|
pb::spotify::LoginResponse_Error_Other;
|
||||||
|
|
||||||
if (!success) {
|
if (!success) {
|
||||||
qLog(Warning) << "Failed to login" << sp_error_message(error);
|
qLog(Warning) << "Failed to login" << sp_error_message(error);
|
||||||
|
@ -137,15 +141,15 @@ void SpotifyClient::LoggedInCallback(sp_session* session, sp_error error) {
|
||||||
me->SendLoginCompleted(success, sp_error_message(error), error_code);
|
me->SendLoginCompleted(success, sp_error_message(error), error_code);
|
||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
sp_playlistcontainer_add_callbacks(
|
sp_playlistcontainer_add_callbacks(sp_session_playlistcontainer(session),
|
||||||
sp_session_playlistcontainer(session),
|
|
||||||
&me->playlistcontainer_callbacks_, me);
|
&me->playlistcontainer_callbacks_, me);
|
||||||
sp_session_flush_caches(me->session_);
|
sp_session_flush_caches(me->session_);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpotifyClient::NotifyMainThreadCallback(sp_session* session) {
|
void SpotifyClient::NotifyMainThreadCallback(sp_session* session) {
|
||||||
SpotifyClient* me = reinterpret_cast<SpotifyClient*>(sp_session_userdata(session));
|
SpotifyClient* me =
|
||||||
|
reinterpret_cast<SpotifyClient*>(sp_session_userdata(session));
|
||||||
QMetaObject::invokeMethod(me, "ProcessEvents", Qt::QueuedConnection);
|
QMetaObject::invokeMethod(me, "ProcessEvents", Qt::QueuedConnection);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -160,14 +164,11 @@ void SpotifyClient::LogMessageCallback(sp_session* session, const char* data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpotifyClient::Search(const pb::spotify::SearchRequest& req) {
|
void SpotifyClient::Search(const pb::spotify::SearchRequest& req) {
|
||||||
sp_search* search = sp_search_create(
|
sp_search* search =
|
||||||
session_, req.query().c_str(),
|
sp_search_create(session_, req.query().c_str(), 0, req.limit(), 0,
|
||||||
0, req.limit(),
|
req.limit_album(), 0, 0, // artists
|
||||||
0, req.limit_album(),
|
|
||||||
0, 0, // artists
|
|
||||||
0, 0, // playlists
|
0, 0, // playlists
|
||||||
SP_SEARCH_STANDARD,
|
SP_SEARCH_STANDARD, &SearchCompleteCallback, this);
|
||||||
&SearchCompleteCallback, this);
|
|
||||||
|
|
||||||
pending_searches_[search] = req;
|
pending_searches_[search] = req;
|
||||||
}
|
}
|
||||||
|
@ -186,8 +187,8 @@ void SpotifyClient::SearchCompleteCallback(sp_search* result, void* userdata) {
|
||||||
if (count != 0) {
|
if (count != 0) {
|
||||||
for (int i = 0; i < count; ++i) {
|
for (int i = 0; i < count; ++i) {
|
||||||
sp_album* album = sp_search_album(result, i);
|
sp_album* album = sp_search_album(result, i);
|
||||||
sp_albumbrowse* browse =
|
sp_albumbrowse* browse = sp_albumbrowse_create(
|
||||||
sp_albumbrowse_create(me->session_, album, &SearchAlbumBrowseComplete, me);
|
me->session_, album, &SearchAlbumBrowseComplete, me);
|
||||||
|
|
||||||
me->pending_search_album_browse_responses_[browse] = result;
|
me->pending_search_album_browse_responses_[browse] = result;
|
||||||
}
|
}
|
||||||
|
@ -197,7 +198,8 @@ void SpotifyClient::SearchCompleteCallback(sp_search* result, void* userdata) {
|
||||||
me->SendSearchResponse(result);
|
me->SendSearchResponse(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpotifyClient::SearchAlbumBrowseComplete(sp_albumbrowse* result, void* userdata) {
|
void SpotifyClient::SearchAlbumBrowseComplete(sp_albumbrowse* result,
|
||||||
|
void* userdata) {
|
||||||
SpotifyClient* me = reinterpret_cast<SpotifyClient*>(userdata);
|
SpotifyClient* me = reinterpret_cast<SpotifyClient*>(userdata);
|
||||||
|
|
||||||
if (!me->pending_search_album_browse_responses_.contains(result)) {
|
if (!me->pending_search_album_browse_responses_.contains(result)) {
|
||||||
|
@ -208,7 +210,8 @@ void SpotifyClient::SearchAlbumBrowseComplete(sp_albumbrowse* result, void* user
|
||||||
sp_search* search = me->pending_search_album_browse_responses_.take(result);
|
sp_search* search = me->pending_search_album_browse_responses_.take(result);
|
||||||
me->pending_search_album_browses_[search].append(result);
|
me->pending_search_album_browses_[search].append(result);
|
||||||
|
|
||||||
if (me->pending_search_album_browses_[search].count() >= sp_search_num_albums(search)) {
|
if (me->pending_search_album_browses_[search].count() >=
|
||||||
|
sp_search_num_albums(search)) {
|
||||||
me->SendSearchResponse(search);
|
me->SendSearchResponse(search);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -290,16 +293,23 @@ void SpotifyClient::MessageArrived(const pb::spotify::Message& message) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpotifyClient::SetPlaybackSettings(const pb::spotify::PlaybackSettings& req) {
|
void SpotifyClient::SetPlaybackSettings(
|
||||||
|
const pb::spotify::PlaybackSettings& req) {
|
||||||
sp_bitrate bitrate = SP_BITRATE_320k;
|
sp_bitrate bitrate = SP_BITRATE_320k;
|
||||||
switch (req.bitrate()) {
|
switch (req.bitrate()) {
|
||||||
case pb::spotify::Bitrate96k: bitrate = SP_BITRATE_96k; break;
|
case pb::spotify::Bitrate96k:
|
||||||
case pb::spotify::Bitrate160k: bitrate = SP_BITRATE_160k; break;
|
bitrate = SP_BITRATE_96k;
|
||||||
case pb::spotify::Bitrate320k: bitrate = SP_BITRATE_320k; break;
|
break;
|
||||||
|
case pb::spotify::Bitrate160k:
|
||||||
|
bitrate = SP_BITRATE_160k;
|
||||||
|
break;
|
||||||
|
case pb::spotify::Bitrate320k:
|
||||||
|
bitrate = SP_BITRATE_320k;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
qLog(Debug) << "Setting playback settings: bitrate"
|
qLog(Debug) << "Setting playback settings: bitrate" << bitrate
|
||||||
<< bitrate << "normalisation" << req.volume_normalisation();
|
<< "normalisation" << req.volume_normalisation();
|
||||||
|
|
||||||
sp_session_preferred_bitrate(session_, bitrate);
|
sp_session_preferred_bitrate(session_, bitrate);
|
||||||
sp_session_preferred_offline_bitrate(session_, bitrate, false);
|
sp_session_preferred_offline_bitrate(session_, bitrate, false);
|
||||||
|
@ -310,7 +320,8 @@ void SpotifyClient::Login(const pb::spotify::LoginRequest& req) {
|
||||||
sp_error error = sp_session_create(&spotify_config_, &session_);
|
sp_error error = sp_session_create(&spotify_config_, &session_);
|
||||||
if (error != SP_ERROR_OK) {
|
if (error != SP_ERROR_OK) {
|
||||||
qLog(Warning) << "Failed to create session" << sp_error_message(error);
|
qLog(Warning) << "Failed to create session" << sp_error_message(error);
|
||||||
SendLoginCompleted(false, sp_error_message(error), pb::spotify::LoginResponse_Error_Other);
|
SendLoginCompleted(false, sp_error_message(error),
|
||||||
|
pb::spotify::LoginResponse_Error_Other);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -324,15 +335,14 @@ void SpotifyClient::Login(const pb::spotify::LoginRequest& req) {
|
||||||
pb::spotify::LoginResponse_Error_ReloginFailed);
|
pb::spotify::LoginResponse_Error_ReloginFailed);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
sp_session_login(session_,
|
sp_session_login(session_, req.username().c_str(), req.password().c_str(),
|
||||||
req.username().c_str(),
|
|
||||||
req.password().c_str(),
|
|
||||||
true, // Remember the password.
|
true, // Remember the password.
|
||||||
nullptr);
|
nullptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpotifyClient::SendLoginCompleted(bool success, const QString& error,
|
void SpotifyClient::SendLoginCompleted(
|
||||||
|
bool success, const QString& error,
|
||||||
pb::spotify::LoginResponse_Error error_code) {
|
pb::spotify::LoginResponse_Error error_code) {
|
||||||
pb::spotify::Message message;
|
pb::spotify::Message message;
|
||||||
|
|
||||||
|
@ -347,7 +357,8 @@ void SpotifyClient::SendLoginCompleted(bool success, const QString& error,
|
||||||
SendMessage(message);
|
SendMessage(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpotifyClient::PlaylistContainerLoadedCallback(sp_playlistcontainer* pc, void* userdata) {
|
void SpotifyClient::PlaylistContainerLoadedCallback(sp_playlistcontainer* pc,
|
||||||
|
void* userdata) {
|
||||||
SpotifyClient* me = reinterpret_cast<SpotifyClient*>(userdata);
|
SpotifyClient* me = reinterpret_cast<SpotifyClient*>(userdata);
|
||||||
|
|
||||||
// Install callbacks on all the playlists
|
// Install callbacks on all the playlists
|
||||||
|
@ -360,7 +371,9 @@ void SpotifyClient::PlaylistContainerLoadedCallback(sp_playlistcontainer* pc, vo
|
||||||
me->SendPlaylistList();
|
me->SendPlaylistList();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpotifyClient::PlaylistAddedCallback(sp_playlistcontainer* pc, sp_playlist* playlist, int position, void* userdata) {
|
void SpotifyClient::PlaylistAddedCallback(sp_playlistcontainer* pc,
|
||||||
|
sp_playlist* playlist, int position,
|
||||||
|
void* userdata) {
|
||||||
SpotifyClient* me = reinterpret_cast<SpotifyClient*>(userdata);
|
SpotifyClient* me = reinterpret_cast<SpotifyClient*>(userdata);
|
||||||
|
|
||||||
// Install callbacks on this playlist
|
// Install callbacks on this playlist
|
||||||
|
@ -369,12 +382,16 @@ void SpotifyClient::PlaylistAddedCallback(sp_playlistcontainer* pc, sp_playlist*
|
||||||
me->SendPlaylistList();
|
me->SendPlaylistList();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpotifyClient::PlaylistMovedCallback(sp_playlistcontainer* pc, sp_playlist* playlist, int position, int new_position, void* userdata) {
|
void SpotifyClient::PlaylistMovedCallback(sp_playlistcontainer* pc,
|
||||||
|
sp_playlist* playlist, int position,
|
||||||
|
int new_position, void* userdata) {
|
||||||
SpotifyClient* me = reinterpret_cast<SpotifyClient*>(userdata);
|
SpotifyClient* me = reinterpret_cast<SpotifyClient*>(userdata);
|
||||||
me->SendPlaylistList();
|
me->SendPlaylistList();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpotifyClient::PlaylistRemovedCallback(sp_playlistcontainer* pc, sp_playlist* playlist, int position, void* userdata) {
|
void SpotifyClient::PlaylistRemovedCallback(sp_playlistcontainer* pc,
|
||||||
|
sp_playlist* playlist, int position,
|
||||||
|
void* userdata) {
|
||||||
SpotifyClient* me = reinterpret_cast<SpotifyClient*>(userdata);
|
SpotifyClient* me = reinterpret_cast<SpotifyClient*>(userdata);
|
||||||
|
|
||||||
// Remove callbacks from this playlist
|
// Remove callbacks from this playlist
|
||||||
|
@ -400,7 +417,8 @@ void SpotifyClient::SendPlaylistList() {
|
||||||
sp_playlist* playlist = sp_playlistcontainer_playlist(container, i);
|
sp_playlist* playlist = sp_playlistcontainer_playlist(container, i);
|
||||||
const bool is_loaded = sp_playlist_is_loaded(playlist);
|
const bool is_loaded = sp_playlist_is_loaded(playlist);
|
||||||
|
|
||||||
qLog(Debug) << "Got playlist" << i << is_loaded << type << sp_playlist_name(playlist);
|
qLog(Debug) << "Got playlist" << i << is_loaded << type
|
||||||
|
<< sp_playlist_name(playlist);
|
||||||
|
|
||||||
if (!is_loaded) {
|
if (!is_loaded) {
|
||||||
qLog(Info) << "Playlist is not loaded yet, waiting...";
|
qLog(Info) << "Playlist is not loaded yet, waiting...";
|
||||||
|
@ -431,7 +449,8 @@ void SpotifyClient::SendPlaylistList() {
|
||||||
SendMessage(message);
|
SendMessage(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
sp_playlist* SpotifyClient::GetPlaylist(pb::spotify::PlaylistType type, int user_index) {
|
sp_playlist* SpotifyClient::GetPlaylist(pb::spotify::PlaylistType type,
|
||||||
|
int user_index) {
|
||||||
sp_playlist* playlist = nullptr;
|
sp_playlist* playlist = nullptr;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case pb::spotify::Inbox:
|
case pb::spotify::Inbox:
|
||||||
|
@ -446,7 +465,8 @@ sp_playlist* SpotifyClient::GetPlaylist(pb::spotify::PlaylistType type, int user
|
||||||
sp_playlistcontainer* pc = sp_session_playlistcontainer(session_);
|
sp_playlistcontainer* pc = sp_session_playlistcontainer(session_);
|
||||||
|
|
||||||
if (pc && user_index <= sp_playlistcontainer_num_playlists(pc)) {
|
if (pc && user_index <= sp_playlistcontainer_num_playlists(pc)) {
|
||||||
if (sp_playlistcontainer_playlist_type(pc, user_index) == SP_PLAYLIST_TYPE_PLAYLIST) {
|
if (sp_playlistcontainer_playlist_type(pc, user_index) ==
|
||||||
|
SP_PLAYLIST_TYPE_PLAYLIST) {
|
||||||
playlist = sp_playlistcontainer_playlist(pc, user_index);
|
playlist = sp_playlistcontainer_playlist(pc, user_index);
|
||||||
sp_playlist_add_ref(playlist);
|
sp_playlist_add_ref(playlist);
|
||||||
}
|
}
|
||||||
|
@ -469,26 +489,30 @@ void SpotifyClient::LoadPlaylist(const pb::spotify::LoadPlaylistRequest& req) {
|
||||||
qLog(Warning) << "Invalid playlist requested or not logged in";
|
qLog(Warning) << "Invalid playlist requested or not logged in";
|
||||||
|
|
||||||
pb::spotify::Message message;
|
pb::spotify::Message message;
|
||||||
pb::spotify::LoadPlaylistResponse* response = message.mutable_load_playlist_response();
|
pb::spotify::LoadPlaylistResponse* response =
|
||||||
|
message.mutable_load_playlist_response();
|
||||||
*response->mutable_request() = req;
|
*response->mutable_request() = req;
|
||||||
SendMessage(message);
|
SendMessage(message);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
sp_playlist_add_callbacks(pending_load.playlist_, &load_playlist_callbacks_, this);
|
sp_playlist_add_callbacks(pending_load.playlist_, &load_playlist_callbacks_,
|
||||||
|
this);
|
||||||
pending_load_playlists_ << pending_load;
|
pending_load_playlists_ << pending_load;
|
||||||
|
|
||||||
PlaylistStateChangedForLoadPlaylist(pending_load.playlist_, this);
|
PlaylistStateChangedForLoadPlaylist(pending_load.playlist_, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpotifyClient::SyncPlaylist(const pb::spotify::SyncPlaylistRequest& req) {
|
void SpotifyClient::SyncPlaylist(const pb::spotify::SyncPlaylistRequest& req) {
|
||||||
sp_playlist* playlist = GetPlaylist(req.request().type(), req.request().user_playlist_index());
|
sp_playlist* playlist =
|
||||||
|
GetPlaylist(req.request().type(), req.request().user_playlist_index());
|
||||||
|
|
||||||
// The playlist should already be loaded.
|
// The playlist should already be loaded.
|
||||||
sp_playlist_set_offline_mode(session_, playlist, req.offline_sync());
|
sp_playlist_set_offline_mode(session_, playlist, req.offline_sync());
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpotifyClient::PlaylistStateChangedForLoadPlaylist(sp_playlist* pl, void* userdata) {
|
void SpotifyClient::PlaylistStateChangedForLoadPlaylist(sp_playlist* pl,
|
||||||
|
void* userdata) {
|
||||||
SpotifyClient* me = reinterpret_cast<SpotifyClient*>(userdata);
|
SpotifyClient* me = reinterpret_cast<SpotifyClient*>(userdata);
|
||||||
|
|
||||||
// If the playlist isn't loaded yet we have to wait
|
// If the playlist isn't loaded yet we have to wait
|
||||||
|
@ -533,13 +557,13 @@ void SpotifyClient::PlaylistStateChangedForLoadPlaylist(sp_playlist* pl, void* u
|
||||||
|
|
||||||
// Everything is loaded so send the response protobuf and unref everything.
|
// Everything is loaded so send the response protobuf and unref everything.
|
||||||
pb::spotify::Message message;
|
pb::spotify::Message message;
|
||||||
pb::spotify::LoadPlaylistResponse* response = message.mutable_load_playlist_response();
|
pb::spotify::LoadPlaylistResponse* response =
|
||||||
|
message.mutable_load_playlist_response();
|
||||||
|
|
||||||
// For some reason, we receive the starred tracks in reverse order but not
|
// For some reason, we receive the starred tracks in reverse order but not
|
||||||
// other playlists.
|
// other playlists.
|
||||||
if (pending_load->request_.type() == pb::spotify::Starred) {
|
if (pending_load->request_.type() == pb::spotify::Starred) {
|
||||||
std::reverse(pending_load->tracks_.begin(),
|
std::reverse(pending_load->tracks_.begin(), pending_load->tracks_.end());
|
||||||
pending_load->tracks_.end());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
*response->mutable_request() = pending_load->request_;
|
*response->mutable_request() = pending_load->request_;
|
||||||
|
@ -557,7 +581,8 @@ void SpotifyClient::PlaylistStateChangedForLoadPlaylist(sp_playlist* pl, void* u
|
||||||
me->pending_load_playlists_.removeAt(pending_load_index);
|
me->pending_load_playlists_.removeAt(pending_load_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpotifyClient::PlaylistStateChangedForGetPlaylists(sp_playlist* pl, void* userdata) {
|
void SpotifyClient::PlaylistStateChangedForGetPlaylists(sp_playlist* pl,
|
||||||
|
void* userdata) {
|
||||||
SpotifyClient* me = reinterpret_cast<SpotifyClient*>(userdata);
|
SpotifyClient* me = reinterpret_cast<SpotifyClient*>(userdata);
|
||||||
|
|
||||||
me->SendPlaylistList();
|
me->SendPlaylistList();
|
||||||
|
@ -576,9 +601,8 @@ void SpotifyClient::ConvertTrack(sp_track* track, pb::spotify::Track* pb) {
|
||||||
pb->set_track(sp_track_index(track));
|
pb->set_track(sp_track_index(track));
|
||||||
|
|
||||||
// Album art
|
// Album art
|
||||||
const QByteArray art_id(
|
const QByteArray art_id(reinterpret_cast<const char*>(sp_album_cover(
|
||||||
reinterpret_cast<const char*>(
|
sp_track_album(track), SP_IMAGE_SIZE_LARGE)),
|
||||||
sp_album_cover(sp_track_album(track), SP_IMAGE_SIZE_LARGE)),
|
|
||||||
kSpotifyImageIDSize);
|
kSpotifyImageIDSize);
|
||||||
const QString art_id_b64 = QString::fromAscii(art_id.toBase64());
|
const QString art_id_b64 = QString::fromAscii(art_id.toBase64());
|
||||||
pb->set_album_art_id(DataCommaSizeFromQString(art_id_b64));
|
pb->set_album_art_id(DataCommaSizeFromQString(art_id_b64));
|
||||||
|
@ -627,25 +651,29 @@ void SpotifyClient::ConvertAlbum(sp_album* album, pb::spotify::Track* pb) {
|
||||||
pb->set_uri(uri);
|
pb->set_uri(uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpotifyClient::ConvertAlbumBrowse(sp_albumbrowse* browse, pb::spotify::Track* pb) {
|
void SpotifyClient::ConvertAlbumBrowse(sp_albumbrowse* browse,
|
||||||
|
pb::spotify::Track* pb) {
|
||||||
pb->set_track(sp_albumbrowse_num_tracks(browse));
|
pb->set_track(sp_albumbrowse_num_tracks(browse));
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpotifyClient::MetadataUpdatedCallback(sp_session* session) {
|
void SpotifyClient::MetadataUpdatedCallback(sp_session* session) {
|
||||||
SpotifyClient* me = reinterpret_cast<SpotifyClient*>(sp_session_userdata(session));
|
SpotifyClient* me =
|
||||||
|
reinterpret_cast<SpotifyClient*>(sp_session_userdata(session));
|
||||||
|
|
||||||
foreach(const PendingLoadPlaylist & load, me->pending_load_playlists_) {
|
foreach(const PendingLoadPlaylist & load, me->pending_load_playlists_) {
|
||||||
PlaylistStateChangedForLoadPlaylist(load.playlist_, me);
|
PlaylistStateChangedForLoadPlaylist(load.playlist_, me);
|
||||||
}
|
}
|
||||||
foreach (const PendingPlaybackRequest& playback, me->pending_playback_requests_) {
|
foreach(const PendingPlaybackRequest & playback,
|
||||||
|
me->pending_playback_requests_) {
|
||||||
me->TryPlaybackAgain(playback);
|
me->TryPlaybackAgain(playback);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int SpotifyClient::MusicDeliveryCallback(
|
int SpotifyClient::MusicDeliveryCallback(sp_session* session,
|
||||||
sp_session* session, const sp_audioformat* format,
|
const sp_audioformat* format,
|
||||||
const void* frames, int num_frames) {
|
const void* frames, int num_frames) {
|
||||||
SpotifyClient* me = reinterpret_cast<SpotifyClient*>(sp_session_userdata(session));
|
SpotifyClient* me =
|
||||||
|
reinterpret_cast<SpotifyClient*>(sp_session_userdata(session));
|
||||||
|
|
||||||
if (!me->media_pipeline_) {
|
if (!me->media_pipeline_) {
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -668,21 +696,23 @@ int SpotifyClient::MusicDeliveryCallback(
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
me->media_pipeline_->WriteData(
|
me->media_pipeline_->WriteData(reinterpret_cast<const char*>(frames),
|
||||||
reinterpret_cast<const char*>(frames),
|
|
||||||
num_frames * format->channels * 2);
|
num_frames * format->channels * 2);
|
||||||
|
|
||||||
return num_frames;
|
return num_frames;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpotifyClient::EndOfTrackCallback(sp_session* session) {
|
void SpotifyClient::EndOfTrackCallback(sp_session* session) {
|
||||||
SpotifyClient* me = reinterpret_cast<SpotifyClient*>(sp_session_userdata(session));
|
SpotifyClient* me =
|
||||||
|
reinterpret_cast<SpotifyClient*>(sp_session_userdata(session));
|
||||||
|
|
||||||
me->media_pipeline_.reset();
|
me->media_pipeline_.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpotifyClient::StreamingErrorCallback(sp_session* session, sp_error error) {
|
void SpotifyClient::StreamingErrorCallback(sp_session* session,
|
||||||
SpotifyClient* me = reinterpret_cast<SpotifyClient*>(sp_session_userdata(session));
|
sp_error error) {
|
||||||
|
SpotifyClient* me =
|
||||||
|
reinterpret_cast<SpotifyClient*>(sp_session_userdata(session));
|
||||||
|
|
||||||
me->media_pipeline_.reset();
|
me->media_pipeline_.reset();
|
||||||
|
|
||||||
|
@ -690,11 +720,13 @@ void SpotifyClient::StreamingErrorCallback(sp_session* session, sp_error error)
|
||||||
me->SendPlaybackError(QString::fromUtf8(sp_error_message(error)));
|
me->SendPlaybackError(QString::fromUtf8(sp_error_message(error)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpotifyClient::ConnectionErrorCallback(sp_session* session, sp_error error) {
|
void SpotifyClient::ConnectionErrorCallback(sp_session* session,
|
||||||
|
sp_error error) {
|
||||||
qLog(Debug) << Q_FUNC_INFO << sp_error_message(error);
|
qLog(Debug) << Q_FUNC_INFO << sp_error_message(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpotifyClient::UserMessageCallback(sp_session* session, const char* message) {
|
void SpotifyClient::UserMessageCallback(sp_session* session,
|
||||||
|
const char* message) {
|
||||||
qLog(Debug) << Q_FUNC_INFO << message;
|
qLog(Debug) << Q_FUNC_INFO << message;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -707,7 +739,8 @@ void SpotifyClient::StopPlaybackCallback(sp_session* session) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpotifyClient::OfflineStatusUpdatedCallback(sp_session* session) {
|
void SpotifyClient::OfflineStatusUpdatedCallback(sp_session* session) {
|
||||||
SpotifyClient* me = reinterpret_cast<SpotifyClient*>(sp_session_userdata(session));
|
SpotifyClient* me =
|
||||||
|
reinterpret_cast<SpotifyClient*>(sp_session_userdata(session));
|
||||||
sp_playlistcontainer* container = sp_session_playlistcontainer(session);
|
sp_playlistcontainer* container = sp_session_playlistcontainer(session);
|
||||||
if (!container) {
|
if (!container) {
|
||||||
qLog(Warning) << "sp_session_playlistcontainer returned nullptr";
|
qLog(Warning) << "sp_session_playlistcontainer returned nullptr";
|
||||||
|
@ -717,7 +750,8 @@ void SpotifyClient::OfflineStatusUpdatedCallback(sp_session* session) {
|
||||||
const int count = sp_playlistcontainer_num_playlists(container);
|
const int count = sp_playlistcontainer_num_playlists(container);
|
||||||
|
|
||||||
for (int i = 0; i < count; ++i) {
|
for (int i = 0; i < count; ++i) {
|
||||||
const sp_playlist_type type = sp_playlistcontainer_playlist_type(container, i);
|
const sp_playlist_type type =
|
||||||
|
sp_playlistcontainer_playlist_type(container, i);
|
||||||
sp_playlist* playlist = sp_playlistcontainer_playlist(container, i);
|
sp_playlist* playlist = sp_playlistcontainer_playlist(container, i);
|
||||||
|
|
||||||
if (type != SP_PLAYLIST_TYPE_PLAYLIST) {
|
if (type != SP_PLAYLIST_TYPE_PLAYLIST) {
|
||||||
|
@ -748,10 +782,11 @@ void SpotifyClient::OfflineStatusUpdatedCallback(sp_session* session) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpotifyClient::SendDownloadProgress(
|
void SpotifyClient::SendDownloadProgress(pb::spotify::PlaylistType type,
|
||||||
pb::spotify::PlaylistType type, int index, int download_progress) {
|
int index, int download_progress) {
|
||||||
pb::spotify::Message message;
|
pb::spotify::Message message;
|
||||||
pb::spotify::SyncPlaylistProgress* progress = message.mutable_sync_playlist_progress();
|
pb::spotify::SyncPlaylistProgress* progress =
|
||||||
|
message.mutable_sync_playlist_progress();
|
||||||
progress->mutable_request()->set_type(type);
|
progress->mutable_request()->set_type(type);
|
||||||
if (index != -1) {
|
if (index != -1) {
|
||||||
progress->mutable_request()->set_user_playlist_index(index);
|
progress->mutable_request()->set_user_playlist_index(index);
|
||||||
|
@ -864,8 +899,8 @@ void SpotifyClient::LoadImage(const QString& id_b64) {
|
||||||
PendingImageRequest pending_load;
|
PendingImageRequest pending_load;
|
||||||
pending_load.id_ = id;
|
pending_load.id_ = id;
|
||||||
pending_load.id_b64_ = id_b64;
|
pending_load.id_b64_ = id_b64;
|
||||||
pending_load.image_ = sp_image_create(session_,
|
pending_load.image_ =
|
||||||
reinterpret_cast<const byte*>(id.constData()));
|
sp_image_create(session_, reinterpret_cast<const byte*>(id.constData()));
|
||||||
pending_image_requests_ << pending_load;
|
pending_image_requests_ << pending_load;
|
||||||
|
|
||||||
if (!image_callbacks_registered_[pending_load.image_]) {
|
if (!image_callbacks_registered_[pending_load.image_]) {
|
||||||
|
@ -948,16 +983,17 @@ void SpotifyClient::BrowseAlbum(const QString& uri) {
|
||||||
pending_album_browses_[browse] = uri;
|
pending_album_browses_[browse] = uri;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpotifyClient::AlbumBrowseComplete(sp_albumbrowse* result, void* userdata) {
|
void SpotifyClient::AlbumBrowseComplete(sp_albumbrowse* result,
|
||||||
|
void* userdata) {
|
||||||
SpotifyClient* me = reinterpret_cast<SpotifyClient*>(userdata);
|
SpotifyClient* me = reinterpret_cast<SpotifyClient*>(userdata);
|
||||||
|
|
||||||
if (!me->pending_album_browses_.contains(result))
|
if (!me->pending_album_browses_.contains(result)) return;
|
||||||
return;
|
|
||||||
|
|
||||||
QString uri = me->pending_album_browses_.take(result);
|
QString uri = me->pending_album_browses_.take(result);
|
||||||
|
|
||||||
pb::spotify::Message message;
|
pb::spotify::Message message;
|
||||||
pb::spotify::BrowseAlbumResponse* msg = message.mutable_browse_album_response();
|
pb::spotify::BrowseAlbumResponse* msg =
|
||||||
|
message.mutable_browse_album_response();
|
||||||
|
|
||||||
msg->set_uri(DataCommaSizeFromQString(uri));
|
msg->set_uri(DataCommaSizeFromQString(uri));
|
||||||
|
|
||||||
|
@ -970,32 +1006,32 @@ void SpotifyClient::AlbumBrowseComplete(sp_albumbrowse* result, void* userdata)
|
||||||
sp_albumbrowse_release(result);
|
sp_albumbrowse_release(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpotifyClient::BrowseToplist(const pb::spotify::BrowseToplistRequest& req) {
|
void SpotifyClient::BrowseToplist(
|
||||||
|
const pb::spotify::BrowseToplistRequest& req) {
|
||||||
sp_toplistbrowse* browse = sp_toplistbrowse_create(
|
sp_toplistbrowse* browse = sp_toplistbrowse_create(
|
||||||
session_,
|
session_, SP_TOPLIST_TYPE_TRACKS, // TODO: Support albums and artists.
|
||||||
SP_TOPLIST_TYPE_TRACKS, // TODO: Support albums and artists.
|
|
||||||
SP_TOPLIST_REGION_EVERYWHERE, // TODO: Support other regions.
|
SP_TOPLIST_REGION_EVERYWHERE, // TODO: Support other regions.
|
||||||
nullptr,
|
nullptr, &ToplistBrowseComplete, this);
|
||||||
&ToplistBrowseComplete,
|
|
||||||
this);
|
|
||||||
pending_toplist_browses_[browse] = req;
|
pending_toplist_browses_[browse] = req;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpotifyClient::ToplistBrowseComplete(sp_toplistbrowse* result, void* userdata) {
|
void SpotifyClient::ToplistBrowseComplete(sp_toplistbrowse* result,
|
||||||
|
void* userdata) {
|
||||||
SpotifyClient* me = reinterpret_cast<SpotifyClient*>(userdata);
|
SpotifyClient* me = reinterpret_cast<SpotifyClient*>(userdata);
|
||||||
|
|
||||||
qLog(Debug) << "Toplist browse request took:"
|
qLog(Debug) << "Toplist browse request took:"
|
||||||
<< sp_toplistbrowse_backend_request_duration(result)
|
<< sp_toplistbrowse_backend_request_duration(result) << "ms";
|
||||||
<< "ms";
|
|
||||||
|
|
||||||
if (!me->pending_toplist_browses_.contains(result)) {
|
if (!me->pending_toplist_browses_.contains(result)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const pb::spotify::BrowseToplistRequest& request = me->pending_toplist_browses_.take(result);
|
const pb::spotify::BrowseToplistRequest& request =
|
||||||
|
me->pending_toplist_browses_.take(result);
|
||||||
|
|
||||||
pb::spotify::Message message;
|
pb::spotify::Message message;
|
||||||
pb::spotify::BrowseToplistResponse* msg = message.mutable_browse_toplist_response();
|
pb::spotify::BrowseToplistResponse* msg =
|
||||||
|
message.mutable_browse_toplist_response();
|
||||||
msg->mutable_request()->CopyFrom(request);
|
msg->mutable_request()->CopyFrom(request);
|
||||||
|
|
||||||
const int count = sp_toplistbrowse_num_tracks(result);
|
const int count = sp_toplistbrowse_num_tracks(result);
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
// it is used by the Spotify blob which links against libspotify and is not GPL
|
// it is used by the Spotify blob which links against libspotify and is not GPL
|
||||||
// compatible.
|
// compatible.
|
||||||
|
|
||||||
|
|
||||||
#ifndef SPOTIFYCLIENT_H
|
#ifndef SPOTIFYCLIENT_H
|
||||||
#define SPOTIFYCLIENT_H
|
#define SPOTIFYCLIENT_H
|
||||||
|
|
||||||
|
@ -64,49 +63,59 @@ private:
|
||||||
// Spotify session callbacks.
|
// Spotify session callbacks.
|
||||||
static void SP_CALLCONV LoggedInCallback(sp_session* session, sp_error error);
|
static void SP_CALLCONV LoggedInCallback(sp_session* session, sp_error error);
|
||||||
static void SP_CALLCONV NotifyMainThreadCallback(sp_session* session);
|
static void SP_CALLCONV NotifyMainThreadCallback(sp_session* session);
|
||||||
static void SP_CALLCONV LogMessageCallback(sp_session* session, const char* data);
|
static void SP_CALLCONV
|
||||||
static void SP_CALLCONV SearchCompleteCallback(sp_search* result, void* userdata);
|
LogMessageCallback(sp_session* session, const char* data);
|
||||||
|
static void SP_CALLCONV
|
||||||
|
SearchCompleteCallback(sp_search* result, void* userdata);
|
||||||
static void SP_CALLCONV MetadataUpdatedCallback(sp_session* session);
|
static void SP_CALLCONV MetadataUpdatedCallback(sp_session* session);
|
||||||
static int SP_CALLCONV MusicDeliveryCallback(
|
static int SP_CALLCONV
|
||||||
sp_session* session, const sp_audioformat* format,
|
MusicDeliveryCallback(sp_session* session, const sp_audioformat* format,
|
||||||
const void* frames, int num_frames);
|
const void* frames, int num_frames);
|
||||||
static void SP_CALLCONV EndOfTrackCallback(sp_session* session);
|
static void SP_CALLCONV EndOfTrackCallback(sp_session* session);
|
||||||
static void SP_CALLCONV StreamingErrorCallback(sp_session* session, sp_error error);
|
static void SP_CALLCONV
|
||||||
|
StreamingErrorCallback(sp_session* session, sp_error error);
|
||||||
static void SP_CALLCONV OfflineStatusUpdatedCallback(sp_session* session);
|
static void SP_CALLCONV OfflineStatusUpdatedCallback(sp_session* session);
|
||||||
static void SP_CALLCONV ConnectionErrorCallback(sp_session* session, sp_error error);
|
static void SP_CALLCONV
|
||||||
static void SP_CALLCONV UserMessageCallback(sp_session* session, const char* message);
|
ConnectionErrorCallback(sp_session* session, sp_error error);
|
||||||
|
static void SP_CALLCONV
|
||||||
|
UserMessageCallback(sp_session* session, const char* message);
|
||||||
static void SP_CALLCONV StartPlaybackCallback(sp_session* session);
|
static void SP_CALLCONV StartPlaybackCallback(sp_session* session);
|
||||||
static void SP_CALLCONV StopPlaybackCallback(sp_session* session);
|
static void SP_CALLCONV StopPlaybackCallback(sp_session* session);
|
||||||
|
|
||||||
// Spotify playlist container callbacks.
|
// Spotify playlist container callbacks.
|
||||||
static void SP_CALLCONV PlaylistAddedCallback(
|
static void SP_CALLCONV PlaylistAddedCallback(sp_playlistcontainer* pc,
|
||||||
sp_playlistcontainer* pc, sp_playlist* playlist,
|
sp_playlist* playlist,
|
||||||
int position, void* userdata);
|
int position, void* userdata);
|
||||||
static void SP_CALLCONV PlaylistRemovedCallback(
|
static void SP_CALLCONV PlaylistRemovedCallback(sp_playlistcontainer* pc,
|
||||||
sp_playlistcontainer* pc, sp_playlist* playlist,
|
sp_playlist* playlist,
|
||||||
int position, void* userdata);
|
int position, void* userdata);
|
||||||
static void SP_CALLCONV PlaylistMovedCallback(
|
static void SP_CALLCONV
|
||||||
sp_playlistcontainer* pc, sp_playlist* playlist,
|
PlaylistMovedCallback(sp_playlistcontainer* pc, sp_playlist* playlist,
|
||||||
int position, int new_position, void* userdata);
|
int position, int new_position, void* userdata);
|
||||||
static void SP_CALLCONV PlaylistContainerLoadedCallback(
|
static void SP_CALLCONV
|
||||||
sp_playlistcontainer* pc, void* userdata);
|
PlaylistContainerLoadedCallback(sp_playlistcontainer* pc, void* userdata);
|
||||||
|
|
||||||
// Spotify playlist callbacks - when loading the list of playlists
|
// Spotify playlist callbacks - when loading the list of playlists
|
||||||
// initially
|
// initially
|
||||||
static void SP_CALLCONV PlaylistStateChangedForGetPlaylists(sp_playlist* pl, void* userdata);
|
static void SP_CALLCONV
|
||||||
|
PlaylistStateChangedForGetPlaylists(sp_playlist* pl, void* userdata);
|
||||||
|
|
||||||
// Spotify playlist callbacks - when loading a playlist
|
// Spotify playlist callbacks - when loading a playlist
|
||||||
static void SP_CALLCONV PlaylistStateChangedForLoadPlaylist(sp_playlist* pl, void* userdata);
|
static void SP_CALLCONV
|
||||||
|
PlaylistStateChangedForLoadPlaylist(sp_playlist* pl, void* userdata);
|
||||||
|
|
||||||
// Spotify image callbacks.
|
// Spotify image callbacks.
|
||||||
static void SP_CALLCONV ImageLoaded(sp_image* image, void* userdata);
|
static void SP_CALLCONV ImageLoaded(sp_image* image, void* userdata);
|
||||||
|
|
||||||
// Spotify album browse callbacks.
|
// Spotify album browse callbacks.
|
||||||
static void SP_CALLCONV SearchAlbumBrowseComplete(sp_albumbrowse* result, void* userdata);
|
static void SP_CALLCONV
|
||||||
static void SP_CALLCONV AlbumBrowseComplete(sp_albumbrowse* result, void* userdata);
|
SearchAlbumBrowseComplete(sp_albumbrowse* result, void* userdata);
|
||||||
|
static void SP_CALLCONV
|
||||||
|
AlbumBrowseComplete(sp_albumbrowse* result, void* userdata);
|
||||||
|
|
||||||
// Spotify toplist browse callbacks.
|
// Spotify toplist browse callbacks.
|
||||||
static void SP_CALLCONV ToplistBrowseComplete(sp_toplistbrowse* result, void* userdata);
|
static void SP_CALLCONV
|
||||||
|
ToplistBrowseComplete(sp_toplistbrowse* result, void* userdata);
|
||||||
|
|
||||||
// Request handlers.
|
// Request handlers.
|
||||||
void Login(const pb::spotify::LoginRequest& req);
|
void Login(const pb::spotify::LoginRequest& req);
|
||||||
|
@ -157,7 +166,8 @@ private:
|
||||||
void TryPlaybackAgain(const PendingPlaybackRequest& req);
|
void TryPlaybackAgain(const PendingPlaybackRequest& req);
|
||||||
void TryImageAgain(sp_image* image);
|
void TryImageAgain(sp_image* image);
|
||||||
int GetDownloadProgress(sp_playlist* playlist);
|
int GetDownloadProgress(sp_playlist* playlist);
|
||||||
void SendDownloadProgress(pb::spotify::PlaylistType type, int index, int download_progress);
|
void SendDownloadProgress(pb::spotify::PlaylistType type, int index,
|
||||||
|
int download_progress);
|
||||||
|
|
||||||
QByteArray api_key_;
|
QByteArray api_key_;
|
||||||
|
|
||||||
|
@ -178,7 +188,8 @@ private:
|
||||||
QMap<sp_image*, int> image_callbacks_registered_;
|
QMap<sp_image*, int> image_callbacks_registered_;
|
||||||
QMap<sp_search*, pb::spotify::SearchRequest> pending_searches_;
|
QMap<sp_search*, pb::spotify::SearchRequest> pending_searches_;
|
||||||
QMap<sp_albumbrowse*, QString> pending_album_browses_;
|
QMap<sp_albumbrowse*, QString> pending_album_browses_;
|
||||||
QMap<sp_toplistbrowse*, pb::spotify::BrowseToplistRequest> pending_toplist_browses_;
|
QMap<sp_toplistbrowse*, pb::spotify::BrowseToplistRequest>
|
||||||
|
pending_toplist_browses_;
|
||||||
|
|
||||||
QMap<sp_search*, QList<sp_albumbrowse*> > pending_search_album_browses_;
|
QMap<sp_search*, QList<sp_albumbrowse*> > pending_search_album_browses_;
|
||||||
QMap<sp_albumbrowse*, sp_search*> pending_search_album_browse_responses_;
|
QMap<sp_albumbrowse*, sp_search*> pending_search_album_browse_responses_;
|
||||||
|
|
|
@ -18,16 +18,20 @@
|
||||||
// it is used by the Spotify blob which links against libspotify and is not GPL
|
// it is used by the Spotify blob which links against libspotify and is not GPL
|
||||||
// compatible.
|
// compatible.
|
||||||
|
|
||||||
|
|
||||||
// The Spotify terms of service require that application keys are not
|
// The Spotify terms of service require that application keys are not
|
||||||
// accessible to third parties. Therefore this application key is heavily
|
// accessible to third parties. Therefore this application key is heavily
|
||||||
// encrypted here in the source to prevent third parties from viewing it.
|
// encrypted here in the source to prevent third parties from viewing it.
|
||||||
// It is most definitely not base64 encoded.
|
// It is most definitely not base64 encoded.
|
||||||
|
|
||||||
static const char* kSpotifyApiKey =
|
static const char* kSpotifyApiKey =
|
||||||
"AVlOrvJkKx8T+LEsCk+Kyl24I0MSsjohZAtMFzm2O5Lms1bmAWFWgdZaHkpypzSJPmSd+Wi50wwg"
|
"AVlOrvJkKx8T+LEsCk+Kyl24I0MSsjohZAtMFzm2O5Lms1bmAWFWgdZaHkpypzSJPmSd+"
|
||||||
"JwVCU0sq4Lep1zB4t6Z8h26NK6+z8gmkHVkV9DRPkRgebcUkWTDTflwVPKWF4+gdRjUwprsqBw6O"
|
"Wi50wwg"
|
||||||
"iofRLJzeKaxbmaUGqkSkxVLOiXC9lxylNq6ju7Q7uY8u8XkDUsVM3YIxiWy2+EM7I/lhatzT9xrq"
|
"JwVCU0sq4Lep1zB4t6Z8h26NK6+z8gmkHVkV9DRPkRgebcUkWTDTflwVPKWF4+"
|
||||||
"rxHe2lg7CzOwF5kuFdwgmi8MQ72xTYXIKnNlOry/hJDlN9lKxkbUBLh+pzbYvO92S2fYKK5PAHvX"
|
"gdRjUwprsqBw6O"
|
||||||
"5+SmSBGbh6dlpHeCGqb8MPdaeZ5I1YxMcDkxa2+tbLA/Muat7gKA9u57TFCtYjun/u/i/ONwdBIQ"
|
"iofRLJzeKaxbmaUGqkSkxVLOiXC9lxylNq6ju7Q7uY8u8XkDUsVM3YIxiWy2+EM7I/"
|
||||||
|
"lhatzT9xrq"
|
||||||
|
"rxHe2lg7CzOwF5kuFdwgmi8MQ72xTYXIKnNlOry/"
|
||||||
|
"hJDlN9lKxkbUBLh+pzbYvO92S2fYKK5PAHvX"
|
||||||
|
"5+SmSBGbh6dlpHeCGqb8MPdaeZ5I1YxMcDkxa2+tbLA/Muat7gKA9u57TFCtYjun/u/i/"
|
||||||
|
"ONwdBIQ"
|
||||||
"rePzXZjipO32kYmQAiCkN1p8sgQEcF43QxaVwXGo2X0rRnJf";
|
"rePzXZjipO32kYmQAiCkN1p8sgQEcF43QxaVwXGo2X0rRnJf";
|
||||||
|
|
|
@ -31,8 +31,10 @@ int main(int argc, char** argv) {
|
||||||
QStringList args(a.arguments());
|
QStringList args(a.arguments());
|
||||||
|
|
||||||
if (args.count() != 2) {
|
if (args.count() != 2) {
|
||||||
std::cerr << "This program is used internally by Clementine to parse tags in music files\n"
|
std::cerr << "This program is used internally by Clementine to parse tags "
|
||||||
"without exposing the whole application to crashes caused by malformed\n"
|
"in music files\n"
|
||||||
|
"without exposing the whole application to crashes caused by "
|
||||||
|
"malformed\n"
|
||||||
"files. It is not meant to be run on its own.\n";
|
"files. It is not meant to be run on its own.\n";
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,11 +24,8 @@
|
||||||
#include <QTextCodec>
|
#include <QTextCodec>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
|
|
||||||
|
|
||||||
TagReaderWorker::TagReaderWorker(QIODevice* socket, QObject* parent)
|
TagReaderWorker::TagReaderWorker(QIODevice* socket, QObject* parent)
|
||||||
: AbstractMessageHandler<pb::tagreader::Message>(socket, parent)
|
: AbstractMessageHandler<pb::tagreader::Message>(socket, parent) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void TagReaderWorker::MessageArrived(const pb::tagreader::Message& message) {
|
void TagReaderWorker::MessageArrived(const pb::tagreader::Message& message) {
|
||||||
pb::tagreader::Message reply;
|
pb::tagreader::Message reply;
|
||||||
|
@ -42,30 +39,33 @@ void TagReaderWorker::MessageArrived(const pb::tagreader::Message& message) {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (message.has_read_file_request()) {
|
if (message.has_read_file_request()) {
|
||||||
tag_reader_.ReadFile(QStringFromStdString(message.read_file_request().filename()),
|
tag_reader_.ReadFile(
|
||||||
|
QStringFromStdString(message.read_file_request().filename()),
|
||||||
reply.mutable_read_file_response()->mutable_metadata());
|
reply.mutable_read_file_response()->mutable_metadata());
|
||||||
} else if (message.has_save_file_request()) {
|
} else if (message.has_save_file_request()) {
|
||||||
reply.mutable_save_file_response()->set_success(
|
reply.mutable_save_file_response()->set_success(tag_reader_.SaveFile(
|
||||||
tag_reader_.SaveFile(QStringFromStdString(message.save_file_request().filename()),
|
QStringFromStdString(message.save_file_request().filename()),
|
||||||
message.save_file_request().metadata()));
|
message.save_file_request().metadata()));
|
||||||
} else if (message.has_save_song_statistics_to_file_request()) {
|
} else if (message.has_save_song_statistics_to_file_request()) {
|
||||||
reply.mutable_save_song_statistics_to_file_response()->set_success(
|
reply.mutable_save_song_statistics_to_file_response()->set_success(
|
||||||
tag_reader_.SaveSongStatisticsToFile(
|
tag_reader_.SaveSongStatisticsToFile(
|
||||||
QStringFromStdString(message.save_song_statistics_to_file_request().filename()),
|
QStringFromStdString(
|
||||||
|
message.save_song_statistics_to_file_request().filename()),
|
||||||
message.save_song_statistics_to_file_request().metadata()));
|
message.save_song_statistics_to_file_request().metadata()));
|
||||||
} else if (message.has_save_song_rating_to_file_request()) {
|
} else if (message.has_save_song_rating_to_file_request()) {
|
||||||
reply.mutable_save_song_rating_to_file_response()->set_success(
|
reply.mutable_save_song_rating_to_file_response()->set_success(
|
||||||
tag_reader_.SaveSongRatingToFile(
|
tag_reader_.SaveSongRatingToFile(
|
||||||
QStringFromStdString(message.save_song_rating_to_file_request().filename()),
|
QStringFromStdString(
|
||||||
|
message.save_song_rating_to_file_request().filename()),
|
||||||
message.save_song_rating_to_file_request().metadata()));
|
message.save_song_rating_to_file_request().metadata()));
|
||||||
} else if (message.has_is_media_file_request()) {
|
} else if (message.has_is_media_file_request()) {
|
||||||
reply.mutable_is_media_file_response()->set_success(
|
reply.mutable_is_media_file_response()->set_success(tag_reader_.IsMediaFile(
|
||||||
tag_reader_.IsMediaFile(QStringFromStdString(message.is_media_file_request().filename())));
|
QStringFromStdString(message.is_media_file_request().filename())));
|
||||||
} else if (message.has_load_embedded_art_request()) {
|
} else if (message.has_load_embedded_art_request()) {
|
||||||
QByteArray data = tag_reader_.LoadEmbeddedArt(
|
QByteArray data = tag_reader_.LoadEmbeddedArt(
|
||||||
QStringFromStdString(message.load_embedded_art_request().filename()));
|
QStringFromStdString(message.load_embedded_art_request().filename()));
|
||||||
reply.mutable_load_embedded_art_response()->set_data(
|
reply.mutable_load_embedded_art_response()->set_data(data.constData(),
|
||||||
data.constData(), data.size());
|
data.size());
|
||||||
} else if (message.has_read_cloud_file_request()) {
|
} else if (message.has_read_cloud_file_request()) {
|
||||||
#ifdef HAVE_GOOGLE_DRIVE
|
#ifdef HAVE_GOOGLE_DRIVE
|
||||||
const pb::tagreader::ReadCloudFileRequest& req =
|
const pb::tagreader::ReadCloudFileRequest& req =
|
||||||
|
@ -73,8 +73,7 @@ void TagReaderWorker::MessageArrived(const pb::tagreader::Message& message) {
|
||||||
if (!tag_reader_.ReadCloudFile(
|
if (!tag_reader_.ReadCloudFile(
|
||||||
QUrl::fromEncoded(QByteArray(req.download_url().data(),
|
QUrl::fromEncoded(QByteArray(req.download_url().data(),
|
||||||
req.download_url().size())),
|
req.download_url().size())),
|
||||||
QStringFromStdString(req.title()),
|
QStringFromStdString(req.title()), req.size(),
|
||||||
req.size(),
|
|
||||||
QStringFromStdString(req.mime_type()),
|
QStringFromStdString(req.mime_type()),
|
||||||
QStringFromStdString(req.authorisation_header()),
|
QStringFromStdString(req.authorisation_header()),
|
||||||
reply.mutable_read_cloud_file_response()->mutable_metadata())) {
|
reply.mutable_read_cloud_file_response()->mutable_metadata())) {
|
||||||
|
@ -86,10 +85,8 @@ void TagReaderWorker::MessageArrived(const pb::tagreader::Message& message) {
|
||||||
SendReply(message, &reply);
|
SendReply(message, &reply);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void TagReaderWorker::DeviceClosed() {
|
void TagReaderWorker::DeviceClosed() {
|
||||||
AbstractMessageHandler<pb::tagreader::Message>::DeviceClosed();
|
AbstractMessageHandler<pb::tagreader::Message>::DeviceClosed();
|
||||||
|
|
||||||
qApp->exit();
|
qApp->exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,32 +23,20 @@
|
||||||
|
|
||||||
namespace _detail {
|
namespace _detail {
|
||||||
|
|
||||||
ClosureBase::ClosureBase(ObjectHelper* helper)
|
ClosureBase::ClosureBase(ObjectHelper* helper) : helper_(helper) {}
|
||||||
: helper_(helper) {
|
|
||||||
}
|
|
||||||
|
|
||||||
ClosureBase::~ClosureBase() {
|
ClosureBase::~ClosureBase() {}
|
||||||
}
|
|
||||||
|
|
||||||
CallbackClosure::CallbackClosure(
|
CallbackClosure::CallbackClosure(QObject* sender, const char* signal,
|
||||||
QObject* sender,
|
|
||||||
const char* signal,
|
|
||||||
std::function<void()> callback)
|
std::function<void()> callback)
|
||||||
: ClosureBase(new ObjectHelper(sender, signal, this)),
|
: ClosureBase(new ObjectHelper(sender, signal, this)),
|
||||||
callback_(callback) {
|
callback_(callback) {}
|
||||||
}
|
|
||||||
|
|
||||||
void CallbackClosure::Invoke() {
|
void CallbackClosure::Invoke() { callback_(); }
|
||||||
callback_();
|
|
||||||
}
|
|
||||||
|
|
||||||
ObjectHelper* ClosureBase::helper() const {
|
ObjectHelper* ClosureBase::helper() const { return helper_; }
|
||||||
return helper_;
|
|
||||||
}
|
|
||||||
|
|
||||||
ObjectHelper::ObjectHelper(
|
ObjectHelper::ObjectHelper(QObject* sender, const char* signal,
|
||||||
QObject* sender,
|
|
||||||
const char* signal,
|
|
||||||
ClosureBase* closure)
|
ClosureBase* closure)
|
||||||
: closure_(closure) {
|
: closure_(closure) {
|
||||||
connect(sender, signal, SLOT(Invoked()));
|
connect(sender, signal, SLOT(Invoked()));
|
||||||
|
@ -64,12 +52,9 @@ void Unpack(QList<QGenericArgument>*) {}
|
||||||
|
|
||||||
} // namespace _detail
|
} // namespace _detail
|
||||||
|
|
||||||
_detail::ClosureBase* NewClosure(
|
_detail::ClosureBase* NewClosure(QObject* sender, const char* signal,
|
||||||
QObject* sender,
|
|
||||||
const char* signal,
|
|
||||||
std::function<void()> callback) {
|
std::function<void()> callback) {
|
||||||
return new _detail::CallbackClosure(
|
return new _detail::CallbackClosure(sender, signal, callback);
|
||||||
sender, signal, callback);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DoAfter(QObject* receiver, const char* slot, int msec) {
|
void DoAfter(QObject* receiver, const char* slot, int msec) {
|
||||||
|
|
|
@ -52,10 +52,7 @@ class ClosureBase {
|
||||||
class ObjectHelper : public QObject {
|
class ObjectHelper : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
ObjectHelper(
|
ObjectHelper(QObject* parent, const char* signal, ClosureBase* closure);
|
||||||
QObject* parent,
|
|
||||||
const char* signal,
|
|
||||||
ClosureBase* closure);
|
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void Invoked();
|
void Invoked();
|
||||||
|
@ -76,7 +73,8 @@ void Unpack(QList<QGenericArgument>* list, const Arg& arg) {
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Head, typename... Tail>
|
template <typename Head, typename... Tail>
|
||||||
void Unpack(QList<QGenericArgument>* list, const Head& head, const Tail&... tail) {
|
void Unpack(QList<QGenericArgument>* list, const Head& head,
|
||||||
|
const Tail&... tail) {
|
||||||
Unpack(list, head);
|
Unpack(list, head);
|
||||||
Unpack(list, tail...);
|
Unpack(list, tail...);
|
||||||
}
|
}
|
||||||
|
@ -84,12 +82,8 @@ void Unpack(QList<QGenericArgument>* list, const Head& head, const Tail&... tail
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
class Closure : public ClosureBase {
|
class Closure : public ClosureBase {
|
||||||
public:
|
public:
|
||||||
Closure(
|
Closure(QObject* sender, const char* signal, QObject* receiver,
|
||||||
QObject* sender,
|
const char* slot, const Args&... args)
|
||||||
const char* signal,
|
|
||||||
QObject* receiver,
|
|
||||||
const char* slot,
|
|
||||||
const Args&... args)
|
|
||||||
: ClosureBase(new ObjectHelper(sender, signal, this)),
|
: ClosureBase(new ObjectHelper(sender, signal, this)),
|
||||||
// std::bind is the easiest way to store an argument list.
|
// std::bind is the easiest way to store an argument list.
|
||||||
function_(std::bind(&Closure<Args...>::Call, this, args...)),
|
function_(std::bind(&Closure<Args...>::Call, this, args...)),
|
||||||
|
@ -99,20 +93,18 @@ class Closure : public ClosureBase {
|
||||||
const int index = meta_receiver->indexOfSlot(normalised_slot.constData());
|
const int index = meta_receiver->indexOfSlot(normalised_slot.constData());
|
||||||
Q_ASSERT(index != -1);
|
Q_ASSERT(index != -1);
|
||||||
slot_ = meta_receiver->method(index);
|
slot_ = meta_receiver->method(index);
|
||||||
QObject::connect(receiver_, SIGNAL(destroyed()), helper_, SLOT(deleteLater()));
|
QObject::connect(receiver_, SIGNAL(destroyed()), helper_,
|
||||||
|
SLOT(deleteLater()));
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void Invoke() {
|
virtual void Invoke() { function_(); }
|
||||||
function_();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void Call(const Args&... args) {
|
void Call(const Args&... args) {
|
||||||
QList<QGenericArgument> arg_list;
|
QList<QGenericArgument> arg_list;
|
||||||
Unpack(&arg_list, args...);
|
Unpack(&arg_list, args...);
|
||||||
|
|
||||||
slot_.invoke(
|
slot_.invoke(receiver_,
|
||||||
receiver_,
|
|
||||||
arg_list.size() > 0 ? arg_list[0] : QGenericArgument(),
|
arg_list.size() > 0 ? arg_list[0] : QGenericArgument(),
|
||||||
arg_list.size() > 1 ? arg_list[1] : QGenericArgument(),
|
arg_list.size() > 1 ? arg_list[1] : QGenericArgument(),
|
||||||
arg_list.size() > 2 ? arg_list[2] : QGenericArgument(),
|
arg_list.size() > 2 ? arg_list[2] : QGenericArgument(),
|
||||||
|
@ -133,20 +125,10 @@ class Closure : public ClosureBase {
|
||||||
template <typename T, typename... Args>
|
template <typename T, typename... Args>
|
||||||
class SharedClosure : public Closure<Args...> {
|
class SharedClosure : public Closure<Args...> {
|
||||||
public:
|
public:
|
||||||
SharedClosure(
|
SharedClosure(QSharedPointer<T> sender, const char* signal, QObject* receiver,
|
||||||
QSharedPointer<T> sender,
|
const char* slot, const Args&... args)
|
||||||
const char* signal,
|
: Closure<Args...>(sender.data(), signal, receiver, slot, args...),
|
||||||
QObject* receiver,
|
data_(sender) {}
|
||||||
const char* slot,
|
|
||||||
const Args&... args)
|
|
||||||
: Closure<Args...>(
|
|
||||||
sender.data(),
|
|
||||||
signal,
|
|
||||||
receiver,
|
|
||||||
slot,
|
|
||||||
args...),
|
|
||||||
data_(sender) {
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QSharedPointer<T> data_;
|
QSharedPointer<T> data_;
|
||||||
|
@ -154,9 +136,7 @@ class SharedClosure : public Closure<Args...> {
|
||||||
|
|
||||||
class CallbackClosure : public ClosureBase {
|
class CallbackClosure : public ClosureBase {
|
||||||
public:
|
public:
|
||||||
CallbackClosure(
|
CallbackClosure(QObject* sender, const char* signal,
|
||||||
QObject* sender,
|
|
||||||
const char* signal,
|
|
||||||
std::function<void()> callback);
|
std::function<void()> callback);
|
||||||
|
|
||||||
virtual void Invoke();
|
virtual void Invoke();
|
||||||
|
@ -168,61 +148,45 @@ class CallbackClosure : public ClosureBase {
|
||||||
} // namespace _detail
|
} // namespace _detail
|
||||||
|
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
_detail::ClosureBase* NewClosure(
|
_detail::ClosureBase* NewClosure(QObject* sender, const char* signal,
|
||||||
QObject* sender,
|
QObject* receiver, const char* slot,
|
||||||
const char* signal,
|
|
||||||
QObject* receiver,
|
|
||||||
const char* slot,
|
|
||||||
const Args&... args) {
|
const Args&... args) {
|
||||||
return new _detail::Closure<Args...>(
|
return new _detail::Closure<Args...>(sender, signal, receiver, slot, args...);
|
||||||
sender, signal, receiver, slot, args...);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// QSharedPointer variant
|
// QSharedPointer variant
|
||||||
template <typename T, typename... Args>
|
template <typename T, typename... Args>
|
||||||
_detail::ClosureBase* NewClosure(
|
_detail::ClosureBase* NewClosure(QSharedPointer<T> sender, const char* signal,
|
||||||
QSharedPointer<T> sender,
|
QObject* receiver, const char* slot,
|
||||||
const char* signal,
|
|
||||||
QObject* receiver,
|
|
||||||
const char* slot,
|
|
||||||
const Args&... args) {
|
const Args&... args) {
|
||||||
return new _detail::SharedClosure<T, Args...>(
|
return new _detail::SharedClosure<T, Args...>(sender, signal, receiver, slot,
|
||||||
sender, signal, receiver, slot, args...);
|
args...);
|
||||||
}
|
}
|
||||||
|
|
||||||
_detail::ClosureBase* NewClosure(
|
_detail::ClosureBase* NewClosure(QObject* sender, const char* signal,
|
||||||
QObject* sender,
|
|
||||||
const char* signal,
|
|
||||||
std::function<void()> callback);
|
std::function<void()> callback);
|
||||||
|
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
_detail::ClosureBase* NewClosure(
|
_detail::ClosureBase* NewClosure(QObject* sender, const char* signal,
|
||||||
QObject* sender,
|
|
||||||
const char* signal,
|
|
||||||
std::function<void(Args...)> callback,
|
std::function<void(Args...)> callback,
|
||||||
const Args&... args) {
|
const Args&... args) {
|
||||||
return NewClosure(sender, signal, std::bind(callback, args...));
|
return NewClosure(sender, signal, std::bind(callback, args...));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
_detail::ClosureBase* NewClosure(
|
_detail::ClosureBase* NewClosure(QObject* sender, const char* signal,
|
||||||
QObject* sender,
|
|
||||||
const char* signal,
|
|
||||||
void (*callback)(Args...),
|
void (*callback)(Args...),
|
||||||
const Args&... args) {
|
const Args&... args) {
|
||||||
return NewClosure(sender, signal, std::bind(callback, args...));
|
return NewClosure(sender, signal, std::bind(callback, args...));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, typename Unused, typename... Args>
|
template <typename T, typename Unused, typename... Args>
|
||||||
_detail::ClosureBase* NewClosure(
|
_detail::ClosureBase* NewClosure(QObject* sender, const char* signal,
|
||||||
QObject* sender,
|
|
||||||
const char* signal,
|
|
||||||
T* receiver, Unused (T::*callback)(Args...),
|
T* receiver, Unused (T::*callback)(Args...),
|
||||||
const Args&... args) {
|
const Args&... args) {
|
||||||
return NewClosure(sender, signal, std::bind(callback, receiver, args...));
|
return NewClosure(sender, signal, std::bind(callback, receiver, args...));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void DoAfter(QObject* receiver, const char* slot, int msec);
|
void DoAfter(QObject* receiver, const char* slot, int msec);
|
||||||
void DoInAMinuteOrSo(QObject* receiver, const char* slot);
|
void DoInAMinuteOrSo(QObject* receiver, const char* slot);
|
||||||
|
|
||||||
|
|
|
@ -41,13 +41,13 @@
|
||||||
ThreadFunctor object and start it.
|
ThreadFunctor object and start it.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Base abstract classes ThreadFunctorBase and ThreadFunctor (for void and
|
Base abstract classes ThreadFunctorBase and ThreadFunctor (for void and
|
||||||
non-void result):
|
non-void result):
|
||||||
*/
|
*/
|
||||||
template <typename ReturnType>
|
template <typename ReturnType>
|
||||||
class ThreadFunctorBase : public QFutureInterface<ReturnType>, public QRunnable {
|
class ThreadFunctorBase : public QFutureInterface<ReturnType>,
|
||||||
|
public QRunnable {
|
||||||
public:
|
public:
|
||||||
ThreadFunctorBase() {}
|
ThreadFunctorBase() {}
|
||||||
|
|
||||||
|
@ -68,10 +68,8 @@ class ThreadFunctorBase : public QFutureInterface<ReturnType>, public QRunnable
|
||||||
template <typename ReturnType, typename... Args>
|
template <typename ReturnType, typename... Args>
|
||||||
class ThreadFunctor : public ThreadFunctorBase<ReturnType> {
|
class ThreadFunctor : public ThreadFunctorBase<ReturnType> {
|
||||||
public:
|
public:
|
||||||
ThreadFunctor(std::function<ReturnType (Args...)> function,
|
ThreadFunctor(std::function<ReturnType(Args...)> function, Args... args)
|
||||||
Args... args)
|
: function_(std::bind(function, args...)) {}
|
||||||
: function_(std::bind(function, args...)) {
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void run() {
|
virtual void run() {
|
||||||
this->reportResult(function_());
|
this->reportResult(function_());
|
||||||
|
@ -86,10 +84,8 @@ class ThreadFunctor : public ThreadFunctorBase<ReturnType> {
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
class ThreadFunctor<void, Args...> : public ThreadFunctorBase<void> {
|
class ThreadFunctor<void, Args...> : public ThreadFunctorBase<void> {
|
||||||
public:
|
public:
|
||||||
ThreadFunctor(std::function<void (Args...)> function,
|
ThreadFunctor(std::function<void(Args...)> function, Args... args)
|
||||||
Args... args)
|
: function_(std::bind(function, args...)) {}
|
||||||
: function_(std::bind(function, args...)) {
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void run() {
|
virtual void run() {
|
||||||
function_();
|
function_();
|
||||||
|
@ -100,7 +96,6 @@ class ThreadFunctor <void, Args...> : public ThreadFunctorBase<void> {
|
||||||
std::function<void()> function_;
|
std::function<void()> function_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Run functions
|
Run functions
|
||||||
*/
|
*/
|
||||||
|
@ -108,30 +103,25 @@ namespace ConcurrentRun {
|
||||||
|
|
||||||
// Empty argument form.
|
// Empty argument form.
|
||||||
template <typename ReturnType>
|
template <typename ReturnType>
|
||||||
QFuture<ReturnType> Run(
|
QFuture<ReturnType> Run(QThreadPool* threadpool,
|
||||||
QThreadPool* threadpool,
|
|
||||||
std::function<ReturnType()> function) {
|
std::function<ReturnType()> function) {
|
||||||
return (new ThreadFunctor<ReturnType>(function))->Start(threadpool);
|
return (new ThreadFunctor<ReturnType>(function))->Start(threadpool);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function object with arguments form.
|
// Function object with arguments form.
|
||||||
template <typename ReturnType, typename... Args>
|
template <typename ReturnType, typename... Args>
|
||||||
QFuture<ReturnType> Run(
|
QFuture<ReturnType> Run(QThreadPool* threadpool,
|
||||||
QThreadPool* threadpool,
|
|
||||||
std::function<ReturnType(Args...)> function,
|
std::function<ReturnType(Args...)> function,
|
||||||
const Args&... args) {
|
const Args&... args) {
|
||||||
return (new ThreadFunctor<ReturnType, Args...>(
|
return (new ThreadFunctor<ReturnType, Args...>(function, args...))
|
||||||
function, args...))->Start(threadpool);
|
->Start(threadpool);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Support passing C function pointers instead of function objects.
|
// Support passing C function pointers instead of function objects.
|
||||||
template <typename ReturnType, typename... Args>
|
template <typename ReturnType, typename... Args>
|
||||||
QFuture<ReturnType> Run(
|
QFuture<ReturnType> Run(QThreadPool* threadpool,
|
||||||
QThreadPool* threadpool,
|
ReturnType (*function)(Args...), const Args&... args) {
|
||||||
ReturnType (*function) (Args...),
|
return Run(threadpool, std::function<ReturnType(Args...)>(function), args...);
|
||||||
const Args&... args) {
|
|
||||||
return Run(
|
|
||||||
threadpool, std::function<ReturnType (Args...)>(function), args...);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,6 @@
|
||||||
|
|
||||||
#include "logging.h"
|
#include "logging.h"
|
||||||
|
|
||||||
|
|
||||||
namespace logging {
|
namespace logging {
|
||||||
|
|
||||||
static Level sDefaultLevel = Level_Debug;
|
static Level sDefaultLevel = Level_Debug;
|
||||||
|
@ -46,18 +45,25 @@ static const char* kMessageHandlerMagic = "__logging_message__";
|
||||||
static const int kMessageHandlerMagicLength = strlen(kMessageHandlerMagic);
|
static const int kMessageHandlerMagicLength = strlen(kMessageHandlerMagic);
|
||||||
static QtMsgHandler sOriginalMessageHandler = nullptr;
|
static QtMsgHandler sOriginalMessageHandler = nullptr;
|
||||||
|
|
||||||
|
|
||||||
void GLog(const char* domain, int level, const char* message, void* user_data) {
|
void GLog(const char* domain, int level, const char* message, void* user_data) {
|
||||||
switch (level) {
|
switch (level) {
|
||||||
case G_LOG_FLAG_RECURSION:
|
case G_LOG_FLAG_RECURSION:
|
||||||
case G_LOG_FLAG_FATAL:
|
case G_LOG_FLAG_FATAL:
|
||||||
case G_LOG_LEVEL_ERROR:
|
case G_LOG_LEVEL_ERROR:
|
||||||
case G_LOG_LEVEL_CRITICAL: qLog(Error) << message; break;
|
case G_LOG_LEVEL_CRITICAL:
|
||||||
case G_LOG_LEVEL_WARNING: qLog(Warning) << message; break;
|
qLog(Error) << message;
|
||||||
|
break;
|
||||||
|
case G_LOG_LEVEL_WARNING:
|
||||||
|
qLog(Warning) << message;
|
||||||
|
break;
|
||||||
case G_LOG_LEVEL_MESSAGE:
|
case G_LOG_LEVEL_MESSAGE:
|
||||||
case G_LOG_LEVEL_INFO: qLog(Info) << message; break;
|
case G_LOG_LEVEL_INFO:
|
||||||
|
qLog(Info) << message;
|
||||||
|
break;
|
||||||
case G_LOG_LEVEL_DEBUG:
|
case G_LOG_LEVEL_DEBUG:
|
||||||
default: qLog(Debug) << message; break;
|
default:
|
||||||
|
qLog(Debug) << message;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,10 +76,16 @@ static void MessageHandler(QtMsgType type, const char* message) {
|
||||||
Level level = Level_Debug;
|
Level level = Level_Debug;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case QtFatalMsg:
|
case QtFatalMsg:
|
||||||
case QtCriticalMsg: level = Level_Error; break;
|
case QtCriticalMsg:
|
||||||
case QtWarningMsg: level = Level_Warning; break;
|
level = Level_Error;
|
||||||
|
break;
|
||||||
|
case QtWarningMsg:
|
||||||
|
level = Level_Warning;
|
||||||
|
break;
|
||||||
case QtDebugMsg:
|
case QtDebugMsg:
|
||||||
default: level = Level_Debug; break;
|
default:
|
||||||
|
level = Level_Debug;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach(const QString & line, QString::fromLocal8Bit(message).split('\n')) {
|
foreach(const QString & line, QString::fromLocal8Bit(message).split('\n')) {
|
||||||
|
@ -85,7 +97,6 @@ static void MessageHandler(QtMsgType type, const char* message) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Init() {
|
void Init() {
|
||||||
delete sClassLevels;
|
delete sClassLevels;
|
||||||
delete sNullDevice;
|
delete sNullDevice;
|
||||||
|
@ -100,8 +111,7 @@ void Init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetLevels(const QString& levels) {
|
void SetLevels(const QString& levels) {
|
||||||
if (!sClassLevels)
|
if (!sClassLevels) return;
|
||||||
return;
|
|
||||||
|
|
||||||
foreach(const QString & item, levels.split(',')) {
|
foreach(const QString & item, levels.split(',')) {
|
||||||
const QStringList class_level = item.split(':');
|
const QStringList class_level = item.split(':');
|
||||||
|
@ -154,11 +164,21 @@ QDebug CreateLogger(Level level, const QString& class_name, int line) {
|
||||||
// Map the level to a string
|
// Map the level to a string
|
||||||
const char* level_name = nullptr;
|
const char* level_name = nullptr;
|
||||||
switch (level) {
|
switch (level) {
|
||||||
case Level_Debug: level_name = " DEBUG "; break;
|
case Level_Debug:
|
||||||
case Level_Info: level_name = " INFO "; break;
|
level_name = " DEBUG ";
|
||||||
case Level_Warning: level_name = " WARN "; break;
|
break;
|
||||||
case Level_Error: level_name = " ERROR "; break;
|
case Level_Info:
|
||||||
case Level_Fatal: level_name = " FATAL "; break;
|
level_name = " INFO ";
|
||||||
|
break;
|
||||||
|
case Level_Warning:
|
||||||
|
level_name = " WARN ";
|
||||||
|
break;
|
||||||
|
case Level_Error:
|
||||||
|
level_name = " ERROR ";
|
||||||
|
break;
|
||||||
|
case Level_Fatal:
|
||||||
|
level_name = " FATAL ";
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check the settings to see if we're meant to show or hide this message.
|
// Check the settings to see if we're meant to show or hide this message.
|
||||||
|
@ -182,9 +202,11 @@ QDebug CreateLogger(Level level, const QString& class_name, int line) {
|
||||||
}
|
}
|
||||||
|
|
||||||
QDebug ret(type);
|
QDebug ret(type);
|
||||||
ret.nospace() << kMessageHandlerMagic
|
ret.nospace() << kMessageHandlerMagic << QDateTime::currentDateTime()
|
||||||
<< QDateTime::currentDateTime().toString("hh:mm:ss.zzz").toAscii().constData()
|
.toString("hh:mm:ss.zzz")
|
||||||
<< level_name << function_line.leftJustified(32).toAscii().constData();
|
.toAscii()
|
||||||
|
.constData() << level_name
|
||||||
|
<< function_line.leftJustified(32).toAscii().constData();
|
||||||
|
|
||||||
return ret.space();
|
return ret.space();
|
||||||
}
|
}
|
||||||
|
@ -192,10 +214,7 @@ QDebug CreateLogger(Level level, const QString& class_name, int line) {
|
||||||
QString CXXDemangle(const QString& mangled_function) {
|
QString CXXDemangle(const QString& mangled_function) {
|
||||||
int status;
|
int status;
|
||||||
char* demangled_function = abi::__cxa_demangle(
|
char* demangled_function = abi::__cxa_demangle(
|
||||||
mangled_function.toAscii().constData(),
|
mangled_function.toAscii().constData(), nullptr, nullptr, &status);
|
||||||
nullptr,
|
|
||||||
nullptr,
|
|
||||||
&status);
|
|
||||||
if (status == 0) {
|
if (status == 0) {
|
||||||
QString ret = QString::fromAscii(demangled_function);
|
QString ret = QString::fromAscii(demangled_function);
|
||||||
free(demangled_function);
|
free(demangled_function);
|
||||||
|
@ -232,8 +251,10 @@ QString DemangleSymbol(const QString& symbol) {
|
||||||
void DumpStackTrace() {
|
void DumpStackTrace() {
|
||||||
#ifdef Q_OS_UNIX
|
#ifdef Q_OS_UNIX
|
||||||
void* callstack[128];
|
void* callstack[128];
|
||||||
int callstack_size = backtrace(reinterpret_cast<void**>(&callstack), sizeof(callstack));
|
int callstack_size =
|
||||||
char** symbols = backtrace_symbols(reinterpret_cast<void**>(&callstack), callstack_size);
|
backtrace(reinterpret_cast<void**>(&callstack), sizeof(callstack));
|
||||||
|
char** symbols =
|
||||||
|
backtrace_symbols(reinterpret_cast<void**>(&callstack), callstack_size);
|
||||||
// Start from 1 to skip ourself.
|
// Start from 1 to skip ourself.
|
||||||
for (int i = 1; i < callstack_size; ++i) {
|
for (int i = 1; i < callstack_size; ++i) {
|
||||||
qLog(Debug) << DemangleSymbol(QString::fromAscii(symbols[i]));
|
qLog(Debug) << DemangleSymbol(QString::fromAscii(symbols[i]));
|
||||||
|
|
|
@ -18,18 +18,19 @@
|
||||||
// it is used by the Spotify blob which links against libspotify and is not GPL
|
// it is used by the Spotify blob which links against libspotify and is not GPL
|
||||||
// compatible.
|
// compatible.
|
||||||
|
|
||||||
|
|
||||||
#ifndef LOGGING_H
|
#ifndef LOGGING_H
|
||||||
#define LOGGING_H
|
#define LOGGING_H
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
|
||||||
#ifdef QT_NO_DEBUG_STREAM
|
#ifdef QT_NO_DEBUG_STREAM
|
||||||
# define qLog(level) while (false) QNoDebug()
|
#define qLog(level) \
|
||||||
|
while (false) QNoDebug()
|
||||||
#else
|
#else
|
||||||
#define qLog(level) \
|
#define qLog(level) \
|
||||||
logging::CreateLogger(logging::Level_##level, \
|
logging::CreateLogger(logging::Level_##level, \
|
||||||
logging::ParsePrettyFunction(__PRETTY_FUNCTION__), __LINE__)
|
logging::ParsePrettyFunction(__PRETTY_FUNCTION__), \
|
||||||
|
__LINE__)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace logging {
|
namespace logging {
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
// it is used by the Spotify blob which links against libspotify and is not GPL
|
// it is used by the Spotify blob which links against libspotify and is not GPL
|
||||||
// compatible.
|
// compatible.
|
||||||
|
|
||||||
|
|
||||||
#include "messagehandler.h"
|
#include "messagehandler.h"
|
||||||
#include "core/logging.h"
|
#include "core/logging.h"
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
// it is used by the Spotify blob which links against libspotify and is not GPL
|
// it is used by the Spotify blob which links against libspotify and is not GPL
|
||||||
// compatible.
|
// compatible.
|
||||||
|
|
||||||
|
|
||||||
#ifndef MESSAGEHANDLER_H
|
#ifndef MESSAGEHANDLER_H
|
||||||
#define MESSAGEHANDLER_H
|
#define MESSAGEHANDLER_H
|
||||||
|
|
||||||
|
@ -37,11 +36,8 @@ class QAbstractSocket;
|
||||||
class QIODevice;
|
class QIODevice;
|
||||||
class QLocalSocket;
|
class QLocalSocket;
|
||||||
|
|
||||||
#define QStringFromStdString(x) \
|
#define QStringFromStdString(x) QString::fromUtf8(x.data(), x.size())
|
||||||
QString::fromUtf8(x.data(), x.size())
|
#define DataCommaSizeFromQString(x) x.toUtf8().constData(), x.toUtf8().length()
|
||||||
#define DataCommaSizeFromQString(x) \
|
|
||||||
x.toUtf8().constData(), x.toUtf8().length()
|
|
||||||
|
|
||||||
|
|
||||||
// Reads and writes uint32 length encoded protobufs to a socket.
|
// Reads and writes uint32 length encoded protobufs to a socket.
|
||||||
// This base QObject is separate from AbstractMessageHandler because moc can't
|
// This base QObject is separate from AbstractMessageHandler because moc can't
|
||||||
|
@ -83,7 +79,6 @@ protected:
|
||||||
bool is_device_closed_;
|
bool is_device_closed_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// Reads and writes uint32 length encoded MessageType messages to a socket.
|
// Reads and writes uint32 length encoded MessageType messages to a socket.
|
||||||
// You should subclass this and implement the MessageArrived(MessageType)
|
// You should subclass this and implement the MessageArrived(MessageType)
|
||||||
// method.
|
// method.
|
||||||
|
@ -125,13 +120,10 @@ private:
|
||||||
QMap<int, ReplyType*> pending_replies_;
|
QMap<int, ReplyType*> pending_replies_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
template <typename MT>
|
template <typename MT>
|
||||||
AbstractMessageHandler<MT>::AbstractMessageHandler(
|
AbstractMessageHandler<MT>::AbstractMessageHandler(QIODevice* device,
|
||||||
QIODevice* device, QObject* parent)
|
QObject* parent)
|
||||||
: _MessageHandlerBase(device, parent)
|
: _MessageHandlerBase(device, parent) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename MT>
|
template <typename MT>
|
||||||
void AbstractMessageHandler<MT>::SendMessage(const MessageType& message) {
|
void AbstractMessageHandler<MT>::SendMessage(const MessageType& message) {
|
||||||
|
@ -144,7 +136,8 @@ void AbstractMessageHandler<MT>::SendMessage(const MessageType& message) {
|
||||||
template <typename MT>
|
template <typename MT>
|
||||||
void AbstractMessageHandler<MT>::SendMessageAsync(const MessageType& message) {
|
void AbstractMessageHandler<MT>::SendMessageAsync(const MessageType& message) {
|
||||||
std::string data = message.SerializeAsString();
|
std::string data = message.SerializeAsString();
|
||||||
metaObject()->invokeMethod(this, "WriteMessage", Qt::QueuedConnection,
|
metaObject()->invokeMethod(
|
||||||
|
this, "WriteMessage", Qt::QueuedConnection,
|
||||||
Q_ARG(QByteArray, QByteArray(data.data(), data.size())));
|
Q_ARG(QByteArray, QByteArray(data.data(), data.size())));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -182,12 +175,8 @@ bool AbstractMessageHandler<MT>::RawMessageArrived(const QByteArray& data) {
|
||||||
|
|
||||||
template <typename MT>
|
template <typename MT>
|
||||||
void AbstractMessageHandler<MT>::AbortAll() {
|
void AbstractMessageHandler<MT>::AbortAll() {
|
||||||
foreach (ReplyType* reply, pending_replies_) {
|
foreach(ReplyType * reply, pending_replies_) { reply->Abort(); }
|
||||||
reply->Abort();
|
|
||||||
}
|
|
||||||
pending_replies_.clear();
|
pending_replies_.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endif // MESSAGEHANDLER_H
|
#endif // MESSAGEHANDLER_H
|
||||||
|
|
|
@ -18,11 +18,7 @@
|
||||||
#include "messagereply.h"
|
#include "messagereply.h"
|
||||||
|
|
||||||
_MessageReplyBase::_MessageReplyBase(QObject* parent)
|
_MessageReplyBase::_MessageReplyBase(QObject* parent)
|
||||||
: QObject(parent),
|
: QObject(parent), finished_(false), success_(false) {}
|
||||||
finished_(false),
|
|
||||||
success_(false)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _MessageReplyBase::WaitForFinished() {
|
bool _MessageReplyBase::WaitForFinished() {
|
||||||
qLog(Debug) << "Waiting on ID" << id();
|
qLog(Debug) << "Waiting on ID" << id();
|
||||||
|
|
|
@ -53,7 +53,6 @@ protected:
|
||||||
QSemaphore semaphore_;
|
QSemaphore semaphore_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// A reply future class that is returned immediately for requests that will
|
// A reply future class that is returned immediately for requests that will
|
||||||
// occur in the background. Similar to QNetworkReply.
|
// occur in the background. Similar to QNetworkReply.
|
||||||
template <typename MessageType>
|
template <typename MessageType>
|
||||||
|
@ -72,12 +71,10 @@ private:
|
||||||
MessageType reply_message_;
|
MessageType reply_message_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
template <typename MessageType>
|
template <typename MessageType>
|
||||||
MessageReply<MessageType>::MessageReply(const MessageType& request_message,
|
MessageReply<MessageType>::MessageReply(const MessageType& request_message,
|
||||||
QObject* parent)
|
QObject* parent)
|
||||||
: _MessageReplyBase(parent)
|
: _MessageReplyBase(parent) {
|
||||||
{
|
|
||||||
request_message_.MergeFrom(request_message);
|
request_message_.MergeFrom(request_message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
// it is used by the Spotify blob which links against libspotify and is not GPL
|
// it is used by the Spotify blob which links against libspotify and is not GPL
|
||||||
// compatible.
|
// compatible.
|
||||||
|
|
||||||
|
|
||||||
#ifndef OVERRIDE_H
|
#ifndef OVERRIDE_H
|
||||||
#define OVERRIDE_H
|
#define OVERRIDE_H
|
||||||
|
|
||||||
|
|
|
@ -17,9 +17,4 @@
|
||||||
|
|
||||||
#include "workerpool.h"
|
#include "workerpool.h"
|
||||||
|
|
||||||
_WorkerPoolBase::_WorkerPoolBase(QObject* parent)
|
_WorkerPoolBase::_WorkerPoolBase(QObject* parent) : QObject(parent) {}
|
||||||
: QObject(parent)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,6 @@
|
||||||
#include "core/closure.h"
|
#include "core/closure.h"
|
||||||
#include "core/logging.h"
|
#include "core/logging.h"
|
||||||
|
|
||||||
|
|
||||||
// Base class containing signals and slots - required because moc doesn't do
|
// Base class containing signals and slots - required because moc doesn't do
|
||||||
// templated objects.
|
// templated objects.
|
||||||
class _WorkerPoolBase : public QObject {
|
class _WorkerPoolBase : public QObject {
|
||||||
|
@ -53,7 +52,6 @@ protected slots:
|
||||||
virtual void SendQueuedMessages() {}
|
virtual void SendQueuedMessages() {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// Manages a pool of one or more external processes. A local socket server is
|
// Manages a pool of one or more external processes. A local socket server is
|
||||||
// started for each process, and the address is passed to the process as
|
// started for each process, and the address is passed to the process as
|
||||||
// argv[1]. The process is expected to connect back to the socket server, and
|
// argv[1]. The process is expected to connect back to the socket server, and
|
||||||
|
@ -100,7 +98,10 @@ protected:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct Worker {
|
struct Worker {
|
||||||
Worker() : local_server_(NULL), local_socket_(NULL), process_(NULL),
|
Worker()
|
||||||
|
: local_server_(NULL),
|
||||||
|
local_socket_(NULL),
|
||||||
|
process_(NULL),
|
||||||
handler_(NULL) {}
|
handler_(NULL) {}
|
||||||
|
|
||||||
QLocalServer* local_server_;
|
QLocalServer* local_server_;
|
||||||
|
@ -155,26 +156,21 @@ private:
|
||||||
QQueue<ReplyType*> message_queue_;
|
QQueue<ReplyType*> message_queue_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
template <typename HandlerType>
|
template <typename HandlerType>
|
||||||
WorkerPool<HandlerType>::WorkerPool(QObject* parent)
|
WorkerPool<HandlerType>::WorkerPool(QObject* parent)
|
||||||
: _WorkerPoolBase(parent),
|
: _WorkerPoolBase(parent), next_worker_(0), next_id_(0) {
|
||||||
next_worker_(0),
|
|
||||||
next_id_(0)
|
|
||||||
{
|
|
||||||
worker_count_ = qBound(1, QThread::idealThreadCount() / 2, 2);
|
worker_count_ = qBound(1, QThread::idealThreadCount() / 2, 2);
|
||||||
local_server_name_ = qApp->applicationName().toLower();
|
local_server_name_ = qApp->applicationName().toLower();
|
||||||
|
|
||||||
if (local_server_name_.isEmpty())
|
if (local_server_name_.isEmpty()) local_server_name_ = "workerpool";
|
||||||
local_server_name_ = "workerpool";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename HandlerType>
|
template <typename HandlerType>
|
||||||
WorkerPool<HandlerType>::~WorkerPool() {
|
WorkerPool<HandlerType>::~WorkerPool() {
|
||||||
foreach(const Worker & worker, workers_) {
|
foreach(const Worker & worker, workers_) {
|
||||||
if (worker.local_socket_ && worker.process_) {
|
if (worker.local_socket_ && worker.process_) {
|
||||||
disconnect(worker.process_, SIGNAL(error(QProcess::ProcessError)),
|
disconnect(worker.process_, SIGNAL(error(QProcess::ProcessError)), this,
|
||||||
this, SLOT(ProcessError(QProcess::ProcessError)));
|
SLOT(ProcessError(QProcess::ProcessError)));
|
||||||
|
|
||||||
// The worker is connected. Close his socket and wait for him to exit.
|
// The worker is connected. Close his socket and wait for him to exit.
|
||||||
qLog(Debug) << "Closing worker socket";
|
qLog(Debug) << "Closing worker socket";
|
||||||
|
@ -192,9 +188,7 @@ WorkerPool<HandlerType>::~WorkerPool() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (ReplyType* reply, message_queue_) {
|
foreach(ReplyType * reply, message_queue_) { reply->Abort(); }
|
||||||
reply->Abort();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename HandlerType>
|
template <typename HandlerType>
|
||||||
|
@ -204,13 +198,15 @@ void WorkerPool<HandlerType>::SetWorkerCount(int count) {
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename HandlerType>
|
template <typename HandlerType>
|
||||||
void WorkerPool<HandlerType>::SetLocalServerName(const QString& local_server_name) {
|
void WorkerPool<HandlerType>::SetLocalServerName(
|
||||||
|
const QString& local_server_name) {
|
||||||
Q_ASSERT(workers_.isEmpty());
|
Q_ASSERT(workers_.isEmpty());
|
||||||
local_server_name_ = local_server_name;
|
local_server_name_ = local_server_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename HandlerType>
|
template <typename HandlerType>
|
||||||
void WorkerPool<HandlerType>::SetExecutableName(const QString& executable_name) {
|
void WorkerPool<HandlerType>::SetExecutableName(
|
||||||
|
const QString& executable_name) {
|
||||||
Q_ASSERT(workers_.isEmpty());
|
Q_ASSERT(workers_.isEmpty());
|
||||||
executable_name_ = executable_name;
|
executable_name_ = executable_name;
|
||||||
}
|
}
|
||||||
|
@ -264,14 +260,16 @@ void WorkerPool<HandlerType>::StartOneWorker(Worker* worker) {
|
||||||
worker->local_server_ = new QLocalServer(this);
|
worker->local_server_ = new QLocalServer(this);
|
||||||
worker->process_ = new QProcess(this);
|
worker->process_ = new QProcess(this);
|
||||||
|
|
||||||
connect(worker->local_server_, SIGNAL(newConnection()), SLOT(NewConnection()));
|
connect(worker->local_server_, SIGNAL(newConnection()),
|
||||||
|
SLOT(NewConnection()));
|
||||||
connect(worker->process_, SIGNAL(error(QProcess::ProcessError)),
|
connect(worker->process_, SIGNAL(error(QProcess::ProcessError)),
|
||||||
SLOT(ProcessError(QProcess::ProcessError)));
|
SLOT(ProcessError(QProcess::ProcessError)));
|
||||||
|
|
||||||
// Create a server, find an unused name and start listening
|
// Create a server, find an unused name and start listening
|
||||||
forever {
|
forever {
|
||||||
const int unique_number = qrand() ^ ((int)(quint64(this) & 0xFFFFFFFF));
|
const int unique_number = qrand() ^ ((int)(quint64(this) & 0xFFFFFFFF));
|
||||||
const QString name = QString("%1_%2").arg(local_server_name_).arg(unique_number);
|
const QString name =
|
||||||
|
QString("%1_%2").arg(local_server_name_).arg(unique_number);
|
||||||
|
|
||||||
if (worker->local_server_->listen(name)) {
|
if (worker->local_server_->listen(name)) {
|
||||||
break;
|
break;
|
||||||
|
@ -284,7 +282,8 @@ void WorkerPool<HandlerType>::StartOneWorker(Worker* worker) {
|
||||||
// Start the process
|
// Start the process
|
||||||
worker->process_->setProcessChannelMode(QProcess::ForwardedChannels);
|
worker->process_->setProcessChannelMode(QProcess::ForwardedChannels);
|
||||||
worker->process_->start(executable_path_,
|
worker->process_->start(executable_path_,
|
||||||
QStringList() << worker->local_server_->fullServerName());
|
QStringList()
|
||||||
|
<< worker->local_server_->fullServerName());
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename HandlerType>
|
template <typename HandlerType>
|
||||||
|
@ -295,10 +294,10 @@ void WorkerPool<HandlerType>::NewConnection() {
|
||||||
|
|
||||||
// Find the worker with this server.
|
// Find the worker with this server.
|
||||||
Worker* worker = FindWorker(&Worker::local_server_, server);
|
Worker* worker = FindWorker(&Worker::local_server_, server);
|
||||||
if (!worker)
|
if (!worker) return;
|
||||||
return;
|
|
||||||
|
|
||||||
qLog(Debug) << "Worker" << worker << "connected to" << server->fullServerName();
|
qLog(Debug) << "Worker" << worker << "connected to"
|
||||||
|
<< server->fullServerName();
|
||||||
|
|
||||||
// Accept the connection.
|
// Accept the connection.
|
||||||
worker->local_socket_ = server->nextPendingConnection();
|
worker->local_socket_ = server->nextPendingConnection();
|
||||||
|
@ -322,8 +321,7 @@ void WorkerPool<HandlerType>::ProcessError(QProcess::ProcessError error) {
|
||||||
|
|
||||||
// Find the worker with this process.
|
// Find the worker with this process.
|
||||||
Worker* worker = FindWorker(&Worker::process_, process);
|
Worker* worker = FindWorker(&Worker::process_, process);
|
||||||
if (!worker)
|
if (!worker) return;
|
||||||
return;
|
|
||||||
|
|
||||||
switch (error) {
|
switch (error) {
|
||||||
case QProcess::FailedToStart:
|
case QProcess::FailedToStart:
|
||||||
|
@ -336,15 +334,16 @@ void WorkerPool<HandlerType>::ProcessError(QProcess::ProcessError error) {
|
||||||
|
|
||||||
default:
|
default:
|
||||||
// On any other error we just restart the process.
|
// On any other error we just restart the process.
|
||||||
qLog(Debug) << "Worker" << worker << "failed with error" << error << "- restarting";
|
qLog(Debug) << "Worker" << worker << "failed with error" << error
|
||||||
|
<< "- restarting";
|
||||||
StartOneWorker(worker);
|
StartOneWorker(worker);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename HandlerType>
|
template <typename HandlerType>
|
||||||
typename WorkerPool<HandlerType>::ReplyType*
|
typename WorkerPool<HandlerType>::ReplyType* WorkerPool<HandlerType>::NewReply(
|
||||||
WorkerPool<HandlerType>::NewReply(MessageType* message) {
|
MessageType* message) {
|
||||||
const int id = next_id_.fetchAndAddOrdered(1);
|
const int id = next_id_.fetchAndAddOrdered(1);
|
||||||
message->set_id(id);
|
message->set_id(id);
|
||||||
|
|
||||||
|
|
|
@ -32,9 +32,9 @@ namespace {
|
||||||
static const int kTaglibSuffixCacheBytes = 8 * 1024;
|
static const int kTaglibSuffixCacheBytes = 8 * 1024;
|
||||||
}
|
}
|
||||||
|
|
||||||
CloudStream::CloudStream(
|
CloudStream::CloudStream(const QUrl& url, const QString& filename,
|
||||||
const QUrl& url, const QString& filename, const long length,
|
const long length, const QString& auth,
|
||||||
const QString& auth, QNetworkAccessManager* network)
|
QNetworkAccessManager* network)
|
||||||
: url_(url),
|
: url_(url),
|
||||||
filename_(filename),
|
filename_(filename),
|
||||||
encoded_filename_(filename_.toUtf8()),
|
encoded_filename_(filename_.toUtf8()),
|
||||||
|
@ -43,12 +43,9 @@ CloudStream::CloudStream(
|
||||||
cursor_(0),
|
cursor_(0),
|
||||||
network_(network),
|
network_(network),
|
||||||
cache_(length),
|
cache_(length),
|
||||||
num_requests_(0) {
|
num_requests_(0) {}
|
||||||
}
|
|
||||||
|
|
||||||
TagLib::FileName CloudStream::name() const {
|
TagLib::FileName CloudStream::name() const { return encoded_filename_.data(); }
|
||||||
return encoded_filename_.data();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CloudStream::CheckCache(int start, int end) {
|
bool CloudStream::CheckCache(int start, int end) {
|
||||||
for (int i = start; i <= end; ++i) {
|
for (int i = start; i <= end; ++i) {
|
||||||
|
@ -113,8 +110,8 @@ TagLib::ByteVector CloudStream::readBlock(ulong length) {
|
||||||
if (!auth_.isEmpty()) {
|
if (!auth_.isEmpty()) {
|
||||||
request.setRawHeader("Authorization", auth_.toUtf8());
|
request.setRawHeader("Authorization", auth_.toUtf8());
|
||||||
}
|
}
|
||||||
request.setRawHeader(
|
request.setRawHeader("Range",
|
||||||
"Range", QString("bytes=%1-%2").arg(start).arg(end).toUtf8());
|
QString("bytes=%1-%2").arg(start).arg(end).toUtf8());
|
||||||
request.setAttribute(QNetworkRequest::CacheLoadControlAttribute,
|
request.setAttribute(QNetworkRequest::CacheLoadControlAttribute,
|
||||||
QNetworkRequest::AlwaysNetwork);
|
QNetworkRequest::AlwaysNetwork);
|
||||||
// The Ubuntu One server applies the byte range to the gzipped data, rather
|
// The Ubuntu One server applies the byte range to the gzipped data, rather
|
||||||
|
@ -124,7 +121,8 @@ TagLib::ByteVector CloudStream::readBlock(ulong length) {
|
||||||
}
|
}
|
||||||
|
|
||||||
QNetworkReply* reply = network_->get(request);
|
QNetworkReply* reply = network_->get(request);
|
||||||
connect(reply, SIGNAL(sslErrors(QList<QSslError>)), SLOT(SSLErrors(QList<QSslError>)));
|
connect(reply, SIGNAL(sslErrors(QList<QSslError>)),
|
||||||
|
SLOT(SSLErrors(QList<QSslError>)));
|
||||||
++num_requests_;
|
++num_requests_;
|
||||||
|
|
||||||
QEventLoop loop;
|
QEventLoop loop;
|
||||||
|
@ -163,9 +161,7 @@ bool CloudStream::readOnly() const {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CloudStream::isOpen() const {
|
bool CloudStream::isOpen() const { return true; }
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CloudStream::seek(long offset, TagLib::IOStream::Position p) {
|
void CloudStream::seek(long offset, TagLib::IOStream::Position p) {
|
||||||
switch (p) {
|
switch (p) {
|
||||||
|
@ -184,17 +180,11 @@ void CloudStream::seek(long offset, TagLib::IOStream::Position p) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CloudStream::clear() {
|
void CloudStream::clear() { cursor_ = 0; }
|
||||||
cursor_ = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
long CloudStream::tell() const {
|
long CloudStream::tell() const { return cursor_; }
|
||||||
return cursor_;
|
|
||||||
}
|
|
||||||
|
|
||||||
long CloudStream::length() {
|
long CloudStream::length() { return length_; }
|
||||||
return length_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CloudStream::truncate(long) {
|
void CloudStream::truncate(long) {
|
||||||
qLog(Debug) << Q_FUNC_INFO << "not implemented";
|
qLog(Debug) << Q_FUNC_INFO << "not implemented";
|
||||||
|
|
|
@ -31,11 +31,8 @@ class QNetworkAccessManager;
|
||||||
class CloudStream : public QObject, public TagLib::IOStream {
|
class CloudStream : public QObject, public TagLib::IOStream {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
CloudStream(const QUrl& url,
|
CloudStream(const QUrl& url, const QString& filename, const long length,
|
||||||
const QString& filename,
|
const QString& auth, QNetworkAccessManager* network);
|
||||||
const long length,
|
|
||||||
const QString& auth,
|
|
||||||
QNetworkAccessManager* network);
|
|
||||||
|
|
||||||
// Taglib::IOStream
|
// Taglib::IOStream
|
||||||
virtual TagLib::FileName name() const;
|
virtual TagLib::FileName name() const;
|
||||||
|
@ -55,9 +52,7 @@ class CloudStream : public QObject, public TagLib::IOStream {
|
||||||
return cache_.num_nonempty();
|
return cache_.num_nonempty();
|
||||||
}
|
}
|
||||||
|
|
||||||
int num_requests() const {
|
int num_requests() const { return num_requests_; }
|
||||||
return num_requests_;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use educated guess to request the bytes that TagLib will probably want.
|
// Use educated guess to request the bytes that TagLib will probably want.
|
||||||
void Precache();
|
void Precache();
|
||||||
|
|
|
@ -25,8 +25,9 @@
|
||||||
using std::placeholders::_1;
|
using std::placeholders::_1;
|
||||||
using std::placeholders::_2;
|
using std::placeholders::_2;
|
||||||
|
|
||||||
FMPSParser::FMPSParser() :
|
FMPSParser::FMPSParser()
|
||||||
// The float regex ends with (?:$|(?=::|;;)) to ensure it matches all the way
|
: // The float regex ends with (?:$|(?=::|;;)) to ensure it matches all the
|
||||||
|
// way
|
||||||
// up to the end of the value. Without it, it would match a string that
|
// up to the end of the value. Without it, it would match a string that
|
||||||
// starts with a number, like "123abc".
|
// starts with a number, like "123abc".
|
||||||
float_re_("\\s*([+-]?\\d+(?:\\.\\d+)?)\\s*(?:$|(?=::|;;))"),
|
float_re_("\\s*([+-]?\\d+(?:\\.\\d+)?)\\s*(?:$|(?=::|;;))"),
|
||||||
|
@ -35,9 +36,7 @@ FMPSParser::FMPSParser() :
|
||||||
string_re_("((?:[^\\\\;:]|(?:\\\\[\\\\:;]))+)(?:$|(?=::|;;))"),
|
string_re_("((?:[^\\\\;:]|(?:\\\\[\\\\:;]))+)(?:$|(?=::|;;))"),
|
||||||
|
|
||||||
// Used for replacing escaped characters.
|
// Used for replacing escaped characters.
|
||||||
escape_re_("\\\\([\\\\:;])")
|
escape_re_("\\\\([\\\\:;])") {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parses a list of things (of type T) that are separated by two consecutive
|
// Parses a list of things (of type T) that are separated by two consecutive
|
||||||
// Separator characters. Each individual thing is parsed by the F function.
|
// Separator characters. Each individual thing is parsed by the F function.
|
||||||
|
@ -59,16 +58,16 @@ static int ParseContainer(const QStringRef& data, F f, QList<T>* ret) {
|
||||||
int pos = 0;
|
int pos = 0;
|
||||||
while (pos < data.length()) {
|
while (pos < data.length()) {
|
||||||
const int len = data.length() - pos;
|
const int len = data.length() - pos;
|
||||||
int matched_len = f(QStringRef(data.string(), data.position() + pos, len), &value);
|
int matched_len =
|
||||||
if (matched_len == -1 || matched_len > len)
|
f(QStringRef(data.string(), data.position() + pos, len), &value);
|
||||||
break;
|
if (matched_len == -1 || matched_len > len) break;
|
||||||
|
|
||||||
ret->append(value);
|
ret->append(value);
|
||||||
pos += matched_len;
|
pos += matched_len;
|
||||||
|
|
||||||
// Expect two separators in a row
|
// Expect two separators in a row
|
||||||
if (pos + 2 <= data.length() && data.at(pos) == Separator
|
if (pos + 2 <= data.length() && data.at(pos) == Separator &&
|
||||||
&& data.at(pos+1) == Separator) {
|
data.at(pos + 1) == Separator) {
|
||||||
pos += 2;
|
pos += 2;
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -59,7 +59,8 @@
|
||||||
#include "core/timeconstants.h"
|
#include "core/timeconstants.h"
|
||||||
|
|
||||||
// Taglib added support for FLAC pictures in 1.7.0
|
// Taglib added support for FLAC pictures in 1.7.0
|
||||||
#if (TAGLIB_MAJOR_VERSION > 1) || (TAGLIB_MAJOR_VERSION == 1 && TAGLIB_MINOR_VERSION >= 7)
|
#if (TAGLIB_MAJOR_VERSION > 1) || \
|
||||||
|
(TAGLIB_MAJOR_VERSION == 1 && TAGLIB_MINOR_VERSION >= 7)
|
||||||
#define TAGLIB_HAS_FLAC_PICTURELIST
|
#define TAGLIB_HAS_FLAC_PICTURELIST
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -67,7 +68,8 @@
|
||||||
#include "cloudstream.h"
|
#include "cloudstream.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define NumberToASFAttribute(x) TagLib::ASF::Attribute(QStringToTaglibString(QString::number(x)))
|
#define NumberToASFAttribute(x) \
|
||||||
|
TagLib::ASF::Attribute(QStringToTaglibString(QString::number(x)))
|
||||||
|
|
||||||
class FileRefFactory {
|
class FileRefFactory {
|
||||||
public:
|
public:
|
||||||
|
@ -95,18 +97,19 @@ TagLib::String StdStringToTaglibString(const std::string& s) {
|
||||||
TagLib::String QStringToTaglibString(const QString& s) {
|
TagLib::String QStringToTaglibString(const QString& s) {
|
||||||
return TagLib::String(s.toUtf8().constData(), TagLib::String::UTF8);
|
return TagLib::String(s.toUtf8().constData(), TagLib::String::UTF8);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* TagReader::kMP4_FMPS_Rating_ID = "----:com.apple.iTunes:FMPS_Rating";
|
const char* TagReader::kMP4_FMPS_Rating_ID =
|
||||||
const char* TagReader::kMP4_FMPS_Playcount_ID = "----:com.apple.iTunes:FMPS_Playcount";
|
"----:com.apple.iTunes:FMPS_Rating";
|
||||||
const char* TagReader::kMP4_FMPS_Score_ID = "----:com.apple.iTunes:FMPS_Rating_Amarok_Score";
|
const char* TagReader::kMP4_FMPS_Playcount_ID =
|
||||||
|
"----:com.apple.iTunes:FMPS_Playcount";
|
||||||
|
const char* TagReader::kMP4_FMPS_Score_ID =
|
||||||
|
"----:com.apple.iTunes:FMPS_Rating_Amarok_Score";
|
||||||
|
|
||||||
TagReader::TagReader()
|
TagReader::TagReader()
|
||||||
: factory_(new TagLibFileRefFactory),
|
: factory_(new TagLibFileRefFactory),
|
||||||
network_(new QNetworkAccessManager),
|
network_(new QNetworkAccessManager),
|
||||||
kEmbeddedCover("(embedded)")
|
kEmbeddedCover("(embedded)") {}
|
||||||
{}
|
|
||||||
|
|
||||||
void TagReader::ReadFile(const QString& filename,
|
void TagReader::ReadFile(const QString& filename,
|
||||||
pb::tagreader::SongMetadata* song) const {
|
pb::tagreader::SongMetadata* song) const {
|
||||||
|
@ -141,14 +144,17 @@ void TagReader::ReadFile(const QString& filename,
|
||||||
QString disc;
|
QString disc;
|
||||||
QString compilation;
|
QString compilation;
|
||||||
|
|
||||||
// Handle all the files which have VorbisComments (Ogg, OPUS, ...) in the same way;
|
// Handle all the files which have VorbisComments (Ogg, OPUS, ...) in the same
|
||||||
|
// way;
|
||||||
// apart, so we keep specific behavior for some formats by adding another
|
// apart, so we keep specific behavior for some formats by adding another
|
||||||
// "else if" block below.
|
// "else if" block below.
|
||||||
if (TagLib::Ogg::XiphComment* tag = dynamic_cast<TagLib::Ogg::XiphComment*>(fileref->file()->tag())) {
|
if (TagLib::Ogg::XiphComment* tag =
|
||||||
|
dynamic_cast<TagLib::Ogg::XiphComment*>(fileref->file()->tag())) {
|
||||||
ParseOggTag(tag->fieldListMap(), nullptr, &disc, &compilation, song);
|
ParseOggTag(tag->fieldListMap(), nullptr, &disc, &compilation, song);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (TagLib::MPEG::File* file = dynamic_cast<TagLib::MPEG::File*>(fileref->file())) {
|
if (TagLib::MPEG::File* file =
|
||||||
|
dynamic_cast<TagLib::MPEG::File*>(fileref->file())) {
|
||||||
if (file->ID3v2Tag()) {
|
if (file->ID3v2Tag()) {
|
||||||
const TagLib::ID3v2::FrameListMap& map = file->ID3v2Tag()->frameListMap();
|
const TagLib::ID3v2::FrameListMap& map = file->ID3v2Tag()->frameListMap();
|
||||||
|
|
||||||
|
@ -156,24 +162,29 @@ void TagReader::ReadFile(const QString& filename,
|
||||||
disc = TStringToQString(map["TPOS"].front()->toString()).trimmed();
|
disc = TStringToQString(map["TPOS"].front()->toString()).trimmed();
|
||||||
|
|
||||||
if (!map["TBPM"].isEmpty())
|
if (!map["TBPM"].isEmpty())
|
||||||
song->set_bpm(TStringToQString(map["TBPM"].front()->toString()).trimmed().toFloat());
|
song->set_bpm(TStringToQString(map["TBPM"].front()->toString())
|
||||||
|
.trimmed()
|
||||||
|
.toFloat());
|
||||||
|
|
||||||
if (!map["TCOM"].isEmpty())
|
if (!map["TCOM"].isEmpty())
|
||||||
Decode(map["TCOM"].front()->toString(), nullptr, song->mutable_composer());
|
Decode(map["TCOM"].front()->toString(), nullptr,
|
||||||
|
song->mutable_composer());
|
||||||
|
|
||||||
if (!map["TIT1"].isEmpty()) // content group
|
if (!map["TIT1"].isEmpty()) // content group
|
||||||
Decode(map["TIT1"].front()->toString(), nullptr, song->mutable_grouping());
|
Decode(map["TIT1"].front()->toString(), nullptr,
|
||||||
|
song->mutable_grouping());
|
||||||
|
|
||||||
// Skip TPE1 (which is the artist) here because we already fetched it
|
// Skip TPE1 (which is the artist) here because we already fetched it
|
||||||
|
|
||||||
if (!map["TPE2"].isEmpty()) // non-standard: Apple, Microsoft
|
if (!map["TPE2"].isEmpty()) // non-standard: Apple, Microsoft
|
||||||
Decode(map["TPE2"].front()->toString(), nullptr, song->mutable_albumartist());
|
Decode(map["TPE2"].front()->toString(), nullptr,
|
||||||
|
song->mutable_albumartist());
|
||||||
|
|
||||||
if (!map["TCMP"].isEmpty())
|
if (!map["TCMP"].isEmpty())
|
||||||
compilation = TStringToQString(map["TCMP"].front()->toString()).trimmed();
|
compilation =
|
||||||
|
TStringToQString(map["TCMP"].front()->toString()).trimmed();
|
||||||
|
|
||||||
if (!map["APIC"].isEmpty())
|
if (!map["APIC"].isEmpty()) song->set_art_automatic(kEmbeddedCover);
|
||||||
song->set_art_automatic(kEmbeddedCover);
|
|
||||||
|
|
||||||
// Find a suitable comment tag. For now we ignore iTunNORM comments.
|
// Find a suitable comment tag. For now we ignore iTunNORM comments.
|
||||||
for (int i = 0; i < map["COMM"].size(); ++i) {
|
for (int i = 0; i < map["COMM"].size(); ++i) {
|
||||||
|
@ -189,12 +200,12 @@ void TagReader::ReadFile(const QString& filename,
|
||||||
// Parse FMPS frames
|
// Parse FMPS frames
|
||||||
for (int i = 0; i < map["TXXX"].size(); ++i) {
|
for (int i = 0; i < map["TXXX"].size(); ++i) {
|
||||||
const TagLib::ID3v2::UserTextIdentificationFrame* frame =
|
const TagLib::ID3v2::UserTextIdentificationFrame* frame =
|
||||||
dynamic_cast<const TagLib::ID3v2::UserTextIdentificationFrame*>(map["TXXX"][i]);
|
dynamic_cast<const TagLib::ID3v2::UserTextIdentificationFrame*>(
|
||||||
|
map["TXXX"][i]);
|
||||||
|
|
||||||
if (frame && frame->description().startsWith("FMPS_")) {
|
if (frame && frame->description().startsWith("FMPS_")) {
|
||||||
ParseFMPSFrame(TStringToQString(frame->description()),
|
ParseFMPSFrame(TStringToQString(frame->description()),
|
||||||
TStringToQString(frame->fieldList()[1]),
|
TStringToQString(frame->fieldList()[1]), song);
|
||||||
song);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -203,7 +214,8 @@ void TagReader::ReadFile(const QString& filename,
|
||||||
// will consider POPM tags iff song has no rating/playcount already set.
|
// will consider POPM tags iff song has no rating/playcount already set.
|
||||||
if (!map["POPM"].isEmpty()) {
|
if (!map["POPM"].isEmpty()) {
|
||||||
const TagLib::ID3v2::PopularimeterFrame* frame =
|
const TagLib::ID3v2::PopularimeterFrame* frame =
|
||||||
dynamic_cast<const TagLib::ID3v2::PopularimeterFrame*>(map["POPM"].front());
|
dynamic_cast<const TagLib::ID3v2::PopularimeterFrame*>(
|
||||||
|
map["POPM"].front());
|
||||||
if (frame) {
|
if (frame) {
|
||||||
// Take a user rating only if there's no rating already set
|
// Take a user rating only if there's no rating already set
|
||||||
if (song->rating() <= 0 && frame->rating() > 0) {
|
if (song->rating() <= 0 && frame->rating() > 0) {
|
||||||
|
@ -214,11 +226,12 @@ void TagReader::ReadFile(const QString& filename,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
} else if (TagLib::FLAC::File* file = dynamic_cast<TagLib::FLAC::File*>(fileref->file())) {
|
} else if (TagLib::FLAC::File* file =
|
||||||
|
dynamic_cast<TagLib::FLAC::File*>(fileref->file())) {
|
||||||
if (file->xiphComment()) {
|
if (file->xiphComment()) {
|
||||||
ParseOggTag(file->xiphComment()->fieldListMap(), nullptr, &disc, &compilation, song);
|
ParseOggTag(file->xiphComment()->fieldListMap(), nullptr, &disc,
|
||||||
|
&compilation, song);
|
||||||
#ifdef TAGLIB_HAS_FLAC_PICTURELIST
|
#ifdef TAGLIB_HAS_FLAC_PICTURELIST
|
||||||
if (!file->pictureList().isEmpty()) {
|
if (!file->pictureList().isEmpty()) {
|
||||||
song->set_art_automatic(kEmbeddedCover);
|
song->set_art_automatic(kEmbeddedCover);
|
||||||
|
@ -226,7 +239,8 @@ void TagReader::ReadFile(const QString& filename,
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
Decode(tag->comment(), nullptr, song->mutable_comment());
|
Decode(tag->comment(), nullptr, song->mutable_comment());
|
||||||
} else if (TagLib::MP4::File* file = dynamic_cast<TagLib::MP4::File*>(fileref->file())) {
|
} else if (TagLib::MP4::File* file =
|
||||||
|
dynamic_cast<TagLib::MP4::File*>(fileref->file())) {
|
||||||
if (file->tag()) {
|
if (file->tag()) {
|
||||||
TagLib::MP4::Tag* mp4_tag = file->tag();
|
TagLib::MP4::Tag* mp4_tag = file->tag();
|
||||||
const TagLib::MP4::ItemListMap& items = mp4_tag->itemListMap();
|
const TagLib::MP4::ItemListMap& items = mp4_tag->itemListMap();
|
||||||
|
@ -246,51 +260,67 @@ void TagReader::ReadFile(const QString& filename,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (items.contains("disk")) {
|
if (items.contains("disk")) {
|
||||||
disc = TStringToQString(TagLib::String::number(items["disk"].toIntPair().first));
|
disc = TStringToQString(
|
||||||
|
TagLib::String::number(items["disk"].toIntPair().first));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (items.contains(kMP4_FMPS_Rating_ID)) {
|
if (items.contains(kMP4_FMPS_Rating_ID)) {
|
||||||
float rating = TStringToQString(items[kMP4_FMPS_Rating_ID].toStringList().toString('\n')).toFloat();
|
float rating =
|
||||||
|
TStringToQString(items[kMP4_FMPS_Rating_ID].toStringList().toString(
|
||||||
|
'\n')).toFloat();
|
||||||
if (song->rating() <= 0 && rating > 0) {
|
if (song->rating() <= 0 && rating > 0) {
|
||||||
song->set_rating(rating);
|
song->set_rating(rating);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (items.contains(kMP4_FMPS_Playcount_ID)) {
|
if (items.contains(kMP4_FMPS_Playcount_ID)) {
|
||||||
int playcount = TStringToQString(items[kMP4_FMPS_Playcount_ID].toStringList().toString('\n')).toFloat();
|
int playcount =
|
||||||
|
TStringToQString(
|
||||||
|
items[kMP4_FMPS_Playcount_ID].toStringList().toString('\n'))
|
||||||
|
.toFloat();
|
||||||
if (song->playcount() <= 0 && playcount > 0) {
|
if (song->playcount() <= 0 && playcount > 0) {
|
||||||
song->set_playcount(playcount);
|
song->set_playcount(playcount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (items.contains(kMP4_FMPS_Playcount_ID)) {
|
if (items.contains(kMP4_FMPS_Playcount_ID)) {
|
||||||
int score = TStringToQString(items[kMP4_FMPS_Score_ID].toStringList().toString('\n')).toFloat() * 100;
|
int score = TStringToQString(
|
||||||
|
items[kMP4_FMPS_Score_ID].toStringList().toString('\n'))
|
||||||
|
.toFloat() *
|
||||||
|
100;
|
||||||
if (song->score() <= 0 && score > 0) {
|
if (song->score() <= 0 && score > 0) {
|
||||||
song->set_score(score);
|
song->set_score(score);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (items.contains("\251wrt")) {
|
if (items.contains("\251wrt")) {
|
||||||
Decode(items["\251wrt"].toStringList().toString(", "), nullptr, song->mutable_composer());
|
Decode(items["\251wrt"].toStringList().toString(", "), nullptr,
|
||||||
|
song->mutable_composer());
|
||||||
}
|
}
|
||||||
if (items.contains("\251grp")) {
|
if (items.contains("\251grp")) {
|
||||||
Decode(items["\251grp"].toStringList().toString(" "), nullptr, song->mutable_grouping());
|
Decode(items["\251grp"].toStringList().toString(" "), nullptr,
|
||||||
|
song->mutable_grouping());
|
||||||
}
|
}
|
||||||
Decode(mp4_tag->comment(), nullptr, song->mutable_comment());
|
Decode(mp4_tag->comment(), nullptr, song->mutable_comment());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#ifdef TAGLIB_WITH_ASF
|
#ifdef TAGLIB_WITH_ASF
|
||||||
else if (TagLib::ASF::File* file = dynamic_cast<TagLib::ASF::File*>(fileref->file())) {
|
else if (TagLib::ASF::File* file =
|
||||||
const TagLib::ASF::AttributeListMap& attributes_map = file->tag()->attributeListMap();
|
dynamic_cast<TagLib::ASF::File*>(fileref->file())) {
|
||||||
|
const TagLib::ASF::AttributeListMap& attributes_map =
|
||||||
|
file->tag()->attributeListMap();
|
||||||
if (attributes_map.contains("FMPS/Rating")) {
|
if (attributes_map.contains("FMPS/Rating")) {
|
||||||
const TagLib::ASF::AttributeList& attributes = attributes_map["FMPS/Rating"];
|
const TagLib::ASF::AttributeList& attributes =
|
||||||
|
attributes_map["FMPS/Rating"];
|
||||||
if (!attributes.isEmpty()) {
|
if (!attributes.isEmpty()) {
|
||||||
float rating = TStringToQString(attributes.front().toString()).toFloat();
|
float rating =
|
||||||
|
TStringToQString(attributes.front().toString()).toFloat();
|
||||||
if (song->rating() <= 0 && rating > 0) {
|
if (song->rating() <= 0 && rating > 0) {
|
||||||
song->set_rating(rating);
|
song->set_rating(rating);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (attributes_map.contains("FMPS/Playcount")) {
|
if (attributes_map.contains("FMPS/Playcount")) {
|
||||||
const TagLib::ASF::AttributeList& attributes = attributes_map["FMPS/Playcount"];
|
const TagLib::ASF::AttributeList& attributes =
|
||||||
|
attributes_map["FMPS/Playcount"];
|
||||||
if (!attributes.isEmpty()) {
|
if (!attributes.isEmpty()) {
|
||||||
int playcount = TStringToQString(attributes.front().toString()).toInt();
|
int playcount = TStringToQString(attributes.front().toString()).toInt();
|
||||||
if (song->playcount() <= 0 && playcount > 0) {
|
if (song->playcount() <= 0 && playcount > 0) {
|
||||||
|
@ -299,9 +329,11 @@ void TagReader::ReadFile(const QString& filename,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (attributes_map.contains("FMPS/Rating_Amarok_Score")) {
|
if (attributes_map.contains("FMPS/Rating_Amarok_Score")) {
|
||||||
const TagLib::ASF::AttributeList& attributes = attributes_map["FMPS/Rating_Amarok_Score"];
|
const TagLib::ASF::AttributeList& attributes =
|
||||||
|
attributes_map["FMPS/Rating_Amarok_Score"];
|
||||||
if (!attributes.isEmpty()) {
|
if (!attributes.isEmpty()) {
|
||||||
int score = TStringToQString(attributes.front().toString()).toFloat() * 100;
|
int score =
|
||||||
|
TStringToQString(attributes.front().toString()).toFloat() * 100;
|
||||||
if (song->score() <= 0 && score > 0) {
|
if (song->score() <= 0 && score > 0) {
|
||||||
song->set_score(score);
|
song->set_score(score);
|
||||||
}
|
}
|
||||||
|
@ -316,7 +348,8 @@ void TagReader::ReadFile(const QString& filename,
|
||||||
if (!disc.isEmpty()) {
|
if (!disc.isEmpty()) {
|
||||||
const int i = disc.indexOf('/');
|
const int i = disc.indexOf('/');
|
||||||
if (i != -1) {
|
if (i != -1) {
|
||||||
// disc.right( i ).toInt() is total number of discs, we don't use this at the moment
|
// disc.right( i ).toInt() is total number of discs, we don't use this at
|
||||||
|
// the moment
|
||||||
song->set_disc(disc.left(i).toInt());
|
song->set_disc(disc.left(i).toInt());
|
||||||
} else {
|
} else {
|
||||||
song->set_disc(disc.toInt());
|
song->set_disc(disc.toInt());
|
||||||
|
@ -335,14 +368,18 @@ void TagReader::ReadFile(const QString& filename,
|
||||||
if (fileref->audioProperties()) {
|
if (fileref->audioProperties()) {
|
||||||
song->set_bitrate(fileref->audioProperties()->bitrate());
|
song->set_bitrate(fileref->audioProperties()->bitrate());
|
||||||
song->set_samplerate(fileref->audioProperties()->sampleRate());
|
song->set_samplerate(fileref->audioProperties()->sampleRate());
|
||||||
song->set_length_nanosec(fileref->audioProperties()->length() * kNsecPerSec);
|
song->set_length_nanosec(fileref->audioProperties()->length() *
|
||||||
|
kNsecPerSec);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the filetype if we can
|
// Get the filetype if we can
|
||||||
song->set_type(GuessFileType(fileref.get()));
|
song->set_type(GuessFileType(fileref.get()));
|
||||||
|
|
||||||
// Set integer fields to -1 if they're not valid
|
// Set integer fields to -1 if they're not valid
|
||||||
#define SetDefault(field) if (song->field() <= 0) { song->set_##field(-1); }
|
#define SetDefault(field) \
|
||||||
|
if (song->field() <= 0) { \
|
||||||
|
song->set_##field(-1); \
|
||||||
|
}
|
||||||
SetDefault(track);
|
SetDefault(track);
|
||||||
SetDefault(disc);
|
SetDefault(disc);
|
||||||
SetDefault(bpm);
|
SetDefault(bpm);
|
||||||
|
@ -353,13 +390,13 @@ void TagReader::ReadFile(const QString& filename,
|
||||||
#undef SetDefault
|
#undef SetDefault
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void TagReader::Decode(const TagLib::String& tag, const QTextCodec* codec,
|
void TagReader::Decode(const TagLib::String& tag, const QTextCodec* codec,
|
||||||
std::string* output) {
|
std::string* output) {
|
||||||
QString tmp;
|
QString tmp;
|
||||||
|
|
||||||
if (codec && tag.isLatin1()) { // Never override UTF-8.
|
if (codec && tag.isLatin1()) { // Never override UTF-8.
|
||||||
const std::string fixed = QString::fromUtf8(tag.toCString(true)).toStdString();
|
const std::string fixed =
|
||||||
|
QString::fromUtf8(tag.toCString(true)).toStdString();
|
||||||
tmp = codec->toUnicode(fixed.c_str()).trimmed();
|
tmp = codec->toUnicode(fixed.c_str()).trimmed();
|
||||||
} else {
|
} else {
|
||||||
tmp = TStringToQString(tag).trimmed();
|
tmp = TStringToQString(tag).trimmed();
|
||||||
|
@ -382,8 +419,7 @@ void TagReader::ParseFMPSFrame(const QString& name, const QString& value,
|
||||||
pb::tagreader::SongMetadata* song) const {
|
pb::tagreader::SongMetadata* song) const {
|
||||||
qLog(Debug) << "Parsing FMPSFrame" << name << ", " << value;
|
qLog(Debug) << "Parsing FMPSFrame" << name << ", " << value;
|
||||||
FMPSParser parser;
|
FMPSParser parser;
|
||||||
if (!parser.Parse(value) || parser.is_empty())
|
if (!parser.Parse(value) || parser.is_empty()) return;
|
||||||
return;
|
|
||||||
|
|
||||||
QVariant var;
|
QVariant var;
|
||||||
if (name == "FMPS_Rating") {
|
if (name == "FMPS_Rating") {
|
||||||
|
@ -421,8 +457,8 @@ void TagReader::ParseFMPSFrame(const QString& name, const QString& value,
|
||||||
}
|
}
|
||||||
|
|
||||||
void TagReader::ParseOggTag(const TagLib::Ogg::FieldListMap& map,
|
void TagReader::ParseOggTag(const TagLib::Ogg::FieldListMap& map,
|
||||||
const QTextCodec* codec,
|
const QTextCodec* codec, QString* disc,
|
||||||
QString* disc, QString* compilation,
|
QString* compilation,
|
||||||
pb::tagreader::SongMetadata* song) const {
|
pb::tagreader::SongMetadata* song) const {
|
||||||
if (!map["COMPOSER"].isEmpty())
|
if (!map["COMPOSER"].isEmpty())
|
||||||
Decode(map["COMPOSER"].front(), codec, song->mutable_composer());
|
Decode(map["COMPOSER"].front(), codec, song->mutable_composer());
|
||||||
|
@ -446,43 +482,67 @@ void TagReader::ParseOggTag(const TagLib::Ogg::FieldListMap& map,
|
||||||
if (!map["COMPILATION"].isEmpty())
|
if (!map["COMPILATION"].isEmpty())
|
||||||
*compilation = TStringToQString(map["COMPILATION"].front()).trimmed();
|
*compilation = TStringToQString(map["COMPILATION"].front()).trimmed();
|
||||||
|
|
||||||
if (!map["COVERART"].isEmpty())
|
if (!map["COVERART"].isEmpty()) song->set_art_automatic(kEmbeddedCover);
|
||||||
song->set_art_automatic(kEmbeddedCover);
|
|
||||||
|
|
||||||
if (!map["METADATA_BLOCK_PICTURE"].isEmpty())
|
if (!map["METADATA_BLOCK_PICTURE"].isEmpty())
|
||||||
song->set_art_automatic(kEmbeddedCover);
|
song->set_art_automatic(kEmbeddedCover);
|
||||||
|
|
||||||
if (!map["FMPS_RATING"].isEmpty() && song->rating() <= 0)
|
if (!map["FMPS_RATING"].isEmpty() && song->rating() <= 0)
|
||||||
song->set_rating(TStringToQString( map["FMPS_RATING"].front() ).trimmed().toFloat());
|
song->set_rating(
|
||||||
|
TStringToQString(map["FMPS_RATING"].front()).trimmed().toFloat());
|
||||||
|
|
||||||
if (!map["FMPS_PLAYCOUNT"].isEmpty() && song->playcount() <= 0)
|
if (!map["FMPS_PLAYCOUNT"].isEmpty() && song->playcount() <= 0)
|
||||||
song->set_playcount(TStringToQString( map["FMPS_PLAYCOUNT"].front() ).trimmed().toFloat());
|
song->set_playcount(
|
||||||
|
TStringToQString(map["FMPS_PLAYCOUNT"].front()).trimmed().toFloat());
|
||||||
|
|
||||||
if (!map["FMPS_RATING_AMAROK_SCORE"].isEmpty() && song->score() <= 0)
|
if (!map["FMPS_RATING_AMAROK_SCORE"].isEmpty() && song->score() <= 0)
|
||||||
song->set_score(TStringToQString( map["FMPS_RATING_AMAROK_SCORE"].front() ).trimmed().toFloat() * 100);
|
song->set_score(TStringToQString(map["FMPS_RATING_AMAROK_SCORE"].front())
|
||||||
|
.trimmed()
|
||||||
|
.toFloat() *
|
||||||
|
100);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TagReader::SetVorbisComments(TagLib::Ogg::XiphComment* vorbis_comments,
|
void TagReader::SetVorbisComments(TagLib::Ogg::XiphComment* vorbis_comments,
|
||||||
const pb::tagreader::SongMetadata& song) const {
|
const pb::tagreader::SongMetadata& song)
|
||||||
|
const {
|
||||||
|
|
||||||
vorbis_comments->addField("COMPOSER", StdStringToTaglibString(song.composer()), true);
|
vorbis_comments->addField("COMPOSER",
|
||||||
vorbis_comments->addField("PERFORMER", StdStringToTaglibString(song.performer()), true);
|
StdStringToTaglibString(song.composer()), true);
|
||||||
vorbis_comments->addField("CONTENT GROUP", StdStringToTaglibString(song.grouping()), true);
|
vorbis_comments->addField("PERFORMER",
|
||||||
vorbis_comments->addField("BPM", QStringToTaglibString(song.bpm() <= 0 -1 ? QString() : QString::number(song.bpm())), true);
|
StdStringToTaglibString(song.performer()), true);
|
||||||
vorbis_comments->addField("DISCNUMBER", QStringToTaglibString(song.disc() <= 0 -1 ? QString() : QString::number(song.disc())), true);
|
vorbis_comments->addField("CONTENT GROUP",
|
||||||
vorbis_comments->addField("COMPILATION", StdStringToTaglibString(song.compilation() ? "1" : "0"), true);
|
StdStringToTaglibString(song.grouping()), true);
|
||||||
|
vorbis_comments->addField(
|
||||||
|
"BPM", QStringToTaglibString(
|
||||||
|
song.bpm() <= 0 - 1 ? QString() : QString::number(song.bpm())),
|
||||||
|
true);
|
||||||
|
vorbis_comments->addField(
|
||||||
|
"DISCNUMBER",
|
||||||
|
QStringToTaglibString(
|
||||||
|
song.disc() <= 0 - 1 ? QString() : QString::number(song.disc())),
|
||||||
|
true);
|
||||||
|
vorbis_comments->addField(
|
||||||
|
"COMPILATION", StdStringToTaglibString(song.compilation() ? "1" : "0"),
|
||||||
|
true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TagReader::SetFMPSStatisticsVorbisComments(TagLib::Ogg::XiphComment* vorbis_comments,
|
void TagReader::SetFMPSStatisticsVorbisComments(
|
||||||
|
TagLib::Ogg::XiphComment* vorbis_comments,
|
||||||
const pb::tagreader::SongMetadata& song) const {
|
const pb::tagreader::SongMetadata& song) const {
|
||||||
vorbis_comments->addField("FMPS_PLAYCOUNT", QStringToTaglibString(QString::number(song.playcount())));
|
vorbis_comments->addField(
|
||||||
vorbis_comments->addField("FMPS_RATING_AMAROK_SCORE", QStringToTaglibString(QString::number(song.score() / 100.0)));
|
"FMPS_PLAYCOUNT",
|
||||||
|
QStringToTaglibString(QString::number(song.playcount())));
|
||||||
|
vorbis_comments->addField(
|
||||||
|
"FMPS_RATING_AMAROK_SCORE",
|
||||||
|
QStringToTaglibString(QString::number(song.score() / 100.0)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void TagReader::SetFMPSRatingVorbisComments(TagLib::Ogg::XiphComment* vorbis_comments,
|
void TagReader::SetFMPSRatingVorbisComments(
|
||||||
|
TagLib::Ogg::XiphComment* vorbis_comments,
|
||||||
const pb::tagreader::SongMetadata& song) const {
|
const pb::tagreader::SongMetadata& song) const {
|
||||||
|
|
||||||
vorbis_comments->addField("FMPS_RATING", QStringToTaglibString(QString::number(song.rating())));
|
vorbis_comments->addField(
|
||||||
|
"FMPS_RATING", QStringToTaglibString(QString::number(song.rating())));
|
||||||
}
|
}
|
||||||
|
|
||||||
pb::tagreader::SongMetadata_Type TagReader::GuessFileType(
|
pb::tagreader::SongMetadata_Type TagReader::GuessFileType(
|
||||||
|
@ -523,8 +583,7 @@ pb::tagreader::SongMetadata_Type TagReader::GuessFileType(
|
||||||
|
|
||||||
bool TagReader::SaveFile(const QString& filename,
|
bool TagReader::SaveFile(const QString& filename,
|
||||||
const pb::tagreader::SongMetadata& song) const {
|
const pb::tagreader::SongMetadata& song) const {
|
||||||
if (filename.isNull())
|
if (filename.isNull()) return false;
|
||||||
return false;
|
|
||||||
|
|
||||||
qLog(Debug) << "Saving tags to" << filename;
|
qLog(Debug) << "Saving tags to" << filename;
|
||||||
|
|
||||||
|
@ -541,32 +600,44 @@ bool TagReader::SaveFile(const QString& filename,
|
||||||
fileref->tag()->setYear(song.year());
|
fileref->tag()->setYear(song.year());
|
||||||
fileref->tag()->setTrack(song.track());
|
fileref->tag()->setTrack(song.track());
|
||||||
|
|
||||||
if (TagLib::MPEG::File* file = dynamic_cast<TagLib::MPEG::File*>(fileref->file())) {
|
if (TagLib::MPEG::File* file =
|
||||||
|
dynamic_cast<TagLib::MPEG::File*>(fileref->file())) {
|
||||||
TagLib::ID3v2::Tag* tag = file->ID3v2Tag(true);
|
TagLib::ID3v2::Tag* tag = file->ID3v2Tag(true);
|
||||||
SetTextFrame("TPOS", song.disc() <= 0 -1 ? QString() : QString::number(song.disc()), tag);
|
SetTextFrame(
|
||||||
SetTextFrame("TBPM", song.bpm() <= 0 -1 ? QString() : QString::number(song.bpm()), tag);
|
"TPOS", song.disc() <= 0 - 1 ? QString() : QString::number(song.disc()),
|
||||||
|
tag);
|
||||||
|
SetTextFrame("TBPM",
|
||||||
|
song.bpm() <= 0 - 1 ? QString() : QString::number(song.bpm()),
|
||||||
|
tag);
|
||||||
SetTextFrame("TCOM", song.composer(), tag);
|
SetTextFrame("TCOM", song.composer(), tag);
|
||||||
SetTextFrame("TIT1", song.grouping(), tag);
|
SetTextFrame("TIT1", song.grouping(), tag);
|
||||||
// Skip TPE1 (which is the artist) here because we already set it
|
// Skip TPE1 (which is the artist) here because we already set it
|
||||||
SetTextFrame("TPE2", song.albumartist(), tag);
|
SetTextFrame("TPE2", song.albumartist(), tag);
|
||||||
SetTextFrame("TCMP", std::string(song.compilation() ? "1" : "0"), tag);
|
SetTextFrame("TCMP", std::string(song.compilation() ? "1" : "0"), tag);
|
||||||
} else if (TagLib::FLAC::File* file = dynamic_cast<TagLib::FLAC::File*>(fileref->file())) {
|
} else if (TagLib::FLAC::File* file =
|
||||||
|
dynamic_cast<TagLib::FLAC::File*>(fileref->file())) {
|
||||||
TagLib::Ogg::XiphComment* tag = file->xiphComment();
|
TagLib::Ogg::XiphComment* tag = file->xiphComment();
|
||||||
SetVorbisComments(tag, song);
|
SetVorbisComments(tag, song);
|
||||||
} else if (TagLib::MP4::File* file = dynamic_cast<TagLib::MP4::File*>(fileref->file())) {
|
} else if (TagLib::MP4::File* file =
|
||||||
|
dynamic_cast<TagLib::MP4::File*>(fileref->file())) {
|
||||||
TagLib::MP4::Tag* tag = file->tag();
|
TagLib::MP4::Tag* tag = file->tag();
|
||||||
tag->itemListMap()["disk"] = TagLib::MP4::Item(song.disc() <= 0 -1 ? 0 : song.disc(), 0);
|
tag->itemListMap()["disk"] =
|
||||||
tag->itemListMap()["tmpo"] = TagLib::StringList(song.bpm() <= 0 -1 ? "0" : TagLib::String::number(song.bpm()));
|
TagLib::MP4::Item(song.disc() <= 0 - 1 ? 0 : song.disc(), 0);
|
||||||
|
tag->itemListMap()["tmpo"] = TagLib::StringList(
|
||||||
|
song.bpm() <= 0 - 1 ? "0" : TagLib::String::number(song.bpm()));
|
||||||
tag->itemListMap()["\251wrt"] = TagLib::StringList(song.composer());
|
tag->itemListMap()["\251wrt"] = TagLib::StringList(song.composer());
|
||||||
tag->itemListMap()["\251grp"] = TagLib::StringList(song.grouping());
|
tag->itemListMap()["\251grp"] = TagLib::StringList(song.grouping());
|
||||||
tag->itemListMap()["aART"] = TagLib::StringList(song.albumartist());
|
tag->itemListMap()["aART"] = TagLib::StringList(song.albumartist());
|
||||||
tag->itemListMap()["cpil"] = TagLib::StringList(song.compilation() ? "1" : "0");
|
tag->itemListMap()["cpil"] =
|
||||||
|
TagLib::StringList(song.compilation() ? "1" : "0");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle all the files which have VorbisComments (Ogg, OPUS, ...) in the same way;
|
// Handle all the files which have VorbisComments (Ogg, OPUS, ...) in the same
|
||||||
|
// way;
|
||||||
// apart, so we keep specific behavior for some formats by adding another
|
// apart, so we keep specific behavior for some formats by adding another
|
||||||
// "else if" block above.
|
// "else if" block above.
|
||||||
if (TagLib::Ogg::XiphComment* tag = dynamic_cast<TagLib::Ogg::XiphComment*>(fileref->file()->tag())) {
|
if (TagLib::Ogg::XiphComment* tag =
|
||||||
|
dynamic_cast<TagLib::Ogg::XiphComment*>(fileref->file()->tag())) {
|
||||||
SetVorbisComments(tag, song);
|
SetVorbisComments(tag, song);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -582,10 +653,9 @@ bool TagReader::SaveFile(const QString& filename,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TagReader::SaveSongStatisticsToFile(const QString& filename,
|
bool TagReader::SaveSongStatisticsToFile(
|
||||||
const pb::tagreader::SongMetadata& song) const {
|
const QString& filename, const pb::tagreader::SongMetadata& song) const {
|
||||||
if (filename.isNull())
|
if (filename.isNull()) return false;
|
||||||
return false;
|
|
||||||
|
|
||||||
qLog(Debug) << "Saving song statistics tags to" << filename;
|
qLog(Debug) << "Saving song statistics tags to" << filename;
|
||||||
|
|
||||||
|
@ -594,34 +664,44 @@ bool TagReader::SaveSongStatisticsToFile(const QString& filename,
|
||||||
if (!fileref || fileref->isNull()) // The file probably doesn't exist
|
if (!fileref || fileref->isNull()) // The file probably doesn't exist
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (TagLib::MPEG::File* file = dynamic_cast<TagLib::MPEG::File*>(fileref->file())) {
|
if (TagLib::MPEG::File* file =
|
||||||
|
dynamic_cast<TagLib::MPEG::File*>(fileref->file())) {
|
||||||
TagLib::ID3v2::Tag* tag = file->ID3v2Tag(true);
|
TagLib::ID3v2::Tag* tag = file->ID3v2Tag(true);
|
||||||
|
|
||||||
// Save as FMPS
|
// Save as FMPS
|
||||||
SetUserTextFrame("FMPS_PlayCount", QString::number(song.playcount()), tag);
|
SetUserTextFrame("FMPS_PlayCount", QString::number(song.playcount()), tag);
|
||||||
SetUserTextFrame("FMPS_Rating_Amarok_Score", QString::number(song.score() / 100.0), tag);
|
SetUserTextFrame("FMPS_Rating_Amarok_Score",
|
||||||
|
QString::number(song.score() / 100.0), tag);
|
||||||
|
|
||||||
// Also save as POPM
|
// Also save as POPM
|
||||||
TagLib::ID3v2::PopularimeterFrame* frame = GetPOPMFrameFromTag(tag);
|
TagLib::ID3v2::PopularimeterFrame* frame = GetPOPMFrameFromTag(tag);
|
||||||
frame->setCounter(song.playcount());
|
frame->setCounter(song.playcount());
|
||||||
|
|
||||||
} else if (TagLib::FLAC::File* file = dynamic_cast<TagLib::FLAC::File*>(fileref->file())) {
|
} else if (TagLib::FLAC::File* file =
|
||||||
|
dynamic_cast<TagLib::FLAC::File*>(fileref->file())) {
|
||||||
TagLib::Ogg::XiphComment* vorbis_comments = file->xiphComment(true);
|
TagLib::Ogg::XiphComment* vorbis_comments = file->xiphComment(true);
|
||||||
SetFMPSStatisticsVorbisComments(vorbis_comments, song);
|
SetFMPSStatisticsVorbisComments(vorbis_comments, song);
|
||||||
} else if (TagLib::Ogg::XiphComment* tag = dynamic_cast<TagLib::Ogg::XiphComment*>(fileref->file()->tag())) {
|
} else if (TagLib::Ogg::XiphComment* tag =
|
||||||
|
dynamic_cast<TagLib::Ogg::XiphComment*>(
|
||||||
|
fileref->file()->tag())) {
|
||||||
SetFMPSStatisticsVorbisComments(tag, song);
|
SetFMPSStatisticsVorbisComments(tag, song);
|
||||||
}
|
}
|
||||||
#ifdef TAGLIB_WITH_ASF
|
#ifdef TAGLIB_WITH_ASF
|
||||||
else if (TagLib::ASF::File* file = dynamic_cast<TagLib::ASF::File*>(fileref->file())) {
|
else if (TagLib::ASF::File* file =
|
||||||
|
dynamic_cast<TagLib::ASF::File*>(fileref->file())) {
|
||||||
TagLib::ASF::Tag* tag = file->tag();
|
TagLib::ASF::Tag* tag = file->tag();
|
||||||
tag->addAttribute("FMPS/Playcount", NumberToASFAttribute(song.playcount()));
|
tag->addAttribute("FMPS/Playcount", NumberToASFAttribute(song.playcount()));
|
||||||
tag->addAttribute("FMPS/Rating_Amarok_Score", NumberToASFAttribute(song.score() / 100.0));
|
tag->addAttribute("FMPS/Rating_Amarok_Score",
|
||||||
|
NumberToASFAttribute(song.score() / 100.0));
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
else if (TagLib::MP4::File* file = dynamic_cast<TagLib::MP4::File*>(fileref->file())) {
|
else if (TagLib::MP4::File* file =
|
||||||
|
dynamic_cast<TagLib::MP4::File*>(fileref->file())) {
|
||||||
TagLib::MP4::Tag* tag = file->tag();
|
TagLib::MP4::Tag* tag = file->tag();
|
||||||
tag->itemListMap()[kMP4_FMPS_Score_ID] = TagLib::StringList(QStringToTaglibString(QString::number(song.score() / 100.0)));
|
tag->itemListMap()[kMP4_FMPS_Score_ID] = TagLib::StringList(
|
||||||
tag->itemListMap()[kMP4_FMPS_Playcount_ID] = TagLib::StringList(TagLib::String::number(song.playcount()));
|
QStringToTaglibString(QString::number(song.score() / 100.0)));
|
||||||
|
tag->itemListMap()[kMP4_FMPS_Playcount_ID] =
|
||||||
|
TagLib::StringList(TagLib::String::number(song.playcount()));
|
||||||
} else {
|
} else {
|
||||||
// Nothing to save: stop now
|
// Nothing to save: stop now
|
||||||
return true;
|
return true;
|
||||||
|
@ -638,10 +718,9 @@ bool TagReader::SaveSongStatisticsToFile(const QString& filename,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TagReader::SaveSongRatingToFile(const QString& filename,
|
bool TagReader::SaveSongRatingToFile(
|
||||||
const pb::tagreader::SongMetadata& song) const {
|
const QString& filename, const pb::tagreader::SongMetadata& song) const {
|
||||||
if (filename.isNull())
|
if (filename.isNull()) return false;
|
||||||
return false;
|
|
||||||
|
|
||||||
qLog(Debug) << "Saving song rating tags to" << filename;
|
qLog(Debug) << "Saving song rating tags to" << filename;
|
||||||
|
|
||||||
|
@ -650,7 +729,8 @@ bool TagReader::SaveSongRatingToFile(const QString& filename,
|
||||||
if (!fileref || fileref->isNull()) // The file probably doesn't exist
|
if (!fileref || fileref->isNull()) // The file probably doesn't exist
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (TagLib::MPEG::File* file = dynamic_cast<TagLib::MPEG::File*>(fileref->file())) {
|
if (TagLib::MPEG::File* file =
|
||||||
|
dynamic_cast<TagLib::MPEG::File*>(fileref->file())) {
|
||||||
TagLib::ID3v2::Tag* tag = file->ID3v2Tag(true);
|
TagLib::ID3v2::Tag* tag = file->ID3v2Tag(true);
|
||||||
|
|
||||||
// Save as FMPS
|
// Save as FMPS
|
||||||
|
@ -660,21 +740,27 @@ bool TagReader::SaveSongRatingToFile(const QString& filename,
|
||||||
TagLib::ID3v2::PopularimeterFrame* frame = GetPOPMFrameFromTag(tag);
|
TagLib::ID3v2::PopularimeterFrame* frame = GetPOPMFrameFromTag(tag);
|
||||||
frame->setRating(ConvertToPOPMRating(song.rating()));
|
frame->setRating(ConvertToPOPMRating(song.rating()));
|
||||||
|
|
||||||
} else if (TagLib::FLAC::File* file = dynamic_cast<TagLib::FLAC::File*>(fileref->file())) {
|
} else if (TagLib::FLAC::File* file =
|
||||||
|
dynamic_cast<TagLib::FLAC::File*>(fileref->file())) {
|
||||||
TagLib::Ogg::XiphComment* vorbis_comments = file->xiphComment(true);
|
TagLib::Ogg::XiphComment* vorbis_comments = file->xiphComment(true);
|
||||||
SetFMPSRatingVorbisComments(vorbis_comments, song);
|
SetFMPSRatingVorbisComments(vorbis_comments, song);
|
||||||
} else if (TagLib::Ogg::XiphComment* tag = dynamic_cast<TagLib::Ogg::XiphComment*>(fileref->file()->tag())) {
|
} else if (TagLib::Ogg::XiphComment* tag =
|
||||||
|
dynamic_cast<TagLib::Ogg::XiphComment*>(
|
||||||
|
fileref->file()->tag())) {
|
||||||
SetFMPSRatingVorbisComments(tag, song);
|
SetFMPSRatingVorbisComments(tag, song);
|
||||||
}
|
}
|
||||||
#ifdef TAGLIB_WITH_ASF
|
#ifdef TAGLIB_WITH_ASF
|
||||||
else if (TagLib::ASF::File* file = dynamic_cast<TagLib::ASF::File*>(fileref->file())) {
|
else if (TagLib::ASF::File* file =
|
||||||
|
dynamic_cast<TagLib::ASF::File*>(fileref->file())) {
|
||||||
TagLib::ASF::Tag* tag = file->tag();
|
TagLib::ASF::Tag* tag = file->tag();
|
||||||
tag->addAttribute("FMPS/Rating", NumberToASFAttribute(song.rating()));
|
tag->addAttribute("FMPS/Rating", NumberToASFAttribute(song.rating()));
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
else if (TagLib::MP4::File* file = dynamic_cast<TagLib::MP4::File*>(fileref->file())) {
|
else if (TagLib::MP4::File* file =
|
||||||
|
dynamic_cast<TagLib::MP4::File*>(fileref->file())) {
|
||||||
TagLib::MP4::Tag* tag = file->tag();
|
TagLib::MP4::Tag* tag = file->tag();
|
||||||
tag->itemListMap()[kMP4_FMPS_Rating_ID] = TagLib::StringList(QStringToTaglibString(QString::number(song.rating())));
|
tag->itemListMap()[kMP4_FMPS_Rating_ID] = TagLib::StringList(
|
||||||
|
QStringToTaglibString(QString::number(song.rating())));
|
||||||
} else {
|
} else {
|
||||||
// Nothing to save: stop now
|
// Nothing to save: stop now
|
||||||
return true;
|
return true;
|
||||||
|
@ -691,7 +777,8 @@ bool TagReader::SaveSongRatingToFile(const QString& filename,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TagReader::SetUserTextFrame(const QString& description, const QString& value,
|
void TagReader::SetUserTextFrame(const QString& description,
|
||||||
|
const QString& value,
|
||||||
TagLib::ID3v2::Tag* tag) const {
|
TagLib::ID3v2::Tag* tag) const {
|
||||||
const QByteArray descr_utf8(description.toUtf8());
|
const QByteArray descr_utf8(description.toUtf8());
|
||||||
const QByteArray value_utf8(value.toUtf8());
|
const QByteArray value_utf8(value.toUtf8());
|
||||||
|
@ -752,8 +839,7 @@ bool TagReader::IsMediaFile(const QString& filename) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray TagReader::LoadEmbeddedArt(const QString& filename) const {
|
QByteArray TagReader::LoadEmbeddedArt(const QString& filename) const {
|
||||||
if (filename.isEmpty())
|
if (filename.isEmpty()) return QByteArray();
|
||||||
return QByteArray();
|
|
||||||
|
|
||||||
qLog(Debug) << "Loading art from" << filename;
|
qLog(Debug) << "Loading art from" << filename;
|
||||||
|
|
||||||
|
@ -763,20 +849,20 @@ QByteArray TagReader::LoadEmbeddedArt(const QString& filename) const {
|
||||||
TagLib::FileRef ref(QFile::encodeName(filename).constData());
|
TagLib::FileRef ref(QFile::encodeName(filename).constData());
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (ref.isNull() || !ref.file())
|
if (ref.isNull() || !ref.file()) return QByteArray();
|
||||||
return QByteArray();
|
|
||||||
|
|
||||||
// MP3
|
// MP3
|
||||||
TagLib::MPEG::File* file = dynamic_cast<TagLib::MPEG::File*>(ref.file());
|
TagLib::MPEG::File* file = dynamic_cast<TagLib::MPEG::File*>(ref.file());
|
||||||
if (file && file->ID3v2Tag()) {
|
if (file && file->ID3v2Tag()) {
|
||||||
TagLib::ID3v2::FrameList apic_frames = file->ID3v2Tag()->frameListMap()["APIC"];
|
TagLib::ID3v2::FrameList apic_frames =
|
||||||
if (apic_frames.isEmpty())
|
file->ID3v2Tag()->frameListMap()["APIC"];
|
||||||
return QByteArray();
|
if (apic_frames.isEmpty()) return QByteArray();
|
||||||
|
|
||||||
TagLib::ID3v2::AttachedPictureFrame* pic =
|
TagLib::ID3v2::AttachedPictureFrame* pic =
|
||||||
static_cast<TagLib::ID3v2::AttachedPictureFrame*>(apic_frames.front());
|
static_cast<TagLib::ID3v2::AttachedPictureFrame*>(apic_frames.front());
|
||||||
|
|
||||||
return QByteArray((const char*) pic->picture().data(), pic->picture().size());
|
return QByteArray((const char*)pic->picture().data(),
|
||||||
|
pic->picture().size());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ogg vorbis/speex
|
// Ogg vorbis/speex
|
||||||
|
@ -786,12 +872,14 @@ QByteArray TagReader::LoadEmbeddedArt(const QString& filename) const {
|
||||||
if (xiph_comment) {
|
if (xiph_comment) {
|
||||||
TagLib::Ogg::FieldListMap map = xiph_comment->fieldListMap();
|
TagLib::Ogg::FieldListMap map = xiph_comment->fieldListMap();
|
||||||
|
|
||||||
// Other than the below mentioned non-standard COVERART, METADATA_BLOCK_PICTURE
|
// Other than the below mentioned non-standard COVERART,
|
||||||
|
// METADATA_BLOCK_PICTURE
|
||||||
// is the proposed tag for cover pictures.
|
// is the proposed tag for cover pictures.
|
||||||
// (see http://wiki.xiph.org/VorbisComment#METADATA_BLOCK_PICTURE)
|
// (see http://wiki.xiph.org/VorbisComment#METADATA_BLOCK_PICTURE)
|
||||||
if (map.contains("METADATA_BLOCK_PICTURE")) {
|
if (map.contains("METADATA_BLOCK_PICTURE")) {
|
||||||
TagLib::StringList pict_list = map["METADATA_BLOCK_PICTURE"];
|
TagLib::StringList pict_list = map["METADATA_BLOCK_PICTURE"];
|
||||||
for(std::list<TagLib::String>::iterator it = pict_list.begin(); it != pict_list.end(); ++it) {
|
for (std::list<TagLib::String>::iterator it = pict_list.begin();
|
||||||
|
it != pict_list.end(); ++it) {
|
||||||
QByteArray data(QByteArray::fromBase64(it->toCString()));
|
QByteArray data(QByteArray::fromBase64(it->toCString()));
|
||||||
TagLib::ByteVector tdata(data.data(), data.size());
|
TagLib::ByteVector tdata(data.data(), data.size());
|
||||||
TagLib::FLAC::Picture p(tdata);
|
TagLib::FLAC::Picture p(tdata);
|
||||||
|
@ -799,7 +887,8 @@ QByteArray TagReader::LoadEmbeddedArt(const QString& filename) const {
|
||||||
return QByteArray(p.data().data(), p.data().size());
|
return QByteArray(p.data().data(), p.data().size());
|
||||||
}
|
}
|
||||||
// If there was no specific front cover, just take the first picture
|
// If there was no specific front cover, just take the first picture
|
||||||
QByteArray data(QByteArray::fromBase64(map["METADATA_BLOCK_PICTURE"].front().toCString()));
|
QByteArray data(QByteArray::fromBase64(
|
||||||
|
map["METADATA_BLOCK_PICTURE"].front().toCString()));
|
||||||
TagLib::ByteVector tdata(data.data(), data.size());
|
TagLib::ByteVector tdata(data.data(), data.size());
|
||||||
TagLib::FLAC::Picture p(tdata);
|
TagLib::FLAC::Picture p(tdata);
|
||||||
return QByteArray(p.data().data(), p.data().size());
|
return QByteArray(p.data().data(), p.data().size());
|
||||||
|
@ -807,8 +896,7 @@ QByteArray TagReader::LoadEmbeddedArt(const QString& filename) const {
|
||||||
|
|
||||||
// Ogg lacks a definitive standard for embedding cover art, but it seems
|
// Ogg lacks a definitive standard for embedding cover art, but it seems
|
||||||
// b64 encoding a field called COVERART is the general convention
|
// b64 encoding a field called COVERART is the general convention
|
||||||
if (!map.contains("COVERART"))
|
if (!map.contains("COVERART")) return QByteArray();
|
||||||
return QByteArray();
|
|
||||||
|
|
||||||
return QByteArray::fromBase64(map["COVERART"].toString().toCString());
|
return QByteArray::fromBase64(map["COVERART"].toString().toCString());
|
||||||
}
|
}
|
||||||
|
@ -850,62 +938,45 @@ QByteArray TagReader::LoadEmbeddedArt(const QString& filename) const {
|
||||||
return QByteArray();
|
return QByteArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#ifdef HAVE_GOOGLE_DRIVE
|
#ifdef HAVE_GOOGLE_DRIVE
|
||||||
bool TagReader::ReadCloudFile(const QUrl& download_url,
|
bool TagReader::ReadCloudFile(const QUrl& download_url, const QString& title,
|
||||||
const QString& title,
|
int size, const QString& mime_type,
|
||||||
int size,
|
|
||||||
const QString& mime_type,
|
|
||||||
const QString& authorisation_header,
|
const QString& authorisation_header,
|
||||||
pb::tagreader::SongMetadata* song) const {
|
pb::tagreader::SongMetadata* song) const {
|
||||||
qLog(Debug) << "Loading tags from" << title;
|
qLog(Debug) << "Loading tags from" << title;
|
||||||
|
|
||||||
CloudStream* stream = new CloudStream(
|
CloudStream* stream = new CloudStream(download_url, title, size,
|
||||||
download_url, title, size, authorisation_header, network_);
|
authorisation_header, network_);
|
||||||
stream->Precache();
|
stream->Precache();
|
||||||
std::unique_ptr<TagLib::File> tag;
|
std::unique_ptr<TagLib::File> tag;
|
||||||
if (mime_type == "audio/mpeg" && title.endsWith(".mp3")) {
|
if (mime_type == "audio/mpeg" && title.endsWith(".mp3")) {
|
||||||
tag.reset(new TagLib::MPEG::File(
|
tag.reset(new TagLib::MPEG::File(stream, // Takes ownership.
|
||||||
stream, // Takes ownership.
|
|
||||||
TagLib::ID3v2::FrameFactory::instance(),
|
TagLib::ID3v2::FrameFactory::instance(),
|
||||||
TagLib::AudioProperties::Accurate));
|
TagLib::AudioProperties::Accurate));
|
||||||
} else if (mime_type == "audio/mp4" ||
|
} else if (mime_type == "audio/mp4" ||
|
||||||
(mime_type == "audio/mpeg" && title.endsWith(".m4a"))) {
|
(mime_type == "audio/mpeg" && title.endsWith(".m4a"))) {
|
||||||
tag.reset(new TagLib::MP4::File(
|
tag.reset(
|
||||||
stream,
|
new TagLib::MP4::File(stream, true, TagLib::AudioProperties::Accurate));
|
||||||
true,
|
|
||||||
TagLib::AudioProperties::Accurate));
|
|
||||||
}
|
}
|
||||||
#ifdef TAGLIB_HAS_OPUS
|
#ifdef TAGLIB_HAS_OPUS
|
||||||
else if ((mime_type == "application/opus" ||
|
else if ((mime_type == "application/opus" || mime_type == "audio/opus" ||
|
||||||
mime_type == "audio/opus" ||
|
mime_type == "application/ogg" || mime_type == "audio/ogg") &&
|
||||||
mime_type == "application/ogg" ||
|
title.endsWith(".opus")) {
|
||||||
mime_type == "audio/ogg") && title.endsWith(".opus")) {
|
tag.reset(new TagLib::Ogg::Opus::File(stream, true,
|
||||||
tag.reset(new TagLib::Ogg::Opus::File(
|
|
||||||
stream,
|
|
||||||
true,
|
|
||||||
TagLib::AudioProperties::Accurate));
|
TagLib::AudioProperties::Accurate));
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
else if (mime_type == "application/ogg" ||
|
else if (mime_type == "application/ogg" || mime_type == "audio/ogg") {
|
||||||
mime_type == "audio/ogg") {
|
tag.reset(new TagLib::Ogg::Vorbis::File(stream, true,
|
||||||
tag.reset(new TagLib::Ogg::Vorbis::File(
|
|
||||||
stream,
|
|
||||||
true,
|
|
||||||
TagLib::AudioProperties::Accurate));
|
TagLib::AudioProperties::Accurate));
|
||||||
} else if (mime_type == "application/x-flac" ||
|
} else if (mime_type == "application/x-flac" || mime_type == "audio/flac" ||
|
||||||
mime_type == "audio/flac" ||
|
|
||||||
mime_type == "audio/x-flac") {
|
mime_type == "audio/x-flac") {
|
||||||
tag.reset(new TagLib::FLAC::File(
|
tag.reset(new TagLib::FLAC::File(stream,
|
||||||
stream,
|
|
||||||
TagLib::ID3v2::FrameFactory::instance(),
|
TagLib::ID3v2::FrameFactory::instance(),
|
||||||
true,
|
true, TagLib::AudioProperties::Accurate));
|
||||||
TagLib::AudioProperties::Accurate));
|
|
||||||
} else if (mime_type == "audio/x-ms-wma") {
|
} else if (mime_type == "audio/x-ms-wma") {
|
||||||
tag.reset(new TagLib::ASF::File(
|
tag.reset(
|
||||||
stream,
|
new TagLib::ASF::File(stream, true, TagLib::AudioProperties::Accurate));
|
||||||
true,
|
|
||||||
TagLib::AudioProperties::Accurate));
|
|
||||||
} else {
|
} else {
|
||||||
qLog(Debug) << "Unknown mime type for tagging:" << mime_type;
|
qLog(Debug) << "Unknown mime type for tagging:" << mime_type;
|
||||||
return false;
|
return false;
|
||||||
|
@ -914,8 +985,7 @@ bool TagReader::ReadCloudFile(const QUrl& download_url,
|
||||||
if (stream->num_requests() > 2) {
|
if (stream->num_requests() > 2) {
|
||||||
// Warn if pre-caching failed.
|
// Warn if pre-caching failed.
|
||||||
qLog(Warning) << "Total requests for file:" << title
|
qLog(Warning) << "Total requests for file:" << title
|
||||||
<< stream->num_requests()
|
<< stream->num_requests() << stream->cached_bytes();
|
||||||
<< stream->cached_bytes();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tag->tag() && !tag->tag()->isEmpty()) {
|
if (tag->tag() && !tag->tag()->isEmpty()) {
|
||||||
|
@ -943,12 +1013,14 @@ bool TagReader::ReadCloudFile(const QUrl& download_url,
|
||||||
}
|
}
|
||||||
#endif // HAVE_GOOGLE_DRIVE
|
#endif // HAVE_GOOGLE_DRIVE
|
||||||
|
|
||||||
TagLib::ID3v2::PopularimeterFrame* TagReader::GetPOPMFrameFromTag(TagLib::ID3v2::Tag* tag) {
|
TagLib::ID3v2::PopularimeterFrame* TagReader::GetPOPMFrameFromTag(
|
||||||
|
TagLib::ID3v2::Tag* tag) {
|
||||||
TagLib::ID3v2::PopularimeterFrame* frame = nullptr;
|
TagLib::ID3v2::PopularimeterFrame* frame = nullptr;
|
||||||
|
|
||||||
const TagLib::ID3v2::FrameListMap& map = tag->frameListMap();
|
const TagLib::ID3v2::FrameListMap& map = tag->frameListMap();
|
||||||
if (!map["POPM"].isEmpty()) {
|
if (!map["POPM"].isEmpty()) {
|
||||||
frame = dynamic_cast<TagLib::ID3v2::PopularimeterFrame*>(map["POPM"].front());
|
frame =
|
||||||
|
dynamic_cast<TagLib::ID3v2::PopularimeterFrame*>(map["POPM"].front());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!frame) {
|
if (!frame) {
|
||||||
|
|
|
@ -30,7 +30,6 @@ class QString;
|
||||||
class QTextCodec;
|
class QTextCodec;
|
||||||
class QUrl;
|
class QUrl;
|
||||||
|
|
||||||
|
|
||||||
namespace TagLib {
|
namespace TagLib {
|
||||||
class FileRef;
|
class FileRef;
|
||||||
class String;
|
class String;
|
||||||
|
@ -52,23 +51,24 @@ class TagReader {
|
||||||
public:
|
public:
|
||||||
TagReader();
|
TagReader();
|
||||||
|
|
||||||
void ReadFile(const QString& filename, pb::tagreader::SongMetadata* song) const;
|
void ReadFile(const QString& filename,
|
||||||
bool SaveFile(const QString& filename, const pb::tagreader::SongMetadata& song) const;
|
pb::tagreader::SongMetadata* song) const;
|
||||||
|
bool SaveFile(const QString& filename,
|
||||||
|
const pb::tagreader::SongMetadata& song) const;
|
||||||
// Returns false if something went wrong; returns true otherwise (might
|
// Returns false if something went wrong; returns true otherwise (might
|
||||||
// returns true if the file exists but nothing has been written inside because
|
// returns true if the file exists but nothing has been written inside because
|
||||||
// statistics tag format is not supported for this kind of file)
|
// statistics tag format is not supported for this kind of file)
|
||||||
bool SaveSongStatisticsToFile(const QString& filename, const pb::tagreader::SongMetadata& song) const;
|
bool SaveSongStatisticsToFile(const QString& filename,
|
||||||
bool SaveSongRatingToFile(const QString& filename, const pb::tagreader::SongMetadata& song) const;
|
const pb::tagreader::SongMetadata& song) const;
|
||||||
|
bool SaveSongRatingToFile(const QString& filename,
|
||||||
|
const pb::tagreader::SongMetadata& song) const;
|
||||||
|
|
||||||
bool IsMediaFile(const QString& filename) const;
|
bool IsMediaFile(const QString& filename) const;
|
||||||
QByteArray LoadEmbeddedArt(const QString& filename) const;
|
QByteArray LoadEmbeddedArt(const QString& filename) const;
|
||||||
|
|
||||||
#ifdef HAVE_GOOGLE_DRIVE
|
#ifdef HAVE_GOOGLE_DRIVE
|
||||||
bool ReadCloudFile(const QUrl& download_url,
|
bool ReadCloudFile(const QUrl& download_url, const QString& title, int size,
|
||||||
const QString& title,
|
const QString& mime_type, const QString& access_token,
|
||||||
int size,
|
|
||||||
const QString& mime_type,
|
|
||||||
const QString& access_token,
|
|
||||||
pb::tagreader::SongMetadata* song) const;
|
pb::tagreader::SongMetadata* song) const;
|
||||||
#endif // HAVE_GOOGLE_DRIVE
|
#endif // HAVE_GOOGLE_DRIVE
|
||||||
|
|
||||||
|
@ -80,21 +80,24 @@ class TagReader {
|
||||||
void ParseFMPSFrame(const QString& name, const QString& value,
|
void ParseFMPSFrame(const QString& name, const QString& value,
|
||||||
pb::tagreader::SongMetadata* song) const;
|
pb::tagreader::SongMetadata* song) const;
|
||||||
void ParseOggTag(const TagLib::Ogg::FieldListMap& map,
|
void ParseOggTag(const TagLib::Ogg::FieldListMap& map,
|
||||||
const QTextCodec* codec,
|
const QTextCodec* codec, QString* disc, QString* compilation,
|
||||||
QString* disc, QString* compilation,
|
|
||||||
pb::tagreader::SongMetadata* song) const;
|
pb::tagreader::SongMetadata* song) const;
|
||||||
void SetVorbisComments(TagLib::Ogg::XiphComment* vorbis_comments,
|
void SetVorbisComments(TagLib::Ogg::XiphComment* vorbis_comments,
|
||||||
const pb::tagreader::SongMetadata& song) const;
|
const pb::tagreader::SongMetadata& song) const;
|
||||||
void SetFMPSStatisticsVorbisComments(TagLib::Ogg::XiphComment* vorbis_comments,
|
void SetFMPSStatisticsVorbisComments(
|
||||||
|
TagLib::Ogg::XiphComment* vorbis_comments,
|
||||||
const pb::tagreader::SongMetadata& song) const;
|
const pb::tagreader::SongMetadata& song) const;
|
||||||
void SetFMPSRatingVorbisComments(TagLib::Ogg::XiphComment* vorbis_comments,
|
void SetFMPSRatingVorbisComments(TagLib::Ogg::XiphComment* vorbis_comments,
|
||||||
const pb::tagreader::SongMetadata& song) const;
|
const pb::tagreader::SongMetadata& song)
|
||||||
|
const;
|
||||||
|
|
||||||
pb::tagreader::SongMetadata_Type GuessFileType(TagLib::FileRef* fileref) const;
|
pb::tagreader::SongMetadata_Type GuessFileType(TagLib::FileRef* fileref)
|
||||||
|
const;
|
||||||
|
|
||||||
void SetUserTextFrame(const QString& description, const QString& value,
|
void SetUserTextFrame(const QString& description, const QString& value,
|
||||||
TagLib::ID3v2::Tag* tag) const;
|
TagLib::ID3v2::Tag* tag) const;
|
||||||
void SetUserTextFrame(const std::string& description, const std::string& value,
|
void SetUserTextFrame(const std::string& description,
|
||||||
|
const std::string& value,
|
||||||
TagLib::ID3v2::Tag* tag) const;
|
TagLib::ID3v2::Tag* tag) const;
|
||||||
|
|
||||||
void SetTextFrame(const char* id, const QString& value,
|
void SetTextFrame(const char* id, const QString& value,
|
||||||
|
@ -106,11 +109,13 @@ private:
|
||||||
static const char* kMP4_FMPS_Rating_ID;
|
static const char* kMP4_FMPS_Rating_ID;
|
||||||
static const char* kMP4_FMPS_Playcount_ID;
|
static const char* kMP4_FMPS_Playcount_ID;
|
||||||
static const char* kMP4_FMPS_Score_ID;
|
static const char* kMP4_FMPS_Score_ID;
|
||||||
// Returns a float in [0.0..1.0] corresponding to the rating range we use in Clementine
|
// Returns a float in [0.0..1.0] corresponding to the rating range we use in
|
||||||
|
// Clementine
|
||||||
static float ConvertPOPMRating(const int POPM_rating);
|
static float ConvertPOPMRating(const int POPM_rating);
|
||||||
// Reciprocal
|
// Reciprocal
|
||||||
static int ConvertToPOPMRating(const float rating);
|
static int ConvertToPOPMRating(const float rating);
|
||||||
static TagLib::ID3v2::PopularimeterFrame* GetPOPMFrameFromTag(TagLib::ID3v2::Tag* tag);
|
static TagLib::ID3v2::PopularimeterFrame* GetPOPMFrameFromTag(
|
||||||
|
TagLib::ID3v2::Tag* tag);
|
||||||
|
|
||||||
FileRefFactory* factory_;
|
FileRefFactory* factory_;
|
||||||
QNetworkAccessManager* network_;
|
QNetworkAccessManager* network_;
|
||||||
|
|
|
@ -3,9 +3,7 @@
|
||||||
#include "engines/enginebase.h"
|
#include "engines/enginebase.h"
|
||||||
|
|
||||||
AnalyzerBase::AnalyzerBase(QWidget* parent)
|
AnalyzerBase::AnalyzerBase(QWidget* parent)
|
||||||
: QGLWidget(parent),
|
: QGLWidget(parent), engine_(nullptr) {}
|
||||||
engine_(nullptr) {
|
|
||||||
}
|
|
||||||
|
|
||||||
void AnalyzerBase::set_engine(Engine::Base* engine) {
|
void AnalyzerBase::set_engine(Engine::Base* engine) {
|
||||||
disconnect(engine_);
|
disconnect(engine_);
|
||||||
|
|
|
@ -27,48 +27,45 @@
|
||||||
#include "engines/enginebase.h"
|
#include "engines/enginebase.h"
|
||||||
|
|
||||||
// INSTRUCTIONS Base2D
|
// INSTRUCTIONS Base2D
|
||||||
// 1. do anything that depends on height() in init(), Base2D will call it before you are shown
|
// 1. do anything that depends on height() in init(), Base2D will call it before
|
||||||
|
// you are shown
|
||||||
// 2. otherwise you can use the constructor to initialise things
|
// 2. otherwise you can use the constructor to initialise things
|
||||||
// 3. reimplement analyze(), and paint to canvas(), Base2D will update the widget when you return control to it
|
// 3. reimplement analyze(), and paint to canvas(), Base2D will update the
|
||||||
|
// widget when you return control to it
|
||||||
// 4. if you want to manipulate the scope, reimplement transform()
|
// 4. if you want to manipulate the scope, reimplement transform()
|
||||||
// 5. for convenience <vector> <qpixmap.h> <qwdiget.h> are pre-included
|
// 5. for convenience <vector> <qpixmap.h> <qwdiget.h> are pre-included
|
||||||
// TODO make an INSTRUCTIONS file
|
// TODO make an INSTRUCTIONS file
|
||||||
// can't mod scope in analyze you have to use transform
|
// can't mod scope in analyze you have to use transform
|
||||||
|
|
||||||
|
|
||||||
// TODO for 2D use setErasePixmap Qt function insetead of m_background
|
// TODO for 2D use setErasePixmap Qt function insetead of m_background
|
||||||
|
|
||||||
// make the linker happy only for gcc < 4.0
|
// make the linker happy only for gcc < 4.0
|
||||||
#if !( __GNUC__ > 4 || ( __GNUC__ == 4 && __GNUC_MINOR__ >= 0 ) ) && !defined(Q_OS_WIN32)
|
#if !(__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 0)) && \
|
||||||
|
!defined(Q_OS_WIN32)
|
||||||
template class Analyzer::Base<QWidget>;
|
template class Analyzer::Base<QWidget>;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
Analyzer::Base::Base(QWidget* parent, uint scopeSize)
|
Analyzer::Base::Base(QWidget* parent, uint scopeSize)
|
||||||
: QWidget( parent )
|
: QWidget(parent),
|
||||||
, m_timeout( 40 ) // msec
|
m_timeout(40) // msec
|
||||||
, m_fht( new FHT(scopeSize) )
|
,
|
||||||
, m_engine(nullptr)
|
m_fht(new FHT(scopeSize)),
|
||||||
, m_lastScope(512)
|
m_engine(nullptr),
|
||||||
, new_frame_(false)
|
m_lastScope(512),
|
||||||
, is_playing_(false)
|
new_frame_(false),
|
||||||
{
|
is_playing_(false) {}
|
||||||
}
|
|
||||||
|
|
||||||
void Analyzer::Base::hideEvent(QHideEvent *) {
|
void Analyzer::Base::hideEvent(QHideEvent*) { m_timer.stop(); }
|
||||||
m_timer.stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Analyzer::Base::showEvent(QShowEvent *) {
|
void Analyzer::Base::showEvent(QShowEvent*) { m_timer.start(timeout(), this); }
|
||||||
m_timer.start(timeout(), this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Analyzer::Base::transform(Scope& scope) // virtual
|
void Analyzer::Base::transform(Scope& scope) // virtual
|
||||||
{
|
{
|
||||||
// this is a standard transformation that should give
|
// this is a standard transformation that should give
|
||||||
// an FFT scope that has bands for pretty analyzers
|
// an FFT scope that has bands for pretty analyzers
|
||||||
|
|
||||||
//NOTE resizing here is redundant as FHT routines only calculate FHT::size() values
|
// NOTE resizing here is redundant as FHT routines only calculate FHT::size()
|
||||||
|
// values
|
||||||
// scope.resize( m_fht->size() );
|
// scope.resize( m_fht->size() );
|
||||||
|
|
||||||
float* front = static_cast<float*>(&scope.front());
|
float* front = static_cast<float*>(&scope.front());
|
||||||
|
@ -82,22 +79,20 @@ void Analyzer::Base::transform( Scope &scope ) //virtual
|
||||||
delete[] f;
|
delete[] f;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Analyzer::Base::paintEvent(QPaintEvent * e)
|
void Analyzer::Base::paintEvent(QPaintEvent* e) {
|
||||||
{
|
|
||||||
QPainter p(this);
|
QPainter p(this);
|
||||||
p.fillRect(e->rect(), palette().color(QPalette::Window));
|
p.fillRect(e->rect(), palette().color(QPalette::Window));
|
||||||
|
|
||||||
switch( m_engine->state() )
|
switch (m_engine->state()) {
|
||||||
{
|
case Engine::Playing: {
|
||||||
case Engine::Playing:
|
|
||||||
{
|
|
||||||
const Engine::Scope& thescope = m_engine->scope();
|
const Engine::Scope& thescope = m_engine->scope();
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
|
||||||
// convert to mono here - our built in analyzers need mono, but we the engines provide interleaved pcm
|
// convert to mono here - our built in analyzers need mono, but we the
|
||||||
for( uint x = 0; (int)x < m_fht->size(); ++x )
|
// engines provide interleaved pcm
|
||||||
{
|
for (uint x = 0; (int)x < m_fht->size(); ++x) {
|
||||||
m_lastScope[x] = double(thescope[i] + thescope[i+1]) / (2*(1<<15));
|
m_lastScope[x] =
|
||||||
|
double(thescope[i] + thescope[i + 1]) / (2 * (1 << 15));
|
||||||
i += 2;
|
i += 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,12 +114,10 @@ void Analyzer::Base::paintEvent(QPaintEvent * e)
|
||||||
demo(p);
|
demo(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
new_frame_ = false;
|
new_frame_ = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Analyzer::Base::resizeExponent( int exp )
|
int Analyzer::Base::resizeExponent(int exp) {
|
||||||
{
|
|
||||||
if (exp < 3)
|
if (exp < 3)
|
||||||
exp = 3;
|
exp = 3;
|
||||||
else if (exp > 9)
|
else if (exp > 9)
|
||||||
|
@ -137,8 +130,7 @@ int Analyzer::Base::resizeExponent( int exp )
|
||||||
return exp;
|
return exp;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Analyzer::Base::resizeForBands( int bands )
|
int Analyzer::Base::resizeForBands(int bands) {
|
||||||
{
|
|
||||||
int exp;
|
int exp;
|
||||||
if (bands <= 8)
|
if (bands <= 8)
|
||||||
exp = 4;
|
exp = 4;
|
||||||
|
@ -162,8 +154,7 @@ void Analyzer::Base::demo(QPainter& p) //virtual
|
||||||
static int t = 201; // FIXME make static to namespace perhaps
|
static int t = 201; // FIXME make static to namespace perhaps
|
||||||
|
|
||||||
if (t > 999) t = 1; // 0 = wasted calculations
|
if (t > 999) t = 1; // 0 = wasted calculations
|
||||||
if( t < 201 )
|
if (t < 201) {
|
||||||
{
|
|
||||||
Scope s(32);
|
Scope s(32);
|
||||||
|
|
||||||
const double dt = double(t) / 200;
|
const double dt = double(t) / 200;
|
||||||
|
@ -171,52 +162,43 @@ void Analyzer::Base::demo(QPainter& p) //virtual
|
||||||
s[i] = dt * (sin(M_PI + (i * M_PI) / s.size()) + 1.0);
|
s[i] = dt * (sin(M_PI + (i * M_PI) / s.size()) + 1.0);
|
||||||
|
|
||||||
analyze(p, s, new_frame_);
|
analyze(p, s, new_frame_);
|
||||||
}
|
} else
|
||||||
else analyze( p, Scope( 32, 0 ), new_frame_ );
|
analyze(p, Scope(32, 0), new_frame_);
|
||||||
|
|
||||||
++t;
|
++t;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Analyzer::Base::polishEvent() {
|
||||||
void Analyzer::Base::polishEvent()
|
|
||||||
{
|
|
||||||
init(); // virtual
|
init(); // virtual
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void Analyzer::interpolate(const Scope& inVec, Scope& outVec) // static
|
||||||
Analyzer::interpolate( const Scope &inVec, Scope &outVec ) //static
|
|
||||||
{
|
{
|
||||||
double pos = 0.0;
|
double pos = 0.0;
|
||||||
const double step = (double)inVec.size() / outVec.size();
|
const double step = (double)inVec.size() / outVec.size();
|
||||||
|
|
||||||
for ( uint i = 0; i < outVec.size(); ++i, pos += step )
|
for (uint i = 0; i < outVec.size(); ++i, pos += step) {
|
||||||
{
|
|
||||||
const double error = pos - std::floor(pos);
|
const double error = pos - std::floor(pos);
|
||||||
const unsigned long offset = (unsigned long)pos;
|
const unsigned long offset = (unsigned long)pos;
|
||||||
|
|
||||||
unsigned long indexLeft = offset + 0;
|
unsigned long indexLeft = offset + 0;
|
||||||
|
|
||||||
if ( indexLeft >= inVec.size() )
|
if (indexLeft >= inVec.size()) indexLeft = inVec.size() - 1;
|
||||||
indexLeft = inVec.size() - 1;
|
|
||||||
|
|
||||||
unsigned long indexRight = offset + 1;
|
unsigned long indexRight = offset + 1;
|
||||||
|
|
||||||
if ( indexRight >= inVec.size() )
|
if (indexRight >= inVec.size()) indexRight = inVec.size() - 1;
|
||||||
indexRight = inVec.size() - 1;
|
|
||||||
|
|
||||||
outVec[i] = inVec[indexLeft ] * ( 1.0 - error ) +
|
outVec[i] = inVec[indexLeft] * (1.0 - error) + inVec[indexRight] * error;
|
||||||
inVec[indexRight] * error;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void Analyzer::initSin(Scope& v, const uint size) // static
|
||||||
Analyzer::initSin( Scope &v, const uint size ) //static
|
|
||||||
{
|
{
|
||||||
double step = (M_PI * 2) / size;
|
double step = (M_PI * 2) / size;
|
||||||
double radian = 0;
|
double radian = 0;
|
||||||
|
|
||||||
for ( uint i = 0; i < size; i++ )
|
for (uint i = 0; i < size; i++) {
|
||||||
{
|
|
||||||
v.push_back(sin(radian));
|
v.push_back(sin(radian));
|
||||||
radian += step;
|
radian += step;
|
||||||
}
|
}
|
||||||
|
@ -224,8 +206,7 @@ Analyzer::initSin( Scope &v, const uint size ) //static
|
||||||
|
|
||||||
void Analyzer::Base::timerEvent(QTimerEvent* e) {
|
void Analyzer::Base::timerEvent(QTimerEvent* e) {
|
||||||
QWidget::timerEvent(e);
|
QWidget::timerEvent(e);
|
||||||
if (e->timerId() != m_timer.timerId())
|
if (e->timerId() != m_timer.timerId()) return;
|
||||||
return;
|
|
||||||
|
|
||||||
new_frame_ = true;
|
new_frame_ = true;
|
||||||
update();
|
update();
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
#ifndef ANALYZERBASE_H
|
#ifndef ANALYZERBASE_H
|
||||||
#define ANALYZERBASE_H
|
#define ANALYZERBASE_H
|
||||||
|
|
||||||
|
|
||||||
#ifdef __FreeBSD__
|
#ifdef __FreeBSD__
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#endif
|
#endif
|
||||||
|
@ -29,13 +28,11 @@ class QEvent;
|
||||||
class QPaintEvent;
|
class QPaintEvent;
|
||||||
class QResizeEvent;
|
class QResizeEvent;
|
||||||
|
|
||||||
|
|
||||||
namespace Analyzer {
|
namespace Analyzer {
|
||||||
|
|
||||||
typedef std::vector<float> Scope;
|
typedef std::vector<float> Scope;
|
||||||
|
|
||||||
class Base : public QWidget
|
class Base : public QWidget {
|
||||||
{
|
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -81,7 +78,6 @@ protected:
|
||||||
bool is_playing_;
|
bool is_playing_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
void interpolate(const Scope&, Scope&);
|
void interpolate(const Scope&, Scope&);
|
||||||
void initSin(Scope&, const uint = 6000);
|
void initSin(Scope&, const uint = 6000);
|
||||||
|
|
||||||
|
|
|
@ -52,8 +52,7 @@ AnalyzerContainer::AnalyzerContainer(QWidget *parent)
|
||||||
double_click_timer_(new QTimer(this)),
|
double_click_timer_(new QTimer(this)),
|
||||||
ignore_next_click_(false),
|
ignore_next_click_(false),
|
||||||
current_analyzer_(nullptr),
|
current_analyzer_(nullptr),
|
||||||
engine_(nullptr)
|
engine_(nullptr) {
|
||||||
{
|
|
||||||
QHBoxLayout* layout = new QHBoxLayout(this);
|
QHBoxLayout* layout = new QHBoxLayout(this);
|
||||||
setLayout(layout);
|
setLayout(layout);
|
||||||
layout->setContentsMargins(0, 0, 0, 0);
|
layout->setContentsMargins(0, 0, 0, 0);
|
||||||
|
@ -62,7 +61,8 @@ AnalyzerContainer::AnalyzerContainer(QWidget *parent)
|
||||||
AddFramerate(tr("Low (%1 fps)").arg(kLowFramerate), kLowFramerate);
|
AddFramerate(tr("Low (%1 fps)").arg(kLowFramerate), kLowFramerate);
|
||||||
AddFramerate(tr("Medium (%1 fps)").arg(kMediumFramerate), kMediumFramerate);
|
AddFramerate(tr("Medium (%1 fps)").arg(kMediumFramerate), kMediumFramerate);
|
||||||
AddFramerate(tr("High (%1 fps)").arg(kHighFramerate), kHighFramerate);
|
AddFramerate(tr("High (%1 fps)").arg(kHighFramerate), kHighFramerate);
|
||||||
AddFramerate(tr("Super high (%1 fps)").arg(kSuperHighFramerate), kSuperHighFramerate);
|
AddFramerate(tr("Super high (%1 fps)").arg(kSuperHighFramerate),
|
||||||
|
kSuperHighFramerate);
|
||||||
connect(mapper_framerate_, SIGNAL(mapped(int)), SLOT(ChangeFramerate(int)));
|
connect(mapper_framerate_, SIGNAL(mapped(int)), SLOT(ChangeFramerate(int)));
|
||||||
|
|
||||||
context_menu_->addMenu(context_menu_framerate_);
|
context_menu_->addMenu(context_menu_framerate_);
|
||||||
|
@ -76,8 +76,8 @@ AnalyzerContainer::AnalyzerContainer(QWidget *parent)
|
||||||
AddAnalyzerType<NyanCatAnalyzer>();
|
AddAnalyzerType<NyanCatAnalyzer>();
|
||||||
|
|
||||||
connect(mapper_, SIGNAL(mapped(int)), SLOT(ChangeAnalyzer(int)));
|
connect(mapper_, SIGNAL(mapped(int)), SLOT(ChangeAnalyzer(int)));
|
||||||
disable_action_ =
|
disable_action_ = context_menu_->addAction(tr("No analyzer"), this,
|
||||||
context_menu_->addAction(tr("No analyzer"), this, SLOT(DisableAnalyzer()));
|
SLOT(DisableAnalyzer()));
|
||||||
disable_action_->setCheckable(true);
|
disable_action_->setCheckable(true);
|
||||||
group_->addAction(disable_action_);
|
group_->addAction(disable_action_);
|
||||||
|
|
||||||
|
@ -119,8 +119,7 @@ void AnalyzerContainer::mouseDoubleClickEvent(QMouseEvent *) {
|
||||||
double_click_timer_->stop();
|
double_click_timer_->stop();
|
||||||
ignore_next_click_ = true;
|
ignore_next_click_ = true;
|
||||||
|
|
||||||
if (visualisation_action_)
|
if (visualisation_action_) visualisation_action_->trigger();
|
||||||
visualisation_action_->trigger();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnalyzerContainer::wheelEvent(QWheelEvent* e) {
|
void AnalyzerContainer::wheelEvent(QWheelEvent* e) {
|
||||||
|
@ -128,8 +127,7 @@ void AnalyzerContainer::wheelEvent(QWheelEvent* e) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnalyzerContainer::SetEngine(EngineBase* engine) {
|
void AnalyzerContainer::SetEngine(EngineBase* engine) {
|
||||||
if (current_analyzer_)
|
if (current_analyzer_) current_analyzer_->set_engine(engine);
|
||||||
current_analyzer_->set_engine(engine);
|
|
||||||
engine_ = engine;
|
engine_ = engine;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,7 +142,8 @@ void AnalyzerContainer::ChangeAnalyzer(int id) {
|
||||||
QObject* instance = analyzer_types_[id]->newInstance(Q_ARG(QWidget*, this));
|
QObject* instance = analyzer_types_[id]->newInstance(Q_ARG(QWidget*, this));
|
||||||
|
|
||||||
if (!instance) {
|
if (!instance) {
|
||||||
qLog(Warning) << "Couldn't intialise a new" << analyzer_types_[id]->className();
|
qLog(Warning) << "Couldn't intialise a new"
|
||||||
|
<< analyzer_types_[id]->className();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,7 +151,8 @@ void AnalyzerContainer::ChangeAnalyzer(int id) {
|
||||||
current_analyzer_ = qobject_cast<Analyzer::Base*>(instance);
|
current_analyzer_ = qobject_cast<Analyzer::Base*>(instance);
|
||||||
current_analyzer_->set_engine(engine_);
|
current_analyzer_->set_engine(engine_);
|
||||||
// Even if it is not supposed to happen, I don't want to get a dbz error
|
// Even if it is not supposed to happen, I don't want to get a dbz error
|
||||||
current_framerate_ = current_framerate_ == 0 ? kMediumFramerate : current_framerate_;
|
current_framerate_ =
|
||||||
|
current_framerate_ == 0 ? kMediumFramerate : current_framerate_;
|
||||||
current_analyzer_->changeTimeout(1000 / current_framerate_);
|
current_analyzer_->changeTimeout(1000 / current_framerate_);
|
||||||
|
|
||||||
layout()->addWidget(current_analyzer_);
|
layout()->addWidget(current_analyzer_);
|
||||||
|
@ -212,13 +212,14 @@ void AnalyzerContainer::Save() {
|
||||||
QSettings s;
|
QSettings s;
|
||||||
s.beginGroup(kSettingsGroup);
|
s.beginGroup(kSettingsGroup);
|
||||||
|
|
||||||
s.setValue("type", current_analyzer_ ?
|
s.setValue("type", current_analyzer_
|
||||||
current_analyzer_->metaObject()->className() :
|
? current_analyzer_->metaObject()->className()
|
||||||
QVariant());
|
: QVariant());
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnalyzerContainer::AddFramerate(const QString& name, int framerate) {
|
void AnalyzerContainer::AddFramerate(const QString& name, int framerate) {
|
||||||
QAction *action = context_menu_framerate_->addAction(name, mapper_framerate_, SLOT(map()));
|
QAction* action =
|
||||||
|
context_menu_framerate_->addAction(name, mapper_framerate_, SLOT(map()));
|
||||||
mapper_framerate_->setMapping(action, framerate);
|
mapper_framerate_->setMapping(action, framerate);
|
||||||
group_framerate_->addAction(action);
|
group_framerate_->addAction(action);
|
||||||
framerate_list_ << framerate;
|
framerate_list_ << framerate;
|
||||||
|
|
|
@ -92,7 +92,8 @@ template <typename T>
|
||||||
int id = analyzer_types_.count();
|
int id = analyzer_types_.count();
|
||||||
analyzer_types_ << &T::staticMetaObject;
|
analyzer_types_ << &T::staticMetaObject;
|
||||||
|
|
||||||
QAction* action = context_menu_->addAction(tr(T::kName), mapper_, SLOT(map()));
|
QAction* action =
|
||||||
|
context_menu_->addAction(tr(T::kName), mapper_, SLOT(map()));
|
||||||
group_->addAction(action);
|
group_->addAction(action);
|
||||||
mapper_->setMapping(action, id);
|
mapper_->setMapping(action, id);
|
||||||
action->setCheckable(true);
|
action->setCheckable(true);
|
||||||
|
@ -100,4 +101,3 @@ template <typename T>
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -16,8 +16,8 @@
|
||||||
#include <QtDebug>
|
#include <QtDebug>
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
|
|
||||||
const char* BarAnalyzer::kName = QT_TRANSLATE_NOOP("AnalyzerContainer", "Bar analyzer");
|
const char* BarAnalyzer::kName =
|
||||||
|
QT_TRANSLATE_NOOP("AnalyzerContainer", "Bar analyzer");
|
||||||
|
|
||||||
BarAnalyzer::BarAnalyzer(QWidget* parent)
|
BarAnalyzer::BarAnalyzer(QWidget* parent)
|
||||||
: Analyzer::Base(parent, 8)
|
: Analyzer::Base(parent, 8)
|
||||||
|
@ -31,31 +31,26 @@ BarAnalyzer::BarAnalyzer( QWidget *parent )
|
||||||
|
|
||||||
QColor fg(0xff, 0x50, 0x70);
|
QColor fg(0xff, 0x50, 0x70);
|
||||||
|
|
||||||
double dr = double(m_bg.red() - fg.red()) / (NUM_ROOFS-1); //-1 because we start loop below at 0
|
double dr = double(m_bg.red() - fg.red()) /
|
||||||
|
(NUM_ROOFS - 1); //-1 because we start loop below at 0
|
||||||
double dg = double(m_bg.green() - fg.green()) / (NUM_ROOFS - 1);
|
double dg = double(m_bg.green() - fg.green()) / (NUM_ROOFS - 1);
|
||||||
double db = double(m_bg.blue() - fg.blue()) / (NUM_ROOFS - 1);
|
double db = double(m_bg.blue() - fg.blue()) / (NUM_ROOFS - 1);
|
||||||
|
|
||||||
for ( uint i = 0; i < NUM_ROOFS; ++i )
|
for (uint i = 0; i < NUM_ROOFS; ++i) {
|
||||||
{
|
|
||||||
m_pixRoof[i] = QPixmap(COLUMN_WIDTH, 1);
|
m_pixRoof[i] = QPixmap(COLUMN_WIDTH, 1);
|
||||||
m_pixRoof[i].fill( QColor( fg.red()+int(dr*i), fg.green()+int(dg*i), fg.blue()+int(db*i) ) );
|
m_pixRoof[i].fill(QColor(fg.red() + int(dr * i), fg.green() + int(dg * i),
|
||||||
|
fg.blue() + int(db * i)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
void BarAnalyzer::resizeEvent(QResizeEvent* e) { init(); }
|
||||||
|
|
||||||
void BarAnalyzer::resizeEvent( QResizeEvent * e )
|
|
||||||
{
|
|
||||||
init();
|
|
||||||
}
|
|
||||||
|
|
||||||
// METHODS =====================================================
|
// METHODS =====================================================
|
||||||
|
|
||||||
void BarAnalyzer::init()
|
void BarAnalyzer::init() {
|
||||||
{
|
|
||||||
const double MAX_AMPLITUDE = 1.0;
|
const double MAX_AMPLITUDE = 1.0;
|
||||||
const double F = double(height() - 2) / (log10(255) * MAX_AMPLITUDE);
|
const double F = double(height() - 2) / (log10(255) * MAX_AMPLITUDE);
|
||||||
|
|
||||||
|
|
||||||
BAND_COUNT = width() / 5;
|
BAND_COUNT = width() / 5;
|
||||||
MAX_DOWN = int(0 - (qMax(1, height() / 50)));
|
MAX_DOWN = int(0 - (qMax(1, height() / 50)));
|
||||||
MAX_UP = int(qMax(1, height() / 25));
|
MAX_UP = int(qMax(1, height() / 25));
|
||||||
|
@ -66,9 +61,9 @@ void BarAnalyzer::init()
|
||||||
m_roofMem.resize(BAND_COUNT);
|
m_roofMem.resize(BAND_COUNT);
|
||||||
m_scope.resize(BAND_COUNT);
|
m_scope.resize(BAND_COUNT);
|
||||||
|
|
||||||
//generate a list of values that express amplitudes in range 0-MAX_AMP as ints from 0-height() on log scale
|
// generate a list of values that express amplitudes in range 0-MAX_AMP as
|
||||||
for ( uint x = 0; x < 256; ++x )
|
// ints from 0-height() on log scale
|
||||||
{
|
for (uint x = 0; x < 256; ++x) {
|
||||||
m_lvlMapper[x] = uint(F * log10(x + 1));
|
m_lvlMapper[x] = uint(F * log10(x + 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,40 +71,39 @@ void BarAnalyzer::init()
|
||||||
m_pixCompose = QPixmap(size());
|
m_pixCompose = QPixmap(size());
|
||||||
|
|
||||||
QPainter p(&m_pixBarGradient);
|
QPainter p(&m_pixBarGradient);
|
||||||
for ( int x=0, r=0x40, g=0x30, b=0xff, r2=255-r;
|
for (int x = 0, r = 0x40, g = 0x30, b = 0xff, r2 = 255 - r; x < height();
|
||||||
x < height(); ++x )
|
++x) {
|
||||||
{
|
for (int y = x; y > 0; --y) {
|
||||||
for ( int y = x; y > 0; --y )
|
|
||||||
{
|
|
||||||
const double fraction = (double)y / height();
|
const double fraction = (double)y / height();
|
||||||
|
|
||||||
// p.setPen( QColor( r + (int)(r2 * fraction), g, b - (int)(255 * fraction) ) );
|
// p.setPen( QColor( r + (int)(r2 * fraction), g, b - (int)(255 *
|
||||||
|
// fraction) ) );
|
||||||
p.setPen(QColor(r + (int)(r2 * fraction), g, b));
|
p.setPen(QColor(r + (int)(r2 * fraction), g, b));
|
||||||
p.drawLine( x*COLUMN_WIDTH, height() - y, (x+1)*COLUMN_WIDTH, height() - y );
|
p.drawLine(x * COLUMN_WIDTH, height() - y, (x + 1) * COLUMN_WIDTH,
|
||||||
|
height() - y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
setMinimumSize(QSize(BAND_COUNT * COLUMN_WIDTH, 10));
|
setMinimumSize(QSize(BAND_COUNT * COLUMN_WIDTH, 10));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BarAnalyzer::analyze(QPainter& p, const Scope& s, bool new_frame) {
|
||||||
void BarAnalyzer::analyze( QPainter& p, const Scope &s, bool new_frame)
|
|
||||||
{
|
|
||||||
// Analyzer::interpolate( s, m_bands );
|
// Analyzer::interpolate( s, m_bands );
|
||||||
|
|
||||||
Scope& v = m_scope;
|
Scope& v = m_scope;
|
||||||
Analyzer::interpolate(s, v);
|
Analyzer::interpolate(s, v);
|
||||||
|
|
||||||
for ( uint i = 0, x = 0, y2; i < v.size(); ++i, x+=COLUMN_WIDTH+1 )
|
for (uint i = 0, x = 0, y2; i < v.size(); ++i, x += COLUMN_WIDTH + 1) {
|
||||||
{
|
|
||||||
// assign pre[log10]'d value
|
// assign pre[log10]'d value
|
||||||
y2 = uint(v[i] * 256); //256 will be optimised to a bitshift //no, it's a float
|
y2 = uint(v[i] *
|
||||||
y2 = m_lvlMapper[ (y2 > 255) ? 255 : y2 ]; //lvlMapper is array of ints with values 0 to height()
|
256); // 256 will be optimised to a bitshift //no, it's a float
|
||||||
|
y2 = m_lvlMapper[(y2 > 255) ? 255 : y2]; // lvlMapper is array of ints with
|
||||||
|
// values 0 to height()
|
||||||
|
|
||||||
int change = y2 - barVector[i];
|
int change = y2 - barVector[i];
|
||||||
|
|
||||||
//using the best of Markey's, piggz and Max's ideas on the way to shift the bars
|
// using the best of Markey's, piggz and Max's ideas on the way to shift the
|
||||||
|
// bars
|
||||||
// we have the following:
|
// we have the following:
|
||||||
// 1. don't adjust shift when doing small up movements
|
// 1. don't adjust shift when doing small up movements
|
||||||
// 2. shift large upwards with a bias towards last value
|
// 2. shift large upwards with a bias towards last value
|
||||||
|
@ -119,12 +113,11 @@ void BarAnalyzer::analyze( QPainter& p, const Scope &s, bool new_frame)
|
||||||
//add some dynamics - makes the value slightly closer to what it was last time
|
//add some dynamics - makes the value slightly closer to what it was last time
|
||||||
y2 = ( barVector[i] + MAX_UP );
|
y2 = ( barVector[i] + MAX_UP );
|
||||||
//y2 = ( barVector[i] * 2 + y2 ) / 3;
|
//y2 = ( barVector[i] * 2 + y2 ) / 3;
|
||||||
else*/ if ( change < MAX_DOWN )
|
else*/ if (change <
|
||||||
|
MAX_DOWN)
|
||||||
y2 = barVector[i] + MAX_DOWN;
|
y2 = barVector[i] + MAX_DOWN;
|
||||||
|
|
||||||
|
if ((int)y2 > roofVector[i]) {
|
||||||
if ( (int)y2 > roofVector[i] )
|
|
||||||
{
|
|
||||||
roofVector[i] = (int)y2;
|
roofVector[i] = (int)y2;
|
||||||
roofVelocityVector[i] = 1;
|
roofVelocityVector[i] = 1;
|
||||||
}
|
}
|
||||||
|
@ -137,30 +130,32 @@ void BarAnalyzer::analyze( QPainter& p, const Scope &s, bool new_frame)
|
||||||
|
|
||||||
// blt last n roofs, a.k.a motion blur
|
// blt last n roofs, a.k.a motion blur
|
||||||
for (uint c = 0; c < m_roofMem[i].size(); ++c)
|
for (uint c = 0; c < m_roofMem[i].size(); ++c)
|
||||||
//bitBlt( m_pComposePixmap, x, m_roofMem[i]->at( c ), m_roofPixmaps[ c ] );
|
// bitBlt( m_pComposePixmap, x, m_roofMem[i]->at( c ), m_roofPixmaps[ c ]
|
||||||
//bitBlt( canvas(), x, m_roofMem[i][c], &m_pixRoof[ NUM_ROOFS - 1 - c ] );
|
// );
|
||||||
|
// bitBlt( canvas(), x, m_roofMem[i][c], &m_pixRoof[ NUM_ROOFS - 1 - c ]
|
||||||
|
// );
|
||||||
p.drawPixmap(x, m_roofMem[i][c], m_pixRoof[NUM_ROOFS - 1 - c]);
|
p.drawPixmap(x, m_roofMem[i][c], m_pixRoof[NUM_ROOFS - 1 - c]);
|
||||||
|
|
||||||
// blt the bar
|
// blt the bar
|
||||||
p.drawPixmap(x, height() - y2,
|
p.drawPixmap(x, height() - y2, *gradient(), y2 * COLUMN_WIDTH,
|
||||||
*gradient(), y2 * COLUMN_WIDTH, height() - y2, COLUMN_WIDTH, y2);
|
height() - y2, COLUMN_WIDTH, y2);
|
||||||
/*bitBlt( canvas(), x, height() - y2,
|
/*bitBlt( canvas(), x, height() - y2,
|
||||||
gradient(), y2 * COLUMN_WIDTH, height() - y2, COLUMN_WIDTH, y2, Qt::CopyROP );*/
|
gradient(), y2 * COLUMN_WIDTH, height() - y2, COLUMN_WIDTH, y2,
|
||||||
|
Qt::CopyROP );*/
|
||||||
|
|
||||||
m_roofMem[i].push_back(height() - roofVector[i] - 2);
|
m_roofMem[i].push_back(height() - roofVector[i] - 2);
|
||||||
|
|
||||||
// set roof parameters for the NEXT draw
|
// set roof parameters for the NEXT draw
|
||||||
if ( roofVelocityVector[i] != 0 )
|
if (roofVelocityVector[i] != 0) {
|
||||||
{
|
|
||||||
if (roofVelocityVector[i] > 32) // no reason to do == 32
|
if (roofVelocityVector[i] > 32) // no reason to do == 32
|
||||||
roofVector[i] -= (roofVelocityVector[i] - 32) / 20; //trivial calculation
|
roofVector[i] -=
|
||||||
|
(roofVelocityVector[i] - 32) / 20; // trivial calculation
|
||||||
|
|
||||||
if ( roofVector[i] < 0 )
|
if (roofVector[i] < 0) {
|
||||||
{
|
|
||||||
roofVector[i] = 0; // not strictly necessary
|
roofVector[i] = 0; // not strictly necessary
|
||||||
roofVelocityVector[i] = 0;
|
roofVelocityVector[i] = 0;
|
||||||
}
|
} else
|
||||||
else ++roofVelocityVector[i];
|
++roofVelocityVector[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,9 +10,7 @@
|
||||||
|
|
||||||
typedef std::vector<uint> aroofMemVec;
|
typedef std::vector<uint> aroofMemVec;
|
||||||
|
|
||||||
|
class BarAnalyzer : public Analyzer::Base {
|
||||||
class BarAnalyzer : public Analyzer::Base
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
Q_INVOKABLE BarAnalyzer(QWidget*);
|
Q_INVOKABLE BarAnalyzer(QWidget*);
|
||||||
|
@ -41,7 +39,8 @@ class BarAnalyzer : public Analyzer::Base
|
||||||
QPixmap m_pixRoof[NUM_ROOFS];
|
QPixmap m_pixRoof[NUM_ROOFS];
|
||||||
// vector<uint> m_roofMem[BAND_COUNT];
|
// vector<uint> m_roofMem[BAND_COUNT];
|
||||||
|
|
||||||
//Scope m_bands; //copy of the Scope to prevent creating/destroying a Scope every iteration
|
// Scope m_bands; //copy of the Scope to prevent creating/destroying a Scope
|
||||||
|
// every iteration
|
||||||
uint m_lvlMapper[256];
|
uint m_lvlMapper[256];
|
||||||
std::vector<aroofMemVec> m_roofMem;
|
std::vector<aroofMemVec> m_roofMem;
|
||||||
std::vector<uint> barVector; // positions of bars
|
std::vector<uint> barVector; // positions of bars
|
||||||
|
|
|
@ -19,36 +19,42 @@ const uint BlockAnalyzer::MIN_COLUMNS = 32; //arbituary
|
||||||
const uint BlockAnalyzer::MAX_COLUMNS = 256; // must be 2**n
|
const uint BlockAnalyzer::MAX_COLUMNS = 256; // must be 2**n
|
||||||
const uint BlockAnalyzer::FADE_SIZE = 90;
|
const uint BlockAnalyzer::FADE_SIZE = 90;
|
||||||
|
|
||||||
const char* BlockAnalyzer::kName = QT_TRANSLATE_NOOP("AnalyzerContainer", "Block analyzer");
|
const char* BlockAnalyzer::kName =
|
||||||
|
QT_TRANSLATE_NOOP("AnalyzerContainer", "Block analyzer");
|
||||||
|
|
||||||
BlockAnalyzer::BlockAnalyzer(QWidget* parent)
|
BlockAnalyzer::BlockAnalyzer(QWidget* parent)
|
||||||
: Analyzer::Base( parent, 9 )
|
: Analyzer::Base(parent, 9),
|
||||||
, m_columns( 0 ) //uint
|
m_columns(0) // uint
|
||||||
, m_rows( 0 ) //uint
|
,
|
||||||
, m_y( 0 ) //uint
|
m_rows(0) // uint
|
||||||
, m_barPixmap( 1, 1 ) //null qpixmaps cause crashes
|
,
|
||||||
, m_topBarPixmap( WIDTH, HEIGHT )
|
m_y(0) // uint
|
||||||
, m_scope( MIN_COLUMNS ) //Scope
|
,
|
||||||
, m_store( 1 << 8, 0 ) //vector<uint>
|
m_barPixmap(1, 1) // null qpixmaps cause crashes
|
||||||
, m_fade_bars( FADE_SIZE ) //vector<QPixmap>
|
,
|
||||||
, m_fade_pos( 1 << 8, 50 ) //vector<uint>
|
m_topBarPixmap(WIDTH, HEIGHT),
|
||||||
, m_fade_intensity( 1 << 8, 32 ) //vector<uint>
|
m_scope(MIN_COLUMNS) // Scope
|
||||||
|
,
|
||||||
|
m_store(1 << 8, 0) // vector<uint>
|
||||||
|
,
|
||||||
|
m_fade_bars(FADE_SIZE) // vector<QPixmap>
|
||||||
|
,
|
||||||
|
m_fade_pos(1 << 8, 50) // vector<uint>
|
||||||
|
,
|
||||||
|
m_fade_intensity(1 << 8, 32) // vector<uint>
|
||||||
{
|
{
|
||||||
setMinimumSize( MIN_COLUMNS*(WIDTH+1) -1, MIN_ROWS*(HEIGHT+1) -1 ); //-1 is padding, no drawing takes place there
|
setMinimumSize(MIN_COLUMNS * (WIDTH + 1) - 1,
|
||||||
|
MIN_ROWS * (HEIGHT + 1) -
|
||||||
|
1); //-1 is padding, no drawing takes place there
|
||||||
setMaximumWidth(MAX_COLUMNS * (WIDTH + 1) - 1);
|
setMaximumWidth(MAX_COLUMNS * (WIDTH + 1) - 1);
|
||||||
|
|
||||||
// mxcl says null pixmaps cause crashes, so let's play it safe
|
// mxcl says null pixmaps cause crashes, so let's play it safe
|
||||||
for ( uint i = 0; i < FADE_SIZE; ++i )
|
for (uint i = 0; i < FADE_SIZE; ++i) m_fade_bars[i] = QPixmap(1, 1);
|
||||||
m_fade_bars[i] = QPixmap( 1, 1 );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BlockAnalyzer::~BlockAnalyzer()
|
BlockAnalyzer::~BlockAnalyzer() {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void BlockAnalyzer::resizeEvent(QResizeEvent* e) {
|
||||||
BlockAnalyzer::resizeEvent( QResizeEvent *e )
|
|
||||||
{
|
|
||||||
QWidget::resizeEvent(e);
|
QWidget::resizeEvent(e);
|
||||||
|
|
||||||
m_background = QPixmap(size());
|
m_background = QPixmap(size());
|
||||||
|
@ -73,7 +79,8 @@ BlockAnalyzer::resizeEvent( QResizeEvent *e )
|
||||||
|
|
||||||
m_yscale.resize(m_rows + 1);
|
m_yscale.resize(m_rows + 1);
|
||||||
|
|
||||||
const uint PRE = 1, PRO = 1; //PRE and PRO allow us to restrict the range somewhat
|
const uint PRE = 1,
|
||||||
|
PRO = 1; // PRE and PRO allow us to restrict the range somewhat
|
||||||
|
|
||||||
for (uint z = 0; z < m_rows; ++z)
|
for (uint z = 0; z < m_rows; ++z)
|
||||||
m_yscale[z] = 1 - (log10(PRE + z) / log10(PRE + m_rows + PRO));
|
m_yscale[z] = 1 - (log10(PRE + z) / log10(PRE + m_rows + PRO));
|
||||||
|
@ -84,39 +91,37 @@ BlockAnalyzer::resizeEvent( QResizeEvent *e )
|
||||||
paletteChange(palette());
|
paletteChange(palette());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
drawBackground();
|
drawBackground();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void BlockAnalyzer::determineStep() {
|
||||||
BlockAnalyzer::determineStep()
|
// falltime is dependent on rowcount due to our digital resolution (ie we have
|
||||||
{
|
// boxes/blocks of pixels)
|
||||||
// falltime is dependent on rowcount due to our digital resolution (ie we have boxes/blocks of pixels)
|
|
||||||
// I calculated the value 30 based on some trial and error
|
// I calculated the value 30 based on some trial and error
|
||||||
|
|
||||||
const double fallTime = 30 * m_rows;
|
const double fallTime = 30 * m_rows;
|
||||||
m_step = double(m_rows * timeout()) / fallTime;
|
m_step = double(m_rows * timeout()) / fallTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void BlockAnalyzer::transform(Analyzer::Scope& s) // pure virtual
|
||||||
BlockAnalyzer::transform( Analyzer::Scope &s ) //pure virtual
|
|
||||||
{
|
{
|
||||||
for( uint x = 0; x < s.size(); ++x )
|
for (uint x = 0; x < s.size(); ++x) s[x] *= 2;
|
||||||
s[x] *= 2;
|
|
||||||
|
|
||||||
float* front = static_cast<float*>(&s.front());
|
float* front = static_cast<float*>(&s.front());
|
||||||
|
|
||||||
m_fht->spectrum(front);
|
m_fht->spectrum(front);
|
||||||
m_fht->scale(front, 1.0 / 20);
|
m_fht->scale(front, 1.0 / 20);
|
||||||
|
|
||||||
//the second half is pretty dull, so only show it if the user has a large analyzer
|
// the second half is pretty dull, so only show it if the user has a large
|
||||||
//by setting to m_scope.size() if large we prevent interpolation of large analyzers, this is good!
|
// analyzer
|
||||||
s.resize( m_scope.size() <= MAX_COLUMNS/2 ? MAX_COLUMNS/2 : m_scope.size() );
|
// by setting to m_scope.size() if large we prevent interpolation of large
|
||||||
|
// analyzers, this is good!
|
||||||
|
s.resize(m_scope.size() <= MAX_COLUMNS / 2 ? MAX_COLUMNS / 2
|
||||||
|
: m_scope.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void BlockAnalyzer::analyze(QPainter& p, const Analyzer::Scope& s,
|
||||||
BlockAnalyzer::analyze( QPainter& p, const Analyzer::Scope &s, bool new_frame)
|
bool new_frame) {
|
||||||
{
|
|
||||||
// y = 2 3 2 1 0 2
|
// y = 2 3 2 1 0 2
|
||||||
// . . . . # .
|
// . . . . # .
|
||||||
// . . . # # .
|
// . . . # # .
|
||||||
|
@ -135,8 +140,7 @@ BlockAnalyzer::analyze( QPainter& p, const Analyzer::Scope &s, bool new_frame)
|
||||||
// Paint the background
|
// Paint the background
|
||||||
p.drawPixmap(0, 0, m_background);
|
p.drawPixmap(0, 0, m_background);
|
||||||
|
|
||||||
for( uint y, x = 0; x < m_scope.size(); ++x )
|
for (uint y, x = 0; x < m_scope.size(); ++x) {
|
||||||
{
|
|
||||||
// determine y
|
// determine y
|
||||||
for (y = 0; m_scope[x] < m_yscale[y]; ++y)
|
for (y = 0; m_scope[x] < m_yscale[y]; ++y)
|
||||||
;
|
;
|
||||||
|
@ -148,7 +152,8 @@ BlockAnalyzer::analyze( QPainter& p, const Analyzer::Scope &s, bool new_frame)
|
||||||
else
|
else
|
||||||
m_store[x] = y;
|
m_store[x] = y;
|
||||||
|
|
||||||
// if y is lower than m_fade_pos, then the bar has exceeded the height of the fadeout
|
// if y is lower than m_fade_pos, then the bar has exceeded the height of
|
||||||
|
// the fadeout
|
||||||
// if the fadeout is quite faded now, then display the new one
|
// if the fadeout is quite faded now, then display the new one
|
||||||
if (y <= m_fade_pos[x] /*|| m_fade_intensity[x] < FADE_SIZE / 3*/) {
|
if (y <= m_fade_pos[x] /*|| m_fade_intensity[x] < FADE_SIZE / 3*/) {
|
||||||
m_fade_pos[x] = y;
|
m_fade_pos[x] = y;
|
||||||
|
@ -158,27 +163,24 @@ BlockAnalyzer::analyze( QPainter& p, const Analyzer::Scope &s, bool new_frame)
|
||||||
if (m_fade_intensity[x] > 0) {
|
if (m_fade_intensity[x] > 0) {
|
||||||
const uint offset = --m_fade_intensity[x];
|
const uint offset = --m_fade_intensity[x];
|
||||||
const uint y = m_y + (m_fade_pos[x] * (HEIGHT + 1));
|
const uint y = m_y + (m_fade_pos[x] * (HEIGHT + 1));
|
||||||
p.drawPixmap(x*(WIDTH+1), y, m_fade_bars[offset], 0, 0, WIDTH, height() - y);
|
p.drawPixmap(x * (WIDTH + 1), y, m_fade_bars[offset], 0, 0, WIDTH,
|
||||||
|
height() - y);
|
||||||
}
|
}
|
||||||
|
|
||||||
if( m_fade_intensity[x] == 0 )
|
if (m_fade_intensity[x] == 0) m_fade_pos[x] = m_rows;
|
||||||
m_fade_pos[x] = m_rows;
|
|
||||||
|
|
||||||
//REMEMBER: y is a number from 0 to m_rows, 0 means all blocks are glowing, m_rows means none are
|
// REMEMBER: y is a number from 0 to m_rows, 0 means all blocks are glowing,
|
||||||
p.drawPixmap( x*(WIDTH+1), y*(HEIGHT+1) + m_y, *bar(), 0, y*(HEIGHT+1), bar()->width(), bar()->height() );
|
// m_rows means none are
|
||||||
|
p.drawPixmap(x * (WIDTH + 1), y * (HEIGHT + 1) + m_y, *bar(), 0,
|
||||||
|
y * (HEIGHT + 1), bar()->width(), bar()->height());
|
||||||
}
|
}
|
||||||
|
|
||||||
for (uint x = 0; x < m_store.size(); ++x)
|
for (uint x = 0; x < m_store.size(); ++x)
|
||||||
p.drawPixmap(x*(WIDTH+1), int(m_store[x])*(HEIGHT+1) + m_y, m_topBarPixmap );
|
p.drawPixmap(x * (WIDTH + 1), int(m_store[x]) * (HEIGHT + 1) + m_y,
|
||||||
|
m_topBarPixmap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void adjustToLimits(int& b, int& f, uint& amount) {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static inline void
|
|
||||||
adjustToLimits( int &b, int &f, uint &amount )
|
|
||||||
{
|
|
||||||
// with a range of 0-255 and maximum adjustment of amount,
|
// with a range of 0-255 and maximum adjustment of amount,
|
||||||
// maximise the difference between f and b
|
// maximise the difference between f and b
|
||||||
|
|
||||||
|
@ -190,8 +192,7 @@ adjustToLimits( int &b, int &f, uint &amount )
|
||||||
amount -= (255 - f);
|
amount -= (255 - f);
|
||||||
f = 255;
|
f = 255;
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
if (f > 255 - b) {
|
if (f > 255 - b) {
|
||||||
amount -= f;
|
amount -= f;
|
||||||
f = 0;
|
f = 0;
|
||||||
|
@ -205,17 +206,20 @@ adjustToLimits( int &b, int &f, uint &amount )
|
||||||
/**
|
/**
|
||||||
* Clever contrast function
|
* Clever contrast function
|
||||||
*
|
*
|
||||||
* It will try to adjust the foreground color such that it contrasts well with the background
|
* It will try to adjust the foreground color such that it contrasts well with
|
||||||
|
*the background
|
||||||
* It won't modify the hue of fg unless absolutely necessary
|
* It won't modify the hue of fg unless absolutely necessary
|
||||||
* @return the adjusted form of fg
|
* @return the adjusted form of fg
|
||||||
*/
|
*/
|
||||||
QColor
|
QColor ensureContrast(const QColor& bg, const QColor& fg, uint _amount = 150) {
|
||||||
ensureContrast( const QColor &bg, const QColor &fg, uint _amount = 150 )
|
|
||||||
{
|
|
||||||
class OutputOnExit {
|
class OutputOnExit {
|
||||||
public:
|
public:
|
||||||
OutputOnExit(const QColor& color) : c(color) {}
|
OutputOnExit(const QColor& color) : c(color) {}
|
||||||
~OutputOnExit() { int h,s,v; c.getHsv( &h, &s, &v ); }
|
~OutputOnExit() {
|
||||||
|
int h, s, v;
|
||||||
|
c.getHsv(&h, &s, &v);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const QColor& c;
|
const QColor& c;
|
||||||
};
|
};
|
||||||
|
@ -223,8 +227,10 @@ ensureContrast( const QColor &bg, const QColor &fg, uint _amount = 150 )
|
||||||
// hack so I don't have to cast everywhere
|
// hack so I don't have to cast everywhere
|
||||||
#define amount static_cast<int>(_amount)
|
#define amount static_cast<int>(_amount)
|
||||||
// #define STAMP debug() << (QValueList<int>() << fh << fs << fv) << endl;
|
// #define STAMP debug() << (QValueList<int>() << fh << fs << fv) << endl;
|
||||||
// #define STAMP1( string ) debug() << string << ": " << (QValueList<int>() << fh << fs << fv) << endl;
|
// #define STAMP1( string ) debug() << string << ": " <<
|
||||||
// #define STAMP2( string, value ) debug() << string << "=" << value << ": " << (QValueList<int>() << fh << fs << fv) << endl;
|
// (QValueList<int>() << fh << fs << fv) << endl;
|
||||||
|
// #define STAMP2( string, value ) debug() << string << "=" << value << ":
|
||||||
|
// " << (QValueList<int>() << fh << fs << fv) << endl;
|
||||||
|
|
||||||
OutputOnExit allocateOnTheStack(fg);
|
OutputOnExit allocateOnTheStack(fg);
|
||||||
|
|
||||||
|
@ -240,16 +246,14 @@ ensureContrast( const QColor &bg, const QColor &fg, uint _amount = 150 )
|
||||||
|
|
||||||
// value is the best measure of contrast
|
// value is the best measure of contrast
|
||||||
// if there is enough difference in value already, return fg unchanged
|
// if there is enough difference in value already, return fg unchanged
|
||||||
if( dv > amount )
|
if (dv > amount) return fg;
|
||||||
return fg;
|
|
||||||
|
|
||||||
int ds = abs(bs - fs);
|
int ds = abs(bs - fs);
|
||||||
|
|
||||||
// STAMP2( "DS", ds );
|
// STAMP2( "DS", ds );
|
||||||
|
|
||||||
// saturation is good enough too. But not as good. TODO adapt this a little
|
// saturation is good enough too. But not as good. TODO adapt this a little
|
||||||
if( ds > amount )
|
if (ds > amount) return fg;
|
||||||
return fg;
|
|
||||||
|
|
||||||
int dh = abs(bh - fh);
|
int dh = abs(bh - fh);
|
||||||
|
|
||||||
|
@ -263,13 +267,16 @@ ensureContrast( const QColor &bg, const QColor &fg, uint _amount = 150 )
|
||||||
// check the saturation for the two colours is sufficient that hue alone can
|
// check the saturation for the two colours is sufficient that hue alone can
|
||||||
// provide sufficient contrast
|
// provide sufficient contrast
|
||||||
if (ds > amount / 2 && (bs > 125 && fs > 125))
|
if (ds > amount / 2 && (bs > 125 && fs > 125))
|
||||||
// STAMP1( "Sufficient saturation difference, and hues are compliemtary" );
|
// STAMP1( "Sufficient saturation difference, and hues are
|
||||||
|
// compliemtary" );
|
||||||
return fg;
|
return fg;
|
||||||
else if (dv > amount / 2 && (bv > 125 && fv > 125))
|
else if (dv > amount / 2 && (bv > 125 && fv > 125))
|
||||||
// STAMP1( "Sufficient value difference, and hues are compliemtary" );
|
// STAMP1( "Sufficient value difference, and hues are
|
||||||
|
// compliemtary" );
|
||||||
return fg;
|
return fg;
|
||||||
|
|
||||||
// STAMP1( "Hues are complimentary but we must modify the value or saturation of the contrasting colour" );
|
// STAMP1( "Hues are complimentary but we must modify the value or
|
||||||
|
// saturation of the contrasting colour" );
|
||||||
|
|
||||||
// but either the colours are two desaturated, or too dark
|
// but either the colours are two desaturated, or too dark
|
||||||
// so we need to adjust the system, although not as much
|
// so we need to adjust the system, although not as much
|
||||||
|
@ -287,22 +294,19 @@ ensureContrast( const QColor &bg, const QColor &fg, uint _amount = 150 )
|
||||||
}
|
}
|
||||||
|
|
||||||
// test that there is available value to honor our contrast requirement
|
// test that there is available value to honor our contrast requirement
|
||||||
if( 255 - dv < amount )
|
if (255 - dv < amount) {
|
||||||
{
|
|
||||||
// we have to modify the value and saturation of fg
|
// we have to modify the value and saturation of fg
|
||||||
// adjustToLimits( bv, fv, amount );
|
// adjustToLimits( bv, fv, amount );
|
||||||
|
|
||||||
// STAMP
|
// STAMP
|
||||||
|
|
||||||
// see if we need to adjust the saturation
|
// see if we need to adjust the saturation
|
||||||
if( amount > 0 )
|
if (amount > 0) adjustToLimits(bs, fs, _amount);
|
||||||
adjustToLimits( bs, fs, _amount );
|
|
||||||
|
|
||||||
// STAMP
|
// STAMP
|
||||||
|
|
||||||
// see if we need to adjust the hue
|
// see if we need to adjust the hue
|
||||||
if( amount > 0 )
|
if (amount > 0) fh += amount; // cycles around;
|
||||||
fh += amount; // cycles around;
|
|
||||||
|
|
||||||
// STAMP
|
// STAMP
|
||||||
|
|
||||||
|
@ -311,13 +315,11 @@ ensureContrast( const QColor &bg, const QColor &fg, uint _amount = 150 )
|
||||||
|
|
||||||
// STAMP
|
// STAMP
|
||||||
|
|
||||||
if( fv > bv && bv > amount )
|
if (fv > bv && bv > amount) return QColor::fromHsv(fh, fs, bv - amount);
|
||||||
return QColor::fromHsv( fh, fs, bv - amount);
|
|
||||||
|
|
||||||
// STAMP
|
// STAMP
|
||||||
|
|
||||||
if( fv < bv && fv > amount )
|
if (fv < bv && fv > amount) return QColor::fromHsv(fh, fs, fv - amount);
|
||||||
return QColor::fromHsv( fh, fs, fv - amount);
|
|
||||||
|
|
||||||
// STAMP
|
// STAMP
|
||||||
|
|
||||||
|
@ -338,8 +340,7 @@ ensureContrast( const QColor &bg, const QColor &fg, uint _amount = 150 )
|
||||||
// #undef STAMP
|
// #undef STAMP
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void BlockAnalyzer::paletteChange(const QPalette&) // virtual
|
||||||
BlockAnalyzer::paletteChange( const QPalette& ) //virtual
|
|
||||||
{
|
{
|
||||||
const QColor bg = palette().color(QPalette::Background);
|
const QColor bg = palette().color(QPalette::Background);
|
||||||
const QColor fg = ensureContrast(bg, palette().color(QPalette::Highlight));
|
const QColor fg = ensureContrast(bg, palette().color(QPalette::Highlight));
|
||||||
|
@ -356,14 +357,16 @@ BlockAnalyzer::paletteChange( const QPalette& ) //virtual
|
||||||
QPainter p(bar());
|
QPainter p(bar());
|
||||||
for (int y = 0; (uint)y < m_rows; ++y)
|
for (int y = 0; (uint)y < m_rows; ++y)
|
||||||
// graduate the fg color
|
// graduate the fg color
|
||||||
p.fillRect( 0, y*(HEIGHT+1), WIDTH, HEIGHT, QColor( r+int(dr*y), g+int(dg*y), b+int(db*y) ) );
|
p.fillRect(0, y * (HEIGHT + 1), WIDTH, HEIGHT,
|
||||||
|
QColor(r + int(dr * y), g + int(dg * y), b + int(db * y)));
|
||||||
|
|
||||||
{
|
{
|
||||||
const QColor bg = palette().color(QPalette::Background).dark(112);
|
const QColor bg = palette().color(QPalette::Background).dark(112);
|
||||||
|
|
||||||
// make a complimentary fadebar colour
|
// make a complimentary fadebar colour
|
||||||
// TODO dark is not always correct, dumbo!
|
// TODO dark is not always correct, dumbo!
|
||||||
int h,s,v; palette().color(QPalette::Background).dark( 150 ).getHsv( &h, &s, &v );
|
int h, s, v;
|
||||||
|
palette().color(QPalette::Background).dark(150).getHsv(&h, &s, &v);
|
||||||
const QColor fg(QColor::fromHsv(h + 120, s, v));
|
const QColor fg(QColor::fromHsv(h + 120, s, v));
|
||||||
|
|
||||||
const double dr = fg.red() - bg.red();
|
const double dr = fg.red() - bg.red();
|
||||||
|
@ -377,7 +380,8 @@ BlockAnalyzer::paletteChange( const QPalette& ) //virtual
|
||||||
QPainter f(&m_fade_bars[y]);
|
QPainter f(&m_fade_bars[y]);
|
||||||
for (int z = 0; (uint)z < m_rows; ++z) {
|
for (int z = 0; (uint)z < m_rows; ++z) {
|
||||||
const double Y = 1.0 - (log10(FADE_SIZE - y) / log10(FADE_SIZE));
|
const double Y = 1.0 - (log10(FADE_SIZE - y) / log10(FADE_SIZE));
|
||||||
f.fillRect( 0, z*(HEIGHT+1), WIDTH, HEIGHT, QColor( r+int(dr*Y), g+int(dg*Y), b+int(db*Y) ) );
|
f.fillRect(0, z * (HEIGHT + 1), WIDTH, HEIGHT,
|
||||||
|
QColor(r + int(dr * Y), g + int(dg * Y), b + int(db * Y)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -385,9 +389,7 @@ BlockAnalyzer::paletteChange( const QPalette& ) //virtual
|
||||||
drawBackground();
|
drawBackground();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void BlockAnalyzer::drawBackground() {
|
||||||
BlockAnalyzer::drawBackground()
|
|
||||||
{
|
|
||||||
const QColor bg = palette().color(QPalette::Background);
|
const QColor bg = palette().color(QPalette::Background);
|
||||||
const QColor bgdark = bg.dark(112);
|
const QColor bgdark = bg.dark(112);
|
||||||
|
|
||||||
|
@ -396,5 +398,6 @@ BlockAnalyzer::drawBackground()
|
||||||
QPainter p(&m_background);
|
QPainter p(&m_background);
|
||||||
for (int x = 0; (uint)x < m_columns; ++x)
|
for (int x = 0; (uint)x < m_columns; ++x)
|
||||||
for (int y = 0; (uint)y < m_rows; ++y)
|
for (int y = 0; (uint)y < m_rows; ++y)
|
||||||
p.fillRect( x*(WIDTH+1), y*(HEIGHT+1) + m_y, WIDTH, HEIGHT, bgdark );
|
p.fillRect(x * (WIDTH + 1), y * (HEIGHT + 1) + m_y, WIDTH, HEIGHT,
|
||||||
|
bgdark);
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,13 +12,11 @@ class QResizeEvent;
|
||||||
class QMouseEvent;
|
class QMouseEvent;
|
||||||
class QPalette;
|
class QPalette;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Max Howell
|
* @author Max Howell
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class BlockAnalyzer : public Analyzer::Base
|
class BlockAnalyzer : public Analyzer::Base {
|
||||||
{
|
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
Q_INVOKABLE BlockAnalyzer(QWidget*);
|
Q_INVOKABLE BlockAnalyzer(QWidget*);
|
||||||
|
|
|
@ -5,39 +5,32 @@
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
|
|
||||||
const char* BoomAnalyzer::kName = QT_TRANSLATE_NOOP("AnalyzerContainer", "Boom analyzer");
|
const char* BoomAnalyzer::kName =
|
||||||
|
QT_TRANSLATE_NOOP("AnalyzerContainer", "Boom analyzer");
|
||||||
|
|
||||||
BoomAnalyzer::BoomAnalyzer(QWidget* parent)
|
BoomAnalyzer::BoomAnalyzer(QWidget* parent)
|
||||||
: Analyzer::Base( parent, 9 )
|
: Analyzer::Base(parent, 9),
|
||||||
, K_barHeight( 1.271 )//1.471
|
K_barHeight(1.271) // 1.471
|
||||||
, F_peakSpeed( 1.103 )//1.122
|
,
|
||||||
, F( 1.0 )
|
F_peakSpeed(1.103) // 1.122
|
||||||
, bar_height( BAND_COUNT, 0 )
|
,
|
||||||
, peak_height( BAND_COUNT, 0 )
|
F(1.0),
|
||||||
, peak_speed( BAND_COUNT, 0.01 )
|
bar_height(BAND_COUNT, 0),
|
||||||
, barPixmap( COLUMN_WIDTH, 50 )
|
peak_height(BAND_COUNT, 0),
|
||||||
{
|
peak_speed(BAND_COUNT, 0.01),
|
||||||
}
|
barPixmap(COLUMN_WIDTH, 50) {}
|
||||||
|
|
||||||
void
|
void BoomAnalyzer::changeK_barHeight(int newValue) {
|
||||||
BoomAnalyzer::changeK_barHeight( int newValue )
|
|
||||||
{
|
|
||||||
K_barHeight = (double)newValue / 1000;
|
K_barHeight = (double)newValue / 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void BoomAnalyzer::changeF_peakSpeed(int newValue) {
|
||||||
BoomAnalyzer::changeF_peakSpeed( int newValue )
|
|
||||||
{
|
|
||||||
F_peakSpeed = (double)newValue / 1000;
|
F_peakSpeed = (double)newValue / 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BoomAnalyzer::resizeEvent(QResizeEvent *) {
|
void BoomAnalyzer::resizeEvent(QResizeEvent*) { init(); }
|
||||||
init();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void BoomAnalyzer::init() {
|
||||||
BoomAnalyzer::init()
|
|
||||||
{
|
|
||||||
const uint HEIGHT = height() - 2;
|
const uint HEIGHT = height() - 2;
|
||||||
const double h = 1.2 / HEIGHT;
|
const double h = 1.2 / HEIGHT;
|
||||||
|
|
||||||
|
@ -46,21 +39,17 @@ BoomAnalyzer::init()
|
||||||
barPixmap = QPixmap(COLUMN_WIDTH - 2, HEIGHT);
|
barPixmap = QPixmap(COLUMN_WIDTH - 2, HEIGHT);
|
||||||
|
|
||||||
QPainter p(&barPixmap);
|
QPainter p(&barPixmap);
|
||||||
for( uint y = 0; y < HEIGHT; ++y )
|
for (uint y = 0; y < HEIGHT; ++y) {
|
||||||
{
|
|
||||||
const double F = (double)y * h;
|
const double F = (double)y * h;
|
||||||
|
|
||||||
p.setPen(QColor(
|
p.setPen(QColor(qMax(0, 255 - int(229.0 * F)),
|
||||||
qMax(0, 255 - int(229.0 * F)),
|
|
||||||
qMax(0, 255 - int(229.0 * F)),
|
qMax(0, 255 - int(229.0 * F)),
|
||||||
qMax(0, 255 - int(191.0 * F))));
|
qMax(0, 255 - int(191.0 * F))));
|
||||||
p.drawLine(0, y, COLUMN_WIDTH - 2, y);
|
p.drawLine(0, y, COLUMN_WIDTH - 2, y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void BoomAnalyzer::transform(Scope& s) {
|
||||||
BoomAnalyzer::transform( Scope &s )
|
|
||||||
{
|
|
||||||
float* front = static_cast<float*>(&s.front());
|
float* front = static_cast<float*>(&s.front());
|
||||||
|
|
||||||
m_fht->spectrum(front);
|
m_fht->spectrum(front);
|
||||||
|
@ -68,52 +57,43 @@ BoomAnalyzer::transform( Scope &s )
|
||||||
|
|
||||||
Scope scope(32, 0);
|
Scope scope(32, 0);
|
||||||
|
|
||||||
const uint xscale[] = { 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,19,24,29,36,43,52,63,76,91,108,129,153,182,216,255 };
|
const uint xscale[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
|
||||||
|
11, 12, 13, 14, 15, 16, 17, 19, 24, 29, 36,
|
||||||
|
43, 52, 63, 76, 91, 108, 129, 153, 182, 216, 255};
|
||||||
|
|
||||||
for (uint j, i = 0; i < 32; i++)
|
for (uint j, i = 0; i < 32; i++)
|
||||||
for (j = xscale[i]; j < xscale[i + 1]; j++)
|
for (j = xscale[i]; j < xscale[i + 1]; j++)
|
||||||
if ( s[j] > scope[i] )
|
if (s[j] > scope[i]) scope[i] = s[j];
|
||||||
scope[i] = s[j];
|
|
||||||
|
|
||||||
s = scope;
|
s = scope;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void BoomAnalyzer::analyze(QPainter& p, const Scope& scope, bool new_frame) {
|
||||||
BoomAnalyzer::analyze( QPainter& p, const Scope& scope, bool new_frame)
|
|
||||||
{
|
|
||||||
float h;
|
float h;
|
||||||
const uint MAX_HEIGHT = height() - 1;
|
const uint MAX_HEIGHT = height() - 1;
|
||||||
|
|
||||||
for( uint i = 0, x = 0, y; i < BAND_COUNT; ++i, x += COLUMN_WIDTH+1 )
|
for (uint i = 0, x = 0, y; i < BAND_COUNT; ++i, x += COLUMN_WIDTH + 1) {
|
||||||
{
|
|
||||||
h = log10(scope[i] * 256.0) * F;
|
h = log10(scope[i] * 256.0) * F;
|
||||||
|
|
||||||
if( h > MAX_HEIGHT )
|
if (h > MAX_HEIGHT) h = MAX_HEIGHT;
|
||||||
h = MAX_HEIGHT;
|
|
||||||
|
|
||||||
if( h > bar_height[i] )
|
if (h > bar_height[i]) {
|
||||||
{
|
|
||||||
bar_height[i] = h;
|
bar_height[i] = h;
|
||||||
|
|
||||||
if( h > peak_height[i] )
|
if (h > peak_height[i]) {
|
||||||
{
|
|
||||||
peak_height[i] = h;
|
peak_height[i] = h;
|
||||||
peak_speed[i] = 0.01;
|
peak_speed[i] = 0.01;
|
||||||
}
|
} else
|
||||||
else goto peak_handling;
|
goto peak_handling;
|
||||||
}
|
} else {
|
||||||
else
|
if (bar_height[i] > 0.0) {
|
||||||
{
|
|
||||||
if( bar_height[i] > 0.0 )
|
|
||||||
{
|
|
||||||
bar_height[i] -= K_barHeight; // 1.4
|
bar_height[i] -= K_barHeight; // 1.4
|
||||||
if (bar_height[i] < 0.0) bar_height[i] = 0.0;
|
if (bar_height[i] < 0.0) bar_height[i] = 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
peak_handling:
|
peak_handling:
|
||||||
|
|
||||||
if( peak_height[i] > 0.0 )
|
if (peak_height[i] > 0.0) {
|
||||||
{
|
|
||||||
peak_height[i] -= peak_speed[i];
|
peak_height[i] -= peak_speed[i];
|
||||||
peak_speed[i] *= F_peakSpeed; // 1.12
|
peak_speed[i] *= F_peakSpeed; // 1.12
|
||||||
|
|
||||||
|
@ -125,8 +105,7 @@ BoomAnalyzer::analyze( QPainter& p, const Scope& scope, bool new_frame)
|
||||||
y = height() - uint(bar_height[i]);
|
y = height() - uint(bar_height[i]);
|
||||||
p.drawPixmap(x + 1, y, barPixmap, 0, y, -1, -1);
|
p.drawPixmap(x + 1, y, barPixmap, 0, y, -1, -1);
|
||||||
p.setPen(palette().color(QPalette::Highlight));
|
p.setPen(palette().color(QPalette::Highlight));
|
||||||
if (bar_height[i] > 0)
|
if (bar_height[i] > 0) p.drawRect(x, y, COLUMN_WIDTH - 1, height() - y - 1);
|
||||||
p.drawRect( x, y, COLUMN_WIDTH - 1, height() - y - 1 );
|
|
||||||
|
|
||||||
y = height() - uint(peak_height[i]);
|
y = height() - uint(peak_height[i]);
|
||||||
p.setPen(palette().color(QPalette::Base));
|
p.setPen(palette().color(QPalette::Base));
|
||||||
|
|
|
@ -11,8 +11,7 @@
|
||||||
@author Max Howell
|
@author Max Howell
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class BoomAnalyzer : public Analyzer::Base
|
class BoomAnalyzer : public Analyzer::Base {
|
||||||
{
|
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
Q_INVOKABLE BoomAnalyzer(QWidget*);
|
Q_INVOKABLE BoomAnalyzer(QWidget*);
|
||||||
|
|
|
@ -23,20 +23,14 @@
|
||||||
#include "glanalyzer.h"
|
#include "glanalyzer.h"
|
||||||
#include <kdebug.h>
|
#include <kdebug.h>
|
||||||
|
|
||||||
|
|
||||||
GLAnalyzer::GLAnalyzer(QWidget* parent)
|
GLAnalyzer::GLAnalyzer(QWidget* parent)
|
||||||
: Analyzer::Base3D(parent, 15)
|
: Analyzer::Base3D(parent, 15), m_oldy(32, -10.0f), m_peaks(32) {}
|
||||||
, m_oldy(32, -10.0f)
|
|
||||||
, m_peaks(32)
|
|
||||||
{}
|
|
||||||
|
|
||||||
GLAnalyzer::~GLAnalyzer()
|
GLAnalyzer::~GLAnalyzer() {}
|
||||||
{}
|
|
||||||
|
|
||||||
// METHODS =====================================================
|
// METHODS =====================================================
|
||||||
|
|
||||||
void GLAnalyzer::analyze( const Scope &s )
|
void GLAnalyzer::analyze(const Scope& s) {
|
||||||
{
|
|
||||||
// kdDebug() << "Scope Size: " << s.size() << endl;
|
// kdDebug() << "Scope Size: " << s.size() << endl;
|
||||||
/* Scope t(32);
|
/* Scope t(32);
|
||||||
if (s.size() != 32)
|
if (s.size() != 32)
|
||||||
|
@ -52,39 +46,36 @@ void GLAnalyzer::analyze( const Scope &s )
|
||||||
float mfactor = 0.0;
|
float mfactor = 0.0;
|
||||||
static int drawcount;
|
static int drawcount;
|
||||||
|
|
||||||
if (s.size() == 64)
|
if (s.size() == 64) {
|
||||||
{
|
|
||||||
offset = 8;
|
offset = 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
glRotatef(0.25f, 0.0f, 1.0f, 0.5f); // Rotate the scene
|
glRotatef(0.25f, 0.0f, 1.0f, 0.5f); // Rotate the scene
|
||||||
drawFloor();
|
drawFloor();
|
||||||
drawcount++;
|
drawcount++;
|
||||||
if (drawcount > 25)
|
if (drawcount > 25) {
|
||||||
{
|
|
||||||
drawcount = 0;
|
drawcount = 0;
|
||||||
peak = 0.0;
|
peak = 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
for ( uint i = 0; i < 32; i++ )
|
for (uint i = 0; i < 32; i++) {
|
||||||
{
|
if (s[i] > peak) {
|
||||||
if (s[i] > peak)
|
|
||||||
{
|
|
||||||
peak = s[i];
|
peak = s[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mfactor = 20 / peak;
|
mfactor = 20 / peak;
|
||||||
for ( uint i = 0; i < 32; i++ )
|
for (uint i = 0; i < 32; i++) {
|
||||||
{
|
|
||||||
|
|
||||||
// kdDebug() << "Scope item " << i << " value: " << s[i] << endl;
|
// kdDebug() << "Scope item " << i << " value: " << s[i] << endl;
|
||||||
|
|
||||||
// Calculate new horizontal position (x) depending on number of samples
|
// Calculate new horizontal position (x) depending on number of samples
|
||||||
x = -16.0f + i;
|
x = -16.0f + i;
|
||||||
|
|
||||||
// Calculating new vertical position (y) depending on the data passed by amarok
|
// Calculating new vertical position (y) depending on the data passed by
|
||||||
y = float(s[i+offset] * mfactor); //This make it kinda dynamically resize depending on the data
|
// amarok
|
||||||
|
y = float(s[i + offset] * mfactor); // This make it kinda dynamically
|
||||||
|
// resize depending on the data
|
||||||
|
|
||||||
// Some basic bounds checking
|
// Some basic bounds checking
|
||||||
if (y > 30)
|
if (y > 30)
|
||||||
|
@ -96,43 +87,36 @@ void GLAnalyzer::analyze( const Scope &s )
|
||||||
{
|
{
|
||||||
y = m_oldy[i] - 0.7f;
|
y = m_oldy[i] - 0.7f;
|
||||||
}
|
}
|
||||||
if (y < 0.0f)
|
if (y < 0.0f) {
|
||||||
{
|
|
||||||
y = 0.0f;
|
y = 0.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_oldy[i] = y; // Save value as last value
|
m_oldy[i] = y; // Save value as last value
|
||||||
|
|
||||||
// Peak Code
|
// Peak Code
|
||||||
if (m_oldy[i] > m_peaks[i].level)
|
if (m_oldy[i] > m_peaks[i].level) {
|
||||||
{
|
|
||||||
m_peaks[i].level = m_oldy[i];
|
m_peaks[i].level = m_oldy[i];
|
||||||
m_peaks[i].delay = 30;
|
m_peaks[i].delay = 30;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_peaks[i].delay > 0)
|
if (m_peaks[i].delay > 0) {
|
||||||
{
|
|
||||||
m_peaks[i].delay--;
|
m_peaks[i].delay--;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_peaks[i].level > 1.0f)
|
if (m_peaks[i].level > 1.0f) {
|
||||||
{
|
if (m_peaks[i].delay <= 0) {
|
||||||
if (m_peaks[i].delay <= 0)
|
|
||||||
{
|
|
||||||
m_peaks[i].level -= 0.4f;
|
m_peaks[i].level -= 0.4f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Draw the bar
|
// Draw the bar
|
||||||
drawBar(x, y);
|
drawBar(x, y);
|
||||||
drawPeak(x, m_peaks[i].level);
|
drawPeak(x, m_peaks[i].level);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
updateGL();
|
updateGL();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLAnalyzer::initializeGL()
|
void GLAnalyzer::initializeGL() {
|
||||||
{
|
|
||||||
// Clear frame (next fading will be preferred to clearing)
|
// Clear frame (next fading will be preferred to clearing)
|
||||||
glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // Set clear color to black
|
glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // Set clear color to black
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
@ -150,8 +134,7 @@ void GLAnalyzer::initializeGL()
|
||||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLAnalyzer::resizeGL( int w, int h )
|
void GLAnalyzer::resizeGL(int w, int h) {
|
||||||
{
|
|
||||||
glViewport(0, 0, (GLint)w, (GLint)h);
|
glViewport(0, 0, (GLint)w, (GLint)h);
|
||||||
glMatrixMode(GL_PROJECTION);
|
glMatrixMode(GL_PROJECTION);
|
||||||
glLoadIdentity();
|
glLoadIdentity();
|
||||||
|
@ -160,8 +143,7 @@ void GLAnalyzer::resizeGL( int w, int h )
|
||||||
glLoadIdentity();
|
glLoadIdentity();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLAnalyzer::paintGL()
|
void GLAnalyzer::paintGL() {
|
||||||
{
|
|
||||||
glMatrixMode(GL_MODELVIEW);
|
glMatrixMode(GL_MODELVIEW);
|
||||||
#if 0
|
#if 0
|
||||||
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
|
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
|
||||||
|
@ -185,11 +167,9 @@ void GLAnalyzer::paintGL()
|
||||||
// swapBuffers();
|
// swapBuffers();
|
||||||
|
|
||||||
glFlush();
|
glFlush();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLAnalyzer::drawBar(float xPos, float height)
|
void GLAnalyzer::drawBar(float xPos, float height) {
|
||||||
{
|
|
||||||
glPushMatrix();
|
glPushMatrix();
|
||||||
|
|
||||||
// Sets color to blue
|
// Sets color to blue
|
||||||
|
@ -206,8 +186,7 @@ void GLAnalyzer::drawBar(float xPos, float height)
|
||||||
glPopMatrix();
|
glPopMatrix();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLAnalyzer::drawFloor()
|
void GLAnalyzer::drawFloor() {
|
||||||
{
|
|
||||||
glPushMatrix();
|
glPushMatrix();
|
||||||
|
|
||||||
// Sets color to amarok blue
|
// Sets color to amarok blue
|
||||||
|
@ -223,8 +202,7 @@ void GLAnalyzer::drawFloor()
|
||||||
glPopMatrix();
|
glPopMatrix();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLAnalyzer::drawPeak(float xPos, float ypos)
|
void GLAnalyzer::drawPeak(float xPos, float ypos) {
|
||||||
{
|
|
||||||
glPushMatrix();
|
glPushMatrix();
|
||||||
|
|
||||||
// Set the colour to red
|
// Set the colour to red
|
||||||
|
@ -237,8 +215,7 @@ void GLAnalyzer::drawPeak(float xPos, float ypos)
|
||||||
glPopMatrix();
|
glPopMatrix();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLAnalyzer::drawCube()
|
void GLAnalyzer::drawCube() {
|
||||||
{
|
|
||||||
glPushMatrix();
|
glPushMatrix();
|
||||||
glBegin(GL_POLYGON);
|
glBegin(GL_POLYGON);
|
||||||
|
|
||||||
|
@ -287,12 +264,10 @@ void GLAnalyzer::drawCube()
|
||||||
glEnd();
|
glEnd();
|
||||||
glPopMatrix();
|
glPopMatrix();
|
||||||
}
|
}
|
||||||
void GLAnalyzer::drawFrame()
|
void GLAnalyzer::drawFrame() {
|
||||||
{
|
|
||||||
glPushMatrix();
|
glPushMatrix();
|
||||||
glBegin(GL_LINES);
|
glBegin(GL_LINES);
|
||||||
|
|
||||||
|
|
||||||
// This is the top face
|
// This is the top face
|
||||||
glVertex3f(0.0f, 1.0f, 0.0f);
|
glVertex3f(0.0f, 1.0f, 0.0f);
|
||||||
glVertex3f(1.0f, 1.0f, 0.0f);
|
glVertex3f(1.0f, 1.0f, 0.0f);
|
||||||
|
|
|
@ -27,15 +27,12 @@
|
||||||
*@author piggz
|
*@author piggz
|
||||||
*/
|
*/
|
||||||
|
|
||||||
typedef struct
|
typedef struct {
|
||||||
{
|
|
||||||
float level;
|
float level;
|
||||||
uint delay;
|
uint delay;
|
||||||
}
|
} peak_tx;
|
||||||
peak_tx;
|
|
||||||
|
|
||||||
class GLAnalyzer : public Analyzer::Base3D
|
class GLAnalyzer : public Analyzer::Base3D {
|
||||||
{
|
|
||||||
private:
|
private:
|
||||||
std::vector<float> m_oldy;
|
std::vector<float> m_oldy;
|
||||||
std::vector<peak_tx> m_peaks;
|
std::vector<peak_tx> m_peaks;
|
||||||
|
@ -47,6 +44,7 @@ private:
|
||||||
void drawFloor();
|
void drawFloor();
|
||||||
|
|
||||||
GLfloat x, y;
|
GLfloat x, y;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
GLAnalyzer(QWidget*);
|
GLAnalyzer(QWidget*);
|
||||||
~GLAnalyzer();
|
~GLAnalyzer();
|
||||||
|
|
|
@ -27,10 +27,7 @@
|
||||||
#include <qimage.h>
|
#include <qimage.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
|
|
||||||
|
GLAnalyzer2::GLAnalyzer2(QWidget* parent) : Analyzer::Base3D(parent, 15) {
|
||||||
GLAnalyzer2::GLAnalyzer2( QWidget *parent ):
|
|
||||||
Analyzer::Base3D(parent, 15)
|
|
||||||
{
|
|
||||||
// initialize openGL context before managing GL calls
|
// initialize openGL context before managing GL calls
|
||||||
makeCurrent();
|
makeCurrent();
|
||||||
loadTexture(locate("data", "amarok/data/dot.png"), dotTexture);
|
loadTexture(locate("data", "amarok/data/dot.png"), dotTexture);
|
||||||
|
@ -43,15 +40,13 @@ Analyzer::Base3D(parent, 15)
|
||||||
frame.rotDegrees = 0.0;
|
frame.rotDegrees = 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
GLAnalyzer2::~GLAnalyzer2()
|
GLAnalyzer2::~GLAnalyzer2() {
|
||||||
{
|
|
||||||
freeTexture(dotTexture);
|
freeTexture(dotTexture);
|
||||||
freeTexture(w1Texture);
|
freeTexture(w1Texture);
|
||||||
freeTexture(w2Texture);
|
freeTexture(w2Texture);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLAnalyzer2::initializeGL()
|
void GLAnalyzer2::initializeGL() {
|
||||||
{
|
|
||||||
// Set a smooth shade model
|
// Set a smooth shade model
|
||||||
glShadeModel(GL_SMOOTH);
|
glShadeModel(GL_SMOOTH);
|
||||||
|
|
||||||
|
@ -68,8 +63,7 @@ void GLAnalyzer2::initializeGL()
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLAnalyzer2::resizeGL( int w, int h )
|
void GLAnalyzer2::resizeGL(int w, int h) {
|
||||||
{
|
|
||||||
// Setup screen. We're going to manually do the perspective projection
|
// Setup screen. We're going to manually do the perspective projection
|
||||||
glViewport(0, 0, (GLint)w, (GLint)h);
|
glViewport(0, 0, (GLint)w, (GLint)h);
|
||||||
glMatrixMode(GL_PROJECTION);
|
glMatrixMode(GL_PROJECTION);
|
||||||
|
@ -77,9 +71,7 @@ void GLAnalyzer2::resizeGL( int w, int h )
|
||||||
glOrtho(-10.0f, 10.0f, -10.0f, 10.0f, -5.0f, 5.0f);
|
glOrtho(-10.0f, 10.0f, -10.0f, 10.0f, -5.0f, 5.0f);
|
||||||
|
|
||||||
// Get the aspect ratio of the screen to draw 'cicular' particles
|
// Get the aspect ratio of the screen to draw 'cicular' particles
|
||||||
float ratio = (float)w / (float)h,
|
float ratio = (float)w / (float)h, eqPixH = 60, eqPixW = 80;
|
||||||
eqPixH = 60,
|
|
||||||
eqPixW = 80;
|
|
||||||
if (ratio >= (4.0 / 3.0)) {
|
if (ratio >= (4.0 / 3.0)) {
|
||||||
unitX = 10.0 / (eqPixH * ratio);
|
unitX = 10.0 / (eqPixH * ratio);
|
||||||
unitY = 10.0 / eqPixH;
|
unitY = 10.0 / eqPixH;
|
||||||
|
@ -94,45 +86,34 @@ void GLAnalyzer2::resizeGL( int w, int h )
|
||||||
show.timeStamp = (double)tv.tv_sec + (double)tv.tv_usec / 1000000.0;
|
show.timeStamp = (double)tv.tv_sec + (double)tv.tv_usec / 1000000.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLAnalyzer2::paused()
|
void GLAnalyzer2::paused() { analyze(Scope()); }
|
||||||
{
|
|
||||||
analyze( Scope() );
|
|
||||||
}
|
|
||||||
|
|
||||||
void GLAnalyzer2::analyze( const Scope &s )
|
void GLAnalyzer2::analyze(const Scope& s) {
|
||||||
{
|
|
||||||
bool haveNoData = s.empty();
|
bool haveNoData = s.empty();
|
||||||
|
|
||||||
// if we're going into pause mode, clear timers.
|
// if we're going into pause mode, clear timers.
|
||||||
if ( !show.paused && haveNoData )
|
if (!show.paused && haveNoData) show.pauseTimer = 0.0;
|
||||||
show.pauseTimer = 0.0;
|
|
||||||
|
|
||||||
// if we have got data, interpolate it (asking myself why I'm doing it here..)
|
// if we have got data, interpolate it (asking myself why I'm doing it here..)
|
||||||
if ( !(show.paused = haveNoData) )
|
if (!(show.paused = haveNoData)) {
|
||||||
{
|
int bands = s.size(), lowbands = bands / 4, hibands = bands / 3,
|
||||||
int bands = s.size(),
|
midbands = bands - lowbands - hibands;
|
||||||
lowbands = bands / 4,
|
Q_UNUSED(midbands);
|
||||||
hibands = bands / 3,
|
float currentEnergy = 0, currentMeanBand = 0, maxValue = 0;
|
||||||
midbands = bands - lowbands - hibands; Q_UNUSED( midbands );
|
for (int i = 0; i < bands; i++) {
|
||||||
float currentEnergy = 0,
|
|
||||||
currentMeanBand = 0,
|
|
||||||
maxValue = 0;
|
|
||||||
for ( int i = 0; i < bands; i++ )
|
|
||||||
{
|
|
||||||
float value = s[i];
|
float value = s[i];
|
||||||
currentEnergy += value;
|
currentEnergy += value;
|
||||||
currentMeanBand += (float)i * value;
|
currentMeanBand += (float)i * value;
|
||||||
if ( value > maxValue )
|
if (value > maxValue) maxValue = value;
|
||||||
maxValue = value;
|
|
||||||
}
|
}
|
||||||
frame.silence = currentEnergy < 0.001;
|
frame.silence = currentEnergy < 0.001;
|
||||||
if ( !frame.silence )
|
if (!frame.silence) {
|
||||||
{
|
|
||||||
frame.meanBand = 100.0 * currentMeanBand / (currentEnergy * bands);
|
frame.meanBand = 100.0 * currentMeanBand / (currentEnergy * bands);
|
||||||
currentEnergy = 100.0 * currentEnergy / (float)bands;
|
currentEnergy = 100.0 * currentEnergy / (float)bands;
|
||||||
frame.dEnergy = currentEnergy - frame.energy;
|
frame.dEnergy = currentEnergy - frame.energy;
|
||||||
frame.energy = currentEnergy;
|
frame.energy = currentEnergy;
|
||||||
// printf( "%d [%f :: %f ]\t%f \n", bands, frame.energy, frame.meanBand, maxValue );
|
// printf( "%d [%f :: %f ]\t%f \n", bands, frame.energy,
|
||||||
|
// frame.meanBand, maxValue );
|
||||||
} else
|
} else
|
||||||
frame.energy = 0.0;
|
frame.energy = 0.0;
|
||||||
}
|
}
|
||||||
|
@ -141,8 +122,7 @@ void GLAnalyzer2::analyze( const Scope &s )
|
||||||
updateGL();
|
updateGL();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLAnalyzer2::paintGL()
|
void GLAnalyzer2::paintGL() {
|
||||||
{
|
|
||||||
// Compute the dT since the last call to paintGL and update timings
|
// Compute the dT since the last call to paintGL and update timings
|
||||||
timeval tv;
|
timeval tv;
|
||||||
gettimeofday(&tv, nullptr);
|
gettimeofday(&tv, nullptr);
|
||||||
|
@ -171,10 +151,8 @@ void GLAnalyzer2::paintGL()
|
||||||
glEnable(GL_TEXTURE_2D);
|
glEnable(GL_TEXTURE_2D);
|
||||||
float alphaN = show.paused ? 0.2 : (frame.energy / 10.0),
|
float alphaN = show.paused ? 0.2 : (frame.energy / 10.0),
|
||||||
alphaP = show.paused ? 1.0 : (1 - frame.energy / 20.0);
|
alphaP = show.paused ? 1.0 : (1 - frame.energy / 20.0);
|
||||||
if ( alphaN > 1.0 )
|
if (alphaN > 1.0) alphaN = 1.0;
|
||||||
alphaN = 1.0;
|
if (alphaP < 0.1) alphaP = 0.1;
|
||||||
if ( alphaP < 0.1 )
|
|
||||||
alphaP = 0.1;
|
|
||||||
glBindTexture(GL_TEXTURE_2D, w2Texture);
|
glBindTexture(GL_TEXTURE_2D, w2Texture);
|
||||||
setTextureMatrix(show.rotDegrees, 0.707 * alphaP);
|
setTextureMatrix(show.rotDegrees, 0.707 * alphaP);
|
||||||
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
|
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
|
||||||
|
@ -211,12 +189,9 @@ void GLAnalyzer2::paintGL()
|
||||||
frame.rotDegrees += 80.0 * show.dT;
|
frame.rotDegrees += 80.0 * show.dT;
|
||||||
|
|
||||||
// handle the 'pause' status
|
// handle the 'pause' status
|
||||||
if ( show.paused )
|
if (show.paused) {
|
||||||
{
|
if (show.pauseTimer > 0.5) {
|
||||||
if ( show.pauseTimer > 0.5 )
|
if (show.pauseTimer > 0.6) show.pauseTimer -= 0.6;
|
||||||
{
|
|
||||||
if ( show.pauseTimer > 0.6 )
|
|
||||||
show.pauseTimer -= 0.6;
|
|
||||||
drawFullDot(0.0f, 0.4f, 0.8f, 1.0f);
|
drawFullDot(0.0f, 0.4f, 0.8f, 1.0f);
|
||||||
drawFullDot(0.0f, 0.4f, 0.8f, 1.0f);
|
drawFullDot(0.0f, 0.4f, 0.8f, 1.0f);
|
||||||
}
|
}
|
||||||
|
@ -246,14 +221,9 @@ void GLAnalyzer2::paintGL()
|
||||||
glEnd();
|
glEnd();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLAnalyzer2::drawDot( float x, float y, float size )
|
void GLAnalyzer2::drawDot(float x, float y, float size) {
|
||||||
{
|
float sizeX = size * unitX, sizeY = size * unitY, pLeft = x - sizeX,
|
||||||
float sizeX = size * unitX,
|
pTop = y + sizeY, pRight = x + sizeX, pBottom = y - sizeY;
|
||||||
sizeY = size * unitY,
|
|
||||||
pLeft = x - sizeX,
|
|
||||||
pTop = y + sizeY,
|
|
||||||
pRight = x + sizeX,
|
|
||||||
pBottom = y - sizeY;
|
|
||||||
glTexCoord2f(0, 0); // Bottom Left
|
glTexCoord2f(0, 0); // Bottom Left
|
||||||
glVertex2f(pLeft, pBottom);
|
glVertex2f(pLeft, pBottom);
|
||||||
glTexCoord2f(0, 1); // Top Left
|
glTexCoord2f(0, 1); // Top Left
|
||||||
|
@ -264,8 +234,7 @@ void GLAnalyzer2::drawDot( float x, float y, float size )
|
||||||
glVertex2f(pRight, pBottom);
|
glVertex2f(pRight, pBottom);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLAnalyzer2::drawFullDot( float r, float g, float b, float a )
|
void GLAnalyzer2::drawFullDot(float r, float g, float b, float a) {
|
||||||
{
|
|
||||||
glBindTexture(GL_TEXTURE_2D, dotTexture);
|
glBindTexture(GL_TEXTURE_2D, dotTexture);
|
||||||
glEnable(GL_TEXTURE_2D);
|
glEnable(GL_TEXTURE_2D);
|
||||||
glColor4f(r, g, b, a);
|
glColor4f(r, g, b, a);
|
||||||
|
@ -282,13 +251,10 @@ void GLAnalyzer2::drawFullDot( float r, float g, float b, float a )
|
||||||
glDisable(GL_TEXTURE_2D);
|
glDisable(GL_TEXTURE_2D);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GLAnalyzer2::setTextureMatrix(float rot, float scale) {
|
||||||
void GLAnalyzer2::setTextureMatrix( float rot, float scale )
|
|
||||||
{
|
|
||||||
glMatrixMode(GL_TEXTURE);
|
glMatrixMode(GL_TEXTURE);
|
||||||
glLoadIdentity();
|
glLoadIdentity();
|
||||||
if ( rot != 0.0 || scale != 0.0 )
|
if (rot != 0.0 || scale != 0.0) {
|
||||||
{
|
|
||||||
glTranslatef(0.5f, 0.5f, 0.0f);
|
glTranslatef(0.5f, 0.5f, 0.0f);
|
||||||
glRotatef(rot, 0.0f, 0.0f, 1.0f);
|
glRotatef(rot, 0.0f, 0.0f, 1.0f);
|
||||||
glScalef(scale, scale, 1.0f);
|
glScalef(scale, scale, 1.0f);
|
||||||
|
@ -297,36 +263,30 @@ void GLAnalyzer2::setTextureMatrix( float rot, float scale )
|
||||||
glMatrixMode(GL_MODELVIEW);
|
glMatrixMode(GL_MODELVIEW);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GLAnalyzer2::loadTexture( QString fileName, GLuint& textureID )
|
bool GLAnalyzer2::loadTexture(QString fileName, GLuint& textureID) {
|
||||||
{
|
|
||||||
// reset texture ID to the default EMPTY value
|
// reset texture ID to the default EMPTY value
|
||||||
textureID = 0;
|
textureID = 0;
|
||||||
|
|
||||||
// load image
|
// load image
|
||||||
QImage tmp;
|
QImage tmp;
|
||||||
if ( !tmp.load( fileName ) )
|
if (!tmp.load(fileName)) return false;
|
||||||
return false;
|
|
||||||
|
|
||||||
// convert it to suitable format (flipped RGBA)
|
// convert it to suitable format (flipped RGBA)
|
||||||
QImage texture = QGLWidget::convertToGLFormat(tmp);
|
QImage texture = QGLWidget::convertToGLFormat(tmp);
|
||||||
if ( texture.isNull() )
|
if (texture.isNull()) return false;
|
||||||
return false;
|
|
||||||
|
|
||||||
// get texture number and bind loaded image to that texture
|
// get texture number and bind loaded image to that texture
|
||||||
glGenTextures(1, &textureID);
|
glGenTextures(1, &textureID);
|
||||||
glBindTexture(GL_TEXTURE_2D, textureID);
|
glBindTexture(GL_TEXTURE_2D, textureID);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||||
glTexImage2D( GL_TEXTURE_2D, 0, 4, texture.width(), texture.height(),
|
glTexImage2D(GL_TEXTURE_2D, 0, 4, texture.width(), texture.height(), 0,
|
||||||
0, GL_RGBA, GL_UNSIGNED_BYTE, texture.bits() );
|
GL_RGBA, GL_UNSIGNED_BYTE, texture.bits());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GLAnalyzer2::freeTexture(GLuint& textureID) {
|
||||||
void GLAnalyzer2::freeTexture( GLuint & textureID )
|
if (textureID > 0) glDeleteTextures(1, &textureID);
|
||||||
{
|
|
||||||
if ( textureID > 0 )
|
|
||||||
glDeleteTextures( 1, &textureID );
|
|
||||||
textureID = 0;
|
textureID = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,9 +25,7 @@
|
||||||
#include <qstring.h>
|
#include <qstring.h>
|
||||||
#include <qptrlist.h>
|
#include <qptrlist.h>
|
||||||
|
|
||||||
|
class GLAnalyzer2 : public Analyzer::Base3D {
|
||||||
class GLAnalyzer2 : public Analyzer::Base3D
|
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
GLAnalyzer2(QWidget*);
|
GLAnalyzer2(QWidget*);
|
||||||
~GLAnalyzer2();
|
~GLAnalyzer2();
|
||||||
|
|
|
@ -28,30 +28,32 @@
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
|
|
||||||
#ifndef HAVE_FABSF
|
#ifndef HAVE_FABSF
|
||||||
inline float fabsf(float f)
|
inline float fabsf(float f) { return f < 0.f ? -f : f; }
|
||||||
{
|
|
||||||
return f < 0.f ? -f : f;
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
class Ball {
|
||||||
class Ball
|
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
Ball() : x( drand48() - drand48() ), y( 1 - 2.0 * drand48() ),
|
Ball()
|
||||||
z( drand48() ), vx( 0.0 ), vy( 0.0 ), vz( 0.0 ),
|
: x(drand48() - drand48()),
|
||||||
|
y(1 - 2.0 * drand48()),
|
||||||
|
z(drand48()),
|
||||||
|
vx(0.0),
|
||||||
|
vy(0.0),
|
||||||
|
vz(0.0),
|
||||||
mass(0.01 + drand48() / 10.0)
|
mass(0.01 + drand48() / 10.0)
|
||||||
//,color( (float[3]) { 0.0, drand48()*0.5, 0.7 + drand48() * 0.3 } )
|
//,color( (float[3]) { 0.0, drand48()*0.5, 0.7 + drand48() * 0.3 } )
|
||||||
{
|
{
|
||||||
//this is because GCC < 3.3 can't compile the above line, we aren't sure why though
|
// this is because GCC < 3.3 can't compile the above line, we aren't sure
|
||||||
color[0] = 0.0; color[1] = drand48()*0.5; color[2] = 0.7 + drand48() * 0.3;
|
// why though
|
||||||
|
color[0] = 0.0;
|
||||||
|
color[1] = drand48() * 0.5;
|
||||||
|
color[2] = 0.7 + drand48() * 0.3;
|
||||||
};
|
};
|
||||||
|
|
||||||
float x, y, z, vx, vy, vz, mass;
|
float x, y, z, vx, vy, vz, mass;
|
||||||
float color[3];
|
float color[3];
|
||||||
|
|
||||||
void updatePhysics( float dT )
|
void updatePhysics(float dT) {
|
||||||
{
|
|
||||||
x += vx * dT; // position
|
x += vx * dT; // position
|
||||||
y += vy * dT; // position
|
y += vy * dT; // position
|
||||||
z += vz * dT; // position
|
z += vz * dT; // position
|
||||||
|
@ -66,21 +68,18 @@ class Ball
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class Paddle
|
class Paddle {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
Paddle( float xPos ) : onLeft( xPos < 0 ), mass( 1.0 ),
|
Paddle(float xPos)
|
||||||
X( xPos ), x( xPos ), vx( 0.0 ) {};
|
: onLeft(xPos < 0), mass(1.0), X(xPos), x(xPos), vx(0.0) {};
|
||||||
|
|
||||||
void updatePhysics( float dT )
|
void updatePhysics(float dT) {
|
||||||
{
|
|
||||||
x += vx * dT; // posision
|
x += vx * dT; // posision
|
||||||
vx += (1300 * (X - x) / mass) * dT; // elasticity
|
vx += (1300 * (X - x) / mass) * dT; // elasticity
|
||||||
vx *= (1 - 4.0 * dT); // air friction
|
vx *= (1 - 4.0 * dT); // air friction
|
||||||
}
|
}
|
||||||
|
|
||||||
void renderGL()
|
void renderGL() {
|
||||||
{
|
|
||||||
glBegin(GL_TRIANGLE_STRIP);
|
glBegin(GL_TRIANGLE_STRIP);
|
||||||
glColor3f(0.0f, 0.1f, 0.3f);
|
glColor3f(0.0f, 0.1f, 0.3f);
|
||||||
glVertex3f(x, -1.0f, 0.0);
|
glVertex3f(x, -1.0f, 0.0);
|
||||||
|
@ -91,17 +90,13 @@ class Paddle
|
||||||
glEnd();
|
glEnd();
|
||||||
}
|
}
|
||||||
|
|
||||||
void bounce( Ball * ball )
|
void bounce(Ball* ball) {
|
||||||
{
|
if (onLeft && ball->x < x) {
|
||||||
if ( onLeft && ball->x < x )
|
|
||||||
{
|
|
||||||
ball->vx = vx * mass / (mass + ball->mass) + fabsf(ball->vx);
|
ball->vx = vx * mass / (mass + ball->mass) + fabsf(ball->vx);
|
||||||
ball->vy = (drand48() - drand48()) * 1.8;
|
ball->vy = (drand48() - drand48()) * 1.8;
|
||||||
ball->vz = (drand48() - drand48()) * 0.9;
|
ball->vz = (drand48() - drand48()) * 0.9;
|
||||||
ball->x = x;
|
ball->x = x;
|
||||||
}
|
} else if (!onLeft && ball->x > x) {
|
||||||
else if ( !onLeft && ball->x > x )
|
|
||||||
{
|
|
||||||
ball->vx = vx * mass / (mass + ball->mass) - fabsf(ball->vx);
|
ball->vx = vx * mass / (mass + ball->mass) - fabsf(ball->vx);
|
||||||
ball->vy = (drand48() - drand48()) * 1.8;
|
ball->vy = (drand48() - drand48()) * 1.8;
|
||||||
ball->vz = (drand48() - drand48()) * 0.9;
|
ball->vz = (drand48() - drand48()) * 0.9;
|
||||||
|
@ -109,10 +104,8 @@ class Paddle
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void impulse( float strength )
|
void impulse(float strength) {
|
||||||
{
|
if ((onLeft && strength > vx) || (!onLeft && strength < vx)) vx += strength;
|
||||||
if ( (onLeft && strength > vx) || (!onLeft && strength < vx) )
|
|
||||||
vx += strength;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -120,10 +113,7 @@ class Paddle
|
||||||
float mass, X, x, vx;
|
float mass, X, x, vx;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
GLAnalyzer3::GLAnalyzer3(QWidget* parent) : Analyzer::Base3D(parent, 15) {
|
||||||
GLAnalyzer3::GLAnalyzer3( QWidget *parent ):
|
|
||||||
Analyzer::Base3D(parent, 15)
|
|
||||||
{
|
|
||||||
// initialize openGL context before managing GL calls
|
// initialize openGL context before managing GL calls
|
||||||
makeCurrent();
|
makeCurrent();
|
||||||
loadTexture(locate("data", "amarok/data/ball.png"), ballTexture);
|
loadTexture(locate("data", "amarok/data/ball.png"), ballTexture);
|
||||||
|
@ -132,8 +122,7 @@ Analyzer::Base3D(parent, 15)
|
||||||
balls.setAutoDelete(true);
|
balls.setAutoDelete(true);
|
||||||
leftPaddle = new Paddle(-1.0);
|
leftPaddle = new Paddle(-1.0);
|
||||||
rightPaddle = new Paddle(1.0);
|
rightPaddle = new Paddle(1.0);
|
||||||
for ( int i = 0; i < NUMBER_OF_BALLS; i++ )
|
for (int i = 0; i < NUMBER_OF_BALLS; i++) balls.append(new Ball());
|
||||||
balls.append( new Ball() );
|
|
||||||
|
|
||||||
show.colorK = 0.0;
|
show.colorK = 0.0;
|
||||||
show.gridScrollK = 0.0;
|
show.gridScrollK = 0.0;
|
||||||
|
@ -146,8 +135,7 @@ Analyzer::Base3D(parent, 15)
|
||||||
frame.dEnergy = 0.0;
|
frame.dEnergy = 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
GLAnalyzer3::~GLAnalyzer3()
|
GLAnalyzer3::~GLAnalyzer3() {
|
||||||
{
|
|
||||||
freeTexture(ballTexture);
|
freeTexture(ballTexture);
|
||||||
freeTexture(gridTexture);
|
freeTexture(gridTexture);
|
||||||
delete leftPaddle;
|
delete leftPaddle;
|
||||||
|
@ -155,8 +143,7 @@ GLAnalyzer3::~GLAnalyzer3()
|
||||||
balls.clear();
|
balls.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLAnalyzer3::initializeGL()
|
void GLAnalyzer3::initializeGL() {
|
||||||
{
|
|
||||||
// Set a smooth shade model
|
// Set a smooth shade model
|
||||||
glShadeModel(GL_SMOOTH);
|
glShadeModel(GL_SMOOTH);
|
||||||
|
|
||||||
|
@ -170,8 +157,7 @@ void GLAnalyzer3::initializeGL()
|
||||||
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLAnalyzer3::resizeGL( int w, int h )
|
void GLAnalyzer3::resizeGL(int w, int h) {
|
||||||
{
|
|
||||||
// Setup screen. We're going to manually do the perspective projection
|
// Setup screen. We're going to manually do the perspective projection
|
||||||
glViewport(0, 0, (GLint)w, (GLint)h);
|
glViewport(0, 0, (GLint)w, (GLint)h);
|
||||||
glMatrixMode(GL_PROJECTION);
|
glMatrixMode(GL_PROJECTION);
|
||||||
|
@ -194,13 +180,9 @@ void GLAnalyzer3::resizeGL( int w, int h )
|
||||||
show.timeStamp = (double)tv.tv_sec + (double)tv.tv_usec / 1000000.0;
|
show.timeStamp = (double)tv.tv_sec + (double)tv.tv_usec / 1000000.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLAnalyzer3::paused()
|
void GLAnalyzer3::paused() { analyze(Scope()); }
|
||||||
{
|
|
||||||
analyze( Scope() );
|
|
||||||
}
|
|
||||||
|
|
||||||
void GLAnalyzer3::analyze( const Scope &s )
|
void GLAnalyzer3::analyze(const Scope& s) {
|
||||||
{
|
|
||||||
// compute the dTime since the last call
|
// compute the dTime since the last call
|
||||||
timeval tv;
|
timeval tv;
|
||||||
gettimeofday(&tv, nullptr);
|
gettimeofday(&tv, nullptr);
|
||||||
|
@ -209,24 +191,19 @@ void GLAnalyzer3::analyze( const Scope &s )
|
||||||
show.timeStamp = currentTime;
|
show.timeStamp = currentTime;
|
||||||
|
|
||||||
// compute energy integrating frame's spectrum
|
// compute energy integrating frame's spectrum
|
||||||
if ( !s.empty() )
|
if (!s.empty()) {
|
||||||
{
|
|
||||||
int bands = s.size();
|
int bands = s.size();
|
||||||
float currentEnergy = 0,
|
float currentEnergy = 0, maxValue = 0;
|
||||||
maxValue = 0;
|
|
||||||
// integrate spectrum -> energy
|
// integrate spectrum -> energy
|
||||||
for ( int i = 0; i < bands; i++ )
|
for (int i = 0; i < bands; i++) {
|
||||||
{
|
|
||||||
float value = s[i];
|
float value = s[i];
|
||||||
currentEnergy += value;
|
currentEnergy += value;
|
||||||
if ( value > maxValue )
|
if (value > maxValue) maxValue = value;
|
||||||
maxValue = value;
|
|
||||||
}
|
}
|
||||||
currentEnergy *= 100.0 / (float)bands;
|
currentEnergy *= 100.0 / (float)bands;
|
||||||
// emulate a peak detector: currentEnergy -> peakEnergy (3tau = 30 seconds)
|
// emulate a peak detector: currentEnergy -> peakEnergy (3tau = 30 seconds)
|
||||||
show.peakEnergy = 1.0 + (show.peakEnergy - 1.0) * exp(-show.dT / 10.0);
|
show.peakEnergy = 1.0 + (show.peakEnergy - 1.0) * exp(-show.dT / 10.0);
|
||||||
if ( currentEnergy > show.peakEnergy )
|
if (currentEnergy > show.peakEnergy) show.peakEnergy = currentEnergy;
|
||||||
show.peakEnergy = currentEnergy;
|
|
||||||
// check for silence
|
// check for silence
|
||||||
frame.silence = currentEnergy < 0.001;
|
frame.silence = currentEnergy < 0.001;
|
||||||
// normalize frame energy against peak energy and compute frame stats
|
// normalize frame energy against peak energy and compute frame stats
|
||||||
|
@ -240,14 +217,11 @@ void GLAnalyzer3::analyze( const Scope &s )
|
||||||
updateGL();
|
updateGL();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLAnalyzer3::paintGL()
|
void GLAnalyzer3::paintGL() {
|
||||||
{
|
|
||||||
// limit max dT to 0.05 and update color and scroll constants
|
// limit max dT to 0.05 and update color and scroll constants
|
||||||
if ( show.dT > 0.05 )
|
if (show.dT > 0.05) show.dT = 0.05;
|
||||||
show.dT = 0.05;
|
|
||||||
show.colorK += show.dT * 0.4;
|
show.colorK += show.dT * 0.4;
|
||||||
if ( show.colorK > 3.0 )
|
if (show.colorK > 3.0) show.colorK -= 3.0;
|
||||||
show.colorK -= 3.0;
|
|
||||||
show.gridScrollK += 0.2 * show.peakEnergy * show.dT;
|
show.gridScrollK += 0.2 * show.peakEnergy * show.dT;
|
||||||
|
|
||||||
// Switch to MODEL matrix and clear screen
|
// Switch to MODEL matrix and clear screen
|
||||||
|
@ -256,8 +230,7 @@ void GLAnalyzer3::paintGL()
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
|
||||||
// Draw scrolling grid
|
// Draw scrolling grid
|
||||||
if ( (show.gridEnergyK > 0.05) || (!frame.silence && frame.dEnergy < -0.3) )
|
if ((show.gridEnergyK > 0.05) || (!frame.silence && frame.dEnergy < -0.3)) {
|
||||||
{
|
|
||||||
show.gridEnergyK *= exp(-show.dT / 0.1);
|
show.gridEnergyK *= exp(-show.dT / 0.1);
|
||||||
if (-frame.dEnergy > show.gridEnergyK)
|
if (-frame.dEnergy > show.gridEnergyK)
|
||||||
show.gridEnergyK = -frame.dEnergy * 2.0;
|
show.gridEnergyK = -frame.dEnergy * 2.0;
|
||||||
|
@ -290,26 +263,19 @@ void GLAnalyzer3::paintGL()
|
||||||
glDisable(GL_TEXTURE_2D);
|
glDisable(GL_TEXTURE_2D);
|
||||||
glEnable(GL_BLEND);
|
glEnable(GL_BLEND);
|
||||||
Ball* ball = balls.first();
|
Ball* ball = balls.first();
|
||||||
for ( ; ball; ball = balls.next() )
|
for (; ball; ball = balls.next()) {
|
||||||
{
|
float color[3], angle = show.colorK;
|
||||||
float color[3],
|
|
||||||
angle = show.colorK;
|
|
||||||
// Rotate the color based on 'angle' value [0,3)
|
// Rotate the color based on 'angle' value [0,3)
|
||||||
if ( angle < 1.0 )
|
if (angle < 1.0) {
|
||||||
{
|
|
||||||
color[0] = ball->color[0] * (1 - angle) + ball->color[1] * angle;
|
color[0] = ball->color[0] * (1 - angle) + ball->color[1] * angle;
|
||||||
color[1] = ball->color[1] * (1 - angle) + ball->color[2] * angle;
|
color[1] = ball->color[1] * (1 - angle) + ball->color[2] * angle;
|
||||||
color[2] = ball->color[2] * (1 - angle) + ball->color[0] * angle;
|
color[2] = ball->color[2] * (1 - angle) + ball->color[0] * angle;
|
||||||
}
|
} else if (angle < 2.0) {
|
||||||
else if ( angle < 2.0 )
|
|
||||||
{
|
|
||||||
angle -= 1.0;
|
angle -= 1.0;
|
||||||
color[0] = ball->color[1] * (1 - angle) + ball->color[2] * angle;
|
color[0] = ball->color[1] * (1 - angle) + ball->color[2] * angle;
|
||||||
color[1] = ball->color[2] * (1 - angle) + ball->color[0] * angle;
|
color[1] = ball->color[2] * (1 - angle) + ball->color[0] * angle;
|
||||||
color[2] = ball->color[0] * (1 - angle) + ball->color[1] * angle;
|
color[2] = ball->color[0] * (1 - angle) + ball->color[1] * angle;
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
angle -= 2.0;
|
angle -= 2.0;
|
||||||
color[0] = ball->color[2] * (1 - angle) + ball->color[0] * angle;
|
color[0] = ball->color[2] * (1 - angle) + ball->color[0] * angle;
|
||||||
color[1] = ball->color[0] * (1 - angle) + ball->color[1] * angle;
|
color[1] = ball->color[0] * (1 - angle) + ball->color[1] * angle;
|
||||||
|
@ -330,22 +296,16 @@ void GLAnalyzer3::paintGL()
|
||||||
// Update physics of paddles
|
// Update physics of paddles
|
||||||
leftPaddle->updatePhysics(show.dT);
|
leftPaddle->updatePhysics(show.dT);
|
||||||
rightPaddle->updatePhysics(show.dT);
|
rightPaddle->updatePhysics(show.dT);
|
||||||
if ( !frame.silence )
|
if (!frame.silence) {
|
||||||
{
|
|
||||||
leftPaddle->impulse(frame.energy * 3.0 + frame.dEnergy * 6.0);
|
leftPaddle->impulse(frame.energy * 3.0 + frame.dEnergy * 6.0);
|
||||||
rightPaddle->impulse(-frame.energy * 3.0 - frame.dEnergy * 6.0);
|
rightPaddle->impulse(-frame.energy * 3.0 - frame.dEnergy * 6.0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLAnalyzer3::drawDot3s( float x, float y, float z, float size )
|
void GLAnalyzer3::drawDot3s(float x, float y, float z, float size) {
|
||||||
{
|
|
||||||
// Circular XY dot drawing functions
|
// Circular XY dot drawing functions
|
||||||
float sizeX = size * unitX,
|
float sizeX = size * unitX, sizeY = size * unitY, pXm = x - sizeX,
|
||||||
sizeY = size * unitY,
|
pXM = x + sizeX, pYm = y - sizeY, pYM = y + sizeY;
|
||||||
pXm = x - sizeX,
|
|
||||||
pXM = x + sizeX,
|
|
||||||
pYm = y - sizeY,
|
|
||||||
pYM = y + sizeY;
|
|
||||||
// Draw the Dot
|
// Draw the Dot
|
||||||
glBegin(GL_QUADS);
|
glBegin(GL_QUADS);
|
||||||
glTexCoord2f(0, 0); // Bottom Left
|
glTexCoord2f(0, 0); // Bottom Left
|
||||||
|
@ -359,13 +319,9 @@ void GLAnalyzer3::drawDot3s( float x, float y, float z, float size )
|
||||||
glEnd();
|
glEnd();
|
||||||
|
|
||||||
// Shadow XZ drawing functions
|
// Shadow XZ drawing functions
|
||||||
float sizeZ = size / 10.0,
|
float sizeZ = size / 10.0, pZm = z - sizeZ, pZM = z + sizeZ, currentColor[4];
|
||||||
pZm = z - sizeZ,
|
|
||||||
pZM = z + sizeZ,
|
|
||||||
currentColor[4];
|
|
||||||
glGetFloatv(GL_CURRENT_COLOR, currentColor);
|
glGetFloatv(GL_CURRENT_COLOR, currentColor);
|
||||||
float alpha = currentColor[3],
|
float alpha = currentColor[3], topSide = (y + 1) / 4,
|
||||||
topSide = (y + 1) / 4,
|
|
||||||
bottomSide = (1 - y) / 4;
|
bottomSide = (1 - y) / 4;
|
||||||
// Draw the top shadow
|
// Draw the top shadow
|
||||||
currentColor[3] = topSide * topSide * alpha;
|
currentColor[3] = topSide * topSide * alpha;
|
||||||
|
@ -395,8 +351,7 @@ void GLAnalyzer3::drawDot3s( float x, float y, float z, float size )
|
||||||
glEnd();
|
glEnd();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLAnalyzer3::drawHFace( float y )
|
void GLAnalyzer3::drawHFace(float y) {
|
||||||
{
|
|
||||||
glBegin(GL_TRIANGLE_STRIP);
|
glBegin(GL_TRIANGLE_STRIP);
|
||||||
glColor3f(0.0f, 0.1f, 0.2f);
|
glColor3f(0.0f, 0.1f, 0.2f);
|
||||||
glVertex3f(-1.0f, y, 0.0);
|
glVertex3f(-1.0f, y, 0.0);
|
||||||
|
@ -407,17 +362,14 @@ void GLAnalyzer3::drawHFace( float y )
|
||||||
glEnd();
|
glEnd();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLAnalyzer3::drawScrollGrid( float scroll, float color[4] )
|
void GLAnalyzer3::drawScrollGrid(float scroll, float color[4]) {
|
||||||
{
|
if (!gridTexture) return;
|
||||||
if ( !gridTexture )
|
|
||||||
return;
|
|
||||||
glMatrixMode(GL_TEXTURE);
|
glMatrixMode(GL_TEXTURE);
|
||||||
glLoadIdentity();
|
glLoadIdentity();
|
||||||
glTranslatef(0.0, -scroll, 0.0);
|
glTranslatef(0.0, -scroll, 0.0);
|
||||||
glMatrixMode(GL_MODELVIEW);
|
glMatrixMode(GL_MODELVIEW);
|
||||||
float backColor[4] = {1.0, 1.0, 1.0, 0.0};
|
float backColor[4] = {1.0, 1.0, 1.0, 0.0};
|
||||||
for ( int i = 0; i < 3; i++ )
|
for (int i = 0; i < 3; i++) backColor[i] = color[i];
|
||||||
backColor[ i ] = color[ i ];
|
|
||||||
glEnable(GL_TEXTURE_2D);
|
glEnable(GL_TEXTURE_2D);
|
||||||
glBindTexture(GL_TEXTURE_2D, gridTexture);
|
glBindTexture(GL_TEXTURE_2D, gridTexture);
|
||||||
glEnable(GL_BLEND);
|
glEnable(GL_BLEND);
|
||||||
|
@ -445,35 +397,30 @@ void GLAnalyzer3::drawScrollGrid( float scroll, float color[4] )
|
||||||
glMatrixMode(GL_MODELVIEW);
|
glMatrixMode(GL_MODELVIEW);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GLAnalyzer3::loadTexture( QString fileName, GLuint& textureID )
|
bool GLAnalyzer3::loadTexture(QString fileName, GLuint& textureID) {
|
||||||
{
|
|
||||||
// reset texture ID to the default EMPTY value
|
// reset texture ID to the default EMPTY value
|
||||||
textureID = 0;
|
textureID = 0;
|
||||||
|
|
||||||
// load image
|
// load image
|
||||||
QImage tmp;
|
QImage tmp;
|
||||||
if ( !tmp.load( fileName ) )
|
if (!tmp.load(fileName)) return false;
|
||||||
return false;
|
|
||||||
|
|
||||||
// convert it to suitable format (flipped RGBA)
|
// convert it to suitable format (flipped RGBA)
|
||||||
QImage texture = QGLWidget::convertToGLFormat(tmp);
|
QImage texture = QGLWidget::convertToGLFormat(tmp);
|
||||||
if ( texture.isNull() )
|
if (texture.isNull()) return false;
|
||||||
return false;
|
|
||||||
|
|
||||||
// get texture number and bind loaded image to that texture
|
// get texture number and bind loaded image to that texture
|
||||||
glGenTextures(1, &textureID);
|
glGenTextures(1, &textureID);
|
||||||
glBindTexture(GL_TEXTURE_2D, textureID);
|
glBindTexture(GL_TEXTURE_2D, textureID);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||||
glTexImage2D( GL_TEXTURE_2D, 0, 4, texture.width(), texture.height(),
|
glTexImage2D(GL_TEXTURE_2D, 0, 4, texture.width(), texture.height(), 0,
|
||||||
0, GL_RGBA, GL_UNSIGNED_BYTE, texture.bits() );
|
GL_RGBA, GL_UNSIGNED_BYTE, texture.bits());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLAnalyzer3::freeTexture( GLuint& textureID )
|
void GLAnalyzer3::freeTexture(GLuint& textureID) {
|
||||||
{
|
if (textureID > 0) glDeleteTextures(1, &textureID);
|
||||||
if ( textureID > 0 )
|
|
||||||
glDeleteTextures( 1, &textureID );
|
|
||||||
textureID = 0;
|
textureID = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,8 +29,7 @@ class QWidget;
|
||||||
class Ball;
|
class Ball;
|
||||||
class Paddle;
|
class Paddle;
|
||||||
|
|
||||||
class GLAnalyzer3 : public Analyzer::Base3D
|
class GLAnalyzer3 : public Analyzer::Base3D {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
GLAnalyzer3(QWidget*);
|
GLAnalyzer3(QWidget*);
|
||||||
~GLAnalyzer3();
|
~GLAnalyzer3();
|
||||||
|
|
|
@ -26,7 +26,6 @@
|
||||||
const char* NyanCatAnalyzer::kName = "Nyanalyzer cat";
|
const char* NyanCatAnalyzer::kName = "Nyanalyzer cat";
|
||||||
const float NyanCatAnalyzer::kPixelScale = 0.02f;
|
const float NyanCatAnalyzer::kPixelScale = 0.02f;
|
||||||
|
|
||||||
|
|
||||||
NyanCatAnalyzer::NyanCatAnalyzer(QWidget* parent)
|
NyanCatAnalyzer::NyanCatAnalyzer(QWidget* parent)
|
||||||
: Analyzer::Base(parent, 9),
|
: Analyzer::Base(parent, 9),
|
||||||
cat_(":/nyancat.png"),
|
cat_(":/nyancat.png"),
|
||||||
|
@ -36,24 +35,22 @@ NyanCatAnalyzer::NyanCatAnalyzer(QWidget* parent)
|
||||||
available_rainbow_width_(0),
|
available_rainbow_width_(0),
|
||||||
px_per_frame_(0),
|
px_per_frame_(0),
|
||||||
x_offset_(0),
|
x_offset_(0),
|
||||||
background_brush_(QColor(0x0f, 0x43, 0x73))
|
background_brush_(QColor(0x0f, 0x43, 0x73)) {
|
||||||
{
|
|
||||||
memset(history_, 0, sizeof(history_));
|
memset(history_, 0, sizeof(history_));
|
||||||
|
|
||||||
for (int i = 0; i < kRainbowBands; ++i) {
|
for (int i = 0; i < kRainbowBands; ++i) {
|
||||||
colors_[i] = QPen(QColor::fromHsv(i * 255 / kRainbowBands, 255, 255),
|
colors_[i] = QPen(QColor::fromHsv(i * 255 / kRainbowBands, 255, 255),
|
||||||
kCatHeight/kRainbowBands,
|
kCatHeight / kRainbowBands, Qt::SolidLine, Qt::FlatCap,
|
||||||
Qt::SolidLine, Qt::FlatCap, Qt::RoundJoin);
|
Qt::RoundJoin);
|
||||||
|
|
||||||
// pow constants computed so that
|
// pow constants computed so that
|
||||||
// | band_scale(0) | ~= .5 and | band_scale(5) | ~= 32
|
// | band_scale(0) | ~= .5 and | band_scale(5) | ~= 32
|
||||||
band_scale_[i] = -std::cos(M_PI * i / (kRainbowBands-1)) * 0.5 * std::pow(2.3, i);
|
band_scale_[i] =
|
||||||
|
-std::cos(M_PI * i / (kRainbowBands - 1)) * 0.5 * std::pow(2.3, i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NyanCatAnalyzer::transform(Scope& s) {
|
void NyanCatAnalyzer::transform(Scope& s) { m_fht->spectrum(&s.front()); }
|
||||||
m_fht->spectrum(&s.front());
|
|
||||||
}
|
|
||||||
|
|
||||||
void NyanCatAnalyzer::timerEvent(QTimerEvent* e) {
|
void NyanCatAnalyzer::timerEvent(QTimerEvent* e) {
|
||||||
if (e->timerId() == timer_id_) {
|
if (e->timerId() == timer_id_) {
|
||||||
|
@ -74,7 +71,8 @@ void NyanCatAnalyzer::resizeEvent(QResizeEvent* e) {
|
||||||
x_offset_ = px_per_frame_ * (kHistorySize - 1) - available_rainbow_width_;
|
x_offset_ = px_per_frame_ * (kHistorySize - 1) - available_rainbow_width_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NyanCatAnalyzer::analyze(QPainter& p, const Analyzer::Scope& s, bool new_frame) {
|
void NyanCatAnalyzer::analyze(QPainter& p, const Analyzer::Scope& s,
|
||||||
|
bool new_frame) {
|
||||||
// Discard the second half of the transform
|
// Discard the second half of the transform
|
||||||
const int scope_size = s.size() / 2;
|
const int scope_size = s.size() / 2;
|
||||||
|
|
||||||
|
@ -110,7 +108,8 @@ void NyanCatAnalyzer::analyze(QPainter& p, const Analyzer::Scope& s, bool new_fr
|
||||||
const float top_of_cat = float(height()) / 2 - float(kCatHeight) / 2;
|
const float top_of_cat = float(height()) / 2 - float(kCatHeight) / 2;
|
||||||
for (int band = 0; band < kRainbowBands; ++band) {
|
for (int band = 0; band < kRainbowBands; ++band) {
|
||||||
// Calculate the Y position of this band.
|
// Calculate the Y position of this band.
|
||||||
const float y = float(kCatHeight) / (kRainbowBands + 1) * (band + 0.5) + top_of_cat;
|
const float y =
|
||||||
|
float(kCatHeight) / (kRainbowBands + 1) * (band + 0.5) + top_of_cat;
|
||||||
|
|
||||||
// Add each point in the line.
|
// Add each point in the line.
|
||||||
for (int x = 0; x < kHistorySize; ++x) {
|
for (int x = 0; x < kHistorySize; ++x) {
|
||||||
|
@ -132,27 +131,32 @@ void NyanCatAnalyzer::analyze(QPainter& p, const Analyzer::Scope& s, bool new_fr
|
||||||
buffer_painter.setRenderHint(QPainter::Antialiasing);
|
buffer_painter.setRenderHint(QPainter::Antialiasing);
|
||||||
for (int band = kRainbowBands - 1; band >= 0; --band) {
|
for (int band = kRainbowBands - 1; band >= 0; --band) {
|
||||||
buffer_painter.setPen(colors_[band]);
|
buffer_painter.setPen(colors_[band]);
|
||||||
buffer_painter.drawPolyline(&polyline[band*kHistorySize], kHistorySize);
|
buffer_painter.drawPolyline(&polyline[band * kHistorySize],
|
||||||
buffer_painter.drawPolyline(&polyline[band*kHistorySize], kHistorySize);
|
kHistorySize);
|
||||||
|
buffer_painter.drawPolyline(&polyline[band * kHistorySize],
|
||||||
|
kHistorySize);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const int last_buffer = current_buffer_;
|
const int last_buffer = current_buffer_;
|
||||||
current_buffer_ = (current_buffer_ + 1) % 2;
|
current_buffer_ = (current_buffer_ + 1) % 2;
|
||||||
|
|
||||||
// We can just shuffle the buffer along a bit and draw the new frame's data.
|
// We can just shuffle the buffer along a bit and draw the new frame's
|
||||||
|
// data.
|
||||||
QPainter buffer_painter(&buffer_[current_buffer_]);
|
QPainter buffer_painter(&buffer_[current_buffer_]);
|
||||||
buffer_painter.setRenderHint(QPainter::Antialiasing);
|
buffer_painter.setRenderHint(QPainter::Antialiasing);
|
||||||
|
|
||||||
buffer_painter.drawPixmap(0, 0, buffer_[last_buffer],
|
buffer_painter.drawPixmap(
|
||||||
px_per_frame_, 0,
|
0, 0, buffer_[last_buffer], px_per_frame_, 0,
|
||||||
x_offset_ + available_rainbow_width_ - px_per_frame_, 0);
|
x_offset_ + available_rainbow_width_ - px_per_frame_, 0);
|
||||||
buffer_painter.fillRect(x_offset_ + available_rainbow_width_ - px_per_frame_, 0,
|
buffer_painter.fillRect(
|
||||||
|
x_offset_ + available_rainbow_width_ - px_per_frame_, 0,
|
||||||
kCatWidth - kRainbowOverlap + px_per_frame_, height(),
|
kCatWidth - kRainbowOverlap + px_per_frame_, height(),
|
||||||
background_brush_);
|
background_brush_);
|
||||||
|
|
||||||
for (int band = kRainbowBands - 1; band >= 0; --band) {
|
for (int band = kRainbowBands - 1; band >= 0; --band) {
|
||||||
buffer_painter.setPen(colors_[band]);
|
buffer_painter.setPen(colors_[band]);
|
||||||
buffer_painter.drawPolyline(&polyline[(band+1)*kHistorySize - 3], 3);
|
buffer_painter.drawPolyline(&polyline[(band + 1) * kHistorySize - 3],
|
||||||
|
3);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,8 +60,8 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
inline QRect CatDestRect() const {
|
inline QRect CatDestRect() const {
|
||||||
return QRect(width() - kCatWidth, (height() - kCatHeight) / 2,
|
return QRect(width() - kCatWidth, (height() - kCatHeight) / 2, kCatWidth,
|
||||||
kCatWidth, kCatHeight);
|
kCatHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline QRect SleepingCatDestRect() const {
|
inline QRect SleepingCatDestRect() const {
|
||||||
|
|
|
@ -15,21 +15,14 @@
|
||||||
|
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
|
|
||||||
const char* Sonogram::kName = QT_TRANSLATE_NOOP("AnalyzerContainer", "Sonogram");
|
const char* Sonogram::kName =
|
||||||
|
QT_TRANSLATE_NOOP("AnalyzerContainer", "Sonogram");
|
||||||
|
|
||||||
Sonogram::Sonogram(QWidget *parent) :
|
Sonogram::Sonogram(QWidget* parent) : Analyzer::Base(parent, 9) {}
|
||||||
Analyzer::Base(parent, 9)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
|
Sonogram::~Sonogram() {}
|
||||||
|
|
||||||
Sonogram::~Sonogram()
|
void Sonogram::resizeEvent(QResizeEvent* e) {
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void Sonogram::resizeEvent(QResizeEvent *e)
|
|
||||||
{
|
|
||||||
QWidget::resizeEvent(e);
|
QWidget::resizeEvent(e);
|
||||||
|
|
||||||
// only for gcc < 4.0
|
// only for gcc < 4.0
|
||||||
|
@ -41,9 +34,7 @@ void Sonogram::resizeEvent(QResizeEvent *e)
|
||||||
canvas_.fill(palette().color(QPalette::Background));
|
canvas_.fill(palette().color(QPalette::Background));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Sonogram::analyze(QPainter& p, const Scope& s, bool new_frame) {
|
||||||
void Sonogram::analyze(QPainter& p, const Scope &s, bool new_frame)
|
|
||||||
{
|
|
||||||
int x = width() - 1;
|
int x = width() - 1;
|
||||||
QColor c;
|
QColor c;
|
||||||
|
|
||||||
|
@ -64,8 +55,7 @@ void Sonogram::analyze(QPainter& p, const Scope &s, bool new_frame)
|
||||||
canvas_painter.setPen(c);
|
canvas_painter.setPen(c);
|
||||||
canvas_painter.drawPoint(x, y--);
|
canvas_painter.drawPoint(x, y--);
|
||||||
|
|
||||||
if (it < end)
|
if (it < end) ++it;
|
||||||
++it;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
canvas_painter.end();
|
canvas_painter.end();
|
||||||
|
@ -73,18 +63,13 @@ void Sonogram::analyze(QPainter& p, const Scope &s, bool new_frame)
|
||||||
p.drawPixmap(0, 0, canvas_);
|
p.drawPixmap(0, 0, canvas_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Sonogram::transform(Scope& scope) {
|
||||||
void Sonogram::transform(Scope &scope)
|
|
||||||
{
|
|
||||||
float* front = static_cast<float*>(&scope.front());
|
float* front = static_cast<float*>(&scope.front());
|
||||||
m_fht->power2(front);
|
m_fht->power2(front);
|
||||||
m_fht->scale(front, 1.0 / 256);
|
m_fht->scale(front, 1.0 / 256);
|
||||||
scope.resize(m_fht->size() / 2);
|
scope.resize(m_fht->size() / 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Sonogram::demo(QPainter& p) {
|
||||||
void Sonogram::demo(QPainter& p)
|
|
||||||
{
|
|
||||||
analyze(p, Scope(m_fht->size(), 0), new_frame_);
|
analyze(p, Scope(m_fht->size(), 0), new_frame_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,8 +20,7 @@
|
||||||
@author Melchior FRANZ
|
@author Melchior FRANZ
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class Sonogram : public Analyzer::Base
|
class Sonogram : public Analyzer::Base {
|
||||||
{
|
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
Q_INVOKABLE Sonogram(QWidget*);
|
Q_INVOKABLE Sonogram(QWidget*);
|
||||||
|
|
|
@ -12,44 +12,36 @@
|
||||||
|
|
||||||
#include "turbine.h"
|
#include "turbine.h"
|
||||||
|
|
||||||
const char* TurbineAnalyzer::kName = QT_TRANSLATE_NOOP("AnalyzerContainer", "Turbine");
|
const char* TurbineAnalyzer::kName =
|
||||||
|
QT_TRANSLATE_NOOP("AnalyzerContainer", "Turbine");
|
||||||
|
|
||||||
void TurbineAnalyzer::analyze( QPainter& p, const Scope &scope, bool new_frame)
|
void TurbineAnalyzer::analyze(QPainter& p, const Scope& scope, bool new_frame) {
|
||||||
{
|
|
||||||
float h;
|
float h;
|
||||||
const uint hd2 = height() / 2;
|
const uint hd2 = height() / 2;
|
||||||
const uint MAX_HEIGHT = hd2 - 1;
|
const uint MAX_HEIGHT = hd2 - 1;
|
||||||
|
|
||||||
for( uint i = 0, x = 0, y; i < BAND_COUNT; ++i, x += COLUMN_WIDTH+1 )
|
for (uint i = 0, x = 0, y; i < BAND_COUNT; ++i, x += COLUMN_WIDTH + 1) {
|
||||||
{
|
|
||||||
h = log10(scope[i] * 256.0) * F * 0.5;
|
h = log10(scope[i] * 256.0) * F * 0.5;
|
||||||
|
|
||||||
if( h > MAX_HEIGHT )
|
if (h > MAX_HEIGHT) h = MAX_HEIGHT;
|
||||||
h = MAX_HEIGHT;
|
|
||||||
|
|
||||||
if( h > bar_height[i] )
|
if (h > bar_height[i]) {
|
||||||
{
|
|
||||||
bar_height[i] = h;
|
bar_height[i] = h;
|
||||||
|
|
||||||
if( h > peak_height[i] )
|
if (h > peak_height[i]) {
|
||||||
{
|
|
||||||
peak_height[i] = h;
|
peak_height[i] = h;
|
||||||
peak_speed[i] = 0.01;
|
peak_speed[i] = 0.01;
|
||||||
}
|
} else
|
||||||
else goto peak_handling;
|
goto peak_handling;
|
||||||
}
|
} else {
|
||||||
else
|
if (bar_height[i] > 0.0) {
|
||||||
{
|
|
||||||
if( bar_height[i] > 0.0 )
|
|
||||||
{
|
|
||||||
bar_height[i] -= K_barHeight; // 1.4
|
bar_height[i] -= K_barHeight; // 1.4
|
||||||
if (bar_height[i] < 0.0) bar_height[i] = 0.0;
|
if (bar_height[i] < 0.0) bar_height[i] = 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
peak_handling:
|
peak_handling:
|
||||||
|
|
||||||
if( peak_height[i] > 0.0 )
|
if (peak_height[i] > 0.0) {
|
||||||
{
|
|
||||||
peak_height[i] -= peak_speed[i];
|
peak_height[i] -= peak_speed[i];
|
||||||
peak_speed[i] *= F_peakSpeed; // 1.12
|
peak_speed[i] *= F_peakSpeed; // 1.12
|
||||||
|
|
||||||
|
@ -58,7 +50,6 @@ void TurbineAnalyzer::analyze( QPainter& p, const Scope &scope, bool new_frame)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
y = hd2 - uint(bar_height[i]);
|
y = hd2 - uint(bar_height[i]);
|
||||||
p.drawPixmap(x + 1, y, barPixmap, 0, y, -1, -1);
|
p.drawPixmap(x + 1, y, barPixmap, 0, y, -1, -1);
|
||||||
p.drawPixmap(x + 1, hd2, barPixmap, 0, int(bar_height[i]), -1, -1);
|
p.drawPixmap(x + 1, hd2, barPixmap, 0, int(bar_height[i]), -1, -1);
|
||||||
|
|
|
@ -11,8 +11,7 @@
|
||||||
|
|
||||||
#include "boomanalyzer.h"
|
#include "boomanalyzer.h"
|
||||||
|
|
||||||
class TurbineAnalyzer : public BoomAnalyzer
|
class TurbineAnalyzer : public BoomAnalyzer {
|
||||||
{
|
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
Q_INVOKABLE TurbineAnalyzer(QWidget* parent) : BoomAnalyzer(parent) {}
|
Q_INVOKABLE TurbineAnalyzer(QWidget* parent) : BoomAnalyzer(parent) {}
|
||||||
|
|
|
@ -27,24 +27,21 @@ const char* Appearance::kBackgroundColor = "background-color";
|
||||||
|
|
||||||
const QPalette Appearance::kDefaultPalette = QPalette();
|
const QPalette Appearance::kDefaultPalette = QPalette();
|
||||||
|
|
||||||
Appearance::Appearance(QObject* parent)
|
Appearance::Appearance(QObject* parent) : QObject(parent) {
|
||||||
: QObject(parent)
|
|
||||||
{
|
|
||||||
QSettings s;
|
QSettings s;
|
||||||
s.beginGroup(kSettingsGroup);
|
s.beginGroup(kSettingsGroup);
|
||||||
QPalette p = QApplication::palette();
|
QPalette p = QApplication::palette();
|
||||||
background_color_ = s.value(kBackgroundColor,
|
background_color_ =
|
||||||
p.color(QPalette::WindowText)).value<QColor>();
|
s.value(kBackgroundColor, p.color(QPalette::WindowText)).value<QColor>();
|
||||||
foreground_color_ = s.value(kForegroundColor,
|
foreground_color_ =
|
||||||
p.color(QPalette::Window)).value<QColor>();
|
s.value(kForegroundColor, p.color(QPalette::Window)).value<QColor>();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Appearance::LoadUserTheme() {
|
void Appearance::LoadUserTheme() {
|
||||||
QSettings s;
|
QSettings s;
|
||||||
s.beginGroup(kSettingsGroup);
|
s.beginGroup(kSettingsGroup);
|
||||||
bool use_a_custom_color_set = s.value(kUseCustomColorSet).toBool();
|
bool use_a_custom_color_set = s.value(kUseCustomColorSet).toBool();
|
||||||
if (!use_a_custom_color_set)
|
if (!use_a_custom_color_set) return;
|
||||||
return;
|
|
||||||
|
|
||||||
ChangeForegroundColor(foreground_color_);
|
ChangeForegroundColor(foreground_color_);
|
||||||
ChangeBackgroundColor(background_color_);
|
ChangeBackgroundColor(background_color_);
|
||||||
|
|
|
@ -69,8 +69,7 @@ Application::Application(QObject* parent)
|
||||||
moodbar_loader_(nullptr),
|
moodbar_loader_(nullptr),
|
||||||
moodbar_controller_(nullptr),
|
moodbar_controller_(nullptr),
|
||||||
network_remote_(nullptr),
|
network_remote_(nullptr),
|
||||||
network_remote_helper_(nullptr)
|
network_remote_helper_(nullptr) {
|
||||||
{
|
|
||||||
tag_reader_client_ = new TagReaderClient(this);
|
tag_reader_client_ = new TagReaderClient(this);
|
||||||
MoveToNewThread(tag_reader_client_);
|
MoveToNewThread(tag_reader_client_);
|
||||||
tag_reader_client_->Start();
|
tag_reader_client_->Start();
|
||||||
|
@ -111,7 +110,8 @@ Application::Application(QObject* parent)
|
||||||
MoveToNewThread(network_remote_);
|
MoveToNewThread(network_remote_);
|
||||||
|
|
||||||
// This must be before libraray_->Init();
|
// This must be before libraray_->Init();
|
||||||
// In the constructor the helper waits for the signal PlaylistManagerInitialized
|
// In the constructor the helper waits for the signal
|
||||||
|
// PlaylistManagerInitialized
|
||||||
// to start the remote. Without the playlist manager clementine can
|
// to start the remote. Without the playlist manager clementine can
|
||||||
// crash when a client connects before the manager is initialized!
|
// crash when a client connects before the manager is initialized!
|
||||||
network_remote_helper_ = new NetworkRemoteHelper(this);
|
network_remote_helper_ = new NetworkRemoteHelper(this);
|
||||||
|
@ -125,19 +125,14 @@ Application::~Application() {
|
||||||
// It's important that the device manager is deleted before the database.
|
// It's important that the device manager is deleted before the database.
|
||||||
// Deleting the database deletes all objects that have been created in its
|
// Deleting the database deletes all objects that have been created in its
|
||||||
// thread, including some device library backends.
|
// thread, including some device library backends.
|
||||||
delete device_manager_; device_manager_ = nullptr;
|
delete device_manager_;
|
||||||
|
device_manager_ = nullptr;
|
||||||
|
|
||||||
foreach (QObject* object, objects_in_threads_) {
|
foreach(QObject * object, objects_in_threads_) { object->deleteLater(); }
|
||||||
object->deleteLater();
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (QThread* thread, threads_) {
|
foreach(QThread * thread, threads_) { thread->quit(); }
|
||||||
thread->quit();
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (QThread* thread, threads_) {
|
foreach(QThread * thread, threads_) { thread->wait(); }
|
||||||
thread->wait();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::MoveToNewThread(QObject* object) {
|
void Application::MoveToNewThread(QObject* object) {
|
||||||
|
@ -155,9 +150,7 @@ void Application::MoveToThread(QObject* object, QThread* thread) {
|
||||||
objects_in_threads_ << object;
|
objects_in_threads_ << object;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::AddError(const QString& message) {
|
void Application::AddError(const QString& message) { emit ErrorAdded(message); }
|
||||||
emit ErrorAdded(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Application::language_without_region() const {
|
QString Application::language_without_region() const {
|
||||||
const int underscore = language_name_.indexOf('_');
|
const int underscore = language_name_.indexOf('_');
|
||||||
|
@ -171,13 +164,9 @@ LibraryBackend* Application::library_backend() const {
|
||||||
return library()->backend();
|
return library()->backend();
|
||||||
}
|
}
|
||||||
|
|
||||||
LibraryModel* Application::library_model() const {
|
LibraryModel* Application::library_model() const { return library()->model(); }
|
||||||
return library()->model();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Application::ReloadSettings() {
|
void Application::ReloadSettings() { emit SettingsChanged(); }
|
||||||
emit SettingsChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Application::OpenSettingsDialogAtPage(SettingsDialog::Page page) {
|
void Application::OpenSettingsDialogAtPage(SettingsDialog::Page page) {
|
||||||
emit SettingsDialogRequested(page);
|
emit SettingsDialogRequested(page);
|
||||||
|
|
|
@ -47,7 +47,6 @@ class PodcastUpdater;
|
||||||
class TagReaderClient;
|
class TagReaderClient;
|
||||||
class TaskManager;
|
class TaskManager;
|
||||||
|
|
||||||
|
|
||||||
class Application : public QObject {
|
class Application : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
|
@ -58,7 +57,8 @@ public:
|
||||||
~Application();
|
~Application();
|
||||||
|
|
||||||
const QString& language_name() const { return language_name_; }
|
const QString& language_name() const { return language_name_; }
|
||||||
// Same as language_name, but remove the region code at the end if there is one
|
// Same as language_name, but remove the region code at the end if there is
|
||||||
|
// one
|
||||||
QString language_without_region() const;
|
QString language_without_region() const;
|
||||||
void set_language_name(const QString& name) { language_name_ = name; }
|
void set_language_name(const QString& name) { language_name_ = name; }
|
||||||
|
|
||||||
|
@ -83,7 +83,9 @@ public:
|
||||||
MoodbarLoader* moodbar_loader() const { return moodbar_loader_; }
|
MoodbarLoader* moodbar_loader() const { return moodbar_loader_; }
|
||||||
MoodbarController* moodbar_controller() const { return moodbar_controller_; }
|
MoodbarController* moodbar_controller() const { return moodbar_controller_; }
|
||||||
NetworkRemote* network_remote() const { return network_remote_; }
|
NetworkRemote* network_remote() const { return network_remote_; }
|
||||||
NetworkRemoteHelper* network_remote_helper() const { return network_remote_helper_; }
|
NetworkRemoteHelper* network_remote_helper() const {
|
||||||
|
return network_remote_helper_;
|
||||||
|
}
|
||||||
|
|
||||||
LibraryBackend* library_backend() const;
|
LibraryBackend* library_backend() const;
|
||||||
LibraryModel* library_model() const;
|
LibraryModel* library_model() const;
|
||||||
|
|
|
@ -9,17 +9,14 @@
|
||||||
|
|
||||||
const char* BackgroundStreams::kSettingsGroup = "BackgroundStreams";
|
const char* BackgroundStreams::kSettingsGroup = "BackgroundStreams";
|
||||||
const char* BackgroundStreams::kHypnotoadUrl = "hypnotoad:///";
|
const char* BackgroundStreams::kHypnotoadUrl = "hypnotoad:///";
|
||||||
const char* BackgroundStreams::kRainUrl = "http://data.clementine-player.org/rainymood";
|
const char* BackgroundStreams::kRainUrl =
|
||||||
|
"http://data.clementine-player.org/rainymood";
|
||||||
const char* BackgroundStreams::kEnterpriseUrl = "enterprise:///";
|
const char* BackgroundStreams::kEnterpriseUrl = "enterprise:///";
|
||||||
|
|
||||||
BackgroundStreams::BackgroundStreams(EngineBase* engine, QObject* parent)
|
BackgroundStreams::BackgroundStreams(EngineBase* engine, QObject* parent)
|
||||||
: QObject(parent),
|
: QObject(parent), engine_(engine) {}
|
||||||
engine_(engine) {
|
|
||||||
}
|
|
||||||
|
|
||||||
BackgroundStreams::~BackgroundStreams() {
|
BackgroundStreams::~BackgroundStreams() { SaveStreams(); }
|
||||||
SaveStreams();
|
|
||||||
}
|
|
||||||
|
|
||||||
void BackgroundStreams::LoadStreams() {
|
void BackgroundStreams::LoadStreams() {
|
||||||
QSettings s;
|
QSettings s;
|
||||||
|
@ -39,8 +36,7 @@ void BackgroundStreams::LoadStreams() {
|
||||||
int size = s.beginReadArray("streams");
|
int size = s.beginReadArray("streams");
|
||||||
for (int i = 0; i < size; ++i) {
|
for (int i = 0; i < size; ++i) {
|
||||||
s.setArrayIndex(i);
|
s.setArrayIndex(i);
|
||||||
AddStream(s.value("name").toString(),
|
AddStream(s.value("name").toString(), s.value("url").toUrl(),
|
||||||
s.value("url").toUrl(),
|
|
||||||
s.value("volume").toInt());
|
s.value("volume").toInt());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,8 +59,7 @@ void BackgroundStreams::SaveStreams() {
|
||||||
s.endArray();
|
s.endArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
void BackgroundStreams::AddStream(const QString& name,
|
void BackgroundStreams::AddStream(const QString& name, const QUrl& url,
|
||||||
const QUrl& url,
|
|
||||||
int volume) {
|
int volume) {
|
||||||
if (streams_.contains(name)) {
|
if (streams_.contains(name)) {
|
||||||
return;
|
return;
|
||||||
|
@ -134,7 +129,8 @@ bool BackgroundStreams::IsPlaying(const QString& name) const {
|
||||||
|
|
||||||
void BackgroundStreams::AddAction(const QString& name, QAction* action) {
|
void BackgroundStreams::AddAction(const QString& name, QAction* action) {
|
||||||
if (!streams_.contains(name)) {
|
if (!streams_.contains(name)) {
|
||||||
qLog(Error) << "Tried to add action for stream" << name << "which doesn't exist";
|
qLog(Error) << "Tried to add action for stream" << name
|
||||||
|
<< "which doesn't exist";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,12 +9,9 @@ template <typename T, typename D>
|
||||||
class BoundFutureWatcher : public QFutureWatcher<T>, boost::noncopyable {
|
class BoundFutureWatcher : public QFutureWatcher<T>, boost::noncopyable {
|
||||||
public:
|
public:
|
||||||
BoundFutureWatcher(const D& data, QObject* parent = 0)
|
BoundFutureWatcher(const D& data, QObject* parent = 0)
|
||||||
: QFutureWatcher<T>(parent),
|
: QFutureWatcher<T>(parent), data_(data) {}
|
||||||
data_(data) {
|
|
||||||
}
|
|
||||||
|
|
||||||
~BoundFutureWatcher() {
|
~BoundFutureWatcher() {}
|
||||||
}
|
|
||||||
|
|
||||||
const D& data() const { return data_; }
|
const D& data() const { return data_; }
|
||||||
|
|
||||||
|
|
|
@ -36,8 +36,7 @@ public:
|
||||||
int cache_duration_secs)
|
int cache_duration_secs)
|
||||||
: settings_group_(settings_group),
|
: settings_group_(settings_group),
|
||||||
name_(name),
|
name_(name),
|
||||||
cache_duration_secs_(cache_duration_secs) {
|
cache_duration_secs_(cache_duration_secs) {}
|
||||||
}
|
|
||||||
|
|
||||||
void Load() {
|
void Load() {
|
||||||
QSettings s;
|
QSettings s;
|
||||||
|
@ -76,12 +75,11 @@ public:
|
||||||
|
|
||||||
bool IsStale() const {
|
bool IsStale() const {
|
||||||
return last_updated_.isNull() ||
|
return last_updated_.isNull() ||
|
||||||
last_updated_.secsTo(QDateTime::currentDateTime()) > cache_duration_secs_;
|
last_updated_.secsTo(QDateTime::currentDateTime()) >
|
||||||
|
cache_duration_secs_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Sort() {
|
void Sort() { qSort(data_); }
|
||||||
qSort(data_);
|
|
||||||
}
|
|
||||||
|
|
||||||
const ListType& Data() const { return data_; }
|
const ListType& Data() const { return data_; }
|
||||||
operator ListType() const { return data_; }
|
operator ListType() const { return data_; }
|
||||||
|
|
|
@ -28,7 +28,6 @@
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
|
|
||||||
|
|
||||||
const char* CommandlineOptions::kHelpText =
|
const char* CommandlineOptions::kHelpText =
|
||||||
"%1: clementine [%2] [%3]\n"
|
"%1: clementine [%2] [%3]\n"
|
||||||
"\n"
|
"\n"
|
||||||
|
@ -62,8 +61,7 @@ const char* CommandlineOptions::kHelpText =
|
||||||
" --log-levels <levels> %29\n"
|
" --log-levels <levels> %29\n"
|
||||||
" --version %30\n";
|
" --version %30\n";
|
||||||
|
|
||||||
const char* CommandlineOptions::kVersionText =
|
const char* CommandlineOptions::kVersionText = "Clementine %1";
|
||||||
"Clementine %1";
|
|
||||||
|
|
||||||
CommandlineOptions::CommandlineOptions(int argc, char** argv)
|
CommandlineOptions::CommandlineOptions(int argc, char** argv)
|
||||||
: argc_(argc),
|
: argc_(argc),
|
||||||
|
@ -77,8 +75,7 @@ CommandlineOptions::CommandlineOptions(int argc, char** argv)
|
||||||
play_track_at_(-1),
|
play_track_at_(-1),
|
||||||
show_osd_(false),
|
show_osd_(false),
|
||||||
toggle_pretty_osd_(false),
|
toggle_pretty_osd_(false),
|
||||||
log_levels_(logging::kDefaultLogLevels)
|
log_levels_(logging::kDefaultLogLevels) {
|
||||||
{
|
|
||||||
#ifdef Q_OS_DARWIN
|
#ifdef Q_OS_DARWIN
|
||||||
// Remove -psn_xxx option that Mac passes when opened from Finder.
|
// Remove -psn_xxx option that Mac passes when opened from Finder.
|
||||||
RemoveArg("-psn", 1);
|
RemoveArg("-psn", 1);
|
||||||
|
@ -104,7 +101,6 @@ void CommandlineOptions::RemoveArg(const QString& starts_with, int count) {
|
||||||
bool CommandlineOptions::Parse() {
|
bool CommandlineOptions::Parse() {
|
||||||
static const struct option kOptions[] = {
|
static const struct option kOptions[] = {
|
||||||
{"help", no_argument, 0, 'h'},
|
{"help", no_argument, 0, 'h'},
|
||||||
|
|
||||||
{"play", no_argument, 0, 'p'},
|
{"play", no_argument, 0, 'p'},
|
||||||
{"play-pause", no_argument, 0, 't'},
|
{"play-pause", no_argument, 0, 't'},
|
||||||
{"pause", no_argument, 0, 'u'},
|
{"pause", no_argument, 0, 'u'},
|
||||||
|
@ -113,14 +109,12 @@ bool CommandlineOptions::Parse() {
|
||||||
{"next", no_argument, 0, 'f'},
|
{"next", no_argument, 0, 'f'},
|
||||||
{"volume", required_argument, 0, 'v'},
|
{"volume", required_argument, 0, 'v'},
|
||||||
{"volume-up", no_argument, 0, VolumeUp},
|
{"volume-up", no_argument, 0, VolumeUp},
|
||||||
|
|
||||||
{"volume-down", no_argument, 0, VolumeDown},
|
{"volume-down", no_argument, 0, VolumeDown},
|
||||||
{"volume-increase-by", required_argument, 0, VolumeIncreaseBy},
|
{"volume-increase-by", required_argument, 0, VolumeIncreaseBy},
|
||||||
{"volume-decrease-by", required_argument, 0, VolumeDecreaseBy},
|
{"volume-decrease-by", required_argument, 0, VolumeDecreaseBy},
|
||||||
{"seek-to", required_argument, 0, SeekTo},
|
{"seek-to", required_argument, 0, SeekTo},
|
||||||
{"seek-by", required_argument, 0, SeekBy},
|
{"seek-by", required_argument, 0, SeekBy},
|
||||||
{"restart-or-previous", no_argument, 0, RestartOrPrevious},
|
{"restart-or-previous", no_argument, 0, RestartOrPrevious},
|
||||||
|
|
||||||
{"append", no_argument, 0, 'a'},
|
{"append", no_argument, 0, 'a'},
|
||||||
{"load", no_argument, 0, 'l'},
|
{"load", no_argument, 0, 'l'},
|
||||||
{"play-track", required_argument, 0, 'k'},
|
{"play-track", required_argument, 0, 'k'},
|
||||||
|
@ -131,9 +125,7 @@ bool CommandlineOptions::Parse() {
|
||||||
{"verbose", no_argument, 0, Verbose},
|
{"verbose", no_argument, 0, Verbose},
|
||||||
{"log-levels", required_argument, 0, LogLevels},
|
{"log-levels", required_argument, 0, LogLevels},
|
||||||
{"version", no_argument, 0, Version},
|
{"version", no_argument, 0, Version},
|
||||||
|
{0, 0, 0, 0}};
|
||||||
{0, 0, 0, 0}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Parse the arguments
|
// Parse the arguments
|
||||||
bool ok = false;
|
bool ok = false;
|
||||||
|
@ -141,62 +133,97 @@ bool CommandlineOptions::Parse() {
|
||||||
int c = getopt_long(argc_, argv_, "hptusrfv:alk:oyg:", kOptions, nullptr);
|
int c = getopt_long(argc_, argv_, "hptusrfv:alk:oyg:", kOptions, nullptr);
|
||||||
|
|
||||||
// End of the options
|
// End of the options
|
||||||
if (c == -1)
|
if (c == -1) break;
|
||||||
break;
|
|
||||||
|
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case 'h': {
|
case 'h': {
|
||||||
QString translated_help_text = QString(kHelpText).arg(
|
QString translated_help_text =
|
||||||
tr("Usage"), tr("options"), tr("URL(s)"), tr("Player options"),
|
QString(kHelpText)
|
||||||
|
.arg(tr("Usage"), tr("options"), tr("URL(s)"),
|
||||||
|
tr("Player options"),
|
||||||
tr("Start the playlist currently playing"),
|
tr("Start the playlist currently playing"),
|
||||||
tr("Play if stopped, pause if playing"),
|
tr("Play if stopped, pause if playing"),
|
||||||
tr("Pause playback"),
|
tr("Pause playback"), tr("Stop playback"),
|
||||||
tr("Stop playback"),
|
tr("Skip backwards in playlist"))
|
||||||
tr("Skip backwards in playlist")).arg(
|
.arg(tr("Skip forwards in playlist"),
|
||||||
tr("Skip forwards in playlist"),
|
|
||||||
tr("Set the volume to <value> percent"),
|
tr("Set the volume to <value> percent"),
|
||||||
tr("Increase the volume by 4%"),
|
tr("Increase the volume by 4%"),
|
||||||
tr("Decrease the volume by 4%"),
|
tr("Decrease the volume by 4%"),
|
||||||
tr("Increase the volume by <value> percent"),
|
tr("Increase the volume by <value> percent"),
|
||||||
tr("Decrease the volume by <value> percent")).arg(
|
tr("Decrease the volume by <value> percent"))
|
||||||
tr("Seek the currently playing track to an absolute position"),
|
.arg(tr("Seek the currently playing track to an absolute "
|
||||||
tr("Seek the currently playing track by a relative amount"),
|
"position"),
|
||||||
tr("Restart the track, or play the previous track if within 8 seconds of start."),
|
tr("Seek the currently playing track by a relative "
|
||||||
|
"amount"),
|
||||||
|
tr("Restart the track, or play the previous track if "
|
||||||
|
"within 8 seconds of start."),
|
||||||
tr("Playlist options"),
|
tr("Playlist options"),
|
||||||
tr("Append files/URLs to the playlist"),
|
tr("Append files/URLs to the playlist"),
|
||||||
tr("Loads files/URLs, replacing current playlist"),
|
tr("Loads files/URLs, replacing current playlist"),
|
||||||
tr("Play the <n>th track in the playlist")).arg(
|
tr("Play the <n>th track in the playlist"))
|
||||||
tr("Other options"),
|
.arg(tr("Other options"), tr("Display the on-screen-display"),
|
||||||
tr("Display the on-screen-display"),
|
|
||||||
tr("Toggle visibility for the pretty on-screen-display"),
|
tr("Toggle visibility for the pretty on-screen-display"),
|
||||||
tr("Change the language"),
|
tr("Change the language"),
|
||||||
tr("Equivalent to --log-levels *:1"),
|
tr("Equivalent to --log-levels *:1"),
|
||||||
tr("Equivalent to --log-levels *:3"),
|
tr("Equivalent to --log-levels *:3"),
|
||||||
tr("Comma separated list of class:level, level is 0-3")).arg(
|
tr("Comma separated list of class:level, level is 0-3"))
|
||||||
tr("Print out version information"));
|
.arg(tr("Print out version information"));
|
||||||
|
|
||||||
std::cout << translated_help_text.toLocal8Bit().constData();
|
std::cout << translated_help_text.toLocal8Bit().constData();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'p': player_action_ = Player_Play; break;
|
case 'p':
|
||||||
case 't': player_action_ = Player_PlayPause; break;
|
player_action_ = Player_Play;
|
||||||
case 'u': player_action_ = Player_Pause; break;
|
break;
|
||||||
case 's': player_action_ = Player_Stop; break;
|
case 't':
|
||||||
case 'r': player_action_ = Player_Previous; break;
|
player_action_ = Player_PlayPause;
|
||||||
case 'f': player_action_ = Player_Next; break;
|
break;
|
||||||
case 'a': url_list_action_ = UrlList_Append; break;
|
case 'u':
|
||||||
case 'l': url_list_action_ = UrlList_Load; break;
|
player_action_ = Player_Pause;
|
||||||
case 'o': show_osd_ = true; break;
|
break;
|
||||||
case 'y': toggle_pretty_osd_ = true; break;
|
case 's':
|
||||||
case 'g': language_ = QString(optarg); break;
|
player_action_ = Player_Stop;
|
||||||
case VolumeUp: volume_modifier_ = +4; break;
|
break;
|
||||||
case VolumeDown: volume_modifier_ = -4; break;
|
case 'r':
|
||||||
case Quiet: log_levels_ = "1"; break;
|
player_action_ = Player_Previous;
|
||||||
case Verbose: log_levels_ = "3"; break;
|
break;
|
||||||
case LogLevels: log_levels_ = QString(optarg); break;
|
case 'f':
|
||||||
|
player_action_ = Player_Next;
|
||||||
|
break;
|
||||||
|
case 'a':
|
||||||
|
url_list_action_ = UrlList_Append;
|
||||||
|
break;
|
||||||
|
case 'l':
|
||||||
|
url_list_action_ = UrlList_Load;
|
||||||
|
break;
|
||||||
|
case 'o':
|
||||||
|
show_osd_ = true;
|
||||||
|
break;
|
||||||
|
case 'y':
|
||||||
|
toggle_pretty_osd_ = true;
|
||||||
|
break;
|
||||||
|
case 'g':
|
||||||
|
language_ = QString(optarg);
|
||||||
|
break;
|
||||||
|
case VolumeUp:
|
||||||
|
volume_modifier_ = +4;
|
||||||
|
break;
|
||||||
|
case VolumeDown:
|
||||||
|
volume_modifier_ = -4;
|
||||||
|
break;
|
||||||
|
case Quiet:
|
||||||
|
log_levels_ = "1";
|
||||||
|
break;
|
||||||
|
case Verbose:
|
||||||
|
log_levels_ = "3";
|
||||||
|
break;
|
||||||
|
case LogLevels:
|
||||||
|
log_levels_ = QString(optarg);
|
||||||
|
break;
|
||||||
case Version: {
|
case Version: {
|
||||||
QString version_text = QString(kVersionText).arg(CLEMENTINE_VERSION_DISPLAY);
|
QString version_text =
|
||||||
|
QString(kVersionText).arg(CLEMENTINE_VERSION_DISPLAY);
|
||||||
std::cout << version_text.toLocal8Bit().constData() << std::endl;
|
std::cout << version_text.toLocal8Bit().constData() << std::endl;
|
||||||
std::exit(0);
|
std::exit(0);
|
||||||
}
|
}
|
||||||
|
@ -254,15 +281,10 @@ bool CommandlineOptions::Parse() {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CommandlineOptions::is_empty() const {
|
bool CommandlineOptions::is_empty() const {
|
||||||
return player_action_ == Player_None &&
|
return player_action_ == Player_None && set_volume_ == -1 &&
|
||||||
set_volume_ == -1 &&
|
volume_modifier_ == 0 && seek_to_ == -1 && seek_by_ == 0 &&
|
||||||
volume_modifier_ == 0 &&
|
play_track_at_ == -1 && show_osd_ == false &&
|
||||||
seek_to_ == -1 &&
|
toggle_pretty_osd_ == false && urls_.isEmpty();
|
||||||
seek_by_ == 0 &&
|
|
||||||
play_track_at_ == -1 &&
|
|
||||||
show_osd_ == false &&
|
|
||||||
toggle_pretty_osd_ == false &&
|
|
||||||
urls_.isEmpty();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray CommandlineOptions::Serialize() const {
|
QByteArray CommandlineOptions::Serialize() const {
|
||||||
|
@ -290,17 +312,9 @@ QString CommandlineOptions::tr(const char *source_text) {
|
||||||
}
|
}
|
||||||
|
|
||||||
QDataStream& operator<<(QDataStream& s, const CommandlineOptions& a) {
|
QDataStream& operator<<(QDataStream& s, const CommandlineOptions& a) {
|
||||||
s << qint32(a.player_action_)
|
s << qint32(a.player_action_) << qint32(a.url_list_action_) << a.set_volume_
|
||||||
<< qint32(a.url_list_action_)
|
<< a.volume_modifier_ << a.seek_to_ << a.seek_by_ << a.play_track_at_
|
||||||
<< a.set_volume_
|
<< a.show_osd_ << a.urls_ << a.log_levels_ << a.toggle_pretty_osd_;
|
||||||
<< a.volume_modifier_
|
|
||||||
<< a.seek_to_
|
|
||||||
<< a.seek_by_
|
|
||||||
<< a.play_track_at_
|
|
||||||
<< a.show_osd_
|
|
||||||
<< a.urls_
|
|
||||||
<< a.log_levels_
|
|
||||||
<< a.toggle_pretty_osd_;
|
|
||||||
|
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
@ -308,17 +322,9 @@ QDataStream& operator<<(QDataStream& s, const CommandlineOptions& a) {
|
||||||
QDataStream& operator>>(QDataStream& s, CommandlineOptions& a) {
|
QDataStream& operator>>(QDataStream& s, CommandlineOptions& a) {
|
||||||
quint32 player_action = 0;
|
quint32 player_action = 0;
|
||||||
quint32 url_list_action = 0;
|
quint32 url_list_action = 0;
|
||||||
s >> player_action
|
s >> player_action >> url_list_action >> a.set_volume_ >>
|
||||||
>> url_list_action
|
a.volume_modifier_ >> a.seek_to_ >> a.seek_by_ >> a.play_track_at_ >>
|
||||||
>> a.set_volume_
|
a.show_osd_ >> a.urls_ >> a.log_levels_ >> a.toggle_pretty_osd_;
|
||||||
>> a.volume_modifier_
|
|
||||||
>> a.seek_to_
|
|
||||||
>> a.seek_by_
|
|
||||||
>> a.play_track_at_
|
|
||||||
>> a.show_osd_
|
|
||||||
>> a.urls_
|
|
||||||
>> a.log_levels_
|
|
||||||
>> a.toggle_pretty_osd_;
|
|
||||||
a.player_action_ = CommandlineOptions::PlayerAction(player_action);
|
a.player_action_ = CommandlineOptions::PlayerAction(player_action);
|
||||||
a.url_list_action_ = CommandlineOptions::UrlListAction(url_list_action);
|
a.url_list_action_ = CommandlineOptions::UrlListAction(url_list_action);
|
||||||
|
|
||||||
|
|
|
@ -34,10 +34,7 @@ class CommandlineOptions {
|
||||||
|
|
||||||
// Don't change the values or order, these get serialised and sent to
|
// Don't change the values or order, these get serialised and sent to
|
||||||
// possibly a different version of Clementine
|
// possibly a different version of Clementine
|
||||||
enum UrlListAction {
|
enum UrlListAction { UrlList_Append = 0, UrlList_Load = 1, };
|
||||||
UrlList_Append = 0,
|
|
||||||
UrlList_Load = 1,
|
|
||||||
};
|
|
||||||
enum PlayerAction {
|
enum PlayerAction {
|
||||||
Player_None = 0,
|
Player_None = 0,
|
||||||
Player_Play = 1,
|
Player_Play = 1,
|
||||||
|
@ -90,7 +87,6 @@ class CommandlineOptions {
|
||||||
void RemoveArg(const QString& starts_with, int count);
|
void RemoveArg(const QString& starts_with, int count);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
int argc_;
|
int argc_;
|
||||||
char** argv_;
|
char** argv_;
|
||||||
|
|
||||||
|
|
|
@ -35,8 +35,8 @@
|
||||||
#include "third_party/lss/linux_syscall_support.h"
|
#include "third_party/lss/linux_syscall_support.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
const char* CrashSender::kUploadURL =
|
||||||
const char* CrashSender::kUploadURL = "http://crashes.clementine-player.org/getuploadurl";
|
"http://crashes.clementine-player.org/getuploadurl";
|
||||||
const char* CrashReporting::kSendCrashReportOption = "--send-crash-report";
|
const char* CrashReporting::kSendCrashReportOption = "--send-crash-report";
|
||||||
char* CrashReporting::sPath = nullptr;
|
char* CrashReporting::sPath = nullptr;
|
||||||
|
|
||||||
|
@ -45,11 +45,9 @@ char* CrashReporting::sPath = nullptr;
|
||||||
CrashReporting::CrashReporting()
|
CrashReporting::CrashReporting()
|
||||||
: handler_(new google_breakpad::ExceptionHandler(
|
: handler_(new google_breakpad::ExceptionHandler(
|
||||||
QDir::tempPath().toLocal8Bit().constData(), nullptr,
|
QDir::tempPath().toLocal8Bit().constData(), nullptr,
|
||||||
CrashReporting::Handler, this, true)) {
|
CrashReporting::Handler, this, true)) {}
|
||||||
}
|
|
||||||
|
|
||||||
CrashReporting::~CrashReporting() {
|
CrashReporting::~CrashReporting() {}
|
||||||
}
|
|
||||||
|
|
||||||
bool CrashReporting::SendCrashReport(int argc, char** argv) {
|
bool CrashReporting::SendCrashReport(int argc, char** argv) {
|
||||||
if (argc != 4 || strcmp(argv[1], kSendCrashReportOption) != 0) {
|
if (argc != 4 || strcmp(argv[1], kSendCrashReportOption) != 0) {
|
||||||
|
@ -76,21 +74,21 @@ void CrashReporting::Print(const char* message) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CrashReporting::Handler(const char* dump_path,
|
bool CrashReporting::Handler(const char* dump_path, const char* minidump_id,
|
||||||
const char* minidump_id,
|
void* context, bool succeeded) {
|
||||||
void* context,
|
|
||||||
bool succeeded) {
|
|
||||||
Print("Clementine has crashed! A crash report has been saved to:\n ");
|
Print("Clementine has crashed! A crash report has been saved to:\n ");
|
||||||
Print(dump_path);
|
Print(dump_path);
|
||||||
Print("/");
|
Print("/");
|
||||||
Print(minidump_id);
|
Print(minidump_id);
|
||||||
Print("\n\nPlease send this to the developers so they can fix the problem:\n"
|
Print(
|
||||||
|
"\n\nPlease send this to the developers so they can fix the problem:\n"
|
||||||
" http://code.google.com/p/clementine-player/issues/entry\n\n");
|
" http://code.google.com/p/clementine-player/issues/entry\n\n");
|
||||||
|
|
||||||
if (sPath) {
|
if (sPath) {
|
||||||
// We know the path to clementine, so exec it again to prompt the user to
|
// We know the path to clementine, so exec it again to prompt the user to
|
||||||
// upload the report.
|
// upload the report.
|
||||||
const char* argv[] = {sPath, kSendCrashReportOption, dump_path, minidump_id, nullptr};
|
const char* argv[] = {sPath, kSendCrashReportOption, dump_path, minidump_id,
|
||||||
|
nullptr};
|
||||||
|
|
||||||
sys_execv(sPath, argv);
|
sys_execv(sPath, argv);
|
||||||
}
|
}
|
||||||
|
@ -102,8 +100,7 @@ CrashSender::CrashSender(const QString& path)
|
||||||
: network_(new QNetworkAccessManager(this)),
|
: network_(new QNetworkAccessManager(this)),
|
||||||
path_(path),
|
path_(path),
|
||||||
file_(new QFile(path_, this)),
|
file_(new QFile(path_, this)),
|
||||||
progress_(nullptr) {
|
progress_(nullptr) {}
|
||||||
}
|
|
||||||
|
|
||||||
bool CrashSender::Start() {
|
bool CrashSender::Start() {
|
||||||
if (!file_->open(QIODevice::ReadOnly)) {
|
if (!file_->open(QIODevice::ReadOnly)) {
|
||||||
|
@ -112,9 +109,12 @@ bool CrashSender::Start() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// No tr() here.
|
// No tr() here.
|
||||||
QMessageBox prompt(QMessageBox::Critical, "Clementine has crashed!", QString(
|
QMessageBox prompt(QMessageBox::Critical, "Clementine has crashed!",
|
||||||
"A crash report has been created and saved to '%1'. With your permission "
|
QString(
|
||||||
"it can be automatically sent to our server so the developers can find "
|
"A crash report has been created and saved to '%1'. "
|
||||||
|
"With your permission "
|
||||||
|
"it can be automatically sent to our server so the "
|
||||||
|
"developers can find "
|
||||||
"out what happened.").arg(path_));
|
"out what happened.").arg(path_));
|
||||||
prompt.addButton("Don't send", QMessageBox::RejectRole);
|
prompt.addButton("Don't send", QMessageBox::RejectRole);
|
||||||
prompt.addButton("Send crash report", QMessageBox::AcceptRole);
|
prompt.addButton("Send crash report", QMessageBox::AcceptRole);
|
||||||
|
@ -141,7 +141,8 @@ void CrashSender::RedirectFinished() {
|
||||||
|
|
||||||
reply->deleteLater();
|
reply->deleteLater();
|
||||||
|
|
||||||
QUrl url = reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
|
QUrl url =
|
||||||
|
reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
|
||||||
if (!url.isValid()) {
|
if (!url.isValid()) {
|
||||||
progress_->close();
|
progress_->close();
|
||||||
return;
|
return;
|
||||||
|
@ -160,14 +161,17 @@ void CrashSender::RedirectFinished() {
|
||||||
}
|
}
|
||||||
|
|
||||||
QNetworkRequest req(url);
|
QNetworkRequest req(url);
|
||||||
req.setHeader(QNetworkRequest::ContentTypeHeader, "multipart/form-data; boundary=" + boundary);
|
req.setHeader(QNetworkRequest::ContentTypeHeader,
|
||||||
|
"multipart/form-data; boundary=" + boundary);
|
||||||
|
|
||||||
// Construct the multipart/form-data
|
// Construct the multipart/form-data
|
||||||
QByteArray form_data;
|
QByteArray form_data;
|
||||||
form_data.reserve(file_data.size() + 1024);
|
form_data.reserve(file_data.size() + 1024);
|
||||||
form_data.append("--");
|
form_data.append("--");
|
||||||
form_data.append(boundary);
|
form_data.append(boundary);
|
||||||
form_data.append("\nContent-Disposition: form-data; name=\"data\"; filename=\"data.dmp\"\n");
|
form_data.append(
|
||||||
|
"\nContent-Disposition: form-data; name=\"data\"; "
|
||||||
|
"filename=\"data.dmp\"\n");
|
||||||
form_data.append("Content-Type: application/octet-stream\n\n");
|
form_data.append("Content-Type: application/octet-stream\n\n");
|
||||||
form_data.append(file_data);
|
form_data.append(file_data);
|
||||||
form_data.append("\n--");
|
form_data.append("\n--");
|
||||||
|
@ -178,13 +182,12 @@ void CrashSender::RedirectFinished() {
|
||||||
|
|
||||||
// Upload the data
|
// Upload the data
|
||||||
reply = network_->post(req, form_data);
|
reply = network_->post(req, form_data);
|
||||||
connect(reply, SIGNAL(uploadProgress(qint64,qint64)), SLOT(UploadProgress(qint64)));
|
connect(reply, SIGNAL(uploadProgress(qint64, qint64)),
|
||||||
|
SLOT(UploadProgress(qint64)));
|
||||||
connect(reply, SIGNAL(finished()), progress_, SLOT(close()));
|
connect(reply, SIGNAL(finished()), progress_, SLOT(close()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void CrashSender::UploadProgress(qint64 bytes) {
|
void CrashSender::UploadProgress(qint64 bytes) { progress_->setValue(bytes); }
|
||||||
progress_->setValue(bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
#else // HAVE_BREAKPAD
|
#else // HAVE_BREAKPAD
|
||||||
|
|
||||||
|
@ -192,17 +195,12 @@ namespace google_breakpad {
|
||||||
class ExceptionHandler {};
|
class ExceptionHandler {};
|
||||||
}
|
}
|
||||||
|
|
||||||
CrashReporting::CrashReporting() {
|
CrashReporting::CrashReporting() {}
|
||||||
}
|
|
||||||
|
|
||||||
CrashReporting::~CrashReporting() {
|
CrashReporting::~CrashReporting() {}
|
||||||
}
|
|
||||||
|
|
||||||
bool CrashReporting::SendCrashReport(int, char**) {
|
bool CrashReporting::SendCrashReport(int, char**) { return false; }
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CrashReporting::SetApplicationPath(const QString&) {
|
void CrashReporting::SetApplicationPath(const QString&) {}
|
||||||
}
|
|
||||||
|
|
||||||
#endif // HAVE_BREAKPAD
|
#endif // HAVE_BREAKPAD
|
||||||
|
|
|
@ -30,7 +30,6 @@ namespace google_breakpad {
|
||||||
class ExceptionHandler;
|
class ExceptionHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Wraps google_breakpad::ExceptionHandler - while an instance of this class
|
// Wraps google_breakpad::ExceptionHandler - while an instance of this class
|
||||||
// is alive crashes will be handled.
|
// is alive crashes will be handled.
|
||||||
class CrashReporting {
|
class CrashReporting {
|
||||||
|
@ -53,10 +52,8 @@ private:
|
||||||
static void Print(const char* message);
|
static void Print(const char* message);
|
||||||
|
|
||||||
// Breakpad callback.
|
// Breakpad callback.
|
||||||
static bool Handler(const char* dump_path,
|
static bool Handler(const char* dump_path, const char* minidump_id,
|
||||||
const char* minidump_id,
|
void* context, bool succeeded);
|
||||||
void* context,
|
|
||||||
bool succeeded);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Q_DISABLE_COPY(CrashReporting);
|
Q_DISABLE_COPY(CrashReporting);
|
||||||
|
@ -67,7 +64,6 @@ private:
|
||||||
std::unique_ptr<google_breakpad::ExceptionHandler> handler_;
|
std::unique_ptr<google_breakpad::ExceptionHandler> handler_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// Asks the user if he wants to send a crash report, and displays a progress
|
// Asks the user if he wants to send a crash report, and displays a progress
|
||||||
// dialog while uploading it if he does.
|
// dialog while uploading it if he does.
|
||||||
class CrashSender : public QObject {
|
class CrashSender : public QObject {
|
||||||
|
|
|
@ -46,15 +46,11 @@ int Database::sNextConnectionId = 1;
|
||||||
QMutex Database::sNextConnectionIdMutex;
|
QMutex Database::sNextConnectionIdMutex;
|
||||||
|
|
||||||
Database::Token::Token(const QString& token, int start, int end)
|
Database::Token::Token(const QString& token, int start, int end)
|
||||||
: token(token),
|
: token(token), start_offset(start), end_offset(end) {}
|
||||||
start_offset(start),
|
|
||||||
end_offset(end) {
|
|
||||||
}
|
|
||||||
|
|
||||||
struct sqlite3_tokenizer_module {
|
struct sqlite3_tokenizer_module {
|
||||||
int iVersion;
|
int iVersion;
|
||||||
int (*xCreate)(
|
int (*xCreate)(int argc, /* Size of argv array */
|
||||||
int argc, /* Size of argv array */
|
|
||||||
const char* const* argv, /* Tokenizer argument strings */
|
const char* const* argv, /* Tokenizer argument strings */
|
||||||
sqlite3_tokenizer** ppTokenizer); /* OUT: Created tokenizer */
|
sqlite3_tokenizer** ppTokenizer); /* OUT: Created tokenizer */
|
||||||
|
|
||||||
|
@ -87,24 +83,22 @@ struct sqlite3_tokenizer_cursor {
|
||||||
|
|
||||||
sqlite3_tokenizer_module* Database::sFTSTokenizer = nullptr;
|
sqlite3_tokenizer_module* Database::sFTSTokenizer = nullptr;
|
||||||
|
|
||||||
|
int Database::FTSCreate(int argc, const char* const* argv,
|
||||||
int Database::FTSCreate(int argc, const char* const* argv, sqlite3_tokenizer** tokenizer) {
|
sqlite3_tokenizer** tokenizer) {
|
||||||
*tokenizer = reinterpret_cast<sqlite3_tokenizer*>(new UnicodeTokenizer);
|
*tokenizer = reinterpret_cast<sqlite3_tokenizer*>(new UnicodeTokenizer);
|
||||||
|
|
||||||
return SQLITE_OK;
|
return SQLITE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Database::FTSDestroy(sqlite3_tokenizer* tokenizer) {
|
int Database::FTSDestroy(sqlite3_tokenizer* tokenizer) {
|
||||||
UnicodeTokenizer* real_tokenizer = reinterpret_cast<UnicodeTokenizer*>(tokenizer);
|
UnicodeTokenizer* real_tokenizer =
|
||||||
|
reinterpret_cast<UnicodeTokenizer*>(tokenizer);
|
||||||
delete real_tokenizer;
|
delete real_tokenizer;
|
||||||
return SQLITE_OK;
|
return SQLITE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Database::FTSOpen(
|
int Database::FTSOpen(sqlite3_tokenizer* pTokenizer, const char* input,
|
||||||
sqlite3_tokenizer* pTokenizer,
|
int bytes, sqlite3_tokenizer_cursor** cursor) {
|
||||||
const char* input,
|
|
||||||
int bytes,
|
|
||||||
sqlite3_tokenizer_cursor** cursor) {
|
|
||||||
UnicodeTokenizerCursor* new_cursor = new UnicodeTokenizerCursor;
|
UnicodeTokenizerCursor* new_cursor = new UnicodeTokenizerCursor;
|
||||||
new_cursor->pTokenizer = pTokenizer;
|
new_cursor->pTokenizer = pTokenizer;
|
||||||
new_cursor->position = 0;
|
new_cursor->position = 0;
|
||||||
|
@ -163,20 +157,18 @@ int Database::FTSOpen(
|
||||||
}
|
}
|
||||||
|
|
||||||
int Database::FTSClose(sqlite3_tokenizer_cursor* cursor) {
|
int Database::FTSClose(sqlite3_tokenizer_cursor* cursor) {
|
||||||
UnicodeTokenizerCursor* real_cursor = reinterpret_cast<UnicodeTokenizerCursor*>(cursor);
|
UnicodeTokenizerCursor* real_cursor =
|
||||||
|
reinterpret_cast<UnicodeTokenizerCursor*>(cursor);
|
||||||
delete real_cursor;
|
delete real_cursor;
|
||||||
|
|
||||||
return SQLITE_OK;
|
return SQLITE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Database::FTSNext(
|
int Database::FTSNext(sqlite3_tokenizer_cursor* cursor, const char** token,
|
||||||
sqlite3_tokenizer_cursor* cursor,
|
int* bytes, int* start_offset, int* end_offset,
|
||||||
const char** token,
|
|
||||||
int* bytes,
|
|
||||||
int* start_offset,
|
|
||||||
int* end_offset,
|
|
||||||
int* position) {
|
int* position) {
|
||||||
UnicodeTokenizerCursor* real_cursor = reinterpret_cast<UnicodeTokenizerCursor*>(cursor);
|
UnicodeTokenizerCursor* real_cursor =
|
||||||
|
reinterpret_cast<UnicodeTokenizerCursor*>(cursor);
|
||||||
|
|
||||||
QList<Token> tokens = real_cursor->tokens;
|
QList<Token> tokens = real_cursor->tokens;
|
||||||
if (real_cursor->position >= tokens.size()) {
|
if (real_cursor->position >= tokens.size()) {
|
||||||
|
@ -196,7 +188,6 @@ int Database::FTSNext(
|
||||||
return SQLITE_OK;
|
return SQLITE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Database::StaticInit() {
|
void Database::StaticInit() {
|
||||||
sFTSTokenizer = new sqlite3_tokenizer_module;
|
sFTSTokenizer = new sqlite3_tokenizer_module;
|
||||||
sFTSTokenizer->iVersion = 0;
|
sFTSTokenizer->iVersion = 0;
|
||||||
|
@ -208,21 +199,21 @@ void Database::StaticInit() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Database::Database(Application* app, QObject* parent, const QString& database_name)
|
Database::Database(Application* app, QObject* parent,
|
||||||
|
const QString& database_name)
|
||||||
: QObject(parent),
|
: QObject(parent),
|
||||||
app_(app),
|
app_(app),
|
||||||
mutex_(QMutex::Recursive),
|
mutex_(QMutex::Recursive),
|
||||||
injected_database_name_(database_name),
|
injected_database_name_(database_name),
|
||||||
query_hash_(0),
|
query_hash_(0),
|
||||||
startup_schema_version_(-1)
|
startup_schema_version_(-1) {
|
||||||
{
|
|
||||||
{
|
{
|
||||||
QMutexLocker l(&sNextConnectionIdMutex);
|
QMutexLocker l(&sNextConnectionIdMutex);
|
||||||
connection_id_ = sNextConnectionId++;
|
connection_id_ = sNextConnectionId++;
|
||||||
}
|
}
|
||||||
|
|
||||||
directory_ = QDir::toNativeSeparators(
|
directory_ =
|
||||||
Utilities::GetConfigPath(Utilities::Path_Root));
|
QDir::toNativeSeparators(Utilities::GetConfigPath(Utilities::Path_Root));
|
||||||
|
|
||||||
attached_databases_["jamendo"] = AttachedDatabase(
|
attached_databases_["jamendo"] = AttachedDatabase(
|
||||||
directory_ + "/jamendo.db", ":/schema/jamendo.sql", false);
|
directory_ + "/jamendo.db", ":/schema/jamendo.sql", false);
|
||||||
|
@ -241,8 +232,7 @@ QSqlDatabase Database::Connect() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString connection_id =
|
const QString connection_id = QString("%1_thread_%2").arg(connection_id_).arg(
|
||||||
QString("%1_thread_%2").arg(connection_id_).arg(
|
|
||||||
reinterpret_cast<quint64>(QThread::currentThread()));
|
reinterpret_cast<quint64>(QThread::currentThread()));
|
||||||
|
|
||||||
// Try to find an existing connection for this thread
|
// Try to find an existing connection for this thread
|
||||||
|
@ -269,8 +259,9 @@ QSqlDatabase Database::Connect() {
|
||||||
{
|
{
|
||||||
QSqlQuery set_fts_tokenizer("SELECT fts3_tokenizer(:name, :pointer)", db);
|
QSqlQuery set_fts_tokenizer("SELECT fts3_tokenizer(:name, :pointer)", db);
|
||||||
set_fts_tokenizer.bindValue(":name", "unicode");
|
set_fts_tokenizer.bindValue(":name", "unicode");
|
||||||
set_fts_tokenizer.bindValue(":pointer", QByteArray(
|
set_fts_tokenizer.bindValue(
|
||||||
reinterpret_cast<const char*>(&sFTSTokenizer), sizeof(&sFTSTokenizer)));
|
":pointer", QByteArray(reinterpret_cast<const char*>(&sFTSTokenizer),
|
||||||
|
sizeof(&sFTSTokenizer)));
|
||||||
if (!set_fts_tokenizer.exec()) {
|
if (!set_fts_tokenizer.exec()) {
|
||||||
qLog(Warning) << "Couldn't register FTS3 tokenizer";
|
qLog(Warning) << "Couldn't register FTS3 tokenizer";
|
||||||
}
|
}
|
||||||
|
@ -288,15 +279,15 @@ QSqlDatabase Database::Connect() {
|
||||||
for (const QString& key : attached_databases_.keys()) {
|
for (const QString& key : attached_databases_.keys()) {
|
||||||
QString filename = attached_databases_[key].filename_;
|
QString filename = attached_databases_[key].filename_;
|
||||||
|
|
||||||
if (!injected_database_name_.isNull())
|
if (!injected_database_name_.isNull()) filename = injected_database_name_;
|
||||||
filename = injected_database_name_;
|
|
||||||
|
|
||||||
// Attach the db
|
// Attach the db
|
||||||
QSqlQuery q("ATTACH DATABASE :filename AS :alias", db);
|
QSqlQuery q("ATTACH DATABASE :filename AS :alias", db);
|
||||||
q.bindValue(":filename", filename);
|
q.bindValue(":filename", filename);
|
||||||
q.bindValue(":alias", key);
|
q.bindValue(":alias", key);
|
||||||
if (!q.exec()) {
|
if (!q.exec()) {
|
||||||
qFatal("Couldn't attach external database '%s'", key.toAscii().constData());
|
qFatal("Couldn't attach external database '%s'",
|
||||||
|
key.toAscii().constData());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -311,8 +302,10 @@ QSqlDatabase Database::Connect() {
|
||||||
attached_databases_[key].schema_.isEmpty())
|
attached_databases_[key].schema_.isEmpty())
|
||||||
continue;
|
continue;
|
||||||
// Find out if there are any tables in this database
|
// Find out if there are any tables in this database
|
||||||
QSqlQuery q(QString("SELECT ROWID FROM %1.sqlite_master"
|
QSqlQuery q(QString(
|
||||||
" WHERE type='table'").arg(key), db);
|
"SELECT ROWID FROM %1.sqlite_master"
|
||||||
|
" WHERE type='table'").arg(key),
|
||||||
|
db);
|
||||||
if (!q.exec() || !q.next()) {
|
if (!q.exec() || !q.next()) {
|
||||||
q.finish();
|
q.finish();
|
||||||
ExecSchemaCommandsFromFile(db, attached_databases_[key].schema_, 0);
|
ExecSchemaCommandsFromFile(db, attached_databases_[key].schema_, 0);
|
||||||
|
@ -327,8 +320,7 @@ void Database::UpdateMainSchema(QSqlDatabase* db) {
|
||||||
int schema_version = 0;
|
int schema_version = 0;
|
||||||
{
|
{
|
||||||
QSqlQuery q("SELECT version FROM schema_version", *db);
|
QSqlQuery q("SELECT version FROM schema_version", *db);
|
||||||
if (q.next())
|
if (q.next()) schema_version = q.value(0).toInt();
|
||||||
schema_version = q.value(0).toInt();
|
|
||||||
// Implicit invocation of ~QSqlQuery() when leaving the scope
|
// Implicit invocation of ~QSqlQuery() when leaving the scope
|
||||||
// to release any remaining database locks!
|
// to release any remaining database locks!
|
||||||
}
|
}
|
||||||
|
@ -336,7 +328,8 @@ void Database::UpdateMainSchema(QSqlDatabase* db) {
|
||||||
startup_schema_version_ = schema_version;
|
startup_schema_version_ = schema_version;
|
||||||
|
|
||||||
if (schema_version > kSchemaVersion) {
|
if (schema_version > kSchemaVersion) {
|
||||||
qLog(Warning) << "The database schema (version" << schema_version << ") is newer than I was expecting";
|
qLog(Warning) << "The database schema (version" << schema_version
|
||||||
|
<< ") is newer than I was expecting";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (schema_version < kSchemaVersion) {
|
if (schema_version < kSchemaVersion) {
|
||||||
|
@ -394,7 +387,8 @@ void Database::AttachDatabaseOnDbConnection(const QString &database_name,
|
||||||
q.bindValue(":filename", database.filename_);
|
q.bindValue(":filename", database.filename_);
|
||||||
q.bindValue(":alias", database_name);
|
q.bindValue(":alias", database_name);
|
||||||
if (!q.exec()) {
|
if (!q.exec()) {
|
||||||
qFatal("Couldn't attach external database '%s'", database_name.toAscii().constData());
|
qFatal("Couldn't attach external database '%s'",
|
||||||
|
database_name.toAscii().constData());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -434,20 +428,22 @@ void Database::UpdateDatabaseSchema(int version, QSqlDatabase &db) {
|
||||||
UrlEncodeFilenameColumn(table, db);
|
UrlEncodeFilenameColumn(table, db);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
qLog(Debug) << "Applying database schema update" << version
|
qLog(Debug) << "Applying database schema update" << version << "from"
|
||||||
<< "from" << filename;
|
<< filename;
|
||||||
ExecSchemaCommandsFromFile(db, filename, version - 1, true);
|
ExecSchemaCommandsFromFile(db, filename, version - 1, true);
|
||||||
t.Commit();
|
t.Commit();
|
||||||
} else {
|
} else {
|
||||||
qLog(Debug) << "Applying database schema update" << version
|
qLog(Debug) << "Applying database schema update" << version << "from"
|
||||||
<< "from" << filename;
|
<< filename;
|
||||||
ExecSchemaCommandsFromFile(db, filename, version - 1);
|
ExecSchemaCommandsFromFile(db, filename, version - 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Database::UrlEncodeFilenameColumn(const QString& table, QSqlDatabase& db) {
|
void Database::UrlEncodeFilenameColumn(const QString& table, QSqlDatabase& db) {
|
||||||
QSqlQuery select(QString("SELECT ROWID, filename FROM %1").arg(table), db);
|
QSqlQuery select(QString("SELECT ROWID, filename FROM %1").arg(table), db);
|
||||||
QSqlQuery update(QString("UPDATE %1 SET filename=:filename WHERE ROWID=:id").arg(table), db);
|
QSqlQuery update(
|
||||||
|
QString("UPDATE %1 SET filename=:filename WHERE ROWID=:id").arg(table),
|
||||||
|
db);
|
||||||
select.exec();
|
select.exec();
|
||||||
if (CheckErrors(select)) return;
|
if (CheckErrors(select)) return;
|
||||||
while (select.next()) {
|
while (select.next()) {
|
||||||
|
@ -475,16 +471,12 @@ void Database::ExecSchemaCommandsFromFile(QSqlDatabase& db,
|
||||||
QFile schema_file(filename);
|
QFile schema_file(filename);
|
||||||
if (!schema_file.open(QIODevice::ReadOnly))
|
if (!schema_file.open(QIODevice::ReadOnly))
|
||||||
qFatal("Couldn't open schema file %s", filename.toUtf8().constData());
|
qFatal("Couldn't open schema file %s", filename.toUtf8().constData());
|
||||||
ExecSchemaCommands(db,
|
ExecSchemaCommands(db, QString::fromUtf8(schema_file.readAll()),
|
||||||
QString::fromUtf8(schema_file.readAll()),
|
schema_version, in_transaction);
|
||||||
schema_version,
|
|
||||||
in_transaction);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Database::ExecSchemaCommands(QSqlDatabase& db,
|
void Database::ExecSchemaCommands(QSqlDatabase& db, const QString& schema,
|
||||||
const QString& schema,
|
int schema_version, bool in_transaction) {
|
||||||
int schema_version,
|
|
||||||
bool in_transaction) {
|
|
||||||
// Run each command
|
// Run each command
|
||||||
const QStringList commands(schema.split(QRegExp("; *\n\n")));
|
const QStringList commands(schema.split(QRegExp("; *\n\n")));
|
||||||
|
|
||||||
|
@ -530,8 +522,7 @@ void Database::ExecSongTablesCommands(QSqlDatabase& db,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
QSqlQuery query(db.exec(command));
|
QSqlQuery query(db.exec(command));
|
||||||
if (CheckErrors(query))
|
if (CheckErrors(query)) qFatal("Unable to update music library database");
|
||||||
qFatal("Unable to update music library database");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -541,14 +532,17 @@ QStringList Database::SongsTables(QSqlDatabase& db, int schema_version) const {
|
||||||
|
|
||||||
// look for the tables in the main db
|
// look for the tables in the main db
|
||||||
for (const QString& table : db.tables()) {
|
for (const QString& table : db.tables()) {
|
||||||
if (table == "songs" || table.endsWith("_songs"))
|
if (table == "songs" || table.endsWith("_songs")) ret << table;
|
||||||
ret << table;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// look for the tables in attached dbs
|
// look for the tables in attached dbs
|
||||||
for (const QString& key : attached_databases_.keys()) {
|
for (const QString& key : attached_databases_.keys()) {
|
||||||
QSqlQuery q(QString("SELECT NAME FROM %1.sqlite_master"
|
QSqlQuery q(
|
||||||
" WHERE type='table' AND name='songs' OR name LIKE '%songs'").arg(key), db);
|
QString(
|
||||||
|
"SELECT NAME FROM %1.sqlite_master"
|
||||||
|
" WHERE type='table' AND name='songs' OR name LIKE '%songs'")
|
||||||
|
.arg(key),
|
||||||
|
db);
|
||||||
if (q.exec()) {
|
if (q.exec()) {
|
||||||
while (q.next()) {
|
while (q.next()) {
|
||||||
QString tab_name = key + "." + q.value(0).toString();
|
QString tab_name = key + "." + q.value(0).toString();
|
||||||
|
@ -595,8 +589,10 @@ bool Database::IntegrityCheck(QSqlDatabase db) {
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
if (!error_reported) {
|
if (!error_reported) {
|
||||||
app_->AddError(tr("Database corruption detected. Please read "
|
app_->AddError(
|
||||||
"https://code.google.com/p/clementine-player/wiki/DatabaseCorruption "
|
tr("Database corruption detected. Please read "
|
||||||
|
"https://code.google.com/p/clementine-player/wiki/"
|
||||||
|
"DatabaseCorruption "
|
||||||
"for instructions on how to recover your database"));
|
"for instructions on how to recover your database"));
|
||||||
}
|
}
|
||||||
app_->AddError("Database: " + message);
|
app_->AddError("Database: " + message);
|
||||||
|
@ -621,17 +617,16 @@ void Database::DoBackup() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Database::OpenDatabase(const QString& filename, sqlite3** connection) const {
|
bool Database::OpenDatabase(const QString& filename,
|
||||||
|
sqlite3** connection) const {
|
||||||
int ret = sqlite3_open(filename.toUtf8(), connection);
|
int ret = sqlite3_open(filename.toUtf8(), connection);
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
if (*connection) {
|
if (*connection) {
|
||||||
const char* error_message = sqlite3_errmsg(*connection);
|
const char* error_message = sqlite3_errmsg(*connection);
|
||||||
qLog(Error) << "Failed to open database for backup:"
|
qLog(Error) << "Failed to open database for backup:" << filename
|
||||||
<< filename
|
|
||||||
<< error_message;
|
<< error_message;
|
||||||
} else {
|
} else {
|
||||||
qLog(Error) << "Failed to open database for backup:"
|
qLog(Error) << "Failed to open database for backup:" << filename;
|
||||||
<< filename;
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -641,7 +636,8 @@ bool Database::OpenDatabase(const QString& filename, sqlite3** connection) const
|
||||||
void Database::BackupFile(const QString& filename) {
|
void Database::BackupFile(const QString& filename) {
|
||||||
qLog(Debug) << "Starting database backup";
|
qLog(Debug) << "Starting database backup";
|
||||||
QString dest_filename = QString("%1.bak").arg(filename);
|
QString dest_filename = QString("%1.bak").arg(filename);
|
||||||
const int task_id = app_->task_manager()->StartTask(tr("Backing up database"));
|
const int task_id =
|
||||||
|
app_->task_manager()->StartTask(tr("Backing up database"));
|
||||||
|
|
||||||
sqlite3* source_connection = nullptr;
|
sqlite3* source_connection = nullptr;
|
||||||
sqlite3* dest_connection = nullptr;
|
sqlite3* dest_connection = nullptr;
|
||||||
|
@ -651,7 +647,8 @@ void Database::BackupFile(const QString& filename) {
|
||||||
sqlite3_close(source_connection);
|
sqlite3_close(source_connection);
|
||||||
sqlite3_close(dest_connection);
|
sqlite3_close(dest_connection);
|
||||||
app_->task_manager()->SetTaskFinished(task_id);
|
app_->task_manager()->SetTaskFinished(task_id);
|
||||||
} BOOST_SCOPE_EXIT_END
|
}
|
||||||
|
BOOST_SCOPE_EXIT_END
|
||||||
|
|
||||||
bool success = OpenDatabase(filename, &source_connection);
|
bool success = OpenDatabase(filename, &source_connection);
|
||||||
if (!success) {
|
if (!success) {
|
||||||
|
@ -663,9 +660,8 @@ void Database::BackupFile(const QString& filename) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
sqlite3_backup* backup = sqlite3_backup_init(
|
sqlite3_backup* backup =
|
||||||
dest_connection, "main",
|
sqlite3_backup_init(dest_connection, "main", source_connection, "main");
|
||||||
source_connection, "main");
|
|
||||||
if (!backup) {
|
if (!backup) {
|
||||||
const char* error_message = sqlite3_errmsg(dest_connection);
|
const char* error_message = sqlite3_errmsg(dest_connection);
|
||||||
qLog(Error) << "Failed to start database backup:" << error_message;
|
qLog(Error) << "Failed to start database backup:" << error_message;
|
||||||
|
|
|
@ -34,7 +34,6 @@ extern "C" {
|
||||||
struct sqlite3_tokenizer;
|
struct sqlite3_tokenizer;
|
||||||
struct sqlite3_tokenizer_cursor;
|
struct sqlite3_tokenizer_cursor;
|
||||||
struct sqlite3_tokenizer_module;
|
struct sqlite3_tokenizer_module;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class Application;
|
class Application;
|
||||||
|
@ -48,7 +47,8 @@ class Database : public QObject {
|
||||||
|
|
||||||
struct AttachedDatabase {
|
struct AttachedDatabase {
|
||||||
AttachedDatabase() {}
|
AttachedDatabase() {}
|
||||||
AttachedDatabase(const QString& filename, const QString& schema, bool is_temporary)
|
AttachedDatabase(const QString& filename, const QString& schema,
|
||||||
|
bool is_temporary)
|
||||||
: filename_(filename), schema_(schema), is_temporary_(is_temporary) {}
|
: filename_(filename), schema_(schema), is_temporary_(is_temporary) {}
|
||||||
|
|
||||||
QString filename_;
|
QString filename_;
|
||||||
|
@ -65,15 +65,14 @@ class Database : public QObject {
|
||||||
QMutex* Mutex() { return &mutex_; }
|
QMutex* Mutex() { return &mutex_; }
|
||||||
|
|
||||||
void RecreateAttachedDb(const QString& database_name);
|
void RecreateAttachedDb(const QString& database_name);
|
||||||
void ExecSchemaCommands(QSqlDatabase& db,
|
void ExecSchemaCommands(QSqlDatabase& db, const QString& schema,
|
||||||
const QString& schema,
|
int schema_version, bool in_transaction = false);
|
||||||
int schema_version,
|
|
||||||
bool in_transaction = false);
|
|
||||||
|
|
||||||
int startup_schema_version() const { return startup_schema_version_; }
|
int startup_schema_version() const { return startup_schema_version_; }
|
||||||
int current_schema_version() const { return kSchemaVersion; }
|
int current_schema_version() const { return kSchemaVersion; }
|
||||||
|
|
||||||
void AttachDatabase(const QString& database_name, const AttachedDatabase& database);
|
void AttachDatabase(const QString& database_name,
|
||||||
|
const AttachedDatabase& database);
|
||||||
void AttachDatabaseOnDbConnection(const QString& database_name,
|
void AttachDatabaseOnDbConnection(const QString& database_name,
|
||||||
const AttachedDatabase& database,
|
const AttachedDatabase& database,
|
||||||
QSqlDatabase& db);
|
QSqlDatabase& db);
|
||||||
|
@ -88,12 +87,10 @@ class Database : public QObject {
|
||||||
private:
|
private:
|
||||||
void UpdateMainSchema(QSqlDatabase* db);
|
void UpdateMainSchema(QSqlDatabase* db);
|
||||||
|
|
||||||
void ExecSchemaCommandsFromFile(QSqlDatabase& db,
|
void ExecSchemaCommandsFromFile(QSqlDatabase& db, const QString& filename,
|
||||||
const QString& filename,
|
|
||||||
int schema_version,
|
int schema_version,
|
||||||
bool in_transaction = false);
|
bool in_transaction = false);
|
||||||
void ExecSongTablesCommands(QSqlDatabase& db,
|
void ExecSongTablesCommands(QSqlDatabase& db, const QStringList& song_tables,
|
||||||
const QStringList& song_tables,
|
|
||||||
const QStringList& commands);
|
const QStringList& commands);
|
||||||
|
|
||||||
void UpdateDatabaseSchema(int version, QSqlDatabase& db);
|
void UpdateDatabaseSchema(int version, QSqlDatabase& db);
|
||||||
|
@ -137,26 +134,23 @@ class Database : public QObject {
|
||||||
// Do static initialisation like loading sqlite functions.
|
// Do static initialisation like loading sqlite functions.
|
||||||
static void StaticInit();
|
static void StaticInit();
|
||||||
|
|
||||||
typedef int (*Sqlite3CreateFunc) (
|
typedef int (*Sqlite3CreateFunc)(sqlite3*, const char*, int, int, void*,
|
||||||
sqlite3*, const char*, int, int, void*,
|
void (*)(sqlite3_context*, int,
|
||||||
void (*) (sqlite3_context*, int, sqlite3_value**),
|
sqlite3_value**),
|
||||||
void (*) (sqlite3_context*, int, sqlite3_value**),
|
void (*)(sqlite3_context*, int,
|
||||||
|
sqlite3_value**),
|
||||||
void (*)(sqlite3_context*));
|
void (*)(sqlite3_context*));
|
||||||
|
|
||||||
static sqlite3_tokenizer_module* sFTSTokenizer;
|
static sqlite3_tokenizer_module* sFTSTokenizer;
|
||||||
|
|
||||||
static int FTSCreate(int argc, const char* const* argv, sqlite3_tokenizer** tokenizer);
|
static int FTSCreate(int argc, const char* const* argv,
|
||||||
|
sqlite3_tokenizer** tokenizer);
|
||||||
static int FTSDestroy(sqlite3_tokenizer* tokenizer);
|
static int FTSDestroy(sqlite3_tokenizer* tokenizer);
|
||||||
static int FTSOpen(sqlite3_tokenizer* tokenizer,
|
static int FTSOpen(sqlite3_tokenizer* tokenizer, const char* input, int bytes,
|
||||||
const char* input,
|
|
||||||
int bytes,
|
|
||||||
sqlite3_tokenizer_cursor** cursor);
|
sqlite3_tokenizer_cursor** cursor);
|
||||||
static int FTSClose(sqlite3_tokenizer_cursor* cursor);
|
static int FTSClose(sqlite3_tokenizer_cursor* cursor);
|
||||||
static int FTSNext(sqlite3_tokenizer_cursor* cursor,
|
static int FTSNext(sqlite3_tokenizer_cursor* cursor, const char** token,
|
||||||
const char** token,
|
int* bytes, int* start_offset, int* end_offset,
|
||||||
int* bytes,
|
|
||||||
int* start_offset,
|
|
||||||
int* end_offset,
|
|
||||||
int* position);
|
int* position);
|
||||||
struct Token {
|
struct Token {
|
||||||
Token(const QString& token, int start, int end);
|
Token(const QString& token, int start, int end);
|
||||||
|
|
|
@ -34,17 +34,14 @@ DeleteFiles::DeleteFiles(TaskManager* task_manager,
|
||||||
storage_(storage),
|
storage_(storage),
|
||||||
started_(false),
|
started_(false),
|
||||||
task_id_(0),
|
task_id_(0),
|
||||||
progress_(0)
|
progress_(0) {
|
||||||
{
|
|
||||||
original_thread_ = thread();
|
original_thread_ = thread();
|
||||||
}
|
}
|
||||||
|
|
||||||
DeleteFiles::~DeleteFiles() {
|
DeleteFiles::~DeleteFiles() {}
|
||||||
}
|
|
||||||
|
|
||||||
void DeleteFiles::Start(const SongList& songs) {
|
void DeleteFiles::Start(const SongList& songs) {
|
||||||
if (thread_)
|
if (thread_) return;
|
||||||
return;
|
|
||||||
|
|
||||||
songs_ = songs;
|
songs_ = songs;
|
||||||
|
|
||||||
|
|
|
@ -22,12 +22,7 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "fht.h"
|
#include "fht.h"
|
||||||
|
|
||||||
|
FHT::FHT(int n) : m_buf(0), m_tab(0), m_log(0) {
|
||||||
FHT::FHT(int n) :
|
|
||||||
m_buf(0),
|
|
||||||
m_tab(0),
|
|
||||||
m_log(0)
|
|
||||||
{
|
|
||||||
if (n < 3) {
|
if (n < 3) {
|
||||||
m_num = 0;
|
m_num = 0;
|
||||||
m_exp2 = -1;
|
m_exp2 = -1;
|
||||||
|
@ -42,59 +37,43 @@ FHT::FHT(int n) :
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FHT::~FHT() {
|
||||||
FHT::~FHT()
|
|
||||||
{
|
|
||||||
delete[] m_buf;
|
delete[] m_buf;
|
||||||
delete[] m_tab;
|
delete[] m_tab;
|
||||||
delete[] m_log;
|
delete[] m_log;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FHT::makeCasTable(void) {
|
||||||
void FHT::makeCasTable(void)
|
|
||||||
{
|
|
||||||
float d, *costab, *sintab;
|
float d, *costab, *sintab;
|
||||||
int ul, ndiv2 = m_num / 2;
|
int ul, ndiv2 = m_num / 2;
|
||||||
|
|
||||||
for (costab = m_tab, sintab = m_tab + m_num / 2 + 1, ul = 0; ul < m_num; ul++) {
|
for (costab = m_tab, sintab = m_tab + m_num / 2 + 1, ul = 0; ul < m_num;
|
||||||
|
ul++) {
|
||||||
d = M_PI * ul / ndiv2;
|
d = M_PI * ul / ndiv2;
|
||||||
*costab = *sintab = cos(d);
|
*costab = *sintab = cos(d);
|
||||||
|
|
||||||
costab += 2, sintab += 2;
|
costab += 2, sintab += 2;
|
||||||
if (sintab > m_tab + m_num * 2)
|
if (sintab > m_tab + m_num * 2) sintab = m_tab + 1;
|
||||||
sintab = m_tab + 1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float* FHT::copy(float* d, float* s) {
|
||||||
float* FHT::copy(float *d, float *s)
|
|
||||||
{
|
|
||||||
return (float*)memcpy(d, s, m_num * sizeof(float));
|
return (float*)memcpy(d, s, m_num * sizeof(float));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float* FHT::clear(float* d) {
|
||||||
float* FHT::clear(float *d)
|
|
||||||
{
|
|
||||||
return (float*)memset(d, 0, m_num * sizeof(float));
|
return (float*)memset(d, 0, m_num * sizeof(float));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FHT::scale(float* p, float d) {
|
||||||
void FHT::scale(float *p, float d)
|
for (int i = 0; i < (m_num / 2); i++) *p++ *= d;
|
||||||
{
|
|
||||||
for (int i = 0; i < (m_num / 2); i++)
|
|
||||||
*p++ *= d;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FHT::ewma(float* d, float* s, float w) {
|
||||||
void FHT::ewma(float *d, float *s, float w)
|
for (int i = 0; i < (m_num / 2); i++, d++, s++) *d = *d * w + *s * (1 - w);
|
||||||
{
|
|
||||||
for (int i = 0; i < (m_num / 2); i++, d++, s++)
|
|
||||||
*d = *d * w + *s * (1 - w);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FHT::logSpectrum(float* out, float* p) {
|
||||||
void FHT::logSpectrum(float *out, float *p)
|
|
||||||
{
|
|
||||||
int n = m_num / 2, i, j, k, *r;
|
int n = m_num / 2, i, j, k, *r;
|
||||||
if (!m_log) {
|
if (!m_log) {
|
||||||
m_log = new int[n];
|
m_log = new int[n];
|
||||||
|
@ -113,15 +92,12 @@ void FHT::logSpectrum(float *out, float *p)
|
||||||
else {
|
else {
|
||||||
float base = p[k - 1];
|
float base = p[k - 1];
|
||||||
float step = (p[j] - base) / (j - (k - 1));
|
float step = (p[j] - base) / (j - (k - 1));
|
||||||
for (float corr = 0; k <= j; k++, corr += step)
|
for (float corr = 0; k <= j; k++, corr += step) *out++ = base + corr;
|
||||||
*out++ = base + corr;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FHT::semiLogSpectrum(float* p) {
|
||||||
void FHT::semiLogSpectrum(float *p)
|
|
||||||
{
|
|
||||||
float e;
|
float e;
|
||||||
power2(p);
|
power2(p);
|
||||||
for (int i = 0; i < (m_num / 2); i++, p++) {
|
for (int i = 0; i < (m_num / 2); i++, p++) {
|
||||||
|
@ -130,25 +106,17 @@ void FHT::semiLogSpectrum(float *p)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FHT::spectrum(float* p) {
|
||||||
void FHT::spectrum(float *p)
|
|
||||||
{
|
|
||||||
power2(p);
|
power2(p);
|
||||||
for (int i = 0; i < (m_num / 2); i++, p++)
|
for (int i = 0; i < (m_num / 2); i++, p++) *p = (float)sqrt(*p * .5);
|
||||||
*p = (float)sqrt(*p * .5);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FHT::power(float* p) {
|
||||||
void FHT::power(float *p)
|
|
||||||
{
|
|
||||||
power2(p);
|
power2(p);
|
||||||
for (int i = 0; i < (m_num / 2); i++)
|
for (int i = 0; i < (m_num / 2); i++) *p++ *= .5;
|
||||||
*p++ *= .5;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FHT::power2(float* p) {
|
||||||
void FHT::power2(float *p)
|
|
||||||
{
|
|
||||||
int i;
|
int i;
|
||||||
float* q;
|
float* q;
|
||||||
_transform(p, m_num, 0);
|
_transform(p, m_num, 0);
|
||||||
|
@ -159,18 +127,14 @@ void FHT::power2(float *p)
|
||||||
*p = (*p * *p) + (*q * *q), p++;
|
*p = (*p * *p) + (*q * *q), p++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FHT::transform(float* p) {
|
||||||
void FHT::transform(float *p)
|
|
||||||
{
|
|
||||||
if (m_num == 8)
|
if (m_num == 8)
|
||||||
transform8(p);
|
transform8(p);
|
||||||
else
|
else
|
||||||
_transform(p, m_num, 0);
|
_transform(p, m_num, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FHT::transform8(float* p) {
|
||||||
void FHT::transform8(float *p)
|
|
||||||
{
|
|
||||||
float a, b, c, d, e, f, g, h, b_f2, d_h2;
|
float a, b, c, d, e, f, g, h, b_f2, d_h2;
|
||||||
float a_c_eg, a_ce_g, ac_e_g, aceg, b_df_h, bdfh;
|
float a_c_eg, a_ce_g, ac_e_g, aceg, b_df_h, bdfh;
|
||||||
|
|
||||||
|
@ -197,9 +161,7 @@ void FHT::transform8(float *p)
|
||||||
*--p = aceg + bdfh;
|
*--p = aceg + bdfh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FHT::_transform(float* p, int n, int k) {
|
||||||
void FHT::_transform(float *p, int n, int k)
|
|
||||||
{
|
|
||||||
if (n == 8) {
|
if (n == 8) {
|
||||||
transform8(p + k);
|
transform8(p + k);
|
||||||
return;
|
return;
|
||||||
|
@ -239,4 +201,3 @@ void FHT::_transform(float *p, int n, int k)
|
||||||
}
|
}
|
||||||
memcpy(p + k, m_buf, sizeof(float) * n);
|
memcpy(p + k, m_buf, sizeof(float) * n);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,8 +29,7 @@
|
||||||
*
|
*
|
||||||
* [1] Computer in Physics, Vol. 9, No. 4, Jul/Aug 1995 pp 373-379
|
* [1] Computer in Physics, Vol. 9, No. 4, Jul/Aug 1995 pp 373-379
|
||||||
*/
|
*/
|
||||||
class FHT
|
class FHT {
|
||||||
{
|
|
||||||
int m_exp2;
|
int m_exp2;
|
||||||
int m_num;
|
int m_num;
|
||||||
float* m_buf;
|
float* m_buf;
|
||||||
|
|
|
@ -23,17 +23,14 @@
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
|
|
||||||
FilesystemMusicStorage::FilesystemMusicStorage(const QString& root)
|
FilesystemMusicStorage::FilesystemMusicStorage(const QString& root)
|
||||||
: root_(root)
|
: root_(root) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FilesystemMusicStorage::CopyToStorage(const CopyJob& job) {
|
bool FilesystemMusicStorage::CopyToStorage(const CopyJob& job) {
|
||||||
const QFileInfo src = QFileInfo(job.source_);
|
const QFileInfo src = QFileInfo(job.source_);
|
||||||
const QFileInfo dest = QFileInfo(root_ + "/" + job.destination_);
|
const QFileInfo dest = QFileInfo(root_ + "/" + job.destination_);
|
||||||
|
|
||||||
// Don't do anything if the destination is the same as the source
|
// Don't do anything if the destination is the same as the source
|
||||||
if (src == dest)
|
if (src == dest) return true;
|
||||||
return true;
|
|
||||||
|
|
||||||
// Create directories as required
|
// Create directories as required
|
||||||
QDir dir;
|
QDir dir;
|
||||||
|
@ -43,8 +40,7 @@ bool FilesystemMusicStorage::CopyToStorage(const CopyJob& job) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove the destination file if it exists and we want to overwrite
|
// Remove the destination file if it exists and we want to overwrite
|
||||||
if (job.overwrite_ && dest.exists())
|
if (job.overwrite_ && dest.exists()) QFile::remove(dest.absoluteFilePath());
|
||||||
QFile::remove(dest.absoluteFilePath());
|
|
||||||
|
|
||||||
// Copy or move
|
// Copy or move
|
||||||
if (job.remove_original_)
|
if (job.remove_original_)
|
||||||
|
|
|
@ -24,10 +24,10 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
FileSystemWatcherInterface::FileSystemWatcherInterface(QObject* parent)
|
FileSystemWatcherInterface::FileSystemWatcherInterface(QObject* parent)
|
||||||
: QObject(parent) {
|
: QObject(parent) {}
|
||||||
}
|
|
||||||
|
|
||||||
FileSystemWatcherInterface* FileSystemWatcherInterface::Create(QObject* parent) {
|
FileSystemWatcherInterface* FileSystemWatcherInterface::Create(
|
||||||
|
QObject* parent) {
|
||||||
FileSystemWatcherInterface* ret;
|
FileSystemWatcherInterface* ret;
|
||||||
#ifdef Q_OS_DARWIN
|
#ifdef Q_OS_DARWIN
|
||||||
ret = new MacFSListener(parent);
|
ret = new MacFSListener(parent);
|
||||||
|
|
|
@ -20,16 +20,11 @@
|
||||||
#include "globalshortcuts.h"
|
#include "globalshortcuts.h"
|
||||||
|
|
||||||
GlobalShortcutBackend::GlobalShortcutBackend(GlobalShortcuts* parent)
|
GlobalShortcutBackend::GlobalShortcutBackend(GlobalShortcuts* parent)
|
||||||
: QObject(parent),
|
: QObject(parent), manager_(parent), active_(false) {}
|
||||||
manager_(parent),
|
|
||||||
active_(false)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GlobalShortcutBackend::Register() {
|
bool GlobalShortcutBackend::Register() {
|
||||||
bool ret = DoRegister();
|
bool ret = DoRegister();
|
||||||
if (ret)
|
if (ret) active_ = true;
|
||||||
active_ = true;
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,18 +38,22 @@ GlobalShortcuts::GlobalShortcuts(QWidget *parent)
|
||||||
gnome_backend_(nullptr),
|
gnome_backend_(nullptr),
|
||||||
system_backend_(nullptr),
|
system_backend_(nullptr),
|
||||||
use_gnome_(false),
|
use_gnome_(false),
|
||||||
rating_signals_mapper_(new QSignalMapper(this))
|
rating_signals_mapper_(new QSignalMapper(this)) {
|
||||||
{
|
|
||||||
settings_.beginGroup(kSettingsGroup);
|
settings_.beginGroup(kSettingsGroup);
|
||||||
|
|
||||||
// Create actions
|
// Create actions
|
||||||
AddShortcut("play", tr("Play"), SIGNAL(Play()));
|
AddShortcut("play", tr("Play"), SIGNAL(Play()));
|
||||||
AddShortcut("pause", tr("Pause"), SIGNAL(Pause()));
|
AddShortcut("pause", tr("Pause"), SIGNAL(Pause()));
|
||||||
AddShortcut("play_pause", tr("Play/Pause"), SIGNAL(PlayPause()), QKeySequence(Qt::Key_MediaPlay));
|
AddShortcut("play_pause", tr("Play/Pause"), SIGNAL(PlayPause()),
|
||||||
AddShortcut("stop", tr("Stop"), SIGNAL(Stop()), QKeySequence(Qt::Key_MediaStop));
|
QKeySequence(Qt::Key_MediaPlay));
|
||||||
AddShortcut("stop_after", tr("Stop playing after current track"), SIGNAL(StopAfter()));
|
AddShortcut("stop", tr("Stop"), SIGNAL(Stop()),
|
||||||
AddShortcut("next_track", tr("Next track"), SIGNAL(Next()), QKeySequence(Qt::Key_MediaNext));
|
QKeySequence(Qt::Key_MediaStop));
|
||||||
AddShortcut("prev_track", tr("Previous track"), SIGNAL(Previous()), QKeySequence(Qt::Key_MediaPrevious));
|
AddShortcut("stop_after", tr("Stop playing after current track"),
|
||||||
|
SIGNAL(StopAfter()));
|
||||||
|
AddShortcut("next_track", tr("Next track"), SIGNAL(Next()),
|
||||||
|
QKeySequence(Qt::Key_MediaNext));
|
||||||
|
AddShortcut("prev_track", tr("Previous track"), SIGNAL(Previous()),
|
||||||
|
QKeySequence(Qt::Key_MediaPrevious));
|
||||||
AddShortcut("inc_volume", tr("Increase volume"), SIGNAL(IncVolume()));
|
AddShortcut("inc_volume", tr("Increase volume"), SIGNAL(IncVolume()));
|
||||||
AddShortcut("dec_volume", tr("Decrease volume"), SIGNAL(DecVolume()));
|
AddShortcut("dec_volume", tr("Decrease volume"), SIGNAL(DecVolume()));
|
||||||
AddShortcut("mute", tr("Mute"), SIGNAL(Mute()));
|
AddShortcut("mute", tr("Mute"), SIGNAL(Mute()));
|
||||||
|
@ -57,19 +61,32 @@ GlobalShortcuts::GlobalShortcuts(QWidget *parent)
|
||||||
AddShortcut("seek_backward", tr("Seek backward"), SIGNAL(SeekBackward()));
|
AddShortcut("seek_backward", tr("Seek backward"), SIGNAL(SeekBackward()));
|
||||||
AddShortcut("show_hide", tr("Show/Hide"), SIGNAL(ShowHide()));
|
AddShortcut("show_hide", tr("Show/Hide"), SIGNAL(ShowHide()));
|
||||||
AddShortcut("show_osd", tr("Show OSD"), SIGNAL(ShowOSD()));
|
AddShortcut("show_osd", tr("Show OSD"), SIGNAL(ShowOSD()));
|
||||||
AddShortcut("toggle_pretty_osd", tr("Toggle Pretty OSD"), SIGNAL(TogglePrettyOSD())); // Toggling possible only for pretty OSD
|
AddShortcut(
|
||||||
AddShortcut("shuffle_mode", tr("Change shuffle mode"), SIGNAL(CycleShuffleMode()));
|
"toggle_pretty_osd", tr("Toggle Pretty OSD"),
|
||||||
AddShortcut("repeat_mode", tr("Change repeat mode"), SIGNAL(CycleRepeatMode()));
|
SIGNAL(TogglePrettyOSD())); // Toggling possible only for pretty OSD
|
||||||
AddShortcut("toggle_last_fm_scrobbling", tr("Enable/disable Last.fm scrobbling"), SIGNAL(ToggleScrobbling()));
|
AddShortcut("shuffle_mode", tr("Change shuffle mode"),
|
||||||
|
SIGNAL(CycleShuffleMode()));
|
||||||
|
AddShortcut("repeat_mode", tr("Change repeat mode"),
|
||||||
|
SIGNAL(CycleRepeatMode()));
|
||||||
|
AddShortcut("toggle_last_fm_scrobbling",
|
||||||
|
tr("Enable/disable Last.fm scrobbling"),
|
||||||
|
SIGNAL(ToggleScrobbling()));
|
||||||
|
|
||||||
AddRatingShortcut("rate_zero_star", tr("Rate the current song 0 stars"), rating_signals_mapper_, 0);
|
AddRatingShortcut("rate_zero_star", tr("Rate the current song 0 stars"),
|
||||||
AddRatingShortcut("rate_one_star", tr("Rate the current song 1 star"), rating_signals_mapper_, 1);
|
rating_signals_mapper_, 0);
|
||||||
AddRatingShortcut("rate_two_star", tr("Rate the current song 2 stars"), rating_signals_mapper_, 2);
|
AddRatingShortcut("rate_one_star", tr("Rate the current song 1 star"),
|
||||||
AddRatingShortcut("rate_three_star", tr("Rate the current song 3 stars"), rating_signals_mapper_, 3);
|
rating_signals_mapper_, 1);
|
||||||
AddRatingShortcut("rate_four_star", tr("Rate the current song 4 stars"), rating_signals_mapper_, 4);
|
AddRatingShortcut("rate_two_star", tr("Rate the current song 2 stars"),
|
||||||
AddRatingShortcut("rate_five_star", tr("Rate the current song 5 stars"), rating_signals_mapper_, 5);
|
rating_signals_mapper_, 2);
|
||||||
|
AddRatingShortcut("rate_three_star", tr("Rate the current song 3 stars"),
|
||||||
|
rating_signals_mapper_, 3);
|
||||||
|
AddRatingShortcut("rate_four_star", tr("Rate the current song 4 stars"),
|
||||||
|
rating_signals_mapper_, 4);
|
||||||
|
AddRatingShortcut("rate_five_star", tr("Rate the current song 5 stars"),
|
||||||
|
rating_signals_mapper_, 5);
|
||||||
|
|
||||||
connect(rating_signals_mapper_, SIGNAL(mapped(int)), SIGNAL(RateCurrentSong(int)));
|
connect(rating_signals_mapper_, SIGNAL(mapped(int)),
|
||||||
|
SIGNAL(RateCurrentSong(int)));
|
||||||
|
|
||||||
// Create backends - these do the actual shortcut registration
|
// Create backends - these do the actual shortcut registration
|
||||||
gnome_backend_ = new GnomeGlobalShortcutBackend(this);
|
gnome_backend_ = new GnomeGlobalShortcutBackend(this);
|
||||||
|
@ -98,8 +115,8 @@ void GlobalShortcuts::AddRatingShortcut(const QString& id, const QString& name,
|
||||||
mapper->setMapping(shortcut.action, rating);
|
mapper->setMapping(shortcut.action, rating);
|
||||||
}
|
}
|
||||||
|
|
||||||
GlobalShortcuts::Shortcut GlobalShortcuts::AddShortcut(const QString& id, const QString& name,
|
GlobalShortcuts::Shortcut GlobalShortcuts::AddShortcut(
|
||||||
const QKeySequence& default_key) {
|
const QString& id, const QString& name, const QKeySequence& default_key) {
|
||||||
Shortcut shortcut;
|
Shortcut shortcut;
|
||||||
shortcut.action = new QAction(name, this);
|
shortcut.action = new QAction(name, this);
|
||||||
QKeySequence key_sequence = QKeySequence::fromString(
|
QKeySequence key_sequence = QKeySequence::fromString(
|
||||||
|
@ -137,21 +154,19 @@ void GlobalShortcuts::ReloadSettings() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GlobalShortcuts::Unregister() {
|
void GlobalShortcuts::Unregister() {
|
||||||
if (gnome_backend_->is_active())
|
if (gnome_backend_->is_active()) gnome_backend_->Unregister();
|
||||||
gnome_backend_->Unregister();
|
if (system_backend_->is_active()) system_backend_->Unregister();
|
||||||
if (system_backend_->is_active())
|
|
||||||
system_backend_->Unregister();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GlobalShortcuts::Register() {
|
void GlobalShortcuts::Register() {
|
||||||
if (use_gnome_ && gnome_backend_->Register())
|
if (use_gnome_ && gnome_backend_->Register()) return;
|
||||||
return;
|
|
||||||
system_backend_->Register();
|
system_backend_->Register();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GlobalShortcuts::IsMacAccessibilityEnabled() const {
|
bool GlobalShortcuts::IsMacAccessibilityEnabled() const {
|
||||||
#ifdef Q_OS_MAC
|
#ifdef Q_OS_MAC
|
||||||
return static_cast<MacGlobalShortcutBackend*>(system_backend_)->IsAccessibilityEnabled();
|
return static_cast<MacGlobalShortcutBackend*>(system_backend_)
|
||||||
|
->IsAccessibilityEnabled();
|
||||||
#else
|
#else
|
||||||
return true;
|
return true;
|
||||||
#endif
|
#endif
|
||||||
|
@ -159,6 +174,7 @@ bool GlobalShortcuts::IsMacAccessibilityEnabled() const {
|
||||||
|
|
||||||
void GlobalShortcuts::ShowMacAccessibilityDialog() {
|
void GlobalShortcuts::ShowMacAccessibilityDialog() {
|
||||||
#ifdef Q_OS_MAC
|
#ifdef Q_OS_MAC
|
||||||
static_cast<MacGlobalShortcutBackend*>(system_backend_)->ShowAccessibilityDialog();
|
static_cast<MacGlobalShortcutBackend*>(system_backend_)
|
||||||
|
->ShowAccessibilityDialog();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,9 +79,11 @@ signals:
|
||||||
private:
|
private:
|
||||||
void AddShortcut(const QString& id, const QString& name, const char* signal,
|
void AddShortcut(const QString& id, const QString& name, const char* signal,
|
||||||
const QKeySequence& default_key = QKeySequence(0));
|
const QKeySequence& default_key = QKeySequence(0));
|
||||||
void AddRatingShortcut(const QString& id, const QString& name, QSignalMapper* mapper,
|
void AddRatingShortcut(const QString& id, const QString& name,
|
||||||
int rating, const QKeySequence& default_key = QKeySequence(0));
|
QSignalMapper* mapper, int rating,
|
||||||
Shortcut AddShortcut(const QString& id, const QString& name, const QKeySequence& default_key);
|
const QKeySequence& default_key = QKeySequence(0));
|
||||||
|
Shortcut AddShortcut(const QString& id, const QString& name,
|
||||||
|
const QKeySequence& default_key);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
GlobalShortcutBackend* gnome_backend_;
|
GlobalShortcutBackend* gnome_backend_;
|
||||||
|
|
|
@ -33,23 +33,24 @@
|
||||||
#include <QtDBus>
|
#include <QtDBus>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
const char* GnomeGlobalShortcutBackend::kGsdService = "org.gnome.SettingsDaemon";
|
const char* GnomeGlobalShortcutBackend::kGsdService =
|
||||||
const char* GnomeGlobalShortcutBackend::kGsdPath = "/org/gnome/SettingsDaemon/MediaKeys";
|
"org.gnome.SettingsDaemon";
|
||||||
const char* GnomeGlobalShortcutBackend::kGsdInterface = "org.gnome.SettingsDaemon.MediaKeys";
|
const char* GnomeGlobalShortcutBackend::kGsdPath =
|
||||||
|
"/org/gnome/SettingsDaemon/MediaKeys";
|
||||||
|
const char* GnomeGlobalShortcutBackend::kGsdInterface =
|
||||||
|
"org.gnome.SettingsDaemon.MediaKeys";
|
||||||
|
|
||||||
GnomeGlobalShortcutBackend::GnomeGlobalShortcutBackend(GlobalShortcuts* parent)
|
GnomeGlobalShortcutBackend::GnomeGlobalShortcutBackend(GlobalShortcuts* parent)
|
||||||
: GlobalShortcutBackend(parent),
|
: GlobalShortcutBackend(parent),
|
||||||
interface_(nullptr),
|
interface_(nullptr),
|
||||||
is_connected_(false)
|
is_connected_(false) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool GnomeGlobalShortcutBackend::DoRegister() {
|
bool GnomeGlobalShortcutBackend::DoRegister() {
|
||||||
#ifdef QT_DBUS_LIB
|
#ifdef QT_DBUS_LIB
|
||||||
qLog(Debug) << "registering";
|
qLog(Debug) << "registering";
|
||||||
// Check if the GSD service is available
|
// Check if the GSD service is available
|
||||||
if (!QDBusConnection::sessionBus().interface()->isServiceRegistered(kGsdService)) {
|
if (!QDBusConnection::sessionBus().interface()->isServiceRegistered(
|
||||||
|
kGsdService)) {
|
||||||
qLog(Warning) << "gnome settings daemon not registered";
|
qLog(Warning) << "gnome settings daemon not registered";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -64,9 +65,8 @@ bool GnomeGlobalShortcutBackend::DoRegister() {
|
||||||
QDateTime::currentDateTime().toTime_t());
|
QDateTime::currentDateTime().toTime_t());
|
||||||
|
|
||||||
QDBusPendingCallWatcher* watcher = new QDBusPendingCallWatcher(reply, this);
|
QDBusPendingCallWatcher* watcher = new QDBusPendingCallWatcher(reply, this);
|
||||||
NewClosure(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)),
|
NewClosure(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this,
|
||||||
this, SLOT(RegisterFinished(QDBusPendingCallWatcher*)),
|
SLOT(RegisterFinished(QDBusPendingCallWatcher*)), watcher);
|
||||||
watcher);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
#else // QT_DBUS_LIB
|
#else // QT_DBUS_LIB
|
||||||
|
@ -75,19 +75,20 @@ bool GnomeGlobalShortcutBackend::DoRegister() {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void GnomeGlobalShortcutBackend::RegisterFinished(QDBusPendingCallWatcher* watcher) {
|
void GnomeGlobalShortcutBackend::RegisterFinished(
|
||||||
|
QDBusPendingCallWatcher* watcher) {
|
||||||
#ifdef QT_DBUS_LIB
|
#ifdef QT_DBUS_LIB
|
||||||
QDBusMessage reply = watcher->reply();
|
QDBusMessage reply = watcher->reply();
|
||||||
watcher->deleteLater();
|
watcher->deleteLater();
|
||||||
|
|
||||||
if (reply.type() == QDBusMessage::ErrorMessage) {
|
if (reply.type() == QDBusMessage::ErrorMessage) {
|
||||||
qLog(Warning) << "Failed to grab media keys"
|
qLog(Warning) << "Failed to grab media keys" << reply.errorName()
|
||||||
<< reply.errorName() <<reply.errorMessage();
|
<< reply.errorMessage();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
connect(interface_, SIGNAL(MediaPlayerKeyPressed(QString,QString)),
|
connect(interface_, SIGNAL(MediaPlayerKeyPressed(QString, QString)), this,
|
||||||
this, SLOT(GnomeMediaKeyPressed(QString,QString)));
|
SLOT(GnomeMediaKeyPressed(QString, QString)));
|
||||||
is_connected_ = true;
|
is_connected_ = true;
|
||||||
|
|
||||||
qLog(Debug) << "registered";
|
qLog(Debug) << "registered";
|
||||||
|
@ -98,20 +99,21 @@ void GnomeGlobalShortcutBackend::DoUnregister() {
|
||||||
qLog(Debug) << "unregister";
|
qLog(Debug) << "unregister";
|
||||||
#ifdef QT_DBUS_LIB
|
#ifdef QT_DBUS_LIB
|
||||||
// Check if the GSD service is available
|
// Check if the GSD service is available
|
||||||
if (!QDBusConnection::sessionBus().interface()->isServiceRegistered(kGsdService))
|
if (!QDBusConnection::sessionBus().interface()->isServiceRegistered(
|
||||||
return;
|
kGsdService))
|
||||||
if (!interface_ || !is_connected_)
|
|
||||||
return;
|
return;
|
||||||
|
if (!interface_ || !is_connected_) return;
|
||||||
|
|
||||||
is_connected_ = false;
|
is_connected_ = false;
|
||||||
|
|
||||||
interface_->ReleaseMediaPlayerKeys(QCoreApplication::applicationName());
|
interface_->ReleaseMediaPlayerKeys(QCoreApplication::applicationName());
|
||||||
disconnect(interface_, SIGNAL(MediaPlayerKeyPressed(QString,QString)),
|
disconnect(interface_, SIGNAL(MediaPlayerKeyPressed(QString, QString)), this,
|
||||||
this, SLOT(GnomeMediaKeyPressed(QString,QString)));
|
SLOT(GnomeMediaKeyPressed(QString, QString)));
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void GnomeGlobalShortcutBackend::GnomeMediaKeyPressed(const QString&, const QString& key) {
|
void GnomeGlobalShortcutBackend::GnomeMediaKeyPressed(const QString&,
|
||||||
|
const QString& key) {
|
||||||
if (key == "Play") manager_->shortcuts()["play_pause"].action->trigger();
|
if (key == "Play") manager_->shortcuts()["play_pause"].action->trigger();
|
||||||
if (key == "Stop") manager_->shortcuts()["stop"].action->trigger();
|
if (key == "Stop") manager_->shortcuts()["stop"].action->trigger();
|
||||||
if (key == "Next") manager_->shortcuts()["next_track"].action->trigger();
|
if (key == "Next") manager_->shortcuts()["next_track"].action->trigger();
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
#import <Breakpad/Breakpad.h>
|
#import <Breakpad/Breakpad.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
class PlatformInterface;
|
class PlatformInterface;
|
||||||
@class SPMediaKeyTap;
|
@class SPMediaKeyTap;
|
||||||
|
|
||||||
|
@ -25,10 +24,12 @@ class PlatformInterface;
|
||||||
- (id)initWithHandler:(PlatformInterface*)handler;
|
- (id)initWithHandler:(PlatformInterface*)handler;
|
||||||
|
|
||||||
// NSApplicationDelegate
|
// NSApplicationDelegate
|
||||||
- (BOOL) applicationShouldHandleReopen: (NSApplication*)app hasVisibleWindows:(BOOL)flag;
|
- (BOOL)applicationShouldHandleReopen:(NSApplication*)app
|
||||||
|
hasVisibleWindows:(BOOL)flag;
|
||||||
- (NSMenu*)applicationDockMenu:(NSApplication*)sender;
|
- (NSMenu*)applicationDockMenu:(NSApplication*)sender;
|
||||||
- (void)applicationDidFinishLaunching:(NSNotification*)aNotification;
|
- (void)applicationDidFinishLaunching:(NSNotification*)aNotification;
|
||||||
- (NSApplicationTerminateReply) applicationShouldTerminate:(NSApplication*)sender;
|
- (NSApplicationTerminateReply)applicationShouldTerminate:
|
||||||
|
(NSApplication*)sender;
|
||||||
|
|
||||||
// NSUserNotificationCenterDelegate
|
// NSUserNotificationCenterDelegate
|
||||||
- (BOOL)userNotificationCenter:(id)center
|
- (BOOL)userNotificationCenter:(id)center
|
||||||
|
@ -37,6 +38,6 @@ class PlatformInterface;
|
||||||
- (void)setDockMenu:(NSMenu*)menu;
|
- (void)setDockMenu:(NSMenu*)menu;
|
||||||
- (MacGlobalShortcutBackend*)shortcut_handler;
|
- (MacGlobalShortcutBackend*)shortcut_handler;
|
||||||
- (void)setShortcutHandler:(MacGlobalShortcutBackend*)backend;
|
- (void)setShortcutHandler:(MacGlobalShortcutBackend*)backend;
|
||||||
- (void) mediaKeyTap: (SPMediaKeyTap*)keyTap receivedMediaKeyEvent:(NSEvent*)event;
|
- (void)mediaKeyTap:(SPMediaKeyTap*)keyTap
|
||||||
|
receivedMediaKeyEvent:(NSEvent*)event;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
|
@ -30,5 +30,4 @@ namespace mac {
|
||||||
QKeySequence KeySequenceFromNSEvent(NSEvent* event);
|
QKeySequence KeySequenceFromNSEvent(NSEvent* event);
|
||||||
void DumpDictionary(CFDictionaryRef dict);
|
void DumpDictionary(CFDictionaryRef dict);
|
||||||
float GetDevicePixelRatio(QWidget* widget);
|
float GetDevicePixelRatio(QWidget* widget);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,11 +45,8 @@ class MacFSListener : public FileSystemWatcherInterface {
|
||||||
private:
|
private:
|
||||||
void UpdateStreamAsync();
|
void UpdateStreamAsync();
|
||||||
|
|
||||||
static void EventStreamCallback(
|
static void EventStreamCallback(ConstFSEventStreamRef stream, void* user_data,
|
||||||
ConstFSEventStreamRef stream,
|
size_t num_events, void* event_paths,
|
||||||
void* user_data,
|
|
||||||
size_t num_events,
|
|
||||||
void* event_paths,
|
|
||||||
const FSEventStreamEventFlags event_flags[],
|
const FSEventStreamEventFlags event_flags[],
|
||||||
const FSEventStreamEventId event_ids[]);
|
const FSEventStreamEventId event_ids[]);
|
||||||
|
|
||||||
|
|
|
@ -39,15 +39,12 @@ using boost::multi_index::multi_index_container;
|
||||||
using boost::multi_index::ordered_unique;
|
using boost::multi_index::ordered_unique;
|
||||||
using boost::multi_index::tag;
|
using boost::multi_index::tag;
|
||||||
|
|
||||||
std::size_t hash_value(const QModelIndex& index) {
|
std::size_t hash_value(const QModelIndex& index) { return qHash(index); }
|
||||||
return qHash(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
struct Mapping {
|
struct Mapping {
|
||||||
Mapping(const QModelIndex& _source_index)
|
Mapping(const QModelIndex& _source_index) : source_index(_source_index) {}
|
||||||
: source_index(_source_index) {}
|
|
||||||
|
|
||||||
QModelIndex source_index;
|
QModelIndex source_index;
|
||||||
};
|
};
|
||||||
|
@ -64,10 +61,8 @@ class MergedProxyModelPrivate {
|
||||||
indexed_by<
|
indexed_by<
|
||||||
hashed_unique<tag<tag_by_source>,
|
hashed_unique<tag<tag_by_source>,
|
||||||
member<Mapping, QModelIndex, &Mapping::source_index> >,
|
member<Mapping, QModelIndex, &Mapping::source_index> >,
|
||||||
ordered_unique<tag<tag_by_pointer>,
|
ordered_unique<tag<tag_by_pointer>, identity<Mapping*> > > >
|
||||||
identity<Mapping*> >
|
MappingContainer;
|
||||||
>
|
|
||||||
> MappingContainer;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
MappingContainer mappings_;
|
MappingContainer mappings_;
|
||||||
|
@ -76,12 +71,9 @@ class MergedProxyModelPrivate {
|
||||||
MergedProxyModel::MergedProxyModel(QObject* parent)
|
MergedProxyModel::MergedProxyModel(QObject* parent)
|
||||||
: QAbstractProxyModel(parent),
|
: QAbstractProxyModel(parent),
|
||||||
resetting_model_(nullptr),
|
resetting_model_(nullptr),
|
||||||
p_(new MergedProxyModelPrivate) {
|
p_(new MergedProxyModelPrivate) {}
|
||||||
}
|
|
||||||
|
|
||||||
MergedProxyModel::~MergedProxyModel() {
|
MergedProxyModel::~MergedProxyModel() { DeleteAllMappings(); }
|
||||||
DeleteAllMappings();
|
|
||||||
}
|
|
||||||
|
|
||||||
void MergedProxyModel::DeleteAllMappings() {
|
void MergedProxyModel::DeleteAllMappings() {
|
||||||
const auto& begin = p_->mappings_.get<tag_by_pointer>().begin();
|
const auto& begin = p_->mappings_.get<tag_by_pointer>().begin();
|
||||||
|
@ -92,27 +84,25 @@ void MergedProxyModel::DeleteAllMappings() {
|
||||||
void MergedProxyModel::AddSubModel(const QModelIndex& source_parent,
|
void MergedProxyModel::AddSubModel(const QModelIndex& source_parent,
|
||||||
QAbstractItemModel* submodel) {
|
QAbstractItemModel* submodel) {
|
||||||
connect(submodel, SIGNAL(modelReset()), this, SLOT(SubModelReset()));
|
connect(submodel, SIGNAL(modelReset()), this, SLOT(SubModelReset()));
|
||||||
connect(submodel, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)),
|
connect(submodel, SIGNAL(rowsAboutToBeInserted(QModelIndex, int, int)), this,
|
||||||
this, SLOT(RowsAboutToBeInserted(QModelIndex,int,int)));
|
SLOT(RowsAboutToBeInserted(QModelIndex, int, int)));
|
||||||
connect(submodel, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
|
connect(submodel, SIGNAL(rowsAboutToBeRemoved(QModelIndex, int, int)), this,
|
||||||
this, SLOT(RowsAboutToBeRemoved(QModelIndex,int,int)));
|
SLOT(RowsAboutToBeRemoved(QModelIndex, int, int)));
|
||||||
connect(submodel, SIGNAL(rowsInserted(QModelIndex,int,int)),
|
connect(submodel, SIGNAL(rowsInserted(QModelIndex, int, int)), this,
|
||||||
this, SLOT(RowsInserted(QModelIndex,int,int)));
|
SLOT(RowsInserted(QModelIndex, int, int)));
|
||||||
connect(submodel, SIGNAL(rowsRemoved(QModelIndex,int,int)),
|
connect(submodel, SIGNAL(rowsRemoved(QModelIndex, int, int)), this,
|
||||||
this, SLOT(RowsRemoved(QModelIndex,int,int)));
|
SLOT(RowsRemoved(QModelIndex, int, int)));
|
||||||
connect(submodel, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
|
connect(submodel, SIGNAL(dataChanged(QModelIndex, QModelIndex)), this,
|
||||||
this, SLOT(DataChanged(QModelIndex,QModelIndex)));
|
SLOT(DataChanged(QModelIndex, QModelIndex)));
|
||||||
|
|
||||||
QModelIndex proxy_parent = mapFromSource(source_parent);
|
QModelIndex proxy_parent = mapFromSource(source_parent);
|
||||||
const int rows = submodel->rowCount();
|
const int rows = submodel->rowCount();
|
||||||
|
|
||||||
if (rows)
|
if (rows) beginInsertRows(proxy_parent, 0, rows - 1);
|
||||||
beginInsertRows(proxy_parent, 0, rows-1);
|
|
||||||
|
|
||||||
merge_points_.insert(submodel, source_parent);
|
merge_points_.insert(submodel, source_parent);
|
||||||
|
|
||||||
if (rows)
|
if (rows) endInsertRows();
|
||||||
endInsertRows();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MergedProxyModel::RemoveSubModel(const QModelIndex& source_parent) {
|
void MergedProxyModel::RemoveSubModel(const QModelIndex& source_parent) {
|
||||||
|
@ -147,21 +137,24 @@ void MergedProxyModel::RemoveSubModel(const QModelIndex &source_parent) {
|
||||||
|
|
||||||
void MergedProxyModel::setSourceModel(QAbstractItemModel* source_model) {
|
void MergedProxyModel::setSourceModel(QAbstractItemModel* source_model) {
|
||||||
if (sourceModel()) {
|
if (sourceModel()) {
|
||||||
disconnect(sourceModel(), SIGNAL(modelReset()), this, SLOT(SourceModelReset()));
|
disconnect(sourceModel(), SIGNAL(modelReset()), this,
|
||||||
disconnect(sourceModel(), SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)),
|
SLOT(SourceModelReset()));
|
||||||
this, SLOT(RowsAboutToBeInserted(QModelIndex,int,int)));
|
disconnect(sourceModel(),
|
||||||
disconnect(sourceModel(), SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
|
SIGNAL(rowsAboutToBeInserted(QModelIndex, int, int)), this,
|
||||||
this, SLOT(RowsAboutToBeRemoved(QModelIndex,int,int)));
|
SLOT(RowsAboutToBeInserted(QModelIndex, int, int)));
|
||||||
disconnect(sourceModel(), SIGNAL(rowsInserted(QModelIndex,int,int)),
|
disconnect(sourceModel(),
|
||||||
this, SLOT(RowsInserted(QModelIndex,int,int)));
|
SIGNAL(rowsAboutToBeRemoved(QModelIndex, int, int)), this,
|
||||||
disconnect(sourceModel(), SIGNAL(rowsRemoved(QModelIndex,int,int)),
|
SLOT(RowsAboutToBeRemoved(QModelIndex, int, int)));
|
||||||
this, SLOT(RowsRemoved(QModelIndex,int,int)));
|
disconnect(sourceModel(), SIGNAL(rowsInserted(QModelIndex, int, int)), this,
|
||||||
|
SLOT(RowsInserted(QModelIndex, int, int)));
|
||||||
|
disconnect(sourceModel(), SIGNAL(rowsRemoved(QModelIndex, int, int)), this,
|
||||||
|
SLOT(RowsRemoved(QModelIndex, int, int)));
|
||||||
disconnect(sourceModel(), SIGNAL(dataChanged(QModelIndex, QModelIndex)),
|
disconnect(sourceModel(), SIGNAL(dataChanged(QModelIndex, QModelIndex)),
|
||||||
this, SLOT(DataChanged(QModelIndex, QModelIndex)));
|
this, SLOT(DataChanged(QModelIndex, QModelIndex)));
|
||||||
disconnect(sourceModel(), SIGNAL(layoutAboutToBeChanged()),
|
disconnect(sourceModel(), SIGNAL(layoutAboutToBeChanged()), this,
|
||||||
this, SLOT(LayoutAboutToBeChanged()));
|
SLOT(LayoutAboutToBeChanged()));
|
||||||
disconnect(sourceModel(), SIGNAL(layoutChanged()),
|
disconnect(sourceModel(), SIGNAL(layoutChanged()), this,
|
||||||
this, SLOT(LayoutChanged()));
|
SLOT(LayoutChanged()));
|
||||||
}
|
}
|
||||||
|
|
||||||
QAbstractProxyModel::setSourceModel(source_model);
|
QAbstractProxyModel::setSourceModel(source_model);
|
||||||
|
@ -171,16 +164,15 @@ void MergedProxyModel::setSourceModel(QAbstractItemModel* source_model) {
|
||||||
this, SLOT(RowsAboutToBeInserted(QModelIndex, int, int)));
|
this, SLOT(RowsAboutToBeInserted(QModelIndex, int, int)));
|
||||||
connect(sourceModel(), SIGNAL(rowsAboutToBeRemoved(QModelIndex, int, int)),
|
connect(sourceModel(), SIGNAL(rowsAboutToBeRemoved(QModelIndex, int, int)),
|
||||||
this, SLOT(RowsAboutToBeRemoved(QModelIndex, int, int)));
|
this, SLOT(RowsAboutToBeRemoved(QModelIndex, int, int)));
|
||||||
connect(sourceModel(), SIGNAL(rowsInserted(QModelIndex,int,int)),
|
connect(sourceModel(), SIGNAL(rowsInserted(QModelIndex, int, int)), this,
|
||||||
this, SLOT(RowsInserted(QModelIndex,int,int)));
|
SLOT(RowsInserted(QModelIndex, int, int)));
|
||||||
connect(sourceModel(), SIGNAL(rowsRemoved(QModelIndex,int,int)),
|
connect(sourceModel(), SIGNAL(rowsRemoved(QModelIndex, int, int)), this,
|
||||||
this, SLOT(RowsRemoved(QModelIndex,int,int)));
|
SLOT(RowsRemoved(QModelIndex, int, int)));
|
||||||
connect(sourceModel(), SIGNAL(dataChanged(QModelIndex,QModelIndex)),
|
connect(sourceModel(), SIGNAL(dataChanged(QModelIndex, QModelIndex)), this,
|
||||||
this, SLOT(DataChanged(QModelIndex,QModelIndex)));
|
SLOT(DataChanged(QModelIndex, QModelIndex)));
|
||||||
connect(sourceModel(), SIGNAL(layoutAboutToBeChanged()),
|
connect(sourceModel(), SIGNAL(layoutAboutToBeChanged()), this,
|
||||||
this, SLOT(LayoutAboutToBeChanged()));
|
SLOT(LayoutAboutToBeChanged()));
|
||||||
connect(sourceModel(), SIGNAL(layoutChanged()),
|
connect(sourceModel(), SIGNAL(layoutChanged()), this, SLOT(LayoutChanged()));
|
||||||
this, SLOT(LayoutChanged()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MergedProxyModel::SourceModelReset() {
|
void MergedProxyModel::SourceModelReset() {
|
||||||
|
@ -234,8 +226,8 @@ void MergedProxyModel::SubModelReset() {
|
||||||
emit SubModelReset(proxy_parent, submodel);
|
emit SubModelReset(proxy_parent, submodel);
|
||||||
}
|
}
|
||||||
|
|
||||||
QModelIndex MergedProxyModel::GetActualSourceParent(const QModelIndex& source_parent,
|
QModelIndex MergedProxyModel::GetActualSourceParent(
|
||||||
QAbstractItemModel* model) const {
|
const QModelIndex& source_parent, QAbstractItemModel* model) const {
|
||||||
if (!source_parent.isValid() && model != sourceModel())
|
if (!source_parent.isValid() && model != sourceModel())
|
||||||
return merge_points_.value(model);
|
return merge_points_.value(model);
|
||||||
return source_parent;
|
return source_parent;
|
||||||
|
@ -243,7 +235,8 @@ QModelIndex MergedProxyModel::GetActualSourceParent(const QModelIndex& source_pa
|
||||||
|
|
||||||
void MergedProxyModel::RowsAboutToBeInserted(const QModelIndex& source_parent,
|
void MergedProxyModel::RowsAboutToBeInserted(const QModelIndex& source_parent,
|
||||||
int start, int end) {
|
int start, int end) {
|
||||||
beginInsertRows(mapFromSource(GetActualSourceParent(
|
beginInsertRows(
|
||||||
|
mapFromSource(GetActualSourceParent(
|
||||||
source_parent, static_cast<QAbstractItemModel*>(sender()))),
|
source_parent, static_cast<QAbstractItemModel*>(sender()))),
|
||||||
start, end);
|
start, end);
|
||||||
}
|
}
|
||||||
|
@ -254,7 +247,8 @@ void MergedProxyModel::RowsInserted(const QModelIndex&, int, int) {
|
||||||
|
|
||||||
void MergedProxyModel::RowsAboutToBeRemoved(const QModelIndex& source_parent,
|
void MergedProxyModel::RowsAboutToBeRemoved(const QModelIndex& source_parent,
|
||||||
int start, int end) {
|
int start, int end) {
|
||||||
beginRemoveRows(mapFromSource(GetActualSourceParent(
|
beginRemoveRows(
|
||||||
|
mapFromSource(GetActualSourceParent(
|
||||||
source_parent, static_cast<QAbstractItemModel*>(sender()))),
|
source_parent, static_cast<QAbstractItemModel*>(sender()))),
|
||||||
start, end);
|
start, end);
|
||||||
}
|
}
|
||||||
|
@ -263,29 +257,26 @@ void MergedProxyModel::RowsRemoved(const QModelIndex&, int, int) {
|
||||||
endRemoveRows();
|
endRemoveRows();
|
||||||
}
|
}
|
||||||
|
|
||||||
QModelIndex MergedProxyModel::mapToSource(const QModelIndex& proxy_index) const {
|
QModelIndex MergedProxyModel::mapToSource(const QModelIndex& proxy_index)
|
||||||
if (!proxy_index.isValid())
|
const {
|
||||||
return QModelIndex();
|
if (!proxy_index.isValid()) return QModelIndex();
|
||||||
|
|
||||||
Mapping* mapping = static_cast<Mapping*>(proxy_index.internalPointer());
|
Mapping* mapping = static_cast<Mapping*>(proxy_index.internalPointer());
|
||||||
if (p_->mappings_.get<tag_by_pointer>().find(mapping) ==
|
if (p_->mappings_.get<tag_by_pointer>().find(mapping) ==
|
||||||
p_->mappings_.get<tag_by_pointer>().end())
|
p_->mappings_.get<tag_by_pointer>().end())
|
||||||
return QModelIndex();
|
return QModelIndex();
|
||||||
if (mapping->source_index.model() == resetting_model_)
|
if (mapping->source_index.model() == resetting_model_) return QModelIndex();
|
||||||
return QModelIndex();
|
|
||||||
|
|
||||||
return mapping->source_index;
|
return mapping->source_index;
|
||||||
}
|
}
|
||||||
|
|
||||||
QModelIndex MergedProxyModel::mapFromSource(const QModelIndex& source_index) const {
|
QModelIndex MergedProxyModel::mapFromSource(const QModelIndex& source_index)
|
||||||
if (!source_index.isValid())
|
const {
|
||||||
return QModelIndex();
|
if (!source_index.isValid()) return QModelIndex();
|
||||||
if (source_index.model() == resetting_model_)
|
if (source_index.model() == resetting_model_) return QModelIndex();
|
||||||
return QModelIndex();
|
|
||||||
|
|
||||||
// Add a mapping if we don't have one already
|
// Add a mapping if we don't have one already
|
||||||
const auto& it =
|
const auto& it = p_->mappings_.get<tag_by_source>().find(source_index);
|
||||||
p_->mappings_.get<tag_by_source>().find(source_index);
|
|
||||||
Mapping* mapping;
|
Mapping* mapping;
|
||||||
if (it != p_->mappings_.get<tag_by_source>().end()) {
|
if (it != p_->mappings_.get<tag_by_source>().end()) {
|
||||||
mapping = *it;
|
mapping = *it;
|
||||||
|
@ -297,7 +288,8 @@ QModelIndex MergedProxyModel::mapFromSource(const QModelIndex& source_index) con
|
||||||
return createIndex(source_index.row(), source_index.column(), mapping);
|
return createIndex(source_index.row(), source_index.column(), mapping);
|
||||||
}
|
}
|
||||||
|
|
||||||
QModelIndex MergedProxyModel::index(int row, int column, const QModelIndex &parent) const {
|
QModelIndex MergedProxyModel::index(int row, int column,
|
||||||
|
const QModelIndex& parent) const {
|
||||||
QModelIndex source_index;
|
QModelIndex source_index;
|
||||||
|
|
||||||
if (!parent.isValid()) {
|
if (!parent.isValid()) {
|
||||||
|
@ -320,8 +312,7 @@ QModelIndex MergedProxyModel::parent(const QModelIndex &child) const {
|
||||||
if (source_child.model() == sourceModel())
|
if (source_child.model() == sourceModel())
|
||||||
return mapFromSource(source_child.parent());
|
return mapFromSource(source_child.parent());
|
||||||
|
|
||||||
if (!IsKnownModel(source_child.model()))
|
if (!IsKnownModel(source_child.model())) return QModelIndex();
|
||||||
return QModelIndex();
|
|
||||||
|
|
||||||
if (!source_child.parent().isValid())
|
if (!source_child.parent().isValid())
|
||||||
return mapFromSource(merge_points_.value(GetModel(source_child)));
|
return mapFromSource(merge_points_.value(GetModel(source_child)));
|
||||||
|
@ -329,12 +320,10 @@ QModelIndex MergedProxyModel::parent(const QModelIndex &child) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
int MergedProxyModel::rowCount(const QModelIndex& parent) const {
|
int MergedProxyModel::rowCount(const QModelIndex& parent) const {
|
||||||
if (!parent.isValid())
|
if (!parent.isValid()) return sourceModel()->rowCount(QModelIndex());
|
||||||
return sourceModel()->rowCount(QModelIndex());
|
|
||||||
|
|
||||||
QModelIndex source_parent = mapToSource(parent);
|
QModelIndex source_parent = mapToSource(parent);
|
||||||
if (!IsKnownModel(source_parent.model()))
|
if (!IsKnownModel(source_parent.model())) return 0;
|
||||||
return 0;
|
|
||||||
|
|
||||||
const QAbstractItemModel* child_model = merge_points_.key(source_parent);
|
const QAbstractItemModel* child_model = merge_points_.key(source_parent);
|
||||||
if (child_model) {
|
if (child_model) {
|
||||||
|
@ -349,26 +338,21 @@ int MergedProxyModel::rowCount(const QModelIndex &parent) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
int MergedProxyModel::columnCount(const QModelIndex& parent) const {
|
int MergedProxyModel::columnCount(const QModelIndex& parent) const {
|
||||||
if (!parent.isValid())
|
if (!parent.isValid()) return sourceModel()->columnCount(QModelIndex());
|
||||||
return sourceModel()->columnCount(QModelIndex());
|
|
||||||
|
|
||||||
QModelIndex source_parent = mapToSource(parent);
|
QModelIndex source_parent = mapToSource(parent);
|
||||||
if (!IsKnownModel(source_parent.model()))
|
if (!IsKnownModel(source_parent.model())) return 0;
|
||||||
return 0;
|
|
||||||
|
|
||||||
const QAbstractItemModel* child_model = merge_points_.key(source_parent);
|
const QAbstractItemModel* child_model = merge_points_.key(source_parent);
|
||||||
if (child_model)
|
if (child_model) return child_model->columnCount(QModelIndex());
|
||||||
return child_model->columnCount(QModelIndex());
|
|
||||||
return source_parent.model()->columnCount(source_parent);
|
return source_parent.model()->columnCount(source_parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MergedProxyModel::hasChildren(const QModelIndex& parent) const {
|
bool MergedProxyModel::hasChildren(const QModelIndex& parent) const {
|
||||||
if (!parent.isValid())
|
if (!parent.isValid()) return sourceModel()->hasChildren(QModelIndex());
|
||||||
return sourceModel()->hasChildren(QModelIndex());
|
|
||||||
|
|
||||||
QModelIndex source_parent = mapToSource(parent);
|
QModelIndex source_parent = mapToSource(parent);
|
||||||
if (!IsKnownModel(source_parent.model()))
|
if (!IsKnownModel(source_parent.model())) return false;
|
||||||
return false;
|
|
||||||
|
|
||||||
const QAbstractItemModel* child_model = merge_points_.key(source_parent);
|
const QAbstractItemModel* child_model = merge_points_.key(source_parent);
|
||||||
|
|
||||||
|
@ -380,25 +364,23 @@ bool MergedProxyModel::hasChildren(const QModelIndex &parent) const {
|
||||||
|
|
||||||
QVariant MergedProxyModel::data(const QModelIndex& proxyIndex, int role) const {
|
QVariant MergedProxyModel::data(const QModelIndex& proxyIndex, int role) const {
|
||||||
QModelIndex source_index = mapToSource(proxyIndex);
|
QModelIndex source_index = mapToSource(proxyIndex);
|
||||||
if (!IsKnownModel(source_index.model()))
|
if (!IsKnownModel(source_index.model())) return QVariant();
|
||||||
return QVariant();
|
|
||||||
|
|
||||||
return source_index.model()->data(source_index, role);
|
return source_index.model()->data(source_index, role);
|
||||||
}
|
}
|
||||||
|
|
||||||
QMap<int, QVariant> MergedProxyModel::itemData(const QModelIndex& proxy_index) const {
|
QMap<int, QVariant> MergedProxyModel::itemData(const QModelIndex& proxy_index)
|
||||||
|
const {
|
||||||
QModelIndex source_index = mapToSource(proxy_index);
|
QModelIndex source_index = mapToSource(proxy_index);
|
||||||
|
|
||||||
if (!source_index.isValid())
|
if (!source_index.isValid()) return sourceModel()->itemData(QModelIndex());
|
||||||
return sourceModel()->itemData(QModelIndex());
|
|
||||||
return source_index.model()->itemData(source_index);
|
return source_index.model()->itemData(source_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
Qt::ItemFlags MergedProxyModel::flags(const QModelIndex& index) const {
|
Qt::ItemFlags MergedProxyModel::flags(const QModelIndex& index) const {
|
||||||
QModelIndex source_index = mapToSource(index);
|
QModelIndex source_index = mapToSource(index);
|
||||||
|
|
||||||
if (!source_index.isValid())
|
if (!source_index.isValid()) return sourceModel()->flags(QModelIndex());
|
||||||
return sourceModel()->flags(QModelIndex());
|
|
||||||
return source_index.model()->flags(source_index);
|
return source_index.model()->flags(source_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -423,8 +405,7 @@ QStringList MergedProxyModel::mimeTypes() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
QMimeData* MergedProxyModel::mimeData(const QModelIndexList& indexes) const {
|
QMimeData* MergedProxyModel::mimeData(const QModelIndexList& indexes) const {
|
||||||
if (indexes.isEmpty())
|
if (indexes.isEmpty()) return 0;
|
||||||
return 0;
|
|
||||||
|
|
||||||
// Only ask the first index's model
|
// Only ask the first index's model
|
||||||
const QAbstractItemModel* model = mapToSource(indexes[0]).model();
|
const QAbstractItemModel* model = mapToSource(indexes[0]).model();
|
||||||
|
@ -437,15 +418,16 @@ QMimeData* MergedProxyModel::mimeData(const QModelIndexList &indexes) const {
|
||||||
|
|
||||||
foreach(const QModelIndex & proxy_index, indexes) {
|
foreach(const QModelIndex & proxy_index, indexes) {
|
||||||
QModelIndex source_index = mapToSource(proxy_index);
|
QModelIndex source_index = mapToSource(proxy_index);
|
||||||
if (source_index.model() != model)
|
if (source_index.model() != model) continue;
|
||||||
continue;
|
|
||||||
indexes_in_model << source_index;
|
indexes_in_model << source_index;
|
||||||
}
|
}
|
||||||
|
|
||||||
return model->mimeData(indexes_in_model);
|
return model->mimeData(indexes_in_model);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MergedProxyModel::dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent) {
|
bool MergedProxyModel::dropMimeData(const QMimeData* data,
|
||||||
|
Qt::DropAction action, int row, int column,
|
||||||
|
const QModelIndex& parent) {
|
||||||
if (!parent.isValid()) {
|
if (!parent.isValid()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -453,13 +435,12 @@ bool MergedProxyModel::dropMimeData(const QMimeData* data, Qt::DropAction action
|
||||||
return sourceModel()->dropMimeData(data, action, row, column, parent);
|
return sourceModel()->dropMimeData(data, action, row, column, parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
QModelIndex MergedProxyModel::FindSourceParent(const QModelIndex& proxy_index) const {
|
QModelIndex MergedProxyModel::FindSourceParent(const QModelIndex& proxy_index)
|
||||||
if (!proxy_index.isValid())
|
const {
|
||||||
return QModelIndex();
|
if (!proxy_index.isValid()) return QModelIndex();
|
||||||
|
|
||||||
QModelIndex source_index = mapToSource(proxy_index);
|
QModelIndex source_index = mapToSource(proxy_index);
|
||||||
if (source_index.model() == sourceModel())
|
if (source_index.model() == sourceModel()) return source_index;
|
||||||
return source_index;
|
|
||||||
return merge_points_.value(GetModel(source_index));
|
return merge_points_.value(GetModel(source_index));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -480,20 +461,20 @@ void MergedProxyModel::fetchMore(const QModelIndex& parent) {
|
||||||
GetModel(source_index)->fetchMore(source_index);
|
GetModel(source_index)->fetchMore(source_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
QAbstractItemModel* MergedProxyModel::GetModel(const QModelIndex& source_index) const {
|
QAbstractItemModel* MergedProxyModel::GetModel(const QModelIndex& source_index)
|
||||||
|
const {
|
||||||
// This is essentially const_cast<QAbstractItemModel*>(source_index.model()),
|
// This is essentially const_cast<QAbstractItemModel*>(source_index.model()),
|
||||||
// but without the const_cast
|
// but without the const_cast
|
||||||
const QAbstractItemModel* const_model = source_index.model();
|
const QAbstractItemModel* const_model = source_index.model();
|
||||||
if (const_model == sourceModel())
|
if (const_model == sourceModel()) return sourceModel();
|
||||||
return sourceModel();
|
|
||||||
foreach(QAbstractItemModel * submodel, merge_points_.keys()) {
|
foreach(QAbstractItemModel * submodel, merge_points_.keys()) {
|
||||||
if (submodel == const_model)
|
if (submodel == const_model) return submodel;
|
||||||
return submodel;
|
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MergedProxyModel::DataChanged(const QModelIndex& top_left, const QModelIndex& bottom_right) {
|
void MergedProxyModel::DataChanged(const QModelIndex& top_left,
|
||||||
|
const QModelIndex& bottom_right) {
|
||||||
emit dataChanged(mapFromSource(top_left), mapFromSource(bottom_right));
|
emit dataChanged(mapFromSource(top_left), mapFromSource(bottom_right));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -506,8 +487,7 @@ void MergedProxyModel::LayoutAboutToBeChanged() {
|
||||||
|
|
||||||
void MergedProxyModel::LayoutChanged() {
|
void MergedProxyModel::LayoutChanged() {
|
||||||
foreach(QAbstractItemModel * key, merge_points_.keys()) {
|
foreach(QAbstractItemModel * key, merge_points_.keys()) {
|
||||||
if (!old_merge_points_.contains(key))
|
if (!old_merge_points_.contains(key)) continue;
|
||||||
continue;
|
|
||||||
|
|
||||||
const int old_row = old_merge_points_[key].row();
|
const int old_row = old_merge_points_[key].row();
|
||||||
const int new_row = merge_points_[key].row();
|
const int new_row = merge_points_[key].row();
|
||||||
|
@ -526,7 +506,8 @@ bool MergedProxyModel::IsKnownModel(const QAbstractItemModel* model) const {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
QModelIndexList MergedProxyModel::mapFromSource(const QModelIndexList& source_indexes) const {
|
QModelIndexList MergedProxyModel::mapFromSource(
|
||||||
|
const QModelIndexList& source_indexes) const {
|
||||||
QModelIndexList ret;
|
QModelIndexList ret;
|
||||||
foreach(const QModelIndex & index, source_indexes) {
|
foreach(const QModelIndex & index, source_indexes) {
|
||||||
ret << mapFromSource(index);
|
ret << mapFromSource(index);
|
||||||
|
@ -534,7 +515,8 @@ QModelIndexList MergedProxyModel::mapFromSource(const QModelIndexList& source_in
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
QModelIndexList MergedProxyModel::mapToSource(const QModelIndexList& proxy_indexes) const {
|
QModelIndexList MergedProxyModel::mapToSource(
|
||||||
|
const QModelIndexList& proxy_indexes) const {
|
||||||
QModelIndexList ret;
|
QModelIndexList ret;
|
||||||
foreach(const QModelIndex & index, proxy_indexes) {
|
foreach(const QModelIndex & index, proxy_indexes) {
|
||||||
ret << mapToSource(index);
|
ret << mapToSource(index);
|
||||||
|
|
|
@ -34,7 +34,8 @@ class MergedProxyModel : public QAbstractProxyModel {
|
||||||
~MergedProxyModel();
|
~MergedProxyModel();
|
||||||
|
|
||||||
// Make another model appear as a child of the given item in the source model.
|
// Make another model appear as a child of the given item in the source model.
|
||||||
void AddSubModel(const QModelIndex& source_parent, QAbstractItemModel* submodel);
|
void AddSubModel(const QModelIndex& source_parent,
|
||||||
|
QAbstractItemModel* submodel);
|
||||||
void RemoveSubModel(const QModelIndex& source_parent);
|
void RemoveSubModel(const QModelIndex& source_parent);
|
||||||
|
|
||||||
// Find the item in the source model that is the parent of the model
|
// Find the item in the source model that is the parent of the model
|
||||||
|
@ -47,14 +48,16 @@ class MergedProxyModel : public QAbstractProxyModel {
|
||||||
QModelIndex parent(const QModelIndex& child) const;
|
QModelIndex parent(const QModelIndex& child) const;
|
||||||
int rowCount(const QModelIndex& parent) const;
|
int rowCount(const QModelIndex& parent) const;
|
||||||
int columnCount(const QModelIndex& parent) const;
|
int columnCount(const QModelIndex& parent) const;
|
||||||
QVariant data(const QModelIndex &proxyIndex, int role = Qt::DisplayRole) const;
|
QVariant data(const QModelIndex& proxyIndex,
|
||||||
|
int role = Qt::DisplayRole) const;
|
||||||
bool hasChildren(const QModelIndex& parent) const;
|
bool hasChildren(const QModelIndex& parent) const;
|
||||||
QMap<int, QVariant> itemData(const QModelIndex& proxyIndex) const;
|
QMap<int, QVariant> itemData(const QModelIndex& proxyIndex) const;
|
||||||
Qt::ItemFlags flags(const QModelIndex& index) const;
|
Qt::ItemFlags flags(const QModelIndex& index) const;
|
||||||
bool setData(const QModelIndex& index, const QVariant& value, int role);
|
bool setData(const QModelIndex& index, const QVariant& value, int role);
|
||||||
QStringList mimeTypes() const;
|
QStringList mimeTypes() const;
|
||||||
QMimeData* mimeData(const QModelIndexList& indexes) const;
|
QMimeData* mimeData(const QModelIndexList& indexes) const;
|
||||||
bool dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent);
|
bool dropMimeData(const QMimeData* data, Qt::DropAction action, int row,
|
||||||
|
int column, const QModelIndex& parent);
|
||||||
bool canFetchMore(const QModelIndex& parent) const;
|
bool canFetchMore(const QModelIndex& parent) const;
|
||||||
void fetchMore(const QModelIndex& parent);
|
void fetchMore(const QModelIndex& parent);
|
||||||
|
|
||||||
|
@ -77,11 +80,14 @@ class MergedProxyModel : public QAbstractProxyModel {
|
||||||
void SourceModelReset();
|
void SourceModelReset();
|
||||||
void SubModelReset();
|
void SubModelReset();
|
||||||
|
|
||||||
void RowsAboutToBeInserted(const QModelIndex& source_parent, int start, int end);
|
void RowsAboutToBeInserted(const QModelIndex& source_parent, int start,
|
||||||
|
int end);
|
||||||
void RowsInserted(const QModelIndex& source_parent, int start, int end);
|
void RowsInserted(const QModelIndex& source_parent, int start, int end);
|
||||||
void RowsAboutToBeRemoved(const QModelIndex& source_parent, int start, int end);
|
void RowsAboutToBeRemoved(const QModelIndex& source_parent, int start,
|
||||||
|
int end);
|
||||||
void RowsRemoved(const QModelIndex& source_parent, int start, int end);
|
void RowsRemoved(const QModelIndex& source_parent, int start, int end);
|
||||||
void DataChanged(const QModelIndex& top_left, const QModelIndex& bottom_right);
|
void DataChanged(const QModelIndex& top_left,
|
||||||
|
const QModelIndex& bottom_right);
|
||||||
|
|
||||||
void LayoutAboutToBeChanged();
|
void LayoutAboutToBeChanged();
|
||||||
void LayoutChanged();
|
void LayoutChanged();
|
||||||
|
@ -93,7 +99,6 @@ class MergedProxyModel : public QAbstractProxyModel {
|
||||||
void DeleteAllMappings();
|
void DeleteAllMappings();
|
||||||
bool IsKnownModel(const QAbstractItemModel* model) const;
|
bool IsKnownModel(const QAbstractItemModel* model) const;
|
||||||
|
|
||||||
|
|
||||||
QMap<QAbstractItemModel*, QPersistentModelIndex> merge_points_;
|
QMap<QAbstractItemModel*, QPersistentModelIndex> merge_points_;
|
||||||
QAbstractItemModel* resetting_model_;
|
QAbstractItemModel* resetting_model_;
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,8 @@ void RegisterMetaTypes() {
|
||||||
qRegisterMetaType<const char*>("const char*");
|
qRegisterMetaType<const char*>("const char*");
|
||||||
qRegisterMetaType<CoverSearchResult>("CoverSearchResult");
|
qRegisterMetaType<CoverSearchResult>("CoverSearchResult");
|
||||||
qRegisterMetaType<CoverSearchResults>("CoverSearchResults");
|
qRegisterMetaType<CoverSearchResults>("CoverSearchResults");
|
||||||
qRegisterMetaType<DigitallyImportedClient::Channel>("DigitallyImportedClient::Channel");
|
qRegisterMetaType<DigitallyImportedClient::Channel>(
|
||||||
|
"DigitallyImportedClient::Channel");
|
||||||
qRegisterMetaType<Directory>("Directory");
|
qRegisterMetaType<Directory>("Directory");
|
||||||
qRegisterMetaType<DirectoryList>("DirectoryList");
|
qRegisterMetaType<DirectoryList>("DirectoryList");
|
||||||
qRegisterMetaType<Engine::SimpleMetaBundle>("Engine::SimpleMetaBundle");
|
qRegisterMetaType<Engine::SimpleMetaBundle>("Engine::SimpleMetaBundle");
|
||||||
|
@ -49,8 +50,10 @@ void RegisterMetaTypes() {
|
||||||
qRegisterMetaType<PodcastList>("PodcastList");
|
qRegisterMetaType<PodcastList>("PodcastList");
|
||||||
qRegisterMetaType<QList<CoverSearchResult> >("QList<CoverSearchResult>");
|
qRegisterMetaType<QList<CoverSearchResult> >("QList<CoverSearchResult>");
|
||||||
qRegisterMetaType<QList<PlaylistItemPtr> >("QList<PlaylistItemPtr>");
|
qRegisterMetaType<QList<PlaylistItemPtr> >("QList<PlaylistItemPtr>");
|
||||||
qRegisterMetaType<PlaylistSequence::RepeatMode>("PlaylistSequence::RepeatMode");
|
qRegisterMetaType<PlaylistSequence::RepeatMode>(
|
||||||
qRegisterMetaType<PlaylistSequence::ShuffleMode>("PlaylistSequence::ShuffleMode");
|
"PlaylistSequence::RepeatMode");
|
||||||
|
qRegisterMetaType<PlaylistSequence::ShuffleMode>(
|
||||||
|
"PlaylistSequence::ShuffleMode");
|
||||||
qRegisterMetaType<QList<PodcastEpisode> >("QList<PodcastEpisode>");
|
qRegisterMetaType<QList<PodcastEpisode> >("QList<PodcastEpisode>");
|
||||||
qRegisterMetaType<QList<Podcast> >("QList<Podcast>");
|
qRegisterMetaType<QList<Podcast> >("QList<Podcast>");
|
||||||
qRegisterMetaType<QList<QNetworkCookie> >("QList<QNetworkCookie>");
|
qRegisterMetaType<QList<QNetworkCookie> >("QList<QNetworkCookie>");
|
||||||
|
@ -60,14 +63,17 @@ void RegisterMetaTypes() {
|
||||||
qRegisterMetaType<QNetworkReply**>("QNetworkReply**");
|
qRegisterMetaType<QNetworkReply**>("QNetworkReply**");
|
||||||
qRegisterMetaType<SearchProvider::ResultList>("SearchProvider::ResultList");
|
qRegisterMetaType<SearchProvider::ResultList>("SearchProvider::ResultList");
|
||||||
qRegisterMetaType<SearchProvider::Result>("SearchProvider::Result");
|
qRegisterMetaType<SearchProvider::Result>("SearchProvider::Result");
|
||||||
qRegisterMetaType<smart_playlists::GeneratorPtr>("smart_playlists::GeneratorPtr");
|
qRegisterMetaType<smart_playlists::GeneratorPtr>(
|
||||||
|
"smart_playlists::GeneratorPtr");
|
||||||
qRegisterMetaType<SomaFMService::Stream>("SomaFMService::Stream");
|
qRegisterMetaType<SomaFMService::Stream>("SomaFMService::Stream");
|
||||||
qRegisterMetaType<SongList>("SongList");
|
qRegisterMetaType<SongList>("SongList");
|
||||||
qRegisterMetaType<Song>("Song");
|
qRegisterMetaType<Song>("Song");
|
||||||
qRegisterMetaTypeStreamOperators<DigitallyImportedClient::Channel>("DigitallyImportedClient::Channel");
|
qRegisterMetaTypeStreamOperators<DigitallyImportedClient::Channel>(
|
||||||
|
"DigitallyImportedClient::Channel");
|
||||||
qRegisterMetaTypeStreamOperators<Equalizer::Params>("Equalizer::Params");
|
qRegisterMetaTypeStreamOperators<Equalizer::Params>("Equalizer::Params");
|
||||||
qRegisterMetaTypeStreamOperators<QMap<int, int> >("ColumnAlignmentMap");
|
qRegisterMetaTypeStreamOperators<QMap<int, int> >("ColumnAlignmentMap");
|
||||||
qRegisterMetaTypeStreamOperators<SomaFMService::Stream>("SomaFMService::Stream");
|
qRegisterMetaTypeStreamOperators<SomaFMService::Stream>(
|
||||||
|
"SomaFMService::Stream");
|
||||||
qRegisterMetaType<SubdirectoryList>("SubdirectoryList");
|
qRegisterMetaType<SubdirectoryList>("SubdirectoryList");
|
||||||
qRegisterMetaType<Subdirectory>("Subdirectory");
|
qRegisterMetaType<Subdirectory>("Subdirectory");
|
||||||
qRegisterMetaType<QList<QUrl> >("QList<QUrl>");
|
qRegisterMetaType<QList<QUrl> >("QList<QUrl>");
|
||||||
|
|
|
@ -24,8 +24,8 @@ class MimeData : public QMimeData {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
MimeData(bool clear = false, bool play_now = false,
|
MimeData(bool clear = false, bool play_now = false, bool enqueue = false,
|
||||||
bool enqueue = false, bool open_in_new_playlist = false)
|
bool open_in_new_playlist = false)
|
||||||
: override_user_settings_(false),
|
: override_user_settings_(false),
|
||||||
clear_first_(clear),
|
clear_first_(clear),
|
||||||
play_now_(play_now),
|
play_now_(play_now),
|
||||||
|
@ -43,7 +43,8 @@ public:
|
||||||
|
|
||||||
// If this is set then the first item that is inserted will start playing
|
// If this is set then the first item that is inserted will start playing
|
||||||
// immediately. Note: this is always overridden with the user's preference
|
// immediately. Note: this is always overridden with the user's preference
|
||||||
// if the MimeData goes via MainWindow, unless you set override_user_settings_.
|
// if the MimeData goes via MainWindow, unless you set
|
||||||
|
// override_user_settings_.
|
||||||
bool play_now_;
|
bool play_now_;
|
||||||
|
|
||||||
// If this is set then the items are added to the queue after being inserted.
|
// If this is set then the items are added to the queue after being inserted.
|
||||||
|
@ -60,11 +61,14 @@ public:
|
||||||
// the defaults set by the user.
|
// the defaults set by the user.
|
||||||
bool from_doubleclick_;
|
bool from_doubleclick_;
|
||||||
|
|
||||||
// Returns a pretty name for a playlist containing songs described by this MimeData
|
// Returns a pretty name for a playlist containing songs described by this
|
||||||
// object. By pretty name we mean the value of 'name_for_new_playlist_' or generic
|
// MimeData
|
||||||
|
// object. By pretty name we mean the value of 'name_for_new_playlist_' or
|
||||||
|
// generic
|
||||||
// "Playlist" string if the 'name_for_new_playlist_' attribute is empty.
|
// "Playlist" string if the 'name_for_new_playlist_' attribute is empty.
|
||||||
QString get_name_for_new_playlist() {
|
QString get_name_for_new_playlist() {
|
||||||
return name_for_new_playlist_.isEmpty() ? tr("Playlist") : name_for_new_playlist_;
|
return name_for_new_playlist_.isEmpty() ? tr("Playlist")
|
||||||
|
: name_for_new_playlist_;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -8,18 +8,14 @@ template <typename T>
|
||||||
class ModelFutureWatcher : public QFutureWatcher<T> {
|
class ModelFutureWatcher : public QFutureWatcher<T> {
|
||||||
public:
|
public:
|
||||||
ModelFutureWatcher(const QModelIndex& index, QObject* parent = 0)
|
ModelFutureWatcher(const QModelIndex& index, QObject* parent = 0)
|
||||||
: QFutureWatcher<T>(parent),
|
: QFutureWatcher<T>(parent), index_(index) {}
|
||||||
index_(index) {
|
|
||||||
}
|
|
||||||
|
|
||||||
~ModelFutureWatcher() {
|
~ModelFutureWatcher() {}
|
||||||
}
|
|
||||||
|
|
||||||
const QPersistentModelIndex& index() const { return index_; }
|
const QPersistentModelIndex& index() const { return index_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QPersistentModelIndex index_;
|
QPersistentModelIndex index_;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -24,8 +24,7 @@ namespace mpris {
|
||||||
Mpris::Mpris(Application* app, QObject* parent)
|
Mpris::Mpris(Application* app, QObject* parent)
|
||||||
: QObject(parent),
|
: QObject(parent),
|
||||||
mpris1_(new mpris::Mpris1(app, this)),
|
mpris1_(new mpris::Mpris1(app, this)),
|
||||||
mpris2_(new mpris::Mpris2(app, mpris1_, this))
|
mpris2_(new mpris::Mpris2(app, mpris1_, this)) {
|
||||||
{
|
|
||||||
connect(mpris2_, SIGNAL(RaiseMainWindow()), SIGNAL(RaiseMainWindow()));
|
connect(mpris2_, SIGNAL(RaiseMainWindow()), SIGNAL(RaiseMainWindow()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -43,8 +43,7 @@ Mpris1::Mpris1(Application* app, QObject* parent,
|
||||||
dbus_service_name_(dbus_service_name),
|
dbus_service_name_(dbus_service_name),
|
||||||
root_(nullptr),
|
root_(nullptr),
|
||||||
player_(nullptr),
|
player_(nullptr),
|
||||||
tracklist_(nullptr)
|
tracklist_(nullptr) {
|
||||||
{
|
|
||||||
qDBusRegisterMetaType<DBusStatus>();
|
qDBusRegisterMetaType<DBusStatus>();
|
||||||
qDBusRegisterMetaType<Version>();
|
qDBusRegisterMetaType<Version>();
|
||||||
|
|
||||||
|
@ -53,7 +52,8 @@ Mpris1::Mpris1(Application* app, QObject* parent,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!QDBusConnection::sessionBus().registerService(dbus_service_name_)) {
|
if (!QDBusConnection::sessionBus().registerService(dbus_service_name_)) {
|
||||||
qLog(Warning) << "Failed to register" << dbus_service_name_ << "on the session bus";
|
qLog(Warning) << "Failed to register" << dbus_service_name_
|
||||||
|
<< "on the session bus";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,8 +61,10 @@ Mpris1::Mpris1(Application* app, QObject* parent,
|
||||||
player_ = new Mpris1Player(app, this);
|
player_ = new Mpris1Player(app, this);
|
||||||
tracklist_ = new Mpris1TrackList(app, this);
|
tracklist_ = new Mpris1TrackList(app, this);
|
||||||
|
|
||||||
connect(app->current_art_loader(), SIGNAL(ArtLoaded(const Song&, const QString&, const QImage&)),
|
connect(app->current_art_loader(),
|
||||||
player_, SLOT(CurrentSongChanged(const Song&, const QString&, const QImage&)));
|
SIGNAL(ArtLoaded(const Song&, const QString&, const QImage&)),
|
||||||
|
player_,
|
||||||
|
SLOT(CurrentSongChanged(const Song&, const QString&, const QImage&)));
|
||||||
}
|
}
|
||||||
|
|
||||||
Mpris1::~Mpris1() {
|
Mpris1::~Mpris1() {
|
||||||
|
@ -70,27 +72,29 @@ Mpris1::~Mpris1() {
|
||||||
}
|
}
|
||||||
|
|
||||||
Mpris1Root::Mpris1Root(Application* app, QObject* parent)
|
Mpris1Root::Mpris1Root(Application* app, QObject* parent)
|
||||||
: QObject(parent),
|
: QObject(parent), app_(app) {
|
||||||
app_(app) {
|
|
||||||
new MprisRoot(this);
|
new MprisRoot(this);
|
||||||
QDBusConnection::sessionBus().registerObject("/", this);
|
QDBusConnection::sessionBus().registerObject("/", this);
|
||||||
}
|
}
|
||||||
|
|
||||||
Mpris1Player::Mpris1Player(Application* app, QObject* parent)
|
Mpris1Player::Mpris1Player(Application* app, QObject* parent)
|
||||||
: QObject(parent),
|
: QObject(parent), app_(app) {
|
||||||
app_(app) {
|
|
||||||
new MprisPlayer(this);
|
new MprisPlayer(this);
|
||||||
QDBusConnection::sessionBus().registerObject("/Player", this);
|
QDBusConnection::sessionBus().registerObject("/Player", this);
|
||||||
|
|
||||||
connect(app_->player()->engine(), SIGNAL(StateChanged(Engine::State)), SLOT(EngineStateChanged(Engine::State)));
|
connect(app_->player()->engine(), SIGNAL(StateChanged(Engine::State)),
|
||||||
connect(app_->playlist_manager(), SIGNAL(PlaylistManagerInitialized()), SLOT(PlaylistManagerInitialized()));
|
SLOT(EngineStateChanged(Engine::State)));
|
||||||
|
connect(app_->playlist_manager(), SIGNAL(PlaylistManagerInitialized()),
|
||||||
|
SLOT(PlaylistManagerInitialized()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// when PlaylistManager gets it ready, we connect PlaylistSequence with this
|
// when PlaylistManager gets it ready, we connect PlaylistSequence with this
|
||||||
void Mpris1Player::PlaylistManagerInitialized() {
|
void Mpris1Player::PlaylistManagerInitialized() {
|
||||||
connect(app_->playlist_manager()->sequence(), SIGNAL(ShuffleModeChanged(PlaylistSequence::ShuffleMode)),
|
connect(app_->playlist_manager()->sequence(),
|
||||||
|
SIGNAL(ShuffleModeChanged(PlaylistSequence::ShuffleMode)),
|
||||||
SLOT(ShuffleModeChanged()));
|
SLOT(ShuffleModeChanged()));
|
||||||
connect(app_->playlist_manager()->sequence(), SIGNAL(RepeatModeChanged(PlaylistSequence::RepeatMode)),
|
connect(app_->playlist_manager()->sequence(),
|
||||||
|
SIGNAL(RepeatModeChanged(PlaylistSequence::RepeatMode)),
|
||||||
SLOT(RepeatModeChanged()));
|
SLOT(RepeatModeChanged()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,7 +103,8 @@ Mpris1TrackList::Mpris1TrackList(Application* app, QObject* parent)
|
||||||
new MprisTrackList(this);
|
new MprisTrackList(this);
|
||||||
QDBusConnection::sessionBus().registerObject("/TrackList", this);
|
QDBusConnection::sessionBus().registerObject("/TrackList", this);
|
||||||
|
|
||||||
connect(app_->playlist_manager(), SIGNAL(PlaylistChanged(Playlist*)), SLOT(PlaylistChanged(Playlist*)));
|
connect(app_->playlist_manager(), SIGNAL(PlaylistChanged(Playlist*)),
|
||||||
|
SLOT(PlaylistChanged(Playlist*)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Mpris1TrackList::PlaylistChanged(Playlist* playlist) {
|
void Mpris1TrackList::PlaylistChanged(Playlist* playlist) {
|
||||||
|
@ -113,8 +118,8 @@ void Mpris1Player::EngineStateChanged(Engine::State state) {
|
||||||
emit CapsChange(GetCaps(state));
|
emit CapsChange(GetCaps(state));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Mpris1Player::CurrentSongChanged(
|
void Mpris1Player::CurrentSongChanged(const Song& song, const QString& art_uri,
|
||||||
const Song& song, const QString& art_uri, const QImage&) {
|
const QImage&) {
|
||||||
last_metadata_ = Mpris1::GetMetadata(song);
|
last_metadata_ = Mpris1::GetMetadata(song);
|
||||||
|
|
||||||
if (!art_uri.isEmpty()) {
|
if (!art_uri.isEmpty()) {
|
||||||
|
@ -126,10 +131,8 @@ void Mpris1Player::CurrentSongChanged(
|
||||||
emit CapsChange(GetCaps());
|
emit CapsChange(GetCaps());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
QString Mpris1Root::Identity() {
|
QString Mpris1Root::Identity() {
|
||||||
return QString("%1 %2").arg(
|
return QString("%1 %2").arg(QCoreApplication::applicationName(),
|
||||||
QCoreApplication::applicationName(),
|
|
||||||
QCoreApplication::applicationVersion());
|
QCoreApplication::applicationVersion());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,42 +143,26 @@ Version Mpris1Root::MprisVersion() {
|
||||||
return version;
|
return version;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Mpris1Root::Quit() {
|
void Mpris1Root::Quit() { qApp->quit(); }
|
||||||
qApp->quit();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Mpris1Player::Pause() {
|
void Mpris1Player::Pause() { app_->player()->PlayPause(); }
|
||||||
app_->player()->PlayPause();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Mpris1Player::Stop() {
|
void Mpris1Player::Stop() { app_->player()->Stop(); }
|
||||||
app_->player()->Stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Mpris1Player::Prev() {
|
void Mpris1Player::Prev() { app_->player()->Previous(); }
|
||||||
app_->player()->Previous();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Mpris1Player::Play() {
|
void Mpris1Player::Play() { app_->player()->Play(); }
|
||||||
app_->player()->Play();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Mpris1Player::Next() {
|
void Mpris1Player::Next() { app_->player()->Next(); }
|
||||||
app_->player()->Next();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Mpris1Player::Repeat(bool repeat) {
|
void Mpris1Player::Repeat(bool repeat) {
|
||||||
app_->playlist_manager()->sequence()->SetRepeatMode(
|
app_->playlist_manager()->sequence()->SetRepeatMode(
|
||||||
repeat ? PlaylistSequence::Repeat_Track : PlaylistSequence::Repeat_Off);
|
repeat ? PlaylistSequence::Repeat_Track : PlaylistSequence::Repeat_Off);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Mpris1Player::ShuffleModeChanged() {
|
void Mpris1Player::ShuffleModeChanged() { emit StatusChange(GetStatus()); }
|
||||||
emit StatusChange(GetStatus());
|
|
||||||
}
|
|
||||||
|
|
||||||
void Mpris1Player::RepeatModeChanged() {
|
void Mpris1Player::RepeatModeChanged() { emit StatusChange(GetStatus()); }
|
||||||
emit StatusChange(GetStatus());
|
|
||||||
}
|
|
||||||
|
|
||||||
DBusStatus Mpris1Player::GetStatus() const {
|
DBusStatus Mpris1Player::GetStatus() const {
|
||||||
return GetStatus(app_->player()->GetState());
|
return GetStatus(app_->player()->GetState());
|
||||||
|
@ -199,25 +186,27 @@ DBusStatus Mpris1Player::GetStatus(Engine::State state) const {
|
||||||
|
|
||||||
if (app_->playlist_manager()->sequence()) {
|
if (app_->playlist_manager()->sequence()) {
|
||||||
PlaylistManagerInterface* playlists_ = app_->playlist_manager();
|
PlaylistManagerInterface* playlists_ = app_->playlist_manager();
|
||||||
PlaylistSequence::RepeatMode repeat_mode = playlists_->sequence()->repeat_mode();
|
PlaylistSequence::RepeatMode repeat_mode =
|
||||||
|
playlists_->sequence()->repeat_mode();
|
||||||
|
|
||||||
status.random = playlists_->sequence()->shuffle_mode() == PlaylistSequence::Shuffle_Off ? 0 : 1;
|
status.random =
|
||||||
|
playlists_->sequence()->shuffle_mode() == PlaylistSequence::Shuffle_Off
|
||||||
|
? 0
|
||||||
|
: 1;
|
||||||
status.repeat = repeat_mode == PlaylistSequence::Repeat_Track ? 1 : 0;
|
status.repeat = repeat_mode == PlaylistSequence::Repeat_Track ? 1 : 0;
|
||||||
status.repeat_playlist = (repeat_mode == PlaylistSequence::Repeat_Album ||
|
status.repeat_playlist =
|
||||||
|
(repeat_mode == PlaylistSequence::Repeat_Album ||
|
||||||
repeat_mode == PlaylistSequence::Repeat_Playlist ||
|
repeat_mode == PlaylistSequence::Repeat_Playlist ||
|
||||||
repeat_mode == PlaylistSequence::Repeat_Track) ? 1 : 0;
|
repeat_mode == PlaylistSequence::Repeat_Track)
|
||||||
|
? 1
|
||||||
|
: 0;
|
||||||
}
|
}
|
||||||
return status;
|
return status;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Mpris1Player::VolumeSet(int volume) {
|
void Mpris1Player::VolumeSet(int volume) { app_->player()->SetVolume(volume); }
|
||||||
app_->player()->SetVolume(volume);
|
|
||||||
}
|
|
||||||
|
|
||||||
int Mpris1Player::VolumeGet() const {
|
int Mpris1Player::VolumeGet() const { return app_->player()->GetVolume(); }
|
||||||
return app_->player()->GetVolume();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Mpris1Player::PositionSet(int pos_msec) {
|
void Mpris1Player::PositionSet(int pos_msec) {
|
||||||
app_->player()->SeekTo(pos_msec / kMsecPerSec);
|
app_->player()->SeekTo(pos_msec / kMsecPerSec);
|
||||||
|
@ -227,9 +216,7 @@ int Mpris1Player::PositionGet() const {
|
||||||
return app_->player()->engine()->position_nanosec() / kNsecPerMsec;
|
return app_->player()->engine()->position_nanosec() / kNsecPerMsec;
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariantMap Mpris1Player::GetMetadata() const {
|
QVariantMap Mpris1Player::GetMetadata() const { return last_metadata_; }
|
||||||
return last_metadata_;
|
|
||||||
}
|
|
||||||
|
|
||||||
int Mpris1Player::GetCaps() const {
|
int Mpris1Player::GetCaps() const {
|
||||||
return GetCaps(app_->player()->GetState());
|
return GetCaps(app_->player()->GetState());
|
||||||
|
@ -241,9 +228,12 @@ int Mpris1Player::GetCaps(Engine::State state) const {
|
||||||
PlaylistManagerInterface* playlists = app_->playlist_manager();
|
PlaylistManagerInterface* playlists = app_->playlist_manager();
|
||||||
|
|
||||||
if (playlists->active()) {
|
if (playlists->active()) {
|
||||||
// play is disabled when playlist is empty or when last.fm stream is already playing
|
// play is disabled when playlist is empty or when last.fm stream is already
|
||||||
if (playlists->active() && playlists->active()->rowCount() != 0
|
// playing
|
||||||
&& !(state == Engine::Playing && (app_->player()->GetCurrentItem()->options() & PlaylistItem::LastFMControls))) {
|
if (playlists->active() && playlists->active()->rowCount() != 0 &&
|
||||||
|
!(state == Engine::Playing &&
|
||||||
|
(app_->player()->GetCurrentItem()->options() &
|
||||||
|
PlaylistItem::LastFMControls))) {
|
||||||
caps |= CAN_PLAY;
|
caps |= CAN_PLAY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -257,7 +247,8 @@ int Mpris1Player::GetCaps(Engine::State state) const {
|
||||||
|
|
||||||
if (current_item) {
|
if (current_item) {
|
||||||
caps |= CAN_PROVIDE_METADATA;
|
caps |= CAN_PROVIDE_METADATA;
|
||||||
if (state == Engine::Playing && !(current_item->options() & PlaylistItem::PauseDisabled)) {
|
if (state == Engine::Playing &&
|
||||||
|
!(current_item->options() & PlaylistItem::PauseDisabled)) {
|
||||||
caps |= CAN_PAUSE;
|
caps |= CAN_PAUSE;
|
||||||
}
|
}
|
||||||
if (state != Engine::Empty && !current_item->Metadata().is_stream()) {
|
if (state != Engine::Empty && !current_item->Metadata().is_stream()) {
|
||||||
|
@ -268,25 +259,17 @@ int Mpris1Player::GetCaps(Engine::State state) const {
|
||||||
return caps;
|
return caps;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Mpris1Player::VolumeUp(int change) {
|
void Mpris1Player::VolumeUp(int change) { VolumeSet(VolumeGet() + change); }
|
||||||
VolumeSet(VolumeGet() + change);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Mpris1Player::VolumeDown(int change) {
|
void Mpris1Player::VolumeDown(int change) { VolumeSet(VolumeGet() - change); }
|
||||||
VolumeSet(VolumeGet() - change);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Mpris1Player::Mute() {
|
void Mpris1Player::Mute() { app_->player()->Mute(); }
|
||||||
app_->player()->Mute();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Mpris1Player::ShowOSD() {
|
void Mpris1Player::ShowOSD() { app_->player()->ShowOSD(); }
|
||||||
app_->player()->ShowOSD();
|
|
||||||
}
|
|
||||||
|
|
||||||
int Mpris1TrackList::AddTrack(const QString& track, bool play) {
|
int Mpris1TrackList::AddTrack(const QString& track, bool play) {
|
||||||
app_->playlist_manager()->active()->InsertUrls(
|
app_->playlist_manager()->active()->InsertUrls(QList<QUrl>() << QUrl(track),
|
||||||
QList<QUrl>() << QUrl(track), -1, play);
|
-1, play);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -304,15 +287,15 @@ int Mpris1TrackList::GetLength() const {
|
||||||
|
|
||||||
QVariantMap Mpris1TrackList::GetMetadata(int pos) const {
|
QVariantMap Mpris1TrackList::GetMetadata(int pos) const {
|
||||||
PlaylistItemPtr item = app_->player()->GetItemAt(pos);
|
PlaylistItemPtr item = app_->player()->GetItemAt(pos);
|
||||||
if (!item)
|
if (!item) return QVariantMap();
|
||||||
return QVariantMap();
|
|
||||||
|
|
||||||
return Mpris1::GetMetadata(item->Metadata());
|
return Mpris1::GetMetadata(item->Metadata());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Mpris1TrackList::SetLoop(bool enable) {
|
void Mpris1TrackList::SetLoop(bool enable) {
|
||||||
app_->playlist_manager()->active()->sequence()->SetRepeatMode(
|
app_->playlist_manager()->active()->sequence()->SetRepeatMode(
|
||||||
enable ? PlaylistSequence::Repeat_Playlist : PlaylistSequence::Repeat_Off);
|
enable ? PlaylistSequence::Repeat_Playlist
|
||||||
|
: PlaylistSequence::Repeat_Off);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Mpris1TrackList::SetRandom(bool enable) {
|
void Mpris1TrackList::SetRandom(bool enable) {
|
||||||
|
@ -353,7 +336,6 @@ QVariantMap Mpris1::GetMetadata(const Song& song) {
|
||||||
|
|
||||||
} // namespace mpris
|
} // namespace mpris
|
||||||
|
|
||||||
|
|
||||||
QDBusArgument& operator<<(QDBusArgument& arg, const Version& version) {
|
QDBusArgument& operator<<(QDBusArgument& arg, const Version& version) {
|
||||||
arg.beginStructure();
|
arg.beginStructure();
|
||||||
arg << version.major << version.minor;
|
arg << version.major << version.minor;
|
||||||
|
|
|
@ -29,16 +29,13 @@ class Playlist;
|
||||||
|
|
||||||
struct DBusStatus { // From Amarok.
|
struct DBusStatus { // From Amarok.
|
||||||
DBusStatus()
|
DBusStatus()
|
||||||
: play(Mpris_Stopped),
|
: play(Mpris_Stopped), random(0), repeat(0), repeat_playlist(0) {}
|
||||||
random(0),
|
|
||||||
repeat(0),
|
|
||||||
repeat_playlist(0)
|
|
||||||
{}
|
|
||||||
|
|
||||||
int play; // Playing = 0, Paused = 1, Stopped = 2
|
int play; // Playing = 0, Paused = 1, Stopped = 2
|
||||||
int random; // Linearly = 0, Randomly = 1
|
int random; // Linearly = 0, Randomly = 1
|
||||||
int repeat; // Go_To_Next = 0, Repeat_Current = 1
|
int repeat; // Go_To_Next = 0, Repeat_Current = 1
|
||||||
int repeat_playlist; // Stop_When_Finished = 0, Never_Give_Up_Playing = 1, Never_Let_You_Down = 42
|
int repeat_playlist; // Stop_When_Finished = 0, Never_Give_Up_Playing = 1,
|
||||||
|
// Never_Let_You_Down = 42
|
||||||
|
|
||||||
enum MprisPlayState {
|
enum MprisPlayState {
|
||||||
Mpris_Playing = 0,
|
Mpris_Playing = 0,
|
||||||
|
@ -51,7 +48,6 @@ Q_DECLARE_METATYPE(DBusStatus);
|
||||||
QDBusArgument& operator<<(QDBusArgument& arg, const DBusStatus& status);
|
QDBusArgument& operator<<(QDBusArgument& arg, const DBusStatus& status);
|
||||||
const QDBusArgument& operator>>(const QDBusArgument& arg, DBusStatus& status);
|
const QDBusArgument& operator>>(const QDBusArgument& arg, DBusStatus& status);
|
||||||
|
|
||||||
|
|
||||||
struct Version {
|
struct Version {
|
||||||
quint16 minor;
|
quint16 minor;
|
||||||
quint16 major;
|
quint16 major;
|
||||||
|
@ -78,7 +74,6 @@ class Mpris1Root;
|
||||||
class Mpris1Player;
|
class Mpris1Player;
|
||||||
class Mpris1TrackList;
|
class Mpris1TrackList;
|
||||||
|
|
||||||
|
|
||||||
class Mpris1 : public QObject {
|
class Mpris1 : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
|
@ -103,7 +98,6 @@ private:
|
||||||
Mpris1TrackList* tracklist_;
|
Mpris1TrackList* tracklist_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class Mpris1Root : public QObject {
|
class Mpris1Root : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
|
@ -118,7 +112,6 @@ private:
|
||||||
Application* app_;
|
Application* app_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class Mpris1Player : public QObject {
|
class Mpris1Player : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
|
@ -132,7 +125,8 @@ public:
|
||||||
void Next();
|
void Next();
|
||||||
void Repeat(bool);
|
void Repeat(bool);
|
||||||
|
|
||||||
// those methods will use engine's state obtained with player->GetState() method
|
// those methods will use engine's state obtained with player->GetState()
|
||||||
|
// method
|
||||||
DBusStatus GetStatus() const;
|
DBusStatus GetStatus() const;
|
||||||
int GetCaps() const;
|
int GetCaps() const;
|
||||||
// those methods will use engine's state provided as an argument
|
// those methods will use engine's state provided as an argument
|
||||||
|
@ -152,8 +146,8 @@ public:
|
||||||
void ShowOSD();
|
void ShowOSD();
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void CurrentSongChanged(
|
void CurrentSongChanged(const Song& song, const QString& art_uri,
|
||||||
const Song& song, const QString& art_uri, const QImage&);
|
const QImage&);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void CapsChange(int);
|
void CapsChange(int);
|
||||||
|
@ -173,7 +167,6 @@ private:
|
||||||
QVariantMap last_metadata_;
|
QVariantMap last_metadata_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class Mpris1TrackList : public QObject {
|
class Mpris1TrackList : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
|
|
|
@ -48,8 +48,8 @@ QDBusArgument& operator<< (QDBusArgument& arg, const MprisPlaylist& playlist) {
|
||||||
return arg;
|
return arg;
|
||||||
}
|
}
|
||||||
|
|
||||||
const QDBusArgument& operator>> (
|
const QDBusArgument& operator>>(const QDBusArgument& arg,
|
||||||
const QDBusArgument& arg, MprisPlaylist& playlist) {
|
MprisPlaylist& playlist) {
|
||||||
arg.beginStructure();
|
arg.beginStructure();
|
||||||
arg >> playlist.id >> playlist.name >> playlist.icon;
|
arg >> playlist.id >> playlist.name >> playlist.icon;
|
||||||
arg.endStructure();
|
arg.endStructure();
|
||||||
|
@ -64,8 +64,8 @@ QDBusArgument& operator<< (QDBusArgument& arg, const MaybePlaylist& playlist) {
|
||||||
return arg;
|
return arg;
|
||||||
}
|
}
|
||||||
|
|
||||||
const QDBusArgument& operator>> (
|
const QDBusArgument& operator>>(const QDBusArgument& arg,
|
||||||
const QDBusArgument& arg, MaybePlaylist& playlist) {
|
MaybePlaylist& playlist) {
|
||||||
arg.beginStructure();
|
arg.beginStructure();
|
||||||
arg >> playlist.valid >> playlist.playlist;
|
arg >> playlist.valid >> playlist.playlist;
|
||||||
arg.endStructure();
|
arg.endStructure();
|
||||||
|
@ -79,39 +79,45 @@ const char* Mpris2::kServiceName = "org.mpris.MediaPlayer2.clementine";
|
||||||
const char* Mpris2::kFreedesktopPath = "org.freedesktop.DBus.Properties";
|
const char* Mpris2::kFreedesktopPath = "org.freedesktop.DBus.Properties";
|
||||||
|
|
||||||
Mpris2::Mpris2(Application* app, Mpris1* mpris1, QObject* parent)
|
Mpris2::Mpris2(Application* app, Mpris1* mpris1, QObject* parent)
|
||||||
: QObject(parent),
|
: QObject(parent), app_(app), mpris1_(mpris1) {
|
||||||
app_(app),
|
|
||||||
mpris1_(mpris1)
|
|
||||||
{
|
|
||||||
new Mpris2Root(this);
|
new Mpris2Root(this);
|
||||||
new Mpris2TrackList(this);
|
new Mpris2TrackList(this);
|
||||||
new Mpris2Player(this);
|
new Mpris2Player(this);
|
||||||
new Mpris2Playlists(this);
|
new Mpris2Playlists(this);
|
||||||
|
|
||||||
if (!QDBusConnection::sessionBus().registerService(kServiceName)) {
|
if (!QDBusConnection::sessionBus().registerService(kServiceName)) {
|
||||||
qLog(Warning) << "Failed to register" << QString(kServiceName) << "on the session bus";
|
qLog(Warning) << "Failed to register" << QString(kServiceName)
|
||||||
|
<< "on the session bus";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QDBusConnection::sessionBus().registerObject(kMprisObjectPath, this);
|
QDBusConnection::sessionBus().registerObject(kMprisObjectPath, this);
|
||||||
|
|
||||||
connect(app_->current_art_loader(), SIGNAL(ArtLoaded(Song,QString,QImage)), SLOT(ArtLoaded(Song,QString)));
|
connect(app_->current_art_loader(), SIGNAL(ArtLoaded(Song, QString, QImage)),
|
||||||
|
SLOT(ArtLoaded(Song, QString)));
|
||||||
|
|
||||||
connect(app_->player()->engine(), SIGNAL(StateChanged(Engine::State)), SLOT(EngineStateChanged(Engine::State)));
|
connect(app_->player()->engine(), SIGNAL(StateChanged(Engine::State)),
|
||||||
|
SLOT(EngineStateChanged(Engine::State)));
|
||||||
connect(app_->player(), SIGNAL(VolumeChanged(int)), SLOT(VolumeChanged()));
|
connect(app_->player(), SIGNAL(VolumeChanged(int)), SLOT(VolumeChanged()));
|
||||||
connect(app_->player(), SIGNAL(Seeked(qlonglong)), SIGNAL(Seeked(qlonglong)));
|
connect(app_->player(), SIGNAL(Seeked(qlonglong)), SIGNAL(Seeked(qlonglong)));
|
||||||
|
|
||||||
connect(app_->playlist_manager(), SIGNAL(PlaylistManagerInitialized()), SLOT(PlaylistManagerInitialized()));
|
connect(app_->playlist_manager(), SIGNAL(PlaylistManagerInitialized()),
|
||||||
connect(app_->playlist_manager(), SIGNAL(CurrentSongChanged(Song)), SLOT(CurrentSongChanged(Song)));
|
SLOT(PlaylistManagerInitialized()));
|
||||||
connect(app_->playlist_manager(), SIGNAL(PlaylistChanged(Playlist*)), SLOT(PlaylistChanged(Playlist*)));
|
connect(app_->playlist_manager(), SIGNAL(CurrentSongChanged(Song)),
|
||||||
connect(app_->playlist_manager(), SIGNAL(CurrentChanged(Playlist*)), SLOT(PlaylistCollectionChanged(Playlist*)));
|
SLOT(CurrentSongChanged(Song)));
|
||||||
|
connect(app_->playlist_manager(), SIGNAL(PlaylistChanged(Playlist*)),
|
||||||
|
SLOT(PlaylistChanged(Playlist*)));
|
||||||
|
connect(app_->playlist_manager(), SIGNAL(CurrentChanged(Playlist*)),
|
||||||
|
SLOT(PlaylistCollectionChanged(Playlist*)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// when PlaylistManager gets it ready, we connect PlaylistSequence with this
|
// when PlaylistManager gets it ready, we connect PlaylistSequence with this
|
||||||
void Mpris2::PlaylistManagerInitialized() {
|
void Mpris2::PlaylistManagerInitialized() {
|
||||||
connect(app_->playlist_manager()->sequence(), SIGNAL(ShuffleModeChanged(PlaylistSequence::ShuffleMode)),
|
connect(app_->playlist_manager()->sequence(),
|
||||||
|
SIGNAL(ShuffleModeChanged(PlaylistSequence::ShuffleMode)),
|
||||||
SLOT(ShuffleModeChanged()));
|
SLOT(ShuffleModeChanged()));
|
||||||
connect(app_->playlist_manager()->sequence(), SIGNAL(RepeatModeChanged(PlaylistSequence::RepeatMode)),
|
connect(app_->playlist_manager()->sequence(),
|
||||||
|
SIGNAL(RepeatModeChanged(PlaylistSequence::RepeatMode)),
|
||||||
SLOT(RepeatModeChanged()));
|
SLOT(RepeatModeChanged()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,65 +130,54 @@ void Mpris2::EngineStateChanged(Engine::State newState) {
|
||||||
EmitNotification("PlaybackStatus", PlaybackStatus(newState));
|
EmitNotification("PlaybackStatus", PlaybackStatus(newState));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Mpris2::VolumeChanged() {
|
void Mpris2::VolumeChanged() { EmitNotification("Volume"); }
|
||||||
EmitNotification("Volume");
|
|
||||||
}
|
|
||||||
|
|
||||||
void Mpris2::ShuffleModeChanged() {
|
void Mpris2::ShuffleModeChanged() { EmitNotification("Shuffle"); }
|
||||||
EmitNotification("Shuffle");
|
|
||||||
}
|
|
||||||
|
|
||||||
void Mpris2::RepeatModeChanged() {
|
void Mpris2::RepeatModeChanged() { EmitNotification("LoopStatus"); }
|
||||||
EmitNotification("LoopStatus");
|
|
||||||
}
|
|
||||||
|
|
||||||
void Mpris2::EmitNotification(const QString& name, const QVariant& val) {
|
void Mpris2::EmitNotification(const QString& name, const QVariant& val) {
|
||||||
EmitNotification(name, val, "org.mpris.MediaPlayer2.Player");
|
EmitNotification(name, val, "org.mpris.MediaPlayer2.Player");
|
||||||
}
|
}
|
||||||
|
|
||||||
void Mpris2::EmitNotification(const QString& name, const QVariant& val, const QString& mprisEntity) {
|
void Mpris2::EmitNotification(const QString& name, const QVariant& val,
|
||||||
|
const QString& mprisEntity) {
|
||||||
QDBusMessage msg = QDBusMessage::createSignal(
|
QDBusMessage msg = QDBusMessage::createSignal(
|
||||||
kMprisObjectPath, kFreedesktopPath, "PropertiesChanged");
|
kMprisObjectPath, kFreedesktopPath, "PropertiesChanged");
|
||||||
QVariantMap map;
|
QVariantMap map;
|
||||||
map.insert(name, val);
|
map.insert(name, val);
|
||||||
QVariantList args = QVariantList()
|
QVariantList args = QVariantList() << mprisEntity << map << QStringList();
|
||||||
<< mprisEntity
|
|
||||||
<< map
|
|
||||||
<< QStringList();
|
|
||||||
msg.setArguments(args);
|
msg.setArguments(args);
|
||||||
QDBusConnection::sessionBus().send(msg);
|
QDBusConnection::sessionBus().send(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Mpris2::EmitNotification(const QString& name) {
|
void Mpris2::EmitNotification(const QString& name) {
|
||||||
QVariant value;
|
QVariant value;
|
||||||
if (name == "PlaybackStatus") value = PlaybackStatus();
|
if (name == "PlaybackStatus")
|
||||||
else if (name == "LoopStatus") value = LoopStatus();
|
value = PlaybackStatus();
|
||||||
else if (name == "Shuffle") value = Shuffle();
|
else if (name == "LoopStatus")
|
||||||
else if (name == "Metadata") value = Metadata();
|
value = LoopStatus();
|
||||||
else if (name == "Volume") value = Volume();
|
else if (name == "Shuffle")
|
||||||
else if (name == "Position") value = Position();
|
value = Shuffle();
|
||||||
|
else if (name == "Metadata")
|
||||||
|
value = Metadata();
|
||||||
|
else if (name == "Volume")
|
||||||
|
value = Volume();
|
||||||
|
else if (name == "Position")
|
||||||
|
value = Position();
|
||||||
|
|
||||||
if (value.isValid())
|
if (value.isValid()) EmitNotification(name, value);
|
||||||
EmitNotification(name, value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------Root Interface--------------------------//
|
//------------------Root Interface--------------------------//
|
||||||
|
|
||||||
bool Mpris2::CanQuit() const {
|
bool Mpris2::CanQuit() const { return true; }
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Mpris2::CanRaise() const {
|
bool Mpris2::CanRaise() const { return true; }
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Mpris2::HasTrackList() const {
|
bool Mpris2::HasTrackList() const { return true; }
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Mpris2::Identity() const {
|
QString Mpris2::Identity() const { return QCoreApplication::applicationName(); }
|
||||||
return QCoreApplication::applicationName();
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Mpris2::DesktopEntryAbsolutePath() const {
|
QString Mpris2::DesktopEntryAbsolutePath() const {
|
||||||
QStringList xdg_data_dirs = QString(getenv("XDG_DATA_DIRS")).split(":");
|
QStringList xdg_data_dirs = QString(getenv("XDG_DATA_DIRS")).split(":");
|
||||||
|
@ -190,10 +185,9 @@ QString Mpris2::DesktopEntryAbsolutePath() const {
|
||||||
xdg_data_dirs.append("/usr/share/");
|
xdg_data_dirs.append("/usr/share/");
|
||||||
|
|
||||||
foreach(const QString & directory, xdg_data_dirs) {
|
foreach(const QString & directory, xdg_data_dirs) {
|
||||||
QString path = QString("%1/applications/%2.desktop").
|
QString path = QString("%1/applications/%2.desktop").arg(
|
||||||
arg(directory, QApplication::applicationName().toLower());
|
directory, QApplication::applicationName().toLower());
|
||||||
if (QFile::exists(path))
|
if (QFile::exists(path)) return path;
|
||||||
return path;
|
|
||||||
}
|
}
|
||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
|
@ -203,8 +197,7 @@ QString Mpris2::DesktopEntry() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList Mpris2::SupportedUriSchemes() const {
|
QStringList Mpris2::SupportedUriSchemes() const {
|
||||||
static QStringList res = QStringList()
|
static QStringList res = QStringList() << "file"
|
||||||
<< "file"
|
|
||||||
<< "http"
|
<< "http"
|
||||||
<< "cdda"
|
<< "cdda"
|
||||||
<< "smb"
|
<< "smb"
|
||||||
|
@ -213,8 +206,7 @@ QStringList Mpris2::SupportedUriSchemes() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList Mpris2::SupportedMimeTypes() const {
|
QStringList Mpris2::SupportedMimeTypes() const {
|
||||||
static QStringList res = QStringList()
|
static QStringList res = QStringList() << "application/ogg"
|
||||||
<< "application/ogg"
|
|
||||||
<< "application/x-ogg"
|
<< "application/x-ogg"
|
||||||
<< "application/x-ogm-audio"
|
<< "application/x-ogm-audio"
|
||||||
<< "audio/aac"
|
<< "audio/aac"
|
||||||
|
@ -242,13 +234,9 @@ QStringList Mpris2::SupportedMimeTypes() const {
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Mpris2::Raise() {
|
void Mpris2::Raise() { emit RaiseMainWindow(); }
|
||||||
emit RaiseMainWindow();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Mpris2::Quit() {
|
void Mpris2::Quit() { qApp->quit(); }
|
||||||
qApp->quit();
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Mpris2::PlaybackStatus() const {
|
QString Mpris2::PlaybackStatus() const {
|
||||||
return PlaybackStatus(app_->player()->GetState());
|
return PlaybackStatus(app_->player()->GetState());
|
||||||
|
@ -256,9 +244,12 @@ QString Mpris2::PlaybackStatus() const {
|
||||||
|
|
||||||
QString Mpris2::PlaybackStatus(Engine::State state) const {
|
QString Mpris2::PlaybackStatus(Engine::State state) const {
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case Engine::Playing: return "Playing";
|
case Engine::Playing:
|
||||||
case Engine::Paused: return "Paused";
|
return "Playing";
|
||||||
default: return "Stopped";
|
case Engine::Paused:
|
||||||
|
return "Paused";
|
||||||
|
default:
|
||||||
|
return "Stopped";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -269,9 +260,12 @@ QString Mpris2::LoopStatus() const {
|
||||||
|
|
||||||
switch (app_->playlist_manager()->sequence()->repeat_mode()) {
|
switch (app_->playlist_manager()->sequence()->repeat_mode()) {
|
||||||
case PlaylistSequence::Repeat_Album:
|
case PlaylistSequence::Repeat_Album:
|
||||||
case PlaylistSequence::Repeat_Playlist: return "Playlist";
|
case PlaylistSequence::Repeat_Playlist:
|
||||||
case PlaylistSequence::Repeat_Track: return "Track";
|
return "Playlist";
|
||||||
default: return "None";
|
case PlaylistSequence::Repeat_Track:
|
||||||
|
return "Track";
|
||||||
|
default:
|
||||||
|
return "None";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -289,9 +283,7 @@ void Mpris2::SetLoopStatus(const QString& value) {
|
||||||
app_->playlist_manager()->active()->sequence()->SetRepeatMode(mode);
|
app_->playlist_manager()->active()->sequence()->SetRepeatMode(mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
double Mpris2::Rate() const {
|
double Mpris2::Rate() const { return 1.0; }
|
||||||
return 1.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Mpris2::SetRate(double rate) {
|
void Mpris2::SetRate(double rate) {
|
||||||
if (rate == 0) {
|
if (rate == 0) {
|
||||||
|
@ -315,24 +307,20 @@ void Mpris2::SetShuffle(bool value) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariantMap Mpris2::Metadata() const {
|
QVariantMap Mpris2::Metadata() const { return last_metadata_; }
|
||||||
return last_metadata_;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Mpris2::current_track_id() const {
|
QString Mpris2::current_track_id() const {
|
||||||
if (!mpris1_->tracklist()) {
|
if (!mpris1_->tracklist()) {
|
||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
|
|
||||||
return QString("/org/mpris/MediaPlayer2/Track/%1").arg(
|
return QString("/org/mpris/MediaPlayer2/Track/%1")
|
||||||
QString::number(mpris1_->tracklist()->GetCurrentTrack()));
|
.arg(QString::number(mpris1_->tracklist()->GetCurrentTrack()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// We send Metadata change notification as soon as the process of
|
// We send Metadata change notification as soon as the process of
|
||||||
// changing song starts...
|
// changing song starts...
|
||||||
void Mpris2::CurrentSongChanged(const Song& song) {
|
void Mpris2::CurrentSongChanged(const Song& song) { ArtLoaded(song, ""); }
|
||||||
ArtLoaded(song, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
// ... and we add the cover information later, when it's available.
|
// ... and we add the cover information later, when it's available.
|
||||||
void Mpris2::ArtLoaded(const Song& song, const QString& art_uri) {
|
void Mpris2::ArtLoaded(const Song& song, const QString& art_uri) {
|
||||||
|
@ -363,21 +351,15 @@ double Mpris2::Volume() const {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Mpris2::SetVolume(double value) {
|
void Mpris2::SetVolume(double value) { app_->player()->SetVolume(value * 100); }
|
||||||
app_->player()->SetVolume(value * 100);
|
|
||||||
}
|
|
||||||
|
|
||||||
qlonglong Mpris2::Position() const {
|
qlonglong Mpris2::Position() const {
|
||||||
return app_->player()->engine()->position_nanosec() / kNsecPerUsec;
|
return app_->player()->engine()->position_nanosec() / kNsecPerUsec;
|
||||||
}
|
}
|
||||||
|
|
||||||
double Mpris2::MaximumRate() const {
|
double Mpris2::MaximumRate() const { return 1.0; }
|
||||||
return 1.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
double Mpris2::MinimumRate() const {
|
double Mpris2::MinimumRate() const { return 1.0; }
|
||||||
return 1.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Mpris2::CanGoNext() const {
|
bool Mpris2::CanGoNext() const {
|
||||||
if (mpris1_->player()) {
|
if (mpris1_->player()) {
|
||||||
|
@ -395,17 +377,14 @@ bool Mpris2::CanGoPrevious() const {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Mpris2::CanPlay() const {
|
bool Mpris2::CanPlay() const { return mpris1_->player()->GetCaps() & CAN_PLAY; }
|
||||||
return mpris1_->player()->GetCaps() & CAN_PLAY;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This one's a bit different than MPRIS 1 - we want this to be true even when
|
// This one's a bit different than MPRIS 1 - we want this to be true even when
|
||||||
// the song is already paused or stopped.
|
// the song is already paused or stopped.
|
||||||
bool Mpris2::CanPause() const {
|
bool Mpris2::CanPause() const {
|
||||||
if (mpris1_->player()) {
|
if (mpris1_->player()) {
|
||||||
return mpris1_->player()->GetCaps() & CAN_PAUSE
|
return mpris1_->player()->GetCaps() & CAN_PAUSE ||
|
||||||
|| PlaybackStatus() == "Paused"
|
PlaybackStatus() == "Paused" || PlaybackStatus() == "Stopped";
|
||||||
|| PlaybackStatus() == "Stopped";
|
|
||||||
} else {
|
} else {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -419,9 +398,7 @@ bool Mpris2::CanSeek() const {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Mpris2::CanControl() const {
|
bool Mpris2::CanControl() const { return true; }
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Mpris2::Next() {
|
void Mpris2::Next() {
|
||||||
if (CanGoNext()) {
|
if (CanGoNext()) {
|
||||||
|
@ -447,9 +424,7 @@ void Mpris2::PlayPause() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Mpris2::Stop() {
|
void Mpris2::Stop() { app_->player()->Stop(); }
|
||||||
app_->player()->Stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Mpris2::Play() {
|
void Mpris2::Play() {
|
||||||
if (CanPlay()) {
|
if (CanPlay()) {
|
||||||
|
@ -459,7 +434,8 @@ void Mpris2::Play() {
|
||||||
|
|
||||||
void Mpris2::Seek(qlonglong offset) {
|
void Mpris2::Seek(qlonglong offset) {
|
||||||
if (CanSeek()) {
|
if (CanSeek()) {
|
||||||
app_->player()->SeekTo(app_->player()->engine()->position_nanosec() / kNsecPerSec +
|
app_->player()->SeekTo(app_->player()->engine()->position_nanosec() /
|
||||||
|
kNsecPerSec +
|
||||||
offset / kUsecPerSec);
|
offset / kUsecPerSec);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -468,7 +444,8 @@ void Mpris2::SetPosition(const QDBusObjectPath& trackId, qlonglong offset) {
|
||||||
if (CanSeek() && trackId.path() == current_track_id() && offset >= 0) {
|
if (CanSeek() && trackId.path() == current_track_id() && offset >= 0) {
|
||||||
offset *= kNsecPerUsec;
|
offset *= kNsecPerUsec;
|
||||||
|
|
||||||
if(offset < app_->player()->GetCurrentItem()->Metadata().length_nanosec()) {
|
if (offset <
|
||||||
|
app_->player()->GetCurrentItem()->Metadata().length_nanosec()) {
|
||||||
app_->player()->SeekTo(offset / kNsecPerSec);
|
app_->player()->SeekTo(offset / kNsecPerSec);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -485,16 +462,15 @@ TrackIds Mpris2::Tracks() const {
|
||||||
return TrackIds();
|
return TrackIds();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Mpris2::CanEditTracks() const {
|
bool Mpris2::CanEditTracks() const { return false; }
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
TrackMetadata Mpris2::GetTracksMetadata(const TrackIds& tracks) const {
|
TrackMetadata Mpris2::GetTracksMetadata(const TrackIds& tracks) const {
|
||||||
// TODO
|
// TODO
|
||||||
return TrackMetadata();
|
return TrackMetadata();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Mpris2::AddTrack(const QString &uri, const QDBusObjectPath &afterTrack, bool setAsCurrent) {
|
void Mpris2::AddTrack(const QString& uri, const QDBusObjectPath& afterTrack,
|
||||||
|
bool setAsCurrent) {
|
||||||
// TODO
|
// TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -510,17 +486,14 @@ quint32 Mpris2::PlaylistCount() const {
|
||||||
return app_->playlist_manager()->GetAllPlaylists().size();
|
return app_->playlist_manager()->GetAllPlaylists().size();
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList Mpris2::Orderings() const {
|
QStringList Mpris2::Orderings() const { return QStringList() << "User"; }
|
||||||
return QStringList() << "User";
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
QDBusObjectPath MakePlaylistPath(int id) {
|
QDBusObjectPath MakePlaylistPath(int id) {
|
||||||
return QDBusObjectPath(QString(
|
return QDBusObjectPath(
|
||||||
"/org/mpris/MediaPlayer2/Playlists/%1").arg(id));
|
QString("/org/mpris/MediaPlayer2/Playlists/%1").arg(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MaybePlaylist Mpris2::ActivePlaylist() const {
|
MaybePlaylist Mpris2::ActivePlaylist() const {
|
||||||
|
@ -557,8 +530,9 @@ void Mpris2::ActivatePlaylist(const QDBusObjectPath& playlist_id) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Support sort orders.
|
// TODO: Support sort orders.
|
||||||
MprisPlaylistList Mpris2::GetPlaylists(
|
MprisPlaylistList Mpris2::GetPlaylists(quint32 index, quint32 max_count,
|
||||||
quint32 index, quint32 max_count, const QString& order, bool reverse_order) {
|
const QString& order,
|
||||||
|
bool reverse_order) {
|
||||||
MprisPlaylistList ret;
|
MprisPlaylistList ret;
|
||||||
foreach(Playlist * p, app_->playlist_manager()->GetAllPlaylists()) {
|
foreach(Playlist * p, app_->playlist_manager()->GetAllPlaylists()) {
|
||||||
MprisPlaylist mpris_playlist;
|
MprisPlaylist mpris_playlist;
|
||||||
|
@ -577,7 +551,8 @@ MprisPlaylistList Mpris2::GetPlaylists(
|
||||||
void Mpris2::PlaylistChanged(Playlist* playlist) {
|
void Mpris2::PlaylistChanged(Playlist* playlist) {
|
||||||
MprisPlaylist mpris_playlist;
|
MprisPlaylist mpris_playlist;
|
||||||
mpris_playlist.id = MakePlaylistPath(playlist->id());
|
mpris_playlist.id = MakePlaylistPath(playlist->id());
|
||||||
mpris_playlist.name = app_->playlist_manager()->GetPlaylistName(playlist->id());
|
mpris_playlist.name =
|
||||||
|
app_->playlist_manager()->GetPlaylistName(playlist->id());
|
||||||
emit PlaylistChanged(mpris_playlist);
|
emit PlaylistChanged(mpris_playlist);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -48,13 +48,12 @@ struct MaybePlaylist {
|
||||||
Q_DECLARE_METATYPE(MaybePlaylist);
|
Q_DECLARE_METATYPE(MaybePlaylist);
|
||||||
|
|
||||||
QDBusArgument& operator<<(QDBusArgument& arg, const MprisPlaylist& playlist);
|
QDBusArgument& operator<<(QDBusArgument& arg, const MprisPlaylist& playlist);
|
||||||
const QDBusArgument& operator>> (
|
const QDBusArgument& operator>>(const QDBusArgument& arg,
|
||||||
const QDBusArgument& arg, MprisPlaylist& playlist);
|
MprisPlaylist& playlist);
|
||||||
|
|
||||||
QDBusArgument& operator<<(QDBusArgument& arg, const MaybePlaylist& playlist);
|
QDBusArgument& operator<<(QDBusArgument& arg, const MaybePlaylist& playlist);
|
||||||
const QDBusArgument& operator>> (
|
const QDBusArgument& operator>>(const QDBusArgument& arg,
|
||||||
const QDBusArgument& arg, MaybePlaylist& playlist);
|
MaybePlaylist& playlist);
|
||||||
|
|
||||||
|
|
||||||
namespace mpris {
|
namespace mpris {
|
||||||
|
|
||||||
|
@ -161,7 +160,8 @@ class Mpris2 : public QObject {
|
||||||
|
|
||||||
// Methods
|
// Methods
|
||||||
TrackMetadata GetTracksMetadata(const TrackIds& tracks) const;
|
TrackMetadata GetTracksMetadata(const TrackIds& tracks) const;
|
||||||
void AddTrack(const QString& uri, const QDBusObjectPath& afterTrack, bool setAsCurrent);
|
void AddTrack(const QString& uri, const QDBusObjectPath& afterTrack,
|
||||||
|
bool setAsCurrent);
|
||||||
void RemoveTrack(const QDBusObjectPath& trackId);
|
void RemoveTrack(const QDBusObjectPath& trackId);
|
||||||
void GoTo(const QDBusObjectPath& trackId);
|
void GoTo(const QDBusObjectPath& trackId);
|
||||||
|
|
||||||
|
@ -172,8 +172,8 @@ class Mpris2 : public QObject {
|
||||||
|
|
||||||
// Methods
|
// Methods
|
||||||
void ActivatePlaylist(const QDBusObjectPath& playlist_id);
|
void ActivatePlaylist(const QDBusObjectPath& playlist_id);
|
||||||
QList<MprisPlaylist> GetPlaylists(
|
QList<MprisPlaylist> GetPlaylists(quint32 index, quint32 max_count,
|
||||||
quint32 index, quint32 max_count, const QString& order, bool reverse_order);
|
const QString& order, bool reverse_order);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
// Player
|
// Player
|
||||||
|
@ -183,7 +183,8 @@ signals:
|
||||||
void TrackListReplaced(const TrackIds& Tracks, QDBusObjectPath CurrentTrack);
|
void TrackListReplaced(const TrackIds& Tracks, QDBusObjectPath CurrentTrack);
|
||||||
void TrackAdded(const TrackMetadata& Metadata, QDBusObjectPath AfterTrack);
|
void TrackAdded(const TrackMetadata& Metadata, QDBusObjectPath AfterTrack);
|
||||||
void TrackRemoved(const QDBusObjectPath& trackId);
|
void TrackRemoved(const QDBusObjectPath& trackId);
|
||||||
void TrackMetadataChanged(const QDBusObjectPath& trackId, const TrackMetadata& metadata);
|
void TrackMetadataChanged(const QDBusObjectPath& trackId,
|
||||||
|
const TrackMetadata& metadata);
|
||||||
|
|
||||||
void RaiseMainWindow();
|
void RaiseMainWindow();
|
||||||
|
|
||||||
|
@ -205,7 +206,8 @@ private slots:
|
||||||
private:
|
private:
|
||||||
void EmitNotification(const QString& name);
|
void EmitNotification(const QString& name);
|
||||||
void EmitNotification(const QString& name, const QVariant& val);
|
void EmitNotification(const QString& name, const QVariant& val);
|
||||||
void EmitNotification(const QString& name, const QVariant& val, const QString& mprisEntity);
|
void EmitNotification(const QString& name, const QVariant& val,
|
||||||
|
const QString& mprisEntity);
|
||||||
|
|
||||||
QString PlaybackStatus(Engine::State state) const;
|
QString PlaybackStatus(Engine::State state) const;
|
||||||
|
|
||||||
|
|
|
@ -25,11 +25,13 @@
|
||||||
|
|
||||||
namespace mpris {
|
namespace mpris {
|
||||||
|
|
||||||
inline void AddMetadata(const QString& key, const QString& metadata, QVariantMap* map) {
|
inline void AddMetadata(const QString& key, const QString& metadata,
|
||||||
|
QVariantMap* map) {
|
||||||
if (!metadata.isEmpty()) (*map)[key] = metadata;
|
if (!metadata.isEmpty()) (*map)[key] = metadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void AddMetadataAsList(const QString& key, const QString& metadata, QVariantMap* map) {
|
inline void AddMetadataAsList(const QString& key, const QString& metadata,
|
||||||
|
QVariantMap* map) {
|
||||||
if (!metadata.isEmpty()) (*map)[key] = QStringList() << metadata;
|
if (!metadata.isEmpty()) (*map)[key] = QStringList() << metadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,7 +47,8 @@ inline void AddMetadata(const QString& key, double metadata, QVariantMap* map) {
|
||||||
if (metadata != 0.0) (*map)[key] = metadata;
|
if (metadata != 0.0) (*map)[key] = metadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void AddMetadata(const QString& key, const QDateTime& metadata, QVariantMap* map) {
|
inline void AddMetadata(const QString& key, const QDateTime& metadata,
|
||||||
|
QVariantMap* map) {
|
||||||
if (metadata.isValid()) (*map)[key] = metadata;
|
if (metadata.isValid()) (*map)[key] = metadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,9 +6,7 @@
|
||||||
#include <QTime>
|
#include <QTime>
|
||||||
|
|
||||||
MultiSortFilterProxy::MultiSortFilterProxy(QObject* parent)
|
MultiSortFilterProxy::MultiSortFilterProxy(QObject* parent)
|
||||||
: QSortFilterProxyModel(parent)
|
: QSortFilterProxyModel(parent) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void MultiSortFilterProxy::AddSortSpec(int role, Qt::SortOrder order) {
|
void MultiSortFilterProxy::AddSortSpec(int role, Qt::SortOrder order) {
|
||||||
sorting_ << SortSpec(role, order);
|
sorting_ << SortSpec(role, order);
|
||||||
|
@ -31,29 +29,38 @@ bool MultiSortFilterProxy::lessThan(const QModelIndex& left,
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
static inline int DoCompare(T left, T right) {
|
static inline int DoCompare(T left, T right) {
|
||||||
if (left < right)
|
if (left < right) return -1;
|
||||||
return -1;
|
if (left > right) return 1;
|
||||||
if (left > right)
|
|
||||||
return 1;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int MultiSortFilterProxy::Compare(const QVariant& left, const QVariant& right) const {
|
int MultiSortFilterProxy::Compare(const QVariant& left,
|
||||||
|
const QVariant& right) const {
|
||||||
// Copied from the QSortFilterProxyModel::lessThan implementation, but returns
|
// Copied from the QSortFilterProxyModel::lessThan implementation, but returns
|
||||||
// -1, 0 or 1 instead of true or false.
|
// -1, 0 or 1 instead of true or false.
|
||||||
switch (left.userType()) {
|
switch (left.userType()) {
|
||||||
case QVariant::Invalid:
|
case QVariant::Invalid:
|
||||||
return (right.type() != QVariant::Invalid) ? -1 : 0;
|
return (right.type() != QVariant::Invalid) ? -1 : 0;
|
||||||
case QVariant::Int: return DoCompare(left.toInt(), right.toInt());
|
case QVariant::Int:
|
||||||
case QVariant::UInt: return DoCompare(left.toUInt(), right.toUInt());
|
return DoCompare(left.toInt(), right.toInt());
|
||||||
case QVariant::LongLong: return DoCompare(left.toLongLong(), right.toLongLong());
|
case QVariant::UInt:
|
||||||
case QVariant::ULongLong: return DoCompare(left.toULongLong(), right.toULongLong());
|
return DoCompare(left.toUInt(), right.toUInt());
|
||||||
case QMetaType::Float: return DoCompare(left.toFloat(), right.toFloat());
|
case QVariant::LongLong:
|
||||||
case QVariant::Double: return DoCompare(left.toDouble(), right.toDouble());
|
return DoCompare(left.toLongLong(), right.toLongLong());
|
||||||
case QVariant::Char: return DoCompare(left.toChar(), right.toChar());
|
case QVariant::ULongLong:
|
||||||
case QVariant::Date: return DoCompare(left.toDate(), right.toDate());
|
return DoCompare(left.toULongLong(), right.toULongLong());
|
||||||
case QVariant::Time: return DoCompare(left.toTime(), right.toTime());
|
case QMetaType::Float:
|
||||||
case QVariant::DateTime: return DoCompare(left.toDateTime(), right.toDateTime());
|
return DoCompare(left.toFloat(), right.toFloat());
|
||||||
|
case QVariant::Double:
|
||||||
|
return DoCompare(left.toDouble(), right.toDouble());
|
||||||
|
case QVariant::Char:
|
||||||
|
return DoCompare(left.toChar(), right.toChar());
|
||||||
|
case QVariant::Date:
|
||||||
|
return DoCompare(left.toDate(), right.toDate());
|
||||||
|
case QVariant::Time:
|
||||||
|
return DoCompare(left.toTime(), right.toTime());
|
||||||
|
case QVariant::DateTime:
|
||||||
|
return DoCompare(left.toDateTime(), right.toDateTime());
|
||||||
case QVariant::String:
|
case QVariant::String:
|
||||||
default:
|
default:
|
||||||
if (isSortLocaleAware())
|
if (isSortLocaleAware())
|
||||||
|
|
|
@ -17,6 +17,4 @@
|
||||||
|
|
||||||
#include "musicstorage.h"
|
#include "musicstorage.h"
|
||||||
|
|
||||||
MusicStorage::MusicStorage()
|
MusicStorage::MusicStorage() {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
|
@ -62,10 +62,16 @@ public:
|
||||||
virtual QString LocalPath() const { return QString(); }
|
virtual QString LocalPath() const { return QString(); }
|
||||||
|
|
||||||
virtual TranscodeMode GetTranscodeMode() const { return Transcode_Never; }
|
virtual TranscodeMode GetTranscodeMode() const { return Transcode_Never; }
|
||||||
virtual Song::FileType GetTranscodeFormat() const { return Song::Type_Unknown; }
|
virtual Song::FileType GetTranscodeFormat() const {
|
||||||
virtual bool GetSupportedFiletypes(QList<Song::FileType>* ret) { return true; }
|
return Song::Type_Unknown;
|
||||||
|
}
|
||||||
|
virtual bool GetSupportedFiletypes(QList<Song::FileType>* ret) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
virtual bool StartCopy(QList<Song::FileType>* supported_types) { return true;}
|
virtual bool StartCopy(QList<Song::FileType>* supported_types) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
virtual bool CopyToStorage(const CopyJob& job) = 0;
|
virtual bool CopyToStorage(const CopyJob& job) = 0;
|
||||||
virtual void FinishCopy(bool success) {}
|
virtual void FinishCopy(bool success) {}
|
||||||
|
|
||||||
|
|
|
@ -29,12 +29,12 @@
|
||||||
QMutex ThreadSafeNetworkDiskCache::sMutex;
|
QMutex ThreadSafeNetworkDiskCache::sMutex;
|
||||||
QNetworkDiskCache* ThreadSafeNetworkDiskCache::sCache = nullptr;
|
QNetworkDiskCache* ThreadSafeNetworkDiskCache::sCache = nullptr;
|
||||||
|
|
||||||
|
|
||||||
ThreadSafeNetworkDiskCache::ThreadSafeNetworkDiskCache(QObject* parent) {
|
ThreadSafeNetworkDiskCache::ThreadSafeNetworkDiskCache(QObject* parent) {
|
||||||
QMutexLocker l(&sMutex);
|
QMutexLocker l(&sMutex);
|
||||||
if (!sCache) {
|
if (!sCache) {
|
||||||
sCache = new QNetworkDiskCache;
|
sCache = new QNetworkDiskCache;
|
||||||
sCache->setCacheDirectory(Utilities::GetConfigPath(Utilities::Path_NetworkCache));
|
sCache->setCacheDirectory(
|
||||||
|
Utilities::GetConfigPath(Utilities::Path_NetworkCache));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,7 +58,8 @@ QNetworkCacheMetaData ThreadSafeNetworkDiskCache::metaData(const QUrl& url) {
|
||||||
return sCache->metaData(url);
|
return sCache->metaData(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
QIODevice* ThreadSafeNetworkDiskCache::prepare(const QNetworkCacheMetaData& metaData) {
|
QIODevice* ThreadSafeNetworkDiskCache::prepare(
|
||||||
|
const QNetworkCacheMetaData& metaData) {
|
||||||
QMutexLocker l(&sMutex);
|
QMutexLocker l(&sMutex);
|
||||||
return sCache->prepare(metaData);
|
return sCache->prepare(metaData);
|
||||||
}
|
}
|
||||||
|
@ -68,7 +69,8 @@ bool ThreadSafeNetworkDiskCache::remove(const QUrl& url) {
|
||||||
return sCache->remove(url);
|
return sCache->remove(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ThreadSafeNetworkDiskCache::updateMetaData(const QNetworkCacheMetaData& metaData) {
|
void ThreadSafeNetworkDiskCache::updateMetaData(
|
||||||
|
const QNetworkCacheMetaData& metaData) {
|
||||||
QMutexLocker l(&sMutex);
|
QMutexLocker l(&sMutex);
|
||||||
sCache->updateMetaData(metaData);
|
sCache->updateMetaData(metaData);
|
||||||
}
|
}
|
||||||
|
@ -78,18 +80,17 @@ void ThreadSafeNetworkDiskCache::clear() {
|
||||||
sCache->clear();
|
sCache->clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
NetworkAccessManager::NetworkAccessManager(QObject* parent)
|
NetworkAccessManager::NetworkAccessManager(QObject* parent)
|
||||||
: QNetworkAccessManager(parent)
|
: QNetworkAccessManager(parent) {
|
||||||
{
|
|
||||||
setCache(new ThreadSafeNetworkDiskCache(this));
|
setCache(new ThreadSafeNetworkDiskCache(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
QNetworkReply* NetworkAccessManager::createRequest(
|
QNetworkReply* NetworkAccessManager::createRequest(
|
||||||
Operation op, const QNetworkRequest& request, QIODevice* outgoingData) {
|
Operation op, const QNetworkRequest& request, QIODevice* outgoingData) {
|
||||||
QByteArray user_agent = QString("%1 %2").arg(
|
QByteArray user_agent = QString("%1 %2")
|
||||||
QCoreApplication::applicationName(),
|
.arg(QCoreApplication::applicationName(),
|
||||||
QCoreApplication::applicationVersion()).toUtf8();
|
QCoreApplication::applicationVersion())
|
||||||
|
.toUtf8();
|
||||||
|
|
||||||
if (request.hasRawHeader("User-Agent")) {
|
if (request.hasRawHeader("User-Agent")) {
|
||||||
// Append the existing user-agent set by a client library (such as
|
// Append the existing user-agent set by a client library (such as
|
||||||
|
@ -107,8 +108,8 @@ QNetworkReply* NetworkAccessManager::createRequest(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prefer the cache unless the caller has changed the setting already
|
// Prefer the cache unless the caller has changed the setting already
|
||||||
if (request.attribute(QNetworkRequest::CacheLoadControlAttribute).toInt()
|
if (request.attribute(QNetworkRequest::CacheLoadControlAttribute).toInt() ==
|
||||||
== QNetworkRequest::PreferNetwork) {
|
QNetworkRequest::PreferNetwork) {
|
||||||
new_request.setAttribute(QNetworkRequest::CacheLoadControlAttribute,
|
new_request.setAttribute(QNetworkRequest::CacheLoadControlAttribute,
|
||||||
QNetworkRequest::PreferCache);
|
QNetworkRequest::PreferCache);
|
||||||
}
|
}
|
||||||
|
@ -116,14 +117,11 @@ QNetworkReply* NetworkAccessManager::createRequest(
|
||||||
return QNetworkAccessManager::createRequest(op, new_request, outgoingData);
|
return QNetworkAccessManager::createRequest(op, new_request, outgoingData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
NetworkTimeouts::NetworkTimeouts(int timeout_msec, QObject* parent)
|
NetworkTimeouts::NetworkTimeouts(int timeout_msec, QObject* parent)
|
||||||
: timeout_msec_(timeout_msec) {
|
: timeout_msec_(timeout_msec) {}
|
||||||
}
|
|
||||||
|
|
||||||
void NetworkTimeouts::AddReply(QNetworkReply* reply) {
|
void NetworkTimeouts::AddReply(QNetworkReply* reply) {
|
||||||
if (timers_.contains(reply))
|
if (timers_.contains(reply)) return;
|
||||||
return;
|
|
||||||
|
|
||||||
connect(reply, SIGNAL(destroyed()), SLOT(ReplyFinished()));
|
connect(reply, SIGNAL(destroyed()), SLOT(ReplyFinished()));
|
||||||
connect(reply, SIGNAL(finished()), SLOT(ReplyFinished()));
|
connect(reply, SIGNAL(finished()), SLOT(ReplyFinished()));
|
||||||
|
@ -167,8 +165,8 @@ void NetworkTimeouts::timerEvent(QTimerEvent* e) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RedirectFollower::RedirectFollower(QNetworkReply* first_reply,
|
||||||
RedirectFollower::RedirectFollower(QNetworkReply* first_reply, int max_redirects)
|
int max_redirects)
|
||||||
: QObject(nullptr),
|
: QObject(nullptr),
|
||||||
current_reply_(first_reply),
|
current_reply_(first_reply),
|
||||||
redirects_remaining_(max_redirects) {
|
redirects_remaining_(max_redirects) {
|
||||||
|
@ -177,15 +175,19 @@ RedirectFollower::RedirectFollower(QNetworkReply* first_reply, int max_redirects
|
||||||
|
|
||||||
void RedirectFollower::ConnectReply(QNetworkReply* reply) {
|
void RedirectFollower::ConnectReply(QNetworkReply* reply) {
|
||||||
connect(reply, SIGNAL(readyRead()), SLOT(ReadyRead()));
|
connect(reply, SIGNAL(readyRead()), SLOT(ReadyRead()));
|
||||||
connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), SIGNAL(error(QNetworkReply::NetworkError)));
|
connect(reply, SIGNAL(error(QNetworkReply::NetworkError)),
|
||||||
connect(reply, SIGNAL(downloadProgress(qint64,qint64)), SIGNAL(downloadProgress(qint64,qint64)));
|
SIGNAL(error(QNetworkReply::NetworkError)));
|
||||||
connect(reply, SIGNAL(uploadProgress(qint64,qint64)), SIGNAL(uploadProgress(qint64,qint64)));
|
connect(reply, SIGNAL(downloadProgress(qint64, qint64)),
|
||||||
|
SIGNAL(downloadProgress(qint64, qint64)));
|
||||||
|
connect(reply, SIGNAL(uploadProgress(qint64, qint64)),
|
||||||
|
SIGNAL(uploadProgress(qint64, qint64)));
|
||||||
connect(reply, SIGNAL(finished()), SLOT(ReplyFinished()));
|
connect(reply, SIGNAL(finished()), SLOT(ReplyFinished()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void RedirectFollower::ReadyRead() {
|
void RedirectFollower::ReadyRead() {
|
||||||
// Don't re-emit this signal for redirect replies.
|
// Don't re-emit this signal for redirect replies.
|
||||||
if (current_reply_->attribute(QNetworkRequest::RedirectionTargetAttribute).isValid()) {
|
if (current_reply_->attribute(QNetworkRequest::RedirectionTargetAttribute)
|
||||||
|
.isValid()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -195,14 +197,16 @@ void RedirectFollower::ReadyRead() {
|
||||||
void RedirectFollower::ReplyFinished() {
|
void RedirectFollower::ReplyFinished() {
|
||||||
current_reply_->deleteLater();
|
current_reply_->deleteLater();
|
||||||
|
|
||||||
if (current_reply_->attribute(QNetworkRequest::RedirectionTargetAttribute).isValid()) {
|
if (current_reply_->attribute(QNetworkRequest::RedirectionTargetAttribute)
|
||||||
|
.isValid()) {
|
||||||
if (redirects_remaining_-- == 0) {
|
if (redirects_remaining_-- == 0) {
|
||||||
emit finished();
|
emit finished();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const QUrl next_url = current_reply_->url().resolved(
|
const QUrl next_url = current_reply_->url().resolved(
|
||||||
current_reply_->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl());
|
current_reply_->attribute(QNetworkRequest::RedirectionTargetAttribute)
|
||||||
|
.toUrl());
|
||||||
|
|
||||||
QNetworkRequest req(current_reply_->request());
|
QNetworkRequest req(current_reply_->request());
|
||||||
req.setUrl(next_url);
|
req.setUrl(next_url);
|
||||||
|
|
|
@ -44,7 +44,6 @@ private:
|
||||||
static QNetworkDiskCache* sCache;
|
static QNetworkDiskCache* sCache;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class NetworkAccessManager : public QNetworkAccessManager {
|
class NetworkAccessManager : public QNetworkAccessManager {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
|
@ -56,7 +55,6 @@ protected:
|
||||||
QIODevice* outgoingData);
|
QIODevice* outgoingData);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class RedirectFollower : public QObject {
|
class RedirectFollower : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
|
@ -69,8 +67,12 @@ public:
|
||||||
// These are all forwarded to the current reply.
|
// These are all forwarded to the current reply.
|
||||||
QNetworkReply::NetworkError error() const { return current_reply_->error(); }
|
QNetworkReply::NetworkError error() const { return current_reply_->error(); }
|
||||||
QString errorString() const { return current_reply_->errorString(); }
|
QString errorString() const { return current_reply_->errorString(); }
|
||||||
QVariant attribute(QNetworkRequest::Attribute code) const { return current_reply_->attribute(code); }
|
QVariant attribute(QNetworkRequest::Attribute code) const {
|
||||||
QVariant header(QNetworkRequest::KnownHeaders header) const { return current_reply_->header(header); }
|
return current_reply_->attribute(code);
|
||||||
|
}
|
||||||
|
QVariant header(QNetworkRequest::KnownHeaders header) const {
|
||||||
|
return current_reply_->header(header);
|
||||||
|
}
|
||||||
qint64 bytesAvailable() const { return current_reply_->bytesAvailable(); }
|
qint64 bytesAvailable() const { return current_reply_->bytesAvailable(); }
|
||||||
QUrl url() const { return current_reply_->url(); }
|
QUrl url() const { return current_reply_->url(); }
|
||||||
QByteArray readAll() { return current_reply_->readAll(); }
|
QByteArray readAll() { return current_reply_->readAll(); }
|
||||||
|
@ -98,7 +100,6 @@ private:
|
||||||
int redirects_remaining_;
|
int redirects_remaining_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class NetworkTimeouts : public QObject {
|
class NetworkTimeouts : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
|
|
|
@ -15,8 +15,7 @@ NetworkProxyFactory::NetworkProxyFactory()
|
||||||
: mode_(Mode_System),
|
: mode_(Mode_System),
|
||||||
type_(QNetworkProxy::HttpProxy),
|
type_(QNetworkProxy::HttpProxy),
|
||||||
port_(8080),
|
port_(8080),
|
||||||
use_authentication_(false)
|
use_authentication_(false) {
|
||||||
{
|
|
||||||
#ifdef Q_OS_LINUX
|
#ifdef Q_OS_LINUX
|
||||||
// Linux uses environment variables to pass proxy configuration information,
|
// Linux uses environment variables to pass proxy configuration information,
|
||||||
// which systemProxyForQuery doesn't support for some reason.
|
// which systemProxyForQuery doesn't support for some reason.
|
||||||
|
@ -30,8 +29,7 @@ NetworkProxyFactory::NetworkProxyFactory()
|
||||||
qLog(Debug) << "Detected system proxy URLs:" << urls;
|
qLog(Debug) << "Detected system proxy URLs:" << urls;
|
||||||
|
|
||||||
foreach(const QString & url_str, urls) {
|
foreach(const QString & url_str, urls) {
|
||||||
if (url_str.isEmpty())
|
if (url_str.isEmpty()) continue;
|
||||||
continue;
|
|
||||||
|
|
||||||
env_url_ = QUrl(url_str);
|
env_url_ = QUrl(url_str);
|
||||||
break;
|
break;
|
||||||
|
@ -56,7 +54,8 @@ void NetworkProxyFactory::ReloadSettings() {
|
||||||
s.beginGroup(kSettingsGroup);
|
s.beginGroup(kSettingsGroup);
|
||||||
|
|
||||||
mode_ = Mode(s.value("mode", Mode_System).toInt());
|
mode_ = Mode(s.value("mode", Mode_System).toInt());
|
||||||
type_ = QNetworkProxy::ProxyType(s.value("type", QNetworkProxy::HttpProxy).toInt());
|
type_ = QNetworkProxy::ProxyType(
|
||||||
|
s.value("type", QNetworkProxy::HttpProxy).toInt());
|
||||||
hostname_ = s.value("hostname").toString();
|
hostname_ = s.value("hostname").toString();
|
||||||
port_ = s.value("port", 8080).toInt();
|
port_ = s.value("port", 8080).toInt();
|
||||||
use_authentication_ = s.value("use_authentication", false).toBool();
|
use_authentication_ = s.value("use_authentication", false).toBool();
|
||||||
|
|
|
@ -8,11 +8,7 @@
|
||||||
class NetworkProxyFactory : public QNetworkProxyFactory {
|
class NetworkProxyFactory : public QNetworkProxyFactory {
|
||||||
public:
|
public:
|
||||||
// These values are persisted
|
// These values are persisted
|
||||||
enum Mode {
|
enum Mode { Mode_System = 0, Mode_Direct = 1, Mode_Manual = 2, };
|
||||||
Mode_System = 0,
|
|
||||||
Mode_Direct = 1,
|
|
||||||
Mode_Manual = 2,
|
|
||||||
};
|
|
||||||
|
|
||||||
static NetworkProxyFactory* Instance();
|
static NetworkProxyFactory* Instance();
|
||||||
static const char* kSettingsGroup;
|
static const char* kSettingsGroup;
|
||||||
|
|
|
@ -53,8 +53,7 @@ Organise::Organise(TaskManager* task_manager,
|
||||||
tasks_complete_(0),
|
tasks_complete_(0),
|
||||||
started_(false),
|
started_(false),
|
||||||
task_id_(0),
|
task_id_(0),
|
||||||
current_copy_progress_(0)
|
current_copy_progress_(0) {
|
||||||
{
|
|
||||||
original_thread_ = thread();
|
original_thread_ = thread();
|
||||||
|
|
||||||
for (const NewSongInfo& song_info : songs_info) {
|
for (const NewSongInfo& song_info : songs_info) {
|
||||||
|
@ -63,15 +62,15 @@ Organise::Organise(TaskManager* task_manager,
|
||||||
}
|
}
|
||||||
|
|
||||||
void Organise::Start() {
|
void Organise::Start() {
|
||||||
if (thread_)
|
if (thread_) return;
|
||||||
return;
|
|
||||||
|
|
||||||
task_id_ = task_manager_->StartTask(tr("Organising files"));
|
task_id_ = task_manager_->StartTask(tr("Organising files"));
|
||||||
task_manager_->SetTaskBlocksLibraryScans(true);
|
task_manager_->SetTaskBlocksLibraryScans(true);
|
||||||
|
|
||||||
thread_ = new QThread;
|
thread_ = new QThread;
|
||||||
connect(thread_, SIGNAL(started()), SLOT(ProcessSomeFiles()));
|
connect(thread_, SIGNAL(started()), SLOT(ProcessSomeFiles()));
|
||||||
connect(transcoder_, SIGNAL(JobComplete(QString, bool)), SLOT(FileTranscoded(QString, bool)));
|
connect(transcoder_, SIGNAL(JobComplete(QString, bool)),
|
||||||
|
SLOT(FileTranscoded(QString, bool)));
|
||||||
|
|
||||||
moveToThread(thread_);
|
moveToThread(thread_);
|
||||||
thread_->start();
|
thread_->start();
|
||||||
|
@ -102,8 +101,7 @@ void Organise::ProcessSomeFiles() {
|
||||||
UpdateProgress();
|
UpdateProgress();
|
||||||
|
|
||||||
destination_->FinishCopy(files_with_errors_.isEmpty());
|
destination_->FinishCopy(files_with_errors_.isEmpty());
|
||||||
if (eject_after_)
|
if (eject_after_) destination_->Eject();
|
||||||
destination_->Eject();
|
|
||||||
|
|
||||||
task_manager_->SetTaskFinished(task_id_);
|
task_manager_->SetTaskFinished(task_id_);
|
||||||
|
|
||||||
|
@ -123,16 +121,14 @@ void Organise::ProcessSomeFiles() {
|
||||||
for (int i = 0; i < kBatchSize; ++i) {
|
for (int i = 0; i < kBatchSize; ++i) {
|
||||||
SetSongProgress(0);
|
SetSongProgress(0);
|
||||||
|
|
||||||
if (tasks_pending_.isEmpty())
|
if (tasks_pending_.isEmpty()) break;
|
||||||
break;
|
|
||||||
|
|
||||||
Task task = tasks_pending_.takeFirst();
|
Task task = tasks_pending_.takeFirst();
|
||||||
qLog(Info) << "Processing" << task.song_info_.song_.url().toLocalFile();
|
qLog(Info) << "Processing" << task.song_info_.song_.url().toLocalFile();
|
||||||
|
|
||||||
// Use a Song instead of a tag reader
|
// Use a Song instead of a tag reader
|
||||||
Song song = task.song_info_.song_;
|
Song song = task.song_info_.song_;
|
||||||
if (!song.is_valid())
|
if (!song.is_valid()) continue;
|
||||||
continue;
|
|
||||||
|
|
||||||
// Maybe this file is one that's been transcoded already?
|
// Maybe this file is one that's been transcoded already?
|
||||||
if (!task.transcoded_filename_.isEmpty()) {
|
if (!task.transcoded_filename_.isEmpty()) {
|
||||||
|
@ -142,10 +138,13 @@ void Organise::ProcessSomeFiles() {
|
||||||
song.set_filetype(task.new_filetype_);
|
song.set_filetype(task.new_filetype_);
|
||||||
|
|
||||||
// Fiddle the filename extension as well to match the new type
|
// Fiddle the filename extension as well to match the new type
|
||||||
song.set_url(QUrl::fromLocalFile(Utilities::FiddleFileExtension(song.basefilename(), task.new_extension_)));
|
song.set_url(QUrl::fromLocalFile(Utilities::FiddleFileExtension(
|
||||||
song.set_basefilename(Utilities::FiddleFileExtension(song.basefilename(), task.new_extension_));
|
song.basefilename(), task.new_extension_)));
|
||||||
|
song.set_basefilename(Utilities::FiddleFileExtension(
|
||||||
|
song.basefilename(), task.new_extension_));
|
||||||
|
|
||||||
// Have to set this to the size of the new file or else funny stuff happens
|
// Have to set this to the size of the new file or else funny stuff
|
||||||
|
// happens
|
||||||
song.set_filesize(QFileInfo(task.transcoded_filename_).size());
|
song.set_filesize(QFileInfo(task.transcoded_filename_).size());
|
||||||
} else {
|
} else {
|
||||||
// Figure out if we need to transcode it
|
// Figure out if we need to transcode it
|
||||||
|
@ -167,21 +166,23 @@ void Organise::ProcessSomeFiles() {
|
||||||
// Start the transcoding - this will happen in the background and
|
// Start the transcoding - this will happen in the background and
|
||||||
// FileTranscoded() will get called when it's done. At that point the
|
// FileTranscoded() will get called when it's done. At that point the
|
||||||
// task will get re-added to the pending queue with the new filename.
|
// task will get re-added to the pending queue with the new filename.
|
||||||
transcoder_->AddJob(task.song_info_.song_.url().toLocalFile(), preset, task.transcoded_filename_);
|
transcoder_->AddJob(task.song_info_.song_.url().toLocalFile(), preset,
|
||||||
|
task.transcoded_filename_);
|
||||||
transcoder_->Start();
|
transcoder_->Start();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MusicStorage::CopyJob job;
|
MusicStorage::CopyJob job;
|
||||||
job.source_ = task.transcoded_filename_.isEmpty() ?
|
job.source_ = task.transcoded_filename_.isEmpty()
|
||||||
task.song_info_.song_.url().toLocalFile() : task.transcoded_filename_;
|
? task.song_info_.song_.url().toLocalFile()
|
||||||
|
: task.transcoded_filename_;
|
||||||
job.destination_ = task.song_info_.new_filename_;
|
job.destination_ = task.song_info_.new_filename_;
|
||||||
job.metadata_ = song;
|
job.metadata_ = song;
|
||||||
job.overwrite_ = overwrite_;
|
job.overwrite_ = overwrite_;
|
||||||
job.remove_original_ = !copy_;
|
job.remove_original_ = !copy_;
|
||||||
job.progress_ = std::bind(&Organise::SetSongProgress,
|
job.progress_ = std::bind(&Organise::SetSongProgress, this, _1,
|
||||||
this, _1, !task.transcoded_filename_.isEmpty());
|
!task.transcoded_filename_.isEmpty());
|
||||||
|
|
||||||
if (!destination_->CopyToStorage(job)) {
|
if (!destination_->CopyToStorage(job)) {
|
||||||
files_with_errors_ << task.song_info_.song_.basefilename();
|
files_with_errors_ << task.song_info_.song_.basefilename();
|
||||||
|
@ -199,8 +200,7 @@ void Organise::ProcessSomeFiles() {
|
||||||
}
|
}
|
||||||
|
|
||||||
Song::FileType Organise::CheckTranscode(Song::FileType original_type) const {
|
Song::FileType Organise::CheckTranscode(Song::FileType original_type) const {
|
||||||
if (original_type == Song::Type_Stream)
|
if (original_type == Song::Type_Stream) return Song::Type_Unknown;
|
||||||
return Song::Type_Unknown;
|
|
||||||
|
|
||||||
const MusicStorage::TranscodeMode mode = destination_->GetTranscodeMode();
|
const MusicStorage::TranscodeMode mode = destination_->GetTranscodeMode();
|
||||||
const Song::FileType format = destination_->GetTranscodeFormat();
|
const Song::FileType format = destination_->GetTranscodeFormat();
|
||||||
|
@ -210,16 +210,15 @@ Song::FileType Organise::CheckTranscode(Song::FileType original_type) const {
|
||||||
return Song::Type_Unknown;
|
return Song::Type_Unknown;
|
||||||
|
|
||||||
case MusicStorage::Transcode_Always:
|
case MusicStorage::Transcode_Always:
|
||||||
if (original_type == format)
|
if (original_type == format) return Song::Type_Unknown;
|
||||||
return Song::Type_Unknown;
|
|
||||||
return format;
|
return format;
|
||||||
|
|
||||||
case MusicStorage::Transcode_Unsupported:
|
case MusicStorage::Transcode_Unsupported:
|
||||||
if (supported_filetypes_.isEmpty() || supported_filetypes_.contains(original_type))
|
if (supported_filetypes_.isEmpty() ||
|
||||||
|
supported_filetypes_.contains(original_type))
|
||||||
return Song::Type_Unknown;
|
return Song::Type_Unknown;
|
||||||
|
|
||||||
if (format != Song::Type_Unknown)
|
if (format != Song::Type_Unknown) return format;
|
||||||
return format;
|
|
||||||
|
|
||||||
// The user hasn't visited the device properties page yet to set a
|
// The user hasn't visited the device properties page yet to set a
|
||||||
// preferred format for the device, so we have to pick the best
|
// preferred format for the device, so we have to pick the best
|
||||||
|
@ -242,9 +241,9 @@ void Organise::UpdateProgress() {
|
||||||
// Update transcoding progress
|
// Update transcoding progress
|
||||||
QMap<QString, float> transcode_progress = transcoder_->GetProgress();
|
QMap<QString, float> transcode_progress = transcoder_->GetProgress();
|
||||||
for (const QString& filename : transcode_progress.keys()) {
|
for (const QString& filename : transcode_progress.keys()) {
|
||||||
if (!tasks_transcoding_.contains(filename))
|
if (!tasks_transcoding_.contains(filename)) continue;
|
||||||
continue;
|
tasks_transcoding_[filename].transcode_progress_ =
|
||||||
tasks_transcoding_[filename].transcode_progress_ = transcode_progress[filename];
|
transcode_progress[filename];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Count the progress of all tasks that are in the queue. Files that need
|
// Count the progress of all tasks that are in the queue. Files that need
|
||||||
|
|
|
@ -34,17 +34,16 @@ class Organise : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
struct NewSongInfo {
|
struct NewSongInfo {
|
||||||
NewSongInfo(const Song& song = Song(), const QString& new_filename = QString())
|
NewSongInfo(const Song& song = Song(),
|
||||||
|
const QString& new_filename = QString())
|
||||||
: song_(song), new_filename_(new_filename) {}
|
: song_(song), new_filename_(new_filename) {}
|
||||||
Song song_;
|
Song song_;
|
||||||
QString new_filename_;
|
QString new_filename_;
|
||||||
};
|
};
|
||||||
typedef QList<NewSongInfo> NewSongInfoList;
|
typedef QList<NewSongInfo> NewSongInfoList;
|
||||||
|
|
||||||
Organise(TaskManager* task_manager,
|
Organise(TaskManager* task_manager, std::shared_ptr<MusicStorage> destination,
|
||||||
std::shared_ptr<MusicStorage> destination,
|
|
||||||
const OrganiseFormat& format, bool copy, bool overwrite,
|
const OrganiseFormat& format, bool copy, bool overwrite,
|
||||||
const NewSongInfoList& songs, bool eject_after);
|
const NewSongInfoList& songs, bool eject_after);
|
||||||
|
|
||||||
|
|
|
@ -27,11 +27,24 @@
|
||||||
|
|
||||||
const char* OrganiseFormat::kTagPattern = "\\%([a-zA-Z]*)";
|
const char* OrganiseFormat::kTagPattern = "\\%([a-zA-Z]*)";
|
||||||
const char* OrganiseFormat::kBlockPattern = "\\{([^{}]+)\\}";
|
const char* OrganiseFormat::kBlockPattern = "\\{([^{}]+)\\}";
|
||||||
const QStringList OrganiseFormat::kKnownTags = QStringList()
|
const QStringList OrganiseFormat::kKnownTags = QStringList() << "title"
|
||||||
<< "title" << "album" << "artist" << "artistinitial" << "albumartist"
|
<< "album"
|
||||||
<< "composer" << "track" << "disc" << "bpm" << "year" << "genre"
|
<< "artist"
|
||||||
<< "comment" << "length" << "bitrate" << "samplerate" << "extension"
|
<< "artistinitial"
|
||||||
<< "performer" << "grouping";
|
<< "albumartist"
|
||||||
|
<< "composer"
|
||||||
|
<< "track"
|
||||||
|
<< "disc"
|
||||||
|
<< "bpm"
|
||||||
|
<< "year"
|
||||||
|
<< "genre"
|
||||||
|
<< "comment"
|
||||||
|
<< "length"
|
||||||
|
<< "bitrate"
|
||||||
|
<< "samplerate"
|
||||||
|
<< "extension"
|
||||||
|
<< "performer"
|
||||||
|
<< "grouping";
|
||||||
|
|
||||||
// From http://en.wikipedia.org/wiki/8.3_filename#Directory_table
|
// From http://en.wikipedia.org/wiki/8.3_filename#Directory_table
|
||||||
const char* OrganiseFormat::kInvalidFatCharacters = "\"*/\\:<>?|";
|
const char* OrganiseFormat::kInvalidFatCharacters = "\"*/\\:<>?|";
|
||||||
|
@ -56,8 +69,7 @@ OrganiseFormat::OrganiseFormat(const QString &format)
|
||||||
: format_(format),
|
: format_(format),
|
||||||
replace_non_ascii_(false),
|
replace_non_ascii_(false),
|
||||||
replace_spaces_(false),
|
replace_spaces_(false),
|
||||||
replace_the_(false) {
|
replace_the_(false) {}
|
||||||
}
|
|
||||||
|
|
||||||
void OrganiseFormat::set_format(const QString& v) {
|
void OrganiseFormat::set_format(const QString& v) {
|
||||||
format_ = v;
|
format_ = v;
|
||||||
|
@ -78,13 +90,14 @@ QString OrganiseFormat::GetFilenameForSong(const Song &song) const {
|
||||||
if (QFileInfo(filename).completeBaseName().isEmpty()) {
|
if (QFileInfo(filename).completeBaseName().isEmpty()) {
|
||||||
// Avoid having empty filenames, or filenames with extension only: in this
|
// Avoid having empty filenames, or filenames with extension only: in this
|
||||||
// case, keep the original filename.
|
// case, keep the original filename.
|
||||||
// We remove the extension from "filename" if it exists, as song.basefilename()
|
// We remove the extension from "filename" if it exists, as
|
||||||
|
// song.basefilename()
|
||||||
// also contains the extension.
|
// also contains the extension.
|
||||||
filename = Utilities::PathWithoutFilenameExtension(filename) + song.basefilename();
|
filename =
|
||||||
|
Utilities::PathWithoutFilenameExtension(filename) + song.basefilename();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (replace_spaces_)
|
if (replace_spaces_) filename.replace(QRegExp("\\s"), "_");
|
||||||
filename.replace(QRegExp("\\s"), "_");
|
|
||||||
|
|
||||||
if (replace_non_ascii_) {
|
if (replace_non_ascii_) {
|
||||||
QString stripped;
|
QString stripped;
|
||||||
|
@ -117,8 +130,7 @@ QString OrganiseFormat::ParseBlock(QString block, const Song& song,
|
||||||
// Recursively parse the block
|
// Recursively parse the block
|
||||||
bool empty = false;
|
bool empty = false;
|
||||||
QString value = ParseBlock(block_regexp.cap(1), song, &empty);
|
QString value = ParseBlock(block_regexp.cap(1), song, &empty);
|
||||||
if (empty)
|
if (empty) value = "";
|
||||||
value = "";
|
|
||||||
|
|
||||||
// Replace the block's value
|
// Replace the block's value
|
||||||
block.replace(pos, block_regexp.matchedLength(), value);
|
block.replace(pos, block_regexp.matchedLength(), value);
|
||||||
|
@ -130,58 +142,68 @@ QString OrganiseFormat::ParseBlock(QString block, const Song& song,
|
||||||
pos = 0;
|
pos = 0;
|
||||||
while ((pos = tag_regexp.indexIn(block, pos)) != -1) {
|
while ((pos = tag_regexp.indexIn(block, pos)) != -1) {
|
||||||
QString value = TagValue(tag_regexp.cap(1), song);
|
QString value = TagValue(tag_regexp.cap(1), song);
|
||||||
if (value.isEmpty())
|
if (value.isEmpty()) empty = true;
|
||||||
empty = true;
|
|
||||||
|
|
||||||
block.replace(pos, tag_regexp.matchedLength(), value);
|
block.replace(pos, tag_regexp.matchedLength(), value);
|
||||||
pos += value.length();
|
pos += value.length();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (any_empty)
|
if (any_empty) *any_empty = empty;
|
||||||
*any_empty = empty;
|
|
||||||
return block;
|
return block;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString OrganiseFormat::TagValue(const QString& tag, const Song& song) const {
|
QString OrganiseFormat::TagValue(const QString& tag, const Song& song) const {
|
||||||
QString value;
|
QString value;
|
||||||
|
|
||||||
if (tag == "title") value = song.title();
|
if (tag == "title")
|
||||||
else if (tag == "album") value = song.album();
|
value = song.title();
|
||||||
else if (tag == "artist") value = song.artist();
|
else if (tag == "album")
|
||||||
else if (tag == "composer") value = song.composer();
|
value = song.album();
|
||||||
else if (tag == "performer") value = song.performer();
|
else if (tag == "artist")
|
||||||
else if (tag == "grouping") value = song.grouping();
|
value = song.artist();
|
||||||
else if (tag == "genre") value = song.genre();
|
else if (tag == "composer")
|
||||||
else if (tag == "comment") value = song.comment();
|
value = song.composer();
|
||||||
else if (tag == "year") value = QString::number(song.year());
|
else if (tag == "performer")
|
||||||
else if (tag == "track") value = QString::number(song.track());
|
value = song.performer();
|
||||||
else if (tag == "disc") value = QString::number(song.disc());
|
else if (tag == "grouping")
|
||||||
else if (tag == "bpm") value = QString::number(song.bpm());
|
value = song.grouping();
|
||||||
else if (tag == "length") value =
|
else if (tag == "genre")
|
||||||
QString::number(song.length_nanosec() / kNsecPerSec);
|
value = song.genre();
|
||||||
else if (tag == "bitrate") value = QString::number(song.bitrate());
|
else if (tag == "comment")
|
||||||
else if (tag == "samplerate") value = QString::number(song.samplerate());
|
value = song.comment();
|
||||||
else if (tag == "extension") value = QFileInfo(song.url().toLocalFile()).suffix();
|
else if (tag == "year")
|
||||||
|
value = QString::number(song.year());
|
||||||
|
else if (tag == "track")
|
||||||
|
value = QString::number(song.track());
|
||||||
|
else if (tag == "disc")
|
||||||
|
value = QString::number(song.disc());
|
||||||
|
else if (tag == "bpm")
|
||||||
|
value = QString::number(song.bpm());
|
||||||
|
else if (tag == "length")
|
||||||
|
value = QString::number(song.length_nanosec() / kNsecPerSec);
|
||||||
|
else if (tag == "bitrate")
|
||||||
|
value = QString::number(song.bitrate());
|
||||||
|
else if (tag == "samplerate")
|
||||||
|
value = QString::number(song.samplerate());
|
||||||
|
else if (tag == "extension")
|
||||||
|
value = QFileInfo(song.url().toLocalFile()).suffix();
|
||||||
else if (tag == "artistinitial") {
|
else if (tag == "artistinitial") {
|
||||||
value = song.effective_albumartist().trimmed();
|
value = song.effective_albumartist().trimmed();
|
||||||
if (replace_the_ && !value.isEmpty())
|
if (replace_the_ && !value.isEmpty())
|
||||||
value.replace(QRegExp("^the\\s+", Qt::CaseInsensitive), "");
|
value.replace(QRegExp("^the\\s+", Qt::CaseInsensitive), "");
|
||||||
if (!value.isEmpty()) value = value[0].toUpper();
|
if (!value.isEmpty()) value = value[0].toUpper();
|
||||||
} else if (tag == "albumartist") {
|
} else if (tag == "albumartist") {
|
||||||
value = song.is_compilation()
|
value = song.is_compilation() ? "Various Artists"
|
||||||
? "Various Artists"
|
|
||||||
: song.effective_albumartist();
|
: song.effective_albumartist();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (replace_the_ && (tag == "artist" || tag == "albumartist"))
|
if (replace_the_ && (tag == "artist" || tag == "albumartist"))
|
||||||
value.replace(QRegExp("^the\\s+", Qt::CaseInsensitive), "");
|
value.replace(QRegExp("^the\\s+", Qt::CaseInsensitive), "");
|
||||||
|
|
||||||
if (value == "0" || value == "-1")
|
if (value == "0" || value == "-1") value = "";
|
||||||
value = "";
|
|
||||||
|
|
||||||
// Prepend a 0 to single-digit track numbers
|
// Prepend a 0 to single-digit track numbers
|
||||||
if (tag == "track" && value.length() == 1)
|
if (tag == "track" && value.length() == 1) value.prepend('0');
|
||||||
value.prepend('0');
|
|
||||||
|
|
||||||
// Replace characters that really shouldn't be in paths
|
// Replace characters that really shouldn't be in paths
|
||||||
for (int i = 0; i < kInvalidFatCharactersCount; ++i) {
|
for (int i = 0; i < kInvalidFatCharactersCount; ++i) {
|
||||||
|
@ -191,12 +213,10 @@ QString OrganiseFormat::TagValue(const QString &tag, const Song &song) const {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OrganiseFormat::Validator::Validator(QObject* parent) : QValidator(parent) {}
|
||||||
|
|
||||||
OrganiseFormat::Validator::Validator(QObject *parent)
|
QValidator::State OrganiseFormat::Validator::validate(QString& input,
|
||||||
: QValidator(parent) {}
|
int&) const {
|
||||||
|
|
||||||
QValidator::State OrganiseFormat::Validator::validate(
|
|
||||||
QString& input, int&) const {
|
|
||||||
QRegExp tag_regexp(kTagPattern);
|
QRegExp tag_regexp(kTagPattern);
|
||||||
|
|
||||||
// Make sure all the blocks match up
|
// Make sure all the blocks match up
|
||||||
|
@ -207,12 +227,10 @@ QValidator::State OrganiseFormat::Validator::validate(
|
||||||
else if (input[i] == '}')
|
else if (input[i] == '}')
|
||||||
block_level--;
|
block_level--;
|
||||||
|
|
||||||
if (block_level < 0 || block_level > 1)
|
if (block_level < 0 || block_level > 1) return QValidator::Invalid;
|
||||||
return QValidator::Invalid;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (block_level != 0)
|
if (block_level != 0) return QValidator::Invalid;
|
||||||
return QValidator::Invalid;
|
|
||||||
|
|
||||||
// Make sure the tags are valid
|
// Make sure the tags are valid
|
||||||
int pos = 0;
|
int pos = 0;
|
||||||
|
@ -226,7 +244,6 @@ QValidator::State OrganiseFormat::Validator::validate(
|
||||||
return QValidator::Acceptable;
|
return QValidator::Acceptable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
OrganiseFormat::SyntaxHighlighter::SyntaxHighlighter(QObject* parent)
|
OrganiseFormat::SyntaxHighlighter::SyntaxHighlighter(QObject* parent)
|
||||||
: QSyntaxHighlighter(parent) {}
|
: QSyntaxHighlighter(parent) {}
|
||||||
|
|
||||||
|
@ -274,4 +291,3 @@ void OrganiseFormat::SyntaxHighlighter::highlightBlock(const QString& text) {
|
||||||
pos += tag_regexp.matchedLength();
|
pos += tag_regexp.matchedLength();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -46,7 +46,6 @@ class OrganiseFormat {
|
||||||
bool IsValid() const;
|
bool IsValid() const;
|
||||||
QString GetFilenameForSong(const Song& song) const;
|
QString GetFilenameForSong(const Song& song) const;
|
||||||
|
|
||||||
|
|
||||||
class Validator : public QValidator {
|
class Validator : public QValidator {
|
||||||
public:
|
public:
|
||||||
explicit Validator(QObject* parent = 0);
|
explicit Validator(QObject* parent = 0);
|
||||||
|
@ -69,8 +68,8 @@ class OrganiseFormat {
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString ParseBlock(
|
QString ParseBlock(QString block, const Song& song,
|
||||||
QString block, const Song& song, bool* any_empty = NULL) const;
|
bool* any_empty = NULL) const;
|
||||||
QString TagValue(const QString& tag, const Song& song) const;
|
QString TagValue(const QString& tag, const Song& song) const;
|
||||||
|
|
||||||
QString format_;
|
QString format_;
|
||||||
|
|
|
@ -48,26 +48,26 @@ Player::Player(Application* app, QObject* parent)
|
||||||
stream_change_type_(Engine::First),
|
stream_change_type_(Engine::First),
|
||||||
last_state_(Engine::Empty),
|
last_state_(Engine::Empty),
|
||||||
nb_errors_received_(0),
|
nb_errors_received_(0),
|
||||||
volume_before_mute_(50)
|
volume_before_mute_(50) {
|
||||||
{
|
|
||||||
settings_.beginGroup("Player");
|
settings_.beginGroup("Player");
|
||||||
|
|
||||||
SetVolume(settings_.value("volume", 50).toInt());
|
SetVolume(settings_.value("volume", 50).toInt());
|
||||||
|
|
||||||
connect(engine_.get(), SIGNAL(Error(QString)), SIGNAL(Error(QString)));
|
connect(engine_.get(), SIGNAL(Error(QString)), SIGNAL(Error(QString)));
|
||||||
|
|
||||||
connect(engine_.get(), SIGNAL(ValidSongRequested(QUrl)), SLOT(ValidSongRequested(QUrl)));
|
connect(engine_.get(), SIGNAL(ValidSongRequested(QUrl)),
|
||||||
connect(engine_.get(), SIGNAL(InvalidSongRequested(QUrl)), SLOT(InvalidSongRequested(QUrl)));
|
SLOT(ValidSongRequested(QUrl)));
|
||||||
|
connect(engine_.get(), SIGNAL(InvalidSongRequested(QUrl)),
|
||||||
|
SLOT(InvalidSongRequested(QUrl)));
|
||||||
}
|
}
|
||||||
|
|
||||||
Player::~Player() {
|
Player::~Player() {}
|
||||||
}
|
|
||||||
|
|
||||||
void Player::Init() {
|
void Player::Init() {
|
||||||
if (!engine_->Init())
|
if (!engine_->Init()) qFatal("Error initialising audio engine");
|
||||||
qFatal("Error initialising audio engine");
|
|
||||||
|
|
||||||
connect(engine_.get(), SIGNAL(StateChanged(Engine::State)), SLOT(EngineStateChanged(Engine::State)));
|
connect(engine_.get(), SIGNAL(StateChanged(Engine::State)),
|
||||||
|
SLOT(EngineStateChanged(Engine::State)));
|
||||||
connect(engine_.get(), SIGNAL(TrackAboutToEnd()), SLOT(TrackAboutToEnd()));
|
connect(engine_.get(), SIGNAL(TrackAboutToEnd()), SLOT(TrackAboutToEnd()));
|
||||||
connect(engine_.get(), SIGNAL(TrackEnded()), SLOT(TrackEnded()));
|
connect(engine_.get(), SIGNAL(TrackEnded()), SLOT(TrackEnded()));
|
||||||
connect(engine_.get(), SIGNAL(MetaData(Engine::SimpleMetaBundle)),
|
connect(engine_.get(), SIGNAL(MetaData(Engine::SimpleMetaBundle)),
|
||||||
|
@ -80,9 +80,7 @@ void Player::Init() {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void Player::ReloadSettings() {
|
void Player::ReloadSettings() { engine_->ReloadSettings(); }
|
||||||
engine_->ReloadSettings();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Player::HandleLoadResult(const UrlHandler::LoadResult& result) {
|
void Player::HandleLoadResult(const UrlHandler::LoadResult& result) {
|
||||||
switch (result.type_) {
|
switch (result.type_) {
|
||||||
|
@ -97,28 +95,27 @@ void Player::HandleLoadResult(const UrlHandler::LoadResult& result) {
|
||||||
case UrlHandler::LoadResult::TrackAvailable: {
|
case UrlHandler::LoadResult::TrackAvailable: {
|
||||||
// Might've been an async load, so check we're still on the same item
|
// Might've been an async load, so check we're still on the same item
|
||||||
int current_index = app_->playlist_manager()->active()->current_row();
|
int current_index = app_->playlist_manager()->active()->current_row();
|
||||||
if (current_index == -1)
|
if (current_index == -1) return;
|
||||||
return;
|
|
||||||
|
|
||||||
shared_ptr<PlaylistItem> item = app_->playlist_manager()->active()->item_at(current_index);
|
shared_ptr<PlaylistItem> item =
|
||||||
if (!item || item->Url() != result.original_url_)
|
app_->playlist_manager()->active()->item_at(current_index);
|
||||||
return;
|
if (!item || item->Url() != result.original_url_) return;
|
||||||
|
|
||||||
qLog(Debug) << "URL handler for" << result.original_url_
|
qLog(Debug) << "URL handler for" << result.original_url_ << "returned"
|
||||||
<< "returned" << result.media_url_;
|
<< result.media_url_;
|
||||||
|
|
||||||
// If there was no length info in song's metadata, use the one provided by
|
// If there was no length info in song's metadata, use the one provided by
|
||||||
// URL handler, if there is one
|
// URL handler, if there is one
|
||||||
if (item->Metadata().length_nanosec() <= 0 && result.length_nanosec_ != -1) {
|
if (item->Metadata().length_nanosec() <= 0 &&
|
||||||
|
result.length_nanosec_ != -1) {
|
||||||
Song song = item->Metadata();
|
Song song = item->Metadata();
|
||||||
song.set_length_nanosec(result.length_nanosec_);
|
song.set_length_nanosec(result.length_nanosec_);
|
||||||
item->SetTemporaryMetadata(song);
|
item->SetTemporaryMetadata(song);
|
||||||
app_->playlist_manager()->active()->InformOfCurrentSongChange();
|
app_->playlist_manager()->active()->InformOfCurrentSongChange();
|
||||||
}
|
}
|
||||||
engine_->Play(result.media_url_, stream_change_type_,
|
engine_->Play(
|
||||||
item->Metadata().has_cue(),
|
result.media_url_, stream_change_type_, item->Metadata().has_cue(),
|
||||||
item->Metadata().beginning_nanosec(),
|
item->Metadata().beginning_nanosec(), item->Metadata().end_nanosec());
|
||||||
item->Metadata().end_nanosec());
|
|
||||||
|
|
||||||
current_item_ = item;
|
current_item_ = item;
|
||||||
loading_async_ = QUrl();
|
loading_async_ = QUrl();
|
||||||
|
@ -135,21 +132,17 @@ void Player::HandleLoadResult(const UrlHandler::LoadResult& result) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Player::Next() {
|
void Player::Next() { NextInternal(Engine::Manual); }
|
||||||
NextInternal(Engine::Manual);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Player::NextInternal(Engine::TrackChangeFlags change) {
|
void Player::NextInternal(Engine::TrackChangeFlags change) {
|
||||||
if (HandleStopAfter())
|
if (HandleStopAfter()) return;
|
||||||
return;
|
|
||||||
|
|
||||||
if (app_->playlist_manager()->active()->current_item()) {
|
if (app_->playlist_manager()->active()->current_item()) {
|
||||||
const QUrl url = app_->playlist_manager()->active()->current_item()->Url();
|
const QUrl url = app_->playlist_manager()->active()->current_item()->Url();
|
||||||
|
|
||||||
if (url_handlers_.contains(url.scheme())) {
|
if (url_handlers_.contains(url.scheme())) {
|
||||||
// The next track is already being loaded
|
// The next track is already being loaded
|
||||||
if (url == loading_async_)
|
if (url == loading_async_) return;
|
||||||
return;
|
|
||||||
|
|
||||||
stream_change_type_ = change;
|
stream_change_type_ = change;
|
||||||
HandleLoadResult(url_handlers_[url.scheme()]->LoadNext(url));
|
HandleLoadResult(url_handlers_[url.scheme()]->LoadNext(url));
|
||||||
|
@ -168,8 +161,10 @@ void Player::NextItem(Engine::TrackChangeFlags change) {
|
||||||
const PlaylistSequence::RepeatMode repeat_mode =
|
const PlaylistSequence::RepeatMode repeat_mode =
|
||||||
active_playlist->sequence()->repeat_mode();
|
active_playlist->sequence()->repeat_mode();
|
||||||
if (repeat_mode != PlaylistSequence::Repeat_Off) {
|
if (repeat_mode != PlaylistSequence::Repeat_Off) {
|
||||||
if ((repeat_mode == PlaylistSequence::Repeat_Track && nb_errors_received_ >= 3) ||
|
if ((repeat_mode == PlaylistSequence::Repeat_Track &&
|
||||||
(nb_errors_received_ >= app_->playlist_manager()->active()->proxy()->rowCount())) {
|
nb_errors_received_ >= 3) ||
|
||||||
|
(nb_errors_received_ >=
|
||||||
|
app_->playlist_manager()->active()->proxy()->rowCount())) {
|
||||||
// We received too many "Error" state changes: probably looping over a
|
// We received too many "Error" state changes: probably looping over a
|
||||||
// playlist which contains only unavailable elements: stop now.
|
// playlist which contains only unavailable elements: stop now.
|
||||||
nb_errors_received_ = 0;
|
nb_errors_received_ = 0;
|
||||||
|
@ -211,13 +206,13 @@ bool Player::HandleStopAfter() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Player::TrackEnded() {
|
void Player::TrackEnded() {
|
||||||
if (HandleStopAfter())
|
if (HandleStopAfter()) return;
|
||||||
return;
|
|
||||||
|
|
||||||
if (current_item_ && current_item_->IsLocalLibraryItem() &&
|
if (current_item_ && current_item_->IsLocalLibraryItem() &&
|
||||||
current_item_->Metadata().id() != -1 &&
|
current_item_->Metadata().id() != -1 &&
|
||||||
!app_->playlist_manager()->active()->have_incremented_playcount() &&
|
!app_->playlist_manager()->active()->have_incremented_playcount() &&
|
||||||
app_->playlist_manager()->active()->get_lastfm_status() != Playlist::LastFM_Seeked) {
|
app_->playlist_manager()->active()->get_lastfm_status() !=
|
||||||
|
Playlist::LastFM_Seeked) {
|
||||||
// The track finished before its scrobble point (30 seconds), so increment
|
// The track finished before its scrobble point (30 seconds), so increment
|
||||||
// the play count now.
|
// the play count now.
|
||||||
app_->playlist_manager()->library_backend()->IncrementPlayCountAsync(
|
app_->playlist_manager()->library_backend()->IncrementPlayCountAsync(
|
||||||
|
@ -235,7 +230,8 @@ void Player::PlayPause() {
|
||||||
|
|
||||||
case Engine::Playing: {
|
case Engine::Playing: {
|
||||||
// We really shouldn't pause last.fm streams
|
// We really shouldn't pause last.fm streams
|
||||||
// Stopping seems like a reasonable thing to do (especially on mac where there
|
// Stopping seems like a reasonable thing to do (especially on mac where
|
||||||
|
// there
|
||||||
// is no media key for stop).
|
// is no media key for stop).
|
||||||
if (current_item_->options() & PlaylistItem::PauseDisabled) {
|
if (current_item_->options() & PlaylistItem::PauseDisabled) {
|
||||||
Stop();
|
Stop();
|
||||||
|
@ -248,9 +244,9 @@ void Player::PlayPause() {
|
||||||
case Engine::Empty:
|
case Engine::Empty:
|
||||||
case Engine::Error:
|
case Engine::Error:
|
||||||
case Engine::Idle: {
|
case Engine::Idle: {
|
||||||
app_->playlist_manager()->SetActivePlaylist(app_->playlist_manager()->current_id());
|
app_->playlist_manager()->SetActivePlaylist(
|
||||||
if (app_->playlist_manager()->active()->rowCount() == 0)
|
app_->playlist_manager()->current_id());
|
||||||
break;
|
if (app_->playlist_manager()->active()->rowCount() == 0) break;
|
||||||
|
|
||||||
int i = app_->playlist_manager()->active()->current_row();
|
int i = app_->playlist_manager()->active()->current_row();
|
||||||
if (i == -1) i = app_->playlist_manager()->active()->last_played_row();
|
if (i == -1) i = app_->playlist_manager()->active()->last_played_row();
|
||||||
|
@ -263,8 +259,7 @@ void Player::PlayPause() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Player::RestartOrPrevious() {
|
void Player::RestartOrPrevious() {
|
||||||
if (engine_->position_nanosec() < 8*kNsecPerSec)
|
if (engine_->position_nanosec() < 8 * kNsecPerSec) return Previous();
|
||||||
return Previous();
|
|
||||||
|
|
||||||
SeekTo(0);
|
SeekTo(0);
|
||||||
}
|
}
|
||||||
|
@ -276,12 +271,11 @@ void Player::Stop() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Player::StopAfterCurrent() {
|
void Player::StopAfterCurrent() {
|
||||||
app_->playlist_manager()->active()->StopAfter(app_->playlist_manager()->active()->current_row());
|
app_->playlist_manager()->active()->StopAfter(
|
||||||
|
app_->playlist_manager()->active()->current_row());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Player::Previous() {
|
void Player::Previous() { PreviousItem(Engine::Manual); }
|
||||||
PreviousItem(Engine::Manual);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Player::PreviousItem(Engine::TrackChangeFlags change) {
|
void Player::PreviousItem(Engine::TrackChangeFlags change) {
|
||||||
const bool ignore_repeat_track = change & Engine::Manual;
|
const bool ignore_repeat_track = change & Engine::Manual;
|
||||||
|
@ -304,11 +298,17 @@ void Player::EngineStateChanged(Engine::State state) {
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case Engine::Paused: emit Paused(); break;
|
case Engine::Paused:
|
||||||
case Engine::Playing: emit Playing(); break;
|
emit Paused();
|
||||||
|
break;
|
||||||
|
case Engine::Playing:
|
||||||
|
emit Playing();
|
||||||
|
break;
|
||||||
case Engine::Error:
|
case Engine::Error:
|
||||||
case Engine::Empty:
|
case Engine::Empty:
|
||||||
case Engine::Idle: emit Stopped(); break;
|
case Engine::Idle:
|
||||||
|
emit Stopped();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
last_state_ = state;
|
last_state_ = state;
|
||||||
}
|
}
|
||||||
|
@ -323,15 +323,14 @@ void Player::SetVolume(int value) {
|
||||||
if (volume != old_volume) {
|
if (volume != old_volume) {
|
||||||
emit VolumeChanged(volume);
|
emit VolumeChanged(volume);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int Player::GetVolume() const {
|
int Player::GetVolume() const { return engine_->volume(); }
|
||||||
return engine_->volume();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Player::PlayAt(int index, Engine::TrackChangeFlags change, bool reshuffle) {
|
void Player::PlayAt(int index, Engine::TrackChangeFlags change,
|
||||||
if (change == Engine::Manual && engine_->position_nanosec() != engine_->length_nanosec()) {
|
bool reshuffle) {
|
||||||
|
if (change == Engine::Manual &&
|
||||||
|
engine_->position_nanosec() != engine_->length_nanosec()) {
|
||||||
emit TrackSkipped(current_item_);
|
emit TrackSkipped(current_item_);
|
||||||
const QUrl& url = current_item_->Url();
|
const QUrl& url = current_item_->Url();
|
||||||
if (url_handlers_.contains(url.scheme())) {
|
if (url_handlers_.contains(url.scheme())) {
|
||||||
|
@ -339,15 +338,13 @@ void Player::PlayAt(int index, Engine::TrackChangeFlags change, bool reshuffle)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (current_item_ &&
|
if (current_item_ && app_->playlist_manager()->active()->has_item_at(index) &&
|
||||||
app_->playlist_manager()->active()->has_item_at(index) &&
|
|
||||||
current_item_->Metadata().IsOnSameAlbum(
|
current_item_->Metadata().IsOnSameAlbum(
|
||||||
app_->playlist_manager()->active()->item_at(index)->Metadata())) {
|
app_->playlist_manager()->active()->item_at(index)->Metadata())) {
|
||||||
change |= Engine::SameAlbum;
|
change |= Engine::SameAlbum;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (reshuffle)
|
if (reshuffle) app_->playlist_manager()->active()->ReshuffleIndices();
|
||||||
app_->playlist_manager()->active()->ReshuffleIndices();
|
|
||||||
app_->playlist_manager()->active()->set_current_row(index);
|
app_->playlist_manager()->active()->set_current_row(index);
|
||||||
|
|
||||||
if (app_->playlist_manager()->active()->current_row() == -1) {
|
if (app_->playlist_manager()->active()->current_row() == -1) {
|
||||||
|
@ -360,8 +357,7 @@ void Player::PlayAt(int index, Engine::TrackChangeFlags change, bool reshuffle)
|
||||||
|
|
||||||
if (url_handlers_.contains(url.scheme())) {
|
if (url_handlers_.contains(url.scheme())) {
|
||||||
// It's already loading
|
// It's already loading
|
||||||
if (url == loading_async_)
|
if (url == loading_async_) return;
|
||||||
return;
|
|
||||||
|
|
||||||
stream_change_type_ = change;
|
stream_change_type_ = change;
|
||||||
HandleLoadResult(url_handlers_[url.scheme()]->StartLoading(url));
|
HandleLoadResult(url_handlers_[url.scheme()]->StartLoading(url));
|
||||||
|
@ -398,14 +394,16 @@ void Player::SeekTo(int seconds) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const qint64 nanosec = qBound(0ll, qint64(seconds) * kNsecPerSec,
|
const qint64 nanosec =
|
||||||
length_nanosec);
|
qBound(0ll, qint64(seconds) * kNsecPerSec, length_nanosec);
|
||||||
engine_->Seek(nanosec);
|
engine_->Seek(nanosec);
|
||||||
|
|
||||||
// If we seek the track we don't want to submit it to last.fm
|
// If we seek the track we don't want to submit it to last.fm
|
||||||
qLog(Info) << "Track seeked to" << nanosec << "ns - not scrobbling";
|
qLog(Info) << "Track seeked to" << nanosec << "ns - not scrobbling";
|
||||||
if (app_->playlist_manager()->active()->get_lastfm_status() == Playlist::LastFM_New) {
|
if (app_->playlist_manager()->active()->get_lastfm_status() ==
|
||||||
app_->playlist_manager()->active()->set_lastfm_status(Playlist::LastFM_Seeked);
|
Playlist::LastFM_New) {
|
||||||
|
app_->playlist_manager()->active()->set_lastfm_status(
|
||||||
|
Playlist::LastFM_Seeked);
|
||||||
}
|
}
|
||||||
|
|
||||||
emit Seeked(nanosec / 1000);
|
emit Seeked(nanosec / 1000);
|
||||||
|
@ -421,8 +419,7 @@ void Player::SeekBackward() {
|
||||||
|
|
||||||
void Player::EngineMetadataReceived(const Engine::SimpleMetaBundle& bundle) {
|
void Player::EngineMetadataReceived(const Engine::SimpleMetaBundle& bundle) {
|
||||||
PlaylistItemPtr item = app_->playlist_manager()->active()->current_item();
|
PlaylistItemPtr item = app_->playlist_manager()->active()->current_item();
|
||||||
if (!item)
|
if (!item) return;
|
||||||
return;
|
|
||||||
|
|
||||||
Engine::SimpleMetaBundle bundle_copy = bundle;
|
Engine::SimpleMetaBundle bundle_copy = bundle;
|
||||||
|
|
||||||
|
@ -445,8 +442,7 @@ void Player::EngineMetadataReceived(const Engine::SimpleMetaBundle& bundle) {
|
||||||
song.MergeFromSimpleMetaBundle(bundle_copy);
|
song.MergeFromSimpleMetaBundle(bundle_copy);
|
||||||
|
|
||||||
// Ignore useless metadata
|
// Ignore useless metadata
|
||||||
if (song.title().isEmpty() && song.artist().isEmpty())
|
if (song.title().isEmpty() && song.artist().isEmpty()) return;
|
||||||
return;
|
|
||||||
|
|
||||||
app_->playlist_manager()->active()->SetStreamMetadata(item->Url(), song);
|
app_->playlist_manager()->active()->SetStreamMetadata(item->Url(), song);
|
||||||
}
|
}
|
||||||
|
@ -468,9 +464,7 @@ void Player::Mute() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Player::Pause() {
|
void Player::Pause() { engine_->Pause(); }
|
||||||
engine_->Pause();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Player::Play() {
|
void Player::Play() {
|
||||||
switch (GetState()) {
|
switch (GetState()) {
|
||||||
|
@ -487,13 +481,11 @@ void Player::Play() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Player::ShowOSD() {
|
void Player::ShowOSD() {
|
||||||
if (current_item_)
|
if (current_item_) emit ForceShowOSD(current_item_->Metadata(), false);
|
||||||
emit ForceShowOSD(current_item_->Metadata(), false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Player::TogglePrettyOSD() {
|
void Player::TogglePrettyOSD() {
|
||||||
if (current_item_)
|
if (current_item_) emit ForceShowOSD(current_item_->Metadata(), true);
|
||||||
emit ForceShowOSD(current_item_->Metadata(), true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Player::TrackAboutToEnd() {
|
void Player::TrackAboutToEnd() {
|
||||||
|
@ -509,11 +501,13 @@ void Player::TrackAboutToEnd() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool has_next_row = app_->playlist_manager()->active()->next_row() != -1;
|
const bool has_next_row =
|
||||||
|
app_->playlist_manager()->active()->next_row() != -1;
|
||||||
PlaylistItemPtr next_item;
|
PlaylistItemPtr next_item;
|
||||||
|
|
||||||
if (has_next_row) {
|
if (has_next_row) {
|
||||||
next_item = app_->playlist_manager()->active()->item_at(app_->playlist_manager()->active()->next_row());
|
next_item = app_->playlist_manager()->active()->item_at(
|
||||||
|
app_->playlist_manager()->active()->next_row());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (engine_->is_autocrossfade_enabled()) {
|
if (engine_->is_autocrossfade_enabled()) {
|
||||||
|
@ -522,15 +516,12 @@ void Player::TrackAboutToEnd() {
|
||||||
|
|
||||||
// But, if there's no next track and we don't want to fade out, then do
|
// But, if there's no next track and we don't want to fade out, then do
|
||||||
// nothing and just let the track finish to completion.
|
// nothing and just let the track finish to completion.
|
||||||
if (!engine_->is_fadeout_enabled() && !has_next_row)
|
if (!engine_->is_fadeout_enabled() && !has_next_row) return;
|
||||||
return;
|
|
||||||
|
|
||||||
// If the next track is on the same album (or same cue file), and the
|
// If the next track is on the same album (or same cue file), and the
|
||||||
// user doesn't want to crossfade between tracks on the same album, then
|
// user doesn't want to crossfade between tracks on the same album, then
|
||||||
// don't do this automatic crossfading.
|
// don't do this automatic crossfading.
|
||||||
if (engine_->crossfade_same_album() ||
|
if (engine_->crossfade_same_album() || !has_next_row || !next_item ||
|
||||||
!has_next_row ||
|
|
||||||
!next_item ||
|
|
||||||
!current_item_->Metadata().IsOnSameAlbum(next_item->Metadata())) {
|
!current_item_->Metadata().IsOnSameAlbum(next_item->Metadata())) {
|
||||||
TrackEnded();
|
TrackEnded();
|
||||||
return;
|
return;
|
||||||
|
@ -539,8 +530,7 @@ void Player::TrackAboutToEnd() {
|
||||||
|
|
||||||
// Crossfade is off, so start preloading the next track so we don't get a
|
// Crossfade is off, so start preloading the next track so we don't get a
|
||||||
// gap between songs.
|
// gap between songs.
|
||||||
if (!has_next_row || !next_item)
|
if (!has_next_row || !next_item) return;
|
||||||
return;
|
|
||||||
|
|
||||||
QUrl url = next_item->Url();
|
QUrl url = next_item->Url();
|
||||||
|
|
||||||
|
@ -588,7 +578,8 @@ void Player::RegisterUrlHandler(UrlHandler* handler) {
|
||||||
|
|
||||||
qLog(Info) << "Registered URL handler for" << scheme;
|
qLog(Info) << "Registered URL handler for" << scheme;
|
||||||
url_handlers_.insert(scheme, handler);
|
url_handlers_.insert(scheme, handler);
|
||||||
connect(handler, SIGNAL(destroyed(QObject*)), SLOT(UrlHandlerDestroyed(QObject*)));
|
connect(handler, SIGNAL(destroyed(QObject*)),
|
||||||
|
SLOT(UrlHandlerDestroyed(QObject*)));
|
||||||
connect(handler, SIGNAL(AsyncLoadComplete(UrlHandler::LoadResult)),
|
connect(handler, SIGNAL(AsyncLoadComplete(UrlHandler::LoadResult)),
|
||||||
SLOT(HandleLoadResult(UrlHandler::LoadResult)));
|
SLOT(HandleLoadResult(UrlHandler::LoadResult)));
|
||||||
}
|
}
|
||||||
|
@ -596,20 +587,22 @@ void Player::RegisterUrlHandler(UrlHandler* handler) {
|
||||||
void Player::UnregisterUrlHandler(UrlHandler* handler) {
|
void Player::UnregisterUrlHandler(UrlHandler* handler) {
|
||||||
const QString scheme = url_handlers_.key(handler);
|
const QString scheme = url_handlers_.key(handler);
|
||||||
if (scheme.isEmpty()) {
|
if (scheme.isEmpty()) {
|
||||||
qLog(Warning) << "Tried to unregister a URL handler for" << handler->scheme()
|
qLog(Warning) << "Tried to unregister a URL handler for"
|
||||||
<< "that wasn't registered";
|
<< handler->scheme() << "that wasn't registered";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
qLog(Info) << "Unregistered URL handler for" << scheme;
|
qLog(Info) << "Unregistered URL handler for" << scheme;
|
||||||
url_handlers_.remove(scheme);
|
url_handlers_.remove(scheme);
|
||||||
disconnect(handler, SIGNAL(destroyed(QObject*)), this, SLOT(UrlHandlerDestroyed(QObject*)));
|
disconnect(handler, SIGNAL(destroyed(QObject*)), this,
|
||||||
disconnect(handler, SIGNAL(AsyncLoadComplete(UrlHandler::LoadResult)),
|
SLOT(UrlHandlerDestroyed(QObject*)));
|
||||||
this, SLOT(HandleLoadResult(UrlHandler::LoadResult)));
|
disconnect(handler, SIGNAL(AsyncLoadComplete(UrlHandler::LoadResult)), this,
|
||||||
|
SLOT(HandleLoadResult(UrlHandler::LoadResult)));
|
||||||
}
|
}
|
||||||
|
|
||||||
const UrlHandler* Player::HandlerForUrl(const QUrl& url) const {
|
const UrlHandler* Player::HandlerForUrl(const QUrl& url) const {
|
||||||
QMap<QString, UrlHandler*>::const_iterator it = url_handlers_.constFind(url.scheme());
|
QMap<QString, UrlHandler*>::const_iterator it =
|
||||||
|
url_handlers_.constFind(url.scheme());
|
||||||
if (it == url_handlers_.constEnd()) {
|
if (it == url_handlers_.constEnd()) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,6 @@
|
||||||
class Application;
|
class Application;
|
||||||
class LastFMService;
|
class LastFMService;
|
||||||
|
|
||||||
|
|
||||||
class PlayerInterface : public QObject {
|
class PlayerInterface : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
|
@ -54,7 +53,8 @@ public slots:
|
||||||
virtual void ReloadSettings() = 0;
|
virtual void ReloadSettings() = 0;
|
||||||
|
|
||||||
// Manual track change to the specified track
|
// Manual track change to the specified track
|
||||||
virtual void PlayAt(int i, Engine::TrackChangeFlags change, bool reshuffle) = 0;
|
virtual void PlayAt(int i, Engine::TrackChangeFlags change,
|
||||||
|
bool reshuffle) = 0;
|
||||||
|
|
||||||
// If there's currently a song playing, pause it, otherwise play the track
|
// If there's currently a song playing, pause it, otherwise play the track
|
||||||
// that was playing last, or the first one on the playlist
|
// that was playing last, or the first one on the playlist
|
||||||
|
@ -93,11 +93,13 @@ signals:
|
||||||
// Emitted when there's a manual change to the current's track position.
|
// Emitted when there's a manual change to the current's track position.
|
||||||
void Seeked(qlonglong microseconds);
|
void Seeked(qlonglong microseconds);
|
||||||
|
|
||||||
// Emitted when Player has processed a request to play another song. This contains
|
// Emitted when Player has processed a request to play another song. This
|
||||||
|
// contains
|
||||||
// the URL of the song and a flag saying whether it was able to play the song.
|
// the URL of the song and a flag saying whether it was able to play the song.
|
||||||
void SongChangeRequestProcessed(const QUrl& url, bool valid);
|
void SongChangeRequestProcessed(const QUrl& url, bool valid);
|
||||||
|
|
||||||
// The toggle parameter is true when user requests to toggle visibility for Pretty OSD
|
// The toggle parameter is true when user requests to toggle visibility for
|
||||||
|
// Pretty OSD
|
||||||
void ForceShowOSD(Song, bool toogle);
|
void ForceShowOSD(Song, bool toogle);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,8 @@
|
||||||
|
|
||||||
class PoTranslator : public QTranslator {
|
class PoTranslator : public QTranslator {
|
||||||
public:
|
public:
|
||||||
QString translate(const char* context, const char* source_text, const char* disambiguation = 0) const {
|
QString translate(const char* context, const char* source_text,
|
||||||
|
const char* disambiguation = 0) const {
|
||||||
QString ret = QTranslator::translate(context, source_text, disambiguation);
|
QString ret = QTranslator::translate(context, source_text, disambiguation);
|
||||||
if (!ret.isEmpty()) return ret;
|
if (!ret.isEmpty()) return ret;
|
||||||
return QTranslator::translate(NULL, source_text, disambiguation);
|
return QTranslator::translate(NULL, source_text, disambiguation);
|
||||||
|
|
|
@ -21,9 +21,7 @@
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
|
|
||||||
#if QT_VERSION < 0x040700
|
#if QT_VERSION < 0x040700
|
||||||
inline uint qHash(const QUrl& url) {
|
inline uint qHash(const QUrl& url) { return qHash(url.toEncoded()); }
|
||||||
return qHash(url.toEncoded());
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif // QHASH_QURL_H
|
#endif // QHASH_QURL_H
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue