Sound: apply channel params in ProcessSoundCommand

This commit is contained in:
Iliyas Jorio 2021-02-28 23:39:54 +01:00
parent 6399e16cc6
commit d8f5f4b324
2 changed files with 68 additions and 27 deletions

View File

@ -234,9 +234,9 @@ typedef SndCallBackProcPtr SndCallbackUPP;
typedef struct SndChannel typedef struct SndChannel
{ {
SndChannelPtr nextChan; SndChannelPtr nextChan;
Ptr firstMod; // reserved for the Sound Manager (Pomme: used as internal ptr)
SndCallBackProcPtr callBack; SndCallBackProcPtr callBack;
long long userInfo; // free for application's use (Pomme: made it 64 bit so app can store ptrs) long long userInfo; // free for application's use (Pomme: made it 64 bit so app can store ptrs)
Ptr channelImpl; // Pomme: internal ptr to implementation
#if 0 #if 0
long wait; // The following is for internal Sound Manager use only. long wait; // The following is for internal Sound Manager use only.
SndCommand cmdInProgress; SndCommand cmdInProgress;

View File

@ -79,6 +79,14 @@ enum SampledSoundEncoding
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Internal channel info // Internal channel info
enum ApplyParametersMask
{
kApplyParameters_PanAndGain = 1 << 0,
kApplyParameters_Pitch = 1 << 1,
kApplyParameters_Loop = 1 << 2,
kApplyParameters_All = 0xFFFFFFFF
};
struct ChannelImpl struct ChannelImpl
{ {
private: private:
@ -86,27 +94,34 @@ private:
ChannelImpl* next; ChannelImpl* next;
public: public:
// Pointer to application-facing interface
SndChannelPtr macChannel; SndChannelPtr macChannel;
bool macChannelStructAllocatedByPomme; bool macChannelStructAllocatedByPomme;
cmixer::WavStream source; cmixer::WavStream source;
FilePlayCompletionProcPtr onComplete;
Byte baseNote = kMiddleC; // Parameters coming from Mac sound commands, passed back to cmixer source
Byte playbackNote = kMiddleC; double pan;
double pitchMult = 1; double gain;
Byte baseNote;
Byte playbackNote;
double pitchMult;
bool loop;
bool temporaryPause = false; bool temporaryPause = false;
ChannelImpl(SndChannelPtr _macChannel, bool transferMacChannelOwnership) ChannelImpl(SndChannelPtr _macChannel, bool transferMacChannelOwnership)
: macChannel(_macChannel) : macChannel(_macChannel)
, macChannelStructAllocatedByPomme(transferMacChannelOwnership) , macChannelStructAllocatedByPomme(transferMacChannelOwnership)
, source() , source()
, onComplete(nullptr) , pan(0.0)
, gain(1.0)
, baseNote(kMiddleC) , baseNote(kMiddleC)
, playbackNote(kMiddleC) , playbackNote(kMiddleC)
, pitchMult(1.0) , pitchMult(1.0)
, loop(false)
{ {
macChannel->firstMod = (Ptr) this; macChannel->channelImpl = (Ptr) this;
Link(); // Link chan into our list of managed chans Link(); // Link chan into our list of managed chans
} }
@ -115,7 +130,7 @@ public:
{ {
Unlink(); Unlink();
macChannel->firstMod = nullptr; macChannel->channelImpl = nullptr;
if (macChannelStructAllocatedByPomme) if (macChannelStructAllocatedByPomme)
{ {
@ -126,21 +141,35 @@ public:
void Recycle() void Recycle()
{ {
source.Clear(); source.Clear();
baseNote = kMiddleC;
playbackNote = kMiddleC;
pitchMult = 1;
temporaryPause = false;
} }
void ApplyPitch() void ApplyParametersToSource(uint32_t mask, bool evenIfInactive = false)
{ {
if (!source.active) if (!evenIfInactive && !source.active)
{ {
return; return;
} }
double baseFreq = midiNoteFrequencies[baseNote];
double playbackFreq = midiNoteFrequencies[playbackNote]; // Pitch
source.SetPitch(pitchMult * playbackFreq / baseFreq); if (mask & kApplyParameters_Pitch)
{
double baseFreq = midiNoteFrequencies[baseNote];
double playbackFreq = midiNoteFrequencies[playbackNote];
source.SetPitch(pitchMult * playbackFreq / baseFreq);
}
// Pan and gain
if (mask & kApplyParameters_PanAndGain)
{
source.SetPan(pan);
source.SetGain(gain);
}
// Interpolation
if (mask & kApplyParameters_Interpolation)
{
source.SetInterpolation(interpolate);
}
} }
ChannelImpl* GetPrev() const ChannelImpl* GetPrev() const
@ -212,7 +241,7 @@ public:
static inline ChannelImpl& GetImpl(SndChannelPtr chan) static inline ChannelImpl& GetImpl(SndChannelPtr chan)
{ {
return *(ChannelImpl*) chan->firstMod; return *(ChannelImpl*) chan->channelImpl;
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -464,8 +493,16 @@ static void ProcessSoundCmd(SndChannelPtr chan, const Ptr sndhdr)
impl.source.SetLoop(true); impl.source.SetLoop(true);
} }
impl.ApplyPitch(); // Pass Mac channel parameters to cmixer source.
// The loop param is a special case -- we're detecting it automatically according
// to the sound header. If your application needs to force set the loop, it must
// issue pommeSetLoopCmd *after* bufferCmd/soundCmd.
impl.ApplyParametersToSource(kApplyParameters_All & ~kApplyParameters_Loop, true);
// Override systemwide audio pause.
impl.temporaryPause = false; impl.temporaryPause = false;
// Get it going!
impl.source.Play(); impl.source.Play();
} }
@ -494,7 +531,8 @@ OSErr SndDoImmediate(SndChannelPtr chan, const SndCommand* cmd)
break; break;
case ampCmd: case ampCmd:
impl.source.SetGain(cmd->param1 / 256.0); impl.gain = cmd->param1 / 256.0;
impl.ApplyParametersToSource(kApplyParameters_PanAndGain);
break; break;
case volumeCmd: case volumeCmd:
@ -505,31 +543,34 @@ OSErr SndDoImmediate(SndChannelPtr chan, const SndCommand* cmd)
double pan = (double)rvol / (rvol + lvol); double pan = (double)rvol / (rvol + lvol);
pan = (pan - 0.5) * 2.0; // Transpose pan from [0...1] to [-1...+1] pan = (pan - 0.5) * 2.0; // Transpose pan from [0...1] to [-1...+1]
impl.source.SetPan(pan); impl.pan = pan;
impl.source.SetGain(std::max(lvol, rvol) / 256.0); impl.gain = std::max(lvol, rvol) / 256.0;
impl.ApplyParametersToSource(kApplyParameters_PanAndGain);
break; break;
} }
case freqCmd: case freqCmd:
LOG << "freqCmd " << cmd->param2 << " " << GetMidiNoteName(cmd->param2) << " " << midiNoteFrequencies[cmd->param2] << "\n"; LOG << "freqCmd " << cmd->param2 << " " << GetMidiNoteName(cmd->param2) << " " << midiNoteFrequencies[cmd->param2] << "\n";
impl.playbackNote = Byte(cmd->param2); impl.playbackNote = Byte(cmd->param2);
impl.ApplyPitch(); impl.ApplyParametersToSource(kApplyParameters_Pitch);
break; break;
case rateCmd: case rateCmd:
// IM:S says it's a fixed-point multiplier of 22KHz, but Nanosaur uses rate "1" everywhere, // IM:S says it's a fixed-point multiplier of 22KHz, but Nanosaur uses rate "1" everywhere,
// even for sounds sampled at 44Khz, so I'm treating it as just a pitch multiplier. // even for sounds sampled at 44Khz, so I'm treating it as just a pitch multiplier.
impl.pitchMult = cmd->param2 / 65536.0; impl.pitchMult = cmd->param2 / 65536.0;
impl.ApplyPitch(); impl.ApplyParametersToSource(kApplyParameters_Pitch);
break; break;
case rateMultiplierCmd: case rateMultiplierCmd:
impl.pitchMult = cmd->param2 / 65536.0; impl.pitchMult = cmd->param2 / 65536.0;
impl.ApplyPitch(); impl.ApplyParametersToSource(kApplyParameters_Pitch);
break; break;
case pommeSetLoopCmd: case pommeSetLoopCmd:
impl.source.SetLoop(cmd->param1); impl.loop = cmd->param1;
impl.ApplyParametersToSource(kApplyParameters_Loop);
break; break;
default: default:
@ -541,7 +582,7 @@ OSErr SndDoImmediate(SndChannelPtr chan, const SndCommand* cmd)
OSErr SndDoCommand(SndChannelPtr chan, const SndCommand* cmd, Boolean noWait) OSErr SndDoCommand(SndChannelPtr chan, const SndCommand* cmd, Boolean noWait)
{ {
TODOMINOR(); TODOMINOR2("SndDoCommand isn't implemented yet, but you can probably use SndDoImmediate instead.");
return noErr; return noErr;
} }