mirror of
https://github.com/jorio/Pomme.git
synced 2024-12-26 14:29:38 +00:00
MP3 playback support
This commit is contained in:
parent
2acbe6cc86
commit
5d3fcb6ec6
@ -54,6 +54,12 @@ else()
|
|||||||
add_compile_definitions(POMME_NO_SOUND_FORMATS)
|
add_compile_definitions(POMME_NO_SOUND_FORMATS)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if (NOT(POMME_NO_MP3))
|
||||||
|
list(APPEND POMME_SOURCES ${POMME_SRCDIR}/SoundFormats/mp3.cpp)
|
||||||
|
else()
|
||||||
|
add_compile_definitions(POMME_NO_MP3)
|
||||||
|
endif()
|
||||||
|
|
||||||
if (NOT(POMME_NO_SOUND_MIXER))
|
if (NOT(POMME_NO_SOUND_MIXER))
|
||||||
list(APPEND POMME_SOURCES
|
list(APPEND POMME_SOURCES
|
||||||
${POMME_SRCDIR}/SoundMixer/ChannelImpl.cpp
|
${POMME_SRCDIR}/SoundMixer/ChannelImpl.cpp
|
||||||
|
12
LICENSE.md
12
LICENSE.md
@ -168,3 +168,15 @@ by Tristan Brindle (Boost license 1.0):
|
|||||||
> FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
> FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||||
> ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
> ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
> DEALINGS IN THE SOFTWARE.
|
> DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
--------
|
||||||
|
|
||||||
|
Portions copied from [lieff/minimp3](https://github.com/lieff/minimp3)
|
||||||
|
(Creative Commons Zero v1.0 Universal license):
|
||||||
|
- minimp3.h
|
||||||
|
|
||||||
|
> To the extent possible under law, the author(s) have dedicated all copyright
|
||||||
|
> and related and neighboring rights to this software to the public domain
|
||||||
|
> worldwide.
|
||||||
|
> This software is distributed without any warranty.
|
||||||
|
> See <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||||
|
@ -32,6 +32,8 @@ namespace Pomme::Sound
|
|||||||
int8_t baseNote;
|
int8_t baseNote;
|
||||||
uint32_t loopStart;
|
uint32_t loopStart;
|
||||||
uint32_t loopEnd;
|
uint32_t loopEnd;
|
||||||
|
|
||||||
|
SndListHandle MakeStandaloneResource(char** dataOffsetOut = nullptr) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Codec
|
class Codec
|
||||||
@ -101,7 +103,8 @@ namespace Pomme::Sound
|
|||||||
|
|
||||||
void GetSoundInfoFromSndResource(Handle sndHandle, SampledSoundInfo& info);
|
void GetSoundInfoFromSndResource(Handle sndHandle, SampledSoundInfo& info);
|
||||||
|
|
||||||
std::streampos GetSoundInfoFromAIFF(std::istream& input, SampledSoundInfo& info);
|
SndListHandle LoadAIFFAsResource(std::istream& input);
|
||||||
|
SndListHandle LoadMP3AsResource(std::istream& input);
|
||||||
|
|
||||||
std::unique_ptr<Pomme::Sound::Codec> GetCodec(uint32_t fourCC);
|
std::unique_ptr<Pomme::Sound::Codec> GetCodec(uint32_t fourCC);
|
||||||
}
|
}
|
||||||
|
@ -88,9 +88,9 @@ static void ParseINST(Pomme::BigEndianIStream& f, Pomme::Sound::SampledSoundInfo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::streampos Pomme::Sound::GetSoundInfoFromAIFF(std::istream& input, SampledSoundInfo& info)
|
static std::streampos GetSoundInfoFromAIFF(std::istream& input, Pomme::Sound::SampledSoundInfo& info)
|
||||||
{
|
{
|
||||||
BigEndianIStream f(input);
|
Pomme::BigEndianIStream f(input);
|
||||||
|
|
||||||
AIFFAssert('FORM' == f.Read<uint32_t>(), "AIFF: invalid FORM");
|
AIFFAssert('FORM' == f.Read<uint32_t>(), "AIFF: invalid FORM");
|
||||||
auto formSize = f.Read<uint32_t>();
|
auto formSize = f.Read<uint32_t>();
|
||||||
@ -181,3 +181,16 @@ std::streampos Pomme::Sound::GetSoundInfoFromAIFF(std::istream& input, SampledSo
|
|||||||
return sampledSoundDataOffset;
|
return sampledSoundDataOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SndListHandle Pomme::Sound::LoadAIFFAsResource(std::istream& stream)
|
||||||
|
{
|
||||||
|
Pomme::Sound::SampledSoundInfo info = {};
|
||||||
|
std::streampos ssndStart = GetSoundInfoFromAIFF(stream, info);
|
||||||
|
|
||||||
|
char* dataOffset = nullptr;
|
||||||
|
SndListHandle h = info.MakeStandaloneResource(&dataOffset);
|
||||||
|
|
||||||
|
stream.seekg(ssndStart, std::ios::beg);
|
||||||
|
stream.read(dataOffset, info.compressedLength);
|
||||||
|
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
@ -230,24 +230,61 @@ void Pomme::Sound::GetSoundInfoFromSndResource(Handle sndHandle, SampledSoundInf
|
|||||||
GetSoundInfo(sndhdr, info);
|
GetSoundInfo(sndhdr, info);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SndListHandle Pomme::Sound::SampledSoundInfo::MakeStandaloneResource(char** dataOffsetOut) const
|
||||||
|
{
|
||||||
|
const char* data = dataStart;
|
||||||
|
|
||||||
|
SampledSoundInfo info = *this;
|
||||||
|
|
||||||
|
Handle h = NewHandleClear(2 + 4 + sizeof(info) + compressedLength);
|
||||||
|
Ptr p = *h;
|
||||||
|
|
||||||
|
memcpy(p, "poPOMM", 6); // "po": see kSoundResourceType_Pomme; "POMM": see GetSoundInfo
|
||||||
|
p += 6;
|
||||||
|
|
||||||
|
info.dataStart = p + sizeof(info);
|
||||||
|
memcpy(p, &info, sizeof(info));
|
||||||
|
p += sizeof(info);
|
||||||
|
|
||||||
|
if (data != nullptr)
|
||||||
|
{
|
||||||
|
memcpy(p, data, info.compressedLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dataOffsetOut != nullptr)
|
||||||
|
{
|
||||||
|
*dataOffsetOut = p;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (SndListHandle) h;
|
||||||
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// Extension: load AIFF file as resource
|
// Extension: load AIFF file as resource
|
||||||
|
|
||||||
SndListHandle Pomme_SndLoadFileAsResource(short fRefNum)
|
SndListHandle Pomme_SndLoadFileAsResource(short fRefNum)
|
||||||
{
|
{
|
||||||
|
auto& spec = Pomme::Files::GetSpec(fRefNum);
|
||||||
auto& stream = Pomme::Files::GetStream(fRefNum);
|
auto& stream = Pomme::Files::GetStream(fRefNum);
|
||||||
|
|
||||||
Pomme::Sound::SampledSoundInfo info = {};
|
std::string fileName(spec.cName);
|
||||||
std::streampos ssndStart = Pomme::Sound::GetSoundInfoFromAIFF(stream, info);
|
std::transform(fileName.begin(), fileName.end(), fileName.begin(), tolower);
|
||||||
|
|
||||||
stream.seekg(ssndStart, std::ios::beg);
|
// Guess media container from extension
|
||||||
|
if (fileName.ends_with(".aiff")
|
||||||
|
|| fileName.ends_with(".aifc")
|
||||||
|
|| fileName.ends_with(".aif"))
|
||||||
|
{
|
||||||
|
return LoadAIFFAsResource(stream);
|
||||||
|
}
|
||||||
|
else if (fileName.ends_with(".mp3"))
|
||||||
|
{
|
||||||
|
#ifndef POMME_NO_MP3
|
||||||
|
return LoadMP3AsResource(stream);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
Handle h = NewHandleClear(2 + 4 + sizeof(SampledSoundInfo) + info.compressedLength);
|
return nullptr;
|
||||||
memcpy(*h, "poPOMM", 6);
|
|
||||||
memcpy(*h+6, &info, sizeof(SampledSoundInfo));
|
|
||||||
stream.read(*h+6+sizeof(SampledSoundInfo), info.compressedLength);
|
|
||||||
|
|
||||||
return (SndListHandle) h;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
1865
src/SoundFormats/minimp3.h
Normal file
1865
src/SoundFormats/minimp3.h
Normal file
File diff suppressed because it is too large
Load Diff
80
src/SoundFormats/mp3.cpp
Normal file
80
src/SoundFormats/mp3.cpp
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
#ifndef POMME_NO_MP3
|
||||||
|
|
||||||
|
#include "PommeSound.h"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#define MINIMP3_IMPLEMENTATION
|
||||||
|
#include "SoundFormats/minimp3.h"
|
||||||
|
|
||||||
|
#define MINIMP3_IO_SIZE (128*1024) // io buffer size for streaming functions, must be greater than MINIMP3_BUF_SIZE
|
||||||
|
#define MINIMP3_BUF_SIZE (16*1024) // buffer which can hold minimum 10 consecutive mp3 frames (~16KB) worst case
|
||||||
|
|
||||||
|
SndListHandle Pomme::Sound::LoadMP3AsResource(std::istream& stream)
|
||||||
|
{
|
||||||
|
mp3dec_t context = {};
|
||||||
|
mp3dec_init(&context);
|
||||||
|
|
||||||
|
mp3dec_frame_info_t frameInfo;
|
||||||
|
|
||||||
|
std::vector<uint8_t> fileBuf;
|
||||||
|
std::vector<mp3d_sample_t> songPCM;
|
||||||
|
std::vector<mp3d_sample_t> tempPCM(MINIMP3_MAX_SAMPLES_PER_FRAME);
|
||||||
|
|
||||||
|
int totalSamples = 0;
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
// Refill buffer
|
||||||
|
if (fileBuf.size() < MINIMP3_BUF_SIZE)
|
||||||
|
{
|
||||||
|
if (stream.eof())
|
||||||
|
{
|
||||||
|
// bail
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto oldSize = fileBuf.size();
|
||||||
|
auto toRead = MINIMP3_BUF_SIZE - oldSize;
|
||||||
|
|
||||||
|
fileBuf.resize(MINIMP3_BUF_SIZE);
|
||||||
|
stream.read((char*) (fileBuf.data() + oldSize), (int) toRead);
|
||||||
|
|
||||||
|
auto didRead = stream.gcount();
|
||||||
|
fileBuf.resize(oldSize + didRead);
|
||||||
|
}
|
||||||
|
|
||||||
|
int numDecodedSamples = mp3dec_decode_frame(&context, fileBuf.data(), (int) fileBuf.size(), tempPCM.data(), &frameInfo);
|
||||||
|
|
||||||
|
if (numDecodedSamples > 0)
|
||||||
|
{
|
||||||
|
size_t minCapacity = songPCM.size() + (numDecodedSamples * frameInfo.channels);
|
||||||
|
if (songPCM.capacity() < minCapacity)
|
||||||
|
{
|
||||||
|
songPCM.reserve(2 * minCapacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
songPCM.insert(songPCM.end(), tempPCM.begin(), tempPCM.begin() + (numDecodedSamples * frameInfo.channels));
|
||||||
|
totalSamples += numDecodedSamples;
|
||||||
|
}
|
||||||
|
|
||||||
|
fileBuf.erase(fileBuf.begin(), fileBuf.begin() + frameInfo.frame_bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
Pomme::Sound::SampledSoundInfo info = {};
|
||||||
|
info.compressionType = 'swot';
|
||||||
|
info.bigEndian = false;
|
||||||
|
info.isCompressed = false;
|
||||||
|
info.baseNote = 60; // Middle C
|
||||||
|
info.codecBitDepth = 8 * sizeof(mp3d_sample_t);
|
||||||
|
info.sampleRate = frameInfo.hz;
|
||||||
|
info.nChannels = frameInfo.channels;
|
||||||
|
info.nPackets = totalSamples;
|
||||||
|
info.decompressedLength = totalSamples * info.nChannels * sizeof(mp3d_sample_t);
|
||||||
|
info.compressedLength = info.decompressedLength;
|
||||||
|
info.dataStart = (char*) songPCM.data();
|
||||||
|
return info.MakeStandaloneResource();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // POMME_NO_MP3
|
@ -334,6 +334,12 @@ OSErr SndStartFilePlay(
|
|||||||
}
|
}
|
||||||
|
|
||||||
SndListHandle sndListHandle = Pomme_SndLoadFileAsResource(fRefNum);
|
SndListHandle sndListHandle = Pomme_SndLoadFileAsResource(fRefNum);
|
||||||
|
|
||||||
|
if (!sndListHandle)
|
||||||
|
{
|
||||||
|
return badFileFormat;
|
||||||
|
}
|
||||||
|
|
||||||
long offset = 0;
|
long offset = 0;
|
||||||
GetSoundHeaderOffset(sndListHandle, &offset);
|
GetSoundHeaderOffset(sndListHandle, &offset);
|
||||||
InstallSoundInChannel(chan, ((Ptr) *sndListHandle) + offset, true);
|
InstallSoundInChannel(chan, ((Ptr) *sndListHandle) + offset, true);
|
||||||
|
Loading…
Reference in New Issue
Block a user