Главная » Программирование звука » Реализация очереди

0

Очередь  (также  известная  под  названием  кольцевого  буфера)   это  стандартное средство, используемое в случае, если потребитель и источник данных не могутсинхронизировать свою работу по времени. B нашем случае потребителем данных  является  аппаратура  воспроизведения  звука,  при  работе  с  ней  необходимо соблюдать  жесткие  требования  по  времени.  Остальная  часть  программы  подготавливает  данные,  используя  файловый  ввод/вывод,  компрессию  и  другие  операции, время выполнения которых непредсказуемо.

Листинг 5.3. Программа aplayer.cpp (продолжение)

void AbstractPlayer::InitializeQueue(unsigned long queueSize) {

_queue = new AudioSample[queueSize];

_queueEnd = _queue+queueSize;

_queueFirst = _queueLast = _queue; FillQueue();

}

B  операционных  системах  Mac  OS  и  Windows  обращения  к  очереди  происходят  из  различных  потоков.  При  использовании  многопотокового  программирования  необходима  особая  аккуратность.  Обратите  внимание,  что  соответствующие указатели  помечены  как  volatile (подвижные).  Кроме  того,  необходимо  отметить, что важен порядок, в котором производится обновление указателей. При добавлении, например, данных в очередь, данные сначала копируются в область памяти, отведенную под очередь, а затем корректируются указатели. Таким образом, если  какие-нибудь  потоки  считают  данные  из  очереди  в  ходе  процесса  добавления данных в очередь, им будут доступны для чтения только те из них, на которые ссылаются указатели.

He  забывайте  о  разнице  между  переменными  _queue и  _queueEnd,  которые указывают на начало и конец области памяти, занимаемой очередью и переменными  _queueFirst и  _queueLast,  ссылающимися  на  первый  и  последний  байты активного  блока  данных  в этой очереди. (B сущности,  _queueLast указывает  на первый байт, идущий после блока активных данных.) Здесь нужно рассмотреть два важных случая. B ситуации, когда _queueFirst меньше, чем _queueLast, активный блок данных расположен в середине очереди. Если _queueFirst больше, чем

_queueLast, значит очередь «завернулась».

Важно никогда не допускать максимального заполнения очереди. B противном

случае  вы  окажетесь  в  сложной  ситуации.  Значения  переменных  _queueLast

и _queueFirst могут быть равными, только если очередь пуста, или совсем

заполнена.  Если  вы  попытаетесь  максимально  заполнить  очередь,  создастся  впечатление, будто она пуста, в результате чего вы потеряете блок данных, объем которого в точности равен размеру очереди.

Также   учтите,   что,   поскольку   при   использовании   нескольких   каналов   звук записывается  в  виде  фреймов  (см.  раздел  «Стереозвук»  в  главе  4),  необходимо следить за тем, чтобы запрашиваемый объем данных был кратным числу каналов.

Листинг 5.З. Программа aplayer.cpp (продолжение)

void AbstractPlayer::FillQueue() {

if (!_endOfSource && (_queueLast >= _queueFirst)) {

if (_queueFirst == _queue)    // He заполняет буфер.

DataToQueue(_queueEnd _queueLast 1);

else

DataToQueue(_queueEnd _queueLast);

}

if (!_endOfSource && (_queueFirst > (_queueLast+1))) DataToQueue(_queueFirst _queueLast 1);

}

void AbstractPlayer::DataToQueue(long samplesNeeded) {

long samplesRead;

volatile AudioSample *pDest = _queueLast;

// Удостоверяем, что  длина

// ответа  кратна  числу каналов.

samplesNeeded = samplesNeeded % Channels();

samplesRead = Previous()->GetSamples(

const_cast<AudioSample*>(pDest),samplesNeeded);

pDest += samplesRead;

if (pDest >= _queueEnd) pDest = _queue;

_queueLast = pDest;

if (samplesRead < samplesNeeded)

_endOfSource = true;

}

Считываются  данные  из  очереди  аналогичным  образом.  Есть  только  одно  незначительное  замечание.  Различные  системные  модули  управления  звуком  предъявляют  различные  требования  к  размерам  отсчетов,  которые  должны  подаваться им  на  вход. Для  простоты все  разработанные мной  компоненты  работы  со звуком для  хранения  выборок  используют  тип  AudioSample,  однако  в  конкретных  системах может потребоваться 8-битное либо 16-битное представление отсчетов. Из этих соображений  ниже  мной  приведены  две  версии  метода  FromQueue.  Различаются они только типом используемого указателя.

Листинг 5.З. Программа aplayer.cpp (продолжение)

long AbstractPlayer::FromQueue(Sample16 *pDest, long destSize) {

long destRemaining = destSize;

if (_queueLast < _queueFirst) {

int copySize = _queueEnd _queueFirst;     // Количество

// доступных

// отсчетов.

if (copySize > destRemaining)

copySize = destRemaining; DataFromQueue(pDest,copySize); destRemaining = copySize; pDest += copySize;

}

if ((destRemaining > 0) && (_queueLast > _queueFirst)) {

int copySize = _queueLast _queueFirst;

if (copySize > destRemaining)

copySize = destRemaining; DataFromQueue(pDest, copySize); destRemaining = copySize; pDest += copySize;

}

if ((destRemaining > 0) && _endOfSource)

_endOfQueue = true;

return (destSize destRemaining);

};

long AbstractPlayer::FromQueue(Sample8 *pDest, long destSize) {

long destRemaining = destSize;

if (_queueLast < _queueFirst) {

int copySize = _queueEnd _queueFirst; // Количество

// доступных отсчетов.

if (copySize > destRemaining)

copySize = destRemaining; DataFromQueue(pDest,copySize); destRemaining = copySize; pDest += copySize;

}

if ((destRemaining > 0) && (_queueLast > _queueFirst)) {

int copySize = _queueLast _queueFirst;

if (copySize > destRemaining)

copySize = destRemaining; DataFromQueue(pDest, copySize); destRemaining = copySize; pDest += copySize;

}

if ((destRemaining > 0) && _endOfSource)

_endOfQueue = true;

return (destSize destRemaining);

};

Метод  DataFromQueue просто копирует сплошной блок данных из очереди.

Этот метод дважды вызывается из метода FromQueue.

Листинг 5.3. Программа aplayer.cpp (продолжение)

/* private: */

void AbstractPlayer::DataFromQueue(Sample16 *pDest, long copySize) {

volatile AudioSample *newQueueFirst = _queueFirst;

for(int i=0;i<copySize;i++)

*pDest++ = *newQueueFirst++

>> ((sizeof(*newQueueFirst) sizeof(*pDest)) * 8 );

if (newQueueFirst >= _queueEnd)

newQueueFirst = _queue;

_queueFirst = newQueueFirst;

}

/* private: */

void AbstractPlayer::DataFromQueue(Sample8 *pDest, long copySize)

{

volatile AudioSample *newQueueFirst = _queueFirst;

for(int i=0;i<copySize;i++)

*pDest++ = *newQueueFirst++

>> ((sizeof(*newQueueFirst) sizeof(*pDest)) * 8 );

if (newQueueFirst >= _queueEnd)

newQueueFirst = _queue;

_queueFirst = newQueueFirst;

}

Источник: Кинтцель Т.  Руководство программиста по работе со звуком = A Programmer’s Guide to Sound: Пер. с англ. М.: ДМК Пресс, 2000. 432 с, ил. (Серия «Для программистов»).

По теме:

  • Комментарии