Port Audio and Libsndfile no data?

428 views Asked by At

Im probably doing something stupid but I am not getting any form of data from libsndfile when I'm using it in a sound system that I'm developing. sf_read_floatf returns greater than zero but looking at the buffer itself it's just zeroed out data. I'm also using libsamplerate but at the moment I have both samplerates of the audio file and port audio the same for testing.

Port Audio Constructor:

PortAudioSystem::PortAudioSystem(double sampleRate, PaDeviceIndex device, void * hostApiSpecificStreamInfo) {
    this->m_masterVol = this->m_musicVol = this->m_sfxVol = 1.0f;
    this->m_deltaTime = 0.0f;
    this->audioStream = nullptr;
    this->_hasPaError = false;

    this->m_sampleRate = sampleRate;

    PaStreamParameters streamParams;
    streamParams.device = device; //set device to use
    streamParams.hostApiSpecificStreamInfo = hostApiSpecificStreamInfo;
    streamParams.sampleFormat = paFloat32; // 32bit float format
    streamParams.suggestedLatency = 0.2; //200 ms ought to satisfy even the worst sound card
    streamParams.channelCount = 2; //number of channels (1: mono, 2: left/right, etc)

    int err = 0;

    err = Pa_OpenStream(
        &this->audioStream,
        0, // no input
        &streamParams,
        sampleRate,
        paFramesPerBufferUnspecified, // let portaudio choose the buffersize
        paNoFlag,/* no special modes (clip off, dither off) */
        PortAudioSystem::paCallbackCommon,
        this
    );

    if (err != paNoError) {
        pushPaError(err, Pa_GetErrorText(err));
        this->audioStream = nullptr;
    }

    int src_err;
    this->m_srcState = src_new(SRC_SINC_FASTEST,2,&src_err); //create a new sample rate converter

    if (this->m_srcState == NULL) {
        //src_error(this->m_srcState);
        this->pushPaError(src_err, src_strerror(src_err));
    }

    this->m_nextPlayID = 0;
}

Port Audio Callback:

