Merge branch 'master' of https://github.com/tildearrow/furnace into ym2610b
# Conflicts: # src/engine/engine.cpp # src/engine/platform/ym2610Interface.cpp
This commit is contained in:
		
						commit
						d831a522a4
					
				|  | @ -25,6 +25,8 @@ furthermore, an `or reserved` indicates this field is always present, but is res | ||||||
| 
 | 
 | ||||||
| the format versions are: | the format versions are: | ||||||
| 
 | 
 | ||||||
|  | - 59: Furnace dev59 | ||||||
|  | - 58: Furnace dev58 | ||||||
| - 57: Furnace dev57 | - 57: Furnace dev57 | ||||||
| 
 | 
 | ||||||
| - 53: Furnace 0.5.7 | - 53: Furnace 0.5.7 | ||||||
|  | @ -202,6 +204,8 @@ size | description | ||||||
|  S?? | channel short names |  S?? | channel short names | ||||||
|      | - same as above |      | - same as above | ||||||
|  STR | song comment |  STR | song comment | ||||||
|  |   4f | master volume, 1.0f=100% (>=59) | ||||||
|  |      | this is 2.0f for modules before 59 | ||||||
| 
 | 
 | ||||||
| # instrument | # instrument | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -444,46 +444,67 @@ void DivEngine::renderSamples() { | ||||||
|   sPreview.sample=-1; |   sPreview.sample=-1; | ||||||
|   sPreview.pos=0; |   sPreview.pos=0; | ||||||
| 
 | 
 | ||||||
|  |   // step 1: render samples
 | ||||||
