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:
John Maguire 2014-02-07 16:34:20 +01:00
parent acfc7e6d21
commit bebd781fdf
803 changed files with 22699 additions and 22831 deletions

View File

@ -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>

View File

@ -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;

View File

@ -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

View File

@ -32,7 +32,6 @@ QString GetUserDataDirectory();
QString GetCacheDirectory(); QString GetCacheDirectory();
QString GetSettingsDirectory(); QString GetSettingsDirectory();
} }
#endif #endif

View File

@ -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;
} }
} }

View File

@ -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);

View File

@ -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_;

View File

@ -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";

View File

@ -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;
} }

View File

@ -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();
} }

View File

@ -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) {

View File

@ -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);

View File

@ -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...);
} }
} }

View File

@ -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]));

View File

@ -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 {

View File

@ -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"

View File

@ -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

View File

@ -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();

View File

@ -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);
} }

View File

@ -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

View File

@ -17,9 +17,4 @@
#include "workerpool.h" #include "workerpool.h"
_WorkerPoolBase::_WorkerPoolBase(QObject* parent) _WorkerPoolBase::_WorkerPoolBase(QObject* parent) : QObject(parent) {}
: QObject(parent)
{
}

View File

@ -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);

View File

@ -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";

View File

@ -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();

View File

@ -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;

View File

@ -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) {

View File

@ -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_;

View File

@ -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_);

View File

@ -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();

View File

@ -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);

View File

@ -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;

View File

@ -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

View File

@ -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];
} }
} }
} }

View File

@ -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

View File

@ -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);
} }

View File

@ -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*);

View File

@ -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));

View File

@ -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*);

View File

@ -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);

View File

@ -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();

View File

@ -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;
} }

View File

@ -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();

View File

@ -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;
} }

View File

@ -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();

View File

@ -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);
} }
} }
} }

View File

@ -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 {

View File

@ -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_);
} }

View File

@ -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*);

View File

@ -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);

View File

@ -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) {}

View File

@ -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_);

View File

@ -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);

View File

@ -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;

View File

@ -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;
} }

View File

@ -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_; }

View File

@ -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_; }

View File

@ -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);

View File

@ -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_;

View File

@ -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

View File

@ -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 {

View File

@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -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);
} }

View File

@ -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;

View File

@ -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_)

View File

@ -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);

View File

@ -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;
} }

View File

@ -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
} }

View File

@ -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_;

View File

@ -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();

View File

@ -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

View File

@ -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);
} }

View File

@ -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[]);

View File

@ -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);

View File

@ -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_;

View File

@ -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>");

View File

@ -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_;
} }
}; };

View File

@ -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

View File

@ -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()));
} }

View File

@ -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;

View File

@ -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

View File

@ -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);
} }

View File

@ -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;

View File

@ -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;
} }

View File

@ -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())

View File

@ -17,6 +17,4 @@
#include "musicstorage.h" #include "musicstorage.h"
MusicStorage::MusicStorage() MusicStorage::MusicStorage() {}
{
}

View File

@ -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) {}

View File

@ -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);

View File

@ -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

View File

@ -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();

View File

@ -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;

View File

@ -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

View File

@ -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);

View File

@ -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();
} }
} }

View File

@ -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_;

View File

@ -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;
} }

View File

@ -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);
}; };

View File

@ -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);

View File

@ -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