Відтворення звуку в Windows

B той час, коли я пишу ці рядки, Microsoft пропонує використовувати новий інтерфейс власної розробки, званий DirectSound K жаль, цей інтерфейс зявився зовсім недавно і ще не отримав широкої підтримки Тому в цій книзі я розповім тільки про більш старому інтерфейсі wave Основна відмінність між цими двома інтерфейсами полягає в тривалості затримки: при використанні DirectSound непродуктивні затримки нижче, що дозволяє дуже швидко починати відтворення звуку Хоча ця можливість необхідна для розробки ігор, для інших додатків вона не є настільки важливою

Для забезпечення безперервного потоку звукових даних у Windows використовується подвійна буферизація Створюється, як мінімум, два буфера Поки записані в один з буферів звуки відтворюються, в решту можна записувати нові порції даних B системі Windows міститься звязаний список переданих вами буферів Як тільки дані в одному з цих буферів закінчуються, система переключається на наступний і звертається до функції зворотного виклику для заповнення звільнився буфера

Відзначимо одну важливу деталь: система створює новий потік для того, щоб управляти відтворенням звуку Розроблені вами функції відтворення звуку викликаються з цього потоку Якщо ви не дуже впевнено себе почуваєте в багатопотоковому програмуванні, то вам слід обмежити набір дій, виконуваних функціями зворотного виклику B пропонованої мною реалізації функція зворотного виклику просто зчитує дані з черги Черга повністю утримується в основному потоці A самі по собі процедури роботи з чергою, з точки зору потоків, безпечні

Лістинг 61 Програма winplayrh

/*

* Player class for Win32

*/

#ifndef WIN_PLAYER_H_INCLUDED

#define WIN_PLAYER_H_INCLUDED

#include &quotaudioh&quot

#include &quotaplayerh&quot

#include &ltwindowsh&gt

#include &ltmmsystemh&gt

# Define winBufferSize 10000 / / Кількість відліків в буфері

class WinPlayer : public AbstractPlayer {

private:

HWAVEOUT _device / / Аудіопристрій Windows, яке

/ / Відкриватимемо

volatile bool _paused / / Істина, якщо пристрій

/ / В стані паузи

int_sampleWidth / / Розрядність вихідних даних

int SelectDevice (void) / / Відкриває відповідне пристрій

/ / Дозволяємо функціям зворотного

/ / Виклику звертатися до функцій-членам

friend void CALLBACK WaveOutCallback(HWAVEOUT hwo, UINT uMsg, DWORD dwInstance, DWORD dwParaml, DWORD

dwParam2)

/ / Певні вище функції зворотного

/ / Виклику це просто пакувальники,

/ / Звертаються до цього методу

void NextBuff(WAVEHDR *)

public:

WinPlayer(AudioAbstract *a): AbstractPlayer(a) {

_device = 0

_paused = true

_sampleWidth = 0

}

~WinPlayer() {}

void Play () / / Власне відтворення запису

}

#endif

Оголосивши WaveOutCallback іншому класу, я дозволив цієї функції звертатися до захищених даних класу WinPlayer (зверніть увагу на те, що функція WaveOutCallback не є членом класу WinPlayer)

Лістинг 62 Програма winplayrcpp

#include &ltwindowsh&gt

#include &ltmmsystemh&gt

#include &ltiostream&gt

#include &quotaplayerh&quot

#include &quotwinplayrh&quot

Відтворення

Функції зворотного виклику використовуються тільки тоді, коли буфер стає порожнім B результаті цього дані з перших буферів доводиться виводити до того, як буде заіущен основний цикл Функції зворотного виклику повинні точно знати, що записати в буфери на вимогу системи навіть в тому випадку, якщо в черзі немає даних

Давайте розглянемо, що відбудеться, якщо програма виявиться нездатна досить швидко готувати дані Настане такий момент, коли система звернеться до функції зворотного виклику для того, щоб вона перезаписала буфер, а чергу виявиться порожньою B цей буфер необхідно записати якісь дані і повернути його системі, інакше цим буфером не можна буде користуватися Функція зворотного виклику могла б повернути порожній буфер, проте це призведе до негайного повторного виклику цієї ж функції Якщо затримка з підготовкою даних триватиме, то саме безглузде, що можна буде робити, це безглуздо завантажувати процесор переходами по порожніх буферам Можна було б заповнити буфер нулями і повернути його системі, проте це також призведе до безглуздого відтворення тиші

B такій ситуації краще всього перервати процес виведення Для цього буде потрібно певна взаємодія з основним циклом, в якому доведеться стежити за значенням змінної _paused і продовжити відтворення, як тільки буфери наповняться знову

Так як до змінної _paused йдуть звернення з двох потоків, дуже важливим стає порядок опрацювання звернень Функція зворотного виклику всегдаделает паузу, перш ніж встановити значення _paused, відповідне істині, а головний цикл встановлює для змінної _paused значення «брехня» перед тим, як продовжити відтворення звуку

Лістинг 63 Реалізація класу WinPalyer

void WinPlayer::Play(void) {

if (SelectDevice ()) return / / Відкриваємо підходяще

/ / Пристрій

waveOutPause (_device) / / Ще не відтворюємо

_paused = true

InitializeQueue (128 * 1024L) / / Виділяємо 128 Кб на чергу

WAVEHDR waveHdr[2]

for (int i=0 i&lt2 i ++ ) {

waveHdr [i] dwBufferLength / / Розмір в байтах

= winBufferSize * _sampleWidth/8

waveHdr[i]dwFlags = 0 waveHdr[i]dwLoops = 0 waveHdr[i]lpData

= reinterpret_cast&ltLPSTR&gt(

new BYTE[waveHdr[i]dwBufferLength *

Channels()])

waveOutPrepareHeader (_device, & waveHdr [i], sizeof (waveHdr [i])) NextBuff (& waveHdr [i]) / / Заповнення і копіювання

/ / Буфера у вихідні дані

}

_paused = false

/ / Чекаємо, поки відтворення

/ / Не припиниться, і обидва буфера

/ / Не звільняться

waveOutRestart (_device) / / Тепер починаємо

/ / Відтворення

while ( _endOfQueue / / Черга порожня

| | ((WaveHdr [0] DwFlags & WHDR_DONE) == 0) / / Дані

/ / В буферах закінчилися

| | ((WaveHdr [1] DwFlags & WHDR_DONE) == 0)) {FillQueue () / / Кінець заповнення черги

if (_paused) {/ / Якщо потік сервера знаходиться

/ / В стані паузи,

/ / Перезапускаємо його

_paused = false

/ / Вивід звуку перезапущений

cerr &lt&lt &quotSound output restarted\n"

waveOutRestart(_device)

}

Sleep (50 / * ms * /) / / Циклічні повторення

/ / Приблизно 20 раз

/ / В секунду

}

MMRESULT err = waveOutClose(_device)

while (err == WAVERR_STILLPLAYING) {/ / Якщо відтворення

/ / Ще не закінчилося ..

Sleep (250) / / Чекаємо трохи ..

waveOutClose (_device) / / Пробуємо знову ..

}

for(int i1=0 i1&lt2 i1++) {

waveOutUnprepareHeader(_device,&ampwaveHdr[i1] ,sizeof(waveHdr[i1]))

delete [] waveHdr[i1]lpData

}

}

Джерело: Кінтцель Т Керівництво програміста по роботі зі звуком = A Programmers Guide to Sound: Пер з англ М: ДМК Пресс, 2000 432 с, іл (Серія «Для програмістів»)

Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*