Split SoundFormats/SoundMixer

This commit is contained in:
Iliyas Jorio 2021-08-15 12:42:31 +02:00
parent 6f5592fbc3
commit 104c75ad56
14 changed files with 299 additions and 244 deletions

View File

@ -41,26 +41,29 @@ set(POMME_SOURCES
$<$<BOOL:${WIN32}>:${POMME_SRCDIR}/Platform/Windows/PommeWindows.h> $<$<BOOL:${WIN32}>:${POMME_SRCDIR}/Platform/Windows/PommeWindows.h>
) )
if (NOT(POMME_NO_SOUND)) if (NOT(POMME_NO_SOUND_FORMATS))
list(APPEND POMME_SOURCES list(APPEND POMME_SOURCES
${POMME_SRCDIR}/Sound/AIFF.cpp ${POMME_SRCDIR}/SoundFormats/AIFF.cpp
${POMME_SRCDIR}/Sound/IMA4.cpp ${POMME_SRCDIR}/SoundFormats/IMA4.cpp
${POMME_SRCDIR}/Sound/MACE.cpp ${POMME_SRCDIR}/SoundFormats/MACE.cpp
${POMME_SRCDIR}/Sound/SoundUtilities.cpp ${POMME_SRCDIR}/SoundFormats/Midi.cpp
${POMME_SRCDIR}/Sound/xlaw.cpp ${POMME_SRCDIR}/SoundFormats/SoundFormats.cpp
${POMME_SRCDIR}/SoundFormats/xlaw.cpp
) )
else() else()
add_compile_definitions(POMME_NO_SOUND) add_compile_definitions(POMME_NO_SOUND_FORMATS)
endif() endif()
if (NOT(POMME_NO_SOUND_PLAYBACK)) if (NOT(POMME_NO_SOUND_MIXER))
list(APPEND POMME_SOURCES list(APPEND POMME_SOURCES
${POMME_SRCDIR}/Sound/cmixer.cpp ${POMME_SRCDIR}/SoundMixer/ChannelImpl.cpp
${POMME_SRCDIR}/Sound/cmixer.h ${POMME_SRCDIR}/SoundMixer/ChannelImpl.h
${POMME_SRCDIR}/Sound/SoundManager.cpp ${POMME_SRCDIR}/SoundMixer/cmixer.cpp
) ${POMME_SRCDIR}/SoundMixer/cmixer.h
${POMME_SRCDIR}/SoundMixer/SoundManager.cpp
)
else() else()
add_compile_definitions(POMME_NO_SOUND_PLAYBACK) add_compile_definitions(POMME_NO_SOUND_MIXER)
endif() endif()
if (NOT(POMME_NO_GRAPHICS)) if (NOT(POMME_NO_GRAPHICS))

View File

@ -53,8 +53,12 @@ void Pomme::Init()
Pomme::Graphics::Init(); Pomme::Graphics::Init();
#endif #endif
#ifndef POMME_NO_SOUND_PLAYBACK #ifndef POMME_NO_SOUND_FORMATS
Pomme::Sound::Init(); Pomme::Sound::InitMidiFrequencyTable();
#endif
#ifndef POMME_NO_SOUND_MIXER
Pomme::Sound::InitMixer();
#endif #endif
#ifndef POMME_NO_INPUT #ifndef POMME_NO_INPUT
@ -64,7 +68,7 @@ void Pomme::Init()
void Pomme::Shutdown() void Pomme::Shutdown()
{ {
#ifndef POMME_NO_SOUND_PLAYBACK #ifndef POMME_NO_SOUND_MIXER
Pomme::Sound::Shutdown(); Pomme::Sound::ShutdownMixer();
#endif #endif
} }

View File

