Введение в Simple DirectMedia Layer

Что это такое?


SDL - бесплатный кроссплатформенный мультимедийный программный интерфейс приложения

- http://www.libsdl.org/

Используется для создания игр, библиотек для игр, демонстрационных программ, эмуляторов, MPEG плэйеров и других программ.

[Назад] Содержание [Дальше]


Что она может делать?

Видео
  • Установка видеорежима с любой глубиной цвета (от 8 бит и выше) с необязательным преобразованием, если режим не поддерживается аппаратно.

  • Прямая запись в линейный графический видеобуфер.

  • Создание поверхностей с атрибутами прозрачности или альфа-сопряжения (alpha blending).

  • Копирование поверхностей с автоматическим преобразованием в целевой формат, используя оптимизированные процедуры копирования и, по возможности, аппаратное ускорение. На платформе x86 имеется MMX оптимизированные процедуры копирования.

  • Используется аппаратное ускорение операций копирования и заполнения, если это поддерживается оборудованием.

  • Совет:

    Вы можете установить строку заголовка вашего приложения и иконку, используя функции SDL_WM_SetCaption() и SDL_WM_SetIcon() соответственно.
    События
  • Предусмотрены события для:

  • Изменения вида приложения

  • Ввода с клавиатуры

  • Ввода мыши

  • Выхода по требованию пользователя

  • Каждое событие может быть разрешено или запрещено функцией SDL_EventState().

  • События передаются через указанную пользователем фильтрующую функцию перед отправлением их во внутреннюю очередь событий.

  • Очередь событий надежна при использовании потоков (thread-safe).

  • Совет:

    Используйте SDL_PeepEvents() для поиска событий конкретного типа в очереди событий.
    Звук
  • Воспроизведение 8-ми и 16-ти битных звуков, моно или стерео, с необязательным преобразованием, если формат не поддерживается оборудованием.

  • Звук запускается независимо в отдельном потоке, используя для заполнения механизм пользовательской функции обратного вызова (callback).

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

  • Совет:

    Используйте функции SDL_LockAudio()
    и SDL_UnlockAudio()  для синхронизации доступа к данным, разделяемым между звуковой функцией обратного вызова и остальной частью программы.
    CD-ROM аудио
  • Полный API для управления CD аудио

  • Совет:

    Если вы передадите NULL в качестве дескриптора CD-ROM в любую функцию CD-ROM API, это будет действовать как последний открытый CD-ROM.
    Потоки
  • Простой API для создания потоков

  • Простые двоичные семафоры для синхронизации

  • Совет:

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

  • Ожидание указанного числа миллисекунд

  • Установка одиночных периодических таймеров с точностью 10 мс

  • Совет:

    Вы можете спокойно заменить Win32 GetTickCount() на SDL_GetTicks().
    Независимость от порядка байтов в слове
  • Определение порядка байтов в текущей системе

  • Процедуры для быстрого обмена значений

  • Чтение и запись данных с указанным порядком байтов

  • Совет:

    При чтении ваших файлов данных вам может потребоваться перестановка байтов в 16-битной графике.
    <
    [Назад] Содержание [Дальше]


    Инициализация библиотеки

    Используйте SDL_Init() для динамической загрузки и инициализации библиотеки. Эта функция принимает набор флагов, соответствующих частям, которые Вы хотите активизировать:
    SDL_INIT_AUDIO
    SDL_INIT_VIDEO
    SDL_INIT_CDROM
    SDL_INIT_TIMER
    Используйте SDL_Quit() для выгрузки библиотеки и освобождения ресурсов по окончанию работы. Совет:

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

    Пример:
    #include #include "SDL.h" main(int argc, char *argv[]){    if ( SDL_Init(SDL_INIT_AUDIO|SDL_INIT_VIDEO) < 0 ) {        fprintf(stderr, "Не могу инициализировать SDL: %s\n", SDL_GetError());        exit(1);    }    atexit(SDL_Quit);     ...}
    [Назад] Содержание [Дальше]


    Использование CD-ROM audio

  • Открытие CD-ROM устройства для последующего использования

  • Во можете узнать количество CD-Rom устройств в системе вызвав функцию SDL_CDNumDrives() и затем открыть один из них, вызвав SDL_CDOpen().
    Основной (по умолчанию) CD-ROM всегда 0. Заметьте, что CD-ROM устройство может быть открыто, даже если в дисковод не вставлен диск.
    Для определения текущего состояния устройства вы можете воспользоваться функцией SDL_CDStatus(). По завершении использования CD-ROM устройства, закройте его функцией SDL_CDClose(). Совет:

    Вы можете определить системное имя CD-ROM дисковода используя SDL_CDName() функцию.

    Пример:
    { SDL_CD *cdrom; if ( SDL_CDNumDrives() > 0 ) { cdrom = SDL_CDOpen(0); if ( cdrom == NULL ) { fprintf(stderr, "Не могу открыть CD-ROM по умолчанию: %s\n" SDL_GetError()); return; } ... SDL_CDClose(cdrom); }}
  • Воспроизведение CD-ROM дисков

  • CD-ROM диски используют время в MSF формате (mins/secs/frames = минуты/секунды/кадры) или непосредственно в кадрах. Кард - это стандартная единица времени для CD, равная 1/75 секунды. SDL использует кадры вместо MFS формата когда определяет длину трека и текущее положение, но вы всегда можете конвертировать один формат в другой с помощью макросов FRAMES_TO_MSF() и MSF_TO_FRAMES().
    SDL не обновляет автоматически информацию с структуре SDL_CD пока вы не вызовете SDL_CDStatus(), таким образом вы должны вызывать SDL_CDStatus каждый раз, когда вам надо узнать, что за диск вставлен в дисковод и какие треки на нем доступны. Заметьте, что первый трек имеет индекс 0.
    SDL имеет две функции для воспроизведения CD-ROM. Вы можете как играть определенный трек, используя функцию SDL_CDPlayTracks(), так и устанавливать смещение от начала всего диска, используя SDL_CDPlay().
    SDL не предусматривает автоматическое оповещение при вставке CD или при завершении воспроизведения. Для отслеживания этих ситуаций вы должны периодически производить опрос состояния устройства с помощью SDL_CDStatus(). Совет:

    Чтобы понять, какие треки - аудио треки, а какие - треки данных, вы можете прочитать cdrom->tracks[track].type, и сравнить с SDL_AUDIO_TRACK и SDL_DATA_TRACK.

    Пример:
    void PlayTrack(SDL_CD *cdrom, int track){ if ( CD_INDRIVE(SDL_CDStatus(cdrom)) ) { SDL_CDPlayTracks(cdrom, track, 0, track+1, 0); } while ( SDL_CDStatus(cdrom) == CD_PLAYING ) { SDL_Delay(1000); }}
    [Назад] Содержание[Дальше]

    Использование таймеров

  • Получение текущего времени в миллисекундах

  • SDL_GetTicks() говорит, сколько миллисекунд прошло с произвольно выбранного момента. Совет:

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

    Пример:
    #define TICK_INTERVAL 30 Uint32 TimeLeft(void){ static Uint32 next_time = 0; Uint32 now; now = SDL_GetTicks(); if ( next_time <= now ) { next_time = now+TICK_INTERVAL; return(0); } return(next_time-now);}
  • Задержка

  • Функция SDL_Delay() позволяет установить задержку на какое-то количество миллисекунд.
    Поскольку ОС, поддерживающие SDL, в большинстве своем многозадачные, вызов это функции не дает гарантии, что программа задержится ровно на данное время! Чаще эта функция просто вызывается для небольшой задержки. Совет:

    Большинство ОС имеют планировщик временных участков с интервалом около 10 миллисекунд. Вы можете использовать SDL_Delay(1) как способ оставить CPU текущий временной участок, позволив выполниться остальным потокам. Так чаще всего делают, когда необходимо выпонять свой поток в быстром непрерывном цикле, но также необходимо, чтобы и другие потоки (например, аудио) тоже выполнялись.

    Пример:
    { while ( game_running ) { UpdateGameState(); SDL_Delay(TimeLeft()); }}
    [Назад] Содержание [Дальше]

    На каких платформах выполняется?

    Linux
  • Используется X11 в качестве дисплея, беря преимущества XFree86 DGA расширений и новое MTRR ускорение для полноэкранного режима.

  • Используется OSS API  для работы со звуком.

  • Потоки обеспечиваются использованием или системным вызовом clone()  и SysV IPC, или потоками библиотеки glibc-2.1.

  • Совет:

    Вы можете получить часть скрытого интерфейса драйвера SDL через функцию SDL_GetWMInfo(). Это позволит вам делать вещи наподобие удаления оформления окна и задавать иконку вашего приложения.
    Win32
  • Две версии, одна надежная для всех систем, основанна на Win32 API, а другая высокопроизводительная, основанна на DirectX API.

  • Надежная версия использует GDI для отображения графики. Высокопроизводительная версия использует DirectDraw для работы с видео, по возможности используя преимущества аппаратного ускорения.

  • Надежная версия использует waveOut API для воспроизведения звука. Высокопроизводительная версия использует DirectSound для воспроизведения звука.

  • Совет:

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

  • BSoundPlayer API используется для звука.

  • Совет:

    Linux и BeOS поддерживают флаг SDL_INIT_EVENTTHREAD, который при передачи в SDL_Init() запрашивает запуск обработки очереди сообщений в отдельном потоке. Это полезно для окраски курсора, реагируя, таким образом, когда приложение занято.
    MacOS, MacOS X
  • Carbon и DrawSprockets используется для графики.

  • SoundManager API используется для звука.

  • Преимущественно родная поддержка потоков в MacOS X

  • Неофициально перенесены, в процессе переноса
  • Solaris, IRIX, FreeBSD, QNX, OSF/True64


  • [Назад] Содержание [Далее]

    Независимость от порядка байтов

  • Определение порядка байтов в текущей системе

  • Препроцессор C заменяет SDL_BYTEORDER на SDL_LIL_ENDIAN (младше-конечные) или SDL_BIG_ENDIAN (старше-конечные), в зависимости от порядка байтов в текущей системе.
    Младше-конечные системы это те, которые пишут данные на диск размещая так:
    [младшие байты] [старшие байты]

    Старше-конечные системы пишут данные на диск так:
    [старшие байты] [младшие байты] Совет:

    В системах x86 младший байт-первый (little-endian), PPC наоборот (big-endian).

    Example:
    #include "SDL_endian.h" #if SDL_BYTEORDER == SDL_LIL_ENDIAN#define SWAP16(X)    (X)#define SWAP32(X)    (X)#else#define SWAP16(X)    SDL_Swap16(X)#define SWAP32(X)    SDL_Swap32(X)#endif
  • Обмен данных между системами с различным порядком байтов

  • SDL предоставляет совокупность быстродействующих макросов в SDL_endian.h, SDL_Swap16() и SDL_Swap32(), которые производят обмен данными с указаным вами порядком байтов. Также определены макросы для обмена данными со специфичным для данной системы порядком байтов. Совет:

    Если вам нужно знать порядок байтов в текущей системе, но не нужны все перестановочные функции, подключите SDL_byteorder.h вместо SDL_endian.h

    Пример:
    #include "SDL_endian.h" void ReadScanline16(FILE *file, Uint16 *scanline, int length){    fread(scanline, length, sizeof(Uint16), file);    if ( SDL_BYTEORDER == SDL_BIG_ENDIAN ) {        int i;        for ( i=length-1; i >= 0; --i )            scanline[i] = SDL_SwapLE16(scanline[i]);    }}
    [Назад] Содержание


    О разработчике:

    Sam Lantinga, Инженер по программному обеспечению в Blizzard Entertainment.
    Фоновая деятельность:
  • Главный программист в Loki Software, Inc. с января 1999 по август 2001.

  • Автор Simple DirectMedia Layer (SDL)

  • Перенос Maelstrom с Macintosh в Linux

  • Работа по переносу Macintosh emulator Executor в Win32

  • Перенос утилит от DOOM из DOS (DEU, DHE, и др.)

  • Другие проекты: http://www.devolution.com/~slouken/

  • Связь:
  • slouken@libsdl.org



  • Перевод на русский язык:


    Скиданов Александр

    mailto:shd@bk.ru

    http://morgeyz.narod.ru/
    Сашнов Александр

    mailto:sashnov@ngs.ru

    http://sashnov.chat.ru/

    [Назад] Содержание ] [Далее]


    Потоки

  • Создание простого потока

  • Создание потока делается через вызов функции SDL_CreateThread(). После успешного возврата из функции ваша функция теперь запущена одновременно с основным приложением в своем контексте задачи (стек, регистры и прочее) и может получать доступ к памяти и открытым файлам используемыми в основном приложении. Совет:

    Второй аргумент для SDL_CreateThread() передается как параметр для порожденного потока. Вы можете использовать его для передачи значений в стек или только как указатель на данные, которые будут использоваться в потоке.

    Пример:
    #include "SDL_thread.h" int global_data = 0; int thread_func(void *unused){ int last_value = 0; while ( global_data != -1 ) { if ( global_data != last_value ) { printf("Data value changed to %d\n", global_data); last_value = global_data; } SDL_Delay(100); } printf("Thread quitting\n"); return(0);} { SDL_Thread *thread; int i; thread = SDL_CreateThread(thread_func, NULL); if ( thread == NULL ) { fprintf(stderr, "Unable to create thread: %s\n", SDL_GetError()); return; } for ( i=0; i<5; ++i ) { printf("Changing value to %d\n", i); global_data = i; SDL_Delay(1000); } printf("Signaling thread to quit\n"); global_data = -1; SDL_WaitThread(thread, NULL);} <
  • Синхронизация доступа к ресурсу


  • Вы можете предотвращать доступ к ресурсу более чем из одного потока с помощью создания семафора и получать доступ к ресурсу, окружая доступ вызовом блокировки (SDL_mutexP()) и разблокировки (SDL_mutexV()).
    Совет:

    Любые данные, которые могут быть доступны более чем одному потоку одновременно, должны быть защищены семафорами.

    Пример:

    #include "SDL_thread.h"#include "SDL_mutex.h" int potty = 0;int gotta_go; int thread_func(void *data){ SDL_mutex *lock = (SDL_mutex *)data; int times_went; times_went = 0; while ( gotta_go ) { SDL_mutexP(lock); /* Lock the potty */ ++potty; printf("Thread %d using the potty\n", SDL_ThreadID()); if ( potty > 1 ) { printf("Uh oh, somebody else is using the potty!\n"); } --potty; SDL_mutexV(lock); ++times_went; } printf("Yep\n"); return(times_went);} { const int progeny = 5; SDL_Thread *kids[progeny]; SDL_mutex *lock; int i, lots; /* Create the synchronization lock */ lock = SDL_CreateMutex(); gotta_go = 1; for ( i=0; i
    <


    [Назад] Содержание [Дальше]


    Для ожидания события воспользуйтесь функцией


  • Ожидание событий

  • Для ожидания события воспользуйтесь функцией SDL_WaitEvent().
    Совет:

    SDL имеет международную поддержку клавиатуры, трансляцию событий клавиатуры и помещение эквивалента UNICODE в event.key.keysym.unicode. Так как это требует дополнительной обработки, это должно быть разрешено функцией SDL_EnableUNICODE().


    Пример:
    {    SDL_Event event;     SDL_WaitEvent(&event);     switch (event.type) {        case SDL_KEYDOWN:            printf("The %s key was pressed!\n",                   SDL_GetKeyName(event.key.keysym.sym));            break;        case SDL_QUIT:            exit(0);    }}


  • Опрос событий

  • Для опроса событий используйте SDL_PollEvent().
    Совет:

    Вы можете считывать события из очереди без их удаления оттуда, используя параметр SDL_PEEKEVENT в функции SDL_PeepEvents().


    Пример:
    {    SDL_Event event;     while ( SDL_PollEvent(&event) ) {        switch (event.type) {            case SDL_MOUSEMOTION:                printf("Mouse moved by %d,%d to (%d,%d)\n",                        event.motion.xrel, event.motion.yrel,                       event.motion.x, event.motion.y);                break;            case SDL_MOUSEBUTTONDOWN:                printf("Mouse button %d pressed at (%d,%d)\n",                       event.button.button, event.button.x, event.button.y);                break;            case SDL_QUIT:                exit(0);        }    }}


  • Опрос состояния событий

  • В дополнение к обработке событий непосредственно, каждый тип события имеет функцию, которая позволяет проверить состояние приложения. Если вы используете только эту функцию, вы должны игнорировать все события от функции SDL_EventState() и периодически вызывать SDL_PumpEvents() для обработки приложением событий.
    Совет:

    Вы можете скрывать и показывать системный курсор мыши используя SDL_ShowCursor().


    Пример:
    {    SDL_EventState(SDL_MOUSEMOTION, SDL_IGNORE);} void CheckMouseHover(void){    int mouse_x, mouse_y;     SDL_PumpEvents();     SDL_GetMouseState(&mouse_x, &mouse_y);    if ( (mouse_x < 32) && (mouse_y < 32) ) {        printf("Mouse in upper left hand corner!\n");    }}
    <

    Видео

    Выбор и установка разрешения и глубины цвета  (простой способ)
    Просто выберите свое любимое разрешение и глубину цвета и установите его!
    Совет #1:

    Вы можете узнать оптимальную глубину, поддерживаемую аппаратурой, используя функцию SDL_GetVideoInfo().
    Совет #2:

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

    Пример:
    { SDL_Surface *screen;     screen = SDL_SetVideoMode(640, 480, 16, SDL_SWSURFACE);    if ( screen == NULL ) {// Если установить разрешение не удалось        fprintf(stderr, "Невозможно установить разрешение 640x480: %s\n", SDL_GetError());        exit(1);    }}
  • Рисование точек (pixels) на экране

  • Рисование точек сводится к прямой записи в видеобуфер и вызову функции обновления содержимого экрана.

      Совет:

    Если вам предстоит рисовать много объектов, лучше заблокировать (lock) экран единожды перед рисованием, вывести все объекты на экран, сохраняя список областей, требующих обновления, и разблокировать (unlock) экран вновь перед обновлением экрана.

    Пример:
    Рисование точки на экране необходимым цветом
    void DrawPixel(SDL_Surface *screen, Uint8 R, Uint8 G, Uint8 B){    Uint32 color = SDL_MapRGB(screen->format, R, G, B);     if ( SDL_MUSTLOCK(screen) ) {        if ( SDL_LockSurface(screen) < 0 ) {            return;        }    }    // Смотрим, сколькими байтами кодируется каждый пиксель (bytes per pixel, bpp)    switch (screen->format->BytesPerPixel) {        case 1: { /* Если 8-bpp */            Uint8 *bufp;             bufp = (Uint8 *)screen->pixels + y*screen->pitch + x;            *bufp = color;        }        break;         case 2: { /* Если 15-bpp или 16-bpp */            Uint16 *bufp;             bufp = (Uint16 *)screen->pixels + y*screen->pitch/2 + x;            *bufp = color;        }        break;         case 3: { /* Медленный 24-bpp режим, обычно не используется */            Uint8 *bufp;             bufp = (Uint8 *)screen->pixels + y*screen->pitch + x;            *(bufp+screen->format->Rshift/8) = R;            *(bufp+screen->format->Gshift/8) = G;            *(bufp+screen->format->Bshift/8) = B;        }        break;         case 4: { /* Наверное, 32-bpp */            Uint32 *bufp;             bufp = (Uint32 *)screen->pixels + y*screen->pitch/4 + x;            *bufp = color;        }        break;    }    if ( SDL_MUSTLOCK(screen) ) {        SDL_UnlockSurface(screen);    }    SDL_UpdateRect(screen, x, y, 1, 1);} <
  • Загрузка и отображение рисунков


  • Для вашего удобства, SDL предоставляет единственную функцию для загрузки изображений, SDL_LoadBMP().  Библиотека для загрузки изображений может быть найдена в архиве с демонстрациями SDL (SDL demos archive).

    Вы можете отобразить загруженную картинку, используя SDL_BlitSurface() для копирования ее в видеобуфер. SDL_BlitSurface() автоматически усекает копируемую область, которая должна быть передана SDL_UpdateRects() для обновления изменившейся части экрана.

    Совет #1:

    Если вам необходимо показывать некоторую картинку много раз, вы можете ускорить вывод, конвертировав ее в формат дисплея. Для этого используется функция SDL_DisplayFormat()

    Совет #2:

    Большинство изображений спрайтов имеют прозрачный фон. Вы можете разрешит копирование с прозрачностью (colorkey blit) функцией SDL_SetColorKey().

    Пример:

    void ShowBMP(char *file, SDL_Surface *screen, int x, int y){    SDL_Surface *image;    SDL_Rect dest;     /* Загрузим BMP файл на поверхность */    image = SDL_LoadBMP(file);    if ( image == NULL ) {        fprintf(stderr, "Couldn't load %s: %s\n", file, SDL_GetError());        return;    }     /* Блитируем (копируем) на экранную поверхность.       Поверхность не должна быть заблокирована (locked).     */    dest.x = x;    dest.y = y;    dest.w = image->w;    dest.h = image->h;    SDL_BlitSurface(image, NULL, screen, &dest);     /* Обновим измененную часть экрана */    SDL_UpdateRects(screen, 1, &dest);}
    [Назад] Содержание [Дальше]


    Звук

  • Открытие аудио устройства

  • Вам необходима callback-функция, которая будет смешивать ваши аудио данные и помещать их в аудио поток. Затем необходимо выбрать желательный аудио формат и частоту, и открыть аудио устройство.
    Воспроизведение не начнется до тех пор, пока вы не вызовете SDL_PauseAudio(0). Эта функция позволяет выполнить действия, которые необходимо выполнить до вызова CallBack функции. Как только вы завершите использование звука, закройте его функцией SDL_CloseAudio(). Совет:

    Если ваша программа оперирует с различными звуковыми форматами, передайте вторым параметром указатель на SDL_AudioSpec  в SDL_OpenAudio(), чтобы получить текущий аппаратный аудио формат. Если же вы оставите второй указатель равным NULL, аудио данные будут конвертированы в аппаратный аудио формат во время выполнения.

    Пример:
    #include "SDL.h"#include "SDL_audio.h"{    extern void mixaudio(void *unused, Uint8 *stream, int len);    SDL_AudioSpec fmt;     /* Установить 16бит стерео с 22Khz */    fmt.freq = 22050;    fmt.format = AUDIO_S16;    fmt.channels = 2;    fmt.samples = 512;        /* Хорошее значение для игр */    fmt.callback = mixaudio;    fmt.userdata = NULL;     /* открыть аудио усройство и начать воспроизведение! */    if ( SDL_OpenAudio(&fmt, NULL) < 0 ) {        fprintf(stderr, "Не могу открыть аудио: %s\n", SDL_GetError());        exit(1);    }    SDL_PauseAudio(0);     ...     SDL_CloseAudio();}
  • Загрузка и воспроизведение звука

  • Для вашего удобства, SDL предоставляет единственную функцию для загрузки звука, SDL_LoadWAV(). После того, как вы загрузили звук, вы должны преобразовать его к звуковому формату выходного потока с помощью функции SDL_ConvertAudio(), и сделать его доступным для вашей функции микширования. Совет:

    Устройства SDL audio предназначены для низкоуровневого программного аудио микшера. Хорошую реализацию такого микшера, доступную по LGPL лицензии, можно найти в архиве SDL demos.
    <


    Пример:

    #define NUM_SOUNDS 2struct sample {    Uint8 *data;    Uint32 dpos;    Uint32 dlen;} sounds[NUM_SOUNDS]; void mixaudio(void *unused, Uint8 *stream, int len){    int i;    Uint32 amount;     for ( i=0; i len ) {            amount = len;        }        SDL_MixAudio(stream, &sounds[i].data[sounds[i].dpos], amount, SDL_MIX_MAXVOLUME);        sounds[i].dpos += amount;    }} void PlaySound(char *file){    int index;    SDL_AudioSpec wave;    Uint8 *data;    Uint32 dlen;    SDL_AudioCVT cvt;     /* Найти пустой ( или до конца воспроизведенный) звуковой слот */    for ( index=0; index
    [Назад] Содержание [Дальше]


    

        Инновации: Менеджмент - Моделирование - Софт