Главная » Программирование звука » Блоки данных

0

При работе с файлом VOC чтение и обработка блоков данных ведется в порядке  их  появления  в  файле.  Есть  ряд  случаев,  в  которых  разные  блоки  могут определять одни и те же данные; первое определение всегда перекрывает последующие. Формат блока показан в табл. 16.2.

Таблица 16.2. Формат блока данных VОС

Длина                                                               Описание

1                                                                        Тип блока

3                                                                        Длина данных: л

n                                                                        Данные

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

private:

void GetBlock(void);

int _blockType;       // Тип  текущего блока.

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

void VocRead::GetBlock(void) {

long blockLength = 0;

_blockType = ReadIntLsb(_stream,1); if(_stream.eof()) _blockType = 0; if (_blockType != 0)

blockLength = ReadIntLsb(_stream,3);

switch(_blockType) {

}

}

Наиболее  важные  типы  блоков   это  блок  признака  окончания  (тип  0),  блоки расширения (типы 8 и 9) и блок звуковых данных (тип 1).

Блок признака окончания (тип 0)

Блок   признака   окончания   файла     это   единственный   блок,   формат   которого отличается  от  формата  блока,  показанного  в  табл.  16.2.  Он  состоит  из  единственного  нулевого  байта  без  ассоциированного  поля  длины.  Этот  блок  отмечает  конец файла формата VOC.

Листинг 16.4. Обработка различных типов блоков формата VOC

case 0:                  // Конец  данных. break;

Блок звуковых данных (тип 1)

Блок  звуковых  данных  (тип  1)  начинается  с  двух  байт,  определяющих  частоту  дискретизации  и  компрессии.  Далее  следуют  собственно  звуковые  данные.  Код частоты   дискретизации   считается   по   значению   частоты   дискретизации   следующим  образом:  256   (1000000/частота  дискретизации).  И  наоборот,  если  у  вас  есть код,  частота  дискретизации  равна  1000000/(256   код).  Коды  компрессии  показаны  в  табл.  16.3.  Учтите,  что  файл  VOC  может  содержать  только  один  блок  звуковых данных; дополнительные могут быть сохранены в блоке их продолжения.

Таблица 16.3. Коды компрессии

Код                                                              Описание

0                                                                   Беззнаковые 8-битные данные ИКМ

1                                                                   Сжатые, 4 бита на отсчет

2                                                                   Сжатые, 2,6 бита на отсчет

3                                                                  Сжатые, 2 бита на отсчет

4                                                                  Знаковые 16-битные данные ИКМ

6                                                                  ?-функция в соответствии со стандартом CCITT

7                                                                   Мю-функция в соответствии со стандартом CCITT

512                                                               16-битный формат Creative Labs в 4-битной АДИКМ

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

пользования только в блоках расширения типа 9.

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

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

private:

unsigned long bytesRemaining;        // Остаток байтов  в  этом

// блоке.

void ReadBlock1(long blockLength);

Чтобы  не  допустить  чрезмерного  увеличения  размеров  метода  GetBlock (некоторые  компиляторы  отказываются  работать  со  слишком  большими  функциями), выделим обработку в отдельный метод ReadBlock1.

Листинг 16.4. Обработка различных типов блоков формата VOC (продолжение)

case 1:     // Блок звуковых  данных.

ReadBlock1(blockLength) ;

break;

Формат  VOC  был  усовершенствован  путем  добавления  новых  типов  блоков, определяющими звуковой формат различными способами. Эти блоки (типа 8 и 9) всегда  предшествуют  блоку  звуковых  данных,  поэтому  необходимо  учесть,  что нельзя переопределять ни один параметр, который уже мог быть установлен в блоке, обработанном ранее.

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

