Strange behaviour of audioIn() and audioOut() methods on Linux

Hello dear coding people:

I hope you are in good health and everything is ending with code 0.
I am currently implementing a sequencer with a sample-accurate metronome with oF. This metronome class was originally written by me using the JUCE framework and was tested and working properly under that paradigm. Anyway, because of things that do not matter now, I want to write my app using oF. The metronome class counts the samples in the output buffer using the scheduled call to audioOut() to calculate when the next bang should be to start copying the sample data.
I succeded in “translating” the class from JUCE to oF but found that the metronome under oF was running very slow, so I started checking the chain of methods that were called from ofApp onwards.
Long story short: with bufferSize = 512 and sampleRate = 48000, I found out that the audioIn() and audioOut() methods are called every 42647 plus minus 135 microseconds (!!?). This number should be 10666.7 microseconds, almost exactly one fourth of the figure I’m measuring.
So I went to a minimal code for testing this. Below is just what I wrote based on the audioInputExample and the chrono c++ library for showing this issue.
Other useful data: oF version: of_v20230709_linux64gcc6_release, running on a Lenovo T450, compiled using g++ (Debian 12.2.0-14) 12.2.0.

Any hints on this, please? Thank you very much in advance.

GusCarr

ofApp.h:

#pragma once

#include "ofMain.h"
#include "chrono"
#include <iostream>

class ofApp : public ofBaseApp{

    public:

        void setup();
        void update();
        void draw();

        void keyPressed(int key);
        void keyReleased(int key);
        void mouseMoved(int x, int y );
        void mouseDragged(int x, int y, int button);
        void mousePressed(int x, int y, int button);
        void mouseReleased(int x, int y, int button);
        void mouseEntered(int x, int y);
        void mouseExited(int x, int y);
        void windowResized(int w, int h);
        void dragEvent(ofDragInfo dragInfo);
        void gotMessage(ofMessage msg);

        void audioIn(ofSoundBuffer & input);
        void audioOut(ofSoundBuffer & buffer);

        vector <float> left;
        vector <float> right;
        vector <float> volHistory;

        int bufferSize {512};
        int sampleRate {48000};

        int     bufferCounter {0};
        int     drawCounter {0};
        double   secondsCounter {0};

        float smoothedVol;
        float scaledVol;

        ofSoundStream soundStream;

        std::chrono::steady_clock::time_point ibegin;
        std::chrono::steady_clock::time_point iend; /
        std::chrono::steady_clock::time_point obegin;
        std::chrono::steady_clock::time_point oend;

        string idife {""};
        string odife {""};
};

ofApp.cpp (relevant methods only)

#include "ofApp.h"

//--------------------------------------------------------------
void ofApp::setup(){

    ofSetVerticalSync(true);
    ofSetCircleResolution(80);
    ofBackground(54, 54, 54);

    left.assign(bufferSize, 0.0);
    right.assign(bufferSize, 0.0);
    volHistory.assign(400, 0.0);

    bufferCounter   = 0;
    drawCounter     = 0;
    smoothedVol     = 0.0;
    scaledVol       = 0.0;

    ofSoundStreamSettings settings;

    auto devices = soundStream.getMatchingDevices("default");
    if(!devices.empty())    settings.setInDevice(devices[0]);   

    settings.setInListener(this);
    settings.setOutListener(this);

    settings.sampleRate = sampleRate;

    settings.numOutputChannels = 2;

    settings.numInputChannels = 2;
    settings.bufferSize = bufferSize;
    soundStream.setup(settings);

    ibegin = std::chrono::steady_clock::now();
    iend = std::chrono::steady_clock::now();

    obegin = std::chrono::steady_clock::now();
    oend = std::chrono::steady_clock::now();
}

//--------------------------------------------------------------
void ofApp::update(){}

