Страницы

Поиск по вопросам

среда, 5 февраля 2020 г.

Проблемы с BASS

#c #bassdll


Пытаюсь скомпилировать такой код:

/*
    BASS simple console player
    Copyright (c) 1999-2015 Un4seen Developments Ltd.
*/

#include 
#include 
#define BASSDEF(f) (WINAPI *f) // define the functions as pointers
#include "bass.h"

#ifdef _WIN32 // Windows
#include 
#else // OSX/Linux
#include 
#include 
#include 
#include 

#define Sleep(x) usleep(x*1000)

int _kbhit()
{
    int r;
    fd_set rfds;
    struct timeval tv={0};
    struct termios term,oterm;
    tcgetattr(0,&oterm);
    memcpy(&term,&oterm,sizeof(term));
    cfmakeraw(&term);
    tcsetattr(0,TCSANOW,&term);
    FD_ZERO(&rfds);
    FD_SET(0,&rfds);
    r=select(1,&rfds,NULL,NULL,&tv);
    tcsetattr(0,TCSANOW,&oterm);
    return r;
}
#endif

// display error messages
void Error(const char *text) 
{
    printf("Error(%d): %s\n",BASS_ErrorGetCode(),text);
    BASS_Free();
    exit(0);
}

void ListDevices()
{
    BASS_DEVICEINFO di;
    int a;
    for (a=1;BASS_GetDeviceInfo(a,&di);a++) {
        if (di.flags&BASS_DEVICE_ENABLED) // enabled output device
            printf("dev %d: %s\n",a,di.name);
    }
}

void main(int argc, char **argv)
{
    DWORD chan,act,time,level;
    BOOL ismod;
    QWORD pos;
    int a,device=-1;

    printf("Simple console mode BASS example : MOD/MPx/OGG/WAV player\n"
            "---------------------------------------------------------\n");

    // check the correct BASS was loaded
    if (HIWORD(BASS_GetVersion())!=BASSVERSION) {
        printf("An incorrect version of BASS was loaded");
        return;
    }

    for (a=1;a\n"
            "\t-l = list devices\n"
            "\t-d = device number\n");
        return;
    }

    // initialize output device
    if (!BASS_Init(device,44100,0,0,NULL))
        Error("Can't initialize device");

    // try streaming the file/url
    if ((chan=BASS_StreamCreateFile(FALSE,argv[argc-1],0,0,BASS_SAMPLE_LOOP))
        || (chan=BASS_StreamCreateURL(argv[argc-1],0,BASS_SAMPLE_LOOP,0,0))) {
        pos=BASS_ChannelGetLength(chan,BASS_POS_BYTE);
        if (BASS_StreamGetFilePosition(chan,BASS_FILEPOS_DOWNLOAD)!=-1) {
            // streaming from the internet
            if (pos!=-1)
#ifdef _WIN32
                printf("streaming internet file [%I64d bytes]",pos);
#else
                printf("streaming internet file [%lld bytes]",pos);
#endif
            else
                printf("streaming internet file");
        } else
#ifdef _WIN32
            printf("streaming file [%I64d bytes]",pos);
#else
            printf("streaming file [%lld bytes]",pos);
#endif
        ismod=FALSE;
    } else {
        // try loading the MOD (with looping, sensitive ramping, and calculate the
duration)
        if (!(chan=BASS_MusicLoad(FALSE,argv[argc-1],0,0,BASS_SAMPLE_LOOP|BASS_MUSIC_RAMPS|BASS_MUSIC_PRESCAN,1)))
            // not a MOD either
            Error("Can't play the file");
        { // count channels
            float dummy;
            for (a=0;BASS_ChannelGetAttribute(chan,BASS_ATTRIB_MUSIC_VOL_CHAN+a,&dummy);a++);
        }
        printf("playing MOD music \"%s\" [%u chans, %u orders]",
            BASS_ChannelGetTags(chan,BASS_TAG_MUSIC_NAME),a,(DWORD)BASS_ChannelGetLength(chan,BASS_POS_MUSIC_ORDER));
        pos=BASS_ChannelGetLength(chan,BASS_POS_BYTE);
        ismod=TRUE;
    }

    // display the time length
    if (pos!=-1) {
        time=(DWORD)BASS_ChannelBytes2Seconds(chan,pos);
        printf(" %u:%02u\n",time/60,time%60);
    } else // no time length available
        printf("\n");

    BASS_ChannelPlay(chan,FALSE);

    while (!_kbhit() && (act=BASS_ChannelIsActive(chan))) {
        // display some stuff and wait a bit
        level=BASS_ChannelGetLevel(chan);
        pos=BASS_ChannelGetPosition(chan,BASS_POS_BYTE);
        time=BASS_ChannelBytes2Seconds(chan,pos);
#ifdef _WIN32
        printf("pos %09I64u",pos);
#else
        printf("pos %09llu",pos);
#endif
        if (ismod) {
            pos=BASS_ChannelGetPosition(chan,BASS_POS_MUSIC_ORDER);
            printf(" (%03u:%03u)",LOWORD(pos),HIWORD(pos));
        }
        printf(" - %u:%02u - L ",time/60,time%60);
        if (act==BASS_ACTIVE_STALLED) { // playback has stalled
            printf("-- buffering : %05u --",(DWORD)BASS_StreamGetFilePosition(chan,BASS_FILEPOS_BUFFER));
        } else {
            for (a=27204;a>200;a=a*2/3) putchar(LOWORD(level)>=a?'*':'-');
            putchar(' ');
            for (a=210;a<32768;a=a*3/2) putchar(HIWORD(level)>=a?'*':'-');
        }
        printf(" R - cpu %.2f%%  \r",BASS_GetCPU());
        fflush(stdout);
        Sleep(50);
    }
    printf("                                                                    
        \r");

    // wind the frequency down...
    BASS_ChannelSlideAttribute(chan,BASS_ATTRIB_FREQ,1000,500);
    Sleep(300);
    // ...and fade-out to avoid a "click"
    BASS_ChannelSlideAttribute(chan,BASS_ATTRIB_VOL,-1,200);
    // wait for slide to finish
    while (BASS_ChannelIsSliding(chan,0)) Sleep(1);

    BASS_Free();
}