void VocRead::ReadBlock1(long blockLength) {

// Читаем  и  интерпретируем первые два  байта…

int sampleRateCode = ReadIntLsb(_stream,1);

int compressionCode = ReadIntLsb(_stream,1);

if (_fileSampleRate == 1)

_fileSampleRate = 1000000L/(256-sampleRateCode);

if (_fileCompression == 1)

_fileCompression = compressionCode;

if (_fileChannels == 1)

_fileChannels = 1;

if (_fileWidth == 1)

_fileWidth = 8;

if (_decoder == 0) {  // Еще  нет объекта-декодировщика?

switch(_fileCompression) {

case 0:            // Беззнаковая 8-битная  ИКМ.

_decoder = new DecompressPcm8Unsigned(*this);

break;

case 4:            // Знаковая  16-битная ИКМ.

_decoder = new DecompressPcml6LsbSigned(*this);

break;

case 1:  // 8-битный  Creative Labs в  4-битную АДИКМ.

case 2:  // 8-битный  Creative Labs в  2,6-битную АДИКМ.

case 3:  // 8-битный  Creative Labs в  2-битную АДИКМ.

case 6:  // CCITT А-функция.

case 7:  // CCITT мю-функция.

case 512: // 16-битный  Creative Labs в  4-битную АДИКМ.

default:

// Этот  тип   компрессии  VOC-файлов

// не   поддерживается.

cerr << "I don’t support VOC compression type ";

cerr << _fileCompression << ".\n";

exit(1);

}

}

bytesRemaining = blockLength 2;

}

Блок продолжения данных (тип 2)

Файл VOC может иметь всего один блок звуковых данных типа 1. Тем не менее может возникнуть необходимость разбить звуковые данные на несколько блоков. Это обычно происходит, когда вы хотите вставить блок другого типа (например,  маркер повторения) в  середину  звуковых  данных. Последующая информация сохраняется в блоке их продолжения типа 2. Он содержит только данные, относящиеся к звуковым отсчетам. B нем не повторяется никакая информация о формате из предшествующего блока типа 1.

Листинг 16.4. Обработка различных типов блоков формата VOC (продолжение)

case 2:                  // Продолжение  звуковых  данных. bytesRemaining = blockLength;

break;

Блок паузы (тип 3)

Для уменьшения общего размера данных промежутки отсутствия звучания могут быть заменены блоками паузы, определяющими период тишины. Данные этого блока содержат 2-байтное число, задающее количество  используемых отсчетов тишины,  и  однобайтовый  код  частоты  дискретизации.  Теоретически  это  позволяет использовать  в  паузе  частоту  дискретизации,  отличную  от  применяемой  для  звуковых данных. Ha практике следует работать той же частотой дискретизации, что и в предшествующих звуковых данных.

Листинг 16.4. Обработка различных типов блоков формата VОС (продолжение)

case 3:        // Тишина.

{

if (blockLength != 3) {

// Блок паузы  VOC-файла имеет  длину…

cerr << "VOC Silence Block has length " << blockLength;

// Должно   быть  3.

cerr << "(should be 3)\n";

}

bytesRemaining = ReadIntLsb(_stream,2) + 1; int sampleRateCode = ReadIntLsb(_stream,1); if (_fileSampleRate == 1)

_fileSampleRate = 1000000/(256-sampleRateCode);

bytesRemaining = blockLength 3;

break;

}

Маркерный блок (тип 4)

Данные  маркерного  блока  представляют  собой  2-байтное  число.  Оно  может использоваться   мультимедийными   приложениями   для   синхронизации   событий. Например,   приложение   последовательного   просмотра   изображений   каждый   раз, когда  встречает  маркерный  блок,  изменяет  картинку  на  экране,  чтобы  она  соответствовала  звуковому  сопровождению.  Значения  0  и  65536  зарезервированы  и  не должны использоваться.

Листинг 16.4. Обработка различных типов блоков формата VОС (продолжение)

case 4: // Маркерный  блок. if (blockLength != 2) {

// Маркерный  блок  VOC-файла имеет  длину…

cerr << "VOC Marker Block has length " << blockLength;

// Должно   быть  2.

cerr << "(should be 2)\n";

}

// VOC маркер:

cerr << "VOC Marker: ";

cerr << ReadIntLsb(_stream,blockLength);

cerr << "\n";

bytesRemaining = blockLength 2;

break;

Текстовый блок (тип 5)

