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

0

Теперь,  когда  вы  в  общих  чертах  представляете,  как  устроен  файл  MOD,  поговорим  о  частностях.  Затем  начнем  разработку  программы  для  чтения  и  воспроизведения файлов MOD.

Проигрыватель  файлов  MOD  гораздо  более  сложен,  чем  проигрыватель  форматов  WAVE  или  AIFF  (но  менее  сложен,  хотя  это  спорно,  чем  проигрыватель MIDI).   Помимо   очевидно   необходимого   класса   ModRead,   определим   несколько вспомогательных:

?     ModNoteData хранит данные одной ноты (период, код инструмента и  код эффекта);

?     ModSong хранит все музыкальное произведение, включая трафареты, кото-

рые  содержат  множество  объектов  ModNoteData,  и  плей-лист.  Bo  время

воспроизведения   ModSong контролирует   состояние   текущего   трафарета

и тактов внутри него;

?     ModInstrument наследуется из AbstractInstrument и использует

объект

SampledInstrument для управления воспроизведением;

?     ModNote это объект воспроизведения, наследуемый от AbstractNote;

?     ModChannel хранит информацию о канале, включая ноту, проигрываемую

в текущий момент времени (ModNoteData и ModNote), и используемые по

умолчанию значения для различных эффектов.

Листинг 23.2. Программа mod.h

#ifndef MOD_H_INCLUDED

#define MOD_H_INCLUDED

#include <cstdio>

#include "audio.h"

#include "instrumt.h"

#include "sampled.h"

bool IsModFile(istream &file);

// Сначала проводим предварительное  определение  ряда  компонентов.

struct ModNoteData;

class ModInstrument;

class ModNote: public AbstractNote {

};

class ModInstrument: public AbstractInstrument {

};

struct ModNoteData {

};

class ModSong {

};

struct ModChannel {

};

class ModRead: public AudioAbstract {

};

#endif

Листинг 23.3. Программа mod.cpp

#include <cstdio>

#include <cmath>

#include <cstdlib>

#include <cstring>

#include "mod.h"

#include "audio.h"

#include "instrumt.h"

#include "sampled.h"

Инструменты

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

22 символа).

Таблица 23.2. Данные об инструменте в файле MOD

Байты                         Описание

22                                 Имя семпла (необязательно должно заканчиваться нулевым символом)

2                                  Длина отсчета, в словах

1                                  Точная настройка

1                                   Громкость

2                                   Начало области повторения, в словах

2                                  Длина области повторения, в словах

Внутри файла информация об инструменте разбита на две части. 30-байтный заголовок хранится в начале файла и содержит информацию, приведенную в табл. 23.2. Сами дискретные данные (8-битные знаковые отсчеты) записаны в конце файла.

Некоторые параметры, упомянутые в табл. 23.2, нуждаются в пояснении. Заметим, что длина данных и информация о повторении хранятся в виде числа 16-битных слов,  несмотря  на  то  что  сами  звуковые  данные  записаны  8-битными  знаковыми байтами. Некоторые варианты хранят начало области повторения как байтовое смещение. Область повторения обычно (но не всегда) продолжается до конца образца. Инструменты,  не  имеющие  области  повторения,  часто  сохраняются  со  значением начала области повторения равным нулю и длиной области повторения равной одному слову.

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

Громкость  всегда  находится  в  диапазоне  от  0  до  64.  Громкость  инструмента  это  значение,  используемое  по  умолчанию  для  всех  нот,  проигрываемых  на  данном  инструменте.  Некоторые  файлы  все-таки  содержат  значения,  выходящие  за рамки  этого  диапазона;  кроме  того,  различные  эффекты  могут  «выталкивать»  величину  громкости  за  установленные  пределы,  однако  проигрыватели  MOD  отсекают такие значения.

Точная  настройка  –  это  легкое  изменение  высоты  тона  звучания,  применяемое  к  инструменту.  Вместо  повторной  дискретизации  звука  (с  соответствующей неизбежной  потерей  качества)  музыкальные  редакторы  используют  данную  операцию  для  регулирования  высоты  проигрываемого  звука.  Значение  задается  числом от -8 до +7. Единица измерения 1/8 полутона.

Разобраться  с  именами  классов  не  всегда  просто.  Класс  ModNoteData компактно сохраняет основную нотную информацию, a ModNote это объект, непосредственно   осуществляющий   воспроизведение.   Bo   время   проигрывания   один объект  ModNoteData создается  для  каждой  ноты  всего  музыкального  произведения, а объект ModNote единственный на каждый канал.

