Merge branch 'master' into duplicate_channel_struct
This commit is contained in:
commit
1ae1f32574
|
@ -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.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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 New Issue