diff options
-rw-r--r-- | engines/kyra/sound.h | 3 | ||||
-rw-r--r-- | engines/kyra/sound_amiga.cpp | 39 | ||||
-rw-r--r-- | engines/kyra/sound_intern.h | 6 | ||||
-rw-r--r-- | sound/mods/maxtrax.cpp | 77 | ||||
-rw-r--r-- | sound/mods/maxtrax.h | 73 | ||||
-rw-r--r-- | sound/mods/paula.cpp | 31 | ||||
-rw-r--r-- | tfmx/mxtxplayer.cpp | 4 |
7 files changed, 124 insertions, 109 deletions
diff --git a/engines/kyra/sound.h b/engines/kyra/sound.h index 263cd586f7..2f24a264f1 100644 --- a/engines/kyra/sound.h +++ b/engines/kyra/sound.h @@ -55,7 +55,8 @@ public: kMidiGM, kTowns, kPC98, - kPCSpkr + kPCSpkr, + kAmiga }; virtual kType getMusicType() const = 0; diff --git a/engines/kyra/sound_amiga.cpp b/engines/kyra/sound_amiga.cpp index ee4fba5f22..11c28940fd 100644 --- a/engines/kyra/sound_amiga.cpp +++ b/engines/kyra/sound_amiga.cpp @@ -41,7 +41,7 @@ const char *const SoundAmiga::kFilenameTable[3][2] = { }; SoundAmiga::SoundAmiga(KyraEngine_v1 *vm, Audio::Mixer *mixer) : - Sound(vm, mixer), _driver(0), _fileLoaded((uint)-1) { + Sound(vm, mixer), _driver(0), _fileLoaded(kFileNone) { } SoundAmiga::~SoundAmiga() { @@ -56,7 +56,7 @@ bool SoundAmiga::init() { void SoundAmiga::loadSoundFile(uint file) { assert(file < ARRAYSIZE(kFilenameTable)); - if (_fileLoaded == file) + if (_fileLoaded == (FileType)file) return; Common::SeekableReadStream *scoreIn = _vm->resource()->createReadStream(kFilenameTable[file][0]); @@ -65,20 +65,17 @@ void SoundAmiga::loadSoundFile(uint file) { if (scoreIn && sampleIn) { _driver->load(*scoreIn, true, false); _driver->load(*sampleIn, false, true); - _fileLoaded = file; + _fileLoaded = (FileType)file; } delete sampleIn; } else { if (scoreIn) { _driver->load(*scoreIn); - _fileLoaded = file; + _fileLoaded = (FileType)file; } } delete scoreIn; } -void SoundAmiga::loadSoundFile(Common::String) { - assert("Dont call me" == 0); -} void SoundAmiga::playTrack(uint8 track) { static const byte tempoIntro[6] = { 0x46, 0x55, 0x3C, 0x41, 0x78, 0x50 }; @@ -96,11 +93,12 @@ void SoundAmiga::playTrack(uint8 track) { // intro if (track >= 2) { track -= 2; - _driver->setVolume(0x40); - _driver->doSong(track); - _driver->setTempo(tempoIntro[track] << 4); - if (!_mixer->isSoundHandleActive(_soundChannels[0])) - _mixer->playInputStream(Audio::Mixer::kPlainSoundType, &_soundChannels[0], _driver); + if (_driver->playSong(track)) { + _driver->setVolume(0x40); + _driver->setTempo(tempoIntro[track] << 4); + if (!_mixer->isSoundHandleActive(_soundChannels[0])) + _mixer->playInputStream(Audio::Mixer::kPlainSoundType, &_soundChannels[0], _driver); + } } else if (track == 0){ _driver->stopMusic(); } else { // track == 1 @@ -111,13 +109,13 @@ void SoundAmiga::playTrack(uint8 track) { if (false && track < 0x80 && track != 3) { if (track >= 2) { track -= 0xB; - _driver->setVolume(0x40); - if (loopIngame) - ; // TODO: enable looping - _driver->doSong(track); - _driver->setTempo(tempoIngame[track] << 4); - if (!_mixer->isSoundHandleActive(_soundChannels[0])) - _mixer->playInputStream(Audio::Mixer::kPlainSoundType, &_soundChannels[0], _driver); + if (_driver->playSong(track, loopIngame[track] != 0)) { + _driver->setVolume(0x40); + + _driver->setTempo(tempoIngame[track] << 4); + if (!_mixer->isSoundHandleActive(_soundChannels[0])) + _mixer->playInputStream(Audio::Mixer::kPlainSoundType, &_soundChannels[0], _driver); + } } else if (track == 0){ _driver->stopMusic(); } else { // track == 1 @@ -147,7 +145,7 @@ void SoundAmiga::playSoundEffect(uint8 track) { // intro assert(track < ARRAYSIZE(tableEffectsIntro)); const EffectEntry &entry = tableEffectsIntro[track]; - _driver->playNote(entry.note, entry.patch, entry.duration, entry.volume, entry.pan != 0); + bool success = _driver->playNote(entry.note, entry.patch, entry.duration, entry.volume, entry.pan != 0) >= 0; if (!_mixer->isSoundHandleActive(_soundChannels[0])) _mixer->playInputStream(Audio::Mixer::kPlainSoundType, &_soundChannels[0], _driver); @@ -166,7 +164,6 @@ void SoundAmiga::playSoundEffect(uint8 track) { _driver->playNote(entry.note, entry.patch, entry.duration, entry.volume, pan != 0); if (!_mixer->isSoundHandleActive(_soundChannels[0])) _mixer->playInputStream(Audio::Mixer::kPlainSoundType, &_soundChannels[0], _driver); - } } } diff --git a/engines/kyra/sound_intern.h b/engines/kyra/sound_intern.h index 3c0487d02a..982d370bf8 100644 --- a/engines/kyra/sound_intern.h +++ b/engines/kyra/sound_intern.h @@ -290,13 +290,13 @@ public: SoundAmiga(KyraEngine_v1 *vm, Audio::Mixer *mixer); ~SoundAmiga(); - virtual kType getMusicType() const { return kPC98; } //FIXME + virtual kType getMusicType() const { return kAmiga; } //FIXME bool init(); void process() {} void loadSoundFile(uint file); - void loadSoundFile(Common::String); + void loadSoundFile(Common::String) {} void playTrack(uint8 track); void haltTrack(); @@ -307,7 +307,7 @@ public: protected: Audio::MaxTrax *_driver; - uint _fileLoaded; + enum FileType { kFileNone = -1, kFileIntro = 0, kFileGame = 1, kFileFinal = 2 } _fileLoaded; static const char *const kFilenameTable[3][2]; static const struct EffectEntry { 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<stereo>(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<stereo>(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 diff --git a/tfmx/mxtxplayer.cpp b/tfmx/mxtxplayer.cpp index 1ceebf8167..0790d7ac70 100644 --- a/tfmx/mxtxplayer.cpp +++ b/tfmx/mxtxplayer.cpp @@ -74,7 +74,7 @@ void modcmdmain(const int argc, const char *const argv[]) { param = atoi(argv[++i]); debug( "play Song %02X", param); - player->doSong(param); + player->playSong(param); //player->noteOn(player->_channelCtx[0], 43, 64, 0); @@ -88,7 +88,7 @@ void modcmdmain(const int argc, const char *const argv[]) { } if (!hasCmd) { - player->doSong(0); + player->playSong(0); player->playNote(0x18, 0x03, 0x0F52, 120, 1); } |