Probleme bei der Initialisierungsreihenfolge von globalen Objekten



  • Hallo Leutz, wie der Titel schon verlauten lässt habe ich womöglich ein Probleme mit der Initialisierungsreihenfolge von Objekten bzw mit der Boot Prozedur der PS2.

    Das Problem ist das der Audio Stream nicht triggert weil ein Semaphore hängt.
    Hier ist mal die komplette Audiostream.h

    #ifndef AUDIOSTREAM_H
    #define AUDIOSTREAM_H
    #ifdef __cplusplus
    #include <cstdlib>
    #else
    #include <stdlib.h>
    #endif
    
    #include <stdio.h>
    #include <kernel.h>
    #include <tamtypes.h>
    #include <cstdlib> // für free
    #include <cstdint>  // Für uint8_t, uint32_t
    #include <cstddef>  // Für NULL
    #include <cstring>  // Für memcpy
    
    #define STACK_SIZE (32 * 1024)
    
    // 1. WICHTIG: Konstante definieren, damit sie überall bekannt ist
    #define AUDIO_BLOCK_SAMPLES 256
    #define AUDIO_SAMPLE_RATE_EXACT 48000.0f
    #define AUDIO_SAMPLE_RATE (int)(AUDIO_SAMPLE_RATE_EXACT)
    #define MAX_AUDIO_MEMORY 6144
    #define NUM_MASKS (((MAX_AUDIO_MEMORY / AUDIO_BLOCK_SAMPLES / 2) + 31) / 32)
    
    // 2. Forward Declarations
    class AudioStream;
    class AudioConnection;
    
    typedef struct audio_block_struct
    {
        uint8_t  ref_count;
        uint16_t memory_pool_index;
        float    data[AUDIO_BLOCK_SAMPLES];
    } audio_block_t;
    
    class AudioConnection
    {
    public:
        AudioConnection();
        AudioConnection(AudioStream &source, AudioStream &destination)
            : AudioConnection()
        {
            connect(source,destination);
        }
        AudioConnection(AudioStream &source, unsigned char sourceOutput,
                        AudioStream &destination, unsigned char destinationInput)
            : AudioConnection()
        {
            connect(source,sourceOutput, destination,destinationInput);
        }
        friend class AudioStream;
        ~AudioConnection();
        int disconnect(void);
        int connect(void);
        int connect(AudioStream &source, AudioStream &destination)
        {
            return connect(source,0,destination,0);
        };
        int connect(AudioStream &source, unsigned char sourceOutput,
                    AudioStream &destination, unsigned char destinationInput);
    
    protected:
        AudioStream* src;
        AudioStream* dst;
        unsigned char src_index;
        unsigned char dest_index;
        AudioConnection *next_dest;
        bool isConnected;
    };
    
    class AudioStream
    {
    public:
        AudioStream(unsigned char ninput, audio_block_t **iqueue) :
            num_inputs(ninput), inputQueue(iqueue)
        {
            active = false;
            destination_list = NULL;
            for (int i=0; i < num_inputs; i++)
            {
                inputQueue[i] = NULL;
            }
            // add to a simple list, for update_all
            // TODO: replace with a proper data flow analysis in update_all
            if (first_update == NULL)
            {
                first_update = this;
            }
            else
            {
                AudioStream *p;
                for (p=first_update; p->next_update; p = p->next_update) ;
                p->next_update = this;
            }
            next_update = NULL;
            numConnections = 0;
        }
    
        static void initialize_memory(audio_block_t *data, unsigned int num);
        static void AudioNoInterrupts()
        {
            update_exceptions++;
        }
        static void AudioInterrupts()
        {
            if (update_exceptions > 0) update_exceptions--;
        }
    
    protected:
        bool active;
        friend class AudioConnection;
        static audio_block_t * allocate(void);
        static void release(audio_block_t * block);
        void transmit(audio_block_t *block, unsigned char index = 0);
        audio_block_t * receiveReadOnly(unsigned int index = 0);
        audio_block_t * receiveWritable(unsigned int index = 0);
        static bool update_setup(void);
        static void update_stop(void);
        static void update_all(void)
        {
                printf("DEBUG: update_all check! audioSemaID ist: %d (Adresse: %p)\n", audioSemaID, (void*)&audioSemaID);
            if(audioSemaID == -1 )return;
            SignalSema(audioSemaID);
        }
        uint8_t num_inputs;
        uint32_t numConnections;
        friend void software_isr(void* arg);
        friend u64 tick_cb(s32 id, u64 scheduled, u64 actual, void* arg, void* pc_val);
    
    private:
        static AudioConnection* unused;
        audio_block_t **inputQueue;
        static AudioStream *first_update;
        AudioStream *next_update;
        AudioConnection *destination_list;
        virtual void update(void) = 0;
        static audio_block_t *memory_pool;
        static uint32_t memory_pool_available_mask[NUM_MASKS];
        static uint16_t memory_pool_first_mask;
        // Statistiken (optional)
        static uint16_t memory_used;
        static uint16_t memory_used_max;
        static volatile s32 audioSemaID;
        static bool update_scheduled;
        static u64 s_audio_ticks;
        static volatile uint32_t update_exceptions;
    };
    
    #define AudioMemory(num) ({ \
    	static audio_block_t data[num]; \
    	AudioStream::initialize_memory(data, num); \
    })
    
    
    #endif
    

    AudioOutput:

    #ifndef AUDIOOUTPUTSTREAM_H
    #define AUDIOOUTPUTSTREAM_H
    #ifdef __cplusplus
    #include <cstdlib>
    #else
    #include <stdlib.h>
    #endif
    #include <cstdint>
    #include "AudioStream.h"
    
    #define CHUNK_SIZE 2048              // Bytes pro audsrv_play_audio Aufruf
    #define RING_BUFFER_BLOCKS 8
    
    class AudioOutputStream  : public AudioStream
    {
    public:
        AudioOutputStream(void) : AudioStream(2, inputQueueArray)
        {
            begin();
        }
        void begin(void);
        virtual void update(void);
        friend class AudioInputStream;
    
    protected:
        static bool update_responsibility;
        friend int fillbuffer(void *arg);
    
    private:
        static volatile int write_idx;
        static volatile int read_idx;
        static volatile int blocks_available;
        audio_block_t *inputQueueArray[2];
    };
    
    #endif // AUDIOOUTPUTSTREAM_H
    
    
    int fillbuffer(void *arg)
    {
    
    /*
        u32 sec, usec;
        TimerBusClock2USec(GetTimerSystemTime(), &sec, &usec);
        u32 now =  usec;
        time_callback = now - last_time_callback;
        last_time_callback = now;
    */
        if(AudioOutputStream::update_responsibility)
        {
            AudioStream::update_all();
            AudioStream::update_all();
        }
    
        // Wir brauchen 2048 Bytes, also genau 2 Audio-Blöcke (2 x 1024 Bytes)
        if (AudioOutputStream::blocks_available >= 2)
        {
            // Zeiger auf den aktuellen Startblock
            // (Da wir immer um 2 erhöhen, sind die nächsten 2048 Bytes garantiert am Stück im RAM)
            char *src_ptr = (char*)&ring_buffer[AudioOutputStream::read_idx * AUDIO_BLOCK_SAMPLES * 2];
    
            // Ein einziger Aufruf für die vollen 2048 Bytes
            int played = audsrv_play_audio(src_ptr, CHUNK_SIZE);
    
            // audsrv_play_audio gibt die Anzahl der verarbeiteten Bytes zurück
            if (played > 0)
            {
                // Lese-Index um 2 Blöcke weiterschieben
                AudioOutputStream::read_idx = (AudioOutputStream::read_idx + 2) % RING_BUFFER_BLOCKS;
    
                DI();
                AudioOutputStream::blocks_available -= 2;
                EI();
            }
        }
        else
        {
            audsrv_play_audio(silence, CHUNK_SIZE);
        }
    
        return 0;
    }
    
    
    void AudioOutputStream::begin()
    {
        update_responsibility = update_setup();//this booting the IOP
        int ret;
        int err;
        printf("#Lade audsrv\n");
        #ifdef _DTL_T10000
         int modul_ret;
    
        ret = SifLoadModule("host:irx/libsd.irx", 0, NULL);
    	printf("LIBSD loadmodule %d\n", ret);
    
        ret = SifLoadStartModule("host:irx/audsrv.irx", 0, NULL, &modul_ret);
        printf("AUDSRV SifLoad_ret: %d Modul_ret: %d\n", ret, modul_ret);
    
        #else
        ret = SifLoadModule("rom0:LIBSD", 0, NULL);
        printf("libsd loadmodule %d\n", ret);
    
        ret = SifLoadModule("host:audsrv.irx", 0, NULL);
        printf("audsrv loadmodule %d\n", ret);
            #endif // _DTL_T10000
    
        ret = audsrv_init();
        if (ret != 0)
        {
            printf("failed to initialize audsrv\n");
            printf("audsrv returned error string: %s\n", audsrv_get_error_string());
            return;
        }
        struct audsrv_fmt_t format;
        format.bits = 16;
        format.freq = AUDIO_SAMPLE_RATE;
        format.channels = 2;
        err = audsrv_set_format(&format);
        printf("set format returned %d\n", err);
        printf("audsrv returned error string: %s\n", audsrv_get_error_string());
    
        audsrv_set_volume(MAX_VOLUME);
    
        err = audsrv_on_fillbuf(CHUNK_SIZE, fillbuffer, NULL);
        if (err != AUDSRV_ERR_NOERROR)
        {
            printf("audsrv_on_fillbuf failed with err=%d\n", err);
        }
    }
    
    void AudioOutputStream::update()
    {
    /*
        u32 sec, usec;
        TimerBusClock2USec(GetTimerSystemTime(), &sec, &usec);
        u32 now =  usec;
        printf("Intervall update: %d\n",now - last_time_update);
        last_time_update = now;
        printf("Intervall callback: %d\n", time_callback);
    
    */
    printf("AudioOutputStream::update()\n");
    
        // 1. Check auf Buffer Overflow (optional, aber empfohlen)
        if (blocks_available >= RING_BUFFER_BLOCKS) return;
    

    In der main.cpp instanziiere ich globale Objecte.
    AudioSynthWaveformSine sine1;
    //AudioInputStream audioIn;
    AudioOutputStream audioOut;
    Audio_fftr FFT;

    AudioConnection patchCord0(sine1, 0, audioOut, 0);
    AudioConnection patchCord1(sine1, 0, audioOut, 1);
    AudioConnection patchCord2(sine1, 0, FFT, 0);

    und in der main Funktion kommt irwann erst
    AudioMemory(32);
    das widerum feuert void AudioStream::initialize_memory(audio_block_t *data, unsigned int num)

    #include "AudioStream.h"
    #include <timer.h>
    #include <stdio.h>
    #include "IOP_BOOT.h"
    
    audio_block_t * AudioStream::memory_pool;
    uint32_t AudioStream::memory_pool_available_mask[NUM_MASKS];
    uint16_t AudioStream::memory_pool_first_mask;
    
    bool AudioStream::update_scheduled = false;
    uint16_t AudioStream::memory_used = 0;
    uint16_t AudioStream::memory_used_max = 0;
    AudioConnection* AudioStream::unused = NULL;
    AudioStream * AudioStream::first_update = NULL;
    static u8 threadStack[STACK_SIZE] __attribute__((aligned(16)));
    volatile s32 AudioStream::audioSemaID = -1;
    u64 AudioStream::s_audio_ticks = 0;
    volatile uint32_t AudioStream::update_exceptions = 0;
    
    
    void software_isr(void* arg)
    {
        while(1)
        {
            // 1. Der Thread wartet IMMER auf den Timer
            WaitSema(AudioStream::audioSemaID);
            // 2. Nur wenn keine Sperre vorliegt, wird gerechnet
            if (AudioStream::update_exceptions == 0)
            {
                AudioStream *p;
                for (p = AudioStream::first_update; p; p = p->next_update)
                {
                    if (p->active) p->update();
                }
            }
            EE_SYNC();
    
        }
    }
    
    // Die Signatur entspricht exakt deinem typedef
    u64 tick_cb(s32 id, u64 scheduled, u64 actual, void* arg, void* pc_val)
    {
        // Signal an die Semaphore
        iSignalSema(AudioStream::audioSemaID);
        // WICHTIG: Damit es ein Intervall bleibt, geben wir den nächsten
        // Zeitpunkt basierend auf der GEPLANTEN Zeit zurück.
        // Das verhindert, dass sich kleine Verzögerungen aufsummieren (Drift).
        return AudioStream::s_audio_ticks;
    }
    
    void AudioStream::initialize_memory(audio_block_t *data, unsigned int num)
    {
        unsigned int i;
        unsigned int maxnum = MAX_AUDIO_MEMORY / AUDIO_BLOCK_SAMPLES / 2;
        if (num > maxnum) num = maxnum;
    
        // --- START CRITICAL SECTION ---
        DI();
        memory_pool = data;
        memory_pool_first_mask = 0;
        for (i=0; i < NUM_MASKS; i++)
        {
            memory_pool_available_mask[i] = 0;
        }
        for (i=0; i < num; i++)
        {
            memory_pool_available_mask[i >> 5] |= (1 << (i & 0x1F));
        }
        for (i=0; i < num; i++)
        {
            data[i].memory_pool_index = i;
        }
        EI();
        // --- END CRITICAL SECTION ---
    
        // Ab hier sind Interrupts wieder an. Kernel-Calls sind jetzt sicher!
    
        ee_sema_t audioSema;
        memset(&audioSema, 0, sizeof(audioSema));
        audioSema.init_count = 0;
        audioSema.max_count = 3;
        audioSema.option = 0;
        audioSemaID = CreateSema(&audioSema);
        printf("DEBUG: Frisch erstellt! audioSemaID ist jetzt: %d (Adresse: %p)\n", audioSemaID, (void*)&audioSemaID);
        fflush(stdout);
    
        ee_thread_t threadParam;
        memset(&threadParam, 0, sizeof(threadParam));
        threadParam.func = (void*)software_isr;
        threadParam.stack = threadStack;
        threadParam.stack_size = STACK_SIZE;
        threadParam.gp_reg = &_gp; // Global Pointer der EE
        threadParam.initial_priority = 10; // Hohe Priorität als Main 16
        s32 threadID = CreateThread(&threadParam);
        StartThread(threadID, NULL);
    
        printf("::initialize_memory %d\n", update_scheduled);
    
        if (update_scheduled == false)
        {
            update_setup();
            float usec = 1e6 * AUDIO_BLOCK_SAMPLES / AUDIO_SAMPLE_RATE_EXACT;
            s_audio_ticks = USec2TimerBusClock(usec);
            s32 id = AllocTimerCounter();
            SetTimerHandler(id,  s_audio_ticks, tick_cb, &s_audio_ticks);
            StartTimerCounter(id);
        }
    }
    

    Die Sache ist nun das zuerst die globalen Objekte erstellt werden, das widerum feuert
    void AudioOutputStream::begin()
    {
    update_responsibility = update_setup();//this booting the IOP

    damit hällt AudioOutputStream die update_responsibility
    fillbuffer feuert ab jetzt AudioStream::update_all();

    und hier geht es nicht weiter '

    static void update_all(void)
        {
                //printf("DEBUG: update_all check! audioSemaID ist: %d (Adresse: %p)\n", audioSemaID, (void*)&audioSemaID);
            if(audioSemaID == -1 )return;
            SignalSema(audioSemaID);
        }
    

    if(audioSemaID == -1 ) wird nie true bzw false im dem Fall.
    initialize_memory wird aufgerufen wenn auch etwas später
    weil ich sehe ja diese Ausgabe:
    ee_sema_t audioSema;
    memset(&audioSema, 0, sizeof(audioSema));
    audioSema.init_count = 0;
    audioSema.max_count = 3;
    audioSema.option = 0;
    audioSemaID = CreateSema(&audioSema);
    printf("DEBUG: Frisch erstellt! audioSemaID ist jetzt: %d (Adresse: %p)\n", audioSemaID, (void*)&audioSemaID);
    fflush(stdout);

    Die einzige logische Idee, die ich habe ist, es gibt zwei instanzen von audioSemaID
    Leider kann ich die Speicheradressen nicht ausgeben
    soballt printf in static void update_all(void) feuert wird das print in
    initialize_memory verschluckt.

    Das Passiert weil die Ausgaben über den IOP laufen und wenn man da in eienr Schleife zu oft Ausgaben macht, geht das ein oder ander unter.
    Vorallem wenn man mit zwei Threads auf dem EE ausgaben machnen will. Das ist ein Nerviges Problem bei der Playstation 2.

    Ich habe noch einen älteren Code wo ich noch keine Bildausgabe habe, da funktioert das audio. Das erklärt aber nicht weshalb audioSemaID nie > -1 wird.



  • Ich habe ein paar formelle Anmerkungen:

    • Der Code ist weiten teils nicht richtig eingerückt bzw. formatiert.

    @D_Key sagte in Probleme bei der Initialisierungsreihenfolge von globalen Objekten:

    Objecte

    • Entweder Englisch (Objects) oder Deutsch (Objekte), aber nicht beides mixen.

    @D_Key sagte in Probleme bei der Initialisierungsreihenfolge von globalen Objekten:

    PS2

    • Damit kenne ich mich leider nicht aus, aber hast du mal die KI gefragt? Prädestiniert dafür...

    Nur überflogen: Vermutlich gibt es einen Mutex-Lock (RAII) irgendwo.





  • Ein Rassenkonflikt 😬



  • Die Formulierung ist mir auch aufgestoßen 👀 er hat aber wohl einfach "Race Conditions" 1:1 übersetzt.. Hoffe, der Lösungsvorschlag ist dagegen richtig.. 😉





  • @InternalHigh sagte in Probleme bei der Initialisierungsreihenfolge von globalen Objekten:

    Ein Rassenkonflikt

    Läuft bei google....

    @Anon-81275 sagte in Probleme bei der Initialisierungsreihenfolge von globalen Objekten:

    Hoffe, der Lösungsvorschlag ist dagegen richtig..

    Leider nein.
    Wenn man das begin im Konstruktor heraus nimmt
    übernimmt der Software timer die "responsibility".
    if (update_scheduled == false)
    {
    update_setup();
    float usec = 1e6 * AUDIO_BLOCK_SAMPLES / AUDIO_SAMPLE_RATE_EXACT;
    s_audio_ticks = USec2TimerBusClock(usec);
    s32 id = AllocTimerCounter();
    SetTimerHandler(id, s_audio_ticks, tick_cb, &s_audio_ticks);
    StartTimerCounter(id);
    }
    }

    // When an object has taken responsibility for calling update_all()
    // at each block interval (approx 2.9ms), this variable is set to
    // true. Objects that are capable of calling update_all(), typically
    // input and output based on interrupts, must check this variable in
    // their constructors.
    bool AudioStream::update_setup(void)
    {
    if (update_scheduled) return false;
    bootIOP();
    update_scheduled = true;
    return true;
    }
    Das ist nur dann notwendig wenn man keine Hardware für Audio Nutzt.
    z.B ein Sinus Ton mit einer FFT Verknüpft.
    Sobald ein Hardware Gerät ins Spiel kommt übernimmt das die Taktung.
    Das ist das Problem an KI, es kann den vollen Umfang der Audio Bibliothek nicht erfassen.
    Das ganze soll aber Plug&Play laufen.

    Man kann sich hier:
    https://www.janbob.com/electron/OpenAudio_Design_Tool/index.html
    Audio Verbindungen zusammen bauen.

    Mittlerweile ist das Problem gefunden, nachdem ich Wochen nicht weiter gekommen bin.
    Habe in der main Funktion die main Thread Priorität herab gesetzt:
    int main(int argc, char **argv)
    {
    ChangeThreadPriority(GetThreadId(), 100);
    AudioMemory(32);

    Jetzt läuft es.
    Ein bisschen bin ich verwundert warum es erst mit einer Priorität von 100 geht.
    Ist aber auch egal Hauptsache Audio läuft.



  • @D_Key sagte in Probleme bei der Initialisierungsreihenfolge von globalen Objekten:

    Läuft bei google

    Könnte auch etwas mit dem aktuellen Tagesgeschehen unserer Nachbarn aus Frankreich zu tun haben. 😉 Würde Google da jetzt keinen Strick draus drehen...

    @D_Key sagte in Probleme bei der Initialisierungsreihenfolge von globalen Objekten:

    Leider nein.
    Wenn man das begin im Konstruktor heraus nimmt
    übernimmt der Software timer die "responsibility".

    Das kann ich wie gesagt nicht überprüfen... Ich besaß noch nie eine Playstation.



  • AudioStream.h

    class AudioStream {
        // ... dein bestehender Code ...
    protected:
        static volatile bool is_audio_system_ready; 
    };
    

    AudioStream.cpp

    // Am Anfang der Datei initialisieren
    volatile bool AudioStream::is_audio_system_ready = false;
    
    void software_isr(void* arg) {
        // 1. Warten, bis der Main-Thread komplett fertig ist
        while (!AudioStream::is_audio_system_ready) {
            EE_SYNC(); // Verhindert CPU-Optimierungen beim Schleifen-Check
        }
    
        while(1) {
            // 2. Erst jetzt ist die Semaphore garantiert gültig erstellt!
            WaitSema(AudioStream::audioSemaID);
    
            if (AudioStream::update_exceptions == 0) {
                AudioStream *p;
                for (p = AudioStream::first_update; p; p = p->next_update) {
                    if (p->active) p->update();
                }
            }
            EE_SYNC();
        }
    }
    
    void AudioStream::initialize_memory(audio_block_t *data, unsigned int num) {
        // ... Deine bisherige Initialisierung (Speicher, Semaphore, Thread-Erstellung) ...
    
        s32 threadID = CreateThread(&threadParam);
        StartThread(threadID, NULL);
    
        // ... Timer-Setup ...
        s32 id = AllocTimerCounter();
        SetTimerHandler(id, s_audio_ticks, tick_cb, &s_audio_ticks);
        StartTimerCounter(id);
    
        // GANZ AM ENDE: Erst wenn alles (Sema, Thread, Timer) bereit ist, 
        // geben wir den Audio-Thread frei.
        EE_SYNC(); 
        is_audio_system_ready = true;
        EE_SYNC(); // Garantiert, dass die Flag im RAM landet, bevor dieser Thread etwas anderes tut
    }
    

    Das wäre vermutlich "sauberer", als die pauschale Thread-Priorität 100 zu setzen - da es dabei zu weiteren Problemen kommen könnte, wenn die main umfangreicher werden und somit länger brauchen sollte...

    Assisted by: https://share.google/aimode/nfpRxkKSHe0v2wUcO


Anmelden zum Antworten