2009-08-19 6 views
5

Mam program, który produkuje sygnały audio, które powinny być odtwarzane jednocześnie. W tym celu odtwarzam przedział 100 ms strumienia audio w każdym okresie 100 ms. Ale mam niepożądane sygnały na początku i końcu każdego strumienia akustycznego o długości 100 ms (z powodu prądu stałego), tak że dźwięk wyjściowy nie jest płynny, nawet wartość sygnałów jest taka sama. Mój kod jest dołączony poniżej. Proszę, pomóż mi, co powinienem zrobić, żeby mieć poprawny dźwięk w czasie rzeczywistym.Jak odtwarzać dźwięk ze strumienia w czasie rzeczywistym?

using System; 
using System.Windows.Forms; 
using Microsoft.DirectX.DirectSound; 
using System.IO; 

namespace TestSound 
{ 
    class CSound : Form 
    { 
     const int HEADER_SIZE = 44; 
     const bool FLAG_STEREO = true; 
     const short BITS_PER_SAMPLE = 16; 
     const int SAMPLE_RATE = 44100; 

     int numberOfSamples; 
     MemoryStream stream; 
     BinaryWriter writer; 
     Device ApplicationDevice = null; 
     SecondaryBuffer buffer = null; 
     BufferDescription description; 

     public CSound() 
     { 
      try 
      { 
       ApplicationDevice = new Device(); 
      } 
      catch 
      { 
       MessageBox.Show("Unable to create sound device."); 
       ApplicationDevice = null; 
       return; 
      } 
      ApplicationDevice.SetCooperativeLevel(this, CooperativeLevel.Priority); 
      description = new BufferDescription(); 
      description.ControlEffects = false; 
      stream = new MemoryStream(); 
      writer = new BinaryWriter(stream); 
     } 

     private void AddHeader() 
     { 
      stream.Position = 0; 

      writer.Write(0x46464952); // "RIFF" in ASCII 
      writer.Write((int)(HEADER_SIZE + (numberOfSamples * BITS_PER_SAMPLE * (FLAG_STEREO ? 2 : 1)/8)) - 8); 
      writer.Write(0x45564157); // "WAVE" in ASCII 
      writer.Write(0x20746d66); // "fmt " in ASCII 
      writer.Write(16); 
      writer.Write((short)1); 
      writer.Write((short)(FLAG_STEREO ? 2 : 1)); 
      writer.Write(SAMPLE_RATE); 
      writer.Write(SAMPLE_RATE * (FLAG_STEREO ? 2 : 1) * BITS_PER_SAMPLE/8); 
      writer.Write((short)((FLAG_STEREO ? 2 : 1) * BITS_PER_SAMPLE/8)); 
      writer.Write(BITS_PER_SAMPLE); 
      writer.Write(0x61746164); // "data" in ASCII 
      writer.Write((int)(numberOfSamples * BITS_PER_SAMPLE * (FLAG_STEREO ? 2 : 1)/8)); 
     } 

     public void Play(short[] samples) 
     { 
      if (ApplicationDevice == null) 
       return; 

      stream.Position = HEADER_SIZE; 
      numberOfSamples = samples.Length; 
      for (int i = 0; i < numberOfSamples; i++) 
      { 
       writer.Write(samples[i]); 
       if (FLAG_STEREO) 
        writer.Write(samples[i]); 
      } 
      AddHeader(); 
      stream.Position = 0; 

      try 
      { 
       if (buffer != null) 
       { 
        buffer.Dispose(); 
        buffer = null; 
       } 
       buffer = new SecondaryBuffer(stream, description, ApplicationDevice); 
       buffer.Play(0, BufferPlayFlags.Default); 
      } 
      catch (Exception e) 
      { 
       MessageBox.Show(e.Message); 
      } 
     } 

     static short[] samples = new short[4410]; // 100 ms 
     static CSound sound; 

     static void Main() 
     { 
      Form form = new Form(); 
      form.Show(); 

      sound = new CSound(); 
      Random random = new Random(); 
      for (int i = 0; i < samples.Length; i++) 
       samples[i] = 1000; // constant value 

      while (true) 
      { 
       sound.Play(samples); 
       System.Threading.Thread.Sleep(100); // 100 ms 
      } 
     } 
    } 
} 

Odpowiedz

1

