Главная страница Случайная страница КАТЕГОРИИ: АвтомобилиАстрономияБиологияГеографияДом и садДругие языкиДругоеИнформатикаИсторияКультураЛитератураЛогикаМатематикаМедицинаМеталлургияМеханикаОбразованиеОхрана трудаПедагогикаПолитикаПравоПсихологияРелигияРиторикаСоциологияСпортСтроительствоТехнологияТуризмФизикаФилософияФинансыХимияЧерчениеЭкологияЭкономикаЭлектроника |
Листинг 9.6. Полная программа воспроизведения звука.
// ВКЛЮЧАЕМЫЕ ФАЙЛЫ //////////////////////////////////////// #include < io.h> #include < stdio.h> #include < stdlib.h> #include < dos.h> #include < bios.h> #include < fcntl.h> // ГЛОБАЛЬНЫЕ ПEPEMEHHЫE ////////////////////////////// char far *driver_ptr; unsigned version; char _huge *data_ptr; unsigned ct_voice_status; // ФУНКЦИИ //////////////////////////// void Voc_Get_Version(void) { // получить версию драйвера и вывести, ее на экран _asni { mov bx, 0; функция 0 возвращает номер версии call driver_ptr; вызов драйвера mov version, ax; сохранить номер версии } // конец ассемблерной вставки printf(" \nVersion of Driver = %Х.0%Х", ((version> > 8) & 0x00ff), (version& 0x00ff)); } // конец функции //////////////////////////////////////////////////////////// int Voc_lnit_Driver(void) { // инициализация драйвера функция возвращает слово состояния int status; _asm { mov bx, 3; функция инициализации драйвера имеет номер 3 call driver_ptr; вызов драйвера mov status, ах; сохранение номера версии }// конец ассемблерной вставки // возвратить слово состояния printf(" \nDriver Initialized"); return(status); } // конец функции //////////////////////////////////////////////////////////// int Voc_Terminate_Driver(void) { // прекратить работу драйвера _asm { mov bx, 9; функция 9 прекращает работу драйвера call driver_ptr; вызов драйвера } // конец ассемблерной вставки // освобождение памяти _dos_freemem(_FP_SEG(driver_ptr)); printf(" \nDriver Terminated"); } // конец функции //////////////////////////////////////////////////////////// void Voc_Set_Port(unsigned port) { // установить адрес порта ввода/вывода Sound Blaster _asm { mov bx, l; функция 1 устанавливает адрес порта ввода/вывода mov ax, port; поместить адрес порта ввода/вывода в регистр АХ call driver_ptr; вызов драйвера } // конец ассемблерной вставки } // конец функции //////////////////////////////////////////////////////////// void Voc_Set_Speaker(unsigned on) { // включить/выключить вывод звука _asm { mov bx, 4; функция 4 включает или выключает вывод звука mov ах, on; поместить флаг включить/выключить в регистр АХ call driver_ptr; вызов драйвера } // конец ассемблерной вставки } // конец функции //////////////////////////////////////////////////////////// int Voc_Play_Sound(unsigned char far *addr, unsigned char header_lehgth) { // проигрывает загруженный в память VOC-файл unsigned segm, offm; segm = _FP_SEG(addr); offm = _FP_OFF(addr) + header_length; asm { mov bx, 6; Функция 6: воспроизведение" VOC-файла mov ax, segm mov es, ax; в регистр ES загружается сегмент mov di, offm; и регистр DI загружается смещение call driver_ptr; вызов драйвера } // конец ассемблерной вставки } // конец функции //////////////////////////////////////////////////////////// int Voc_Stop_Sound(void) { // прекращает воспроизведение звука _asm { mov bx, 8; функция 8 прекращает воспроизведение звука call driver_ptr; вызов драйвера } // конец ассемблерной вставки } // конец функции //////////////////////////////////////////////////////////// int Voc_Pause_Sound(void) { // приостанавливает воспроизведение звука _asm { mov bx, 10; функция 10 приостанавливает ; воспроизведение звука call driver_ptr; вызов драйвера } // конец ассемблерной вставки } // конец функции ///////////////////////////////////////////////// int Voc_Continue_Sound(void) { // продолжает воспроизведение приостановленного звука asm { mov bx, 11; функция 11 продолжает воспроизведение ; приостановленного звука call driver_ptr; вызов драйвера } // конец ассемблерной вставки } // конец функции ///////////////////////////////////////////////// int Voc_Break_Sound(void) { // прерывает цикл воспроизведения звука _asm { mov bx, 12; функция 12 прерывает цикл воспроизведения звука call driver_ptr; вызов драйвера } // конец ассемблерной вставки } // конец функции ////////////////////////////////////////////////////////// void Voc_Set_DMA(unsigned dma) { // устанавливает номер прерывания прямого доступа к памяти _asm { mov bx, 2; функция 2 устанавливает номер прерывания ; прямого доступа к памяти mov ax, dma; поместить в регистр АХ ; номер прерывания прямого доступа в память call driver_ptr; вызов драйвера } // конец ассемблерной вставки } // конец функции //////////////////////////////////////////////////////////// Voc Set_Status_Addr(char _far *status) { unsigned segni, offm; segm = _FP_SEG(status); offm = _FP_OFF(status); // задает переменную слова состояния asm { mov bx, 5; функция задания переменной слова состояния ; имеет номер 5 mov еs, segm; в регистр ез загружается сегмент переменной mov di, offm; в регистр di загружается смещение переменной call driver_ptr; вызов драйвера } // конец ассемблерной вставки } // конец функции /////////////////////////////////////// void Voc_Load_Driver(void) { // загрузить CT-VOICE.DRV int driver_handle; unsigned errno, segment_offset, num_para, bytes_read; // открыть файл драйвера _dos_open(" CT-VOICE.DRV", _O_RDONLY, & driver_handle); // выделить память num_para= 1 + (filelength(driver__handle))/16; _dos_allocmem(num_para, & segment); // установить указатель на область данных драйвера _FP_SEG(driver_ptr) = segment; _FP_OFF(driver_ptr) =0; // загрузить код драйвера data_ptr = driver_ptr; do { _dos_read(driver_handle, data_ptr, 0х4000, & bytes_read); data_ptr += bytes_read; } while (bytes_read==0x4000); // закрыть файл _dos_close(driver_handle); } // конец функции //////////////////////////////////////////////////////////// char far *Voc_Load_Sound(char *filename, unsigned char *header_length) { // загрузка звукового файла с диска в память //и установка указателя на его начало char far *temp_ptr; char far *data_ptr; unsigned int sum; int sound handle, t; unsigned errno, segment, offset, num_para, bytes_read; // Открыть звуковой файл _dos_open(filename, _O_RDONLY, & sound_handle); // Выделить память num_para = 1 + (filelength(sound_handle))/16; _dos_allocmem(num_para, & segment); // Установить указатель на выделенную память _FP_SEG(data_ptr) = segment; _FP_OFF(data_ptr} = 0; // Загрузить звуковые данные temp_ptr = data_ptr; do { _dog_read(sound_handle, temp_ptr, 0х4000, & bytes_read); temp_ptr += bytes_read; sum +=bytes_read; } while (bytes_read==0х4000); // проверить на всякий случай, звуковые ли это данные; // для этого проверяется присутствие слова " Creative" if ((data_ptr[0]! ='C') || (data_ptr[1]! = 'r')) { printf(" \n%s is not a voc file! ", filename); _dos_freemem(_FP_SEG(data_ptr)); return(0); } // конец звукового файла *header_length = (unsigned char)data_ptr[20]; // закрыть файл _dos_close(sound_handle); return(data_ptr); } //конец функции ///////////////////////////////////////////////////////////// void Voc_Unload_Sound(char far *sound_ptr) { // удаление звуковых данных из памяти _dos_freemem(_FP_SEG(sound_ptr)); ) // конец функции //////////////////////////////////////////////////////////// void main(void) { char far *sounds[4]; unsigned char lengths[4]; int done=0, sel; Voc_Load_Driver(); Voc_Init_Driver (); Voc_Set_Port (0х220); Voc_Set_DMA(5); Voc_Get_Version(); Voc Set_Status_Addr((char _far *) & ct_voice_status); // загрузка звуковых файлов sounds[0] = Voc_Load_Sound(" beav.voc", & lengths[0]); soundsll] = Voc_Load_Sound(" ed209.voc", & lengths[1]); sounds[2] = Voc_Load_Sound{" term.voc", & lengths[2]); sounds[3] = Voc_Load_Sound(" driver.voc" f & lengths[3]); Voc_Set_Speaker(1); // главный цикл событий;.позволяет пользователю // выбрать звуковой файл. // Заметьте, что воспроизведение текущего звука можно прервать while(! done) { printf(" \n\nSound Demo Menu"); printf(" \nl - Beavis"); printf(" \n2 - ED 209"); printf(" \n3 - Terminator"); printf(" \n4 - Exit"); printf(" \n\nSelect One? "); scant(" %d", & sel); switch (sel) { case 1: { Voc_Stop_Sound(); Voc Play_Sound(sounds[0], lengths[0]); } break; case 2: { Voc_Stop_Sound(); Voc_Play_Sound(sounds[1], lengths[1]); } break; case 3: { Voc_Stop_Sound(l; Voc_Play_Sound(sounds[2], lengths[2]); } break; case 4: { done = 1; } break; default: { printf(" \nFunction %d is not a selection.", sel); } break; } // конец оператора switch } // конец цикла while // закончить работу Voc_Play_Sound(sounds[3], lengths[3]); // ожидание окончания звуковой последовательности // слово состояния имеет значение -1 при воспроизведении звука // и 0 - в; противном случае. while(ct_voice_status()) {} Voc_Set Speaker(O); // выгрузить звуковые файлы Voc_Unload_Sound(sounds[0]); Voc_Unload_Sound(sounds[1]); Voc_Unload_Sound(sounds[2]); Voc_Unload_Sound(sounds[3]); Voc_Terminate_Driver ();; } // конец функции main Частотный синтезатор Важнейшей частью звуковой карты Sound Blaster является частотный синтезатор. Как я уже говорил в этой главе, чазтотный синтезатор создает нужный сигнал путем модулирования несущей волны. Это позволяет получать гармоники. Более того, с помощью этого метода можно аппроксимировать частотные характеристики звуков реальных музыкальных инструментов и настоящего человеческого голоса. Частотный синтезатор имеет 18 управляющих блоков, каждый из которых состоит из двух частей: § Блок модулятора; § Блок несущей. Рисунок 9.8 показывает блок типичного частотного синтезатора. Здесь А -общая амплитуда, Wc - частота несущей волны (радиан/с), Wm - частота модулятора (радиан/с). Выходной сигнал блока модулятора добавляется к сигналу блока несущей волны. В результате этого одна волна «оседлывает» другую. Математически это описывается произведением двух синусоидальных волновых функций. Выходная функция имеет следующий вид: F(t) = А * sin(Wc * t + I * sin(Wm) * t) К сожалению, у нас нет времени надолго задерживаться на разговоре об устройстве синтезатора. Нам надо писать игру! (Хотя, конечно, нам и очень нужна потрясающая музыка!) Музыка И MIDI Так же, как и в случае с частотным синтезатором, у нас нет достаточного времени, чтобы разобраться во всех тонкостях воспроизведения музыкич с помощью Sound Blaster, Мы лишь затронем этот допрос для того, чтобы у вас сложилось общее представление об этом. Я хотел бы, по крайней, мере рассказать о том, что вам, возможно, пригодится в будущем. Sound Blaster поставляется вместе с драйвером для поддержки воспроизведения MIDI-музыки. (Если вы уже забыли, MIDI - это интерфейс электромузыкальных инструментов.) Этот драйвер похож на CT-VOICE.DRV, который мы обсуждали выше, и называется SBFMDRV.EXE. Функции этого драйвера приведены в таблице 9.3. Табл. 9.3. Функции драйвера FMDRV.DRV для Sound Blaster
Это все. что я хотел сказать о музыкальном драйвере. Я могу поспорить, что мастеру в написании компьютерных игр этой информации вместе с теми программами, что мы написали для CT-VOICE.DRV, вполне достаточно. Использование звука в играх Я, конечно, не могу не рассказать о правильном и своевременном использовании звуков в компьютерных играх. Я видел людей, которые приходили в экстаз от того, что именно в нужный момент в игре звучал гармонирующий с сюжетом звук. Так как ваши возможности ограничены количеством памяти компьютера, вы должны тщательно подбирать звуки и использовать их действительно в тех местах, где они могут усилить впечатление от созданного вами мира. Все звуки должны быть немного преувеличены и быть чуть длиннее, чем в реальной жизни. Никому не интересно услышать реальный звук винтовочного выстрела, который звучит достаточно тривиально. Игрок хочет слышать даже полет фотонной торпеды! Ну и что из того, что так не бывает? Ведь мы в своем мире полубоги, следовательно, там мы можем делать все, что захотим! Давайте поговорим об архитектуре звукового интерфейса компьютерных игр. Нам требуется, чтобы разные звуки исполнялись как функция от времени. Для примера возьмем игру, которую мы собираемся сделать, и которую я назвал Warlock. В течение игры, возможно, сложится такая ситуация, которая требует воспроизведения нескольких оцифрованных звуков одновременно. К сожалению, у нас есть только один цифровой канал, и поэтому мы можем исполнять только один поток данных. К счастью, у этой проблемы есть решение. Но мы об этом поговорим немного позже. Итак, обсудим основные идеи, которых мы должны будем твердо придерживаться в любом случае. В Warlock я хочу использовать рычание, шум ветра и звуки, издаваемые оружием. Вполне может случиться так, что все эти звуки должны будут раздаваться одновременно. Мы должны создать систему приоритетов для звуков и реализовать процедуру вытеснения звуков с низким приоритетом звуками с высоким приоритетом. Один из алгоритмов, который мы можем использовать, приведен ниже. Алгоритм 9, 1. Псевдокод планировщика звуков. while(true) // оценить время поставленных в очередь звуковых эффектов. // Если звук уже неактуален, удалить его из очереди. Если нет // исполняемого звукового эффекта, начать воспроизведение // первого звука в очереди else // если запрашиваемый звуковой эффект имеет более высокий // приоритет, чем текущий звук (например, игрок выстрелил // из ружья) - прервать текущий звук и начать исполнение // эффекта с более высоким приоритетом else // если запрашиваемый звуковой эффект имеет меньший приоритет, // чем текущий звук, установить его в очередь end Алгоритм 9.1 - весьма хороший черновой пример планировщика. Он позволяет организовать очередь звуков и контролировать их исполнение. Но все-таки если во время компьютерной игры может раздаваться только один звук в каждый момент времени, это будет слишком скучно. Как же воспроизводить несколько звуков одновременно? Мы можем это сделать с помощью одного трюка. Представьте, что звук выстрела из лазерного пистолета и звук рычания страшного чудовища должны раздаться одновременно. Предположим также, что у них одинаковый приоритет. По Алгоритму 9.1 мы должны бросить жребий и сыграть их последовательно. Но почему нельзя сложить звуки вместе и получить требуемый результат? Это вполне возможно. Мы используем теорему о суперпозиции, которая говорит о том, что две любые волны в пространстве могут накладываться одна На другую и в результате порождать новую волну. В нашем случае мы должны сложить данные VOC-файлов. Есть только проблема в применении этого метода: пространство и время безграничны, но восемь битов остаются восемью битами! Во время сложения двух волн мы можем выйти за пределы восьми или шестнадцати бит. Здесь можно поступить по-разному: § Мы можем просто отсечь звук по границе восьми или шестнадцати бит; § Мы можем брать только процентное соотношение волн, что гарантирует попадание их суммы в нужные нам пределы. Алгоритм 9.2 представляет собой один из вариантов процедуры наложения звуков.
|