diff options
author | Jef <jef@targetspot.com> | 2024-09-24 08:54:57 -0400 |
---|---|---|
committer | Jef <jef@targetspot.com> | 2024-09-24 08:54:57 -0400 |
commit | 20d28e80a5c861a9d5f449ea911ab75b4f37ad0d (patch) | |
tree | 12f17f78986871dd2cfb0a56e5e93b545c1ae0d0 /Src/Plugins/Visualization/vis_avs/bpm.cpp | |
parent | 537bcbc86291b32fc04ae4133ce4d7cac8ebe9a7 (diff) | |
download | winamp-20d28e80a5c861a9d5f449ea911ab75b4f37ad0d.tar.gz |
Initial community commit
Diffstat (limited to 'Src/Plugins/Visualization/vis_avs/bpm.cpp')
-rw-r--r-- | Src/Plugins/Visualization/vis_avs/bpm.cpp | 711 |
1 files changed, 711 insertions, 0 deletions
diff --git a/Src/Plugins/Visualization/vis_avs/bpm.cpp b/Src/Plugins/Visualization/vis_avs/bpm.cpp new file mode 100644 index 00000000..1ab80f79 --- /dev/null +++ b/Src/Plugins/Visualization/vis_avs/bpm.cpp @@ -0,0 +1,711 @@ +/* + LICENSE + ------- +Copyright 2005 Nullsoft, Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + * Neither the name of Nullsoft nor the names of its contributors may be used to + endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ +#include <windows.h> +#include <commctrl.h> +#include <math.h> +#include <stdio.h> +#include "draw.h" +#include "wnd.h" +#include "r_defs.h" +#include "render.h" +#include "vis.h" +#include "cfgwnd.h" +#include "resource.h" +#include "bpm.h" +#include "../Agave/Language/api_language.h" + +int refineBeat(int isBeat); +BOOL TCHistStep(BeatType *t, DWORD _Avg, int *_halfDiscriminated, int *_hdPos, DWORD *_lastTC, DWORD TC, int Type); +void InsertHistStep(BeatType *t, DWORD TC, int Type, int i); +void CalcBPM(void); +BOOL ReadyToLearn(void); +BOOL ReadyToGuess(void); +void doubleBeat(void); +void halfBeat(void); +void ResetAdapt(void); +void SliderStep(int Ctl, int *slide); +void initBpm(void); +extern int g_fakeinit; + +int cfg_smartbeat=0; +int cfg_smartbeatsticky=1; +int cfg_smartbeatresetnewsong=1; +int cfg_smartbeatonlysticky=0; +int sticked=0; +int arbVal, skipVal; // Values of arbitrary beat and beat skip +int Bpm, Confidence, Confidence1, Confidence2; // Calculated BPM (realtime), Confidence computation +DWORD lastTC; // Last beat Tick count +DWORD lastTC2; // Last beat tick count 2 +BeatType TCHist[8]; // History of last 8 beats +BeatType TCHist2[8]; // History of last 8 beats +int Smoother[8]; // History of last 8 Bpm values, used to smooth changes +int halfDiscriminated[8]; // Discriminated beats table +int halfDiscriminated2[8]; // Discriminated beats table +int hdPos; // Position in discrimination table +int hdPos2; // Position in discrimination table +int smPtr, smSize; // Smoother pointer and size +int TCHistPtr; // Tick count history pointer +int TCHistSize; // Tick count history size +int offIMax; // Max divisor/multiplier used to guess/discriminate beats +int lastBPM; // Last calculated BPM, used by the smoother to detect new entry +int insertionCount; // Remembers how many beats were guessed +DWORD predictionLastTC; // Last tick count of guessed/accepted beat +DWORD Avg; // Average tick count interval between beats +DWORD Avg2; // Average tick count interval between beats +int skipCount; // Beat counter used by beat skipper +int inInc, outInc; // +1/-1, Used by the nifty beatsynced sliders +int inSlide, outSlide; // Positions of sliders +int oldInSlide, oldOutSlide; // Used by timer to detect changes in sliders +int oldsticked; // Used by timer to detect changes in sticked state +char txt[256]; // Contains txt about current BPM and confidence +int halfCount, doubleCount; // Counter used to autodetect if double/half beat needed +int TCUsed; // Remembers how many beats in the history were actually used for computation +int predictionBpm; // Contains BPM actually used to prediction (eliminates Bpm driftings) +int oldDisplayBpm, oldDisplayConfidence; // Detects stuff to redraw +int bestConfidence; // Best confidence we had so far +char lastSongName[256]; // Last song name, used to detect song changes in winamp +HWND winampWnd; // Winamp window +int forceNewBeat; // Force new bpm adoption +int betterConfidenceCount; // Used to decide when to adpot new beat +int topConfidenceCount; // Used to decide when to adpot new beat +int stickyConfidenceCount; // Used to decided when to go sticky +BOOL doResyncBpm=FALSE; + +// configuration dialog stuff +BOOL CALLBACK DlgProc_Bpm(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam) +{ +switch (uMsg) + { + case WM_INITDIALOG: + inInc = 1; + outInc = 1; + inSlide = 0; + outSlide = 0; + oldDisplayBpm=-1; + oldDisplayConfidence=-1; + oldInSlide=-1; + oldOutSlide=-1; + if (cfg_smartbeat) CheckDlgButton(hwndDlg,IDC_BPMADV,BST_CHECKED); else CheckDlgButton(hwndDlg,IDC_BPMSTD,BST_CHECKED); + if (cfg_smartbeatsticky) CheckDlgButton(hwndDlg,IDC_STICKY,BST_CHECKED); + if (cfg_smartbeatresetnewsong) CheckDlgButton(hwndDlg,IDC_NEWRESET,BST_CHECKED); else CheckDlgButton(hwndDlg,IDC_NEWADAPT,BST_CHECKED); + if (cfg_smartbeatonlysticky) CheckDlgButton(hwndDlg,IDC_ONLYSTICKY,BST_CHECKED); + SendDlgItemMessage(hwndDlg, IDC_IN, TBM_SETTICFREQ, 1, 0); + SendDlgItemMessage(hwndDlg, IDC_IN, TBM_SETRANGE, TRUE, MAKELONG(0, 8)); + SendDlgItemMessage(hwndDlg, IDC_OUT, TBM_SETTICFREQ, 1, 0); + SendDlgItemMessage(hwndDlg, IDC_OUT, TBM_SETRANGE, TRUE, MAKELONG(0, 8)); + if (predictionBpm) + { + ShowWindow(GetDlgItem(hwndDlg, IDC_STICK), sticked ? SW_HIDE : SW_NORMAL); + ShowWindow(GetDlgItem(hwndDlg, IDC_UNSTICK), sticked ? SW_NORMAL : SW_HIDE); + } + else + { + ShowWindow(GetDlgItem(hwndDlg, IDC_STICK), SW_HIDE); + ShowWindow(GetDlgItem(hwndDlg, IDC_UNSTICK), SW_HIDE); + } +/* ShowWindow(GetDlgItem(hwndDlg, IDC_CURBPM), cfg_smartbeat ? SW_NORMAL : SW_HIDE); + ShowWindow(GetDlgItem(hwndDlg, IDC_CURCONF), cfg_smartbeat ? SW_NORMAL : SW_HIDE); + ShowWindow(GetDlgItem(hwndDlg, IDC_BPM), cfg_smartbeat ? SW_NORMAL : SW_HIDE); + ShowWindow(GetDlgItem(hwndDlg, IDC_CONFIDENCE), cfg_smartbeat ? SW_NORMAL : SW_HIDE); + ShowWindow(GetDlgItem(hwndDlg, IDC_RESET), cfg_smartbeat ? SW_NORMAL : SW_HIDE);*/ + SetTimer(hwndDlg, 0, 50, NULL); + return 1; + case WM_TIMER: + { + if (oldInSlide != inSlide) { + SendDlgItemMessage(hwndDlg, IDC_IN, TBM_SETPOS, TRUE, inSlide); oldInSlide=inSlide; } + if (oldOutSlide != outSlide) { + SendDlgItemMessage(hwndDlg, IDC_OUT, TBM_SETPOS, TRUE, outSlide); oldOutSlide=outSlide; } + if (oldDisplayBpm != predictionBpm || oldsticked != sticked) { + char lBuf[16]; + wsprintf(txt, predictionBpm ? "%d%s"/*/%d"*/ : WASABI_API_LNGSTRING_BUF(IDS_LEARNING,lBuf,16), predictionBpm, cfg_smartbeatsticky && sticked ? WASABI_API_LNGSTRING(IDS_GOT_IT) : ""/*, Bpm*/); + SetDlgItemText(hwndDlg, IDC_BPM, txt); + oldDisplayBpm=predictionBpm; + oldsticked=sticked; + if (predictionBpm) + { + ShowWindow(GetDlgItem(hwndDlg, IDC_STICK), sticked ? SW_HIDE : SW_NORMAL); + ShowWindow(GetDlgItem(hwndDlg, IDC_UNSTICK), sticked ? SW_NORMAL : SW_HIDE); + } + else + { + ShowWindow(GetDlgItem(hwndDlg, IDC_STICK), SW_HIDE); + ShowWindow(GetDlgItem(hwndDlg, IDC_UNSTICK), SW_HIDE); + } + } + if (oldDisplayConfidence != Confidence) { + wsprintf(txt, "%d%%"/* (%d%%/%d%% - %d)"*/, Confidence/*, Confidence1, Confidence2, TCUsed*/); + SetDlgItemText(hwndDlg, IDC_CONFIDENCE, txt); + oldDisplayConfidence=Confidence; + } + } + return 0; + case WM_COMMAND: + if ((LOWORD(wParam) == IDC_BPMSTD) || + (LOWORD(wParam) == IDC_BPMADV) || + (LOWORD(wParam) == IDC_NEWRESET) || + (LOWORD(wParam) == IDC_NEWADAPT) || + (LOWORD(wParam) == IDC_ONLYSTICKY) || + (LOWORD(wParam) == IDC_STICKY)) + { + cfg_smartbeat=IsDlgButtonChecked(hwndDlg,IDC_BPMADV)?1:0; + cfg_smartbeatsticky=IsDlgButtonChecked(hwndDlg,IDC_STICKY)?1:0; + cfg_smartbeatresetnewsong=IsDlgButtonChecked(hwndDlg,IDC_NEWRESET)?1:0; + cfg_smartbeatonlysticky=IsDlgButtonChecked(hwndDlg,IDC_ONLYSTICKY)?1:0; + oldsticked=-1; +/* ShowWindow(GetDlgItem(hwndDlg, IDC_CURBPM), cfg_smartbeat ? SW_NORMAL : SW_HIDE); + ShowWindow(GetDlgItem(hwndDlg, IDC_CURCONF), cfg_smartbeat ? SW_NORMAL : SW_HIDE); + ShowWindow(GetDlgItem(hwndDlg, IDC_BPM), cfg_smartbeat ? SW_NORMAL : SW_HIDE); + ShowWindow(GetDlgItem(hwndDlg, IDC_CONFIDENCE), cfg_smartbeat ? SW_NORMAL : SW_HIDE); + ShowWindow(GetDlgItem(hwndDlg, IDC_RESET), cfg_smartbeat ? SW_NORMAL : SW_HIDE);*/ + } + if (LOWORD(wParam) == IDC_2X) + doubleBeat(); + if (LOWORD(wParam) == IDC_DIV2) + halfBeat(); + if (LOWORD(wParam) == IDC_RESET) + ResetAdapt(); + if (LOWORD(wParam) == IDC_STICK) + { + sticked=1; + stickyConfidenceCount=0; + } + if (LOWORD(wParam) == IDC_UNSTICK) + { + sticked=0; + stickyConfidenceCount=0; + } + return 0; + case WM_DESTROY: + KillTimer(hwndDlg, 0); + return 0; + } +return 0; +} + +void initBpm(void) +{ + if (g_fakeinit) return; + TCUsed=0; + *txt=0; + betterConfidenceCount=0; + topConfidenceCount=0; + forceNewBeat=0; + hdPos=0; + hdPos2=0; + inSlide=0; + outSlide=0; + oldDisplayBpm=-1; + oldDisplayConfidence=-1; + oldInSlide=0; + oldOutSlide=0; + Bpm = 0; + Avg = 0; + Avg2 = 0; + smPtr = 0; + smSize = 8; + offIMax = 8; + insertionCount = 0; + predictionLastTC = 0; + Confidence = 0; + Confidence1 = 0; + Confidence2 = 0; + halfCount=0; + doubleCount=0; + TCHistSize = 8; + predictionBpm=0; + lastTC=GetTickCount(); + stickyConfidenceCount=0; + memset(TCHist, 0, TCHistSize*sizeof(BeatType)); + memset(Smoother, 0, smSize*sizeof(int)); + memset(halfDiscriminated, 0, TCHistSize*sizeof(int)); + memset(halfDiscriminated2, 0, TCHistSize*sizeof(int)); + winampWnd = FindWindow("Winamp v1.x", NULL); + *lastSongName=0; + sticked=0; + oldsticked=-1; +} + +BOOL songChanged(DWORD TCNow) +{ +static DWORD lastCheck=0; +if (TCNow > lastCheck+1000) + { + char songName[256]; + lastCheck=TCNow; + GetWindowText(winampWnd, songName, 255); + if (strcmp(songName, lastSongName)) + { + strcpy(lastSongName, songName); + return TRUE; + } + } +return FALSE; +} + +void ResetAdapt(void) +{ +// Reset adaptive learning +*txt=0; +TCUsed=0; +hdPos=0; +Avg = 0; +Confidence=0; +Confidence1=0; +Confidence2=0; +betterConfidenceCount=0; +topConfidenceCount=0; +Bpm = 0; +smPtr = 0; +smSize = 8; +offIMax = 8; +insertionCount = 0; +predictionLastTC = 0; +halfCount=0; +doubleCount=0; +TCHistSize = 8; +predictionBpm=0; +bestConfidence=0; +lastTC=GetTickCount(); +sticked=0; +oldsticked=-1; +stickyConfidenceCount=0; +memset(TCHist, 0, TCHistSize*sizeof(BeatType)); +memset(TCHist2, 0, TCHistSize*sizeof(BeatType)); +memset(Smoother, 0, smSize*sizeof(int)); +memset(halfDiscriminated, 0, TCHistSize*sizeof(int)); +} + +// Insert a beat in history table. May be either real beat or guessed +void InsertHistStep(BeatType *t, DWORD TC, int Type, int i) +{ +if (i >= TCHistSize) return; +if (t == TCHist && insertionCount < TCHistSize*2) insertionCount++; +memmove(t+i+1, t+i, sizeof(BeatType)*(TCHistSize-(i+1))); +t[0].TC = TC; +t[0].Type = Type; +} + +// Doubles current beat +void doubleBeat(void) +{ +int i; +int iv[8]; + +if (sticked && Bpm > MIN_BPM) return; + +for (i=0;i<TCHistSize-1;i++) + iv[i] = TCHist[i].TC - TCHist[i+1].TC; + +for (i=1;i<TCHistSize;i++) + TCHist[i].TC = TCHist[i-1].TC-iv[i-1]/2; + +Avg /= 2; +Bpm *= 2; +doubleCount=0; +memset(Smoother, 0, smSize*sizeof(int)); +memset(halfDiscriminated, 0, TCHistSize*sizeof(int)); +//forceNewBeat=1; +} + +// Halfs current beat +void halfBeat(void) +{ +int i; +int iv[8]; + +if (sticked && Bpm < MIN_BPM) return; + +for (i=0;i<TCHistSize-1;i++) + iv[i] = TCHist[i].TC - TCHist[i+1].TC; + +for (i=1;i<TCHistSize;i++) + TCHist[i].TC = TCHist[i-1].TC-iv[i-1]*2; + +Avg *= 2; +Bpm /= 2; +halfCount=0; +memset(Smoother, 0, smSize*sizeof(int)); +memset(halfDiscriminated, 0, TCHistSize*sizeof(int)); +} + +// Called whenever isBeat was true in render +BOOL TCHistStep(BeatType *t, DWORD _Avg, int *_halfDiscriminated, int *_hdPos, DWORD *_lastTC, DWORD TC, int Type) +{ +int i=0; +int offI; +DWORD thisLen; +BOOL learning = ReadyToLearn(); +thisLen = TC-lastTC; + +// If this beat is sooner than half the average - 20%, throw it away +if (thisLen < Avg/2 - Avg*0.2) + { + if (learning) + { + if (labs(Avg - (TC - t[1].TC)) < labs(Avg - (t[0].TC - t[1].TC))) + { +/* if (predictionLastTC && t[0].Type == BEAT_GUESSED && Type == BEAT_REAL) + predictionLastTC += (TC - t[0].TC)/2;*/ + t[0].TC = TC; + t[0].Type = Type; + return TRUE; + } + } + return FALSE; + } + +if (learning) + for (offI=2;offI<offIMax;offI++) // Try to see if this beat is in the middle of our current Bpm, or maybe 1/3, 1/4 etc... to offIMax + if ((float)labs((Avg/offI)-thisLen) < (float)(Avg/offI)*0.2) + { + _halfDiscriminated[(*_hdPos)++]=1; // Should test if offI==2 before doing that, but seems to have better results ? I'll have to investigate this + (*_hdPos)%=8; + return FALSE; + } + +// This beat is accepted, so set this discrimination entry to false +_halfDiscriminated[hdPos++]=0; +(*_hdPos)%=8; + +// Check if we missed some beats +/*if (learning && thisLen > 1000 || (float)abs(Avg-thisLen) > (float)Avg*0.3) + for (offI=2;offI<offIMax;offI++) + { + if ((float)abs((Avg*offI)-thisLen) < (float)Avg*0.2) + { + for (j=1;j<offI;j++) // Oh yeah we definitly did, add one! + InsertHistStep(TC - (Avg*j), BEAT_GUESSED, offI-1); // beat has been guessed so report it so confidence can be calculated + break; + } + }*/ + + +// Remember this tick count +*_lastTC = TC; +// Insert this beat. +InsertHistStep(t, TC, Type, 0); +return TRUE; +} + +// Am i ready to learn ? +BOOL ReadyToLearn(void) +{ +int i; +for (i=0;i<TCHistSize;i++) + if (TCHist[i].TC==0) return FALSE; +return TRUE; +} + +// Am i ready to guess ? +BOOL ReadyToGuess(void) +{ +return insertionCount == TCHistSize*2; +} + +void newBpm(int thisBpm) +{ +Smoother[smPtr++] = thisBpm; +smPtr %= smSize; +} + +int GetBpm(void) +{ +int i; +int smN=0; +int smSum=0; +// Calculate smoothed Bpm +for (i=0;i<smSize;i++) + if (Smoother[i] > 0) { + smSum += Smoother[i]; + smN++; + } +if (smN) return smSum / smN; +return 0; +} + +// Calculate BPM according to beat history +void CalcBPM(void) +{ +int i; +int hdCount=0; +int r=0; +int totalTC=0, totalN=0; +float rC, etC; +int v; +double sc=0; +int mx=0; +float et; +int smSum=0, smN=0; + +if (!ReadyToLearn()) + return; + +// First calculate average beat +for (i=0;i<TCHistSize-1;i++) + totalTC += TCHist[i].TC - TCHist[i+1].TC; + +Avg = totalTC/(TCHistSize-1); + +// Count how many of then are real as opposed to guessed +for (i=0;i<TCHistSize;i++) + if (TCHist[i].Type == BEAT_REAL) + r++; + +// Calculate part 1 of confidence +rC = (float)min((float)((float)r / (float)TCHistSize) * 2, 1); + +// Calculate typical drift +for (i=0;i<TCHistSize-1;i++) + { + v = TCHist[i].TC - TCHist[i+1].TC; + mx = max(mx, v); + sc += v*v; + } +et = (float)sqrt(sc / (TCHistSize-1) - Avg*Avg); +// Calculate confidence based on typical drift and max derivation +etC = 1 - ((float)et / (float)mx); + +// Calculate confidence +Confidence = max(0, (int)(((rC * etC) * 100.0) - 50) * 2); +Confidence1 = (int)(rC * 100); +Confidence2 = (int)(etC * 100); + +// Now apply second layer, recalculate average using only beats within range of typical drift +// Also, count how many of them we are keeping +totalTC=0; +for (i=0;i<TCHistSize-1;i++) + { + v += TCHist[i].TC - TCHist[i+1].TC; + if (labs(Avg-v) < et) + { + totalTC += v; + totalN++; + v = 0; + } + else + if ((float)v > Avg) + v = 0; + } +TCUsed = totalN; +// If no beat was within typical drift (how would it be possible? well lets cover our ass) then keep the simple +// average calculated earlier, else recalculate average of beats within range +if (totalN) + Avg = totalTC/totalN; + +if (ReadyToGuess()) + { + if (Avg) // Avg = 0 ? Ahem.. + Bpm = 60000 / Avg; + + + if (Bpm != lastBPM) + { + newBpm(Bpm); // If realtime Bpm has changed since last time, then insert it in the smoothing tab;e + lastBPM = Bpm; + + if (cfg_smartbeatsticky && predictionBpm && Confidence >= ((predictionBpm < 90) ? STICKY_THRESHOLD_LOW : STICKY_THRESHOLD)) + { + stickyConfidenceCount++; + if (stickyConfidenceCount >= MIN_STICKY) + sticked=1; + } + else + stickyConfidenceCount=0; + } + + Bpm = GetBpm(); + + // Count how many beats we discriminated + for (i=0;i<TCHistSize;i++) + if (halfDiscriminated[i]) hdCount++; + + if (hdCount >= TCHistSize/2) // If we removed at least half of our beats, then we are off course. We should double our bpm + { + if (Bpm * 2 < MAX_BPM) // Lets do so only if the doubled bpm is < MAX_BPM + { + doubleBeat(); + memset(halfDiscriminated, 0, TCHistSize*sizeof(int)); // Reset discrimination table + } + } + if (Bpm > 500 || Bpm < 0) + { + ResetAdapt(); + } + if (Bpm < MIN_BPM) + { + if (++doubleCount > 4) // We're going too slow, lets double our bpm + doubleBeat(); + } + else + doubleCount=0; + if (Bpm > MAX_BPM) // We're going too fast, lets slow our bpm by a factor of 2 + { + if (++halfCount > 4) + halfBeat(); + } + else + halfCount=0; + } +} + +void SliderStep(int Ctl, int *slide) +{ +*slide += Ctl == IDC_IN ? inInc : outInc; +if (!*slide || *slide == 8) (Ctl == IDC_IN ? inInc : outInc) *= -1; +} + +// render function +// render should return 0 if it only used framebuffer, or 1 if the new output data is in fbout. this is +// used when you want to do something that you'd otherwise need to make a copy of the framebuffer. +// w and h are the width and height of the screen, in pixels. +// isBeat is 1 if a beat has been detected. +// visdata is in the format of [spectrum:0,wave:1][channel][band]. + +int refineBeat(int isBeat) +{ + BOOL accepted=FALSE; + BOOL predicted=FALSE; + BOOL resyncin=FALSE; + BOOL resyncout=FALSE; + + if (isBeat) // Show the beat received from AVS + SliderStep(IDC_IN, &inSlide); + + DWORD TCNow = GetTickCount(); + + if (songChanged(TCNow)) + { + bestConfidence=(int)((float)bestConfidence*0.5); + sticked=0; + stickyConfidenceCount=0; + if (cfg_smartbeatresetnewsong) + ResetAdapt(); + } + + // Try to predict if this frame should be a beat + if (Bpm && TCNow > predictionLastTC + (60000 / Bpm)) + predicted = TRUE; + + + if (isBeat) // If it is a real beat, do discrimination/guessing and computations, then see if it is accepted + accepted = TCHistStep(TCHist, Avg, halfDiscriminated, &hdPos, &lastTC, TCNow, BEAT_REAL); + + // Calculate current Bpm + CalcBPM(); + + // If prediction Bpm has not yet been set + // or if prediction bpm is too high or too low + // or if 3/4 of our history buffer contains beats within the range of typical drift + // the accept the calculated Bpm as the new prediction Bpm + // This allows keeping the beat going on when the music fades out, and readapt to the new beat as soon as + // the music fades in again + if ((accepted || predicted) && !sticked && (!predictionBpm || predictionBpm > MAX_BPM || predictionBpm < MIN_BPM)) + { + if (Confidence >= bestConfidence) + { +/* betterConfidenceCount++; + if (!predictionBpm || betterConfidenceCount == BETTER_CONF_ADOPT) + {*/ + forceNewBeat=1; +/* betterConfidenceCount=0; + }*/ + } + if (Confidence >= 50) + { + topConfidenceCount++; + if (topConfidenceCount == TOP_CONF_ADOPT) + { + forceNewBeat=1; + topConfidenceCount=0; + } + } + if (forceNewBeat) + { + forceNewBeat=0; + bestConfidence = Confidence; + predictionBpm=Bpm; + } + } + + if (!sticked) predictionBpm = Bpm; + Bpm=predictionBpm; + + +/* resync = (predictionBpm && + (predictionLastTC < TCNow - (30000/predictionBpm) - (60000/predictionBpm)*0.2) || + (predictionLastTC < TCNow - (30000/predictionBpm) - (60000/predictionBpm)*0.2));*/ + if (predictionBpm && accepted && !predicted) + { + int b=0; + if (TCNow > predictionLastTC + (60000 / predictionBpm)*0.7) + { + resyncin = TRUE; + b = (int)((float)predictionBpm * 1.01); + } + if (TCNow < predictionLastTC + (60000 / predictionBpm)*0.3) + { + resyncout = TRUE; + b = (int)((float)predictionBpm * 0.98); + } + if (!sticked && doResyncBpm && (resyncin || resyncout)) + { + newBpm(b); + predictionBpm = GetBpm(); + } + } + + if (resyncin) + { + predictionLastTC = TCNow; + SliderStep(IDC_OUT, &outSlide); + doResyncBpm=TRUE; + return ((cfg_smartbeat && !cfg_smartbeatonlysticky) || (cfg_smartbeat && cfg_smartbeatonlysticky && sticked)) ? 1 : isBeat; + } + if (predicted) + { + predictionLastTC = TCNow; + if (Confidence > 25) TCHistStep(TCHist, Avg, halfDiscriminated, &hdPos, &lastTC, TCNow, BEAT_GUESSED); + SliderStep(IDC_OUT, &outSlide); + doResyncBpm=FALSE; + return ((cfg_smartbeat && !cfg_smartbeatonlysticky) || (cfg_smartbeat && cfg_smartbeatonlysticky && sticked)) ? 1 : isBeat; + } + if (resyncout) + { + predictionLastTC = TCNow; + doResyncBpm=TRUE; + return ((cfg_smartbeat && !cfg_smartbeatonlysticky) || (cfg_smartbeat && cfg_smartbeatonlysticky && sticked)) ? 0 : isBeat; + } + + return ((cfg_smartbeat && !cfg_smartbeatonlysticky) || (cfg_smartbeat && cfg_smartbeatonlysticky && sticked)) ? (predictionBpm ? 0 : isBeat) : isBeat; +} + + + |