Jest wiele rzeczy nie w porządku z tym kodem. Zgaduję, że kiedy uruchamiasz ten kod, słyszysz klikanie lub trzaskanie dźwięków co 100 ms. Wynika to z połączenia Thread.Sleep (100) wewnątrz pętli while (true). Zasadniczo twoja aplikacja czeka 100 ms (podaj lub przyjmij małą ilość czasu), a następnie wywołaj Play(), która wykonuje niewielką obróbkę, a następnie umieszcza kolejkę w kolejce do odtwarzania. W rezultacie istnieje niewielka luka czasowa między odtwarzaniem każdej tablicy 100 ms, która generuje kliknięcia.

Jednakże, jeśli właśnie skomentowałeś linię Thread.Sleep (100), twoja aplikacja przechodzi do nieskończonej pętli, w której utrzymuje kolejkę do 100 ms tablicy po tablicy 100 ms, aż zabraknie pamięci. Ale przynajmniej odtwarzanie nie miałoby artefaktów co 100 ms.

Jeśli zmieniłeś linię na Thread.Sleep (80), działałoby to trochę lepiej, w tym sensie, że zabrakłoby Ci więcej czasu na zapamiętywanie, ale nadal by się tak działo, ponieważ nadal byś wyrzucał bufory do systemu odtwarzania audio szybciej niż system może je odtwarzać.

Ponadto, nawet jeśli pozbędziesz się kliknięcia co 100 ms, nadal nie usłyszysz nic z głośników, ponieważ twój kod ustawia każdą wartość próbki na 1000. Zawsze usłyszysz cokolwiek jeśli zmieniasz wartości próbek w czasie. Nawiasem mówiąc, jedynym powodem, dla którego słyszysz kliknięcia w ogóle, jest to, że ta wartość próbki jest ustawiona na 1000, i podczas tych małych przedziałów czasowych między porcjami wartość odtwarzania powraca do 0. Jeśli ustawisz każdą wartość próbki na 0, nigdy nie będziesz usłyszeć cokolwiek.

Mogę ci w tym pomóc, ale muszę mieć dokładniejsze wyobrażenie o tym, co próbujesz zrobić. Czy próbujesz odtwarzać ciągły dźwięk z określoną częstotliwością?

4

Jeśli szukasz sposobu, aby odtworzyć dźwięk przez zdefiniowany strumień, czy rozważałeś NAudio http://naudio.codeplex.com/?

Możesz zdefiniować strumień z pliku lub innej lokalizacji (np. Z pamięci), a następnie wypełnić strumień danymi, które chcesz odtworzyć. Dopóki jesteś w stanie kontynuować dostarczanie danych dźwiękowych do strumienia, zanim wskaźnik odczytu dotrze na koniec bufora, nie usłyszysz tych artefaktów w wygenerowanym dźwięku.

BTW - Zakładam, że wiesz, że biblioteki Managed Direct X dla .Net nie są już rozwijane i są faktycznie ślepym zaułkiem dla tego rodzaju rozwoju Audio?

0

Jeśli przez "niepożądane sygnały" rozumiecie niewielki trzaskanie na jednym końcu, może to być problem z kopertą, który w Csound może być kontrolowany przez "lniany" kod operacji lub coś podobnego. Chodzi o to, że musisz podnieść amplitudę na przednim końcu i lekko na tylnym końcu, aby uniknąć kliknięcia głośników gwałtownie przerywając ich produkcję w środkowej fali, że tak powiem. Wystarczy kilka ms - eksperymentuj z nim, aż pozbędziesz się poppingu, nie zauważając modulacji amplitudowej.

Spójrz tutaj: http://www.csounds.com/journal/issue11/csoundEnvelopes.html

Jeśli próbujesz utworzyć płynnego sygnału przez artykulacji identycznego przebiegu kolejno w regularnych odstępach czasu, to zawsze słyszę ten dźwięk popping ponieważ koniec jednej fali nie kolejce z początkiem następnego przebiegu. Bardzo trudno jest uzyskać dokładne kształty przebiegu, a to nie jest dobra strategia. Lepszą strategią byłoby użycie obwiedni (jak opisano powyżej) i nakładanie się kształtów fal (zwanych gołębnikami), tak aby rozpad dawnej artykulacji następował równocześnie ze wzrostem nowej artykulacji.

Jednak strategia ta nie zapewni całkowicie czystego dźwięku, ponieważ obecność dwóch nieco podobnych przebiegów zachodzących na siebie asynchronicznie spowoduje wzajemne zerwanie się nawzajem i zmniejszenie amplitudy w każdym z połączeń przebiegów.