Данные  текстового  блока  представляют  собой  строку  ASCII,  заканчивающуюся   нулевым   символом.   Она   может   использоваться   для   сохранения   информации  об  авторских  правах  или  другой  полезной  текстовой  информации.  Процедуры  записи  призваны  обеспечить  включение  завершающего  нулевого  символа; процедуры  чтения  должны  корректно  работать,  даже  если  нулевой  символ  пропущен.

Листинг 16.4. Обработка различных типов блоков формата VOC (продолжение)

case 5: // Блок ASCII-текста.

{

char *text = new char[blockLength+1];

_stream.read(text,blockLength);

text[blockLength] = 0;

// Комментарий:

cerr << "Comment: " << text << "\n";

delete [] text; bytesRemaining = 0; break;

}

Циклы повторения (типы 6 и 7)

Блоки  типов  6  и  7  используются  для  организации  циклов  внутри  файла  формата  VOC.  Блок  типа  6  размещается  в  начале  области,  которую  необходимо  повторить, а блок типа 7 помечает ее конец. Данные маркера начала содержат 2-байтное число, показывающее, сколько раз область будет повторена (нулевое значение указывает,  что  данные  следует  проиграть  единожды).  Завершающий  блок  данных не содержит.

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

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

private:

int repeatDepth; // Количество  вложенных циклов.

#define maxRepeat 5

int repeatCounts[maxRepeat];

streampos repeatPosition[maxRepeat];

Листинг 16.5. Инициализация данных формата VOC

repeatDepth = 1;

Листинг 16.4. Обработка различных типов блоков формата VOC (продолжение)

case 6:        // Цикл   повторения.

{

if (blockLength != 2) {

// Блок цикла повторения  в VOC файле имеет  длину… cerr << "VOC Repeat Loop Block has length " << blockLength;

// Должно  быть  2.

cerr << "(should be 2)\n";

}

// Начало блока  повторения.

cerr << "Start of VOC Repeat Block.\n"; int repeatCount = ReadIntLsb(_stream,2); repeatDepth++;

if (repeatDepth > maxRepeat) {

// Слишком  много  вложенных повторений.

cerr << "Too many nested repeats.\n";

exit(1);

}

repeatCounts[repeatDepth] = repeatCount; repeatPosition[repeatDepth] = _stream.tellg(); bytesRemaining = blockLength 2;

break;

}

case 7:        // Конец   повторения.

{

bytesRemaining = blockLength;

if (blockLength != 0) {

// Блок конца повторения  в VOC файле  имеет длину…

cerr << "VOC `End Repeat’ block has length " <<

blockLength;

// Должно   быть  0. cerr << "(should be 0)\n";

}

if (repeatDepth < 0) {

// Неверно указан  блок конца повторения.

cerr << "Improper VOC `End Repeat’ block.\n";

break;

}

// Ha Маках   значение repeatCounts[repeatDepth]

// всегда  выводится как 1 или   0?

// Это   что, ошибка в  библиотеке  C++?

// Конец   блока повторений в  VOC-файле.

cerr << "End of VOC Repeat Block(";

cerr << repeatCounts[repeatDepth] << ")\n";

if (repeatCounts[repeatDepth] <= 0) { // End of repeat?

repeatDepth–;

} else {    // Позиция для  следующей итерации.

_stream.seekg(repeatPosition[repeatDepth]);

repeatCounts[repeatDepth]–;

}

break;

}

Блок расширения (тип 8)

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

Данные  блока  расширения,  структура  которого  приведена  в  табл.  16.4,  боль-

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

Код   частоты   дискретизации   для   стереозвука   обрабатывается   довольно   не-

обычно.  2-байтный  код,  как  правило,  вычисляется  следующим  образом:  65536  -

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

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

private:

void ReadBlock8(long blockLength);

Для предохранения GetBlock от чрезмерного разрастания я снова выделил

обработку блоков типа 8 в отдельную функцию.

Листинг 16.4. Обработка различных типов блоков формата VOC (продолжение)

case 8: // Расширение.

ReadBlock8(blockLength);

break;

Таблица 16.4. Данные блока расширения типа 8

Длина                                      Описание