ModInstrument не   обеспечивает   явным   образом   хранение   семпла     этот

процесс  реализуется  классом  SampledInstrument.  Тем  не  менее  он  обеспечи-

вает альтернативные способы задания высоты тона и громкости. B файлах MOD

вычисление  высоты  происходит  в  терминах  периода  (значения  делителя  звуковой подсистемы Amiga). Легче встроить преобразование периода в высоту в класс ModNote.

Листинг 23.4. Члены класса ModNote

private:

AbstractNote *_abstractNote;

public:

void Restart() {

if (_abstractNote) _abstractNote->Restart();

}

size_t AddSamples(AudioSample *buff, size_t length) {

if (_abstractNote) return _abstractNote-

>AddSamples(buff,length);

else return 0;

}

// Единственное  место, где предполагается, что мы

// используем ноту, представленную  семплом.

void SetSampleOffset(int offset) {

// Should use dynamic_cast here if (_abstractNote)

reinterpret_cast<SampledNote *>(_abstractNote)

->SetSampleOffset(offset);

}

void Pitch(float t) {

if (_abstractNote) _abstractNote->Pitch(t);

}

float Pitch() {

if (_abstractNote) return _abstractNote->Pitch();

else                return 440.0;

}

void EndNote(float v) {

if (_abstractNote) _abstractNote->EndNote(v);

}

private:

void Volume(float) {};

float Volume() {return 1.0;}

public:

void SetModVolume(int volume); void SetModPeriod(int period); ModNote(AbstractNote *,int period, int volume);

~ModNote();

Листинг 23.5. Члены класса Modlnstrument

private:

SampledInstrument *_sampledInstrument;

private:

char _name[23]; /* Строка  с  завершающим нулем. */

public:

const char *Name() { return _name; }

private:

signed char _finetune; /* от -8 до   +7. */ unsigned char _volume; /* от 0 до   64. */ long _length; /* B байтах.  */

long _repeatStart; /* Байтов от  начала.  */

long _repeatLength; /* Длина   области  повторения. */

public:

ModInstrument() { _sampledInstrument = 0; _name[0] = 0; }

virtual ~ModInstrument(){

if (_sampledInstrument)

delete _sampledInstrument;

};

void ReadHeader(istream &modFile); void ReadSample(istream &modFile); ModNote *NewModNote(int period); void SamplingRate(long s) {

AbstractInstrument::SamplingRate(s);

if(_sampledInstrument)

_sampledInstrument->SamplingRate(s);

}

long SamplingRate() {

return AbstractInstrument::SamplingRate();

}

private:

AbstractNote *NewNote(float pitch, float volume);

Так  как данные  инструмента  хранятся  разделенными  на  две  части, то  нам  понадобятся  два  метода  для  извлечения  этих  частей.  Метод  ReadHead извлекает основные  параметры,  ReadSample производит  считывание  данных.  Последний также  осуществляет  проверку  некоторых  особых  случаев,  таких  как  короткая  область повторения нулевых байтов.

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

void ModInstrument::ReadHeader(istream &modFile){

modFile.read(_name,22);

_name[22]= 0;

_length = ReadIntMsb(modFile,2) * 2;

_finetune = ReadIntMsb(modFile,1);

if (_finetune > 7) _finetune -= 16;

_volume = ReadIntMsb(modFile,1);

if (_volume > 64) _volume = 64;

_repeatStart = ReadIntMsb(modFile,2) * 2;

_repeatLength = ReadIntMsb(modFile,2) * 2;

}

void ModInstrument::ReadSample(istream &modFile) (

if (_length < 1) return; AudioSample *data

= new AudioSample[_length+_repeatLength+1024]; AudioByte *byteBuff

= reinterpret_cast<AudioByte *>(data);

modFile.read(reinterpret_cast<char *>(byteBuff),_length);

for(long s = _length-1; s>=0; s–) {

data[s] = static_cast<signed char>(byteBuff[s]) * (1<< (8*sizeof(AudioSample)-8));

}

// Корректируем область  повторения,

int repeatTest=0;

for(int i=_repeatStart;i<_repeatLength;i++)

repeatTest += data[i];

// If entire repeat is zero, set to no repeat if(repeatTest==0) {

_repeatLength = 0;

_repeatStart = _length;

}

// Преобразование MOD: 2-байтная  область  повторения

// в начале означает, что  в  файле повторений нет.

if((_repeatLength<=2) && (_repeatStart == 0)) {

_repeatLength = 0;

_repeatStart = _length;

}

