2010-09-18 50 views
11

Bất cứ ai cũng biết một cách hợp lý để tạo ra một làn sóng âm thanh ARBITRARY trong C# và phát lại từ loa?Tạo âm thanh ở mức độ thấp trong C#?

Vấn đề này đã trở lại với mọi hiện tại và sau đó trong nhiều năm, tôi luôn kết thúc sau khi thất bại mà không tìm ra giải pháp.

Điều tôi muốn làm giống như trình hiển thị ngược, tức là, tôi không muốn tạo "số" từ âm thanh, tôi muốn tạo âm thanh từ các số.

Giống như nhận hàm mà tôi cung cấp với tỷ lệ mẫu, kích thước mẫu và dữ liệu âm thanh (một mảng các số nguyên) và nó sẽ tạo tệp wav thích hợp từ đó (phát lại âm thanh theo thời gian thực sẽ lý tưởng) , nhưng tôi cũng sẽ hài lòng hơn với điều này).

Tôi biết thông số tệp wav nằm trên mạng nội bộ và đã thực hiện một số lần tạo chức năng ở trên, có một số thành công cho tần số thấp, nhưng một khi tôi bắt đầu rối tung với bit trên mỗi mẫu vv ... , không thể kiểm soát được.

Điều này chưa được thực hiện theo bất kỳ cách nào? Tôi sẽ không nhớ những gì nó sử dụng, miễn là có một NET quản lý wrapper cho nó (và tôi có thể truy cập nó từ thời gian gần đây nhất VS). XNA không hỗ trợ âm thanh mức thấp theo cách này. Cũng tìm thấy một số ví dụ tuyên bố để đạt được một cái gì đó tương tự, nhưng họ hoặc là không làm việc ở tất cả, hoặc làm một cái gì đó hoàn toàn khác nhau.

Cảm ơn bạn.

Trả lời

8

này trông hấp dẫn hơn, tôi đã gõ lên một ứng dụng đơn giản rằng:

  • Tạo mẫu trong hai giây của một giai điệu tinh khiết (440Hz A).
  • Chuyển đổi chúng thành một mảng byte ở định dạng tệp WAV.
  • Phát âm thanh bằng cách chuyển mảng byte tới API PlaySound.
  • Cũng bao gồm mã để lưu dữ liệu WAV vào tệp WAV.

Bạn có thể dễ dàng thay đổi tốc độ mẫu, tần số âm và thời lượng mẫu. Mã này rất xấu và không hiệu quả nhưng nó hoạt động. Sau đây là một hoàn chỉnh ứng dụng dòng lệnh:

 
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); 
      } 
      */ 
     } 
    } 
} 
+0

Điều này trông thực sự tuyệt vời, và tôi cảm thấy thực sự xấu hổ nhưng không có thời gian để thực sự chơi với nó được nêu ra. Chỉ một câu hỏi: Có dễ dàng làm cho nó 4 byte cho mỗi mẫu không? – jssyjrm

+0

Bạn có thể làm cho nó 4 byte cho mỗi mẫu nhưng tôi không biết nếu Windows sẽ chơi nó. Nó có thể, tôi chỉ không biết. Dù sao, nếu bạn muốn làm điều này thay đổi tất cả các tham chiếu đến sizeof (ngắn) thành sizeof (int), thay đổi kiểu mẫu thành int, thay đổi hệ số chia tỷ lệ (short.MaxValue) thành int.MaxValue và sửa vòng lặp lấp đầy mảng byte để thêm bốn byte cho mỗi mẫu. Nhưng tôi sẽ ngạc nhiên nếu bạn có thể nghe thấy một sự khác biệt. – arx

+0

Cảm ơn rất nhiều vì điều này. Làm thế nào tôi có thể thêm chức năng dừng (và có thể tạm dừng) ở đây? Tôi đoán tôi sẽ cần một nhân viên nền để phần còn lại của GUI là miễn phí cho đầu vào. Loại mã nào sẽ là 'âm thanh dừng' trông như thế nào? –

2

FMOD có thể thực hiện tải mẫu từ bộ nhớ và có trình bao bọc C#.

+0

Được rồi, chỉ cần có rất nhiều thứ sắp tới do đó, không thể thử nghiệm nhiều nhưng tôi xin lỗi. FMOD chắc chắn có thể làm điều đó, nhưng nó có một wrapper quản lý tự động được tạo ra khủng khiếp. Có một ví dụ cụ thể về việc làm điều này với một số cài đặt nhất định, nhưng đó là một nỗi đau để thay đổi các cài đặt đó và nó buộc các nhà phát triển sử dụng mã không an toàn ở khắp mọi nơi. Cảm ơn bạn đã chỉ ra suy nghĩ, khi tôi sẽ có thêm thời gian, tôi sẽ hỏi họ tại sao tôi không thể sử dụng hơn 2 byte cho mỗi cài đặt mẫu. – jssyjrm

2

How to play from an array dưới

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(); 
    } 
Các vấn đề liên quan