2010-09-18 10 views
11

कोई भी सी # में ARBITRARY ध्वनि तरंग बनाने के लिए एक समझदार तरीके से जानता है और इसे स्पीकर से वापस चलाता है?सी # में वास्तविक निम्न स्तर की ध्वनि उत्पादन?

यह समस्या हर समय वापस आ रही है और फिर वर्षों से, मैं समाधान खोजने के बिना बहुत सारी विफलता के बाद इसे समाप्त कर देता हूं।

मैं जो करना चाहता हूं वह एक रिवर्स-विज़ुअलाइज़र की तरह है, यानी, मैं ध्वनि से "संख्याएं" उत्पन्न नहीं करना चाहता, मैं संख्याओं से ध्वनि उत्पन्न करना चाहता हूं।

नमूना दर, नमूना आकार, और ध्वनि डेटा (उदाहरण के लिए पूर्णांक की एक सरणी) के साथ प्रदान करने वाला एक कार्य प्राप्त करने की तरह, और इससे उपयुक्त WAV फ़ाइल उत्पन्न होगी (वास्तविक समय ध्वनि प्लेबैक आदर्श होगा , लेकिन मैं भी इसके साथ खुश होने से अधिक होगा)।

मुझे पता है कि WAV फ़ाइल विनिर्देश सभी इंटरवेब पर हैं, और उपरोक्त फ़ंक्शन बनाने के कई प्रयास किए हैं, कम आवृत्तियों के लिए कुछ सफलता मिली है, लेकिन एक बार जब मैं नमूना प्रति बिट्स के साथ गड़बड़ करना शुरू करता हूं ... यह एक बड़ा हो जाता है अनियंत्रित गड़बड़

क्या यह पहले से ही किसी भी तरह से नहीं किया गया है? मुझे यह नहीं लगेगा कि इसका उपयोग क्या होता है, जब तक इसके लिए .NET प्रबंधित रैपर होता है (और मैं इसे नवीनतम वीएस से समय तक एक्सेस कर सकता हूं)। एक्सएनए इस तरह निम्न स्तर के ऑडियो का समर्थन नहीं करता है। कुछ उदाहरण भी प्राप्त हुए जो कुछ समान प्राप्त करने का दावा करते हैं, लेकिन वे या तो बिल्कुल काम नहीं करते हैं, या कुछ पूरी तरह से अलग करते हैं।

धन्यवाद।

उत्तर

8

यह दिलचस्प लग रहा था तो मुझे लगा कि एक सरल अनुप्रयोग खटखटाया है:

  • एक शुद्ध स्वर (440Hz ए) के दो सेकंड के लिए नमूने बनाता है।
  • उन्हें डब्ल्यूएवी फ़ाइल प्रारूप में बाइट सरणी में परिवर्तित करता है।
  • प्लेसाउंड एपीआई में बाइट सरणी पास करके ध्वनि चलाता है।
  • WAV डेटा को WAV फ़ाइल में सहेजने के लिए कोड भी शामिल है।

आप आसानी से नमूना दर, स्वर आवृत्ति और नमूना अवधि बदल सकते हैं। कोड बहुत बदसूरत और अंतरिक्ष-अक्षम है लेकिन यह काम करता है। ,

 
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

यह वास्तव में कमाल दिखता है, और मुझे वास्तव में शर्म आती है लेकिन अभी तक इसके साथ वास्तव में खेलने का समय नहीं है। सिर्फ एक सवाल: क्या प्रति नमूना 4 बाइट बनाना आसान है? – jssyjrm

+0

आप प्रति नमूना 4 बाइट बना सकते हैं लेकिन मुझे नहीं पता कि विंडोज इसे खेलेंगे या नहीं। यह हो सकता है, मैं बस नहीं जानता। वैसे भी, यदि आप यह परिवर्तन करना चाहते हैं तो आकार (छोटा) आकार (int) के आकार के सभी संदर्भ, नमूना प्रकार को int में बदलें, स्केलिंग कारक (लघु। मैक्सवेल्यू) को int में बदलें।MaxValue और लूप को ठीक करें जो प्रति बाइट चार बाइट जोड़ने के लिए बाइट सरणी भरता है। लेकिन अगर आप एक अंतर सुन सकते हैं तो मुझे आश्चर्य होगा। – arx

+0

इसके लिए बहुत बहुत धन्यवाद। मैं यहां स्टॉप (और शायद रोकें) कार्यक्षमता कैसे जोड़ सकता हूं? मुझे लगता है कि मुझे पृष्ठभूमि कार्यकर्ता की आवश्यकता होगी ताकि बाकी जीयूआई इनपुट के लिए स्वतंत्र हो। 'स्टॉप ध्वनि' किस तरह का कोड दिखता है? –

2

एफएमओडी स्मृति से नमूना भार कर सकता है और इसमें सी # रैपर है।

+0

ठीक है सिर्फ इसलिए आ रहा है प्रयोग नहीं कर सकता है कि ज्यादा अभी तक मैं माफी चाहता हूँ के लिए बहुत कुछ था: निम्नलिखित एक पूरी कमांड लाइन अनुप्रयोग है। एफएमओडी निश्चित रूप से ऐसा कर सकता है, लेकिन इसमें एक भयानक ऑटो-जनरेटेड प्रबंधित रैपर है। कुछ सेटिंग्स के साथ ऐसा करने का एक विशिष्ट उदाहरण है, लेकिन उन सेटिंग्स को बदलने में दर्द होता है और यह डेवलपर्स को हर जगह असुरक्षित कोड का उपयोग करने के लिए मजबूर करता है। विचारों को इंगित करने के लिए धन्यवाद, जब मेरे पास अधिक समय लगेगा तो मैं उनसे पूछूंगा कि मैं नमूना सेटिंग्स प्रति 2 बाइट्स का उपयोग क्यों नहीं कर सका। – jssyjrm

2

How to play from an array नीचे

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(); 
    } 
संबंधित मुद्दे