Version
menu_open
link
Target Platform(s):
Wwise SDK 2022.1.12
AkPlatformFuncs.h
Go to the documentation of this file.
1 /*******************************************************************************
2 The content of this file includes portions of the AUDIOKINETIC Wwise Technology
3 released in source code form as part of the SDK installer package.
4 
5 Commercial License Usage
6 
7 Licensees holding valid commercial licenses to the AUDIOKINETIC Wwise Technology
8 may use this file in accordance with the end user license agreement provided
9 with the software or, alternatively, in accordance with the terms contained in a
10 written agreement between you and Audiokinetic Inc.
11 
12 Apache License Usage
13 
14 Alternatively, this file may be used under the Apache License, Version 2.0 (the
15 "Apache License"); you may not use this file except in compliance with the
16 Apache License. You may obtain a copy of the Apache License at
17 http://www.apache.org/licenses/LICENSE-2.0.
18 
19 Unless required by applicable law or agreed to in writing, software distributed
20 under the Apache License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES
21 OR CONDITIONS OF ANY KIND, either express or implied. See the Apache License for
22 the specific language governing permissions and limitations under the License.
23 
24  Copyright (c) 2024 Audiokinetic Inc.
25 *******************************************************************************/
26 
27 #ifndef _AK_PLATFORM_FUNCS_H_
28 #define _AK_PLATFORM_FUNCS_H_
29 
30 #include "malloc.h"
33 #include <windows.h>
34 #include <stdio.h>
35 
36 #if defined(_WIN64)
37 // on 64 bit, removes warning C4985: 'ceil': attributes not present on previous declaration.
38 // see http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=294649
39 #include <math.h>
40 #endif // _WIN64
41 #include <intrin.h>
42 
43 #if defined(AK_XBOXSERIESX)
44 #include <ammintrin.h>
45 #endif
46 
47 //-----------------------------------------------------------------------------
48 // Platform-specific thread properties definition.
49 //-----------------------------------------------------------------------------
50 struct AkThreadProperties
51 {
52  int nPriority; ///< Thread priority
53 #ifdef AK_WIN_UNIVERSAL_APP
54  PROCESSOR_NUMBER processorNumber;///< Ideal processor (passed to SetThreadIdealProcessorEx)
55 #else
56  AkUInt32 dwAffinityMask; ///< Affinity mask
57 #endif
58  AkUInt32 uStackSize; ///< Thread stack size.
59 };
60 
61 //-----------------------------------------------------------------------------
62 // External variables.
63 //-----------------------------------------------------------------------------
64 // g_fFreqRatio is used by time helpers to return time values in milliseconds.
65 // It is declared and updated by the sound engine.
66 namespace AK
67 {
68  extern AkReal32 g_fFreqRatio;
69 }
70 
71 //-----------------------------------------------------------------------------
72 // Defines for Win32.
73 //-----------------------------------------------------------------------------
74 #define AK_DECLARE_THREAD_ROUTINE( FuncName ) DWORD WINAPI FuncName(LPVOID lpParameter)
75 #define AK_THREAD_RETURN( _param_ ) return (_param_);
76 #define AK_THREAD_ROUTINE_PARAMETER lpParameter
77 #define AK_GET_THREAD_ROUTINE_PARAMETER_PTR(type) reinterpret_cast<type*>( AK_THREAD_ROUTINE_PARAMETER )
78 #define AK_RETURN_THREAD_OK 0x00000000
79 #define AK_RETURN_THREAD_ERROR 0x00000001
80 #define AK_DEFAULT_STACK_SIZE (128*1024)
81 #define AK_VM_PAGE_SIZE (64*1024)
82 #define AK_VM_HUGE_PAGE_SIZE (2*1024*1024)
83 #define AK_THREAD_PRIORITY_NORMAL THREAD_PRIORITY_NORMAL
84 #define AK_THREAD_PRIORITY_ABOVE_NORMAL THREAD_PRIORITY_ABOVE_NORMAL
85 #define AK_THREAD_PRIORITY_BELOW_NORMAL THREAD_PRIORITY_BELOW_NORMAL
86 #define AK_THREAD_PRIORITY_TIME_CRITICAL THREAD_PRIORITY_TIME_CRITICAL
87 #define AK_THREAD_MODE_BACKGROUND_BEGIN THREAD_MODE_BACKGROUND_BEGIN
88 
89 // NULL objects
90 #define AK_NULL_THREAD NULL
91 
92 #define AK_INFINITE INFINITE
93 
94 #define AkMax(x1, x2) (((x1) > (x2))? (x1): (x2))
95 #define AkMin(x1, x2) (((x1) < (x2))? (x1): (x2))
96 #define AkClamp(x, min, max) ((x) < (min)) ? (min) : (((x) > (max) ? (max) : (x)))
97 
98 namespace AKPLATFORM
99 {
100  // Simple automatic event API
101  // ------------------------------------------------------------------
102 
103  /// Platform Independent Helper
104  inline void AkClearEvent( AkEvent & out_event )
105  {
106  out_event = NULL;
107  }
108 
109  /// Platform Independent Helper
110  inline AKRESULT AkCreateEvent( AkEvent & out_event )
111  {
112 #ifdef AK_USE_UWP_API
113  out_event = CreateEventEx(nullptr, nullptr, 0, STANDARD_RIGHTS_ALL|EVENT_MODIFY_STATE);
114 #else
115  out_event = ::CreateEvent( NULL, // No security attributes
116  false, // Reset type: automatic
117  false, // Initial signaled state: not signaled
118  NULL // No name
119  );
120 #endif
121  return ( out_event ) ? AK_Success : AK_Fail;
122  }
123 
124  /// Platform Independent Helper
125  inline void AkDestroyEvent( AkEvent & io_event )
126  {
127  if ( io_event )
128  ::CloseHandle( io_event );
129  io_event = NULL;
130  }
131 
132  /// Platform Independent Helper
133  inline void AkWaitForEvent( AkEvent & in_event )
134  {
135 #ifdef AK_USE_UWP_API
136  DWORD dwWaitResult = ::WaitForSingleObjectEx( in_event, INFINITE, FALSE );
137  AKASSERT( dwWaitResult == WAIT_OBJECT_0 );
138 #else
139  AKVERIFY( ::WaitForSingleObject( in_event, INFINITE ) == WAIT_OBJECT_0 );
140 #endif
141  }
142 
143  /// Platform Independent Helper
144  inline void AkSignalEvent( const AkEvent & in_event )
145  {
146  AKVERIFY( ::SetEvent( in_event ) );
147  }
148 
149  /// Platform Independent Helper
150  AkForceInline void AkClearSemaphore(AkSemaphore& io_semaphore)
151  {
152  io_semaphore = NULL;
153  }
154 
155  /// Platform Independent Helper
156  inline AKRESULT AkCreateSemaphore(AkSemaphore& out_semaphore, AkUInt32 in_initialCount)
157  {
158 #ifdef AK_USE_UWP_API
159  out_semaphore = ::CreateSemaphoreEx(
160  NULL, // no security attributes
161  in_initialCount, // initial count
162  INT_MAX, // no maximum -- matches posix semaphore behaviour
163  NULL, // no name
164  0, // reserved
165  STANDARD_RIGHTS_ALL | SEMAPHORE_MODIFY_STATE);
166 #else
167  out_semaphore = ::CreateSemaphore(
168  NULL, // no security attributes
169  in_initialCount, // initial count
170  INT_MAX, // no maximum -- matches posix semaphore behaviour
171  NULL); // no name
172 #endif
173  return (out_semaphore) ? AK_Success : AK_Fail;
174  }
175 
176  /// Platform Independent Helper
177  inline void AkDestroySemaphore(AkSemaphore& io_semaphore)
178  {
179  ::CloseHandle(io_semaphore);
180  }
181 
182  /// Platform Independent Helper - Semaphore wait, aka Operation P. Decrements value of semaphore, and, if the semaphore would be less than 0, waits for the semaphore to be released.
183  inline void AkWaitForSemaphore(AkSemaphore& in_semaphore)
184  {
185  AKVERIFY(::WaitForSingleObject(in_semaphore, INFINITE) == WAIT_OBJECT_0);
186  }
187 
188  /// Platform Independent Helper - Semaphore signal, aka Operation V. Increments value of semaphore by an arbitrary count.
189  inline void AkReleaseSemaphore(AkSemaphore& in_semaphore, AkUInt32 in_count)
190  {
191  AKVERIFY(ReleaseSemaphore(in_semaphore, in_count, NULL) >= 0);
192  }
193 
194  // Virtual Memory
195  // ------------------------------------------------------------------
196 
197 #if defined(AK_WIN)
198 #ifdef AK_WIN_UNIVERSAL_APP
199  AkForceInline void* AllocVM(size_t size, size_t* /*extra*/)
200  {
201  return VirtualAllocFromApp(NULL, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
202  }
203 #else
204  AkForceInline void* AllocVM(size_t size, size_t* /*extra*/)
205  {
206  return VirtualAlloc(NULL, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
207  }
208 
209 #endif
210  AkForceInline void FreeVM(void* address, size_t size, size_t /*extra*/, size_t release)
211  {
212  VirtualFree(address, release ? 0 : size, release ? MEM_RELEASE : MEM_DECOMMIT);
213  }
214 #endif
215 
216  // Threads
217  // ------------------------------------------------------------------
218 
219  /// Platform Independent Helper
220  inline bool AkIsValidThread( AkThread * in_pThread )
221  {
222  return (*in_pThread != AK_NULL_THREAD);
223  }
224 
225  /// Platform Independent Helper
226  inline void AkClearThread( AkThread * in_pThread )
227  {
228  *in_pThread = AK_NULL_THREAD;
229  }
230 
231  /// Platform Independent Helper
232  inline void AkCloseThread( AkThread * in_pThread )
233  {
234  AKASSERT( in_pThread );
235  AKASSERT( *in_pThread );
236  AKVERIFY( ::CloseHandle( *in_pThread ) );
237  AkClearThread( in_pThread );
238  }
239 
240 #define AkExitThread( _result ) return _result;
241 
242  /// Platform Independent Helper
243  inline void AkGetDefaultThreadProperties( AkThreadProperties & out_threadProperties )
244  {
245  out_threadProperties.nPriority = AK_THREAD_PRIORITY_NORMAL;
246  out_threadProperties.uStackSize= AK_DEFAULT_STACK_SIZE;
247 #ifdef AK_WIN_UNIVERSAL_APP
248  out_threadProperties.processorNumber.Group = 0;
249  out_threadProperties.processorNumber.Number = MAXIMUM_PROCESSORS;
250  out_threadProperties.processorNumber.Reserved = 0;
251 #else
252  out_threadProperties.dwAffinityMask = 0;
253 #endif
254  }
255 
256  /// Set the name of a thread: see http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx
257  inline void AkSetThreadName( AkThread in_threadHnd, DWORD in_dwThreadID, LPCSTR in_szThreadName )
258  {
259  const DWORD MS_VC_EXCEPTION=0x406D1388;
260 
261 #pragma pack(push,8)
262  typedef struct tagTHREADNAME_INFO
263  {
264  DWORD dwType;
265  LPCSTR szName;
266  DWORD dwThreadID;
267  DWORD dwFlags;
268  } THREADNAME_INFO;
269 #pragma pack(pop)
270 
271  THREADNAME_INFO info;
272  info.dwType = 0x1000;
273  info.szName = in_szThreadName;
274  info.dwThreadID = in_dwThreadID;
275  info.dwFlags = 0;
276 
277  __try
278  {
279  RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info );
280  }
281 #pragma warning(suppress: 6312 6322)
282  __except(EXCEPTION_CONTINUE_EXECUTION)
283  {
284  }
285 
286 #if defined(AK_XBOX) || defined(_GAMING_DESKTOP) // also applicable on Windows when Win7 support is dropped. SetThreadDescription is a Win10 only API
287  wchar_t wszThreadName[32];
288  AkUInt32 maxStrLen = (sizeof(wszThreadName) / sizeof(wchar_t)) - 1;
289  AkUInt32 nameStrLen = AkMin((int)strlen(in_szThreadName), maxStrLen);
290  MultiByteToWideChar(CP_UTF8, 0, in_szThreadName, nameStrLen, wszThreadName, maxStrLen);
291  wszThreadName[nameStrLen] = '\0';
292  SetThreadDescription(in_threadHnd, wszThreadName);
293 #endif
294  }
295 
296  /// Platform Independent Helper
297  inline void AkCreateThread(
298  AkThreadRoutine pStartRoutine, // Thread routine.
299  void * pParams, // Routine params.
300  const AkThreadProperties & in_threadProperties, // Properties. NULL for default.
301  AkThread * out_pThread, // Returned thread handle.
302  const char * in_szThreadName ) // Opt thread name.
303  {
304  AKASSERT( out_pThread != NULL );
305  AKASSERT( (in_threadProperties.nPriority >= THREAD_PRIORITY_LOWEST && in_threadProperties.nPriority <= THREAD_PRIORITY_HIGHEST)
306  || ( in_threadProperties.nPriority == THREAD_PRIORITY_TIME_CRITICAL )
307  || ( in_threadProperties.nPriority == THREAD_MODE_BACKGROUND_BEGIN ) );
308 
309  DWORD dwThreadID;
310  *out_pThread = ::CreateThread( NULL, // No security attributes
311  in_threadProperties.uStackSize, // StackSize (0 uses system default)
312  pStartRoutine, // Thread start routine
313  pParams, // Thread function parameter
314  CREATE_SUSPENDED, // Creation flags: create suspended so we can set priority/affinity before starting
315  &dwThreadID );
316 
317  // ::CreateThread() return NULL if it fails.
318  if ( !*out_pThread )
319  {
320  AkClearThread( out_pThread );
321  return;
322  }
323 
324  // Set thread name.
325  if (in_szThreadName)
326  {
327  AkSetThreadName(*out_pThread, dwThreadID, in_szThreadName);
328  }
329 
330  // Set properties.
331  if ( !::SetThreadPriority( *out_pThread, in_threadProperties.nPriority ) &&
332  in_threadProperties.nPriority != THREAD_MODE_BACKGROUND_BEGIN )
333  {
334  AKASSERT( !"Failed setting thread priority" );
335  AkCloseThread( out_pThread );
336  AkClearThread(out_pThread);
337  return;
338  }
339 #ifdef AK_WIN_UNIVERSAL_APP
340  if ( in_threadProperties.processorNumber.Number != MAXIMUM_PROCESSORS)
341  {
342  if ( !SetThreadIdealProcessorEx( *out_pThread, const_cast<PPROCESSOR_NUMBER>(&in_threadProperties.processorNumber), NULL) )
343  {
344  AKASSERT( !"Failed setting thread ideal processor" );
345  AkCloseThread( out_pThread );
346  AkClearThread(out_pThread);
347  return;
348  }
349  }
350 #else
351  if (in_threadProperties.dwAffinityMask)
352  {
353  AkUInt32 dwAffinityMask = in_threadProperties.dwAffinityMask;
354  DWORD_PTR procAffinity, sysAffinity;
355  if (::GetProcessAffinityMask(::GetCurrentProcess(), &procAffinity, &sysAffinity))
356  {
357  // To avoid errors in SetThreadAffinityMask, make sure the user-supplied mask is a subset of what is allowed by the process
358  dwAffinityMask &= procAffinity;
359  }
360  if (!::SetThreadAffinityMask(*out_pThread, dwAffinityMask))
361  {
362  AKASSERT(!"Failed setting thread affinity mask");
363  AkCloseThread(out_pThread);
364  AkClearThread(out_pThread);
365  return;
366  }
367  }
368 #endif
369  // Thread is ready, start it up.
370  if (!::ResumeThread(*out_pThread))
371  {
372  AKASSERT(!"Failed to start the thread");
373  AkCloseThread(out_pThread);
374  AkClearThread(out_pThread);
375  return;
376  }
377  }
378 
379  /// Platform Independent Helper
380  inline void AkWaitForSingleThread( AkThread * in_pThread )
381  {
382  AKASSERT( in_pThread );
383  AKASSERT( *in_pThread );
384 #ifdef AK_USE_UWP_API
385  ::WaitForSingleObjectEx( *in_pThread, INFINITE, FALSE );
386 #else
387  ::WaitForSingleObject( *in_pThread, INFINITE );
388 #endif
389  }
390 
391  /// Returns the calling thread's ID.
392  inline AkThreadID CurrentThread()
393  {
394  return ::GetCurrentThreadId();
395  }
396 
397  /// Platform Independent Helper
398  inline void AkSleep( AkUInt32 in_ulMilliseconds )
399  {
400  ::Sleep( in_ulMilliseconds );
401  }
402 
403  // Optimized memory functions
404  // --------------------------------------------------------------------
405 
406  /// Platform Independent Helper
407  inline void AkMemCpy( void * pDest, const void * pSrc, AkUInt32 uSize )
408  {
409  memcpy( pDest, pSrc, uSize );
410  }
411 
412  /// Platform Independent Helper
413  inline void AkMemMove( void* pDest, const void* pSrc, AkUInt32 uSize )
414  {
415  memmove( pDest, pSrc, uSize );
416  }
417 
418  /// Platform Independent Helper
419  inline void AkMemSet( void * pDest, AkInt32 iVal, AkUInt32 uSize )
420  {
421  memset( pDest, iVal, uSize );
422  }
423 
424  // Time functions
425  // ------------------------------------------------------------------
426 
427  /// Platform Independent Helper
428  inline void PerformanceCounter( AkInt64 * out_piLastTime )
429  {
430  ::QueryPerformanceCounter( (LARGE_INTEGER*)out_piLastTime );
431  }
432 
433  /// Platform Independent Helper
434  inline void PerformanceFrequency( AkInt64 * out_piFreq )
435  {
436  ::QueryPerformanceFrequency( (LARGE_INTEGER*)out_piFreq );
437  }
438 
439  /// Platform Independent Helper
440  inline void UpdatePerformanceFrequency()
441  {
442  AkInt64 iFreq;
443  PerformanceFrequency( &iFreq );
444  AK::g_fFreqRatio = (AkReal32)((AkReal64)iFreq / 1000);
445  }
446 
447  /// Returns a time range in milliseconds, using the sound engine's updated count->milliseconds ratio.
448  inline AkReal32 Elapsed( const AkInt64 & in_iNow, const AkInt64 & in_iStart )
449  {
450  return ( in_iNow - in_iStart ) / AK::g_fFreqRatio;
451  }
452 
453 #if defined(AK_XBOXSERIESX)
454  // Waits for a limited amount of time for in_pVal to hit zero (without yielding the thread)
455  inline void AkLimitedSpinForZero(AkAtomic32* in_pVal)
456  {
457  // monitorx and waitx are available on certain AMD CPUs, so we can have a custom impl of AkLimitedSpinForZero
458  AkInt64 endSpinTime = 0;
459  AkInt64 currentTime = 0;
460  PerformanceCounter(&endSpinTime);
461  endSpinTime += AkInt64(AK::g_fFreqRatio * 0.01); // only spin for about 10us
462  while (true)
463  {
464  // set up monitorx on pVal
465  _mm_monitorx((void*)in_pVal, 0U, 0U);
466  // if pval is zero, skip out
467  if (AkAtomicLoad32(in_pVal) == 0)
468  {
469  break;
470  }
471  // wait until a store to pVal occurs (or ~1us passes)
472  _mm_mwaitx(2U, 0U, 1000U);
473 
474  // Check if we've hit the deadline for the timeout
475  PerformanceCounter(&currentTime);
476  if (currentTime > endSpinTime)
477  {
478  break;
479  }
480  }
481  }
482 
483  // Waits for a limited amount of time for in_pVal to get atomically shift from the expected value to the proposed one
484  // returns true if acquisition succeeded
485  inline bool AkLimitedSpinToAcquire(AkAtomic32* in_pVal, AkInt32 in_proposed, AkInt32 in_expected)
486  {
487  if (AkAtomicCas32(in_pVal, in_proposed, in_expected))
488  {
489  return true;
490  }
491 
492  // Cas failed, start the slower evaluation
493 
494  // monitorx and waitx are available on certain AMD CPUs, so we can use monitorx/waitx
495  AkInt64 endSpinTime = 0;
496  AkInt64 currentTime = 0;
497  PerformanceCounter(&endSpinTime);
498  endSpinTime += AkInt64(AK::g_fFreqRatio * 0.01); // only spin for about 10us
499  while (true)
500  {
501  // set up monitorx on pVal
502  _mm_monitorx((void*)in_pVal, 0U, 0U);
503  // attempt cas to acquire and if successful, skip out
504  if (AkAtomicCas32(in_pVal, in_proposed, in_expected))
505  {
506  return true;
507  }
508  // wait until a store to pVal occurs (or ~1us passes)
509  _mm_mwaitx(2U, 0U, 1000U);
510 
511  // Check if we've hit the deadline for the timeout
512  PerformanceCounter(&currentTime);
513  if (currentTime > endSpinTime)
514  {
515  return false;
516  }
517  }
518  }
519 
520 #define AK_LIMITEDSPINFORZERO // mark AkLimitedSpinForZero as defined to avoid duplicate definitions
521 #endif
522 
523  /// String conversion helper. If io_pszAnsiString is null, the function returns the required size.
524  inline AkInt32 AkWideCharToChar( const wchar_t* in_pszUnicodeString,
525  AkUInt32 in_uiOutBufferSize,
526  char* io_pszAnsiString )
527  {
528  if(!io_pszAnsiString)
529  return WideCharToMultiByte(CP_UTF8, 0, in_pszUnicodeString, -1, NULL, 0, NULL, NULL);
530 
531  int iWritten = ::WideCharToMultiByte(CP_UTF8, // code page
532  0, // performance and mapping flags
533  in_pszUnicodeString, // wide-character string
534  (int)AkMin( ( (AkUInt32)wcslen( in_pszUnicodeString )), in_uiOutBufferSize-1 ), // number of chars in string : -1 = NULL terminated string.
535  io_pszAnsiString, // buffer for new string
536  in_uiOutBufferSize, // size of buffer
537  NULL, // default for unmappable chars
538  NULL); // set when default char used
539  io_pszAnsiString[iWritten] = 0;
540  return iWritten;
541  }
542 
543  /// String conversion helper
544  inline AkInt32 AkCharToWideChar( const char* in_pszAnsiString,
545  AkUInt32 in_uiOutBufferSize,
546  void* io_pvUnicodeStringBuffer )
547  {
548  return ::MultiByteToWideChar( CP_UTF8, // code page
549  0, // performance and mapping flags
550  in_pszAnsiString, // wide-character string
551  -1, // number of chars in string : -1 = NULL terminated string.
552  (wchar_t*)io_pvUnicodeStringBuffer, // buffer for new string
553  in_uiOutBufferSize); // size of buffer
554  }
555 
556  /// String conversion helper
557  inline AkInt32 AkUtf8ToWideChar( const char* in_pszUtf8String,
558  AkUInt32 in_uiOutBufferSize,
559  void* io_pvUnicodeStringBuffer )
560  {
561  return ::MultiByteToWideChar( CP_UTF8, // code page
562  0, // performance and mapping flags
563  in_pszUtf8String, // wide-character string
564  -1, // number of chars in string : -1 = NULL terminated string.
565  (wchar_t*)io_pvUnicodeStringBuffer, // buffer for new string
566  in_uiOutBufferSize); // size of buffer
567  }
568 
569  /// Safe unicode string copy.
570  inline void SafeStrCpy( wchar_t * in_pDest, const wchar_t* in_pSrc, size_t in_uDestMaxNumChars )
571  {
572  size_t iSizeCopy = AkMin( in_uDestMaxNumChars - 1, wcslen( in_pSrc ) + 1 );
573  wcsncpy_s( in_pDest, in_uDestMaxNumChars, in_pSrc, iSizeCopy );
574  in_pDest[iSizeCopy] = '\0';
575  }
576 
577  /// Safe string copy.
578  inline void SafeStrCpy( char * in_pDest, const char* in_pSrc, size_t in_uDestMaxNumChars )
579  {
580  size_t iSizeCopy = AkMin( in_uDestMaxNumChars - 1, strlen( in_pSrc ) + 1 );
581  strncpy_s( in_pDest, in_uDestMaxNumChars, in_pSrc, iSizeCopy );
582  in_pDest[iSizeCopy] = '\0';
583  }
584 
585  /// Safe unicode string concatenation.
586  inline void SafeStrCat( wchar_t * in_pDest, const wchar_t* in_pSrc, size_t in_uDestMaxNumChars )
587  {
588  int iAvailableSize = (int)( in_uDestMaxNumChars - wcslen( in_pDest ) - 1 );
589  wcsncat_s( in_pDest, in_uDestMaxNumChars, in_pSrc, AkMin( iAvailableSize, (int)wcslen( in_pSrc ) ) );
590  }
591 
592  /// Safe string concatenation.
593  inline void SafeStrCat( char * in_pDest, const char* in_pSrc, size_t in_uDestMaxNumChars )
594  {
595  int iAvailableSize = (int)( in_uDestMaxNumChars - strlen( in_pDest ) - 1 );
596  strncat_s( in_pDest, in_uDestMaxNumChars, in_pSrc, AkMin( iAvailableSize, (int)strlen( in_pSrc ) ) );
597  }
598 
599  inline int SafeStrFormat(wchar_t * in_pDest, size_t in_uDestMaxNumChars, const wchar_t* in_pszFmt, ...)
600  {
601  va_list args;
602  va_start(args, in_pszFmt);
603  int r = vswprintf(in_pDest, in_uDestMaxNumChars, in_pszFmt, args);
604  va_end(args);
605  return r;
606  }
607 
608  inline int SafeStrFormat(char * in_pDest, size_t in_uDestMaxNumChars, const char* in_pszFmt, ...)
609  {
610  va_list args;
611  va_start(args, in_pszFmt);
612  int r = vsnprintf(in_pDest, in_uDestMaxNumChars, in_pszFmt, args);
613  va_end(args);
614  return r;
615  }
616 
617  /// Stack allocations.
618  #define AkAlloca( _size_ ) _alloca( _size_ )
619 
620  /// Output a debug message on the console
621 #if ! defined(AK_OPTIMIZED)
622  inline void OutputDebugMsg( const wchar_t* in_pszMsg )
623  {
624  OutputDebugStringW( in_pszMsg );
625  }
626 
627  /// Output a debug message on the console
628  inline void OutputDebugMsg( const char* in_pszMsg )
629  {
630  OutputDebugStringA( in_pszMsg );
631  }
632 
633  /// Output a debug message on the console (variadic function).
634  /// Use MaxSize to specify the size to reserve for the message.
635  /// Warning: On Win32, OutputDebugMsgV with wchar_t will truncate the string
636  /// to MaxSize.
637  template <int MaxSize = 256>
638  inline void OutputDebugMsgV(const wchar_t* in_pszFmt, ...)
639  {
640  // No equivalent of snprintf for wide string so truncate if necessary
641  // vswprintf returns the number of characters written and not the numbers
642  // that would be written
643 
644  wchar_t* msg = (wchar_t*)AkAlloca(MaxSize * sizeof(wchar_t));
645  msg[MaxSize - 1] = '\0';
646 
647  va_list args;
648  va_start(args, in_pszFmt);
649  vswprintf(msg, MaxSize, in_pszFmt, args);
650  va_end(args);
651 
652  OutputDebugMsg(msg);
653  }
654 
655  /// Output a debug message on the console (variadic function).
656  /// Use MaxSize to specify the expected size of the message.
657  /// If the required size is superior to MaxSize, a new string
658  /// will be allocated on the stack with the required size.
659  template <int MaxSize = 256>
660  inline void OutputDebugMsgV(const char* in_pszFmt, ...)
661  {
662  int size = 0;
663  {
664  // Try with a reasonable guess first
665  char msg[MaxSize];
666  msg[MaxSize - 1] = '\0';
667 
668  va_list args;
669  va_start(args, in_pszFmt);
670  size = vsnprintf(msg, MaxSize, in_pszFmt, args);
671  va_end(args);
672 
673  // If it was enough, print to debug log
674  if (0 <= size && size <= MaxSize)
675  {
676  OutputDebugMsg(msg);
677  return;
678  }
679  }
680 
681  // Else, we need more memory to prevent truncation
682  {
683  // size + 1 more char for the last '\0'
684  size++;
685 
686  char* msg = (char*)AkAlloca((size) * sizeof(char));
687  msg[size - 1] = '\0';
688 
689  va_list args;
690  va_start(args, in_pszFmt);
691  vsnprintf(msg, size, in_pszFmt, args);
692  va_end(args);
693 
694  OutputDebugMsg(msg);
695  }
696  }
697 
698 #else
699  inline void OutputDebugMsg(const wchar_t*){}
700  inline void OutputDebugMsg(const char*){}
701 
702  template <int MaxSize = 0>
703  inline void OutputDebugMsgV(const wchar_t*, ...) {}
704 
705  template <int MaxSize = 0>
706  inline void OutputDebugMsgV(const char*, ...) {}
707 #endif
708 
709  /// Converts a wchar_t string to an AkOSChar string.
710  /// \remark On some platforms the AkOSChar string simply points to the same string,
711  /// on others a new buffer is allocated on the stack using AkAlloca. This means
712  /// you must make sure that:
713  /// - The source string stays valid and unmodified for as long as you need the
714  /// AkOSChar string (for cases where they point to the same string)
715  /// - The AkOSChar string is used within this scope only -- for example, do NOT
716  /// return that string from a function (for cases where it is allocated on the stack)
717  #define CONVERT_WIDE_TO_OSCHAR( _wstring_, _oscharstring_ ) ( _oscharstring_ ) = (AkOSChar*)( _wstring_ )
718 
719  /// Converts a char string to an AkOSChar string.
720  /// \remark On some platforms the AkOSChar string simply points to the same string,
721  /// on others a new buffer is allocated on the stack using AkAlloca. This means
722  /// you must make sure that:
723  /// - The source string stays valid and unmodified for as long as you need the
724  /// AkOSChar string (for cases where they point to the same string)
725  /// - The AkOSChar string is used within this scope only -- for example, do NOT
726  /// return that string from a function (for cases where it is allocated on the stack)
727  #define CONVERT_CHAR_TO_OSCHAR( _astring_, _oscharstring_ ) \
728  _oscharstring_ = (AkOSChar*)AkAlloca( (1 + strlen( _astring_ )) * sizeof(AkOSChar)); \
729  AKPLATFORM::AkCharToWideChar( _astring_, (AkUInt32)(1 + strlen(_astring_ )), (AkOSChar*)( _oscharstring_ ) )
730 
731  /// Converts a AkOSChar string into wide char string.
732  /// \remark On some platforms the AkOSChar string simply points to the same string,
733  /// on others a new buffer is allocated on the stack using AkAlloca. This means
734  /// you must make sure that:
735  /// - The source string stays valid and unmodified for as long as you need the
736  /// AkOSChar string (for cases where they point to the same string)
737  /// - The AkOSChar string is used within this scope only -- for example, do NOT
738  /// return that string from a function (for cases where it is allocated on the stack)
739  #define CONVERT_OSCHAR_TO_WIDE( _osstring_, _wstring_ ) _wstring_ = _osstring_
740 
741  /// Converts a AkOSChar string into char string.
742  /// \remark On some platforms the AkOSChar string simply points to the same string,
743  /// on others a new buffer is allocated on the stack using AkAlloca. This means
744  /// you must make sure that:
745  /// - The source string stays valid and unmodified for as long as you need the
746  /// AkOSChar string (for cases where they point to the same string)
747  /// - The AkOSChar string is used within this scope only -- for example, do NOT
748  /// return that string from a function (for cases where it is allocated on the stack)
749  #define CONVERT_OSCHAR_TO_CHAR( _osstring_, _astring_ ) \
750  _astring_ = (char*)AkAlloca( 1 + AKPLATFORM::AkWideCharToChar( _osstring_, 0, NULL )); \
751  AKPLATFORM::AkWideCharToChar( _osstring_, AkUInt32(1 + AKPLATFORM::AkWideCharToChar( _osstring_, 0, NULL )), _astring_ );
752 
753  /// Get the length, in characters, of a NULL-terminated AkUtf16 string
754  /// \return The length, in characters, of the specified string (excluding terminating NULL)
755  inline size_t AkUtf16StrLen( const AkUtf16* in_pStr )
756  {
757  return ( wcslen( in_pStr ) );
758  }
759 
760  /// Get the length, in characters, of a NULL-terminated AkOSChar string
761  /// \return The length, in characters, of the specified string (excluding terminating NULL)
762  inline size_t OsStrLen( const AkOSChar* in_pszString )
763  {
764  return ( wcslen( in_pszString ) );
765  }
766 
767  /// AkOSChar version of sprintf().
768  #define AK_OSPRINTF swprintf_s
769 
770  /// Compare two NULL-terminated AkOSChar strings
771  /// \return
772  /// - < 0 if in_pszString1 < in_pszString2
773  /// - 0 if the two strings are identical
774  /// - > 0 if in_pszString1 > in_pszString2
775  /// \remark The comparison is case-sensitive
776  inline int OsStrCmp( const AkOSChar* in_pszString1, const AkOSChar* in_pszString2 )
777  {
778  return ( wcscmp( in_pszString1, in_pszString2 ) );
779  }
780 
781  /// Compare two NULL-terminated AkOSChar strings up to the specified count of characters.
782  /// \return
783  /// - < 0 if in_pszString1 < in_pszString2
784  /// - 0 if the two strings are identical
785  /// - > 0 if in_pszString1 > in_pszString2
786  /// \remark The comparison is case-sensitive
787  inline int OsStrNCmp( const AkOSChar* in_pszString1, const AkOSChar* in_pszString2, size_t in_MaxCountSize)
788  {
789  return wcsncmp(in_pszString1, in_pszString2, in_MaxCountSize);
790  }
791 
792  #define AK_UTF16_TO_WCHAR( in_pdDest, in_pSrc, in_MaxSize ) AKPLATFORM::SafeStrCpy( in_pdDest, in_pSrc, in_MaxSize )
793  #define AK_WCHAR_TO_UTF16( in_pdDest, in_pSrc, in_MaxSize ) AKPLATFORM::SafeStrCpy( in_pdDest, in_pSrc, in_MaxSize )
794  #define AK_UTF8_TO_OSCHAR( in_pdDest, in_pSrc, in_MaxSize ) AKPLATFORM::AkCharToWideChar( in_pSrc, in_MaxSize, in_pdDest )
795  #define AK_UTF16_TO_OSCHAR( in_pdDest, in_pSrc, in_MaxSize ) AKPLATFORM::SafeStrCpy( in_pdDest, in_pSrc, in_MaxSize )
796  #define AK_UTF16_TO_CHAR( in_pdDest, in_pSrc, in_MaxSize ) AKPLATFORM::AkWideCharToChar( in_pSrc, in_MaxSize, in_pdDest )
797  #define AK_CHAR_TO_UTF16( in_pdDest, in_pSrc, in_MaxSize ) AKPLATFORM::AkCharToWideChar( in_pSrc, in_MaxSize, in_pdDest )
798  #define AK_OSCHAR_TO_UTF16( in_pdDest, in_pSrc, in_MaxSize ) AKPLATFORM::SafeStrCpy( in_pdDest, in_pSrc, in_MaxSize )
799 
800  /// Detects whether the string represents an absolute path to a file
801  inline bool IsAbsolutePath(const AkOSChar* in_pszPath, size_t in_pathLen)
802  {
803  return
804  (in_pathLen >= 3 && in_pszPath[1] == ':' && in_pszPath[2] == '\\') || // Classic "C:\..." DOS-style path
805  (in_pathLen >= 2 && in_pszPath[0] == '\\'); // Uncommon "\..." absolute path from current drive, or UNC "\\..."
806  }
807 
808  // Use with AkOSChar.
809  #define AK_PATH_SEPARATOR (L"\\")
810  #define AK_LIBRARY_PREFIX (L"")
811  #define AK_DYNAMIC_LIBRARY_EXTENSION (L".dll")
812 
813  #define AK_FILEHANDLE_TO_UINTPTR(_h) ((AkUIntPtr)_h)
814  #define AK_SET_FILEHANDLE_TO_UINTPTR(_h,_u) _h = (AkFileHandle)_u
815 
816  #if defined(AK_ENABLE_PERF_RECORDING)
817 
818  static AkUInt32 g_uAkPerfRecExecCount = 0;
819  static AkReal32 g_fAkPerfRecExecTime = 0.f;
820 
821  #define AK_PERF_RECORDING_RESET() \
822  AKPLATFORM::g_uAkPerfRecExecCount = 0;\
823  AKPLATFORM::g_fAkPerfRecExecTime = 0.f;
824 
825  #define AK_PERF_RECORDING_START( __StorageName__, __uExecutionCountStart__, __uExecutionCountStop__ ) \
826  AkInt64 iAkPerfRecTimeBefore; \
827  if ( (AKPLATFORM::g_uAkPerfRecExecCount >= (__uExecutionCountStart__)) && (AKPLATFORM::g_uAkPerfRecExecCount <= (__uExecutionCountStop__)) ) \
828  AKPLATFORM::PerformanceCounter( &iAkPerfRecTimeBefore );
829 
830  #define AK_PERF_RECORDING_STOP( __StorageName__, __uExecutionCountStart__, __uExecutionCountStop__ ) \
831  if ( (AKPLATFORM::g_uAkPerfRecExecCount >= (__uExecutionCountStart__)) && (AKPLATFORM::g_uAkPerfRecExecCount <= (__uExecutionCountStop__)) ) \
832  { \
833  AkInt64 iAkPerfRecTimeAfter; \
834  AKPLATFORM::PerformanceCounter( &iAkPerfRecTimeAfter ); \
835  AKPLATFORM::g_fAkPerfRecExecTime += AKPLATFORM::Elapsed( iAkPerfRecTimeAfter, iAkPerfRecTimeBefore ); \
836  if ( AKPLATFORM::g_uAkPerfRecExecCount == (__uExecutionCountStop__) ) \
837  { \
838  AkReal32 fAverageExecutionTime = AKPLATFORM::g_fAkPerfRecExecTime/((__uExecutionCountStop__)-(__uExecutionCountStart__)); \
839  AkOSChar str[256]; \
840  swprintf_s(str, 256, AKTEXT("%s average execution time: %f\n"), AKTEXT(__StorageName__), fAverageExecutionTime);\
841  AKPLATFORM::OutputDebugMsg( str ); \
842  } \
843  } \
844  AKPLATFORM::g_uAkPerfRecExecCount++;
845  #endif // AK_ENABLE_PERF_RECORDING
846 
847 #if (defined(AK_CPU_X86_64) || defined(AK_CPU_X86))
848  /// Support to fetch the CPUID for the platform. Only valid for X86 targets
849  /// \remark Note that IAkProcessorFeatures should be preferred to fetch this data
850  /// as it will have already translated the feature bits into AK-relevant enums
851  inline void CPUID(AkUInt32 in_uLeafOpcode, AkUInt32 in_uSubLeafOpcode, unsigned int out_uCPUFeatures[4])
852  {
853  __cpuidex((int*)out_uCPUFeatures, in_uLeafOpcode, in_uSubLeafOpcode);
854  }
855 #endif
856 }
857 
858 #endif // _AK_PLATFORM_FUNCS_H_
AKRESULT AkCreateSemaphore(AkSemaphore &out_semaphore, AkUInt32 in_initialCount)
Platform Independent Helper.
size_t AkUtf16StrLen(const AkUtf16 *in_pStr)
Audiokinetic namespace.
@ AK_Fail
The operation failed.
Definition: AkTypes.h:202
#define AK_THREAD_PRIORITY_NORMAL
semaphore_t AkEvent
Definition: AkTypes.h:84
#define AK_DEFAULT_STACK_SIZE
AkForceInline void FreeVM(void *address, size_t size, size_t extra, size_t release)
int nPriority
Thread priority.
void AkClearEvent(AkEvent &out_event)
Platform Independent Helper.
AkForceInline void AkClearSemaphore(AkSemaphore &io_semaphore)
Platform Independent Helper.
Platform-dependent helpers.
void AkWaitForEvent(AkEvent &in_event)
Platform Independent Helper.
AkForceInline void UpdatePerformanceFrequency()
Platform Independent Helper.
int AkThreadID
Definition: AkTypes.h:69
AKRESULT
Standard function call result.
Definition: AkTypes.h:199
void OutputDebugMsg(const char *in_pszMsg)
Output a debug message on the console (Ansi string)
AkForceInline void * AllocVM(size_t size, size_t *extra)
__forceinline int AkAtomicCas32(AkAtomic32 *pDest, long proposed, long expected)
Definition: AkAtomic.h:67
void CPUID(AkUInt32 in_uLeafOpcode, AkUInt32 in_uSubLeafOpcode, unsigned int out_uCPUFeatures[4])
char AkOSChar
Generic character string.
Definition: AkTypes.h:60
int SafeStrFormat(wchar_t *in_pDest, size_t in_uDestMaxNumChars, const wchar_t *in_pszFmt,...)
#define NULL
Definition: AkTypes.h:46
AkThreadID CurrentThread()
Returns the calling thread's ID.
bool IsAbsolutePath(const AkOSChar *in_pszPath, size_t in_pathLen)
Detects whether the string represents an absolute path to a file.
float AkReal32
32-bit floating point
@ AK_Success
The operation was successful.
Definition: AkTypes.h:201
int32_t AkInt32
Signed 32-bit integer.
void AkCreateThread(AkThreadRoutine pStartRoutine, void *pParams, const AkThreadProperties &in_threadProperties, AkThread *out_pThread, const char *)
Platform Independent Helper.
AkUInt16 AkUtf16
Definition: AkTypes.h:61
void OutputDebugMsgV(const char *in_pszFmt,...)
Output a debug message on the console (variadic function).
void PerformanceCounter(AkInt64 *out_piLastTime)
Platform Independent Helper.
AkUInt32 uStackSize
Thread stack size.
void AkSetThreadName(AkThread in_threadHnd, DWORD in_dwThreadID, LPCSTR in_szThreadName)
Set the name of a thread: see http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx.
void AkDestroySemaphore(AkSemaphore &io_semaphore)
Platform Independent Helper.
void AkDestroyEvent(AkEvent &io_event)
Platform Independent Helper.
nn::os::ThreadFunction AkThreadRoutine
Thread routine.
Definition: AkTypes.h:87
AkForceInline bool AkIsValidThread(AkThread *in_pThread)
Platform Independent Helper.
AkForceInline void AkMemCpy(void *pDest, const void *pSrc, AkUInt32 uSize)
Platform Independent Helper.
#define AKASSERT(Condition)
Definition: AkAssert.h:67
#define AKVERIFY(x)
Definition: AkAssert.h:69
AkForceInline void AkSleep(AkUInt32 in_ulMilliseconds)
Platform Independent Helper.
AkForceInline void AkClearThread(AkThread *in_pThread)
Platform Independent Helper.
AkForceInline void AkGetDefaultThreadProperties(AkThreadProperties &out_threadProperties)
Platform Independent Helper.
#define AK_NULL_THREAD
int OsStrNCmp(const AkOSChar *in_pszString1, const AkOSChar *in_pszString2, size_t in_MaxCountSize)
void AkLimitedSpinForZero(AkAtomic32 *in_pVal)
bool AkLimitedSpinToAcquire(AkAtomic32 *in_pVal, AkInt32 in_proposed, AkInt32 in_expected)
AkForceInline void AkMemSet(void *pDest, AkInt32 iVal, AkUInt32 uSize)
Platform Independent Helper.
void AkMemMove(void *pDest, const void *pSrc, AkUInt32 uSize)
Platform Independent Helper.
SceKernelCpumask dwAffinityMask
Affinity mask.
AkReal32 g_fFreqRatio
double AkReal64
64-bit floating point
#define AkMin(x1, x2)
AKRESULT AkCreateEvent(AkEvent &out_event)
Platform Independent Helper.
AkForceInline void SafeStrCpy(wchar_t *in_pDest, const wchar_t *in_pSrc, size_t in_uDestMaxNumChars)
Safe unicode string copy.
int64_t AkInt64
Signed 64-bit integer.
AkUInt32 dwAffinityMask
Affinity mask.
volatile long AkAtomic32
Definition: AkAtomic.h:42
void AkWaitForSemaphore(AkSemaphore &in_semaphore)
Platform Independent Helper - Semaphore wait, aka Operation P. Decrements value of semaphore,...
size_t uStackSize
Thread stack size.
void PerformanceFrequency(AkInt64 *out_piFreq)
Platform Independent Helper.
AkForceInline AkInt32 AkCharToWideChar(const char *in_pszAnsiString, AkUInt32 in_uiOutBufferSize, void *io_pvUnicodeStringBuffer)
String conversion helper.
uint32_t AkUInt32
Unsigned 32-bit integer.
void AkReleaseSemaphore(AkSemaphore &in_semaphore, AkUInt32 in_count)
Platform Independent Helper - Semaphore signal, aka Operation V. Increments value of semaphore by an ...
AkForceInline void SafeStrCat(wchar_t *in_pDest, const wchar_t *in_pSrc, size_t in_uDestMaxNumChars)
Safe unicode string concatenation.
AkForceInline AkInt32 AkWideCharToChar(const wchar_t *in_pszUnicodeString, AkUInt32 in_uiOutBufferSize, char *io_pszAnsiString)
String conversion helper.
AkForceInline void AkWaitForSingleThread(AkThread *in_pThread)
Platform Independent Helper.
#define AkAlloca(_size_)
Stack allocations.
void AkSignalEvent(const AkEvent &in_event)
Platform Independent Helper.
AkForceInline AkInt32 AkUtf8ToWideChar(const char *in_pszUtf8String, AkUInt32 in_uiOutBufferSize, void *io_pvUnicodeStringBuffer)
String conversion helper.
semaphore_t AkSemaphore
Definition: AkTypes.h:85
AkForceInline void AkCloseThread(AkThread *in_pThread)
Platform Independent Helper.
AkForceInline int OsStrCmp(const AkOSChar *in_pszString1, const AkOSChar *in_pszString2)
#define AkForceInline
Definition: AkTypes.h:63
__forceinline long AkAtomicLoad32(AkAtomic32 *pSrc)
Definition: AkAtomic.h:57
AkForceInline size_t OsStrLen(const AkOSChar *in_pszString)
AkForceInline AkReal32 Elapsed(const AkInt64 &in_iNow, const AkInt64 &in_iStart)
Returns a time range in milliseconds, using the sound engine's updated count->milliseconds ratio.

Was this page helpful?

Need Support?

Questions? Problems? Need more info? Contact us, and we can help!

Visit our Support page

Tell us about your project. We're here to help.

Register your project and we'll help you get started with no strings attached!

Get started with Wwise