Деталі формату

Тепер, коли ви в загальних рисах уявляєте, як влаштований файл MOD, поговоримо про конкретні речі Потім почнемо розробку програми для читання і відтворення файлів MOD

Програвач файлів MOD набагато складніший, ніж програвач форматів WAVE або AIFF (але менш складний, хоча це спірно, ніж програвач MIDI) Крім очевидно необхідного класу ModRead, визначимо декілька допоміжних:

 ‰     ModNoteData Ñ .. Ñ € Ð ° Ð ½ Ð ¸ Ñ, ÐÐ ° Ð ½ Ð ½ Ñ <Ðμ Ð ¾ Ð'Ð ½ Ð ¾ Ð ¹ Ð ½ Ð ¾ Ñ, Ñ <(Ð ¿ÐμÑ € Ð ¸ Ð ¾ Ð ', Ð º Ð ¾ Ð' Ð ¸ Ð ½ Ñ Ñ, Ñ € уР¼ ÐμÐ ½ Ñ, Ð ° Ð ¸  Рº Ð ¾ Ð 'Ñ Ñ "Ñ" ÐμÐ º Ñ, Ð °);

 ‰     ModSongÂ Ñ .. Ñ € Ð ° Ð ½ Ð ¸ Ñ, Ð ² Ñ Ðμ Ð ¼ уР· Ñ <Ð º Ð ° Ð »ÑŒÐ ½ Ð ¾ Ðμ Ð ¿Ñ € Ð ¾ Ð ¸ Ð · Ð ² ÐμÐ'ÐμÐ ½ Ð ¸ Ðμ, Ð ² Ð º л ÑŽÑ ‡ Ð ° Ñ Ñ , Ñ € Ð ° Ñ "Ð ° Ñ € ÐμÑ, Ñ <, Ð º Ð ¾ Ñ, Ð ¾ -

рие містять безліч обєктів ModNoteData, і плей-лист Bo час

відтворення ModSong контролює стан поточного трафарету

і тактів всередині нього

 ‰     ModInstrument Р½ Ð ° Ñ Ð »ÐμÐуÐμÑ, Ñ Ñ Ð ¸ Ð · AbstractInstrument Р¸ Ð ¸ Ñ Ð ¿Ð ¾ л ьР· уÐμÑ,

обєкт

SampledInstrument для управління відтворенням

 ‰     ModNoteÂ Ñ Ñ, Ð ¾ Ð ¾ Ð ± ÑŠÐμÐ º Ñ, Ð ² Ð ¾ Ñ Ð ¿Ñ € Ð ¾ Ð ¸ Ð · Ð ² ÐμÐÐμÐ ½ Ð ¸ Ñ, Ð ½ Ð ° Ñ Ð »ÐμÐуÐμÐ ¼ Ñ <Ð ¹ Ð ¾ Ñ, AbstractNote;

 ‰     ModChannelÂ Ñ .. Ñ € Ð ° Ð ½ Ð ¸ Ñ, Ð ¸ Ð ½ Ñ Ð ¾ Ñ € Ð ¼ Ð ° Ñ † Ð ¸ ÑŽ Ð ¾ Ð º Ð ° Ð ½ Ð ° л Ðμ, Ð ² Ð º Ð »ÑŽÑ ‡ Ð ° Ñ Ð ½ Ð ¾ Ñ, у, Ð ¿Ñ € Ð ¾ Ð ¸ Ð ³ Ñ € Ñ <Ð ² Ð ° ÐμÐ ¼ ую

в поточний момент часу (ModNoteData і ModNote), і використовуються за

замовчуванням значення для різних ефектів

Лістинг 232 Програма modh

#ifndef MOD_H_INCLUDED

#define MOD_H_INCLUDED

#include &ltcstdio&gt

#include &quotaudioh&quot

#include &quotinstrumth&quot

#include &quotsampledh&quot

bool IsModFile(istream &ampfile)

/ / Спочатку проводимо попереднє визначення ряду компонентів

struct ModNoteData

class ModInstrument

class ModNote: public AbstractNote {

}

class ModInstrument: public AbstractInstrument {

}

struct ModNoteData {

}

class ModSong {

}

struct ModChannel {

}

class ModRead: public AudioAbstract {

}

#endif

Лістинг 233 Програма modcpp

#include &ltcstdio&gt

#include &ltcmath&gt

#include &ltcstdlib&gt

#include &ltcstring&gt

#include &quotmodh&quot

#include &quotaudioh&quot

#include &quotinstrumth&quot

#include &quotsampledh&quot

Інструменти

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

