私は娘のジュニアでのキャリアデーのための「クールなデモ」をまとめようとしています。5日間で高値になるため、エコープリントライブラリを使用して無線(OTA)オーディオ認識を実行しようとしています。私はC++の「helloworld」よりもはるかに遠くまで行ったことがなく、C ++ /CLIを使用してechoprintcodegenライブラリをラップし、C#から呼び出すことができるようにしようとしています。これが私のヘッダーファイルです:
// 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マイククラスを使用して音声を録音しています。これはbyte[]の配列を返すので、バイトをfloatに変換してから、ラッパーを介して次のようにCodegenクラスに渡します(C#)。
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が発生します。
ソースコード全体は、GitHubでVS 2010SP1またはVS11ソリューションとして入手できます:https ://github.com/developmentalmadness/echoprint-net/tree/3c48d3783136188bfa213d3e9fd1ebea0f151bed
そのURLは、現在問題が発生しているリビジョンを指している必要があります。
編集 私はここで提案を試しました:C ++/CLIからC++-DLLを呼び出すときのAccessViolation
#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;
}
}
それでもアクセス違反が発生しますが、デバッガーがクラッシュした後、ネイティブコードにドロップしました(自分でそこに到達する方法がわかりません)。そして、コンストラクター内で爆撃します。ポインター(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()メソッドで爆撃することを想像します:
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);
}