mirror of
https://github.com/jorio/Pomme.git
synced 2024-12-06 18:50:18 +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)
|
||||
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))
|
||||
list(APPEND POMME_SOURCES
|
||||
${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,
|
||||
> ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
> 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;
|
||||
uint32_t loopStart;
|
||||
uint32_t loopEnd;
|
||||
|
||||
SndListHandle MakeStandaloneResource(char** dataOffsetOut = nullptr) const;
|
||||
};
|
||||
|
||||
class Codec
|
||||
@ -101,7 +103,8 @@ namespace Pomme::Sound
|
||||
|
||||
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);
|
||||
}
|
||||
|
@ -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");
|
||||
auto formSize = f.Read<uint32_t>();
|
||||
@ -181,3 +181,16 @@ std::streampos Pomme::Sound::GetSoundInfoFromAIFF(std::istream& input, SampledSo
|
||||
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);
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
SndListHandle Pomme_SndLoadFileAsResource(short fRefNum)
|
||||
{
|
||||
auto& spec = Pomme::Files::GetSpec(fRefNum);
|
||||
auto& stream = Pomme::Files::GetStream(fRefNum);
|
||||
|
||||
Pomme::Sound::SampledSoundInfo info = {};
|
||||
std::streampos ssndStart = Pomme::Sound::GetSoundInfoFromAIFF(stream, info);
|
||||
std::string fileName(spec.cName);
|
||||
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);
|
||||
memcpy(*h, "poPOMM", 6);
|
||||
memcpy(*h+6, &info, sizeof(SampledSoundInfo));
|
||||
stream.read(*h+6+sizeof(SampledSoundInfo), info.compressedLength);
|
||||
|
||||
return (SndListHandle) h;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
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);
|
||||
|
||||
if (!sndListHandle)
|
||||
{
|
||||
return badFileFormat;
|
||||
}
|
||||
|
||||
long offset = 0;
|
||||
GetSoundHeaderOffset(sndListHandle, &offset);
|
||||
InstallSoundInChannel(chan, ((Ptr) *sndListHandle) + offset, true);
|
||||
|
Loading…
Reference in New Issue
Block a user