The concept of making a class that oversees the entire system a global variable

Good afternoon, ladies and gentlemen.
I am new to DAISY SEED.
I have looked at some of your sample projects.
One of the things that bothered me a little was the use of global variables.
I don’t like to use global variables as much as possible.
So, I thought of creating a class called CoreSystem to control the whole program and implement all the necessary functions in it.
Is this an appropriate approach?
First, main.cpp should be as follows


#include "daisy_seed.h"
#include "daisysp.h"
#include "core_system.hpp"

int main(void){
    core = std::make_unique<CoreSystem>();
    core->run();
    return 0;
}

Then, core_system.cpp should look like this


#include "daisy_seed.h"
#include "daisysp.h"
#include <memory>

#include "pedal_board.hpp"

using namespace daisysp;
using namespace daisy;

// CoreSystem クラスの前方宣言
class CoreSystem;

// CoreSystem クラスのインスタンスを保持するユニークポインタ
std::unique_ptr<CoreSystem> core;

// オーディオコールバック関数の前方宣言
void audioCallback(AudioHandle::InputBuffer in, AudioHandle::OutputBuffer out, size_t size);

// CoreSystem クラスの定義
class CoreSystem
{
public:
    CoreSystem() : feedback_(0.5f), delay_time_(0.5f * 48000.0f) // ディレイタイム0.5秒
    {
    }

    void init(){
        hw_.Configure();
        hw_.Init();
        hw_.SetAudioBlockSize(4); // コールバックごとのサンプル数
        hw_.StartAudio(audioCallback);

        // ディレイエフェクトの初期化
        delay_.Init();
        delay_.SetDelay(delay_time_);
    }

    void run(){
        init();
        while(1) {
            // メインループのコード
        }
    }

    // インスタンスメンバー関数
    void processAudio(AudioHandle::InputBuffer in, AudioHandle::OutputBuffer out, size_t size)
    {
        for (size_t i = 0; i < size; i++)
        {
            applyDelayEffect(in, out, i);
        }
    }

private:
    daisy::DaisySeed hw_;
    DelayLine<float, 48000> delay_; // 1秒のディレイバッファ
    float feedback_; // フィードバック量
    float delay_time_; // ディレイタイム(サンプル数)

    // ディレイエフェクトを適用するメンバー関数
    void applyDelayEffect(AudioHandle::InputBuffer in, AudioHandle::OutputBuffer out, size_t index)
    {
        float dry_signal = in[0][index];
        float wet_signal = delay_.Read();
        delay_.Write(dry_signal + (wet_signal * feedback_));
        out[0][index] = dry_signal + wet_signal;
        out[1][index] = out[0][index];
    }
};

// オーディオコールバック関数の定義
void audioCallback(AudioHandle::InputBuffer in, AudioHandle::OutputBuffer out, size_t size)
{
    if (core)
    {
        core->processAudio(in, out, size);
    }
}

The pointer to the CoreSystem is made a global variable, and in main(), we just initialize the CoreSystem.
StartAudio() is a static function because the argument to hw_.StartAudio() must be a static function,
StartAudio() must be a static function, so only the audio callback is a global function.
In this function, CoreSystem, a global variable, is referenced, and necessary processing is implemented in CoreSystem.

Aggregating this entire system into a CoreSystem Class has the advantage that while CoreSystem can be accessed from all programs, only one global variable is needed.
Is there any problem with this idea?

Also, I would like to be able to declare the audio callback in the class, but do you have any other ideas? (I thought about making it a static function of the class, but decided against it because I felt it would be too complicated.)

In my opinion, it isn’t useful or necessary to be dogmatic about global variables on projects which are really quite small, and are likely to be done by one person.

Just my opinion, of course.

1 Like

Yes, that is correct.
But I want to write “highly readable source code” even if it is my personal product, so I am trying to think of a better way.

Isn’t that the essence of the singleton pattern?

This is the first time I have heard of a singleton pattern.
I will study about this.