Главная » Программирование звука » Чтение файлов VOC

0

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

Листинг 16.6. Программа voc.h

#ifndef VOC_H_INCLUDED

#define VOC_H_INCLUDED

#include "audio.h"

#include "compress.h"

#include <iostream>

bool IsVocFile(istream &file);

class VocRead: public AudioAbstract {

};

#endif

Листинг 16.7. Программа voc.cpp

#include <cstring>

#include "voc.h"

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

private:

istream &_stream;

public:

VocRead(istream &input = cin);

~VocRead() {

if (_decoder) delete _decoder;

};

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

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

VocRead::VocRead (istream &input):AudioAbstract(),_stream(input)

{

// Формат   файла: Creative Labs VOC. cerr << "File Format: Creative Labs VOC\n";

ReadHeader();

do {     // Ищем блок звуковых блок или   блок признака

// окончания.

GetBlock();

} while ((_blockType != 1) && (_blockType != 0));

}

Так  как  определять  параметры  звука  могут  несколько  типов  блоков,  важно помнить, были ли прежде установлены эти значения. Ниже представлена инициализация основных звуковых параметров запрещенным значением (-1).

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

_fileChannels = 1;

_fileSampleRate = 1;

_fileCompression = 1;

_fileWidth = 1;

_decoder = 0;

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

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

private:

int _fileChannels; int _fileSampleRate; int _fileWidth;

int _fileCompression;

AbstractDecompressor *_decoder;

protected:

void MinMaxSamplingRate(long *min, long *max, long *preferred)

{

*min = *max = *preferred = _fileSampleRate;

};

void MinMaxChannels(int *min, int *max, int *preferred) {

*min = *max = *preferred = _fileChannels;

};

size_t GetSamples(AudioSample *buffer, size_t numSamples);

Метод  GetSamples усложнен  за  счет  добавления  возможности  перемежения блоков  пауз  (несжимаемых)  и  обычных  звуковых  данных,  которые  необходимо декодировать. Для наиболее распространенных файлов VOC, содержащих только

данные  ИКМ,  это  не  представляет  проблемы.  K  сожалению,  фирма  Creative  Labs не  определила,  какое  значение  это  имеет  для  сжатых  аудиоданных.  (Неясно,  должен  ли  компрессор  заново  инициализироваться  после  блока  паузы  или  использование блоков пауз в сжатых данных просто запрещено.)

Метод  GetSamples проверяет  тип  текущего  блока  и  решает,  игнорироватьли его  (блок  не  содержит  звуковых  данных),  заполнить  буфер  нулевыми  отсчетами (если это блок паузы) или запросить данные у декомпрессора.

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

size_t VocRead::GetSamples(AudioSample *buffer, size_t numSamples) {

size_t samplesReturned = 0;

while (numSamples > 0) {

switch(_blockType) {

case 0:      // Конец   файла.

return samplesReturned;

case 1:      // Кодированные звуковые  данные.

case 2: {

size_t samplesRead = _decoder-

>GetSamples(buffer,numSamples); buffer += samplesRead; numSamples = samplesRead; samplesReturned += samplesRead;

if (bytesRemaining == 0) GetBlock();

break;

}

case 3:      // Блок  паузы.

while ((numSamples > 0) && (bytesRemaining > 0)) {

*buffer++ = 0; samplesReturned++; numSamp1es–; bytesRemaining–;

}

if (bytesRemaining == 0) GetBlock();

break;

default: GetBlock(); break;

}

}

return samplesReturned;

}

Объект-декодер, вызываемый GetSamples, вызовет метод ReadBytes для получения необработанных звуковых данных. Так как GetSamples гарантирует, что ReadBytes вызывается только при чтении блоков типа 1 или 2, процедура ReadBytes может быть простой.

Заметьте,  что  эта  версия  ReadBytes способна  возвращать  количество  байтов меньше запрошенного, даже если конец файла еще не достигнут. Метод GetSamples,

приведенный выше, реализует повторные вызовы декодера (возможно, выражающиеся  в  множественных  вызовах  ReadBytes)  до  тех  пор,  пока  запрос  не  будет обработан.

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

public:

size_t ReadBytes(AudioByte *buffer, size_t length);

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

size_t VocRead::ReadBytes(AudioByte *buffer, size_t length) {

if (length > bytesRemaining) length = bytesRemaining;

_stream.read(reinterpret_cast<char *>(buffer), length);

size_t bytesRead = _stream.gcount();

if (bytesRead < length) {

cerr << "Error: VOC file ended prematurely.";

exit(1);

}

bytesRemaining = bytesRead; // Количество  данных, оставшихся

в блоке.

return bytesRead;

}

Глава 17. Формат файла WAVE

B силу того что корпорация Microsoft использовала формат WAVE в операционной системе Windows, он стал одним из самых  популярных форматов звуковых файлов. B целом его структура основана на формате Interchange File Format (IFi формат файла обмена данными), который первоначально был разработан фирмой Electronic Arts  для  использования в  компьютерах  Amiga (см.  главу 19). Наоснове  IFF  разработан  формат  AIFF,  применяемый  фирмой  Apple  для  записи  звука (см. главу 18).

