furnace/src/engine/dispatchContainer.cpp
cam900 ef104ce0b0 Add VRC6 support
2 Pulse channels:
- 8 level pulse duty cycle, DAC mode(just ignores duty cycle)/pulse wave mode, 4 bit volume.
- Furnace support PCM playback in pulse channels with duty cycle ignore mode.

Sawtooth:
- nothing but 6 bit volume (8 bit accumulator in technically) and 12 bit frequency (periodic).

VRC6 instrument:
- 6 bit Volume macro for finer sawtooth volume handling, also 3 bit Duty cycle macro for pulse channels.

Duty, PCM mode command and Duty macro affects for pulse channel only.
2022-03-28 01:06:56 +09:00

311 lines
8.7 KiB
C++

/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "blip_buf.h"
#include "engine.h"
#include "platform/genesis.h"
#include "platform/genesisext.h"
#include "platform/sms.h"
#include "platform/opll.h"
#include "platform/gb.h"
#include "platform/pce.h"
#include "platform/nes.h"
#include "platform/c64.h"
#include "platform/arcade.h"
#include "platform/ym2610.h"
#include "platform/ym2610ext.h"
#include "platform/ym2610b.h"
#include "platform/ym2610bext.h"
#include "platform/ay.h"
#include "platform/ay8930.h"
#include "platform/opl.h"
#include "platform/tia.h"
#include "platform/saa.h"
#include "platform/amiga.h"
#include "platform/pcspkr.h"
#include "platform/segapcm.h"
#include "platform/qsound.h"
#include "platform/vera.h"
#include "platform/x1_010.h"
#include "platform/swan.h"
#include "platform/lynx.h"
#include "platform/bubsyswsg.h"
#include "platform/pet.h"
#include "platform/vic20.h"
#include "platform/vrc6.h"
#include "platform/dummy.h"
#include "../ta-log.h"
#include "song.h"
void DivDispatchContainer::setRates(double gotRate) {
blip_set_rates(bb[0],dispatch->rate,gotRate);
blip_set_rates(bb[1],dispatch->rate,gotRate);
}
void DivDispatchContainer::setQuality(bool lowQual) {
lowQuality=lowQual;
}
void DivDispatchContainer::acquire(size_t offset, size_t count) {
dispatch->acquire(bbIn[0],bbIn[1],offset,count);
}
void DivDispatchContainer::flush(size_t count) {
blip_read_samples(bb[0],bbOut[0],count,0);
if (dispatch->isStereo()) {
blip_read_samples(bb[1],bbOut[1],count,0);
}
}
void DivDispatchContainer::fillBuf(size_t runtotal, size_t offset, size_t size) {
if (lowQuality) {
for (size_t i=0; i<runtotal; i++) {
temp[0]=bbIn[0][i];
blip_add_delta_fast(bb[0],i,temp[0]-prevSample[0]);
prevSample[0]=temp[0];
}
if (dispatch->isStereo()) for (size_t i=0; i<runtotal; i++) {
temp[1]=bbIn[1][i];
blip_add_delta_fast(bb[1],i,temp[1]-prevSample[1]);
prevSample[1]=temp[1];
}
} else {
for (size_t i=0; i<runtotal; i++) {
temp[0]=bbIn[0][i];
blip_add_delta(bb[0],i,temp[0]-prevSample[0]);
prevSample[0]=temp[0];
}
if (dispatch->isStereo()) for (size_t i=0; i<runtotal; i++) {
temp[1]=bbIn[1][i];
blip_add_delta(bb[1],i,temp[1]-prevSample[1]);
prevSample[1]=temp[1];
}
}
blip_end_frame(bb[0],runtotal);
blip_read_samples(bb[0],bbOut[0]+offset,size,0);
/*if (totalRead<(int)size && totalRead>0) {
for (size_t i=totalRead; i<size; i++) {
bbOut[0][i]=bbOut[0][totalRead-1];//bbOut[0][totalRead];
}
}*/
if (dispatch->isStereo()) {
blip_end_frame(bb[1],runtotal);
blip_read_samples(bb[1],bbOut[1]+offset,size,0);
}
}
void DivDispatchContainer::clear() {
blip_clear(bb[0]);
blip_clear(bb[1]);
temp[0]=0;
temp[1]=0;
prevSample[0]=0;
prevSample[1]=0;
// run for one cycle to determine DC offset
// TODO: SAA1099 doesn't like that
/*dispatch->acquire(bbIn[0],bbIn[1],0,1);
temp[0]=bbIn[0][0];
temp[1]=bbIn[1][0];
prevSample[0]=temp[0];
prevSample[1]=temp[1];*/
}
void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, double gotRate, unsigned int flags) {
if (dispatch!=NULL) return;
bb[0]=blip_new(32768);
if (bb[0]==NULL) {
logE("not enough memory!\n");
return;
}
bb[1]=blip_new(32768);
if (bb[1]==NULL) {
logE("not enough memory!\n");
return;
}
bbOut[0]=new short[32768];
bbOut[1]=new short[32768];
bbIn[0]=new short[32768];
bbIn[1]=new short[32768];
bbInLen=32768;
switch (sys) {
case DIV_SYSTEM_YMU759:
dispatch=new DivPlatformOPL;
((DivPlatformOPL*)dispatch)->setOPLType(759,false);
break;
case DIV_SYSTEM_YM2612:
dispatch=new DivPlatformGenesis;
((DivPlatformGenesis*)dispatch)->setYMFM(eng->getConfInt("ym2612Core",0));
break;
case DIV_SYSTEM_YM2612_EXT:
dispatch=new DivPlatformGenesisExt;
((DivPlatformGenesisExt*)dispatch)->setYMFM(eng->getConfInt("ym2612Core",0));
break;
case DIV_SYSTEM_SMS:
dispatch=new DivPlatformSMS;
break;
case DIV_SYSTEM_GB:
dispatch=new DivPlatformGB;
break;
case DIV_SYSTEM_PCE:
dispatch=new DivPlatformPCE;
break;
case DIV_SYSTEM_NES:
dispatch=new DivPlatformNES;
break;
case DIV_SYSTEM_C64_6581:
dispatch=new DivPlatformC64;
((DivPlatformC64*)dispatch)->setChipModel(true);
break;
case DIV_SYSTEM_C64_8580:
dispatch=new DivPlatformC64;
((DivPlatformC64*)dispatch)->setChipModel(false);
break;
case DIV_SYSTEM_YM2151:
dispatch=new DivPlatformArcade;
((DivPlatformArcade*)dispatch)->setYMFM(eng->getConfInt("arcadeCore",0)==0);
break;
case DIV_SYSTEM_YM2610:
case DIV_SYSTEM_YM2610_FULL:
dispatch=new DivPlatformYM2610;
break;
case DIV_SYSTEM_YM2610_EXT:
case DIV_SYSTEM_YM2610_FULL_EXT:
dispatch=new DivPlatformYM2610Ext;
break;
case DIV_SYSTEM_YM2610B:
dispatch=new DivPlatformYM2610B;
break;
case DIV_SYSTEM_YM2610B_EXT:
dispatch=new DivPlatformYM2610BExt;
break;
case DIV_SYSTEM_AMIGA:
dispatch=new DivPlatformAmiga;
break;
case DIV_SYSTEM_AY8910:
dispatch=new DivPlatformAY8910;
break;
case DIV_SYSTEM_AY8930:
dispatch=new DivPlatformAY8930;
break;
case DIV_SYSTEM_TIA:
dispatch=new DivPlatformTIA;
break;
case DIV_SYSTEM_OPLL:
case DIV_SYSTEM_OPLL_DRUMS:
case DIV_SYSTEM_VRC7:
dispatch=new DivPlatformOPLL;
((DivPlatformOPLL*)dispatch)->setVRC7(sys==DIV_SYSTEM_VRC7);
((DivPlatformOPLL*)dispatch)->setProperDrums(sys==DIV_SYSTEM_OPLL_DRUMS);
break;
case DIV_SYSTEM_OPL:
dispatch=new DivPlatformOPL;
((DivPlatformOPL*)dispatch)->setOPLType(1,false);
break;
case DIV_SYSTEM_OPL_DRUMS:
dispatch=new DivPlatformOPL;
((DivPlatformOPL*)dispatch)->setOPLType(1,true);
break;
case DIV_SYSTEM_OPL2:
dispatch=new DivPlatformOPL;
((DivPlatformOPL*)dispatch)->setOPLType(2,false);
break;
case DIV_SYSTEM_OPL2_DRUMS:
dispatch=new DivPlatformOPL;
((DivPlatformOPL*)dispatch)->setOPLType(2,true);
break;
case DIV_SYSTEM_OPL3:
dispatch=new DivPlatformOPL;
((DivPlatformOPL*)dispatch)->setOPLType(3,false);
break;
case DIV_SYSTEM_OPL3_DRUMS:
dispatch=new DivPlatformOPL;
((DivPlatformOPL*)dispatch)->setOPLType(3,true);
break;
case DIV_SYSTEM_SAA1099: {
int saaCore=eng->getConfInt("saaCore",1);
if (saaCore<0 || saaCore>2) saaCore=0;
dispatch=new DivPlatformSAA1099;
((DivPlatformSAA1099*)dispatch)->setCore((DivSAACores)saaCore);
break;
}
case DIV_SYSTEM_PCSPKR:
dispatch=new DivPlatformPCSpeaker;
break;
case DIV_SYSTEM_LYNX:
dispatch=new DivPlatformLynx;
break;
case DIV_SYSTEM_QSOUND:
dispatch=new DivPlatformQSound;
break;
case DIV_SYSTEM_SEGAPCM:
case DIV_SYSTEM_SEGAPCM_COMPAT:
dispatch=new DivPlatformSegaPCM;
break;
case DIV_SYSTEM_X1_010:
dispatch=new DivPlatformX1_010;
break;
case DIV_SYSTEM_SWAN:
dispatch=new DivPlatformSwan;
break;
case DIV_SYSTEM_VERA:
dispatch=new DivPlatformVERA;
break;
case DIV_SYSTEM_BUBSYS_WSG:
dispatch=new DivPlatformBubSysWSG;
break;
case DIV_SYSTEM_PET:
dispatch=new DivPlatformPET;
break;
case DIV_SYSTEM_VIC20:
dispatch=new DivPlatformVIC20;
break;
case DIV_SYSTEM_VRC6:
dispatch=new DivPlatformVRC6;
break;
default:
logW("this system is not supported yet! using dummy platform.\n");
dispatch=new DivPlatformDummy;
break;
}
dispatch->init(eng,chanCount,gotRate,flags);
}
void DivDispatchContainer::quit() {
if (dispatch==NULL) return;
dispatch->quit();
delete dispatch;
dispatch=NULL;
delete[] bbOut[0];
delete[] bbOut[1];
delete[] bbIn[0];
delete[] bbIn[1];
bbInLen=0;
blip_delete(bb[0]);
blip_delete(bb[1]);
}