Главная » Программирование звука » Серверы и потоки

0

Для   воспроизведения   звука   с   помощью   NAS   необходимо   иметь   подключение  к  серверу  и  организовать  на  этом  сервере  поток.  Поток   это  обрабатывающий  звук  конвейер,  с  помощью  которого  данные  передаются от  источника  до  получателя.   C   помощью   потоков   можно   различными   способами   комбинировать данные, поступающие из нескольких источников.

Листинг 8.3. Частные члены класса NasPlayer

private:

AuServer *_server; AuFlowID _flow;

Метод   Play   сначала   устанавливает   соединение   с   сервером,   что   включает в себя формирование и регистрацию потока. B NAS все потоки при создании находятся в состоянии паузы. Как только поток будет выведен из состояния  паузы, сервер начнет запрашивать аудиоданные. Заметим, что в системе NAS задача контроля за двойной буферизацией возложена на сервер, клиентская часть просто отвечает  на  запросы,  передавая  серверу  данные.  Все  перечисленные  ниже  функции типа AuXxxx относятся к библиотеке NAS.

Листинг 8.4. Реализация класса NasPlayer

void NasPlayer::Play(){

OpenServer();                   // Устанавливаем  связь

// с сервером.

InitializeQueue(128*1024L);   // Инициализация очереди

// длиной  128 Кб.

AuStartFlow(_server,_flow,NULL); // Запускаем  поток.

while (!_finished) {            // Задерживаем  сообщения  до  тех

// пор, пока воспроизведение  не

// будет закончено.

AuEvent ev;

AuNextEvent(_server, AuTrue, &ev); AuDispatchEvent(_server, &ev);

}

AuCloseServer(_server);        // Закрываем  подключение

// к  серверу.

}

Вызов подсистем и сообщения

Всякий  раз,  когда  аудиосерверу  требуется  следующая  порция  данных,  он  пересылает клиенту сообщение. Метод Play, o котором мы рассказывали выше, для обработки   этих   сообщений   использует   библиотечную   функцию   AuDispatchEvent.  Эта  функция  вызывает  функцию  обратного  вызова.  Если  функция  обратного  вызова  получает  уведомляющее  (notify)  сообщение,  она  активизирует  метод Notify,  который  интерпретирует  сообщение,  и  инициализирует  SendData для пересылки на сервер данных в соответствии с запросом.

Листинг 8.3. Частные члены класса NasPlayer (продолжение)

private:

void OpenServer(); // Подключение  к  серверу. friend AuBool NasEventHandler(AuServer *server,

AuEvent *event, AuEventHandlerRec *eventData); AuBool Notify(AuElementNotifyEvent *notifyEvent);

void SendData(AuUint32 numBytes);

B каждом запросе от сервера NAS указывается, какой объем данных требуется передать серверу. Сервер требует к себе особенно внимательного отношения:

клиентская  часть  должна  предоставить  именно  тот  объем  данных,  который  был затребован. Поскольку затребованные данные могут находиться в очереди «в разбивку», я копирую данные из очереди во вспомогательный буфер, а затем переписываю этот буфер на сервер.

.

Листинг 8.3. Частные члены класса NasPlayer (продолжение)

private:

#define nasBufferSize 100000

Sample16 _buffer[nasBufferSize];

Библиотечная   функция   AuDispatchEvent пересылает   сообщения   функции обратного   вызова.   Функция   OpenServer регистрирует   функцию   NasEventHandler в  качестве  функции  обратного  вызова,  которая  будет  обрабатывать  сообщения. Таким образом, каждый раз, когда аудиопоток нуждается в пополнении данных, сервер посылает сообщение, которое в конце концов доходит до функции NasEventHandler.

Основная  задача  этой  функции   преобразовать  вызов  функции  в  активизацию метода. Она использует для хранения указателя на объект класса NasPlayer поле данных пользователя и запускает метод Notify для обработки сообщения.

Листинг 8.4. Реализация класса NasPlayer (продолжение)

AuBool NasEventHandler(AuServer *,     // He используется.

AuEvent *event, AuEventHandlerRec *eventData)

{

NasPlayer *me = reinterpret_cast<NasPlayer *>(eventData >

data);

switch (event->type) {

case AuEventTypeElementNotify:

return me->Notify(reinterpret_cast<AuElementNotifyEvent

* >(event));

default:                             // Произошло  какое-то

// другое событие.

break;

}

return AuTrue;

}

