-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[WIP] Livemetadata PR #1675
[WIP] Livemetadata PR #1675
Changes from 11 commits
32cece7
ba829a2
5169748
3d8c501
523d09b
23ef862
0a4a4d6
d203e85
1a6bc0d
ff27795
db2db23
7f55150
121443e
2cd5bbc
b9c4a6c
e10df9e
c1de7db
50019b0
d805306
4e714d5
b3f8ea8
8cd9c16
d4b9aa5
ec98ef6
0209b4a
2d8f4a2
88c4819
ab8b524
97daf77
4bc2de3
49b8b93
567e9b5
5748fa9
3a677b6
9c43d09
2c21183
a6d67d7
ba8a6fc
6b7f207
f7c6a83
1057b06
152925c
ad43628
751f567
64d35c5
ddfb859
e4dc8df
a1ad4c6
ee4c5a5
4571d43
185d2f2
0bc22be
0af2279
6be0042
62eae49
c2317c7
8b5e54a
cac8c1f
6807838
d6880a1
32e565a
1b77c8b
7ba10a7
94ab525
5e8fb60
41bdfb9
0def3e3
9fe5277
97271d8
f4b9649
dde6c47
e1cfd7a
69a3020
7af4dd7
170e6c9
8c6fa58
899f207
4a13665
39dc901
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -57,3 +57,5 @@ lib/*/*.lib | |
lib/*/lib/*.lib | ||
|
||
lib/qtscript-bytearray/*.cc | ||
|
||
*.vscode | ||
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
#include "metadatabroadcast.h" | ||
#include "mixer/playerinfo.h" | ||
|
||
MetadataBroadcast::MetadataBroadcast() { | ||
|
||
} | ||
|
||
void MetadataBroadcast::slotReadyToBeScrobbled(Track *pTrack) { | ||
|
||
} | ||
|
||
void MetadataBroadcast::slotNowListening(Track *pTrack) { | ||
|
||
} | ||
|
||
QLinkedList<TrackPointer> MetadataBroadcast::getTrackedTracks() { | ||
return m_trackedTracks; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
#pragma once | ||
|
||
#include <QObject> | ||
#include <QLinkedList> | ||
#include "track/track.h" | ||
|
||
class MetadataBroadcast : public QObject { | ||
Q_OBJECT | ||
public: | ||
MetadataBroadcast(); | ||
QLinkedList<TrackPointer> getTrackedTracks(); | ||
public slots: | ||
void slotReadyToBeScrobbled(Track *pTrack); | ||
void slotNowListening(Track *pTrack); | ||
private: | ||
QLinkedList<TrackPointer> m_trackedTracks; | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,215 @@ | ||
#include <QObject> | ||
|
||
#include "broadcast/scrobblingmanager.h" | ||
#include "control/controlproxy.h" | ||
#include "engine/enginexfader.h" | ||
#include "mixer/deck.h" | ||
|
||
ScrobblingManager::ScrobblingManager() : | ||
m_CPGuiTick("[Master]", "guiTick50ms",this), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Some style nits:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Okey, thanks. |
||
m_CPCrossfader("[Master]","crossfader", this), | ||
m_CPXFaderCurve(ConfigKey(EngineXfader::kXfaderConfigKey, | ||
"xFaderCurve"),this), | ||
|
||
m_CPXFaderCalibration(ConfigKey(EngineXfader::kXfaderConfigKey, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can't you call the function that calculates the xfader gain instead? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, but I need the correct parameters which are given by those Control objects, aren't they? I copied the code from EngineMaster. |
||
"xFaderCalibration"),this), | ||
|
||
m_CPXFaderMode(ConfigKey(EngineXfader::kXfaderConfigKey, | ||
"xFaderMode"),this), | ||
|
||
m_CPXFaderReverse(ConfigKey(EngineXfader::kXfaderConfigKey, | ||
"xFaderReverse"),this) | ||
{ | ||
m_CPGuiTick.connectValueChanged(SLOT(slotGuiTick(double))); | ||
startTimer(1000); | ||
} | ||
|
||
|
||
void ScrobblingManager::slotTrackPaused(TrackPointer pPausedTrack) { | ||
//Extra functional decision to only track main decks. | ||
Deck *sourceDeck = qobject_cast<Deck*>(sender()); | ||
if (sourceDeck == 0) { | ||
qDebug() << "Didn't load track in a deck yet scrobbling " | ||
"received paused signal."; | ||
return; | ||
} | ||
// QMutexLocker locker(&m_mutex); | ||
bool allPaused = true; | ||
TrackInfo *pausedTrackInfo; | ||
for (TrackInfo *trackInfo : m_trackList) { | ||
if (trackInfo->m_pTrack == pPausedTrack) { | ||
pausedTrackInfo = trackInfo; | ||
for (BaseTrackPlayer *player : trackInfo->m_players) { | ||
BaseTrackPlayerImpl *playerImpl = | ||
qobject_cast<BaseTrackPlayerImpl*>(player); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Avoid upcasting. here it is better to put the required functions as pure virtuals into the base class. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I didn't want to pollute the interface with functions I need in case someone complains. But I will if you're ok. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The readability of the whole code base finally counts. |
||
if (playerImpl == 0) { | ||
qDebug() << "Track player interface isn't a " | ||
"BaseTrackPlayerImpl"; | ||
return; | ||
} | ||
if (!playerImpl->isTrackPaused()) | ||
allPaused = false; | ||
} | ||
break; | ||
} | ||
} | ||
if (allPaused) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this one the line, that is related to the deadlock? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes |
||
pausedTrackInfo->m_trackInfo.pausePlayedTime(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You crash is not a deadlock, it is a Segmentation fault, which happens when you access memory, not allocated by the process. In this case, you access memory via the uninitialized pointer pausedTrackInfo. It is a good idea to always initialize empty pointers with "nullptr". Than you can decorate your code with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wow, how can a seg fault keep all threads in a futex? Wouldn't it kill the process? Thanks for the help. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It actually kills the process here on my Ubuntu Trusty. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Mybe we have an additional deadlock? |
||
} | ||
|
||
void ScrobblingManager::slotTrackResumed(TrackPointer pResumedTrack) { | ||
//Extra functional decision to only track main decks. | ||
Deck *sourceDeck = qobject_cast<Deck*>(sender()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please avoid these kind of implication. It is better to maintain to send the desire informations explicit. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Okey. |
||
if (sourceDeck == 0) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if (!sourceDeck) { or == nullptr There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I saw the specification of qobject_cast and it returns 0, I read the standard and nullptr doesn't have to be 0, same with NULL that is implementation defined. So I went with 0 because that's what the Qt doc says. Though I agree it's more readable. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. They mean the actual register value. This is relevant if you reinterpret a nullptr as integer. It is not guarantied that this is 0. It is more about type safety here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh, ok, then I will use ! |
||
qDebug() << "Didn't load track in a deck yet scrobbling " | ||
"received resumed signal."; | ||
return; | ||
} | ||
if (isTrackAudible(pResumedTrack,sourceDeck)) { | ||
// QMutexLocker locker(&m_mutex); | ||
for (TrackInfo *trackInfo : m_trackList) { | ||
if (trackInfo->m_pTrack == pResumedTrack) { | ||
trackInfo->m_trackInfo.resumePlayedTime(); | ||
break; | ||
} | ||
} | ||
} | ||
} | ||
|
||
void ScrobblingManager::slotLoadingTrack(TrackPointer pNewTrack, TrackPointer pOldTrack) { | ||
//Extra functional decision to only track main decks. | ||
Deck *sourceDeck = qobject_cast<Deck*>(sender()); | ||
if (sourceDeck == 0) { | ||
qDebug() << "Didn't load track in a deck yet scrobbling " | ||
"received loading signal."; | ||
return; | ||
} | ||
if (pOldTrack) { | ||
m_tracksToBeReset.append(TrackToBeReset(pOldTrack,sourceDeck)); | ||
} | ||
} | ||
|
||
void ScrobblingManager::slotNewTrackLoaded(TrackPointer pNewTrack) { | ||
//Empty deck gives a null pointer. | ||
if (!pNewTrack) | ||
return; | ||
//Extra functional decision to only track main decks. | ||
Deck *newDeck = qobject_cast<Deck*>(sender()); | ||
if (newDeck == 0) { | ||
qDebug() << "Didn't load track in a deck yet scrobbling " | ||
"received loaded signal."; | ||
return; | ||
} | ||
// QMutexLocker locker(&m_mutex); | ||
bool trackAlreadyAdded = false; | ||
for (TrackInfo *trackInfo : m_trackList) { | ||
if (trackInfo->m_pTrack == pNewTrack) { | ||
trackInfo->m_players.append(newDeck); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Storing a pointer you not own is always kind of dangerous, because you don't know the live time of it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well, the player manager doesn't destroy the decks until it itself gets destroyed, and this object gets destroyed too. But I will use a group string because shared pointer implies modifying player manager. |
||
trackAlreadyAdded = true; | ||
break; | ||
} | ||
} | ||
if (!trackAlreadyAdded) { | ||
TrackInfo *newTrackInfo = new TrackInfo(pNewTrack,newDeck); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. you can consider to make it a unique pointer. |
||
newTrackInfo->m_players.append(newDeck); | ||
m_trackList.append(newTrackInfo); | ||
connect(&m_trackList.last()->m_trackInfo,SIGNAL(readyToBeScrobbled(TrackPointer)), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do you establish a signal/slot connection between all instances of the internal TrackTimingInfo class and the broadcaster instead of only using a single connection between the ScrobblingManager and the broadcaster? You send the affected track with as the payload data with every signal and the track is bound to each instance of TrackTimingInfo anyway. If you need more context than just the TrackPointer then you could always collect this in a dedicated signal data class. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't see how it is any different, you're just adding one level of indirection. In the end, the broadcaster manages the info on the grace period timer, the scrobbling manager doesn't care about it. Could you please explain why you think this is a bad idea? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Simple and robust design? It doesn't make sense to use n connections when 1 is sufficient and all the receiver needs to know about the context is already part of the message. The track info objects are an implementation detail of your class that should not be visible from the outside. The connections make them visible, indirectly. You are also not allowed to delete those objects like you do now while some signals might still be pending. You have to delete them with QObject::deleteLater(). Otherwise the receiver will get a dangling sender pointer. But all this doesn't matter if you lift the connection up to the next level. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Correct me if I'm wrong but if they live on the same thread aren't they under a direct connection? Then there are no pending signals because the slot is called immediately. However, I didn't think about exposing implementation details so you're right, I will call them from the scrobbling manager. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A direct connection is finally a simple callback, Unfortunately, Qt has a lot of boilerplate code to detect that and to make connect and reconnect save in all circumstances. |
||
this,SLOT(slotReadyToBeScrobbled(TrackPointer))); | ||
} | ||
//A new track has been loaded so must unload old one. | ||
resetTracks(); | ||
} | ||
|
||
void ScrobblingManager::slotPlayerEmpty() { | ||
// QMutexLocker locker(&m_mutex); | ||
resetTracks(); | ||
} | ||
|
||
void ScrobblingManager::resetTracks() { | ||
for (TrackToBeReset candidateTrack : m_tracksToBeReset) { | ||
for (TrackInfo *trackInfo : m_trackList) { | ||
if (trackInfo->m_pTrack == candidateTrack.m_pTrack) { | ||
if (!trackInfo->m_players.contains(candidateTrack.m_pPlayer)) { | ||
qDebug() << "Track doesn't contain player" | ||
"yet is requested for deletion."; | ||
return; | ||
} | ||
//Load error, stray from engine buffer. | ||
if (candidateTrack.m_pPlayer->getLoadedTrack() == | ||
candidateTrack.m_pTrack) | ||
break; | ||
QLinkedList<BaseTrackPlayer*>::iterator it = | ||
trackInfo->m_players.begin(); | ||
while (it != trackInfo->m_players.end()) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this an endless loop? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for pointing it out |
||
if (*it == candidateTrack.m_pPlayer) { | ||
trackInfo->m_players.erase(it); | ||
} | ||
} | ||
if (trackInfo->m_players.empty()) { | ||
trackInfo->m_trackInfo.pausePlayedTime(); | ||
trackInfo->m_trackInfo.resetPlayedTime(); | ||
delete trackInfo; | ||
} | ||
break; | ||
} | ||
} | ||
} | ||
} | ||
|
||
bool ScrobblingManager::isTrackAudible(TrackPointer pTrack, BaseTrackPlayer * pPlayer) { | ||
if (pPlayer->getLoadedTrack() != pTrack) { | ||
qDebug() << "Track can't be audible because is not in player"; | ||
return false; | ||
} | ||
return getPlayerVolume(pPlayer) >= 0.20; | ||
} | ||
|
||
double ScrobblingManager::getPlayerVolume(BaseTrackPlayer *pPlayer) { | ||
double finalVolume; | ||
ControlProxy trackPreGain(pPlayer->getGroup(),"pregain",this); | ||
double preGain = trackPreGain.get(); | ||
ControlProxy trackVolume(pPlayer->getGroup(),"volume",this); | ||
double volume = trackVolume.get(); | ||
ControlProxy deckOrientation(pPlayer->getGroup(),"orientation",this); | ||
int orientation = deckOrientation.get(); | ||
|
||
double xFaderLeft,xFaderRight; | ||
|
||
EngineXfader::getXfadeGains(m_CPCrossfader.get(), | ||
m_CPXFaderCurve.get(), | ||
m_CPXFaderCalibration.get(), | ||
m_CPXFaderMode.get(), | ||
m_CPXFaderReverse.toBool(), | ||
&xFaderLeft,&xFaderRight); | ||
finalVolume = preGain * volume; | ||
if (orientation == EngineChannel::LEFT) | ||
finalVolume *= xFaderLeft; | ||
else if (orientation == EngineChannel::RIGHT) | ||
finalVolume *= xFaderRight; | ||
return finalVolume; | ||
} | ||
|
||
void ScrobblingManager::slotGuiTick(double timeSinceLastTick) { | ||
for (TrackInfo *trackInfo : m_trackList) { | ||
trackInfo->m_trackInfo.slotGuiTick(timeSinceLastTick); | ||
} | ||
} | ||
|
||
void ScrobblingManager::timerEvent(QTimerEvent *timerEvent) { | ||
for (TrackInfo *trackInfo : m_trackList) { | ||
bool inaudible = true; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please try to avoid naming a boolean variables with a negation names. The name "audible" would work much better here. But the local variable is not needed anyway, the code can be simplified. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll change the name of the variable. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You don't need the variable. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you elaborate? |
||
for (BaseTrackPlayer *player : trackInfo->m_players) { | ||
if (isTrackAudible(trackInfo->m_pTrack,player)) { | ||
inaudible = false; | ||
break; | ||
} | ||
} | ||
if (inaudible) { | ||
trackInfo->m_trackInfo.pausePlayedTime(); | ||
} | ||
} | ||
} | ||
|
||
void ScrobblingManager::slotReadyToBeScrobbled(TrackPointer pTrack) { | ||
qDebug() << "Track ready to be scrobbled"; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
#pragma once | ||
|
||
#include <QObject> | ||
#include <QMutex> | ||
#include <QLinkedList> | ||
|
||
#include "mixer/basetrackplayer.h" | ||
#include "track/track.h" | ||
#include "track/tracktiminginfo.h" | ||
|
||
class BaseTrackPlayer; | ||
|
||
class ScrobblingManager : public QObject { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the name of this class is not self explaining.
Will this become a CurrentTrackManager or CurrentTrackCalculator? @kshitij98: could you reuse portions of this code? Or could you share your rules for a master clock track? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The current scrobble track, metadata streaming and history playlist are all the same, the one given by PlayerInfo.h. This manager takes into account when a track is loaded, paused, etc. and checks if they're audible every second to update the timers, and when they are it sends a scrobble signal. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
Q_OBJECT | ||
public: | ||
ScrobblingManager(); | ||
private: | ||
struct TrackInfo { | ||
TrackPointer m_pTrack; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Storing shared track pointers in a class that exists for an unforeseeable time span, that is manually allocated and deleted and moreover stored as a plain pointer in a list managed by 2 elongate nested for loops concerns me. Remember, without releasing the track pointers you will not only introduce a memory leak, but also prevent that any modifications are written into the database or exported into files. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well the tracks are released as soon as they are ejected from all decks. I don't see how this is a problem since as long as there is a deck with a track the pointer is not released either. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are you sure that they are deleted always and promptly? The code dealing with this lifecycle management is too complex and hard to reason about. That's my concern. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well, I'm reasonably sure, unless there are unforeseen bugs. The problem is if I substitute it by a weak pointer then after the track has been dropped from the decks it will already be deleted, and then I can't query it further to mantain internal state. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IMHO the weak pointer idea is not too bad. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I can add weak pointers and then pass all the info by value to the metadata broadcaster. |
||
TrackTimingInfo m_trackInfo; | ||
QLinkedList<BaseTrackPlayer*> m_players; | ||
TrackInfo(TrackPointer pTrack, BaseTrackPlayer *player) : | ||
m_pTrack(pTrack), m_trackInfo(pTrack) | ||
{} | ||
}; | ||
struct TrackToBeReset { | ||
TrackPointer m_pTrack; | ||
BaseTrackPlayer *m_pPlayer; | ||
TrackToBeReset(TrackPointer pTrack, BaseTrackPlayer *player) : | ||
m_pTrack(pTrack),m_pPlayer(player) {} | ||
}; | ||
QMutex m_mutex; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do you need this mutex? What exactly are the shared resources it protects and who are the contestants? I only see some slots at the interface, which is good decision. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As far as I know, each deck can request a track to the engine buffer independently with its own thread. And each thread signals independently. I'm not sure about this since I haven't explored in depth the engine. However, if that's the case, I need to protect the list of tracks from concurrent access, don't I? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Your slots will only be invoked by your object's event loop thread. It doesn't matter if the signal is sent from another thread. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm now that you mention it, if the decks are owned by the PlayerManager, and the manager owns the ScrobblingManager, they all live in the same thread don't they? I guess I don't need a mutex then. But I need to make absolutely sure because I don't want to find data race conditions further down the road which can be a pain in the ass to detect. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We will finally have threads, accessing this Infos. If they will be qt threads, receiving a singnal, we can get arround the issue. |
||
QLinkedList<TrackInfo*> m_trackList; | ||
QLinkedList<TrackToBeReset> m_tracksToBeReset; | ||
ControlProxy m_CPGuiTick; | ||
ControlProxy m_CPCrossfader; | ||
ControlProxy m_CPXFaderCurve; | ||
ControlProxy m_CPXFaderCalibration; | ||
ControlProxy m_CPXFaderMode; | ||
ControlProxy m_CPXFaderReverse; | ||
|
||
void resetTracks(); | ||
bool isTrackAudible(TrackPointer pTrack, BaseTrackPlayer * pPlayer); | ||
double getPlayerVolume(BaseTrackPlayer *pPlayer); | ||
protected: | ||
void timerEvent(QTimerEvent *timerEvent) override; | ||
public slots: | ||
void slotTrackPaused(TrackPointer pPausedTrack); | ||
void slotTrackResumed(TrackPointer pResumedTrack); | ||
void slotLoadingTrack(TrackPointer pNewTrack, TrackPointer pOldTrack); | ||
void slotNewTrackLoaded(TrackPointer pNewTrack); | ||
void slotPlayerEmpty(); | ||
private slots: | ||
void slotGuiTick(double timeSinceLastTick); | ||
void slotReadyToBeScrobbled(TrackPointer pTrack); | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This file is not created by Mixxx or its build system. Adding patterns for every conceivable editor/IDE to this project's gitignore is neither desirable nor feasible. You should add it to your global gitignore instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree, we should stop adding these editor generated files to our shared .gitignore and remove the ones that are already there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was already mentioned in that PR ages ago. I can't seem to find it.