mirror of
https://github.com/tildearrow/furnace.git
synced 2024-11-26 22:43:01 +00:00
Merge branch 'master' into duplicate_channel_struct
This commit is contained in:
commit
1ae1f32574
63 changed files with 1357 additions and 629 deletions
3
.github/workflows/build.yml
vendored
3
.github/workflows/build.yml
vendored
|
@ -11,7 +11,7 @@ defaults:
|
|||
shell: bash
|
||||
|
||||
env:
|
||||
BUILD_TYPE: Debug
|
||||
BUILD_TYPE: Release
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
@ -275,6 +275,7 @@ jobs:
|
|||
cp -v ../LICENSE LICENSE.txt
|
||||
cp -v ../README.md README.txt
|
||||
cp -vr ../{papers,demos,instruments} ../${binPath}/furnace.exe ./
|
||||
sha256sum ../${binPath}/furnace.exe > checksum.txt
|
||||
|
||||
popd
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@ the coding style is described here:
|
|||
- no spaces in operations except for `||` and `&&`
|
||||
- no space between variable name and assignment
|
||||
- space between macro in string literals
|
||||
- space after comment delimiter
|
||||
- C++ pointer style: `void* variable` rather than `void *variable`
|
||||
- indent switch cases
|
||||
- preprocessor directives not intended
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
demos/TimeTrial.fur
Normal file
BIN
demos/TimeTrial.fur
Normal file
Binary file not shown.
BIN
demos/atari breakbeat.fur
Normal file
BIN
demos/atari breakbeat.fur
Normal file
Binary file not shown.
Binary file not shown.
BIN
demos/duty fun.fur
Normal file
BIN
demos/duty fun.fur
Normal file
Binary file not shown.
Binary file not shown.
BIN
demos/genesis thing.fur
Normal file
BIN
demos/genesis thing.fur
Normal file
Binary file not shown.
BIN
demos/overdrive.fur
Normal file
BIN
demos/overdrive.fur
Normal file
Binary file not shown.
BIN
demos/snowdin.fur
Executable file
BIN
demos/snowdin.fur
Executable file
Binary file not shown.
BIN
demos/very chill snes.fur
Normal file
BIN
demos/very chill snes.fur
Normal file
Binary file not shown.
2
extern/imgui_patched/imgui.cpp
vendored
2
extern/imgui_patched/imgui.cpp
vendored
|
@ -3970,6 +3970,7 @@ void ImGui::UpdateMouseMovingWindowNewFrame()
|
|||
{
|
||||
MarkIniSettingsDirty(moving_window);
|
||||
SetWindowPos(moving_window, pos, ImGuiCond_Always);
|
||||
g.InertialScrollInhibited=true;
|
||||
if (moving_window->ViewportOwned) // Synchronize viewport immediately because some overlays may relies on clipping rectangle before we Begin() into the window.
|
||||
{
|
||||
moving_window->Viewport->Pos = pos;
|
||||
|
@ -6025,6 +6026,7 @@ static bool ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& s
|
|||
if (size_target.x != FLT_MAX)
|
||||
{
|
||||
window->SizeFull = size_target;
|
||||
g.InertialScrollInhibited=true;
|
||||
MarkIniSettingsDirty(window);
|
||||
}
|
||||
if (pos_target.x != FLT_MAX)
|
||||
|
|
|
@ -126,7 +126,7 @@ void k007232_core::voice_t::write(u8 address, u8 data)
|
|||
m_start = (m_start & ~0x0ff00) | (u32(data) << 8);
|
||||
break;
|
||||
case 4: // start address bit 16
|
||||
m_start = (m_start & ~0x10000) | (u32(bitfield(data, 16)) << 16);
|
||||
m_start = (m_start & ~0x10000) | (u32(bitfield(data, 0)) << 16);
|
||||
break;
|
||||
case 5: // keyon trigger
|
||||
keyon();
|
||||
|
|
|
@ -59,6 +59,7 @@ void k053260_core::voice_t::tick()
|
|||
{
|
||||
m_bitpos -= 8;
|
||||
}
|
||||
m_counter = bitfield(m_pitch, 0, 12);
|
||||
}
|
||||
m_data = m_host.m_intf.read_sample(bitfield(m_addr, 0, 21)); // fetch ROM
|
||||
if (update)
|
||||
|
@ -76,6 +77,7 @@ void k053260_core::voice_t::tick()
|
|||
if (m_loop)
|
||||
{
|
||||
m_addr = m_start;
|
||||
m_remain = m_length;
|
||||
m_adpcm_buf = 0;
|
||||
}
|
||||
else
|
||||
|
|
|
@ -32,6 +32,8 @@ these fields are 0 in format versions prior to 100 (0.6pre1).
|
|||
|
||||
the format versions are:
|
||||
|
||||
- 129: Furnace dev129
|
||||
- 128: Furnace dev128
|
||||
- 127: Furnace dev127
|
||||
- 126: Furnace dev126
|
||||
- 125: Furnace dev125
|
||||
|
@ -1120,9 +1122,11 @@ size | description
|
|||
| - 16: 16-bit PCM
|
||||
1 | loop direction (>=123) or reserved
|
||||
| - 0: forward
|
||||
| - 0: backward
|
||||
| - 0: ping-pong
|
||||
2 | reserved
|
||||
| - 1: backward
|
||||
| - 2: ping-pong
|
||||
1 | flags (>=129) or reserved
|
||||
| - 0: BRR emphasis
|
||||
1 | reserved
|
||||
4 | loop start
|
||||
| - -1 means no loop
|
||||
4 | loop end
|
||||
|
|
|
@ -106,6 +106,13 @@ bool TAAudioSDL::init(TAAudioDesc& request, TAAudioDesc& response) {
|
|||
audioSysStarted=true;
|
||||
}
|
||||
|
||||
const char* audioDriver=SDL_GetCurrentAudioDriver();
|
||||
if (audioDriver==NULL) {
|
||||
logD("SDL audio driver: NULL!");
|
||||
} else {
|
||||
logD("SDL audio driver: %s",audioDriver);
|
||||
}
|
||||
|
||||
desc=request;
|
||||
desc.outFormat=TA_AUDIO_FORMAT_F32;
|
||||
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
*/
|
||||
|
||||
#include "brrUtils.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#define NEXT_SAMPLE buf[j]-(buf[j]>>3)
|
||||
|
||||
|
@ -56,384 +58,257 @@
|
|||
last2=last1; \
|
||||
last1=nextDec; \
|
||||
|
||||
long brrEncode(short* buf, unsigned char* out, long len, long loopStart) {
|
||||
void brrEncodeBlock(const short* buf, unsigned char* out, unsigned char range, unsigned char filter, short* last1, short* last2, int* errorSum) {
|
||||
// encode one block using BRR
|
||||
unsigned char nibble=0;
|
||||
int preOut=0;
|
||||
int pred=0;
|
||||
int nextDec=0;
|
||||
int nextError=0;
|
||||
*errorSum=0;
|
||||
for (int j=0; j<16; j++) {
|
||||
short s=NEXT_SAMPLE;
|
||||
switch (filter) {
|
||||
case 0: // no filter
|
||||
pred=s;
|
||||
break;
|
||||
case 1: // simple
|
||||
pred=s-(((int)(*last1*2)*15)>>4);
|
||||
break;
|
||||
case 2: // complex
|
||||
pred=s+(((int)(*last2*2)*15)>>4)-(((int)(*last1*2)*61)>>5);
|
||||
break;
|
||||
case 3:
|
||||
pred=s+(((int)(*last2*2)*13)>>4)-(((int)(*last1*2)*115)>>6);
|
||||
break;
|
||||
}
|
||||
|
||||
if (pred<-32768) pred=-32768;
|
||||
if (pred>32767) pred=32767;
|
||||
|
||||
preOut=pred>>range;
|
||||
if (range) {
|
||||
if (pred&(1<<(range>>1))) preOut++;
|
||||
if (filter==0 && range>=12) if (preOut<-7) preOut=-7;
|
||||
}
|
||||
if (preOut>7) preOut=7;
|
||||
if (preOut<-8) preOut=-8;
|
||||
|
||||
nibble=preOut&15;
|
||||
if (j&1) {
|
||||
out[j>>1]|=nibble;
|
||||
} else {
|
||||
out[j>>1]=nibble<<4;
|
||||
}
|
||||
|
||||
// roll last1/last2
|
||||
nextDec=nibble;
|
||||
if (nextDec&8) nextDec|=0xfffffff0;
|
||||
|
||||
if (range>=13) { /* invalid shift */
|
||||
nextDec=(nextDec<0)?0xfffff800:0;
|
||||
} else {
|
||||
nextDec<<=range; /* range */
|
||||
nextDec>>=1;
|
||||
}
|
||||
|
||||
switch (filter) { /* filter */
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
nextDec+=(*last1)+((-(*last1))>>4);
|
||||
break;
|
||||
case 2:
|
||||
nextDec+=(*last1)*2+((-(*last1)*3)>>5)-(*last2)+((*last2)>>4);
|
||||
break;
|
||||
case 3:
|
||||
nextDec+=(*last1)*2+((-(*last1)*13)>>6)-(*last2)+(((*last2)*3)>>4);
|
||||
break;
|
||||
}
|
||||
|
||||
nextDec&=0x7fff;
|
||||
if (nextDec&0x4000) nextDec|=0xffff8000;
|
||||
|
||||
nextError=s-(nextDec<<1);
|
||||
if (nextError<0) nextError=-nextError;
|
||||
*errorSum+=nextError;
|
||||
|
||||
*last2=*last1;
|
||||
*last1=nextDec;
|
||||
}
|
||||
}
|
||||
|
||||
long brrEncode(short* buf, unsigned char* out, long len, long loopStart, unsigned char emphasis) {
|
||||
if (len==0) return 0;
|
||||
|
||||
// encoding process:
|
||||
// 1. read next group of 16 samples
|
||||
// 2. is this the first block?
|
||||
// - if yes, don't filter. output and then go to 1
|
||||
// 3. is this the loop block?
|
||||
// - if yes, don't filter. output and then go to 1
|
||||
// 4. try encoding using 4 filters
|
||||
// - perform linear prediction
|
||||
// - calculate range
|
||||
// - decode and apply correction to achieve low error
|
||||
// 4. try encoding using 3 filters and 12 ranges (besides no filter)
|
||||
// 5. which one of these yields the least amount of error?
|
||||
// 6. output the one which does
|
||||
// 7. is this the last block?
|
||||
// - if yes, mark end and finish
|
||||
// 8. go to 1
|
||||
// 7. do we still have more to encode?
|
||||
// - if so, go to 1
|
||||
// 8. is loop point set?
|
||||
// - if not, end process here
|
||||
// 9. is transition between last block and loop block smooth?
|
||||
// - if not, encode the loop block again and output it
|
||||
long total=0;
|
||||
unsigned char next0[8];
|
||||
unsigned char next1[8];
|
||||
unsigned char next2[8];
|
||||
unsigned char next3[8];
|
||||
unsigned char filter=0;
|
||||
unsigned char range0=0;
|
||||
unsigned char range1=0;
|
||||
unsigned char range2=0;
|
||||
unsigned char range3=0;
|
||||
unsigned char o=0;
|
||||
int pred1[16];
|
||||
int pred2[16];
|
||||
int pred3[16];
|
||||
short o1=0;
|
||||
short o2=0;
|
||||
short o0=0;
|
||||
short o1f1=0;
|
||||
short o1f2=0;
|
||||
short o1f3=0;
|
||||
//short o2f1=0;
|
||||
short o2f2=0;
|
||||
short o2f3=0;
|
||||
unsigned char range=0;
|
||||
|
||||
int last1=0;
|
||||
int last2=0;
|
||||
int nextDec=0;
|
||||
int maxError[4];
|
||||
int avgError[4];
|
||||
short x0=0;
|
||||
short x1=0;
|
||||
short x2=0;
|
||||
int emphOut=0;
|
||||
|
||||
short in[17];
|
||||
|
||||
short last1[4][13];
|
||||
short last2[4][13];
|
||||
int avgError[4][13];
|
||||
unsigned char possibleOut[4][13][8];
|
||||
|
||||
memset(in,0,16*sizeof(short));
|
||||
memset(last1,0,4*13*sizeof(short));
|
||||
memset(last2,0,4*13*sizeof(short));
|
||||
memset(avgError,0,4*13*sizeof(int));
|
||||
memset(possibleOut,0,4*13*8);
|
||||
|
||||
len&=~15;
|
||||
loopStart&=~15;
|
||||
for (long i=0; i<len; i+=16) {
|
||||
range0=0;
|
||||
// encode with no filter
|
||||
for (int j=0; j<16; j++) {
|
||||
short s=NEXT_SAMPLE;
|
||||
if (s<0) s=-s;
|
||||
while (range0<12 && s>((8<<range0)-1)) range0++;
|
||||
}
|
||||
for (int j=0; j<16; j++) {
|
||||
short s=NEXT_SAMPLE;
|
||||
o0=s>>range0;
|
||||
if (range0) if (s&(1<<(range1>>1))) o0++;
|
||||
if (o0>7) o0=7;
|
||||
if (o0<-8) o0=-8;
|
||||
if (range0>=12) if (o0<-7) o0=-7;
|
||||
o=o0&15;
|
||||
if (j&1) {
|
||||
next0[j>>1]|=o;
|
||||
} else {
|
||||
next0[j>>1]=o<<4;
|
||||
}
|
||||
}
|
||||
|
||||
// encode with filter
|
||||
if (i /*&& i!=loopStart*/) {
|
||||
// 1: x = o0 - o1 * 15/16
|
||||
// 2: x = o0 + o2 * 15/16 - o1 * 61/32
|
||||
// 3: x = o0 + o2 * 13/16 - o1 * 115/64
|
||||
range1=0;
|
||||
range2=0;
|
||||
range3=0;
|
||||
//o2f1=o2;
|
||||
o2f2=o2;
|
||||
o2f3=o2;
|
||||
o1f1=o1;
|
||||
o1f2=o1;
|
||||
o1f3=o1;
|
||||
// first pass
|
||||
for (int j=0; j<16; j++) {
|
||||
int s=NEXT_SAMPLE;
|
||||
|
||||
pred1[j]=s-(((int)o1*15)>>4);
|
||||
if (pred1[j]<-32768) pred1[j]=-32768;
|
||||
if (pred1[j]>32767) pred1[j]=32767;
|
||||
|
||||
pred2[j]=s+(((int)o2*15)>>4)-(((int)o1*61)>>5);
|
||||
if (pred2[j]<-32768) pred2[j]=-32768;
|
||||
if (pred2[j]>32767) pred2[j]=32767;
|
||||
|
||||
pred3[j]=s+(((int)o2*13)>>4)-(((int)o1*115)>>6);
|
||||
if (pred3[j]<-32768) pred3[j]=-32768;
|
||||
if (pred3[j]>32767) pred3[j]=32767;
|
||||
|
||||
o2=o1;
|
||||
o1=s;
|
||||
}
|
||||
// calculate range of values
|
||||
for (int j=0; j<16; j++) {
|
||||
short s=pred1[j];
|
||||
if (s<0) s=-s;
|
||||
while (range1<12 && s>((8<<range1)-1)) range1++;
|
||||
|
||||
s=pred2[j];
|
||||
if (s<0) s=-s;
|
||||
while (range2<12 && s>((8<<range2)-1)) range2++;
|
||||
|
||||
s=pred3[j];
|
||||
if (s<0) s=-s;
|
||||
while (range3<12 && s>((8<<range3)-1)) range3++;
|
||||
}
|
||||
// second pass
|
||||
int prevLast1=last1;
|
||||
int prevLast2=last2;
|
||||
filter=1;
|
||||
for (int j=0; j<16; j++) {
|
||||
int s=NEXT_SAMPLE;
|
||||
|
||||
pred1[j]=s-(((int)o1f1*15)>>4);
|
||||
if (pred1[j]<-32768) pred1[j]=-32768;
|
||||
if (pred1[j]>32767) pred1[j]=32767;
|
||||
|
||||
o0=pred1[j]>>range1;
|
||||
if (range1) if (pred1[j]&(1<<(range1>>1))) o0++;
|
||||
if (o0>7) o0=7;
|
||||
if (o0<-8) o0=-8;
|
||||
o=o0&15;
|
||||
if (j&1) {
|
||||
next1[j>>1]|=o;
|
||||
if (i+17>len) {
|
||||
long p=i;
|
||||
for (int j=0; j<17; j++) {
|
||||
if (p>=len) {
|
||||
if (loopStart<0 || loopStart>=len) {
|
||||
in[j]=0;
|
||||
} else {
|
||||
p=loopStart;
|
||||
in[j]=buf[p++];
|
||||
}
|
||||
} else {
|
||||
next1[j>>1]=o<<4;
|
||||
}
|
||||
|
||||
nextDec=o;
|
||||
DO_ONE_DEC(range1);
|
||||
//o2f1=last2<<1;
|
||||
o1f1=last1<<1;
|
||||
}
|
||||
last1=prevLast1;
|
||||
last2=prevLast2;
|
||||
filter=2;
|
||||
for (int j=0; j<16; j++) {
|
||||
int s=NEXT_SAMPLE;
|
||||
pred2[j]=s+(((int)o2f2*15)>>4)-(((int)o1f2*61)>>5);
|
||||
if (pred2[j]<-32768) pred2[j]=-32768;
|
||||
if (pred2[j]>32767) pred2[j]=32767;
|
||||
|
||||
o0=pred2[j]>>range2;
|
||||
if (range2) if (pred2[j]&(1<<(range2>>1))) o0++;
|
||||
if (o0>7) o0=7;
|
||||
if (o0<-8) o0=-8;
|
||||
o=o0&15;
|
||||
if (j&1) {
|
||||
next2[j>>1]|=o;
|
||||
} else {
|
||||
next2[j>>1]=o<<4;
|
||||
}
|
||||
|
||||
nextDec=o;
|
||||
DO_ONE_DEC(range2);
|
||||
o2f2=last2<<1;
|
||||
o1f2=last1<<1;
|
||||
}
|
||||
last1=prevLast1;
|
||||
last2=prevLast2;
|
||||
filter=3;
|
||||
for (int j=0; j<16; j++) {
|
||||
int s=NEXT_SAMPLE;
|
||||
pred3[j]=s+(((int)o2f3*13)>>4)-(((int)o1f3*115)>>6);
|
||||
if (pred3[j]<-32768) pred3[j]=-32768;
|
||||
if (pred3[j]>32767) pred3[j]=32767;
|
||||
|
||||
o0=pred3[j]>>range3;
|
||||
if (range3) if (pred3[j]&(1<<(range3>>1))) o0++;
|
||||
if (o0>7) o0=7;
|
||||
if (o0<-8) o0=-8;
|
||||
o=o0&15;
|
||||
if (j&1) {
|
||||
next3[j>>1]|=o;
|
||||
} else {
|
||||
next3[j>>1]=o<<4;
|
||||
}
|
||||
|
||||
nextDec=o;
|
||||
DO_ONE_DEC(range3);
|
||||
o2f3=last2<<1;
|
||||
o1f3=last1<<1;
|
||||
}
|
||||
last1=prevLast1;
|
||||
last2=prevLast2;
|
||||
|
||||
// find best filter
|
||||
int error=0;
|
||||
|
||||
maxError[0]=0;
|
||||
maxError[1]=0;
|
||||
maxError[2]=0;
|
||||
maxError[3]=0;
|
||||
avgError[0]=0;
|
||||
avgError[1]=0;
|
||||
avgError[2]=0;
|
||||
avgError[3]=0;
|
||||
|
||||
// test filter 0
|
||||
filter=0;
|
||||
for (int j=0; j<16; j++) {
|
||||
int s=NEXT_SAMPLE;
|
||||
if (j&1) {
|
||||
nextDec=next0[j>>1]&15;
|
||||
} else {
|
||||
nextDec=next0[j>>1]>>4;
|
||||
}
|
||||
DO_ONE_DEC(range0);
|
||||
error=s-(nextDec<<1);
|
||||
if (error<0) error=-error;
|
||||
avgError[0]+=error;
|
||||
if (error>maxError[0]) maxError[0]=error;
|
||||
}
|
||||
last1=prevLast1;
|
||||
last2=prevLast2;
|
||||
|
||||
// test filter 1
|
||||
filter=1;
|
||||
for (int j=0; j<16; j++) {
|
||||
int s=NEXT_SAMPLE;
|
||||
if (j&1) {
|
||||
nextDec=next1[j>>1]&15;
|
||||
} else {
|
||||
nextDec=next1[j>>1]>>4;
|
||||
}
|
||||
DO_ONE_DEC(range1);
|
||||
error=s-(nextDec<<1);
|
||||
if (error<0) error=-error;
|
||||
avgError[1]+=error;
|
||||
if (error>maxError[1]) maxError[1]=error;
|
||||
}
|
||||
last1=prevLast1;
|
||||
last2=prevLast2;
|
||||
|
||||
// test filter 2
|
||||
filter=2;
|
||||
for (int j=0; j<16; j++) {
|
||||
int s=NEXT_SAMPLE;
|
||||
if (j&1) {
|
||||
nextDec=next2[j>>1]&15;
|
||||
} else {
|
||||
nextDec=next2[j>>1]>>4;
|
||||
}
|
||||
DO_ONE_DEC(range2);
|
||||
error=s-(nextDec<<1);
|
||||
if (error<0) error=-error;
|
||||
avgError[2]+=error;
|
||||
if (error>maxError[2]) maxError[2]=error;
|
||||
}
|
||||
last1=prevLast1;
|
||||
last2=prevLast2;
|
||||
|
||||
// test filter 3
|
||||
filter=3;
|
||||
for (int j=0; j<16; j++) {
|
||||
int s=NEXT_SAMPLE;
|
||||
if (j&1) {
|
||||
nextDec=next3[j>>1]&15;
|
||||
} else {
|
||||
nextDec=next3[j>>1]>>4;
|
||||
}
|
||||
DO_ONE_DEC(range3);
|
||||
error=s-(nextDec<<1);
|
||||
if (error<0) error=-error;
|
||||
avgError[3]+=error;
|
||||
if (error>maxError[3]) maxError[3]=error;
|
||||
}
|
||||
last1=prevLast1;
|
||||
last2=prevLast2;
|
||||
|
||||
// pick best filter
|
||||
int candError=0x7fffffff;
|
||||
for (int j=0; j<4; j++) {
|
||||
if (avgError[j]<candError) {
|
||||
candError=avgError[j];
|
||||
filter=j;
|
||||
in[j]=buf[p++];
|
||||
}
|
||||
}
|
||||
//printf("block %ld: %8d %8d %8d %8d -> %d\n",i>>4,avgError[0],avgError[1],avgError[2],avgError[3],filter);
|
||||
} else {
|
||||
// don't filter on the first or loop block
|
||||
memcpy(in,&buf[i],17*sizeof(short));
|
||||
}
|
||||
|
||||
// emphasis
|
||||
if (emphasis) {
|
||||
for (int j=0; j<17; j++) {
|
||||
x0=x1;
|
||||
x1=x2;
|
||||
x2=in[j];
|
||||
|
||||
if (j==0) continue;
|
||||
emphOut=((x1<<11)-x0*370-in[j]*374)/1305;
|
||||
if (emphOut<-32768) emphOut=-32768;
|
||||
if (emphOut>32767) emphOut=32767;
|
||||
in[j-1]=emphOut;
|
||||
}
|
||||
}
|
||||
|
||||
// encode
|
||||
for (int j=0; j<4; j++) {
|
||||
for (int k=0; k<13; k++) {
|
||||
brrEncodeBlock(in,possibleOut[j][k],k,j,&last1[j][k],&last2[j][k],&avgError[j][k]);
|
||||
}
|
||||
}
|
||||
|
||||
// find best filter/range
|
||||
int candError=0x7fffffff;
|
||||
if (i==0) {
|
||||
filter=0;
|
||||
for (int k=0; k<13; k++) {
|
||||
if (avgError[0][k]<candError) {
|
||||
candError=avgError[0][k];
|
||||
range=k;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int j=0; j<4; j++) {
|
||||
for (int k=0; k<13; k++) {
|
||||
if (avgError[j][k]<candError) {
|
||||
candError=avgError[j][k];
|
||||
filter=j;
|
||||
range=k;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch (filter) {
|
||||
case 0:
|
||||
for (int j=0; j<8; j++) {
|
||||
nextDec=next0[j]>>4;
|
||||
DO_ONE_DEC(range0);
|
||||
nextDec=next0[j]&15;
|
||||
DO_ONE_DEC(range0);
|
||||
}
|
||||
o2=last2<<1;
|
||||
o1=last1<<1;
|
||||
|
||||
out[0]=(range0<<4)|(filter<<2)|((i+16>=len)?((loopStart>=0)?3:1):0);
|
||||
out[1]=next0[0];
|
||||
out[2]=next0[1];
|
||||
out[3]=next0[2];
|
||||
out[4]=next0[3];
|
||||
out[5]=next0[4];
|
||||
out[6]=next0[5];
|
||||
out[7]=next0[6];
|
||||
out[8]=next0[7];
|
||||
break;
|
||||
case 1:
|
||||
for (int j=0; j<8; j++) {
|
||||
nextDec=next1[j]>>4;
|
||||
DO_ONE_DEC(range1);
|
||||
nextDec=next1[j]&15;
|
||||
DO_ONE_DEC(range1);
|
||||
}
|
||||
o2=last2<<1;
|
||||
o1=last1<<1;
|
||||
out[0]=(range1<<4)|(filter<<2)|((i+16>=len)?((loopStart>=0)?3:1):0);
|
||||
out[1]=next1[0];
|
||||
out[2]=next1[1];
|
||||
out[3]=next1[2];
|
||||
out[4]=next1[3];
|
||||
out[5]=next1[4];
|
||||
out[6]=next1[5];
|
||||
out[7]=next1[6];
|
||||
out[8]=next1[7];
|
||||
break;
|
||||
case 2:
|
||||
for (int j=0; j<8; j++) {
|
||||
nextDec=next2[j]>>4;
|
||||
DO_ONE_DEC(range2);
|
||||
nextDec=next2[j]&15;
|
||||
DO_ONE_DEC(range2);
|
||||
}
|
||||
o2=last2<<1;
|
||||
o1=last1<<1;
|
||||
out[0]=(range2<<4)|(filter<<2)|((i+16>=len)?((loopStart>=0)?3:1):0);
|
||||
out[1]=next2[0];
|
||||
out[2]=next2[1];
|
||||
out[3]=next2[2];
|
||||
out[4]=next2[3];
|
||||
out[5]=next2[4];
|
||||
out[6]=next2[5];
|
||||
out[7]=next2[6];
|
||||
out[8]=next2[7];
|
||||
break;
|
||||
case 3:
|
||||
for (int j=0; j<8; j++) {
|
||||
nextDec=next3[j]>>4;
|
||||
DO_ONE_DEC(range3);
|
||||
nextDec=next3[j]&15;
|
||||
DO_ONE_DEC(range3);
|
||||
}
|
||||
o2=last2<<1;
|
||||
o1=last1<<1;
|
||||
out[0]=(range3<<4)|(filter<<2)|((i+16>=len)?((loopStart>=0)?3:1):0);
|
||||
out[1]=next3[0];
|
||||
out[2]=next3[1];
|
||||
out[3]=next3[2];
|
||||
out[4]=next3[3];
|
||||
out[5]=next3[4];
|
||||
out[6]=next3[5];
|
||||
out[7]=next3[6];
|
||||
out[8]=next3[7];
|
||||
break;
|
||||
// write
|
||||
out[0]=(range<<4)|(filter<<2)|((i+16>=len && loopStart<0)?1:0);
|
||||
for (int j=0; j<8; j++) {
|
||||
out[j+1]=possibleOut[filter][range][j];
|
||||
}
|
||||
|
||||
for (int j=0; j<4; j++) {
|
||||
for (int k=0; k<13; k++) {
|
||||
last1[j][k]=last1[filter][range];
|
||||
last2[j][k]=last2[filter][range];
|
||||
}
|
||||
}
|
||||
out+=9;
|
||||
total+=9;
|
||||
}
|
||||
// encode loop block
|
||||
if (loopStart>=0) {
|
||||
long p=loopStart;
|
||||
for (int i=0; i<17; i++) {
|
||||
if (p>=len) {
|
||||
p=loopStart;
|
||||
}
|
||||
in[i]=buf[p++];
|
||||
}
|
||||
|
||||
if (emphasis) {
|
||||
for (int j=0; j<17; j++) {
|
||||
x0=x1;
|
||||
x1=x2;
|
||||
x2=in[j];
|
||||
|
||||
if (j==0) continue;
|
||||
emphOut=((x1<<11)-x0*370-in[j]*374)/1305;
|
||||
if (emphOut<-32768) emphOut=-32768;
|
||||
if (emphOut>32767) emphOut=32767;
|
||||
in[j-1]=emphOut;
|
||||
}
|
||||
}
|
||||
|
||||
// encode (filter 0/1 only)
|
||||
for (int j=0; j<2; j++) {
|
||||
for (int k=0; k<13; k++) {
|
||||
brrEncodeBlock(in,possibleOut[j][k],k,j,&last1[j][k],&last2[j][k],&avgError[j][k]);
|
||||
}
|
||||
}
|
||||
|
||||
// find best filter/range
|
||||
int candError=0x7fffffff;
|
||||
for (int j=0; j<2; j++) {
|
||||
for (int k=0; k<13; k++) {
|
||||
if (avgError[j][k]<candError) {
|
||||
candError=avgError[j][k];
|
||||
filter=j;
|
||||
range=k;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// write
|
||||
out[0]=(range<<4)|(filter<<2)|3;
|
||||
for (int j=0; j<8; j++) {
|
||||
out[j+1]=possibleOut[filter][range][j];
|
||||
}
|
||||
|
||||
for (int j=0; j<4; j++) {
|
||||
for (int k=0; k<13; k++) {
|
||||
last1[j][k]=last1[filter][range];
|
||||
last2[j][k]=last2[filter][range];
|
||||
}
|
||||
}
|
||||
buf+=16;
|
||||
out+=9;
|
||||
total+=9;
|
||||
}
|
||||
|
@ -474,9 +349,11 @@ long brrEncode(short* buf, unsigned char* out, long len, long loopStart) {
|
|||
*out=next<<1; \
|
||||
out++;
|
||||
|
||||
long brrDecode(unsigned char* buf, short* out, long len) {
|
||||
long brrDecode(unsigned char* buf, short* out, long len, unsigned char emphasis) {
|
||||
if (len==0) return 0;
|
||||
|
||||
short* outOrig=out;
|
||||
|
||||
long total=0;
|
||||
|
||||
int last1=0;
|
||||
|
@ -503,5 +380,20 @@ long brrDecode(unsigned char* buf, short* out, long len) {
|
|||
buf+=9;
|
||||
}
|
||||
|
||||
if (emphasis) {
|
||||
short x0=0;
|
||||
short x1=0;
|
||||
short x2=0;
|
||||
for (long i=0; i<=total; i++) {
|
||||
x0=x1;
|
||||
x1=x2;
|
||||
x2=(i>=total)?0:outOrig[i];
|
||||
|
||||
if (i==0) continue;
|
||||
|
||||
outOrig[i-1]=(x0*370+x1*1305+x2*374)>>11;
|
||||
}
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
|
|
@ -30,21 +30,23 @@ extern "C" {
|
|||
/**
|
||||
* read len samples from buf, encode in BRR and output to out.
|
||||
* @param buf input data.
|
||||
* @param out output buffer. shall be at least 9*(len/16) shorts in size.
|
||||
* @param out output buffer. shall be at least 9*((15+len)/16) shorts in size (9 more if loopStart is not -1!)
|
||||
* @param len input length (should be a multiple of 16. if it isn't, the output will be padded).
|
||||
* @param loopStart beginning of loop area (may be -1 for no loop). this is used to ensure the respective block has no filter in order to loop properly.
|
||||
* @param emphasis apply filter to compensate for Gaussian interpolation high frequency loss.
|
||||
* @return number of written samples.
|
||||
*/
|
||||
long brrEncode(short* buf, unsigned char* out, long len, long loopStart);
|
||||
long brrEncode(short* buf, unsigned char* out, long len, long loopStart, unsigned char emphasis);
|
||||
|
||||
/**
|
||||
* read len bytes from buf, decode BRR and output to out.
|
||||
* @param buf input data.
|
||||
* @param out output buffer. shall be at least 16*(len/9) shorts in size.
|
||||
* @param len input length (shall be a multiple of 9).
|
||||
* @param emphasis apply filter to simulate Gaussian interpolation high frequency loss.
|
||||
* @return number of written bytes.
|
||||
*/
|
||||
long brrDecode(unsigned char* buf, short* out, long len);
|
||||
long brrDecode(unsigned char* buf, short* out, long len, unsigned char emphasis);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
36
src/engine/defines.h
Normal file
36
src/engine/defines.h
Normal file
|
@ -0,0 +1,36 @@
|
|||
/**
|
||||
* 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 _DEFINES_H
|
||||
#define _DEFINES_H
|
||||
|
||||
// global
|
||||
#define DIV_MAX_CHIPS 32
|
||||
#define DIV_MAX_CHANS 128
|
||||
#define DIV_MAX_PATTERNS 256
|
||||
|
||||
// in-pattern
|
||||
#define DIV_MAX_ROWS 256
|
||||
#define DIV_MAX_COLS 32
|
||||
#define DIV_MAX_EFFECTS 8
|
||||
|
||||
// sample related
|
||||
#define DIV_MAX_SAMPLE_TYPE 4
|
||||
|
||||
#endif
|
|
@ -884,10 +884,10 @@ void DivEngine::runExportThread() {
|
|||
break;
|
||||
}
|
||||
case DIV_EXPORT_MODE_MANY_SYS: {
|
||||
SNDFILE* sf[32];
|
||||
SF_INFO si[32];
|
||||
String fname[32];
|
||||
SFWrapper sfWrap[32];
|
||||
SNDFILE* sf[DIV_MAX_CHIPS];
|
||||
SF_INFO si[DIV_MAX_CHIPS];
|
||||
String fname[DIV_MAX_CHIPS];
|
||||
SFWrapper sfWrap[DIV_MAX_CHIPS];
|
||||
for (int i=0; i<song.systemLen; i++) {
|
||||
sf[i]=NULL;
|
||||
si[i].samplerate=got.rate;
|
||||
|
@ -915,7 +915,7 @@ void DivEngine::runExportThread() {
|
|||
float* outBuf[2];
|
||||
outBuf[0]=new float[EXPORT_BUFSIZE];
|
||||
outBuf[1]=new float[EXPORT_BUFSIZE];
|
||||
short* sysBuf[32];
|
||||
short* sysBuf[DIV_MAX_CHIPS];
|
||||
for (int i=0; i<song.systemLen; i++) {
|
||||
sysBuf[i]=new short[EXPORT_BUFSIZE*2];
|
||||
}
|
||||
|
@ -1399,7 +1399,7 @@ void DivEngine::initSongWithDesc(const char* description, bool inBase64) {
|
|||
c.loadFromMemory(description);
|
||||
}
|
||||
int index=0;
|
||||
for (; index<32; index++) {
|
||||
for (; index<DIV_MAX_CHIPS; index++) {
|
||||
song.system[index]=systemFromFileFur(c.getInt(fmt::sprintf("id%d",index),0));
|
||||
if (song.system[index]==DIV_SYSTEM_NULL) {
|
||||
break;
|
||||
|
@ -1456,7 +1456,7 @@ void DivEngine::swapChannels(int src, int dest) {
|
|||
return;
|
||||
}
|
||||
|
||||
for (int i=0; i<256; i++) {
|
||||
for (int i=0; i<DIV_MAX_PATTERNS; i++) {
|
||||
curOrders->ord[dest][i]^=curOrders->ord[src][i];
|
||||
curOrders->ord[src][i]^=curOrders->ord[dest][i];
|
||||
curOrders->ord[dest][i]^=curOrders->ord[src][i];
|
||||
|
@ -1487,7 +1487,7 @@ void DivEngine::swapChannels(int src, int dest) {
|
|||
|
||||
void DivEngine::stompChannel(int ch) {
|
||||
logV("stomping channel %d",ch);
|
||||
for (int i=0; i<256; i++) {
|
||||
for (int i=0; i<DIV_MAX_PATTERNS; i++) {
|
||||
curOrders->ord[ch][i]=0;
|
||||
}
|
||||
curPat[ch].wipePatterns();
|
||||
|
@ -1649,8 +1649,8 @@ void DivEngine::changeSystem(int index, DivSystem which, bool preserveOrder) {
|
|||
}
|
||||
|
||||
bool DivEngine::addSystem(DivSystem which) {
|
||||
if (song.systemLen>32) {
|
||||
lastError="max number of systems is 32";
|
||||
if (song.systemLen>DIV_MAX_CHIPS) {
|
||||
lastError=fmt::sprintf("max number of systems is %d",DIV_MAX_CHIPS);
|
||||
return false;
|
||||
}
|
||||
if (chans+getChannelCount(which)>DIV_MAX_CHANS) {
|
||||
|
@ -1786,7 +1786,7 @@ bool DivEngine::swapSystem(int src, int dest, bool preserveOrder) {
|
|||
|
||||
for (size_t i=0; i<song.subsong.size(); i++) {
|
||||
DivOrders prevOrders=song.subsong[i]->orders;
|
||||
DivPattern* prevPat[DIV_MAX_CHANS][256];
|
||||
DivPattern* prevPat[DIV_MAX_CHANS][DIV_MAX_PATTERNS];
|
||||
unsigned char prevEffectCols[DIV_MAX_CHANS];
|
||||
String prevChanName[DIV_MAX_CHANS];
|
||||
String prevChanShortName[DIV_MAX_CHANS];
|
||||
|
@ -1794,7 +1794,7 @@ bool DivEngine::swapSystem(int src, int dest, bool preserveOrder) {
|
|||
unsigned char prevChanCollapse[DIV_MAX_CHANS];
|
||||
|
||||
for (int j=0; j<tchans; j++) {
|
||||
for (int k=0; k<256; k++) {
|
||||
for (int k=0; k<DIV_MAX_PATTERNS; k++) {
|
||||
prevPat[j][k]=song.subsong[i]->pat[j].data[k];
|
||||
}
|
||||
prevEffectCols[j]=song.subsong[i]->pat[j].effectCols;
|
||||
|
@ -1806,7 +1806,7 @@ bool DivEngine::swapSystem(int src, int dest, bool preserveOrder) {
|
|||
}
|
||||
|
||||
for (int j=0; j<tchans; j++) {
|
||||
for (int k=0; k<256; k++) {
|
||||
for (int k=0; k<DIV_MAX_PATTERNS; k++) {
|
||||
song.subsong[i]->orders.ord[j][k]=prevOrders.ord[swappedChannels[j]][k];
|
||||
song.subsong[i]->pat[j].data[k]=prevPat[swappedChannels[j]][k];
|
||||
}
|
||||
|
@ -2768,7 +2768,7 @@ void DivEngine::delInstrument(int index) {
|
|||
song.insLen=song.ins.size();
|
||||
for (int i=0; i<chans; i++) {
|
||||
for (size_t j=0; j<song.subsong.size(); j++) {
|
||||
for (int k=0; k<256; k++) {
|
||||
for (int k=0; k<DIV_MAX_PATTERNS; k++) {
|
||||
if (song.subsong[j]->pat[i].data[k]==NULL) continue;
|
||||
for (int l=0; l<song.subsong[j]->patLen; l++) {
|
||||
if (song.subsong[j]->pat[i].data[k]->data[l][2]>index) {
|
||||
|
@ -3468,7 +3468,7 @@ void DivEngine::delSample(int index) {
|
|||
|
||||
void DivEngine::addOrder(bool duplicate, bool where) {
|
||||
unsigned char order[DIV_MAX_CHANS];
|
||||
if (curSubSong->ordersLen>=0xff) return;
|
||||
if (curSubSong->ordersLen>=(DIV_MAX_PATTERNS-1)) return;
|
||||
memset(order,0,DIV_MAX_CHANS);
|
||||
BUSY_BEGIN_SOFT;
|
||||
if (duplicate) {
|
||||
|
@ -3476,14 +3476,14 @@ void DivEngine::addOrder(bool duplicate, bool where) {
|
|||
order[i]=curOrders->ord[i][curOrder];
|
||||
}
|
||||
} else {
|
||||
bool used[256];
|
||||
bool used[DIV_MAX_PATTERNS];
|
||||
for (int i=0; i<chans; i++) {
|
||||
memset(used,0,sizeof(bool)*256);
|
||||
memset(used,0,sizeof(bool)*DIV_MAX_PATTERNS);
|
||||
for (int j=0; j<curSubSong->ordersLen; j++) {
|
||||
used[curOrders->ord[i][j]]=true;
|
||||
}
|
||||
order[i]=0xff;
|
||||
for (int j=0; j<256; j++) {
|
||||
order[i]=(DIV_MAX_PATTERNS-1);
|
||||
for (int j=0; j<DIV_MAX_PATTERNS; j++) {
|
||||
if (!used[j]) {
|
||||
order[i]=j;
|
||||
break;
|
||||
|
@ -3518,7 +3518,7 @@ void DivEngine::addOrder(bool duplicate, bool where) {
|
|||
|
||||
void DivEngine::deepCloneOrder(bool where) {
|
||||
unsigned char order[DIV_MAX_CHANS];
|
||||
if (curSubSong->ordersLen>=0xff) return;
|
||||
if (curSubSong->ordersLen>=(DIV_MAX_PATTERNS-1)) return;
|
||||
warnings="";
|
||||
BUSY_BEGIN_SOFT;
|
||||
for (int i=0; i<chans; i++) {
|
||||
|
@ -3526,14 +3526,14 @@ void DivEngine::deepCloneOrder(bool where) {
|
|||
logD("channel %d",i);
|
||||
order[i]=curOrders->ord[i][curOrder];
|
||||
// find free slot
|
||||
for (int j=0; j<256; j++) {
|
||||
for (int j=0; j<DIV_MAX_PATTERNS; j++) {
|
||||
logD("finding free slot in %d...",j);
|
||||
if (curPat[i].data[j]==NULL) {
|
||||
int origOrd=order[i];
|
||||
order[i]=j;
|
||||
DivPattern* oldPat=curPat[i].getPattern(origOrd,false);
|
||||
DivPattern* pat=curPat[i].getPattern(j,true);
|
||||
memcpy(pat->data,oldPat->data,256*32*sizeof(short));
|
||||
memcpy(pat->data,oldPat->data,DIV_MAX_ROWS*DIV_MAX_COLS*sizeof(short));
|
||||
logD("found at %d",j);
|
||||
didNotFind=false;
|
||||
break;
|
||||
|
@ -3629,7 +3629,7 @@ void DivEngine::moveOrderDown() {
|
|||
void DivEngine::exchangeIns(int one, int two) {
|
||||
for (int i=0; i<chans; i++) {
|
||||
for (size_t j=0; j<song.subsong.size(); j++) {
|
||||
for (int k=0; k<256; k++) {
|
||||
for (int k=0; k<DIV_MAX_PATTERNS; k++) {
|
||||
if (song.subsong[j]->pat[i].data[k]==NULL) continue;
|
||||
for (int l=0; l<song.subsong[j]->patLen; l++) {
|
||||
if (song.subsong[j]->pat[i].data[k]->data[l][2]==one) {
|
||||
|
|
|
@ -47,8 +47,8 @@
|
|||
#define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock();
|
||||
#define BUSY_END isBusy.unlock(); softLocked=false;
|
||||
|
||||
#define DIV_VERSION "dev128"
|
||||
#define DIV_ENGINE_VERSION 128
|
||||
#define DIV_VERSION "dev129"
|
||||
#define DIV_ENGINE_VERSION 129
|
||||
// for imports
|
||||
#define DIV_VERSION_MOD 0xff01
|
||||
#define DIV_VERSION_FC 0xff02
|
||||
|
@ -318,7 +318,7 @@ enum DivChanTypes {
|
|||
extern const char* cmdName[];
|
||||
|
||||
class DivEngine {
|
||||
DivDispatchContainer disCont[32];
|
||||
DivDispatchContainer disCont[DIV_MAX_CHIPS];
|
||||
TAAudio* output;
|
||||
TAAudioDesc want, got;
|
||||
String exportPath;
|
||||
|
|
|
@ -1581,7 +1581,7 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
|||
unsigned int wavePtr[256];
|
||||
unsigned int samplePtr[256];
|
||||
unsigned int subSongPtr[256];
|
||||
unsigned int sysFlagsPtr[32];
|
||||
unsigned int sysFlagsPtr[DIV_MAX_CHIPS];
|
||||
std::vector<int> patPtr;
|
||||
int numberOfSubSongs=0;
|
||||
char magic[5];
|
||||
|
@ -1760,7 +1760,7 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
|||
delete[] file;
|
||||
return false;
|
||||
}
|
||||
if (subSong->patLen>256) {
|
||||
if (subSong->patLen>DIV_MAX_ROWS) {
|
||||
logE("pattern length is too large!");
|
||||
lastError="pattern length is too large!";
|
||||
delete[] file;
|
||||
|
@ -1772,7 +1772,7 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
|||
delete[] file;
|
||||
return false;
|
||||
}
|
||||
if (subSong->ordersLen>256) {
|
||||
if (subSong->ordersLen>DIV_MAX_PATTERNS) {
|
||||
logE("song is too long!");
|
||||
lastError="song is too long!";
|
||||
delete[] file;
|
||||
|
@ -1804,7 +1804,7 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
|||
}
|
||||
|
||||
logD("systems:");
|
||||
for (int i=0; i<32; i++) {
|
||||
for (int i=0; i<DIV_MAX_CHIPS; i++) {
|
||||
unsigned char sysID=reader.readC();
|
||||
ds.system[i]=systemFromFileFur(sysID);
|
||||
logD("- %d: %.2x (%s)",i,sysID,getSystemName(ds.system[i]));
|
||||
|
@ -1826,7 +1826,7 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
|||
}
|
||||
|
||||
// system volume
|
||||
for (int i=0; i<32; i++) {
|
||||
for (int i=0; i<DIV_MAX_CHIPS; i++) {
|
||||
ds.systemVol[i]=reader.readC();
|
||||
if (ds.version<59 && ds.system[i]==DIV_SYSTEM_NES) {
|
||||
ds.systemVol[i]/=4;
|
||||
|
@ -1834,15 +1834,15 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
|||
}
|
||||
|
||||
// system panning
|
||||
for (int i=0; i<32; i++) ds.systemPan[i]=reader.readC();
|
||||
for (int i=0; i<DIV_MAX_CHIPS; i++) ds.systemPan[i]=reader.readC();
|
||||
|
||||
// system props
|
||||
for (int i=0; i<32; i++) {
|
||||
for (int i=0; i<DIV_MAX_CHIPS; i++) {
|
||||
sysFlagsPtr[i]=reader.readI();
|
||||
}
|
||||
|
||||
// handle compound systems
|
||||
for (int i=0; i<32; i++) {
|
||||
for (int i=0; i<DIV_MAX_CHIPS; i++) {
|
||||
if (ds.system[i]==DIV_SYSTEM_GENESIS ||
|
||||
ds.system[i]==DIV_SYSTEM_GENESIS_EXT ||
|
||||
ds.system[i]==DIV_SYSTEM_ARCADE) {
|
||||
|
@ -1851,7 +1851,7 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
|||
ds.systemVol[j]=ds.systemVol[j-1];
|
||||
ds.systemPan[j]=ds.systemPan[j-1];
|
||||
}
|
||||
if (++ds.systemLen>32) ds.systemLen=32;
|
||||
if (++ds.systemLen>DIV_MAX_CHIPS) ds.systemLen=DIV_MAX_CHIPS;
|
||||
|
||||
if (ds.system[i]==DIV_SYSTEM_GENESIS) {
|
||||
ds.system[i]=DIV_SYSTEM_YM2612;
|
||||
|
@ -2000,7 +2000,7 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
|||
|
||||
for (int i=0; i<tchans; i++) {
|
||||
subSong->pat[i].effectCols=reader.readC();
|
||||
if (subSong->pat[i].effectCols<1 || subSong->pat[i].effectCols>8) {
|
||||
if (subSong->pat[i].effectCols<1 || subSong->pat[i].effectCols>DIV_MAX_EFFECTS) {
|
||||
logE("channel %d has zero or too many effect columns! (%d)",i,subSong->pat[i].effectCols);
|
||||
lastError=fmt::sprintf("channel %d has too many effect columns! (%d)",i,subSong->pat[i].effectCols);
|
||||
delete[] file;
|
||||
|
@ -2198,7 +2198,7 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
|||
// read system flags
|
||||
if (ds.version>=119) {
|
||||
logD("reading chip flags...");
|
||||
for (int i=0; i<32; i++) {
|
||||
for (int i=0; i<DIV_MAX_CHIPS; i++) {
|
||||
if (sysFlagsPtr[i]==0) continue;
|
||||
|
||||
if (!reader.seek(sysFlagsPtr[i],SEEK_SET)) {
|
||||
|
@ -2416,7 +2416,7 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
|||
delete[] file;
|
||||
return false;
|
||||
}
|
||||
if (index<0 || index>255) {
|
||||
if (index<0 || index>(DIV_MAX_PATTERNS-1)) {
|
||||
logE("pattern index out of range!",i);
|
||||
lastError="pattern index out of range!";
|
||||
ds.unload();
|
||||
|
@ -4175,14 +4175,14 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
|
|||
|
||||
// fail if values are out of range
|
||||
/*
|
||||
if (subSong->ordersLen>256) {
|
||||
logE("maximum song length is 256!");
|
||||
lastError="maximum song length is 256";
|
||||
if (subSong->ordersLen>DIV_MAX_PATTERNS) {
|
||||
logE("maximum song length is %d!",DIV_MAX_PATTERNS);
|
||||
lastError=fmt::sprintf("maximum song length is %d",DIV_MAX_PATTERNS);
|
||||
return NULL;
|
||||
}
|
||||
if (subSong->patLen>256) {
|
||||
logE("maximum pattern length is 256!");
|
||||
lastError="maximum pattern length is 256";
|
||||
if (subSong->patLen>DIV_MAX_ROWS) {
|
||||
logE("maximum pattern length is %d!",DIV_MAX_ROWS);
|
||||
lastError=fmt::sprintf("maximum pattern length is %d",DIV_MAX_ROWS);
|
||||
return NULL;
|
||||
}
|
||||
*/
|
||||
|
@ -4236,18 +4236,18 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
|
|||
for (int i=0; i<chans; i++) {
|
||||
for (size_t j=0; j<song.subsong.size(); j++) {
|
||||
DivSubSong* subs=song.subsong[j];
|
||||
for (int k=0; k<256; k++) {
|
||||
for (int k=0; k<DIV_MAX_PATTERNS; k++) {
|
||||
if (subs->pat[i].data[k]==NULL) continue;
|
||||
patsToWrite.push_back(PatToWrite(j,i,k));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
bool alreadyAdded[256];
|
||||
bool alreadyAdded[DIV_MAX_PATTERNS];
|
||||
for (int i=0; i<chans; i++) {
|
||||
for (size_t j=0; j<song.subsong.size(); j++) {
|
||||
DivSubSong* subs=song.subsong[j];
|
||||
memset(alreadyAdded,0,256*sizeof(bool));
|
||||
memset(alreadyAdded,0,DIV_MAX_PATTERNS*sizeof(bool));
|
||||
for (int k=0; k<subs->ordersLen; k++) {
|
||||
if (alreadyAdded[subs->orders.ord[i][k]]) continue;
|
||||
patsToWrite.push_back(PatToWrite(j,i,subs->orders.ord[i][k]));
|
||||
|
@ -4276,7 +4276,7 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
|
|||
w->writeS(song.sampleLen);
|
||||
w->writeI(patsToWrite.size());
|
||||
|
||||
for (int i=0; i<32; i++) {
|
||||
for (int i=0; i<DIV_MAX_CHIPS; i++) {
|
||||
if (i>=song.systemLen) {
|
||||
w->writeC(0);
|
||||
} else {
|
||||
|
@ -4284,17 +4284,17 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
|
|||
}
|
||||
}
|
||||
|
||||
for (int i=0; i<32; i++) {
|
||||
for (int i=0; i<DIV_MAX_CHIPS; i++) {
|
||||
w->writeC(song.systemVol[i]);
|
||||
}
|
||||
|
||||
for (int i=0; i<32; i++) {
|
||||
for (int i=0; i<DIV_MAX_CHIPS; i++) {
|
||||
w->writeC(song.systemPan[i]);
|
||||
}
|
||||
|
||||
// chip flags (we'll seek here later)
|
||||
sysFlagsPtrSeek=w->tell();
|
||||
for (int i=0; i<32; i++) {
|
||||
for (int i=0; i<DIV_MAX_CHIPS; i++) {
|
||||
w->writeI(0);
|
||||
}
|
||||
|
||||
|
|
|
@ -21,10 +21,10 @@
|
|||
#define _ORDERS_H
|
||||
|
||||
struct DivOrders {
|
||||
unsigned char ord[DIV_MAX_CHANS][256];
|
||||
unsigned char ord[DIV_MAX_CHANS][DIV_MAX_PATTERNS];
|
||||
|
||||
DivOrders() {
|
||||
memset(ord,0,DIV_MAX_CHANS*256);
|
||||
memset(ord,0,DIV_MAX_CHANS*DIV_MAX_PATTERNS);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -23,8 +23,8 @@
|
|||
static DivPattern emptyPat;
|
||||
|
||||
DivPattern::DivPattern() {
|
||||
memset(data,-1,256*32*sizeof(short));
|
||||
for (int i=0; i<256; i++) {
|
||||
memset(data,-1,DIV_MAX_ROWS*DIV_MAX_COLS*sizeof(short));
|
||||
for (int i=0; i<DIV_MAX_ROWS; i++) {
|
||||
data[i][0]=0;
|
||||
data[i][1]=0;
|
||||
}
|
||||
|
@ -43,13 +43,13 @@ DivPattern* DivChannelData::getPattern(int index, bool create) {
|
|||
|
||||
std::vector<std::pair<int,int>> DivChannelData::optimize() {
|
||||
std::vector<std::pair<int,int>> ret;
|
||||
for (int i=0; i<256; i++) {
|
||||
for (int i=0; i<DIV_MAX_PATTERNS; i++) {
|
||||
if (data[i]!=NULL) {
|
||||
// compare
|
||||
for (int j=0; j<256; j++) {
|
||||
for (int j=0; j<DIV_MAX_PATTERNS; j++) {
|
||||
if (j==i) continue;
|
||||
if (data[j]==NULL) continue;
|
||||
if (memcmp(data[i]->data,data[j]->data,256*32*sizeof(short))==0) {
|
||||
if (memcmp(data[i]->data,data[j]->data,DIV_MAX_ROWS*DIV_MAX_COLS*sizeof(short))==0) {
|
||||
delete data[j];
|
||||
data[j]=NULL;
|
||||
logV("%d == %d",i,j);
|
||||
|
@ -63,15 +63,15 @@ std::vector<std::pair<int,int>> DivChannelData::optimize() {
|
|||
|
||||
std::vector<std::pair<int,int>> DivChannelData::rearrange() {
|
||||
std::vector<std::pair<int,int>> ret;
|
||||
for (int i=0; i<256; i++) {
|
||||
for (int i=0; i<DIV_MAX_PATTERNS; i++) {
|
||||
if (data[i]==NULL) {
|
||||
for (int j=i; j<256; j++) {
|
||||
for (int j=i; j<DIV_MAX_PATTERNS; j++) {
|
||||
if (data[j]!=NULL) {
|
||||
data[i]=data[j];
|
||||
data[j]=NULL;
|
||||
logV("%d -> %d",j,i);
|
||||
ret.push_back(std::pair<int,int>(j,i));
|
||||
if (++i>=256) break;
|
||||
if (++i>=DIV_MAX_PATTERNS) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -80,7 +80,7 @@ std::vector<std::pair<int,int>> DivChannelData::rearrange() {
|
|||
}
|
||||
|
||||
void DivChannelData::wipePatterns() {
|
||||
for (int i=0; i<256; i++) {
|
||||
for (int i=0; i<DIV_MAX_PATTERNS; i++) {
|
||||
if (data[i]!=NULL) {
|
||||
delete data[i];
|
||||
data[i]=NULL;
|
||||
|
@ -95,5 +95,5 @@ void DivPattern::copyOn(DivPattern* dest) {
|
|||
|
||||
DivChannelData::DivChannelData():
|
||||
effectCols(1) {
|
||||
memset(data,0,256*sizeof(void*));
|
||||
memset(data,0,DIV_MAX_PATTERNS*sizeof(void*));
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
|
||||
struct DivPattern {
|
||||
String name;
|
||||
short data[256][32];
|
||||
short data[DIV_MAX_ROWS][DIV_MAX_COLS];
|
||||
|
||||
/**
|
||||
* copy this pattern to another.
|
||||
|
@ -42,7 +42,7 @@ struct DivChannelData {
|
|||
// 3: volume
|
||||
// 4-5+: effect/effect value
|
||||
// do NOT access directly unless you know what you're doing!
|
||||
DivPattern* data[256];
|
||||
DivPattern* data[DIV_MAX_PATTERNS];
|
||||
|
||||
/**
|
||||
* get a pattern from this channel, or the empty pattern if not initialized.
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
#include "sound/c64_fp/siddefs-fp.h"
|
||||
#include <math.h>
|
||||
|
||||
#define rWrite(a,v) if (!skipRegisterWrites) {if (isFP) {sid_fp.write(a,v);} else {sid.write(a,v);}; regPool[(a)&0x1f]=v; if (dumpWrites) {addWrite(a,v);} }
|
||||
#define rWrite(a,v) if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(a,v);} }
|
||||
|
||||
#define CHIP_FREQBASE 524288
|
||||
|
||||
|
@ -66,6 +66,16 @@ const char** DivPlatformC64::getRegisterSheet() {
|
|||
void DivPlatformC64::acquire(short* bufL, short* bufR, size_t start, size_t len) {
|
||||
int dcOff=isFP?0:sid.get_dc(0);
|
||||
for (size_t i=start; i<start+len; i++) {
|
||||
if (!writes.empty()) {
|
||||
QueuedWrite w=writes.front();
|
||||
if (isFP) {
|
||||
sid_fp.write(w.addr,w.val);
|
||||
} else {
|
||||
sid.write(w.addr,w.val);
|
||||
};
|
||||
regPool[w.addr&0x1f]=w.val;
|
||||
writes.pop();
|
||||
}
|
||||
if (isFP) {
|
||||
sid_fp.clock(4,&bufL[i]);
|
||||
if (++writeOscBuf>=4) {
|
||||
|
@ -483,6 +493,7 @@ float DivPlatformC64::getPostAmp() {
|
|||
}
|
||||
|
||||
void DivPlatformC64::reset() {
|
||||
while (!writes.empty()) writes.pop();
|
||||
for (int i=0; i<3; i++) {
|
||||
chan[i]=DivPlatformC64::Channel();
|
||||
chan[i].std.setEngine(parent);
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#define _C64_H
|
||||
|
||||
#include "../dispatch.h"
|
||||
#include <queue>
|
||||
#include "../macroInt.h"
|
||||
#include "sound/c64/sid.h"
|
||||
#include "sound/c64_fp/SID.h"
|
||||
|
@ -61,6 +62,12 @@ class DivPlatformC64: public DivDispatch {
|
|||
Channel chan[3];
|
||||
DivDispatchOscBuffer* oscBuf[3];
|
||||
bool isMuted[3];
|
||||
struct QueuedWrite {
|
||||
unsigned char addr;
|
||||
unsigned char val;
|
||||
QueuedWrite(unsigned char a, unsigned char v): addr(a), val(v) {}
|
||||
};
|
||||
std::queue<QueuedWrite> writes;
|
||||
|
||||
unsigned char filtControl, filtRes, vol;
|
||||
unsigned char writeOscBuf;
|
||||
|
|
|
@ -484,9 +484,6 @@ void DivPlatformNamcoWSG::reset() {
|
|||
namco->set_voices(chans);
|
||||
namco->set_stereo((devType==2 || devType==30));
|
||||
namco->device_start(NULL);
|
||||
lastPan=0xff;
|
||||
cycles=0;
|
||||
curChan=-1;
|
||||
}
|
||||
|
||||
bool DivPlatformNamcoWSG::isStereo() {
|
||||
|
|
|
@ -53,9 +53,7 @@ class DivPlatformNamcoWSG: public DivDispatch {
|
|||
QueuedWrite(unsigned short a, unsigned char v): addr(a), val(v) {}
|
||||
};
|
||||
std::queue<QueuedWrite> writes;
|
||||
unsigned char lastPan;
|
||||
|
||||
int cycles, curChan, delay;
|
||||
namco_audio_device* namco;
|
||||
int devType, chans;
|
||||
unsigned char regPool[512];
|
||||
|
|
|
@ -783,7 +783,7 @@ void DivPlatformQSound::renderSamples(int sysID) {
|
|||
for (int i=0; i<length; i++) {
|
||||
sampleMem[(memPos+i)]=s->dataQSoundA[i];
|
||||
}
|
||||
sampleLoaded[i]=true;
|
||||
sampleLoadedBS[i]=true;
|
||||
}
|
||||
offBS[i]=memPos;
|
||||
memPos+=length+16;
|
||||
|
|
|
@ -125,6 +125,8 @@ double DivPlatformSMS::NOTE_SN(int ch, int note) {
|
|||
if (parent->song.linearPitch==2 || !easyNoise) {
|
||||
return NOTE_PERIODIC(note);
|
||||
}
|
||||
int easyStartingPeriod=16;
|
||||
int easyThreshold=round(12.0*log((chipClock/(easyStartingPeriod*CHIP_DIVIDER))/(0.0625*parent->song.tuning))/log(2.0))-3;
|
||||
if (note>easyThreshold) {
|
||||
return MAX(0,easyStartingPeriod-(note-easyThreshold));
|
||||
}
|
||||
|
@ -132,19 +134,23 @@ double DivPlatformSMS::NOTE_SN(int ch, int note) {
|
|||
}
|
||||
|
||||
int DivPlatformSMS::snCalcFreq(int ch) {
|
||||
if (parent->song.linearPitch==2 && easyNoise && chan[ch].baseFreq+chan[ch].pitch+chan[ch].pitch2>(easyThreshold<<7)) {
|
||||
int ret=(((easyStartingPeriod<<7)+0x40)-(chan[ch].baseFreq+chan[ch].pitch+chan[ch].pitch2-(easyThreshold<<7)))>>7;
|
||||
double CHIP_DIVIDER=toneDivider;
|
||||
if (ch==3) CHIP_DIVIDER=noiseDivider;
|
||||
int easyStartingPeriod=16;
|
||||
int easyThreshold=round(128.0*12.0*log((chipClock/(easyStartingPeriod*CHIP_DIVIDER))/(0.0625*parent->song.tuning))/log(2.0))-384+64;
|
||||
if (parent->song.linearPitch==2 && easyNoise && chan[ch].baseFreq+chan[ch].pitch+chan[ch].pitch2>(easyThreshold)) {
|
||||
int ret=(((easyStartingPeriod<<7))-(chan[ch].baseFreq+chan[ch].pitch+chan[ch].pitch2-(easyThreshold)))>>7;
|
||||
if (ret<0) ret=0;
|
||||
return ret;
|
||||
}
|
||||
return parent->calcFreq(chan[ch].baseFreq,chan[ch].pitch,true,0,chan[ch].pitch2,chipClock,ch==3?noiseDivider:toneDivider);
|
||||
return parent->calcFreq(chan[ch].baseFreq,chan[ch].pitch,true,0,chan[ch].pitch2,chipClock,CHIP_DIVIDER);
|
||||
}
|
||||
|
||||
void DivPlatformSMS::tick(bool sysTick) {
|
||||
for (int i=0; i<4; i++) {
|
||||
chan[i].std.next();
|
||||
if (chan[i].std.vol.had) {
|
||||
chan[i].outVol=VOL_SCALE_LOG_BROKEN(chan[i].std.vol.val,chan[i].vol,15);
|
||||
chan[i].outVol=VOL_SCALE_LOG(chan[i].std.vol.val,chan[i].vol,15);
|
||||
if (chan[i].outVol<0) chan[i].outVol=0;
|
||||
// old formula
|
||||
// ((chan[i].vol&15)*MIN(15,chan[i].std.vol.val))>>4;
|
||||
|
@ -152,10 +158,9 @@ void DivPlatformSMS::tick(bool sysTick) {
|
|||
}
|
||||
if (chan[i].std.arp.had) {
|
||||
if (!chan[i].inPorta) {
|
||||
// TODO: check whether this weird octave boundary thing applies to other systems as well
|
||||
// TODO: add compatibility flag. this is horrible.
|
||||
int areYouSerious=parent->calcArp(chan[i].note,chan[i].std.arp.val);
|
||||
while (areYouSerious>0x60) areYouSerious-=12;
|
||||
if (!easyNoise) while (areYouSerious>0x60) areYouSerious-=12;
|
||||
chan[i].baseFreq=NOTE_SN(i,areYouSerious);
|
||||
chan[i].actualNote=areYouSerious;
|
||||
chan[i].freqChanged=true;
|
||||
|
@ -422,7 +427,7 @@ void DivPlatformSMS::reset() {
|
|||
YMPSG_Init(&sn_nuked,isRealSN,12,isRealSN?13:15,isRealSN?16383:32767);
|
||||
snNoiseMode=3;
|
||||
rWrite(0,0xe7);
|
||||
updateSNMode=false;
|
||||
updateSNMode=true;
|
||||
oldValue=0xff;
|
||||
lastPan=0xff;
|
||||
if (stereo) {
|
||||
|
@ -464,40 +469,27 @@ void DivPlatformSMS::setFlags(const DivConfig& flags) {
|
|||
switch (flags.getInt("clockSel",0)) {
|
||||
case 1:
|
||||
chipClock=COLOR_PAL*4.0/5.0;
|
||||
easyThreshold=84;
|
||||
easyStartingPeriod=13;
|
||||
break;
|
||||
case 2:
|
||||
chipClock=4000000;
|
||||
easyThreshold=86;
|
||||
easyStartingPeriod=13;
|
||||
break;
|
||||
case 3:
|
||||
chipClock=COLOR_NTSC/2.0;
|
||||
easyThreshold=72;
|
||||
easyStartingPeriod=13;
|
||||
break;
|
||||
case 4:
|
||||
chipClock=3000000;
|
||||
easyThreshold=81;
|
||||
easyStartingPeriod=13;
|
||||
break;
|
||||
case 5:
|
||||
chipClock=2000000;
|
||||
easyThreshold=74;
|
||||
easyStartingPeriod=13;
|
||||
break;
|
||||
case 6:
|
||||
chipClock=COLOR_NTSC/8.0;
|
||||
easyThreshold=48;
|
||||
easyStartingPeriod=13;
|
||||
break;
|
||||
default:
|
||||
chipClock=COLOR_NTSC;
|
||||
easyThreshold=84;
|
||||
easyStartingPeriod=13;
|
||||
break;
|
||||
}
|
||||
CHECK_CUSTOM_CLOCK;
|
||||
resetPhase=!flags.getBool("noPhaseReset",false);
|
||||
easyNoise=!flags.getBool("noEasyNoise",false);
|
||||
divider=16;
|
||||
|
@ -568,7 +560,7 @@ void DivPlatformSMS::setFlags(const DivConfig& flags) {
|
|||
stereo=false;
|
||||
break;
|
||||
}
|
||||
CHECK_CUSTOM_CLOCK;
|
||||
|
||||
rate=chipClock/divider;
|
||||
for (int i=0; i<4; i++) {
|
||||
oscBuf[i]->rate=rate;
|
||||
|
|
|
@ -52,8 +52,6 @@ class DivPlatformSMS: public DivDispatch {
|
|||
int divider=16;
|
||||
double toneDivider=64.0;
|
||||
double noiseDivider=64.0;
|
||||
int easyThreshold;
|
||||
int easyStartingPeriod;
|
||||
bool updateSNMode;
|
||||
bool resetPhase;
|
||||
bool isRealSN;
|
||||
|
|
|
@ -214,13 +214,13 @@ void DivPlatformSNES::tick(bool sysTick) {
|
|||
loop=start;
|
||||
} else if (chan[i].sample>=0 && chan[i].sample<parent->song.sampleLen) {
|
||||
start=sampleOff[chan[i].sample];
|
||||
end=MIN(start+MAX(s->lengthBRR,1),getSampleMemCapacity());
|
||||
end=MIN(start+MAX(s->lengthBRR+((s->loop && s->depth!=DIV_SAMPLE_DEPTH_BRR)?9:0),1),getSampleMemCapacity());
|
||||
loop=MAX(start,end-1);
|
||||
if (chan[i].audPos>0) {
|
||||
start=start+MIN(chan[i].audPos,s->lengthBRR-1)/16*9;
|
||||
}
|
||||
if (s->loopStart>=0) {
|
||||
loop=start+s->loopStart/16*9;
|
||||
loop=((s->depth!=DIV_SAMPLE_DEPTH_BRR)?9:0)+start+((s->loopStart/16)*9);
|
||||
}
|
||||
} else {
|
||||
start=0;
|
||||
|
@ -817,7 +817,7 @@ void DivPlatformSNES::renderSamples(int sysID) {
|
|||
continue;
|
||||
}
|
||||
|
||||
int length=s->lengthBRR;
|
||||
int length=s->lengthBRR+((s->loop && s->depth!=DIV_SAMPLE_DEPTH_BRR)?9:0);
|
||||
int actualLength=MIN((int)(getSampleMemCapacity()-memPos)/9*9,length);
|
||||
if (actualLength>0) {
|
||||
sampleOff[i]=memPos;
|
||||
|
|
|
@ -80,7 +80,7 @@ void DivPlatformVIC20::calcAndWriteOutVol(int ch, int env) {
|
|||
}
|
||||
|
||||
void DivPlatformVIC20::writeOutVol(int ch) {
|
||||
if (!isMuted[ch]) {
|
||||
if (!isMuted[ch] && chan[ch].active) {
|
||||
rWrite(14,chan[ch].outVol);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1581,7 +1581,11 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi
|
|||
if (disCont[i].lastAvail>0) {
|
||||
disCont[i].flush(disCont[i].lastAvail);
|
||||
}
|
||||
disCont[i].runtotal=blip_clocks_needed(disCont[i].bb[0],size-disCont[i].lastAvail);
|
||||
if (size<disCont[i].lastAvail) {
|
||||
disCont[i].runtotal=0;
|
||||
} else {
|
||||
disCont[i].runtotal=blip_clocks_needed(disCont[i].bb[0],size-disCont[i].lastAvail);
|
||||
}
|
||||
if (disCont[i].runtotal>disCont[i].bbInLen) {
|
||||
logV("growing dispatch %d bbIn to %d",i,disCont[i].runtotal+256);
|
||||
delete[] disCont[i].bbIn[0];
|
||||
|
|
|
@ -52,14 +52,14 @@ void DivSample::putSampleData(SafeWriter* w) {
|
|||
w->writeI(centerRate);
|
||||
w->writeC(depth);
|
||||
w->writeC(loopMode);
|
||||
w->writeC(brrEmphasis);
|
||||
w->writeC(0); // reserved
|
||||
w->writeC(0);
|
||||
w->writeI(loop?loopStart:-1);
|
||||
w->writeI(loop?loopEnd:-1);
|
||||
|
||||
for (int i=0; i<4; i++) {
|
||||
for (int i=0; i<DIV_MAX_SAMPLE_TYPE; i++) {
|
||||
unsigned int out=0;
|
||||
for (int j=0; j<32; j++) {
|
||||
for (int j=0; j<DIV_MAX_CHIPS; j++) {
|
||||
if (renderOn[i][j]) out|=1<<j;
|
||||
}
|
||||
w->writeI(out);
|
||||
|
@ -125,17 +125,21 @@ DivDataErrors DivSample::readSampleData(SafeReader& reader, short version) {
|
|||
reader.readC();
|
||||
}
|
||||
|
||||
if (version>=129) {
|
||||
brrEmphasis=reader.readC();
|
||||
} else {
|
||||
reader.readC();
|
||||
}
|
||||
// reserved
|
||||
reader.readC();
|
||||
reader.readC();
|
||||
|
||||
loopStart=reader.readI();
|
||||
loopEnd=reader.readI();
|
||||
loop=(loopStart>=0)&&(loopEnd>=0);
|
||||
|
||||
for (int i=0; i<4; i++) {
|
||||
for (int i=0; i<DIV_MAX_SAMPLE_TYPE; i++) {
|
||||
unsigned int outMask=(unsigned int)reader.readI();
|
||||
for (int j=0; j<32; j++) {
|
||||
for (int j=0; j<DIV_MAX_CHIPS; j++) {
|
||||
renderOn[i][j]=outMask&(1<<j);
|
||||
}
|
||||
}
|
||||
|
@ -490,8 +494,8 @@ bool DivSample::initInternal(DivSampleDepth d, int count) {
|
|||
case DIV_SAMPLE_DEPTH_BRR: // BRR
|
||||
if (dataBRR!=NULL) delete[] dataBRR;
|
||||
lengthBRR=9*((count+15)/16);
|
||||
dataBRR=new unsigned char[lengthBRR];
|
||||
memset(dataBRR,0,lengthBRR);
|
||||
dataBRR=new unsigned char[lengthBRR+9];
|
||||
memset(dataBRR,0,lengthBRR+9);
|
||||
break;
|
||||
case DIV_SAMPLE_DEPTH_VOX: // VOX
|
||||
if (dataVOX!=NULL) delete[] dataVOX;
|
||||
|
@ -1041,7 +1045,7 @@ void DivSample::render(unsigned int formatMask) {
|
|||
}
|
||||
break;
|
||||
case DIV_SAMPLE_DEPTH_BRR: // BRR
|
||||
brrDecode(dataBRR,data16,lengthBRR);
|
||||
brrDecode(dataBRR,data16,lengthBRR,brrEmphasis);
|
||||
break;
|
||||
case DIV_SAMPLE_DEPTH_VOX: // VOX
|
||||
oki_decode(dataVOX,data16,samples);
|
||||
|
@ -1100,7 +1104,7 @@ void DivSample::render(unsigned int formatMask) {
|
|||
}
|
||||
if (NOT_IN_FORMAT(DIV_SAMPLE_DEPTH_BRR)) { // BRR
|
||||
if (!initInternal(DIV_SAMPLE_DEPTH_BRR,samples)) return;
|
||||
brrEncode(data16,dataBRR,(samples+15)&(~15),loop?loopStart:-1);
|
||||
brrEncode(data16,dataBRR,samples,loop?loopStart:-1,brrEmphasis);
|
||||
}
|
||||
if (NOT_IN_FORMAT(DIV_SAMPLE_DEPTH_VOX)) { // VOX
|
||||
if (!initInternal(DIV_SAMPLE_DEPTH_VOX,samples)) return;
|
||||
|
@ -1174,9 +1178,9 @@ DivSampleHistory* DivSample::prepareUndo(bool data, bool doNotPush) {
|
|||
duplicate=new unsigned char[getCurBufLen()];
|
||||
memcpy(duplicate,getCurBuf(),getCurBufLen());
|
||||
}
|
||||
h=new DivSampleHistory(duplicate,getCurBufLen(),samples,depth,rate,centerRate,loopStart,loopEnd,loop,loopMode);
|
||||
h=new DivSampleHistory(duplicate,getCurBufLen(),samples,depth,rate,centerRate,loopStart,loopEnd,loop,brrEmphasis,loopMode);
|
||||
} else {
|
||||
h=new DivSampleHistory(depth,rate,centerRate,loopStart,loopEnd,loop,loopMode);
|
||||
h=new DivSampleHistory(depth,rate,centerRate,loopStart,loopEnd,loop,brrEmphasis,loopMode);
|
||||
}
|
||||
if (!doNotPush) {
|
||||
while (!redoHist.empty()) {
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#define _SAMPLE_H
|
||||
|
||||
#include "../ta-utils.h"
|
||||
#include "defines.h"
|
||||
#include "safeWriter.h"
|
||||
#include "dataErrors.h"
|
||||
#include <deque>
|
||||
|
@ -60,10 +61,10 @@ struct DivSampleHistory {
|
|||
unsigned int length, samples;
|
||||
DivSampleDepth depth;
|
||||
int rate, centerRate, loopStart, loopEnd;
|
||||
bool loop;
|
||||
bool loop, brrEmphasis;
|
||||
DivSampleLoopMode loopMode;
|
||||
bool hasSample;
|
||||
DivSampleHistory(void* d, unsigned int l, unsigned int s, DivSampleDepth de, int r, int cr, int ls, int le, bool lp, DivSampleLoopMode lm):
|
||||
DivSampleHistory(void* d, unsigned int l, unsigned int s, DivSampleDepth de, int r, int cr, int ls, int le, bool lp, bool be, DivSampleLoopMode lm):
|
||||
data((unsigned char*)d),
|
||||
length(l),
|
||||
samples(s),
|
||||
|
@ -73,9 +74,10 @@ struct DivSampleHistory {
|
|||
loopStart(ls),
|
||||
loopEnd(le),
|
||||
loop(lp),
|
||||
brrEmphasis(be),
|
||||
loopMode(lm),
|
||||
hasSample(true) {}
|
||||
DivSampleHistory(DivSampleDepth de, int r, int cr, int ls, int le, bool lp, DivSampleLoopMode lm):
|
||||
DivSampleHistory(DivSampleDepth de, int r, int cr, int ls, int le, bool lp, bool be, DivSampleLoopMode lm):
|
||||
data(NULL),
|
||||
length(0),
|
||||
samples(0),
|
||||
|
@ -85,6 +87,7 @@ struct DivSampleHistory {
|
|||
loopStart(ls),
|
||||
loopEnd(le),
|
||||
loop(lp),
|
||||
brrEmphasis(be),
|
||||
loopMode(lm),
|
||||
hasSample(false) {}
|
||||
~DivSampleHistory();
|
||||
|
@ -106,14 +109,14 @@ struct DivSample {
|
|||
// - 10: VOX ADPCM
|
||||
// - 16: 16-bit PCM
|
||||
DivSampleDepth depth;
|
||||
bool loop;
|
||||
bool loop, brrEmphasis;
|
||||
// valid values are:
|
||||
// - 0: Forward loop
|
||||
// - 1: Backward loop
|
||||
// - 2: Pingpong loop
|
||||
DivSampleLoopMode loopMode;
|
||||
|
||||
bool renderOn[4][32];
|
||||
bool renderOn[DIV_MAX_SAMPLE_TYPE][DIV_MAX_CHIPS];
|
||||
|
||||
// these are the new data structures.
|
||||
signed char* data8; // 8
|
||||
|
@ -306,6 +309,7 @@ struct DivSample {
|
|||
loopOffP(0),
|
||||
depth(DIV_SAMPLE_DEPTH_16BIT),
|
||||
loop(false),
|
||||
brrEmphasis(true),
|
||||
loopMode(DIV_SAMPLE_LOOP_FORWARD),
|
||||
data8(NULL),
|
||||
data16(NULL),
|
||||
|
@ -328,11 +332,10 @@ struct DivSample {
|
|||
lengthBRR(0),
|
||||
lengthVOX(0),
|
||||
samples(0) {
|
||||
for (int i=0; i<32; i++) {
|
||||
renderOn[0][i]=true;
|
||||
renderOn[1][i]=true;
|
||||
renderOn[2][i]=true;
|
||||
renderOn[3][i]=true;
|
||||
for (int i=0; i<DIV_MAX_CHIPS; i++) {
|
||||
for (int j=0; j<DIV_MAX_SAMPLE_TYPE; j++) {
|
||||
renderOn[j][i]=true;
|
||||
}
|
||||
}
|
||||
}
|
||||
~DivSample();
|
||||
|
|
|
@ -25,7 +25,7 @@ void DivSubSong::clearData() {
|
|||
pat[i].wipePatterns();
|
||||
}
|
||||
|
||||
memset(orders.ord,0,DIV_MAX_CHANS*256);
|
||||
memset(orders.ord,0,DIV_MAX_CHANS*DIV_MAX_PATTERNS);
|
||||
ordersLen=1;
|
||||
}
|
||||
|
||||
|
@ -34,7 +34,7 @@ void DivSubSong::optimizePatterns() {
|
|||
logD("optimizing channel %d...",i);
|
||||
std::vector<std::pair<int,int>> clearOuts=pat[i].optimize();
|
||||
for (auto& j: clearOuts) {
|
||||
for (int k=0; k<256; k++) {
|
||||
for (int k=0; k<DIV_MAX_PATTERNS; k++) {
|
||||
if (orders.ord[i][k]==j.first) {
|
||||
orders.ord[i][k]=j.second;
|
||||
}
|
||||
|
@ -48,7 +48,7 @@ void DivSubSong::rearrangePatterns() {
|
|||
logD("re-arranging channel %d...",i);
|
||||
std::vector<std::pair<int,int>> clearOuts=pat[i].rearrange();
|
||||
for (auto& j: clearOuts) {
|
||||
for (int k=0; k<256; k++) {
|
||||
for (int k=0; k<DIV_MAX_PATTERNS; k++) {
|
||||
if (orders.ord[i][k]==j.first) {
|
||||
orders.ord[i][k]=j.second;
|
||||
}
|
||||
|
|
|
@ -22,8 +22,7 @@
|
|||
#include <stdio.h>
|
||||
#include <vector>
|
||||
|
||||
#define DIV_MAX_CHANS 128
|
||||
|
||||
#include "defines.h"
|
||||
#include "../ta-utils.h"
|
||||
#include "config.h"
|
||||
#include "orders.h"
|
||||
|
@ -230,11 +229,11 @@ struct DivSong {
|
|||
bool isDMF;
|
||||
|
||||
// system
|
||||
DivSystem system[32];
|
||||
DivSystem system[DIV_MAX_CHIPS];
|
||||
unsigned char systemLen;
|
||||
signed char systemVol[32];
|
||||
signed char systemPan[32];
|
||||
DivConfig systemFlags[32];
|
||||
signed char systemVol[DIV_MAX_CHIPS];
|
||||
signed char systemPan[DIV_MAX_CHIPS];
|
||||
DivConfig systemFlags[DIV_MAX_CHIPS];
|
||||
|
||||
// song information
|
||||
String name, author, systemName;
|
||||
|
@ -428,7 +427,7 @@ struct DivSong {
|
|||
snNoLowPeriods(false),
|
||||
disableSampleMacro(false),
|
||||
autoSystem(true) {
|
||||
for (int i=0; i<32; i++) {
|
||||
for (int i=0; i<DIV_MAX_CHIPS; i++) {
|
||||
system[i]=DIV_SYSTEM_NULL;
|
||||
systemVol[i]=64;
|
||||
systemPan[i]=0;
|
||||
|
|
|
@ -945,15 +945,15 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
|
|||
w->writeI(0); // will be written later
|
||||
w->writeI(version);
|
||||
|
||||
bool willExport[32];
|
||||
bool isSecond[32];
|
||||
int streamIDs[32];
|
||||
bool willExport[DIV_MAX_CHIPS];
|
||||
bool isSecond[DIV_MAX_CHIPS];
|
||||
int streamIDs[DIV_MAX_CHIPS];
|
||||
double loopTimer[DIV_MAX_CHANS];
|
||||
double loopFreq[DIV_MAX_CHANS];
|
||||
int loopSample[DIV_MAX_CHANS];
|
||||
bool sampleDir[DIV_MAX_CHANS];
|
||||
std::vector<unsigned int> chipVol;
|
||||
std::vector<DivDelayedWrite> delayedWrites[32];
|
||||
std::vector<DivDelayedWrite> delayedWrites[DIV_MAX_CHIPS];
|
||||
std::vector<std::pair<int,DivDelayedWrite>> sortedWrites;
|
||||
|
||||
for (int i=0; i<DIV_MAX_CHANS; i++) {
|
||||
|
|
|
@ -58,11 +58,21 @@ DivDataErrors DivWavetable::readWaveData(SafeReader& reader, short version) {
|
|||
min=reader.readI();
|
||||
max=reader.readI();
|
||||
|
||||
if (len>256 || min!=0 || max>255) {
|
||||
logV("invalid len/min/max: %d %d %d",len,min,max);
|
||||
if (len>256) {
|
||||
logE("invalid len: %d",len);
|
||||
return DIV_DATA_INVALID_DATA;
|
||||
}
|
||||
|
||||
if (min!=0) {
|
||||
logW("invalid min %d",min);
|
||||
min=0;
|
||||
}
|
||||
|
||||
if (max>255) {
|
||||
logW("invalid max %d",max);
|
||||
max=255;
|
||||
}
|
||||
|
||||
for (int i=0; i<len; i++) {
|
||||
data[i]=reader.readI();
|
||||
}
|
||||
|
|
|
@ -77,6 +77,7 @@ const char* aboutLine[]={
|
|||
"freq-mod",
|
||||
"iyatemu",
|
||||
"JayBOB18",
|
||||
"Jimmy-DS",
|
||||
"kleeder",
|
||||
"jaezu",
|
||||
"Laggy",
|
||||
|
@ -87,7 +88,7 @@ const char* aboutLine[]={
|
|||
"Mahbod Karamoozian",
|
||||
"Miker",
|
||||
"nicco1690",
|
||||
"NikonTeen",
|
||||
"<nk>",
|
||||
"potatoTeto",
|
||||
"psxdominator",
|
||||
"Raijin",
|
||||
|
|
|
@ -47,6 +47,9 @@ void FurnaceGUI::drawInsList(bool asChild) {
|
|||
if (ImGui::Button(ICON_FA_PLUS "##InsAdd")) {
|
||||
if (!settings.unifiedDataView) doAction(GUI_ACTION_INS_LIST_ADD);
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Add");
|
||||
}
|
||||
if (settings.unifiedDataView) {
|
||||
if (ImGui::BeginPopupContextItem("UnifiedAdd",ImGuiMouseButton_Left)) {
|
||||
if (ImGui::MenuItem("instrument")) {
|
||||
|
@ -70,6 +73,9 @@ void FurnaceGUI::drawInsList(bool asChild) {
|
|||
if (ImGui::Button(ICON_FA_FILES_O "##InsClone")) {
|
||||
if (!settings.unifiedDataView) doAction(GUI_ACTION_INS_LIST_DUPLICATE);
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Duplicate");
|
||||
}
|
||||
if (settings.unifiedDataView) {
|
||||
if (ImGui::BeginPopupContextItem("UnifiedClone",ImGuiMouseButton_Left)) {
|
||||
if (ImGui::MenuItem("instrument")) {
|
||||
|
@ -88,6 +94,9 @@ void FurnaceGUI::drawInsList(bool asChild) {
|
|||
if (ImGui::Button(ICON_FA_FOLDER_OPEN "##InsLoad")) {
|
||||
if (!settings.unifiedDataView) doAction(GUI_ACTION_INS_LIST_OPEN);
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Open");
|
||||
}
|
||||
if (settings.unifiedDataView) {
|
||||
if (ImGui::BeginPopupContextItem("UnifiedLoad",ImGuiMouseButton_Left)) {
|
||||
if (ImGui::MenuItem("instrument")) {
|
||||
|
@ -127,6 +136,9 @@ void FurnaceGUI::drawInsList(bool asChild) {
|
|||
if (ImGui::Button(ICON_FA_FLOPPY_O "##InsSave")) {
|
||||
if (!settings.unifiedDataView) doAction(GUI_ACTION_INS_LIST_SAVE);
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Save");
|
||||
}
|
||||
if (settings.unifiedDataView) {
|
||||
if (ImGui::BeginPopupContextItem("UnifiedSave",ImGuiMouseButton_Left)) {
|
||||
if (ImGui::MenuItem("instrument")) {
|
||||
|
@ -166,15 +178,24 @@ void FurnaceGUI::drawInsList(bool asChild) {
|
|||
if (ImGui::ArrowButton("InsUp",ImGuiDir_Up)) {
|
||||
doAction(GUI_ACTION_INS_LIST_MOVE_UP);
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Move up");
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::ArrowButton("InsDown",ImGuiDir_Down)) {
|
||||
doAction(GUI_ACTION_INS_LIST_MOVE_DOWN);
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Move down");
|
||||
}
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button(ICON_FA_TIMES "##InsDelete")) {
|
||||
if (!settings.unifiedDataView) doAction(GUI_ACTION_INS_LIST_DELETE);
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Delete");
|
||||
}
|
||||
if (settings.unifiedDataView) {
|
||||
if (ImGui::BeginPopupContextItem("UnifiedDelete",ImGuiMouseButton_Left)) {
|
||||
if (ImGui::MenuItem("instrument")) {
|
||||
|
@ -521,14 +542,23 @@ void FurnaceGUI::drawWaveList(bool asChild) {
|
|||
if (ImGui::Button(ICON_FA_PLUS "##WaveAdd")) {
|
||||
doAction(GUI_ACTION_WAVE_LIST_ADD);
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Add");
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button(ICON_FA_FILES_O "##WaveClone")) {
|
||||
doAction(GUI_ACTION_WAVE_LIST_DUPLICATE);
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Duplicate");
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button(ICON_FA_FOLDER_OPEN "##WaveLoad")) {
|
||||
doAction(GUI_ACTION_WAVE_LIST_OPEN);
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Open");
|
||||
}
|
||||
if (ImGui::BeginPopupContextItem("WaveOpenOpt")) {
|
||||
if (ImGui::MenuItem("replace...")) {
|
||||
doAction((curWave>=0 && curWave<(int)e->song.wave.size())?GUI_ACTION_WAVE_LIST_OPEN_REPLACE:GUI_ACTION_WAVE_LIST_OPEN);
|
||||
|
@ -539,6 +569,9 @@ void FurnaceGUI::drawWaveList(bool asChild) {
|
|||
if (ImGui::Button(ICON_FA_FLOPPY_O "##WaveSave")) {
|
||||
doAction(GUI_ACTION_WAVE_LIST_SAVE);
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Save");
|
||||
}
|
||||
if (!settings.unifiedDataView) {
|
||||
if (ImGui::BeginPopupContextItem("WaveSaveFormats",ImGuiMouseButton_Right)) {
|
||||
if (ImGui::MenuItem("save as .dmw...")) {
|
||||
|
@ -554,14 +587,23 @@ void FurnaceGUI::drawWaveList(bool asChild) {
|
|||
if (ImGui::ArrowButton("WaveUp",ImGuiDir_Up)) {
|
||||
doAction(GUI_ACTION_WAVE_LIST_MOVE_UP);
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Move up");
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::ArrowButton("WaveDown",ImGuiDir_Down)) {
|
||||
doAction(GUI_ACTION_WAVE_LIST_MOVE_DOWN);
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Move down");
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button(ICON_FA_TIMES "##WaveDelete")) {
|
||||
doAction(GUI_ACTION_WAVE_LIST_DELETE);
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Delete");
|
||||
}
|
||||
ImGui::Separator();
|
||||
if (ImGui::BeginTable("WaveListScroll",1,ImGuiTableFlags_ScrollY)) {
|
||||
actualWaveList();
|
||||
|
@ -598,14 +640,23 @@ void FurnaceGUI::drawSampleList(bool asChild) {
|
|||
if (ImGui::Button(ICON_FA_FILE "##SampleAdd")) {
|
||||
doAction(GUI_ACTION_SAMPLE_LIST_ADD);
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Add");
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button(ICON_FA_FILES_O "##SampleClone")) {
|
||||
doAction(GUI_ACTION_SAMPLE_LIST_DUPLICATE);
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Duplicate");
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button(ICON_FA_FOLDER_OPEN "##SampleLoad")) {
|
||||
doAction(GUI_ACTION_SAMPLE_LIST_OPEN);
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Open");
|
||||
}
|
||||
if (ImGui::BeginPopupContextItem("SampleOpenOpt")) {
|
||||
if (ImGui::MenuItem("replace...")) {
|
||||
doAction((curSample>=0 && curSample<(int)e->song.sample.size())?GUI_ACTION_SAMPLE_LIST_OPEN_REPLACE:GUI_ACTION_SAMPLE_LIST_OPEN);
|
||||
|
@ -623,26 +674,44 @@ void FurnaceGUI::drawSampleList(bool asChild) {
|
|||
if (ImGui::Button(ICON_FA_FLOPPY_O "##SampleSave")) {
|
||||
doAction(GUI_ACTION_SAMPLE_LIST_SAVE);
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Save");
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::ArrowButton("SampleUp",ImGuiDir_Up)) {
|
||||
doAction(GUI_ACTION_SAMPLE_LIST_MOVE_UP);
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Move up");
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::ArrowButton("SampleDown",ImGuiDir_Down)) {
|
||||
doAction(GUI_ACTION_SAMPLE_LIST_MOVE_DOWN);
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Move down");
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button(ICON_FA_TIMES "##SampleDelete")) {
|
||||
doAction(GUI_ACTION_SAMPLE_LIST_DELETE);
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Delete");
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button(ICON_FA_VOLUME_UP "##PreviewSampleL")) {
|
||||
doAction(GUI_ACTION_SAMPLE_LIST_PREVIEW);
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Preview");
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button(ICON_FA_VOLUME_OFF "##StopSampleL")) {
|
||||
doAction(GUI_ACTION_SAMPLE_LIST_STOP_PREVIEW);
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Stop preview");
|
||||
}
|
||||
ImGui::Separator();
|
||||
if (ImGui::BeginTable("SampleListScroll",1,ImGuiTableFlags_ScrollY)) {
|
||||
actualSampleList();
|
||||
|
@ -684,20 +753,48 @@ void FurnaceGUI::actualWaveList() {
|
|||
|
||||
void FurnaceGUI::actualSampleList() {
|
||||
for (int i=0; i<(int)e->song.sample.size(); i++) {
|
||||
bool memWarning=false;
|
||||
|
||||
DivSample* sample=e->song.sample[i];
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
for (int j=0; j<e->song.systemLen; j++) {
|
||||
DivDispatch* dispatch=e->getDispatch(j);
|
||||
if (dispatch==NULL) continue;
|
||||
|
||||
for (int k=0; k<DIV_MAX_SAMPLE_TYPE; k++) {
|
||||
if (dispatch->getSampleMemCapacity(k)==0) continue;
|
||||
if (!dispatch->isSampleLoaded(k,i) && sample->renderOn[k][j]) {
|
||||
memWarning=true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (memWarning) break;
|
||||
}
|
||||
if (memWarning) ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_SAMPLE_CHIP_WARNING]);
|
||||
if (ImGui::Selectable(fmt::sprintf("%d: %s##_SAM%d",i,sample->name,i).c_str(),curSample==i)) {
|
||||
curSample=i;
|
||||
samplePos=0;
|
||||
updateSampleTex=true;
|
||||
}
|
||||
if (wantScrollList && curSample==i) ImGui::SetScrollHereY();
|
||||
if (ImGui::IsItemHovered() && !mobileUI) {
|
||||
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_TEXT]);
|
||||
ImGui::SetTooltip("Bank %d: %s",i/12,sampleNote[i%12]);
|
||||
if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
|
||||
sampleEditOpen=true;
|
||||
}
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
if (memWarning) {
|
||||
ImGui::SameLine();
|
||||
ImGui::Text(ICON_FA_EXCLAMATION_TRIANGLE);
|
||||
if (ImGui::IsItemHovered() && !mobileUI) {
|
||||
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_TEXT]);
|
||||
ImGui::SetTooltip("out of memory for this sample!");
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
if (wantScrollList && curSample==i) ImGui::SetScrollHereY();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -174,6 +174,9 @@ void FurnaceGUI::doAction(int what) {
|
|||
case GUI_ACTION_PANIC:
|
||||
e->syncReset();
|
||||
break;
|
||||
case GUI_ACTION_CLEAR:
|
||||
showWarning("Are you sure you want to clear... (cannot be undone!)",GUI_WARN_CLEAR);
|
||||
break;
|
||||
|
||||
case GUI_ACTION_WINDOW_EDIT_CONTROLS:
|
||||
nextWindow=GUI_WINDOW_EDIT_CONTROLS;
|
||||
|
@ -522,7 +525,7 @@ void FurnaceGUI::doAction(int what) {
|
|||
case GUI_ACTION_PAT_INCREASE_COLUMNS:
|
||||
if (cursor.xCoarse<0 || cursor.xCoarse>=e->getTotalChannelCount()) break;
|
||||
e->curPat[cursor.xCoarse].effectCols++;
|
||||
if (e->curPat[cursor.xCoarse].effectCols>8) e->curPat[cursor.xCoarse].effectCols=8;
|
||||
if (e->curPat[cursor.xCoarse].effectCols>DIV_MAX_EFFECTS) e->curPat[cursor.xCoarse].effectCols=DIV_MAX_EFFECTS;
|
||||
break;
|
||||
case GUI_ACTION_PAT_DECREASE_COLUMNS:
|
||||
if (cursor.xCoarse<0 || cursor.xCoarse>=e->getTotalChannelCount()) break;
|
||||
|
@ -554,6 +557,10 @@ void FurnaceGUI::doAction(int what) {
|
|||
break;
|
||||
case GUI_ACTION_PAT_LATCH: // TODO
|
||||
break;
|
||||
case GUI_ACTION_PAT_SCROLL_MODE: // TODO
|
||||
break;
|
||||
case GUI_ACTION_PAT_CLEAR_LATCH: // TODO
|
||||
break;
|
||||
|
||||
case GUI_ACTION_INS_LIST_ADD:
|
||||
curIns=e->addInstrument(cursor.xCoarse);
|
||||
|
|
|
@ -19,31 +19,317 @@
|
|||
|
||||
#include "gui.h"
|
||||
#include "IconsFontAwesome4.h"
|
||||
#include <imgui.h>
|
||||
#include <fmt/printf.h>
|
||||
|
||||
// 0: all directions
|
||||
// 1: half
|
||||
// 2: half
|
||||
// 3: quarter
|
||||
const float mobileButtonAngles[4][8]={
|
||||
{0.0, 0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.875},
|
||||
{0.8333, 0.0, 0.1667, 0.8, 0.9, 0.0, 0.1, 0.2},
|
||||
{0.0833, 0.25, 0.4167, 0.45, 0.35, 0.25, 0.15, 0.05},
|
||||
{0.25, 0.125, 0.0, 0.25, 0.1875, 0.125, 0.0625, 0.0}
|
||||
};
|
||||
|
||||
const float mobileButtonDistances[4][8]={
|
||||
{1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0},
|
||||
{0.8, 0.75, 0.8, 1.5, 1.5, 1.5, 1.5, 1.5},
|
||||
{0.8, 0.75, 0.8, 1.5, 1.5, 1.5, 1.5, 1.5},
|
||||
{0.9, 1.0, 0.9, 1.78, 1.82, 1.95, 1.82, 1.78}
|
||||
};
|
||||
|
||||
const char* mobileButtonLabels[32]={
|
||||
// page 1
|
||||
"cut",
|
||||
"copy",
|
||||
"paste",
|
||||
"delete",
|
||||
"select\nall",
|
||||
"piano",
|
||||
"undo",
|
||||
"redo",
|
||||
|
||||
// page 2
|
||||
"paste\nmix",
|
||||
"paste\nmix bg",
|
||||
"paste\nins",
|
||||
"paste\nins bg",
|
||||
"paste\nflood",
|
||||
"paste\noverflow",
|
||||
"transpose\nnotes",
|
||||
"transpose\nvalues",
|
||||
|
||||
// page 3
|
||||
"change\nins",
|
||||
"find/\nreplace",
|
||||
"collapse",
|
||||
"expand",
|
||||
"flip",
|
||||
"invert",
|
||||
"interpolate",
|
||||
"scale",
|
||||
|
||||
// page 4
|
||||
"fade",
|
||||
"randomize",
|
||||
"opmask",
|
||||
"scroll\nmode",
|
||||
"input\nlatch",
|
||||
"set\nlatch",
|
||||
"clear\nlatch",
|
||||
"clear"
|
||||
};
|
||||
|
||||
const int mobileButtonActions[32]={
|
||||
// page 1
|
||||
GUI_ACTION_PAT_CUT,
|
||||
GUI_ACTION_PAT_COPY,
|
||||
GUI_ACTION_PAT_PASTE,
|
||||
GUI_ACTION_PAT_DELETE,
|
||||
GUI_ACTION_PAT_SELECT_ALL,
|
||||
0,
|
||||
GUI_ACTION_UNDO,
|
||||
GUI_ACTION_REDO,
|
||||
|
||||
// page 2
|
||||
GUI_ACTION_PAT_PASTE_MIX,
|
||||
GUI_ACTION_PAT_PASTE_MIX_BG,
|
||||
0,
|
||||
0,
|
||||
GUI_ACTION_PAT_PASTE_FLOOD,
|
||||
GUI_ACTION_PAT_PASTE_OVERFLOW,
|
||||
0,
|
||||
0,
|
||||
|
||||
// page 3
|
||||
0,
|
||||
GUI_ACTION_WINDOW_FIND,
|
||||
GUI_ACTION_PAT_COLLAPSE_ROWS,
|
||||
GUI_ACTION_PAT_EXPAND_ROWS,
|
||||
GUI_ACTION_PAT_FLIP_SELECTION,
|
||||
GUI_ACTION_PAT_INVERT_VALUES,
|
||||
GUI_ACTION_PAT_INTERPOLATE,
|
||||
0,
|
||||
|
||||
// page 4
|
||||
GUI_ACTION_PAT_FADE,
|
||||
0,
|
||||
0,
|
||||
GUI_ACTION_PAT_SCROLL_MODE,
|
||||
0,
|
||||
GUI_ACTION_PAT_LATCH,
|
||||
GUI_ACTION_PAT_CLEAR_LATCH,
|
||||
GUI_ACTION_CLEAR
|
||||
};
|
||||
|
||||
const bool mobileButtonPersist[32]={
|
||||
// page 1
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
|
||||
// page 2
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
|
||||
// page 3
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
|
||||
// page 4
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
};
|
||||
|
||||
void FurnaceGUI::drawMobileControls() {
|
||||
float timeScale=1.0f/(60.0f*ImGui::GetIO().DeltaTime);
|
||||
if (mobileMenuOpen) {
|
||||
if (mobileMenuPos<0.999f) {
|
||||
WAKE_UP;
|
||||
mobileMenuPos+=MIN(0.1,(1.0-mobileMenuPos)*0.65)*timeScale;
|
||||
if (dragMobileMenu) {
|
||||
if (portrait) {
|
||||
mobileMenuPos=(dragMobileMenuOrigin.y-ImGui::GetMousePos().y)/(canvasH*0.65);
|
||||
} else {
|
||||
mobileMenuPos=1.0f;
|
||||
mobileMenuPos=(ImGui::GetMousePos().x-dragMobileMenuOrigin.x)/(canvasW*0.65);
|
||||
}
|
||||
if (mobileMenuPos<0.0f) mobileMenuPos=0.0f;
|
||||
if (mobileMenuPos>1.0f) mobileMenuPos=1.0f;
|
||||
} else {
|
||||
if (mobileMenuPos>0.001f) {
|
||||
WAKE_UP;
|
||||
mobileMenuPos-=MIN(0.1,mobileMenuPos*0.65)*timeScale;
|
||||
if (mobileMenuOpen) {
|
||||
if (mobileMenuPos<0.999f) {
|
||||
WAKE_UP;
|
||||
mobileMenuPos+=MIN(0.1,(1.0-mobileMenuPos)*0.65)*timeScale;
|
||||
} else {
|
||||
mobileMenuPos=1.0f;
|
||||
}
|
||||
} else {
|
||||
mobileMenuPos=0.0f;
|
||||
if (mobileMenuPos>0.001f) {
|
||||
WAKE_UP;
|
||||
mobileMenuPos-=MIN(0.1,mobileMenuPos*0.65)*timeScale;
|
||||
} else {
|
||||
mobileMenuPos=0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (dragMobileEditButton) {
|
||||
if (ImGui::GetIO().MouseDragMaxDistanceSqr[ImGuiMouseButton_Left]>ImGui::GetIO().ConfigInertialScrollToleranceSqr) {
|
||||
mobileEditButtonPos.x=((ImGui::GetMousePos().x/canvasW)-((portrait?0.16*canvasW:0.16*canvasH)/2)/canvasW);
|
||||
mobileEditButtonPos.y=((ImGui::GetMousePos().y/canvasH)-((portrait?0.16*canvasW:0.16*canvasH)/2)/canvasH);
|
||||
}
|
||||
}
|
||||
|
||||
if (mobileEditButtonPos.x<0) mobileEditButtonPos.x=0;
|
||||
if (mobileEditButtonPos.x>1) mobileEditButtonPos.x=1;
|
||||
if (mobileEditButtonPos.y<0) mobileEditButtonPos.y=0;
|
||||
if (mobileEditButtonPos.y>1) mobileEditButtonPos.y=1;
|
||||
|
||||
if (mobileEdit) {
|
||||
mobileEditAnim+=ImGui::GetIO().DeltaTime*2.4;
|
||||
if (mobileEditAnim>1.0f) {
|
||||
mobileEditAnim=1.0f;
|
||||
} else {
|
||||
WAKE_UP;
|
||||
}
|
||||
} else {
|
||||
mobileEditAnim-=ImGui::GetIO().DeltaTime*2.4;
|
||||
if (mobileEditAnim<0.0f) {
|
||||
mobileEditAnim=0.0f;
|
||||
} else {
|
||||
WAKE_UP;
|
||||
}
|
||||
}
|
||||
|
||||
if (curWindowLast==GUI_WINDOW_PATTERN) {
|
||||
if (mobileEditAnim>0.0f) {
|
||||
ImGui::SetNextWindowPos(ImVec2(0.0f,0.0f));
|
||||
ImGui::SetNextWindowSize(ImVec2(canvasW,canvasH));
|
||||
} else {
|
||||
ImGui::SetNextWindowPos(ImVec2((mobileEditButtonPos.x+(portrait?0:(mobileMenuPos*0.65)))*canvasW,(mobileEditButtonPos.y-(portrait?(mobileMenuPos*0.65):0))*canvasH));
|
||||
ImGui::SetNextWindowSize(portrait?ImVec2(0.16*canvasW,0.16*canvasW):ImVec2(0.16*canvasH,0.16*canvasH));
|
||||
}
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding,ImVec2(0.0f,0.0f));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding,mobileEditButtonSize.x);
|
||||
if (ImGui::Begin("MobileEdit",NULL,ImGuiWindowFlags_NoScrollbar|ImGuiWindowFlags_NoScrollWithMouse|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoBackground|ImGuiWindowFlags_NoDecoration)) {
|
||||
bool mobileEditWas=mobileEdit;
|
||||
if (ImGui::IsMouseReleased(ImGuiMouseButton_Left) && mobileEdit) {
|
||||
mobileEdit=false;
|
||||
}
|
||||
|
||||
if (mobileEditAnim>0.0f) {
|
||||
int curButtonPos=0;
|
||||
float buttonDir, buttonDist;
|
||||
float buttonMirrorX=1.0f;
|
||||
float buttonMirrorY=1.0f;
|
||||
|
||||
int buttonLayout=0;
|
||||
|
||||
ImVec2 scaledButtonPos=ImVec2(
|
||||
mobileEditButtonPos.x+((mobileEditButtonSize.x*0.5)/(float)canvasW),
|
||||
mobileEditButtonPos.y+((mobileEditButtonSize.y*0.5)/(float)canvasH)
|
||||
);
|
||||
|
||||
if (scaledButtonPos.x>0.25 &&
|
||||
scaledButtonPos.x<0.75 &&
|
||||
scaledButtonPos.y>0.2 &&
|
||||
scaledButtonPos.y<0.8) {
|
||||
buttonLayout=0;
|
||||
} else if (scaledButtonPos.x>0.4 && scaledButtonPos.x<0.6) {
|
||||
buttonLayout=2;
|
||||
} else if (scaledButtonPos.y>0.25 && scaledButtonPos.y<0.75) {
|
||||
buttonLayout=1;
|
||||
} else {
|
||||
buttonLayout=3;
|
||||
}
|
||||
|
||||
switch (buttonLayout) {
|
||||
case 1:
|
||||
if (mobileEditButtonPos.x>0.5) buttonMirrorX=-1.0f;
|
||||
break;
|
||||
case 2:
|
||||
if (mobileEditButtonPos.y>0.5) buttonMirrorY=-1.0f;
|
||||
break;
|
||||
case 3:
|
||||
if (mobileEditButtonPos.x>0.5) buttonMirrorX=-1.0f;
|
||||
if (mobileEditButtonPos.y>0.5) buttonMirrorY=-1.0f;
|
||||
break;
|
||||
}
|
||||
|
||||
for (int i=0; i<8; i++) {
|
||||
float anim=(mobileEditAnim*1.5)-(float)i*0.05;
|
||||
if (anim<0.0f) anim=0.0f;
|
||||
if (anim>1.0f) anim=1.0f;
|
||||
anim=5*anim-7*pow(anim,2.0f)+3*pow(anim,3.0f);
|
||||
|
||||
buttonDir=mobileButtonAngles[buttonLayout][curButtonPos];
|
||||
buttonDist=mobileButtonDistances[buttonLayout][curButtonPos]*mobileEditButtonSize.x*1.6f;
|
||||
|
||||
ImGui::SetCursorPos(ImVec2(
|
||||
(mobileEditButtonPos.x*canvasW)+cos(buttonDir*2.0*M_PI)*buttonDist*buttonMirrorX*anim,
|
||||
(mobileEditButtonPos.y*canvasH)+sin(buttonDir*2.0*M_PI)*buttonDist*buttonMirrorY*anim
|
||||
));
|
||||
if (ImGui::Button(mobileButtonLabels[i+mobileEditPage*8],mobileEditButtonSize)) {
|
||||
if (mobileButtonActions[i+mobileEditPage*8]) {
|
||||
doAction(mobileButtonActions[i+mobileEditPage*8]);
|
||||
}
|
||||
if (mobileButtonPersist[i+mobileEditPage*8]) {
|
||||
if (mobileMenuPos<=0.0) mobileEdit=true;
|
||||
}
|
||||
}
|
||||
|
||||
curButtonPos++;
|
||||
}
|
||||
|
||||
ImGui::SetCursorPos(ImVec2(mobileEditButtonPos.x*canvasW,mobileEditButtonPos.y*canvasH));
|
||||
} else {
|
||||
float avail=portrait?ImGui::GetContentRegionAvail().y:ImGui::GetContentRegionAvail().x;
|
||||
mobileEditButtonSize=ImVec2(avail,avail);
|
||||
}
|
||||
|
||||
if (ImGui::Button(ICON_FA_PENCIL "##Edit",mobileEditButtonSize)) {
|
||||
// click
|
||||
if (mobileEditWas) {
|
||||
if (++mobileEditPage>3) mobileEditPage=0;
|
||||
}
|
||||
if (ImGui::GetIO().MouseDragMaxDistanceSqr[ImGuiMouseButton_Left]<=ImGui::GetIO().ConfigInertialScrollToleranceSqr) {
|
||||
if (mobileMenuPos<=0.0) mobileEdit=true;
|
||||
}
|
||||
}
|
||||
if (ImGui::IsItemClicked() && !mobileEdit && mobileMenuPos<=0.0) {
|
||||
dragMobileEditButton=true;
|
||||
}
|
||||
}
|
||||
ImGui::End();
|
||||
ImGui::PopStyleVar(2);
|
||||
}
|
||||
|
||||
ImGui::SetNextWindowPos(portrait?ImVec2(0.0f,((1.0-mobileMenuPos*0.65)*canvasH)-(0.16*canvasW)):ImVec2(0.5*canvasW*mobileMenuPos,0.0f));
|
||||
ImGui::SetNextWindowSize(portrait?ImVec2(canvasW,0.16*canvasW):ImVec2(0.16*canvasH,canvasH));
|
||||
if (ImGui::Begin("Mobile Controls",NULL,ImGuiWindowFlags_NoScrollbar|ImGuiWindowFlags_NoScrollWithMouse|globalWinFlags)) {
|
||||
float avail=portrait?ImGui::GetContentRegionAvail().y:ImGui::GetContentRegionAvail().x;
|
||||
ImVec2 buttonSize=ImVec2(avail,avail);
|
||||
|
||||
const char* mobButtonName=ICON_FA_CHEVRON_RIGHT "##MobileMenu";
|
||||
if (portrait) mobButtonName=ICON_FA_CHEVRON_UP "##MobileMenu";
|
||||
if (mobileMenuOpen) {
|
||||
|
@ -54,7 +340,20 @@ void FurnaceGUI::drawMobileControls() {
|
|||
}
|
||||
}
|
||||
if (ImGui::Button(mobButtonName,buttonSize)) {
|
||||
mobileMenuOpen=!mobileMenuOpen;
|
||||
if (!dragMobileMenu) {
|
||||
mobileMenuOpen=!mobileMenuOpen;
|
||||
}
|
||||
}
|
||||
if (ImGui::IsItemActive() && ImGui::GetIO().MouseDragMaxDistanceSqr[ImGuiMouseButton_Left]>ImGui::GetIO().ConfigInertialScrollToleranceSqr*2.0f) {
|
||||
if (!dragMobileMenu) {
|
||||
dragMobileMenu=true;
|
||||
dragMobileMenuOrigin=ImGui::GetMousePos();
|
||||
if (portrait) {
|
||||
dragMobileMenuOrigin.y+=mobileMenuPos*canvasH*0.65f;
|
||||
} else {
|
||||
dragMobileMenuOrigin.x-=mobileMenuPos*canvasW*0.65f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!portrait) ImGui::Separator();
|
||||
|
@ -321,11 +620,17 @@ void FurnaceGUI::drawEditControls() {
|
|||
if (ImGui::Button(ICON_FA_PLAY "##Play")) {
|
||||
play();
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Play");
|
||||
}
|
||||
popToggleColors();
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button(ICON_FA_STOP "##Stop")) {
|
||||
stop();
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Stop");
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::Checkbox("Edit",&edit);
|
||||
ImGui::SameLine();
|
||||
|
@ -349,6 +654,9 @@ void FurnaceGUI::drawEditControls() {
|
|||
e->stepOne(cursor.y);
|
||||
pendingStepUpdate=true;
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Step one row");
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
pushToggleColors(noteInputPoly);
|
||||
|
@ -356,6 +664,9 @@ void FurnaceGUI::drawEditControls() {
|
|||
noteInputPoly=!noteInputPoly;
|
||||
e->setAutoNotePoly(noteInputPoly);
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Polyphony");
|
||||
}
|
||||
popToggleColors();
|
||||
}
|
||||
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_EDIT_CONTROLS;
|
||||
|
@ -366,17 +677,26 @@ void FurnaceGUI::drawEditControls() {
|
|||
if (ImGui::Button(ICON_FA_STOP "##Stop")) {
|
||||
stop();
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Stop");
|
||||
}
|
||||
ImGui::SameLine();
|
||||
pushToggleColors(e->isPlaying());
|
||||
if (ImGui::Button(ICON_FA_PLAY "##Play")) {
|
||||
play();
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Play");
|
||||
}
|
||||
popToggleColors();
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button(ICON_FA_ARROW_DOWN "##StepOne")) {
|
||||
e->stepOne(cursor.y);
|
||||
pendingStepUpdate=true;
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Step one row");
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
bool repeatPattern=e->getRepeatPattern();
|
||||
|
@ -384,6 +704,9 @@ void FurnaceGUI::drawEditControls() {
|
|||
if (ImGui::Button(ICON_FA_REPEAT "##RepeatPattern")) {
|
||||
e->setRepeatPattern(!repeatPattern);
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Repeat pattern");
|
||||
}
|
||||
popToggleColors();
|
||||
|
||||
ImGui::SameLine();
|
||||
|
@ -391,6 +714,9 @@ void FurnaceGUI::drawEditControls() {
|
|||
if (ImGui::Button(ICON_FA_CIRCLE "##Edit")) {
|
||||
edit=!edit;
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Edit");
|
||||
}
|
||||
popToggleColors();
|
||||
|
||||
ImGui::SameLine();
|
||||
|
@ -399,6 +725,9 @@ void FurnaceGUI::drawEditControls() {
|
|||
if (ImGui::Button(ICON_FA_BELL_O "##Metronome")) {
|
||||
e->setMetronome(!metro);
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Metronome");
|
||||
}
|
||||
popToggleColors();
|
||||
|
||||
ImGui::SameLine();
|
||||
|
@ -441,6 +770,9 @@ void FurnaceGUI::drawEditControls() {
|
|||
noteInputPoly=!noteInputPoly;
|
||||
e->setAutoNotePoly(noteInputPoly);
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Polyphony");
|
||||
}
|
||||
popToggleColors();
|
||||
}
|
||||
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_EDIT_CONTROLS;
|
||||
|
@ -453,26 +785,41 @@ void FurnaceGUI::drawEditControls() {
|
|||
if (ImGui::Button(ICON_FA_PLAY "##Play",buttonSize)) {
|
||||
play();
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Play");
|
||||
}
|
||||
popToggleColors();
|
||||
if (ImGui::Button(ICON_FA_STOP "##Stop",buttonSize)) {
|
||||
stop();
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Stop");
|
||||
}
|
||||
if (ImGui::Button(ICON_FA_ARROW_DOWN "##StepOne",buttonSize)) {
|
||||
e->stepOne(cursor.y);
|
||||
pendingStepUpdate=true;
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Step one row");
|
||||
}
|
||||
|
||||
bool repeatPattern=e->getRepeatPattern();
|
||||
pushToggleColors(repeatPattern);
|
||||
if (ImGui::Button(ICON_FA_REPEAT "##RepeatPattern",buttonSize)) {
|
||||
e->setRepeatPattern(!repeatPattern);
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Repeat pattern");
|
||||
}
|
||||
popToggleColors();
|
||||
|
||||
pushToggleColors(edit);
|
||||
if (ImGui::Button(ICON_FA_CIRCLE "##Edit",buttonSize)) {
|
||||
edit=!edit;
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Edit");
|
||||
}
|
||||
popToggleColors();
|
||||
|
||||
bool metro=e->getMetronome();
|
||||
|
@ -480,9 +827,15 @@ void FurnaceGUI::drawEditControls() {
|
|||
if (ImGui::Button(ICON_FA_BELL_O "##Metronome",buttonSize)) {
|
||||
e->setMetronome(!metro);
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Metronome");
|
||||
}
|
||||
popToggleColors();
|
||||
|
||||
ImGui::Text("Oct.");
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Octave");
|
||||
}
|
||||
float avail=ImGui::GetContentRegionAvail().x;
|
||||
ImGui::SetNextItemWidth(avail);
|
||||
if (ImGui::InputInt("##Octave",&curOctave,0,0)) {
|
||||
|
@ -507,15 +860,24 @@ void FurnaceGUI::drawEditControls() {
|
|||
}
|
||||
|
||||
ImGui::Text("Foll.");
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Follow");
|
||||
}
|
||||
pushToggleColors(followOrders);
|
||||
if (ImGui::Button("Ord##FollowOrders",buttonSize)) { handleUnimportant
|
||||
followOrders=!followOrders;
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Orders");
|
||||
}
|
||||
popToggleColors();
|
||||
pushToggleColors(followPattern);
|
||||
if (ImGui::Button("Pat##FollowPattern",buttonSize)) { handleUnimportant
|
||||
followPattern=!followPattern;
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Pattern");
|
||||
}
|
||||
popToggleColors();
|
||||
|
||||
pushToggleColors(noteInputPoly);
|
||||
|
@ -523,6 +885,9 @@ void FurnaceGUI::drawEditControls() {
|
|||
noteInputPoly=!noteInputPoly;
|
||||
e->setAutoNotePoly(noteInputPoly);
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Polyphony");
|
||||
}
|
||||
popToggleColors();
|
||||
}
|
||||
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_EDIT_CONTROLS;
|
||||
|
@ -535,33 +900,51 @@ void FurnaceGUI::drawEditControls() {
|
|||
if (ImGui::Button(ICON_FA_STOP "##Stop")) {
|
||||
stop();
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Stop");
|
||||
}
|
||||
popToggleColors();
|
||||
} else {
|
||||
if (ImGui::Button(ICON_FA_PLAY "##Play")) {
|
||||
play(oldRow);
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Play");
|
||||
}
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button(ICON_FA_PLAY_CIRCLE "##PlayAgain")) {
|
||||
e->setRepeatPattern(false);
|
||||
play();
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Play from the beginning of this pattern");
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button(ICON_FA_STEP_FORWARD "##PlayRepeat")) {
|
||||
e->setRepeatPattern(true);
|
||||
play();
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Repeat from the beginning of this pattern");
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button(ICON_FA_ARROW_DOWN "##StepOne")) {
|
||||
e->stepOne(cursor.y);
|
||||
pendingStepUpdate=true;
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Step one row");
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
pushToggleColors(edit);
|
||||
if (ImGui::Button(ICON_FA_CIRCLE "##Edit")) {
|
||||
edit=!edit;
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Edit");
|
||||
}
|
||||
popToggleColors();
|
||||
|
||||
bool metro=e->getMetronome();
|
||||
|
@ -570,6 +953,9 @@ void FurnaceGUI::drawEditControls() {
|
|||
if (ImGui::Button(ICON_FA_BELL_O "##Metronome")) {
|
||||
e->setMetronome(!metro);
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Metronome");
|
||||
}
|
||||
popToggleColors();
|
||||
|
||||
ImGui::SameLine();
|
||||
|
@ -578,6 +964,9 @@ void FurnaceGUI::drawEditControls() {
|
|||
if (ImGui::Button(ICON_FA_REPEAT "##RepeatPattern")) {
|
||||
e->setRepeatPattern(!repeatPattern);
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Repeat pattern");
|
||||
}
|
||||
popToggleColors();
|
||||
|
||||
ImGui::SameLine();
|
||||
|
@ -586,6 +975,9 @@ void FurnaceGUI::drawEditControls() {
|
|||
noteInputPoly=!noteInputPoly;
|
||||
e->setAutoNotePoly(noteInputPoly);
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Polyphony");
|
||||
}
|
||||
popToggleColors();
|
||||
}
|
||||
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_EDIT_CONTROLS;
|
||||
|
|
|
@ -119,7 +119,7 @@ void FurnaceGUI::makeUndo(ActionType action) {
|
|||
for (int i=0; i<e->getTotalChannelCount(); i++) {
|
||||
DivPattern* p=e->curPat[i].getPattern(e->curOrders->ord[i][curOrder],false);
|
||||
for (int j=0; j<e->curSubSong->patLen; j++) {
|
||||
for (int k=0; k<32; k++) {
|
||||
for (int k=0; k<DIV_MAX_COLS; k++) {
|
||||
if (p->data[j][k]!=oldPat[i]->data[j][k]) {
|
||||
s.pat.push_back(UndoPatternData(subSong,i,e->curOrders->ord[i][curOrder],j,k,oldPat[i]->data[j][k],p->data[j][k]));
|
||||
}
|
||||
|
|
|
@ -220,20 +220,20 @@ void FurnaceGUI::doReplace() {
|
|||
UndoStep us;
|
||||
us.type=GUI_UNDO_REPLACE;
|
||||
|
||||
short prevVal[32];
|
||||
memset(prevVal,0,32*sizeof(short));
|
||||
short prevVal[DIV_MAX_COLS];
|
||||
memset(prevVal,0,DIV_MAX_COLS*sizeof(short));
|
||||
|
||||
for (FurnaceGUIQueryResult& i: curQueryResults) {
|
||||
int patIndex=e->song.subsong[i.subsong]->orders.ord[i.x][i.order];
|
||||
DivPattern* p=e->song.subsong[i.subsong]->pat[i.x].getPattern(patIndex,true);
|
||||
if (touched[i.x]==NULL) {
|
||||
touched[i.x]=new bool[256*256];
|
||||
memset(touched[i.x],0,256*256*sizeof(bool));
|
||||
touched[i.x]=new bool[DIV_MAX_PATTERNS*DIV_MAX_ROWS];
|
||||
memset(touched[i.x],0,DIV_MAX_PATTERNS*DIV_MAX_ROWS*sizeof(bool));
|
||||
}
|
||||
if (touched[i.x][(patIndex<<8)|i.y]) continue;
|
||||
touched[i.x][(patIndex<<8)|i.y]=true;
|
||||
|
||||
memcpy(prevVal,p->data[i.y],32*sizeof(short));
|
||||
memcpy(prevVal,p->data[i.y],DIV_MAX_COLS*sizeof(short));
|
||||
|
||||
if (queryReplaceNoteDo) {
|
||||
switch (queryReplaceNoteMode) {
|
||||
|
@ -462,7 +462,7 @@ void FurnaceGUI::doReplace() {
|
|||
}
|
||||
|
||||
// issue undo step
|
||||
for (int j=0; j<32; j++) {
|
||||
for (int j=0; j<DIV_MAX_COLS; j++) {
|
||||
if (p->data[i.y][j]!=prevVal[j]) {
|
||||
us.pat.push_back(UndoPatternData(i.subsong,i.x,patIndex,i.y,j,prevVal[j],p->data[i.y][j]));
|
||||
}
|
||||
|
|
|
@ -1040,13 +1040,13 @@ void FurnaceGUI::noteInput(int num, int key, int vol) {
|
|||
|
||||
prepareUndo(GUI_UNDO_PATTERN_EDIT);
|
||||
|
||||
if (key==100) { // note off
|
||||
if (key==GUI_NOTE_OFF) { // note off
|
||||
pat->data[cursor.y][0]=100;
|
||||
pat->data[cursor.y][1]=0;
|
||||
} else if (key==101) { // note off + env release
|
||||
} else if (key==GUI_NOTE_OFF_RELEASE) { // note off + env release
|
||||
pat->data[cursor.y][0]=101;
|
||||
pat->data[cursor.y][1]=0;
|
||||
} else if (key==102) { // env release only
|
||||
} else if (key==GUI_NOTE_RELEASE) { // env release only
|
||||
pat->data[cursor.y][0]=102;
|
||||
pat->data[cursor.y][1]=0;
|
||||
} else {
|
||||
|
@ -2931,6 +2931,17 @@ void FurnaceGUI::pointUp(int x, int y, int button) {
|
|||
}
|
||||
orderScrollLocked=false;
|
||||
orderScrollTolerance=false;
|
||||
if (dragMobileMenu) {
|
||||
dragMobileMenu=false;
|
||||
if (mobileMenuOpen) {
|
||||
mobileMenuOpen=(mobileMenuPos>=0.85f);
|
||||
} else {
|
||||
mobileMenuOpen=(mobileMenuPos>=0.15f);
|
||||
}
|
||||
}
|
||||
if (dragMobileEditButton) {
|
||||
dragMobileEditButton=false;
|
||||
}
|
||||
if (selecting) {
|
||||
if (!selectingFull) cursor=selEnd;
|
||||
finishSelection();
|
||||
|
@ -3616,7 +3627,7 @@ bool FurnaceGUI::loop() {
|
|||
editOptions(true);
|
||||
ImGui::Separator();
|
||||
if (ImGui::MenuItem("clear...")) {
|
||||
showWarning("Are you sure you want to clear... (cannot be undone!)",GUI_WARN_CLEAR);
|
||||
doAction(GUI_ACTION_CLEAR);
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
@ -3771,7 +3782,6 @@ bool FurnaceGUI::loop() {
|
|||
globalWinFlags=ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoBringToFrontOnFocus;
|
||||
//globalWinFlags=ImGuiWindowFlags_NoTitleBar;
|
||||
// scene handling goes here!
|
||||
pianoOpen=true;
|
||||
drawMobileControls();
|
||||
switch (mobScene) {
|
||||
case GUI_SCENE_ORDERS:
|
||||
|
@ -3813,6 +3823,9 @@ bool FurnaceGUI::loop() {
|
|||
drawPattern();
|
||||
drawPiano();
|
||||
drawMobileOrderSel();
|
||||
|
||||
globalWinFlags=0;
|
||||
drawFindReplace();
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -3820,6 +3833,7 @@ bool FurnaceGUI::loop() {
|
|||
drawSettings();
|
||||
drawDebug();
|
||||
drawLog();
|
||||
drawStats();
|
||||
} else {
|
||||
globalWinFlags=0;
|
||||
ImGui::DockSpaceOverViewport(NULL,lockLayout?(ImGuiDockNodeFlags_NoWindowMenuButton|ImGuiDockNodeFlags_NoMove|ImGuiDockNodeFlags_NoResize|ImGuiDockNodeFlags_NoCloseButton|ImGuiDockNodeFlags_NoDocking|ImGuiDockNodeFlags_NoDockingSplitMe|ImGuiDockNodeFlags_NoDockingSplitOther):0);
|
||||
|
@ -4755,7 +4769,7 @@ bool FurnaceGUI::loop() {
|
|||
if (ImGui::Button("Orders")) {
|
||||
stop();
|
||||
e->lockEngine([this]() {
|
||||
memset(e->curOrders->ord,0,DIV_MAX_CHANS*256);
|
||||
memset(e->curOrders->ord,0,DIV_MAX_CHANS*DIV_MAX_PATTERNS);
|
||||
e->curSubSong->ordersLen=1;
|
||||
});
|
||||
e->setOrder(0);
|
||||
|
@ -4771,8 +4785,8 @@ bool FurnaceGUI::loop() {
|
|||
e->lockEngine([this]() {
|
||||
for (int i=0; i<e->getTotalChannelCount(); i++) {
|
||||
DivPattern* pat=e->curPat[i].getPattern(e->curOrders->ord[i][curOrder],true);
|
||||
memset(pat->data,-1,256*32*sizeof(short));
|
||||
for (int j=0; j<256; j++) {
|
||||
memset(pat->data,-1,DIV_MAX_ROWS*DIV_MAX_COLS*sizeof(short));
|
||||
for (int j=0; j<DIV_MAX_ROWS; j++) {
|
||||
pat->data[j][0]=0;
|
||||
pat->data[j][1]=0;
|
||||
}
|
||||
|
@ -5139,7 +5153,11 @@ bool FurnaceGUI::init() {
|
|||
volMeterOpen=e->getConfBool("volMeterOpen",true);
|
||||
statsOpen=e->getConfBool("statsOpen",false);
|
||||
compatFlagsOpen=e->getConfBool("compatFlagsOpen",false);
|
||||
#ifdef IS_MOBILE
|
||||
pianoOpen=e->getConfBool("pianoOpen",true);
|
||||
#else
|
||||
pianoOpen=e->getConfBool("pianoOpen",false);
|
||||
#endif
|
||||
notesOpen=e->getConfBool("notesOpen",false);
|
||||
channelsOpen=e->getConfBool("channelsOpen",false);
|
||||
patManagerOpen=e->getConfBool("patManagerOpen",false);
|
||||
|
@ -5648,14 +5666,19 @@ FurnaceGUI::FurnaceGUI():
|
|||
pendingInsSingle(false),
|
||||
displayPendingRawSample(false),
|
||||
snesFilterHex(false),
|
||||
mobileEdit(false),
|
||||
vgmExportVersion(0x171),
|
||||
drawHalt(10),
|
||||
zsmExportTickRate(60),
|
||||
macroPointSize(16),
|
||||
waveEditStyle(0),
|
||||
displayInsTypeListMakeInsSample(-1),
|
||||
mobileEditPage(0),
|
||||
mobileMenuPos(0.0f),
|
||||
autoButtonSize(0.0f),
|
||||
mobileEditAnim(0.0f),
|
||||
mobileEditButtonPos(0.7f,0.7f),
|
||||
mobileEditButtonSize(60.0f,60.0f),
|
||||
curSysSection(NULL),
|
||||
pendingRawSampleDepth(8),
|
||||
pendingRawSampleChannels(1),
|
||||
|
@ -5785,12 +5808,15 @@ FurnaceGUI::FurnaceGUI():
|
|||
keepLoopAlive(false),
|
||||
orderScrollLocked(false),
|
||||
orderScrollTolerance(false),
|
||||
dragMobileMenu(false),
|
||||
dragMobileEditButton(false),
|
||||
curWindow(GUI_WINDOW_NOTHING),
|
||||
nextWindow(GUI_WINDOW_NOTHING),
|
||||
curWindowLast(GUI_WINDOW_NOTHING),
|
||||
curWindowThreadSafe(GUI_WINDOW_NOTHING),
|
||||
lastPatternWidth(0.0f),
|
||||
longThreshold(0.48f),
|
||||
buttonLongThreshold(0.20f),
|
||||
latchNote(-1),
|
||||
latchIns(-2),
|
||||
latchVol(-1),
|
||||
|
@ -5877,6 +5903,7 @@ FurnaceGUI::FurnaceGUI():
|
|||
orderScroll(0.0f),
|
||||
orderScrollSlideOrigin(0.0f),
|
||||
orderScrollRealOrigin(0.0f,0.0f),
|
||||
dragMobileMenuOrigin(0.0f,0.0f),
|
||||
layoutTimeBegin(0),
|
||||
layoutTimeEnd(0),
|
||||
layoutTimeDelta(0),
|
||||
|
@ -5913,6 +5940,7 @@ FurnaceGUI::FurnaceGUI():
|
|||
sampleSelStart(-1),
|
||||
sampleSelEnd(-1),
|
||||
sampleInfo(true),
|
||||
sampleCompatRate(false),
|
||||
sampleDragActive(false),
|
||||
sampleDragMode(false),
|
||||
sampleDrag16(false),
|
||||
|
@ -5959,8 +5987,8 @@ FurnaceGUI::FurnaceGUI():
|
|||
pianoOptionsSet(false),
|
||||
pianoOffset(6),
|
||||
pianoOffsetEdit(9),
|
||||
pianoView(2),
|
||||
pianoInputPadMode(2),
|
||||
pianoView(PIANO_LAYOUT_AUTOMATIC),
|
||||
pianoInputPadMode(PIANO_INPUT_PAD_SPLIT_AUTO),
|
||||
#else
|
||||
pianoOctaves(7),
|
||||
pianoOctavesEdit(4),
|
||||
|
@ -5968,8 +5996,8 @@ FurnaceGUI::FurnaceGUI():
|
|||
pianoSharePosition(true),
|
||||
pianoOffset(6),
|
||||
pianoOffsetEdit(6),
|
||||
pianoView(0),
|
||||
pianoInputPadMode(0),
|
||||
pianoView(PIANO_LAYOUT_STANDARD),
|
||||
pianoInputPadMode(PIANO_INPUT_PAD_DISABLE),
|
||||
#endif
|
||||
hasACED(false),
|
||||
waveGenBaseShape(0),
|
||||
|
@ -6012,7 +6040,7 @@ FurnaceGUI::FurnaceGUI():
|
|||
valueKeys[SDLK_KP_8]=8;
|
||||
valueKeys[SDLK_KP_9]=9;
|
||||
|
||||
memset(willExport,1,32*sizeof(bool));
|
||||
memset(willExport,1,DIV_MAX_CHIPS*sizeof(bool));
|
||||
|
||||
peak[0]=0;
|
||||
peak[1]=0;
|
||||
|
|
|
@ -54,7 +54,7 @@
|
|||
}
|
||||
|
||||
#define CHECK_LONG_HOLD (mobileUI && ImGui::GetIO().MouseDown[ImGuiMouseButton_Left] && ImGui::GetIO().MouseDownDuration[ImGuiMouseButton_Left]>=longThreshold && ImGui::GetIO().MouseDownDurationPrev[ImGuiMouseButton_Left]<longThreshold && ImGui::GetIO().MouseDragMaxDistanceSqr[ImGuiMouseButton_Left]<=ImGui::GetIO().ConfigInertialScrollToleranceSqr)
|
||||
|
||||
#define CHECK_BUTTON_LONG_HOLD (mobileUI && ImGui::GetIO().MouseDown[ImGuiMouseButton_Left] && ImGui::GetIO().MouseDownDuration[ImGuiMouseButton_Left]>=buttonLongThreshold && ImGui::GetIO().MouseDownDurationPrev[ImGuiMouseButton_Left]<buttonLongThreshold)
|
||||
// for now
|
||||
#define NOTIFY_LONG_HOLD \
|
||||
if (vibrator && vibratorAvailable) { \
|
||||
|
@ -421,6 +421,7 @@ enum FurnaceGUIActions {
|
|||
GUI_ACTION_FULLSCREEN,
|
||||
GUI_ACTION_TX81Z_REQUEST,
|
||||
GUI_ACTION_PANIC,
|
||||
GUI_ACTION_CLEAR,
|
||||
|
||||
GUI_ACTION_WINDOW_EDIT_CONTROLS,
|
||||
GUI_ACTION_WINDOW_ORDERS,
|
||||
|
@ -520,6 +521,8 @@ enum FurnaceGUIActions {
|
|||
GUI_ACTION_PAT_COLLAPSE_SONG,
|
||||
GUI_ACTION_PAT_EXPAND_SONG,
|
||||
GUI_ACTION_PAT_LATCH,
|
||||
GUI_ACTION_PAT_SCROLL_MODE,
|
||||
GUI_ACTION_PAT_CLEAR_LATCH,
|
||||
GUI_ACTION_PAT_MAX,
|
||||
|
||||
GUI_ACTION_INS_LIST_MIN,
|
||||
|
@ -653,6 +656,12 @@ enum PasteMode {
|
|||
GUI_PASTE_MODE_INS_BG
|
||||
};
|
||||
|
||||
enum NoteCtrl {
|
||||
GUI_NOTE_OFF=100,
|
||||
GUI_NOTE_OFF_RELEASE=101,
|
||||
GUI_NOTE_RELEASE=102
|
||||
};
|
||||
|
||||
#define FURKMOD_CTRL (1U<<31)
|
||||
#define FURKMOD_SHIFT (1<<29)
|
||||
#define FURKMOD_META (1<<28)
|
||||
|
@ -1088,14 +1097,17 @@ class FurnaceGUI {
|
|||
bool wantCaptureKeyboard, oldWantCaptureKeyboard, displayMacroMenu;
|
||||
bool displayNew, fullScreen, preserveChanPos, wantScrollList, noteInputPoly;
|
||||
bool displayPendingIns, pendingInsSingle, displayPendingRawSample, snesFilterHex;
|
||||
bool willExport[32];
|
||||
bool mobileEdit;
|
||||
bool willExport[DIV_MAX_CHIPS];
|
||||
int vgmExportVersion;
|
||||
int drawHalt;
|
||||
int zsmExportTickRate;
|
||||
int macroPointSize;
|
||||
int waveEditStyle;
|
||||
int displayInsTypeListMakeInsSample;
|
||||
float mobileMenuPos, autoButtonSize;
|
||||
int mobileEditPage;
|
||||
float mobileMenuPos, autoButtonSize, mobileEditAnim;
|
||||
ImVec2 mobileEditButtonPos, mobileEditButtonSize;
|
||||
const int* curSysSection;
|
||||
DivInstrumentFM opllPreview;
|
||||
|
||||
|
@ -1435,13 +1447,14 @@ class FurnaceGUI {
|
|||
SelectionPoint selStart, selEnd, cursor, cursorDrag, dragStart, dragEnd;
|
||||
bool selecting, selectingFull, dragging, curNibble, orderNibble, followOrders, followPattern, changeAllOrders, mobileUI;
|
||||
bool collapseWindow, demandScrollX, fancyPattern, wantPatName, firstFrame, tempoView, waveHex, waveSigned, waveGenVisible, lockLayout, editOptsVisible, latchNibble, nonLatchNibble;
|
||||
bool keepLoopAlive, orderScrollLocked, orderScrollTolerance;
|
||||
bool keepLoopAlive, orderScrollLocked, orderScrollTolerance, dragMobileMenu, dragMobileEditButton;
|
||||
FurnaceGUIWindows curWindow, nextWindow, curWindowLast;
|
||||
std::atomic<FurnaceGUIWindows> curWindowThreadSafe;
|
||||
float peak[2];
|
||||
float patChanX[DIV_MAX_CHANS+1];
|
||||
float patChanSlideY[DIV_MAX_CHANS+1];
|
||||
float lastPatternWidth, longThreshold;
|
||||
float buttonLongThreshold;
|
||||
String nextDesc;
|
||||
String nextDescName;
|
||||
|
||||
|
@ -1581,6 +1594,7 @@ class FurnaceGUI {
|
|||
float nextScroll, nextAddScroll, orderScroll, orderScrollSlideOrigin;
|
||||
|
||||
ImVec2 orderScrollRealOrigin;
|
||||
ImVec2 dragMobileMenuOrigin;
|
||||
|
||||
int layoutTimeBegin, layoutTimeEnd, layoutTimeDelta;
|
||||
int renderTimeBegin, renderTimeEnd, renderTimeDelta;
|
||||
|
@ -1615,7 +1629,7 @@ class FurnaceGUI {
|
|||
int resampleStrat;
|
||||
float amplifyVol;
|
||||
int sampleSelStart, sampleSelEnd;
|
||||
bool sampleInfo;
|
||||
bool sampleInfo, sampleCompatRate;
|
||||
bool sampleDragActive, sampleDragMode, sampleDrag16, sampleZoomAuto;
|
||||
void* sampleDragTarget;
|
||||
ImVec2 sampleDragStart;
|
||||
|
@ -1673,6 +1687,21 @@ class FurnaceGUI {
|
|||
bool followLog;
|
||||
|
||||
// piano
|
||||
enum PianoLayoutMode {
|
||||
PIANO_LAYOUT_STANDARD = 0,
|
||||
PIANO_LAYOUT_CONTINUOUS,
|
||||
PIANO_LAYOUT_AUTOMATIC,
|
||||
PIANO_LAYOUT_MAX
|
||||
};
|
||||
|
||||
enum PianoInputPadMode {
|
||||
PIANO_INPUT_PAD_DISABLE = 0,
|
||||
PIANO_INPUT_PAD_REPLACE,
|
||||
PIANO_INPUT_PAD_SPLIT_AUTO,
|
||||
PIANO_INPUT_PAD_SPLIT_VISIBLE,
|
||||
PIANO_INPUT_PAD_MAX
|
||||
};
|
||||
|
||||
int pianoOctaves, pianoOctavesEdit;
|
||||
bool pianoOptions, pianoSharePosition, pianoOptionsSet;
|
||||
float pianoKeyHit[180];
|
||||
|
|
|
@ -483,6 +483,7 @@ const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={
|
|||
D("FULLSCREEN", "Toggle full-screen", SDLK_F11),
|
||||
D("TX81Z_REQUEST", "Request voice from TX81Z", 0),
|
||||
D("PANIC", "Panic", SDLK_F12),
|
||||
D("CLEAR", "Clear song data", 0),
|
||||
|
||||
D("WINDOW_EDIT_CONTROLS", "Edit Controls", 0),
|
||||
D("WINDOW_ORDERS", "Orders", 0),
|
||||
|
@ -582,6 +583,8 @@ const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={
|
|||
D("PAT_COLLAPSE_SONG", "Collapse song", 0),
|
||||
D("PAT_EXPAND_SONG", "Expand song", 0),
|
||||
D("PAT_LATCH", "Set note input latch", 0),
|
||||
D("PAT_SCROLL_MODE", "Change mobile scroll mode", 0),
|
||||
D("PAT_CLEAR_LATCH", "Clear note input latch", 0),
|
||||
D("PAT_MAX", "", NOT_AN_ACTION),
|
||||
|
||||
D("INS_LIST_MIN", "---Instrument list", NOT_AN_ACTION),
|
||||
|
|
|
@ -2166,10 +2166,16 @@ void FurnaceGUI::drawInsEdit() {
|
|||
if (ImGui::Button(ICON_FA_FOLDER_OPEN "##IELoad")) {
|
||||
doAction(GUI_ACTION_INS_LIST_OPEN_REPLACE);
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Open");
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button(ICON_FA_FLOPPY_O "##IESave")) {
|
||||
doAction(GUI_ACTION_INS_LIST_SAVE);
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Save");
|
||||
}
|
||||
if (ImGui::BeginPopupContextItem("InsSaveFormats",ImGuiMouseButton_Right)) {
|
||||
if (ImGui::MenuItem("save in legacy format...")) {
|
||||
doAction(GUI_ACTION_INS_LIST_SAVE_OLD);
|
||||
|
|
|
@ -177,10 +177,10 @@ void FurnaceGUI::drawOrders() {
|
|||
e->lockSave([this,i,j]() {
|
||||
if (changeAllOrders) {
|
||||
for (int k=0; k<e->getTotalChannelCount(); k++) {
|
||||
if (e->curOrders->ord[k][i]<0xff) e->curOrders->ord[k][i]++;
|
||||
if (e->curOrders->ord[k][i]<(unsigned char)(DIV_MAX_PATTERNS-1)) e->curOrders->ord[k][i]++;
|
||||
}
|
||||
} else {
|
||||
if (e->curOrders->ord[j][i]<0xff) e->curOrders->ord[j][i]++;
|
||||
if (e->curOrders->ord[j][i]<(unsigned char)(DIV_MAX_PATTERNS-1)) e->curOrders->ord[j][i]++;
|
||||
}
|
||||
});
|
||||
e->walkSong(loopOrder,loopRow,loopEnd);
|
||||
|
|
|
@ -30,8 +30,8 @@ void FurnaceGUI::drawPatManager() {
|
|||
}
|
||||
if (!patManagerOpen) return;
|
||||
char id[1024];
|
||||
unsigned char isUsed[256];
|
||||
bool isNull[256];
|
||||
unsigned char isUsed[DIV_MAX_PATTERNS];
|
||||
bool isNull[DIV_MAX_PATTERNS];
|
||||
if (ImGui::Begin("Pattern Manager",&patManagerOpen,globalWinFlags)) {
|
||||
ImGui::Text("Global Tasks");
|
||||
|
||||
|
@ -52,19 +52,19 @@ void FurnaceGUI::drawPatManager() {
|
|||
|
||||
for (int i=0; i<e->getTotalChannelCount(); i++) {
|
||||
ImGui::TableNextRow();
|
||||
memset(isUsed,0,256);
|
||||
memset(isNull,0,256*sizeof(bool));
|
||||
memset(isUsed,0,DIV_MAX_PATTERNS);
|
||||
memset(isNull,0,DIV_MAX_PATTERNS*sizeof(bool));
|
||||
for (int j=0; j<e->curSubSong->ordersLen; j++) {
|
||||
isUsed[e->curSubSong->orders.ord[i][j]]++;
|
||||
}
|
||||
for (int j=0; j<256; j++) {
|
||||
for (int j=0; j<DIV_MAX_PATTERNS; j++) {
|
||||
isNull[j]=(e->curSubSong->pat[i].data[j]==NULL);
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s",e->getChannelShortName(i));
|
||||
|
||||
ImGui::PushID(1000+i);
|
||||
for (int k=0; k<256; k++) {
|
||||
for (int k=0; k<DIV_MAX_PATTERNS; k++) {
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
snprintf(id,1023,"%.2X",k);
|
||||
|
|
|
@ -875,11 +875,11 @@ void FurnaceGUI::drawPattern() {
|
|||
}
|
||||
ImGui::EndDisabled();
|
||||
ImGui::SameLine();
|
||||
ImGui::BeginDisabled(e->curPat[i].effectCols>=8);
|
||||
ImGui::BeginDisabled(e->curPat[i].effectCols>=DIV_MAX_EFFECTS);
|
||||
snprintf(chanID,2048,">##_RCH%d",i);
|
||||
if (ImGui::SmallButton(chanID)) {
|
||||
e->curPat[i].effectCols++;
|
||||
if (e->curPat[i].effectCols>8) e->curPat[i].effectCols=8;
|
||||
if (e->curPat[i].effectCols>DIV_MAX_EFFECTS) e->curPat[i].effectCols=DIV_MAX_EFFECTS;
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
|
|
|
@ -63,7 +63,7 @@ void FurnaceGUI::drawPiano() {
|
|||
if (ImGui::BeginTable("PianoLayout",((pianoOptions && (!mobileUI || !portrait))?2:1),ImGuiTableFlags_BordersInnerV)) {
|
||||
int& off=(e->isPlaying() || pianoSharePosition)?pianoOffset:pianoOffsetEdit;
|
||||
int& oct=(e->isPlaying() || pianoSharePosition)?pianoOctaves:pianoOctavesEdit;
|
||||
bool view=(pianoView==2)?(!e->isPlaying()):pianoView;
|
||||
bool view=(pianoView==PIANO_LAYOUT_AUTOMATIC)?(!e->isPlaying()):pianoView;
|
||||
if (pianoOptions && (!mobileUI || !portrait)) {
|
||||
ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed);
|
||||
}
|
||||
|
@ -76,11 +76,11 @@ void FurnaceGUI::drawPiano() {
|
|||
ImVec2 optionSize=ImVec2((mobileUI && portrait)?((ImGui::GetContentRegionAvail().x-ImGui::GetStyle().ItemSpacing.x*5.0f)/6.0f):(1.2f*optionSizeY),optionSizeY);
|
||||
if (pianoOptionsSet) {
|
||||
if (ImGui::Button("OFF##PianoNOff",optionSize)) {
|
||||
if (edit) noteInput(0,100);
|
||||
if (edit) noteInput(0,GUI_NOTE_OFF);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("===##PianoNRel",optionSize)) {
|
||||
if (edit) noteInput(0,101);
|
||||
if (edit) noteInput(0,GUI_NOTE_OFF_RELEASE);
|
||||
}
|
||||
} else {
|
||||
if (ImGui::Button(ICON_FA_ARROW_LEFT "##PianoLeft",optionSize)) {
|
||||
|
@ -95,29 +95,32 @@ void FurnaceGUI::drawPiano() {
|
|||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::Button(ICON_FA_ELLIPSIS_V "##PianoOptions",optionSize);
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Options");
|
||||
}
|
||||
if (ImGui::BeginPopupContextItem("PianoOptions",ImGuiPopupFlags_MouseButtonLeft)) {
|
||||
ImGui::Text("Key layout:");
|
||||
if (ImGui::RadioButton("Automatic",pianoView==2)) {
|
||||
pianoView=2;
|
||||
if (ImGui::RadioButton("Automatic",pianoView==PIANO_LAYOUT_AUTOMATIC)) {
|
||||
pianoView=PIANO_LAYOUT_AUTOMATIC;
|
||||
}
|
||||
if (ImGui::RadioButton("Standard",pianoView==0)) {
|
||||
pianoView=0;
|
||||
if (ImGui::RadioButton("Standard",pianoView==PIANO_LAYOUT_STANDARD)) {
|
||||
pianoView=PIANO_LAYOUT_STANDARD;
|
||||
}
|
||||
if (ImGui::RadioButton("Continuous",pianoView==1)) {
|
||||
pianoView=1;
|
||||
if (ImGui::RadioButton("Continuous",pianoView==PIANO_LAYOUT_CONTINUOUS)) {
|
||||
pianoView=PIANO_LAYOUT_CONTINUOUS;
|
||||
}
|
||||
ImGui::Text("Value input pad:");
|
||||
if (ImGui::RadioButton("Disabled",pianoInputPadMode==0)) {
|
||||
pianoInputPadMode=0;
|
||||
if (ImGui::RadioButton("Disabled",pianoInputPadMode==PIANO_INPUT_PAD_DISABLE)) {
|
||||
pianoInputPadMode=PIANO_INPUT_PAD_DISABLE;
|
||||
}
|
||||
if (ImGui::RadioButton("Replace piano",pianoInputPadMode==1)) {
|
||||
pianoInputPadMode=1;
|
||||
if (ImGui::RadioButton("Replace piano",pianoInputPadMode==PIANO_INPUT_PAD_REPLACE)) {
|
||||
pianoInputPadMode=PIANO_INPUT_PAD_REPLACE;
|
||||
}
|
||||
if (ImGui::RadioButton("Split (automatic)",pianoInputPadMode==2)) {
|
||||
pianoInputPadMode=2;
|
||||
if (ImGui::RadioButton("Split (automatic)",pianoInputPadMode==PIANO_INPUT_PAD_SPLIT_AUTO)) {
|
||||
pianoInputPadMode=PIANO_INPUT_PAD_SPLIT_AUTO;
|
||||
}
|
||||
if (ImGui::RadioButton("Split (always visible)",pianoInputPadMode==3)) {
|
||||
pianoInputPadMode=3;
|
||||
if (ImGui::RadioButton("Split (always visible)",pianoInputPadMode==PIANO_INPUT_PAD_SPLIT_VISIBLE)) {
|
||||
pianoInputPadMode=PIANO_INPUT_PAD_SPLIT_VISIBLE;
|
||||
}
|
||||
ImGui::Checkbox("Share play/edit offset/range",&pianoSharePosition);
|
||||
ImGui::EndPopup();
|
||||
|
@ -129,7 +132,7 @@ void FurnaceGUI::drawPiano() {
|
|||
|
||||
if (pianoOptionsSet) {
|
||||
if (ImGui::Button("REL##PianoNMRel",optionSize)) {
|
||||
if (edit) noteInput(0,102);
|
||||
if (edit) noteInput(0,GUI_NOTE_RELEASE);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button(ICON_FA_TIMES "##PianoDelP",optionSize)) {
|
||||
|
@ -158,7 +161,7 @@ void FurnaceGUI::drawPiano() {
|
|||
}
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
if (pianoInputPadMode==1 && cursor.xFine>0 && curWindow==GUI_WINDOW_PATTERN) {
|
||||
if (pianoInputPadMode==PIANO_INPUT_PAD_REPLACE && cursor.xFine>0 && curWindow==GUI_WINDOW_PATTERN) {
|
||||
ImVec2 buttonSize=ImGui::GetContentRegionAvail();
|
||||
if (ImGui::BeginTable("InputPadP",8,ImGuiTableFlags_SizingFixedSame)) {
|
||||
ImGui::TableNextRow();
|
||||
|
@ -431,7 +434,7 @@ void FurnaceGUI::drawPiano() {
|
|||
ImGui::End();
|
||||
|
||||
// draw input pad if necessary
|
||||
if (curWindow==GUI_WINDOW_PATTERN && ((pianoInputPadMode==2 && cursor.xFine>0) || pianoInputPadMode==3)) {
|
||||
if (curWindow==GUI_WINDOW_PATTERN && ((pianoInputPadMode==PIANO_INPUT_PAD_SPLIT_AUTO && cursor.xFine>0) || pianoInputPadMode==PIANO_INPUT_PAD_SPLIT_VISIBLE)) {
|
||||
if (ImGui::Begin("Input Pad",NULL,ImGuiWindowFlags_NoTitleBar)) {
|
||||
ImGui::BeginDisabled(cursor.xFine==0);
|
||||
if (ImGui::BeginTable("InputPad",3,ImGuiTableFlags_Borders)) {
|
||||
|
|
|
@ -118,10 +118,16 @@ void FurnaceGUI::drawSampleEdit() {
|
|||
if (ImGui::Button(ICON_FA_FOLDER_OPEN "##SELoad")) {
|
||||
doAction(GUI_ACTION_SAMPLE_LIST_OPEN_REPLACE);
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Open");
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button(ICON_FA_FLOPPY_O "##SESave")) {
|
||||
doAction(GUI_ACTION_SAMPLE_LIST_SAVE);
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Save");
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
|
@ -134,7 +140,32 @@ void FurnaceGUI::drawSampleEdit() {
|
|||
|
||||
ImGui::Separator();
|
||||
|
||||
if (ImGui::BeginTable("SampleProps",4,ImGuiTableFlags_SizingStretchSame|ImGuiTableFlags_BordersV|ImGuiTableFlags_BordersOuterH)) {
|
||||
bool isChipVisible[DIV_MAX_CHIPS];
|
||||
bool isTypeVisible[DIV_MAX_SAMPLE_TYPE];
|
||||
bool isMemVisible[DIV_MAX_SAMPLE_TYPE][DIV_MAX_CHIPS];
|
||||
bool isMemWarning[DIV_MAX_SAMPLE_TYPE][DIV_MAX_CHIPS];
|
||||
memset(isChipVisible,0,DIV_MAX_CHIPS*sizeof(bool));
|
||||
memset(isTypeVisible,0,DIV_MAX_SAMPLE_TYPE*sizeof(bool));
|
||||
memset(isMemVisible,0,DIV_MAX_CHIPS*DIV_MAX_SAMPLE_TYPE*sizeof(bool));
|
||||
memset(isMemWarning,0,DIV_MAX_CHIPS*DIV_MAX_SAMPLE_TYPE*sizeof(bool));
|
||||
for (int i=0; i<e->song.systemLen; i++) {
|
||||
DivDispatch* dispatch=e->getDispatch(i);
|
||||
if (dispatch==NULL) continue;
|
||||
|
||||
for (int j=0; j<DIV_MAX_SAMPLE_TYPE; j++) {
|
||||
if (dispatch->getSampleMemCapacity(j)==0) continue;
|
||||
isChipVisible[i]=true;
|
||||
isTypeVisible[j]=true;
|
||||
isMemVisible[j][i]=true;
|
||||
if (!dispatch->isSampleLoaded(j,curSample)) isMemWarning[j][i]=true;
|
||||
}
|
||||
}
|
||||
int selColumns=1;
|
||||
for (int i=0; i<DIV_MAX_CHIPS; i++) {
|
||||
if (isChipVisible[i]) selColumns++;
|
||||
}
|
||||
|
||||
if (ImGui::BeginTable("SampleProps",(selColumns>1)?4:3,ImGuiTableFlags_SizingStretchSame|ImGuiTableFlags_BordersV|ImGuiTableFlags_BordersOuterH)) {
|
||||
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::Button(sampleInfo?(ICON_FA_CHEVRON_UP "##SECollapse"):(ICON_FA_CHEVRON_DOWN "##SECollapse"))) {
|
||||
|
@ -143,7 +174,20 @@ void FurnaceGUI::drawSampleEdit() {
|
|||
ImGui::SameLine();
|
||||
ImGui::Text("Info");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("Rate");
|
||||
pushToggleColors(!sampleCompatRate);
|
||||
if (ImGui::Button("Rate")) {
|
||||
sampleCompatRate=false;
|
||||
}
|
||||
popToggleColors();
|
||||
ImGui::SameLine();
|
||||
pushToggleColors(sampleCompatRate);
|
||||
if (ImGui::Button("Compat Rate")) {
|
||||
sampleCompatRate=true;
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("used in DefleMask-compatible sample mode (17xx), in where samples are mapped to an octave.");
|
||||
}
|
||||
popToggleColors();
|
||||
ImGui::TableNextColumn();
|
||||
bool doLoop=(sample->isLoopable());
|
||||
if (ImGui::Checkbox("Loop",&doLoop)) { MARK_MODIFIED
|
||||
|
@ -164,8 +208,11 @@ void FurnaceGUI::drawSampleEdit() {
|
|||
if (ImGui::IsItemHovered() && sample->depth==DIV_SAMPLE_DEPTH_BRR) {
|
||||
ImGui::SetTooltip("changing the loop in a BRR sample may result in glitches!");
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("Chips");
|
||||
|
||||
if (selColumns>1) {
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("Chips");
|
||||
}
|
||||
|
||||
if (sampleInfo) {
|
||||
ImGui::TableNextRow();
|
||||
|
@ -190,21 +237,135 @@ void FurnaceGUI::drawSampleEdit() {
|
|||
ImGui::EndCombo();
|
||||
}
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("C-4 (Hz)");
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||
if (ImGui::InputInt("##SampleCenter",&sample->centerRate,10,200)) { MARK_MODIFIED
|
||||
if (sample->centerRate<100) sample->centerRate=100;
|
||||
if (sample->centerRate>65535) sample->centerRate=65535;
|
||||
bool isThereSNES=false;
|
||||
for (int i=0; i<e->song.systemLen; i++) {
|
||||
if (e->song.system[i]==DIV_SYSTEM_SNES) {
|
||||
isThereSNES=true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (sample->depth==DIV_SAMPLE_DEPTH_BRR || isThereSNES) {
|
||||
bool be=sample->brrEmphasis;
|
||||
if (ImGui::Checkbox("BRR emphasis",&be)) {
|
||||
sample->prepareUndo(true);
|
||||
sample->brrEmphasis=be;
|
||||
e->renderSamplesP();
|
||||
updateSampleTex=true;
|
||||
MARK_MODIFIED;
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
if (sample->depth==DIV_SAMPLE_DEPTH_BRR) {
|
||||
ImGui::SetTooltip("this is a BRR sample.\nenabling this option will muffle it (only affects non-SNES chips).");
|
||||
} else {
|
||||
ImGui::SetTooltip("enable this option to slightly boost high frequencies\nto compensate for the SNES' Gaussian filter's muffle.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::Text("Compat");
|
||||
int targetRate=sampleCompatRate?sample->rate:sample->centerRate;
|
||||
int sampleNote=round(64.0+(128.0*12.0*log((double)targetRate/8363.0)/log(2.0)));
|
||||
int sampleNoteCoarse=60+(sampleNote>>7);
|
||||
int sampleNoteFine=(sampleNote&127)-64;
|
||||
|
||||
if (sampleNoteCoarse<0) {
|
||||
sampleNoteCoarse=0;
|
||||
sampleNoteFine=-64;
|
||||
}
|
||||
if (sampleNoteCoarse>119) {
|
||||
sampleNoteCoarse=119;
|
||||
sampleNoteFine=63;
|
||||
}
|
||||
|
||||
bool coarseChanged=false;
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("Hz");
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||
if (ImGui::InputInt("##SampleRate",&sample->rate,10,200)) { MARK_MODIFIED
|
||||
if (sample->rate<100) sample->rate=100;
|
||||
if (sample->rate>96000) sample->rate=96000;
|
||||
if (ImGui::InputInt("##SampleRate",&targetRate,10,200)) { MARK_MODIFIED
|
||||
if (targetRate<100) targetRate=100;
|
||||
if (targetRate>384000) targetRate=384000;
|
||||
|
||||
if (sampleCompatRate) {
|
||||
sample->rate=targetRate;
|
||||
} else {
|
||||
sample->centerRate=targetRate;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::Text("Note");
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||
if (ImGui::BeginCombo("##SampleNote",noteNames[sampleNoteCoarse+60])) {
|
||||
char temp[1024];
|
||||
for (int i=0; i<120; i++) {
|
||||
snprintf(temp,1023,"%s##_SRN%d",noteNames[i+60],i);
|
||||
if (ImGui::Selectable(temp,i==sampleNoteCoarse)) {
|
||||
sampleNoteCoarse=i;
|
||||
coarseChanged=true;
|
||||
}
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
} else if (ImGui::IsItemHovered()) {
|
||||
if (wheelY!=0) {
|
||||
sampleNoteCoarse-=wheelY;
|
||||
if (sampleNoteCoarse<0) {
|
||||
sampleNoteCoarse=0;
|
||||
sampleNoteFine=-64;
|
||||
}
|
||||
if (sampleNoteCoarse>119) {
|
||||
sampleNoteCoarse=119;
|
||||
sampleNoteFine=63;
|
||||
}
|
||||
coarseChanged=true;
|
||||
}
|
||||
}
|
||||
|
||||
if (coarseChanged) { MARK_MODIFIED
|
||||
sampleNote=((sampleNoteCoarse-60)<<7)+sampleNoteFine;
|
||||
|
||||
targetRate=8363.0*pow(2.0,(double)sampleNote/(128.0*12.0));
|
||||
if (targetRate<100) targetRate=100;
|
||||
if (targetRate>384000) targetRate=384000;
|
||||
|
||||
if (sampleCompatRate) {
|
||||
sample->rate=targetRate;
|
||||
} else {
|
||||
sample->centerRate=targetRate;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::Text("Fine");
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||
int prevFine=sampleNoteFine;
|
||||
int prevSampleRate=targetRate;
|
||||
if (ImGui::InputInt("##SampleFine",&sampleNoteFine,1,10)) { MARK_MODIFIED
|
||||
if (sampleNoteFine>63) sampleNoteFine=63;
|
||||
if (sampleNoteFine<-64) sampleNoteFine=-64;
|
||||
|
||||
sampleNote=((sampleNoteCoarse-60)<<7)+sampleNoteFine;
|
||||
|
||||
targetRate=round(8363.0*pow(2.0,(double)sampleNote/(128.0*12.0)));
|
||||
|
||||
if (targetRate==prevSampleRate) {
|
||||
if (prevFine==sampleNoteFine) {
|
||||
// do nothing
|
||||
} else if (prevFine>sampleNoteFine) { // coarse incr/decr due to precision loss
|
||||
targetRate--;
|
||||
} else {
|
||||
targetRate++;
|
||||
}
|
||||
}
|
||||
|
||||
if (targetRate<100) targetRate=100;
|
||||
if (targetRate>384000) targetRate=384000;
|
||||
|
||||
if (sampleCompatRate) {
|
||||
sample->rate=targetRate;
|
||||
} else {
|
||||
sample->centerRate=targetRate;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
|
@ -272,35 +433,8 @@ void FurnaceGUI::drawSampleEdit() {
|
|||
}
|
||||
ImGui::EndDisabled();
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
bool isChipVisible[32];
|
||||
bool isTypeVisible[4];
|
||||
bool isMemVisible[4][32];
|
||||
bool isMemWarning[4][32];
|
||||
memset(isChipVisible,0,32*sizeof(bool));
|
||||
memset(isTypeVisible,0,4*sizeof(bool));
|
||||
memset(isMemVisible,0,32*4*sizeof(bool));
|
||||
memset(isMemWarning,0,32*4*sizeof(bool));
|
||||
for (int i=0; i<e->song.systemLen; i++) {
|
||||
DivDispatch* dispatch=e->getDispatch(i);
|
||||
if (dispatch==NULL) continue;
|
||||
|
||||
for (int j=0; j<4; j++) {
|
||||
if (dispatch->getSampleMemCapacity(j)==0) continue;
|
||||
isChipVisible[i]=true;
|
||||
isTypeVisible[j]=true;
|
||||
isMemVisible[j][i]=true;
|
||||
if (!dispatch->isSampleLoaded(j,curSample)) isMemWarning[j][i]=true;
|
||||
}
|
||||
}
|
||||
int selColumns=1;
|
||||
for (int i=0; i<32; i++) {
|
||||
if (isChipVisible[i]) selColumns++;
|
||||
}
|
||||
if (selColumns<=1) {
|
||||
ImGui::Text("NO CHIPS LESS GOOO");
|
||||
} else {
|
||||
if (selColumns>1) {
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::BeginTable("SEChipSel",selColumns,ImGuiTableFlags_SizingFixedSame)) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
|
@ -310,7 +444,7 @@ void FurnaceGUI::drawSampleEdit() {
|
|||
ImGui::Text("%d",i+1);
|
||||
}
|
||||
char id[1024];
|
||||
for (int i=0; i<4; i++) {
|
||||
for (int i=0; i<DIV_MAX_SAMPLE_TYPE; i++) {
|
||||
if (!isTypeVisible[i]) continue;
|
||||
|
||||
ImGui::TableNextRow();
|
||||
|
@ -847,7 +981,26 @@ void FurnaceGUI::drawSampleEdit() {
|
|||
|
||||
ImGui::Separator();
|
||||
|
||||
ImVec2 avail=ImGui::GetContentRegionAvail(); // graph size determined here
|
||||
// time
|
||||
ImDrawList* dl=ImGui::GetWindowDrawList();
|
||||
ImGuiWindow* window=ImGui::GetCurrentWindow();
|
||||
|
||||
ImVec2 size=ImVec2(ImGui::GetContentRegionAvail().x,ImGui::GetFontSize()+2.0*dpiScale);
|
||||
|
||||
ImVec2 minArea=window->DC.CursorPos;
|
||||
ImVec2 maxArea=ImVec2(
|
||||
minArea.x+size.x,
|
||||
minArea.y+size.y
|
||||
);
|
||||
ImRect rect=ImRect(minArea,maxArea);
|
||||
ImGuiStyle& style=ImGui::GetStyle();
|
||||
|
||||
ImGui::ItemSize(size,style.FramePadding.y);
|
||||
if (ImGui::ItemAdd(rect,ImGui::GetID("SETime"))) {
|
||||
dl->AddText(minArea,0xffffffff,"0");
|
||||
}
|
||||
|
||||
ImVec2 avail=ImGui::GetContentRegionAvail(); // sample view size determined here
|
||||
// don't do this. reason: mobile.
|
||||
/*if (ImGui::GetContentRegionAvail().y>(ImGui::GetContentRegionAvail().x*0.5f)) {
|
||||
avail=ImVec2(ImGui::GetContentRegionAvail().x,ImGui::GetContentRegionAvail().x*0.5f);
|
||||
|
@ -1075,6 +1228,8 @@ void FurnaceGUI::drawSampleEdit() {
|
|||
}
|
||||
|
||||
String statusBar=sampleDragMode?"Draw":"Select";
|
||||
String statusBar2="";
|
||||
String statusBar3=fmt::sprintf("%d samples, %d bytes",sample->samples,sample->getCurBufLen());
|
||||
bool drawSelection=false;
|
||||
|
||||
if (!sampleDragMode) {
|
||||
|
@ -1155,12 +1310,15 @@ void FurnaceGUI::drawSampleEdit() {
|
|||
}
|
||||
posY=(0.5-pos.y/rectSize.y)*((sample->depth==DIV_SAMPLE_DEPTH_8BIT)?255:32767);
|
||||
if (posX>=0) {
|
||||
statusBar+=fmt::sprintf(" | (%d, %d)",posX,posY);
|
||||
statusBar2=fmt::sprintf("(%d, %d)",posX,posY);
|
||||
}
|
||||
}
|
||||
|
||||
if (e->isPreviewingSample()) {
|
||||
statusBar+=fmt::sprintf(" | %.2fHz",e->getSamplePreviewRate());
|
||||
if (!statusBar2.empty()) {
|
||||
statusBar2+=" | ";
|
||||
}
|
||||
statusBar2+=fmt::sprintf("%.2fHz",e->getSamplePreviewRate());
|
||||
|
||||
int start=sampleSelStart;
|
||||
int end=sampleSelEnd;
|
||||
|
@ -1243,7 +1401,23 @@ void FurnaceGUI::drawSampleEdit() {
|
|||
}
|
||||
|
||||
ImGui::SetCursorPosY(ImGui::GetCursorPosY()+ImGui::GetStyle().ScrollbarSize);
|
||||
ImGui::Text("%s",statusBar.c_str());
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_CellPadding,ImVec2(0,0));
|
||||
if (ImGui::BeginTable("SEStatus",3,ImGuiTableFlags_BordersInnerV)) {
|
||||
ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthStretch,0.7);
|
||||
ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthStretch,0.3);
|
||||
ImGui::TableSetupColumn("c2",ImGuiTableColumnFlags_WidthFixed);
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(statusBar.c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(statusBar2.c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(statusBar3.c_str());
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
ImGui::PopStyleVar();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -180,7 +180,7 @@ void FurnaceGUI::drawSongInfo(bool asChild) {
|
|||
int patLen=e->curSubSong->patLen;
|
||||
if (ImGui::InputInt("##PatLength",&patLen,1,3)) { MARK_MODIFIED
|
||||
if (patLen<1) patLen=1;
|
||||
if (patLen>256) patLen=256;
|
||||
if (patLen>DIV_MAX_PATTERNS) patLen=DIV_MAX_PATTERNS;
|
||||
e->curSubSong->patLen=patLen;
|
||||
}
|
||||
|
||||
|
@ -192,7 +192,7 @@ void FurnaceGUI::drawSongInfo(bool asChild) {
|
|||
int ordLen=e->curSubSong->ordersLen;
|
||||
if (ImGui::InputInt("##OrdLength",&ordLen,1,3)) { MARK_MODIFIED
|
||||
if (ordLen<1) ordLen=1;
|
||||
if (ordLen>256) ordLen=256;
|
||||
if (ordLen>DIV_MAX_PATTERNS) ordLen=DIV_MAX_PATTERNS;
|
||||
e->curSubSong->ordersLen=ordLen;
|
||||
if (curOrder>=ordLen) {
|
||||
setOrder(ordLen-1);
|
||||
|
|
|
@ -50,10 +50,16 @@ void FurnaceGUI::drawSubSongs() {
|
|||
if (ImGui::SmallButton(ICON_FA_ARROW_UP "##SubUp")) {
|
||||
e->moveSubSongUp(i);
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Move up");
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::SmallButton(ICON_FA_ARROW_DOWN "##SubDown")) {
|
||||
e->moveSubSongDown(i);
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Move down");
|
||||
}
|
||||
ImGui::PopID();
|
||||
}
|
||||
ImGui::EndTable();
|
||||
|
@ -79,6 +85,9 @@ void FurnaceGUI::drawSubSongs() {
|
|||
MARK_MODIFIED;
|
||||
}
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Add");
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button(ICON_FA_MINUS "##SubSongDel")) {
|
||||
if (e->song.subsong.size()<=1) {
|
||||
|
@ -87,6 +96,9 @@ void FurnaceGUI::drawSubSongs() {
|
|||
showWarning("are you sure you want to remove this subsong?",GUI_WARN_SUBSONG_DEL);
|
||||
}
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Remove");
|
||||
}
|
||||
|
||||
ImGui::Text("Name");
|
||||
ImGui::SameLine();
|
||||
|
|
|
@ -112,7 +112,7 @@ void FurnaceGUI::drawSysManager() {
|
|||
ImGui::EndDisabled();
|
||||
ImGui::PopID();
|
||||
}
|
||||
if (e->song.systemLen<32) {
|
||||
if (e->song.systemLen<DIV_MAX_CHIPS) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TableNextColumn();
|
||||
|
|
|
@ -241,10 +241,16 @@ void FurnaceGUI::drawWaveEdit() {
|
|||
if (ImGui::Button(ICON_FA_FOLDER_OPEN "##WELoad")) {
|
||||
doAction(GUI_ACTION_WAVE_LIST_OPEN_REPLACE);
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Open");
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button(ICON_FA_FLOPPY_O "##WESave")) {
|
||||
doAction(GUI_ACTION_WAVE_LIST_SAVE);
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Save");
|
||||
}
|
||||
if (ImGui::BeginPopupContextItem("WaveSaveFormats",ImGuiMouseButton_Right)) {
|
||||
if (ImGui::MenuItem("save as .dmw...")) {
|
||||
doAction(GUI_ACTION_WAVE_LIST_SAVE_DMW);
|
||||
|
@ -643,11 +649,11 @@ void FurnaceGUI::drawWaveEdit() {
|
|||
}
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::Button("Scale Y")) {
|
||||
if (waveGenScaleY>0 && wave->max!=waveGenScaleY) e->lockEngine([this,wave]() {
|
||||
if (waveGenScaleY>0 && wave->max!=(waveGenScaleY-1)) e->lockEngine([this,wave]() {
|
||||
for (int i=0; i<wave->len; i++) {
|
||||
wave->data[i]=(wave->data[i]*(waveGenScaleY+1))/(wave->max+1);
|
||||
}
|
||||
wave->max=waveGenScaleY;
|
||||
wave->max=waveGenScaleY-1;
|
||||
MARK_MODIFIED;
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue