11

C#で任意の音波を作成してスピーカーから再生する賢明な方法を知っている人はいますか?

この問題は時々戻ってきて、何年もの間、私はいつも解決策を見つけることなく多くの失敗の後にそれをあきらめることになります。

私がやりたいのは、逆ビジュアライザーのようなものです。つまり、音から「数字」を生成するのではなく、数字から音を生成したいのです。

サンプルレート、サンプルサイズ、サウンドデータ(たとえば整数の配列)を提供する関数を取得すると、そこから適切なwavファイルが生成されます(リアルタイムのサウンド再生が理想的ですが、私はdこれにも満足している)。

私はwavファイルの仕様がインターウェブ全体にあることを知っており、上記の関数を作成するためにいくつかの試みを行い、低周波数である程度の成功を収めましたが、サンプルごとのビットなどをいじり始めると...それは巨大で制御できない混乱になります。

これはまだ行われていませんか?.NETマネージラッパーがあれば、それが何を使用するかは気になりません(そして、最新のVSからいつでもアクセスできます)。XNAは、この方法では低レベルのオーディオをサポートしていません。また、似たようなことを達成すると主張するいくつかの例を見つけましたが、それらはまったく機能しないか、まったく異なることをします。

ありがとうございました。

4

3 に答える 3

8

これは面白そうだったので、次のような簡単なアプリをノックアップしました。

  • 純音(440Hz A)の2秒間のサンプルを作成します。
  • それらをWAVファイル形式のバイト配列に変換します。
  • バイト配列をPlaySoundAPIに渡すことでサウンドを再生します。
  • WAVデータをWAVファイルに保存するためのコードも含まれています。

サンプルレート、トーン周波数、サンプル持続時間を簡単に変更できます。コードは非常に醜く、スペース効率が悪いですが、機能します。以下は完全なコマンドラインアプリです。

システムを使用する;
System.Diagnosticsを使用します。
System.IOを使用します。
System.Runtime.InteropServicesを使用します。

名前空間playwav
{{
    クラスプログラム
    {{
        [DllImport( "winmm.dll"、EntryPoint = "PlaySound"、SetLastError = true)]
        private extern static int PlaySound(byte [] wavData、IntPtr hModule、PlaySoundFlagsフラグ);

        //#define SND_SYNC 0x0000 / *同期再生(デフォルト)* /
        //#define SND_ASYNC 0x0001/*非同期で再生*/
        //#define SND_NODEFAULT 0x0002 / *サウンドが見つからない場合は無音(!default)* /
        //#define SND_MEMORY 0x0004 /*pszSoundはメモリファイルを指します*/
        //#define SND_LOOP 0x0008/*次のsndPlaySoundまでサウンドをループします*/
        //#define SND_NOSTOP 0x0010/*現在再生中のサウンドを停止しない*/

        //#define SND_NOWAIT 0x00002000L/*ドライバーがビジーの場合は待機しないでください*/
        //#define SND_ALIAS 0x00010000L/*名前はレジストリエイリアスです*/
        //#define SND_ALIAS_ID 0x00110000L/*エイリアスは事前定義されたIDです*/
        //#define SND_FILENAME 0x00020000L/*名前はファイル名です*/
        //#define SND_RESOURCE 0x00040004L/*名前はリソース名またはアトムです*/

        列挙型PlaySoundFlags
        {{
            SND_SYNC = 0x0000、
            SND_ASYNC = 0x0001、
            SND_MEMORY = 0x0004
        }

        //バイト配列に表示されるwavファイルを再生します
        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ヘッダー
            wav [b ++] =(バイト)'R';
            wav [b ++] =(バイト)'I';
            wav [b ++] =(バイト)'F';
            wav [b ++] =(バイト)'F';
            intchunkSize = 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 ++] =(バイト)'W';
            wav [b ++] =(バイト)'A';
            wav [b ++] =(バイト)'V';
            wav [b ++] =(バイト)'E';

            //ヘッダーをフォーマットします
            wav [b ++] =(バイト)'f';
            wav [b ++] =(バイト)'m';
            wav [b ++] =(バイト)'t';
            wav [b ++] =(バイト)'';
            wav [b ++] = 16;
            wav [b ++] = 0;
            wav [b ++] = 0;
            wav [b ++] = 0; //チャンクサイズ
            wav [b ++] = 1;
            wav [b ++] = 0; //圧縮コード
            wav [b ++] = channelCount;
            wav [b ++] = 0; //チャンネル数
            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); //すべてのチャネルのバイトレート
            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; //ブロックアライン(サンプルあたりのバイト数)
            wav [b ++] = sizeof(short)* 8;
            wav [b ++] = 0; //サンプルあたりのビット数

            //データチャンクヘッダー
            wav [b ++] =(バイト)'d';
            wav [b ++] =(バイト)'a';
            wav [b ++] =(バイト)'t';
            wav [b ++] =(バイト)'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);

            wavを返します。
        }

        //単純な正弦波を作成します
        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; //2秒
            左=新しいshort[カウント];
            right = new short [count];

            for(int i = 0; i!= count; ++ i)
            {{
                double t =(double)i / sampleRate; //このサンプルの時間(秒単位)
                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[]左;
            short[]右;
            int sampleRate = 44100;
            CreateSamples(左から、右から、sampleRate);
            byte [] wav = ConvertSamplesToWavFileFormat(left、right、sampleRate);
            PlayWav(wav);

            / *
            //データをwavファイルに書き込みます
            using(FileStream fs = new FileStream(@ "C:\ document and settings \ carlos \ desktop \ a440stereo.wav"、FileMode.Create))
            {{
                fs.Write(wav、0、wav.Length);
            }
            * /
        }
    }
}
于 2010-09-19T20:54:35.453 に答える
2

FMODはメモリからサンプルの読み込みを行うことができ、C#ラッパーを備えています。

于 2010-09-18T21:34:08.097 に答える
2

以下の配列から再生する方法

    PlayerEx pl = new PlayerEx();

    private static void PlayArray(PlayerEx pl)
    {
        double fs = 8000; // sample freq
        double freq = 1000; // desired tone
        short[] mySound = new short[4000];
        for (int i = 0; i < 4000; i++)
        {
            double t = (double)i / fs; // current time
            mySound[i] = (short)(Math.Cos(t * freq) * (short.MaxValue));
        }
        IntPtr format = AudioCompressionManager.GetPcmFormat(1, 16, (int)fs);
        pl.OpenPlayer(format);
        byte[] mySoundByte = new byte[mySound.Length * 2];
        Buffer.BlockCopy(mySound, 0, mySoundByte, 0, mySoundByte.Length);
        pl.AddData(mySoundByte);
        pl.StartPlay();
    }
于 2014-02-02T21:25:56.740 に答える