From dbe300fc5303028af56a55efeecd99a0992a7928 Mon Sep 17 00:00:00 2001 From: Norbert Lange Date: Sun, 19 Jul 2009 14:02:19 +0000 Subject: fixed retiring of external Notes fixed/improved counting DMAs in Paula added flag for looping songs rearranged maxtrax.h a bit svn-id: r42613 --- sound/mods/maxtrax.cpp | 77 ++++++++++++++++++++++++++++++-------------------- sound/mods/maxtrax.h | 73 ++++++++++++++++++++--------------------------- sound/mods/paula.cpp | 31 ++++++++++++++------ 3 files changed, 99 insertions(+), 82 deletions(-) (limited to 'sound/mods') diff --git a/sound/mods/maxtrax.cpp b/sound/mods/maxtrax.cpp index 38327eda32..b09e2ea732 100644 --- a/sound/mods/maxtrax.cpp +++ b/sound/mods/maxtrax.cpp @@ -33,7 +33,7 @@ namespace Audio { MaxTrax::MaxTrax(int rate, bool stereo) - : Paula(stereo, rate, rate/50), _voiceCtx(), _patch(), _scores(), _numScores(), _microtonal() { + : Paula(stereo, rate, rate/50), _voiceCtx(), _patch(), _scores(), _numScores() { _playerCtx.maxScoreNum = 128; _playerCtx.vBlankFreq = 50; _playerCtx.frameUnit = (uint16)((1000 * (1<<8)) / _playerCtx.vBlankFreq); @@ -180,7 +180,7 @@ endOfEventLoop: switch (voice.status) { case VoiceContext::kStatusSustain: - if (!channel.isAltered && !voice.hasPortamento && !channel.modulation) + if (!channel.isAltered && !voice.hasPortamento/* && !channel.modulation*/) continue; // Update Volume and Period break; @@ -277,7 +277,7 @@ endOfEventLoop: voice.preCalcNote = precalcNote(voice.baseNote, patch.tune, voice.octave); } voice.lastPeriod = calcNote(voice); - } else if (channel.isAltered || channel.modulation) + } else if (channel.isAltered/* || channel.modulation*/) voice.lastPeriod = calcNote(voice); } @@ -288,8 +288,20 @@ endOfEventLoop: for (ChannelContext *c = _channelCtx; c != &_channelCtx[ARRAYSIZE(_channelCtx)]; ++c) c->isAltered = false; - //modulation stuff, sinevalue += tickunit + + // we need to check if some voices have no sustainSample. + // in that case they are finished after the attackSample is done + for (int i = 0; i < ARRAYSIZE(_voiceCtx); ++i) { + VoiceContext &voice = _voiceCtx[i]; + if (voice.dmaOff && Paula::getChannelDmaCount((byte)i) >= voice.dmaOff ) { + voice.isBlocked = false; + voice.priority = 0; + voice.dmaOff = 0; + if (voice.status == VoiceContext::kStatusSustain) + voice.status = VoiceContext::kStatusRelease; + } + } } int32 MaxTrax::calcVolumeDelta(int32 delta, uint16 time) { @@ -306,12 +318,12 @@ void MaxTrax::stopMusic() { _playerCtx.nextEvent = 0; } -bool MaxTrax::doSong(int songIndex, int advance) { +bool MaxTrax::playSong(int songIndex, bool loop, int advance) { if (songIndex < 0 || songIndex >= _numScores) return false; Common::StackLock lock(_mutex); _playerCtx.musicPlaying = false; - _playerCtx.musicLoop = false; + _playerCtx.musicLoop = loop; setTempo(_playerCtx.tempoInitial << 4); _playerCtx.tempoTime = 0; @@ -326,9 +338,9 @@ bool MaxTrax::doSong(int songIndex, int advance) { const Event *cev = _scores[songIndex].events; // Songs are special markers in the score for (; advance > 0; --advance) { - // TODO - check for boundaries + // TODO - check for boundaries for(; cev->command != 0xFF && (cev->command != 0xA0 || (cev->stopTime >> 8) != 0x00); ++cev) - ; + ; // no end_command or special_command + end } _playerCtx.nextEvent = cev; _playerCtx.nextEventTime = cev->startTime; @@ -345,9 +357,11 @@ void MaxTrax::killVoice(byte num) { voice.channel = 0; voice.envelope = 0; voice.status = VoiceContext::kStatusFree; - voice.flags = 0; + voice.isBlocked = false; + voice.hasDamper = false; voice.hasPortamento = false; voice.priority = 0; + voice.dmaOff = 0; //voice.uinqueId = 0; // "stop" voice, set period to 1, vol to 0 @@ -357,7 +371,6 @@ void MaxTrax::killVoice(byte num) { } int8 MaxTrax::pickvoice(const VoiceContext voices[4], uint pick, int16 pri) { - pick &= 3; enum { kPrioFlagFixedSide = 1 << 3 }; if ((pri & (kPrioFlagFixedSide)) == 0) { const bool leftSide = (uint)(pick - 1) > 1; @@ -366,17 +379,16 @@ int8 MaxTrax::pickvoice(const VoiceContext voices[4], uint pick, int16 pri) { const int sameSide = (leftSide) ? leftBest : rightBest; const int otherSide = leftBest + rightBest - sameSide; - if (sameSide > VoiceContext::kStatusRelease && otherSide <= VoiceContext::kStatusRelease) { + if (sameSide > VoiceContext::kStatusRelease && otherSide <= VoiceContext::kStatusRelease) pick ^= 1; // switches sides - } } - pri &= ~kPrioFlagFixedSide; + pick &= 3; - for (int i = 1; i > 0; --i) { + for (int i = 2; i > 0; --i) { const VoiceContext *voice = &voices[pick]; const VoiceContext *alternate = &voices[pick ^ 3]; - if (voice->status > alternate->status + if (voice->status > alternate->status || (voice->status == alternate->status && voice->lastVolume > alternate->lastVolume)) { // TODO: tiebreaking pick ^= 3; // switch channels @@ -385,9 +397,9 @@ int8 MaxTrax::pickvoice(const VoiceContext voices[4], uint pick, int16 pri) { alternate = tmp; } - if ((voice->flags & VoiceContext::kFlagBlocked) != 0 || voice->priority > pri) { + if (voice->isBlocked || voice->priority > pri) { pick ^= 3; // switch channels - if ((alternate->flags & VoiceContext::kFlagBlocked) != 0 || alternate->priority > pri) { + if (alternate->isBlocked || alternate->priority > pri) { // if not already done, switch sides and try again pick ^= 1; continue; @@ -397,6 +409,7 @@ int8 MaxTrax::pickvoice(const VoiceContext voices[4], uint pick, int16 pri) { return (int8)pick; } // failed + debug("Nopick"); return -1; } @@ -426,13 +439,13 @@ uint16 MaxTrax::calcNote(const VoiceContext &voice) { } int8 MaxTrax::noteOn(ChannelContext &channel, const byte note, uint16 volume, uint16 pri) { - if (channel.microtonal >= 0) - _microtonal[note % 127] = channel.microtonal; +// if (channel.microtonal >= 0) +// _microtonal[note % 127] = channel.microtonal; if (!volume) return -1; const Patch &patch = *channel.patch; - if (!patch.samplePtr) + if (!patch.samplePtr || patch.sampleTotalLen == 0) return -1; int8 voiceNum = -1; if ((channel.flags & ChannelContext::kFlagMono) != 0 && channel.voicesActive) { @@ -454,12 +467,11 @@ int8 MaxTrax::noteOn(ChannelContext &channel, const byte note, uint16 volume, ui voiceNum = pickvoice(_voiceCtx, (channel.flags & ChannelContext::kFlagRightChannel) != 0 ? 1 : 0, pri); if (voiceNum >= 0) { VoiceContext &voice = _voiceCtx[voiceNum]; - voice.flags = 0; + voice.hasDamper = false; + voice.isBlocked = false; voice.hasPortamento = false; - if (voice.channel) { + if (voice.channel) killVoice(voiceNum); - voice.flags |= VoiceContext::kFlagStolen; - } voice.channel = &channel; voice.patch = &patch; voice.baseNote = note; @@ -501,6 +513,8 @@ int8 MaxTrax::noteOn(ChannelContext &channel, const byte note, uint16 volume, ui if (patch.sampleTotalLen == patch.sampleAttackLen) { Paula::setChannelSampleStart(voiceNum, 0); Paula::setChannelSampleLen(voiceNum, 0); + Paula::setChannelDmaCount(voiceNum); + voice.dmaOff = 1; } } @@ -543,7 +557,7 @@ void MaxTrax::noteOff(VoiceContext &voice, const byte note) { assert(refNote == note); if (refNote == note) { if ((channel.flags & ChannelContext::kFlagDamper) != 0) - voice.flags |= VoiceContext::kFlagDamper; + voice.hasDamper = true; else voice.status = VoiceContext::kStatusRelease; } @@ -551,9 +565,9 @@ void MaxTrax::noteOff(VoiceContext &voice, const byte note) { } void MaxTrax::resetChannel(ChannelContext &chan, bool rightChannel) { - chan.modulation = 0; - chan.modulationTime = 1000; - chan.microtonal = -1; +// chan.modulation = 0; +// chan.modulationTime = 1000; +// chan.microtonal = -1; chan.portamentoTime = 500; chan.pitchBend = 64 << 7; chan.pitchReal = 0; @@ -575,7 +589,7 @@ void MaxTrax::freeScores() { _scores = 0; } _numScores = 0; - memset(_microtonal, 0, sizeof(_microtonal)); +// memset(_microtonal, 0, sizeof(_microtonal)); } void MaxTrax::freePatches() { @@ -600,6 +614,7 @@ int MaxTrax::playNote(byte note, byte patch, uint16 duration, uint16 volume, boo voice.stopEventCommand = note; voice.stopEventParameter = kNumChannels; voice.stopEventTime = duration << 8; + debug("Extranote: %d, stoptime: %d", voiceIndex, (voice.stopEventTime / (_playerCtx.frameUnit * 50)) ); } return voiceIndex; } @@ -629,10 +644,10 @@ bool MaxTrax::load(Common::SeekableReadStream &musicData, bool loadScores, bool if (flags & (1 << 15)) { debug("Song has microtonal"); - if (loadScores) { +/* if (loadScores) { for (int i = 0; i < ARRAYSIZE(_microtonal); ++i) _microtonal[i] = musicData.readUint16BE(); - } else + } else*/ musicData.skip(128 * 2); } diff --git a/sound/mods/maxtrax.h b/sound/mods/maxtrax.h index 9fef3e68f0..65a504fcaf 100644 --- a/sound/mods/maxtrax.h +++ b/sound/mods/maxtrax.h @@ -35,18 +35,38 @@ public: MaxTrax(int rate, bool stereo); virtual ~MaxTrax(); + bool load(Common::SeekableReadStream &musicData, bool loadScores = true, bool loadSamples = true); + bool playSong(int songIndex, bool loop = false, int advance = 0); + int playNote(byte note, byte patch, uint16 duration, uint16 volume, bool rightSide); + void setVolume(const byte volume) { _playerCtx.volume = volume; } + void setTempo(const uint16 tempo) { + _playerCtx.tickUnit = (int32)(((uint32)(tempo & 0xFFF0) << 8) / (uint16)(5 * _playerCtx.vBlankFreq)); + } + void stopMusic(); + protected: void interrupt(); private: -public: - enum { kNumPatches = 64, kNumVoices = 4, kNumChannels = 16, kNumExtraChannels = 1 }; enum { kPriorityScore, kPriorityNote, kPrioritySound }; - int16 _microtonal[128]; - struct Event; +// int16 _microtonal[128]; - struct PlayerContext { + struct Event { + uint16 startTime; + uint16 stopTime; + byte command; + byte parameter; + }; + + struct Score { + const Event *events; + uint32 numEvents; + } *_scores; + + int _numScores; + + struct { uint16 vBlankFreq; int32 ticks; int32 tickUnit; @@ -93,20 +113,6 @@ public: uint16 sampleOctaves; } _patch[kNumPatches]; - struct Event { - uint16 startTime; - uint16 stopTime; - byte command; - byte parameter; - }; - - struct Score { - const Event *events; - uint32 numEvents; - } *_scores; - - int _numScores; - struct ChannelContext { const Patch *patch; uint16 regParamNumber; @@ -114,7 +120,7 @@ public: uint16 modulation; uint16 modulationTime; - int16 microtonal; +// int16 microtonal; uint16 portamentoTime; @@ -131,7 +137,7 @@ public: kFlagPortamento = 1 << 1, kFlagDamper = 1 << 2, kFlagMono = 1 << 3, - kFlagMicrotonal = 1 << 4, +// kFlagMicrotonal = 1 << 4, kFlagModVolume = 1 << 5//, //kFlagAltered = 1 << 6 }; @@ -139,7 +145,7 @@ public: bool isAltered; uint8 lastNote; - uint8 program; +// uint8 program; } _channelCtx[kNumChannels + kNumExtraChannels]; @@ -178,26 +184,17 @@ public: kStatusStart }; byte status; - enum { - kFlagStolen = 1 << 0, - //kFlagPortamento = 1 << 1, - kFlagDamper = 1 << 2, - kFlagBlocked = 1 << 3, - kFlagRecalc = 1 << 4 - }; - byte flags; + bool hasDamper; + bool isBlocked; byte lastVolume; bool hasPortamento; + byte dmaOff; int32 stopEventTime; byte stopEventCommand; // TODO: Remove? byte stopEventParameter; // TODO: Remove? } _voiceCtx[kNumVoices]; - bool load(Common::SeekableReadStream &musicData, bool loadScores = true, bool loadSamples = true); - bool doSong(int songIndex, int advance = 0); - - void stopMusic(); void freePatches(); void freeScores(); void resetChannel(ChannelContext &chan, bool rightChannel); @@ -208,14 +205,6 @@ public: int8 noteOn(ChannelContext &channel, byte note, uint16 volume, uint16 pri); void noteOff(VoiceContext &voice, byte note); void killVoice(byte num); - int playNote(byte note, byte patch, uint16 duration, uint16 volume, bool rightSide); - - void setVolume(const byte volume) { - _playerCtx.volume = volume; - } - void setTempo(const uint16 tempo) { - _playerCtx.tickUnit = (int32)(((uint32)(tempo & 0xFFF0) << 8) / (uint16)(5 * _playerCtx.vBlankFreq)); - } static int32 precalcNote(byte baseNote, int16 tune, byte octave) { return 0x9fd77 + 0x3C000 + (1 << 16) - ((baseNote << 14) + (tune << 11) / 3) / 3 - (octave << 16); diff --git a/sound/mods/paula.cpp b/sound/mods/paula.cpp index 2e19927d83..f801ecfc9f 100644 --- a/sound/mods/paula.cpp +++ b/sound/mods/paula.cpp @@ -130,41 +130,54 @@ int Paula::readBufferIntern(int16 *buffer, const int numSamples) { int16 *p = buffer; int end = 0; int neededSamples = nSamples; + assert(offset < sLen); // Compute the number of samples to generate; that is, either generate // just as many as were requested, or until the buffer is used up. // Note that dividing two frac_t yields an integer (as the denominators // cancel out each other). // Note that 'end' could be 0 here. No harm in that :-). - end = MIN(neededSamples, (int)((sLen - offset + rate - 1) / rate)); + const int leftSamples = (int)((sLen - offset + rate - 1) / rate); + end = MIN(neededSamples, leftSamples); mixBuffer(p, data, offset, rate, end, _voice[voice].volume, _voice[voice].panning); neededSamples -= end; - // If we have not yet generated enough samples, and looping is active: loop! - if (neededSamples > 0 && _voice[voice].lengthRepeat > 2) { - // At this point we know that we have used up all samples in the buffer, so reset it. - _voice[voice].data = data = _voice[voice].dataRepeat; + if (leftSamples > 0 && end == leftSamples) { + dmaCount++; + data = _voice[voice].data = _voice[voice].dataRepeat; _voice[voice].length = _voice[voice].lengthRepeat; + // TODO: offset -= sLen; but make sure there is no way offset >= 2*sLen + offset &= FRAC_LO_MASK; + } + + // If we have not yet generated enough samples, and looping is active: loop! + if (neededSamples > 0 && _voice[voice].length > 2) { sLen = intToFrac(_voice[voice].length); // If the "rate" exceeds the sample rate, we would have to perform constant // wrap arounds. So, apply the first step of the euclidean algorithm to // achieve the same more efficiently: Take rate modulo sLen - // TODO: This messes up dmaCount + // TODO: This messes up dmaCount and shouldnt happen? if (sLen < rate) - rate %= sLen; + warning("Paula: lenght %d is lesser than rate", _voice[voice].length); +// rate %= sLen; // Repeat as long as necessary. while (neededSamples > 0) { - // TODO offset -= sLen, but only if same rate otherwise need to scale first + // TODO: offset -= sLen; but make sure there is no way offset >= 2*sLen offset &= FRAC_LO_MASK; dmaCount++; - // Compute the number of samples to generate (see above) and mix 'em. end = MIN(neededSamples, (int)((sLen - offset + rate - 1) / rate)); mixBuffer(p, data, offset, rate, end, _voice[voice].volume, _voice[voice].panning); neededSamples -= end; } + + if (offset < sLen) + dmaCount--; + else + offset &= FRAC_LO_MASK; + } // Write back the cached data -- cgit v1.2.3