_sampledInstrument = new SampledInstrument(data,

_length,_repeatStart,_repeatStart+_repeatLength);

// Параметры  Pitch() определяются исходя из  соглашения,

// что  для ноты   Ля  440 Гц  используется  делитель 254,

// модифицированный  в соответстви  и  с  величиной

// тонкой настройки.

_sampledInstrument

->BasePitch(440*pow(2.0,-_finetune/96.0),

3575872.0F/254.0F);

delete [] data;

}

Поскольку в файлах MOD громкость задается целыми значениями от 0 до 64, а  высота   значением  делителя,  разумно  устанавливать  громкость  и  высоту,  непосредственно   используя  эти  значения.  Предположение,  что  значение  делителя равное 254 соответствует ноте Ля 440 Гц, условно. Пока вы используете одни и те же допущения, результаты будут одинаковыми.

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

void ModNote::SetModVolume(int volume) {

if (volume > 64) volume = 64;

if (volume < 0) volume = 0;

if (_abstractNote)

_abstractNote->Volume(volume/64.0/4.0);

}

void ModNote::SetModPeriod(int period) {

if (period < 113) period=113; if (period > 856) period=856; if (_abstractNote)

_abstractNote->Pitch(440.0*254.0/period);

}

Имеет  смысл  создавать  новые  ноты,  применяя  соглашения  MOD.  Рассматриваемый  конструктор  ModNote обрабатывает  значения  периода  и  громкости,  задаваемые в стиле MOD. Соответствующий метод ModInstrument: :NewModNote реализует  это  соглашение,  но  настраивает  значение  громкости  из  данных  инструмента в соответствии с величиной, используемой по умолчанию.

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

ModNote::ModNote(AbstractNote *abstractNote, int period, int volume) {

_abstractNote = abstractNote; SetModVolume(volume); SetModPeriod(period) ; Restart();

}

ModNote::~ModNote(){

if (_abstractNote) delete _abstractNote;

}

ModNote *

ModInstrument::NewModNote(int period) {

AbstractNote *note = NewNote(440,0.5);    // Общая  нота.

// Конструктор  ModNote "сбрасывает" высоту тона  и  громкость.

return new ModNote(note,period,_volume);

}

AbstractNote *

ModInstrument::NewNote(float pitch, float volume) {

if (_sampledInstrument)

return _sampledInstrument->NewNote(pitch,volume);

else

return 0;

}

Ноты

Каждая  нота  содержит  8-битный  номер  инструмента,  12-битную  длину  периода и  12-битное  значение  эффекта.  B  файле  эти  данные  упаковываются  в  4  байта, как показано в табл. 23.3. Если вспомнить, что ранняя версия этого формата поддерживала всего 15 инструментов, то будет легче понять подобную компоновку.

Формально  мы  можем  выбирать  любое  значение  периода  для  любой  ноты. Это  позволило  бы,  изменяя  период,  использовать  различные  масштабы  или  более медленное изменение высоты ноты. Ha практике задаются только периоды,

Таблица 23.3. Компоновка нотной информации

Поле                                  Описание

Instr1                                        Старшие 4 бита номера инструмента, первый байт

Period                                       12-битное значение периода, первые 2 байта

Instr2                                        Младшие 4 бита номера инструмента, третий байт

Effect                                        12-битное значение эффекта, байты 3 и 4

Таблица 23.4. Значения периодов для нот

Октава

До

До#

Pe

Ре#

Ми

Фа

Фа#

Соль

Соль#

Ля

Ля#

Си

0

1712

1616

1525

1440

1357

1281

1209

1141

1077

1017

961

907

1             856     808        762   720       678       640     604       570        538          508      480      453

2

428

404

381

360

339

320

302

285

269

254

240

226

3

214

202

190

180

170

160

151

143

135

127

120

113

4

107

101

95

90

85

80

76

71

67

64

60

57

перечисленные в табл. 23.4, с ними корректно работают некоторые проигрыватели и редакторы файлов MOD.

Заметьте, что октавы 0 и 4 поддерживаются недостаточно широко.

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

Значение эффекта рассматривается как 4-битный код эффекта, за которым следуют либо один 8-битный параметр, либо два 4-битных. Когда речь пойдет о программном коде воспроизведения, эффекты будут рассмотрены более подробно.

Листинг 23.6. Члены класса ModNoteData

