2010-03-16 6 views
2

Я работаю над приложением, которое непрерывно воспроизводит звук, используя API waveOut... от winmm.dll. Приложение использует «leapfrog» буферы, которые в основном представляют собой набор массивов образцов, которые вы выгружаете в очередь аудио. Windows воспроизводит их последовательно последовательно, и по мере того как каждый буфер завершает работу Windows, вызывает функцию обратного вызова. Внутри этой функции я загружаю следующий набор выборок в буфер, обрабатываю их, а затем удаляю буфер обратно в очередь аудио. Таким образом, звук воспроизводится бесконечно.Проблема с waveOutWrite и deadOutGetPosition deadlock

В целях анимации я пытаюсь включить waveOutGetPosition в приложение (так как обратные вызовы «сделанные буфером» достаточно нерегулярны, чтобы вызвать отрывистую анимацию). waveOutGetPosition возвращает текущее положение воспроизведения, поэтому оно является гиперточным.

Проблема заключается в том, что в моем приложении вызовы waveOutGetPosition в конечном итоге заставляют приложение блокироваться - звук останавливается, и вызов никогда не возвращается. Я проглотил все до простого приложения, которое демонстрирует проблему. Вы можете запустить приложение здесь:

http://www.musigenesis.com/SO/waveOut%20demo.exe

Если вы просто услышать чуть-чуть пианино снова и снова, это работает. Это просто означает продемонстрировать проблему. Исходный код для этого проекта здесь (все мясо в LeapFrogPlayer.cs):

http://www.musigenesis.com/SO/WaveOutDemo.zip

первая кнопка запускает приложение в режиме чехарда, не делая звонки waveOutGetPosition. Если вы нажмете на это, приложение будет играть вечно без взлома (кнопка X закроет его и выключит). Вторая кнопка запускает scapperger, а также запускает таймер форм, который вызывает waveOutGetPosition и отображает текущую позицию. Нажмите эту кнопку, и приложение запустится ненадолго, а затем заблокируется. На моем ноутбуке он обычно блокируется через 15-30 секунд; максимум на минуту.

Я понятия не имею, как это исправить, поэтому любая помощь или предложения были бы наиболее желанными. Я нашел очень мало сообщений по этой проблеме, но кажется, что существует потенциальный тупик - от нескольких вызовов до waveOutGetPosition или от вызовов до этого и waveOutWrite, которые происходят одновременно. Возможно, я слишком часто вызываю это для системы.

Редактировать: забыл упомянуть, я запускаю это в Windows Vista. Это может не произойти вообще на других ОС.

Edit 2: Я нашел немного об этой проблеме в Интернете, для них (без ответа) сообщений, за исключением:

http://social.msdn.microsoft.com/Forums/en-US/windowsgeneraldevelopmentissues/thread/c6a1e80e-4a18-47e7-af11-56a89f638ad7

Edit 3: Ну, теперь я в состоянии воспроизвести эта проблема по желанию. Если я вызываю waveOutGetPosition сразу после waveOutWrite (в следующей строке кода) приложение зависает каждый раз. Он также висит особенно плохо - кажется, он блокирует всю мою ОС на некоторое время, а не только на самом приложении. Таким образом, оказывается, что waveOutGetPosition взаимоблокировки, если это происходит на почти таким же образом, что и waveOutWrite, а не буквально в то же время, что может объяснить, почему блокировки не работают на меня. Иш.

ответ

0

Решение этого было очень простым (спасибо Ларри Остерману): замените обратный вызов WndProc.

Метод waveOutOpen может принимать делегат (для обратного вызова) или дескриптор окна. Я использовал подход делегата, который, по-видимому, по своей сути склонен к взаимоблокировке (имеет смысл, особенно в управляемом коде). Я смог просто наследовать класс класса от Control и переопределить метод WndProc и сделать то же самое в этом методе, что и в обратном вызове. Теперь я могу называть waveOutGetPosition навсегда, и он никогда не блокируется.

3

Это взаимоблокировки внутри кода API mmsys.Вызов waveOutGetPosition() внутри тупиков обратного вызова, когда основной поток занят выполнением waveOutWrite(). Это исправление, вам понадобится блокировка, чтобы эти две функции не могли выполняться одновременно. Добавьте это поле в LeapFrogPlayer:

private object mLocker = new object(); 

