2 * OpenAL cross platform audio library
3 * Copyright (C) 2011 by authors.
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 * Or go to http://www.gnu.org/copyleft/lgpl.html
23 #define _WIN32_IE 0x501
25 #define _WIN32_IE 0x400
42 #ifndef AL_NO_UID_DEFS
43 #if defined(HAVE_GUIDDEF_H) || defined(HAVE_INITGUID_H)
52 DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM
, 0x00000001, 0x0000, 0x0010, 0x80,0x00, 0x00,0xaa,0x00,0x38,0x9b,0x71);
53 DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
, 0x00000003, 0x0000, 0x0010, 0x80,0x00, 0x00,0xaa,0x00,0x38,0x9b,0x71);
55 DEFINE_GUID(IID_IDirectSoundNotify
, 0xb0210783, 0x89cd, 0x11d0, 0xaf,0x08, 0x00,0xa0,0xc9,0x25,0xcd,0x16);
57 DEFINE_GUID(CLSID_MMDeviceEnumerator
, 0xbcde0395, 0xe52f, 0x467c, 0x8e,0x3d, 0xc4,0x57,0x92,0x91,0x69,0x2e);
58 DEFINE_GUID(IID_IMMDeviceEnumerator
, 0xa95664d2, 0x9614, 0x4f35, 0xa7,0x46, 0xde,0x8d,0xb6,0x36,0x17,0xe6);
59 DEFINE_GUID(IID_IAudioClient
, 0x1cb9ad4c, 0xdbfa, 0x4c32, 0xb1,0x78, 0xc2,0xf5,0x68,0xa7,0x03,0xb2);
60 DEFINE_GUID(IID_IAudioRenderClient
, 0xf294acfc, 0x3146, 0x4483, 0xa7,0xbf, 0xad,0xdc,0xa7,0xc2,0x60,0xe2);
61 DEFINE_GUID(IID_IAudioCaptureClient
, 0xc8adbd64, 0xe71e, 0x48a0, 0xa4,0xde, 0x18,0x5c,0x39,0x5c,0xd3,0x17);
64 #include <devpropdef.h>
65 #include <propkeydef.h>
66 DEFINE_DEVPROPKEY(DEVPKEY_Device_FriendlyName
, 0xa45c254e, 0xdf1c, 0x4efd, 0x80,0x20, 0x67,0xd1,0x46,0xa8,0x50,0xe0, 14);
67 DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_FormFactor
, 0x1da5d803, 0xd492, 0x4edd, 0x8c,0x23, 0xe0,0xc0,0xff,0xee,0x7f,0x0e, 0);
70 #endif /* AL_NO_UID_DEFS */
81 #ifdef HAVE_SYS_SYSCONF_H
82 #include <sys/sysconf.h>
93 #elif defined(_WIN32_IE)
102 #include "alstring.h"
107 extern inline ALuint
NextPowerOf2(ALuint value
);
108 extern inline ALint
fastf2i(ALfloat f
);
109 extern inline ALuint
fastf2u(ALfloat f
);
112 ALuint CPUCapFlags
= 0;
115 void FillCPUCaps(ALuint capfilter
)
119 /* FIXME: We really should get this for all available CPUs in case different
120 * CPUs have different caps (is that possible on one machine?). */
121 #if defined(HAVE_GCC_GET_CPUID) && (defined(__i386__) || defined(__x86_64__) || \
122 defined(_M_IX86) || defined(_M_X64))
124 unsigned int regs
[4];
125 char str
[sizeof(unsigned int[4])];
128 if(!__get_cpuid(0, &cpuinf
[0].regs
[0], &cpuinf
[0].regs
[1], &cpuinf
[0].regs
[2], &cpuinf
[0].regs
[3]))
129 ERR("Failed to get CPUID\n");
132 unsigned int maxfunc
= cpuinf
[0].regs
[0];
133 unsigned int maxextfunc
= 0;
135 if(__get_cpuid(0x80000000, &cpuinf
[0].regs
[0], &cpuinf
[0].regs
[1], &cpuinf
[0].regs
[2], &cpuinf
[0].regs
[3]))
136 maxextfunc
= cpuinf
[0].regs
[0];
137 TRACE("Detected max CPUID function: 0x%x (ext. 0x%x)\n", maxfunc
, maxextfunc
);
139 TRACE("Vendor ID: \"%.4s%.4s%.4s\"\n", cpuinf
[0].str
+4, cpuinf
[0].str
+12, cpuinf
[0].str
+8);
140 if(maxextfunc
>= 0x80000004 &&
141 __get_cpuid(0x80000002, &cpuinf
[0].regs
[0], &cpuinf
[0].regs
[1], &cpuinf
[0].regs
[2], &cpuinf
[0].regs
[3]) &&
142 __get_cpuid(0x80000003, &cpuinf
[1].regs
[0], &cpuinf
[1].regs
[1], &cpuinf
[1].regs
[2], &cpuinf
[1].regs
[3]) &&
143 __get_cpuid(0x80000004, &cpuinf
[2].regs
[0], &cpuinf
[2].regs
[1], &cpuinf
[2].regs
[2], &cpuinf
[2].regs
[3]))
144 TRACE("Name: \"%.16s%.16s%.16s\"\n", cpuinf
[0].str
, cpuinf
[1].str
, cpuinf
[2].str
);
147 __get_cpuid(1, &cpuinf
[0].regs
[0], &cpuinf
[0].regs
[1], &cpuinf
[0].regs
[2], &cpuinf
[0].regs
[3]))
149 if((cpuinf
[0].regs
[3]&(1<<25)))
152 if((cpuinf
[0].regs
[3]&(1<<26)))
154 caps
|= CPU_CAP_SSE2
;
155 if((cpuinf
[0].regs
[2]&(1<<0)))
157 caps
|= CPU_CAP_SSE3
;
158 if((cpuinf
[0].regs
[2]&(1<<19)))
159 caps
|= CPU_CAP_SSE4_1
;
165 #elif defined(HAVE_CPUID_INTRINSIC) && (defined(__i386__) || defined(__x86_64__) || \
166 defined(_M_IX86) || defined(_M_X64))
169 char str
[sizeof(int[4])];
172 (__cpuid
)(cpuinf
[0].regs
, 0);
173 if(cpuinf
[0].regs
[0] == 0)
174 ERR("Failed to get CPUID\n");
177 unsigned int maxfunc
= cpuinf
[0].regs
[0];
178 unsigned int maxextfunc
;
180 (__cpuid
)(cpuinf
[0].regs
, 0x80000000);
181 maxextfunc
= cpuinf
[0].regs
[0];
183 TRACE("Detected max CPUID function: 0x%x (ext. 0x%x)\n", maxfunc
, maxextfunc
);
185 TRACE("Vendor ID: \"%.4s%.4s%.4s\"\n", cpuinf
[0].str
+4, cpuinf
[0].str
+12, cpuinf
[0].str
+8);
186 if(maxextfunc
>= 0x80000004)
188 (__cpuid
)(cpuinf
[0].regs
, 0x80000002);
189 (__cpuid
)(cpuinf
[1].regs
, 0x80000003);
190 (__cpuid
)(cpuinf
[2].regs
, 0x80000004);
191 TRACE("Name: \"%.16s%.16s%.16s\"\n", cpuinf
[0].str
, cpuinf
[1].str
, cpuinf
[2].str
);
196 (__cpuid
)(cpuinf
[0].regs
, 1);
197 if((cpuinf
[0].regs
[3]&(1<<25)))
200 if((cpuinf
[0].regs
[3]&(1<<26)))
202 caps
|= CPU_CAP_SSE2
;
203 if((cpuinf
[0].regs
[2]&(1<<0)))
205 caps
|= CPU_CAP_SSE3
;
206 if((cpuinf
[0].regs
[2]&(1<<19)))
207 caps
|= CPU_CAP_SSE4_1
;
214 /* Assume support for whatever's supported if we can't check for it */
215 #if defined(HAVE_SSE4_1)
216 #warning "Assuming SSE 4.1 run-time support!"
217 caps
|= CPU_CAP_SSE
| CPU_CAP_SSE2
| CPU_CAP_SSE3
| CPU_CAP_SSE4_1
;
218 #elif defined(HAVE_SSE3)
219 #warning "Assuming SSE 3 run-time support!"
220 caps
|= CPU_CAP_SSE
| CPU_CAP_SSE2
| CPU_CAP_SSE3
;
221 #elif defined(HAVE_SSE2)
222 #warning "Assuming SSE 2 run-time support!"
223 caps
|= CPU_CAP_SSE
| CPU_CAP_SSE2
;
224 #elif defined(HAVE_SSE)
225 #warning "Assuming SSE run-time support!"
230 /* Assume Neon support if compiled with it */
231 caps
|= CPU_CAP_NEON
;
234 TRACE("Extensions:%s%s%s%s%s%s\n",
235 ((capfilter
&CPU_CAP_SSE
) ? ((caps
&CPU_CAP_SSE
) ? " +SSE" : " -SSE") : ""),
236 ((capfilter
&CPU_CAP_SSE2
) ? ((caps
&CPU_CAP_SSE2
) ? " +SSE2" : " -SSE2") : ""),
237 ((capfilter
&CPU_CAP_SSE3
) ? ((caps
&CPU_CAP_SSE3
) ? " +SSE3" : " -SSE3") : ""),
238 ((capfilter
&CPU_CAP_SSE4_1
) ? ((caps
&CPU_CAP_SSE4_1
) ? " +SSE4.1" : " -SSE4.1") : ""),
239 ((capfilter
&CPU_CAP_NEON
) ? ((caps
&CPU_CAP_NEON
) ? " +Neon" : " -Neon") : ""),
240 ((!capfilter
) ? " -none-" : "")
242 CPUCapFlags
= caps
& capfilter
;
246 void SetMixerFPUMode(FPUCtl
*ctl
)
249 fegetenv(STATIC_CAST(fenv_t
, ctl
));
250 #if defined(__GNUC__) && defined(HAVE_SSE)
251 /* FIXME: Some fegetenv implementations can get the SSE environment too?
252 * How to tell when it does? */
253 if((CPUCapFlags
&CPU_CAP_SSE
))
254 __asm__
__volatile__("stmxcsr %0" : "=m" (*&ctl
->sse_state
));
258 fesetround(FE_TOWARDZERO
);
260 #if defined(__GNUC__) && defined(HAVE_SSE)
261 if((CPUCapFlags
&CPU_CAP_SSE
))
263 int sseState
= ctl
->sse_state
;
264 sseState
|= 0x6000; /* set round-to-zero */
265 sseState
|= 0x8000; /* set flush-to-zero */
266 if((CPUCapFlags
&CPU_CAP_SSE2
))
267 sseState
|= 0x0040; /* set denormals-are-zero */
268 __asm__
__volatile__("ldmxcsr %0" : : "m" (*&sseState
));
272 #elif defined(HAVE___CONTROL87_2)
275 __control87_2(0, 0, &ctl
->state
, NULL
);
276 __control87_2(_RC_CHOP
, _MCW_RC
, &mode
, NULL
);
278 if((CPUCapFlags
&CPU_CAP_SSE
))
280 __control87_2(0, 0, NULL
, &ctl
->sse_state
);
281 __control87_2(_RC_CHOP
|_DN_FLUSH
, _MCW_RC
|_MCW_DN
, NULL
, &mode
);
285 #elif defined(HAVE__CONTROLFP)
287 ctl
->state
= _controlfp(0, 0);
288 (void)_controlfp(_RC_CHOP
, _MCW_RC
);
292 void RestoreFPUMode(const FPUCtl
*ctl
)
295 fesetenv(STATIC_CAST(fenv_t
, ctl
));
296 #if defined(__GNUC__) && defined(HAVE_SSE)
297 if((CPUCapFlags
&CPU_CAP_SSE
))
298 __asm__
__volatile__("ldmxcsr %0" : : "m" (*&ctl
->sse_state
));
301 #elif defined(HAVE___CONTROL87_2)
304 __control87_2(ctl
->state
, _MCW_RC
, &mode
, NULL
);
306 if((CPUCapFlags
&CPU_CAP_SSE
))
307 __control87_2(ctl
->sse_state
, _MCW_RC
|_MCW_DN
, NULL
, &mode
);
310 #elif defined(HAVE__CONTROLFP)
312 _controlfp(ctl
->state
, _MCW_RC
);
317 static int StringSortCompare(const void *str1
, const void *str2
)
319 return al_string_cmp(*(const_al_string
*)str1
, *(const_al_string
*)str2
);
324 static WCHAR
*strrchrW(WCHAR
*str
, WCHAR ch
)
336 al_string
GetProcPath(void)
338 al_string ret
= AL_STRING_INIT_STATIC();
339 WCHAR
*pathname
, *sep
;
344 pathname
= malloc(pathlen
* sizeof(pathname
[0]));
345 while(pathlen
> 0 && (len
=GetModuleFileNameW(NULL
, pathname
, pathlen
)) == pathlen
)
349 pathname
= malloc(pathlen
* sizeof(pathname
[0]));
354 ERR("Failed to get process name: error %lu\n", GetLastError());
359 if((sep
= strrchrW(pathname
, '\\')))
361 WCHAR
*sep2
= strrchrW(pathname
, '/');
365 else if((sep
= strrchrW(pathname
, '/')))
367 al_string_copy_wcstr(&ret
, pathname
);
370 TRACE("Got: %s\n", al_string_get_cstr(ret
));
375 static WCHAR
*FromUTF8(const char *str
)
380 if((len
=MultiByteToWideChar(CP_UTF8
, 0, str
, -1, NULL
, 0)) > 0)
382 out
= calloc(sizeof(WCHAR
), len
);
383 MultiByteToWideChar(CP_UTF8
, 0, str
, -1, out
, len
);
389 void *LoadLib(const char *name
)
394 wname
= FromUTF8(name
);
396 ERR("Failed to convert UTF-8 filename: \"%s\"\n", name
);
399 hdl
= LoadLibraryW(wname
);
404 void CloseLib(void *handle
)
405 { FreeLibrary((HANDLE
)handle
); }
406 void *GetSymbol(void *handle
, const char *name
)
410 ret
= (void*)GetProcAddress((HANDLE
)handle
, name
);
412 ERR("Failed to load %s\n", name
);
416 WCHAR
*strdupW(const WCHAR
*str
)
426 ret
= calloc(sizeof(WCHAR
), len
+1);
428 memcpy(ret
, str
, sizeof(WCHAR
)*len
);
432 FILE *al_fopen(const char *fname
, const char *mode
)
434 WCHAR
*wname
=NULL
, *wmode
=NULL
;
437 wname
= FromUTF8(fname
);
438 wmode
= FromUTF8(mode
);
440 ERR("Failed to convert UTF-8 filename: \"%s\"\n", fname
);
442 ERR("Failed to convert UTF-8 mode: \"%s\"\n", mode
);
444 file
= _wfopen(wname
, wmode
);
453 void al_print(const char *type
, const char *func
, const char *fmt
, ...)
460 vsnprintf(str
, sizeof(str
), fmt
, ap
);
463 str
[sizeof(str
)-1] = 0;
464 wstr
= FromUTF8(str
);
466 fprintf(LogFile
, "AL lib: %s %s: <UTF-8 error> %s", type
, func
, str
);
469 fprintf(LogFile
, "AL lib: %s %s: %ls", type
, func
, wstr
);
477 static inline int is_slash(int c
)
478 { return (c
== '\\' || c
== '/'); }
480 static void DirectorySearch(const char *path
, const char *ext
, vector_al_string
*results
)
482 al_string pathstr
= AL_STRING_INIT_STATIC();
483 WIN32_FIND_DATAW fdata
;
487 al_string_copy_cstr(&pathstr
, path
);
488 al_string_append_cstr(&pathstr
, "\\*");
489 al_string_append_cstr(&pathstr
, ext
);
491 TRACE("Searching %s\n", al_string_get_cstr(pathstr
));
493 wpath
= FromUTF8(al_string_get_cstr(pathstr
));
495 hdl
= FindFirstFileW(wpath
, &fdata
);
496 if(hdl
!= INVALID_HANDLE_VALUE
)
498 size_t base
= VECTOR_SIZE(*results
);
500 al_string str
= AL_STRING_INIT_STATIC();
501 al_string_copy_cstr(&str
, path
);
502 al_string_append_char(&str
, '\\');
503 al_string_append_wcstr(&str
, fdata
.cFileName
);
504 TRACE("Got result %s\n", al_string_get_cstr(str
));
505 VECTOR_PUSH_BACK(*results
, str
);
506 } while(FindNextFileW(hdl
, &fdata
));
509 if(VECTOR_SIZE(*results
) > base
)
510 qsort(VECTOR_BEGIN(*results
)+base
, VECTOR_SIZE(*results
)-base
,
511 sizeof(VECTOR_FRONT(*results
)), StringSortCompare
);
515 al_string_deinit(&pathstr
);
518 vector_al_string
SearchDataFiles(const char *ext
, const char *subdir
)
520 static const int ids
[2] = { CSIDL_APPDATA
, CSIDL_COMMON_APPDATA
};
521 static RefCount search_lock
;
522 vector_al_string results
= VECTOR_INIT_STATIC();
525 while(ATOMIC_EXCHANGE(uint
, &search_lock
, 1) == 1)
528 /* If the path is absolute, use it directly. */
529 if(isalpha(subdir
[0]) && subdir
[1] == ':' && is_slash(subdir
[2]))
531 al_string path
= AL_STRING_INIT_STATIC();
532 al_string_copy_cstr(&path
, subdir
);
533 #define FIX_SLASH(i) do { if(*(i) == '/') *(i) = '\\'; } while(0)
534 VECTOR_FOR_EACH(char, path
, FIX_SLASH
);
537 DirectorySearch(al_string_get_cstr(path
), ext
, &results
);
539 al_string_deinit(&path
);
541 else if(subdir
[0] == '\\' && subdir
[1] == '\\' && subdir
[2] == '?' && subdir
[3] == '\\')
542 DirectorySearch(subdir
, ext
, &results
);
545 al_string path
= AL_STRING_INIT_STATIC();
548 /* Search the app-local directory. */
549 if((cwdbuf
=_wgetenv(L
"ALSOFT_LOCAL_PATH")) && *cwdbuf
!= '\0')
551 al_string_copy_wcstr(&path
, cwdbuf
);
552 if(is_slash(VECTOR_BACK(path
)))
554 VECTOR_POP_BACK(path
);
555 *VECTOR_END(path
) = 0;
558 else if(!(cwdbuf
=_wgetcwd(NULL
, 0)))
559 al_string_copy_cstr(&path
, ".");
562 al_string_copy_wcstr(&path
, cwdbuf
);
563 if(is_slash(VECTOR_BACK(path
)))
565 VECTOR_POP_BACK(path
);
566 *VECTOR_END(path
) = 0;
570 #define FIX_SLASH(i) do { if(*(i) == '/') *(i) = '\\'; } while(0)
571 VECTOR_FOR_EACH(char, path
, FIX_SLASH
);
573 DirectorySearch(al_string_get_cstr(path
), ext
, &results
);
575 /* Search the local and global data dirs. */
576 for(i
= 0;i
< COUNTOF(ids
);i
++)
578 WCHAR buffer
[PATH_MAX
];
579 if(SHGetSpecialFolderPathW(NULL
, buffer
, ids
[i
], FALSE
) != FALSE
)
581 al_string_copy_wcstr(&path
, buffer
);
582 if(!is_slash(VECTOR_BACK(path
)))
583 al_string_append_char(&path
, '\\');
584 al_string_append_cstr(&path
, subdir
);
585 #define FIX_SLASH(i) do { if(*(i) == '/') *(i) = '\\'; } while(0)
586 VECTOR_FOR_EACH(char, path
, FIX_SLASH
);
589 DirectorySearch(al_string_get_cstr(path
), ext
, &results
);
593 al_string_deinit(&path
);
596 ATOMIC_STORE(&search_lock
, 0);
603 al_string
GetProcPath(void)
605 al_string ret
= AL_STRING_INIT_STATIC();
607 char *pathname
, *sep
;
612 pathname
= malloc(pathlen
);
614 fname
= "/proc/self/exe";
615 len
= readlink(fname
, pathname
, pathlen
);
616 if(len
== -1 && errno
== ENOENT
)
618 fname
= "/proc/self/file";
619 len
= readlink(fname
, pathname
, pathlen
);
622 while(len
> 0 && (size_t)len
== pathlen
)
626 pathname
= malloc(pathlen
);
627 len
= readlink(fname
, pathname
, pathlen
);
632 ERR("Failed to link %s: %s\n", fname
, strerror(errno
));
637 sep
= strrchr(pathname
, '/');
639 al_string_copy_range(&ret
, pathname
, sep
);
641 al_string_copy_cstr(&ret
, pathname
);
644 TRACE("Got: %s\n", al_string_get_cstr(ret
));
651 void *LoadLib(const char *name
)
657 handle
= dlopen(name
, RTLD_NOW
);
658 if((err
=dlerror()) != NULL
)
662 void CloseLib(void *handle
)
664 void *GetSymbol(void *handle
, const char *name
)
670 sym
= dlsym(handle
, name
);
671 if((err
=dlerror()) != NULL
)
673 WARN("Failed to load %s: %s\n", name
, err
);
679 #endif /* HAVE_DLFCN_H */
681 void al_print(const char *type
, const char *func
, const char *fmt
, ...)
686 fprintf(LogFile
, "AL lib: %s %s: ", type
, func
);
687 vfprintf(LogFile
, fmt
, ap
);
694 static void DirectorySearch(const char *path
, const char *ext
, vector_al_string
*results
)
696 size_t extlen
= strlen(ext
);
699 TRACE("Searching %s for *%s\n", path
, ext
);
703 size_t base
= VECTOR_SIZE(*results
);
704 struct dirent
*dirent
;
705 while((dirent
=readdir(dir
)) != NULL
)
709 if(strcmp(dirent
->d_name
, ".") == 0 || strcmp(dirent
->d_name
, "..") == 0)
712 len
= strlen(dirent
->d_name
);
715 if(strcasecmp(dirent
->d_name
+len
-extlen
, ext
) != 0)
719 al_string_copy_cstr(&str
, path
);
720 if(VECTOR_BACK(str
) != '/')
721 al_string_append_char(&str
, '/');
722 al_string_append_cstr(&str
, dirent
->d_name
);
723 TRACE("Got result %s\n", al_string_get_cstr(str
));
724 VECTOR_PUSH_BACK(*results
, str
);
728 if(VECTOR_SIZE(*results
) > base
)
729 qsort(VECTOR_BEGIN(*results
)+base
, VECTOR_SIZE(*results
)-base
,
730 sizeof(VECTOR_FRONT(*results
)), StringSortCompare
);
734 vector_al_string
SearchDataFiles(const char *ext
, const char *subdir
)
736 static RefCount search_lock
;
737 vector_al_string results
= VECTOR_INIT_STATIC();
739 while(ATOMIC_EXCHANGE(uint
, &search_lock
, 1) == 1)
743 DirectorySearch(subdir
, ext
, &results
);
746 al_string path
= AL_STRING_INIT_STATIC();
747 const char *str
, *next
;
748 char cwdbuf
[PATH_MAX
];
750 /* Search the app-local directory. */
751 if((str
=getenv("ALSOFT_LOCAL_PATH")) && *str
!= '\0')
752 DirectorySearch(str
, ext
, &results
);
753 else if(getcwd(cwdbuf
, sizeof(cwdbuf
)))
754 DirectorySearch(cwdbuf
, ext
, &results
);
756 DirectorySearch(".", ext
, &results
);
758 // Search local data dir
759 if((str
=getenv("XDG_DATA_HOME")) != NULL
&& str
[0] != '\0')
761 al_string_copy_cstr(&path
, str
);
762 if(VECTOR_BACK(path
) != '/')
763 al_string_append_char(&path
, '/');
764 al_string_append_cstr(&path
, subdir
);
765 DirectorySearch(al_string_get_cstr(path
), ext
, &results
);
767 else if((str
=getenv("HOME")) != NULL
&& str
[0] != '\0')
769 al_string_copy_cstr(&path
, str
);
770 if(VECTOR_BACK(path
) == '/')
772 VECTOR_POP_BACK(path
);
773 *VECTOR_END(path
) = 0;
775 al_string_append_cstr(&path
, "/.local/share/");
776 al_string_append_cstr(&path
, subdir
);
777 DirectorySearch(al_string_get_cstr(path
), ext
, &results
);
780 // Search global data dirs
781 if((str
=getenv("XDG_DATA_DIRS")) == NULL
|| str
[0] == '\0')
782 str
= "/usr/local/share/:/usr/share/";
785 while((str
=next
) != NULL
&& str
[0] != '\0')
787 next
= strchr(str
, ':');
789 al_string_copy_cstr(&path
, str
);
792 al_string_copy_range(&path
, str
, next
);
795 if(!al_string_empty(path
))
797 if(VECTOR_BACK(path
) != '/')
798 al_string_append_char(&path
, '/');
799 al_string_append_cstr(&path
, subdir
);
801 DirectorySearch(al_string_get_cstr(path
), ext
, &results
);
805 al_string_deinit(&path
);
808 ATOMIC_STORE(&search_lock
, 0);
816 void SetRTPriority(void)
818 ALboolean failed
= AL_FALSE
;
822 failed
= !SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL
);
823 #elif defined(HAVE_PTHREAD_SETSCHEDPARAM) && !defined(__OpenBSD__)
826 struct sched_param param
;
827 /* Use the minimum real-time priority possible for now (on Linux this
828 * should be 1 for SCHED_RR) */
829 param
.sched_priority
= sched_get_priority_min(SCHED_RR
);
830 failed
= !!pthread_setschedparam(pthread_self(), SCHED_RR
, ¶m
);
833 /* Real-time priority not available */
834 failed
= (RTPrioLevel
>0);
837 ERR("Failed to set priority level for thread\n");
841 ALboolean
vector_reserve(char *ptr
, size_t base_size
, size_t obj_size
, size_t obj_count
, ALboolean exact
)
843 vector_
*vecptr
= (vector_
*)ptr
;
844 if((*vecptr
? (*vecptr
)->Capacity
: 0) < obj_count
)
846 size_t old_size
= (*vecptr
? (*vecptr
)->Size
: 0);
849 /* Use the next power-of-2 size if we don't need to allocate the exact
850 * amount. This is preferred when regularly increasing the vector since
851 * it means fewer reallocations. Though it means it also wastes some
853 if(exact
== AL_FALSE
&& obj_count
< INT_MAX
)
854 obj_count
= NextPowerOf2((ALuint
)obj_count
);
856 /* Need to be explicit with the caller type's base size, because it
857 * could have extra padding before the start of the array (that is,
858 * sizeof(*vector_) may not equal base_size). */
859 temp
= al_calloc(16, base_size
+ obj_size
*obj_count
);
860 if(temp
== NULL
) return AL_FALSE
;
861 memcpy(((ALubyte
*)temp
)+base_size
, ((ALubyte
*)*vecptr
)+base_size
,
866 (*vecptr
)->Capacity
= obj_count
;
867 (*vecptr
)->Size
= old_size
;
872 ALboolean
vector_resize(char *ptr
, size_t base_size
, size_t obj_size
, size_t obj_count
)
874 vector_
*vecptr
= (vector_
*)ptr
;
875 if(*vecptr
|| obj_count
> 0)
877 if(!vector_reserve((char*)vecptr
, base_size
, obj_size
, obj_count
, AL_TRUE
))
879 (*vecptr
)->Size
= obj_count
;
885 extern inline void al_string_deinit(al_string
*str
);
886 extern inline size_t al_string_length(const_al_string str
);
887 extern inline ALboolean
al_string_empty(const_al_string str
);
888 extern inline const al_string_char_type
*al_string_get_cstr(const_al_string str
);
890 void al_string_clear(al_string
*str
)
892 if(!al_string_empty(*str
))
894 /* Reserve one more character than the total size of the string. This
895 * is to ensure we have space to add a null terminator in the string
896 * data so it can be used as a C-style string.
898 VECTOR_RESERVE(*str
, 1);
899 VECTOR_RESIZE(*str
, 0);
900 *VECTOR_END(*str
) = 0;
904 static inline int al_string_compare(const al_string_char_type
*str1
, size_t str1len
,
905 const al_string_char_type
*str2
, size_t str2len
)
907 size_t complen
= (str1len
< str2len
) ? str1len
: str2len
;
908 int ret
= memcmp(str1
, str2
, complen
);
911 if(str1len
> str2len
) return 1;
912 if(str1len
< str2len
) return -1;
916 int al_string_cmp(const_al_string str1
, const_al_string str2
)
918 return al_string_compare(&VECTOR_FRONT(str1
), al_string_length(str1
),
919 &VECTOR_FRONT(str2
), al_string_length(str2
));
921 int al_string_cmp_cstr(const_al_string str1
, const al_string_char_type
*str2
)
923 return al_string_compare(&VECTOR_FRONT(str1
), al_string_length(str1
),
927 void al_string_copy(al_string
*str
, const_al_string from
)
929 size_t len
= al_string_length(from
);
932 VECTOR_RESERVE(*str
, len
+1);
933 VECTOR_RESIZE(*str
, len
);
934 for(i
= 0;i
< len
;i
++)
935 VECTOR_ELEM(*str
, i
) = VECTOR_ELEM(from
, i
);
936 *VECTOR_END(*str
) = 0;
939 void al_string_copy_cstr(al_string
*str
, const al_string_char_type
*from
)
941 size_t len
= strlen(from
);
944 VECTOR_RESERVE(*str
, len
+1);
945 VECTOR_RESIZE(*str
, len
);
946 for(i
= 0;i
< len
;i
++)
947 VECTOR_ELEM(*str
, i
) = from
[i
];
948 *VECTOR_END(*str
) = 0;
951 void al_string_copy_range(al_string
*str
, const al_string_char_type
*from
, const al_string_char_type
*to
)
953 size_t len
= to
- from
;
956 VECTOR_RESERVE(*str
, len
+1);
957 VECTOR_RESIZE(*str
, len
);
958 for(i
= 0;i
< len
;i
++)
959 VECTOR_ELEM(*str
, i
) = from
[i
];
960 *VECTOR_END(*str
) = 0;
963 void al_string_append_char(al_string
*str
, const al_string_char_type c
)
965 VECTOR_RESERVE(*str
, al_string_length(*str
)+2);
966 VECTOR_PUSH_BACK(*str
, c
);
967 *VECTOR_END(*str
) = 0;
970 void al_string_append_cstr(al_string
*str
, const al_string_char_type
*from
)
972 size_t len
= strlen(from
);
975 size_t base
= al_string_length(*str
);
978 VECTOR_RESERVE(*str
, base
+len
+1);
979 VECTOR_RESIZE(*str
, base
+len
);
980 for(i
= 0;i
< len
;i
++)
981 VECTOR_ELEM(*str
, base
+i
) = from
[i
];
982 *VECTOR_END(*str
) = 0;
986 void al_string_append_range(al_string
*str
, const al_string_char_type
*from
, const al_string_char_type
*to
)
990 size_t base
= al_string_length(*str
);
991 size_t len
= to
- from
;
994 VECTOR_RESERVE(*str
, base
+len
+1);
995 VECTOR_RESIZE(*str
, base
+len
);
996 for(i
= 0;i
< len
;i
++)
997 VECTOR_ELEM(*str
, base
+i
) = from
[i
];
998 *VECTOR_END(*str
) = 0;
1003 void al_string_copy_wcstr(al_string
*str
, const wchar_t *from
)
1006 if((len
=WideCharToMultiByte(CP_UTF8
, 0, from
, -1, NULL
, 0, NULL
, NULL
)) > 0)
1008 VECTOR_RESERVE(*str
, len
);
1009 VECTOR_RESIZE(*str
, len
-1);
1010 WideCharToMultiByte(CP_UTF8
, 0, from
, -1, &VECTOR_FRONT(*str
), len
, NULL
, NULL
);
1011 *VECTOR_END(*str
) = 0;
1015 void al_string_append_wcstr(al_string
*str
, const wchar_t *from
)
1018 if((len
=WideCharToMultiByte(CP_UTF8
, 0, from
, -1, NULL
, 0, NULL
, NULL
)) > 0)
1020 size_t strlen
= al_string_length(*str
);
1021 VECTOR_RESERVE(*str
, strlen
+len
);
1022 VECTOR_RESIZE(*str
, strlen
+len
-1);
1023 WideCharToMultiByte(CP_UTF8
, 0, from
, -1, &VECTOR_FRONT(*str
) + strlen
, len
, NULL
, NULL
);
1024 *VECTOR_END(*str
) = 0;
1028 void al_string_append_wrange(al_string
*str
, const wchar_t *from
, const wchar_t *to
)
1031 if((len
=WideCharToMultiByte(CP_UTF8
, 0, from
, (int)(to
-from
), NULL
, 0, NULL
, NULL
)) > 0)
1033 size_t strlen
= al_string_length(*str
);
1034 VECTOR_RESERVE(*str
, strlen
+len
+1);
1035 VECTOR_RESIZE(*str
, strlen
+len
);
1036 WideCharToMultiByte(CP_UTF8
, 0, from
, (int)(to
-from
), &VECTOR_FRONT(*str
) + strlen
, len
+1, NULL
, NULL
);
1037 *VECTOR_END(*str
) = 0;