22 символу)

Таблиця 232 Дані про інструмент у файлі MOD

Байти Опис

22 Імя семпла (необовязково має закінчуватися нульовим символом)

2 Довжина відліку, в словах

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

1 Гучність

2 Початок області повторення, в словах

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

Всередині файлу інформація про інструмент розбита на дві частини 30-байтний заголовок зберігається на початку файлу і містить інформацію, наведену в табл 232 Самі дискретні дані (8-бітові знакові відліки) записані в кінці файлу

Деякі параметри, згадані в табл 232, потребують поясненні Зауважимо, що довжина даних і інформація про повторення зберігаються у вигляді числа 16-бітових слів, незважаючи на те що самі звукові дані записані 8-бітними знаковими байтами Деякі варіанти зберігають початок області повторення як байтовое зсув Область повторення зазвичай (але не завжди) триває до кінця зразка Інструменти, не мають області повторення, часто зберігаються зі значенням початку області повторення рівним нулю і довжиною області повторення рівний одному слову

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

Гучність завжди знаходиться в діапазоні від 0 до 64 Гучність інструменту це значення, яке використовується за умовчанням для всіх нот, що програються на даному інструменті Деякі файли таки містять значення, що виходять за рамки цього діапазону крім того, різні ефекти можуть «виштовхувати» величину гучності за встановлені межі, проте програвачі MOD відсікають такі значення

Точна настройка – це легка зміна висоти тону звучання, що застосовується до інструмента Замість повторної дискретизації звуку (з відповідною неминучою втратою якості) музичні редактори використовують дану операцію для регулювання висоти програється звуку Значення задається числом від -8 до +7 Одиниця виміру 1/8 півтони

Розібратися з іменами класів не завжди просто Клас ModNoteData компактно зберігає основну нотну інформацію, a ModNote це обєкт, безпосередньо здійснює відтворення Bo час програвання один обєкт ModNoteData створюється для кожної ноти всього музичного твору, а обєкт ModNote єдиний на кожен канал

ModInstrument не забезпечує явним чином зберігання семпла цей

процес реалізується класом SampledInstrument Проте він забезпечують-

кість альтернативні способи завдання висоти тону і гучності B файлах MOD

обчислення висоти відбувається в термінах періоду (значення дільника звукової підсистеми Amiga) Легше вмонтувати перетворення періоду у висоту в клас ModNote

Лістинг 234 Члени класу ModNote

private:

AbstractNote *_abstractNote

public:

void Restart() {

if (_abstractNote) _abstractNote-&gtRestart()

}

size_t AddSamples(AudioSample *buff, size_t length) {

if (_abstractNote) return _abstractNote-

&gtAddSamples(buff,length)

else return 0

}

/ / Єдине місце, де передбачається, що ми

/ / Використовуємо ноту, представлену семплом

void SetSampleOffset(int offset) {

// Should use dynamic_cast here if (_abstractNote)

reinterpret_cast&ltSampledNote *&gt(_abstractNote)

-&gtSetSampleOffset(offset)

}

void Pitch(float t) {

if (_abstractNote) _abstractNote-&gtPitch(t)

}

float Pitch() {

if (_abstractNote) return _abstractNote-&gtPitch()

else                return 4400

}

void EndNote(float v) {

if (_abstractNote) _abstractNote-&gtEndNote(v)

}

private:

void Volume(float) {}

float Volume() {return 10}

public:

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

~ModNote()

Лістинг 235 Члени класу 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 &ampmodFile) void ReadSample(istream &ampmodFile) ModNote *NewModNote(int period) void SamplingRate(long s) {

AbstractInstrument::SamplingRate(s)

if(_sampledInstrument)

_sampledInstrument-&gtSamplingRate(s)

}

long SamplingRate() {

return AbstractInstrument::SamplingRate()

}

private:

AbstractNote *NewNote(float pitch, float volume)

Так як дані інструменту зберігаються розділеними на дві частини, то нам знадобляться два методи для вилучення цих частин Метод ReadHead витягує основні параметри, ReadSample виробляє зчитування даних Останній також здійснює перевірку деяких особливих випадків, таких як коротка область повторення нульових байтів

Лістинг 233 Програма modcpp (продовження)

void ModInstrument::ReadHeader(istream &ampmodFile){

modFileread(_name,22)

_name[22]= 0

_length = ReadIntMsb(modFile,2) * 2

_finetune = ReadIntMsb(modFile,1)

if (_finetune &gt 7) _finetune -= 16

_volume = ReadIntMsb(modFile,1)

if (_volume &gt 64) _volume = 64

_repeatStart = ReadIntMsb(modFile,2) * 2

_repeatLength = ReadIntMsb(modFile,2) * 2

}

