From ef11c2bf97f096e5b4e821b38383af82b1b5cd32 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 10 Jan 2026 03:44:58 -0500 Subject: [PATCH] implement tremolo in command stream player --- src/engine/cmdStream.cpp | 30 ++++++++++++++++++++++++------ src/engine/cmdStream.h | 11 +++++++++++ 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/src/engine/cmdStream.cpp b/src/engine/cmdStream.cpp index 9060d85a6..e3bdb2e74 100644 --- a/src/engine/cmdStream.cpp +++ b/src/engine/cmdStream.cpp @@ -172,6 +172,7 @@ bool DivCSPlayer::tick() { case 0xc8: // vol slide chan[i].volSpeed=(short)(bigEndian?stream.readS_BE():stream.readS()); chan[i].volSpeedTarget=-1; + chan[i].tremoloDepth=0; break; case 0xc9: // porta chan[i].portaTarget=(int)((unsigned char)stream.readC())-60; @@ -195,11 +196,18 @@ bool DivCSPlayer::tick() { chan[i].volSpeedTarget=arg0==0 ? -1 : arg1; break; } - case 0xcc: // tremolo (TODO) - stream.readC(); + case 0xcc: // tremolo + unsigned char param=stream.readC(); + chan[i].tremoloDepth=param&15; + chan[i].tremoloRate=param>>4; + chan[i].volSpeed=0; + chan[i].volSpeedTarget=-1; + sendVolume=true; break; - case 0xcd: // panbrello (TODO) - stream.readC(); + case 0xcd: // panbrello + unsigned char param=stream.readC(); + chan[i].panbrelloDepth=param&15; + chan[i].panbrelloRate=param>>4; break; case 0xce: // pan slide (TODO) stream.readC(); @@ -554,7 +562,7 @@ bool DivCSPlayer::tick() { if (mustTell) chan[i].readPos=stream.tell(); } - if (sendVolume || chan[i].volSpeed!=0) { + if (sendVolume || chan[i].volSpeed!=0 || chan[i].tremoloDepth!=0) { int preSpeedVol=chan[i].volume; chan[i].volume+=chan[i].volSpeed; if (chan[i].volSpeedTarget!=-1) { @@ -578,13 +586,20 @@ bool DivCSPlayer::tick() { chan[i].volSpeedTarget=-1; } } + if (chan[i].volume<0) { chan[i].volume=0; } if (chan[i].volume>chan[i].volMax) { chan[i].volume=chan[i].volMax; } - e->dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8)); + if (chan[i].tremoloDepth>0) { + chan[i].tremoloPos+=chan[i].tremoloRate; + chan[i].tremoloPos&=127; + dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,MAX(0,chan[i].volume-(tremTable[chan[i].tremoloPos]*chan[i].tremoloDepth))>>8)); + } else { + e->dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8)); + } } if (sendPitch || chan[i].vibratoDepth!=0) { @@ -716,6 +731,9 @@ bool DivCSPlayer::init() { for (int i=0; i<64; i++) { vibTable[i]=127*sin(((double)i/64.0)*(2*M_PI)); } + for (int i=0; i<128; i++) { + tremTable[i]=255*0.5*(1.0-cos(((double)i/128.0)*(2*M_PI))); + } arpSpeed=1; bAccessTS=new unsigned short[bLen]; diff --git a/src/engine/cmdStream.h b/src/engine/cmdStream.h index b38be0b04..24368fd6a 100644 --- a/src/engine/cmdStream.h +++ b/src/engine/cmdStream.h @@ -37,6 +37,8 @@ struct DivCSChannelState { int note, pitch; int volume, volMax, volSpeed, volSpeedTarget; int vibratoDepth, vibratoRate, vibratoPos, vibratoRange, vibratoShape; + int tremoloDepth, tremoloRate, tremoloPos; + int panbrelloDepth, panbrelloRate, panbrelloPos; int portaTarget, portaSpeed; unsigned char arp, arpStage, arpTicks; @@ -61,6 +63,14 @@ struct DivCSChannelState { vibratoDepth(0), vibratoRate(0), vibratoPos(0), + vibratoRange(15), + vibratoShape(0), + tremoloDepth(0), + tremoloRate(0), + tremoloPos(0), + panbrelloDepth(0), + panbrelloRate(0), + panbrelloPos(0), portaTarget(0), portaSpeed(0), arp(0), @@ -93,6 +103,7 @@ class DivCSPlayer { bool bigEndian; short vibTable[64]; + short tremTable[128]; public: unsigned char* getData(); unsigned short* getDataAccess();