2014-12-10 2 views
3

Я использую следующий код для динамического создания частоты тона в памяти и воспроизводить тон асинхронно:Как узнать, когда SoundPlayer закончил воспроизведение звука

public static void PlayTone(UInt16 frequency, int msDuration, UInt16 volume = 16383) 
{ 
    using (var mStrm = new MemoryStream()) 
    { 
     using (var writer = new BinaryWriter(mStrm)) 
     { 
      const double tau = 2*Math.PI; 
      const int formatChunkSize = 16; 
      const int headerSize = 8; 
      const short formatType = 1; 
      const short tracks = 1; 
      const int samplesPerSecond = 44100; 
      const short bitsPerSample = 16; 
      const short frameSize = (short) (tracks*((bitsPerSample + 7)/8)); 
      const int bytesPerSecond = samplesPerSecond*frameSize; 
      const int waveSize = 4; 
      var samples = (int) ((decimal) samplesPerSecond*msDuration/1000); 
      int dataChunkSize = samples*frameSize; 
      int fileSize = waveSize + headerSize + formatChunkSize + headerSize + dataChunkSize; 

      writer.Write(0x46464952); 
      writer.Write(fileSize); 
      writer.Write(0x45564157); 
      writer.Write(0x20746D66); 
      writer.Write(formatChunkSize); 
      writer.Write(formatType); 
      writer.Write(tracks); 
      writer.Write(samplesPerSecond); 
      writer.Write(bytesPerSecond); 
      writer.Write(frameSize); 
      writer.Write(bitsPerSample); 
      writer.Write(0x61746164); 
      writer.Write(dataChunkSize); 

      double theta = frequency*tau/samplesPerSecond; 
      double amp = volume >> 2; 
      for (int step = 0; step < samples; step++) 
      { 
       writer.Write((short) (amp*Math.Sin(theta*step))); 
      } 

      mStrm.Seek(0, SeekOrigin.Begin); 
      using (var player = new System.Media.SoundPlayer(mStrm)) 
      { 
       player.Play(); 
      } 
     } 
    } 
} 

код работает отлично. Единственная проблема, как узнать, когда тон перестает играть? Кажется, что не было события Completed в классе SoundPlayer, на который я могу подписаться.

+0

Вы правы, что у вас нет завершенного события, если вам это нужно, вам, скорее всего, понадобится использовать элемент управления ActiveX для Windows Media Player (http://msdn.microsoft.com/en-us/library/ dd564582 (v = vs.85) .aspx). –

+0

@MarkHall - Может ли элемент управления ActiveX захватить источник из MemoryStream или он должен поступать из URL/файла? – Icemanind

+0

Я не уверен, что это так или нет, одна из причин, по которой я разместил его в качестве комментария. Прошло несколько лет с тех пор, как мне пришлось его использовать, и я просто использовал файл. –

ответ

5

Знаете, если все, что вы хотите сделать, это воспроизвести один тон, есть Console.Beep. Конечно, это не делает это в фоновом режиме, но техника, описанная ниже, будет отлично работать для Console.Beep, и это мешает вам создать поток памяти только для воспроизведения тона.

В любом случае, SoundPlayer не обладает такими функциями, которые вы хотите, но можете имитировать его.

Во-первых, создать свой обработчик события:

void SoundPlayed(object sender, EventArgs e) 
{ 
    // do whatever here 
} 

Измените свой метод PlayTone так, что он принимает параметр функции обратного вызова:

public static void PlayTone(UInt16 frequency, int msDuration, UInt16 volume = 16383, EventHandler doneCallback = null) 

Затем изменить конец метода, так что он вызывает PlaySync, а не Play, и называет doneCallback после того, как это сделано:

using (var player = new System.Media.SoundPlayer(mStrm)) 
{ 
    player.PlaySync(); 
}  
if (doneCallback != null) 
{ 
    // the callback is executed on the thread. 
    doneCallback(this, new EventArgs()); 
} 

Затем выполните его в потоке или задаче. Например:

var t = Task.Factory.StartNew(() => {PlayTone(100, 1000, 16383, SoundPlayed)); 

Основная проблема заключается в том, что уведомление о событии происходит в фоновом потоке. Вероятно, самое лучшее, что нужно сделать, если вам нужно повлиять на пользовательский интерфейс, - это синхронизировать обработчик событий с потоком пользовательского интерфейса. Поэтому в методе SoundPlayed вы должны позвонить Form.Invoke (для WinForms) или Dispatcher.Invoke (WPF) для выполнения любых действий пользовательского интерфейса.

+0

Does Console.Beep воспроизводит звук через звуковую карту или динамик ПК? – Icemanind

+1

Это сработало! Я использую .NET 3.5, поэтому мне пришлось называть его следующим: «var t = new Thread (() => PlayTone (1000, 1000, Done));» и затем запустите его: 't.Start() ; '. Но он делает то, что мне нужно. – Icemanind

+0

@icemanind: Документация говорит, что она играет «через консольный динамик». На моей машине она воспроизводится через звуковую карту. Немного оглядываясь, кажется, говорит, что он будет воспроизводить звуковую карту, если она доступна. В противном случае он подает звуковой сигнал внутреннему громкоговорителю. Но я не проверял его, поэтому не могу сказать точно. –

Смежные вопросы