void ModInstrument::ReadSample(istream &ampmodFile) (

if (_length &lt 1) return AudioSample *data

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

= reinterpret_cast&ltAudioByte *&gt(data)

modFileread(reinterpret_cast&ltchar *&gt(byteBuff),_length)

for(long s = _length-1 s&gt=0 s–) {

data[s] = static_cast&ltsigned char&gt(byteBuff[s]) * (1&lt&lt (8*sizeof(AudioSample)-8))

}

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

int repeatTest=0

for(int i=_repeatStarti&lt_repeatLengthi++)

repeatTest += data[i]

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

_repeatLength = 0

_repeatStart = _length

}

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

/ / На початку означає, що у файлі повторень немає

if((_repeatLength&lt=2) &amp&amp (_repeatStart == 0)) {

_repeatLength = 0

_repeatStart = _length

}

_sampledInstrument = new SampledInstrument(data,

_length,_repeatStart,_repeatStart+_repeatLength)

/ / Параметри Pitch () визначаються виходячи з угоди,

/ / Що для ноти Ля 440 Гц використовується дільник 254,

/ / Модифіковані відповідно і з величиною

/ / Тонкої настройки

_sampledInstrument

-&gtBasePitch(440*pow(20,-_finetune/960),

35758720F/2540F)

delete [] data

}

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

Лістинг 233 Програма modcpp (продовження)

void ModNote::SetModVolume(int volume) {

if (volume &gt 64) volume = 64

if (volume &lt 0) volume = 0

if (_abstractNote)

_abstractNote-&gtVolume(volume/640/40)

}

void ModNote::SetModPeriod(int period) {

if (period &lt 113) period=113 if (period &gt 856) period=856 if (_abstractNote)

_abstractNote-&gtPitch(4400*2540/period)

}

Має сенс створювати нові ноти, застосовуючи угоди MOD Розглянутий конструктор ModNote обробляє значення періоду і гучності, що задаються в стилі MOD Відповідний метод ModInstrument: : NewModNote реалізує цю угоду, але налаштовує значення гучності з даних інструменту відповідно до величини, яка використовується за замовчуванням

Лістинг 233 Програма modcpp (продовження)

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,05) / / Загальна нота

/ / Конструктор ModNote скидає висоту тону і гучність

return new ModNote(note,period,_volume)

}

AbstractNote *

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

if (_sampledInstrument)

return _sampledInstrument-&gtNewNote(pitch,volume)

else

return 0

}

Ноти

Кожна нота містить 8-бітний номер інструменту, 12-бітну довжину періоду і 12-бітове значення ефекту B файлі ці дані упаковуються в 4 байта, як показано в табл 233 Якщо згадати, що рання версія цього формату підтримувала всього 15 інструментів, то буде легше зрозуміти подібну компоновку

Формально ми можемо вибирати будь-яке значення періоду для будь ноти Це дозволило б, змінюючи період, використовувати різні масштаби або більш повільне зміна висоти ноти Ha практиці задаються тільки періоди,

Таблиця 233 Компонування нотної інформації

Поле Опис

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

Period 12-бітове значення періоду, перші 2 байта

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

Effect 12-бітове значення ефекту, байти 3 і 4

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

Октава

До

До #

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

перераховані в табл 234, з ними коректно працюють деякі програвачі та редактори файлів MOD

Зауважте, що октави 0 і 4 підтримуються недостатньо широко

Взаємозвязок між періодами і нотами припускає, що семпли отримані певним способом Оціфровивая звучання інструментів відповідним чином, ви можете отримати значно більше, ніж приводиться тут триоктавні діапазон

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

Лістинг 236 Члени класу 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 &ampmodFile)

Замість використання коду ефекту у форматі MOD може застосовуватися послідовна, поступова зміна висоти, комбінована з послідовною зміною періоду Поряд з цим можна поекспериментувати з 13-тоновим або іншими екзотичними ладами Багато програм відтворення MIDI перетворять періоди у внутрішні нотні коди: такі програми не визнають періоди, відмінні від стандартних

Метод, використовуваний для перетворення періодів у нотні коди, корисний при роздруківці файлів MOD

Лістинг 237 Перетворення періодів в імена нот

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[] =

{&quotC-&quot,&quotC#&quot,&quotD-&quot,&quotD#&quot,&quotE-&quot,&quotF-&quot,&quotF#&quot,&quotG-&quot,&quotG#&quot,&quotA-&quot,&quotA#&quot,&quotB-

&quot}

