Newer
Older
Import / applications / HighwayDash / ports / Framework / Mp3Music.cpp
#include "Mp3Music.h"

#include "MP3/minimp3.h"
#include "MP3/minimp3.c"
#include "Log.h"

#include "ResourceLoader.h"


// Perhaps for the game, could play creative commons mp3s.
// Nice one is  'The Sleeping Countess' by 'Kirbi',  Tunguska Chill Out Grooves
// About 10min play time, and about 14Mb.
// Perhaps could download and cache while there is networking?

void Mp3Music::open(const char* filename)
{
    // Perhaps could stream the file?
    // Probably one way to handle the streaming case is like this:
    //    Support loading chucks of a file at a time, so have a request API that allows fetching bytes X -> Y, eg: 0 - 10000
    //    That might be seperate HTTP requests
    //    So for playing an MP3, start by getting the beginning of the file, then start playing it
    //    When consuming the data, if the cached data dips below some level, start doing more chucked featching to re-fill the buffers
    
    LoadFileAsync(filename, true, [this](Resource* res) {
        ByteArray& data = res->data;

        mp3 = mp3_create();
        streamPos = data.data();
        bytesLeft = data.size();

        if (data[0] == 'I' && data[1] == 'D' && data[2] == '3') {
          int tagsize = ((data[9] & 0xFF) | ((data[8] & 0xFF) << 7 ) | ((data[7] & 0xFF) << 14 ) | ((data[6] & 0xFF) << 21 )) + 10;
          streamPos += tagsize;
          bytesLeft -= tagsize;
        }

        frameSize = mp3_decode(mp3, (void*)streamPos, bytesLeft, sampleBuf, &info);
        if (!frameSize) {
            Log(LL_Error, "MP3", "Error: not a valid MP3 audio file!");
        }
        bufOffset = 0;
        bufRemaining = MP3_MAX_SAMPLES_PER_FRAME;
    });
}


void Mp3Music::renderSamples(void* a_outputBuffer, size_t a_frameCount)
{
    if (bytesLeft < 1000) {
        memset(a_outputBuffer, 0, a_frameCount);
        return;
    }

    size_t outputOffset = 0;
    size_t outputRemaining = a_frameCount;

    while (outputRemaining)
    {
        size_t canCopy = std::min<size_t>(outputRemaining, bufRemaining);
        memcpy((char*)a_outputBuffer + outputOffset*2, &sampleBuf[bufOffset], canCopy*2);
        bufRemaining -= canCopy;
        outputRemaining -= canCopy;
        bufOffset += canCopy;
        outputOffset += canCopy;
        if (!bufRemaining)
        {
          frameSize = mp3_decode(mp3, (void*)streamPos, bytesLeft, sampleBuf, &info);
          if (info.audio_bytes <= 0) {
            memset(a_outputBuffer, 0, a_frameCount);
            return;
          }
          streamPos += frameSize;
          bytesLeft -= frameSize;
          bufOffset = 0;
          //bufRemaining += info.audio_bytes;//MP3_MAX_SAMPLES_PER_FRAME;
          bufRemaining += MP3_MAX_SAMPLES_PER_FRAME;
          if (bytesLeft < 100000) {
            memset(a_outputBuffer, 0, a_frameCount);
            return;
          }
        }
    }
}