enum effectType { none=0, arpeggio, slideUp, slideDown, pitchSlide, vibrato, pitchSlidePlusVolumeSlide, vibratoPlusVolumeSlide, tremolo, setSampleOffset, volumeSlide, farJump, setVolume, smallJump, setTempo, setFilter, pitchUp, pitchDown, setGlissando, setVibrato, setFinetune, patternLoop, setTremolo, retrigger, volumeUp, volumeDown, cutNote, delayNote, delayPattern, invertLoop, effectCount

};

unsigned char _instrument;           // 8 бит для инструмента. unsigned short _period;              // 12 бит  на   период. ModNoteData::effectType _effect;     // 4(8) бит для  эффекта. unsigned char _parameter;            // 8(4) бит для параметра.

public:

void ReadNote(istream &modFile);

Вместо  использования  кода  эффекта  в  формате  MOD  может  применяться  последовательная,  постепенная  смена   высоты,   комбинированная   с  последовательным изменением периода. Наряду с этим можно поэкспериментировать с 13-тоновым  или  другими  экзотическими  ладами.  Многие  программы  воспроизведения MIDI  преобразуют  периоды  во  внутренние  нотные  коды:  такие  приложения  не признают периоды, отличные от стандартных.

Метод,  используемый  для  преобразования  периодов  в  нотные  коды,  полезен при распечатке файлов MOD.

Листинг 23.7. Преобразование периодов в имена нот