@ -6,13 +6,16 @@
#include <istream> #include <istream>
#include <ostream> #include <ostream>
#include <memory> #include <memory>
#include "Sound/cmixer.h"
namespace Pomme::Sound namespace Pomme::Sound
{ {
void Init(); void InitMidiFrequencyTable();
void Shutdown(); void InitMixer();
void ShutdownMixer();
double GetMidiNoteFrequency(int note);
std::string GetMidiNoteName(int note);
struct SampledSoundInfo struct SampledSoundInfo
{ {

55
src/SoundFormats/Midi.cpp Normal file
View File

@ -0,0 +1,55 @@
#include "PommeSound.h"
#include <sstream>
constexpr int kNumMidiNotes = 128;
static double gMidiNoteFrequencies[kNumMidiNotes];
void Pomme::Sound::InitMidiFrequencyTable()
{
// powers of twelfth root of two
double gamme[12];
gamme[0] = 1.0;
for (int i = 1; i < 12; i++)
{
gamme[i] = gamme[i - 1] * 1.059630943592952646;
}
for (int i = 0; i < 128; i++)
{
int octave = 1 + (i + 3) / 12; // A440 and middle C are in octave 7
int semitone = (i + 3) % 12; // halfsteps up from A in current octave
float freq;
if (octave < 7)
freq = gamme[semitone] * 440.0 / (1 << (7 - octave)); // 440/(2**octaveDiff)
else
freq = gamme[semitone] * 440.0 * (1 << (octave - 7)); // 440*(2**octaveDiff)
gMidiNoteFrequencies[i] = freq;
}
}
double Pomme::Sound::GetMidiNoteFrequency(int note)
{
if (note < 0 || note >= kNumMidiNotes)
return 440.0;
else
return gMidiNoteFrequencies[note];
}
// Note: these names are according to IM:S:2-43.
// These names won't match real-world names.
// E.g. for note 67 (A 440Hz), this will return "A6", whereas the real-world
// convention for that note is "A4".
std::string Pomme::Sound::GetMidiNoteName(int note)
{
static const char* gamme[12] = {"A", "A#", "B", "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#"};
int octave = 1 + (note + 3) / 12;
int semitonesFromA = (note + 3) % 12;
std::stringstream ss;
ss << gamme[semitonesFromA] << octave;
return ss.str();
}

View File

@ -0,0 +1,126 @@
#include "PommeSound.h"
#include "SoundMixer/ChannelImpl.h"
#include <cassert>
namespace Pomme::Sound
{
extern ChannelImpl* gHeadChan;
extern int gNumManagedChans;
}
ChannelImpl::ChannelImpl(SndChannelPtr _macChannel, bool transferMacChannelOwnership)
: macChannel(_macChannel)
, macChannelStructAllocatedByPomme(transferMacChannelOwnership)
, source()
, pan(0.0)
, gain(1.0)
, baseNote(kMiddleC)
, playbackNote(kMiddleC)
, pitchMult(1.0)
, loop(false)
, interpolate(false)
{
macChannel->channelImpl = (Ptr) this;
Link(); // Link chan into our list of managed chans
}
ChannelImpl::~ChannelImpl()
{
Unlink();
macChannel->channelImpl = nullptr;
if (macChannelStructAllocatedByPomme)
{
delete macChannel;
}
}
void ChannelImpl::Recycle()
{
source.Clear();
}
void ChannelImpl::SetInitializationParameters(long initBits)
{
interpolate = !(initBits & initNoInterp);
source.SetInterpolation(interpolate);
}
void ChannelImpl::ApplyParametersToSource(uint32_t mask, bool evenIfInactive)
{
if (!evenIfInactive && !source.active)
{
return;
}
// Pitch
if (mask & kApplyParameters_Pitch)
{
double baseFreq = Pomme::Sound::GetMidiNoteFrequency(baseNote);
double playbackFreq = Pomme::Sound::GetMidiNoteFrequency(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);
}
// Interpolation
if (mask & kApplyParameters_Loop)
{
source.SetLoop(loop);
}
}
void ChannelImpl::Link()
{
if (!Pomme::Sound::gHeadChan)
{
SetNext(nullptr);
}
else
{
assert(nullptr == Pomme::Sound::gHeadChan->GetPrev());
Pomme::Sound::gHeadChan->SetPrev(this);
SetNext(Pomme::Sound::gHeadChan);
}
Pomme::Sound::gHeadChan = this;
SetPrev(nullptr);
Pomme::Sound::gNumManagedChans++;
}
void ChannelImpl::Unlink()
{
if (Pomme::Sound::gHeadChan == this)
{
Pomme::Sound::gHeadChan = GetNext();
}
if (nullptr != GetPrev())
{
GetPrev()->SetNext(GetNext());
}
if (nullptr != GetNext())
{
GetNext()->SetPrev(GetPrev());
}
SetPrev(nullptr);
SetNext(nullptr);
Pomme::Sound::gNumManagedChans--;
}

View File

@ -0,0 +1,73 @@
#pragma once
#include "Pomme.h"
#include "SoundMixer/cmixer.h"
enum ApplyParametersMask
{
kApplyParameters_PanAndGain = 1 << 0,
kApplyParameters_Pitch = 1 << 1,
kApplyParameters_Loop = 1 << 2,
kApplyParameters_Interpolation = 1 << 3,
kApplyParameters_All = 0xFFFFFFFF
};
struct ChannelImpl
{
private:
ChannelImpl* prev;
ChannelImpl* next;
public:
// Pointer to application-facing interface
SndChannelPtr macChannel;
bool macChannelStructAllocatedByPomme;
cmixer::WavStream source;
// Parameters coming from Mac sound commands, passed back to cmixer source
double pan;
double gain;
Byte baseNote;
Byte playbackNote;
double pitchMult;
bool loop;
bool interpolate;
bool temporaryPause = false;
ChannelImpl(SndChannelPtr _macChannel, bool transferMacChannelOwnership);
~ChannelImpl();
void Recycle();
void SetInitializationParameters(long initBits);
void ApplyParametersToSource(uint32_t mask, bool evenIfInactive = false);
inline ChannelImpl* GetPrev() const
{
return prev;
}
inline ChannelImpl* GetNext() const
{
return next;
}
inline void SetPrev(ChannelImpl* newPrev)
{
prev = newPrev;
}
inline void SetNext(ChannelImpl* newNext)
{
next = newNext;
macChannel->nextChan = newNext ? newNext->macChannel : nullptr;
}
void Link();
void Unlink();
};

View File

@ -1,7 +1,8 @@
#include "Pomme.h" #include "Pomme.h"
#include "PommeFiles.h" #include "PommeFiles.h"
#include "cmixer.h"
#include "PommeSound.h" #include "PommeSound.h"
#include "SoundMixer/ChannelImpl.h"
#include "SoundMixer/cmixer.h"
#include "Utilities/bigendianstreams.h" #include "Utilities/bigendianstreams.h"
#include "Utilities/IEEEExtended.h" #include "Utilities/IEEEExtended.h"
#include "Utilities/memstream.h" #include "Utilities/memstream.h"
@ -15,184 +16,14 @@
#define LOG POMME_GENLOG(POMME_DEBUG_SOUND, "SOUN") #define LOG POMME_GENLOG(POMME_DEBUG_SOUND, "SOUN")
#define LOG_NOPREFIX POMME_GENLOG_NOPREFIX(POMME_DEBUG_SOUND) #define LOG_NOPREFIX POMME_GENLOG_NOPREFIX(POMME_DEBUG_SOUND)
static struct ChannelImpl* headChan = nullptr;
static int nManagedChans = 0;
static double midiNoteFrequencies[128];
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Internal channel info // Internal channel info
enum ApplyParametersMask namespace Pomme::Sound
{ {
kApplyParameters_PanAndGain = 1 << 0, struct ChannelImpl* gHeadChan = nullptr;
kApplyParameters_Pitch = 1 << 1, int gNumManagedChans = 0;
kApplyParameters_Loop = 1 << 2, }
kApplyParameters_Interpolation = 1 << 3,
kApplyParameters_All = 0xFFFFFFFF
};
struct ChannelImpl
{
private:
ChannelImpl* prev;
ChannelImpl* next;
public:
// Pointer to application-facing interface
SndChannelPtr macChannel;
bool macChannelStructAllocatedByPomme;
cmixer::WavStream source;
// Parameters coming from Mac sound commands, passed back to cmixer source
double pan;
double gain;
Byte baseNote;
Byte playbackNote;
double pitchMult;
bool loop;
bool interpolate;
bool temporaryPause = false;
ChannelImpl(SndChannelPtr _macChannel, bool transferMacChannelOwnership)
: macChannel(_macChannel)
, macChannelStructAllocatedByPomme(transferMacChannelOwnership)
, source()
, pan(0.0)
, gain(1.0)
, baseNote(kMiddleC)
, playbackNote(kMiddleC)
, pitchMult(1.0)
, loop(false)
, interpolate(false)
{
macChannel->channelImpl = (Ptr) this;
Link(); // Link chan into our list of managed chans
}
~ChannelImpl()
{
Unlink();
macChannel->channelImpl = nullptr;
if (macChannelStructAllocatedByPomme)
{
delete macChannel;
}
}
void Recycle()
{
source.Clear();
}
void SetInitializationParameters(long initBits)
{
interpolate = !(initBits & initNoInterp);
source.SetInterpolation(interpolate);
}
void ApplyParametersToSource(uint32_t mask, bool evenIfInactive = false)
{
if (!evenIfInactive && !source.active)
{
return;
}
// Pitch
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);
}
// Interpolation
if (mask & kApplyParameters_Loop)
{
source.SetLoop(loop);
}
}
ChannelImpl* GetPrev() const
{
return prev;
}
ChannelImpl* GetNext() const
{
return next;
}
void SetPrev(ChannelImpl* newPrev)
{
prev = newPrev;
}
void SetNext(ChannelImpl* newNext)
{
next = newNext;
macChannel->nextChan = newNext ? newNext->macChannel : nullptr;
}
void Link()
{
if (!headChan)
{
SetNext(nullptr);
}
else
{
assert(nullptr == headChan->GetPrev());
headChan->SetPrev(this);
SetNext(headChan);
}
headChan = this;
SetPrev(nullptr);
nManagedChans++;
}
void Unlink()
{
if (headChan == this)
{
headChan = GetNext();
}
if (nullptr != GetPrev())
{
GetPrev()->SetNext(GetNext());
}
if (nullptr != GetNext())
{
GetNext()->SetPrev(GetPrev());
}
SetPrev(nullptr);
SetNext(nullptr);
nManagedChans--;
}
};
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Internal utilities // Internal utilities
@ -202,47 +33,6 @@ static inline ChannelImpl& GetChannelImpl(SndChannelPtr chan)
return *(ChannelImpl*) chan->channelImpl; return *(ChannelImpl*) chan->channelImpl;
} }
//-----------------------------------------------------------------------------
// MIDI note utilities
// Note: these names are according to IM:S:2-43.
// These names won't match real-world names.
// E.g. for note 67 (A 440Hz), this will return "A6", whereas the real-world
// convention for that note is "A4".
static std::string GetMidiNoteName(int i)
{
static const char* gamme[12] = {"A", "A#", "B", "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#"};
int octave = 1 + (i + 3) / 12;
int semitonesFromA = (i + 3) % 12;
std::stringstream ss;
ss << gamme[semitonesFromA] << octave;
return ss.str();
}
static void InitMidiFrequencyTable()
{
// powers of twelfth root of two
double gamme[12];
gamme[0] = 1.0;
for (int i = 1; i < 12; i++)
{
gamme[i] = gamme[i - 1] * 1.059630943592952646;
}
for (int i = 0; i < 128; i++)
{
int octave = 1 + (i + 3) / 12; // A440 and middle C are in octave 7
int semitone = (i + 3) % 12; // halfsteps up from A in current octave
if (octave < 7)
midiNoteFrequencies[i] = gamme[semitone] * 440.0 / (1 << (7 - octave)); // 440/(2**octaveDiff)
else
midiNoteFrequencies[i] = gamme[semitone] * 440.0 * (1 << (octave - 7)); // 440*(2**octaveDiff)
//LOG << i << "\t" << GetMidiNoteName(i) << "\t" << midiNoteFrequencies[i] << "\n";
}
}
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Sound Manager // Sound Manager
@ -297,7 +87,8 @@ OSErr SndNewChannel(SndChannelPtr* macChanPtr, short synth, long init, SndCallBa
//--------------------------- //---------------------------
// Done // Done
LOG << "New channel created, init = $" << std::hex << init << std::dec << ", total managed channels = " << nManagedChans << "\n"; LOG << "New channel created, init = $" << std::hex << init << std::dec
<< ", total managed channels = " << Pomme::Sound::gNumManagedChans << "\n";
return noErr; return noErr;
} }
@ -440,7 +231,8 @@ OSErr SndDoImmediate(SndChannelPtr chan, const SndCommand* cmd)
} }
case freqCmd: case freqCmd:
LOG << "freqCmd " << cmd->param2 << " " << GetMidiNoteName(cmd->param2) << " " << midiNoteFrequencies[cmd->param2] << "\n"; LOG << "freqCmd " << cmd->param2 << " "
<< Pomme::Sound::GetMidiNoteName(cmd->param2) << " " << Pomme::Sound::GetMidiNoteFrequency(cmd->param2) << "\n";
impl.playbackNote = Byte(cmd->param2); impl.playbackNote = Byte(cmd->param2);
impl.ApplyParametersToSource(kApplyParameters_Pitch); impl.ApplyParametersToSource(kApplyParameters_Pitch);
break; break;
@ -598,7 +390,7 @@ NumVersion SndSoundManagerVersion()
void Pomme_PauseAllChannels(Boolean pause) void Pomme_PauseAllChannels(Boolean pause)
{ {
for (auto* chan = headChan; chan; chan = chan->GetNext()) for (auto* chan = Pomme::Sound::gHeadChan; chan; chan = chan->GetNext())
{ {
auto& source = chan->source; auto& source = chan->source;
if (pause && source.state == cmixer::CM_STATE_PLAYING && !chan->temporaryPause) if (pause && source.state == cmixer::CM_STATE_PLAYING && !chan->temporaryPause)
@ -617,17 +409,16 @@ void Pomme_PauseAllChannels(Boolean pause)
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Init Sound Manager // Init Sound Manager
void Pomme::Sound::Init() void Pomme::Sound::InitMixer()
{ {
InitMidiFrequencyTable();
cmixer::InitWithSDL(); cmixer::InitWithSDL();
} }
void Pomme::Sound::Shutdown() void Pomme::Sound::ShutdownMixer()
{ {
cmixer::ShutdownWithSDL(); cmixer::ShutdownWithSDL();
while (headChan) while (Pomme::Sound::gHeadChan)
{ {
SndDisposeChannel(headChan->macChannel, true); SndDisposeChannel(Pomme::Sound::gHeadChan->macChannel, true);
} }
} }