Как произвести точно синхронизированный тон и тишину?

У меня есть проект C#, который играет азбуку Морзе для каналов RSS. Я пишу это с помощью Управляемого DirectX, только чтобы обнаружить, что Управляемый DirectX является старым и устаревшим. Задача, которую я имею, состоит в том, чтобы играть чистые синусоидальные пакеты, вкрапленные периодами тишины (код), которые точно синхронизированы относительно их продолжительности. Я должен смочь вызвать функцию, которая играет чистый тон для такого количества миллисекунд, затем Поток. Сон () затем играет другого и т.д. В его самом быстром тоны и пробелы могут быть всего 40 мс.

Это работает вполне хорошо в Управляемом DirectX. Для получения точно синхронизированного тона, я создаю 1 секунду. из синусоидальной волны во вторичный буфер затем для проигрывания тона определенной продолжительности я ищу вперед на в x миллисекундах конца буфера, затем играют.

Я попробовал Систему. Медиа. SoundPlayer. Это - проигравший [редактирование - видит мой ответ ниже], потому что необходимо Играть (), Сон (), затем Остановка () для произвольных тональных длин. Результатом является тон, который является слишком длинным, переменным загрузкой ЦП. Требуется неопределенное количество времени для фактической остановки тона.

Я затем начал долгую попытку использовать NAudio 1.3. Я закончил с резидентным потоком, обеспечивающим тональные данные и снова ищущим вперед отъезд желаемой длины тона, остающегося в потоке, затем играя. Этот обработанный OK на классе DirectSoundOut некоторое время (см. ниже), но класс WaveOut быстро умирает с внутренним, утверждает высказывание, что буферы находятся все еще на очереди несмотря на PlayerStopped = верны. Это нечетно, так как я играю, в конец затем помещает ожидание той же продолжительности между концом тона и запуском следующего. Вы думали бы, что 80 мс после стартовой Игры тона на 40 мс, что она не будет иметь буферов на очереди.

DirectSoundOut работает хорошо некоторое время, но его проблема состоит в том, который для каждого тона разрывал Игру (), он отделяет отдельный поток. В конечном счете (приблизительно 5 минут) это просто прекращает работать. Вы видите поток после потока после потока, выходящего в Окне вывода при выполнении проекта в VS2008 IDE. Я не создаю новые объекты во время проигрывания, я просто Ищу (), тональный поток затем называет Игру () много раз, таким образом, я не думаю, что это - проблема с осиротевшей буферной/независимо от того, что укладкой до, он дросселируется.

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

22
задан Bob Denny 28 April 2010 в 12:06
поделиться

4 ответа

Не могу поверить ... Я вернулся к System.Media.SoundPlayer и заставил его делать то, что я хочу ... не гигант библиотека зависимостей с 95% неиспользуемым кодом и / или причудами, ожидающими обнаружения :-). Кроме того, он работает на MacOSX под управлением Mono (2.6) !!! [неправильно - нет звука, задам отдельный вопрос]

Я использовал MemoryStream и BinaryWriter для хранения WAV-файла с заголовком RIFF и фрагментами. Никакого "фактического" фрагмента не требуется, это 16-битные сэмплы на частоте 44100 Гц. Итак, теперь у меня есть MemoryStream с 1000 мс сэмплов в нем, обернутый BinaryReader.

В файле RIFF есть две длины по 4/32 бита: «общая» длина, которая составляет 4 байта в потоке (сразу после «RIFF» в ASCII), и длина «данных» непосредственно перед байты данных выборки. Моя стратегия заключалась в том, чтобы искать в потоке и использовать BinaryWriter для изменения двух длин, чтобы заставить SoundPlayer думать, что аудиопоток - это просто длина / продолжительность, которую я хочу, а затем Play ().В следующий раз продолжительность будет другой, поэтому еще раз перезапишите длины в MemoryStream с помощью BinaryWriter, Flush () и еще раз вызовите Play ().

Когда я попробовал это, я не смог заставить SoundPlayer видеть изменения в потоке, даже если я установил его свойство Stream. Я был вынужден создавать новый SoundPlayer ... каждые 40 миллисекунд ??? Нет.

Что ж, я хочу вернуться к этому коду сегодня и начал смотреть на участников SoundPlayer. Я видел "SoundLocation" и читал его. Там сказано, что побочным эффектом установки SoundLocation будет обнуление свойства Stream, и наоборот для Stream. Поэтому я добавил строку кода, чтобы установить для свойства SOundLocation какое-то фиктивное значение, «x», а затем установил для свойства Stream значение my (только что измененное) MemoryStream. Блин, если он не улавливал это и не воспроизводил тон ровно столько, сколько я просил. Кажется, нет никаких сумасшедших побочных эффектов, таких как мертвое время после этого или увеличение памяти, или ??? На настройку потока WAV и последующую загрузку / запуск плеера уходит 1-2 миллисекунды, но это очень мало и цена приемлемая!

Я также реализовал свойство Frequency, которое повторно генерирует сэмплы и использует трюк Seek / BinaryWriter для наложения старых данных в RIFF / WAV MemoryStream с тем же количеством сэмплов, но с другой частотой, и снова сделал то же самое. вещь для свойства амплитуды.

Этот проект находится на SourceForge . Вы можете найти код C # для этого взлома в SPTones.CS на этой странице в браузере SVN . Спасибо всем, кто предоставил информацию по этому поводу, в том числе @arke, чьи мысли были близки мне.Я ценю это.

8
ответ дан 29 November 2019 в 05:53
поделиться

Преобразовать из ManagedDX в SlimDX ...

Редактировать: Что вас останавливает, кстати, просто предварительное создание n образцов синусоидальной волны? (Где n - ближайшее к желаемому количеству миллисекунд). На самом деле создание данных не занимает много времени. Кроме того, если у вас есть буфер 22 кГц и вам нужны последние 100 выборок, почему бы вам просто не отправить 'buffer + 21950' и установить длину буфера на 100 выборок?

1
ответ дан 29 November 2019 в 05:53
поделиться

Лучше всего просто генерировать синусоидальные волны и тишину вместе в буфер, который вы играете. То есть всегда играйте во что-нибудь, но записывайте все, что вам нужно дальше, в этот буфер.

Вы знаете частоту дискретизации, и, учитывая частоту дискретизации, вы можете рассчитать количество сэмплов, которое вам нужно записать.

uint numSamples = timeWantedInSeconds * sampleRate;

Это количество сэмплов, необходимое для генерации синусоидальной волны или тишины, в зависимости от того, что. Затем просто заполните буфер по мере необходимости. Таким образом, вы получите максимально точное время.

3
ответ дан 29 November 2019 в 05:53
поделиться

Попробуйте использовать XNA.

Вам нужно будет предоставить файл или поток для статического тона, который вы можете зациклить. Затем вы можете изменить высоту и громкость этого тона.

Поскольку XNA создан для игр, у него не будет никаких проблем с задержками в 40 мс.

1
ответ дан 29 November 2019 в 05:53
поделиться
Другие вопросы по тегам:

Похожие вопросы: