2022-02-14 22:12:20 -05:00
/**
* Furnace Tracker - multi - system chiptune tracker
2024-01-16 21:26:57 -05:00
* Copyright ( C ) 2021 - 2024 tildearrow and contributors
2022-02-14 22:12:20 -05:00
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License along
* with this program ; if not , write to the Free Software Foundation , Inc . ,
* 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 USA .
*/
2021-12-15 14:15:44 -05:00
# include "song.h"
2022-08-16 04:19:16 -04:00
# include "../ta-log.h"
2021-12-15 14:15:44 -05:00
2024-06-26 05:03:49 -04:00
bool DivSubSong : : walk ( int & loopOrder , int & loopRow , int & loopEnd , int chans , int jumpTreatment , int ignoreJumpAtEnd , int firstPat ) {
loopOrder = 0 ;
loopRow = 0 ;
loopEnd = - 1 ;
int nextOrder = - 1 ;
int nextRow = 0 ;
int effectVal = 0 ;
int lastSuspectedLoopEnd = - 1 ;
DivPattern * subPat [ DIV_MAX_CHANS ] ;
unsigned char wsWalked [ 8192 ] ;
memset ( wsWalked , 0 , 8192 ) ;
if ( firstPat > 0 ) {
memset ( wsWalked , 255 , 32 * firstPat ) ;
}
for ( int i = firstPat ; i < ordersLen ; i + + ) {
for ( int j = 0 ; j < chans ; j + + ) {
subPat [ j ] = pat [ j ] . getPattern ( orders . ord [ j ] [ i ] , false ) ;
}
if ( i > lastSuspectedLoopEnd ) {
lastSuspectedLoopEnd = i ;
}
for ( int j = nextRow ; j < patLen ; j + + ) {
nextRow = 0 ;
bool changingOrder = false ;
bool jumpingOrder = false ;
if ( wsWalked [ ( ( i < < 5 ) + ( j > > 3 ) ) & 8191 ] & ( 1 < < ( j & 7 ) ) ) {
loopOrder = i ;
loopRow = j ;
loopEnd = lastSuspectedLoopEnd ;
return true ;
}
for ( int k = 0 ; k < chans ; k + + ) {
for ( int l = 0 ; l < pat [ k ] . effectCols ; l + + ) {
effectVal = subPat [ k ] - > data [ j ] [ 5 + ( l < < 1 ) ] ;
if ( effectVal < 0 ) effectVal = 0 ;
if ( subPat [ k ] - > data [ j ] [ 4 + ( l < < 1 ) ] = = 0x0d ) {
if ( jumpTreatment = = 2 ) {
if ( ( i < ordersLen - 1 | | ! ignoreJumpAtEnd ) ) {
nextOrder = i + 1 ;
nextRow = effectVal ;
jumpingOrder = true ;
}
} else if ( jumpTreatment = = 1 ) {
if ( nextOrder = = - 1 & & ( i < ordersLen - 1 | | ! ignoreJumpAtEnd ) ) {
nextOrder = i + 1 ;
nextRow = effectVal ;
jumpingOrder = true ;
}
} else {
if ( ( i < ordersLen - 1 | | ! ignoreJumpAtEnd ) ) {
if ( ! changingOrder ) {
nextOrder = i + 1 ;
}
jumpingOrder = true ;
nextRow = effectVal ;
}
}
} else if ( subPat [ k ] - > data [ j ] [ 4 + ( l < < 1 ) ] = = 0x0b ) {
if ( nextOrder = = - 1 | | jumpTreatment = = 0 ) {
nextOrder = effectVal ;
if ( jumpTreatment = = 1 | | jumpTreatment = = 2 | | ! jumpingOrder ) {
nextRow = 0 ;
}
changingOrder = true ;
}
}
}
}
wsWalked [ ( ( i < < 5 ) + ( j > > 3 ) ) & 8191 ] | = 1 < < ( j & 7 ) ;
if ( nextOrder ! = - 1 ) {
i = nextOrder - 1 ;
nextOrder = - 1 ;
break ;
}
}
}
return false ;
}
2024-08-21 08:11:11 -04:00
double calcRowLenInSeconds ( const DivGroovePattern & speeds , float hz , int vN , int vD , int timeBaseFromSong )
{
double hl = 1 ; //count for 1 row
if ( hl < = 0.0 ) hl = 4.0 ;
double timeBase = timeBaseFromSong + 1 ;
double speedSum = 0 ;
for ( int i = 0 ; i < MIN ( 16 , speeds . len ) ; i + + ) {
speedSum + = speeds . val [ i ] ;
}
speedSum / = MAX ( 1 , speeds . len ) ;
if ( timeBase < 1.0 ) timeBase = 1.0 ;
if ( speedSum < 1.0 ) speedSum = 1.0 ;
if ( vD < 1 ) vD = 1 ;
//return (60.0 * hz / (timeBase * hl * speedSum)) * (double)vN / (double)vD;
return 1.0 / ( ( 60.0 * hz / ( timeBase * hl * speedSum ) ) * ( double ) vN / ( double ) vD / 60.0 ) ;
}
void DivSubSong : : findLength ( int loopOrder , int loopRow , double fadeoutLen , int & rowsForFadeout , bool & hasFFxx , std : : vector < int > & orders_vec , std : : vector < DivGroovePattern > & grooves , int & length , int chans , int jumpTreatment , int ignoreJumpAtEnd , int firstPat )
2024-08-20 15:02:07 -04:00
{
length = 0 ;
hasFFxx = false ;
2024-08-21 08:11:11 -04:00
rowsForFadeout = 0 ;
float secondsPerThisRow = 0.0f ;
DivGroovePattern curSpeeds = speeds ; //simulate that we are playing the song, track all speed/BPM/tempo/engine rate changes
short curVirtualTempoN = virtualTempoN ;
short curVirtualTempoD = virtualTempoD ;
float curHz = hz ;
double curDivider = ( double ) timeBase ;
double curLen = 0.0 ; //how many seconds passed since the start of song
2024-08-20 15:02:07 -04:00
int nextOrder = - 1 ;
int nextRow = 0 ;
int effectVal = 0 ;
int lastSuspectedLoopEnd = - 1 ;
DivPattern * subPat [ DIV_MAX_CHANS ] ;
unsigned char wsWalked [ 8192 ] ;
memset ( wsWalked , 0 , 8192 ) ;
if ( firstPat > 0 ) {
memset ( wsWalked , 255 , 32 * firstPat ) ;
}
for ( int i = firstPat ; i < ordersLen ; i + + )
{
bool jumped = false ;
for ( int j = 0 ; j < chans ; j + + )
{
subPat [ j ] = pat [ j ] . getPattern ( orders . ord [ j ] [ i ] , false ) ;
}
if ( i > lastSuspectedLoopEnd )
{
lastSuspectedLoopEnd = i ;
}
for ( int j = nextRow ; j < patLen ; j + + )
{
nextRow = 0 ;
bool changingOrder = false ;
bool jumpingOrder = false ;
if ( wsWalked [ ( ( i < < 5 ) + ( j > > 3 ) ) & 8191 ] & ( 1 < < ( j & 7 ) ) )
{
return ;
}
for ( int k = 0 ; k < chans ; k + + )
{
for ( int l = 0 ; l < pat [ k ] . effectCols ; l + + )
{
effectVal = subPat [ k ] - > data [ j ] [ 5 + ( l < < 1 ) ] ;
if ( effectVal < 0 ) effectVal = 0 ;
if ( subPat [ k ] - > data [ j ] [ 4 + ( l < < 1 ) ] = = 0xff )
{
hasFFxx = true ;
//FFxx makes YOU SHALL NOT PASS!!! move
orders_vec . push_back ( j + 1 ) ; //order len
length + = j + 1 ; //add length of order to song length
return ;
}
2024-08-21 08:11:11 -04:00
switch ( subPat [ k ] - > data [ j ] [ 4 + ( l < < 1 ) ] ) //track speed/BMP/Hz/tempo changes
{
case 0x09 : // select groove pattern/speed 1
{
if ( grooves . empty ( ) ) {
if ( effectVal > 0 ) curSpeeds . val [ 0 ] = effectVal ;
} else {
if ( effectVal < ( short ) grooves . size ( ) ) {
curSpeeds = grooves [ effectVal ] ;
//curSpeed=0;
}
}
break ;
}
case 0x0f : // speed 1/speed 2
{
if ( curSpeeds . len = = 2 & & grooves . empty ( ) ) {
if ( effectVal > 0 ) curSpeeds . val [ 1 ] = effectVal ;
} else {
if ( effectVal > 0 ) curSpeeds . val [ 0 ] = effectVal ;
}
break ;
}
case 0xfd : // virtual tempo num
{
if ( effectVal > 0 ) curVirtualTempoN = effectVal ;
break ;
}
case 0xfe : // virtual tempo den
{
if ( effectVal > 0 ) curVirtualTempoD = effectVal ;
break ;
}
case 0xf0 : // set Hz by tempo (set bpm)
{
curDivider = ( double ) effectVal * 2.0 / 5.0 ;
if ( curDivider < 1 ) curDivider = 1 ;
//cycles=got.rate*pow(2,MASTER_CLOCK_PREC)/divider;
//clockDrift=0;
//subticks=0;
break ;
}
default : break ;
}
2024-08-20 15:02:07 -04:00
if ( subPat [ k ] - > data [ j ] [ 4 + ( l < < 1 ) ] = = 0x0d )
{
if ( jumpTreatment = = 2 )
{
if ( ( i < ordersLen - 1 | | ! ignoreJumpAtEnd ) )
{
nextOrder = i + 1 ;
nextRow = effectVal ;
jumpingOrder = true ;
}
}
else if ( jumpTreatment = = 1 )
{
if ( nextOrder = = - 1 & & ( i < ordersLen - 1 | | ! ignoreJumpAtEnd ) )
{
nextOrder = i + 1 ;
nextRow = effectVal ;
jumpingOrder = true ;
}
}
else
{
if ( ( i < ordersLen - 1 | | ! ignoreJumpAtEnd ) )
{
if ( ! changingOrder )
{
nextOrder = i + 1 ;
}
jumpingOrder = true ;
nextRow = effectVal ;
}
}
}
else if ( subPat [ k ] - > data [ j ] [ 4 + ( l < < 1 ) ] = = 0x0b )
{
if ( nextOrder = = - 1 | | jumpTreatment = = 0 )
{
nextOrder = effectVal ;
if ( jumpTreatment = = 1 | | jumpTreatment = = 2 | | ! jumpingOrder )
{
nextRow = 0 ;
}
changingOrder = true ;
}
}
}
}
2024-08-21 08:11:11 -04:00
if ( i > loopOrder | | ( i = = loopOrder & & j > loopRow ) )
{
if ( curLen < = fadeoutLen & & fadeoutLen > 0.0 ) //we count each row fadeout lasts. When our time is greater than fadeout length we successfully counted the number of fadeout rows
{
secondsPerThisRow = calcRowLenInSeconds ( speeds , curHz , curVirtualTempoN , curVirtualTempoD , curDivider ) ;
curLen + = secondsPerThisRow ;
rowsForFadeout + + ;
}
}
2024-08-20 15:02:07 -04:00
wsWalked [ ( ( i < < 5 ) + ( j > > 3 ) ) & 8191 ] | = 1 < < ( j & 7 ) ;
if ( nextOrder ! = - 1 )
{
i = nextOrder - 1 ;
orders_vec . push_back ( j + 1 ) ; //order len
length + = j + 1 ; //add length of order to song length
jumped = true ;
nextOrder = - 1 ;
break ;
}
}
if ( ! jumped ) //if no jump occured we add full pattern length
{
orders_vec . push_back ( patLen ) ; //order len
length + = patLen ; //add length of order to song length
}
}
}
2022-05-15 02:42:49 -04:00
void DivSubSong : : clearData ( ) {
2022-04-24 18:39:18 -04:00
for ( int i = 0 ; i < DIV_MAX_CHANS ; i + + ) {
pat [ i ] . wipePatterns ( ) ;
}
2022-12-09 00:30:27 -05:00
memset ( orders . ord , 0 , DIV_MAX_CHANS * DIV_MAX_PATTERNS ) ;
2022-04-24 18:39:18 -04:00
ordersLen = 1 ;
}
2022-08-16 04:19:16 -04:00
void DivSubSong : : optimizePatterns ( ) {
for ( int i = 0 ; i < DIV_MAX_CHANS ; i + + ) {
logD ( " optimizing channel %d... " , i ) ;
std : : vector < std : : pair < int , int > > clearOuts = pat [ i ] . optimize ( ) ;
for ( auto & j : clearOuts ) {
2022-12-09 00:30:27 -05:00
for ( int k = 0 ; k < DIV_MAX_PATTERNS ; k + + ) {
2022-08-16 04:19:16 -04:00
if ( orders . ord [ i ] [ k ] = = j . first ) {
orders . ord [ i ] [ k ] = j . second ;
}
}
}
}
}
void DivSubSong : : rearrangePatterns ( ) {
for ( int i = 0 ; i < DIV_MAX_CHANS ; i + + ) {
logD ( " re-arranging channel %d... " , i ) ;
std : : vector < std : : pair < int , int > > clearOuts = pat [ i ] . rearrange ( ) ;
for ( auto & j : clearOuts ) {
2022-12-09 00:30:27 -05:00
for ( int k = 0 ; k < DIV_MAX_PATTERNS ; k + + ) {
2022-08-16 04:19:16 -04:00
if ( orders . ord [ i ] [ k ] = = j . first ) {
orders . ord [ i ] [ k ] = j . second ;
}
}
}
}
}
2024-02-21 18:05:39 -05:00
void DivSubSong : : sortOrders ( ) {
2024-04-29 19:13:44 -04:00
for ( int i = 0 ; i < DIV_MAX_CHANS ; i + + ) {
DivPattern * patPointer [ DIV_MAX_PATTERNS ] ;
unsigned char orderMap [ DIV_MAX_PATTERNS ] ;
bool seen [ DIV_MAX_PATTERNS ] ;
int orderMapLen = 0 ;
memcpy ( patPointer , pat [ i ] . data , DIV_MAX_PATTERNS * sizeof ( void * ) ) ;
memset ( orderMap , 0 , DIV_MAX_PATTERNS ) ;
memset ( seen , 0 , DIV_MAX_PATTERNS * sizeof ( bool ) ) ;
// 1. sort orders
for ( int j = 0 ; j < ordersLen ; j + + ) {
if ( ! seen [ orders . ord [ i ] [ j ] ] ) {
orderMap [ orders . ord [ i ] [ j ] ] = orderMapLen + + ;
seen [ orders . ord [ i ] [ j ] ] = true ;
}
}
// 2. populate the rest
for ( int j = 0 ; j < DIV_MAX_PATTERNS ; j + + ) {
if ( ! seen [ j ] ) orderMap [ j ] = orderMapLen + + ;
}
// 3. swap pattern pointers
for ( int j = 0 ; j < DIV_MAX_PATTERNS ; j + + ) {
pat [ i ] . data [ orderMap [ j ] ] = patPointer [ j ] ;
}
// 4. swap orders
for ( int j = 0 ; j < ordersLen ; j + + ) {
orders . ord [ i ] [ j ] = orderMap [ orders . ord [ i ] [ j ] ] ;
}
}
2024-02-21 18:05:39 -05:00
}
void DivSubSong : : makePatUnique ( ) {
2024-04-25 18:59:56 -04:00
for ( int i = 0 ; i < DIV_MAX_CHANS ; i + + ) {
logD ( " making channel %d unique... " , i ) ;
bool seen [ DIV_MAX_PATTERNS ] ;
bool used [ DIV_MAX_PATTERNS ] ;
memset ( seen , 0 , DIV_MAX_PATTERNS * sizeof ( bool ) ) ;
memset ( used , 0 , DIV_MAX_PATTERNS * sizeof ( bool ) ) ;
// 1. populate used patterns
for ( int j = 0 ; j < ordersLen ; j + + ) {
used [ orders . ord [ i ] [ j ] ] = true ;
}
// 2. make patterns unique
for ( int j = 0 ; j < ordersLen ; j + + ) {
if ( seen [ orders . ord [ i ] [ j ] ] ) {
for ( int k = 0 ; k < DIV_MAX_PATTERNS ; k + + ) {
if ( ! used [ k ] ) {
// copy here
DivPattern * dest = pat [ i ] . getPattern ( k , true ) ;
DivPattern * src = pat [ i ] . getPattern ( orders . ord [ i ] [ j ] , false ) ;
src - > copyOn ( dest ) ;
2024-04-29 19:26:26 -04:00
used [ k ] = true ;
2024-04-25 18:59:56 -04:00
orders . ord [ i ] [ j ] = k ;
break ;
}
}
} else {
seen [ orders . ord [ i ] [ j ] ] = true ;
}
}
}
2024-02-21 18:05:39 -05:00
}
2024-06-26 05:03:49 -04:00
void DivSong : : findSubSongs ( int chans ) {
std : : vector < DivSubSong * > newSubSongs ;
for ( DivSubSong * i : subsong ) {
std : : vector < int > subSongStart ;
std : : vector < int > subSongEnd ;
int loopOrder = 0 ;
int loopRow = 0 ;
int loopEnd = - 1 ;
int curStart = - 1 ;
// find possible subsongs
logD ( " finding subsongs... " ) ;
while ( + + curStart < i - > ordersLen ) {
if ( ! i - > walk ( loopOrder , loopRow , loopEnd , chans , jumpTreatment , ignoreJumpAtEnd , curStart ) ) break ;
// make sure we don't pick the same range twice
if ( ! subSongEnd . empty ( ) ) {
if ( subSongEnd . back ( ) = = loopEnd ) continue ;
}
logV ( " found a subsong: %d-%d " , curStart , loopEnd ) ;
subSongStart . push_back ( curStart ) ;
subSongEnd . push_back ( loopEnd ) ;
}
// if this is the only song, quit
if ( subSongStart . size ( ) < 2 ) {
subSongStart . clear ( ) ;
subSongEnd . clear ( ) ;
newSubSongs . clear ( ) ;
continue ;
}
// now copy the song
bool isTouched [ DIV_MAX_CHANS ] [ DIV_MAX_PATTERNS ] ;
memset ( isTouched , 0 , DIV_MAX_CHANS * DIV_MAX_PATTERNS * sizeof ( bool ) ) ;
for ( size_t j = 1 ; j < subSongStart . size ( ) ; j + + ) {
bool isUsed [ DIV_MAX_CHANS ] [ DIV_MAX_PATTERNS ] ;
int start = subSongStart [ j ] ;
int end = subSongEnd [ j ] ;
DivSubSong * theCopy = new DivSubSong ;
theCopy - > name = i - > name ;
theCopy - > notes = i - > notes ;
theCopy - > hilightA = i - > hilightA ;
theCopy - > hilightB = i - > hilightB ;
theCopy - > timeBase = i - > timeBase ;
theCopy - > arpLen = i - > arpLen ;
theCopy - > speeds = i - > speeds ;
theCopy - > virtualTempoN = i - > virtualTempoN ;
theCopy - > virtualTempoD = i - > virtualTempoD ;
theCopy - > hz = i - > hz ;
theCopy - > patLen = i - > patLen ;
// copy orders
memset ( isUsed , 0 , DIV_MAX_CHANS * DIV_MAX_PATTERNS * sizeof ( bool ) ) ;
for ( int k = start , kIndex = 0 ; k < = end ; k + + , kIndex + + ) {
for ( int l = 0 ; l < DIV_MAX_CHANS ; l + + ) {
theCopy - > orders . ord [ l ] [ kIndex ] = i - > orders . ord [ l ] [ k ] ;
isUsed [ l ] [ i - > orders . ord [ l ] [ k ] ] = true ;
isTouched [ l ] [ i - > orders . ord [ l ] [ k ] ] = true ;
}
}
theCopy - > ordersLen = end - start + 1 ;
memcpy ( theCopy - > chanShow , i - > chanShow , DIV_MAX_CHANS * sizeof ( bool ) ) ;
memcpy ( theCopy - > chanShowChanOsc , i - > chanShowChanOsc , DIV_MAX_CHANS * sizeof ( bool ) ) ;
memcpy ( theCopy - > chanCollapse , i - > chanCollapse , DIV_MAX_CHANS ) ;
for ( int k = 0 ; k < DIV_MAX_CHANS ; k + + ) {
theCopy - > chanName [ k ] = i - > chanName [ k ] ;
theCopy - > chanShortName [ j ] = i - > chanShortName [ k ] ;
theCopy - > pat [ k ] . effectCols = i - > pat [ k ] . effectCols ;
for ( int l = 0 ; l < DIV_MAX_PATTERNS ; l + + ) {
if ( i - > pat [ k ] . data [ l ] = = NULL ) continue ;
if ( ! isUsed [ k ] [ l ] ) continue ;
DivPattern * origPat = i - > pat [ k ] . getPattern ( l , false ) ;
DivPattern * copyPat = theCopy - > pat [ k ] . getPattern ( l , true ) ;
origPat - > copyOn ( copyPat ) ;
}
}
newSubSongs . push_back ( theCopy ) ;
}
// and cut this one
i - > ordersLen = subSongEnd [ 0 ] + 1 ;
// remove taken patterns as well, as long as they're not used in the original subsong
// first unmark patterns which are used
for ( int j = subSongStart [ 0 ] ; j < = subSongEnd [ 0 ] ; j + + ) {
for ( int k = 0 ; k < DIV_MAX_CHANS ; k + + ) {
isTouched [ k ] [ i - > orders . ord [ k ] [ j ] ] = false ;
}
}
// then remove the rest
for ( int j = 0 ; j < DIV_MAX_CHANS ; j + + ) {
for ( int k = 0 ; k < DIV_MAX_PATTERNS ; k + + ) {
if ( isTouched [ j ] [ k ] ) {
if ( i - > pat [ j ] . data [ k ] ! = NULL ) {
delete i - > pat [ j ] . data [ k ] ;
i - > pat [ j ] . data [ k ] = NULL ;
}
}
}
}
}
// append every subsong we found
for ( DivSubSong * i : newSubSongs ) {
subsong . push_back ( i ) ;
}
2024-06-24 19:02:31 -04:00
}
2022-05-15 02:42:49 -04:00
void DivSong : : clearSongData ( ) {
for ( DivSubSong * i : subsong ) {
i - > clearData ( ) ;
delete i ;
}
subsong . clear ( ) ;
subsong . push_back ( new DivSubSong ) ;
}
2022-04-24 18:39:18 -04:00
void DivSong : : clearInstruments ( ) {
for ( DivInstrument * i : ins ) {
delete i ;
}
ins . clear ( ) ;
insLen = 0 ;
}
void DivSong : : clearWavetables ( ) {
for ( DivWavetable * i : wave ) {
delete i ;
}
wave . clear ( ) ;
waveLen = 0 ;
}
void DivSong : : clearSamples ( ) {
for ( DivSample * i : sample ) {
delete i ;
}
sample . clear ( ) ;
sampleLen = 0 ;
}
2021-12-15 14:15:44 -05:00
void DivSong : : unload ( ) {
for ( DivInstrument * i : ins ) {
delete i ;
}
ins . clear ( ) ;
2022-04-24 18:39:18 -04:00
insLen = 0 ;
2021-12-15 14:15:44 -05:00
for ( DivWavetable * i : wave ) {
delete i ;
}
wave . clear ( ) ;
2022-04-24 18:39:18 -04:00
waveLen = 0 ;
2021-12-15 14:15:44 -05:00
for ( DivSample * i : sample ) {
delete i ;
}
2021-12-15 17:32:08 -05:00
sample . clear ( ) ;
2022-04-24 18:39:18 -04:00
sampleLen = 0 ;
2021-12-15 17:32:08 -05:00
2022-05-15 02:42:49 -04:00
for ( DivSubSong * i : subsong ) {
i - > clearData ( ) ;
delete i ;
2021-12-15 17:32:08 -05:00
}
2022-05-15 02:42:49 -04:00
subsong . clear ( ) ;
2021-12-15 14:15:44 -05:00
}