私は現在、クロマプリントを使用して曲を識別し、acoustid.org からデータを取得する Win 10 アプリを作成しようとしています。
しかし、私の ExtractPCM-Methods は間違った値を返します。多くの値に返される最初の問題。237 秒に対して 246 秒という多くのデータが必要です。2 番目の値は fpcalc.exe によって返されます。
2 つ目の問題は、私の ExtractPCM の実装が、オープンソース プロジェクトで見つけた実際の実装とは完全に異なる値を返すことです。私はオーディオをよく理解していませんが、私の価値観は明らかに間違っていると思います。
コードをテストするために使用する参照実装はAresRpgです。BASS を使用して PCM データを抽出します。
Data BASS の戻り値は 10.054 のゼロから始まり、次のように続きます。
-1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -1 -1 0 0 0 -1 0 0 0 0 0 0 0 0 0 -1 - 1 0
2 秒マークの最後の最後の値は次のようになります。
612 627 635 647 655 656 662 662 663 658
私の実装は 10.528 ゼロで始まり、その後に次のものが続きます。
-15194 11946 21344 12111 25732 12414 -28715 12748 6098 12973 -31198 13046 28784 12844 21248 12797 1592 13165 22544 13294 -20448
そして、で終わる
13636 -17580 -18770 -17639 -29613 -17604 10168 -17608 20472 -17618
私のExtractPCMは次のようになります
public static async Task<PcmInfo> ExtractPcm(IStorageFile file)
{
int sampleRate = 0;
int chanels = 0;
double seconds = 0;
short[] erg = null;
var graphResult = await Windows.Media.Audio.AudioGraph.CreateAsync(new Windows.Media.Audio.AudioGraphSettings(Windows.Media.Render.AudioRenderCategory.Media));
using (var graph = graphResult.Graph)
{
var encodingProperties = Windows.Media.MediaProperties.AudioEncodingProperties.CreatePcm(22050, 1, 16);
var fileResult = await graph.CreateFileInputNodeAsync(file);
var fileInput = fileResult.FileInputNode;
{
var output = graph.CreateFrameOutputNode(encodingProperties);
{
fileInput.AddOutgoingConnection(output);
var duratio = fileInput.Duration.TotalSeconds;
var taskSource = new TaskCompletionSource<object>();
fileInput.FileCompleted += (source, e) =>
{
graph.Stop();
output.Stop();
taskSource.TrySetResult(null);
};
graph.Start();
await taskSource.Task;
var audioFrame = output.GetFrame();
using (var lockedBuffer = audioFrame.LockBuffer(Windows.Media.AudioBufferAccessMode.ReadWrite))
{
using (var refference = lockedBuffer.CreateReference())
{
await Task.Run(() =>
{
unsafe
{
var memoryByteAccess = refference as IMemoryBufferByteAccess;
byte* p;
uint capacity;
memoryByteAccess.GetBuffer(out p, out capacity);
chanels = (int)output.EncodingProperties.ChannelCount;
sampleRate = (int)output.EncodingProperties.SampleRate;
int length = (int)(capacity / sizeof(Int16));
Int16* b = (Int16*)(p);
erg = new short[length];
for (int i = 0; i < erg.Length; i++)
erg[i] = b[i];
seconds = length / (double)output.EncodingProperties.SampleRate / chanels;
}
});
}
}
}
}
var sb = new StringBuilder();
foreach (var item in erg.SkipWhile(y => y == 0).Take(sampleRate * 2))
{
sb.Append($"{item} ");
}
System.Diagnostics.Debug.WriteLine($"Leading zeros {erg.TakeWhile(y => y == 0).Count()} ");
System.Diagnostics.Debug.WriteLine(sb);
return new PcmInfo() { Data = erg, Seconds = seconds, SampleRate = sampleRate, Chanels = chanels };
}
}
AresRPG は次のコードを使用してデータを読み取ります。
public static System.Int16[] ExtractPcm(String file, out double seconds)
{
seconds = 0;
int handle = Bass.BASS_StreamCreateFile(file, 0, 0, BASSFlag.BASS_STREAM_DECODE | BASSFlag.BASS_SAMPLE_MONO | BASSFlag.BASS_STREAM_PRESCAN);
if (handle == 0)
{
BASSError error = Bass.BASS_ErrorGetCode();
// System.Console.WriteLine("ERROR: " + error);
return null;
}
long length = Bass.BASS_ChannelGetLength(handle);
seconds = Bass.BASS_ChannelBytes2Seconds(handle, length);
int mixHandle = Un4seen.Bass.AddOn.Mix.BassMix.BASS_Mixer_StreamCreate(22050, 1, BASSFlag.BASS_STREAM_DECODE);
if (mixHandle == 0)
{
BASSError error = Bass.BASS_ErrorGetCode();
// System.Console.WriteLine("ERROR: " + error);
return null;
}
if (!Un4seen.Bass.AddOn.Mix.BassMix.BASS_Mixer_StreamAddChannel(mixHandle, handle, BASSFlag.BASS_DEFAULT))
{
BASSError error = Bass.BASS_ErrorGetCode();
// System.Console.WriteLine("ERROR: " + error);
return null;
}
List<System.Int16> data = new List<System.Int16>();
while (true)
{
Int16[] buffer = new Int16[512];
int num = Bass.BASS_ChannelGetData(mixHandle, buffer, buffer.Length * 2);
if (num == -1)
{
BASSError error = Bass.BASS_ErrorGetCode();
Bass.BASS_StreamFree(handle);
// System.Console.WriteLine("ERROR: " + error);
return null;
}
for (int i = 0; i < num / 2; ++i)
{
if (i < buffer.Length)
data.Add(buffer[i]);
else
throw new ApplicationException();
}
if (num < buffer.Length * 2)
break;
}
Bass.BASS_StreamFree(handle);
try
{
return data.ToArray();
}
catch (System.OutOfMemoryException)
{
System.GC.Collect();
return null;
}
}
編集
安全でないブロックを次のように変更しました。
await Task.Run(() =>
{
unsafe
{
var memoryByteAccess = refference as IMemoryBufferByteAccess;
byte* p;
uint capacity;
memoryByteAccess.GetBuffer(out p, out capacity);
chanels = (int)output.EncodingProperties.ChannelCount;
sampleRate = (int)output.EncodingProperties.SampleRate;
int length = Math.Min((int)(sampleRate * duratio) * chanels, (int)(capacity / sizeof(float)));
float* b = (float*)(p);
erg = new short[length];
for (int i = 0; i < erg.Length; i++)
erg[i] = (Int16)(b[i] * Int16.MaxValue);
seconds = length / (double)output.EncodingProperties.SampleRate / chanels;
}
});
これは、SubType が float でない場合でも、データの格納に使用された形式が浮動小数点だったためです。
今、私は曲として認識できるデータを取得します (私は人として) が、それでも何かがおかしい :(
私の抽出は少し長く、より大きくなっています。したがって、両方のバージョンを同時に再生すると、開始は同期しているように見えますが、すぐに両方のトラックが異なる速度で再生されることがわかります。最終的には約1秒の差です。音量が指紋に影響を与える可能性はないと思いますが、デコード速度が遅いほど違いが生じる可能性があります.
私がテストに使用したサンプルは、acousid.org によって識別される Adele の Rolling Deep でした。しかし、編集したバージョンを投稿することは許可されていないと思います。
そこで、このcc Songを使用して、左側のチャネルに私のデコードを、右側のチャネルに Ares のデコードを含むwav ファイルを作成しました。
残念ながら、この曲は acusticId データベースにありません。少なくとも指紋は見つかりません。