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
// compatible.
#include <QCoreApplication>
#include <QStringList>

View File

@ -25,15 +25,13 @@
#include <cstring>
MediaPipeline::MediaPipeline(int port, quint64 length_msec)
: port_(port),
length_msec_(length_msec),
accepting_data_(true),
pipeline_(nullptr),
appsrc_(nullptr),
byte_rate_(1),
offset_bytes_(0)
{
}
: port_(port),
length_msec_(length_msec),
accepting_data_(true),
pipeline_(nullptr),
appsrc_(nullptr),
byte_rate_(1),
offset_bytes_(0) {}
MediaPipeline::~MediaPipeline() {
if (pipeline_) {
@ -43,8 +41,7 @@ MediaPipeline::~MediaPipeline() {
}
bool MediaPipeline::Init(int sample_rate, int channels) {
if (is_initialised())
return false;
if (is_initialised()) return false;
pipeline_ = gst_pipeline_new("pipeline");
@ -54,10 +51,21 @@ bool MediaPipeline::Init(int sample_rate, int channels) {
tcpsink_ = gst_element_factory_make("tcpclientsink", nullptr);
if (!pipeline_ || !appsrc_ || !tcpsink_) {
if (pipeline_) { gst_object_unref(GST_OBJECT(pipeline_)); pipeline_ = 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; }
if (pipeline_) {
gst_object_unref(GST_OBJECT(pipeline_));
pipeline_ = 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;
}
@ -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
// 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
g_object_set(G_OBJECT(appsrc_), "format", GST_FORMAT_TIME, nullptr);
@ -97,14 +106,11 @@ bool MediaPipeline::Init(int sample_rate, int channels) {
#endif
// Set caps
GstCaps* caps = gst_caps_new_simple("audio/x-raw-int",
"endianness", G_TYPE_INT, endianness,
"signed", G_TYPE_BOOLEAN, TRUE,
"width", G_TYPE_INT, 16,
"depth", G_TYPE_INT, 16,
"rate", G_TYPE_INT, sample_rate,
"channels", G_TYPE_INT, channels,
nullptr);
GstCaps* caps = gst_caps_new_simple(
"audio/x-raw-int", "endianness", G_TYPE_INT, endianness, "signed",
G_TYPE_BOOLEAN, TRUE, "width", G_TYPE_INT, 16, "depth", G_TYPE_INT, 16,
"rate", G_TYPE_INT, sample_rate, "channels", G_TYPE_INT, channels,
nullptr);
gst_app_src_set_caps(appsrc_, caps);
gst_caps_unref(caps);
@ -115,12 +121,12 @@ bool MediaPipeline::Init(int sample_rate, int channels) {
gst_app_src_set_size(appsrc_, bytes);
// 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) {
if (!is_initialised())
return;
if (!is_initialised()) return;
GstBuffer* buffer = gst_buffer_new_and_alloc(length);
@ -137,8 +143,7 @@ void MediaPipeline::WriteData(const char* data, qint64 length) {
}
void MediaPipeline::EndStream() {
if (!is_initialised())
return;
if (!is_initialised()) return;
gst_app_src_end_of_stream(appsrc_);
}
@ -153,8 +158,9 @@ void MediaPipeline::EnoughDataCallback(GstAppSrc* src, void* data) {
me->accepting_data_ = false;
}
gboolean MediaPipeline::SeekDataCallback(GstAppSrc* src, guint64 offset, void * data) {
//MediaPipeline* me = reinterpret_cast<MediaPipeline*>(data);
gboolean MediaPipeline::SeekDataCallback(GstAppSrc* src, guint64 offset,
void* data) {
// MediaPipeline* me = reinterpret_cast<MediaPipeline*>(data);
qLog(Debug) << "Gstreamer wants seek to" << offset;
return false;

View File

@ -27,7 +27,7 @@
#include <gst/app/gstappsrc.h>
class MediaPipeline {
public:
public:
MediaPipeline(int port, quint64 length_msec);
~MediaPipeline();
@ -38,12 +38,12 @@ public:
void WriteData(const char* data, qint64 length);
void EndStream();
private:
private:
static void NeedDataCallback(GstAppSrc* src, guint length, void* data);
static void EnoughDataCallback(GstAppSrc* src, void* data);
static gboolean SeekDataCallback(GstAppSrc* src, guint64 offset, void* data);
private:
private:
Q_DISABLE_COPY(MediaPipeline)
const int port_;
@ -59,4 +59,4 @@ private:
quint64 offset_bytes_;
};
#endif // MEDIAPIPELINE_H
#endif // MEDIAPIPELINE_H

View File

@ -31,7 +31,8 @@ namespace utilities {
QString GetCacheDirectory() {
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.
@ -47,10 +48,11 @@ QString GetSettingsDirectory() {
QString ret;
#ifdef Q_OS_WIN32
ret = GetUserDataDirectory() + "/" + QCoreApplication::applicationName() + "/spotify-settings";
ret = GetUserDataDirectory() + "/" + QCoreApplication::applicationName() +
"/spotify-settings";
#else
ret = QFileInfo(QSettings().fileName()).absolutePath() + "/spotify-settings";
#endif // Q_OS_WIN32
#endif // Q_OS_WIN32
// Create the directory
QDir dir;
@ -59,6 +61,6 @@ QString GetSettingsDirectory() {
return ret;
}
#endif // Q_OS_DARWIN
#endif // Q_OS_DARWIN
} // namespace utilities

View File

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

View File

@ -10,10 +10,8 @@ QString GetUserDataDirectory() {
NSAutoreleasePool* pool = [NSAutoreleasePool alloc];
[pool init];
NSArray* paths = NSSearchPathForDirectoriesInDomains(
NSCachesDirectory,
NSUserDomainMask,
YES);
NSArray* paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory,
NSUserDomainMask, YES);
QString ret;
if ([paths count] > 0) {
NSString* user_path = [paths objectAtIndex:0];
@ -28,9 +26,7 @@ QString GetUserDataDirectory() {
QString GetSettingsDirectory() {
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
NSArray* paths = NSSearchPathForDirectoriesInDomains(
NSApplicationSupportDirectory,
NSUserDomainMask,
YES);
NSApplicationSupportDirectory, NSUserDomainMask, YES);
NSString* ret;
if ([paths count] > 0) {
ret = [paths objectAtIndex:0];
@ -40,15 +36,13 @@ QString GetSettingsDirectory() {
ret = [ret stringByAppendingString:@"/Clementine/spotify-settings"];
NSFileManager* file_manager = [NSFileManager defaultManager];
[file_manager createDirectoryAtPath:
ret
withIntermediateDirectories:YES
attributes:nil
error:nil];
[file_manager createDirectoryAtPath:ret
withIntermediateDirectories:YES
attributes:nil
error:nil];
QString path = QString::fromUtf8([ret UTF8String]);
[pool drain];
return path;
}
}

View File

@ -36,18 +36,18 @@
const int SpotifyClient::kSpotifyImageIDSize = 20;
const int SpotifyClient::kWaveHeaderSize = 44;
SpotifyClient::SpotifyClient(QObject* parent)
: AbstractMessageHandler<pb::spotify::Message>(nullptr, parent),
api_key_(QByteArray::fromBase64(kSpotifyApiKey)),
protocol_socket_(new QTcpSocket(this)),
session_(nullptr),
events_timer_(new QTimer(this)) {
: AbstractMessageHandler<pb::spotify::Message>(nullptr, parent),
api_key_(QByteArray::fromBase64(kSpotifyApiKey)),
protocol_socket_(new QTcpSocket(this)),
session_(nullptr),
events_timer_(new QTimer(this)) {
SetDevice(protocol_socket_);
memset(&spotify_callbacks_, 0, sizeof(spotify_callbacks_));
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(&load_playlist_callbacks_, 0, sizeof(load_playlist_callbacks_));
@ -64,15 +64,17 @@ SpotifyClient::SpotifyClient(QObject* parent)
spotify_callbacks_.start_playback = &StartPlaybackCallback;
spotify_callbacks_.stop_playback = &StopPlaybackCallback;
playlistcontainer_callbacks_.container_loaded = &PlaylistContainerLoadedCallback;
playlistcontainer_callbacks_.container_loaded =
&PlaylistContainerLoadedCallback;
playlistcontainer_callbacks_.playlist_added = &PlaylistAddedCallback;
playlistcontainer_callbacks_.playlist_moved = &PlaylistMovedCallback;
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();
qLog(Debug) << "Using:" << cache << "for Spotify cache";
@ -111,41 +113,43 @@ void SpotifyClient::Init(quint16 port) {
}
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;
pb::spotify::LoginResponse_Error error_code = pb::spotify::LoginResponse_Error_Other;
pb::spotify::LoginResponse_Error error_code =
pb::spotify::LoginResponse_Error_Other;
if (!success) {
qLog(Warning) << "Failed to login" << sp_error_message(error);
}
switch (error) {
case SP_ERROR_BAD_USERNAME_OR_PASSWORD:
error_code = pb::spotify::LoginResponse_Error_BadUsernameOrPassword;
break;
case SP_ERROR_USER_BANNED:
error_code = pb::spotify::LoginResponse_Error_UserBanned;
break;
case SP_ERROR_USER_NEEDS_PREMIUM :
error_code = pb::spotify::LoginResponse_Error_UserNeedsPremium;
break;
default:
error_code = pb::spotify::LoginResponse_Error_Other;
break;
case SP_ERROR_BAD_USERNAME_OR_PASSWORD:
error_code = pb::spotify::LoginResponse_Error_BadUsernameOrPassword;
break;
case SP_ERROR_USER_BANNED:
error_code = pb::spotify::LoginResponse_Error_UserBanned;
break;
case SP_ERROR_USER_NEEDS_PREMIUM:
error_code = pb::spotify::LoginResponse_Error_UserNeedsPremium;
break;
default:
error_code = pb::spotify::LoginResponse_Error_Other;
break;
}
me->SendLoginCompleted(success, sp_error_message(error), error_code);
if (success) {
sp_playlistcontainer_add_callbacks(
sp_session_playlistcontainer(session),
&me->playlistcontainer_callbacks_, me);
sp_playlistcontainer_add_callbacks(sp_session_playlistcontainer(session),
&me->playlistcontainer_callbacks_, me);
sp_session_flush_caches(me->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);
}
@ -160,14 +164,11 @@ void SpotifyClient::LogMessageCallback(sp_session* session, const char* data) {
}
void SpotifyClient::Search(const pb::spotify::SearchRequest& req) {
sp_search* search = sp_search_create(
session_, req.query().c_str(),
0, req.limit(),
0, req.limit_album(),
0, 0, // artists
0, 0, // playlists
SP_SEARCH_STANDARD,
&SearchCompleteCallback, this);
sp_search* search =
sp_search_create(session_, req.query().c_str(), 0, req.limit(), 0,
req.limit_album(), 0, 0, // artists
0, 0, // playlists
SP_SEARCH_STANDARD, &SearchCompleteCallback, this);
pending_searches_[search] = req;
}
@ -184,10 +185,10 @@ void SpotifyClient::SearchCompleteCallback(sp_search* result, void* userdata) {
// we can send our response.
const int count = sp_search_num_albums(result);
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_albumbrowse* browse =
sp_albumbrowse_create(me->session_, album, &SearchAlbumBrowseComplete, me);
sp_albumbrowse* browse = sp_albumbrowse_create(
me->session_, album, &SearchAlbumBrowseComplete, me);
me->pending_search_album_browse_responses_[browse] = result;
}
@ -197,7 +198,8 @@ void SpotifyClient::SearchCompleteCallback(sp_search* result, void* userdata) {
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);
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);
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);
}
}
@ -235,14 +238,14 @@ void SpotifyClient::SendSearchResponse(sp_search* result) {
// Get the list of tracks from the search
int count = sp_search_num_tracks(result);
for (int i=0 ; i<count ; ++i) {
for (int i = 0; i < count; ++i) {
sp_track* track = sp_search_track(result, i);
ConvertTrack(track, response->add_result());
}
// Get the albums from the search. All these should be resolved by now.
QList<sp_albumbrowse*> browses = pending_search_album_browses_.take(result);
foreach (sp_albumbrowse* browse, browses) {
foreach(sp_albumbrowse * browse, browses) {
sp_album* album = sp_albumbrowse_album(browse);
pb::spotify::Album* msg = response->add_album();
@ -251,7 +254,7 @@ void SpotifyClient::SendSearchResponse(sp_search* result) {
// Add all tracks
const int tracks = sp_albumbrowse_num_tracks(browse);
for (int i=0 ; i<tracks ; ++i) {
for (int i = 0; i < tracks; ++i) {
ConvertTrack(sp_albumbrowse_track(browse, i), msg->add_track());
}
@ -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;
switch (req.bitrate()) {
case pb::spotify::Bitrate96k: bitrate = SP_BITRATE_96k; break;
case pb::spotify::Bitrate160k: bitrate = SP_BITRATE_160k; break;
case pb::spotify::Bitrate320k: bitrate = SP_BITRATE_320k; break;
case pb::spotify::Bitrate96k:
bitrate = SP_BITRATE_96k;
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"
<< bitrate << "normalisation" << req.volume_normalisation();
qLog(Debug) << "Setting playback settings: bitrate" << bitrate
<< "normalisation" << req.volume_normalisation();
sp_session_preferred_bitrate(session_, bitrate);
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_);
if (error != SP_ERROR_OK) {
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;
}
@ -324,16 +335,15 @@ void SpotifyClient::Login(const pb::spotify::LoginRequest& req) {
pb::spotify::LoginResponse_Error_ReloginFailed);
}
} else {
sp_session_login(session_,
req.username().c_str(),
req.password().c_str(),
true, // Remember the password.
sp_session_login(session_, req.username().c_str(), req.password().c_str(),
true, // Remember the password.
nullptr);
}
}
void SpotifyClient::SendLoginCompleted(bool success, const QString& error,
pb::spotify::LoginResponse_Error error_code) {
void SpotifyClient::SendLoginCompleted(
bool success, const QString& error,
pb::spotify::LoginResponse_Error error_code) {
pb::spotify::Message message;
pb::spotify::LoginResponse* response = message.mutable_login_response();
@ -347,12 +357,13 @@ void SpotifyClient::SendLoginCompleted(bool success, const QString& error,
SendMessage(message);
}
void SpotifyClient::PlaylistContainerLoadedCallback(sp_playlistcontainer* pc, void* userdata) {
void SpotifyClient::PlaylistContainerLoadedCallback(sp_playlistcontainer* pc,
void* userdata) {
SpotifyClient* me = reinterpret_cast<SpotifyClient*>(userdata);
// Install callbacks on all the playlists
const int count = sp_playlistcontainer_num_playlists(pc);
for (int i=0 ; i<count ; ++i) {
for (int i = 0; i < count; ++i) {
sp_playlist* playlist = sp_playlistcontainer_playlist(pc, i);
sp_playlist_add_callbacks(playlist, &me->get_playlists_callbacks_, me);
}
@ -360,7 +371,9 @@ void SpotifyClient::PlaylistContainerLoadedCallback(sp_playlistcontainer* pc, vo
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);
// Install callbacks on this playlist
@ -369,12 +382,16 @@ void SpotifyClient::PlaylistAddedCallback(sp_playlistcontainer* pc, sp_playlist*
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);
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);
// Remove callbacks from this playlist
@ -395,12 +412,13 @@ void SpotifyClient::SendPlaylistList() {
const int count = sp_playlistcontainer_num_playlists(container);
for (int i=0 ; i<count ; ++i) {
for (int i = 0; i < count; ++i) {
const int type = sp_playlistcontainer_playlist_type(container, i);
sp_playlist* playlist = sp_playlistcontainer_playlist(container, i);
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) {
qLog(Info) << "Playlist is not loaded yet, waiting...";
@ -431,7 +449,8 @@ void SpotifyClient::SendPlaylistList() {
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;
switch (type) {
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_);
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);
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";
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;
SendMessage(message);
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;
PlaylistStateChangedForLoadPlaylist(pending_load.playlist_, this);
}
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.
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);
// If the playlist isn't loaded yet we have to wait
@ -500,7 +524,7 @@ void SpotifyClient::PlaylistStateChangedForLoadPlaylist(sp_playlist* pl, void* u
// Find this playlist's pending load object
int pending_load_index = -1;
PendingLoadPlaylist* pending_load = nullptr;
for (int i=0 ; i<me->pending_load_playlists_.count() ; ++i) {
for (int i = 0; i < me->pending_load_playlists_.count(); ++i) {
if (me->pending_load_playlists_[i].playlist_ == pl) {
pending_load_index = i;
pending_load = &me->pending_load_playlists_[i];
@ -516,7 +540,7 @@ void SpotifyClient::PlaylistStateChangedForLoadPlaylist(sp_playlist* pl, void* u
// If the playlist was just loaded then get all its tracks and ref them
if (pending_load->tracks_.isEmpty()) {
const int count = sp_playlist_num_tracks(pl);
for (int i=0 ; i<count ; ++i) {
for (int i = 0; i < count; ++i) {
sp_track* track = sp_playlist_track(pl, i);
sp_track_add_ref(track);
pending_load->tracks_ << track;
@ -524,7 +548,7 @@ void SpotifyClient::PlaylistStateChangedForLoadPlaylist(sp_playlist* pl, void* u
}
// If any of the tracks aren't loaded yet we have to wait
foreach (sp_track* track, pending_load->tracks_) {
foreach(sp_track * track, pending_load->tracks_) {
if (!sp_track_is_loaded(track)) {
qLog(Debug) << "One or more tracks aren't loaded yet, waiting";
return;
@ -533,17 +557,17 @@ void SpotifyClient::PlaylistStateChangedForLoadPlaylist(sp_playlist* pl, void* u
// Everything is loaded so send the response protobuf and unref everything.
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
// other playlists.
if (pending_load->request_.type() == pb::spotify::Starred) {
std::reverse(pending_load->tracks_.begin(),
pending_load->tracks_.end());
std::reverse(pending_load->tracks_.begin(), pending_load->tracks_.end());
}
*response->mutable_request() = pending_load->request_;
foreach (sp_track* track, pending_load->tracks_) {
foreach(sp_track * track, pending_load->tracks_) {
me->ConvertTrack(track, response->add_track());
sp_track_release(track);
}
@ -557,7 +581,8 @@ void SpotifyClient::PlaylistStateChangedForLoadPlaylist(sp_playlist* pl, void* u
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);
me->SendPlaylistList();
@ -576,15 +601,14 @@ void SpotifyClient::ConvertTrack(sp_track* track, pb::spotify::Track* pb) {
pb->set_track(sp_track_index(track));
// Album art
const QByteArray art_id(
reinterpret_cast<const char*>(
sp_album_cover(sp_track_album(track), SP_IMAGE_SIZE_LARGE)),
kSpotifyImageIDSize);
const QByteArray art_id(reinterpret_cast<const char*>(sp_album_cover(
sp_track_album(track), SP_IMAGE_SIZE_LARGE)),
kSpotifyImageIDSize);
const QString art_id_b64 = QString::fromAscii(art_id.toBase64());
pb->set_album_art_id(DataCommaSizeFromQString(art_id_b64));
// Artists
for (int i=0 ; i<sp_track_num_artists(track) ; ++i) {
for (int i = 0; i < sp_track_num_artists(track); ++i) {
pb->add_artist(sp_artist_name(sp_track_artist(track, i)));
}
@ -613,8 +637,8 @@ void SpotifyClient::ConvertAlbum(sp_album* album, pb::spotify::Track* pb) {
// Album art
const QByteArray art_id(
reinterpret_cast<const char*>(sp_album_cover(album, SP_IMAGE_SIZE_LARGE)),
kSpotifyImageIDSize);
reinterpret_cast<const char*>(sp_album_cover(album, SP_IMAGE_SIZE_LARGE)),
kSpotifyImageIDSize);
const QString art_id_b64 = QString::fromAscii(art_id.toBase64());
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);
}
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));
}
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);
}
foreach (const PendingPlaybackRequest& playback, me->pending_playback_requests_) {
foreach(const PendingPlaybackRequest & playback,
me->pending_playback_requests_) {
me->TryPlaybackAgain(playback);
}
}
int SpotifyClient::MusicDeliveryCallback(
sp_session* session, const sp_audioformat* format,
const void* frames, int num_frames) {
SpotifyClient* me = reinterpret_cast<SpotifyClient*>(sp_session_userdata(session));
int SpotifyClient::MusicDeliveryCallback(sp_session* session,
const sp_audioformat* format,
const void* frames, int num_frames) {
SpotifyClient* me =
reinterpret_cast<SpotifyClient*>(sp_session_userdata(session));
if (!me->media_pipeline_) {
return 0;
@ -668,21 +696,23 @@ int SpotifyClient::MusicDeliveryCallback(
return 0;
}
me->media_pipeline_->WriteData(
reinterpret_cast<const char*>(frames),
num_frames * format->channels * 2);
me->media_pipeline_->WriteData(reinterpret_cast<const char*>(frames),
num_frames * format->channels * 2);
return num_frames;
}
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();
}
void SpotifyClient::StreamingErrorCallback(sp_session* session, sp_error error) {
SpotifyClient* me = reinterpret_cast<SpotifyClient*>(sp_session_userdata(session));
void SpotifyClient::StreamingErrorCallback(sp_session* session,
sp_error error) {
SpotifyClient* me =
reinterpret_cast<SpotifyClient*>(sp_session_userdata(session));
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)));
}
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);
}
void SpotifyClient::UserMessageCallback(sp_session* session, const char* message) {
void SpotifyClient::UserMessageCallback(sp_session* session,
const char* message) {
qLog(Debug) << Q_FUNC_INFO << message;
}
@ -707,7 +739,8 @@ void SpotifyClient::StopPlaybackCallback(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);
if (!container) {
qLog(Warning) << "sp_session_playlistcontainer returned nullptr";
@ -716,8 +749,9 @@ void SpotifyClient::OfflineStatusUpdatedCallback(sp_session* session) {
const int count = sp_playlistcontainer_num_playlists(container);
for (int i=0 ; i<count ; ++i) {
const sp_playlist_type type = sp_playlistcontainer_playlist_type(container, i);
for (int i = 0; i < count; ++i) {
const sp_playlist_type type =
sp_playlistcontainer_playlist_type(container, i);
sp_playlist* playlist = sp_playlistcontainer_playlist(container, i);
if (type != SP_PLAYLIST_TYPE_PLAYLIST) {
@ -748,10 +782,11 @@ void SpotifyClient::OfflineStatusUpdatedCallback(sp_session* session) {
}
}
void SpotifyClient::SendDownloadProgress(
pb::spotify::PlaylistType type, int index, int download_progress) {
void SpotifyClient::SendDownloadProgress(pb::spotify::PlaylistType type,
int index, int download_progress) {
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);
if (index != -1) {
progress->mutable_request()->set_user_playlist_index(index);
@ -762,7 +797,7 @@ void SpotifyClient::SendDownloadProgress(
int SpotifyClient::GetDownloadProgress(sp_playlist* playlist) {
sp_playlist_offline_status status =
sp_playlist_get_offline_status(session_, playlist);
sp_playlist_get_offline_status(session_, playlist);
switch (status) {
case SP_PLAYLIST_OFFLINE_STATUS_NO:
return -1;
@ -864,14 +899,14 @@ void SpotifyClient::LoadImage(const QString& id_b64) {
PendingImageRequest pending_load;
pending_load.id_ = id;
pending_load.id_b64_ = id_b64;
pending_load.image_ = sp_image_create(session_,
reinterpret_cast<const byte*>(id.constData()));
pending_load.image_ =
sp_image_create(session_, reinterpret_cast<const byte*>(id.constData()));
pending_image_requests_ << pending_load;
if (!image_callbacks_registered_[pending_load.image_]) {
sp_image_add_load_callback(pending_load.image_, &ImageLoaded, this);
}
image_callbacks_registered_[pending_load.image_] ++;
image_callbacks_registered_[pending_load.image_]++;
TryImageAgain(pending_load.image_);
}
@ -885,7 +920,7 @@ void SpotifyClient::TryImageAgain(sp_image* image) {
// Find the pending request for this image
int index = -1;
PendingImageRequest* req = nullptr;
for (int i=0 ; i<pending_image_requests_.count() ; ++i) {
for (int i = 0; i < pending_image_requests_.count(); ++i) {
if (pending_image_requests_[i].image_ == image) {
index = i;
req = &pending_image_requests_[i];
@ -912,7 +947,7 @@ void SpotifyClient::TryImageAgain(sp_image* image) {
SendMessage(message);
// Free stuff
image_callbacks_registered_[image] --;
image_callbacks_registered_[image]--;
// TODO: memory leak?
// sp_image_remove_load_callback(image, &ImageLoaded, this);
@ -948,21 +983,22 @@ void SpotifyClient::BrowseAlbum(const QString& 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);
if (!me->pending_album_browses_.contains(result))
return;
if (!me->pending_album_browses_.contains(result)) return;
QString uri = me->pending_album_browses_.take(result);
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));
const int count = sp_albumbrowse_num_tracks(result);
for (int i=0 ; i<count ; ++i) {
for (int i = 0; i < count; ++i) {
me->ConvertTrack(sp_albumbrowse_track(result, i), msg->add_track());
}
@ -970,32 +1006,32 @@ void SpotifyClient::AlbumBrowseComplete(sp_albumbrowse* result, void* userdata)
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(
session_,
SP_TOPLIST_TYPE_TRACKS, // TODO: Support albums and artists.
SP_TOPLIST_REGION_EVERYWHERE, // TODO: Support other regions.
nullptr,
&ToplistBrowseComplete,
this);
session_, SP_TOPLIST_TYPE_TRACKS, // TODO: Support albums and artists.
SP_TOPLIST_REGION_EVERYWHERE, // TODO: Support other regions.
nullptr, &ToplistBrowseComplete, this);
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);
qLog(Debug) << "Toplist browse request took:"
<< sp_toplistbrowse_backend_request_duration(result)
<< "ms";
<< sp_toplistbrowse_backend_request_duration(result) << "ms";
if (!me->pending_toplist_browses_.contains(result)) {
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::BrowseToplistResponse* msg = message.mutable_browse_toplist_response();
pb::spotify::BrowseToplistResponse* msg =
message.mutable_browse_toplist_response();
msg->mutable_request()->CopyFrom(request);
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
// compatible.
#ifndef SPOTIFYCLIENT_H
#define SPOTIFYCLIENT_H
@ -39,7 +38,7 @@ class ResponseMessage;
class SpotifyClient : public AbstractMessageHandler<pb::spotify::Message> {
Q_OBJECT
public:
public:
SpotifyClient(QObject* parent = 0);
~SpotifyClient();
@ -48,14 +47,14 @@ public:
void Init(quint16 port);
protected:
protected:
void MessageArrived(const pb::spotify::Message& message);
void DeviceClosed();
private slots:
private slots:
void ProcessEvents();
private:
private:
void SendLoginCompleted(bool success, const QString& error,
pb::spotify::LoginResponse_Error error_code);
void SendPlaybackError(const QString& error);
@ -64,49 +63,59 @@ private:
// Spotify session callbacks.
static void SP_CALLCONV LoggedInCallback(sp_session* session, sp_error error);
static void SP_CALLCONV NotifyMainThreadCallback(sp_session* session);
static void SP_CALLCONV LogMessageCallback(sp_session* session, const char* data);
static void SP_CALLCONV SearchCompleteCallback(sp_search* result, void* userdata);
static void SP_CALLCONV
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 int SP_CALLCONV MusicDeliveryCallback(
sp_session* session, const sp_audioformat* format,
const void* frames, int num_frames);
static int SP_CALLCONV
MusicDeliveryCallback(sp_session* session, const sp_audioformat* format,
const void* frames, int num_frames);
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 ConnectionErrorCallback(sp_session* session, sp_error error);
static void SP_CALLCONV UserMessageCallback(sp_session* session, const char* message);
static void SP_CALLCONV
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 StopPlaybackCallback(sp_session* session);
// Spotify playlist container callbacks.
static void SP_CALLCONV PlaylistAddedCallback(
sp_playlistcontainer* pc, sp_playlist* playlist,
int position, void* userdata);
static void SP_CALLCONV PlaylistRemovedCallback(
sp_playlistcontainer* pc, sp_playlist* playlist,
int position, void* userdata);
static void SP_CALLCONV PlaylistMovedCallback(
sp_playlistcontainer* pc, sp_playlist* playlist,
int position, int new_position, void* userdata);
static void SP_CALLCONV PlaylistContainerLoadedCallback(
sp_playlistcontainer* pc, void* userdata);
static void SP_CALLCONV PlaylistAddedCallback(sp_playlistcontainer* pc,
sp_playlist* playlist,
int position, void* userdata);
static void SP_CALLCONV PlaylistRemovedCallback(sp_playlistcontainer* pc,
sp_playlist* playlist,
int position, void* userdata);
static void SP_CALLCONV
PlaylistMovedCallback(sp_playlistcontainer* pc, sp_playlist* playlist,
int position, int new_position, void* userdata);
static void SP_CALLCONV
PlaylistContainerLoadedCallback(sp_playlistcontainer* pc, void* userdata);
// Spotify playlist callbacks - when loading the list of playlists
// 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
static void SP_CALLCONV PlaylistStateChangedForLoadPlaylist(sp_playlist* pl, void* userdata);
static void SP_CALLCONV
PlaylistStateChangedForLoadPlaylist(sp_playlist* pl, void* userdata);
// Spotify image callbacks.
static void SP_CALLCONV ImageLoaded(sp_image* image, void* userdata);
// Spotify album browse callbacks.
static void SP_CALLCONV SearchAlbumBrowseComplete(sp_albumbrowse* result, void* userdata);
static void SP_CALLCONV AlbumBrowseComplete(sp_albumbrowse* result, void* userdata);
static void SP_CALLCONV
SearchAlbumBrowseComplete(sp_albumbrowse* result, void* userdata);
static void SP_CALLCONV
AlbumBrowseComplete(sp_albumbrowse* result, void* userdata);
// 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.
void Login(const pb::spotify::LoginRequest& req);
@ -129,7 +138,7 @@ private:
// Gets the appropriate sp_playlist* but does not load it.
sp_playlist* GetPlaylist(pb::spotify::PlaylistType type, int user_index);
private:
private:
struct PendingLoadPlaylist {
pb::spotify::LoadPlaylistRequest request_;
sp_playlist* playlist_;
@ -142,7 +151,7 @@ private:
sp_link* link_;
sp_track* track_;
bool operator ==(const PendingPlaybackRequest& other) const {
bool operator==(const PendingPlaybackRequest& other) const {
return request_.track_uri() == other.request_.track_uri() &&
request_.media_port() == other.request_.media_port();
}
@ -157,7 +166,8 @@ private:
void TryPlaybackAgain(const PendingPlaybackRequest& req);
void TryImageAgain(sp_image* image);
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_;
@ -178,7 +188,8 @@ private:
QMap<sp_image*, int> image_callbacks_registered_;
QMap<sp_search*, pb::spotify::SearchRequest> pending_searches_;
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_albumbrowse*, sp_search*> pending_search_album_browse_responses_;
@ -186,4 +197,4 @@ private:
QScopedPointer<MediaPipeline> media_pipeline_;
};
#endif // SPOTIFYCLIENT_H
#endif // SPOTIFYCLIENT_H

View File

@ -18,16 +18,20 @@
// it is used by the Spotify blob which links against libspotify and is not GPL
// compatible.
// The Spotify terms of service require that application keys are not
// accessible to third parties. Therefore this application key is heavily
// encrypted here in the source to prevent third parties from viewing it.
// It is most definitely not base64 encoded.
static const char* kSpotifyApiKey =
"AVlOrvJkKx8T+LEsCk+Kyl24I0MSsjohZAtMFzm2O5Lms1bmAWFWgdZaHkpypzSJPmSd+Wi50wwg"
"JwVCU0sq4Lep1zB4t6Z8h26NK6+z8gmkHVkV9DRPkRgebcUkWTDTflwVPKWF4+gdRjUwprsqBw6O"
"iofRLJzeKaxbmaUGqkSkxVLOiXC9lxylNq6ju7Q7uY8u8XkDUsVM3YIxiWy2+EM7I/lhatzT9xrq"
"rxHe2lg7CzOwF5kuFdwgmi8MQ72xTYXIKnNlOry/hJDlN9lKxkbUBLh+pzbYvO92S2fYKK5PAHvX"
"5+SmSBGbh6dlpHeCGqb8MPdaeZ5I1YxMcDkxa2+tbLA/Muat7gKA9u57TFCtYjun/u/i/ONwdBIQ"
"rePzXZjipO32kYmQAiCkN1p8sgQEcF43QxaVwXGo2X0rRnJf";
"AVlOrvJkKx8T+LEsCk+Kyl24I0MSsjohZAtMFzm2O5Lms1bmAWFWgdZaHkpypzSJPmSd+"
"Wi50wwg"
"JwVCU0sq4Lep1zB4t6Z8h26NK6+z8gmkHVkV9DRPkRgebcUkWTDTflwVPKWF4+"
"gdRjUwprsqBw6O"
"iofRLJzeKaxbmaUGqkSkxVLOiXC9lxylNq6ju7Q7uY8u8XkDUsVM3YIxiWy2+EM7I/"
"lhatzT9xrq"
"rxHe2lg7CzOwF5kuFdwgmi8MQ72xTYXIKnNlOry/"
"hJDlN9lKxkbUBLh+pzbYvO92S2fYKK5PAHvX"
"5+SmSBGbh6dlpHeCGqb8MPdaeZ5I1YxMcDkxa2+tbLA/Muat7gKA9u57TFCtYjun/u/i/"
"ONwdBIQ"
"rePzXZjipO32kYmQAiCkN1p8sgQEcF43QxaVwXGo2X0rRnJf";

View File

@ -31,15 +31,17 @@ int main(int argc, char** argv) {
QStringList args(a.arguments());
if (args.count() != 2) {
std::cerr << "This program is used internally by Clementine to parse tags in music files\n"
"without exposing the whole application to crashes caused by malformed\n"
std::cerr << "This program is used internally by Clementine to parse tags "
"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";
return 1;
}
// Seed random number generator
timeval time;
gettimeofday(&time,nullptr);
gettimeofday(&time, nullptr);
qsrand((time.tv_sec * 1000) + (time.tv_usec / 1000));
logging::Init();

View File

@ -24,11 +24,8 @@
#include <QTextCodec>
#include <QUrl>
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) {
pb::tagreader::Message reply;
@ -42,42 +39,44 @@ void TagReaderWorker::MessageArrived(const pb::tagreader::Message& message) {
#endif
if (message.has_read_file_request()) {
tag_reader_.ReadFile(QStringFromStdString(message.read_file_request().filename()),
reply.mutable_read_file_response()->mutable_metadata());
tag_reader_.ReadFile(
QStringFromStdString(message.read_file_request().filename()),
reply.mutable_read_file_response()->mutable_metadata());
} else if (message.has_save_file_request()) {
reply.mutable_save_file_response()->set_success(
tag_reader_.SaveFile(QStringFromStdString(message.save_file_request().filename()),
message.save_file_request().metadata()));
reply.mutable_save_file_response()->set_success(tag_reader_.SaveFile(
QStringFromStdString(message.save_file_request().filename()),
message.save_file_request().metadata()));
} else if (message.has_save_song_statistics_to_file_request()) {
reply.mutable_save_song_statistics_to_file_response()->set_success(
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()));
} else if (message.has_save_song_rating_to_file_request()) {
reply.mutable_save_song_rating_to_file_response()->set_success(
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()));
} else if (message.has_is_media_file_request()) {
reply.mutable_is_media_file_response()->set_success(
tag_reader_.IsMediaFile(QStringFromStdString(message.is_media_file_request().filename())));
reply.mutable_is_media_file_response()->set_success(tag_reader_.IsMediaFile(
QStringFromStdString(message.is_media_file_request().filename())));
} else if (message.has_load_embedded_art_request()) {
QByteArray data = tag_reader_.LoadEmbeddedArt(
QStringFromStdString(message.load_embedded_art_request().filename()));
reply.mutable_load_embedded_art_response()->set_data(
data.constData(), data.size());
QStringFromStdString(message.load_embedded_art_request().filename()));
reply.mutable_load_embedded_art_response()->set_data(data.constData(),
data.size());
} else if (message.has_read_cloud_file_request()) {
#ifdef HAVE_GOOGLE_DRIVE
const pb::tagreader::ReadCloudFileRequest& req =
message.read_cloud_file_request();
if (!tag_reader_.ReadCloudFile(
QUrl::fromEncoded(QByteArray(req.download_url().data(),
req.download_url().size())),
QStringFromStdString(req.title()),
req.size(),
QStringFromStdString(req.mime_type()),
QStringFromStdString(req.authorisation_header()),
reply.mutable_read_cloud_file_response()->mutable_metadata())) {
QUrl::fromEncoded(QByteArray(req.download_url().data(),
req.download_url().size())),
QStringFromStdString(req.title()), req.size(),
QStringFromStdString(req.mime_type()),
QStringFromStdString(req.authorisation_header()),
reply.mutable_read_cloud_file_response()->mutable_metadata())) {
reply.mutable_read_cloud_file_response()->clear_metadata();
}
#endif
@ -86,10 +85,8 @@ void TagReaderWorker::MessageArrived(const pb::tagreader::Message& message) {
SendReply(message, &reply);
}
void TagReaderWorker::DeviceClosed() {
AbstractMessageHandler<pb::tagreader::Message>::DeviceClosed();
qApp->exit();
}

View File

@ -24,15 +24,15 @@
#include "core/messagehandler.h"
class TagReaderWorker : public AbstractMessageHandler<pb::tagreader::Message> {
public:
public:
TagReaderWorker(QIODevice* socket, QObject* parent = NULL);
protected:
protected:
void MessageArrived(const pb::tagreader::Message& message);
void DeviceClosed();
private:
private:
TagReader tag_reader_;
};
#endif // TAGREADERWORKER_H
#endif // TAGREADERWORKER_H

View File

@ -23,34 +23,22 @@
namespace _detail {
ClosureBase::ClosureBase(ObjectHelper* helper)
: helper_(helper) {
}
ClosureBase::ClosureBase(ObjectHelper* helper) : helper_(helper) {}
ClosureBase::~ClosureBase() {
}
ClosureBase::~ClosureBase() {}
CallbackClosure::CallbackClosure(
QObject* sender,
const char* signal,
std::function<void()> callback)
: ClosureBase(new ObjectHelper(sender, signal, this)),
callback_(callback) {
}
CallbackClosure::CallbackClosure(QObject* sender, const char* signal,
std::function<void()> callback)
: ClosureBase(new ObjectHelper(sender, signal, this)),
callback_(callback) {}
void CallbackClosure::Invoke() {
callback_();
}
void CallbackClosure::Invoke() { callback_(); }
ObjectHelper* ClosureBase::helper() const {
return helper_;
}
ObjectHelper* ClosureBase::helper() const { return helper_; }
ObjectHelper::ObjectHelper(
QObject* sender,
const char* signal,
ClosureBase* closure)
: closure_(closure) {
ObjectHelper::ObjectHelper(QObject* sender, const char* signal,
ClosureBase* closure)
: closure_(closure) {
connect(sender, signal, SLOT(Invoked()));
connect(sender, SIGNAL(destroyed()), SLOT(deleteLater()));
}
@ -64,12 +52,9 @@ void Unpack(QList<QGenericArgument>*) {}
} // namespace _detail
_detail::ClosureBase* NewClosure(
QObject* sender,
const char* signal,
std::function<void()> callback) {
return new _detail::CallbackClosure(
sender, signal, callback);
_detail::ClosureBase* NewClosure(QObject* sender, const char* signal,
std::function<void()> callback) {
return new _detail::CallbackClosure(sender, signal, callback);
}
void DoAfter(QObject* receiver, const char* slot, int msec) {

View File

@ -52,10 +52,7 @@ class ClosureBase {
class ObjectHelper : public QObject {
Q_OBJECT
public:
ObjectHelper(
QObject* parent,
const char* signal,
ClosureBase* closure);
ObjectHelper(QObject* parent, const char* signal, ClosureBase* closure);
private slots:
void Invoked();
@ -76,7 +73,8 @@ void Unpack(QList<QGenericArgument>* list, const Arg& arg) {
}
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, tail...);
}
@ -84,45 +82,39 @@ void Unpack(QList<QGenericArgument>* list, const Head& head, const Tail&... tail
template <typename... Args>
class Closure : public ClosureBase {
public:
Closure(
QObject* sender,
const char* signal,
QObject* receiver,
const char* slot,
const Args&... args)
: ClosureBase(new ObjectHelper(sender, signal, this)),
// std::bind is the easiest way to store an argument list.
function_(std::bind(&Closure<Args...>::Call, this, args...)),
receiver_(receiver) {
Closure(QObject* sender, const char* signal, QObject* receiver,
const char* slot, const Args&... args)
: ClosureBase(new ObjectHelper(sender, signal, this)),
// std::bind is the easiest way to store an argument list.
function_(std::bind(&Closure<Args...>::Call, this, args...)),
receiver_(receiver) {
const QMetaObject* meta_receiver = receiver->metaObject();
QByteArray normalised_slot = QMetaObject::normalizedSignature(slot + 1);
const int index = meta_receiver->indexOfSlot(normalised_slot.constData());
Q_ASSERT(index != -1);
slot_ = meta_receiver->method(index);
QObject::connect(receiver_, SIGNAL(destroyed()), helper_, SLOT(deleteLater()));
QObject::connect(receiver_, SIGNAL(destroyed()), helper_,
SLOT(deleteLater()));
}
virtual void Invoke() {
function_();
}
virtual void Invoke() { function_(); }
private:
void Call(const Args&... args) {
QList<QGenericArgument> arg_list;
Unpack(&arg_list, args...);
slot_.invoke(
receiver_,
arg_list.size() > 0 ? arg_list[0] : QGenericArgument(),
arg_list.size() > 1 ? arg_list[1] : QGenericArgument(),
arg_list.size() > 2 ? arg_list[2] : QGenericArgument(),
arg_list.size() > 3 ? arg_list[3] : QGenericArgument(),
arg_list.size() > 4 ? arg_list[4] : QGenericArgument(),
arg_list.size() > 5 ? arg_list[5] : QGenericArgument(),
arg_list.size() > 6 ? arg_list[6] : QGenericArgument(),
arg_list.size() > 7 ? arg_list[7] : QGenericArgument(),
arg_list.size() > 8 ? arg_list[8] : QGenericArgument(),
arg_list.size() > 9 ? arg_list[9] : QGenericArgument());
slot_.invoke(receiver_,
arg_list.size() > 0 ? arg_list[0] : QGenericArgument(),
arg_list.size() > 1 ? arg_list[1] : QGenericArgument(),
arg_list.size() > 2 ? arg_list[2] : QGenericArgument(),
arg_list.size() > 3 ? arg_list[3] : QGenericArgument(),
arg_list.size() > 4 ? arg_list[4] : QGenericArgument(),
arg_list.size() > 5 ? arg_list[5] : QGenericArgument(),
arg_list.size() > 6 ? arg_list[6] : QGenericArgument(),
arg_list.size() > 7 ? arg_list[7] : QGenericArgument(),
arg_list.size() > 8 ? arg_list[8] : QGenericArgument(),
arg_list.size() > 9 ? arg_list[9] : QGenericArgument());
}
std::function<void()> function_;
@ -133,20 +125,10 @@ class Closure : public ClosureBase {
template <typename T, typename... Args>
class SharedClosure : public Closure<Args...> {
public:
SharedClosure(
QSharedPointer<T> sender,
const char* signal,
QObject* receiver,
const char* slot,
const Args&... args)
: Closure<Args...>(
sender.data(),
signal,
receiver,
slot,
args...),
data_(sender) {
}
SharedClosure(QSharedPointer<T> sender, const char* signal, QObject* receiver,
const char* slot, const Args&... args)
: Closure<Args...>(sender.data(), signal, receiver, slot, args...),
data_(sender) {}
private:
QSharedPointer<T> data_;
@ -154,10 +136,8 @@ class SharedClosure : public Closure<Args...> {
class CallbackClosure : public ClosureBase {
public:
CallbackClosure(
QObject* sender,
const char* signal,
std::function<void()> callback);
CallbackClosure(QObject* sender, const char* signal,
std::function<void()> callback);
virtual void Invoke();
@ -168,61 +148,45 @@ class CallbackClosure : public ClosureBase {
} // namespace _detail
template <typename... Args>
_detail::ClosureBase* NewClosure(
QObject* sender,
const char* signal,
QObject* receiver,
const char* slot,
const Args&... args) {
return new _detail::Closure<Args...>(
sender, signal, receiver, slot, args...);
_detail::ClosureBase* NewClosure(QObject* sender, const char* signal,
QObject* receiver, const char* slot,
const Args&... args) {
return new _detail::Closure<Args...>(sender, signal, receiver, slot, args...);
}
// QSharedPointer variant
template <typename T, typename... Args>
_detail::ClosureBase* NewClosure(
QSharedPointer<T> sender,
const char* signal,
QObject* receiver,
const char* slot,
const Args&... args) {
return new _detail::SharedClosure<T, Args...>(
sender, signal, receiver, slot, args...);
_detail::ClosureBase* NewClosure(QSharedPointer<T> sender, const char* signal,
QObject* receiver, const char* slot,
const Args&... args) {
return new _detail::SharedClosure<T, Args...>(sender, signal, receiver, slot,
args...);
}
_detail::ClosureBase* NewClosure(
QObject* sender,
const char* signal,
std::function<void()> callback);
_detail::ClosureBase* NewClosure(QObject* sender, const char* signal,
std::function<void()> callback);
template <typename... Args>
_detail::ClosureBase* NewClosure(
QObject* sender,
const char* signal,
std::function<void(Args...)> callback,
const Args&... args) {
_detail::ClosureBase* NewClosure(QObject* sender, const char* signal,
std::function<void(Args...)> callback,
const Args&... args) {
return NewClosure(sender, signal, std::bind(callback, args...));
}
template <typename... Args>
_detail::ClosureBase* NewClosure(
QObject* sender,
const char* signal,
void (*callback)(Args...),
const Args&... args) {
_detail::ClosureBase* NewClosure(QObject* sender, const char* signal,
void (*callback)(Args...),
const Args&... args) {
return NewClosure(sender, signal, std::bind(callback, args...));
}
template <typename T, typename Unused, typename... Args>
_detail::ClosureBase* NewClosure(
QObject* sender,
const char* signal,
T* receiver, Unused (T::*callback)(Args...),
const Args&... args) {
_detail::ClosureBase* NewClosure(QObject* sender, const char* signal,
T* receiver, Unused (T::*callback)(Args...),
const Args&... args) {
return NewClosure(sender, signal, std::bind(callback, receiver, args...));
}
void DoAfter(QObject* receiver, const char* slot, int msec);
void DoInAMinuteOrSo(QObject* receiver, const char* slot);

View File

@ -41,13 +41,13 @@
ThreadFunctor object and start it.
*/
/*
Base abstract classes ThreadFunctorBase and ThreadFunctor (for void and
non-void result):
*/
template<typename ReturnType>
class ThreadFunctorBase : public QFutureInterface<ReturnType>, public QRunnable {
template <typename ReturnType>
class ThreadFunctorBase : public QFutureInterface<ReturnType>,
public QRunnable {
public:
ThreadFunctorBase() {}
@ -68,10 +68,8 @@ class ThreadFunctorBase : public QFutureInterface<ReturnType>, public QRunnable
template <typename ReturnType, typename... Args>
class ThreadFunctor : public ThreadFunctorBase<ReturnType> {
public:
ThreadFunctor(std::function<ReturnType (Args...)> function,
Args... args)
: function_(std::bind(function, args...)) {
}
ThreadFunctor(std::function<ReturnType(Args...)> function, Args... args)
: function_(std::bind(function, args...)) {}
virtual void run() {
this->reportResult(function_());
@ -84,12 +82,10 @@ class ThreadFunctor : public ThreadFunctorBase<ReturnType> {
// Partial specialisation for void return type.
template <typename... Args>
class ThreadFunctor <void, Args...> : public ThreadFunctorBase<void> {
class ThreadFunctor<void, Args...> : public ThreadFunctorBase<void> {
public:
ThreadFunctor(std::function<void (Args...)> function,
Args... args)
: function_(std::bind(function, args...)) {
}
ThreadFunctor(std::function<void(Args...)> function, Args... args)
: function_(std::bind(function, args...)) {}
virtual void run() {
function_();
@ -100,39 +96,33 @@ class ThreadFunctor <void, Args...> : public ThreadFunctorBase<void> {
std::function<void()> function_;
};
/*
Run functions
*/
namespace ConcurrentRun {
// Empty argument form.
template <typename ReturnType>
QFuture<ReturnType> Run(
QThreadPool* threadpool,
std::function<ReturnType ()> function) {
return (new ThreadFunctor<ReturnType>(function))->Start(threadpool);
}
// Function object with arguments form.
template <typename ReturnType, typename... Args>
QFuture<ReturnType> Run(
QThreadPool* threadpool,
std::function<ReturnType (Args...)> function,
const Args&... args) {
return (new ThreadFunctor<ReturnType, Args...>(
function, args...))->Start(threadpool);
}
// Support passing C function pointers instead of function objects.
template <typename ReturnType, typename... Args>
QFuture<ReturnType> Run(
QThreadPool* threadpool,
ReturnType (*function) (Args...),
const Args&... args) {
return Run(
threadpool, std::function<ReturnType (Args...)>(function), args...);
}
// Empty argument form.
template <typename ReturnType>
QFuture<ReturnType> Run(QThreadPool* threadpool,
std::function<ReturnType()> function) {
return (new ThreadFunctor<ReturnType>(function))->Start(threadpool);
}
#endif // CONCURRENTRUN_H
// Function object with arguments form.
template <typename ReturnType, typename... Args>
QFuture<ReturnType> Run(QThreadPool* threadpool,
std::function<ReturnType(Args...)> function,
const Args&... args) {
return (new ThreadFunctor<ReturnType, Args...>(function, args...))
->Start(threadpool);
}
// Support passing C function pointers instead of function objects.
template <typename ReturnType, typename... Args>
QFuture<ReturnType> Run(QThreadPool* threadpool,
ReturnType (*function)(Args...), const Args&... args) {
return Run(threadpool, std::function<ReturnType(Args...)>(function), args...);
}
}
#endif // CONCURRENTRUN_H

View File

@ -33,7 +33,6 @@
#include "logging.h"
namespace logging {
static Level sDefaultLevel = Level_Debug;
@ -46,18 +45,25 @@ static const char* kMessageHandlerMagic = "__logging_message__";
static const int kMessageHandlerMagicLength = strlen(kMessageHandlerMagic);
static QtMsgHandler sOriginalMessageHandler = nullptr;
void GLog(const char* domain, int level, const char* message, void* user_data) {
switch (level) {
case G_LOG_FLAG_RECURSION:
case G_LOG_FLAG_FATAL:
case G_LOG_LEVEL_ERROR:
case G_LOG_LEVEL_CRITICAL: qLog(Error) << message; break;
case G_LOG_LEVEL_WARNING: qLog(Warning) << message; break;
case G_LOG_LEVEL_CRITICAL:
qLog(Error) << message;
break;
case G_LOG_LEVEL_WARNING:
qLog(Warning) << message;
break;
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:
default: qLog(Debug) << message; break;
default:
qLog(Debug) << message;
break;
}
}
@ -70,13 +76,19 @@ static void MessageHandler(QtMsgType type, const char* message) {
Level level = Level_Debug;
switch (type) {
case QtFatalMsg:
case QtCriticalMsg: level = Level_Error; break;
case QtWarningMsg: level = Level_Warning; break;
case QtCriticalMsg:
level = Level_Error;
break;
case QtWarningMsg:
level = Level_Warning;
break;
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')) {
CreateLogger(level, "unknown", -1) << line.toLocal8Bit().constData();
}
@ -85,7 +97,6 @@ static void MessageHandler(QtMsgType type, const char* message) {
}
}
void Init() {
delete sClassLevels;
delete sNullDevice;
@ -100,10 +111,9 @@ void Init() {
}
void SetLevels(const QString& levels) {
if (!sClassLevels)
return;
if (!sClassLevels) return;
foreach (const QString& item, levels.split(',')) {
foreach(const QString & item, levels.split(',')) {
const QStringList class_level = item.split(':');
QString class_name;
@ -122,14 +132,14 @@ void SetLevels(const QString& levels) {
}
if (class_name.isEmpty() || class_name == "*") {
sDefaultLevel = (Level) level;
sDefaultLevel = (Level)level;
} else {
sClassLevels->insert(class_name, (Level) level);
sClassLevels->insert(class_name, (Level)level);
}
}
}
QString ParsePrettyFunction(const char * pretty_function) {
QString ParsePrettyFunction(const char* pretty_function) {
// Get the class name out of the function name.
QString class_name = pretty_function;
const int paren = class_name.indexOf('(');
@ -144,7 +154,7 @@ QString ParsePrettyFunction(const char * pretty_function) {
const int space = class_name.lastIndexOf(' ');
if (space != -1) {
class_name = class_name.mid(space+1);
class_name = class_name.mid(space + 1);
}
return class_name;
@ -154,11 +164,21 @@ QDebug CreateLogger(Level level, const QString& class_name, int line) {
// Map the level to a string
const char* level_name = nullptr;
switch (level) {
case Level_Debug: level_name = " DEBUG "; break;
case Level_Info: 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;
case Level_Debug:
level_name = " DEBUG ";
break;
case Level_Info:
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.
@ -182,9 +202,11 @@ QDebug CreateLogger(Level level, const QString& class_name, int line) {
}
QDebug ret(type);
ret.nospace() << kMessageHandlerMagic
<< QDateTime::currentDateTime().toString("hh:mm:ss.zzz").toAscii().constData()
<< level_name << function_line.leftJustified(32).toAscii().constData();
ret.nospace() << kMessageHandlerMagic << QDateTime::currentDateTime()
.toString("hh:mm:ss.zzz")
.toAscii()
.constData() << level_name
<< function_line.leftJustified(32).toAscii().constData();
return ret.space();
}
@ -192,10 +214,7 @@ QDebug CreateLogger(Level level, const QString& class_name, int line) {
QString CXXDemangle(const QString& mangled_function) {
int status;
char* demangled_function = abi::__cxa_demangle(
mangled_function.toAscii().constData(),
nullptr,
nullptr,
&status);
mangled_function.toAscii().constData(), nullptr, nullptr, &status);
if (status == 0) {
QString ret = QString::fromAscii(demangled_function);
free(demangled_function);
@ -232,8 +251,10 @@ QString DemangleSymbol(const QString& symbol) {
void DumpStackTrace() {
#ifdef Q_OS_UNIX
void* callstack[128];
int callstack_size = backtrace(reinterpret_cast<void**>(&callstack), sizeof(callstack));
char** symbols = backtrace_symbols(reinterpret_cast<void**>(&callstack), callstack_size);
int 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.
for (int i = 1; i < callstack_size; ++i) {
qLog(Debug) << DemangleSymbol(QString::fromAscii(symbols[i]));
@ -244,4 +265,4 @@ void DumpStackTrace() {
#endif
}
} // namespace logging
} // namespace logging

View File

@ -18,46 +18,47 @@
// it is used by the Spotify blob which links against libspotify and is not GPL
// compatible.
#ifndef LOGGING_H
#define LOGGING_H
#include <QDebug>
#ifdef QT_NO_DEBUG_STREAM
# define qLog(level) while (false) QNoDebug()
#define qLog(level) \
while (false) QNoDebug()
#else
# define qLog(level) \
logging::CreateLogger(logging::Level_##level, \
logging::ParsePrettyFunction(__PRETTY_FUNCTION__), __LINE__)
#define qLog(level) \
logging::CreateLogger(logging::Level_##level, \
logging::ParsePrettyFunction(__PRETTY_FUNCTION__), \
__LINE__)
#endif
namespace logging {
class NullDevice : public QIODevice {
protected:
qint64 readData(char*, qint64) { return -1; }
qint64 writeData(const char*, qint64 len) { return len; }
};
class NullDevice : public QIODevice {
protected:
qint64 readData(char*, qint64) { return -1; }
qint64 writeData(const char*, qint64 len) { return len; }
};
enum Level {
Level_Fatal = -1,
Level_Error = 0,
Level_Warning,
Level_Info,
Level_Debug,
};
enum Level {
Level_Fatal = -1,
Level_Error = 0,
Level_Warning,
Level_Info,
Level_Debug,
};
void Init();
void SetLevels(const QString& levels);
void Init();
void SetLevels(const QString& levels);
void DumpStackTrace();
void DumpStackTrace();
QString ParsePrettyFunction(const char* pretty_function);
QDebug CreateLogger(Level level, const QString& class_name, int line);
QString ParsePrettyFunction(const char* pretty_function);
QDebug CreateLogger(Level level, const QString& class_name, int line);
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);
extern const char* kDefaultLogLevels;
extern const char* kDefaultLogLevels;
}
#endif // LOGGING_H
#endif // LOGGING_H

View File

@ -18,7 +18,6 @@
// it is used by the Spotify blob which links against libspotify and is not GPL
// compatible.
#include "messagehandler.h"
#include "core/logging.h"
@ -26,13 +25,13 @@
#include <QLocalSocket>
_MessageHandlerBase::_MessageHandlerBase(QIODevice* device, QObject* parent)
: QObject(parent),
device_(nullptr),
flush_abstract_socket_(nullptr),
flush_local_socket_(nullptr),
reading_protobuf_(false),
expected_length_(0),
is_device_closed_(false) {
: QObject(parent),
device_(nullptr),
flush_abstract_socket_(nullptr),
flush_local_socket_(nullptr),
reading_protobuf_(false),
expected_length_(0),
is_device_closed_(false) {
if (device) {
SetDevice(device);
}

View File

@ -18,7 +18,6 @@
// it is used by the Spotify blob which links against libspotify and is not GPL
// compatible.
#ifndef MESSAGEHANDLER_H
#define MESSAGEHANDLER_H
@ -37,11 +36,8 @@ class QAbstractSocket;
class QIODevice;
class QLocalSocket;
#define QStringFromStdString(x) \
QString::fromUtf8(x.data(), x.size())
#define DataCommaSizeFromQString(x) \
x.toUtf8().constData(), x.toUtf8().length()
#define QStringFromStdString(x) QString::fromUtf8(x.data(), x.size())
#define DataCommaSizeFromQString(x) x.toUtf8().constData(), x.toUtf8().length()
// Reads and writes uint32 length encoded protobufs to a socket.
// This base QObject is separate from AbstractMessageHandler because moc can't
@ -49,7 +45,7 @@ class QLocalSocket;
class _MessageHandlerBase : public QObject {
Q_OBJECT
public:
public:
// device can be NULL, in which case you must call SetDevice before writing
// any messages.
_MessageHandlerBase(QIODevice* device, QObject* parent);
@ -59,16 +55,16 @@ public:
// After this is true, messages cannot be sent to the handler any more.
bool is_device_closed() const { return is_device_closed_; }
protected slots:
protected slots:
void WriteMessage(const QByteArray& data);
void DeviceReadyRead();
virtual void DeviceClosed();
protected:
protected:
virtual bool RawMessageArrived(const QByteArray& data) = 0;
virtual void AbortAll() = 0;
protected:
protected:
typedef bool (QAbstractSocket::*FlushAbstractSocket)();
typedef bool (QLocalSocket::*FlushLocalSocket)();
@ -83,13 +79,12 @@ protected:
bool is_device_closed_;
};
// Reads and writes uint32 length encoded MessageType messages to a socket.
// You should subclass this and implement the MessageArrived(MessageType)
// method.
template <typename MT>
class AbstractMessageHandler : public _MessageHandlerBase {
public:
public:
AbstractMessageHandler(QIODevice* device, QObject* parent);
~AbstractMessageHandler() { AbortAll(); }
@ -113,7 +108,7 @@ public:
// reply on the socket. Used on the worker side.
void SendReply(const MessageType& request, MessageType* reply);
protected:
protected:
// Called when a message is received from the socket.
virtual void MessageArrived(const MessageType& message) {}
@ -121,19 +116,16 @@ protected:
bool RawMessageArrived(const QByteArray& data);
void AbortAll();
private:
private:
QMap<int, ReplyType*> pending_replies_;
};
template <typename MT>
AbstractMessageHandler<MT>::AbstractMessageHandler(QIODevice* device,
QObject* parent)
: _MessageHandlerBase(device, parent) {}
template<typename MT>
AbstractMessageHandler<MT>::AbstractMessageHandler(
QIODevice* device, QObject* parent)
: _MessageHandlerBase(device, parent)
{
}
template<typename MT>
template <typename MT>
void AbstractMessageHandler<MT>::SendMessage(const MessageType& message) {
Q_ASSERT(QThread::currentThread() == thread());
@ -141,27 +133,28 @@ void AbstractMessageHandler<MT>::SendMessage(const MessageType& message) {
WriteMessage(QByteArray(data.data(), data.size()));
}
template<typename MT>
template <typename MT>
void AbstractMessageHandler<MT>::SendMessageAsync(const MessageType& message) {
std::string data = message.SerializeAsString();
metaObject()->invokeMethod(this, "WriteMessage", Qt::QueuedConnection,
Q_ARG(QByteArray, QByteArray(data.data(), data.size())));
metaObject()->invokeMethod(
this, "WriteMessage", Qt::QueuedConnection,
Q_ARG(QByteArray, QByteArray(data.data(), data.size())));
}
template<typename MT>
template <typename MT>
void AbstractMessageHandler<MT>::SendRequest(ReplyType* reply) {
pending_replies_[reply->id()] = reply;
SendMessage(reply->request_message());
}
template<typename MT>
template <typename MT>
void AbstractMessageHandler<MT>::SendReply(const MessageType& request,
MessageType* reply) {
reply->set_id(request.id());
SendMessage(*reply);
}
template<typename MT>
template <typename MT>
bool AbstractMessageHandler<MT>::RawMessageArrived(const QByteArray& data) {
MessageType message;
if (!message.ParseFromArray(data.constData(), data.size())) {
@ -180,14 +173,10 @@ bool AbstractMessageHandler<MT>::RawMessageArrived(const QByteArray& data) {
return true;
}
template<typename MT>
template <typename MT>
void AbstractMessageHandler<MT>::AbortAll() {
foreach (ReplyType* reply, pending_replies_) {
reply->Abort();
}
foreach(ReplyType * reply, pending_replies_) { reply->Abort(); }
pending_replies_.clear();
}
#endif // MESSAGEHANDLER_H
#endif // MESSAGEHANDLER_H

View File

@ -18,11 +18,7 @@
#include "messagereply.h"
_MessageReplyBase::_MessageReplyBase(QObject* parent)
: QObject(parent),
finished_(false),
success_(false)
{
}
: QObject(parent), finished_(false), success_(false) {}
bool _MessageReplyBase::WaitForFinished() {
qLog(Debug) << "Waiting on ID" << id();

View File

@ -29,7 +29,7 @@
class _MessageReplyBase : public QObject {
Q_OBJECT
public:
public:
_MessageReplyBase(QObject* parent = 0);
virtual int id() const = 0;
@ -46,19 +46,18 @@ public:
signals:
void Finished(bool success);
protected:
protected:
bool finished_;
bool success_;
QSemaphore semaphore_;
};
// A reply future class that is returned immediately for requests that will
// occur in the background. Similar to QNetworkReply.
template <typename MessageType>
class MessageReply : public _MessageReplyBase {
public:
public:
MessageReply(const MessageType& request_message, QObject* parent = 0);
int id() const { return request_message_.id(); }
@ -67,21 +66,19 @@ public:
void SetReply(const MessageType& message);
private:
private:
MessageType request_message_;
MessageType reply_message_;
};
template<typename MessageType>
template <typename MessageType>
MessageReply<MessageType>::MessageReply(const MessageType& request_message,
QObject* parent)
: _MessageReplyBase(parent)
{
: _MessageReplyBase(parent) {
request_message_.MergeFrom(request_message);
}
template<typename MessageType>
template <typename MessageType>
void MessageReply<MessageType>::SetReply(const MessageType& message) {
Q_ASSERT(!finished_);
@ -94,4 +91,4 @@ void MessageReply<MessageType>::SetReply(const MessageType& message) {
semaphore_.release();
}
#endif // MESSAGEREPLY_H
#endif // MESSAGEREPLY_H

View File

@ -18,7 +18,6 @@
// it is used by the Spotify blob which links against libspotify and is not GPL
// compatible.
#ifndef OVERRIDE_H
#define OVERRIDE_H
@ -26,13 +25,13 @@
// it is available.
#ifndef __has_extension
#define __has_extension(x) 0
#define __has_extension(x) 0
#endif
#if __has_extension(cxx_override_control) // Clang feature checking macro.
# define OVERRIDE override
#define OVERRIDE override
#else
# define OVERRIDE
#define OVERRIDE
#endif
#endif // OVERRIDE_H

View File

@ -22,4 +22,4 @@ class QObject;
void WaitForSignal(QObject* sender, const char* signal);
#endif // WAITFORSIGNAL_H
#endif // WAITFORSIGNAL_H

View File

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

View File

@ -32,13 +32,12 @@
#include "core/closure.h"
#include "core/logging.h"
// Base class containing signals and slots - required because moc doesn't do
// templated objects.
class _WorkerPoolBase : public QObject {
Q_OBJECT
public:
public:
_WorkerPoolBase(QObject* parent = 0);
signals:
@ -46,14 +45,13 @@ signals:
// worker wasn't found, or couldn't be executed.
void WorkerFailedToStart();
protected slots:
protected slots:
virtual void DoStart() {}
virtual void NewConnection() {}
virtual void ProcessError(QProcess::ProcessError) {}
virtual void SendQueuedMessages() {}
};
// 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
// argv[1]. The process is expected to connect back to the socket server, and
@ -61,7 +59,7 @@ protected slots:
// Instances of HandlerType are created in the WorkerPool's thread.
template <typename HandlerType>
class WorkerPool : public _WorkerPoolBase {
public:
public:
WorkerPool(QObject* parent = 0);
~WorkerPool();
@ -90,7 +88,7 @@ public:
// worker. Can be called from any thread.
ReplyType* SendMessageWithReply(MessageType* message);
protected:
protected:
// These are all reimplemented slots, they are called on the WorkerPool's
// thread.
void DoStart();
@ -98,10 +96,13 @@ protected:
void ProcessError(QProcess::ProcessError error);
void SendQueuedMessages();
private:
private:
struct Worker {
Worker() : local_server_(NULL), local_socket_(NULL), process_(NULL),
handler_(NULL) {}
Worker()
: local_server_(NULL),
local_socket_(NULL),
process_(NULL),
handler_(NULL) {}
QLocalServer* local_server_;
QLocalSocket* local_socket_;
@ -114,8 +115,8 @@ private:
template <typename T>
Worker* FindWorker(T Worker::*member, T value) {
for (typename QList<Worker>::iterator it = workers_.begin() ;
it != workers_.end() ; ++it) {
for (typename QList<Worker>::iterator it = workers_.begin();
it != workers_.end(); ++it) {
if ((*it).*member == value) {
return &(*it);
}
@ -140,7 +141,7 @@ private:
// my thread.
HandlerType* NextHandler() const;
private:
private:
QString local_server_name_;
QString executable_name_;
QString executable_path_;
@ -155,26 +156,21 @@ private:
QQueue<ReplyType*> message_queue_;
};
template <typename HandlerType>
WorkerPool<HandlerType>::WorkerPool(QObject* parent)
: _WorkerPoolBase(parent),
next_worker_(0),
next_id_(0)
{
: _WorkerPoolBase(parent), next_worker_(0), next_id_(0) {
worker_count_ = qBound(1, QThread::idealThreadCount() / 2, 2);
local_server_name_ = qApp->applicationName().toLower();
if (local_server_name_.isEmpty())
local_server_name_ = "workerpool";
if (local_server_name_.isEmpty()) local_server_name_ = "workerpool";
}
template <typename HandlerType>
WorkerPool<HandlerType>::~WorkerPool() {
foreach (const Worker& worker, workers_) {
foreach(const Worker & worker, workers_) {
if (worker.local_socket_ && worker.process_) {
disconnect(worker.process_, SIGNAL(error(QProcess::ProcessError)),
this, SLOT(ProcessError(QProcess::ProcessError)));
disconnect(worker.process_, SIGNAL(error(QProcess::ProcessError)), this,
SLOT(ProcessError(QProcess::ProcessError)));
// The worker is connected. Close his socket and wait for him to exit.
qLog(Debug) << "Closing worker socket";
@ -192,9 +188,7 @@ WorkerPool<HandlerType>::~WorkerPool() {
}
}
foreach (ReplyType* reply, message_queue_) {
reply->Abort();
}
foreach(ReplyType * reply, message_queue_) { reply->Abort(); }
}
template <typename HandlerType>
@ -204,13 +198,15 @@ void WorkerPool<HandlerType>::SetWorkerCount(int count) {
}
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());
local_server_name_ = local_server_name;
}
template <typename HandlerType>
void WorkerPool<HandlerType>::SetExecutableName(const QString& executable_name) {
void WorkerPool<HandlerType>::SetExecutableName(
const QString& executable_name) {
Q_ASSERT(workers_.isEmpty());
executable_name_ = executable_name;
}
@ -235,7 +231,7 @@ void WorkerPool<HandlerType>::DoStart() {
search_path << qApp->applicationDirPath() + "/../PlugIns";
#endif
foreach (const QString& path_prefix, search_path) {
foreach(const QString & path_prefix, search_path) {
const QString executable_path = path_prefix + "/" + executable_name_;
if (QFile::exists(executable_path)) {
executable_path_ = executable_path;
@ -244,7 +240,7 @@ void WorkerPool<HandlerType>::DoStart() {
}
// Start all the workers
for (int i=0 ; i<worker_count_ ; ++i) {
for (int i = 0; i < worker_count_; ++i) {
Worker worker;
StartOneWorker(&worker);
@ -264,14 +260,16 @@ void WorkerPool<HandlerType>::StartOneWorker(Worker* worker) {
worker->local_server_ = new QLocalServer(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)),
SLOT(ProcessError(QProcess::ProcessError)));
// Create a server, find an unused name and start listening
forever {
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)) {
break;
@ -284,7 +282,8 @@ void WorkerPool<HandlerType>::StartOneWorker(Worker* worker) {
// Start the process
worker->process_->setProcessChannelMode(QProcess::ForwardedChannels);
worker->process_->start(executable_path_,
QStringList() << worker->local_server_->fullServerName());
QStringList()
<< worker->local_server_->fullServerName());
}
template <typename HandlerType>
@ -295,10 +294,10 @@ void WorkerPool<HandlerType>::NewConnection() {
// Find the worker with this server.
Worker* worker = FindWorker(&Worker::local_server_, server);
if (!worker)
return;
if (!worker) return;
qLog(Debug) << "Worker" << worker << "connected to" << server->fullServerName();
qLog(Debug) << "Worker" << worker << "connected to"
<< server->fullServerName();
// Accept the connection.
worker->local_socket_ = server->nextPendingConnection();
@ -322,29 +321,29 @@ void WorkerPool<HandlerType>::ProcessError(QProcess::ProcessError error) {
// Find the worker with this process.
Worker* worker = FindWorker(&Worker::process_, process);
if (!worker)
return;
if (!worker) return;
switch (error) {
case QProcess::FailedToStart:
// Failed to start errors are bad - it usually means the worker isn't
// installed. Don't restart the process, but tell our owner, who will
// probably want to do something fatal.
qLog(Error) << "Worker failed to start";
emit WorkerFailedToStart();
break;
case QProcess::FailedToStart:
// Failed to start errors are bad - it usually means the worker isn't
// installed. Don't restart the process, but tell our owner, who will
// probably want to do something fatal.
qLog(Error) << "Worker failed to start";
emit WorkerFailedToStart();
break;
default:
// On any other error we just restart the process.
qLog(Debug) << "Worker" << worker << "failed with error" << error << "- restarting";
StartOneWorker(worker);
break;
default:
// On any other error we just restart the process.
qLog(Debug) << "Worker" << worker << "failed with error" << error
<< "- restarting";
StartOneWorker(worker);
break;
}
}
template <typename HandlerType>
typename WorkerPool<HandlerType>::ReplyType*
WorkerPool<HandlerType>::NewReply(MessageType* message) {
typename WorkerPool<HandlerType>::ReplyType* WorkerPool<HandlerType>::NewReply(
MessageType* message) {
const int id = next_id_.fetchAndAddOrdered(1);
message->set_id(id);
@ -390,7 +389,7 @@ void WorkerPool<HandlerType>::SendQueuedMessages() {
template <typename HandlerType>
HandlerType* WorkerPool<HandlerType>::NextHandler() const {
for (int i=0 ; i<workers_.count() ; ++i) {
for (int i = 0; i < workers_.count(); ++i) {
const int worker_index = (next_worker_ + i) % workers_.count();
if (workers_[worker_index].handler_ &&
@ -403,4 +402,4 @@ HandlerType* WorkerPool<HandlerType>::NextHandler() const {
return NULL;
}
#endif // WORKERPOOL_H
#endif // WORKERPOOL_H

View File

@ -28,13 +28,13 @@
#include "core/logging.h"
namespace {
static const int kTaglibPrefixCacheBytes = 64 * 1024; // Should be enough.
static const int kTaglibSuffixCacheBytes = 8 * 1024;
static const int kTaglibPrefixCacheBytes = 64 * 1024; // Should be enough.
static const int kTaglibSuffixCacheBytes = 8 * 1024;
}
CloudStream::CloudStream(
const QUrl& url, const QString& filename, const long length,
const QString& auth, QNetworkAccessManager* network)
CloudStream::CloudStream(const QUrl& url, const QString& filename,
const long length, const QString& auth,
QNetworkAccessManager* network)
: url_(url),
filename_(filename),
encoded_filename_(filename_.toUtf8()),
@ -43,12 +43,9 @@ CloudStream::CloudStream(
cursor_(0),
network_(network),
cache_(length),
num_requests_(0) {
}
num_requests_(0) {}
TagLib::FileName CloudStream::name() const {
return encoded_filename_.data();
}
TagLib::FileName CloudStream::name() const { return encoded_filename_.data(); }
bool CloudStream::CheckCache(int start, int end) {
for (int i = start; i <= end; ++i) {
@ -113,8 +110,8 @@ TagLib::ByteVector CloudStream::readBlock(ulong length) {
if (!auth_.isEmpty()) {
request.setRawHeader("Authorization", auth_.toUtf8());
}
request.setRawHeader(
"Range", QString("bytes=%1-%2").arg(start).arg(end).toUtf8());
request.setRawHeader("Range",
QString("bytes=%1-%2").arg(start).arg(end).toUtf8());
request.setAttribute(QNetworkRequest::CacheLoadControlAttribute,
QNetworkRequest::AlwaysNetwork);
// 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);
connect(reply, SIGNAL(sslErrors(QList<QSslError>)), SLOT(SSLErrors(QList<QSslError>)));
connect(reply, SIGNAL(sslErrors(QList<QSslError>)),
SLOT(SSLErrors(QList<QSslError>)));
++num_requests_;
QEventLoop loop;
@ -163,9 +161,7 @@ bool CloudStream::readOnly() const {
return true;
}
bool CloudStream::isOpen() const {
return true;
}
bool CloudStream::isOpen() const { return true; }
void CloudStream::seek(long offset, TagLib::IOStream::Position p) {
switch (p) {
@ -184,24 +180,18 @@ void CloudStream::seek(long offset, TagLib::IOStream::Position p) {
}
}
void CloudStream::clear() {
cursor_ = 0;
}
void CloudStream::clear() { cursor_ = 0; }
long CloudStream::tell() const {
return cursor_;
}
long CloudStream::tell() const { return cursor_; }
long CloudStream::length() {
return length_;
}
long CloudStream::length() { return length_; }
void CloudStream::truncate(long) {
qLog(Debug) << Q_FUNC_INFO << "not implemented";
}
void CloudStream::SSLErrors(const QList<QSslError>& errors) {
foreach (const QSslError& error, errors) {
foreach(const QSslError & error, errors) {
qLog(Debug) << error.error() << error.errorString();
qLog(Debug) << error.certificate();
}

View File

@ -31,11 +31,8 @@ class QNetworkAccessManager;
class CloudStream : public QObject, public TagLib::IOStream {
Q_OBJECT
public:
CloudStream(const QUrl& url,
const QString& filename,
const long length,
const QString& auth,
QNetworkAccessManager* network);
CloudStream(const QUrl& url, const QString& filename, const long length,
const QString& auth, QNetworkAccessManager* network);
// Taglib::IOStream
virtual TagLib::FileName name() const;
@ -55,9 +52,7 @@ class CloudStream : public QObject, public TagLib::IOStream {
return cache_.num_nonempty();
}
int num_requests() const {
return num_requests_;
}
int num_requests() const { return num_requests_; }
// Use educated guess to request the bytes that TagLib will probably want.
void Precache();
@ -84,4 +79,4 @@ class CloudStream : public QObject, public TagLib::IOStream {
int num_requests_;
};
#endif // GOOGLEDRIVESTREAM_H
#endif // GOOGLEDRIVESTREAM_H

View File

@ -25,19 +25,18 @@
using std::placeholders::_1;
using std::placeholders::_2;
FMPSParser::FMPSParser() :
// 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
// starts with a number, like "123abc".
float_re_("\\s*([+-]?\\d+(?:\\.\\d+)?)\\s*(?:$|(?=::|;;))"),
FMPSParser::FMPSParser()
: // 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
// starts with a number, like "123abc".
float_re_("\\s*([+-]?\\d+(?:\\.\\d+)?)\\s*(?:$|(?=::|;;))"),
// Matches any character except unescaped slashes, colons and semicolons.
string_re_("((?:[^\\\\;:]|(?:\\\\[\\\\:;]))+)(?:$|(?=::|;;))"),
// Matches any character except unescaped slashes, colons and semicolons.
string_re_("((?:[^\\\\;:]|(?:\\\\[\\\\:;]))+)(?:$|(?=::|;;))"),
// Used for replacing escaped characters.
escape_re_("\\\\([\\\\:;])")
{
}
// Used for replacing escaped characters.
escape_re_("\\\\([\\\\:;])") {}
// 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.
@ -59,16 +58,16 @@ static int ParseContainer(const QStringRef& data, F f, QList<T>* ret) {
int pos = 0;
while (pos < data.length()) {
const int len = data.length() - pos;
int matched_len = f(QStringRef(data.string(), data.position() + pos, len), &value);
if (matched_len == -1 || matched_len > len)
break;
int matched_len =
f(QStringRef(data.string(), data.position() + pos, len), &value);
if (matched_len == -1 || matched_len > len) break;
ret->append(value);
pos += matched_len;
// Expect two separators in a row
if (pos + 2 <= data.length() && data.at(pos) == Separator
&& data.at(pos+1) == Separator) {
if (pos + 2 <= data.length() && data.at(pos) == Separator &&
data.at(pos + 1) == Separator) {
pos += 2;
} else {
break;

View File

@ -22,7 +22,7 @@
#include <QVariantList>
class FMPSParser {
public:
public:
FMPSParser();
// A FMPS result is a list of lists of values (where a value is a string or
@ -48,11 +48,11 @@ public:
int ParseListList(const QString& data, Result* ret) const;
int ParseListListRef(const QStringRef& data, Result* ret) const;
private:
private:
QRegExp float_re_;
QRegExp string_re_;
QRegExp escape_re_;
Result result_;
};
#endif // FMPSPARSER_H
#endif // FMPSPARSER_H

View File

@ -59,15 +59,17 @@
#include "core/timeconstants.h"
// Taglib added support for FLAC pictures in 1.7.0
#if (TAGLIB_MAJOR_VERSION > 1) || (TAGLIB_MAJOR_VERSION == 1 && TAGLIB_MINOR_VERSION >= 7)
# define TAGLIB_HAS_FLAC_PICTURELIST
#if (TAGLIB_MAJOR_VERSION > 1) || \
(TAGLIB_MAJOR_VERSION == 1 && TAGLIB_MINOR_VERSION >= 7)
#define TAGLIB_HAS_FLAC_PICTURELIST
#endif
#ifdef HAVE_GOOGLE_DRIVE
# include "cloudstream.h"
#include "cloudstream.h"
#endif
#define NumberToASFAttribute(x) TagLib::ASF::Attribute(QStringToTaglibString(QString::number(x)))
#define NumberToASFAttribute(x) \
TagLib::ASF::Attribute(QStringToTaglibString(QString::number(x)))
class FileRefFactory {
public:
@ -78,11 +80,11 @@ class FileRefFactory {
class TagLibFileRefFactory : public FileRefFactory {
public:
virtual TagLib::FileRef* GetFileRef(const QString& filename) {
#ifdef Q_OS_WIN32
return new TagLib::FileRef(filename.toStdWString().c_str());
#else
return new TagLib::FileRef(QFile::encodeName(filename).constData());
#endif
#ifdef Q_OS_WIN32
return new TagLib::FileRef(filename.toStdWString().c_str());
#else
return new TagLib::FileRef(QFile::encodeName(filename).constData());
#endif
}
};
@ -95,21 +97,22 @@ TagLib::String StdStringToTaglibString(const std::string& s) {
TagLib::String QStringToTaglibString(const QString& s) {
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_Playcount_ID = "----:com.apple.iTunes:FMPS_Playcount";
const char* TagReader::kMP4_FMPS_Score_ID = "----:com.apple.iTunes:FMPS_Rating_Amarok_Score";
const char* TagReader::kMP4_FMPS_Rating_ID =
"----:com.apple.iTunes:FMPS_Rating";
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()
: factory_(new TagLibFileRefFactory),
network_(new QNetworkAccessManager),
kEmbeddedCover("(embedded)")
{}
: factory_(new TagLibFileRefFactory),
network_(new QNetworkAccessManager),
kEmbeddedCover("(embedded)") {}
void TagReader::ReadFile(const QString& filename,
pb::tagreader::SongMetadata* song) const {
pb::tagreader::SongMetadata* song) const {
const QByteArray url(QUrl::fromLocalFile(filename).toEncoded());
const QFileInfo info(filename);
@ -122,7 +125,7 @@ void TagReader::ReadFile(const QString& filename,
song->set_ctime(info.created().toTime_t());
std::unique_ptr<TagLib::FileRef> fileref(factory_->GetFileRef(filename));
if(fileref->isNull()) {
if (fileref->isNull()) {
qLog(Info) << "TagLib hasn't been able to read " << filename << " file";
return;
}
@ -130,7 +133,7 @@ void TagReader::ReadFile(const QString& filename,
TagLib::Tag* tag = fileref->tag();
if (tag) {
Decode(tag->title(), nullptr, song->mutable_title());
Decode(tag->artist(), nullptr, song->mutable_artist()); // TPE1
Decode(tag->artist(), nullptr, song->mutable_artist()); // TPE1
Decode(tag->album(), nullptr, song->mutable_album());
Decode(tag->genre(), nullptr, song->mutable_genre());
song->set_year(tag->year());
@ -141,14 +144,17 @@ void TagReader::ReadFile(const QString& filename,
QString disc;
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
// "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);
}
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()) {
const TagLib::ID3v2::FrameListMap& map = file->ID3v2Tag()->frameListMap();
@ -156,27 +162,32 @@ void TagReader::ReadFile(const QString& filename,
disc = TStringToQString(map["TPOS"].front()->toString()).trimmed();
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())
Decode(map["TCOM"].front()->toString(), nullptr, song->mutable_composer());
Decode(map["TCOM"].front()->toString(), nullptr,
song->mutable_composer());
if (!map["TIT1"].isEmpty()) // content group
Decode(map["TIT1"].front()->toString(), nullptr, song->mutable_grouping());
if (!map["TIT1"].isEmpty()) // content group
Decode(map["TIT1"].front()->toString(), nullptr,
song->mutable_grouping());
// Skip TPE1 (which is the artist) here because we already fetched it
if (!map["TPE2"].isEmpty()) // non-standard: Apple, Microsoft
Decode(map["TPE2"].front()->toString(), nullptr, song->mutable_albumartist());
if (!map["TPE2"].isEmpty()) // non-standard: Apple, Microsoft
Decode(map["TPE2"].front()->toString(), nullptr,
song->mutable_albumartist());
if (!map["TCMP"].isEmpty())
compilation = TStringToQString(map["TCMP"].front()->toString()).trimmed();
compilation =
TStringToQString(map["TCMP"].front()->toString()).trimmed();
if (!map["APIC"].isEmpty())
song->set_art_automatic(kEmbeddedCover);
if (!map["APIC"].isEmpty()) song->set_art_automatic(kEmbeddedCover);
// 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) {
const TagLib::ID3v2::CommentsFrame* frame =
dynamic_cast<const TagLib::ID3v2::CommentsFrame*>(map["COMM"][i]);
@ -187,14 +198,14 @@ void TagReader::ReadFile(const QString& filename,
}
// 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 =
dynamic_cast<const TagLib::ID3v2::UserTextIdentificationFrame*>(map["TXXX"][i]);
dynamic_cast<const TagLib::ID3v2::UserTextIdentificationFrame*>(
map["TXXX"][i]);
if (frame && frame->description().startsWith("FMPS_")) {
ParseFMPSFrame(TStringToQString(frame->description()),
TStringToQString(frame->fieldList()[1]),
song);
TStringToQString(frame->fieldList()[1]), song);
}
}
@ -203,7 +214,8 @@ void TagReader::ReadFile(const QString& filename,
// will consider POPM tags iff song has no rating/playcount already set.
if (!map["POPM"].isEmpty()) {
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) {
// Take a user rating only if there's no rating already set
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())) {
if ( file->xiphComment() ) {
ParseOggTag(file->xiphComment()->fieldListMap(), nullptr, &disc, &compilation, song);
} else if (TagLib::FLAC::File* file =
dynamic_cast<TagLib::FLAC::File*>(fileref->file())) {
if (file->xiphComment()) {
ParseOggTag(file->xiphComment()->fieldListMap(), nullptr, &disc,
&compilation, song);
#ifdef TAGLIB_HAS_FLAC_PICTURELIST
if (!file->pictureList().isEmpty()) {
song->set_art_automatic(kEmbeddedCover);
@ -226,7 +239,8 @@ void TagReader::ReadFile(const QString& filename,
#endif
}
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()) {
TagLib::MP4::Tag* mp4_tag = file->tag();
const TagLib::MP4::ItemListMap& items = mp4_tag->itemListMap();
@ -246,51 +260,67 @@ void TagReader::ReadFile(const QString& filename,
}
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)) {
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) {
song->set_rating(rating);
}
}
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) {
song->set_playcount(playcount);
}
}
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) {
song->set_score(score);
}
}
if(items.contains("\251wrt")) {
Decode(items["\251wrt"].toStringList().toString(", "), nullptr, song->mutable_composer());
if (items.contains("\251wrt")) {
Decode(items["\251wrt"].toStringList().toString(", "), nullptr,
song->mutable_composer());
}
if(items.contains("\251grp")) {
Decode(items["\251grp"].toStringList().toString(" "), nullptr, song->mutable_grouping());
if (items.contains("\251grp")) {
Decode(items["\251grp"].toStringList().toString(" "), nullptr,
song->mutable_grouping());
}
Decode(mp4_tag->comment(), nullptr, song->mutable_comment());
}
}
#ifdef TAGLIB_WITH_ASF
else if (TagLib::ASF::File* file = dynamic_cast<TagLib::ASF::File*>(fileref->file())) {
const TagLib::ASF::AttributeListMap& attributes_map = file->tag()->attributeListMap();
else if (TagLib::ASF::File* file =
dynamic_cast<TagLib::ASF::File*>(fileref->file())) {
const TagLib::ASF::AttributeListMap& attributes_map =
file->tag()->attributeListMap();
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()) {
float rating = TStringToQString(attributes.front().toString()).toFloat();
float rating =
TStringToQString(attributes.front().toString()).toFloat();
if (song->rating() <= 0 && rating > 0) {
song->set_rating(rating);
}
}
}
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()) {
int playcount = TStringToQString(attributes.front().toString()).toInt();
if (song->playcount() <= 0 && playcount > 0) {
@ -299,9 +329,11 @@ void TagReader::ReadFile(const QString& filename,
}
}
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()) {
int score = TStringToQString(attributes.front().toString()).toFloat() * 100;
int score =
TStringToQString(attributes.front().toString()).toFloat() * 100;
if (song->score() <= 0 && score > 0) {
song->set_score(score);
}
@ -316,7 +348,8 @@ void TagReader::ReadFile(const QString& filename,
if (!disc.isEmpty()) {
const int i = disc.indexOf('/');
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());
} else {
song->set_disc(disc.toInt());
@ -335,14 +368,18 @@ void TagReader::ReadFile(const QString& filename,
if (fileref->audioProperties()) {
song->set_bitrate(fileref->audioProperties()->bitrate());
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
song->set_type(GuessFileType(fileref.get()));
// Set integer fields to -1 if they're not valid
#define SetDefault(field) if (song->field() <= 0) { song->set_##field(-1); }
// Set integer fields to -1 if they're not valid
#define SetDefault(field) \
if (song->field() <= 0) { \
song->set_##field(-1); \
}
SetDefault(track);
SetDefault(disc);
SetDefault(bpm);
@ -350,16 +387,16 @@ void TagReader::ReadFile(const QString& filename,
SetDefault(bitrate);
SetDefault(samplerate);
SetDefault(lastplayed);
#undef SetDefault
#undef SetDefault
}
void TagReader::Decode(const TagLib::String& tag, const QTextCodec* codec,
std::string* output) {
std::string* output) {
QString tmp;
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();
} else {
tmp = TStringToQString(tag).trimmed();
@ -369,7 +406,7 @@ void TagReader::Decode(const TagLib::String& tag, const QTextCodec* codec,
}
void TagReader::Decode(const QString& tag, const QTextCodec* codec,
std::string* output) {
std::string* output) {
if (!codec) {
output->assign(DataCommaSizeFromQString(tag));
} else {
@ -379,11 +416,10 @@ void TagReader::Decode(const QString& tag, const QTextCodec* codec,
}
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;
FMPSParser parser;
if (!parser.Parse(value) || parser.is_empty())
return;
if (!parser.Parse(value) || parser.is_empty()) return;
QVariant var;
if (name == "FMPS_Rating") {
@ -421,9 +457,9 @@ void TagReader::ParseFMPSFrame(const QString& name, const QString& value,
}
void TagReader::ParseOggTag(const TagLib::Ogg::FieldListMap& map,
const QTextCodec* codec,
QString* disc, QString* compilation,
pb::tagreader::SongMetadata* song) const {
const QTextCodec* codec, QString* disc,
QString* compilation,
pb::tagreader::SongMetadata* song) const {
if (!map["COMPOSER"].isEmpty())
Decode(map["COMPOSER"].front(), codec, song->mutable_composer());
if (!map["PERFORMER"].isEmpty())
@ -437,52 +473,76 @@ void TagReader::ParseOggTag(const TagLib::Ogg::FieldListMap& map,
Decode(map["ALBUM ARTIST"].front(), codec, song->mutable_albumartist());
}
if (!map["BPM"].isEmpty() )
song->set_bpm(TStringToQString( map["BPM"].front() ).trimmed().toFloat());
if (!map["BPM"].isEmpty())
song->set_bpm(TStringToQString(map["BPM"].front()).trimmed().toFloat());
if (!map["DISCNUMBER"].isEmpty() )
*disc = TStringToQString( map["DISCNUMBER"].front() ).trimmed();
if (!map["DISCNUMBER"].isEmpty())
*disc = TStringToQString(map["DISCNUMBER"].front()).trimmed();
if (!map["COMPILATION"].isEmpty() )
*compilation = TStringToQString( map["COMPILATION"].front() ).trimmed();
if (!map["COMPILATION"].isEmpty())
*compilation = TStringToQString(map["COMPILATION"].front()).trimmed();
if (!map["COVERART"].isEmpty())
song->set_art_automatic(kEmbeddedCover);
if (!map["COVERART"].isEmpty()) song->set_art_automatic(kEmbeddedCover);
if (!map["METADATA_BLOCK_PICTURE"].isEmpty())
song->set_art_automatic(kEmbeddedCover);
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)
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)
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,
const pb::tagreader::SongMetadata& song) const {
const pb::tagreader::SongMetadata& song)
const {
vorbis_comments->addField("COMPOSER", StdStringToTaglibString(song.composer()), true);
vorbis_comments->addField("PERFORMER", StdStringToTaglibString(song.performer()), true);
vorbis_comments->addField("CONTENT GROUP", 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);
vorbis_comments->addField("COMPOSER",
StdStringToTaglibString(song.composer()), true);
vorbis_comments->addField("PERFORMER",
StdStringToTaglibString(song.performer()), true);
vorbis_comments->addField("CONTENT GROUP",
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,
const pb::tagreader::SongMetadata& song) const {
vorbis_comments->addField("FMPS_PLAYCOUNT", QStringToTaglibString(QString::number(song.playcount())));
vorbis_comments->addField("FMPS_RATING_AMAROK_SCORE", QStringToTaglibString(QString::number(song.score() / 100.0)));
void TagReader::SetFMPSStatisticsVorbisComments(
TagLib::Ogg::XiphComment* vorbis_comments,
const pb::tagreader::SongMetadata& song) const {
vorbis_comments->addField(
"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,
const pb::tagreader::SongMetadata& song) const {
void TagReader::SetFMPSRatingVorbisComments(
TagLib::Ogg::XiphComment* vorbis_comments,
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(
@ -522,135 +582,155 @@ pb::tagreader::SongMetadata_Type TagReader::GuessFileType(
}
bool TagReader::SaveFile(const QString& filename,
const pb::tagreader::SongMetadata& song) const {
if (filename.isNull())
return false;
const pb::tagreader::SongMetadata& song) const {
if (filename.isNull()) return false;
qLog(Debug) << "Saving tags to" << filename;
std::unique_ptr<TagLib::FileRef> fileref(factory_->GetFileRef(filename));
if (!fileref || fileref->isNull()) // The file probably doesn't exist
if (!fileref || fileref->isNull()) // The file probably doesn't exist
return false;
fileref->tag()->setTitle(StdStringToTaglibString(song.title()));
fileref->tag()->setArtist(StdStringToTaglibString(song.artist())); // TPE1
fileref->tag()->setArtist(StdStringToTaglibString(song.artist())); // TPE1
fileref->tag()->setAlbum(StdStringToTaglibString(song.album()));
fileref->tag()->setGenre(StdStringToTaglibString(song.genre()));
fileref->tag()->setComment(StdStringToTaglibString(song.comment()));
fileref->tag()->setYear(song.year());
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);
SetTextFrame("TPOS", song.disc() <= 0 -1 ? QString() : QString::number(song.disc()), tag);
SetTextFrame("TBPM", song.bpm() <= 0 -1 ? QString() : QString::number(song.bpm()), tag);
SetTextFrame(
"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("TIT1", song.grouping(), tag);
// Skip TPE1 (which is the artist) here because we already set it
SetTextFrame("TPE2", song.albumartist(), 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();
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();
tag->itemListMap()["disk"] = 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()["disk"] =
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()["\251grp"] = TagLib::StringList(song.grouping());
tag->itemListMap()["aART"] = TagLib::StringList(song.albumartist());
tag->itemListMap()["cpil"] = TagLib::StringList(song.compilation() ? "1" : "0");
tag->itemListMap()["aART"] = TagLib::StringList(song.albumartist());
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
// "else if" block above.
if (TagLib::Ogg::XiphComment* tag = dynamic_cast<TagLib::Ogg::XiphComment*>(fileref->file()->tag())) {
SetVorbisComments(tag, song);
if (TagLib::Ogg::XiphComment* tag =
dynamic_cast<TagLib::Ogg::XiphComment*>(fileref->file()->tag())) {
SetVorbisComments(tag, song);
}
bool ret = fileref->save();
#ifdef Q_OS_LINUX
#ifdef Q_OS_LINUX
if (ret) {
// Linux: inotify doesn't seem to notice the change to the file unless we
// change the timestamps as well. (this is what touch does)
utimensat(0, QFile::encodeName(filename).constData(), nullptr, 0);
}
#endif // Q_OS_LINUX
#endif // Q_OS_LINUX
return ret;
}
bool TagReader::SaveSongStatisticsToFile(const QString& filename,
const pb::tagreader::SongMetadata& song) const {
if (filename.isNull())
return false;
bool TagReader::SaveSongStatisticsToFile(
const QString& filename, const pb::tagreader::SongMetadata& song) const {
if (filename.isNull()) return false;
qLog(Debug) << "Saving song statistics tags to" << filename;
std::unique_ptr<TagLib::FileRef> fileref(factory_->GetFileRef(filename));
if (!fileref || fileref->isNull()) // The file probably doesn't exist
if (!fileref || fileref->isNull()) // The file probably doesn't exist
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);
// Save as FMPS
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
TagLib::ID3v2::PopularimeterFrame* frame = GetPOPMFrameFromTag(tag);
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);
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);
}
#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();
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
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();
tag->itemListMap()[kMP4_FMPS_Score_ID] = TagLib::StringList(QStringToTaglibString(QString::number(song.score() / 100.0)));
tag->itemListMap()[kMP4_FMPS_Playcount_ID] = TagLib::StringList(TagLib::String::number(song.playcount()));
tag->itemListMap()[kMP4_FMPS_Score_ID] = TagLib::StringList(
QStringToTaglibString(QString::number(song.score() / 100.0)));
tag->itemListMap()[kMP4_FMPS_Playcount_ID] =
TagLib::StringList(TagLib::String::number(song.playcount()));
} else {
// Nothing to save: stop now
return true;
}
bool ret = fileref->save();
#ifdef Q_OS_LINUX
#ifdef Q_OS_LINUX
if (ret) {
// Linux: inotify doesn't seem to notice the change to the file unless we
// change the timestamps as well. (this is what touch does)
utimensat(0, QFile::encodeName(filename).constData(), nullptr, 0);
}
#endif // Q_OS_LINUX
#endif // Q_OS_LINUX
return ret;
}
bool TagReader::SaveSongRatingToFile(const QString& filename,
const pb::tagreader::SongMetadata& song) const {
if (filename.isNull())
return false;
bool TagReader::SaveSongRatingToFile(
const QString& filename, const pb::tagreader::SongMetadata& song) const {
if (filename.isNull()) return false;
qLog(Debug) << "Saving song rating tags to" << filename;
std::unique_ptr<TagLib::FileRef> fileref(factory_->GetFileRef(filename));
if (!fileref || fileref->isNull()) // The file probably doesn't exist
if (!fileref || fileref->isNull()) // The file probably doesn't exist
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);
// Save as FMPS
@ -660,38 +740,45 @@ bool TagReader::SaveSongRatingToFile(const QString& filename,
TagLib::ID3v2::PopularimeterFrame* frame = GetPOPMFrameFromTag(tag);
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);
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);
}
#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();
tag->addAttribute("FMPS/Rating", NumberToASFAttribute(song.rating()));
tag->addAttribute("FMPS/Rating", NumberToASFAttribute(song.rating()));
}
#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();
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 {
// Nothing to save: stop now
return true;
}
bool ret = fileref->save();
#ifdef Q_OS_LINUX
#ifdef Q_OS_LINUX
if (ret) {
// Linux: inotify doesn't seem to notice the change to the file unless we
// change the timestamps as well. (this is what touch does)
utimensat(0, QFile::encodeName(filename).constData(), nullptr, 0);
}
#endif // Q_OS_LINUX
#endif // Q_OS_LINUX
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 {
const QByteArray descr_utf8(description.toUtf8());
const QByteArray value_utf8(value.toUtf8());
@ -721,13 +808,13 @@ void TagReader::SetUserTextFrame(const std::string& description,
}
void TagReader::SetTextFrame(const char* id, const QString& value,
TagLib::ID3v2::Tag* tag) const {
TagLib::ID3v2::Tag* tag) const {
const QByteArray utf8(value.toUtf8());
SetTextFrame(id, std::string(utf8.constData(), utf8.length()), tag);
}
void TagReader::SetTextFrame(const char* id, const std::string& value,
TagLib::ID3v2::Tag* tag) const {
TagLib::ID3v2::Tag* tag) const {
TagLib::ByteVector id_vector(id);
// Remove the frame if it already exists
@ -752,8 +839,7 @@ bool TagReader::IsMediaFile(const QString& filename) const {
}
QByteArray TagReader::LoadEmbeddedArt(const QString& filename) const {
if (filename.isEmpty())
return QByteArray();
if (filename.isEmpty()) return QByteArray();
qLog(Debug) << "Loading art from" << filename;
@ -763,20 +849,20 @@ QByteArray TagReader::LoadEmbeddedArt(const QString& filename) const {
TagLib::FileRef ref(QFile::encodeName(filename).constData());
#endif
if (ref.isNull() || !ref.file())
return QByteArray();
if (ref.isNull() || !ref.file()) return QByteArray();
// MP3
TagLib::MPEG::File* file = dynamic_cast<TagLib::MPEG::File*>(ref.file());
if (file && file->ID3v2Tag()) {
TagLib::ID3v2::FrameList apic_frames = file->ID3v2Tag()->frameListMap()["APIC"];
if (apic_frames.isEmpty())
return QByteArray();
TagLib::ID3v2::FrameList apic_frames =
file->ID3v2Tag()->frameListMap()["APIC"];
if (apic_frames.isEmpty()) return QByteArray();
TagLib::ID3v2::AttachedPictureFrame* pic =
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
@ -786,12 +872,14 @@ QByteArray TagReader::LoadEmbeddedArt(const QString& filename) const {
if (xiph_comment) {
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.
// (see http://wiki.xiph.org/VorbisComment#METADATA_BLOCK_PICTURE)
if (map.contains("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()));
TagLib::ByteVector tdata(data.data(), data.size());
TagLib::FLAC::Picture p(tdata);
@ -799,7 +887,8 @@ QByteArray TagReader::LoadEmbeddedArt(const QString& filename) const {
return QByteArray(p.data().data(), p.data().size());
}
// 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::FLAC::Picture p(tdata);
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
// b64 encoding a field called COVERART is the general convention
if (!map.contains("COVERART"))
return QByteArray();
if (!map.contains("COVERART")) return QByteArray();
return QByteArray::fromBase64(map["COVERART"].toString().toCString());
}
@ -850,62 +938,45 @@ QByteArray TagReader::LoadEmbeddedArt(const QString& filename) const {
return QByteArray();
}
#ifdef HAVE_GOOGLE_DRIVE
bool TagReader::ReadCloudFile(const QUrl& download_url,
const QString& title,
int size,
const QString& mime_type,
const QString& authorisation_header,
pb::tagreader::SongMetadata* song) const {
bool TagReader::ReadCloudFile(const QUrl& download_url, const QString& title,
int size, const QString& mime_type,
const QString& authorisation_header,
pb::tagreader::SongMetadata* song) const {
qLog(Debug) << "Loading tags from" << title;
CloudStream* stream = new CloudStream(
download_url, title, size, authorisation_header, network_);
CloudStream* stream = new CloudStream(download_url, title, size,
authorisation_header, network_);
stream->Precache();
std::unique_ptr<TagLib::File> tag;
if (mime_type == "audio/mpeg" && title.endsWith(".mp3")) {
tag.reset(new TagLib::MPEG::File(
stream, // Takes ownership.
TagLib::ID3v2::FrameFactory::instance(),
TagLib::AudioProperties::Accurate));
tag.reset(new TagLib::MPEG::File(stream, // Takes ownership.
TagLib::ID3v2::FrameFactory::instance(),
TagLib::AudioProperties::Accurate));
} else if (mime_type == "audio/mp4" ||
(mime_type == "audio/mpeg" && title.endsWith(".m4a"))) {
tag.reset(new TagLib::MP4::File(
stream,
true,
TagLib::AudioProperties::Accurate));
tag.reset(
new TagLib::MP4::File(stream, true, TagLib::AudioProperties::Accurate));
}
#ifdef TAGLIB_HAS_OPUS
else if ((mime_type == "application/opus" ||
mime_type == "audio/opus" ||
mime_type == "application/ogg" ||
mime_type == "audio/ogg") && title.endsWith(".opus")) {
tag.reset(new TagLib::Ogg::Opus::File(
stream,
true,
TagLib::AudioProperties::Accurate));
else if ((mime_type == "application/opus" || mime_type == "audio/opus" ||
mime_type == "application/ogg" || mime_type == "audio/ogg") &&
title.endsWith(".opus")) {
tag.reset(new TagLib::Ogg::Opus::File(stream, true,
TagLib::AudioProperties::Accurate));
}
#endif
else if (mime_type == "application/ogg" ||
mime_type == "audio/ogg") {
tag.reset(new TagLib::Ogg::Vorbis::File(
stream,
true,
TagLib::AudioProperties::Accurate));
} else if (mime_type == "application/x-flac" ||
mime_type == "audio/flac" ||
mime_type == "audio/x-flac") {
tag.reset(new TagLib::FLAC::File(
stream,
TagLib::ID3v2::FrameFactory::instance(),
true,
TagLib::AudioProperties::Accurate));
else if (mime_type == "application/ogg" || mime_type == "audio/ogg") {
tag.reset(new TagLib::Ogg::Vorbis::File(stream, true,
TagLib::AudioProperties::Accurate));
} else if (mime_type == "application/x-flac" || mime_type == "audio/flac" ||
mime_type == "audio/x-flac") {
tag.reset(new TagLib::FLAC::File(stream,
TagLib::ID3v2::FrameFactory::instance(),
true, TagLib::AudioProperties::Accurate));
} else if (mime_type == "audio/x-ms-wma") {
tag.reset(new TagLib::ASF::File(
stream,
true,
TagLib::AudioProperties::Accurate));
tag.reset(
new TagLib::ASF::File(stream, true, TagLib::AudioProperties::Accurate));
} else {
qLog(Debug) << "Unknown mime type for tagging:" << mime_type;
return false;
@ -914,8 +985,7 @@ bool TagReader::ReadCloudFile(const QUrl& download_url,
if (stream->num_requests() > 2) {
// Warn if pre-caching failed.
qLog(Warning) << "Total requests for file:" << title
<< stream->num_requests()
<< stream->cached_bytes();
<< stream->num_requests() << stream->cached_bytes();
}
if (tag->tag() && !tag->tag()->isEmpty()) {
@ -941,14 +1011,16 @@ bool TagReader::ReadCloudFile(const QUrl& download_url,
return false;
}
#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;
const TagLib::ID3v2::FrameListMap& map = tag->frameListMap();
if (!map["POPM"].isEmpty()) {
frame = dynamic_cast<TagLib::ID3v2::PopularimeterFrame*>(map["POPM"].front());
frame =
dynamic_cast<TagLib::ID3v2::PopularimeterFrame*>(map["POPM"].front());
}
if (!frame) {
@ -962,15 +1034,15 @@ float TagReader::ConvertPOPMRating(const int POPM_rating) {
if (POPM_rating < 0x01) {
return 0.0;
} else if (POPM_rating < 0x40) {
return 0.20; // 1 star
return 0.20; // 1 star
} else if (POPM_rating < 0x80) {
return 0.40; // 2 stars
return 0.40; // 2 stars
} else if (POPM_rating < 0xC0) {
return 0.60; // 3 stars
} else if (POPM_rating < 0xFC) { // some players store 5 stars as 0xFC
return 0.80; // 4 stars
return 0.60; // 3 stars
} else if (POPM_rating < 0xFC) { // some players store 5 stars as 0xFC
return 0.80; // 4 stars
}
return 1.0; // 5 stars
return 1.0; // 5 stars
}
int TagReader::ConvertToPOPMRating(const float rating) {

View File

@ -30,15 +30,14 @@ class QString;
class QTextCodec;
class QUrl;
namespace TagLib {
class FileRef;
class String;
class FileRef;
class String;
namespace ID3v2 {
class Tag;
class PopularimeterFrame;
}
namespace ID3v2 {
class Tag;
class PopularimeterFrame;
}
}
class FileRefFactory;
@ -52,25 +51,26 @@ class TagReader {
public:
TagReader();
void ReadFile(const QString& filename, pb::tagreader::SongMetadata* song) const;
bool SaveFile(const QString& filename, const pb::tagreader::SongMetadata& song) const;
void ReadFile(const QString& filename,
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 true if the file exists but nothing has been written inside because
// statistics tag format is not supported for this kind of file)
bool SaveSongStatisticsToFile(const QString& filename, const pb::tagreader::SongMetadata& song) const;
bool SaveSongRatingToFile(const QString& filename, const pb::tagreader::SongMetadata& song) const;
bool SaveSongStatisticsToFile(const QString& filename,
const pb::tagreader::SongMetadata& song) const;
bool SaveSongRatingToFile(const QString& filename,
const pb::tagreader::SongMetadata& song) const;
bool IsMediaFile(const QString& filename) const;
QByteArray LoadEmbeddedArt(const QString& filename) const;
#ifdef HAVE_GOOGLE_DRIVE
bool ReadCloudFile(const QUrl& download_url,
const QString& title,
int size,
const QString& mime_type,
const QString& access_token,
#ifdef HAVE_GOOGLE_DRIVE
bool ReadCloudFile(const QUrl& download_url, const QString& title, int size,
const QString& mime_type, const QString& access_token,
pb::tagreader::SongMetadata* song) const;
#endif // HAVE_GOOGLE_DRIVE
#endif // HAVE_GOOGLE_DRIVE
static void Decode(const TagLib::String& tag, const QTextCodec* codec,
std::string* output);
@ -80,21 +80,24 @@ class TagReader {
void ParseFMPSFrame(const QString& name, const QString& value,
pb::tagreader::SongMetadata* song) const;
void ParseOggTag(const TagLib::Ogg::FieldListMap& map,
const QTextCodec* codec,
QString* disc, QString* compilation,
const QTextCodec* codec, QString* disc, QString* compilation,
pb::tagreader::SongMetadata* song) const;
void SetVorbisComments(TagLib::Ogg::XiphComment* vorbis_comments,
const pb::tagreader::SongMetadata& song) const;
void SetFMPSStatisticsVorbisComments(TagLib::Ogg::XiphComment* vorbis_comments,
const pb::tagreader::SongMetadata& song) const;
void SetFMPSStatisticsVorbisComments(
TagLib::Ogg::XiphComment* vorbis_comments,
const pb::tagreader::SongMetadata& song) const;
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,
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;
void SetTextFrame(const char* id, const QString& value,
@ -102,15 +105,17 @@ class TagReader {
void SetTextFrame(const char* id, const std::string& value,
TagLib::ID3v2::Tag* tag) const;
private:
private:
static const char* kMP4_FMPS_Rating_ID;
static const char* kMP4_FMPS_Playcount_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);
// Reciprocal
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_;
QNetworkAccessManager* network_;
@ -118,4 +123,4 @@ private:
const std::string kEmbeddedCover;
};
#endif // TAGREADER_H
#endif // TAGREADER_H

View File

@ -3,15 +3,13 @@
#include "engines/enginebase.h"
AnalyzerBase::AnalyzerBase(QWidget* parent)
: QGLWidget(parent),
engine_(nullptr) {
}
: QGLWidget(parent), engine_(nullptr) {}
void AnalyzerBase::set_engine(Engine::Base* engine) {
disconnect(engine_);
engine_ = engine;
if (engine_) {
connect(engine_, SIGNAL(SpectrumAvailable(const QVector<float>&)),
SLOT(SpectrumAvailable(const QVector<float>&)));
SLOT(SpectrumAvailable(const QVector<float>&)));
}
}

View File

@ -17,9 +17,9 @@
#include "analyzerbase.h"
#include <cmath> //interpolate()
#include <cmath> //interpolate()
#include <QEvent> //event()
#include <QEvent> //event()
#include <QPainter>
#include <QPaintEvent>
#include <QtDebug>
@ -27,205 +27,186 @@
#include "engines/enginebase.h"
// 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
// 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()
// 5. for convenience <vector> <qpixmap.h> <qwdiget.h> are pre-included
// 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
#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>;
#endif
Analyzer::Base::Base(QWidget* parent, uint scopeSize)
: QWidget(parent),
m_timeout(40) // msec
,
m_fht(new FHT(scopeSize)),
m_engine(nullptr),
m_lastScope(512),
new_frame_(false),
is_playing_(false) {}
Analyzer::Base::Base( QWidget *parent, uint scopeSize )
: QWidget( parent )
, m_timeout( 40 ) // msec
, m_fht( new FHT(scopeSize) )
, m_engine(nullptr)
, m_lastScope(512)
, new_frame_(false)
, is_playing_(false)
void Analyzer::Base::hideEvent(QHideEvent*) { m_timer.stop(); }
void Analyzer::Base::showEvent(QShowEvent*) { m_timer.start(timeout(), this); }
void Analyzer::Base::transform(Scope& scope) // virtual
{
// this is a standard transformation that should give
// an FFT scope that has bands for pretty analyzers
// NOTE resizing here is redundant as FHT routines only calculate FHT::size()
// values
// scope.resize( m_fht->size() );
float* front = static_cast<float*>(&scope.front());
float* f = new float[m_fht->size()];
m_fht->copy(&f[0], front);
m_fht->logSpectrum(front, &f[0]);
m_fht->scale(front, 1.0 / 20);
scope.resize(m_fht->size() / 2); // second half of values are rubbish
delete[] f;
}
void Analyzer::Base::hideEvent(QHideEvent *) {
m_timer.stop();
}
void Analyzer::Base::paintEvent(QPaintEvent* e) {
QPainter p(this);
p.fillRect(e->rect(), palette().color(QPalette::Window));
void Analyzer::Base::showEvent(QShowEvent *) {
m_timer.start(timeout(), this);
}
switch (m_engine->state()) {
case Engine::Playing: {
const Engine::Scope& thescope = m_engine->scope();
int i = 0;
void Analyzer::Base::transform( Scope &scope ) //virtual
{
//this is a standard transformation that should give
//an FFT scope that has bands for pretty analyzers
// convert to mono here - our built in analyzers need mono, but we the
// 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));
i += 2;
}
//NOTE resizing here is redundant as FHT routines only calculate FHT::size() values
//scope.resize( m_fht->size() );
is_playing_ = true;
transform(m_lastScope);
analyze(p, m_lastScope, new_frame_);
float *front = static_cast<float*>( &scope.front() );
// scope.resize( m_fht->size() );
float* f = new float[ m_fht->size() ];
m_fht->copy( &f[0], front );
m_fht->logSpectrum( front, &f[0] );
m_fht->scale( front, 1.0 / 20 );
scope.resize( m_fht->size() / 2 ); //second half of values are rubbish
delete [] f;
}
void Analyzer::Base::paintEvent(QPaintEvent * e)
{
QPainter p(this);
p.fillRect(e->rect(), palette().color(QPalette::Window));
switch( m_engine->state() )
{
case Engine::Playing:
{
const Engine::Scope &thescope = m_engine->scope();
int i = 0;
// convert to mono here - our built in analyzers need mono, but we the 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));
i += 2;
}
is_playing_ = true;
transform( m_lastScope );
analyze( p, m_lastScope, new_frame_ );
//scope.resize( m_fht->size() );
break;
break;
}
case Engine::Paused:
is_playing_ = false;
analyze(p, m_lastScope, new_frame_);
break;
is_playing_ = false;
analyze(p, m_lastScope, new_frame_);
break;
default:
is_playing_ = false;
demo(p);
}
is_playing_ = false;
demo(p);
}
new_frame_ = false;
new_frame_ = false;
}
int Analyzer::Base::resizeExponent( int exp )
{
if ( exp < 3 )
exp = 3;
else if ( exp > 9 )
exp = 9;
int Analyzer::Base::resizeExponent(int exp) {
if (exp < 3)
exp = 3;
else if (exp > 9)
exp = 9;
if ( exp != m_fht->sizeExp() ) {
delete m_fht;
m_fht = new FHT( exp );
}
return exp;
if (exp != m_fht->sizeExp()) {
delete m_fht;
m_fht = new FHT(exp);
}
return exp;
}
int Analyzer::Base::resizeForBands( int bands )
{
int exp;
if ( bands <= 8 )
exp = 4;
else if ( bands <= 16 )
exp = 5;
else if ( bands <= 32 )
exp = 6;
else if ( bands <= 64 )
exp = 7;
else if ( bands <= 128 )
exp = 8;
else
exp = 9;
int Analyzer::Base::resizeForBands(int bands) {
int exp;
if (bands <= 8)
exp = 4;
else if (bands <= 16)
exp = 5;
else if (bands <= 32)
exp = 6;
else if (bands <= 64)
exp = 7;
else if (bands <= 128)
exp = 8;
else
exp = 9;
resizeExponent( exp );
return m_fht->size() / 2;
resizeExponent(exp);
return m_fht->size() / 2;
}
void Analyzer::Base::demo(QPainter& p) //virtual
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 < 201 )
{
Scope s( 32 );
if (t > 999) t = 1; // 0 = wasted calculations
if (t < 201) {
Scope s(32);
const double dt = double(t) / 200;
for( uint i = 0; i < s.size(); ++i )
s[i] = dt * (sin( M_PI + (i * M_PI) / s.size() ) + 1.0);
const double dt = double(t) / 200;
for (uint i = 0; i < s.size(); ++i)
s[i] = dt * (sin(M_PI + (i * M_PI) / s.size()) + 1.0);
analyze( p, s, new_frame_ );
}
else analyze( p, Scope( 32, 0 ), new_frame_ );
analyze(p, s, new_frame_);
} else
analyze(p, Scope(32, 0), new_frame_);
++t;
++t;
}
void Analyzer::Base::polishEvent()
{
init(); //virtual
void Analyzer::Base::polishEvent() {
init(); // virtual
}
void
Analyzer::interpolate( const Scope &inVec, Scope &outVec ) //static
void Analyzer::interpolate(const Scope& inVec, Scope& outVec) // static
{
double pos = 0.0;
const double step = (double)inVec.size() / outVec.size();
double pos = 0.0;
const double step = (double)inVec.size() / outVec.size();
for ( uint i = 0; i < outVec.size(); ++i, pos += step )
{
const double error = pos - std::floor( pos );
const unsigned long offset = (unsigned long)pos;
for (uint i = 0; i < outVec.size(); ++i, pos += step) {
const double error = pos - std::floor(pos);
const unsigned long offset = (unsigned long)pos;
unsigned long indexLeft = offset + 0;
unsigned long indexLeft = offset + 0;
if ( indexLeft >= inVec.size() )
indexLeft = inVec.size() - 1;
if (indexLeft >= inVec.size()) indexLeft = inVec.size() - 1;
unsigned long indexRight = offset + 1;
unsigned long indexRight = offset + 1;
if ( indexRight >= inVec.size() )
indexRight = inVec.size() - 1;
if (indexRight >= inVec.size()) indexRight = inVec.size() - 1;
outVec[i] = inVec[indexLeft ] * ( 1.0 - error ) +
inVec[indexRight] * error;
}
outVec[i] = inVec[indexLeft] * (1.0 - error) + inVec[indexRight] * error;
}
}
void
Analyzer::initSin( Scope &v, const uint size ) //static
void Analyzer::initSin(Scope& v, const uint size) // static
{
double step = ( M_PI * 2 ) / size;
double radian = 0;
double step = (M_PI * 2) / size;
double radian = 0;
for ( uint i = 0; i < size; i++ )
{
v.push_back( sin( radian ) );
radian += step;
}
for (uint i = 0; i < size; i++) {
v.push_back(sin(radian));
radian += step;
}
}
void Analyzer::Base::timerEvent(QTimerEvent* e) {
QWidget::timerEvent(e);
if (e->timerId() != m_timer.timerId())
return;
if (e->timerId() != m_timer.timerId()) return;
new_frame_ = true;
update();

View File

@ -4,19 +4,18 @@
#ifndef ANALYZERBASE_H
#define ANALYZERBASE_H
#ifdef __FreeBSD__
#include <sys/types.h>
#endif
#include "core/fht.h" //stack allocated and convenience
#include "core/fht.h" //stack allocated and convenience
#include "engines/engine_fwd.h"
#include <QPixmap> //stack allocated and convenience
#include <QPixmap> //stack allocated and convenience
#include <QBasicTimer> //stack allocated
#include <QWidget> //baseclass
#include <vector> //included for convenience
#include <QWidget> //baseclass
#include <vector> //included for convenience
#include <QGLWidget> //baseclass
#include <QGLWidget> //baseclass
#ifdef Q_WS_MACX
#include <OpenGL/gl.h> //included for convenience
#include <OpenGL/glu.h> //included for convenience
@ -29,63 +28,60 @@ class QEvent;
class QPaintEvent;
class QResizeEvent;
namespace Analyzer {
typedef std::vector<float> Scope;
class Base : public QWidget
{
class Base : public QWidget {
Q_OBJECT
public:
~Base() { delete m_fht; }
public:
~Base() { delete m_fht; }
uint timeout() const { return m_timeout; }
uint timeout() const { return m_timeout; }
void set_engine(EngineBase* engine) { m_engine = engine; }
void set_engine(EngineBase* engine) { m_engine = engine; }
void changeTimeout( uint newTimeout ) {
m_timeout = newTimeout;
if (m_timer.isActive()) {
m_timer.stop();
m_timer.start(m_timeout, this);
}
void changeTimeout(uint newTimeout) {
m_timeout = newTimeout;
if (m_timer.isActive()) {
m_timer.stop();
m_timer.start(m_timeout, this);
}
}
protected:
Base( QWidget*, uint scopeSize = 7 );
protected:
Base(QWidget*, uint scopeSize = 7);
void hideEvent(QHideEvent *);
void showEvent(QShowEvent *);
void paintEvent( QPaintEvent* );
void timerEvent(QTimerEvent *);
void hideEvent(QHideEvent*);
void showEvent(QShowEvent*);
void paintEvent(QPaintEvent*);
void timerEvent(QTimerEvent*);
void polishEvent();
void polishEvent();
int resizeExponent( int );
int resizeForBands( int );
virtual void init() {}
virtual void transform( Scope& );
virtual void analyze( QPainter& p, const Scope&, bool new_frame) = 0;
virtual void demo(QPainter& p);
int resizeExponent(int);
int resizeForBands(int);
virtual void init() {}
virtual void transform(Scope&);
virtual void analyze(QPainter& p, const Scope&, bool new_frame) = 0;
virtual void demo(QPainter& p);
protected:
QBasicTimer m_timer;
uint m_timeout;
FHT *m_fht;
EngineBase* m_engine;
Scope m_lastScope;
protected:
QBasicTimer m_timer;
uint m_timeout;
FHT* m_fht;
EngineBase* m_engine;
Scope m_lastScope;
bool new_frame_;
bool is_playing_;
bool new_frame_;
bool is_playing_;
};
void interpolate(const Scope&, Scope&);
void initSin(Scope&, const uint = 6000);
void interpolate( const Scope&, Scope& );
void initSin( Scope&, const uint = 6000 );
} //END namespace Analyzer
} // END namespace Analyzer
using Analyzer::Scope;

View File

@ -39,21 +39,20 @@ const int AnalyzerContainer::kMediumFramerate = 25;
const int AnalyzerContainer::kHighFramerate = 30;
const int AnalyzerContainer::kSuperHighFramerate = 60;
AnalyzerContainer::AnalyzerContainer(QWidget *parent)
: QWidget(parent),
current_framerate_(kMediumFramerate),
context_menu_(new QMenu(this)),
context_menu_framerate_(new QMenu(tr("Framerate"), this)),
group_(new QActionGroup(this)),
group_framerate_(new QActionGroup(this)),
mapper_(new QSignalMapper(this)),
mapper_framerate_(new QSignalMapper(this)),
visualisation_action_(nullptr),
double_click_timer_(new QTimer(this)),
ignore_next_click_(false),
current_analyzer_(nullptr),
engine_(nullptr)
{
AnalyzerContainer::AnalyzerContainer(QWidget* parent)
: QWidget(parent),
current_framerate_(kMediumFramerate),
context_menu_(new QMenu(this)),
context_menu_framerate_(new QMenu(tr("Framerate"), this)),
group_(new QActionGroup(this)),
group_framerate_(new QActionGroup(this)),
mapper_(new QSignalMapper(this)),
mapper_framerate_(new QSignalMapper(this)),
visualisation_action_(nullptr),
double_click_timer_(new QTimer(this)),
ignore_next_click_(false),
current_analyzer_(nullptr),
engine_(nullptr) {
QHBoxLayout* layout = new QHBoxLayout(this);
setLayout(layout);
layout->setContentsMargins(0, 0, 0, 0);
@ -62,7 +61,8 @@ AnalyzerContainer::AnalyzerContainer(QWidget *parent)
AddFramerate(tr("Low (%1 fps)").arg(kLowFramerate), kLowFramerate);
AddFramerate(tr("Medium (%1 fps)").arg(kMediumFramerate), kMediumFramerate);
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)));
context_menu_->addMenu(context_menu_framerate_);
@ -76,8 +76,8 @@ AnalyzerContainer::AnalyzerContainer(QWidget *parent)
AddAnalyzerType<NyanCatAnalyzer>();
connect(mapper_, SIGNAL(mapped(int)), SLOT(ChangeAnalyzer(int)));
disable_action_ =
context_menu_->addAction(tr("No analyzer"), this, SLOT(DisableAnalyzer()));
disable_action_ = context_menu_->addAction(tr("No analyzer"), this,
SLOT(DisableAnalyzer()));
disable_action_->setCheckable(true);
group_->addAction(disable_action_);
@ -115,12 +115,11 @@ void AnalyzerContainer::ShowPopupMenu() {
context_menu_->popup(last_click_pos_);
}
void AnalyzerContainer::mouseDoubleClickEvent(QMouseEvent *) {
void AnalyzerContainer::mouseDoubleClickEvent(QMouseEvent*) {
double_click_timer_->stop();
ignore_next_click_ = true;
if (visualisation_action_)
visualisation_action_->trigger();
if (visualisation_action_) visualisation_action_->trigger();
}
void AnalyzerContainer::wheelEvent(QWheelEvent* e) {
@ -128,8 +127,7 @@ void AnalyzerContainer::wheelEvent(QWheelEvent* e) {
}
void AnalyzerContainer::SetEngine(EngineBase* engine) {
if (current_analyzer_)
current_analyzer_->set_engine(engine);
if (current_analyzer_) current_analyzer_->set_engine(engine);
engine_ = engine;
}
@ -144,7 +142,8 @@ void AnalyzerContainer::ChangeAnalyzer(int id) {
QObject* instance = analyzer_types_[id]->newInstance(Q_ARG(QWidget*, this));
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;
}
@ -152,7 +151,8 @@ void AnalyzerContainer::ChangeAnalyzer(int id) {
current_analyzer_ = qobject_cast<Analyzer::Base*>(instance);
current_analyzer_->set_engine(engine_);
// 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_);
layout()->addWidget(current_analyzer_);
@ -161,7 +161,7 @@ void AnalyzerContainer::ChangeAnalyzer(int id) {
}
void AnalyzerContainer::ChangeFramerate(int new_framerate) {
if(current_analyzer_) {
if (current_analyzer_) {
// Even if it is not supposed to happen, I don't want to get a dbz error
new_framerate = new_framerate == 0 ? kMediumFramerate : new_framerate;
current_analyzer_->changeTimeout(1000 / new_framerate);
@ -179,7 +179,7 @@ void AnalyzerContainer::Load() {
DisableAnalyzer();
disable_action_->setChecked(true);
} else {
for (int i=0 ; i<analyzer_types_.count() ; ++i) {
for (int i = 0; i < analyzer_types_.count(); ++i) {
if (type == analyzer_types_[i]->className()) {
ChangeAnalyzer(i);
actions_[i]->setChecked(true);
@ -190,8 +190,8 @@ void AnalyzerContainer::Load() {
// Framerate
current_framerate_ = s.value(kSettingsFramerate, kMediumFramerate).toInt();
for (int i=0 ; i<framerate_list_.count() ; ++i) {
if(current_framerate_ == framerate_list_[i]) {
for (int i = 0; i < framerate_list_.count(); ++i) {
if (current_framerate_ == framerate_list_[i]) {
ChangeFramerate(current_framerate_);
group_framerate_->actions()[i]->setChecked(true);
break;
@ -200,8 +200,8 @@ void AnalyzerContainer::Load() {
}
void AnalyzerContainer::SaveFramerate(int framerate) {
// For now, framerate is common for all analyzers. Maybe each analyzer should
// have its own framerate?
// For now, framerate is common for all analyzers. Maybe each analyzer should
// have its own framerate?
current_framerate_ = framerate;
QSettings s;
s.beginGroup(kSettingsGroup);
@ -212,13 +212,14 @@ void AnalyzerContainer::Save() {
QSettings s;
s.beginGroup(kSettingsGroup);
s.setValue("type", current_analyzer_ ?
current_analyzer_->metaObject()->className() :
QVariant());
s.setValue("type", current_analyzer_
? current_analyzer_->metaObject()->className()
: QVariant());
}
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);
group_framerate_->addAction(action);
framerate_list_ << framerate;

View File

@ -28,7 +28,7 @@
class AnalyzerContainer : public QWidget {
Q_OBJECT
public:
public:
AnalyzerContainer(QWidget* parent);
void SetEngine(EngineBase* engine);
@ -40,18 +40,18 @@ public:
signals:
void WheelEvent(int delta);
protected:
void mouseReleaseEvent(QMouseEvent *);
void mouseDoubleClickEvent(QMouseEvent *);
protected:
void mouseReleaseEvent(QMouseEvent*);
void mouseDoubleClickEvent(QMouseEvent*);
void wheelEvent(QWheelEvent* e);
private slots:
private slots:
void ChangeAnalyzer(int id);
void ChangeFramerate(int new_framerate);
void DisableAnalyzer();
void ShowPopupMenu();
private:
private:
static const int kLowFramerate;
static const int kMediumFramerate;
static const int kHighFramerate;
@ -61,11 +61,11 @@ private:
void Save();
void SaveFramerate(int framerate);
template <typename T>
void AddAnalyzerType();
void AddAnalyzerType();
void AddFramerate(const QString& name, int framerate);
private:
int current_framerate_; // fps
private:
int current_framerate_; // fps
QMenu* context_menu_;
QMenu* context_menu_framerate_;
QActionGroup* group_;
@ -88,11 +88,12 @@ private:
};
template <typename T>
void AnalyzerContainer::AddAnalyzerType() {
void AnalyzerContainer::AddAnalyzerType() {
int id = analyzer_types_.count();
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);
mapper_->setMapping(action, id);
action->setCheckable(true);
@ -100,4 +101,3 @@ template <typename T>
}
#endif

View File

@ -12,155 +12,150 @@
//
#include "baranalyzer.h"
#include <cmath> //log10(), etc.
#include <cmath> //log10(), etc.
#include <QtDebug>
#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 )
: Analyzer::Base( parent, 8 )
//, m_bands( BAND_COUNT )
//, barVector( BAND_COUNT, 0 )
//, roofVector( BAND_COUNT, 50 )
//, roofVelocityVector( BAND_COUNT, ROOF_VELOCITY_REDUCTION_FACTOR )
BarAnalyzer::BarAnalyzer(QWidget* parent)
: Analyzer::Base(parent, 8)
//, m_bands( BAND_COUNT )
//, barVector( BAND_COUNT, 0 )
//, roofVector( BAND_COUNT, 50 )
//, roofVelocityVector( BAND_COUNT, ROOF_VELOCITY_REDUCTION_FACTOR )
{
//roof pixmaps don't depend on size() so we do in the ctor
m_bg = parent->palette().color(QPalette::Background);
// roof pixmaps don't depend on size() so we do in the ctor
m_bg = parent->palette().color(QPalette::Background);
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 dg = double(m_bg.green() - fg.green()) / (NUM_ROOFS-1);
double db = double(m_bg.blue() - fg.blue()) / (NUM_ROOFS-1);
for ( uint i = 0; i < NUM_ROOFS; ++i )
{
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) ) );
}
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 db = double(m_bg.blue() - fg.blue()) / (NUM_ROOFS - 1);
for (uint i = 0; i < NUM_ROOFS; ++i) {
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)));
}
}
void BarAnalyzer::resizeEvent( QResizeEvent * e )
{
init();
}
void BarAnalyzer::resizeEvent(QResizeEvent* e) { init(); }
// METHODS =====================================================
void BarAnalyzer::init()
{
const double MAX_AMPLITUDE = 1.0;
const double F = double(height() - 2) / (log10( 255 ) * MAX_AMPLITUDE );
void BarAnalyzer::init() {
const double MAX_AMPLITUDE = 1.0;
const double F = double(height() - 2) / (log10(255) * MAX_AMPLITUDE);
BAND_COUNT = width() / 5;
MAX_DOWN = int(0 - (qMax(1, height() / 50)));
MAX_UP = int(qMax(1, height() / 25));
BAND_COUNT = width() / 5;
MAX_DOWN = int(0 -(qMax(1, height() / 50)));
MAX_UP = int(qMax(1, height() / 25));
barVector.resize(BAND_COUNT, 0);
roofVector.resize(BAND_COUNT, height() - 5);
roofVelocityVector.resize(BAND_COUNT, ROOF_VELOCITY_REDUCTION_FACTOR);
m_roofMem.resize(BAND_COUNT);
m_scope.resize(BAND_COUNT);
barVector.resize( BAND_COUNT, 0 );
roofVector.resize( BAND_COUNT, height() -5 );
roofVelocityVector.resize( BAND_COUNT, ROOF_VELOCITY_REDUCTION_FACTOR );
m_roofMem.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
for (uint x = 0; x < 256; ++x) {
m_lvlMapper[x] = uint(F * log10(x + 1));
}
//generate a list of values that express amplitudes in range 0-MAX_AMP as ints from 0-height() on log scale
for ( uint x = 0; x < 256; ++x )
{
m_lvlMapper[x] = uint( F * log10( x+1 ) );
m_pixBarGradient = QPixmap(height() * COLUMN_WIDTH, height());
m_pixCompose = QPixmap(size());
QPainter p(&m_pixBarGradient);
for (int x = 0, r = 0x40, g = 0x30, b = 0xff, r2 = 255 - r; x < height();
++x) {
for (int y = x; y > 0; --y) {
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));
p.drawLine(x * COLUMN_WIDTH, height() - y, (x + 1) * COLUMN_WIDTH,
height() - y);
}
}
m_pixBarGradient = QPixmap( height()*COLUMN_WIDTH, height() );
m_pixCompose = QPixmap( size() );
QPainter p( &m_pixBarGradient );
for ( int x=0, r=0x40, g=0x30, b=0xff, r2=255-r;
x < height(); ++x )
{
for ( int y = x; y > 0; --y )
{
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 ) );
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) {
// Analyzer::interpolate( s, m_bands );
void BarAnalyzer::analyze( QPainter& p, const Scope &s, bool new_frame)
{
//Analyzer::interpolate( s, m_bands );
Scope& v = m_scope;
Analyzer::interpolate(s, v);
Scope &v = m_scope;
Analyzer::interpolate( s, v );
for (uint i = 0, x = 0, y2; i < v.size(); ++i, x += COLUMN_WIDTH + 1) {
// assign pre[log10]'d value
y2 = uint(v[i] *
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()
for ( uint i = 0, x = 0, y2; i < v.size(); ++i, x+=COLUMN_WIDTH+1 )
{
//assign pre[log10]'d value
y2 = uint(v[i] * 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
// we have the following:
// 1. don't adjust shift when doing small up movements
// 2. shift large upwards with a bias towards last value
// 3. fall downwards at a constant pace
//using the best of Markey's, piggz and Max's ideas on the way to shift the bars
//we have the following:
// 1. don't adjust shift when doing small up movements
// 2. shift large upwards with a bias towards last value
// 3. fall downwards at a constant pace
/*if ( change > MAX_UP ) //anything too much greater than 2 gives "jitter"
/*if ( change > MAX_UP ) //anything too much greater than 2 gives "jitter"
//add some dynamics - makes the value slightly closer to what it was last time
y2 = ( barVector[i] + MAX_UP );
//y2 = ( barVector[i] * 2 + y2 ) / 3;
else*/ if ( change < MAX_DOWN )
y2 = barVector[i] + MAX_DOWN;
else*/ if (change <
MAX_DOWN)
y2 = barVector[i] + MAX_DOWN;
if ( (int)y2 > roofVector[i] )
{
roofVector[i] = (int)y2;
roofVelocityVector[i] = 1;
}
//remember where we are
barVector[i] = y2;
if ( m_roofMem[i].size() > NUM_ROOFS )
m_roofMem[i].erase( m_roofMem[i].begin() );
//blt last n roofs, a.k.a motion blur
for ( uint c = 0; c < m_roofMem[i].size(); ++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 ] );
p.drawPixmap(x, m_roofMem[i][c], m_pixRoof[ NUM_ROOFS - 1 - c ]);
//blt the bar
p.drawPixmap(x, height() - y2,
*gradient(), y2 * COLUMN_WIDTH, height() - y2, COLUMN_WIDTH, y2);
/*bitBlt( canvas(), x, height() - y2,
gradient(), y2 * COLUMN_WIDTH, height() - y2, COLUMN_WIDTH, y2, Qt::CopyROP );*/
m_roofMem[i].push_back( height() - roofVector[i] - 2 );
//set roof parameters for the NEXT draw
if ( roofVelocityVector[i] != 0 )
{
if ( roofVelocityVector[i] > 32 ) //no reason to do == 32
roofVector[i] -= (roofVelocityVector[i] - 32) / 20; //trivial calculation
if ( roofVector[i] < 0 )
{
roofVector[i] = 0; //not strictly necessary
roofVelocityVector[i] = 0;
}
else ++roofVelocityVector[i];
}
if ((int)y2 > roofVector[i]) {
roofVector[i] = (int)y2;
roofVelocityVector[i] = 1;
}
// remember where we are
barVector[i] = y2;
if (m_roofMem[i].size() > NUM_ROOFS)
m_roofMem[i].erase(m_roofMem[i].begin());
// blt last n roofs, a.k.a motion blur
for (uint c = 0; c < m_roofMem[i].size(); ++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 ]
// );
p.drawPixmap(x, m_roofMem[i][c], m_pixRoof[NUM_ROOFS - 1 - c]);
// blt the bar
p.drawPixmap(x, height() - y2, *gradient(), y2 * COLUMN_WIDTH,
height() - y2, COLUMN_WIDTH, y2);
/*bitBlt( canvas(), x, height() - y2,
gradient(), y2 * COLUMN_WIDTH, height() - y2, COLUMN_WIDTH, y2,
Qt::CopyROP );*/
m_roofMem[i].push_back(height() - roofVector[i] - 2);
// set roof parameters for the NEXT draw
if (roofVelocityVector[i] != 0) {
if (roofVelocityVector[i] > 32) // no reason to do == 32
roofVector[i] -=
(roofVelocityVector[i] - 32) / 20; // trivial calculation
if (roofVector[i] < 0) {
roofVector[i] = 0; // not strictly necessary
roofVelocityVector[i] = 0;
} else
++roofVelocityVector[i];
}
}
}

View File

@ -10,51 +10,50 @@
typedef std::vector<uint> aroofMemVec;
class BarAnalyzer : public Analyzer::Base
{
class BarAnalyzer : public Analyzer::Base {
Q_OBJECT
public:
Q_INVOKABLE BarAnalyzer( QWidget* );
public:
Q_INVOKABLE BarAnalyzer(QWidget*);
void init();
virtual void analyze( QPainter& p, const Scope&, bool new_frame);
//virtual void transform( Scope& );
void init();
virtual void analyze(QPainter& p, const Scope&, bool new_frame);
// virtual void transform( Scope& );
/**
* Resizes the widget to a new geometry according to @p e
* @param e The resize-event
*/
void resizeEvent( QResizeEvent * e);
/**
* Resizes the widget to a new geometry according to @p e
* @param e The resize-event
*/
void resizeEvent(QResizeEvent* e);
uint BAND_COUNT;
int MAX_DOWN;
int MAX_UP;
static const uint ROOF_HOLD_TIME = 48;
static const int ROOF_VELOCITY_REDUCTION_FACTOR = 32;
static const uint NUM_ROOFS = 16;
static const uint COLUMN_WIDTH = 4;
uint BAND_COUNT;
int MAX_DOWN;
int MAX_UP;
static const uint ROOF_HOLD_TIME = 48;
static const int ROOF_VELOCITY_REDUCTION_FACTOR = 32;
static const uint NUM_ROOFS = 16;
static const uint COLUMN_WIDTH = 4;
static const char* kName;
static const char* kName;
protected:
QPixmap m_pixRoof[NUM_ROOFS];
//vector<uint> m_roofMem[BAND_COUNT];
protected:
QPixmap m_pixRoof[NUM_ROOFS];
// vector<uint> m_roofMem[BAND_COUNT];
//Scope m_bands; //copy of the Scope to prevent creating/destroying a Scope every iteration
uint m_lvlMapper[256];
std::vector<aroofMemVec> m_roofMem;
std::vector<uint> barVector; //positions of bars
std::vector<int> roofVector; //positions of roofs
std::vector<uint> roofVelocityVector; //speed that roofs falls
// Scope m_bands; //copy of the Scope to prevent creating/destroying a Scope
// every iteration
uint m_lvlMapper[256];
std::vector<aroofMemVec> m_roofMem;
std::vector<uint> barVector; // positions of bars
std::vector<int> roofVector; // positions of roofs
std::vector<uint> roofVelocityVector; // speed that roofs falls
const QPixmap *gradient() const { return &m_pixBarGradient; }
const QPixmap* gradient() const { return &m_pixBarGradient; }
private:
QPixmap m_pixBarGradient;
QPixmap m_pixCompose;
Scope m_scope; //so we don't create a vector every frame
QColor m_bg;
private:
QPixmap m_pixBarGradient;
QPixmap m_pixCompose;
Scope m_scope; // so we don't create a vector every frame
QColor m_bg;
};
#endif

View File

@ -12,389 +12,392 @@
#include <cstdlib>
#include <QPainter>
const uint BlockAnalyzer::HEIGHT = 2;
const uint BlockAnalyzer::WIDTH = 4;
const uint BlockAnalyzer::MIN_ROWS = 3; //arbituary
const uint BlockAnalyzer::MIN_COLUMNS = 32; //arbituary
const uint BlockAnalyzer::MAX_COLUMNS = 256; //must be 2**n
const uint BlockAnalyzer::FADE_SIZE = 90;
const uint BlockAnalyzer::HEIGHT = 2;
const uint BlockAnalyzer::WIDTH = 4;
const uint BlockAnalyzer::MIN_ROWS = 3; // arbituary
const uint BlockAnalyzer::MIN_COLUMNS = 32; // arbituary
const uint BlockAnalyzer::MAX_COLUMNS = 256; // must be 2**n
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 )
: Analyzer::Base( parent, 9 )
, m_columns( 0 ) //uint
, m_rows( 0 ) //uint
, m_y( 0 ) //uint
, m_barPixmap( 1, 1 ) //null qpixmaps cause crashes
, m_topBarPixmap( WIDTH, HEIGHT )
, 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>
BlockAnalyzer::BlockAnalyzer(QWidget* parent)
: Analyzer::Base(parent, 9),
m_columns(0) // uint
,
m_rows(0) // uint
,
m_y(0) // uint
,
m_barPixmap(1, 1) // null qpixmaps cause crashes
,
m_topBarPixmap(WIDTH, HEIGHT),
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
setMaximumWidth( MAX_COLUMNS*(WIDTH+1) -1 );
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);
// mxcl says null pixmaps cause crashes, so let's play it safe
for ( uint i = 0; i < FADE_SIZE; ++i )
m_fade_bars[i] = QPixmap( 1, 1 );
// mxcl says null pixmaps cause crashes, so let's play it safe
for (uint i = 0; i < FADE_SIZE; ++i) m_fade_bars[i] = QPixmap(1, 1);
}
BlockAnalyzer::~BlockAnalyzer()
{
BlockAnalyzer::~BlockAnalyzer() {}
void BlockAnalyzer::resizeEvent(QResizeEvent* e) {
QWidget::resizeEvent(e);
m_background = QPixmap(size());
const uint oldRows = m_rows;
// all is explained in analyze()..
//+1 to counter -1 in maxSizes, trust me we need this!
m_columns = qMax(uint(double(width() + 1) / (WIDTH + 1)), MAX_COLUMNS);
m_rows = uint(double(height() + 1) / (HEIGHT + 1));
// this is the y-offset for drawing from the top of the widget
m_y = (height() - (m_rows * (HEIGHT + 1)) + 2) / 2;
m_scope.resize(m_columns);
if (m_rows != oldRows) {
m_barPixmap = QPixmap(WIDTH, m_rows * (HEIGHT + 1));
for (uint i = 0; i < FADE_SIZE; ++i)
m_fade_bars[i] = QPixmap(WIDTH, m_rows * (HEIGHT + 1));
m_yscale.resize(m_rows + 1);
const uint PRE = 1,
PRO = 1; // PRE and PRO allow us to restrict the range somewhat
for (uint z = 0; z < m_rows; ++z)
m_yscale[z] = 1 - (log10(PRE + z) / log10(PRE + m_rows + PRO));
m_yscale[m_rows] = 0;
determineStep();
paletteChange(palette());
}
drawBackground();
}
void
BlockAnalyzer::resizeEvent( QResizeEvent *e )
{
QWidget::resizeEvent( e );
void BlockAnalyzer::determineStep() {
// 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
m_background = QPixmap(size());
const uint oldRows = m_rows;
//all is explained in analyze()..
//+1 to counter -1 in maxSizes, trust me we need this!
m_columns = qMax( uint(double(width()+1) / (WIDTH+1)), MAX_COLUMNS );
m_rows = uint(double(height()+1) / (HEIGHT+1));
//this is the y-offset for drawing from the top of the widget
m_y = (height() - (m_rows * (HEIGHT+1)) + 2) / 2;
m_scope.resize( m_columns );
if( m_rows != oldRows ) {
m_barPixmap = QPixmap( WIDTH, m_rows*(HEIGHT+1) );
for ( uint i = 0; i < FADE_SIZE; ++i )
m_fade_bars[i] = QPixmap( WIDTH, m_rows*(HEIGHT+1) );
m_yscale.resize( m_rows + 1 );
const uint PRE = 1, PRO = 1; //PRE and PRO allow us to restrict the range somewhat
for( uint z = 0; z < m_rows; ++z )
m_yscale[z] = 1 - (log10( PRE+z ) / log10( PRE+m_rows+PRO ));
m_yscale[m_rows] = 0;
determineStep();
paletteChange( palette() );
}
drawBackground();
const double fallTime = 30 * m_rows;
m_step = double(m_rows * timeout()) / fallTime;
}
void
BlockAnalyzer::determineStep()
void BlockAnalyzer::transform(Analyzer::Scope& s) // pure virtual
{
// 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
for (uint x = 0; x < s.size(); ++x) s[x] *= 2;
const double fallTime = 30 * m_rows;
m_step = double(m_rows * timeout()) / fallTime;
float* front = static_cast<float*>(&s.front());
m_fht->spectrum(front);
m_fht->scale(front, 1.0 / 20);
// the second half is pretty dull, so only show it if the user has a large
// analyzer
// 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
BlockAnalyzer::transform( Analyzer::Scope &s ) //pure virtual
{
for( uint x = 0; x < s.size(); ++x )
s[x] *= 2;
void BlockAnalyzer::analyze(QPainter& p, const Analyzer::Scope& s,
bool new_frame) {
// y = 2 3 2 1 0 2
// . . . . # .
// . . . # # .
// # . # # # #
// # # # # # #
//
// visual aid for how this analyzer works.
// y represents the number of blanks
// y starts from the top and increases in units of blocks
float *front = static_cast<float*>( &s.front() );
// m_yscale looks similar to: { 0.7, 0.5, 0.25, 0.15, 0.1, 0 }
// if it contains 6 elements there are 5 rows in the analyzer
m_fht->spectrum( front );
m_fht->scale( front, 1.0 / 20 );
Analyzer::interpolate(s, m_scope);
//the second half is pretty dull, so only show it if the user has a large analyzer
//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() );
}
// Paint the background
p.drawPixmap(0, 0, m_background);
void
BlockAnalyzer::analyze( QPainter& p, const Analyzer::Scope &s, bool new_frame)
{
// y = 2 3 2 1 0 2
// . . . . # .
// . . . # # .
// # . # # # #
// # # # # # #
//
// visual aid for how this analyzer works.
// y represents the number of blanks
// y starts from the top and increases in units of blocks
for (uint y, x = 0; x < m_scope.size(); ++x) {
// determine y
for (y = 0; m_scope[x] < m_yscale[y]; ++y)
;
// m_yscale looks similar to: { 0.7, 0.5, 0.25, 0.15, 0.1, 0 }
// if it contains 6 elements there are 5 rows in the analyzer
// this is opposite to what you'd think, higher than y
// means the bar is lower than y (physically)
if ((float)y > m_store[x])
y = int(m_store[x] += m_step);
else
m_store[x] = y;
Analyzer::interpolate( s, m_scope );
// Paint the background
p.drawPixmap(0, 0, m_background);
for( uint y, x = 0; x < m_scope.size(); ++x )
{
// determine y
for( y = 0; m_scope[x] < m_yscale[y]; ++y )
;
// this is opposite to what you'd think, higher than y
// means the bar is lower than y (physically)
if( (float)y > m_store[x] )
y = int(m_store[x] += m_step);
else
m_store[x] = y;
// 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( y <= m_fade_pos[x] /*|| m_fade_intensity[x] < FADE_SIZE / 3*/ ) {
m_fade_pos[x] = y;
m_fade_intensity[x] = FADE_SIZE;
}
if( m_fade_intensity[x] > 0 ) {
const uint offset = --m_fade_intensity[x];
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);
}
if( m_fade_intensity[x] == 0 )
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
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 )
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 )
{
// with a range of 0-255 and maximum adjustment of amount,
// maximise the difference between f and b
if( b < f ) {
if( b > 255 - f ) {
amount -= f;
f = 0;
} else {
amount -= (255 - f);
f = 255;
}
// 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 (y <= m_fade_pos[x] /*|| m_fade_intensity[x] < FADE_SIZE / 3*/) {
m_fade_pos[x] = y;
m_fade_intensity[x] = FADE_SIZE;
}
else {
if( f > 255 - b ) {
amount -= f;
f = 0;
} else {
amount -= (255 - f);
f = 255;
}
if (m_fade_intensity[x] > 0) {
const uint offset = --m_fade_intensity[x];
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);
}
if (m_fade_intensity[x] == 0) 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
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)
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) {
// with a range of 0-255 and maximum adjustment of amount,
// maximise the difference between f and b
if (b < f) {
if (b > 255 - f) {
amount -= f;
f = 0;
} else {
amount -= (255 - f);
f = 255;
}
} else {
if (f > 255 - b) {
amount -= f;
f = 0;
} else {
amount -= (255 - f);
f = 255;
}
}
}
/**
* 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
* @return the adjusted form of fg
*/
QColor
ensureContrast( const QColor &bg, const QColor &fg, uint _amount = 150 )
{
class OutputOnExit {
public:
OutputOnExit( const QColor &color ) : c( color ) {}
~OutputOnExit() { int h,s,v; c.getHsv( &h, &s, &v ); }
private:
const QColor &c;
};
// hack so I don't have to cast everywhere
#define amount static_cast<int>(_amount)
// #define STAMP debug() << (QValueList<int>() << fh << fs << fv) << endl;
// #define STAMP1( string ) debug() << string << ": " << (QValueList<int>() << fh << fs << fv) << endl;
// #define STAMP2( string, value ) debug() << string << "=" << value << ": " << (QValueList<int>() << fh << fs << fv) << endl;
OutputOnExit allocateOnTheStack( fg );
int bh, bs, bv;
int fh, fs, fv;
bg.getHsv( &bh, &bs, &bv );
fg.getHsv( &fh, &fs, &fv );
int dv = abs( bv - fv );
// STAMP2( "DV", dv );
// value is the best measure of contrast
// if there is enough difference in value already, return fg unchanged
if( dv > amount )
return fg;
int ds = abs( bs - fs );
// STAMP2( "DS", ds );
// saturation is good enough too. But not as good. TODO adapt this a little
if( ds > amount )
return fg;
int dh = abs( bh - fh );
// STAMP2( "DH", dh );
if( dh > 120 ) {
// a third of the colour wheel automatically guarentees contrast
// but only if the values are high enough and saturations significant enough
// to allow the colours to be visible and not be shades of grey or black
// check the saturation for the two colours is sufficient that hue alone can
// provide sufficient contrast
if( ds > amount / 2 && (bs > 125 && fs > 125) )
// STAMP1( "Sufficient saturation difference, and hues are compliemtary" );
return fg;
else if( dv > amount / 2 && (bv > 125 && fv > 125) )
// STAMP1( "Sufficient value difference, and hues are compliemtary" );
return fg;
// 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
//so we need to adjust the system, although not as much
///_amount /= 2;
QColor ensureContrast(const QColor& bg, const QColor& fg, uint _amount = 150) {
class OutputOnExit {
public:
OutputOnExit(const QColor& color) : c(color) {}
~OutputOnExit() {
int h, s, v;
c.getHsv(&h, &s, &v);
}
if( fs < 50 && ds < 40 ) {
// low saturation on a low saturation is sad
const int tmp = 50 - fs;
fs = 50;
if( amount > tmp )
_amount -= tmp;
else
_amount = 0;
}
private:
const QColor& c;
};
// test that there is available value to honor our contrast requirement
if( 255 - dv < amount )
{
// we have to modify the value and saturation of fg
//adjustToLimits( bv, fv, amount );
// hack so I don't have to cast everywhere
#define amount static_cast<int>(_amount)
// #define STAMP debug() << (QValueList<int>() << fh << fs << fv) << endl;
// #define STAMP1( string ) debug() << string << ": " <<
// (QValueList<int>() << fh << fs << fv) << endl;
// #define STAMP2( string, value ) debug() << string << "=" << value << ":
// " << (QValueList<int>() << fh << fs << fv) << endl;
// STAMP
OutputOnExit allocateOnTheStack(fg);
// see if we need to adjust the saturation
if( amount > 0 )
adjustToLimits( bs, fs, _amount );
int bh, bs, bv;
int fh, fs, fv;
// STAMP
bg.getHsv(&bh, &bs, &bv);
fg.getHsv(&fh, &fs, &fv);
// see if we need to adjust the hue
if( amount > 0 )
fh += amount; // cycles around;
int dv = abs(bv - fv);
// STAMP
// STAMP2( "DV", dv );
return QColor::fromHsv(fh, fs, fv);
}
// value is the best measure of contrast
// if there is enough difference in value already, return fg unchanged
if (dv > amount) return fg;
// STAMP
int ds = abs(bs - fs);
if( fv > bv && bv > amount )
return QColor::fromHsv( fh, fs, bv - amount);
// STAMP2( "DS", ds );
// STAMP
// saturation is good enough too. But not as good. TODO adapt this a little
if (ds > amount) return fg;
if( fv < bv && fv > amount )
return QColor::fromHsv( fh, fs, fv - amount);
int dh = abs(bh - fh);
// STAMP
// STAMP2( "DH", dh );
if( fv > bv && (255 - fv > amount) )
return QColor::fromHsv( fh, fs, fv + amount);
if (dh > 120) {
// a third of the colour wheel automatically guarentees contrast
// but only if the values are high enough and saturations significant enough
// to allow the colours to be visible and not be shades of grey or black
// STAMP
// check the saturation for the two colours is sufficient that hue alone can
// provide sufficient contrast
if (ds > amount / 2 && (bs > 125 && fs > 125))
// STAMP1( "Sufficient saturation difference, and hues are
// compliemtary" );
return fg;
else if (dv > amount / 2 && (bv > 125 && fv > 125))
// STAMP1( "Sufficient value difference, and hues are
// compliemtary" );
return fg;
if( fv < bv && (255 - bv > amount ) )
return QColor::fromHsv( fh, fs, bv + amount);
// STAMP1( "Hues are complimentary but we must modify the value or
// saturation of the contrasting colour" );
// STAMP
// debug() << "Something went wrong!\n";
// but either the colours are two desaturated, or too dark
// so we need to adjust the system, although not as much
///_amount /= 2;
}
return Qt::blue;
if (fs < 50 && ds < 40) {
// low saturation on a low saturation is sad
const int tmp = 50 - fs;
fs = 50;
if (amount > tmp)
_amount -= tmp;
else
_amount = 0;
}
#undef amount
// #undef STAMP
// test that there is available value to honor our contrast requirement
if (255 - dv < amount) {
// we have to modify the value and saturation of fg
// adjustToLimits( bv, fv, amount );
// STAMP
// see if we need to adjust the saturation
if (amount > 0) adjustToLimits(bs, fs, _amount);
// STAMP
// see if we need to adjust the hue
if (amount > 0) fh += amount; // cycles around;
// STAMP
return QColor::fromHsv(fh, fs, fv);
}
// STAMP
if (fv > bv && bv > amount) return QColor::fromHsv(fh, fs, bv - amount);
// STAMP
if (fv < bv && fv > amount) return QColor::fromHsv(fh, fs, fv - amount);
// STAMP
if (fv > bv && (255 - fv > amount))
return QColor::fromHsv(fh, fs, fv + amount);
// STAMP
if (fv < bv && (255 - bv > amount))
return QColor::fromHsv(fh, fs, bv + amount);
// STAMP
// debug() << "Something went wrong!\n";
return Qt::blue;
#undef amount
// #undef STAMP
}
void
BlockAnalyzer::paletteChange( const QPalette& ) //virtual
void BlockAnalyzer::paletteChange(const QPalette&) // virtual
{
const QColor bg = palette().color(QPalette::Background);
const QColor fg = ensureContrast( bg, palette().color(QPalette::Highlight) );
const QColor bg = palette().color(QPalette::Background);
const QColor fg = ensureContrast(bg, palette().color(QPalette::Highlight));
m_topBarPixmap.fill( fg );
m_topBarPixmap.fill(fg);
const double dr = 15*double(bg.red() - fg.red()) / (m_rows*16);
const double dg = 15*double(bg.green() - fg.green()) / (m_rows*16);
const double db = 15*double(bg.blue() - fg.blue()) / (m_rows*16);
const int r = fg.red(), g = fg.green(), b = fg.blue();
const double dr = 15 * double(bg.red() - fg.red()) / (m_rows * 16);
const double dg = 15 * double(bg.green() - fg.green()) / (m_rows * 16);
const double db = 15 * double(bg.blue() - fg.blue()) / (m_rows * 16);
const int r = fg.red(), g = fg.green(), b = fg.blue();
bar()->fill( bg );
bar()->fill(bg);
QPainter p( bar() );
for( int y = 0; (uint)y < m_rows; ++y )
//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) ) );
QPainter p(bar());
for (int y = 0; (uint)y < m_rows; ++y)
// 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)));
{
const QColor bg = palette().color(QPalette::Background).dark( 112 );
{
const QColor bg = palette().color(QPalette::Background).dark(112);
//make a complimentary fadebar colour
//TODO dark is not always correct, dumbo!
int h,s,v; palette().color(QPalette::Background).dark( 150 ).getHsv( &h, &s, &v );
const QColor fg( QColor::fromHsv(h + 120, s, v));
// make a complimentary fadebar colour
// TODO dark is not always correct, dumbo!
int h, s, v;
palette().color(QPalette::Background).dark(150).getHsv(&h, &s, &v);
const QColor fg(QColor::fromHsv(h + 120, s, v));
const double dr = fg.red() - bg.red();
const double dg = fg.green() - bg.green();
const double db = fg.blue() - bg.blue();
const int r = bg.red(), g = bg.green(), b = bg.blue();
const double dr = fg.red() - bg.red();
const double dg = fg.green() - bg.green();
const double db = fg.blue() - bg.blue();
const int r = bg.red(), g = bg.green(), b = bg.blue();
// Precalculate all fade-bar pixmaps
for( uint y = 0; y < FADE_SIZE; ++y ) {
m_fade_bars[y].fill( palette().color(QPalette::Background) );
QPainter f( &m_fade_bars[y] );
for( int z = 0; (uint)z < m_rows; ++z ) {
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) ) );
}
// Precalculate all fade-bar pixmaps
for (uint y = 0; y < FADE_SIZE; ++y) {
m_fade_bars[y].fill(palette().color(QPalette::Background));
QPainter f(&m_fade_bars[y]);
for (int z = 0; (uint)z < m_rows; ++z) {
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)));
}
}
}
}
drawBackground();
drawBackground();
}
void
BlockAnalyzer::drawBackground()
{
const QColor bg = palette().color(QPalette::Background);
const QColor bgdark = bg.dark( 112 );
void BlockAnalyzer::drawBackground() {
const QColor bg = palette().color(QPalette::Background);
const QColor bgdark = bg.dark(112);
m_background.fill( bg );
m_background.fill(bg);
QPainter p( &m_background );
for( int x = 0; (uint)x < m_columns; ++x )
for( int y = 0; (uint)y < m_rows; ++y )
p.fillRect( x*(WIDTH+1), y*(HEIGHT+1) + m_y, WIDTH, HEIGHT, bgdark );
QPainter p(&m_background);
for (int x = 0; (uint)x < m_columns; ++x)
for (int y = 0; (uint)y < m_rows; ++y)
p.fillRect(x * (WIDTH + 1), y * (HEIGHT + 1) + m_y, WIDTH, HEIGHT,
bgdark);
}

View File

@ -12,54 +12,52 @@ class QResizeEvent;
class QMouseEvent;
class QPalette;
/**
* @author Max Howell
*/
class BlockAnalyzer : public Analyzer::Base
{
class BlockAnalyzer : public Analyzer::Base {
Q_OBJECT
public:
Q_INVOKABLE BlockAnalyzer( QWidget* );
~BlockAnalyzer();
public:
Q_INVOKABLE BlockAnalyzer(QWidget*);
~BlockAnalyzer();
static const uint HEIGHT;
static const uint WIDTH;
static const uint MIN_ROWS;
static const uint MIN_COLUMNS;
static const uint MAX_COLUMNS;
static const uint FADE_SIZE;
static const uint HEIGHT;
static const uint WIDTH;
static const uint MIN_ROWS;
static const uint MIN_COLUMNS;
static const uint MAX_COLUMNS;
static const uint FADE_SIZE;
static const char* kName;
static const char* kName;
protected:
virtual void transform( Scope& );
virtual void analyze( QPainter& p, const Scope&, bool new_frame);
virtual void resizeEvent( QResizeEvent* );
virtual void paletteChange( const QPalette& );
protected:
virtual void transform(Scope&);
virtual void analyze(QPainter& p, const Scope&, bool new_frame);
virtual void resizeEvent(QResizeEvent*);
virtual void paletteChange(const QPalette&);
void drawBackground();
void determineStep();
void drawBackground();
void determineStep();
private:
QPixmap* bar() { return &m_barPixmap; }
private:
QPixmap* bar() { return &m_barPixmap; }
uint m_columns, m_rows; //number of rows and columns of blocks
uint m_y; //y-offset from top of widget
QPixmap m_barPixmap;
QPixmap m_topBarPixmap;
QPixmap m_background;
Scope m_scope; //so we don't create a vector every frame
std::vector<float> m_store; //current bar heights
std::vector<float> m_yscale;
uint m_columns, m_rows; // number of rows and columns of blocks
uint m_y; // y-offset from top of widget
QPixmap m_barPixmap;
QPixmap m_topBarPixmap;
QPixmap m_background;
Scope m_scope; // so we don't create a vector every frame
std::vector<float> m_store; // current bar heights
std::vector<float> m_yscale;
//FIXME why can't I namespace these? c++ issue?
std::vector<QPixmap> m_fade_bars;
std::vector<uint> m_fade_pos;
std::vector<int> m_fade_intensity;
// FIXME why can't I namespace these? c++ issue?
std::vector<QPixmap> m_fade_bars;
std::vector<uint> m_fade_pos;
std::vector<int> m_fade_intensity;
float m_step; //rows to fall per frame
float m_step; // rows to fall per frame
};
#endif

View File

@ -5,131 +5,110 @@
#include <cmath>
#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 )
: Analyzer::Base( parent, 9 )
, K_barHeight( 1.271 )//1.471
, F_peakSpeed( 1.103 )//1.122
, F( 1.0 )
, bar_height( BAND_COUNT, 0 )
, peak_height( BAND_COUNT, 0 )
, peak_speed( BAND_COUNT, 0.01 )
, barPixmap( COLUMN_WIDTH, 50 )
{
BoomAnalyzer::BoomAnalyzer(QWidget* parent)
: Analyzer::Base(parent, 9),
K_barHeight(1.271) // 1.471
,
F_peakSpeed(1.103) // 1.122
,
F(1.0),
bar_height(BAND_COUNT, 0),
peak_height(BAND_COUNT, 0),
peak_speed(BAND_COUNT, 0.01),
barPixmap(COLUMN_WIDTH, 50) {}
void BoomAnalyzer::changeK_barHeight(int newValue) {
K_barHeight = (double)newValue / 1000;
}
void
BoomAnalyzer::changeK_barHeight( int newValue )
{
K_barHeight = (double)newValue / 1000;
void BoomAnalyzer::changeF_peakSpeed(int newValue) {
F_peakSpeed = (double)newValue / 1000;
}
void
BoomAnalyzer::changeF_peakSpeed( int newValue )
{
F_peakSpeed = (double)newValue / 1000;
void BoomAnalyzer::resizeEvent(QResizeEvent*) { init(); }
void BoomAnalyzer::init() {
const uint HEIGHT = height() - 2;
const double h = 1.2 / HEIGHT;
F = double(HEIGHT) / (log10(256) * 1.1 /*<- max. amplitude*/);
barPixmap = QPixmap(COLUMN_WIDTH - 2, HEIGHT);
QPainter p(&barPixmap);
for (uint y = 0; y < HEIGHT; ++y) {
const double F = (double)y * h;
p.setPen(QColor(qMax(0, 255 - int(229.0 * F)),
qMax(0, 255 - int(229.0 * F)),
qMax(0, 255 - int(191.0 * F))));
p.drawLine(0, y, COLUMN_WIDTH - 2, y);
}
}
void BoomAnalyzer::resizeEvent(QResizeEvent *) {
init();
void BoomAnalyzer::transform(Scope& s) {
float* front = static_cast<float*>(&s.front());
m_fht->spectrum(front);
m_fht->scale(front, 1.0 / 60);
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};
for (uint j, i = 0; i < 32; i++)
for (j = xscale[i]; j < xscale[i + 1]; j++)
if (s[j] > scope[i]) scope[i] = s[j];
s = scope;
}
void
BoomAnalyzer::init()
{
const uint HEIGHT = height() - 2;
const double h = 1.2 / HEIGHT;
void BoomAnalyzer::analyze(QPainter& p, const Scope& scope, bool new_frame) {
float h;
const uint MAX_HEIGHT = height() - 1;
F = double(HEIGHT) / (log10( 256 ) * 1.1 /*<- max. amplitude*/);
for (uint i = 0, x = 0, y; i < BAND_COUNT; ++i, x += COLUMN_WIDTH + 1) {
h = log10(scope[i] * 256.0) * F;
barPixmap = QPixmap( COLUMN_WIDTH-2, HEIGHT );
if (h > MAX_HEIGHT) h = MAX_HEIGHT;
QPainter p( &barPixmap );
for( uint y = 0; y < HEIGHT; ++y )
{
const double F = (double)y * h;
if (h > bar_height[i]) {
bar_height[i] = h;
p.setPen(QColor(
qMax(0, 255 - int(229.0 * F)),
qMax(0, 255 - int(229.0 * F)),
qMax(0, 255 - int(191.0 * F))));
p.drawLine( 0, y, COLUMN_WIDTH-2, y );
}
}
void
BoomAnalyzer::transform( Scope &s )
{
float *front = static_cast<float*>( &s.front() );
m_fht->spectrum( front );
m_fht->scale( front, 1.0 / 60 );
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 };
for( uint j, i = 0; i < 32; i++ )
for( j = xscale[i]; j < xscale[i + 1]; j++ )
if ( s[j] > scope[i] )
scope[i] = s[j];
s = scope;
}
void
BoomAnalyzer::analyze( QPainter& p, const Scope& scope, bool new_frame)
{
float h;
const uint MAX_HEIGHT = height() - 1;
for( uint i = 0, x = 0, y; i < BAND_COUNT; ++i, x += COLUMN_WIDTH+1 )
{
h = log10( scope[i]*256.0 ) * F;
if( h > MAX_HEIGHT )
h = MAX_HEIGHT;
if( h > bar_height[i] )
{
bar_height[i] = h;
if( h > peak_height[i] )
{
peak_height[i] = h;
peak_speed[i] = 0.01;
}
else goto peak_handling;
}
else
{
if( bar_height[i] > 0.0 )
{
bar_height[i] -= K_barHeight; //1.4
if( bar_height[i] < 0.0 ) bar_height[i] = 0.0;
}
peak_handling:
if( peak_height[i] > 0.0 )
{
peak_height[i] -= peak_speed[i];
peak_speed[i] *= F_peakSpeed; //1.12
if( peak_height[i] < bar_height[i] ) peak_height[i] = bar_height[i];
if( peak_height[i] < 0.0 ) peak_height[i] = 0.0;
}
}
y = height() - uint(bar_height[i]);
p.drawPixmap(x+1, y, barPixmap, 0, y, -1, -1);
p.setPen( palette().color(QPalette::Highlight) );
if (bar_height[i] > 0)
p.drawRect( x, y, COLUMN_WIDTH - 1, height() - y - 1 );
y = height() - uint(peak_height[i]);
p.setPen( palette().color(QPalette::Base) );
p.drawLine( x, y, x+COLUMN_WIDTH-1, y );
if (h > peak_height[i]) {
peak_height[i] = h;
peak_speed[i] = 0.01;
} else
goto peak_handling;
} else {
if (bar_height[i] > 0.0) {
bar_height[i] -= K_barHeight; // 1.4
if (bar_height[i] < 0.0) bar_height[i] = 0.0;
}
peak_handling:
if (peak_height[i] > 0.0) {
peak_height[i] -= peak_speed[i];
peak_speed[i] *= F_peakSpeed; // 1.12
if (peak_height[i] < bar_height[i]) peak_height[i] = bar_height[i];
if (peak_height[i] < 0.0) peak_height[i] = 0.0;
}
}
y = height() - uint(bar_height[i]);
p.drawPixmap(x + 1, y, barPixmap, 0, y, -1, -1);
p.setPen(palette().color(QPalette::Highlight));
if (bar_height[i] > 0) p.drawRect(x, y, COLUMN_WIDTH - 1, height() - y - 1);
y = height() - uint(peak_height[i]);
p.setPen(palette().color(QPalette::Base));
p.drawLine(x, y, x + COLUMN_WIDTH - 1, y);
}
}

View File

@ -11,35 +11,34 @@
@author Max Howell
*/
class BoomAnalyzer : public Analyzer::Base
{
Q_OBJECT
public:
Q_INVOKABLE BoomAnalyzer( QWidget* );
class BoomAnalyzer : public Analyzer::Base {
Q_OBJECT
public:
Q_INVOKABLE BoomAnalyzer(QWidget*);
static const char* kName;
static const char* kName;
virtual void init();
virtual void transform( Scope &s );
virtual void analyze( QPainter& p, const Scope&, bool new_frame);
virtual void init();
virtual void transform(Scope& s);
virtual void analyze(QPainter& p, const Scope&, bool new_frame);
public slots:
void changeK_barHeight( int );
void changeF_peakSpeed( int );
public slots:
void changeK_barHeight(int);
void changeF_peakSpeed(int);
protected:
void resizeEvent( QResizeEvent * e);
protected:
void resizeEvent(QResizeEvent* e);
static const uint COLUMN_WIDTH = 4;
static const uint BAND_COUNT = 32;
static const uint COLUMN_WIDTH = 4;
static const uint BAND_COUNT = 32;
double K_barHeight, F_peakSpeed, F;
double K_barHeight, F_peakSpeed, F;
std::vector<float> bar_height;
std::vector<float> peak_height;
std::vector<float> peak_speed;
std::vector<float> bar_height;
std::vector<float> peak_height;
std::vector<float> peak_speed;
QPixmap barPixmap;
QPixmap barPixmap;
};
#endif

View File

@ -23,320 +23,295 @@
#include "glanalyzer.h"
#include <kdebug.h>
GLAnalyzer::GLAnalyzer(QWidget* parent)
: Analyzer::Base3D(parent, 15), m_oldy(32, -10.0f), m_peaks(32) {}
GLAnalyzer::GLAnalyzer( QWidget *parent )
: Analyzer::Base3D(parent, 15)
, m_oldy(32, -10.0f)
, m_peaks(32)
{}
GLAnalyzer::~GLAnalyzer()
{}
GLAnalyzer::~GLAnalyzer() {}
// METHODS =====================================================
void GLAnalyzer::analyze( const Scope &s )
{
//kdDebug() << "Scope Size: " << s.size() << endl;
/* Scope t(32);
if (s.size() != 32)
{
Analyzer::interpolate(s, t);
}
else
{
t = s;
}*/
uint offset = 0;
static float peak;
float mfactor = 0.0;
static int drawcount;
void GLAnalyzer::analyze(const Scope& s) {
// kdDebug() << "Scope Size: " << s.size() << endl;
/* Scope t(32);
if (s.size() != 32)
{
Analyzer::interpolate(s, t);
}
else
{
t = s;
}*/
uint offset = 0;
static float peak;
float mfactor = 0.0;
static int drawcount;
if (s.size() == 64)
{
offset=8;
}
if (s.size() == 64) {
offset = 8;
}
glRotatef(0.25f, 0.0f, 1.0f, 0.5f); //Rotate the scene
drawFloor();
drawcount++;
if (drawcount > 25)
{
drawcount = 0;
peak = 0.0;
}
glRotatef(0.25f, 0.0f, 1.0f, 0.5f); // Rotate the scene
drawFloor();
drawcount++;
if (drawcount > 25) {
drawcount = 0;
peak = 0.0;
}
for ( uint i = 0; i < 32; i++ )
{
if (s[i] > peak)
{
peak = s[i];
}
}
for (uint i = 0; i < 32; i++) {
if (s[i] > peak) {
peak = s[i];
}
}
mfactor = 20 / peak;
for ( uint i = 0; i < 32; i++ )
{
mfactor = 20 / peak;
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
x = -16.0f + i;
// Calculate new horizontal position (x) depending on number of samples
x = -16.0f + i;
// Calculating new vertical position (y) depending on the data passed by amarok
y = float(s[i+offset] * mfactor); //This make it kinda dynamically resize depending on the data
// Calculating new vertical position (y) depending on the data passed by
// amarok
y = float(s[i + offset] * mfactor); // This make it kinda dynamically
// resize depending on the data
//Some basic bounds checking
if (y > 30)
y = 30;
else if (y < 0)
y = 0;
// Some basic bounds checking
if (y > 30)
y = 30;
else if (y < 0)
y = 0;
if((y - m_oldy[i]) < -0.6f) // Going Down Too Much
{
y = m_oldy[i] - 0.7f;
}
if (y < 0.0f)
{
y = 0.0f;
}
if ((y - m_oldy[i]) < -0.6f) // Going Down Too Much
{
y = m_oldy[i] - 0.7f;
}
if (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
if (m_oldy[i] > m_peaks[i].level)
{
m_peaks[i].level = m_oldy[i];
m_peaks[i].delay = 30;
}
// Peak Code
if (m_oldy[i] > m_peaks[i].level) {
m_peaks[i].level = m_oldy[i];
m_peaks[i].delay = 30;
}
if (m_peaks[i].delay > 0)
{
m_peaks[i].delay--;
}
if (m_peaks[i].delay > 0) {
m_peaks[i].delay--;
}
if (m_peaks[i].level > 1.0f)
{
if (m_peaks[i].delay <= 0)
{
m_peaks[i].level-=0.4f;
}
}
// Draw the bar
drawBar(x,y);
drawPeak(x, m_peaks[i].level);
if (m_peaks[i].level > 1.0f) {
if (m_peaks[i].delay <= 0) {
m_peaks[i].level -= 0.4f;
}
}
// Draw the bar
drawBar(x, y);
drawPeak(x, m_peaks[i].level);
}
}
updateGL();
updateGL();
}
void GLAnalyzer::initializeGL()
{
// Clear frame (next fading will be preferred to clearing)
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);// Set clear color to black
glClear( GL_COLOR_BUFFER_BIT );
void GLAnalyzer::initializeGL() {
// Clear frame (next fading will be preferred to clearing)
glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // Set clear color to black
glClear(GL_COLOR_BUFFER_BIT);
// Set the shading model
glShadeModel(GL_SMOOTH);
// Set the shading model
glShadeModel(GL_SMOOTH);
// Set the polygon mode to fill
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
// Set the polygon mode to fill
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
// Enable depth testing for hidden line removal
glEnable(GL_DEPTH_TEST);
// Enable depth testing for hidden line removal
glEnable(GL_DEPTH_TEST);
// Set blend parameters for 'composting alpha'
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
// Set blend parameters for 'composting alpha'
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
void GLAnalyzer::resizeGL( int w, int h )
{
glViewport( 0, 0, (GLint)w, (GLint)h );
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
glOrtho(-16.0f, 16.0f, -10.0f, 10.0f, -50.0f, 100.0f);
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
void GLAnalyzer::resizeGL(int w, int h) {
glViewport(0, 0, (GLint)w, (GLint)h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-16.0f, 16.0f, -10.0f, 10.0f, -50.0f, 100.0f);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
void GLAnalyzer::paintGL()
{
glMatrixMode( GL_MODELVIEW );
void GLAnalyzer::paintGL() {
glMatrixMode(GL_MODELVIEW);
#if 0
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
#else
glEnable( GL_DEPTH_TEST );
glEnable( GL_BLEND );
glPushMatrix();
glLoadIdentity();
glBegin( GL_TRIANGLE_STRIP );
glColor4f( 0.0f, 0.0f, 0.1f, 0.08f );
glVertex2f( 20.0f, 10.0f );
glVertex2f( -20.0f, 10.0f );
glVertex2f( 20.0f, -10.0f );
glVertex2f( -20.0f, -10.0f );
glEnd();
glPopMatrix();
glDisable( GL_BLEND );
glEnable( GL_DEPTH_TEST );
glClear( GL_DEPTH_BUFFER_BIT );
glEnable(GL_DEPTH_TEST);
glEnable(GL_BLEND);
glPushMatrix();
glLoadIdentity();
glBegin(GL_TRIANGLE_STRIP);
glColor4f(0.0f, 0.0f, 0.1f, 0.08f);
glVertex2f(20.0f, 10.0f);
glVertex2f(-20.0f, 10.0f);
glVertex2f(20.0f, -10.0f);
glVertex2f(-20.0f, -10.0f);
glEnd();
glPopMatrix();
glDisable(GL_BLEND);
glEnable(GL_DEPTH_TEST);
glClear(GL_DEPTH_BUFFER_BIT);
#endif
//swapBuffers();
glFlush();
// swapBuffers();
glFlush();
}
void GLAnalyzer::drawBar(float xPos, float height)
{
glPushMatrix();
void GLAnalyzer::drawBar(float xPos, float height) {
glPushMatrix();
//Sets color to blue
//Set the colour depending on the height of the bar
glColor3f((height/40) + 0.5f, (height/40) + 0.625f, 1.0f);
glTranslatef(xPos, -10.0f, 0.0f);
// Sets color to blue
// Set the colour depending on the height of the bar
glColor3f((height / 40) + 0.5f, (height / 40) + 0.625f, 1.0f);
glTranslatef(xPos, -10.0f, 0.0f);
glScalef(1.0f, height, 3.0f);
drawCube();
glScalef(1.0f, height, 3.0f);
drawCube();
//Set colour to full blue
//glColor3f(0.0f, 0.0f, 1.0f);
//drawFrame();
glPopMatrix();
// Set colour to full blue
// glColor3f(0.0f, 0.0f, 1.0f);
// drawFrame();
glPopMatrix();
}
void GLAnalyzer::drawFloor()
{
glPushMatrix();
void GLAnalyzer::drawFloor() {
glPushMatrix();
//Sets color to amarok blue
glColor3f( 0.5f, 0.625f, 1.0f);
glTranslatef(-16.0f,-11.0f, -4.0f);
// Sets color to amarok blue
glColor3f(0.5f, 0.625f, 1.0f);
glTranslatef(-16.0f, -11.0f, -4.0f);
glScalef(32.0f, 1.0f, 10.0f);
drawCube();
glScalef(32.0f, 1.0f, 10.0f);
drawCube();
//Set colour to full blue
glColor3f(0.0f, 0.0f, 1.0f);
drawFrame();
glPopMatrix();
// Set colour to full blue
glColor3f(0.0f, 0.0f, 1.0f);
drawFrame();
glPopMatrix();
}
void GLAnalyzer::drawPeak(float xPos, float ypos)
{
glPushMatrix();
void GLAnalyzer::drawPeak(float xPos, float ypos) {
glPushMatrix();
//Set the colour to red
glColor3f(1.0f, 0.0f, 0.0f);
glTranslatef(xPos, ypos - 10.0f, 0.0f);
// Set the colour to red
glColor3f(1.0f, 0.0f, 0.0f);
glTranslatef(xPos, ypos - 10.0f, 0.0f);
glScalef(1.0f, 1.0f, 3.0f);
drawCube();
glScalef(1.0f, 1.0f, 3.0f);
drawCube();
glPopMatrix();
glPopMatrix();
}
void GLAnalyzer::drawCube()
{
glPushMatrix();
glBegin(GL_POLYGON);
void GLAnalyzer::drawCube() {
glPushMatrix();
glBegin(GL_POLYGON);
//This is the top face
glVertex3f(0.0f, 1.0f, 0.0f);
glVertex3f(1.0f, 1.0f, 0.0f);
glVertex3f(1.0f, 1.0f, 1.0f);
glVertex3f(0.0f, 1.0f, 1.0f);
glVertex3f(0.0f, 1.0f, 0.0f);
// This is the top face
glVertex3f(0.0f, 1.0f, 0.0f);
glVertex3f(1.0f, 1.0f, 0.0f);
glVertex3f(1.0f, 1.0f, 1.0f);
glVertex3f(0.0f, 1.0f, 1.0f);
glVertex3f(0.0f, 1.0f, 0.0f);
//This is the front face
glVertex3f(0.0f, 0.0f, 0.0f);
glVertex3f(1.0f, 0.0f, 0.0f);
glVertex3f(1.0f, 1.0f, 0.0f);
glVertex3f(0.0f, 1.0f, 0.0f);
glVertex3f(0.0f, 0.0f, 0.0f);
// This is the front face
glVertex3f(0.0f, 0.0f, 0.0f);
glVertex3f(1.0f, 0.0f, 0.0f);
glVertex3f(1.0f, 1.0f, 0.0f);
glVertex3f(0.0f, 1.0f, 0.0f);
glVertex3f(0.0f, 0.0f, 0.0f);
//This is the right face
glVertex3f(1.0f, 0.0f, 0.0f);
glVertex3f(1.0f, 0.0f, 1.0f);
glVertex3f(1.0f, 1.0f, 1.0f);
glVertex3f(1.0f, 1.0f, 0.0f);
glVertex3f(1.0f, 0.0f, 0.0f);
// This is the right face
glVertex3f(1.0f, 0.0f, 0.0f);
glVertex3f(1.0f, 0.0f, 1.0f);
glVertex3f(1.0f, 1.0f, 1.0f);
glVertex3f(1.0f, 1.0f, 0.0f);
glVertex3f(1.0f, 0.0f, 0.0f);
//This is the left face
glVertex3f(0.0f, 0.0f, 0.0f);
glVertex3f(0.0f, 0.0f, 1.0f);
glVertex3f(0.0f, 1.0f, 1.0f);
glVertex3f(0.0f, 1.0f, 0.0f);
glVertex3f(0.0f, 0.0f, 0.0f);
// This is the left face
glVertex3f(0.0f, 0.0f, 0.0f);
glVertex3f(0.0f, 0.0f, 1.0f);
glVertex3f(0.0f, 1.0f, 1.0f);
glVertex3f(0.0f, 1.0f, 0.0f);
glVertex3f(0.0f, 0.0f, 0.0f);
//This is the bottom face
glVertex3f(0.0f, 0.0f, 0.0f);
glVertex3f(1.0f, 0.0f, 0.0f);
glVertex3f(1.0f, 0.0f, 1.0f);
glVertex3f(0.0f, 0.0f, 1.0f);
glVertex3f(0.0f, 0.0f, 0.0f);
// This is the bottom face
glVertex3f(0.0f, 0.0f, 0.0f);
glVertex3f(1.0f, 0.0f, 0.0f);
glVertex3f(1.0f, 0.0f, 1.0f);
glVertex3f(0.0f, 0.0f, 1.0f);
glVertex3f(0.0f, 0.0f, 0.0f);
//This is the back face
glVertex3f(0.0f, 0.0f, 1.0f);
glVertex3f(1.0f, 0.0f, 1.0f);
glVertex3f(1.0f, 1.0f, 1.0f);
glVertex3f(0.0f, 1.0f, 1.0f);
glVertex3f(0.0f, 0.0f, 1.0f);
// This is the back face
glVertex3f(0.0f, 0.0f, 1.0f);
glVertex3f(1.0f, 0.0f, 1.0f);
glVertex3f(1.0f, 1.0f, 1.0f);
glVertex3f(0.0f, 1.0f, 1.0f);
glVertex3f(0.0f, 0.0f, 1.0f);
glEnd();
glPopMatrix();
glEnd();
glPopMatrix();
}
void GLAnalyzer::drawFrame()
{
glPushMatrix();
glBegin(GL_LINES);
void GLAnalyzer::drawFrame() {
glPushMatrix();
glBegin(GL_LINES);
// This is the top face
glVertex3f(0.0f, 1.0f, 0.0f);
glVertex3f(1.0f, 1.0f, 0.0f);
glVertex3f(1.0f, 1.0f, 1.0f);
glVertex3f(0.0f, 1.0f, 1.0f);
glVertex3f(0.0f, 1.0f, 0.0f);
//This is the top face
glVertex3f(0.0f, 1.0f, 0.0f);
glVertex3f(1.0f, 1.0f, 0.0f);
glVertex3f(1.0f, 1.0f, 1.0f);
glVertex3f(0.0f, 1.0f, 1.0f);
glVertex3f(0.0f, 1.0f, 0.0f);
// This is the front face
glVertex3f(0.0f, 0.0f, 0.0f);
glVertex3f(1.0f, 0.0f, 0.0f);
glVertex3f(1.0f, 1.0f, 0.0f);
glVertex3f(0.0f, 1.0f, 0.0f);
glVertex3f(0.0f, 0.0f, 0.0f);
//This is the front face
glVertex3f(0.0f, 0.0f, 0.0f);
glVertex3f(1.0f, 0.0f, 0.0f);
glVertex3f(1.0f, 1.0f, 0.0f);
glVertex3f(0.0f, 1.0f, 0.0f);
glVertex3f(0.0f, 0.0f, 0.0f);
// This is the right face
glVertex3f(1.0f, 0.0f, 0.0f);
glVertex3f(1.0f, 0.0f, 1.0f);
glVertex3f(1.0f, 1.0f, 1.0f);
glVertex3f(1.0f, 1.0f, 0.0f);
glVertex3f(1.0f, 0.0f, 0.0f);
//This is the right face
glVertex3f(1.0f, 0.0f, 0.0f);
glVertex3f(1.0f, 0.0f, 1.0f);
glVertex3f(1.0f, 1.0f, 1.0f);
glVertex3f(1.0f, 1.0f, 0.0f);
glVertex3f(1.0f, 0.0f, 0.0f);
// This is the left face
glVertex3f(0.0f, 0.0f, 0.0f);
glVertex3f(0.0f, 0.0f, 1.0f);
glVertex3f(0.0f, 1.0f, 1.0f);
glVertex3f(0.0f, 1.0f, 0.0f);
glVertex3f(0.0f, 0.0f, 0.0f);
//This is the left face
glVertex3f(0.0f, 0.0f, 0.0f);
glVertex3f(0.0f, 0.0f, 1.0f);
glVertex3f(0.0f, 1.0f, 1.0f);
glVertex3f(0.0f, 1.0f, 0.0f);
glVertex3f(0.0f, 0.0f, 0.0f);
// This is the bottom face
glVertex3f(0.0f, 0.0f, 0.0f);
glVertex3f(1.0f, 0.0f, 0.0f);
glVertex3f(1.0f, 0.0f, 1.0f);
glVertex3f(0.0f, 0.0f, 1.0f);
glVertex3f(0.0f, 0.0f, 0.0f);
//This is the bottom face
glVertex3f(0.0f, 0.0f, 0.0f);
glVertex3f(1.0f, 0.0f, 0.0f);
glVertex3f(1.0f, 0.0f, 1.0f);
glVertex3f(0.0f, 0.0f, 1.0f);
glVertex3f(0.0f, 0.0f, 0.0f);
// This is the back face
glVertex3f(0.0f, 0.0f, 1.0f);
glVertex3f(1.0f, 0.0f, 1.0f);
glVertex3f(1.0f, 1.0f, 1.0f);
glVertex3f(0.0f, 1.0f, 1.0f);
glVertex3f(0.0f, 0.0f, 1.0f);
//This is the back face
glVertex3f(0.0f, 0.0f, 1.0f);
glVertex3f(1.0f, 0.0f, 1.0f);
glVertex3f(1.0f, 1.0f, 1.0f);
glVertex3f(0.0f, 1.0f, 1.0f);
glVertex3f(0.0f, 0.0f, 1.0f);
glEnd();
glPopMatrix();
glEnd();
glPopMatrix();
}
#endif

View File

@ -27,16 +27,13 @@
*@author piggz
*/
typedef struct
{
typedef struct {
float level;
uint delay;
}
peak_tx;
} peak_tx;
class GLAnalyzer : public Analyzer::Base3D
{
private:
class GLAnalyzer : public Analyzer::Base3D {
private:
std::vector<float> m_oldy;
std::vector<peak_tx> m_peaks;
@ -47,14 +44,15 @@ private:
void drawFloor();
GLfloat x, y;
public:
GLAnalyzer(QWidget *);
~GLAnalyzer();
void analyze( const Scope & );
protected:
public:
GLAnalyzer(QWidget*);
~GLAnalyzer();
void analyze(const Scope&);
protected:
void initializeGL();
void resizeGL( int w, int h );
void resizeGL(int w, int h);
void paintGL();
};

View File

@ -27,307 +27,267 @@
#include <qimage.h>
#include <sys/time.h>
GLAnalyzer2::GLAnalyzer2(QWidget* parent) : Analyzer::Base3D(parent, 15) {
// initialize openGL context before managing GL calls
makeCurrent();
loadTexture(locate("data", "amarok/data/dot.png"), dotTexture);
loadTexture(locate("data", "amarok/data/wirl1.png"), w1Texture);
loadTexture(locate("data", "amarok/data/wirl2.png"), w2Texture);
GLAnalyzer2::GLAnalyzer2( QWidget *parent ):
Analyzer::Base3D(parent, 15)
{
//initialize openGL context before managing GL calls
makeCurrent();
loadTexture( locate("data","amarok/data/dot.png"), dotTexture );
loadTexture( locate("data","amarok/data/wirl1.png"), w1Texture );
loadTexture( locate("data","amarok/data/wirl2.png"), w2Texture );
show.paused = true;
show.pauseTimer = 0.0;
show.rotDegrees = 0.0;
frame.rotDegrees = 0.0;
show.paused = true;
show.pauseTimer = 0.0;
show.rotDegrees = 0.0;
frame.rotDegrees = 0.0;
}
GLAnalyzer2::~GLAnalyzer2()
{
freeTexture( dotTexture );
freeTexture( w1Texture );
freeTexture( w2Texture );
GLAnalyzer2::~GLAnalyzer2() {
freeTexture(dotTexture);
freeTexture(w1Texture);
freeTexture(w2Texture);
}
void GLAnalyzer2::initializeGL()
{
// Set a smooth shade model
glShadeModel(GL_SMOOTH);
void GLAnalyzer2::initializeGL() {
// Set a smooth shade model
glShadeModel(GL_SMOOTH);
// Disable depth test (all is drawn on a 2d plane)
glDisable(GL_DEPTH_TEST);
// Disable depth test (all is drawn on a 2d plane)
glDisable(GL_DEPTH_TEST);
// Set blend parameters for 'composting alpha'
glBlendFunc( GL_SRC_ALPHA, GL_ONE ); //superpose
//glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); //fade
glEnable( GL_BLEND );
// Set blend parameters for 'composting alpha'
glBlendFunc(GL_SRC_ALPHA, GL_ONE); // superpose
// glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); //fade
glEnable(GL_BLEND);
// Clear frame with a black background
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear( GL_COLOR_BUFFER_BIT );
// Clear frame with a black background
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
}
void GLAnalyzer2::resizeGL( int w, int h )
{
// Setup screen. We're going to manually do the perspective projection
glViewport( 0, 0, (GLint)w, (GLint)h );
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
glOrtho( -10.0f, 10.0f, -10.0f, 10.0f, -5.0f, 5.0f );
void GLAnalyzer2::resizeGL(int w, int h) {
// Setup screen. We're going to manually do the perspective projection
glViewport(0, 0, (GLint)w, (GLint)h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-10.0f, 10.0f, -10.0f, 10.0f, -5.0f, 5.0f);
// Get the aspect ratio of the screen to draw 'cicular' particles
float ratio = (float)w / (float)h,
eqPixH = 60,
eqPixW = 80;
if ( ratio >= (4.0/3.0) ) {
unitX = 10.0 / (eqPixH * ratio);
unitY = 10.0 / eqPixH;
} else {
unitX = 10.0 / eqPixW;
unitY = 10.0 / (eqPixW / ratio);
// Get the aspect ratio of the screen to draw 'cicular' particles
float ratio = (float)w / (float)h, eqPixH = 60, eqPixW = 80;
if (ratio >= (4.0 / 3.0)) {
unitX = 10.0 / (eqPixH * ratio);
unitY = 10.0 / eqPixH;
} else {
unitX = 10.0 / eqPixW;
unitY = 10.0 / (eqPixW / ratio);
}
// Get current timestamp.
timeval tv;
gettimeofday(&tv, nullptr);
show.timeStamp = (double)tv.tv_sec + (double)tv.tv_usec / 1000000.0;
}
void GLAnalyzer2::paused() { analyze(Scope()); }
void GLAnalyzer2::analyze(const Scope& s) {
bool haveNoData = s.empty();
// if we're going into pause mode, clear timers.
if (!show.paused && haveNoData) show.pauseTimer = 0.0;
// if we have got data, interpolate it (asking myself why I'm doing it here..)
if (!(show.paused = haveNoData)) {
int bands = s.size(), lowbands = bands / 4, hibands = bands / 3,
midbands = bands - lowbands - hibands;
Q_UNUSED(midbands);
float currentEnergy = 0, currentMeanBand = 0, maxValue = 0;
for (int i = 0; i < bands; i++) {
float value = s[i];
currentEnergy += value;
currentMeanBand += (float)i * value;
if (value > maxValue) maxValue = value;
}
// Get current timestamp.
timeval tv;
gettimeofday( &tv, nullptr );
show.timeStamp = (double)tv.tv_sec + (double)tv.tv_usec/1000000.0;
}
void GLAnalyzer2::paused()
{
analyze( Scope() );
}
void GLAnalyzer2::analyze( const Scope &s )
{
bool haveNoData = s.empty();
// if we're going into pause mode, clear timers.
if ( !show.paused && haveNoData )
show.pauseTimer = 0.0;
// if we have got data, interpolate it (asking myself why I'm doing it here..)
if ( !(show.paused = haveNoData) )
{
int bands = s.size(),
lowbands = bands / 4,
hibands = bands / 3,
midbands = bands - lowbands - hibands; Q_UNUSED( midbands );
float currentEnergy = 0,
currentMeanBand = 0,
maxValue = 0;
for ( int i = 0; i < bands; i++ )
{
float value = s[i];
currentEnergy += value;
currentMeanBand += (float)i * value;
if ( value > maxValue )
maxValue = value;
}
frame.silence = currentEnergy < 0.001;
if ( !frame.silence )
{
frame.meanBand = 100.0 * currentMeanBand / (currentEnergy * bands);
currentEnergy = 100.0 * currentEnergy / (float)bands;
frame.dEnergy = currentEnergy - frame.energy;
frame.energy = currentEnergy;
// printf( "%d [%f :: %f ]\t%f \n", bands, frame.energy, frame.meanBand, maxValue );
} else
frame.energy = 0.0;
}
// update the frame
updateGL();
}
void GLAnalyzer2::paintGL()
{
// Compute the dT since the last call to paintGL and update timings
timeval tv;
gettimeofday( &tv, nullptr );
double currentTime = (double)tv.tv_sec + (double)tv.tv_usec/1000000.0;
show.dT = currentTime - show.timeStamp;
show.timeStamp = currentTime;
// Clear frame
glClear( GL_COLOR_BUFFER_BIT );
// Shitch to MODEL matrix and reset it to default
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
// Fade the previous drawings.
/* glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
glBegin( GL_TRIANGLE_STRIP );
glColor4f( 0.0f, 0.0f, 0.0f, 0.2f );
glVertex2f( 10.0f, 10.0f );
glVertex2f( -10.0f, 10.0f );
glVertex2f( 10.0f, -10.0f );
glVertex2f( -10.0f, -10.0f );
glEnd();*/
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
glEnable( GL_TEXTURE_2D );
float alphaN = show.paused ? 0.2 : (frame.energy / 10.0),
alphaP = show.paused ? 1.0 : (1 - frame.energy / 20.0);
if ( alphaN > 1.0 )
alphaN = 1.0;
if ( alphaP < 0.1 )
alphaP = 0.1;
glBindTexture( GL_TEXTURE_2D, w2Texture );
setTextureMatrix( show.rotDegrees, 0.707*alphaP );
glColor4f( 1.0f, 1.0f, 1.0f, 1.0f );
glBegin( GL_TRIANGLE_STRIP );
glTexCoord2f( 1.0, 1.0 );
glVertex2f( 10.0f, 10.0f );
glTexCoord2f( 0.0, 1.0 );
glVertex2f( -10.0f, 10.0f );
glTexCoord2f( 1.0, 0.0 );
glVertex2f( 10.0f, -10.0f );
glTexCoord2f( 0.0 , 0.0 );
glVertex2f( -10.0f, -10.0f );
glEnd();
glBindTexture( GL_TEXTURE_2D, w1Texture );
setTextureMatrix( -show.rotDegrees * 2, 0.707 );
glColor4f( 1.0f, 1.0f, 1.0f, alphaN );
glBegin( GL_TRIANGLE_STRIP );
glTexCoord2f( 1.0, 1.0 );
glVertex2f( 10.0f, 10.0f );
glTexCoord2f( 0.0, 1.0 );
glVertex2f( -10.0f, 10.0f );
glTexCoord2f( 1.0, 0.0 );
glVertex2f( 10.0f, -10.0f );
glTexCoord2f( 0.0 , 0.0 );
glVertex2f( -10.0f, -10.0f );
glEnd();
setTextureMatrix( 0.0, 0.0 );
glDisable( GL_TEXTURE_2D );
glBlendFunc( GL_SRC_ALPHA, GL_ONE );
// Here begins the real draw loop
// some updates to the show
show.rotDegrees += 40.0 * show.dT;
frame.rotDegrees += 80.0 * show.dT;
// handle the 'pause' status
if ( show.paused )
{
if ( show.pauseTimer > 0.5 )
{
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 );
}
show.pauseTimer += show.dT;
return;
}
if ( dotTexture ) {
glEnable( GL_TEXTURE_2D );
glBindTexture( GL_TEXTURE_2D, dotTexture );
frame.silence = currentEnergy < 0.001;
if (!frame.silence) {
frame.meanBand = 100.0 * currentMeanBand / (currentEnergy * bands);
currentEnergy = 100.0 * currentEnergy / (float)bands;
frame.dEnergy = currentEnergy - frame.energy;
frame.energy = currentEnergy;
// printf( "%d [%f :: %f ]\t%f \n", bands, frame.energy,
// frame.meanBand, maxValue );
} else
glDisable( GL_TEXTURE_2D );
frame.energy = 0.0;
}
glLoadIdentity();
// glRotatef( -frame.rotDegrees, 0,0,1 );
glBegin( GL_QUADS );
// Particle * particle = particleList.first();
// for (; particle; particle = particleList.next())
{
glColor4f( 0.0f, 1.0f, 0.0f, 1.0f );
drawDot( 0, 0, kMax(10.0,(10.0 * frame.energy)) );
glColor4f( 1.0f, 0.0f, 0.0f, 1.0f );
drawDot( 6, 0, kMax(10.0, (5.0 * frame.energy)) );
glColor4f( 0.0f, 0.4f, 1.0f, 1.0f );
drawDot( -6, 0, kMax(10.0, (5.0 * frame.energy)) );
// update the frame
updateGL();
}
void GLAnalyzer2::paintGL() {
// Compute the dT since the last call to paintGL and update timings
timeval tv;
gettimeofday(&tv, nullptr);
double currentTime = (double)tv.tv_sec + (double)tv.tv_usec / 1000000.0;
show.dT = currentTime - show.timeStamp;
show.timeStamp = currentTime;
// Clear frame
glClear(GL_COLOR_BUFFER_BIT);
// Shitch to MODEL matrix and reset it to default
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
// Fade the previous drawings.
/* glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
glBegin( GL_TRIANGLE_STRIP );
glColor4f( 0.0f, 0.0f, 0.0f, 0.2f );
glVertex2f( 10.0f, 10.0f );
glVertex2f( -10.0f, 10.0f );
glVertex2f( 10.0f, -10.0f );
glVertex2f( -10.0f, -10.0f );
glEnd();*/
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_TEXTURE_2D);
float alphaN = show.paused ? 0.2 : (frame.energy / 10.0),
alphaP = show.paused ? 1.0 : (1 - frame.energy / 20.0);
if (alphaN > 1.0) alphaN = 1.0;
if (alphaP < 0.1) alphaP = 0.1;
glBindTexture(GL_TEXTURE_2D, w2Texture);
setTextureMatrix(show.rotDegrees, 0.707 * alphaP);
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
glBegin(GL_TRIANGLE_STRIP);
glTexCoord2f(1.0, 1.0);
glVertex2f(10.0f, 10.0f);
glTexCoord2f(0.0, 1.0);
glVertex2f(-10.0f, 10.0f);
glTexCoord2f(1.0, 0.0);
glVertex2f(10.0f, -10.0f);
glTexCoord2f(0.0, 0.0);
glVertex2f(-10.0f, -10.0f);
glEnd();
glBindTexture(GL_TEXTURE_2D, w1Texture);
setTextureMatrix(-show.rotDegrees * 2, 0.707);
glColor4f(1.0f, 1.0f, 1.0f, alphaN);
glBegin(GL_TRIANGLE_STRIP);
glTexCoord2f(1.0, 1.0);
glVertex2f(10.0f, 10.0f);
glTexCoord2f(0.0, 1.0);
glVertex2f(-10.0f, 10.0f);
glTexCoord2f(1.0, 0.0);
glVertex2f(10.0f, -10.0f);
glTexCoord2f(0.0, 0.0);
glVertex2f(-10.0f, -10.0f);
glEnd();
setTextureMatrix(0.0, 0.0);
glDisable(GL_TEXTURE_2D);
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
// Here begins the real draw loop
// some updates to the show
show.rotDegrees += 40.0 * show.dT;
frame.rotDegrees += 80.0 * show.dT;
// handle the 'pause' status
if (show.paused) {
if (show.pauseTimer > 0.5) {
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);
}
glEnd();
show.pauseTimer += show.dT;
return;
}
if (dotTexture) {
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, dotTexture);
} else
glDisable(GL_TEXTURE_2D);
glLoadIdentity();
// glRotatef( -frame.rotDegrees, 0,0,1 );
glBegin(GL_QUADS);
// Particle * particle = particleList.first();
// for (; particle; particle = particleList.next())
{
glColor4f(0.0f, 1.0f, 0.0f, 1.0f);
drawDot(0, 0, kMax(10.0, (10.0 * frame.energy)));
glColor4f(1.0f, 0.0f, 0.0f, 1.0f);
drawDot(6, 0, kMax(10.0, (5.0 * frame.energy)));
glColor4f(0.0f, 0.4f, 1.0f, 1.0f);
drawDot(-6, 0, kMax(10.0, (5.0 * frame.energy)));
}
glEnd();
}
void GLAnalyzer2::drawDot( float x, float y, float size )
{
float sizeX = size * unitX,
sizeY = size * unitY,
pLeft = x - sizeX,
pTop = y + sizeY,
pRight = x + sizeX,
pBottom = y - sizeY;
glTexCoord2f( 0, 0 ); // Bottom Left
glVertex2f( pLeft, pBottom );
glTexCoord2f( 0, 1 ); // Top Left
glVertex2f( pLeft, pTop );
glTexCoord2f( 1, 1 ); // Top Right
glVertex2f( pRight, pTop );
glTexCoord2f( 1, 0 ); // Bottom Right
glVertex2f( pRight, pBottom );
void GLAnalyzer2::drawDot(float x, float y, float size) {
float sizeX = size * unitX, sizeY = size * unitY, pLeft = x - sizeX,
pTop = y + sizeY, pRight = x + sizeX, pBottom = y - sizeY;
glTexCoord2f(0, 0); // Bottom Left
glVertex2f(pLeft, pBottom);
glTexCoord2f(0, 1); // Top Left
glVertex2f(pLeft, pTop);
glTexCoord2f(1, 1); // Top Right
glVertex2f(pRight, pTop);
glTexCoord2f(1, 0); // Bottom Right
glVertex2f(pRight, pBottom);
}
void GLAnalyzer2::drawFullDot( float r, float g, float b, float a )
{
glBindTexture( GL_TEXTURE_2D, dotTexture );
glEnable( GL_TEXTURE_2D );
glColor4f( r, g, b, a );
glBegin( GL_TRIANGLE_STRIP );
glTexCoord2f( 1.0, 1.0 );
glVertex2f( 10.0f, 10.0f );
glTexCoord2f( 0.0, 1.0 );
glVertex2f( -10.0f, 10.0f );
glTexCoord2f( 1.0, 0.0 );
glVertex2f( 10.0f, -10.0f );
glTexCoord2f( 0.0 , 0.0 );
glVertex2f( -10.0f, -10.0f );
glEnd();
glDisable( GL_TEXTURE_2D );
void GLAnalyzer2::drawFullDot(float r, float g, float b, float a) {
glBindTexture(GL_TEXTURE_2D, dotTexture);
glEnable(GL_TEXTURE_2D);
glColor4f(r, g, b, a);
glBegin(GL_TRIANGLE_STRIP);
glTexCoord2f(1.0, 1.0);
glVertex2f(10.0f, 10.0f);
glTexCoord2f(0.0, 1.0);
glVertex2f(-10.0f, 10.0f);
glTexCoord2f(1.0, 0.0);
glVertex2f(10.0f, -10.0f);
glTexCoord2f(0.0, 0.0);
glVertex2f(-10.0f, -10.0f);
glEnd();
glDisable(GL_TEXTURE_2D);
}
void GLAnalyzer2::setTextureMatrix( float rot, float scale )
{
glMatrixMode( GL_TEXTURE);
glLoadIdentity();
if ( rot != 0.0 || scale != 0.0 )
{
glTranslatef( 0.5f, 0.5f, 0.0f );
glRotatef( rot, 0.0f, 0.0f, 1.0f );
glScalef( scale, scale, 1.0f );
glTranslatef( -0.5f, -0.5f, 0.0f );
}
glMatrixMode( GL_MODELVIEW );
void GLAnalyzer2::setTextureMatrix(float rot, float scale) {
glMatrixMode(GL_TEXTURE);
glLoadIdentity();
if (rot != 0.0 || scale != 0.0) {
glTranslatef(0.5f, 0.5f, 0.0f);
glRotatef(rot, 0.0f, 0.0f, 1.0f);
glScalef(scale, scale, 1.0f);
glTranslatef(-0.5f, -0.5f, 0.0f);
}
glMatrixMode(GL_MODELVIEW);
}
bool GLAnalyzer2::loadTexture( QString fileName, GLuint& textureID )
{
//reset texture ID to the default EMPTY value
textureID = 0;
bool GLAnalyzer2::loadTexture(QString fileName, GLuint& textureID) {
// reset texture ID to the default EMPTY value
textureID = 0;
//load image
QImage tmp;
if ( !tmp.load( fileName ) )
return false;
// load image
QImage tmp;
if (!tmp.load(fileName)) return false;
//convert it to suitable format (flipped RGBA)
QImage texture = QGLWidget::convertToGLFormat( tmp );
if ( texture.isNull() )
return false;
// convert it to suitable format (flipped RGBA)
QImage texture = QGLWidget::convertToGLFormat(tmp);
if (texture.isNull()) return false;
//get texture number and bind loaded image to that texture
glGenTextures( 1, &textureID );
glBindTexture( GL_TEXTURE_2D, textureID );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
glTexImage2D( GL_TEXTURE_2D, 0, 4, texture.width(), texture.height(),
0, GL_RGBA, GL_UNSIGNED_BYTE, texture.bits() );
return true;
// get texture number and bind loaded image to that texture
glGenTextures(1, &textureID);
glBindTexture(GL_TEXTURE_2D, textureID);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, 4, texture.width(), texture.height(), 0,
GL_RGBA, GL_UNSIGNED_BYTE, texture.bits());
return true;
}
void GLAnalyzer2::freeTexture( GLuint & textureID )
{
if ( textureID > 0 )
glDeleteTextures( 1, &textureID );
textureID = 0;
void GLAnalyzer2::freeTexture(GLuint& textureID) {
if (textureID > 0) glDeleteTextures(1, &textureID);
textureID = 0;
}
#endif

View File

@ -25,48 +25,46 @@
#include <qstring.h>
#include <qptrlist.h>
class GLAnalyzer2 : public Analyzer::Base3D {
public:
GLAnalyzer2(QWidget*);
~GLAnalyzer2();
void analyze(const Scope&);
void paused();
class GLAnalyzer2 : public Analyzer::Base3D
{
public:
GLAnalyzer2(QWidget *);
~GLAnalyzer2();
void analyze( const Scope & );
void paused();
protected:
void initializeGL();
void resizeGL(int w, int h);
void paintGL();
protected:
void initializeGL();
void resizeGL( int w, int h );
void paintGL();
private:
struct ShowProperties {
private:
struct ShowProperties {
bool paused;
double timeStamp;
double dT;
double pauseTimer;
float rotDegrees;
} show;
} show;
struct FrameProperties {
struct FrameProperties {
float energy;
float dEnergy;
float meanBand;
float rotDegrees;
bool silence;
} frame;
} frame;
GLuint dotTexture;
GLuint w1Texture;
GLuint w2Texture;
float unitX, unitY;
GLuint dotTexture;
GLuint w1Texture;
GLuint w2Texture;
float unitX, unitY;
void drawDot( float x, float y, float size );
void drawFullDot( float r, float g, float b, float a );
void setTextureMatrix( float rot, float scale );
void drawDot(float x, float y, float size);
void drawFullDot(float r, float g, float b, float a);
void setTextureMatrix(float rot, float scale);
bool loadTexture(QString file, GLuint& textureID);
void freeTexture(GLuint& textureID);
bool loadTexture(QString file, GLuint& textureID);
void freeTexture(GLuint& textureID);
};
#endif

View File

@ -28,453 +28,400 @@
#include <sys/time.h>
#ifndef HAVE_FABSF
inline float fabsf(float f)
{
return f < 0.f ? -f : f;
}
inline float fabsf(float f) { return f < 0.f ? -f : f; }
#endif
class Ball
{
public:
Ball() : 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 )
class Ball {
public:
Ball()
: 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)
//,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
color[0] = 0.0; color[1] = drand48()*0.5; color[2] = 0.7 + drand48() * 0.3;
};
{
// this is because GCC < 3.3 can't compile the above line, we aren't sure
// 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 color[3];
float x, y, z, vx, vy, vz, mass;
float color[3];
void updatePhysics( float dT )
{
x += vx * dT; // position
y += vy * dT; // position
z += vz * dT; // position
if ( y < -0.8 ) vy = fabsf( vy );
if ( y > 0.8 ) vy = -fabsf( vy );
if ( z < 0.1 ) vz = fabsf( vz );
if ( z > 0.9 ) vz = -fabsf( vz );
vx += (( x > 0 ) ? 4.94 : -4.94) * dT; // G-force
vx *= (1 - 2.9 * dT); // air friction
vy *= (1 - 2.9 * dT); // air friction
vz *= (1 - 2.9 * dT); // air friction
}
void updatePhysics(float dT) {
x += vx * dT; // position
y += vy * dT; // position
z += vz * dT; // position
if (y < -0.8) vy = fabsf(vy);
if (y > 0.8) vy = -fabsf(vy);
if (z < 0.1) vz = fabsf(vz);
if (z > 0.9) vz = -fabsf(vz);
vx += ((x > 0) ? 4.94 : -4.94) * dT; // G-force
vx *= (1 - 2.9 * dT); // air friction
vy *= (1 - 2.9 * dT); // air friction
vz *= (1 - 2.9 * dT); // air friction
}
};
class Paddle
{
public:
Paddle( float xPos ) : onLeft( xPos < 0 ), mass( 1.0 ),
X( xPos ), x( xPos ), vx( 0.0 ) {};
class Paddle {
public:
Paddle(float xPos)
: onLeft(xPos < 0), mass(1.0), X(xPos), x(xPos), vx(0.0) {};
void updatePhysics( float dT )
{
x += vx * dT; // posision
vx += (1300 * (X - x) / mass) * dT; // elasticity
vx *= (1 - 4.0 * dT); // air friction
void updatePhysics(float dT) {
x += vx * dT; // posision
vx += (1300 * (X - x) / mass) * dT; // elasticity
vx *= (1 - 4.0 * dT); // air friction
}
void renderGL() {
glBegin(GL_TRIANGLE_STRIP);
glColor3f(0.0f, 0.1f, 0.3f);
glVertex3f(x, -1.0f, 0.0);
glVertex3f(x, 1.0f, 0.0);
glColor3f(0.1f, 0.2f, 0.6f);
glVertex3f(x, -1.0f, 1.0);
glVertex3f(x, 1.0f, 1.0);
glEnd();
}
void bounce(Ball* ball) {
if (onLeft && ball->x < x) {
ball->vx = vx * mass / (mass + ball->mass) + fabsf(ball->vx);
ball->vy = (drand48() - drand48()) * 1.8;
ball->vz = (drand48() - drand48()) * 0.9;
ball->x = x;
} else if (!onLeft && ball->x > x) {
ball->vx = vx * mass / (mass + ball->mass) - fabsf(ball->vx);
ball->vy = (drand48() - drand48()) * 1.8;
ball->vz = (drand48() - drand48()) * 0.9;
ball->x = x;
}
}
void renderGL()
{
glBegin( GL_TRIANGLE_STRIP );
glColor3f( 0.0f, 0.1f, 0.3f );
glVertex3f( x, -1.0f, 0.0 );
glVertex3f( x, 1.0f, 0.0 );
glColor3f( 0.1f, 0.2f, 0.6f );
glVertex3f( x, -1.0f, 1.0 );
glVertex3f( x, 1.0f, 1.0 );
glEnd();
}
void impulse(float strength) {
if ((onLeft && strength > vx) || (!onLeft && strength < vx)) vx += strength;
}
void bounce( Ball * ball )
{
if ( onLeft && ball->x < x )
{
ball->vx = vx * mass / (mass + ball->mass) + fabsf( ball->vx );
ball->vy = (drand48() - drand48()) * 1.8;
ball->vz = (drand48() - drand48()) * 0.9;
ball->x = x;
}
else if ( !onLeft && ball->x > x )
{
ball->vx = vx * mass / (mass + ball->mass) - fabsf( ball->vx );
ball->vy = (drand48() - drand48()) * 1.8;
ball->vz = (drand48() - drand48()) * 0.9;
ball->x = x;
}
}
void impulse( float strength )
{
if ( (onLeft && strength > vx) || (!onLeft && strength < vx) )
vx += strength;
}
private:
bool onLeft;
float mass, X, x, vx;
private:
bool onLeft;
float mass, X, x, vx;
};
GLAnalyzer3::GLAnalyzer3(QWidget* parent) : Analyzer::Base3D(parent, 15) {
// initialize openGL context before managing GL calls
makeCurrent();
loadTexture(locate("data", "amarok/data/ball.png"), ballTexture);
loadTexture(locate("data", "amarok/data/grid.png"), gridTexture);
GLAnalyzer3::GLAnalyzer3( QWidget *parent ):
Analyzer::Base3D(parent, 15)
{
//initialize openGL context before managing GL calls
makeCurrent();
loadTexture( locate("data","amarok/data/ball.png"), ballTexture );
loadTexture( locate("data","amarok/data/grid.png"), gridTexture );
balls.setAutoDelete(true);
leftPaddle = new Paddle(-1.0);
rightPaddle = new Paddle(1.0);
for (int i = 0; i < NUMBER_OF_BALLS; i++) balls.append(new Ball());
balls.setAutoDelete( true );
leftPaddle = new Paddle( -1.0 );
rightPaddle = new Paddle( 1.0 );
for ( int i = 0; i < NUMBER_OF_BALLS; i++ )
balls.append( new Ball() );
show.colorK = 0.0;
show.gridScrollK = 0.0;
show.gridEnergyK = 0.0;
show.camRot = 0.0;
show.camRoll = 0.0;
show.peakEnergy = 1.0;
frame.silence = true;
frame.energy = 0.0;
frame.dEnergy = 0.0;
show.colorK = 0.0;
show.gridScrollK = 0.0;
show.gridEnergyK = 0.0;
show.camRot = 0.0;
show.camRoll = 0.0;
show.peakEnergy = 1.0;
frame.silence = true;
frame.energy = 0.0;
frame.dEnergy = 0.0;
}
GLAnalyzer3::~GLAnalyzer3()
{
freeTexture( ballTexture );
freeTexture( gridTexture );
delete leftPaddle;
delete rightPaddle;
balls.clear();
GLAnalyzer3::~GLAnalyzer3() {
freeTexture(ballTexture);
freeTexture(gridTexture);
delete leftPaddle;
delete rightPaddle;
balls.clear();
}
void GLAnalyzer3::initializeGL()
{
// Set a smooth shade model
glShadeModel(GL_SMOOTH);
void GLAnalyzer3::initializeGL() {
// Set a smooth shade model
glShadeModel(GL_SMOOTH);
// Disable depth test (all is drawn 'z-sorted')
glDisable( GL_DEPTH_TEST );
// Disable depth test (all is drawn 'z-sorted')
glDisable(GL_DEPTH_TEST);
// Set blending function (Alpha addition)
glBlendFunc( GL_SRC_ALPHA, GL_ONE );
// Set blending function (Alpha addition)
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
// Clear frame with a black background
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
// Clear frame with a black background
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
}
void GLAnalyzer3::resizeGL( int w, int h )
{
// Setup screen. We're going to manually do the perspective projection
glViewport( 0, 0, (GLint)w, (GLint)h );
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
glFrustum( -0.5f, 0.5f, -0.5f, 0.5f, 0.5f, 4.5f );
void GLAnalyzer3::resizeGL(int w, int h) {
// Setup screen. We're going to manually do the perspective projection
glViewport(0, 0, (GLint)w, (GLint)h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glFrustum(-0.5f, 0.5f, -0.5f, 0.5f, 0.5f, 4.5f);
// Get the aspect ratio of the screen to draw 'circular' particles
float ratio = (float)w / (float)h;
if ( ratio >= 1.0 ) {
// Get the aspect ratio of the screen to draw 'circular' particles
float ratio = (float)w / (float)h;
if (ratio >= 1.0) {
unitX = 0.34 / ratio;
unitY = 0.34;
} else {
} else {
unitX = 0.34;
unitY = 0.34 * ratio;
}
}
// Get current timestamp.
timeval tv;
gettimeofday( &tv, nullptr );
show.timeStamp = (double)tv.tv_sec + (double)tv.tv_usec/1000000.0;
// Get current timestamp.
timeval tv;
gettimeofday(&tv, nullptr);
show.timeStamp = (double)tv.tv_sec + (double)tv.tv_usec / 1000000.0;
}
void GLAnalyzer3::paused()
{
analyze( Scope() );
}
void GLAnalyzer3::paused() { analyze(Scope()); }
void GLAnalyzer3::analyze( const Scope &s )
{
// compute the dTime since the last call
timeval tv;
gettimeofday( &tv, nullptr );
double currentTime = (double)tv.tv_sec + (double)tv.tv_usec/1000000.0;
show.dT = currentTime - show.timeStamp;
show.timeStamp = currentTime;
void GLAnalyzer3::analyze(const Scope& s) {
// compute the dTime since the last call
timeval tv;
gettimeofday(&tv, nullptr);
double currentTime = (double)tv.tv_sec + (double)tv.tv_usec / 1000000.0;
show.dT = currentTime - show.timeStamp;
show.timeStamp = currentTime;
// compute energy integrating frame's spectrum
if ( !s.empty() )
{
// compute energy integrating frame's spectrum
if (!s.empty()) {
int bands = s.size();
float currentEnergy = 0,
maxValue = 0;
float currentEnergy = 0, maxValue = 0;
// integrate spectrum -> energy
for ( int i = 0; i < bands; i++ )
{
float value = s[i];
currentEnergy += value;
if ( value > maxValue )
maxValue = value;
for (int i = 0; i < bands; i++) {
float value = s[i];
currentEnergy += value;
if (value > maxValue) maxValue = value;
}
currentEnergy *= 100.0 / (float)bands;
// emulate a peak detector: currentEnergy -> peakEnergy (3tau = 30 seconds)
show.peakEnergy = 1.0 + ( show.peakEnergy - 1.0 ) * exp( - show.dT / 10.0 );
if ( currentEnergy > show.peakEnergy )
show.peakEnergy = currentEnergy;
show.peakEnergy = 1.0 + (show.peakEnergy - 1.0) * exp(-show.dT / 10.0);
if (currentEnergy > show.peakEnergy) show.peakEnergy = currentEnergy;
// check for silence
frame.silence = currentEnergy < 0.001;
// normalize frame energy against peak energy and compute frame stats
currentEnergy /= show.peakEnergy;
frame.dEnergy = currentEnergy - frame.energy;
frame.energy = currentEnergy;
} else
} else
frame.silence = true;
// update the frame
updateGL();
// update the frame
updateGL();
}
void GLAnalyzer3::paintGL()
{
// limit max dT to 0.05 and update color and scroll constants
if ( show.dT > 0.05 )
show.dT = 0.05;
show.colorK += show.dT * 0.4;
if ( show.colorK > 3.0 )
show.colorK -= 3.0;
show.gridScrollK += 0.2 * show.peakEnergy * show.dT;
void GLAnalyzer3::paintGL() {
// limit max dT to 0.05 and update color and scroll constants
if (show.dT > 0.05) show.dT = 0.05;
show.colorK += show.dT * 0.4;
if (show.colorK > 3.0) show.colorK -= 3.0;
show.gridScrollK += 0.2 * show.peakEnergy * show.dT;
// Switch to MODEL matrix and clear screen
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
glClear( GL_COLOR_BUFFER_BIT );
// Switch to MODEL matrix and clear screen
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glClear(GL_COLOR_BUFFER_BIT);
// Draw scrolling grid
if ( (show.gridEnergyK > 0.05) || (!frame.silence && frame.dEnergy < -0.3) )
{
show.gridEnergyK *= exp( -show.dT / 0.1 );
if ( -frame.dEnergy > show.gridEnergyK )
show.gridEnergyK = -frame.dEnergy*2.0;
float gridColor[4] = { 0.0, 1.0, 0.6, show.gridEnergyK };
drawScrollGrid( show.gridScrollK, gridColor );
}
// Draw scrolling grid
if ((show.gridEnergyK > 0.05) || (!frame.silence && frame.dEnergy < -0.3)) {
show.gridEnergyK *= exp(-show.dT / 0.1);
if (-frame.dEnergy > show.gridEnergyK)
show.gridEnergyK = -frame.dEnergy * 2.0;
float gridColor[4] = {0.0, 1.0, 0.6, show.gridEnergyK};
drawScrollGrid(show.gridScrollK, gridColor);
}
// Roll camera up/down handling the beat
show.camRot += show.camRoll * show.dT; // posision
show.camRoll -= 400 * show.camRot * show.dT; // elasticity
show.camRoll *= (1 - 2.0 * show.dT); // friction
if ( !frame.silence && frame.dEnergy > 0.4 )
show.camRoll += show.peakEnergy*2.0;
glRotatef( show.camRoll / 2.0, 1,0,0 );
// Roll camera up/down handling the beat
show.camRot += show.camRoll * show.dT; // posision
show.camRoll -= 400 * show.camRot * show.dT; // elasticity
show.camRoll *= (1 - 2.0 * show.dT); // friction
if (!frame.silence && frame.dEnergy > 0.4)
show.camRoll += show.peakEnergy * 2.0;
glRotatef(show.camRoll / 2.0, 1, 0, 0);
// Translate the drawing plane
glTranslatef( 0.0f, 0.0f, -1.8f );
// Translate the drawing plane
glTranslatef(0.0f, 0.0f, -1.8f);
// Draw upper/lower planes and paddles
drawHFace( -1.0 );
drawHFace( 1.0 );
leftPaddle->renderGL();
rightPaddle->renderGL();
// Draw upper/lower planes and paddles
drawHFace(-1.0);
drawHFace(1.0);
leftPaddle->renderGL();
rightPaddle->renderGL();
// Draw Balls
if ( ballTexture ) {
glEnable( GL_TEXTURE_2D );
glBindTexture( GL_TEXTURE_2D, ballTexture );
} else
glDisable( GL_TEXTURE_2D );
glEnable( GL_BLEND );
Ball * ball = balls.first();
for ( ; ball; ball = balls.next() )
{
float color[3],
angle = show.colorK;
// Draw Balls
if (ballTexture) {
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, ballTexture);
} else
glDisable(GL_TEXTURE_2D);
glEnable(GL_BLEND);
Ball* ball = balls.first();
for (; ball; ball = balls.next()) {
float color[3], angle = show.colorK;
// Rotate the color based on 'angle' value [0,3)
if ( angle < 1.0 )
{
color[ 0 ] = ball->color[ 0 ] * (1 - angle) + ball->color[ 1 ] * angle;
color[ 1 ] = ball->color[ 1 ] * (1 - angle) + ball->color[ 2 ] * angle;
color[ 2 ] = ball->color[ 2 ] * (1 - angle) + ball->color[ 0 ] * angle;
}
else if ( angle < 2.0 )
{
angle -= 1.0;
color[ 0 ] = ball->color[ 1 ] * (1 - angle) + ball->color[ 2 ] * angle;
color[ 1 ] = ball->color[ 2 ] * (1 - angle) + ball->color[ 0 ] * angle;
color[ 2 ] = ball->color[ 0 ] * (1 - angle) + ball->color[ 1 ] * angle;
}
else
{
angle -= 2.0;
color[ 0 ] = ball->color[ 2 ] * (1 - angle) + ball->color[ 0 ] * angle;
color[ 1 ] = ball->color[ 0 ] * (1 - angle) + ball->color[ 1 ] * angle;
color[ 2 ] = ball->color[ 1 ] * (1 - angle) + ball->color[ 2 ] * angle;
if (angle < 1.0) {
color[0] = ball->color[0] * (1 - angle) + ball->color[1] * angle;
color[1] = ball->color[1] * (1 - angle) + ball->color[2] * angle;
color[2] = ball->color[2] * (1 - angle) + ball->color[0] * angle;
} else if (angle < 2.0) {
angle -= 1.0;
color[0] = ball->color[1] * (1 - angle) + ball->color[2] * angle;
color[1] = ball->color[2] * (1 - angle) + ball->color[0] * angle;
color[2] = ball->color[0] * (1 - angle) + ball->color[1] * angle;
} else {
angle -= 2.0;
color[0] = ball->color[2] * (1 - angle) + ball->color[0] * angle;
color[1] = ball->color[0] * (1 - angle) + ball->color[1] * angle;
color[2] = ball->color[1] * (1 - angle) + ball->color[2] * angle;
}
// Draw the dot and update its physics also checking at bounces
glColor3fv( color );
drawDot3s( ball->x, ball->y, ball->z, 1.0 );
ball->updatePhysics( show.dT );
if ( ball->x < 0 )
leftPaddle->bounce( ball );
glColor3fv(color);
drawDot3s(ball->x, ball->y, ball->z, 1.0);
ball->updatePhysics(show.dT);
if (ball->x < 0)
leftPaddle->bounce(ball);
else
rightPaddle->bounce( ball );
}
glDisable( GL_BLEND );
glDisable( GL_TEXTURE_2D );
rightPaddle->bounce(ball);
}
glDisable(GL_BLEND);
glDisable(GL_TEXTURE_2D);
// Update physics of paddles
leftPaddle->updatePhysics( show.dT );
rightPaddle->updatePhysics( show.dT );
if ( !frame.silence )
{
leftPaddle->impulse( frame.energy*3.0 + frame.dEnergy*6.0 );
rightPaddle->impulse( -frame.energy*3.0 - frame.dEnergy*6.0 );
}
// Update physics of paddles
leftPaddle->updatePhysics(show.dT);
rightPaddle->updatePhysics(show.dT);
if (!frame.silence) {
leftPaddle->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 )
{
// Circular XY dot drawing functions
float sizeX = size * unitX,
sizeY = size * unitY,
pXm = x - sizeX,
pXM = x + sizeX,
pYm = y - sizeY,
pYM = y + sizeY;
// Draw the Dot
glBegin( GL_QUADS );
glTexCoord2f( 0, 0 ); // Bottom Left
glVertex3f( pXm, pYm, z );
glTexCoord2f( 0, 1 ); // Top Left
glVertex3f( pXm, pYM, z );
glTexCoord2f( 1, 1 ); // Top Right
glVertex3f( pXM, pYM, z );
glTexCoord2f( 1, 0 ); // Bottom Right
glVertex3f( pXM, pYm, z );
glEnd();
void GLAnalyzer3::drawDot3s(float x, float y, float z, float size) {
// Circular XY dot drawing functions
float sizeX = size * unitX, sizeY = size * unitY, pXm = x - sizeX,
pXM = x + sizeX, pYm = y - sizeY, pYM = y + sizeY;
// Draw the Dot
glBegin(GL_QUADS);
glTexCoord2f(0, 0); // Bottom Left
glVertex3f(pXm, pYm, z);
glTexCoord2f(0, 1); // Top Left
glVertex3f(pXm, pYM, z);
glTexCoord2f(1, 1); // Top Right
glVertex3f(pXM, pYM, z);
glTexCoord2f(1, 0); // Bottom Right
glVertex3f(pXM, pYm, z);
glEnd();
// Shadow XZ drawing functions
float sizeZ = size / 10.0,
pZm = z - sizeZ,
pZM = z + sizeZ,
currentColor[4];
glGetFloatv( GL_CURRENT_COLOR, currentColor );
float alpha = currentColor[3],
topSide = (y + 1) / 4,
bottomSide = (1 - y) / 4;
// Draw the top shadow
currentColor[3] = topSide * topSide * alpha;
glColor4fv( currentColor );
glBegin( GL_QUADS );
glTexCoord2f( 0, 0 ); // Bottom Left
glVertex3f( pXm, 1, pZm );
glTexCoord2f( 0, 1 ); // Top Left
glVertex3f( pXm, 1, pZM );
glTexCoord2f( 1, 1 ); // Top Right
glVertex3f( pXM, 1, pZM );
glTexCoord2f( 1, 0 ); // Bottom Right
glVertex3f( pXM, 1, pZm );
glEnd();
// Draw the bottom shadow
currentColor[3] = bottomSide * bottomSide * alpha;
glColor4fv( currentColor );
glBegin( GL_QUADS );
glTexCoord2f( 0, 0 ); // Bottom Left
glVertex3f( pXm, -1, pZm );
glTexCoord2f( 0, 1 ); // Top Left
glVertex3f( pXm, -1, pZM );
glTexCoord2f( 1, 1 ); // Top Right
glVertex3f( pXM, -1, pZM );
glTexCoord2f( 1, 0 ); // Bottom Right
glVertex3f( pXM, -1, pZm );
glEnd();
// Shadow XZ drawing functions
float sizeZ = size / 10.0, pZm = z - sizeZ, pZM = z + sizeZ, currentColor[4];
glGetFloatv(GL_CURRENT_COLOR, currentColor);
float alpha = currentColor[3], topSide = (y + 1) / 4,
bottomSide = (1 - y) / 4;
// Draw the top shadow
currentColor[3] = topSide * topSide * alpha;
glColor4fv(currentColor);
glBegin(GL_QUADS);
glTexCoord2f(0, 0); // Bottom Left
glVertex3f(pXm, 1, pZm);
glTexCoord2f(0, 1); // Top Left
glVertex3f(pXm, 1, pZM);
glTexCoord2f(1, 1); // Top Right
glVertex3f(pXM, 1, pZM);
glTexCoord2f(1, 0); // Bottom Right
glVertex3f(pXM, 1, pZm);
glEnd();
// Draw the bottom shadow
currentColor[3] = bottomSide * bottomSide * alpha;
glColor4fv(currentColor);
glBegin(GL_QUADS);
glTexCoord2f(0, 0); // Bottom Left
glVertex3f(pXm, -1, pZm);
glTexCoord2f(0, 1); // Top Left
glVertex3f(pXm, -1, pZM);
glTexCoord2f(1, 1); // Top Right
glVertex3f(pXM, -1, pZM);
glTexCoord2f(1, 0); // Bottom Right
glVertex3f(pXM, -1, pZm);
glEnd();
}
void GLAnalyzer3::drawHFace( float y )
{
glBegin( GL_TRIANGLE_STRIP );
glColor3f( 0.0f, 0.1f, 0.2f );
glVertex3f( -1.0f, y, 0.0 );
glVertex3f( 1.0f, y, 0.0 );
glColor3f( 0.1f, 0.6f, 0.5f );
glVertex3f( -1.0f, y, 2.0 );
glVertex3f( 1.0f, y, 2.0 );
glEnd();
void GLAnalyzer3::drawHFace(float y) {
glBegin(GL_TRIANGLE_STRIP);
glColor3f(0.0f, 0.1f, 0.2f);
glVertex3f(-1.0f, y, 0.0);
glVertex3f(1.0f, y, 0.0);
glColor3f(0.1f, 0.6f, 0.5f);
glVertex3f(-1.0f, y, 2.0);
glVertex3f(1.0f, y, 2.0);
glEnd();
}
void GLAnalyzer3::drawScrollGrid( float scroll, float color[4] )
{
if ( !gridTexture )
return;
glMatrixMode( GL_TEXTURE );
glLoadIdentity();
glTranslatef( 0.0, -scroll, 0.0 );
glMatrixMode( GL_MODELVIEW );
float backColor[4] = { 1.0, 1.0, 1.0, 0.0 };
for ( int i = 0; i < 3; i++ )
backColor[ i ] = color[ i ];
glEnable( GL_TEXTURE_2D );
glBindTexture( GL_TEXTURE_2D, gridTexture );
glEnable( GL_BLEND );
glBegin( GL_TRIANGLE_STRIP );
glColor4fv( color ); // top face
glTexCoord2f( 0.0f, 1.0f );
glVertex3f( -1.0f, 1.0f, -1.0f );
glTexCoord2f( 1.0f, 1.0f );
glVertex3f( 1.0f, 1.0f, -1.0f );
glColor4fv( backColor ); // central points
glTexCoord2f( 0.0f, 0.0f );
glVertex3f( -1.0f, 0.0f, -3.0f );
glTexCoord2f( 1.0f, 0.0f );
glVertex3f( 1.0f, 0.0f, -3.0f );
glColor4fv( color ); // bottom face
glTexCoord2f( 0.0f, 1.0f );
glVertex3f( -1.0f, -1.0f, -1.0f );
glTexCoord2f( 1.0f, 1.0f );
glVertex3f( 1.0f, -1.0f, -1.0f );
glEnd();
glDisable( GL_BLEND );
glDisable( GL_TEXTURE_2D );
glMatrixMode( GL_TEXTURE );
glLoadIdentity();
glMatrixMode( GL_MODELVIEW );
void GLAnalyzer3::drawScrollGrid(float scroll, float color[4]) {
if (!gridTexture) return;
glMatrixMode(GL_TEXTURE);
glLoadIdentity();
glTranslatef(0.0, -scroll, 0.0);
glMatrixMode(GL_MODELVIEW);
float backColor[4] = {1.0, 1.0, 1.0, 0.0};
for (int i = 0; i < 3; i++) backColor[i] = color[i];
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, gridTexture);
glEnable(GL_BLEND);
glBegin(GL_TRIANGLE_STRIP);
glColor4fv(color); // top face
glTexCoord2f(0.0f, 1.0f);
glVertex3f(-1.0f, 1.0f, -1.0f);
glTexCoord2f(1.0f, 1.0f);
glVertex3f(1.0f, 1.0f, -1.0f);
glColor4fv(backColor); // central points
glTexCoord2f(0.0f, 0.0f);
glVertex3f(-1.0f, 0.0f, -3.0f);
glTexCoord2f(1.0f, 0.0f);
glVertex3f(1.0f, 0.0f, -3.0f);
glColor4fv(color); // bottom face
glTexCoord2f(0.0f, 1.0f);
glVertex3f(-1.0f, -1.0f, -1.0f);
glTexCoord2f(1.0f, 1.0f);
glVertex3f(1.0f, -1.0f, -1.0f);
glEnd();
glDisable(GL_BLEND);
glDisable(GL_TEXTURE_2D);
glMatrixMode(GL_TEXTURE);
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
}
bool GLAnalyzer3::loadTexture( QString fileName, GLuint& textureID )
{
//reset texture ID to the default EMPTY value
textureID = 0;
bool GLAnalyzer3::loadTexture(QString fileName, GLuint& textureID) {
// reset texture ID to the default EMPTY value
textureID = 0;
//load image
QImage tmp;
if ( !tmp.load( fileName ) )
return false;
// load image
QImage tmp;
if (!tmp.load(fileName)) return false;
//convert it to suitable format (flipped RGBA)
QImage texture = QGLWidget::convertToGLFormat( tmp );
if ( texture.isNull() )
return false;
// convert it to suitable format (flipped RGBA)
QImage texture = QGLWidget::convertToGLFormat(tmp);
if (texture.isNull()) return false;
//get texture number and bind loaded image to that texture
glGenTextures( 1, &textureID );
glBindTexture( GL_TEXTURE_2D, textureID );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
glTexImage2D( GL_TEXTURE_2D, 0, 4, texture.width(), texture.height(),
0, GL_RGBA, GL_UNSIGNED_BYTE, texture.bits() );
return true;
// get texture number and bind loaded image to that texture
glGenTextures(1, &textureID);
glBindTexture(GL_TEXTURE_2D, textureID);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, 4, texture.width(), texture.height(), 0,
GL_RGBA, GL_UNSIGNED_BYTE, texture.bits());
return true;
}
void GLAnalyzer3::freeTexture( GLuint& textureID )
{
if ( textureID > 0 )
glDeleteTextures( 1, &textureID );
textureID = 0;
void GLAnalyzer3::freeTexture(GLuint& textureID) {
if (textureID > 0) glDeleteTextures(1, &textureID);
textureID = 0;
}
#endif

View File

@ -29,51 +29,50 @@ class QWidget;
class Ball;
class Paddle;
class GLAnalyzer3 : public Analyzer::Base3D
{
public:
GLAnalyzer3(QWidget *);
~GLAnalyzer3();
void analyze( const Scope & );
void paused();
class GLAnalyzer3 : public Analyzer::Base3D {
public:
GLAnalyzer3(QWidget*);
~GLAnalyzer3();
void analyze(const Scope&);
void paused();
protected:
void initializeGL();
void resizeGL( int w, int h );
void paintGL();
protected:
void initializeGL();
void resizeGL(int w, int h);
void paintGL();
private:
struct ShowProperties {
double timeStamp;
double dT;
float colorK;
float gridScrollK;
float gridEnergyK;
float camRot;
float camRoll;
float peakEnergy;
} show;
private:
struct ShowProperties {
double timeStamp;
double dT;
float colorK;
float gridScrollK;
float gridEnergyK;
float camRot;
float camRoll;
float peakEnergy;
} show;
struct FrameProperties {
bool silence;
float energy;
float dEnergy;
} frame;
struct FrameProperties {
bool silence;
float energy;
float dEnergy;
} frame;
static const int NUMBER_OF_BALLS = 16;
static const int NUMBER_OF_BALLS = 16;
QPtrList<Ball> balls;
Paddle * leftPaddle, * rightPaddle;
float unitX, unitY;
GLuint ballTexture;
GLuint gridTexture;
QPtrList<Ball> balls;
Paddle* leftPaddle, *rightPaddle;
float unitX, unitY;
GLuint ballTexture;
GLuint gridTexture;
void drawDot3s( float x, float y, float z, float size );
void drawHFace( float y );
void drawScrollGrid( float scroll, float color[4] );
void drawDot3s(float x, float y, float z, float size);
void drawHFace(float y);
void drawScrollGrid(float scroll, float color[4]);
bool loadTexture(QString file, GLuint& textureID);
void freeTexture(GLuint& textureID);
bool loadTexture(QString file, GLuint& textureID);
void freeTexture(GLuint& textureID);
};
#endif

View File

@ -26,34 +26,31 @@
const char* NyanCatAnalyzer::kName = "Nyanalyzer cat";
const float NyanCatAnalyzer::kPixelScale = 0.02f;
NyanCatAnalyzer::NyanCatAnalyzer(QWidget* parent)
: Analyzer::Base(parent, 9),
cat_(":/nyancat.png"),
timer_id_(startTimer(kFrameIntervalMs)),
frame_(0),
current_buffer_(0),
available_rainbow_width_(0),
px_per_frame_(0),
x_offset_(0),
background_brush_(QColor(0x0f, 0x43, 0x73))
{
: Analyzer::Base(parent, 9),
cat_(":/nyancat.png"),
timer_id_(startTimer(kFrameIntervalMs)),
frame_(0),
current_buffer_(0),
available_rainbow_width_(0),
px_per_frame_(0),
x_offset_(0),
background_brush_(QColor(0x0f, 0x43, 0x73)) {
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),
kCatHeight/kRainbowBands,
Qt::SolidLine, Qt::FlatCap, Qt::RoundJoin);
kCatHeight / kRainbowBands, Qt::SolidLine, Qt::FlatCap,
Qt::RoundJoin);
// pow constants computed so that
// | 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) {
m_fht->spectrum(&s.front());
}
void NyanCatAnalyzer::transform(Scope& s) { m_fht->spectrum(&s.front()); }
void NyanCatAnalyzer::timerEvent(QTimerEvent* e) {
if (e->timerId() == timer_id_) {
@ -70,18 +67,19 @@ void NyanCatAnalyzer::resizeEvent(QResizeEvent* e) {
buffer_[1] = QPixmap();
available_rainbow_width_ = width() - kCatWidth + kRainbowOverlap;
px_per_frame_ = float(available_rainbow_width_) / (kHistorySize-1) + 1;
x_offset_ = px_per_frame_ * (kHistorySize-1) - available_rainbow_width_;
px_per_frame_ = float(available_rainbow_width_) / (kHistorySize - 1) + 1;
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
const int scope_size = s.size() / 2;
if ((new_frame && is_playing_) ||
(buffer_[0].isNull() && buffer_[1].isNull())) {
// Transform the music into rainbows!
for (int band=0 ; band<kRainbowBands ; ++band) {
for (int band = 0; band < kRainbowBands; ++band) {
float* band_start = history_ + band * kHistorySize;
// Move the history of each band across by 1 frame.
@ -93,13 +91,13 @@ void NyanCatAnalyzer::analyze(QPainter& p, const Analyzer::Scope& s, bool new_fr
// but for now it's a series of separate square filters.
const int samples_per_band = scope_size / kRainbowBands;
int sample = 0;
for (int band=0 ; band<kRainbowBands ; ++band) {
for (int band = 0; band < kRainbowBands; ++band) {
float accumulator = 0.0;
for (int i=0 ; i<samples_per_band ; ++i) {
for (int i = 0; i < samples_per_band; ++i) {
accumulator += s[sample++];
}
history_[(band+1) * kHistorySize - 1] = accumulator * band_scale_[band];
history_[(band + 1) * kHistorySize - 1] = accumulator * band_scale_[band];
}
// Create polylines for the rainbows.
@ -107,22 +105,23 @@ void NyanCatAnalyzer::analyze(QPainter& p, const Analyzer::Scope& s, bool new_fr
QPointF* dest = polyline;
float* source = history_;
const float top_of_cat = float(height())/2 - float(kCatHeight)/2;
for (int band=0 ; band<kRainbowBands ; ++band) {
const float top_of_cat = float(height()) / 2 - float(kCatHeight) / 2;
for (int band = 0; band < kRainbowBands; ++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.
for (int x=0 ; x<kHistorySize; ++x) {
*dest = QPointF(px_per_frame_ * x, y + *source * kPixelScale);
++ dest;
++ source;
for (int x = 0; x < kHistorySize; ++x) {
*dest = QPointF(px_per_frame_ * x, y + *source * kPixelScale);
++dest;
++source;
}
}
// Do we have to draw the whole rainbow into the buffer?
if (buffer_[0].isNull()) {
for (int i=0 ; i<2 ; ++i) {
for (int i = 0; i < 2; ++i) {
buffer_[i] = QPixmap(QSize(width() + x_offset_, height()));
buffer_[i].fill(background_brush_.color());
}
@ -130,29 +129,34 @@ void NyanCatAnalyzer::analyze(QPainter& p, const Analyzer::Scope& s, bool new_fr
QPainter buffer_painter(&buffer_[0]);
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.drawPolyline(&polyline[band*kHistorySize], kHistorySize);
buffer_painter.drawPolyline(&polyline[band*kHistorySize], kHistorySize);
buffer_painter.drawPolyline(&polyline[band * kHistorySize],
kHistorySize);
buffer_painter.drawPolyline(&polyline[band * kHistorySize],
kHistorySize);
}
} else {
const int last_buffer = current_buffer_;
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_]);
buffer_painter.setRenderHint(QPainter::Antialiasing);
buffer_painter.drawPixmap(0, 0, buffer_[last_buffer],
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,
kCatWidth - kRainbowOverlap + px_per_frame_, height(),
background_brush_);
buffer_painter.drawPixmap(
0, 0, buffer_[last_buffer], 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,
kCatWidth - kRainbowOverlap + px_per_frame_, height(),
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.drawPolyline(&polyline[(band+1)*kHistorySize - 3], 3);
buffer_painter.drawPolyline(&polyline[(band + 1) * kHistorySize - 3],
3);
}
}
}

View File

@ -25,19 +25,19 @@
class NyanCatAnalyzer : public Analyzer::Base {
Q_OBJECT
public:
public:
Q_INVOKABLE NyanCatAnalyzer(QWidget* parent);
static const char* kName;
protected:
protected:
void transform(Scope&);
void analyze(QPainter& p, const Analyzer::Scope&, bool new_frame);
void timerEvent(QTimerEvent* e);
void resizeEvent(QResizeEvent* e);
private:
private:
static const int kCatHeight = 21;
static const int kCatWidth = 34;
static const int kCatFrameCount = 6;
@ -50,7 +50,7 @@ private:
static const int kFrameIntervalMs = 150;
private:
private:
inline QRect CatSourceRect() const {
return QRect(0, kCatHeight * frame_, kCatWidth, kCatHeight);
}
@ -60,8 +60,8 @@ private:
}
inline QRect CatDestRect() const {
return QRect(width() - kCatWidth, (height() - kCatHeight) / 2,
kCatWidth, kCatHeight);
return QRect(width() - kCatWidth, (height() - kCatHeight) / 2, kCatWidth,
kCatHeight);
}
inline QRect SleepingCatDestRect() const {
@ -69,7 +69,7 @@ private:
kCatWidth, kSleepingCatHeight);
}
private:
private:
// "constants" that get initialised in the constructor
float band_scale_[kRainbowBands];
QPen colors_[kRainbowBands];
@ -102,4 +102,4 @@ private:
QBrush background_brush_;
};
#endif // NYANCATANALYZER_H
#endif // NYANCATANALYZER_H

View File

@ -15,76 +15,61 @@
#include <QPainter>
const char* Sonogram::kName = QT_TRANSLATE_NOOP("AnalyzerContainer", "Sonogram");
const char* Sonogram::kName =
QT_TRANSLATE_NOOP("AnalyzerContainer", "Sonogram");
Sonogram::Sonogram(QWidget *parent) :
Analyzer::Base(parent, 9)
{
}
Sonogram::Sonogram(QWidget* parent) : Analyzer::Base(parent, 9) {}
Sonogram::~Sonogram() {}
Sonogram::~Sonogram()
{
}
void Sonogram::resizeEvent(QResizeEvent* e) {
QWidget::resizeEvent(e);
void Sonogram::resizeEvent(QResizeEvent *e)
{
QWidget::resizeEvent(e);
//only for gcc < 4.0
#if !( __GNUC__ > 4 || ( __GNUC__ == 4 && __GNUC_MINOR__ >= 0 ) )
resizeForBands(height() < 128 ? 128 : height());
// only for gcc < 4.0
#if !(__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 0))
resizeForBands(height() < 128 ? 128 : height());
#endif
canvas_ = QPixmap(size());
canvas_.fill(palette().color(QPalette::Background));
canvas_ = QPixmap(size());
canvas_.fill(palette().color(QPalette::Background));
}
void Sonogram::analyze(QPainter& p, const Scope& s, bool new_frame) {
int x = width() - 1;
QColor c;
void Sonogram::analyze(QPainter& p, const Scope &s, bool new_frame)
{
int x = width() - 1;
QColor c;
QPainter canvas_painter(&canvas_);
canvas_painter.drawPixmap(0, 0, canvas_, 1, 0, x, -1);
QPainter canvas_painter(&canvas_);
canvas_painter.drawPixmap(0, 0, canvas_, 1, 0, x, -1);
Scope::const_iterator it = s.begin(), end = s.end();
for (int y = height() - 1; y;) {
if (it >= end || *it < .005)
c = palette().color(QPalette::Background);
else if (*it < .05)
c.setHsv(95, 255, 255 - int(*it * 4000.0));
else if (*it < 1.0)
c.setHsv(95 - int(*it * 90.0), 255, 255);
else
c = Qt::red;
Scope::const_iterator it = s.begin(), end = s.end();
for (int y = height() - 1; y;) {
if (it >= end || *it < .005)
c = palette().color(QPalette::Background);
else if (*it < .05)
c.setHsv(95, 255, 255 - int(*it * 4000.0));
else if (*it < 1.0)
c.setHsv(95 - int(*it * 90.0), 255, 255);
else
c = Qt::red;
canvas_painter.setPen(c);
canvas_painter.drawPoint(x, y--);
canvas_painter.setPen(c);
canvas_painter.drawPoint(x, y--);
if (it < end) ++it;
}
if (it < end)
++it;
}
canvas_painter.end();
canvas_painter.end();
p.drawPixmap(0, 0, canvas_);
p.drawPixmap(0, 0, canvas_);
}
void Sonogram::transform(Scope &scope)
{
float *front = static_cast<float*>(&scope.front());
m_fht->power2(front);
m_fht->scale(front, 1.0 / 256);
scope.resize( m_fht->size() / 2 );
void Sonogram::transform(Scope& scope) {
float* front = static_cast<float*>(&scope.front());
m_fht->power2(front);
m_fht->scale(front, 1.0 / 256);
scope.resize(m_fht->size() / 2);
}
void Sonogram::demo(QPainter& p)
{
analyze(p, Scope(m_fht->size(), 0), new_frame_);
void Sonogram::demo(QPainter& p) {
analyze(p, Scope(m_fht->size(), 0), new_frame_);
}

View File

@ -20,22 +20,21 @@
@author Melchior FRANZ
*/
class Sonogram : public Analyzer::Base
{
class Sonogram : public Analyzer::Base {
Q_OBJECT
public:
Q_INVOKABLE Sonogram(QWidget*);
~Sonogram();
public:
Q_INVOKABLE Sonogram(QWidget*);
~Sonogram();
static const char* kName;
static const char* kName;
protected:
void analyze(QPainter& p, const Scope&, bool new_frame);
void transform(Scope&);
void demo(QPainter& p);
void resizeEvent(QResizeEvent*);
protected:
void analyze(QPainter& p, const Scope&, bool new_frame);
void transform(Scope&);
void demo(QPainter& p);
void resizeEvent(QResizeEvent*);
QPixmap canvas_;
QPixmap canvas_;
};
#endif

View File

@ -12,66 +12,57 @@
#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)
{
float h;
const uint hd2 = height() / 2;
const uint MAX_HEIGHT = hd2 - 1;
void TurbineAnalyzer::analyze(QPainter& p, const Scope& scope, bool new_frame) {
float h;
const uint hd2 = height() / 2;
const uint MAX_HEIGHT = hd2 - 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;
for (uint i = 0, x = 0, y; i < BAND_COUNT; ++i, x += COLUMN_WIDTH + 1) {
h = log10(scope[i] * 256.0) * F * 0.5;
if( h > MAX_HEIGHT )
h = MAX_HEIGHT;
if (h > MAX_HEIGHT) h = MAX_HEIGHT;
if( h > bar_height[i] )
{
bar_height[i] = h;
if (h > bar_height[i]) {
bar_height[i] = h;
if( h > peak_height[i] )
{
peak_height[i] = h;
peak_speed[i] = 0.01;
}
else goto peak_handling;
}
else
{
if( bar_height[i] > 0.0 )
{
bar_height[i] -= K_barHeight; //1.4
if( bar_height[i] < 0.0 ) bar_height[i] = 0.0;
}
if (h > peak_height[i]) {
peak_height[i] = h;
peak_speed[i] = 0.01;
} else
goto peak_handling;
} else {
if (bar_height[i] > 0.0) {
bar_height[i] -= K_barHeight; // 1.4
if (bar_height[i] < 0.0) bar_height[i] = 0.0;
}
peak_handling:
peak_handling:
if( peak_height[i] > 0.0 )
{
peak_height[i] -= peak_speed[i];
peak_speed[i] *= F_peakSpeed; //1.12
if (peak_height[i] > 0.0) {
peak_height[i] -= peak_speed[i];
peak_speed[i] *= F_peakSpeed; // 1.12
if( peak_height[i] < bar_height[i] ) peak_height[i] = bar_height[i];
if( peak_height[i] < 0.0 ) peak_height[i] = 0.0;
}
}
y = hd2 - uint(bar_height[i]);
p.drawPixmap(x+1, y, barPixmap, 0, y, -1, -1);
p.drawPixmap(x+1, hd2, barPixmap, 0, int(bar_height[i]), -1, -1);
p.setPen( palette().color(QPalette::Highlight) );
if (bar_height[i] > 0)
p.drawRect( x, y, COLUMN_WIDTH-1, (int)bar_height[i]*2 -1 );
const uint x2 = x+COLUMN_WIDTH-1;
p.setPen( palette().color(QPalette::Base) );
y = hd2 - uint(peak_height[i]);
p.drawLine( x, y, x2, y );
y = hd2 + uint(peak_height[i]);
p.drawLine( x, y, x2, y );
if (peak_height[i] < bar_height[i]) peak_height[i] = bar_height[i];
if (peak_height[i] < 0.0) peak_height[i] = 0.0;
}
}
y = hd2 - uint(bar_height[i]);
p.drawPixmap(x + 1, y, barPixmap, 0, y, -1, -1);
p.drawPixmap(x + 1, hd2, barPixmap, 0, int(bar_height[i]), -1, -1);
p.setPen(palette().color(QPalette::Highlight));
if (bar_height[i] > 0)
p.drawRect(x, y, COLUMN_WIDTH - 1, (int)bar_height[i] * 2 - 1);
const uint x2 = x + COLUMN_WIDTH - 1;
p.setPen(palette().color(QPalette::Base));
y = hd2 - uint(peak_height[i]);
p.drawLine(x, y, x2, y);
y = hd2 + uint(peak_height[i]);
p.drawLine(x, y, x2, y);
}
}

View File

@ -11,15 +11,14 @@
#include "boomanalyzer.h"
class TurbineAnalyzer : public BoomAnalyzer
{
class TurbineAnalyzer : public BoomAnalyzer {
Q_OBJECT
public:
Q_INVOKABLE TurbineAnalyzer( QWidget *parent ) : BoomAnalyzer( parent ) {}
public:
Q_INVOKABLE TurbineAnalyzer(QWidget* parent) : BoomAnalyzer(parent) {}
void analyze( QPainter& p, const Scope&, bool new_frame);
void analyze(QPainter& p, const Scope&, bool new_frame);
static const char* kName;
static const char* kName;
};
#endif

View File

@ -27,24 +27,21 @@ const char* Appearance::kBackgroundColor = "background-color";
const QPalette Appearance::kDefaultPalette = QPalette();
Appearance::Appearance(QObject* parent)
: QObject(parent)
{
Appearance::Appearance(QObject* parent) : QObject(parent) {
QSettings s;
s.beginGroup(kSettingsGroup);
QPalette p = QApplication::palette();
background_color_ = s.value(kBackgroundColor,
p.color(QPalette::WindowText)).value<QColor>();
foreground_color_ = s.value(kForegroundColor,
p.color(QPalette::Window)).value<QColor>();
background_color_ =
s.value(kBackgroundColor, p.color(QPalette::WindowText)).value<QColor>();
foreground_color_ =
s.value(kForegroundColor, p.color(QPalette::Window)).value<QColor>();
}
void Appearance::LoadUserTheme() {
QSettings s;
s.beginGroup(kSettingsGroup);
bool use_a_custom_color_set = s.value(kUseCustomColorSet).toBool();
if (!use_a_custom_color_set)
return;
if (!use_a_custom_color_set) return;
ChangeForegroundColor(foreground_color_);
ChangeBackgroundColor(background_color_);

View File

@ -22,24 +22,24 @@
#include <QPalette>
class Appearance : public QObject {
public:
Appearance(QObject* parent = NULL);
// Load the user preferred theme, which could the default system theme or a
// custom set of colors that user has chosen
void LoadUserTheme();
void ResetToSystemDefaultTheme();
void ChangeForegroundColor(const QColor& color);
void ChangeBackgroundColor(const QColor& color);
public:
Appearance(QObject* parent = NULL);
// Load the user preferred theme, which could the default system theme or a
// custom set of colors that user has chosen
void LoadUserTheme();
void ResetToSystemDefaultTheme();
void ChangeForegroundColor(const QColor& color);
void ChangeBackgroundColor(const QColor& color);
static const char* kSettingsGroup;
static const char* kUseCustomColorSet;
static const char* kForegroundColor;
static const char* kBackgroundColor;
static const QPalette kDefaultPalette;
static const char* kSettingsGroup;
static const char* kUseCustomColorSet;
static const char* kForegroundColor;
static const char* kBackgroundColor;
static const QPalette kDefaultPalette;
private:
QColor foreground_color_;
QColor background_color_;
private:
QColor foreground_color_;
QColor background_color_;
};
#endif // APPEARANCE_H
#endif // APPEARANCE_H

View File

@ -40,37 +40,36 @@
#include "podcasts/podcastupdater.h"
#ifdef HAVE_MOODBAR
# include "moodbar/moodbarcontroller.h"
# include "moodbar/moodbarloader.h"
#include "moodbar/moodbarcontroller.h"
#include "moodbar/moodbarloader.h"
#endif
bool Application::kIsPortable = false;
Application::Application(QObject* parent)
: QObject(parent),
tag_reader_client_(nullptr),
database_(nullptr),
album_cover_loader_(nullptr),
playlist_backend_(nullptr),
podcast_backend_(nullptr),
appearance_(nullptr),
cover_providers_(nullptr),
task_manager_(nullptr),
player_(nullptr),
playlist_manager_(nullptr),
current_art_loader_(nullptr),
global_search_(nullptr),
internet_model_(nullptr),
library_(nullptr),
device_manager_(nullptr),
podcast_updater_(nullptr),
podcast_downloader_(nullptr),
gpodder_sync_(nullptr),
moodbar_loader_(nullptr),
moodbar_controller_(nullptr),
network_remote_(nullptr),
network_remote_helper_(nullptr)
{
: QObject(parent),
tag_reader_client_(nullptr),
database_(nullptr),
album_cover_loader_(nullptr),
playlist_backend_(nullptr),
podcast_backend_(nullptr),
appearance_(nullptr),
cover_providers_(nullptr),
task_manager_(nullptr),
player_(nullptr),
playlist_manager_(nullptr),
current_art_loader_(nullptr),
global_search_(nullptr),
internet_model_(nullptr),
library_(nullptr),
device_manager_(nullptr),
podcast_updater_(nullptr),
podcast_downloader_(nullptr),
gpodder_sync_(nullptr),
moodbar_loader_(nullptr),
moodbar_controller_(nullptr),
network_remote_(nullptr),
network_remote_helper_(nullptr) {
tag_reader_client_ = new TagReaderClient(this);
MoveToNewThread(tag_reader_client_);
tag_reader_client_->Start();
@ -111,7 +110,8 @@ Application::Application(QObject* parent)
MoveToNewThread(network_remote_);
// 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
// crash when a client connects before the manager is initialized!
network_remote_helper_ = new NetworkRemoteHelper(this);
@ -125,19 +125,14 @@ Application::~Application() {
// It's important that the device manager is deleted before the database.
// Deleting the database deletes all objects that have been created in its
// thread, including some device library backends.
delete device_manager_; device_manager_ = nullptr;
delete device_manager_;
device_manager_ = nullptr;
foreach (QObject* object, objects_in_threads_) {
object->deleteLater();
}
foreach(QObject * object, objects_in_threads_) { object->deleteLater(); }
foreach (QThread* thread, threads_) {
thread->quit();
}
foreach(QThread * thread, threads_) { thread->quit(); }
foreach (QThread* thread, threads_) {
thread->wait();
}
foreach(QThread * thread, threads_) { thread->wait(); }
}
void Application::MoveToNewThread(QObject* object) {
@ -155,9 +150,7 @@ void Application::MoveToThread(QObject* object, QThread* thread) {
objects_in_threads_ << object;
}
void Application::AddError(const QString& message) {
emit ErrorAdded(message);
}
void Application::AddError(const QString& message) { emit ErrorAdded(message); }
QString Application::language_without_region() const {
const int underscore = language_name_.indexOf('_');
@ -171,13 +164,9 @@ LibraryBackend* Application::library_backend() const {
return library()->backend();
}
LibraryModel* Application::library_model() const {
return library()->model();
}
LibraryModel* Application::library_model() const { return library()->model(); }
void Application::ReloadSettings() {
emit SettingsChanged();
}
void Application::ReloadSettings() { emit SettingsChanged(); }
void Application::OpenSettingsDialogAtPage(SettingsDialog::Page page) {
emit SettingsDialogRequested(page);

View File

@ -47,18 +47,18 @@ class PodcastUpdater;
class TagReaderClient;
class TaskManager;
class Application : public QObject {
Q_OBJECT
public:
public:
static bool kIsPortable;
Application(QObject* parent = NULL);
~Application();
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;
void set_language_name(const QString& name) { language_name_ = name; }
@ -83,7 +83,9 @@ public:
MoodbarLoader* moodbar_loader() const { return moodbar_loader_; }
MoodbarController* moodbar_controller() const { return moodbar_controller_; }
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;
LibraryModel* library_model() const;
@ -91,7 +93,7 @@ public:
void MoveToNewThread(QObject* object);
void MoveToThread(QObject* object, QThread* thread);
public slots:
public slots:
void AddError(const QString& message);
void ReloadSettings();
void OpenSettingsDialogAtPage(SettingsDialog::Page page);
@ -101,7 +103,7 @@ signals:
void SettingsChanged();
void SettingsDialogRequested(SettingsDialog::Page page);
private:
private:
QString language_name_;
TagReaderClient* tag_reader_client_;
@ -131,4 +133,4 @@ private:
QList<QThread*> threads_;
};
#endif // APPLICATION_H
#endif // APPLICATION_H

View File

@ -9,17 +9,14 @@
const char* BackgroundStreams::kSettingsGroup = "BackgroundStreams";
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:///";
BackgroundStreams::BackgroundStreams(EngineBase* engine, QObject* parent)
: QObject(parent),
engine_(engine) {
}
: QObject(parent), engine_(engine) {}
BackgroundStreams::~BackgroundStreams() {
SaveStreams();
}
BackgroundStreams::~BackgroundStreams() { SaveStreams(); }
void BackgroundStreams::LoadStreams() {
QSettings s;
@ -39,8 +36,7 @@ void BackgroundStreams::LoadStreams() {
int size = s.beginReadArray("streams");
for (int i = 0; i < size; ++i) {
s.setArrayIndex(i);
AddStream(s.value("name").toString(),
s.value("url").toUrl(),
AddStream(s.value("name").toString(), s.value("url").toUrl(),
s.value("volume").toInt());
}
@ -63,8 +59,7 @@ void BackgroundStreams::SaveStreams() {
s.endArray();
}
void BackgroundStreams::AddStream(const QString& name,
const QUrl& url,
void BackgroundStreams::AddStream(const QString& name, const QUrl& url,
int volume) {
if (streams_.contains(name)) {
return;
@ -134,7 +129,8 @@ bool BackgroundStreams::IsPlaying(const QString& name) const {
void BackgroundStreams::AddAction(const QString& name, QAction* action) {
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;
}
@ -156,7 +152,7 @@ void BackgroundStreams::StreamActionDestroyed() {
return;
}
foreach (Stream* stream, streams_.values()) {
foreach(Stream * stream, streams_.values()) {
if (stream->action == action) {
stream->action = nullptr;
}
@ -169,7 +165,7 @@ void BackgroundStreams::StreamActionToggled(bool checked) {
return;
}
foreach (Stream* stream, streams_.values()) {
foreach(Stream * stream, streams_.values()) {
if (stream->action == action) {
EnableStream(stream->name, checked);
}

View File

@ -29,7 +29,7 @@ class BackgroundStreams : public QObject {
void AddAction(const QString& name, QAction* action);
signals:
signals:
void StreamStarted(const QString& name);
void StreamStopped(const QString& name);

View File

@ -9,12 +9,9 @@ template <typename T, typename D>
class BoundFutureWatcher : public QFutureWatcher<T>, boost::noncopyable {
public:
BoundFutureWatcher(const D& data, QObject* parent = 0)
: QFutureWatcher<T>(parent),
data_(data) {
}
: QFutureWatcher<T>(parent), data_(data) {}
~BoundFutureWatcher() {
}
~BoundFutureWatcher() {}
const D& data() const { return data_; }

View File

@ -23,7 +23,7 @@
template <typename T>
class CachedList {
public:
public:
// Use a CachedList when you want to download and save a list of things from a
// remote service, updating it only periodically.
// T must be a registered metatype and must support being stored in
@ -34,10 +34,9 @@ public:
CachedList(const QString& settings_group, const QString& name,
int cache_duration_secs)
: settings_group_(settings_group),
name_(name),
cache_duration_secs_(cache_duration_secs) {
}
: settings_group_(settings_group),
name_(name),
cache_duration_secs_(cache_duration_secs) {}
void Load() {
QSettings s;
@ -47,7 +46,7 @@ public:
data_.clear();
const int count = s.beginReadArray(name_ + "_data");
for (int i=0 ; i<count ; ++i) {
for (int i = 0; i < count; ++i) {
s.setArrayIndex(i);
data_ << s.value("value").value<T>();
}
@ -61,7 +60,7 @@ public:
s.setValue("last_refreshed_" + name_, last_updated_);
s.beginWriteArray(name_ + "_data", data_.size());
for (int i=0 ; i<data_.size() ; ++i) {
for (int i = 0; i < data_.size(); ++i) {
s.setArrayIndex(i);
s.setValue("value", QVariant::fromValue(data_[i]));
}
@ -76,12 +75,11 @@ public:
bool IsStale() const {
return last_updated_.isNull() ||
last_updated_.secsTo(QDateTime::currentDateTime()) > cache_duration_secs_;
last_updated_.secsTo(QDateTime::currentDateTime()) >
cache_duration_secs_;
}
void Sort() {
qSort(data_);
}
void Sort() { qSort(data_); }
const ListType& Data() const { return data_; }
operator ListType() const { return data_; }
@ -91,7 +89,7 @@ public:
const_iterator begin() const { return data_.begin(); }
const_iterator end() const { return data_.end(); }
private:
private:
const QString settings_group_;
const QString name_;
const int cache_duration_secs_;
@ -100,4 +98,4 @@ private:
ListType data_;
};
#endif // CACHEDLIST_H
#endif // CACHEDLIST_H

View File

@ -28,7 +28,6 @@
#include <QCoreApplication>
#include <QFileInfo>
const char* CommandlineOptions::kHelpText =
"%1: clementine [%2] [%3]\n"
"\n"
@ -62,23 +61,21 @@ const char* CommandlineOptions::kHelpText =
" --log-levels <levels> %29\n"
" --version %30\n";
const char* CommandlineOptions::kVersionText =
"Clementine %1";
const char* CommandlineOptions::kVersionText = "Clementine %1";
CommandlineOptions::CommandlineOptions(int argc, char** argv)
: argc_(argc),
argv_(argv),
url_list_action_(UrlList_Append),
player_action_(Player_None),
set_volume_(-1),
volume_modifier_(0),
seek_to_(-1),
seek_by_(0),
play_track_at_(-1),
show_osd_(false),
toggle_pretty_osd_(false),
log_levels_(logging::kDefaultLogLevels)
{
: argc_(argc),
argv_(argv),
url_list_action_(UrlList_Append),
player_action_(Player_None),
set_volume_(-1),
volume_modifier_(0),
seek_to_(-1),
seek_by_(0),
play_track_at_(-1),
show_osd_(false),
toggle_pretty_osd_(false),
log_levels_(logging::kDefaultLogLevels) {
#ifdef Q_OS_DARWIN
// Remove -psn_xxx option that Mac passes when opened from Finder.
RemoveArg("-psn", 1);
@ -93,7 +90,7 @@ void CommandlineOptions::RemoveArg(const QString& starts_with, int count) {
QString opt(argv_[i]);
if (opt.startsWith(starts_with)) {
for (int j = i; j < argc_ - count + 1; ++j) {
argv_[j] = argv_[j+count];
argv_[j] = argv_[j + count];
}
argc_ -= count;
break;
@ -103,37 +100,32 @@ void CommandlineOptions::RemoveArg(const QString& starts_with, int count) {
bool CommandlineOptions::Parse() {
static const struct option kOptions[] = {
{"help", no_argument, 0, 'h'},
{"play", no_argument, 0, 'p'},
{"play-pause", no_argument, 0, 't'},
{"pause", no_argument, 0, 'u'},
{"stop", no_argument, 0, 's'},
{"previous", no_argument, 0, 'r'},
{"next", no_argument, 0, 'f'},
{"volume", required_argument, 0, 'v'},
{"volume-up", no_argument, 0, VolumeUp},
{"volume-down", no_argument, 0, VolumeDown},
{"volume-increase-by", required_argument, 0, VolumeIncreaseBy},
{"volume-decrease-by", required_argument, 0, VolumeDecreaseBy},
{"seek-to", required_argument, 0, SeekTo},
{"seek-by", required_argument, 0, SeekBy},
{"restart-or-previous", no_argument, 0, RestartOrPrevious},
{"append", no_argument, 0, 'a'},
{"load", no_argument, 0, 'l'},
{"play-track", required_argument, 0, 'k'},
{"show-osd", no_argument, 0, 'o'},
{"toggle-pretty-osd", no_argument, 0, 'y'},
{"language", required_argument, 0, 'g'},
{"quiet", no_argument, 0, Quiet},
{"verbose", no_argument, 0, Verbose},
{"log-levels", required_argument, 0, LogLevels},
{"version", no_argument, 0, Version},
{0, 0, 0, 0}
};
{"help", no_argument, 0, 'h'},
{"play", no_argument, 0, 'p'},
{"play-pause", no_argument, 0, 't'},
{"pause", no_argument, 0, 'u'},
{"stop", no_argument, 0, 's'},
{"previous", no_argument, 0, 'r'},
{"next", no_argument, 0, 'f'},
{"volume", required_argument, 0, 'v'},
{"volume-up", no_argument, 0, VolumeUp},
{"volume-down", no_argument, 0, VolumeDown},
{"volume-increase-by", required_argument, 0, VolumeIncreaseBy},
{"volume-decrease-by", required_argument, 0, VolumeDecreaseBy},
{"seek-to", required_argument, 0, SeekTo},
{"seek-by", required_argument, 0, SeekBy},
{"restart-or-previous", no_argument, 0, RestartOrPrevious},
{"append", no_argument, 0, 'a'},
{"load", no_argument, 0, 'l'},
{"play-track", required_argument, 0, 'k'},
{"show-osd", no_argument, 0, 'o'},
{"toggle-pretty-osd", no_argument, 0, 'y'},
{"language", required_argument, 0, 'g'},
{"quiet", no_argument, 0, Quiet},
{"verbose", no_argument, 0, Verbose},
{"log-levels", required_argument, 0, LogLevels},
{"version", no_argument, 0, Version},
{0, 0, 0, 0}};
// Parse the arguments
bool ok = false;
@ -141,62 +133,97 @@ bool CommandlineOptions::Parse() {
int c = getopt_long(argc_, argv_, "hptusrfv:alk:oyg:", kOptions, nullptr);
// End of the options
if (c == -1)
break;
if (c == -1) break;
switch (c) {
case 'h': {
QString translated_help_text = QString(kHelpText).arg(
tr("Usage"), tr("options"), tr("URL(s)"), tr("Player options"),
tr("Start the playlist currently playing"),
tr("Play if stopped, pause if playing"),
tr("Pause playback"),
tr("Stop playback"),
tr("Skip backwards in playlist")).arg(
tr("Skip forwards in playlist"),
tr("Set the volume to <value> percent"),
tr("Increase the volume by 4%"),
tr("Decrease the volume by 4%"),
tr("Increase the volume by <value> percent"),
tr("Decrease the volume by <value> percent")).arg(
tr("Seek the currently playing track to an absolute position"),
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("Append files/URLs to the playlist"),
tr("Loads files/URLs, replacing current playlist"),
tr("Play the <n>th track in the playlist")).arg(
tr("Other options"),
tr("Display the on-screen-display"),
tr("Toggle visibility for the pretty on-screen-display"),
tr("Change the language"),
tr("Equivalent to --log-levels *:1"),
tr("Equivalent to --log-levels *:3"),
tr("Comma separated list of class:level, level is 0-3")).arg(
tr("Print out version information"));
QString translated_help_text =
QString(kHelpText)
.arg(tr("Usage"), tr("options"), tr("URL(s)"),
tr("Player options"),
tr("Start the playlist currently playing"),
tr("Play if stopped, pause if playing"),
tr("Pause playback"), tr("Stop playback"),
tr("Skip backwards in playlist"))
.arg(tr("Skip forwards in playlist"),
tr("Set the volume to <value> percent"),
tr("Increase the volume by 4%"),
tr("Decrease the volume by 4%"),
tr("Increase the volume by <value> percent"),
tr("Decrease the volume by <value> percent"))
.arg(tr("Seek the currently playing track to an absolute "
"position"),
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("Append files/URLs to the playlist"),
tr("Loads files/URLs, replacing current playlist"),
tr("Play the <n>th track in the playlist"))
.arg(tr("Other options"), tr("Display the on-screen-display"),
tr("Toggle visibility for the pretty on-screen-display"),
tr("Change the language"),
tr("Equivalent to --log-levels *:1"),
tr("Equivalent to --log-levels *:3"),
tr("Comma separated list of class:level, level is 0-3"))
.arg(tr("Print out version information"));
std::cout << translated_help_text.toLocal8Bit().constData();
return false;
}
case 'p': player_action_ = Player_Play; break;
case 't': player_action_ = Player_PlayPause; break;
case 'u': player_action_ = Player_Pause; break;
case 's': player_action_ = Player_Stop; break;
case 'r': player_action_ = Player_Previous; 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 'p':
player_action_ = Player_Play;
break;
case 't':
player_action_ = Player_PlayPause;
break;
case 'u':
player_action_ = Player_Pause;
break;
case 's':
player_action_ = Player_Stop;
break;
case 'r':
player_action_ = Player_Previous;
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: {
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::exit(0);
}
@ -241,7 +268,7 @@ bool CommandlineOptions::Parse() {
}
// Get any filenames or URLs following the arguments
for (int i=optind ; i<argc_ ; ++i) {
for (int i = optind; i < argc_; ++i) {
QString value = QFile::decodeName(argv_[i]);
QFileInfo file_info(value);
if (file_info.exists())
@ -254,15 +281,10 @@ bool CommandlineOptions::Parse() {
}
bool CommandlineOptions::is_empty() const {
return player_action_ == Player_None &&
set_volume_ == -1 &&
volume_modifier_ == 0 &&
seek_to_ == -1 &&
seek_by_ == 0 &&
play_track_at_ == -1 &&
show_osd_ == false &&
toggle_pretty_osd_ == false &&
urls_.isEmpty();
return player_action_ == Player_None && set_volume_ == -1 &&
volume_modifier_ == 0 && seek_to_ == -1 && seek_by_ == 0 &&
play_track_at_ == -1 && show_osd_ == false &&
toggle_pretty_osd_ == false && urls_.isEmpty();
}
QByteArray CommandlineOptions::Serialize() const {
@ -276,7 +298,7 @@ QByteArray CommandlineOptions::Serialize() const {
return buf.data();
}
void CommandlineOptions::Load(const QByteArray &serialized) {
void CommandlineOptions::Load(const QByteArray& serialized) {
QByteArray copy(serialized);
QBuffer buf(&copy);
buf.open(QIODevice::ReadOnly);
@ -285,22 +307,14 @@ void CommandlineOptions::Load(const QByteArray &serialized) {
s >> *this;
}
QString CommandlineOptions::tr(const char *source_text) {
QString CommandlineOptions::tr(const char* source_text) {
return QObject::tr(source_text);
}
QDataStream& operator<<(QDataStream& s, const CommandlineOptions& a) {
s << qint32(a.player_action_)
<< qint32(a.url_list_action_)
<< a.set_volume_
<< 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_;
s << qint32(a.player_action_) << qint32(a.url_list_action_) << a.set_volume_
<< 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;
}
@ -308,17 +322,9 @@ QDataStream& operator<<(QDataStream& s, const CommandlineOptions& a) {
QDataStream& operator>>(QDataStream& s, CommandlineOptions& a) {
quint32 player_action = 0;
quint32 url_list_action = 0;
s >> player_action
>> url_list_action
>> a.set_volume_
>> 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_;
s >> player_action >> url_list_action >> a.set_volume_ >>
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.url_list_action_ = CommandlineOptions::UrlListAction(url_list_action);

View File

@ -27,17 +27,14 @@ class CommandlineOptions {
friend QDataStream& operator>>(QDataStream& s, CommandlineOptions& a);
public:
CommandlineOptions(int argc = 0, char** argv = NULL);
CommandlineOptions(int argc = 0, char* *argv = NULL);
static const char* kHelpText;
static const char* kVersionText;
// Don't change the values or order, these get serialised and sent to
// possibly a different version of Clementine
enum UrlListAction {
UrlList_Append = 0,
UrlList_Load = 1,
};
enum UrlListAction { UrlList_Append = 0, UrlList_Load = 1, };
enum PlayerAction {
Player_None = 0,
Player_Play = 1,
@ -90,7 +87,6 @@ class CommandlineOptions {
void RemoveArg(const QString& starts_with, int count);
private:
int argc_;
char** argv_;
@ -114,4 +110,4 @@ class CommandlineOptions {
QDataStream& operator<<(QDataStream& s, const CommandlineOptions& a);
QDataStream& operator>>(QDataStream& s, CommandlineOptions& a);
#endif // COMMANDLINEOPTIONS_H
#endif // COMMANDLINEOPTIONS_H

View File

@ -31,25 +31,23 @@
#include <QtDebug>
#if defined(HAVE_BREAKPAD) and defined(Q_OS_LINUX)
# include "client/linux/handler/exception_handler.h"
# include "third_party/lss/linux_syscall_support.h"
#include "client/linux/handler/exception_handler.h"
#include "third_party/lss/linux_syscall_support.h"
#endif
const char* CrashSender::kUploadURL = "http://crashes.clementine-player.org/getuploadurl";
const char* CrashSender::kUploadURL =
"http://crashes.clementine-player.org/getuploadurl";
const char* CrashReporting::kSendCrashReportOption = "--send-crash-report";
char* CrashReporting::sPath = nullptr;
#if defined(HAVE_BREAKPAD) and defined(Q_OS_LINUX)
CrashReporting::CrashReporting()
: handler_(new google_breakpad::ExceptionHandler(
QDir::tempPath().toLocal8Bit().constData(), nullptr,
CrashReporting::Handler, this, true)) {
}
: handler_(new google_breakpad::ExceptionHandler(
QDir::tempPath().toLocal8Bit().constData(), nullptr,
CrashReporting::Handler, this, true)) {}
CrashReporting::~CrashReporting() {
}
CrashReporting::~CrashReporting() {}
bool CrashReporting::SendCrashReport(int argc, char** argv) {
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,
const char* minidump_id,
void* context,
bool succeeded) {
bool CrashReporting::Handler(const char* dump_path, const char* minidump_id,
void* context, bool succeeded) {
Print("Clementine has crashed! A crash report has been saved to:\n ");
Print(dump_path);
Print("/");
Print(minidump_id);
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");
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");
if (sPath) {
// We know the path to clementine, so exec it again to prompt the user to
// 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);
}
@ -99,11 +97,10 @@ bool CrashReporting::Handler(const char* dump_path,
}
CrashSender::CrashSender(const QString& path)
: network_(new QNetworkAccessManager(this)),
path_(path),
file_(new QFile(path_, this)),
progress_(nullptr) {
}
: network_(new QNetworkAccessManager(this)),
path_(path),
file_(new QFile(path_, this)),
progress_(nullptr) {}
bool CrashSender::Start() {
if (!file_->open(QIODevice::ReadOnly)) {
@ -112,10 +109,13 @@ bool CrashSender::Start() {
}
// No tr() here.
QMessageBox prompt(QMessageBox::Critical, "Clementine has crashed!", QString(
"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_));
QMessageBox prompt(QMessageBox::Critical, "Clementine has crashed!",
QString(
"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_));
prompt.addButton("Don't send", QMessageBox::RejectRole);
prompt.addButton("Send crash report", QMessageBox::AcceptRole);
if (prompt.exec() == QDialog::Rejected) {
@ -141,7 +141,8 @@ void CrashSender::RedirectFinished() {
reply->deleteLater();
QUrl url = reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
QUrl url =
reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
if (!url.isValid()) {
progress_->close();
return;
@ -160,14 +161,17 @@ void CrashSender::RedirectFinished() {
}
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
QByteArray form_data;
form_data.reserve(file_data.size() + 1024);
form_data.append("--");
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(file_data);
form_data.append("\n--");
@ -178,31 +182,25 @@ void CrashSender::RedirectFinished() {
// Upload the 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()));
}
void CrashSender::UploadProgress(qint64 bytes) {
progress_->setValue(bytes);
}
void CrashSender::UploadProgress(qint64 bytes) { progress_->setValue(bytes); }
#else // HAVE_BREAKPAD
#else // HAVE_BREAKPAD
namespace google_breakpad {
class ExceptionHandler {};
class ExceptionHandler {};
}
CrashReporting::CrashReporting() {
}
CrashReporting::CrashReporting() {}
CrashReporting::~CrashReporting() {
}
CrashReporting::~CrashReporting() {}
bool CrashReporting::SendCrashReport(int, char**) {
return false;
}
bool CrashReporting::SendCrashReport(int, char**) { return false; }
void CrashReporting::SetApplicationPath(const QString&) {
}
void CrashReporting::SetApplicationPath(const QString&) {}
#endif // HAVE_BREAKPAD
#endif // HAVE_BREAKPAD

View File

@ -27,14 +27,13 @@ class QNetworkAccessManager;
class QProgressDialog;
namespace google_breakpad {
class ExceptionHandler;
class ExceptionHandler;
}
// Wraps google_breakpad::ExceptionHandler - while an instance of this class
// is alive crashes will be handled.
class CrashReporting {
public:
public:
CrashReporting();
~CrashReporting();
@ -48,17 +47,15 @@ public:
// --send-crash-report when a crash happens.
static void SetApplicationPath(const QString& path);
private:
private:
// Prints the message to stdout without using libc.
static void Print(const char* message);
// Breakpad callback.
static bool Handler(const char* dump_path,
const char* minidump_id,
void* context,
bool succeeded);
static bool Handler(const char* dump_path, const char* minidump_id,
void* context, bool succeeded);
private:
private:
Q_DISABLE_COPY(CrashReporting);
static const char* kSendCrashReportOption;
@ -67,24 +64,23 @@ private:
std::unique_ptr<google_breakpad::ExceptionHandler> handler_;
};
// Asks the user if he wants to send a crash report, and displays a progress
// dialog while uploading it if he does.
class CrashSender : public QObject {
Q_OBJECT
public:
public:
CrashSender(const QString& path);
// Returns false if the user doesn't want to send the crash report (caller
// should exit), or true if he does (caller should start the Qt event loop).
bool Start();
private slots:
private slots:
void RedirectFinished();
void UploadProgress(qint64 bytes);
private:
private:
static const char* kUploadURL;
QNetworkAccessManager* network_;
@ -94,4 +90,4 @@ private:
QProgressDialog* progress_;
};
#endif // CRASHREPORTING_H
#endif // CRASHREPORTING_H

View File

@ -46,65 +46,59 @@ int Database::sNextConnectionId = 1;
QMutex Database::sNextConnectionIdMutex;
Database::Token::Token(const QString& token, int start, int end)
: token(token),
start_offset(start),
end_offset(end) {
}
: token(token), start_offset(start), end_offset(end) {}
struct sqlite3_tokenizer_module {
int iVersion;
int (*xCreate)(
int argc, /* Size of argv array */
const char *const*argv, /* Tokenizer argument strings */
sqlite3_tokenizer **ppTokenizer); /* OUT: Created tokenizer */
int (*xCreate)(int argc, /* Size of argv array */
const char* const* argv, /* Tokenizer argument strings */
sqlite3_tokenizer** ppTokenizer); /* OUT: Created tokenizer */
int (*xDestroy)(sqlite3_tokenizer *pTokenizer);
int (*xDestroy)(sqlite3_tokenizer* pTokenizer);
int (*xOpen)(
sqlite3_tokenizer *pTokenizer, /* Tokenizer object */
const char *pInput, int nBytes, /* Input buffer */
sqlite3_tokenizer_cursor **ppCursor);/* OUT: Created tokenizer cursor */
sqlite3_tokenizer* pTokenizer, /* Tokenizer object */
const char* pInput, int nBytes, /* Input buffer */
sqlite3_tokenizer_cursor** ppCursor); /* OUT: Created tokenizer cursor */
int (*xClose)(sqlite3_tokenizer_cursor *pCursor);
int (*xClose)(sqlite3_tokenizer_cursor* pCursor);
int (*xNext)(
sqlite3_tokenizer_cursor *pCursor, /* Tokenizer cursor */
const char **ppToken, int *pnBytes, /* OUT: Normalized text for token */
int *piStartOffset, /* OUT: Byte offset of token in input buffer */
int *piEndOffset, /* OUT: Byte offset of end of token in input buffer */
int *piPosition); /* OUT: Number of tokens returned before this one */
sqlite3_tokenizer_cursor* pCursor, /* Tokenizer cursor */
const char** ppToken, int* pnBytes, /* OUT: Normalized text for token */
int* piStartOffset, /* OUT: Byte offset of token in input buffer */
int* piEndOffset, /* OUT: Byte offset of end of token in input buffer */
int* piPosition); /* OUT: Number of tokens returned before this one */
};
struct sqlite3_tokenizer {
const sqlite3_tokenizer_module *pModule; /* The module for this tokenizer */
const sqlite3_tokenizer_module* pModule; /* The module for this tokenizer */
/* Tokenizer implementations will typically add additional fields */
};
struct sqlite3_tokenizer_cursor {
sqlite3_tokenizer *pTokenizer; /* Tokenizer for this cursor. */
sqlite3_tokenizer* pTokenizer; /* Tokenizer for this cursor. */
/* Tokenizer implementations will typically add additional fields */
};
sqlite3_tokenizer_module* Database::sFTSTokenizer = nullptr;
int Database::FTSCreate(int argc, const char* const* argv, sqlite3_tokenizer** tokenizer) {
int Database::FTSCreate(int argc, const char* const* argv,
sqlite3_tokenizer** tokenizer) {
*tokenizer = reinterpret_cast<sqlite3_tokenizer*>(new UnicodeTokenizer);
return SQLITE_OK;
}
int Database::FTSDestroy(sqlite3_tokenizer* tokenizer) {
UnicodeTokenizer* real_tokenizer = reinterpret_cast<UnicodeTokenizer*>(tokenizer);
UnicodeTokenizer* real_tokenizer =
reinterpret_cast<UnicodeTokenizer*>(tokenizer);
delete real_tokenizer;
return SQLITE_OK;
}
int Database::FTSOpen(
sqlite3_tokenizer* pTokenizer,
const char* input,
int bytes,
sqlite3_tokenizer_cursor** cursor) {
int Database::FTSOpen(sqlite3_tokenizer* pTokenizer, const char* input,
int bytes, sqlite3_tokenizer_cursor** cursor) {
UnicodeTokenizerCursor* new_cursor = new UnicodeTokenizerCursor;
new_cursor->pTokenizer = pTokenizer;
new_cursor->position = 0;
@ -163,20 +157,18 @@ int Database::FTSOpen(
}
int Database::FTSClose(sqlite3_tokenizer_cursor* cursor) {
UnicodeTokenizerCursor* real_cursor = reinterpret_cast<UnicodeTokenizerCursor*>(cursor);
UnicodeTokenizerCursor* real_cursor =
reinterpret_cast<UnicodeTokenizerCursor*>(cursor);
delete real_cursor;
return SQLITE_OK;
}
int Database::FTSNext(
sqlite3_tokenizer_cursor* cursor,
const char** token,
int* bytes,
int* start_offset,
int* end_offset,
int* position) {
UnicodeTokenizerCursor* real_cursor = reinterpret_cast<UnicodeTokenizerCursor*>(cursor);
int Database::FTSNext(sqlite3_tokenizer_cursor* cursor, const char** token,
int* bytes, int* start_offset, int* end_offset,
int* position) {
UnicodeTokenizerCursor* real_cursor =
reinterpret_cast<UnicodeTokenizerCursor*>(cursor);
QList<Token> tokens = real_cursor->tokens;
if (real_cursor->position >= tokens.size()) {
@ -196,7 +188,6 @@ int Database::FTSNext(
return SQLITE_OK;
}
void Database::StaticInit() {
sFTSTokenizer = new sqlite3_tokenizer_module;
sFTSTokenizer->iVersion = 0;
@ -208,24 +199,24 @@ void Database::StaticInit() {
return;
}
Database::Database(Application* app, QObject* parent, const QString& database_name)
: QObject(parent),
app_(app),
mutex_(QMutex::Recursive),
injected_database_name_(database_name),
query_hash_(0),
startup_schema_version_(-1)
{
Database::Database(Application* app, QObject* parent,
const QString& database_name)
: QObject(parent),
app_(app),
mutex_(QMutex::Recursive),
injected_database_name_(database_name),
query_hash_(0),
startup_schema_version_(-1) {
{
QMutexLocker l(&sNextConnectionIdMutex);
connection_id_ = sNextConnectionId++;
}
directory_ = QDir::toNativeSeparators(
Utilities::GetConfigPath(Utilities::Path_Root));
directory_ =
QDir::toNativeSeparators(Utilities::GetConfigPath(Utilities::Path_Root));
attached_databases_["jamendo"] = AttachedDatabase(
directory_ + "/jamendo.db", ":/schema/jamendo.sql", false);
directory_ + "/jamendo.db", ":/schema/jamendo.sql", false);
QMutexLocker l(&mutex_);
Connect();
@ -241,9 +232,8 @@ QSqlDatabase Database::Connect() {
}
}
const QString connection_id =
QString("%1_thread_%2").arg(connection_id_).arg(
reinterpret_cast<quint64>(QThread::currentThread()));
const QString connection_id = QString("%1_thread_%2").arg(connection_id_).arg(
reinterpret_cast<quint64>(QThread::currentThread()));
// Try to find an existing connection for this thread
QSqlDatabase db = QSqlDatabase::database(connection_id);
@ -269,8 +259,9 @@ QSqlDatabase Database::Connect() {
{
QSqlQuery set_fts_tokenizer("SELECT fts3_tokenizer(:name, :pointer)", db);
set_fts_tokenizer.bindValue(":name", "unicode");
set_fts_tokenizer.bindValue(":pointer", QByteArray(
reinterpret_cast<const char*>(&sFTSTokenizer), sizeof(&sFTSTokenizer)));
set_fts_tokenizer.bindValue(
":pointer", QByteArray(reinterpret_cast<const char*>(&sFTSTokenizer),
sizeof(&sFTSTokenizer)));
if (!set_fts_tokenizer.exec()) {
qLog(Warning) << "Couldn't register FTS3 tokenizer";
}
@ -288,15 +279,15 @@ QSqlDatabase Database::Connect() {
for (const QString& key : attached_databases_.keys()) {
QString filename = attached_databases_[key].filename_;
if (!injected_database_name_.isNull())
filename = injected_database_name_;
if (!injected_database_name_.isNull()) filename = injected_database_name_;
// Attach the db
QSqlQuery q("ATTACH DATABASE :filename AS :alias", db);
q.bindValue(":filename", filename);
q.bindValue(":alias", key);
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())
continue;
// Find out if there are any tables in this database
QSqlQuery q(QString("SELECT ROWID FROM %1.sqlite_master"
" WHERE type='table'").arg(key), db);
QSqlQuery q(QString(
"SELECT ROWID FROM %1.sqlite_master"
" WHERE type='table'").arg(key),
db);
if (!q.exec() || !q.next()) {
q.finish();
ExecSchemaCommandsFromFile(db, attached_databases_[key].schema_, 0);
@ -327,8 +320,7 @@ void Database::UpdateMainSchema(QSqlDatabase* db) {
int schema_version = 0;
{
QSqlQuery q("SELECT version FROM schema_version", *db);
if (q.next())
schema_version = q.value(0).toInt();
if (q.next()) schema_version = q.value(0).toInt();
// Implicit invocation of ~QSqlQuery() when leaving the scope
// to release any remaining database locks!
}
@ -336,12 +328,13 @@ void Database::UpdateMainSchema(QSqlDatabase* db) {
startup_schema_version_ = schema_version;
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;
}
if (schema_version < kSchemaVersion) {
// Update the schema
for (int v = schema_version+1; v <= kSchemaVersion; ++v) {
for (int v = schema_version + 1; v <= kSchemaVersion; ++v) {
UpdateDatabaseSchema(v, *db);
}
}
@ -384,8 +377,8 @@ void Database::AttachDatabase(const QString& database_name,
attached_databases_[database_name] = database;
}
void Database::AttachDatabaseOnDbConnection(const QString &database_name,
const AttachedDatabase &database,
void Database::AttachDatabaseOnDbConnection(const QString& database_name,
const AttachedDatabase& database,
QSqlDatabase& db) {
AttachDatabase(database_name, database);
@ -394,7 +387,8 @@ void Database::AttachDatabaseOnDbConnection(const QString &database_name,
q.bindValue(":filename", database.filename_);
q.bindValue(":alias", database_name);
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());
}
}
@ -414,7 +408,7 @@ void Database::DetachDatabase(const QString& database_name) {
attached_databases_.remove(database_name);
}
void Database::UpdateDatabaseSchema(int version, QSqlDatabase &db) {
void Database::UpdateDatabaseSchema(int version, QSqlDatabase& db) {
QString filename;
if (version == 0)
filename = ":/schema/schema.sql";
@ -434,20 +428,22 @@ void Database::UpdateDatabaseSchema(int version, QSqlDatabase &db) {
UrlEncodeFilenameColumn(table, db);
}
}
qLog(Debug) << "Applying database schema update" << version
<< "from" << filename;
qLog(Debug) << "Applying database schema update" << version << "from"
<< filename;
ExecSchemaCommandsFromFile(db, filename, version - 1, true);
t.Commit();
} else {
qLog(Debug) << "Applying database schema update" << version
<< "from" << filename;
qLog(Debug) << "Applying database schema update" << version << "from"
<< filename;
ExecSchemaCommandsFromFile(db, filename, version - 1);
}
}
void Database::UrlEncodeFilenameColumn(const QString& table, QSqlDatabase& 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();
if (CheckErrors(select)) return;
while (select.next()) {
@ -475,16 +471,12 @@ void Database::ExecSchemaCommandsFromFile(QSqlDatabase& db,
QFile schema_file(filename);
if (!schema_file.open(QIODevice::ReadOnly))
qFatal("Couldn't open schema file %s", filename.toUtf8().constData());
ExecSchemaCommands(db,
QString::fromUtf8(schema_file.readAll()),
schema_version,
in_transaction);
ExecSchemaCommands(db, QString::fromUtf8(schema_file.readAll()),
schema_version, in_transaction);
}
void Database::ExecSchemaCommands(QSqlDatabase& db,
const QString& schema,
int schema_version,
bool in_transaction) {
void Database::ExecSchemaCommands(QSqlDatabase& db, const QString& schema,
int schema_version, bool in_transaction) {
// Run each command
const QStringList commands(schema.split(QRegExp("; *\n\n")));
@ -530,8 +522,7 @@ void Database::ExecSongTablesCommands(QSqlDatabase& db,
}
} else {
QSqlQuery query(db.exec(command));
if (CheckErrors(query))
qFatal("Unable to update music library database");
if (CheckErrors(query)) 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
for (const QString& table : db.tables()) {
if (table == "songs" || table.endsWith("_songs"))
ret << table;
if (table == "songs" || table.endsWith("_songs")) ret << table;
}
// look for the tables in attached dbs
for (const QString& key : attached_databases_.keys()) {
QSqlQuery q(QString("SELECT NAME FROM %1.sqlite_master"
" WHERE type='table' AND name='songs' OR name LIKE '%songs'").arg(key), db);
QSqlQuery q(
QString(
"SELECT NAME FROM %1.sqlite_master"
" WHERE type='table' AND name='songs' OR name LIKE '%songs'")
.arg(key),
db);
if (q.exec()) {
while (q.next()) {
QString tab_name = key + "." + q.value(0).toString();
@ -595,9 +589,11 @@ bool Database::IntegrityCheck(QSqlDatabase db) {
break;
} else {
if (!error_reported) {
app_->AddError(tr("Database corruption detected. Please read "
"https://code.google.com/p/clementine-player/wiki/DatabaseCorruption "
"for instructions on how to recover your database"));
app_->AddError(
tr("Database corruption detected. Please read "
"https://code.google.com/p/clementine-player/wiki/"
"DatabaseCorruption "
"for instructions on how to recover your database"));
}
app_->AddError("Database: " + message);
error_reported = true;
@ -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);
if (ret != 0) {
if (*connection) {
const char* error_message = sqlite3_errmsg(*connection);
qLog(Error) << "Failed to open database for backup:"
<< filename
qLog(Error) << "Failed to open database for backup:" << filename
<< error_message;
} else {
qLog(Error) << "Failed to open database for backup:"
<< filename;
qLog(Error) << "Failed to open database for backup:" << filename;
}
return false;
}
@ -641,7 +636,8 @@ bool Database::OpenDatabase(const QString& filename, sqlite3** connection) const
void Database::BackupFile(const QString& filename) {
qLog(Debug) << "Starting database backup";
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* dest_connection = nullptr;
@ -651,7 +647,8 @@ void Database::BackupFile(const QString& filename) {
sqlite3_close(source_connection);
sqlite3_close(dest_connection);
app_->task_manager()->SetTaskFinished(task_id);
} BOOST_SCOPE_EXIT_END
}
BOOST_SCOPE_EXIT_END
bool success = OpenDatabase(filename, &source_connection);
if (!success) {
@ -663,9 +660,8 @@ void Database::BackupFile(const QString& filename) {
return;
}
sqlite3_backup* backup = sqlite3_backup_init(
dest_connection, "main",
source_connection, "main");
sqlite3_backup* backup =
sqlite3_backup_init(dest_connection, "main", source_connection, "main");
if (!backup) {
const char* error_message = sqlite3_errmsg(dest_connection);
qLog(Error) << "Failed to start database backup:" << error_message;

View File

@ -34,7 +34,6 @@ extern "C" {
struct sqlite3_tokenizer;
struct sqlite3_tokenizer_cursor;
struct sqlite3_tokenizer_module;
}
class Application;
@ -48,8 +47,9 @@ class Database : public QObject {
struct AttachedDatabase {
AttachedDatabase() {}
AttachedDatabase(const QString& filename, const QString& schema, bool is_temporary)
: filename_(filename), schema_(schema), is_temporary_(is_temporary) {}
AttachedDatabase(const QString& filename, const QString& schema,
bool is_temporary)
: filename_(filename), schema_(schema), is_temporary_(is_temporary) {}
QString filename_;
QString schema_;
@ -65,21 +65,20 @@ class Database : public QObject {
QMutex* Mutex() { return &mutex_; }
void RecreateAttachedDb(const QString& database_name);
void ExecSchemaCommands(QSqlDatabase& db,
const QString& schema,
int schema_version,
bool in_transaction = false);
void ExecSchemaCommands(QSqlDatabase& db, const QString& schema,
int schema_version, bool in_transaction = false);
int startup_schema_version() const { return startup_schema_version_; }
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,
const AttachedDatabase& database,
QSqlDatabase& db);
void DetachDatabase(const QString& database_name);
signals:
signals:
void Error(const QString& message);
public slots:
@ -88,12 +87,10 @@ class Database : public QObject {
private:
void UpdateMainSchema(QSqlDatabase* db);
void ExecSchemaCommandsFromFile(QSqlDatabase& db,
const QString& filename,
void ExecSchemaCommandsFromFile(QSqlDatabase& db, const QString& filename,
int schema_version,
bool in_transaction = false);
void ExecSongTablesCommands(QSqlDatabase& db,
const QStringList& song_tables,
void ExecSongTablesCommands(QSqlDatabase& db, const QStringList& song_tables,
const QStringList& commands);
void UpdateDatabaseSchema(int version, QSqlDatabase& db);
@ -137,26 +134,23 @@ class Database : public QObject {
// Do static initialisation like loading sqlite functions.
static void StaticInit();
typedef int (*Sqlite3CreateFunc) (
sqlite3*, const char*, int, int, void*,
void (*) (sqlite3_context*, int, sqlite3_value**),
void (*) (sqlite3_context*, int, sqlite3_value**),
void (*) (sqlite3_context*));
typedef int (*Sqlite3CreateFunc)(sqlite3*, const char*, int, int, void*,
void (*)(sqlite3_context*, int,
sqlite3_value**),
void (*)(sqlite3_context*, int,
sqlite3_value**),
void (*)(sqlite3_context*));
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 FTSOpen(sqlite3_tokenizer* tokenizer,
const char* input,
int bytes,
static int FTSOpen(sqlite3_tokenizer* tokenizer, const char* input, int bytes,
sqlite3_tokenizer_cursor** cursor);
static int FTSClose(sqlite3_tokenizer_cursor* cursor);
static int FTSNext(sqlite3_tokenizer_cursor* cursor,
const char** token,
int* bytes,
int* start_offset,
int* end_offset,
static int FTSNext(sqlite3_tokenizer_cursor* cursor, const char** token,
int* bytes, int* start_offset, int* end_offset,
int* position);
struct Token {
Token(const QString& token, int start, int end);
@ -182,11 +176,11 @@ class Database : public QObject {
class MemoryDatabase : public Database {
public:
MemoryDatabase(Application* app, QObject* parent = 0)
: Database(app, parent, ":memory:") {}
: Database(app, parent, ":memory:") {}
~MemoryDatabase() {
// Make sure Qt doesn't reuse the same database
QSqlDatabase::removeDatabase(Connect().connectionName());
}
};
#endif // DATABASE_H
#endif // DATABASE_H

View File

@ -29,22 +29,19 @@ const int DeleteFiles::kBatchSize = 50;
DeleteFiles::DeleteFiles(TaskManager* task_manager,
std::shared_ptr<MusicStorage> storage)
: thread_(nullptr),
task_manager_(task_manager),
storage_(storage),
started_(false),
task_id_(0),
progress_(0)
{
: thread_(nullptr),
task_manager_(task_manager),
storage_(storage),
started_(false),
task_id_(0),
progress_(0) {
original_thread_ = thread();
}
DeleteFiles::~DeleteFiles() {
}
DeleteFiles::~DeleteFiles() {}
void DeleteFiles::Start(const SongList& songs) {
if (thread_)
return;
if (thread_) return;
songs_ = songs;
@ -60,7 +57,7 @@ void DeleteFiles::Start(const SongList& songs) {
void DeleteFiles::Start(const QStringList& filenames) {
SongList songs;
foreach (const QString& filename, filenames) {
foreach(const QString & filename, filenames) {
Song song;
song.set_url(QUrl::fromLocalFile(filename));
songs << song;
@ -98,7 +95,7 @@ void DeleteFiles::ProcessSomeFiles() {
// We process files in batches so we can be cancelled part-way through.
const int n = qMin(songs_.count(), progress_ + kBatchSize);
for ( ; progress_<n ; ++progress_) {
for (; progress_ < n; ++progress_) {
task_manager_->SetTaskProgress(task_id_, progress_, songs_.count());
const Song& song = songs_[progress_];

View File

@ -30,7 +30,7 @@ class TaskManager;
class DeleteFiles : public QObject {
Q_OBJECT
public:
public:
DeleteFiles(TaskManager* task_manager, std::shared_ptr<MusicStorage> storage);
~DeleteFiles();
@ -42,10 +42,10 @@ public:
signals:
void Finished(const SongList& songs_with_errors);
private slots:
private slots:
void ProcessSomeFiles();
private:
private:
QThread* thread_;
QThread* original_thread_;
TaskManager* task_manager_;
@ -61,4 +61,4 @@ private:
SongList songs_with_errors_;
};
#endif // DELETEFILES_H
#endif // DELETEFILES_H

View File

@ -22,221 +22,182 @@
#include <string.h>
#include "fht.h"
FHT::FHT(int n) : m_buf(0), m_tab(0), m_log(0) {
if (n < 3) {
m_num = 0;
m_exp2 = -1;
return;
}
m_exp2 = n;
m_num = 1 << n;
if (n > 3) {
m_buf = new float[m_num];
m_tab = new float[m_num * 2];
makeCasTable();
}
}
FHT::FHT(int n) :
m_buf(0),
m_tab(0),
m_log(0)
{
if (n < 3) {
m_num = 0;
m_exp2 = -1;
return;
FHT::~FHT() {
delete[] m_buf;
delete[] m_tab;
delete[] m_log;
}
void FHT::makeCasTable(void) {
float d, *costab, *sintab;
int ul, ndiv2 = m_num / 2;
for (costab = m_tab, sintab = m_tab + m_num / 2 + 1, ul = 0; ul < m_num;
ul++) {
d = M_PI * ul / ndiv2;
*costab = *sintab = cos(d);
costab += 2, sintab += 2;
if (sintab > m_tab + m_num * 2) sintab = m_tab + 1;
}
}
float* FHT::copy(float* d, float* s) {
return (float*)memcpy(d, s, m_num * sizeof(float));
}
float* FHT::clear(float* d) {
return (float*)memset(d, 0, m_num * sizeof(float));
}
void FHT::scale(float* p, float d) {
for (int i = 0; i < (m_num / 2); i++) *p++ *= d;
}
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);
}
void FHT::logSpectrum(float* out, float* p) {
int n = m_num / 2, i, j, k, *r;
if (!m_log) {
m_log = new int[n];
float f = n / log10((double)n);
for (i = 0, r = m_log; i < n; i++, r++) {
j = int(rint(log10(i + 1.0) * f));
*r = j >= n ? n - 1 : j;
}
m_exp2 = n;
m_num = 1 << n;
if (n > 3) {
m_buf = new float[m_num];
m_tab = new float[m_num * 2];
makeCasTable();
}
semiLogSpectrum(p);
*out++ = *p = *p / 100;
for (k = i = 1, r = m_log; i < n; i++) {
j = *r++;
if (i == j)
*out++ = p[i];
else {
float base = p[k - 1];
float step = (p[j] - base) / (j - (k - 1));
for (float corr = 0; k <= j; k++, corr += step) *out++ = base + corr;
}
}
}
FHT::~FHT()
{
delete[] m_buf;
delete[] m_tab;
delete[] m_log;
void FHT::semiLogSpectrum(float* p) {
float e;
power2(p);
for (int i = 0; i < (m_num / 2); i++, p++) {
e = 10.0 * log10(sqrt(*p * .5));
*p = e < 0 ? 0 : e;
}
}
void FHT::makeCasTable(void)
{
float d, *costab, *sintab;
int ul, ndiv2 = m_num / 2;
for (costab = m_tab, sintab = m_tab + m_num / 2 + 1, ul = 0; ul < m_num; ul++) {
d = M_PI * ul / ndiv2;
*costab = *sintab = cos(d);
costab += 2, sintab += 2;
if (sintab > m_tab + m_num * 2)
sintab = m_tab + 1;
}
void FHT::spectrum(float* p) {
power2(p);
for (int i = 0; i < (m_num / 2); i++, p++) *p = (float)sqrt(*p * .5);
}
float* FHT::copy(float *d, float *s)
{
return (float *)memcpy(d, s, m_num * sizeof(float));
void FHT::power(float* p) {
power2(p);
for (int i = 0; i < (m_num / 2); i++) *p++ *= .5;
}
void FHT::power2(float* p) {
int i;
float* q;
_transform(p, m_num, 0);
float* FHT::clear(float *d)
{
return (float *)memset(d, 0, m_num * sizeof(float));
*p = (*p * *p), *p += *p, p++;
for (i = 1, q = p + m_num - 2; i < (m_num / 2); i++, --q)
*p = (*p * *p) + (*q * *q), p++;
}
void FHT::scale(float *p, float d)
{
for (int i = 0; i < (m_num / 2); i++)
*p++ *= d;
}
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);
}
void FHT::logSpectrum(float *out, float *p)
{
int n = m_num / 2, i, j, k, *r;
if (!m_log) {
m_log = new int[n];
float f = n / log10((double)n);
for (i = 0, r = m_log; i < n; i++, r++) {
j = int(rint(log10(i + 1.0) * f));
*r = j >= n ? n - 1 : j;
}
}
semiLogSpectrum(p);
*out++ = *p = *p / 100;
for (k = i = 1, r = m_log; i < n; i++) {
j = *r++;
if (i == j)
*out++ = p[i];
else {
float base = p[k - 1];
float step = (p[j] - base) / (j - (k - 1));
for (float corr = 0; k <= j; k++, corr += step)
*out++ = base + corr;
}
}
}
void FHT::semiLogSpectrum(float *p)
{
float e;
power2(p);
for (int i = 0; i < (m_num / 2); i++, p++) {
e = 10.0 * log10(sqrt(*p * .5));
*p = e < 0 ? 0 : e;
}
}
void FHT::spectrum(float *p)
{
power2(p);
for (int i = 0; i < (m_num / 2); i++, p++)
*p = (float)sqrt(*p * .5);
}
void FHT::power(float *p)
{
power2(p);
for (int i = 0; i < (m_num / 2); i++)
*p++ *= .5;
}
void FHT::power2(float *p)
{
int i;
float *q;
void FHT::transform(float* p) {
if (m_num == 8)
transform8(p);
else
_transform(p, m_num, 0);
*p = (*p * *p), *p += *p, p++;
for (i = 1, q = p + m_num - 2; i < (m_num / 2); i++, --q)
*p = (*p * *p) + (*q * *q), p++;
}
void FHT::transform8(float* p) {
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;
void FHT::transform(float *p)
{
if (m_num == 8)
transform8(p);
else
_transform(p, m_num, 0);
a = *p++, b = *p++, c = *p++, d = *p++;
e = *p++, f = *p++, g = *p++, h = *p;
b_f2 = (b - f) * M_SQRT2;
d_h2 = (d - h) * M_SQRT2;
a_c_eg = a - c - e + g;
a_ce_g = a - c + e - g;
ac_e_g = a + c - e - g;
aceg = a + c + e + g;
b_df_h = b - d + f - h;
bdfh = b + d + f + h;
*p = a_c_eg - d_h2;
*--p = a_ce_g - b_df_h;
*--p = ac_e_g - b_f2;
*--p = aceg - bdfh;
*--p = a_c_eg + d_h2;
*--p = a_ce_g + b_df_h;
*--p = ac_e_g + b_f2;
*--p = aceg + bdfh;
}
void FHT::_transform(float* p, int n, int k) {
if (n == 8) {
transform8(p + k);
return;
}
void FHT::transform8(float *p)
{
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;
int i, j, ndiv2 = n / 2;
float a, *t1, *t2, *t3, *t4, *ptab, *pp;
a = *p++, b = *p++, c = *p++, d = *p++;
e = *p++, f = *p++, g = *p++, h = *p;
b_f2 = (b - f) * M_SQRT2;
d_h2 = (d - h) * M_SQRT2;
for (i = 0, t1 = m_buf, t2 = m_buf + ndiv2, pp = &p[k]; i < ndiv2; i++)
*t1++ = *pp++, *t2++ = *pp++;
a_c_eg = a - c - e + g;
a_ce_g = a - c + e - g;
ac_e_g = a + c - e - g;
aceg = a + c + e + g;
memcpy(p + k, m_buf, sizeof(float) * n);
b_df_h = b - d + f - h;
bdfh = b + d + f + h;
_transform(p, ndiv2, k);
_transform(p, ndiv2, k + ndiv2);
*p = a_c_eg - d_h2;
*--p = a_ce_g - b_df_h;
*--p = ac_e_g - b_f2;
*--p = aceg - bdfh;
*--p = a_c_eg + d_h2;
*--p = a_ce_g + b_df_h;
*--p = ac_e_g + b_f2;
*--p = aceg + bdfh;
}
j = m_num / ndiv2 - 1;
t1 = m_buf;
t2 = t1 + ndiv2;
t3 = p + k + ndiv2;
ptab = m_tab;
pp = p + k;
a = *ptab++ * *t3++;
a += *ptab * *pp;
ptab += j;
void FHT::_transform(float *p, int n, int k)
{
if (n == 8) {
transform8(p + k);
return;
}
int i, j, ndiv2 = n / 2;
float a, *t1, *t2, *t3, *t4, *ptab, *pp;
for (i = 0, t1 = m_buf, t2 = m_buf + ndiv2, pp = &p[k]; i < ndiv2; i++)
*t1++ = *pp++, *t2++ = *pp++;
memcpy(p + k, m_buf, sizeof(float) * n);
_transform(p, ndiv2, k);
_transform(p, ndiv2, k + ndiv2);
j = m_num / ndiv2 - 1;
t1 = m_buf;
t2 = t1 + ndiv2;
t3 = p + k + ndiv2;
ptab = m_tab;
pp = p + k;
*t1++ = *pp + a;
*t2++ = *pp++ - a;
for (i = 1, t4 = p + k + n; i < ndiv2; i++, ptab += j) {
a = *ptab++ * *t3++;
a += *ptab * *pp;
ptab += j;
a += *ptab * *--t4;
*t1++ = *pp + a;
*t2++ = *pp++ - a;
for (i = 1, t4 = p + k + n; i < ndiv2; i++, ptab += j) {
a = *ptab++ * *t3++;
a += *ptab * *--t4;
*t1++ = *pp + a;
*t2++ = *pp++ - a;
}
memcpy(p + k, m_buf, sizeof(float) * n);
}
memcpy(p + k, m_buf, sizeof(float) * n);
}

View File

@ -29,91 +29,90 @@
*
* [1] Computer in Physics, Vol. 9, No. 4, Jul/Aug 1995 pp 373-379
*/
class FHT
{
int m_exp2;
int m_num;
float *m_buf;
float *m_tab;
int *m_log;
class FHT {
int m_exp2;
int m_num;
float* m_buf;
float* m_tab;
int* m_log;
/**
* Create a table of "cas" (cosine and sine) values.
* Has only to be done in the constructor and saves from
* calculating the same values over and over while transforming.
*/
void makeCasTable();
/**
* Create a table of "cas" (cosine and sine) values.
* Has only to be done in the constructor and saves from
* calculating the same values over and over while transforming.
*/
void makeCasTable();
/**
* Recursive in-place Hartley transform. For internal use only!
*/
void _transform(float *, int, int);
/**
* Recursive in-place Hartley transform. For internal use only!
*/
void _transform(float*, int, int);
public:
/**
* Prepare transform for data sets with @f$2^n@f$ numbers, whereby @f$n@f$
* should be at least 3. Values of more than 3 need a trigonometry table.
* @see makeCasTable()
*/
FHT(int);
public:
/**
* Prepare transform for data sets with @f$2^n@f$ numbers, whereby @f$n@f$
* should be at least 3. Values of more than 3 need a trigonometry table.
* @see makeCasTable()
*/
FHT(int);
~FHT();
inline int sizeExp() const { return m_exp2; }
inline int size() const { return m_num; }
float *copy(float *, float *);
float *clear(float *);
void scale(float *, float);
~FHT();
inline int sizeExp() const { return m_exp2; }
inline int size() const { return m_num; }
float* copy(float*, float*);
float* clear(float*);
void scale(float*, float);
/**
* Exponentially Weighted Moving Average (EWMA) filter.
* @param d is the filtered data.
* @param s is fresh input.
* @param w is the weighting factor.
*/
void ewma(float *d, float *s, float w);
/**
* Exponentially Weighted Moving Average (EWMA) filter.
* @param d is the filtered data.
* @param s is fresh input.
* @param w is the weighting factor.
*/
void ewma(float* d, float* s, float w);
/**
* Logarithmic audio spectrum. Maps semi-logarithmic spectrum
* to logarithmic frequency scale, interpolates missing values.
* A logarithmic index map is calculated at the first run only.
* @param p is the input array.
* @param out is the spectrum.
*/
void logSpectrum(float *out, float *p);
/**
* Logarithmic audio spectrum. Maps semi-logarithmic spectrum
* to logarithmic frequency scale, interpolates missing values.
* A logarithmic index map is calculated at the first run only.
* @param p is the input array.
* @param out is the spectrum.
*/
void logSpectrum(float* out, float* p);
/**
* Semi-logarithmic audio spectrum.
*/
void semiLogSpectrum(float *);
/**
* Semi-logarithmic audio spectrum.
*/
void semiLogSpectrum(float*);
/**
* Fourier spectrum.
*/
void spectrum(float *);
/**
* Fourier spectrum.
*/
void spectrum(float*);
/**
* Calculates a mathematically correct FFT power spectrum.
* If further scaling is applied later, use power2 instead
* and factor the 0.5 in the final scaling factor.
* @see FHT::power2()
*/
void power(float *);
/**
* Calculates a mathematically correct FFT power spectrum.
* If further scaling is applied later, use power2 instead
* and factor the 0.5 in the final scaling factor.
* @see FHT::power2()
*/
void power(float*);
/**
* Calculates an FFT power spectrum with doubled values as a
* result. The values need to be multiplied by 0.5 to be exact.
* Note that you only get @f$2^{n-1}@f$ power values for a data set
* of @f$2^n@f$ input values. This is the fastest transform.
* @see FHT::power()
*/
void power2(float *);
/**
* Calculates an FFT power spectrum with doubled values as a
* result. The values need to be multiplied by 0.5 to be exact.
* Note that you only get @f$2^{n-1}@f$ power values for a data set
* of @f$2^n@f$ input values. This is the fastest transform.
* @see FHT::power()
*/
void power2(float*);
/**
* Discrete Hartley transform of data sets with 8 values.
*/
void transform8(float *);
/**
* Discrete Hartley transform of data sets with 8 values.
*/
void transform8(float*);
void transform(float *);
void transform(float*);
};
#endif

View File

@ -23,17 +23,14 @@
#include <QUrl>
FilesystemMusicStorage::FilesystemMusicStorage(const QString& root)
: root_(root)
{
}
: root_(root) {}
bool FilesystemMusicStorage::CopyToStorage(const CopyJob& job) {
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
if (src == dest)
return true;
if (src == dest) return true;
// Create directories as required
QDir dir;
@ -43,8 +40,7 @@ bool FilesystemMusicStorage::CopyToStorage(const CopyJob& job) {
}
// Remove the destination file if it exists and we want to overwrite
if (job.overwrite_ && dest.exists())
QFile::remove(dest.absoluteFilePath());
if (job.overwrite_ && dest.exists()) QFile::remove(dest.absoluteFilePath());
// Copy or move
if (job.remove_original_)

View File

@ -21,7 +21,7 @@
#include "musicstorage.h"
class FilesystemMusicStorage : public virtual MusicStorage {
public:
public:
FilesystemMusicStorage(const QString& root);
~FilesystemMusicStorage() {}
@ -30,8 +30,8 @@ public:
bool CopyToStorage(const CopyJob& job);
bool DeleteFromStorage(const DeleteJob& job);
private:
private:
QString root_;
};
#endif // FILESYSTEMMUSICSTORAGE_H
#endif // FILESYSTEMMUSICSTORAGE_H

View File

@ -24,10 +24,10 @@
#endif
FileSystemWatcherInterface::FileSystemWatcherInterface(QObject* parent)
: QObject(parent) {
}
: QObject(parent) {}
FileSystemWatcherInterface* FileSystemWatcherInterface::Create(QObject* parent) {
FileSystemWatcherInterface* FileSystemWatcherInterface::Create(
QObject* parent) {
FileSystemWatcherInterface* ret;
#ifdef Q_OS_DARWIN
ret = new MacFSListener(parent);

View File

@ -31,7 +31,7 @@ class FileSystemWatcherInterface : public QObject {
static FileSystemWatcherInterface* Create(QObject* parent = 0);
signals:
signals:
void PathChanged(const QString& path);
};

View File

@ -20,16 +20,11 @@
#include "globalshortcuts.h"
GlobalShortcutBackend::GlobalShortcutBackend(GlobalShortcuts* parent)
: QObject(parent),
manager_(parent),
active_(false)
{
}
: QObject(parent), manager_(parent), active_(false) {}
bool GlobalShortcutBackend::Register() {
bool ret = DoRegister();
if (ret)
active_ = true;
if (ret) active_ = true;
return ret;
}

View File

@ -25,7 +25,7 @@ class GlobalShortcuts;
class GlobalShortcutBackend : public QObject {
Q_OBJECT
public:
public:
GlobalShortcutBackend(GlobalShortcuts* parent = 0);
virtual ~GlobalShortcutBackend() {}
@ -37,7 +37,7 @@ public:
signals:
void RegisterFinished(bool success);
protected:
protected:
virtual bool DoRegister() = 0;
virtual void DoUnregister() = 0;
@ -45,4 +45,4 @@ protected:
bool active_;
};
#endif // GLOBALSHORTCUTBACKEND_H
#endif // GLOBALSHORTCUTBACKEND_H

View File

@ -28,28 +28,32 @@
#include <QtDebug>
#ifdef QT_DBUS_LIB
# include <QtDBus>
#include <QtDBus>
#endif
const char* GlobalShortcuts::kSettingsGroup = "Shortcuts";
GlobalShortcuts::GlobalShortcuts(QWidget *parent)
: QWidget(parent),
gnome_backend_(nullptr),
system_backend_(nullptr),
use_gnome_(false),
rating_signals_mapper_(new QSignalMapper(this))
{
GlobalShortcuts::GlobalShortcuts(QWidget* parent)
: QWidget(parent),
gnome_backend_(nullptr),
system_backend_(nullptr),
use_gnome_(false),
rating_signals_mapper_(new QSignalMapper(this)) {
settings_.beginGroup(kSettingsGroup);
// Create actions
AddShortcut("play", tr("Play"), SIGNAL(Play()));
AddShortcut("pause", tr("Pause"), SIGNAL(Pause()));
AddShortcut("play_pause", tr("Play/Pause"), SIGNAL(PlayPause()), QKeySequence(Qt::Key_MediaPlay));
AddShortcut("stop", tr("Stop"), SIGNAL(Stop()), QKeySequence(Qt::Key_MediaStop));
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("play_pause", tr("Play/Pause"), SIGNAL(PlayPause()),
QKeySequence(Qt::Key_MediaPlay));
AddShortcut("stop", tr("Stop"), SIGNAL(Stop()),
QKeySequence(Qt::Key_MediaStop));
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("dec_volume", tr("Decrease volume"), SIGNAL(DecVolume()));
AddShortcut("mute", tr("Mute"), SIGNAL(Mute()));
@ -57,19 +61,32 @@ GlobalShortcuts::GlobalShortcuts(QWidget *parent)
AddShortcut("seek_backward", tr("Seek backward"), SIGNAL(SeekBackward()));
AddShortcut("show_hide", tr("Show/Hide"), SIGNAL(ShowHide()));
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("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()));
AddShortcut(
"toggle_pretty_osd", tr("Toggle Pretty OSD"),
SIGNAL(TogglePrettyOSD())); // Toggling possible only for pretty OSD
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_one_star", tr("Rate the current song 1 star"), rating_signals_mapper_, 1);
AddRatingShortcut("rate_two_star", tr("Rate the current song 2 stars"), 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);
AddRatingShortcut("rate_zero_star", tr("Rate the current song 0 stars"),
rating_signals_mapper_, 0);
AddRatingShortcut("rate_one_star", tr("Rate the current song 1 star"),
rating_signals_mapper_, 1);
AddRatingShortcut("rate_two_star", tr("Rate the current song 2 stars"),
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
gnome_backend_ = new GnomeGlobalShortcutBackend(this);
@ -98,8 +115,8 @@ void GlobalShortcuts::AddRatingShortcut(const QString& id, const QString& name,
mapper->setMapping(shortcut.action, rating);
}
GlobalShortcuts::Shortcut GlobalShortcuts::AddShortcut(const QString& id, const QString& name,
const QKeySequence& default_key) {
GlobalShortcuts::Shortcut GlobalShortcuts::AddShortcut(
const QString& id, const QString& name, const QKeySequence& default_key) {
Shortcut shortcut;
shortcut.action = new QAction(name, this);
QKeySequence key_sequence = QKeySequence::fromString(
@ -122,7 +139,7 @@ bool GlobalShortcuts::IsGsdAvailable() const {
#ifdef QT_DBUS_LIB
return QDBusConnection::sessionBus().interface()->isServiceRegistered(
GnomeGlobalShortcutBackend::kGsdService);
#else // QT_DBUS_LIB
#else // QT_DBUS_LIB
return false;
#endif
}
@ -137,21 +154,19 @@ void GlobalShortcuts::ReloadSettings() {
}
void GlobalShortcuts::Unregister() {
if (gnome_backend_->is_active())
gnome_backend_->Unregister();
if (system_backend_->is_active())
system_backend_->Unregister();
if (gnome_backend_->is_active()) gnome_backend_->Unregister();
if (system_backend_->is_active()) system_backend_->Unregister();
}
void GlobalShortcuts::Register() {
if (use_gnome_ && gnome_backend_->Register())
return;
if (use_gnome_ && gnome_backend_->Register()) return;
system_backend_->Register();
}
bool GlobalShortcuts::IsMacAccessibilityEnabled() const {
#ifdef Q_OS_MAC
return static_cast<MacGlobalShortcutBackend*>(system_backend_)->IsAccessibilityEnabled();
return static_cast<MacGlobalShortcutBackend*>(system_backend_)
->IsAccessibilityEnabled();
#else
return true;
#endif
@ -159,6 +174,7 @@ bool GlobalShortcuts::IsMacAccessibilityEnabled() const {
void GlobalShortcuts::ShowMacAccessibilityDialog() {
#ifdef Q_OS_MAC
static_cast<MacGlobalShortcutBackend*>(system_backend_)->ShowAccessibilityDialog();
static_cast<MacGlobalShortcutBackend*>(system_backend_)
->ShowAccessibilityDialog();
#endif
}

View File

@ -32,7 +32,7 @@ class QSignalMapper;
class GlobalShortcuts : public QWidget {
Q_OBJECT
public:
public:
GlobalShortcuts(QWidget* parent = 0);
static const char* kSettingsGroup;
@ -48,7 +48,7 @@ public:
bool IsGsdAvailable() const;
bool IsMacAccessibilityEnabled() const;
public slots:
public slots:
void ReloadSettings();
void ShowMacAccessibilityDialog();
@ -76,14 +76,16 @@ signals:
void CycleRepeatMode();
void ToggleScrobbling();
private:
private:
void AddShortcut(const QString& id, const QString& name, const char* signal,
const QKeySequence& default_key = QKeySequence(0));
void AddRatingShortcut(const QString& id, const QString& name, QSignalMapper* mapper,
int rating, const QKeySequence& default_key = QKeySequence(0));
Shortcut AddShortcut(const QString& id, const QString& name, const QKeySequence& default_key);
void AddRatingShortcut(const QString& id, const QString& name,
QSignalMapper* mapper, int rating,
const QKeySequence& default_key = QKeySequence(0));
Shortcut AddShortcut(const QString& id, const QString& name,
const QKeySequence& default_key);
private:
private:
GlobalShortcutBackend* gnome_backend_;
GlobalShortcutBackend* system_backend_;

View File

@ -21,7 +21,7 @@
#include "core/logging.h"
#ifdef QT_DBUS_LIB
# include "dbus/gnomesettingsdaemon.h"
#include "dbus/gnomesettingsdaemon.h"
#endif
#include <QAction>
@ -30,26 +30,27 @@
#include <QtDebug>
#ifdef QT_DBUS_LIB
# include <QtDBus>
#include <QtDBus>
#endif
const char* GnomeGlobalShortcutBackend::kGsdService = "org.gnome.SettingsDaemon";
const char* GnomeGlobalShortcutBackend::kGsdPath = "/org/gnome/SettingsDaemon/MediaKeys";
const char* GnomeGlobalShortcutBackend::kGsdInterface = "org.gnome.SettingsDaemon.MediaKeys";
const char* GnomeGlobalShortcutBackend::kGsdService =
"org.gnome.SettingsDaemon";
const char* GnomeGlobalShortcutBackend::kGsdPath =
"/org/gnome/SettingsDaemon/MediaKeys";
const char* GnomeGlobalShortcutBackend::kGsdInterface =
"org.gnome.SettingsDaemon.MediaKeys";
GnomeGlobalShortcutBackend::GnomeGlobalShortcutBackend(GlobalShortcuts* parent)
: GlobalShortcutBackend(parent),
interface_(nullptr),
is_connected_(false)
{
}
: GlobalShortcutBackend(parent),
interface_(nullptr),
is_connected_(false) {}
bool GnomeGlobalShortcutBackend::DoRegister() {
#ifdef QT_DBUS_LIB
qLog(Debug) << "registering";
// 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";
return false;
}
@ -64,56 +65,57 @@ bool GnomeGlobalShortcutBackend::DoRegister() {
QDateTime::currentDateTime().toTime_t());
QDBusPendingCallWatcher* watcher = new QDBusPendingCallWatcher(reply, this);
NewClosure(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)),
this, SLOT(RegisterFinished(QDBusPendingCallWatcher*)),
watcher);
NewClosure(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this,
SLOT(RegisterFinished(QDBusPendingCallWatcher*)), watcher);
return true;
#else // QT_DBUS_LIB
#else // QT_DBUS_LIB
qLog(Warning) << "dbus not available";
return false;
#endif
}
void GnomeGlobalShortcutBackend::RegisterFinished(QDBusPendingCallWatcher* watcher) {
void GnomeGlobalShortcutBackend::RegisterFinished(
QDBusPendingCallWatcher* watcher) {
#ifdef QT_DBUS_LIB
QDBusMessage reply = watcher->reply();
watcher->deleteLater();
if (reply.type() == QDBusMessage::ErrorMessage) {
qLog(Warning) << "Failed to grab media keys"
<< reply.errorName() <<reply.errorMessage();
qLog(Warning) << "Failed to grab media keys" << reply.errorName()
<< reply.errorMessage();
return;
}
connect(interface_, SIGNAL(MediaPlayerKeyPressed(QString,QString)),
this, SLOT(GnomeMediaKeyPressed(QString,QString)));
connect(interface_, SIGNAL(MediaPlayerKeyPressed(QString, QString)), this,
SLOT(GnomeMediaKeyPressed(QString, QString)));
is_connected_ = true;
qLog(Debug) << "registered";
#endif // QT_DBUS_LIB
#endif // QT_DBUS_LIB
}
void GnomeGlobalShortcutBackend::DoUnregister() {
qLog(Debug) << "unregister";
#ifdef QT_DBUS_LIB
// Check if the GSD service is available
if (!QDBusConnection::sessionBus().interface()->isServiceRegistered(kGsdService))
return;
if (!interface_ || !is_connected_)
if (!QDBusConnection::sessionBus().interface()->isServiceRegistered(
kGsdService))
return;
if (!interface_ || !is_connected_) return;
is_connected_ = false;
interface_->ReleaseMediaPlayerKeys(QCoreApplication::applicationName());
disconnect(interface_, SIGNAL(MediaPlayerKeyPressed(QString,QString)),
this, SLOT(GnomeMediaKeyPressed(QString,QString)));
disconnect(interface_, SIGNAL(MediaPlayerKeyPressed(QString, QString)), this,
SLOT(GnomeMediaKeyPressed(QString, QString)));
#endif
}
void GnomeGlobalShortcutBackend::GnomeMediaKeyPressed(const QString&, const QString& key) {
if (key == "Play") manager_->shortcuts()["play_pause"].action->trigger();
if (key == "Stop") manager_->shortcuts()["stop"].action->trigger();
if (key == "Next") manager_->shortcuts()["next_track"].action->trigger();
void GnomeGlobalShortcutBackend::GnomeMediaKeyPressed(const QString&,
const QString& key) {
if (key == "Play") manager_->shortcuts()["play_pause"].action->trigger();
if (key == "Stop") manager_->shortcuts()["stop"].action->trigger();
if (key == "Next") manager_->shortcuts()["next_track"].action->trigger();
if (key == "Previous") manager_->shortcuts()["prev_track"].action->trigger();
}

View File

@ -27,26 +27,26 @@ class QDBusPendingCallWatcher;
class GnomeGlobalShortcutBackend : public GlobalShortcutBackend {
Q_OBJECT
public:
public:
GnomeGlobalShortcutBackend(GlobalShortcuts* parent);
static const char* kGsdService;
static const char* kGsdPath;
static const char* kGsdInterface;
protected:
protected:
bool RegisterInNewThread() const { return true; }
bool DoRegister();
void DoUnregister();
private slots:
private slots:
void RegisterFinished(QDBusPendingCallWatcher* watcher);
void GnomeMediaKeyPressed(const QString& application, const QString& key);
private:
private:
OrgGnomeSettingsDaemonMediaKeysInterface* interface_;
bool is_connected_;
};
#endif // GNOMEGLOBALSHORTCUTBACKEND_H
#endif // GNOMEGLOBALSHORTCUTBACKEND_H

View File

@ -7,11 +7,10 @@
#import <Breakpad/Breakpad.h>
#endif
class PlatformInterface;
@class SPMediaKeyTap;
@interface AppDelegate :NSObject <NSApplicationDelegate> {
@interface AppDelegate : NSObject<NSApplicationDelegate> {
PlatformInterface* application_handler_;
NSMenu* dock_menu_;
MacGlobalShortcutBackend* shortcut_handler_;
@ -22,21 +21,23 @@ class PlatformInterface;
#endif
}
- (id) initWithHandler: (PlatformInterface*)handler;
- (id)initWithHandler:(PlatformInterface*)handler;
// NSApplicationDelegate
- (BOOL) applicationShouldHandleReopen: (NSApplication*)app hasVisibleWindows:(BOOL)flag;
- (NSMenu*) applicationDockMenu: (NSApplication*)sender;
- (BOOL)applicationShouldHandleReopen:(NSApplication*)app
hasVisibleWindows:(BOOL)flag;
- (NSMenu*)applicationDockMenu:(NSApplication*)sender;
- (void)applicationDidFinishLaunching:(NSNotification*)aNotification;
- (NSApplicationTerminateReply) applicationShouldTerminate:(NSApplication*)sender;
- (NSApplicationTerminateReply)applicationShouldTerminate:
(NSApplication*)sender;
// NSUserNotificationCenterDelegate
- (BOOL) userNotificationCenter: (id)center
shouldPresentNotification: (id)notification;
- (BOOL)userNotificationCenter:(id)center
shouldPresentNotification:(id)notification;
- (void) setDockMenu: (NSMenu*)menu;
- (MacGlobalShortcutBackend*) shortcut_handler;
- (void) setShortcutHandler: (MacGlobalShortcutBackend*)backend;
- (void) mediaKeyTap: (SPMediaKeyTap*)keyTap receivedMediaKeyEvent:(NSEvent*)event;
- (void)setDockMenu:(NSMenu*)menu;
- (MacGlobalShortcutBackend*)shortcut_handler;
- (void)setShortcutHandler:(MacGlobalShortcutBackend*)backend;
- (void)mediaKeyTap:(SPMediaKeyTap*)keyTap
receivedMediaKeyEvent:(NSEvent*)event;
@end

View File

@ -30,5 +30,4 @@ namespace mac {
QKeySequence KeySequenceFromNSEvent(NSEvent* event);
void DumpDictionary(CFDictionaryRef dict);
float GetDevicePixelRatio(QWidget* widget);
}

View File

@ -36,7 +36,7 @@ class MacFSListener : public FileSystemWatcherInterface {
void RemovePath(const QString& path);
void Clear();
signals:
signals:
void PathChanged(const QString& path);
private slots:
@ -45,13 +45,10 @@ class MacFSListener : public FileSystemWatcherInterface {
private:
void UpdateStreamAsync();
static void EventStreamCallback(
ConstFSEventStreamRef stream,
void* user_data,
size_t num_events,
void* event_paths,
const FSEventStreamEventFlags event_flags[],
const FSEventStreamEventId event_ids[]);
static void EventStreamCallback(ConstFSEventStreamRef stream, void* user_data,
size_t num_events, void* event_paths,
const FSEventStreamEventFlags event_flags[],
const FSEventStreamEventId event_ids[]);
CFRunLoopRef run_loop_;
FSEventStreamRef stream_;

View File

@ -31,7 +31,7 @@ class QAction;
class MacGlobalShortcutBackend : public GlobalShortcutBackend {
Q_OBJECT
public:
public:
MacGlobalShortcutBackend(GlobalShortcuts* parent);
virtual ~MacGlobalShortcutBackend();
@ -40,11 +40,11 @@ public:
void MacMediaKeyPressed(int key);
protected:
protected:
bool DoRegister();
void DoUnregister();
private:
private:
bool KeyPressed(const QKeySequence& sequence);
QMap<QKeySequence, QAction*> shortcuts_;
@ -53,4 +53,4 @@ private:
std::unique_ptr<MacGlobalShortcutBackendPrivate> p_;
};
#endif // MACGLOBALSHORTCUTBACKEND_H
#endif // MACGLOBALSHORTCUTBACKEND_H

View File

@ -39,15 +39,12 @@ using boost::multi_index::multi_index_container;
using boost::multi_index::ordered_unique;
using boost::multi_index::tag;
std::size_t hash_value(const QModelIndex& index) {
return qHash(index);
}
std::size_t hash_value(const QModelIndex& index) { return qHash(index); }
namespace {
struct Mapping {
Mapping(const QModelIndex& _source_index)
: source_index(_source_index) {}
Mapping(const QModelIndex& _source_index) : source_index(_source_index) {}
QModelIndex source_index;
};
@ -60,28 +57,23 @@ struct tag_by_pointer {};
class MergedProxyModelPrivate {
private:
typedef multi_index_container<
Mapping*,
indexed_by<
hashed_unique<tag<tag_by_source>,
member<Mapping, QModelIndex, &Mapping::source_index> >,
ordered_unique<tag<tag_by_pointer>,
identity<Mapping*> >
>
> MappingContainer;
Mapping*,
indexed_by<
hashed_unique<tag<tag_by_source>,
member<Mapping, QModelIndex, &Mapping::source_index> >,
ordered_unique<tag<tag_by_pointer>, identity<Mapping*> > > >
MappingContainer;
public:
MappingContainer mappings_;
};
MergedProxyModel::MergedProxyModel(QObject* parent)
: QAbstractProxyModel(parent),
resetting_model_(nullptr),
p_(new MergedProxyModelPrivate) {
}
: QAbstractProxyModel(parent),
resetting_model_(nullptr),
p_(new MergedProxyModelPrivate) {}
MergedProxyModel::~MergedProxyModel() {
DeleteAllMappings();
}
MergedProxyModel::~MergedProxyModel() { DeleteAllMappings(); }
void MergedProxyModel::DeleteAllMappings() {
const auto& begin = p_->mappings_.get<tag_by_pointer>().begin();
@ -92,30 +84,28 @@ void MergedProxyModel::DeleteAllMappings() {
void MergedProxyModel::AddSubModel(const QModelIndex& source_parent,
QAbstractItemModel* submodel) {
connect(submodel, SIGNAL(modelReset()), this, SLOT(SubModelReset()));
connect(submodel, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)),
this, SLOT(RowsAboutToBeInserted(QModelIndex,int,int)));
connect(submodel, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
this, SLOT(RowsAboutToBeRemoved(QModelIndex,int,int)));
connect(submodel, SIGNAL(rowsInserted(QModelIndex,int,int)),
this, SLOT(RowsInserted(QModelIndex,int,int)));
connect(submodel, SIGNAL(rowsRemoved(QModelIndex,int,int)),
this, SLOT(RowsRemoved(QModelIndex,int,int)));
connect(submodel, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
this, SLOT(DataChanged(QModelIndex,QModelIndex)));
connect(submodel, SIGNAL(rowsAboutToBeInserted(QModelIndex, int, int)), this,
SLOT(RowsAboutToBeInserted(QModelIndex, int, int)));
connect(submodel, SIGNAL(rowsAboutToBeRemoved(QModelIndex, int, int)), this,
SLOT(RowsAboutToBeRemoved(QModelIndex, int, int)));
connect(submodel, SIGNAL(rowsInserted(QModelIndex, int, int)), this,
SLOT(RowsInserted(QModelIndex, int, int)));
connect(submodel, SIGNAL(rowsRemoved(QModelIndex, int, int)), this,
SLOT(RowsRemoved(QModelIndex, int, int)));
connect(submodel, SIGNAL(dataChanged(QModelIndex, QModelIndex)), this,
SLOT(DataChanged(QModelIndex, QModelIndex)));
QModelIndex proxy_parent = mapFromSource(source_parent);
const int rows = submodel->rowCount();
if (rows)
beginInsertRows(proxy_parent, 0, rows-1);
if (rows) beginInsertRows(proxy_parent, 0, rows - 1);
merge_points_.insert(submodel, source_parent);
if (rows)
endInsertRows();
if (rows) endInsertRows();
}
void MergedProxyModel::RemoveSubModel(const QModelIndex &source_parent) {
void MergedProxyModel::RemoveSubModel(const QModelIndex& source_parent) {
// Find the submodel that the parent corresponded to
QAbstractItemModel* submodel = merge_points_.key(source_parent);
merge_points_.remove(submodel);
@ -147,40 +137,42 @@ void MergedProxyModel::RemoveSubModel(const QModelIndex &source_parent) {
void MergedProxyModel::setSourceModel(QAbstractItemModel* source_model) {
if (sourceModel()) {
disconnect(sourceModel(), SIGNAL(modelReset()), this, SLOT(SourceModelReset()));
disconnect(sourceModel(), SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)),
this, SLOT(RowsAboutToBeInserted(QModelIndex,int,int)));
disconnect(sourceModel(), SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
this, SLOT(RowsAboutToBeRemoved(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)),
this, SLOT(DataChanged(QModelIndex,QModelIndex)));
disconnect(sourceModel(), SIGNAL(layoutAboutToBeChanged()),
this, SLOT(LayoutAboutToBeChanged()));
disconnect(sourceModel(), SIGNAL(layoutChanged()),
this, SLOT(LayoutChanged()));
disconnect(sourceModel(), SIGNAL(modelReset()), this,
SLOT(SourceModelReset()));
disconnect(sourceModel(),
SIGNAL(rowsAboutToBeInserted(QModelIndex, int, int)), this,
SLOT(RowsAboutToBeInserted(QModelIndex, int, int)));
disconnect(sourceModel(),
SIGNAL(rowsAboutToBeRemoved(QModelIndex, int, int)), this,
SLOT(RowsAboutToBeRemoved(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)),
this, SLOT(DataChanged(QModelIndex, QModelIndex)));
disconnect(sourceModel(), SIGNAL(layoutAboutToBeChanged()), this,
SLOT(LayoutAboutToBeChanged()));
disconnect(sourceModel(), SIGNAL(layoutChanged()), this,
SLOT(LayoutChanged()));
}
QAbstractProxyModel::setSourceModel(source_model);
connect(sourceModel(), SIGNAL(modelReset()), this, SLOT(SourceModelReset()));
connect(sourceModel(), SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)),
this, SLOT(RowsAboutToBeInserted(QModelIndex,int,int)));
connect(sourceModel(), SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
this, SLOT(RowsAboutToBeRemoved(QModelIndex,int,int)));
connect(sourceModel(), SIGNAL(rowsInserted(QModelIndex,int,int)),
this, SLOT(RowsInserted(QModelIndex,int,int)));
connect(sourceModel(), SIGNAL(rowsRemoved(QModelIndex,int,int)),
this, SLOT(RowsRemoved(QModelIndex,int,int)));
connect(sourceModel(), SIGNAL(dataChanged(QModelIndex,QModelIndex)),
this, SLOT(DataChanged(QModelIndex,QModelIndex)));
connect(sourceModel(), SIGNAL(layoutAboutToBeChanged()),
this, SLOT(LayoutAboutToBeChanged()));
connect(sourceModel(), SIGNAL(layoutChanged()),
this, SLOT(LayoutChanged()));
connect(sourceModel(), SIGNAL(rowsAboutToBeInserted(QModelIndex, int, int)),
this, SLOT(RowsAboutToBeInserted(QModelIndex, int, int)));
connect(sourceModel(), SIGNAL(rowsAboutToBeRemoved(QModelIndex, int, int)),
this, SLOT(RowsAboutToBeRemoved(QModelIndex, int, int)));
connect(sourceModel(), SIGNAL(rowsInserted(QModelIndex, int, int)), this,
SLOT(RowsInserted(QModelIndex, int, int)));
connect(sourceModel(), SIGNAL(rowsRemoved(QModelIndex, int, int)), this,
SLOT(RowsRemoved(QModelIndex, int, int)));
connect(sourceModel(), SIGNAL(dataChanged(QModelIndex, QModelIndex)), this,
SLOT(DataChanged(QModelIndex, QModelIndex)));
connect(sourceModel(), SIGNAL(layoutAboutToBeChanged()), this,
SLOT(LayoutAboutToBeChanged()));
connect(sourceModel(), SIGNAL(layoutChanged()), this, SLOT(LayoutChanged()));
}
void MergedProxyModel::SourceModelReset() {
@ -227,15 +219,15 @@ void MergedProxyModel::SubModelReset() {
// "Insert" items from the newly reset submodel
int count = submodel->rowCount();
if (count) {
beginInsertRows(proxy_parent, 0, count-1);
beginInsertRows(proxy_parent, 0, count - 1);
endInsertRows();
}
emit SubModelReset(proxy_parent, submodel);
}
QModelIndex MergedProxyModel::GetActualSourceParent(const QModelIndex& source_parent,
QAbstractItemModel* model) const {
QModelIndex MergedProxyModel::GetActualSourceParent(
const QModelIndex& source_parent, QAbstractItemModel* model) const {
if (!source_parent.isValid() && model != sourceModel())
return merge_points_.value(model);
return source_parent;
@ -243,8 +235,9 @@ QModelIndex MergedProxyModel::GetActualSourceParent(const QModelIndex& source_pa
void MergedProxyModel::RowsAboutToBeInserted(const QModelIndex& source_parent,
int start, int end) {
beginInsertRows(mapFromSource(GetActualSourceParent(
source_parent, static_cast<QAbstractItemModel*>(sender()))),
beginInsertRows(
mapFromSource(GetActualSourceParent(
source_parent, static_cast<QAbstractItemModel*>(sender()))),
start, end);
}
@ -254,8 +247,9 @@ void MergedProxyModel::RowsInserted(const QModelIndex&, int, int) {
void MergedProxyModel::RowsAboutToBeRemoved(const QModelIndex& source_parent,
int start, int end) {
beginRemoveRows(mapFromSource(GetActualSourceParent(
source_parent, static_cast<QAbstractItemModel*>(sender()))),
beginRemoveRows(
mapFromSource(GetActualSourceParent(
source_parent, static_cast<QAbstractItemModel*>(sender()))),
start, end);
}
@ -263,29 +257,26 @@ void MergedProxyModel::RowsRemoved(const QModelIndex&, int, int) {
endRemoveRows();
}
QModelIndex MergedProxyModel::mapToSource(const QModelIndex& proxy_index) const {
if (!proxy_index.isValid())
return QModelIndex();
QModelIndex MergedProxyModel::mapToSource(const QModelIndex& proxy_index)
const {
if (!proxy_index.isValid()) return QModelIndex();
Mapping* mapping = static_cast<Mapping*>(proxy_index.internalPointer());
if (p_->mappings_.get<tag_by_pointer>().find(mapping) ==
p_->mappings_.get<tag_by_pointer>().end())
return QModelIndex();
if (mapping->source_index.model() == resetting_model_)
return QModelIndex();
if (mapping->source_index.model() == resetting_model_) return QModelIndex();
return mapping->source_index;
}
QModelIndex MergedProxyModel::mapFromSource(const QModelIndex& source_index) const {
if (!source_index.isValid())
return QModelIndex();
if (source_index.model() == resetting_model_)
return QModelIndex();
QModelIndex MergedProxyModel::mapFromSource(const QModelIndex& source_index)
const {
if (!source_index.isValid()) return QModelIndex();
if (source_index.model() == resetting_model_) return QModelIndex();
// Add a mapping if we don't have one already
const auto& it =
p_->mappings_.get<tag_by_source>().find(source_index);
const auto& it = p_->mappings_.get<tag_by_source>().find(source_index);
Mapping* mapping;
if (it != p_->mappings_.get<tag_by_source>().end()) {
mapping = *it;
@ -297,7 +288,8 @@ QModelIndex MergedProxyModel::mapFromSource(const QModelIndex& source_index) con
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;
if (!parent.isValid()) {
@ -315,26 +307,23 @@ QModelIndex MergedProxyModel::index(int row, int column, const QModelIndex &pare
return mapFromSource(source_index);
}
QModelIndex MergedProxyModel::parent(const QModelIndex &child) const {
QModelIndex MergedProxyModel::parent(const QModelIndex& child) const {
QModelIndex source_child = mapToSource(child);
if (source_child.model() == sourceModel())
return mapFromSource(source_child.parent());
if (!IsKnownModel(source_child.model()))
return QModelIndex();
if (!IsKnownModel(source_child.model())) return QModelIndex();
if (!source_child.parent().isValid())
return mapFromSource(merge_points_.value(GetModel(source_child)));
return mapFromSource(source_child.parent());
}
int MergedProxyModel::rowCount(const QModelIndex &parent) const {
if (!parent.isValid())
return sourceModel()->rowCount(QModelIndex());
int MergedProxyModel::rowCount(const QModelIndex& parent) const {
if (!parent.isValid()) return sourceModel()->rowCount(QModelIndex());
QModelIndex source_parent = mapToSource(parent);
if (!IsKnownModel(source_parent.model()))
return 0;
if (!IsKnownModel(source_parent.model())) return 0;
const QAbstractItemModel* child_model = merge_points_.key(source_parent);
if (child_model) {
@ -348,27 +337,22 @@ int MergedProxyModel::rowCount(const QModelIndex &parent) const {
return source_parent.model()->rowCount(source_parent);
}
int MergedProxyModel::columnCount(const QModelIndex &parent) const {
if (!parent.isValid())
return sourceModel()->columnCount(QModelIndex());
int MergedProxyModel::columnCount(const QModelIndex& parent) const {
if (!parent.isValid()) return sourceModel()->columnCount(QModelIndex());
QModelIndex source_parent = mapToSource(parent);
if (!IsKnownModel(source_parent.model()))
return 0;
if (!IsKnownModel(source_parent.model())) return 0;
const QAbstractItemModel* child_model = merge_points_.key(source_parent);
if (child_model)
return child_model->columnCount(QModelIndex());
if (child_model) return child_model->columnCount(QModelIndex());
return source_parent.model()->columnCount(source_parent);
}
bool MergedProxyModel::hasChildren(const QModelIndex &parent) const {
if (!parent.isValid())
return sourceModel()->hasChildren(QModelIndex());
bool MergedProxyModel::hasChildren(const QModelIndex& parent) const {
if (!parent.isValid()) return sourceModel()->hasChildren(QModelIndex());
QModelIndex source_parent = mapToSource(parent);
if (!IsKnownModel(source_parent.model()))
return false;
if (!IsKnownModel(source_parent.model())) return false;
const QAbstractItemModel* child_model = merge_points_.key(source_parent);
@ -380,29 +364,27 @@ bool MergedProxyModel::hasChildren(const QModelIndex &parent) const {
QVariant MergedProxyModel::data(const QModelIndex& proxyIndex, int role) const {
QModelIndex source_index = mapToSource(proxyIndex);
if (!IsKnownModel(source_index.model()))
return QVariant();
if (!IsKnownModel(source_index.model())) return QVariant();
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);
if (!source_index.isValid())
return sourceModel()->itemData(QModelIndex());
if (!source_index.isValid()) return sourceModel()->itemData(QModelIndex());
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);
if (!source_index.isValid())
return sourceModel()->flags(QModelIndex());
if (!source_index.isValid()) return sourceModel()->flags(QModelIndex());
return source_index.model()->flags(source_index);
}
bool MergedProxyModel::setData(const QModelIndex &index, const QVariant &value,
bool MergedProxyModel::setData(const QModelIndex& index, const QVariant& value,
int role) {
QModelIndex source_index = mapToSource(index);
@ -415,16 +397,15 @@ QStringList MergedProxyModel::mimeTypes() const {
QStringList ret;
ret << sourceModel()->mimeTypes();
foreach (const QAbstractItemModel* model, merge_points_.keys()) {
foreach(const QAbstractItemModel * model, merge_points_.keys()) {
ret << model->mimeTypes();
}
return ret;
}
QMimeData* MergedProxyModel::mimeData(const QModelIndexList &indexes) const {
if (indexes.isEmpty())
return 0;
QMimeData* MergedProxyModel::mimeData(const QModelIndexList& indexes) const {
if (indexes.isEmpty()) return 0;
// Only ask the first index's model
const QAbstractItemModel* model = mapToSource(indexes[0]).model();
@ -435,17 +416,18 @@ QMimeData* MergedProxyModel::mimeData(const QModelIndexList &indexes) const {
// Only ask about the indexes that are actually in that model
QModelIndexList indexes_in_model;
foreach (const QModelIndex& proxy_index, indexes) {
foreach(const QModelIndex & proxy_index, indexes) {
QModelIndex source_index = mapToSource(proxy_index);
if (source_index.model() != model)
continue;
if (source_index.model() != model) continue;
indexes_in_model << source_index;
}
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()) {
return false;
}
@ -453,17 +435,16 @@ bool MergedProxyModel::dropMimeData(const QMimeData* data, Qt::DropAction action
return sourceModel()->dropMimeData(data, action, row, column, parent);
}
QModelIndex MergedProxyModel::FindSourceParent(const QModelIndex& proxy_index) const {
if (!proxy_index.isValid())
return QModelIndex();
QModelIndex MergedProxyModel::FindSourceParent(const QModelIndex& proxy_index)
const {
if (!proxy_index.isValid()) return QModelIndex();
QModelIndex source_index = mapToSource(proxy_index);
if (source_index.model() == sourceModel())
return source_index;
if (source_index.model() == sourceModel()) return source_index;
return merge_points_.value(GetModel(source_index));
}
bool MergedProxyModel::canFetchMore(const QModelIndex &parent) const {
bool MergedProxyModel::canFetchMore(const QModelIndex& parent) const {
QModelIndex source_index = mapToSource(parent);
if (!source_index.isValid())
@ -480,34 +461,33 @@ void MergedProxyModel::fetchMore(const QModelIndex& parent) {
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()),
// but without the const_cast
const QAbstractItemModel* const_model = source_index.model();
if (const_model == sourceModel())
return sourceModel();
foreach (QAbstractItemModel* submodel, merge_points_.keys()) {
if (submodel == const_model)
return submodel;
if (const_model == sourceModel()) return sourceModel();
foreach(QAbstractItemModel * submodel, merge_points_.keys()) {
if (submodel == const_model) return submodel;
}
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));
}
void MergedProxyModel::LayoutAboutToBeChanged() {
old_merge_points_.clear();
foreach (QAbstractItemModel* key, merge_points_.keys()) {
foreach(QAbstractItemModel * key, merge_points_.keys()) {
old_merge_points_[key] = merge_points_.value(key);
}
}
void MergedProxyModel::LayoutChanged() {
foreach (QAbstractItemModel* key, merge_points_.keys()) {
if (!old_merge_points_.contains(key))
continue;
foreach(QAbstractItemModel * key, merge_points_.keys()) {
if (!old_merge_points_.contains(key)) continue;
const int old_row = old_merge_points_[key].row();
const int new_row = merge_points_[key].row();
@ -526,17 +506,19 @@ bool MergedProxyModel::IsKnownModel(const QAbstractItemModel* model) const {
return false;
}
QModelIndexList MergedProxyModel::mapFromSource(const QModelIndexList& source_indexes) const {
QModelIndexList MergedProxyModel::mapFromSource(
const QModelIndexList& source_indexes) const {
QModelIndexList ret;
foreach (const QModelIndex& index, source_indexes) {
foreach(const QModelIndex & index, source_indexes) {
ret << mapFromSource(index);
}
return ret;
}
QModelIndexList MergedProxyModel::mapToSource(const QModelIndexList& proxy_indexes) const {
QModelIndexList MergedProxyModel::mapToSource(
const QModelIndexList& proxy_indexes) const {
QModelIndexList ret;
foreach (const QModelIndex& index, proxy_indexes) {
foreach(const QModelIndex & index, proxy_indexes) {
ret << mapToSource(index);
}
return ret;

View File

@ -34,7 +34,8 @@ class MergedProxyModel : public QAbstractProxyModel {
~MergedProxyModel();
// 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);
// Find the item in the source model that is the parent of the model
@ -43,45 +44,50 @@ class MergedProxyModel : public QAbstractProxyModel {
QModelIndex FindSourceParent(const QModelIndex& proxy_index) const;
// QAbstractItemModel
QModelIndex index(int row, int column, const QModelIndex &parent) const;
QModelIndex parent(const QModelIndex &child) const;
int rowCount(const QModelIndex &parent) const;
int columnCount(const QModelIndex &parent) const;
QVariant data(const QModelIndex &proxyIndex, int role = Qt::DisplayRole) const;
bool hasChildren(const QModelIndex &parent) const;
QMap<int, QVariant> itemData(const QModelIndex &proxyIndex) const;
Qt::ItemFlags flags(const QModelIndex &index) const;
bool setData(const QModelIndex &index, const QVariant &value, int role);
QModelIndex index(int row, int column, const QModelIndex& parent) const;
QModelIndex parent(const QModelIndex& child) const;
int rowCount(const QModelIndex& parent) const;
int columnCount(const QModelIndex& parent) const;
QVariant data(const QModelIndex& proxyIndex,
int role = Qt::DisplayRole) const;
bool hasChildren(const QModelIndex& parent) const;
QMap<int, QVariant> itemData(const QModelIndex& proxyIndex) const;
Qt::ItemFlags flags(const QModelIndex& index) const;
bool setData(const QModelIndex& index, const QVariant& value, int role);
QStringList mimeTypes() const;
QMimeData* mimeData(const QModelIndexList &indexes) const;
bool dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent);
bool canFetchMore(const QModelIndex &parent) const;
QMimeData* mimeData(const QModelIndexList& indexes) const;
bool dropMimeData(const QMimeData* data, Qt::DropAction action, int row,
int column, const QModelIndex& parent);
bool canFetchMore(const QModelIndex& parent) const;
void fetchMore(const QModelIndex& parent);
// QAbstractProxyModel
// Note that these implementations of map{To,From}Source will not always
// give you an index in sourceModel(), you might get an index in one of the
// child models instead.
QModelIndex mapFromSource(const QModelIndex &sourceIndex) const;
QModelIndex mapToSource(const QModelIndex &proxyIndex) const;
void setSourceModel(QAbstractItemModel *sourceModel);
QModelIndex mapFromSource(const QModelIndex& sourceIndex) const;
QModelIndex mapToSource(const QModelIndex& proxyIndex) const;
void setSourceModel(QAbstractItemModel* sourceModel);
// Convenience functions that call map{To,From}Source multiple times.
QModelIndexList mapFromSource(const QModelIndexList& source_indexes) const;
QModelIndexList mapToSource(const QModelIndexList& proxy_indexes) const;
signals:
signals:
void SubModelReset(const QModelIndex& root, QAbstractItemModel* model);
private slots:
void SourceModelReset();
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 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 DataChanged(const QModelIndex& top_left, const QModelIndex& bottom_right);
void DataChanged(const QModelIndex& top_left,
const QModelIndex& bottom_right);
void LayoutAboutToBeChanged();
void LayoutChanged();
@ -93,7 +99,6 @@ class MergedProxyModel : public QAbstractProxyModel {
void DeleteAllMappings();
bool IsKnownModel(const QAbstractItemModel* model) const;
QMap<QAbstractItemModel*, QPersistentModelIndex> merge_points_;
QAbstractItemModel* resetting_model_;
@ -102,4 +107,4 @@ class MergedProxyModel : public QAbstractProxyModel {
std::unique_ptr<MergedProxyModelPrivate> p_;
};
#endif // MERGEDPROXYMODEL_H
#endif // MERGEDPROXYMODEL_H

View File

@ -32,7 +32,8 @@ void RegisterMetaTypes() {
qRegisterMetaType<const char*>("const char*");
qRegisterMetaType<CoverSearchResult>("CoverSearchResult");
qRegisterMetaType<CoverSearchResults>("CoverSearchResults");
qRegisterMetaType<DigitallyImportedClient::Channel>("DigitallyImportedClient::Channel");
qRegisterMetaType<DigitallyImportedClient::Channel>(
"DigitallyImportedClient::Channel");
qRegisterMetaType<Directory>("Directory");
qRegisterMetaType<DirectoryList>("DirectoryList");
qRegisterMetaType<Engine::SimpleMetaBundle>("Engine::SimpleMetaBundle");
@ -49,8 +50,10 @@ void RegisterMetaTypes() {
qRegisterMetaType<PodcastList>("PodcastList");
qRegisterMetaType<QList<CoverSearchResult> >("QList<CoverSearchResult>");
qRegisterMetaType<QList<PlaylistItemPtr> >("QList<PlaylistItemPtr>");
qRegisterMetaType<PlaylistSequence::RepeatMode>("PlaylistSequence::RepeatMode");
qRegisterMetaType<PlaylistSequence::ShuffleMode>("PlaylistSequence::ShuffleMode");
qRegisterMetaType<PlaylistSequence::RepeatMode>(
"PlaylistSequence::RepeatMode");
qRegisterMetaType<PlaylistSequence::ShuffleMode>(
"PlaylistSequence::ShuffleMode");
qRegisterMetaType<QList<PodcastEpisode> >("QList<PodcastEpisode>");
qRegisterMetaType<QList<Podcast> >("QList<Podcast>");
qRegisterMetaType<QList<QNetworkCookie> >("QList<QNetworkCookie>");
@ -60,14 +63,17 @@ void RegisterMetaTypes() {
qRegisterMetaType<QNetworkReply**>("QNetworkReply**");
qRegisterMetaType<SearchProvider::ResultList>("SearchProvider::ResultList");
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<SongList>("SongList");
qRegisterMetaType<Song>("Song");
qRegisterMetaTypeStreamOperators<DigitallyImportedClient::Channel>("DigitallyImportedClient::Channel");
qRegisterMetaTypeStreamOperators<DigitallyImportedClient::Channel>(
"DigitallyImportedClient::Channel");
qRegisterMetaTypeStreamOperators<Equalizer::Params>("Equalizer::Params");
qRegisterMetaTypeStreamOperators<QMap<int, int> >("ColumnAlignmentMap");
qRegisterMetaTypeStreamOperators<SomaFMService::Stream>("SomaFMService::Stream");
qRegisterMetaTypeStreamOperators<SomaFMService::Stream>(
"SomaFMService::Stream");
qRegisterMetaType<SubdirectoryList>("SubdirectoryList");
qRegisterMetaType<Subdirectory>("Subdirectory");
qRegisterMetaType<QList<QUrl> >("QList<QUrl>");

View File

@ -23,16 +23,16 @@
class MimeData : public QMimeData {
Q_OBJECT
public:
MimeData(bool clear = false, bool play_now = false,
bool enqueue = false, bool open_in_new_playlist = false)
: override_user_settings_(false),
clear_first_(clear),
play_now_(play_now),
enqueue_now_(enqueue),
open_in_new_playlist_(open_in_new_playlist),
name_for_new_playlist_(QString()),
from_doubleclick_(false) {}
public:
MimeData(bool clear = false, bool play_now = false, bool enqueue = false,
bool open_in_new_playlist = false)
: override_user_settings_(false),
clear_first_(clear),
play_now_(play_now),
enqueue_now_(enqueue),
open_in_new_playlist_(open_in_new_playlist),
name_for_new_playlist_(QString()),
from_doubleclick_(false) {}
// If this is set then MainWindow will not touch any of the other flags.
bool override_user_settings_;
@ -43,7 +43,8 @@ public:
// 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
// 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_;
// If this is set then the items are added to the queue after being inserted.
@ -60,12 +61,15 @@ public:
// the defaults set by the user.
bool from_doubleclick_;
// Returns a pretty name for a playlist containing songs described by this MimeData
// object. By pretty name we mean the value of 'name_for_new_playlist_' or generic
// Returns a pretty name for a playlist containing songs described by this
// 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.
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_;
}
};
#endif // MIMEDATA_H
#endif // MIMEDATA_H

View File

@ -8,18 +8,14 @@ template <typename T>
class ModelFutureWatcher : public QFutureWatcher<T> {
public:
ModelFutureWatcher(const QModelIndex& index, QObject* parent = 0)
: QFutureWatcher<T>(parent),
index_(index) {
}
: QFutureWatcher<T>(parent), index_(index) {}
~ModelFutureWatcher() {
}
~ModelFutureWatcher() {}
const QPersistentModelIndex& index() const { return index_; }
private:
QPersistentModelIndex index_;
};
#endif

View File

@ -22,11 +22,10 @@
namespace mpris {
Mpris::Mpris(Application* app, QObject* parent)
: QObject(parent),
mpris1_(new mpris::Mpris1(app, this)),
mpris2_(new mpris::Mpris2(app, mpris1_, this))
{
: QObject(parent),
mpris1_(new mpris::Mpris1(app, this)),
mpris2_(new mpris::Mpris2(app, mpris1_, this)) {
connect(mpris2_, SIGNAL(RaiseMainWindow()), SIGNAL(RaiseMainWindow()));
}
} // namespace mpris
} // namespace mpris

View File

@ -30,17 +30,17 @@ class Mpris2;
class Mpris : public QObject {
Q_OBJECT
public:
public:
Mpris(Application* app, QObject* parent = 0);
signals:
void RaiseMainWindow();
private:
private:
Mpris1* mpris1_;
Mpris2* mpris2_;
};
} // namespace mpris
} // namespace mpris
#endif // MPRIS_H
#endif // MPRIS_H

View File

@ -39,12 +39,11 @@ const char* Mpris1::kDefaultDbusServiceName = "org.mpris.clementine";
Mpris1::Mpris1(Application* app, QObject* parent,
const QString& dbus_service_name)
: QObject(parent),
dbus_service_name_(dbus_service_name),
root_(nullptr),
player_(nullptr),
tracklist_(nullptr)
{
: QObject(parent),
dbus_service_name_(dbus_service_name),
root_(nullptr),
player_(nullptr),
tracklist_(nullptr) {
qDBusRegisterMetaType<DBusStatus>();
qDBusRegisterMetaType<Version>();
@ -53,7 +52,8 @@ Mpris1::Mpris1(Application* app, QObject* parent,
}
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;
}
@ -61,8 +61,10 @@ Mpris1::Mpris1(Application* app, QObject* parent,
player_ = new Mpris1Player(app, this);
tracklist_ = new Mpris1TrackList(app, this);
connect(app->current_art_loader(), SIGNAL(ArtLoaded(const Song&, const QString&, const QImage&)),
player_, SLOT(CurrentSongChanged(const Song&, const QString&, const QImage&)));
connect(app->current_art_loader(),
SIGNAL(ArtLoaded(const Song&, const QString&, const QImage&)),
player_,
SLOT(CurrentSongChanged(const Song&, const QString&, const QImage&)));
}
Mpris1::~Mpris1() {
@ -70,27 +72,29 @@ Mpris1::~Mpris1() {
}
Mpris1Root::Mpris1Root(Application* app, QObject* parent)
: QObject(parent),
app_(app) {
: QObject(parent), app_(app) {
new MprisRoot(this);
QDBusConnection::sessionBus().registerObject("/", this);
}
Mpris1Player::Mpris1Player(Application* app, QObject* parent)
: QObject(parent),
app_(app) {
: QObject(parent), app_(app) {
new MprisPlayer(this);
QDBusConnection::sessionBus().registerObject("/Player", this);
connect(app_->player()->engine(), SIGNAL(StateChanged(Engine::State)), SLOT(EngineStateChanged(Engine::State)));
connect(app_->playlist_manager(), SIGNAL(PlaylistManagerInitialized()), SLOT(PlaylistManagerInitialized()));
connect(app_->player()->engine(), SIGNAL(StateChanged(Engine::State)),
SLOT(EngineStateChanged(Engine::State)));
connect(app_->playlist_manager(), SIGNAL(PlaylistManagerInitialized()),
SLOT(PlaylistManagerInitialized()));
}
// when PlaylistManager gets it ready, we connect PlaylistSequence with this
void Mpris1Player::PlaylistManagerInitialized() {
connect(app_->playlist_manager()->sequence(), SIGNAL(ShuffleModeChanged(PlaylistSequence::ShuffleMode)),
connect(app_->playlist_manager()->sequence(),
SIGNAL(ShuffleModeChanged(PlaylistSequence::ShuffleMode)),
SLOT(ShuffleModeChanged()));
connect(app_->playlist_manager()->sequence(), SIGNAL(RepeatModeChanged(PlaylistSequence::RepeatMode)),
connect(app_->playlist_manager()->sequence(),
SIGNAL(RepeatModeChanged(PlaylistSequence::RepeatMode)),
SLOT(RepeatModeChanged()));
}
@ -99,7 +103,8 @@ Mpris1TrackList::Mpris1TrackList(Application* app, QObject* parent)
new MprisTrackList(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) {
@ -113,8 +118,8 @@ void Mpris1Player::EngineStateChanged(Engine::State state) {
emit CapsChange(GetCaps(state));
}
void Mpris1Player::CurrentSongChanged(
const Song& song, const QString& art_uri, const QImage&) {
void Mpris1Player::CurrentSongChanged(const Song& song, const QString& art_uri,
const QImage&) {
last_metadata_ = Mpris1::GetMetadata(song);
if (!art_uri.isEmpty()) {
@ -126,11 +131,9 @@ void Mpris1Player::CurrentSongChanged(
emit CapsChange(GetCaps());
}
QString Mpris1Root::Identity() {
return QString("%1 %2").arg(
QCoreApplication::applicationName(),
QCoreApplication::applicationVersion());
return QString("%1 %2").arg(QCoreApplication::applicationName(),
QCoreApplication::applicationVersion());
}
Version Mpris1Root::MprisVersion() {
@ -140,42 +143,26 @@ Version Mpris1Root::MprisVersion() {
return version;
}
void Mpris1Root::Quit() {
qApp->quit();
}
void Mpris1Root::Quit() { qApp->quit(); }
void Mpris1Player::Pause() {
app_->player()->PlayPause();
}
void Mpris1Player::Pause() { app_->player()->PlayPause(); }
void Mpris1Player::Stop() {
app_->player()->Stop();
}
void Mpris1Player::Stop() { app_->player()->Stop(); }
void Mpris1Player::Prev() {
app_->player()->Previous();
}
void Mpris1Player::Prev() { app_->player()->Previous(); }
void Mpris1Player::Play() {
app_->player()->Play();
}
void Mpris1Player::Play() { app_->player()->Play(); }
void Mpris1Player::Next() {
app_->player()->Next();
}
void Mpris1Player::Next() { app_->player()->Next(); }
void Mpris1Player::Repeat(bool repeat) {
app_->playlist_manager()->sequence()->SetRepeatMode(
repeat ? PlaylistSequence::Repeat_Track : PlaylistSequence::Repeat_Off);
}
void Mpris1Player::ShuffleModeChanged() {
emit StatusChange(GetStatus());
}
void Mpris1Player::ShuffleModeChanged() { emit StatusChange(GetStatus()); }
void Mpris1Player::RepeatModeChanged() {
emit StatusChange(GetStatus());
}
void Mpris1Player::RepeatModeChanged() { emit StatusChange(GetStatus()); }
DBusStatus Mpris1Player::GetStatus() const {
return GetStatus(app_->player()->GetState());
@ -199,25 +186,27 @@ DBusStatus Mpris1Player::GetStatus(Engine::State state) const {
if (app_->playlist_manager()->sequence()) {
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_playlist = (repeat_mode == PlaylistSequence::Repeat_Album ||
repeat_mode == PlaylistSequence::Repeat_Playlist ||
repeat_mode == PlaylistSequence::Repeat_Track) ? 1 : 0;
status.repeat_playlist =
(repeat_mode == PlaylistSequence::Repeat_Album ||
repeat_mode == PlaylistSequence::Repeat_Playlist ||
repeat_mode == PlaylistSequence::Repeat_Track)
? 1
: 0;
}
return status;
}
void Mpris1Player::VolumeSet(int volume) {
app_->player()->SetVolume(volume);
}
void Mpris1Player::VolumeSet(int volume) { app_->player()->SetVolume(volume); }
int Mpris1Player::VolumeGet() const {
return app_->player()->GetVolume();
}
int Mpris1Player::VolumeGet() const { return app_->player()->GetVolume(); }
void Mpris1Player::PositionSet(int pos_msec) {
app_->player()->SeekTo(pos_msec / kMsecPerSec);
@ -227,9 +216,7 @@ int Mpris1Player::PositionGet() const {
return app_->player()->engine()->position_nanosec() / kNsecPerMsec;
}
QVariantMap Mpris1Player::GetMetadata() const {
return last_metadata_;
}
QVariantMap Mpris1Player::GetMetadata() const { return last_metadata_; }
int Mpris1Player::GetCaps() const {
return GetCaps(app_->player()->GetState());
@ -241,9 +228,12 @@ int Mpris1Player::GetCaps(Engine::State state) const {
PlaylistManagerInterface* playlists = app_->playlist_manager();
if (playlists->active()) {
// play is disabled when playlist is empty or when last.fm stream is already playing
if (playlists->active() && playlists->active()->rowCount() != 0
&& !(state == Engine::Playing && (app_->player()->GetCurrentItem()->options() & PlaylistItem::LastFMControls))) {
// play is disabled when playlist is empty or when last.fm stream is already
// playing
if (playlists->active() && playlists->active()->rowCount() != 0 &&
!(state == Engine::Playing &&
(app_->player()->GetCurrentItem()->options() &
PlaylistItem::LastFMControls))) {
caps |= CAN_PLAY;
}
@ -257,7 +247,8 @@ int Mpris1Player::GetCaps(Engine::State state) const {
if (current_item) {
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;
}
if (state != Engine::Empty && !current_item->Metadata().is_stream()) {
@ -268,25 +259,17 @@ int Mpris1Player::GetCaps(Engine::State state) const {
return caps;
}
void Mpris1Player::VolumeUp(int change) {
VolumeSet(VolumeGet() + change);
}
void Mpris1Player::VolumeUp(int change) { VolumeSet(VolumeGet() + change); }
void Mpris1Player::VolumeDown(int change) {
VolumeSet(VolumeGet() - change);
}
void Mpris1Player::VolumeDown(int change) { VolumeSet(VolumeGet() - change); }
void Mpris1Player::Mute() {
app_->player()->Mute();
}
void Mpris1Player::Mute() { app_->player()->Mute(); }
void Mpris1Player::ShowOSD() {
app_->player()->ShowOSD();
}
void Mpris1Player::ShowOSD() { app_->player()->ShowOSD(); }
int Mpris1TrackList::AddTrack(const QString& track, bool play) {
app_->playlist_manager()->active()->InsertUrls(
QList<QUrl>() << QUrl(track), -1, play);
app_->playlist_manager()->active()->InsertUrls(QList<QUrl>() << QUrl(track),
-1, play);
return 0;
}
@ -304,15 +287,15 @@ int Mpris1TrackList::GetLength() const {
QVariantMap Mpris1TrackList::GetMetadata(int pos) const {
PlaylistItemPtr item = app_->player()->GetItemAt(pos);
if (!item)
return QVariantMap();
if (!item) return QVariantMap();
return Mpris1::GetMetadata(item->Metadata());
}
void Mpris1TrackList::SetLoop(bool enable) {
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) {
@ -351,24 +334,23 @@ QVariantMap Mpris1::GetMetadata(const Song& song) {
return ret;
}
} // namespace mpris
} // namespace mpris
QDBusArgument& operator<< (QDBusArgument& arg, const Version& version) {
QDBusArgument& operator<<(QDBusArgument& arg, const Version& version) {
arg.beginStructure();
arg << version.major << version.minor;
arg.endStructure();
return arg;
}
const QDBusArgument& operator>> (const QDBusArgument& arg, Version& version) {
const QDBusArgument& operator>>(const QDBusArgument& arg, Version& version) {
arg.beginStructure();
arg >> version.major >> version.minor;
arg.endStructure();
return arg;
}
QDBusArgument& operator<< (QDBusArgument& arg, const DBusStatus& status) {
QDBusArgument& operator<<(QDBusArgument& arg, const DBusStatus& status) {
arg.beginStructure();
arg << status.play;
arg << status.random;
@ -378,7 +360,7 @@ QDBusArgument& operator<< (QDBusArgument& arg, const DBusStatus& status) {
return arg;
}
const QDBusArgument& operator>> (const QDBusArgument& arg, DBusStatus& status) {
const QDBusArgument& operator>>(const QDBusArgument& arg, DBusStatus& status) {
arg.beginStructure();
arg >> status.play;
arg >> status.random;

View File

@ -27,18 +27,15 @@
class Application;
class Playlist;
struct DBusStatus { // From Amarok.
struct DBusStatus { // From Amarok.
DBusStatus()
: play(Mpris_Stopped),
random(0),
repeat(0),
repeat_playlist(0)
{}
: play(Mpris_Stopped), random(0), repeat(0), repeat_playlist(0) {}
int play; // Playing = 0, Paused = 1, Stopped = 2
int random; // Linearly = 0, Randomly = 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 play; // Playing = 0, Paused = 1, Stopped = 2
int random; // Linearly = 0, Randomly = 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
enum MprisPlayState {
Mpris_Playing = 0,
@ -48,9 +45,8 @@ struct DBusStatus { // From Amarok.
};
Q_DECLARE_METATYPE(DBusStatus);
QDBusArgument& operator <<(QDBusArgument& arg, const DBusStatus& status);
const QDBusArgument& operator >>(const QDBusArgument& arg, DBusStatus& status);
QDBusArgument& operator<<(QDBusArgument& arg, const DBusStatus& status);
const QDBusArgument& operator>>(const QDBusArgument& arg, DBusStatus& status);
struct Version {
quint16 minor;
@ -58,31 +54,30 @@ struct Version {
};
Q_DECLARE_METATYPE(Version);
QDBusArgument& operator <<(QDBusArgument& arg, const Version& version);
const QDBusArgument& operator >>(const QDBusArgument& arg, Version& version);
QDBusArgument& operator<<(QDBusArgument& arg, const Version& version);
const QDBusArgument& operator>>(const QDBusArgument& arg, Version& version);
namespace mpris {
enum DBusCaps {
NONE = 0,
CAN_GO_NEXT = 1 << 0,
CAN_GO_PREV = 1 << 1,
CAN_PAUSE = 1 << 2,
CAN_PLAY = 1 << 3,
CAN_SEEK = 1 << 4,
NONE = 0,
CAN_GO_NEXT = 1 << 0,
CAN_GO_PREV = 1 << 1,
CAN_PAUSE = 1 << 2,
CAN_PLAY = 1 << 3,
CAN_SEEK = 1 << 4,
CAN_PROVIDE_METADATA = 1 << 5,
CAN_HAS_TRACKLIST = 1 << 6,
CAN_HAS_TRACKLIST = 1 << 6,
};
class Mpris1Root;
class Mpris1Player;
class Mpris1TrackList;
class Mpris1 : public QObject {
Q_OBJECT
public:
public:
Mpris1(Application* app, QObject* parent = 0,
const QString& dbus_service_name = QString());
~Mpris1();
@ -93,7 +88,7 @@ public:
Mpris1Player* player() const { return player_; }
Mpris1TrackList* tracklist() const { return tracklist_; }
private:
private:
static const char* kDefaultDbusServiceName;
QString dbus_service_name_;
@ -103,26 +98,24 @@ private:
Mpris1TrackList* tracklist_;
};
class Mpris1Root : public QObject {
Q_OBJECT
public:
public:
Mpris1Root(Application* app, QObject* parent = 0);
QString Identity();
void Quit();
Version MprisVersion();
private:
private:
Application* app_;
};
class Mpris1Player : public QObject {
Q_OBJECT
public:
public:
Mpris1Player(Application* app, QObject* parent = 0);
void Pause();
@ -132,7 +125,8 @@ public:
void Next();
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;
int GetCaps() const;
// those methods will use engine's state provided as an argument
@ -151,33 +145,32 @@ public:
void Mute();
void ShowOSD();
public slots:
void CurrentSongChanged(
const Song& song, const QString& art_uri, const QImage&);
public slots:
void CurrentSongChanged(const Song& song, const QString& art_uri,
const QImage&);
signals:
void CapsChange(int);
void TrackChange(const QVariantMap&);
void StatusChange(DBusStatus);
private slots:
private slots:
void PlaylistManagerInitialized();
void EngineStateChanged(Engine::State state);
void ShuffleModeChanged();
void RepeatModeChanged();
private:
private:
Application* app_;
QVariantMap last_metadata_;
};
class Mpris1TrackList : public QObject {
Q_OBJECT
public:
public:
Mpris1TrackList(Application* app, QObject* parent = 0);
int AddTrack(const QString&, bool);
@ -194,13 +187,13 @@ public:
signals:
void TrackListChange(int i);
private slots:
private slots:
void PlaylistChanged(Playlist* playlist);
private:
private:
Application* app_;
};
} // namespace mpris
} // namespace mpris
#endif // MPRIS1_H
#endif // MPRIS1_H

View File

@ -41,22 +41,22 @@
#include <QDBusConnection>
#include <QtConcurrentRun>
QDBusArgument& operator<< (QDBusArgument& arg, const MprisPlaylist& playlist) {
QDBusArgument& operator<<(QDBusArgument& arg, const MprisPlaylist& playlist) {
arg.beginStructure();
arg << playlist.id << playlist.name << playlist.icon;
arg.endStructure();
return arg;
}
const QDBusArgument& operator>> (
const QDBusArgument& arg, MprisPlaylist& playlist) {
const QDBusArgument& operator>>(const QDBusArgument& arg,
MprisPlaylist& playlist) {
arg.beginStructure();
arg >> playlist.id >> playlist.name >> playlist.icon;
arg.endStructure();
return arg;
}
QDBusArgument& operator<< (QDBusArgument& arg, const MaybePlaylist& playlist) {
QDBusArgument& operator<<(QDBusArgument& arg, const MaybePlaylist& playlist) {
arg.beginStructure();
arg << playlist.valid;
arg << playlist.playlist;
@ -64,8 +64,8 @@ QDBusArgument& operator<< (QDBusArgument& arg, const MaybePlaylist& playlist) {
return arg;
}
const QDBusArgument& operator>> (
const QDBusArgument& arg, MaybePlaylist& playlist) {
const QDBusArgument& operator>>(const QDBusArgument& arg,
MaybePlaylist& playlist) {
arg.beginStructure();
arg >> playlist.valid >> playlist.playlist;
arg.endStructure();
@ -79,121 +79,115 @@ const char* Mpris2::kServiceName = "org.mpris.MediaPlayer2.clementine";
const char* Mpris2::kFreedesktopPath = "org.freedesktop.DBus.Properties";
Mpris2::Mpris2(Application* app, Mpris1* mpris1, QObject* parent)
: QObject(parent),
app_(app),
mpris1_(mpris1)
{
: QObject(parent), app_(app), mpris1_(mpris1) {
new Mpris2Root(this);
new Mpris2TrackList(this);
new Mpris2Player(this);
new Mpris2Playlists(this);
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;
}
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(Seeked(qlonglong)), SIGNAL(Seeked(qlonglong)));
connect(app_->playlist_manager(), SIGNAL(PlaylistManagerInitialized()), SLOT(PlaylistManagerInitialized()));
connect(app_->playlist_manager(), SIGNAL(CurrentSongChanged(Song)), SLOT(CurrentSongChanged(Song)));
connect(app_->playlist_manager(), SIGNAL(PlaylistChanged(Playlist*)), SLOT(PlaylistChanged(Playlist*)));
connect(app_->playlist_manager(), SIGNAL(CurrentChanged(Playlist*)), SLOT(PlaylistCollectionChanged(Playlist*)));
connect(app_->playlist_manager(), SIGNAL(PlaylistManagerInitialized()),
SLOT(PlaylistManagerInitialized()));
connect(app_->playlist_manager(), SIGNAL(CurrentSongChanged(Song)),
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
void Mpris2::PlaylistManagerInitialized() {
connect(app_->playlist_manager()->sequence(), SIGNAL(ShuffleModeChanged(PlaylistSequence::ShuffleMode)),
connect(app_->playlist_manager()->sequence(),
SIGNAL(ShuffleModeChanged(PlaylistSequence::ShuffleMode)),
SLOT(ShuffleModeChanged()));
connect(app_->playlist_manager()->sequence(), SIGNAL(RepeatModeChanged(PlaylistSequence::RepeatMode)),
connect(app_->playlist_manager()->sequence(),
SIGNAL(RepeatModeChanged(PlaylistSequence::RepeatMode)),
SLOT(RepeatModeChanged()));
}
void Mpris2::EngineStateChanged(Engine::State newState) {
if(newState != Engine::Playing && newState != Engine::Paused) {
last_metadata_= QVariantMap();
if (newState != Engine::Playing && newState != Engine::Paused) {
last_metadata_ = QVariantMap();
EmitNotification("Metadata");
}
EmitNotification("PlaybackStatus", PlaybackStatus(newState));
}
void Mpris2::VolumeChanged() {
EmitNotification("Volume");
}
void Mpris2::VolumeChanged() { EmitNotification("Volume"); }
void Mpris2::ShuffleModeChanged() {
EmitNotification("Shuffle");
}
void Mpris2::ShuffleModeChanged() { EmitNotification("Shuffle"); }
void Mpris2::RepeatModeChanged() {
EmitNotification("LoopStatus");
}
void Mpris2::RepeatModeChanged() { EmitNotification("LoopStatus"); }
void Mpris2::EmitNotification(const QString& name, const QVariant& val) {
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(
kMprisObjectPath, kFreedesktopPath, "PropertiesChanged");
kMprisObjectPath, kFreedesktopPath, "PropertiesChanged");
QVariantMap map;
map.insert(name, val);
QVariantList args = QVariantList()
<< mprisEntity
<< map
<< QStringList();
QVariantList args = QVariantList() << mprisEntity << map << QStringList();
msg.setArguments(args);
QDBusConnection::sessionBus().send(msg);
}
void Mpris2::EmitNotification(const QString& name) {
QVariant value;
if (name == "PlaybackStatus") value = PlaybackStatus();
else if (name == "LoopStatus") value = LoopStatus();
else if (name == "Shuffle") value = Shuffle();
else if (name == "Metadata") value = Metadata();
else if (name == "Volume") value = Volume();
else if (name == "Position") value = Position();
if (name == "PlaybackStatus")
value = PlaybackStatus();
else if (name == "LoopStatus")
value = LoopStatus();
else if (name == "Shuffle")
value = Shuffle();
else if (name == "Metadata")
value = Metadata();
else if (name == "Volume")
value = Volume();
else if (name == "Position")
value = Position();
if (value.isValid())
EmitNotification(name, value);
if (value.isValid()) EmitNotification(name, value);
}
//------------------Root Interface--------------------------//
bool Mpris2::CanQuit() const {
return true;
}
bool Mpris2::CanQuit() const { return true; }
bool Mpris2::CanRaise() const {
return true;
}
bool Mpris2::CanRaise() const { return true; }
bool Mpris2::HasTrackList() const {
return true;
}
bool Mpris2::HasTrackList() const { return true; }
QString Mpris2::Identity() const {
return QCoreApplication::applicationName();
}
QString Mpris2::Identity() const { return QCoreApplication::applicationName(); }
QString Mpris2::DesktopEntryAbsolutePath() const {
QStringList xdg_data_dirs = QString(getenv("XDG_DATA_DIRS")).split(":");
xdg_data_dirs.append("/usr/local/share/");
xdg_data_dirs.append("/usr/share/");
foreach (const QString& directory, xdg_data_dirs) {
QString path = QString("%1/applications/%2.desktop").
arg(directory, QApplication::applicationName().toLower());
if (QFile::exists(path))
return path;
foreach(const QString & directory, xdg_data_dirs) {
QString path = QString("%1/applications/%2.desktop").arg(
directory, QApplication::applicationName().toLower());
if (QFile::exists(path)) return path;
}
return QString();
}
@ -203,52 +197,46 @@ QString Mpris2::DesktopEntry() const {
}
QStringList Mpris2::SupportedUriSchemes() const {
static QStringList res = QStringList()
<< "file"
<< "http"
<< "cdda"
<< "smb"
<< "sftp";
static QStringList res = QStringList() << "file"
<< "http"
<< "cdda"
<< "smb"
<< "sftp";
return res;
}
QStringList Mpris2::SupportedMimeTypes() const {
static QStringList res = QStringList()
<< "application/ogg"
<< "application/x-ogg"
<< "application/x-ogm-audio"
<< "audio/aac"
<< "audio/mp4"
<< "audio/mpeg"
<< "audio/mpegurl"
<< "audio/ogg"
<< "audio/vnd.rn-realaudio"
<< "audio/vorbis"
<< "audio/x-flac"
<< "audio/x-mp3"
<< "audio/x-mpeg"
<< "audio/x-mpegurl"
<< "audio/x-ms-wma"
<< "audio/x-musepack"
<< "audio/x-oggflac"
<< "audio/x-pn-realaudio"
<< "audio/x-scpls"
<< "audio/x-speex"
<< "audio/x-vorbis"
<< "audio/x-vorbis+ogg"
<< "audio/x-wav"
<< "video/x-ms-asf"
<< "x-content/audio-player";
static QStringList res = QStringList() << "application/ogg"
<< "application/x-ogg"
<< "application/x-ogm-audio"
<< "audio/aac"
<< "audio/mp4"
<< "audio/mpeg"
<< "audio/mpegurl"
<< "audio/ogg"
<< "audio/vnd.rn-realaudio"
<< "audio/vorbis"
<< "audio/x-flac"
<< "audio/x-mp3"
<< "audio/x-mpeg"
<< "audio/x-mpegurl"
<< "audio/x-ms-wma"
<< "audio/x-musepack"
<< "audio/x-oggflac"
<< "audio/x-pn-realaudio"
<< "audio/x-scpls"
<< "audio/x-speex"
<< "audio/x-vorbis"
<< "audio/x-vorbis+ogg"
<< "audio/x-wav"
<< "video/x-ms-asf"
<< "x-content/audio-player";
return res;
}
void Mpris2::Raise() {
emit RaiseMainWindow();
}
void Mpris2::Raise() { emit RaiseMainWindow(); }
void Mpris2::Quit() {
qApp->quit();
}
void Mpris2::Quit() { qApp->quit(); }
QString Mpris2::PlaybackStatus() const {
return PlaybackStatus(app_->player()->GetState());
@ -256,9 +244,12 @@ QString Mpris2::PlaybackStatus() const {
QString Mpris2::PlaybackStatus(Engine::State state) const {
switch (state) {
case Engine::Playing: return "Playing";
case Engine::Paused: return "Paused";
default: return "Stopped";
case Engine::Playing:
return "Playing";
case Engine::Paused:
return "Paused";
default:
return "Stopped";
}
}
@ -269,9 +260,12 @@ QString Mpris2::LoopStatus() const {
switch (app_->playlist_manager()->sequence()->repeat_mode()) {
case PlaylistSequence::Repeat_Album:
case PlaylistSequence::Repeat_Playlist: return "Playlist";
case PlaylistSequence::Repeat_Track: return "Track";
default: return "None";
case PlaylistSequence::Repeat_Playlist:
return "Playlist";
case PlaylistSequence::Repeat_Track:
return "Track";
default:
return "None";
}
}
@ -289,12 +283,10 @@ void Mpris2::SetLoopStatus(const QString& value) {
app_->playlist_manager()->active()->sequence()->SetRepeatMode(mode);
}
double Mpris2::Rate() const {
return 1.0;
}
double Mpris2::Rate() const { return 1.0; }
void Mpris2::SetRate(double rate) {
if(rate == 0) {
if (rate == 0) {
if (mpris1_->player()) {
mpris1_->player()->Pause();
}
@ -315,24 +307,20 @@ void Mpris2::SetShuffle(bool value) {
}
}
QVariantMap Mpris2::Metadata() const {
return last_metadata_;
}
QVariantMap Mpris2::Metadata() const { return last_metadata_; }
QString Mpris2::current_track_id() const {
if (!mpris1_->tracklist()) {
return QString();
}
return QString("/org/mpris/MediaPlayer2/Track/%1").arg(
QString::number(mpris1_->tracklist()->GetCurrentTrack()));
return QString("/org/mpris/MediaPlayer2/Track/%1")
.arg(QString::number(mpris1_->tracklist()->GetCurrentTrack()));
}
// We send Metadata change notification as soon as the process of
// changing song starts...
void Mpris2::CurrentSongChanged(const Song& song) {
ArtLoaded(song, "");
}
void Mpris2::CurrentSongChanged(const Song& song) { ArtLoaded(song, ""); }
// ... and we add the cover information later, when it's available.
void Mpris2::ArtLoaded(const Song& song, const QString& art_uri) {
@ -363,21 +351,15 @@ double Mpris2::Volume() const {
}
}
void Mpris2::SetVolume(double value) {
app_->player()->SetVolume(value * 100);
}
void Mpris2::SetVolume(double value) { app_->player()->SetVolume(value * 100); }
qlonglong Mpris2::Position() const {
return app_->player()->engine()->position_nanosec() / kNsecPerUsec;
}
double Mpris2::MaximumRate() const {
return 1.0;
}
double Mpris2::MaximumRate() const { return 1.0; }
double Mpris2::MinimumRate() const {
return 1.0;
}
double Mpris2::MinimumRate() const { return 1.0; }
bool Mpris2::CanGoNext() const {
if (mpris1_->player()) {
@ -395,17 +377,14 @@ bool Mpris2::CanGoPrevious() const {
}
}
bool Mpris2::CanPlay() const {
return mpris1_->player()->GetCaps() & CAN_PLAY;
}
bool Mpris2::CanPlay() const { return mpris1_->player()->GetCaps() & CAN_PLAY; }
// This one's a bit different than MPRIS 1 - we want this to be true even when
// the song is already paused or stopped.
bool Mpris2::CanPause() const {
if (mpris1_->player()) {
return mpris1_->player()->GetCaps() & CAN_PAUSE
|| PlaybackStatus() == "Paused"
|| PlaybackStatus() == "Stopped";
return mpris1_->player()->GetCaps() & CAN_PAUSE ||
PlaybackStatus() == "Paused" || PlaybackStatus() == "Stopped";
} else {
return true;
}
@ -419,24 +398,22 @@ bool Mpris2::CanSeek() const {
}
}
bool Mpris2::CanControl() const {
return true;
}
bool Mpris2::CanControl() const { return true; }
void Mpris2::Next() {
if(CanGoNext()) {
if (CanGoNext()) {
app_->player()->Next();
}
}
void Mpris2::Previous() {
if(CanGoPrevious()) {
if (CanGoPrevious()) {
app_->player()->Previous();
}
}
void Mpris2::Pause() {
if(CanPause() && app_->player()->GetState() != Engine::Paused) {
if (CanPause() && app_->player()->GetState() != Engine::Paused) {
app_->player()->Pause();
}
}
@ -447,20 +424,19 @@ void Mpris2::PlayPause() {
}
}
void Mpris2::Stop() {
app_->player()->Stop();
}
void Mpris2::Stop() { app_->player()->Stop(); }
void Mpris2::Play() {
if(CanPlay()) {
if (CanPlay()) {
app_->player()->Play();
}
}
void Mpris2::Seek(qlonglong offset) {
if(CanSeek()) {
app_->player()->SeekTo(app_->player()->engine()->position_nanosec() / kNsecPerSec +
offset / kUsecPerSec);
if (CanSeek()) {
app_->player()->SeekTo(app_->player()->engine()->position_nanosec() /
kNsecPerSec +
offset / kUsecPerSec);
}
}
@ -468,7 +444,8 @@ void Mpris2::SetPosition(const QDBusObjectPath& trackId, qlonglong offset) {
if (CanSeek() && trackId.path() == current_track_id() && offset >= 0) {
offset *= kNsecPerUsec;
if(offset < app_->player()->GetCurrentItem()->Metadata().length_nanosec()) {
if (offset <
app_->player()->GetCurrentItem()->Metadata().length_nanosec()) {
app_->player()->SeekTo(offset / kNsecPerSec);
}
}
@ -481,46 +458,42 @@ void Mpris2::OpenUri(const QString& uri) {
}
TrackIds Mpris2::Tracks() const {
//TODO
// TODO
return TrackIds();
}
bool Mpris2::CanEditTracks() const {
return false;
}
bool Mpris2::CanEditTracks() const { return false; }
TrackMetadata Mpris2::GetTracksMetadata(const TrackIds &tracks) const {
//TODO
TrackMetadata Mpris2::GetTracksMetadata(const TrackIds& tracks) const {
// TODO
return TrackMetadata();
}
void Mpris2::AddTrack(const QString &uri, const QDBusObjectPath &afterTrack, bool setAsCurrent) {
//TODO
void Mpris2::AddTrack(const QString& uri, const QDBusObjectPath& afterTrack,
bool setAsCurrent) {
// TODO
}
void Mpris2::RemoveTrack(const QDBusObjectPath &trackId) {
//TODO
void Mpris2::RemoveTrack(const QDBusObjectPath& trackId) {
// TODO
}
void Mpris2::GoTo(const QDBusObjectPath &trackId) {
//TODO
void Mpris2::GoTo(const QDBusObjectPath& trackId) {
// TODO
}
quint32 Mpris2::PlaylistCount() const {
return app_->playlist_manager()->GetAllPlaylists().size();
}
QStringList Mpris2::Orderings() const {
return QStringList() << "User";
}
QStringList Mpris2::Orderings() const { return QStringList() << "User"; }
namespace {
QDBusObjectPath MakePlaylistPath(int id) {
return QDBusObjectPath(QString(
"/org/mpris/MediaPlayer2/Playlists/%1").arg(id));
return QDBusObjectPath(
QString("/org/mpris/MediaPlayer2/Playlists/%1").arg(id));
}
}
MaybePlaylist Mpris2::ActivePlaylist() const {
@ -557,10 +530,11 @@ void Mpris2::ActivatePlaylist(const QDBusObjectPath& playlist_id) {
}
// TODO: Support sort orders.
MprisPlaylistList Mpris2::GetPlaylists(
quint32 index, quint32 max_count, const QString& order, bool reverse_order) {
MprisPlaylistList Mpris2::GetPlaylists(quint32 index, quint32 max_count,
const QString& order,
bool reverse_order) {
MprisPlaylistList ret;
foreach (Playlist* p, app_->playlist_manager()->GetAllPlaylists()) {
foreach(Playlist * p, app_->playlist_manager()->GetAllPlaylists()) {
MprisPlaylist mpris_playlist;
mpris_playlist.id = MakePlaylistPath(p->id());
mpris_playlist.name = app_->playlist_manager()->GetPlaylistName(p->id());
@ -577,7 +551,8 @@ MprisPlaylistList Mpris2::GetPlaylists(
void Mpris2::PlaylistChanged(Playlist* playlist) {
MprisPlaylist mpris_playlist;
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);
}
@ -585,4 +560,4 @@ void Mpris2::PlaylistCollectionChanged(Playlist* playlist) {
EmitNotification("PlaylistCount", "", "org.mpris.MediaPlayer2.Playlists");
}
} // namespace mpris
} // namespace mpris

View File

@ -47,14 +47,13 @@ struct MaybePlaylist {
};
Q_DECLARE_METATYPE(MaybePlaylist);
QDBusArgument& operator<< (QDBusArgument& arg, const MprisPlaylist& playlist);
const QDBusArgument& operator>> (
const QDBusArgument& arg, MprisPlaylist& playlist);
QDBusArgument& operator<< (QDBusArgument& arg, const MaybePlaylist& playlist);
const QDBusArgument& operator>> (
const QDBusArgument& arg, MaybePlaylist& playlist);
QDBusArgument& operator<<(QDBusArgument& arg, const MprisPlaylist& playlist);
const QDBusArgument& operator>>(const QDBusArgument& arg,
MprisPlaylist& playlist);
QDBusArgument& operator<<(QDBusArgument& arg, const MaybePlaylist& playlist);
const QDBusArgument& operator>>(const QDBusArgument& arg,
MaybePlaylist& playlist);
namespace mpris {
@ -64,44 +63,44 @@ class Mpris2 : public QObject {
Q_OBJECT
public:
//org.mpris.MediaPlayer2 MPRIS 2.0 Root interface
Q_PROPERTY( bool CanQuit READ CanQuit )
Q_PROPERTY( bool CanRaise READ CanRaise )
Q_PROPERTY( bool HasTrackList READ HasTrackList )
Q_PROPERTY( QString Identity READ Identity )
Q_PROPERTY( QString DesktopEntry READ DesktopEntry )
Q_PROPERTY( QStringList SupportedUriSchemes READ SupportedUriSchemes )
Q_PROPERTY( QStringList SupportedMimeTypes READ SupportedMimeTypes )
// org.mpris.MediaPlayer2 MPRIS 2.0 Root interface
Q_PROPERTY(bool CanQuit READ CanQuit)
Q_PROPERTY(bool CanRaise READ CanRaise)
Q_PROPERTY(bool HasTrackList READ HasTrackList)
Q_PROPERTY(QString Identity READ Identity)
Q_PROPERTY(QString DesktopEntry READ DesktopEntry)
Q_PROPERTY(QStringList SupportedUriSchemes READ SupportedUriSchemes)
Q_PROPERTY(QStringList SupportedMimeTypes READ SupportedMimeTypes)
//org.mpris.MediaPlayer2 MPRIS 2.2 Root interface
Q_PROPERTY( bool CanSetFullscreen READ CanSetFullscreen )
Q_PROPERTY( bool Fullscreen READ Fullscreen WRITE SetFullscreen )
// org.mpris.MediaPlayer2 MPRIS 2.2 Root interface
Q_PROPERTY(bool CanSetFullscreen READ CanSetFullscreen)
Q_PROPERTY(bool Fullscreen READ Fullscreen WRITE SetFullscreen)
//org.mpris.MediaPlayer2.Player MPRIS 2.0 Player interface
Q_PROPERTY( QString PlaybackStatus READ PlaybackStatus )
Q_PROPERTY( QString LoopStatus READ LoopStatus WRITE SetLoopStatus )
Q_PROPERTY( double Rate READ Rate WRITE SetRate )
Q_PROPERTY( bool Shuffle READ Shuffle WRITE SetShuffle )
Q_PROPERTY( QVariantMap Metadata READ Metadata )
Q_PROPERTY( double Volume READ Volume WRITE SetVolume )
Q_PROPERTY( qlonglong Position READ Position )
Q_PROPERTY( double MinimumRate READ MinimumRate )
Q_PROPERTY( double MaximumRate READ MaximumRate )
Q_PROPERTY( bool CanGoNext READ CanGoNext )
Q_PROPERTY( bool CanGoPrevious READ CanGoPrevious )
Q_PROPERTY( bool CanPlay READ CanPlay )
Q_PROPERTY( bool CanPause READ CanPause )
Q_PROPERTY( bool CanSeek READ CanSeek )
Q_PROPERTY( bool CanControl READ CanControl )
// org.mpris.MediaPlayer2.Player MPRIS 2.0 Player interface
Q_PROPERTY(QString PlaybackStatus READ PlaybackStatus)
Q_PROPERTY(QString LoopStatus READ LoopStatus WRITE SetLoopStatus)
Q_PROPERTY(double Rate READ Rate WRITE SetRate)
Q_PROPERTY(bool Shuffle READ Shuffle WRITE SetShuffle)
Q_PROPERTY(QVariantMap Metadata READ Metadata)
Q_PROPERTY(double Volume READ Volume WRITE SetVolume)
Q_PROPERTY(qlonglong Position READ Position)
Q_PROPERTY(double MinimumRate READ MinimumRate)
Q_PROPERTY(double MaximumRate READ MaximumRate)
Q_PROPERTY(bool CanGoNext READ CanGoNext)
Q_PROPERTY(bool CanGoPrevious READ CanGoPrevious)
Q_PROPERTY(bool CanPlay READ CanPlay)
Q_PROPERTY(bool CanPause READ CanPause)
Q_PROPERTY(bool CanSeek READ CanSeek)
Q_PROPERTY(bool CanControl READ CanControl)
//org.mpris.MediaPlayer2.TrackList MPRIS 2.0 Player interface
Q_PROPERTY( TrackIds Tracks READ Tracks )
Q_PROPERTY( bool CanEditTracks READ CanEditTracks )
// org.mpris.MediaPlayer2.TrackList MPRIS 2.0 Player interface
Q_PROPERTY(TrackIds Tracks READ Tracks)
Q_PROPERTY(bool CanEditTracks READ CanEditTracks)
//org.mpris.MediaPlayer2.Playlists MPRIS 2.1 Playlists interface
Q_PROPERTY( quint32 PlaylistCount READ PlaylistCount )
Q_PROPERTY( QStringList Orderings READ Orderings )
Q_PROPERTY( MaybePlaylist ActivePlaylist READ ActivePlaylist )
// org.mpris.MediaPlayer2.Playlists MPRIS 2.1 Playlists interface
Q_PROPERTY(quint32 PlaylistCount READ PlaylistCount)
Q_PROPERTY(QStringList Orderings READ Orderings)
Q_PROPERTY(MaybePlaylist ActivePlaylist READ ActivePlaylist)
Mpris2(Application* app, Mpris1* mpris1, QObject* parent = 0);
@ -161,7 +160,8 @@ class Mpris2 : public QObject {
// Methods
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 GoTo(const QDBusObjectPath& trackId);
@ -172,8 +172,8 @@ class Mpris2 : public QObject {
// Methods
void ActivatePlaylist(const QDBusObjectPath& playlist_id);
QList<MprisPlaylist> GetPlaylists(
quint32 index, quint32 max_count, const QString& order, bool reverse_order);
QList<MprisPlaylist> GetPlaylists(quint32 index, quint32 max_count,
const QString& order, bool reverse_order);
signals:
// Player
@ -183,14 +183,15 @@ signals:
void TrackListReplaced(const TrackIds& Tracks, QDBusObjectPath CurrentTrack);
void TrackAdded(const TrackMetadata& Metadata, QDBusObjectPath AfterTrack);
void TrackRemoved(const QDBusObjectPath& trackId);
void TrackMetadataChanged(const QDBusObjectPath& trackId, const TrackMetadata& metadata);
void TrackMetadataChanged(const QDBusObjectPath& trackId,
const TrackMetadata& metadata);
void RaiseMainWindow();
// Playlist
void PlaylistChanged(const MprisPlaylist& playlist);
private slots:
private slots:
void ArtLoaded(const Song& song, const QString& art_uri);
void EngineStateChanged(Engine::State newState);
void VolumeChanged();
@ -202,10 +203,11 @@ private slots:
void PlaylistChanged(Playlist* playlist);
void PlaylistCollectionChanged(Playlist* playlist);
private:
private:
void EmitNotification(const QString& name);
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;
@ -213,7 +215,7 @@ private:
QString DesktopEntryAbsolutePath() const;
private:
private:
static const char* kMprisObjectPath;
static const char* kServiceName;
static const char* kFreedesktopPath;
@ -224,6 +226,6 @@ private:
Mpris1* mpris1_;
};
} // namespace mpris
} // namespace mpris
#endif

View File

@ -25,34 +25,37 @@
namespace mpris {
inline void AddMetadata(const QString& key, const QString& metadata, QVariantMap* map) {
if (!metadata.isEmpty()) (*map)[key] = metadata;
inline void AddMetadata(const QString& key, const QString& metadata,
QVariantMap* map) {
if (!metadata.isEmpty()) (*map)[key] = metadata;
}
inline void AddMetadataAsList(const QString& key, const QString& metadata, QVariantMap* map) {
if (!metadata.isEmpty()) (*map)[key] = QStringList() << metadata;
inline void AddMetadataAsList(const QString& key, const QString& metadata,
QVariantMap* map) {
if (!metadata.isEmpty()) (*map)[key] = QStringList() << metadata;
}
inline void AddMetadata(const QString& key, int metadata, QVariantMap* map) {
if (metadata > 0) (*map)[key] = metadata;
if (metadata > 0) (*map)[key] = metadata;
}
inline void AddMetadata(const QString& key, qint64 metadata, QVariantMap* map) {
if (metadata > 0) (*map)[key] = metadata;
if (metadata > 0) (*map)[key] = metadata;
}
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) {
if (metadata.isValid()) (*map)[key] = metadata;
inline void AddMetadata(const QString& key, const QDateTime& metadata,
QVariantMap* map) {
if (metadata.isValid()) (*map)[key] = metadata;
}
inline QString AsMPRISDateTimeType(uint time) {
return time != -1 ? QDateTime::fromTime_t(time).toString(Qt::ISODate) : "";
}
} // namespace mpris
} // namespace mpris
#endif // MPRIS_COMMON_H
#endif // MPRIS_COMMON_H

View File

@ -6,9 +6,7 @@
#include <QTime>
MultiSortFilterProxy::MultiSortFilterProxy(QObject* parent)
: QSortFilterProxyModel(parent)
{
}
: QSortFilterProxyModel(parent) {}
void MultiSortFilterProxy::AddSortSpec(int role, Qt::SortOrder order) {
sorting_ << SortSpec(role, order);
@ -16,7 +14,7 @@ void MultiSortFilterProxy::AddSortSpec(int role, Qt::SortOrder order) {
bool MultiSortFilterProxy::lessThan(const QModelIndex& left,
const QModelIndex& right) const {
foreach (const SortSpec& spec, sorting_) {
foreach(const SortSpec & spec, sorting_) {
const int ret = Compare(left.data(spec.first), right.data(spec.first));
if (ret < 0) {
@ -31,29 +29,38 @@ bool MultiSortFilterProxy::lessThan(const QModelIndex& left,
template <typename T>
static inline int DoCompare(T left, T right) {
if (left < right)
return -1;
if (left > right)
return 1;
if (left < right) return -1;
if (left > right) return 1;
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
// -1, 0 or 1 instead of true or false.
switch (left.userType()) {
case QVariant::Invalid:
return (right.type() != QVariant::Invalid) ? -1 : 0;
case QVariant::Int: return DoCompare(left.toInt(), right.toInt());
case QVariant::UInt: return DoCompare(left.toUInt(), right.toUInt());
case QVariant::LongLong: return DoCompare(left.toLongLong(), right.toLongLong());
case QVariant::ULongLong: return DoCompare(left.toULongLong(), right.toULongLong());
case QMetaType::Float: 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::Int:
return DoCompare(left.toInt(), right.toInt());
case QVariant::UInt:
return DoCompare(left.toUInt(), right.toUInt());
case QVariant::LongLong:
return DoCompare(left.toLongLong(), right.toLongLong());
case QVariant::ULongLong:
return DoCompare(left.toULongLong(), right.toULongLong());
case QMetaType::Float:
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:
default:
if (isSortLocaleAware())

View File

@ -4,19 +4,19 @@
#include <QSortFilterProxyModel>
class MultiSortFilterProxy : public QSortFilterProxyModel {
public:
public:
MultiSortFilterProxy(QObject* parent = NULL);
void AddSortSpec(int role, Qt::SortOrder order = Qt::AscendingOrder);
protected:
protected:
bool lessThan(const QModelIndex& left, const QModelIndex& right) const;
private:
private:
int Compare(const QVariant& left, const QVariant& right) const;
typedef QPair<int, Qt::SortOrder> SortSpec;
QList<SortSpec> sorting_;
};
#endif // MULTISORTFILTERPROXY_H
#endif // MULTISORTFILTERPROXY_H

Some files were not shown because too many files have changed in this diff Show More