Это здорово! Так просто!
Но есть вопрос.
Бегло изучив DirectSound, я понял так, что буферы воспроизводятся один за одним (при наполнении буфера звуковой информацией в методе .Lock мы получаем AudioPtr1 для наполнения и AudioPtr2 для информации, куда перетечёт излишек байт). Может, неверно понял, но это даже не важно. Важно, что буферы плавно переходят друг в друга, исключая щелчки.
А вот вопрос:
Можно ли сделать так, чтобы первый буфер сыграл один раз и перетёк во второй буфер, но чтобы этот второй буфер играл ЦИКЛИЧЕСКИ?
А по выходе из цикла (Buffer.Stop или специальной командой) перетёк бы в третий буфер, который опять же БЕЗ цикла завершит звучание.
То есть, середина звукового файла должна быть зациклена. А начало и конец - свободны.
Ведь цикличность мы задаём только в команде DSBuffer.Play - параметром DSBPLAY_LOOPING. А как сделать плавное перетекание (и желательно автоматическое) между буферами без цикла и буферами с циклом?
Поясню, зачем это нужно.
Делаю движок для воспроизведения звуковых шрифтов (SoundFonts) по файлам SFZ. Парсинг файла SFZ уже сделал, основные команды программа запоминает и готова управлять воспроизведением сэмплов.
И тут-то загвоздка. Очень часто нужно сэмпл разделить на три части:
№1. Начало - сыграть один раз;
№2. Середина - играть циклически;
№3. Окончание - сыграть один раз после принудительного выхода из цикла №2 (по отпусканию MIDI-клавиши).
Вроде бы варианта два:
1) Принудительный луп вручную. Загружаем файл сэмпла в буфер целиком командой DS.CreateSoundBuffer и устанавливаем в конце лупа контрольную точку командой SetNotificationPositions. И следим: как только достигаем этой контрольной точки, возвращаем позицию командой DSBuffer.SetCurrentPosition в точку начала лупа, и так всё время. А когда пользователь отпускает клавишу, снимаем эту необходимость и проходим на окончание сэмпла. Недостатки: ручное слежение - это отъём ресурсов, вероятность промашек, щелчки на стыках...
2) Частично встроенный луп. Загружаем файл сэмпла в ТРИ РАЗНЫХ буфера: начало, луп и конец. Как только отыграли начало, программа ТУТ ЖЕ должна получить импульс на запуск второй фазы - циклической, по "DSBuffer.Play 0,0,DSBPLAY_LOOPING". Но откуда получить этот импульс? Придётся ставить уведомление:
- Код: Выделить всё
Dim Notify As IDirectSoundNotify
Dim Notifies() As DSBPOSITIONNOTIFY: ReDim Notifies(0)
Notifies(0).dwOffset = DSBPN_OFFSETSTOP
Notifies(0).hEventNotify=DX.CreateEvent.......
Notify.SetNotificationPositions 1, Notifies(0)
А для этого устанавливать и подключать DirectX 8 лишь для CreateEvent! Но ведь Event нам нужен не по достижении определённой точки времени в буфере, а просто по его окончании. Может, можно выполнить это проще? Без доп. зависимостей и т.д.
И всё равно программе придётся следить за каждым звуком: когда ему пора переходить в циклическую фазу?
А звуков-то много. Нет-нет, да и опоздает, не уследит за всеми.
А вдруг возможно запрограммировать такое поведение в сам DirectSound?
Приложил максимально упрощённый проект, где по нажатии кнопки воспроизводится звук колокольчика, но как программно перейти в середине этого звука в Цикл (Audio-Loop, позволяющий оттянуть это звучание сколь угодно долго без угасания) - я всё-таки не разобрался. Сэмплы так сделаны, что заранее известны точки цикла, чтобы он был без щелчков. Но как выйти в Цикл после Бесциклового Начала ноты?