2012-04-20 30 views
2

私は娘のジュニアでキャリアの日のために "クールなデモ"をまとめようとしています。 5日で最高と私は空気(OTA)音声認識を実行するためにthe echoprint libraryを使用しようとしています。私はC++で "hello world"よりもずっと遠く離れたことはなく、C++/CLIを使ってechoprint codegenライブラリをラップしてC#から呼び出せるようにしようとしています。ここに私のヘッダファイルがあります:C++/CLIのnoob:System.AccessViolationException

// echoprint-cli.h 

#pragma once 

#include "Codegen.h"; 

using namespace System; 

namespace echoprintcli { 

    public ref class CodegenCLI 
    { 
    public: 
     String^ getCodeString(array<float>^ buffer, unsigned int samples, int start_offset); 
    }; 
} 

ここに私の実装です:

#include "stdafx.h" 
#include <msclr\marshal_cppstd.h> 
#include "echoprint-cli.h" 

using namespace System; 
using namespace System::Runtime::InteropServices; 
using namespace msclr::interop; 

namespace echoprintcli { 
    String^ CodegenCLI::getCodeString(array<float>^ buffer, unsigned int samples, int start_offset){ 
     String^ result = String::Empty; 

     if(buffer->Length > 0){ 
      GCHandle h = GCHandle::Alloc(buffer, System::Runtime::InteropServices::GCHandleType::Pinned); 

      try{ 
       float* pcm = (float*)(void*)h.AddrOfPinnedObject(); 
       Codegen* codegen = new Codegen(pcm, samples, start_offset); //System.AccessViolationException here 
       std::string code; 
       try{ 
        code = codegen->getCodeString(); 
       }finally{ 
        delete codegen; 
       } 
       result = marshal_as<String^>(code); 
      } 
      finally{ 
       h.Free(); 
      } 
     } 
     return result; 
    } 
} 

