2022-02-15 03:12:20 +00:00
/**
* Furnace Tracker - multi - system chiptune tracker
* Copyright ( C ) 2021 - 2022 tildearrow and contributors
*
* 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 .
*/
2022-08-04 05:51:47 +00:00
# include "dispatch.h"
2022-08-26 08:03:36 +00:00
# include "song.h"
2022-01-17 23:18:28 +00:00
# define _USE_MATH_DEFINES
2021-05-11 20:08:08 +00:00
# include "engine.h"
2021-12-09 07:38:55 +00:00
# include "instrument.h"
2021-05-11 20:08:08 +00:00
# include "safeReader.h"
# include "../ta-log.h"
2022-01-20 10:04:03 +00:00
# include "../fileutils.h"
2022-05-23 00:01:50 +00:00
# ifdef HAVE_SDL2
2022-06-23 21:25:51 +00:00
# include "../audio/sdlAudio.h"
2022-05-23 00:01:50 +00:00
# endif
2021-12-19 21:52:04 +00:00
# include <stdexcept>
2021-06-09 08:33:03 +00:00
# ifdef HAVE_JACK
# include "../audio/jack.h"
# endif
2021-05-14 19:16:48 +00:00
# include <math.h>
2022-07-19 22:01:19 +00:00
# include <float.h>
2022-05-23 00:01:50 +00:00
# ifdef HAVE_SNDFILE
2022-06-28 07:00:08 +00:00
# include "sfWrapper.h"
2022-05-23 00:01:50 +00:00
# endif
2021-12-17 08:33:12 +00:00
# include <fmt/printf.h>
2021-05-11 20:08:08 +00:00
2021-05-12 08:58:55 +00:00
void process ( void * u , float * * in , float * * out , int inChans , int outChans , unsigned int size ) {
( ( DivEngine * ) u ) - > nextBuf ( in , out , inChans , outChans , size ) ;
2021-05-11 20:08:08 +00:00
}
2022-04-19 23:44:05 +00:00
const char * DivEngine : : getEffectDesc ( unsigned char effect , int chan , bool notNull ) {
2022-01-20 18:48:20 +00:00
switch ( effect ) {
case 0x00 :
return " 00xy: Arpeggio " ;
case 0x01 :
return " 01xx: Pitch slide up " ;
case 0x02 :
return " 02xx: Pitch slide down " ;
case 0x03 :
return " 03xx: Portamento " ;
case 0x04 :
2022-02-15 06:46:03 +00:00
return " 04xy: Vibrato (x: speed; y: depth) " ;
2022-03-14 14:50:52 +00:00
case 0x07 :
return " 07xy: Tremolo (x: speed; y: depth) " ;
2022-01-20 18:48:20 +00:00
case 0x08 :
2022-02-15 06:46:03 +00:00
return " 08xy: Set panning (x: left; y: right) " ;
2022-01-20 18:48:20 +00:00
case 0x09 :
return " 09xx: Set speed 1 " ;
case 0x0a :
2022-02-15 06:46:03 +00:00
return " 0Axy: Volume slide (0y: down; x0: up) " ;
2022-01-20 18:48:20 +00:00
case 0x0b :
return " 0Bxx: Jump to pattern " ;
case 0x0c :
return " 0Cxx: Retrigger " ;
case 0x0d :
return " 0Dxx: Jump to next pattern " ;
case 0x0f :
return " 0Fxx: Set speed 2 " ;
2022-04-30 04:41:14 +00:00
case 0x80 :
return " 80xx: Set panning (00: left; 80: center; FF: right) " ;
case 0x81 :
return " 81xx: Set panning (left channel) " ;
case 0x82 :
return " 82xx: Set panning (right channel) " ;
2022-02-15 06:46:03 +00:00
case 0xc0 : case 0xc1 : case 0xc2 : case 0xc3 :
2022-03-14 20:59:42 +00:00
return " Cxxx: Set tick rate (hz) " ;
2022-01-20 18:48:20 +00:00
case 0xe0 :
return " E0xx: Set arp speed " ;
case 0xe1 :
2022-02-15 06:46:03 +00:00
return " E1xy: Note slide up (x: speed; y: semitones) " ;
2022-01-20 18:48:20 +00:00
case 0xe2 :
2022-02-15 06:46:03 +00:00
return " E2xy: Note slide down (x: speed; y: semitones) " ;
2022-01-20 18:48:20 +00:00
case 0xe3 :
2022-02-15 06:46:03 +00:00
return " E3xx: Set vibrato shape (0: up/down; 1: up only; 2: down only) " ;
2022-01-20 18:48:20 +00:00
case 0xe4 :
return " E4xx: Set vibrato range " ;
case 0xe5 :
2022-02-15 06:46:03 +00:00
return " E5xx: Set pitch (80: center) " ;
2022-01-20 18:48:20 +00:00
case 0xea :
return " EAxx: Legato " ;
case 0xeb :
return " EBxx: Set sample bank " ;
case 0xec :
return " ECxx: Note cut " ;
case 0xed :
return " EDxx: Note delay " ;
case 0xee :
return " EExx: Send external command " ;
case 0xef :
2022-02-15 06:46:03 +00:00
return " EFxx: Set global tuning (quirky!) " ;
2022-03-14 20:59:42 +00:00
case 0xf0 :
return " F0xx: Set tick rate (bpm) " ;
2022-03-14 14:50:52 +00:00
case 0xf1 :
2022-03-14 20:59:42 +00:00
return " F1xx: Single tick note slide up " ;
2022-03-14 14:50:52 +00:00
case 0xf2 :
2022-03-14 20:59:42 +00:00
return " F2xx: Single tick note slide down " ;
2022-03-27 01:58:33 +00:00
case 0xf3 :
return " F3xx: Fine volume slide up " ;
case 0xf4 :
return " F4xx: Fine volume slide down " ;
2022-03-14 14:50:52 +00:00
case 0xf8 :
2022-03-14 20:59:42 +00:00
return " F8xx: Single tick volume slide up " ;
2022-03-14 14:50:52 +00:00
case 0xf9 :
2022-03-14 20:59:42 +00:00
return " F9xx: Single tick volume slide down " ;
2022-03-14 14:50:52 +00:00
case 0xfa :
return " FAxx: Fast volume slide (0y: down; x0: up) " ;
2022-02-04 19:43:57 +00:00
case 0xff :
2022-02-17 08:15:51 +00:00
return " FFxx: Stop song " ;
2022-01-20 18:48:20 +00:00
default :
2022-03-14 14:50:52 +00:00
if ( ( effect & 0xf0 ) = = 0x90 ) {
return " 9xxx: Set sample offset*256 " ;
2022-04-19 23:44:05 +00:00
} else if ( chan > = 0 & & chan < chans ) {
2022-08-18 06:26:22 +00:00
DivSysDef * sysDef = sysDefs [ sysOfChan [ chan ] ] ;
auto iter = sysDef - > effectHandlers . find ( effect ) ;
if ( iter ! = sysDef - > effectHandlers . end ( ) ) {
return iter - > second . description ;
}
iter = sysDef - > postEffectHandlers . find ( effect ) ;
if ( iter ! = sysDef - > postEffectHandlers . end ( ) ) {
return iter - > second . description ;
}
2022-01-20 18:48:20 +00:00
}
break ;
}
2022-04-19 23:44:05 +00:00
return notNull ? " Invalid effect " : NULL ;
2022-01-20 18:48:20 +00:00
}
2022-01-27 06:04:26 +00:00
void DivEngine : : walkSong ( int & loopOrder , int & loopRow , int & loopEnd ) {
2022-01-25 20:06:29 +00:00
loopOrder = 0 ;
loopRow = 0 ;
2022-01-27 06:04:26 +00:00
loopEnd = - 1 ;
2022-01-25 20:06:29 +00:00
int nextOrder = - 1 ;
int nextRow = 0 ;
int effectVal = 0 ;
2022-09-10 07:34:47 +00:00
int lastSuspectedLoopEnd = - 1 ;
2022-01-25 20:06:29 +00:00
DivPattern * pat [ DIV_MAX_CHANS ] ;
2022-09-10 07:34:47 +00:00
unsigned char wsWalked [ 8192 ] ;
memset ( wsWalked , 0 , 8192 ) ;
2022-05-15 06:42:49 +00:00
for ( int i = 0 ; i < curSubSong - > ordersLen ; i + + ) {
2022-01-25 20:06:29 +00:00
for ( int j = 0 ; j < chans ; j + + ) {
2022-05-15 06:42:49 +00:00
pat [ j ] = curPat [ j ] . getPattern ( curOrders - > ord [ j ] [ i ] , false ) ;
2022-01-25 20:06:29 +00:00
}
2022-09-10 07:34:47 +00:00
if ( i > lastSuspectedLoopEnd ) {
lastSuspectedLoopEnd = i ;
}
2022-05-15 06:42:49 +00:00
for ( int j = nextRow ; j < curSubSong - > patLen ; j + + ) {
2022-01-25 20:06:29 +00:00
nextRow = 0 ;
2022-09-10 07:34:47 +00:00
bool changingOrder = false ;
bool jumpingOrder = false ;
if ( wsWalked [ ( ( i < < 5 ) + ( j > > 3 ) ) & 8191 ] & ( 1 < < ( j & 7 ) ) ) {
loopOrder = i ;
loopRow = j ;
loopEnd = lastSuspectedLoopEnd ;
return ;
}
2022-01-25 20:06:29 +00:00
for ( int k = 0 ; k < chans ; k + + ) {
2022-05-15 06:42:49 +00:00
for ( int l = 0 ; l < curPat [ k ] . effectCols ; l + + ) {
2022-01-25 20:06:29 +00:00
effectVal = pat [ k ] - > data [ j ] [ 5 + ( l < < 1 ) ] ;
if ( effectVal < 0 ) effectVal = 0 ;
if ( pat [ k ] - > data [ j ] [ 4 + ( l < < 1 ) ] = = 0x0d ) {
2022-09-10 07:34:47 +00:00
if ( song . jumpTreatment = = 2 ) {
if ( ( i < curSubSong - > ordersLen - 1 | | ! song . ignoreJumpAtEnd ) ) {
nextOrder = i + 1 ;
nextRow = effectVal ;
jumpingOrder = true ;
}
} else if ( song . jumpTreatment = = 1 ) {
if ( nextOrder = = - 1 & & ( i < curSubSong - > ordersLen - 1 | | ! song . ignoreJumpAtEnd ) ) {
nextOrder = i + 1 ;
nextRow = effectVal ;
jumpingOrder = true ;
}
} else {
if ( ( i < curSubSong - > ordersLen - 1 | | ! song . ignoreJumpAtEnd ) ) {
if ( ! changingOrder ) {
nextOrder = i + 1 ;
}
jumpingOrder = true ;
nextRow = effectVal ;
}
2022-01-27 06:04:26 +00:00
}
2022-01-25 20:06:29 +00:00
} else if ( pat [ k ] - > data [ j ] [ 4 + ( l < < 1 ) ] = = 0x0b ) {
2022-09-10 07:34:47 +00:00
if ( nextOrder = = - 1 | | song . jumpTreatment = = 0 ) {
2022-01-27 06:04:26 +00:00
nextOrder = effectVal ;
2022-09-10 07:34:47 +00:00
if ( song . jumpTreatment = = 1 | | song . jumpTreatment = = 2 | | ! jumpingOrder ) {
nextRow = 0 ;
}
changingOrder = true ;
2022-01-27 06:04:26 +00:00
}
2022-01-25 20:06:29 +00:00
}
}
}
2022-09-10 07:34:47 +00:00
wsWalked [ ( ( i < < 5 ) + ( j > > 3 ) ) & 8191 ] | = 1 < < ( j & 7 ) ;
2022-01-25 20:06:29 +00:00
if ( nextOrder ! = - 1 ) {
i = nextOrder - 1 ;
nextOrder = - 1 ;
break ;
}
}
}
}
2022-07-19 22:01:19 +00:00
# define EXPORT_BUFSIZE 2048
double DivEngine : : benchmarkPlayback ( ) {
float * outBuf [ 2 ] ;
outBuf [ 0 ] = new float [ EXPORT_BUFSIZE ] ;
outBuf [ 1 ] = new float [ EXPORT_BUFSIZE ] ;
curOrder = 0 ;
prevOrder = 0 ;
remainingLoops = 1 ;
playSub ( false ) ;
std : : chrono : : high_resolution_clock : : time_point timeStart = std : : chrono : : high_resolution_clock : : now ( ) ;
// benchmark
while ( playing ) {
nextBuf ( NULL , outBuf , 0 , 2 , EXPORT_BUFSIZE ) ;
}
std : : chrono : : high_resolution_clock : : time_point timeEnd = std : : chrono : : high_resolution_clock : : now ( ) ;
delete [ ] outBuf [ 0 ] ;
delete [ ] outBuf [ 1 ] ;
double t = ( double ) ( std : : chrono : : duration_cast < std : : chrono : : microseconds > ( timeEnd - timeStart ) . count ( ) ) / 1000000.0 ;
printf ( " [RESULT] %fs \n " , t ) ;
return t ;
}
double DivEngine : : benchmarkSeek ( ) {
double t [ 20 ] ;
curOrder = curSubSong - > ordersLen - 1 ;
prevOrder = curSubSong - > ordersLen - 1 ;
// benchmark
for ( int i = 0 ; i < 20 ; i + + ) {
std : : chrono : : high_resolution_clock : : time_point timeStart = std : : chrono : : high_resolution_clock : : now ( ) ;
playSub ( false ) ;
std : : chrono : : high_resolution_clock : : time_point timeEnd = std : : chrono : : high_resolution_clock : : now ( ) ;
t [ i ] = ( double ) ( std : : chrono : : duration_cast < std : : chrono : : microseconds > ( timeEnd - timeStart ) . count ( ) ) / 1000000.0 ;
printf ( " [#%d] %fs \n " , i + 1 , t [ i ] ) ;
}
double tMin = DBL_MAX ;
double tMax = 0.0 ;
double tAvg = 0.0 ;
for ( int i = 0 ; i < 20 ; i + + ) {
if ( t [ i ] < tMin ) tMin = t [ i ] ;
if ( t [ i ] > tMax ) tMax = t [ i ] ;
tAvg + = t [ i ] ;
}
tAvg / = 20.0 ;
printf ( " [RESULT] min %fs max %fs average %fs \n " , tMin , tMax , tAvg ) ;
return tAvg ;
}
2022-08-04 23:50:52 +00:00
# define WRITE_TICK \
if ( ! wroteTick ) { \
wroteTick = true ; \
if ( binary ) { \
if ( tick - lastTick > 255 ) { \
w - > writeC ( 0xfc ) ; \
w - > writeS ( tick - lastTick ) ; \
} else if ( tick - lastTick > 1 ) { \
w - > writeC ( 0xfd ) ; \
w - > writeC ( tick - lastTick ) ; \
} else { \
w - > writeC ( 0xfe ) ; \
} \
} else { \
w - > writeText ( fmt : : sprintf ( " >> TICK %d \n " , tick ) ) ; \
} \
lastTick = tick ; \
}
void writePackedCommandValues ( SafeWriter * w , const DivCommand & c ) {
w - > writeC ( c . cmd ) ;
switch ( c . cmd ) {
case DIV_CMD_NOTE_ON :
case DIV_CMD_HINT_LEGATO :
if ( c . value = = DIV_NOTE_NULL ) {
w - > writeC ( 0xff ) ;
} else {
w - > writeC ( c . value + 60 ) ;
}
break ;
case DIV_CMD_NOTE_OFF :
case DIV_CMD_NOTE_OFF_ENV :
case DIV_CMD_ENV_RELEASE :
break ;
case DIV_CMD_INSTRUMENT :
case DIV_CMD_HINT_VIBRATO_RANGE :
case DIV_CMD_HINT_VIBRATO_SHAPE :
case DIV_CMD_HINT_PITCH :
case DIV_CMD_HINT_VOLUME :
case DIV_CMD_SAMPLE_MODE :
case DIV_CMD_SAMPLE_FREQ :
case DIV_CMD_SAMPLE_BANK :
case DIV_CMD_SAMPLE_POS :
case DIV_CMD_SAMPLE_DIR :
case DIV_CMD_FM_HARD_RESET :
case DIV_CMD_FM_LFO :
case DIV_CMD_FM_LFO_WAVE :
case DIV_CMD_FM_FB :
case DIV_CMD_FM_EXTCH :
case DIV_CMD_FM_AM_DEPTH :
case DIV_CMD_FM_PM_DEPTH :
case DIV_CMD_STD_NOISE_FREQ :
case DIV_CMD_STD_NOISE_MODE :
case DIV_CMD_WAVE :
case DIV_CMD_GB_SWEEP_TIME :
case DIV_CMD_GB_SWEEP_DIR :
case DIV_CMD_PCE_LFO_MODE :
case DIV_CMD_PCE_LFO_SPEED :
case DIV_CMD_NES_DMC :
case DIV_CMD_C64_CUTOFF :
case DIV_CMD_C64_RESONANCE :
case DIV_CMD_C64_FILTER_MODE :
case DIV_CMD_C64_RESET_TIME :
case DIV_CMD_C64_RESET_MASK :
case DIV_CMD_C64_FILTER_RESET :
case DIV_CMD_C64_DUTY_RESET :
case DIV_CMD_C64_EXTENDED :
case DIV_CMD_AY_ENVELOPE_SET :
case DIV_CMD_AY_ENVELOPE_LOW :
case DIV_CMD_AY_ENVELOPE_HIGH :
case DIV_CMD_AY_ENVELOPE_SLIDE :
case DIV_CMD_AY_NOISE_MASK_AND :
case DIV_CMD_AY_NOISE_MASK_OR :
case DIV_CMD_AY_AUTO_ENVELOPE :
w - > writeC ( c . value ) ;
break ;
case DIV_CMD_PANNING :
case DIV_CMD_HINT_VIBRATO :
case DIV_CMD_HINT_ARPEGGIO :
case DIV_CMD_HINT_PORTA :
case DIV_CMD_FM_TL :
case DIV_CMD_FM_AM :
case DIV_CMD_FM_AR :
case DIV_CMD_FM_DR :
case DIV_CMD_FM_SL :
case DIV_CMD_FM_D2R :
case DIV_CMD_FM_RR :
case DIV_CMD_FM_DT :
case DIV_CMD_FM_DT2 :
case DIV_CMD_FM_RS :
case DIV_CMD_FM_KSR :
case DIV_CMD_FM_VIB :
case DIV_CMD_FM_SUS :
case DIV_CMD_FM_WS :
case DIV_CMD_FM_SSG :
case DIV_CMD_FM_REV :
case DIV_CMD_FM_EG_SHIFT :
case DIV_CMD_FM_MULT :
case DIV_CMD_FM_FINE :
case DIV_CMD_AY_IO_WRITE :
case DIV_CMD_AY_AUTO_PWM :
w - > writeC ( c . value ) ;
w - > writeC ( c . value2 ) ;
break ;
case DIV_CMD_PRE_PORTA :
w - > writeC ( ( c . value ? 0x80 : 0 ) | ( c . value2 ? 0x40 : 0 ) ) ;
break ;
case DIV_CMD_HINT_VOL_SLIDE :
case DIV_CMD_C64_FINE_DUTY :
case DIV_CMD_C64_FINE_CUTOFF :
w - > writeS ( c . value ) ;
break ;
case DIV_CMD_FM_FIXFREQ :
w - > writeS ( ( c . value < < 12 ) | ( c . value2 & 0x7ff ) ) ;
break ;
case DIV_CMD_NES_SWEEP :
w - > writeC ( ( c . value ? 8 : 0 ) | ( c . value2 & 0x77 ) ) ;
break ;
default :
logW ( " unimplemented command %s! " , cmdName [ c . cmd ] ) ;
break ;
}
}
2022-08-04 05:51:47 +00:00
SafeWriter * DivEngine : : saveCommand ( bool binary ) {
stop ( ) ;
repeatPattern = false ;
2022-09-29 05:27:40 +00:00
shallStop = false ;
2022-08-04 05:51:47 +00:00
setOrder ( 0 ) ;
BUSY_BEGIN_SOFT ;
// determine loop point
int loopOrder = 0 ;
int loopRow = 0 ;
int loopEnd = 0 ;
walkSong ( loopOrder , loopRow , loopEnd ) ;
logI ( " loop point: %d %d " , loopOrder , loopRow ) ;
SafeWriter * w = new SafeWriter ;
w - > init ( ) ;
// write header
2022-08-04 23:50:52 +00:00
if ( binary ) {
w - > write ( " FCS " , 4 ) ;
} else {
w - > writeText ( " # Furnace Command Stream \n \n " ) ;
2022-08-04 05:51:47 +00:00
2022-08-04 23:50:52 +00:00
w - > writeText ( " [Information] \n " ) ;
w - > writeText ( fmt : : sprintf ( " name: %s \n " , song . name ) ) ;
w - > writeText ( fmt : : sprintf ( " author: %s \n " , song . author ) ) ;
w - > writeText ( fmt : : sprintf ( " category: %s \n " , song . category ) ) ;
w - > writeText ( fmt : : sprintf ( " system: %s \n " , song . systemName ) ) ;
2022-08-04 05:51:47 +00:00
2022-08-04 23:50:52 +00:00
w - > writeText ( " \n " ) ;
2022-08-04 05:51:47 +00:00
2022-08-04 23:50:52 +00:00
w - > writeText ( " [SubSongInformation] \n " ) ;
w - > writeText ( fmt : : sprintf ( " name: %s \n " , curSubSong - > name ) ) ;
w - > writeText ( fmt : : sprintf ( " tickRate: %f \n " , curSubSong - > hz ) ) ;
2022-08-04 05:51:47 +00:00
2022-08-04 23:50:52 +00:00
w - > writeText ( " \n " ) ;
2022-08-04 05:51:47 +00:00
2022-08-04 23:50:52 +00:00
w - > writeText ( " [SysDefinition] \n " ) ;
// TODO
2022-08-04 05:51:47 +00:00
2022-08-04 23:50:52 +00:00
w - > writeText ( " \n " ) ;
}
2022-08-04 05:51:47 +00:00
// play the song ourselves
bool done = false ;
playSub ( false ) ;
2022-08-04 23:50:52 +00:00
if ( ! binary ) {
w - > writeText ( " [Stream] \n " ) ;
}
2022-08-04 05:51:47 +00:00
int tick = 0 ;
bool oldCmdStreamEnabled = cmdStreamEnabled ;
cmdStreamEnabled = true ;
double curDivider = divider ;
2022-08-04 23:50:52 +00:00
int lastTick = 0 ;
2022-08-04 05:51:47 +00:00
while ( ! done ) {
if ( nextTick ( false , true ) | | ! playing ) {
done = true ;
}
// get command stream
bool wroteTick = false ;
if ( curDivider ! = divider ) {
curDivider = divider ;
2022-08-04 23:50:52 +00:00
WRITE_TICK ;
if ( binary ) {
w - > writeC ( 0xfb ) ;
w - > writeI ( ( int ) ( curDivider * 65536 ) ) ;
} else {
w - > writeText ( fmt : : sprintf ( " >> SET_RATE %f \n " , curDivider ) ) ;
2022-08-04 05:51:47 +00:00
}
}
for ( DivCommand & i : cmdStream ) {
switch ( i . cmd ) {
// strip away hinted/useless commands
case DIV_ALWAYS_SET_VOLUME :
break ;
case DIV_CMD_GET_VOLUME :
break ;
case DIV_CMD_VOLUME :
break ;
case DIV_CMD_NOTE_PORTA :
break ;
case DIV_CMD_LEGATO :
break ;
case DIV_CMD_PITCH :
break ;
2022-08-04 22:47:59 +00:00
case DIV_CMD_PRE_NOTE :
break ;
2022-08-04 05:51:47 +00:00
default :
2022-08-04 23:50:52 +00:00
WRITE_TICK ;
if ( binary ) {
w - > writeC ( i . chan ) ;
writePackedCommandValues ( w , i ) ;
} else {
w - > writeText ( fmt : : sprintf ( " %d: %s %d %d \n " , i . chan , cmdName [ i . cmd ] , i . value , i . value2 ) ) ;
2022-08-04 05:51:47 +00:00
}
break ;
}
}
cmdStream . clear ( ) ;
tick + + ;
}
cmdStreamEnabled = oldCmdStreamEnabled ;
2022-08-04 23:50:52 +00:00
if ( binary ) {
w - > writeC ( 0xff ) ;
2022-08-04 05:51:47 +00:00
} else {
2022-08-04 23:50:52 +00:00
if ( ! playing ) {
w - > writeText ( " >> END \n " ) ;
} else {
w - > writeText ( " >> LOOP 0 \n " ) ;
}
2022-08-04 05:51:47 +00:00
}
remainingLoops = - 1 ;
playing = false ;
freelance = false ;
extValuePresent = false ;
BUSY_END ;
return w ;
}
2022-01-18 04:34:29 +00:00
void _runExportThread ( DivEngine * caller ) {
caller - > runExportThread ( ) ;
}
bool DivEngine : : isExporting ( ) {
return exporting ;
}
2022-05-23 00:01:50 +00:00
# ifdef HAVE_SNDFILE
2022-01-18 04:34:29 +00:00
void DivEngine : : runExportThread ( ) {
2022-06-06 08:05:55 +00:00
size_t fadeOutSamples = got . rate * exportFadeOut ;
size_t curFadeOutSample = 0 ;
bool isFadingOut = false ;
2022-01-18 06:26:22 +00:00
switch ( exportMode ) {
case DIV_EXPORT_MODE_ONE : {
SNDFILE * sf ;
SF_INFO si ;
2022-06-28 07:00:08 +00:00
SFWrapper sfWrap ;
2022-01-18 06:26:22 +00:00
si . samplerate = got . rate ;
si . channels = 2 ;
si . format = SF_FORMAT_WAV | SF_FORMAT_PCM_16 ;
2022-06-28 07:00:08 +00:00
sf = sfWrap . doOpen ( exportPath . c_str ( ) , SFM_WRITE , & si ) ;
2022-01-18 06:26:22 +00:00
if ( sf = = NULL ) {
2022-04-11 03:12:02 +00:00
logE ( " could not open file for writing! (%s) " , sf_strerror ( NULL ) ) ;
2022-01-18 06:26:22 +00:00
exporting = false ;
return ;
}
2022-01-18 04:34:29 +00:00
2022-01-18 06:26:22 +00:00
float * outBuf [ 3 ] ;
outBuf [ 0 ] = new float [ EXPORT_BUFSIZE ] ;
outBuf [ 1 ] = new float [ EXPORT_BUFSIZE ] ;
outBuf [ 2 ] = new float [ EXPORT_BUFSIZE * 2 ] ;
2022-01-18 04:34:29 +00:00
2022-01-18 06:26:22 +00:00
// take control of audio output
deinitAudioBackend ( ) ;
playSub ( false ) ;
2022-01-18 04:34:29 +00:00
2022-04-11 03:12:02 +00:00
logI ( " rendering to file... " ) ;
2022-01-23 04:50:49 +00:00
2022-01-18 06:26:22 +00:00
while ( playing ) {
2022-06-06 08:05:55 +00:00
size_t total = 0 ;
2022-01-18 06:26:22 +00:00
nextBuf ( NULL , outBuf , 0 , 2 , EXPORT_BUFSIZE ) ;
if ( totalProcessed > EXPORT_BUFSIZE ) {
2022-04-11 03:12:02 +00:00
logE ( " error: total processed is bigger than export bufsize! %d>%d " , totalProcessed , EXPORT_BUFSIZE ) ;
2022-06-06 08:05:55 +00:00
totalProcessed = EXPORT_BUFSIZE ;
}
for ( int i = 0 ; i < ( int ) totalProcessed ; i + + ) {
total + + ;
if ( isFadingOut ) {
double mul = ( 1.0 - ( ( double ) curFadeOutSample / ( double ) fadeOutSamples ) ) ;
outBuf [ 2 ] [ i < < 1 ] = MAX ( - 1.0f , MIN ( 1.0f , outBuf [ 0 ] [ i ] ) ) * mul ;
outBuf [ 2 ] [ 1 + ( i < < 1 ) ] = MAX ( - 1.0f , MIN ( 1.0f , outBuf [ 1 ] [ i ] ) ) * mul ;
if ( + + curFadeOutSample > = fadeOutSamples ) {
playing = false ;
break ;
}
} else {
outBuf [ 2 ] [ i < < 1 ] = MAX ( - 1.0f , MIN ( 1.0f , outBuf [ 0 ] [ i ] ) ) ;
outBuf [ 2 ] [ 1 + ( i < < 1 ) ] = MAX ( - 1.0f , MIN ( 1.0f , outBuf [ 1 ] [ i ] ) ) ;
if ( lastLoopPos > - 1 & & i > = lastLoopPos & & totalLoops > = exportLoopCount ) {
logD ( " start fading out... " ) ;
isFadingOut = true ;
}
}
2022-01-18 06:26:22 +00:00
}
2022-06-06 08:05:55 +00:00
if ( sf_writef_float ( sf , outBuf [ 2 ] , total ) ! = ( int ) total ) {
2022-04-11 03:12:02 +00:00
logE ( " error: failed to write entire buffer! " ) ;
2022-01-18 06:26:22 +00:00
break ;
}
}
2022-01-18 07:04:03 +00:00
delete [ ] outBuf [ 0 ] ;
delete [ ] outBuf [ 1 ] ;
delete [ ] outBuf [ 2 ] ;
2022-06-28 07:00:08 +00:00
if ( sfWrap . doClose ( ) ! = 0 ) {
2022-04-11 03:12:02 +00:00
logE ( " could not close audio file! " ) ;
2022-01-18 06:26:22 +00:00
}
exporting = false ;
if ( initAudioBackend ( ) ) {
for ( int i = 0 ; i < song . systemLen ; i + + ) {
disCont [ i ] . setRates ( got . rate ) ;
disCont [ i ] . setQuality ( lowQuality ) ;
}
if ( ! output - > setRun ( true ) ) {
2022-04-11 03:12:02 +00:00
logE ( " error while activating audio! " ) ;
2022-01-18 06:26:22 +00:00
}
}
2022-04-11 03:12:02 +00:00
logI ( " done! " ) ;
2022-01-18 04:34:29 +00:00
break ;
}
2022-01-18 06:26:22 +00:00
case DIV_EXPORT_MODE_MANY_SYS : {
SNDFILE * sf [ 32 ] ;
SF_INFO si [ 32 ] ;
String fname [ 32 ] ;
2022-06-28 07:00:08 +00:00
SFWrapper sfWrap [ 32 ] ;
2022-01-18 06:26:22 +00:00
for ( int i = 0 ; i < song . systemLen ; i + + ) {
sf [ i ] = NULL ;
si [ i ] . samplerate = got . rate ;
if ( disCont [ i ] . dispatch - > isStereo ( ) ) {
si [ i ] . channels = 2 ;
} else {
si [ i ] . channels = 1 ;
}
si [ i ] . format = SF_FORMAT_WAV | SF_FORMAT_PCM_16 ;
}
2022-01-18 04:34:29 +00:00
2022-01-18 06:26:22 +00:00
for ( int i = 0 ; i < song . systemLen ; i + + ) {
2022-02-16 02:15:19 +00:00
fname [ i ] = fmt : : sprintf ( " %s_s%02d.wav " , exportPath , i + 1 ) ;
2022-04-11 03:12:02 +00:00
logI ( " - %s " , fname [ i ] . c_str ( ) ) ;
2022-06-28 07:00:08 +00:00
sf [ i ] = sfWrap [ i ] . doOpen ( fname [ i ] . c_str ( ) , SFM_WRITE , & si [ i ] ) ;
2022-01-18 06:26:22 +00:00
if ( sf [ i ] = = NULL ) {
2022-04-11 03:12:02 +00:00
logE ( " could not open file for writing! (%s) " , sf_strerror ( NULL ) ) ;
2022-01-18 06:26:22 +00:00
for ( int j = 0 ; j < i ; j + + ) {
2022-06-28 07:00:08 +00:00
sfWrap [ i ] . doClose ( ) ;
2022-01-18 06:26:22 +00:00
}
return ;
}
}
2022-01-18 04:34:29 +00:00
2022-01-18 06:26:22 +00:00
float * outBuf [ 2 ] ;
outBuf [ 0 ] = new float [ EXPORT_BUFSIZE ] ;
outBuf [ 1 ] = new float [ EXPORT_BUFSIZE ] ;
2022-06-06 09:00:56 +00:00
short * sysBuf [ 32 ] ;
for ( int i = 0 ; i < song . systemLen ; i + + ) {
sysBuf [ i ] = new short [ EXPORT_BUFSIZE * 2 ] ;
}
2022-01-18 06:26:22 +00:00
// take control of audio output
deinitAudioBackend ( ) ;
playSub ( false ) ;
2022-04-11 03:12:02 +00:00
logI ( " rendering to files... " ) ;
2022-01-23 04:50:49 +00:00
2022-01-18 06:26:22 +00:00
while ( playing ) {
2022-06-06 09:00:56 +00:00
size_t total = 0 ;
2022-01-18 06:26:22 +00:00
nextBuf ( NULL , outBuf , 0 , 2 , EXPORT_BUFSIZE ) ;
2022-06-06 09:00:56 +00:00
if ( totalProcessed > EXPORT_BUFSIZE ) {
logE ( " error: total processed is bigger than export bufsize! %d>%d " , totalProcessed , EXPORT_BUFSIZE ) ;
totalProcessed = EXPORT_BUFSIZE ;
}
for ( int j = 0 ; j < ( int ) totalProcessed ; j + + ) {
total + + ;
if ( isFadingOut ) {
double mul = ( 1.0 - ( ( double ) curFadeOutSample / ( double ) fadeOutSamples ) ) ;
for ( int i = 0 ; i < song . systemLen ; i + + ) {
if ( ! disCont [ i ] . dispatch - > isStereo ( ) ) {
sysBuf [ i ] [ j ] = ( double ) disCont [ i ] . bbOut [ 0 ] [ j ] * mul ;
} else {
sysBuf [ i ] [ j < < 1 ] = ( double ) disCont [ i ] . bbOut [ 0 ] [ j ] * mul ;
sysBuf [ i ] [ 1 + ( j < < 1 ) ] = ( double ) disCont [ i ] . bbOut [ 1 ] [ j ] * mul ;
}
}
if ( + + curFadeOutSample > = fadeOutSamples ) {
playing = false ;
break ;
}
} else {
for ( int i = 0 ; i < song . systemLen ; i + + ) {
if ( ! disCont [ i ] . dispatch - > isStereo ( ) ) {
sysBuf [ i ] [ j ] = disCont [ i ] . bbOut [ 0 ] [ j ] ;
} else {
sysBuf [ i ] [ j < < 1 ] = disCont [ i ] . bbOut [ 0 ] [ j ] ;
sysBuf [ i ] [ 1 + ( j < < 1 ) ] = disCont [ i ] . bbOut [ 1 ] [ j ] ;
}
}
if ( lastLoopPos > - 1 & & j > = lastLoopPos & & totalLoops > = exportLoopCount ) {
logD ( " start fading out... " ) ;
isFadingOut = true ;
2022-01-18 06:26:22 +00:00
}
}
2022-06-06 09:00:56 +00:00
}
for ( int i = 0 ; i < song . systemLen ; i + + ) {
if ( sf_writef_short ( sf [ i ] , sysBuf [ i ] , total ) ! = ( int ) total ) {
2022-04-11 03:12:02 +00:00
logE ( " error: failed to write entire buffer! (%d) " , i ) ;
2022-01-18 06:26:22 +00:00
break ;
}
}
}
2022-01-18 07:04:03 +00:00
delete [ ] outBuf [ 0 ] ;
delete [ ] outBuf [ 1 ] ;
2022-01-18 06:26:22 +00:00
for ( int i = 0 ; i < song . systemLen ; i + + ) {
2022-06-06 09:00:56 +00:00
delete [ ] sysBuf [ i ] ;
2022-06-28 07:00:08 +00:00
if ( sfWrap [ i ] . doClose ( ) ! = 0 ) {
2022-04-11 03:12:02 +00:00
logE ( " could not close audio file! " ) ;
2022-01-18 06:26:22 +00:00
}
}
exporting = false ;
if ( initAudioBackend ( ) ) {
for ( int i = 0 ; i < song . systemLen ; i + + ) {
disCont [ i ] . setRates ( got . rate ) ;
disCont [ i ] . setQuality ( lowQuality ) ;
}
if ( ! output - > setRun ( true ) ) {
2022-04-11 03:12:02 +00:00
logE ( " error while activating audio! " ) ;
2022-01-18 06:26:22 +00:00
}
}
2022-04-11 03:12:02 +00:00
logI ( " done! " ) ;
2022-01-18 06:26:22 +00:00
break ;
2022-01-18 04:34:29 +00:00
}
2022-01-18 06:26:22 +00:00
case DIV_EXPORT_MODE_MANY_CHAN : {
2022-01-18 07:04:03 +00:00
// take control of audio output
deinitAudioBackend ( ) ;
float * outBuf [ 3 ] ;
outBuf [ 0 ] = new float [ EXPORT_BUFSIZE ] ;
outBuf [ 1 ] = new float [ EXPORT_BUFSIZE ] ;
outBuf [ 2 ] = new float [ EXPORT_BUFSIZE * 2 ] ;
int loopCount = remainingLoops ;
2022-01-23 04:50:49 +00:00
2022-04-11 03:12:02 +00:00
logI ( " rendering to files... " ) ;
2022-01-18 07:04:03 +00:00
for ( int i = 0 ; i < chans ; i + + ) {
SNDFILE * sf ;
SF_INFO si ;
2022-06-28 07:00:08 +00:00
SFWrapper sfWrap ;
2022-02-16 02:15:19 +00:00
String fname = fmt : : sprintf ( " %s_c%02d.wav " , exportPath , i + 1 ) ;
2022-04-11 03:12:02 +00:00
logI ( " - %s " , fname . c_str ( ) ) ;
2022-01-18 07:04:03 +00:00
si . samplerate = got . rate ;
si . channels = 2 ;
si . format = SF_FORMAT_WAV | SF_FORMAT_PCM_16 ;
2022-06-28 07:00:08 +00:00
sf = sfWrap . doOpen ( fname . c_str ( ) , SFM_WRITE , & si ) ;
2022-01-18 07:04:03 +00:00
if ( sf = = NULL ) {
2022-04-11 03:12:02 +00:00
logE ( " could not open file for writing! (%s) " , sf_strerror ( NULL ) ) ;
2022-01-18 07:04:03 +00:00
break ;
}
for ( int j = 0 ; j < chans ; j + + ) {
bool mute = ( j ! = i ) ;
isMuted [ j ] = mute ;
2022-04-14 07:16:08 +00:00
}
if ( getChannelType ( i ) = = 5 ) {
for ( int j = i ; j < chans ; j + + ) {
if ( getChannelType ( j ) ! = 5 ) break ;
isMuted [ j ] = false ;
}
}
for ( int j = 0 ; j < chans ; j + + ) {
2022-01-18 07:04:03 +00:00
if ( disCont [ dispatchOfChan [ j ] ] . dispatch ! = NULL ) {
disCont [ dispatchOfChan [ j ] ] . dispatch - > muteChannel ( dispatchChanOfChan [ j ] , isMuted [ j ] ) ;
}
}
curOrder = 0 ;
2022-06-06 08:05:55 +00:00
prevOrder = 0 ;
2022-06-06 09:00:56 +00:00
curFadeOutSample = 0 ;
isFadingOut = false ;
2022-06-06 08:05:55 +00:00
if ( exportFadeOut < = 0.01 ) {
remainingLoops = loopCount ;
} else {
remainingLoops = - 1 ;
}
2022-01-18 07:04:03 +00:00
playSub ( false ) ;
while ( playing ) {
2022-06-06 09:00:56 +00:00
size_t total = 0 ;
2022-01-18 07:04:03 +00:00
nextBuf ( NULL , outBuf , 0 , 2 , EXPORT_BUFSIZE ) ;
if ( totalProcessed > EXPORT_BUFSIZE ) {
2022-04-11 03:12:02 +00:00
logE ( " error: total processed is bigger than export bufsize! %d>%d " , totalProcessed , EXPORT_BUFSIZE ) ;
2022-06-06 09:00:56 +00:00
totalProcessed = EXPORT_BUFSIZE ;
}
for ( int j = 0 ; j < ( int ) totalProcessed ; j + + ) {
total + + ;
if ( isFadingOut ) {
double mul = ( 1.0 - ( ( double ) curFadeOutSample / ( double ) fadeOutSamples ) ) ;
outBuf [ 2 ] [ j < < 1 ] = MAX ( - 1.0f , MIN ( 1.0f , outBuf [ 0 ] [ j ] ) ) * mul ;
outBuf [ 2 ] [ 1 + ( j < < 1 ) ] = MAX ( - 1.0f , MIN ( 1.0f , outBuf [ 1 ] [ j ] ) ) * mul ;
if ( + + curFadeOutSample > = fadeOutSamples ) {
playing = false ;
break ;
}
} else {
outBuf [ 2 ] [ j < < 1 ] = MAX ( - 1.0f , MIN ( 1.0f , outBuf [ 0 ] [ j ] ) ) ;
outBuf [ 2 ] [ 1 + ( j < < 1 ) ] = MAX ( - 1.0f , MIN ( 1.0f , outBuf [ 1 ] [ j ] ) ) ;
if ( lastLoopPos > - 1 & & j > = lastLoopPos & & totalLoops > = exportLoopCount ) {
logD ( " start fading out... " ) ;
isFadingOut = true ;
}
}
2022-01-18 07:04:03 +00:00
}
2022-06-06 09:00:56 +00:00
if ( sf_writef_float ( sf , outBuf [ 2 ] , total ) ! = ( int ) total ) {
2022-04-11 03:12:02 +00:00
logE ( " error: failed to write entire buffer! " ) ;
2022-01-18 07:04:03 +00:00
break ;
}
}
2022-06-28 07:00:08 +00:00
if ( sfWrap . doClose ( ) ! = 0 ) {
2022-04-11 03:12:02 +00:00
logE ( " could not close audio file! " ) ;
2022-01-18 07:04:03 +00:00
}
2022-04-14 07:16:08 +00:00
if ( getChannelType ( i ) = = 5 ) {
i + + ;
while ( true ) {
2022-04-18 09:42:51 +00:00
if ( i > = chans ) break ;
2022-04-14 07:16:08 +00:00
if ( getChannelType ( i ) ! = 5 ) break ;
2022-06-27 08:55:55 +00:00
i + + ;
2022-04-14 07:16:08 +00:00
}
i - - ;
}
2022-04-14 07:26:47 +00:00
if ( stopExport ) break ;
2022-01-18 07:04:03 +00:00
}
exporting = false ;
delete [ ] outBuf [ 0 ] ;
delete [ ] outBuf [ 1 ] ;
delete [ ] outBuf [ 2 ] ;
for ( int i = 0 ; i < chans ; i + + ) {
isMuted [ i ] = false ;
if ( disCont [ dispatchOfChan [ i ] ] . dispatch ! = NULL ) {
disCont [ dispatchOfChan [ i ] ] . dispatch - > muteChannel ( dispatchChanOfChan [ i ] , false ) ;
}
}
if ( initAudioBackend ( ) ) {
for ( int i = 0 ; i < song . systemLen ; i + + ) {
disCont [ i ] . setRates ( got . rate ) ;
disCont [ i ] . setQuality ( lowQuality ) ;
}
if ( ! output - > setRun ( true ) ) {
2022-04-11 03:12:02 +00:00
logE ( " error while activating audio! " ) ;
2022-01-18 07:04:03 +00:00
}
}
2022-04-11 03:12:02 +00:00
logI ( " done! " ) ;
2022-01-18 06:26:22 +00:00
break ;
2022-01-18 04:34:29 +00:00
}
}
2022-04-14 07:26:47 +00:00
stopExport = false ;
2022-01-18 04:34:29 +00:00
}
2022-05-23 00:01:50 +00:00
# else
void DivEngine : : runExportThread ( ) {
}
# endif
2022-01-18 04:34:29 +00:00
2022-06-06 08:05:55 +00:00
bool DivEngine : : saveAudio ( const char * path , int loops , DivAudioExportModes mode , double fadeOutTime ) {
2022-05-23 00:01:50 +00:00
# ifndef HAVE_SNDFILE
logE ( " Furnace was not compiled with libsndfile. cannot export! " ) ;
return false ;
# else
2022-01-18 04:34:29 +00:00
exportPath = path ;
2022-01-18 06:26:22 +00:00
exportMode = mode ;
2022-06-06 08:05:55 +00:00
exportFadeOut = fadeOutTime ;
2022-04-14 07:26:47 +00:00
if ( exportMode ! = DIV_EXPORT_MODE_ONE ) {
// remove extension
String lowerCase = exportPath ;
for ( char & i : lowerCase ) {
if ( i > = ' A ' & & i < = ' Z ' ) i + = ' a ' - ' A ' ;
}
size_t extPos = lowerCase . rfind ( " .wav " ) ;
if ( extPos ! = String : : npos ) {
exportPath = exportPath . substr ( 0 , extPos ) ;
}
}
2022-01-18 04:34:29 +00:00
exporting = true ;
2022-04-14 07:26:47 +00:00
stopExport = false ;
2022-01-18 04:34:29 +00:00
stop ( ) ;
2022-02-22 04:05:41 +00:00
repeatPattern = false ;
2022-01-18 04:34:29 +00:00
setOrder ( 0 ) ;
2022-06-06 08:05:55 +00:00
if ( exportFadeOut < = 0.01 ) {
remainingLoops = loops ;
} else {
remainingLoops = - 1 ;
}
exportLoopCount = loops ;
2022-01-18 04:34:29 +00:00
exportThread = new std : : thread ( _runExportThread , this ) ;
return true ;
2022-05-23 00:01:50 +00:00
# endif
2022-01-18 04:34:29 +00:00
}
void DivEngine : : waitAudioFile ( ) {
if ( exportThread ! = NULL ) {
exportThread - > join ( ) ;
}
}
bool DivEngine : : haltAudioFile ( ) {
2022-04-14 07:26:47 +00:00
stopExport = true ;
2022-01-18 04:34:29 +00:00
stop ( ) ;
return true ;
}
2022-01-18 04:59:52 +00:00
void DivEngine : : notifyInsChange ( int ins ) {
2022-03-24 02:38:28 +00:00
BUSY_BEGIN ;
2022-01-18 04:59:52 +00:00
for ( int i = 0 ; i < song . systemLen ; i + + ) {
disCont [ i ] . dispatch - > notifyInsChange ( ins ) ;
}
2022-03-24 02:38:28 +00:00
BUSY_END ;
2022-01-18 04:59:52 +00:00
}
2022-01-18 05:25:10 +00:00
void DivEngine : : notifyWaveChange ( int wave ) {
2022-03-24 02:38:28 +00:00
BUSY_BEGIN ;
2022-01-18 05:25:10 +00:00
for ( int i = 0 ; i < song . systemLen ; i + + ) {
disCont [ i ] . dispatch - > notifyWaveChange ( wave ) ;
}
2022-03-24 02:38:28 +00:00
BUSY_END ;
2022-01-18 05:25:10 +00:00
}
2022-05-13 03:15:03 +00:00
int DivEngine : : loadSampleROM ( String path , ssize_t expectedSize , unsigned char * & ret ) {
2022-05-11 21:39:36 +00:00
ret = NULL ;
if ( path . empty ( ) ) {
return 0 ;
}
logI ( " loading ROM %s... " , path ) ;
FILE * f = ps_fopen ( path . c_str ( ) , " rb " ) ;
if ( f = = NULL ) {
logE ( " error: %s " , strerror ( errno ) ) ;
lastError = strerror ( errno ) ;
return - 1 ;
}
if ( fseek ( f , 0 , SEEK_END ) < 0 ) {
logE ( " size error: %s " , strerror ( errno ) ) ;
lastError = fmt : : sprintf ( " on seek: %s " , strerror ( errno ) ) ;
fclose ( f ) ;
return - 1 ;
}
ssize_t len = ftell ( f ) ;
if ( len = = ( SIZE_MAX > > 1 ) ) {
logE ( " could not get file length: %s " , strerror ( errno ) ) ;
lastError = fmt : : sprintf ( " on pre tell: %s " , strerror ( errno ) ) ;
fclose ( f ) ;
return - 1 ;
}
if ( len < 1 ) {
if ( len = = 0 ) {
logE ( " that file is empty! " ) ;
lastError = " file is empty " ;
} else {
logE ( " tell error: %s " , strerror ( errno ) ) ;
lastError = fmt : : sprintf ( " on tell: %s " , strerror ( errno ) ) ;
}
fclose ( f ) ;
return - 1 ;
}
if ( len ! = expectedSize ) {
logE ( " ROM size mismatch, expected: %d bytes, was: %d bytes " , expectedSize , len ) ;
lastError = fmt : : sprintf ( " ROM size mismatch, expected: %d bytes, was: %d " , expectedSize , len ) ;
return - 1 ;
}
if ( fseek ( f , 0 , SEEK_SET ) < 0 ) {
logE ( " size error: %s " , strerror ( errno ) ) ;
lastError = fmt : : sprintf ( " on get size: %s " , strerror ( errno ) ) ;
fclose ( f ) ;
return - 1 ;
}
unsigned char * file = new unsigned char [ len ] ;
if ( fread ( file , 1 , ( size_t ) len , f ) ! = ( size_t ) len ) {
logE ( " read error: %s " , strerror ( errno ) ) ;
lastError = fmt : : sprintf ( " on read: %s " , strerror ( errno ) ) ;
fclose ( f ) ;
delete [ ] file ;
return - 1 ;
}
fclose ( f ) ;
ret = file ;
return 0 ;
}
2022-10-02 06:32:12 +00:00
unsigned int DivEngine : : getSampleFormatMask ( ) {
unsigned int formatMask = 1U < < 16 ; // 16-bit is always on
for ( int i = 0 ; i < song . systemLen ; i + + ) {
const DivSysDef * s = getSystemDef ( song . system [ i ] ) ;
if ( s = = NULL ) continue ;
formatMask | = s - > sampleFormatMask ;
}
return formatMask ;
}
2022-05-13 03:15:03 +00:00
int DivEngine : : loadSampleROMs ( ) {
if ( yrw801ROM ! = NULL ) {
delete [ ] yrw801ROM ;
yrw801ROM = NULL ;
}
if ( tg100ROM ! = NULL ) {
delete [ ] tg100ROM ;
tg100ROM = NULL ;
}
if ( mu5ROM ! = NULL ) {
delete [ ] mu5ROM ;
mu5ROM = NULL ;
}
2022-05-11 21:39:36 +00:00
int error = 0 ;
2022-05-13 03:15:03 +00:00
error + = loadSampleROM ( getConfString ( " yrw801Path " , " " ) , 0x200000 , yrw801ROM ) ;
error + = loadSampleROM ( getConfString ( " tg100Path " , " " ) , 0x200000 , tg100ROM ) ;
error + = loadSampleROM ( getConfString ( " mu5Path " , " " ) , 0x200000 , mu5ROM ) ;
2022-05-11 21:39:36 +00:00
return error ;
}
2021-12-17 08:33:12 +00:00
void DivEngine : : renderSamplesP ( ) {
2022-03-24 02:38:28 +00:00
BUSY_BEGIN ;
2021-12-17 08:33:12 +00:00
renderSamples ( ) ;
2022-03-24 02:38:28 +00:00
BUSY_END ;
2021-12-17 08:33:12 +00:00
}
2021-05-13 07:39:26 +00:00
void DivEngine : : renderSamples ( ) {
2021-12-21 21:02:31 +00:00
sPreview . sample = - 1 ;
sPreview . pos = 0 ;
2022-08-11 13:21:54 +00:00
sPreview . dir = false ;
2021-12-10 09:22:13 +00:00
2022-10-02 06:32:12 +00:00
logD ( " rendering samples... " ) ;
2022-09-25 22:18:04 +00:00
// step 0: make sample format mask
unsigned int formatMask = 1U < < 16 ; // 16-bit is always on
for ( int i = 0 ; i < song . systemLen ; i + + ) {
const DivSysDef * s = getSystemDef ( song . system [ i ] ) ;
if ( s = = NULL ) continue ;
formatMask | = s - > sampleFormatMask ;
}
2022-02-24 21:16:02 +00:00
// step 1: render samples
2021-05-13 07:39:26 +00:00
for ( int i = 0 ; i < song . sampleLen ; i + + ) {
2022-09-25 22:18:04 +00:00
song . sample [ i ] - > render ( formatMask ) ;
2021-05-13 07:39:26 +00:00
}
2021-12-11 04:41:00 +00:00
2022-05-01 17:57:44 +00:00
// step 2: render samples to dispatch
for ( int i = 0 ; i < song . systemLen ; i + + ) {
if ( disCont [ i ] . dispatch ! = NULL ) {
disCont [ i ] . dispatch - > renderSamples ( ) ;
2022-03-06 17:31:03 +00:00
}
}
2021-05-13 07:39:26 +00:00
}
2022-09-30 05:26:54 +00:00
String DivEngine : : decodeSysDesc ( String desc ) {
DivConfig newDesc ;
2022-04-30 06:37:37 +00:00
bool hasVal = false ;
bool negative = false ;
int val = 0 ;
int curStage = 0 ;
2022-04-30 09:30:33 +00:00
int sysID = 0 ;
int sysVol = 0 ;
int sysPan = 0 ;
int sysFlags = 0 ;
2022-09-30 05:26:54 +00:00
int curSys = 0 ;
2022-04-30 06:37:37 +00:00
desc + = ' ' ; // ha
for ( char i : desc ) {
switch ( i ) {
case ' ' :
if ( hasVal ) {
if ( negative ) val = - val ;
switch ( curStage ) {
case 0 :
sysID = val ;
curStage + + ;
break ;
case 1 :
sysVol = val ;
curStage + + ;
break ;
case 2 :
sysPan = val ;
curStage + + ;
break ;
case 3 :
sysFlags = val ;
2022-09-30 05:26:54 +00:00
if ( sysID ! = 0 ) {
2022-04-30 06:37:37 +00:00
if ( sysVol < - 128 ) sysVol = - 128 ;
if ( sysVol > 127 ) sysVol = 127 ;
if ( sysPan < - 128 ) sysPan = - 128 ;
if ( sysPan > 127 ) sysPan = 127 ;
2022-09-30 05:26:54 +00:00
newDesc . set ( fmt : : sprintf ( " id%d " , curSys ) , sysID ) ;
newDesc . set ( fmt : : sprintf ( " vol%d " , curSys ) , sysVol ) ;
newDesc . set ( fmt : : sprintf ( " pan%d " , curSys ) , sysPan ) ;
DivConfig newFlagsC ;
newFlagsC . clear ( ) ;
convertOldFlags ( ( unsigned int ) sysFlags , newFlagsC , systemFromFileFur ( sysID ) ) ;
newDesc . set ( fmt : : sprintf ( " flags%d " , curSys ) , newFlagsC . toBase64 ( ) ) ;
curSys + + ;
2022-04-30 06:37:37 +00:00
}
curStage = 0 ;
break ;
}
hasVal = false ;
negative = false ;
val = 0 ;
}
break ;
case ' 0 ' : case ' 1 ' : case ' 2 ' : case ' 3 ' : case ' 4 ' :
case ' 5 ' : case ' 6 ' : case ' 7 ' : case ' 8 ' : case ' 9 ' :
val = ( val * 10 ) + ( i - ' 0 ' ) ;
hasVal = true ;
break ;
case ' - ' :
if ( ! hasVal ) negative = true ;
break ;
}
}
2022-09-30 05:26:54 +00:00
return newDesc . toBase64 ( ) ;
2022-04-30 06:37:37 +00:00
}
2022-09-30 05:26:54 +00:00
void DivEngine : : initSongWithDesc ( const char * description ) {
2022-04-30 06:37:37 +00:00
int chanCount = 0 ;
2022-09-30 05:26:54 +00:00
DivConfig c ;
c . loadFromBase64 ( description ) ;
int index = 0 ;
for ( ; index < 32 ; index + + ) {
song . system [ index ] = systemFromFileFur ( c . getInt ( fmt : : sprintf ( " id%d " , index ) , 0 ) ) ;
if ( song . system [ index ] = = DIV_SYSTEM_NULL ) {
break ;
2022-04-30 06:37:37 +00:00
}
2022-09-30 05:26:54 +00:00
chanCount + = getChannelCount ( song . system [ index ] ) ;
if ( chanCount > = DIV_MAX_CHANS ) {
song . system [ index ] = DIV_SYSTEM_NULL ;
break ;
}
song . systemVol [ index ] = c . getInt ( fmt : : sprintf ( " vol%d " , index ) , DIV_SYSTEM_NULL ) ;
song . systemPan [ index ] = c . getInt ( fmt : : sprintf ( " pan%d " , index ) , DIV_SYSTEM_NULL ) ;
song . systemFlags [ index ] . clear ( ) ;
String flags = c . getString ( fmt : : sprintf ( " flags%d " , index ) , " " ) ;
song . systemFlags [ index ] . loadFromBase64 ( flags . c_str ( ) ) ;
2022-04-30 06:37:37 +00:00
}
2022-09-30 05:26:54 +00:00
song . systemLen = index ;
2022-04-30 06:37:37 +00:00
}
2022-09-30 05:26:54 +00:00
void DivEngine : : createNew ( const char * description , String sysName ) {
2021-12-24 23:23:01 +00:00
quitDispatch ( ) ;
2022-03-24 02:38:28 +00:00
BUSY_BEGIN ;
2022-03-21 21:17:51 +00:00
saveLock . lock ( ) ;
2021-12-24 23:23:01 +00:00
song . unload ( ) ;
song = DivSong ( ) ;
2022-05-15 06:42:49 +00:00
changeSong ( 0 ) ;
2022-03-01 22:19:52 +00:00
if ( description ! = NULL ) {
2022-04-30 06:37:37 +00:00
initSongWithDesc ( description ) ;
2022-03-01 22:19:52 +00:00
}
2022-07-23 22:02:03 +00:00
if ( sysName = = " " ) {
2022-07-27 07:36:48 +00:00
song . systemName = getSongSystemLegacyName ( song , ! getConfInt ( " noMultiSystem " , 0 ) ) ;
2022-07-23 22:02:03 +00:00
} else {
song . systemName = sysName ;
}
2022-01-08 21:03:32 +00:00
recalcChans ( ) ;
2022-03-21 21:17:51 +00:00
saveLock . unlock ( ) ;
2022-03-24 02:38:28 +00:00
BUSY_END ;
2021-12-24 23:23:01 +00:00
initDispatch ( ) ;
2022-03-24 02:38:28 +00:00
BUSY_BEGIN ;
2022-05-01 17:57:44 +00:00
renderSamples ( ) ;
2021-12-24 23:23:01 +00:00
reset ( ) ;
2022-03-24 02:38:28 +00:00
BUSY_END ;
2021-12-24 23:23:01 +00:00
}
2022-04-28 08:36:15 +00:00
void DivEngine : : swapChannels ( int src , int dest ) {
logV ( " swapping channel %d with %d " , src , dest ) ;
if ( src = = dest ) {
logV ( " not swapping channels because it's the same channel! " , src , dest ) ;
return ;
}
for ( int i = 0 ; i < 256 ; i + + ) {
2022-05-15 06:42:49 +00:00
curOrders - > ord [ dest ] [ i ] ^ = curOrders - > ord [ src ] [ i ] ;
curOrders - > ord [ src ] [ i ] ^ = curOrders - > ord [ dest ] [ i ] ;
curOrders - > ord [ dest ] [ i ] ^ = curOrders - > ord [ src ] [ i ] ;
2022-04-28 08:36:15 +00:00
2022-05-15 06:42:49 +00:00
DivPattern * prev = curPat [ src ] . data [ i ] ;
curPat [ src ] . data [ i ] = curPat [ dest ] . data [ i ] ;
curPat [ dest ] . data [ i ] = prev ;
2022-04-28 08:36:15 +00:00
}
2022-05-15 06:42:49 +00:00
curPat [ src ] . effectCols ^ = curPat [ dest ] . effectCols ;
curPat [ dest ] . effectCols ^ = curPat [ src ] . effectCols ;
curPat [ src ] . effectCols ^ = curPat [ dest ] . effectCols ;
2022-04-28 19:03:58 +00:00
2022-05-15 06:42:49 +00:00
String prevChanName = curSubSong - > chanName [ src ] ;
String prevChanShortName = curSubSong - > chanShortName [ src ] ;
bool prevChanShow = curSubSong - > chanShow [ src ] ;
2022-08-31 07:52:35 +00:00
unsigned char prevChanCollapse = curSubSong - > chanCollapse [ src ] ;
2022-04-28 19:03:58 +00:00
2022-05-15 06:42:49 +00:00
curSubSong - > chanName [ src ] = curSubSong - > chanName [ dest ] ;
curSubSong - > chanShortName [ src ] = curSubSong - > chanShortName [ dest ] ;
curSubSong - > chanShow [ src ] = curSubSong - > chanShow [ dest ] ;
curSubSong - > chanCollapse [ src ] = curSubSong - > chanCollapse [ dest ] ;
curSubSong - > chanName [ dest ] = prevChanName ;
curSubSong - > chanShortName [ dest ] = prevChanShortName ;
curSubSong - > chanShow [ dest ] = prevChanShow ;
curSubSong - > chanCollapse [ dest ] = prevChanCollapse ;
2022-04-28 08:36:15 +00:00
}
void DivEngine : : stompChannel ( int ch ) {
logV ( " stomping channel %d " , ch ) ;
for ( int i = 0 ; i < 256 ; i + + ) {
2022-05-15 06:42:49 +00:00
curOrders - > ord [ ch ] [ i ] = 0 ;
}
curPat [ ch ] . wipePatterns ( ) ;
curPat [ ch ] . effectCols = 1 ;
curSubSong - > chanName [ ch ] = " " ;
curSubSong - > chanShortName [ ch ] = " " ;
curSubSong - > chanShow [ ch ] = true ;
curSubSong - > chanCollapse [ ch ] = false ;
}
void DivEngine : : changeSong ( size_t songIndex ) {
if ( songIndex > = song . subsong . size ( ) ) return ;
curSubSong = song . subsong [ songIndex ] ;
curPat = song . subsong [ songIndex ] - > pat ;
curOrders = & song . subsong [ songIndex ] - > orders ;
curSubSongIndex = songIndex ;
curOrder = 0 ;
curRow = 0 ;
2022-06-06 06:05:06 +00:00
prevOrder = 0 ;
prevRow = 0 ;
2022-04-28 08:36:15 +00:00
}
void DivEngine : : swapChannelsP ( int src , int dest ) {
if ( src < 0 | | src > = chans ) return ;
if ( dest < 0 | | dest > = chans ) return ;
BUSY_BEGIN ;
saveLock . lock ( ) ;
swapChannels ( src , dest ) ;
saveLock . unlock ( ) ;
BUSY_END ;
}
2022-05-15 06:42:49 +00:00
void DivEngine : : changeSongP ( size_t index ) {
if ( index > = song . subsong . size ( ) ) return ;
if ( index = = curSubSongIndex ) return ;
stop ( ) ;
BUSY_BEGIN ;
saveLock . lock ( ) ;
changeSong ( index ) ;
saveLock . unlock ( ) ;
BUSY_END ;
}
int DivEngine : : addSubSong ( ) {
if ( song . subsong . size ( ) > = 127 ) return - 1 ;
BUSY_BEGIN ;
saveLock . lock ( ) ;
song . subsong . push_back ( new DivSubSong ) ;
saveLock . unlock ( ) ;
BUSY_END ;
return song . subsong . size ( ) - 1 ;
}
bool DivEngine : : removeSubSong ( int index ) {
if ( song . subsong . size ( ) < = 1 ) return false ;
stop ( ) ;
BUSY_BEGIN ;
saveLock . lock ( ) ;
song . subsong [ index ] - > clearData ( ) ;
delete song . subsong [ index ] ;
song . subsong . erase ( song . subsong . begin ( ) + index ) ;
changeSong ( 0 ) ;
saveLock . unlock ( ) ;
BUSY_END ;
return true ;
}
2022-06-01 23:50:30 +00:00
void DivEngine : : moveSubSongUp ( size_t index ) {
if ( index < 1 | | index > = song . subsong . size ( ) ) return ;
BUSY_BEGIN ;
saveLock . lock ( ) ;
if ( index = = curSubSongIndex ) {
curSubSongIndex - - ;
} else if ( index - 1 = = curSubSongIndex ) {
curSubSongIndex + + ;
}
DivSubSong * prev = song . subsong [ index - 1 ] ;
song . subsong [ index - 1 ] = song . subsong [ index ] ;
song . subsong [ index ] = prev ;
saveLock . unlock ( ) ;
BUSY_END ;
}
void DivEngine : : moveSubSongDown ( size_t index ) {
if ( index > = song . subsong . size ( ) - 1 ) return ;
BUSY_BEGIN ;
saveLock . lock ( ) ;
if ( index = = curSubSongIndex ) {
curSubSongIndex + + ;
} else if ( index + 1 = = curSubSongIndex ) {
curSubSongIndex - - ;
}
DivSubSong * prev = song . subsong [ index + 1 ] ;
song . subsong [ index + 1 ] = song . subsong [ index ] ;
song . subsong [ index ] = prev ;
saveLock . unlock ( ) ;
BUSY_END ;
}
2022-05-17 06:42:21 +00:00
void DivEngine : : clearSubSongs ( ) {
BUSY_BEGIN ;
saveLock . lock ( ) ;
song . clearSongData ( ) ;
changeSong ( 0 ) ;
curOrder = 0 ;
2022-06-06 06:05:06 +00:00
prevOrder = 0 ;
2022-05-17 06:42:21 +00:00
saveLock . unlock ( ) ;
BUSY_END ;
}
2022-04-28 08:36:15 +00:00
void DivEngine : : changeSystem ( int index , DivSystem which , bool preserveOrder ) {
int chanCount = chans ;
2021-12-18 03:14:41 +00:00
quitDispatch ( ) ;
2022-03-24 02:38:28 +00:00
BUSY_BEGIN ;
2022-03-21 22:56:48 +00:00
saveLock . lock ( ) ;
2022-04-28 08:36:15 +00:00
if ( ! preserveOrder ) {
int firstChan = 0 ;
int chanMovement = getChannelCount ( which ) - getChannelCount ( song . system [ index ] ) ;
while ( dispatchOfChan [ firstChan ] ! = index ) firstChan + + ;
int lastChan = firstChan + getChannelCount ( song . system [ index ] ) ;
if ( chanMovement ! = 0 ) {
if ( chanMovement > 0 ) {
// add channels
for ( int i = chanCount + chanMovement - 1 ; i > = lastChan + chanMovement ; i - - ) {
swapChannels ( i , i - chanMovement ) ;
}
for ( int i = lastChan ; i < lastChan + chanMovement ; i + + ) {
stompChannel ( i ) ;
}
} else {
// remove channels
for ( int i = lastChan + chanMovement ; i < lastChan ; i + + ) {
stompChannel ( i ) ;
}
for ( int i = lastChan + chanMovement ; i < chanCount + chanMovement ; i + + ) {
swapChannels ( i , i - chanMovement ) ;
}
}
}
}
2022-01-08 21:03:32 +00:00
song . system [ index ] = which ;
2022-09-30 05:26:54 +00:00
song . systemFlags [ index ] . clear ( ) ;
2022-01-08 21:03:32 +00:00
recalcChans ( ) ;
2022-03-21 22:56:48 +00:00
saveLock . unlock ( ) ;
2022-03-24 02:38:28 +00:00
BUSY_END ;
2021-12-18 03:14:41 +00:00
initDispatch ( ) ;
2022-03-24 02:38:28 +00:00
BUSY_BEGIN ;
2021-12-18 03:14:41 +00:00
renderSamples ( ) ;
reset ( ) ;
2022-03-24 02:38:28 +00:00
BUSY_END ;
2021-12-18 03:14:41 +00:00
}
2022-01-08 23:18:23 +00:00
bool DivEngine : : addSystem ( DivSystem which ) {
if ( song . systemLen > 32 ) {
2022-03-15 23:36:24 +00:00
lastError = " max number of systems is 32 " ;
2022-01-08 23:18:23 +00:00
return false ;
}
2022-05-27 04:38:45 +00:00
if ( chans + getChannelCount ( which ) > DIV_MAX_CHANS ) {
lastError = fmt : : sprintf ( " max number of total channels is %d " , DIV_MAX_CHANS ) ;
2022-01-08 23:18:23 +00:00
return false ;
}
quitDispatch ( ) ;
2022-03-24 02:38:28 +00:00
BUSY_BEGIN ;
2022-03-21 22:56:48 +00:00
saveLock . lock ( ) ;
2022-03-15 23:34:41 +00:00
song . system [ song . systemLen ] = which ;
song . systemVol [ song . systemLen ] = 64 ;
song . systemPan [ song . systemLen ] = 0 ;
2022-09-30 05:26:54 +00:00
song . systemFlags [ song . systemLen + + ] . clear ( ) ;
2022-01-08 23:18:23 +00:00
recalcChans ( ) ;
2022-03-21 22:56:48 +00:00
saveLock . unlock ( ) ;
2022-03-24 02:38:28 +00:00
BUSY_END ;
2022-01-08 23:18:23 +00:00
initDispatch ( ) ;
2022-03-24 02:38:28 +00:00
BUSY_BEGIN ;
2022-01-08 23:18:23 +00:00
renderSamples ( ) ;
reset ( ) ;
2022-03-24 02:38:28 +00:00
BUSY_END ;
2022-01-08 23:18:23 +00:00
return true ;
}
2022-05-15 06:55:25 +00:00
// TODO: maybe issue with subsongs?
2022-04-28 08:36:15 +00:00
bool DivEngine : : removeSystem ( int index , bool preserveOrder ) {
2022-01-09 21:36:47 +00:00
if ( song . systemLen < = 1 ) {
lastError = " cannot remove the last one " ;
return false ;
}
if ( index < 0 | | index > = song . systemLen ) {
lastError = " invalid index " ;
return false ;
}
2022-04-28 08:36:15 +00:00
int chanCount = chans ;
2022-01-09 21:36:47 +00:00
quitDispatch ( ) ;
2022-03-24 02:38:28 +00:00
BUSY_BEGIN ;
2022-03-21 22:56:48 +00:00
saveLock . lock ( ) ;
2022-04-28 08:36:15 +00:00
if ( ! preserveOrder ) {
int firstChan = 0 ;
while ( dispatchOfChan [ firstChan ] ! = index ) firstChan + + ;
for ( int i = 0 ; i < getChannelCount ( song . system [ index ] ) ; i + + ) {
stompChannel ( i + firstChan ) ;
}
for ( int i = firstChan + getChannelCount ( song . system [ index ] ) ; i < chanCount ; i + + ) {
swapChannels ( i , i - getChannelCount ( song . system [ index ] ) ) ;
}
}
2022-01-09 21:36:47 +00:00
song . system [ index ] = DIV_SYSTEM_NULL ;
song . systemLen - - ;
for ( int i = index ; i < song . systemLen ; i + + ) {
2022-01-19 03:02:04 +00:00
song . system [ i ] = song . system [ i + 1 ] ;
2022-04-28 08:36:15 +00:00
song . systemVol [ i ] = song . systemVol [ i + 1 ] ;
song . systemPan [ i ] = song . systemPan [ i + 1 ] ;
2022-09-30 05:26:54 +00:00
song . systemFlags [ i ] = song . systemFlags [ i + 1 ] ;
2022-01-09 21:36:47 +00:00
}
recalcChans ( ) ;
2022-03-21 22:56:48 +00:00
saveLock . unlock ( ) ;
2022-03-24 02:38:28 +00:00
BUSY_END ;
2022-01-09 21:36:47 +00:00
initDispatch ( ) ;
2022-03-24 02:38:28 +00:00
BUSY_BEGIN ;
2022-01-09 21:36:47 +00:00
renderSamples ( ) ;
reset ( ) ;
2022-03-24 02:38:28 +00:00
BUSY_END ;
2022-01-09 21:36:47 +00:00
return true ;
}
2022-08-19 21:25:32 +00:00
bool DivEngine : : swapSystem ( int src , int dest , bool preserveOrder ) {
if ( src = = dest ) {
lastError = " source and destination are equal " ;
return false ;
}
if ( src < 0 | | src > = song . systemLen ) {
lastError = " invalid source index " ;
return false ;
}
if ( dest < 0 | | dest > = song . systemLen ) {
lastError = " invalid destination index " ;
return false ;
}
//int chanCount=chans;
quitDispatch ( ) ;
BUSY_BEGIN ;
saveLock . lock ( ) ;
if ( ! preserveOrder ) {
// move channels
2022-08-26 08:03:36 +00:00
unsigned char unswappedChannels [ DIV_MAX_CHANS ] ;
unsigned char swappedChannels [ DIV_MAX_CHANS ] ;
std : : vector < std : : vector < int > > swapList ;
std : : vector < int > chanList ;
int tchans = 0 ;
for ( int i = 0 ; i < song . systemLen ; i + + ) {
tchans + = getChannelCount ( song . system [ i ] ) ;
}
memset ( unswappedChannels , 0 , DIV_MAX_CHANS ) ;
memset ( swappedChannels , 0 , DIV_MAX_CHANS ) ;
for ( int i = 0 ; i < tchans ; i + + ) {
unswappedChannels [ i ] = i ;
}
// prepare swap list
int index = 0 ;
for ( int i = 0 ; i < song . systemLen ; i + + ) {
chanList . clear ( ) ;
for ( int j = 0 ; j < getChannelCount ( song . system [ i ] ) ; j + + ) {
chanList . push_back ( index ) ;
index + + ;
}
swapList . push_back ( chanList ) ;
}
swapList [ src ] . swap ( swapList [ dest ] ) ;
// unfold it
index = 0 ;
for ( std : : vector < int > & i : swapList ) {
for ( int & j : i ) {
swappedChannels [ index + + ] = j ;
}
}
2022-08-31 07:52:35 +00:00
// swap channels
2022-08-26 08:03:36 +00:00
logV ( " swap list: " ) ;
for ( int i = 0 ; i < tchans ; i + + ) {
logV ( " - %d -> %d " , unswappedChannels [ i ] , swappedChannels [ i ] ) ;
}
2022-08-31 07:52:35 +00:00
for ( size_t i = 0 ; i < song . subsong . size ( ) ; i + + ) {
DivOrders prevOrders = song . subsong [ i ] - > orders ;
DivPattern * prevPat [ DIV_MAX_CHANS ] [ 256 ] ;
unsigned char prevEffectCols [ DIV_MAX_CHANS ] ;
String prevChanName [ DIV_MAX_CHANS ] ;
String prevChanShortName [ DIV_MAX_CHANS ] ;
bool prevChanShow [ DIV_MAX_CHANS ] ;
unsigned char prevChanCollapse [ DIV_MAX_CHANS ] ;
for ( int j = 0 ; j < tchans ; j + + ) {
for ( int k = 0 ; k < 256 ; k + + ) {
prevPat [ j ] [ k ] = song . subsong [ i ] - > pat [ j ] . data [ k ] ;
2022-08-26 08:03:36 +00:00
}
2022-08-31 07:52:35 +00:00
prevEffectCols [ j ] = song . subsong [ i ] - > pat [ j ] . effectCols ;
prevChanName [ j ] = song . subsong [ i ] - > chanName [ j ] ;
prevChanShortName [ j ] = song . subsong [ i ] - > chanShortName [ j ] ;
prevChanShow [ j ] = song . subsong [ i ] - > chanShow [ j ] ;
prevChanCollapse [ j ] = song . subsong [ i ] - > chanCollapse [ j ] ;
}
for ( int j = 0 ; j < tchans ; j + + ) {
for ( int k = 0 ; k < 256 ; k + + ) {
song . subsong [ i ] - > orders . ord [ j ] [ k ] = prevOrders . ord [ swappedChannels [ j ] ] [ k ] ;
song . subsong [ i ] - > pat [ j ] . data [ k ] = prevPat [ swappedChannels [ j ] ] [ k ] ;
}
song . subsong [ i ] - > pat [ j ] . effectCols = prevEffectCols [ swappedChannels [ j ] ] ;
song . subsong [ i ] - > chanName [ j ] = prevChanName [ swappedChannels [ j ] ] ;
song . subsong [ i ] - > chanShortName [ j ] = prevChanShortName [ swappedChannels [ j ] ] ;
song . subsong [ i ] - > chanShow [ j ] = prevChanShow [ swappedChannels [ j ] ] ;
song . subsong [ i ] - > chanCollapse [ j ] = prevChanCollapse [ swappedChannels [ j ] ] ;
2022-08-26 08:03:36 +00:00
}
}
2022-08-19 21:25:32 +00:00
}
DivSystem srcSystem = song . system [ src ] ;
song . system [ src ] = song . system [ dest ] ;
song . system [ dest ] = srcSystem ;
song . systemVol [ src ] ^ = song . systemVol [ dest ] ;
song . systemVol [ dest ] ^ = song . systemVol [ src ] ;
song . systemVol [ src ] ^ = song . systemVol [ dest ] ;
song . systemPan [ src ] ^ = song . systemPan [ dest ] ;
song . systemPan [ dest ] ^ = song . systemPan [ src ] ;
song . systemPan [ src ] ^ = song . systemPan [ dest ] ;
2022-09-30 05:26:54 +00:00
// I am kinda scared to use std::swap
DivConfig oldFlags = song . systemFlags [ src ] ;
song . systemFlags [ src ] = song . systemFlags [ dest ] ;
song . systemFlags [ dest ] = oldFlags ;
2022-08-19 21:25:32 +00:00
recalcChans ( ) ;
saveLock . unlock ( ) ;
BUSY_END ;
initDispatch ( ) ;
BUSY_BEGIN ;
renderSamples ( ) ;
reset ( ) ;
BUSY_END ;
return true ;
}
2022-02-01 23:08:19 +00:00
void DivEngine : : poke ( int sys , unsigned int addr , unsigned short val ) {
if ( sys < 0 | | sys > = song . systemLen ) return ;
2022-03-24 02:38:28 +00:00
BUSY_BEGIN ;
2022-02-01 23:08:19 +00:00
disCont [ sys ] . dispatch - > poke ( addr , val ) ;
2022-03-24 02:38:28 +00:00
BUSY_END ;
2022-02-01 23:08:19 +00:00
}
void DivEngine : : poke ( int sys , std : : vector < DivRegWrite > & wlist ) {
if ( sys < 0 | | sys > = song . systemLen ) return ;
2022-03-24 02:38:28 +00:00
BUSY_BEGIN ;
2022-02-01 23:08:19 +00:00
disCont [ sys ] . dispatch - > poke ( wlist ) ;
2022-03-24 02:38:28 +00:00
BUSY_END ;
2022-02-01 23:08:19 +00:00
}
2021-12-21 04:20:30 +00:00
String DivEngine : : getLastError ( ) {
return lastError ;
}
2022-01-29 06:22:32 +00:00
String DivEngine : : getWarnings ( ) {
return warnings ;
}
2022-10-02 06:02:01 +00:00
String DivEngine : : getPlaybackDebugInfo ( ) {
return fmt : : sprintf (
" curOrder: %d \n "
" prevOrder: %d \n "
" curRow: %d \n "
" prevRow: %d \n "
" ticks: %d \n "
" subticks: %d \n "
" totalLoops: %d \n "
" lastLoopPos: %d \n "
" nextSpeed: %d \n "
" divider: %f \n "
" cycles: %d \n "
" clockDrift: %f \n "
" changeOrd: %d \n "
" changePos: %d \n "
" totalSeconds: %d \n "
" totalTicks: %d \n "
" totalTicksR: %d \n "
" totalCmds: %d \n "
" lastCmds: %d \n "
" cmdsPerSecond: %d \n "
" globalPitch: %d \n "
" extValue: %d \n "
" speed1: %d \n "
" speed2: %d \n "
" tempoAccum: %d \n "
" totalProcessed: %d \n " ,
curOrder , prevOrder , curRow , prevRow , ticks , subticks , totalLoops , lastLoopPos , nextSpeed , divider , cycles , clockDrift ,
changeOrd , changePos , totalSeconds , totalTicks , totalTicksR , totalCmds , lastCmds , cmdsPerSecond , globalPitch ,
( int ) extValue , ( int ) speed1 , ( int ) speed2 , ( int ) tempoAccum , ( int ) totalProcessed
) ;
}
2022-04-21 07:24:06 +00:00
DivInstrument * DivEngine : : getIns ( int index , DivInstrumentType fallbackType ) {
2022-04-25 23:58:17 +00:00
if ( index = = - 2 & & tempIns ! = NULL ) {
return tempIns ;
}
2022-04-21 07:24:06 +00:00
if ( index < 0 | | index > = song . insLen ) {
switch ( fallbackType ) {
case DIV_INS_OPLL :
return & song . nullInsOPLL ;
break ;
case DIV_INS_OPL :
return & song . nullInsOPL ;
break ;
2022-05-31 22:44:52 +00:00
case DIV_INS_OPL_DRUMS :
return & song . nullInsOPLDrums ;
break ;
2022-04-21 07:24:06 +00:00
default :
break ;
}
return & song . nullIns ;
}
2021-05-17 01:49:54 +00:00
return song . ins [ index ] ;
}
2021-05-27 18:30:37 +00:00
DivWavetable * DivEngine : : getWave ( int index ) {
2021-06-09 03:21:05 +00:00
if ( index < 0 | | index > = song . waveLen ) {
if ( song . waveLen > 0 ) {
return song . wave [ 0 ] ;
} else {
return & song . nullWave ;
}
}
2021-05-27 18:30:37 +00:00
return song . wave [ index ] ;
}
2022-02-25 03:52:20 +00:00
DivSample * DivEngine : : getSample ( int index ) {
if ( index < 0 | | index > = song . sampleLen ) return & song . nullSample ;
return song . sample [ index ] ;
}
2022-05-01 17:57:44 +00:00
DivDispatch * DivEngine : : getDispatch ( int index ) {
if ( index < 0 | | index > = song . systemLen ) return NULL ;
return disCont [ index ] . dispatch ;
}
2021-12-07 09:22:36 +00:00
void DivEngine : : setLoops ( int loops ) {
remainingLoops = loops ;
}
2022-01-27 05:29:16 +00:00
DivChannelState * DivEngine : : getChanState ( int ch ) {
if ( ch < 0 | | ch > = chans ) return NULL ;
return & chan [ ch ] ;
}
void * DivEngine : : getDispatchChanState ( int ch ) {
if ( ch < 0 | | ch > = chans ) return NULL ;
return disCont [ dispatchOfChan [ ch ] ] . dispatch - > getChanState ( dispatchChanOfChan [ ch ] ) ;
}
2022-02-22 09:01:57 +00:00
unsigned char * DivEngine : : getRegisterPool ( int sys , int & size , int & depth ) {
2022-02-22 03:31:27 +00:00
if ( sys < 0 | | sys > = song . systemLen ) return NULL ;
if ( disCont [ sys ] . dispatch = = NULL ) return NULL ;
size = disCont [ sys ] . dispatch - > getRegisterPoolSize ( ) ;
2022-02-22 09:01:57 +00:00
depth = disCont [ sys ] . dispatch - > getRegisterPoolDepth ( ) ;
2022-02-22 03:31:27 +00:00
return disCont [ sys ] . dispatch - > getRegisterPool ( ) ;
}
2022-04-30 08:58:30 +00:00
DivMacroInt * DivEngine : : getMacroInt ( int chan ) {
if ( chan < 0 | | chan > = chans ) return NULL ;
return disCont [ dispatchOfChan [ chan ] ] . dispatch - > getChanMacroInt ( dispatchChanOfChan [ chan ] ) ;
}
DivDispatchOscBuffer * DivEngine : : getOscBuffer ( int chan ) {
if ( chan < 0 | | chan > = chans ) return NULL ;
return disCont [ dispatchOfChan [ chan ] ] . dispatch - > getOscBuffer ( dispatchChanOfChan [ chan ] ) ;
}
2022-02-16 21:11:15 +00:00
void DivEngine : : enableCommandStream ( bool enable ) {
cmdStreamEnabled = enable ;
}
void DivEngine : : getCommandStream ( std : : vector < DivCommand > & where ) {
2022-03-24 02:38:28 +00:00
BUSY_BEGIN ;
2022-02-16 21:11:15 +00:00
where . clear ( ) ;
for ( DivCommand & i : cmdStream ) {
where . push_back ( i ) ;
}
cmdStream . clear ( ) ;
2022-03-24 02:38:28 +00:00
BUSY_END ;
2022-02-16 21:11:15 +00:00
}
2022-02-06 05:07:35 +00:00
void DivEngine : : playSub ( bool preserveDrift , int goalRow ) {
2022-04-10 22:24:41 +00:00
std : : chrono : : high_resolution_clock : : time_point timeStart = std : : chrono : : high_resolution_clock : : now ( ) ;
2022-02-21 04:03:42 +00:00
for ( int i = 0 ; i < song . systemLen ; i + + ) disCont [ i ] . dispatch - > setSkipRegisterWrites ( false ) ;
2021-12-11 21:51:34 +00:00
reset ( ) ;
2021-12-21 07:02:25 +00:00
if ( preserveDrift & & curOrder = = 0 ) return ;
2021-12-21 22:42:27 +00:00
bool oldRepeatPattern = repeatPattern ;
repeatPattern = false ;
2021-12-21 06:29:07 +00:00
int goal = curOrder ;
curOrder = 0 ;
2021-12-11 08:34:43 +00:00
curRow = 0 ;
2022-06-06 06:05:06 +00:00
prevOrder = 0 ;
prevRow = 0 ;
2022-02-06 05:42:07 +00:00
stepPlay = 0 ;
2022-01-12 22:45:07 +00:00
int prevDrift ;
prevDrift = clockDrift ;
clockDrift = 0 ;
cycles = 0 ;
2021-12-21 07:02:25 +00:00
if ( preserveDrift ) {
endOfSong = false ;
} else {
ticks = 1 ;
2022-05-18 05:05:25 +00:00
tempoAccum = 0 ;
2021-12-21 07:30:09 +00:00
totalTicks = 0 ;
2022-01-12 07:45:26 +00:00
totalSeconds = 0 ;
totalTicksR = 0 ;
2022-06-06 08:05:55 +00:00
totalLoops = 0 ;
lastLoopPos = - 1 ;
2021-12-21 07:02:25 +00:00
}
2021-12-12 09:21:09 +00:00
speedAB = false ;
2021-12-11 21:51:34 +00:00
playing = true ;
2022-03-31 08:33:05 +00:00
skipping = true ;
2022-09-10 06:39:42 +00:00
memset ( walked , 0 , 8192 ) ;
2022-01-08 21:03:32 +00:00
for ( int i = 0 ; i < song . systemLen ; i + + ) disCont [ i ] . dispatch - > setSkipRegisterWrites ( true ) ;
2022-10-02 06:02:01 +00:00
logV ( " goal: %d goalRow: %d " , goal , goalRow ) ;
2022-02-17 08:20:08 +00:00
while ( playing & & curOrder < goal ) {
2022-03-31 08:33:05 +00:00
if ( nextTick ( preserveDrift ) ) {
skipping = false ;
return ;
}
2021-12-21 06:29:07 +00:00
}
2022-02-06 05:07:35 +00:00
int oldOrder = curOrder ;
2022-08-31 08:05:06 +00:00
while ( playing & & ( curRow < goalRow | | ticks > 1 ) ) {
2022-03-31 08:33:05 +00:00
if ( nextTick ( preserveDrift ) ) {
skipping = false ;
return ;
}
2022-02-06 05:07:35 +00:00
if ( oldOrder ! = curOrder ) break ;
2022-10-02 06:02:01 +00:00
if ( ticks - ( ( tempoAccum + curSubSong - > virtualTempoN ) / MAX ( 1 , curSubSong - > virtualTempoD ) ) < 1 & & curRow > = goalRow ) break ;
2022-02-06 05:07:35 +00:00
}
2022-01-08 21:03:32 +00:00
for ( int i = 0 ; i < song . systemLen ; i + + ) disCont [ i ] . dispatch - > setSkipRegisterWrites ( false ) ;
2022-02-06 05:07:35 +00:00
if ( goal > 0 | | goalRow > 0 ) {
2022-01-08 21:03:32 +00:00
for ( int i = 0 ; i < song . systemLen ; i + + ) disCont [ i ] . dispatch - > forceIns ( ) ;
2021-12-21 21:02:31 +00:00
}
2022-02-14 07:43:56 +00:00
for ( int i = 0 ; i < chans ; i + + ) {
chan [ i ] . cut = - 1 ;
}
2021-12-21 22:42:27 +00:00
repeatPattern = oldRepeatPattern ;
2022-01-12 22:45:07 +00:00
if ( preserveDrift ) {
clockDrift = prevDrift ;
} else {
clockDrift = 0 ;
cycles = 0 ;
2021-12-21 07:02:25 +00:00
}
if ( ! preserveDrift ) {
ticks = 1 ;
2022-04-16 03:22:47 +00:00
subticks = 1 ;
2022-06-06 06:05:06 +00:00
prevOrder = curOrder ;
prevRow = curRow ;
2022-10-02 06:02:01 +00:00
tempoAccum = 0 ;
2021-12-21 07:02:25 +00:00
}
2022-03-31 08:33:05 +00:00
skipping = false ;
2022-02-16 21:11:15 +00:00
cmdStream . clear ( ) ;
2022-04-10 22:24:41 +00:00
std : : chrono : : high_resolution_clock : : time_point timeEnd = std : : chrono : : high_resolution_clock : : now ( ) ;
2022-04-11 03:16:42 +00:00
logV ( " playSub() took %dµs " , std : : chrono : : duration_cast < std : : chrono : : microseconds > ( timeEnd - timeStart ) . count ( ) ) ;
2021-12-21 06:29:07 +00:00
}
2022-03-24 04:19:16 +00:00
/*
2022-01-28 05:55:51 +00:00
int DivEngine : : calcBaseFreq ( double clock , double divider , int note , bool period ) {
double base = ( period ? ( song . tuning * 0.0625 ) : song . tuning ) * pow ( 2.0 , ( float ) ( note + 3 ) / 12.0 ) ;
return period ?
round ( ( clock / base ) / divider ) :
base * ( divider / clock ) ;
2022-03-24 04:19:16 +00:00
} */
double DivEngine : : calcBaseFreq ( double clock , double divider , int note , bool period ) {
2022-05-10 08:51:18 +00:00
if ( song . linearPitch = = 2 ) { // full linear
return ( note < < 7 ) ;
}
2022-03-24 04:19:16 +00:00
double base = ( period ? ( song . tuning * 0.0625 ) : song . tuning ) * pow ( 2.0 , ( float ) ( note + 3 ) / 12.0 ) ;
return period ?
( clock / base ) / divider :
base * ( divider / clock ) ;
2022-01-28 05:55:51 +00:00
}
2022-05-10 21:22:40 +00:00
# define CONVERT_FNUM_BLOCK(bf,bits,note) \
double tuning = song . tuning ; \
if ( tuning < 400.0 ) tuning = 400.0 ; \
if ( tuning > 500.0 ) tuning = 500.0 ; \
int boundaryBottom = tuning * pow ( 2.0 , 0.25 ) * ( divider / clock ) ; \
int boundaryTop = 2.0 * tuning * pow ( 2.0 , 0.25 ) * ( divider / clock ) ; \
int block = ( note ) / 12 ; \
if ( block < 0 ) block = 0 ; \
if ( block > 7 ) block = 7 ; \
bf > > = block ; \
if ( bf < 0 ) bf = 0 ; \
/* octave boundaries */ \
while ( bf > 0 & & bf < boundaryBottom & & block > 0 ) { \
bf < < = 1 ; \
block - - ; \
} \
if ( bf > boundaryTop ) { \
while ( block < 7 & & bf > boundaryTop ) { \
bf > > = 1 ; \
block + + ; \
} \
if ( bf > ( ( 1 < < bits ) - 1 ) ) { \
bf = ( 1 < < bits ) - 1 ; \
} \
} \
/* logV("f-num: %d block: %d",bf,block); */ \
2022-04-25 03:45:59 +00:00
return bf | ( block < < bits ) ;
2022-05-10 21:22:40 +00:00
2022-05-11 04:17:40 +00:00
int DivEngine : : calcBaseFreqFNumBlock ( double clock , double divider , int note , int bits ) {
2022-05-10 08:51:18 +00:00
if ( song . linearPitch = = 2 ) { // full linear
return ( note < < 7 ) ;
}
2022-04-25 03:45:59 +00:00
int bf = calcBaseFreq ( clock , divider , note , false ) ;
2022-05-10 21:22:40 +00:00
CONVERT_FNUM_BLOCK ( bf , bits , note )
2022-04-25 03:45:59 +00:00
}
2022-05-10 21:22:40 +00:00
int DivEngine : : calcFreq ( int base , int pitch , bool period , int octave , int pitch2 , double clock , double divider , int blockBits ) {
2022-05-10 08:51:18 +00:00
if ( song . linearPitch = = 2 ) {
// do frequency calculation here
2022-05-10 21:22:40 +00:00
int nbase = base + pitch + pitch2 ;
double fbase = ( period ? ( song . tuning * 0.0625 ) : song . tuning ) * pow ( 2.0 , ( float ) ( nbase + 384 ) / ( 128.0 * 12.0 ) ) ;
int bf = period ?
2022-05-11 04:42:24 +00:00
round ( ( clock / fbase ) / divider ) :
round ( fbase * ( divider / clock ) ) ;
2022-05-10 21:22:40 +00:00
if ( blockBits > 0 ) {
CONVERT_FNUM_BLOCK ( bf , blockBits , nbase > > 7 )
} else {
return bf ;
}
2022-05-10 08:51:18 +00:00
}
if ( song . linearPitch = = 1 ) {
2022-03-25 08:41:43 +00:00
// global pitch multiplier
int whatTheFuck = ( 1024 + ( globalPitch < < 6 ) - ( globalPitch < 0 ? globalPitch - 6 : 0 ) ) ;
if ( whatTheFuck < 1 ) whatTheFuck = 1 ; // avoids division by zero but please kill me
2022-04-28 05:26:21 +00:00
if ( song . pitchMacroIsLinear ) {
pitch + = pitch2 ;
}
2022-03-25 07:52:41 +00:00
pitch + = 2048 ;
if ( pitch < 0 ) pitch = 0 ;
if ( pitch > 4095 ) pitch = 4095 ;
2022-04-28 05:26:21 +00:00
int ret = period ?
( ( base * ( reversePitchTable [ pitch ] ) ) / whatTheFuck ) :
( ( ( base * ( pitchTable [ pitch ] ) ) > > 10 ) * whatTheFuck ) / 1024 ;
if ( ! song . pitchMacroIsLinear ) {
ret + = period ? ( - pitch2 ) : pitch2 ;
}
return ret ;
2022-02-03 05:52:50 +00:00
}
2021-12-28 05:51:38 +00:00
return period ?
2022-04-28 05:26:21 +00:00
base - pitch - pitch2 :
base + ( ( pitch * octave ) > > 1 ) + pitch2 ;
2021-12-28 05:51:38 +00:00
}
2022-08-22 20:59:45 +00:00
int DivEngine : : calcArp ( int note , int arp , int offset ) {
2022-08-23 00:09:08 +00:00
if ( arp < 0 ) {
if ( ! ( arp & 0x40000000 ) ) return ( arp | 0x40000000 ) + offset ;
} else {
if ( arp & 0x40000000 ) return ( arp & ( ~ 0x40000000 ) ) + offset ;
}
2022-08-22 20:59:45 +00:00
return note + arp ;
}
2022-04-29 04:58:11 +00:00
int DivEngine : : convertPanSplitToLinear ( unsigned int val , unsigned char bits , int range ) {
int panL = val > > bits ;
int panR = val & ( ( 1 < < bits ) - 1 ) ;
int diff = panR - panL ;
float pan = 0.5f ;
if ( diff ! = 0 ) {
pan = ( 1.0f + ( ( float ) diff / ( float ) MAX ( panL , panR ) ) ) * 0.5f ;
}
return pan * range ;
}
2022-04-30 04:41:14 +00:00
int DivEngine : : convertPanSplitToLinearLR ( unsigned char left , unsigned char right , int range ) {
return convertPanSplitToLinear ( ( left < < 8 ) | right , 8 , range ) ;
}
2022-04-29 04:58:11 +00:00
unsigned int DivEngine : : convertPanLinearToSplit ( int val , unsigned char bits , int range ) {
if ( val < 0 ) val = 0 ;
if ( val > range ) val = range ;
int maxV = ( 1 < < bits ) - 1 ;
int panL = ( ( ( range - val ) * maxV * 2 ) ) / range ;
int panR = ( ( val ) * maxV * 2 ) / range ;
if ( panL > maxV ) panL = maxV ;
if ( panR > maxV ) panR = maxV ;
return ( panL < < bits ) | panR ;
}
2021-12-21 06:29:07 +00:00
void DivEngine : : play ( ) {
2022-03-24 02:38:28 +00:00
BUSY_BEGIN_SOFT ;
2022-06-06 06:05:06 +00:00
curOrder = prevOrder ;
2022-02-06 21:29:30 +00:00
sPreview . sample = - 1 ;
sPreview . wave = - 1 ;
sPreview . pos = 0 ;
2022-08-11 13:21:54 +00:00
sPreview . dir = false ;
2022-09-29 05:27:40 +00:00
shallStop = false ;
2022-02-06 05:42:07 +00:00
if ( stepPlay = = 0 ) {
freelance = false ;
playSub ( false ) ;
} else {
stepPlay = 0 ;
}
2022-02-10 08:15:39 +00:00
for ( int i = 0 ; i < DIV_MAX_CHANS ; i + + ) {
keyHit [ i ] = false ;
}
2022-04-09 06:50:44 +00:00
if ( output ) if ( ! skipping & & output - > midiOut ! = NULL ) {
int pos = totalTicksR / 6 ;
output - > midiOut - > send ( TAMidiMessage ( TA_MIDI_POSITION , ( pos > > 7 ) & 0x7f , pos & 0x7f ) ) ;
output - > midiOut - > send ( TAMidiMessage ( TA_MIDI_MACHINE_PLAY , 0 , 0 ) ) ;
}
2022-03-24 02:38:28 +00:00
BUSY_END ;
2021-12-11 08:34:43 +00:00
}
2022-02-06 05:07:35 +00:00
void DivEngine : : playToRow ( int row ) {
2022-03-24 02:38:28 +00:00
BUSY_BEGIN_SOFT ;
2022-02-06 21:29:30 +00:00
sPreview . sample = - 1 ;
sPreview . wave = - 1 ;
sPreview . pos = 0 ;
2022-08-11 13:21:54 +00:00
sPreview . dir = false ;
2022-02-06 05:07:35 +00:00
freelance = false ;
playSub ( false , row ) ;
2022-02-10 08:15:39 +00:00
for ( int i = 0 ; i < DIV_MAX_CHANS ; i + + ) {
keyHit [ i ] = false ;
}
2022-03-24 02:38:28 +00:00
BUSY_END ;
2022-02-06 05:07:35 +00:00
}
2022-02-06 05:42:07 +00:00
void DivEngine : : stepOne ( int row ) {
if ( ! isPlaying ( ) ) {
2022-03-24 02:38:28 +00:00
BUSY_BEGIN_SOFT ;
2022-02-06 05:42:07 +00:00
freelance = false ;
playSub ( false , row ) ;
2022-02-10 08:15:39 +00:00
for ( int i = 0 ; i < DIV_MAX_CHANS ; i + + ) {
keyHit [ i ] = false ;
}
2022-03-24 02:38:28 +00:00
} else {
BUSY_BEGIN ;
2022-02-06 05:42:07 +00:00
}
stepPlay = 2 ;
ticks = 1 ;
2022-03-24 02:38:28 +00:00
BUSY_END ;
2022-02-06 05:42:07 +00:00
}
2021-12-11 08:34:43 +00:00
void DivEngine : : stop ( ) {
2022-03-24 02:38:28 +00:00
BUSY_BEGIN ;
2021-12-28 23:23:57 +00:00
freelance = false ;
2021-12-11 08:34:43 +00:00
playing = false ;
2021-12-21 00:46:49 +00:00
extValuePresent = false ;
2022-06-21 03:14:16 +00:00
endOfSong = false ; // what?
2022-02-06 05:42:07 +00:00
stepPlay = 0 ;
2022-06-21 03:14:16 +00:00
curOrder = prevOrder ;
curRow = prevRow ;
2022-01-18 04:34:29 +00:00
remainingLoops = - 1 ;
2022-02-06 21:29:30 +00:00
sPreview . sample = - 1 ;
sPreview . wave = - 1 ;
sPreview . pos = 0 ;
2022-08-11 13:21:54 +00:00
sPreview . dir = false ;
2022-03-07 06:48:48 +00:00
for ( int i = 0 ; i < song . systemLen ; i + + ) {
disCont [ i ] . dispatch - > notifyPlaybackStop ( ) ;
}
2022-04-09 06:50:44 +00:00
if ( output ) if ( output - > midiOut ! = NULL ) {
output - > midiOut - > send ( TAMidiMessage ( TA_MIDI_MACHINE_STOP , 0 , 0 ) ) ;
for ( int i = 0 ; i < chans ; i + + ) {
if ( chan [ i ] . curMidiNote > = 0 ) {
output - > midiOut - > send ( TAMidiMessage ( 0x80 | ( i & 15 ) , chan [ i ] . curMidiNote , 0 ) ) ;
}
}
}
2022-03-24 02:38:28 +00:00
BUSY_END ;
2021-12-11 08:34:43 +00:00
}
2022-02-03 23:38:57 +00:00
void DivEngine : : halt ( ) {
2022-03-24 02:38:28 +00:00
BUSY_BEGIN ;
2022-02-03 23:38:57 +00:00
halted = true ;
2022-03-24 02:38:28 +00:00
BUSY_END ;
2022-02-03 23:38:57 +00:00
}
void DivEngine : : resume ( ) {
2022-03-24 02:38:28 +00:00
BUSY_BEGIN ;
2022-02-03 23:38:57 +00:00
halted = false ;
haltOn = DIV_HALT_NONE ;
2022-03-24 02:38:28 +00:00
BUSY_END ;
2022-02-03 23:38:57 +00:00
}
void DivEngine : : haltWhen ( DivHaltPositions when ) {
2022-03-24 02:38:28 +00:00
BUSY_BEGIN ;
2022-02-03 23:38:57 +00:00
halted = false ;
haltOn = when ;
2022-03-24 02:38:28 +00:00
BUSY_END ;
2022-02-03 23:38:57 +00:00
}
bool DivEngine : : isHalted ( ) {
return halted ;
}
const char * * DivEngine : : getRegisterSheet ( int sys ) {
if ( sys < 0 | | sys > = song . systemLen ) return NULL ;
return disCont [ sys ] . dispatch - > getRegisterSheet ( ) ;
}
2022-01-08 21:03:32 +00:00
void DivEngine : : recalcChans ( ) {
2022-04-27 09:48:56 +00:00
bool isInsTypePossible [ DIV_INS_MAX ] ;
2022-01-08 21:03:32 +00:00
chans = 0 ;
int chanIndex = 0 ;
2022-04-27 09:48:56 +00:00
memset ( isInsTypePossible , 0 , DIV_INS_MAX * sizeof ( bool ) ) ;
2022-01-08 21:03:32 +00:00
for ( int i = 0 ; i < song . systemLen ; i + + ) {
int chanCount = getChannelCount ( song . system [ i ] ) ;
chans + = chanCount ;
for ( int j = 0 ; j < chanCount ; j + + ) {
sysOfChan [ chanIndex ] = song . system [ i ] ;
dispatchOfChan [ chanIndex ] = i ;
dispatchChanOfChan [ chanIndex ] = j ;
chanIndex + + ;
2022-04-27 09:48:56 +00:00
if ( sysDefs [ song . system [ i ] ] ! = NULL ) {
if ( sysDefs [ song . system [ i ] ] - > chanInsType [ j ] [ 0 ] ! = DIV_INS_NULL ) {
isInsTypePossible [ sysDefs [ song . system [ i ] ] - > chanInsType [ j ] [ 0 ] ] = true ;
}
if ( sysDefs [ song . system [ i ] ] - > chanInsType [ j ] [ 1 ] ! = DIV_INS_NULL ) {
isInsTypePossible [ sysDefs [ song . system [ i ] ] - > chanInsType [ j ] [ 1 ] ] = true ;
}
}
2022-01-08 21:03:32 +00:00
}
}
2022-04-27 09:48:56 +00:00
possibleInsTypes . clear ( ) ;
for ( int i = 0 ; i < DIV_INS_MAX ; i + + ) {
if ( isInsTypePossible [ i ] ) possibleInsTypes . push_back ( ( DivInstrumentType ) i ) ;
}
2022-04-30 06:37:37 +00:00
hasLoadedSomething = true ;
2022-01-08 21:03:32 +00:00
}
2021-12-11 21:51:34 +00:00
void DivEngine : : reset ( ) {
2022-09-22 07:46:55 +00:00
if ( output ) if ( output - > midiOut ! = NULL ) {
output - > midiOut - > send ( TAMidiMessage ( TA_MIDI_MACHINE_STOP , 0 , 0 ) ) ;
for ( int i = 0 ; i < chans ; i + + ) {
if ( chan [ i ] . curMidiNote > = 0 ) {
output - > midiOut - > send ( TAMidiMessage ( 0x80 | ( i & 15 ) , chan [ i ] . curMidiNote , 0 ) ) ;
}
}
}
2022-01-08 06:57:37 +00:00
for ( int i = 0 ; i < DIV_MAX_CHANS ; i + + ) {
2021-12-11 21:51:34 +00:00
chan [ i ] = DivChannelState ( ) ;
2022-01-08 21:03:32 +00:00
if ( i < chans ) chan [ i ] . volMax = ( disCont [ dispatchOfChan [ i ] ] . dispatch - > dispatch ( DivCommand ( DIV_CMD_GET_VOLMAX , dispatchChanOfChan [ i ] ) ) < < 8 ) | 0xff ;
2021-12-11 21:51:34 +00:00
chan [ i ] . volume = chan [ i ] . volMax ;
2022-05-10 08:51:18 +00:00
if ( song . linearPitch = = 0 ) chan [ i ] . vibratoFine = 4 ;
2021-12-11 21:51:34 +00:00
}
2021-12-21 00:46:49 +00:00
extValue = 0 ;
extValuePresent = 0 ;
2022-05-15 06:42:49 +00:00
speed1 = curSubSong - > speed1 ;
speed2 = curSubSong - > speed2 ;
2022-03-24 04:56:59 +00:00
firstTick = false ;
2022-09-29 05:27:40 +00:00
shallStop = false ;
2021-12-29 07:08:50 +00:00
nextSpeed = speed1 ;
2022-01-12 07:45:26 +00:00
divider = 60 ;
2022-05-15 06:42:49 +00:00
if ( curSubSong - > customTempo ) {
divider = curSubSong - > hz ;
2022-01-12 07:45:26 +00:00
} else {
2022-05-15 06:42:49 +00:00
if ( curSubSong - > pal ) {
2022-01-12 07:45:26 +00:00
divider = 60 ;
} else {
divider = 50 ;
}
}
2021-12-27 20:22:57 +00:00
globalPitch = 0 ;
2022-01-08 21:03:32 +00:00
for ( int i = 0 ; i < song . systemLen ; i + + ) {
disCont [ i ] . dispatch - > reset ( ) ;
2022-01-20 02:04:51 +00:00
disCont [ i ] . clear ( ) ;
2022-01-08 21:03:32 +00:00
}
2021-12-11 21:51:34 +00:00
}
2021-12-20 02:11:23 +00:00
void DivEngine : : syncReset ( ) {
2022-03-24 02:38:28 +00:00
BUSY_BEGIN ;
2021-12-20 02:11:23 +00:00
reset ( ) ;
2022-03-24 02:38:28 +00:00
BUSY_END ;
2021-12-20 02:11:23 +00:00
}
2021-12-21 18:06:14 +00:00
const int sampleRates [ 6 ] = {
4000 , 8000 , 11025 , 16000 , 22050 , 32000
} ;
2021-12-23 23:04:44 +00:00
int DivEngine : : fileToDivRate ( int frate ) {
if ( frate < 0 ) frate = 0 ;
if ( frate > 5 ) frate = 5 ;
return sampleRates [ frate ] ;
}
int DivEngine : : divToFileRate ( int drate ) {
if ( drate > 26000 ) {
return 5 ;
} else if ( drate > 18000 ) {
return 4 ;
} else if ( drate > 14000 ) {
return 3 ;
} else if ( drate > 9500 ) {
return 2 ;
} else if ( drate > 6000 ) {
return 1 ;
} else {
return 0 ;
}
return 4 ;
}
int DivEngine : : getEffectiveSampleRate ( int rate ) {
if ( rate < 1 ) return 0 ;
2022-01-08 08:02:04 +00:00
switch ( song . system [ 0 ] ) {
2021-12-23 23:04:44 +00:00
case DIV_SYSTEM_YMU759 :
return 8000 ;
2022-02-23 07:52:30 +00:00
case DIV_SYSTEM_YM2612 : case DIV_SYSTEM_YM2612_EXT :
2021-12-23 23:04:44 +00:00
return 1278409 / ( 1280000 / rate ) ;
case DIV_SYSTEM_PCE :
return 1789773 / ( 1789773 / rate ) ;
2022-02-23 07:52:30 +00:00
case DIV_SYSTEM_SEGAPCM : case DIV_SYSTEM_SEGAPCM_COMPAT :
2021-12-23 23:04:44 +00:00
return ( 31250 * MIN ( 255 , ( rate * 255 / 31250 ) ) ) / 255 ;
2022-03-07 15:15:21 +00:00
case DIV_SYSTEM_QSOUND :
2022-02-24 16:02:35 +00:00
return ( 24038 * MIN ( 65535 , ( rate * 4096 / 24038 ) ) ) / 4096 ;
case DIV_SYSTEM_YM2610 : case DIV_SYSTEM_YM2610_EXT : case DIV_SYSTEM_YM2610_FULL : case DIV_SYSTEM_YM2610_FULL_EXT : case DIV_SYSTEM_YM2610B : case DIV_SYSTEM_YM2610B_EXT :
2021-12-23 23:04:44 +00:00
return 18518 ;
2022-03-04 11:13:49 +00:00
case DIV_SYSTEM_VERA :
return ( 48828 * MIN ( 128 , ( rate * 128 / 48828 ) ) ) / 128 ;
2022-03-06 17:31:03 +00:00
case DIV_SYSTEM_X1_010 :
return ( 31250 * MIN ( 255 , ( rate * 16 / 31250 ) ) ) / 16 ; // TODO: support variable clock case
2021-12-23 23:04:44 +00:00
default :
break ;
}
return rate ;
}
2022-05-29 07:13:08 +00:00
void DivEngine : : previewSample ( int sample , int note , int pStart , int pEnd ) {
2022-03-24 02:38:28 +00:00
BUSY_BEGIN ;
2022-05-29 07:13:08 +00:00
sPreview . pBegin = pStart ;
sPreview . pEnd = pEnd ;
2022-08-11 13:21:54 +00:00
sPreview . dir = false ;
2021-12-29 04:10:13 +00:00
if ( sample < 0 | | sample > = ( int ) song . sample . size ( ) ) {
2021-12-21 18:06:14 +00:00
sPreview . sample = - 1 ;
sPreview . pos = 0 ;
2022-08-11 13:21:54 +00:00
sPreview . dir = false ;
2022-03-24 02:38:28 +00:00
BUSY_END ;
2021-12-21 18:06:14 +00:00
return ;
}
2022-01-08 21:03:32 +00:00
blip_clear ( samp_bb ) ;
2022-01-20 21:51:31 +00:00
double rate = song . sample [ sample ] - > rate ;
if ( note > = 0 ) {
2022-09-30 23:24:20 +00:00
rate = ( pow ( 2.0 , ( double ) ( note ) / 12.0 ) * ( ( double ) song . sample [ sample ] - > centerRate ) * 0.0625 ) ;
2022-01-20 21:51:31 +00:00
if ( rate < = 0 ) rate = song . sample [ sample ] - > rate ;
}
2022-03-20 18:36:48 +00:00
if ( rate < 100 ) rate = 100 ;
2022-01-20 21:51:31 +00:00
blip_set_rates ( samp_bb , rate , got . rate ) ;
2022-01-08 21:03:32 +00:00
samp_prevSample = 0 ;
2022-09-30 22:47:17 +00:00
sPreview . rate = rate ;
2022-05-29 07:13:08 +00:00
sPreview . pos = ( sPreview . pBegin > = 0 ) ? sPreview . pBegin : 0 ;
2021-12-21 18:06:14 +00:00
sPreview . sample = sample ;
2022-01-20 05:07:53 +00:00
sPreview . wave = - 1 ;
2022-08-11 13:21:54 +00:00
sPreview . dir = false ;
2022-03-24 02:38:28 +00:00
BUSY_END ;
2021-12-21 18:06:14 +00:00
}
2022-01-20 21:51:31 +00:00
void DivEngine : : stopSamplePreview ( ) {
2022-03-24 02:38:28 +00:00
BUSY_BEGIN ;
2022-01-20 21:51:31 +00:00
sPreview . sample = - 1 ;
sPreview . pos = 0 ;
2022-08-11 13:21:54 +00:00
sPreview . dir = false ;
2022-03-24 02:38:28 +00:00
BUSY_END ;
2022-01-20 21:51:31 +00:00
}
2022-01-20 05:07:53 +00:00
void DivEngine : : previewWave ( int wave , int note ) {
2022-03-24 02:38:28 +00:00
BUSY_BEGIN ;
2022-01-20 05:07:53 +00:00
if ( wave < 0 | | wave > = ( int ) song . wave . size ( ) ) {
sPreview . wave = - 1 ;
sPreview . pos = 0 ;
2022-08-11 13:21:54 +00:00
sPreview . dir = false ;
2022-03-24 02:38:28 +00:00
BUSY_END ;
2022-01-20 05:07:53 +00:00
return ;
}
2022-01-20 05:43:08 +00:00
if ( song . wave [ wave ] - > len < = 0 ) {
2022-03-24 02:38:28 +00:00
BUSY_END ;
2022-01-20 05:43:08 +00:00
return ;
}
2022-01-20 05:07:53 +00:00
blip_clear ( samp_bb ) ;
2022-03-20 18:36:48 +00:00
double rate = song . wave [ wave ] - > len * ( ( song . tuning * 0.0625 ) * pow ( 2.0 , ( double ) ( note + 3 ) / 12.0 ) ) ;
if ( rate < 100 ) rate = 100 ;
blip_set_rates ( samp_bb , rate , got . rate ) ;
2022-01-20 05:07:53 +00:00
samp_prevSample = 0 ;
2022-09-30 22:47:17 +00:00
sPreview . rate = rate ;
2022-01-20 05:07:53 +00:00
sPreview . pos = 0 ;
sPreview . sample = - 1 ;
sPreview . wave = wave ;
2022-08-11 13:21:54 +00:00
sPreview . dir = false ;
2022-03-24 02:38:28 +00:00
BUSY_END ;
2022-01-20 05:07:53 +00:00
}
void DivEngine : : stopWavePreview ( ) {
2022-03-24 02:38:28 +00:00
BUSY_BEGIN ;
2022-01-20 05:07:53 +00:00
sPreview . wave = - 1 ;
sPreview . pos = 0 ;
2022-08-11 13:21:54 +00:00
sPreview . dir = false ;
2022-03-24 02:38:28 +00:00
BUSY_END ;
2022-01-20 05:07:53 +00:00
}
2022-09-30 22:47:17 +00:00
bool DivEngine : : isPreviewingSample ( ) {
return ( sPreview . sample > = 0 & & sPreview . sample < ( int ) song . sample . size ( ) ) ;
}
int DivEngine : : getSamplePreviewPos ( ) {
return sPreview . pos ;
}
double DivEngine : : getSamplePreviewRate ( ) {
return sPreview . rate ;
}
2021-12-19 08:16:24 +00:00
String DivEngine : : getConfigPath ( ) {
return configPath ;
}
2021-12-13 22:09:46 +00:00
int DivEngine : : getMaxVolumeChan ( int ch ) {
return chan [ ch ] . volMax > > 8 ;
}
2021-12-11 08:34:43 +00:00
unsigned char DivEngine : : getOrder ( ) {
2022-06-06 06:05:06 +00:00
return prevOrder ;
2021-12-11 08:34:43 +00:00
}
2021-12-13 22:09:46 +00:00
int DivEngine : : getRow ( ) {
2022-06-06 06:05:06 +00:00
return prevRow ;
2021-12-13 22:09:46 +00:00
}
2022-05-15 06:42:49 +00:00
size_t DivEngine : : getCurrentSubSong ( ) {
return curSubSongIndex ;
}
2021-12-21 07:30:09 +00:00
unsigned char DivEngine : : getSpeed1 ( ) {
return speed1 ;
}
unsigned char DivEngine : : getSpeed2 ( ) {
return speed2 ;
}
2022-03-16 04:30:15 +00:00
float DivEngine : : getHz ( ) {
2022-05-15 06:42:49 +00:00
if ( curSubSong - > customTempo ) {
return curSubSong - > hz ;
} else if ( curSubSong - > pal ) {
2022-03-16 04:30:15 +00:00
return 60.0 ;
2021-12-21 07:30:09 +00:00
} else {
2022-03-16 04:30:15 +00:00
return 50.0 ;
2021-12-21 07:30:09 +00:00
}
2022-03-16 04:30:15 +00:00
return 60.0 ;
2021-12-21 07:30:09 +00:00
}
2022-03-16 04:30:15 +00:00
float DivEngine : : getCurHz ( ) {
2022-01-12 07:45:26 +00:00
return divider ;
}
int DivEngine : : getTotalSeconds ( ) {
return totalSeconds ;
}
2021-12-21 07:30:09 +00:00
int DivEngine : : getTotalTicks ( ) {
return totalTicks ;
}
2021-12-21 22:42:27 +00:00
bool DivEngine : : getRepeatPattern ( ) {
return repeatPattern ;
}
void DivEngine : : setRepeatPattern ( bool value ) {
2022-03-24 02:38:28 +00:00
BUSY_BEGIN ;
2021-12-21 22:42:27 +00:00
repeatPattern = value ;
2022-03-24 02:38:28 +00:00
BUSY_END ;
2021-12-21 22:42:27 +00:00
}
2021-12-21 00:46:49 +00:00
bool DivEngine : : hasExtValue ( ) {
return extValuePresent ;
}
unsigned char DivEngine : : getExtValue ( ) {
return extValue ;
}
2021-12-13 22:09:46 +00:00
bool DivEngine : : isPlaying ( ) {
2021-12-28 23:23:57 +00:00
return ( playing & & ! freelance ) ;
2021-12-13 22:09:46 +00:00
}
2022-06-29 06:37:12 +00:00
bool DivEngine : : isRunning ( ) {
return playing ;
}
2022-02-06 05:42:07 +00:00
bool DivEngine : : isStepping ( ) {
return ! ( stepPlay = = 0 ) ;
}
2021-12-18 08:25:42 +00:00
bool DivEngine : : isChannelMuted ( int chan ) {
return isMuted [ chan ] ;
}
void DivEngine : : toggleMute ( int chan ) {
muteChannel ( chan , ! isMuted [ chan ] ) ;
}
void DivEngine : : toggleSolo ( int chan ) {
bool solo = false ;
for ( int i = 0 ; i < chans ; i + + ) {
if ( i = = chan ) {
solo = true ;
continue ;
} else {
if ( ! isMuted [ i ] ) {
solo = false ;
break ;
}
}
}
2022-03-24 02:38:28 +00:00
BUSY_BEGIN ;
2021-12-18 08:25:42 +00:00
if ( ! solo ) {
for ( int i = 0 ; i < chans ; i + + ) {
isMuted [ i ] = ( i ! = chan ) ;
2022-01-08 21:03:32 +00:00
if ( disCont [ dispatchOfChan [ i ] ] . dispatch ! = NULL ) {
disCont [ dispatchOfChan [ i ] ] . dispatch - > muteChannel ( dispatchChanOfChan [ i ] , isMuted [ i ] ) ;
2021-12-18 08:25:42 +00:00
}
}
} else {
for ( int i = 0 ; i < chans ; i + + ) {
isMuted [ i ] = false ;
2022-01-08 21:03:32 +00:00
if ( disCont [ dispatchOfChan [ i ] ] . dispatch ! = NULL ) {
disCont [ dispatchOfChan [ i ] ] . dispatch - > muteChannel ( dispatchChanOfChan [ i ] , isMuted [ i ] ) ;
2021-12-18 08:25:42 +00:00
}
}
}
2022-03-24 02:38:28 +00:00
BUSY_END ;
2021-12-18 08:25:42 +00:00
}
void DivEngine : : muteChannel ( int chan , bool mute ) {
2022-03-24 02:38:28 +00:00
BUSY_BEGIN ;
2021-12-18 08:25:42 +00:00
isMuted [ chan ] = mute ;
2022-01-08 21:03:32 +00:00
if ( disCont [ dispatchOfChan [ chan ] ] . dispatch ! = NULL ) {
disCont [ dispatchOfChan [ chan ] ] . dispatch - > muteChannel ( dispatchChanOfChan [ chan ] , isMuted [ chan ] ) ;
2021-12-18 08:25:42 +00:00
}
2022-03-24 02:38:28 +00:00
BUSY_END ;
2021-12-18 08:25:42 +00:00
}
2022-02-11 23:20:39 +00:00
void DivEngine : : unmuteAll ( ) {
2022-03-24 02:38:28 +00:00
BUSY_BEGIN ;
2022-02-11 23:20:39 +00:00
for ( int i = 0 ; i < chans ; i + + ) {
isMuted [ i ] = false ;
if ( disCont [ dispatchOfChan [ i ] ] . dispatch ! = NULL ) {
disCont [ dispatchOfChan [ i ] ] . dispatch - > muteChannel ( dispatchChanOfChan [ i ] , isMuted [ i ] ) ;
}
}
2022-03-24 02:38:28 +00:00
BUSY_END ;
2022-02-11 23:20:39 +00:00
}
2022-01-10 04:50:26 +00:00
int DivEngine : : addInstrument ( int refChan ) {
2022-05-14 22:51:05 +00:00
if ( song . ins . size ( ) > = 256 ) return - 1 ;
2022-03-24 02:38:28 +00:00
BUSY_BEGIN ;
2021-12-17 08:33:12 +00:00
DivInstrument * ins = new DivInstrument ;
int insCount = ( int ) song . ins . size ( ) ;
2022-04-21 07:29:20 +00:00
DivInstrumentType prefType = getPreferInsType ( refChan ) ;
switch ( prefType ) {
case DIV_INS_OPLL :
* ins = song . nullInsOPLL ;
break ;
case DIV_INS_OPL :
* ins = song . nullInsOPL ;
break ;
2022-05-31 22:44:52 +00:00
case DIV_INS_OPL_DRUMS :
* ins = song . nullInsOPLDrums ;
break ;
2022-04-21 07:29:20 +00:00
default :
break ;
}
2022-04-29 04:58:11 +00:00
if ( sysOfChan [ refChan ] = = DIV_SYSTEM_QSOUND ) {
* ins = song . nullInsQSound ;
}
2021-12-17 08:33:12 +00:00
ins - > name = fmt : : sprintf ( " Instrument %d " , insCount ) ;
2022-05-13 08:18:14 +00:00
if ( prefType ! = DIV_INS_NULL ) {
ins - > type = prefType ;
}
2022-03-21 21:17:51 +00:00
saveLock . lock ( ) ;
2021-12-17 08:33:12 +00:00
song . ins . push_back ( ins ) ;
song . insLen = insCount + 1 ;
2022-03-21 21:17:51 +00:00
saveLock . unlock ( ) ;
2022-03-24 02:38:28 +00:00
BUSY_END ;
2021-12-17 08:33:12 +00:00
return insCount ;
}
2022-04-03 06:56:49 +00:00
int DivEngine : : addInstrumentPtr ( DivInstrument * which ) {
2022-05-14 22:51:05 +00:00
if ( song . ins . size ( ) > = 256 ) {
delete which ;
return - 1 ;
}
2022-04-03 06:56:49 +00:00
BUSY_BEGIN ;
saveLock . lock ( ) ;
song . ins . push_back ( which ) ;
song . insLen = song . ins . size ( ) ;
saveLock . unlock ( ) ;
BUSY_END ;
return song . insLen ;
}
2022-04-25 23:23:12 +00:00
void DivEngine : : loadTempIns ( DivInstrument * which ) {
BUSY_BEGIN ;
if ( tempIns = = NULL ) {
tempIns = new DivInstrument ;
}
2022-04-25 23:41:24 +00:00
* tempIns = * which ;
2022-04-25 23:23:12 +00:00
BUSY_END ;
}
2021-12-17 08:33:12 +00:00
void DivEngine : : delInstrument ( int index ) {
2022-03-24 02:38:28 +00:00
BUSY_BEGIN ;
2022-03-21 21:17:51 +00:00
saveLock . lock ( ) ;
2021-12-17 08:33:12 +00:00
if ( index > = 0 & & index < ( int ) song . ins . size ( ) ) {
2022-01-13 22:40:29 +00:00
for ( int i = 0 ; i < song . systemLen ; i + + ) {
disCont [ i ] . dispatch - > notifyInsDeletion ( song . ins [ index ] ) ;
}
2021-12-17 08:33:12 +00:00
delete song . ins [ index ] ;
song . ins . erase ( song . ins . begin ( ) + index ) ;
song . insLen = song . ins . size ( ) ;
2022-02-08 04:42:54 +00:00
for ( int i = 0 ; i < chans ; i + + ) {
2022-08-16 03:54:31 +00:00
for ( size_t j = 0 ; j < song . subsong . size ( ) ; j + + ) {
for ( int k = 0 ; k < 256 ; k + + ) {
if ( song . subsong [ j ] - > pat [ i ] . data [ k ] = = NULL ) continue ;
for ( int l = 0 ; l < song . subsong [ j ] - > patLen ; l + + ) {
if ( song . subsong [ j ] - > pat [ i ] . data [ k ] - > data [ l ] [ 2 ] > index ) {
song . subsong [ j ] - > pat [ i ] . data [ k ] - > data [ l ] [ 2 ] - - ;
}
2022-02-08 04:42:54 +00:00
}
}
}
}
2021-12-17 08:33:12 +00:00
}
2022-03-21 21:17:51 +00:00
saveLock . unlock ( ) ;
2022-03-24 02:38:28 +00:00
BUSY_END ;
2021-12-17 08:33:12 +00:00
}
int DivEngine : : addWave ( ) {
2022-08-13 09:17:32 +00:00
if ( song . wave . size ( ) > = 256 ) {
lastError = " too many wavetables! " ;
return - 1 ;
}
2022-03-24 02:38:28 +00:00
BUSY_BEGIN ;
2022-03-21 21:17:51 +00:00
saveLock . lock ( ) ;
2021-12-17 08:33:12 +00:00
DivWavetable * wave = new DivWavetable ;
int waveCount = ( int ) song . wave . size ( ) ;
song . wave . push_back ( wave ) ;
song . waveLen = waveCount + 1 ;
2022-03-21 21:17:51 +00:00
saveLock . unlock ( ) ;
2022-03-24 02:38:28 +00:00
BUSY_END ;
2021-12-17 08:33:12 +00:00
return waveCount ;
}
2022-08-13 09:17:32 +00:00
int DivEngine : : addWavePtr ( DivWavetable * which ) {
2022-05-14 22:51:05 +00:00
if ( song . wave . size ( ) > = 256 ) {
lastError = " too many wavetables! " ;
2022-08-13 09:17:32 +00:00
delete which ;
return - 1 ;
2022-05-14 22:51:05 +00:00
}
2022-08-13 09:17:32 +00:00
BUSY_BEGIN ;
saveLock . lock ( ) ;
int waveCount = ( int ) song . wave . size ( ) ;
song . wave . push_back ( which ) ;
song . waveLen = waveCount + 1 ;
saveLock . unlock ( ) ;
BUSY_END ;
return song . waveLen ;
}
DivWavetable * DivEngine : : waveFromFile ( const char * path , bool addRaw ) {
2022-01-21 22:59:48 +00:00
FILE * f = ps_fopen ( path , " rb " ) ;
if ( f = = NULL ) {
2022-05-14 22:51:05 +00:00
lastError = fmt : : sprintf ( " %s " , strerror ( errno ) ) ;
2022-08-13 09:17:32 +00:00
return NULL ;
2022-01-21 22:59:48 +00:00
}
unsigned char * buf ;
ssize_t len ;
if ( fseek ( f , 0 , SEEK_END ) ! = 0 ) {
fclose ( f ) ;
2022-05-14 22:51:05 +00:00
lastError = fmt : : sprintf ( " could not seek to end: %s " , strerror ( errno ) ) ;
2022-08-13 09:17:32 +00:00
return NULL ;
2022-01-21 22:59:48 +00:00
}
len = ftell ( f ) ;
if ( len < 0 ) {
fclose ( f ) ;
2022-05-14 22:51:05 +00:00
lastError = fmt : : sprintf ( " could not determine file size: %s " , strerror ( errno ) ) ;
2022-08-13 09:17:32 +00:00
return NULL ;
2022-01-21 22:59:48 +00:00
}
2022-04-26 06:07:28 +00:00
if ( len = = ( SIZE_MAX > > 1 ) ) {
fclose ( f ) ;
2022-05-14 22:51:05 +00:00
lastError = " file size is invalid! " ;
2022-08-13 09:17:32 +00:00
return NULL ;
2022-04-26 06:07:28 +00:00
}
2022-01-21 22:59:48 +00:00
if ( len = = 0 ) {
fclose ( f ) ;
2022-05-14 22:51:05 +00:00
lastError = " file is empty " ;
2022-08-13 09:17:32 +00:00
return NULL ;
2022-01-21 22:59:48 +00:00
}
if ( fseek ( f , 0 , SEEK_SET ) ! = 0 ) {
fclose ( f ) ;
2022-05-14 22:51:05 +00:00
lastError = fmt : : sprintf ( " could not seek to beginning: %s " , strerror ( errno ) ) ;
2022-08-13 09:17:32 +00:00
return NULL ;
2022-01-21 22:59:48 +00:00
}
buf = new unsigned char [ len ] ;
if ( fread ( buf , 1 , len , f ) ! = ( size_t ) len ) {
2022-04-11 03:12:02 +00:00
logW ( " did not read entire wavetable file buffer! " ) ;
2022-01-21 22:59:48 +00:00
delete [ ] buf ;
2022-05-14 22:51:05 +00:00
lastError = fmt : : sprintf ( " could not read entire file: %s " , strerror ( errno ) ) ;
2022-08-13 09:17:32 +00:00
return NULL ;
2022-01-21 22:59:48 +00:00
}
fclose ( f ) ;
SafeReader reader = SafeReader ( buf , len ) ;
unsigned char magic [ 16 ] ;
bool isFurnaceTable = false ;
try {
reader . read ( magic , 16 ) ;
if ( memcmp ( " -Furnace waveta- " , magic , 16 ) = = 0 ) {
isFurnaceTable = true ;
}
2022-03-19 06:13:00 +00:00
} catch ( EndOfFileException & e ) {
2022-01-21 22:59:48 +00:00
reader . seek ( 0 , SEEK_SET ) ;
}
DivWavetable * wave = new DivWavetable ;
try {
if ( isFurnaceTable ) {
reader . seek ( 16 , SEEK_SET ) ;
short version = reader . readS ( ) ;
reader . readS ( ) ; // reserved
2022-01-21 23:17:05 +00:00
reader . seek ( 20 , SEEK_SET ) ;
if ( wave - > readWaveData ( reader , version ) ! = DIV_DATA_SUCCESS ) {
lastError = " invalid wavetable header/data! " ;
delete wave ;
2022-01-21 22:59:48 +00:00
delete [ ] buf ;
2022-08-13 09:17:32 +00:00
return NULL ;
2022-01-21 22:59:48 +00:00
}
} else {
try {
// read as .dmw
reader . seek ( 0 , SEEK_SET ) ;
int len = reader . readI ( ) ;
2022-05-30 03:21:37 +00:00
if ( len < = 0 | | len > 256 ) {
throw EndOfFileException ( & reader , reader . size ( ) ) ;
}
2022-01-21 22:59:48 +00:00
wave - > max = ( unsigned char ) reader . readC ( ) ;
2022-02-21 03:16:43 +00:00
if ( wave - > max = = 255 ) { // new wavetable format
unsigned char waveVersion = reader . readC ( ) ;
2022-04-11 03:12:02 +00:00
logI ( " reading modern .dmw... " ) ;
logD ( " wave version %d " , waveVersion ) ;
2022-02-21 03:16:43 +00:00
wave - > max = reader . readC ( ) ;
for ( int i = 0 ; i < len ; i + + ) {
wave - > data [ i ] = reader . readI ( ) ;
}
} else if ( reader . size ( ) = = ( size_t ) ( len + 5 ) ) {
2022-01-21 22:59:48 +00:00
// read as .dmw
2022-04-11 03:12:02 +00:00
logI ( " reading .dmw... " ) ;
2022-01-21 22:59:48 +00:00
if ( len > 256 ) len = 256 ;
for ( int i = 0 ; i < len ; i + + ) {
wave - > data [ i ] = ( unsigned char ) reader . readC ( ) ;
}
} else {
// read as binary
2022-05-28 23:51:05 +00:00
if ( addRaw ) {
logI ( " reading binary... " ) ;
len = reader . size ( ) ;
if ( len > 256 ) len = 256 ;
reader . seek ( 0 , SEEK_SET ) ;
for ( int i = 0 ; i < len ; i + + ) {
wave - > data [ i ] = ( unsigned char ) reader . readC ( ) ;
if ( wave - > max < wave - > data [ i ] ) wave - > max = wave - > data [ i ] ;
}
wave - > len = len ;
} else {
delete wave ;
delete [ ] buf ;
2022-08-13 09:17:32 +00:00
return NULL ;
2022-05-28 23:51:05 +00:00
}
}
} catch ( EndOfFileException & e ) {
// read as binary
if ( addRaw ) {
2022-01-21 22:59:48 +00:00
len = reader . size ( ) ;
2022-05-28 23:51:05 +00:00
logI ( " reading binary for being too small... " ) ;
2022-01-21 22:59:48 +00:00
if ( len > 256 ) len = 256 ;
reader . seek ( 0 , SEEK_SET ) ;
for ( int i = 0 ; i < len ; i + + ) {
wave - > data [ i ] = ( unsigned char ) reader . readC ( ) ;
if ( wave - > max < wave - > data [ i ] ) wave - > max = wave - > data [ i ] ;
}
wave - > len = len ;
2022-05-28 23:51:05 +00:00
} else {
delete wave ;
delete [ ] buf ;
2022-08-13 09:17:32 +00:00
return NULL ;
2022-01-21 22:59:48 +00:00
}
}
}
2022-03-19 06:13:00 +00:00
} catch ( EndOfFileException & e ) {
2022-01-21 22:59:48 +00:00
delete wave ;
delete [ ] buf ;
2022-05-14 22:51:05 +00:00
lastError = " premature end of file " ;
2022-08-13 09:17:32 +00:00
return NULL ;
2022-01-21 22:59:48 +00:00
}
2022-08-13 09:17:32 +00:00
return wave ;
2022-01-11 21:25:55 +00:00
}
2021-12-17 08:33:12 +00:00
void DivEngine : : delWave ( int index ) {
2022-03-24 02:38:28 +00:00
BUSY_BEGIN ;
2022-03-21 21:17:51 +00:00
saveLock . lock ( ) ;
2021-12-17 08:33:12 +00:00
if ( index > = 0 & & index < ( int ) song . wave . size ( ) ) {
delete song . wave [ index ] ;
song . wave . erase ( song . wave . begin ( ) + index ) ;
song . waveLen = song . wave . size ( ) ;
}
2022-03-21 21:17:51 +00:00
saveLock . unlock ( ) ;
2022-03-24 02:38:28 +00:00
BUSY_END ;
2021-12-17 08:33:12 +00:00
}
int DivEngine : : addSample ( ) {
2022-08-13 09:17:32 +00:00
if ( song . sample . size ( ) > = 256 ) {
lastError = " too many samples! " ;
return - 1 ;
}
2022-03-24 02:38:28 +00:00
BUSY_BEGIN ;
2022-03-21 21:17:51 +00:00
saveLock . lock ( ) ;
2021-12-17 08:33:12 +00:00
DivSample * sample = new DivSample ;
int sampleCount = ( int ) song . sample . size ( ) ;
sample - > name = fmt : : sprintf ( " Sample %d " , sampleCount ) ;
song . sample . push_back ( sample ) ;
song . sampleLen = sampleCount + 1 ;
2022-06-17 04:39:38 +00:00
sPreview . sample = - 1 ;
sPreview . pos = 0 ;
2022-08-11 13:21:54 +00:00
sPreview . dir = false ;
2022-03-21 21:17:51 +00:00
saveLock . unlock ( ) ;
2021-12-17 08:33:12 +00:00
renderSamples ( ) ;
2022-03-24 02:38:28 +00:00
BUSY_END ;
2021-12-17 08:33:12 +00:00
return sampleCount ;
}
2022-08-13 09:17:32 +00:00
int DivEngine : : addSamplePtr ( DivSample * which ) {
2022-05-14 22:51:05 +00:00
if ( song . sample . size ( ) > = 256 ) {
lastError = " too many samples! " ;
2022-08-13 09:17:32 +00:00
delete which ;
2022-05-14 22:51:05 +00:00
return - 1 ;
}
2022-08-13 09:17:32 +00:00
int sampleCount = ( int ) song . sample . size ( ) ;
BUSY_BEGIN ;
saveLock . lock ( ) ;
song . sample . push_back ( which ) ;
song . sampleLen = sampleCount + 1 ;
saveLock . unlock ( ) ;
renderSamples ( ) ;
BUSY_END ;
return sampleCount ;
}
DivSample * DivEngine : : sampleFromFile ( const char * path ) {
if ( song . sample . size ( ) > = 256 ) {
lastError = " too many samples! " ;
return NULL ;
}
2022-03-24 02:38:28 +00:00
BUSY_BEGIN ;
2022-04-12 08:34:53 +00:00
warnings = " " ;
const char * pathRedux = strrchr ( path , DIR_SEPARATOR ) ;
if ( pathRedux = = NULL ) {
pathRedux = path ;
} else {
pathRedux + + ;
}
String stripPath ;
const char * pathReduxEnd = strrchr ( pathRedux , ' . ' ) ;
if ( pathReduxEnd = = NULL ) {
stripPath = pathRedux ;
} else {
for ( const char * i = pathRedux ; i ! = pathReduxEnd & & ( * i ) ; i + + ) {
stripPath + = * i ;
}
}
const char * ext = strrchr ( path , ' . ' ) ;
if ( ext ! = NULL ) {
String extS ;
for ( ; * ext ; ext + + ) {
char i = * ext ;
if ( i > = ' A ' & & i < = ' Z ' ) {
i + = ' a ' - ' A ' ;
}
extS + = i ;
}
2022-09-25 06:20:08 +00:00
if ( extS = = " .dmc " | | extS = = " .brr " ) { // read as .dmc or .brr
2022-04-12 08:34:53 +00:00
size_t len = 0 ;
DivSample * sample = new DivSample ;
sample - > name = stripPath ;
2022-05-08 04:50:18 +00:00
FILE * f = ps_fopen ( path , " rb " ) ;
2022-04-12 08:34:53 +00:00
if ( f = = NULL ) {
BUSY_END ;
lastError = fmt : : sprintf ( " could not open file! (%s) " , strerror ( errno ) ) ;
delete sample ;
2022-08-13 09:17:32 +00:00
return NULL ;
2022-04-12 08:34:53 +00:00
}
if ( fseek ( f , 0 , SEEK_END ) < 0 ) {
fclose ( f ) ;
BUSY_END ;
lastError = fmt : : sprintf ( " could not get file length! (%s) " , strerror ( errno ) ) ;
delete sample ;
2022-08-13 09:17:32 +00:00
return NULL ;
2022-04-12 08:34:53 +00:00
}
len = ftell ( f ) ;
if ( len = = 0 ) {
fclose ( f ) ;
BUSY_END ;
lastError = " file is empty! " ;
delete sample ;
2022-08-13 09:17:32 +00:00
return NULL ;
2022-04-12 08:34:53 +00:00
}
2022-04-26 06:07:28 +00:00
if ( len = = ( SIZE_MAX > > 1 ) ) {
fclose ( f ) ;
BUSY_END ;
lastError = " file is invalid! " ;
delete sample ;
2022-08-13 09:17:32 +00:00
return NULL ;
2022-04-26 06:07:28 +00:00
}
2022-04-12 08:34:53 +00:00
if ( fseek ( f , 0 , SEEK_SET ) < 0 ) {
fclose ( f ) ;
BUSY_END ;
lastError = fmt : : sprintf ( " could not seek to beginning of file! (%s) " , strerror ( errno ) ) ;
delete sample ;
2022-08-13 09:17:32 +00:00
return NULL ;
2022-04-12 08:34:53 +00:00
}
2022-09-25 06:20:08 +00:00
if ( extS = = " .dmc " ) {
sample - > rate = 33144 ;
sample - > centerRate = 33144 ;
sample - > depth = DIV_SAMPLE_DEPTH_1BIT_DPCM ;
sample - > init ( len * 8 ) ;
} else if ( extS = = " .brr " ) {
sample - > rate = 32000 ;
sample - > centerRate = 32000 ;
sample - > depth = DIV_SAMPLE_DEPTH_BRR ;
sample - > init ( 16 * ( len / 9 ) ) ;
} else {
fclose ( f ) ;
BUSY_END ;
lastError = " wait... is that right? no I don't think so... " ;
delete sample ;
return NULL ;
}
unsigned char * dataBuf = sample - > dataDPCM ;
if ( extS = = " .brr " ) {
dataBuf = sample - > dataBRR ;
if ( ( len % 9 ) = = 2 ) {
// ignore loop position
fseek ( f , 2 , SEEK_SET ) ;
len - = 2 ;
if ( len = = 0 ) {
fclose ( f ) ;
BUSY_END ;
lastError = " BRR sample is empty! " ;
delete sample ;
return NULL ;
}
} else if ( ( len % 9 ) ! = 0 ) {
fclose ( f ) ;
BUSY_END ;
lastError = " possibly corrupt BRR sample! " ;
delete sample ;
return NULL ;
}
}
2022-04-12 08:34:53 +00:00
2022-09-25 06:20:08 +00:00
if ( fread ( dataBuf , 1 , len , f ) = = 0 ) {
2022-04-12 08:34:53 +00:00
fclose ( f ) ;
BUSY_END ;
lastError = fmt : : sprintf ( " could not read file! (%s) " , strerror ( errno ) ) ;
delete sample ;
2022-08-13 09:17:32 +00:00
return NULL ;
2022-04-12 08:34:53 +00:00
}
BUSY_END ;
2022-08-13 09:17:32 +00:00
return sample ;
2022-04-12 08:34:53 +00:00
}
}
2022-05-23 00:01:50 +00:00
# ifndef HAVE_SNDFILE
lastError = " Furnace was not compiled with libsndfile! " ;
2022-08-13 09:17:32 +00:00
return NULL ;
2022-05-23 00:01:50 +00:00
# else
2021-12-17 08:33:12 +00:00
SF_INFO si ;
2022-06-28 07:00:08 +00:00
SFWrapper sfWrap ;
2022-06-17 02:25:21 +00:00
memset ( & si , 0 , sizeof ( SF_INFO ) ) ;
2022-06-28 07:00:08 +00:00
SNDFILE * f = sfWrap . doOpen ( path , SFM_READ , & si ) ;
2021-12-17 08:33:12 +00:00
if ( f = = NULL ) {
2022-03-24 02:38:28 +00:00
BUSY_END ;
2022-03-23 22:00:40 +00:00
int err = sf_error ( NULL ) ;
if ( err = = SF_ERR_SYSTEM ) {
lastError = fmt : : sprintf ( " could not open file! (%s %s) " , sf_error_number ( err ) , strerror ( errno ) ) ;
} else {
2022-08-13 09:17:32 +00:00
lastError = fmt : : sprintf ( " could not open file! (%s) \n if this is raw sample data, you may import it by right-clicking the Load Sample icon and selecting \" import raw \" . " , sf_error_number ( err ) ) ;
2022-03-23 22:00:40 +00:00
}
2022-08-13 09:17:32 +00:00
return NULL ;
2021-12-17 08:33:12 +00:00
}
2022-03-23 21:39:08 +00:00
if ( si . frames > 16777215 ) {
lastError = " this sample is too big! max sample size is 16777215. " ;
2022-06-28 07:00:08 +00:00
sfWrap . doClose ( ) ;
2022-03-24 02:38:28 +00:00
BUSY_END ;
2022-08-13 09:17:32 +00:00
return NULL ;
2021-12-17 08:33:12 +00:00
}
2022-06-17 03:07:12 +00:00
void * buf = NULL ;
sf_count_t sampleLen = sizeof ( short ) ;
if ( ( si . format & SF_FORMAT_SUBMASK ) = = SF_FORMAT_PCM_U8 ) {
logD ( " sample is 8-bit unsigned " ) ;
buf = new unsigned char [ si . channels * si . frames ] ;
sampleLen = sizeof ( unsigned char ) ;
} else if ( ( si . format & SF_FORMAT_SUBMASK ) = = SF_FORMAT_FLOAT ) {
logD ( " sample is 32-bit float " ) ;
buf = new float [ si . channels * si . frames ] ;
sampleLen = sizeof ( float ) ;
} else {
logD ( " sample is 16-bit signed " ) ;
buf = new short [ si . channels * si . frames ] ;
sampleLen = sizeof ( short ) ;
}
2022-06-24 03:33:25 +00:00
if ( ( si . format & SF_FORMAT_SUBMASK ) = = SF_FORMAT_PCM_U8 | | ( si . format & SF_FORMAT_SUBMASK ) = = SF_FORMAT_FLOAT ) {
if ( sf_read_raw ( f , buf , si . frames * si . channels * sampleLen ) ! = ( si . frames * si . channels * sampleLen ) ) {
logW ( " sample read size mismatch! " ) ;
}
} else {
if ( sf_read_short ( f , ( short * ) buf , si . frames * si . channels ) ! = ( si . frames * si . channels ) ) {
logW ( " sample read size mismatch! " ) ;
}
2021-12-17 08:33:12 +00:00
}
DivSample * sample = new DivSample ;
int sampleCount = ( int ) song . sample . size ( ) ;
2022-04-12 08:34:53 +00:00
sample - > name = stripPath ;
2021-12-17 08:33:12 +00:00
int index = 0 ;
2022-02-24 08:57:45 +00:00
if ( ( si . format & SF_FORMAT_SUBMASK ) = = SF_FORMAT_PCM_U8 ) {
2022-07-22 04:36:42 +00:00
sample - > depth = DIV_SAMPLE_DEPTH_8BIT ;
2022-02-24 08:57:45 +00:00
} else {
2022-07-22 04:36:42 +00:00
sample - > depth = DIV_SAMPLE_DEPTH_16BIT ;
2022-02-24 08:57:45 +00:00
}
sample - > init ( si . frames ) ;
2022-06-17 03:07:12 +00:00
if ( ( si . format & SF_FORMAT_SUBMASK ) = = SF_FORMAT_PCM_U8 ) {
for ( int i = 0 ; i < si . frames * si . channels ; i + = si . channels ) {
int averaged = 0 ;
for ( int j = 0 ; j < si . channels ; j + + ) {
2022-06-17 03:10:57 +00:00
averaged + = ( ( int ) ( ( unsigned char * ) buf ) [ i + j ] ) - 128 ;
2022-06-17 03:07:12 +00:00
}
averaged / = si . channels ;
sample - > data8 [ index + + ] = averaged ;
2021-12-17 08:33:12 +00:00
}
2022-06-17 03:07:12 +00:00
delete [ ] ( unsigned char * ) buf ;
} else if ( ( si . format & SF_FORMAT_SUBMASK ) = = SF_FORMAT_FLOAT ) {
for ( int i = 0 ; i < si . frames * si . channels ; i + = si . channels ) {
float averaged = 0.0f ;
for ( int j = 0 ; j < si . channels ; j + + ) {
averaged + = ( ( float * ) buf ) [ i + j ] ;
}
averaged / = si . channels ;
averaged * = 32767.0 ;
if ( averaged < - 32768.0 ) averaged = - 32768.0 ;
if ( averaged > 32767.0 ) averaged = 32767.0 ;
sample - > data16 [ index + + ] = averaged ;
}
delete [ ] ( float * ) buf ;
} else {
for ( int i = 0 ; i < si . frames * si . channels ; i + = si . channels ) {
int averaged = 0 ;
for ( int j = 0 ; j < si . channels ; j + + ) {
averaged + = ( ( short * ) buf ) [ i + j ] ;
}
averaged / = si . channels ;
2022-02-24 08:57:45 +00:00
sample - > data16 [ index + + ] = averaged ;
}
2022-06-17 03:07:12 +00:00
delete [ ] ( short * ) buf ;
2021-12-17 08:33:12 +00:00
}
2022-06-17 03:07:12 +00:00
2021-12-23 23:04:44 +00:00
sample - > rate = si . samplerate ;
if ( sample - > rate < 4000 ) sample - > rate = 4000 ;
2022-02-22 09:01:57 +00:00
if ( sample - > rate > 96000 ) sample - > rate = 96000 ;
2022-02-23 13:32:35 +00:00
sample - > centerRate = si . samplerate ;
SF_INSTRUMENT inst ;
if ( sf_command ( f , SFC_GET_INSTRUMENT , & inst , sizeof ( inst ) ) = = SF_TRUE )
{
// There's no documentation on libsndfile detune range, but the code
// implies -50..50. Yet when loading a file you can get a >50 value.
if ( inst . detune > 50 )
inst . detune = inst . detune - 100 ;
short pitch = ( ( 0x3c - inst . basenote ) * 100 ) + inst . detune ;
sample - > centerRate = si . samplerate * pow ( 2.0 , pitch / ( 12.0 * 100.0 ) ) ;
2022-08-11 13:21:54 +00:00
if ( inst . loop_count & & inst . loops [ 0 ] . mode > = SF_LOOP_FORWARD )
2022-02-23 13:32:35 +00:00
{
2022-08-27 08:28:08 +00:00
sample - > loop = true ;
2022-08-11 13:21:54 +00:00
sample - > loopMode = ( DivSampleLoopMode ) ( inst . loops [ 0 ] . mode - SF_LOOP_FORWARD ) ;
2022-02-23 13:32:35 +00:00
sample - > loopStart = inst . loops [ 0 ] . start ;
2022-07-22 04:36:42 +00:00
sample - > loopEnd = inst . loops [ 0 ] . end ;
2022-02-23 13:32:35 +00:00
if ( inst . loops [ 0 ] . end < ( unsigned int ) sampleCount )
sampleCount = inst . loops [ 0 ] . end ;
}
2022-08-11 13:21:54 +00:00
else
sample - > loop = false ;
2022-02-23 13:32:35 +00:00
}
2021-12-17 08:33:12 +00:00
2022-02-23 13:32:35 +00:00
if ( sample - > centerRate < 4000 ) sample - > centerRate = 4000 ;
if ( sample - > centerRate > 64000 ) sample - > centerRate = 64000 ;
2022-06-28 07:00:08 +00:00
sfWrap . doClose ( ) ;
2022-03-24 02:38:28 +00:00
BUSY_END ;
2022-08-13 09:17:32 +00:00
return sample ;
2022-05-23 00:01:50 +00:00
# endif
2021-12-17 08:33:12 +00:00
}
2022-08-13 11:25:11 +00:00
DivSample * DivEngine : : sampleFromFileRaw ( const char * path , DivSampleDepth depth , int channels , bool bigEndian , bool unsign ) {
2022-08-13 10:50:36 +00:00
if ( song . sample . size ( ) > = 256 ) {
lastError = " too many samples! " ;
return NULL ;
}
if ( channels < 1 ) {
lastError = " invalid channel count " ;
return NULL ;
}
if ( depth ! = DIV_SAMPLE_DEPTH_8BIT & & depth ! = DIV_SAMPLE_DEPTH_16BIT ) {
if ( channels ! = 1 ) {
lastError = " channel count has to be 1 for non-8/16-bit format " ;
return NULL ;
}
}
BUSY_BEGIN ;
warnings = " " ;
const char * pathRedux = strrchr ( path , DIR_SEPARATOR ) ;
if ( pathRedux = = NULL ) {
pathRedux = path ;
} else {
pathRedux + + ;
}
String stripPath ;
const char * pathReduxEnd = strrchr ( pathRedux , ' . ' ) ;
if ( pathReduxEnd = = NULL ) {
stripPath = pathRedux ;
} else {
for ( const char * i = pathRedux ; i ! = pathReduxEnd & & ( * i ) ; i + + ) {
stripPath + = * i ;
}
}
size_t len = 0 ;
size_t lenDivided = 0 ;
DivSample * sample = new DivSample ;
sample - > name = stripPath ;
FILE * f = ps_fopen ( path , " rb " ) ;
if ( f = = NULL ) {
BUSY_END ;
lastError = fmt : : sprintf ( " could not open file! (%s) " , strerror ( errno ) ) ;
delete sample ;
return NULL ;
}
if ( fseek ( f , 0 , SEEK_END ) < 0 ) {
fclose ( f ) ;
BUSY_END ;
lastError = fmt : : sprintf ( " could not get file length! (%s) " , strerror ( errno ) ) ;
delete sample ;
return NULL ;
}
len = ftell ( f ) ;
if ( len = = 0 ) {
fclose ( f ) ;
BUSY_END ;
lastError = " file is empty! " ;
delete sample ;
return NULL ;
}
if ( len = = ( SIZE_MAX > > 1 ) ) {
fclose ( f ) ;
BUSY_END ;
lastError = " file is invalid! " ;
delete sample ;
return NULL ;
}
if ( fseek ( f , 0 , SEEK_SET ) < 0 ) {
fclose ( f ) ;
BUSY_END ;
lastError = fmt : : sprintf ( " could not seek to beginning of file! (%s) " , strerror ( errno ) ) ;
delete sample ;
return NULL ;
}
lenDivided = len / channels ;
unsigned int samples = lenDivided ;
switch ( depth ) {
case DIV_SAMPLE_DEPTH_1BIT :
case DIV_SAMPLE_DEPTH_1BIT_DPCM :
samples = lenDivided * 8 ;
break ;
case DIV_SAMPLE_DEPTH_YMZ_ADPCM :
case DIV_SAMPLE_DEPTH_QSOUND_ADPCM :
case DIV_SAMPLE_DEPTH_ADPCM_A :
case DIV_SAMPLE_DEPTH_ADPCM_B :
case DIV_SAMPLE_DEPTH_VOX :
samples = lenDivided * 2 ;
break ;
case DIV_SAMPLE_DEPTH_8BIT :
samples = lenDivided ;
break ;
case DIV_SAMPLE_DEPTH_BRR :
samples = 16 * ( ( lenDivided + 8 ) / 9 ) ;
break ;
case DIV_SAMPLE_DEPTH_16BIT :
samples = ( lenDivided + 1 ) / 2 ;
break ;
default :
break ;
}
if ( samples > 16777215 ) {
fclose ( f ) ;
BUSY_END ;
lastError = " this sample is too big! max sample size is 16777215. " ;
delete sample ;
return NULL ;
}
sample - > rate = 32000 ;
sample - > centerRate = 32000 ;
sample - > depth = depth ;
sample - > init ( samples ) ;
unsigned char * buf = new unsigned char [ len ] ;
if ( fread ( buf , 1 , len , f ) = = 0 ) {
fclose ( f ) ;
BUSY_END ;
lastError = fmt : : sprintf ( " could not read file! (%s) " , strerror ( errno ) ) ;
delete [ ] buf ;
delete sample ;
return NULL ;
}
fclose ( f ) ;
// import sample
size_t pos = 0 ;
if ( depth = = DIV_SAMPLE_DEPTH_16BIT ) {
for ( unsigned int i = 0 ; i < samples ; i + + ) {
int accum = 0 ;
for ( int j = 0 ; j < channels ; j + + ) {
if ( pos + 1 > = len ) break ;
2022-08-13 11:25:11 +00:00
if ( bigEndian ) {
accum + = ( short ) ( ( ( short ) ( ( buf [ pos ] < < 8 ) | buf [ pos + 1 ] ) ) ^ ( unsign ? 0x8000 : 0 ) ) ;
} else {
accum + = ( short ) ( ( ( short ) ( buf [ pos ] | ( buf [ pos + 1 ] < < 8 ) ) ) ^ ( unsign ? 0x8000 : 0 ) ) ;
}
2022-08-13 10:50:36 +00:00
pos + = 2 ;
}
accum / = channels ;
sample - > data16 [ i ] = accum ;
}
} else if ( depth = = DIV_SAMPLE_DEPTH_8BIT ) {
for ( unsigned int i = 0 ; i < samples ; i + + ) {
int accum = 0 ;
for ( int j = 0 ; j < channels ; j + + ) {
if ( pos > = len ) break ;
2022-08-13 11:25:11 +00:00
accum + = ( signed char ) ( buf [ pos + + ] ^ ( unsign ? 0x80 : 0 ) ) ;
2022-08-13 10:50:36 +00:00
}
accum / = channels ;
sample - > data8 [ i ] = accum ;
}
} else {
memcpy ( sample - > getCurBuf ( ) , buf , len ) ;
}
delete [ ] buf ;
BUSY_END ;
return sample ;
}
2021-12-17 08:33:12 +00:00
void DivEngine : : delSample ( int index ) {
2022-03-24 02:38:28 +00:00
BUSY_BEGIN ;
2022-06-17 04:39:38 +00:00
sPreview . sample = - 1 ;
sPreview . pos = 0 ;
2022-08-11 13:21:54 +00:00
sPreview . dir = false ;
2022-03-21 21:17:51 +00:00
saveLock . lock ( ) ;
2021-12-17 08:33:12 +00:00
if ( index > = 0 & & index < ( int ) song . sample . size ( ) ) {
delete song . sample [ index ] ;
song . sample . erase ( song . sample . begin ( ) + index ) ;
song . sampleLen = song . sample . size ( ) ;
renderSamples ( ) ;
}
2022-03-21 21:17:51 +00:00
saveLock . unlock ( ) ;
2022-03-24 02:38:28 +00:00
BUSY_END ;
2021-12-17 08:33:12 +00:00
}
2021-12-22 22:39:16 +00:00
void DivEngine : : addOrder ( bool duplicate , bool where ) {
2022-01-10 03:17:03 +00:00
unsigned char order [ DIV_MAX_CHANS ] ;
2022-05-15 06:42:49 +00:00
if ( curSubSong - > ordersLen > = 0xff ) return ;
2022-05-30 19:02:54 +00:00
memset ( order , 0 , DIV_MAX_CHANS ) ;
2022-03-24 02:38:28 +00:00
BUSY_BEGIN_SOFT ;
2021-12-22 22:39:16 +00:00
if ( duplicate ) {
2022-01-08 22:15:12 +00:00
for ( int i = 0 ; i < DIV_MAX_CHANS ; i + + ) {
2022-05-15 06:42:49 +00:00
order [ i ] = curOrders - > ord [ i ] [ curOrder ] ;
2021-12-22 22:39:16 +00:00
}
} else {
bool used [ 256 ] ;
for ( int i = 0 ; i < chans ; i + + ) {
memset ( used , 0 , sizeof ( bool ) * 256 ) ;
2022-05-15 06:42:49 +00:00
for ( int j = 0 ; j < curSubSong - > ordersLen ; j + + ) {
used [ curOrders - > ord [ i ] [ j ] ] = true ;
2021-12-22 22:39:16 +00:00
}
2022-04-08 22:21:36 +00:00
order [ i ] = 0xff ;
2021-12-22 22:39:16 +00:00
for ( int j = 0 ; j < 256 ; j + + ) {
if ( ! used [ j ] ) {
order [ i ] = j ;
break ;
}
}
}
}
if ( where ) { // at the end
2022-03-21 21:17:51 +00:00
saveLock . lock ( ) ;
2022-01-08 22:15:12 +00:00
for ( int i = 0 ; i < DIV_MAX_CHANS ; i + + ) {
2022-05-15 06:42:49 +00:00
curOrders - > ord [ i ] [ curSubSong - > ordersLen ] = order [ i ] ;
2021-12-22 22:39:16 +00:00
}
2022-05-15 06:42:49 +00:00
curSubSong - > ordersLen + + ;
2022-03-21 21:17:51 +00:00
saveLock . unlock ( ) ;
2021-12-22 22:39:16 +00:00
} else { // after current order
2022-03-21 21:17:51 +00:00
saveLock . lock ( ) ;
2022-01-08 22:15:12 +00:00
for ( int i = 0 ; i < DIV_MAX_CHANS ; i + + ) {
2022-05-15 06:42:49 +00:00
for ( int j = curSubSong - > ordersLen ; j > curOrder ; j - - ) {
curOrders - > ord [ i ] [ j ] = curOrders - > ord [ i ] [ j - 1 ] ;
2021-12-22 22:39:16 +00:00
}
2022-05-15 06:42:49 +00:00
curOrders - > ord [ i ] [ curOrder + 1 ] = order [ i ] ;
2021-12-22 22:39:16 +00:00
}
2022-05-15 06:42:49 +00:00
curSubSong - > ordersLen + + ;
2022-03-21 21:17:51 +00:00
saveLock . unlock ( ) ;
2021-12-22 22:39:16 +00:00
curOrder + + ;
2021-12-28 23:23:57 +00:00
if ( playing & & ! freelance ) {
2021-12-22 22:39:16 +00:00
playSub ( false ) ;
}
}
2022-03-24 02:38:28 +00:00
BUSY_END ;
2021-12-22 22:39:16 +00:00
}
2022-02-12 08:59:05 +00:00
void DivEngine : : deepCloneOrder ( bool where ) {
unsigned char order [ DIV_MAX_CHANS ] ;
2022-05-15 06:42:49 +00:00
if ( curSubSong - > ordersLen > = 0xff ) return ;
2022-02-12 23:02:33 +00:00
warnings = " " ;
2022-03-24 02:38:28 +00:00
BUSY_BEGIN_SOFT ;
2022-02-12 08:59:05 +00:00
for ( int i = 0 ; i < chans ; i + + ) {
2022-02-21 04:07:46 +00:00
bool didNotFind = true ;
2022-04-11 03:12:02 +00:00
logD ( " channel %d " , i ) ;
2022-05-15 06:42:49 +00:00
order [ i ] = curOrders - > ord [ i ] [ curOrder ] ;
2022-02-12 08:59:05 +00:00
// find free slot
2022-04-08 22:21:36 +00:00
for ( int j = 0 ; j < 256 ; j + + ) {
2022-04-11 03:12:02 +00:00
logD ( " finding free slot in %d... " , j ) ;
2022-05-15 06:42:49 +00:00
if ( curPat [ i ] . data [ j ] = = NULL ) {
2022-02-12 08:59:05 +00:00
int origOrd = order [ i ] ;
order [ i ] = j ;
2022-05-15 06:42:49 +00:00
DivPattern * oldPat = curPat [ i ] . getPattern ( origOrd , false ) ;
DivPattern * pat = curPat [ i ] . getPattern ( j , true ) ;
2022-02-12 08:59:05 +00:00
memcpy ( pat - > data , oldPat - > data , 256 * 32 * sizeof ( short ) ) ;
2022-04-11 03:12:02 +00:00
logD ( " found at %d " , j ) ;
2022-02-21 04:07:46 +00:00
didNotFind = false ;
2022-02-12 08:59:05 +00:00
break ;
}
}
2022-02-21 04:07:46 +00:00
if ( didNotFind ) {
2022-02-12 23:02:33 +00:00
addWarning ( fmt : : sprintf ( " no free patterns in channel %d! " , i ) ) ;
}
2022-02-12 08:59:05 +00:00
}
if ( where ) { // at the end
2022-03-21 21:17:51 +00:00
saveLock . lock ( ) ;
2022-02-12 08:59:05 +00:00
for ( int i = 0 ; i < chans ; i + + ) {
2022-05-15 06:42:49 +00:00
curOrders - > ord [ i ] [ curSubSong - > ordersLen ] = order [ i ] ;
2022-02-12 08:59:05 +00:00
}
2022-05-15 06:42:49 +00:00
curSubSong - > ordersLen + + ;
2022-03-21 21:17:51 +00:00
saveLock . unlock ( ) ;
2022-02-12 08:59:05 +00:00
} else { // after current order
2022-03-21 21:17:51 +00:00
saveLock . lock ( ) ;
2022-02-12 08:59:05 +00:00
for ( int i = 0 ; i < chans ; i + + ) {
2022-05-15 06:42:49 +00:00
for ( int j = curSubSong - > ordersLen ; j > curOrder ; j - - ) {
curOrders - > ord [ i ] [ j ] = curOrders - > ord [ i ] [ j - 1 ] ;
2022-02-12 08:59:05 +00:00
}
2022-05-15 06:42:49 +00:00
curOrders - > ord [ i ] [ curOrder + 1 ] = order [ i ] ;
2022-02-12 08:59:05 +00:00
}
2022-05-15 06:42:49 +00:00
curSubSong - > ordersLen + + ;
2022-03-21 21:17:51 +00:00
saveLock . unlock ( ) ;
2022-02-12 08:59:05 +00:00
curOrder + + ;
if ( playing & & ! freelance ) {
playSub ( false ) ;
}
}
2022-03-24 02:38:28 +00:00
BUSY_END ;
2022-02-12 08:59:05 +00:00
}
2021-12-22 22:39:16 +00:00
void DivEngine : : deleteOrder ( ) {
2022-05-15 06:42:49 +00:00
if ( curSubSong - > ordersLen < = 1 ) return ;
2022-03-24 02:38:28 +00:00
BUSY_BEGIN_SOFT ;
2022-03-21 21:17:51 +00:00
saveLock . lock ( ) ;
2022-01-08 22:15:12 +00:00
for ( int i = 0 ; i < DIV_MAX_CHANS ; i + + ) {
2022-05-15 06:42:49 +00:00
for ( int j = curOrder ; j < curSubSong - > ordersLen ; j + + ) {
curOrders - > ord [ i ] [ j ] = curOrders - > ord [ i ] [ j + 1 ] ;
2021-12-22 22:39:16 +00:00
}
}
2022-05-15 06:42:49 +00:00
curSubSong - > ordersLen - - ;
2022-03-21 21:17:51 +00:00
saveLock . unlock ( ) ;
2022-05-15 06:42:49 +00:00
if ( curOrder > = curSubSong - > ordersLen ) curOrder = curSubSong - > ordersLen - 1 ;
2021-12-28 23:23:57 +00:00
if ( playing & & ! freelance ) {
2021-12-22 22:39:16 +00:00
playSub ( false ) ;
}
2022-03-24 02:38:28 +00:00
BUSY_END ;
2021-12-22 22:39:16 +00:00
}
void DivEngine : : moveOrderUp ( ) {
2022-03-24 02:38:28 +00:00
BUSY_BEGIN_SOFT ;
2021-12-22 22:39:16 +00:00
if ( curOrder < 1 ) {
2022-03-24 02:38:28 +00:00
BUSY_END ;
2021-12-22 22:39:16 +00:00
return ;
}
2022-03-21 21:17:51 +00:00
saveLock . lock ( ) ;
2022-01-08 22:15:12 +00:00
for ( int i = 0 ; i < DIV_MAX_CHANS ; i + + ) {
2022-05-15 06:42:49 +00:00
curOrders - > ord [ i ] [ curOrder ] ^ = curOrders - > ord [ i ] [ curOrder - 1 ] ;
curOrders - > ord [ i ] [ curOrder - 1 ] ^ = curOrders - > ord [ i ] [ curOrder ] ;
curOrders - > ord [ i ] [ curOrder ] ^ = curOrders - > ord [ i ] [ curOrder - 1 ] ;
2021-12-22 22:39:16 +00:00
}
2022-03-21 21:17:51 +00:00
saveLock . unlock ( ) ;
2021-12-22 22:39:16 +00:00
curOrder - - ;
2021-12-28 23:23:57 +00:00
if ( playing & & ! freelance ) {
2021-12-22 22:39:16 +00:00
playSub ( false ) ;
}
2022-03-24 02:38:28 +00:00
BUSY_END ;
2021-12-22 22:39:16 +00:00
}
void DivEngine : : moveOrderDown ( ) {
2022-03-24 02:38:28 +00:00
BUSY_BEGIN_SOFT ;
2022-05-15 06:42:49 +00:00
if ( curOrder > = curSubSong - > ordersLen - 1 ) {
2022-03-24 02:38:28 +00:00
BUSY_END ;
2021-12-22 22:39:16 +00:00
return ;
}
2022-03-21 21:17:51 +00:00
saveLock . lock ( ) ;
2022-01-08 22:15:12 +00:00
for ( int i = 0 ; i < DIV_MAX_CHANS ; i + + ) {
2022-05-15 06:42:49 +00:00
curOrders - > ord [ i ] [ curOrder ] ^ = curOrders - > ord [ i ] [ curOrder + 1 ] ;
curOrders - > ord [ i ] [ curOrder + 1 ] ^ = curOrders - > ord [ i ] [ curOrder ] ;
curOrders - > ord [ i ] [ curOrder ] ^ = curOrders - > ord [ i ] [ curOrder + 1 ] ;
2021-12-22 22:39:16 +00:00
}
2022-03-21 21:17:51 +00:00
saveLock . unlock ( ) ;
2021-12-22 22:39:16 +00:00
curOrder + + ;
2021-12-28 23:23:57 +00:00
if ( playing & & ! freelance ) {
2021-12-22 22:39:16 +00:00
playSub ( false ) ;
}
2022-03-24 02:38:28 +00:00
BUSY_END ;
2021-12-22 22:39:16 +00:00
}
2022-02-08 04:42:54 +00:00
void DivEngine : : exchangeIns ( int one , int two ) {
for ( int i = 0 ; i < chans ; i + + ) {
2022-08-16 03:54:31 +00:00
for ( size_t j = 0 ; j < song . subsong . size ( ) ; j + + ) {
for ( int k = 0 ; k < 256 ; k + + ) {
if ( song . subsong [ j ] - > pat [ i ] . data [ k ] = = NULL ) continue ;
for ( int l = 0 ; l < curSubSong - > patLen ; l + + ) {
if ( song . subsong [ j ] - > pat [ i ] . data [ k ] - > data [ l ] [ 2 ] = = one ) {
song . subsong [ j ] - > pat [ i ] . data [ k ] - > data [ l ] [ 2 ] = two ;
} else if ( song . subsong [ j ] - > pat [ i ] . data [ k ] - > data [ l ] [ 2 ] = = two ) {
song . subsong [ j ] - > pat [ i ] . data [ k ] - > data [ l ] [ 2 ] = one ;
}
2022-02-08 04:42:54 +00:00
}
}
}
}
}
2022-01-11 08:52:11 +00:00
bool DivEngine : : moveInsUp ( int which ) {
if ( which < 1 | | which > = ( int ) song . ins . size ( ) ) return false ;
2022-03-24 02:38:28 +00:00
BUSY_BEGIN ;
2022-01-11 08:52:11 +00:00
DivInstrument * prev = song . ins [ which ] ;
2022-03-21 21:17:51 +00:00
saveLock . lock ( ) ;
2022-01-11 08:52:11 +00:00
song . ins [ which ] = song . ins [ which - 1 ] ;
song . ins [ which - 1 ] = prev ;
2022-02-08 04:42:54 +00:00
exchangeIns ( which , which - 1 ) ;
2022-03-21 21:17:51 +00:00
saveLock . unlock ( ) ;
2022-03-24 02:38:28 +00:00
BUSY_END ;
2022-01-11 08:52:11 +00:00
return true ;
}
bool DivEngine : : moveWaveUp ( int which ) {
if ( which < 1 | | which > = ( int ) song . wave . size ( ) ) return false ;
2022-03-24 02:38:28 +00:00
BUSY_BEGIN ;
2022-01-11 08:52:11 +00:00
DivWavetable * prev = song . wave [ which ] ;
2022-03-21 21:17:51 +00:00
saveLock . lock ( ) ;
2022-01-11 08:52:11 +00:00
song . wave [ which ] = song . wave [ which - 1 ] ;
song . wave [ which - 1 ] = prev ;
2022-03-21 21:17:51 +00:00
saveLock . unlock ( ) ;
2022-03-24 02:38:28 +00:00
BUSY_END ;
2022-01-11 08:52:11 +00:00
return true ;
}
bool DivEngine : : moveSampleUp ( int which ) {
if ( which < 1 | | which > = ( int ) song . sample . size ( ) ) return false ;
2022-03-24 02:38:28 +00:00
BUSY_BEGIN ;
2022-06-17 04:39:38 +00:00
sPreview . sample = - 1 ;
sPreview . pos = 0 ;
2022-08-11 13:21:54 +00:00
sPreview . dir = false ;
2022-01-11 08:52:11 +00:00
DivSample * prev = song . sample [ which ] ;
2022-03-21 21:17:51 +00:00
saveLock . lock ( ) ;
2022-01-11 08:52:11 +00:00
song . sample [ which ] = song . sample [ which - 1 ] ;
song . sample [ which - 1 ] = prev ;
2022-03-21 21:17:51 +00:00
saveLock . unlock ( ) ;
2022-10-02 07:08:33 +00:00
renderSamples ( ) ;
2022-03-24 02:38:28 +00:00
BUSY_END ;
2022-01-11 08:52:11 +00:00
return true ;
}
bool DivEngine : : moveInsDown ( int which ) {
if ( which < 0 | | which > = ( ( int ) song . ins . size ( ) ) - 1 ) return false ;
2022-03-24 02:38:28 +00:00
BUSY_BEGIN ;
2022-01-11 08:52:11 +00:00
DivInstrument * prev = song . ins [ which ] ;
2022-03-21 21:17:51 +00:00
saveLock . lock ( ) ;
2022-01-11 08:52:11 +00:00
song . ins [ which ] = song . ins [ which + 1 ] ;
song . ins [ which + 1 ] = prev ;
2022-02-08 04:42:54 +00:00
exchangeIns ( which , which + 1 ) ;
2022-03-21 21:17:51 +00:00
saveLock . unlock ( ) ;
2022-03-24 02:38:28 +00:00
BUSY_END ;
2022-01-11 08:52:11 +00:00
return true ;
}
bool DivEngine : : moveWaveDown ( int which ) {
if ( which < 0 | | which > = ( ( int ) song . wave . size ( ) ) - 1 ) return false ;
2022-03-24 02:38:28 +00:00
BUSY_BEGIN ;
2022-01-11 08:52:11 +00:00
DivWavetable * prev = song . wave [ which ] ;
2022-03-21 21:17:51 +00:00
saveLock . lock ( ) ;
2022-01-11 08:52:11 +00:00
song . wave [ which ] = song . wave [ which + 1 ] ;
song . wave [ which + 1 ] = prev ;
2022-03-21 21:17:51 +00:00
saveLock . unlock ( ) ;
2022-03-24 02:38:28 +00:00
BUSY_END ;
2022-01-11 08:52:11 +00:00
return true ;
}
bool DivEngine : : moveSampleDown ( int which ) {
if ( which < 0 | | which > = ( ( int ) song . sample . size ( ) ) - 1 ) return false ;
2022-03-24 02:38:28 +00:00
BUSY_BEGIN ;
2022-06-17 04:39:38 +00:00
sPreview . sample = - 1 ;
sPreview . pos = 0 ;
2022-08-11 13:21:54 +00:00
sPreview . dir = false ;
2022-01-11 08:52:11 +00:00
DivSample * prev = song . sample [ which ] ;
2022-03-21 21:17:51 +00:00
saveLock . lock ( ) ;
2022-01-11 08:52:11 +00:00
song . sample [ which ] = song . sample [ which + 1 ] ;
song . sample [ which + 1 ] = prev ;
2022-03-21 21:17:51 +00:00
saveLock . unlock ( ) ;
2022-10-02 07:00:31 +00:00
renderSamples ( ) ;
2022-03-24 02:38:28 +00:00
BUSY_END ;
2022-01-11 08:52:11 +00:00
return true ;
}
2021-12-28 23:23:57 +00:00
void DivEngine : : noteOn ( int chan , int ins , int note , int vol ) {
2022-01-24 22:13:47 +00:00
if ( chan < 0 | | chan > = chans ) return ;
2022-03-24 02:38:28 +00:00
BUSY_BEGIN ;
2022-08-27 05:37:32 +00:00
pendingNotes . push_back ( DivNoteEvent ( chan , ins , note , vol , true ) ) ;
2021-12-28 23:23:57 +00:00
if ( ! playing ) {
reset ( ) ;
freelance = true ;
playing = true ;
}
2022-03-24 02:38:28 +00:00
BUSY_END ;
2021-12-28 23:23:57 +00:00
}
void DivEngine : : noteOff ( int chan ) {
2022-01-24 22:13:47 +00:00
if ( chan < 0 | | chan > = chans ) return ;
2022-03-24 02:38:28 +00:00
BUSY_BEGIN ;
2022-08-27 05:37:32 +00:00
pendingNotes . push_back ( DivNoteEvent ( chan , - 1 , - 1 , - 1 , false ) ) ;
2021-12-28 23:23:57 +00:00
if ( ! playing ) {
reset ( ) ;
freelance = true ;
playing = true ;
}
2022-03-24 02:38:28 +00:00
BUSY_END ;
2021-12-28 23:23:57 +00:00
}
2022-03-31 06:51:57 +00:00
void DivEngine : : autoNoteOn ( int ch , int ins , int note , int vol ) {
2022-04-28 10:04:34 +00:00
bool isViable [ DIV_MAX_CHANS ] ;
bool canPlayAnyway = false ;
2022-04-28 23:32:24 +00:00
bool notInViableChannel = false ;
2022-03-31 06:51:57 +00:00
if ( midiBaseChan < 0 ) midiBaseChan = 0 ;
if ( midiBaseChan > = chans ) midiBaseChan = chans - 1 ;
int finalChan = midiBaseChan ;
2022-04-28 10:04:34 +00:00
int finalChanType = getChannelType ( finalChan ) ;
2022-03-31 06:51:57 +00:00
if ( ! playing ) {
reset ( ) ;
freelance = true ;
playing = true ;
}
2022-04-28 10:04:34 +00:00
// 1. check which channels are viable for this instrument
DivInstrument * insInst = getIns ( ins ) ;
2022-05-13 08:18:14 +00:00
if ( getPreferInsType ( finalChan ) ! = insInst - > type & & getPreferInsSecondType ( finalChan ) ! = insInst - > type & & getPreferInsType ( finalChan ) ! = DIV_INS_NULL ) notInViableChannel = true ;
2022-04-28 10:04:34 +00:00
for ( int i = 0 ; i < chans ; i + + ) {
2022-05-13 08:18:14 +00:00
if ( ins = = - 1 | | ins > = song . insLen | | getPreferInsType ( i ) = = insInst - > type | | ( getPreferInsType ( i ) = = DIV_INS_NULL & & finalChanType = = DIV_CH_NOISE ) | | getPreferInsSecondType ( i ) = = insInst - > type ) {
2022-04-28 10:04:34 +00:00
if ( insInst - > type = = DIV_INS_OPL ) {
if ( insInst - > fm . ops = = 2 | | getChannelType ( i ) = = DIV_CH_OP ) {
isViable [ i ] = true ;
canPlayAnyway = true ;
} else {
isViable [ i ] = false ;
}
} else {
isViable [ i ] = true ;
canPlayAnyway = true ;
}
} else {
isViable [ i ] = false ;
}
}
if ( ! canPlayAnyway ) return ;
// 2. find a free channel
2022-03-31 06:51:57 +00:00
do {
2022-06-03 23:05:07 +00:00
if ( ( ! midiPoly ) | | ( isViable [ finalChan ] & & chan [ finalChan ] . midiNote = = - 1 & & ( insInst - > type = = DIV_INS_OPL | | getChannelType ( finalChan ) = = finalChanType | | notInViableChannel ) ) ) {
2022-03-31 06:51:57 +00:00
chan [ finalChan ] . midiNote = note ;
2022-04-28 10:04:34 +00:00
chan [ finalChan ] . midiAge = midiAgeCounter + + ;
2022-08-27 05:37:32 +00:00
pendingNotes . push_back ( DivNoteEvent ( finalChan , ins , note , vol , true ) ) ;
2022-04-28 10:04:34 +00:00
return ;
}
if ( + + finalChan > = chans ) {
finalChan = 0 ;
}
} while ( finalChan ! = midiBaseChan ) ;
// 3. find the oldest channel
int candidate = finalChan ;
do {
2022-04-28 23:32:24 +00:00
if ( isViable [ finalChan ] & & ( insInst - > type = = DIV_INS_OPL | | getChannelType ( finalChan ) = = finalChanType | | notInViableChannel ) & & chan [ finalChan ] . midiAge < chan [ candidate ] . midiAge ) {
2022-04-28 10:04:34 +00:00
candidate = finalChan ;
2022-03-31 06:51:57 +00:00
}
if ( + + finalChan > = chans ) {
finalChan = 0 ;
}
} while ( finalChan ! = midiBaseChan ) ;
2022-04-28 10:04:34 +00:00
chan [ candidate ] . midiNote = note ;
chan [ candidate ] . midiAge = midiAgeCounter + + ;
2022-08-27 05:37:32 +00:00
pendingNotes . push_back ( DivNoteEvent ( candidate , ins , note , vol , true ) ) ;
2022-03-31 06:51:57 +00:00
}
void DivEngine : : autoNoteOff ( int ch , int note , int vol ) {
if ( ! playing ) {
reset ( ) ;
freelance = true ;
playing = true ;
}
//if (ch<0 || ch>=chans) return;
for ( int i = 0 ; i < chans ; i + + ) {
if ( chan [ i ] . midiNote = = note ) {
2022-08-27 05:37:32 +00:00
pendingNotes . push_back ( DivNoteEvent ( i , - 1 , - 1 , - 1 , false ) ) ;
2022-03-31 06:51:57 +00:00
chan [ i ] . midiNote = - 1 ;
}
}
}
2022-04-16 05:10:52 +00:00
void DivEngine : : autoNoteOffAll ( ) {
if ( ! playing ) {
reset ( ) ;
freelance = true ;
playing = true ;
}
for ( int i = 0 ; i < chans ; i + + ) {
if ( chan [ i ] . midiNote ! = - 1 ) {
2022-08-27 05:37:32 +00:00
pendingNotes . push_back ( DivNoteEvent ( i , - 1 , - 1 , - 1 , false ) ) ;
2022-04-16 05:10:52 +00:00
chan [ i ] . midiNote = - 1 ;
}
}
}
2022-06-03 23:05:07 +00:00
void DivEngine : : setAutoNotePoly ( bool poly ) {
midiPoly = poly ;
}
2021-12-11 08:34:43 +00:00
void DivEngine : : setOrder ( unsigned char order ) {
2022-03-24 02:38:28 +00:00
BUSY_BEGIN_SOFT ;
2021-12-11 08:34:43 +00:00
curOrder = order ;
2022-05-15 06:42:49 +00:00
if ( order > = curSubSong - > ordersLen ) curOrder = 0 ;
2022-06-06 06:05:06 +00:00
prevOrder = curOrder ;
2021-12-28 23:23:57 +00:00
if ( playing & & ! freelance ) {
2021-12-21 07:02:25 +00:00
playSub ( false ) ;
2021-12-11 08:34:43 +00:00
}
2022-03-24 02:38:28 +00:00
BUSY_END ;
2021-05-11 20:08:08 +00:00
}
2022-09-30 07:14:54 +00:00
void DivEngine : : updateSysFlags ( int system , bool restart ) {
2022-03-24 02:38:28 +00:00
BUSY_BEGIN_SOFT ;
2022-09-30 01:13:40 +00:00
disCont [ system ] . dispatch - > setFlags ( song . systemFlags [ system ] ) ;
2022-01-28 23:12:56 +00:00
disCont [ system ] . setRates ( got . rate ) ;
2022-03-20 03:03:12 +00:00
if ( restart & & isPlaying ( ) ) {
2022-02-07 22:24:26 +00:00
playSub ( false ) ;
}
2022-03-24 02:38:28 +00:00
BUSY_END ;
2022-01-28 23:12:56 +00:00
}
2022-03-16 04:30:15 +00:00
void DivEngine : : setSongRate ( float hz , bool pal ) {
2022-03-24 02:38:28 +00:00
BUSY_BEGIN ;
2022-03-21 21:17:51 +00:00
saveLock . lock ( ) ;
2022-05-15 06:42:49 +00:00
curSubSong - > pal = ! pal ;
curSubSong - > hz = hz ;
2022-03-16 04:30:15 +00:00
// what?
2022-05-15 06:42:49 +00:00
curSubSong - > customTempo = true ;
2022-01-12 07:45:26 +00:00
divider = 60 ;
2022-05-15 06:42:49 +00:00
if ( curSubSong - > customTempo ) {
divider = curSubSong - > hz ;
2022-01-12 07:45:26 +00:00
} else {
2022-05-15 06:42:49 +00:00
if ( curSubSong - > pal ) {
2022-01-12 07:45:26 +00:00
divider = 60 ;
} else {
divider = 50 ;
}
2022-01-08 21:03:32 +00:00
}
2022-03-21 21:17:51 +00:00
saveLock . unlock ( ) ;
2022-03-24 02:38:28 +00:00
BUSY_END ;
2021-12-15 22:32:08 +00:00
}
2021-06-09 08:33:03 +00:00
void DivEngine : : setAudio ( DivAudioEngines which ) {
audioEngine = which ;
}
void DivEngine : : setView ( DivStatusView which ) {
view = which ;
}
2022-01-04 05:02:41 +00:00
bool DivEngine : : getMetronome ( ) {
return metronome ;
}
void DivEngine : : setMetronome ( bool enable ) {
metronome = enable ;
metroAmp = 0 ;
}
2022-04-13 07:29:07 +00:00
void DivEngine : : setMetronomeVol ( float vol ) {
metroVol = vol ;
}
2021-12-18 09:26:17 +00:00
void DivEngine : : setConsoleMode ( bool enable ) {
consoleMode = enable ;
}
2022-02-06 04:48:56 +00:00
bool DivEngine : : switchMaster ( ) {
2022-09-11 04:32:04 +00:00
logI ( " switching output... " ) ;
deinitAudioBackend ( true ) ;
2022-01-17 06:42:26 +00:00
if ( initAudioBackend ( ) ) {
for ( int i = 0 ; i < song . systemLen ; i + + ) {
disCont [ i ] . setRates ( got . rate ) ;
disCont [ i ] . setQuality ( lowQuality ) ;
}
if ( ! output - > setRun ( true ) ) {
2022-04-11 03:12:02 +00:00
logE ( " error while activating audio! " ) ;
2022-02-06 04:48:56 +00:00
return false ;
2022-01-17 06:42:26 +00:00
}
2022-02-06 04:48:56 +00:00
} else {
return false ;
2022-01-17 06:42:26 +00:00
}
2022-05-02 07:54:23 +00:00
renderSamples ( ) ;
2022-02-06 04:48:56 +00:00
return true ;
2022-01-17 06:42:26 +00:00
}
2022-03-31 06:51:57 +00:00
void DivEngine : : setMidiBaseChan ( int chan ) {
if ( chan < 0 | | chan > = chans ) chan = 0 ;
midiBaseChan = chan ;
}
2022-04-01 07:21:10 +00:00
void DivEngine : : setMidiDirect ( bool value ) {
midiIsDirect = value ;
}
2022-03-28 23:19:47 +00:00
void DivEngine : : setMidiCallback ( std : : function < int ( const TAMidiMessage & ) > what ) {
2022-03-28 20:24:09 +00:00
midiCallback = what ;
}
2022-05-08 07:01:32 +00:00
bool DivEngine : : sendMidiMessage ( TAMidiMessage & msg ) {
if ( output = = NULL ) {
logW ( " output is NULL! " ) ;
return false ;
}
if ( output - > midiOut = = NULL ) {
logW ( " MIDI output is NULL! " ) ;
return false ;
}
BUSY_BEGIN ;
logD ( " sending MIDI message... " ) ;
bool ret = ( output - > midiOut - > send ( msg ) ) ;
BUSY_END ;
return ret ;
}
2022-03-19 08:42:44 +00:00
void DivEngine : : synchronized ( const std : : function < void ( ) > & what ) {
2022-03-24 02:38:28 +00:00
BUSY_BEGIN ;
2022-03-19 08:42:44 +00:00
what ( ) ;
2022-03-24 02:38:28 +00:00
BUSY_END ;
2022-03-19 08:42:44 +00:00
}
2022-03-21 21:17:51 +00:00
void DivEngine : : lockSave ( const std : : function < void ( ) > & what ) {
saveLock . lock ( ) ;
what ( ) ;
saveLock . unlock ( ) ;
}
void DivEngine : : lockEngine ( const std : : function < void ( ) > & what ) {
2022-03-24 02:38:28 +00:00
BUSY_BEGIN ;
2022-03-21 21:17:51 +00:00
saveLock . lock ( ) ;
what ( ) ;
saveLock . unlock ( ) ;
2022-03-24 02:38:28 +00:00
BUSY_END ;
2022-03-21 21:17:51 +00:00
}
2022-02-06 02:26:24 +00:00
TAAudioDesc & DivEngine : : getAudioDescWant ( ) {
return want ;
}
TAAudioDesc & DivEngine : : getAudioDescGot ( ) {
return got ;
}
2022-02-14 02:42:57 +00:00
std : : vector < String > & DivEngine : : getAudioDevices ( ) {
return audioDevs ;
}
2022-03-28 08:46:50 +00:00
std : : vector < String > & DivEngine : : getMidiIns ( ) {
return midiIns ;
}
std : : vector < String > & DivEngine : : getMidiOuts ( ) {
return midiOuts ;
}
2022-02-14 02:42:57 +00:00
void DivEngine : : rescanAudioDevices ( ) {
audioDevs . clear ( ) ;
if ( output ! = NULL ) {
audioDevs = output - > listAudioDevices ( ) ;
2022-03-28 08:46:50 +00:00
if ( output - > midiIn ! = NULL ) {
midiIns = output - > midiIn - > listDevices ( ) ;
}
if ( output - > midiOut ! = NULL ) {
midiOuts = output - > midiOut - > listDevices ( ) ;
}
2022-02-14 02:42:57 +00:00
}
}
2021-12-13 19:40:03 +00:00
void DivEngine : : initDispatch ( ) {
2022-03-24 02:38:28 +00:00
BUSY_BEGIN ;
2022-01-08 21:03:32 +00:00
for ( int i = 0 ; i < song . systemLen ; i + + ) {
2022-09-30 01:13:40 +00:00
disCont [ i ] . init ( song . system [ i ] , this , getChannelCount ( song . system [ i ] ) , got . rate , song . systemFlags [ i ] ) ;
2022-01-08 21:03:32 +00:00
disCont [ i ] . setRates ( got . rate ) ;
2022-01-17 06:42:26 +00:00
disCont [ i ] . setQuality ( lowQuality ) ;
2021-12-13 19:40:03 +00:00
}
2022-01-08 21:03:32 +00:00
recalcChans ( ) ;
2022-03-24 02:38:28 +00:00
BUSY_END ;
2021-12-13 19:40:03 +00:00
}
void DivEngine : : quitDispatch ( ) {
2022-03-24 02:38:28 +00:00
BUSY_BEGIN ;
2022-01-08 21:03:32 +00:00
for ( int i = 0 ; i < song . systemLen ; i + + ) {
disCont [ i ] . quit ( ) ;
}
2022-01-12 22:45:07 +00:00
cycles = 0 ;
clockDrift = 0 ;
2021-12-15 05:37:27 +00:00
chans = 0 ;
playing = false ;
speedAB = false ;
endOfSong = false ;
ticks = 0 ;
2022-05-18 05:05:25 +00:00
tempoAccum = 0 ;
2021-12-15 05:37:27 +00:00
curRow = 0 ;
curOrder = 0 ;
2022-06-06 06:05:06 +00:00
prevRow = 0 ;
prevOrder = 0 ;
2021-12-15 05:37:27 +00:00
nextSpeed = 3 ;
changeOrd = - 1 ;
changePos = 0 ;
totalTicks = 0 ;
2022-01-12 07:45:26 +00:00
totalSeconds = 0 ;
totalTicksR = 0 ;
2021-12-15 05:37:27 +00:00
totalCmds = 0 ;
lastCmds = 0 ;
cmdsPerSecond = 0 ;
2022-01-08 06:57:37 +00:00
for ( int i = 0 ; i < DIV_MAX_CHANS ; i + + ) {
2021-12-18 08:25:42 +00:00
isMuted [ i ] = 0 ;
}
2022-03-24 02:38:28 +00:00
BUSY_END ;
2021-12-13 19:40:03 +00:00
}
2022-01-17 06:20:02 +00:00
bool DivEngine : : initAudioBackend ( ) {
2022-01-17 06:42:26 +00:00
// load values
2022-09-11 04:32:04 +00:00
logI ( " initializing audio. " ) ;
2022-01-23 04:50:49 +00:00
if ( audioEngine = = DIV_AUDIO_NULL ) {
if ( getConfString ( " audioEngine " , " SDL " ) = = " JACK " ) {
audioEngine = DIV_AUDIO_JACK ;
} else {
audioEngine = DIV_AUDIO_SDL ;
}
2022-01-17 06:42:26 +00:00
}
lowQuality = getConfInt ( " audioQuality " , 0 ) ;
2022-02-04 22:04:36 +00:00
forceMono = getConfInt ( " forceMono " , 0 ) ;
2022-07-25 23:41:47 +00:00
clampSamples = getConfInt ( " clampSamples " , 0 ) ;
2022-04-15 10:37:23 +00:00
lowLatency = getConfInt ( " lowLatency " , 0 ) ;
2022-04-13 07:29:07 +00:00
metroVol = ( float ) ( getConfInt ( " metroVol " , 100 ) ) / 100.0f ;
2022-09-26 06:27:36 +00:00
midiOutClock = getConfInt ( " midiOutClock " , 0 ) ;
midiOutMode = getConfInt ( " midiOutMode " , DIV_MIDI_MODE_NOTE ) ;
2022-04-13 07:29:07 +00:00
if ( metroVol < 0.0f ) metroVol = 0.0f ;
if ( metroVol > 2.0f ) metroVol = 2.0f ;
2022-01-17 06:42:26 +00:00
2022-04-15 19:38:25 +00:00
if ( lowLatency ) logI ( " using low latency mode. " ) ;
2022-01-17 06:20:02 +00:00
switch ( audioEngine ) {
case DIV_AUDIO_JACK :
# ifndef HAVE_JACK
2022-04-11 03:12:02 +00:00
logE ( " Furnace was not compiled with JACK support! " ) ;
2022-01-17 06:20:02 +00:00
setConf ( " audioEngine " , " SDL " ) ;
saveConf ( ) ;
2022-05-23 00:01:50 +00:00
# ifdef HAVE_SDL2
2022-01-17 06:20:02 +00:00
output = new TAAudioSDL ;
2022-05-23 00:01:50 +00:00
# else
logE ( " Furnace was not compiled with SDL support either! " ) ;
output = new TAAudio ;
# endif
2022-01-17 06:20:02 +00:00
# else
output = new TAAudioJACK ;
# endif
break ;
case DIV_AUDIO_SDL :
2022-05-23 00:01:50 +00:00
# ifdef HAVE_SDL2
2022-01-17 06:20:02 +00:00
output = new TAAudioSDL ;
2022-05-23 00:01:50 +00:00
# else
logE ( " Furnace was not compiled with SDL support! " ) ;
output = new TAAudio ;
# endif
2022-01-17 06:20:02 +00:00
break ;
2022-01-23 04:50:49 +00:00
case DIV_AUDIO_DUMMY :
output = new TAAudio ;
break ;
2022-01-17 06:20:02 +00:00
default :
2022-04-11 03:12:02 +00:00
logE ( " invalid audio engine! " ) ;
2022-01-17 06:20:02 +00:00
return false ;
}
2022-02-14 02:42:57 +00:00
audioDevs = output - > listAudioDevices ( ) ;
want . deviceName = getConfString ( " audioDevice " , " " ) ;
2022-01-17 06:20:02 +00:00
want . bufsize = getConfInt ( " audioBufSize " , 1024 ) ;
want . rate = getConfInt ( " audioRate " , 44100 ) ;
want . fragments = 2 ;
want . inChans = 0 ;
want . outChans = 2 ;
want . outFormat = TA_AUDIO_FORMAT_F32 ;
want . name = " Furnace " ;
output - > setCallback ( process , this ) ;
if ( ! output - > init ( want , got ) ) {
2022-04-11 03:12:02 +00:00
logE ( " error while initializing audio! " ) ;
2022-01-17 06:42:26 +00:00
delete output ;
output = NULL ;
2022-02-06 22:00:01 +00:00
audioEngine = DIV_AUDIO_NULL ;
2022-01-17 06:20:02 +00:00
return false ;
}
2022-01-27 22:49:00 +00:00
2022-03-28 08:46:50 +00:00
if ( output - > initMidi ( false ) ) {
midiIns = output - > midiIn - > listDevices ( ) ;
midiOuts = output - > midiOut - > listDevices ( ) ;
} else {
2022-04-11 03:12:02 +00:00
logW ( " error while initializing MIDI! " ) ;
2022-03-28 08:46:50 +00:00
}
if ( output - > midiIn ) {
String inName = getConfString ( " midiInDevice " , " " ) ;
if ( ! inName . empty ( ) ) {
// try opening device
2022-04-11 03:12:02 +00:00
logI ( " opening MIDI input. " ) ;
2022-03-28 08:46:50 +00:00
if ( ! output - > midiIn - > openDevice ( inName ) ) {
2022-04-11 03:12:02 +00:00
logW ( " could not open MIDI input device! " ) ;
2022-03-28 08:46:50 +00:00
}
2022-06-17 04:55:17 +00:00
} else {
logV ( " no MIDI input device selected. " ) ;
2022-03-28 08:46:50 +00:00
}
}
2022-03-31 08:33:05 +00:00
if ( output - > midiOut ) {
String outName = getConfString ( " midiOutDevice " , " " ) ;
if ( ! outName . empty ( ) ) {
// try opening device
2022-04-11 03:12:02 +00:00
logI ( " opening MIDI output. " ) ;
2022-03-31 08:33:05 +00:00
if ( ! output - > midiOut - > openDevice ( outName ) ) {
2022-04-11 03:12:02 +00:00
logW ( " could not open MIDI output device! " ) ;
2022-03-31 08:33:05 +00:00
}
2022-06-17 04:55:17 +00:00
} else {
logV ( " no MIDI output device selected. " ) ;
2022-03-31 08:33:05 +00:00
}
}
2022-03-28 08:46:50 +00:00
2022-01-17 06:20:02 +00:00
return true ;
}
2022-09-11 04:32:04 +00:00
bool DivEngine : : deinitAudioBackend ( bool dueToSwitchMaster ) {
2022-01-17 06:20:02 +00:00
if ( output ! = NULL ) {
2022-09-11 04:32:04 +00:00
logI ( " closing audio output. " ) ;
2022-08-07 22:37:07 +00:00
output - > quit ( ) ;
2022-03-28 08:46:50 +00:00
if ( output - > midiIn ) {
if ( output - > midiIn - > isDeviceOpen ( ) ) {
2022-04-11 03:12:02 +00:00
logI ( " closing MIDI input. " ) ;
2022-03-28 08:46:50 +00:00
output - > midiIn - > closeDevice ( ) ;
}
}
2022-03-31 08:33:05 +00:00
if ( output - > midiOut ) {
if ( output - > midiOut - > isDeviceOpen ( ) ) {
2022-04-11 03:12:02 +00:00
logI ( " closing MIDI output. " ) ;
2022-03-31 08:33:05 +00:00
output - > midiOut - > closeDevice ( ) ;
}
}
2022-03-28 08:46:50 +00:00
output - > quitMidi ( ) ;
2022-01-17 06:20:02 +00:00
delete output ;
output = NULL ;
2022-09-11 04:32:04 +00:00
if ( dueToSwitchMaster ) {
audioEngine = DIV_AUDIO_NULL ;
}
2022-01-17 06:20:02 +00:00
}
return true ;
}
2022-01-17 04:32:13 +00:00
bool DivEngine : : init ( ) {
2022-04-26 23:32:33 +00:00
// register systems
2022-04-27 05:56:15 +00:00
if ( ! systemsRegistered ) registerSystems ( ) ;
2022-07-24 03:11:30 +00:00
2021-12-19 08:16:24 +00:00
// init config
2022-07-24 03:11:30 +00:00
initConfDir ( ) ;
2022-04-11 03:12:02 +00:00
logD ( " config path: %s " , configPath . c_str ( ) ) ;
2021-12-19 08:16:24 +00:00
2021-12-19 21:52:04 +00:00
loadConf ( ) ;
2022-05-13 03:15:03 +00:00
loadSampleROMs ( ) ;
2022-05-11 21:39:36 +00:00
2022-04-30 06:37:37 +00:00
// set default system preset
if ( ! hasLoadedSomething ) {
2022-05-01 22:26:56 +00:00
logD ( " setting default preset " ) ;
2022-09-30 05:26:54 +00:00
String preset = getConfString ( " initialSys2 " , " " ) ;
if ( preset . empty ( ) ) {
// try loading old preset
preset = decodeSysDesc ( getConfString ( " initialSys " , " " ) ) ;
}
2022-05-01 22:26:56 +00:00
logD ( " preset size %ld " , preset . size ( ) ) ;
2022-04-30 06:37:37 +00:00
if ( preset . size ( ) > 0 & & ( preset . size ( ) & 3 ) = = 0 ) {
2022-09-30 05:26:54 +00:00
initSongWithDesc ( preset . c_str ( ) ) ;
2022-04-30 06:37:37 +00:00
}
2022-07-23 22:02:03 +00:00
String sysName = getConfString ( " initialSysName " , " " ) ;
if ( sysName = = " " ) {
2022-07-27 07:36:48 +00:00
song . systemName = getSongSystemLegacyName ( song , ! getConfInt ( " noMultiSystem " , 0 ) ) ;
2022-07-23 22:02:03 +00:00
} else {
song . systemName = sysName ;
}
2022-04-30 06:37:37 +00:00
hasLoadedSomething = true ;
}
2021-12-19 08:16:24 +00:00
// init the rest of engine
2022-02-06 04:48:56 +00:00
bool haveAudio = false ;
if ( ! initAudioBackend ( ) ) {
2022-04-11 03:12:02 +00:00
logE ( " no audio output available! " ) ;
2022-02-06 04:48:56 +00:00
} else {
haveAudio = true ;
}
2021-05-11 20:08:08 +00:00
2022-01-08 21:03:32 +00:00
samp_bb = blip_new ( 32768 ) ;
if ( samp_bb = = NULL ) {
2022-04-11 03:12:02 +00:00
logE ( " not enough memory! " ) ;
2021-05-12 08:58:55 +00:00
return false ;
}
2022-02-06 04:48:56 +00:00
samp_bbOut = new short [ 32768 ] ;
2021-12-21 18:06:14 +00:00
2022-01-08 21:03:32 +00:00
samp_bbIn = new short [ 32768 ] ;
samp_bbInLen = 32768 ;
2021-12-21 18:06:14 +00:00
2022-01-08 21:03:32 +00:00
blip_set_rates ( samp_bb , 44100 , got . rate ) ;
2021-12-06 10:21:42 +00:00
2021-05-16 08:03:23 +00:00
for ( int i = 0 ; i < 64 ; i + + ) {
vibTable [ i ] = 127 * sin ( ( ( double ) i / 64.0 ) * ( 2 * M_PI ) ) ;
2021-05-14 19:16:48 +00:00
}
2022-03-25 07:52:41 +00:00
for ( int i = 0 ; i < 4096 ; i + + ) {
reversePitchTable [ i ] = round ( 1024.0 * pow ( 2.0 , ( 2048.0 - ( double ) i ) / ( 12.0 * 128.0 ) ) ) ;
pitchTable [ i ] = round ( 1024.0 * pow ( 2.0 , ( ( double ) i - 2048.0 ) / ( 12.0 * 128.0 ) ) ) ;
}
2021-05-14 19:16:48 +00:00
2022-01-08 06:57:37 +00:00
for ( int i = 0 ; i < DIV_MAX_CHANS ; i + + ) {
2021-12-18 08:25:42 +00:00
isMuted [ i ] = 0 ;
2022-02-10 08:15:39 +00:00
keyHit [ i ] = false ;
2021-12-18 08:25:42 +00:00
}
2022-01-27 22:49:00 +00:00
oscBuf [ 0 ] = new float [ 32768 ] ;
oscBuf [ 1 ] = new float [ 32768 ] ;
2022-05-31 02:40:07 +00:00
memset ( oscBuf [ 0 ] , 0 , 32768 * sizeof ( float ) ) ;
memset ( oscBuf [ 1 ] , 0 , 32768 * sizeof ( float ) ) ;
2021-12-13 19:40:03 +00:00
initDispatch ( ) ;
2022-05-01 17:57:44 +00:00
renderSamples ( ) ;
2021-12-11 21:51:34 +00:00
reset ( ) ;
2021-12-20 19:20:05 +00:00
active = true ;
2021-05-17 20:06:11 +00:00
2022-02-06 04:48:56 +00:00
if ( ! haveAudio ) {
2022-01-17 04:32:13 +00:00
return false ;
2022-02-06 04:48:56 +00:00
} else {
if ( ! output - > setRun ( true ) ) {
2022-04-11 03:12:02 +00:00
logE ( " error while activating! " ) ;
2022-02-06 04:48:56 +00:00
return false ;
}
2021-05-11 20:08:08 +00:00
}
return true ;
}
2021-12-13 22:09:46 +00:00
bool DivEngine : : quit ( ) {
2022-01-17 06:20:02 +00:00
deinitAudioBackend ( ) ;
2021-12-13 22:09:46 +00:00
quitDispatch ( ) ;
2022-04-11 03:12:02 +00:00
logI ( " saving config. " ) ;
2021-12-19 21:52:04 +00:00
saveConf ( ) ;
2021-12-20 19:20:05 +00:00
active = false ;
2022-01-27 22:49:00 +00:00
delete [ ] oscBuf [ 0 ] ;
delete [ ] oscBuf [ 1 ] ;
2022-05-13 03:15:03 +00:00
if ( yrw801ROM ! = NULL ) delete [ ] yrw801ROM ;
if ( tg100ROM ! = NULL ) delete [ ] tg100ROM ;
if ( mu5ROM ! = NULL ) delete [ ] mu5ROM ;
2022-05-15 06:42:49 +00:00
song . unload ( ) ;
2021-12-13 22:09:46 +00:00
return true ;
2021-12-14 17:33:26 +00:00
}