|   for (int i=0; i<song.sampleLen; i++) { |   for (int i=0; i<song.sampleLen; i++) { | ||||||
|     song.sample[i]->render(); |     song.sample[i]->render(); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   /*
 |   // step 2: allocate ADPCM-A samples
 | ||||||
|   // step 3: allocate ADPCM samples
 |   if (adpcmAMem==NULL) adpcmAMem=new unsigned char[16777216]; | ||||||
|   if (adpcmMem==NULL) adpcmMem=new unsigned char[16777216]; |  | ||||||
| 
 | 
 | ||||||
|   size_t memPos=0; |   size_t memPos=0; | ||||||
|   for (int i=0; i<song.sampleLen; i++) { |   for (int i=0; i<song.sampleLen; i++) { | ||||||
|     DivSample* s=song.sample[i]; |     DivSample* s=song.sample[i]; | ||||||
|     if ((memPos&0xf00000)!=((memPos+s->adpcmRendLength)&0xf00000)) { |     int paddedLen=(s->lengthA+255)&(~0xff); | ||||||
|  |     if ((memPos&0xf00000)!=((memPos+paddedLen)&0xf00000)) { | ||||||
|       memPos=(memPos+0xfffff)&0xf00000; |       memPos=(memPos+0xfffff)&0xf00000; | ||||||
|     } |     } | ||||||
|     if (memPos>=16777216) { |     if (memPos>=16777216) { | ||||||
|       logW("out of ADPCM memory for sample %d!\n",i); |       logW("out of ADPCM memory for sample %d!\n",i); | ||||||
|       break; |       break; | ||||||
|     } |     } | ||||||
|     if (memPos+s->adpcmRendLength>=16777216) { |     if (memPos+paddedLen>=16777216) { | ||||||
|       memcpy(adpcmMem+memPos,s->adpcmRendData,16777216-memPos); |       memcpy(adpcmAMem+memPos,s->dataA,16777216-memPos); | ||||||
|       logW("out of ADPCM memory for sample %d!\n",i); |       logW("out of ADPCM memory for sample %d!\n",i); | ||||||
|     } else { |     } else { | ||||||
|       memcpy(adpcmMem+memPos,s->adpcmRendData,s->adpcmRendLength); |       memcpy(adpcmAMem+memPos,s->dataA,paddedLen); | ||||||
|     } |     } | ||||||
|     s->rendOff=memPos; |     s->offA=memPos; | ||||||
|     memPos+=s->adpcmRendLength; |     memPos+=paddedLen; | ||||||
|   } |   } | ||||||
|   adpcmMemLen=memPos+256; |   adpcmAMemLen=memPos+256; | ||||||
| 
 | 
 | ||||||
|   // step 4: allocate qsound pcm samples
 |   // step 3: allocate ADPCM-B samples
 | ||||||
|   if (qsoundMem==NULL) qsoundMem=new unsigned char[16777216]; |   if (adpcmBMem==NULL) adpcmBMem=new unsigned char[16777216]; | ||||||
| 
 |  | ||||||
|   memset(qsoundMem, 0, 16777216); |  | ||||||
| 
 | 
 | ||||||
|   memPos=0; |   memPos=0; | ||||||
|   for (int i=0; i<song.sampleLen; i++) { |   for (int i=0; i<song.sampleLen; i++) { | ||||||
|     DivSample* s=song.sample[i]; |     DivSample* s=song.sample[i]; | ||||||
|     int length = s->rendLength; |     int paddedLen=(s->lengthB+255)&(~0xff); | ||||||
|     if (length > 65536-16) { |     if (memPos>=16777216) { | ||||||
|       length = 65536-16; |       logW("out of ADPCM-B memory for sample %d!\n",i); | ||||||
|  |       break; | ||||||
|  |     } | ||||||
|  |     if (memPos+paddedLen>=16777216) { | ||||||
|  |       memcpy(adpcmBMem+memPos,s->dataB,16777216-memPos); | ||||||
|  |       logW("out of ADPCM-B memory for sample %d!\n",i); | ||||||
|  |     } else { | ||||||
|  |       memcpy(adpcmBMem+memPos,s->dataB,paddedLen); | ||||||
|  |     } | ||||||
|  |     s->offB=memPos; | ||||||
|  |     memPos+=paddedLen; | ||||||
|  |   } | ||||||
|  |   adpcmBMemLen=memPos+256; | ||||||
|  | 
 | ||||||
|  |   // step 4: allocate qsound pcm samples
 | ||||||
|  |   if (qsoundMem==NULL) qsoundMem=new unsigned char[16777216]; | ||||||
|  | 
 | ||||||
|  |   memPos=0; | ||||||
|  |   for (int i=0; i<song.sampleLen; i++) { | ||||||
|  |     DivSample* s=song.sample[i]; | ||||||
|  |     int length=s->length8; | ||||||
|  |     if (length>65536-16) { | ||||||
|  |       length=65536-16; | ||||||
|     } |     } | ||||||
|     if ((memPos&0xff0000)!=((memPos+length)&0xff0000)) { |     if ((memPos&0xff0000)!=((memPos+length)&0xff0000)) { | ||||||
|       memPos=(memPos+0xffff)&0xff0000; |       memPos=(memPos+0xffff)&0xff0000; | ||||||
|  | @ -494,41 +515,18 @@ void DivEngine::renderSamples() { | ||||||
|     } |     } | ||||||
|     if (memPos+length>=16777216) { |     if (memPos+length>=16777216) { | ||||||
|       for (unsigned int i=0; i<16777216-(memPos+length); i++) { |       for (unsigned int i=0; i<16777216-(memPos+length); i++) { | ||||||
|         qsoundMem[(memPos + i) ^ 0x8000] = s->rendData[i] >> ((s->depth == 16) ? 8 : 0); |         qsoundMem[(memPos+i)^0x8000]=s->data8[i]; | ||||||
|       } |       } | ||||||
|       logW("out of QSound PCM memory for sample %d!\n",i); |       logW("out of QSound PCM memory for sample %d!\n",i); | ||||||
|     } else { |     } else { | ||||||
|       for (int i=0; i<length; i++) { |       for (int i=0; i<length; i++) { | ||||||
|         qsoundMem[(memPos + i) ^ 0x8000] = s->rendData[i] >> ((s->depth == 16) ? 8 : 0); |         qsoundMem[(memPos+i)^0x8000]=s->data8[i]; | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|     s->rendOffQsound=memPos ^ 0x8000; |     s->offQSound=memPos^0x8000; | ||||||
|     memPos+=length+16; |     memPos+=length+16; | ||||||
|   } |   } | ||||||
|   qsoundMemLen=memPos+256; |   qsoundMemLen=memPos+256; | ||||||
| 
 |  | ||||||
|   // step 5: allocate ADPCM-B samples
 |  | ||||||
|   if (adpcmBMem==NULL) adpcmBMem=new unsigned char[16777216]; |  | ||||||
| 
 |  | ||||||
|   memPos=0; |  | ||||||
|   for (int i=0; i<song.sampleLen; i++) { |  | ||||||
|     DivSample* s=song.sample[i]; |  | ||||||
|     if (memPos>=16777216) { |  | ||||||
|       logW("out of ADPCM-B memory for sample %d!\n",i); |  | ||||||
|       break; |  | ||||||
|     } |  | ||||||
|     if (memPos+s->adpcmBRendLength>=16777216) { |  | ||||||
|       memcpy(adpcmBMem+memPos,s->adpcmBRendData,16777216-memPos); |  | ||||||
|       logW("out of ADPCM-B memory for sample %d!\n",i); |  | ||||||
|     } else { |  | ||||||
|       memcpy(adpcmBMem+memPos,s->adpcmBRendData,s->adpcmBRendLength); |  | ||||||
|     } |  | ||||||
|     s->rendOff=memPos; |  | ||||||
|     memPos+=s->adpcmBRendLength; |  | ||||||
|   } |  | ||||||
|   adpcmBMemLen=memPos+256; |  | ||||||
| 
 |  | ||||||
|   */ |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void DivEngine::createNew() { | void DivEngine::createNew() { | ||||||
|  |  | ||||||
|  | @ -37,8 +37,8 @@ | ||||||
|     warnings+=(String("\n")+x); \ |     warnings+=(String("\n")+x); \ | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
| #define DIV_VERSION "dev58" | #define DIV_VERSION "dev59" | ||||||
| #define DIV_ENGINE_VERSION 58 | #define DIV_ENGINE_VERSION 59 | ||||||
| 
 | 
 | ||||||
| enum DivStatusView { | enum DivStatusView { | ||||||
|   DIV_STATUS_NOTHING=0, |   DIV_STATUS_NOTHING=0, | ||||||
|  | @ -621,12 +621,16 @@ class DivEngine { | ||||||
|     // terminate the engine.
 |     // terminate the engine.
 | ||||||
|     bool quit(); |     bool quit(); | ||||||
| 
 | 
 | ||||||
|     unsigned char* adpcmMem; |     unsigned char* adpcmAMem; | ||||||
|     size_t adpcmMemLen; |     size_t adpcmAMemLen; | ||||||
|     unsigned char* adpcmBMem; |     unsigned char* adpcmBMem; | ||||||
|     size_t adpcmBMemLen; |     size_t adpcmBMemLen; | ||||||
|     unsigned char* qsoundMem; |     unsigned char* qsoundMem; | ||||||
|     size_t qsoundMemLen; |     size_t qsoundMemLen; | ||||||
|  |     unsigned char* qsoundAMem; | ||||||
|  |     size_t qsoundAMemLen; | ||||||
|  |     unsigned char* dpcmMem; | ||||||
|  |     size_t dpcmMemLen; | ||||||
| 
 | 
 | ||||||
|     DivEngine(): |     DivEngine(): | ||||||
|       output(NULL), |       output(NULL), | ||||||
|  | @ -680,11 +684,15 @@ class DivEngine { | ||||||
|       totalProcessed(0), |       totalProcessed(0), | ||||||
|       oscBuf{NULL,NULL}, |       oscBuf{NULL,NULL}, | ||||||
|       oscSize(1), |       oscSize(1), | ||||||
|       adpcmMem(NULL), |       adpcmAMem(NULL), | ||||||
|       adpcmMemLen(0), |       adpcmAMemLen(0), | ||||||
|       adpcmBMem(NULL), |       adpcmBMem(NULL), | ||||||
|       adpcmBMemLen(0), |       adpcmBMemLen(0), | ||||||
|       qsoundMem(NULL), |       qsoundMem(NULL), | ||||||
|       qsoundMemLen(0) {} |       qsoundMemLen(0), | ||||||
|  |       qsoundAMem(NULL), | ||||||
|  |       qsoundAMemLen(0), | ||||||
|  |       dpcmMem(NULL), | ||||||
|  |       dpcmMemLen(0) {} | ||||||
| }; | }; | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  | @ -845,7 +845,12 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { | ||||||
|     if (tchans>DIV_MAX_CHANS) tchans=DIV_MAX_CHANS; |     if (tchans>DIV_MAX_CHANS) tchans=DIV_MAX_CHANS; | ||||||
| 
 | 
 | ||||||
|     // system volume
 |     // system volume
 | ||||||
|     for (int i=0; i<32; i++) ds.systemVol[i]=reader.readC(); |     for (int i=0; i<32; i++) { | ||||||
|  |       ds.systemVol[i]=reader.readC(); | ||||||
|  |       if (ds.version<59 && ds.system[i]==DIV_SYSTEM_NES) { | ||||||
|  |         ds.systemVol[i]/=4; | ||||||
|  |       } | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     // system panning
 |     // system panning
 | ||||||
|     for (int i=0; i<32; i++) ds.systemPan[i]=reader.readC(); |     for (int i=0; i<32; i++) ds.systemPan[i]=reader.readC(); | ||||||
|  | @ -1003,6 +1008,12 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { | ||||||
|       ds.notes=reader.readString(); |       ds.notes=reader.readString(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     if (ds.version>=59) { | ||||||
|  |       ds.masterVol=reader.readF(); | ||||||
|  |     } else { | ||||||
|  |       ds.masterVol=2.0f; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     // read instruments
 |     // read instruments
 | ||||||
|     for (int i=0; i<ds.insLen; i++) { |     for (int i=0; i<ds.insLen; i++) { | ||||||
|       DivInstrument* ins=new DivInstrument; |       DivInstrument* ins=new DivInstrument; | ||||||
|  | @ -1448,6 +1459,8 @@ SafeWriter* DivEngine::saveFur() { | ||||||
| 
 | 
 | ||||||
|   w->writeString(song.notes,false); |   w->writeString(song.notes,false); | ||||||
| 
 | 
 | ||||||
|  |   w->writeF(song.masterVol); | ||||||
|  | 
 | ||||||
|   /// INSTRUMENT
 |   /// INSTRUMENT
 | ||||||
|   for (int i=0; i<song.insLen; i++) { |   for (int i=0; i<song.insLen; i++) { | ||||||
|     DivInstrument* ins=song.ins[i]; |     DivInstrument* ins=song.ins[i]; | ||||||
|  |  | ||||||
|  | @ -30,6 +30,7 @@ | ||||||
| #define WRITE_CONTROL(ch,v) rWrite(0x25+(ch<<3),(v)) | #define WRITE_CONTROL(ch,v) rWrite(0x25+(ch<<3),(v)) | ||||||
| #define WRITE_OTHER(ch,v) rWrite(0x27+(ch<<3),(v)) | #define WRITE_OTHER(ch,v) rWrite(0x27+(ch<<3),(v)) | ||||||
| #define WRITE_ATTEN(ch,v) rWrite((0x40+ch),(v)) | #define WRITE_ATTEN(ch,v) rWrite((0x40+ch),(v)) | ||||||
|  | #define WRITE_STEREO(v) rWrite(0x50,(v)) | ||||||
| 
 | 
 | ||||||
| #define CHIP_DIVIDER 64 | #define CHIP_DIVIDER 64 | ||||||
| 
 | 
 | ||||||
|  | @ -197,7 +198,8 @@ int DivPlatformLynx::dispatch(DivCommand c) { | ||||||
|       } |       } | ||||||
|       break; |       break; | ||||||
|     case DIV_CMD_PANNING: |     case DIV_CMD_PANNING: | ||||||
|       WRITE_ATTEN(c.chan, c.value); |       chan[c.chan].pan=((c.value&0x0f)<<4)|((c.value&0xf0)>>4); | ||||||
|  |       WRITE_ATTEN(c.chan,chan[c.chan].pan); | ||||||
|       break; |       break; | ||||||
|     case DIV_CMD_GET_VOLUME: |     case DIV_CMD_GET_VOLUME: | ||||||
|       if (chan[c.chan].std.hasVol) { |       if (chan[c.chan].std.hasVol) { | ||||||
|  | @ -261,6 +263,10 @@ void DivPlatformLynx::muteChannel(int ch, bool mute) { | ||||||
|   if (chan[ch].active) WRITE_VOLUME(ch,(isMuted[ch]?0:(chan[ch].outVol&127))); |   if (chan[ch].active) WRITE_VOLUME(ch,(isMuted[ch]?0:(chan[ch].outVol&127))); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | bool DivPlatformLynx::isStereo() { | ||||||
|  |   return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void DivPlatformLynx::forceIns() { | void DivPlatformLynx::forceIns() { | ||||||
|   for (int i=0; i<4; i++) { |   for (int i=0; i<4; i++) { | ||||||
|     if (chan[i].active) { |     if (chan[i].active) { | ||||||
|  | @ -294,6 +300,7 @@ void DivPlatformLynx::reset() { | ||||||
|   if (dumpWrites) { |   if (dumpWrites) { | ||||||
|     addWrite(0xffffffff,0); |     addWrite(0xffffffff,0); | ||||||
|   } |   } | ||||||
|  |   WRITE_STEREO(0); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool DivPlatformLynx::keyOffAffectsArp(int ch) { | bool DivPlatformLynx::keyOffAffectsArp(int ch) { | ||||||
|  |  | ||||||
|  | @ -45,7 +45,7 @@ class DivPlatformLynx: public DivDispatch { | ||||||
|     MikeyFreqDiv fd; |     MikeyFreqDiv fd; | ||||||
|     MikeyDuty duty; |     MikeyDuty duty; | ||||||
|     int baseFreq, pitch, note, actualNote, lfsr; |     int baseFreq, pitch, note, actualNote, lfsr; | ||||||
|     unsigned char ins; |     unsigned char ins, pan; | ||||||
|     bool active, insChanged, freqChanged, keyOn, keyOff, inPorta; |     bool active, insChanged, freqChanged, keyOn, keyOff, inPorta; | ||||||
|     signed char vol, outVol; |     signed char vol, outVol; | ||||||
|     Channel(): |     Channel(): | ||||||
|  | @ -58,6 +58,7 @@ class DivPlatformLynx: public DivDispatch { | ||||||
|       actualNote(0), |       actualNote(0), | ||||||
|       lfsr(-1), |       lfsr(-1), | ||||||
|       ins(-1), |       ins(-1), | ||||||
|  |       pan(0xff), | ||||||
|       active(false), |       active(false), | ||||||
|       insChanged(true), |       insChanged(true), | ||||||
|       freqChanged(false), |       freqChanged(false), | ||||||
|  | @ -81,6 +82,7 @@ class DivPlatformLynx: public DivDispatch { | ||||||
|     void forceIns(); |     void forceIns(); | ||||||
|     void tick(); |     void tick(); | ||||||
|     void muteChannel(int ch, bool mute); |     void muteChannel(int ch, bool mute); | ||||||
|  |     bool isStereo(); | ||||||
|     bool keyOffAffectsArp(int ch); |     bool keyOffAffectsArp(int ch); | ||||||
|     bool keyOffAffectsPorta(int ch); |     bool keyOffAffectsPorta(int ch); | ||||||
|     //int getPortaFloor(int ch);
 |     //int getPortaFloor(int ch);
 | ||||||
|  |  | ||||||
|  | @ -99,7 +99,10 @@ void DivPlatformNES::acquire(short* bufL, short* bufR, size_t start, size_t len) | ||||||
|     if (nes->apu.clocked) { |     if (nes->apu.clocked) { | ||||||
|       nes->apu.clocked=false; |       nes->apu.clocked=false; | ||||||
|     } |     } | ||||||
|     bufL[i]=(pulse_output(nes)+tnd_output(nes))*30; |     int sample=(pulse_output(nes)+tnd_output(nes)-128)<<7; | ||||||
|  |     if (sample>32767) sample=32767; | ||||||
|  |     if (sample<-32768) sample=-32768; | ||||||
|  |     bufL[i]=sample; | ||||||
|   } |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -301,7 +301,9 @@ void DivPlatformSMS::poke(std::vector<DivRegWrite>& wlist) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void DivPlatformSMS::setFlags(unsigned int flags) { | void DivPlatformSMS::setFlags(unsigned int flags) { | ||||||
|   if ((flags&3)==2) { |   if ((flags&3)==3) { | ||||||
|  |     chipClock=COLOR_NTSC/2.0; | ||||||
|  |   } else if ((flags&3)==2) { | ||||||
|     chipClock=4000000; |     chipClock=4000000; | ||||||
|   } else if ((flags&3)==1) { |   } else if ((flags&3)==1) { | ||||||
|     chipClock=COLOR_PAL*4.0/5.0; |     chipClock=COLOR_PAL*4.0/5.0; | ||||||
|  |  | ||||||
|  | @ -22,11 +22,19 @@ | ||||||
| #include "../engine.h" | #include "../engine.h" | ||||||
| 
 | 
 | ||||||
| uint8_t DivYM2610Interface::ymfm_external_read(ymfm::access_class type, uint32_t address) { | uint8_t DivYM2610Interface::ymfm_external_read(ymfm::access_class type, uint32_t address) { | ||||||
|   //printf("wants to read from %x\n",address);
 |   switch (type) { | ||||||
|   if (type!=ymfm::ACCESS_ADPCM_A) return /*s->dataB[address&0xffffff];*/0; |     case ymfm::ACCESS_ADPCM_A: | ||||||
|   return parent->adpcmMem[address&0xffffff];/*s->dataA[address&0xffffff]*/ |       if (parent->adpcmAMem==NULL) return 0; | ||||||
|   /*if (12*sampleBank+(address>>16)>=parent->song.sampleLen) return 0;
 |       if ((address&0xffffff)>=parent->adpcmAMemLen) return 0; | ||||||
|   return parent->song.sample[12*sampleBank+(address>>16)]->adpcmRendData[(address&0xffff)];*/ |       return parent->adpcmAMem[address&0xffffff]; | ||||||
|  |     case ymfm::ACCESS_ADPCM_B: | ||||||
|  |       if (parent->adpcmBMem==NULL) return 0; | ||||||
|  |       if ((address&0xffffff)>=parent->adpcmBMemLen) return 0; | ||||||
|  |       return parent->adpcmBMem[address&0xffffff]; | ||||||
|  |     default: | ||||||
|  |       return 0; | ||||||
|  |   } | ||||||
|  |   return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void DivYM2610Interface::ymfm_external_write(ymfm::access_class type, uint32_t address, uint8_t data) { | void DivYM2610Interface::ymfm_external_write(ymfm::access_class type, uint32_t address, uint8_t data) { | ||||||
|  |  | ||||||
|  | @ -237,7 +237,7 @@ void DivPlatformYM2610B::tick() { | ||||||
|    |    | ||||||
|   // FM
 |   // FM
 | ||||||
|   for (int i=0; i<6; i++) { |   for (int i=0; i<6; i++) { | ||||||
|     if (i==1 && extMode) continue; |     if (i==2 && extMode) continue; | ||||||
|     chan[i].std.next(); |     chan[i].std.next(); | ||||||
| 
 | 
 | ||||||
|     if (chan[i].std.hadVol) { |     if (chan[i].std.hadVol) { | ||||||
|  | @ -366,7 +366,7 @@ void DivPlatformYM2610B::tick() { | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   for (int i=0; i<6; i++) { |   for (int i=0; i<6; i++) { | ||||||
|     if (i==1 && extMode) continue; |     if (i==2 && extMode) continue; | ||||||
|     if (chan[i].freqChanged) { |     if (chan[i].freqChanged) { | ||||||
|       chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,octave(chan[i].baseFreq)); |       chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,octave(chan[i].baseFreq)); | ||||||
|       if (chan[i].freq>262143) chan[i].freq=262143; |       if (chan[i].freq>262143) chan[i].freq=262143; | ||||||
|  |  | ||||||
|  | @ -1314,17 +1314,17 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   for (int i=0; i<song.systemLen; i++) { |   for (int i=0; i<song.systemLen; i++) { | ||||||
|     float volL=((float)song.systemVol[i]/64.0f)*((float)MIN(127,127-(int)song.systemPan[i])/127.0f); |     float volL=((float)song.systemVol[i]/64.0f)*((float)MIN(127,127-(int)song.systemPan[i])/127.0f)*song.masterVol; | ||||||
|     float volR=((float)song.systemVol[i]/64.0f)*((float)MIN(127,127+(int)song.systemPan[i])/127.0f); |     float volR=((float)song.systemVol[i]/64.0f)*((float)MIN(127,127+(int)song.systemPan[i])/127.0f)*song.masterVol; | ||||||
|     if (disCont[i].dispatch->isStereo()) { |     if (disCont[i].dispatch->isStereo()) { | ||||||
|       for (size_t j=0; j<size; j++) { |       for (size_t j=0; j<size; j++) { | ||||||
|         out[0][j]+=((float)disCont[i].bbOut[0][j]/16384.0)*volL; |         out[0][j]+=((float)disCont[i].bbOut[0][j]/32768.0)*volL; | ||||||
|         out[1][j]+=((float)disCont[i].bbOut[1][j]/16384.0)*volR; |         out[1][j]+=((float)disCont[i].bbOut[1][j]/32768.0)*volR; | ||||||
|       } |       } | ||||||
|     } else { |     } else { | ||||||
|       for (size_t j=0; j<size; j++) { |       for (size_t j=0; j<size; j++) { | ||||||
|         out[0][j]+=((float)disCont[i].bbOut[0][j]/16384.0)*volL; |         out[0][j]+=((float)disCont[i].bbOut[0][j]/32768.0)*volL; | ||||||
|         out[1][j]+=((float)disCont[i].bbOut[0][j]/16384.0)*volR; |         out[1][j]+=((float)disCont[i].bbOut[0][j]/32768.0)*volR; | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  | @ -73,6 +73,7 @@ bool DivSample::save(const char* path) { | ||||||
|   return true; |   return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // 16-bit memory is padded to 512, to make things easier for ADPCM-A/B.
 | ||||||
| bool DivSample::initInternal(unsigned char d, int count) { | bool DivSample::initInternal(unsigned char d, int count) { | ||||||
|   switch (d) { |   switch (d) { | ||||||
|     case 0: // 1-bit
 |     case 0: // 1-bit
 | ||||||
|  | @ -96,14 +97,14 @@ bool DivSample::initInternal(unsigned char d, int count) { | ||||||
|     case 5: // ADPCM-A
 |     case 5: // ADPCM-A
 | ||||||
|       if (dataA!=NULL) delete[] dataA; |       if (dataA!=NULL) delete[] dataA; | ||||||
|       lengthA=(count+1)/2; |       lengthA=(count+1)/2; | ||||||
|       dataA=new unsigned char[lengthA]; |       dataA=new unsigned char[(lengthA+255)&(~0xff)]; | ||||||
|       memset(dataA,0,lengthA); |       memset(dataA,0,(lengthA+255)&(~0xff)); | ||||||
|       break; |       break; | ||||||
|     case 6: // ADPCM-B
 |     case 6: // ADPCM-B
 | ||||||
|       if (dataB!=NULL) delete[] dataB; |       if (dataB!=NULL) delete[] dataB; | ||||||
|       lengthB=(count+1)/2; |       lengthB=(count+1)/2; | ||||||
|       dataB=new unsigned char[lengthB]; |       dataB=new unsigned char[(lengthB+255)&(~0xff)]; | ||||||
|       memset(dataB,0,lengthB); |       memset(dataB,0,(lengthB+255)&(~0xff)); | ||||||
|       break; |       break; | ||||||
|     case 7: // X68000 ADPCM
 |     case 7: // X68000 ADPCM
 | ||||||
|       if (dataX68!=NULL) delete[] dataX68; |       if (dataX68!=NULL) delete[] dataX68; | ||||||
|  | @ -132,8 +133,8 @@ bool DivSample::initInternal(unsigned char d, int count) { | ||||||
|     case 16: // 16-bit
 |     case 16: // 16-bit
 | ||||||
|       if (data16!=NULL) delete[] data16; |       if (data16!=NULL) delete[] data16; | ||||||
|       length16=count*2; |       length16=count*2; | ||||||
|       data16=new short[count]; |       data16=new short[(count+511)&(~0x1ff)]; | ||||||
|       memset(data16,0,count*sizeof(short)); |       memset(data16,0,((count+511)&(~0x1ff))*sizeof(short)); | ||||||
|       break; |       break; | ||||||
|     default: |     default: | ||||||
|       return false; |       return false; | ||||||
|  | @ -223,13 +224,14 @@ void DivSample::render() { | ||||||
|     if (!initInternal(4,samples)) return; |     if (!initInternal(4,samples)) return; | ||||||
|     bs_encode(data16,dataQSoundA,samples); |     bs_encode(data16,dataQSoundA,samples); | ||||||
|   } |   } | ||||||
|  |   // TODO: pad to 256.
 | ||||||
|   if (depth!=5) { // ADPCM-A
 |   if (depth!=5) { // ADPCM-A
 | ||||||
|     if (!initInternal(5,samples)) return; |     if (!initInternal(5,samples)) return; | ||||||
|     yma_encode(data16,dataA,samples); |     yma_encode(data16,dataA,(samples+511)&(~0x1ff)); | ||||||
|   } |   } | ||||||
|   if (depth!=6) { // ADPCM-B
 |   if (depth!=6) { // ADPCM-B
 | ||||||
|     if (!initInternal(6,samples)) return; |     if (!initInternal(6,samples)) return; | ||||||
|     ymb_encode(data16,dataB,samples); |     ymb_encode(data16,dataB,(samples+511)&(~0x1ff)); | ||||||
|   } |   } | ||||||
|   if (depth!=7) { // X68000 ADPCM
 |   if (depth!=7) { // X68000 ADPCM
 | ||||||
|     if (!initInternal(7,samples)) return; |     if (!initInternal(7,samples)) return; | ||||||
|  |  | ||||||
|  | @ -242,6 +242,7 @@ struct DivSong { | ||||||
|   bool pal; |   bool pal; | ||||||
|   bool customTempo; |   bool customTempo; | ||||||
|   int hz, patLen, ordersLen, insLen, waveLen, sampleLen; |   int hz, patLen, ordersLen, insLen, waveLen, sampleLen; | ||||||
|  |   float masterVol; | ||||||
|   float tuning; |   float tuning; | ||||||
| 
 | 
 | ||||||
|   // compatibility flags
 |   // compatibility flags
 | ||||||
|  | @ -309,6 +310,7 @@ struct DivSong { | ||||||
|     insLen(0), |     insLen(0), | ||||||
|     waveLen(0), |     waveLen(0), | ||||||
|     sampleLen(0), |     sampleLen(0), | ||||||
|  |     masterVol(1.0f), | ||||||
|     tuning(440.0f), |     tuning(440.0f), | ||||||
|     limitSlides(false), |     limitSlides(false), | ||||||
|     linearPitch(true), |     linearPitch(true), | ||||||
|  |  | ||||||
|  | @ -938,14 +938,24 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop) { | ||||||
|     delete[] pcmMem; |     delete[] pcmMem; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   if (writeADPCM && adpcmMemLen>0) { |   if (writeADPCM && adpcmAMemLen>0) { | ||||||
|     w->writeC(0x67); |     w->writeC(0x67); | ||||||
|     w->writeC(0x66); |     w->writeC(0x66); | ||||||
|     w->writeC(0x82); |     w->writeC(0x82); | ||||||
|     w->writeI(adpcmMemLen+8); |     w->writeI(adpcmAMemLen+8); | ||||||
|     w->writeI(adpcmMemLen); |     w->writeI(adpcmAMemLen); | ||||||
|     w->writeI(0); |     w->writeI(0); | ||||||
|     w->write(adpcmMem,adpcmMemLen); |     w->write(adpcmAMem,adpcmAMemLen); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   if (writeADPCM && adpcmBMemLen>0) { | ||||||
|  |     w->writeC(0x67); | ||||||
|  |     w->writeC(0x66); | ||||||
|  |     w->writeC(0x83); | ||||||
|  |     w->writeI(adpcmBMemLen+8); | ||||||
|  |     w->writeI(adpcmBMemLen); | ||||||
|  |     w->writeI(0); | ||||||
|  |     w->write(adpcmBMem,adpcmBMemLen); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   if (writeQSound && qsoundMemLen>0) { |   if (writeQSound && qsoundMemLen>0) { | ||||||
|  |  | ||||||
|  | @ -1345,6 +1345,10 @@ void FurnaceGUI::drawMixer() { | ||||||
|   ImGui::SetNextWindowSizeConstraints(ImVec2(400.0f*dpiScale,200.0f*dpiScale),ImVec2(scrW*dpiScale,scrH*dpiScale)); |   ImGui::SetNextWindowSizeConstraints(ImVec2(400.0f*dpiScale,200.0f*dpiScale),ImVec2(scrW*dpiScale,scrH*dpiScale)); | ||||||
|   if (ImGui::Begin("Mixer",&mixerOpen,settings.allowEditDocking?0:ImGuiWindowFlags_NoDocking)) { |   if (ImGui::Begin("Mixer",&mixerOpen,settings.allowEditDocking?0:ImGuiWindowFlags_NoDocking)) { | ||||||
|     char id[32]; |     char id[32]; | ||||||
|  |     if (ImGui::SliderFloat("Master Volume",&e->song.masterVol,0,3,"%.2fx")) { | ||||||
|  |       if (e->song.masterVol<0) e->song.masterVol=0; | ||||||
|  |       if (e->song.masterVol>3) e->song.masterVol=3; | ||||||
|  |     } | ||||||
|     for (int i=0; i<e->song.systemLen; i++) { |     for (int i=0; i<e->song.systemLen; i++) { | ||||||
|       snprintf(id,31,"MixS%d",i); |       snprintf(id,31,"MixS%d",i); | ||||||
|       bool doInvert=e->song.systemVol[i]&128; |       bool doInvert=e->song.systemVol[i]&128; | ||||||
|  | @ -1898,12 +1902,12 @@ void FurnaceGUI::drawStats() { | ||||||
|   } |   } | ||||||
|   if (!statsOpen) return; |   if (!statsOpen) return; | ||||||
|   if (ImGui::Begin("Statistics",&statsOpen)) { |   if (ImGui::Begin("Statistics",&statsOpen)) { | ||||||
|     String adpcmUsage=fmt::sprintf("%d/16384KB",e->adpcmMemLen/1024); |     String adpcmAUsage=fmt::sprintf("%d/16384KB",e->adpcmAMemLen/1024); | ||||||
|     String adpcmBUsage=fmt::sprintf("%d/16384KB",e->adpcmBMemLen/1024); |     String adpcmBUsage=fmt::sprintf("%d/16384KB",e->adpcmBMemLen/1024); | ||||||
|     String qsoundUsage=fmt::sprintf("%d/16384KB",e->qsoundMemLen/1024); |     String qsoundUsage=fmt::sprintf("%d/16384KB",e->qsoundMemLen/1024); | ||||||
|     ImGui::Text("ADPCM-A"); |     ImGui::Text("ADPCM-A"); | ||||||
|     ImGui::SameLine(); |     ImGui::SameLine(); | ||||||
|     ImGui::ProgressBar(((float)e->adpcmMemLen)/16777216.0f,ImVec2(-FLT_MIN,0),adpcmUsage.c_str()); |     ImGui::ProgressBar(((float)e->adpcmAMemLen)/16777216.0f,ImVec2(-FLT_MIN,0),adpcmAUsage.c_str()); | ||||||
|     ImGui::Text("ADPCM-B"); |     ImGui::Text("ADPCM-B"); | ||||||
|     ImGui::SameLine(); |     ImGui::SameLine(); | ||||||
|     ImGui::ProgressBar(((float)e->adpcmBMemLen)/16777216.0f,ImVec2(-FLT_MIN,0),adpcmBUsage.c_str()); |     ImGui::ProgressBar(((float)e->adpcmBMemLen)/16777216.0f,ImVec2(-FLT_MIN,0),adpcmBUsage.c_str()); | ||||||
|  | @ -4542,6 +4546,10 @@ bool FurnaceGUI::loop() { | ||||||
|                   e->setSysFlags(i,(flags&(~3))|2,restart); |                   e->setSysFlags(i,(flags&(~3))|2,restart); | ||||||
|                   updateWindowTitle(); |                   updateWindowTitle(); | ||||||
|                 } |                 } | ||||||
|  |                 if (ImGui::RadioButton("Half NTSC (1.79MHz)",(flags&3)==3)) { | ||||||
|  |                   e->setSysFlags(i,(flags&(~3))|3,restart); | ||||||
|  |                   updateWindowTitle(); | ||||||
|  |                 } | ||||||
|                 ImGui::Text("Chip type:"); |                 ImGui::Text("Chip type:"); | ||||||
|                 if (ImGui::RadioButton("Sega VDP/Master System",((flags>>2)&3)==0)) { |                 if (ImGui::RadioButton("Sega VDP/Master System",((flags>>2)&3)==0)) { | ||||||
|                   e->setSysFlags(i,(flags&(~12))|0,restart); |                   e->setSysFlags(i,(flags&(~12))|0,restart); | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 cam900
						cam900