jss*_*jrm 11 c# audio wav low-level
任何人都知道在C#中创建ARBITRARY声波并从扬声器播放的合理方法吗?
这个问题已经不断出现多年,我总是在经历了很多失败之后放弃了它而没有找到解决方案.
我想做的就像一个反向可视化器,也就是说,我不想从声音生成"数字",我想从数字生成声音.
就像我提供的一个函数,我提供了采样率,样本大小和声音数据(例如整数数组),它会从中生成相应的wav文件(实时声音播放将是理想的,但我'对此也非常满意).
我知道wav文件规格遍布整个interweb,并且确实做了几次创建上述功能的尝试,但是一旦我开始弄乱每个样本的比特等等......它就变成了一个巨大的,无法控制的混乱.
这还没有以任何方式完成吗?我不会介意是什么用途,只要有一个.NET托管包装它(我可以从最近VS访问时间).XNA不支持低级音频.还发现了几个声称可以实现类似功能的例子,但它们要么根本不起作用,要么做一些完全不同的事情.
谢谢.
这看起来很有趣所以我打破了一个简单的应用:
您可以轻松更改采样率,音频和采样持续时间.代码非常丑陋且空间效率低但是有效.以下是一个完整的命令行应用程序:
using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
namespace playwav
{
class Program
{
[DllImport("winmm.dll", EntryPoint = "PlaySound", SetLastError = true)]
private extern static int PlaySound(byte[] wavData, IntPtr hModule, PlaySoundFlags flags);
//#define SND_SYNC 0x0000 /* play synchronously (default) */
//#define SND_ASYNC 0x0001 /* play asynchronously */
//#define SND_NODEFAULT 0x0002 /* silence (!default) if sound not found */
//#define SND_MEMORY 0x0004 /* pszSound points to a memory file */
//#define SND_LOOP 0x0008 /* loop the sound until next sndPlaySound */
//#define SND_NOSTOP 0x0010 /* don't stop any currently playing sound */
//#define SND_NOWAIT 0x00002000L /* don't wait if the driver is busy */
//#define SND_ALIAS 0x00010000L /* name is a registry alias */
//#define SND_ALIAS_ID 0x00110000L /* alias is a predefined ID */
//#define SND_FILENAME 0x00020000L /* name is file name */
//#define SND_RESOURCE 0x00040004L /* name is resource name or atom */
enum PlaySoundFlags
{
SND_SYNC = 0x0000,
SND_ASYNC = 0x0001,
SND_MEMORY = 0x0004
}
// Play a wav file appearing in a byte array
static void PlayWav(byte[] wav)
{
PlaySound(wav, System.IntPtr.Zero, PlaySoundFlags.SND_MEMORY | PlaySoundFlags.SND_SYNC);
}
static byte[] ConvertSamplesToWavFileFormat(short[] left, short[] right, int sampleRate)
{
Debug.Assert(left.Length == right.Length);
const int channelCount = 2;
int sampleSize = sizeof(short) * channelCount * left.Length;
int totalSize = 12 + 24 + 8 + sampleSize;
byte[] wav = new byte[totalSize];
int b = 0;
// RIFF header
wav[b++] = (byte)'R';
wav[b++] = (byte)'I';
wav[b++] = (byte)'F';
wav[b++] = (byte)'F';
int chunkSize = totalSize - 8;
wav[b++] = (byte)(chunkSize & 0xff);
wav[b++] = (byte)((chunkSize >> 8) & 0xff);
wav[b++] = (byte)((chunkSize >> 16) & 0xff);
wav[b++] = (byte)((chunkSize >> 24) & 0xff);
wav[b++] = (byte)'W';
wav[b++] = (byte)'A';
wav[b++] = (byte)'V';
wav[b++] = (byte)'E';
// Format header
wav[b++] = (byte)'f';
wav[b++] = (byte)'m';
wav[b++] = (byte)'t';
wav[b++] = (byte)' ';
wav[b++] = 16;
wav[b++] = 0;
wav[b++] = 0;
wav[b++] = 0; // Chunk size
wav[b++] = 1;
wav[b++] = 0; // Compression code
wav[b++] = channelCount;
wav[b++] = 0; // Number of channels
wav[b++] = (byte)(sampleRate & 0xff);
wav[b++] = (byte)((sampleRate >> 8) & 0xff);
wav[b++] = (byte)((sampleRate >> 16) & 0xff);
wav[b++] = (byte)((sampleRate >> 24) & 0xff);
int byteRate = sampleRate * channelCount * sizeof(short); // byte rate for all channels
wav[b++] = (byte)(byteRate & 0xff);
wav[b++] = (byte)((byteRate >> 8) & 0xff);
wav[b++] = (byte)((byteRate >> 16) & 0xff);
wav[b++] = (byte)((byteRate >> 24) & 0xff);
wav[b++] = channelCount * sizeof(short);
wav[b++] = 0; // Block align (bytes per sample)
wav[b++] = sizeof(short) * 8;
wav[b++] = 0; // Bits per sample
// Data chunk header
wav[b++] = (byte)'d';
wav[b++] = (byte)'a';
wav[b++] = (byte)'t';
wav[b++] = (byte)'a';
wav[b++] = (byte)(sampleSize & 0xff);
wav[b++] = (byte)((sampleSize >> 8) & 0xff);
wav[b++] = (byte)((sampleSize >> 16) & 0xff);
wav[b++] = (byte)((sampleSize >> 24) & 0xff);
Debug.Assert(b == 44);
for (int s = 0; s != left.Length; ++s)
{
wav[b++] = (byte)(left[s] & 0xff);
wav[b++] = (byte)(((ushort)left[s] >> 8) & 0xff);
wav[b++] = (byte)(right[s] & 0xff);
wav[b++] = (byte)(((ushort)right[s] >> 8) & 0xff);
}
Debug.Assert(b == totalSize);
return wav;
}
// Create a simple sine wave
static void CreateSamples(out short[] left, out short[] right, int sampleRate)
{
const double middleC = 261.626;
const double standardA = 440;
const double frequency = standardA;
int count = sampleRate * 2; // Two seconds
left = new short[count];
right = new short[count];
for (int i = 0; i != count; ++i)
{
double t = (double)i / sampleRate; // Time of this sample in seconds
short s = (short)Math.Floor(Math.Sin(t * 2 * Math.PI * frequency) * short.MaxValue);
left[i] = s;
right[i] = s;
}
}
static void Main(string[] args)
{
short[] left;
short[] right;
int sampleRate = 44100;
CreateSamples(out left, out right, sampleRate);
byte[] wav = ConvertSamplesToWavFileFormat(left, right, sampleRate);
PlayWav(wav);
/*
// Write the data to a wav file
using (FileStream fs = new FileStream(@"C:\documents and settings\carlos\desktop\a440stereo.wav", FileMode.Create))
{
fs.Write(wav, 0, wav.Length);
}
*/
}
}
}