Этот пример доступен из коробки. От себя добавил только

#define BASSDEF(f) (WINAPI* f) // define the functions as pointers


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

Program received signal SIGSEGV, Segmentation fault.
0x00000000 in ?? ()

(gdb) bt
#0  0x00000000 in ?? ()
#1  0x004015c1 in main (argc=1, argv=0xdc15b0) at qks.c:68


Даже этот пример не работает:

#define BASSDEF(f) (WINAPI *f)
#include "bass.h"

int main() {
    BASS_Init(-1, 44100, 0, 0, NULL);
}


Компилирую так:

gcc.exe -m32 -Wall -Wextra -Wpedantic -lbass -mwindows qks.c -o qks.exe


ОС - Windows 10 x64; GCC - 4.9.2

UPD

Появился небольшой прогресс. Эта программа компилируется и успешно завершается:

#include 
#include 
#include 
#define BASSDEF(f) (WINAPI *f)
#include "bass.h"

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine,
int nCmdShow) {
    (void) hPrevInstance, (void) lpCmdLine, (void) nCmdShow;

    HINSTANCE bass=LoadLibrary("bass.dll"); // load BASS
    BASS_Init=GetProcAddress(bass,"BASS_Init"); // get BASS_Init
    BASS_Init(-1,44100,0,NULL,NULL) // call BASS_Init
}


Вот что написано в документации:


  The downside is that you have to manually import each function that
  you use, with the GetProcAddress function. But it has been made a lot
  simpler to import BASS this way by the use of the BASSDEF #define.
  Here's a small example:

#define BASSDEF(f) (WINAPI *f) // define the functions as pointers
#include "bass.h" ... HINSTANCE bass=LoadLibrary("BASS.DLL"); // load BASS
BASS_Init=GetProcAddress(bass,"BASS_Init"); // get BASS_Init
BASS_Init(-1,44100,0,hWnd,NULL); // call BASS_Init



Однако при попытке получить адрес других функций ловлю предупреждение:

warning: assignment from incompatible pointer type
  BASS_GetVersion=GetProcAddress(bass,"BASS_GetVersion");


И если вызвать эту функцию (или какие-нибудь другие), то снова происходит SIGSEGV.

UPD 2 

