如何从音频文件中获取样本的浮点数组
Posted
技术标签:
【中文标题】如何从音频文件中获取样本的浮点数组【英文标题】:How to get float array of samples from audio file 【发布时间】:2017-02-27 10:48:12 【问题描述】:我正在开发一个处理音频数据的 UWP 应用程序(适用于 Windows 10)。它在开始时以样本浮点数组的形式接收样本缓冲区,其中项目从 -1f 变为 1f。 早些时候,我使用了 NAudio.dll 1.8.0,它提供了所有必要的功能。 使用 WaveFileReader、waveBuffer.FloatBuffer、WaveFileWriter 类。 但是,当我完成这个应用程序并尝试构建发布版本时,得到了这个错误: ILT0042:当前不支持指针类型数组:'System.Int32*[]'。
我已经尝试解决了:
-
https://forums.xamarin.com/discussion/73169/uwp-10-build-fail-arrays-of-pointer-types-error
有建议删除指向 .dll 的链接,但我需要它。
我尝试使用 Manage NuGet Packages 安装相同版本的 NAudio,但 WaveFileReader、WaveFileWriter 不可用。
在 NAudio 开发人员的回答 (How to store a .wav file in Windows 10 with NAudio) 中,我阅读了有关使用 AudioGraph 的信息,但我只能在实时播放中构建样本的浮点数组,但我需要在音频文件上传后立即打包完整的样本。在录制过程或播放过程中获取样本的示例: https://docs.microsoft.com/ru-ru/windows/uwp/audio-video-camera/audio-graphs
这就是我需要帮助的原因:如何在音频文件上传后获取 FloatBuffer 来处理样本?例如,用于构建音频波或应用音频效果的计算。
提前谢谢你。
我尝试使用 FileStream 和 BitConverter.ToSingle(),但是与 NAudio 相比,我得到了不同的结果。 换句话说,我还在寻找解决方案。
private float[] GetBufferArray()
string _path = ApplicationData.Current.LocalFolder.Path.ToString() + "/track_1.mp3";
FileStream _stream = new FileStream(_path, FileMode.Open);
BinaryReader _binaryReader = new BinaryReader(_stream);
int _dataSize = _binaryReader.ReadInt32();
byte[] _byteBuffer = _binaryReader.ReadBytes(_dataSize);
int _sizeFloat = sizeof(float);
float[] _floatBuffer = new float[_byteBuffer.Length / _sizeFloat];
for (int i = 0, j = 0; i < _byteBuffer.Length - _sizeFloat; i += _sizeFloat, j++)
_floatBuffer[j] = BitConverter.ToSingle(_byteBuffer, i);
return _floatBuffer;
【问题讨论】:
链接:1) msdn.microsoft.com/en-us/library/ff827591.aspx 2) msdn.microsoft.com/ru-ru/library/… 如果您使用的是 Naudio,为什么不使用AudioFileReader
或 Mp3FileReader
类? AudioFileReader
将为您提供开箱即用的音频数据的 float[]。
您描述的功能确实有效,但在 UWP 项目使用 NAudio.dll 构建解决方案的发行版中,出现以下错误:''ILT0042:当前不支持指针类型数组:' System.Int32*[]''' 在 Nuget NAudio 包中,这些方法无法实现,我找不到它们的替代方法。
如果库本身抛出异常,您可能需要在尝试完全使用它之前寻找更兼容的版本或替代方案。我能想到的唯一其他选择是 MediaFoundationReader
类,它应该为您读取最常见的音频文件。
感谢您的帮助,仅找到一个使用 AudioFileReader 的示例。使用 MediaFoundationReader 查找音频数据的示例未满足。我一定会尝试这门课。
【参考方案1】:
在 UWP 中从音频文件读取样本的另一种方法是使用 AudioGraph API。它适用于 Windows10 支持的所有音频格式
这是一个示例代码
namespace AudioGraphAPI_read_samples_from_file
// App opens a file using FileOpenPicker and reads samples into array of
// floats using AudioGragh API
// Declare COM interface to access AudioBuffer
[ComImport]
[Guid("5B0D3235-4DBA-4D44-865E-8F1D0E4FD04D")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
unsafe interface IMemoryBufferByteAccess
void GetBuffer(out byte* buffer, out uint capacity);
public sealed partial class MainPage : Page
StorageFile mediaFile;
AudioGraph audioGraph;
AudioFileInputNode fileInputNode;
AudioFrameOutputNode frameOutputNode;
/// <summary>
/// We are going to fill this array with audio samples
/// This app loads only one channel
/// </summary>
float[] audioData;
/// <summary>
/// Current position in audioData array for loading audio samples
/// </summary>
int audioDataCurrentPosition = 0;
public MainPage()
this.InitializeComponent();
private async void Open_Button_Click(object sender, RoutedEventArgs e)
// We ask user to pick an audio file
FileOpenPicker filePicker = new FileOpenPicker();
filePicker.SuggestedStartLocation = PickerLocationId.MusicLibrary;
filePicker.FileTypeFilter.Add(".mp3");
filePicker.FileTypeFilter.Add(".wav");
filePicker.FileTypeFilter.Add(".wma");
filePicker.FileTypeFilter.Add(".m4a");
filePicker.ViewMode = PickerViewMode.Thumbnail;
mediaFile = await filePicker.PickSingleFileAsync();
if (mediaFile == null)
return;
// We load samples from file
await LoadAudioFromFile(mediaFile);
// We wait 5 sec
await Task.Delay(5000);
if (audioData == null)
ShowMessage("Error loading samples");
return;
// After LoadAudioFromFile method finished we can use audioData
// For example we can find max amplitude
float max = audioData[0];
for (int i = 1; i < audioData.Length; i++)
if (Math.Abs(audioData[i]) > Math.Abs(max))
max = audioData[i];
ShowMessage("Maximum is " + max.ToString());
private async void ShowMessage(string Message)
var dialog = new MessageDialog(Message);
await dialog.ShowAsync();
private async Task LoadAudioFromFile(StorageFile file)
// We initialize an instance of AudioGraph
AudioGraphSettings settings =
new AudioGraphSettings(
Windows.Media.Render.AudioRenderCategory.Media
);
CreateAudioGraphResult result1 = await AudioGraph.CreateAsync(settings);
if (result1.Status != AudioGraphCreationStatus.Success)
ShowMessage("AudioGraph creation error: " + result1.Status.ToString());
audioGraph = result1.Graph;
if (audioGraph == null)
return;
// We initialize FileInputNode
CreateAudioFileInputNodeResult result2 =
await audioGraph.CreateFileInputNodeAsync(file);
if (result2.Status != AudioFileNodeCreationStatus.Success)
ShowMessage("FileInputNode creation error: " + result2.Status.ToString());
fileInputNode = result2.FileInputNode;
if (fileInputNode == null)
return;
// We read audio file encoding properties to pass them to FrameOutputNode creator
AudioEncodingProperties audioEncodingProperties = fileInputNode.EncodingProperties;
// We initialize FrameOutputNode and connect it to fileInputNode
frameOutputNode = audioGraph.CreateFrameOutputNode(audioEncodingProperties);
fileInputNode.AddOutgoingConnection(frameOutputNode);
// We add a handler achiving the end of a file
fileInputNode.FileCompleted += FileInput_FileCompleted;
// We add a handler which will transfer every audio frame into audioData
audioGraph.QuantumStarted += AudioGraph_QuantumStarted;
// We initialize audioData
int numOfSamples = (int)Math.Ceiling(
(decimal)0.0000001
* fileInputNode.Duration.Ticks
* fileInputNode.EncodingProperties.SampleRate
);
audioData = new float[numOfSamples];
audioDataCurrentPosition = 0;
// We start process which will read audio file frame by frame
// and will generated events QuantumStarted when a frame is in memory
audioGraph.Start();
private void FileInput_FileCompleted(AudioFileInputNode sender, object args)
audioGraph.Stop();
private void AudioGraph_QuantumStarted(AudioGraph sender, object args)
AudioFrame frame = frameOutputNode.GetFrame();
ProcessInputFrame(frame);
unsafe private void ProcessInputFrame(AudioFrame frame)
using (AudioBuffer buffer = frame.LockBuffer(AudioBufferAccessMode.Read))
using (IMemoryBufferReference reference = buffer.CreateReference())
// We get data from current buffer
((IMemoryBufferByteAccess)reference).GetBuffer(
out byte* dataInBytes,
out uint capacityInBytes
);
// We discard first frame; it's full of zeros because of latency
if (audioGraph.CompletedQuantumCount == 1) return;
float* dataInFloat = (float*)dataInBytes;
uint capacityInFloat = capacityInBytes / sizeof(float);
// Number of channels defines step between samples in buffer
uint step = fileInputNode.EncodingProperties.ChannelCount;
// We transfer audio samples from buffer into audioData
for (uint i = 0; i < capacityInFloat; i += step)
if (audioDataCurrentPosition < audioData.Length)
audioData[audioDataCurrentPosition] = dataInFloat[i];
audioDataCurrentPosition++;
已编辑:它解决了问题,因为它将文件中的样本读取到浮点数组中
【讨论】:
仅仅链接到您自己的库或教程并不是一个好的答案。链接到它,解释它为什么解决问题,提供如何解决问题的代码,并否认你编写了它,这样可以得到更好的答案。见:What signifies “Good” self promotion?【参考方案2】:第一种从 Wav 文件中获取 AudioData 的流行方式。
感谢 PI 用户的回答How to read the data in a wav file to an array,我已经解决了 UWP 项目中浮动数组中读取 wav 文件的问题。 但是当使用 AudioGraph 录制到 wav 文件时,文件的结构与标准的不同(也许,只有在我的项目中存在这样的问题)。它导致不可预测的结果。我们收到 value1263424842 而不是可预测的 544501094 获取格式 ID。之后,以下所有值都显示不正确。我在字节中顺序搜索找到了正确的 id。我意识到 AudioGraph 在录制的 wav 文件中添加了额外的数据块,但录制的格式仍然是 PCM。这个额外的数据块看起来像关于文件格式的数据,但它也包含空值、空字节。我找不到任何相关信息,也许这里有人知道? PI 的解决方案我已经根据自己的需要进行了更改。这就是我所拥有的:
using (FileStream fs = File.Open(filename, FileMode.Open))
BinaryReader reader = new BinaryReader(fs);
int chunkID = reader.ReadInt32();
int fileSize = reader.ReadInt32();
int riffType = reader.ReadInt32();
int fmtID;
long _position = reader.BaseStream.Position;
while (_position != reader.BaseStream.Length-1)
reader.BaseStream.Position = _position;
int _fmtId = reader.ReadInt32();
if (_fmtId == 544501094)
fmtID = _fmtId;
break;
_position++;
int fmtSize = reader.ReadInt32();
int fmtCode = reader.ReadInt16();
int channels = reader.ReadInt16();
int sampleRate = reader.ReadInt32();
int byteRate = reader.ReadInt32();
int fmtBlockAlign = reader.ReadInt16();
int bitDepth = reader.ReadInt16();
int fmtExtraSize;
if (fmtSize == 18)
fmtExtraSize = reader.ReadInt16();
reader.ReadBytes(fmtExtraSize);
int dataID = reader.ReadInt32();
int dataSize = reader.ReadInt32();
byte[] byteArray = reader.ReadBytes(dataSize);
int bytesForSamp = bitDepth / 8;
int samps = dataSize / bytesForSamp;
float[] asFloat = null;
switch (bitDepth)
case 16:
Int16[] asInt16 = new Int16[samps];
Buffer.BlockCopy(byteArray, 0, asInt16, 0, dataSize);
IEnumerable<float> tempInt16 =
from i in asInt16
select i / (float)Int16.MaxValue;
asFloat = tempInt16.ToArray();
break;
default:
return false;
//For one channel wav audio
floatLeftBuffer.AddRange(asFloat);
从缓冲区到文件记录有逆算法。目前,这是处理 wav 文件的唯一一种正确算法,它允许获取音频数据。 使用这篇文章使用 AudioGraph - https://docs.microsoft.com/ru-ru/windows/uwp/audio-video-camera/audio-graphs。请注意,您可以使用 AudioEncodingQuality 从 MIC 到文件设置录制格式的必要数据。
使用 NAudio 从 Nugget 包中获取 AudioData 的第二种方法。
我使用了 MediaFoundationReader 类。
float[] floatBuffer;
using (MediaFoundationReader media = new MediaFoundationReader(path))
int _byteBuffer32_length = (int)media.Length * 2;
int _floatBuffer_length = _byteBuffer32_length / sizeof(float);
IWaveProvider stream32 = new Wave16ToFloatProvider(media);
WaveBuffer _waveBuffer = new WaveBuffer(_byteBuffer32_length);
stream32.Read(_waveBuffer, 0, (int)_byteBuffer32_length);
floatBuffer = new float[_floatBuffer_length];
for (int i = 0; i < _floatBuffer_length; i++)
floatBuffer[i] = _waveBuffer.FloatBuffer[i];
比较我注意到的两种方式:
收到的样本值相差 1/1 000 000。我不能说哪种方法更精确(如果您知道,将很高兴听到); 获取 AudioData 的第二种方法也适用于 MP3 文件。如果您发现任何错误或对此有意见,欢迎。
【讨论】:
【参考方案3】:进口声明
using NAudio.Wave;
using NAudio.Wave.SampleProviders;
内部函数
AudioFileReader reader = new AudioFileReader(filename);
ISampleProvider isp = reader.ToSampleProvider();
float[] buffer = new float[reader.Length / 2];
isp.Read(buffer, 0, buffer.Length);
缓冲区数组将包含 32 位 IEEE 浮点样本。 这是使用 NAudio Nuget Package Visual Studio。
【讨论】:
浮点数组大小应该是“样本数”。 reader.Length 以字节为单位给出音频的大小。现在由于比特率是 16 位(2 个字节),每个样本是 2 个字节。因此,字节到无样本转换需要除以 2。如果 wav 文件是 32 位比特率,则除以 4。以上是关于如何从音频文件中获取样本的浮点数组的主要内容,如果未能解决你的问题,请参考以下文章