void NoteName (int period, char *name){

static const int periods [] =  // Периоды для  октавы  0.

{1712,1616,1525,1440,1357,1281,1209,1141,1077,1017,961,907};

static const char *notenames[] =

{"C-","C#","D-","D#","E-","F-","F#","G-","G#","A-","A#","B-

"};

int octave = 0;                // Смещаем   период в октаву  0.

while (period < 870) {octave++; period *=2; }

int a, b, с;                 // Двоичный поиск  в  таблице.

а = 0; b = sizeof (periods)/sizeof(prriods[0]) -1;

while ((b a)>1) {

с = (b+a) /2;

if (period <periods[c]) a=c; else b=c;

}

// Выбираем  наиболее  близкий

// период.

if (periods[a]-period > period-periods[b]) с = b;

else с = а;

// Формат   названия  ноты

// в буфере.

sprintf (name, "%2s%1d", notenames[c], octave);

}

Вы,  возможно,  заметили,  что  приведенный  ранее  список  эффектов  содержит более  16  наименований.  B  действительности  в  формате  MOD  для  задания  эффекта используется  старший полубайт. Если же он равен 14, применяется  второй полубайт.  Метод  ReadNote предусматривает  две  таблицы  отображения  для  преобразования кодов эффектов MOD во внутренние коды эффектов.

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

void ModNoteData::ReadNote(istream &modFile) {

static ModNoteData::effectType primaryEffects[] = { arpeggio, slideUp, slideDown, pitchSlide, vibrato, pitchSlidePlusVolumeSlide, vibratoPlusVolumeSlide, tremolo, none,

setSampleOffset, volumeSlide, farJump, setVolume, smallJump, none, setTempo

};

static ModNoteData::effectType secondaryEffects[] = { setFilter, pitchUp, pitchDown, setGlissando, setVibrato, setFinetune, patternLoop, setTremolo, none, retrigger, volumeUp, volumeDown, cutNote, delayNote, delayPattern, invertLoop

};

unsigned char b[4];

int effectValue;

modFile.read(reinterpret_cast<char *>(b),4);

_instrument = (b[0]&0xF0) | ((b[2]>>4) & 0x0F);

_period = ((static_cast<int>(b[0])&0x0F)<<8)

+ (static_cast<int>(b[1]) & OxFF);

effectValue = static_cast<int>(b[2]) & 0x0F;

_parameter = static_cast<int>(b[3]) & 0xFF;

// Эффект   14 обрабатывается слегка  иначе…

if (effectValue == 14) {

_effect = secondaryEffects[(_parameter>>4)&0x0F];

_parameter &= 0x0F;

} else if ((effectValue == 0) && (_parameter == 0)){

_effect = none;

} else {

_effect = primaryEffects[effectValue];

}

}

Хранение музыкального произведения

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

Листинг 23.8. Члены класса ModSong

private:

unsigned char _playList[128];

int _playListLength;

int _maxPattern;

typedef ModNoteData Beat[4]; // 4 ноты   на   такт.

typedef Beat *Pattern; // Трафарет  массив тактов.

Pattern *_patterns;

public:

void ReadPlayList(istream &modFile); void ReadPatterns(istream &modFile); ModSong();

~ModSong();

Класс  ModSong  также  контролирует  текущее  положение  внутри  записи  музыкального  произведения.  Проблема  в  том,  что  различные  эффекты  могут  изменять порядок   воспроизведения.   Например,   эффект   «задержки   трафарета»   заставляет повторно  воспроизвести  предыдущий  такт.  Это  требует  запоминания  информации о том, какой такт проигрывался последним. Другие эффекты определяют

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

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

private:

// Информация, необходимая для  воспроизведения  MOD-файла. int _thisIndex;    // Текущее положение в  плей-листе.

int _thisBeat;     // Текущий такт  в  соответствующем

// трафарете.

int _lastIndex;    // Предыдущий   индекс/такт.

int _lastBeat;

int _nextIndex;    // Следующий   индекс/такт.

int _nextBeat;

public:

bool Advance();    // Переход  на   следующий  такт; истина,

// если  достигнут  конец записи.

void Back();       // Откат  на   предыдущий  такт.

void AdvanceNextIndex(); // Переход  на   следующий  индекс

// трафарета.

void NextIndex(int i);   // Установка  индекса  следующего

// трафарета.

void NextBeat(int b);    // Установка  следующего такта.

void Stop();             // Следующий   вызов  Advance() возвратит

// истину.

ModNoteData &ThisNote(int ch) {

return _patterns[ _playList[_thisIndex] ] [_thisBeat] [ch];

}

Листинг 23.9. Инициализация переменных класса ModSong

_lastIndex = _lastBeat = 0;

_thisIndex = _thisBeat = 0;

_nextIndex = _nextBeat = 0;

Переход на следующий такт непрост. Метод Advance также возвращает при-

знак конца записи.

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

void ModSong::Stop() {

_nextIndex = _playListLength;

}

bool ModSong::Advance() {    // Возвращает  истину, если достигнут

// конец записи.

_lastBeat = _thisBeat;

_lastIndex = _thisIndex;

_thisBeat = _nextBeat;

_thisIndex = _nextIndex;

_nextBeat++;

if (_nextBeat >= 64) {   // Переходить  на   следующий  трафарет?

_nextBeat = 0;

_nextIndex++;

}

if (_thisIndex >= _playListLength) return true;

else return false;

}

void ModSong::Back() {

_nextBeat = _thisBeat;

_nextIndex = _thisIndex;

_thisBeat = _lastBeat;

_thisIndex = _lastIndex;

}

void ModSong::NextBeat(int b) {

_nextBeat = b;

}

void ModSong::NextIndex(int i) {

_nextIndex = i;

}

void ModSong::AdvanceNextIndex() {

_nextIndex++;

}

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

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

ModSong::ModSong() {

_maxPattern=0;

_patterns = 0;

}

ModSong::~ModSong(){     // Освобождаем  память,

// выделенную  под   трафареты.

if (_patterns) {

for(int p=0;p<=_maxPattern;p++)

delete [] _patterns[p];

delete [] _patterns;

}

}

Для  чтения  плей-листа  и  трафаретов  необходимо  вычислить  старший  номер трафарета.

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

void ModSong::ReadPlayList(istream &modFile) {

_playListLength = ReadIntMsb(modFile,1);

(void)ReadIntMsb(modFile,1); // Отбрасываем  лишний   байт.

_maxPattern=0;

for(int i=0;i<128;i++) {

_playList[i] = ReadIntMsb(modFile,1);

if (_playList[i] > _maxPattern)

_maxPattern = _playList[i];

}

}

void ModSong::ReadPatterns(istream &modFile) {

_patterns = new Pattern[_maxPattern+1];

for (int p=0;p<=_maxPattern;p++) {

_patterns[p] = new Beat[64];

for (int b=0;b<64;b++)

for (int n=0;n<4;n++)

_patterns[p][b][n].ReadNote(modFile);

}

}

Общая структура файла

Теперь, когда мы увидели, как хранятся все части музыкального произведе-

ния, обратимся к табл. 23.5, в которой показано их распределение в файле.

Таблица 23.5. Общая организация файла MOD

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

20                                                         Название музыкального произведения

30 на каждый инструмент                 Данные инструмента

1                                                          Длина плей-листа

1                                                           Количество трафаретов (только в файлах старых версий)

128                                                       Плей-лист

4                                                           Сигнатура

1024 для каждого трафарета            Трафареты

Затем следуют звуковые данные

Сигнатура  используется  для  определения  точного  формата  файла.  K  сожалению, не во всех вариантах MOD она появляется в одном и том же месте. B табл. 23.6 приводится  краткая  информация  по  наиболее  распространенным  вариантам,  а  также  указано,  чем  эти  варианты  отличаются  от  рассмотренных.  Если  сигнатура  не соответствует ни одному из приведенных  в таблице образцов, возможно, файл является  старым  15-инструментным  вариантом,  для  которого  определенной  сигнатуры не существует.

B  этой  главе  классом  высшего  уровня  является  класс  ModRead,  производный от класса AudioAbstract. Для хранения информации о каждом канале во время воспроизведения ModRead использует вспомогательный класс ModChannel.

Таблица 23.6. Распространенные сигнатуры и их значения

Сигнатура            Описание

M.К.                       Наиболее распространенная сигнатура. Эти файлы имеют формат,

описанный в данной главе

М!К!                       То же самое, что и предыдущий вариант, но использует более 64 паттернов

FLT4                      Идентичен М!К!

FLT8                      Каждый такт содержит 8 каналов

6CHN                     Каждый такт содержит 6 каналов

8CHN                     Каждый такт содержит 8 каналов

Листинг 23.10. Члены класса ModRead

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

{

*min = 8000;

*max = 44100;

*preferred = 11025;

}

long SamplingRate() { return AudioAbstract::SamplingRate(); }

void SamplingRate(long s) { AudioAbstract::SamplingRate(s);

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

if(_instrument[i])

_instrument[i]->SamplingRate(s);

}

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

*min = 1;

*max = *preferred = 2;

}

