-
-
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
Intro/Outro cues and silence detection #1242
Changes from 1 commit
99994dc
871474b
b118d49
555911a
bed7843
6587080
54cc99f
0f583b9
b8ecf19
095dd81
a5b22b5
90f2c81
d5385e5
aee31e0
6eaa83d
49888d7
666068d
59f19eb
20f8796
d0f1d90
92b79f1
f95c646
4889fa2
87a3e04
8c01dd0
416bc08
f971053
3ebfdc2
4d27845
43c6441
4472462
13ffd54
64082c0
784c9f8
259a169
83766c0
44beb4c
8dacede
e7fb679
e09f369
865e454
221b5a4
6a41779
7dab7fd
3ab12a3
98828b9
227ebe8
ad1bf43
7959376
d5bdb64
c42089c
f5aeeba
fbc60e7
7f16257
ddeee23
24e9099
2b2da0c
35707a4
638cad8
032c1c0
c2916dc
e6a0112
e89a753
d6cfc92
ae26278
4b07a89
84b774c
310e4c5
70f0f32
24f137d
1a2d5c1
fa95569
a61ac72
8a9f716
bd92970
3a6e8dc
02332e7
9250a74
667370a
586d331
3b51ac4
d36ee69
10bb36e
2ab4adb
468017e
fd4d979
6d7d523
a8d66a7
e9923a6
2913cdd
f345515
bd5a5a4
58565ee
2a75438
6f50e4c
86fae61
1ba11bd
6ed59d5
028fc57
cbefc58
da03ecb
21b51f9
39acb51
cbb2eb2
4f29098
fa3855c
b345adc
59d20a8
f1d887f
4ebecca
e827104
425bb20
cc24260
0ddf621
2aa9f99
e69e41a
090e1ca
788c67a
d8d4875
a7cfbd5
e4f0d5a
ce04bfa
ee4ab9e
acc6b4c
014a55f
1341ef1
25a67d9
807c180
7a7e3e5
65dc18e
8cc7d9d
1cd3fcc
55307f7
60d5326
bbf985a
edbbf3c
fcdff0f
eb03b2f
9dbac85
4923a2a
36b4418
57a922d
6b36e82
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 |
---|---|---|
@@ -0,0 +1,104 @@ | ||
#include "analyzer/analyzersilence.h" | ||
|
||
const int kChannelCount = 2; | ||
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 use mixxx::AudioSource::kChannelCountStereo |
||
const float kSilenceThreshold = -60.0f; | ||
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 name it kSilenceThresholdDb to document the unit |
||
|
||
AnalyzerSilence::AnalyzerSilence(UserSettingsPointer pConfig) | ||
: m_pConfig(pConfig), | ||
m_fThreshold(db2ratio(kSilenceThreshold)), | ||
m_iSampleRate(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. unused, please remove m_iSampleRate and m_iTotalSamples |
||
m_iTotalSamples(0), | ||
m_iFramesProcessed(0), | ||
m_bPrevSilence(true), | ||
m_iSignalBegin(-1), | ||
m_iSignalEnd(-1) { | ||
} | ||
|
||
AnalyzerSilence::~AnalyzerSilence() { | ||
} | ||
|
||
bool AnalyzerSilence::initialize(TrackPointer tio, int sampleRate, int totalSamples) { | ||
Q_UNUSED(tio); | ||
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 we shouldn't enable the analyzer if the track already has an in/out cue (since the user can manually edit their cues, so we don't know if we are overwriting user data) 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 may consider her to move the cue points if they are finally independent from auto DJ an they are not user editable |
||
|
||
m_iSampleRate = sampleRate; | ||
m_iTotalSamples = totalSamples; | ||
m_iFramesProcessed = 0; | ||
m_bPrevSilence = true; | ||
m_iSignalBegin = 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. I would initialize with -1 to distinguish between not found and 0. |
||
m_iSignalEnd = 0; | ||
|
||
return true; | ||
} | ||
|
||
bool AnalyzerSilence::isDisabledOrLoadStoredSuccess(TrackPointer tio) const { | ||
Q_UNUSED(tio); | ||
|
||
return false; | ||
} | ||
|
||
void AnalyzerSilence::process(const CSAMPLE *pIn, const int iLen) { | ||
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 document your assumptions explicitly: Otherwise your code might read outside the bounds of the provided array! But this is actually a flaw in the Analyzer's design which needs to be migrated to frame indexing. It should always provide complete frames, that's why the assertion is sufficient 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. Regarding coding guidelines: |
||
for (int i = 0; i < iLen; i += kChannelCount) { | ||
daschuer marked this conversation as resolved.
Show resolved
Hide resolved
|
||
bool bSilence = math_max(fabs(pIn[i]), fabs(pIn[i + 1])) < m_fThreshold; | ||
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. Implicit assumption of 2 channels! Please make sure that your code is generic and works with any number of channels in kChannelCount. Otherwise document your assumptions explicitly with an assertion. |
||
|
||
if (m_bPrevSilence && !bSilence) { | ||
if (m_iSignalBegin <= 0) { | ||
m_iSignalBegin = m_iFramesProcessed + i / kChannelCount; | ||
} | ||
} else if (!m_bPrevSilence && bSilence) { | ||
m_iSignalEnd = m_iFramesProcessed + i / kChannelCount; | ||
} | ||
|
||
m_bPrevSilence = bSilence; | ||
} | ||
|
||
m_iFramesProcessed += iLen / kChannelCount; | ||
} | ||
|
||
void AnalyzerSilence::cleanup(TrackPointer tio) { | ||
Q_UNUSED(tio); | ||
} | ||
|
||
void AnalyzerSilence::finalize(TrackPointer tio) { | ||
// If track didn't end with silence, place signal end marker | ||
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. handle m_iSignalBegin < 0 and m_iSignalEnd < 0 explicitly here ? |
||
// on the end of the track. | ||
if (!m_bPrevSilence) { | ||
m_iSignalEnd = m_iFramesProcessed; | ||
} | ||
|
||
bool bBeginPointFoundAndSet = false; | ||
bool bEndPointFoundAndSet = false; | ||
QList<CuePointer> cues = tio->getCuePoints(); | ||
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 think we should edit cues since at the point we create them they become user data and we can't tell if they were edited by the user |
||
foreach (CuePointer pCue, cues) { | ||
if (pCue->getType() == Cue::BEGIN) { | ||
pCue->setHotCue(0); | ||
pCue->setLabel("BEGIN"); | ||
pCue->setLength(0); | ||
pCue->setPosition(kChannelCount * m_iSignalBegin); | ||
bBeginPointFoundAndSet = true; | ||
} else if (pCue->getType() == Cue::END) { | ||
pCue->setHotCue(1); | ||
pCue->setLabel("END"); | ||
pCue->setLength(0); | ||
pCue->setPosition(kChannelCount * m_iSignalEnd); | ||
bEndPointFoundAndSet = true; | ||
} | ||
} | ||
|
||
if (!bBeginPointFoundAndSet) { | ||
CuePointer pCue = tio->createAndAddCue(); | ||
pCue->setType(Cue::BEGIN); | ||
pCue->setHotCue(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. please don't set a hotcue since it might conflict with other hotcues |
||
pCue->setLabel("BEGIN"); | ||
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. label is meant for user tagging / notes to themselves, so I wouldn't set the label |
||
pCue->setLength(0); | ||
pCue->setPosition(kChannelCount * m_iSignalBegin); | ||
} | ||
|
||
if (!bEndPointFoundAndSet) { | ||
CuePointer pCue = tio->createAndAddCue(); | ||
pCue->setType(Cue::END); | ||
pCue->setHotCue(1); | ||
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 don't set a hotcue since it might conflict with other hotcues |
||
pCue->setLabel("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. label is meant for user tagging / notes to themselves, so I wouldn't set the label |
||
pCue->setLength(0); | ||
pCue->setPosition(kChannelCount * m_iSignalEnd); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
#ifndef ANALYZER_ANALYZERSILENCE_H | ||
#define ANALYZER_ANALYZERSILENCE_H | ||
|
||
#include "analyzer/analyzer.h" | ||
#include "preferences/usersettings.h" | ||
|
||
class AnalyzerSilence : public Analyzer { | ||
public: | ||
AnalyzerSilence(UserSettingsPointer pConfig); | ||
virtual ~AnalyzerSilence(); | ||
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 mark the destructor override too |
||
|
||
bool initialize(TrackPointer tio, int sampleRate, int totalSamples) override; | ||
bool isDisabledOrLoadStoredSuccess(TrackPointer tio) const override; | ||
void process(const CSAMPLE *pIn, const int iLen) override; | ||
void finalize(TrackPointer tio) override; | ||
void cleanup(TrackPointer tio) override; | ||
|
||
private: | ||
UserSettingsPointer m_pConfig; | ||
float m_fThreshold; | ||
int m_iSampleRate; | ||
int m_iTotalSamples; | ||
int m_iFramesProcessed; | ||
bool m_bPrevSilence; | ||
int m_iSignalBegin; | ||
int m_iSignalEnd; | ||
}; | ||
|
||
#endif // ANALYZER_ANALYZERSILENCE_H |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -102,6 +102,9 @@ CueControl::CueControl(QString group, | |
m_pCueIndicator = new ControlIndicator(ConfigKey(group, "cue_indicator")); | ||
m_pPlayIndicator = new ControlIndicator(ConfigKey(group, "play_indicator")); | ||
|
||
m_pSignalBeginPosition = new ControlObject(ConfigKey(group, "signal_begin_position")); | ||
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. along with renaming the cue type, please call these fade_in_cue_point or similar? |
||
m_pSignalEndPosition = new ControlObject(ConfigKey(group, "signal_end_position")); | ||
|
||
m_pVinylControlEnabled = new ControlProxy(group, "vinylcontrol_enabled"); | ||
m_pVinylControlMode = new ControlProxy(group, "vinylcontrol_mode"); | ||
} | ||
|
@@ -120,6 +123,8 @@ CueControl::~CueControl() { | |
delete m_pPlayStutter; | ||
delete m_pCueIndicator; | ||
delete m_pPlayIndicator; | ||
delete m_pSignalBeginPosition; | ||
delete m_pSignalEndPosition; | ||
delete m_pVinylControlEnabled; | ||
delete m_pVinylControlMode; | ||
qDeleteAll(m_hotcueControls); | ||
|
@@ -198,6 +203,8 @@ void CueControl::trackLoaded(TrackPointer pNewTrack, TrackPointer pOldTrack) { | |
|
||
m_pCueIndicator->setBlinkValue(ControlIndicator::OFF); | ||
m_pCuePoint->set(-1.0); | ||
m_pSignalBeginPosition->set(-1.0); | ||
m_pSignalEndPosition->set(-1.0); | ||
m_pLoadedTrack.reset(); | ||
} | ||
|
||
|
@@ -219,6 +226,12 @@ void CueControl::trackLoaded(TrackPointer pNewTrack, TrackPointer pOldTrack) { | |
DEBUG_ASSERT(!pLoadCue); | ||
pLoadCue = pCue; | ||
} | ||
if (pCue->getType() == Cue::BEGIN) { | ||
m_pSignalBeginPosition->set(pCue->getPosition()); | ||
} | ||
if (pCue->getType() == Cue::END) { | ||
m_pSignalEndPosition->set(pCue->getPosition()); | ||
} | ||
int hotcue = pCue->getHotCue(); | ||
if (hotcue != -1) { | ||
attachCue(pCue, hotcue); | ||
|
@@ -273,6 +286,11 @@ void CueControl::trackCuesUpdated() { | |
QListIterator<CuePointer> it(cuePoints); | ||
while (it.hasNext()) { | ||
CuePointer pCue(it.next()); | ||
if (pCue->getType() == Cue::BEGIN) { | ||
m_pSignalBeginPosition->set(pCue->getPosition()); | ||
} else if (pCue->getType() == Cue::END) { | ||
m_pSignalEndPosition->set(pCue->getPosition()); | ||
} | ||
|
||
if (pCue->getType() != Cue::CUE && pCue->getType() != Cue::LOAD) | ||
continue; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,6 +21,8 @@ class Cue : public QObject { | |
BEAT = 3, | ||
LOOP = 4, | ||
JUMP = 5, | ||
BEGIN = 6, | ||
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 it might make more sense to call these "fade in" and "fade out" cues (or autodj_in / autodj_out), since that's more aligned with the purpose. Tracks may not have a beginning or end, and user's probably don't particularly care about having the beginning and end of the track annotated, but they do care about annotating where AutoDJ would fade in and out. Plus if they edit the cue to be located where they want AutoDJ to fade in, then it doesn't point at the track start anymore so it'd be misnamed. |
||
END = 7, | ||
}; | ||
|
||
~Cue() override; | ||
|
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.
Once we get closer to merging, please remember to update Deere and Shade.