Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Crashes type EXC_BAD_ACCESS KERN_INVALID_ADDRESS, SuperpoweredIOSAudioIO incorrect data writing #787

Open
AndrewRudyk opened this issue Sep 27, 2024 · 13 comments

Comments

@AndrewRudyk
Copy link

Superpowered version: 2.6.6

Describe the bug
My app uses Superpowered SDK to create different effects: AutomaticVocalPitchCorrection, ThreeBandEQ, TimeStretching, Bitcrusher, etc.
Now I have a lot of reports from Firebase Crashlytics
image
image
A lot of problems arise in the variable "samplerate" from class SuperpoweredIOSAudioIO that comes with the SDK.

Xcode shows a lot of Warnings in this class, for example
"This code path does interprocess communication underneath which can cause non-deterministic delays. Investigate ways to do this work off the main thread

There are reports of this code path causing UI hangs. See Xcode Organizer for details . Look for the report that shows calls to -[AVAudioSession currentRoute] underneath"

I think that SuperpoweredIOSAudioIO writes incorrect data to the samplerate variable, which is then used by other SuperpoweredSDK objects.

Can you fix this?

Steps to Reproduce
Due to the nature of the bug, the reproduction paths vary, and the crash does not happen predictably, making it challenging to outline specific steps to trigger the issue.

Device information
Please list which devices have this bug.

  • Device: from iPhone 11 to iPhone 15
  • OS: iOS 14 - 18
@ivannador
Copy link
Contributor

Could you please try with the latest update to the SuperpoweredIOSAudioIO class?

caf2102

@AndrewRudyk
Copy link
Author

AndrewRudyk commented Sep 30, 2024

Could you please try with the latest update to the SuperpoweredIOSAudioIO class?

caf2102

Hello! I'll check it and get back to you with the results. We will have more results after we release the app.
Thank you.

@AndrewRudyk
Copy link
Author

Hello,
I updated Superpowered SDK to 2.2.8 and sent our app to AppStore.
And I see current statistics in Firebase,
image
image
image
image
But something still goes wrong:)

I saw comment in SuperpoweredIOSAudioIO
image
Perhaps this problem is still too common

@gaborszanto
Copy link
Member

Some memory handling is wrong in your Superpowered.mm file, and therefore the Superpowered features you're using are crashing. Please check your buffer sizes.

@AndrewRudyk
Copy link
Author

Hello,
I took code for Superpowered.mm from your examples in repo and preferredBufferSize == 12 took from repo too.
How can I change or check the preferredBufferSize?

I use effect objects (Recorder, Flanger, Filter, etc) from SuperpoweredSDK in chain.

- (void)startProcessing {
    if (audioIO == nil) {
        audioIO = [[SuperpoweredIOSAudioIO alloc] initWithDelegate:(id<SuperpoweredIOSAudioIODelegate>)self
                                               preferredBufferSize:12
                                               preferredSamplerate:mySamplerate
                                              audioSessionCategory:AVAudioSessionCategoryPlayAndRecord
                                                          channels:2
                                           audioProcessingCallback:audioProcessing
                                                        clientdata:(__bridge void *)self];
        [audioIO start];
    }
}

// ...

// This code crashes
// SuperpoweredIOSAudioIO calls this closure

static bool audioProcessing(void *clientdata, float *input, float *output, unsigned int numberOfFrames, unsigned int samplerate, uint64_t hostTime) {
    __unsafe_unretained SuperpoweredBridge *self = (__bridge SuperpoweredBridge *)clientdata;
     
    if (self->limiter) {
        self->limiter->samplerate = samplerate;
        self->limiter->process(input, input, numberOfFrames);
    }
    
    if (self->compressor) {
        self->compressor->samplerate = samplerate;
        self->compressor->process(input, input, numberOfFrames);
    }
    
    // preparing for recording
    self->playerA->outputSamplerate = samplerate;
    if (self->_recording && !self->recorderIsPrepared) {
        self->recorderIsPrepared = self->recorder->prepare(self->test2Url, samplerate, false, 0);
    }

    if (self->_recording) {
        self->recorder->recordInterleaved(input, numberOfFrames);
    }

    if (self->spVoicetune) {
        self->spVoicetune->samplerate = samplerate;
        self->spVoicetune->process(input, input, true, numberOfFrames);
    }
    
    if (self->timeStretching) {
        self->timeStretching->samplerate = samplerate;
        
        self->timeStretching->addInput(input, numberOfFrames);
        self->timeStretching->getOutput(input, numberOfFrames);
    }
    
    if (self->threeBandEQ) {
        self->threeBandEQ->samplerate = samplerate;
        self->threeBandEQ->process(input, input, numberOfFrames);
    }
    
    // ...
    
    if (self->echo) {
        self->echo->samplerate = samplerate;
        self->echo->process(input, input, numberOfFrames);
    }
    
    if (self->delayIsEnabled) {
        self->delay->samplerate = samplerate;
        const float *delayOutput = self->delay->process(input, numberOfFrames);
        memcpy(input, delayOutput, numberOfFrames * sizeof(float) * 2);
    }
    
    // ...
    
    if (self->_recording) {
        Superpowered::Volume(input, output, 0.6f, 0.6f, numberOfFrames);
        self->playerA->processStereo(output, true, numberOfFrames, 0.6f);
    }
    
    return true;
}

@ivannador
Copy link
Contributor

Two things at first glance:

  1. You shouldn't call recorder->prepare(...) on the real-time thread. (Check here)
  2. It would be wise to check if float *input is not NULL. If for some reason the audio system can't render into the input buffers, it can be NULL.

@AndrewRudyk
Copy link
Author

AndrewRudyk commented Oct 14, 2024

Two things at first glance:

  1. You shouldn't call recorder->prepare(...) on the real-time thread. (Check here)
  2. It would be wise to check if float *input is not NULL. If for some reason the audio system can't render into the input buffers, it can be NULL.

I have two notes and questions:

  1. I had to move 'recorder->prepare(...)' from another Prepare function to this location because 'static bool audioProcessing(...)' returned a different Samplerate value than '- (void)startProcessing'. For example, I had set 48000 in '- (void)startProcessing', 'static bool audioProcessing(...)' returned 44100 and I had problems in recording.

  2. If the input can be NULL, why do the crashes not occur at the beginning of the chain?

@AndrewRudyk
Copy link
Author

Hello,
I have a question:
SuperpoweredSDK's objects modify input-data in "static bool audioProcessing(...)" by chain. One of the objects modifies data in such a way that next object can fail when using this data. How can I check this data and fix it?

@ivannador
Copy link
Contributor

Hey Andrew!

Best way would be to log specific edge points of the buffer that goes through the chain and capture where it might go wrong. Something seems to be amiss at some point, so definitely a good idea to log the buffer.

Also, you could use a fixed sample rate on the recorder when preparing, and use a Resampler if the actual sample rate differs in the real-time loop.

@AndrewRudyk
Copy link
Author

Hello Ivan!
Could you please explain how to define specific edge points of the buffer.
Thank you very much!

@ivannador
Copy link
Contributor

Log values around the supposed end of the buffer, check if values get messed up that causes one of the steps in the chain to crash.

@ivannador
Copy link
Contributor

Hey @AndrewRudyk, any updates from your side?

@AndrewRudyk
Copy link
Author

AndrewRudyk commented Nov 8, 2024

Hi
I don't have any updates, I haven't researched it from your point of view yet.

I'll return to this topic next spring, when I'm preparing for the next release.

Sorry

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants