Клас AudioAbstract

Майже всі програми обробки звуку є спадкоємцями класу AudioAbstract Внаслідок цього можливості класу AudioAbstract використовуються тими засобами обробки звуку, про які я збираюся розповісти в цій книзі Цей клас визначається у файлі audioh, що є просто характерним визначенням класу C + + Спочатку ми розглянемо загальний опис цього класу, а потім по черзі розробимо його компоненти

Лістинг 43 Програма audioh

#ifndef AUDIO_H_INCLUDED

#define AUDIO_H_INCLUDED

#include &lttypeinfo&gt

#include &ltiostream&gt

#include &ltcstddef&gt

/ / Подальша рядок програми необхідна тільки в тому випадку,

/ / Якщо використовуваний вами компілятор строго слід ANSI-

/ / Стандарту мови C + + (що практично не зустрічається)

/ / Однак у деяких компіляторах ця можливість

/ / Не реалізована взагалі

/ / Якщо ваш компілятор видасть повідомлення про помилку в цьому рядку,

/ / Просто позначте її як коментар і спробуйте

/ / Відкомпілювати програму заново

using namespace std

class AudioAbstract {

}

#endif

Розглянутий вище заголовний файл містить визначення класу AudioAbstract, і в ньому передбачено місце для ряду додаткових функцій B асоційоване Cpp файлі я визначаю кілька нетривіальних функційчленов (методів)

Лістинг 44 Програма audiocpp

#include &quotaudioh&quot

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

Лістинг 45 Оголошення допоміжних елементів класу AudioAbstract

typedef short AudioSample / / Окрема аудіовиборка typedef unsigned char AudioByte / / 8-бітний беззнаковий байт

Обєкти класу поділяють можливість передавати інформацію по напрямку від одного обєкта до іншого Реальна аудіоінформація передається за запитом її обєктом у попереднього обєкта

Лістинг 46 Інтерфейс класу AudioAbstract

private:

AudioAbstract * _previous / / Обєкт, від якого потрібно

/ / Отримати дані

AudioAbstract * _next / / Обєкт, який одержує дані

/ / З нашого обєкта

public:

AudioAbstract * Previous (void) {return _previous } Void Previous (AudioAbstract * a) {_previous = а } AudioAbstract * Next (void) {return _next }

void Next(AudioAbstract *a) {_next = a}

За замовчуванням при ініціалізації в конструкторі використовуються нульові покажчики Змінні _samplingRate і _channels ми обговоримо трохи пізніше

Лістинг 46 Інтерфейс класу AudioAbstract (продовження)

public: AudioAbstract(void) {

_previous = 0

_next = 0

_samplingRate = 0  _samplingRateFrozen = false

_channels = 0        _channelsFrozen = false

}

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

Лістинг 46 Інтерфейс класу AudioAbstract (продовження)

public:

AudioAbstract(AudioAbstract *audio) {

_previous = audio

_next = 0

audio-&gtNext(this)

_samplingRate = 0 _samplingRateFrozen = false

_channels = 0        _channelsFrozen = false

}

Деструктор класу AudioAbstract не виробляє ніяких дій, але для того щоб уникнути проблем з підкласами, його потрібно описати як virtual

Лістинг 46 Інтерфейс класу AudioAbstract (продовження)

public:

virtual ~AudioAbstract(void) {}

Метод GetSamples є основним для модулів обробки звуку Для створення нового такого модуля нам доведеться перевизначити цей метод, щоб виконати ті операції, для яких призначений даний модуль Крім того, знадобиться створити конструктор для виконання всієї необхідної ініціалізації

Лістинг 46 Інтерфейс класу AudioAbstract (продовження)

public:

/ / Повертає кількість реально лічених вибірок

/ / Звуку або 0 у разі помилки Якщо не відбулося помилки

/ / Або не досягнуть кінець файлу, цей метод повинен завжди

/ / Повертати запит повністю

virtual size_t GetSamples(AudioSample *, size_t) = 0

Ha практиці в аудіообектах, зчитувальних дані з файлу, метод GetSamples зазвичай реалізується через метод ReadBytes, який видає необроблені байти За замовчуванням ReadBytes просто передає запит наступного аудіообекту (Опис того, як подібна переадресування використовується для роботи з різноманітними методами компресії в класах, призначених для читання файлів, можна знайти в главі 10)

Лістинг 46 Інтерфейс класу AudioAbstract (продовження)

public:

virtual size_t ReadBytes(AudioByte * buff, size_t length) {

return Previous()-&gtReadBytes(buff,length)

}

Стереозвук

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

Якщо говорити про стереозвуці, то подібна організація даних передбачає, що каналичергуються:відліки для лівого каналу записуються по черзі з отсчетами для правого каналу Подібний підхід широко поширений при запису даних за допомогою ІКМ у всіх відомих мені файлових форматах вважається, що в ІКМ-запису стереозвуку дані чергуються Більш того, судячи з усього, широкого поширення набуло угоду, згідно з яким дані для лівого каналу передують даними для правого Ha практиці це означає, що мені при розробці численних класів для роботи зі звуком немає необхідності замислюватися про багатоканальних даних Якщо файл узгоджується з системним аудіоінтерфейсом, то необхідності проводити в ході роботи зі звуком небудь явні перетворення не виникне

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

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

Узгодження

Для роботи будь-якого модуля обробки звуку необхідно, щоб були узгоджені два параметри переданого потоку звукової інформації: частота дискретизації і кількість каналів (один для монозаписи, два для стерео)

Щоб спростити використання цих обєктів, вони автоматично погоджують ці параметри Наприклад, як тільки один з обєктів вперше запросить частоту дискретизації (з базового класу AudioAbstact), буде передано повідомлення останньому елементу ланцюга

Цей замикає обєкт запросить всі обєкти в ланцюзі і після цього задасть базові параметри у всіх згаданих обєктах

Узгодження грунтується на двох змінних для кожного параметра B змінної _samplingRate зберігається поточне значення частоти дискретизації,

повязана з нею змінна _samplingRateFrosen служить ознакою того,

чи було значення _samplingRate задано в ході узгодження

Процес узгодження запускається щоразу, коли запитується значення

частоти дискретизації Якщо поточне значення цієї частоти ще не зафіксовано (змінна _samplingRateFrosen), то перед тим як відповісти на запит, необхідно попередньо узгодити значення частоти дискретизації і отримати її правильне значення Аналогічним чином спроба змінити значення змінної приречена на невдачу, якщо узгодження цього параметра вже проводилося

Лістинг 47 Інтерфейс узгодження частоти дискретизації класу AudioAbstract

private:

long _samplingRate

bool _samplingRateFrozen

public:

virtual long SamplingRate(void) {

if ( _samplingRateFrozen) / / Значення не зафіксовано

NegotiateSamplingRate () / / Тоді визначаємо його

return _samplingRate / / Повертаємо значення

}

virtual void SamplingRate (long s) {/ / Встановлює

/ / Частоту дискретизації

if (_samplingRateFrozen) {

/ / He можу змінити

/ / Частоту дискретизації

cerr &lt&lt &quotCant change sampling rate\n"

exit (1)

}

_samplingRate = s

}

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

Обраний мною підхід в цілому включає в себе кілька методів Ha рис 41

представленабагатоступенева схема,ілюструє взаємозвязок цих методів Ha ній показані повідомлення, якими обмінюються три обєкта (представлені вертикальними лініями) в ході роботи (час збільшується в напрямку зверху вниз) Як ви можете переконатися, на початок цієї схеми доводиться момент, коли центральний обєкт запрошувати значення частоти дискретизації Так як це значення поки не зафіксовано, обєкт посилає повідомлення NegotiateSamplingRate своєму лівому сусідові, спонукаючи останній обєкт (програвач звуку) ініціювати процес повного узгодження

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

Лістинг 47 Інтерфейс узгодження частоти дискретизації класу AudioAbstract (продовження)

public:

virtual void NegotiateSamplingRate(void)

Лістинг 48 Узгодження частоти дискретизації класу

AudioAbstract

void AudioAbstract::NegotiateSamplingRate (void) {

if (Next ()) / / Обєкт крайній зліва

Next () -> NegotiateSamplingRate () / / Ні, продовжуємо роботу

else {/ / Так

long min = 8000, max = 44100, preferred = 44100 MinMaxSamplingRate (& min, & max, & preferred) / / Отримуємо

/ / Переважні значення

if (min> max) {/ / Перевірка на недійсну відповідь

/ / He вдається узгодити

/ / Частоту дискретизації

cerr &lt&lt &quotCouldnt negotiate sampling rate\n"

exit(1)

SetSamplingRateRecursive (preferred) / / Встановлюємо

/ / У всіх обєктах

/ / Обрані значення

}

}

Процес узгодження полягає у виборі діапазону значень, прийнятного для всіх обєктів B обєкті також можна вказати preferred (переважне) значення цього параметра, що не що виходить за даний діапазон Якщо якомусь обєкті необхідно строго задати певну частоту, слід вказати однакові значення для мінімального і максимального меж

Bo багатьох класах може знадобитися зробити підміну методу MinMaxSamplingRate для того, щоб обчислити їх параметри Після виконання перевантаженого методу для продовження процесу узгодження викликається метод AudioAbstract :: MinMaxSamplingRate Наприклад, в обєкті, що виробляє читання даних з файлу, після читання заголовка знадобиться зробити паузу до тих пір, поки обєкт не отримає повідомлення з запитом інформації з файлу

Як тільки буде вибрано переважне значення, метод SetSamplingRateRecursive задасть це значення у всіх обєктах

Лістинг 47 Інтерфейс узгодження частоти дискретизації класу AudioAbstract (продовження)

public:

virtual void MinMaxSamplingRate(long *min, long *max, long

*prefer)

virtual void SetSamplingRateRecursive(long s)

Лістинг 48 Узгодження частоти дискретизації класу AudioAbstract

(Продовження)

void AudioAbstract::MinMaxSamplingRate(long *min, long *max, long *preferred) {

if (Previous()) Previous()-

&gtMinMaxSamplingRate(min,max,preferred)

if (_samplingRate) *preferred = _samplingRate

if (*preferred &lt *min) *preferred = *min

if (*preferred &gt *max) *preferred = *max

}

void AudioAbstract::SetSamplingRateRecursive(long s) {

if (Previous ()) / / Спочатку встановлюємо

/ / Для того, що справа

Previous () -> SetSamplingRateRecursive (s) SamplingRate (s) / / Встановлюємо

_samplingRateFrozen = true / / Так, частота узгоджена

}

Більша частина повідомлень, використовуваних при узгодженні, відноситься до типу virtual, що дозволяє окремим підкласам підміняти специфічні деталі узгодження

Узгодження кількості каналів проводиться аналогічним чином Можливо, деякі програми могли б отримати перевагу, використовуючи різні формати AudioSample (вибірка), такі як 8-бітний і 16-бітний Ви легко змогли б аналогічним чином провести додаткове узгодження величини моментального значення

Лістинг 49 Інтерфейс узгодження кількості каналів класу AudioAbstract

private:

long _channels

bool _channelsFrozen

public:

virtual int Channels(void) {

if (_channelsFrozen) NegotiateChannels()

return _channels

}

virtual void Channels(int ch) {

if (_channelsFrozen) {

/ / He можу змінити число каналів

cerr &lt&lt &quotCant change number of channels\n"

exit(1)

}

_channels = ch

}

virtual void NegotiateChannels(void)

virtual void MinMaxChannels(int *min, int *max, int

*preferred)

virtual void SetChannelsRecursive(int s)

Лістинг 410 Узгодження кількості каналів класу AudioAbstract

void AudioAbstract::NegotiateChannels(void) {

if (Next())

Next()-&gtNegotiateChannels()

else {

int min = 1, max = 2, preferred = 1 / / Значення, використовувані

/ / За замовчуванням

MinMaxChannels(&ampmin,&ampmax,&amppreferred)

if (min &gt max) {

/ / He вдалося узгодити

/ / Частоту дискретизації

cerr &lt&lt &quotCouldnt negotiate sampling rate\n"

exit(1)

} SetChannelsRecursive(preferred)

}

}

void AudioAbstract::MinMaxChannels(int *min, int *max, int

*preferred) {

if (Previous()) Previous()-&gtMinMaxChannels(min,max,preferred)

if (_channels) *preferred = _channels

if (*preferred &lt *min) *preferred = *min

if (*preferred &gt *max) *preferred = *max

}

void AudioAbstract::SetChannelsRecursive(int ch) {

if (Previous()) Previous()-&gtSetChannelsRecursive(ch) Channels(ch)

_channelsFrozen = true

}

Джерело: Кінтцель Т Керівництво програміста по роботі зі звуком = 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>

*

*