Search code examples
c++memcpy

C++ Trying to copy several smaller, differently sized, blocks of data into one larger block of data


I have a bit of a strange issue. My audio engine (FMOD) is asking for data in different sized chunks, and my file streaming service (Theoraplay) is providing it's audio data also in variable sized chunks.

What I would like to do is push in the audio samples back to back, with the last one likely being only partly pressed in, the difference saved as an offset for the next time it is called. The problems I am having are A: memcpy_s is unhappy with the void pointer that FMOD is using, and B: I worry that using offsets wrong might cause memory issues with different architectures and would prefer to make this reliable.

This is the callback function currently. pData is a pointer to what FMOD want's filled, and nDatalen is it's size.

AudioCallbackQueue is a linked list that holds the audio sample, a link to the next one, and it's own current offset. The Samples themselves also vary in size thanks to "frames."

Data cleanup is elsewhere in the program.

FMOD_RESULT F_CALLBACK pcmQueue(FMOD_SOUND* pSound, void* pData, unsigned int nDataLen)
{
    FMOD::Sound*            pCurrentSound = (FMOD::Sound*)pSound; //required or else we get compiler errors
    void*                   pUserData = 0;
    AudioCallbackQueue*     pCurrent = 0;
    FMOD_RESULT             fError = FMOD_OK;
    unsigned int            nCurrentOffSet = 0;
    unsigned int            nRemainingLength = nDataLen;

    fError = pCurrentSound->getUserData( &pUserData );

    pCurrent = (AudioCallbackQueue*)pUserData;

    if(pCurrent != 0) //Error checking.
    {
        while (nRemainingLength != 0) //break when full
        {
            unsigned int nSize = (sizeof(float) * (pCurrent->pRawAudio->frames) * (pCurrent->pRawAudio->channels)) - pCurrent->nOffset; //This is the size of the current sample, minus whatever has been used previously
            
            if (nRemainingLength < nSize) //if here our sample is longer than what data is left to fill, so fill it to max and set the offset.
            {
                nSize = nRemainingLength;
                memcpy_s((pData + nCurrentOffSet), nSize, ((pCurrent->pRawAudio->samples) + pCurrent->nOffset), nSize);
                pCurrent->nOffset += nSize;
            }
            else //If here we are using the last of the sample, move on to the next
            {
                memcpy_s((pData + nCurrentOffSet), nSize, ((pCurrent->pRawAudio->samples) + pCurrent->nOffset), nSize);
                pCurrent = pCurrent->pNext;
            }

            nCurrentOffSet += nSize;
            nRemainingLength -= nSize;

            if (nSize > nDataLen)
                nSize = nDataLen;


        }
    }
    else //No samples around, nothing to process
        return FMOD_RESULT::FMOD_ERR_FILE_ENDOFDATA;

    pCurrentSound->setUserData(pCurrent);

    return FMOD_RESULT::FMOD_OK;
}

Solution

  • As @Retired Ninja says in the linked answer, you need to cast pData to a char * so that you can do pointer arithmetic on it, like this (I am using a C-style cast for brevity):

    memcpy_s(((char *) pData + nCurrentOffSet), nSize, ((pCurrent->pRawAudio->samples) + pCurrent->nOffset), nSize);
    

    Also, just something that strikes me, what do you do when you have a partial frame left over? You need some scheme to process that the next time you call pcmQueue.