mirror of
https://github.com/tildearrow/furnace.git
synced 2024-12-01 00:43:02 +00:00
Merge pull request #1347 from mooinglemur/20230811-zsmtuning
ZSM: include song tuning in export
This commit is contained in:
commit
0073357cb7
4 changed files with 31 additions and 11 deletions
|
@ -162,11 +162,12 @@ There are currently no supported expansion HW IDs assigned.
|
|||
|
||||
The purpose of this channel is to provide for music synchronization cues that applications may use to perform operations in sync with the music (such as when the Goombas jump in New Super Mario Bros in time with the BOP! BOP! notes in the music). It is intended for the reference player to provide a sync channel callback, passing the data bytes to the callback function, and then to proceed with playback.
|
||||
|
||||
The synchronization format currently defines this one event type:
|
||||
The synchronization format currently defines these event types:
|
||||
|
||||
Event Type|Description|Message Format
|
||||
--|--|--
|
||||
`0x00`|Generic sync message|`xx` (any value from `0x00`-`0xff`)
|
||||
`0x01`|Song tuning|a signed byte indicating an offset from A-440 tuning represented in 256ths of a semitone
|
||||
|
||||
An example of an EXTCMD containing one sync event might look as follows: `0x40 0x82 0x00 0x05`
|
||||
|
||||
|
|
|
@ -116,6 +116,10 @@ void DivZSM::writeYM(unsigned char a, unsigned char v) {
|
|||
}
|
||||
}
|
||||
|
||||
void DivZSM::writeSync(unsigned char a, unsigned char v) {
|
||||
return syncCache.push_back(DivRegWrite(a,v));
|
||||
}
|
||||
|
||||
void DivZSM::writePSG(unsigned char a, unsigned char v) {
|
||||
// TODO: suppress writes to PSG voice that is not audible (volume=0)
|
||||
// ^ Let's leave these alone, ZSMKit has a feature that can benefit
|
||||
|
@ -126,7 +130,7 @@ void DivZSM::writePSG(unsigned char a, unsigned char v) {
|
|||
} else if (a==68) {
|
||||
// Sync event
|
||||
numWrites++;
|
||||
return syncCache.push_back(v);
|
||||
return writeSync(0x00,v);
|
||||
} else if (a>=64) {
|
||||
return writePCM(a-64,v);
|
||||
}
|
||||
|
@ -390,16 +394,19 @@ void DivZSM::flushWrites() {
|
|||
}
|
||||
}
|
||||
n=0;
|
||||
while (n<(long)syncCache.size()) { // we have one or more sync events to write
|
||||
int writes=syncCache.size()-n;
|
||||
for (DivRegWrite& write: syncCache) {
|
||||
if (n%ZSM_SYNC_MAX_WRITES==0) {
|
||||
w->writeC(ZSM_EXT);
|
||||
if (writes>ZSM_SYNC_MAX_WRITES) writes=ZSM_SYNC_MAX_WRITES;
|
||||
w->writeC(ZSM_EXT_SYNC|(writes<<1));
|
||||
for (; writes>0; writes--) {
|
||||
w->writeC(0x00); // 0x00 = Arbitrary sync message
|
||||
w->writeC(syncCache[n++]);
|
||||
if (syncCache.size()-n>ZSM_SYNC_MAX_WRITES) {
|
||||
w->writeC((unsigned char)(ZSM_EXT_SYNC|(ZSM_SYNC_MAX_WRITES<<1)));
|
||||
} else {
|
||||
w->writeC((unsigned char)(ZSM_EXT_SYNC|((syncCache.size()-n)<<1)));
|
||||
}
|
||||
}
|
||||
n++;
|
||||
w->writeC(write.addr);
|
||||
w->writeC(write.val);
|
||||
}
|
||||
syncCache.clear();
|
||||
numWrites=0;
|
||||
}
|
||||
|
|
|
@ -63,7 +63,7 @@ class DivZSM {
|
|||
std::vector<unsigned char> pcmData;
|
||||
std::vector<unsigned char> pcmCache;
|
||||
std::vector<S_pcmInst> pcmInsts;
|
||||
std::vector<unsigned char> syncCache;
|
||||
std::vector<DivRegWrite> syncCache;
|
||||
int loopOffset;
|
||||
int numWrites;
|
||||
int ticks;
|
||||
|
@ -78,6 +78,7 @@ class DivZSM {
|
|||
void writeYM(unsigned char a, unsigned char v);
|
||||
void writePSG(unsigned char a, unsigned char v);
|
||||
void writePCM(unsigned char a, unsigned char v);
|
||||
void writeSync(unsigned char a, unsigned char v);
|
||||
void tick(int numticks = 1);
|
||||
void setLoopPoint();
|
||||
SafeWriter* finish();
|
||||
|
|
|
@ -107,6 +107,17 @@ SafeWriter* DivEngine::saveZSM(unsigned int zsmrate, bool loop) {
|
|||
// TODO: incorporate the Furnace meta-command for init data and filter
|
||||
// out writes to otherwise-unused channels.
|
||||
}
|
||||
// Indicate the song's tuning as a sync meta-event
|
||||
// specified in terms of how many 1/256th semitones
|
||||
// the song is offset from standard A-440 tuning.
|
||||
// This is mainly to benefit visualizations in players
|
||||
// for non-standard tunings so that they can avoid
|
||||
// displaying the entire song held in pitch bend.
|
||||
// Tunings offsets that exceed a half semitone
|
||||
// will simply be represented in a different key
|
||||
// by nature of overflowing the signed char value
|
||||
signed char tuningoffset=(signed char)(round(3072*(log(song.tuning/440.0)/log(2))))&0xff;
|
||||
zsm.writeSync(0x01,tuningoffset);
|
||||
|
||||
while (!done) {
|
||||
if (loopPos==-1) {
|
||||
|
|
Loading…
Reference in a new issue