int octave = 0 / / Зміщуємо період в октаву 0

while (period &lt 870) {octave++ period *=2 }

int a, b, с / / Двійковий пошук в таблиці

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

while ((b a)&gt1) {

с = (b + a) / 2

if (period &ltperiods[c]) a=c else b=c

}

/ / Вибираємо найбільш близький

/ / Період

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

else с = а

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

/ / В буфері

sprintf (name, &quot%2s%1d&quot, notenames[c], octave)

}

Ви, можливо, помітили, що наведений раніше список ефектів містить більше 16 найменувань B дійсності у форматі MOD для завдання ефекту використовується старший напівбайт Якщо ж він дорівнює 14, застосовується другий напівбайт Метод ReadNote передбачає дві таблиці відображення для перетворення кодів ефектів MOD у внутрішні коди ефектів

Лістинг 233 Програма modcpp (продовження)

void ModNoteData::ReadNote(istream &ampmodFile) {

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

modFileread(reinterpret_cast&ltchar *&gt(b),4)

_instrument = (b[0]&amp0xF0) | ((b[2]&gt&gt4) &amp 0x0F)

_period = ((static_cast&ltint&gt(b[0])&amp0x0F)&lt&lt8)

+ (static_cast&ltint&gt(b[1]) &amp OxFF)

effectValue = static_cast&ltint&gt(b[2]) &amp 0x0F

_parameter = static_cast&ltint&gt(b[3]) &amp 0xFF

/ / Ефект 14 обробляється злегка інакше ..

if (effectValue == 14) {

_effect = secondaryEffects[(_parameter&gt&gt4)&amp0x0F]

_parameter &amp= 0x0F

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

_effect = none

} else {

_effect = primaryEffects[effectValue]

}

}

Зберігання музичного твору

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

Лістинг 238 Члени класу ModSong

private:

unsigned char _playList[128]

int _playListLength

int _maxPattern

typedef ModNoteData Beat [4] / / 4 ноти на такт

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

Pattern *_patterns

public:

void ReadPlayList(istream &ampmodFile) void ReadPatterns(istream &ampmodFile) ModSong()

~ModSong()

Клас ModSong також контролює поточне положення всередині запису музичного твору Проблема в тому, що різні ефекти можуть змінювати порядок відтворення Наприклад, ефект «затримки трафарету» змушує повторно відтворити попередній такт Це вимагає запамятовування інформації про те, який такт програвався останнім Інші ефекти визначають

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

Лістинг 238 Члени класу 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 &ampThisNote(int ch) {

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

}

Лістинг 239 Ініціалізація змінних класу ModSong

_lastIndex = _lastBeat = 0

_thisIndex = _thisBeat = 0

_nextIndex = _nextBeat = 0

Перехід на наступний такт непростий Метод Advance також повертає при-

знак кінця запису

Лістинг 233 Програма modcpp (продовження)

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 &gt= _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++

}

Для економії памяті, використовуваної при читанні музичних творів з невеликим числом трафаретів, виділимо память під окремі трафарети по мірі їх зчитування Деструктори необхідно звільнити память тільки під призначені трафарети

Лістинг 233 Програма modcpp (продовження)

ModSong::ModSong() {

_maxPattern=0

_patterns = 0

}

ModSong :: ~ ModSong () {/ / Звільняємо память,

/ / Виділену під трафарети

if (_patterns) {

for(int p=0p&lt=_maxPatternp++)

delete [] _patterns[p]

delete [] _patterns

}

}

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

Лістинг 233 Програма modcpp (продовження)

void ModSong::ReadPlayList(istream &ampmodFile) {

_playListLength = ReadIntMsb(modFile,1)

(Void) ReadIntMsb (modFile, 1) / / Відкидаємо зайвий байт

_maxPattern=0

for(int i=0i&lt128i++) {

_playList[i] = ReadIntMsb(modFile,1)

if (_playList[i] &gt _maxPattern)

_maxPattern = _playList[i]

}

}

void ModSong::ReadPatterns(istream &ampmodFile) {

_patterns = new Pattern[_maxPattern+1]

for (int p=0p&lt=_maxPatternp++) {

_patterns[p] = new Beat[64]

for (int b=0b&lt64b++)

for (int n=0n&lt4n++)

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

}

}

Загальна структура файлу

Тепер, коли ми побачили, як зберігаються всі частини музичного виробленою

ня, звернемося до табл 235, в якій показано їх розподіл у файлі

Таблиця 235 Загальна організація файлу MOD

Довжина Опис

