2857 lines
64 KiB
C++
2857 lines
64 KiB
C++
|
|
#include "fmod_settings.h"
|
||
|
|
|
||
|
|
#ifdef FMOD_SUPPORT_S3M
|
||
|
|
|
||
|
|
#include "fmod.h"
|
||
|
|
|
||
|
|
#include "fmod_channel_software.h"
|
||
|
|
#include "fmod_codec_s3m.h"
|
||
|
|
#include "fmod_debug.h"
|
||
|
|
#include "fmod_dspi.h"
|
||
|
|
#include "fmod_file.h"
|
||
|
|
#include "fmod_localcriticalsection.h"
|
||
|
|
#include "fmod_memory.h"
|
||
|
|
#include "fmod_systemi.h"
|
||
|
|
#include "fmod_string.h"
|
||
|
|
|
||
|
|
#include <stdio.h>
|
||
|
|
#include <stdlib.h>
|
||
|
|
|
||
|
|
namespace FMOD
|
||
|
|
{
|
||
|
|
|
||
|
|
|
||
|
|
FMOD_CODEC_DESCRIPTION_EX s3mcodec;
|
||
|
|
|
||
|
|
|
||
|
|
#ifdef PLUGIN_EXPORTS
|
||
|
|
|
||
|
|
#ifdef __cplusplus
|
||
|
|
extern "C" {
|
||
|
|
#endif
|
||
|
|
|
||
|
|
/*
|
||
|
|
FMODGetCodecDescription is mandantory for every fmod plugin. This is the symbol the registerplugin function searches for.
|
||
|
|
Must be declared with F_API to make it export as stdcall.
|
||
|
|
*/
|
||
|
|
F_DECLSPEC F_DLLEXPORT FMOD_CODEC_DESCRIPTION_EX * F_API FMODGetCodecDescriptionEx()
|
||
|
|
{
|
||
|
|
return CodecS3M::getDescriptionEx();
|
||
|
|
}
|
||
|
|
|
||
|
|
#ifdef __cplusplus
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#endif /* PLUGIN_EXPORTS */
|
||
|
|
|
||
|
|
|
||
|
|
/*
|
||
|
|
[
|
||
|
|
[DESCRIPTION]
|
||
|
|
|
||
|
|
[PARAMETERS]
|
||
|
|
|
||
|
|
[RETURN_VALUE]
|
||
|
|
|
||
|
|
[REMARKS]
|
||
|
|
|
||
|
|
[PLATFORMS]
|
||
|
|
Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube
|
||
|
|
|
||
|
|
[SEE_ALSO]
|
||
|
|
]
|
||
|
|
*/
|
||
|
|
FMOD_CODEC_DESCRIPTION_EX *CodecS3M::getDescriptionEx()
|
||
|
|
{
|
||
|
|
FMOD_memset(&s3mcodec, 0, sizeof(FMOD_CODEC_DESCRIPTION_EX));
|
||
|
|
|
||
|
|
s3mcodec.name = "FMOD S3M Codec";
|
||
|
|
s3mcodec.version = 0x00010100;
|
||
|
|
s3mcodec.timeunits = (FMOD_TIMEUNIT)(FMOD_TIMEUNIT_PCM | FMOD_TIMEUNIT_MODORDER | FMOD_TIMEUNIT_MODROW | FMOD_TIMEUNIT_MODPATTERN);
|
||
|
|
s3mcodec.defaultasstream = 1;
|
||
|
|
s3mcodec.open = &CodecS3M::openCallback;
|
||
|
|
s3mcodec.close = &CodecS3M::closeCallback;
|
||
|
|
s3mcodec.read = &CodecS3M::readCallback;
|
||
|
|
s3mcodec.getlength = &MusicSong::getLengthCallback;
|
||
|
|
s3mcodec.setposition = &CodecS3M::setPositionCallback;
|
||
|
|
s3mcodec.getposition = &MusicSong::getPositionCallback;
|
||
|
|
|
||
|
|
s3mcodec.getmusicnumchannels = &MusicSong::getMusicNumChannelsCallback;
|
||
|
|
s3mcodec.setmusicchannelvolume = &MusicSong::setMusicChannelVolumeCallback;
|
||
|
|
s3mcodec.getmusicchannelvolume = &MusicSong::getMusicChannelVolumeCallback;
|
||
|
|
|
||
|
|
s3mcodec.mType = FMOD_SOUND_TYPE_S3M;
|
||
|
|
s3mcodec.mSize = sizeof(CodecS3M);
|
||
|
|
|
||
|
|
return &s3mcodec;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/*
|
||
|
|
[
|
||
|
|
[DESCRIPTION]
|
||
|
|
|
||
|
|
[PARAMETERS]
|
||
|
|
|
||
|
|
[RETURN_VALUE]
|
||
|
|
|
||
|
|
[REMARKS]
|
||
|
|
|
||
|
|
[PLATFORMS]
|
||
|
|
Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube
|
||
|
|
|
||
|
|
[SEE_ALSO]
|
||
|
|
]
|
||
|
|
*/
|
||
|
|
FMOD_RESULT CodecS3M::calculateLength()
|
||
|
|
{
|
||
|
|
waveformat[0].lengthpcm = 0;
|
||
|
|
|
||
|
|
play();
|
||
|
|
|
||
|
|
while (!(mFinished))
|
||
|
|
{
|
||
|
|
update(false);
|
||
|
|
|
||
|
|
waveformat[0].lengthpcm += mMixerSamplesPerTick;
|
||
|
|
}
|
||
|
|
|
||
|
|
stop();
|
||
|
|
|
||
|
|
return FMOD_OK;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
/*
|
||
|
|
[
|
||
|
|
[DESCRIPTION]
|
||
|
|
|
||
|
|
[PARAMETERS]
|
||
|
|
|
||
|
|
[RETURN_VALUE]
|
||
|
|
|
||
|
|
[REMARKS]
|
||
|
|
|
||
|
|
[SEE_ALSO]
|
||
|
|
]
|
||
|
|
*/
|
||
|
|
FMOD_RESULT MusicChannelS3M::volumeSlide()
|
||
|
|
{
|
||
|
|
MusicVirtualChannel *vcptr = (MusicVirtualChannel *)mVirtualChannelHead.getNext();
|
||
|
|
|
||
|
|
if (!(mVolumeSlide & 0xF))
|
||
|
|
{
|
||
|
|
vcptr->mVolume += (mVolumeSlide >> 4);
|
||
|
|
}
|
||
|
|
if (!(mVolumeSlide >> 4))
|
||
|
|
{
|
||
|
|
vcptr->mVolume -= (mVolumeSlide & 0xF);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (vcptr->mVolume > 64)
|
||
|
|
{
|
||
|
|
vcptr->mVolume = 64;
|
||
|
|
}
|
||
|
|
if (vcptr->mVolume < 0)
|
||
|
|
{
|
||
|
|
vcptr->mVolume = 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
vcptr->mNoteControl |= FMUSIC_VOLUME;
|
||
|
|
|
||
|
|
return FMOD_OK;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/*
|
||
|
|
[
|
||
|
|
[DESCRIPTION]
|
||
|
|
|
||
|
|
[PARAMETERS]
|
||
|
|
|
||
|
|
[RETURN_VALUE]
|
||
|
|
|
||
|
|
[REMARKS]
|
||
|
|
|
||
|
|
[SEE_ALSO]
|
||
|
|
]
|
||
|
|
*/
|
||
|
|
FMOD_RESULT MusicChannelS3M::portamento()
|
||
|
|
{
|
||
|
|
MusicVirtualChannel *vcptr = (MusicVirtualChannel *)mVirtualChannelHead.getNext();
|
||
|
|
|
||
|
|
/*
|
||
|
|
Slide pitch down if it needs too.
|
||
|
|
*/
|
||
|
|
if (vcptr->mFrequency < mPortaTarget)
|
||
|
|
{
|
||
|
|
vcptr->mFrequency += (mPortaSpeed << 2);
|
||
|
|
if (vcptr->mFrequency > mPortaTarget)
|
||
|
|
{
|
||
|
|
vcptr->mFrequency = mPortaTarget;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
Slide pitch up if it needs too.
|
||
|
|
*/
|
||
|
|
if (vcptr->mFrequency > mPortaTarget)
|
||
|
|
{
|
||
|
|
vcptr->mFrequency -= (mPortaSpeed << 2);
|
||
|
|
if (vcptr->mFrequency < mPortaTarget)
|
||
|
|
{
|
||
|
|
vcptr->mFrequency = mPortaTarget;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
if (glissando[track])
|
||
|
|
{
|
||
|
|
}
|
||
|
|
*/
|
||
|
|
|
||
|
|
vcptr->mNoteControl |= FMUSIC_FREQ;
|
||
|
|
|
||
|
|
return FMOD_OK;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/*
|
||
|
|
[
|
||
|
|
[DESCRIPTION]
|
||
|
|
to carry out a vibrato at a certain depth and speed
|
||
|
|
|
||
|
|
[PARAMETERS]
|
||
|
|
track - the track number to do the vibrato too
|
||
|
|
|
||
|
|
[RETURN_VALUE]
|
||
|
|
|
||
|
|
[REMARKS]
|
||
|
|
AND'ing temp with 31 removes the sign bit giving the abs value
|
||
|
|
|
||
|
|
[SEE_ALSO]
|
||
|
|
]
|
||
|
|
*/
|
||
|
|
FMOD_RESULT MusicChannelS3M::vibrato()
|
||
|
|
{
|
||
|
|
int delta;
|
||
|
|
unsigned char temp;
|
||
|
|
MusicVirtualChannel *vcptr = (MusicVirtualChannel *)mVirtualChannelHead.getNext();
|
||
|
|
|
||
|
|
temp = (unsigned char)(mVibPos & 31);
|
||
|
|
|
||
|
|
switch (mWaveControl & 3)
|
||
|
|
{
|
||
|
|
case 0:
|
||
|
|
delta = gSineTable[temp]; /* sine */
|
||
|
|
break;
|
||
|
|
case 1:
|
||
|
|
temp <<= 3; /* ramp down */
|
||
|
|
if (mVibPos < 0)
|
||
|
|
{
|
||
|
|
temp=255-temp;
|
||
|
|
}
|
||
|
|
delta=temp;
|
||
|
|
break;
|
||
|
|
case 2:
|
||
|
|
delta = 255; /* square */
|
||
|
|
break;
|
||
|
|
case 3:
|
||
|
|
delta = FMOD_RAND()&255; /* random */
|
||
|
|
break;
|
||
|
|
default :
|
||
|
|
delta = 0;
|
||
|
|
break;
|
||
|
|
};
|
||
|
|
|
||
|
|
delta *= mVibDepth;
|
||
|
|
delta >>=7;
|
||
|
|
delta <<=2; /* we use 4*periods so make vibrato 4 times bigger */
|
||
|
|
|
||
|
|
if (mVibPos >= 0)
|
||
|
|
{
|
||
|
|
vcptr->mFrequencyDelta = delta;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
vcptr->mFrequencyDelta = -delta;
|
||
|
|
}
|
||
|
|
|
||
|
|
mVibPos += mVibSpeed;
|
||
|
|
if (mVibPos > 31)
|
||
|
|
{
|
||
|
|
mVibPos -= 64;
|
||
|
|
}
|
||
|
|
|
||
|
|
vcptr->mNoteControl |= FMUSIC_FREQ;
|
||
|
|
|
||
|
|
return FMOD_OK;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/*
|
||
|
|
[
|
||
|
|
[DESCRIPTION]
|
||
|
|
To carry out a tremolo at a certain depth and speed
|
||
|
|
|
||
|
|
[PARAMETERS]
|
||
|
|
|
||
|
|
[RETURN_VALUE]
|
||
|
|
|
||
|
|
[REMARKS]
|
||
|
|
|
||
|
|
[SEE_ALSO]
|
||
|
|
]
|
||
|
|
*/
|
||
|
|
FMOD_RESULT MusicChannelS3M::tremolo()
|
||
|
|
{
|
||
|
|
int delta;
|
||
|
|
unsigned char temp;
|
||
|
|
MusicVirtualChannel *vcptr = (MusicVirtualChannel *)mVirtualChannelHead.getNext();
|
||
|
|
|
||
|
|
temp = (mTremoloPosition & 31);
|
||
|
|
|
||
|
|
switch((mWaveControl >> 4) & 3)
|
||
|
|
{
|
||
|
|
case 0: delta = gSineTable[temp]; /* sine */
|
||
|
|
break;
|
||
|
|
case 1: temp <<= 3; /* ramp down */
|
||
|
|
if (mTremoloPosition < 0)
|
||
|
|
{
|
||
|
|
temp=255-temp;
|
||
|
|
}
|
||
|
|
delta=temp;
|
||
|
|
break;
|
||
|
|
case 2: delta = 255; /* square */
|
||
|
|
break;
|
||
|
|
case 3: delta = FMOD_RAND()&255; /* random */
|
||
|
|
break;
|
||
|
|
default :
|
||
|
|
delta = 0;
|
||
|
|
break;
|
||
|
|
};
|
||
|
|
|
||
|
|
delta *= mTremoloDepth;
|
||
|
|
delta >>= 6;
|
||
|
|
|
||
|
|
if (mTremoloPosition >= 0)
|
||
|
|
{
|
||
|
|
if (vcptr->mVolume+delta > 64)
|
||
|
|
{
|
||
|
|
delta = 64 - vcptr->mVolume;
|
||
|
|
}
|
||
|
|
vcptr->mVolumeDelta = delta;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
if (((short)vcptr->mVolume-delta) < 0)
|
||
|
|
{
|
||
|
|
delta = vcptr->mVolume;
|
||
|
|
}
|
||
|
|
vcptr->mVolumeDelta = -delta;
|
||
|
|
}
|
||
|
|
|
||
|
|
mTremoloPosition += mTremoloDepth;
|
||
|
|
if (mTremoloPosition > 31)
|
||
|
|
{
|
||
|
|
mTremoloPosition -=64;
|
||
|
|
}
|
||
|
|
|
||
|
|
vcptr->mNoteControl |= FMUSIC_VOLUME;
|
||
|
|
|
||
|
|
return FMOD_OK;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/*
|
||
|
|
[
|
||
|
|
[DESCRIPTION]
|
||
|
|
|
||
|
|
[PARAMETERS]
|
||
|
|
|
||
|
|
[RETURN_VALUE]
|
||
|
|
|
||
|
|
[REMARKS]
|
||
|
|
|
||
|
|
[SEE_ALSO]
|
||
|
|
]
|
||
|
|
*/
|
||
|
|
FMOD_RESULT MusicChannelS3M::fineVibrato()
|
||
|
|
{
|
||
|
|
int delta;
|
||
|
|
unsigned char temp;
|
||
|
|
MusicVirtualChannel *vcptr = (MusicVirtualChannel *)mVirtualChannelHead.getNext();
|
||
|
|
|
||
|
|
temp = (mVibPos & 31);
|
||
|
|
|
||
|
|
switch (mWaveControl & 3)
|
||
|
|
{
|
||
|
|
case 0: delta = gSineTable[temp]; /* sine */
|
||
|
|
break;
|
||
|
|
case 1: temp <<= 3; /* ramp down */
|
||
|
|
if (mVibPos < 0)
|
||
|
|
{
|
||
|
|
temp=255-temp;
|
||
|
|
}
|
||
|
|
delta=temp;
|
||
|
|
break;
|
||
|
|
case 2: delta = 255; /* square */
|
||
|
|
break;
|
||
|
|
case 3: delta = FMOD_RAND()&255; /* random */
|
||
|
|
break;
|
||
|
|
default :
|
||
|
|
delta = 0;
|
||
|
|
break;
|
||
|
|
};
|
||
|
|
|
||
|
|
delta *= mVibDepth;
|
||
|
|
delta >>=7;
|
||
|
|
|
||
|
|
if (mVibPos >= 0)
|
||
|
|
{
|
||
|
|
vcptr->mFrequencyDelta = delta;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
vcptr->mFrequencyDelta = -delta;
|
||
|
|
}
|
||
|
|
|
||
|
|
mVibPos += mVibSpeed;
|
||
|
|
if (mVibPos > 31)
|
||
|
|
{
|
||
|
|
mVibPos -= 64;
|
||
|
|
}
|
||
|
|
|
||
|
|
vcptr->mNoteControl |= FMUSIC_FREQ;
|
||
|
|
|
||
|
|
return FMOD_OK;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/*
|
||
|
|
[
|
||
|
|
[DESCRIPTION]
|
||
|
|
|
||
|
|
[PARAMETERS]
|
||
|
|
|
||
|
|
[RETURN_VALUE]
|
||
|
|
|
||
|
|
[REMARKS]
|
||
|
|
|
||
|
|
[SEE_ALSO]
|
||
|
|
]
|
||
|
|
*/
|
||
|
|
FMOD_RESULT CodecS3M::updateNote(bool audible)
|
||
|
|
{
|
||
|
|
MusicNote *current;
|
||
|
|
bool breakflag = false;
|
||
|
|
bool jumpflag = false;
|
||
|
|
int count;
|
||
|
|
|
||
|
|
/*
|
||
|
|
Point our note pointer to the correct pattern buffer, and to the
|
||
|
|
correct offset in this buffer indicated by row and number of channels
|
||
|
|
*/
|
||
|
|
current = mPattern[mOrderList[mOrder]].mData + (mRow * mNumChannels);
|
||
|
|
if (!current)
|
||
|
|
{
|
||
|
|
return FMOD_OK;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (mVisited)
|
||
|
|
{
|
||
|
|
if (mVisited[(mOrder * FMUSIC_MAXROWS) + mRow])
|
||
|
|
{
|
||
|
|
mFinished = true;
|
||
|
|
return FMOD_OK;
|
||
|
|
}
|
||
|
|
mVisited[(mOrder * FMUSIC_MAXROWS) + mRow] = true;
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
Loop through each channel in the row until we have finished
|
||
|
|
*/
|
||
|
|
for (count = 0; count < mNumChannels; count++,current++)
|
||
|
|
{
|
||
|
|
MusicChannel *cptr = 0;
|
||
|
|
MusicVirtualChannel *vcptr = 0;
|
||
|
|
MusicSample *sptr = 0;
|
||
|
|
unsigned char paramx, paramy;
|
||
|
|
int oldvolume, oldfreq;
|
||
|
|
|
||
|
|
paramx = current->mEffectParam >> 4; /* get effect param x */
|
||
|
|
paramy = current->mEffectParam & 0xF; /* get effect param y */
|
||
|
|
|
||
|
|
cptr = mMusicChannel[count];
|
||
|
|
if (cptr->mVirtualChannelHead.isEmpty())
|
||
|
|
{
|
||
|
|
vcptr = &gDummyVirtualChannel; /* no channels allocated yet */
|
||
|
|
vcptr->mSample = &gDummySample;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
vcptr = (MusicVirtualChannel *)cptr->mVirtualChannelHead.getNext();
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
First store note and instrument number if there was one
|
||
|
|
*/
|
||
|
|
if (current->mNumber)
|
||
|
|
{
|
||
|
|
cptr->mInstrument = current->mNumber - 1; /* remember the Instrument # */
|
||
|
|
|
||
|
|
// if (mInstCallback[current->mNumber] && mInstCallback[current->mNumber]->callback)
|
||
|
|
// {
|
||
|
|
// FMUSIC_CheckCallback(mod, FMUSIC_CALLBACK_INSTRUMENT, current->mNumber);
|
||
|
|
// }
|
||
|
|
}
|
||
|
|
if (current->mNote && current->mNote != FMUSIC_KEYOFF)
|
||
|
|
{
|
||
|
|
cptr->mNote = current->mNote-1; /* remember the note */
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
Set up sample pointer
|
||
|
|
*/
|
||
|
|
if (cptr->mInstrument < mNumSamples)
|
||
|
|
{
|
||
|
|
sptr = &mSample[cptr->mInstrument];
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
sptr = &gDummySample;
|
||
|
|
}
|
||
|
|
|
||
|
|
oldvolume = vcptr->mVolume;
|
||
|
|
oldfreq = vcptr->mFrequency;
|
||
|
|
|
||
|
|
/*
|
||
|
|
if there is no more tremolo, set volume to volume + last tremolo delta
|
||
|
|
*/
|
||
|
|
if (cptr->mRecentEffect == FMUSIC_S3M_TREMOLO && current->mEffect != FMUSIC_S3M_TREMOLO)
|
||
|
|
{
|
||
|
|
vcptr->mVolume += vcptr->mVolumeDelta;
|
||
|
|
}
|
||
|
|
cptr->mRecentEffect = current->mEffect;
|
||
|
|
|
||
|
|
vcptr->mVolumeDelta = 0;
|
||
|
|
vcptr->mNoteControl = 0;
|
||
|
|
|
||
|
|
/*
|
||
|
|
PROCESS NOTE
|
||
|
|
*/
|
||
|
|
if (current->mNote && current->mNote != FMUSIC_KEYOFF)
|
||
|
|
{
|
||
|
|
if (vcptr == &gDummyVirtualChannel)
|
||
|
|
{
|
||
|
|
FMOD_RESULT result;
|
||
|
|
|
||
|
|
result = spawnNewVirtualChannel(cptr, sptr, &vcptr);
|
||
|
|
if (result != FMOD_OK)
|
||
|
|
{
|
||
|
|
vcptr = &gDummyVirtualChannel; /* no channels allocated yet */
|
||
|
|
vcptr->mSample = &gDummySample;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
get period according to relative note, c2spd and finetune
|
||
|
|
*/
|
||
|
|
cptr->mNote = current->mNote-1; /* now remember the note */
|
||
|
|
if (sptr->mMiddleC)
|
||
|
|
{
|
||
|
|
cptr->mPeriod = 8363L * gPeriodTable[cptr->mNote] / sptr->mMiddleC;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
cptr->mPeriod = gPeriodTable[cptr->mNote];
|
||
|
|
}
|
||
|
|
|
||
|
|
vcptr->mPan = mDefaultPan[count];
|
||
|
|
|
||
|
|
/*
|
||
|
|
Frequency only changes if there are no portamento effects
|
||
|
|
*/
|
||
|
|
if (current->mEffect != FMUSIC_S3M_PORTATO &&
|
||
|
|
current->mEffect != FMUSIC_S3M_PORTATOVOLSLIDE)
|
||
|
|
{
|
||
|
|
vcptr->mFrequency = cptr->mPeriod;
|
||
|
|
}
|
||
|
|
|
||
|
|
vcptr->mNoteControl = FMUSIC_TRIGGER;
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
PROCESS INSTRUMENT NUMBER
|
||
|
|
*/
|
||
|
|
if (current->mNumber)
|
||
|
|
{
|
||
|
|
vcptr->mVolume = sptr->mDefaultVolume;
|
||
|
|
cptr->mTremorPosition = 0; /* retrigger tremor count */
|
||
|
|
|
||
|
|
/*
|
||
|
|
Retrigger tremolo and vibrato waveforms
|
||
|
|
*/
|
||
|
|
if ((cptr->mWaveControl & 0xF) < 4)
|
||
|
|
{
|
||
|
|
cptr->mVibPos=0;
|
||
|
|
}
|
||
|
|
if ((cptr->mWaveControl >> 4) < 4)
|
||
|
|
{
|
||
|
|
cptr->mTremoloPosition = 0;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
vcptr->mFrequencyDelta = 0;
|
||
|
|
vcptr->mNoteControl |= FMUSIC_FREQ | FMUSIC_VOLUME | FMUSIC_PAN;
|
||
|
|
|
||
|
|
/*
|
||
|
|
PROCESS VOLUME BYTE
|
||
|
|
*/
|
||
|
|
if (current->mVolume)
|
||
|
|
{
|
||
|
|
vcptr->mVolume = current->mVolume - 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
PROCESS KEY OFF
|
||
|
|
*/
|
||
|
|
if (current->mNote == FMUSIC_KEYOFF)
|
||
|
|
{
|
||
|
|
vcptr->mVolume = 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
TICK 0 EFFECTS
|
||
|
|
*/
|
||
|
|
switch (current->mEffect)
|
||
|
|
{
|
||
|
|
case FMUSIC_S3M_SETSPEED :
|
||
|
|
{
|
||
|
|
if (current->mEffectParam)
|
||
|
|
{
|
||
|
|
mSpeed = current->mEffectParam;
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
case FMUSIC_S3M_PATTERNJUMP : /* --- 00 B00 : --- 00 D63 , should put us at ord=0, row=63 */
|
||
|
|
{
|
||
|
|
mNextOrder = current->mEffectParam;
|
||
|
|
mNextRow = 0;
|
||
|
|
|
||
|
|
if (mNextOrder >= mNumOrders)
|
||
|
|
{
|
||
|
|
mNextOrder = 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
jumpflag = 1;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
case FMUSIC_S3M_PATTERNBREAK :
|
||
|
|
{
|
||
|
|
mNextRow = (paramx*10) + paramy;
|
||
|
|
|
||
|
|
if (mNextRow > 63)
|
||
|
|
{
|
||
|
|
mNextRow = 0;
|
||
|
|
}
|
||
|
|
if (!breakflag && !jumpflag)
|
||
|
|
{
|
||
|
|
mNextOrder = mOrder + 1;
|
||
|
|
}
|
||
|
|
if (mNextOrder >= mNumOrders)
|
||
|
|
{
|
||
|
|
mNextOrder=0;
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
case FMUSIC_S3M_VOLUMESLIDE :
|
||
|
|
{
|
||
|
|
/*
|
||
|
|
Dxy - Volume slide, fine vol DFx = slide down, DxF = slide up
|
||
|
|
*/
|
||
|
|
if (current->mEffectParam)
|
||
|
|
{
|
||
|
|
cptr->mVolumeSlide = current->mEffectParam;
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
DFF is classed as a slide up so it gets priority
|
||
|
|
*/
|
||
|
|
if ((cptr->mVolumeSlide &0xF) == 0xF)
|
||
|
|
{
|
||
|
|
vcptr->mVolume += (cptr->mVolumeSlide >> 4);
|
||
|
|
}
|
||
|
|
else if ((cptr->mVolumeSlide >>4 ) == 0xF)
|
||
|
|
{
|
||
|
|
vcptr->mVolume -= (cptr->mVolumeSlide & 0xF);
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
Perform an extra slide if using old fast vol slides!
|
||
|
|
*/
|
||
|
|
if (mMusicFlags == FMUSIC_FLAGS_FASTVOLUMESLIDES)
|
||
|
|
{
|
||
|
|
if (!(cptr->mVolumeSlide & 0xF))
|
||
|
|
{
|
||
|
|
vcptr->mVolume +=(cptr->mVolumeSlide >> 4);
|
||
|
|
}
|
||
|
|
if (!(cptr->mVolumeSlide >> 4))
|
||
|
|
{
|
||
|
|
vcptr->mVolume -=(cptr->mVolumeSlide & 0xF);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (vcptr->mVolume > 64)
|
||
|
|
{
|
||
|
|
vcptr->mVolume = 64;
|
||
|
|
}
|
||
|
|
if (vcptr->mVolume < 0)
|
||
|
|
{
|
||
|
|
vcptr->mVolume = 0;
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
case FMUSIC_S3M_PORTADOWN :
|
||
|
|
{
|
||
|
|
if (current->mEffectParam)
|
||
|
|
{
|
||
|
|
cptr->mPortaUpDown = current->mEffectParam;
|
||
|
|
}
|
||
|
|
|
||
|
|
if ((cptr->mPortaUpDown >> 4)==0xF)
|
||
|
|
{
|
||
|
|
vcptr->mFrequency += ((cptr->mPortaUpDown & 0xF) << 2);
|
||
|
|
}
|
||
|
|
if ((cptr->mPortaUpDown >> 4)==0xE)
|
||
|
|
{
|
||
|
|
vcptr->mFrequency += (cptr->mPortaUpDown & 0xF);
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
case FMUSIC_S3M_PORTAUP :
|
||
|
|
{
|
||
|
|
if (current->mEffectParam)
|
||
|
|
{
|
||
|
|
cptr->mPortaUpDown = current->mEffectParam;
|
||
|
|
}
|
||
|
|
|
||
|
|
if ((cptr->mPortaUpDown >>4)==0xF)
|
||
|
|
{
|
||
|
|
vcptr->mFrequency -= ((cptr->mPortaUpDown & 0xF) << 2);
|
||
|
|
}
|
||
|
|
if ((cptr->mPortaUpDown >>4)==0xE)
|
||
|
|
{
|
||
|
|
vcptr->mFrequency -= (cptr->mPortaUpDown & 0xF);
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
case FMUSIC_S3M_PORTATO :
|
||
|
|
{
|
||
|
|
if (current->mEffectParam)
|
||
|
|
{
|
||
|
|
cptr->mPortaSpeed = current->mEffectParam;
|
||
|
|
}
|
||
|
|
|
||
|
|
cptr->mPortaTarget = cptr->mPeriod;
|
||
|
|
vcptr->mNoteControl &= ~FMUSIC_TRIGGER;
|
||
|
|
vcptr->mNoteControl &= ~FMUSIC_FREQ;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
case FMUSIC_S3M_VIBRATO :
|
||
|
|
{
|
||
|
|
if (paramx)
|
||
|
|
{
|
||
|
|
cptr->mVibSpeed = paramx;
|
||
|
|
}
|
||
|
|
if (paramy)
|
||
|
|
{
|
||
|
|
cptr->mVibDepth = paramy;
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
case FMUSIC_S3M_TREMOR :
|
||
|
|
{
|
||
|
|
if (current->mEffectParam)
|
||
|
|
{
|
||
|
|
cptr->mTremorOn = (paramx+1);
|
||
|
|
cptr->mTremorOff = (paramy+1);
|
||
|
|
}
|
||
|
|
if (cptr->mTremorPosition >= cptr->mTremorOn)
|
||
|
|
{
|
||
|
|
vcptr->mVolumeDelta = -vcptr->mVolume;
|
||
|
|
}
|
||
|
|
cptr->mTremorPosition++;
|
||
|
|
if (cptr->mTremorPosition >= (cptr->mTremorOn + cptr->mTremorOff))
|
||
|
|
{
|
||
|
|
cptr->mTremorPosition = 0;
|
||
|
|
}
|
||
|
|
vcptr->mNoteControl |= FMUSIC_VOLUME;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
case FMUSIC_S3M_PORTATOVOLSLIDE :
|
||
|
|
{
|
||
|
|
if (current->mEffectParam)
|
||
|
|
{
|
||
|
|
cptr->mVolumeSlide = current->mEffectParam;
|
||
|
|
}
|
||
|
|
cptr->mPortaTarget = cptr->mPeriod;
|
||
|
|
vcptr->mNoteControl &= ~FMUSIC_TRIGGER;
|
||
|
|
vcptr->mNoteControl &= ~FMUSIC_FREQ;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
case FMUSIC_S3M_VIBRATOVOLSLIDE :
|
||
|
|
{
|
||
|
|
if (current->mEffectParam)
|
||
|
|
{
|
||
|
|
cptr->mVolumeSlide = current->mEffectParam;
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
case FMUSIC_S3M_ARPEGGIO :
|
||
|
|
{
|
||
|
|
if (current->mEffectParam)
|
||
|
|
{
|
||
|
|
cptr->mArpeggio = current->mEffectParam;
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
case FMUSIC_S3M_SETSAMPLEOFFSET :
|
||
|
|
{
|
||
|
|
unsigned int offset;
|
||
|
|
|
||
|
|
offset = (int)(current->mEffectParam) << 8;
|
||
|
|
|
||
|
|
if (offset >= sptr->mLoopStart + sptr->mLoopLength)
|
||
|
|
{
|
||
|
|
vcptr->mNoteControl &= ~FMUSIC_TRIGGER;
|
||
|
|
vcptr->mNoteControl |= FMUSIC_STOP;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
vcptr->mSampleOffset = offset;
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
case FMUSIC_S3M_RETRIGVOLSLIDE :
|
||
|
|
{
|
||
|
|
if (current->mEffectParam)
|
||
|
|
{
|
||
|
|
cptr->mRetrigX = paramx;
|
||
|
|
cptr->mRetrigY = paramy;
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
case FMUSIC_S3M_TREMOLO :
|
||
|
|
{
|
||
|
|
if (paramx)
|
||
|
|
{
|
||
|
|
cptr->mTremoloDepth = paramx;
|
||
|
|
}
|
||
|
|
if (paramy)
|
||
|
|
{
|
||
|
|
cptr->mTremoloDepth = paramy;
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
case FMUSIC_S3M_SPECIAL :
|
||
|
|
{
|
||
|
|
switch (paramx)
|
||
|
|
{
|
||
|
|
case FMUSIC_S3M_SETFILTER :
|
||
|
|
case FMUSIC_S3M_NOTECUT :
|
||
|
|
case FMUSIC_S3M_FUNKREPEAT :
|
||
|
|
{
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
case FMUSIC_S3M_SETGLISSANDO :
|
||
|
|
{
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
case FMUSIC_S3M_SETFINETUNE :
|
||
|
|
{
|
||
|
|
fineTune2Hz(paramy, &sptr->mMiddleC);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
case FMUSIC_S3M_SETVIBRATOWAVE :
|
||
|
|
{
|
||
|
|
cptr->mWaveControl &= 0xF0;
|
||
|
|
cptr->mWaveControl |= paramy;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
case FMUSIC_S3M_SETTREMOLOWAVE :
|
||
|
|
{
|
||
|
|
cptr->mWaveControl &= 0xF;
|
||
|
|
cptr->mWaveControl |= (paramy<<4);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
case FMUSIC_S3M_SETPANPOSITION16 :
|
||
|
|
{
|
||
|
|
vcptr->mPan = paramy<<4;
|
||
|
|
vcptr->mNoteControl |= FMUSIC_PAN;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
case FMUSIC_S3M_STEREOCONTROL :
|
||
|
|
{
|
||
|
|
if (paramy > 7)
|
||
|
|
{
|
||
|
|
paramy-=8;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
paramy+=8;
|
||
|
|
}
|
||
|
|
|
||
|
|
vcptr->mPan = paramy<<4;
|
||
|
|
vcptr->mNoteControl |= FMUSIC_PAN;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
case FMUSIC_S3M_PATTERNLOOP :
|
||
|
|
{
|
||
|
|
if (paramy == 0)
|
||
|
|
{
|
||
|
|
cptr->mPatternLoopRow = mRow;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
if (!cptr->mPatternLoopNumber)
|
||
|
|
{
|
||
|
|
cptr->mPatternLoopNumber = paramy;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
cptr->mPatternLoopNumber--;
|
||
|
|
}
|
||
|
|
if (cptr->mPatternLoopNumber)
|
||
|
|
{
|
||
|
|
int count2;
|
||
|
|
|
||
|
|
mNextRow = cptr->mPatternLoopRow;
|
||
|
|
|
||
|
|
if (mVisited)
|
||
|
|
{
|
||
|
|
for (count2 = cptr->mPatternLoopRow; count2 <= mRow; count2++)
|
||
|
|
{
|
||
|
|
mVisited[(mOrder * FMUSIC_MAXROWS) + count2] = false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
case FMUSIC_S3M_NOTEDELAY :
|
||
|
|
{
|
||
|
|
vcptr->mVolume = oldvolume;
|
||
|
|
vcptr->mFrequency = oldfreq;
|
||
|
|
vcptr->mNoteControl &= ~FMUSIC_FREQ;
|
||
|
|
vcptr->mNoteControl &= ~FMUSIC_PAN;
|
||
|
|
vcptr->mNoteControl &= ~FMUSIC_VOLUME;
|
||
|
|
vcptr->mNoteControl &= ~FMUSIC_TRIGGER;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
case FMUSIC_S3M_PATTERNDELAY :
|
||
|
|
{
|
||
|
|
mPatternDelay = paramy;
|
||
|
|
mPatternDelay *= mSpeed;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
};
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
case FMUSIC_S3M_FINEVIBRATO :
|
||
|
|
{
|
||
|
|
if (paramx)
|
||
|
|
{
|
||
|
|
cptr->mVibSpeed = paramx;
|
||
|
|
}
|
||
|
|
if (paramy)
|
||
|
|
{
|
||
|
|
cptr->mVibDepth = paramy;
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
case FMUSIC_S3M_GLOBALVOLUME :
|
||
|
|
{
|
||
|
|
mGlobalVolume = current->mEffectParam;
|
||
|
|
if (mGlobalVolume > 64)
|
||
|
|
{
|
||
|
|
mGlobalVolume = 64;
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
case FMUSIC_S3M_SETTEMPO :
|
||
|
|
{
|
||
|
|
if (current->mEffectParam >= 0x20)
|
||
|
|
{
|
||
|
|
setBPM(current->mEffectParam);
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
case FMUSIC_S3M_SETPAN :
|
||
|
|
{
|
||
|
|
vcptr->mPan = current->mEffectParam*2;
|
||
|
|
vcptr->mNoteControl |= FMUSIC_PAN;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
case FMUSIC_S3M_Z :
|
||
|
|
{
|
||
|
|
// FMUSIC_CheckCallback(mod, FMUSIC_CALLBACK_ZXX, current->mEffectParam);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
};
|
||
|
|
|
||
|
|
if (audible)
|
||
|
|
{
|
||
|
|
vcptr = (MusicVirtualChannel *)cptr->mVirtualChannelHead.getNext();
|
||
|
|
|
||
|
|
if (!(vcptr->mFrequency+vcptr->mFrequencyDelta))
|
||
|
|
{
|
||
|
|
vcptr->mNoteControl &= ~FMUSIC_FREQ;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (vcptr->mNoteControl & FMUSIC_TRIGGER)
|
||
|
|
{
|
||
|
|
playSound(sptr, vcptr, false);
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
Volume = 6bit, global = 6bit.. halve volume for clipping 1 bit
|
||
|
|
*/
|
||
|
|
if (vcptr->mNoteControl & FMUSIC_VOLUME)
|
||
|
|
{
|
||
|
|
vcptr->mChannel.setVolume((float)((vcptr->mVolume + vcptr->mVolumeDelta) * mGlobalVolume) / (64.0f * 64.0f) * 0.5f * cptr->mMasterVolume);
|
||
|
|
}
|
||
|
|
if (vcptr->mNoteControl & FMUSIC_PAN)
|
||
|
|
{
|
||
|
|
float finalpan = ((float)vcptr->mPan - 128.0f) * mPanSeparation;
|
||
|
|
|
||
|
|
vcptr->mChannel.setPan(finalpan / 128.0f);
|
||
|
|
}
|
||
|
|
if (vcptr->mNoteControl & FMUSIC_FREQ)
|
||
|
|
{
|
||
|
|
int finalfreq = vcptr->mFrequency + vcptr->mFrequencyDelta;
|
||
|
|
|
||
|
|
if (finalfreq < 1)
|
||
|
|
{
|
||
|
|
finalfreq = 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
vcptr->mChannel.setFrequency((float)period2HZ(finalfreq));
|
||
|
|
}
|
||
|
|
if (vcptr->mNoteControl & FMUSIC_STOP)
|
||
|
|
{
|
||
|
|
vcptr->mChannel.stopEx(CHANNELI_STOPFLAG_RESETCALLBACKS);
|
||
|
|
#ifdef FMOD_SUPPORT_SOFTWARE
|
||
|
|
mSystem->flushDSPConnectionRequests();
|
||
|
|
#endif
|
||
|
|
vcptr->mSampleOffset = 0; /* if this channel gets stolen it will be safe */
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return FMOD_OK;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/*
|
||
|
|
[
|
||
|
|
[DESCRIPTION]
|
||
|
|
|
||
|
|
[PARAMETERS]
|
||
|
|
|
||
|
|
[RETURN_VALUE]
|
||
|
|
|
||
|
|
[REMARKS]
|
||
|
|
|
||
|
|
[SEE_ALSO]
|
||
|
|
]
|
||
|
|
*/
|
||
|
|
FMOD_RESULT CodecS3M::updateEffects()
|
||
|
|
{
|
||
|
|
MusicNote *current;
|
||
|
|
int count;
|
||
|
|
|
||
|
|
current = mPattern[mOrderList[mOrder]].mData + (mRow * mNumChannels);
|
||
|
|
|
||
|
|
if (!current)
|
||
|
|
{
|
||
|
|
return FMOD_OK;
|
||
|
|
}
|
||
|
|
|
||
|
|
for (count=0; count < mNumChannels; count++,current++)
|
||
|
|
{
|
||
|
|
MusicChannelS3M *cptr = 0;
|
||
|
|
MusicVirtualChannel *vcptr = 0;
|
||
|
|
MusicSample *sptr = 0;
|
||
|
|
unsigned char effect, paramx, paramy;
|
||
|
|
|
||
|
|
cptr = (MusicChannelS3M *)mMusicChannel[count];
|
||
|
|
|
||
|
|
if (cptr->mInstrument < mNumSamples)
|
||
|
|
{
|
||
|
|
sptr = &mSample[cptr->mInstrument];
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
sptr = &gDummySample;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (cptr->mVirtualChannelHead.isEmpty())
|
||
|
|
{
|
||
|
|
vcptr = &gDummyVirtualChannel; /* no channels allocated yet */
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
vcptr = (MusicVirtualChannel *)cptr->mVirtualChannelHead.getNext();
|
||
|
|
}
|
||
|
|
|
||
|
|
effect = current->mEffect; /* grab the effect number */
|
||
|
|
paramx = current->mEffectParam >> 4; /* grab the effect parameter x */
|
||
|
|
paramy = current->mEffectParam & 0xF; /* grab the effect parameter y */
|
||
|
|
|
||
|
|
vcptr->mVolumeDelta = 0; /* this is for tremolo etc */
|
||
|
|
vcptr->mFrequencyDelta = 0; /* this is for vibrato / arpeggio etc */
|
||
|
|
vcptr->mNoteControl = 0;
|
||
|
|
|
||
|
|
switch (effect)
|
||
|
|
{
|
||
|
|
case FMUSIC_S3M_VOLUMESLIDE :
|
||
|
|
{
|
||
|
|
cptr->volumeSlide();
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
case FMUSIC_S3M_PORTADOWN :
|
||
|
|
{
|
||
|
|
if (cptr->mPortaUpDown <0xE0)
|
||
|
|
{
|
||
|
|
vcptr->mFrequency += (cptr->mPortaUpDown << 2);
|
||
|
|
}
|
||
|
|
vcptr->mNoteControl |= FMUSIC_FREQ;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
case FMUSIC_S3M_PORTAUP :
|
||
|
|
{
|
||
|
|
if (cptr->mPortaUpDown <0xE0)
|
||
|
|
{
|
||
|
|
vcptr->mFrequency -= (cptr->mPortaUpDown << 2);
|
||
|
|
if (vcptr->mFrequency < 1)
|
||
|
|
{
|
||
|
|
vcptr->mNoteControl |= FMUSIC_STOP;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
vcptr->mNoteControl |= FMUSIC_FREQ;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
case FMUSIC_S3M_PORTATO :
|
||
|
|
{
|
||
|
|
cptr->portamento();
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
case FMUSIC_S3M_VIBRATO :
|
||
|
|
{
|
||
|
|
cptr->vibrato();
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
case FMUSIC_S3M_TREMOR :
|
||
|
|
{
|
||
|
|
if (cptr->mTremorPosition >= cptr->mTremorOn)
|
||
|
|
{
|
||
|
|
vcptr->mVolumeDelta = -vcptr->mVolume;
|
||
|
|
}
|
||
|
|
|
||
|
|
cptr->mTremorPosition++;
|
||
|
|
if (cptr->mTremorPosition >= cptr->mTremorOn + cptr->mTremorOff)
|
||
|
|
{
|
||
|
|
cptr->mTremorPosition = 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
vcptr->mNoteControl |= FMUSIC_VOLUME;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
case FMUSIC_S3M_ARPEGGIO :
|
||
|
|
{
|
||
|
|
if (cptr->mArpeggio > 0)
|
||
|
|
{
|
||
|
|
paramx = cptr->mArpeggio >> 4;
|
||
|
|
paramy = cptr->mArpeggio & 0xF;
|
||
|
|
|
||
|
|
switch (mTick % 3)
|
||
|
|
{
|
||
|
|
case 1:
|
||
|
|
{
|
||
|
|
if (sptr->mMiddleC)
|
||
|
|
{
|
||
|
|
vcptr->mFrequencyDelta = (8363L * gPeriodTable[cptr->mNote+paramx] / sptr->mMiddleC) - (8363L * gPeriodTable[cptr->mNote] / sptr->mMiddleC);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
vcptr->mFrequencyDelta = gPeriodTable[cptr->mNote+paramx] - gPeriodTable[cptr->mNote];
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
case 2:
|
||
|
|
{
|
||
|
|
if (sptr->mMiddleC)
|
||
|
|
{
|
||
|
|
vcptr->mFrequencyDelta = (8363L * gPeriodTable[cptr->mNote+paramy] / sptr->mMiddleC) - (8363L * gPeriodTable[cptr->mNote] / sptr->mMiddleC);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
vcptr->mFrequencyDelta = gPeriodTable[cptr->mNote+paramy] - gPeriodTable[cptr->mNote];
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
vcptr->mNoteControl |= FMUSIC_FREQ;
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
case FMUSIC_S3M_VIBRATOVOLSLIDE :
|
||
|
|
{
|
||
|
|
cptr->vibrato();
|
||
|
|
cptr->volumeSlide();
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
case FMUSIC_S3M_PORTATOVOLSLIDE :
|
||
|
|
{
|
||
|
|
cptr->portamento();
|
||
|
|
cptr->volumeSlide();
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
case FMUSIC_S3M_RETRIGVOLSLIDE :
|
||
|
|
{
|
||
|
|
if (!cptr->mRetrigY)
|
||
|
|
{
|
||
|
|
break; /* divide by 0 bugfix */
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!(mTick % cptr->mRetrigY))
|
||
|
|
{
|
||
|
|
if (cptr->mRetrigX)
|
||
|
|
{
|
||
|
|
switch (cptr->mRetrigX)
|
||
|
|
{
|
||
|
|
case 1: vcptr->mVolume--;
|
||
|
|
break;
|
||
|
|
case 2: vcptr->mVolume -= 2;
|
||
|
|
break;
|
||
|
|
case 3: vcptr->mVolume -= 4;
|
||
|
|
break;
|
||
|
|
case 4: vcptr->mVolume -= 8;
|
||
|
|
break;
|
||
|
|
case 5: vcptr->mVolume -= 16;
|
||
|
|
break;
|
||
|
|
case 6: vcptr->mVolume *= 2/3;
|
||
|
|
break;
|
||
|
|
case 7: vcptr->mVolume >>= 1;
|
||
|
|
break;
|
||
|
|
case 8: /* ? */
|
||
|
|
break;
|
||
|
|
case 9: vcptr->mVolume++;
|
||
|
|
break;
|
||
|
|
case 0xA: vcptr->mVolume += 2;
|
||
|
|
break;
|
||
|
|
case 0xB: vcptr->mVolume += 4;
|
||
|
|
break;
|
||
|
|
case 0xC: vcptr->mVolume += 8;
|
||
|
|
break;
|
||
|
|
case 0xD: vcptr->mVolume += 16;
|
||
|
|
break;
|
||
|
|
case 0xE: vcptr->mVolume *= 3/2;
|
||
|
|
break;
|
||
|
|
case 0xF: vcptr->mVolume <<= 1;
|
||
|
|
break;
|
||
|
|
};
|
||
|
|
if (vcptr->mVolume > 64)
|
||
|
|
{
|
||
|
|
vcptr->mVolume = 64;
|
||
|
|
}
|
||
|
|
if (vcptr->mVolume < 0)
|
||
|
|
{
|
||
|
|
vcptr->mVolume = 0;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
vcptr->mPan = mDefaultPan[count];
|
||
|
|
vcptr->mFrequency = cptr->mPeriod;
|
||
|
|
vcptr->mFrequencyDelta= 0;
|
||
|
|
|
||
|
|
vcptr->mNoteControl |= FMUSIC_VOLUME;
|
||
|
|
vcptr->mNoteControl |= FMUSIC_PAN;
|
||
|
|
vcptr->mNoteControl |= FMUSIC_FREQ;
|
||
|
|
vcptr->mNoteControl |= FMUSIC_TRIGGER;
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
case FMUSIC_S3M_TREMOLO :
|
||
|
|
{
|
||
|
|
cptr->tremolo();
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
case FMUSIC_S3M_SPECIAL :
|
||
|
|
{
|
||
|
|
switch (paramx)
|
||
|
|
{
|
||
|
|
case FMUSIC_S3M_NOTECUT:
|
||
|
|
{
|
||
|
|
if (mTick==paramy)
|
||
|
|
{
|
||
|
|
vcptr->mVolume = 0;
|
||
|
|
vcptr->mNoteControl |= FMUSIC_VOLUME;
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
case FMUSIC_S3M_NOTEDELAY :
|
||
|
|
{
|
||
|
|
if (mTick == paramy)
|
||
|
|
{
|
||
|
|
if (vcptr == &gDummyVirtualChannel)
|
||
|
|
{
|
||
|
|
FMOD_RESULT result;
|
||
|
|
|
||
|
|
result = spawnNewVirtualChannel(cptr, sptr, &vcptr);
|
||
|
|
if (result != FMOD_OK)
|
||
|
|
{
|
||
|
|
vcptr = &gDummyVirtualChannel; /* no channels allocated yet */
|
||
|
|
vcptr->mSample = &gDummySample;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (current->mNumber)
|
||
|
|
{
|
||
|
|
vcptr->mVolume = sptr->mDefaultVolume;
|
||
|
|
/*
|
||
|
|
Retrigger tremolo and vibrato waveforms
|
||
|
|
*/
|
||
|
|
if ((cptr->mWaveControl & 0xF) < 4)
|
||
|
|
{
|
||
|
|
cptr->mVibPos = 0;
|
||
|
|
}
|
||
|
|
if ((cptr->mWaveControl >> 4) < 4)
|
||
|
|
{
|
||
|
|
cptr->mTremoloPosition = 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
cptr->mTremorPosition = 0; /* retrigger tremor count */
|
||
|
|
|
||
|
|
vcptr->mNoteControl |= FMUSIC_VOLUME;
|
||
|
|
}
|
||
|
|
|
||
|
|
vcptr->mPan = mDefaultPan[count];
|
||
|
|
vcptr->mFrequency = cptr->mPeriod;
|
||
|
|
vcptr->mFrequencyDelta= 0;
|
||
|
|
|
||
|
|
vcptr->mNoteControl |= FMUSIC_FREQ;
|
||
|
|
vcptr->mNoteControl |= FMUSIC_PAN;
|
||
|
|
if (current->mVolume)
|
||
|
|
{
|
||
|
|
vcptr->mVolume = current->mVolume - 1;
|
||
|
|
vcptr->mNoteControl |= FMUSIC_VOLUME;
|
||
|
|
}
|
||
|
|
vcptr->mNoteControl |= FMUSIC_TRIGGER;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
vcptr->mNoteControl &= ~FMUSIC_VOLUME;
|
||
|
|
vcptr->mNoteControl &= ~FMUSIC_FREQ;
|
||
|
|
vcptr->mNoteControl &= ~FMUSIC_PAN;
|
||
|
|
vcptr->mNoteControl &= ~FMUSIC_TRIGGER;
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
};
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
case FMUSIC_S3M_FINEVIBRATO :
|
||
|
|
{
|
||
|
|
cptr->fineVibrato();
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
};
|
||
|
|
|
||
|
|
{
|
||
|
|
vcptr = (MusicVirtualChannel *)cptr->mVirtualChannelHead.getNext();
|
||
|
|
|
||
|
|
if (!(vcptr->mFrequency+vcptr->mFrequencyDelta))
|
||
|
|
{
|
||
|
|
vcptr->mNoteControl &= ~FMUSIC_FREQ;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (vcptr->mNoteControl & FMUSIC_TRIGGER)
|
||
|
|
{
|
||
|
|
playSound(sptr, vcptr, false);
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
volume = 6bit, global = 6bit.. halve volume for clipping 1 bit
|
||
|
|
*/
|
||
|
|
if (vcptr->mNoteControl & FMUSIC_VOLUME)
|
||
|
|
{
|
||
|
|
vcptr->mChannel.setVolume((float)((vcptr->mVolume + vcptr->mVolumeDelta) * mGlobalVolume) / (64.0f * 64.0f) * 0.5f * cptr->mMasterVolume);
|
||
|
|
}
|
||
|
|
if (vcptr->mNoteControl & FMUSIC_PAN)
|
||
|
|
{
|
||
|
|
float finalpan = ((float)vcptr->mPan - 128.0f) * mPanSeparation;
|
||
|
|
|
||
|
|
vcptr->mChannel.setPan(finalpan / 128.0f);
|
||
|
|
}
|
||
|
|
if (vcptr->mNoteControl & FMUSIC_FREQ)
|
||
|
|
{
|
||
|
|
int finalfreq = vcptr->mFrequency + vcptr->mFrequencyDelta;
|
||
|
|
|
||
|
|
if (finalfreq < 1)
|
||
|
|
{
|
||
|
|
finalfreq = 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
vcptr->mChannel.setFrequency((float)period2HZ(finalfreq));
|
||
|
|
}
|
||
|
|
if (vcptr->mNoteControl & FMUSIC_STOP)
|
||
|
|
{
|
||
|
|
vcptr->mChannel.stopEx(CHANNELI_STOPFLAG_RESETCALLBACKS);
|
||
|
|
#ifdef FMOD_SUPPORT_SOFTWARE
|
||
|
|
mSystem->flushDSPConnectionRequests();
|
||
|
|
#endif
|
||
|
|
vcptr->mSampleOffset = 0; /* if this channel gets stolen it will be safe */
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return FMOD_OK;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/*
|
||
|
|
[
|
||
|
|
[DESCRIPTION]
|
||
|
|
|
||
|
|
[PARAMETERS]
|
||
|
|
|
||
|
|
[RETURN_VALUE]
|
||
|
|
|
||
|
|
[REMARKS]
|
||
|
|
|
||
|
|
[SEE_ALSO]
|
||
|
|
]
|
||
|
|
*/
|
||
|
|
FMOD_RESULT CodecS3M::update(bool audible)
|
||
|
|
{
|
||
|
|
if (mTick == 0) /* new note */
|
||
|
|
{
|
||
|
|
if (mFinished && !mLooping)
|
||
|
|
{
|
||
|
|
stop();
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
/*
|
||
|
|
Process any commands to set the next order/row from previous row
|
||
|
|
*/
|
||
|
|
if (mNextOrder >= 0)
|
||
|
|
{
|
||
|
|
mOrder = mNextOrder;
|
||
|
|
// checkCallback(FMUSIC_CALLBACK_ORDER, (unsigned char)mOrder);
|
||
|
|
if (mNextOrder >= 0)
|
||
|
|
{
|
||
|
|
mOrder = mNextOrder;
|
||
|
|
}
|
||
|
|
mNextOrder = -1;
|
||
|
|
}
|
||
|
|
if (mNextRow >= 0)
|
||
|
|
{
|
||
|
|
mRow = mNextRow;
|
||
|
|
// checkCallback(FMUSIC_CALLBACK_ROW, (unsigned char)mRow);
|
||
|
|
if (mNextRow >= 0)
|
||
|
|
{
|
||
|
|
mRow = mNextRow;
|
||
|
|
}
|
||
|
|
mNextRow = -1;
|
||
|
|
}
|
||
|
|
|
||
|
|
updateNote(audible); /* Update and play the note */
|
||
|
|
|
||
|
|
/*
|
||
|
|
If there were no row commands
|
||
|
|
*/
|
||
|
|
if (mNextRow == -1)
|
||
|
|
{
|
||
|
|
mNextRow = mRow+1;
|
||
|
|
if (mNextRow >= 64) /* if end of pattern */
|
||
|
|
{
|
||
|
|
mNextOrder = mOrder+1; /* so increment the order */
|
||
|
|
if (mNextOrder >= mNumOrders)
|
||
|
|
{
|
||
|
|
mNextOrder = mRestart;
|
||
|
|
}
|
||
|
|
|
||
|
|
mNextRow = 0; /* start at top of pattern */
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
else if (audible)
|
||
|
|
{
|
||
|
|
updateEffects(); /* Else update the inbetween row effects */
|
||
|
|
}
|
||
|
|
|
||
|
|
mTick++;
|
||
|
|
if (mTick >= mSpeed + mPatternDelay)
|
||
|
|
{
|
||
|
|
mPatternDelay = 0;
|
||
|
|
mTick = 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
mPCMOffset += mMixerSamplesPerTick;
|
||
|
|
|
||
|
|
return FMOD_OK;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/*
|
||
|
|
[
|
||
|
|
[DESCRIPTION]
|
||
|
|
|
||
|
|
[PARAMETERS]
|
||
|
|
|
||
|
|
[RETURN_VALUE]
|
||
|
|
|
||
|
|
[REMARKS]
|
||
|
|
|
||
|
|
[PLATFORMS]
|
||
|
|
Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube
|
||
|
|
|
||
|
|
[SEE_ALSO]
|
||
|
|
]
|
||
|
|
*/
|
||
|
|
FMOD_RESULT CodecS3M::openInternal(FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo)
|
||
|
|
{
|
||
|
|
unsigned char remap[32], st3pan, mastervol, temp[4];
|
||
|
|
unsigned short temp16, songlength, filenumpatterns;
|
||
|
|
unsigned short parapoint[355];
|
||
|
|
unsigned int samplepoint[99], lengthbytes;
|
||
|
|
int count;
|
||
|
|
FMOD_RESULT result = FMOD_OK;
|
||
|
|
|
||
|
|
if (!mFile->mFlags & FMOD_FILE_SEEKABLE)
|
||
|
|
{
|
||
|
|
return FMOD_ERR_FORMAT;
|
||
|
|
}
|
||
|
|
|
||
|
|
init(FMOD_SOUND_TYPE_S3M);
|
||
|
|
|
||
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecS3M::openInternal", "attempting to open as S3M..\n"));
|
||
|
|
|
||
|
|
result = mFile->seek(0, SEEK_SET);
|
||
|
|
if (result != FMOD_OK)
|
||
|
|
{
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
Get size of file in bytes
|
||
|
|
*/
|
||
|
|
result = mFile->getSize(&lengthbytes);
|
||
|
|
if (result != FMOD_OK)
|
||
|
|
{
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
Verify Format
|
||
|
|
*/
|
||
|
|
result = mFile->seek(0x2C, SEEK_SET);
|
||
|
|
if (result != FMOD_OK)
|
||
|
|
{
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
result = mFile->read(&temp, 1, 4, 0);
|
||
|
|
if (result != FMOD_OK)
|
||
|
|
{
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
if (FMOD_strncmp((char *)temp, "SCRM", 4))
|
||
|
|
{
|
||
|
|
FLOG((FMOD_DEBUG_LEVEL_LOG, __FILE__, __LINE__, "CodecS3M::openInternal", "'SCRM' ID check failed [%c%c%c%c]\n", temp[0], temp[1], temp[2], temp[3]));
|
||
|
|
return FMOD_ERR_FORMAT;
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
Set a few default values for this format
|
||
|
|
*/
|
||
|
|
for (count = 0; count < MUSIC_MAXCHANNELS; count++)
|
||
|
|
{
|
||
|
|
mMusicChannel[count] = 0;
|
||
|
|
}
|
||
|
|
mPattern = 0;
|
||
|
|
mPanSeparation = 0.8f;
|
||
|
|
mMasterSpeed = 1.0f;
|
||
|
|
mDefaultSpeed = 6;
|
||
|
|
mDefaultBPM = 125;
|
||
|
|
mDefaultGlobalVolume = 64;
|
||
|
|
mNumPatterns = 0;
|
||
|
|
mRestart = 0;
|
||
|
|
|
||
|
|
result = mFile->seek(0, SEEK_SET);
|
||
|
|
if (result != FMOD_OK)
|
||
|
|
{
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
result = mFile->read(mSongName, 1, 28, 0); /* read in module name. */
|
||
|
|
if (result != FMOD_OK)
|
||
|
|
{
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
result = mFile->getByte();
|
||
|
|
if (result != FMOD_OK)
|
||
|
|
{
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
result = mFile->seek(0x20, SEEK_SET);
|
||
|
|
if (result != FMOD_OK)
|
||
|
|
{
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
result = mFile->getWord(&songlength);
|
||
|
|
if (result != FMOD_OK)
|
||
|
|
{
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
result = mFile->getWord(&mNumSamples);
|
||
|
|
if (result != FMOD_OK)
|
||
|
|
{
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
result = mFile->getWord(&filenumpatterns);
|
||
|
|
if (result != FMOD_OK)
|
||
|
|
{
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
result = mFile->getWord(&temp16);
|
||
|
|
if (result != FMOD_OK)
|
||
|
|
{
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (temp16 & 64)
|
||
|
|
{
|
||
|
|
mMusicFlags = FMUSIC_FLAGS_FASTVOLUMESLIDES;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
mMusicFlags = FMUSIC_FLAGS_NORMALVOLUMESLIDES;
|
||
|
|
}
|
||
|
|
|
||
|
|
result = mFile->getWord(&temp16);
|
||
|
|
if (result != FMOD_OK)
|
||
|
|
{
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
if ((temp16 & 0xFFF) == 300)
|
||
|
|
{
|
||
|
|
mMusicFlags = FMUSIC_FLAGS_FASTVOLUMESLIDES; /* fast volume slides? */
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
Seek to offset 02Ch for SCRM tag
|
||
|
|
*/
|
||
|
|
result = mFile->seek(0x2C, SEEK_SET);
|
||
|
|
if (result != FMOD_OK)
|
||
|
|
{
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
result = mFile->read(&temp, 1, 4, 0);
|
||
|
|
if (result != FMOD_OK)
|
||
|
|
{
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
if (FMOD_strncmp((char *)temp, "SCRM", 4))
|
||
|
|
{
|
||
|
|
return FMOD_ERR_FORMAT;
|
||
|
|
}
|
||
|
|
|
||
|
|
result = mFile->getByte(&mDefaultGlobalVolume);
|
||
|
|
if (result != FMOD_OK)
|
||
|
|
{
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
result = mFile->getByte(&mDefaultSpeed);
|
||
|
|
if (result != FMOD_OK)
|
||
|
|
{
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
result = mFile->getByte(&mDefaultBPM);
|
||
|
|
if (result != FMOD_OK)
|
||
|
|
{
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
result = mFile->getByte(&mastervol);
|
||
|
|
if (result != FMOD_OK)
|
||
|
|
{
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
result = mFile->getByte();
|
||
|
|
if (result != FMOD_OK)
|
||
|
|
{
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
result = mFile->getByte(&st3pan);
|
||
|
|
if (result != FMOD_OK)
|
||
|
|
{
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
Find the number of channels and remap the used channels linearly
|
||
|
|
*/
|
||
|
|
result = mFile->seek(0x40, SEEK_SET);
|
||
|
|
if (result != FMOD_OK)
|
||
|
|
{
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
mNumChannels = 0;
|
||
|
|
FMOD_memset(remap, 255, 32);
|
||
|
|
|
||
|
|
for (count=0; count<32; count++)
|
||
|
|
{
|
||
|
|
unsigned char temp;
|
||
|
|
|
||
|
|
result = mFile->getByte(&temp);
|
||
|
|
if (result != FMOD_OK)
|
||
|
|
{
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
if (temp < 16)
|
||
|
|
{
|
||
|
|
remap[count] = (unsigned char)mNumChannels;
|
||
|
|
if (temp <= 7)
|
||
|
|
{
|
||
|
|
mDefaultPan[mNumChannels] = 0;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
mDefaultPan[mNumChannels] = 255;
|
||
|
|
}
|
||
|
|
|
||
|
|
mNumChannels++;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
result = metaData(FMOD_TAGTYPE_FMOD, "Number of channels", &mNumChannels, sizeof(mNumChannels), FMOD_TAGDATATYPE_INT, false);
|
||
|
|
if (result != FMOD_OK)
|
||
|
|
{
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
ALLOCATE MUSIC CHANNELS
|
||
|
|
*/
|
||
|
|
for (count = 0; count < mNumChannels; count++)
|
||
|
|
{
|
||
|
|
mMusicChannel[count] = FMOD_Object_Calloc(MusicChannelS3M);
|
||
|
|
if (!mMusicChannel[count])
|
||
|
|
{
|
||
|
|
return FMOD_ERR_MEMORY;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
Load order data & calculate number of physical patterns
|
||
|
|
*/
|
||
|
|
result = mFile->seek(0x60, SEEK_SET);
|
||
|
|
if (result != FMOD_OK)
|
||
|
|
{
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
result = mFile->read(mOrderList, 1, songlength, 0);
|
||
|
|
if (result != FMOD_OK)
|
||
|
|
{
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
mNumOrders = 0;
|
||
|
|
mNumPatterns = 0;
|
||
|
|
for (count=0; count<songlength; count++)
|
||
|
|
{
|
||
|
|
mOrderList[mNumOrders] = mOrderList[count];
|
||
|
|
if (mOrderList[count]<254)
|
||
|
|
{
|
||
|
|
mNumOrders++;
|
||
|
|
if (mOrderList[count] > mNumPatterns)
|
||
|
|
{
|
||
|
|
mNumPatterns = mOrderList[count];
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
mNumPatterns++;
|
||
|
|
|
||
|
|
/*
|
||
|
|
Load in instrument and pattern parapointers
|
||
|
|
*/
|
||
|
|
result = mFile->read(parapoint, 2, mNumSamples + filenumpatterns, 0);
|
||
|
|
if (result != FMOD_OK)
|
||
|
|
{
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
Check for default panning if the panning flag is set (252 = 0xFC :)
|
||
|
|
*/
|
||
|
|
if (st3pan == 252)
|
||
|
|
{
|
||
|
|
for (count=0; count<32; count++)
|
||
|
|
{
|
||
|
|
unsigned char temp;
|
||
|
|
|
||
|
|
result = mFile->getByte(&temp);
|
||
|
|
if (result != FMOD_OK)
|
||
|
|
{
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
if (temp & 0x10)
|
||
|
|
{
|
||
|
|
mDefaultPan[remap[count]] = ((temp & 0xF)<<4);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
If stereo flag is not set then make song mono
|
||
|
|
*/
|
||
|
|
if (!(mastervol & 128))
|
||
|
|
{
|
||
|
|
for (count=0; count<32; count++)
|
||
|
|
{
|
||
|
|
mDefaultPan[count]= 128;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
Load instrument information
|
||
|
|
*/
|
||
|
|
for (count = 0; count< mNumSamples; count++)
|
||
|
|
{
|
||
|
|
char sample_name[28];
|
||
|
|
FMOD_MODE sample_mode;
|
||
|
|
unsigned int sample_length;
|
||
|
|
FMOD_SOUND_FORMAT sample_format;
|
||
|
|
unsigned char instflag;
|
||
|
|
unsigned char temp;
|
||
|
|
int channels = 1;
|
||
|
|
|
||
|
|
FMOD_memset(&mSample[count], 0, sizeof(MusicSample));
|
||
|
|
|
||
|
|
/*
|
||
|
|
Jump to instrument parapointer.
|
||
|
|
*/
|
||
|
|
result = mFile->seek((unsigned int)((parapoint[count]) << 4), SEEK_SET);
|
||
|
|
if (result != FMOD_OK)
|
||
|
|
{
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
result = mFile->seek(13, SEEK_CUR); /* skip filename */
|
||
|
|
if (result != FMOD_OK)
|
||
|
|
{
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
Find parapointer to actual sample data (3 bytes)
|
||
|
|
*/
|
||
|
|
result = mFile->getByte(&temp);
|
||
|
|
if (result != FMOD_OK)
|
||
|
|
{
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
result = mFile->getWord(&temp16);
|
||
|
|
if (result != FMOD_OK)
|
||
|
|
{
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
samplepoint[count] = ((unsigned int )temp << 16) + temp16;
|
||
|
|
|
||
|
|
/*
|
||
|
|
Get rest of information
|
||
|
|
*/
|
||
|
|
result = mFile->read(&sample_length, 4, 1, 0); /* get length */
|
||
|
|
if (result != FMOD_OK)
|
||
|
|
{
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
result = mFile->read(&mSample[count].mLoopStart, 4, 1, 0); /* loop start */
|
||
|
|
if (result != FMOD_OK)
|
||
|
|
{
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
result = mFile->read(&mSample[count].mLoopLength, 4, 1, 0); /* loop end */
|
||
|
|
if (result != FMOD_OK)
|
||
|
|
{
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
mSample[count].mLoopLength -= mSample[count].mLoopStart;
|
||
|
|
|
||
|
|
result = mFile->getByte(&mSample[count].mDefaultVolume);
|
||
|
|
if (result != FMOD_OK)
|
||
|
|
{
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
result = mFile->getWord();
|
||
|
|
if (result != FMOD_OK)
|
||
|
|
{
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
result = mFile->getByte(&instflag);
|
||
|
|
if (result != FMOD_OK)
|
||
|
|
{
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
result = mFile->getWord(&mSample[count].mMiddleC);
|
||
|
|
if (result != FMOD_OK)
|
||
|
|
{
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
result = mFile->seek(14, SEEK_CUR);
|
||
|
|
if (result != FMOD_OK)
|
||
|
|
{
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
result = mFile->read(sample_name, 28, 1, 0); /* Inst name */
|
||
|
|
if (result != FMOD_OK)
|
||
|
|
{
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
{
|
||
|
|
char s[256];
|
||
|
|
|
||
|
|
sprintf(s, "Sample name %d", count);
|
||
|
|
|
||
|
|
result = metaData(FMOD_TAGTYPE_FMOD, s, sample_name, 28, FMOD_TAGDATATYPE_STRING, false);
|
||
|
|
if (result != FMOD_OK)
|
||
|
|
{
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
result = mFile->seek(4, SEEK_CUR); /* 'SCRS' */
|
||
|
|
if (result != FMOD_OK)
|
||
|
|
{
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
Any samples with a loop length of 2 or less have no loop.
|
||
|
|
*/
|
||
|
|
sample_mode = FMOD_SOFTWARE | FMOD_2D;
|
||
|
|
|
||
|
|
if (instflag & 1 && mSample[count].mLoopLength > 2)
|
||
|
|
{
|
||
|
|
sample_mode |= FMOD_LOOP_NORMAL;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
sample_mode |= FMOD_LOOP_OFF;
|
||
|
|
mSample[count].mLoopStart = 0;
|
||
|
|
mSample[count].mLoopLength = sample_length;
|
||
|
|
}
|
||
|
|
|
||
|
|
sample_format = FMOD_SOUND_FORMAT_PCM8;
|
||
|
|
if (instflag & 4)
|
||
|
|
{
|
||
|
|
sample_format = FMOD_SOUND_FORMAT_PCM16;
|
||
|
|
sample_length <<= 1;
|
||
|
|
}
|
||
|
|
if (instflag & 2)
|
||
|
|
{
|
||
|
|
channels = 2;
|
||
|
|
sample_length <<= 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
ALLOCATE MEMORY FOR THE SAMPLE BUFFER
|
||
|
|
*/
|
||
|
|
if (sample_length)
|
||
|
|
{
|
||
|
|
FMOD_CREATESOUNDEXINFO exinfo;
|
||
|
|
|
||
|
|
FMOD_memset(&exinfo, 0, sizeof(FMOD_CREATESOUNDEXINFO));
|
||
|
|
exinfo.cbsize = sizeof(FMOD_CREATESOUNDEXINFO);
|
||
|
|
exinfo.length = sample_length;
|
||
|
|
exinfo.numchannels = channels;
|
||
|
|
exinfo.defaultfrequency = mSample[count].mMiddleC;
|
||
|
|
exinfo.format = sample_format;
|
||
|
|
|
||
|
|
result = mSystem->createSound(0, sample_mode | FMOD_OPENUSER, &exinfo, &mSample[count].mSound);
|
||
|
|
if (result != FMOD_OK)
|
||
|
|
{
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (sample_mode & FMOD_LOOP_NORMAL)
|
||
|
|
{
|
||
|
|
result = mSample[count].mSound->setLoopPoints(mSample[count].mLoopStart, FMOD_TIMEUNIT_PCM, mSample[count].mLoopStart + mSample[count].mLoopLength - 1, FMOD_TIMEUNIT_PCM);
|
||
|
|
if (result != FMOD_OK)
|
||
|
|
{
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
Alloc pattern array
|
||
|
|
*/
|
||
|
|
mNumPatternsMem = (mNumPatterns > filenumpatterns ? mNumPatterns : filenumpatterns);
|
||
|
|
mPattern = (MusicPattern *)FMOD_Memory_Calloc(mNumPatternsMem * sizeof(MusicPattern));
|
||
|
|
if (!mPattern)
|
||
|
|
{
|
||
|
|
return FMOD_ERR_MEMORY;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/*
|
||
|
|
Load the pattern data
|
||
|
|
*/
|
||
|
|
for (count=0; count < filenumpatterns; count++)
|
||
|
|
{
|
||
|
|
MusicPattern *pptr;
|
||
|
|
MusicNote *nptr;
|
||
|
|
int row,len;
|
||
|
|
|
||
|
|
/*
|
||
|
|
Jump to pattern parapointer +2, skipping length bytes.
|
||
|
|
*/
|
||
|
|
result = mFile->seek((((unsigned int )parapoint[mNumSamples+count])<<4) + 2, SEEK_SET);
|
||
|
|
if (result != FMOD_OK)
|
||
|
|
{
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
Allocate memory for the pattern header
|
||
|
|
*/
|
||
|
|
pptr = &mPattern[count];
|
||
|
|
pptr->mRows = 64;
|
||
|
|
|
||
|
|
/*
|
||
|
|
Allocate memory for pattern buffer
|
||
|
|
*/
|
||
|
|
len = mNumChannels * pptr->mRows * sizeof(MusicNote);
|
||
|
|
pptr->mData = (MusicNote *)FMOD_Memory_Calloc(len);
|
||
|
|
if (!pptr->mData)
|
||
|
|
{
|
||
|
|
return FMOD_ERR_MEMORY;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/*
|
||
|
|
Unpack one pattern (pattern number 'count')
|
||
|
|
*/
|
||
|
|
for (row=0; row<64;)
|
||
|
|
{
|
||
|
|
unsigned char temp, channel;
|
||
|
|
MusicNote dummy;
|
||
|
|
|
||
|
|
result = mFile->getByte(&temp);
|
||
|
|
if (result != FMOD_OK)
|
||
|
|
{
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
If it not 0, unpack note, if it is 0 then end of row.
|
||
|
|
*/
|
||
|
|
if(temp)
|
||
|
|
{
|
||
|
|
channel=remap[temp & 31];
|
||
|
|
|
||
|
|
/*
|
||
|
|
If it has told us to use a channel outside of the number of channels, load it into a dummy
|
||
|
|
*/
|
||
|
|
if(channel < mNumChannels)
|
||
|
|
{
|
||
|
|
nptr = pptr->mData + (mNumChannels * row) + channel;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
nptr = &dummy;
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
If there is a note
|
||
|
|
*/
|
||
|
|
if(temp & 32)
|
||
|
|
{
|
||
|
|
result = mFile->getByte(&temp16);
|
||
|
|
if (result != FMOD_OK)
|
||
|
|
{
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
Convert s3m note to internal type note
|
||
|
|
*/
|
||
|
|
|
||
|
|
switch(temp16)
|
||
|
|
{
|
||
|
|
case 255 : nptr->mNote = 0;
|
||
|
|
break;
|
||
|
|
case 254 : nptr->mNote = FMUSIC_KEYOFF;
|
||
|
|
break;
|
||
|
|
default : nptr->mNote = ((temp16>>4)*12)+(temp16&0xf) + 1; /* starts at 1 */
|
||
|
|
};
|
||
|
|
result = mFile->getByte(&nptr->mNumber);
|
||
|
|
if (result != FMOD_OK)
|
||
|
|
{
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
If there is a volume byte
|
||
|
|
*/
|
||
|
|
if (temp & 64)
|
||
|
|
{
|
||
|
|
result = mFile->getByte(&nptr->mVolume);
|
||
|
|
if (result != FMOD_OK)
|
||
|
|
{
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
nptr->mVolume++;
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
If there is an effect
|
||
|
|
*/
|
||
|
|
if(temp & 128)
|
||
|
|
{
|
||
|
|
result = mFile->getByte(&nptr->mEffect);
|
||
|
|
if (result != FMOD_OK)
|
||
|
|
{
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
result = mFile->getByte(&nptr->mEffectParam);
|
||
|
|
if (result != FMOD_OK)
|
||
|
|
{
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
else row++;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/*
|
||
|
|
Allocate and clean out any extra patterns
|
||
|
|
*/
|
||
|
|
if (mNumPatterns > filenumpatterns)
|
||
|
|
{
|
||
|
|
for (count = filenumpatterns; count < mNumPatterns; count++)
|
||
|
|
{
|
||
|
|
MusicPattern *pptr;
|
||
|
|
|
||
|
|
pptr = &mPattern[count];
|
||
|
|
pptr->mRows = 64;
|
||
|
|
|
||
|
|
/*
|
||
|
|
Allocate memory for pattern buffer
|
||
|
|
*/
|
||
|
|
pptr->mData = (MusicNote *)FMOD_Memory_Calloc(mNumChannels * pptr->mRows * sizeof(MusicNote));
|
||
|
|
if (!pptr->mData)
|
||
|
|
{
|
||
|
|
return FMOD_ERR_MEMORY;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
Load sample data
|
||
|
|
*/
|
||
|
|
for (count = 0; count < mNumSamples; count++)
|
||
|
|
{
|
||
|
|
MusicSample *sptr;
|
||
|
|
|
||
|
|
result = mFile->seek(samplepoint[count] << 4, SEEK_SET);
|
||
|
|
if (result != FMOD_OK)
|
||
|
|
{
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
sptr = &mSample[count];
|
||
|
|
if (sptr->mSound)
|
||
|
|
{
|
||
|
|
void *ptr1, *ptr2;
|
||
|
|
unsigned int len1, len2, lenbytes;
|
||
|
|
|
||
|
|
result = sptr->mSound->getLength(&lenbytes, FMOD_TIMEUNIT_PCMBYTES);
|
||
|
|
if (result != FMOD_OK)
|
||
|
|
{
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
result = sptr->mSound->lock(0, lenbytes, &ptr1, &ptr2, &len1, &len2);
|
||
|
|
if (result != FMOD_OK)
|
||
|
|
{
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (ptr1 && len1)
|
||
|
|
{
|
||
|
|
unsigned int count2;
|
||
|
|
|
||
|
|
if (sptr->mSound->mFormat == FMOD_SOUND_FORMAT_PCM16)
|
||
|
|
{
|
||
|
|
if (sptr->mSound->mChannels == 1)
|
||
|
|
{
|
||
|
|
result = mFile->read(ptr1, 2, len1 >> 1);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
unsigned int count2;
|
||
|
|
|
||
|
|
/*
|
||
|
|
S3M stereo samples are not interleaved!
|
||
|
|
*/
|
||
|
|
unsigned int off, len;
|
||
|
|
|
||
|
|
/* LEFT */
|
||
|
|
off = 0;
|
||
|
|
len = len1 >> 2;
|
||
|
|
while (len)
|
||
|
|
{
|
||
|
|
unsigned int r;
|
||
|
|
signed short tmp[512];
|
||
|
|
|
||
|
|
r = len > 512 ? 512 : len;
|
||
|
|
|
||
|
|
result = mFile->read(tmp, 2, r);
|
||
|
|
|
||
|
|
for (count2 = 0; count2 < r; count2++)
|
||
|
|
{
|
||
|
|
((signed short *)ptr1)[(off + count2) * 2] = tmp[count2];
|
||
|
|
}
|
||
|
|
|
||
|
|
len -= r;
|
||
|
|
off += r;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* RIGHT */
|
||
|
|
off = 0;
|
||
|
|
len = len1 >> 2;
|
||
|
|
while (len)
|
||
|
|
{
|
||
|
|
unsigned int r;
|
||
|
|
signed short tmp[512];
|
||
|
|
|
||
|
|
r = len > 512 ? 512 : len;
|
||
|
|
|
||
|
|
result = mFile->read(tmp, 2, r);
|
||
|
|
|
||
|
|
for (count2 = 0; count2 < r; count2++)
|
||
|
|
{
|
||
|
|
((signed short *)ptr1)[((off + count2) * 2) + 1] = tmp[count2];
|
||
|
|
}
|
||
|
|
|
||
|
|
len -= r;
|
||
|
|
off += r;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
for (count2 = 0; count2 < len1 / 2; count2++)
|
||
|
|
{
|
||
|
|
((unsigned short *)ptr1)[count2] ^= 32768;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
if (sptr->mSound->mChannels == 1)
|
||
|
|
{
|
||
|
|
result = mFile->read(ptr1, 1, len1);
|
||
|
|
if (result != FMOD_OK)
|
||
|
|
{
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
unsigned int count2;
|
||
|
|
|
||
|
|
/*
|
||
|
|
S3M stereo samples are not interleaved!
|
||
|
|
*/
|
||
|
|
unsigned int off, len;
|
||
|
|
|
||
|
|
/* LEFT */
|
||
|
|
off = 0;
|
||
|
|
len = len1 >> 2;
|
||
|
|
while (len)
|
||
|
|
{
|
||
|
|
unsigned int r;
|
||
|
|
signed char tmp[512];
|
||
|
|
|
||
|
|
r = len > 512 ? 512 : len;
|
||
|
|
|
||
|
|
result = mFile->read(tmp, 1, r);
|
||
|
|
|
||
|
|
for (count2 = 0; count2 < r; count2++)
|
||
|
|
{
|
||
|
|
((signed char *)ptr1)[(off + count2) * 2] = tmp[count2];
|
||
|
|
}
|
||
|
|
|
||
|
|
len -= r;
|
||
|
|
off += r;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* RIGHT */
|
||
|
|
off = 0;
|
||
|
|
len = len1 >> 2;
|
||
|
|
while (len)
|
||
|
|
{
|
||
|
|
unsigned int r;
|
||
|
|
signed char tmp[512];
|
||
|
|
|
||
|
|
r = len > 512 ? 512 : len;
|
||
|
|
|
||
|
|
result = mFile->read(tmp, 1, r);
|
||
|
|
|
||
|
|
for (count2 = 0; count2 < r; count2++)
|
||
|
|
{
|
||
|
|
((signed char *)ptr1)[((off + count2) * 2) + 1] = tmp[count2];
|
||
|
|
}
|
||
|
|
|
||
|
|
len -= r;
|
||
|
|
off += r;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
for (count2 = 0; count2 < len1; count2++)
|
||
|
|
{
|
||
|
|
((unsigned char *)ptr1)[count2] ^= 128;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (result != FMOD_OK && result != FMOD_ERR_FILE_EOF)
|
||
|
|
{
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
result = sptr->mSound->unlock(ptr1, ptr2, len1, len2);
|
||
|
|
if (result != FMOD_OK)
|
||
|
|
{
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
mWaveFormatMemory = (FMOD_CODEC_WAVEFORMAT *)FMOD_Memory_Calloc(sizeof(FMOD_CODEC_WAVEFORMAT));
|
||
|
|
if (!mWaveFormatMemory)
|
||
|
|
{
|
||
|
|
return FMOD_ERR_MEMORY;
|
||
|
|
}
|
||
|
|
|
||
|
|
waveformat = mWaveFormatMemory;
|
||
|
|
waveformat[0].lengthbytes = lengthbytes;
|
||
|
|
|
||
|
|
/*
|
||
|
|
Set up general codec parameters.
|
||
|
|
*/
|
||
|
|
if (userexinfo && userexinfo->format != FMOD_SOUND_FORMAT_NONE)
|
||
|
|
{
|
||
|
|
waveformat[0].format = userexinfo->format;
|
||
|
|
}
|
||
|
|
#ifndef PLATFORM_PS3
|
||
|
|
else if (usermode & FMOD_SOFTWARE)
|
||
|
|
{
|
||
|
|
waveformat[0].format = FMOD_SOUND_FORMAT_PCMFLOAT;
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
else
|
||
|
|
{
|
||
|
|
waveformat[0].format = FMOD_SOUND_FORMAT_PCM16;
|
||
|
|
}
|
||
|
|
|
||
|
|
waveformat[0].channels = 2;
|
||
|
|
FMOD_strncpy(waveformat[0].name, mSongName, FMOD_STRING_MAXNAMELEN);
|
||
|
|
|
||
|
|
result = mSystem->getSoftwareFormat(&waveformat[0].frequency, 0, 0, 0, 0, 0);
|
||
|
|
if (result != FMOD_OK)
|
||
|
|
{
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
mSrcDataOffset = 0;
|
||
|
|
|
||
|
|
SoundI::getBytesFromSamples(1, (unsigned int *)&waveformat[0].blockalign, waveformat[0].channels, waveformat[0].format);
|
||
|
|
|
||
|
|
/*
|
||
|
|
Create a head unit that software channels connect to.
|
||
|
|
*/
|
||
|
|
{
|
||
|
|
FMOD_DSP_DESCRIPTION_EX descriptionex;
|
||
|
|
|
||
|
|
FMOD_memset(&descriptionex, 0, sizeof(FMOD_DSP_DESCRIPTION_EX));
|
||
|
|
FMOD_strcpy(descriptionex.name, "FMOD S3M Target Unit");
|
||
|
|
descriptionex.version = 0x00010100;
|
||
|
|
descriptionex.channels = waveformat[0].channels;
|
||
|
|
descriptionex.mFormat = waveformat[0].format;
|
||
|
|
descriptionex.mCategory = FMOD_DSP_CATEGORY_SOUNDCARD;
|
||
|
|
|
||
|
|
result = mSystem->createDSP(&descriptionex, &mDSPHead);
|
||
|
|
if (result != FMOD_OK)
|
||
|
|
{
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
mDSPHead->mDefaultFrequency = (float)waveformat[0].frequency;
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
Create a pool of virtual channels.
|
||
|
|
*/
|
||
|
|
{
|
||
|
|
mNumVirtualChannels = mNumChannels;
|
||
|
|
mVirtualChannel = (MusicVirtualChannel *)FMOD_Memory_Calloc(sizeof(MusicVirtualChannel) * mNumVirtualChannels);
|
||
|
|
if (!mVirtualChannel)
|
||
|
|
{
|
||
|
|
return FMOD_ERR_MEMORY;
|
||
|
|
}
|
||
|
|
for (count = 0; count < mNumVirtualChannels; count++)
|
||
|
|
{
|
||
|
|
new (&mVirtualChannel[count]) MusicVirtualChannel;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
Create a pool of real channels. 2x the number of virtual channels for double buffer channel usage.
|
||
|
|
*/
|
||
|
|
{
|
||
|
|
int numrealchannels = mNumVirtualChannels * 2;
|
||
|
|
|
||
|
|
mChannelPool = FMOD_Object_Calloc(ChannelPool);
|
||
|
|
if (!mChannelPool)
|
||
|
|
{
|
||
|
|
return FMOD_ERR_MEMORY;
|
||
|
|
}
|
||
|
|
|
||
|
|
result = mChannelPool->init(mSystem, 0, numrealchannels);
|
||
|
|
if (result != FMOD_OK)
|
||
|
|
{
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
mChannelSoftware = (ChannelSoftware *)FMOD_Memory_Calloc(sizeof(ChannelSoftware) * numrealchannels);
|
||
|
|
if (!mChannelSoftware)
|
||
|
|
{
|
||
|
|
return FMOD_ERR_MEMORY;
|
||
|
|
}
|
||
|
|
|
||
|
|
for (count = 0; count < numrealchannels; count++)
|
||
|
|
{
|
||
|
|
new (&mChannelSoftware[count]) ChannelSoftware;
|
||
|
|
CHECK_RESULT(mChannelPool->setChannel(count, &mChannelSoftware[count], mDSPHead));
|
||
|
|
CHECK_RESULT(mChannelSoftware[count].allowReverb(false));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if ((usermode & FMOD_ACCURATETIME) || (usermode & FMOD_CREATESAMPLE))
|
||
|
|
{
|
||
|
|
mVisited = (bool *)FMOD_Memory_Calloc(sizeof(bool) * mNumOrders * FMUSIC_MAXROWS);
|
||
|
|
if (!mVisited)
|
||
|
|
{
|
||
|
|
return FMOD_ERR_MEMORY;
|
||
|
|
}
|
||
|
|
|
||
|
|
calculateLength();
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
mVisited = 0;
|
||
|
|
waveformat[0].lengthpcm = (unsigned int)-1; /* Thanks to those stupid comment markers! */
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
Fill out base class members, also pointing to or allocating storage for them.
|
||
|
|
*/
|
||
|
|
numsubsounds = 0;
|
||
|
|
|
||
|
|
play(true);
|
||
|
|
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/*
|
||
|
|
[
|
||
|
|
[DESCRIPTION]
|
||
|
|
|
||
|
|
[PARAMETERS]
|
||
|
|
|
||
|
|
[RETURN_VALUE]
|
||
|
|
|
||
|
|
[REMARKS]
|
||
|
|
|
||
|
|
[PLATFORMS]
|
||
|
|
Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube
|
||
|
|
|
||
|
|
[SEE_ALSO]
|
||
|
|
]
|
||
|
|
*/
|
||
|
|
FMOD_RESULT CodecS3M::closeInternal()
|
||
|
|
{
|
||
|
|
int count;
|
||
|
|
|
||
|
|
stop();
|
||
|
|
|
||
|
|
if (mChannelPool)
|
||
|
|
{
|
||
|
|
mChannelPool->release();
|
||
|
|
mChannelPool = 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (mDSPHead)
|
||
|
|
{
|
||
|
|
mDSPHead->release();
|
||
|
|
mDSPHead = 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
for (count = 0; count < mNumSamples; count++)
|
||
|
|
{
|
||
|
|
if (mSample[count].mSound)
|
||
|
|
{
|
||
|
|
mSample[count].mSound->release();
|
||
|
|
mSample[count].mSound = 0;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (mVirtualChannel)
|
||
|
|
{
|
||
|
|
FMOD_Memory_Free(mVirtualChannel);
|
||
|
|
mVirtualChannel = 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (mChannelSoftware)
|
||
|
|
{
|
||
|
|
FMOD_Memory_Free(mChannelSoftware);
|
||
|
|
mChannelSoftware = 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (mPattern)
|
||
|
|
{
|
||
|
|
for (count = 0; count < mNumPatternsMem; count++)
|
||
|
|
{
|
||
|
|
if (mPattern[count].mData)
|
||
|
|
{
|
||
|
|
FMOD_Memory_Free(mPattern[count].mData);
|
||
|
|
mPattern[count].mData = 0;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
FMOD_Memory_Free(mPattern);
|
||
|
|
mPattern = 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
for (count = 0; count < mNumChannels; count++)
|
||
|
|
{
|
||
|
|
if (mMusicChannel[count])
|
||
|
|
{
|
||
|
|
FMOD_Memory_Free(mMusicChannel[count]);
|
||
|
|
mMusicChannel[count] = 0;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (mVisited)
|
||
|
|
{
|
||
|
|
FMOD_Memory_Free(mVisited);
|
||
|
|
mVisited = 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (mWaveFormatMemory)
|
||
|
|
{
|
||
|
|
FMOD_Memory_Free(mWaveFormatMemory);
|
||
|
|
mWaveFormatMemory = 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
return FMOD_OK;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/*
|
||
|
|
[
|
||
|
|
[DESCRIPTION]
|
||
|
|
|
||
|
|
[PARAMETERS]
|
||
|
|
|
||
|
|
[RETURN_VALUE]
|
||
|
|
|
||
|
|
[REMARKS]
|
||
|
|
|
||
|
|
[PLATFORMS]
|
||
|
|
Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube
|
||
|
|
|
||
|
|
[SEE_ALSO]
|
||
|
|
]
|
||
|
|
*/
|
||
|
|
FMOD_RESULT CodecS3M::readInternal(void *buffer, unsigned int sizebytes, unsigned int *bytesread)
|
||
|
|
{
|
||
|
|
FMOD_RESULT result = FMOD_OK;
|
||
|
|
unsigned int numsamples;
|
||
|
|
int numchannels;
|
||
|
|
LocalCriticalSection criticalsection(mSystem->mDSPCrit);
|
||
|
|
|
||
|
|
numchannels = waveformat[0].channels;
|
||
|
|
|
||
|
|
SoundI::getSamplesFromBytes(sizebytes, &numsamples, numchannels, waveformat[0].format);
|
||
|
|
|
||
|
|
if (mPlaying && mMasterSpeed)
|
||
|
|
{
|
||
|
|
unsigned int mixedsofar = 0;
|
||
|
|
unsigned int mixedleft = mMixerSamplesLeft;
|
||
|
|
unsigned int samplestomix;
|
||
|
|
char *destptr;
|
||
|
|
|
||
|
|
/*
|
||
|
|
Keep resetting the mix pointer to the beginning of this portion of the ring buffer
|
||
|
|
*/
|
||
|
|
destptr = (char *)buffer;
|
||
|
|
|
||
|
|
while (mixedsofar < numsamples)
|
||
|
|
{
|
||
|
|
unsigned int read, bytes;
|
||
|
|
|
||
|
|
if (!mixedleft)
|
||
|
|
{
|
||
|
|
result = update(true);
|
||
|
|
if (result != FMOD_OK)
|
||
|
|
{
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
samplestomix = mMixerSamplesPerTick;
|
||
|
|
mixedleft = samplestomix;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
samplestomix = mixedleft;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (mixedsofar + samplestomix > numsamples)
|
||
|
|
{
|
||
|
|
samplestomix = numsamples - mixedsofar;
|
||
|
|
}
|
||
|
|
|
||
|
|
read = samplestomix;
|
||
|
|
|
||
|
|
criticalsection.enter();
|
||
|
|
if (buffer)
|
||
|
|
{
|
||
|
|
result = mDSPHead->read(destptr, &read, FMOD_SPEAKERMODE_STEREO_LINEAR, 2, mDSPTick);
|
||
|
|
if (result != FMOD_OK)
|
||
|
|
{
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
mDSPTick++;
|
||
|
|
}
|
||
|
|
|
||
|
|
SoundI::getBytesFromSamples(read, &bytes, numchannels, waveformat[0].format);
|
||
|
|
|
||
|
|
/*
|
||
|
|
Buffer returned from the DSP head execute may not be the one we sent in (it may be
|
||
|
|
one of the global buffers). Don't leave mDSPCrit until after we have copied data out
|
||
|
|
*/
|
||
|
|
criticalsection.leave();
|
||
|
|
|
||
|
|
mixedsofar += read;
|
||
|
|
destptr += bytes;
|
||
|
|
mixedleft -= read;
|
||
|
|
}
|
||
|
|
|
||
|
|
mMixerSamplesLeft = mixedleft;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (bytesread)
|
||
|
|
{
|
||
|
|
*bytesread = sizebytes;
|
||
|
|
}
|
||
|
|
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
/*
|
||
|
|
[
|
||
|
|
[DESCRIPTION]
|
||
|
|
|
||
|
|
[PARAMETERS]
|
||
|
|
|
||
|
|
[RETURN_VALUE]
|
||
|
|
|
||
|
|
[REMARKS]
|
||
|
|
|
||
|
|
[PLATFORMS]
|
||
|
|
Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube
|
||
|
|
|
||
|
|
[SEE_ALSO]
|
||
|
|
]
|
||
|
|
*/
|
||
|
|
FMOD_RESULT CodecS3M::setPositionInternal(int subsound, unsigned int position, FMOD_TIMEUNIT postype)
|
||
|
|
{
|
||
|
|
if (postype == FMOD_TIMEUNIT_MODORDER)
|
||
|
|
{
|
||
|
|
play();
|
||
|
|
mNextOrder = mOrder = position;
|
||
|
|
|
||
|
|
return FMOD_OK;
|
||
|
|
}
|
||
|
|
else if (postype == FMOD_TIMEUNIT_PCM)
|
||
|
|
{
|
||
|
|
bool restarted = false;
|
||
|
|
|
||
|
|
if (position == mPCMOffset)
|
||
|
|
{
|
||
|
|
return FMOD_OK;
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
Want to seek backwards, start from the start.
|
||
|
|
*/
|
||
|
|
if (position < mPCMOffset)
|
||
|
|
{
|
||
|
|
play();
|
||
|
|
restarted = true;
|
||
|
|
}
|
||
|
|
|
||
|
|
while (mPCMOffset < position)
|
||
|
|
{
|
||
|
|
update(true);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (restarted)
|
||
|
|
{
|
||
|
|
bool oldplaying = mPlaying;
|
||
|
|
bool oldfinished = mFinished;
|
||
|
|
|
||
|
|
stop();
|
||
|
|
|
||
|
|
mPlaying = oldplaying;
|
||
|
|
mFinished = oldfinished;
|
||
|
|
}
|
||
|
|
|
||
|
|
return FMOD_OK;
|
||
|
|
}
|
||
|
|
|
||
|
|
return FMOD_ERR_FORMAT;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/*
|
||
|
|
[
|
||
|
|
[DESCRIPTION]
|
||
|
|
|
||
|
|
[PARAMETERS]
|
||
|
|
|
||
|
|
[RETURN_VALUE]
|
||
|
|
|
||
|
|
[REMARKS]
|
||
|
|
|
||
|
|
[PLATFORMS]
|
||
|
|
Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube
|
||
|
|
|
||
|
|
[SEE_ALSO]
|
||
|
|
]
|
||
|
|
*/
|
||
|
|
FMOD_RESULT F_CALLBACK CodecS3M::openCallback(FMOD_CODEC_STATE *codec, FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo)
|
||
|
|
{
|
||
|
|
CodecS3M *cs3m = (CodecS3M *)codec;
|
||
|
|
|
||
|
|
return cs3m->openInternal(usermode, userexinfo);
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/*
|
||
|
|
[
|
||
|
|
[DESCRIPTION]
|
||
|
|
|
||
|
|
[PARAMETERS]
|
||
|
|
|
||
|
|
[RETURN_VALUE]
|
||
|
|
|
||
|
|
[REMARKS]
|
||
|
|
|
||
|
|
[PLATFORMS]
|
||
|
|
Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube
|
||
|
|
|
||
|
|
[SEE_ALSO]
|
||
|
|
]
|
||
|
|
*/
|
||
|
|
FMOD_RESULT F_CALLBACK CodecS3M::closeCallback(FMOD_CODEC_STATE *codec)
|
||
|
|
{
|
||
|
|
CodecS3M *cs3m = (CodecS3M *)codec;
|
||
|
|
|
||
|
|
return cs3m->closeInternal();
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/*
|
||
|
|
[
|
||
|
|
[DESCRIPTION]
|
||
|
|
|
||
|
|
[PARAMETERS]
|
||
|
|
|
||
|
|
[RETURN_VALUE]
|
||
|
|
|
||
|
|
[REMARKS]
|
||
|
|
|
||
|
|
[PLATFORMS]
|
||
|
|
Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube
|
||
|
|
|
||
|
|
[SEE_ALSO]
|
||
|
|
]
|
||
|
|
*/
|
||
|
|
FMOD_RESULT F_CALLBACK CodecS3M::readCallback(FMOD_CODEC_STATE *codec, void *buffer, unsigned int sizebytes, unsigned int *bytesread)
|
||
|
|
{
|
||
|
|
CodecS3M *cs3m = (CodecS3M *)codec;
|
||
|
|
|
||
|
|
return cs3m->readInternal(buffer, sizebytes, bytesread);
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
[
|
||
|
|
[DESCRIPTION]
|
||
|
|
|
||
|
|
[PARAMETERS]
|
||
|
|
|
||
|
|
[RETURN_VALUE]
|
||
|
|
|
||
|
|
[REMARKS]
|
||
|
|
|
||
|
|
[PLATFORMS]
|
||
|
|
Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube
|
||
|
|
|
||
|
|
[SEE_ALSO]
|
||
|
|
]
|
||
|
|
*/
|
||
|
|
FMOD_RESULT F_CALLBACK CodecS3M::setPositionCallback(FMOD_CODEC_STATE *codec, int subsound, unsigned int position, FMOD_TIMEUNIT postype )
|
||
|
|
{
|
||
|
|
CodecS3M *cs3m = (CodecS3M *)codec;
|
||
|
|
|
||
|
|
return cs3m->setPositionInternal(subsound, position, postype);
|
||
|
|
}
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
#endif
|
||
|
|
|
||
|
|
|