Страницы

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

понедельник, 8 апреля 2019 г.

Проблемы с BASS

Пытаюсь скомпилировать такой код:
/* 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
",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
",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
" "---------------------------------------------------------
");
// check the correct BASS was loaded if (HIWORD(BASS_GetVersion())!=BASSVERSION) { printf("An incorrect version of BASS was loaded"); return; }
for (a=1;a
" "\t-l = list devices
" "\t-d = device number
"); 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
",time/60,time%60); } else // no time length available printf("
");
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%%
",BASS_GetCPU()); fflush(stdout); Sleep(50); } printf("
");
// 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
" (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Система:
OS: Windows 10 x64 CPU: AMD A9-9410 RADEON R5, COMPUTE CORES 2C+3G 2.90 GHz
Команды компиляции и запуска:
gcc.exe -g -std=c11 -m32 -Wall -Wextra -Wpedantic -lbass -mwindows qks.c -o qks.exe qks.exe -d -1 "C:\Users\user\Music\Eminem\Rap God.mp3"


Ответ

Есть такая штука - 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 и студийный проект.

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

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