И использовать его в GetElapsedMilliseconds():

 if (!noAPIcall) 
     { 
      lock (mLocker) { 
      ret = WaveOutX.waveOutGetPosition(_hWaveOut, ref _timestruct, 
       _timestructsize); 
      } 
     } 

и HandleWaveCallback():

 // play the next buffer 
     lock (mLocker) { 
      int ret = WaveOutX.waveOutWrite(_hWaveOut, ref _header[_currentBuffer], 
       Marshal.SizeOf(_header[_currentBuffer])); 
      if (ret != WaveOutX.MMSYSERR_NOERROR) { 
      throw new Exception("error writing audio"); 
      } 
     } 

Это может иметь побочные эффекты, я Жду» Не заметите. Посмотрите на NAudio project.

Пожалуйста, используйте Build + Clean при следующем создании загружаемого ZIP-файла вашего проекта.

+0

Я пробовал это, но он все еще блокируется. Я также попытаюсь заблокировать вызовы waveOutPrepareHeader. Зачем вам нужно строить + чистить? Трудно было скомпилировать проект или что-то еще? – MusiGenesis

+2

Работал на моей машине. Используйте Debug + Break All, Debug + Windows + Threads, чтобы увидеть, где потоки застряли. Никто не любит большие загрузки с .exe-файлами с ненадежного URL-адреса Интернета. –

+0

Во всяком случае, эта модификация делает проблему еще хуже. Скорее всего, сейчас он блокируется намного быстрее; примерно в половине случаев он блокируется при первом вызове waveOutGetPosition. Это работает на вашем компьютере? – MusiGenesis

1

Я использую NAudio и часто запрашиваю WaveOut.GetPosition(), а также вижу частые взаимоблокировки при использовании стратегии Callback. Это по существу та же проблема, что и у OP, поэтому я полагаю, что это решение может помочь кому-то другому.

Я попытался использовать стратегии на основе окон (как указано в ответе), но звук заикается, когда в очередь сообщений пробивается много сообщений. Поэтому я перешел на стратегию Callback. Затем я начал получать тупики.

Я запрашиваю аудио положение со скоростью 60 кадров в секунду, чтобы синхронизировать анимацию, поэтому я довольно регулярно забираю тупик (примерно 20 секунд в среднем). Примечание:Я уверен, что могу уменьшить количество, которое я называю API, но это не моя точка здесь!

Кажется, что вызовы winmm.dll блокируются внутри одного и того же объекта/ручки. Если это предположение верно, то в NAudio мне почти гарантирован тупик. Вот сценарий с двумя потоками: A (поток пользовательского интерфейса); и B (обратная связь в winmm.dll) и две замки waveOutLock (как в NAudio) и mmdll (замок, который я принимаю winmm.DLL использует):

  1. A -> Блокировку (waveOutLock) --- приобретенной
  2. B -> замок (mmdll) для обратного вызова --- приобретенной
  3. B -> обратного вызова в код пользователя
  4. B -> попытка заблокировать (waveOutLock) - ожидание А выпустить
  5. А -> возобновлено в связи с B ждет
  6. А -> называют waveOutGetPosition
  7. A -> попытка заблокировать (mmdll) - тупик

Моим решением было делегировать работу, выполненную в обратном вызове, в мою собственную нить, чтобы обратный вызов мог немедленно вернуться и отпустить (гипотетический) замок mmdll. Кажется, это отлично работает для меня, поскольку тупик исчез.

Для интересующихся, у меня есть forked and modified источник NAudio для включения моих изменений. Я использовал пул потоков, и звук иногда немного взломан. Это может быть связано с управлением потоками пула потоков, поэтому может быть решение, которое работает лучше.

+0

Вы можете протестировать это решение очень сильно и убедиться, что он работает на 100%.В моем реальном приложении работа, требующая вызова 'waveOutGetPosition', была выполнена в отдельном потоке. Он работал очень хорошо, но он все равно иногда был бы тупиком - гораздо реже, чем в демонстрационном приложении, которое я разместил здесь, но часто достаточно, чтобы меня еще очень беспокоить. В моей текущей версии с использованием подхода wndProc механизм воспроизведения на 100% надежен; Я оставил его работать несколько дней подряд, и я не видел ни одного случая тупика. Но с помощью обратного вызова он редко запускался ... – MusiGenesis

+0

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

+0

BTW, я никогда не слышал ни одного заикания, как то, что вы описываете при использовании подхода wndProc. Что происходит в вашей программе и вашей системе, когда это происходит? – MusiGenesis

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

  • Нет связанных вопросов^_^