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
49 #ifdef HAVE_SSE_INTRINSICS
50 #include <xmmintrin.h>
52 #ifdef HAVE_SYS_SYSCONF_H
53 #include <sys/sysconf.h>
56 #ifdef HAVE_PROC_PIDPATH
61 #include <sys/types.h>
62 #include <sys/sysctl.h>
67 #elif defined(_WIN32_IE)
73 #include "alfstream.h"
78 #include "fpu_modes.h"
84 #if defined(HAVE_GCC_GET_CPUID) && (defined(__i386__) || defined(__x86_64__) || \
85 defined(_M_IX86) || defined(_M_X64))
86 using reg_type
= unsigned int;
87 static inline void get_cpuid(unsigned int f
, reg_type
*regs
)
88 { __get_cpuid(f
, ®s
[0], ®s
[1], ®s
[2], ®s
[3]); }
90 #elif defined(HAVE_CPUID_INTRINSIC) && (defined(__i386__) || defined(__x86_64__) || \
91 defined(_M_IX86) || defined(_M_X64))
93 static inline void get_cpuid(unsigned int f
, reg_type
*regs
)
94 { (__cpuid
)(regs
, f
); }
100 void FillCPUCaps(int capfilter
)
104 /* FIXME: We really should get this for all available CPUs in case different
105 * CPUs have different caps (is that possible on one machine?). */
109 char str
[sizeof(reg_type
[4])];
112 get_cpuid(0, cpuinf
[0].regs
);
113 if(cpuinf
[0].regs
[0] == 0)
114 ERR("Failed to get CPUID\n");
117 unsigned int maxfunc
= cpuinf
[0].regs
[0];
118 unsigned int maxextfunc
;
120 get_cpuid(0x80000000, cpuinf
[0].regs
);
121 maxextfunc
= cpuinf
[0].regs
[0];
123 TRACE("Detected max CPUID function: 0x%x (ext. 0x%x)\n", maxfunc
, maxextfunc
);
125 TRACE("Vendor ID: \"%.4s%.4s%.4s\"\n", cpuinf
[0].str
+4, cpuinf
[0].str
+12, cpuinf
[0].str
+8);
126 if(maxextfunc
>= 0x80000004)
128 get_cpuid(0x80000002, cpuinf
[0].regs
);
129 get_cpuid(0x80000003, cpuinf
[1].regs
);
130 get_cpuid(0x80000004, cpuinf
[2].regs
);
131 TRACE("Name: \"%.16s%.16s%.16s\"\n", cpuinf
[0].str
, cpuinf
[1].str
, cpuinf
[2].str
);
136 get_cpuid(1, cpuinf
[0].regs
);
137 if((cpuinf
[0].regs
[3]&(1<<25)))
139 if((caps
&CPU_CAP_SSE
) && (cpuinf
[0].regs
[3]&(1<<26)))
140 caps
|= CPU_CAP_SSE2
;
141 if((caps
&CPU_CAP_SSE2
) && (cpuinf
[0].regs
[2]&(1<<0)))
142 caps
|= CPU_CAP_SSE3
;
143 if((caps
&CPU_CAP_SSE3
) && (cpuinf
[0].regs
[2]&(1<<19)))
144 caps
|= CPU_CAP_SSE4_1
;
148 /* Assume support for whatever's supported if we can't check for it */
149 #if defined(HAVE_SSE4_1)
150 #warning "Assuming SSE 4.1 run-time support!"
151 caps
|= CPU_CAP_SSE
| CPU_CAP_SSE2
| CPU_CAP_SSE3
| CPU_CAP_SSE4_1
;
152 #elif defined(HAVE_SSE3)
153 #warning "Assuming SSE 3 run-time support!"
154 caps
|= CPU_CAP_SSE
| CPU_CAP_SSE2
| CPU_CAP_SSE3
;
155 #elif defined(HAVE_SSE2)
156 #warning "Assuming SSE 2 run-time support!"
157 caps
|= CPU_CAP_SSE
| CPU_CAP_SSE2
;
158 #elif defined(HAVE_SSE)
159 #warning "Assuming SSE run-time support!"
164 al::ifstream file
{"/proc/cpuinfo"};
166 ERR("Failed to open /proc/cpuinfo, cannot check for NEON support\n");
169 std::string features
;
171 auto getline
= [](std::istream
&f
, std::string
&output
) -> bool
173 while(f
.good() && f
.peek() == '\n')
175 return std::getline(f
, output
) && !output
.empty();
178 while(getline(file
, features
))
180 if(features
.compare(0, 10, "Features\t:", 10) == 0)
186 while((extpos
=features
.find("neon", extpos
+1)) != std::string::npos
)
188 if((extpos
== 0 || std::isspace(features
[extpos
-1])) &&
189 (extpos
+4 == features
.length() || std::isspace(features
[extpos
+4])))
191 caps
|= CPU_CAP_NEON
;
198 TRACE("Extensions:%s%s%s%s%s%s\n",
199 ((capfilter
&CPU_CAP_SSE
) ? ((caps
&CPU_CAP_SSE
) ? " +SSE" : " -SSE") : ""),
200 ((capfilter
&CPU_CAP_SSE2
) ? ((caps
&CPU_CAP_SSE2
) ? " +SSE2" : " -SSE2") : ""),
201 ((capfilter
&CPU_CAP_SSE3
) ? ((caps
&CPU_CAP_SSE3
) ? " +SSE3" : " -SSE3") : ""),
202 ((capfilter
&CPU_CAP_SSE4_1
) ? ((caps
&CPU_CAP_SSE4_1
) ? " +SSE4.1" : " -SSE4.1") : ""),
203 ((capfilter
&CPU_CAP_NEON
) ? ((caps
&CPU_CAP_NEON
) ? " +NEON" : " -NEON") : ""),
204 ((!capfilter
) ? " -none-" : "")
206 CPUCapFlags
= caps
& capfilter
;
212 #if defined(HAVE_SSE_INTRINSICS)
213 this->sse_state
= _mm_getcsr();
214 unsigned int sseState
= this->sse_state
;
215 sseState
|= 0x8000; /* set flush-to-zero */
216 sseState
|= 0x0040; /* set denormals-are-zero */
217 _mm_setcsr(sseState
);
219 #elif defined(__GNUC__) && defined(HAVE_SSE)
221 if((CPUCapFlags
&CPU_CAP_SSE
))
223 __asm__
__volatile__("stmxcsr %0" : "=m" (*&this->sse_state
));
224 unsigned int sseState
= this->sse_state
;
225 sseState
|= 0x8000; /* set flush-to-zero */
226 if((CPUCapFlags
&CPU_CAP_SSE2
))
227 sseState
|= 0x0040; /* set denormals-are-zero */
228 __asm__
__volatile__("ldmxcsr %0" : : "m" (*&sseState
));
232 this->in_mode
= true;
237 if(!this->in_mode
) return;
239 #if defined(HAVE_SSE_INTRINSICS)
240 _mm_setcsr(this->sse_state
);
242 #elif defined(__GNUC__) && defined(HAVE_SSE)
244 if((CPUCapFlags
&CPU_CAP_SSE
))
245 __asm__
__volatile__("ldmxcsr %0" : : "m" (*&this->sse_state
));
247 this->in_mode
= false;
253 const PathNamePair
&GetProcBinary()
255 static PathNamePair ret
;
256 if(!ret
.fname
.empty() || !ret
.path
.empty())
259 al::vector
<WCHAR
> fullpath(256);
261 while((len
=GetModuleFileNameW(nullptr, fullpath
.data(), static_cast<DWORD
>(fullpath
.size()))) == fullpath
.size())
262 fullpath
.resize(fullpath
.size() << 1);
265 ERR("Failed to get process name: error %lu\n", GetLastError());
269 fullpath
.resize(len
);
270 if(fullpath
.back() != 0)
271 fullpath
.push_back(0);
273 auto sep
= std::find(fullpath
.rbegin()+1, fullpath
.rend(), '\\');
274 sep
= std::find(fullpath
.rbegin()+1, sep
, '/');
275 if(sep
!= fullpath
.rend())
278 ret
.fname
= wstr_to_utf8(&*sep
+ 1);
279 ret
.path
= wstr_to_utf8(fullpath
.data());
282 ret
.fname
= wstr_to_utf8(fullpath
.data());
284 TRACE("Got binary: %s, %s\n", ret
.path
.c_str(), ret
.fname
.c_str());
289 void al_print(FILE *logfile
, const char *fmt
, ...)
291 al::vector
<char> dynmsg
;
297 va_copy(args2
, args
);
298 int msglen
{std::vsnprintf(str
, sizeof(stcmsg
), fmt
, args
)};
299 if UNLIKELY(msglen
>= 0 && static_cast<size_t>(msglen
) >= sizeof(stcmsg
))
301 dynmsg
.resize(static_cast<size_t>(msglen
) + 1u);
303 msglen
= std::vsnprintf(str
, dynmsg
.size(), fmt
, args2
);
308 std::wstring wstr
{utf8_to_wstr(str
)};
309 fputws(wstr
.c_str(), logfile
);
314 static inline int is_slash(int c
)
315 { return (c
== '\\' || c
== '/'); }
317 static void DirectorySearch(const char *path
, const char *ext
, al::vector
<std::string
> *const results
)
319 std::string pathstr
{path
};
322 TRACE("Searching %s\n", pathstr
.c_str());
324 std::wstring wpath
{utf8_to_wstr(pathstr
.c_str())};
325 WIN32_FIND_DATAW fdata
;
326 HANDLE hdl
{FindFirstFileW(wpath
.c_str(), &fdata
)};
327 if(hdl
== INVALID_HANDLE_VALUE
) return;
329 const auto base
= results
->size();
332 results
->emplace_back();
333 std::string
&str
= results
->back();
336 str
+= wstr_to_utf8(fdata
.cFileName
);
337 } while(FindNextFileW(hdl
, &fdata
));
340 const al::span
<std::string
> newlist
{results
->data()+base
, results
->size()-base
};
341 std::sort(newlist
.begin(), newlist
.end());
342 for(const auto &name
: newlist
)
343 TRACE(" got %s\n", name
.c_str());
346 al::vector
<std::string
> SearchDataFiles(const char *ext
, const char *subdir
)
348 static std::mutex search_lock
;
349 std::lock_guard
<std::mutex
> _
{search_lock
};
351 /* If the path is absolute, use it directly. */
352 al::vector
<std::string
> results
;
353 if(isalpha(subdir
[0]) && subdir
[1] == ':' && is_slash(subdir
[2]))
355 std::string path
{subdir
};
356 std::replace(path
.begin(), path
.end(), '/', '\\');
357 DirectorySearch(path
.c_str(), ext
, &results
);
360 if(subdir
[0] == '\\' && subdir
[1] == '\\' && subdir
[2] == '?' && subdir
[3] == '\\')
362 DirectorySearch(subdir
, ext
, &results
);
368 /* Search the app-local directory. */
369 if(auto localpath
= al::getenv(L
"ALSOFT_LOCAL_PATH"))
371 path
= wstr_to_utf8(localpath
->c_str());
372 if(is_slash(path
.back()))
375 else if(WCHAR
*cwdbuf
{_wgetcwd(nullptr, 0)})
377 path
= wstr_to_utf8(cwdbuf
);
378 if(is_slash(path
.back()))
384 std::replace(path
.begin(), path
.end(), '/', '\\');
385 DirectorySearch(path
.c_str(), ext
, &results
);
387 /* Search the local and global data dirs. */
388 static constexpr int ids
[2]{ CSIDL_APPDATA
, CSIDL_COMMON_APPDATA
};
391 WCHAR buffer
[MAX_PATH
];
392 if(SHGetSpecialFolderPathW(nullptr, buffer
, id
, FALSE
) == FALSE
)
395 path
= wstr_to_utf8(buffer
);
396 if(!is_slash(path
.back()))
399 std::replace(path
.begin(), path
.end(), '/', '\\');
401 DirectorySearch(path
.c_str(), ext
, &results
);
407 void SetRTPriority(void)
411 failed
= !SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL
);
412 if(failed
) ERR("Failed to set priority level for thread\n");
417 #if defined(HAVE_PTHREAD_SETSCHEDPARAM) && !defined(__OpenBSD__)
422 const PathNamePair
&GetProcBinary()
424 static PathNamePair ret
;
425 if(!ret
.fname
.empty() || !ret
.path
.empty())
428 al::vector
<char> pathname
;
431 int mib
[4] = { CTL_KERN
, KERN_PROC
, KERN_PROC_PATHNAME
, -1 };
432 if(sysctl(mib
, 4, nullptr, &pathlen
, nullptr, 0) == -1)
433 WARN("Failed to sysctl kern.proc.pathname: %s\n", strerror(errno
));
436 pathname
.resize(pathlen
+ 1);
437 sysctl(mib
, 4, pathname
.data(), &pathlen
, nullptr, 0);
438 pathname
.resize(pathlen
);
441 #ifdef HAVE_PROC_PIDPATH
444 char procpath
[PROC_PIDPATHINFO_MAXSIZE
]{};
445 const pid_t pid
{getpid()};
446 if(proc_pidpath(pid
, procpath
, sizeof(procpath
)) < 1)
447 ERR("proc_pidpath(%d, ...) failed: %s\n", pid
, strerror(errno
));
449 pathname
.insert(pathname
.end(), procpath
, procpath
+strlen(procpath
));
454 pathname
.resize(256);
456 const char *selfname
{"/proc/self/exe"};
457 ssize_t len
{readlink(selfname
, pathname
.data(), pathname
.size())};
458 if(len
== -1 && errno
== ENOENT
)
460 selfname
= "/proc/self/file";
461 len
= readlink(selfname
, pathname
.data(), pathname
.size());
463 if(len
== -1 && errno
== ENOENT
)
465 selfname
= "/proc/curproc/exe";
466 len
= readlink(selfname
, pathname
.data(), pathname
.size());
468 if(len
== -1 && errno
== ENOENT
)
470 selfname
= "/proc/curproc/file";
471 len
= readlink(selfname
, pathname
.data(), pathname
.size());
474 while(len
> 0 && static_cast<size_t>(len
) == pathname
.size())
476 pathname
.resize(pathname
.size() << 1);
477 len
= readlink(selfname
, pathname
.data(), pathname
.size());
481 WARN("Failed to readlink %s: %s\n", selfname
, strerror(errno
));
485 pathname
.resize(static_cast<size_t>(len
));
487 while(!pathname
.empty() && pathname
.back() == 0)
490 auto sep
= std::find(pathname
.crbegin(), pathname
.crend(), '/');
491 if(sep
!= pathname
.crend())
493 ret
.path
= std::string(pathname
.cbegin(), sep
.base()-1);
494 ret
.fname
= std::string(sep
.base(), pathname
.cend());
497 ret
.fname
= std::string(pathname
.cbegin(), pathname
.cend());
499 TRACE("Got binary: %s, %s\n", ret
.path
.c_str(), ret
.fname
.c_str());
504 void al_print(FILE *logfile
, const char *fmt
, ...)
509 vfprintf(logfile
, fmt
, ap
);
516 static void DirectorySearch(const char *path
, const char *ext
, al::vector
<std::string
> *const results
)
518 TRACE("Searching %s for *%s\n", path
, ext
);
519 DIR *dir
{opendir(path
)};
522 const auto base
= results
->size();
523 const size_t extlen
{strlen(ext
)};
525 struct dirent
*dirent
;
526 while((dirent
=readdir(dir
)) != nullptr)
528 if(strcmp(dirent
->d_name
, ".") == 0 || strcmp(dirent
->d_name
, "..") == 0)
531 const size_t len
{strlen(dirent
->d_name
)};
532 if(len
<= extlen
) continue;
533 if(al::strcasecmp(dirent
->d_name
+len
-extlen
, ext
) != 0)
536 results
->emplace_back();
537 std::string
&str
= results
->back();
539 if(str
.back() != '/')
541 str
+= dirent
->d_name
;
545 const al::span
<std::string
> newlist
{results
->data()+base
, results
->size()-base
};
546 std::sort(newlist
.begin(), newlist
.end());
547 for(const auto &name
: newlist
)
548 TRACE(" got %s\n", name
.c_str());
551 al::vector
<std::string
> SearchDataFiles(const char *ext
, const char *subdir
)
553 static std::mutex search_lock
;
554 std::lock_guard
<std::mutex
> _
{search_lock
};
556 al::vector
<std::string
> results
;
559 DirectorySearch(subdir
, ext
, &results
);
563 /* Search the app-local directory. */
564 if(auto localpath
= al::getenv("ALSOFT_LOCAL_PATH"))
565 DirectorySearch(localpath
->c_str(), ext
, &results
);
568 al::vector
<char> cwdbuf(256);
569 while(!getcwd(cwdbuf
.data(), cwdbuf
.size()))
576 cwdbuf
.resize(cwdbuf
.size() << 1);
579 DirectorySearch(".", ext
, &results
);
582 DirectorySearch(cwdbuf
.data(), ext
, &results
);
587 // Search local data dir
588 if(auto datapath
= al::getenv("XDG_DATA_HOME"))
590 std::string
&path
= *datapath
;
591 if(path
.back() != '/')
594 DirectorySearch(path
.c_str(), ext
, &results
);
596 else if(auto homepath
= al::getenv("HOME"))
598 std::string
&path
= *homepath
;
599 if(path
.back() == '/')
601 path
+= "/.local/share/";
603 DirectorySearch(path
.c_str(), ext
, &results
);
606 // Search global data dirs
607 std::string datadirs
{al::getenv("XDG_DATA_DIRS").value_or("/usr/local/share/:/usr/share/")};
610 while(curpos
< datadirs
.size())
612 size_t nextpos
{datadirs
.find(':', curpos
)};
614 std::string path
{(nextpos
!= std::string::npos
) ?
615 datadirs
.substr(curpos
, nextpos
++ - curpos
) : datadirs
.substr(curpos
)};
618 if(path
.empty()) continue;
619 if(path
.back() != '/')
623 DirectorySearch(path
.c_str(), ext
, &results
);
632 #if defined(HAVE_PTHREAD_SETSCHEDPARAM) && !defined(__OpenBSD__)
635 struct sched_param param
;
636 /* Use the minimum real-time priority possible for now (on Linux this
637 * should be 1 for SCHED_RR) */
638 param
.sched_priority
= sched_get_priority_min(SCHED_RR
);
639 failed
= !!pthread_setschedparam(pthread_self(), SCHED_RR
, ¶m
);
642 /* Real-time priority not available */
643 failed
= (RTPrioLevel
>0);
646 ERR("Failed to set priority level for thread\n");