Для  идентификации  события  требуется  многоуровневая  классификация.  Первый  уровень   это  mun  сообщения,  который  анализируется  функцией  NasEventHandler.  Метод  Notify обрабатывает  сообщения  уведомляющего   типа,  классифицируя  их  в  соответствии  с  видом  уведомления  и  причиной,  вызывающей уведомление такого вида. Здесь надо выделить два важных события: первое когда количество  данных  на  сервере достигает  минимальной  отметки,  и  второе   когда сервер вынужден прервать вывод из-за отсутствия данных в буфере. B результате этих событий происходит обращение к функции SendData для получения дополнительной порции данных и пересылки их на сервер.

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

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

что при остановке выполнения программы сервер продолжает работать.

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

Листинг 8.4. Реализация класса NasPlayer (продолжение)

AuBool NasPlayer::Notify(AuElementNotifyEvent *notifyEvent) {

switch (notifyEvent->kind) {

case AuElementNotifyKindLowWater:    // Достигнут   минимальный

// уровень.

SendData(notifyEvent->num_bytes); // Переслать

// дополнительные данные.

break;

case AuElementNotifyKindState:

// Работа сервера  прервана

// из-за  недостатка данных?

if ( (notifyEvent->cur_state == AuStatePause)

&&(notifyEvent->reason == AuReasonUnderrun)) SendData(notifyEvent->num_bytes);

else                              // Любое  другое изменение

// статуса: просто

// заканчиваем.

_finished = true;

break;

}

return AuTrue;

}

Передача данных на сервер проблем не вызывает. Нужно просто извлечь данные  из  очереди и передать  их  серверу. Сложность только одна: сервер  сообщает, сколько ему нужно байтов, а не отсчетов. Также учтите, что, в отличие от операционных  систем  Windows  и  Mac  OS,  очередь  перезаполняется  сразу,  а  не  в  какомлибо другом потоке выполнения программы.

Листинг 8.4. Реализация класса NasPlayer (продолжение)

void NasPlayer::SendData(AuUint32 numBytes){

unsigned long bytesRead

= FromQueue(_buffer,numBytes/sizeof(Sample16))

* sizeof(Sample16);

bool allDone = ((bytesRead < numBytes) && _endOfSource); AuWriteElement(_server, _flow, 0, bytesRead, _buffer,

allDone, NULL);

FillQueue();

}

Сервер  NAS  не  использует  хранимую  отдельно  информацию  о  разрядности и  формате  данных.  Наоборот,  для  отображения  обоих  параметров  применяется единый код. Сервер может воспринимать данные, записанные с помощью различных типов ИКМ, а наряду с ними и данные нескольких форматов, использующих

компрессию.  (He  забывайте,  что  сервер  может  быть  запущен  на  другой  машине, использующей  иной  порядок  следования  байтов,  поэтому  необходимо  указывать, где  находится  младший  разряд  LSB,  а  где  старший   MSB.)  Эта  функция  преобразовывает  информацию  о  разрядности  отсчета  в  подходящий  код  сервера. Я  также  тестирую  порядок  следования  байтов  на  локальной  машине,  что  позволяет сообщить серверу, в каком формате будут записаны поступающие 16-разрядные выборки.

Данная функция написана в предположении, что short это 16-битный це-

лочисленный формат. Bo многих современных компьютерах так оно и есть.

Листинг 8.4. Реализация класса NasPlayer (продолжение)

unsigned char NasFormatCode(int sampleBits)

{

if (sampleBits == 8)

return AuFormatLinearSigned8;

int lsb = 0;          // По  умолчанию: формат  MSB.

{                     // Проверка внутреннего  формата

// системы  на   соответствие  формату LSB.

union { short int sixteen;       // 16-битное  значение.

struct { char a, b; } eight;  // Два   байта.

} lsbTest ;

lsbTest.sixteen = 1;              // Задаем младший   байт,

// отбрасываем старший.

if (lsbTest.eight.a) { lsb = 1; } // Проверяем  первый  байт.

}

if (lsb) return AuFormatLinearSigned16LSB;

else return AuFormatLinearSigned16MSB;

}

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

По теме:

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