Вслед  за  IFF  Microsoft  разработала  общий  файловый  формат,  названный Resource Interchanging File Format (RIFF, формат файлов для обмена ресурсами), RIFF-файл  организован  как набор  вложенных  блоков.  Для  идентификациисодержимого   RIFF-файла   используются   дескрипторы.   Широко   распространеныдве версии этого формата: файлы WAVE (или WAV) для хранения звука и файлы AVI, в которых хранится видео.

Идентификация WAVE-файлов

WAVE-файл   это  частный  случай  файла  в  формате  RIFF,  а  каждый  RIFFфайл  начинается  с  группы  символов  RIFF.  После  этих  символов  идут  4  байта, в которых записана длина файла, а за ними код типа. Сейчас нас интересуют только WAVE-файлы.

Листинг 17.1. Распознавание файлов WA VE

bool IsWaveFile(istream &file) { file.seekg(0); // Ищем начало файла. unsigned long form = ReadIntMsb(file,4); if (form != ChunkName(‘R’,’I’,’F’,’F’))

return false;  // He RIFF-файл.

SkipBytes(file,4); // Пропускаем размер  блока.

unsigned long type = ReadIntMsb(file,4);

if (type == ChunkName(‘W’,’A’,’V’,’E’))

return true;

return false;  // Файл  формата  RIFF, но   не   WAVE.

}

O файлах формата RIFF и IFF

Поскольку  WAVE-файл  является  разновидностью  RIFF-файла,  я  начнусоб· суждения основных свойств формата RIFF. To, о чем я расскажу, будет в большинстве случаев справедливо и для других модификаций IFF, в том числе AIFF и IFF/

8SVX.

Как  показано  на  рис.  17.1,  RIFF-файл  представляет  собой  набор  вложенных блоков.  B  начале  каждого  блока  стоит  4-символьный  код  (RIFF,  fmt  или  LIST, короткие коды дополняются пробелами). Он задает тип блока. Блок с кодом fmt, например,  содержит  информацию  о  формате  звука.  Вслед  за  кодом  типа  блока идут 4 байта, в которых содержится информация о длине данных, хранящихся в блоке. Обратите внимание, что весь файл, изображенный на рис. 17.1, это один блок RIFF.  B  поле  длины  RIFF-блока  указывается  величина  ровно  на  8  байт  меньше, чем общий объем файла, так как тип и размер блока не учитываются.

Несколько  видов  блоков,  в  частности  RIFF  и  LIST,  показанные  на  рис.  17.1, относятся к контейнернъим блокам, в состав которых входят другие. Данные в контейнерном  блоке  начинаются  с  четырехсимвольного  кода,  указывающего  тип  содержащихся данных. Например, RIFF-блок на рис. 17.1 имеет код WAVE, который означает,  что  здесь  записаны  аудиоданные.  Внутренние  имена  блоков  могут  интерпретироваться  ??-разному  в  зависимости  от  того,  в  какой  контейнер  включен данный блок. Поскольку блок формата fmt заключен в контейнер RIFF WAVE, мы знаем,  что  он  описывает  аудиоданные,  а  будучи  помещен  в  контейнер  другого типа, мог бы содержать другую информацию.

Формат WAVE. Обзор

WAVE-файлы   это  RIFF-файлы,  в  которых  самый  внешний  блок   RIFFконтейнер  типа  WAVE.  Большинство  WAVE-файлов  содержат  блоки  fmt  и  data (рис. 17.2).

Поскольку  очень  многие  WAVE-файлы  обладают  одной  и  той  же  базовой структурой,   во   многих   программах,   написанных   неквалифицированными   программистами,  делается  расчет  на  то,  что  WAVE-файлы  имеют  фиксированный заголовок, формат которого представлен в табл. 17.1. Этот подход отлично работает  при  записи  WAVE-файлов  до  тех  пор,  пока  описываются  только  ИКМ-данные  и  правильно  задаются  размеры  различных  полей.  Программа  чтения  WAVEфайла, однако, должна быть более тщательно продумана.

Таблица 17.1. Формат примитивного WAVE-файла

Размер                                Описание

4                                                Тип блока: RIFF

4                                                Длина файла (минус) 8

4                                                Тип RIFF-контейнера: WAVE

4                                                Тип блока: fmt

4                                                Длина области данных блока формата: обычно 16

16                                              Область данных блока формата

4                                                Тип блока: data

4                                                Длина области звуковых данных

n                                                Звуковые выборки

WAVE-файлы  могут  содержать  и  другие  блоки.  B  частности,  вместо  одиночного  блока  data иногда  используется  контейнерный  блок  LIST,  состоящий  из блоков  типа  slnt,  соответствующих  периодам  отсутствия  звука,  и  data,  содержащих звуковые данные.

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

По теме:

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