Я не могу в это поверить ... Я вернулся к 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 значение моего (только что измененного) MemoryStream. Черт, если бы он не уловил это и не играл тоном ровно столько, сколько я просил. Кажется, нет никаких сумасшедших побочных эффектов, таких как мертвое время после или увеличение памяти, или ??? Требуется 1-2 миллисекунды, чтобы выполнить настройку потока WAV, а затем загрузить / запустить плеер, но он очень маленький и цена подходящая!
Я также реализовал свойство Frequency, которое повторно генерирует выборки и использует трюк Seek / BinaryWriter для наложения старых данных в RIFF / WAV MemoryStream с тем же числом выборок, но для другой частоты, и снова сделал то же самое вещь для свойства Amplitude.
Этот проект находится на SourceForge . Вы можете получить код C # для этого взлома в SPTones.CS с этой страницы в браузере SVN . Спасибо всем, кто предоставил информацию об этом, включая @arke, чье мышление было близко к моему. Я ценю это.