私はオーディオを記録するためにXNA Microphoneクラスを使用しています。これは、[]バイトの配列を返しますので、私はfloat型にバイトを変換し、この(C#の)のようなcodegenのクラスに私のラッパーを通してそれを渡している:

 var mic = Microphone.Default; 
     Log(String.Format("Using '{0}' as audio input...", mic.Name)); 
     var buffer = new byte[mic.GetSampleSizeInBytes(TimeSpan.FromSeconds(22))]; 

     int bytesRead = 0; 
     string fileName = String.Empty; 

     try 
     { 
      mic.Start(); 
      try 
      { 
       Log(String.Format("{0:HH:mm:ss} Start recording audio stream...", DateTime.Now)); 
       while (bytesRead < buffer.Length) 
       { 
        Thread.Sleep(1000); 
        var bytes = mic.GetData(buffer, bytesRead, (buffer.Length - bytesRead)); 
        Log(String.Format("{0:HH:mm:ss} Saving {1} bytes to stream...", DateTime.Now, bytes)); 
        bytesRead += bytes; 
       } 
       Log(String.Format("{0:HH:mm:ss} Finished recording audio stream...", DateTime.Now)); 
      } 
      finally 
      { 
       mic.Stop(); 
      } 

      Func<byte, float> convert = (b) => System.Convert.ToSingle(b); 
      var converter = new Converter<byte, float>(convert); 
      float[] pcm = Array.ConvertAll<byte, float>(buffer, converter); 

      Log(String.Format("{0:HH:mm:ss} Generating audio fingerprint...", DateTime.Now)); 
      var codeg = new CodegenCLI(); 
      String code = codeg.getCodeString(pcm, (uint)pcm.Length, 0); 

しかし、とき、私のC++/CLI方式( getCodeString)がネイティブメソッドを呼び出すと、私はSysetem.AccessViolationExceptionを取得します。 https://github.com/developmentalmadness/echoprint-net/tree/3c48d3783136188bfa213d3e9fd1ebea0f151bed

URLは現在、問題が発生していリビジョンを指している必要があります:

全体のソースコードは、VS 2010 SP1またはgithubの上のVS 11ソリューションとして提供されています。

EDIT 私はここでの提案を試してみました:AccessViolation, when calling C++-DLL from C++/CLI

#include "stdafx.h" 
#include <msclr\marshal_cppstd.h> 
#include "echoprint-cli.h" 

using namespace System; 
using namespace System::Runtime::InteropServices; 
using namespace msclr::interop; 

namespace echoprintcli { 
    String^ CodegenCLI::getCodeString(array<float>^ buffer, unsigned int samples, int start_offset){ 
     String^ result = String::Empty; 

     IntPtr p = Marshal::AllocHGlobal(buffer->Length * sizeof(float)); 
     try{ 
      pin_ptr<float> pcm = static_cast<float*>(p.ToPointer()); 
      Codegen* codegen = new Codegen(pcm, samples, start_offset); // System.AccessViolationException here 
      std::string code; 
      try{ 
       code = codegen->getCodeString(); 
      }finally{ 
       delete codegen; 
      } 
      result = marshal_as<String^>(code); 
     } 
     finally{ 
      Marshal::FreeHGlobal(p); 
     } 
     return result; 
    } 
} 

そして、私はまだアクセス違反を取得しますが、デバッガをクラッシュすると、ネイティブコードに私を落とした後、(私はそこに着くためにどのように見当がつかない私自身)。そしてそれはctorの内部に爆弾を投下する。ポインタ(PCM)は、アドレス0.0000000の値を持っていますが、私はここのソースを表示するよりも、自分が他のコードにデバッグする方法を見つけ出すことはできません。

Codegen::Codegen(const float* pcm, unsigned int numSamples, int start_offset) { 
    if (Params::AudioStreamInput::MaxSamples < (uint)numSamples) 
     throw std::runtime_error("File was too big\n"); 

    Whitening *pWhitening = new Whitening(pcm, numSamples); //System.AccessViolationException 

デバッグできずに、私ができます唯一のスタック2つのステップダウン従うことを前提としています

Whitening::Whitening(const float* pSamples, uint numSamples) : 
    _pSamples(pSamples), _NumSamples(numSamples) { 
    Init(); 
} 

をそして、私はどこかのinit()メソッド内で爆弾を想像:EchoNestフォーラムに約束したよう

void Whitening::Init() { 
    int i; 
    _p = 40; 

    _R = (float *)malloc((_p+1)*sizeof(float)); 
    for (i = 0; i <= _p; ++i) { _R[i] = 0.0; } 
    _R[0] = 0.001; 

    _Xo = (float *)malloc((_p+1)*sizeof(float)); 
    for (i = 0; i < _p; ++i) { _Xo[i] = 0.0; } 

    _ai = (float *)malloc((_p+1)*sizeof(float)); 
    _whitened = (float*) malloc(sizeof(float)*_NumSamples); 
} 
+0

ネイティブコードにデバッグするとどうなりますか? – Nick

+0

私の編集内容を参照してください –

+1

プロジェクト設定で「ネイティブデバッグ」を有効にしましたか? – Nick

答えて

2

、ここでやっての私の方法は、それ。 codegen.dllを変更し、適切なエクスポート関数を提供すると、CLIを使用せずに簡単に実行できます。コード生成でmain.cxx

、次のメソッドを追加:C#の側に

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()); 
} 

を、あなたは、単にこれを行うことができます。

/// <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); 

を今、あなただけのためにフロートのこの特別なバッファを必要とします最初のパラメータ。あなたはすでに1つを持っていると言いますが、別のフォーマットのオーディオデータを持っている人のためのボーナスとして、以下はオーディオファイルのほとんどを正しいフロートバッファに変換する方法です。要件は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; 
} 
+0

Hello floele私はそのプロジェクトをコンパイルすることに多くの問題があるので、編集したDLLを教えてください – alizx

+1

@alizx確かに、https://cdburnerxp.se/downloads/additional/codegen_x64.dllとここでhttps:// cdburnerxpをダウンロードしてください。 se/downloads/additional/codegen_x86.dll – floele

+0

は両方ともENMFPであり、EchoprintシグネチャジェネレータDLLは同じcodegen.dll名を持っていますか?その種の私のために混乱している! – alizx

関連する問題