20 Назва музичного твору

30 на кожен інструмент Дані інструменту

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

1 Кількість трафаретів (тільки у файлах старих версій)

128 Плей-лист

4 Сигнатура

1024 для кожного трафарету Трафарети

Потім слідують звукові дані

Сигнатура використовується для визначення точного формату файлу K жаль, не у всіх варіантах MOD вона зявляється в одному і тому ж місці B табл 236 наводиться коротка інформація по найбільш поширеним варіантам, а також зазначено, чим ці варіанти відрізняються від розглянутих Якщо сигнатура не відповідає жодному з наведених у таблиці зразків, можливо, файл є старим 15-інструментним варіантом, для якого певної сигнатури не існує

B цій главі класом вищого рівня є клас ModRead, похідний від класу AudioAbstract Для зберігання інформації про кожному каналі під час відтворення ModRead використовує допоміжний клас ModChannel

Таблиця 236 Поширені сигнатури і їх значення

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

MК Найбільш поширена сигнатура Ці файли мають формат,

описаний у цій главі

М К Те ж саме, що і попередній варіант, але використовує більш 64 патернів

FLT4 Ідентичний М К

FLT8 Кожен такт містить 8 каналів

6CHN Кожен такт містить 6 каналів

8CHN Кожен такт містить 8 каналів

Лістинг 2310 Члени класу 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 = 0i&ltnumInstruments i++)

if(_instrument[i])

_instrument[i]-&gtSamplingRate(s)

}

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

*min = 1

*max = *preferred = 2

}

Для зберігання вмісту файлу класу ModRead необхідно кілька змінних

Лістинг 2310 Члени класу ModRead (продовження)

public:

ModRead (istream &) / / Конструктор використовує відкритий потік

~ModRead()

private:

char _name[21]

char _marker[4]

/ / Читання даних з файлу MOD

enum {numInstruments = 32}

ModInstrument *_instrument[numInstruments] ModSong _song

void ReadMod(istream &ampmodFile)

Метод ReadMod викликається конструктором для читання файлу Він, в свою оче-

редь, створює інші обєкти і просить їх рахуватися з файлу Принаймні розробки

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

ModRead інші змінні

Лістинг 233 Програма modcpp (продовження)

ModRead::ModRead(istream &ampmodFile) {

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

cerr &lt&lt &quotFile Format: ProTracker MOD\n"

ReadMod (modFile) / / Втягнули файл

}

ModRead::~ModRead() {

for (int i=li&ltnumInstrumentsi++)

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

if (_sampleBufferLeft) delete [] _sampleBufferLeft

if (_sampleBufferRight) delete [] _sampleBufferRight

}

void ModRead::ReadMod(istream &ampmodFile) {

modFileread(_name,20)

_name[20] = 0

/ / Назва cerr << "Name:" << _name << "\ N";

_instrument[0] = NULL

for (int i=li&ltnumInstrumentsi++) {

_instrument[i] = new ModInstrument()

_instrument[i]-&gtReadHeader (modFile)

}

{/ / Багато композитори поміщають коментар

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

/ / Цю область користувачеві

char msg[80]

/ / Інструменти:

cerr &lt&lt &quotInstruments: \n"

int step = (numInstruments +2) / 3 / / Використовуємо три стовпці

for (int i=1i&lt=stepi++) {

sprintf(msg,&quot%2d:%-22s &quot,i,_instrument[i]-&gtName())

cerr &lt&lt msg

if (i+step &lt numInstruments) {

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

_instrument[i+step]-&gtName())

cerr &lt&lt msg

}

if (i+step+step &lt numInstruments) {

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

_instrument[i+step+step]-&gtName())

cerr &lt&lt msg

}

cerr &lt&lt &quot\n"

}

}

_songReadPlayList (modFile) modFileread (_marker, 4) / / Прочитуємо роздільник if (memcmp (_marker, MK”, 4) == 0) {

/ / Protracker MOD, не більше 64 трафаретів

} else if (memcmp(_marker,&quotMK&quot,4)==0) {

/ / Protracker MOD, більше 64 трафаретів

} else {

/ / Інші формати в програмі не підтримуються

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

cerr &lt&lt &quotUnrecognized signature &quot

&lt&lt _marker[0] &lt&lt _marker[1]

&lt&lt _marker[2] &lt&lt _marker[3]

&lt&lt &quot \n&quot ,·

exit (1)

}

_songReadPatterns(modFile)

for (int i1=1i1&ltnumInstrumentsi1++)

_instrument[i1]-&gtReadSample(modFile)

}

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

*

*