是否可以使用 NAudio 从短 [] 而不是字节 [] 播放音频?

Posted

技术标签:

【中文标题】是否可以使用 NAudio 从短 [] 而不是字节 [] 播放音频?【英文标题】:Is it possible to play audio from a short[] instead of a byte[] with NAudio? 【发布时间】:2011-11-03 10:32:22 【问题描述】:

我正在尝试播放通过网络接收到的音频,该音频以一系列短裤的形式到达。我正在尝试使用 NAudio 的 WaveOut 对象来完成这项工作,但据我所知,这仅适用于字节 []。所以到目前为止我的问题是:

是否可以从 short[] 而不是 byte[] 播放音频? 如果是这样,该怎么做?

不多,但我现在拥有的是这个(显然是从一个字节[]播放音频):

protected override void Write(VoiceSource source, VoicePacket packet)

      if (connected)
      
            try
            
                dispatcher.Invoke((WriteCallback)provider.Write, packet.Data);
            
            catch (Exception e)
            
                MessageBox.Show(e.Message);
            
      

WriteCallback 定义为:

private delegate void WriteCallback(byte[] data);

我要写信的提供者是IWaveProvider 接口的实现。


编辑

我尝试了 Mark 告诉我的“技巧”,我的界面如下所示:

interface ISampleBuffer 
    byte[] Bytes  get; set; 
    short[] Shorts  get; set; 

缓冲区结构如下所示:

[StructLayout(LayoutKind.Explicit, Pack = 2)]
public struct SampleBuffer : ISampleBuffer

    [FieldOffset(0)]
    private byte[] bytes;
    [FieldOffset(0)]
    private short[] shorts;
    public byte[] Bytes  get  return bytes;  set  bytes = value;  
    public short[] Shorts  get  return shorts;  set  shorts = value;  

我已经这样实现了这个缓冲区:

short[] audio = decoder.Decode(packet.Data);
buffer.Bytes = new byte[audio.Length * 2];
buffer.Shorts = audio;
dispatcher.Invoke((WriteCallback)provider.Write, buffer.Bytes);

但是,每当我运行此设置时,我的日志中都会出现堆栈跟踪,并出现以下异常:

Object of type 'System.Int16[]' cannot be converted to type 'System.Byte[]'.

我的实现方式有问题吗?

【问题讨论】:

您误解了 byte[]short[] 的实际含义。数组是引用类型,因此byte[] 类型的变量实际上是指向堆上其他位置的指针。要做这样的事情,你需要在不安全的结构中使用fixed 字段,并且你需要在编译时知道数组的大小。 数组的大小在编译时是已知的。我得到的音频是 24o 长度的短数组。所以应该是可以的。我需要改变什么才能让它工作? 使用固定缓冲区:msdn.microsoft.com/en-us/library/zycewsya%28v=VS.100%29.aspx。您必须使用指针和其他错误将其转换为数组。虽然我觉得你从错误的角度解决问题,但如果你不得不求助于那些...... 好吧,由于实现它的复杂性,我宁愿不使用它。但我只是在探索各个角度,看看什么是有效的。我已经尝试过您的方法,从技术上讲,它可以顺利运行。但实际上,我什么也没听到。没有音频正在播放。 应该反过来。 buffer.Shorts = decoder.Decode... 然后将 buffer.Bytes 传递给你的函数 【参考方案1】:

NAudio 包含一个巧妙的技巧(或根据您的观点而定的肮脏技巧),它允许您从 short[] 转换为 byte[]。它被称为WaveBuffer 类,并通过使用具有显式布局的结构来工作。只需写入ShortBuffer 属性并读出ByteBuffer 属性即可。

我刚才blogged 谈到了它。它已经可靠运行了好几年,并且节省了使用Buffer.BlockCopy 的开销。

【讨论】:

你有这个类的具体使用例子吗?我真的不知道如何实现 IWaveBuffer 接口......:S【参考方案2】:

不确定 NAudio 位,但我可以帮助您将 short[] 转换为 byte[]Buffer.BlockCopy 在这里可能有用,因为它可以在不同类型的数组之间复制字节:

 short[] shortArray = ...
 byte[] byteArray = new byte[shortArray.Length*2];
 Buffer.BlockCopy(shortArray, 0, byteArray, 0, byteArray.Length);

但是,您可能会遇到字节序问题。您需要尝试一下,看看会发生什么。如果你需要交换组成一个短的两个字节,你可以手动交换它:

 for (int i=0; i<byteArray.Length; i += 2) 
     byte b = byteArray[i];
     byteArray[i] = byteArray[i+1];
     byteArray[i] = b;
 

或者,如果您希望事情更整洁,并且不想担心字节序转换,您可以使用BinaryWriter 为您进行转换:

BinaryWriter writer = new BinaryWriter(new MemoryStream(shortArray.Length*2));
foreach (short s in shortArray)
    writer.Write(s);

byte[] byteArray = ((MemoryStream)writer.BaseStream).ToArray();

【讨论】:

+1,很好的答案,而 Buffer.BlockCopy 是在 .NET 中执行此操作的“正确”方法,尽管 NAudio 确实有一个技巧,我在另一个答案中已经写过。 我已经尝试过了,从程序的角度来看,它没有任何抱怨。我得到的唯一问题是我根本没有音频...... 再次尝试您的最后一个建议后,我能够获得音频。然而,扬声器发出的声音与我给出的输入完全不同。即使我只是对着麦克风吹气,输出也会断断续续,有轻微的回声......我知道让这些东西工作起来很困难,但没那么难......

以上是关于是否可以使用 NAudio 从短 [] 而不是字节 [] 播放音频?的主要内容,如果未能解决你的问题,请参考以下文章

NAudio - wav 的流式字节 [] 使其播放缓慢

从 UDP 接收到的消息播放音频,点击声音(Naudio API)

NAudio在改变音高而不是文件时寻找一种从流中读取的方法

使用 NAudio 发送正在播放的音频

使用 C# 从字节数组中解码 dtmf

如何使用 C# NAudio 操作字节?