Приведенный выше пример работает, если добавит в начало тела main эти строки:

HINSTANCE bass=LoadLibrary("bass.dll"); // load BASS
BASS_Init=GetProcAddress(bass,"BASS_Init"); // get BASS_Init
BASS_GetVersion=GetProcAddress(bass,"BASS_GetVersion");
BASS_Free=GetProcAddress(bass,"BASS_Free");
BASS_SetVolume=GetProcAddress(bass,"BASS_SetVolume");
BASS_SetConfig=GetProcAddress(bass,"BASS_SetConfig");
BASS_SampleFree=GetProcAddress(bass,"BASS_SampleFree");
BASS_SampleLoad=GetProcAddress(bass,"BASS_SampleLoad");
BASS_SampleGetChannel=GetProcAddress(bass,"BASS_SampleGetChannel");
BASS_ChannelPlay=GetProcAddress(bass,"BASS_ChannelPlay");
BASS_ChannelStop=GetProcAddress(bass,"BASS_ChannelStop");
BASS_GetVolume=GetProcAddress(bass,"BASS_GetVolume");
BASS_GetVersion=GetProcAddress(bass,"BASS_GetVersion");
BASS_GetCPU=GetProcAddress(bass,"BASS_GetCPU");
BASS_Pause=GetProcAddress(bass,"BASS_Pause");
BASS_Start=GetProcAddress(bass,"BASS_Start");
BASS_MusicFree=GetProcAddress(bass,"BASS_MusicFree");
BASS_MusicLoad=GetProcAddress(bass,"BASS_MusicLoad");
BASS_ChannelSetAttribute=GetProcAddress(bass,"BASS_ChannelSetAttribute");
BASS_StreamCreateFile=GetProcAddress(bass,"BASS_StreamCreateFile");
BASS_ErrorGetCode=GetProcAddress(bass,"BASS_ErrorGetCode");
BASS_StreamCreateURL=GetProcAddress(bass, "BASS_StreamCreateURL");
BASS_ChannelGetLength=GetProcAddress(bass, "BASS_ChannelGetLength");
BASS_StreamGetFilePosition=GetProcAddress(bass, "BASS_StreamGetFilePosition");
BASS_ChannelGetAttribute=GetProcAddress(bass, "BASS_ChannelGetAttribute");
BASS_ChannelGetTags=GetProcAddress(bass, "BASS_ChannelGetTags");
BASS_ChannelGetLength=GetProcAddress(bass, "BASS_ChannelGetLength");
BASS_ChannelBytes2Seconds=GetProcAddress(bass, "BASS_ChannelBytes2Seconds");
BASS_ChannelIsActive=GetProcAddress(bass, "BASS_ChannelIsActive");
BASS_ChannelGetLevel=GetProcAddress(bass, "BASS_ChannelGetLevel");
BASS_ChannelGetPosition=GetProcAddress(bass, "BASS_ChannelGetPosition");
BASS_ChannelSlideAttribute=GetProcAddress(bass, "BASS_ChannelSlideAttribute");


Одно большое "НО":
Звук воспроизводиться при запуске из командной строки (cmd.exe, ConEmu), но нет текстовой
информации. Т.е. я запускаю программу и она работает в фоне, в терминале сразу появляется
приглашение ввода:

user@LAPTOP-OIQUIP0K C:\Users\user\Documents\Projects\sound 
> qks.exe -d -1 "C:\Users\user\Music\Radiohead\Creep.mp3"   

user@LAPTOP-OIQUIP0K C:\Users\user\Documents\Projects\sound 
>             


Однако вся текстовая информация (звук тоже работает) появляется во встроенной консоли
SublimeText 3:

C:\Users\user\Documents\Projects\sound>qks.exe -d -1 "C:\Users\user\Music\Radiohead\Creep.mp3" 
Simple console mode BASS example : MOD/MPx/OGG/WAV player
---------------------------------------------------------
streaming file [41587200 bytes] 3:55
pos 000000136 - 0:00 - L ------------- ------------- R - cpu 0.00%  
pos 000005944 - 0:00 - L ------------- ------------- R - cpu 0.00%  
pos 000014808 - 0:00 - L ------------- ------------- R - cpu 0.00%  
pos 000023728 - 0:00 - L ------------- ------------- R - cpu 0.00%  
pos 000032628 - 0:00 - L ------------- ------------- R - cpu 0.07%  
pos 000041584 - 0:00 - L ---********** **********--- R - cpu 0.07%  
pos 000050496 - 0:00 - L -----******** ********----- R - cpu 0.14%  
pos 000059536 - 0:00 - L -----******** *********---- R - cpu 0.14%  
pos 000068572 - 0:00 - L -----******** *********---- R - cpu 0.19%  
pos 000077544 - 0:00 - L -----******** ********----- R - cpu 0.19%  
pos 000086464 - 0:00 - L -----******** *********---- R - cpu 0.25%  
pos 000095364 - 0:00 - L -----******** ********----- R - cpu 0.25%
...


Информация о компиляторе (gcc -v):

Using built-in specs.
COLLECT_GCC=gcc.exe
COLLECT_LTO_WRAPPER=c:/mingw64/bin/../libexec/gcc/x86_64-w64-mingw32/4.8.1/lto-wrapper.exe
Target: x86_64-w64-mingw32
Configured with: ../../../src/gcc-4.8.1/configure --build=x86_64-w64-mingw32 --enable-targets=all
--enable-languages=ada,c,c++,fortran,lto,objc,obj-c++ --enable-libgomp --enable-lto
--enable-graphite --enable-cxx-flags=-DWINPTHREAD_STATIC --enable-libstdcxx-debug --enable-threads=posix
--enable-version-specific-runtime-libs --enable-fully-dynamic-string --enable-libstdcxx-threads
--enable-libstdcxx-time --with-gnu-ld --disable-werror --disable-nls --disable-win32-registry
--prefix=/mingw64tdm --with-local-prefix=/mingw64tdm --with-pkgversion=tdm64-2 --with-bugurl=http://tdm-gcc.tdragon.net/bugs
Thread model: posix
gcc version 4.8.1 (tdm64-2)


Отладка:

user@LAPTOP-OIQUIP0K C:\Users\user\Documents\Projects\sound
> gdb.exe qks.exe
GNU gdb (GDB) 7.7.50.20140303-cvs
Copyright (C) 2014 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later 
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "i686-pc-mingw32".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
.
Find the GDB manual and other documentation resources online at:
.
For help, type "help".
Type "apropos word" to search for commands related to "word".

This binary was built by Equation Solution ...
Reading symbols from qks.exe...done.
(gdb) b main
Breakpoint 1 at 0x4015c5: file qks.c, line 59.
(gdb) r -d -1 "C:\Users\user\Music\Radiohead\Creep.mp3"
Starting program: C:\Users\user\Documents\Projects\sound\qks.exe -d -1 "C:\Users\user\Music\Radiohead\Creep.mp3"
[New Thread 2708.0x16f4]
[New Thread 2708.0x4c0]

Breakpoint 1, main (argc=4, argv=0xab2390) at qks.c:59
59              HINSTANCE bass=LoadLibrary("bass.dll"); // load BASS
(gdb) n
[New Thread 2708.0xd28]
60              BASS_Init=GetProcAddress(bass,"BASS_Init"); // get BASS_Init
(gdb)
61              BASS_GetVersion=GetProcAddress(bass,"BASS_GetVersion");
(gdb)
62              BASS_Free=GetProcAddress(bass,"BASS_Free");
(gdb)
63              BASS_SetVolume=GetProcAddress(bass,"BASS_SetVolume");
(gdb)
64              BASS_SetConfig=GetProcAddress(bass,"BASS_SetConfig");
(gdb)
65              BASS_SampleFree=GetProcAddress(bass,"BASS_SampleFree");
(gdb)
66              BASS_SampleLoad=GetProcAddress(bass,"BASS_SampleLoad");
(gdb)
67              BASS_SampleGetChannel=GetProcAddress(bass,"BASS_SampleGetChannel");
(gdb)
68              BASS_ChannelPlay=GetProcAddress(bass,"BASS_ChannelPlay");
(gdb)
69              BASS_ChannelStop=GetProcAddress(bass,"BASS_ChannelStop");
(gdb)
70              BASS_GetVolume=GetProcAddress(bass,"BASS_GetVolume");
(gdb)
71              BASS_GetVersion=GetProcAddress(bass,"BASS_GetVersion");
(gdb)
72              BASS_GetCPU=GetProcAddress(bass,"BASS_GetCPU");
(gdb)
73              BASS_Pause=GetProcAddress(bass,"BASS_Pause");
(gdb)
74              BASS_Start=GetProcAddress(bass,"BASS_Start");
(gdb)
75              BASS_MusicFree=GetProcAddress(bass,"BASS_MusicFree");
(gdb)
76              BASS_MusicLoad=GetProcAddress(bass,"BASS_MusicLoad");
(gdb)
77              BASS_ChannelSetAttribute=GetProcAddress(bass,"BASS_ChannelSetAttribute");
(gdb)
78              BASS_StreamCreateFile=GetProcAddress(bass,"BASS_StreamCreateFile");
(gdb)
79              BASS_ErrorGetCode=GetProcAddress(bass,"BASS_ErrorGetCode");
(gdb)
80              BASS_StreamCreateURL=GetProcAddress(bass, "BASS_StreamCreateURL");
(gdb)
81              BASS_ChannelGetLength=GetProcAddress(bass, "BASS_ChannelGetLength");
(gdb)
82              BASS_StreamGetFilePosition=GetProcAddress(bass, "BASS_StreamGetFilePosition");
(gdb)
83              BASS_ChannelGetAttribute=GetProcAddress(bass, "BASS_ChannelGetAttribute");
(gdb)
84              BASS_ChannelGetTags=GetProcAddress(bass, "BASS_ChannelGetTags");
(gdb)
85              BASS_ChannelGetLength=GetProcAddress(bass, "BASS_ChannelGetLength");
(gdb)
86              BASS_ChannelBytes2Seconds=GetProcAddress(bass, "BASS_ChannelBytes2Seconds");
(gdb)
87              BASS_ChannelIsActive=GetProcAddress(bass, "BASS_ChannelIsActive");
(gdb)
88              BASS_ChannelGetLevel=GetProcAddress(bass, "BASS_ChannelGetLevel");
(gdb)
89              BASS_ChannelGetPosition=GetProcAddress(bass, "BASS_ChannelGetPosition");
(gdb)
90              BASS_ChannelSlideAttribute=GetProcAddress(bass, "BASS_ChannelSlideAttribute");
(gdb)
95          int a,device=-1;
(gdb)
97          printf("Simple console mode BASS example : MOD/MPx/OGG/WAV player\n"
(gdb)
101         if (HIWORD(BASS_GetVersion())!=BASSVERSION) {
(gdb)
0x744d59c6 in ?? ()
(gdb) ni
0x744b10f9 in ?? () from C:\Users\user\Documents\Projects\sound\bass.dll
(gdb)
0x744b45c5 in ?? () from C:\Users\user\Documents\Projects\sound\bass.dll
(gdb)
0x744b45c6 in ?? () from C:\Users\user\Documents\Projects\sound\bass.dll
(gdb)
0x744b45cc in ?? () from C:\Users\user\Documents\Projects\sound\bass.dll
(gdb)
0x744b45d2 in ?? () from C:\Users\user\Documents\Projects\sound\bass.dll
(gdb)
0x744b45d4 in ?? () from C:\Users\user\Documents\Projects\sound\bass.dll
(gdb)
0x744b45d6 in ?? () from C:\Users\user\Documents\Projects\sound\bass.dll
(gdb)
0x744b45ee in ?? () from C:\Users\user\Documents\Projects\sound\bass.dll
(gdb)
0x744b45f0 in ?? () from C:\Users\user\Documents\Projects\sound\bass.dll
(gdb)
0x744b45f1 in ?? () from C:\Users\user\Documents\Projects\sound\bass.dll
(gdb)
0x744b10fe in ?? () from C:\Users\user\Documents\Projects\sound\bass.dll
(gdb)
0x744b1101 in ?? () from C:\Users\user\Documents\Projects\sound\bass.dll
(gdb)
0x744b1103 in ?? () from C:\Users\user\Documents\Projects\sound\bass.dll
(gdb)
0x744b1104 in ?? () from C:\Users\user\Documents\Projects\sound\bass.dll
(gdb)
0x744d59cb in ?? ()
(gdb)
0x744d59d0 in ?? ()
(gdb)
0x00401976 in main (argc=4, argv=0xab2390) at qks.c:101
101         if (HIWORD(BASS_GetVersion())!=BASSVERSION) {
(gdb) n
106         for (a=1;a


Ответы

Ответ 1



Есть такая штука - ABI - то есть описание того, как функция/класс будет представлена в бинарном виде. Это очень важно, когда линкуются два разных куска кода (например, exe и статическая библиотка) или просто вызываются (например, обращение к dll). В некоторых случаях линковщик (а это его работа) может проверить, что интерфейс не совпадает и поднять панику. А может и не знать или проигнорировать. Если два куска кода не совместимы на уровне ABI, то может произойти все что угодно. Самый простой пример - вызывающая сторона ожидает параметры в регистрах, а вызываемая - в стеке. Что именно прочитается со стека - никто не знает. В лучшем случае упадет с Access violation. В случае чистого Си эту проблему достаточно хорошо решили (всякие stdcall, cdecl и подобное). А вот в случае с++ все сильно сложнее - есть классы, а им нужно хранить данные, которые никак не стандартизированы. Даже если использовать только gcc, он может использовать различное ABI. Из последнего, на что я лично натолкнулся, это сильно измененный ABI при переходе от 4.9.х на 5.4.х (он вроде после 5.2 поменялся). Здесь есть много деталей. Теперь ближе к проблеме. По внешнему виду файла обычно сложно догадаться, какой версией компилятора он собран. Но её можно попробовать угадать. Судя по всему, там внутри все собрано с помощью vc6 (это очень древнее существо) и более-менее свежие gcc с ним поэтому и не совместимы. А старые - да. @VadimTagil просто повезло, что он угадал правильную версию. Что делать? использовать gcc 3.4 как не очень, как по мне, поэтому, лучше взять сорцы и пересобрать их именно тем gcc, которым будет собираться все остальное. Благое дело, там есть makefile и студийный проект.

Ответ 2



Ну, предупреждение "assignment from incompatible pointer type" вполне логично. GetProcAddress возвращает указатель на функцию с неопределенным списком параметров, возвращающую int. Приведение к любым другим указателям выведет это предупреждение. Его можно игнорировать. Что касается Segmentation Fault, у меня ничего такого не наблюдается. BASS_GetVersion, BASS_GetDeviceInfo нормально работают. Либо вы не ту версию DLL-ки подкладываете (32-битную вместо 64-битной или наоборот), либо какая-то локальная проблема с вашей системой. UPD: На всякий случай, мой рабочий пример: #include #include #define BASSDEF(f) (WINAPI *f) // define the functions as pointers #include "bass.h" #include void ListDevices() { BASS_DEVICEINFO di; int a; for (a=1;BASS_GetDeviceInfo(a,&di);a++) { if (di.flags&BASS_DEVICE_ENABLED) // enabled output device printf("dev %d: %s\n",a,di.name); } } void main(int argc, char **argv) { HINSTANCE bass=LoadLibrary("BASS.DLL"); // load BASS BASS_Init=GetProcAddress(bass,"BASS_Init"); BASS_GetVersion=GetProcAddress(bass,"BASS_GetVersion"); BASS_GetDeviceInfo=GetProcAddress(bass,"BASS_GetDeviceInfo"); // check the correct BASS was loaded DWORD ver; ver=BASS_GetVersion(); if (HIWORD(ver)!=BASSVERSION) { printf("An incorrect version of BASS was loaded"); return; } else { printf("BASS version is %d\n\n",ver); } ListDevices(); system("pause"); } Компилируется и успешно работает (выводит список устройств), тестировалось на: Windows 7 x64 GCC 3.4.2, 4.9.2 (32-битный режим) BASS 2.4

Ответ 3



Может то, что от себя добавлено, должно так выглядеть: #ifndef BASSDEF #ifdef WIN32 #define BASSDEF(f) WINAPI f #else #define BASSDEF(f) f #endif #endif Взято отсюда https://github.com/kusma/spaceflowers/blob/master/bassmod/bassmod.h Или просто вот так: #define BASSDEF(f) WINAPI f https://github.com/sonicretro/sadx-mod-loader/blob/master/extlib/bass/bass.h

Комментариев нет:

Отправить комментарий