int PortAudioSystem::paCallback(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags, void *userData) {
    this->m_deltaTime = (float)((double)framesPerBuffer / this->m_sampleRate); //get delta time based on sample rate and number of frames per buffer
    if (this->m_playingAudioFiles.size() == 0) return paContinue; //if we dont have any playing audio files, skip.

    AudioFrame::_2 *outFrame = (AudioFrame::_2*)outputBuffer; //convert the port audio buffer to an audio frame buffer.

    //zero out the output buffer.
    for (unsigned long zeroI = 0; zeroI < framesPerBuffer; zeroI++) {
        outFrame[zeroI].left = 0.0f;
        outFrame[zeroI].right = 0.0f;
    }

    //for (PlayingAudioFile playingfile : this->m_playingAudioFiles) playingfile.audioFile->Seek(playingfile.currentFrame); //seek to the current position of the file

    AudioFrame::_2 *framesOut = new AudioFrame::_2[framesPerBuffer]; //create a buffer for frames out.
    //float * fFramesOut = new float[framesPerBuffer * 2]; //create a float buffer for frames out (*2 for left and right channel)

    //prep common data for sample rate conversion.
    SRC_DATA src_data;
    src_data.output_frames = framesPerBuffer; //set output frames to be the max output frames to have.
    src_data.end_of_input = 0; //we are not at the end of the file (change for specific audio file system [function?]).
    src_data.data_out = (float *)framesOut; //set our output frames to be the buffer that we created.

    flow.lock(); //mutex lock
    for (std::pair<long, PlayingAudioFile> entry : this->m_playingAudioFiles){ //for each playing audio file
        if (entry.second.paused) continue; //if the audio file is paused, skip it.

        PlayingAudioFile& playingfile = entry.second;
        playingfile.audioFile->Seek(playingfile.currentFrame); //seek to the current position of the file

        src_data.src_ratio = this->m_sampleRate / playingfile.audioFile->GetSampleRate(); //get the ratio of the sample rate conversion

        if (src_data.src_ratio == 1) { //if we are 1 to 1, dont do sample rate conversion.
            src_data.input_frames_used = src_data.output_frames_gen = (long)playingfile.audioFile->GetFrames(framesPerBuffer, (float *)framesOut);
        } else { //otherwise convert to port audio system's sample rate.
            //adjust the number of frames to read based on ratio.
            long long framesToRead//;
            /*if (fmod((double)framesPerBuffer, src_data.src_ratio) == 0) framesToRead = framesPerBuffer;
            else framesToRead*/ = (framesPerBuffer / ((long long)src_data.src_ratio));// + 2;

            AudioFrame::_2* framesIn = new AudioFrame::_2[framesToRead]; //create a buffer for frames in.
            //float * fFramesIn = new float[framesToRead * 2]; //create a float buffer to read in frames. (*2 for left and right audio channel)
            src_data.data_in = (float *)framesIn; //set the frame in buffer

            src_data.input_frames = (long)playingfile.audioFile->GetFrames(framesToRead, (float *)framesIn); //read the frames; //set the number of frames that were read

            if (src_data.input_frames == 0) {
                delete[] framesIn; //free up the frame in buffer to prevent memory leaks.
                continue; //if we have no data, skip
            }

            src_reset(this->m_srcState); //reset the sample rate conversion state
            int src_err = src_process(this->m_srcState, &src_data); //convert the sample rate

            if (src_err != 0) this->pushPaError(src_err, src_strerror(src_err)); //if we have an error, push it back.

            delete[] framesIn; //free up the frame in buffer
        }

        //AudioFrame::_2* framesOut = (AudioFrame::_2*) fFramesOut; //convert the float buffer to an audio frame buffer for easier access to the channels

        for (unsigned long outFrameI = 0; outFrameI < src_data.output_frames_gen; outFrameI++) { //for each frame (based on how many frames were generated from conversion [possibly under framesPerBuffer])
            //individual frame data;
            AudioFrame::_2 frame = framesOut[outFrameI];

            //adjust volume from master volume
            frame.left *= this->m_masterVol;
            frame.right *= this->m_masterVol;

            //adjust volume
            switch (playingfile.channel) { //based on sound channel
            case eAT_Music: //if we are a music channel
                frame.left *= this->m_musicVol;
                frame.right *= this->m_musicVol;
                break;
            case eAT_SFX: //if we are a sfx channel
                frame.left *= this->m_sfxVol;
                frame.right *= this->m_sfxVol;
                break;
            }

            if (frame.left != 0.0f){ //make sure we have data.
                if (outFrame[outFrameI].left == 0.0f) { //if the output frame has no data
                    outFrame[outFrameI].left = frame.left; //set the audio
                } else { //if the output frame has data
                    outFrame[outFrameI].left += frame.left; //mix the channels
                    outFrame[outFrameI].left /= 2; //ghetto way of making sure we dont clip?
                }
            }
            if (frame.right != 0.0f){ //make sure we have data.
                if (outFrame[outFrameI].right == 0.0f) { //if the output frame has no data
                    outFrame[outFrameI].right = frame.right; //set the audio
                } else { //if the output frame has data 
                    outFrame[outFrameI].right += frame.right; //mix the channels
                    outFrame[outFrameI].right /= 2; //ghetto way of making sure we dont clip?
                }
            }
        }

        playingfile.currentFrame += src_data.input_frames_used; //update the current position time based on how many frames were converted;

        if (playingfile.currentFrame >= playingfile.endFrame && !playingfile.loop) { //if we are finnished playing.
            //mark for removal.
            m_stoppedAudioFiles.push_back(entry.first);
        } else {
            //loop the file
            playingfile.currentFrame = playingfile.startFrame;
        }
    }

    delete [] framesOut;

    //removed stopped audio files.
    while (m_stoppedAudioFiles.size() > 0) {
        this->m_playingAudioFiles.erase(m_stoppedAudioFiles.back());
        m_stoppedAudioFiles.pop_back();
    }

    flow.unlock(); //mutex unlock

    return paContinue; //continue playback
}

AudioFile_Libsnd:

bool AudioFile_Libsnd::Seek(long long position){
    if (!this->_hasSfError) {
        if (sf_seek(this->sndFile, position, SF_SEEK_SET) == -1) {
            int err = sf_error(this->sndFile);
            pushSfError(err, sf_error_number(err));
            return false;
        }
        return true;
    }
    return false;
}

long long AudioFile_Libsnd::GetFrames(long long framesToRead, float buff[]){
    if (this->_hasSfError) return 0;

    long long framesRead = sf_readf_float(this->sndFile, buff, framesToRead);

    return framesRead;
}

bool AudioFile_Libsnd::GetFrame(float* frame) {
    return sf_readf_float(this->sndFile, frame, 1) == 1;
}

AudioFrame::_2:

    struct _2 {
        float left;
        float right;

        _2() : left(0.0f), right(0.0f) {}
    };

PlayingAudioFile:

struct PlayingAudioFile {
    IAudioFile* audioFile;
    long long currentFrame;
    long long startFrame;
    long long endFrame;
    bool loop;
    bool paused;
    EAudioChannel channel;
};

I know the whole code is kind of long but I currently don't know what I'm doing wrong... I believe that I am constructing the buffers correctly but I have tried pure float pointers, both array defined and created with new and that didn't work. I have also tried going frame by frame with a for loop and saving it into a single float but that apparently threw me a stack around variable was corrupt error... Any help would be appreciated.

1

There are 1 answers

0
alatnet On BEST ANSWER

Fixed it. I was doing stupid. This is the culprit right here:

    if (playingfile.currentFrame >= playingfile.endFrame && !playingfile.loop) { //if we are finnished playing.
        //mark for removal.
        m_stoppedAudioFiles.push_back(entry.first);
    } else {
        //loop the file
        playingfile.currentFrame = playingfile.startFrame;
    }

changed the "else" to "else if (playingsource.currentFrame >= playingsource.endFrame && playingsource.loop)" because it was setting the current frame to the starting frame every time