31#include <core/config.h>
65 for (
const auto ppattern : *pOther->m_pPlayingPatterns ) {
66 if ( ppattern !=
nullptr ) {
72 for (
const auto ppattern : *pOther->m_pNextPatterns ) {
73 if ( ppattern !=
nullptr ) {
107 ERRORLOG( QString(
"[%1] Provided bpm [%2] is too high. Assigning upper bound %3 instead" )
110 }
else if ( fNewBpm <
MIN_BPM ) {
111 ERRORLOG( QString(
"[%1] Provided bpm [%2] is too low. Assigning lower bound %3 instead" )
124 if ( nNewFrame < 0 ) {
125 ERRORLOG( QString(
"[%1] Provided frame [%2] is negative. Setting frame 0 instead." )
126 .arg(
m_sLabel ).arg( nNewFrame ) );
134 if ( fNewTick < 0 ) {
135 ERRORLOG( QString(
"[%1] Provided tick [%2] is negative. Setting frame 0 instead." )
144 if ( fNewTickSize <= 0 ) {
145 ERRORLOG( QString(
"[%1] Provided tick size [%2] is too small. Using 400 as a fallback instead." )
146 .arg(
m_sLabel ).arg( fNewTickSize ) );
154 if ( nPatternStartTick < 0 ) {
155 ERRORLOG( QString(
"[%1] Provided tick [%2] is negative. Setting frame 0 instead." )
156 .arg(
m_sLabel ).arg( nPatternStartTick ) );
157 nPatternStartTick = 0;
164 if ( nPatternTickPosition < 0 ) {
165 ERRORLOG( QString(
"[%1] Provided tick [%2] is negative. Setting frame 0 instead." )
166 .arg(
m_sLabel ).arg( nPatternTickPosition ) );
167 nPatternTickPosition = 0;
174 if ( nColumn < -1 ) {
175 ERRORLOG( QString(
"[%1] Provided column [%2] it too small. Using [-1] as a fallback instead." )
185 if ( nPatternSize < 0 ) {
186 ERRORLOG( QString(
"[%1] Provided pattern size [%2] it too small. Using [0] as a fallback instead." )
187 .arg(
m_sLabel ).arg( nPatternSize ) );
195 ERRORLOG( QString(
"[%1] Provided bar [%2] it too small. Using [1] as a fallback instead." )
204 ERRORLOG( QString(
"[%1] Provided beat [%2] it too small. Using [1] as a fallback instead." )
216 const auto pSong = pHydrogen->getSong();
217 const auto pTimeline = pHydrogen->getTimeline();
218 const auto pAudioEngine = pHydrogen->getAudioEngine();
219 const auto pAudioDriver = pHydrogen->getAudioOutput();
221 if ( pSong ==
nullptr || pTimeline ==
nullptr ) {
226 if ( pAudioDriver ==
nullptr ) {
227 ERRORLOG(
"AudioDriver is not ready!" );
232 if ( nSampleRate == 0 ) {
233 nSampleRate = pAudioDriver->getSampleRate();
235 const int nResolution = pSong->getResolution();
236 const double fSongSizeInTicks = pAudioEngine->getSongSizeInTicks();
238 if ( nSampleRate == 0 || nResolution == 0 ) {
239 ERRORLOG(
"Not properly initialized yet" );
249 const auto tempoMarkers = pTimeline->getAllTempoMarkers();
253 long long nNewFrame = 0;
254 if ( pHydrogen->isTimelineEnabled() &&
255 ! ( tempoMarkers.size() == 1 &&
256 pTimeline->isFirstTempoMarkerSpecial() ) &&
258 pSong->getPatternGroupVector()->size() > 0 ) {
260 double fNewTick = fTick;
261 double fRemainingTicks = fTick;
262 double fNextTick, fPassedTicks = 0;
263 double fNextTickSize;
264 double fNewFrame = 0;
267 const int nColumns = pSong->getPatternGroupVector()->size();
269 auto handleEnd = [&]() {
271 fNewFrame += fRemainingTicks * fNextTickSize;
273 nNewFrame =
static_cast<long long>( std::round( fNewFrame ) );
280 const double fRoundingErrorInTicks =
281 ( fNewFrame -
static_cast<double>( nNewFrame ) ) /
289 if ( fRoundingErrorInTicks >
290 fPassedTicks + fRemainingTicks - fNextTick ) {
293 *fTickMismatch = fRoundingErrorInTicks;
298 *fTickMismatch = fPassedTicks + fRemainingTicks - fNextTick;
300 const double fFinalFrame = fNewFrame +
301 ( fNextTick - fPassedTicks - fRemainingTicks ) * fNextTickSize;
304 double fFinalTickSize;
305 if ( ii < tempoMarkers.size() ) {
307 nSampleRate, tempoMarkers[ ii ]->fBpm, nResolution );
311 nSampleRate, tempoMarkers[ 0 ]->fBpm, nResolution );
325 *fTickMismatch += ( fFinalFrame -
static_cast<double>(nNewFrame) ) /
344 fRemainingTicks -= fNewTick - fPassedTicks;
347 while ( fRemainingTicks > 0 ) {
349 for ( ii = 1; ii <= tempoMarkers.size(); ++ii ) {
350 if ( ii == tempoMarkers.size() ||
351 tempoMarkers[ ii ]->nColumn >= nColumns ) {
352 fNextTick = fSongSizeInTicks;
355 static_cast<double>(pHydrogen->getTickForColumn( tempoMarkers[ ii ]->nColumn ) );
360 tempoMarkers[ ii - 1 ]->fBpm,
363 if ( fRemainingTicks > ( fNextTick - fPassedTicks ) ) {
366 fNewFrame += ( fNextTick - fPassedTicks ) * fNextTickSize;
381 fRemainingTicks -= fNextTick - fPassedTicks;
383 fPassedTicks = fNextTick;
392 if ( fRemainingTicks > 0 ) {
396 const int nRepetitions = std::floor(fTick / fSongSizeInTicks);
397 const double fSongSizeInFrames = fNewFrame;
399 fNewFrame *=
static_cast<double>(nRepetitions);
400 fNewTick = std::fmod( fTick, fSongSizeInTicks );
401 fRemainingTicks = fNewTick;
414 if ( std::isinf( fNewFrame ) ||
415 static_cast<long long>(fNewFrame) >
416 std::numeric_limits<long long>::max() ) {
417 ERRORLOG( QString(
"Provided ticks [%1] are too large." ).arg( fTick ) );
425 if ( fRemainingTicks == 0 ) {
426 ii = tempoMarkers.size();
427 fNextTick =
static_cast<double>(pHydrogen->getTickForColumn(
428 tempoMarkers[ 0 ]->nColumn ) );
430 nSampleRate, tempoMarkers[ ii - 1 ]->fBpm, nResolution );
444 const double fTickSize =
449 const double fNewFrame =
static_cast<double>(fTick) *
451 nNewFrame =
static_cast<long long>( std::round( fNewFrame ) );
452 *fTickMismatch = ( fNewFrame -
static_cast<double>(nNewFrame ) ) /
470 ERRORLOG( QString(
"Provided frame [%1] must be non-negative" ).arg( nFrame ) );
473 const auto pSong = pHydrogen->getSong();
474 const auto pTimeline = pHydrogen->getTimeline();
475 const auto pAudioEngine = pHydrogen->getAudioEngine();
476 const auto pAudioDriver = pHydrogen->getAudioOutput();
478 if ( pSong ==
nullptr || pTimeline ==
nullptr ) {
482 if ( pAudioDriver ==
nullptr ) {
483 ERRORLOG(
"AudioDriver is not ready!" );
487 if ( nSampleRate == 0 ) {
488 nSampleRate = pAudioDriver->getSampleRate();
490 const int nResolution = pSong->getResolution();
493 const double fSongSizeInTicks = pAudioEngine->getSongSizeInTicks();
495 if ( nSampleRate == 0 || nResolution == 0 ) {
496 ERRORLOG(
"Not properly initialized yet" );
504 const auto tempoMarkers = pTimeline->getAllTempoMarkers();
508 if ( pHydrogen->isTimelineEnabled() &&
509 ! ( tempoMarkers.size() == 1 &&
510 pTimeline->isFirstTempoMarkerSpecial() ) &&
512 pSong->getPatternGroupVector()->size() ) {
516 const double fTargetFrame =
static_cast<double>(nFrame);
517 double fPassedFrames = 0;
518 double fNextFrame = 0;
519 double fNextTicks, fPassedTicks = 0;
520 double fNextTickSize;
521 long long nRemainingFrames;
523 const int nColumns = pSong->getPatternGroupVector()->size();
525 while ( fPassedFrames < fTargetFrame ) {
527 for (
int ii = 1; ii <= tempoMarkers.size(); ++ii ) {
531 tempoMarkers[ ii - 1 ]->fBpm,
534 if ( ii == tempoMarkers.size() ||
535 tempoMarkers[ ii ]->nColumn >= nColumns ) {
536 fNextTicks = fSongSizeInTicks;
539 static_cast<double>(pHydrogen->getTickForColumn( tempoMarkers[ ii ]->nColumn ));
541 fNextFrame = (fNextTicks - fPassedTicks) * fNextTickSize;
543 if ( fNextFrame < ( fTargetFrame -
563 fTick += fNextTicks - fPassedTicks;
565 fPassedFrames += fNextFrame;
566 fPassedTicks = fNextTicks;
570 const double fNewTick = (fTargetFrame - fPassedFrames ) /
590 fPassedFrames = fTargetFrame;
596 if ( fPassedFrames != fTargetFrame ) {
600 const double fSongSizeInFrames = fPassedFrames;
601 const int nRepetitions = std::floor(fTargetFrame / fSongSizeInFrames);
602 if ( fSongSizeInTicks * nRepetitions >
603 std::numeric_limits<double>::max() ) {
604 ERRORLOG( QString(
"Provided frames [%1] are too large." ).arg( nFrame ) );
607 fTick = fSongSizeInTicks * nRepetitions;
609 fPassedFrames =
static_cast<double>(nRepetitions) *
631 const double fTickSize =
636 fTick =
static_cast<double>(nFrame) / fTickSize;
647 return std::round( fTick * fTickSize );
651 return nFrame / fTickSize;
658 sOutput = QString(
"%1[TransportPosition]\n" ).arg( sPrefix )
659 .append( QString(
"%1%2m_sLabel: %3\n" ).arg( sPrefix ).arg( s ).arg(
m_sLabel ) )
660 .append( QString(
"%1%2m_nFrame: %3\n" ).arg( sPrefix ).arg( s ).arg(
getFrame() ) )
661 .append( QString(
"%1%2m_fTick: %3\n" ).arg( sPrefix ).arg( s ).arg(
getDoubleTick(), 0,
'f' ) )
662 .append( QString(
"%1%2m_fTick (rounded): %3\n" ).arg( sPrefix ).arg( s ).arg(
getTick() ) )
663 .append( QString(
"%1%2m_fTickSize: %3\n" ).arg( sPrefix ).arg( s ).arg(
getTickSize(), 0,
'f' ) )
664 .append( QString(
"%1%2m_fBpm: %3\n" ).arg( sPrefix ).arg( s ).arg(
getBpm(), 0,
'f' ) )
665 .append( QString(
"%1%2m_nPatternStartTick: %3\n" ).arg( sPrefix ).arg( s ).arg(
m_nPatternStartTick ) )
666 .append( QString(
"%1%2m_nPatternTickPosition: %3\n" ).arg( sPrefix ).arg( s ).arg(
m_nPatternTickPosition ) )
667 .append( QString(
"%1%2m_nColumn: %3\n" ).arg( sPrefix ).arg( s ).arg(
m_nColumn ) )
668 .append( QString(
"%1%2m_fTickMismatch: %3\n" ).arg( sPrefix ).arg( s ).arg(
m_fTickMismatch, 0,
'f' ) )
669 .append( QString(
"%1%2m_nFrameOffsetTempo: %3\n" ).arg( sPrefix ).arg( s ).arg(
m_nFrameOffsetTempo ) )
670 .append( QString(
"%1%2m_fTickOffsetQueuing: %3\n" ).arg( sPrefix ).arg( s ).arg(
m_fTickOffsetQueuing, 0,
'f' ) )
671 .append( QString(
"%1%2m_fTickOffsetSongSize: %3\n" ).arg( sPrefix ).arg( s ).arg(
m_fTickOffsetSongSize, 0,
'f' ) );
673 sOutput.append( QString(
"%1%2m_pPlayingPatterns: %3\n" ).arg( sPrefix ).arg( s ).arg(
m_pPlayingPatterns->
toQString( sPrefix + s ), bShort ) );
676 sOutput.append( QString(
"%1%2m_pNextPatterns: %3\n" ).arg( sPrefix ).arg( s ).arg(
m_pNextPatterns->
toQString( sPrefix + s ), bShort ) );
678 sOutput.append( QString(
"%1%2m_nPatternSize: %3\n" ).arg( sPrefix ).arg( s ).arg(
m_nPatternSize ) )
679 .append( QString(
"%1%2m_nLastLeadLagFactor: %3\n" ).arg( sPrefix ).arg( s ).arg(
m_nLastLeadLagFactor ) )
680 .append( QString(
"%1%2m_nBar: %3\n" ).arg( sPrefix ).arg( s ).arg(
m_nBar ) )
681 .append( QString(
"%1%2m_nBeat: %3\n" ).arg( sPrefix ).arg( s ).arg(
m_nBeat ) );
684 sOutput = QString(
"%1[TransportPosition]" ).arg( sPrefix )
685 .append( QString(
" m_sLabel: %1" ).arg(
m_sLabel ) )
686 .append( QString(
", m_nFrame: %1" ).arg(
getFrame() ) )
687 .append( QString(
", m_fTick: %1" ).arg(
getDoubleTick(), 0,
'f' ) )
688 .append( QString(
", m_fTick (rounded): %1" ).arg(
getTick() ) )
689 .append( QString(
", m_fTickSize: %1" ).arg(
getTickSize(), 0,
'f' ) )
690 .append( QString(
", m_fBpm: %1" ).arg(
getBpm(), 0,
'f' ) )
693 .append( QString(
", m_nColumn: %1" ).arg(
m_nColumn ) )
694 .append( QString(
", m_fTickMismatch: %1" ).arg(
m_fTickMismatch, 0,
'f' ) )
704 sOutput.append( QString(
", m_nPatternSize: %1" ).arg(
m_nPatternSize ) )
706 .append( QString(
", m_nBar: %1" ).arg(
m_nBar ) )
707 .append( QString(
", m_nBeat: %1" ).arg(
m_nBeat ) );
void setNeedsLock(bool bNeedsLock)
The audio processing thread can modify some PatternLists.
static float getBpmAtColumn(int nColumn)
static double computeDoubleTickSize(const int nSampleRate, const float fBpm, const int nResolution)
static QString sPrintIndention
String used to format the debugging string output of some core classes.
static Hydrogen * get_instance()
Returns the current Hydrogen instance __instance.
void recalculateRubberband(float fBpm)
Recalculates all Samples using RubberBand for a specific tempo fBpm.
PatternList is a collection of patterns.
void add(Pattern *pattern, bool bAddVirtuals=false)
add a pattern to the list
QString toQString(const QString &sPrefix="", bool bShort=true) const override
Formatted string version for debugging purposes.
void clear()
empty the pattern list
static Preferences * get_instance()
Returns a pointer to the current Preferences singleton stored in __instance.
double getDoubleTick() const
double m_fTickOffsetQueuing
Tick offset introduced when changing the tempo of the song.
long long m_nFrameOffsetTempo
Frame offset introduced when changing the tempo of the song.
TransportPosition(const QString sLabel="")
void setPatternStartTick(long nPatternStartTick)
void setColumn(int nColumn)
long m_nPatternTickPosition
Ticks passed since m_nPatternStartTick.
double m_fTickMismatch
Number of ticks m_nFrame is ahead/behind of m_fTick.
int m_nColumn
Specifies the column transport is located in and can be used as the index of the current PatternList/...
long m_nPatternStartTick
Dicstance in ticks between the beginning of the song and the beginning of the current column (m_nColu...
static double computeTick(long long nFrame, float fTickSize)
Converts frames into ticks under the assumption of a constant fTickSize (sample rate,...
static long long computeFrameFromTick(double fTick, double *fTickMismatch, int nSampleRate=0)
Calculates frame equivalent of fTick.
float getTickSize() const
float m_fBpm
Current tempo in beats per minute.
int m_nBeat
Last bar passed since m_nBar.
long long m_nLastLeadLagFactor
long long getFrame() const
long getTick() const
Retrieve a rounded version of m_fTick.
void setTick(double fNewTick)
void setPatternTickPosition(long nPatternTickPosition)
void set(std::shared_ptr< TransportPosition > pOther)
Copying the content of one position into the other is a lot cheaper than performing computations,...
void setFrame(long long nNewFrame)
int m_nPatternSize
Maximum size of all patterns in m_pPlayingPatterns.
int m_nBar
Last beat (column + 1) passed.
PatternList * m_pPlayingPatterns
Contains all Patterns currently played back.
void setTickSize(float fNewTickSize)
static double computeTickFromFrame(long long nFrame, int nSampleRate=0)
Calculates tick equivalent of nFrame.
long long m_nFrame
Current transport position in number of frames since the beginning of the song.
void setBpm(float fNewBpm)
double m_fTickOffsetSongSize
Tick offset introduced when changing the size of the song.
float m_fTickSize
Number of frames that make up one tick.
QString toQString(const QString &sPrefix="", bool bShort=true) const override
Formatted string version for debugging purposes.
double m_fTick
Current transport position in number of ticks since the beginning of the song.
void setPatternSize(int nPatternSize)
static long long computeFrame(double fTick, float fTickSize)
Converts ticks into frames under the assumption of a constant fTickSize (sample rate,...
PatternList * m_pNextPatterns
Patterns used to toggle the ones in m_pPlayingPatterns in Song::PatternMode::Stacked.
const QString m_sLabel
Identifier of the transport position.
#define MAX_NOTES
Maximum number of notes.