2                                               Код точного значения частоты дискретизации

1                                               Код компрессии

1                                               Режим (0 для моно, 1 для стерео)

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

void VocRead::ReadBlock8(long blockLength) {

if (blockLength != 4) {

// Блок расширения 8 типа имеет длину…

cerr << "VOC Extension Block 8 has length " << blockLength;

// Должно   быть  4.

cerr << "(should be 4)\n";

}

int sampleRateCode = ReadIntLsb(_stream,2);

if (_fileSampleRate == 1)

_fileSampleRate = 256000000L/(65536L-sampleRateCode);

int compressionCode = ReadIntLsb(_stream,1);

if (_fileCompression == 1)

_fileCompression = compressionCode; int channels = ReadIntLsb(_stream,1); if (_fileChannels == 1) {

_fileChannels = channels + 1;

_fileSampleRate /= _fileChannels;

}

if (_fileWidth == 1)

_fileWidth = 8;

bytesRemaining = blockLength 4;

}

Блок расширения (тип 9)

Подобно блоку расширения типа 8 блок типа 9 был введен с целью добавления  новых  возможностей.  B  частности,  он  поддерживает  дополнительные  методы компрессии и в явном виде сохраняет информацию о формате звука. Данный блок предшествует  блокам  типа  1  или  8,  а  информация  в  нем  переопределяет  информацию в указанных блоках.

Блок типа 9, описанный в табл. 16.5, сохраняет звуковую информацию спосо-

бом,  который  значительно  отличается  от  способов  представления  звука  в  блоках типа  1  или  8.  Например,  частота  дискретизации  сохраняется  непосредственно, а не кодируется. Кроме того, она не корректируется по количеству каналов. Поле, определяющее  количество  битов  на  отсчет,  может  принимать  значения  от  2  (для сжатых данных) до 16 (для 16-битных данных ИКМ). Последние 4 байта зарезервированы для будущего расширения.

Таблица 16.5. Данные блока расширения типа 9

Длина                                       Описание

4                                                Частота дискретизации

1                                                Количество битов, используемых для каждой выборки

1                                                Количество каналов (1 для моно, 2 для стерео)

2                                                Код компрессии

4                                                Зарезервировано на будущее

B  целях  обеспечения  обратной  совместимости  процедура  записи,  которая  создает блоки типа 9, должна по возможности вставлять за ними блок типа 8. Однако,  поскольку  блок  типа  9  вводит  несколько  новых  кодов  компрессии  (включая

16-битный ИКМ, см. табл. 16.3), это будет получаться не всегда.

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

private:

void ReadBlock9(long blockLength);

Листинг 16.4. Обработка различных типов блоков формата VOC (продолжение)

case 9:    // Расширение.

ReadBlock9(blockLength);

break;

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

void VocRead::ReadBlock9(long blockLength) {

if (blockLength != 12) {

// Блок расширения типа  9 имеет длину…

cerr << "VOC Extension Block 9 has length " << blockLength;

// Должно   быть  12.

cerr << "(should be 12)\n";

}

long sampleRate = ReadIntLsb(_stream,4);

if (_fileSampleRate == 1)

_fileSampleRate = sampleRate;

int bitsPerSample = ReadIntLsb(_stream,1);

if (_fileWidth == 1)

_fileWidth = bitsPerSample;

int channels = ReadIntLsb(_stream,1);

if (_fileChannels == 1)

_fileChannels = channels;

int compressionCode = ReadIntLsb(_stream,2);

if (_fileCompression == 1)

_fileCompression = compressionCode; SkipBytes(_stream,blockLength 8);

}

Другие блоки

B настоящее время определены только блоки с типами от 0 до 9. Любой нераспо-

знанный тип блока следует игнорировать.

Листинг 16.4. Обработка различных типов блоков формата VOC (продолжение)

default:    // Пропускаем блоки всех  остальных типов.

// Пропускаем  VOC-блок нераспознанного типа.

cerr << "Ignoring unrecognized VOC block type " << _blockType

<< "\n"; SkipBytes(_stream,blockLength); break;

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

По теме:

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