aboutsummaryrefslogtreecommitdiff
path: root/sound
diff options
context:
space:
mode:
Diffstat (limited to 'sound')
-rw-r--r--sound/mods/maxtrax.cpp230
-rw-r--r--sound/mods/maxtrax.h20
2 files changed, 123 insertions, 127 deletions
diff --git a/sound/mods/maxtrax.cpp b/sound/mods/maxtrax.cpp
index df4aafd4b2..59305ed42e 100644
--- a/sound/mods/maxtrax.cpp
+++ b/sound/mods/maxtrax.cpp
@@ -72,41 +72,42 @@ void MaxTrax::interrupt() {
const int32 millis = _playerCtx.ticks >> 8; // d4
for (int i = 0; i < ARRAYSIZE(_voiceCtx); ++i) {
VoiceContext &voice = _voiceCtx[i]; // a3
- if (!voice.channel || voice.stopEventCommand >= 0x80)
- continue;
- const int channelNo = voice.stopEventParameter;
- assert(channelNo == voice.channel - _channelCtx);
- voice.stopEventTime -= (channelNo < kNumChannels) ? _playerCtx.tickUnit : _playerCtx.frameUnit;
- if (voice.stopEventTime <= 0) {
- noteOff(voice, voice.stopEventCommand);
- voice.stopEventCommand = 0xFF;
+ if (voice.channel && voice.stopEventCommand < 0x80) {
+ const int channelNo = voice.stopEventParameter;
+ assert(channelNo == voice.channel - _channelCtx); // TODO remove
+ voice.stopEventTime -= (channelNo < kNumChannels) ? _playerCtx.tickUnit : _playerCtx.frameUnit;
+ if (voice.stopEventTime <= 0) {
+ noteOff(voice, voice.stopEventCommand);
+ voice.stopEventCommand = 0xFF;
+ }
}
}
if (_playerCtx.musicPlaying) {
const Event *curEvent = _playerCtx.nextEvent;
- int32 eventTime = _playerCtx.nextEventTime;
- for (; eventTime <= millis; eventTime += (++curEvent)->startTime) {
+ int32 eventDelta = _playerCtx.nextEventTime - millis;
+ for (; eventDelta <= 0; eventDelta += (++curEvent)->startTime) {
const byte cmd = curEvent->command;
const byte data = curEvent->parameter;
const uint16 stopTime = curEvent->stopTime;
ChannelContext &channel = _channelCtx[data & 0x0F];
outPutEvent(*curEvent);
- debug("CurTime, EventTime, NextEvent: %d, %d, %d", millis, eventTime, eventTime + curEvent[1].startTime );
+ debug("CurTime, EventDelta, NextDelta: %d, %d, %d", millis, eventDelta, eventDelta + curEvent[1].startTime );
- if (cmd < 0x80) {
+ if (cmd < 0x80) { // Note
const uint16 vol = (data & 0xF0) >> 1;
-
const int8 voiceIndex = noteOn(channel, cmd, vol, kPriorityScore);
if (voiceIndex >= 0) {
VoiceContext &voice = _voiceCtx[voiceIndex];
voice.stopEventCommand = cmd;
voice.stopEventParameter = data & 0x0F;
- voice.stopEventTime = (eventTime + stopTime - millis) << 8;
+ voice.stopEventTime = (eventDelta + stopTime) << 8;
}
+
} else {
switch (cmd) {
+
case 0x80: // TEMPO
if ((_playerCtx.tickUnit >> 8) > stopTime) {
setTempo(data << 4);
@@ -118,69 +119,72 @@ void MaxTrax::interrupt() {
_playerCtx.tempoTicks = 0;
}
break;
+
/* case 0xA0: // SPECIAL
break;
+
case 0xB0: // CONTROL
// TODO: controlChange((byte)stopTime, (byte)(stopTime >> 8))
break;
+
*/ case 0xC0: // PROGRAM
channel.patch = &_patch[stopTime & (kNumPatches - 1)];
break;
+
case 0xE0: // BEND
channel.pitchBend = ((stopTime & 0x7F00) >> 1) | (stopTime & 0x7f);
- channel.pitchReal = ((int32)(channel.pitchBendRange << 8) * (channel.pitchBend - (64 << 7))) / (64 << 7);
- channel.pitchReal = ((channel.pitchBendRange * channel.pitchBend) >> 5) - (channel.pitchBendRange << 8);
- channel.flags |= ChannelContext::kFlagAltered;
+ // channel.pitchReal = ((int32)(channel.pitchBendRange << 8) * (channel.pitchBend - (64 << 7))) / (64 << 7);
+ channel.pitchReal = (((int32)channel.pitchBendRange * channel.pitchBend) >> 5) - (channel.pitchBendRange << 8);
+ channel.isAltered = true;
break;
+
case 0xFF: // END
if (_playerCtx.musicLoop) {
- // event -1 as it gets increased at the end of the loop
- curEvent = _scores[_playerCtx.scoreIndex].events - 1;
+ curEvent = _scores[_playerCtx.scoreIndex].events;
+ eventDelta = curEvent->startTime - millis;
_playerCtx.ticks = 0;
- eventTime = 0;
} else
_playerCtx.musicPlaying = false;
- break;
+ // stop processing for this tick
+ goto endOfEventLoop;
+
default:
debug("Unhandled Command");
outPutEvent(*curEvent);
}
}
}
+endOfEventLoop:
_playerCtx.nextEvent = curEvent;
- _playerCtx.nextEventTime = eventTime;
+ _playerCtx.nextEventTime = eventDelta + millis;
// tempoEffect
if (_playerCtx.tempoTime) {
_playerCtx.tempoTicks += _playerCtx.tickUnit;
- uint16 newTempo;
+ uint16 newTempo = _playerCtx.tempoStart;
if (_playerCtx.tempoTicks < _playerCtx.tempoTime) {
- const uint16 delta = (_playerCtx.tempoTicks * _playerCtx.tempoDelta) / _playerCtx.tempoTime;
- newTempo = delta;
+ newTempo += (_playerCtx.tempoTicks * _playerCtx.tempoDelta) / _playerCtx.tempoTime;
} else {
_playerCtx.tempoTime = 0;
- newTempo = _playerCtx.tempoDelta;
+ newTempo += _playerCtx.tempoDelta;
}
setTempo(_playerCtx.tempoStart + newTempo);
}
}
- // envelopes
+ // Handling of Envelopes and Portamento
for (int i = 0; i < ARRAYSIZE(_voiceCtx); ++i) {
- VoiceContext &voice = _voiceCtx[i]; // a2
+ VoiceContext &voice = _voiceCtx[i];
if (!voice.channel)
continue;
- const ChannelContext &channel = *voice.channel; // a3
- const Patch &patch = *voice.patch; // a5, used with start and later
- voice.lastTicks += _playerCtx.tickUnit;
- bool envHandling = true;
- byte newVolume = 0xFF; // if set to 0 this means skip recalc
+ const ChannelContext &channel = *voice.channel;
+ const Patch &patch = *voice.patch;
+
switch (voice.status) {
case VoiceContext::kStatusSustain:
- if ((channel.flags & (ChannelContext::kFlagAltered | VoiceContext::kFlagPortamento)) == 0 && !channel.modulation)
+ if (!channel.isAltered && !voice.hasPortamento && !channel.modulation)
continue;
- // goto .l18
- envHandling = false;
+ // Update Volume and Period
break;
case VoiceContext::kStatusHalt:
@@ -188,121 +192,114 @@ void MaxTrax::interrupt() {
continue;
case VoiceContext::kStatusStart:
- voice.envelope = patch.attackPtr;
if (patch.attackLen) {
+ voice.envelope = patch.attackPtr;
const uint16 duration = voice.envelope->duration;
voice.envelopeLeft = patch.attackLen;
voice.ticksLeft = duration << 8;
voice.status = VoiceContext::kStatusAttack;
- voice.lastTicks = _playerCtx.tickUnit;
- const int32 vol = voice.envelope->volume;
- voice.incrVolume = (duration) ? (1000 * vol) / (duration * _playerCtx.vBlankFreq) : vol;
- // skip to I9
+ voice.incrVolume = calcVolumeDelta((int32)voice.envelope->volume, duration);
+ // Process Envelope
} else {
voice.status = VoiceContext::kStatusSustain;
voice.baseVolume = patch.volume;
- voice.lastTicks = _playerCtx.tickUnit;
- // goto .l18
- envHandling = false;
+ // Update Volume and Period
}
break;
case VoiceContext::kStatusRelease:
- voice.envelope = patch.attackPtr + patch.attackLen;
if (patch.releaseLen) {
+ voice.envelope = patch.attackPtr + patch.attackLen;
const uint16 duration = voice.envelope->duration;
voice.envelopeLeft = patch.releaseLen;
voice.ticksLeft = duration << 8;
voice.status = VoiceContext::kStatusDecay;
- voice.lastTicks = _playerCtx.tickUnit;
- const int32 vol = voice.envelope->volume - voice.baseVolume;
- voice.incrVolume = (duration) ? (1000 * vol) / (duration * _playerCtx.vBlankFreq) : vol;
- // skip to I9
+ voice.incrVolume = calcVolumeDelta((int32)voice.envelope->volume - voice.baseVolume, duration);
+ // Process Envelope
} else {
voice.status = VoiceContext::kStatusHalt;
- // set d4 = 0, goto I17
- envHandling = false;
- newVolume = 0;
+ voice.lastVolume = 0;
+ // Send Audio Packet
}
break;
}
- // .I9 - env managment
- if (envHandling) {
+
+ // Process Envelope
+ const uint16 envUnit = _playerCtx.frameUnit;
+ if (voice.envelope) {
+ // TODO remove paranoid asserts
assert(voice.status != VoiceContext::kStatusSustain);
+ assert(voice.status == VoiceContext::kStatusAttack || VoiceContext::kStatusRelease);
assert(voice.envelope);
assert(voice.envelopeLeft >= 0);
- if (voice.ticksLeft > _playerCtx.tickUnit) {
+ if (voice.ticksLeft > envUnit) { // envelope still active
voice.baseVolume = (uint16)MIN(MAX(0, voice.baseVolume + voice.incrVolume), 0x8000);
- voice.ticksLeft -= _playerCtx.tickUnit;
- // goto .l18
- } else {
- // a0 = voice.envelope
+ voice.ticksLeft -= envUnit;
+ // Update Volume and Period
+
+ } else { // next or last Envelope
voice.baseVolume = voice.envelope->volume;
assert(voice.envelopeLeft > 0);
if (--voice.envelopeLeft) {
++voice.envelope;
const uint16 duration = voice.envelope->duration;
voice.ticksLeft = duration << 8;
- const int32 vol = voice.envelope->volume - voice.baseVolume;
- voice.incrVolume = (duration) ? (1000 * vol) / (duration * _playerCtx.vBlankFreq) : vol;
- // goto .l18
+ voice.incrVolume = calcVolumeDelta((int32)voice.envelope->volume - voice.baseVolume, duration);
+ // Update Volume and Period
} else if (voice.status == VoiceContext::kStatusDecay) {
- voice.envelope = 0;
voice.status = VoiceContext::kStatusHalt;
- // set d4 = 0, goto I17
- newVolume = 0;
+ voice.envelope = 0;
+ voice.lastVolume = 0;
+ // Send Audio Packet
} else {
assert(voice.status == VoiceContext::kStatusAttack);
voice.status = VoiceContext::kStatusSustain;
- voice.lastTicks = _playerCtx.tickUnit;
voice.envelope = 0;
- // goto .l18
+ // Update Volume and Period
}
}
- } else
- assert(voice.envelope == 0);
+ }
- // .l18 - recalc
- if (newVolume) {
- // calc volume
+ // Update Volume and Period
+ if (voice.status >= VoiceContext::kStatusDecay) {
+ // Calc volume
uint16 vol = (voice.noteVolume < (1 << 7)) ? (voice.noteVolume * _playerCtx.volume) >> 7 : _playerCtx.volume;
if (voice.baseVolume < (1 << 15))
vol = (uint16)(((uint32)vol * voice.baseVolume) >> 15);
if (voice.channel->volume < (1 << 7))
vol = (vol * voice.channel->volume) >> 7;
+ voice.lastVolume = (byte)MIN(vol, (uint16)0x64);
- newVolume = (byte)MIN(vol, (uint16)0x64);
- voice.lastVolume = newVolume = 0x64;
-
- if ((voice.flags & VoiceContext::kFlagPortamento) != 0) {
- voice.portaTicks += _playerCtx.tickUnit;
+ // Calc Period
+ if (voice.hasPortamento) {
+ voice.portaTicks += envUnit;
if ((uint16)(voice.portaTicks >> 8) >= channel.portamentoTime) {
- voice.flags &= ~VoiceContext::kFlagPortamento;
+ voice.hasPortamento = false;
voice.baseNote = voice.endNote;
}
- voice.flags |= VoiceContext::kFlagRecalc;
- calcNote(voice);
- } else {
- if ((channel.flags & ChannelContext::kFlagAltered) != 0 || channel.modulation) {
- voice.flags |= VoiceContext::kFlagRecalc;
- calcNote(voice);
- }
- }
+ voice.lastPeriod = calcNote(voice);
+ } else if (channel.isAltered || channel.modulation)
+ voice.lastPeriod = calcNote(voice);
}
- // .l17 - send audio package
+ // Send Audio Packet
Paula::setChannelPeriod(i, (voice.lastPeriod) ? voice.lastPeriod : 1000);
- Paula::setChannelVolume(i, (voice.lastPeriod) ? newVolume : 0);
- }
- for (int i = 0; i < ARRAYSIZE(_channelCtx); ++i) {
- ChannelContext &channel = _channelCtx[i];
- channel.flags &= ~ChannelContext::kFlagAltered;
+ Paula::setChannelVolume(i, (voice.lastPeriod) ? voice.lastVolume : 0);
}
+ for (ChannelContext *c = _channelCtx; c != &_channelCtx[ARRAYSIZE(_channelCtx)]; ++c)
+ c->isAltered = false;
//modulation stuff, sinevalue += tickunit
}
+int32 MaxTrax::calcVolumeDelta(int32 delta, uint16 time) {
+ const int32 div = time * _playerCtx.vBlankFreq;
+ if (div <= 1000)
+ return delta; // time to small or 0
+ return (1000 * delta) / div;
+}
+
void MaxTrax::stopMusic() {
}
@@ -327,8 +324,10 @@ void MaxTrax::killVoice(byte num) {
VoiceContext &voice = _voiceCtx[num];
--(voice.channel->voicesActive);
voice.channel = 0;
+ voice.envelope = 0;
voice.status = VoiceContext::kStatusFree;
voice.flags = 0;
+ voice.hasPortamento = false;
voice.priority = 0;
voice.uinqueId = 0;
@@ -382,14 +381,11 @@ int8 MaxTrax::pickvoice(const VoiceContext voices[4], uint pick, int16 pri) {
return -1;
}
-int MaxTrax::calcNote(VoiceContext &voice) {
+uint16 MaxTrax::calcNote(const VoiceContext &voice, int32 *offset) {
const ChannelContext &channel = *voice.channel;
- voice.lastPeriod = 0;
-
int16 bend = 0;
- if ((voice.flags & VoiceContext::kFlagPortamento) != 0) {
+ if (voice.hasPortamento) {
// microtonal crap
-
bend = (int16)(((int8)(voice.endNote - voice.baseNote)) * voice.portaTicks) / channel.portamentoTime;
}
// modulation
@@ -416,20 +412,21 @@ int MaxTrax::calcNote(VoiceContext &voice) {
enum { K_VALUE = 0x9fd77, PREF_PERIOD = 0x8fd77, PERIOD_LIMIT = 0x6f73d };
tone = K_VALUE - tone;
- int octave = 0;
- if ((voice.flags & VoiceContext::kFlagRecalc) == 0) {
- octave = (tone > PREF_PERIOD) ? MIN((int)((tone + 0xFFFF - PREF_PERIOD) >> 16), (int)patch.sampleOctaves - 1) : 0;
- voice.periodOffset = octave << 16;
- }
- tone -= voice.periodOffset;
+ // calculate which sample to use
+ if (offset) {
+ *offset = ((tone > PREF_PERIOD) ? MIN((int32)((tone + 0xFFFF - PREF_PERIOD) >> 16), (int32)(patch.sampleOctaves - 1)) : 0) << 16;
+ tone -= *offset;
+ } else
+ tone -= voice.periodOffset;
+
if (tone >= PERIOD_LIMIT) {
// calculate 2^tone and round towards nearest integer
// 2*2^tone = exp((tone+1) * ln(2))
const uint16 periodX2 = (uint16)expf((float)(tone + (1 << 16)) * (float)(0.69314718055994530942 / (1 << 16)));
- voice.lastPeriod = (periodX2 + 1) / 2;
+ return (periodX2 + 1) / 2;
}
- return octave;
+ return 0;
}
int8 MaxTrax::noteOn(ChannelContext &channel, const byte note, uint16 volume, uint16 pri) {
@@ -448,13 +445,12 @@ int8 MaxTrax::noteOn(ChannelContext &channel, const byte note, uint16 volume, ui
;
if (voiceNum >= 0 && voice->status >= VoiceContext::kStatusSustain && (channel.flags & ChannelContext::kFlagPortamento) != 0) {
// reset previous porta
- if ((voice->flags & VoiceContext::kFlagPortamento) != 0)
+ if (voice->hasPortamento)
voice->baseNote = voice->endNote;
voice->portaTicks = 0;
- voice->flags |= VoiceContext::kFlagPortamento;
+ voice->hasPortamento = true;
voice->endNote = channel.lastNote = note;
voice->noteVolume = (_playerCtx.handleVolume) ? volume + 1 : 128;
- return voiceNum;
}
} else {
@@ -469,8 +465,12 @@ int8 MaxTrax::noteOn(ChannelContext &channel, const byte note, uint16 volume, ui
voice.channel = &channel;
voice.patch = &patch;
voice.baseNote = note;
+ voice.hasPortamento = false;
+
+
+ voice.lastPeriod = calcNote(voice, &voice.periodOffset);
+ const int useOctave = voice.periodOffset >> 16;
- int useOctave = calcNote(voice);
voice.priority = (byte)pri;
voice.status = VoiceContext::kStatusStart;
@@ -481,11 +481,8 @@ int8 MaxTrax::noteOn(ChannelContext &channel, const byte note, uint16 volume, ui
voice.noteVolume = (voice.noteVolume * channel.volume) >> 7;
voice.baseVolume = 0;
- voice.lastTicks = 0;
- uint16 period = voice.lastPeriod;
- if (!period)
- period = 1000;
+ const uint16 period = (voice.lastPeriod) ? voice.lastPeriod : 1000;
// TODO: since the original player is using the OS-functions, more than 1 sample could be queued up already
// get samplestart for the given octave
@@ -525,7 +522,7 @@ int8 MaxTrax::noteOn(ChannelContext &channel, const byte note, uint16 volume, ui
voice.portaTicks = 0;
voice.endNote = voice.baseNote;
voice.baseNote = channel.lastNote;
- voice.flags |= VoiceContext::kFlagPortamento;
+ voice.hasPortamento = true;
}
channel.lastNote = note;
}
@@ -538,7 +535,8 @@ int8 MaxTrax::noteOn(ChannelContext &channel, const byte note, uint16 volume, ui
void MaxTrax::noteOff(VoiceContext &voice, const byte note) {
ChannelContext &channel = *voice.channel;
if (channel.voicesActive && voice.status != VoiceContext::kStatusRelease) {
- const byte refNote = ((voice.flags & VoiceContext::kFlagPortamento) != 0) ? voice.endNote : voice.baseNote;
+ // TODO is this check really necessary?
+ const byte refNote = (voice.hasPortamento) ? voice.endNote : voice.baseNote;
assert(refNote == note);
if (refNote == note) {
if ((channel.flags & ChannelContext::kFlagDamper) != 0)
@@ -559,7 +557,7 @@ void MaxTrax::resetChannel(ChannelContext &chan, bool rightChannel) {
chan.pitchBendRange = 24;
chan.volume = 128;
chan.flags &= ~ChannelContext::kFlagPortamento & ~ChannelContext::kFlagMicrotonal;
- chan.flags |= ChannelContext::kFlagAltered;
+ chan.isAltered = true;
if (rightChannel)
chan.flags |= ChannelContext::kFlagRightChannel;
else
diff --git a/sound/mods/maxtrax.h b/sound/mods/maxtrax.h
index a1791b3fef..4b0b8b0360 100644
--- a/sound/mods/maxtrax.h
+++ b/sound/mods/maxtrax.h
@@ -70,11 +70,6 @@ public:
int scoreIndex;
const Event *nextEvent;
int32 nextEventTime;
-
- bool addedNote;
- byte lastVoice;
- byte voicesActive;
-
} _playerCtx;
struct Envelope {
@@ -141,6 +136,7 @@ public:
kFlagAltered = 1 << 6
};
byte flags;
+ bool isAltered;
uint8 lastNote;
uint8 program;
@@ -156,7 +152,7 @@ public:
uint32 ticksLeft;
int32 portaTicks;
int32 incrVolume;
- uint32 periodOffset;
+ int32 periodOffset;
/*ifne FASTSOUND
APTR voice_CurFastIOB ; current fast iob playing
APTR voice_NextFastIOB ; next fast iob to play
@@ -183,17 +179,18 @@ public:
byte status;
enum {
kFlagStolen = 1 << 0,
- kFlagPortamento = 1 << 1,
+ //kFlagPortamento = 1 << 1,
kFlagDamper = 1 << 2,
kFlagBlocked = 1 << 3,
kFlagRecalc = 1 << 4
};
byte flags;
byte lastVolume;
+ bool hasPortamento;
int32 stopEventTime;
- byte stopEventCommand;
- byte stopEventParameter;
+ byte stopEventCommand; // TODO: Remove?
+ byte stopEventParameter; // TODO: Remove?
} _voiceCtx[kNumVoices];
bool load(Common::SeekableReadStream &musicData, bool loadScores = true, bool loadSamples = true);
@@ -204,8 +201,9 @@ public:
void freeScores();
void resetChannel(ChannelContext &chan, bool rightChannel);
- static int8 MaxTrax::pickvoice(const VoiceContext voice[4], uint pick, int16 pri);
- int calcNote(VoiceContext &voice);
+ static int8 pickvoice(const VoiceContext voice[4], uint pick, int16 pri);
+ int32 calcVolumeDelta(int32 delta, uint16 time);
+ uint16 calcNote(const VoiceContext &voice, int32 *offset = 0);
int8 noteOn(ChannelContext &channel, byte note, uint16 volume, uint16 pri);
void noteOff(VoiceContext &voice, byte note);
void killVoice(byte num);