C++/CLI noob: System.AccessViolationException

倾然丶 夕夏残阳落幕 提交于 2019-12-06 09:50:41

As promised on the EchoNest forum, here is my way of doing it. You can have it easier and go without CLI if you modify codegen.dll and provide a suitable exported function.

To main.cxx in codegen, add the following method:

extern "C" __declspec(dllexport) void GetCodeStringFromPcm(const float* pcm, uint numSamples, int start_offset, BSTR* sResultString)
{
  // pcm: a buffer of floats, mono, 11025 Hz
  Codegen * pCodegen = new Codegen(pcm, numSamples, start_offset);
  string code = pCodegen->getCodeString();

  // http://stackoverflow.com/questions/2573834/c-convert-string-or-char-to-wstring-or-wchar-t
  std::wstring ws(code.size(), L' '); // Overestimate number of code points.
  ws.resize(mbstowcs(&ws[0], code.c_str(), code.size())); // Shrink to fit.

  *sResultString = SysAllocStringLen(ws.data(), ws.size());
}

Now on the C# side, you can simply do this:

/// <summary>
/// Generates audio fringerprint for usage with Echonest.
/// </summary>
/// <param name="pcm">const float*, 4 byte per float in C++</param>
[DllImport("codegen.dll")]
private static extern void GetCodeStringFromPcm(float[] pcm, uint numSamples, int start_offset, [MarshalAs(UnmanagedType.BStr)] ref string sResultString);

Now you only need this special buffer of floats for the first parameter. You mention already having one, but as a bonus for everyone who has audio data of another format, below is a method for converting pretty much any audio file into the correct buffer of floats. Requirement is the BASS.NET audio library:

using BassLib = Un4seen.Bass.Bass;
using BassMix = Un4seen.Bass.AddOn.Mix.BassMix;

/// <summary>
/// Creates a fingerprint code for an audio track, using the codegen.dll.
/// </summary>
public string GetCodeStringFromFile(string fileName)
{
    // Read input stream
    int streamIn = BassLib.BASS_StreamCreateFile(fileName, 0, 0, Un4seen.Bass.BASSFlag.BASS_STREAM_DECODE);
    if (streamIn == 0) return null;

    // New mixer stream that allows us to read floating point samples. EchoNest requires mono data.
    int mixerStream = BassMix.BASS_Mixer_StreamCreate(targetSampleRate, 1, Un4seen.Bass.BASSFlag.BASS_STREAM_DECODE | Un4seen.Bass.BASSFlag.BASS_SAMPLE_FLOAT);
    BassMix.BASS_Mixer_StreamAddChannel(mixerStream, streamIn, Un4seen.Bass.BASSFlag.BASS_STREAM_DECODE | Un4seen.Bass.BASSFlag.BASS_MIXER_DOWNMIX);

    long bufferSizeInBytes = BassLib.BASS_ChannelSeconds2Bytes(mixerStream, 0.1f);
    double totalSeconds = BassLib.BASS_ChannelBytes2Seconds(streamIn, BassLib.BASS_ChannelGetLength(streamIn));

    // Use progress data in whatever way you need it.
    int progress = 0;
    List<float> resultData = new List<float>();

    while (true)
    {
        float[] data = new float[bufferSizeInBytes / 4];
        int readBytes = BassLib.BASS_ChannelGetData(mixerStream, data, (int)bufferSizeInBytes);
        if (readBytes <= 0) break;

        for (int i = 0; i < readBytes / 4; i++)
        {
            resultData.Add(data[i]);
        }

        double secondsPos = BassLib.BASS_ChannelBytes2Seconds(mixerStream, BassLib.BASS_ChannelGetPosition(mixerStream));
        progress = (int)(secondsPos / totalSeconds * 100);
    }

    BassLib.BASS_StreamFree(streamIn);
    BassLib.BASS_StreamFree(mixerStream);

    // We need to pass an array of samples to C.
    float[] resultArray = resultData.ToArray();

    // Clear list to prevent occupying too much memory.
    resultData.Clear();

    // Marshaller will pass float[] just fine to C.
    string resultCodegenData = string.Empty;
    GetCodeStringFromPcm(resultArray, (uint)resultArray.Length, 0, ref resultCodegenData);

    return resultCodegenData;
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!