//--------------------------------------------------------------
void ofApp::draw(){

    ofSetColor(225);
    ofDrawBitmapString("AUDIO INPUT EXAMPLE", 32, 32);
    ofDrawBitmapString("press 's' to unpause the audio\n'e' to pause the audio", 31, 92);

    drawCounter++;

    ofSetColor(225);
    string reportString = "buffers received: " + ofToString(bufferCounter)
                    + "\nseconds received: " + ofToString(secondsCounter)
                    + "\ndraw routines called: " + ofToString(drawCounter)
                    + "\nticks: " + ofToString(soundStream.getTickCount());
    ofDrawBitmapString(reportString, 32, 200);

    string timespans = " In time span: " + idife + "\nOut time span: " + odife;

    ofDrawBitmapString(timespans, 32, 330);
}

//--------------------------------------------------------------
void ofApp::audioIn(ofSoundBuffer & input){

    bufferCounter++;
    secondsCounter += (static_cast<double>(bufferSize) / static_cast<double>(sampleRate));
    iend = std::chrono::steady_clock::now();
    idife = ofToString (std::chrono::duration_cast<std::chrono::microseconds> (iend - ibegin).count());
    ibegin = std::chrono::steady_clock::now();
}

//--------------------------------------------------------------
void ofApp::audioOut(ofSoundBuffer & buffer)
{
    oend = std::chrono::steady_clock::now();
    odife = ofToString (std::chrono::duration_cast<std::chrono::microseconds> (oend - obegin).count());
    obegin = std::chrono::steady_clock::now();
}

//--------------------------------------------------------------
void ofApp::keyPressed  (int key){
    if( key == 's' )        soundStream.start();
    if( key == 'e' )        soundStream.stop();
}

main.cpp

#include "ofMain.h"
#include "ofApp.h"

//========================================================================
int main( ){
	//Use ofGLFWWindowSettings for more options like multi-monitor fullscreen
	ofGLWindowSettings settings;
	settings.setSize(1024, 768);
	settings.windowMode = OF_WINDOW; //can also be OF_FULLSCREEN
	auto window = ofCreateWindow(settings);
	ofRunApp(window, make_shared<ofApp>());
	ofRunMainLoop();
}

can you make your calculations using the buffer’s input.getNumFrames() instead of relying on your bufferSize global?

(you are currently presuming buffers to be 512, and SR to be 48kHz but “asking” for it does not mean it’s running at that — same with sampling rate)

Yes! Thank you very much @burton. It was that. Seems that for some frameworks bufferSize means “amount of frames” and I was lost in translation, ha ha. Now 42666 µs are correct for 2048 frames.

Screenshot_2023-09-12_11-58-52-checkIncomingAudio

You don’t know how pleased I am that you showed me that I made such a silly mistake. The moral lesson of this is “Don’t keep up coding just because you are in a good mood, at some point you need to take a break”.

Thank you again. I love this forum.

1 Like

and just to be sure you say:

Seems that for some frameworks bufferSize means “amount of frames” and I was lost in translation, ha ha. Now 42666 µs are correct for 2048 frames.

they do mean the same thing! what happens is that you request a buffer size of 512, but for some reason the OS/driver/backend is limited to 2048. so you are actually running a buffer size of 2048 (but of course your global does not get updated). same thing with SR: you could ask for 760000Hz, but the system will probably not support it and set it to as high as possible, perhaps 48000.

Well, that is quite correct. It’s the first time that the operative system doesn’t give me the buffer size I requested. Well, another thing to bear in mind. I’ll keep you updated. Thank you again.

it might be also the backend, and/or some other subtlety.

ofSoundDevice has a .sampleRates property that lists the available sampling rates (say you want to make a little GUI with a popup to set the sampling rate, you need to know what is available), but apparently not a list of available buffer sizes…

can you get the buffer size lower than 2048 with different software on the same setup? if not, it’s an OS config (or hardware limit) problem. if so, perhaps post a new issue here about not being able to get a small buffer size.