Для хранения содержимого файла классу ModRead необходимо несколько переменных.

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

public:

ModRead(istream &);   // Конструктор использует  открытый  поток.

~ModRead();

private:

char _name[21];

char _marker[4];

// Чтение  данных из  файла MOD.

enum {numInstruments = 32};

ModInstrument *_instrument[numInstruments]; ModSong _song;

void ReadMod(istream &modFile);

Метод ReadMod вызывается конструктором для чтения файла. Он, в свою оче-

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

программ проигрывания файлов в следующем разделе мы будем добавлять в класс

ModRead другие переменные.

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

ModRead::ModRead(istream &modFile) {

// Формат  файла: ProTracker MOD.

cerr << "File Format: ProTracker MOD\n";

ReadMod(modFile);  // "Втянули" файл.

}

ModRead::~ModRead() {

for (int i=l;i<numInstruments;i++)

if (_instrument[i]) delete _instrument[i];

if (_sampleBufferLeft) delete [] _sampleBufferLeft;

if (_sampleBufferRight) delete [] _sampleBufferRight;

}

void ModRead::ReadMod(istream &modFile) {

modFile.read(_name,20);

_name[20] = 0;

// Название. cerr << "Name: " << _name << "\n";

_instrument[0] = NULL;

for (int i=l;i<numInstruments;i++) {

_instrument[i] = new ModInstrument();

_instrument[i]->ReadHeader (modFile);

}

{                  // Многие   композиторы  помещают   комментарий

// в  область  названия  инструмента. Покажем

// эту область пользователю.

char msg[80];

// Инструменты:

cerr << "Instruments: \n";

int step = (numInstruments+2)/3; // Используем три  столбца.

for (int i=1;i<=step;i++) {

sprintf(msg,"%2d:%-22s ",i,_instrument[i]->Name());

cerr << msg;

if (i+step < numInstruments) {

sprintf(msg,"%2d:%-22s ",i+step,

_instrument[i+step]->Name());

cerr << msg;

}

if (i+step+step < numInstruments) {

sprintf(msg,"%2d:%-22s",i+step+step,

_instrument[i+step+step]->Name());

cerr << msg;

}

cerr << "\n";

}

}

_song.ReadPlayList(modFile); modFile.read(_marker,4); // Считываем  разделитель. if (memcmp(_marker,"M.K.",4)==0) {

// Protracker MOD, не   более 64 трафаретов.

} else if (memcmp(_marker,"M!K!",4)==0) {

// Protracker MOD, более 64 трафаретов.

} else {

// Другие  форматы в  программе  не   поддерживаются.

// Нераспознанная сигнатура.

cerr << "Unrecognized signature ‘"

<< _marker[0] << _marker[1]

<< _marker[2] << _marker[3]

<< " ‘ \n" ,·

exit (1);

}

_song.ReadPatterns(modFile);

for (int i1=1;i1<numInstruments;i1++)

_instrument[i1]->ReadSample(modFile);

}

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

По теме:

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