3

I have a rather baffling issue, a program that used to always work now only works once per reboot, when running it again I'm granted with:

Exception thrown: read access violation. this was 0xBF13D000.

I've extended a C++ project with C exports so I can use it from C#:

C exports:

KeyFinder::AudioData* new_audio_data(const unsigned frame_rate, const unsigned channels, const unsigned samples)
{
    auto audio_data = new KeyFinder::AudioData();
    audio_data->setFrameRate(frame_rate);
    audio_data->setChannels(channels);
    audio_data->addToSampleCount(samples);
    return audio_data;
}

void audio_data_set_sample(KeyFinder::AudioData* audio_data, const unsigned index, const double value)
{
    audio_data->setSample(index, value);
}

void keyfinder_progressive_chromagram(KeyFinder::KeyFinder* key_finder, KeyFinder::AudioData* audio_data, KeyFinder::Workspace* workspace)
{
    key_finder->progressiveChromagram(*audio_data, *workspace);
}

KeyFinder::key_t keyfinder_key_of_chromagram(KeyFinder::KeyFinder* key_finder, KeyFinder::Workspace* workspace)
{
    return key_finder->keyOfChromagram(*workspace);
}

enum key_t {
    A_MAJOR = 0,
    A_MINOR,
    B_FLAT_MAJOR,
    B_FLAT_MINOR,
    B_MAJOR,
    B_MINOR = 5,
    C_MAJOR,
    C_MINOR,
    D_FLAT_MAJOR,
    D_FLAT_MINOR,
    D_MAJOR = 10,
    D_MINOR,
    E_FLAT_MAJOR,
    E_FLAT_MINOR,
    E_MAJOR,
    E_MINOR = 15,
    F_MAJOR,
    F_MINOR,
    G_FLAT_MAJOR,
    G_FLAT_MINOR,
    G_MAJOR = 20,
    G_MINOR,
    A_FLAT_MAJOR,
    A_FLAT_MINOR,
    SILENCE = 24
};

C# declarations:

[DllImport("libKeyFinder", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr new_audio_data(
    uint frameRate, uint channels, uint samples);

[DllImport("libKeyFinder", CallingConvention = CallingConvention.Cdecl)]
private static extern void audio_data_set_sample(
    IntPtr audioData, uint index, double value);

[DllImport("libKeyFinder", CallingConvention = CallingConvention.Cdecl)]
private static extern void keyfinder_progressive_chromagram(IntPtr keyFinder, IntPtr audioData, IntPtr workspace);

[DllImport("libKeyFinder", CallingConvention = CallingConvention.Cdecl)]
private static extern Key keyfinder_key_of_chromagram(IntPtr keyFinder, IntPtr workspace);

public enum Key
{
    AMajor = 0,
    AMinor,
    BFlatMajor,
    BFlatMinor,
    BMajor,
    BMinor = 5,
    CMajor,
    CMinor,
    DFlatMajor,
    DFlatMinor,
    DMajor = 10,
    DMinor,
    EFlatMajor,
    EFlatMinor,
    EMajor,
    EMinor = 15,
    FMajor,
    FMinor,
    GFlatMajor,
    GFlatMinor,
    GMajor = 20,
    GMinor,
    AFlatMajor,
    AFlatMinor,
    Silence = 24
}

C# usage:

public void SetSample(uint index, double value)
{
    audio_data_set_sample(_audioData, index, value);
}

What is really puzzling is that when I debug it, the seemingly disposed/destroyed pointer is already visible in the C# part: SetSample._audioData. Initially, when new_audio_data is called from C# I get a valid pointer like 0x032fe940 but for some reason it becomes 0xBF13D000. Note that it always become the value 0xBF13D000 so I've searched about such value online in a hope to spot a known memory pattern but without success.

As I said, there hasn't been any changes to the program, so I'm at a total loss as on what could be causing this.

aybe
  • 15,516
  • 9
  • 57
  • 105
  • Have you installed new software that uses your sound card? Or whatever device you are sampling from? – Grantly Dec 09 '17 at 21:43
  • Do you have finalizer in a class with `SetSample`? – Evk Dec 09 '17 at 21:44
  • 1
    @Grantly nothing recently and I am getting sample data from a local WAV file. – aybe Dec 09 '17 at 21:45
  • @Evk yes I do have a finalizer, a breakpoint is set but never hit. – aybe Dec 09 '17 at 21:46
  • @Evk I've just commented out all dispose pattern related code but that didn't change anything :( – aybe Dec 09 '17 at 21:47
  • If invalid pointer is visible in C# - that means it is set somewhere in C# to that invalid value (well unless you pass that as out parameter to some unmanaged call). How large is that class? In how many places do you set that `_audioData`? Is multithreading involved somehow? – Evk Dec 09 '17 at 21:49
  • That pointer is set once in the constructor and its signature is `private readonly IntPtr _audioData;`. The class is really small, there's the constructor, that method to feed samples and a last one to get the result of the analysis. And no multi-threading is involved in the C# part, in the C++ however there is some like mutex lock/unlock. – aybe Dec 09 '17 at 21:51
  • And how that last method to get result looks like (I mean it's import signature)? Can you figure out at which point pointer changes to bad value? I suppose that happens after some external call to your C++ lib? – Evk Dec 09 '17 at 21:57
  • Updated my question, sorry there are two methods actually. Now interestingly, my `keyfinder_progressive_chromagram` calls `void progressiveChromagram(AudioData audio, Workspace& workspace);` which expects a reference but I guess I did correctly. However, that nullified pointer happens before I call onto that method ... – aybe Dec 09 '17 at 22:06
  • Can you clarify that the second time you run your program (after the first successful attempt), SetSample._audioData in your C# is getting correctly set to a valid pointer value when you call new_audio_data( ), but is then somehow getting changed before you try to use it in a subsequent call to your C/C++ library? Or is it possible new_audio_data( ) is returning something invalid? – Dylan Nicholson Dec 09 '17 at 22:22
  • (BTW I'm intrigued about your library - does it really detect the key signature of music in an audio file?) – Dylan Nicholson Dec 09 '17 at 22:23
  • Yes it is always valid, and find the library here : https://github.com/ibsh/libKeyFinder it's the only one open source with pretty good results (for DJing) compared to commercial alternatives ! – aybe Dec 09 '17 at 23:05
  • Are you closing the stream properly? Perhaps thats why you need a reboot for it to work once and once only – Grantly Dec 09 '17 at 23:14
  • Yes I do. Actually I am onto something, it seems that the library I'm using to read audio data is the culprit because when I create a trivial project where I feed silence (so without that library) I don't get all these funny things happening. Will be back to post an answer, hopefully ! – aybe Dec 10 '17 at 00:04

2 Answers2

0

Try add volatile for _audioData https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/volatile

Interreto
  • 122
  • 1
  • 3
0

It turned out that some of the native libraries needed to be all rebuilt using the same compiler version, now it works flawlessly !

aybe
  • 15,516
  • 9
  • 57
  • 105