├── Makefile ├── MakefileGCC5 ├── Makefiledb ├── MakefiledbGCC5 ├── README.md ├── http.c ├── http.h ├── in2.h ├── in_opus.c ├── in_opus.txt ├── infobox.c ├── infobox.h ├── opusfile ├── info.c ├── internal.h ├── makelib.bat ├── opusfile.c ├── opusfile.h ├── stream.c └── winerrno.h ├── resample.c ├── resample.h ├── resource.h ├── resource.rc ├── utf_ansi.c ├── wa_ipc.h ├── winerrno.h ├── wspiapi.c └── wspiapi.h /Makefile: -------------------------------------------------------------------------------- 1 | CC=gcc 2 | 3 | CFLAGS=-I. -I./opus \ 4 | -m32 -Os \ 5 | -Wall -Wextra \ 6 | -Wno-unused-parameter \ 7 | -Wstack-usage=4096 \ 8 | -fno-stack-check \ 9 | -fno-stack-protector \ 10 | -mno-stack-arg-probe \ 11 | -momit-leaf-frame-pointer \ 12 | -march=i386 -mtune=i686 -ffast-math \ 13 | -flto -flto-partition=none \ 14 | -fno-ident \ 15 | -mpreferred-stack-boundary=2 \ 16 | -foptimize-strlen \ 17 | -fno-exceptions \ 18 | -fno-dwarf2-cfi-asm \ 19 | -fno-asynchronous-unwind-tables \ 20 | -fmerge-all-constants \ 21 | -fno-semantic-interposition \ 22 | -fgcse-sm \ 23 | -fgcse-las \ 24 | -fipa-pta \ 25 | -D__USE_MINGW_ANSI_STDIO=0 \ 26 | -fno-plt 27 | 28 | 29 | # -DLOCAL_LRINTF \ 30 | 31 | # -fomit-frame-pointer \ 32 | 33 | # -freorder-blocks -fweb -frename-registers -funswitch-loops\ 34 | # -fwhole-program -fstrict-aliasing -fschedule-insns 35 | # -D__SSE1 sse_func.o 36 | 37 | LDFLAGS= -nostdlib -lgcc -lm -lkernel32 -lmsvcrt -luser32 -lgdi32 -lwsock32 -s 38 | LDFLAGS+= -Wl,-s,-dynamicbase \ 39 | -Wl,-nxcompat \ 40 | -Wl,--no-seh \ 41 | -Wl,--relax \ 42 | -Wl,--disable-runtime-pseudo-reloc \ 43 | -Wl,--enable-auto-import \ 44 | -Wl,--disable-stdcall-fixup 45 | 46 | in_opus.dll: in_opus.o resample.o infobox.o http.o wspiapi.o resource.o 47 | $(CC) -o in_opus.dll in_opus.o resample.o infobox.o http.o wspiapi.o resource.o\ 48 | oflto152/*.o opusfile/*.c logg/*.c -e_DllMain@12 -mdll $(LDFLAGS) $(CFLAGS) 49 | 50 | cp in_opus.dll "D:\Program Files\MediaPlayers\Winamp\Plugins" 51 | cp in_opus.dll "D:\Shared docs" 52 | cp in_opus.dll "D:\Program Files\MediaPlayers\winamp566\Plugins" 53 | cp in_opus.dll "D:\Program Files\MediaPlayers\winamp58\Plugins" 54 | cp in_opus.dll "D:\Program Files\MediaPlayers\winamp531\Plugins" 55 | cp in_opus.dll "D:\Program Files\MediaPlayers\MediaMonkey3\Plugins" 56 | cp in_opus.dll "D:\Program Files\MediaPlayers\MediaMonkey4\Plugins" 57 | cp in_opus.dll "D:\Program Files\MediaPlayers\Winamp591rc3\Plugins" 58 | 59 | 60 | in_opus.o : in_opus.c infobox.h resample.h resource.h 61 | $(CC) -c in_opus.c $(CFLAGS) 62 | infobox.o : infobox.c infobox.h utf_ansi.c 63 | $(CC) -c infobox.c $(CFLAGS) 64 | resource.o: resource.rc 65 | windres resource.rc resource.o 66 | wspiapi.o : wspiapi.c wspiapi.h 67 | $(CC) -c wspiapi.c $(CFLAGS) 68 | http.o : http.c http.h wspiapi.h winerrno.h 69 | $(CC) -c http.c $(CFLAGS) 70 | resample.o: resample.c resample.h 71 | $(CC) -c resample.c $(CFLAGS) -O2 -ffast-math 72 | #info.o: opusfile/info.c 73 | # $(CC) -c opusfile/info.c -o opusfile/info.o $(CFLAGS) 74 | #opusfile.o: opusfile/opusfile.c 75 | # $(CC) -c opusfile/opusfile.c -o opusfile/opusfile.o $(CFLAGS) 76 | #opusfile.o: opusfile/opusfile.c 77 | # $(CC) -c opusfile/opusfile.c -o opusfile/opusfile.o $(CFLAGS) 78 | #stream.o: opusfile/stream.c 79 | # $(CC) -c opusfile/stream.c -o opusfile/stream.o $(CFLAGS) 80 | clean : 81 | rm *.o in_opus.dll 82 | -------------------------------------------------------------------------------- /MakefileGCC5: -------------------------------------------------------------------------------- 1 | CC=gcc 2 | 3 | CFLAGS=-I. -I./opus \ 4 | -m32 -Os \ 5 | -Wall -Wextra \ 6 | -Wno-unused-parameter \ 7 | -fno-stack-check \ 8 | -fno-stack-protector \ 9 | -mno-stack-arg-probe \ 10 | -momit-leaf-frame-pointer \ 11 | -march=i486 -mtune=i686 \ 12 | -flto \ 13 | -fno-ident \ 14 | -mpreferred-stack-boundary=2 \ 15 | -foptimize-strlen \ 16 | -fno-exceptions \ 17 | -fno-dwarf2-cfi-asm \ 18 | -fno-asynchronous-unwind-tables \ 19 | -Wstack-usage=4096 \ 20 | -fgcse-sm \ 21 | -fgcse-las \ 22 | -D__USE_MINGW_ANSI_STDIO=0 \ 23 | 24 | 25 | # -fomit-frame-pointer \ 26 | 27 | # -freorder-blocks -fweb -frename-registers -funswitch-loops\ 28 | # -fwhole-program -fstrict-aliasing -fschedule-insns 29 | # -D__SSE1 sse_func.o 30 | 31 | LDFLAGS= -nostdlib -lgcc -lkernel32 -lmsvcrt -luser32 -lgdi32 -lwsock32 -s 32 | LDFLAGS+= -Wl,-s,-dynamicbase \ 33 | -Wl,-nxcompat \ 34 | -Wl,--no-seh \ 35 | -Wl,--relax \ 36 | -Wl,--disable-runtime-pseudo-reloc \ 37 | -Wl,--enable-auto-import \ 38 | -Wl,--disable-stdcall-fixup 39 | 40 | in_opus.dll: in_opus.o resample.o infobox.o http.o wspiapi.o resource.o 41 | $(CC) -o in_opus.dll in_opus.o resample.o infobox.o http.o wspiapi.o resource.o\ 42 | oflto/*.o opusfile/*.c logg/*.c -e_DllMain@12 -mdll $(LDFLAGS) $(CFLAGS) 43 | 44 | cp in_opus.dll "D:\Program Files\MediaPlayers\Winamp\Plugins" 45 | cp in_opus.dll "D:\Shared docs" 46 | cp in_opus.dll "D:\Program Files\MediaPlayers\winamp566\Plugins" 47 | cp in_opus.dll "D:\Program Files\MediaPlayers\winamp58\Plugins" 48 | cp in_opus.dll "D:\Program Files\MediaPlayers\winamp531\Plugins" 49 | cp in_opus.dll "D:\Program Files\MediaPlayers\MediaMonkey3\Plugins" 50 | cp in_opus.dll "D:\Program Files\MediaPlayers\MediaMonkey4\Plugins" 51 | cp in_opus.dll "D:\Program Files\MediaPlayers\Winamp591rc3\Plugins" 52 | 53 | in_opus.o : in_opus.c infobox.h resample.h resource.h 54 | $(CC) -c in_opus.c $(CFLAGS) 55 | infobox.o : infobox.c infobox.h utf_ansi.c 56 | $(CC) -c infobox.c $(CFLAGS) 57 | resource.o: resource.rc 58 | windres resource.rc resource.o 59 | wspiapi.o : wspiapi.c wspiapi.h 60 | $(CC) -c wspiapi.c $(CFLAGS) 61 | http.o : http.c http.h wspiapi.h winerrno.h 62 | $(CC) -c http.c $(CFLAGS) 63 | resample.o: resample.c resample.h 64 | $(CC) -c resample.c $(CFLAGS) -O2 -ffast-math 65 | #info.o: opusfile/info.c 66 | # $(CC) -c opusfile/info.c -o opusfile/info.o $(CFLAGS) 67 | #opusfile.o: opusfile/opusfile.c 68 | # $(CC) -c opusfile/opusfile.c -o opusfile/opusfile.o $(CFLAGS) 69 | #opusfile.o: opusfile/opusfile.c 70 | # $(CC) -c opusfile/opusfile.c -o opusfile/opusfile.o $(CFLAGS) 71 | #stream.o: opusfile/stream.c 72 | # $(CC) -c opusfile/stream.c -o opusfile/stream.o $(CFLAGS) 73 | clean : 74 | rm *.o in_opus.dll 75 | -------------------------------------------------------------------------------- /Makefiledb: -------------------------------------------------------------------------------- 1 | CC=gcc 2 | 3 | CFLAGS=-I. -I./opus \ 4 | -Og -g \ 5 | -Wall -Wextra \ 6 | -Wno-unused-parameter \ 7 | -fno-stack-check \ 8 | -fno-stack-protector \ 9 | -mno-stack-arg-probe \ 10 | -momit-leaf-frame-pointer \ 11 | -march=i486 -mtune=i686 \ 12 | -fanalyzer \ 13 | -fno-ident \ 14 | -mpreferred-stack-boundary=2 \ 15 | -foptimize-strlen \ 16 | -fno-exceptions \ 17 | -fno-dwarf2-cfi-asm \ 18 | -fno-asynchronous-unwind-tables \ 19 | -Wstack-usage=4096 \ 20 | -fgcse-sm \ 21 | -fgcse-las \ 22 | -D__USE_MINGW_ANSI_STDIO=0 \ 23 | -DLOCAL_LRINTF 24 | 25 | 26 | # -fomit-frame-pointer \ 27 | 28 | # -freorder-blocks -fweb -frename-registers -funswitch-loops\ 29 | # -fwhole-program -fstrict-aliasing -fschedule-insns 30 | # -D__SSE1 sse_func.o 31 | 32 | LDFLAGS= -nostdlib -lgcc -lm -lkernel32 -lmsvcrt -luser32 -lgdi32 -lwsock32 33 | LDFLAGS+= -Wl,-dynamicbase \ 34 | -Wl,-nxcompat \ 35 | -Wl,--no-seh \ 36 | -Wl,--relax \ 37 | -Wl,--enable-auto-import \ 38 | -Wl,--disable-stdcall-fixup 39 | 40 | in_opus.dll: in_opus.o resample.o infobox.o http.o wspiapi.o resource.o 41 | $(CC) -o in_opus.dll in_opus.o resample.o infobox.o http.o wspiapi.o resource.o\ 42 | libopus152.a logg/*.c opusfile/*.c -e_DllMain@12 -mdll $(LDFLAGS) $(CFLAGS) 43 | 44 | cp in_opus.dll "D:\Program Files\MediaPlayers\Winamp\Plugins" 45 | cp in_opus.dll "D:\Shared docs" 46 | cp in_opus.dll "D:\Program Files\MediaPlayers\winamp566\Plugins" 47 | cp in_opus.dll "D:\Program Files\MediaPlayers\winamp58\Plugins" 48 | cp in_opus.dll "D:\Program Files\MediaPlayers\winamp531\Plugins" 49 | cp in_opus.dll "D:\Program Files\MediaPlayers\MediaMonkey3\Plugins" 50 | cp in_opus.dll "D:\Program Files\MediaPlayers\MediaMonkey4\Plugins" 51 | 52 | in_opus.o : in_opus.c infobox.h resample.h resource.h 53 | $(CC) -c in_opus.c $(CFLAGS) 54 | infobox.o : infobox.c infobox.h utf_ansi.c 55 | $(CC) -c infobox.c $(CFLAGS) 56 | resource.o: resource.rc 57 | windres resource.rc resource.o 58 | wspiapi.o : wspiapi.c wspiapi.h 59 | $(CC) -c wspiapi.c $(CFLAGS) 60 | http.o : http.c http.h wspiapi.h winerrno.h 61 | $(CC) -c http.c $(CFLAGS) 62 | resample.o: resample.c resample.h 63 | $(CC) -c resample.c $(CFLAGS) -O2 -ffast-math 64 | #sse_func.o: sse_func.c sse_func.h 65 | # $(CC) -c sse_func.c $(CFLAGS) -msse -O2 -ffast-math -fno-lto 66 | #dietlibc.o: dietlibc.c dietlibc.h 67 | # $(CC) -c dietlibc.c $(CFLAGS) 68 | 69 | clean : 70 | rm *.o in_opus.dll 71 | -------------------------------------------------------------------------------- /MakefiledbGCC5: -------------------------------------------------------------------------------- 1 | CC=gcc 2 | 3 | CFLAGS=-I. -I./opus \ 4 | -Og -g \ 5 | -Wall -Wextra \ 6 | -Wno-unused-parameter \ 7 | -fno-stack-check \ 8 | -fno-stack-protector \ 9 | -mno-stack-arg-probe \ 10 | -momit-leaf-frame-pointer \ 11 | -march=i486 -mtune=i686 \ 12 | -flto \ 13 | -fno-ident \ 14 | -mpreferred-stack-boundary=2 \ 15 | -foptimize-strlen \ 16 | -fno-exceptions \ 17 | -fno-dwarf2-cfi-asm \ 18 | -fno-asynchronous-unwind-tables \ 19 | -Wstack-usage=4096 \ 20 | -fgcse-sm \ 21 | -fgcse-las 22 | 23 | # -fomit-frame-pointer \ 24 | 25 | # -freorder-blocks -fweb -frename-registers -funswitch-loops\ 26 | # -fwhole-program -fstrict-aliasing -fschedule-insns 27 | # -D__SSE1 sse_func.o 28 | 29 | LDFLAGS= -nostdlib -lgcc -lkernel32 -lmsvcrt -luser32 -lgdi32 -lwsock32 30 | LDFLAGS+= -Wl,-dynamicbase \ 31 | -Wl,-nxcompat \ 32 | -Wl,--no-seh \ 33 | -Wl,--relax \ 34 | -Wl,--enable-auto-import \ 35 | -Wl,--disable-stdcall-fixup 36 | 37 | in_opus.dll: in_opus.o resample.o infobox.o http.o wspiapi.o resource.o 38 | $(CC) -o in_opus.dll in_opus.o resample.o infobox.o http.o wspiapi.o resource.o\ 39 | oflto/*.o opusfile/*.c -e_DllMain@12 -mdll $(LDFLAGS) $(CFLAGS) 40 | 41 | cp in_opus.dll "D:\Program Files\MediaPlayers\Winamp\Plugins" 42 | cp in_opus.dll "D:\Shared docs" 43 | cp in_opus.dll "D:\Program Files\MediaPlayers\winamp566\Plugins" 44 | cp in_opus.dll "D:\Program Files\MediaPlayers\winamp58\Plugins" 45 | cp in_opus.dll "D:\Program Files\MediaPlayers\winamp531\Plugins" 46 | cp in_opus.dll "D:\Program Files\MediaPlayers\MediaMonkey3\Plugins" 47 | cp in_opus.dll "D:\Program Files\MediaPlayers\MediaMonkey4\Plugins" 48 | 49 | in_opus.o : in_opus.c infobox.h resample.h resource.h 50 | $(CC) -c in_opus.c $(CFLAGS) 51 | infobox.o : infobox.c infobox.h utf_ansi.c 52 | $(CC) -c infobox.c $(CFLAGS) 53 | resource.o: resource.rc 54 | windres resource.rc resource.o 55 | wspiapi.o : wspiapi.c wspiapi.h 56 | $(CC) -c wspiapi.c $(CFLAGS) 57 | http.o : http.c http.h wspiapi.h winerrno.h 58 | $(CC) -c http.c $(CFLAGS) 59 | resample.o: resample.c resample.h 60 | $(CC) -c resample.c $(CFLAGS) -O2 -ffast-math 61 | #sse_func.o: sse_func.c sse_func.h 62 | # $(CC) -c sse_func.c $(CFLAGS) -msse -O2 -ffast-math -fno-lto 63 | #dietlibc.o: dietlibc.c dietlibc.h 64 | # $(CC) -c dietlibc.c $(CFLAGS) 65 | 66 | clean : 67 | rm *.o in_opus.dll 68 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # in_opus 2 | Winamp 2.x and 5.x input plugin for Opus file 3 | 4 | 5 | == PURPOSE == 6 | 7 | Opus is a quite new audio file format, very good for generic lossy 8 | compression especially at low bitrates including voice. 9 | 10 | I developed this plugin because I wanted to be able to play OPUS files 11 | with Winamp 2, both on my Windows XP machine and an old Windows 98 SE, 12 | That is still used for playing music and for retro gaming at my home. 13 | 14 | I had to build myself the libraries because they include dependencies 15 | that are not present on this old OS. 16 | 17 | The plugin was tested and works fine on Windows 95/98SE/NT4/2000/XP/2003/7. 18 | 19 | There is a very good plugin that was already written by thinktink but 20 | it only works for Winamp 5 and recent windows versions. 21 | 22 | I provide this plugin for compatibility with old platforms. to my knowledge, 23 | this is the only Opus player for Windows 9x. and some of you may still 24 | use Winamp 2 for some reasons. 25 | 26 | 27 | == THIS PROGRAM IS BASED ON == 28 | 29 | libopus 1.5.2, * opusfile 0.12 (http://opus-codec.org/) 30 | libogg 1.3.2 (http://xiph.org/ogg/) 31 | Compiled using gcc 13.2, (MinGW64 / winlibs.com) on Windows Server 2003 32 | 33 | == FEATURES == 34 | 35 | * Opens *.opus and *.opu files contained in an ogg stream (this is what opus 36 | files are supposed to be anyway) the extension has to be .opu or .opus. 37 | 38 | * Opens HTTP OPUS Radio streams since v0.610, displays Shoutcast/Icecast 39 | server info on Alt+3 since v0.888. 40 | 41 | * Displays most standard Tags values (Title, Artist, Date... on Alt+3) 42 | In addition there is a panel at the bottom of the Tag dialog box, 43 | displaying the content of the "DESCRIPTION" tag, used by youtube-dl 44 | for the video comment when using the --add-metadata switch. 45 | 46 | * Full replaygain support. 47 | 48 | * Output at any rate from 1-192kHz, since v0.777, 49 | and using 8, 16, 24 or 32 bits per samples wince v0.892. 50 | 51 | * Runs on Windows 95/98SE/NT4/2000/XP/2003/7/8/10, with Winamp 2.x, 5.x, 52 | WACUP and XMPlay 2.1+. You need XMPlay3.7+ to enable UNICODE_FILE. 53 | Finally the plugin was tested with MediaMonkey 3.2.5 and 4.1.29 54 | 55 | * UNICODE filename support (v0.666+) under Windows NT4+ as well as UNICODE 56 | tags support (v0.555+). 57 | 58 | 59 | == LIMITATIONS == 60 | 61 | I did not want to lose time so there are some limitations, 62 | they may change in the future if I continue the project. 63 | 64 | * No way to edit tags. 65 | 66 | * UTF-8 is not really supported under Win9x; all tags are converted 67 | to Windows-1252 for display Thus you will not have all the Unicode 68 | characters. Not a big deal if you are from a European country. 69 | Under NT the UTF-8 strings are converted to UTF-16 since 0.555. 70 | 71 | * In the Winamp playlist only the filename/URL will be visible. 72 | Since v0.911 proper format will be shown in the PL on Winamp 5.x 73 | -------------------------------------------------------------------------------- /http.h: -------------------------------------------------------------------------------- 1 | #ifndef _OPUSFILE_HTTP_H 2 | #define _OPUSFILE_HTTP_H (1) 3 | 4 | #ifndef _LARGEFILE_SOURCE 5 | # define _LARGEFILE_SOURCE 6 | #endif 7 | #ifndef _LARGEFILE64_SOURCE 8 | # define _LARGEFILE64_SOURCE 9 | #endif 10 | #ifndef _FILE_OFFSET_BITS 11 | # define _FILE_OFFSET_BITS 64 12 | #endif 13 | 14 | #include 15 | #include 16 | 17 | # if OP_GNUC_PREREQ(3,0) 18 | /*Another alternative is 19 | (__builtin_constant_p(_x)?!!(_x):__builtin_expect(!!(_x),1)) 20 | but that evaluates _x multiple times, which may be bad.*/ 21 | # define OP_LIKELY(_x) (__builtin_expect(!!(_x),1)) 22 | # define OP_UNLIKELY(_x) (__builtin_expect(!!(_x),0)) 23 | # else 24 | # define OP_LIKELY(_x) (!!(_x)) 25 | # define OP_UNLIKELY(_x) (!!(_x)) 26 | # endif 27 | 28 | #define OP_FATAL(_str) abort() 29 | #define OP_ASSERT(_cond) 30 | #define OP_ALWAYS_TRUE(_cond) ((void)(_cond)) 31 | 32 | #define OP_INT64_MAX (2*(((ogg_int64_t)1<<62)-1)|1) 33 | #define OP_INT64_MIN (-OP_INT64_MAX-1) 34 | #define OP_INT32_MAX (2*(((ogg_int32_t)1<<30)-1)|1) 35 | #define OP_INT32_MIN (-OP_INT32_MAX-1) 36 | 37 | #define OP_MIN(_a,_b) ((_a)<(_b)?(_a):(_b)) 38 | #define OP_MAX(_a,_b) ((_a)>(_b)?(_a):(_b)) 39 | #define OP_CLAMP(_lo,_x,_hi) (OP_MAX(_lo,OP_MIN(_x,_hi))) 40 | 41 | /* Advance a file offset by the given amount, clamping against OP_INT64_MAX. 42 | * This is used to advance a known offset by things like OP_CHUNK_SIZE or 43 | * OP_PAGE_SIZE_MAX, while making sure to avoid signed overflow. 44 | * It assumes that both _offset and _amount are non-negative. 45 | */ 46 | #define OP_ADV_OFFSET(_offset,_amount) \ 47 | (OP_MIN(_offset,OP_INT64_MAX-(_amount))+(_amount)) 48 | 49 | /*The maximum channel count for any mapping we'll actually decode.*/ 50 | # define OP_NCHANNELS_MAX (8) 51 | 52 | /*Initial state.*/ 53 | # define OP_NOTOPEN (0) 54 | /*We've found the first Opus stream in the first link.*/ 55 | # define OP_PARTOPEN (1) 56 | # define OP_OPENED (2) 57 | /*We've found the first Opus stream in the current link.*/ 58 | # define OP_STREAMSET (3) 59 | /*We've initialized the decoder for the chosen Opus stream in the current 60 | link.*/ 61 | # define OP_INITSET (4) 62 | 63 | /* Information cached for a single link in a chained Ogg Opus file. 64 | * We choose the first Opus stream encountered in each link to play back (and 65 | * require at least one). 66 | */ 67 | 68 | int op_strncasecmp(const char *_a,const char *_b,int _n); 69 | 70 | #endif 71 | -------------------------------------------------------------------------------- /in2.h: -------------------------------------------------------------------------------- 1 | #include "out.h" 2 | 3 | // note: exported symbol is now winampGetInModule2. 4 | 5 | #define IN_VER 0x100 6 | 7 | typedef struct 8 | { 9 | int version; // module type (IN_VER) 10 | char *description; // description of module, with version string 11 | 12 | HWND hMainWindow; // winamp's main window (filled in by winamp) 13 | HINSTANCE hDllInstance; // DLL instance handle (Also filled in by winamp) 14 | 15 | char *FileExtensions; // "mp3\0Layer 3 MPEG\0mp2\0Layer 2 MPEG\0mpg\0Layer 1 MPEG\0" 16 | // May be altered from Config, so the user can select what they want 17 | 18 | int is_seekable; // is this stream seekable? 19 | int UsesOutputPlug; // does this plug-in use the output plug-ins? (musn't ever change, ever :) 20 | 21 | void (*Config)(HWND hwndParent); // configuration dialog 22 | void (*About)(HWND hwndParent); // about dialog 23 | 24 | void (*Init)(); // called at program init 25 | void (*Quit)(); // called at program quit 26 | 27 | void (*GetFileInfo)(char *file, char *title, int *length_in_ms); // if file == NULL, current playing is used 28 | int (*InfoBox)(char *file, HWND hwndParent); 29 | 30 | int (*IsOurFile)(char *fn); // called before extension checks, to allow detection of mms://, etc 31 | // playback stuff 32 | int (*Play)(char *fn); // return zero on success, -1 on file-not-found, some other value on other (stopping winamp) error 33 | void (*Pause)(); // pause stream 34 | void (*UnPause)(); // unpause stream 35 | int (*IsPaused)(); // ispaused? return 1 if paused, 0 if not 36 | void (*Stop)(); // stop (unload) stream 37 | 38 | // time stuff 39 | int (*GetLength)(); // get length in ms 40 | int (*GetOutputTime)(); // returns current output time in ms. (usually returns outMod->GetOutputTime() 41 | void (*SetOutputTime)(int time_in_ms); // seeks to point in stream (in ms). Usually you signal yoru thread to seek, which seeks and calls outMod->Flush().. 42 | 43 | // volume stuff 44 | void (*SetVolume)(int volume); // from 0 to 255.. usually just call outMod->SetVolume 45 | void (*SetPan)(int pan); // from -127 to 127.. usually just call outMod->SetPan 46 | 47 | // in-window builtin vis stuff 48 | 49 | void (*SAVSAInit)(int maxlatency_in_ms, int srate); // call once in Play(). maxlatency_in_ms should be the value returned from outMod->Open() 50 | // call after opening audio device with max latency in ms and samplerate 51 | void (*SAVSADeInit)(); // call in Stop() 52 | 53 | 54 | // simple vis supplying mode 55 | void (*SAAddPCMData)(void *PCMData, int nch, int bps, int timestamp); 56 | // sets the spec data directly from PCM data 57 | // quick and easy way to get vis working :) 58 | // needs at least 576 samples :) 59 | 60 | // advanced vis supplying mode, only use if you're cool. Use SAAddPCMData for most stuff. 61 | int (*SAGetMode)(); // gets csa (the current type (4=ws,2=osc,1=spec)) 62 | // use when calling SAAdd() 63 | void (*SAAdd)(void *data, int timestamp, int csa); // sets the spec data, filled in by winamp 64 | 65 | 66 | // vis stuff (plug-in) 67 | // simple vis supplying mode 68 | void (*VSAAddPCMData)(void *PCMData, int nch, int bps, int timestamp); // sets the vis data directly from PCM data 69 | // quick and easy way to get vis working :) 70 | // needs at least 576 samples :) 71 | 72 | // advanced vis supplying mode, only use if you're cool. Use VSAAddPCMData for most stuff. 73 | int (*VSAGetMode)(int *specNch, int *waveNch); // use to figure out what to give to VSAAdd 74 | void (*VSAAdd)(void *data, int timestamp); // filled in by winamp, called by plug-in 75 | 76 | 77 | // call this in Play() to tell the vis plug-ins the current output params. 78 | void (*VSASetInfo)(int nch, int srate); 79 | 80 | 81 | // dsp plug-in processing: 82 | // (filled in by winamp, called by input plug) 83 | 84 | // returns 1 if active (which means that the number of samples returned by dsp_dosamples 85 | // could be greater than went in.. Use it to estimate if you'll have enough room in the 86 | // output buffer 87 | int (*dsp_isactive)(); 88 | 89 | // returns number of samples to output. This can be as much as twice numsamples. 90 | // be sure to allocate enough buffer for samples, then. 91 | int (*dsp_dosamples)(short int *samples, int numsamples, int bps, int nch, int srate); 92 | 93 | 94 | // eq stuff 95 | void (*EQSet)(int on, char data[10], int preamp); // 0-64 each, 31 is +0, 0 is +12, 63 is -12. Do nothing to ignore. 96 | 97 | // info setting (filled in by winamp) 98 | void (*SetInfo)(int bitrate, int srate, int stereo, int synched); // if -1, changes ignored? :) 99 | 100 | Out_Module *outMod; // filled in by winamp, optionally used :) 101 | } In_Module; 102 | 103 | 104 | -------------------------------------------------------------------------------- /in_opus.c: -------------------------------------------------------------------------------- 1 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 2 | * in_opus.c by Raymond GILLIBERT, from template in_raw.c. * 3 | * * 4 | * This is a WINAMP 2+ Plugin to play .opus files and radio streams. * 5 | * I think you can do whatever you want with the code if you find it. * 6 | * The code depends on libopus, libogg and libopusfile * 7 | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include "resource.h" 16 | #include "infobox.h" 17 | #include "in2.h" 18 | #include "wa_ipc.h" 19 | #include "resample.h" 20 | 21 | // post this to the main window at end of file (after playback as stopped) 22 | #define WM_WA_MPEG_EOF WM_USER+2 23 | #define IN_UNICODE 0x0F000000 24 | 25 | // raw configuration. 26 | #define NCH 2 27 | #define SR 48000 28 | #define VERBOSE 0 29 | #define BIG_LENGTH INT_MAX 30 | #define BIG_LENGTH_MULT 60 31 | #define DECODE_BUFF_SIZE (20*(SR/1000)*NCH)// Smaller one 20ms in // in SAMPLES 32 | 33 | // FUNCTION DECLARATION 34 | static void first_init(char *font_str, int *font_height); 35 | void setvolume(int volume); 36 | void pause(); 37 | char *utf16_to_utf8(const wchar_t *input); 38 | wchar_t *utf8_to_utf16(const char *utfs); 39 | DWORD WINAPI DecodeThread(LPVOID b); // the decode thread procedure 40 | 41 | // GLOBAL VARIABLES 42 | static In_Module mod; // the output module (filled in near the bottom of this file) 43 | char lastfn[4*MAX_PATH]; // currently playing file 44 | 45 | opus_int64 decode_pos_ms; // current decoding position, in milliseconds. 46 | // Used for correcting DSP plug-in pitch changes 47 | volatile opus_int64 seek_needed; // if != -1, it is the point that the decode 48 | // thread should seek to, in ms. 49 | int paused; // are we paused? 50 | HANDLE thread_handle=INVALID_HANDLE_VALUE; // the handle to the decode thread 51 | 52 | volatile char killDecodeThread=0; // the kill switch for the decode thread 53 | 54 | char TAGS_DISPLAY_MODE, isNT; // 0: raw, 1: ANSI, 2: Unicode, 3: AUTO 55 | char UNICODE_FILE; 56 | char TARGET_LUFS; 57 | char USE_REPLAY_GAIN=0; // 0 : no, 1: Album, 2: Track. 58 | char UNIFIED_DIALOG; 59 | HFONT TAGS_FONT=NULL; 60 | 61 | static OggOpusFile *_of=NULL; 62 | static int THREAD_PRIORITY; 63 | static opus_int32 PRE_GAIN; 64 | static opus_int32 RADIO_GAIN; 65 | static int OP_CURRENT_GAIN; 66 | static int SAMPLERATE; 67 | static char RESAMPLE_Q; 68 | static char BPS; 69 | static char INTERNAL_VOLUME; 70 | static char VIS_BROKEN; // Winamp version < 5.11 71 | static char HOURS_MODE_ON; // to fix the 24days 20h 31min 24s and 647ms bug 72 | static char USE_DITHERING; 73 | static char RADIO; 74 | static char OGGEXT_HACK; 75 | static char INSTANT_BR; 76 | static char FORMAT_TITLE; 77 | 78 | ///////////////////////////////////////////////////////////////////////////// 79 | // This function is mendatory when linking with MinGW to be able to load dll 80 | // Uner windows 95. You have to built everithing with the following flags 81 | // -mdll -e _DllMain@12 -fno-stack-check -fno-stack-protector 82 | // -mno-stack-arg-probe -nostdlib -lgcc -lkernel32 -lmsvcrt -luser32 -lgdi32 83 | // I Know that is a lot.... (I am using TDM GCC 5.1, [2015]) 84 | BOOL WINAPI DllMain(HANDLE hInst, ULONG reason_for_call, LPVOID lpReserved) 85 | { 86 | return TRUE; 87 | } 88 | 89 | ///////////////////////////////////////////////////////////////////////////// 90 | // Configuration box we'd want to write it here (using DialogBox, etc) 91 | void config(HWND hwndParent) 92 | { 93 | int font_height; 94 | char monstring[1024]; 95 | char font_str[LF_FACESIZE]; 96 | 97 | // To read the config so we show up to date data 98 | // This also makes a way not to restart winamp... 99 | first_init(font_str, &font_height); 100 | 101 | sprintf(monstring, 102 | "Open \"winamp.ini\" with notepad and write the options you want to change\n" 103 | "Current Config:\n\n" 104 | 105 | "[IN_OPUS]\n" 106 | "USE_REPLAY_GAIN=%d\n" 107 | "PRE_GAIN=%.2f\n" 108 | "RADIO_GAIN=%.2f\n" 109 | "TAGS_DISPLAY_MODE=%d\n" 110 | "TAGS_FONT=%d %s\n" 111 | "OUT_SAMPLERATE=%d\n" 112 | "OUT_BITS=%d\n" 113 | "INTERNAL_VOLUME=%d\n" 114 | "TARGET_LUFS=%d\n\n" 115 | 116 | "USE_REPLAY_GAIN values:\n" 117 | "0: Disable (default), 1: Album gain 2: Track gain \n" 118 | "3: Auto track/album gain when shuffle on/off\n" 119 | "4: Raw gain (not even header gain)\n" 120 | "PRE_GAIN is a preamplification factor in dB, applied before RG.\n" 121 | "TAGS_DISPLAY_MODE: 0: Raw, 1: ANSI, 2: Force Unicode 3: Auto\n" 122 | "OUT_SAMPLERATE to set the output samplerate\n" 123 | "INTERNAL_VOLUME set to 1 to enable internal handling of volume.\n" 124 | "TARGET_LUFS, is the Loudness in Units of Full Scale that you want (default -23)" 125 | 126 | , USE_REPLAY_GAIN, ((float)PRE_GAIN)/256.F, ((float)RADIO_GAIN)/256.F 127 | , TAGS_DISPLAY_MODE, font_height, font_str, SAMPLERATE, BPS 128 | , INTERNAL_VOLUME, TARGET_LUFS 129 | ); 130 | MessageBox(hwndParent, monstring, "This is not the configuration you are looking for", MB_OK); 131 | } 132 | ///////////////////////////////////////////////////////////////////////////// 133 | // About Dialog box... 134 | void about(HWND hwndParent) 135 | { 136 | MessageBox(hwndParent, 137 | "OPUS File Player v0.914, by Raymond Gillibert (*.opus, *.opu files).\n" 138 | "Using libopus 1.5.2, libogg 1.3.2 and libopusfile 0.12.\n" 139 | "You can write me at raymond_gillibert@yahoo.fr if necessary." 140 | , "About Winamp OPUS Player",MB_OK); 141 | } 142 | 143 | ///////////////////////////////////////////////////////////////////////////// 144 | // To read the internal config 145 | static void readinternalconfig(char *font_str, int *font_height) 146 | { 147 | float pre_gain_float=0.0f, radio_gain_float=0.0f; 148 | int UNICODE_FILE_i=0, RESAMPLE_Q_i=0, USE_REPLAY_GAIN_i=0, INTERNAL_VOLUME_i=0 149 | , TAGS_DISPLAY_MODE_i=0, USE_DITHERING_i=0, TARGET_LUFS_i=0, bps_i=0 150 | , OGGEXT_HACK_i=0, UNIFIED_DIALOG_i=0, INSTANT_BR_i=0, FORMAT_TITLE_i=0; 151 | 152 | THREAD_PRIORITY = THREAD_PRIORITY_ABOVE_NORMAL; 153 | 154 | sscanf( "Out=48000, 2, 16 Uni=2, Gain=0, +0.0000, -3.0000, -23, 0, 3, 1, 0, 1, 1, 1, 0 " 155 | "Times New Roman or other font with a super long name", 156 | "Out=%d, %d, %d Uni=%d, Gain=%d, %f, %f, %d, %d, %d, %d, %d, %d, %d, %d, %d %[^\n]s" 157 | , &SAMPLERATE, &RESAMPLE_Q_i, &bps_i 158 | , &UNICODE_FILE_i 159 | , &USE_REPLAY_GAIN_i, &pre_gain_float, &radio_gain_float 160 | , &TARGET_LUFS_i 161 | , &INTERNAL_VOLUME_i 162 | , &TAGS_DISPLAY_MODE_i 163 | , &USE_DITHERING_i 164 | , &OGGEXT_HACK_i 165 | , &UNIFIED_DIALOG_i 166 | , &INSTANT_BR_i 167 | , &FORMAT_TITLE_i 168 | , font_height, font_str); 169 | 170 | RESAMPLE_Q = (char) RESAMPLE_Q_i; 171 | UNICODE_FILE = (char) UNICODE_FILE_i; 172 | BPS = (char) bps_i; 173 | USE_REPLAY_GAIN = (char) USE_REPLAY_GAIN_i; 174 | TAGS_DISPLAY_MODE = (char) TAGS_DISPLAY_MODE_i; 175 | USE_DITHERING = (char) USE_DITHERING_i; 176 | INTERNAL_VOLUME = (char) INTERNAL_VOLUME_i; 177 | TARGET_LUFS = (char) TARGET_LUFS_i; 178 | OGGEXT_HACK = (char) OGGEXT_HACK_i; 179 | UNIFIED_DIALOG = (char) UNIFIED_DIALOG_i; 180 | INSTANT_BR = (char) INSTANT_BR_i; 181 | FORMAT_TITLE = (char) FORMAT_TITLE_i; 182 | PRE_GAIN = lrintf(pre_gain_float*256.F); 183 | RADIO_GAIN = lrintf(radio_gain_float*256.F); 184 | } 185 | ///////////////////////////////////////////////////////////////////////////// 186 | // To read the config 187 | static void readconfig(char *ini_name, char *rez, char *font_str, int *font_height) 188 | { 189 | if(!ini_name || *ini_name=='\0' || !rez || !font_str || !font_height) return; 190 | if(INVALID_FILE_ATTRIBUTES == GetFileAttributes(ini_name)) return; 191 | 192 | USE_REPLAY_GAIN =GetPrivateProfileInt("IN_OPUS", "USE_REPLAY_GAIN", USE_REPLAY_GAIN, ini_name); 193 | THREAD_PRIORITY =GetPrivateProfileInt("IN_OPUS", "THREAD_PRIORITY", THREAD_PRIORITY, ini_name); 194 | TAGS_DISPLAY_MODE=GetPrivateProfileInt("IN_OPUS", "TAGS_DISPLAY_MODE", TAGS_DISPLAY_MODE, ini_name); 195 | USE_DITHERING =GetPrivateProfileInt("IN_OPUS", "USE_DITHERING", USE_DITHERING, ini_name); 196 | UNICODE_FILE =GetPrivateProfileInt("IN_OPUS", "UNICODE_FILE", UNICODE_FILE, ini_name); 197 | SAMPLERATE =GetPrivateProfileInt("IN_OPUS", "OUT_SAMPLERATE", SAMPLERATE, ini_name); 198 | RESAMPLE_Q =GetPrivateProfileInt("IN_OPUS", "RESAMPLE_Q", RESAMPLE_Q, ini_name); 199 | INTERNAL_VOLUME =GetPrivateProfileInt("IN_OPUS", "INTERNAL_VOLUME", INTERNAL_VOLUME, ini_name); 200 | TARGET_LUFS =GetPrivateProfileInt("IN_OPUS", "TARGET_LUFS", TARGET_LUFS, ini_name); 201 | BPS =GetPrivateProfileInt("IN_OPUS", "OUT_BITS", BPS, ini_name); 202 | OGGEXT_HACK =GetPrivateProfileInt("IN_OPUS", "OGGEXT_HACK", OGGEXT_HACK, ini_name); 203 | UNIFIED_DIALOG =GetPrivateProfileInt("IN_OPUS", "UNIFIED_DIALOG", UNIFIED_DIALOG, ini_name); 204 | INSTANT_BR =GetPrivateProfileInt("IN_OPUS", "INSTANT_BR", INSTANT_BR, ini_name); 205 | FORMAT_TITLE =GetPrivateProfileInt("IN_OPUS", "FORMAT_TITLE", FORMAT_TITLE, ini_name); 206 | 207 | if(BPS!=8 && BPS!=24 && BPS!=32) BPS=16; 208 | 209 | if(RESAMPLE_Q < 0) RESAMPLE_Q=0; else if (RESAMPLE_Q > 4) RESAMPLE_Q=4; 210 | if(SAMPLERATE < 1) SAMPLERATE = SR; else if (SAMPLERATE > 192000) SAMPLERATE=192000; 211 | 212 | if(GetPrivateProfileString("IN_OPUS", "PRE_GAIN", "666.0", rez, 255, ini_name)){ 213 | float tmpfloatgain; 214 | sscanf(rez, "%f", &tmpfloatgain); 215 | if(tmpfloatgain < 665.0F) PRE_GAIN = lrintf(tmpfloatgain*256.F); 216 | } 217 | if(GetPrivateProfileString("IN_OPUS", "RADIO_GAIN", "666.0", rez, 255, ini_name)){ 218 | float tmpfloatgain; 219 | sscanf(rez, "%f", &tmpfloatgain); 220 | if (tmpfloatgain < 665.0F) RADIO_GAIN = lrintf(tmpfloatgain*256.F); 221 | } 222 | 223 | ///// CUSTOM FONTS ///// 224 | if(GetPrivateProfileString("IN_OPUS", "TAGS_FONT", "", rez, 255, ini_name)){ 225 | sscanf(rez, "%d %[^\n]s", font_height, font_str); 226 | } 227 | } 228 | ///////////////////////////////////////////////////////////////////////////// 229 | // 230 | static HFONT applyglobalfont(char *font_str, int font_height) 231 | { 232 | HDC hdc; 233 | long lfHeight; 234 | HFONT tags_font=NULL; // To set the font 235 | 236 | if(font_str == NULL || font_height == 0 || *font_str == '\0'){ 237 | if(VERBOSE)MessageBox(NULL,"No font substitution","in_opus",MB_OK); 238 | tags_font = NULL; 239 | } else { 240 | hdc = GetDC(NULL); 241 | lfHeight = -MulDiv(font_height, GetDeviceCaps(hdc, LOGPIXELSY), 72); 242 | ReleaseDC(NULL, hdc); 243 | tags_font = CreateFont(lfHeight,0,0,0,0,FALSE,0,0,0,0,0,0,0,font_str); 244 | if(VERBOSE)MessageBox(NULL,font_str,"in_opus: using font",MB_OK); 245 | } 246 | 247 | return tags_font; 248 | } 249 | ///////////////////////////////////////////////////////////////////////////// 250 | // We need config to be read already. 251 | void applyglobal_unicode_fn(char *ini_name, int ininamelength, char *rez) 252 | { 253 | if(UNICODE_FILE == 2 && isNT){ // Auto mode and WinNT 254 | FILE *Whatsnew=NULL; 255 | float version=0; 256 | char *p; 257 | 258 | p = ini_name + ininamelength; 259 | while (p >= ini_name && *p != '\\' && *p != '/') p--; 260 | ++p; 261 | size_t remaining = ini_name + MAX_PATH - p; 262 | lstrcpy_sA(p, remaining, "wacup.exe"); 263 | if(INVALID_FILE_ATTRIBUTES != GetFileAttributes(ini_name)){ 264 | if(VERBOSE)MessageBox(mod.hMainWindow,"You are using WACUP dude!","in_opus",MB_OK); 265 | return; 266 | } 267 | lstrcpy_sA(p, remaining, "MediaMonkey.exe"); 268 | if(INVALID_FILE_ATTRIBUTES != GetFileAttributes(ini_name)){ 269 | if(VERBOSE)MessageBox(mod.hMainWindow,"You are using WACUP dude!","in_opus",MB_OK); 270 | UNICODE_FILE=3; // WE ARE USING MEDIA MONKEY 271 | return; 272 | } 273 | lstrcpy_sA(p, remaining, "whatsnew.txt"); 274 | // NOW ini_name contains the path towards "whatsnew.txt" 275 | if(INVALID_FILE_ATTRIBUTES != GetFileAttributes(ini_name)) 276 | Whatsnew=fopen(ini_name, "r"); 277 | if(Whatsnew){ 278 | if(fgets(rez, 255, Whatsnew)){ 279 | sscanf(rez, "Winamp %f", &version); 280 | if(VERBOSE){ 281 | sprintf(rez, "You are using winamp version %f", version); 282 | MessageBox(mod.hMainWindow, rez, "in_opus", MB_OK); 283 | } 284 | } 285 | if(version > 5.295) UNICODE_FILE=2; // if Auto and WinNT and Winamp 5.3+ 286 | else UNICODE_FILE=0; // if Winamp 5.29- 287 | }else{ 288 | if(VERBOSE) MessageBox(mod.hMainWindow,"We could not determine WINAMP's version!","in_opus",MB_OK); 289 | UNICODE_FILE=0; // if we could not find the "whatsnew.txt" 290 | } 291 | if(Whatsnew) fclose(Whatsnew); 292 | } else if(UNICODE_FILE == 2) { // In AUTO mode and not NT. 293 | if(VERBOSE) MessageBox(mod.hMainWindow,"You are using Windows 9x\n" 294 | "No UNICODE support dude!","in_opus",MB_OK); 295 | UNICODE_FILE=0; // if we are not under NT based OS 296 | } 297 | // Here if UNICODE_FILE=1 it means it was before. 298 | // if UNICODE_FILE=0 it means that it was set to 0 befort or that the system does 299 | // not meet the requirements. 300 | // Finally if UNICODE_FILE=2 it means that Auto mode was enabled requirements are met. 301 | } 302 | ///////////////////////////////////////////////////////////////////////////// 303 | // Initialisation called at startup... 304 | void init() 305 | { 306 | HOURS_MODE_ON=0; 307 | char ini_name[MAX_PATH], rez[256], font_str[LF_FACESIZE]; 308 | int font_height=0; 309 | int winamp_version; 310 | ini_name[0] = rez[0] = font_str[0] = '\0'; 311 | 312 | VIS_BROKEN=1; 313 | winamp_version = SendMessage(mod.hMainWindow, WM_WA_IPC, 0, IPC_GETVERSION); 314 | 315 | if(winamp_version >= 0x5011){ 316 | if(winamp_version >= 0x5012) VIS_BROKEN=0; // Winamp 5.12 allows 24/32b visualisation. 317 | // this gets the string of the full ini file path 318 | char *newini = (char *)SendMessage(mod.hMainWindow, WM_WA_IPC, 0, IPC_GETINIFILE); 319 | if(!newini) return; 320 | lstrcpy_sA(ini_name, countof(ini_name), newini); 321 | 322 | // Lastfn here contains the winamp.ini path, we read the file only if 323 | // we have not read it already. 324 | if(_stricmp(lastfn, ini_name)){ 325 | if(VERBOSE)MessageBox(NULL, ini_name,"in_opus: Winamp ini_name bis",MB_OK); 326 | readconfig(ini_name, rez, font_str, &font_height); //winamp.ini 327 | applyglobalfont(font_str, font_height); 328 | } 329 | } 330 | } 331 | 332 | char *ffilestart(const char *str) 333 | { 334 | const char *p = strrchr(str, '\\'); 335 | if (!p) p = strrchr(str, '/'); 336 | return (char *)p; 337 | } 338 | 339 | ///////////////////////////////////////////////////////////////////////////// 340 | // Initialisation called before winamp loads the module. 341 | static void first_init(char *Fstr, int *Fnth) 342 | { 343 | if(VERBOSE)MessageBox(NULL,"first_init start","in_opus",MB_OK); 344 | 345 | int font_height=0; 346 | 347 | char ini_name[MAX_PATH], rez[MAX_PATH], font_str[MAX_PATH]; 348 | int ininamelength; 349 | char *p; 350 | ini_name[0] = rez[0] = font_str[0] = '\0'; 351 | 352 | isNT = !(GetVersion() & 0x80000000); /* To Use Unicode stuff on NT */ 353 | 354 | readinternalconfig(font_str, &font_height); 355 | 356 | //// Find in_opus.ini in plugin folder //// 357 | GetModuleFileName(NULL, ini_name, countof(ini_name)); 358 | ininamelength = strlen(ini_name); 359 | p = ffilestart(ini_name); 360 | if (!p) p = ini_name; 361 | *p = '\0'; 362 | lstrcat_sA(p, countof(ini_name), "\\Plugins\\in_opus.ini"); 363 | if(VERBOSE)MessageBox(NULL, ini_name,"in_opus: in_opus.ini path",MB_OK); 364 | 365 | readconfig(ini_name, rez, font_str, &font_height); //in_opus.ini 366 | 367 | //// Find winamp.ini IN Winamp's folder //// 368 | GetModuleFileName(NULL, ini_name, sizeof(ini_name)); 369 | ininamelength = strlen(ini_name); 370 | p = strrchr(ini_name, '.'); 371 | if (!p) p = ini_name + ininamelength; 372 | size_t remaining = ini_name + countof(ini_name) - p; 373 | lstrcpy_sA(p, remaining, ".ini"); 374 | if(VERBOSE)MessageBox(NULL, ini_name,"in_opus: Winamp.ini path",MB_OK); 375 | 376 | readconfig(ini_name, rez, font_str, &font_height); //winamp.ini 377 | 378 | TAGS_FONT = applyglobalfont(font_str, font_height); 379 | 380 | applyglobal_unicode_fn(ini_name, ininamelength, rez); 381 | if (Fstr && Fnth) { 382 | if(font_height!=0) lstrcpy_sA(Fstr, LF_FACESIZE, font_str); 383 | else lstrcpy_sA(Fstr, LF_FACESIZE, "(Not user set)"); 384 | *Fnth=font_height; 385 | } else { 386 | lstrcpy_sA(lastfn, countof(lastfn), ini_name); 387 | } 388 | } // END OF INIT 389 | ///////////////////////////////////////////////////////////////////////////// 390 | // Called when winanmp quits... 391 | void quit() 392 | { 393 | op_free(_of); // Just in case 394 | } 395 | 396 | ///////////////////////////////////////////////////////////////////////////// 397 | // Used for detecting URL streams... 398 | int isourfile(const char *const fn) 399 | { 400 | int err = -132; 401 | int ret=0; 402 | 403 | const char *ffn = fn; 404 | if (UNICODE_FILE) { 405 | char *fnUTF8 = utf16_to_utf8((const wchar_t *)fn); 406 | if (fnUTF8) 407 | ffn = fnUTF8; 408 | } 409 | 410 | const char *p=ffn; 411 | p+= strlen(p) - 4 ; 412 | if (isURL(ffn)) { 413 | 414 | if (!_strnicmp(p,".opu", 4) 415 | || !_strnicmp(--p,".opus", 5) ){ 416 | ret=1; 417 | } 418 | } else if (OGGEXT_HACK && !_strnicmp(p, ".ogg", 4)){ 419 | // Maybe an opus file with .ogg ext 420 | OggOpusFile *_tmp = op_test_file(ffn, &err); 421 | 422 | if (err == 0 && _tmp) { 423 | op_free(_tmp); 424 | //MessageBox(NULL, ".OGG OPUS file", "isourfile", MB_OK); 425 | ret = 1; // This is actually an opus file. 426 | } else { 427 | //MessageBox(NULL, ".OGG NOT OPUS file", "isourfile", MB_OK); 428 | ret = 0; 429 | } 430 | } 431 | 432 | if (ffn!=fn) free((char *)ffn); 433 | 434 | return ret; 435 | } 436 | ///////////////////////////////////////////////////////////////////////////// 437 | // This is for the current file only. We could add more features like 438 | // handelig of peak tag info, 439 | static void apply_replaygain(void) 440 | { 441 | int lufs_offset; 442 | 443 | // We apply the gain to reach desired LUFs only if RG is enabled 444 | // AND RG info is availabes in file 445 | if(USE_REPLAY_GAIN) lufs_offset = (isRGavailable(_of))? (TARGET_LUFS+23)*256 : 0; 446 | else lufs_offset = 0; 447 | 448 | switch (USE_REPLAY_GAIN) { 449 | case 1: OP_CURRENT_GAIN = OP_ALBUM_GAIN; break; 450 | case 2: OP_CURRENT_GAIN = OP_TRACK_GAIN; break; 451 | case 3: OP_CURRENT_GAIN = SendMessage(mod.hMainWindow, WM_WA_IPC, 0, IPC_GET_SHUFFLE) 452 | ? OP_TRACK_GAIN 453 | : OP_ALBUM_GAIN; 454 | break; 455 | // NO LUFS in case of absolute gain 456 | case 4: OP_CURRENT_GAIN = OP_ABSOLUTE_GAIN; lufs_offset = 0; break; 457 | 458 | // By default we use only header gain 459 | default: OP_CURRENT_GAIN = OP_HEADER_GAIN; 460 | } 461 | op_set_gain_offset(_of, OP_CURRENT_GAIN, lufs_offset+(RADIO? RADIO_GAIN: PRE_GAIN)); 462 | } 463 | void TryResolvePath(char *fn) 464 | { 465 | // Nothing to do on Windows 9x 466 | // Or in unicode mode 467 | if (!isNT || UNICODE_FILE || isURL(fn)) return; 468 | if (strchr(fn, '?')) { 469 | // We got an invalid full path name 470 | // Try to resolve the short path name 471 | WIN32_FIND_DATA finddat, finddat2; 472 | HANDLE f1 = FindFirstFile(fn, &finddat); 473 | if (f1 != INVALID_HANDLE_VALUE) { 474 | // We found only one file that can match the pattern 475 | if (!FindNextFile(f1, &finddat2)) { 476 | char *p = ffilestart(fn); 477 | if (p) { 478 | lstrcpy_sA(p+1, fn + MAX_PATH - p - 1, finddat.cAlternateFileName); 479 | return; // Sucess! 480 | } 481 | } 482 | FindClose(f1); 483 | } 484 | } 485 | if (INVALID_FILE_ATTRIBUTES != GetFileAttributes(fn)) 486 | return; // the file name is valid, nothing to do. 487 | // We failed it mean that we can try to list all files in the 488 | // file's directorry and match them 489 | char *lastbks = ffilestart(fn); 490 | if (!lastbks) return; 491 | 492 | // Get ANSI file name only. 493 | char * const filenameA = lastbks+1; 494 | // get file extension 495 | const char * const extA = strrchr(filenameA, '.'); 496 | if (!extA) return; // no extension, we got a problem! 497 | 498 | { // Check if path exist 499 | char oldlastbs = *lastbks; 500 | *lastbks = '\0'; // get fn = path only. 501 | DWORD attrib = GetFileAttributes(fn); 502 | *lastbks = oldlastbs; // restore fn 503 | // Path does not exist, we stop here. 504 | if (attrib == 0xFFFFFFFF || !(attrib&FILE_ATTRIBUTE_DIRECTORY)) return; 505 | } 506 | 507 | // Generate unicode path name (path only). 508 | wchar_t wfnsearch[MAX_PATH]; 509 | wchar_t extW[8]; 510 | int ret = MultiByteToWideChar(CP_ACP, 0, extA, -1, extW, 8); 511 | if (!ret) return; 512 | ret = MultiByteToWideChar(CP_ACP, 0, fn, -1, wfnsearch, MAX_PATH); 513 | if (!ret) return; 514 | 515 | // Find last backslash 516 | wchar_t *lastbsW = wcsrchr(wfnsearch, L'\\'); 517 | if (!lastbsW) lastbsW = wcsrchr(wfnsearch, L'/'); 518 | if (!lastbsW) return; 519 | 520 | // We must look for files in the form "x:\path\to\file\*.ext" 521 | lastbsW[1] = '\0'; // Null terminate the path after the backslash 522 | lstrcat_sW(wfnsearch, countof(wfnsearch), L"*"); 523 | lstrcat_sW(wfnsearch, countof(wfnsearch), extW); 524 | // here wfnsearch is in the x:\path\to\file\*.ext form. 525 | 526 | unsigned matches=0; 527 | char foundfnShortA[16]=""; // short file name... 528 | WIN32_FIND_DATAW finddat; 529 | HANDLE f1 = FindFirstFileW(wfnsearch, &finddat); 530 | if (f1) { 531 | do { 532 | //finddat.cAlternateFileName 533 | //MessageBoxW(NULL, finddat.cFileName, finddat.cAlternateFileName, 0); 534 | 535 | // filenameA: input ansi file name 536 | char foundfilenameA[MAX_PATH]; 537 | WideCharToMultiByte(CP_ACP, 0, finddat.cFileName, -1, foundfilenameA, MAX_PATH, NULL, NULL); 538 | if (!strcmp(filenameA, foundfilenameA) 539 | && finddat.cAlternateFileName[0]) { 540 | // Copy short name in foundfnShortA. 541 | WideCharToMultiByte(CP_ACP, 0, finddat.cAlternateFileName, -1, foundfnShortA, 16, NULL, NULL); 542 | matches++; 543 | } 544 | 545 | } while (FindNextFileW(f1, &finddat)); 546 | FindClose(f1); 547 | 548 | if (matches == 1) { 549 | // If we only found a single match! 550 | filenameA[0] = '\0'; 551 | lstrcpy_sA(filenameA, fn + MAX_PATH - filenameA , foundfnShortA); 552 | } 553 | } 554 | } 555 | static OggOpusFile *op_open_file_TR(char *path, int *err) 556 | { 557 | TryResolvePath(path); 558 | return op_open_file(path, err); 559 | } 560 | ///////////////////////////////////////////////////////////////////////////// 561 | // Called when winamp wants to play a file 562 | int play(char *fn) 563 | { 564 | //MessageBox(NULL, "Play", NULL, 0); 565 | if (_of) MessageBox(NULL, "File already opened!", "in_opus error", 0); 566 | int maxlatency, err; 567 | DWORD thread_id; 568 | char *p; 569 | paused=0; 570 | decode_pos_ms=0; 571 | seek_needed=-1; 572 | 573 | // Convert file name in UTF8 if needed. 574 | char *ffn=fn; 575 | if (UNICODE_FILE) { 576 | char *fnUTF8 = utf16_to_utf8((wchar_t *)fn); 577 | if(!fnUTF8) 578 | return 1; 579 | ffn = fnUTF8; 580 | } 581 | if(VERBOSE) MessageBox(mod.hMainWindow, ffn 582 | , "in_opus: play function, going to OPEN FILE",MB_OK); 583 | if (isURL(ffn)) { 584 | RADIO = 1; 585 | mod.is_seekable = 0; 586 | 587 | if(VERBOSE) MessageBox(mod.hMainWindow, fn , "in_opus: This is an URL",MB_OK); 588 | p = ffn; 589 | p += strlen(p) - 6; 590 | if(!_strnicmp(p,">.opus", 6)) *p ='\0'; 591 | _of = op_open_url(ffn, &err, NULL); 592 | 593 | } else { 594 | _of = op_open_file_TR(ffn, &err); 595 | RADIO = 0; 596 | mod.is_seekable = 1; 597 | } 598 | if (_of == NULL || err) { // error opening file 599 | if (VERBOSE) MessageBox(mod.hMainWindow, ffn, "in_opus: unable to open file",MB_OK); 600 | // we return error. 1 means to keep going in the playlist, -1 601 | // means to stop the playlist. 602 | if (RADIO) lstrcpy_sA(lastfn, countof(lastfn), ffn); 603 | 604 | if (ffn!=fn) free(ffn); 605 | return RADIO? 0 : 1; 606 | } 607 | 608 | lstrcpy_sA(lastfn, countof(lastfn), ffn); 609 | if (ffn!=fn) free(ffn); 610 | 611 | // -1 and -1 are to specify buffer and prebuffer lengths. -1 means 612 | // to use the default, which all input plug-ins should really do. 613 | maxlatency = mod.outMod->Open(SAMPLERATE,NCH,BPS, -1,-1); 614 | 615 | // maxlatency is the maxium latency between a outMod->Write() call and 616 | // when you hear those samples. In ms. Used primarily by the visualization 617 | // system if < 0 means error opening device. 618 | if (maxlatency < 0) { 619 | if(VERBOSE) MessageBox(mod.hMainWindow,"Unable to open devide (maxlatency < 0)" 620 | , "in_opus error:",MB_OK); 621 | op_free(_of); _of=NULL; 622 | return 1; 623 | } 624 | // initialize visualization stuff 625 | mod.SAVSAInit(maxlatency,SAMPLERATE); 626 | mod.VSASetInfo(SAMPLERATE,NCH); 627 | 628 | // Set Dithering and RG 629 | op_set_dither_enabled(_of, USE_DITHERING); 630 | apply_replaygain(); 631 | 632 | // set the output plug-ins default volume. 633 | // volume is 0-255, -666 is a token for current volume. 634 | setvolume(-666); // mod.outMod->SetVolume(-666); 635 | 636 | // LAUNCH DECODE THREAD 637 | if(VERBOSE >= 2) MessageBox(NULL,"Going to Lunch DecodeThread", "in_opus", MB_OK); 638 | killDecodeThread=0; 639 | thread_handle = (HANDLE) CreateThread(NULL, 0, DecodeThread, NULL, 0, &thread_id); 640 | SetThreadPriority (thread_handle, THREAD_PRIORITY); 641 | 642 | return 0; 643 | } // END OF play() 644 | 645 | ///////////////////////////////////////////////////////////////////////////// 646 | // Standard pause implementation 647 | void pause() { paused=1; mod.outMod->Pause(1); } 648 | void unpause() { paused=0; mod.outMod->Pause(0); } 649 | int ispaused() { return paused; } 650 | 651 | ///////////////////////////////////////////////////////////////////////////// 652 | // Stop playing. 653 | void stop() 654 | { 655 | //MessageBox(NULL, "Stop", NULL, 0); 656 | if (thread_handle != INVALID_HANDLE_VALUE) { 657 | killDecodeThread=1; 658 | if (WaitForSingleObject(thread_handle, 5000) == WAIT_TIMEOUT){ // 5 sec 659 | MessageBox(mod.hMainWindow 660 | ,"Error, DecodeThread not responding...\nKilling decode thread!" 661 | ,"in_opus: error", 0); 662 | TerminateThread(thread_handle, 0); 663 | } 664 | CloseHandle(thread_handle); 665 | thread_handle = INVALID_HANDLE_VALUE; 666 | } 667 | 668 | // close output system 669 | mod.outMod->Close(); 670 | 671 | // deinitialize visualization 672 | mod.SAVSADeInit(); 673 | 674 | // Write your own file closing code here 675 | HOURS_MODE_ON=0; 676 | op_free(_of); 677 | _of=NULL; 678 | } // END OF stop() 679 | 680 | ///////////////////////////////////////////////////////////////////////////// 681 | // Returns length of playing track in ms 682 | int getlength() 683 | { 684 | opus_int64 length_in_ms; // -1000 means unknown 685 | 686 | length_in_ms = -1000; 687 | if(HOURS_MODE_ON!=2) HOURS_MODE_ON=0; 688 | 689 | if(!RADIO && _of) length_in_ms = op_pcm_total(_of, -1)/(opus_int64)48; 690 | 691 | if(length_in_ms > BIG_LENGTH){ // Could be INT_MAX here or 1 000 min 692 | length_in_ms = length_in_ms/BIG_LENGTH_MULT; 693 | HOURS_MODE_ON=1; 694 | } 695 | 696 | return length_in_ms; // Will be 60x smaller in hour mode 697 | } 698 | 699 | ///////////////////////////////////////////////////////////////////////////// 700 | // Returns current output position, in ms. you could just use 701 | // return mod.outMod->GetOutputTime(), but the dsp plug-ins that do tempo 702 | // changing tend to make that wrong. 703 | int getoutputtime() 704 | { 705 | if(!HOURS_MODE_ON){ 706 | return decode_pos_ms + ( mod.outMod->GetOutputTime()-mod.outMod->GetWrittenTime() ); 707 | } else { 708 | return decode_pos_ms/BIG_LENGTH_MULT; 709 | } 710 | } 711 | 712 | ///////////////////////////////////////////////////////////////////////////// 713 | // Called when the user releases the seek scroll bar. 714 | // Usually we use it to set seek_needed to the seek 715 | // point (seek_needed is -1 when no seek is needed) 716 | // and the decode thread checks seek_needed. 717 | void setoutputtime(int time_in_ms) // Or mili-minutes when in HOURS_MODE 718 | { 719 | seek_needed = HOURS_MODE_ON? ((opus_int64)time_in_ms*(opus_int64)BIG_LENGTH_MULT): time_in_ms; 720 | } 721 | 722 | ///////////////////////////////////////////////////////////////////////////// 723 | // Standard volume/pan functions 724 | void setvolume(int volume) 725 | { 726 | mod.outMod->SetVolume(volume); 727 | static int cvol; 728 | int current_pregain =RADIO? RADIO_GAIN: PRE_GAIN; 729 | if(volume != -666)cvol=volume; 730 | if(INTERNAL_VOLUME==1 || (INTERNAL_VOLUME==2 && RADIO)){ 731 | if(_of)op_set_gain_offset(_of, OP_CURRENT_GAIN, (cvol>0)? current_pregain - 20*(255-cvol): -32768); 732 | } else { 733 | mod.outMod->SetVolume(volume); 734 | } 735 | } 736 | void setpan(int pan) 737 | { 738 | mod.outMod->SetPan(pan); 739 | } 740 | 741 | ///////////////////////////////////////////////////////////////////////////// 742 | // This gets called when the use hits Alt+3 to get the file info. 743 | int infoDlg(char *fn, HWND hwnd) 744 | { 745 | TryResolvePath(fn); 746 | DoInfoBox(mod.hDllInstance, hwnd, fn); 747 | return 0; 748 | } 749 | 750 | ///////////////////////////////////////////////////////////////////////////// 751 | // Returns length of opus file track in ms or mili-minutes 752 | static int get_opus_length_auto(OggOpusFile *_tmp, char *hours_mode_on) 753 | { 754 | opus_int64 length_in_ms; // -1000 means unknown 755 | 756 | if(hours_mode_on) *hours_mode_on = 0; 757 | 758 | length_in_ms = _tmp ? op_pcm_total(_tmp, -1)/48 : -1000; 759 | 760 | if(length_in_ms > BIG_LENGTH) { // Could be INT_MAX here it is 1 000 min 761 | length_in_ms = length_in_ms/BIG_LENGTH_MULT; 762 | if(hours_mode_on) *hours_mode_on = 1; 763 | } 764 | 765 | return (int)length_in_ms; // Will be 60x smaller in hour mode 766 | } 767 | 768 | static const char *GetTTitleA(const char *fn, char *buf) 769 | { 770 | // get non-path portion of lastfn 771 | const char *p=fn+strlen(fn); 772 | while (p >= fn && *p != '\\') p--; 773 | p++; 774 | 775 | const char *ttl=p; 776 | if (buf && FORMAT_TITLE) { 777 | if( winampGetExtendedFileInfo(fn, "Artist", buf, MAX_PATH/3) 778 | && winampGetExtendedFileInfo(fn, "Title", lstrcat_sA(buf, MAX_PATH, " - "), MAX_PATH/3) ) 779 | ttl = buf; 780 | } 781 | if (!strcmp(ttl, " - ")) ttl = p; 782 | 783 | return ttl; 784 | } 785 | 786 | static void internal_getfileinfoA(char *fn, char *title, int *length_in_ms) 787 | { 788 | int err = 0; 789 | char hours_mode_on = 0; 790 | OggOpusFile *_tmp = NULL; 791 | 792 | char is_fn_url = isURL(fn); 793 | if(!is_fn_url) { 794 | _tmp = op_open_file_TR(fn, &err); 795 | } 796 | if (length_in_ms) { 797 | *length_in_ms = get_opus_length_auto(_tmp, &hours_mode_on); 798 | } 799 | if (title) { // Set the track title in *title 800 | char buf[MAX_PATH*2]; 801 | const char *ttl = GetTTitleA(fn, buf); 802 | 803 | if(err && !is_fn_url && !UNICODE_FILE) 804 | wsprintfA(title,"[in_opus err %d %.220s] %.220s",err,TranslateOpusErr(err), ttl); 805 | else if(hours_mode_on) 806 | wsprintfA(title,"%.500 [h:min]", ttl); 807 | else 808 | lstrcpy_sA(title, 512, ttl); 809 | } 810 | op_free(_tmp); 811 | } 812 | ///////////////////////////////////////////////////////////////////////////// 813 | // This is an odd function. it is used to get the title and/or 814 | // length of a track. 815 | // If filename is either NULL or of length 0, it means you should 816 | // return the info of lastfn. Otherwise, return the information 817 | // for the file in filename. 818 | // If title is NULL, no title is copied into it. 819 | // If length_in_ms is NULL, no length is copied into it. 820 | void getfileinfo(char *filename, char *title, int *length_in_ms) 821 | { 822 | 823 | if (!filename || !*filename) { // currently playing file => lastfn 824 | internal_getfileinfoA(lastfn, title, length_in_ms); 825 | } else { // some other file => filename 826 | internal_getfileinfoA(filename, title, length_in_ms); 827 | } 828 | } 829 | 830 | // fn must be UTF8! 831 | static void internal_getfileinfoW(const char* fn, wchar_t *title, int *length_in_ms) 832 | { 833 | int err=0; 834 | OggOpusFile *_tmp=NULL; 835 | char hours_mode_on=0; 836 | 837 | char is_fn_url = isURL(fn); 838 | if (!is_fn_url)_tmp = op_open_file(fn, &err); 839 | if (length_in_ms) { 840 | *length_in_ms = get_opus_length_auto(_tmp, &hours_mode_on); 841 | } 842 | if (title){ // get non-path portion of fn which is UTF8 already 843 | char *p=lastfn+strlen(lastfn); 844 | while (p >= lastfn && *p != '\\') p--; 845 | p++; 846 | wchar_t *tmpW = utf8_to_utf16(p); 847 | 848 | if(err && !is_fn_url) wsprintfW(title, L"[in_opus err %d %.220hs] %.220s", err, TranslateOpusErr(err), tmpW); 849 | else if (hours_mode_on) wsprintfW(title, L"%.500s [h:min]", tmpW); 850 | else lstrcpy_sW(title, 512, tmpW); 851 | 852 | free(tmpW); 853 | } 854 | op_free(_tmp); 855 | } 856 | 857 | ///////////////////////////////////////////////////////////////////////////// 858 | void getfileinfoW(char *filenamew, char *titlew, int *length_in_ms) 859 | { 860 | wchar_t *filename = (wchar_t *)filenamew; 861 | if (!filename || !*filename) { // currently playing file 862 | internal_getfileinfoW(lastfn, (wchar_t *)titlew, length_in_ms); 863 | } else {// some other file => filename 864 | char *fnUTF8 = utf16_to_utf8(filename); 865 | if(fnUTF8) { 866 | internal_getfileinfoW(fnUTF8, (wchar_t *)titlew, length_in_ms); 867 | free(fnUTF8); 868 | } 869 | } 870 | } 871 | 872 | ///////////////////////////////////////////////////////////////////////////// 873 | // Winamp2 visuals have problems accepting sample sizes larger than 874 | // 16 bits, so we reduce it to 8 bits if in 24 or 32 bit mode. 875 | // Function cp/pasted and modified from in_flac.c (libflac-1.2.1) 876 | static inline void do_vis(char *__restrict data, char *__restrict vis_buffer 877 | , long long pos, unsigned samples, char resolution) 878 | { 879 | char *ptr; 880 | unsigned size, count; 881 | int position; 882 | 883 | position = HOURS_MODE_ON? pos/BIG_LENGTH_MULT: pos; 884 | 885 | if(!vis_buffer) goto DEFAULT; // If we did not allocate buffer it means 886 | // that ther is no deed to reduce BPS. 887 | switch(resolution) { 888 | case 32: 889 | case 24: 890 | size = resolution / 8; 891 | count = samples; 892 | data += size - 1; 893 | ptr = vis_buffer; 894 | 895 | while(count--) { 896 | *ptr++ = data[0] ^ 0x80; 897 | data += size; 898 | } 899 | data = vis_buffer; 900 | resolution = 8; 901 | /* fall through */ 902 | case 16: 903 | case 8: 904 | default: 905 | DEFAULT: 906 | mod.SAAddPCMData (data, NCH, resolution, position); 907 | mod.VSAAddPCMData(data, NCH, resolution, position); 908 | } 909 | } 910 | 911 | ///////////////////////////////////////////////////////////////////////////// 912 | // if you CAN do EQ with your format, each data byte is 0-63 (+20db to -20db) 913 | // and preamp is the same. 914 | void eq_set(int on, char data[10], int preamp) 915 | { 916 | 917 | } 918 | 919 | ///////////////////////////////////////////////////////////////////////////// 920 | // Render 576 samples into buf. This function is only used by DecodeThread. 921 | // Note that if you adjust the size of sample_buffer, for say, 1024 922 | // sample blocks, it will still work, but some of the visualization 923 | // might not look as good as it could. Stick with 576 sample blocks 924 | // if you can, and have an additional auxiliary (overflow) buffer if 925 | // necessary... Buff size should be 576*NCH*(BPS/8) in bytes 926 | static inline int get_576_samples(char *__restrict buf, float *__restrict Ibuff) 927 | { 928 | int l; 929 | 930 | // op_read_stereo always output 16b 48kHz stereo output, the return 931 | // value is the number of samples per channel. 932 | if(BPS!=16){ 933 | l = op_read_float_stereo(_of, Ibuff, DECODE_BUFF_SIZE); 934 | float2int_dither(buf, Ibuff, l*NCH, BPS, USE_DITHERING); 935 | }else{ 936 | l = op_read_stereo(_of, (opus_int16*) buf, DECODE_BUFF_SIZE); // (2ch * 60ms at 48KHz = 11520) 937 | } 938 | 939 | return NCH*(BPS/8)*l; // = Number of bytes 940 | } 941 | ///////////////////////////////////////////////////////////////////////////// 942 | // Alternate version that down sample to SAMPLERATE. 943 | static inline int get_samples44( 944 | char *__restrict buf, 945 | float *__restrict Ibuff, float *__restrict Obuff, 946 | SpeexResamplerState *StResampler, 947 | spx_uint32_t max_out_samples) 948 | { 949 | spx_uint32_t l, lout; 950 | lout = max_out_samples; 951 | 952 | l = op_read_float_stereo(_of, Ibuff, DECODE_BUFF_SIZE); // (2ch * 60ms at 48KHz = 11520) 953 | 954 | int lbck=l; 955 | int err = speex_resampler_process_interleaved_float(StResampler, 956 | Ibuff, &l, 957 | Obuff, &lout); 958 | float2int_dither(buf, Obuff, lout*NCH, BPS, USE_DITHERING); 959 | 960 | if (VERBOSE >= 3){ 961 | char tmp[128]; 962 | sprintf(tmp, "We are after resampling, in=%d/%d, out=%d/%d\nERROR: %d" 963 | , l, lbck, lout, max_out_samples, err); 964 | MessageBoxA(NULL,tmp, "in_opus", MB_OK); 965 | } 966 | 967 | return NCH*(BPS/8)*lout; // number of bytes 968 | } 969 | 970 | ///////////////////////////////////////////////////////////////////////////// 971 | DWORD WINAPI DecodeThread(LPVOID b) 972 | { 973 | float *Ibuff=NULL, *Obuff=NULL; 974 | char *sample_buffer=NULL; 975 | char *vis_buffer=NULL; 976 | SpeexResamplerState *StResampler=NULL; 977 | int err, l, max_out_length; 978 | int done=0, avbitrate=0; // set to TRUE if decoding has finished 979 | 980 | if(VERBOSE >= 3) MessageBox(NULL,"Decode Thread Start", "in_opus", MB_OK); 981 | 982 | max_out_length = (DECODE_BUFF_SIZE*(BPS/8)*SAMPLERATE)/SR; // in byte! not samples 983 | sample_buffer = malloc(max_out_length); 984 | if(!sample_buffer) goto QUIT; 985 | 986 | if(SAMPLERATE != SR){ 987 | StResampler = speex_resampler_init(NCH, SR, SAMPLERATE, RESAMPLE_Q, &err); 988 | if(VERBOSE >= 3) MessageBox(NULL,"Speex Resampler started", "in_opus", MB_OK); 989 | Ibuff= malloc( (DECODE_BUFF_SIZE)*sizeof(float)); 990 | Obuff= malloc( ( (DECODE_BUFF_SIZE)*sizeof(float)*SAMPLERATE )/SR); 991 | if(!StResampler || !Ibuff || !Obuff) goto QUIT; 992 | } else if(BPS!=16) { 993 | Ibuff= malloc( (DECODE_BUFF_SIZE)*sizeof(float)); 994 | } 995 | if(VIS_BROKEN && (BPS==24 || BPS==32)) vis_buffer=malloc( (DECODE_BUFF_SIZE*SAMPLERATE)/SR ); //always 8b 996 | 997 | if(!INSTANT_BR) avbitrate = op_bitrate(_of, -1)/1000; 998 | 999 | mod.SetInfo(avbitrate, SAMPLERATE/1000, NCH, 1); 1000 | 1001 | while (!killDecodeThread) { 1002 | if(VERBOSE >= 3) MessageBox(NULL,"Decode Thread not dead", "in_opus", MB_OK); 1003 | 1004 | if (seek_needed != -1){ // seek is needed. 1005 | if(VERBOSE >= 2) MessageBox(NULL,"Going to seek in file", "in_opus", MB_OK); 1006 | decode_pos_ms = seek_needed; 1007 | seek_needed=-1; 1008 | done=0; 1009 | mod.outMod->Flush(HOURS_MODE_ON?decode_pos_ms/BIG_LENGTH_MULT:decode_pos_ms); 1010 | // flush output device and set 1011 | // output position to the seek position 1012 | op_pcm_seek (_of, decode_pos_ms*48); //sample to seek to. 1013 | } 1014 | 1015 | if (done){ // done was set to TRUE during decoding, signaling eof 1016 | mod.outMod->CanWrite(); // some output drivers need CanWrite 1017 | // to be called on a regular basis. 1018 | if (!mod.outMod->IsPlaying()) { 1019 | // we're done playing, so tell Winamp and quit the thread. 1020 | PostMessage(mod.hMainWindow, WM_WA_MPEG_EOF, 0, 0); 1021 | goto QUIT; // quit thread 1022 | } 1023 | Sleep(10); // give a little CPU time back to the system. 1024 | 1025 | } else if (mod.outMod->CanWrite() >= ((max_out_length)*(mod.dsp_isactive()?2:1))) 1026 | // CanWrite() returns the number of bytes you can write, so we check that 1027 | // to the block size. the reason we multiply the block size by two if 1028 | // mod.dsp_isactive() is that DSP plug-ins can change it by up to a 1029 | // factor of two (for tempo adjustment). 1030 | { 1031 | if(VERBOSE >= 3) MessageBox(NULL,"Going to get samples", "in_opus", MB_OK); 1032 | if (StResampler) { 1033 | l = get_samples44(sample_buffer, Ibuff, Obuff, StResampler, max_out_length/(NCH*(BPS/8))); 1034 | } else { 1035 | l = get_576_samples(sample_buffer, Ibuff); 1036 | } 1037 | 1038 | if (!l) { // no samples means we're at eof 1039 | if (VERBOSE >= 2) MessageBox(NULL,"No samples found, we are at OEF", "in_opus", MB_OK); 1040 | done = 1; 1041 | } else { // we got samples! 1042 | if (VERBOSE >= 3) MessageBox(NULL,"We got samples", "in_opus", MB_OK); 1043 | 1044 | do_vis(sample_buffer, vis_buffer, decode_pos_ms, max_out_length/(BPS/8), BPS); 1045 | 1046 | // adjust decode position variable 1047 | decode_pos_ms+=(l*500)/(SAMPLERATE*(BPS/8)); 1048 | 1049 | // if we have a DSP plug-in, then call it on our samples 1050 | if (mod.dsp_isactive()) { 1051 | 1052 | l=mod.dsp_dosamples( (short *)sample_buffer // dsp_dosamples 1053 | , l/(NCH*(BPS/8)), BPS, NCH 1054 | , SAMPLERATE) *(NCH*(BPS/8)); 1055 | } 1056 | if (VERBOSE >= 3) MessageBox(NULL,"going to write pcm data to the output system" 1057 | , "in_opus", MB_OK); 1058 | 1059 | // write the pcm data to the output system 1060 | mod.outMod->Write(sample_buffer, l); 1061 | 1062 | // Write informations in the winamp windows 1063 | // dividing by 1000 for the first parameter of setinfo makes it 1064 | // display 'H'... for hundred.. i.e. 14H Kbps. 1065 | if(INSTANT_BR) { 1066 | int br=op_bitrate_instant(_of)/1000; 1067 | mod.SetInfo((br> 0)? br:-1,-1,-1,-1); 1068 | } 1069 | } 1070 | } else { 1071 | // if we can't write data, wait a little bit. Otherwise, continue 1072 | // through the loop writing more data (without sleeping) 1073 | Sleep(20); 1074 | if(VERBOSE >= 3) MessageBox(NULL,"Unable to write samples,\n Sleeping 20 ms" 1075 | , "in_opus", MB_OK); 1076 | } 1077 | if(decode_pos_ms > BIG_LENGTH) HOURS_MODE_ON=2; 1078 | } // END of While !Kill decode thread 1079 | 1080 | QUIT: 1081 | if (StResampler) { speex_resampler_destroy(StResampler); StResampler=NULL; } 1082 | free(Obuff); 1083 | free(Ibuff); 1084 | free(sample_buffer); 1085 | free(vis_buffer); 1086 | 1087 | return 0; 1088 | } 1089 | 1090 | ///////////////////////////////////////////////////////////////////////////// 1091 | // module definition. 1092 | static In_Module mod = 1093 | { 1094 | IN_VER, // defined in IN2.H 1095 | "OPUS Player v0.914 by Raymond", 1096 | 0, // hMainWindow (filled in by winamp) 1097 | 0, // hDllInstance (filled in by winamp) 1098 | "OPUS\0OPUS Audio File (*.OPUS)\0OPU\0Opus Audio File (*.OPU)\0", 1099 | // this is a double-null limited list. "EXT\0Description\0EXT\0Description\0" etc. 1100 | 1, // is_seekable 1101 | 1, // uses output plug-in system 1102 | config, 1103 | about, 1104 | init, 1105 | quit, 1106 | getfileinfo, 1107 | infoDlg, 1108 | isourfile, 1109 | play, 1110 | pause, 1111 | unpause, 1112 | ispaused, 1113 | stop, 1114 | 1115 | getlength, 1116 | getoutputtime, 1117 | setoutputtime, 1118 | 1119 | setvolume, 1120 | setpan, 1121 | 1122 | 0,0,0,0,0,0,0,0,0, // visualization calls filled in by winamp 1123 | 1124 | 0,0, // dsp calls filled in by winamp 1125 | 1126 | eq_set, //eq_set 1127 | 1128 | NULL, // setinfo call filled in by winamp 1129 | 1130 | 0 // out_mod filled in by winamp 1131 | }; 1132 | ///////////////////////////////////////////////////////////////////////////// 1133 | // If we want to support unicode filenmes we need to modify these functions: 1134 | // void GetFileInfo(char *filename, char *title, int *length_in_ms)* DONE 1135 | // int infoDlg(char *fn, HWND hwnd) * DONE 1136 | // IsOurFile()* DONE 1137 | // Play() * DONE 1138 | ///////////////////////////////////////////////////////////////////////////// 1139 | // exported symbol. Returns a pointer to the output module. 1140 | __declspec( dllexport ) In_Module * winampGetInModule2() 1141 | { 1142 | first_init(NULL, NULL); 1143 | 1144 | if(isNT && UNICODE_FILE){ 1145 | if(VERBOSE) MessageBox(NULL ,"Unicode filename support enabled" ,"in_opus",MB_OK); 1146 | mod.version=(IN_VER | IN_UNICODE); 1147 | if(UNICODE_FILE != 3) mod.GetFileInfo=getfileinfoW; // Except For MediaMonkey 1148 | } 1149 | return &mod; 1150 | } 1151 | 1152 | #ifdef LOCAL_LRINTF 1153 | long __cdecl lrintf(float x) 1154 | { 1155 | long ll = 0; 1156 | asm volatile("fistpl %0" : "=m" (ll) : "t" (x) : "st"); 1157 | return ll; 1158 | } 1159 | #endif 1160 | 1161 | void *__cdecl malloc(size_t sz) { return HeapAlloc(GetProcessHeap(), 0, sz); } 1162 | void *__cdecl calloc(size_t n, size_t sz) { return HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, n*sz); } 1163 | void __cdecl free(void *x) { HeapFree(GetProcessHeap(), 0, x); } 1164 | void * __cdecl realloc(void *x, size_t sz) 1165 | { 1166 | if(!sz) { if(x)HeapFree(GetProcessHeap(), 0, x); return NULL; } 1167 | if(!x) return HeapAlloc(GetProcessHeap(), 0, sz); 1168 | return HeapReAlloc(GetProcessHeap(), 0, x, sz); 1169 | } 1170 | -------------------------------------------------------------------------------- /in_opus.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RamonUnch/in_opus/722f5373d750de5b9444fe4e644d98f842077098/in_opus.txt -------------------------------------------------------------------------------- /infobox.c: -------------------------------------------------------------------------------- 1 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 2 | * in_opus info box by Gillibert Raymond, * 3 | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "resource.h" 10 | #include "infobox.h" 11 | 12 | extern HFONT TAGS_FONT; // To set the font 13 | extern char USE_REPLAY_GAIN; // 0 : no, 1: Album, 2: Track. 14 | extern char TAGS_DISPLAY_MODE, isNT; 15 | extern char UNICODE_FILE; 16 | extern char TARGET_LUFS; 17 | extern char UNIFIED_DIALOG; 18 | 19 | #include "utf_ansi.c" 20 | 21 | /*************************************** 22 | * SHARED HELPERS * 23 | ***************************************/ 24 | void lstrcpy_sA(char *__restrict__ dest, size_t N, const char *src) 25 | { 26 | char *dmax=dest+N-1; /* keep space for a terminating NULL */ 27 | for (; dest.opus", 6)){ 234 | lstrcpy_sA(buffer, countof(buffer), p); 235 | buffer[L]='\0'; 236 | L=0; 237 | } 238 | _tmp = op_open_url(!L? buffer: p, &err,OP_GET_SERVER_INFO(_info),NULL); 239 | 240 | } else { // NOT an URL 241 | if (ffn) 242 | _tmp = op_open_file(ffn, &err); 243 | } 244 | 245 | if (!_tmp) { 246 | if (file_is_url && err == OP_ENOTFORMAT) { 247 | sprintf(buffer,"Cannot open file: \n\n%s\n\n" 248 | "Error No. -132: Not an opus stream\n" 249 | "Try renaming the stream adding ?.mp3 or ?.ogg at the end." 250 | , ffn); 251 | 252 | } else { 253 | sprintf(buffer, "Cannot open file: \n\n%s\n\n Error No. %d: %s" 254 | , ffn, err, TranslateOpusErr(err)); 255 | } 256 | MessageBox(hwnd, buffer, "OPUS Stream Error", MB_OK); 257 | 258 | goto FAIL; 259 | } 260 | free(fnUTF8); fnUTF8=NULL; 261 | 262 | 263 | /* Get Bitrate, Gain and duration info */ 264 | if (!file_is_url) { 265 | _tags = op_tags(_tmp, -1); 266 | if (!_tags) goto FAIL; 267 | 268 | /* Repaly gains. */ 269 | int tgain=0, again=0, lufs_offset=0; 270 | int retT = opus_tags_get_track_gain(_tags, &tgain); // Track Gain 271 | int retA = opus_tags_get_album_gain(_tags, &again); // Album Gain 272 | int hgain = op_get_header_gain(_tmp); // Header gain, all in 256th dB 273 | if(hgain != 0 || retT == 0 || retA == 0) 274 | lufs_offset = (23 + TARGET_LUFS)*256; 275 | 276 | /* Fill small Encoder/RG info at the bottom */ 277 | sprintf(buffer 278 | ,"Encoder: %s\nTrk %+.1f dB, Alb %+.1f dB, Hed %+.1f dB, (%s)" 279 | , opus_tags_query(_tags, "ENCODER", 0) 280 | ,((float)(tgain))*(1.F/256.F) 281 | ,((float)(again))*(1.F/256.F) 282 | ,((float)(hgain+lufs_offset))*(1.F/256.F) 283 | ,GetRGmode(USE_REPLAY_GAIN) ); 284 | 285 | SetDlgItemText(hwnd, IDC_ENCODER, buffer); 286 | 287 | /* File size / duration / bps */ 288 | filesize = op_raw_total (_tmp, -1); // In Bites 289 | unsigned length = (unsigned)(op_pcm_total(_tmp, -1)/48000);// File length in seconds 290 | float bps = (float)op_bitrate(_tmp, -1); // In bits per seconds 291 | sprintf(buffer, "%I64d bytes (%d ch) for %u h %u min %u s at %.1f Kbps" 292 | , filesize, op_channel_count(_tmp, -1) ,(length/3600) 293 | , (length/60)%60, length%60, bps/1000.F); 294 | } else { 295 | sprintf(buffer, "Radio Stream: %s (%d ch) at %d kbps" 296 | , _info->server, op_channel_count(_tmp, -1) 297 | , _info->bitrate_kbps); 298 | } 299 | /* Print size / duration / bps */ 300 | SetDlgItemText(hwnd, IDC_INFO, buffer); 301 | 302 | 303 | /* OPUS TAGS */ 304 | SetFonts(hwnd, TAGS_FONT); // Setting spetial font if needed 305 | 306 | if(!file_is_url){ // Normal TAGS 307 | SetText (hwnd, IDC_TITLE, "TITLE", _tags); 308 | SetText (hwnd, IDC_ARTIST, "ARTIST", _tags); 309 | SetText (hwnd, IDC_ALBUM, "ALBUM", _tags); 310 | SetText (hwnd, IDC_COMMENT, "COMMENT", _tags); 311 | SetText (hwnd, IDC_GENRE, "GENRE", _tags); 312 | SetText (hwnd, IDC_COMPOSER, "COMPOSER", _tags); 313 | SetText (hwnd, IDC_PERFORMER, "PERFORMER", _tags); 314 | SetText (hwnd, IDC_COPYRIGHT, "COPYRIGHT", _tags); 315 | SetTextRaw (hwnd, IDC_YEAR, "DATE", _tags); 316 | SetTextRaw (hwnd, IDC_TRACK, "TRACKNUMBER", _tags); 317 | SetTextRaw (hwnd, IDC_TRACKTOTAL, "TRACKTOTAL", _tags); 318 | SetTextRaw (hwnd, IDC_URL, "PURL", _tags); 319 | SetTextML (hwnd, IDC_DESCRIPTION,"DESCRIPTION", _tags); //MultiLine 320 | } else if (_info){ // RADIO mode 321 | SetTextStr (hwnd, IDC_TITLE, _info->name); 322 | SetTextStr (hwnd, IDC_DESCRIPTION, _info->description); 323 | SetTextStr (hwnd, IDC_GENRE, _info->genre); 324 | SetTextStr (hwnd, IDC_URL, _info->url); 325 | SetTextStr (hwnd, IDC_COMMENT, _info->content_type); 326 | SetTextStr (hwnd, IDC_COPYRIGHT, (_info->is_public==1)?"Public server" 327 | : (_info->is_public==-1)?"": "Private server"); 328 | opus_server_info_clear(_info); 329 | } 330 | 331 | /* END OF TAGS */ 332 | 333 | ret=TRUE; goto FREALL; // if we are here everything is good. 334 | 335 | FAIL: ret=FALSE; 336 | 337 | FREALL: /* Free all mem and exit */ 338 | free(fnUTF8); 339 | op_free(_tmp); 340 | 341 | return ret; 342 | } 343 | 344 | static INT_PTR CALLBACK InfoProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) 345 | { 346 | switch (msg) { // Big Switch de msg 347 | 348 | /* Initialisation */ 349 | case WM_INITDIALOG: 350 | SetWindowText(hwnd, isURL((char *)lParam)? "OPUS Radio Info": "OPUS File Info"); 351 | /* init fields using our funcion */ 352 | if (!InitInfoboxInfo(hwnd, (const char*)lParam)) 353 | PostMessage(hwnd, WM_CLOSE, 0, 0); 354 | return TRUE; 355 | 356 | /* If Destroy message is sent */ 357 | case WM_DESTROY: break; 358 | 359 | /* A Command is sent */ 360 | case WM_COMMAND: 361 | switch (LOWORD(wParam)) { /* OK / Cancel */ 362 | case IDOK: 363 | /* int SUCESS = write_tags_in_file(hwnd, (const char*)lParam)); 364 | * EndDialog(hwnd, LOWORD(wParam)); 365 | * return SUCESS; */ 366 | case IDCANCEL: 367 | EndDialog(hwnd, LOWORD(wParam)); 368 | return TRUE; 369 | } break; 370 | 371 | } // END Big Switch de msg 372 | 373 | return 0; 374 | } 375 | 376 | void DoInfoBox(HINSTANCE inst, HWND hwnd, const char *filename) 377 | { 378 | int ret; 379 | 380 | if(TAGS_DISPLAY_MODE == 2 || (TAGS_DISPLAY_MODE == 3 && isNT)){ 381 | SetLastError(0); 382 | ret = DialogBoxParamW(inst, MAKEINTRESOURCEW(IDD_INFOBOX), hwnd, InfoProc, (LONG)filename); 383 | 384 | if (ret == 0 || GetLastError() == 120) { 385 | /*MessageBox(hwnd, "Error: cannot create Unicode Dialog Box,\n" 386 | "Your system does not handle 'DialogBoxParamW' function\n" 387 | "Go in wianmp.ini and set TAGS_DISPLAY_MODE=0, 1 or 3\n" 388 | "in the [IN_OPUS] section\n\n" 389 | "Disabling Unicode support for this session" 390 | , "in_opus error", MB_OK); 391 | */ 392 | TAGS_DISPLAY_MODE=1; 393 | } 394 | } 395 | 396 | if (TAGS_DISPLAY_MODE <= 1 || (TAGS_DISPLAY_MODE == 3 && !isNT)) { 397 | ret = DialogBoxParam(inst, MAKEINTRESOURCE(IDD_INFOBOX), hwnd, InfoProc, (LONG)filename); 398 | } 399 | } 400 | 401 | 402 | ///////////////////////////////////////////////////////////////////////////// 403 | // This is called for every metadata item to be saved to a file and has a unicode and ansi 404 | // version. The unicode version is the prefered version on unicode compatible Winamp clients 405 | // though the ansi function will be called if the unicode is not present or being used on an 406 | // older style client without internal unicode support for metadata. 407 | // data is the metadata tag being queried which typically consists of the id3v1 fields but also includes some 408 | // additional ones which Winamp uses for determining how to handle files or display different things to the user. 409 | // They can consist of, but are not limited to the following (and plug-in specific tags can be handled): 410 | // "track", "title", "artist", "album", "year", "comment", "genre", "length", 411 | // "type", "family", "formatinformation", "gain", "bitrate", "vbr", "stereo" and more 412 | int winampGetExtendedFileInfo_utf8(const char *fn, const char *data, char *dest, size_t destlen) 413 | { 414 | const char *z = NULL; 415 | int err=0; 416 | const OpusTags *_tags=NULL; 417 | OggOpusFile *_of; 418 | 419 | 420 | //MessageBoxA(NULL, data, fn, 0); 421 | if (!_stricmp(data, "type")) { 422 | dest[0] = '0'; // audio format 423 | return 1; 424 | 425 | } else if (!_stricmp(data, "family")) { 426 | // strcpy(dest, "Ogg/Opus Audio File"); 427 | return 0; 428 | 429 | } else if (!_stricmp(data, "length")) { 430 | return 0; 431 | 432 | } 433 | // opening file 434 | _of = op_open_file(fn, &err); 435 | if(_of && !err){ // if file opened correctly, open tags. 436 | _tags = op_tags(_of, -1); 437 | } else if (err != -132 || has_opus_ext(fn)) { // if the file could not be opened and is opus. 438 | op_free(_of); // free it in case. 439 | if(!_stricmp(data, "title")) { 440 | // send error message... 441 | const char *p; 442 | if ( (p = ffilestart(fn)) ) p++; else p = (char *)fn; 443 | 444 | sprintf(dest, "[in_opus err %d %s] %s" 445 | , err, TranslateOpusErr(err), p); 446 | return 1; 447 | } else { 448 | return 0; 449 | } 450 | } 451 | // if file opened correctly 452 | if(!_tags) goto fail; 453 | 454 | // FROM HERE TAGS HAS TO BE OK! 455 | 456 | if (!_stricmp(data, "bitrate")) { 457 | sprintf(dest, "%1.f", (float)op_bitrate(_of, -1)); 458 | goto finish; 459 | 460 | } else if (!_stricmp(data, "formatinformation")) { 461 | sprintf(dest, 462 | "Size: %I64d bites\n" 463 | "Length: %I64d s\n" 464 | "Bitrate: %.2f kbps\n" 465 | "Channels: %d\n" 466 | "Header gain: %.2f dB\n" 467 | "Input samplerate: %d Hz\n" 468 | "Encoder: %s\n" 469 | , op_raw_total(_of, -1) 470 | , op_pcm_total(_of, -1)/48000 471 | , (float)op_bitrate(_of, -1)/1000.F 472 | , op_channel_count(_of, -1) 473 | , (float)(op_get_header_gain(_of)/256.F) 474 | , op_get_input_sample_rate(_of) 475 | , opus_tags_query(_tags, "ENCODER", 0) 476 | ); 477 | 478 | goto finish; 479 | 480 | } else if (!_stricmp(data, "replaygain_track_gain")) { 481 | int tgain=0; 482 | opus_tags_get_track_gain(_tags, &tgain); 483 | sprintf(dest, "%.2f", (float)tgain/256.f); 484 | goto finish; 485 | 486 | } else if (!_stricmp(data, "replaygain_album_gain")) { 487 | int again=0; 488 | opus_tags_get_album_gain (_tags, &again); 489 | sprintf(dest, "%.2f", (float)again/256.f); 490 | goto finish; 491 | 492 | } else if (!_stricmp(data, "comment")) { // convert to DOS 493 | char *dos; 494 | z = opus_tags_query(_tags, "DESCRIPTION", 0); 495 | dos = unix2dos(z); 496 | if(!dos) goto fail; 497 | lstrcpy_sA(dest, destlen-1, dos); 498 | free(dos); 499 | goto finish; 500 | 501 | } else if (!_stricmp(data, "year")) { 502 | z = opus_tags_query(_tags, "DATE", 0); 503 | } else if (!_stricmp(data, "track")) { 504 | z = opus_tags_query(_tags, "TRACKNUMBER", 0); 505 | } else if (!_stricmp(data, "disc")) { 506 | z = opus_tags_query(_tags, "DISCNUMBER", 0); 507 | } else { 508 | z = opus_tags_query(_tags, data, 0); 509 | } 510 | if (z) { 511 | lstrcpy_sA(dest, destlen-1, z); 512 | goto finish; 513 | } 514 | fail: 515 | op_free(_of); 516 | return 0; 517 | 518 | finish: // sucess! 519 | op_free(_of); 520 | return 1; 521 | } 522 | 523 | __declspec(dllexport) int winampGetExtendedFileInfo(const char *fn, const char *data, char *dest, size_t destlen) 524 | { 525 | char *dest_utf8, *dest_ansi; 526 | int ret = 0; 527 | 528 | dest_utf8=calloc(destlen, sizeof(char)); if(!dest_utf8) return 0; 529 | 530 | if (winampGetExtendedFileInfo_utf8(fn, data, dest_utf8, destlen)) { 531 | dest_ansi = utf8_to_ansi(dest_utf8); if (!dest_ansi) goto fail; 532 | dest[0] = '\0'; 533 | lstrcpy_sA(dest, destlen-1, dest_ansi); 534 | free(dest_ansi); 535 | ret = 1; 536 | } else { 537 | ret = 0; 538 | } 539 | fail: 540 | free(dest_utf8); 541 | return ret; 542 | } 543 | 544 | __declspec(dllexport) int winampGetExtendedFileInfoW(const wchar_t *fn, const char *data, wchar_t *dest, size_t destlen) 545 | { 546 | char *dest_utf8 = NULL, *fnUTF8 = NULL; 547 | int ret =0; 548 | fnUTF8 = utf16_to_utf8(fn); if(!fnUTF8) goto fail; 549 | dest_utf8 = calloc(destlen, sizeof(char)); if(!dest_utf8) goto fail; 550 | 551 | if (winampGetExtendedFileInfo_utf8(fnUTF8, data, dest_utf8, destlen)) { 552 | wchar_t *dest_w = utf8_to_utf16(dest_utf8); if(!dest_w) goto fail; 553 | dest[0] = '\0'; 554 | lstrcpy_sW(dest, destlen-1, dest_w); 555 | free(dest_w); 556 | ret = 1; 557 | } else { 558 | ret = 0; 559 | } 560 | 561 | fail: 562 | free(dest_utf8); 563 | free(fnUTF8); 564 | 565 | return ret; 566 | } 567 | __declspec(dllexport) int winampUseUnifiedFileInfoDlg(const wchar_t *fn) 568 | { 569 | int err=-132; 570 | wchar_t *p=(wchar_t *)fn; 571 | // MessageBox(NULL,"START","winampUseUnifiedFileInfoDlg",MB_OK); 572 | 573 | if(UNIFIED_DIALOG){ 574 | p += wcslen(p); 575 | 576 | if (!_wcsnicmp(fn,L"http://", 7)){ 577 | // if URL; 578 | // MessageBox(NULL,"URL return 0","winampUseUnifiedFileInfoDlg",MB_OK); 579 | return 0; 580 | 581 | } else if(has_opus_extW(fn)) { 582 | // extension is .opus or .opu 583 | // MessageBox(NULL,"OPUS file and return 1","winampUseUnifiedFileInfoDlg",MB_OK); 584 | return 1; 585 | 586 | } else if((p[-1]=='g'&&p[-2]=='g'&&p[-3]=='o'&&p[-4]=='.')) { 587 | // extenson is .ogg 588 | char *fnUTF8; 589 | OggOpusFile *_tmp; 590 | 591 | fnUTF8 = utf16_to_utf8(fn); if(!fnUTF8) return 0; 592 | _tmp = op_test_file(fnUTF8, &err); 593 | free(fnUTF8); 594 | op_free(_tmp); 595 | 596 | if(err == 0){ 597 | // MessageBox(NULL,"OGG file and return 1","winampUseUnifiedFileInfoDlg",MB_OK); 598 | return 1; 599 | } else { 600 | // MessageBox(NULL,"OGG file and return 0","winampUseUnifiedFileInfoDlg",MB_OK); 601 | return 0; 602 | } 603 | } 604 | 605 | } else { 606 | return 0; 607 | } 608 | return 0; 609 | } 610 | __declspec(dllexport) int winampSetExtendedFileInfoW(const wchar_t *fn, const char *data, const wchar_t *val) 611 | { 612 | return 0; 613 | } 614 | __declspec(dllexport) int winampSetExtendedFileInfo(const char *fn, const char *data, const char *val) 615 | { 616 | return 0; 617 | } 618 | __declspec(dllexport) int winampWriteExtendedFileInfo() 619 | { 620 | return 0; 621 | } 622 | -------------------------------------------------------------------------------- /infobox.h: -------------------------------------------------------------------------------- 1 | #define countof(array) (sizeof(array)/sizeof(array[0])) 2 | 3 | void DoInfoBox(HINSTANCE inst, HWND hwnd, const char *filename); 4 | 5 | opus_int32 op_get_header_gain(const OggOpusFile *_of); 6 | opus_int32 op_get_input_sample_rate(const OggOpusFile *_of); 7 | const char *TranslateOpusErr(int err); 8 | 9 | char isURL(const char *const fn); 10 | char isRGavailable(const OggOpusFile *_of); 11 | char *ffilestart(const char *str); 12 | int isourfile(const char *const fn); 13 | 14 | void lstrcpy_sA(char *__restrict__ dest, size_t N, const char *src); 15 | void lstrcpy_sW(wchar_t *__restrict__ dest, size_t N, const wchar_t *src); 16 | char *lstrcat_sA(char *__restrict__ d, const size_t N, const char *__restrict__ s); 17 | wchar_t *lstrcat_sW(wchar_t *__restrict__ d, const size_t N, const wchar_t *__restrict__ s); 18 | 19 | 20 | __declspec(dllexport) int winampGetExtendedFileInfo(const char *fn, const char *data, char *dest, size_t destlen); 21 | __declspec(dllexport) int winampGetExtendedFileInfoW(const wchar_t *fn, const char *data, wchar_t *dest, size_t destlen); 22 | 23 | //int has_opus_ext(const char *p); 24 | //int has_opus_extW(const wchar_t *p); 25 | -------------------------------------------------------------------------------- /opusfile/info.c: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | * * 3 | * THIS FILE IS PART OF THE libopusfile SOFTWARE CODEC SOURCE CODE. * 4 | * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * 5 | * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * 6 | * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * 7 | * * 8 | * THE libopusfile SOURCE CODE IS (C) COPYRIGHT 2012-2020 * 9 | * by the Xiph.Org Foundation and contributors https://xiph.org/ * 10 | * * 11 | ********************************************************************/ 12 | #ifdef HAVE_CONFIG_H 13 | #include "config.h" 14 | #endif 15 | 16 | #include "internal.h" 17 | #include 18 | #include 19 | 20 | int op_strncasecmp(const char *_a,const char *_b,int _n) 21 | { 22 | int i; 23 | for(i=0;i<_n;i++){ 24 | int a; 25 | int b; 26 | int d; 27 | a=_a[i]; 28 | b=_b[i]; 29 | if(a>='a'&&a<='z')a-='a'-'A'; 30 | if(b>='a'&&b<='z')b-='a'-'A'; 31 | d=a-b; 32 | if(d)return d; 33 | } 34 | return 0; 35 | } 36 | 37 | static unsigned op_parse_uint16le(const unsigned char *_data){ 38 | return _data[0]|_data[1]<<8; 39 | } 40 | 41 | static int op_parse_int16le(const unsigned char *_data){ 42 | int ret; 43 | ret=_data[0]|_data[1]<<8; 44 | return (ret^0x8000)-0x8000; 45 | } 46 | 47 | static opus_uint32 op_parse_uint32le(const unsigned char *_data){ 48 | return _data[0]|(opus_uint32)_data[1]<<8| 49 | (opus_uint32)_data[2]<<16|(opus_uint32)_data[3]<<24; 50 | } 51 | 52 | static opus_uint32 op_parse_uint32be(const unsigned char *_data){ 53 | return _data[3]|(opus_uint32)_data[2]<<8| 54 | (opus_uint32)_data[1]<<16|(opus_uint32)_data[0]<<24; 55 | } 56 | 57 | int opus_head_parse(OpusHead *_head,const unsigned char *_data,size_t _len){ 58 | OpusHead head; 59 | if(_len<8)return OP_ENOTFORMAT; 60 | if(memcmp(_data,"OpusHead",8)!=0)return OP_ENOTFORMAT; 61 | if(_len<9)return OP_EBADHEADER; 62 | head.version=_data[8]; 63 | if(head.version>15)return OP_EVERSION; 64 | if(_len<19)return OP_EBADHEADER; 65 | head.channel_count=_data[9]; 66 | head.pre_skip=op_parse_uint16le(_data+10); 67 | head.input_sample_rate=op_parse_uint32le(_data+12); 68 | head.output_gain=op_parse_int16le(_data+16); 69 | head.mapping_family=_data[18]; 70 | if(head.mapping_family==0){ 71 | if(head.channel_count<1||head.channel_count>2)return OP_EBADHEADER; 72 | if(head.version<=1&&_len>19)return OP_EBADHEADER; 73 | head.stream_count=1; 74 | head.coupled_count=head.channel_count-1; 75 | if(_head!=NULL){ 76 | _head->mapping[0]=0; 77 | _head->mapping[1]=1; 78 | } 79 | } 80 | else if(head.mapping_family==1){ 81 | size_t size; 82 | int ci; 83 | if(head.channel_count<1||head.channel_count>8)return OP_EBADHEADER; 84 | size=21+head.channel_count; 85 | if(_lensize)return OP_EBADHEADER; 86 | head.stream_count=_data[19]; 87 | if(head.stream_count<1)return OP_EBADHEADER; 88 | head.coupled_count=_data[20]; 89 | if(head.coupled_count>head.stream_count)return OP_EBADHEADER; 90 | for(ci=0;ci=head.stream_count+head.coupled_count 92 | &&_data[21+ci]!=255){ 93 | return OP_EBADHEADER; 94 | } 95 | } 96 | if(_head!=NULL)memcpy(_head->mapping,_data+21,head.channel_count); 97 | } 98 | /*General purpose players should not attempt to play back content with 99 | channel mapping family 255.*/ 100 | else if(head.mapping_family==255)return OP_EIMPL; 101 | /*No other channel mapping families are currently defined.*/ 102 | else return OP_EBADHEADER; 103 | if(_head!=NULL)memcpy(_head,&head,head.mapping-(unsigned char *)&head); 104 | return 0; 105 | } 106 | 107 | void opus_tags_init(OpusTags *_tags){ 108 | memset(_tags,0,sizeof(*_tags)); 109 | } 110 | 111 | void opus_tags_clear(OpusTags *_tags){ 112 | int ncomments; 113 | int ci; 114 | ncomments=_tags->comments; 115 | if(_tags->user_comments!=NULL)ncomments++; 116 | else{ 117 | OP_ASSERT(ncomments==0); 118 | } 119 | for(ci=ncomments;ci-->0;)_ogg_free(_tags->user_comments[ci]); 120 | _ogg_free(_tags->user_comments); 121 | _ogg_free(_tags->comment_lengths); 122 | _ogg_free(_tags->vendor); 123 | } 124 | 125 | /*Ensure there's room for up to _ncomments comments.*/ 126 | static int op_tags_ensure_capacity(OpusTags *_tags,size_t _ncomments){ 127 | char **user_comments; 128 | int *comment_lengths; 129 | int cur_ncomments; 130 | size_t size; 131 | if(OP_UNLIKELY(_ncomments>=(size_t)INT_MAX))return OP_EFAULT; 132 | size=sizeof(*_tags->comment_lengths)*(_ncomments+1); 133 | if(size/sizeof(*_tags->comment_lengths)!=_ncomments+1)return OP_EFAULT; 134 | cur_ncomments=_tags->comments; 135 | /*We only support growing. 136 | Trimming requires cleaning up the allocated strings in the old space, and 137 | is best handled separately if it's ever needed.*/ 138 | OP_ASSERT(_ncomments>=(size_t)cur_ncomments); 139 | comment_lengths=(int *)_ogg_realloc(_tags->comment_lengths,size); 140 | if(OP_UNLIKELY(comment_lengths==NULL))return OP_EFAULT; 141 | if(_tags->comment_lengths==NULL){ 142 | OP_ASSERT(cur_ncomments==0); 143 | comment_lengths[cur_ncomments]=0; 144 | } 145 | comment_lengths[_ncomments]=comment_lengths[cur_ncomments]; 146 | _tags->comment_lengths=comment_lengths; 147 | size=sizeof(*_tags->user_comments)*(_ncomments+1); 148 | if(size/sizeof(*_tags->user_comments)!=_ncomments+1)return OP_EFAULT; 149 | user_comments=(char **)_ogg_realloc(_tags->user_comments,size); 150 | if(OP_UNLIKELY(user_comments==NULL))return OP_EFAULT; 151 | if(_tags->user_comments==NULL){ 152 | OP_ASSERT(cur_ncomments==0); 153 | user_comments[cur_ncomments]=NULL; 154 | } 155 | user_comments[_ncomments]=user_comments[cur_ncomments]; 156 | _tags->user_comments=user_comments; 157 | return 0; 158 | } 159 | 160 | /*Duplicate a (possibly non-NUL terminated) string with a known length.*/ 161 | static char *op_strdup_with_len(const char *_s,size_t _len){ 162 | size_t size; 163 | char *ret; 164 | size=sizeof(*ret)*(_len+1); 165 | if(OP_UNLIKELY(size<_len))return NULL; 166 | ret=(char *)_ogg_malloc(size); 167 | if(OP_LIKELY(ret!=NULL)){ 168 | ret=(char *)memcpy(ret,_s,sizeof(*ret)*_len); 169 | ret[_len]='\0'; 170 | } 171 | return ret; 172 | } 173 | 174 | /*The actual implementation of opus_tags_parse(). 175 | Unlike the public API, this function requires _tags to already be 176 | initialized, modifies its contents before success is guaranteed, and assumes 177 | the caller will clear it on error.*/ 178 | static int opus_tags_parse_impl(OpusTags *_tags, 179 | const unsigned char *_data,size_t _len){ 180 | opus_uint32 count; 181 | size_t len; 182 | int ncomments; 183 | int ci; 184 | len=_len; 185 | if(len<8)return OP_ENOTFORMAT; 186 | if(memcmp(_data,"OpusTags",8)!=0)return OP_ENOTFORMAT; 187 | if(len<16)return OP_EBADHEADER; 188 | _data+=8; 189 | len-=8; 190 | count=op_parse_uint32le(_data); 191 | _data+=4; 192 | len-=4; 193 | if(count>len)return OP_EBADHEADER; 194 | if(_tags!=NULL){ 195 | _tags->vendor=op_strdup_with_len((char *)_data,count); 196 | if(_tags->vendor==NULL)return OP_EFAULT; 197 | } 198 | _data+=count; 199 | len-=count; 200 | if(len<4)return OP_EBADHEADER; 201 | count=op_parse_uint32le(_data); 202 | _data+=4; 203 | len-=4; 204 | /*Check to make sure there's minimally sufficient data left in the packet.*/ 205 | if(count>len>>2)return OP_EBADHEADER; 206 | /*Check for overflow (the API limits this to an int).*/ 207 | if(count>(opus_uint32)INT_MAX-1)return OP_EFAULT; 208 | if(_tags!=NULL){ 209 | int ret; 210 | ret=op_tags_ensure_capacity(_tags,count); 211 | if(ret<0)return ret; 212 | } 213 | ncomments=(int)count; 214 | for(ci=0;cilen>>2)return OP_EBADHEADER; 217 | count=op_parse_uint32le(_data); 218 | _data+=4; 219 | len-=4; 220 | if(count>len)return OP_EBADHEADER; 221 | /*Check for overflow (the API limits this to an int).*/ 222 | if(count>(opus_uint32)INT_MAX)return OP_EFAULT; 223 | if(_tags!=NULL){ 224 | _tags->user_comments[ci]=op_strdup_with_len((char *)_data,count); 225 | if(_tags->user_comments[ci]==NULL)return OP_EFAULT; 226 | _tags->comment_lengths[ci]=(int)count; 227 | _tags->comments=ci+1; 228 | /*Needed by opus_tags_clear() if we fail before parsing the (optional) 229 | binary metadata.*/ 230 | _tags->user_comments[ci+1]=NULL; 231 | } 232 | _data+=count; 233 | len-=count; 234 | } 235 | if(len>0&&(_data[0]&1)){ 236 | if(len>(opus_uint32)INT_MAX)return OP_EFAULT; 237 | if(_tags!=NULL){ 238 | _tags->user_comments[ncomments]=(char *)_ogg_malloc(len); 239 | if(OP_UNLIKELY(_tags->user_comments[ncomments]==NULL))return OP_EFAULT; 240 | memcpy(_tags->user_comments[ncomments],_data,len); 241 | _tags->comment_lengths[ncomments]=(int)len; 242 | } 243 | } 244 | return 0; 245 | } 246 | 247 | int opus_tags_parse(OpusTags *_tags,const unsigned char *_data,size_t _len){ 248 | if(_tags!=NULL){ 249 | OpusTags tags; 250 | int ret; 251 | opus_tags_init(&tags); 252 | ret=opus_tags_parse_impl(&tags,_data,_len); 253 | if(ret<0)opus_tags_clear(&tags); 254 | else *_tags=*&tags; 255 | return ret; 256 | } 257 | else return opus_tags_parse_impl(NULL,_data,_len); 258 | } 259 | 260 | /*The actual implementation of opus_tags_copy(). 261 | Unlike the public API, this function requires _dst to already be 262 | initialized, modifies its contents before success is guaranteed, and assumes 263 | the caller will clear it on error.*/ 264 | static int opus_tags_copy_impl(OpusTags *_dst,const OpusTags *_src){ 265 | char *vendor; 266 | int ncomments; 267 | int ret; 268 | int ci; 269 | vendor=_src->vendor; 270 | _dst->vendor=op_strdup_with_len(vendor,strlen(vendor)); 271 | if(OP_UNLIKELY(_dst->vendor==NULL))return OP_EFAULT; 272 | ncomments=_src->comments; 273 | ret=op_tags_ensure_capacity(_dst,ncomments); 274 | if(OP_UNLIKELY(ret<0))return ret; 275 | for(ci=0;cicomment_lengths[ci]; 278 | OP_ASSERT(len>=0); 279 | _dst->user_comments[ci]=op_strdup_with_len(_src->user_comments[ci],len); 280 | if(OP_UNLIKELY(_dst->user_comments[ci]==NULL))return OP_EFAULT; 281 | _dst->comment_lengths[ci]=len; 282 | _dst->comments=ci+1; 283 | } 284 | if(_src->comment_lengths!=NULL){ 285 | int len; 286 | len=_src->comment_lengths[ncomments]; 287 | if(len>0){ 288 | _dst->user_comments[ncomments]=(char *)_ogg_malloc(len); 289 | if(OP_UNLIKELY(_dst->user_comments[ncomments]==NULL))return OP_EFAULT; 290 | memcpy(_dst->user_comments[ncomments],_src->user_comments[ncomments],len); 291 | _dst->comment_lengths[ncomments]=len; 292 | } 293 | } 294 | return 0; 295 | } 296 | 297 | int opus_tags_copy(OpusTags *_dst,const OpusTags *_src){ 298 | OpusTags dst; 299 | int ret; 300 | opus_tags_init(&dst); 301 | ret=opus_tags_copy_impl(&dst,_src); 302 | if(OP_UNLIKELY(ret<0))opus_tags_clear(&dst); 303 | else *_dst=*&dst; 304 | return ret; 305 | } 306 | 307 | int opus_tags_add(OpusTags *_tags,const char *_tag,const char *_value){ 308 | char *comment; 309 | size_t tag_len; 310 | size_t value_len; 311 | int ncomments; 312 | int ret; 313 | ncomments=_tags->comments; 314 | ret=op_tags_ensure_capacity(_tags,ncomments+1); 315 | if(OP_UNLIKELY(ret<0))return ret; 316 | tag_len=strlen(_tag); 317 | value_len=strlen(_value); 318 | /*+2 for '=' and '\0'.*/ 319 | if(tag_len+value_len(size_t)INT_MAX-2)return OP_EFAULT; 321 | comment=(char *)_ogg_malloc(sizeof(*comment)*(tag_len+value_len+2)); 322 | if(OP_UNLIKELY(comment==NULL))return OP_EFAULT; 323 | memcpy(comment,_tag,sizeof(*comment)*tag_len); 324 | comment[tag_len]='='; 325 | memcpy(comment+tag_len+1,_value,sizeof(*comment)*(value_len+1)); 326 | _tags->user_comments[ncomments]=comment; 327 | _tags->comment_lengths[ncomments]=(int)(tag_len+value_len+1); 328 | _tags->comments=ncomments+1; 329 | return 0; 330 | } 331 | 332 | int opus_tags_add_comment(OpusTags *_tags,const char *_comment){ 333 | char *comment; 334 | int comment_len; 335 | int ncomments; 336 | int ret; 337 | ncomments=_tags->comments; 338 | ret=op_tags_ensure_capacity(_tags,ncomments+1); 339 | if(OP_UNLIKELY(ret<0))return ret; 340 | comment_len=(int)strlen(_comment); 341 | comment=op_strdup_with_len(_comment,comment_len); 342 | if(OP_UNLIKELY(comment==NULL))return OP_EFAULT; 343 | _tags->user_comments[ncomments]=comment; 344 | _tags->comment_lengths[ncomments]=comment_len; 345 | _tags->comments=ncomments+1; 346 | return 0; 347 | } 348 | 349 | int opus_tags_set_binary_suffix(OpusTags *_tags, 350 | const unsigned char *_data,int _len){ 351 | unsigned char *binary_suffix_data; 352 | int ncomments; 353 | int ret; 354 | if(_len<0||_len>0&&(_data==NULL||!(_data[0]&1)))return OP_EINVAL; 355 | ncomments=_tags->comments; 356 | ret=op_tags_ensure_capacity(_tags,ncomments); 357 | if(OP_UNLIKELY(ret<0))return ret; 358 | binary_suffix_data= 359 | (unsigned char *)_ogg_realloc(_tags->user_comments[ncomments],_len); 360 | if(OP_UNLIKELY(binary_suffix_data==NULL))return OP_EFAULT; 361 | memcpy(binary_suffix_data,_data,_len); 362 | _tags->user_comments[ncomments]=(char *)binary_suffix_data; 363 | _tags->comment_lengths[ncomments]=_len; 364 | return 0; 365 | } 366 | 367 | int opus_tagcompare(const char *_tag_name,const char *_comment){ 368 | size_t tag_len; 369 | tag_len=strlen(_tag_name); 370 | if(OP_UNLIKELY(tag_len>(size_t)INT_MAX))return -1; 371 | return opus_tagncompare(_tag_name,(int)tag_len,_comment); 372 | } 373 | 374 | int opus_tagncompare(const char *_tag_name,int _tag_len,const char *_comment){ 375 | int ret; 376 | OP_ASSERT(_tag_len>=0); 377 | ret=op_strncasecmp(_tag_name,_comment,_tag_len); 378 | return ret?ret:'='-_comment[_tag_len]; 379 | } 380 | 381 | const char *opus_tags_query(const OpusTags *_tags,const char *_tag,int _count){ 382 | char **user_comments; 383 | size_t tag_len; 384 | int found; 385 | int ncomments; 386 | int ci; 387 | tag_len=strlen(_tag); 388 | if(OP_UNLIKELY(tag_len>(size_t)INT_MAX))return NULL; 389 | ncomments=_tags->comments; 390 | user_comments=_tags->user_comments; 391 | found=0; 392 | for(ci=0;ci(size_t)INT_MAX))return 0; 410 | ncomments=_tags->comments; 411 | user_comments=_tags->user_comments; 412 | found=0; 413 | for(ci=0;cicomments; 424 | len=_tags->comment_lengths==NULL?0:_tags->comment_lengths[ncomments]; 425 | *_len=len; 426 | OP_ASSERT(len==0||_tags->user_comments!=NULL); 427 | return len>0?(const unsigned char *)_tags->user_comments[ncomments]:NULL; 428 | } 429 | 430 | static int opus_tags_get_gain(const OpusTags *_tags,int *_gain_q8, 431 | const char *_tag_name,size_t _tag_len){ 432 | char **comments; 433 | int ncomments; 434 | int ci; 435 | comments=_tags->user_comments; 436 | ncomments=_tags->comments; 437 | /*Look for the first valid tag with the name _tag_name and use that.*/ 438 | for(ci=0;ci='0'&&*p<='9'){ 453 | gain_q8=10*gain_q8+*p-'0'; 454 | if(gain_q8>32767-negative)break; 455 | p++; 456 | } 457 | /*This didn't look like a signed 16-bit decimal integer. 458 | Not a valid gain tag.*/ 459 | if(*p!='\0')continue; 460 | *_gain_q8=(int)(gain_q8+negative^negative); 461 | return 0; 462 | } 463 | } 464 | return OP_FALSE; 465 | } 466 | 467 | int opus_tags_get_album_gain(const OpusTags *_tags,int *_gain_q8){ 468 | return opus_tags_get_gain(_tags,_gain_q8,"R128_ALBUM_GAIN",15); 469 | } 470 | 471 | int opus_tags_get_track_gain(const OpusTags *_tags,int *_gain_q8){ 472 | return opus_tags_get_gain(_tags,_gain_q8,"R128_TRACK_GAIN",15); 473 | } 474 | 475 | static int op_is_jpeg(const unsigned char *_buf,size_t _buf_sz){ 476 | return _buf_sz>=3&&memcmp(_buf,"\xFF\xD8\xFF",3)==0; 477 | } 478 | 479 | /*Tries to extract the width, height, bits per pixel, and palette size of a 480 | JPEG. 481 | On failure, simply leaves its outputs unmodified.*/ 482 | static void op_extract_jpeg_params(const unsigned char *_buf,size_t _buf_sz, 483 | opus_uint32 *_width,opus_uint32 *_height, 484 | opus_uint32 *_depth,opus_uint32 *_colors,int *_has_palette){ 485 | if(op_is_jpeg(_buf,_buf_sz)){ 486 | size_t offs; 487 | offs=2; 488 | for(;;){ 489 | size_t segment_len; 490 | int marker; 491 | while(offs<_buf_sz&&_buf[offs]!=0xFF)offs++; 492 | while(offs<_buf_sz&&_buf[offs]==0xFF)offs++; 493 | marker=_buf[offs]; 494 | offs++; 495 | /*If we hit EOI* (end of image), or another SOI* (start of image), 496 | or SOS (start of scan), then stop now.*/ 497 | if(offs>=_buf_sz||(marker>=0xD8&&marker<=0xDA))break; 498 | /*RST* (restart markers): skip (no segment length).*/ 499 | else if(marker>=0xD0&&marker<=0xD7)continue; 500 | /*Read the length of the marker segment.*/ 501 | if(_buf_sz-offs<2)break; 502 | segment_len=_buf[offs]<<8|_buf[offs+1]; 503 | if(segment_len<2||_buf_sz-offs0xC0&&marker<0xD0&&(marker&3)!=0)){ 505 | /*Found a SOFn (start of frame) marker segment:*/ 506 | if(segment_len>=8){ 507 | *_height=_buf[offs+3]<<8|_buf[offs+4]; 508 | *_width=_buf[offs+5]<<8|_buf[offs+6]; 509 | *_depth=_buf[offs+2]*_buf[offs+7]; 510 | *_colors=0; 511 | *_has_palette=0; 512 | } 513 | break; 514 | } 515 | /*Other markers: skip the whole marker segment.*/ 516 | offs+=segment_len; 517 | } 518 | } 519 | } 520 | 521 | static int op_is_png(const unsigned char *_buf,size_t _buf_sz){ 522 | return _buf_sz>=8&&memcmp(_buf,"\x89PNG\x0D\x0A\x1A\x0A",8)==0; 523 | } 524 | 525 | /*Tries to extract the width, height, bits per pixel, and palette size of a 526 | PNG. 527 | On failure, simply leaves its outputs unmodified.*/ 528 | static void op_extract_png_params(const unsigned char *_buf,size_t _buf_sz, 529 | opus_uint32 *_width,opus_uint32 *_height, 530 | opus_uint32 *_depth,opus_uint32 *_colors,int *_has_palette){ 531 | if(op_is_png(_buf,_buf_sz)){ 532 | size_t offs; 533 | offs=8; 534 | while(_buf_sz-offs>=12){ 535 | ogg_uint32_t chunk_len; 536 | chunk_len=op_parse_uint32be(_buf+offs); 537 | if(chunk_len>_buf_sz-(offs+12))break; 538 | else if(chunk_len==13&&memcmp(_buf+offs+4,"IHDR",4)==0){ 539 | int color_type; 540 | *_width=op_parse_uint32be(_buf+offs+8); 541 | *_height=op_parse_uint32be(_buf+offs+12); 542 | color_type=_buf[offs+17]; 543 | if(color_type==3){ 544 | *_depth=24; 545 | *_has_palette=1; 546 | } 547 | else{ 548 | int sample_depth; 549 | sample_depth=_buf[offs+16]; 550 | if(color_type==0)*_depth=sample_depth; 551 | else if(color_type==2)*_depth=sample_depth*3; 552 | else if(color_type==4)*_depth=sample_depth*2; 553 | else if(color_type==6)*_depth=sample_depth*4; 554 | *_colors=0; 555 | *_has_palette=0; 556 | break; 557 | } 558 | } 559 | else if(*_has_palette>0&&memcmp(_buf+offs+4,"PLTE",4)==0){ 560 | *_colors=chunk_len/3; 561 | break; 562 | } 563 | offs+=12+chunk_len; 564 | } 565 | } 566 | } 567 | 568 | static int op_is_gif(const unsigned char *_buf,size_t _buf_sz){ 569 | return _buf_sz>=6&&(memcmp(_buf,"GIF87a",6)==0||memcmp(_buf,"GIF89a",6)==0); 570 | } 571 | 572 | /*Tries to extract the width, height, bits per pixel, and palette size of a 573 | GIF. 574 | On failure, simply leaves its outputs unmodified.*/ 575 | static void op_extract_gif_params(const unsigned char *_buf,size_t _buf_sz, 576 | opus_uint32 *_width,opus_uint32 *_height, 577 | opus_uint32 *_depth,opus_uint32 *_colors,int *_has_palette){ 578 | if(op_is_gif(_buf,_buf_sz)&&_buf_sz>=14){ 579 | *_width=_buf[6]|_buf[7]<<8; 580 | *_height=_buf[8]|_buf[9]<<8; 581 | /*libFLAC hard-codes the depth to 24.*/ 582 | *_depth=24; 583 | *_colors=1<<((_buf[10]&7)+1); 584 | *_has_palette=1; 585 | } 586 | } 587 | 588 | /*The actual implementation of opus_picture_tag_parse(). 589 | Unlike the public API, this function requires _pic to already be 590 | initialized, modifies its contents before success is guaranteed, and assumes 591 | the caller will clear it on error.*/ 592 | static int opus_picture_tag_parse_impl(OpusPictureTag *_pic,const char *_tag, 593 | unsigned char *_buf,size_t _buf_sz,size_t _base64_sz){ 594 | opus_int32 picture_type; 595 | opus_uint32 mime_type_length; 596 | char *mime_type; 597 | opus_uint32 description_length; 598 | char *description; 599 | opus_uint32 width; 600 | opus_uint32 height; 601 | opus_uint32 depth; 602 | opus_uint32 colors; 603 | opus_uint32 data_length; 604 | opus_uint32 file_width; 605 | opus_uint32 file_height; 606 | opus_uint32 file_depth; 607 | opus_uint32 file_colors; 608 | int format; 609 | int has_palette; 610 | int colors_set; 611 | size_t i; 612 | /*Decode the BASE64 data.*/ 613 | OP_ASSERT(_base64_sz>=11); 614 | for(i=0;i<_base64_sz;i++){ 615 | opus_uint32 value; 616 | int j; 617 | value=0; 618 | for(j=0;j<4;j++){ 619 | unsigned c; 620 | unsigned d; 621 | c=(unsigned char)_tag[4*i+j]; 622 | if(c=='+')d=62; 623 | else if(c=='/')d=63; 624 | else if(c>='0'&&c<='9')d=52+c-'0'; 625 | else if(c>='a'&&c<='z')d=26+c-'a'; 626 | else if(c>='A'&&c<='Z')d=c-'A'; 627 | else if(c=='='&&3*i+j>_buf_sz)d=0; 628 | else return OP_ENOTFORMAT; 629 | value=value<<6|d; 630 | } 631 | _buf[3*i]=(unsigned char)(value>>16); 632 | if(3*i+1<_buf_sz){ 633 | _buf[3*i+1]=(unsigned char)(value>>8); 634 | if(3*i+2<_buf_sz)_buf[3*i+2]=(unsigned char)value; 635 | } 636 | } 637 | i=0; 638 | picture_type=op_parse_uint32be(_buf+i); 639 | i+=4; 640 | /*Extract the MIME type.*/ 641 | mime_type_length=op_parse_uint32be(_buf+i); 642 | i+=4; 643 | if(mime_type_length>_buf_sz-32)return OP_ENOTFORMAT; 644 | mime_type=(char *)_ogg_malloc(sizeof(*_pic->mime_type)*(mime_type_length+1)); 645 | if(mime_type==NULL)return OP_EFAULT; 646 | memcpy(mime_type,_buf+i,sizeof(*mime_type)*mime_type_length); 647 | mime_type[mime_type_length]='\0'; 648 | _pic->mime_type=mime_type; 649 | i+=mime_type_length; 650 | /*Extract the description string.*/ 651 | description_length=op_parse_uint32be(_buf+i); 652 | i+=4; 653 | if(description_length>_buf_sz-mime_type_length-32)return OP_ENOTFORMAT; 654 | description= 655 | (char *)_ogg_malloc(sizeof(*_pic->mime_type)*(description_length+1)); 656 | if(description==NULL)return OP_EFAULT; 657 | memcpy(description,_buf+i,sizeof(*description)*description_length); 658 | description[description_length]='\0'; 659 | _pic->description=description; 660 | i+=description_length; 661 | /*Extract the remaining fields.*/ 662 | width=op_parse_uint32be(_buf+i); 663 | i+=4; 664 | height=op_parse_uint32be(_buf+i); 665 | i+=4; 666 | depth=op_parse_uint32be(_buf+i); 667 | i+=4; 668 | colors=op_parse_uint32be(_buf+i); 669 | i+=4; 670 | /*If one of these is set, they all must be, but colors==0 is a valid value.*/ 671 | colors_set=width!=0||height!=0||depth!=0||colors!=0; 672 | if((width==0||height==0||depth==0)&&colors_set)return OP_ENOTFORMAT; 673 | data_length=op_parse_uint32be(_buf+i); 674 | i+=4; 675 | if(data_length>_buf_sz-i)return OP_ENOTFORMAT; 676 | /*Trim extraneous data so we don't copy it below.*/ 677 | _buf_sz=i+data_length; 678 | /*Attempt to determine the image format.*/ 679 | format=OP_PIC_FORMAT_UNKNOWN; 680 | if(mime_type_length==3&&strcmp(mime_type,"-->")==0){ 681 | format=OP_PIC_FORMAT_URL; 682 | /*Picture type 1 must be a 32x32 PNG.*/ 683 | if(picture_type==1&&(width!=0||height!=0)&&(width!=32||height!=32)){ 684 | return OP_ENOTFORMAT; 685 | } 686 | /*Append a terminating NUL for the convenience of our callers.*/ 687 | _buf[_buf_sz++]='\0'; 688 | } 689 | else{ 690 | if(mime_type_length==10 691 | &&op_strncasecmp(mime_type,"image/jpeg",mime_type_length)==0){ 692 | if(op_is_jpeg(_buf+i,data_length))format=OP_PIC_FORMAT_JPEG; 693 | } 694 | else if(mime_type_length==9 695 | &&op_strncasecmp(mime_type,"image/png",mime_type_length)==0){ 696 | if(op_is_png(_buf+i,data_length))format=OP_PIC_FORMAT_PNG; 697 | } 698 | else if(mime_type_length==9 699 | &&op_strncasecmp(mime_type,"image/gif",mime_type_length)==0){ 700 | if(op_is_gif(_buf+i,data_length))format=OP_PIC_FORMAT_GIF; 701 | } 702 | else if(mime_type_length==0||(mime_type_length==6 703 | &&op_strncasecmp(mime_type,"image/",mime_type_length)==0)){ 704 | if(op_is_jpeg(_buf+i,data_length))format=OP_PIC_FORMAT_JPEG; 705 | else if(op_is_png(_buf+i,data_length))format=OP_PIC_FORMAT_PNG; 706 | else if(op_is_gif(_buf+i,data_length))format=OP_PIC_FORMAT_GIF; 707 | } 708 | file_width=file_height=file_depth=file_colors=0; 709 | has_palette=-1; 710 | switch(format){ 711 | case OP_PIC_FORMAT_JPEG:{ 712 | op_extract_jpeg_params(_buf+i,data_length, 713 | &file_width,&file_height,&file_depth,&file_colors,&has_palette); 714 | }break; 715 | case OP_PIC_FORMAT_PNG:{ 716 | op_extract_png_params(_buf+i,data_length, 717 | &file_width,&file_height,&file_depth,&file_colors,&has_palette); 718 | }break; 719 | case OP_PIC_FORMAT_GIF:{ 720 | op_extract_gif_params(_buf+i,data_length, 721 | &file_width,&file_height,&file_depth,&file_colors,&has_palette); 722 | }break; 723 | } 724 | if(has_palette>=0){ 725 | /*If we successfully extracted these parameters from the image, override 726 | any declared values.*/ 727 | width=file_width; 728 | height=file_height; 729 | depth=file_depth; 730 | colors=file_colors; 731 | } 732 | /*Picture type 1 must be a 32x32 PNG.*/ 733 | if(picture_type==1&&(format!=OP_PIC_FORMAT_PNG||width!=32||height!=32)){ 734 | return OP_ENOTFORMAT; 735 | } 736 | } 737 | /*Adjust _buf_sz instead of using data_length to capture the terminating NUL 738 | for URLs.*/ 739 | _buf_sz-=i; 740 | memmove(_buf,_buf+i,sizeof(*_buf)*_buf_sz); 741 | _buf=(unsigned char *)_ogg_realloc(_buf,_buf_sz); 742 | if(_buf_sz>0&&_buf==NULL)return OP_EFAULT; 743 | _pic->type=picture_type; 744 | _pic->width=width; 745 | _pic->height=height; 746 | _pic->depth=depth; 747 | _pic->colors=colors; 748 | _pic->data_length=data_length; 749 | _pic->data=_buf; 750 | _pic->format=format; 751 | return 0; 752 | } 753 | 754 | int opus_picture_tag_parse(OpusPictureTag *_pic,const char *_tag){ 755 | OpusPictureTag pic; 756 | unsigned char *buf; 757 | size_t base64_sz; 758 | size_t buf_sz; 759 | size_t tag_length; 760 | int ret; 761 | if(opus_tagncompare("METADATA_BLOCK_PICTURE",22,_tag)==0)_tag+=23; 762 | /*Figure out how much BASE64-encoded data we have.*/ 763 | tag_length=strlen(_tag); 764 | if(tag_length&3)return OP_ENOTFORMAT; 765 | base64_sz=tag_length>>2; 766 | buf_sz=3*base64_sz; 767 | if(buf_sz<32)return OP_ENOTFORMAT; 768 | if(_tag[tag_length-1]=='=')buf_sz--; 769 | if(_tag[tag_length-2]=='=')buf_sz--; 770 | if(buf_sz<32)return OP_ENOTFORMAT; 771 | /*Allocate an extra byte to allow appending a terminating NUL to URL data.*/ 772 | buf=(unsigned char *)_ogg_malloc(sizeof(*buf)*(buf_sz+1)); 773 | if(buf==NULL)return OP_EFAULT; 774 | opus_picture_tag_init(&pic); 775 | ret=opus_picture_tag_parse_impl(&pic,_tag,buf,buf_sz,base64_sz); 776 | if(ret<0){ 777 | opus_picture_tag_clear(&pic); 778 | _ogg_free(buf); 779 | } 780 | else *_pic=*&pic; 781 | return ret; 782 | } 783 | 784 | void opus_picture_tag_init(OpusPictureTag *_pic){ 785 | memset(_pic,0,sizeof(*_pic)); 786 | } 787 | 788 | void opus_picture_tag_clear(OpusPictureTag *_pic){ 789 | _ogg_free(_pic->description); 790 | _ogg_free(_pic->mime_type); 791 | _ogg_free(_pic->data); 792 | } 793 | -------------------------------------------------------------------------------- /opusfile/internal.h: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | * * 3 | * THIS FILE IS PART OF THE libopusfile SOFTWARE CODEC SOURCE CODE. * 4 | * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * 5 | * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * 6 | * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * 7 | * * 8 | * THE libopusfile SOURCE CODE IS (C) COPYRIGHT 2012 * 9 | * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * 10 | * * 11 | ********************************************************************/ 12 | #if !defined(_opusfile_internal_h) 13 | # define _opusfile_internal_h (1) 14 | 15 | # if !defined(_REENTRANT) 16 | # define _REENTRANT 17 | # endif 18 | # if !defined(_GNU_SOURCE) 19 | # define _GNU_SOURCE 20 | # endif 21 | # if !defined(_LARGEFILE_SOURCE) 22 | # define _LARGEFILE_SOURCE 23 | # endif 24 | # if !defined(_LARGEFILE64_SOURCE) 25 | # define _LARGEFILE64_SOURCE 26 | # endif 27 | # if !defined(_FILE_OFFSET_BITS) 28 | # define _FILE_OFFSET_BITS 64 29 | # endif 30 | 31 | # include 32 | # include 33 | 34 | typedef struct OggOpusLink OggOpusLink; 35 | 36 | # if defined(OP_FIXED_POINT) 37 | 38 | typedef opus_int16 op_sample; 39 | 40 | # else 41 | 42 | typedef float op_sample; 43 | 44 | /*We're using this define to test for libopus 1.1 or later until libopus 45 | provides a better mechanism.*/ 46 | # if defined(OPUS_GET_EXPERT_FRAME_DURATION_REQUEST) 47 | /*Enable soft clipping prevention in 16-bit decodes.*/ 48 | # define OP_SOFT_CLIP (1) 49 | # endif 50 | 51 | # endif 52 | 53 | # if OP_GNUC_PREREQ(4,2) 54 | /*Disable excessive warnings about the order of operations.*/ 55 | # pragma GCC diagnostic ignored "-Wparentheses" 56 | # elif defined(_MSC_VER) 57 | /*Disable excessive warnings about the order of operations.*/ 58 | # pragma warning(disable:4554) 59 | /*Disable warnings about "deprecated" POSIX functions.*/ 60 | # pragma warning(disable:4996) 61 | # endif 62 | 63 | # if OP_GNUC_PREREQ(3,0) 64 | /*Another alternative is 65 | (__builtin_constant_p(_x)?!!(_x):__builtin_expect(!!(_x),1)) 66 | but that evaluates _x multiple times, which may be bad.*/ 67 | # define OP_LIKELY(_x) (__builtin_expect(!!(_x),1)) 68 | # define OP_UNLIKELY(_x) (__builtin_expect(!!(_x),0)) 69 | # else 70 | # define OP_LIKELY(_x) (!!(_x)) 71 | # define OP_UNLIKELY(_x) (!!(_x)) 72 | # endif 73 | 74 | # if defined(OP_ENABLE_ASSERTIONS) 75 | # if OP_GNUC_PREREQ(2,5)||__SUNPRO_C>=0x590 76 | __attribute__((noreturn)) 77 | # endif 78 | void op_fatal_impl(const char *_str,const char *_file,int _line); 79 | 80 | # define OP_FATAL(_str) (op_fatal_impl(_str,__FILE__,__LINE__)) 81 | 82 | # define OP_ASSERT(_cond) \ 83 | do{ \ 84 | if(OP_UNLIKELY(!(_cond)))OP_FATAL("assertion failed: " #_cond); \ 85 | } \ 86 | while(0) 87 | # define OP_ALWAYS_TRUE(_cond) OP_ASSERT(_cond) 88 | 89 | # else 90 | # define OP_FATAL(_str) abort() 91 | # define OP_ASSERT(_cond) 92 | # define OP_ALWAYS_TRUE(_cond) ((void)(_cond)) 93 | # endif 94 | 95 | # define OP_INT64_MAX (2*(((ogg_int64_t)1<<62)-1)|1) 96 | # define OP_INT64_MIN (-OP_INT64_MAX-1) 97 | # define OP_INT32_MAX (2*(((ogg_int32_t)1<<30)-1)|1) 98 | # define OP_INT32_MIN (-OP_INT32_MAX-1) 99 | 100 | # define OP_MIN(_a,_b) ((_a)<(_b)?(_a):(_b)) 101 | # define OP_MAX(_a,_b) ((_a)>(_b)?(_a):(_b)) 102 | # define OP_CLAMP(_l,_x,_h) ( (_x)<(_l)?(_l) : ((_x)>(_h)? (_h): (_x)) ) 103 | /*# define OP_CLAMP(_lo,_x,_hi) (OP_MAX(_lo,OP_MIN(_x,_hi)))*/ 104 | 105 | /*Advance a file offset by the given amount, clamping against OP_INT64_MAX. 106 | This is used to advance a known offset by things like OP_CHUNK_SIZE or 107 | OP_PAGE_SIZE_MAX, while making sure to avoid signed overflow. 108 | It assumes that both _offset and _amount are non-negative.*/ 109 | #define OP_ADV_OFFSET(_offset,_amount) \ 110 | (OP_MIN(_offset,OP_INT64_MAX-(_amount))+(_amount)) 111 | 112 | /*The maximum channel count for any mapping we'll actually decode.*/ 113 | # define OP_NCHANNELS_MAX (8) 114 | 115 | /*Initial state.*/ 116 | # define OP_NOTOPEN (0) 117 | /*We've found the first Opus stream in the first link.*/ 118 | # define OP_PARTOPEN (1) 119 | # define OP_OPENED (2) 120 | /*We've found the first Opus stream in the current link.*/ 121 | # define OP_STREAMSET (3) 122 | /*We've initialized the decoder for the chosen Opus stream in the current 123 | link.*/ 124 | # define OP_INITSET (4) 125 | 126 | /*Information cached for a single link in a chained Ogg Opus file. 127 | We choose the first Opus stream encountered in each link to play back (and 128 | require at least one).*/ 129 | struct OggOpusLink{ 130 | /*The byte offset of the first header page in this link.*/ 131 | opus_int64 offset; 132 | /*The byte offset of the first data page from the chosen Opus stream in this 133 | link (after the headers).*/ 134 | opus_int64 data_offset; 135 | /*The byte offset of the last page from the chosen Opus stream in this link. 136 | This is used when seeking to ensure we find a page before the last one, so 137 | that end-trimming calculations work properly. 138 | This is only valid for seekable sources.*/ 139 | opus_int64 end_offset; 140 | /*The total duration of all prior links. 141 | This is always zero for non-seekable sources.*/ 142 | ogg_int64_t pcm_file_offset; 143 | /*The granule position of the last sample. 144 | This is only valid for seekable sources.*/ 145 | ogg_int64_t pcm_end; 146 | /*The granule position before the first sample.*/ 147 | ogg_int64_t pcm_start; 148 | /*The serial number.*/ 149 | ogg_uint32_t serialno; 150 | /*The contents of the info header.*/ 151 | OpusHead head; 152 | /*The contents of the comment header.*/ 153 | OpusTags tags; 154 | }; 155 | 156 | struct OggOpusFile{ 157 | /*The callbacks used to access the stream.*/ 158 | OpusFileCallbacks callbacks; 159 | /*A FILE *, memory buffer, etc.*/ 160 | void *stream; 161 | /*Whether or not we can seek with this stream.*/ 162 | int seekable; 163 | /*The number of links in this chained Ogg Opus file.*/ 164 | int nlinks; 165 | /*The cached information from each link in a chained Ogg Opus file. 166 | If stream isn't seekable (e.g., it's a pipe), only the current link 167 | appears.*/ 168 | OggOpusLink *links; 169 | /*The number of serial numbers from a single link.*/ 170 | int nserialnos; 171 | /*The capacity of the list of serial numbers from a single link.*/ 172 | int cserialnos; 173 | /*Storage for the list of serial numbers from a single link. 174 | This is a scratch buffer used when scanning the BOS pages at the start of 175 | each link.*/ 176 | ogg_uint32_t *serialnos; 177 | /*This is the current offset of the data processed by the ogg_sync_state. 178 | After a seek, this should be set to the target offset so that we can track 179 | the byte offsets of subsequent pages. 180 | After a call to op_get_next_page(), this will point to the first byte after 181 | that page.*/ 182 | opus_int64 offset; 183 | /*The total size of this stream, or -1 if it's unseekable.*/ 184 | opus_int64 end; 185 | /*Used to locate pages in the stream.*/ 186 | ogg_sync_state oy; 187 | /*One of OP_NOTOPEN, OP_PARTOPEN, OP_OPENED, OP_STREAMSET, OP_INITSET.*/ 188 | int ready_state; 189 | /*The current link being played back.*/ 190 | int cur_link; 191 | /*The number of decoded samples to discard from the start of decoding.*/ 192 | opus_int32 cur_discard_count; 193 | /*The granule position of the previous packet (current packet start time).*/ 194 | ogg_int64_t prev_packet_gp; 195 | /*The stream offset of the most recent page with completed packets, or -1. 196 | This is only needed to recover continued packet data in the seeking logic, 197 | when we use the current position as one of our bounds, only to later 198 | discover it was the correct starting point.*/ 199 | opus_int64 prev_page_offset; 200 | /*The number of bytes read since the last bitrate query, including framing.*/ 201 | opus_int64 bytes_tracked; 202 | /*The number of samples decoded since the last bitrate query.*/ 203 | ogg_int64_t samples_tracked; 204 | /*Takes physical pages and welds them into a logical stream of packets.*/ 205 | ogg_stream_state os; 206 | /*Re-timestamped packets from a single page. 207 | Buffering these relies on the undocumented libogg behavior that ogg_packet 208 | pointers remain valid until the next page is submitted to the 209 | ogg_stream_state they came from.*/ 210 | ogg_packet op[255]; 211 | /*The index of the next packet to return.*/ 212 | int op_pos; 213 | /*The total number of packets available.*/ 214 | int op_count; 215 | /*Central working state for the packet-to-PCM decoder.*/ 216 | OpusMSDecoder *od; 217 | /*The application-provided packet decode callback.*/ 218 | op_decode_cb_func decode_cb; 219 | /*The application-provided packet decode callback context.*/ 220 | void *decode_cb_ctx; 221 | /*The stream count used to initialize the decoder.*/ 222 | int od_stream_count; 223 | /*The coupled stream count used to initialize the decoder.*/ 224 | int od_coupled_count; 225 | /*The channel count used to initialize the decoder.*/ 226 | int od_channel_count; 227 | /*The channel mapping used to initialize the decoder.*/ 228 | unsigned char od_mapping[OP_NCHANNELS_MAX]; 229 | /*The buffered data for one decoded packet.*/ 230 | op_sample *od_buffer; 231 | /*The current position in the decoded buffer.*/ 232 | int od_buffer_pos; 233 | /*The number of valid samples in the decoded buffer.*/ 234 | int od_buffer_size; 235 | /*The type of gain offset to apply. 236 | One of OP_HEADER_GAIN, OP_ALBUM_GAIN, OP_TRACK_GAIN, or OP_ABSOLUTE_GAIN.*/ 237 | int gain_type; 238 | /*The offset to apply to the gain.*/ 239 | opus_int32 gain_offset_q8; 240 | /*Internal state for soft clipping and dithering float->short output.*/ 241 | #if !defined(OP_FIXED_POINT) 242 | # if defined(OP_SOFT_CLIP) 243 | float clip_state[OP_NCHANNELS_MAX]; 244 | # endif 245 | float dither_a[OP_NCHANNELS_MAX*4]; 246 | float dither_b[OP_NCHANNELS_MAX*4]; 247 | opus_uint32 dither_seed; 248 | int dither_mute; 249 | int dither_disabled; 250 | /*The number of channels represented by the internal state. 251 | This gets set to 0 whenever anything that would prevent state propagation 252 | occurs (switching between the float/short APIs, or between the 253 | stereo/multistream APIs).*/ 254 | int state_channel_count; 255 | #endif 256 | }; 257 | 258 | //int op_strncasecmp(const char *_a,const char *_b,int _n); 259 | 260 | #endif 261 | -------------------------------------------------------------------------------- /opusfile/makelib.bat: -------------------------------------------------------------------------------- 1 | :: To make the modified version of libopusfile 2 | gcc -c *.c -I. -Os -fno-stack-check -fno-stack-protector -mno-stack-arg-probe -march=i486 -mtune=i686 -mpreferred-stack-boundary=2 3 | -------------------------------------------------------------------------------- /opusfile/stream.c: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | * * 3 | * THIS FILE IS PART OF THE libopusfile SOFTWARE CODEC SOURCE CODE. * 4 | * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * 5 | * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * 6 | * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * 7 | * * 8 | * THE libopusfile SOURCE CODE IS (C) COPYRIGHT 1994-2012 * 9 | * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * 10 | * * 11 | ******************************************************************** 12 | 13 | function: stdio-based convenience library for opening/seeking/decoding 14 | last mod: $Id: vorbisfile.c 17573 2010-10-27 14:53:59Z xiphmont $ 15 | 16 | ********************************************************************/ 17 | #ifdef HAVE_CONFIG_H 18 | #include "config.h" 19 | #endif 20 | 21 | #include "internal.h" 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #if defined(_WIN32) 28 | # include 29 | #include 30 | #endif 31 | 32 | wchar_t *utf8_to_utf16(const char *utfs); 33 | static FILE *(WINAPI *my_wfreopen)(const wchar_t *path, const wchar_t *mode, FILE *stream ); 34 | static FILE *(WINAPI *my_wfopen)(const wchar_t *filename, const wchar_t *mode ); 35 | 36 | 37 | typedef struct OpusMemStream OpusMemStream; 38 | 39 | #define OP_MEM_SIZE_MAX (~(size_t)0>>1) 40 | #define OP_MEM_DIFF_MAX ((ptrdiff_t)OP_MEM_SIZE_MAX) 41 | 42 | /* The context information needed to read from a block of memory 43 | * as if it were a file. 44 | */ 45 | struct OpusMemStream{ 46 | /*The block of memory to read from.*/ 47 | const unsigned char *data; 48 | /* The total size of the block. 49 | * This must be at most OP_MEM_SIZE_MAX to prevent 50 | * signed overflow while seeking. 51 | */ 52 | ptrdiff_t size; 53 | /* The current file position. 54 | * This is allowed to be set arbitrarily greater than size (i.e., past the end 55 | * of the block, though we will not read data past the end of the block), but 56 | * is not allowed to be negative (i.e., before the beginning of the block). 57 | */ 58 | ptrdiff_t pos; 59 | }; 60 | 61 | static int op_fread(void *_stream,unsigned char *_ptr,int _buf_size) 62 | { 63 | FILE *stream; 64 | size_t ret; 65 | /*Check for empty read.*/ 66 | if(_buf_size<=0)return 0; 67 | stream=(FILE *)_stream; 68 | ret=fread(_ptr,1,_buf_size,stream); 69 | OP_ASSERT(ret<=(size_t)_buf_size); 70 | /*If ret==0 and !feof(stream), there was a read error.*/ 71 | return ret>0||feof(stream)?(int)ret:OP_EREAD; 72 | } 73 | 74 | static int op_fseek(void *_stream,opus_int64 _offset,int _whence) 75 | { 76 | #if defined(_WIN32) 77 | /* _fseeki64() is not exposed until MSCVCRT80. 78 | * This is the default starting with MSVC 2005 (_MSC_VER>=1400), but we want 79 | * to allow linking against older MSVCRT versions for compatibility back to 80 | * XP without installing extra runtime libraries. 81 | * i686-pc-mingw32 does not have fseeko() and requires 82 | * __MSVCRT_VERSION__>=0x800 for _fseeki64(), which screws up linking with 83 | * other libraries (that don't use MSVCRT80 from MSVC 2005 by default). 84 | * i686-w64-mingw32 does have fseeko() and respects _FILE_OFFSET_BITS, but I 85 | * don't know how to detect that at compile time. 86 | * We could just use fseeko64() (which is available in both), but its 87 | * implemented using fgetpos()/fsetpos() just like this code, except without 88 | * the overflow checking, so we prefer our version. 89 | */ 90 | opus_int64 pos; 91 | /* We don't use fpos_t directly because it might be a struct if __STDC__ is 92 | * non-zero or _INTEGRAL_MAX_BITS < 64. 93 | * I'm not certain when the latter is true, but someone could in theory set 94 | * the former. 95 | * Either way, it should be binary compatible with a normal 64-bit int (this 96 | * assumption is not portable, but I believe it is true for MSVCRT). 97 | */ 98 | OP_ASSERT(sizeof(pos)==sizeof(fpos_t)); 99 | /*Translate the seek to an absolute one.*/ 100 | if(_whence==SEEK_CUR){ 101 | int ret; 102 | ret=fgetpos((FILE *)_stream,(fpos_t *)&pos); 103 | if(ret)return ret; 104 | } 105 | else if(_whence==SEEK_END)pos=_filelengthi64(_fileno((FILE *)_stream)); 106 | else if(_whence==SEEK_SET)pos=0; 107 | else return -1; 108 | /*Check for errors or overflow.*/ 109 | if(pos<0||_offset<-pos||_offset>OP_INT64_MAX-pos)return -1; 110 | pos+=_offset; 111 | return fsetpos((FILE *)_stream,(fpos_t *)&pos); 112 | #else 113 | /* This function actually conforms to the SUSv2 and POSIX.1-2001, so we prefer 114 | * it except on Windows. 115 | */ 116 | return fseek((FILE *)_stream,(off_t)_offset,_whence); 117 | #endif 118 | } 119 | 120 | static opus_int64 op_ftell(void *_stream) 121 | { 122 | #if defined(_WIN32) 123 | /* _ftelli64() is not exposed until MSCVCRT80, and ftello()/ftello64() have 124 | * the same problems as fseeko()/fseeko64() in MingW. 125 | * See above for a more detailed explanation. 126 | */ 127 | opus_int64 pos; 128 | OP_ASSERT(sizeof(pos)==sizeof(fpos_t)); 129 | return fgetpos((FILE *)_stream,(fpos_t *)&pos)?-1:pos; 130 | #else 131 | /* This function actually conforms to the SUSv2 and POSIX.1-2001, so we prefer 132 | * it except on Windows. 133 | */ 134 | return ftell((FILE *)_stream); 135 | #endif 136 | } 137 | 138 | static const OpusFileCallbacks OP_FILE_CALLBACKS={ 139 | op_fread, 140 | op_fseek, 141 | op_ftell, 142 | (op_close_func)fclose 143 | }; 144 | 145 | void *op_fopen(OpusFileCallbacks *_cb,const char *_path,const char *_mode) 146 | { 147 | FILE *fp=NULL; 148 | if(!_path || !_mode || *_path == '\0' || *_mode == '\0') return NULL; 149 | 150 | fp=fopen(_path,_mode); // 1st try to open ANSI 151 | 152 | if(!fp){ // If it does not work then try unicode. 153 | wchar_t *wpath; 154 | wchar_t *wmode; 155 | 156 | wpath=utf8_to_utf16(_path); if(!wpath) errno=EINVAL; 157 | wmode=utf8_to_utf16(_mode); if(!wmode) errno=ENOENT; 158 | 159 | HINSTANCE hMsvcrtDll=NULL; 160 | 161 | hMsvcrtDll = GetModuleHandle("MSVCRT.DLL"); 162 | if(hMsvcrtDll) my_wfopen = (void *)GetProcAddress(hMsvcrtDll, "_wfopen" ); 163 | if(my_wfopen) fp=(FILE *)my_wfopen(wpath, wmode); 164 | 165 | if(wmode) _ogg_free(wmode); 166 | if(wpath) _ogg_free(wpath); 167 | } 168 | 169 | if(fp != NULL)*_cb = OP_FILE_CALLBACKS; 170 | return fp; 171 | } 172 | 173 | void *op_fdopen(OpusFileCallbacks *_cb, int _fd,const char *_mode) 174 | { 175 | FILE *fp; 176 | fp=_fdopen(_fd,_mode); 177 | 178 | if(fp!=NULL)*_cb = OP_FILE_CALLBACKS; 179 | return fp; 180 | } 181 | 182 | void *op_freopen(OpusFileCallbacks *_cb,const char *_path,const char *_mode, void *_stream) 183 | { 184 | FILE *fp; 185 | 186 | fp=freopen(_path,_mode,(FILE *)_stream); 187 | 188 | if(!fp){ // If it does not work then try unicode. 189 | wchar_t *wpath; 190 | wchar_t *wmode; 191 | 192 | wpath=utf8_to_utf16(_path); if(!wpath) errno=EINVAL; 193 | wmode=utf8_to_utf16(_mode); if(!wmode) errno=EINVAL; 194 | 195 | 196 | HINSTANCE hMsvcrtDll=NULL; 197 | 198 | hMsvcrtDll = GetModuleHandle("MSVCRT.DLL"); 199 | if(hMsvcrtDll) my_wfreopen = (void *)GetProcAddress(hMsvcrtDll, "_wfreopen"); 200 | if(my_wfreopen) fp=(FILE *)my_wfreopen(wpath, wmode, (FILE *)_stream); 201 | 202 | if(wmode) _ogg_free(wmode); 203 | if(wpath) _ogg_free(wpath); 204 | } 205 | 206 | if(fp!=NULL)*_cb=*&OP_FILE_CALLBACKS; 207 | return fp; 208 | } 209 | 210 | static int op_mem_read(void *_stream,unsigned char *_ptr,int _buf_size) 211 | { 212 | OpusMemStream *stream; 213 | ptrdiff_t size; 214 | ptrdiff_t pos; 215 | stream=(OpusMemStream *)_stream; 216 | /*Check for empty read.*/ 217 | if(_buf_size<=0)return 0; 218 | size=stream->size; 219 | pos=stream->pos; 220 | /*Check for EOF.*/ 221 | if(pos>=size)return 0; 222 | /*Check for a short read.*/ 223 | _buf_size=(int)OP_MIN(size-pos,_buf_size); 224 | memcpy(_ptr,stream->data+pos,_buf_size); 225 | pos+=_buf_size; 226 | stream->pos=pos; 227 | return _buf_size; 228 | } 229 | 230 | static int op_mem_seek(void *_stream,opus_int64 _offset,int _whence) 231 | { 232 | OpusMemStream *stream; 233 | ptrdiff_t pos; 234 | stream=(OpusMemStream *)_stream; 235 | pos=stream->pos; 236 | OP_ASSERT(pos>=0); 237 | switch(_whence){ 238 | case SEEK_SET:{ 239 | /*Check for overflow:*/ 240 | if(_offset<0||_offset>OP_MEM_DIFF_MAX)return -1; 241 | pos=(ptrdiff_t)_offset; 242 | }break; 243 | case SEEK_CUR:{ 244 | /*Check for overflow:*/ 245 | if(_offset<-pos||_offset>OP_MEM_DIFF_MAX-pos)return -1; 246 | pos=(ptrdiff_t)(pos+_offset); 247 | }break; 248 | case SEEK_END:{ 249 | ptrdiff_t size; 250 | size=stream->size; 251 | OP_ASSERT(size>=0); 252 | /*Check for overflow:*/ 253 | if(_offset>size||_offsetpos=pos; 259 | return 0; 260 | } 261 | 262 | static opus_int64 op_mem_tell(void *_stream) 263 | { 264 | OpusMemStream *stream; 265 | stream=(OpusMemStream *)_stream; 266 | return (ogg_int64_t)stream->pos; 267 | } 268 | 269 | static int op_mem_close(void *_stream) 270 | { 271 | _ogg_free(_stream); 272 | return 0; 273 | } 274 | 275 | static const OpusFileCallbacks OP_MEM_CALLBACKS={ 276 | op_mem_read, 277 | op_mem_seek, 278 | op_mem_tell, 279 | op_mem_close 280 | }; 281 | 282 | void *op_mem_stream_create(OpusFileCallbacks *_cb, const unsigned char *_data,size_t _size) 283 | { 284 | OpusMemStream *stream; 285 | if(_size>OP_MEM_SIZE_MAX)return NULL; 286 | stream=(OpusMemStream *)_ogg_malloc(sizeof(*stream)); 287 | if(stream!=NULL){ 288 | *_cb=*&OP_MEM_CALLBACKS; 289 | stream->data=_data; 290 | stream->size=_size; 291 | stream->pos=0; 292 | } 293 | return stream; 294 | } 295 | -------------------------------------------------------------------------------- /opusfile/winerrno.h: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | * * 3 | * THIS FILE IS PART OF THE libopusfile SOFTWARE CODEC SOURCE CODE. * 4 | * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * 5 | * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * 6 | * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * 7 | * * 8 | * THE libopusfile SOURCE CODE IS (C) COPYRIGHT 2012 * 9 | * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * 10 | * * 11 | ********************************************************************/ 12 | #if !defined(_opusfile_winerrno_h) 13 | # define _opusfile_winerrno_h (1) 14 | 15 | # include 16 | # include 17 | 18 | /*These conflict with the MSVC errno.h definitions, but we don't need to use 19 | the original ones in any file that deals with sockets. 20 | We could map the WSA errors to the errno.h ones (most of which are only 21 | available on sufficiently new versions of MSVC), but they aren't ordered the 22 | same, and given how rarely we actually look at the values, I don't think 23 | it's worth a lookup table.*/ 24 | # undef EWOULDBLOCK 25 | # undef EINPROGRESS 26 | # undef EALREADY 27 | # undef ENOTSOCK 28 | # undef EDESTADDRREQ 29 | # undef EMSGSIZE 30 | # undef EPROTOTYPE 31 | # undef ENOPROTOOPT 32 | # undef EPROTONOSUPPORT 33 | # undef EOPNOTSUPP 34 | # undef EAFNOSUPPORT 35 | # undef EADDRINUSE 36 | # undef EADDRNOTAVAIL 37 | # undef ENETDOWN 38 | # undef ENETUNREACH 39 | # undef ENETRESET 40 | # undef ECONNABORTED 41 | # undef ECONNRESET 42 | # undef ENOBUFS 43 | # undef EISCONN 44 | # undef ENOTCONN 45 | # undef ETIMEDOUT 46 | # undef ECONNREFUSED 47 | # undef ELOOP 48 | # undef ENAMETOOLONG 49 | # undef EHOSTUNREACH 50 | # undef ENOTEMPTY 51 | 52 | # define EWOULDBLOCK (WSAEWOULDBLOCK-WSABASEERR) 53 | # define EINPROGRESS (WSAEINPROGRESS-WSABASEERR) 54 | # define EALREADY (WSAEALREADY-WSABASEERR) 55 | # define ENOTSOCK (WSAENOTSOCK-WSABASEERR) 56 | # define EDESTADDRREQ (WSAEDESTADDRREQ-WSABASEERR) 57 | # define EMSGSIZE (WSAEMSGSIZE-WSABASEERR) 58 | # define EPROTOTYPE (WSAEPROTOTYPE-WSABASEERR) 59 | # define ENOPROTOOPT (WSAENOPROTOOPT-WSABASEERR) 60 | # define EPROTONOSUPPORT (WSAEPROTONOSUPPORT-WSABASEERR) 61 | # define ESOCKTNOSUPPORT (WSAESOCKTNOSUPPORT-WSABASEERR) 62 | # define EOPNOTSUPP (WSAEOPNOTSUPP-WSABASEERR) 63 | # define EPFNOSUPPORT (WSAEPFNOSUPPORT-WSABASEERR) 64 | # define EAFNOSUPPORT (WSAEAFNOSUPPORT-WSABASEERR) 65 | # define EADDRINUSE (WSAEADDRINUSE-WSABASEERR) 66 | # define EADDRNOTAVAIL (WSAEADDRNOTAVAIL-WSABASEERR) 67 | # define ENETDOWN (WSAENETDOWN-WSABASEERR) 68 | # define ENETUNREACH (WSAENETUNREACH-WSABASEERR) 69 | # define ENETRESET (WSAENETRESET-WSABASEERR) 70 | # define ECONNABORTED (WSAECONNABORTED-WSABASEERR) 71 | # define ECONNRESET (WSAECONNRESET-WSABASEERR) 72 | # define ENOBUFS (WSAENOBUFS-WSABASEERR) 73 | # define EISCONN (WSAEISCONN-WSABASEERR) 74 | # define ENOTCONN (WSAENOTCONN-WSABASEERR) 75 | # define ESHUTDOWN (WSAESHUTDOWN-WSABASEERR) 76 | # define ETOOMANYREFS (WSAETOOMANYREFS-WSABASEERR) 77 | # define ETIMEDOUT (WSAETIMEDOUT-WSABASEERR) 78 | # define ECONNREFUSED (WSAECONNREFUSED-WSABASEERR) 79 | # define ELOOP (WSAELOOP-WSABASEERR) 80 | # define ENAMETOOLONG (WSAENAMETOOLONG-WSABASEERR) 81 | # define EHOSTDOWN (WSAEHOSTDOWN-WSABASEERR) 82 | # define EHOSTUNREACH (WSAEHOSTUNREACH-WSABASEERR) 83 | # define ENOTEMPTY (WSAENOTEMPTY-WSABASEERR) 84 | # define EPROCLIM (WSAEPROCLIM-WSABASEERR) 85 | # define EUSERS (WSAEUSERS-WSABASEERR) 86 | # define EDQUOT (WSAEDQUOT-WSABASEERR) 87 | # define ESTALE (WSAESTALE-WSABASEERR) 88 | # define EREMOTE (WSAEREMOTE-WSABASEERR) 89 | 90 | #endif 91 | -------------------------------------------------------------------------------- /resample.c: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | * Copyright (C) 2007-2008 Jean-Marc Valin * 3 | * Copyright (C) 2008 Thorvald Natvig * 4 | * * 5 | * File: resample.c * 6 | * Arbitrary resampling code * 7 | **************************************************************************** 8 | * Redistribution and use in source and binary forms, with or without * 9 | * modification, are permitted provided that the following conditions are * 10 | * met: * 11 | * * 12 | * 1. Redistributions of source code must retain the above copyright notice,* 13 | * this list of conditions and the following disclaimer. * 14 | * * 15 | * 2. Redistributions in binary form must reproduce the above copyright * 16 | * notice, this list of conditions and the following disclaimer in the * 17 | * documentation and/or other materials provided with the distribution. * 18 | * * 19 | * 3. The name of the author may not be used to endorse or promote products * 20 | * derived from this software without specific prior written permission. * 21 | **************************************************************************** 22 | * The design goals of this code are: * 23 | * - Very fast algorithm * 24 | * - SIMD-friendly algorithm * 25 | * - Low memory requirement * 26 | * - Good *perceptual* quality (and not best SNR) * 27 | * Warning: This resampler is relatively new. Although I think I got rid of * 28 | * all the major bugs and I don't expect the API to change anymore, there * 29 | * may be something I've missed. So use with caution. * 30 | * * 31 | * This algorithm is based on this original resampling algorithm: * 32 | * Smith, Julius O. Digital Audio Resampling Home Page * 33 | * Center for Computer Research in Music and Acoustics (CCRMA), * 34 | * Stanford University, 2007. * 35 | * Web published at https://ccrma.stanford.edu/~jos/resample/. * 36 | * * 37 | * There is one main difference, though. This resampler uses cubic * 38 | * interpolation instead of linear interpolation in the above paper. This * 39 | * makes the table much smaller and makes it possible to compute that table * 40 | * on a per-stream basis. In turn, being able to tweak the table for each * 41 | * stream makes it possible to both reduce complexity on simple ratios * 42 | * (e.g. 2/3), and get rid of the rounding operations in the inner loop. * 43 | * The latter both reduces CPU time and makes the algorithm more * 44 | * SIMD-friendly. * 45 | ****************************************************************************/ 46 | 47 | #define MULT16_32_Q15(a,b) ((a)*(b)) 48 | #define MULT16_16(a,b) ((spx_word32_t)(a)*(spx_word32_t)(b)) 49 | #define SHR32(a,shift) (a) 50 | #define PSHR32(a,shift) (a) 51 | #define SATURATE32PSHR(x,shift,a) (x) 52 | typedef float spx_word16_t; 53 | typedef float spx_word32_t; 54 | 55 | #include 56 | static void *speex_alloc(size_t size) {return calloc(size,1);} 57 | static void *speex_realloc(void *ptr, size_t size) {return realloc(ptr, size);} 58 | static void speex_free(void *ptr) {free(ptr);} 59 | 60 | #include "resample.h" 61 | 62 | #include 63 | #include 64 | #include 65 | 66 | #ifndef M_PI 67 | #define M_PI 3.14159265358979323846 68 | #endif 69 | 70 | /* #define IMAX(a,b) ((a) > (b) ? (a) : (b)) 71 | * #define IMIN(a,b) ((a) < (b) ? (a) : (b)) */ 72 | 73 | #ifndef UINT32_MAX 74 | #define UINT32_MAX 4294967295U 75 | #endif 76 | 77 | typedef int (*resampler_basic_func)(SpeexResamplerState *, spx_uint32_t , const spx_word16_t *, spx_uint32_t *, spx_word16_t *, spx_uint32_t *); 78 | 79 | struct SpeexResamplerState_ { 80 | spx_uint32_t in_rate; 81 | spx_uint32_t out_rate; 82 | spx_uint32_t num_rate; 83 | spx_uint32_t den_rate; 84 | 85 | int quality; 86 | spx_uint32_t nb_channels; 87 | spx_uint32_t filt_len; 88 | spx_uint32_t mem_alloc_size; 89 | spx_uint32_t buffer_size; 90 | int int_advance; 91 | int frac_advance; 92 | float cutoff; 93 | spx_uint32_t oversample; 94 | int initialised; 95 | int started; 96 | 97 | /* These are per-channel */ 98 | spx_int32_t *last_sample; 99 | spx_uint32_t *samp_frac_num; 100 | spx_uint32_t *magic_samples; 101 | 102 | spx_word16_t *mem; 103 | spx_word16_t *sinc_table; 104 | spx_uint32_t sinc_table_length; 105 | resampler_basic_func resampler_ptr; 106 | 107 | int in_stride; 108 | int out_stride; 109 | } ; 110 | 111 | static const float kaiser8_table[36] = { 112 | 0.99635258, 1.00000000, 0.99635258, 0.98548012, 0.96759014, 0.94302200, 113 | 0.91223751, 0.87580811, 0.83439927, 0.78875245, 0.73966538, 0.68797126, 114 | 0.63451750, 0.58014482, 0.52566725, 0.47185369, 0.41941150, 0.36897272, 115 | 0.32108304, 0.27619388, 0.23465776, 0.19672670, 0.16255380, 0.13219758, 116 | 0.10562887, 0.08273982, 0.06335451, 0.04724088, 0.03412321, 0.02369490, 117 | 0.01563093, 0.00959968, 0.00527363, 0.00233883, 0.00050000, 0.00000000 118 | }; 119 | 120 | static const float kaiser6_table[36] = { 121 | 0.99733006, 1.00000000, 0.99733006, 0.98935595, 0.97618418, 0.95799003, 122 | 0.93501423, 0.90755855, 0.87598009, 0.84068475, 0.80211977, 0.76076565, 123 | 0.71712752, 0.67172623, 0.62508937, 0.57774224, 0.53019925, 0.48295561, 124 | 0.43647969, 0.39120616, 0.34752997, 0.30580127, 0.26632152, 0.22934058, 125 | 0.19505503, 0.16360756, 0.13508755, 0.10953262, 0.08693120, 0.06722600, 126 | 0.05031820, 0.03607231, 0.02432151, 0.01487334, 0.00752000, 0.00000000 127 | }; 128 | 129 | struct FuncDef { 130 | const float *table; 131 | int oversample; 132 | }; 133 | 134 | static const struct FuncDef kaiser8_funcdef = {kaiser8_table, 32}; 135 | #define KAISER8 (&kaiser8_funcdef) 136 | static const struct FuncDef kaiser6_funcdef = {kaiser6_table, 32}; 137 | #define KAISER6 (&kaiser6_funcdef) 138 | 139 | struct QualityMapping { 140 | int base_length; 141 | int oversample; 142 | float downsample_bandwidth; 143 | float upsample_bandwidth; 144 | const struct FuncDef *window_func; 145 | }; 146 | 147 | /* This table maps conversion quality to internal parameters. There are two 148 | * reasons that explain why the up-sampling bandwidth is larger than the 149 | * down-sampling bandwidth: 150 | * 1) When up-sampling, we can assume that the spectrum is already attenuated 151 | * close to the Nyquist rate (from an A/D or a previous resampling filter) 152 | * 2) Any aliasing that occurs very close to the Nyquist rate will be masked 153 | * by the sinusoids/noise just below the Nyquist rate (guaranteed only for 154 | * up-sampling). 155 | */ 156 | static const struct QualityMapping quality_map[5] = { 157 | { 8, 4, 0.830f, 0.860f, KAISER6 }, /* Q0 */ 158 | { 16, 4, 0.850f, 0.880f, KAISER6 }, /* Q1 */ 159 | { 32, 4, 0.882f, 0.910f, KAISER6 }, /* Q2 */ /* 82.3% cutoff ( ~60 dB stop) 6 */ 160 | { 48, 8, 0.895f, 0.917f, KAISER8 }, /* Q3 */ /* 84.9% cutoff ( ~80 dB stop) 8 */ 161 | { 64, 8, 0.921f, 0.940f, KAISER8 }, /* Q4 */ /* 88.7% cutoff ( ~80 dB stop) 8 */ 162 | }; 163 | /* 8, 24, 40, 56, 80, 104, 128, 160, 200, 256, 320 */ 164 | static inline float compute_func(float x, const struct FuncDef *func) 165 | { 166 | float y, frac; 167 | float interp[4]; 168 | int ind; 169 | y = x*func->oversample; 170 | // ind = (y>0)? (int)y: (int)(y-1.f); 171 | ind= (int)(y + 32768.F) - 32768; 172 | frac = (y-ind); 173 | /* CSE with handle the repeated powers */ 174 | interp[3] = -0.1666666667F*frac + 0.1666666667F*frac*(frac*frac); 175 | interp[2] = frac + 0.5F*(frac*frac) - 0.5F*(frac*frac)*frac; 176 | interp[0] = -0.3333333333F*frac + 0.5F*(frac*frac) - 0.1666666667F*frac*(frac*frac); 177 | /* Just to make sure we don't have rounding problems */ 178 | interp[1] = 1.f-interp[3]-interp[2]-interp[0]; 179 | 180 | return interp[0]*func->table[ind] + interp[1]*func->table[ind+1] 181 | + interp[2]*func->table[ind+2] + interp[3]*func->table[ind+3]; 182 | } 183 | 184 | /* The slow way of computing a sinc for the table. Should improve that some day */ 185 | static spx_word16_t sinc(float cutoff, float x, int N, const struct FuncDef *window_func) 186 | { 187 | /*fprintf (stderr, "%f ", x);*/ 188 | float xx, xabs; 189 | xabs=fabsf(x); 190 | if (xabs < 1e-6) 191 | return cutoff; 192 | else if (xabs > .5*N) 193 | return 0; 194 | 195 | /*FIXME: Can it really be any slower than this? */ 196 | xx = x * cutoff; 197 | return cutoff*sinf(M_PI*xx)/(M_PI*xx) * compute_func(2.F*xabs/N, window_func); 198 | } 199 | 200 | /* SSE ASEMBLY FUNCTION */ 201 | #ifdef __SSE1 202 | float inner_product_single(const float *a, const float *b, unsigned int len); 203 | char HAVE_SSE; 204 | #endif 205 | 206 | static int resampler_basic_direct_single(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_word16_t *in, spx_uint32_t *in_len, spx_word16_t *out, spx_uint32_t *out_len) 207 | { 208 | int N, out_sample, last_sample, j; 209 | 210 | spx_uint32_t samp_frac_num; 211 | spx_word16_t *sinc_table; 212 | int out_stride, int_advance, frac_advance; 213 | spx_uint32_t den_rate; 214 | spx_word32_t sum; 215 | const spx_word16_t *sinct; 216 | const spx_word16_t *iptr; 217 | 218 | N = st->filt_len; 219 | out_sample = 0; 220 | last_sample = st->last_sample[channel_index]; 221 | samp_frac_num = st->samp_frac_num[channel_index]; 222 | sinc_table = st->sinc_table; 223 | out_stride = st->out_stride; 224 | int_advance = st->int_advance; 225 | frac_advance = st->frac_advance; 226 | den_rate = st->den_rate; 227 | 228 | 229 | while (!(last_sample >= (spx_int32_t)*in_len || out_sample >= (spx_int32_t)*out_len)) 230 | { 231 | sinct = &sinc_table[samp_frac_num*N]; 232 | iptr = &in[last_sample]; 233 | 234 | #ifdef __SSE1 235 | if(HAVE_SSE){ 236 | sum = inner_product_single(sinct, iptr, N); 237 | } else 238 | #endif 239 | { 240 | sum = 0; 241 | for(j=0; j= den_rate) 249 | { 250 | samp_frac_num -= den_rate; 251 | last_sample++; 252 | } 253 | } 254 | 255 | st->last_sample[channel_index] = last_sample; 256 | st->samp_frac_num[channel_index] = samp_frac_num; 257 | 258 | return out_sample; 259 | } 260 | 261 | static int multiply_frac(spx_uint32_t *result, spx_uint32_t value, spx_uint32_t num, spx_uint32_t den) 262 | { 263 | spx_uint32_t major = value / den; 264 | spx_uint32_t remain = value % den; 265 | /* TODO: Could use 64 bits operation to check for overflow. But only guaranteed in C99+ */ 266 | if (remain > UINT32_MAX / num || major > UINT32_MAX / num 267 | || major * num > UINT32_MAX - remain * num / den) 268 | return RESAMPLER_ERR_OVERFLOW; 269 | *result = remain * num / den + major * num; 270 | return RESAMPLER_ERR_SUCCESS; 271 | } 272 | 273 | static int update_filter(SpeexResamplerState *st) 274 | { 275 | spx_uint32_t old_length = st->filt_len; 276 | spx_uint32_t old_alloc_size = st->mem_alloc_size; 277 | spx_uint32_t min_sinc_table_length; 278 | spx_uint32_t min_alloc_size; 279 | 280 | spx_int32_t i, j; 281 | 282 | st->int_advance = st->num_rate/st->den_rate; 283 | st->frac_advance = st->num_rate%st->den_rate; 284 | st->oversample = quality_map[st->quality].oversample; 285 | st->filt_len = quality_map[st->quality].base_length; 286 | 287 | if (st->num_rate > st->den_rate) 288 | { 289 | /* down-sampling */ 290 | st->cutoff = quality_map[st->quality].downsample_bandwidth * st->den_rate / st->num_rate; 291 | if (multiply_frac(&st->filt_len,st->filt_len,st->num_rate,st->den_rate) != RESAMPLER_ERR_SUCCESS) 292 | goto fail; 293 | /* Round up to make sure we have a multiple of 8 for SSE */ 294 | st->filt_len = ((st->filt_len-1)&(~0x7))+8; 295 | if (2*st->den_rate < st->num_rate) 296 | st->oversample >>= 1; 297 | if (4*st->den_rate < st->num_rate) 298 | st->oversample >>= 1; 299 | if (8*st->den_rate < st->num_rate) 300 | st->oversample >>= 1; 301 | if (16*st->den_rate < st->num_rate) 302 | st->oversample >>= 1; 303 | if (st->oversample < 1) 304 | st->oversample = 1; 305 | } else { 306 | /* up-sampling */ 307 | st->cutoff = quality_map[st->quality].upsample_bandwidth; 308 | } 309 | 310 | if (INT_MAX/sizeof(spx_word16_t)/st->den_rate < st->filt_len) 311 | goto fail; 312 | 313 | min_sinc_table_length = st->filt_len*st->den_rate; 314 | 315 | if (st->sinc_table_length < min_sinc_table_length) 316 | { 317 | spx_word16_t *sinc_table = speex_realloc(st->sinc_table,min_sinc_table_length*sizeof(spx_word16_t)); 318 | if (!sinc_table) 319 | goto fail; 320 | 321 | st->sinc_table = sinc_table; 322 | st->sinc_table_length = min_sinc_table_length; 323 | } 324 | for (i=0;i<(spx_int32_t)st->den_rate;i++) 325 | for (j=0;j<(spx_int32_t)st->filt_len;j++) 326 | st->sinc_table[i*st->filt_len+j] = sinc(st->cutoff,((j-(spx_int32_t)st->filt_len/2+1) 327 | - ((float)i)/st->den_rate), st->filt_len 328 | , quality_map[st->quality].window_func); 329 | 330 | st->resampler_ptr = resampler_basic_direct_single; 331 | 332 | /*fprintf (stderr, "resampler uses direct sinc table and normalised cutoff %f\n", cutoff);*/ 333 | 334 | /* Here's the place where we update the filter memory to take into account 335 | the change in filter length. It's probably the messiest part of the code 336 | due to handling of lots of corner cases. */ 337 | 338 | /* Adding buffer_size to filt_len won't overflow here because filt_len 339 | could be multiplied by sizeof(spx_word16_t) above. */ 340 | min_alloc_size = st->filt_len-1 + st->buffer_size; 341 | if (min_alloc_size > st->mem_alloc_size) 342 | { 343 | spx_word16_t *mem; 344 | if (INT_MAX/sizeof(spx_word16_t)/st->nb_channels < min_alloc_size) 345 | goto fail; 346 | else if (!(mem = (spx_word16_t*)speex_realloc(st->mem, st->nb_channels*min_alloc_size * sizeof(*mem)))) 347 | goto fail; 348 | 349 | st->mem = mem; 350 | st->mem_alloc_size = min_alloc_size; 351 | } 352 | if (!st->started) 353 | { 354 | spx_uint32_t i; 355 | for (i=0;inb_channels*st->mem_alloc_size;i++) 356 | st->mem[i] = 0; 357 | /*speex_warning("reinit filter");*/ 358 | } else if (st->filt_len > old_length) 359 | { 360 | spx_uint32_t i; 361 | /* Increase the filter length */ 362 | /*speex_warning("increase filter size");*/ 363 | for (i=st->nb_channels;i--;) 364 | { 365 | spx_uint32_t j; 366 | spx_uint32_t olen = old_length; 367 | /*if (st->magic_samples[i])*/ 368 | { 369 | /* Try and remove the magic samples as if nothing had happened */ 370 | 371 | /* FIXME: This is wrong but for now we need it to avoid going over the array bounds */ 372 | olen = old_length + 2*st->magic_samples[i]; 373 | for (j=old_length-1+st->magic_samples[i];j--;) 374 | st->mem[i*st->mem_alloc_size+j+st->magic_samples[i]] = st->mem[i*old_alloc_size+j]; 375 | for (j=0;jmagic_samples[i];j++) 376 | st->mem[i*st->mem_alloc_size+j] = 0; 377 | st->magic_samples[i] = 0; 378 | } 379 | if (st->filt_len > olen) 380 | { 381 | /* If the new filter length is still bigger than the "augmented" length */ 382 | /* Copy data going backward */ 383 | for (j=0;jmem[i*st->mem_alloc_size+(st->filt_len-2-j)] = st->mem[i*st->mem_alloc_size+(olen-2-j)]; 385 | /* Then put zeros for lack of anything better */ 386 | for (;jfilt_len-1;j++) 387 | st->mem[i*st->mem_alloc_size+(st->filt_len-2-j)] = 0; 388 | /* Adjust last_sample */ 389 | st->last_sample[i] += (st->filt_len - olen)/2; 390 | } else { 391 | /* Put back some of the magic! */ 392 | st->magic_samples[i] = (olen - st->filt_len)/2; 393 | for (j=0;jfilt_len-1+st->magic_samples[i];j++) 394 | st->mem[i*st->mem_alloc_size+j] = st->mem[i*st->mem_alloc_size+j+st->magic_samples[i]]; 395 | } 396 | } 397 | } else if (st->filt_len < old_length) 398 | { 399 | spx_uint32_t i; 400 | /* Reduce filter length, this a bit tricky. We need to store some of the memory as "magic" 401 | samples so they can be used directly as input the next time(s) */ 402 | for (i=0;inb_channels;i++) 403 | { 404 | spx_uint32_t j; 405 | spx_uint32_t old_magic = st->magic_samples[i]; 406 | st->magic_samples[i] = (old_length - st->filt_len)/2; 407 | /* We must copy some of the memory that's no longer used */ 408 | /* Copy data going backward */ 409 | for (j=0;jfilt_len-1+st->magic_samples[i]+old_magic;j++) 410 | st->mem[i*st->mem_alloc_size+j] = st->mem[i*st->mem_alloc_size+j+st->magic_samples[i]]; 411 | st->magic_samples[i] += old_magic; 412 | } 413 | } 414 | return RESAMPLER_ERR_SUCCESS; 415 | 416 | fail: 417 | st->resampler_ptr = NULL; 418 | /* st->mem may still contain consumed input samples for the filter. 419 | Restore filt_len so that filt_len - 1 still points to the position after 420 | the last of these samples. */ 421 | st->filt_len = old_length; 422 | // MessageBoxA(NULL, "RESAMPLER_ERR_ALLOC_FAILED", "in_OPUS_resampler", 0); 423 | 424 | return RESAMPLER_ERR_ALLOC_FAILED; 425 | 426 | } // END update_filter(SpeexResamplerState *st) 427 | 428 | 429 | SpeexResamplerState *speex_resampler_init(spx_uint32_t nb_channels, spx_uint32_t in_rate, spx_uint32_t out_rate, int quality, int *err) 430 | { 431 | return speex_resampler_init_frac(nb_channels, in_rate, out_rate, in_rate, out_rate, quality, err); 432 | } 433 | 434 | SpeexResamplerState *speex_resampler_init_frac(spx_uint32_t nb_channels 435 | , spx_uint32_t ratio_num 436 | , spx_uint32_t ratio_den 437 | , spx_uint32_t in_rate 438 | , spx_uint32_t out_rate 439 | , int quality 440 | , int *err) 441 | { 442 | SpeexResamplerState *st; 443 | int filter_err; 444 | #ifdef __SSE1 445 | __builtin_cpu_init(); 446 | HAVE_SSE=__builtin_cpu_supports("sse"); 447 | #endif 448 | // if(HAVE_SSE)MessageBoxA(NULL, "SSE", "We have SSE", 0); 449 | 450 | if (nb_channels == 0 451 | || ratio_num == 0 452 | || ratio_den == 0 453 | || quality > SPEEX_RESAMPLER_QUALITY_MAX 454 | || quality < SPEEX_RESAMPLER_QUALITY_MIN) 455 | { 456 | if (err) 457 | *err = RESAMPLER_ERR_INVALID_ARG; 458 | return NULL; 459 | } 460 | st = speex_alloc(sizeof(SpeexResamplerState)); 461 | if (!st) 462 | { 463 | if (err) 464 | *err = RESAMPLER_ERR_ALLOC_FAILED; 465 | return NULL; 466 | } 467 | st->initialised = 0; 468 | st->started = 0; 469 | st->in_rate = 0; 470 | st->out_rate = 0; 471 | st->num_rate = 0; 472 | st->den_rate = 0; 473 | st->quality = -1; 474 | st->sinc_table_length = 0; 475 | st->mem_alloc_size = 0; 476 | st->filt_len = 0; 477 | st->mem = 0; 478 | st->resampler_ptr = 0; 479 | 480 | st->cutoff = 1.f; 481 | st->nb_channels = nb_channels; 482 | st->in_stride = 1; 483 | st->out_stride = 1; 484 | 485 | st->buffer_size = 160; 486 | 487 | /* Per channel data */ 488 | if (!(st->last_sample = speex_alloc(nb_channels*sizeof(spx_int32_t)))) 489 | goto fail; 490 | if (!(st->magic_samples = speex_alloc(nb_channels*sizeof(spx_uint32_t)))) 491 | goto fail; 492 | if (!(st->samp_frac_num = speex_alloc(nb_channels*sizeof(spx_uint32_t)))) 493 | goto fail; 494 | 495 | speex_resampler_set_quality(st, quality); 496 | speex_resampler_set_rate_frac(st, ratio_num, ratio_den, in_rate, out_rate); 497 | 498 | filter_err = update_filter(st); 499 | if (filter_err == RESAMPLER_ERR_SUCCESS) 500 | { 501 | st->initialised = 1; 502 | } else { 503 | speex_resampler_destroy(st); 504 | st = NULL; 505 | } 506 | if (err) 507 | *err = filter_err; 508 | 509 | return st; 510 | 511 | fail: 512 | if (err) 513 | *err = RESAMPLER_ERR_ALLOC_FAILED; 514 | speex_resampler_destroy(st); 515 | return NULL; 516 | } 517 | 518 | void speex_resampler_destroy(SpeexResamplerState *st) 519 | { 520 | speex_free(st->mem); 521 | speex_free(st->sinc_table); 522 | speex_free(st->last_sample); 523 | speex_free(st->magic_samples); 524 | speex_free(st->samp_frac_num); 525 | speex_free(st); 526 | } 527 | 528 | static int speex_resampler_process_native( 529 | SpeexResamplerState *__restrict st, spx_uint32_t channel_index, 530 | spx_uint32_t *in_len, 531 | spx_word16_t *out, spx_uint32_t *out_len) 532 | { 533 | int j=0; 534 | const int N = st->filt_len; 535 | int out_sample = 0; 536 | spx_word16_t *mem = st->mem + channel_index * st->mem_alloc_size; 537 | spx_uint32_t ilen; 538 | 539 | st->started = 1; 540 | 541 | /* Call the right resampler through the function ptr */ 542 | out_sample = st->resampler_ptr(st, channel_index, mem, in_len, out, out_len); 543 | 544 | if (st->last_sample[channel_index] < (spx_int32_t)*in_len) 545 | *in_len = st->last_sample[channel_index]; 546 | *out_len = out_sample; 547 | st->last_sample[channel_index] -= *in_len; 548 | 549 | ilen = *in_len; 550 | 551 | for(j=0;jmagic_samples[channel_index]; 559 | spx_word16_t *mem = st->mem + channel_index * st->mem_alloc_size; 560 | const int N = st->filt_len; 561 | 562 | speex_resampler_process_native(st, channel_index, &tmp_in_len, *out, &out_len); 563 | 564 | st->magic_samples[channel_index] -= tmp_in_len; 565 | 566 | /* If we couldn't process all "magic" input samples, save the rest for next time */ 567 | if (st->magic_samples[channel_index]) 568 | { 569 | spx_uint32_t i; 570 | for (i=0;imagic_samples[channel_index];i++) 571 | mem[N-1+i]=mem[N-1+i+tmp_in_len]; 572 | } 573 | *out += out_len*st->out_stride; 574 | return out_len; 575 | } 576 | 577 | static inline int speex_resampler_process_float( 578 | SpeexResamplerState *__restrict st, spx_uint32_t channel_index, 579 | const float *__restrict in, spx_uint32_t *in_len, 580 | float *out, spx_uint32_t *out_len) 581 | { 582 | spx_uint32_t j; 583 | spx_uint32_t ilen = *in_len; 584 | spx_uint32_t olen = *out_len; 585 | spx_word16_t *x = st->mem + channel_index * st->mem_alloc_size; 586 | const int filt_offs = st->filt_len - 1; 587 | const spx_uint32_t xlen = st->mem_alloc_size - filt_offs; 588 | const int istride = st->in_stride; 589 | 590 | if(st->resampler_ptr == NULL) return RESAMPLER_ERR_ALLOC_FAILED; 591 | 592 | if (st->magic_samples[channel_index]) 593 | olen -= speex_resampler_magic(st, channel_index, &out, olen); 594 | if (! st->magic_samples[channel_index]) { 595 | while (ilen && olen) { 596 | spx_uint32_t ichunk = (ilen > xlen) ? xlen : ilen; 597 | spx_uint32_t ochunk = olen; 598 | 599 | if (in) { 600 | for(j=0;jout_stride; 610 | if (in) 611 | in += ichunk * istride; 612 | } 613 | } 614 | *in_len -= ilen; 615 | *out_len -= olen; 616 | 617 | return RESAMPLER_ERR_SUCCESS; 618 | } 619 | 620 | int speex_resampler_process_interleaved_float( 621 | SpeexResamplerState *__restrict st, 622 | const float *__restrict in, spx_uint32_t *in_len, 623 | float *__restrict out, spx_uint32_t *out_len) 624 | { 625 | spx_uint32_t i; 626 | int istride_save, ostride_save; 627 | spx_uint32_t bak_out_len = *out_len; 628 | spx_uint32_t bak_in_len = *in_len; 629 | 630 | istride_save = st->in_stride; 631 | ostride_save = st->out_stride; 632 | st->in_stride = st->out_stride = st->nb_channels; 633 | 634 | if (in == NULL) return RESAMPLER_ERR_INVALID_ARG; 635 | if(st->resampler_ptr == NULL) return RESAMPLER_ERR_ALLOC_FAILED; 636 | 637 | for (i=0 ; i < st->nb_channels; i++) 638 | { 639 | *out_len = bak_out_len; 640 | *in_len = bak_in_len; 641 | speex_resampler_process_float(st, i, in+i, in_len, out+i, out_len); 642 | } 643 | st->in_stride = istride_save; 644 | st->out_stride = ostride_save; 645 | 646 | return RESAMPLER_ERR_SUCCESS; 647 | } 648 | 649 | int speex_resampler_set_rate(SpeexResamplerState *st, spx_uint32_t in_rate, spx_uint32_t out_rate) 650 | { 651 | return speex_resampler_set_rate_frac(st, in_rate, out_rate, in_rate, out_rate); 652 | } 653 | 654 | void speex_resampler_get_rate(SpeexResamplerState *st, spx_uint32_t *in_rate, spx_uint32_t *out_rate) 655 | { 656 | *in_rate = st->in_rate; 657 | *out_rate = st->out_rate; 658 | } 659 | 660 | static inline spx_uint32_t compute_gcd(spx_uint32_t a, spx_uint32_t b) 661 | { 662 | while (b != 0) 663 | { 664 | spx_uint32_t temp = a; 665 | 666 | a = b; 667 | b = temp % b; 668 | } 669 | return a; 670 | } 671 | int speex_resampler_set_rate_frac(SpeexResamplerState *st, spx_uint32_t ratio_num, spx_uint32_t ratio_den, spx_uint32_t in_rate, spx_uint32_t out_rate) 672 | { 673 | spx_uint32_t fact; 674 | spx_uint32_t old_den; 675 | spx_uint32_t i; 676 | 677 | if (ratio_num == 0 || ratio_den == 0) 678 | return RESAMPLER_ERR_INVALID_ARG; 679 | 680 | if (st->in_rate == in_rate && st->out_rate == out_rate && st->num_rate == ratio_num && st->den_rate == ratio_den) 681 | return RESAMPLER_ERR_SUCCESS; 682 | 683 | old_den = st->den_rate; 684 | st->in_rate = in_rate; 685 | st->out_rate = out_rate; 686 | st->num_rate = ratio_num; 687 | st->den_rate = ratio_den; 688 | 689 | fact = compute_gcd(st->num_rate, st->den_rate); 690 | 691 | st->num_rate /= fact; 692 | st->den_rate /= fact; 693 | 694 | if (old_den > 0) 695 | { 696 | for (i=0;inb_channels;i++) 697 | { 698 | if (multiply_frac(&st->samp_frac_num[i],st->samp_frac_num[i],st->den_rate,old_den) != RESAMPLER_ERR_SUCCESS) 699 | return RESAMPLER_ERR_OVERFLOW; 700 | /* Safety net */ 701 | if (st->samp_frac_num[i] >= st->den_rate) 702 | st->samp_frac_num[i] = st->den_rate-1; 703 | } 704 | } 705 | 706 | if (st->initialised) 707 | return update_filter(st); 708 | return RESAMPLER_ERR_SUCCESS; 709 | } 710 | 711 | void speex_resampler_get_ratio(SpeexResamplerState *st, spx_uint32_t *ratio_num, spx_uint32_t *ratio_den) 712 | { 713 | *ratio_num = st->num_rate; 714 | *ratio_den = st->den_rate; 715 | } 716 | 717 | int speex_resampler_set_quality(SpeexResamplerState *st, int quality) 718 | { 719 | if (quality > SPEEX_RESAMPLER_QUALITY_MAX || quality < SPEEX_RESAMPLER_QUALITY_MIN) 720 | return RESAMPLER_ERR_INVALID_ARG; 721 | if (st->quality == quality) 722 | return RESAMPLER_ERR_SUCCESS; 723 | st->quality = quality; 724 | if (st->initialised) 725 | return update_filter(st); 726 | return RESAMPLER_ERR_SUCCESS; 727 | } 728 | void speex_resampler_get_quality(SpeexResamplerState *st, int *quality) 729 | { 730 | *quality = st->quality; 731 | } 732 | 733 | void speex_resampler_set_input_stride(SpeexResamplerState *st, spx_uint32_t stride) 734 | { 735 | st->in_stride = stride; 736 | } 737 | 738 | void speex_resampler_get_input_stride(SpeexResamplerState *st, spx_uint32_t *stride) 739 | { 740 | *stride = st->in_stride; 741 | } 742 | 743 | void speex_resampler_set_output_stride(SpeexResamplerState *st, spx_uint32_t stride) 744 | { 745 | st->out_stride = stride; 746 | } 747 | 748 | void speex_resampler_get_output_stride(SpeexResamplerState *st, spx_uint32_t *stride) 749 | { 750 | *stride = st->out_stride; 751 | } 752 | 753 | int speex_resampler_get_input_latency(SpeexResamplerState *st) 754 | { 755 | return st->filt_len / 2; 756 | } 757 | 758 | int speex_resampler_get_output_latency(SpeexResamplerState *st) 759 | { 760 | return ((st->filt_len / 2) * st->den_rate + (st->num_rate >> 1)) / st->num_rate; 761 | } 762 | 763 | int speex_resampler_skip_zeros(SpeexResamplerState *st) 764 | { 765 | spx_uint32_t i; 766 | for (i=0;inb_channels;i++) 767 | st->last_sample[i] = st->filt_len/2; 768 | return RESAMPLER_ERR_SUCCESS; 769 | } 770 | 771 | int speex_resampler_reset_mem(SpeexResamplerState *st) 772 | { 773 | spx_uint32_t i; 774 | for (i=0;inb_channels;i++) 775 | { 776 | st->last_sample[i] = 0; 777 | st->magic_samples[i] = 0; 778 | st->samp_frac_num[i] = 0; 779 | } 780 | for (i=0;inb_channels*(st->filt_len-1);i++) 781 | st->mem[i] = 0; 782 | return RESAMPLER_ERR_SUCCESS; 783 | } 784 | 785 | const char *speex_resampler_strerror(int err) 786 | { 787 | switch (err) 788 | { 789 | case RESAMPLER_ERR_SUCCESS: 790 | return "Success."; 791 | case RESAMPLER_ERR_ALLOC_FAILED: 792 | return "Memory allocation failed."; 793 | case RESAMPLER_ERR_BAD_STATE: 794 | return "Bad resampler state."; 795 | case RESAMPLER_ERR_INVALID_ARG: 796 | return "Invalid argument."; 797 | case RESAMPLER_ERR_PTR_OVERLAP: 798 | return "Input and output buffers overlap."; 799 | default: 800 | return "Unknown error. Bad error code or strange version mismatch."; 801 | } 802 | } 803 | 804 | 805 | void float2int_dither(void *__restrict _dst, const float *__restrict _src ,int _nsamples, char bps, char use_dithering) 806 | { 807 | int i, si, silent; 808 | static unsigned short seed, tmpseed; 809 | static unsigned mute=16384; 810 | float Gain, r, s; 811 | union{ 812 | int integer; 813 | char byte[3]; 814 | } int24b; 815 | /* Set Correct Gain */ 816 | if (bps==32) Gain=OP_GAIN32; 817 | else if(bps==24) Gain=OP_GAIN24; 818 | else if(bps== 8) Gain=OP_GAIN8; 819 | else Gain=OP_GAIN16; 820 | 821 | for(i=0; i<_nsamples; i++){ 822 | s=_src[i]; 823 | s*=Gain; 824 | si=lrintf(s); 825 | 826 | if(use_dithering){ 827 | if(si == 0) silent=1; 828 | else silent=0; 829 | 830 | if(mute<8192){ /* ~200ms */ 831 | seed=RANDG(seed); 832 | tmpseed=RANDG(seed); 833 | r = ((int)tmpseed-(int)seed)*OP_PRNG_GAINS; 834 | s+=r; 835 | } 836 | 837 | if(bps==16) { 838 | short *dst16=(short *)_dst; 839 | dst16[i]=(short)WORD2INT16(s); 840 | 841 | } else if(bps==8) { 842 | unsigned char *dst8=(unsigned char *)_dst; 843 | dst8[i]=0x80^WORD2INT8(s); 844 | 845 | } else if(bps==24){ 846 | char *buf_=(char *)_dst; 847 | int24b.integer=WORD2INT24(s); 848 | buf_[3*i+0] = int24b.byte[0]; 849 | buf_[3*i+1] = int24b.byte[1]; 850 | buf_[3*i+2] = int24b.byte[2]; 851 | 852 | } else if (bps==32) { 853 | int *buf32=(int *)_dst; 854 | buf32[i]=WORD2INT32(s); //32b mode 855 | } 856 | if(!silent)mute=0; 857 | else mute++; 858 | } else { // No dithering... 859 | if(bps==16) { 860 | short *dst16=(short *)_dst; 861 | dst16[i]=(short)OP_CLAMP(-32768,si,+32767); 862 | 863 | } else if(bps==8) { 864 | unsigned char *dst8=(unsigned char *)_dst; 865 | dst8[i]=0x80^OP_CLAMP(-128,si,+127); 866 | 867 | } else if(bps==24){ 868 | char *buf_=(char *)_dst; 869 | int24b.integer=OP_CLAMP(-8388608,si,+8388607); 870 | buf_[3*i+0] = int24b.byte[0]; 871 | buf_[3*i+1] = int24b.byte[1]; 872 | buf_[3*i+2] = int24b.byte[2]; 873 | 874 | } else if (bps==32) { 875 | int *buf32=(int *)_dst; 876 | buf32[i]=WORD2INT32(s); //32b mode 877 | } 878 | } // end if dithering/else 879 | } 880 | mute=MIN(mute, 16384); 881 | } 882 | 883 | //inline double floorz(double n) 884 | //{ 885 | // long long i=(long long)n; 886 | // return i>=0? i : i-1; 887 | //} 888 | -------------------------------------------------------------------------------- /resample.h: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2007 Jean-Marc Valin 2 | * 3 | * File: speex_resampler.h 4 | * Resampling code 5 | * 6 | * The design goals of this code are: 7 | * - Very fast algorithm 8 | * - Low memory requirement 9 | * - Good *perceptual* quality (and not best SNR) 10 | * 11 | * Redistribution and use in source and binary forms, with or without 12 | * modification, are permitted provided that the following conditions are 13 | * met: 14 | * 15 | * 1. Redistributions of source code must retain the above copyright notice, 16 | * this list of conditions and the following disclaimer. 17 | * 18 | * 2. Redistributions in binary form must reproduce the above copyright 19 | * notice, this list of conditions and the following disclaimer in the 20 | * documentation and/or other materials provided with the distribution. 21 | * 22 | * 3. The name of the author may not be used to endorse or promote products 23 | * derived from this software without specific prior written permission. 24 | */ 25 | 26 | #ifndef SPEEX_RESAMPLER_H 27 | 28 | #define SPEEX_RESAMPLER_H 29 | 30 | #define spx_int16_t short 31 | #define spx_int32_t int 32 | #define spx_uint16_t unsigned short 33 | #define spx_uint32_t unsigned int 34 | 35 | #define WORD2INT8U(x) ((x) < 0.5f ? 0 : \ 36 | ((x) > 254.5f ? 255 : lrintf(x))) 37 | #define WORD2INT8(x) ((x) < -127.5f ? -128 : \ 38 | ((x) > 126.5f ? 127 : lrintf(x))) 39 | #define WORD2INT16(x) ((x) < -32767.5f ? -32768 : \ 40 | ((x) > 32766.5f ? 32767 : (short)lrintf(x))) 41 | #define WORD2INT24(x) ((x) < -8388607.5f ? -8388608 : \ 42 | ((x) > 8388606.5 ? 8388607 : lrintf(x))) 43 | #define WORD2INT32(x) ((x) < -2147483647.5f ? -2147483648 : \ 44 | ((x) > 2147483646.5f ? 2147483647 : lrintf(x))) 45 | 46 | #define OP_GAIN8 (126.F) 47 | #define OP_GAIN16 (32766.F) 48 | #define OP_GAIN24 (8388606.F) 49 | #define OP_GAIN32 (2147483646.F) 50 | #define OP_PRNG_GAINS (1.0F/0xFFFF) 51 | #define RANDG(seed) (1103515245 * (seed) + 12345) 52 | #define MIN(a,b) ((a) < (b) ? (a) : (b)) 53 | #define OP_CLAMP(_l,_x,_h) ((_x)<(_l)?(_l):((_x)>(_h)?(_h):(_x))) 54 | 55 | #ifdef __cplusplus 56 | extern "C" { 57 | #endif 58 | 59 | #define SPEEX_RESAMPLER_QUALITY_MAX 4 60 | #define SPEEX_RESAMPLER_QUALITY_MIN 0 61 | #define SPEEX_RESAMPLER_QUALITY_DEFAULT 3 62 | #define SPEEX_RESAMPLER_QUALITY_VOIP 2 63 | #define SPEEX_RESAMPLER_QUALITY_DESKTOP 4 64 | 65 | enum { 66 | RESAMPLER_ERR_SUCCESS = 0, 67 | RESAMPLER_ERR_ALLOC_FAILED = 1, 68 | RESAMPLER_ERR_BAD_STATE = 2, 69 | RESAMPLER_ERR_INVALID_ARG = 3, 70 | RESAMPLER_ERR_PTR_OVERLAP = 4, 71 | RESAMPLER_ERR_OVERFLOW = 5, 72 | 73 | RESAMPLER_ERR_MAX_ERROR 74 | }; 75 | 76 | struct SpeexResamplerState_; 77 | typedef struct SpeexResamplerState_ SpeexResamplerState; 78 | 79 | /** Create a new resampler with integer input and output rates. 80 | * @param nb_channels Number of channels to be processed 81 | * @param in_rate Input sampling rate (integer number of Hz). 82 | * @param out_rate Output sampling rate (integer number of Hz). 83 | * @param quality Resampling quality between 0 and 10, where 0 has poor quality 84 | * and 10 has very high quality. 85 | * @return Newly created resampler state 86 | * @retval NULL Error: not enough memory 87 | */ 88 | SpeexResamplerState *speex_resampler_init(spx_uint32_t nb_channels, 89 | spx_uint32_t in_rate, 90 | spx_uint32_t out_rate, 91 | int quality, 92 | int *err); 93 | 94 | /** Create a new resampler with fractional input/output rates. The sampling 95 | * rate ratio is an arbitrary rational number with both the numerator and 96 | * denominator being 32-bit integers. 97 | * @param nb_channels Number of channels to be processed 98 | * @param ratio_num Numerator of the sampling rate ratio 99 | * @param ratio_den Denominator of the sampling rate ratio 100 | * @param in_rate Input sampling rate rounded to the nearest integer (in Hz). 101 | * @param out_rate Output sampling rate rounded to the nearest integer (in Hz). 102 | * @param quality Resampling quality between 0 and 10, where 0 has poor quality 103 | * and 10 has very high quality. 104 | * @return Newly created resampler state 105 | * @retval NULL Error: not enough memory 106 | */ 107 | SpeexResamplerState *speex_resampler_init_frac(spx_uint32_t nb_channels, 108 | spx_uint32_t ratio_num, 109 | spx_uint32_t ratio_den, 110 | spx_uint32_t in_rate, 111 | spx_uint32_t out_rate, 112 | int quality, 113 | int *err); 114 | 115 | /** Destroy a resampler state. 116 | * @param st Resampler state 117 | */ 118 | void speex_resampler_destroy(SpeexResamplerState *st); 119 | 120 | /** Resample an interleaved float array. The input and output buffers must *not* overlap. 121 | * @param st Resampler state 122 | * @param in Input buffer 123 | * @param in_len Number of input samples in the input buffer. Returns the number 124 | * of samples processed. This is all per-channel. 125 | * @param out Output buffer 126 | * @param out_len Size of the output buffer. Returns the number of samples written. 127 | * This is all per-channel. 128 | */ 129 | int speex_resampler_process_interleaved_float(SpeexResamplerState *__restrict st, 130 | const float *__restrict in, 131 | spx_uint32_t *in_len, 132 | float *__restrict out, 133 | spx_uint32_t *out_len); 134 | 135 | /** Set (change) the input/output sampling rates (integer value). 136 | * @param st Resampler state 137 | * @param in_rate Input sampling rate (integer number of Hz). 138 | * @param out_rate Output sampling rate (integer number of Hz). 139 | */ 140 | int speex_resampler_set_rate(SpeexResamplerState *st, 141 | spx_uint32_t in_rate, 142 | spx_uint32_t out_rate); 143 | 144 | /** Get the current input/output sampling rates (integer value). 145 | * @param st Resampler state 146 | * @param in_rate Input sampling rate (integer number of Hz) copied. 147 | * @param out_rate Output sampling rate (integer number of Hz) copied. 148 | */ 149 | void speex_resampler_get_rate(SpeexResamplerState *st, 150 | spx_uint32_t *in_rate, 151 | spx_uint32_t *out_rate); 152 | 153 | /** Set (change) the input/output sampling rates and resampling ratio 154 | * (fractional values in Hz supported). 155 | * @param st Resampler state 156 | * @param ratio_num Numerator of the sampling rate ratio 157 | * @param ratio_den Denominator of the sampling rate ratio 158 | * @param in_rate Input sampling rate rounded to the nearest integer (in Hz). 159 | * @param out_rate Output sampling rate rounded to the nearest integer (in Hz). 160 | */ 161 | int speex_resampler_set_rate_frac(SpeexResamplerState *st, 162 | spx_uint32_t ratio_num, 163 | spx_uint32_t ratio_den, 164 | spx_uint32_t in_rate, 165 | spx_uint32_t out_rate); 166 | 167 | /** Get the current resampling ratio. This will be reduced to the least 168 | * common denominator. 169 | * @param st Resampler state 170 | * @param ratio_num Numerator of the sampling rate ratio copied 171 | * @param ratio_den Denominator of the sampling rate ratio copied 172 | */ 173 | void speex_resampler_get_ratio(SpeexResamplerState *st, 174 | spx_uint32_t *ratio_num, 175 | spx_uint32_t *ratio_den); 176 | 177 | /** Set (change) the conversion quality. 178 | * @param st Resampler state 179 | * @param quality Resampling quality between 0 and 10, where 0 has poor 180 | * quality and 10 has very high quality. 181 | */ 182 | int speex_resampler_set_quality(SpeexResamplerState *st, 183 | int quality); 184 | 185 | /** Get the conversion quality. 186 | * @param st Resampler state 187 | * @param quality Resampling quality between 0 and 10, where 0 has poor 188 | * quality and 10 has very high quality. 189 | */ 190 | void speex_resampler_get_quality(SpeexResamplerState *st, 191 | int *quality); 192 | 193 | /** Set (change) the input stride. 194 | * @param st Resampler state 195 | * @param stride Input stride 196 | */ 197 | void speex_resampler_set_input_stride(SpeexResamplerState *st, 198 | spx_uint32_t stride); 199 | 200 | /** Get the input stride. 201 | * @param st Resampler state 202 | * @param stride Input stride copied 203 | */ 204 | void speex_resampler_get_input_stride(SpeexResamplerState *st, 205 | spx_uint32_t *stride); 206 | 207 | /** Set (change) the output stride. 208 | * @param st Resampler state 209 | * @param stride Output stride 210 | */ 211 | void speex_resampler_set_output_stride(SpeexResamplerState *st, 212 | spx_uint32_t stride); 213 | 214 | /** Get the output stride. 215 | * @param st Resampler state copied 216 | * @param stride Output stride 217 | */ 218 | void speex_resampler_get_output_stride(SpeexResamplerState *st, 219 | spx_uint32_t *stride); 220 | 221 | /** Get the latency introduced by the resampler measured in input samples. 222 | * @param st Resampler state 223 | */ 224 | int speex_resampler_get_input_latency(SpeexResamplerState *st); 225 | 226 | /** Get the latency introduced by the resampler measured in output samples. 227 | * @param st Resampler state 228 | */ 229 | int speex_resampler_get_output_latency(SpeexResamplerState *st); 230 | 231 | /** Make sure that the first samples to go out of the resamplers don't have 232 | * leading zeros. This is only useful before starting to use a newly created 233 | * resampler. It is recommended to use that when resampling an audio file, as 234 | * it will generate a file with the same length. For real-time processing, 235 | * it is probably easier not to use this call (so that the output duration 236 | * is the same for the first frame). 237 | * @param st Resampler state 238 | */ 239 | int speex_resampler_skip_zeros(SpeexResamplerState *st); 240 | 241 | /** Reset a resampler so a new (unrelated) stream can be processed. 242 | * @param st Resampler state 243 | */ 244 | int speex_resampler_reset_mem(SpeexResamplerState *st); 245 | 246 | /** Returns the English meaning for an error code 247 | * @param err Error code 248 | * @return English string 249 | */ 250 | const char *speex_resampler_strerror(int err); 251 | 252 | void float2int_dither(void *__restrict__ _dst, const float *__restrict__ _src ,int _nsamples, char bps, char use_dithering); 253 | 254 | #ifdef __cplusplus 255 | } 256 | #endif 257 | 258 | #endif 259 | -------------------------------------------------------------------------------- /resource.h: -------------------------------------------------------------------------------- 1 | // Used by resource.rc 2 | // 3 | #define IDC_RESET 3 4 | #define IDD_CONFIG 101 5 | #define IDD_INFOBOX 105 6 | 7 | #define IDC_ENABLE 2000 8 | #define IDC_ALBUM 2001 9 | #define IDC_LIMITER 2002 10 | #define IDC_COMMENT 2002 11 | #define IDC_PREAMP 2003 12 | #define IDC_YEAR 2003 13 | #define IDC_PA 2004 14 | #define IDC_TRACK 2004 15 | #define IDC_DITHER 2005 16 | #define IDC_DITHERRG 2006 17 | #define IDC_TO 2008 18 | #define IDC_SHAPE 2009 19 | #define IDC_TABS 2009 20 | #define IDC_TITLE 2010 21 | #define IDC_TAGZ_HELP 2011 22 | #define IDC_ARTIST 2011 23 | #define IDC_TAGZ_DEFAULT 2012 24 | #define IDC_SEP 2013 25 | #define IDC_NAME 2014 26 | #define IDC_INFO 2015 27 | #define IDC_GENRE 2017 28 | #define IDC_REMOVE 2020 29 | #define IDC_UPDATE 2021 30 | #define IDC_ID3V1 2030 31 | #define IDC_RESERVE 2032 32 | #define IDC_BPS 2036 33 | #define IDC_ERRORS 2037 34 | #define IDC_COMPOSER 2038 35 | #define IDC_DESCRIPTION 2039 36 | #define IDC_EXINFO 2040 37 | #define IDC_URL 2041 38 | #define IDC_ENCODER 2042 39 | #define IDC_PERFORMER 2043 40 | #define IDC_COPYRIGHT 2044 41 | #define IDC_TRACKTOTAL 2045 42 | 43 | // Next default values for new objects 44 | -------------------------------------------------------------------------------- /resource.rc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RamonUnch/in_opus/722f5373d750de5b9444fe4e644d98f842077098/resource.rc -------------------------------------------------------------------------------- /utf_ansi.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | ///////////////////////////////////////////////////////////////////////////// 7 | // Cool finction that can anhdle all UNICODE 8 | // It will generate real UTF-16, not USC-2, so some charracters will display 9 | // improperly on NT4 but it is beter than stoping at the first non USC-2... 10 | wchar_t *utf8_to_utf16(const char *utfs) 11 | { 12 | size_t si, di; 13 | wchar_t *dst; 14 | size_t len; 15 | unsigned char c0, c1, c2, c3; 16 | 17 | if (utfs == NULL) return NULL; 18 | 19 | len = strlen(utfs); 20 | /*Worst-case output is 1 wide character per 1 input character. */ 21 | dst = malloc( sizeof(*dst) * (len + 1) ); 22 | if (!dst) return NULL; 23 | 24 | for (di = si = 0; si < len; si++) { 25 | 26 | c0 = utfs[si]; 27 | 28 | if (!(c0 & 0x80)) { 29 | /* Start byte says this is a 1-BYTE SEQUENCE. 0xxxxxxx */ 30 | dst[di++] = (wchar_t) c0; 31 | continue; 32 | } else if ( ((c1 = utfs[si + 1]) & 0xC0) == 0x80 ) { 33 | /* Found at least one continuation byte. */ 34 | /* 110xxxxx 10xxxxxx */ 35 | if ((c0 & 0xE0) == 0xC0) { 36 | /* Start byte says this is a 2-BYTE SEQUENCE. */ 37 | dst[di++] = (c0 & 0x1F) << 6 | (c1 & 0x3F);; 38 | si++; 39 | continue; 40 | } else if ( ((c2 = utfs[si + 2]) & 0xC0) == 0x80 ) { 41 | /*F ound at least two continuation bytes. */ 42 | if ( (c0 & 0xF0) == 0xE0 ) { 43 | /* Start byte says this is a 3-BYTE SEQUENCE. (up to U+FFFF) */ 44 | /* 1110xxxx 10xxxxxx 10xxxxxx */ 45 | dst[di++] = (c0 & 0xF) << 12 | (c1 & 0x3F) << 6 | (c2 & 0x3F); 46 | si += 2; 47 | continue; 48 | } else if ( ((c3 = utfs[si + 3]) & 0xC0) == 0x80 ) { 49 | /*Found at least three continuation bytes. */ 50 | /* 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ 51 | if ((c0 & 0xF8) == 0xF0) { 52 | uint32_t w; 53 | /*Start byte says this is a 4-BYTE SEQUENCE. */ 54 | w = (c0 & 7) << 18 | (c1 & 0x3F) << 12 | (c2 & 0x3F) << 6 | (c3 & 0x3F); 55 | if (w >= 0x10000U /* && w < 0x110000U */) { 56 | /* Convert to a surrogate pair. */ 57 | w -= 0x10000U; 58 | dst[di++] = (wchar_t) (0xD800U + (w >> 10)); 59 | dst[di++] = (wchar_t) (0xDC00U + (w & 0x3FF)); 60 | si += 3; 61 | continue; 62 | } else { 63 | dst[di++] = (wchar_t) w; /* Overlong sequence */ 64 | } 65 | } 66 | } /*end else if c3*/ 67 | } /*end else if c2*/ 68 | } /*end else if c1*/ 69 | } /* next si (end for)*/ 70 | dst[di] = '\0'; 71 | 72 | return dst; 73 | } 74 | 75 | /* 76 | * DOS TO UNIX STRING CONVERTION 77 | * Very usefull because the Edit class stuff does 78 | * not like the 10 only files. 79 | */ 80 | char *unix2dos(const char *unixs) 81 | { 82 | char *doss; 83 | size_t l, i, j, dstlen=0; 84 | 85 | if(!unixs) return NULL; 86 | //l = strlen(unixs); 87 | for (l=0; unixs[l]; l++) { 88 | dstlen++; 89 | dstlen += unixs[l] == '\n'; 90 | } 91 | doss = (char *)malloc(dstlen * sizeof(char) + 16); 92 | if(!doss) return NULL; 93 | 94 | j = 0; 95 | if(*unixs == '\n'){ // attention au 1er '\n' 96 | doss[0] = '\r'; doss[1] = '\n'; 97 | j++; 98 | } 99 | 100 | for(i=0; i < l; i++){ 101 | 102 | if(unixs[i] == '\n' && i > 0 && unixs[i-1] != '\r'){ 103 | doss[j] = '\r'; 104 | j++; 105 | doss[j] = '\n'; 106 | } else { 107 | doss[j] = unixs[i]; 108 | } 109 | j++; 110 | } 111 | doss[j] = '\0'; //pour etre sur.... 112 | 113 | return doss; 114 | } 115 | 116 | ///////////////////////////////////////////////////////////////////////////// 117 | // Ststem normal UTF16 -> UTF8 conversion. 118 | // It stops translation as soon as a charracters gets out of USC-2 on NT4. 119 | static char *utf16_to_CP(const wchar_t *input, int cp) 120 | { 121 | char *dst=NULL; 122 | size_t BuffSize = 0, Result = 0; 123 | 124 | if(!input) return NULL; 125 | 126 | BuffSize = WideCharToMultiByte(cp, 0, input, -1, NULL, 0, 0, 0); 127 | dst = (char*) malloc(sizeof(char) * (BuffSize+4)); 128 | if(!dst) return NULL; 129 | 130 | Result = WideCharToMultiByte(cp, 0, input, -1, dst, BuffSize, 0, 0); 131 | if (Result > 0 && Result <= BuffSize){ 132 | dst[BuffSize-1]='\0'; 133 | return dst; 134 | } else return NULL; 135 | } 136 | char *utf16_to_utf8(const wchar_t *in) 137 | { 138 | return utf16_to_CP(in, CP_UTF8); 139 | } 140 | 141 | ////////////////////////////////////////////////////////////////////// 142 | // Convert from UTF-8 to local ANSI codepage (CP_ACP) for display 143 | char *utf8_to_ansi(const char *utfs) 144 | { 145 | wchar_t *utf16; 146 | if ((utf16 = utf8_to_utf16(utfs))) { 147 | char *dst = utf16_to_CP(utf16, CP_ACP); 148 | free(utf16); 149 | return dst; 150 | } 151 | 152 | return NULL; 153 | } 154 | -------------------------------------------------------------------------------- /winerrno.h: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | * * 3 | * THIS FILE IS PART OF THE libopusfile SOFTWARE CODEC SOURCE CODE. * 4 | * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * 5 | * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * 6 | * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * 7 | * * 8 | * THE libopusfile SOURCE CODE IS (C) COPYRIGHT 2012 * 9 | * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * 10 | * * 11 | ********************************************************************/ 12 | #if !defined(_opusfile_winerrno_h) 13 | # define _opusfile_winerrno_h (1) 14 | 15 | # include 16 | # include 17 | 18 | /* These conflict with the MSVC errno.h definitions, but we don't need to use 19 | * the original ones in any file that deals with sockets. 20 | * We could map the WSA errors to the errno.h ones (most of which are only 21 | * available on sufficiently new versions of MSVC), but they aren't ordered the 22 | * same, and given how rarely we actually look at the values, I don't think 23 | * it's worth a lookup table. 24 | */ 25 | 26 | # undef EWOULDBLOCK 27 | # undef EINPROGRESS 28 | # undef EALREADY 29 | # undef ENOTSOCK 30 | # undef EDESTADDRREQ 31 | # undef EMSGSIZE 32 | # undef EPROTOTYPE 33 | # undef ENOPROTOOPT 34 | # undef EPROTONOSUPPORT 35 | # undef EOPNOTSUPP 36 | # undef EAFNOSUPPORT 37 | # undef EADDRINUSE 38 | # undef EADDRNOTAVAIL 39 | # undef ENETDOWN 40 | # undef ENETUNREACH 41 | # undef ENETRESET 42 | # undef ECONNABORTED 43 | # undef ECONNRESET 44 | # undef ENOBUFS 45 | # undef EISCONN 46 | # undef ENOTCONN 47 | # undef ETIMEDOUT 48 | # undef ECONNREFUSED 49 | # undef ELOOP 50 | # undef ENAMETOOLONG 51 | # undef EHOSTUNREACH 52 | # undef ENOTEMPTY 53 | 54 | # define EWOULDBLOCK (WSAEWOULDBLOCK-WSABASEERR) 55 | # define EINPROGRESS (WSAEINPROGRESS-WSABASEERR) 56 | # define EALREADY (WSAEALREADY-WSABASEERR) 57 | # define ENOTSOCK (WSAENOTSOCK-WSABASEERR) 58 | # define EDESTADDRREQ (WSAEDESTADDRREQ-WSABASEERR) 59 | # define EMSGSIZE (WSAEMSGSIZE-WSABASEERR) 60 | # define EPROTOTYPE (WSAEPROTOTYPE-WSABASEERR) 61 | # define ENOPROTOOPT (WSAENOPROTOOPT-WSABASEERR) 62 | # define EPROTONOSUPPORT (WSAEPROTONOSUPPORT-WSABASEERR) 63 | # define ESOCKTNOSUPPORT (WSAESOCKTNOSUPPORT-WSABASEERR) 64 | # define EOPNOTSUPP (WSAEOPNOTSUPP-WSABASEERR) 65 | # define EPFNOSUPPORT (WSAEPFNOSUPPORT-WSABASEERR) 66 | # define EAFNOSUPPORT (WSAEAFNOSUPPORT-WSABASEERR) 67 | # define EADDRINUSE (WSAEADDRINUSE-WSABASEERR) 68 | # define EADDRNOTAVAIL (WSAEADDRNOTAVAIL-WSABASEERR) 69 | # define ENETDOWN (WSAENETDOWN-WSABASEERR) 70 | # define ENETUNREACH (WSAENETUNREACH-WSABASEERR) 71 | # define ENETRESET (WSAENETRESET-WSABASEERR) 72 | # define ECONNABORTED (WSAECONNABORTED-WSABASEERR) 73 | # define ECONNRESET (WSAECONNRESET-WSABASEERR) 74 | # define ENOBUFS (WSAENOBUFS-WSABASEERR) 75 | # define EISCONN (WSAEISCONN-WSABASEERR) 76 | # define ENOTCONN (WSAENOTCONN-WSABASEERR) 77 | # define ESHUTDOWN (WSAESHUTDOWN-WSABASEERR) 78 | # define ETOOMANYREFS (WSAETOOMANYREFS-WSABASEERR) 79 | # define ETIMEDOUT (WSAETIMEDOUT-WSABASEERR) 80 | # define ECONNREFUSED (WSAECONNREFUSED-WSABASEERR) 81 | # define ELOOP (WSAELOOP-WSABASEERR) 82 | # define ENAMETOOLONG (WSAENAMETOOLONG-WSABASEERR) 83 | # define EHOSTDOWN (WSAEHOSTDOWN-WSABASEERR) 84 | # define EHOSTUNREACH (WSAEHOSTUNREACH-WSABASEERR) 85 | # define ENOTEMPTY (WSAENOTEMPTY-WSABASEERR) 86 | # define EPROCLIM (WSAEPROCLIM-WSABASEERR) 87 | # define EUSERS (WSAEUSERS-WSABASEERR) 88 | # define EDQUOT (WSAEDQUOT-WSABASEERR) 89 | # define ESTALE (WSAESTALE-WSABASEERR) 90 | # define EREMOTE (WSAEREMOTE-WSABASEERR) 91 | 92 | #endif 93 | -------------------------------------------------------------------------------- /wspiapi.c: -------------------------------------------------------------------------------- 1 | #ifndef WIN32_LEAN_AND_MEAN 2 | #define WIN32_LEAN_AND_MEAN 3 | #endif 4 | 5 | #include 6 | #include "wspiapi.h" 7 | 8 | int WINAPI WspiapiQueryDNS(const char *pszNodeName, int iSocketType, int iProtocol, 9 | WORD wPort, char pszAlias[NI_MAXHOST], struct addrinfo **pptResult) 10 | { 11 | struct addrinfo **paddrinfo = pptResult; 12 | struct hostent *phost = NULL; 13 | char **h; 14 | 15 | *paddrinfo = NULL; 16 | pszAlias[0] = 0; 17 | phost = gethostbyname (pszNodeName); 18 | if (phost){ 19 | if (phost->h_addrtype == AF_INET && phost->h_length == sizeof(struct in_addr)){ 20 | for (h = phost->h_addr_list; *h != NULL; h++){ 21 | *paddrinfo = WspiapiNewAddrInfo (iSocketType, iProtocol, wPort, 22 | ((struct in_addr *) *h)->s_addr); 23 | if (!*paddrinfo) 24 | return EAI_MEMORY; 25 | paddrinfo = &((*paddrinfo)->ai_next); 26 | } 27 | } 28 | _WSPIAPI_STRCPY_S(pszAlias, NI_MAXHOST, phost->h_name); 29 | return 0; 30 | } 31 | switch(WSAGetLastError()) { 32 | case WSAHOST_NOT_FOUND: break; 33 | case WSATRY_AGAIN: return EAI_AGAIN; 34 | case WSANO_RECOVERY: return EAI_FAIL; 35 | case WSANO_DATA: return EAI_NODATA; 36 | default: break; 37 | } 38 | return EAI_NONAME; 39 | } 40 | 41 | void WINAPI WspiapiLegacyFreeAddrInfo (struct addrinfo *ptHead) 42 | { 43 | struct addrinfo *p; 44 | 45 | for (p = ptHead; p != NULL; p = ptHead){ 46 | if (p->ai_canonname) 47 | WspiapiFree (p->ai_canonname); 48 | if (p->ai_addr) 49 | WspiapiFree (p->ai_addr); 50 | ptHead = p->ai_next; 51 | WspiapiFree (p); 52 | } 53 | } 54 | 55 | int WINAPI WspiapiClone (WORD wPort, struct addrinfo *ptResult) 56 | { 57 | struct addrinfo *p = NULL; 58 | struct addrinfo *n = NULL; 59 | 60 | for (p = ptResult; p != NULL;){ 61 | n = WspiapiNewAddrInfo (SOCK_DGRAM, p->ai_protocol, wPort, 62 | ((struct sockaddr_in *) p->ai_addr)->sin_addr.s_addr); 63 | if (!n) break; 64 | n->ai_next = p->ai_next; 65 | p->ai_next = n; 66 | p = n->ai_next; 67 | } 68 | if (p != NULL) return EAI_MEMORY; 69 | 70 | return 0; 71 | } 72 | 73 | int WINAPI WspiapiLookupNode (const char *pszNodeName, 74 | int iSocketType, int iProtocol, 75 | WORD wPort, WINBOOL bAI_CANONNAME, 76 | struct addrinfo **pptResult) 77 | { 78 | int err = 0, cntAlias = 0; 79 | char name[NI_MAXHOST]; name[0] = '\0'; 80 | char alias[NI_MAXHOST]; alias[0] = '\0'; 81 | char *pname = name, *palias = alias, *tmp = NULL; 82 | 83 | _WSPIAPI_STRCPY_S(pname, NI_MAXHOST, pszNodeName); 84 | for (;;){ 85 | err = WspiapiQueryDNS (pszNodeName, iSocketType, iProtocol, wPort, palias, pptResult); 86 | if (err) break; 87 | if (*pptResult) break; 88 | cntAlias++; 89 | if (strlen (palias) == 0 || !strcmp (pname, palias) || cntAlias == 16){ 90 | err = EAI_FAIL; 91 | break; 92 | } 93 | WspiapiSwap(pname, palias, tmp); 94 | } 95 | if (!err && bAI_CANONNAME){ 96 | (*pptResult)->ai_canonname = WspiapiStrdup (palias); 97 | if (!(*pptResult)->ai_canonname) 98 | err = EAI_MEMORY; 99 | } 100 | return err; 101 | } 102 | 103 | char * WINAPI WspiapiStrdup (const char *pszString) 104 | { 105 | char *rstr; 106 | size_t szlen; 107 | 108 | if(!pszString) return NULL; 109 | szlen = strlen(pszString) + 1; 110 | rstr = (char *) WspiapiMalloc (szlen); 111 | if (!rstr) return NULL; 112 | strcpy (rstr, pszString); 113 | return rstr; 114 | } 115 | 116 | struct addrinfo * WINAPI WspiapiNewAddrInfo (int iSocketType, int iProtocol, WORD wPort, DWORD dwAddress) 117 | { 118 | struct addrinfo *n; 119 | struct sockaddr_in *pa; 120 | 121 | if ((n = (struct addrinfo *) WspiapiMalloc (sizeof (struct addrinfo))) == NULL) 122 | return NULL; 123 | if ((pa = (struct sockaddr_in *) WspiapiMalloc (sizeof(struct sockaddr_in))) == NULL){ 124 | WspiapiFree(n); 125 | return NULL; 126 | } 127 | pa->sin_family = AF_INET; 128 | pa->sin_port = wPort; 129 | pa->sin_addr.s_addr = dwAddress; 130 | n->ai_family = PF_INET; 131 | n->ai_socktype = iSocketType; 132 | n->ai_protocol = iProtocol; 133 | n->ai_addrlen = sizeof (struct sockaddr_in); 134 | n->ai_addr = (struct sockaddr *) pa; 135 | return n; 136 | } 137 | 138 | WINBOOL WINAPI WspiapiParseV4Address (const char *pszAddress, PDWORD pdwAddress) 139 | { 140 | DWORD dwAddress = 0; 141 | const char *h = NULL; 142 | int cnt; 143 | 144 | for (cnt = 0,h = pszAddress; *h != 0; h++) 145 | if (h[0] == '.') cnt++; 146 | if (cnt != 3) return FALSE; 147 | dwAddress = inet_addr (pszAddress); 148 | if (dwAddress == INADDR_NONE) return FALSE; 149 | *pdwAddress = dwAddress; 150 | 151 | return TRUE; 152 | } 153 | 154 | int WINAPI WspiapiLegacyGetAddrInfo(const char *pszNodeName, 155 | const char *pszServiceName, 156 | const struct addrinfo *ptHints, 157 | struct addrinfo **pptResult) 158 | { 159 | int err = 0, iFlags = 0, iFamily = PF_UNSPEC, iSocketType = 0, iProtocol = 0; 160 | struct in_addr inAddress; 161 | struct servent *svc = NULL; 162 | char *pc = NULL; 163 | WINBOOL isCloned = FALSE; 164 | WORD tcpPort = 0, udpPort = 0, port = 0; 165 | 166 | *pptResult = NULL; 167 | if (!pszNodeName && !pszServiceName) 168 | return EAI_NONAME; 169 | if (ptHints) { 170 | if (ptHints->ai_addrlen != 0 || ptHints->ai_canonname != NULL 171 | || ptHints->ai_addr!=NULL || ptHints->ai_next != NULL) 172 | return EAI_FAIL; 173 | iFlags = ptHints->ai_flags; 174 | if ((iFlags & AI_CANONNAME) != 0 && !pszNodeName) 175 | return EAI_BADFLAGS; 176 | iFamily = ptHints->ai_family; 177 | if (iFamily != PF_UNSPEC && iFamily != PF_INET) 178 | return EAI_FAMILY; 179 | iSocketType = ptHints->ai_socktype; 180 | if (iSocketType != 0 && iSocketType != SOCK_STREAM && iSocketType != SOCK_DGRAM 181 | && iSocketType != SOCK_RAW) 182 | return EAI_SOCKTYPE; 183 | iProtocol = ptHints->ai_protocol; 184 | } 185 | 186 | if (pszServiceName) { 187 | port = (WORD) strtoul (pszServiceName, &pc, 10); 188 | if(*pc == 0) { 189 | port = tcpPort = udpPort = htons (port); 190 | if (iSocketType == 0) { 191 | isCloned = TRUE; 192 | iSocketType = SOCK_STREAM; 193 | } 194 | } else { 195 | if (iSocketType == 0 || iSocketType == SOCK_DGRAM) { 196 | svc = getservbyname(pszServiceName, "udp"); 197 | if (svc) 198 | port = udpPort = svc->s_port; 199 | } 200 | if (iSocketType == 0 || iSocketType == SOCK_STREAM) { 201 | svc = getservbyname(pszServiceName, "tcp"); 202 | if (svc) 203 | port = tcpPort = svc->s_port; 204 | else { 205 | if(_stricmp(pszServiceName,"https")==0) 206 | port = tcpPort = 443; 207 | else if(_stricmp(pszServiceName,"http")==0) 208 | port = tcpPort = 80; 209 | } 210 | } 211 | if (port == 0) 212 | return (iSocketType ? EAI_SERVICE : EAI_NONAME); 213 | if (iSocketType==0) { 214 | iSocketType = (tcpPort) ? SOCK_STREAM : SOCK_DGRAM; 215 | isCloned = (tcpPort && udpPort); 216 | } 217 | } 218 | } 219 | if (!pszNodeName || WspiapiParseV4Address(pszNodeName,&inAddress.s_addr)) { 220 | if (!pszNodeName) { 221 | inAddress.s_addr = htonl ((iFlags & AI_PASSIVE) ? INADDR_ANY : INADDR_LOOPBACK); 222 | } 223 | *pptResult = WspiapiNewAddrInfo(iSocketType, iProtocol, port, inAddress.s_addr); 224 | if (!(*pptResult)) 225 | err = EAI_MEMORY; 226 | if (!err && pszNodeName) { 227 | (*pptResult)->ai_flags |= AI_NUMERICHOST; 228 | if (iFlags & AI_CANONNAME) { 229 | (*pptResult)->ai_canonname = WspiapiStrdup (inet_ntoa (inAddress)); 230 | if (!(*pptResult)->ai_canonname) 231 | err = EAI_MEMORY; 232 | } 233 | } 234 | } else if (iFlags & AI_NUMERICHOST) 235 | err = EAI_NONAME; 236 | else 237 | err = WspiapiLookupNode (pszNodeName, iSocketType, iProtocol, port, 238 | (iFlags & AI_CANONNAME), pptResult); 239 | if (!err && isCloned) 240 | err = WspiapiClone(udpPort, *pptResult); 241 | if (err) { 242 | WspiapiLegacyFreeAddrInfo (*pptResult); 243 | *pptResult = NULL; 244 | } 245 | return err; 246 | } 247 | 248 | int WINAPI WspiapiLegacyGetNameInfo(const struct sockaddr *ptSocketAddress, 249 | socklen_t tSocketLength, char *pszNodeName, size_t tNodeLength, 250 | char *pszServiceName, size_t tServiceLength, int iFlags) 251 | { 252 | struct servent *ptService; 253 | WORD wPort; 254 | char szBuffer[] = "65535"; 255 | char *pszService = szBuffer; 256 | struct hostent *ptHost; 257 | struct in_addr tAddress; 258 | char *pszNode = NULL; 259 | char *pc = NULL; 260 | 261 | if ((!ptSocketAddress) || (tSocketLength < (int)sizeof(struct sockaddr))) return EAI_FAIL; 262 | if (ptSocketAddress->sa_family != AF_INET) return EAI_FAMILY; 263 | if (tSocketLength < (int)sizeof(struct sockaddr_in)) return EAI_FAIL; 264 | if (!(pszNodeName && tNodeLength) && !(pszServiceName && tServiceLength)) { 265 | return EAI_NONAME; 266 | } 267 | if ((iFlags & NI_NUMERICHOST) && (iFlags & NI_NAMEREQD)) { 268 | return EAI_BADFLAGS; 269 | } 270 | if (pszServiceName && tServiceLength) { 271 | wPort = ((struct sockaddr_in *) ptSocketAddress)->sin_port; 272 | if (iFlags & NI_NUMERICSERV) { 273 | _WSPIAPI_SPRINTF_S_1(szBuffer, _WSPIAPI_COUNTOF(szBuffer), "%u", ntohs(wPort)); 274 | } 275 | else { 276 | ptService = getservbyport(wPort, (iFlags & NI_DGRAM) ? "udp" : NULL); 277 | if (ptService && ptService->s_name) { 278 | pszService = ptService->s_name; 279 | } 280 | else { 281 | _WSPIAPI_SPRINTF_S_1(szBuffer, _WSPIAPI_COUNTOF(szBuffer), "%u", ntohs(wPort)); 282 | } 283 | } 284 | if (tServiceLength > strlen(pszService)) 285 | _WSPIAPI_STRCPY_S(pszServiceName, tServiceLength, pszService); 286 | else return EAI_FAIL; 287 | } 288 | if (pszNodeName && tNodeLength) { 289 | tAddress = ((struct sockaddr_in *) ptSocketAddress)->sin_addr; 290 | if (iFlags & NI_NUMERICHOST) { 291 | pszNode = inet_ntoa(tAddress); 292 | } 293 | else { 294 | ptHost = gethostbyaddr((char *) &tAddress, sizeof(struct in_addr), AF_INET); 295 | if (ptHost && ptHost->h_name) { 296 | pszNode = ptHost->h_name; 297 | if ((iFlags & NI_NOFQDN) && ((pc = strchr(pszNode, '.')) != NULL)) *pc = '\0'; 298 | } 299 | else { 300 | if (iFlags & NI_NAMEREQD) { 301 | switch (WSAGetLastError()) { 302 | case WSAHOST_NOT_FOUND: return EAI_NONAME; 303 | case WSATRY_AGAIN: return EAI_AGAIN; 304 | case WSANO_RECOVERY: return EAI_FAIL; 305 | default: return EAI_NONAME; 306 | } 307 | } 308 | else pszNode = inet_ntoa(tAddress); 309 | } 310 | } 311 | if (tNodeLength > strlen(pszNode)) _WSPIAPI_STRCPY_S(pszNodeName, tNodeLength, pszNode); 312 | else return EAI_FAIL; 313 | } 314 | 315 | return 0; 316 | } 317 | 318 | char *inet_ntop(int af, const void *src, char *dst, size_t cnt) 319 | { 320 | if (af == AF_INET) 321 | { 322 | struct sockaddr_in in; 323 | memset(&in, 0, sizeof(in)); 324 | in.sin_family = AF_INET; 325 | memcpy(&in.sin_addr, src, sizeof(struct in_addr)); 326 | getnameinfo((struct sockaddr *)&in, sizeof(struct sockaddr_in), 327 | dst, cnt, NULL, 0, NI_NUMERICHOST); 328 | return dst; 329 | } 330 | else if (af == AF_INET6) 331 | { 332 | struct sockaddr_in6 in; 333 | memset(&in, 0, sizeof(in)); 334 | in.sin6_family = AF_INET6; 335 | memcpy(&in.sin6_addr, src, sizeof(struct in_addr6)); 336 | getnameinfo((struct sockaddr *)&in, sizeof(struct sockaddr_in6), 337 | dst, cnt, NULL, 0, NI_NUMERICHOST); 338 | return dst; 339 | } 340 | return NULL; 341 | } 342 | -------------------------------------------------------------------------------- /wspiapi.h: -------------------------------------------------------------------------------- 1 | /** 2 | * This file has no copyright assigned and is placed in the Public Domain. 3 | * This file is part of the mingw-w64 runtime package. 4 | * No warranty is given; refer to the file DISCLAIMER.PD within this package. 5 | */ 6 | #ifndef _WSPIAPI_H_ 7 | #define _WSPIAPI_H_ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #ifdef _MSC_VER 16 | #define WINBOOL BOOL 17 | #endif 18 | 19 | void lstrcpy_sA(char *dst, size_t dstlen, const char *src); 20 | 21 | #define _WSPIAPI_STRCPY_S(_Dst,_Size,_Src) lstrcpy_sA((_Dst), (_Size),(_Src)) 22 | //#define _WSPIAPI_STRCAT_S(_Dst,_Size,_Src) strcat((_Dst),(_Src)) 23 | //#define _WSPIAPI_STRNCPY_S(_Dst,_Size,_Src,_Count) strncpy((_Dst),(_Src),(_Count)); (_Dst)[(_Size) - 1] = 0 24 | #define _WSPIAPI_SPRINTF_S_1(_Dst,_Size,_Format,_Arg1) sprintf((_Dst),(_Format),(_Arg1)) 25 | 26 | #ifndef _WSPIAPI_COUNTOF 27 | #ifndef __cplusplus 28 | #define _WSPIAPI_COUNTOF(_Array) (sizeof(_Array) / sizeof(_Array[0])) 29 | #else 30 | template char (&__wspiapi_countof_helper(__CountofType (&_Array)[__wspiapi_countof_helper_N]))[__wspiapi_countof_helper_N]; 31 | #define _WSPIAPI_COUNTOF(_Array) sizeof(__wspiapi_countof_helper(_Array)) 32 | #endif 33 | #endif 34 | 35 | #define WspiapiMalloc(tSize) calloc(1,(tSize)) 36 | #define WspiapiFree(p) free(p) 37 | #define WspiapiSwap(a,b,c) { (c) = (a); (a) = (b); (b) = (c); } 38 | #define getaddrinfo WspiapiLegacyGetAddrInfo 39 | #define getnameinfo WspiapiLegacyGetNameInfo 40 | #define freeaddrinfo WspiapiLegacyFreeAddrInfo 41 | 42 | #ifndef WSA_NOT_ENOUGH_MEMORY 43 | #define WSA_NOT_ENOUGH_MEMORY 8 44 | #endif 45 | 46 | #ifndef WSATYPE_NOT_FOUND 47 | #define WSATYPE_NOT_FOUND 10109 48 | #endif 49 | 50 | #ifndef AF_INET6 51 | #define AF_INET6 23 52 | #endif 53 | 54 | #define INET6_ADDRSTRLEN 46 55 | 56 | /* getnameinfo constants */ 57 | #define NI_MAXHOST 1025 58 | 59 | #define NI_NOFQDN 0x01 60 | #define NI_NUMERICHOST 0x02 61 | #define NI_NAMEREQD 0x04 62 | #define NI_NUMERICSERV 0x08 63 | #define NI_DGRAM 0x10 64 | 65 | /* getaddrinfo constants */ 66 | #define AI_PASSIVE 1 67 | #define AI_CANONNAME 2 68 | #define AI_NUMERICHOST 4 69 | 70 | /* getaddrinfo error codes */ 71 | #define EAI_AGAIN WSATRY_AGAIN 72 | #define EAI_BADFLAGS WSAEINVAL 73 | #define EAI_FAIL WSANO_RECOVERY 74 | #define EAI_FAMILY WSAEAFNOSUPPORT 75 | #define EAI_MEMORY WSA_NOT_ENOUGH_MEMORY 76 | #define EAI_NODATA WSANO_DATA 77 | #define EAI_NONAME WSAHOST_NOT_FOUND 78 | #define EAI_SERVICE WSATYPE_NOT_FOUND 79 | #define EAI_SOCKTYPE WSAESOCKTNOSUPPORT 80 | 81 | #ifdef __cplusplus 82 | extern "C" { 83 | #endif 84 | typedef int socklen_t; 85 | 86 | struct addrinfo { 87 | int ai_flags; 88 | int ai_family; 89 | int ai_socktype; 90 | int ai_protocol; 91 | size_t ai_addrlen; 92 | char *ai_canonname; 93 | struct sockaddr *ai_addr; 94 | struct addrinfo *ai_next; 95 | }; 96 | 97 | struct in6_addr { 98 | union { 99 | u_char _S6_u8[16]; 100 | u_short _S6_u16[8]; 101 | u_long _S6_u32[4]; 102 | } _S6_un; 103 | }; 104 | 105 | /* These are used in some MS code */ 106 | #define in_addr6 in6_addr 107 | #define _s6_bytes _S6_un._S6_u8 108 | #define _s6_words _S6_un._S6_u16 109 | 110 | typedef struct in6_addr IN6_ADDR, *PIN6_ADDR, *LPIN6_ADDR; 111 | 112 | struct sockaddr_in6 { 113 | short sin6_family; /* AF_INET6 */ 114 | u_short sin6_port; /* transport layer port # */ 115 | u_long sin6_flowinfo; /* IPv6 traffic class & flow info */ 116 | struct in6_addr sin6_addr; /* IPv6 address */ 117 | u_long sin6_scope_id; /* set of interfaces for a scope */ 118 | }; 119 | #ifdef __cplusplus 120 | } 121 | #endif 122 | 123 | typedef int (WINAPI *WSPIAPI_PGETADDRINFO)(const char *nodename,const char *servname,const struct addrinfo *hints,struct addrinfo **res); 124 | typedef int (WINAPI *WSPIAPI_PGETNAMEINFO)(const struct sockaddr *sa,socklen_t salen,char *host,size_t hostlen,char *serv,size_t servlen,int flags); 125 | typedef void (WINAPI *WSPIAPI_PFREEADDRINFO)(struct addrinfo *ai); 126 | 127 | #ifdef __cplusplus 128 | extern "C" { 129 | #endif 130 | typedef struct { 131 | char const *pszName; 132 | FARPROC pfAddress; 133 | } WSPIAPI_FUNCTION; 134 | 135 | #define WSPIAPI_FUNCTION_ARRAY { { "getaddrinfo",(FARPROC) WspiapiLegacyGetAddrInfo }, \ 136 | { "getnameinfo",(FARPROC) WspiapiLegacyGetNameInfo }, \ 137 | { "freeaddrinfo",(FARPROC) WspiapiLegacyFreeAddrInfo } } 138 | 139 | char *WINAPI WspiapiStrdup (const char *pszString); 140 | WINBOOL WINAPI WspiapiParseV4Address (const char *pszAddress,PDWORD pdwAddress); 141 | struct addrinfo * WINAPI WspiapiNewAddrInfo (int iSocketType,int iProtocol,WORD wPort,DWORD dwAddress); 142 | int WINAPI WspiapiQueryDNS (const char *pszNodeName,int iSocketType,int iProtocol,WORD wPort,char pszAlias[NI_MAXHOST],struct addrinfo **pptResult); 143 | int WINAPI WspiapiLookupNode (const char *pszNodeName,int iSocketType,int iProtocol,WORD wPort,WINBOOL bAI_CANONNAME,struct addrinfo **pptResult); 144 | int WINAPI WspiapiClone (WORD wPort,struct addrinfo *ptResult); 145 | void WINAPI WspiapiLegacyFreeAddrInfo (struct addrinfo *ptHead); 146 | int WINAPI WspiapiLegacyGetAddrInfo(const char *pszNodeName,const char *pszServiceName,const struct addrinfo *ptHints,struct addrinfo **pptResult); 147 | int WINAPI WspiapiLegacyGetNameInfo(const struct sockaddr *ptSocketAddress,socklen_t tSocketLength,char *pszNodeName,size_t tNodeLength,char *pszServiceName,size_t tServiceLength,int iFlags); 148 | FARPROC WINAPI WspiapiLoad(WORD wFunction); 149 | int WINAPI WspiapiGetAddrInfo(const char *nodename,const char *servname,const struct addrinfo *hints,struct addrinfo **res); 150 | int WINAPI WspiapiGetNameInfo (const struct sockaddr *sa,socklen_t salen,char *host,size_t hostlen,char *serv,size_t servlen,int flags); 151 | void WINAPI WspiapiFreeAddrInfo (struct addrinfo *ai); 152 | 153 | //static __inline char* 154 | //gai_strerrorA(int ecode) 155 | //{ 156 | // static char message[1024+1]; 157 | // DWORD dwFlags = FORMAT_MESSAGE_FROM_SYSTEM 158 | // | FORMAT_MESSAGE_IGNORE_INSERTS 159 | // | FORMAT_MESSAGE_MAX_WIDTH_MASK; 160 | // DWORD dwLanguageId = MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT); 161 | // FormatMessageA(dwFlags, NULL, ecode, dwLanguageId, (LPSTR)message, 1024, NULL); 162 | // return message; 163 | //} 164 | //static __inline WCHAR* 165 | //gai_strerrorW(int ecode) 166 | //{ 167 | // static WCHAR message[1024+1]; 168 | // DWORD dwFlags = FORMAT_MESSAGE_FROM_SYSTEM 169 | // | FORMAT_MESSAGE_IGNORE_INSERTS 170 | // | FORMAT_MESSAGE_MAX_WIDTH_MASK; 171 | // DWORD dwLanguageId = MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT); 172 | // FormatMessageW(dwFlags, NULL, ecode, dwLanguageId, (LPWSTR)message, 1024, NULL); 173 | // return message; 174 | //} 175 | //#ifdef UNICODE 176 | //#define gai_strerror gai_strerrorW 177 | //#else 178 | //#define gai_strerror gai_strerrorA 179 | //#endif 180 | 181 | #ifdef __cplusplus 182 | } 183 | #endif 184 | 185 | #endif 186 | --------------------------------------------------------------------------------