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 #ifdef HAVE_PROC_PIDPATH
47 #include <sys/types.h>
48 #include <sys/sysctl.h>
51 #ifndef AL_NO_UID_DEFS
52 #if defined(HAVE_GUIDDEF_H) || defined(HAVE_INITGUID_H)
61 DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM
, 0x00000001, 0x0000, 0x0010, 0x80,0x00, 0x00,0xaa,0x00,0x38,0x9b,0x71);
62 DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
, 0x00000003, 0x0000, 0x0010, 0x80,0x00, 0x00,0xaa,0x00,0x38,0x9b,0x71);
64 DEFINE_GUID(IID_IDirectSoundNotify
, 0xb0210783, 0x89cd, 0x11d0, 0xaf,0x08, 0x00,0xa0,0xc9,0x25,0xcd,0x16);
66 DEFINE_GUID(CLSID_MMDeviceEnumerator
, 0xbcde0395, 0xe52f, 0x467c, 0x8e,0x3d, 0xc4,0x57,0x92,0x91,0x69,0x2e);
67 DEFINE_GUID(IID_IMMDeviceEnumerator
, 0xa95664d2, 0x9614, 0x4f35, 0xa7,0x46, 0xde,0x8d,0xb6,0x36,0x17,0xe6);
68 DEFINE_GUID(IID_IAudioClient
, 0x1cb9ad4c, 0xdbfa, 0x4c32, 0xb1,0x78, 0xc2,0xf5,0x68,0xa7,0x03,0xb2);
69 DEFINE_GUID(IID_IAudioRenderClient
, 0xf294acfc, 0x3146, 0x4483, 0xa7,0xbf, 0xad,0xdc,0xa7,0xc2,0x60,0xe2);
70 DEFINE_GUID(IID_IAudioCaptureClient
, 0xc8adbd64, 0xe71e, 0x48a0, 0xa4,0xde, 0x18,0x5c,0x39,0x5c,0xd3,0x17);
74 #include <devpropdef.h>
75 #include <propkeydef.h>
76 DEFINE_DEVPROPKEY(DEVPKEY_Device_FriendlyName
, 0xa45c254e, 0xdf1c, 0x4efd, 0x80,0x20, 0x67,0xd1,0x46,0xa8,0x50,0xe0, 14);
77 DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_FormFactor
, 0x1da5d803, 0xd492, 0x4edd, 0x8c,0x23, 0xe0,0xc0,0xff,0xee,0x7f,0x0e, 0);
78 DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_GUID
, 0x1da5d803, 0xd492, 0x4edd, 0x8c, 0x23,0xe0, 0xc0,0xff,0xee,0x7f,0x0e, 4 );
81 #endif /* AL_NO_UID_DEFS */
92 #ifdef HAVE_SYS_SYSCONF_H
93 #include <sys/sysconf.h>
103 #include <sys/types.h>
104 #include <sys/stat.h>
105 #include <sys/mman.h>
108 #elif defined(_WIN32_IE)
119 #include "cpu_caps.h"
120 #include "fpu_modes.h"
126 #if defined(HAVE_GCC_GET_CPUID) && (defined(__i386__) || defined(__x86_64__) || \
127 defined(_M_IX86) || defined(_M_X64))
128 using reg_type
= unsigned int;
129 static inline void get_cpuid(int f
, reg_type
*regs
)
130 { __get_cpuid(f
, ®s
[0], ®s
[1], ®s
[2], ®s
[3]); }
131 #define CAN_GET_CPUID
132 #elif defined(HAVE_CPUID_INTRINSIC) && (defined(__i386__) || defined(__x86_64__) || \
133 defined(_M_IX86) || defined(_M_X64))
134 using reg_type
= int;
135 static inline void get_cpuid(int f
, reg_type
*regs
)
136 { (__cpuid
)(regs
, f
); }
137 #define CAN_GET_CPUID
142 void FillCPUCaps(int capfilter
)
146 /* FIXME: We really should get this for all available CPUs in case different
147 * CPUs have different caps (is that possible on one machine?). */
151 char str
[sizeof(reg_type
[4])];
152 } cpuinf
[3] = {{ { 0, 0, 0, 0 } }};
154 get_cpuid(0, cpuinf
[0].regs
);
155 if(cpuinf
[0].regs
[0] == 0)
156 ERR("Failed to get CPUID\n");
159 unsigned int maxfunc
= cpuinf
[0].regs
[0];
160 unsigned int maxextfunc
;
162 get_cpuid(0x80000000, cpuinf
[0].regs
);
163 maxextfunc
= cpuinf
[0].regs
[0];
165 TRACE("Detected max CPUID function: 0x%x (ext. 0x%x)\n", maxfunc
, maxextfunc
);
167 TRACE("Vendor ID: \"%.4s%.4s%.4s\"\n", cpuinf
[0].str
+4, cpuinf
[0].str
+12, cpuinf
[0].str
+8);
168 if(maxextfunc
>= 0x80000004)
170 get_cpuid(0x80000002, cpuinf
[0].regs
);
171 get_cpuid(0x80000003, cpuinf
[1].regs
);
172 get_cpuid(0x80000004, cpuinf
[2].regs
);
173 TRACE("Name: \"%.16s%.16s%.16s\"\n", cpuinf
[0].str
, cpuinf
[1].str
, cpuinf
[2].str
);
178 get_cpuid(1, cpuinf
[0].regs
);
179 if((cpuinf
[0].regs
[3]&(1<<25)))
181 if((caps
&CPU_CAP_SSE
) && (cpuinf
[0].regs
[3]&(1<<26)))
182 caps
|= CPU_CAP_SSE2
;
183 if((caps
&CPU_CAP_SSE2
) && (cpuinf
[0].regs
[2]&(1<<0)))
184 caps
|= CPU_CAP_SSE3
;
185 if((caps
&CPU_CAP_SSE3
) && (cpuinf
[0].regs
[2]&(1<<19)))
186 caps
|= CPU_CAP_SSE4_1
;
190 /* Assume support for whatever's supported if we can't check for it */
191 #if defined(HAVE_SSE4_1)
192 #warning "Assuming SSE 4.1 run-time support!"
193 caps
|= CPU_CAP_SSE
| CPU_CAP_SSE2
| CPU_CAP_SSE3
| CPU_CAP_SSE4_1
;
194 #elif defined(HAVE_SSE3)
195 #warning "Assuming SSE 3 run-time support!"
196 caps
|= CPU_CAP_SSE
| CPU_CAP_SSE2
| CPU_CAP_SSE3
;
197 #elif defined(HAVE_SSE2)
198 #warning "Assuming SSE 2 run-time support!"
199 caps
|= CPU_CAP_SSE
| CPU_CAP_SSE2
;
200 #elif defined(HAVE_SSE)
201 #warning "Assuming SSE run-time support!"
206 FILE *file
= fopen("/proc/cpuinfo", "rt");
208 ERR("Failed to open /proc/cpuinfo, cannot check for NEON support\n");
211 std::string features
;
214 while(fgets(buf
, sizeof(buf
), file
) != nullptr)
216 if(strncmp(buf
, "Features\t:", 10) != 0)
220 while(features
.back() != '\n')
222 if(fgets(buf
, sizeof(buf
), file
) == nullptr)
231 if(!features
.empty())
233 const char *str
= features
.c_str();
234 while(isspace(str
[0])) ++str
;
236 TRACE("Got features string:%s\n", str
);
237 while((str
=strstr(str
, "neon")) != nullptr)
239 if(isspace(*(str
-1)) && (str
[4] == 0 || isspace(str
[4])))
241 caps
|= CPU_CAP_NEON
;
250 TRACE("Extensions:%s%s%s%s%s%s\n",
251 ((capfilter
&CPU_CAP_SSE
) ? ((caps
&CPU_CAP_SSE
) ? " +SSE" : " -SSE") : ""),
252 ((capfilter
&CPU_CAP_SSE2
) ? ((caps
&CPU_CAP_SSE2
) ? " +SSE2" : " -SSE2") : ""),
253 ((capfilter
&CPU_CAP_SSE3
) ? ((caps
&CPU_CAP_SSE3
) ? " +SSE3" : " -SSE3") : ""),
254 ((capfilter
&CPU_CAP_SSE4_1
) ? ((caps
&CPU_CAP_SSE4_1
) ? " +SSE4.1" : " -SSE4.1") : ""),
255 ((capfilter
&CPU_CAP_NEON
) ? ((caps
&CPU_CAP_NEON
) ? " +NEON" : " -NEON") : ""),
256 ((!capfilter
) ? " -none-" : "")
258 CPUCapFlags
= caps
& capfilter
;
262 FPUCtl::FPUCtl() noexcept
264 #if defined(__GNUC__) && defined(HAVE_SSE)
265 if((CPUCapFlags
&CPU_CAP_SSE
))
267 __asm__
__volatile__("stmxcsr %0" : "=m" (*&this->sse_state
));
268 unsigned int sseState
= this->sse_state
;
269 sseState
|= 0x8000; /* set flush-to-zero */
270 if((CPUCapFlags
&CPU_CAP_SSE2
))
271 sseState
|= 0x0040; /* set denormals-are-zero */
272 __asm__
__volatile__("ldmxcsr %0" : : "m" (*&sseState
));
275 #elif defined(HAVE___CONTROL87_2)
277 __control87_2(0, 0, &this->state
, &this->sse_state
);
278 _control87(_DN_FLUSH
, _MCW_DN
);
280 #elif defined(HAVE__CONTROLFP)
282 this->state
= _controlfp(0, 0);
283 _controlfp(_DN_FLUSH
, _MCW_DN
);
286 this->in_mode
= true;
289 void FPUCtl::leave() noexcept
291 if(!this->in_mode
) return;
293 #if defined(__GNUC__) && defined(HAVE_SSE)
294 if((CPUCapFlags
&CPU_CAP_SSE
))
295 __asm__
__volatile__("ldmxcsr %0" : : "m" (*&this->sse_state
));
297 #elif defined(HAVE___CONTROL87_2)
300 __control87_2(this->state
, _MCW_DN
, &mode
, nullptr);
301 __control87_2(this->sse_state
, _MCW_DN
, nullptr, &mode
);
303 #elif defined(HAVE__CONTROLFP)
305 _controlfp(this->state
, _MCW_DN
);
307 this->in_mode
= false;
313 const PathNamePair
&GetProcBinary()
315 static PathNamePair ret
;
316 if(!ret
.fname
.empty() || !ret
.path
.empty())
319 al::vector
<WCHAR
> fullpath(256);
321 while((len
=GetModuleFileNameW(nullptr, fullpath
.data(), static_cast<DWORD
>(fullpath
.size()))) == fullpath
.size())
322 fullpath
.resize(fullpath
.size() << 1);
325 ERR("Failed to get process name: error %lu\n", GetLastError());
329 fullpath
.resize(len
);
330 if(fullpath
.back() != 0)
331 fullpath
.push_back(0);
333 auto sep
= std::find(fullpath
.rbegin()+1, fullpath
.rend(), '\\');
334 sep
= std::find(fullpath
.rbegin()+1, sep
, '/');
335 if(sep
!= fullpath
.rend())
338 ret
.fname
= wstr_to_utf8(&*sep
+ 1);
339 ret
.path
= wstr_to_utf8(fullpath
.data());
342 ret
.fname
= wstr_to_utf8(fullpath
.data());
344 TRACE("Got: %s, %s\n", ret
.path
.c_str(), ret
.fname
.c_str());
349 void *LoadLib(const char *name
)
351 std::wstring wname
{utf8_to_wstr(name
)};
352 return LoadLibraryW(wname
.c_str());
354 void CloseLib(void *handle
)
355 { FreeLibrary(static_cast<HMODULE
>(handle
)); }
356 void *GetSymbol(void *handle
, const char *name
)
358 void *ret
{reinterpret_cast<void*>(GetProcAddress(static_cast<HMODULE
>(handle
), name
))};
359 if(!ret
) ERR("Failed to load %s\n", name
);
364 void al_print(const char *type
, const char *prefix
, const char *func
, const char *fmt
, ...)
370 vsnprintf(str
, sizeof(str
), fmt
, ap
);
372 str
[sizeof(str
)-1] = 0;
374 std::wstring wstr
{utf8_to_wstr(str
)};
375 fprintf(gLogFile
, "AL lib: %s %s%s: %ls", type
, prefix
, func
, wstr
.c_str());
380 static inline int is_slash(int c
)
381 { return (c
== '\\' || c
== '/'); }
383 static void DirectorySearch(const char *path
, const char *ext
, al::vector
<std::string
> *const results
)
385 std::string pathstr
{path
};
388 TRACE("Searching %s\n", pathstr
.c_str());
390 std::wstring wpath
{utf8_to_wstr(pathstr
.c_str())};
391 WIN32_FIND_DATAW fdata
;
392 HANDLE hdl
{FindFirstFileW(wpath
.c_str(), &fdata
)};
393 if(hdl
!= INVALID_HANDLE_VALUE
)
395 size_t base
= results
->size();
397 results
->emplace_back();
398 std::string
&str
= results
->back();
401 str
+= wstr_to_utf8(fdata
.cFileName
);
402 TRACE("Got result %s\n", str
.c_str());
403 } while(FindNextFileW(hdl
, &fdata
));
406 std::sort(results
->begin()+base
, results
->end());
410 al::vector
<std::string
> SearchDataFiles(const char *ext
, const char *subdir
)
412 static std::mutex search_lock
;
413 std::lock_guard
<std::mutex
> _
{search_lock
};
415 /* If the path is absolute, use it directly. */
416 al::vector
<std::string
> results
;
417 if(isalpha(subdir
[0]) && subdir
[1] == ':' && is_slash(subdir
[2]))
419 std::string path
{subdir
};
420 std::replace(path
.begin(), path
.end(), '/', '\\');
421 DirectorySearch(path
.c_str(), ext
, &results
);
424 if(subdir
[0] == '\\' && subdir
[1] == '\\' && subdir
[2] == '?' && subdir
[3] == '\\')
426 DirectorySearch(subdir
, ext
, &results
);
432 /* Search the app-local directory. */
433 WCHAR
*cwdbuf
{_wgetenv(L
"ALSOFT_LOCAL_PATH")};
434 if(cwdbuf
&& *cwdbuf
!= '\0')
436 path
= wstr_to_utf8(cwdbuf
);
437 if(is_slash(path
.back()))
440 else if(!(cwdbuf
=_wgetcwd(nullptr, 0)))
444 path
= wstr_to_utf8(cwdbuf
);
445 if(is_slash(path
.back()))
449 std::replace(path
.begin(), path
.end(), '/', '\\');
450 DirectorySearch(path
.c_str(), ext
, &results
);
452 /* Search the local and global data dirs. */
453 static constexpr int ids
[2]{ CSIDL_APPDATA
, CSIDL_COMMON_APPDATA
};
456 WCHAR buffer
[MAX_PATH
];
457 if(SHGetSpecialFolderPathW(nullptr, buffer
, id
, FALSE
) == FALSE
)
460 path
= wstr_to_utf8(buffer
);
461 if(!is_slash(path
.back()))
464 std::replace(path
.begin(), path
.end(), '/', '\\');
466 DirectorySearch(path
.c_str(), ext
, &results
);
472 void SetRTPriority(void)
476 failed
= !SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL
);
477 if(failed
) ERR("Failed to set priority level for thread\n");
482 const PathNamePair
&GetProcBinary()
484 static PathNamePair ret
;
485 if(!ret
.fname
.empty() || !ret
.path
.empty())
488 al::vector
<char> pathname
;
491 int mib
[4] = { CTL_KERN
, KERN_PROC
, KERN_PROC_PATHNAME
, -1 };
492 if(sysctl(mib
, 4, nullptr, &pathlen
, nullptr, 0) == -1)
493 WARN("Failed to sysctl kern.proc.pathname: %s\n", strerror(errno
));
496 pathname
.resize(pathlen
+ 1);
497 sysctl(mib
, 4, pathname
.data(), &pathlen
, nullptr, 0);
498 pathname
.resize(pathlen
);
501 #ifdef HAVE_PROC_PIDPATH
504 char procpath
[PROC_PIDPATHINFO_MAXSIZE
]{};
505 const pid_t pid
{getpid()};
506 if(proc_pidpath(pid
, procpath
, sizeof(procpath
)) < 1)
507 ERR("proc_pidpath(%d, ...) failed: %s\n", pid
, strerror(errno
));
509 pathname
.insert(pathname
.end(), procpath
, procpath
+strlen(procpath
));
514 pathname
.resize(256);
516 const char *selfname
{"/proc/self/exe"};
517 ssize_t len
{readlink(selfname
, pathname
.data(), pathname
.size())};
518 if(len
== -1 && errno
== ENOENT
)
520 selfname
= "/proc/self/file";
521 len
= readlink(selfname
, pathname
.data(), pathname
.size());
523 if(len
== -1 && errno
== ENOENT
)
525 selfname
= "/proc/curproc/exe";
526 len
= readlink(selfname
, pathname
.data(), pathname
.size());
528 if(len
== -1 && errno
== ENOENT
)
530 selfname
= "/proc/curproc/file";
531 len
= readlink(selfname
, pathname
.data(), pathname
.size());
534 while(len
> 0 && static_cast<size_t>(len
) == pathname
.size())
536 pathname
.resize(pathname
.size() << 1);
537 len
= readlink(selfname
, pathname
.data(), pathname
.size());
541 WARN("Failed to readlink %s: %s\n", selfname
, strerror(errno
));
545 pathname
.resize(len
);
547 while(!pathname
.empty() && pathname
.back() == 0)
550 auto sep
= std::find(pathname
.crbegin(), pathname
.crend(), '/');
551 if(sep
!= pathname
.crend())
553 ret
.path
= std::string(pathname
.cbegin(), sep
.base()-1);
554 ret
.fname
= std::string(sep
.base(), pathname
.cend());
557 ret
.fname
= std::string(pathname
.cbegin(), pathname
.cend());
559 TRACE("Got: %s, %s\n", ret
.path
.c_str(), ret
.fname
.c_str());
566 void *LoadLib(const char *name
)
569 void *handle
{dlopen(name
, RTLD_NOW
)};
570 const char *err
{dlerror()};
571 if(err
) handle
= nullptr;
574 void CloseLib(void *handle
)
576 void *GetSymbol(void *handle
, const char *name
)
579 void *sym
{dlsym(handle
, name
)};
580 const char *err
{dlerror()};
583 WARN("Failed to load %s: %s\n", name
, err
);
589 #endif /* HAVE_DLFCN_H */
591 void al_print(const char *type
, const char *prefix
, const char *func
, const char *fmt
, ...)
596 fprintf(gLogFile
, "AL lib: %s %s%s: ", type
, prefix
, func
);
597 vfprintf(gLogFile
, fmt
, ap
);
604 static void DirectorySearch(const char *path
, const char *ext
, al::vector
<std::string
> *const results
)
606 TRACE("Searching %s for *%s\n", path
, ext
);
607 DIR *dir
{opendir(path
)};
610 const size_t extlen
= strlen(ext
);
611 size_t base
= results
->size();
613 struct dirent
*dirent
;
614 while((dirent
=readdir(dir
)) != nullptr)
616 if(strcmp(dirent
->d_name
, ".") == 0 || strcmp(dirent
->d_name
, "..") == 0)
619 size_t len
{strlen(dirent
->d_name
)};
620 if(len
<= extlen
) continue;
621 if(strcasecmp(dirent
->d_name
+len
-extlen
, ext
) != 0)
624 results
->emplace_back();
625 std::string
&str
= results
->back();
627 if(str
.back() != '/')
629 str
+= dirent
->d_name
;
630 TRACE("Got result %s\n", str
.c_str());
634 std::sort(results
->begin()+base
, results
->end());
638 al::vector
<std::string
> SearchDataFiles(const char *ext
, const char *subdir
)
640 static std::mutex search_lock
;
641 std::lock_guard
<std::mutex
> _
{search_lock
};
643 al::vector
<std::string
> results
;
646 DirectorySearch(subdir
, ext
, &results
);
650 /* Search the app-local directory. */
651 const char *str
{getenv("ALSOFT_LOCAL_PATH")};
652 if(str
&& *str
!= '\0')
653 DirectorySearch(str
, ext
, &results
);
656 al::vector
<char> cwdbuf(256);
657 while(!getcwd(cwdbuf
.data(), cwdbuf
.size()))
664 cwdbuf
.resize(cwdbuf
.size() << 1);
667 DirectorySearch(".", ext
, &results
);
670 DirectorySearch(cwdbuf
.data(), ext
, &results
);
675 // Search local data dir
676 if((str
=getenv("XDG_DATA_HOME")) != nullptr && str
[0] != '\0')
678 std::string path
{str
};
679 if(path
.back() != '/')
682 DirectorySearch(path
.c_str(), ext
, &results
);
684 else if((str
=getenv("HOME")) != nullptr && str
[0] != '\0')
686 std::string path
{str
};
687 if(path
.back() == '/')
689 path
+= "/.local/share/";
691 DirectorySearch(path
.c_str(), ext
, &results
);
694 // Search global data dirs
695 if((str
=getenv("XDG_DATA_DIRS")) == nullptr || str
[0] == '\0')
696 str
= "/usr/local/share/:/usr/share/";
698 const char *next
{str
};
699 while((str
=next
) != nullptr && str
[0] != '\0')
701 next
= strchr(str
, ':');
703 std::string path
= (next
? std::string(str
, next
++) : std::string(str
));
704 if(path
.empty()) continue;
706 if(path
.back() != '/')
710 DirectorySearch(path
.c_str(), ext
, &results
);
716 void SetRTPriority(void)
719 #if defined(HAVE_PTHREAD_SETSCHEDPARAM) && !defined(__OpenBSD__)
722 struct sched_param param
;
723 /* Use the minimum real-time priority possible for now (on Linux this
724 * should be 1 for SCHED_RR) */
725 param
.sched_priority
= sched_get_priority_min(SCHED_RR
);
726 failed
= !!pthread_setschedparam(pthread_self(), SCHED_RR
, ¶m
);
729 /* Real-time priority not available */
730 failed
= (RTPrioLevel
>0);
733 ERR("Failed to set priority level for thread\n");