Merge branch 'master' of https://github.com/tildearrow/furnace into es5506_alt
This commit is contained in:
		
						commit
						46880634b4
					
				| 
						 | 
					@ -308,6 +308,7 @@ endif()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
set(ENGINE_SOURCES
 | 
					set(ENGINE_SOURCES
 | 
				
			||||||
src/log.cpp
 | 
					src/log.cpp
 | 
				
			||||||
 | 
					src/baseutils.cpp
 | 
				
			||||||
src/fileutils.cpp
 | 
					src/fileutils.cpp
 | 
				
			||||||
src/utfutils.cpp
 | 
					src/utfutils.cpp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -572,6 +573,7 @@ src/gui/guiConst.cpp
 | 
				
			||||||
src/gui/about.cpp
 | 
					src/gui/about.cpp
 | 
				
			||||||
src/gui/channels.cpp
 | 
					src/gui/channels.cpp
 | 
				
			||||||
src/gui/chanOsc.cpp
 | 
					src/gui/chanOsc.cpp
 | 
				
			||||||
 | 
					src/gui/clock.cpp
 | 
				
			||||||
src/gui/compatFlags.cpp
 | 
					src/gui/compatFlags.cpp
 | 
				
			||||||
src/gui/cursor.cpp
 | 
					src/gui/cursor.cpp
 | 
				
			||||||
src/gui/dataList.cpp
 | 
					src/gui/dataList.cpp
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
# Furnace Tracker
 | 
					# Furnace (chiptune tracker)
 | 
				
			||||||
 | 
					
 | 
				
			||||||

 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										
											BIN
										
									
								
								demos/SeeingRed.fur
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								demos/SeeingRed.fur
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								demos/splashingwater.fur
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								demos/splashingwater.fur
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								demos/watching_paint_dry.fur
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								demos/watching_paint_dry.fur
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								instruments/other/TIA Bass Drum.fui
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								instruments/other/TIA Bass Drum.fui
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								instruments/other/TIA Clap.fui
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								instruments/other/TIA Clap.fui
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								instruments/other/TIA Hi-Hat Long.fui
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								instruments/other/TIA Hi-Hat Long.fui
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								instruments/other/TIA Hi-Hat.fui
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								instruments/other/TIA Hi-Hat.fui
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								instruments/other/TIA Snare Drum.fui
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								instruments/other/TIA Snare Drum.fui
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
						 | 
					@ -3,7 +3,7 @@
 | 
				
			||||||
a backwards-compatible successor to the AY-3-8910, with increased volume resolution, duty cycle control, three envelopes and highly configurable noise generator.
 | 
					a backwards-compatible successor to the AY-3-8910, with increased volume resolution, duty cycle control, three envelopes and highly configurable noise generator.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
sadly, this soundchip has only ever observed minimal success, and has remained rather obscure since.
 | 
					sadly, this soundchip has only ever observed minimal success, and has remained rather obscure since.
 | 
				
			||||||
it is known for being used in the Covox Sound Master, which didn't sell well either.
 | 
					it is best known for being used in the Covox Sound Master, which didn't sell well either. It also observed very minimal success in Merit's CRT-250 machines, but only as a replacement for the AY-3-8910.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
emulation of this chip in Furnace is now complete thanks to community efforts and hardware testing, which an MSX board called Darky has permitted.
 | 
					emulation of this chip in Furnace is now complete thanks to community efforts and hardware testing, which an MSX board called Darky has permitted.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,8 +2,6 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
one of two chips that powered the Sega Genesis. It is a six-channel, four-operator FM synthesizer. Channel #6 can be turned into 8-bit PCM player.
 | 
					one of two chips that powered the Sega Genesis. It is a six-channel, four-operator FM synthesizer. Channel #6 can be turned into 8-bit PCM player.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
For 0.6pre1, Furnace can now support advanced YM2612 features that  [Fractal](https://gitlab.com/Natsumi/Fractal-Sound) sound driver adds: two software-mixed PCM channels (variable pitch, sample offsets, max 13.7 khz rate) and CSM - ch3 special mode feature that can be abused to produce rudimentary speech synthesis.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# effects
 | 
					# effects
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- `10xy`: set LFO parameters.
 | 
					- `10xy`: set LFO parameters.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -32,6 +32,8 @@ these fields are 0 in format versions prior to 100 (0.6pre1).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
the format versions are:
 | 
					the format versions are:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- 123: Furnace dev123
 | 
				
			||||||
 | 
					- 122: Furnace dev122
 | 
				
			||||||
- 121: Furnace dev121
 | 
					- 121: Furnace dev121
 | 
				
			||||||
- 120: Furnace dev120
 | 
					- 120: Furnace dev120
 | 
				
			||||||
- 119: Furnace dev119
 | 
					- 119: Furnace dev119
 | 
				
			||||||
| 
						 | 
					@ -1096,7 +1098,11 @@ size | description
 | 
				
			||||||
     | - 9: BRR (SNES)
 | 
					     | - 9: BRR (SNES)
 | 
				
			||||||
     | - 10: VOX
 | 
					     | - 10: VOX
 | 
				
			||||||
     | - 16: 16-bit PCM
 | 
					     | - 16: 16-bit PCM
 | 
				
			||||||
  3  | reserved
 | 
					  1  | loop direction (>=123) or reserved
 | 
				
			||||||
 | 
					     | - 0: forward
 | 
				
			||||||
 | 
					     | - 0: backward
 | 
				
			||||||
 | 
					     | - 0: ping-pong
 | 
				
			||||||
 | 
					  2  | reserved
 | 
				
			||||||
  4  | loop start
 | 
					  4  | loop start
 | 
				
			||||||
     | - -1 means no loop
 | 
					     | - -1 means no loop
 | 
				
			||||||
  4  | loop end
 | 
					  4  | loop end
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										89
									
								
								src/baseutils.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								src/baseutils.cpp
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,89 @@
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * 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.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "baseutils.h"
 | 
				
			||||||
 | 
					#include <string.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const char* base64Table="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					std::string taEncodeBase64(const std::string& data) {
 | 
				
			||||||
 | 
					  std::string ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ret.reserve((2+data.size()*4)/3);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  unsigned int groupOfThree=0;
 | 
				
			||||||
 | 
					  unsigned char pos=0;
 | 
				
			||||||
 | 
					  for (const char& i: data) {
 | 
				
			||||||
 | 
					    groupOfThree|=((unsigned char)i)<<((2-pos)<<3);
 | 
				
			||||||
 | 
					    if (++pos>=3) {
 | 
				
			||||||
 | 
					      pos=0;
 | 
				
			||||||
 | 
					      ret+=base64Table[(groupOfThree>>18)&63];
 | 
				
			||||||
 | 
					      ret+=base64Table[(groupOfThree>>12)&63];
 | 
				
			||||||
 | 
					      ret+=base64Table[(groupOfThree>>6)&63];
 | 
				
			||||||
 | 
					      ret+=base64Table[groupOfThree&63];
 | 
				
			||||||
 | 
					      groupOfThree=0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if (pos==2) {
 | 
				
			||||||
 | 
					    ret+=base64Table[(groupOfThree>>18)&63];
 | 
				
			||||||
 | 
					    ret+=base64Table[(groupOfThree>>12)&63];
 | 
				
			||||||
 | 
					    ret+=base64Table[(groupOfThree>>6)&63];
 | 
				
			||||||
 | 
					    ret+='=';
 | 
				
			||||||
 | 
					  } else if (pos==1) {
 | 
				
			||||||
 | 
					    ret+=base64Table[(groupOfThree>>18)&63];
 | 
				
			||||||
 | 
					    ret+=base64Table[(groupOfThree>>12)&63];
 | 
				
			||||||
 | 
					    ret+="==";
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  return ret;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					std::string taDecodeBase64(const char* buf) {
 | 
				
			||||||
 | 
					  std::string data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  unsigned int groupOfThree=0;
 | 
				
			||||||
 | 
					  signed char pos=18;
 | 
				
			||||||
 | 
					  for (const char* i=buf; *i; i++) {
 | 
				
			||||||
 | 
					    unsigned char nextVal=0;
 | 
				
			||||||
 | 
					    if ((*i)=='/') {
 | 
				
			||||||
 | 
					      nextVal=63;
 | 
				
			||||||
 | 
					    } else if ((*i)=='+') {
 | 
				
			||||||
 | 
					      nextVal=62;
 | 
				
			||||||
 | 
					    } else if ((*i)>='0' && (*i)<='9') {
 | 
				
			||||||
 | 
					      nextVal=52+((*i)-'0');
 | 
				
			||||||
 | 
					    } else if ((*i)>='a' && (*i)<='z') {
 | 
				
			||||||
 | 
					      nextVal=26+((*i)-'a');
 | 
				
			||||||
 | 
					    } else if ((*i)>='A' && (*i)<='Z') {
 | 
				
			||||||
 | 
					      nextVal=((*i)-'A');
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      nextVal=0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    groupOfThree|=nextVal<<pos;
 | 
				
			||||||
 | 
					    pos-=6;
 | 
				
			||||||
 | 
					    if (pos<0) {
 | 
				
			||||||
 | 
					      pos=18;
 | 
				
			||||||
 | 
					      if ((groupOfThree>>16)&0xff) data+=(groupOfThree>>16)&0xff;
 | 
				
			||||||
 | 
					      if ((groupOfThree>>8)&0xff) data+=(groupOfThree>>8)&0xff;
 | 
				
			||||||
 | 
					      if (groupOfThree&0xff) data+=groupOfThree&0xff;
 | 
				
			||||||
 | 
					      groupOfThree=0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return data;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										28
									
								
								src/baseutils.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								src/baseutils.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,28 @@
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * 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.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef _BASEUTILS_H
 | 
				
			||||||
 | 
					#define _BASEUTILS_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <string>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					std::string taEncodeBase64(const std::string& data);
 | 
				
			||||||
 | 
					std::string taDecodeBase64(const char* str);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
| 
						 | 
					@ -19,6 +19,7 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "config.h"
 | 
					#include "config.h"
 | 
				
			||||||
#include "../ta-log.h"
 | 
					#include "../ta-log.h"
 | 
				
			||||||
 | 
					#include "../baseutils.h"
 | 
				
			||||||
#include "../fileutils.h"
 | 
					#include "../fileutils.h"
 | 
				
			||||||
#include <fmt/printf.h>
 | 
					#include <fmt/printf.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -48,41 +49,9 @@ String DivConfig::toString() {
 | 
				
			||||||
  return ret;
 | 
					  return ret;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const char* base64Table="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
String DivConfig::toBase64() {
 | 
					String DivConfig::toBase64() {
 | 
				
			||||||
  String data=toString();
 | 
					  String data=toString();
 | 
				
			||||||
  String ret;
 | 
					  return taEncodeBase64(data);
 | 
				
			||||||
 | 
					 | 
				
			||||||
  ret.reserve((2+data.size()*4)/3);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  unsigned int groupOfThree=0;
 | 
					 | 
				
			||||||
  unsigned char pos=0;
 | 
					 | 
				
			||||||
  for (char& i: data) {
 | 
					 | 
				
			||||||
    groupOfThree|=((unsigned char)i)<<((2-pos)<<3);
 | 
					 | 
				
			||||||
    if (++pos>=3) {
 | 
					 | 
				
			||||||
      pos=0;
 | 
					 | 
				
			||||||
      ret+=base64Table[(groupOfThree>>18)&63];
 | 
					 | 
				
			||||||
      ret+=base64Table[(groupOfThree>>12)&63];
 | 
					 | 
				
			||||||
      ret+=base64Table[(groupOfThree>>6)&63];
 | 
					 | 
				
			||||||
      ret+=base64Table[groupOfThree&63];
 | 
					 | 
				
			||||||
      groupOfThree=0;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  if (pos==2) {
 | 
					 | 
				
			||||||
    ret+=base64Table[(groupOfThree>>18)&63];
 | 
					 | 
				
			||||||
    ret+=base64Table[(groupOfThree>>12)&63];
 | 
					 | 
				
			||||||
    ret+=base64Table[(groupOfThree>>6)&63];
 | 
					 | 
				
			||||||
    ret+='=';
 | 
					 | 
				
			||||||
  } else if (pos==1) {
 | 
					 | 
				
			||||||
    ret+=base64Table[(groupOfThree>>18)&63];
 | 
					 | 
				
			||||||
    ret+=base64Table[(groupOfThree>>12)&63];
 | 
					 | 
				
			||||||
    ret+="==";
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  
 | 
					 | 
				
			||||||
  logV("toBase64: %s",ret);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  return ret;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void DivConfig::parseLine(const char* line) {
 | 
					void DivConfig::parseLine(const char* line) {
 | 
				
			||||||
| 
						 | 
					@ -143,38 +112,7 @@ bool DivConfig::loadFromMemory(const char* buf) {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool DivConfig::loadFromBase64(const char* buf) {
 | 
					bool DivConfig::loadFromBase64(const char* buf) {
 | 
				
			||||||
  String data;
 | 
					  String data=taDecodeBase64(buf);
 | 
				
			||||||
 | 
					 | 
				
			||||||
  unsigned int groupOfThree=0;
 | 
					 | 
				
			||||||
  signed char pos=18;
 | 
					 | 
				
			||||||
  for (const char* i=buf; *i; i++) {
 | 
					 | 
				
			||||||
    unsigned char nextVal=0;
 | 
					 | 
				
			||||||
    if ((*i)=='/') {
 | 
					 | 
				
			||||||
      nextVal=63;
 | 
					 | 
				
			||||||
    } else if ((*i)=='+') {
 | 
					 | 
				
			||||||
      nextVal=62;
 | 
					 | 
				
			||||||
    } else if ((*i)>='0' && (*i)<='9') {
 | 
					 | 
				
			||||||
      nextVal=52+((*i)-'0');
 | 
					 | 
				
			||||||
    } else if ((*i)>='a' && (*i)<='z') {
 | 
					 | 
				
			||||||
      nextVal=26+((*i)-'a');
 | 
					 | 
				
			||||||
    } else if ((*i)>='A' && (*i)<='Z') {
 | 
					 | 
				
			||||||
      nextVal=((*i)-'A');
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
      nextVal=0;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    groupOfThree|=nextVal<<pos;
 | 
					 | 
				
			||||||
    pos-=6;
 | 
					 | 
				
			||||||
    if (pos<0) {
 | 
					 | 
				
			||||||
      pos=18;
 | 
					 | 
				
			||||||
      if ((groupOfThree>>16)&0xff) data+=(groupOfThree>>16)&0xff;
 | 
					 | 
				
			||||||
      if ((groupOfThree>>8)&0xff) data+=(groupOfThree>>8)&0xff;
 | 
					 | 
				
			||||||
      if (groupOfThree&0xff) data+=groupOfThree&0xff;
 | 
					 | 
				
			||||||
      groupOfThree=0;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  logV("fromBase64: %s",data);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  return loadFromMemory(data.c_str());
 | 
					  return loadFromMemory(data.c_str());
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1390,10 +1390,14 @@ String DivEngine::decodeSysDesc(String desc) {
 | 
				
			||||||
  return newDesc.toBase64();
 | 
					  return newDesc.toBase64();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void DivEngine::initSongWithDesc(const char* description) {
 | 
					void DivEngine::initSongWithDesc(const char* description, bool inBase64) {
 | 
				
			||||||
  int chanCount=0;
 | 
					  int chanCount=0;
 | 
				
			||||||
  DivConfig c;
 | 
					  DivConfig c;
 | 
				
			||||||
  c.loadFromBase64(description);
 | 
					  if (inBase64) {
 | 
				
			||||||
 | 
					    c.loadFromBase64(description);
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    c.loadFromMemory(description);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
  int index=0;
 | 
					  int index=0;
 | 
				
			||||||
  for (; index<32; index++) {
 | 
					  for (; index<32; index++) {
 | 
				
			||||||
    song.system[index]=systemFromFileFur(c.getInt(fmt::sprintf("id%d",index),0));
 | 
					    song.system[index]=systemFromFileFur(c.getInt(fmt::sprintf("id%d",index),0));
 | 
				
			||||||
| 
						 | 
					@ -1414,7 +1418,7 @@ void DivEngine::initSongWithDesc(const char* description) {
 | 
				
			||||||
  song.systemLen=index;
 | 
					  song.systemLen=index;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void DivEngine::createNew(const char* description, String sysName) {
 | 
					void DivEngine::createNew(const char* description, String sysName, bool inBase64) {
 | 
				
			||||||
  quitDispatch();
 | 
					  quitDispatch();
 | 
				
			||||||
  BUSY_BEGIN;
 | 
					  BUSY_BEGIN;
 | 
				
			||||||
  saveLock.lock();
 | 
					  saveLock.lock();
 | 
				
			||||||
| 
						 | 
					@ -1422,7 +1426,7 @@ void DivEngine::createNew(const char* description, String sysName) {
 | 
				
			||||||
  song=DivSong();
 | 
					  song=DivSong();
 | 
				
			||||||
  changeSong(0);
 | 
					  changeSong(0);
 | 
				
			||||||
  if (description!=NULL) {
 | 
					  if (description!=NULL) {
 | 
				
			||||||
    initSongWithDesc(description);
 | 
					    initSongWithDesc(description,inBase64);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  if (sysName=="") {
 | 
					  if (sysName=="") {
 | 
				
			||||||
    song.systemName=getSongSystemLegacyName(song,!getConfInt("noMultiSystem",0));
 | 
					    song.systemName=getSongSystemLegacyName(song,!getConfInt("noMultiSystem",0));
 | 
				
			||||||
| 
						 | 
					@ -2358,6 +2362,9 @@ void DivEngine::reset() {
 | 
				
			||||||
  firstTick=false;
 | 
					  firstTick=false;
 | 
				
			||||||
  shallStop=false;
 | 
					  shallStop=false;
 | 
				
			||||||
  shallStopSched=false;
 | 
					  shallStopSched=false;
 | 
				
			||||||
 | 
					  pendingMetroTick=0;
 | 
				
			||||||
 | 
					  elapsedBars=0;
 | 
				
			||||||
 | 
					  elapsedBeats=0;
 | 
				
			||||||
  nextSpeed=speed1;
 | 
					  nextSpeed=speed1;
 | 
				
			||||||
  divider=60;
 | 
					  divider=60;
 | 
				
			||||||
  if (curSubSong->customTempo) {
 | 
					  if (curSubSong->customTempo) {
 | 
				
			||||||
| 
						 | 
					@ -2548,6 +2555,14 @@ int DivEngine::getRow() {
 | 
				
			||||||
  return prevRow;
 | 
					  return prevRow;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int DivEngine::getElapsedBars() {
 | 
				
			||||||
 | 
					  return elapsedBars;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int DivEngine::getElapsedBeats() {
 | 
				
			||||||
 | 
					  return elapsedBeats;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
size_t DivEngine::getCurrentSubSong() {
 | 
					size_t DivEngine::getCurrentSubSong() {
 | 
				
			||||||
  return curSubSongIndex;
 | 
					  return curSubSongIndex;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -2673,12 +2688,17 @@ void DivEngine::unmuteAll() {
 | 
				
			||||||
  BUSY_END;
 | 
					  BUSY_END;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int DivEngine::addInstrument(int refChan) {
 | 
					int DivEngine::addInstrument(int refChan, DivInstrumentType fallbackType) {
 | 
				
			||||||
  if (song.ins.size()>=256) return -1;
 | 
					  if (song.ins.size()>=256) return -1;
 | 
				
			||||||
  BUSY_BEGIN;
 | 
					  BUSY_BEGIN;
 | 
				
			||||||
  DivInstrument* ins=new DivInstrument;
 | 
					  DivInstrument* ins=new DivInstrument;
 | 
				
			||||||
  int insCount=(int)song.ins.size();
 | 
					  int insCount=(int)song.ins.size();
 | 
				
			||||||
  DivInstrumentType prefType=getPreferInsType(refChan);
 | 
					  DivInstrumentType prefType;
 | 
				
			||||||
 | 
					  if (refChan<0) {
 | 
				
			||||||
 | 
					    prefType=fallbackType;
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    prefType=getPreferInsType(refChan);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
  switch (prefType) {
 | 
					  switch (prefType) {
 | 
				
			||||||
    case DIV_INS_OPLL:
 | 
					    case DIV_INS_OPLL:
 | 
				
			||||||
      *ins=song.nullInsOPLL;
 | 
					      *ins=song.nullInsOPLL;
 | 
				
			||||||
| 
						 | 
					@ -2692,8 +2712,10 @@ int DivEngine::addInstrument(int refChan) {
 | 
				
			||||||
    default:
 | 
					    default:
 | 
				
			||||||
      break;
 | 
					      break;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  if (sysOfChan[refChan]==DIV_SYSTEM_QSOUND) {
 | 
					  if (refChan>=0) {
 | 
				
			||||||
    *ins=song.nullInsQSound;
 | 
					    if (sysOfChan[refChan]==DIV_SYSTEM_QSOUND) {
 | 
				
			||||||
 | 
					      *ins=song.nullInsQSound;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  ins->name=fmt::sprintf("Instrument %d",insCount);
 | 
					  ins->name=fmt::sprintf("Instrument %d",insCount);
 | 
				
			||||||
  if (prefType!=DIV_INS_NULL) {
 | 
					  if (prefType!=DIV_INS_NULL) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -47,8 +47,8 @@
 | 
				
			||||||
#define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock();
 | 
					#define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock();
 | 
				
			||||||
#define BUSY_END isBusy.unlock(); softLocked=false;
 | 
					#define BUSY_END isBusy.unlock(); softLocked=false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define DIV_VERSION "dev122"
 | 
					#define DIV_VERSION "dev125"
 | 
				
			||||||
#define DIV_ENGINE_VERSION 122
 | 
					#define DIV_ENGINE_VERSION 125
 | 
				
			||||||
// for imports
 | 
					// for imports
 | 
				
			||||||
#define DIV_VERSION_MOD 0xff01
 | 
					#define DIV_VERSION_MOD 0xff01
 | 
				
			||||||
#define DIV_VERSION_FC 0xff02
 | 
					#define DIV_VERSION_FC 0xff02
 | 
				
			||||||
| 
						 | 
					@ -351,14 +351,14 @@ class DivEngine {
 | 
				
			||||||
  bool midiOutClock;
 | 
					  bool midiOutClock;
 | 
				
			||||||
  int midiOutMode;
 | 
					  int midiOutMode;
 | 
				
			||||||
  int softLockCount;
 | 
					  int softLockCount;
 | 
				
			||||||
  int subticks, ticks, curRow, curOrder, prevRow, prevOrder, remainingLoops, totalLoops, lastLoopPos, exportLoopCount, nextSpeed;
 | 
					  int subticks, ticks, curRow, curOrder, prevRow, prevOrder, remainingLoops, totalLoops, lastLoopPos, exportLoopCount, nextSpeed, elapsedBars, elapsedBeats;
 | 
				
			||||||
  size_t curSubSongIndex;
 | 
					  size_t curSubSongIndex;
 | 
				
			||||||
  double divider;
 | 
					  double divider;
 | 
				
			||||||
  int cycles;
 | 
					  int cycles;
 | 
				
			||||||
  double clockDrift;
 | 
					  double clockDrift;
 | 
				
			||||||
  int stepPlay;
 | 
					  int stepPlay;
 | 
				
			||||||
  int changeOrd, changePos, totalSeconds, totalTicks, totalTicksR, totalCmds, lastCmds, cmdsPerSecond, globalPitch;
 | 
					  int changeOrd, changePos, totalSeconds, totalTicks, totalTicksR, totalCmds, lastCmds, cmdsPerSecond, globalPitch;
 | 
				
			||||||
  unsigned char extValue;
 | 
					  unsigned char extValue, pendingMetroTick;
 | 
				
			||||||
  unsigned char speed1, speed2;
 | 
					  unsigned char speed1, speed2;
 | 
				
			||||||
  short tempoAccum;
 | 
					  short tempoAccum;
 | 
				
			||||||
  DivStatusView view;
 | 
					  DivStatusView view;
 | 
				
			||||||
| 
						 | 
					@ -382,9 +382,9 @@ class DivEngine {
 | 
				
			||||||
  std::vector<String> midiOuts;
 | 
					  std::vector<String> midiOuts;
 | 
				
			||||||
  std::vector<DivCommand> cmdStream;
 | 
					  std::vector<DivCommand> cmdStream;
 | 
				
			||||||
  std::vector<DivInstrumentType> possibleInsTypes;
 | 
					  std::vector<DivInstrumentType> possibleInsTypes;
 | 
				
			||||||
  DivSysDef* sysDefs[256];
 | 
					  static DivSysDef* sysDefs[256];
 | 
				
			||||||
  DivSystem sysFileMapFur[256];
 | 
					  static DivSystem sysFileMapFur[256];
 | 
				
			||||||
  DivSystem sysFileMapDMF[256];
 | 
					  static DivSystem sysFileMapDMF[256];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  struct SamplePreview {
 | 
					  struct SamplePreview {
 | 
				
			||||||
    double rate;
 | 
					    double rate;
 | 
				
			||||||
| 
						 | 
					@ -440,8 +440,6 @@ class DivEngine {
 | 
				
			||||||
  void reset();
 | 
					  void reset();
 | 
				
			||||||
  void playSub(bool preserveDrift, int goalRow=0);
 | 
					  void playSub(bool preserveDrift, int goalRow=0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  void convertOldFlags(unsigned int oldFlags, DivConfig& newFlags, DivSystem sys);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  bool loadDMF(unsigned char* file, size_t len);
 | 
					  bool loadDMF(unsigned char* file, size_t len);
 | 
				
			||||||
  bool loadFur(unsigned char* file, size_t len);
 | 
					  bool loadFur(unsigned char* file, size_t len);
 | 
				
			||||||
  bool loadMod(unsigned char* file, size_t len);
 | 
					  bool loadMod(unsigned char* file, size_t len);
 | 
				
			||||||
| 
						 | 
					@ -469,7 +467,7 @@ class DivEngine {
 | 
				
			||||||
  bool deinitAudioBackend(bool dueToSwitchMaster=false);
 | 
					  bool deinitAudioBackend(bool dueToSwitchMaster=false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  void registerSystems();
 | 
					  void registerSystems();
 | 
				
			||||||
  void initSongWithDesc(const char* description);
 | 
					  void initSongWithDesc(const char* description, bool inBase64=true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  void exchangeIns(int one, int two);
 | 
					  void exchangeIns(int one, int two);
 | 
				
			||||||
  void swapChannels(int src, int dest);
 | 
					  void swapChannels(int src, int dest);
 | 
				
			||||||
| 
						 | 
					@ -503,7 +501,7 @@ class DivEngine {
 | 
				
			||||||
    // parse old system setup description
 | 
					    // parse old system setup description
 | 
				
			||||||
    String decodeSysDesc(String desc);
 | 
					    String decodeSysDesc(String desc);
 | 
				
			||||||
    // start fresh
 | 
					    // start fresh
 | 
				
			||||||
    void createNew(const char* description, String sysName);
 | 
					    void createNew(const char* description, String sysName, bool inBase64=true);
 | 
				
			||||||
    // load a file.
 | 
					    // load a file.
 | 
				
			||||||
    bool load(unsigned char* f, size_t length);
 | 
					    bool load(unsigned char* f, size_t length);
 | 
				
			||||||
    // save as .dmf.
 | 
					    // save as .dmf.
 | 
				
			||||||
| 
						 | 
					@ -532,10 +530,14 @@ class DivEngine {
 | 
				
			||||||
    void notifyWaveChange(int wave);
 | 
					    void notifyWaveChange(int wave);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // get system IDs
 | 
					    // get system IDs
 | 
				
			||||||
    DivSystem systemFromFileFur(unsigned char val);
 | 
					    static DivSystem systemFromFileFur(unsigned char val);
 | 
				
			||||||
    unsigned char systemToFileFur(DivSystem val);
 | 
					    static unsigned char systemToFileFur(DivSystem val);
 | 
				
			||||||
    DivSystem systemFromFileDMF(unsigned char val);
 | 
					    static DivSystem systemFromFileDMF(unsigned char val);
 | 
				
			||||||
    unsigned char systemToFileDMF(DivSystem val);
 | 
					    static unsigned char systemToFileDMF(DivSystem val);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // convert old flags
 | 
				
			||||||
 | 
					    static void convertOldFlags(unsigned int oldFlags, DivConfig& newFlags, DivSystem sys);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // benchmark (returns time in seconds)
 | 
					    // benchmark (returns time in seconds)
 | 
				
			||||||
    double benchmarkPlayback();
 | 
					    double benchmarkPlayback();
 | 
				
			||||||
| 
						 | 
					@ -709,6 +711,10 @@ class DivEngine {
 | 
				
			||||||
    // get current row
 | 
					    // get current row
 | 
				
			||||||
    int getRow();
 | 
					    int getRow();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // get beat/bar
 | 
				
			||||||
 | 
					    int getElapsedBars();
 | 
				
			||||||
 | 
					    int getElapsedBeats();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // get current subsong
 | 
					    // get current subsong
 | 
				
			||||||
    size_t getCurrentSubSong();
 | 
					    size_t getCurrentSubSong();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -753,7 +759,7 @@ class DivEngine {
 | 
				
			||||||
    bool isExporting();
 | 
					    bool isExporting();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // add instrument
 | 
					    // add instrument
 | 
				
			||||||
    int addInstrument(int refChan=0);
 | 
					    int addInstrument(int refChan=0, DivInstrumentType fallbackType=DIV_INS_STD);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // add instrument from pointer
 | 
					    // add instrument from pointer
 | 
				
			||||||
    int addInstrumentPtr(DivInstrument* which);
 | 
					    int addInstrumentPtr(DivInstrument* which);
 | 
				
			||||||
| 
						 | 
					@ -1058,6 +1064,8 @@ class DivEngine {
 | 
				
			||||||
      lastLoopPos(0),
 | 
					      lastLoopPos(0),
 | 
				
			||||||
      exportLoopCount(0),
 | 
					      exportLoopCount(0),
 | 
				
			||||||
      nextSpeed(3),
 | 
					      nextSpeed(3),
 | 
				
			||||||
 | 
					      elapsedBars(0),
 | 
				
			||||||
 | 
					      elapsedBeats(0),
 | 
				
			||||||
      curSubSongIndex(0),
 | 
					      curSubSongIndex(0),
 | 
				
			||||||
      divider(60),
 | 
					      divider(60),
 | 
				
			||||||
      cycles(0),
 | 
					      cycles(0),
 | 
				
			||||||
| 
						 | 
					@ -1073,6 +1081,7 @@ class DivEngine {
 | 
				
			||||||
      cmdsPerSecond(0),
 | 
					      cmdsPerSecond(0),
 | 
				
			||||||
      globalPitch(0),
 | 
					      globalPitch(0),
 | 
				
			||||||
      extValue(0),
 | 
					      extValue(0),
 | 
				
			||||||
 | 
					      pendingMetroTick(0),
 | 
				
			||||||
      speed1(3),
 | 
					      speed1(3),
 | 
				
			||||||
      speed2(3),
 | 
					      speed2(3),
 | 
				
			||||||
      tempoAccum(0),
 | 
					      tempoAccum(0),
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2383,11 +2383,16 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
 | 
				
			||||||
      if (isNewSample) {
 | 
					      if (isNewSample) {
 | 
				
			||||||
        sample->centerRate=reader.readI();
 | 
					        sample->centerRate=reader.readI();
 | 
				
			||||||
        sample->depth=(DivSampleDepth)reader.readC();
 | 
					        sample->depth=(DivSampleDepth)reader.readC();
 | 
				
			||||||
 | 
					        if (ds.version>=123) {
 | 
				
			||||||
 | 
					          sample->loopMode=(DivSampleLoopMode)reader.readC();
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					          sample->loopMode=DIV_SAMPLE_LOOP_FORWARD;
 | 
				
			||||||
 | 
					          reader.readC();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // reserved
 | 
					        // reserved
 | 
				
			||||||
        reader.readC();
 | 
					        reader.readC();
 | 
				
			||||||
        reader.readC();
 | 
					        reader.readC();
 | 
				
			||||||
        reader.readC();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        sample->loopStart=reader.readI();
 | 
					        sample->loopStart=reader.readI();
 | 
				
			||||||
        sample->loopEnd=reader.readI();
 | 
					        sample->loopEnd=reader.readI();
 | 
				
			||||||
| 
						 | 
					@ -2611,6 +2616,21 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // ExtCh compat flag
 | 
				
			||||||
 | 
					    if (ds.version<125) {
 | 
				
			||||||
 | 
					      for (int i=0; i<ds.systemLen; i++) {
 | 
				
			||||||
 | 
					        if (ds.system[i]==DIV_SYSTEM_YM2612_EXT ||
 | 
				
			||||||
 | 
					            ds.system[i]==DIV_SYSTEM_YM2612_FRAC_EXT ||
 | 
				
			||||||
 | 
					            ds.system[i]==DIV_SYSTEM_YM2610_EXT ||
 | 
				
			||||||
 | 
					            ds.system[i]==DIV_SYSTEM_YM2610_FULL_EXT ||
 | 
				
			||||||
 | 
					            ds.system[i]==DIV_SYSTEM_YM2610B_EXT ||
 | 
				
			||||||
 | 
					            ds.system[i]==DIV_SYSTEM_OPN_EXT ||
 | 
				
			||||||
 | 
					            ds.system[i]==DIV_SYSTEM_PC98_EXT) {
 | 
				
			||||||
 | 
					          ds.systemFlags[i].set("noExtMacros",true);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (active) quitDispatch();
 | 
					    if (active) quitDispatch();
 | 
				
			||||||
    BUSY_BEGIN_SOFT;
 | 
					    BUSY_BEGIN_SOFT;
 | 
				
			||||||
    saveLock.lock();
 | 
					    saveLock.lock();
 | 
				
			||||||
| 
						 | 
					@ -2946,7 +2966,7 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) {
 | 
				
			||||||
              writeFxCol(fxTyp,fxVal);
 | 
					              writeFxCol(fxTyp,fxVal);
 | 
				
			||||||
              break;
 | 
					              break;
 | 
				
			||||||
            case 12: // set vol
 | 
					            case 12: // set vol
 | 
				
			||||||
              data[row][3]=fxVal;
 | 
					              data[row][3]=MIN(0x40,fxVal);
 | 
				
			||||||
              break;
 | 
					              break;
 | 
				
			||||||
            case 13: // break to row (BCD)
 | 
					            case 13: // break to row (BCD)
 | 
				
			||||||
              writeFxCol(fxTyp,((fxVal>>4)*10)+(fxVal&15));
 | 
					              writeFxCol(fxTyp,((fxVal>>4)*10)+(fxVal&15));
 | 
				
			||||||
| 
						 | 
					@ -4624,9 +4644,9 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
 | 
				
			||||||
    w->writeI(sample->rate);
 | 
					    w->writeI(sample->rate);
 | 
				
			||||||
    w->writeI(sample->centerRate);
 | 
					    w->writeI(sample->centerRate);
 | 
				
			||||||
    w->writeC(sample->depth);
 | 
					    w->writeC(sample->depth);
 | 
				
			||||||
 | 
					    w->writeC(sample->loopMode);
 | 
				
			||||||
    w->writeC(0); // reserved
 | 
					    w->writeC(0); // reserved
 | 
				
			||||||
    w->writeC(0);
 | 
					    w->writeC(0);
 | 
				
			||||||
    w->writeC(0);
 | 
					 | 
				
			||||||
    w->writeI(sample->loop?sample->loopStart:-1);
 | 
					    w->writeI(sample->loop?sample->loopStart:-1);
 | 
				
			||||||
    w->writeI(sample->loop?sample->loopEnd:-1);
 | 
					    w->writeI(sample->loop?sample->loopEnd:-1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -285,7 +285,7 @@ void DivPlatformGenesis::tick(bool sysTick) {
 | 
				
			||||||
        chan[i].freqChanged=true;
 | 
					        chan[i].freqChanged=true;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
       if (chan[i].std.arp.had) {
 | 
					      if (chan[i].std.arp.had) {
 | 
				
			||||||
        if (!chan[i].inPorta) {
 | 
					        if (!chan[i].inPorta) {
 | 
				
			||||||
          chan[i].baseFreq=NOTE_FNUM_BLOCK(parent->calcArp(chan[i].note,chan[i].std.arp.val),11);
 | 
					          chan[i].baseFreq=NOTE_FNUM_BLOCK(parent->calcArp(chan[i].note,chan[i].std.arp.val),11);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
| 
						 | 
					@ -1224,6 +1224,7 @@ void DivPlatformGenesis::setFlags(const DivConfig& flags) {
 | 
				
			||||||
      break;
 | 
					      break;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  ladder=flags.getBool("ladderEffect",false);
 | 
					  ladder=flags.getBool("ladderEffect",false);
 | 
				
			||||||
 | 
					  noExtMacros=flags.getBool("noExtMacros",false);
 | 
				
			||||||
  OPN2_SetChipType(ladder?ym3438_mode_ym2612:0);
 | 
					  OPN2_SetChipType(ladder?ym3438_mode_ym2612:0);
 | 
				
			||||||
  if (useYMFM) {
 | 
					  if (useYMFM) {
 | 
				
			||||||
    if (fm_ymfm!=NULL) delete fm_ymfm;
 | 
					    if (fm_ymfm!=NULL) delete fm_ymfm;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -116,7 +116,7 @@ class DivPlatformGenesis: public DivPlatformOPN {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    int softPCMTimer;
 | 
					    int softPCMTimer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    bool extMode, softPCM, useYMFM;
 | 
					    bool extMode, softPCM, noExtMacros, useYMFM;
 | 
				
			||||||
    bool ladder;
 | 
					    bool ladder;
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
    unsigned char dacVolTable[128];
 | 
					    unsigned char dacVolTable[128];
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -52,6 +52,15 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) {
 | 
				
			||||||
        chan[2].state.ams=ins->fm.ams;
 | 
					        chan[2].state.ams=ins->fm.ams;
 | 
				
			||||||
        chan[2].state.op[ordch]=ins->fm.op[ordch];
 | 
					        chan[2].state.op[ordch]=ins->fm.op[ordch];
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (noExtMacros) {
 | 
				
			||||||
 | 
					        opChan[ch].macroInit(NULL);
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        opChan[ch].macroInit(ins);       
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      if (!opChan[ch].std.vol.will) {
 | 
				
			||||||
 | 
					        opChan[ch].outVol=opChan[ch].vol;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
      
 | 
					      
 | 
				
			||||||
      unsigned short baseAddr=chanOffs[2]|opOffs[ordch];
 | 
					      unsigned short baseAddr=chanOffs[2]|opOffs[ordch];
 | 
				
			||||||
      DivInstrumentFM::Operator& op=chan[2].state.op[ordch];
 | 
					      DivInstrumentFM::Operator& op=chan[2].state.op[ordch];
 | 
				
			||||||
| 
						 | 
					@ -60,7 +69,7 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) {
 | 
				
			||||||
        rWrite(baseAddr+0x40,127);
 | 
					        rWrite(baseAddr+0x40,127);
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        if (opChan[ch].insChanged) {
 | 
					        if (opChan[ch].insChanged) {
 | 
				
			||||||
          rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch].vol&0x7f,127));
 | 
					          rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch].outVol&0x7f,127));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      if (opChan[ch].insChanged) {
 | 
					      if (opChan[ch].insChanged) {
 | 
				
			||||||
| 
						 | 
					@ -81,6 +90,7 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) {
 | 
				
			||||||
      if (c.value!=DIV_NOTE_NULL) {
 | 
					      if (c.value!=DIV_NOTE_NULL) {
 | 
				
			||||||
        opChan[ch].baseFreq=NOTE_FNUM_BLOCK(c.value,11);
 | 
					        opChan[ch].baseFreq=NOTE_FNUM_BLOCK(c.value,11);
 | 
				
			||||||
        opChan[ch].portaPause=false;
 | 
					        opChan[ch].portaPause=false;
 | 
				
			||||||
 | 
					        opChan[ch].note=c.value;
 | 
				
			||||||
        opChan[ch].freqChanged=true;
 | 
					        opChan[ch].freqChanged=true;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      opChan[ch].keyOn=true;
 | 
					      opChan[ch].keyOn=true;
 | 
				
			||||||
| 
						 | 
					@ -92,14 +102,28 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) {
 | 
				
			||||||
      opChan[ch].keyOn=false;
 | 
					      opChan[ch].keyOn=false;
 | 
				
			||||||
      opChan[ch].active=false;
 | 
					      opChan[ch].active=false;
 | 
				
			||||||
      break;
 | 
					      break;
 | 
				
			||||||
 | 
					    case DIV_CMD_NOTE_OFF_ENV:
 | 
				
			||||||
 | 
					      if (noExtMacros) break;
 | 
				
			||||||
 | 
					      opChan[ch].keyOff=true;
 | 
				
			||||||
 | 
					      opChan[ch].keyOn=false;
 | 
				
			||||||
 | 
					      opChan[ch].active=false;
 | 
				
			||||||
 | 
					      opChan[ch].std.release();
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
 | 
					    case DIV_CMD_ENV_RELEASE:
 | 
				
			||||||
 | 
					      if (noExtMacros) break;
 | 
				
			||||||
 | 
					      opChan[ch].std.release();
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
    case DIV_CMD_VOLUME: {
 | 
					    case DIV_CMD_VOLUME: {
 | 
				
			||||||
      opChan[ch].vol=c.value;
 | 
					      opChan[ch].vol=c.value;
 | 
				
			||||||
 | 
					      if (!opChan[ch].std.vol.has) {
 | 
				
			||||||
 | 
					        opChan[ch].outVol=c.value;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
      unsigned short baseAddr=chanOffs[2]|opOffs[ordch];
 | 
					      unsigned short baseAddr=chanOffs[2]|opOffs[ordch];
 | 
				
			||||||
      DivInstrumentFM::Operator& op=chan[2].state.op[ordch];
 | 
					      DivInstrumentFM::Operator& op=chan[2].state.op[ordch];
 | 
				
			||||||
      if (isOpMuted[ch]) {
 | 
					      if (isOpMuted[ch]) {
 | 
				
			||||||
        rWrite(baseAddr+0x40,127);
 | 
					        rWrite(baseAddr+0x40,127);
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch].vol&0x7f,127));
 | 
					        rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch].outVol&0x7f,127));
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      break;
 | 
					      break;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					@ -210,7 +234,7 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) {
 | 
				
			||||||
      if (isOpMuted[ch]) {
 | 
					      if (isOpMuted[ch]) {
 | 
				
			||||||
        rWrite(baseAddr+0x40,127);
 | 
					        rWrite(baseAddr+0x40,127);
 | 
				
			||||||
      } else if (KVS(2,c.value)) {
 | 
					      } else if (KVS(2,c.value)) {
 | 
				
			||||||
        rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch].vol&0x7f,127));
 | 
					        rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch].outVol&0x7f,127));
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        rWrite(baseAddr+0x40,op.tl);
 | 
					        rWrite(baseAddr+0x40,op.tl);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
| 
						 | 
					@ -393,8 +417,8 @@ void DivPlatformGenesisExt::muteChannel(int ch, bool mute) {
 | 
				
			||||||
    rWrite(baseAddr+0x40,127);
 | 
					    rWrite(baseAddr+0x40,127);
 | 
				
			||||||
    immWrite(baseAddr+0x40,127);
 | 
					    immWrite(baseAddr+0x40,127);
 | 
				
			||||||
  } else if (KVS(2,ordch)) {
 | 
					  } else if (KVS(2,ordch)) {
 | 
				
			||||||
    rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-2].vol&0x7f,127));
 | 
					    rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-2].outVol&0x7f,127));
 | 
				
			||||||
    immWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-2].vol&0x7f,127));
 | 
					    immWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-2].outVol&0x7f,127));
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
    rWrite(baseAddr+0x40,op.tl);
 | 
					    rWrite(baseAddr+0x40,op.tl);
 | 
				
			||||||
    immWrite(baseAddr+0x40,op.tl);
 | 
					    immWrite(baseAddr+0x40,op.tl);
 | 
				
			||||||
| 
						 | 
					@ -438,6 +462,91 @@ void DivPlatformGenesisExt::tick(bool sysTick) {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (extMode && !noExtMacros) for (int i=0; i<4; i++) {
 | 
				
			||||||
 | 
					    opChan[i].std.next();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (opChan[i].std.vol.had) {
 | 
				
			||||||
 | 
					      opChan[i].outVol=VOL_SCALE_LOG_BROKEN(opChan[i].vol,MIN(127,opChan[i].std.vol.val),127);
 | 
				
			||||||
 | 
					      unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[i]];
 | 
				
			||||||
 | 
					      DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[i]];
 | 
				
			||||||
 | 
					      if (isOpMuted[i]) {
 | 
				
			||||||
 | 
					        rWrite(baseAddr+ADDR_TL,127);
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[i].outVol&0x7f,127));
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (opChan[i].std.arp.had) {
 | 
				
			||||||
 | 
					      if (!opChan[i].inPorta) {
 | 
				
			||||||
 | 
					        opChan[i].baseFreq=NOTE_FNUM_BLOCK(parent->calcArp(opChan[i].note,opChan[i].std.arp.val),11);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      opChan[i].freqChanged=true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (opChan[i].std.pitch.had) {
 | 
				
			||||||
 | 
					      if (opChan[i].std.pitch.mode) {
 | 
				
			||||||
 | 
					        opChan[i].pitch2+=opChan[i].std.pitch.val;
 | 
				
			||||||
 | 
					        CLAMP_VAR(opChan[i].pitch2,-32768,32767);
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        opChan[i].pitch2=opChan[i].std.pitch.val;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      opChan[i].freqChanged=true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // param macros
 | 
				
			||||||
 | 
					    unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[i]];
 | 
				
			||||||
 | 
					    DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[i]];
 | 
				
			||||||
 | 
					    DivMacroInt::IntOp& m=opChan[i].std.op[orderedOps[i]];
 | 
				
			||||||
 | 
					    if (m.am.had) {
 | 
				
			||||||
 | 
					      op.am=m.am.val;
 | 
				
			||||||
 | 
					      rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (m.ar.had) {
 | 
				
			||||||
 | 
					      op.ar=m.ar.val;
 | 
				
			||||||
 | 
					      rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (m.dr.had) {
 | 
				
			||||||
 | 
					      op.dr=m.dr.val;
 | 
				
			||||||
 | 
					      rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (m.mult.had) {
 | 
				
			||||||
 | 
					      op.mult=m.mult.val;
 | 
				
			||||||
 | 
					      rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (m.rr.had) {
 | 
				
			||||||
 | 
					      op.rr=m.rr.val;
 | 
				
			||||||
 | 
					      rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (m.sl.had) {
 | 
				
			||||||
 | 
					      op.sl=m.sl.val;
 | 
				
			||||||
 | 
					      rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (m.tl.had) {
 | 
				
			||||||
 | 
					      op.tl=127-m.tl.val;
 | 
				
			||||||
 | 
					      if (isOpMuted[i]) {
 | 
				
			||||||
 | 
					        rWrite(baseAddr+ADDR_TL,127);
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[i].outVol&0x7f,127));
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (m.rs.had) {
 | 
				
			||||||
 | 
					      op.rs=m.rs.val;
 | 
				
			||||||
 | 
					      rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (m.dt.had) {
 | 
				
			||||||
 | 
					      op.dt=m.dt.val;
 | 
				
			||||||
 | 
					      rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (m.d2r.had) {
 | 
				
			||||||
 | 
					      op.d2r=m.d2r.val;
 | 
				
			||||||
 | 
					      rWrite(baseAddr+ADDR_DT2_D2R,op.d2r&31);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (m.ssg.had) {
 | 
				
			||||||
 | 
					      op.ssgEnv=m.ssg.val;
 | 
				
			||||||
 | 
					      rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  DivPlatformGenesis::tick(sysTick);
 | 
					  DivPlatformGenesis::tick(sysTick);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  bool writeNoteOn=false;
 | 
					  bool writeNoteOn=false;
 | 
				
			||||||
| 
						 | 
					@ -527,7 +636,7 @@ void DivPlatformGenesisExt::forceIns() {
 | 
				
			||||||
        if (isOpMuted[j]) {
 | 
					        if (isOpMuted[j]) {
 | 
				
			||||||
          rWrite(baseAddr+0x40,127);
 | 
					          rWrite(baseAddr+0x40,127);
 | 
				
			||||||
        } else if (KVS(i,j)) {
 | 
					        } else if (KVS(i,j)) {
 | 
				
			||||||
          rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[j].vol&0x7f,127));
 | 
					          rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[j].outVol&0x7f,127));
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
          rWrite(baseAddr+0x40,op.tl);
 | 
					          rWrite(baseAddr+0x40,op.tl);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
| 
						 | 
					@ -601,7 +710,9 @@ void DivPlatformGenesisExt::reset() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  for (int i=0; i<4; i++) {
 | 
					  for (int i=0; i<4; i++) {
 | 
				
			||||||
    opChan[i]=DivPlatformGenesisExt::OpChannel();
 | 
					    opChan[i]=DivPlatformGenesisExt::OpChannel();
 | 
				
			||||||
 | 
					    opChan[i].std.setEngine(parent);
 | 
				
			||||||
    opChan[i].vol=127;
 | 
					    opChan[i].vol=127;
 | 
				
			||||||
 | 
					    opChan[i].outVol=127;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // channel 3 mode
 | 
					  // channel 3 mode
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -25,11 +25,15 @@ class DivPlatformGenesisExt: public DivPlatformGenesis {
 | 
				
			||||||
  struct OpChannel {
 | 
					  struct OpChannel {
 | 
				
			||||||
    DivMacroInt std;
 | 
					    DivMacroInt std;
 | 
				
			||||||
    unsigned char freqH, freqL;
 | 
					    unsigned char freqH, freqL;
 | 
				
			||||||
    int freq, baseFreq, pitch, pitch2, portaPauseFreq, ins;
 | 
					    int freq, baseFreq, pitch, pitch2, portaPauseFreq, ins, note;
 | 
				
			||||||
    signed char konCycles;
 | 
					    signed char konCycles;
 | 
				
			||||||
    bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, inPorta, mask;
 | 
					    bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, inPorta, mask;
 | 
				
			||||||
    int vol;
 | 
					    int vol, outVol;
 | 
				
			||||||
    unsigned char pan;
 | 
					    unsigned char pan;
 | 
				
			||||||
 | 
					    void macroInit(DivInstrument* which) {
 | 
				
			||||||
 | 
					      std.init(which);
 | 
				
			||||||
 | 
					      pitch2=0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    OpChannel():
 | 
					    OpChannel():
 | 
				
			||||||
      freqH(0),
 | 
					      freqH(0),
 | 
				
			||||||
      freqL(0),
 | 
					      freqL(0),
 | 
				
			||||||
| 
						 | 
					@ -39,6 +43,7 @@ class DivPlatformGenesisExt: public DivPlatformGenesis {
 | 
				
			||||||
      pitch2(0),
 | 
					      pitch2(0),
 | 
				
			||||||
      portaPauseFreq(0),
 | 
					      portaPauseFreq(0),
 | 
				
			||||||
      ins(-1),
 | 
					      ins(-1),
 | 
				
			||||||
 | 
					      note(0),
 | 
				
			||||||
      active(false),
 | 
					      active(false),
 | 
				
			||||||
      insChanged(true),
 | 
					      insChanged(true),
 | 
				
			||||||
      freqChanged(false),
 | 
					      freqChanged(false),
 | 
				
			||||||
| 
						 | 
					@ -48,6 +53,7 @@ class DivPlatformGenesisExt: public DivPlatformGenesis {
 | 
				
			||||||
      inPorta(false),
 | 
					      inPorta(false),
 | 
				
			||||||
      mask(true),
 | 
					      mask(true),
 | 
				
			||||||
      vol(0),
 | 
					      vol(0),
 | 
				
			||||||
 | 
					      outVol(0),
 | 
				
			||||||
      pan(3) {}
 | 
					      pan(3) {}
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
  OpChannel opChan[4];
 | 
					  OpChannel opChan[4];
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -587,7 +587,17 @@ bool DivPlatformPCSpeaker::keyOffAffectsArp(int ch) {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void DivPlatformPCSpeaker::setFlags(const DivConfig& flags) {
 | 
					void DivPlatformPCSpeaker::setFlags(const DivConfig& flags) {
 | 
				
			||||||
  chipClock=COLOR_NTSC/3.0;
 | 
					  switch (flags.getInt("clockSel",0)) {
 | 
				
			||||||
 | 
					    case 1: // PC-98
 | 
				
			||||||
 | 
					      chipClock=38400*52;
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
 | 
					    case 2: // PC-98
 | 
				
			||||||
 | 
					      chipClock=38400*64;
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
 | 
					    default: // IBM PC
 | 
				
			||||||
 | 
					      chipClock=COLOR_NTSC/3.0;
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
  rate=chipClock/PCSPKR_DIVIDER;
 | 
					  rate=chipClock/PCSPKR_DIVIDER;
 | 
				
			||||||
  speakerType=flags.getInt("speakerType",0)&3;
 | 
					  speakerType=flags.getInt("speakerType",0)&3;
 | 
				
			||||||
  oscBuf->rate=rate;
 | 
					  oscBuf->rate=rate;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -129,7 +129,7 @@ void DivPlatformSMS::tick(bool sysTick) {
 | 
				
			||||||
      if (chan[i].outVol<0) chan[i].outVol=0;
 | 
					      if (chan[i].outVol<0) chan[i].outVol=0;
 | 
				
			||||||
      // old formula
 | 
					      // old formula
 | 
				
			||||||
      // ((chan[i].vol&15)*MIN(15,chan[i].std.vol.val))>>4;
 | 
					      // ((chan[i].vol&15)*MIN(15,chan[i].std.vol.val))>>4;
 | 
				
			||||||
      rWrite(0,0x90|(i<<5)|(isMuted[i]?15:(15-(chan[i].outVol&15))));
 | 
					      chan[i].writeVol=true;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    if (chan[i].std.arp.had) {
 | 
					    if (chan[i].std.arp.had) {
 | 
				
			||||||
      if (!chan[i].inPorta) {
 | 
					      if (!chan[i].inPorta) {
 | 
				
			||||||
| 
						 | 
					@ -235,6 +235,12 @@ void DivPlatformSMS::tick(bool sysTick) {
 | 
				
			||||||
    chan[3].freqChanged=false;
 | 
					    chan[3].freqChanged=false;
 | 
				
			||||||
    updateSNMode=false;
 | 
					    updateSNMode=false;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					  for (int i=0; i<4; i++) {
 | 
				
			||||||
 | 
					    if (chan[i].writeVol) {
 | 
				
			||||||
 | 
					      rWrite(0,0x90|(i<<5)|(isMuted[i]?15:(15-(chan[i].outVol&15))));
 | 
				
			||||||
 | 
					      chan[i].writeVol=false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int DivPlatformSMS::dispatch(DivCommand c) {
 | 
					int DivPlatformSMS::dispatch(DivCommand c) {
 | 
				
			||||||
| 
						 | 
					@ -249,9 +255,11 @@ int DivPlatformSMS::dispatch(DivCommand c) {
 | 
				
			||||||
        chan[c.chan].actualNote=c.value;
 | 
					        chan[c.chan].actualNote=c.value;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      chan[c.chan].active=true;
 | 
					      chan[c.chan].active=true;
 | 
				
			||||||
      if (!parent->song.brokenOutVol2) {
 | 
					      //if (!parent->song.brokenOutVol2) {
 | 
				
			||||||
        rWrite(0,0x90|c.chan<<5|(isMuted[c.chan]?15:(15-(chan[c.chan].vol&15))));
 | 
					        chan[c.chan].writeVol=true;
 | 
				
			||||||
      }
 | 
					        chan[c.chan].outVol=chan[c.chan].vol;
 | 
				
			||||||
 | 
					        //rWrite(0,0x90|c.chan<<5|(isMuted[c.chan]?15:(15-(chan[c.chan].vol&15))));
 | 
				
			||||||
 | 
					      //}
 | 
				
			||||||
      chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_STD));
 | 
					      chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_STD));
 | 
				
			||||||
      if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
 | 
					      if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
 | 
				
			||||||
        chan[c.chan].outVol=chan[c.chan].vol;
 | 
					        chan[c.chan].outVol=chan[c.chan].vol;
 | 
				
			||||||
| 
						 | 
					@ -276,7 +284,9 @@ int DivPlatformSMS::dispatch(DivCommand c) {
 | 
				
			||||||
        if (!chan[c.chan].std.vol.has) {
 | 
					        if (!chan[c.chan].std.vol.has) {
 | 
				
			||||||
          chan[c.chan].outVol=c.value;
 | 
					          chan[c.chan].outVol=c.value;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        if (chan[c.chan].active) rWrite(0,0x90|c.chan<<5|(isMuted[c.chan]?15:(15-(chan[c.chan].vol&15))));
 | 
					        if (chan[c.chan].active) {
 | 
				
			||||||
 | 
					          chan[c.chan].writeVol=true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      break;
 | 
					      break;
 | 
				
			||||||
    case DIV_CMD_GET_VOLUME:
 | 
					    case DIV_CMD_GET_VOLUME:
 | 
				
			||||||
| 
						 | 
					@ -356,7 +366,7 @@ int DivPlatformSMS::dispatch(DivCommand c) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void DivPlatformSMS::muteChannel(int ch, bool mute) {
 | 
					void DivPlatformSMS::muteChannel(int ch, bool mute) {
 | 
				
			||||||
  isMuted[ch]=mute;
 | 
					  isMuted[ch]=mute;
 | 
				
			||||||
  if (chan[ch].active) rWrite(0,0x90|ch<<5|(isMuted[ch]?15:(15-(chan[ch].outVol&15))));
 | 
					  if (chan[ch].active) chan[ch].writeVol=true;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void DivPlatformSMS::forceIns() {
 | 
					void DivPlatformSMS::forceIns() {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -31,7 +31,7 @@ extern "C" {
 | 
				
			||||||
class DivPlatformSMS: public DivDispatch {
 | 
					class DivPlatformSMS: public DivDispatch {
 | 
				
			||||||
  struct Channel {
 | 
					  struct Channel {
 | 
				
			||||||
    int freq, baseFreq, pitch, pitch2, note, actualNote, ins;
 | 
					    int freq, baseFreq, pitch, pitch2, note, actualNote, ins;
 | 
				
			||||||
    bool active, insChanged, freqChanged, keyOn, keyOff, inPorta;
 | 
					    bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, writeVol;
 | 
				
			||||||
    signed char vol, outVol;
 | 
					    signed char vol, outVol;
 | 
				
			||||||
    DivMacroInt std;
 | 
					    DivMacroInt std;
 | 
				
			||||||
    void macroInit(DivInstrument* which) {
 | 
					    void macroInit(DivInstrument* which) {
 | 
				
			||||||
| 
						 | 
					@ -52,6 +52,7 @@ class DivPlatformSMS: public DivDispatch {
 | 
				
			||||||
      keyOn(false),
 | 
					      keyOn(false),
 | 
				
			||||||
      keyOff(false),
 | 
					      keyOff(false),
 | 
				
			||||||
      inPorta(false),
 | 
					      inPorta(false),
 | 
				
			||||||
 | 
					      writeVol(false),
 | 
				
			||||||
      vol(15),
 | 
					      vol(15),
 | 
				
			||||||
      outVol(15) {}
 | 
					      outVol(15) {}
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -952,6 +952,7 @@ void DivPlatformYM2203::setFlags(const DivConfig& flags) {
 | 
				
			||||||
      ayDiv=16;
 | 
					      ayDiv=16;
 | 
				
			||||||
      break;
 | 
					      break;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					  noExtMacros=flags.getBool("noExtMacros",false);
 | 
				
			||||||
  rate=fm->sample_rate(chipClock);
 | 
					  rate=fm->sample_rate(chipClock);
 | 
				
			||||||
  for (int i=0; i<6; i++) {
 | 
					  for (int i=0; i<6; i++) {
 | 
				
			||||||
    oscBuf[i]->rate=rate;
 | 
					    oscBuf[i]->rate=rate;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -91,7 +91,7 @@ class DivPlatformYM2203: public DivPlatformOPN {
 | 
				
			||||||
    DivPlatformAY8910* ay;
 | 
					    DivPlatformAY8910* ay;
 | 
				
			||||||
    unsigned char sampleBank;
 | 
					    unsigned char sampleBank;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    bool extMode;
 | 
					    bool extMode, noExtMacros;
 | 
				
			||||||
    unsigned char prescale;
 | 
					    unsigned char prescale;
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
    friend void putDispatchChip(void*,int);
 | 
					    friend void putDispatchChip(void*,int);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1394,6 +1394,7 @@ void DivPlatformYM2608::setFlags(const DivConfig& flags) {
 | 
				
			||||||
      ayDiv=32;
 | 
					      ayDiv=32;
 | 
				
			||||||
      break;
 | 
					      break;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					  noExtMacros=flags.getBool("noExtMacros",false);
 | 
				
			||||||
  rate=fm->sample_rate(chipClock);
 | 
					  rate=fm->sample_rate(chipClock);
 | 
				
			||||||
  for (int i=0; i<16; i++) {
 | 
					  for (int i=0; i<16; i++) {
 | 
				
			||||||
    oscBuf[i]->rate=rate;
 | 
					    oscBuf[i]->rate=rate;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -106,7 +106,7 @@ class DivPlatformYM2608: public DivPlatformOPN {
 | 
				
			||||||
    unsigned char writeRSSOff, writeRSSOn;
 | 
					    unsigned char writeRSSOff, writeRSSOn;
 | 
				
			||||||
    int globalRSSVolume;
 | 
					    int globalRSSVolume;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    bool extMode;
 | 
					    bool extMode, noExtMacros;
 | 
				
			||||||
    unsigned char prescale;
 | 
					    unsigned char prescale;
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
    double NOTE_OPNB(int ch, int note);
 | 
					    double NOTE_OPNB(int ch, int note);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -141,7 +141,7 @@ template<int ChanNum> class DivPlatformYM2610Base: public DivPlatformOPN {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    unsigned char sampleBank;
 | 
					    unsigned char sampleBank;
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
    bool extMode;
 | 
					    bool extMode, noExtMacros;
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
    unsigned char writeADPCMAOff, writeADPCMAOn;
 | 
					    unsigned char writeADPCMAOff, writeADPCMAOn;
 | 
				
			||||||
    int globalADPCMAVolume;
 | 
					    int globalADPCMAVolume;
 | 
				
			||||||
| 
						 | 
					@ -269,6 +269,7 @@ template<int ChanNum> class DivPlatformYM2610Base: public DivPlatformOPN {
 | 
				
			||||||
          chipClock=8000000.0;
 | 
					          chipClock=8000000.0;
 | 
				
			||||||
          break;
 | 
					          break;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					      noExtMacros=flags.getBool("noExtMacros",false);
 | 
				
			||||||
      rate=chipClock/16;
 | 
					      rate=chipClock/16;
 | 
				
			||||||
      for (int i=0; i<ChanNum; i++) {
 | 
					      for (int i=0; i<ChanNum; i++) {
 | 
				
			||||||
        oscBuf[i]->rate=rate;
 | 
					        oscBuf[i]->rate=rate;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -975,6 +975,20 @@ void DivEngine::nextRow() {
 | 
				
			||||||
    printf("| %.2x:%s | \x1b[1;33m%3d%s\x1b[m\n",curOrder,pb1,curRow,pb3);
 | 
					    printf("| %.2x:%s | \x1b[1;33m%3d%s\x1b[m\n",curOrder,pb1,curRow,pb3);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (curSubSong->hilightA>0) {
 | 
				
			||||||
 | 
					    if ((curRow%curSubSong->hilightA)==0) {
 | 
				
			||||||
 | 
					      pendingMetroTick=1;
 | 
				
			||||||
 | 
					      elapsedBeats++;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if (curSubSong->hilightB>0) {
 | 
				
			||||||
 | 
					    if ((curRow%curSubSong->hilightB)==0) {
 | 
				
			||||||
 | 
					      pendingMetroTick=2;
 | 
				
			||||||
 | 
					      elapsedBars++;
 | 
				
			||||||
 | 
					      elapsedBeats=0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  prevOrder=curOrder;
 | 
					  prevOrder=curOrder;
 | 
				
			||||||
  prevRow=curRow;
 | 
					  prevRow=curRow;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1608,16 +1622,6 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi
 | 
				
			||||||
    // 2. check whether we gonna tick
 | 
					    // 2. check whether we gonna tick
 | 
				
			||||||
    if (cycles<=0) {
 | 
					    if (cycles<=0) {
 | 
				
			||||||
      // we have to tick
 | 
					      // we have to tick
 | 
				
			||||||
      if (!freelance && stepPlay!=-1 && subticks==1) {
 | 
					 | 
				
			||||||
        unsigned int realPos=size-(runLeftG>>MASTER_CLOCK_PREC);
 | 
					 | 
				
			||||||
        if (realPos>=size) realPos=size-1;
 | 
					 | 
				
			||||||
        if (curSubSong->hilightA>0) {
 | 
					 | 
				
			||||||
          if ((curRow%curSubSong->hilightA)==0 && ticks==1) metroTick[realPos]=1;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        if (curSubSong->hilightB>0) {
 | 
					 | 
				
			||||||
          if ((curRow%curSubSong->hilightB)==0 && ticks==1) metroTick[realPos]=2;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      if (nextTick()) {
 | 
					      if (nextTick()) {
 | 
				
			||||||
        lastLoopPos=size-(runLeftG>>MASTER_CLOCK_PREC);
 | 
					        lastLoopPos=size-(runLeftG>>MASTER_CLOCK_PREC);
 | 
				
			||||||
        logD("last loop pos: %d for a size of %d and runLeftG of %d",lastLoopPos,size,runLeftG);
 | 
					        logD("last loop pos: %d for a size of %d and runLeftG of %d",lastLoopPos,size,runLeftG);
 | 
				
			||||||
| 
						 | 
					@ -1634,6 +1638,12 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					      if (pendingMetroTick) {
 | 
				
			||||||
 | 
					        unsigned int realPos=size-(runLeftG>>MASTER_CLOCK_PREC);
 | 
				
			||||||
 | 
					        if (realPos>=size) realPos=size-1;
 | 
				
			||||||
 | 
					        metroTick[realPos]=pendingMetroTick;
 | 
				
			||||||
 | 
					        pendingMetroTick=0;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      // 3. tick the clock and fill buffers as needed
 | 
					      // 3. tick the clock and fill buffers as needed
 | 
				
			||||||
      if (cycles<runLeftG) {
 | 
					      if (cycles<runLeftG) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -23,6 +23,10 @@
 | 
				
			||||||
#include "song.h"
 | 
					#include "song.h"
 | 
				
			||||||
#include "../ta-log.h"
 | 
					#include "../ta-log.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DivSysDef* DivEngine::sysDefs[256];
 | 
				
			||||||
 | 
					DivSystem DivEngine::sysFileMapFur[256];
 | 
				
			||||||
 | 
					DivSystem DivEngine::sysFileMapDMF[256];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
DivSystem DivEngine::systemFromFileFur(unsigned char val) {
 | 
					DivSystem DivEngine::systemFromFileFur(unsigned char val) {
 | 
				
			||||||
  return sysFileMapFur[val];
 | 
					  return sysFileMapFur[val];
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -94,10 +94,12 @@ SafeWriter* DivEngine::saveZSM(unsigned int zsmrate, bool loop) {
 | 
				
			||||||
  if (VERA >= 0) disCont[VERA].dispatch->toggleRegisterDump(true);
 | 
					  if (VERA >= 0) disCont[VERA].dispatch->toggleRegisterDump(true);
 | 
				
			||||||
  if (YM >= 0) {
 | 
					  if (YM >= 0) {
 | 
				
			||||||
    disCont[YM].dispatch->toggleRegisterDump(true);
 | 
					    disCont[YM].dispatch->toggleRegisterDump(true);
 | 
				
			||||||
    zsm.writeYM(0x18,0); // initialize the LFO freq to 0
 | 
					    // emit LFO initialization commands
 | 
				
			||||||
    // note - I think there's a bug where Furnace writes AMD/PMD=max
 | 
					    zsm.writeYM(0x18,0);    // freq = 0
 | 
				
			||||||
    // that shouldn't be there, requiring this initialization that shouldn't
 | 
					    zsm.writeYM(0x19,0x7F); // AMD  = 7F
 | 
				
			||||||
    // be there for ZSM.
 | 
					    zsm.writeYM(0x19,0xFF); // PMD  = 7F
 | 
				
			||||||
 | 
					    // TODO: incorporate the Furnace meta-command for init data and filter
 | 
				
			||||||
 | 
					    //       out writes to otherwise-unused channels.
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  while (!done) {
 | 
					  while (!done) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -66,6 +66,7 @@ const char* aboutLine[]={
 | 
				
			||||||
  "BlueElectric05",
 | 
					  "BlueElectric05",
 | 
				
			||||||
  "breakthetargets",
 | 
					  "breakthetargets",
 | 
				
			||||||
  "brickblock369",
 | 
					  "brickblock369",
 | 
				
			||||||
 | 
					  "Burnt Fishy",
 | 
				
			||||||
  "CaptainMalware",
 | 
					  "CaptainMalware",
 | 
				
			||||||
  "DeMOSic",
 | 
					  "DeMOSic",
 | 
				
			||||||
  "DevEd",
 | 
					  "DevEd",
 | 
				
			||||||
| 
						 | 
					@ -91,6 +92,7 @@ const char* aboutLine[]={
 | 
				
			||||||
  "Raijin",
 | 
					  "Raijin",
 | 
				
			||||||
  "SnugglyBun",
 | 
					  "SnugglyBun",
 | 
				
			||||||
  "SuperJet Spade",
 | 
					  "SuperJet Spade",
 | 
				
			||||||
 | 
					  "TakuikaNinja",
 | 
				
			||||||
  "TheDuccinator",
 | 
					  "TheDuccinator",
 | 
				
			||||||
  "theloredev",
 | 
					  "theloredev",
 | 
				
			||||||
  "TheRealHedgehogSonic",
 | 
					  "TheRealHedgehogSonic",
 | 
				
			||||||
| 
						 | 
					@ -153,7 +155,6 @@ const char* aboutLine[]={
 | 
				
			||||||
  "MSM6295 emulator by cam900",
 | 
					  "MSM6295 emulator by cam900",
 | 
				
			||||||
  "",
 | 
					  "",
 | 
				
			||||||
  "greetings to:",
 | 
					  "greetings to:",
 | 
				
			||||||
  "Fractal Sound team",
 | 
					 | 
				
			||||||
  "NEOART Costa Rica",
 | 
					  "NEOART Costa Rica",
 | 
				
			||||||
  "all members of Deflers of Noice!",
 | 
					  "all members of Deflers of Noice!",
 | 
				
			||||||
  "",
 | 
					  "",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										116
									
								
								src/gui/clock.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								src/gui/clock.cpp
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,116 @@
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * 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.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "gui.h"
 | 
				
			||||||
 | 
					#include "imgui_internal.h"
 | 
				
			||||||
 | 
					#include "imgui.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void FurnaceGUI::drawClock() {
 | 
				
			||||||
 | 
					  if (nextWindow==GUI_WINDOW_CLOCK) {
 | 
				
			||||||
 | 
					    clockOpen=true;
 | 
				
			||||||
 | 
					    ImGui::SetNextWindowFocus();
 | 
				
			||||||
 | 
					    nextWindow=GUI_WINDOW_NOTHING;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if (!clockOpen) return;
 | 
				
			||||||
 | 
					  if (ImGui::Begin("Clock",&clockOpen,globalWinFlags)) {
 | 
				
			||||||
 | 
					    int row=e->getRow();
 | 
				
			||||||
 | 
					    int elapsedBars=e->getElapsedBars();
 | 
				
			||||||
 | 
					    int elapsedBeats=e->getElapsedBeats();
 | 
				
			||||||
 | 
					    bool playing=e->isPlaying();
 | 
				
			||||||
 | 
					    if (clockShowRow) {
 | 
				
			||||||
 | 
					      ImGui::PushFont(bigFont);
 | 
				
			||||||
 | 
					      ImGui::Text("%.3d:%.3d",e->getOrder(),row);
 | 
				
			||||||
 | 
					      ImGui::PopFont();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (clockShowBeat) {
 | 
				
			||||||
 | 
					      ImGui::PushFont(bigFont);
 | 
				
			||||||
 | 
					      ImGui::Text("%.3d:%.1d",elapsedBars,elapsedBeats+1);
 | 
				
			||||||
 | 
					      ImGui::PopFont();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (clockShowMetro) {
 | 
				
			||||||
 | 
					      ImDrawList* dl=ImGui::GetWindowDrawList();
 | 
				
			||||||
 | 
					      ImGuiWindow* window=ImGui::GetCurrentWindow();
 | 
				
			||||||
 | 
					      ImVec2 size=ImGui::GetContentRegionAvail();
 | 
				
			||||||
 | 
					      size.y=12.0f*dpiScale;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      ImVec2 minArea=window->DC.CursorPos;
 | 
				
			||||||
 | 
					      ImVec2 maxArea=ImVec2(
 | 
				
			||||||
 | 
					        minArea.x+size.x,
 | 
				
			||||||
 | 
					        minArea.y+size.y
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					      ImRect rect=ImRect(minArea,maxArea);
 | 
				
			||||||
 | 
					      /*ImRect inRect=rect;
 | 
				
			||||||
 | 
					      inRect.Min.x+=dpiScale;
 | 
				
			||||||
 | 
					      inRect.Min.y+=dpiScale;
 | 
				
			||||||
 | 
					      inRect.Max.x-=dpiScale;
 | 
				
			||||||
 | 
					      inRect.Max.y-=dpiScale;*/
 | 
				
			||||||
 | 
					      ImGuiStyle& style=ImGui::GetStyle();
 | 
				
			||||||
 | 
					      ImGui::ItemSize(size,style.FramePadding.y);
 | 
				
			||||||
 | 
					      if (ImGui::ItemAdd(rect,ImGui::GetID("metroQ"))) {
 | 
				
			||||||
 | 
					        int h1=e->curSubSong->hilightA;
 | 
				
			||||||
 | 
					        int h2=e->curSubSong->hilightB;
 | 
				
			||||||
 | 
					        if (h1>0 && h2>0) {
 | 
				
			||||||
 | 
					          int beats=(h2+(h1-1))/h1;
 | 
				
			||||||
 | 
					          if (beats<0) beats=1;
 | 
				
			||||||
 | 
					          if (beats>16) beats=16;
 | 
				
			||||||
 | 
					          if (playing) {
 | 
				
			||||||
 | 
					            if (elapsedBeats!=oldBeat || elapsedBars!=oldBar) {
 | 
				
			||||||
 | 
					              if (elapsedBeats>15) elapsedBeats=15;
 | 
				
			||||||
 | 
					              clockMetroTick[elapsedBeats]=1.0f;
 | 
				
			||||||
 | 
					              oldBeat=elapsedBeats;
 | 
				
			||||||
 | 
					              oldBar=elapsedBars;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          } else {
 | 
				
			||||||
 | 
					            oldBeat=-1;
 | 
				
			||||||
 | 
					            oldBar=-1;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          for (int i=0; i<beats; i++) {
 | 
				
			||||||
 | 
					            ImVec2 minB=ImLerp(minArea,maxArea,ImVec2((float)i/(float)beats,0.0f));
 | 
				
			||||||
 | 
					            ImVec2 maxB=ImLerp(minArea,maxArea,ImVec2((float)(i+1)/(float)beats,1.0f));
 | 
				
			||||||
 | 
					            ImVec4 col=ImLerp(uiColors[GUI_COLOR_CLOCK_BEAT_LOW],uiColors[GUI_COLOR_CLOCK_BEAT_HIGH],clockMetroTick[i]);
 | 
				
			||||||
 | 
					            dl->AddQuadFilled(
 | 
				
			||||||
 | 
					              ImLerp(minB,maxB,ImVec2(0.35f,0.0f)),
 | 
				
			||||||
 | 
					              ImLerp(minB,maxB,ImVec2(0.9f,0.0f)),
 | 
				
			||||||
 | 
					              ImLerp(minB,maxB,ImVec2(0.65f,1.0f)),
 | 
				
			||||||
 | 
					              ImLerp(minB,maxB,ImVec2(0.1f,1.0f)),
 | 
				
			||||||
 | 
					              ImGui::GetColorU32(col)
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (elapsedBeats==i && playing) {
 | 
				
			||||||
 | 
					              clockMetroTick[i]-=0.1f*ImGui::GetIO().DeltaTime*60.0f;
 | 
				
			||||||
 | 
					              if (clockMetroTick[i]<0.3f) clockMetroTick[i]=0.3f;
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					              clockMetroTick[i]-=0.1f*ImGui::GetIO().DeltaTime*60.0f;
 | 
				
			||||||
 | 
					              if (clockMetroTick[i]<0.0f) clockMetroTick[i]=0.0f;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (clockShowTime) {
 | 
				
			||||||
 | 
					      int totalTicks=e->getTotalTicks();
 | 
				
			||||||
 | 
					      int totalSeconds=e->getTotalSeconds();
 | 
				
			||||||
 | 
					      ImGui::PushFont(bigFont);
 | 
				
			||||||
 | 
					      ImGui::Text("%.2d:%.2d.%.2d",(totalSeconds/60),totalSeconds%60,totalTicks/10000);
 | 
				
			||||||
 | 
					      ImGui::PopFont();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_SPOILER;
 | 
				
			||||||
 | 
					  ImGui::End();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -60,6 +60,11 @@ void FurnaceGUI::drawInsList(bool asChild) {
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        ImGui::EndPopup();
 | 
					        ImGui::EndPopup();
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) {
 | 
				
			||||||
 | 
					        displayInsTypeList=true;
 | 
				
			||||||
 | 
					        displayInsTypeListMakeInsSample=-1;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    ImGui::SameLine();
 | 
					    ImGui::SameLine();
 | 
				
			||||||
    if (ImGui::Button(ICON_FA_FILES_O "##InsClone")) {
 | 
					    if (ImGui::Button(ICON_FA_FILES_O "##InsClone")) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1291,14 +1291,54 @@ void FurnaceGUI::doAction(int what) {
 | 
				
			||||||
      break;
 | 
					      break;
 | 
				
			||||||
    case GUI_ACTION_SAMPLE_MAKE_INS: {
 | 
					    case GUI_ACTION_SAMPLE_MAKE_INS: {
 | 
				
			||||||
      if (curSample<0 || curSample>=(int)e->song.sample.size()) break;
 | 
					      if (curSample<0 || curSample>=(int)e->song.sample.size()) break;
 | 
				
			||||||
 | 
					      // determine instrument type
 | 
				
			||||||
 | 
					      std::vector<DivInstrumentType> tempTypeList=e->getPossibleInsTypes();
 | 
				
			||||||
 | 
					      makeInsTypeList.clear();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      for (DivInstrumentType& i: tempTypeList) {
 | 
				
			||||||
 | 
					        if (i==DIV_INS_PCE ||
 | 
				
			||||||
 | 
					            i==DIV_INS_MSM6258 ||
 | 
				
			||||||
 | 
					            i==DIV_INS_MSM6295 ||
 | 
				
			||||||
 | 
					            i==DIV_INS_ADPCMA ||
 | 
				
			||||||
 | 
					            i==DIV_INS_ADPCMB ||
 | 
				
			||||||
 | 
					            i==DIV_INS_SEGAPCM ||
 | 
				
			||||||
 | 
					            i==DIV_INS_QSOUND ||
 | 
				
			||||||
 | 
					            i==DIV_INS_YMZ280B ||
 | 
				
			||||||
 | 
					            i==DIV_INS_RF5C68 ||
 | 
				
			||||||
 | 
					            i==DIV_INS_MULTIPCM ||
 | 
				
			||||||
 | 
					            i==DIV_INS_MIKEY ||
 | 
				
			||||||
 | 
					            i==DIV_INS_X1_010 ||
 | 
				
			||||||
 | 
					            i==DIV_INS_SWAN ||
 | 
				
			||||||
 | 
					            i==DIV_INS_AY ||
 | 
				
			||||||
 | 
					            i==DIV_INS_AY8930 ||
 | 
				
			||||||
 | 
					            i==DIV_INS_VRC6 ||
 | 
				
			||||||
 | 
					            i==DIV_INS_SU ||
 | 
				
			||||||
 | 
					            i==DIV_INS_SNES ||
 | 
				
			||||||
 | 
					            i==DIV_INS_ES5506) {
 | 
				
			||||||
 | 
					          makeInsTypeList.push_back(i);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (makeInsTypeList.size()>1) {
 | 
				
			||||||
 | 
					        displayInsTypeList=true;
 | 
				
			||||||
 | 
					        displayInsTypeListMakeInsSample=curSample;
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      DivInstrumentType insType=DIV_INS_AMIGA;
 | 
				
			||||||
 | 
					      if (!makeInsTypeList.empty()) {
 | 
				
			||||||
 | 
					        insType=makeInsTypeList[0];
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      DivSample* sample=e->song.sample[curSample];
 | 
					      DivSample* sample=e->song.sample[curSample];
 | 
				
			||||||
      curIns=e->addInstrument(cursor.xCoarse);
 | 
					      curIns=e->addInstrument(cursor.xCoarse);
 | 
				
			||||||
      if (curIns==-1) {
 | 
					      if (curIns==-1) {
 | 
				
			||||||
        showError("too many instruments!");
 | 
					        showError("too many instruments!");
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        e->song.ins[curIns]->type=DIV_INS_AMIGA;
 | 
					        e->song.ins[curIns]->type=insType;
 | 
				
			||||||
        e->song.ins[curIns]->name=sample->name;
 | 
					        e->song.ins[curIns]->name=sample->name;
 | 
				
			||||||
        e->song.ins[curIns]->amiga.initSample=curSample;
 | 
					        e->song.ins[curIns]->amiga.initSample=curSample;
 | 
				
			||||||
 | 
					        if (insType!=DIV_INS_AMIGA) e->song.ins[curIns]->amiga.useSample=true;
 | 
				
			||||||
        nextWindow=GUI_WINDOW_INS_EDIT;
 | 
					        nextWindow=GUI_WINDOW_INS_EDIT;
 | 
				
			||||||
        MARK_MODIFIED;
 | 
					        MARK_MODIFIED;
 | 
				
			||||||
        wavePreviewInit=true;
 | 
					        wavePreviewInit=true;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -242,6 +242,7 @@ void FurnaceGUI::drawMobileControls() {
 | 
				
			||||||
          ImGui::SameLine();
 | 
					          ImGui::SameLine();
 | 
				
			||||||
          if (ImGui::Button("Settings")) {
 | 
					          if (ImGui::Button("Settings")) {
 | 
				
			||||||
            mobileMenuOpen=false;
 | 
					            mobileMenuOpen=false;
 | 
				
			||||||
 | 
					            settingsOpen=true;
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
          ImGui::SameLine();
 | 
					          ImGui::SameLine();
 | 
				
			||||||
          if (ImGui::Button("Log")) {
 | 
					          if (ImGui::Button("Log")) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2651,8 +2651,11 @@ int _processEvent(void* instance, SDL_Event* event) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int FurnaceGUI::processEvent(SDL_Event* ev) {
 | 
					int FurnaceGUI::processEvent(SDL_Event* ev) {
 | 
				
			||||||
#ifdef IS_MOBILE
 | 
					#ifdef IS_MOBILE
 | 
				
			||||||
  if (ev->type==SDL_APP_WILLENTERBACKGROUND) {
 | 
					  if (ev->type==SDL_APP_TERMINATING) {
 | 
				
			||||||
    // TODO: save "last state" and potentially suspend engine
 | 
					    // TODO: save last song state here
 | 
				
			||||||
 | 
					  } else if (ev->type==SDL_APP_WILLENTERBACKGROUND) {
 | 
				
			||||||
 | 
					    commitState();
 | 
				
			||||||
 | 
					    e->saveConf();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
  if (ev->type==SDL_KEYDOWN) {
 | 
					  if (ev->type==SDL_KEYDOWN) {
 | 
				
			||||||
| 
						 | 
					@ -2958,7 +2961,11 @@ bool FurnaceGUI::detectOutOfBoundsWindow() {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool FurnaceGUI::loop() {
 | 
					bool FurnaceGUI::loop() {
 | 
				
			||||||
 | 
					#ifdef IS_MOBILE
 | 
				
			||||||
 | 
					  bool doThreadedInput=true;
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
  bool doThreadedInput=!settings.noThreadedInput;
 | 
					  bool doThreadedInput=!settings.noThreadedInput;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
  if (doThreadedInput) {
 | 
					  if (doThreadedInput) {
 | 
				
			||||||
    logD("key input: event filter");
 | 
					    logD("key input: event filter");
 | 
				
			||||||
    SDL_SetEventFilter(_processEvent,this);
 | 
					    SDL_SetEventFilter(_processEvent,this);
 | 
				
			||||||
| 
						 | 
					@ -3620,6 +3627,7 @@ bool FurnaceGUI::loop() {
 | 
				
			||||||
        if (ImGui::MenuItem("oscilloscope (master)",BIND_FOR(GUI_ACTION_WINDOW_OSCILLOSCOPE),oscOpen)) oscOpen=!oscOpen;
 | 
					        if (ImGui::MenuItem("oscilloscope (master)",BIND_FOR(GUI_ACTION_WINDOW_OSCILLOSCOPE),oscOpen)) oscOpen=!oscOpen;
 | 
				
			||||||
        if (ImGui::MenuItem("oscilloscope (per-channel)",BIND_FOR(GUI_ACTION_WINDOW_CHAN_OSC),chanOscOpen)) chanOscOpen=!chanOscOpen;
 | 
					        if (ImGui::MenuItem("oscilloscope (per-channel)",BIND_FOR(GUI_ACTION_WINDOW_CHAN_OSC),chanOscOpen)) chanOscOpen=!chanOscOpen;
 | 
				
			||||||
        if (ImGui::MenuItem("volume meter",BIND_FOR(GUI_ACTION_WINDOW_VOL_METER),volMeterOpen)) volMeterOpen=!volMeterOpen;
 | 
					        if (ImGui::MenuItem("volume meter",BIND_FOR(GUI_ACTION_WINDOW_VOL_METER),volMeterOpen)) volMeterOpen=!volMeterOpen;
 | 
				
			||||||
 | 
					        if (ImGui::MenuItem("clock",BIND_FOR(GUI_ACTION_WINDOW_CLOCK),clockOpen)) clockOpen=!clockOpen;
 | 
				
			||||||
        if (ImGui::MenuItem("register view",BIND_FOR(GUI_ACTION_WINDOW_REGISTER_VIEW),regViewOpen)) regViewOpen=!regViewOpen;
 | 
					        if (ImGui::MenuItem("register view",BIND_FOR(GUI_ACTION_WINDOW_REGISTER_VIEW),regViewOpen)) regViewOpen=!regViewOpen;
 | 
				
			||||||
        if (ImGui::MenuItem("log viewer",BIND_FOR(GUI_ACTION_WINDOW_LOG),logOpen)) logOpen=!logOpen;
 | 
					        if (ImGui::MenuItem("log viewer",BIND_FOR(GUI_ACTION_WINDOW_LOG),logOpen)) logOpen=!logOpen;
 | 
				
			||||||
        if (ImGui::MenuItem("statistics",BIND_FOR(GUI_ACTION_WINDOW_STATS),statsOpen)) statsOpen=!statsOpen;
 | 
					        if (ImGui::MenuItem("statistics",BIND_FOR(GUI_ACTION_WINDOW_STATS),statsOpen)) statsOpen=!statsOpen;
 | 
				
			||||||
| 
						 | 
					@ -3747,6 +3755,7 @@ bool FurnaceGUI::loop() {
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      globalWinFlags=0;
 | 
					      globalWinFlags=0;
 | 
				
			||||||
 | 
					      drawSettings();
 | 
				
			||||||
      drawDebug();
 | 
					      drawDebug();
 | 
				
			||||||
      drawLog();
 | 
					      drawLog();
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
| 
						 | 
					@ -3782,6 +3791,7 @@ bool FurnaceGUI::loop() {
 | 
				
			||||||
      drawChannels();
 | 
					      drawChannels();
 | 
				
			||||||
      drawPatManager();
 | 
					      drawPatManager();
 | 
				
			||||||
      drawSysManager();
 | 
					      drawSysManager();
 | 
				
			||||||
 | 
					      drawClock();
 | 
				
			||||||
      drawRegView();
 | 
					      drawRegView();
 | 
				
			||||||
      drawLog();
 | 
					      drawLog();
 | 
				
			||||||
      drawEffectList();
 | 
					      drawEffectList();
 | 
				
			||||||
| 
						 | 
					@ -4399,6 +4409,11 @@ bool FurnaceGUI::loop() {
 | 
				
			||||||
      ImGui::OpenPopup("Import Raw Sample");
 | 
					      ImGui::OpenPopup("Import Raw Sample");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (displayInsTypeList) {
 | 
				
			||||||
 | 
					      displayInsTypeList=false;
 | 
				
			||||||
 | 
					      ImGui::OpenPopup("InsTypeList");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (displayExporting) {
 | 
					    if (displayExporting) {
 | 
				
			||||||
      displayExporting=false;
 | 
					      displayExporting=false;
 | 
				
			||||||
      ImGui::OpenPopup("Rendering...");
 | 
					      ImGui::OpenPopup("Rendering...");
 | 
				
			||||||
| 
						 | 
					@ -4428,9 +4443,14 @@ bool FurnaceGUI::loop() {
 | 
				
			||||||
      ImGui::EndPopup();
 | 
					      ImGui::EndPopup();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ImGui::SetNextWindowSizeConstraints(ImVec2(400.0f*dpiScale,200.0f*dpiScale),ImVec2(canvasW,canvasH));
 | 
					    ImVec2 newSongMinSize=mobileUI?ImVec2(canvasW-(portrait?0:(60.0*dpiScale)),canvasH-60.0*dpiScale):ImVec2(400.0f*dpiScale,200.0f*dpiScale);
 | 
				
			||||||
 | 
					    ImVec2 newSongMaxSize=ImVec2(canvasW-((mobileUI && !portrait)?(60.0*dpiScale):0),canvasH-(mobileUI?(60.0*dpiScale):0));
 | 
				
			||||||
 | 
					    ImGui::SetNextWindowSizeConstraints(newSongMinSize,newSongMaxSize);
 | 
				
			||||||
    if (ImGui::BeginPopupModal("New Song",NULL,ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoScrollWithMouse|ImGuiWindowFlags_NoScrollbar)) {
 | 
					    if (ImGui::BeginPopupModal("New Song",NULL,ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoScrollWithMouse|ImGuiWindowFlags_NoScrollbar)) {
 | 
				
			||||||
      ImGui::SetWindowPos(ImVec2(((canvasW)-ImGui::GetWindowSize().x)*0.5,((canvasH)-ImGui::GetWindowSize().y)*0.5));
 | 
					      ImGui::SetWindowPos(ImVec2(((canvasW)-ImGui::GetWindowSize().x)*0.5,((canvasH)-ImGui::GetWindowSize().y)*0.5));
 | 
				
			||||||
 | 
					      if (ImGui::GetWindowSize().x<newSongMinSize.x || ImGui::GetWindowSize().y<newSongMinSize.y) {
 | 
				
			||||||
 | 
					        ImGui::SetWindowSize(newSongMinSize,ImGuiCond_Always);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
      drawNewSong();
 | 
					      drawNewSong();
 | 
				
			||||||
      ImGui::EndPopup();
 | 
					      ImGui::EndPopup();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					@ -4774,6 +4794,31 @@ bool FurnaceGUI::loop() {
 | 
				
			||||||
      ImGui::EndPopup();
 | 
					      ImGui::EndPopup();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (ImGui::BeginPopup("InsTypeList",ImGuiWindowFlags_NoMove|ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings)) {
 | 
				
			||||||
 | 
					      char temp[1024];
 | 
				
			||||||
 | 
					      for (DivInstrumentType& i: makeInsTypeList) {
 | 
				
			||||||
 | 
					        strncpy(temp,insTypes[i],1023);
 | 
				
			||||||
 | 
					        if (ImGui::MenuItem(temp)) {
 | 
				
			||||||
 | 
					          // create ins
 | 
				
			||||||
 | 
					          curIns=e->addInstrument(-1,i);
 | 
				
			||||||
 | 
					          if (curIns==-1) {
 | 
				
			||||||
 | 
					            showError("too many instruments!");
 | 
				
			||||||
 | 
					          } else {
 | 
				
			||||||
 | 
					            if (displayInsTypeListMakeInsSample>=0 && displayInsTypeListMakeInsSample<(int)e->song.sample.size()) {
 | 
				
			||||||
 | 
					              e->song.ins[curIns]->type=i;
 | 
				
			||||||
 | 
					              e->song.ins[curIns]->name=e->song.sample[displayInsTypeListMakeInsSample]->name;
 | 
				
			||||||
 | 
					              e->song.ins[curIns]->amiga.initSample=displayInsTypeListMakeInsSample;
 | 
				
			||||||
 | 
					              if (i!=DIV_INS_AMIGA) e->song.ins[curIns]->amiga.useSample=true;
 | 
				
			||||||
 | 
					              nextWindow=GUI_WINDOW_INS_EDIT;
 | 
				
			||||||
 | 
					              wavePreviewInit=true;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            MARK_MODIFIED;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      ImGui::EndPopup();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // TODO:
 | 
					    // TODO:
 | 
				
			||||||
    // - multiple selection
 | 
					    // - multiple selection
 | 
				
			||||||
    // - replace instrument
 | 
					    // - replace instrument
 | 
				
			||||||
| 
						 | 
					@ -5020,6 +5065,7 @@ bool FurnaceGUI::init() {
 | 
				
			||||||
  channelsOpen=e->getConfBool("channelsOpen",false);
 | 
					  channelsOpen=e->getConfBool("channelsOpen",false);
 | 
				
			||||||
  patManagerOpen=e->getConfBool("patManagerOpen",false);
 | 
					  patManagerOpen=e->getConfBool("patManagerOpen",false);
 | 
				
			||||||
  sysManagerOpen=e->getConfBool("sysManagerOpen",false);
 | 
					  sysManagerOpen=e->getConfBool("sysManagerOpen",false);
 | 
				
			||||||
 | 
					  clockOpen=e->getConfBool("clockOpen",false);
 | 
				
			||||||
  regViewOpen=e->getConfBool("regViewOpen",false);
 | 
					  regViewOpen=e->getConfBool("regViewOpen",false);
 | 
				
			||||||
  logOpen=e->getConfBool("logOpen",false);
 | 
					  logOpen=e->getConfBool("logOpen",false);
 | 
				
			||||||
  effectListOpen=e->getConfBool("effectListOpen",false);
 | 
					  effectListOpen=e->getConfBool("effectListOpen",false);
 | 
				
			||||||
| 
						 | 
					@ -5333,15 +5379,10 @@ bool FurnaceGUI::init() {
 | 
				
			||||||
  return true;
 | 
					  return true;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool FurnaceGUI::finish() {
 | 
					void FurnaceGUI::commitState() {
 | 
				
			||||||
  if (!mobileUI) {
 | 
					  if (!mobileUI) {
 | 
				
			||||||
    ImGui::SaveIniSettingsToDisk(finalLayoutPath);
 | 
					    ImGui::SaveIniSettingsToDisk(finalLayoutPath);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  ImGui_ImplSDLRenderer_Shutdown();
 | 
					 | 
				
			||||||
  ImGui_ImplSDL2_Shutdown();
 | 
					 | 
				
			||||||
  ImGui::DestroyContext();
 | 
					 | 
				
			||||||
  SDL_DestroyRenderer(sdlRend);
 | 
					 | 
				
			||||||
  SDL_DestroyWindow(sdlWin);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  e->setConf("configVersion",(int)DIV_ENGINE_VERSION);
 | 
					  e->setConf("configVersion",(int)DIV_ENGINE_VERSION);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5383,6 +5424,7 @@ bool FurnaceGUI::finish() {
 | 
				
			||||||
  e->setConf("channelsOpen",channelsOpen);
 | 
					  e->setConf("channelsOpen",channelsOpen);
 | 
				
			||||||
  e->setConf("patManagerOpen",patManagerOpen);
 | 
					  e->setConf("patManagerOpen",patManagerOpen);
 | 
				
			||||||
  e->setConf("sysManagerOpen",sysManagerOpen);
 | 
					  e->setConf("sysManagerOpen",sysManagerOpen);
 | 
				
			||||||
 | 
					  e->setConf("clockOpen",clockOpen);
 | 
				
			||||||
  e->setConf("regViewOpen",regViewOpen);
 | 
					  e->setConf("regViewOpen",regViewOpen);
 | 
				
			||||||
  e->setConf("logOpen",logOpen);
 | 
					  e->setConf("logOpen",logOpen);
 | 
				
			||||||
  e->setConf("effectListOpen",effectListOpen);
 | 
					  e->setConf("effectListOpen",effectListOpen);
 | 
				
			||||||
| 
						 | 
					@ -5454,6 +5496,15 @@ bool FurnaceGUI::finish() {
 | 
				
			||||||
      e->setConf(key,recentFile[i]);
 | 
					      e->setConf(key,recentFile[i]);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool FurnaceGUI::finish() {
 | 
				
			||||||
 | 
					  commitState();
 | 
				
			||||||
 | 
					  ImGui_ImplSDLRenderer_Shutdown();
 | 
				
			||||||
 | 
					  ImGui_ImplSDL2_Shutdown();
 | 
				
			||||||
 | 
					  ImGui::DestroyContext();
 | 
				
			||||||
 | 
					  SDL_DestroyRenderer(sdlRend);
 | 
				
			||||||
 | 
					  SDL_DestroyWindow(sdlWin);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  for (int i=0; i<DIV_MAX_CHANS; i++) {
 | 
					  for (int i=0; i<DIV_MAX_CHANS; i++) {
 | 
				
			||||||
    delete oldPat[i];
 | 
					    delete oldPat[i];
 | 
				
			||||||
| 
						 | 
					@ -5485,6 +5536,7 @@ FurnaceGUI::FurnaceGUI():
 | 
				
			||||||
  zsmExportLoop(true),
 | 
					  zsmExportLoop(true),
 | 
				
			||||||
  vgmExportPatternHints(false),
 | 
					  vgmExportPatternHints(false),
 | 
				
			||||||
  vgmExportDirectStream(false),
 | 
					  vgmExportDirectStream(false),
 | 
				
			||||||
 | 
					  displayInsTypeList(false),
 | 
				
			||||||
  portrait(false),
 | 
					  portrait(false),
 | 
				
			||||||
  injectBackUp(false),
 | 
					  injectBackUp(false),
 | 
				
			||||||
  mobileMenuOpen(false),
 | 
					  mobileMenuOpen(false),
 | 
				
			||||||
| 
						 | 
					@ -5505,6 +5557,7 @@ FurnaceGUI::FurnaceGUI():
 | 
				
			||||||
  zsmExportTickRate(60),
 | 
					  zsmExportTickRate(60),
 | 
				
			||||||
  macroPointSize(16),
 | 
					  macroPointSize(16),
 | 
				
			||||||
  waveEditStyle(0),
 | 
					  waveEditStyle(0),
 | 
				
			||||||
 | 
					  displayInsTypeListMakeInsSample(-1),
 | 
				
			||||||
  mobileMenuPos(0.0f),
 | 
					  mobileMenuPos(0.0f),
 | 
				
			||||||
  autoButtonSize(0.0f),
 | 
					  autoButtonSize(0.0f),
 | 
				
			||||||
  curSysSection(NULL),
 | 
					  curSysSection(NULL),
 | 
				
			||||||
| 
						 | 
					@ -5571,6 +5624,8 @@ FurnaceGUI::FurnaceGUI():
 | 
				
			||||||
  dragSourceY(0),
 | 
					  dragSourceY(0),
 | 
				
			||||||
  dragDestinationX(0),
 | 
					  dragDestinationX(0),
 | 
				
			||||||
  dragDestinationY(0),
 | 
					  dragDestinationY(0),
 | 
				
			||||||
 | 
					  oldBeat(-1),
 | 
				
			||||||
 | 
					  oldBar(-1),
 | 
				
			||||||
  exportFadeOut(5.0),
 | 
					  exportFadeOut(5.0),
 | 
				
			||||||
  editControlsOpen(true),
 | 
					  editControlsOpen(true),
 | 
				
			||||||
  ordersOpen(true),
 | 
					  ordersOpen(true),
 | 
				
			||||||
| 
						 | 
					@ -5603,6 +5658,12 @@ FurnaceGUI::FurnaceGUI():
 | 
				
			||||||
  spoilerOpen(false),
 | 
					  spoilerOpen(false),
 | 
				
			||||||
  patManagerOpen(false),
 | 
					  patManagerOpen(false),
 | 
				
			||||||
  sysManagerOpen(false),
 | 
					  sysManagerOpen(false),
 | 
				
			||||||
 | 
					  clockOpen(false),
 | 
				
			||||||
 | 
					  clockShowReal(true),
 | 
				
			||||||
 | 
					  clockShowRow(true),
 | 
				
			||||||
 | 
					  clockShowBeat(true),
 | 
				
			||||||
 | 
					  clockShowMetro(true),
 | 
				
			||||||
 | 
					  clockShowTime(true),
 | 
				
			||||||
  selecting(false),
 | 
					  selecting(false),
 | 
				
			||||||
  selectingFull(false),
 | 
					  selectingFull(false),
 | 
				
			||||||
  dragging(false),
 | 
					  dragging(false),
 | 
				
			||||||
| 
						 | 
					@ -5631,7 +5692,6 @@ FurnaceGUI::FurnaceGUI():
 | 
				
			||||||
  curWindowLast(GUI_WINDOW_NOTHING),
 | 
					  curWindowLast(GUI_WINDOW_NOTHING),
 | 
				
			||||||
  curWindowThreadSafe(GUI_WINDOW_NOTHING),
 | 
					  curWindowThreadSafe(GUI_WINDOW_NOTHING),
 | 
				
			||||||
  lastPatternWidth(0.0f),
 | 
					  lastPatternWidth(0.0f),
 | 
				
			||||||
  nextDesc(NULL),
 | 
					 | 
				
			||||||
  latchNote(-1),
 | 
					  latchNote(-1),
 | 
				
			||||||
  latchIns(-2),
 | 
					  latchIns(-2),
 | 
				
			||||||
  latchVol(-1),
 | 
					  latchVol(-1),
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -243,6 +243,10 @@ enum FurnaceGUIColors {
 | 
				
			||||||
  GUI_COLOR_PIANO_KEY_BOTTOM_ACTIVE,
 | 
					  GUI_COLOR_PIANO_KEY_BOTTOM_ACTIVE,
 | 
				
			||||||
  GUI_COLOR_PIANO_KEY_TOP_ACTIVE,
 | 
					  GUI_COLOR_PIANO_KEY_TOP_ACTIVE,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  GUI_COLOR_CLOCK_TEXT,
 | 
				
			||||||
 | 
					  GUI_COLOR_CLOCK_BEAT_LOW,
 | 
				
			||||||
 | 
					  GUI_COLOR_CLOCK_BEAT_HIGH,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  GUI_COLOR_LOGLEVEL_ERROR,
 | 
					  GUI_COLOR_LOGLEVEL_ERROR,
 | 
				
			||||||
  GUI_COLOR_LOGLEVEL_WARNING,
 | 
					  GUI_COLOR_LOGLEVEL_WARNING,
 | 
				
			||||||
  GUI_COLOR_LOGLEVEL_INFO,
 | 
					  GUI_COLOR_LOGLEVEL_INFO,
 | 
				
			||||||
| 
						 | 
					@ -285,6 +289,7 @@ enum FurnaceGUIWindows {
 | 
				
			||||||
  GUI_WINDOW_CHAN_OSC,
 | 
					  GUI_WINDOW_CHAN_OSC,
 | 
				
			||||||
  GUI_WINDOW_SUBSONGS,
 | 
					  GUI_WINDOW_SUBSONGS,
 | 
				
			||||||
  GUI_WINDOW_FIND,
 | 
					  GUI_WINDOW_FIND,
 | 
				
			||||||
 | 
					  GUI_WINDOW_CLOCK,
 | 
				
			||||||
  GUI_WINDOW_SPOILER
 | 
					  GUI_WINDOW_SPOILER
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -425,6 +430,7 @@ enum FurnaceGUIActions {
 | 
				
			||||||
  GUI_ACTION_WINDOW_CHAN_OSC,
 | 
					  GUI_ACTION_WINDOW_CHAN_OSC,
 | 
				
			||||||
  GUI_ACTION_WINDOW_SUBSONGS,
 | 
					  GUI_ACTION_WINDOW_SUBSONGS,
 | 
				
			||||||
  GUI_ACTION_WINDOW_FIND,
 | 
					  GUI_ACTION_WINDOW_FIND,
 | 
				
			||||||
 | 
					  GUI_ACTION_WINDOW_CLOCK,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  GUI_ACTION_COLLAPSE_WINDOW,
 | 
					  GUI_ACTION_COLLAPSE_WINDOW,
 | 
				
			||||||
  GUI_ACTION_CLOSE_WINDOW,
 | 
					  GUI_ACTION_CLOSE_WINDOW,
 | 
				
			||||||
| 
						 | 
					@ -893,12 +899,22 @@ struct Gradient2D {
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct FurnaceGUISysDefChip {
 | 
				
			||||||
 | 
					  DivSystem sys;
 | 
				
			||||||
 | 
					  int vol, pan;
 | 
				
			||||||
 | 
					  const char* flags;
 | 
				
			||||||
 | 
					  FurnaceGUISysDefChip(DivSystem s, int v, int p, const char* f):
 | 
				
			||||||
 | 
					    sys(s),
 | 
				
			||||||
 | 
					    vol(v),
 | 
				
			||||||
 | 
					    pan(p),
 | 
				
			||||||
 | 
					    flags(f) {}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct FurnaceGUISysDef {
 | 
					struct FurnaceGUISysDef {
 | 
				
			||||||
  const char* name;
 | 
					  const char* name;
 | 
				
			||||||
  std::vector<int> definition;
 | 
					  String definition;
 | 
				
			||||||
  FurnaceGUISysDef(const char* n, std::initializer_list<int> def):
 | 
					  FurnaceGUISysDef(const char* n, std::initializer_list<int> def);
 | 
				
			||||||
    name(n), definition(def) {
 | 
					  FurnaceGUISysDef(const char* n, std::initializer_list<FurnaceGUISysDefChip> def);
 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct FurnaceGUISysCategory {
 | 
					struct FurnaceGUISysCategory {
 | 
				
			||||||
| 
						 | 
					@ -945,6 +961,12 @@ struct FurnaceGUIMacroDesc {
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct FurnaceGUIMacroEditState {
 | 
				
			||||||
 | 
					  int selectedMacro;
 | 
				
			||||||
 | 
					  FurnaceGUIMacroEditState():
 | 
				
			||||||
 | 
					    selectedMacro(0) {}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
enum FurnaceGUIFindQueryModes {
 | 
					enum FurnaceGUIFindQueryModes {
 | 
				
			||||||
  GUI_QUERY_IGNORE=0,
 | 
					  GUI_QUERY_IGNORE=0,
 | 
				
			||||||
  GUI_QUERY_MATCH,
 | 
					  GUI_QUERY_MATCH,
 | 
				
			||||||
| 
						 | 
					@ -1033,9 +1055,11 @@ class FurnaceGUI {
 | 
				
			||||||
  std::vector<DivSystem> sysSearchResults;
 | 
					  std::vector<DivSystem> sysSearchResults;
 | 
				
			||||||
  std::vector<FurnaceGUISysDef> newSongSearchResults;
 | 
					  std::vector<FurnaceGUISysDef> newSongSearchResults;
 | 
				
			||||||
  std::deque<String> recentFile;
 | 
					  std::deque<String> recentFile;
 | 
				
			||||||
 | 
					  std::vector<DivInstrumentType> makeInsTypeList;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  bool quit, warnQuit, willCommit, edit, modified, displayError, displayExporting, vgmExportLoop, zsmExportLoop, vgmExportPatternHints;
 | 
					  bool quit, warnQuit, willCommit, edit, modified, displayError, displayExporting, vgmExportLoop, zsmExportLoop, vgmExportPatternHints;
 | 
				
			||||||
  bool vgmExportDirectStream;
 | 
					  bool vgmExportDirectStream, displayInsTypeList;
 | 
				
			||||||
  bool portrait, injectBackUp, mobileMenuOpen;
 | 
					  bool portrait, injectBackUp, mobileMenuOpen;
 | 
				
			||||||
  bool wantCaptureKeyboard, oldWantCaptureKeyboard, displayMacroMenu;
 | 
					  bool wantCaptureKeyboard, oldWantCaptureKeyboard, displayMacroMenu;
 | 
				
			||||||
  bool displayNew, fullScreen, preserveChanPos, wantScrollList, noteInputPoly;
 | 
					  bool displayNew, fullScreen, preserveChanPos, wantScrollList, noteInputPoly;
 | 
				
			||||||
| 
						 | 
					@ -1046,6 +1070,7 @@ class FurnaceGUI {
 | 
				
			||||||
  int zsmExportTickRate;
 | 
					  int zsmExportTickRate;
 | 
				
			||||||
  int macroPointSize;
 | 
					  int macroPointSize;
 | 
				
			||||||
  int waveEditStyle;
 | 
					  int waveEditStyle;
 | 
				
			||||||
 | 
					  int displayInsTypeListMakeInsSample;
 | 
				
			||||||
  float mobileMenuPos, autoButtonSize;
 | 
					  float mobileMenuPos, autoButtonSize;
 | 
				
			||||||
  const int* curSysSection;
 | 
					  const int* curSysSection;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1216,6 +1241,7 @@ class FurnaceGUI {
 | 
				
			||||||
    int persistFadeOut;
 | 
					    int persistFadeOut;
 | 
				
			||||||
    int exportLoops;
 | 
					    int exportLoops;
 | 
				
			||||||
    double exportFadeOut;
 | 
					    double exportFadeOut;
 | 
				
			||||||
 | 
					    int macroLayout;
 | 
				
			||||||
    unsigned int maxUndoSteps;
 | 
					    unsigned int maxUndoSteps;
 | 
				
			||||||
    String mainFontPath;
 | 
					    String mainFontPath;
 | 
				
			||||||
    String patFontPath;
 | 
					    String patFontPath;
 | 
				
			||||||
| 
						 | 
					@ -1346,6 +1372,7 @@ class FurnaceGUI {
 | 
				
			||||||
      persistFadeOut(1),
 | 
					      persistFadeOut(1),
 | 
				
			||||||
      exportLoops(0),
 | 
					      exportLoops(0),
 | 
				
			||||||
      exportFadeOut(0.0),
 | 
					      exportFadeOut(0.0),
 | 
				
			||||||
 | 
					      macroLayout(0),
 | 
				
			||||||
      maxUndoSteps(100),
 | 
					      maxUndoSteps(100),
 | 
				
			||||||
      mainFontPath(""),
 | 
					      mainFontPath(""),
 | 
				
			||||||
      patFontPath(""),
 | 
					      patFontPath(""),
 | 
				
			||||||
| 
						 | 
					@ -1367,7 +1394,7 @@ class FurnaceGUI {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  int curIns, curWave, curSample, curOctave, curOrder, prevIns, oldRow, oldOrder, oldOrder1, editStep, exportLoops, soloChan, soloTimeout, orderEditMode, orderCursor;
 | 
					  int curIns, curWave, curSample, curOctave, curOrder, prevIns, oldRow, oldOrder, oldOrder1, editStep, exportLoops, soloChan, soloTimeout, orderEditMode, orderCursor;
 | 
				
			||||||
  int loopOrder, loopRow, loopEnd, isClipping, extraChannelButtons, patNameTarget, newSongCategory, latchTarget;
 | 
					  int loopOrder, loopRow, loopEnd, isClipping, extraChannelButtons, patNameTarget, newSongCategory, latchTarget;
 | 
				
			||||||
  int wheelX, wheelY, dragSourceX, dragSourceY, dragDestinationX, dragDestinationY;
 | 
					  int wheelX, wheelY, dragSourceX, dragSourceY, dragDestinationX, dragDestinationY, oldBeat, oldBar;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  double exportFadeOut;
 | 
					  double exportFadeOut;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1375,7 +1402,10 @@ class FurnaceGUI {
 | 
				
			||||||
  bool waveListOpen, waveEditOpen, sampleListOpen, sampleEditOpen, aboutOpen, settingsOpen;
 | 
					  bool waveListOpen, waveEditOpen, sampleListOpen, sampleEditOpen, aboutOpen, settingsOpen;
 | 
				
			||||||
  bool mixerOpen, debugOpen, inspectorOpen, oscOpen, volMeterOpen, statsOpen, compatFlagsOpen;
 | 
					  bool mixerOpen, debugOpen, inspectorOpen, oscOpen, volMeterOpen, statsOpen, compatFlagsOpen;
 | 
				
			||||||
  bool pianoOpen, notesOpen, channelsOpen, regViewOpen, logOpen, effectListOpen, chanOscOpen;
 | 
					  bool pianoOpen, notesOpen, channelsOpen, regViewOpen, logOpen, effectListOpen, chanOscOpen;
 | 
				
			||||||
  bool subSongsOpen, findOpen, spoilerOpen, patManagerOpen, sysManagerOpen;
 | 
					  bool subSongsOpen, findOpen, spoilerOpen, patManagerOpen, sysManagerOpen, clockOpen;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  bool clockShowReal, clockShowRow, clockShowBeat, clockShowMetro, clockShowTime;
 | 
				
			||||||
 | 
					  float clockMetroTick[16];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  SelectionPoint selStart, selEnd, cursor, cursorDrag, dragStart, dragEnd;
 | 
					  SelectionPoint selStart, selEnd, cursor, cursorDrag, dragStart, dragEnd;
 | 
				
			||||||
  bool selecting, selectingFull, dragging, curNibble, orderNibble, followOrders, followPattern, changeAllOrders, mobileUI;
 | 
					  bool selecting, selectingFull, dragging, curNibble, orderNibble, followOrders, followPattern, changeAllOrders, mobileUI;
 | 
				
			||||||
| 
						 | 
					@ -1387,7 +1417,7 @@ class FurnaceGUI {
 | 
				
			||||||
  float patChanX[DIV_MAX_CHANS+1];
 | 
					  float patChanX[DIV_MAX_CHANS+1];
 | 
				
			||||||
  float patChanSlideY[DIV_MAX_CHANS+1];
 | 
					  float patChanSlideY[DIV_MAX_CHANS+1];
 | 
				
			||||||
  float lastPatternWidth;
 | 
					  float lastPatternWidth;
 | 
				
			||||||
  const int* nextDesc;
 | 
					  String nextDesc;
 | 
				
			||||||
  String nextDescName;
 | 
					  String nextDescName;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  OperationMask opMaskDelete, opMaskPullDelete, opMaskInsert, opMaskPaste, opMaskTransposeNote, opMaskTransposeValue;
 | 
					  OperationMask opMaskDelete, opMaskPullDelete, opMaskInsert, opMaskPaste, opMaskTransposeNote, opMaskTransposeValue;
 | 
				
			||||||
| 
						 | 
					@ -1511,6 +1541,8 @@ class FurnaceGUI {
 | 
				
			||||||
  int macroLoopDragLen;
 | 
					  int macroLoopDragLen;
 | 
				
			||||||
  bool macroLoopDragActive;
 | 
					  bool macroLoopDragActive;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  FurnaceGUIMacroEditState macroEditStateFM, macroEditStateOP[4], macroEditStateMacros;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ImVec2 waveDragStart;
 | 
					  ImVec2 waveDragStart;
 | 
				
			||||||
  ImVec2 waveDragAreaSize;
 | 
					  ImVec2 waveDragAreaSize;
 | 
				
			||||||
  int* waveDragTarget;
 | 
					  int* waveDragTarget;
 | 
				
			||||||
| 
						 | 
					@ -1675,7 +1707,8 @@ class FurnaceGUI {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  void patternRow(int i, bool isPlaying, float lineHeight, int chans, int ord, const DivPattern** patCache, bool inhibitSel);
 | 
					  void patternRow(int i, bool isPlaying, float lineHeight, int chans, int ord, const DivPattern** patCache, bool inhibitSel);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  void drawMacros(std::vector<FurnaceGUIMacroDesc>& macros);
 | 
					  void drawMacroEdit(FurnaceGUIMacroDesc& i, int totalFit, float availableWidth, int index);
 | 
				
			||||||
 | 
					  void drawMacros(std::vector<FurnaceGUIMacroDesc>& macros, FurnaceGUIMacroEditState& state);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  void actualWaveList();
 | 
					  void actualWaveList();
 | 
				
			||||||
  void actualSampleList();
 | 
					  void actualSampleList();
 | 
				
			||||||
| 
						 | 
					@ -1717,6 +1750,7 @@ class FurnaceGUI {
 | 
				
			||||||
  void drawSubSongs();
 | 
					  void drawSubSongs();
 | 
				
			||||||
  void drawFindReplace();
 | 
					  void drawFindReplace();
 | 
				
			||||||
  void drawSpoiler();
 | 
					  void drawSpoiler();
 | 
				
			||||||
 | 
					  void drawClock();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  void parseKeybinds();
 | 
					  void parseKeybinds();
 | 
				
			||||||
  void promptKey(int which);
 | 
					  void promptKey(int which);
 | 
				
			||||||
| 
						 | 
					@ -1736,6 +1770,7 @@ class FurnaceGUI {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  void syncSettings();
 | 
					  void syncSettings();
 | 
				
			||||||
  void commitSettings();
 | 
					  void commitSettings();
 | 
				
			||||||
 | 
					  void commitState();
 | 
				
			||||||
  void processDrags(int dragX, int dragY);
 | 
					  void processDrags(int dragX, int dragY);
 | 
				
			||||||
  void processPoint(SDL_Event& ev);
 | 
					  void processPoint(SDL_Event& ev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -513,6 +513,7 @@ const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={
 | 
				
			||||||
  D("WINDOW_CHAN_OSC", "Oscilloscope (per-channel)", 0),
 | 
					  D("WINDOW_CHAN_OSC", "Oscilloscope (per-channel)", 0),
 | 
				
			||||||
  D("WINDOW_SUBSONGS", "Subsongs", 0),
 | 
					  D("WINDOW_SUBSONGS", "Subsongs", 0),
 | 
				
			||||||
  D("WINDOW_FIND", "Find/Replace", FURKMOD_CMD|SDLK_f),
 | 
					  D("WINDOW_FIND", "Find/Replace", FURKMOD_CMD|SDLK_f),
 | 
				
			||||||
 | 
					  D("WINDOW_CLOCK", "Clock", 0),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  D("COLLAPSE_WINDOW", "Collapse/expand current window", 0),
 | 
					  D("COLLAPSE_WINDOW", "Collapse/expand current window", 0),
 | 
				
			||||||
  D("CLOSE_WINDOW", "Close current window", FURKMOD_SHIFT|SDLK_ESCAPE),
 | 
					  D("CLOSE_WINDOW", "Close current window", FURKMOD_SHIFT|SDLK_ESCAPE),
 | 
				
			||||||
| 
						 | 
					@ -874,6 +875,10 @@ const FurnaceGUIColorDef guiColors[GUI_COLOR_MAX]={
 | 
				
			||||||
  D(GUI_COLOR_PIANO_KEY_BOTTOM_ACTIVE,"",ImVec4(0.5f,0.5f,0.5f,1.0f)),
 | 
					  D(GUI_COLOR_PIANO_KEY_BOTTOM_ACTIVE,"",ImVec4(0.5f,0.5f,0.5f,1.0f)),
 | 
				
			||||||
  D(GUI_COLOR_PIANO_KEY_TOP_ACTIVE,"",ImVec4(0.4f,0.4f,0.4f,1.0f)),
 | 
					  D(GUI_COLOR_PIANO_KEY_TOP_ACTIVE,"",ImVec4(0.4f,0.4f,0.4f,1.0f)),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  D(GUI_COLOR_CLOCK_TEXT,"",ImVec4(1.0f,1.0f,1.0f,1.0f)),
 | 
				
			||||||
 | 
					  D(GUI_COLOR_CLOCK_BEAT_LOW,"",ImVec4(0.1f,0.13f,0.25f,1.0f)),
 | 
				
			||||||
 | 
					  D(GUI_COLOR_CLOCK_BEAT_HIGH,"",ImVec4(0.5f,0.8f,1.0f,1.0f)),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  D(GUI_COLOR_LOGLEVEL_ERROR,"",ImVec4(1.0f,0.2f,0.2f,1.0f)),
 | 
					  D(GUI_COLOR_LOGLEVEL_ERROR,"",ImVec4(1.0f,0.2f,0.2f,1.0f)),
 | 
				
			||||||
  D(GUI_COLOR_LOGLEVEL_WARNING,"",ImVec4(1.0f,1.0f,0.2f,1.0f)),
 | 
					  D(GUI_COLOR_LOGLEVEL_WARNING,"",ImVec4(1.0f,1.0f,0.2f,1.0f)),
 | 
				
			||||||
  D(GUI_COLOR_LOGLEVEL_INFO,"",ImVec4(0.4f,1.0f,0.4f,1.0f)),
 | 
					  D(GUI_COLOR_LOGLEVEL_INFO,"",ImVec4(0.4f,1.0f,0.4f,1.0f)),
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										1120
									
								
								src/gui/insEdit.cpp
									
									
									
									
									
								
							
							
						
						
									
										1120
									
								
								src/gui/insEdit.cpp
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							| 
						 | 
					@ -107,7 +107,7 @@ void FurnaceGUI::drawNewSong() {
 | 
				
			||||||
          ImGui::TableNextRow();
 | 
					          ImGui::TableNextRow();
 | 
				
			||||||
          ImGui::TableNextColumn();
 | 
					          ImGui::TableNextColumn();
 | 
				
			||||||
          if (ImGui::Selectable(i.name,false,ImGuiSelectableFlags_DontClosePopups)) {
 | 
					          if (ImGui::Selectable(i.name,false,ImGuiSelectableFlags_DontClosePopups)) {
 | 
				
			||||||
            nextDesc=i.definition.data();
 | 
					            nextDesc=i.definition;
 | 
				
			||||||
            nextDescName=i.name;
 | 
					            nextDescName=i.name;
 | 
				
			||||||
            accepted=true;
 | 
					            accepted=true;
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
| 
						 | 
					@ -129,7 +129,7 @@ void FurnaceGUI::drawNewSong() {
 | 
				
			||||||
        ImGui::CloseCurrentPopup();
 | 
					        ImGui::CloseCurrentPopup();
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        unsigned int selection=rand()%newSystemCat->systems.size();
 | 
					        unsigned int selection=rand()%newSystemCat->systems.size();
 | 
				
			||||||
        nextDesc=newSystemCat->systems[selection].definition.data();
 | 
					        nextDesc=newSystemCat->systems[selection].definition;
 | 
				
			||||||
        nextDescName=newSystemCat->systems[selection].name;
 | 
					        nextDescName=newSystemCat->systems[selection].name;
 | 
				
			||||||
        accepted=true;
 | 
					        accepted=true;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
| 
						 | 
					@ -143,16 +143,7 @@ void FurnaceGUI::drawNewSong() {
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (accepted) {
 | 
					  if (accepted) {
 | 
				
			||||||
    // TODO: remove after porting all presets to new format
 | 
					    e->createNew(nextDesc.c_str(),nextDescName,false);
 | 
				
			||||||
    String oldDescFormat;
 | 
					 | 
				
			||||||
    for (const int* i=nextDesc; *i; i+=4) {
 | 
					 | 
				
			||||||
      oldDescFormat+=fmt::sprintf("%d ",e->systemToFileFur((DivSystem)i[0]));
 | 
					 | 
				
			||||||
      oldDescFormat+=fmt::sprintf("%d ",i[1]);
 | 
					 | 
				
			||||||
      oldDescFormat+=fmt::sprintf("%d ",i[2]);
 | 
					 | 
				
			||||||
      oldDescFormat+=fmt::sprintf("%d ",i[3]);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    String oldDesc=e->decodeSysDesc(oldDescFormat.c_str());
 | 
					 | 
				
			||||||
    e->createNew(oldDesc.c_str(),nextDescName);
 | 
					 | 
				
			||||||
    undoHist.clear();
 | 
					    undoHist.clear();
 | 
				
			||||||
    redoHist.clear();
 | 
					    redoHist.clear();
 | 
				
			||||||
    curFileName="";
 | 
					    curFileName="";
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -219,11 +219,14 @@ void FurnaceGUI::drawPiano() {
 | 
				
			||||||
        // render piano
 | 
					        // render piano
 | 
				
			||||||
        //ImGui::ItemSize(size,ImGui::GetStyle().FramePadding.y);
 | 
					        //ImGui::ItemSize(size,ImGui::GetStyle().FramePadding.y);
 | 
				
			||||||
        if (ImGui::ItemAdd(rect,ImGui::GetID("pianoDisplay"))) {
 | 
					        if (ImGui::ItemAdd(rect,ImGui::GetID("pianoDisplay"))) {
 | 
				
			||||||
          ImGui::ItemHoverable(rect,ImGui::GetID("pianoDisplay"));
 | 
					          bool canInput=false;
 | 
				
			||||||
 | 
					          if (ImGui::ItemHoverable(rect,ImGui::GetID("pianoDisplay"))) {
 | 
				
			||||||
 | 
					            canInput=true;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
          if (view) {
 | 
					          if (view) {
 | 
				
			||||||
            int notes=oct*12;
 | 
					            int notes=oct*12;
 | 
				
			||||||
            // evaluate input
 | 
					            // evaluate input
 | 
				
			||||||
            for (TouchPoint& i: activePoints) {
 | 
					            if (canInput) for (TouchPoint& i: activePoints) {
 | 
				
			||||||
              if (rect.Contains(ImVec2(i.x,i.y))) {
 | 
					              if (rect.Contains(ImVec2(i.x,i.y))) {
 | 
				
			||||||
                int note=(((i.x-rect.Min.x)/(rect.Max.x-rect.Min.x))*notes)+12*off;
 | 
					                int note=(((i.x-rect.Min.x)/(rect.Max.x-rect.Min.x))*notes)+12*off;
 | 
				
			||||||
                if (note<0) continue;
 | 
					                if (note<0) continue;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										2338
									
								
								src/gui/presets.cpp
									
									
									
									
									
								
							
							
						
						
									
										2338
									
								
								src/gui/presets.cpp
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							| 
						 | 
					@ -197,10 +197,12 @@ double getScaleFactor(const char* driverHint) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // SDL fallback
 | 
					  // SDL fallback
 | 
				
			||||||
#ifdef ANDROID
 | 
					#ifdef ANDROID
 | 
				
			||||||
  float dpiScaleF=192.0f;
 | 
					  float dpiScaleF=160.0f;
 | 
				
			||||||
  if (SDL_GetDisplayDPI(0,&dpiScaleF,NULL,NULL)==0) {
 | 
					  if (SDL_GetDisplayDPI(0,&dpiScaleF,NULL,NULL)==0) {
 | 
				
			||||||
    ret=round(dpiScaleF/192.0f);
 | 
					    ret=dpiScaleF/160.0f;
 | 
				
			||||||
    if (ret<1) ret=1;
 | 
					    if (ret<1) ret=1;
 | 
				
			||||||
 | 
					    logI("dpiScaleF: %f",dpiScaleF);
 | 
				
			||||||
 | 
					    logI("ret: %f",ret);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#else
 | 
					#else
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -33,14 +33,14 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define DEFAULT_NOTE_KEYS "5:7;6:4;7:3;8:16;10:6;11:8;12:24;13:10;16:11;17:9;18:26;19:28;20:12;21:17;22:1;23:19;24:23;25:5;26:14;27:2;28:21;29:0;30:100;31:13;32:15;34:18;35:20;36:22;38:25;39:27;43:100;46:101;47:29;48:31;53:102;"
 | 
					#define DEFAULT_NOTE_KEYS "5:7;6:4;7:3;8:16;10:6;11:8;12:24;13:10;16:11;17:9;18:26;19:28;20:12;21:17;22:1;23:19;24:23;25:5;26:14;27:2;28:21;29:0;30:100;31:13;32:15;34:18;35:20;36:22;38:25;39:27;43:100;46:101;47:29;48:31;53:102;"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#if defined(_WIN32) || defined(__APPLE__)
 | 
					#if defined(_WIN32) || defined(__APPLE__) || defined(IS_MOBILE)
 | 
				
			||||||
#define POWER_SAVE_DEFAULT 1
 | 
					#define POWER_SAVE_DEFAULT 1
 | 
				
			||||||
#else
 | 
					#else
 | 
				
			||||||
// currently off on Linux/other due to Mesa catch-up behavior.
 | 
					// currently off on Linux/other due to Mesa catch-up behavior.
 | 
				
			||||||
#define POWER_SAVE_DEFAULT 0
 | 
					#define POWER_SAVE_DEFAULT 0
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#if defined(__HAIKU__)
 | 
					#if defined(__HAIKU__) || defined(IS_MOBILE)
 | 
				
			||||||
// NFD doesn't support Haiku
 | 
					// NFD doesn't support Haiku
 | 
				
			||||||
#define SYS_FILE_DIALOG_DEFAULT 0
 | 
					#define SYS_FILE_DIALOG_DEFAULT 0
 | 
				
			||||||
#else
 | 
					#else
 | 
				
			||||||
| 
						 | 
					@ -239,6 +239,12 @@ void FurnaceGUI::drawSettings() {
 | 
				
			||||||
    nextWindow=GUI_WINDOW_NOTHING;
 | 
					    nextWindow=GUI_WINDOW_NOTHING;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  if (!settingsOpen) return;
 | 
					  if (!settingsOpen) return;
 | 
				
			||||||
 | 
					  if (mobileUI) {
 | 
				
			||||||
 | 
					    ImVec2 setWindowPos=ImVec2(0,0);
 | 
				
			||||||
 | 
					    ImVec2 setWindowSize=ImVec2(canvasW,canvasH);
 | 
				
			||||||
 | 
					    ImGui::SetNextWindowPos(setWindowPos);
 | 
				
			||||||
 | 
					    ImGui::SetNextWindowSize(setWindowSize);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
  if (ImGui::Begin("Settings",&settingsOpen,ImGuiWindowFlags_NoDocking|globalWinFlags)) {
 | 
					  if (ImGui::Begin("Settings",&settingsOpen,ImGuiWindowFlags_NoDocking|globalWinFlags)) {
 | 
				
			||||||
    if (!settingsOpen) {
 | 
					    if (!settingsOpen) {
 | 
				
			||||||
      settingsOpen=true;
 | 
					      settingsOpen=true;
 | 
				
			||||||
| 
						 | 
					@ -332,7 +338,7 @@ void FurnaceGUI::drawSettings() {
 | 
				
			||||||
            settings.initialSys.set("pan0",0);
 | 
					            settings.initialSys.set("pan0",0);
 | 
				
			||||||
            settings.initialSys.set("flags0","");
 | 
					            settings.initialSys.set("flags0","");
 | 
				
			||||||
            settings.initialSys.set("id1",e->systemToFileFur(DIV_SYSTEM_SMS));
 | 
					            settings.initialSys.set("id1",e->systemToFileFur(DIV_SYSTEM_SMS));
 | 
				
			||||||
            settings.initialSys.set("vol1",64);
 | 
					            settings.initialSys.set("vol1",32);
 | 
				
			||||||
            settings.initialSys.set("pan1",0);
 | 
					            settings.initialSys.set("pan1",0);
 | 
				
			||||||
            settings.initialSys.set("flags1","");
 | 
					            settings.initialSys.set("flags1","");
 | 
				
			||||||
            settings.initialSysName="Sega Genesis/Mega Drive";
 | 
					            settings.initialSysName="Sega Genesis/Mega Drive";
 | 
				
			||||||
| 
						 | 
					@ -343,11 +349,15 @@ void FurnaceGUI::drawSettings() {
 | 
				
			||||||
          ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
 | 
					          ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
 | 
				
			||||||
          ImGui::InputText("##InitSysName",&settings.initialSysName);
 | 
					          ImGui::InputText("##InitSysName",&settings.initialSysName);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          int sysCount=0;
 | 
				
			||||||
 | 
					          int doRemove=-1;
 | 
				
			||||||
          for (size_t i=0; settings.initialSys.getInt(fmt::sprintf("id%d",i),0); i++) {
 | 
					          for (size_t i=0; settings.initialSys.getInt(fmt::sprintf("id%d",i),0); i++) {
 | 
				
			||||||
            DivSystem sysID=e->systemFromFileFur(settings.initialSys.getInt(fmt::sprintf("id%d",i),0));
 | 
					            DivSystem sysID=e->systemFromFileFur(settings.initialSys.getInt(fmt::sprintf("id%d",i),0));
 | 
				
			||||||
            signed char sysVol=settings.initialSys.getInt(fmt::sprintf("vol%d",i),0);
 | 
					            signed char sysVol=settings.initialSys.getInt(fmt::sprintf("vol%d",i),0);
 | 
				
			||||||
            signed char sysPan=settings.initialSys.getInt(fmt::sprintf("pan%d",i),0);
 | 
					            signed char sysPan=settings.initialSys.getInt(fmt::sprintf("pan%d",i),0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            sysCount=i+1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            //bool doRemove=false;
 | 
					            //bool doRemove=false;
 | 
				
			||||||
            bool doInvert=sysVol&128;
 | 
					            bool doInvert=sysVol&128;
 | 
				
			||||||
            signed char vol=sysVol&127;
 | 
					            signed char vol=sysVol&127;
 | 
				
			||||||
| 
						 | 
					@ -373,7 +383,7 @@ void FurnaceGUI::drawSettings() {
 | 
				
			||||||
            ImGui::SameLine();
 | 
					            ImGui::SameLine();
 | 
				
			||||||
            //ImGui::BeginDisabled(settings.initialSys.size()<=4);
 | 
					            //ImGui::BeginDisabled(settings.initialSys.size()<=4);
 | 
				
			||||||
            if (ImGui::Button(ICON_FA_MINUS "##InitSysRemove")) {
 | 
					            if (ImGui::Button(ICON_FA_MINUS "##InitSysRemove")) {
 | 
				
			||||||
              //doRemove=true;
 | 
					              doRemove=i;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            //ImGui::EndDisabled();
 | 
					            //ImGui::EndDisabled();
 | 
				
			||||||
            ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x-(50.0f*dpiScale));
 | 
					            ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x-(50.0f*dpiScale));
 | 
				
			||||||
| 
						 | 
					@ -398,17 +408,31 @@ void FurnaceGUI::drawSettings() {
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            ImGui::PopID();
 | 
					            ImGui::PopID();
 | 
				
			||||||
            /*if (doRemove && settings.initialSys.size()>=8) {
 | 
					 | 
				
			||||||
              settings.initialSys.erase(settings.initialSys.begin()+i,settings.initialSys.begin()+i+4);
 | 
					 | 
				
			||||||
              i-=4;
 | 
					 | 
				
			||||||
            }*/
 | 
					 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          if (ImGui::Button(ICON_FA_PLUS "##InitSysAdd")) {
 | 
					          if (doRemove>=0 && sysCount>1) {
 | 
				
			||||||
            /*settings.initialSys.push_back(DIV_SYSTEM_YM2612);
 | 
					            for (int i=doRemove; i<sysCount-1; i++) {
 | 
				
			||||||
            settings.initialSys.push_back(64);
 | 
					              int sysID=settings.initialSys.getInt(fmt::sprintf("id%d",i+1),0);
 | 
				
			||||||
            settings.initialSys.push_back(0);
 | 
					              int sysVol=settings.initialSys.getInt(fmt::sprintf("vol%d",i+1),0);
 | 
				
			||||||
            settings.initialSys.push_back(0);*/
 | 
					              int sysPan=settings.initialSys.getInt(fmt::sprintf("pan%d",i+1),0);
 | 
				
			||||||
 | 
					              String sysFlags=settings.initialSys.getString(fmt::sprintf("flags%d",i+1),"");
 | 
				
			||||||
 | 
					              settings.initialSys.set(fmt::sprintf("id%d",i),sysID);
 | 
				
			||||||
 | 
					              settings.initialSys.set(fmt::sprintf("vol%d",i),sysVol);
 | 
				
			||||||
 | 
					              settings.initialSys.set(fmt::sprintf("pan%d",i),sysPan);
 | 
				
			||||||
 | 
					              settings.initialSys.set(fmt::sprintf("flags%d",i),sysFlags);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            settings.initialSys.remove(fmt::sprintf("id%d",sysCount-1));
 | 
				
			||||||
 | 
					            settings.initialSys.remove(fmt::sprintf("vol%d",sysCount-1));
 | 
				
			||||||
 | 
					            settings.initialSys.remove(fmt::sprintf("pan%d",sysCount-1));
 | 
				
			||||||
 | 
					            settings.initialSys.remove(fmt::sprintf("flags%d",sysCount-1));
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          if (sysCount<32) if (ImGui::Button(ICON_FA_PLUS "##InitSysAdd")) {
 | 
				
			||||||
 | 
					            settings.initialSys.set(fmt::sprintf("id%d",sysCount),(int)e->systemToFileFur(DIV_SYSTEM_YM2612));
 | 
				
			||||||
 | 
					            settings.initialSys.set(fmt::sprintf("vol%d",sysCount),64);
 | 
				
			||||||
 | 
					            settings.initialSys.set(fmt::sprintf("pan%d",sysCount),0);
 | 
				
			||||||
 | 
					            settings.initialSys.set(fmt::sprintf("flags%d",sysCount),"");
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          ImGui::Separator();
 | 
					          ImGui::Separator();
 | 
				
			||||||
| 
						 | 
					@ -522,6 +546,7 @@ void FurnaceGUI::drawSettings() {
 | 
				
			||||||
            ImGui::SetTooltip("saves power by lowering the frame rate to 2fps when idle.\nmay cause issues under Mesa drivers!");
 | 
					            ImGui::SetTooltip("saves power by lowering the frame rate to 2fps when idle.\nmay cause issues under Mesa drivers!");
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef IS_MOBILE
 | 
				
			||||||
          bool noThreadedInputB=settings.noThreadedInput;
 | 
					          bool noThreadedInputB=settings.noThreadedInput;
 | 
				
			||||||
          if (ImGui::Checkbox("Disable threaded input (restart after changing!)",&noThreadedInputB)) {
 | 
					          if (ImGui::Checkbox("Disable threaded input (restart after changing!)",&noThreadedInputB)) {
 | 
				
			||||||
            settings.noThreadedInput=noThreadedInputB;
 | 
					            settings.noThreadedInput=noThreadedInputB;
 | 
				
			||||||
| 
						 | 
					@ -537,6 +562,7 @@ void FurnaceGUI::drawSettings() {
 | 
				
			||||||
          if (ImGui::IsItemHovered()) {
 | 
					          if (ImGui::IsItemHovered()) {
 | 
				
			||||||
            ImGui::SetTooltip("remembers the window's last position on startup.");
 | 
					            ImGui::SetTooltip("remembers the window's last position on startup.");
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          bool blankInsB=settings.blankIns;
 | 
					          bool blankInsB=settings.blankIns;
 | 
				
			||||||
          if (ImGui::Checkbox("New instruments are blank",&blankInsB)) {
 | 
					          if (ImGui::Checkbox("New instruments are blank",&blankInsB)) {
 | 
				
			||||||
| 
						 | 
					@ -1318,6 +1344,23 @@ void FurnaceGUI::drawSettings() {
 | 
				
			||||||
            settings.susPosition=1;
 | 
					            settings.susPosition=1;
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          ImGui::Text("Macro editor layout:");
 | 
				
			||||||
 | 
					          if (ImGui::RadioButton("Unified##mel0",settings.macroLayout==0)) {
 | 
				
			||||||
 | 
					            settings.macroLayout=0;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          if (ImGui::RadioButton("Mobile##mel1",settings.macroLayout==1)) {
 | 
				
			||||||
 | 
					            settings.macroLayout=1;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          if (ImGui::RadioButton("Grid##mel2",settings.macroLayout==2)) {
 | 
				
			||||||
 | 
					            settings.macroLayout=2;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          if (ImGui::RadioButton("Single (with list)##mel3",settings.macroLayout==3)) {
 | 
				
			||||||
 | 
					            settings.macroLayout=3;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          if (ImGui::RadioButton("Single (combo box)##mel4",settings.macroLayout==4)) {
 | 
				
			||||||
 | 
					            settings.macroLayout=4;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          ImGui::Separator();
 | 
					          ImGui::Separator();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          ImGui::Text("Namco 163 chip name");
 | 
					          ImGui::Text("Namco 163 chip name");
 | 
				
			||||||
| 
						 | 
					@ -2397,6 +2440,7 @@ void FurnaceGUI::syncSettings() {
 | 
				
			||||||
  settings.persistFadeOut=e->getConfInt("persistFadeOut",1);
 | 
					  settings.persistFadeOut=e->getConfInt("persistFadeOut",1);
 | 
				
			||||||
  settings.exportLoops=e->getConfInt("exportLoops",0);
 | 
					  settings.exportLoops=e->getConfInt("exportLoops",0);
 | 
				
			||||||
  settings.exportFadeOut=e->getConfDouble("exportFadeOut",0.0);
 | 
					  settings.exportFadeOut=e->getConfDouble("exportFadeOut",0.0);
 | 
				
			||||||
 | 
					  settings.macroLayout=e->getConfInt("macroLayout",0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  clampSetting(settings.mainFontSize,2,96);
 | 
					  clampSetting(settings.mainFontSize,2,96);
 | 
				
			||||||
  clampSetting(settings.patFontSize,2,96);
 | 
					  clampSetting(settings.patFontSize,2,96);
 | 
				
			||||||
| 
						 | 
					@ -2503,6 +2547,7 @@ void FurnaceGUI::syncSettings() {
 | 
				
			||||||
  clampSetting(settings.centerPattern,0,1);
 | 
					  clampSetting(settings.centerPattern,0,1);
 | 
				
			||||||
  clampSetting(settings.ordersCursor,0,1);
 | 
					  clampSetting(settings.ordersCursor,0,1);
 | 
				
			||||||
  clampSetting(settings.persistFadeOut,0,1);
 | 
					  clampSetting(settings.persistFadeOut,0,1);
 | 
				
			||||||
 | 
					  clampSetting(settings.macroLayout,0,4);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (settings.exportLoops<0.0) settings.exportLoops=0.0;
 | 
					  if (settings.exportLoops<0.0) settings.exportLoops=0.0;
 | 
				
			||||||
  if (settings.exportFadeOut<0.0) settings.exportFadeOut=0.0;
 | 
					  if (settings.exportFadeOut<0.0) settings.exportFadeOut=0.0;
 | 
				
			||||||
| 
						 | 
					@ -2675,6 +2720,7 @@ void FurnaceGUI::commitSettings() {
 | 
				
			||||||
  e->setConf("persistFadeOut",settings.persistFadeOut);
 | 
					  e->setConf("persistFadeOut",settings.persistFadeOut);
 | 
				
			||||||
  e->setConf("exportLoops",settings.exportLoops);
 | 
					  e->setConf("exportLoops",settings.exportLoops);
 | 
				
			||||||
  e->setConf("exportFadeOut",settings.exportFadeOut);
 | 
					  e->setConf("exportFadeOut",settings.exportFadeOut);
 | 
				
			||||||
 | 
					  e->setConf("macroLayout",settings.macroLayout);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // colors
 | 
					  // colors
 | 
				
			||||||
  for (int i=0; i<GUI_COLOR_MAX; i++) {
 | 
					  for (int i=0; i<GUI_COLOR_MAX; i++) {
 | 
				
			||||||
| 
						 | 
					@ -3082,6 +3128,14 @@ void FurnaceGUI::popAccentColors() {
 | 
				
			||||||
#define SYSTEM_PAT_FONT_PATH_1 "/System/Library/Fonts/SFNSMono.ttf"
 | 
					#define SYSTEM_PAT_FONT_PATH_1 "/System/Library/Fonts/SFNSMono.ttf"
 | 
				
			||||||
#define SYSTEM_PAT_FONT_PATH_2 "/System/Library/Fonts/Courier New.ttf"
 | 
					#define SYSTEM_PAT_FONT_PATH_2 "/System/Library/Fonts/Courier New.ttf"
 | 
				
			||||||
#define SYSTEM_PAT_FONT_PATH_3 "/System/Library/Fonts/Courier New.ttf"
 | 
					#define SYSTEM_PAT_FONT_PATH_3 "/System/Library/Fonts/Courier New.ttf"
 | 
				
			||||||
 | 
					#elif defined(ANDROID)
 | 
				
			||||||
 | 
					#define SYSTEM_FONT_PATH_1 "/system/fonts/Roboto-Regular.ttf"
 | 
				
			||||||
 | 
					#define SYSTEM_FONT_PATH_2 "/system/fonts/DroidSans.ttf"
 | 
				
			||||||
 | 
					#define SYSTEM_FONT_PATH_3 "/system/fonts/DroidSans.ttf"
 | 
				
			||||||
 | 
					// ???
 | 
				
			||||||
 | 
					#define SYSTEM_PAT_FONT_PATH_1 "/system/fonts/RobotoMono-Regular.ttf"
 | 
				
			||||||
 | 
					#define SYSTEM_PAT_FONT_PATH_2 "/system/fonts/DroidSansMono.ttf"
 | 
				
			||||||
 | 
					#define SYSTEM_PAT_FONT_PATH_3 "/system/fonts/CutiveMono.ttf"
 | 
				
			||||||
#else
 | 
					#else
 | 
				
			||||||
#define SYSTEM_FONT_PATH_1 "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf"
 | 
					#define SYSTEM_FONT_PATH_1 "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf"
 | 
				
			||||||
#define SYSTEM_FONT_PATH_2 "/usr/share/fonts/TTF/DejaVuSans.ttf"
 | 
					#define SYSTEM_FONT_PATH_2 "/usr/share/fonts/TTF/DejaVuSans.ttf"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -32,6 +32,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo
 | 
				
			||||||
    case DIV_SYSTEM_YM2612_FRAC_EXT: {
 | 
					    case DIV_SYSTEM_YM2612_FRAC_EXT: {
 | 
				
			||||||
      int clockSel=flags.getInt("clockSel",0);
 | 
					      int clockSel=flags.getInt("clockSel",0);
 | 
				
			||||||
      bool ladder=flags.getBool("ladderEffect",0);
 | 
					      bool ladder=flags.getBool("ladderEffect",0);
 | 
				
			||||||
 | 
					      bool noExtMacros=flags.getBool("noExtMacros",false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (ImGui::RadioButton("NTSC (7.67MHz)",clockSel==0)) {
 | 
					      if (ImGui::RadioButton("NTSC (7.67MHz)",clockSel==0)) {
 | 
				
			||||||
        clockSel=0;
 | 
					        clockSel=0;
 | 
				
			||||||
| 
						 | 
					@ -56,11 +57,17 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo
 | 
				
			||||||
      if (ImGui::Checkbox("Enable DAC distortion",&ladder)) {
 | 
					      if (ImGui::Checkbox("Enable DAC distortion",&ladder)) {
 | 
				
			||||||
        altered=true;
 | 
					        altered=true;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					      if (type==DIV_SYSTEM_YM2612_EXT || type==DIV_SYSTEM_YM2612_FRAC_EXT) {
 | 
				
			||||||
 | 
					        if (ImGui::Checkbox("Disable ExtCh FM macros (compatibility)",&noExtMacros)) {
 | 
				
			||||||
 | 
					          altered=true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
      
 | 
					      
 | 
				
			||||||
      if (altered) {
 | 
					      if (altered) {
 | 
				
			||||||
        e->lockSave([&]() {
 | 
					        e->lockSave([&]() {
 | 
				
			||||||
          flags.set("clockSel",clockSel);
 | 
					          flags.set("clockSel",clockSel);
 | 
				
			||||||
          flags.set("ladderEffect",ladder);
 | 
					          flags.set("ladderEffect",ladder);
 | 
				
			||||||
 | 
					          flags.set("noExtMacros",noExtMacros);
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      break;
 | 
					      break;
 | 
				
			||||||
| 
						 | 
					@ -439,6 +446,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo
 | 
				
			||||||
    case DIV_SYSTEM_YM2610B:
 | 
					    case DIV_SYSTEM_YM2610B:
 | 
				
			||||||
    case DIV_SYSTEM_YM2610B_EXT: {
 | 
					    case DIV_SYSTEM_YM2610B_EXT: {
 | 
				
			||||||
      int clockSel=flags.getInt("clockSel",0);
 | 
					      int clockSel=flags.getInt("clockSel",0);
 | 
				
			||||||
 | 
					      bool noExtMacros=flags.getBool("noExtMacros",false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (ImGui::RadioButton("8MHz (Neo Geo MVS)",clockSel==0)) {
 | 
					      if (ImGui::RadioButton("8MHz (Neo Geo MVS)",clockSel==0)) {
 | 
				
			||||||
        clockSel=0;
 | 
					        clockSel=0;
 | 
				
			||||||
| 
						 | 
					@ -449,9 +457,16 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo
 | 
				
			||||||
        altered=true;
 | 
					        altered=true;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (type==DIV_SYSTEM_YM2610_EXT || type==DIV_SYSTEM_YM2610_FULL_EXT || type==DIV_SYSTEM_YM2610B_EXT) {
 | 
				
			||||||
 | 
					        if (ImGui::Checkbox("Disable ExtCh FM macros (compatibility)",&noExtMacros)) {
 | 
				
			||||||
 | 
					          altered=true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (altered) {
 | 
					      if (altered) {
 | 
				
			||||||
        e->lockSave([&]() {
 | 
					        e->lockSave([&]() {
 | 
				
			||||||
          flags.set("clockSel",clockSel);
 | 
					          flags.set("clockSel",clockSel);
 | 
				
			||||||
 | 
					          flags.set("noExtMacros",noExtMacros);
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      break;
 | 
					      break;
 | 
				
			||||||
| 
						 | 
					@ -668,8 +683,23 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo
 | 
				
			||||||
      break;
 | 
					      break;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    case DIV_SYSTEM_PCSPKR: {
 | 
					    case DIV_SYSTEM_PCSPKR: {
 | 
				
			||||||
 | 
					      int clockSel=flags.getInt("clockSel",0);
 | 
				
			||||||
      int speakerType=flags.getInt("speakerType",0);
 | 
					      int speakerType=flags.getInt("speakerType",0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      ImGui::Text("Clock rate:");
 | 
				
			||||||
 | 
					      if (ImGui::RadioButton("1.19MHz (PC)",clockSel==0)) {
 | 
				
			||||||
 | 
					        clockSel=0;
 | 
				
			||||||
 | 
					        altered=true;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      if (ImGui::RadioButton("1.99MHz (PC-98)",clockSel==1)) {
 | 
				
			||||||
 | 
					        clockSel=1;
 | 
				
			||||||
 | 
					        altered=true;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      if (ImGui::RadioButton("2.46MHz (PC-98)",clockSel==2)) {
 | 
				
			||||||
 | 
					        clockSel=2;
 | 
				
			||||||
 | 
					        altered=true;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      ImGui::Text("Speaker type:");
 | 
					      ImGui::Text("Speaker type:");
 | 
				
			||||||
      if (ImGui::RadioButton("Unfiltered",speakerType==0)) {
 | 
					      if (ImGui::RadioButton("Unfiltered",speakerType==0)) {
 | 
				
			||||||
        speakerType=0;
 | 
					        speakerType=0;
 | 
				
			||||||
| 
						 | 
					@ -690,6 +720,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (altered) {
 | 
					      if (altered) {
 | 
				
			||||||
        e->lockSave([&]() {
 | 
					        e->lockSave([&]() {
 | 
				
			||||||
 | 
					          flags.set("clockSel",clockSel);
 | 
				
			||||||
          flags.set("speakerType",speakerType);
 | 
					          flags.set("speakerType",speakerType);
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
| 
						 | 
					@ -805,6 +836,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo
 | 
				
			||||||
    case DIV_SYSTEM_OPN_EXT: {
 | 
					    case DIV_SYSTEM_OPN_EXT: {
 | 
				
			||||||
      int clockSel=flags.getInt("clockSel",0);
 | 
					      int clockSel=flags.getInt("clockSel",0);
 | 
				
			||||||
      int prescale=flags.getInt("prescale",0);
 | 
					      int prescale=flags.getInt("prescale",0);
 | 
				
			||||||
 | 
					      bool noExtMacros=flags.getBool("noExtMacros",false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      ImGui::Text("Clock rate:");
 | 
					      ImGui::Text("Clock rate:");
 | 
				
			||||||
      if (ImGui::RadioButton("3.58MHz (NTSC)",clockSel==0)) {
 | 
					      if (ImGui::RadioButton("3.58MHz (NTSC)",clockSel==0)) {
 | 
				
			||||||
| 
						 | 
					@ -845,10 +877,17 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo
 | 
				
			||||||
        altered=true;
 | 
					        altered=true;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (type==DIV_SYSTEM_OPN_EXT) {
 | 
				
			||||||
 | 
					        if (ImGui::Checkbox("Disable ExtCh FM macros (compatibility)",&noExtMacros)) {
 | 
				
			||||||
 | 
					          altered=true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (altered) {
 | 
					      if (altered) {
 | 
				
			||||||
        e->lockSave([&]() {
 | 
					        e->lockSave([&]() {
 | 
				
			||||||
          flags.set("clockSel",clockSel);
 | 
					          flags.set("clockSel",clockSel);
 | 
				
			||||||
          flags.set("prescale",prescale);
 | 
					          flags.set("prescale",prescale);
 | 
				
			||||||
 | 
					          flags.set("noExtMacros",noExtMacros);
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      break;
 | 
					      break;
 | 
				
			||||||
| 
						 | 
					@ -857,6 +896,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo
 | 
				
			||||||
    case DIV_SYSTEM_PC98_EXT: {
 | 
					    case DIV_SYSTEM_PC98_EXT: {
 | 
				
			||||||
      int clockSel=flags.getInt("clockSel",0);
 | 
					      int clockSel=flags.getInt("clockSel",0);
 | 
				
			||||||
      int prescale=flags.getInt("prescale",0);
 | 
					      int prescale=flags.getInt("prescale",0);
 | 
				
			||||||
 | 
					      bool noExtMacros=flags.getBool("noExtMacros",false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      ImGui::Text("Clock rate:");
 | 
					      ImGui::Text("Clock rate:");
 | 
				
			||||||
      if (ImGui::RadioButton("8MHz (Arcade)",clockSel==0)) {
 | 
					      if (ImGui::RadioButton("8MHz (Arcade)",clockSel==0)) {
 | 
				
			||||||
| 
						 | 
					@ -881,10 +921,17 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo
 | 
				
			||||||
        altered=true;
 | 
					        altered=true;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (type==DIV_SYSTEM_PC98_EXT) {
 | 
				
			||||||
 | 
					        if (ImGui::Checkbox("Disable ExtCh FM macros (compatibility)",&noExtMacros)) {
 | 
				
			||||||
 | 
					          altered=true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (altered) {
 | 
					      if (altered) {
 | 
				
			||||||
        e->lockSave([&]() {
 | 
					        e->lockSave([&]() {
 | 
				
			||||||
          flags.set("clockSel",clockSel);
 | 
					          flags.set("clockSel",clockSel);
 | 
				
			||||||
          flags.set("prescale",prescale);
 | 
					          flags.set("prescale",prescale);
 | 
				
			||||||
 | 
					          flags.set("noExtMacros",noExtMacros);
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      break;
 | 
					      break;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue