mirror of
https://github.com/thunderbrewhq/thunderbrew
synced 2025-12-16 12:42:30 +00:00
feat(gx): add incomplete 'CGxDeviceGLSDL' (#2)
* chore(build): add vendored SDL 3.0.0 library * chore(build): add vendored glew-cmake-2.2.0 library * feat(console): in the presence of -opengl launch flag, change GxApi to OpenGl * feat(gx): add uncompleted CGxDeviceGLSDL targeting Windows and Linux * chore(build): change SDL3 linkage from shared (bad) to to static (good)
This commit is contained in:
parent
934e0fb600
commit
706c8903a1
2043 changed files with 663533 additions and 5 deletions
2131
vendor/sdl-3.0.0/src/audio/SDL_audio.c
vendored
Normal file
2131
vendor/sdl-3.0.0/src/audio/SDL_audio.c
vendored
Normal file
File diff suppressed because it is too large
Load diff
27
vendor/sdl-3.0.0/src/audio/SDL_audio_c.h
vendored
Normal file
27
vendor/sdl-3.0.0/src/audio/SDL_audio_c.h
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
#ifndef SDL_audio_c_h_
|
||||
#define SDL_audio_c_h_
|
||||
|
||||
extern void SDL_UpdateAudio(void);
|
||||
|
||||
#endif /* SDL_audio_c_h_ */
|
||||
1068
vendor/sdl-3.0.0/src/audio/SDL_audio_channel_converters.h
vendored
Normal file
1068
vendor/sdl-3.0.0/src/audio/SDL_audio_channel_converters.h
vendored
Normal file
File diff suppressed because it is too large
Load diff
544
vendor/sdl-3.0.0/src/audio/SDL_audio_resampler_filter.h
vendored
Normal file
544
vendor/sdl-3.0.0/src/audio/SDL_audio_resampler_filter.h
vendored
Normal file
|
|
@ -0,0 +1,544 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
// DO NOT EDIT, THIS FILE WAS GENERATED BY build-scripts/gen_audio_resampler_filter.c
|
||||
|
||||
#define RESAMPLER_ZERO_CROSSINGS 5
|
||||
#define RESAMPLER_BITS_PER_SAMPLE 16
|
||||
#define RESAMPLER_BITS_PER_ZERO_CROSSING ((RESAMPLER_BITS_PER_SAMPLE / 2) + 1)
|
||||
#define RESAMPLER_SAMPLES_PER_ZERO_CROSSING (1 << RESAMPLER_BITS_PER_ZERO_CROSSING)
|
||||
#define RESAMPLER_FILTER_SIZE (RESAMPLER_SAMPLES_PER_ZERO_CROSSING * RESAMPLER_ZERO_CROSSINGS)
|
||||
|
||||
static const float ResamplerFilter[RESAMPLER_FILTER_SIZE] = {
|
||||
1.000000000f, 0.000000000f,-0.000000000f, 0.000000000f,-0.000000000f,
|
||||
0.999993165f,-0.001679888f, 0.000529080f,-0.000151513f, 0.000027455f,
|
||||
0.999972661f,-0.003351212f, 0.001055794f,-0.000302183f, 0.000054683f,
|
||||
0.999938488f,-0.005013955f, 0.001580128f,-0.000452009f, 0.000081685f,
|
||||
0.999890647f,-0.006668099f, 0.002102071f,-0.000600987f, 0.000108459f,
|
||||
0.999829139f,-0.008313629f, 0.002621611f,-0.000749115f, 0.000135007f,
|
||||
0.999753966f,-0.009950528f, 0.003138734f,-0.000896389f, 0.000161328f,
|
||||
0.999665131f,-0.011578779f, 0.003653429f,-0.001042807f, 0.000187423f,
|
||||
0.999562634f,-0.013198368f, 0.004165684f,-0.001188367f, 0.000213291f,
|
||||
0.999446480f,-0.014809279f, 0.004675488f,-0.001333066f, 0.000238933f,
|
||||
0.999316672f,-0.016411497f, 0.005182828f,-0.001476901f, 0.000264348f,
|
||||
0.999173212f,-0.018005007f, 0.005687694f,-0.001619871f, 0.000289537f,
|
||||
0.999016105f,-0.019589795f, 0.006190074f,-0.001761971f, 0.000314501f,
|
||||
0.998845356f,-0.021165846f, 0.006689957f,-0.001903201f, 0.000339239f,
|
||||
0.998660968f,-0.022733147f, 0.007187332f,-0.002043558f, 0.000363751f,
|
||||
0.998462946f,-0.024291684f, 0.007682189f,-0.002183039f, 0.000388037f,
|
||||
0.998251297f,-0.025841443f, 0.008174516f,-0.002321643f, 0.000412099f,
|
||||
0.998026026f,-0.027382413f, 0.008664303f,-0.002459367f, 0.000435935f,
|
||||
0.997787138f,-0.028914579f, 0.009151540f,-0.002596209f, 0.000459547f,
|
||||
0.997534641f,-0.030437930f, 0.009636217f,-0.002732167f, 0.000482934f,
|
||||
0.997268542f,-0.031952453f, 0.010118324f,-0.002867240f, 0.000506097f,
|
||||
0.996988847f,-0.033458137f, 0.010597850f,-0.003001425f, 0.000529036f,
|
||||
0.996695563f,-0.034954970f, 0.011074786f,-0.003134721f, 0.000551752f,
|
||||
0.996388700f,-0.036442941f, 0.011549123f,-0.003267125f, 0.000574244f,
|
||||
0.996068266f,-0.037922039f, 0.012020851f,-0.003398637f, 0.000596512f,
|
||||
0.995734268f,-0.039392253f, 0.012489961f,-0.003529253f, 0.000618558f,
|
||||
0.995386717f,-0.040853574f, 0.012956443f,-0.003658973f, 0.000640382f,
|
||||
0.995025621f,-0.042305990f, 0.013420290f,-0.003787796f, 0.000661984f,
|
||||
0.994650990f,-0.043749493f, 0.013881491f,-0.003915718f, 0.000683363f,
|
||||
0.994262835f,-0.045184072f, 0.014340039f,-0.004042740f, 0.000704522f,
|
||||
0.993861166f,-0.046609719f, 0.014795924f,-0.004168860f, 0.000725459f,
|
||||
0.993445994f,-0.048026424f, 0.015249139f,-0.004294075f, 0.000746176f,
|
||||
0.993017331f,-0.049434180f, 0.015699676f,-0.004418386f, 0.000766672f,
|
||||
0.992575187f,-0.050832978f, 0.016147525f,-0.004541790f, 0.000786949f,
|
||||
0.992119574f,-0.052222809f, 0.016592680f,-0.004664287f, 0.000807006f,
|
||||
0.991650506f,-0.053603666f, 0.017035133f,-0.004785875f, 0.000826844f,
|
||||
0.991167995f,-0.054975543f, 0.017474875f,-0.004906553f, 0.000846464f,
|
||||
0.990672054f,-0.056338431f, 0.017911900f,-0.005026320f, 0.000865865f,
|
||||
0.990162696f,-0.057692323f, 0.018346201f,-0.005145175f, 0.000885049f,
|
||||
0.989639935f,-0.059037214f, 0.018777770f,-0.005263117f, 0.000904016f,
|
||||
0.989103786f,-0.060373097f, 0.019206599f,-0.005380146f, 0.000922766f,
|
||||
0.988554262f,-0.061699966f, 0.019632684f,-0.005496260f, 0.000941300f,
|
||||
0.987991380f,-0.063017815f, 0.020056015f,-0.005611458f, 0.000959619f,
|
||||
0.987415153f,-0.064326639f, 0.020476588f,-0.005725741f, 0.000977722f,
|
||||
0.986825598f,-0.065626433f, 0.020894396f,-0.005839106f, 0.000995611f,
|
||||
0.986222730f,-0.066917192f, 0.021309432f,-0.005951554f, 0.001013285f,
|
||||
0.985606567f,-0.068198912f, 0.021721690f,-0.006063084f, 0.001030746f,
|
||||
0.984977124f,-0.069471588f, 0.022131165f,-0.006173695f, 0.001047994f,
|
||||
0.984334418f,-0.070735217f, 0.022537850f,-0.006283387f, 0.001065030f,
|
||||
0.983678468f,-0.071989794f, 0.022941741f,-0.006392159f, 0.001081853f,
|
||||
0.983009290f,-0.073235317f, 0.023342830f,-0.006500011f, 0.001098466f,
|
||||
0.982326903f,-0.074471782f, 0.023741114f,-0.006606943f, 0.001114868f,
|
||||
0.981631326f,-0.075699186f, 0.024136587f,-0.006712954f, 0.001131059f,
|
||||
0.980922577f,-0.076917527f, 0.024529243f,-0.006818044f, 0.001147042f,
|
||||
0.980200675f,-0.078126804f, 0.024919078f,-0.006922213f, 0.001162815f,
|
||||
0.979465640f,-0.079327013f, 0.025306087f,-0.007025460f, 0.001178380f,
|
||||
0.978717491f,-0.080518153f, 0.025690266f,-0.007127786f, 0.001193738f,
|
||||
0.977956250f,-0.081700223f, 0.026071609f,-0.007229191f, 0.001208889f,
|
||||
0.977181936f,-0.082873221f, 0.026450113f,-0.007329674f, 0.001223833f,
|
||||
0.976394570f,-0.084037148f, 0.026825773f,-0.007429235f, 0.001238572f,
|
||||
0.975594175f,-0.085192002f, 0.027198586f,-0.007527875f, 0.001253106f,
|
||||
0.974780770f,-0.086337783f, 0.027568547f,-0.007625593f, 0.001267436f,
|
||||
0.973954379f,-0.087474491f, 0.027935652f,-0.007722391f, 0.001281562f,
|
||||
0.973115024f,-0.088602126f, 0.028299898f,-0.007818267f, 0.001295485f,
|
||||
0.972262727f,-0.089720690f, 0.028661282f,-0.007913223f, 0.001309207f,
|
||||
0.971397512f,-0.090830182f, 0.029019799f,-0.008007258f, 0.001322726f,
|
||||
0.970519401f,-0.091930604f, 0.029375448f,-0.008100373f, 0.001336045f,
|
||||
0.969628418f,-0.093021958f, 0.029728224f,-0.008192568f, 0.001349164f,
|
||||
0.968724588f,-0.094104245f, 0.030078125f,-0.008283845f, 0.001362084f,
|
||||
0.967807935f,-0.095177467f, 0.030425148f,-0.008374202f, 0.001374806f,
|
||||
0.966878483f,-0.096241627f, 0.030769290f,-0.008463642f, 0.001387329f,
|
||||
0.965936258f,-0.097296726f, 0.031110550f,-0.008552163f, 0.001399656f,
|
||||
0.964981285f,-0.098342768f, 0.031448923f,-0.008639768f, 0.001411786f,
|
||||
0.964013590f,-0.099379756f, 0.031784409f,-0.008726456f, 0.001423721f,
|
||||
0.963033199f,-0.100407693f, 0.032117005f,-0.008812229f, 0.001435461f,
|
||||
0.962040138f,-0.101426582f, 0.032446709f,-0.008897086f, 0.001447008f,
|
||||
0.961034434f,-0.102436428f, 0.032773519f,-0.008981030f, 0.001458361f,
|
||||
0.960016114f,-0.103437235f, 0.033097434f,-0.009064060f, 0.001469522f,
|
||||
0.958985206f,-0.104429007f, 0.033418451f,-0.009146178f, 0.001480492f,
|
||||
0.957941737f,-0.105411749f, 0.033736571f,-0.009227385f, 0.001491271f,
|
||||
0.956885736f,-0.106385466f, 0.034051790f,-0.009307680f, 0.001501860f,
|
||||
0.955817231f,-0.107350163f, 0.034364109f,-0.009387067f, 0.001512261f,
|
||||
0.954736250f,-0.108305845f, 0.034673526f,-0.009465545f, 0.001522473f,
|
||||
0.953642823f,-0.109252518f, 0.034980040f,-0.009543115f, 0.001532497f,
|
||||
0.952536979f,-0.110190189f, 0.035283651f,-0.009619779f, 0.001542336f,
|
||||
0.951418748f,-0.111118864f, 0.035584357f,-0.009695538f, 0.001551988f,
|
||||
0.950288160f,-0.112038548f, 0.035882158f,-0.009770393f, 0.001561456f,
|
||||
0.949145245f,-0.112949250f, 0.036177055f,-0.009844346f, 0.001570741f,
|
||||
0.947990034f,-0.113850976f, 0.036469046f,-0.009917397f, 0.001579842f,
|
||||
0.946822559f,-0.114743733f, 0.036758132f,-0.009989548f, 0.001588761f,
|
||||
0.945642850f,-0.115627529f, 0.037044312f,-0.010060800f, 0.001597498f,
|
||||
0.944450939f,-0.116502372f, 0.037327588f,-0.010131156f, 0.001606056f,
|
||||
0.943246858f,-0.117368270f, 0.037607958f,-0.010200615f, 0.001614434f,
|
||||
0.942030639f,-0.118225231f, 0.037885424f,-0.010269180f, 0.001622633f,
|
||||
0.940802316f,-0.119073264f, 0.038159985f,-0.010336852f, 0.001630655f,
|
||||
0.939561921f,-0.119912378f, 0.038431644f,-0.010403633f, 0.001638500f,
|
||||
0.938309487f,-0.120742582f, 0.038700400f,-0.010469524f, 0.001646169f,
|
||||
0.937045048f,-0.121563886f, 0.038966254f,-0.010534527f, 0.001653663f,
|
||||
0.935768638f,-0.122376299f, 0.039229208f,-0.010598644f, 0.001660984f,
|
||||
0.934480291f,-0.123179830f, 0.039489262f,-0.010661876f, 0.001668131f,
|
||||
0.933180042f,-0.123974491f, 0.039746418f,-0.010724225f, 0.001675106f,
|
||||
0.931867925f,-0.124760291f, 0.040000678f,-0.010785693f, 0.001681910f,
|
||||
0.930543975f,-0.125537242f, 0.040252042f,-0.010846282f, 0.001688544f,
|
||||
0.929208228f,-0.126305353f, 0.040500513f,-0.010905994f, 0.001695008f,
|
||||
0.927860720f,-0.127064637f, 0.040746092f,-0.010964829f, 0.001701305f,
|
||||
0.926501487f,-0.127815104f, 0.040988782f,-0.011022792f, 0.001707433f,
|
||||
0.925130565f,-0.128556767f, 0.041228583f,-0.011079882f, 0.001713396f,
|
||||
0.923747991f,-0.129289637f, 0.041465499f,-0.011136103f, 0.001719193f,
|
||||
0.922353802f,-0.130013726f, 0.041699532f,-0.011191456f, 0.001724825f,
|
||||
0.920948034f,-0.130729047f, 0.041930683f,-0.011245944f, 0.001730294f,
|
||||
0.919530726f,-0.131435613f, 0.042158956f,-0.011299568f, 0.001735601f,
|
||||
0.918101916f,-0.132133435f, 0.042384354f,-0.011352330f, 0.001740746f,
|
||||
0.916661641f,-0.132822528f, 0.042606878f,-0.011404234f, 0.001745730f,
|
||||
0.915209940f,-0.133502905f, 0.042826532f,-0.011455280f, 0.001750555f,
|
||||
0.913746852f,-0.134174578f, 0.043043318f,-0.011505472f, 0.001755221f,
|
||||
0.912272416f,-0.134837563f, 0.043257241f,-0.011554812f, 0.001759730f,
|
||||
0.910786671f,-0.135491873f, 0.043468303f,-0.011603301f, 0.001764082f,
|
||||
0.909289657f,-0.136137522f, 0.043676506f,-0.011650942f, 0.001768278f,
|
||||
0.907781413f,-0.136774525f, 0.043881856f,-0.011697738f, 0.001772320f,
|
||||
0.906261980f,-0.137402897f, 0.044084355f,-0.011743690f, 0.001776208f,
|
||||
0.904731398f,-0.138022653f, 0.044284007f,-0.011788802f, 0.001779944f,
|
||||
0.903189708f,-0.138633807f, 0.044480816f,-0.011833076f, 0.001783528f,
|
||||
0.901636952f,-0.139236376f, 0.044674785f,-0.011876514f, 0.001786962f,
|
||||
0.900073170f,-0.139830375f, 0.044865920f,-0.011919118f, 0.001790246f,
|
||||
0.898498403f,-0.140415819f, 0.045054222f,-0.011960892f, 0.001793381f,
|
||||
0.896912695f,-0.140992726f, 0.045239698f,-0.012001838f, 0.001796369f,
|
||||
0.895316086f,-0.141561111f, 0.045422352f,-0.012041958f, 0.001799211f,
|
||||
0.893708620f,-0.142120991f, 0.045602186f,-0.012081256f, 0.001801907f,
|
||||
0.892090339f,-0.142672383f, 0.045779208f,-0.012119733f, 0.001804459f,
|
||||
0.890461286f,-0.143215304f, 0.045953420f,-0.012157393f, 0.001806868f,
|
||||
0.888821505f,-0.143749771f, 0.046124828f,-0.012194238f, 0.001809134f,
|
||||
0.887171038f,-0.144275802f, 0.046293436f,-0.012230270f, 0.001811259f,
|
||||
0.885509930f,-0.144793414f, 0.046459250f,-0.012265494f, 0.001813243f,
|
||||
0.883838224f,-0.145302625f, 0.046622274f,-0.012299911f, 0.001815089f,
|
||||
0.882155965f,-0.145803453f, 0.046782515f,-0.012333524f, 0.001816796f,
|
||||
0.880463198f,-0.146295917f, 0.046939976f,-0.012366337f, 0.001818366f,
|
||||
0.878759967f,-0.146780035f, 0.047094664f,-0.012398351f, 0.001819800f,
|
||||
0.877046317f,-0.147255826f, 0.047246583f,-0.012429571f, 0.001821099f,
|
||||
0.875322295f,-0.147723309f, 0.047395741f,-0.012459998f, 0.001822264f,
|
||||
0.873587944f,-0.148182503f, 0.047542141f,-0.012489637f, 0.001823295f,
|
||||
0.871843312f,-0.148633428f, 0.047685790f,-0.012518489f, 0.001824196f,
|
||||
0.870088444f,-0.149076103f, 0.047826695f,-0.012546558f, 0.001824965f,
|
||||
0.868323386f,-0.149510548f, 0.047964860f,-0.012573847f, 0.001825604f,
|
||||
0.866548186f,-0.149936783f, 0.048100292f,-0.012600359f, 0.001826115f,
|
||||
0.864762890f,-0.150354828f, 0.048232997f,-0.012626097f, 0.001826498f,
|
||||
0.862967545f,-0.150764704f, 0.048362981f,-0.012651064f, 0.001826754f,
|
||||
0.861162199f,-0.151166432f, 0.048490252f,-0.012675264f, 0.001826885f,
|
||||
0.859346899f,-0.151560031f, 0.048614814f,-0.012698699f, 0.001826891f,
|
||||
0.857521693f,-0.151945524f, 0.048736676f,-0.012721373f, 0.001826774f,
|
||||
0.855686629f,-0.152322931f, 0.048855842f,-0.012743288f, 0.001826534f,
|
||||
0.853841755f,-0.152692274f, 0.048972321f,-0.012764449f, 0.001826173f,
|
||||
0.851987121f,-0.153053574f, 0.049086119f,-0.012784858f, 0.001825691f,
|
||||
0.850122774f,-0.153406854f, 0.049197244f,-0.012804518f, 0.001825091f,
|
||||
0.848248764f,-0.153752135f, 0.049305701f,-0.012823434f, 0.001824372f,
|
||||
0.846365140f,-0.154089440f, 0.049411498f,-0.012841607f, 0.001823536f,
|
||||
0.844471951f,-0.154418791f, 0.049514643f,-0.012859042f, 0.001822584f,
|
||||
0.842569248f,-0.154740210f, 0.049615142f,-0.012875742f, 0.001821517f,
|
||||
0.840657079f,-0.155053721f, 0.049713003f,-0.012891710f, 0.001820336f,
|
||||
0.838735496f,-0.155359346f, 0.049808234f,-0.012906950f, 0.001819042f,
|
||||
0.836804549f,-0.155657108f, 0.049900842f,-0.012921465f, 0.001817636f,
|
||||
0.834864288f,-0.155947032f, 0.049990834f,-0.012935259f, 0.001816120f,
|
||||
0.832914765f,-0.156229140f, 0.050078219f,-0.012948334f, 0.001814493f,
|
||||
0.830956029f,-0.156503456f, 0.050163005f,-0.012960695f, 0.001812758f,
|
||||
0.828988133f,-0.156770004f, 0.050245198f,-0.012972345f, 0.001810916f,
|
||||
0.827011128f,-0.157028808f, 0.050324808f,-0.012983287f, 0.001808967f,
|
||||
0.825025066f,-0.157279893f, 0.050401842f,-0.012993525f, 0.001806912f,
|
||||
0.823029998f,-0.157523282f, 0.050476308f,-0.013003063f, 0.001804753f,
|
||||
0.821025977f,-0.157759001f, 0.050548215f,-0.013011904f, 0.001802491f,
|
||||
0.819013055f,-0.157987074f, 0.050617571f,-0.013020051f, 0.001800126f,
|
||||
0.816991284f,-0.158207526f, 0.050684384f,-0.013027509f, 0.001797660f,
|
||||
0.814960718f,-0.158420382f, 0.050748664f,-0.013034280f, 0.001795094f,
|
||||
0.812921409f,-0.158625668f, 0.050810417f,-0.013040370f, 0.001792428f,
|
||||
0.810873410f,-0.158823410f, 0.050869654f,-0.013045780f, 0.001789664f,
|
||||
0.808816775f,-0.159013631f, 0.050926382f,-0.013050515f, 0.001786804f,
|
||||
0.806751557f,-0.159196360f, 0.050980610f,-0.013054579f, 0.001783847f,
|
||||
0.804677811f,-0.159371620f, 0.051032348f,-0.013057974f, 0.001780795f,
|
||||
0.802595589f,-0.159539440f, 0.051081605f,-0.013060706f, 0.001777649f,
|
||||
0.800504946f,-0.159699844f, 0.051128389f,-0.013062778f, 0.001774411f,
|
||||
0.798405936f,-0.159852860f, 0.051172709f,-0.013064192f, 0.001771080f,
|
||||
0.796298614f,-0.159998514f, 0.051214574f,-0.013064954f, 0.001767659f,
|
||||
0.794183034f,-0.160136832f, 0.051253995f,-0.013065067f, 0.001764147f,
|
||||
0.792059252f,-0.160267843f, 0.051290979f,-0.013064535f, 0.001760547f,
|
||||
0.789927322f,-0.160391572f, 0.051325537f,-0.013063361f, 0.001756860f,
|
||||
0.787787300f,-0.160508047f, 0.051357678f,-0.013061549f, 0.001753085f,
|
||||
0.785639241f,-0.160617296f, 0.051387412f,-0.013059104f, 0.001749225f,
|
||||
0.783483200f,-0.160719346f, 0.051414747f,-0.013056029f, 0.001745280f,
|
||||
0.781319234f,-0.160814225f, 0.051439694f,-0.013052327f, 0.001741252f,
|
||||
0.779147398f,-0.160901960f, 0.051462263f,-0.013048003f, 0.001737141f,
|
||||
0.776967749f,-0.160982580f, 0.051482462f,-0.013043061f, 0.001732948f,
|
||||
0.774780342f,-0.161056113f, 0.051500303f,-0.013037504f, 0.001728675f,
|
||||
0.772585234f,-0.161122587f, 0.051515795f,-0.013031336f, 0.001724323f,
|
||||
0.770382481f,-0.161182031f, 0.051528947f,-0.013024562f, 0.001719892f,
|
||||
0.768172142f,-0.161234473f, 0.051539771f,-0.013017185f, 0.001715383f,
|
||||
0.765954271f,-0.161279942f, 0.051548275f,-0.013009209f, 0.001710798f,
|
||||
0.763728927f,-0.161318466f, 0.051554471f,-0.013000638f, 0.001706137f,
|
||||
0.761496167f,-0.161350075f, 0.051558368f,-0.012991476f, 0.001701402f,
|
||||
0.759256048f,-0.161374798f, 0.051559977f,-0.012981727f, 0.001696593f,
|
||||
0.757008627f,-0.161392665f, 0.051559309f,-0.012971395f, 0.001691712f,
|
||||
0.754753963f,-0.161403704f, 0.051556372f,-0.012960484f, 0.001686760f,
|
||||
0.752492113f,-0.161407945f, 0.051551179f,-0.012948997f, 0.001681737f,
|
||||
0.750223135f,-0.161405418f, 0.051543739f,-0.012936940f, 0.001676644f,
|
||||
0.747947088f,-0.161396153f, 0.051534064f,-0.012924315f, 0.001671483f,
|
||||
0.745664029f,-0.161380179f, 0.051522163f,-0.012911128f, 0.001666254f,
|
||||
0.743374018f,-0.161357527f, 0.051508048f,-0.012897381f, 0.001660959f,
|
||||
0.741077112f,-0.161328227f, 0.051491729f,-0.012883079f, 0.001655598f,
|
||||
0.738773370f,-0.161292309f, 0.051473217f,-0.012868227f, 0.001650173f,
|
||||
0.736462852f,-0.161249804f, 0.051452522f,-0.012852827f, 0.001644684f,
|
||||
0.734145616f,-0.161200742f, 0.051429657f,-0.012836884f, 0.001639133f,
|
||||
0.731821721f,-0.161145154f, 0.051404631f,-0.012820403f, 0.001633520f,
|
||||
0.729491227f,-0.161083070f, 0.051377457f,-0.012803386f, 0.001627846f,
|
||||
0.727154193f,-0.161014523f, 0.051348144f,-0.012785839f, 0.001622112f,
|
||||
0.724810678f,-0.160939542f, 0.051316704f,-0.012767765f, 0.001616320f,
|
||||
0.722460743f,-0.160858158f, 0.051283148f,-0.012749169f, 0.001610470f,
|
||||
0.720104446f,-0.160770404f, 0.051247488f,-0.012730054f, 0.001604563f,
|
||||
0.717741848f,-0.160676310f, 0.051209734f,-0.012710425f, 0.001598600f,
|
||||
0.715373009f,-0.160575909f, 0.051169898f,-0.012690285f, 0.001592582f,
|
||||
0.712997988f,-0.160469230f, 0.051127991f,-0.012669640f, 0.001586510f,
|
||||
0.710616847f,-0.160356307f, 0.051084025f,-0.012648492f, 0.001580385f,
|
||||
0.708229645f,-0.160237171f, 0.051038012f,-0.012626846f, 0.001574207f,
|
||||
0.705836443f,-0.160111854f, 0.050989962f,-0.012604706f, 0.001567979f,
|
||||
0.703437301f,-0.159980389f, 0.050939887f,-0.012582077f, 0.001561700f,
|
||||
0.701032280f,-0.159842806f, 0.050887799f,-0.012558961f, 0.001555372f,
|
||||
0.698621441f,-0.159699138f, 0.050833709f,-0.012535365f, 0.001548995f,
|
||||
0.696204845f,-0.159549419f, 0.050777630f,-0.012511290f, 0.001542571f,
|
||||
0.693782552f,-0.159393679f, 0.050719572f,-0.012486743f, 0.001536101f,
|
||||
0.691354624f,-0.159231952f, 0.050659547f,-0.012461726f, 0.001529584f,
|
||||
0.688921121f,-0.159064270f, 0.050597568f,-0.012436245f, 0.001523023f,
|
||||
0.686482106f,-0.158890666f, 0.050533646f,-0.012410302f, 0.001516417f,
|
||||
0.684037639f,-0.158711173f, 0.050467793f,-0.012383903f, 0.001509769f,
|
||||
0.681587783f,-0.158525823f, 0.050400021f,-0.012357051f, 0.001503079f,
|
||||
0.679132597f,-0.158334650f, 0.050330342f,-0.012329751f, 0.001496347f,
|
||||
0.676672145f,-0.158137687f, 0.050258767f,-0.012302006f, 0.001489575f,
|
||||
0.674206487f,-0.157934966f, 0.050185310f,-0.012273821f, 0.001482764f,
|
||||
0.671735685f,-0.157726522f, 0.050109981f,-0.012245200f, 0.001475914f,
|
||||
0.669259802f,-0.157512387f, 0.050032793f,-0.012216147f, 0.001469026f,
|
||||
0.666778900f,-0.157292594f, 0.049953758f,-0.012186667f, 0.001462101f,
|
||||
0.664293039f,-0.157067178f, 0.049872888f,-0.012156762f, 0.001455141f,
|
||||
0.661802283f,-0.156836172f, 0.049790195f,-0.012126439f, 0.001448145f,
|
||||
0.659306693f,-0.156599609f, 0.049705692f,-0.012095699f, 0.001441115f,
|
||||
0.656806333f,-0.156357523f, 0.049619391f,-0.012064549f, 0.001434051f,
|
||||
0.654301263f,-0.156109948f, 0.049531303f,-0.012032992f, 0.001426955f,
|
||||
0.651791546f,-0.155856918f, 0.049441442f,-0.012001031f, 0.001419827f,
|
||||
0.649277246f,-0.155598467f, 0.049349819f,-0.011968672f, 0.001412668f,
|
||||
0.646758423f,-0.155334628f, 0.049256448f,-0.011935918f, 0.001405479f,
|
||||
0.644235142f,-0.155065436f, 0.049161340f,-0.011902774f, 0.001398261f,
|
||||
0.641707464f,-0.154790925f, 0.049064507f,-0.011869243f, 0.001391015f,
|
||||
0.639175452f,-0.154511129f, 0.048965963f,-0.011835330f, 0.001383741f,
|
||||
0.636639169f,-0.154226083f, 0.048865719f,-0.011801038f, 0.001376440f,
|
||||
0.634098677f,-0.153935820f, 0.048763788f,-0.011766373f, 0.001369113f,
|
||||
0.631554040f,-0.153640376f, 0.048660183f,-0.011731337f, 0.001361761f,
|
||||
0.629005320f,-0.153339784f, 0.048554915f,-0.011695936f, 0.001354384f,
|
||||
0.626452580f,-0.153034079f, 0.048447999f,-0.011660173f, 0.001346984f,
|
||||
0.623895883f,-0.152723296f, 0.048339445f,-0.011624053f, 0.001339561f,
|
||||
0.621335293f,-0.152407470f, 0.048229267f,-0.011587579f, 0.001332116f,
|
||||
0.618770871f,-0.152086634f, 0.048117478f,-0.011550756f, 0.001324650f,
|
||||
0.616202682f,-0.151760824f, 0.048004090f,-0.011513587f, 0.001317164f,
|
||||
0.613630788f,-0.151430075f, 0.047889115f,-0.011476077f, 0.001309657f,
|
||||
0.611055252f,-0.151094421f, 0.047772566f,-0.011438230f, 0.001302132f,
|
||||
0.608476138f,-0.150753897f, 0.047654456f,-0.011400050f, 0.001294589f,
|
||||
0.605893509f,-0.150408538f, 0.047534798f,-0.011361541f, 0.001287028f,
|
||||
0.603307427f,-0.150058380f, 0.047413604f,-0.011322706f, 0.001279451f,
|
||||
0.600717957f,-0.149703457f, 0.047290887f,-0.011283552f, 0.001271857f,
|
||||
0.598125161f,-0.149343804f, 0.047166660f,-0.011244080f, 0.001264249f,
|
||||
0.595529103f,-0.148979456f, 0.047040936f,-0.011204296f, 0.001256626f,
|
||||
0.592929846f,-0.148610450f, 0.046913726f,-0.011164202f, 0.001248990f,
|
||||
0.590327454f,-0.148236818f, 0.046785045f,-0.011123805f, 0.001241340f,
|
||||
0.587721989f,-0.147858598f, 0.046654904f,-0.011083106f, 0.001233679f,
|
||||
0.585113515f,-0.147475824f, 0.046523317f,-0.011042111f, 0.001226005f,
|
||||
0.582502096f,-0.147088532f, 0.046390297f,-0.011000824f, 0.001218321f,
|
||||
0.579887795f,-0.146696757f, 0.046255856f,-0.010959248f, 0.001210627f,
|
||||
0.577270674f,-0.146300533f, 0.046120007f,-0.010917387f, 0.001202924f,
|
||||
0.574650799f,-0.145899898f, 0.045982762f,-0.010875246f, 0.001195211f,
|
||||
0.572028231f,-0.145494885f, 0.045844136f,-0.010832828f, 0.001187491f,
|
||||
0.569403034f,-0.145085532f, 0.045704140f,-0.010790137f, 0.001179764f,
|
||||
0.566775272f,-0.144671872f, 0.045562787f,-0.010747178f, 0.001172030f,
|
||||
0.564145009f,-0.144253941f, 0.045420091f,-0.010703955f, 0.001164289f,
|
||||
0.561512306f,-0.143831776f, 0.045276064f,-0.010660470f, 0.001156544f,
|
||||
0.558877229f,-0.143405412f, 0.045130719f,-0.010616729f, 0.001148794f,
|
||||
0.556239839f,-0.142974883f, 0.044984069f,-0.010572735f, 0.001141040f,
|
||||
0.553600201f,-0.142540227f, 0.044836126f,-0.010528493f, 0.001133283f,
|
||||
0.550958378f,-0.142101478f, 0.044686904f,-0.010484005f, 0.001125523f,
|
||||
0.548314433f,-0.141658672f, 0.044536416f,-0.010439276f, 0.001117761f,
|
||||
0.545668429f,-0.141211844f, 0.044384675f,-0.010394310f, 0.001109997f,
|
||||
0.543020430f,-0.140761032f, 0.044231692f,-0.010349111f, 0.001102233f,
|
||||
0.540370499f,-0.140306269f, 0.044077482f,-0.010303683f, 0.001094469f,
|
||||
0.537718699f,-0.139847592f, 0.043922057f,-0.010258029f, 0.001086705f,
|
||||
0.535065094f,-0.139385036f, 0.043765430f,-0.010212153f, 0.001078942f,
|
||||
0.532409746f,-0.138918637f, 0.043607614f,-0.010166060f, 0.001071182f,
|
||||
0.529752718f,-0.138448432f, 0.043448622f,-0.010119753f, 0.001063423f,
|
||||
0.527094075f,-0.137974455f, 0.043288466f,-0.010073235f, 0.001055668f,
|
||||
0.524433878f,-0.137496742f, 0.043127159f,-0.010026512f, 0.001047916f,
|
||||
0.521772191f,-0.137015329f, 0.042964716f,-0.009979586f, 0.001040168f,
|
||||
0.519109077f,-0.136530252f, 0.042801147f,-0.009932461f, 0.001032425f,
|
||||
0.516444599f,-0.136041547f, 0.042636467f,-0.009885141f, 0.001024687f,
|
||||
0.513778819f,-0.135549248f, 0.042470687f,-0.009837631f, 0.001016955f,
|
||||
0.511111801f,-0.135053393f, 0.042303821f,-0.009789933f, 0.001009230f,
|
||||
0.508443608f,-0.134554017f, 0.042135882f,-0.009742051f, 0.001001511f,
|
||||
0.505774302f,-0.134051154f, 0.041966883f,-0.009693990f, 0.000993800f,
|
||||
0.503103946f,-0.133544842f, 0.041796836f,-0.009645752f, 0.000986098f,
|
||||
0.500432602f,-0.133035116f, 0.041625754f,-0.009597342f, 0.000978404f,
|
||||
0.497760334f,-0.132522012f, 0.041453650f,-0.009548764f, 0.000970719f,
|
||||
0.495087203f,-0.132005564f, 0.041280536f,-0.009500020f, 0.000963044f,
|
||||
0.492413273f,-0.131485810f, 0.041106427f,-0.009451116f, 0.000955380f,
|
||||
0.489738606f,-0.130962784f, 0.040931333f,-0.009402053f, 0.000947726f,
|
||||
0.487063264f,-0.130436523f, 0.040755269f,-0.009352837f, 0.000940083f,
|
||||
0.484387309f,-0.129907061f, 0.040578247f,-0.009303470f, 0.000932453f,
|
||||
0.481710804f,-0.129374435f, 0.040400279f,-0.009253957f, 0.000924835f,
|
||||
0.479033811f,-0.128838680f, 0.040221378f,-0.009204300f, 0.000917229f,
|
||||
0.476356392f,-0.128299831f, 0.040041558f,-0.009154505f, 0.000909637f,
|
||||
0.473678610f,-0.127757925f, 0.039860830f,-0.009104573f, 0.000902059f,
|
||||
0.471000525f,-0.127212997f, 0.039679208f,-0.009054509f, 0.000894496f,
|
||||
0.468322201f,-0.126665082f, 0.039496704f,-0.009004316f, 0.000886947f,
|
||||
0.465643698f,-0.126114216f, 0.039313331f,-0.008953998f, 0.000879414f,
|
||||
0.462965079f,-0.125560435f, 0.039129101f,-0.008903558f, 0.000871896f,
|
||||
0.460286405f,-0.125003773f, 0.038944027f,-0.008853000f, 0.000864395f,
|
||||
0.457607738f,-0.124444266f, 0.038758122f,-0.008802328f, 0.000856910f,
|
||||
0.454929140f,-0.123881950f, 0.038571399f,-0.008751544f, 0.000849443f,
|
||||
0.452250671f,-0.123316860f, 0.038383869f,-0.008700653f, 0.000841994f,
|
||||
0.449572393f,-0.122749031f, 0.038195545f,-0.008649657f, 0.000834562f,
|
||||
0.446894368f,-0.122178499f, 0.038006440f,-0.008598561f, 0.000827149f,
|
||||
0.444216656f,-0.121605299f, 0.037816567f,-0.008547367f, 0.000819755f,
|
||||
0.441539319f,-0.121029465f, 0.037625937f,-0.008496080f, 0.000812381f,
|
||||
0.438862417f,-0.120451035f, 0.037434564f,-0.008444702f, 0.000805026f,
|
||||
0.436186011f,-0.119870041f, 0.037242460f,-0.008393237f, 0.000797692f,
|
||||
0.433510163f,-0.119286520f, 0.037049637f,-0.008341688f, 0.000790378f,
|
||||
0.430834932f,-0.118700507f, 0.036856108f,-0.008290058f, 0.000783086f,
|
||||
0.428160380f,-0.118112036f, 0.036661885f,-0.008238352f, 0.000775815f,
|
||||
0.425486566f,-0.117521144f, 0.036466980f,-0.008186572f, 0.000768565f,
|
||||
0.422813551f,-0.116927863f, 0.036271406f,-0.008134722f, 0.000761339f,
|
||||
0.420141396f,-0.116332231f, 0.036075174f,-0.008082804f, 0.000754135f,
|
||||
0.417470160f,-0.115734281f, 0.035878299f,-0.008030823f, 0.000746954f,
|
||||
0.414799903f,-0.115134048f, 0.035680790f,-0.007978781f, 0.000739796f,
|
||||
0.412130686f,-0.114531567f, 0.035482662f,-0.007926681f, 0.000732662f,
|
||||
0.409462568f,-0.113926872f, 0.035283926f,-0.007874528f, 0.000725553f,
|
||||
0.406795609f,-0.113320000f, 0.035084593f,-0.007822324f, 0.000718468f,
|
||||
0.404129868f,-0.112710983f, 0.034884678f,-0.007770072f, 0.000711408f,
|
||||
0.401465405f,-0.112099857f, 0.034684190f,-0.007717775f, 0.000704373f,
|
||||
0.398802279f,-0.111486655f, 0.034483143f,-0.007665437f, 0.000697364f,
|
||||
0.396140548f,-0.110871414f, 0.034281549f,-0.007613061f, 0.000690381f,
|
||||
0.393480274f,-0.110254166f, 0.034079419f,-0.007560650f, 0.000683424f,
|
||||
0.390821513f,-0.109634947f, 0.033876767f,-0.007508207f, 0.000676494f,
|
||||
0.388164325f,-0.109013790f, 0.033673602f,-0.007455735f, 0.000669590f,
|
||||
0.385508769f,-0.108390730f, 0.033469939f,-0.007403238f, 0.000662714f,
|
||||
0.382854903f,-0.107765801f, 0.033265787f,-0.007350717f, 0.000655866f,
|
||||
0.380202785f,-0.107139037f, 0.033061161f,-0.007298177f, 0.000649046f,
|
||||
0.377552474f,-0.106510472f, 0.032856070f,-0.007245621f, 0.000642253f,
|
||||
0.374904028f,-0.105880140f, 0.032650528f,-0.007193050f, 0.000635490f,
|
||||
0.372257505f,-0.105248075f, 0.032444545f,-0.007140469f, 0.000628755f,
|
||||
0.369612962f,-0.104614312f, 0.032238134f,-0.007087881f, 0.000622049f,
|
||||
0.366970458f,-0.103978882f, 0.032031306f,-0.007035287f, 0.000615373f,
|
||||
0.364330050f,-0.103341821f, 0.031824073f,-0.006982692f, 0.000608726f,
|
||||
0.361691794f,-0.102703162f, 0.031616447f,-0.006930098f, 0.000602110f,
|
||||
0.359055750f,-0.102062939f, 0.031408439f,-0.006877508f, 0.000595523f,
|
||||
0.356421972f,-0.101421184f, 0.031200061f,-0.006824926f, 0.000588967f,
|
||||
0.353790520f,-0.100777932f, 0.030991325f,-0.006772353f, 0.000582442f,
|
||||
0.351161448f,-0.100133216f, 0.030782241f,-0.006719792f, 0.000575948f,
|
||||
0.348534815f,-0.099487069f, 0.030572821f,-0.006667247f, 0.000569485f,
|
||||
0.345910675f,-0.098839524f, 0.030363078f,-0.006614721f, 0.000563054f,
|
||||
0.343289087f,-0.098190615f, 0.030153021f,-0.006562215f, 0.000556654f,
|
||||
0.340670105f,-0.097540374f, 0.029942663f,-0.006509734f, 0.000550287f,
|
||||
0.338053786f,-0.096888834f, 0.029732015f,-0.006457279f, 0.000543951f,
|
||||
0.335440186f,-0.096236029f, 0.029521089f,-0.006404853f, 0.000537649f,
|
||||
0.332829360f,-0.095581991f, 0.029309894f,-0.006352460f, 0.000531378f,
|
||||
0.330221364f,-0.094926753f, 0.029098443f,-0.006300101f, 0.000525141f,
|
||||
0.327616253f,-0.094270347f, 0.028886747f,-0.006247780f, 0.000518937f,
|
||||
0.325014082f,-0.093612807f, 0.028674817f,-0.006195500f, 0.000512766f,
|
||||
0.322414906f,-0.092954163f, 0.028462663f,-0.006143262f, 0.000506629f,
|
||||
0.319818781f,-0.092294450f, 0.028250298f,-0.006091069f, 0.000500525f,
|
||||
0.317225760f,-0.091633699f, 0.028037732f,-0.006038925f, 0.000494456f,
|
||||
0.314635898f,-0.090971943f, 0.027824976f,-0.005986831f, 0.000488420f,
|
||||
0.312049250f,-0.090309213f, 0.027612040f,-0.005934790f, 0.000482419f,
|
||||
0.309465869f,-0.089645542f, 0.027398936f,-0.005882806f, 0.000476453f,
|
||||
0.306885810f,-0.088980961f, 0.027185675f,-0.005830879f, 0.000470521f,
|
||||
0.304309126f,-0.088315503f, 0.026972268f,-0.005779014f, 0.000464623f,
|
||||
0.301735870f,-0.087649199f, 0.026758724f,-0.005727211f, 0.000458761f,
|
||||
0.299166097f,-0.086982081f, 0.026545055f,-0.005675475f, 0.000452934f,
|
||||
0.296599859f,-0.086314180f, 0.026331272f,-0.005623807f, 0.000447143f,
|
||||
0.294037209f,-0.085645528f, 0.026117385f,-0.005572209f, 0.000441387f,
|
||||
0.291478200f,-0.084976157f, 0.025903405f,-0.005520685f, 0.000435666f,
|
||||
0.288922885f,-0.084306097f, 0.025689341f,-0.005469237f, 0.000429981f,
|
||||
0.286371316f,-0.083635380f, 0.025475206f,-0.005417866f, 0.000424332f,
|
||||
0.283823545f,-0.082964036f, 0.025261008f,-0.005366575f, 0.000418720f,
|
||||
0.281279624f,-0.082292098f, 0.025046759f,-0.005315368f, 0.000413143f,
|
||||
0.278739606f,-0.081619595f, 0.024832469f,-0.005264245f, 0.000407603f,
|
||||
0.276203542f,-0.080946559f, 0.024618148f,-0.005213209f, 0.000402099f,
|
||||
0.273671482f,-0.080273020f, 0.024403807f,-0.005162263f, 0.000396632f,
|
||||
0.271143479f,-0.079599009f, 0.024189455f,-0.005111409f, 0.000391201f,
|
||||
0.268619584f,-0.078924556f, 0.023975103f,-0.005060649f, 0.000385807f,
|
||||
0.266099847f,-0.078249692f, 0.023760760f,-0.005009985f, 0.000380450f,
|
||||
0.263584318f,-0.077574447f, 0.023546438f,-0.004959420f, 0.000375130f,
|
||||
0.261073049f,-0.076898851f, 0.023332145f,-0.004908956f, 0.000369847f,
|
||||
0.258566090f,-0.076222934f, 0.023117893f,-0.004858594f, 0.000364602f,
|
||||
0.256063490f,-0.075546727f, 0.022903690f,-0.004808337f, 0.000359393f,
|
||||
0.253565299f,-0.074870258f, 0.022689547f,-0.004758188f, 0.000354222f,
|
||||
0.251071568f,-0.074193558f, 0.022475473f,-0.004708148f, 0.000349088f,
|
||||
0.248582344f,-0.073516656f, 0.022261479f,-0.004658219f, 0.000343992f,
|
||||
0.246097678f,-0.072839582f, 0.022047574f,-0.004608404f, 0.000338934f,
|
||||
0.243617618f,-0.072162365f, 0.021833767f,-0.004558704f, 0.000333913f,
|
||||
0.241142213f,-0.071485035f, 0.021620069f,-0.004509122f, 0.000328930f,
|
||||
0.238671511f,-0.070807621f, 0.021406488f,-0.004459659f, 0.000323984f,
|
||||
0.236205561f,-0.070130151f, 0.021193035f,-0.004410318f, 0.000319077f,
|
||||
0.233744411f,-0.069452655f, 0.020979718f,-0.004361100f, 0.000314207f,
|
||||
0.231288109f,-0.068775161f, 0.020766548f,-0.004312008f, 0.000309375f,
|
||||
0.228836701f,-0.068097699f, 0.020553533f,-0.004263043f, 0.000304582f,
|
||||
0.226390237f,-0.067420297f, 0.020340683f,-0.004214207f, 0.000299826f,
|
||||
0.223948762f,-0.066742982f, 0.020128008f,-0.004165503f, 0.000295108f,
|
||||
0.221512323f,-0.066065785f, 0.019915515f,-0.004116932f, 0.000290429f,
|
||||
0.219080968f,-0.065388732f, 0.019703215f,-0.004068496f, 0.000285787f,
|
||||
0.216654743f,-0.064711852f, 0.019491117f,-0.004020196f, 0.000281184f,
|
||||
0.214233694f,-0.064035173f, 0.019279229f,-0.003972036f, 0.000276619f,
|
||||
0.211817866f,-0.063358724f, 0.019067561f,-0.003924015f, 0.000272092f,
|
||||
0.209407307f,-0.062682530f, 0.018856122f,-0.003876137f, 0.000267604f,
|
||||
0.207002061f,-0.062006621f, 0.018644920f,-0.003828403f, 0.000263153f,
|
||||
0.204602173f,-0.061331024f, 0.018433965f,-0.003780814f, 0.000258741f,
|
||||
0.202207689f,-0.060655766f, 0.018223264f,-0.003733373f, 0.000254367f,
|
||||
0.199818654f,-0.059980874f, 0.018012827f,-0.003686081f, 0.000250032f,
|
||||
0.197435111f,-0.059306375f, 0.017802663f,-0.003638940f, 0.000245734f,
|
||||
0.195057107f,-0.058632298f, 0.017592780f,-0.003591951f, 0.000241475f,
|
||||
0.192684683f,-0.057958667f, 0.017383186f,-0.003545116f, 0.000237254f,
|
||||
0.190317885f,-0.057285511f, 0.017173891f,-0.003498437f, 0.000233071f,
|
||||
0.187956756f,-0.056612855f, 0.016964902f,-0.003451915f, 0.000228927f,
|
||||
0.185601339f,-0.055940727f, 0.016756228f,-0.003405553f, 0.000224821f,
|
||||
0.183251678f,-0.055269152f, 0.016547878f,-0.003359350f, 0.000220752f,
|
||||
0.180907815f,-0.054598157f, 0.016339859f,-0.003313310f, 0.000216722f,
|
||||
0.178569792f,-0.053927768f, 0.016132180f,-0.003267434f, 0.000212731f,
|
||||
0.176237654f,-0.053258011f, 0.015924850f,-0.003221722f, 0.000208777f,
|
||||
0.173911440f,-0.052588912f, 0.015717875f,-0.003176177f, 0.000204861f,
|
||||
0.171591194f,-0.051920496f, 0.015511264f,-0.003130800f, 0.000200983f,
|
||||
0.169276957f,-0.051252790f, 0.015305026f,-0.003085592f, 0.000197143f,
|
||||
0.166968771f,-0.050585817f, 0.015099168f,-0.003040555f, 0.000193341f,
|
||||
0.164666676f,-0.049919605f, 0.014893698f,-0.002995691f, 0.000189577f,
|
||||
0.162370713f,-0.049254177f, 0.014688624f,-0.002951000f, 0.000185851f,
|
||||
0.160080923f,-0.048589560f, 0.014483954f,-0.002906485f, 0.000182163f,
|
||||
0.157797346f,-0.047925777f, 0.014279695f,-0.002862145f, 0.000178512f,
|
||||
0.155520023f,-0.047262854f, 0.014075855f,-0.002817984f, 0.000174899f,
|
||||
0.153248993f,-0.046600815f, 0.013872442f,-0.002774001f, 0.000171324f,
|
||||
0.150984296f,-0.045939685f, 0.013669462f,-0.002730199f, 0.000167786f,
|
||||
0.148725971f,-0.045279488f, 0.013466925f,-0.002686579f, 0.000164285f,
|
||||
0.146474057f,-0.044620249f, 0.013264837f,-0.002643141f, 0.000160822f,
|
||||
0.144228593f,-0.043961991f, 0.013063205f,-0.002599888f, 0.000157396f,
|
||||
0.141989618f,-0.043304738f, 0.012862038f,-0.002556820f, 0.000154008f,
|
||||
0.139757169f,-0.042648514f, 0.012661341f,-0.002513938f, 0.000150656f,
|
||||
0.137531285f,-0.041993344f, 0.012461123f,-0.002471244f, 0.000147342f,
|
||||
0.135312004f,-0.041339249f, 0.012261390f,-0.002428739f, 0.000144064f,
|
||||
0.133099362f,-0.040686254f, 0.012062150f,-0.002386425f, 0.000140824f,
|
||||
0.130893398f,-0.040034383f, 0.011863409f,-0.002344301f, 0.000137620f,
|
||||
0.128694148f,-0.039383657f, 0.011665175f,-0.002302370f, 0.000134453f,
|
||||
0.126501649f,-0.038734100f, 0.011467454f,-0.002260631f, 0.000131323f,
|
||||
0.124315937f,-0.038085735f, 0.011270254f,-0.002219088f, 0.000128229f,
|
||||
0.122137048f,-0.037438584f, 0.011073581f,-0.002177740f, 0.000125171f,
|
||||
0.119965018f,-0.036792671f, 0.010877441f,-0.002136588f, 0.000122150f,
|
||||
0.117799883f,-0.036148017f, 0.010681842f,-0.002095634f, 0.000119165f,
|
||||
0.115641679f,-0.035504644f, 0.010486791f,-0.002054878f, 0.000116216f,
|
||||
0.113490439f,-0.034862575f, 0.010292293f,-0.002014322f, 0.000113304f,
|
||||
0.111346199f,-0.034221832f, 0.010098355f,-0.001973967f, 0.000110427f,
|
||||
0.109208994f,-0.033582436f, 0.009904984f,-0.001933813f, 0.000107585f,
|
||||
0.107078857f,-0.032944409f, 0.009712186f,-0.001893861f, 0.000104780f,
|
||||
0.104955823f,-0.032307773f, 0.009519967f,-0.001854112f, 0.000102010f,
|
||||
0.102839925f,-0.031672548f, 0.009328334f,-0.001814568f, 0.000099275f,
|
||||
0.100731196f,-0.031038757f, 0.009137293f,-0.001775228f, 0.000096576f,
|
||||
0.098629671f,-0.030406420f, 0.008946849f,-0.001736095f, 0.000093911f,
|
||||
0.096535380f,-0.029775558f, 0.008757010f,-0.001697168f, 0.000091282f,
|
||||
0.094448358f,-0.029146192f, 0.008567781f,-0.001658448f, 0.000088688f,
|
||||
0.092368636f,-0.028518343f, 0.008379168f,-0.001619937f, 0.000086128f,
|
||||
0.090296247f,-0.027892030f, 0.008191177f,-0.001581635f, 0.000083603f,
|
||||
0.088231221f,-0.027267275f, 0.008003813f,-0.001543542f, 0.000081113f,
|
||||
0.086173591f,-0.026644097f, 0.007817084f,-0.001505661f, 0.000078656f,
|
||||
0.084123388f,-0.026022517f, 0.007630993f,-0.001467990f, 0.000076234f,
|
||||
0.082080642f,-0.025402554f, 0.007445548f,-0.001430532f, 0.000073846f,
|
||||
0.080045384f,-0.024784228f, 0.007260753f,-0.001393286f, 0.000071492f,
|
||||
0.078017645f,-0.024167559f, 0.007076615f,-0.001356254f, 0.000069172f,
|
||||
0.075997454f,-0.023552566f, 0.006893138f,-0.001319435f, 0.000066885f,
|
||||
0.073984841f,-0.022939269f, 0.006710328f,-0.001282832f, 0.000064632f,
|
||||
0.071979836f,-0.022327686f, 0.006528191f,-0.001246443f, 0.000062412f,
|
||||
0.069982467f,-0.021717836f, 0.006346731f,-0.001210271f, 0.000060225f,
|
||||
0.067992764f,-0.021109739f, 0.006165955f,-0.001174315f, 0.000058072f,
|
||||
0.066010755f,-0.020503413f, 0.005985866f,-0.001138576f, 0.000055951f,
|
||||
0.064036468f,-0.019898876f, 0.005806472f,-0.001103055f, 0.000053862f,
|
||||
0.062069932f,-0.019296147f, 0.005627775f,-0.001067751f, 0.000051807f,
|
||||
0.060111174f,-0.018695244f, 0.005449782f,-0.001032667f, 0.000049783f,
|
||||
0.058160222f,-0.018096185f, 0.005272497f,-0.000997801f, 0.000047792f,
|
||||
0.056217102f,-0.017498988f, 0.005095926f,-0.000963156f, 0.000045833f,
|
||||
0.054281842f,-0.016903671f, 0.004920073f,-0.000928730f, 0.000043905f,
|
||||
0.052354468f,-0.016310251f, 0.004744943f,-0.000894525f, 0.000042010f,
|
||||
0.050435006f,-0.015718746f, 0.004570540f,-0.000860541f, 0.000040146f,
|
||||
0.048523483f,-0.015129173f, 0.004396870f,-0.000826779f, 0.000038313f,
|
||||
0.046619924f,-0.014541549f, 0.004223937f,-0.000793238f, 0.000036511f,
|
||||
0.044724355f,-0.013955892f, 0.004051745f,-0.000759920f, 0.000034740f,
|
||||
0.042836800f,-0.013372217f, 0.003880299f,-0.000726824f, 0.000033000f,
|
||||
0.040957285f,-0.012790543f, 0.003709604f,-0.000693951f, 0.000031291f,
|
||||
0.039085833f,-0.012210884f, 0.003539663f,-0.000661301f, 0.000029612f,
|
||||
0.037222470f,-0.011633259f, 0.003370481f,-0.000628876f, 0.000027964f,
|
||||
0.035367219f,-0.011057682f, 0.003202062f,-0.000596674f, 0.000026345f,
|
||||
0.033520103f,-0.010484170f, 0.003034411f,-0.000564696f, 0.000024756f,
|
||||
0.031681147f,-0.009912739f, 0.002867532f,-0.000532943f, 0.000023197f,
|
||||
0.029850373f,-0.009343404f, 0.002701428f,-0.000501414f, 0.000021668f,
|
||||
0.028027804f,-0.008776182f, 0.002536104f,-0.000470111f, 0.000020168f,
|
||||
0.026213462f,-0.008211087f, 0.002371564f,-0.000439032f, 0.000018697f,
|
||||
0.024407370f,-0.007648135f, 0.002207811f,-0.000408180f, 0.000017255f,
|
||||
0.022609549f,-0.007087341f, 0.002044849f,-0.000377553f, 0.000015842f,
|
||||
0.020820021f,-0.006528720f, 0.001882682f,-0.000347151f, 0.000014457f,
|
||||
0.019038808f,-0.005972286f, 0.001721315f,-0.000316976f, 0.000013101f,
|
||||
0.017265930f,-0.005418056f, 0.001560750f,-0.000287027f, 0.000011773f,
|
||||
0.015501408f,-0.004866042f, 0.001400991f,-0.000257305f, 0.000010473f,
|
||||
0.013745262f,-0.004316260f, 0.001242041f,-0.000227808f, 0.000009201f,
|
||||
0.011997513f,-0.003768723f, 0.001083905f,-0.000198539f, 0.000007956f,
|
||||
0.010258180f,-0.003223445f, 0.000926585f,-0.000169495f, 0.000006739f,
|
||||
0.008527283f,-0.002680442f, 0.000770086f,-0.000140679f, 0.000005549f,
|
||||
0.006804842f,-0.002139725f, 0.000614409f,-0.000112090f, 0.000004386f,
|
||||
0.005090874f,-0.001601309f, 0.000459559f,-0.000083727f, 0.000003250f,
|
||||
0.003385399f,-0.001065208f, 0.000305539f,-0.000055591f, 0.000002140f,
|
||||
0.001688435f,-0.000531434f, 0.000152351f,-0.000027682f, 0.000001057f,
|
||||
};
|
||||
|
||||
1255
vendor/sdl-3.0.0/src/audio/SDL_audiocvt.c
vendored
Normal file
1255
vendor/sdl-3.0.0/src/audio/SDL_audiocvt.c
vendored
Normal file
File diff suppressed because it is too large
Load diff
125
vendor/sdl-3.0.0/src/audio/SDL_audiodev.c
vendored
Normal file
125
vendor/sdl-3.0.0/src/audio/SDL_audiodev.c
vendored
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
// Get the name of the audio device we use for output
|
||||
|
||||
#if defined(SDL_AUDIO_DRIVER_NETBSD) || defined(SDL_AUDIO_DRIVER_OSS)
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h> // For close()
|
||||
|
||||
#include "SDL_audiodev_c.h"
|
||||
|
||||
#ifndef SDL_PATH_DEV_DSP
|
||||
#if defined(__NETBSD__) || defined(__OPENBSD__)
|
||||
#define SDL_PATH_DEV_DSP "/dev/audio"
|
||||
#else
|
||||
#define SDL_PATH_DEV_DSP "/dev/dsp"
|
||||
#endif
|
||||
#endif
|
||||
#ifndef SDL_PATH_DEV_DSP24
|
||||
#define SDL_PATH_DEV_DSP24 "/dev/sound/dsp"
|
||||
#endif
|
||||
#ifndef SDL_PATH_DEV_AUDIO
|
||||
#define SDL_PATH_DEV_AUDIO "/dev/audio"
|
||||
#endif
|
||||
|
||||
static void test_device(const SDL_bool iscapture, const char *fname, int flags, SDL_bool (*test)(int fd))
|
||||
{
|
||||
struct stat sb;
|
||||
if ((stat(fname, &sb) == 0) && (S_ISCHR(sb.st_mode))) {
|
||||
const int audio_fd = open(fname, flags | O_CLOEXEC, 0);
|
||||
if (audio_fd >= 0) {
|
||||
const SDL_bool okay = test(audio_fd);
|
||||
close(audio_fd);
|
||||
if (okay) {
|
||||
static size_t dummyhandle = 0;
|
||||
dummyhandle++;
|
||||
SDL_assert(dummyhandle != 0);
|
||||
|
||||
/* Note that spec is NULL; while we are opening the device
|
||||
* endpoint here, the endpoint does not provide any mix format
|
||||
* information, making this information inaccessible at
|
||||
* enumeration time
|
||||
*/
|
||||
SDL_AddAudioDevice(iscapture, fname, NULL, (void *)(uintptr_t)dummyhandle);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static SDL_bool test_stub(int fd)
|
||||
{
|
||||
return SDL_TRUE;
|
||||
}
|
||||
|
||||
static void SDL_EnumUnixAudioDevices_Internal(const SDL_bool iscapture, const SDL_bool classic, SDL_bool (*test)(int))
|
||||
{
|
||||
const int flags = iscapture ? OPEN_FLAGS_INPUT : OPEN_FLAGS_OUTPUT;
|
||||
const char *audiodev;
|
||||
char audiopath[1024];
|
||||
|
||||
if (test == NULL) {
|
||||
test = test_stub;
|
||||
}
|
||||
|
||||
// Figure out what our audio device is
|
||||
audiodev = SDL_getenv("SDL_PATH_DSP");
|
||||
if (audiodev == NULL) {
|
||||
audiodev = SDL_getenv("AUDIODEV");
|
||||
}
|
||||
if (audiodev == NULL) {
|
||||
if (classic) {
|
||||
audiodev = SDL_PATH_DEV_AUDIO;
|
||||
} else {
|
||||
struct stat sb;
|
||||
|
||||
// Added support for /dev/sound/\* in Linux 2.4
|
||||
if (((stat("/dev/sound", &sb) == 0) && S_ISDIR(sb.st_mode)) && ((stat(SDL_PATH_DEV_DSP24, &sb) == 0) && S_ISCHR(sb.st_mode))) {
|
||||
audiodev = SDL_PATH_DEV_DSP24;
|
||||
} else {
|
||||
audiodev = SDL_PATH_DEV_DSP;
|
||||
}
|
||||
}
|
||||
}
|
||||
test_device(iscapture, audiodev, flags, test);
|
||||
|
||||
if (SDL_strlen(audiodev) < (sizeof(audiopath) - 3)) {
|
||||
int instance = 0;
|
||||
while (instance <= 64) {
|
||||
(void)SDL_snprintf(audiopath, SDL_arraysize(audiopath),
|
||||
"%s%d", audiodev, instance);
|
||||
instance++;
|
||||
test_device(iscapture, audiopath, flags, test);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SDL_EnumUnixAudioDevices(const SDL_bool classic, SDL_bool (*test)(int))
|
||||
{
|
||||
SDL_EnumUnixAudioDevices_Internal(SDL_TRUE, classic, test);
|
||||
SDL_EnumUnixAudioDevices_Internal(SDL_FALSE, classic, test);
|
||||
}
|
||||
|
||||
#endif // Audio device selection
|
||||
41
vendor/sdl-3.0.0/src/audio/SDL_audiodev_c.h
vendored
Normal file
41
vendor/sdl-3.0.0/src/audio/SDL_audiodev_c.h
vendored
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
#ifndef SDL_audiodev_c_h_
|
||||
#define SDL_audiodev_c_h_
|
||||
|
||||
#include "SDL_internal.h"
|
||||
#include "SDL_sysaudio.h"
|
||||
|
||||
// Open the audio device for playback, and don't block if busy
|
||||
//#define USE_BLOCKING_WRITES
|
||||
|
||||
#ifdef USE_BLOCKING_WRITES
|
||||
#define OPEN_FLAGS_OUTPUT O_WRONLY
|
||||
#define OPEN_FLAGS_INPUT O_RDONLY
|
||||
#else
|
||||
#define OPEN_FLAGS_OUTPUT (O_WRONLY | O_NONBLOCK)
|
||||
#define OPEN_FLAGS_INPUT (O_RDONLY | O_NONBLOCK)
|
||||
#endif
|
||||
|
||||
extern void SDL_EnumUnixAudioDevices(const SDL_bool classic, SDL_bool (*test)(int));
|
||||
|
||||
#endif // SDL_audiodev_c_h_
|
||||
516
vendor/sdl-3.0.0/src/audio/SDL_audioqueue.c
vendored
Normal file
516
vendor/sdl-3.0.0/src/audio/SDL_audioqueue.c
vendored
Normal file
|
|
@ -0,0 +1,516 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#include "SDL_audioqueue.h"
|
||||
|
||||
#define AUDIO_SPECS_EQUAL(x, y) (((x).format == (y).format) && ((x).channels == (y).channels) && ((x).freq == (y).freq))
|
||||
|
||||
struct SDL_AudioTrack
|
||||
{
|
||||
SDL_AudioSpec spec;
|
||||
SDL_bool flushed;
|
||||
SDL_AudioTrack *next;
|
||||
|
||||
size_t (*avail)(void *ctx);
|
||||
int (*write)(void *ctx, const Uint8 *buf, size_t len);
|
||||
size_t (*read)(void *ctx, Uint8 *buf, size_t len, SDL_bool advance);
|
||||
void (*destroy)(void *ctx);
|
||||
};
|
||||
|
||||
struct SDL_AudioQueue
|
||||
{
|
||||
SDL_AudioTrack *head;
|
||||
SDL_AudioTrack *tail;
|
||||
size_t chunk_size;
|
||||
};
|
||||
|
||||
typedef struct SDL_AudioChunk SDL_AudioChunk;
|
||||
|
||||
struct SDL_AudioChunk
|
||||
{
|
||||
SDL_AudioChunk *next;
|
||||
size_t head;
|
||||
size_t tail;
|
||||
Uint8 data[SDL_VARIABLE_LENGTH_ARRAY];
|
||||
};
|
||||
|
||||
typedef struct SDL_ChunkedAudioTrack
|
||||
{
|
||||
SDL_AudioTrack track;
|
||||
|
||||
size_t chunk_size;
|
||||
|
||||
SDL_AudioChunk *head;
|
||||
SDL_AudioChunk *tail;
|
||||
size_t queued_bytes;
|
||||
|
||||
SDL_AudioChunk *free_chunks;
|
||||
size_t num_free_chunks;
|
||||
} SDL_ChunkedAudioTrack;
|
||||
|
||||
static void DestroyAudioChunk(SDL_AudioChunk *chunk)
|
||||
{
|
||||
SDL_free(chunk);
|
||||
}
|
||||
|
||||
static void DestroyAudioChunks(SDL_AudioChunk *chunk)
|
||||
{
|
||||
while (chunk) {
|
||||
SDL_AudioChunk *next = chunk->next;
|
||||
DestroyAudioChunk(chunk);
|
||||
chunk = next;
|
||||
}
|
||||
}
|
||||
|
||||
static void ResetAudioChunk(SDL_AudioChunk *chunk)
|
||||
{
|
||||
chunk->next = NULL;
|
||||
chunk->head = 0;
|
||||
chunk->tail = 0;
|
||||
}
|
||||
|
||||
static SDL_AudioChunk *CreateAudioChunk(size_t chunk_size)
|
||||
{
|
||||
SDL_AudioChunk *chunk = (SDL_AudioChunk *)SDL_malloc(sizeof(*chunk) + chunk_size);
|
||||
|
||||
if (chunk == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ResetAudioChunk(chunk);
|
||||
|
||||
return chunk;
|
||||
}
|
||||
|
||||
static void DestroyAudioTrackChunk(SDL_ChunkedAudioTrack *track, SDL_AudioChunk *chunk)
|
||||
{
|
||||
// Keeping a list of free chunks reduces memory allocations,
|
||||
// But also increases the amount of work to perform when freeing the track.
|
||||
const size_t max_free_bytes = 64 * 1024;
|
||||
|
||||
if (track->chunk_size * track->num_free_chunks < max_free_bytes) {
|
||||
chunk->next = track->free_chunks;
|
||||
track->free_chunks = chunk;
|
||||
++track->num_free_chunks;
|
||||
} else {
|
||||
DestroyAudioChunk(chunk);
|
||||
}
|
||||
}
|
||||
|
||||
static SDL_AudioChunk *CreateAudioTrackChunk(SDL_ChunkedAudioTrack *track)
|
||||
{
|
||||
if (track->num_free_chunks > 0) {
|
||||
SDL_AudioChunk *chunk = track->free_chunks;
|
||||
|
||||
track->free_chunks = chunk->next;
|
||||
--track->num_free_chunks;
|
||||
|
||||
ResetAudioChunk(chunk);
|
||||
|
||||
return chunk;
|
||||
}
|
||||
|
||||
return CreateAudioChunk(track->chunk_size);
|
||||
}
|
||||
|
||||
static size_t AvailChunkedAudioTrack(void *ctx)
|
||||
{
|
||||
SDL_ChunkedAudioTrack *track = ctx;
|
||||
|
||||
return track->queued_bytes;
|
||||
}
|
||||
|
||||
static int WriteToChunkedAudioTrack(void *ctx, const Uint8 *data, size_t len)
|
||||
{
|
||||
SDL_ChunkedAudioTrack *track = ctx;
|
||||
|
||||
SDL_AudioChunk *chunk = track->tail;
|
||||
|
||||
// Handle the first chunk
|
||||
if (chunk == NULL) {
|
||||
chunk = CreateAudioTrackChunk(track);
|
||||
|
||||
if (chunk == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
|
||||
SDL_assert((track->head == NULL) && (track->tail == NULL) && (track->queued_bytes == 0));
|
||||
track->head = chunk;
|
||||
track->tail = chunk;
|
||||
}
|
||||
|
||||
size_t total = 0;
|
||||
size_t old_tail = chunk->tail;
|
||||
size_t chunk_size = track->chunk_size;
|
||||
|
||||
while (chunk) {
|
||||
size_t to_write = chunk_size - chunk->tail;
|
||||
to_write = SDL_min(to_write, len - total);
|
||||
SDL_memcpy(&chunk->data[chunk->tail], &data[total], to_write);
|
||||
total += to_write;
|
||||
|
||||
chunk->tail += to_write;
|
||||
|
||||
if (total == len) {
|
||||
break;
|
||||
}
|
||||
|
||||
SDL_AudioChunk *next = CreateAudioTrackChunk(track);
|
||||
chunk->next = next;
|
||||
chunk = next;
|
||||
}
|
||||
|
||||
// Roll back the changes if we couldn't write all the data
|
||||
if (chunk == NULL) {
|
||||
chunk = track->tail;
|
||||
|
||||
SDL_AudioChunk *next = chunk->next;
|
||||
chunk->next = NULL;
|
||||
chunk->tail = old_tail;
|
||||
|
||||
DestroyAudioChunks(next);
|
||||
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
|
||||
track->tail = chunk;
|
||||
track->queued_bytes += total;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static size_t ReadFromChunkedAudioTrack(void *ctx, Uint8 *data, size_t len, SDL_bool advance)
|
||||
{
|
||||
SDL_ChunkedAudioTrack *track = ctx;
|
||||
SDL_AudioChunk *chunk = track->head;
|
||||
|
||||
size_t total = 0;
|
||||
size_t head = 0;
|
||||
|
||||
while (chunk) {
|
||||
head = chunk->head;
|
||||
|
||||
size_t to_read = chunk->tail - head;
|
||||
to_read = SDL_min(to_read, len - total);
|
||||
SDL_memcpy(&data[total], &chunk->data[head], to_read);
|
||||
total += to_read;
|
||||
|
||||
SDL_AudioChunk *next = chunk->next;
|
||||
|
||||
if (total == len) {
|
||||
head += to_read;
|
||||
break;
|
||||
}
|
||||
|
||||
if (advance) {
|
||||
DestroyAudioTrackChunk(track, chunk);
|
||||
}
|
||||
|
||||
chunk = next;
|
||||
}
|
||||
|
||||
if (advance) {
|
||||
if (chunk) {
|
||||
chunk->head = head;
|
||||
track->head = chunk;
|
||||
} else {
|
||||
track->head = NULL;
|
||||
track->tail = NULL;
|
||||
}
|
||||
|
||||
track->queued_bytes -= total;
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
static void DestroyChunkedAudioTrack(void *ctx)
|
||||
{
|
||||
SDL_ChunkedAudioTrack *track = ctx;
|
||||
DestroyAudioChunks(track->head);
|
||||
DestroyAudioChunks(track->free_chunks);
|
||||
SDL_free(track);
|
||||
}
|
||||
|
||||
static SDL_AudioTrack *CreateChunkedAudioTrack(const SDL_AudioSpec *spec, size_t chunk_size)
|
||||
{
|
||||
SDL_ChunkedAudioTrack *track = (SDL_ChunkedAudioTrack *)SDL_calloc(1, sizeof(*track));
|
||||
|
||||
if (track == NULL) {
|
||||
SDL_OutOfMemory();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SDL_copyp(&track->track.spec, spec);
|
||||
track->track.avail = AvailChunkedAudioTrack;
|
||||
track->track.write = WriteToChunkedAudioTrack;
|
||||
track->track.read = ReadFromChunkedAudioTrack;
|
||||
track->track.destroy = DestroyChunkedAudioTrack;
|
||||
|
||||
track->chunk_size = chunk_size;
|
||||
|
||||
return &track->track;
|
||||
}
|
||||
|
||||
SDL_AudioQueue *SDL_CreateAudioQueue(size_t chunk_size)
|
||||
{
|
||||
SDL_AudioQueue *queue = (SDL_AudioQueue *)SDL_calloc(1, sizeof(*queue));
|
||||
|
||||
if (queue == NULL) {
|
||||
SDL_OutOfMemory();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
queue->chunk_size = chunk_size;
|
||||
|
||||
return queue;
|
||||
}
|
||||
|
||||
void SDL_DestroyAudioQueue(SDL_AudioQueue *queue)
|
||||
{
|
||||
SDL_ClearAudioQueue(queue);
|
||||
|
||||
SDL_free(queue);
|
||||
}
|
||||
|
||||
void SDL_ClearAudioQueue(SDL_AudioQueue *queue)
|
||||
{
|
||||
SDL_AudioTrack *track = queue->head;
|
||||
queue->head = NULL;
|
||||
queue->tail = NULL;
|
||||
|
||||
while (track) {
|
||||
SDL_AudioTrack *next = track->next;
|
||||
track->destroy(track);
|
||||
track = next;
|
||||
}
|
||||
}
|
||||
|
||||
static void SDL_FlushAudioTrack(SDL_AudioTrack *track)
|
||||
{
|
||||
track->flushed = SDL_TRUE;
|
||||
track->write = NULL;
|
||||
}
|
||||
|
||||
void SDL_FlushAudioQueue(SDL_AudioQueue *queue)
|
||||
{
|
||||
SDL_AudioTrack *track = queue->tail;
|
||||
|
||||
if (track) {
|
||||
SDL_FlushAudioTrack(track);
|
||||
}
|
||||
}
|
||||
|
||||
void SDL_PopAudioQueueHead(SDL_AudioQueue *queue)
|
||||
{
|
||||
SDL_AudioTrack *track = queue->head;
|
||||
|
||||
for (;;) {
|
||||
SDL_bool flushed = track->flushed;
|
||||
|
||||
SDL_AudioTrack *next = track->next;
|
||||
track->destroy(track);
|
||||
track = next;
|
||||
|
||||
if (flushed) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
queue->head = track;
|
||||
|
||||
if (track == NULL) {
|
||||
queue->tail = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
size_t SDL_GetAudioQueueChunkSize(SDL_AudioQueue *queue)
|
||||
{
|
||||
return queue->chunk_size;
|
||||
}
|
||||
|
||||
SDL_AudioTrack *SDL_CreateChunkedAudioTrack(const SDL_AudioSpec *spec, const Uint8 *data, size_t len, size_t chunk_size)
|
||||
{
|
||||
SDL_AudioTrack *track = CreateChunkedAudioTrack(spec, chunk_size);
|
||||
|
||||
if (track == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (track->write(track, data, len) != 0) {
|
||||
track->destroy(track);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return track;
|
||||
}
|
||||
|
||||
void SDL_AddTrackToAudioQueue(SDL_AudioQueue *queue, SDL_AudioTrack *track)
|
||||
{
|
||||
SDL_AudioTrack *tail = queue->tail;
|
||||
|
||||
if (tail) {
|
||||
// If the spec has changed, make sure to flush the previous track
|
||||
if (!AUDIO_SPECS_EQUAL(tail->spec, track->spec)) {
|
||||
SDL_FlushAudioTrack(tail);
|
||||
}
|
||||
|
||||
tail->next = track;
|
||||
} else {
|
||||
queue->head = track;
|
||||
}
|
||||
|
||||
queue->tail = track;
|
||||
}
|
||||
|
||||
int SDL_WriteToAudioQueue(SDL_AudioQueue *queue, const SDL_AudioSpec *spec, const Uint8 *data, size_t len)
|
||||
{
|
||||
if (len == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
SDL_AudioTrack *track = queue->tail;
|
||||
|
||||
if ((track != NULL) && !AUDIO_SPECS_EQUAL(track->spec, *spec)) {
|
||||
SDL_FlushAudioTrack(track);
|
||||
}
|
||||
|
||||
if ((track == NULL) || (track->write == NULL)) {
|
||||
SDL_AudioTrack *new_track = CreateChunkedAudioTrack(spec, queue->chunk_size);
|
||||
|
||||
if (new_track == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
|
||||
if (track) {
|
||||
track->next = new_track;
|
||||
} else {
|
||||
queue->head = new_track;
|
||||
}
|
||||
|
||||
queue->tail = new_track;
|
||||
|
||||
track = new_track;
|
||||
}
|
||||
|
||||
return track->write(track, data, len);
|
||||
}
|
||||
|
||||
void *SDL_BeginAudioQueueIter(SDL_AudioQueue *queue)
|
||||
{
|
||||
return queue->head;
|
||||
}
|
||||
|
||||
size_t SDL_NextAudioQueueIter(SDL_AudioQueue *queue, void **inout_iter, SDL_AudioSpec *out_spec, SDL_bool *out_flushed)
|
||||
{
|
||||
SDL_AudioTrack *iter = *inout_iter;
|
||||
SDL_assert(iter != NULL);
|
||||
|
||||
SDL_copyp(out_spec, &iter->spec);
|
||||
|
||||
SDL_bool flushed = SDL_FALSE;
|
||||
size_t queued_bytes = 0;
|
||||
|
||||
while (iter) {
|
||||
SDL_AudioTrack *track = iter;
|
||||
iter = iter->next;
|
||||
|
||||
size_t avail = track->avail(track);
|
||||
|
||||
if (avail >= SDL_SIZE_MAX - queued_bytes) {
|
||||
queued_bytes = SDL_SIZE_MAX;
|
||||
flushed = SDL_FALSE;
|
||||
break;
|
||||
}
|
||||
|
||||
queued_bytes += avail;
|
||||
flushed = track->flushed;
|
||||
|
||||
if (flushed) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
*inout_iter = iter;
|
||||
*out_flushed = flushed;
|
||||
|
||||
return queued_bytes;
|
||||
}
|
||||
|
||||
int SDL_ReadFromAudioQueue(SDL_AudioQueue *queue, Uint8 *data, size_t len)
|
||||
{
|
||||
size_t total = 0;
|
||||
SDL_AudioTrack *track = queue->head;
|
||||
|
||||
for (;;) {
|
||||
if (track == NULL) {
|
||||
return SDL_SetError("Reading past end of queue");
|
||||
}
|
||||
|
||||
total += track->read(track, &data[total], len - total, SDL_TRUE);
|
||||
|
||||
if (total == len) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (track->flushed) {
|
||||
return SDL_SetError("Reading past end of flushed track");
|
||||
}
|
||||
|
||||
SDL_AudioTrack *next = track->next;
|
||||
|
||||
if (next == NULL) {
|
||||
return SDL_SetError("Reading past end of incomplete track");
|
||||
}
|
||||
|
||||
queue->head = next;
|
||||
|
||||
track->destroy(track);
|
||||
track = next;
|
||||
}
|
||||
}
|
||||
|
||||
int SDL_PeekIntoAudioQueue(SDL_AudioQueue *queue, Uint8 *data, size_t len)
|
||||
{
|
||||
size_t total = 0;
|
||||
SDL_AudioTrack *track = queue->head;
|
||||
|
||||
for (;;) {
|
||||
if (track == NULL) {
|
||||
return SDL_SetError("Peeking past end of queue");
|
||||
}
|
||||
|
||||
total += track->read(track, &data[total], len - total, SDL_FALSE);
|
||||
|
||||
if (total == len) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (track->flushed) {
|
||||
// If we have run out of data, fill the rest with silence.
|
||||
SDL_memset(&data[total], SDL_GetSilenceValueForFormat(track->spec.format), len - total);
|
||||
return 0;
|
||||
}
|
||||
|
||||
track = track->next;
|
||||
}
|
||||
}
|
||||
77
vendor/sdl-3.0.0/src/audio/SDL_audioqueue.h
vendored
Normal file
77
vendor/sdl-3.0.0/src/audio/SDL_audioqueue.h
vendored
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifndef SDL_audioqueue_h_
|
||||
#define SDL_audioqueue_h_
|
||||
|
||||
// Internal functions used by SDL_AudioStream for queueing audio.
|
||||
|
||||
typedef struct SDL_AudioQueue SDL_AudioQueue;
|
||||
typedef struct SDL_AudioTrack SDL_AudioTrack;
|
||||
|
||||
// Create a new audio queue
|
||||
SDL_AudioQueue *SDL_CreateAudioQueue(size_t chunk_size);
|
||||
|
||||
// Destroy an audio queue
|
||||
void SDL_DestroyAudioQueue(SDL_AudioQueue *queue);
|
||||
|
||||
// Completely clear the queue
|
||||
void SDL_ClearAudioQueue(SDL_AudioQueue *queue);
|
||||
|
||||
// Mark the last track as flushed
|
||||
void SDL_FlushAudioQueue(SDL_AudioQueue *queue);
|
||||
|
||||
// Pop the current head track
|
||||
// REQUIRES: The head track must exist, and must have been flushed
|
||||
void SDL_PopAudioQueueHead(SDL_AudioQueue *queue);
|
||||
|
||||
// Get the chunk size, mostly for use with SDL_CreateChunkedAudioTrack
|
||||
// This can be called from any thread
|
||||
size_t SDL_GetAudioQueueChunkSize(SDL_AudioQueue *queue);
|
||||
|
||||
// Write data to the end of queue
|
||||
// REQUIRES: If the spec has changed, the last track must have been flushed
|
||||
int SDL_WriteToAudioQueue(SDL_AudioQueue *queue, const SDL_AudioSpec *spec, const Uint8 *data, size_t len);
|
||||
|
||||
// Create a track without needing to hold any locks
|
||||
SDL_AudioTrack *SDL_CreateChunkedAudioTrack(const SDL_AudioSpec *spec, const Uint8 *data, size_t len, size_t chunk_size);
|
||||
|
||||
// Add a track to the end of the queue
|
||||
// REQUIRES: `track != NULL`
|
||||
void SDL_AddTrackToAudioQueue(SDL_AudioQueue *queue, SDL_AudioTrack *track);
|
||||
|
||||
// Iterate over the tracks in the queue
|
||||
void *SDL_BeginAudioQueueIter(SDL_AudioQueue *queue);
|
||||
|
||||
// Query and update the track iterator
|
||||
// REQUIRES: `*inout_iter != NULL` (a valid iterator)
|
||||
size_t SDL_NextAudioQueueIter(SDL_AudioQueue *queue, void **inout_iter, SDL_AudioSpec *out_spec, SDL_bool *out_flushed);
|
||||
|
||||
// Read data from the start of the queue
|
||||
// REQUIRES: There must be enough data in the queue
|
||||
int SDL_ReadFromAudioQueue(SDL_AudioQueue *queue, Uint8 *data, size_t len);
|
||||
|
||||
// Peek into the start of the queue
|
||||
// REQUIRES: There must be enough data in the queue, unless it has been flushed, in which case missing data is filled with silence.
|
||||
int SDL_PeekIntoAudioQueue(SDL_AudioQueue *queue, Uint8 *data, size_t len);
|
||||
|
||||
#endif // SDL_audioqueue_h_
|
||||
335
vendor/sdl-3.0.0/src/audio/SDL_audioresample.c
vendored
Normal file
335
vendor/sdl-3.0.0/src/audio/SDL_audioresample.c
vendored
Normal file
|
|
@ -0,0 +1,335 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#include "SDL_sysaudio.h"
|
||||
#include "SDL_audioresample.h"
|
||||
|
||||
// SDL's resampler uses a "bandlimited interpolation" algorithm:
|
||||
// https://ccrma.stanford.edu/~jos/resample/
|
||||
|
||||
#include "SDL_audio_resampler_filter.h"
|
||||
|
||||
// For a given srcpos, `srcpos + frame` are sampled, where `-RESAMPLER_ZERO_CROSSINGS < frame <= RESAMPLER_ZERO_CROSSINGS`.
|
||||
// Note, when upsampling, it is also possible to start sampling from `srcpos = -1`.
|
||||
#define RESAMPLER_MAX_PADDING_FRAMES (RESAMPLER_ZERO_CROSSINGS + 1)
|
||||
|
||||
#define RESAMPLER_FILTER_INTERP_BITS (32 - RESAMPLER_BITS_PER_ZERO_CROSSING)
|
||||
#define RESAMPLER_FILTER_INTERP_RANGE (1 << RESAMPLER_FILTER_INTERP_BITS)
|
||||
|
||||
#define RESAMPLER_SAMPLES_PER_FRAME (RESAMPLER_ZERO_CROSSINGS * 2)
|
||||
|
||||
#define RESAMPLER_FULL_FILTER_SIZE (RESAMPLER_SAMPLES_PER_FRAME * (RESAMPLER_SAMPLES_PER_ZERO_CROSSING + 1))
|
||||
|
||||
static void ResampleFrame_Scalar(const float *src, float *dst, const float *raw_filter, float interp, int chans)
|
||||
{
|
||||
int i, chan;
|
||||
|
||||
float filter[RESAMPLER_SAMPLES_PER_FRAME];
|
||||
|
||||
// Interpolate between the nearest two filters
|
||||
for (i = 0; i < RESAMPLER_SAMPLES_PER_FRAME; i++) {
|
||||
filter[i] = (raw_filter[i] * (1.0f - interp)) + (raw_filter[i + RESAMPLER_SAMPLES_PER_FRAME] * interp);
|
||||
}
|
||||
|
||||
if (chans == 2) {
|
||||
float out[2];
|
||||
out[0] = 0.0f;
|
||||
out[1] = 0.0f;
|
||||
|
||||
for (i = 0; i < RESAMPLER_SAMPLES_PER_FRAME; i++) {
|
||||
const float scale = filter[i];
|
||||
out[0] += src[i * 2 + 0] * scale;
|
||||
out[1] += src[i * 2 + 1] * scale;
|
||||
}
|
||||
|
||||
dst[0] = out[0];
|
||||
dst[1] = out[1];
|
||||
return;
|
||||
}
|
||||
|
||||
if (chans == 1) {
|
||||
float out = 0.0f;
|
||||
|
||||
for (i = 0; i < RESAMPLER_SAMPLES_PER_FRAME; i++) {
|
||||
out += src[i] * filter[i];
|
||||
}
|
||||
|
||||
dst[0] = out;
|
||||
return;
|
||||
}
|
||||
|
||||
for (chan = 0; chan < chans; chan++) {
|
||||
float f = 0.0f;
|
||||
|
||||
for (i = 0; i < RESAMPLER_SAMPLES_PER_FRAME; i++) {
|
||||
f += src[i * chans + chan] * filter[i];
|
||||
}
|
||||
|
||||
dst[chan] = f;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef SDL_SSE_INTRINSICS
|
||||
static void SDL_TARGETING("sse") ResampleFrame_SSE(const float *src, float *dst, const float *raw_filter, float interp, int chans)
|
||||
{
|
||||
#if RESAMPLER_SAMPLES_PER_FRAME != 10
|
||||
#error Invalid samples per frame
|
||||
#endif
|
||||
|
||||
// Load the filter
|
||||
__m128 f0 = _mm_loadu_ps(raw_filter + 0);
|
||||
__m128 f1 = _mm_loadu_ps(raw_filter + 4);
|
||||
__m128 f2 = _mm_loadl_pi(_mm_setzero_ps(), (const __m64 *)(raw_filter + 8));
|
||||
|
||||
__m128 g0 = _mm_loadu_ps(raw_filter + 10);
|
||||
__m128 g1 = _mm_loadu_ps(raw_filter + 14);
|
||||
__m128 g2 = _mm_loadl_pi(_mm_setzero_ps(), (const __m64 *)(raw_filter + 18));
|
||||
|
||||
__m128 interp1 = _mm_set1_ps(interp);
|
||||
__m128 interp2 = _mm_sub_ps(_mm_set1_ps(1.0f), _mm_set1_ps(interp));
|
||||
|
||||
// Linear interpolate the filter
|
||||
f0 = _mm_add_ps(_mm_mul_ps(f0, interp2), _mm_mul_ps(g0, interp1));
|
||||
f1 = _mm_add_ps(_mm_mul_ps(f1, interp2), _mm_mul_ps(g1, interp1));
|
||||
f2 = _mm_add_ps(_mm_mul_ps(f2, interp2), _mm_mul_ps(g2, interp1));
|
||||
|
||||
if (chans == 2) {
|
||||
// Duplicate each of the filter elements
|
||||
g0 = _mm_unpackhi_ps(f0, f0);
|
||||
f0 = _mm_unpacklo_ps(f0, f0);
|
||||
g1 = _mm_unpackhi_ps(f1, f1);
|
||||
f1 = _mm_unpacklo_ps(f1, f1);
|
||||
f2 = _mm_unpacklo_ps(f2, f2);
|
||||
|
||||
// Multiply the filter by the input
|
||||
f0 = _mm_mul_ps(f0, _mm_loadu_ps(src + 0));
|
||||
g0 = _mm_mul_ps(g0, _mm_loadu_ps(src + 4));
|
||||
f1 = _mm_mul_ps(f1, _mm_loadu_ps(src + 8));
|
||||
g1 = _mm_mul_ps(g1, _mm_loadu_ps(src + 12));
|
||||
f2 = _mm_mul_ps(f2, _mm_loadu_ps(src + 16));
|
||||
|
||||
// Calculate the sum
|
||||
f0 = _mm_add_ps(_mm_add_ps(_mm_add_ps(f0, g0), _mm_add_ps(f1, g1)), f2);
|
||||
f0 = _mm_add_ps(f0, _mm_movehl_ps(f0, f0));
|
||||
|
||||
// Store the result
|
||||
_mm_storel_pi((__m64 *)dst, f0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (chans == 1) {
|
||||
// Multiply the filter by the input
|
||||
f0 = _mm_mul_ps(f0, _mm_loadu_ps(src + 0));
|
||||
f1 = _mm_mul_ps(f1, _mm_loadu_ps(src + 4));
|
||||
f2 = _mm_mul_ps(f2, _mm_loadl_pi(_mm_setzero_ps(), (const __m64 *)(src + 8)));
|
||||
|
||||
// Calculate the sum
|
||||
f0 = _mm_add_ps(f0, f1);
|
||||
f0 = _mm_add_ps(_mm_add_ps(f0, f2), _mm_movehl_ps(f0, f0));
|
||||
f0 = _mm_add_ss(f0, _mm_shuffle_ps(f0, f0, _MM_SHUFFLE(1, 1, 1, 1)));
|
||||
|
||||
// Store the result
|
||||
_mm_store_ss(dst, f0);
|
||||
return;
|
||||
}
|
||||
|
||||
float filter[RESAMPLER_SAMPLES_PER_FRAME];
|
||||
_mm_storeu_ps(filter + 0, f0);
|
||||
_mm_storeu_ps(filter + 4, f1);
|
||||
_mm_storel_pi((__m64 *)(filter + 8), f2);
|
||||
|
||||
int i, chan = 0;
|
||||
|
||||
for (; chan + 4 <= chans; chan += 4) {
|
||||
f0 = _mm_setzero_ps();
|
||||
|
||||
for (i = 0; i < RESAMPLER_SAMPLES_PER_FRAME; i++) {
|
||||
f0 = _mm_add_ps(f0, _mm_mul_ps(_mm_loadu_ps(&src[i * chans + chan]), _mm_load1_ps(&filter[i])));
|
||||
}
|
||||
|
||||
_mm_storeu_ps(&dst[chan], f0);
|
||||
}
|
||||
|
||||
for (; chan < chans; chan++) {
|
||||
f0 = _mm_setzero_ps();
|
||||
|
||||
for (i = 0; i < RESAMPLER_SAMPLES_PER_FRAME; i++) {
|
||||
f0 = _mm_add_ss(f0, _mm_mul_ss(_mm_load_ss(&src[i * chans + chan]), _mm_load_ss(&filter[i])));
|
||||
}
|
||||
|
||||
_mm_store_ss(&dst[chan], f0);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void (*ResampleFrame)(const float *src, float *dst, const float *raw_filter, float interp, int chans);
|
||||
|
||||
static float FullResamplerFilter[RESAMPLER_FULL_FILTER_SIZE];
|
||||
|
||||
void SDL_SetupAudioResampler(void)
|
||||
{
|
||||
static SDL_bool setup = SDL_FALSE;
|
||||
if (setup) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Build a table combining the left and right wings, for faster access
|
||||
int i, j;
|
||||
|
||||
for (i = 0; i < RESAMPLER_SAMPLES_PER_ZERO_CROSSING; ++i) {
|
||||
for (j = 0; j < RESAMPLER_ZERO_CROSSINGS; j++) {
|
||||
int lwing = (i * RESAMPLER_SAMPLES_PER_FRAME) + (RESAMPLER_ZERO_CROSSINGS - 1) - j;
|
||||
int rwing = (RESAMPLER_FULL_FILTER_SIZE - 1) - lwing;
|
||||
|
||||
float value = ResamplerFilter[(i * RESAMPLER_ZERO_CROSSINGS) + j];
|
||||
FullResamplerFilter[lwing] = value;
|
||||
FullResamplerFilter[rwing] = value;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < RESAMPLER_ZERO_CROSSINGS; ++i) {
|
||||
int rwing = i + RESAMPLER_ZERO_CROSSINGS;
|
||||
int lwing = (RESAMPLER_FULL_FILTER_SIZE - 1) - rwing;
|
||||
|
||||
FullResamplerFilter[lwing] = 0.0f;
|
||||
FullResamplerFilter[rwing] = 0.0f;
|
||||
}
|
||||
|
||||
ResampleFrame = ResampleFrame_Scalar;
|
||||
|
||||
#ifdef SDL_SSE_INTRINSICS
|
||||
if (SDL_HasSSE()) {
|
||||
ResampleFrame = ResampleFrame_SSE;
|
||||
}
|
||||
#endif
|
||||
|
||||
setup = SDL_TRUE;
|
||||
}
|
||||
|
||||
Sint64 SDL_GetResampleRate(int src_rate, int dst_rate)
|
||||
{
|
||||
SDL_assert(src_rate > 0);
|
||||
SDL_assert(dst_rate > 0);
|
||||
|
||||
Sint64 sample_rate = ((Sint64)src_rate << 32) / (Sint64)dst_rate;
|
||||
SDL_assert(sample_rate > 0);
|
||||
|
||||
return sample_rate;
|
||||
}
|
||||
|
||||
int SDL_GetResamplerHistoryFrames(void)
|
||||
{
|
||||
// Even if we aren't currently resampling, make sure to keep enough history in case we need to later.
|
||||
|
||||
return RESAMPLER_MAX_PADDING_FRAMES;
|
||||
}
|
||||
|
||||
int SDL_GetResamplerPaddingFrames(Sint64 resample_rate)
|
||||
{
|
||||
// This must always be <= SDL_GetResamplerHistoryFrames()
|
||||
|
||||
return resample_rate ? RESAMPLER_MAX_PADDING_FRAMES : 0;
|
||||
}
|
||||
|
||||
// These are not general purpose. They do not check for all possible underflow/overflow
|
||||
SDL_FORCE_INLINE Sint64 ResamplerAdd(Sint64 a, Sint64 b, Sint64 *ret)
|
||||
{
|
||||
if ((b > 0) && (a > SDL_MAX_SINT64 - b)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
*ret = a + b;
|
||||
return 0;
|
||||
}
|
||||
|
||||
SDL_FORCE_INLINE Sint64 ResamplerMul(Sint64 a, Sint64 b, Sint64 *ret)
|
||||
{
|
||||
if ((b > 0) && (a > SDL_MAX_SINT64 / b)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
*ret = a * b;
|
||||
return 0;
|
||||
}
|
||||
|
||||
Sint64 SDL_GetResamplerInputFrames(Sint64 output_frames, Sint64 resample_rate, Sint64 resample_offset)
|
||||
{
|
||||
// Calculate the index of the last input frame, then add 1.
|
||||
// ((((output_frames - 1) * resample_rate) + resample_offset) >> 32) + 1
|
||||
|
||||
Sint64 output_offset;
|
||||
if (ResamplerMul(output_frames, resample_rate, &output_offset) ||
|
||||
ResamplerAdd(output_offset, -resample_rate + resample_offset + 0x100000000, &output_offset)) {
|
||||
output_offset = SDL_MAX_SINT64;
|
||||
}
|
||||
|
||||
Sint64 input_frames = (Sint64)(Sint32)(output_offset >> 32);
|
||||
input_frames = SDL_max(input_frames, 0);
|
||||
|
||||
return input_frames;
|
||||
}
|
||||
|
||||
Sint64 SDL_GetResamplerOutputFrames(Sint64 input_frames, Sint64 resample_rate, Sint64 *inout_resample_offset)
|
||||
{
|
||||
Sint64 resample_offset = *inout_resample_offset;
|
||||
|
||||
// input_offset = (input_frames << 32) - resample_offset;
|
||||
Sint64 input_offset;
|
||||
if (ResamplerMul(input_frames, 0x100000000, &input_offset) ||
|
||||
ResamplerAdd(input_offset, -resample_offset, &input_offset)) {
|
||||
input_offset = SDL_MAX_SINT64;
|
||||
}
|
||||
|
||||
// output_frames = div_ceil(input_offset, resample_rate)
|
||||
Sint64 output_frames = (input_offset > 0) ? (((input_offset - 1) / resample_rate) + 1) : 0;
|
||||
|
||||
*inout_resample_offset = (output_frames * resample_rate) - input_offset;
|
||||
|
||||
return output_frames;
|
||||
}
|
||||
|
||||
void SDL_ResampleAudio(int chans, const float *src, int inframes, float *dst, int outframes,
|
||||
Sint64 resample_rate, Sint64 *inout_resample_offset)
|
||||
{
|
||||
int i;
|
||||
Sint64 srcpos = *inout_resample_offset;
|
||||
|
||||
SDL_assert(resample_rate > 0);
|
||||
|
||||
for (i = 0; i < outframes; i++) {
|
||||
int srcindex = (int)(Sint32)(srcpos >> 32);
|
||||
Uint32 srcfraction = (Uint32)(srcpos & 0xFFFFFFFF);
|
||||
srcpos += resample_rate;
|
||||
|
||||
SDL_assert(srcindex >= -1 && srcindex < inframes);
|
||||
|
||||
const float *filter = &FullResamplerFilter[(srcfraction >> RESAMPLER_FILTER_INTERP_BITS) * RESAMPLER_SAMPLES_PER_FRAME];
|
||||
const float interp = (float)(srcfraction & (RESAMPLER_FILTER_INTERP_RANGE - 1)) * (1.0f / RESAMPLER_FILTER_INTERP_RANGE);
|
||||
|
||||
const float *frame = &src[(srcindex - (RESAMPLER_ZERO_CROSSINGS - 1)) * chans];
|
||||
ResampleFrame(frame, dst, filter, interp, chans);
|
||||
|
||||
dst += chans;
|
||||
}
|
||||
|
||||
*inout_resample_offset = srcpos - ((Sint64)inframes << 32);
|
||||
}
|
||||
43
vendor/sdl-3.0.0/src/audio/SDL_audioresample.h
vendored
Normal file
43
vendor/sdl-3.0.0/src/audio/SDL_audioresample.h
vendored
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifndef SDL_audioresample_h_
|
||||
#define SDL_audioresample_h_
|
||||
|
||||
// Internal functions used by SDL_AudioStream for resampling audio.
|
||||
// The resampler uses 32:32 fixed-point arithmetic to track its position.
|
||||
|
||||
Sint64 SDL_GetResampleRate(int src_rate, int dst_rate);
|
||||
|
||||
int SDL_GetResamplerHistoryFrames(void);
|
||||
int SDL_GetResamplerPaddingFrames(Sint64 resample_rate);
|
||||
|
||||
Sint64 SDL_GetResamplerInputFrames(Sint64 output_frames, Sint64 resample_rate, Sint64 resample_offset);
|
||||
Sint64 SDL_GetResamplerOutputFrames(Sint64 input_frames, Sint64 resample_rate, Sint64 *inout_resample_offset);
|
||||
|
||||
// Resample some audio.
|
||||
// REQUIRES: `inframes >= SDL_GetResamplerInputFrames(outframes)`
|
||||
// REQUIRES: At least `SDL_GetResamplerPaddingFrames(...)` extra frames to the left of src, and right of src+inframes
|
||||
void SDL_ResampleAudio(int chans, const float *src, int inframes, float *dst, int outframes,
|
||||
Sint64 resample_rate, Sint64 *inout_resample_offset);
|
||||
|
||||
#endif // SDL_audioresample_h_
|
||||
1009
vendor/sdl-3.0.0/src/audio/SDL_audiotypecvt.c
vendored
Normal file
1009
vendor/sdl-3.0.0/src/audio/SDL_audiotypecvt.c
vendored
Normal file
File diff suppressed because it is too large
Load diff
293
vendor/sdl-3.0.0/src/audio/SDL_mixer.c
vendored
Normal file
293
vendor/sdl-3.0.0/src/audio/SDL_mixer.c
vendored
Normal file
|
|
@ -0,0 +1,293 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
// This provides the default mixing callback for the SDL audio routines
|
||||
|
||||
#include "SDL_sysaudio.h"
|
||||
|
||||
/* This table is used to add two sound values together and pin
|
||||
* the value to avoid overflow. (used with permission from ARDI)
|
||||
*/
|
||||
static const Uint8 mix8[] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03,
|
||||
0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
|
||||
0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19,
|
||||
0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24,
|
||||
0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
|
||||
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A,
|
||||
0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45,
|
||||
0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50,
|
||||
0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B,
|
||||
0x5C, 0x5D, 0x5E, 0x5F, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66,
|
||||
0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71,
|
||||
0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C,
|
||||
0x7D, 0x7E, 0x7F, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
|
||||
0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, 0x90, 0x91, 0x92,
|
||||
0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D,
|
||||
0x9E, 0x9F, 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8,
|
||||
0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB0, 0xB1, 0xB2, 0xB3,
|
||||
0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE,
|
||||
0xBF, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9,
|
||||
0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, 0xD0, 0xD1, 0xD2, 0xD3, 0xD4,
|
||||
0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF,
|
||||
0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA,
|
||||
0xEB, 0xEC, 0xED, 0xEE, 0xEF, 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5,
|
||||
0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
|
||||
};
|
||||
|
||||
// The volume ranges from 0 - 128
|
||||
#define ADJUST_VOLUME(type, s, v) ((s) = (type)(((s) * (v)) / SDL_MIX_MAXVOLUME))
|
||||
#define ADJUST_VOLUME_U8(s, v) ((s) = (Uint8)(((((s) - 128) * (v)) / SDL_MIX_MAXVOLUME) + 128))
|
||||
|
||||
|
||||
// !!! FIXME: this needs some SIMD magic.
|
||||
|
||||
int SDL_MixAudioFormat(Uint8 *dst, const Uint8 *src, SDL_AudioFormat format,
|
||||
Uint32 len, int volume)
|
||||
{
|
||||
if (volume == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (format) {
|
||||
|
||||
case SDL_AUDIO_U8:
|
||||
{
|
||||
Uint8 src_sample;
|
||||
|
||||
while (len--) {
|
||||
src_sample = *src;
|
||||
ADJUST_VOLUME_U8(src_sample, volume);
|
||||
*dst = mix8[*dst + src_sample];
|
||||
++dst;
|
||||
++src;
|
||||
}
|
||||
} break;
|
||||
|
||||
case SDL_AUDIO_S8:
|
||||
{
|
||||
Sint8 *dst8, *src8;
|
||||
Sint8 src_sample;
|
||||
int dst_sample;
|
||||
const int max_audioval = SDL_MAX_SINT8;
|
||||
const int min_audioval = SDL_MIN_SINT8;
|
||||
|
||||
src8 = (Sint8 *)src;
|
||||
dst8 = (Sint8 *)dst;
|
||||
while (len--) {
|
||||
src_sample = *src8;
|
||||
ADJUST_VOLUME(Sint8, src_sample, volume);
|
||||
dst_sample = *dst8 + src_sample;
|
||||
if (dst_sample > max_audioval) {
|
||||
dst_sample = max_audioval;
|
||||
} else if (dst_sample < min_audioval) {
|
||||
dst_sample = min_audioval;
|
||||
}
|
||||
*dst8 = (Sint8)dst_sample;
|
||||
++dst8;
|
||||
++src8;
|
||||
}
|
||||
} break;
|
||||
|
||||
case SDL_AUDIO_S16LE:
|
||||
{
|
||||
Sint16 src1, src2;
|
||||
int dst_sample;
|
||||
const int max_audioval = SDL_MAX_SINT16;
|
||||
const int min_audioval = SDL_MIN_SINT16;
|
||||
|
||||
len /= 2;
|
||||
while (len--) {
|
||||
src1 = SDL_SwapLE16(*(Sint16 *)src);
|
||||
ADJUST_VOLUME(Sint16, src1, volume);
|
||||
src2 = SDL_SwapLE16(*(Sint16 *)dst);
|
||||
src += 2;
|
||||
dst_sample = src1 + src2;
|
||||
if (dst_sample > max_audioval) {
|
||||
dst_sample = max_audioval;
|
||||
} else if (dst_sample < min_audioval) {
|
||||
dst_sample = min_audioval;
|
||||
}
|
||||
*(Sint16 *)dst = SDL_SwapLE16((Sint16)dst_sample);
|
||||
dst += 2;
|
||||
}
|
||||
} break;
|
||||
|
||||
case SDL_AUDIO_S16BE:
|
||||
{
|
||||
Sint16 src1, src2;
|
||||
int dst_sample;
|
||||
const int max_audioval = SDL_MAX_SINT16;
|
||||
const int min_audioval = SDL_MIN_SINT16;
|
||||
|
||||
len /= 2;
|
||||
while (len--) {
|
||||
src1 = SDL_SwapBE16(*(Sint16 *)src);
|
||||
ADJUST_VOLUME(Sint16, src1, volume);
|
||||
src2 = SDL_SwapBE16(*(Sint16 *)dst);
|
||||
src += 2;
|
||||
dst_sample = src1 + src2;
|
||||
if (dst_sample > max_audioval) {
|
||||
dst_sample = max_audioval;
|
||||
} else if (dst_sample < min_audioval) {
|
||||
dst_sample = min_audioval;
|
||||
}
|
||||
*(Sint16 *)dst = SDL_SwapBE16((Sint16)dst_sample);
|
||||
dst += 2;
|
||||
}
|
||||
} break;
|
||||
|
||||
case SDL_AUDIO_S32LE:
|
||||
{
|
||||
const Uint32 *src32 = (Uint32 *)src;
|
||||
Uint32 *dst32 = (Uint32 *)dst;
|
||||
Sint64 src1, src2;
|
||||
Sint64 dst_sample;
|
||||
const Sint64 max_audioval = SDL_MAX_SINT32;
|
||||
const Sint64 min_audioval = SDL_MIN_SINT32;
|
||||
|
||||
len /= 4;
|
||||
while (len--) {
|
||||
src1 = (Sint64)((Sint32)SDL_SwapLE32(*src32));
|
||||
src32++;
|
||||
ADJUST_VOLUME(Sint64, src1, volume);
|
||||
src2 = (Sint64)((Sint32)SDL_SwapLE32(*dst32));
|
||||
dst_sample = src1 + src2;
|
||||
if (dst_sample > max_audioval) {
|
||||
dst_sample = max_audioval;
|
||||
} else if (dst_sample < min_audioval) {
|
||||
dst_sample = min_audioval;
|
||||
}
|
||||
*(dst32++) = SDL_SwapLE32((Uint32)((Sint32)dst_sample));
|
||||
}
|
||||
} break;
|
||||
|
||||
case SDL_AUDIO_S32BE:
|
||||
{
|
||||
const Uint32 *src32 = (Uint32 *)src;
|
||||
Uint32 *dst32 = (Uint32 *)dst;
|
||||
Sint64 src1, src2;
|
||||
Sint64 dst_sample;
|
||||
const Sint64 max_audioval = SDL_MAX_SINT32;
|
||||
const Sint64 min_audioval = SDL_MIN_SINT32;
|
||||
|
||||
len /= 4;
|
||||
while (len--) {
|
||||
src1 = (Sint64)((Sint32)SDL_SwapBE32(*src32));
|
||||
src32++;
|
||||
ADJUST_VOLUME(Sint64, src1, volume);
|
||||
src2 = (Sint64)((Sint32)SDL_SwapBE32(*dst32));
|
||||
dst_sample = src1 + src2;
|
||||
if (dst_sample > max_audioval) {
|
||||
dst_sample = max_audioval;
|
||||
} else if (dst_sample < min_audioval) {
|
||||
dst_sample = min_audioval;
|
||||
}
|
||||
*(dst32++) = SDL_SwapBE32((Uint32)((Sint32)dst_sample));
|
||||
}
|
||||
} break;
|
||||
|
||||
case SDL_AUDIO_F32LE:
|
||||
{
|
||||
const float fmaxvolume = 1.0f / ((float)SDL_MIX_MAXVOLUME);
|
||||
const float fvolume = (float)volume;
|
||||
const float *src32 = (float *)src;
|
||||
float *dst32 = (float *)dst;
|
||||
float src1, src2;
|
||||
double dst_sample;
|
||||
// !!! FIXME: are these right?
|
||||
const double max_audioval = 3.402823466e+38F;
|
||||
const double min_audioval = -3.402823466e+38F;
|
||||
|
||||
len /= 4;
|
||||
while (len--) {
|
||||
src1 = ((SDL_SwapFloatLE(*src32) * fvolume) * fmaxvolume);
|
||||
src2 = SDL_SwapFloatLE(*dst32);
|
||||
src32++;
|
||||
|
||||
dst_sample = ((double)src1) + ((double)src2);
|
||||
if (dst_sample > max_audioval) {
|
||||
dst_sample = max_audioval;
|
||||
} else if (dst_sample < min_audioval) {
|
||||
dst_sample = min_audioval;
|
||||
}
|
||||
*(dst32++) = SDL_SwapFloatLE((float)dst_sample);
|
||||
}
|
||||
} break;
|
||||
|
||||
case SDL_AUDIO_F32BE:
|
||||
{
|
||||
const float fmaxvolume = 1.0f / ((float)SDL_MIX_MAXVOLUME);
|
||||
const float fvolume = (float)volume;
|
||||
const float *src32 = (float *)src;
|
||||
float *dst32 = (float *)dst;
|
||||
float src1, src2;
|
||||
double dst_sample;
|
||||
// !!! FIXME: are these right?
|
||||
const double max_audioval = 3.402823466e+38F;
|
||||
const double min_audioval = -3.402823466e+38F;
|
||||
|
||||
len /= 4;
|
||||
while (len--) {
|
||||
src1 = ((SDL_SwapFloatBE(*src32) * fvolume) * fmaxvolume);
|
||||
src2 = SDL_SwapFloatBE(*dst32);
|
||||
src32++;
|
||||
|
||||
dst_sample = ((double)src1) + ((double)src2);
|
||||
if (dst_sample > max_audioval) {
|
||||
dst_sample = max_audioval;
|
||||
} else if (dst_sample < min_audioval) {
|
||||
dst_sample = min_audioval;
|
||||
}
|
||||
*(dst32++) = SDL_SwapFloatBE((float)dst_sample);
|
||||
}
|
||||
} break;
|
||||
|
||||
default: // If this happens... FIXME!
|
||||
return SDL_SetError("SDL_MixAudioFormat(): unknown audio format");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
360
vendor/sdl-3.0.0/src/audio/SDL_sysaudio.h
vendored
Normal file
360
vendor/sdl-3.0.0/src/audio/SDL_sysaudio.h
vendored
Normal file
|
|
@ -0,0 +1,360 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifndef SDL_sysaudio_h_
|
||||
#define SDL_sysaudio_h_
|
||||
|
||||
#include "../SDL_hashtable.h"
|
||||
|
||||
#define DEBUG_AUDIOSTREAM 0
|
||||
#define DEBUG_AUDIO_CONVERT 0
|
||||
|
||||
#if DEBUG_AUDIO_CONVERT
|
||||
#define LOG_DEBUG_AUDIO_CONVERT(from, to) SDL_Log("SDL_AUDIO_CONVERT: Converting %s to %s.\n", from, to);
|
||||
#else
|
||||
#define LOG_DEBUG_AUDIO_CONVERT(from, to)
|
||||
#endif
|
||||
|
||||
// These pointers get set during SDL_ChooseAudioConverters() to various SIMD implementations.
|
||||
extern void (*SDL_Convert_S8_to_F32)(float *dst, const Sint8 *src, int num_samples);
|
||||
extern void (*SDL_Convert_U8_to_F32)(float *dst, const Uint8 *src, int num_samples);
|
||||
extern void (*SDL_Convert_S16_to_F32)(float *dst, const Sint16 *src, int num_samples);
|
||||
extern void (*SDL_Convert_S32_to_F32)(float *dst, const Sint32 *src, int num_samples);
|
||||
extern void (*SDL_Convert_F32_to_S8)(Sint8 *dst, const float *src, int num_samples);
|
||||
extern void (*SDL_Convert_F32_to_U8)(Uint8 *dst, const float *src, int num_samples);
|
||||
extern void (*SDL_Convert_F32_to_S16)(Sint16 *dst, const float *src, int num_samples);
|
||||
extern void (*SDL_Convert_F32_to_S32)(Sint32 *dst, const float *src, int num_samples);
|
||||
|
||||
// !!! FIXME: These are wordy and unlocalized...
|
||||
#define DEFAULT_OUTPUT_DEVNAME "System audio output device"
|
||||
#define DEFAULT_INPUT_DEVNAME "System audio capture device"
|
||||
|
||||
// these are used when no better specifics are known. We default to CD audio quality.
|
||||
#define DEFAULT_AUDIO_OUTPUT_FORMAT SDL_AUDIO_S16
|
||||
#define DEFAULT_AUDIO_OUTPUT_CHANNELS 2
|
||||
#define DEFAULT_AUDIO_OUTPUT_FREQUENCY 44100
|
||||
|
||||
#define DEFAULT_AUDIO_CAPTURE_FORMAT SDL_AUDIO_S16
|
||||
#define DEFAULT_AUDIO_CAPTURE_CHANNELS 1
|
||||
#define DEFAULT_AUDIO_CAPTURE_FREQUENCY 44100
|
||||
|
||||
#define AUDIO_SPECS_EQUAL(x, y) (((x).format == (y).format) && ((x).channels == (y).channels) && ((x).freq == (y).freq))
|
||||
|
||||
typedef struct SDL_AudioDevice SDL_AudioDevice;
|
||||
typedef struct SDL_LogicalAudioDevice SDL_LogicalAudioDevice;
|
||||
|
||||
// Used by src/SDL.c to initialize a particular audio driver.
|
||||
extern int SDL_InitAudio(const char *driver_name);
|
||||
|
||||
// Used by src/SDL.c to shut down previously-initialized audio.
|
||||
extern void SDL_QuitAudio(void);
|
||||
|
||||
// Function to get a list of audio formats, ordered most similar to `format` to least, 0-terminated. Don't free results.
|
||||
const SDL_AudioFormat *SDL_ClosestAudioFormats(SDL_AudioFormat format);
|
||||
|
||||
// Must be called at least once before using converters.
|
||||
extern void SDL_ChooseAudioConverters(void);
|
||||
extern void SDL_SetupAudioResampler(void);
|
||||
|
||||
/* Backends should call this as devices are added to the system (such as
|
||||
a USB headset being plugged in), and should also be called for
|
||||
for every device found during DetectDevices(). */
|
||||
extern SDL_AudioDevice *SDL_AddAudioDevice(const SDL_bool iscapture, const char *name, const SDL_AudioSpec *spec, void *handle);
|
||||
|
||||
/* Backends should call this if an opened audio device is lost.
|
||||
This can happen due to i/o errors, or a device being unplugged, etc. */
|
||||
extern void SDL_AudioDeviceDisconnected(SDL_AudioDevice *device);
|
||||
|
||||
// Backends should call this if the system default device changes.
|
||||
extern void SDL_DefaultAudioDeviceChanged(SDL_AudioDevice *new_default_device);
|
||||
|
||||
// Backends should call this if a device's format is changing (opened or not); SDL will update state and carry on with the new format.
|
||||
extern int SDL_AudioDeviceFormatChanged(SDL_AudioDevice *device, const SDL_AudioSpec *newspec, int new_sample_frames);
|
||||
|
||||
// Same as above, but assume the device is already locked.
|
||||
extern int SDL_AudioDeviceFormatChangedAlreadyLocked(SDL_AudioDevice *device, const SDL_AudioSpec *newspec, int new_sample_frames);
|
||||
|
||||
// Find the SDL_AudioDevice associated with the handle supplied to SDL_AddAudioDevice. NULL if not found. DOES NOT LOCK THE DEVICE.
|
||||
extern SDL_AudioDevice *SDL_FindPhysicalAudioDeviceByHandle(void *handle);
|
||||
|
||||
// Find an SDL_AudioDevice, selected by a callback. NULL if not found. DOES NOT LOCK THE DEVICE.
|
||||
extern SDL_AudioDevice *SDL_FindPhysicalAudioDeviceByCallback(SDL_bool (*callback)(SDL_AudioDevice *device, void *userdata), void *userdata);
|
||||
|
||||
// Backends should call this if they change the device format, channels, freq, or sample_frames to keep other state correct.
|
||||
extern void SDL_UpdatedAudioDeviceFormat(SDL_AudioDevice *device);
|
||||
|
||||
// Backends can call this to get a standardized name for a thread to power a specific audio device.
|
||||
extern char *SDL_GetAudioThreadName(SDL_AudioDevice *device, char *buf, size_t buflen);
|
||||
|
||||
// Backends can call these to change a device's refcount.
|
||||
extern void RefPhysicalAudioDevice(SDL_AudioDevice *device);
|
||||
extern void UnrefPhysicalAudioDevice(SDL_AudioDevice *device);
|
||||
|
||||
// These functions are the heart of the audio threads. Backends can call them directly if they aren't using the SDL-provided thread.
|
||||
extern void SDL_OutputAudioThreadSetup(SDL_AudioDevice *device);
|
||||
extern SDL_bool SDL_OutputAudioThreadIterate(SDL_AudioDevice *device);
|
||||
extern void SDL_OutputAudioThreadShutdown(SDL_AudioDevice *device);
|
||||
extern void SDL_CaptureAudioThreadSetup(SDL_AudioDevice *device);
|
||||
extern SDL_bool SDL_CaptureAudioThreadIterate(SDL_AudioDevice *device);
|
||||
extern void SDL_CaptureAudioThreadShutdown(SDL_AudioDevice *device);
|
||||
extern void SDL_AudioThreadFinalize(SDL_AudioDevice *device);
|
||||
|
||||
// this gets used from the audio device threads. It has rules, don't use this if you don't know how to use it!
|
||||
extern void ConvertAudio(int num_frames, const void *src, SDL_AudioFormat src_format, int src_channels,
|
||||
void *dst, SDL_AudioFormat dst_format, int dst_channels, void* scratch);
|
||||
|
||||
// Special case to let something in SDL_audiocvt.c access something in SDL_audio.c. Don't use this.
|
||||
extern void OnAudioStreamCreated(SDL_AudioStream *stream);
|
||||
extern void OnAudioStreamDestroy(SDL_AudioStream *stream);
|
||||
|
||||
typedef struct SDL_AudioDriverImpl
|
||||
{
|
||||
void (*DetectDevices)(SDL_AudioDevice **default_output, SDL_AudioDevice **default_capture);
|
||||
int (*OpenDevice)(SDL_AudioDevice *device);
|
||||
void (*ThreadInit)(SDL_AudioDevice *device); // Called by audio thread at start
|
||||
void (*ThreadDeinit)(SDL_AudioDevice *device); // Called by audio thread at end
|
||||
int (*WaitDevice)(SDL_AudioDevice *device);
|
||||
int (*PlayDevice)(SDL_AudioDevice *device, const Uint8 *buffer, int buflen); // buffer and buflen are always from GetDeviceBuf, passed here for convenience.
|
||||
Uint8 *(*GetDeviceBuf)(SDL_AudioDevice *device, int *buffer_size);
|
||||
int (*WaitCaptureDevice)(SDL_AudioDevice *device);
|
||||
int (*CaptureFromDevice)(SDL_AudioDevice *device, void *buffer, int buflen);
|
||||
void (*FlushCapture)(SDL_AudioDevice *device);
|
||||
void (*CloseDevice)(SDL_AudioDevice *device);
|
||||
void (*FreeDeviceHandle)(SDL_AudioDevice *device); // SDL is done with this device; free the handle from SDL_AddAudioDevice()
|
||||
void (*DeinitializeStart)(void); // SDL calls this, then starts destroying objects, then calls Deinitialize. This is a good place to stop hotplug detection.
|
||||
void (*Deinitialize)(void);
|
||||
|
||||
// Some flags to push duplicate code into the core and reduce #ifdefs.
|
||||
SDL_bool ProvidesOwnCallbackThread; // !!! FIXME: rename this, it's not a callback thread anymore.
|
||||
SDL_bool HasCaptureSupport;
|
||||
SDL_bool OnlyHasDefaultOutputDevice;
|
||||
SDL_bool OnlyHasDefaultCaptureDevice; // !!! FIXME: is there ever a time where you'd have a default output and not a default capture (or vice versa)?
|
||||
} SDL_AudioDriverImpl;
|
||||
|
||||
|
||||
typedef struct SDL_PendingAudioDeviceEvent
|
||||
{
|
||||
Uint32 type;
|
||||
SDL_AudioDeviceID devid;
|
||||
struct SDL_PendingAudioDeviceEvent *next;
|
||||
} SDL_PendingAudioDeviceEvent;
|
||||
|
||||
typedef struct SDL_AudioDriver
|
||||
{
|
||||
const char *name; // The name of this audio driver
|
||||
const char *desc; // The description of this audio driver
|
||||
SDL_AudioDriverImpl impl; // the backend's interface
|
||||
SDL_RWLock *device_hash_lock; // A rwlock that protects `device_hash`
|
||||
SDL_HashTable *device_hash; // the collection of currently-available audio devices (capture, playback, logical and physical!)
|
||||
SDL_AudioStream *existing_streams; // a list of all existing SDL_AudioStreams.
|
||||
SDL_AudioDeviceID default_output_device_id;
|
||||
SDL_AudioDeviceID default_capture_device_id;
|
||||
SDL_PendingAudioDeviceEvent pending_events;
|
||||
SDL_PendingAudioDeviceEvent *pending_events_tail;
|
||||
|
||||
// !!! FIXME: most (all?) of these don't have to be atomic.
|
||||
SDL_AtomicInt output_device_count;
|
||||
SDL_AtomicInt capture_device_count;
|
||||
SDL_AtomicInt last_device_instance_id; // increments on each device add to provide unique instance IDs
|
||||
SDL_AtomicInt shutting_down; // non-zero during SDL_Quit, so we known not to accept any last-minute device hotplugs.
|
||||
} SDL_AudioDriver;
|
||||
|
||||
struct SDL_AudioQueue; // forward decl.
|
||||
|
||||
struct SDL_AudioStream
|
||||
{
|
||||
SDL_Mutex* lock;
|
||||
|
||||
SDL_PropertiesID props;
|
||||
|
||||
SDL_AudioStreamCallback get_callback;
|
||||
void *get_callback_userdata;
|
||||
SDL_AudioStreamCallback put_callback;
|
||||
void *put_callback_userdata;
|
||||
|
||||
SDL_AudioSpec src_spec;
|
||||
SDL_AudioSpec dst_spec;
|
||||
float freq_ratio;
|
||||
|
||||
struct SDL_AudioQueue* queue;
|
||||
Uint64 total_bytes_queued;
|
||||
|
||||
SDL_AudioSpec input_spec; // The spec of input data currently being processed
|
||||
Sint64 resample_offset;
|
||||
|
||||
Uint8 *work_buffer; // used for scratch space during data conversion/resampling.
|
||||
size_t work_buffer_allocation;
|
||||
|
||||
Uint8 *history_buffer; // history for left padding and future sample rate changes.
|
||||
size_t history_buffer_allocation;
|
||||
|
||||
SDL_bool simplified; // SDL_TRUE if created via SDL_OpenAudioDeviceStream
|
||||
|
||||
SDL_LogicalAudioDevice *bound_device;
|
||||
SDL_AudioStream *next_binding;
|
||||
SDL_AudioStream *prev_binding;
|
||||
|
||||
SDL_AudioStream *prev; // linked list of all existing streams (so we can free them on shutdown).
|
||||
SDL_AudioStream *next; // linked list of all existing streams (so we can free them on shutdown).
|
||||
};
|
||||
|
||||
/* Logical devices are an abstraction in SDL3; you can open the same physical
|
||||
device multiple times, and each will result in an object with its own set
|
||||
of bound audio streams, etc, even though internally these are all processed
|
||||
as a group when mixing the final output for the physical device. */
|
||||
struct SDL_LogicalAudioDevice
|
||||
{
|
||||
// the unique instance ID of this device.
|
||||
SDL_AudioDeviceID instance_id;
|
||||
|
||||
// The physical device associated with this opened device.
|
||||
SDL_AudioDevice *physical_device;
|
||||
|
||||
// If whole logical device is paused (process no streams bound to this device).
|
||||
SDL_AtomicInt paused;
|
||||
|
||||
// double-linked list of all audio streams currently bound to this opened device.
|
||||
SDL_AudioStream *bound_streams;
|
||||
|
||||
// SDL_TRUE if this was opened as a default device.
|
||||
SDL_bool opened_as_default;
|
||||
|
||||
// SDL_TRUE if device was opened with SDL_OpenAudioDeviceStream (so it forbids binding changes, etc).
|
||||
SDL_bool simplified;
|
||||
|
||||
// If non-NULL, callback into the app that lets them access the final postmix buffer.
|
||||
SDL_AudioPostmixCallback postmix;
|
||||
|
||||
// App-supplied pointer for postmix callback.
|
||||
void *postmix_userdata;
|
||||
|
||||
// double-linked list of opened devices on the same physical device.
|
||||
SDL_LogicalAudioDevice *next;
|
||||
SDL_LogicalAudioDevice *prev;
|
||||
};
|
||||
|
||||
struct SDL_AudioDevice
|
||||
{
|
||||
// A mutex for locking access to this struct
|
||||
SDL_Mutex *lock;
|
||||
|
||||
// Reference count of the device; logical devices, device threads, etc, add to this.
|
||||
SDL_AtomicInt refcount;
|
||||
|
||||
// These are, initially, set from current_audio, but we might swap them out with Zombie versions on disconnect/failure.
|
||||
int (*WaitDevice)(SDL_AudioDevice *device);
|
||||
int (*PlayDevice)(SDL_AudioDevice *device, const Uint8 *buffer, int buflen);
|
||||
Uint8 *(*GetDeviceBuf)(SDL_AudioDevice *device, int *buffer_size);
|
||||
int (*WaitCaptureDevice)(SDL_AudioDevice *device);
|
||||
int (*CaptureFromDevice)(SDL_AudioDevice *device, void *buffer, int buflen);
|
||||
void (*FlushCapture)(SDL_AudioDevice *device);
|
||||
|
||||
// human-readable name of the device. ("SoundBlaster Pro 16")
|
||||
char *name;
|
||||
|
||||
// the unique instance ID of this device.
|
||||
SDL_AudioDeviceID instance_id;
|
||||
|
||||
// a way for the backend to identify this device _when not opened_
|
||||
void *handle;
|
||||
|
||||
// The device's current audio specification
|
||||
SDL_AudioSpec spec;
|
||||
int buffer_size;
|
||||
|
||||
// The device's default audio specification
|
||||
SDL_AudioSpec default_spec;
|
||||
|
||||
// Number of sample frames the devices wants per-buffer.
|
||||
int sample_frames;
|
||||
|
||||
// Value to use for SDL_memset to silence a buffer in this device's format
|
||||
int silence_value;
|
||||
|
||||
// non-zero if we are signaling the audio thread to end.
|
||||
SDL_AtomicInt shutdown;
|
||||
|
||||
// non-zero if this was a disconnected device and we're waiting for it to be decommissioned.
|
||||
SDL_AtomicInt zombie;
|
||||
|
||||
// SDL_TRUE if this is a capture device instead of an output device
|
||||
SDL_bool iscapture;
|
||||
|
||||
// SDL_TRUE if audio thread can skip silence/mix/convert stages and just do a basic memcpy.
|
||||
SDL_bool simple_copy;
|
||||
|
||||
// Scratch buffers used for mixing.
|
||||
Uint8 *work_buffer;
|
||||
Uint8 *mix_buffer;
|
||||
float *postmix_buffer;
|
||||
|
||||
// Size of work_buffer (and mix_buffer) in bytes.
|
||||
int work_buffer_size;
|
||||
|
||||
// A thread to feed the audio device
|
||||
SDL_Thread *thread;
|
||||
|
||||
// SDL_TRUE if this physical device is currently opened by the backend.
|
||||
SDL_bool currently_opened;
|
||||
|
||||
// Data private to this driver
|
||||
struct SDL_PrivateAudioData *hidden;
|
||||
|
||||
// All logical devices associated with this physical device.
|
||||
SDL_LogicalAudioDevice *logical_devices;
|
||||
};
|
||||
|
||||
typedef struct AudioBootStrap
|
||||
{
|
||||
const char *name;
|
||||
const char *desc;
|
||||
SDL_bool (*init)(SDL_AudioDriverImpl *impl);
|
||||
SDL_bool demand_only; // if SDL_TRUE: request explicitly, or it won't be available.
|
||||
} AudioBootStrap;
|
||||
|
||||
// Not all of these are available in a given build. Use #ifdefs, etc.
|
||||
extern AudioBootStrap PIPEWIRE_bootstrap;
|
||||
extern AudioBootStrap PULSEAUDIO_bootstrap;
|
||||
extern AudioBootStrap ALSA_bootstrap;
|
||||
extern AudioBootStrap JACK_bootstrap;
|
||||
extern AudioBootStrap SNDIO_bootstrap;
|
||||
extern AudioBootStrap NETBSDAUDIO_bootstrap;
|
||||
extern AudioBootStrap DSP_bootstrap;
|
||||
extern AudioBootStrap WASAPI_bootstrap;
|
||||
extern AudioBootStrap DSOUND_bootstrap;
|
||||
extern AudioBootStrap WINMM_bootstrap;
|
||||
extern AudioBootStrap HAIKUAUDIO_bootstrap;
|
||||
extern AudioBootStrap COREAUDIO_bootstrap;
|
||||
extern AudioBootStrap DISKAUDIO_bootstrap;
|
||||
extern AudioBootStrap DUMMYAUDIO_bootstrap;
|
||||
extern AudioBootStrap AAUDIO_bootstrap;
|
||||
extern AudioBootStrap OPENSLES_bootstrap;
|
||||
extern AudioBootStrap ANDROIDAUDIO_bootstrap;
|
||||
extern AudioBootStrap PS2AUDIO_bootstrap;
|
||||
extern AudioBootStrap PSPAUDIO_bootstrap;
|
||||
extern AudioBootStrap VITAAUD_bootstrap;
|
||||
extern AudioBootStrap N3DSAUDIO_bootstrap;
|
||||
extern AudioBootStrap EMSCRIPTENAUDIO_bootstrap;
|
||||
extern AudioBootStrap QSAAUDIO_bootstrap;
|
||||
|
||||
#endif // SDL_sysaudio_h_
|
||||
2129
vendor/sdl-3.0.0/src/audio/SDL_wave.c
vendored
Normal file
2129
vendor/sdl-3.0.0/src/audio/SDL_wave.c
vendored
Normal file
File diff suppressed because it is too large
Load diff
151
vendor/sdl-3.0.0/src/audio/SDL_wave.h
vendored
Normal file
151
vendor/sdl-3.0.0/src/audio/SDL_wave.h
vendored
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
/* RIFF WAVE files are little-endian */
|
||||
|
||||
/*******************************************/
|
||||
/* Define values for Microsoft WAVE format */
|
||||
/*******************************************/
|
||||
/* FOURCC */
|
||||
#define RIFF 0x46464952 /* "RIFF" */
|
||||
#define WAVE 0x45564157 /* "WAVE" */
|
||||
#define FACT 0x74636166 /* "fact" */
|
||||
#define LIST 0x5453494c /* "LIST" */
|
||||
#define BEXT 0x74786562 /* "bext" */
|
||||
#define JUNK 0x4B4E554A /* "JUNK" */
|
||||
#define FMT 0x20746D66 /* "fmt " */
|
||||
#define DATA 0x61746164 /* "data" */
|
||||
/* Format tags */
|
||||
#define UNKNOWN_CODE 0x0000
|
||||
#define PCM_CODE 0x0001
|
||||
#define MS_ADPCM_CODE 0x0002
|
||||
#define IEEE_FLOAT_CODE 0x0003
|
||||
#define ALAW_CODE 0x0006
|
||||
#define MULAW_CODE 0x0007
|
||||
#define IMA_ADPCM_CODE 0x0011
|
||||
#define MPEG_CODE 0x0050
|
||||
#define MPEGLAYER3_CODE 0x0055
|
||||
#define EXTENSIBLE_CODE 0xFFFE
|
||||
|
||||
/* Stores the WAVE format information. */
|
||||
typedef struct WaveFormat
|
||||
{
|
||||
Uint16 formattag; /* Raw value of the first field in the fmt chunk data. */
|
||||
Uint16 encoding; /* Actual encoding, possibly from the extensible header. */
|
||||
Uint16 channels; /* Number of channels. */
|
||||
Uint32 frequency; /* Sampling rate in Hz. */
|
||||
Uint32 byterate; /* Average bytes per second. */
|
||||
Uint16 blockalign; /* Bytes per block. */
|
||||
Uint16 bitspersample; /* Currently supported are 8, 16, 24, 32, and 4 for ADPCM. */
|
||||
|
||||
/* Extra information size. Number of extra bytes starting at byte 18 in the
|
||||
* fmt chunk data. This is at least 22 for the extensible header.
|
||||
*/
|
||||
Uint16 extsize;
|
||||
|
||||
/* Extensible WAVE header fields */
|
||||
Uint16 validsamplebits;
|
||||
Uint32 samplesperblock; /* For compressed formats. Can be zero. Actually 16 bits in the header. */
|
||||
Uint32 channelmask;
|
||||
Uint8 subformat[16]; /* A format GUID. */
|
||||
} WaveFormat;
|
||||
|
||||
/* Stores information on the fact chunk. */
|
||||
typedef struct WaveFact
|
||||
{
|
||||
/* Represents the state of the fact chunk in the WAVE file.
|
||||
* Set to -1 if the fact chunk is invalid.
|
||||
* Set to 0 if the fact chunk is not present
|
||||
* Set to 1 if the fact chunk is present and valid.
|
||||
* Set to 2 if samplelength is going to be used as the number of sample frames.
|
||||
*/
|
||||
Sint32 status;
|
||||
|
||||
/* Version 1 of the RIFF specification calls the field in the fact chunk
|
||||
* dwFileSize. The Standards Update then calls it dwSampleLength and specifies
|
||||
* that it is 'the length of the data in samples'. WAVE files from Windows
|
||||
* with this chunk have it set to the samples per channel (sample frames).
|
||||
* This is useful to truncate compressed audio to a specific sample count
|
||||
* because a compressed block is usually decoded to a fixed number of
|
||||
* sample frames.
|
||||
*/
|
||||
Uint32 samplelength; /* Raw sample length value from the fact chunk. */
|
||||
} WaveFact;
|
||||
|
||||
/* Generic struct for the chunks in the WAVE file. */
|
||||
typedef struct WaveChunk
|
||||
{
|
||||
Uint32 fourcc; /* FOURCC of the chunk. */
|
||||
Uint32 length; /* Size of the chunk data. */
|
||||
Sint64 position; /* Position of the data in the stream. */
|
||||
Uint8 *data; /* When allocated, this points to the chunk data. length is used for the memory allocation size. */
|
||||
size_t size; /* Number of bytes in data that could be read from the stream. Can be smaller than length. */
|
||||
} WaveChunk;
|
||||
|
||||
/* Controls how the size of the RIFF chunk affects the loading of a WAVE file. */
|
||||
typedef enum WaveRiffSizeHint
|
||||
{
|
||||
RiffSizeNoHint,
|
||||
RiffSizeForce,
|
||||
RiffSizeIgnoreZero,
|
||||
RiffSizeIgnore,
|
||||
RiffSizeMaximum
|
||||
} WaveRiffSizeHint;
|
||||
|
||||
/* Controls how a truncated WAVE file is handled. */
|
||||
typedef enum WaveTruncationHint
|
||||
{
|
||||
TruncNoHint,
|
||||
TruncVeryStrict,
|
||||
TruncStrict,
|
||||
TruncDropFrame,
|
||||
TruncDropBlock
|
||||
} WaveTruncationHint;
|
||||
|
||||
/* Controls how the fact chunk affects the loading of a WAVE file. */
|
||||
typedef enum WaveFactChunkHint
|
||||
{
|
||||
FactNoHint,
|
||||
FactTruncate,
|
||||
FactStrict,
|
||||
FactIgnoreZero,
|
||||
FactIgnore
|
||||
} WaveFactChunkHint;
|
||||
|
||||
typedef struct WaveFile
|
||||
{
|
||||
WaveChunk chunk;
|
||||
WaveFormat format;
|
||||
WaveFact fact;
|
||||
|
||||
/* Number of sample frames that will be decoded. Calculated either with the
|
||||
* size of the data chunk or, if the appropriate hint is enabled, with the
|
||||
* sample length value from the fact chunk.
|
||||
*/
|
||||
Sint64 sampleframes;
|
||||
|
||||
void *decoderdata; /* Some decoders require extra data for a state. */
|
||||
|
||||
WaveRiffSizeHint riffhint;
|
||||
WaveTruncationHint trunchint;
|
||||
WaveFactChunkHint facthint;
|
||||
} WaveFile;
|
||||
536
vendor/sdl-3.0.0/src/audio/aaudio/SDL_aaudio.c
vendored
Normal file
536
vendor/sdl-3.0.0/src/audio/aaudio/SDL_aaudio.c
vendored
Normal file
|
|
@ -0,0 +1,536 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifdef SDL_AUDIO_DRIVER_AAUDIO
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
#include "SDL_aaudio.h"
|
||||
|
||||
#include "../../core/android/SDL_android.h"
|
||||
#include <stdbool.h>
|
||||
#include <aaudio/AAudio.h>
|
||||
|
||||
#if __ANDROID_API__ < 31
|
||||
#define AAUDIO_FORMAT_PCM_I32 4
|
||||
#endif
|
||||
|
||||
struct SDL_PrivateAudioData
|
||||
{
|
||||
AAudioStream *stream;
|
||||
int num_buffers;
|
||||
Uint8 *mixbuf; // Raw mixing buffer
|
||||
size_t mixbuf_bytes; // num_buffers * device->buffer_size
|
||||
size_t callback_bytes;
|
||||
size_t processed_bytes;
|
||||
SDL_Semaphore *semaphore;
|
||||
SDL_AtomicInt error_callback_triggered;
|
||||
SDL_bool resume; // Resume device if it was paused automatically
|
||||
};
|
||||
|
||||
// Debug
|
||||
#if 0
|
||||
#define LOGI(...) SDL_Log(__VA_ARGS__);
|
||||
#else
|
||||
#define LOGI(...)
|
||||
#endif
|
||||
|
||||
#define LIB_AAUDIO_SO "libaaudio.so"
|
||||
|
||||
typedef struct AAUDIO_Data
|
||||
{
|
||||
void *handle;
|
||||
#define SDL_PROC(ret, func, params) ret (*func) params;
|
||||
#include "SDL_aaudiofuncs.h"
|
||||
} AAUDIO_Data;
|
||||
static AAUDIO_Data ctx;
|
||||
|
||||
static int AAUDIO_LoadFunctions(AAUDIO_Data *data)
|
||||
{
|
||||
#define SDL_PROC(ret, func, params) \
|
||||
do { \
|
||||
data->func = (ret (*) params)SDL_LoadFunction(data->handle, #func); \
|
||||
if (!data->func) { \
|
||||
return SDL_SetError("Couldn't load AAUDIO function %s: %s", #func, SDL_GetError()); \
|
||||
} \
|
||||
} while (0);
|
||||
#include "SDL_aaudiofuncs.h"
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void AAUDIO_errorCallback(AAudioStream *stream, void *userData, aaudio_result_t error)
|
||||
{
|
||||
LOGI("SDL AAUDIO_errorCallback: %d - %s", error, ctx.AAudio_convertResultToText(error));
|
||||
|
||||
// You MUST NOT close the audio stream from this callback, so we cannot call SDL_AudioDeviceDisconnected here.
|
||||
// Just flag the device so we can kill it in PlayDevice instead.
|
||||
SDL_AudioDevice *device = (SDL_AudioDevice *) userData;
|
||||
SDL_AtomicSet(&device->hidden->error_callback_triggered, (int) error); // AAUDIO_OK is zero, so !triggered means no error.
|
||||
SDL_PostSemaphore(device->hidden->semaphore); // in case we're blocking in WaitDevice.
|
||||
}
|
||||
|
||||
static aaudio_data_callback_result_t AAUDIO_dataCallback(AAudioStream *stream, void *userData, void *audioData, int32_t numFrames)
|
||||
{
|
||||
SDL_AudioDevice *device = (SDL_AudioDevice *) userData;
|
||||
struct SDL_PrivateAudioData *hidden = device->hidden;
|
||||
size_t framesize = SDL_AUDIO_FRAMESIZE(device->spec);
|
||||
size_t callback_bytes = numFrames * framesize;
|
||||
size_t old_buffer_index = hidden->callback_bytes / device->buffer_size;
|
||||
|
||||
if (device->iscapture) {
|
||||
const Uint8 *input = (const Uint8 *)audioData;
|
||||
size_t available_bytes = hidden->mixbuf_bytes - (hidden->callback_bytes - hidden->processed_bytes);
|
||||
size_t size = SDL_min(available_bytes, callback_bytes);
|
||||
size_t offset = hidden->callback_bytes % hidden->mixbuf_bytes;
|
||||
size_t end = (offset + size) % hidden->mixbuf_bytes;
|
||||
SDL_assert(size <= hidden->mixbuf_bytes);
|
||||
|
||||
//LOGI("Recorded %zu frames, %zu available, %zu max (%zu written, %zu read)\n", callback_bytes / framesize, available_bytes / framesize, hidden->mixbuf_bytes / framesize, hidden->callback_bytes / framesize, hidden->processed_bytes / framesize);
|
||||
|
||||
if (offset <= end) {
|
||||
SDL_memcpy(&hidden->mixbuf[offset], input, size);
|
||||
} else {
|
||||
size_t partial = (hidden->mixbuf_bytes - offset);
|
||||
SDL_memcpy(&hidden->mixbuf[offset], &input[0], partial);
|
||||
SDL_memcpy(&hidden->mixbuf[0], &input[partial], end);
|
||||
}
|
||||
|
||||
SDL_MemoryBarrierRelease();
|
||||
hidden->callback_bytes += size;
|
||||
|
||||
if (size < callback_bytes) {
|
||||
LOGI("Audio recording overflow, dropped %zu frames\n", (callback_bytes - size) / framesize);
|
||||
}
|
||||
} else {
|
||||
Uint8 *output = (Uint8 *)audioData;
|
||||
size_t available_bytes = (hidden->processed_bytes - hidden->callback_bytes);
|
||||
size_t size = SDL_min(available_bytes, callback_bytes);
|
||||
size_t offset = hidden->callback_bytes % hidden->mixbuf_bytes;
|
||||
size_t end = (offset + size) % hidden->mixbuf_bytes;
|
||||
SDL_assert(size <= hidden->mixbuf_bytes);
|
||||
|
||||
//LOGI("Playing %zu frames, %zu available, %zu max (%zu written, %zu read)\n", callback_bytes / framesize, available_bytes / framesize, hidden->mixbuf_bytes / framesize, hidden->processed_bytes / framesize, hidden->callback_bytes / framesize);
|
||||
|
||||
SDL_MemoryBarrierAcquire();
|
||||
if (offset <= end) {
|
||||
SDL_memcpy(output, &hidden->mixbuf[offset], size);
|
||||
} else {
|
||||
size_t partial = (hidden->mixbuf_bytes - offset);
|
||||
SDL_memcpy(&output[0], &hidden->mixbuf[offset], partial);
|
||||
SDL_memcpy(&output[partial], &hidden->mixbuf[0], end);
|
||||
}
|
||||
hidden->callback_bytes += size;
|
||||
|
||||
if (size < callback_bytes) {
|
||||
LOGI("Audio playback underflow, missed %zu frames\n", (callback_bytes - size) / framesize);
|
||||
SDL_memset(&output[size], device->silence_value, (callback_bytes - size));
|
||||
}
|
||||
}
|
||||
|
||||
size_t new_buffer_index = hidden->callback_bytes / device->buffer_size;
|
||||
while (old_buffer_index < new_buffer_index) {
|
||||
// Trigger audio processing
|
||||
SDL_PostSemaphore(hidden->semaphore);
|
||||
++old_buffer_index;
|
||||
}
|
||||
|
||||
return AAUDIO_CALLBACK_RESULT_CONTINUE;
|
||||
}
|
||||
|
||||
static Uint8 *AAUDIO_GetDeviceBuf(SDL_AudioDevice *device, int *bufsize)
|
||||
{
|
||||
struct SDL_PrivateAudioData *hidden = device->hidden;
|
||||
size_t offset = (hidden->processed_bytes % hidden->mixbuf_bytes);
|
||||
return &hidden->mixbuf[offset];
|
||||
}
|
||||
|
||||
static int AAUDIO_WaitDevice(SDL_AudioDevice *device)
|
||||
{
|
||||
SDL_WaitSemaphore(device->hidden->semaphore);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int BuildAAudioStream(SDL_AudioDevice *device);
|
||||
|
||||
static int RecoverAAudioDevice(SDL_AudioDevice *device)
|
||||
{
|
||||
struct SDL_PrivateAudioData *hidden = device->hidden;
|
||||
|
||||
// attempt to build a new stream, in case there's a new default device.
|
||||
ctx.AAudioStream_requestStop(hidden->stream);
|
||||
ctx.AAudioStream_close(hidden->stream);
|
||||
hidden->stream = NULL;
|
||||
|
||||
SDL_aligned_free(hidden->mixbuf);
|
||||
hidden->mixbuf = NULL;
|
||||
|
||||
SDL_DestroySemaphore(hidden->semaphore);
|
||||
hidden->semaphore = NULL;
|
||||
|
||||
const int prev_sample_frames = device->sample_frames;
|
||||
SDL_AudioSpec prevspec;
|
||||
SDL_copyp(&prevspec, &device->spec);
|
||||
|
||||
if (BuildAAudioStream(device) < 0) {
|
||||
return -1; // oh well, we tried.
|
||||
}
|
||||
|
||||
// we don't know the new device spec until we open the new device, so we saved off the old one and force it back
|
||||
// so SDL_AudioDeviceFormatChanged can set up all the important state if necessary and then set it back to the new spec.
|
||||
const int new_sample_frames = device->sample_frames;
|
||||
SDL_AudioSpec newspec;
|
||||
SDL_copyp(&newspec, &device->spec);
|
||||
|
||||
device->sample_frames = prev_sample_frames;
|
||||
SDL_copyp(&device->spec, &prevspec);
|
||||
if (SDL_AudioDeviceFormatChangedAlreadyLocked(device, &newspec, new_sample_frames) < 0) {
|
||||
return -1; // ugh
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int AAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
|
||||
{
|
||||
struct SDL_PrivateAudioData *hidden = device->hidden;
|
||||
|
||||
// AAUDIO_dataCallback picks up our work and unblocks AAUDIO_WaitDevice. But make sure we didn't fail here.
|
||||
const aaudio_result_t err = (aaudio_result_t) SDL_AtomicGet(&hidden->error_callback_triggered);
|
||||
if (err) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_AUDIO, "aaudio: Audio device triggered error %d (%s)", (int) err, ctx.AAudio_convertResultToText(err));
|
||||
|
||||
if (RecoverAAudioDevice(device) < 0) {
|
||||
return -1; // oh well, we went down hard.
|
||||
}
|
||||
} else {
|
||||
SDL_MemoryBarrierRelease();
|
||||
hidden->processed_bytes += buflen;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int AAUDIO_CaptureFromDevice(SDL_AudioDevice *device, void *buffer, int buflen)
|
||||
{
|
||||
struct SDL_PrivateAudioData *hidden = device->hidden;
|
||||
|
||||
// AAUDIO_dataCallback picks up our work and unblocks AAUDIO_WaitDevice. But make sure we didn't fail here.
|
||||
if (SDL_AtomicGet(&hidden->error_callback_triggered)) {
|
||||
SDL_AtomicSet(&hidden->error_callback_triggered, 0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
SDL_assert(buflen == device->buffer_size); // If this isn't true, we need to change semaphore trigger logic and account for wrapping copies here
|
||||
size_t offset = (hidden->processed_bytes % hidden->mixbuf_bytes);
|
||||
SDL_MemoryBarrierAcquire();
|
||||
SDL_memcpy(buffer, &hidden->mixbuf[offset], buflen);
|
||||
hidden->processed_bytes += buflen;
|
||||
return buflen;
|
||||
}
|
||||
|
||||
static void AAUDIO_CloseDevice(SDL_AudioDevice *device)
|
||||
{
|
||||
struct SDL_PrivateAudioData *hidden = device->hidden;
|
||||
LOGI(__func__);
|
||||
|
||||
if (hidden) {
|
||||
if (hidden->stream) {
|
||||
ctx.AAudioStream_requestStop(hidden->stream);
|
||||
// !!! FIXME: do we have to wait for the state to change to make sure all buffered audio has played, or will close do this (or will the system do this after the close)?
|
||||
// !!! FIXME: also, will this definitely wait for a running data callback to finish, and then stop the callback from firing again?
|
||||
ctx.AAudioStream_close(hidden->stream);
|
||||
}
|
||||
|
||||
if (hidden->semaphore) {
|
||||
SDL_DestroySemaphore(hidden->semaphore);
|
||||
}
|
||||
|
||||
SDL_aligned_free(hidden->mixbuf);
|
||||
SDL_free(hidden);
|
||||
device->hidden = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int BuildAAudioStream(SDL_AudioDevice *device)
|
||||
{
|
||||
struct SDL_PrivateAudioData *hidden = device->hidden;
|
||||
const SDL_bool iscapture = device->iscapture;
|
||||
aaudio_result_t res;
|
||||
|
||||
SDL_AtomicSet(&hidden->error_callback_triggered, 0);
|
||||
|
||||
AAudioStreamBuilder *builder = NULL;
|
||||
res = ctx.AAudio_createStreamBuilder(&builder);
|
||||
if (res != AAUDIO_OK) {
|
||||
LOGI("SDL Failed AAudio_createStreamBuilder %d", res);
|
||||
return SDL_SetError("SDL Failed AAudio_createStreamBuilder %d", res);
|
||||
} else if (builder == NULL) {
|
||||
LOGI("SDL Failed AAudio_createStreamBuilder - builder NULL");
|
||||
return SDL_SetError("SDL Failed AAudio_createStreamBuilder - builder NULL");
|
||||
}
|
||||
|
||||
#if ALLOW_MULTIPLE_ANDROID_AUDIO_DEVICES
|
||||
const int aaudio_device_id = (int) ((size_t) device->handle);
|
||||
LOGI("Opening device id %d", aaudio_device_id);
|
||||
ctx.AAudioStreamBuilder_setDeviceId(builder, aaudio_device_id);
|
||||
#endif
|
||||
|
||||
aaudio_format_t format;
|
||||
#ifdef SET_AUDIO_FORMAT
|
||||
if ((device->spec.format == SDL_AUDIO_S32) && (SDL_GetAndroidSDKVersion() >= 31)) {
|
||||
format = AAUDIO_FORMAT_PCM_I32;
|
||||
} else if (device->spec.format == SDL_AUDIO_F32) {
|
||||
format = AAUDIO_FORMAT_PCM_FLOAT;
|
||||
} else {
|
||||
format = AAUDIO_FORMAT_PCM_I16; // sint16 is a safe bet for everything else.
|
||||
}
|
||||
ctx.AAudioStreamBuilder_setFormat(builder, format);
|
||||
ctx.AAudioStreamBuilder_setSampleRate(builder, device->spec.freq);
|
||||
ctx.AAudioStreamBuilder_setChannelCount(builder, device->spec.channels);
|
||||
#endif
|
||||
|
||||
const aaudio_direction_t direction = (iscapture ? AAUDIO_DIRECTION_INPUT : AAUDIO_DIRECTION_OUTPUT);
|
||||
ctx.AAudioStreamBuilder_setDirection(builder, direction);
|
||||
ctx.AAudioStreamBuilder_setErrorCallback(builder, AAUDIO_errorCallback, device);
|
||||
ctx.AAudioStreamBuilder_setDataCallback(builder, AAUDIO_dataCallback, device);
|
||||
// Some devices have flat sounding audio when low latency mode is enabled, but this is a better experience for most people
|
||||
if (SDL_GetHintBoolean("SDL_ANDROID_LOW_LATENCY_AUDIO", SDL_TRUE)) {
|
||||
ctx.AAudioStreamBuilder_setPerformanceMode(builder, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY);
|
||||
}
|
||||
|
||||
LOGI("AAudio Try to open %u hz %u bit chan %u %s samples %u",
|
||||
device->spec.freq, SDL_AUDIO_BITSIZE(device->spec.format),
|
||||
device->spec.channels, SDL_AUDIO_ISBIGENDIAN(device->spec.format) ? "BE" : "LE", device->sample_frames);
|
||||
|
||||
res = ctx.AAudioStreamBuilder_openStream(builder, &hidden->stream);
|
||||
if (res != AAUDIO_OK) {
|
||||
LOGI("SDL Failed AAudioStreamBuilder_openStream %d", res);
|
||||
ctx.AAudioStreamBuilder_delete(builder);
|
||||
return SDL_SetError("%s : %s", __func__, ctx.AAudio_convertResultToText(res));
|
||||
}
|
||||
ctx.AAudioStreamBuilder_delete(builder);
|
||||
|
||||
device->sample_frames = (int)ctx.AAudioStream_getFramesPerDataCallback(hidden->stream);
|
||||
if (device->sample_frames == AAUDIO_UNSPECIFIED) {
|
||||
// We'll get variable frames in the callback, make sure we have at least half a buffer available
|
||||
device->sample_frames = (int)ctx.AAudioStream_getBufferCapacityInFrames(hidden->stream) / 2;
|
||||
}
|
||||
|
||||
device->spec.freq = ctx.AAudioStream_getSampleRate(hidden->stream);
|
||||
device->spec.channels = ctx.AAudioStream_getChannelCount(hidden->stream);
|
||||
|
||||
format = ctx.AAudioStream_getFormat(hidden->stream);
|
||||
if (format == AAUDIO_FORMAT_PCM_I16) {
|
||||
device->spec.format = SDL_AUDIO_S16;
|
||||
} else if (format == AAUDIO_FORMAT_PCM_I32) {
|
||||
device->spec.format = SDL_AUDIO_S32;
|
||||
} else if (format == AAUDIO_FORMAT_PCM_FLOAT) {
|
||||
device->spec.format = SDL_AUDIO_F32;
|
||||
} else {
|
||||
return SDL_SetError("Got unexpected audio format %d from AAudioStream_getFormat", (int) format);
|
||||
}
|
||||
|
||||
SDL_UpdatedAudioDeviceFormat(device);
|
||||
|
||||
// Allocate a double buffered mixing buffer
|
||||
hidden->num_buffers = 2;
|
||||
hidden->mixbuf_bytes = (hidden->num_buffers * device->buffer_size);
|
||||
hidden->mixbuf = (Uint8 *)SDL_aligned_alloc(SDL_SIMDGetAlignment(), hidden->mixbuf_bytes);
|
||||
if (hidden->mixbuf == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
hidden->processed_bytes = 0;
|
||||
hidden->callback_bytes = 0;
|
||||
|
||||
hidden->semaphore = SDL_CreateSemaphore(iscapture ? 0 : hidden->num_buffers);
|
||||
if (!hidden->semaphore) {
|
||||
LOGI("SDL Failed SDL_CreateSemaphore %s iscapture:%d", SDL_GetError(), iscapture);
|
||||
return -1;
|
||||
}
|
||||
|
||||
LOGI("AAudio Actually opened %u hz %u bit chan %u %s samples %u, buffers %d",
|
||||
device->spec.freq, SDL_AUDIO_BITSIZE(device->spec.format),
|
||||
device->spec.channels, SDL_AUDIO_ISBIGENDIAN(device->spec.format) ? "BE" : "LE", device->sample_frames, hidden->num_buffers);
|
||||
|
||||
res = ctx.AAudioStream_requestStart(hidden->stream);
|
||||
if (res != AAUDIO_OK) {
|
||||
LOGI("SDL Failed AAudioStream_requestStart %d iscapture:%d", res, iscapture);
|
||||
return SDL_SetError("%s : %s", __func__, ctx.AAudio_convertResultToText(res));
|
||||
}
|
||||
|
||||
LOGI("SDL AAudioStream_requestStart OK");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int AAUDIO_OpenDevice(SDL_AudioDevice *device)
|
||||
{
|
||||
#if ALLOW_MULTIPLE_ANDROID_AUDIO_DEVICES
|
||||
SDL_assert(device->handle != NULL); // AAUDIO_UNSPECIFIED is zero, so legit devices should all be non-zero.
|
||||
#endif
|
||||
|
||||
LOGI(__func__);
|
||||
|
||||
if (device->iscapture) {
|
||||
if (!Android_JNI_RequestPermission("android.permission.RECORD_AUDIO")) {
|
||||
LOGI("This app doesn't have RECORD_AUDIO permission");
|
||||
return SDL_SetError("This app doesn't have RECORD_AUDIO permission");
|
||||
}
|
||||
}
|
||||
|
||||
device->hidden = (struct SDL_PrivateAudioData *)SDL_calloc(1, sizeof(*device->hidden));
|
||||
if (device->hidden == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
|
||||
return BuildAAudioStream(device);
|
||||
}
|
||||
|
||||
static SDL_bool PauseOneDevice(SDL_AudioDevice *device, void *userdata)
|
||||
{
|
||||
struct SDL_PrivateAudioData *hidden = (struct SDL_PrivateAudioData *)device->hidden;
|
||||
if (hidden != NULL) {
|
||||
if (hidden->stream) {
|
||||
aaudio_result_t res;
|
||||
|
||||
if (device->iscapture) {
|
||||
// Pause() isn't implemented for 'capture', use Stop()
|
||||
res = ctx.AAudioStream_requestStop(hidden->stream);
|
||||
} else {
|
||||
res = ctx.AAudioStream_requestPause(hidden->stream);
|
||||
}
|
||||
|
||||
if (res != AAUDIO_OK) {
|
||||
LOGI("SDL Failed AAudioStream_requestPause %d", res);
|
||||
SDL_SetError("%s : %s", __func__, ctx.AAudio_convertResultToText(res));
|
||||
}
|
||||
|
||||
SDL_LockMutex(device->lock);
|
||||
hidden->resume = SDL_TRUE;
|
||||
}
|
||||
}
|
||||
return SDL_FALSE; // keep enumerating.
|
||||
}
|
||||
|
||||
// Pause (block) all non already paused audio devices by taking their mixer lock
|
||||
void AAUDIO_PauseDevices(void)
|
||||
{
|
||||
if (ctx.handle != NULL) { // AAUDIO driver is used?
|
||||
(void) SDL_FindPhysicalAudioDeviceByCallback(PauseOneDevice, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
// Resume (unblock) all non already paused audio devices by releasing their mixer lock
|
||||
static SDL_bool ResumeOneDevice(SDL_AudioDevice *device, void *userdata)
|
||||
{
|
||||
struct SDL_PrivateAudioData *hidden = device->hidden;
|
||||
if (hidden != NULL) {
|
||||
if (hidden->resume) {
|
||||
hidden->resume = SDL_FALSE;
|
||||
SDL_UnlockMutex(device->lock);
|
||||
}
|
||||
|
||||
if (hidden->stream) {
|
||||
aaudio_result_t res = ctx.AAudioStream_requestStart(hidden->stream);
|
||||
if (res != AAUDIO_OK) {
|
||||
LOGI("SDL Failed AAudioStream_requestStart %d", res);
|
||||
SDL_SetError("%s : %s", __func__, ctx.AAudio_convertResultToText(res));
|
||||
}
|
||||
}
|
||||
}
|
||||
return SDL_FALSE; // keep enumerating.
|
||||
}
|
||||
|
||||
void AAUDIO_ResumeDevices(void)
|
||||
{
|
||||
if (ctx.handle != NULL) { // AAUDIO driver is used?
|
||||
(void) SDL_FindPhysicalAudioDeviceByCallback(ResumeOneDevice, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static void AAUDIO_Deinitialize(void)
|
||||
{
|
||||
Android_StopAudioHotplug();
|
||||
|
||||
LOGI(__func__);
|
||||
if (ctx.handle) {
|
||||
SDL_UnloadObject(ctx.handle);
|
||||
}
|
||||
SDL_zero(ctx);
|
||||
LOGI("End AAUDIO %s", SDL_GetError());
|
||||
}
|
||||
|
||||
|
||||
static SDL_bool AAUDIO_Init(SDL_AudioDriverImpl *impl)
|
||||
{
|
||||
LOGI(__func__);
|
||||
|
||||
/* AAudio was introduced in Android 8.0, but has reference counting crash issues in that release,
|
||||
* so don't use it until 8.1.
|
||||
*
|
||||
* See https://github.com/google/oboe/issues/40 for more information.
|
||||
*/
|
||||
if (SDL_GetAndroidSDKVersion() < 27) {
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
SDL_zero(ctx);
|
||||
|
||||
ctx.handle = SDL_LoadObject(LIB_AAUDIO_SO);
|
||||
if (ctx.handle == NULL) {
|
||||
LOGI("SDL couldn't find " LIB_AAUDIO_SO);
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
if (AAUDIO_LoadFunctions(&ctx) < 0) {
|
||||
SDL_UnloadObject(ctx.handle);
|
||||
SDL_zero(ctx);
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
impl->ThreadInit = Android_AudioThreadInit;
|
||||
impl->Deinitialize = AAUDIO_Deinitialize;
|
||||
impl->OpenDevice = AAUDIO_OpenDevice;
|
||||
impl->CloseDevice = AAUDIO_CloseDevice;
|
||||
impl->WaitDevice = AAUDIO_WaitDevice;
|
||||
impl->PlayDevice = AAUDIO_PlayDevice;
|
||||
impl->GetDeviceBuf = AAUDIO_GetDeviceBuf;
|
||||
impl->WaitCaptureDevice = AAUDIO_WaitDevice;
|
||||
impl->CaptureFromDevice = AAUDIO_CaptureFromDevice;
|
||||
|
||||
impl->HasCaptureSupport = SDL_TRUE;
|
||||
|
||||
#if ALLOW_MULTIPLE_ANDROID_AUDIO_DEVICES
|
||||
impl->DetectDevices = Android_StartAudioHotplug;
|
||||
#else
|
||||
impl->OnlyHasDefaultOutputDevice = SDL_TRUE;
|
||||
impl->OnlyHasDefaultCaptureDevice = SDL_TRUE;
|
||||
#endif
|
||||
|
||||
LOGI("SDL AAUDIO_Init OK");
|
||||
return SDL_TRUE;
|
||||
}
|
||||
|
||||
AudioBootStrap AAUDIO_bootstrap = {
|
||||
"AAudio", "AAudio audio driver", AAUDIO_Init, SDL_FALSE
|
||||
};
|
||||
|
||||
#endif // SDL_AUDIO_DRIVER_AAUDIO
|
||||
38
vendor/sdl-3.0.0/src/audio/aaudio/SDL_aaudio.h
vendored
Normal file
38
vendor/sdl-3.0.0/src/audio/aaudio/SDL_aaudio.h
vendored
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifndef SDL_aaudio_h_
|
||||
#define SDL_aaudio_h_
|
||||
|
||||
#ifdef SDL_AUDIO_DRIVER_AAUDIO
|
||||
|
||||
void AAUDIO_ResumeDevices(void);
|
||||
void AAUDIO_PauseDevices(void);
|
||||
|
||||
#else
|
||||
|
||||
#define AAUDIO_ResumeDevices()
|
||||
#define AAUDIO_PauseDevices()
|
||||
|
||||
#endif
|
||||
|
||||
#endif // SDL_aaudio_h_
|
||||
82
vendor/sdl-3.0.0/src/audio/aaudio/SDL_aaudiofuncs.h
vendored
Normal file
82
vendor/sdl-3.0.0/src/audio/aaudio/SDL_aaudiofuncs.h
vendored
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright , (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
#define SDL_PROC_UNUSED(ret, func, params)
|
||||
|
||||
SDL_PROC(const char *, AAudio_convertResultToText, (aaudio_result_t returnCode))
|
||||
SDL_PROC(const char *, AAudio_convertStreamStateToText, (aaudio_stream_state_t state))
|
||||
SDL_PROC(aaudio_result_t, AAudio_createStreamBuilder, (AAudioStreamBuilder * *builder))
|
||||
SDL_PROC(void, AAudioStreamBuilder_setDeviceId, (AAudioStreamBuilder * builder, int32_t deviceId))
|
||||
SDL_PROC(void, AAudioStreamBuilder_setSampleRate, (AAudioStreamBuilder * builder, int32_t sampleRate))
|
||||
SDL_PROC(void, AAudioStreamBuilder_setChannelCount, (AAudioStreamBuilder * builder, int32_t channelCount))
|
||||
SDL_PROC_UNUSED(void, AAudioStreamBuilder_setSamplesPerFrame, (AAudioStreamBuilder * builder, int32_t samplesPerFrame))
|
||||
SDL_PROC(void, AAudioStreamBuilder_setFormat, (AAudioStreamBuilder * builder, aaudio_format_t format))
|
||||
SDL_PROC_UNUSED(void, AAudioStreamBuilder_setSharingMode, (AAudioStreamBuilder * builder, aaudio_sharing_mode_t sharingMode))
|
||||
SDL_PROC(void, AAudioStreamBuilder_setDirection, (AAudioStreamBuilder * builder, aaudio_direction_t direction))
|
||||
SDL_PROC_UNUSED(void, AAudioStreamBuilder_setBufferCapacityInFrames, (AAudioStreamBuilder * builder, int32_t numFrames))
|
||||
SDL_PROC(void, AAudioStreamBuilder_setPerformanceMode, (AAudioStreamBuilder * builder, aaudio_performance_mode_t mode))
|
||||
SDL_PROC_UNUSED(void, AAudioStreamBuilder_setUsage, (AAudioStreamBuilder * builder, aaudio_usage_t usage)) // API 28
|
||||
SDL_PROC_UNUSED(void, AAudioStreamBuilder_setContentType, (AAudioStreamBuilder * builder, aaudio_content_type_t contentType)) // API 28
|
||||
SDL_PROC_UNUSED(void, AAudioStreamBuilder_setInputPreset, (AAudioStreamBuilder * builder, aaudio_input_preset_t inputPreset)) // API 28
|
||||
SDL_PROC_UNUSED(void, AAudioStreamBuilder_setAllowedCapturePolicy, (AAudioStreamBuilder * builder, aaudio_allowed_capture_policy_t capturePolicy)) // API 29
|
||||
SDL_PROC_UNUSED(void, AAudioStreamBuilder_setSessionId, (AAudioStreamBuilder * builder, aaudio_session_id_t sessionId)) // API 28
|
||||
SDL_PROC_UNUSED(void, AAudioStreamBuilder_setPrivacySensitive, (AAudioStreamBuilder * builder, bool privacySensitive)) // API 30
|
||||
SDL_PROC(void, AAudioStreamBuilder_setDataCallback, (AAudioStreamBuilder * builder, AAudioStream_dataCallback callback, void *userData))
|
||||
SDL_PROC(void, AAudioStreamBuilder_setFramesPerDataCallback, (AAudioStreamBuilder * builder, int32_t numFrames))
|
||||
SDL_PROC(void, AAudioStreamBuilder_setErrorCallback, (AAudioStreamBuilder * builder, AAudioStream_errorCallback callback, void *userData))
|
||||
SDL_PROC(aaudio_result_t, AAudioStreamBuilder_openStream, (AAudioStreamBuilder * builder, AAudioStream **stream))
|
||||
SDL_PROC(aaudio_result_t, AAudioStreamBuilder_delete, (AAudioStreamBuilder * builder))
|
||||
SDL_PROC_UNUSED(aaudio_result_t, AAudioStream_release, (AAudioStream * stream)) // API 30
|
||||
SDL_PROC(aaudio_result_t, AAudioStream_close, (AAudioStream * stream))
|
||||
SDL_PROC(aaudio_result_t, AAudioStream_requestStart, (AAudioStream * stream))
|
||||
SDL_PROC(aaudio_result_t, AAudioStream_requestPause, (AAudioStream * stream))
|
||||
SDL_PROC_UNUSED(aaudio_result_t, AAudioStream_requestFlush, (AAudioStream * stream))
|
||||
SDL_PROC(aaudio_result_t, AAudioStream_requestStop, (AAudioStream * stream))
|
||||
SDL_PROC(aaudio_stream_state_t, AAudioStream_getState, (AAudioStream * stream))
|
||||
SDL_PROC_UNUSED(aaudio_result_t, AAudioStream_waitForStateChange, (AAudioStream * stream, aaudio_stream_state_t inputState, aaudio_stream_state_t *nextState, int64_t timeoutNanoseconds))
|
||||
SDL_PROC_UNUSED(aaudio_result_t, AAudioStream_read, (AAudioStream * stream, void *buffer, int32_t numFrames, int64_t timeoutNanoseconds))
|
||||
SDL_PROC_UNUSED(aaudio_result_t, AAudioStream_write, (AAudioStream * stream, const void *buffer, int32_t numFrames, int64_t timeoutNanoseconds))
|
||||
SDL_PROC_UNUSED(aaudio_result_t, AAudioStream_setBufferSizeInFrames, (AAudioStream * stream, int32_t numFrames))
|
||||
SDL_PROC_UNUSED(int32_t, AAudioStream_getBufferSizeInFrames, (AAudioStream * stream))
|
||||
SDL_PROC_UNUSED(int32_t, AAudioStream_getFramesPerBurst, (AAudioStream * stream))
|
||||
SDL_PROC(int32_t, AAudioStream_getBufferCapacityInFrames, (AAudioStream * stream))
|
||||
SDL_PROC(int32_t, AAudioStream_getFramesPerDataCallback, (AAudioStream * stream))
|
||||
SDL_PROC_UNUSED(int32_t, AAudioStream_getXRunCount, (AAudioStream * stream))
|
||||
SDL_PROC(int32_t, AAudioStream_getSampleRate, (AAudioStream * stream))
|
||||
SDL_PROC(int32_t, AAudioStream_getChannelCount, (AAudioStream * stream))
|
||||
SDL_PROC_UNUSED(int32_t, AAudioStream_getSamplesPerFrame, (AAudioStream * stream))
|
||||
SDL_PROC_UNUSED(int32_t, AAudioStream_getDeviceId, (AAudioStream * stream))
|
||||
SDL_PROC(aaudio_format_t, AAudioStream_getFormat, (AAudioStream * stream))
|
||||
SDL_PROC_UNUSED(aaudio_sharing_mode_t, AAudioStream_getSharingMode, (AAudioStream * stream))
|
||||
SDL_PROC_UNUSED(aaudio_performance_mode_t, AAudioStream_getPerformanceMode, (AAudioStream * stream))
|
||||
SDL_PROC_UNUSED(aaudio_direction_t, AAudioStream_getDirection, (AAudioStream * stream))
|
||||
SDL_PROC_UNUSED(int64_t, AAudioStream_getFramesWritten, (AAudioStream * stream))
|
||||
SDL_PROC_UNUSED(int64_t, AAudioStream_getFramesRead, (AAudioStream * stream))
|
||||
SDL_PROC_UNUSED(aaudio_session_id_t, AAudioStream_getSessionId, (AAudioStream * stream)) // API 28
|
||||
SDL_PROC(aaudio_result_t, AAudioStream_getTimestamp, (AAudioStream * stream, clockid_t clockid, int64_t *framePosition, int64_t *timeNanoseconds))
|
||||
SDL_PROC_UNUSED(aaudio_usage_t, AAudioStream_getUsage, (AAudioStream * stream)) // API 28
|
||||
SDL_PROC_UNUSED(aaudio_content_type_t, AAudioStream_getContentType, (AAudioStream * stream)) // API 28
|
||||
SDL_PROC_UNUSED(aaudio_input_preset_t, AAudioStream_getInputPreset, (AAudioStream * stream)) // API 28
|
||||
SDL_PROC_UNUSED(aaudio_allowed_capture_policy_t, AAudioStream_getAllowedCapturePolicy, (AAudioStream * stream)) // API 29
|
||||
SDL_PROC_UNUSED(bool, AAudioStream_isPrivacySensitive, (AAudioStream * stream)) // API 30
|
||||
|
||||
#undef SDL_PROC
|
||||
#undef SDL_PROC_UNUSED
|
||||
994
vendor/sdl-3.0.0/src/audio/alsa/SDL_alsa_audio.c
vendored
Normal file
994
vendor/sdl-3.0.0/src/audio/alsa/SDL_alsa_audio.c
vendored
Normal file
|
|
@ -0,0 +1,994 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifdef SDL_AUDIO_DRIVER_ALSA
|
||||
|
||||
#ifndef SDL_ALSA_NON_BLOCKING
|
||||
#define SDL_ALSA_NON_BLOCKING 0
|
||||
#endif
|
||||
|
||||
// without the thread, you will detect devices on startup, but will not get further hotplug events. But that might be okay.
|
||||
#ifndef SDL_ALSA_HOTPLUG_THREAD
|
||||
#define SDL_ALSA_HOTPLUG_THREAD 1
|
||||
#endif
|
||||
|
||||
// Allow access to a raw mixing buffer
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <signal.h> // For kill()
|
||||
#include <string.h>
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
#include "SDL_alsa_audio.h"
|
||||
|
||||
#ifdef SDL_AUDIO_DRIVER_ALSA_DYNAMIC
|
||||
#endif
|
||||
|
||||
static int (*ALSA_snd_pcm_open)(snd_pcm_t **, const char *, snd_pcm_stream_t, int);
|
||||
static int (*ALSA_snd_pcm_close)(snd_pcm_t *pcm);
|
||||
static int (*ALSA_snd_pcm_start)(snd_pcm_t *pcm);
|
||||
static snd_pcm_sframes_t (*ALSA_snd_pcm_writei)(snd_pcm_t *, const void *, snd_pcm_uframes_t);
|
||||
static snd_pcm_sframes_t (*ALSA_snd_pcm_readi)(snd_pcm_t *, void *, snd_pcm_uframes_t);
|
||||
static int (*ALSA_snd_pcm_recover)(snd_pcm_t *, int, int);
|
||||
static int (*ALSA_snd_pcm_prepare)(snd_pcm_t *);
|
||||
static int (*ALSA_snd_pcm_drain)(snd_pcm_t *);
|
||||
static const char *(*ALSA_snd_strerror)(int);
|
||||
static size_t (*ALSA_snd_pcm_hw_params_sizeof)(void);
|
||||
static size_t (*ALSA_snd_pcm_sw_params_sizeof)(void);
|
||||
static void (*ALSA_snd_pcm_hw_params_copy)(snd_pcm_hw_params_t *, const snd_pcm_hw_params_t *);
|
||||
static int (*ALSA_snd_pcm_hw_params_any)(snd_pcm_t *, snd_pcm_hw_params_t *);
|
||||
static int (*ALSA_snd_pcm_hw_params_set_access)(snd_pcm_t *, snd_pcm_hw_params_t *, snd_pcm_access_t);
|
||||
static int (*ALSA_snd_pcm_hw_params_set_format)(snd_pcm_t *, snd_pcm_hw_params_t *, snd_pcm_format_t);
|
||||
static int (*ALSA_snd_pcm_hw_params_set_channels)(snd_pcm_t *, snd_pcm_hw_params_t *, unsigned int);
|
||||
static int (*ALSA_snd_pcm_hw_params_get_channels)(const snd_pcm_hw_params_t *, unsigned int *);
|
||||
static int (*ALSA_snd_pcm_hw_params_set_rate_near)(snd_pcm_t *, snd_pcm_hw_params_t *, unsigned int *, int *);
|
||||
static int (*ALSA_snd_pcm_hw_params_set_period_size_near)(snd_pcm_t *, snd_pcm_hw_params_t *, snd_pcm_uframes_t *, int *);
|
||||
static int (*ALSA_snd_pcm_hw_params_get_period_size)(const snd_pcm_hw_params_t *, snd_pcm_uframes_t *, int *);
|
||||
static int (*ALSA_snd_pcm_hw_params_set_periods_min)(snd_pcm_t *, snd_pcm_hw_params_t *, unsigned int *, int *);
|
||||
static int (*ALSA_snd_pcm_hw_params_set_periods_first)(snd_pcm_t *, snd_pcm_hw_params_t *, unsigned int *, int *);
|
||||
static int (*ALSA_snd_pcm_hw_params_get_periods)(const snd_pcm_hw_params_t *, unsigned int *, int *);
|
||||
static int (*ALSA_snd_pcm_hw_params_set_buffer_size_near)(snd_pcm_t *pcm, snd_pcm_hw_params_t *, snd_pcm_uframes_t *);
|
||||
static int (*ALSA_snd_pcm_hw_params_get_buffer_size)(const snd_pcm_hw_params_t *, snd_pcm_uframes_t *);
|
||||
static int (*ALSA_snd_pcm_hw_params)(snd_pcm_t *, snd_pcm_hw_params_t *);
|
||||
static int (*ALSA_snd_pcm_sw_params_current)(snd_pcm_t *,
|
||||
snd_pcm_sw_params_t *);
|
||||
static int (*ALSA_snd_pcm_sw_params_set_start_threshold)(snd_pcm_t *, snd_pcm_sw_params_t *, snd_pcm_uframes_t);
|
||||
static int (*ALSA_snd_pcm_sw_params)(snd_pcm_t *, snd_pcm_sw_params_t *);
|
||||
static int (*ALSA_snd_pcm_nonblock)(snd_pcm_t *, int);
|
||||
static int (*ALSA_snd_pcm_wait)(snd_pcm_t *, int);
|
||||
static int (*ALSA_snd_pcm_sw_params_set_avail_min)(snd_pcm_t *, snd_pcm_sw_params_t *, snd_pcm_uframes_t);
|
||||
static int (*ALSA_snd_pcm_reset)(snd_pcm_t *);
|
||||
static int (*ALSA_snd_device_name_hint)(int, const char *, void ***);
|
||||
static char *(*ALSA_snd_device_name_get_hint)(const void *, const char *);
|
||||
static int (*ALSA_snd_device_name_free_hint)(void **);
|
||||
static snd_pcm_sframes_t (*ALSA_snd_pcm_avail)(snd_pcm_t *);
|
||||
#ifdef SND_CHMAP_API_VERSION
|
||||
static snd_pcm_chmap_t *(*ALSA_snd_pcm_get_chmap)(snd_pcm_t *);
|
||||
static int (*ALSA_snd_pcm_chmap_print)(const snd_pcm_chmap_t *map, size_t maxlen, char *buf);
|
||||
#endif
|
||||
|
||||
#ifdef SDL_AUDIO_DRIVER_ALSA_DYNAMIC
|
||||
#define snd_pcm_hw_params_sizeof ALSA_snd_pcm_hw_params_sizeof
|
||||
#define snd_pcm_sw_params_sizeof ALSA_snd_pcm_sw_params_sizeof
|
||||
|
||||
static const char *alsa_library = SDL_AUDIO_DRIVER_ALSA_DYNAMIC;
|
||||
static void *alsa_handle = NULL;
|
||||
|
||||
static int load_alsa_sym(const char *fn, void **addr)
|
||||
{
|
||||
*addr = SDL_LoadFunction(alsa_handle, fn);
|
||||
if (*addr == NULL) {
|
||||
// Don't call SDL_SetError(): SDL_LoadFunction already did.
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
// cast funcs to char* first, to please GCC's strict aliasing rules.
|
||||
#define SDL_ALSA_SYM(x) \
|
||||
if (!load_alsa_sym(#x, (void **)(char *)&ALSA_##x)) \
|
||||
return -1
|
||||
#else
|
||||
#define SDL_ALSA_SYM(x) ALSA_##x = x
|
||||
#endif
|
||||
|
||||
static int load_alsa_syms(void)
|
||||
{
|
||||
SDL_ALSA_SYM(snd_pcm_open);
|
||||
SDL_ALSA_SYM(snd_pcm_close);
|
||||
SDL_ALSA_SYM(snd_pcm_start);
|
||||
SDL_ALSA_SYM(snd_pcm_writei);
|
||||
SDL_ALSA_SYM(snd_pcm_readi);
|
||||
SDL_ALSA_SYM(snd_pcm_recover);
|
||||
SDL_ALSA_SYM(snd_pcm_prepare);
|
||||
SDL_ALSA_SYM(snd_pcm_drain);
|
||||
SDL_ALSA_SYM(snd_strerror);
|
||||
SDL_ALSA_SYM(snd_pcm_hw_params_sizeof);
|
||||
SDL_ALSA_SYM(snd_pcm_sw_params_sizeof);
|
||||
SDL_ALSA_SYM(snd_pcm_hw_params_copy);
|
||||
SDL_ALSA_SYM(snd_pcm_hw_params_any);
|
||||
SDL_ALSA_SYM(snd_pcm_hw_params_set_access);
|
||||
SDL_ALSA_SYM(snd_pcm_hw_params_set_format);
|
||||
SDL_ALSA_SYM(snd_pcm_hw_params_set_channels);
|
||||
SDL_ALSA_SYM(snd_pcm_hw_params_get_channels);
|
||||
SDL_ALSA_SYM(snd_pcm_hw_params_set_rate_near);
|
||||
SDL_ALSA_SYM(snd_pcm_hw_params_set_period_size_near);
|
||||
SDL_ALSA_SYM(snd_pcm_hw_params_get_period_size);
|
||||
SDL_ALSA_SYM(snd_pcm_hw_params_set_periods_min);
|
||||
SDL_ALSA_SYM(snd_pcm_hw_params_set_periods_first);
|
||||
SDL_ALSA_SYM(snd_pcm_hw_params_get_periods);
|
||||
SDL_ALSA_SYM(snd_pcm_hw_params_set_buffer_size_near);
|
||||
SDL_ALSA_SYM(snd_pcm_hw_params_get_buffer_size);
|
||||
SDL_ALSA_SYM(snd_pcm_hw_params);
|
||||
SDL_ALSA_SYM(snd_pcm_sw_params_current);
|
||||
SDL_ALSA_SYM(snd_pcm_sw_params_set_start_threshold);
|
||||
SDL_ALSA_SYM(snd_pcm_sw_params);
|
||||
SDL_ALSA_SYM(snd_pcm_nonblock);
|
||||
SDL_ALSA_SYM(snd_pcm_wait);
|
||||
SDL_ALSA_SYM(snd_pcm_sw_params_set_avail_min);
|
||||
SDL_ALSA_SYM(snd_pcm_reset);
|
||||
SDL_ALSA_SYM(snd_device_name_hint);
|
||||
SDL_ALSA_SYM(snd_device_name_get_hint);
|
||||
SDL_ALSA_SYM(snd_device_name_free_hint);
|
||||
SDL_ALSA_SYM(snd_pcm_avail);
|
||||
#ifdef SND_CHMAP_API_VERSION
|
||||
SDL_ALSA_SYM(snd_pcm_get_chmap);
|
||||
SDL_ALSA_SYM(snd_pcm_chmap_print);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#undef SDL_ALSA_SYM
|
||||
|
||||
#ifdef SDL_AUDIO_DRIVER_ALSA_DYNAMIC
|
||||
|
||||
static void UnloadALSALibrary(void)
|
||||
{
|
||||
if (alsa_handle != NULL) {
|
||||
SDL_UnloadObject(alsa_handle);
|
||||
alsa_handle = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int LoadALSALibrary(void)
|
||||
{
|
||||
int retval = 0;
|
||||
if (alsa_handle == NULL) {
|
||||
alsa_handle = SDL_LoadObject(alsa_library);
|
||||
if (alsa_handle == NULL) {
|
||||
retval = -1;
|
||||
// Don't call SDL_SetError(): SDL_LoadObject already did.
|
||||
} else {
|
||||
retval = load_alsa_syms();
|
||||
if (retval < 0) {
|
||||
UnloadALSALibrary();
|
||||
}
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static void UnloadALSALibrary(void)
|
||||
{
|
||||
}
|
||||
|
||||
static int LoadALSALibrary(void)
|
||||
{
|
||||
load_alsa_syms();
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif // SDL_AUDIO_DRIVER_ALSA_DYNAMIC
|
||||
|
||||
typedef struct ALSA_Device
|
||||
{
|
||||
char *name;
|
||||
SDL_bool iscapture;
|
||||
struct ALSA_Device *next;
|
||||
} ALSA_Device;
|
||||
|
||||
static const ALSA_Device default_output_handle = {
|
||||
"default",
|
||||
SDL_FALSE,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const ALSA_Device default_capture_handle = {
|
||||
"default",
|
||||
SDL_TRUE,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const char *get_audio_device(void *handle, const int channels)
|
||||
{
|
||||
SDL_assert(handle != NULL); // SDL2 used NULL to mean "default" but that's not true in SDL3.
|
||||
|
||||
ALSA_Device *dev = (ALSA_Device *)handle;
|
||||
if (SDL_strcmp(dev->name, "default") == 0) {
|
||||
const char *device = SDL_getenv("AUDIODEV"); // Is there a standard variable name?
|
||||
if (device != NULL) {
|
||||
return device;
|
||||
} else if (channels == 6) {
|
||||
return "plug:surround51";
|
||||
} else if (channels == 4) {
|
||||
return "plug:surround40";
|
||||
}
|
||||
return "default";
|
||||
}
|
||||
|
||||
return dev->name;
|
||||
}
|
||||
|
||||
// !!! FIXME: is there a channel swizzler in alsalib instead?
|
||||
|
||||
// https://bugzilla.libsdl.org/show_bug.cgi?id=110
|
||||
// "For Linux ALSA, this is FL-FR-RL-RR-C-LFE
|
||||
// and for Windows DirectX [and CoreAudio], this is FL-FR-C-LFE-RL-RR"
|
||||
#define SWIZ6(T) \
|
||||
static void swizzle_alsa_channels_6_##T(void *buffer, const Uint32 bufferlen) \
|
||||
{ \
|
||||
T *ptr = (T *)buffer; \
|
||||
Uint32 i; \
|
||||
for (i = 0; i < bufferlen; i++, ptr += 6) { \
|
||||
T tmp; \
|
||||
tmp = ptr[2]; \
|
||||
ptr[2] = ptr[4]; \
|
||||
ptr[4] = tmp; \
|
||||
tmp = ptr[3]; \
|
||||
ptr[3] = ptr[5]; \
|
||||
ptr[5] = tmp; \
|
||||
} \
|
||||
}
|
||||
|
||||
|
||||
// !!! FIXME: is there a channel swizzler in alsalib instead?
|
||||
// !!! FIXME: this screams for a SIMD shuffle operation.
|
||||
|
||||
// https://docs.microsoft.com/en-us/windows-hardware/drivers/audio/mapping-stream-formats-to-speaker-configurations
|
||||
// For Linux ALSA, this appears to be FL-FR-RL-RR-C-LFE-SL-SR
|
||||
// and for Windows DirectX [and CoreAudio], this is FL-FR-C-LFE-SL-SR-RL-RR"
|
||||
#define SWIZ8(T) \
|
||||
static void swizzle_alsa_channels_8_##T(void *buffer, const Uint32 bufferlen) \
|
||||
{ \
|
||||
T *ptr = (T *)buffer; \
|
||||
Uint32 i; \
|
||||
for (i = 0; i < bufferlen; i++, ptr += 6) { \
|
||||
const T center = ptr[2]; \
|
||||
const T subwoofer = ptr[3]; \
|
||||
const T side_left = ptr[4]; \
|
||||
const T side_right = ptr[5]; \
|
||||
const T rear_left = ptr[6]; \
|
||||
const T rear_right = ptr[7]; \
|
||||
ptr[2] = rear_left; \
|
||||
ptr[3] = rear_right; \
|
||||
ptr[4] = center; \
|
||||
ptr[5] = subwoofer; \
|
||||
ptr[6] = side_left; \
|
||||
ptr[7] = side_right; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define CHANNEL_SWIZZLE(x) \
|
||||
x(Uint64) \
|
||||
x(Uint32) \
|
||||
x(Uint16) \
|
||||
x(Uint8)
|
||||
|
||||
CHANNEL_SWIZZLE(SWIZ6)
|
||||
CHANNEL_SWIZZLE(SWIZ8)
|
||||
|
||||
#undef CHANNEL_SWIZZLE
|
||||
#undef SWIZ6
|
||||
#undef SWIZ8
|
||||
|
||||
// Called right before feeding device->hidden->mixbuf to the hardware. Swizzle
|
||||
// channels from Windows/Mac order to the format alsalib will want.
|
||||
static void swizzle_alsa_channels(SDL_AudioDevice *device, void *buffer, Uint32 bufferlen)
|
||||
{
|
||||
switch (device->spec.channels) {
|
||||
#define CHANSWIZ(chans) \
|
||||
case chans: \
|
||||
switch ((device->spec.format & (0xFF))) { \
|
||||
case 8: \
|
||||
swizzle_alsa_channels_##chans##_Uint8(buffer, bufferlen); \
|
||||
break; \
|
||||
case 16: \
|
||||
swizzle_alsa_channels_##chans##_Uint16(buffer, bufferlen); \
|
||||
break; \
|
||||
case 32: \
|
||||
swizzle_alsa_channels_##chans##_Uint32(buffer, bufferlen); \
|
||||
break; \
|
||||
case 64: \
|
||||
swizzle_alsa_channels_##chans##_Uint64(buffer, bufferlen); \
|
||||
break; \
|
||||
default: \
|
||||
SDL_assert(!"unhandled bitsize"); \
|
||||
break; \
|
||||
} \
|
||||
return;
|
||||
|
||||
CHANSWIZ(6);
|
||||
CHANSWIZ(8);
|
||||
#undef CHANSWIZ
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef SND_CHMAP_API_VERSION
|
||||
// Some devices have the right channel map, no swizzling necessary
|
||||
static void no_swizzle(SDL_AudioDevice *device, void *buffer, Uint32 bufferlen)
|
||||
{
|
||||
}
|
||||
#endif // SND_CHMAP_API_VERSION
|
||||
|
||||
// This function waits until it is possible to write a full sound buffer
|
||||
static int ALSA_WaitDevice(SDL_AudioDevice *device)
|
||||
{
|
||||
const int fulldelay = (int) ((((Uint64) device->sample_frames) * 1000) / device->spec.freq);
|
||||
const int delay = SDL_max(fulldelay, 10);
|
||||
|
||||
while (!SDL_AtomicGet(&device->shutdown)) {
|
||||
const int rc = ALSA_snd_pcm_wait(device->hidden->pcm_handle, delay);
|
||||
if (rc < 0 && (rc != -EAGAIN)) {
|
||||
const int status = ALSA_snd_pcm_recover(device->hidden->pcm_handle, rc, 0);
|
||||
if (status < 0) {
|
||||
// Hmm, not much we can do - abort
|
||||
SDL_LogError(SDL_LOG_CATEGORY_AUDIO, "ALSA: snd_pcm_wait failed (unrecoverable): %s", ALSA_snd_strerror(rc));
|
||||
return -1;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (rc > 0) {
|
||||
break; // ready to go!
|
||||
}
|
||||
|
||||
// Timed out! Make sure we aren't shutting down and then wait again.
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ALSA_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
|
||||
{
|
||||
SDL_assert(buffer == device->hidden->mixbuf);
|
||||
Uint8 *sample_buf = (Uint8 *) buffer; // !!! FIXME: deal with this without casting away constness
|
||||
const int frame_size = SDL_AUDIO_FRAMESIZE(device->spec);
|
||||
snd_pcm_uframes_t frames_left = (snd_pcm_uframes_t) (buflen / frame_size);
|
||||
|
||||
device->hidden->swizzle_func(device, sample_buf, frames_left);
|
||||
|
||||
while ((frames_left > 0) && !SDL_AtomicGet(&device->shutdown)) {
|
||||
const int rc = ALSA_snd_pcm_writei(device->hidden->pcm_handle, sample_buf, frames_left);
|
||||
//SDL_LogInfo(SDL_LOG_CATEGORY_AUDIO, "ALSA PLAYDEVICE: WROTE %d of %d bytes", (rc >= 0) ? ((int) (rc * frame_size)) : rc, (int) (frames_left * frame_size));
|
||||
SDL_assert(rc != 0); // assuming this can't happen if we used snd_pcm_wait and queried for available space.
|
||||
if (rc < 0) {
|
||||
SDL_assert(rc != -EAGAIN); // assuming this can't happen if we used snd_pcm_wait and queried for available space. snd_pcm_recover won't handle it!
|
||||
const int status = ALSA_snd_pcm_recover(device->hidden->pcm_handle, rc, 0);
|
||||
if (status < 0) {
|
||||
// Hmm, not much we can do - abort
|
||||
SDL_LogError(SDL_LOG_CATEGORY_AUDIO, "ALSA write failed (unrecoverable): %s", ALSA_snd_strerror(rc));
|
||||
return -1;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
sample_buf += rc * frame_size;
|
||||
frames_left -= rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static Uint8 *ALSA_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
|
||||
{
|
||||
snd_pcm_sframes_t rc = ALSA_snd_pcm_avail(device->hidden->pcm_handle);
|
||||
if (rc <= 0) {
|
||||
// Wait a bit and try again, maybe the hardware isn't quite ready yet?
|
||||
SDL_Delay(1);
|
||||
|
||||
rc = ALSA_snd_pcm_avail(device->hidden->pcm_handle);
|
||||
if (rc <= 0) {
|
||||
// We'll catch it next time
|
||||
*buffer_size = 0;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
const int requested_frames = SDL_min(device->sample_frames, rc);
|
||||
const int requested_bytes = requested_frames * SDL_AUDIO_FRAMESIZE(device->spec);
|
||||
SDL_assert(requested_bytes <= *buffer_size);
|
||||
//SDL_LogInfo(SDL_LOG_CATEGORY_AUDIO, "ALSA GETDEVICEBUF: NEED %d BYTES", requested_bytes);
|
||||
*buffer_size = requested_bytes;
|
||||
return device->hidden->mixbuf;
|
||||
}
|
||||
|
||||
static int ALSA_CaptureFromDevice(SDL_AudioDevice *device, void *buffer, int buflen)
|
||||
{
|
||||
const int frame_size = SDL_AUDIO_FRAMESIZE(device->spec);
|
||||
SDL_assert((buflen % frame_size) == 0);
|
||||
|
||||
const snd_pcm_sframes_t total_available = ALSA_snd_pcm_avail(device->hidden->pcm_handle);
|
||||
const int total_frames = SDL_min(buflen / frame_size, total_available);
|
||||
|
||||
const int rc = ALSA_snd_pcm_readi(device->hidden->pcm_handle, buffer, total_frames);
|
||||
|
||||
SDL_assert(rc != -EAGAIN); // assuming this can't happen if we used snd_pcm_wait and queried for available space. snd_pcm_recover won't handle it!
|
||||
|
||||
if (rc < 0) {
|
||||
const int status = ALSA_snd_pcm_recover(device->hidden->pcm_handle, rc, 0);
|
||||
if (status < 0) {
|
||||
// Hmm, not much we can do - abort
|
||||
SDL_LogError(SDL_LOG_CATEGORY_AUDIO, "ALSA read failed (unrecoverable): %s", ALSA_snd_strerror(rc));
|
||||
return -1;
|
||||
}
|
||||
return 0; // go back to WaitDevice and try again.
|
||||
} else if (rc > 0) {
|
||||
device->hidden->swizzle_func(device, buffer, total_frames - rc);
|
||||
}
|
||||
|
||||
//SDL_LogInfo(SDL_LOG_CATEGORY_AUDIO, "ALSA: captured %d bytes", rc * frame_size);
|
||||
|
||||
return rc * frame_size;
|
||||
}
|
||||
|
||||
static void ALSA_FlushCapture(SDL_AudioDevice *device)
|
||||
{
|
||||
ALSA_snd_pcm_reset(device->hidden->pcm_handle);
|
||||
}
|
||||
|
||||
static void ALSA_CloseDevice(SDL_AudioDevice *device)
|
||||
{
|
||||
if (device->hidden) {
|
||||
if (device->hidden->pcm_handle) {
|
||||
// Wait for the submitted audio to drain. ALSA_snd_pcm_drop() can hang, so don't use that.
|
||||
SDL_Delay(((device->sample_frames * 1000) / device->spec.freq) * 2);
|
||||
ALSA_snd_pcm_close(device->hidden->pcm_handle);
|
||||
}
|
||||
SDL_free(device->hidden->mixbuf);
|
||||
SDL_free(device->hidden);
|
||||
}
|
||||
}
|
||||
|
||||
static int ALSA_set_buffer_size(SDL_AudioDevice *device, snd_pcm_hw_params_t *params)
|
||||
{
|
||||
int status;
|
||||
snd_pcm_hw_params_t *hwparams;
|
||||
snd_pcm_uframes_t persize;
|
||||
unsigned int periods;
|
||||
|
||||
// Copy the hardware parameters for this setup
|
||||
snd_pcm_hw_params_alloca(&hwparams);
|
||||
ALSA_snd_pcm_hw_params_copy(hwparams, params);
|
||||
|
||||
// Attempt to match the period size to the requested buffer size
|
||||
persize = device->sample_frames;
|
||||
status = ALSA_snd_pcm_hw_params_set_period_size_near(
|
||||
device->hidden->pcm_handle, hwparams, &persize, NULL);
|
||||
if (status < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Need to at least double buffer
|
||||
periods = 2;
|
||||
status = ALSA_snd_pcm_hw_params_set_periods_min(
|
||||
device->hidden->pcm_handle, hwparams, &periods, NULL);
|
||||
if (status < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
status = ALSA_snd_pcm_hw_params_set_periods_first(
|
||||
device->hidden->pcm_handle, hwparams, &periods, NULL);
|
||||
if (status < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// "set" the hardware with the desired parameters
|
||||
status = ALSA_snd_pcm_hw_params(device->hidden->pcm_handle, hwparams);
|
||||
if (status < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
device->sample_frames = persize;
|
||||
|
||||
// This is useful for debugging
|
||||
if (SDL_getenv("SDL_AUDIO_ALSA_DEBUG")) {
|
||||
snd_pcm_uframes_t bufsize;
|
||||
|
||||
ALSA_snd_pcm_hw_params_get_buffer_size(hwparams, &bufsize);
|
||||
|
||||
SDL_LogError(SDL_LOG_CATEGORY_AUDIO,
|
||||
"ALSA: period size = %ld, periods = %u, buffer size = %lu",
|
||||
persize, periods, bufsize);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ALSA_OpenDevice(SDL_AudioDevice *device)
|
||||
{
|
||||
const SDL_bool iscapture = device->iscapture;
|
||||
int status = 0;
|
||||
|
||||
// Initialize all variables that we clean on shutdown
|
||||
device->hidden = (struct SDL_PrivateAudioData *)SDL_calloc(1, sizeof(*device->hidden));
|
||||
if (device->hidden == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
|
||||
// Open the audio device
|
||||
// Name of device should depend on # channels in spec
|
||||
snd_pcm_t *pcm_handle = NULL;
|
||||
status = ALSA_snd_pcm_open(&pcm_handle,
|
||||
get_audio_device(device->handle, device->spec.channels),
|
||||
iscapture ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK,
|
||||
SND_PCM_NONBLOCK);
|
||||
|
||||
if (status < 0) {
|
||||
return SDL_SetError("ALSA: Couldn't open audio device: %s", ALSA_snd_strerror(status));
|
||||
}
|
||||
|
||||
device->hidden->pcm_handle = pcm_handle;
|
||||
|
||||
// Figure out what the hardware is capable of
|
||||
snd_pcm_hw_params_t *hwparams = NULL;
|
||||
snd_pcm_hw_params_alloca(&hwparams);
|
||||
status = ALSA_snd_pcm_hw_params_any(pcm_handle, hwparams);
|
||||
if (status < 0) {
|
||||
return SDL_SetError("ALSA: Couldn't get hardware config: %s", ALSA_snd_strerror(status));
|
||||
}
|
||||
|
||||
// SDL only uses interleaved sample output
|
||||
status = ALSA_snd_pcm_hw_params_set_access(pcm_handle, hwparams,
|
||||
SND_PCM_ACCESS_RW_INTERLEAVED);
|
||||
if (status < 0) {
|
||||
return SDL_SetError("ALSA: Couldn't set interleaved access: %s", ALSA_snd_strerror(status));
|
||||
}
|
||||
|
||||
// Try for a closest match on audio format
|
||||
snd_pcm_format_t format = 0;
|
||||
const SDL_AudioFormat *closefmts = SDL_ClosestAudioFormats(device->spec.format);
|
||||
SDL_AudioFormat test_format;
|
||||
while ((test_format = *(closefmts++)) != 0) {
|
||||
switch (test_format) {
|
||||
case SDL_AUDIO_U8:
|
||||
format = SND_PCM_FORMAT_U8;
|
||||
break;
|
||||
case SDL_AUDIO_S8:
|
||||
format = SND_PCM_FORMAT_S8;
|
||||
break;
|
||||
case SDL_AUDIO_S16LE:
|
||||
format = SND_PCM_FORMAT_S16_LE;
|
||||
break;
|
||||
case SDL_AUDIO_S16BE:
|
||||
format = SND_PCM_FORMAT_S16_BE;
|
||||
break;
|
||||
case SDL_AUDIO_S32LE:
|
||||
format = SND_PCM_FORMAT_S32_LE;
|
||||
break;
|
||||
case SDL_AUDIO_S32BE:
|
||||
format = SND_PCM_FORMAT_S32_BE;
|
||||
break;
|
||||
case SDL_AUDIO_F32LE:
|
||||
format = SND_PCM_FORMAT_FLOAT_LE;
|
||||
break;
|
||||
case SDL_AUDIO_F32BE:
|
||||
format = SND_PCM_FORMAT_FLOAT_BE;
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
if (ALSA_snd_pcm_hw_params_set_format(pcm_handle, hwparams, format) >= 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!test_format) {
|
||||
return SDL_SetError("ALSA: Unsupported audio format");
|
||||
}
|
||||
device->spec.format = test_format;
|
||||
|
||||
// Validate number of channels and determine if swizzling is necessary.
|
||||
// Assume original swizzling, until proven otherwise.
|
||||
device->hidden->swizzle_func = swizzle_alsa_channels;
|
||||
#ifdef SND_CHMAP_API_VERSION
|
||||
snd_pcm_chmap_t *chmap = ALSA_snd_pcm_get_chmap(pcm_handle);
|
||||
if (chmap) {
|
||||
char chmap_str[64];
|
||||
if (ALSA_snd_pcm_chmap_print(chmap, sizeof(chmap_str), chmap_str) > 0) {
|
||||
if (SDL_strcmp("FL FR FC LFE RL RR", chmap_str) == 0 ||
|
||||
SDL_strcmp("FL FR FC LFE SL SR", chmap_str) == 0) {
|
||||
device->hidden->swizzle_func = no_swizzle;
|
||||
}
|
||||
}
|
||||
free(chmap); // This should NOT be SDL_free()
|
||||
}
|
||||
#endif // SND_CHMAP_API_VERSION
|
||||
|
||||
// Set the number of channels
|
||||
status = ALSA_snd_pcm_hw_params_set_channels(pcm_handle, hwparams,
|
||||
device->spec.channels);
|
||||
unsigned int channels = device->spec.channels;
|
||||
if (status < 0) {
|
||||
status = ALSA_snd_pcm_hw_params_get_channels(hwparams, &channels);
|
||||
if (status < 0) {
|
||||
return SDL_SetError("ALSA: Couldn't set audio channels");
|
||||
}
|
||||
device->spec.channels = channels;
|
||||
}
|
||||
|
||||
// Set the audio rate
|
||||
unsigned int rate = device->spec.freq;
|
||||
status = ALSA_snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams,
|
||||
&rate, NULL);
|
||||
if (status < 0) {
|
||||
return SDL_SetError("ALSA: Couldn't set audio frequency: %s", ALSA_snd_strerror(status));
|
||||
}
|
||||
device->spec.freq = rate;
|
||||
|
||||
// Set the buffer size, in samples
|
||||
status = ALSA_set_buffer_size(device, hwparams);
|
||||
if (status < 0) {
|
||||
return SDL_SetError("Couldn't set hardware audio parameters: %s", ALSA_snd_strerror(status));
|
||||
}
|
||||
|
||||
// Set the software parameters
|
||||
snd_pcm_sw_params_t *swparams = NULL;
|
||||
snd_pcm_sw_params_alloca(&swparams);
|
||||
status = ALSA_snd_pcm_sw_params_current(pcm_handle, swparams);
|
||||
if (status < 0) {
|
||||
return SDL_SetError("ALSA: Couldn't get software config: %s", ALSA_snd_strerror(status));
|
||||
}
|
||||
status = ALSA_snd_pcm_sw_params_set_avail_min(pcm_handle, swparams, device->sample_frames);
|
||||
if (status < 0) {
|
||||
return SDL_SetError("Couldn't set minimum available samples: %s", ALSA_snd_strerror(status));
|
||||
}
|
||||
status =
|
||||
ALSA_snd_pcm_sw_params_set_start_threshold(pcm_handle, swparams, 1);
|
||||
if (status < 0) {
|
||||
return SDL_SetError("ALSA: Couldn't set start threshold: %s", ALSA_snd_strerror(status));
|
||||
}
|
||||
status = ALSA_snd_pcm_sw_params(pcm_handle, swparams);
|
||||
if (status < 0) {
|
||||
return SDL_SetError("Couldn't set software audio parameters: %s", ALSA_snd_strerror(status));
|
||||
}
|
||||
|
||||
// Calculate the final parameters for this audio specification
|
||||
SDL_UpdatedAudioDeviceFormat(device);
|
||||
|
||||
// Allocate mixing buffer
|
||||
if (!iscapture) {
|
||||
device->hidden->mixbuf = (Uint8 *)SDL_malloc(device->buffer_size);
|
||||
if (device->hidden->mixbuf == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
SDL_memset(device->hidden->mixbuf, device->silence_value, device->buffer_size);
|
||||
}
|
||||
|
||||
#if !SDL_ALSA_NON_BLOCKING
|
||||
if (!iscapture) {
|
||||
ALSA_snd_pcm_nonblock(pcm_handle, 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
ALSA_snd_pcm_start(pcm_handle);
|
||||
|
||||
return 0; // We're ready to rock and roll. :-)
|
||||
}
|
||||
|
||||
static void add_device(const SDL_bool iscapture, const char *name, void *hint, ALSA_Device **pSeen)
|
||||
{
|
||||
ALSA_Device *dev = SDL_malloc(sizeof(ALSA_Device));
|
||||
char *desc;
|
||||
char *ptr;
|
||||
|
||||
if (dev == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Not all alsa devices are enumerable via snd_device_name_get_hint
|
||||
// (i.e. bluetooth devices). Therefore if hint is passed in to this
|
||||
// function as NULL, assume name contains desc.
|
||||
// Make sure not to free the storage associated with desc in this case
|
||||
if (hint) {
|
||||
desc = ALSA_snd_device_name_get_hint(hint, "DESC");
|
||||
if (desc == NULL) {
|
||||
SDL_free(dev);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
desc = (char *)name;
|
||||
}
|
||||
|
||||
SDL_assert(name != NULL);
|
||||
|
||||
// some strings have newlines, like "HDA NVidia, HDMI 0\nHDMI Audio Output".
|
||||
// just chop the extra lines off, this seems to get a reasonable device
|
||||
// name without extra details.
|
||||
ptr = SDL_strchr(desc, '\n');
|
||||
if (ptr != NULL) {
|
||||
*ptr = '\0';
|
||||
}
|
||||
|
||||
//SDL_LogInfo(SDL_LOG_CATEGORY_AUDIO, "ALSA: adding %s device '%s' (%s)", iscapture ? "capture" : "output", name, desc);
|
||||
|
||||
dev->name = SDL_strdup(name);
|
||||
if (!dev->name) {
|
||||
if (hint) {
|
||||
free(desc); // This should NOT be SDL_free()
|
||||
}
|
||||
SDL_free(dev->name);
|
||||
SDL_free(dev);
|
||||
return;
|
||||
}
|
||||
|
||||
// Note that spec is NULL, because we are required to open the device before
|
||||
// acquiring the mix format, making this information inaccessible at
|
||||
// enumeration time
|
||||
SDL_AddAudioDevice(iscapture, desc, NULL, dev);
|
||||
if (hint) {
|
||||
free(desc); // This should NOT be SDL_free()
|
||||
}
|
||||
|
||||
dev->iscapture = iscapture;
|
||||
dev->next = *pSeen;
|
||||
*pSeen = dev;
|
||||
}
|
||||
|
||||
static ALSA_Device *hotplug_devices = NULL;
|
||||
|
||||
static void ALSA_HotplugIteration(SDL_bool *has_default_output, SDL_bool *has_default_capture)
|
||||
{
|
||||
void **hints = NULL;
|
||||
ALSA_Device *unseen = NULL;
|
||||
ALSA_Device *seen = NULL;
|
||||
|
||||
if (ALSA_snd_device_name_hint(-1, "pcm", &hints) == 0) {
|
||||
const char *match = NULL;
|
||||
int bestmatch = 0xFFFF;
|
||||
int has_default = -1;
|
||||
size_t match_len = 0;
|
||||
static const char *const prefixes[] = {
|
||||
"hw:", "sysdefault:", "default:", NULL
|
||||
};
|
||||
|
||||
unseen = hotplug_devices;
|
||||
seen = NULL;
|
||||
|
||||
// Apparently there are several different ways that ALSA lists
|
||||
// actual hardware. It could be prefixed with "hw:" or "default:"
|
||||
// or "sysdefault:" and maybe others. Go through the list and see
|
||||
// if we can find a preferred prefix for the system.
|
||||
for (int i = 0; hints[i]; i++) {
|
||||
char *name = ALSA_snd_device_name_get_hint(hints[i], "NAME");
|
||||
if (name == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (SDL_strcmp(name, "default") == 0) {
|
||||
if (has_default < 0) {
|
||||
has_default = i;
|
||||
}
|
||||
} else {
|
||||
for (int j = 0; prefixes[j]; j++) {
|
||||
const char *prefix = prefixes[j];
|
||||
const size_t prefixlen = SDL_strlen(prefix);
|
||||
if (SDL_strncmp(name, prefix, prefixlen) == 0) {
|
||||
if (j < bestmatch) {
|
||||
bestmatch = j;
|
||||
match = prefix;
|
||||
match_len = prefixlen;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free(name); // This should NOT be SDL_free()
|
||||
}
|
||||
}
|
||||
|
||||
// look through the list of device names to find matches
|
||||
if (match || (has_default >= 0)) { // did we find a device name prefix we like at all...?
|
||||
for (int i = 0; hints[i]; i++) {
|
||||
char *name = ALSA_snd_device_name_get_hint(hints[i], "NAME");
|
||||
if (name == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// only want physical hardware interfaces
|
||||
const SDL_bool is_default = (has_default == i) ? SDL_TRUE : SDL_FALSE;
|
||||
if (is_default || (match != NULL && SDL_strncmp(name, match, match_len) == 0)) {
|
||||
char *ioid = ALSA_snd_device_name_get_hint(hints[i], "IOID");
|
||||
const SDL_bool isoutput = (ioid == NULL) || (SDL_strcmp(ioid, "Output") == 0);
|
||||
const SDL_bool isinput = (ioid == NULL) || (SDL_strcmp(ioid, "Input") == 0);
|
||||
SDL_bool have_output = SDL_FALSE;
|
||||
SDL_bool have_input = SDL_FALSE;
|
||||
|
||||
free(ioid); // This should NOT be SDL_free()
|
||||
|
||||
if (!isoutput && !isinput) {
|
||||
free(name); // This should NOT be SDL_free()
|
||||
continue;
|
||||
}
|
||||
|
||||
if (is_default) {
|
||||
if (has_default_output && isoutput) {
|
||||
*has_default_output = SDL_TRUE;
|
||||
} else if (has_default_capture && isinput) {
|
||||
*has_default_capture = SDL_TRUE;
|
||||
}
|
||||
free(name); // This should NOT be SDL_free()
|
||||
continue;
|
||||
}
|
||||
|
||||
ALSA_Device *prev = NULL;
|
||||
ALSA_Device *next;
|
||||
for (ALSA_Device *dev = unseen; dev; dev = next) {
|
||||
next = dev->next;
|
||||
if ((SDL_strcmp(dev->name, name) == 0) && (((isinput) && dev->iscapture) || ((isoutput) && !dev->iscapture))) {
|
||||
if (prev) {
|
||||
prev->next = next;
|
||||
} else {
|
||||
unseen = next;
|
||||
}
|
||||
dev->next = seen;
|
||||
seen = dev;
|
||||
if (isinput) {
|
||||
have_input = SDL_TRUE;
|
||||
}
|
||||
if (isoutput) {
|
||||
have_output = SDL_TRUE;
|
||||
}
|
||||
} else {
|
||||
prev = dev;
|
||||
}
|
||||
}
|
||||
|
||||
if (isinput && !have_input) {
|
||||
add_device(SDL_TRUE, name, hints[i], &seen);
|
||||
}
|
||||
if (isoutput && !have_output) {
|
||||
add_device(SDL_FALSE, name, hints[i], &seen);
|
||||
}
|
||||
}
|
||||
|
||||
free(name); // This should NOT be SDL_free()
|
||||
}
|
||||
}
|
||||
|
||||
ALSA_snd_device_name_free_hint(hints);
|
||||
|
||||
hotplug_devices = seen; // now we have a known-good list of attached devices.
|
||||
|
||||
// report anything still in unseen as removed.
|
||||
ALSA_Device *next = NULL;
|
||||
for (ALSA_Device *dev = unseen; dev; dev = next) {
|
||||
//SDL_LogInfo(SDL_LOG_CATEGORY_AUDIO, "ALSA: removing %s device '%s'", dev->iscapture ? "capture" : "output", dev->name);
|
||||
next = dev->next;
|
||||
SDL_AudioDeviceDisconnected(SDL_FindPhysicalAudioDeviceByHandle(dev));
|
||||
SDL_free(dev->name);
|
||||
SDL_free(dev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if SDL_ALSA_HOTPLUG_THREAD
|
||||
static SDL_AtomicInt ALSA_hotplug_shutdown;
|
||||
static SDL_Thread *ALSA_hotplug_thread;
|
||||
|
||||
static int SDLCALL ALSA_HotplugThread(void *arg)
|
||||
{
|
||||
SDL_SetThreadPriority(SDL_THREAD_PRIORITY_LOW);
|
||||
|
||||
while (!SDL_AtomicGet(&ALSA_hotplug_shutdown)) {
|
||||
// Block awhile before checking again, unless we're told to stop.
|
||||
const Uint64 ticks = SDL_GetTicks() + 5000;
|
||||
while (!SDL_AtomicGet(&ALSA_hotplug_shutdown) && SDL_GetTicks() < ticks) {
|
||||
SDL_Delay(100);
|
||||
}
|
||||
|
||||
ALSA_HotplugIteration(NULL, NULL); // run the check.
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void ALSA_DetectDevices(SDL_AudioDevice **default_output, SDL_AudioDevice **default_capture)
|
||||
{
|
||||
// ALSA doesn't have a concept of a changeable default device, afaik, so we expose a generic default
|
||||
// device here. It's the best we can do at this level.
|
||||
SDL_bool has_default_output = SDL_FALSE, has_default_capture = SDL_FALSE;
|
||||
ALSA_HotplugIteration(&has_default_output, &has_default_capture); // run once now before a thread continues to check.
|
||||
if (has_default_output) {
|
||||
*default_output = SDL_AddAudioDevice(/*iscapture=*/SDL_FALSE, "ALSA default output device", NULL, (void*)&default_output_handle);
|
||||
}
|
||||
if (has_default_capture) {
|
||||
*default_capture = SDL_AddAudioDevice(/*iscapture=*/SDL_TRUE, "ALSA default capture device", NULL, (void*)&default_capture_handle);
|
||||
}
|
||||
|
||||
#if SDL_ALSA_HOTPLUG_THREAD
|
||||
SDL_AtomicSet(&ALSA_hotplug_shutdown, 0);
|
||||
ALSA_hotplug_thread = SDL_CreateThread(ALSA_HotplugThread, "SDLHotplugALSA", NULL);
|
||||
// if the thread doesn't spin, oh well, you just don't get further hotplug events.
|
||||
#endif
|
||||
}
|
||||
|
||||
static void ALSA_DeinitializeStart(void)
|
||||
{
|
||||
ALSA_Device *dev;
|
||||
ALSA_Device *next;
|
||||
|
||||
#if SDL_ALSA_HOTPLUG_THREAD
|
||||
if (ALSA_hotplug_thread != NULL) {
|
||||
SDL_AtomicSet(&ALSA_hotplug_shutdown, 1);
|
||||
SDL_WaitThread(ALSA_hotplug_thread, NULL);
|
||||
ALSA_hotplug_thread = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Shutting down! Clean up any data we've gathered.
|
||||
for (dev = hotplug_devices; dev; dev = next) {
|
||||
//SDL_LogInfo(SDL_LOG_CATEGORY_AUDIO, "ALSA: at shutdown, removing %s device '%s'", dev->iscapture ? "capture" : "output", dev->name);
|
||||
next = dev->next;
|
||||
SDL_free(dev->name);
|
||||
SDL_free(dev);
|
||||
}
|
||||
hotplug_devices = NULL;
|
||||
}
|
||||
|
||||
static void ALSA_Deinitialize(void)
|
||||
{
|
||||
UnloadALSALibrary();
|
||||
}
|
||||
|
||||
static SDL_bool ALSA_Init(SDL_AudioDriverImpl *impl)
|
||||
{
|
||||
if (LoadALSALibrary() < 0) {
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
impl->DetectDevices = ALSA_DetectDevices;
|
||||
impl->OpenDevice = ALSA_OpenDevice;
|
||||
impl->WaitDevice = ALSA_WaitDevice;
|
||||
impl->GetDeviceBuf = ALSA_GetDeviceBuf;
|
||||
impl->PlayDevice = ALSA_PlayDevice;
|
||||
impl->CloseDevice = ALSA_CloseDevice;
|
||||
impl->DeinitializeStart = ALSA_DeinitializeStart;
|
||||
impl->Deinitialize = ALSA_Deinitialize;
|
||||
impl->WaitCaptureDevice = ALSA_WaitDevice;
|
||||
impl->CaptureFromDevice = ALSA_CaptureFromDevice;
|
||||
impl->FlushCapture = ALSA_FlushCapture;
|
||||
|
||||
impl->HasCaptureSupport = SDL_TRUE;
|
||||
|
||||
return SDL_TRUE;
|
||||
}
|
||||
|
||||
AudioBootStrap ALSA_bootstrap = {
|
||||
"alsa", "ALSA PCM audio", ALSA_Init, SDL_FALSE
|
||||
};
|
||||
|
||||
#endif // SDL_AUDIO_DRIVER_ALSA
|
||||
42
vendor/sdl-3.0.0/src/audio/alsa/SDL_alsa_audio.h
vendored
Normal file
42
vendor/sdl-3.0.0/src/audio/alsa/SDL_alsa_audio.h
vendored
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifndef SDL_ALSA_audio_h_
|
||||
#define SDL_ALSA_audio_h_
|
||||
|
||||
#include <alsa/asoundlib.h>
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
|
||||
struct SDL_PrivateAudioData
|
||||
{
|
||||
// The audio device handle
|
||||
snd_pcm_t *pcm_handle;
|
||||
|
||||
// Raw mixing buffer
|
||||
Uint8 *mixbuf;
|
||||
|
||||
// swizzle function
|
||||
void (*swizzle_func)(SDL_AudioDevice *_this, void *buffer, Uint32 bufferlen);
|
||||
};
|
||||
|
||||
#endif // SDL_ALSA_audio_h_
|
||||
191
vendor/sdl-3.0.0/src/audio/android/SDL_androidaudio.c
vendored
Normal file
191
vendor/sdl-3.0.0/src/audio/android/SDL_androidaudio.c
vendored
Normal file
|
|
@ -0,0 +1,191 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifdef SDL_AUDIO_DRIVER_ANDROID
|
||||
|
||||
// Output audio to Android (legacy interface)
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
#include "SDL_androidaudio.h"
|
||||
|
||||
#include "../../core/android/SDL_android.h"
|
||||
#include <android/log.h>
|
||||
|
||||
|
||||
struct SDL_PrivateAudioData
|
||||
{
|
||||
int resume; // Resume device if it was paused automatically
|
||||
};
|
||||
|
||||
static SDL_AudioDevice *audioDevice = NULL;
|
||||
static SDL_AudioDevice *captureDevice = NULL;
|
||||
|
||||
static int ANDROIDAUDIO_OpenDevice(SDL_AudioDevice *device)
|
||||
{
|
||||
device->hidden = (struct SDL_PrivateAudioData *)SDL_calloc(1, sizeof(*device->hidden));
|
||||
if (device->hidden == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
|
||||
const SDL_bool iscapture = device->iscapture;
|
||||
|
||||
if (iscapture) {
|
||||
if (captureDevice) {
|
||||
return SDL_SetError("An audio capture device is already opened");
|
||||
}
|
||||
captureDevice = device;
|
||||
} else {
|
||||
if (audioDevice) {
|
||||
return SDL_SetError("An audio playback device is already opened");
|
||||
}
|
||||
audioDevice = device;
|
||||
}
|
||||
|
||||
SDL_AudioFormat test_format;
|
||||
const SDL_AudioFormat *closefmts = SDL_ClosestAudioFormats(device->spec.format);
|
||||
while ((test_format = *(closefmts++)) != 0) {
|
||||
if ((test_format == SDL_AUDIO_U8) ||
|
||||
(test_format == SDL_AUDIO_S16) ||
|
||||
(test_format == SDL_AUDIO_F32)) {
|
||||
device->spec.format = test_format;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!test_format) {
|
||||
return SDL_SetError("android: Unsupported audio format");
|
||||
}
|
||||
|
||||
if (Android_JNI_OpenAudioDevice(device) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
SDL_UpdatedAudioDeviceFormat(device);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// !!! FIXME: this needs a WaitDevice implementation.
|
||||
|
||||
static int ANDROIDAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
|
||||
{
|
||||
Android_JNI_WriteAudioBuffer();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static Uint8 *ANDROIDAUDIO_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
|
||||
{
|
||||
return Android_JNI_GetAudioBuffer();
|
||||
}
|
||||
|
||||
static int ANDROIDAUDIO_CaptureFromDevice(SDL_AudioDevice *device, void *buffer, int buflen)
|
||||
{
|
||||
return Android_JNI_CaptureAudioBuffer(buffer, buflen);
|
||||
}
|
||||
|
||||
static void ANDROIDAUDIO_FlushCapture(SDL_AudioDevice *device)
|
||||
{
|
||||
Android_JNI_FlushCapturedAudio();
|
||||
}
|
||||
|
||||
static void ANDROIDAUDIO_CloseDevice(SDL_AudioDevice *device)
|
||||
{
|
||||
/* At this point SDL_CloseAudioDevice via close_audio_device took care of terminating the audio thread
|
||||
so it's safe to terminate the Java side buffer and AudioTrack
|
||||
*/
|
||||
if (device->hidden) {
|
||||
Android_JNI_CloseAudioDevice(device->iscapture);
|
||||
if (device->iscapture) {
|
||||
SDL_assert(captureDevice == device);
|
||||
captureDevice = NULL;
|
||||
} else {
|
||||
SDL_assert(audioDevice == device);
|
||||
audioDevice = NULL;
|
||||
}
|
||||
SDL_free(device->hidden);
|
||||
device->hidden = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// Pause (block) all non already paused audio devices by taking their mixer lock
|
||||
void ANDROIDAUDIO_PauseDevices(void)
|
||||
{
|
||||
// TODO: Handle multiple devices?
|
||||
struct SDL_PrivateAudioData *hidden;
|
||||
if (audioDevice != NULL && audioDevice->hidden != NULL) {
|
||||
hidden = (struct SDL_PrivateAudioData *)audioDevice->hidden;
|
||||
SDL_LockMutex(audioDevice->lock);
|
||||
hidden->resume = SDL_TRUE;
|
||||
}
|
||||
|
||||
if (captureDevice != NULL && captureDevice->hidden != NULL) {
|
||||
hidden = (struct SDL_PrivateAudioData *)captureDevice->hidden;
|
||||
SDL_LockMutex(captureDevice->lock);
|
||||
hidden->resume = SDL_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
// Resume (unblock) all non already paused audio devices by releasing their mixer lock
|
||||
void ANDROIDAUDIO_ResumeDevices(void)
|
||||
{
|
||||
// TODO: Handle multiple devices?
|
||||
struct SDL_PrivateAudioData *hidden;
|
||||
if (audioDevice != NULL && audioDevice->hidden != NULL) {
|
||||
hidden = (struct SDL_PrivateAudioData *)audioDevice->hidden;
|
||||
if (hidden->resume) {
|
||||
hidden->resume = SDL_FALSE;
|
||||
SDL_UnlockMutex(audioDevice->lock);
|
||||
}
|
||||
}
|
||||
|
||||
if (captureDevice != NULL && captureDevice->hidden != NULL) {
|
||||
hidden = (struct SDL_PrivateAudioData *)captureDevice->hidden;
|
||||
if (hidden->resume) {
|
||||
hidden->resume = SDL_FALSE;
|
||||
SDL_UnlockMutex(captureDevice->lock);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static SDL_bool ANDROIDAUDIO_Init(SDL_AudioDriverImpl *impl)
|
||||
{
|
||||
// !!! FIXME: if on Android API < 24, DetectDevices and Deinitialize should be NULL and OnlyHasDefaultOutputDevice and OnlyHasDefaultCaptureDevice should be SDL_TRUE, since audio device enum and hotplug appears to require Android 7.0+.
|
||||
impl->ThreadInit = Android_AudioThreadInit;
|
||||
impl->DetectDevices = Android_StartAudioHotplug;
|
||||
impl->DeinitializeStart = Android_StopAudioHotplug;
|
||||
impl->OpenDevice = ANDROIDAUDIO_OpenDevice;
|
||||
impl->PlayDevice = ANDROIDAUDIO_PlayDevice;
|
||||
impl->GetDeviceBuf = ANDROIDAUDIO_GetDeviceBuf;
|
||||
impl->CloseDevice = ANDROIDAUDIO_CloseDevice;
|
||||
impl->CaptureFromDevice = ANDROIDAUDIO_CaptureFromDevice;
|
||||
impl->FlushCapture = ANDROIDAUDIO_FlushCapture;
|
||||
|
||||
impl->HasCaptureSupport = SDL_TRUE;
|
||||
|
||||
return SDL_TRUE;
|
||||
}
|
||||
|
||||
AudioBootStrap ANDROIDAUDIO_bootstrap = {
|
||||
"android", "SDL Android audio driver", ANDROIDAUDIO_Init, SDL_FALSE
|
||||
};
|
||||
|
||||
#endif // SDL_AUDIO_DRIVER_ANDROID
|
||||
38
vendor/sdl-3.0.0/src/audio/android/SDL_androidaudio.h
vendored
Normal file
38
vendor/sdl-3.0.0/src/audio/android/SDL_androidaudio.h
vendored
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifndef SDL_androidaudio_h_
|
||||
#define SDL_androidaudio_h_
|
||||
|
||||
#ifdef SDL_AUDIO_DRIVER_ANDROID
|
||||
|
||||
void ANDROIDAUDIO_ResumeDevices(void);
|
||||
void ANDROIDAUDIO_PauseDevices(void);
|
||||
|
||||
#else
|
||||
|
||||
static void ANDROIDAUDIO_ResumeDevices(void) {}
|
||||
static void ANDROIDAUDIO_PauseDevices(void) {}
|
||||
|
||||
#endif
|
||||
|
||||
#endif // SDL_androidaudio_h_
|
||||
68
vendor/sdl-3.0.0/src/audio/coreaudio/SDL_coreaudio.h
vendored
Normal file
68
vendor/sdl-3.0.0/src/audio/coreaudio/SDL_coreaudio.h
vendored
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifndef SDL_coreaudio_h_
|
||||
#define SDL_coreaudio_h_
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
|
||||
#ifndef __IOS__
|
||||
#define MACOSX_COREAUDIO
|
||||
#endif
|
||||
|
||||
#ifdef MACOSX_COREAUDIO
|
||||
#include <CoreAudio/CoreAudio.h>
|
||||
#else
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
#import <UIKit/UIApplication.h>
|
||||
#endif
|
||||
|
||||
#include <AudioToolbox/AudioToolbox.h>
|
||||
#include <AudioUnit/AudioUnit.h>
|
||||
|
||||
// Things named "Master" were renamed to "Main" in macOS 12.0's SDK.
|
||||
#ifdef MACOSX_COREAUDIO
|
||||
#include <AvailabilityMacros.h>
|
||||
#ifndef MAC_OS_VERSION_12_0
|
||||
#define kAudioObjectPropertyElementMain kAudioObjectPropertyElementMaster
|
||||
#endif
|
||||
#endif
|
||||
|
||||
struct SDL_PrivateAudioData
|
||||
{
|
||||
SDL_Thread *thread;
|
||||
AudioQueueRef audioQueue;
|
||||
int numAudioBuffers;
|
||||
AudioQueueBufferRef *audioBuffer;
|
||||
AudioQueueBufferRef current_buffer;
|
||||
AudioStreamBasicDescription strdesc;
|
||||
SDL_Semaphore *ready_semaphore;
|
||||
char *thread_error;
|
||||
#ifdef MACOSX_COREAUDIO
|
||||
AudioDeviceID deviceID;
|
||||
#else
|
||||
SDL_bool interrupted;
|
||||
CFTypeRef interruption_listener;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif // SDL_coreaudio_h_
|
||||
979
vendor/sdl-3.0.0/src/audio/coreaudio/SDL_coreaudio.m
vendored
Normal file
979
vendor/sdl-3.0.0/src/audio/coreaudio/SDL_coreaudio.m
vendored
Normal file
|
|
@ -0,0 +1,979 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifdef SDL_AUDIO_DRIVER_COREAUDIO
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
#include "SDL_coreaudio.h"
|
||||
#include "../../thread/SDL_systhread.h"
|
||||
|
||||
#define DEBUG_COREAUDIO 0
|
||||
|
||||
#if DEBUG_COREAUDIO
|
||||
#define CHECK_RESULT(msg) \
|
||||
if (result != noErr) { \
|
||||
SDL_Log("COREAUDIO: Got error %d from '%s'!\n", (int)result, msg); \
|
||||
return SDL_SetError("CoreAudio error (%s): %d", msg, (int)result); \
|
||||
}
|
||||
#else
|
||||
#define CHECK_RESULT(msg) \
|
||||
if (result != noErr) { \
|
||||
return SDL_SetError("CoreAudio error (%s): %d", msg, (int)result); \
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef MACOSX_COREAUDIO
|
||||
static const AudioObjectPropertyAddress devlist_address = {
|
||||
kAudioHardwarePropertyDevices,
|
||||
kAudioObjectPropertyScopeGlobal,
|
||||
kAudioObjectPropertyElementMain
|
||||
};
|
||||
|
||||
static const AudioObjectPropertyAddress default_output_device_address = {
|
||||
kAudioHardwarePropertyDefaultOutputDevice,
|
||||
kAudioObjectPropertyScopeGlobal,
|
||||
kAudioObjectPropertyElementMain
|
||||
};
|
||||
|
||||
static const AudioObjectPropertyAddress default_input_device_address = {
|
||||
kAudioHardwarePropertyDefaultInputDevice,
|
||||
kAudioObjectPropertyScopeGlobal,
|
||||
kAudioObjectPropertyElementMain
|
||||
};
|
||||
|
||||
static const AudioObjectPropertyAddress alive_address = {
|
||||
kAudioDevicePropertyDeviceIsAlive,
|
||||
kAudioObjectPropertyScopeGlobal,
|
||||
kAudioObjectPropertyElementMain
|
||||
};
|
||||
|
||||
|
||||
static OSStatus DeviceAliveNotification(AudioObjectID devid, UInt32 num_addr, const AudioObjectPropertyAddress *addrs, void *data)
|
||||
{
|
||||
SDL_AudioDevice *device = (SDL_AudioDevice *)data;
|
||||
SDL_assert(((AudioObjectID)(size_t)device->handle) == devid);
|
||||
|
||||
UInt32 alive = 1;
|
||||
UInt32 size = sizeof(alive);
|
||||
const OSStatus error = AudioObjectGetPropertyData(devid, addrs, 0, NULL, &size, &alive);
|
||||
|
||||
SDL_bool dead = SDL_FALSE;
|
||||
if (error == kAudioHardwareBadDeviceError) {
|
||||
dead = SDL_TRUE; // device was unplugged.
|
||||
} else if ((error == kAudioHardwareNoError) && (!alive)) {
|
||||
dead = SDL_TRUE; // device died in some other way.
|
||||
}
|
||||
|
||||
if (dead) {
|
||||
#if DEBUG_COREAUDIO
|
||||
SDL_Log("COREAUDIO: device '%s' is lost!", device->name);
|
||||
#endif
|
||||
SDL_AudioDeviceDisconnected(device);
|
||||
}
|
||||
|
||||
return noErr;
|
||||
}
|
||||
|
||||
static void COREAUDIO_FreeDeviceHandle(SDL_AudioDevice *device)
|
||||
{
|
||||
const AudioDeviceID devid = (AudioDeviceID)(size_t)device->handle;
|
||||
AudioObjectRemovePropertyListener(devid, &alive_address, DeviceAliveNotification, device);
|
||||
}
|
||||
|
||||
// This only _adds_ new devices. Removal is handled by devices triggering kAudioDevicePropertyDeviceIsAlive property changes.
|
||||
static void RefreshPhysicalDevices(void)
|
||||
{
|
||||
UInt32 size = 0;
|
||||
AudioDeviceID *devs = NULL;
|
||||
SDL_bool isstack;
|
||||
|
||||
if (AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &devlist_address, 0, NULL, &size) != kAudioHardwareNoError) {
|
||||
return;
|
||||
} else if ((devs = (AudioDeviceID *) SDL_small_alloc(Uint8, size, &isstack)) == NULL) {
|
||||
return;
|
||||
} else if (AudioObjectGetPropertyData(kAudioObjectSystemObject, &devlist_address, 0, NULL, &size, devs) != kAudioHardwareNoError) {
|
||||
SDL_small_free(devs, isstack);
|
||||
return;
|
||||
}
|
||||
|
||||
const UInt32 total_devices = (UInt32) (size / sizeof(AudioDeviceID));
|
||||
for (UInt32 i = 0; i < total_devices; i++) {
|
||||
SDL_AudioDevice *device = SDL_FindPhysicalAudioDeviceByHandle((void *)((size_t)devs[i]));
|
||||
if (device) {
|
||||
devs[i] = 0; // The system and SDL both agree it's already here, don't check it again.
|
||||
}
|
||||
}
|
||||
|
||||
// any non-zero items remaining in `devs` are new devices to be added.
|
||||
for (int iscapture = 0; iscapture < 2; iscapture++) {
|
||||
const AudioObjectPropertyAddress addr = {
|
||||
kAudioDevicePropertyStreamConfiguration,
|
||||
iscapture ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput,
|
||||
kAudioObjectPropertyElementMain
|
||||
};
|
||||
const AudioObjectPropertyAddress nameaddr = {
|
||||
kAudioObjectPropertyName,
|
||||
iscapture ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput,
|
||||
kAudioObjectPropertyElementMain
|
||||
};
|
||||
const AudioObjectPropertyAddress freqaddr = {
|
||||
kAudioDevicePropertyNominalSampleRate,
|
||||
iscapture ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput,
|
||||
kAudioObjectPropertyElementMain
|
||||
};
|
||||
|
||||
for (UInt32 i = 0; i < total_devices; i++) {
|
||||
const AudioDeviceID dev = devs[i];
|
||||
if (!dev) {
|
||||
continue; // already added.
|
||||
}
|
||||
|
||||
AudioBufferList *buflist = NULL;
|
||||
double sampleRate = 0;
|
||||
|
||||
if (AudioObjectGetPropertyDataSize(dev, &addr, 0, NULL, &size) != noErr) {
|
||||
continue;
|
||||
} else if ((buflist = (AudioBufferList *)SDL_malloc(size)) == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
OSStatus result = AudioObjectGetPropertyData(dev, &addr, 0, NULL, &size, buflist);
|
||||
|
||||
SDL_AudioSpec spec;
|
||||
SDL_zero(spec);
|
||||
if (result == noErr) {
|
||||
for (Uint32 j = 0; j < buflist->mNumberBuffers; j++) {
|
||||
spec.channels += buflist->mBuffers[j].mNumberChannels;
|
||||
}
|
||||
}
|
||||
|
||||
SDL_free(buflist);
|
||||
|
||||
if (spec.channels == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
size = sizeof(sampleRate);
|
||||
if (AudioObjectGetPropertyData(dev, &freqaddr, 0, NULL, &size, &sampleRate) == noErr) {
|
||||
spec.freq = (int)sampleRate;
|
||||
}
|
||||
|
||||
CFStringRef cfstr = NULL;
|
||||
size = sizeof(CFStringRef);
|
||||
if (AudioObjectGetPropertyData(dev, &nameaddr, 0, NULL, &size, &cfstr) != kAudioHardwareNoError) {
|
||||
continue;
|
||||
}
|
||||
|
||||
CFIndex len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(cfstr), kCFStringEncodingUTF8);
|
||||
char *name = (char *)SDL_malloc(len + 1);
|
||||
SDL_bool usable = ((name != NULL) && (CFStringGetCString(cfstr, name, len + 1, kCFStringEncodingUTF8))) ? SDL_TRUE : SDL_FALSE;
|
||||
|
||||
CFRelease(cfstr);
|
||||
|
||||
if (usable) {
|
||||
// Some devices have whitespace at the end...trim it.
|
||||
len = (CFIndex) SDL_strlen(name);
|
||||
while ((len > 0) && (name[len - 1] == ' ')) {
|
||||
len--;
|
||||
}
|
||||
usable = (len > 0);
|
||||
}
|
||||
|
||||
if (usable) {
|
||||
name[len] = '\0';
|
||||
|
||||
#if DEBUG_COREAUDIO
|
||||
SDL_Log("COREAUDIO: Found %s device #%d: '%s' (devid %d)\n",
|
||||
((iscapture) ? "capture" : "output"),
|
||||
(int)i, name, (int)dev);
|
||||
#endif
|
||||
|
||||
devs[i] = 0; // don't bother checking this one on the next iscapture iteration of the loop
|
||||
|
||||
SDL_AudioDevice *device = SDL_AddAudioDevice(iscapture ? SDL_TRUE : SDL_FALSE, name, &spec, (void *)((size_t)dev));
|
||||
if (device) {
|
||||
AudioObjectAddPropertyListener(dev, &alive_address, DeviceAliveNotification, device);
|
||||
}
|
||||
}
|
||||
SDL_free(name); // SDL_AddAudioDevice() would have copied the string.
|
||||
}
|
||||
}
|
||||
|
||||
SDL_small_free(devs, isstack);
|
||||
}
|
||||
|
||||
// this is called when the system's list of available audio devices changes.
|
||||
static OSStatus DeviceListChangedNotification(AudioObjectID systemObj, UInt32 num_addr, const AudioObjectPropertyAddress *addrs, void *data)
|
||||
{
|
||||
RefreshPhysicalDevices();
|
||||
return noErr;
|
||||
}
|
||||
|
||||
static OSStatus DefaultAudioDeviceChangedNotification(AudioObjectID inObjectID, const AudioObjectPropertyAddress *addr)
|
||||
{
|
||||
AudioDeviceID devid;
|
||||
Uint32 size = sizeof(devid);
|
||||
if (AudioObjectGetPropertyData(inObjectID, addr, 0, NULL, &size, &devid) == noErr) {
|
||||
SDL_DefaultAudioDeviceChanged(SDL_FindPhysicalAudioDeviceByHandle((void *)((size_t)devid)));
|
||||
}
|
||||
return noErr;
|
||||
}
|
||||
|
||||
static OSStatus DefaultOutputDeviceChangedNotification(AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress *inAddresses, void *inUserData)
|
||||
{
|
||||
#if DEBUG_COREAUDIO
|
||||
SDL_Log("COREAUDIO: default output device changed!");
|
||||
#endif
|
||||
SDL_assert(inNumberAddresses == 1);
|
||||
return DefaultAudioDeviceChangedNotification(inObjectID, inAddresses);
|
||||
}
|
||||
|
||||
static OSStatus DefaultInputDeviceChangedNotification(AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress *inAddresses, void *inUserData)
|
||||
{
|
||||
#if DEBUG_COREAUDIO
|
||||
SDL_Log("COREAUDIO: default input device changed!");
|
||||
#endif
|
||||
SDL_assert(inNumberAddresses == 1);
|
||||
return DefaultAudioDeviceChangedNotification(inObjectID, inAddresses);
|
||||
}
|
||||
|
||||
static void COREAUDIO_DetectDevices(SDL_AudioDevice **default_output, SDL_AudioDevice **default_capture)
|
||||
{
|
||||
RefreshPhysicalDevices();
|
||||
|
||||
AudioObjectAddPropertyListener(kAudioObjectSystemObject, &devlist_address, DeviceListChangedNotification, NULL);
|
||||
|
||||
// Get the Device ID
|
||||
UInt32 size;
|
||||
AudioDeviceID devid;
|
||||
|
||||
size = sizeof(AudioDeviceID);
|
||||
if (AudioObjectGetPropertyData(kAudioObjectSystemObject, &default_output_device_address, 0, NULL, &size, &devid) == noErr) {
|
||||
SDL_AudioDevice *device = SDL_FindPhysicalAudioDeviceByHandle((void *)((size_t)devid));
|
||||
if (device) {
|
||||
*default_output = device;
|
||||
}
|
||||
}
|
||||
AudioObjectAddPropertyListener(kAudioObjectSystemObject, &default_output_device_address, DefaultOutputDeviceChangedNotification, NULL);
|
||||
|
||||
size = sizeof(AudioDeviceID);
|
||||
if (AudioObjectGetPropertyData(kAudioObjectSystemObject, &default_input_device_address, 0, NULL, &size, &devid) == noErr) {
|
||||
SDL_AudioDevice *device = SDL_FindPhysicalAudioDeviceByHandle((void *)((size_t)devid));
|
||||
if (device) {
|
||||
*default_capture = device;
|
||||
}
|
||||
}
|
||||
AudioObjectAddPropertyListener(kAudioObjectSystemObject, &default_input_device_address, DefaultInputDeviceChangedNotification, NULL);
|
||||
}
|
||||
|
||||
#else // iOS-specific section follows.
|
||||
|
||||
static SDL_bool session_active = SDL_FALSE;
|
||||
|
||||
static SDL_bool PauseOneAudioDevice(SDL_AudioDevice *device, void *userdata)
|
||||
{
|
||||
if (device->hidden && device->hidden->audioQueue && !device->hidden->interrupted) {
|
||||
AudioQueuePause(device->hidden->audioQueue);
|
||||
}
|
||||
return SDL_FALSE; // keep enumerating devices until we've paused them all.
|
||||
}
|
||||
|
||||
static void PauseAudioDevices(void)
|
||||
{
|
||||
(void) SDL_FindPhysicalAudioDeviceByCallback(PauseOneAudioDevice, NULL);
|
||||
}
|
||||
|
||||
static SDL_bool ResumeOneAudioDevice(SDL_AudioDevice *device, void *userdata)
|
||||
{
|
||||
if (device->hidden && device->hidden->audioQueue && !device->hidden->interrupted) {
|
||||
AudioQueueStart(device->hidden->audioQueue, NULL);
|
||||
}
|
||||
return SDL_FALSE; // keep enumerating devices until we've resumed them all.
|
||||
}
|
||||
|
||||
static void ResumeAudioDevices(void)
|
||||
{
|
||||
(void) SDL_FindPhysicalAudioDeviceByCallback(ResumeOneAudioDevice, NULL);
|
||||
}
|
||||
|
||||
static void InterruptionBegin(SDL_AudioDevice *device)
|
||||
{
|
||||
if (device != NULL && device->hidden->audioQueue != NULL) {
|
||||
device->hidden->interrupted = SDL_TRUE;
|
||||
AudioQueuePause(device->hidden->audioQueue);
|
||||
}
|
||||
}
|
||||
|
||||
static void InterruptionEnd(SDL_AudioDevice *device)
|
||||
{
|
||||
if (device != NULL && device->hidden != NULL && device->hidden->audioQueue != NULL && device->hidden->interrupted && AudioQueueStart(device->hidden->audioQueue, NULL) == AVAudioSessionErrorCodeNone) {
|
||||
device->hidden->interrupted = SDL_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
@interface SDLInterruptionListener : NSObject
|
||||
|
||||
@property(nonatomic, assign) SDL_AudioDevice *device;
|
||||
|
||||
@end
|
||||
|
||||
@implementation SDLInterruptionListener
|
||||
|
||||
- (void)audioSessionInterruption:(NSNotification *)note
|
||||
{
|
||||
@synchronized(self) {
|
||||
NSNumber *type = note.userInfo[AVAudioSessionInterruptionTypeKey];
|
||||
if (type.unsignedIntegerValue == AVAudioSessionInterruptionTypeBegan) {
|
||||
InterruptionBegin(self.device);
|
||||
} else {
|
||||
InterruptionEnd(self.device);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)applicationBecameActive:(NSNotification *)note
|
||||
{
|
||||
@synchronized(self) {
|
||||
InterruptionEnd(self.device);
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int output;
|
||||
int capture;
|
||||
} CountOpenAudioDevicesData;
|
||||
|
||||
static SDL_bool CountOpenAudioDevices(SDL_AudioDevice *device, void *userdata)
|
||||
{
|
||||
CountOpenAudioDevicesData *data = (CountOpenAudioDevicesData *) userdata;
|
||||
if (device->hidden != NULL) { // assume it's open if hidden != NULL
|
||||
if (device->iscapture) {
|
||||
data->capture++;
|
||||
} else {
|
||||
data->output++;
|
||||
}
|
||||
}
|
||||
return SDL_FALSE; // keep enumerating until all devices have been checked.
|
||||
}
|
||||
|
||||
static SDL_bool UpdateAudioSession(SDL_AudioDevice *device, SDL_bool open, SDL_bool allow_playandrecord)
|
||||
{
|
||||
@autoreleasepool {
|
||||
AVAudioSession *session = [AVAudioSession sharedInstance];
|
||||
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
|
||||
|
||||
NSString *category = AVAudioSessionCategoryPlayback;
|
||||
NSString *mode = AVAudioSessionModeDefault;
|
||||
NSUInteger options = AVAudioSessionCategoryOptionMixWithOthers;
|
||||
NSError *err = nil;
|
||||
const char *hint;
|
||||
|
||||
CountOpenAudioDevicesData data;
|
||||
SDL_zero(data);
|
||||
(void) SDL_FindPhysicalAudioDeviceByCallback(CountOpenAudioDevices, &data);
|
||||
|
||||
hint = SDL_GetHint(SDL_HINT_AUDIO_CATEGORY);
|
||||
if (hint) {
|
||||
if (SDL_strcasecmp(hint, "AVAudioSessionCategoryAmbient") == 0) {
|
||||
category = AVAudioSessionCategoryAmbient;
|
||||
} else if (SDL_strcasecmp(hint, "AVAudioSessionCategorySoloAmbient") == 0) {
|
||||
category = AVAudioSessionCategorySoloAmbient;
|
||||
options &= ~AVAudioSessionCategoryOptionMixWithOthers;
|
||||
} else if (SDL_strcasecmp(hint, "AVAudioSessionCategoryPlayback") == 0 ||
|
||||
SDL_strcasecmp(hint, "playback") == 0) {
|
||||
category = AVAudioSessionCategoryPlayback;
|
||||
options &= ~AVAudioSessionCategoryOptionMixWithOthers;
|
||||
} else if (SDL_strcasecmp(hint, "AVAudioSessionCategoryPlayAndRecord") == 0 ||
|
||||
SDL_strcasecmp(hint, "playandrecord") == 0) {
|
||||
if (allow_playandrecord) {
|
||||
category = AVAudioSessionCategoryPlayAndRecord;
|
||||
}
|
||||
}
|
||||
} else if (data.output && data.capture) {
|
||||
category = AVAudioSessionCategoryPlayAndRecord;
|
||||
} else if (data.capture) {
|
||||
category = AVAudioSessionCategoryRecord;
|
||||
}
|
||||
|
||||
#if !TARGET_OS_TV
|
||||
if (category == AVAudioSessionCategoryPlayAndRecord) {
|
||||
options |= AVAudioSessionCategoryOptionDefaultToSpeaker;
|
||||
}
|
||||
#endif
|
||||
if (category == AVAudioSessionCategoryRecord ||
|
||||
category == AVAudioSessionCategoryPlayAndRecord) {
|
||||
/* AVAudioSessionCategoryOptionAllowBluetooth isn't available in the SDK for
|
||||
Apple TV but is still needed in order to output to Bluetooth devices.
|
||||
*/
|
||||
options |= 0x4; // AVAudioSessionCategoryOptionAllowBluetooth;
|
||||
}
|
||||
if (category == AVAudioSessionCategoryPlayAndRecord) {
|
||||
options |= AVAudioSessionCategoryOptionAllowBluetoothA2DP |
|
||||
AVAudioSessionCategoryOptionAllowAirPlay;
|
||||
}
|
||||
if (category == AVAudioSessionCategoryPlayback ||
|
||||
category == AVAudioSessionCategoryPlayAndRecord) {
|
||||
options |= AVAudioSessionCategoryOptionDuckOthers;
|
||||
}
|
||||
|
||||
if ([session respondsToSelector:@selector(setCategory:mode:options:error:)]) {
|
||||
if (![session.category isEqualToString:category] || session.categoryOptions != options) {
|
||||
// Stop the current session so we don't interrupt other application audio
|
||||
PauseAudioDevices();
|
||||
[session setActive:NO error:nil];
|
||||
session_active = SDL_FALSE;
|
||||
|
||||
if (![session setCategory:category mode:mode options:options error:&err]) {
|
||||
NSString *desc = err.description;
|
||||
SDL_SetError("Could not set Audio Session category: %s", desc.UTF8String);
|
||||
return SDL_FALSE;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (![session.category isEqualToString:category]) {
|
||||
// Stop the current session so we don't interrupt other application audio
|
||||
PauseAudioDevices();
|
||||
[session setActive:NO error:nil];
|
||||
session_active = SDL_FALSE;
|
||||
|
||||
if (![session setCategory:category error:&err]) {
|
||||
NSString *desc = err.description;
|
||||
SDL_SetError("Could not set Audio Session category: %s", desc.UTF8String);
|
||||
return SDL_FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((data.output || data.capture) && !session_active) {
|
||||
if (![session setActive:YES error:&err]) {
|
||||
if ([err code] == AVAudioSessionErrorCodeResourceNotAvailable &&
|
||||
category == AVAudioSessionCategoryPlayAndRecord) {
|
||||
return UpdateAudioSession(device, open, SDL_FALSE);
|
||||
}
|
||||
|
||||
NSString *desc = err.description;
|
||||
SDL_SetError("Could not activate Audio Session: %s", desc.UTF8String);
|
||||
return SDL_FALSE;
|
||||
}
|
||||
session_active = SDL_TRUE;
|
||||
ResumeAudioDevices();
|
||||
} else if (!data.output && !data.capture && session_active) {
|
||||
PauseAudioDevices();
|
||||
[session setActive:NO error:nil];
|
||||
session_active = SDL_FALSE;
|
||||
}
|
||||
|
||||
if (open) {
|
||||
SDLInterruptionListener *listener = [SDLInterruptionListener new];
|
||||
listener.device = device;
|
||||
|
||||
[center addObserver:listener
|
||||
selector:@selector(audioSessionInterruption:)
|
||||
name:AVAudioSessionInterruptionNotification
|
||||
object:session];
|
||||
|
||||
/* An interruption end notification is not guaranteed to be sent if
|
||||
we were previously interrupted... resuming if needed when the app
|
||||
becomes active seems to be the way to go. */
|
||||
// Note: object: below needs to be nil, as otherwise it filters by the object, and session doesn't send foreground / active notifications.
|
||||
[center addObserver:listener
|
||||
selector:@selector(applicationBecameActive:)
|
||||
name:UIApplicationDidBecomeActiveNotification
|
||||
object:nil];
|
||||
|
||||
[center addObserver:listener
|
||||
selector:@selector(applicationBecameActive:)
|
||||
name:UIApplicationWillEnterForegroundNotification
|
||||
object:nil];
|
||||
|
||||
device->hidden->interruption_listener = CFBridgingRetain(listener);
|
||||
} else {
|
||||
SDLInterruptionListener *listener = nil;
|
||||
listener = (SDLInterruptionListener *)CFBridgingRelease(device->hidden->interruption_listener);
|
||||
[center removeObserver:listener];
|
||||
@synchronized(listener) {
|
||||
listener.device = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return SDL_TRUE;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
static int COREAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buffer_size)
|
||||
{
|
||||
AudioQueueBufferRef current_buffer = device->hidden->current_buffer;
|
||||
SDL_assert(current_buffer != NULL); // should have been called from OutputBufferReadyCallback
|
||||
SDL_assert(buffer == (Uint8 *) current_buffer->mAudioData);
|
||||
current_buffer->mAudioDataByteSize = current_buffer->mAudioDataBytesCapacity;
|
||||
device->hidden->current_buffer = NULL;
|
||||
AudioQueueEnqueueBuffer(device->hidden->audioQueue, current_buffer, 0, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static Uint8 *COREAUDIO_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
|
||||
{
|
||||
AudioQueueBufferRef current_buffer = device->hidden->current_buffer;
|
||||
SDL_assert(current_buffer != NULL); // should have been called from OutputBufferReadyCallback
|
||||
SDL_assert(current_buffer->mAudioData != NULL);
|
||||
*buffer_size = (int) current_buffer->mAudioDataBytesCapacity;
|
||||
return (Uint8 *) current_buffer->mAudioData;
|
||||
}
|
||||
|
||||
static void OutputBufferReadyCallback(void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer)
|
||||
{
|
||||
SDL_AudioDevice *device = (SDL_AudioDevice *)inUserData;
|
||||
SDL_assert(inBuffer != NULL); // ...right?
|
||||
SDL_assert(device->hidden->current_buffer == NULL); // shouldn't have anything pending
|
||||
device->hidden->current_buffer = inBuffer;
|
||||
const SDL_bool okay = SDL_OutputAudioThreadIterate(device);
|
||||
SDL_assert((device->hidden->current_buffer == NULL) || !okay); // PlayDevice should have enqueued and cleaned it out, unless we failed or shutdown.
|
||||
|
||||
// buffer is unexpectedly here? We're probably dying, but try to requeue this buffer with silence.
|
||||
if (device->hidden->current_buffer) {
|
||||
AudioQueueBufferRef current_buffer = device->hidden->current_buffer;
|
||||
device->hidden->current_buffer = NULL;
|
||||
SDL_memset(current_buffer->mAudioData, device->silence_value, (size_t) current_buffer->mAudioDataBytesCapacity);
|
||||
AudioQueueEnqueueBuffer(device->hidden->audioQueue, current_buffer, 0, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static int COREAUDIO_CaptureFromDevice(SDL_AudioDevice *device, void *buffer, int buflen)
|
||||
{
|
||||
AudioQueueBufferRef current_buffer = device->hidden->current_buffer;
|
||||
SDL_assert(current_buffer != NULL); // should have been called from InputBufferReadyCallback
|
||||
SDL_assert(current_buffer->mAudioData != NULL);
|
||||
SDL_assert(buflen >= (int) current_buffer->mAudioDataByteSize); // `cpy` makes sure this won't overflow a buffer, but we _will_ drop samples if this assertion fails!
|
||||
const int cpy = SDL_min(buflen, (int) current_buffer->mAudioDataByteSize);
|
||||
SDL_memcpy(buffer, current_buffer->mAudioData, cpy);
|
||||
device->hidden->current_buffer = NULL;
|
||||
AudioQueueEnqueueBuffer(device->hidden->audioQueue, current_buffer, 0, NULL); // requeue for capturing more data later.
|
||||
return cpy;
|
||||
}
|
||||
|
||||
static void COREAUDIO_FlushCapture(SDL_AudioDevice *device)
|
||||
{
|
||||
AudioQueueBufferRef current_buffer = device->hidden->current_buffer;
|
||||
if (current_buffer != NULL) { // also gets called at shutdown, when no buffer is available.
|
||||
// just requeue the current buffer without reading from it, so it can be refilled with new data later.
|
||||
device->hidden->current_buffer = NULL;
|
||||
AudioQueueEnqueueBuffer(device->hidden->audioQueue, current_buffer, 0, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static void InputBufferReadyCallback(void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer,
|
||||
const AudioTimeStamp *inStartTime, UInt32 inNumberPacketDescriptions,
|
||||
const AudioStreamPacketDescription *inPacketDescs)
|
||||
{
|
||||
SDL_AudioDevice *device = (SDL_AudioDevice *)inUserData;
|
||||
SDL_assert(inAQ == device->hidden->audioQueue);
|
||||
SDL_assert(inBuffer != NULL); // ...right?
|
||||
SDL_assert(device->hidden->current_buffer == NULL); // shouldn't have anything pending
|
||||
device->hidden->current_buffer = inBuffer;
|
||||
SDL_CaptureAudioThreadIterate(device);
|
||||
SDL_assert(device->hidden->current_buffer == NULL); // CaptureFromDevice/FlushCapture should have enqueued and cleaned it out.
|
||||
}
|
||||
|
||||
static void COREAUDIO_CloseDevice(SDL_AudioDevice *device)
|
||||
{
|
||||
if (!device->hidden) {
|
||||
return;
|
||||
}
|
||||
|
||||
// dispose of the audio queue before waiting on the thread, or it might stall for a long time!
|
||||
if (device->hidden->audioQueue) {
|
||||
AudioQueueFlush(device->hidden->audioQueue);
|
||||
AudioQueueStop(device->hidden->audioQueue, 0);
|
||||
AudioQueueDispose(device->hidden->audioQueue, 0);
|
||||
}
|
||||
|
||||
if (device->hidden->thread) {
|
||||
SDL_assert(SDL_AtomicGet(&device->shutdown) != 0); // should have been set by SDL_audio.c
|
||||
SDL_WaitThread(device->hidden->thread, NULL);
|
||||
}
|
||||
|
||||
#ifndef MACOSX_COREAUDIO
|
||||
UpdateAudioSession(device, SDL_FALSE, SDL_TRUE);
|
||||
#endif
|
||||
|
||||
if (device->hidden->ready_semaphore) {
|
||||
SDL_DestroySemaphore(device->hidden->ready_semaphore);
|
||||
}
|
||||
|
||||
// AudioQueueDispose() frees the actual buffer objects.
|
||||
SDL_free(device->hidden->audioBuffer);
|
||||
SDL_free(device->hidden->thread_error);
|
||||
SDL_free(device->hidden);
|
||||
}
|
||||
|
||||
#ifdef MACOSX_COREAUDIO
|
||||
static int PrepareDevice(SDL_AudioDevice *device)
|
||||
{
|
||||
void *handle = device->handle;
|
||||
SDL_assert(handle != NULL); // this meant "system default" in SDL2, but doesn't anymore
|
||||
|
||||
const AudioDeviceID devid = (AudioDeviceID)((size_t)handle);
|
||||
OSStatus result = noErr;
|
||||
UInt32 size = 0;
|
||||
|
||||
AudioObjectPropertyAddress addr = {
|
||||
0,
|
||||
kAudioObjectPropertyScopeGlobal,
|
||||
kAudioObjectPropertyElementMain
|
||||
};
|
||||
|
||||
UInt32 alive = 0;
|
||||
size = sizeof(alive);
|
||||
addr.mSelector = kAudioDevicePropertyDeviceIsAlive;
|
||||
addr.mScope = device->iscapture ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput;
|
||||
result = AudioObjectGetPropertyData(devid, &addr, 0, NULL, &size, &alive);
|
||||
CHECK_RESULT("AudioDeviceGetProperty (kAudioDevicePropertyDeviceIsAlive)");
|
||||
if (!alive) {
|
||||
return SDL_SetError("CoreAudio: requested device exists, but isn't alive.");
|
||||
}
|
||||
|
||||
// some devices don't support this property, so errors are fine here.
|
||||
pid_t pid = 0;
|
||||
size = sizeof(pid);
|
||||
addr.mSelector = kAudioDevicePropertyHogMode;
|
||||
result = AudioObjectGetPropertyData(devid, &addr, 0, NULL, &size, &pid);
|
||||
if ((result == noErr) && (pid != -1)) {
|
||||
return SDL_SetError("CoreAudio: requested device is being hogged.");
|
||||
}
|
||||
|
||||
device->hidden->deviceID = devid;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int AssignDeviceToAudioQueue(SDL_AudioDevice *device)
|
||||
{
|
||||
const AudioObjectPropertyAddress prop = {
|
||||
kAudioDevicePropertyDeviceUID,
|
||||
device->iscapture ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput,
|
||||
kAudioObjectPropertyElementMain
|
||||
};
|
||||
|
||||
OSStatus result;
|
||||
CFStringRef devuid;
|
||||
UInt32 devuidsize = sizeof(devuid);
|
||||
result = AudioObjectGetPropertyData(device->hidden->deviceID, &prop, 0, NULL, &devuidsize, &devuid);
|
||||
CHECK_RESULT("AudioObjectGetPropertyData (kAudioDevicePropertyDeviceUID)");
|
||||
result = AudioQueueSetProperty(device->hidden->audioQueue, kAudioQueueProperty_CurrentDevice, &devuid, devuidsize);
|
||||
CHECK_RESULT("AudioQueueSetProperty (kAudioQueueProperty_CurrentDevice)");
|
||||
|
||||
// !!! FIXME: do we need to CFRelease(devuid)?
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int PrepareAudioQueue(SDL_AudioDevice *device)
|
||||
{
|
||||
const AudioStreamBasicDescription *strdesc = &device->hidden->strdesc;
|
||||
const SDL_bool iscapture = device->iscapture;
|
||||
OSStatus result;
|
||||
|
||||
SDL_assert(CFRunLoopGetCurrent() != NULL);
|
||||
|
||||
if (iscapture) {
|
||||
result = AudioQueueNewInput(strdesc, InputBufferReadyCallback, device, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 0, &device->hidden->audioQueue);
|
||||
CHECK_RESULT("AudioQueueNewInput");
|
||||
} else {
|
||||
result = AudioQueueNewOutput(strdesc, OutputBufferReadyCallback, device, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 0, &device->hidden->audioQueue);
|
||||
CHECK_RESULT("AudioQueueNewOutput");
|
||||
}
|
||||
|
||||
#ifdef MACOSX_COREAUDIO
|
||||
if (AssignDeviceToAudioQueue(device) < 0) {
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
SDL_UpdatedAudioDeviceFormat(device); // make sure this is correct.
|
||||
|
||||
// Set the channel layout for the audio queue
|
||||
AudioChannelLayout layout;
|
||||
SDL_zero(layout);
|
||||
switch (device->spec.channels) {
|
||||
case 1:
|
||||
layout.mChannelLayoutTag = kAudioChannelLayoutTag_Mono;
|
||||
break;
|
||||
case 2:
|
||||
layout.mChannelLayoutTag = kAudioChannelLayoutTag_Stereo;
|
||||
break;
|
||||
case 3:
|
||||
layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_4;
|
||||
break;
|
||||
case 4:
|
||||
layout.mChannelLayoutTag = kAudioChannelLayoutTag_Quadraphonic;
|
||||
break;
|
||||
case 5:
|
||||
layout.mChannelLayoutTag = kAudioChannelLayoutTag_MPEG_5_0_A;
|
||||
break;
|
||||
case 6:
|
||||
layout.mChannelLayoutTag = kAudioChannelLayoutTag_MPEG_5_1_A;
|
||||
break;
|
||||
case 7:
|
||||
// FIXME: Need to move channel[4] (BC) to channel[6]
|
||||
layout.mChannelLayoutTag = kAudioChannelLayoutTag_MPEG_6_1_A;
|
||||
break;
|
||||
case 8:
|
||||
layout.mChannelLayoutTag = kAudioChannelLayoutTag_MPEG_7_1_A;
|
||||
break;
|
||||
}
|
||||
if (layout.mChannelLayoutTag != 0) {
|
||||
result = AudioQueueSetProperty(device->hidden->audioQueue, kAudioQueueProperty_ChannelLayout, &layout, sizeof(layout));
|
||||
CHECK_RESULT("AudioQueueSetProperty(kAudioQueueProperty_ChannelLayout)");
|
||||
}
|
||||
|
||||
// Make sure we can feed the device a minimum amount of time
|
||||
double MINIMUM_AUDIO_BUFFER_TIME_MS = 15.0;
|
||||
#ifdef __IOS__
|
||||
if (SDL_floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_7_1) {
|
||||
// Older iOS hardware, use 40 ms as a minimum time
|
||||
MINIMUM_AUDIO_BUFFER_TIME_MS = 40.0;
|
||||
}
|
||||
#endif
|
||||
|
||||
int numAudioBuffers = 2;
|
||||
const double msecs = (device->sample_frames / ((double)device->spec.freq)) * 1000.0;
|
||||
if (msecs < MINIMUM_AUDIO_BUFFER_TIME_MS) { // use more buffers if we have a VERY small sample set.
|
||||
numAudioBuffers = ((int)SDL_ceil(MINIMUM_AUDIO_BUFFER_TIME_MS / msecs) * 2);
|
||||
}
|
||||
|
||||
device->hidden->numAudioBuffers = numAudioBuffers;
|
||||
device->hidden->audioBuffer = SDL_calloc(numAudioBuffers, sizeof(AudioQueueBufferRef));
|
||||
if (device->hidden->audioBuffer == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
|
||||
#if DEBUG_COREAUDIO
|
||||
SDL_Log("COREAUDIO: numAudioBuffers == %d\n", numAudioBuffers);
|
||||
#endif
|
||||
|
||||
for (int i = 0; i < numAudioBuffers; i++) {
|
||||
result = AudioQueueAllocateBuffer(device->hidden->audioQueue, device->buffer_size, &device->hidden->audioBuffer[i]);
|
||||
CHECK_RESULT("AudioQueueAllocateBuffer");
|
||||
SDL_memset(device->hidden->audioBuffer[i]->mAudioData, device->silence_value, device->hidden->audioBuffer[i]->mAudioDataBytesCapacity);
|
||||
device->hidden->audioBuffer[i]->mAudioDataByteSize = device->hidden->audioBuffer[i]->mAudioDataBytesCapacity;
|
||||
// !!! FIXME: should we use AudioQueueEnqueueBufferWithParameters and specify all frames be "trimmed" so these are immediately ready to refill with SDL callback data?
|
||||
result = AudioQueueEnqueueBuffer(device->hidden->audioQueue, device->hidden->audioBuffer[i], 0, NULL);
|
||||
CHECK_RESULT("AudioQueueEnqueueBuffer");
|
||||
}
|
||||
|
||||
result = AudioQueueStart(device->hidden->audioQueue, NULL);
|
||||
CHECK_RESULT("AudioQueueStart");
|
||||
|
||||
return 0; // We're running!
|
||||
}
|
||||
|
||||
static int AudioQueueThreadEntry(void *arg)
|
||||
{
|
||||
SDL_AudioDevice *device = (SDL_AudioDevice *)arg;
|
||||
|
||||
if (device->iscapture) {
|
||||
SDL_CaptureAudioThreadSetup(device);
|
||||
} else {
|
||||
SDL_OutputAudioThreadSetup(device);
|
||||
}
|
||||
|
||||
if (PrepareAudioQueue(device) < 0) {
|
||||
device->hidden->thread_error = SDL_strdup(SDL_GetError());
|
||||
SDL_PostSemaphore(device->hidden->ready_semaphore);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// init was successful, alert parent thread and start running...
|
||||
SDL_PostSemaphore(device->hidden->ready_semaphore);
|
||||
|
||||
// This would be WaitDevice/WaitCaptureDevice in the normal SDL audio thread, but we get *BufferReadyCallback calls here to know when to iterate.
|
||||
while (!SDL_AtomicGet(&device->shutdown)) {
|
||||
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.10, 1);
|
||||
}
|
||||
|
||||
if (device->iscapture) {
|
||||
SDL_CaptureAudioThreadShutdown(device);
|
||||
} else {
|
||||
// Drain off any pending playback.
|
||||
const CFTimeInterval secs = (((CFTimeInterval)device->sample_frames) / ((CFTimeInterval)device->spec.freq)) * 2.0;
|
||||
CFRunLoopRunInMode(kCFRunLoopDefaultMode, secs, 0);
|
||||
SDL_OutputAudioThreadShutdown(device);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int COREAUDIO_OpenDevice(SDL_AudioDevice *device)
|
||||
{
|
||||
// Initialize all variables that we clean on shutdown
|
||||
device->hidden = (struct SDL_PrivateAudioData *)SDL_calloc(1, sizeof(*device->hidden));
|
||||
if (device->hidden == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
|
||||
#ifndef MACOSX_COREAUDIO
|
||||
if (!UpdateAudioSession(device, SDL_TRUE, SDL_TRUE)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Stop CoreAudio from doing expensive audio rate conversion
|
||||
@autoreleasepool {
|
||||
AVAudioSession *session = [AVAudioSession sharedInstance];
|
||||
[session setPreferredSampleRate:device->spec.freq error:nil];
|
||||
device->spec.freq = (int)session.sampleRate;
|
||||
#if TARGET_OS_TV
|
||||
if (device->iscapture) {
|
||||
[session setPreferredInputNumberOfChannels:device->spec.channels error:nil];
|
||||
device->spec.channels = session.preferredInputNumberOfChannels;
|
||||
} else {
|
||||
[session setPreferredOutputNumberOfChannels:device->spec.channels error:nil];
|
||||
device->spec.channels = session.preferredOutputNumberOfChannels;
|
||||
}
|
||||
#else
|
||||
// Calling setPreferredOutputNumberOfChannels seems to break audio output on iOS
|
||||
#endif // TARGET_OS_TV
|
||||
}
|
||||
#endif
|
||||
|
||||
// Setup a AudioStreamBasicDescription with the requested format
|
||||
AudioStreamBasicDescription *strdesc = &device->hidden->strdesc;
|
||||
strdesc->mFormatID = kAudioFormatLinearPCM;
|
||||
strdesc->mFormatFlags = kLinearPCMFormatFlagIsPacked;
|
||||
strdesc->mChannelsPerFrame = device->spec.channels;
|
||||
strdesc->mSampleRate = device->spec.freq;
|
||||
strdesc->mFramesPerPacket = 1;
|
||||
|
||||
const SDL_AudioFormat *closefmts = SDL_ClosestAudioFormats(device->spec.format);
|
||||
SDL_AudioFormat test_format;
|
||||
while ((test_format = *(closefmts++)) != 0) {
|
||||
// CoreAudio handles most of SDL's formats natively.
|
||||
switch (test_format) {
|
||||
case SDL_AUDIO_U8:
|
||||
case SDL_AUDIO_S8:
|
||||
case SDL_AUDIO_S16LE:
|
||||
case SDL_AUDIO_S16BE:
|
||||
case SDL_AUDIO_S32LE:
|
||||
case SDL_AUDIO_S32BE:
|
||||
case SDL_AUDIO_F32LE:
|
||||
case SDL_AUDIO_F32BE:
|
||||
break;
|
||||
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (!test_format) { // shouldn't happen, but just in case...
|
||||
return SDL_SetError("%s: Unsupported audio format", "coreaudio");
|
||||
}
|
||||
device->spec.format = test_format;
|
||||
strdesc->mBitsPerChannel = SDL_AUDIO_BITSIZE(test_format);
|
||||
if (SDL_AUDIO_ISBIGENDIAN(test_format)) {
|
||||
strdesc->mFormatFlags |= kLinearPCMFormatFlagIsBigEndian;
|
||||
}
|
||||
|
||||
if (SDL_AUDIO_ISFLOAT(test_format)) {
|
||||
strdesc->mFormatFlags |= kLinearPCMFormatFlagIsFloat;
|
||||
} else if (SDL_AUDIO_ISSIGNED(test_format)) {
|
||||
strdesc->mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
|
||||
}
|
||||
|
||||
strdesc->mBytesPerFrame = strdesc->mChannelsPerFrame * strdesc->mBitsPerChannel / 8;
|
||||
strdesc->mBytesPerPacket = strdesc->mBytesPerFrame * strdesc->mFramesPerPacket;
|
||||
|
||||
#ifdef MACOSX_COREAUDIO
|
||||
if (PrepareDevice(device) < 0) {
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
// This has to init in a new thread so it can get its own CFRunLoop. :/
|
||||
device->hidden->ready_semaphore = SDL_CreateSemaphore(0);
|
||||
if (!device->hidden->ready_semaphore) {
|
||||
return -1; // oh well.
|
||||
}
|
||||
|
||||
char threadname[64];
|
||||
SDL_GetAudioThreadName(device, threadname, sizeof(threadname));
|
||||
device->hidden->thread = SDL_CreateThreadInternal(AudioQueueThreadEntry, threadname, 0, device);
|
||||
if (!device->hidden->thread) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
SDL_WaitSemaphore(device->hidden->ready_semaphore);
|
||||
SDL_DestroySemaphore(device->hidden->ready_semaphore);
|
||||
device->hidden->ready_semaphore = NULL;
|
||||
|
||||
if ((device->hidden->thread != NULL) && (device->hidden->thread_error != NULL)) {
|
||||
SDL_WaitThread(device->hidden->thread, NULL);
|
||||
device->hidden->thread = NULL;
|
||||
return SDL_SetError("%s", device->hidden->thread_error);
|
||||
}
|
||||
|
||||
return (device->hidden->thread != NULL) ? 0 : -1;
|
||||
}
|
||||
|
||||
static void COREAUDIO_DeinitializeStart(void)
|
||||
{
|
||||
#ifdef MACOSX_COREAUDIO
|
||||
AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &devlist_address, DeviceListChangedNotification, NULL);
|
||||
AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &default_output_device_address, DefaultOutputDeviceChangedNotification, NULL);
|
||||
AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &default_input_device_address, DefaultInputDeviceChangedNotification, NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
static SDL_bool COREAUDIO_Init(SDL_AudioDriverImpl *impl)
|
||||
{
|
||||
impl->OpenDevice = COREAUDIO_OpenDevice;
|
||||
impl->PlayDevice = COREAUDIO_PlayDevice;
|
||||
impl->GetDeviceBuf = COREAUDIO_GetDeviceBuf;
|
||||
impl->CaptureFromDevice = COREAUDIO_CaptureFromDevice;
|
||||
impl->FlushCapture = COREAUDIO_FlushCapture;
|
||||
impl->CloseDevice = COREAUDIO_CloseDevice;
|
||||
impl->DeinitializeStart = COREAUDIO_DeinitializeStart;
|
||||
|
||||
#ifdef MACOSX_COREAUDIO
|
||||
impl->DetectDevices = COREAUDIO_DetectDevices;
|
||||
impl->FreeDeviceHandle = COREAUDIO_FreeDeviceHandle;
|
||||
#else
|
||||
impl->OnlyHasDefaultOutputDevice = SDL_TRUE;
|
||||
impl->OnlyHasDefaultCaptureDevice = SDL_TRUE;
|
||||
#endif
|
||||
|
||||
impl->ProvidesOwnCallbackThread = SDL_TRUE;
|
||||
impl->HasCaptureSupport = SDL_TRUE;
|
||||
|
||||
return SDL_TRUE;
|
||||
}
|
||||
|
||||
AudioBootStrap COREAUDIO_bootstrap = {
|
||||
"coreaudio", "CoreAudio", COREAUDIO_Init, SDL_FALSE
|
||||
};
|
||||
|
||||
#endif // SDL_AUDIO_DRIVER_COREAUDIO
|
||||
675
vendor/sdl-3.0.0/src/audio/directsound/SDL_directsound.c
vendored
Normal file
675
vendor/sdl-3.0.0/src/audio/directsound/SDL_directsound.c
vendored
Normal file
|
|
@ -0,0 +1,675 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifdef SDL_AUDIO_DRIVER_DSOUND
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
#include "SDL_directsound.h"
|
||||
#include <mmreg.h>
|
||||
#ifdef HAVE_MMDEVICEAPI_H
|
||||
#include "../../core/windows/SDL_immdevice.h"
|
||||
#endif
|
||||
|
||||
#ifndef WAVE_FORMAT_IEEE_FLOAT
|
||||
#define WAVE_FORMAT_IEEE_FLOAT 0x0003
|
||||
#endif
|
||||
|
||||
// For Vista+, we can enumerate DSound devices with IMMDevice
|
||||
#ifdef HAVE_MMDEVICEAPI_H
|
||||
static SDL_bool SupportsIMMDevice = SDL_FALSE;
|
||||
#endif
|
||||
|
||||
// DirectX function pointers for audio
|
||||
static void *DSoundDLL = NULL;
|
||||
typedef HRESULT(WINAPI *fnDirectSoundCreate8)(LPGUID, LPDIRECTSOUND *, LPUNKNOWN);
|
||||
typedef HRESULT(WINAPI *fnDirectSoundEnumerateW)(LPDSENUMCALLBACKW, LPVOID);
|
||||
typedef HRESULT(WINAPI *fnDirectSoundCaptureCreate8)(LPCGUID, LPDIRECTSOUNDCAPTURE8 *, LPUNKNOWN);
|
||||
typedef HRESULT(WINAPI *fnDirectSoundCaptureEnumerateW)(LPDSENUMCALLBACKW, LPVOID);
|
||||
typedef HRESULT(WINAPI *fnGetDeviceID)(LPCGUID, LPGUID);
|
||||
static fnDirectSoundCreate8 pDirectSoundCreate8 = NULL;
|
||||
static fnDirectSoundEnumerateW pDirectSoundEnumerateW = NULL;
|
||||
static fnDirectSoundCaptureCreate8 pDirectSoundCaptureCreate8 = NULL;
|
||||
static fnDirectSoundCaptureEnumerateW pDirectSoundCaptureEnumerateW = NULL;
|
||||
static fnGetDeviceID pGetDeviceID = NULL;
|
||||
|
||||
static const GUID SDL_KSDATAFORMAT_SUBTYPE_PCM = { 0x00000001, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } };
|
||||
static const GUID SDL_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = { 0x00000003, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } };
|
||||
|
||||
static void DSOUND_Unload(void)
|
||||
{
|
||||
pDirectSoundCreate8 = NULL;
|
||||
pDirectSoundEnumerateW = NULL;
|
||||
pDirectSoundCaptureCreate8 = NULL;
|
||||
pDirectSoundCaptureEnumerateW = NULL;
|
||||
pGetDeviceID = NULL;
|
||||
|
||||
if (DSoundDLL != NULL) {
|
||||
SDL_UnloadObject(DSoundDLL);
|
||||
DSoundDLL = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int DSOUND_Load(void)
|
||||
{
|
||||
int loaded = 0;
|
||||
|
||||
DSOUND_Unload();
|
||||
|
||||
DSoundDLL = SDL_LoadObject("DSOUND.DLL");
|
||||
if (DSoundDLL == NULL) {
|
||||
SDL_SetError("DirectSound: failed to load DSOUND.DLL");
|
||||
} else {
|
||||
// Now make sure we have DirectX 8 or better...
|
||||
#define DSOUNDLOAD(f) \
|
||||
{ \
|
||||
p##f = (fn##f)SDL_LoadFunction(DSoundDLL, #f); \
|
||||
if (!p##f) \
|
||||
loaded = 0; \
|
||||
}
|
||||
loaded = 1; // will reset if necessary.
|
||||
DSOUNDLOAD(DirectSoundCreate8);
|
||||
DSOUNDLOAD(DirectSoundEnumerateW);
|
||||
DSOUNDLOAD(DirectSoundCaptureCreate8);
|
||||
DSOUNDLOAD(DirectSoundCaptureEnumerateW);
|
||||
DSOUNDLOAD(GetDeviceID);
|
||||
#undef DSOUNDLOAD
|
||||
|
||||
if (!loaded) {
|
||||
SDL_SetError("DirectSound: System doesn't appear to have DX8.");
|
||||
}
|
||||
}
|
||||
|
||||
if (!loaded) {
|
||||
DSOUND_Unload();
|
||||
}
|
||||
|
||||
return loaded;
|
||||
}
|
||||
|
||||
static int SetDSerror(const char *function, int code)
|
||||
{
|
||||
const char *error;
|
||||
|
||||
switch (code) {
|
||||
case E_NOINTERFACE:
|
||||
error = "Unsupported interface -- Is DirectX 8.0 or later installed?";
|
||||
break;
|
||||
case DSERR_ALLOCATED:
|
||||
error = "Audio device in use";
|
||||
break;
|
||||
case DSERR_BADFORMAT:
|
||||
error = "Unsupported audio format";
|
||||
break;
|
||||
case DSERR_BUFFERLOST:
|
||||
error = "Mixing buffer was lost";
|
||||
break;
|
||||
case DSERR_CONTROLUNAVAIL:
|
||||
error = "Control requested is not available";
|
||||
break;
|
||||
case DSERR_INVALIDCALL:
|
||||
error = "Invalid call for the current state";
|
||||
break;
|
||||
case DSERR_INVALIDPARAM:
|
||||
error = "Invalid parameter";
|
||||
break;
|
||||
case DSERR_NODRIVER:
|
||||
error = "No audio device found";
|
||||
break;
|
||||
case DSERR_OUTOFMEMORY:
|
||||
error = "Out of memory";
|
||||
break;
|
||||
case DSERR_PRIOLEVELNEEDED:
|
||||
error = "Caller doesn't have priority";
|
||||
break;
|
||||
case DSERR_UNSUPPORTED:
|
||||
error = "Function not supported";
|
||||
break;
|
||||
default:
|
||||
error = "Unknown DirectSound error";
|
||||
break;
|
||||
}
|
||||
|
||||
return SDL_SetError("%s: %s (0x%x)", function, error, code);
|
||||
}
|
||||
|
||||
static void DSOUND_FreeDeviceHandle(SDL_AudioDevice *device)
|
||||
{
|
||||
#ifdef HAVE_MMDEVICEAPI_H
|
||||
if (SupportsIMMDevice) {
|
||||
SDL_IMMDevice_FreeDeviceHandle(device);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
SDL_free(device->handle);
|
||||
}
|
||||
}
|
||||
|
||||
// FindAllDevs is presumably only used on WinXP; Vista and later can use IMMDevice for better results.
|
||||
typedef struct FindAllDevsData
|
||||
{
|
||||
SDL_bool iscapture;
|
||||
SDL_AudioDevice **default_device;
|
||||
LPCGUID default_device_guid;
|
||||
} FindAllDevsData;
|
||||
|
||||
static BOOL CALLBACK FindAllDevs(LPGUID guid, LPCWSTR desc, LPCWSTR module, LPVOID userdata)
|
||||
{
|
||||
FindAllDevsData *data = (FindAllDevsData *) userdata;
|
||||
if (guid != NULL) { // skip default device
|
||||
char *str = WIN_LookupAudioDeviceName(desc, guid);
|
||||
if (str != NULL) {
|
||||
LPGUID cpyguid = (LPGUID)SDL_malloc(sizeof(GUID));
|
||||
if (cpyguid) {
|
||||
SDL_copyp(cpyguid, guid);
|
||||
|
||||
/* Note that spec is NULL, because we are required to connect to the
|
||||
* device before getting the channel mask and output format, making
|
||||
* this information inaccessible at enumeration time
|
||||
*/
|
||||
SDL_AudioDevice *device = SDL_AddAudioDevice(data->iscapture, str, NULL, cpyguid);
|
||||
if (device && data->default_device && data->default_device_guid) {
|
||||
if (SDL_memcmp(cpyguid, data->default_device_guid, sizeof (GUID)) == 0) {
|
||||
*data->default_device = device;
|
||||
}
|
||||
}
|
||||
}
|
||||
SDL_free(str); // SDL_AddAudioDevice() makes a copy of this string.
|
||||
}
|
||||
}
|
||||
return TRUE; // keep enumerating.
|
||||
}
|
||||
|
||||
static void DSOUND_DetectDevices(SDL_AudioDevice **default_output, SDL_AudioDevice **default_capture)
|
||||
{
|
||||
#ifdef HAVE_MMDEVICEAPI_H
|
||||
if (SupportsIMMDevice) {
|
||||
SDL_IMMDevice_EnumerateEndpoints(default_output, default_capture);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
// Without IMMDevice, you can enumerate devices and figure out the default devices,
|
||||
// but you won't get device hotplug or default device change notifications. But this is
|
||||
// only for WinXP; Windows Vista and later should be using IMMDevice.
|
||||
FindAllDevsData data;
|
||||
GUID guid;
|
||||
|
||||
data.iscapture = SDL_TRUE;
|
||||
data.default_device = default_capture;
|
||||
data.default_device_guid = (pGetDeviceID(&DSDEVID_DefaultCapture, &guid) == DS_OK) ? &guid : NULL;
|
||||
pDirectSoundCaptureEnumerateW(FindAllDevs, &data);
|
||||
|
||||
data.iscapture = SDL_FALSE;
|
||||
data.default_device = default_output;
|
||||
data.default_device_guid = (pGetDeviceID(&DSDEVID_DefaultPlayback, &guid) == DS_OK) ? &guid : NULL;
|
||||
pDirectSoundEnumerateW(FindAllDevs, &data);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static int DSOUND_WaitDevice(SDL_AudioDevice *device)
|
||||
{
|
||||
/* Semi-busy wait, since we have no way of getting play notification
|
||||
on a primary mixing buffer located in hardware (DirectX 5.0)
|
||||
*/
|
||||
while (!SDL_AtomicGet(&device->shutdown)) {
|
||||
DWORD status = 0;
|
||||
DWORD cursor = 0;
|
||||
DWORD junk = 0;
|
||||
HRESULT result = DS_OK;
|
||||
|
||||
// Try to restore a lost sound buffer
|
||||
IDirectSoundBuffer_GetStatus(device->hidden->mixbuf, &status);
|
||||
if (status & DSBSTATUS_BUFFERLOST) {
|
||||
IDirectSoundBuffer_Restore(device->hidden->mixbuf);
|
||||
} else if (!(status & DSBSTATUS_PLAYING)) {
|
||||
result = IDirectSoundBuffer_Play(device->hidden->mixbuf, 0, 0, DSBPLAY_LOOPING);
|
||||
} else {
|
||||
// Find out where we are playing
|
||||
result = IDirectSoundBuffer_GetCurrentPosition(device->hidden->mixbuf, &junk, &cursor);
|
||||
if ((result == DS_OK) && ((cursor / device->buffer_size) != device->hidden->lastchunk)) {
|
||||
break; // ready for next chunk!
|
||||
}
|
||||
}
|
||||
|
||||
if ((result != DS_OK) && (result != DSERR_BUFFERLOST)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
SDL_Delay(1); // not ready yet; sleep a bit.
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int DSOUND_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
|
||||
{
|
||||
// Unlock the buffer, allowing it to play
|
||||
SDL_assert(buflen == device->buffer_size);
|
||||
if (IDirectSoundBuffer_Unlock(device->hidden->mixbuf, (LPVOID) buffer, buflen, NULL, 0) != DS_OK) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static Uint8 *DSOUND_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
|
||||
{
|
||||
DWORD cursor = 0;
|
||||
DWORD junk = 0;
|
||||
HRESULT result = DS_OK;
|
||||
|
||||
SDL_assert(*buffer_size == device->buffer_size);
|
||||
|
||||
// Figure out which blocks to fill next
|
||||
device->hidden->locked_buf = NULL;
|
||||
result = IDirectSoundBuffer_GetCurrentPosition(device->hidden->mixbuf,
|
||||
&junk, &cursor);
|
||||
if (result == DSERR_BUFFERLOST) {
|
||||
IDirectSoundBuffer_Restore(device->hidden->mixbuf);
|
||||
result = IDirectSoundBuffer_GetCurrentPosition(device->hidden->mixbuf,
|
||||
&junk, &cursor);
|
||||
}
|
||||
if (result != DS_OK) {
|
||||
SetDSerror("DirectSound GetCurrentPosition", result);
|
||||
return NULL;
|
||||
}
|
||||
cursor /= device->buffer_size;
|
||||
#ifdef DEBUG_SOUND
|
||||
// Detect audio dropouts
|
||||
{
|
||||
DWORD spot = cursor;
|
||||
if (spot < device->hidden->lastchunk) {
|
||||
spot += device->hidden->num_buffers;
|
||||
}
|
||||
if (spot > device->hidden->lastchunk + 1) {
|
||||
fprintf(stderr, "Audio dropout, missed %d fragments\n",
|
||||
(spot - (device->hidden->lastchunk + 1)));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
device->hidden->lastchunk = cursor;
|
||||
cursor = (cursor + 1) % device->hidden->num_buffers;
|
||||
cursor *= device->buffer_size;
|
||||
|
||||
// Lock the audio buffer
|
||||
DWORD rawlen = 0;
|
||||
result = IDirectSoundBuffer_Lock(device->hidden->mixbuf, cursor,
|
||||
device->buffer_size,
|
||||
(LPVOID *)&device->hidden->locked_buf,
|
||||
&rawlen, NULL, &junk, 0);
|
||||
if (result == DSERR_BUFFERLOST) {
|
||||
IDirectSoundBuffer_Restore(device->hidden->mixbuf);
|
||||
result = IDirectSoundBuffer_Lock(device->hidden->mixbuf, cursor,
|
||||
device->buffer_size,
|
||||
(LPVOID *)&device->hidden->locked_buf, &rawlen, NULL,
|
||||
&junk, 0);
|
||||
}
|
||||
if (result != DS_OK) {
|
||||
SetDSerror("DirectSound Lock", result);
|
||||
return NULL;
|
||||
}
|
||||
return device->hidden->locked_buf;
|
||||
}
|
||||
|
||||
static int DSOUND_WaitCaptureDevice(SDL_AudioDevice *device)
|
||||
{
|
||||
struct SDL_PrivateAudioData *h = device->hidden;
|
||||
while (!SDL_AtomicGet(&device->shutdown)) {
|
||||
DWORD junk, cursor;
|
||||
if (IDirectSoundCaptureBuffer_GetCurrentPosition(h->capturebuf, &junk, &cursor) != DS_OK) {
|
||||
return -1;
|
||||
} else if ((cursor / device->buffer_size) != h->lastchunk) {
|
||||
break;
|
||||
}
|
||||
SDL_Delay(1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int DSOUND_CaptureFromDevice(SDL_AudioDevice *device, void *buffer, int buflen)
|
||||
{
|
||||
struct SDL_PrivateAudioData *h = device->hidden;
|
||||
DWORD ptr1len, ptr2len;
|
||||
VOID *ptr1, *ptr2;
|
||||
|
||||
SDL_assert(buflen == device->buffer_size);
|
||||
|
||||
if (IDirectSoundCaptureBuffer_Lock(h->capturebuf, h->lastchunk * buflen, buflen, &ptr1, &ptr1len, &ptr2, &ptr2len, 0) != DS_OK) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
SDL_assert(ptr1len == (DWORD)buflen);
|
||||
SDL_assert(ptr2 == NULL);
|
||||
SDL_assert(ptr2len == 0);
|
||||
|
||||
SDL_memcpy(buffer, ptr1, ptr1len);
|
||||
|
||||
if (IDirectSoundCaptureBuffer_Unlock(h->capturebuf, ptr1, ptr1len, ptr2, ptr2len) != DS_OK) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
h->lastchunk = (h->lastchunk + 1) % h->num_buffers;
|
||||
|
||||
return (int) ptr1len;
|
||||
}
|
||||
|
||||
static void DSOUND_FlushCapture(SDL_AudioDevice *device)
|
||||
{
|
||||
struct SDL_PrivateAudioData *h = device->hidden;
|
||||
DWORD junk, cursor;
|
||||
if (IDirectSoundCaptureBuffer_GetCurrentPosition(h->capturebuf, &junk, &cursor) == DS_OK) {
|
||||
h->lastchunk = cursor / device->buffer_size;
|
||||
}
|
||||
}
|
||||
|
||||
static void DSOUND_CloseDevice(SDL_AudioDevice *device)
|
||||
{
|
||||
if (device->hidden) {
|
||||
if (device->hidden->mixbuf != NULL) {
|
||||
IDirectSoundBuffer_Stop(device->hidden->mixbuf);
|
||||
IDirectSoundBuffer_Release(device->hidden->mixbuf);
|
||||
}
|
||||
if (device->hidden->sound != NULL) {
|
||||
IDirectSound_Release(device->hidden->sound);
|
||||
}
|
||||
if (device->hidden->capturebuf != NULL) {
|
||||
IDirectSoundCaptureBuffer_Stop(device->hidden->capturebuf);
|
||||
IDirectSoundCaptureBuffer_Release(device->hidden->capturebuf);
|
||||
}
|
||||
if (device->hidden->capture != NULL) {
|
||||
IDirectSoundCapture_Release(device->hidden->capture);
|
||||
}
|
||||
SDL_free(device->hidden);
|
||||
device->hidden = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* This function tries to create a secondary audio buffer, and returns the
|
||||
number of audio chunks available in the created buffer. This is for
|
||||
playback devices, not capture.
|
||||
*/
|
||||
static int CreateSecondary(SDL_AudioDevice *device, const DWORD bufsize, WAVEFORMATEX *wfmt)
|
||||
{
|
||||
LPDIRECTSOUND sndObj = device->hidden->sound;
|
||||
LPDIRECTSOUNDBUFFER *sndbuf = &device->hidden->mixbuf;
|
||||
HRESULT result = DS_OK;
|
||||
DSBUFFERDESC format;
|
||||
LPVOID pvAudioPtr1, pvAudioPtr2;
|
||||
DWORD dwAudioBytes1, dwAudioBytes2;
|
||||
|
||||
// Try to create the secondary buffer
|
||||
SDL_zero(format);
|
||||
format.dwSize = sizeof(format);
|
||||
format.dwFlags = DSBCAPS_GETCURRENTPOSITION2;
|
||||
format.dwFlags |= DSBCAPS_GLOBALFOCUS;
|
||||
format.dwBufferBytes = bufsize;
|
||||
format.lpwfxFormat = wfmt;
|
||||
result = IDirectSound_CreateSoundBuffer(sndObj, &format, sndbuf, NULL);
|
||||
if (result != DS_OK) {
|
||||
return SetDSerror("DirectSound CreateSoundBuffer", result);
|
||||
}
|
||||
IDirectSoundBuffer_SetFormat(*sndbuf, wfmt);
|
||||
|
||||
// Silence the initial audio buffer
|
||||
result = IDirectSoundBuffer_Lock(*sndbuf, 0, format.dwBufferBytes,
|
||||
(LPVOID *)&pvAudioPtr1, &dwAudioBytes1,
|
||||
(LPVOID *)&pvAudioPtr2, &dwAudioBytes2,
|
||||
DSBLOCK_ENTIREBUFFER);
|
||||
if (result == DS_OK) {
|
||||
SDL_memset(pvAudioPtr1, device->silence_value, dwAudioBytes1);
|
||||
IDirectSoundBuffer_Unlock(*sndbuf,
|
||||
(LPVOID)pvAudioPtr1, dwAudioBytes1,
|
||||
(LPVOID)pvAudioPtr2, dwAudioBytes2);
|
||||
}
|
||||
|
||||
return 0; // We're ready to go
|
||||
}
|
||||
|
||||
/* This function tries to create a capture buffer, and returns the
|
||||
number of audio chunks available in the created buffer. This is for
|
||||
capture devices, not playback.
|
||||
*/
|
||||
static int CreateCaptureBuffer(SDL_AudioDevice *device, const DWORD bufsize, WAVEFORMATEX *wfmt)
|
||||
{
|
||||
LPDIRECTSOUNDCAPTURE capture = device->hidden->capture;
|
||||
LPDIRECTSOUNDCAPTUREBUFFER *capturebuf = &device->hidden->capturebuf;
|
||||
DSCBUFFERDESC format;
|
||||
HRESULT result;
|
||||
|
||||
SDL_zero(format);
|
||||
format.dwSize = sizeof(format);
|
||||
format.dwFlags = DSCBCAPS_WAVEMAPPED;
|
||||
format.dwBufferBytes = bufsize;
|
||||
format.lpwfxFormat = wfmt;
|
||||
|
||||
result = IDirectSoundCapture_CreateCaptureBuffer(capture, &format, capturebuf, NULL);
|
||||
if (result != DS_OK) {
|
||||
return SetDSerror("DirectSound CreateCaptureBuffer", result);
|
||||
}
|
||||
|
||||
result = IDirectSoundCaptureBuffer_Start(*capturebuf, DSCBSTART_LOOPING);
|
||||
if (result != DS_OK) {
|
||||
IDirectSoundCaptureBuffer_Release(*capturebuf);
|
||||
return SetDSerror("DirectSound Start", result);
|
||||
}
|
||||
|
||||
#if 0
|
||||
// presumably this starts at zero, but just in case...
|
||||
result = IDirectSoundCaptureBuffer_GetCurrentPosition(*capturebuf, &junk, &cursor);
|
||||
if (result != DS_OK) {
|
||||
IDirectSoundCaptureBuffer_Stop(*capturebuf);
|
||||
IDirectSoundCaptureBuffer_Release(*capturebuf);
|
||||
return SetDSerror("DirectSound GetCurrentPosition", result);
|
||||
}
|
||||
|
||||
device->hidden->lastchunk = cursor / device->buffer_size;
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int DSOUND_OpenDevice(SDL_AudioDevice *device)
|
||||
{
|
||||
// Initialize all variables that we clean on shutdown
|
||||
device->hidden = (struct SDL_PrivateAudioData *)SDL_calloc(1, sizeof(*device->hidden));
|
||||
if (device->hidden == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
|
||||
// Open the audio device
|
||||
LPGUID guid;
|
||||
#ifdef HAVE_MMDEVICEAPI_H
|
||||
if (SupportsIMMDevice) {
|
||||
guid = SDL_IMMDevice_GetDirectSoundGUID(device);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
guid = (LPGUID) device->handle;
|
||||
}
|
||||
|
||||
SDL_assert(guid != NULL);
|
||||
|
||||
HRESULT result;
|
||||
if (device->iscapture) {
|
||||
result = pDirectSoundCaptureCreate8(guid, &device->hidden->capture, NULL);
|
||||
if (result != DS_OK) {
|
||||
return SetDSerror("DirectSoundCaptureCreate8", result);
|
||||
}
|
||||
} else {
|
||||
result = pDirectSoundCreate8(guid, &device->hidden->sound, NULL);
|
||||
if (result != DS_OK) {
|
||||
return SetDSerror("DirectSoundCreate8", result);
|
||||
}
|
||||
result = IDirectSound_SetCooperativeLevel(device->hidden->sound,
|
||||
GetDesktopWindow(),
|
||||
DSSCL_NORMAL);
|
||||
if (result != DS_OK) {
|
||||
return SetDSerror("DirectSound SetCooperativeLevel", result);
|
||||
}
|
||||
}
|
||||
|
||||
const DWORD numchunks = 8;
|
||||
SDL_bool tried_format = SDL_FALSE;
|
||||
SDL_AudioFormat test_format;
|
||||
const SDL_AudioFormat *closefmts = SDL_ClosestAudioFormats(device->spec.format);
|
||||
while ((test_format = *(closefmts++)) != 0) {
|
||||
switch (test_format) {
|
||||
case SDL_AUDIO_U8:
|
||||
case SDL_AUDIO_S16:
|
||||
case SDL_AUDIO_S32:
|
||||
case SDL_AUDIO_F32:
|
||||
tried_format = SDL_TRUE;
|
||||
|
||||
device->spec.format = test_format;
|
||||
|
||||
// Update the fragment size as size in bytes
|
||||
SDL_UpdatedAudioDeviceFormat(device);
|
||||
|
||||
const DWORD bufsize = numchunks * device->buffer_size;
|
||||
if ((bufsize < DSBSIZE_MIN) || (bufsize > DSBSIZE_MAX)) {
|
||||
SDL_SetError("Sound buffer size must be between %d and %d",
|
||||
(int)((DSBSIZE_MIN < numchunks) ? 1 : DSBSIZE_MIN / numchunks),
|
||||
(int)(DSBSIZE_MAX / numchunks));
|
||||
} else {
|
||||
WAVEFORMATEXTENSIBLE wfmt;
|
||||
SDL_zero(wfmt);
|
||||
if (device->spec.channels > 2) {
|
||||
wfmt.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
|
||||
wfmt.Format.cbSize = sizeof(wfmt) - sizeof(WAVEFORMATEX);
|
||||
|
||||
if (SDL_AUDIO_ISFLOAT(device->spec.format)) {
|
||||
SDL_memcpy(&wfmt.SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, sizeof(GUID));
|
||||
} else {
|
||||
SDL_memcpy(&wfmt.SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_PCM, sizeof(GUID));
|
||||
}
|
||||
wfmt.Samples.wValidBitsPerSample = SDL_AUDIO_BITSIZE(device->spec.format);
|
||||
|
||||
switch (device->spec.channels) {
|
||||
case 3: // 3.0 (or 2.1)
|
||||
wfmt.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER;
|
||||
break;
|
||||
case 4: // 4.0
|
||||
wfmt.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT;
|
||||
break;
|
||||
case 5: // 5.0 (or 4.1)
|
||||
wfmt.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT;
|
||||
break;
|
||||
case 6: // 5.1
|
||||
wfmt.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT;
|
||||
break;
|
||||
case 7: // 6.1
|
||||
wfmt.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_BACK_CENTER;
|
||||
break;
|
||||
case 8: // 7.1
|
||||
wfmt.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT;
|
||||
break;
|
||||
default:
|
||||
SDL_assert(0 && "Unsupported channel count!");
|
||||
break;
|
||||
}
|
||||
} else if (SDL_AUDIO_ISFLOAT(device->spec.format)) {
|
||||
wfmt.Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
|
||||
} else {
|
||||
wfmt.Format.wFormatTag = WAVE_FORMAT_PCM;
|
||||
}
|
||||
|
||||
wfmt.Format.wBitsPerSample = SDL_AUDIO_BITSIZE(device->spec.format);
|
||||
wfmt.Format.nChannels = (WORD)device->spec.channels;
|
||||
wfmt.Format.nSamplesPerSec = device->spec.freq;
|
||||
wfmt.Format.nBlockAlign = wfmt.Format.nChannels * (wfmt.Format.wBitsPerSample / 8);
|
||||
wfmt.Format.nAvgBytesPerSec = wfmt.Format.nSamplesPerSec * wfmt.Format.nBlockAlign;
|
||||
|
||||
const int rc = device->iscapture ? CreateCaptureBuffer(device, bufsize, (WAVEFORMATEX *)&wfmt) : CreateSecondary(device, bufsize, (WAVEFORMATEX *)&wfmt);
|
||||
if (rc == 0) {
|
||||
device->hidden->num_buffers = numchunks;
|
||||
break;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (!test_format) {
|
||||
if (tried_format) {
|
||||
return -1; // CreateSecondary() should have called SDL_SetError().
|
||||
}
|
||||
return SDL_SetError("%s: Unsupported audio format", "directsound");
|
||||
}
|
||||
|
||||
// Playback buffers will auto-start playing in DSOUND_WaitDevice()
|
||||
|
||||
return 0; // good to go.
|
||||
}
|
||||
|
||||
static void DSOUND_DeinitializeStart(void)
|
||||
{
|
||||
#ifdef HAVE_MMDEVICEAPI_H
|
||||
if (SupportsIMMDevice) {
|
||||
SDL_IMMDevice_Quit();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void DSOUND_Deinitialize(void)
|
||||
{
|
||||
DSOUND_Unload();
|
||||
#ifdef HAVE_MMDEVICEAPI_H
|
||||
SupportsIMMDevice = SDL_FALSE;
|
||||
#endif
|
||||
}
|
||||
|
||||
static SDL_bool DSOUND_Init(SDL_AudioDriverImpl *impl)
|
||||
{
|
||||
if (!DSOUND_Load()) {
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
#ifdef HAVE_MMDEVICEAPI_H
|
||||
SupportsIMMDevice = !(SDL_IMMDevice_Init(NULL) < 0);
|
||||
#endif
|
||||
|
||||
impl->DetectDevices = DSOUND_DetectDevices;
|
||||
impl->OpenDevice = DSOUND_OpenDevice;
|
||||
impl->PlayDevice = DSOUND_PlayDevice;
|
||||
impl->WaitDevice = DSOUND_WaitDevice;
|
||||
impl->GetDeviceBuf = DSOUND_GetDeviceBuf;
|
||||
impl->WaitCaptureDevice = DSOUND_WaitCaptureDevice;
|
||||
impl->CaptureFromDevice = DSOUND_CaptureFromDevice;
|
||||
impl->FlushCapture = DSOUND_FlushCapture;
|
||||
impl->CloseDevice = DSOUND_CloseDevice;
|
||||
impl->FreeDeviceHandle = DSOUND_FreeDeviceHandle;
|
||||
impl->DeinitializeStart = DSOUND_DeinitializeStart;
|
||||
impl->Deinitialize = DSOUND_Deinitialize;
|
||||
|
||||
impl->HasCaptureSupport = SDL_TRUE;
|
||||
|
||||
return SDL_TRUE;
|
||||
}
|
||||
|
||||
AudioBootStrap DSOUND_bootstrap = {
|
||||
"directsound", "DirectSound", DSOUND_Init, SDL_FALSE
|
||||
};
|
||||
|
||||
#endif // SDL_AUDIO_DRIVER_DSOUND
|
||||
43
vendor/sdl-3.0.0/src/audio/directsound/SDL_directsound.h
vendored
Normal file
43
vendor/sdl-3.0.0/src/audio/directsound/SDL_directsound.h
vendored
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifndef SDL_directsound_h_
|
||||
#define SDL_directsound_h_
|
||||
|
||||
#include "../../core/windows/SDL_directx.h"
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
|
||||
// The DirectSound objects
|
||||
struct SDL_PrivateAudioData
|
||||
{
|
||||
// !!! FIXME: make this a union with capture/playback sections?
|
||||
LPDIRECTSOUND sound;
|
||||
LPDIRECTSOUNDBUFFER mixbuf;
|
||||
LPDIRECTSOUNDCAPTURE capture;
|
||||
LPDIRECTSOUNDCAPTUREBUFFER capturebuf;
|
||||
int num_buffers;
|
||||
DWORD lastchunk;
|
||||
Uint8 *locked_buf;
|
||||
};
|
||||
|
||||
#endif // SDL_directsound_h_
|
||||
173
vendor/sdl-3.0.0/src/audio/disk/SDL_diskaudio.c
vendored
Normal file
173
vendor/sdl-3.0.0/src/audio/disk/SDL_diskaudio.c
vendored
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifdef SDL_AUDIO_DRIVER_DISK
|
||||
|
||||
// Output raw audio data to a file.
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
#include "SDL_diskaudio.h"
|
||||
|
||||
// !!! FIXME: these should be SDL hints, not environment variables.
|
||||
// environment variables and defaults.
|
||||
#define DISKENVR_OUTFILE "SDL_DISKAUDIOFILE"
|
||||
#define DISKDEFAULT_OUTFILE "sdlaudio.raw"
|
||||
#define DISKENVR_INFILE "SDL_DISKAUDIOFILEIN"
|
||||
#define DISKDEFAULT_INFILE "sdlaudio-in.raw"
|
||||
#define DISKENVR_IODELAY "SDL_DISKAUDIODELAY"
|
||||
|
||||
static int DISKAUDIO_WaitDevice(SDL_AudioDevice *device)
|
||||
{
|
||||
SDL_Delay(device->hidden->io_delay);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int DISKAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buffer_size)
|
||||
{
|
||||
const int written = (int)SDL_RWwrite(device->hidden->io, buffer, (size_t)buffer_size);
|
||||
if (written != buffer_size) { // If we couldn't write, assume fatal error for now
|
||||
return -1;
|
||||
}
|
||||
#ifdef DEBUG_AUDIO
|
||||
SDL_Log("DISKAUDIO: Wrote %d bytes of audio data", (int) written);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static Uint8 *DISKAUDIO_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
|
||||
{
|
||||
return device->hidden->mixbuf;
|
||||
}
|
||||
|
||||
static int DISKAUDIO_CaptureFromDevice(SDL_AudioDevice *device, void *buffer, int buflen)
|
||||
{
|
||||
struct SDL_PrivateAudioData *h = device->hidden;
|
||||
const int origbuflen = buflen;
|
||||
|
||||
if (h->io) {
|
||||
const int br = (int)SDL_RWread(h->io, buffer, (size_t)buflen);
|
||||
buflen -= br;
|
||||
buffer = ((Uint8 *)buffer) + br;
|
||||
if (buflen > 0) { // EOF (or error, but whatever).
|
||||
SDL_RWclose(h->io);
|
||||
h->io = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// if we ran out of file, just write silence.
|
||||
SDL_memset(buffer, device->silence_value, buflen);
|
||||
|
||||
return origbuflen;
|
||||
}
|
||||
|
||||
static void DISKAUDIO_FlushCapture(SDL_AudioDevice *device)
|
||||
{
|
||||
// no op...we don't advance the file pointer or anything.
|
||||
}
|
||||
|
||||
static void DISKAUDIO_CloseDevice(SDL_AudioDevice *device)
|
||||
{
|
||||
if (device->hidden) {
|
||||
if (device->hidden->io != NULL) {
|
||||
SDL_RWclose(device->hidden->io);
|
||||
}
|
||||
SDL_free(device->hidden->mixbuf);
|
||||
SDL_free(device->hidden);
|
||||
device->hidden = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static const char *get_filename(const SDL_bool iscapture)
|
||||
{
|
||||
const char *devname = SDL_getenv(iscapture ? DISKENVR_INFILE : DISKENVR_OUTFILE);
|
||||
if (devname == NULL) {
|
||||
devname = iscapture ? DISKDEFAULT_INFILE : DISKDEFAULT_OUTFILE;
|
||||
}
|
||||
return devname;
|
||||
}
|
||||
|
||||
static int DISKAUDIO_OpenDevice(SDL_AudioDevice *device)
|
||||
{
|
||||
SDL_bool iscapture = device->iscapture;
|
||||
const char *fname = get_filename(iscapture);
|
||||
const char *envr = SDL_getenv(DISKENVR_IODELAY);
|
||||
|
||||
device->hidden = (struct SDL_PrivateAudioData *) SDL_calloc(1, sizeof(*device->hidden));
|
||||
if (device->hidden == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
|
||||
if (envr != NULL) {
|
||||
device->hidden->io_delay = SDL_atoi(envr);
|
||||
} else {
|
||||
device->hidden->io_delay = ((device->sample_frames * 1000) / device->spec.freq);
|
||||
}
|
||||
|
||||
// Open the "audio device"
|
||||
device->hidden->io = SDL_RWFromFile(fname, iscapture ? "rb" : "wb");
|
||||
if (device->hidden->io == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Allocate mixing buffer
|
||||
if (!iscapture) {
|
||||
device->hidden->mixbuf = (Uint8 *)SDL_malloc(device->buffer_size);
|
||||
if (device->hidden->mixbuf == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
SDL_memset(device->hidden->mixbuf, device->silence_value, device->buffer_size);
|
||||
}
|
||||
|
||||
SDL_LogCritical(SDL_LOG_CATEGORY_AUDIO, "You are using the SDL disk i/o audio driver!");
|
||||
SDL_LogCritical(SDL_LOG_CATEGORY_AUDIO, " %s file [%s].\n", iscapture ? "Reading from" : "Writing to", fname);
|
||||
|
||||
return 0; // We're ready to rock and roll. :-)
|
||||
}
|
||||
|
||||
static void DISKAUDIO_DetectDevices(SDL_AudioDevice **default_output, SDL_AudioDevice **default_capture)
|
||||
{
|
||||
*default_output = SDL_AddAudioDevice(SDL_FALSE, DEFAULT_OUTPUT_DEVNAME, NULL, (void *)0x1);
|
||||
*default_capture = SDL_AddAudioDevice(SDL_TRUE, DEFAULT_INPUT_DEVNAME, NULL, (void *)0x2);
|
||||
}
|
||||
|
||||
static SDL_bool DISKAUDIO_Init(SDL_AudioDriverImpl *impl)
|
||||
{
|
||||
impl->OpenDevice = DISKAUDIO_OpenDevice;
|
||||
impl->WaitDevice = DISKAUDIO_WaitDevice;
|
||||
impl->WaitCaptureDevice = DISKAUDIO_WaitDevice;
|
||||
impl->PlayDevice = DISKAUDIO_PlayDevice;
|
||||
impl->GetDeviceBuf = DISKAUDIO_GetDeviceBuf;
|
||||
impl->CaptureFromDevice = DISKAUDIO_CaptureFromDevice;
|
||||
impl->FlushCapture = DISKAUDIO_FlushCapture;
|
||||
impl->CloseDevice = DISKAUDIO_CloseDevice;
|
||||
impl->DetectDevices = DISKAUDIO_DetectDevices;
|
||||
|
||||
impl->HasCaptureSupport = SDL_TRUE;
|
||||
|
||||
return SDL_TRUE;
|
||||
}
|
||||
|
||||
AudioBootStrap DISKAUDIO_bootstrap = {
|
||||
"disk", "direct-to-disk audio", DISKAUDIO_Init, SDL_TRUE
|
||||
};
|
||||
|
||||
#endif // SDL_AUDIO_DRIVER_DISK
|
||||
36
vendor/sdl-3.0.0/src/audio/disk/SDL_diskaudio.h
vendored
Normal file
36
vendor/sdl-3.0.0/src/audio/disk/SDL_diskaudio.h
vendored
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifndef SDL_diskaudio_h_
|
||||
#define SDL_diskaudio_h_
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
|
||||
struct SDL_PrivateAudioData
|
||||
{
|
||||
// The file descriptor for the audio device
|
||||
SDL_RWops *io;
|
||||
Uint32 io_delay;
|
||||
Uint8 *mixbuf;
|
||||
};
|
||||
|
||||
#endif // SDL_diskaudio_h_
|
||||
304
vendor/sdl-3.0.0/src/audio/dsp/SDL_dspaudio.c
vendored
Normal file
304
vendor/sdl-3.0.0/src/audio/dsp/SDL_dspaudio.c
vendored
Normal file
|
|
@ -0,0 +1,304 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
// !!! FIXME: clean out perror and fprintf calls in here.
|
||||
|
||||
#ifdef SDL_AUDIO_DRIVER_OSS
|
||||
|
||||
#include <stdio.h> // For perror()
|
||||
#include <string.h> // For strerror()
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <sys/soundcard.h>
|
||||
|
||||
#include "../SDL_audiodev_c.h"
|
||||
#include "../../SDL_utils_c.h"
|
||||
#include "SDL_dspaudio.h"
|
||||
|
||||
static void DSP_DetectDevices(SDL_AudioDevice **default_output, SDL_AudioDevice **default_capture)
|
||||
{
|
||||
SDL_EnumUnixAudioDevices(SDL_FALSE, NULL);
|
||||
}
|
||||
|
||||
static void DSP_CloseDevice(SDL_AudioDevice *device)
|
||||
{
|
||||
if (device->hidden) {
|
||||
if (device->hidden->audio_fd >= 0) {
|
||||
close(device->hidden->audio_fd);
|
||||
}
|
||||
SDL_free(device->hidden->mixbuf);
|
||||
SDL_free(device->hidden);
|
||||
}
|
||||
}
|
||||
|
||||
static int DSP_OpenDevice(SDL_AudioDevice *device)
|
||||
{
|
||||
// Make sure fragment size stays a power of 2, or OSS fails.
|
||||
// (I don't know which of these are actually legal values, though...)
|
||||
if (device->spec.channels > 8) {
|
||||
device->spec.channels = 8;
|
||||
} else if (device->spec.channels > 4) {
|
||||
device->spec.channels = 4;
|
||||
} else if (device->spec.channels > 2) {
|
||||
device->spec.channels = 2;
|
||||
}
|
||||
|
||||
// Initialize all variables that we clean on shutdown
|
||||
device->hidden = (struct SDL_PrivateAudioData *) SDL_calloc(1, sizeof(*device->hidden));
|
||||
if (device->hidden == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
|
||||
// Open the audio device; we hardcode the device path in `device->name` for lack of better info, so use that.
|
||||
const int flags = ((device->iscapture) ? OPEN_FLAGS_INPUT : OPEN_FLAGS_OUTPUT);
|
||||
device->hidden->audio_fd = open(device->name, flags | O_CLOEXEC, 0);
|
||||
if (device->hidden->audio_fd < 0) {
|
||||
return SDL_SetError("Couldn't open %s: %s", device->name, strerror(errno));
|
||||
}
|
||||
|
||||
// Make the file descriptor use blocking i/o with fcntl()
|
||||
{
|
||||
const long ctlflags = fcntl(device->hidden->audio_fd, F_GETFL) & ~O_NONBLOCK;
|
||||
if (fcntl(device->hidden->audio_fd, F_SETFL, ctlflags) < 0) {
|
||||
return SDL_SetError("Couldn't set audio blocking mode");
|
||||
}
|
||||
}
|
||||
|
||||
// Get a list of supported hardware formats
|
||||
int value;
|
||||
if (ioctl(device->hidden->audio_fd, SNDCTL_DSP_GETFMTS, &value) < 0) {
|
||||
perror("SNDCTL_DSP_GETFMTS");
|
||||
return SDL_SetError("Couldn't get audio format list");
|
||||
}
|
||||
|
||||
// Try for a closest match on audio format
|
||||
int format = 0;
|
||||
SDL_AudioFormat test_format;
|
||||
const SDL_AudioFormat *closefmts = SDL_ClosestAudioFormats(device->spec.format);
|
||||
while ((test_format = *(closefmts++)) != 0) {
|
||||
#ifdef DEBUG_AUDIO
|
||||
fprintf(stderr, "Trying format 0x%4.4x\n", test_format);
|
||||
#endif
|
||||
switch (test_format) {
|
||||
case SDL_AUDIO_U8:
|
||||
if (value & AFMT_U8) {
|
||||
format = AFMT_U8;
|
||||
}
|
||||
break;
|
||||
case SDL_AUDIO_S16LE:
|
||||
if (value & AFMT_S16_LE) {
|
||||
format = AFMT_S16_LE;
|
||||
}
|
||||
break;
|
||||
case SDL_AUDIO_S16BE:
|
||||
if (value & AFMT_S16_BE) {
|
||||
format = AFMT_S16_BE;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (format == 0) {
|
||||
return SDL_SetError("Couldn't find any hardware audio formats");
|
||||
}
|
||||
device->spec.format = test_format;
|
||||
|
||||
// Set the audio format
|
||||
value = format;
|
||||
if ((ioctl(device->hidden->audio_fd, SNDCTL_DSP_SETFMT, &value) < 0) ||
|
||||
(value != format)) {
|
||||
perror("SNDCTL_DSP_SETFMT");
|
||||
return SDL_SetError("Couldn't set audio format");
|
||||
}
|
||||
|
||||
// Set the number of channels of output
|
||||
value = device->spec.channels;
|
||||
if (ioctl(device->hidden->audio_fd, SNDCTL_DSP_CHANNELS, &value) < 0) {
|
||||
perror("SNDCTL_DSP_CHANNELS");
|
||||
return SDL_SetError("Cannot set the number of channels");
|
||||
}
|
||||
device->spec.channels = value;
|
||||
|
||||
// Set the DSP frequency
|
||||
value = device->spec.freq;
|
||||
if (ioctl(device->hidden->audio_fd, SNDCTL_DSP_SPEED, &value) < 0) {
|
||||
perror("SNDCTL_DSP_SPEED");
|
||||
return SDL_SetError("Couldn't set audio frequency");
|
||||
}
|
||||
device->spec.freq = value;
|
||||
|
||||
// Calculate the final parameters for this audio specification
|
||||
SDL_UpdatedAudioDeviceFormat(device);
|
||||
|
||||
/* Determine the power of two of the fragment size
|
||||
Since apps don't control this in SDL3, and this driver only accepts 8, 16
|
||||
bit formats and 1, 2, 4, 8 channels, this should always be a power of 2 already. */
|
||||
SDL_assert(SDL_powerof2(device->buffer_size) == device->buffer_size);
|
||||
|
||||
int frag_spec = 0;
|
||||
while ((0x01U << frag_spec) < device->buffer_size) {
|
||||
frag_spec++;
|
||||
}
|
||||
frag_spec |= 0x00020000; // two fragments, for low latency
|
||||
|
||||
// Set the audio buffering parameters
|
||||
#ifdef DEBUG_AUDIO
|
||||
fprintf(stderr, "Requesting %d fragments of size %d\n",
|
||||
(frag_spec >> 16), 1 << (frag_spec & 0xFFFF));
|
||||
#endif
|
||||
if (ioctl(device->hidden->audio_fd, SNDCTL_DSP_SETFRAGMENT, &frag_spec) < 0) {
|
||||
perror("SNDCTL_DSP_SETFRAGMENT");
|
||||
}
|
||||
#ifdef DEBUG_AUDIO
|
||||
{
|
||||
audio_buf_info info;
|
||||
ioctl(device->hidden->audio_fd, SNDCTL_DSP_GETOSPACE, &info);
|
||||
fprintf(stderr, "fragments = %d\n", info.fragments);
|
||||
fprintf(stderr, "fragstotal = %d\n", info.fragstotal);
|
||||
fprintf(stderr, "fragsize = %d\n", info.fragsize);
|
||||
fprintf(stderr, "bytes = %d\n", info.bytes);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Allocate mixing buffer
|
||||
if (!device->iscapture) {
|
||||
device->hidden->mixbuf = (Uint8 *)SDL_malloc(device->buffer_size);
|
||||
if (device->hidden->mixbuf == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
SDL_memset(device->hidden->mixbuf, device->silence_value, device->buffer_size);
|
||||
}
|
||||
|
||||
return 0; // We're ready to rock and roll. :-)
|
||||
}
|
||||
|
||||
static int DSP_WaitDevice(SDL_AudioDevice *device)
|
||||
{
|
||||
const unsigned long ioctlreq = device->iscapture ? SNDCTL_DSP_GETISPACE : SNDCTL_DSP_GETOSPACE;
|
||||
struct SDL_PrivateAudioData *h = device->hidden;
|
||||
|
||||
while (!SDL_AtomicGet(&device->shutdown)) {
|
||||
audio_buf_info info;
|
||||
const int rc = ioctl(h->audio_fd, ioctlreq, &info);
|
||||
if (rc < 0) {
|
||||
if (errno == EAGAIN) {
|
||||
continue;
|
||||
}
|
||||
// Hmm, not much we can do - abort
|
||||
fprintf(stderr, "dsp WaitDevice ioctl failed (unrecoverable): %s\n", strerror(errno));
|
||||
return -1;
|
||||
} else if (info.bytes < device->buffer_size) {
|
||||
SDL_Delay(10);
|
||||
} else {
|
||||
break; // ready to go!
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int DSP_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
|
||||
{
|
||||
struct SDL_PrivateAudioData *h = device->hidden;
|
||||
if (write(h->audio_fd, buffer, buflen) == -1) {
|
||||
perror("Audio write");
|
||||
return -1;
|
||||
}
|
||||
#ifdef DEBUG_AUDIO
|
||||
fprintf(stderr, "Wrote %d bytes of audio data\n", h->mixlen);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static Uint8 *DSP_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
|
||||
{
|
||||
return device->hidden->mixbuf;
|
||||
}
|
||||
|
||||
static int DSP_CaptureFromDevice(SDL_AudioDevice *device, void *buffer, int buflen)
|
||||
{
|
||||
return (int)read(device->hidden->audio_fd, buffer, buflen);
|
||||
}
|
||||
|
||||
static void DSP_FlushCapture(SDL_AudioDevice *device)
|
||||
{
|
||||
struct SDL_PrivateAudioData *h = device->hidden;
|
||||
audio_buf_info info;
|
||||
if (ioctl(h->audio_fd, SNDCTL_DSP_GETISPACE, &info) == 0) {
|
||||
while (info.bytes > 0) {
|
||||
char buf[512];
|
||||
const size_t len = SDL_min(sizeof(buf), info.bytes);
|
||||
const ssize_t br = read(h->audio_fd, buf, len);
|
||||
if (br <= 0) {
|
||||
break;
|
||||
}
|
||||
info.bytes -= br;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static SDL_bool InitTimeDevicesExist = SDL_FALSE;
|
||||
static SDL_bool look_for_devices_test(int fd)
|
||||
{
|
||||
InitTimeDevicesExist = SDL_TRUE; // note that _something_ exists.
|
||||
// Don't add to the device list, we're just seeing if any devices exist.
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
static SDL_bool DSP_Init(SDL_AudioDriverImpl *impl)
|
||||
{
|
||||
InitTimeDevicesExist = SDL_FALSE;
|
||||
SDL_EnumUnixAudioDevices(SDL_FALSE, look_for_devices_test);
|
||||
if (!InitTimeDevicesExist) {
|
||||
SDL_SetError("dsp: No such audio device");
|
||||
return SDL_FALSE; // maybe try a different backend.
|
||||
}
|
||||
|
||||
impl->DetectDevices = DSP_DetectDevices;
|
||||
impl->OpenDevice = DSP_OpenDevice;
|
||||
impl->WaitDevice = DSP_WaitDevice;
|
||||
impl->PlayDevice = DSP_PlayDevice;
|
||||
impl->GetDeviceBuf = DSP_GetDeviceBuf;
|
||||
impl->CloseDevice = DSP_CloseDevice;
|
||||
impl->WaitCaptureDevice = DSP_WaitDevice;
|
||||
impl->CaptureFromDevice = DSP_CaptureFromDevice;
|
||||
impl->FlushCapture = DSP_FlushCapture;
|
||||
|
||||
impl->HasCaptureSupport = SDL_TRUE;
|
||||
|
||||
return SDL_TRUE;
|
||||
}
|
||||
|
||||
AudioBootStrap DSP_bootstrap = {
|
||||
"dsp", "Open Sound System (/dev/dsp)", DSP_Init, SDL_FALSE
|
||||
};
|
||||
|
||||
#endif // SDL_AUDIO_DRIVER_OSS
|
||||
37
vendor/sdl-3.0.0/src/audio/dsp/SDL_dspaudio.h
vendored
Normal file
37
vendor/sdl-3.0.0/src/audio/dsp/SDL_dspaudio.h
vendored
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifndef SDL_dspaudio_h_
|
||||
#define SDL_dspaudio_h_
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
|
||||
struct SDL_PrivateAudioData
|
||||
{
|
||||
// The file descriptor for the audio device
|
||||
int audio_fd;
|
||||
|
||||
// Raw mixing buffer
|
||||
Uint8 *mixbuf;
|
||||
};
|
||||
|
||||
#endif // SDL_dspaudio_h_
|
||||
97
vendor/sdl-3.0.0/src/audio/dummy/SDL_dummyaudio.c
vendored
Normal file
97
vendor/sdl-3.0.0/src/audio/dummy/SDL_dummyaudio.c
vendored
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
// Output audio to nowhere...
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
#include "SDL_dummyaudio.h"
|
||||
|
||||
// !!! FIXME: this should be an SDL hint, not an environment variable.
|
||||
#define DUMMYENVR_IODELAY "SDL_DUMMYAUDIODELAY"
|
||||
|
||||
static int DUMMYAUDIO_WaitDevice(SDL_AudioDevice *device)
|
||||
{
|
||||
SDL_Delay(device->hidden->io_delay);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int DUMMYAUDIO_OpenDevice(SDL_AudioDevice *device)
|
||||
{
|
||||
const char *envr = SDL_getenv(DUMMYENVR_IODELAY);
|
||||
|
||||
device->hidden = (struct SDL_PrivateAudioData *) SDL_calloc(1, sizeof(*device->hidden));
|
||||
if (!device->hidden) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
|
||||
if (!device->iscapture) {
|
||||
device->hidden->mixbuf = (Uint8 *) SDL_malloc(device->buffer_size);
|
||||
if (!device->hidden->mixbuf) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
}
|
||||
|
||||
device->hidden->io_delay = (Uint32) (envr ? SDL_atoi(envr) : ((device->sample_frames * 1000) / device->spec.freq));
|
||||
|
||||
return 0; // we're good; don't change reported device format.
|
||||
}
|
||||
|
||||
static void DUMMYAUDIO_CloseDevice(SDL_AudioDevice *device)
|
||||
{
|
||||
if (device->hidden) {
|
||||
SDL_free(device->hidden->mixbuf);
|
||||
SDL_free(device->hidden);
|
||||
device->hidden = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static Uint8 *DUMMYAUDIO_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
|
||||
{
|
||||
return device->hidden->mixbuf;
|
||||
}
|
||||
|
||||
static int DUMMYAUDIO_CaptureFromDevice(SDL_AudioDevice *device, void *buffer, int buflen)
|
||||
{
|
||||
// always return a full buffer of silence.
|
||||
SDL_memset(buffer, device->silence_value, buflen);
|
||||
return buflen;
|
||||
}
|
||||
|
||||
static SDL_bool DUMMYAUDIO_Init(SDL_AudioDriverImpl *impl)
|
||||
{
|
||||
impl->OpenDevice = DUMMYAUDIO_OpenDevice;
|
||||
impl->CloseDevice = DUMMYAUDIO_CloseDevice;
|
||||
impl->WaitDevice = DUMMYAUDIO_WaitDevice;
|
||||
impl->GetDeviceBuf = DUMMYAUDIO_GetDeviceBuf;
|
||||
impl->WaitCaptureDevice = DUMMYAUDIO_WaitDevice;
|
||||
impl->CaptureFromDevice = DUMMYAUDIO_CaptureFromDevice;
|
||||
|
||||
impl->OnlyHasDefaultOutputDevice = SDL_TRUE;
|
||||
impl->OnlyHasDefaultCaptureDevice = SDL_TRUE;
|
||||
impl->HasCaptureSupport = SDL_TRUE;
|
||||
|
||||
return SDL_TRUE;
|
||||
}
|
||||
|
||||
AudioBootStrap DUMMYAUDIO_bootstrap = {
|
||||
"dummy", "SDL dummy audio driver", DUMMYAUDIO_Init, SDL_TRUE
|
||||
};
|
||||
34
vendor/sdl-3.0.0/src/audio/dummy/SDL_dummyaudio.h
vendored
Normal file
34
vendor/sdl-3.0.0/src/audio/dummy/SDL_dummyaudio.h
vendored
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifndef SDL_dummyaudio_h_
|
||||
#define SDL_dummyaudio_h_
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
|
||||
struct SDL_PrivateAudioData
|
||||
{
|
||||
Uint8 *mixbuf; // The file descriptor for the audio device
|
||||
Uint32 io_delay; // miliseconds to sleep in WaitDevice.
|
||||
};
|
||||
|
||||
#endif // SDL_dummyaudio_h_
|
||||
351
vendor/sdl-3.0.0/src/audio/emscripten/SDL_emscriptenaudio.c
vendored
Normal file
351
vendor/sdl-3.0.0/src/audio/emscripten/SDL_emscriptenaudio.c
vendored
Normal file
|
|
@ -0,0 +1,351 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifdef SDL_AUDIO_DRIVER_EMSCRIPTEN
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
#include "SDL_emscriptenaudio.h"
|
||||
|
||||
#include <emscripten/emscripten.h>
|
||||
|
||||
// just turn off clang-format for this whole file, this INDENT_OFF stuff on
|
||||
// each EM_ASM section is ugly.
|
||||
/* *INDENT-OFF* */ /* clang-format off */
|
||||
|
||||
static Uint8 *EMSCRIPTENAUDIO_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
|
||||
{
|
||||
return device->hidden->mixbuf;
|
||||
}
|
||||
|
||||
static int EMSCRIPTENAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buffer_size)
|
||||
{
|
||||
const int framelen = SDL_AUDIO_FRAMESIZE(device->spec);
|
||||
MAIN_THREAD_EM_ASM({
|
||||
var SDL3 = Module['SDL3'];
|
||||
var numChannels = SDL3.audio.currentOutputBuffer['numberOfChannels'];
|
||||
for (var c = 0; c < numChannels; ++c) {
|
||||
var channelData = SDL3.audio.currentOutputBuffer['getChannelData'](c);
|
||||
if (channelData.length != $1) {
|
||||
throw 'Web Audio output buffer length mismatch! Destination size: ' + channelData.length + ' samples vs expected ' + $1 + ' samples!';
|
||||
}
|
||||
|
||||
for (var j = 0; j < $1; ++j) {
|
||||
channelData[j] = HEAPF32[$0 + ((j*numChannels + c) << 2) >> 2]; // !!! FIXME: why are these shifts here?
|
||||
}
|
||||
}
|
||||
}, buffer, buffer_size / framelen);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void EMSCRIPTENAUDIO_FlushCapture(SDL_AudioDevice *device)
|
||||
{
|
||||
// Do nothing, the new data will just be dropped.
|
||||
}
|
||||
|
||||
static int EMSCRIPTENAUDIO_CaptureFromDevice(SDL_AudioDevice *device, void *buffer, int buflen)
|
||||
{
|
||||
MAIN_THREAD_EM_ASM({
|
||||
var SDL3 = Module['SDL3'];
|
||||
var numChannels = SDL3.capture.currentCaptureBuffer.numberOfChannels;
|
||||
for (var c = 0; c < numChannels; ++c) {
|
||||
var channelData = SDL3.capture.currentCaptureBuffer.getChannelData(c);
|
||||
if (channelData.length != $1) {
|
||||
throw 'Web Audio capture buffer length mismatch! Destination size: ' + channelData.length + ' samples vs expected ' + $1 + ' samples!';
|
||||
}
|
||||
|
||||
if (numChannels == 1) { // fastpath this a little for the common (mono) case.
|
||||
for (var j = 0; j < $1; ++j) {
|
||||
setValue($0 + (j * 4), channelData[j], 'float');
|
||||
}
|
||||
} else {
|
||||
for (var j = 0; j < $1; ++j) {
|
||||
setValue($0 + (((j * numChannels) + c) * 4), channelData[j], 'float');
|
||||
}
|
||||
}
|
||||
}
|
||||
}, buffer, (buflen / sizeof(float)) / device->spec.channels);
|
||||
|
||||
return buflen;
|
||||
}
|
||||
|
||||
static void EMSCRIPTENAUDIO_CloseDevice(SDL_AudioDevice *device)
|
||||
{
|
||||
if (!device->hidden) {
|
||||
return;
|
||||
}
|
||||
|
||||
MAIN_THREAD_EM_ASM({
|
||||
var SDL3 = Module['SDL3'];
|
||||
if ($0) {
|
||||
if (SDL3.capture.silenceTimer !== undefined) {
|
||||
clearInterval(SDL3.capture.silenceTimer);
|
||||
}
|
||||
if (SDL3.capture.stream !== undefined) {
|
||||
var tracks = SDL3.capture.stream.getAudioTracks();
|
||||
for (var i = 0; i < tracks.length; i++) {
|
||||
SDL3.capture.stream.removeTrack(tracks[i]);
|
||||
}
|
||||
}
|
||||
if (SDL3.capture.scriptProcessorNode !== undefined) {
|
||||
SDL3.capture.scriptProcessorNode.onaudioprocess = function(audioProcessingEvent) {};
|
||||
SDL3.capture.scriptProcessorNode.disconnect();
|
||||
}
|
||||
if (SDL3.capture.mediaStreamNode !== undefined) {
|
||||
SDL3.capture.mediaStreamNode.disconnect();
|
||||
}
|
||||
SDL3.capture = undefined;
|
||||
} else {
|
||||
if (SDL3.audio.scriptProcessorNode != undefined) {
|
||||
SDL3.audio.scriptProcessorNode.disconnect();
|
||||
}
|
||||
if (SDL3.audio.silenceTimer !== undefined) {
|
||||
clearInterval(SDL3.audio.silenceTimer);
|
||||
}
|
||||
SDL3.audio = undefined;
|
||||
}
|
||||
if ((SDL3.audioContext !== undefined) && (SDL3.audio === undefined) && (SDL3.capture === undefined)) {
|
||||
SDL3.audioContext.close();
|
||||
SDL3.audioContext = undefined;
|
||||
}
|
||||
}, device->iscapture);
|
||||
|
||||
SDL_free(device->hidden->mixbuf);
|
||||
SDL_free(device->hidden);
|
||||
device->hidden = NULL;
|
||||
|
||||
SDL_AudioThreadFinalize(device);
|
||||
}
|
||||
|
||||
EM_JS_DEPS(sdlaudio, "$autoResumeAudioContext,$dynCall");
|
||||
|
||||
static int EMSCRIPTENAUDIO_OpenDevice(SDL_AudioDevice *device)
|
||||
{
|
||||
// based on parts of library_sdl.js
|
||||
|
||||
// create context
|
||||
const int result = MAIN_THREAD_EM_ASM_INT({
|
||||
if (typeof(Module['SDL3']) === 'undefined') {
|
||||
Module['SDL3'] = {};
|
||||
}
|
||||
var SDL3 = Module['SDL3'];
|
||||
if (!$0) {
|
||||
SDL3.audio = {};
|
||||
} else {
|
||||
SDL3.capture = {};
|
||||
}
|
||||
|
||||
if (!SDL3.audioContext) {
|
||||
if (typeof(AudioContext) !== 'undefined') {
|
||||
SDL3.audioContext = new AudioContext();
|
||||
} else if (typeof(webkitAudioContext) !== 'undefined') {
|
||||
SDL3.audioContext = new webkitAudioContext();
|
||||
}
|
||||
if (SDL3.audioContext) {
|
||||
if ((typeof navigator.userActivation) === 'undefined') { // Firefox doesn't have this (as of August 2023), use autoResumeAudioContext instead.
|
||||
autoResumeAudioContext(SDL3.audioContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
return SDL3.audioContext === undefined ? -1 : 0;
|
||||
}, device->iscapture);
|
||||
|
||||
if (result < 0) {
|
||||
return SDL_SetError("Web Audio API is not available!");
|
||||
}
|
||||
|
||||
device->spec.format = SDL_AUDIO_F32; // web audio only supports floats
|
||||
|
||||
// Initialize all variables that we clean on shutdown
|
||||
device->hidden = (struct SDL_PrivateAudioData *)SDL_calloc(1, sizeof(*device->hidden));
|
||||
if (device->hidden == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
|
||||
// limit to native freq
|
||||
device->spec.freq = EM_ASM_INT({ return Module['SDL3'].audioContext.sampleRate; });
|
||||
|
||||
SDL_UpdatedAudioDeviceFormat(device);
|
||||
|
||||
if (!device->iscapture) {
|
||||
device->hidden->mixbuf = (Uint8 *)SDL_malloc(device->buffer_size);
|
||||
if (device->hidden->mixbuf == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
SDL_memset(device->hidden->mixbuf, device->silence_value, device->buffer_size);
|
||||
}
|
||||
|
||||
if (device->iscapture) {
|
||||
/* The idea is to take the capture media stream, hook it up to an
|
||||
audio graph where we can pass it through a ScriptProcessorNode
|
||||
to access the raw PCM samples and push them to the SDL app's
|
||||
callback. From there, we "process" the audio data into silence
|
||||
and forget about it.
|
||||
|
||||
This should, strictly speaking, use MediaRecorder for capture, but
|
||||
this API is cleaner to use and better supported, and fires a
|
||||
callback whenever there's enough data to fire down into the app.
|
||||
The downside is that we are spending CPU time silencing a buffer
|
||||
that the audiocontext uselessly mixes into any output. On the
|
||||
upside, both of those things are not only run in native code in
|
||||
the browser, they're probably SIMD code, too. MediaRecorder
|
||||
feels like it's a pretty inefficient tapdance in similar ways,
|
||||
to be honest. */
|
||||
|
||||
MAIN_THREAD_EM_ASM({
|
||||
var SDL3 = Module['SDL3'];
|
||||
var have_microphone = function(stream) {
|
||||
//console.log('SDL audio capture: we have a microphone! Replacing silence callback.');
|
||||
if (SDL3.capture.silenceTimer !== undefined) {
|
||||
clearInterval(SDL3.capture.silenceTimer);
|
||||
SDL3.capture.silenceTimer = undefined;
|
||||
SDL3.capture.silenceBuffer = undefined
|
||||
}
|
||||
SDL3.capture.mediaStreamNode = SDL3.audioContext.createMediaStreamSource(stream);
|
||||
SDL3.capture.scriptProcessorNode = SDL3.audioContext.createScriptProcessor($1, $0, 1);
|
||||
SDL3.capture.scriptProcessorNode.onaudioprocess = function(audioProcessingEvent) {
|
||||
if ((SDL3 === undefined) || (SDL3.capture === undefined)) { return; }
|
||||
audioProcessingEvent.outputBuffer.getChannelData(0).fill(0.0);
|
||||
SDL3.capture.currentCaptureBuffer = audioProcessingEvent.inputBuffer;
|
||||
dynCall('vi', $2, [$3]);
|
||||
};
|
||||
SDL3.capture.mediaStreamNode.connect(SDL3.capture.scriptProcessorNode);
|
||||
SDL3.capture.scriptProcessorNode.connect(SDL3.audioContext.destination);
|
||||
SDL3.capture.stream = stream;
|
||||
};
|
||||
|
||||
var no_microphone = function(error) {
|
||||
//console.log('SDL audio capture: we DO NOT have a microphone! (' + error.name + ')...leaving silence callback running.');
|
||||
};
|
||||
|
||||
// we write silence to the audio callback until the microphone is available (user approves use, etc).
|
||||
SDL3.capture.silenceBuffer = SDL3.audioContext.createBuffer($0, $1, SDL3.audioContext.sampleRate);
|
||||
SDL3.capture.silenceBuffer.getChannelData(0).fill(0.0);
|
||||
var silence_callback = function() {
|
||||
SDL3.capture.currentCaptureBuffer = SDL3.capture.silenceBuffer;
|
||||
dynCall('vi', $2, [$3]);
|
||||
};
|
||||
|
||||
SDL3.capture.silenceTimer = setInterval(silence_callback, ($1 / SDL3.audioContext.sampleRate) * 1000);
|
||||
|
||||
if ((navigator.mediaDevices !== undefined) && (navigator.mediaDevices.getUserMedia !== undefined)) {
|
||||
navigator.mediaDevices.getUserMedia({ audio: true, video: false }).then(have_microphone).catch(no_microphone);
|
||||
} else if (navigator.webkitGetUserMedia !== undefined) {
|
||||
navigator.webkitGetUserMedia({ audio: true, video: false }, have_microphone, no_microphone);
|
||||
}
|
||||
}, device->spec.channels, device->sample_frames, SDL_CaptureAudioThreadIterate, device);
|
||||
} else {
|
||||
// setup a ScriptProcessorNode
|
||||
MAIN_THREAD_EM_ASM({
|
||||
var SDL3 = Module['SDL3'];
|
||||
SDL3.audio.scriptProcessorNode = SDL3.audioContext['createScriptProcessor']($1, 0, $0);
|
||||
SDL3.audio.scriptProcessorNode['onaudioprocess'] = function (e) {
|
||||
if ((SDL3 === undefined) || (SDL3.audio === undefined)) { return; }
|
||||
// if we're actually running the node, we don't need the fake callback anymore, so kill it.
|
||||
if (SDL3.audio.silenceTimer !== undefined) {
|
||||
clearInterval(SDL3.audio.silenceTimer);
|
||||
SDL3.audio.silenceTimer = undefined;
|
||||
SDL3.audio.silenceBuffer = undefined;
|
||||
}
|
||||
SDL3.audio.currentOutputBuffer = e['outputBuffer'];
|
||||
dynCall('vi', $2, [$3]);
|
||||
};
|
||||
|
||||
SDL3.audio.scriptProcessorNode['connect'](SDL3.audioContext['destination']);
|
||||
|
||||
if (SDL3.audioContext.state === 'suspended') { // uhoh, autoplay is blocked.
|
||||
SDL3.audio.silenceBuffer = SDL3.audioContext.createBuffer($0, $1, SDL3.audioContext.sampleRate);
|
||||
SDL3.audio.silenceBuffer.getChannelData(0).fill(0.0);
|
||||
var silence_callback = function() {
|
||||
if ((typeof navigator.userActivation) !== 'undefined') { // Almost everything modern except Firefox (as of August 2023)
|
||||
if (navigator.userActivation.hasBeenActive) {
|
||||
SDL3.audioContext.resume();
|
||||
}
|
||||
}
|
||||
|
||||
// the buffer that gets filled here just gets ignored, so the app can make progress
|
||||
// and/or avoid flooding audio queues until we can actually play audio.
|
||||
SDL3.audio.currentOutputBuffer = SDL3.audio.silenceBuffer;
|
||||
dynCall('vi', $2, [$3]);
|
||||
SDL3.audio.currentOutputBuffer = undefined;
|
||||
};
|
||||
|
||||
SDL3.audio.silenceTimer = setInterval(silence_callback, ($1 / SDL3.audioContext.sampleRate) * 1000);
|
||||
}
|
||||
}, device->spec.channels, device->sample_frames, SDL_OutputAudioThreadIterate, device);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SDL_bool EMSCRIPTENAUDIO_Init(SDL_AudioDriverImpl *impl)
|
||||
{
|
||||
SDL_bool available, capture_available;
|
||||
|
||||
impl->OpenDevice = EMSCRIPTENAUDIO_OpenDevice;
|
||||
impl->CloseDevice = EMSCRIPTENAUDIO_CloseDevice;
|
||||
impl->GetDeviceBuf = EMSCRIPTENAUDIO_GetDeviceBuf;
|
||||
impl->PlayDevice = EMSCRIPTENAUDIO_PlayDevice;
|
||||
impl->FlushCapture = EMSCRIPTENAUDIO_FlushCapture;
|
||||
impl->CaptureFromDevice = EMSCRIPTENAUDIO_CaptureFromDevice;
|
||||
|
||||
impl->OnlyHasDefaultOutputDevice = SDL_TRUE;
|
||||
|
||||
// technically, this is just runs in idle time in the main thread, but it's close enough to a "thread" for our purposes.
|
||||
impl->ProvidesOwnCallbackThread = SDL_TRUE;
|
||||
|
||||
// check availability
|
||||
available = MAIN_THREAD_EM_ASM_INT({
|
||||
if (typeof(AudioContext) !== 'undefined') {
|
||||
return true;
|
||||
} else if (typeof(webkitAudioContext) !== 'undefined') {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}) ? SDL_TRUE : SDL_FALSE;
|
||||
|
||||
if (!available) {
|
||||
SDL_SetError("No audio context available");
|
||||
}
|
||||
|
||||
capture_available = available && MAIN_THREAD_EM_ASM_INT({
|
||||
if ((typeof(navigator.mediaDevices) !== 'undefined') && (typeof(navigator.mediaDevices.getUserMedia) !== 'undefined')) {
|
||||
return true;
|
||||
} else if (typeof(navigator.webkitGetUserMedia) !== 'undefined') {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}) ? SDL_TRUE : SDL_FALSE;
|
||||
|
||||
impl->HasCaptureSupport = capture_available ? SDL_TRUE : SDL_FALSE;
|
||||
impl->OnlyHasDefaultCaptureDevice = capture_available ? SDL_TRUE : SDL_FALSE;
|
||||
|
||||
return available;
|
||||
}
|
||||
|
||||
AudioBootStrap EMSCRIPTENAUDIO_bootstrap = {
|
||||
"emscripten", "SDL emscripten audio driver", EMSCRIPTENAUDIO_Init, SDL_FALSE
|
||||
};
|
||||
|
||||
/* *INDENT-ON* */ /* clang-format on */
|
||||
|
||||
#endif // SDL_AUDIO_DRIVER_EMSCRIPTEN
|
||||
33
vendor/sdl-3.0.0/src/audio/emscripten/SDL_emscriptenaudio.h
vendored
Normal file
33
vendor/sdl-3.0.0/src/audio/emscripten/SDL_emscriptenaudio.h
vendored
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifndef SDL_emscriptenaudio_h_
|
||||
#define SDL_emscriptenaudio_h_
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
|
||||
struct SDL_PrivateAudioData
|
||||
{
|
||||
Uint8 *mixbuf;
|
||||
};
|
||||
|
||||
#endif // SDL_emscriptenaudio_h_
|
||||
222
vendor/sdl-3.0.0/src/audio/haiku/SDL_haikuaudio.cc
vendored
Normal file
222
vendor/sdl-3.0.0/src/audio/haiku/SDL_haikuaudio.cc
vendored
Normal file
|
|
@ -0,0 +1,222 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifdef SDL_AUDIO_DRIVER_HAIKU
|
||||
|
||||
// Allow access to the audio stream on Haiku
|
||||
|
||||
#include <SoundPlayer.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include "../../core/haiku/SDL_BeApp.h"
|
||||
|
||||
extern "C"
|
||||
{
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
#include "SDL_haikuaudio.h"
|
||||
|
||||
}
|
||||
|
||||
static Uint8 *HAIKUAUDIO_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
|
||||
{
|
||||
SDL_assert(device->hidden->current_buffer != NULL);
|
||||
SDL_assert(device->hidden->current_buffer_len > 0);
|
||||
*buffer_size = device->hidden->current_buffer_len;
|
||||
return device->hidden->current_buffer;
|
||||
}
|
||||
|
||||
static int HAIKUAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buffer_size)
|
||||
{
|
||||
// We already wrote our output right into the BSoundPlayer's callback's stream. Just clean up our stuff.
|
||||
SDL_assert(device->hidden->current_buffer != NULL);
|
||||
SDL_assert(device->hidden->current_buffer_len > 0);
|
||||
device->hidden->current_buffer = NULL;
|
||||
device->hidden->current_buffer_len = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// The Haiku callback for handling the audio buffer
|
||||
static void FillSound(void *data, void *stream, size_t len, const media_raw_audio_format & format)
|
||||
{
|
||||
SDL_AudioDevice *device = (SDL_AudioDevice *)data;
|
||||
SDL_assert(device->hidden->current_buffer == NULL);
|
||||
SDL_assert(device->hidden->current_buffer_len == 0);
|
||||
device->hidden->current_buffer = (Uint8 *) stream;
|
||||
device->hidden->current_buffer_len = (int) len;
|
||||
SDL_OutputAudioThreadIterate(device);
|
||||
}
|
||||
|
||||
static void HAIKUAUDIO_CloseDevice(SDL_AudioDevice *device)
|
||||
{
|
||||
if (device->hidden) {
|
||||
if (device->hidden->audio_obj) {
|
||||
device->hidden->audio_obj->Stop();
|
||||
delete device->hidden->audio_obj;
|
||||
}
|
||||
delete device->hidden;
|
||||
device->hidden = NULL;
|
||||
SDL_AudioThreadFinalize(device);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static const int sig_list[] = {
|
||||
SIGHUP, SIGINT, SIGQUIT, SIGPIPE, SIGALRM, SIGTERM, SIGWINCH, 0
|
||||
};
|
||||
|
||||
static inline void MaskSignals(sigset_t * omask)
|
||||
{
|
||||
sigset_t mask;
|
||||
int i;
|
||||
|
||||
sigemptyset(&mask);
|
||||
for (i = 0; sig_list[i]; ++i) {
|
||||
sigaddset(&mask, sig_list[i]);
|
||||
}
|
||||
sigprocmask(SIG_BLOCK, &mask, omask);
|
||||
}
|
||||
|
||||
static inline void UnmaskSignals(sigset_t * omask)
|
||||
{
|
||||
sigprocmask(SIG_SETMASK, omask, NULL);
|
||||
}
|
||||
|
||||
|
||||
static int HAIKUAUDIO_OpenDevice(SDL_AudioDevice *device)
|
||||
{
|
||||
// Initialize all variables that we clean on shutdown
|
||||
device->hidden = new SDL_PrivateAudioData;
|
||||
if (device->hidden == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
SDL_zerop(device->hidden);
|
||||
|
||||
// Parse the audio format and fill the Be raw audio format
|
||||
media_raw_audio_format format;
|
||||
SDL_zero(format);
|
||||
format.byte_order = B_MEDIA_LITTLE_ENDIAN;
|
||||
format.frame_rate = (float) device->spec.freq;
|
||||
format.channel_count = device->spec.channels; // !!! FIXME: support > 2?
|
||||
|
||||
SDL_AudioFormat test_format;
|
||||
const SDL_AudioFormat *closefmts = SDL_ClosestAudioFormats(device->spec.format);
|
||||
while ((test_format = *(closefmts++)) != 0) {
|
||||
switch (test_format) {
|
||||
case SDL_AUDIO_S8:
|
||||
format.format = media_raw_audio_format::B_AUDIO_CHAR;
|
||||
break;
|
||||
|
||||
case SDL_AUDIO_U8:
|
||||
format.format = media_raw_audio_format::B_AUDIO_UCHAR;
|
||||
break;
|
||||
|
||||
case SDL_AUDIO_S16LE:
|
||||
format.format = media_raw_audio_format::B_AUDIO_SHORT;
|
||||
break;
|
||||
|
||||
case SDL_AUDIO_S16BE:
|
||||
format.format = media_raw_audio_format::B_AUDIO_SHORT;
|
||||
format.byte_order = B_MEDIA_BIG_ENDIAN;
|
||||
break;
|
||||
|
||||
case SDL_AUDIO_S32LE:
|
||||
format.format = media_raw_audio_format::B_AUDIO_INT;
|
||||
break;
|
||||
|
||||
case SDL_AUDIO_S32BE:
|
||||
format.format = media_raw_audio_format::B_AUDIO_INT;
|
||||
format.byte_order = B_MEDIA_BIG_ENDIAN;
|
||||
break;
|
||||
|
||||
case SDL_AUDIO_F32LE:
|
||||
format.format = media_raw_audio_format::B_AUDIO_FLOAT;
|
||||
break;
|
||||
|
||||
case SDL_AUDIO_F32BE:
|
||||
format.format = media_raw_audio_format::B_AUDIO_FLOAT;
|
||||
format.byte_order = B_MEDIA_BIG_ENDIAN;
|
||||
break;
|
||||
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (!test_format) { // shouldn't happen, but just in case...
|
||||
return SDL_SetError("HAIKU: Unsupported audio format");
|
||||
}
|
||||
device->spec.format = test_format;
|
||||
|
||||
// Calculate the final parameters for this audio specification
|
||||
SDL_UpdatedAudioDeviceFormat(device);
|
||||
|
||||
format.buffer_size = device->buffer_size;
|
||||
|
||||
// Subscribe to the audio stream (creates a new thread)
|
||||
sigset_t omask;
|
||||
MaskSignals(&omask);
|
||||
device->hidden->audio_obj = new BSoundPlayer(&format, "SDL Audio",
|
||||
FillSound, NULL, device);
|
||||
UnmaskSignals(&omask);
|
||||
|
||||
if (device->hidden->audio_obj->Start() == B_NO_ERROR) {
|
||||
device->hidden->audio_obj->SetHasData(true);
|
||||
} else {
|
||||
return SDL_SetError("Unable to start Haiku audio");
|
||||
}
|
||||
|
||||
return 0; // We're running!
|
||||
}
|
||||
|
||||
static void HAIKUAUDIO_Deinitialize(void)
|
||||
{
|
||||
SDL_QuitBeApp();
|
||||
}
|
||||
|
||||
static SDL_bool HAIKUAUDIO_Init(SDL_AudioDriverImpl *impl)
|
||||
{
|
||||
if (SDL_InitBeApp() < 0) {
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
// Set the function pointers
|
||||
impl->OpenDevice = HAIKUAUDIO_OpenDevice;
|
||||
impl->GetDeviceBuf = HAIKUAUDIO_GetDeviceBuf;
|
||||
impl->PlayDevice = HAIKUAUDIO_PlayDevice;
|
||||
impl->CloseDevice = HAIKUAUDIO_CloseDevice;
|
||||
impl->Deinitialize = HAIKUAUDIO_Deinitialize;
|
||||
impl->ProvidesOwnCallbackThread = SDL_TRUE;
|
||||
impl->OnlyHasDefaultOutputDevice = SDL_TRUE;
|
||||
|
||||
return SDL_TRUE;
|
||||
}
|
||||
|
||||
|
||||
extern "C" { extern AudioBootStrap HAIKUAUDIO_bootstrap; }
|
||||
|
||||
AudioBootStrap HAIKUAUDIO_bootstrap = {
|
||||
"haiku", "Haiku BSoundPlayer", HAIKUAUDIO_Init, SDL_FALSE
|
||||
};
|
||||
|
||||
#endif // SDL_AUDIO_DRIVER_HAIKU
|
||||
35
vendor/sdl-3.0.0/src/audio/haiku/SDL_haikuaudio.h
vendored
Normal file
35
vendor/sdl-3.0.0/src/audio/haiku/SDL_haikuaudio.h
vendored
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifndef SDL_haikuaudio_h_
|
||||
#define SDL_haikuaudio_h_
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
|
||||
struct SDL_PrivateAudioData
|
||||
{
|
||||
BSoundPlayer *audio_obj;
|
||||
Uint8 *current_buffer;
|
||||
int current_buffer_len;
|
||||
};
|
||||
|
||||
#endif // SDL_haikuaudio_h_
|
||||
443
vendor/sdl-3.0.0/src/audio/jack/SDL_jackaudio.c
vendored
Normal file
443
vendor/sdl-3.0.0/src/audio/jack/SDL_jackaudio.c
vendored
Normal file
|
|
@ -0,0 +1,443 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifdef SDL_AUDIO_DRIVER_JACK
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
#include "SDL_jackaudio.h"
|
||||
#include "../../thread/SDL_systhread.h"
|
||||
|
||||
static jack_client_t *(*JACK_jack_client_open)(const char *, jack_options_t, jack_status_t *, ...);
|
||||
static int (*JACK_jack_client_close)(jack_client_t *);
|
||||
static void (*JACK_jack_on_shutdown)(jack_client_t *, JackShutdownCallback, void *);
|
||||
static int (*JACK_jack_activate)(jack_client_t *);
|
||||
static int (*JACK_jack_deactivate)(jack_client_t *);
|
||||
static void *(*JACK_jack_port_get_buffer)(jack_port_t *, jack_nframes_t);
|
||||
static int (*JACK_jack_port_unregister)(jack_client_t *, jack_port_t *);
|
||||
static void (*JACK_jack_free)(void *);
|
||||
static const char **(*JACK_jack_get_ports)(jack_client_t *, const char *, const char *, unsigned long);
|
||||
static jack_nframes_t (*JACK_jack_get_sample_rate)(jack_client_t *);
|
||||
static jack_nframes_t (*JACK_jack_get_buffer_size)(jack_client_t *);
|
||||
static jack_port_t *(*JACK_jack_port_register)(jack_client_t *, const char *, const char *, unsigned long, unsigned long);
|
||||
static jack_port_t *(*JACK_jack_port_by_name)(jack_client_t *, const char *);
|
||||
static const char *(*JACK_jack_port_name)(const jack_port_t *);
|
||||
static const char *(*JACK_jack_port_type)(const jack_port_t *);
|
||||
static int (*JACK_jack_connect)(jack_client_t *, const char *, const char *);
|
||||
static int (*JACK_jack_set_process_callback)(jack_client_t *, JackProcessCallback, void *);
|
||||
static int (*JACK_jack_set_sample_rate_callback)(jack_client_t *, JackSampleRateCallback, void *);
|
||||
static int (*JACK_jack_set_buffer_size_callback)(jack_client_t *, JackBufferSizeCallback, void *);
|
||||
|
||||
static int load_jack_syms(void);
|
||||
|
||||
#ifdef SDL_AUDIO_DRIVER_JACK_DYNAMIC
|
||||
|
||||
static const char *jack_library = SDL_AUDIO_DRIVER_JACK_DYNAMIC;
|
||||
static void *jack_handle = NULL;
|
||||
|
||||
// !!! FIXME: this is copy/pasted in several places now
|
||||
static int load_jack_sym(const char *fn, void **addr)
|
||||
{
|
||||
*addr = SDL_LoadFunction(jack_handle, fn);
|
||||
if (*addr == NULL) {
|
||||
// Don't call SDL_SetError(): SDL_LoadFunction already did.
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
// cast funcs to char* first, to please GCC's strict aliasing rules.
|
||||
#define SDL_JACK_SYM(x) \
|
||||
if (!load_jack_sym(#x, (void **)(char *)&JACK_##x)) \
|
||||
return -1
|
||||
|
||||
static void UnloadJackLibrary(void)
|
||||
{
|
||||
if (jack_handle != NULL) {
|
||||
SDL_UnloadObject(jack_handle);
|
||||
jack_handle = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int LoadJackLibrary(void)
|
||||
{
|
||||
int retval = 0;
|
||||
if (jack_handle == NULL) {
|
||||
jack_handle = SDL_LoadObject(jack_library);
|
||||
if (jack_handle == NULL) {
|
||||
retval = -1;
|
||||
// Don't call SDL_SetError(): SDL_LoadObject already did.
|
||||
} else {
|
||||
retval = load_jack_syms();
|
||||
if (retval < 0) {
|
||||
UnloadJackLibrary();
|
||||
}
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#define SDL_JACK_SYM(x) JACK_##x = x
|
||||
|
||||
static void UnloadJackLibrary(void)
|
||||
{
|
||||
}
|
||||
|
||||
static int LoadJackLibrary(void)
|
||||
{
|
||||
load_jack_syms();
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif // SDL_AUDIO_DRIVER_JACK_DYNAMIC
|
||||
|
||||
static int load_jack_syms(void)
|
||||
{
|
||||
SDL_JACK_SYM(jack_client_open);
|
||||
SDL_JACK_SYM(jack_client_close);
|
||||
SDL_JACK_SYM(jack_on_shutdown);
|
||||
SDL_JACK_SYM(jack_activate);
|
||||
SDL_JACK_SYM(jack_deactivate);
|
||||
SDL_JACK_SYM(jack_port_get_buffer);
|
||||
SDL_JACK_SYM(jack_port_unregister);
|
||||
SDL_JACK_SYM(jack_free);
|
||||
SDL_JACK_SYM(jack_get_ports);
|
||||
SDL_JACK_SYM(jack_get_sample_rate);
|
||||
SDL_JACK_SYM(jack_get_buffer_size);
|
||||
SDL_JACK_SYM(jack_port_register);
|
||||
SDL_JACK_SYM(jack_port_by_name);
|
||||
SDL_JACK_SYM(jack_port_name);
|
||||
SDL_JACK_SYM(jack_port_type);
|
||||
SDL_JACK_SYM(jack_connect);
|
||||
SDL_JACK_SYM(jack_set_process_callback);
|
||||
SDL_JACK_SYM(jack_set_sample_rate_callback);
|
||||
SDL_JACK_SYM(jack_set_buffer_size_callback);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void jackShutdownCallback(void *arg) // JACK went away; device is lost.
|
||||
{
|
||||
SDL_AudioDeviceDisconnected((SDL_AudioDevice *)arg);
|
||||
}
|
||||
|
||||
static int jackSampleRateCallback(jack_nframes_t nframes, void *arg)
|
||||
{
|
||||
//SDL_Log("JACK Sample Rate Callback! %d", (int) nframes);
|
||||
SDL_AudioDevice *device = (SDL_AudioDevice *) arg;
|
||||
SDL_AudioSpec newspec;
|
||||
SDL_copyp(&newspec, &device->spec);
|
||||
newspec.freq = (int) nframes;
|
||||
if (SDL_AudioDeviceFormatChanged(device, &newspec, device->sample_frames) < 0) {
|
||||
SDL_AudioDeviceDisconnected(device);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int jackBufferSizeCallback(jack_nframes_t nframes, void *arg)
|
||||
{
|
||||
//SDL_Log("JACK Buffer Size Callback! %d", (int) nframes);
|
||||
SDL_AudioDevice *device = (SDL_AudioDevice *) arg;
|
||||
SDL_AudioSpec newspec;
|
||||
SDL_copyp(&newspec, &device->spec);
|
||||
if (SDL_AudioDeviceFormatChanged(device, &newspec, (int) nframes) < 0) {
|
||||
SDL_AudioDeviceDisconnected(device);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int jackProcessPlaybackCallback(jack_nframes_t nframes, void *arg)
|
||||
{
|
||||
SDL_assert(nframes == ((SDL_AudioDevice *)arg)->sample_frames);
|
||||
SDL_OutputAudioThreadIterate((SDL_AudioDevice *)arg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int JACK_PlayDevice(SDL_AudioDevice *device, const Uint8 *ui8buffer, int buflen)
|
||||
{
|
||||
const float *buffer = (float *) ui8buffer;
|
||||
jack_port_t **ports = device->hidden->sdlports;
|
||||
const int total_channels = device->spec.channels;
|
||||
const int total_frames = device->sample_frames;
|
||||
const jack_nframes_t nframes = (jack_nframes_t) device->sample_frames;
|
||||
|
||||
for (int channelsi = 0; channelsi < total_channels; channelsi++) {
|
||||
float *dst = (float *)JACK_jack_port_get_buffer(ports[channelsi], nframes);
|
||||
if (dst) {
|
||||
const float *src = buffer + channelsi;
|
||||
for (int framesi = 0; framesi < total_frames; framesi++) {
|
||||
*(dst++) = *src;
|
||||
src += total_channels;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static Uint8 *JACK_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
|
||||
{
|
||||
return (Uint8 *)device->hidden->iobuffer;
|
||||
}
|
||||
|
||||
static int jackProcessCaptureCallback(jack_nframes_t nframes, void *arg)
|
||||
{
|
||||
SDL_assert(nframes == ((SDL_AudioDevice *)arg)->sample_frames);
|
||||
SDL_CaptureAudioThreadIterate((SDL_AudioDevice *)arg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int JACK_CaptureFromDevice(SDL_AudioDevice *device, void *vbuffer, int buflen)
|
||||
{
|
||||
float *buffer = (float *) vbuffer;
|
||||
jack_port_t **ports = device->hidden->sdlports;
|
||||
const int total_channels = device->spec.channels;
|
||||
const int total_frames = device->sample_frames;
|
||||
const jack_nframes_t nframes = (jack_nframes_t) device->sample_frames;
|
||||
|
||||
for (int channelsi = 0; channelsi < total_channels; channelsi++) {
|
||||
const float *src = (const float *)JACK_jack_port_get_buffer(ports[channelsi], nframes);
|
||||
if (src) {
|
||||
float *dst = buffer + channelsi;
|
||||
for (int framesi = 0; framesi < total_frames; framesi++) {
|
||||
*dst = *(src++);
|
||||
dst += total_channels;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return buflen;
|
||||
}
|
||||
|
||||
static void JACK_FlushCapture(SDL_AudioDevice *device)
|
||||
{
|
||||
// do nothing, the data will just be replaced next callback.
|
||||
}
|
||||
|
||||
static void JACK_CloseDevice(SDL_AudioDevice *device)
|
||||
{
|
||||
if (device->hidden) {
|
||||
if (device->hidden->client) {
|
||||
JACK_jack_deactivate(device->hidden->client);
|
||||
|
||||
if (device->hidden->sdlports) {
|
||||
const int channels = device->spec.channels;
|
||||
int i;
|
||||
for (i = 0; i < channels; i++) {
|
||||
JACK_jack_port_unregister(device->hidden->client, device->hidden->sdlports[i]);
|
||||
}
|
||||
SDL_free(device->hidden->sdlports);
|
||||
}
|
||||
|
||||
JACK_jack_client_close(device->hidden->client);
|
||||
}
|
||||
|
||||
SDL_free(device->hidden->iobuffer);
|
||||
SDL_free(device->hidden);
|
||||
device->hidden = NULL;
|
||||
|
||||
SDL_AudioThreadFinalize(device);
|
||||
}
|
||||
}
|
||||
|
||||
// !!! FIXME: unify this (PulseAudio has a getAppName, Pipewire has a thing, etc
|
||||
static const char *GetJackAppName(void)
|
||||
{
|
||||
const char *retval = SDL_GetHint(SDL_HINT_AUDIO_DEVICE_APP_NAME);
|
||||
if (retval && *retval) {
|
||||
return retval;
|
||||
}
|
||||
retval = SDL_GetHint(SDL_HINT_APP_NAME);
|
||||
if (retval && *retval) {
|
||||
return retval;
|
||||
}
|
||||
return "SDL Application";
|
||||
}
|
||||
|
||||
static int JACK_OpenDevice(SDL_AudioDevice *device)
|
||||
{
|
||||
/* Note that JACK uses "output" for capture devices (they output audio
|
||||
data to us) and "input" for playback (we input audio data to them).
|
||||
Likewise, SDL's playback port will be "output" (we write data out)
|
||||
and capture will be "input" (we read data in). */
|
||||
SDL_bool iscapture = device->iscapture;
|
||||
const unsigned long sysportflags = iscapture ? JackPortIsOutput : JackPortIsInput;
|
||||
const unsigned long sdlportflags = iscapture ? JackPortIsInput : JackPortIsOutput;
|
||||
const JackProcessCallback callback = iscapture ? jackProcessCaptureCallback : jackProcessPlaybackCallback;
|
||||
const char *sdlportstr = iscapture ? "input" : "output";
|
||||
const char **devports = NULL;
|
||||
int *audio_ports;
|
||||
jack_client_t *client = NULL;
|
||||
jack_status_t status;
|
||||
int channels = 0;
|
||||
int ports = 0;
|
||||
int i;
|
||||
|
||||
// Initialize all variables that we clean on shutdown
|
||||
device->hidden = (struct SDL_PrivateAudioData *)SDL_calloc(1, sizeof(*device->hidden));
|
||||
if (device->hidden == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
|
||||
client = JACK_jack_client_open(GetJackAppName(), JackNoStartServer, &status, NULL);
|
||||
device->hidden->client = client;
|
||||
if (client == NULL) {
|
||||
return SDL_SetError("Can't open JACK client");
|
||||
}
|
||||
|
||||
devports = JACK_jack_get_ports(client, NULL, NULL, JackPortIsPhysical | sysportflags);
|
||||
if (devports == NULL || !devports[0]) {
|
||||
return SDL_SetError("No physical JACK ports available");
|
||||
}
|
||||
|
||||
while (devports[++ports]) {
|
||||
// spin to count devports
|
||||
}
|
||||
|
||||
// Filter out non-audio ports
|
||||
audio_ports = SDL_calloc(ports, sizeof(*audio_ports));
|
||||
for (i = 0; i < ports; i++) {
|
||||
const jack_port_t *dport = JACK_jack_port_by_name(client, devports[i]);
|
||||
const char *type = JACK_jack_port_type(dport);
|
||||
const int len = SDL_strlen(type);
|
||||
// See if type ends with "audio"
|
||||
if (len >= 5 && !SDL_memcmp(type + len - 5, "audio", 5)) {
|
||||
audio_ports[channels++] = i;
|
||||
}
|
||||
}
|
||||
if (channels == 0) {
|
||||
SDL_free(audio_ports);
|
||||
return SDL_SetError("No physical JACK ports available");
|
||||
}
|
||||
|
||||
// Jack pretty much demands what it wants.
|
||||
device->spec.format = SDL_AUDIO_F32;
|
||||
device->spec.freq = JACK_jack_get_sample_rate(client);
|
||||
device->spec.channels = channels;
|
||||
device->sample_frames = JACK_jack_get_buffer_size(client);
|
||||
|
||||
SDL_UpdatedAudioDeviceFormat(device);
|
||||
|
||||
if (!device->iscapture) {
|
||||
device->hidden->iobuffer = (float *)SDL_calloc(1, device->buffer_size);
|
||||
if (!device->hidden->iobuffer) {
|
||||
SDL_free(audio_ports);
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
}
|
||||
|
||||
// Build SDL's ports, which we will connect to the device ports.
|
||||
device->hidden->sdlports = (jack_port_t **)SDL_calloc(channels, sizeof(jack_port_t *));
|
||||
if (device->hidden->sdlports == NULL) {
|
||||
SDL_free(audio_ports);
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
|
||||
for (i = 0; i < channels; i++) {
|
||||
char portname[32];
|
||||
(void)SDL_snprintf(portname, sizeof(portname), "sdl_jack_%s_%d", sdlportstr, i);
|
||||
device->hidden->sdlports[i] = JACK_jack_port_register(client, portname, JACK_DEFAULT_AUDIO_TYPE, sdlportflags, 0);
|
||||
if (device->hidden->sdlports[i] == NULL) {
|
||||
SDL_free(audio_ports);
|
||||
return SDL_SetError("jack_port_register failed");
|
||||
}
|
||||
}
|
||||
|
||||
if (JACK_jack_set_buffer_size_callback(client, jackBufferSizeCallback, device) != 0) {
|
||||
SDL_free(audio_ports);
|
||||
return SDL_SetError("JACK: Couldn't set buffer size callback");
|
||||
} else if (JACK_jack_set_sample_rate_callback(client, jackSampleRateCallback, device) != 0) {
|
||||
SDL_free(audio_ports);
|
||||
return SDL_SetError("JACK: Couldn't set sample rate callback");
|
||||
} else if (JACK_jack_set_process_callback(client, callback, device) != 0) {
|
||||
SDL_free(audio_ports);
|
||||
return SDL_SetError("JACK: Couldn't set process callback");
|
||||
}
|
||||
|
||||
JACK_jack_on_shutdown(client, jackShutdownCallback, device);
|
||||
|
||||
if (JACK_jack_activate(client) != 0) {
|
||||
SDL_free(audio_ports);
|
||||
return SDL_SetError("Failed to activate JACK client");
|
||||
}
|
||||
|
||||
// once activated, we can connect all the ports.
|
||||
for (i = 0; i < channels; i++) {
|
||||
const char *sdlport = JACK_jack_port_name(device->hidden->sdlports[i]);
|
||||
const char *srcport = iscapture ? devports[audio_ports[i]] : sdlport;
|
||||
const char *dstport = iscapture ? sdlport : devports[audio_ports[i]];
|
||||
if (JACK_jack_connect(client, srcport, dstport) != 0) {
|
||||
SDL_free(audio_ports);
|
||||
return SDL_SetError("Couldn't connect JACK ports: %s => %s", srcport, dstport);
|
||||
}
|
||||
}
|
||||
|
||||
// don't need these anymore.
|
||||
JACK_jack_free(devports);
|
||||
SDL_free(audio_ports);
|
||||
|
||||
// We're ready to rock and roll. :-)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void JACK_Deinitialize(void)
|
||||
{
|
||||
UnloadJackLibrary();
|
||||
}
|
||||
|
||||
static SDL_bool JACK_Init(SDL_AudioDriverImpl *impl)
|
||||
{
|
||||
if (LoadJackLibrary() < 0) {
|
||||
return SDL_FALSE;
|
||||
} else {
|
||||
// Make sure a JACK server is running and available.
|
||||
jack_status_t status;
|
||||
jack_client_t *client = JACK_jack_client_open("SDL", JackNoStartServer, &status, NULL);
|
||||
if (client == NULL) {
|
||||
UnloadJackLibrary();
|
||||
return SDL_FALSE;
|
||||
}
|
||||
JACK_jack_client_close(client);
|
||||
}
|
||||
|
||||
impl->OpenDevice = JACK_OpenDevice;
|
||||
impl->GetDeviceBuf = JACK_GetDeviceBuf;
|
||||
impl->PlayDevice = JACK_PlayDevice;
|
||||
impl->CloseDevice = JACK_CloseDevice;
|
||||
impl->Deinitialize = JACK_Deinitialize;
|
||||
impl->CaptureFromDevice = JACK_CaptureFromDevice;
|
||||
impl->FlushCapture = JACK_FlushCapture;
|
||||
impl->OnlyHasDefaultOutputDevice = SDL_TRUE;
|
||||
impl->OnlyHasDefaultCaptureDevice = SDL_TRUE;
|
||||
impl->HasCaptureSupport = SDL_TRUE;
|
||||
impl->ProvidesOwnCallbackThread = SDL_TRUE;
|
||||
|
||||
return SDL_TRUE;
|
||||
}
|
||||
|
||||
AudioBootStrap JACK_bootstrap = {
|
||||
"jack", "JACK Audio Connection Kit", JACK_Init, SDL_FALSE
|
||||
};
|
||||
|
||||
#endif // SDL_AUDIO_DRIVER_JACK
|
||||
35
vendor/sdl-3.0.0/src/audio/jack/SDL_jackaudio.h
vendored
Normal file
35
vendor/sdl-3.0.0/src/audio/jack/SDL_jackaudio.h
vendored
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#ifndef SDL_jackaudio_h_
|
||||
#define SDL_jackaudio_h_
|
||||
|
||||
#include <jack/jack.h>
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
|
||||
struct SDL_PrivateAudioData
|
||||
{
|
||||
jack_client_t *client;
|
||||
jack_port_t **sdlports;
|
||||
float *iobuffer;
|
||||
};
|
||||
|
||||
#endif // SDL_jackaudio_h_
|
||||
287
vendor/sdl-3.0.0/src/audio/n3ds/SDL_n3dsaudio.c
vendored
Normal file
287
vendor/sdl-3.0.0/src/audio/n3ds/SDL_n3dsaudio.c
vendored
Normal file
|
|
@ -0,0 +1,287 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifdef SDL_AUDIO_DRIVER_N3DS
|
||||
|
||||
// N3DS Audio driver
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
#include "SDL_n3dsaudio.h"
|
||||
|
||||
#define N3DSAUDIO_DRIVER_NAME "n3ds"
|
||||
|
||||
static dspHookCookie dsp_hook;
|
||||
static SDL_AudioDevice *audio_device;
|
||||
|
||||
// fully local functions related to the wavebufs / DSP, not the same as the `device->lock` SDL_Mutex!
|
||||
static SDL_INLINE void contextLock(SDL_AudioDevice *device)
|
||||
{
|
||||
LightLock_Lock(&device->hidden->lock);
|
||||
}
|
||||
|
||||
static SDL_INLINE void contextUnlock(SDL_AudioDevice *device)
|
||||
{
|
||||
LightLock_Unlock(&device->hidden->lock);
|
||||
}
|
||||
|
||||
static void N3DSAUD_DspHook(DSP_HookType hook)
|
||||
{
|
||||
if (hook == DSPHOOK_ONCANCEL) {
|
||||
contextLock(audio_device);
|
||||
audio_device->hidden->isCancelled = SDL_TRUE;
|
||||
SDL_AudioDeviceDisconnected(audio_device);
|
||||
CondVar_Broadcast(&audio_device->hidden->cv);
|
||||
contextUnlock(audio_device);
|
||||
}
|
||||
}
|
||||
|
||||
static void AudioFrameFinished(void *vdevice)
|
||||
{
|
||||
bool shouldBroadcast = false;
|
||||
unsigned i;
|
||||
SDL_AudioDevice *device = (SDL_AudioDevice *)vdevice;
|
||||
|
||||
contextLock(device);
|
||||
|
||||
for (i = 0; i < NUM_BUFFERS; i++) {
|
||||
if (device->hidden->waveBuf[i].status == NDSP_WBUF_DONE) {
|
||||
device->hidden->waveBuf[i].status = NDSP_WBUF_FREE;
|
||||
shouldBroadcast = SDL_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldBroadcast) {
|
||||
CondVar_Broadcast(&device->hidden->cv);
|
||||
}
|
||||
|
||||
contextUnlock(device);
|
||||
}
|
||||
|
||||
static int N3DSAUDIO_OpenDevice(SDL_AudioDevice *device)
|
||||
{
|
||||
Result ndsp_init_res;
|
||||
Uint8 *data_vaddr;
|
||||
float mix[12];
|
||||
|
||||
device->hidden = (struct SDL_PrivateAudioData *)SDL_calloc(1, sizeof(*device->hidden));
|
||||
if (device->hidden == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
|
||||
// Initialise the DSP service
|
||||
ndsp_init_res = ndspInit();
|
||||
if (R_FAILED(ndsp_init_res)) {
|
||||
if ((R_SUMMARY(ndsp_init_res) == RS_NOTFOUND) && (R_MODULE(ndsp_init_res) == RM_DSP)) {
|
||||
SDL_SetError("DSP init failed: dspfirm.cdc missing!");
|
||||
} else {
|
||||
SDL_SetError("DSP init failed. Error code: 0x%lX", ndsp_init_res);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Initialise internal state
|
||||
LightLock_Init(&device->hidden->lock);
|
||||
CondVar_Init(&device->hidden->cv);
|
||||
|
||||
if (device->spec.channels > 2) {
|
||||
device->spec.channels = 2;
|
||||
}
|
||||
|
||||
Uint32 format = 0;
|
||||
SDL_AudioFormat test_format;
|
||||
const SDL_AudioFormat *closefmts = SDL_ClosestAudioFormats(device->spec.format);
|
||||
while ((test_format = *(closefmts++)) != 0) {
|
||||
if (test_format == SDL_AUDIO_S8) { // Signed 8-bit audio supported
|
||||
format = (device->spec.channels == 2) ? NDSP_FORMAT_STEREO_PCM8 : NDSP_FORMAT_MONO_PCM8;
|
||||
break;
|
||||
} else if (test_format == SDL_AUDIO_S16) { // Signed 16-bit audio supported
|
||||
format = (device->spec.channels == 2) ? NDSP_FORMAT_STEREO_PCM16 : NDSP_FORMAT_MONO_PCM16;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!test_format) { // shouldn't happen, but just in case...
|
||||
return SDL_SetError("No supported audio format found.");
|
||||
}
|
||||
|
||||
device->spec.format = test_format;
|
||||
|
||||
// Update the fragment size as size in bytes
|
||||
SDL_UpdatedAudioDeviceFormat(device);
|
||||
|
||||
// Allocate mixing buffer
|
||||
if (device->buffer_size >= SDL_MAX_UINT32 / 2) {
|
||||
return SDL_SetError("Mixing buffer is too large.");
|
||||
}
|
||||
|
||||
device->hidden->mixbuf = (Uint8 *)SDL_malloc(device->buffer_size);
|
||||
if (device->hidden->mixbuf == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
|
||||
SDL_memset(device->hidden->mixbuf, device->silence_value, device->buffer_size);
|
||||
|
||||
data_vaddr = (Uint8 *)linearAlloc(device->buffer_size * NUM_BUFFERS);
|
||||
if (data_vaddr == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
|
||||
SDL_memset(data_vaddr, 0, device->buffer_size * NUM_BUFFERS);
|
||||
DSP_FlushDataCache(data_vaddr, device->buffer_size * NUM_BUFFERS);
|
||||
|
||||
device->hidden->nextbuf = 0;
|
||||
|
||||
ndspChnReset(0);
|
||||
|
||||
ndspChnSetInterp(0, NDSP_INTERP_LINEAR);
|
||||
ndspChnSetRate(0, device->spec.freq);
|
||||
ndspChnSetFormat(0, format);
|
||||
|
||||
SDL_zeroa(mix);
|
||||
mix[0] = mix[1] = 1.0f;
|
||||
ndspChnSetMix(0, mix);
|
||||
|
||||
SDL_memset(device->hidden->waveBuf, 0, sizeof(ndspWaveBuf) * NUM_BUFFERS);
|
||||
|
||||
const int sample_frame_size = SDL_AUDIO_FRAMESIZE(device->spec);
|
||||
for (unsigned i = 0; i < NUM_BUFFERS; i++) {
|
||||
device->hidden->waveBuf[i].data_vaddr = data_vaddr;
|
||||
device->hidden->waveBuf[i].nsamples = device->buffer_size / sample_frame_size;
|
||||
data_vaddr += device->buffer_size;
|
||||
}
|
||||
|
||||
// Setup callback
|
||||
audio_device = device;
|
||||
ndspSetCallback(AudioFrameFinished, device);
|
||||
dspHook(&dsp_hook, N3DSAUD_DspHook);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int N3DSAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
|
||||
{
|
||||
contextLock(device);
|
||||
|
||||
const size_t nextbuf = device->hidden->nextbuf;
|
||||
|
||||
if (device->hidden->isCancelled ||
|
||||
device->hidden->waveBuf[nextbuf].status != NDSP_WBUF_FREE) {
|
||||
contextUnlock(device);
|
||||
return 0; // !!! FIXME: is this a fatal error? If so, this should return -1.
|
||||
}
|
||||
|
||||
device->hidden->nextbuf = (nextbuf + 1) % NUM_BUFFERS;
|
||||
|
||||
contextUnlock(device);
|
||||
|
||||
SDL_memcpy((void *)device->hidden->waveBuf[nextbuf].data_vaddr, buffer, buflen);
|
||||
DSP_FlushDataCache(device->hidden->waveBuf[nextbuf].data_vaddr, buflen);
|
||||
|
||||
ndspChnWaveBufAdd(0, &device->hidden->waveBuf[nextbuf]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int N3DSAUDIO_WaitDevice(SDL_AudioDevice *device)
|
||||
{
|
||||
contextLock(device);
|
||||
while (!device->hidden->isCancelled && !SDL_AtomicGet(&device->shutdown) &&
|
||||
device->hidden->waveBuf[device->hidden->nextbuf].status != NDSP_WBUF_FREE) {
|
||||
CondVar_Wait(&device->hidden->cv, &device->hidden->lock);
|
||||
}
|
||||
contextUnlock(device);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static Uint8 *N3DSAUDIO_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
|
||||
{
|
||||
return device->hidden->mixbuf;
|
||||
}
|
||||
|
||||
static void N3DSAUDIO_CloseDevice(SDL_AudioDevice *device)
|
||||
{
|
||||
if (!device->hidden) {
|
||||
return;
|
||||
}
|
||||
|
||||
contextLock(device);
|
||||
|
||||
dspUnhook(&dsp_hook);
|
||||
ndspSetCallback(NULL, NULL);
|
||||
|
||||
if (!device->hidden->isCancelled) {
|
||||
ndspChnReset(0);
|
||||
SDL_memset(device->hidden->waveBuf, 0, sizeof(ndspWaveBuf) * NUM_BUFFERS);
|
||||
CondVar_Broadcast(&device->hidden->cv);
|
||||
}
|
||||
|
||||
contextUnlock(device);
|
||||
|
||||
ndspExit();
|
||||
|
||||
if (device->hidden->waveBuf[0].data_vaddr) {
|
||||
linearFree((void *)device->hidden->waveBuf[0].data_vaddr);
|
||||
}
|
||||
|
||||
if (device->hidden->mixbuf) {
|
||||
SDL_free(device->hidden->mixbuf);
|
||||
device->hidden->mixbuf = NULL;
|
||||
}
|
||||
|
||||
SDL_free(device->hidden);
|
||||
device->hidden = NULL;
|
||||
}
|
||||
|
||||
static void N3DSAUDIO_ThreadInit(SDL_AudioDevice *device)
|
||||
{
|
||||
s32 current_priority = 0x30;
|
||||
svcGetThreadPriority(¤t_priority, CUR_THREAD_HANDLE);
|
||||
current_priority--;
|
||||
// 0x18 is reserved for video, 0x30 is the default for main thread
|
||||
current_priority = SDL_clamp(current_priority, 0x19, 0x2F);
|
||||
svcSetThreadPriority(CUR_THREAD_HANDLE, current_priority);
|
||||
}
|
||||
|
||||
static SDL_bool N3DSAUDIO_Init(SDL_AudioDriverImpl *impl)
|
||||
{
|
||||
impl->OpenDevice = N3DSAUDIO_OpenDevice;
|
||||
impl->PlayDevice = N3DSAUDIO_PlayDevice;
|
||||
impl->WaitDevice = N3DSAUDIO_WaitDevice;
|
||||
impl->GetDeviceBuf = N3DSAUDIO_GetDeviceBuf;
|
||||
impl->CloseDevice = N3DSAUDIO_CloseDevice;
|
||||
impl->ThreadInit = N3DSAUDIO_ThreadInit;
|
||||
impl->OnlyHasDefaultOutputDevice = SDL_TRUE;
|
||||
|
||||
// Should be possible, but micInit would fail
|
||||
impl->HasCaptureSupport = SDL_FALSE;
|
||||
|
||||
return SDL_TRUE;
|
||||
}
|
||||
|
||||
AudioBootStrap N3DSAUDIO_bootstrap = {
|
||||
N3DSAUDIO_DRIVER_NAME,
|
||||
"SDL N3DS audio driver",
|
||||
N3DSAUDIO_Init,
|
||||
0
|
||||
};
|
||||
|
||||
#endif // SDL_AUDIO_DRIVER_N3DS
|
||||
40
vendor/sdl-3.0.0/src/audio/n3ds/SDL_n3dsaudio.h
vendored
Normal file
40
vendor/sdl-3.0.0/src/audio/n3ds/SDL_n3dsaudio.h
vendored
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
#ifndef SDL_n3dsaudio_h
|
||||
#define SDL_n3dsaudio_h
|
||||
|
||||
#include <3ds.h>
|
||||
|
||||
#define NUM_BUFFERS 3 // -- Minimum 2!
|
||||
|
||||
struct SDL_PrivateAudioData
|
||||
{
|
||||
// Speaker data
|
||||
Uint8 *mixbuf;
|
||||
Uint32 nextbuf;
|
||||
ndspWaveBuf waveBuf[NUM_BUFFERS];
|
||||
LightLock lock;
|
||||
CondVar cv;
|
||||
SDL_bool isCancelled;
|
||||
};
|
||||
|
||||
#endif // SDL_n3dsaudio_h
|
||||
328
vendor/sdl-3.0.0/src/audio/netbsd/SDL_netbsdaudio.c
vendored
Normal file
328
vendor/sdl-3.0.0/src/audio/netbsd/SDL_netbsdaudio.c
vendored
Normal file
|
|
@ -0,0 +1,328 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifdef SDL_AUDIO_DRIVER_NETBSD
|
||||
|
||||
// Driver for native NetBSD audio(4).
|
||||
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/audioio.h>
|
||||
|
||||
#include "../../core/unix/SDL_poll.h"
|
||||
#include "../SDL_audiodev_c.h"
|
||||
#include "SDL_netbsdaudio.h"
|
||||
|
||||
//#define DEBUG_AUDIO
|
||||
|
||||
static void NETBSDAUDIO_DetectDevices(SDL_AudioDevice **default_output, SDL_AudioDevice **default_capture)
|
||||
{
|
||||
SDL_EnumUnixAudioDevices(SDL_FALSE, NULL);
|
||||
}
|
||||
|
||||
static void NETBSDAUDIO_Status(SDL_AudioDevice *device)
|
||||
{
|
||||
#ifdef DEBUG_AUDIO
|
||||
/* *INDENT-OFF* */ /* clang-format off */
|
||||
audio_info_t info;
|
||||
const struct audio_prinfo *prinfo;
|
||||
|
||||
if (ioctl(device->hidden->audio_fd, AUDIO_GETINFO, &info) < 0) {
|
||||
fprintf(stderr, "AUDIO_GETINFO failed.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
prinfo = device->iscapture ? &info.record : &info.play;
|
||||
|
||||
fprintf(stderr, "\n"
|
||||
"[%s info]\n"
|
||||
"buffer size : %d bytes\n"
|
||||
"sample rate : %i Hz\n"
|
||||
"channels : %i\n"
|
||||
"precision : %i-bit\n"
|
||||
"encoding : 0x%x\n"
|
||||
"seek : %i\n"
|
||||
"sample count : %i\n"
|
||||
"EOF count : %i\n"
|
||||
"paused : %s\n"
|
||||
"error occurred : %s\n"
|
||||
"waiting : %s\n"
|
||||
"active : %s\n"
|
||||
"",
|
||||
device->iscapture ? "record" : "play",
|
||||
prinfo->buffer_size,
|
||||
prinfo->sample_rate,
|
||||
prinfo->channels,
|
||||
prinfo->precision,
|
||||
prinfo->encoding,
|
||||
prinfo->seek,
|
||||
prinfo->samples,
|
||||
prinfo->eof,
|
||||
prinfo->pause ? "yes" : "no",
|
||||
prinfo->error ? "yes" : "no",
|
||||
prinfo->waiting ? "yes" : "no",
|
||||
prinfo->active ? "yes" : "no");
|
||||
|
||||
fprintf(stderr, "\n"
|
||||
"[audio info]\n"
|
||||
"monitor_gain : %i\n"
|
||||
"hw block size : %d bytes\n"
|
||||
"hi watermark : %i\n"
|
||||
"lo watermark : %i\n"
|
||||
"audio mode : %s\n"
|
||||
"",
|
||||
info.monitor_gain,
|
||||
info.blocksize,
|
||||
info.hiwat, info.lowat,
|
||||
(info.mode == AUMODE_PLAY) ? "PLAY"
|
||||
: (info.mode == AUMODE_RECORD) ? "RECORD"
|
||||
: (info.mode == AUMODE_PLAY_ALL ? "PLAY_ALL" : "?"));
|
||||
|
||||
fprintf(stderr, "\n"
|
||||
"[audio spec]\n"
|
||||
"format : 0x%x\n"
|
||||
"size : %u\n"
|
||||
"",
|
||||
device->spec.format,
|
||||
device->buffer_size);
|
||||
/* *INDENT-ON* */ /* clang-format on */
|
||||
|
||||
#endif // DEBUG_AUDIO
|
||||
}
|
||||
|
||||
static int NETBSDAUDIO_WaitDevice(SDL_AudioDevice *device)
|
||||
{
|
||||
const SDL_bool iscapture = device->iscapture;
|
||||
while (!SDL_AtomicGet(&device->shutdown)) {
|
||||
audio_info_t info;
|
||||
const int rc = ioctl(device->hidden->audio_fd, AUDIO_GETINFO, &info);
|
||||
if (rc < 0) {
|
||||
if (errno == EAGAIN) {
|
||||
continue;
|
||||
}
|
||||
// Hmm, not much we can do - abort
|
||||
fprintf(stderr, "netbsdaudio WaitDevice ioctl failed (unrecoverable): %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
const size_t remain = (size_t)((iscapture ? info.record.seek : info.play.seek) * SDL_AUDIO_BYTESIZE(device->spec.format));
|
||||
if (!iscapture && (remain >= device->buffer_size)) {
|
||||
SDL_Delay(10);
|
||||
} else if (iscapture && (remain < device->buffer_size)) {
|
||||
SDL_Delay(10);
|
||||
} else {
|
||||
break; // ready to go!
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int NETBSDAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
|
||||
{
|
||||
struct SDL_PrivateAudioData *h = device->hidden;
|
||||
const int written = write(h->audio_fd, buffer, buflen);
|
||||
if (written != buflen) { // Treat even partial writes as fatal errors.
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_AUDIO
|
||||
fprintf(stderr, "Wrote %d bytes of audio data\n", written);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static Uint8 *NETBSDAUDIO_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
|
||||
{
|
||||
return device->hidden->mixbuf;
|
||||
}
|
||||
|
||||
static int NETBSDAUDIO_CaptureFromDevice(SDL_AudioDevice *device, void *vbuffer, int buflen)
|
||||
{
|
||||
Uint8 *buffer = (Uint8 *)vbuffer;
|
||||
const int br = read(device->hidden->audio_fd, buffer, buflen);
|
||||
if (br == -1) {
|
||||
// Non recoverable error has occurred. It should be reported!!!
|
||||
perror("audio");
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_AUDIO
|
||||
fprintf(stderr, "Captured %d bytes of audio data\n", br);
|
||||
#endif
|
||||
return br;
|
||||
}
|
||||
|
||||
static void NETBSDAUDIO_FlushCapture(SDL_AudioDevice *device)
|
||||
{
|
||||
struct SDL_PrivateAudioData *h = device->hidden;
|
||||
audio_info_t info;
|
||||
if (ioctl(device->hidden->audio_fd, AUDIO_GETINFO, &info) == 0) {
|
||||
size_t remain = (size_t)(info.record.seek * SDL_AUDIO_BYTESIZE(device->spec.format));
|
||||
while (remain > 0) {
|
||||
char buf[512];
|
||||
const size_t len = SDL_min(sizeof(buf), remain);
|
||||
const ssize_t br = read(h->audio_fd, buf, len);
|
||||
if (br <= 0) {
|
||||
break;
|
||||
}
|
||||
remain -= br;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void NETBSDAUDIO_CloseDevice(SDL_AudioDevice *device)
|
||||
{
|
||||
if (device->hidden) {
|
||||
if (device->hidden->audio_fd >= 0) {
|
||||
close(device->hidden->audio_fd);
|
||||
}
|
||||
SDL_free(device->hidden->mixbuf);
|
||||
SDL_free(device->hidden);
|
||||
device->hidden = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int NETBSDAUDIO_OpenDevice(SDL_AudioDevice *device)
|
||||
{
|
||||
const SDL_bool iscapture = device->iscapture;
|
||||
int encoding = AUDIO_ENCODING_NONE;
|
||||
audio_info_t info, hwinfo;
|
||||
struct audio_prinfo *prinfo = iscapture ? &info.record : &info.play;
|
||||
|
||||
// Initialize all variables that we clean on shutdown
|
||||
device->hidden = (struct SDL_PrivateAudioData *) SDL_calloc(1, sizeof(*device->hidden));
|
||||
if (device->hidden == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
|
||||
// Open the audio device; we hardcode the device path in `device->name` for lack of better info, so use that.
|
||||
const int flags = ((device->iscapture) ? O_RDONLY : O_WRONLY);
|
||||
device->hidden->audio_fd = open(device->name, flags | O_CLOEXEC);
|
||||
if (device->hidden->audio_fd < 0) {
|
||||
return SDL_SetError("Couldn't open %s: %s", device->name, strerror(errno));
|
||||
}
|
||||
|
||||
AUDIO_INITINFO(&info);
|
||||
|
||||
#ifdef AUDIO_GETFORMAT // Introduced in NetBSD 9.0
|
||||
if (ioctl(device->hidden->audio_fd, AUDIO_GETFORMAT, &hwinfo) != -1) {
|
||||
// Use the device's native sample rate so the kernel doesn't have to resample.
|
||||
device->spec.freq = iscapture ? hwinfo.record.sample_rate : hwinfo.play.sample_rate;
|
||||
}
|
||||
#endif
|
||||
|
||||
prinfo->sample_rate = device->spec.freq;
|
||||
prinfo->channels = device->spec.channels;
|
||||
|
||||
SDL_AudioFormat test_format;
|
||||
const SDL_AudioFormat *closefmts = SDL_ClosestAudioFormats(device->spec.format);
|
||||
while ((test_format = *(closefmts++)) != 0) {
|
||||
switch (test_format) {
|
||||
case SDL_AUDIO_U8:
|
||||
encoding = AUDIO_ENCODING_ULINEAR;
|
||||
break;
|
||||
case SDL_AUDIO_S8:
|
||||
encoding = AUDIO_ENCODING_SLINEAR;
|
||||
break;
|
||||
case SDL_AUDIO_S16LE:
|
||||
encoding = AUDIO_ENCODING_SLINEAR_LE;
|
||||
break;
|
||||
case SDL_AUDIO_S16BE:
|
||||
encoding = AUDIO_ENCODING_SLINEAR_BE;
|
||||
break;
|
||||
case SDL_AUDIO_S32LE:
|
||||
encoding = AUDIO_ENCODING_SLINEAR_LE;
|
||||
break;
|
||||
case SDL_AUDIO_S32BE:
|
||||
encoding = AUDIO_ENCODING_SLINEAR_BE;
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (!test_format) {
|
||||
return SDL_SetError("%s: Unsupported audio format", "netbsd");
|
||||
}
|
||||
prinfo->encoding = encoding;
|
||||
prinfo->precision = SDL_AUDIO_BITSIZE(test_format);
|
||||
|
||||
info.hiwat = 5;
|
||||
info.lowat = 3;
|
||||
if (ioctl(device->hidden->audio_fd, AUDIO_SETINFO, &info) < 0) {
|
||||
return SDL_SetError("AUDIO_SETINFO failed for %s: %s", device->name, strerror(errno));
|
||||
}
|
||||
|
||||
if (ioctl(device->hidden->audio_fd, AUDIO_GETINFO, &info) < 0) {
|
||||
return SDL_SetError("AUDIO_GETINFO failed for %s: %s", device->name, strerror(errno));
|
||||
}
|
||||
|
||||
// Final spec used for the device.
|
||||
device->spec.format = test_format;
|
||||
device->spec.freq = prinfo->sample_rate;
|
||||
device->spec.channels = prinfo->channels;
|
||||
|
||||
SDL_UpdatedAudioDeviceFormat(device);
|
||||
|
||||
if (!iscapture) {
|
||||
// Allocate mixing buffer
|
||||
device->hidden->mixlen = device->buffer_size;
|
||||
device->hidden->mixbuf = (Uint8 *)SDL_malloc(device->hidden->mixlen);
|
||||
if (device->hidden->mixbuf == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
SDL_memset(device->hidden->mixbuf, device->silence_value, device->buffer_size);
|
||||
}
|
||||
|
||||
NETBSDAUDIO_Status(device);
|
||||
|
||||
return 0; // We're ready to rock and roll. :-)
|
||||
}
|
||||
|
||||
static SDL_bool NETBSDAUDIO_Init(SDL_AudioDriverImpl *impl)
|
||||
{
|
||||
impl->DetectDevices = NETBSDAUDIO_DetectDevices;
|
||||
impl->OpenDevice = NETBSDAUDIO_OpenDevice;
|
||||
impl->WaitDevice = NETBSDAUDIO_WaitDevice;
|
||||
impl->PlayDevice = NETBSDAUDIO_PlayDevice;
|
||||
impl->GetDeviceBuf = NETBSDAUDIO_GetDeviceBuf;
|
||||
impl->CloseDevice = NETBSDAUDIO_CloseDevice;
|
||||
impl->WaitCaptureDevice = NETBSDAUDIO_WaitDevice;
|
||||
impl->CaptureFromDevice = NETBSDAUDIO_CaptureFromDevice;
|
||||
impl->FlushCapture = NETBSDAUDIO_FlushCapture;
|
||||
|
||||
impl->HasCaptureSupport = SDL_TRUE;
|
||||
|
||||
return SDL_TRUE;
|
||||
}
|
||||
|
||||
AudioBootStrap NETBSDAUDIO_bootstrap = {
|
||||
"netbsd", "NetBSD audio", NETBSDAUDIO_Init, SDL_FALSE
|
||||
};
|
||||
|
||||
#endif // SDL_AUDIO_DRIVER_NETBSD
|
||||
44
vendor/sdl-3.0.0/src/audio/netbsd/SDL_netbsdaudio.h
vendored
Normal file
44
vendor/sdl-3.0.0/src/audio/netbsd/SDL_netbsdaudio.h
vendored
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifndef SDL_netbsdaudio_h_
|
||||
#define SDL_netbsdaudio_h_
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
|
||||
struct SDL_PrivateAudioData
|
||||
{
|
||||
// The file descriptor for the audio device
|
||||
int audio_fd;
|
||||
|
||||
// Raw mixing buffer
|
||||
Uint8 *mixbuf;
|
||||
int mixlen;
|
||||
|
||||
// Support for audio timing using a timer, in addition to SDL_IOReady()
|
||||
float frame_ticks;
|
||||
float next_frame;
|
||||
};
|
||||
|
||||
#define FUDGE_TICKS 10 // The scheduler overhead ticks per frame
|
||||
|
||||
#endif // SDL_netbsdaudio_h_
|
||||
782
vendor/sdl-3.0.0/src/audio/openslES/SDL_openslES.c
vendored
Normal file
782
vendor/sdl-3.0.0/src/audio/openslES/SDL_openslES.c
vendored
Normal file
|
|
@ -0,0 +1,782 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifdef SDL_AUDIO_DRIVER_OPENSLES
|
||||
|
||||
// For more discussion of low latency audio on Android, see this:
|
||||
// https://googlesamples.github.io/android-audio-high-performance/guides/opensl_es.html
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
#include "SDL_openslES.h"
|
||||
|
||||
#include "../../core/android/SDL_android.h"
|
||||
#include <SLES/OpenSLES.h>
|
||||
#include <SLES/OpenSLES_Android.h>
|
||||
#include <android/log.h>
|
||||
|
||||
|
||||
#define NUM_BUFFERS 2 // -- Don't lower this!
|
||||
|
||||
struct SDL_PrivateAudioData
|
||||
{
|
||||
Uint8 *mixbuff;
|
||||
int next_buffer;
|
||||
Uint8 *pmixbuff[NUM_BUFFERS];
|
||||
SDL_Semaphore *playsem;
|
||||
};
|
||||
|
||||
#if 0
|
||||
#define LOG_TAG "SDL_openslES"
|
||||
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
|
||||
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
|
||||
//#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE,LOG_TAG,__VA_ARGS__)
|
||||
#define LOGV(...)
|
||||
#else
|
||||
#define LOGE(...)
|
||||
#define LOGI(...)
|
||||
#define LOGV(...)
|
||||
#endif
|
||||
|
||||
/*
|
||||
#define SL_SPEAKER_FRONT_LEFT ((SLuint32) 0x00000001)
|
||||
#define SL_SPEAKER_FRONT_RIGHT ((SLuint32) 0x00000002)
|
||||
#define SL_SPEAKER_FRONT_CENTER ((SLuint32) 0x00000004)
|
||||
#define SL_SPEAKER_LOW_FREQUENCY ((SLuint32) 0x00000008)
|
||||
#define SL_SPEAKER_BACK_LEFT ((SLuint32) 0x00000010)
|
||||
#define SL_SPEAKER_BACK_RIGHT ((SLuint32) 0x00000020)
|
||||
#define SL_SPEAKER_FRONT_LEFT_OF_CENTER ((SLuint32) 0x00000040)
|
||||
#define SL_SPEAKER_FRONT_RIGHT_OF_CENTER ((SLuint32) 0x00000080)
|
||||
#define SL_SPEAKER_BACK_CENTER ((SLuint32) 0x00000100)
|
||||
#define SL_SPEAKER_SIDE_LEFT ((SLuint32) 0x00000200)
|
||||
#define SL_SPEAKER_SIDE_RIGHT ((SLuint32) 0x00000400)
|
||||
#define SL_SPEAKER_TOP_CENTER ((SLuint32) 0x00000800)
|
||||
#define SL_SPEAKER_TOP_FRONT_LEFT ((SLuint32) 0x00001000)
|
||||
#define SL_SPEAKER_TOP_FRONT_CENTER ((SLuint32) 0x00002000)
|
||||
#define SL_SPEAKER_TOP_FRONT_RIGHT ((SLuint32) 0x00004000)
|
||||
#define SL_SPEAKER_TOP_BACK_LEFT ((SLuint32) 0x00008000)
|
||||
#define SL_SPEAKER_TOP_BACK_CENTER ((SLuint32) 0x00010000)
|
||||
#define SL_SPEAKER_TOP_BACK_RIGHT ((SLuint32) 0x00020000)
|
||||
*/
|
||||
#define SL_ANDROID_SPEAKER_STEREO (SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT)
|
||||
#define SL_ANDROID_SPEAKER_QUAD (SL_ANDROID_SPEAKER_STEREO | SL_SPEAKER_BACK_LEFT | SL_SPEAKER_BACK_RIGHT)
|
||||
#define SL_ANDROID_SPEAKER_5DOT1 (SL_ANDROID_SPEAKER_QUAD | SL_SPEAKER_FRONT_CENTER | SL_SPEAKER_LOW_FREQUENCY)
|
||||
#define SL_ANDROID_SPEAKER_7DOT1 (SL_ANDROID_SPEAKER_5DOT1 | SL_SPEAKER_SIDE_LEFT | SL_SPEAKER_SIDE_RIGHT)
|
||||
|
||||
// engine interfaces
|
||||
static SLObjectItf engineObject = NULL;
|
||||
static SLEngineItf engineEngine = NULL;
|
||||
|
||||
// output mix interfaces
|
||||
static SLObjectItf outputMixObject = NULL;
|
||||
|
||||
// buffer queue player interfaces
|
||||
static SLObjectItf bqPlayerObject = NULL;
|
||||
static SLPlayItf bqPlayerPlay = NULL;
|
||||
static SLAndroidSimpleBufferQueueItf bqPlayerBufferQueue = NULL;
|
||||
#if 0
|
||||
static SLVolumeItf bqPlayerVolume;
|
||||
#endif
|
||||
|
||||
// recorder interfaces
|
||||
static SLObjectItf recorderObject = NULL;
|
||||
static SLRecordItf recorderRecord = NULL;
|
||||
static SLAndroidSimpleBufferQueueItf recorderBufferQueue = NULL;
|
||||
|
||||
#if 0
|
||||
static const char *sldevaudiorecorderstr = "SLES Audio Recorder";
|
||||
static const char *sldevaudioplayerstr = "SLES Audio Player";
|
||||
|
||||
#define SLES_DEV_AUDIO_RECORDER sldevaudiorecorderstr
|
||||
#define SLES_DEV_AUDIO_PLAYER sldevaudioplayerstr
|
||||
static void OPENSLES_DetectDevices( int iscapture )
|
||||
{
|
||||
LOGI( "openSLES_DetectDevices()" );
|
||||
if ( iscapture )
|
||||
addfn( SLES_DEV_AUDIO_RECORDER );
|
||||
else
|
||||
addfn( SLES_DEV_AUDIO_PLAYER );
|
||||
}
|
||||
#endif
|
||||
|
||||
static void OPENSLES_DestroyEngine(void)
|
||||
{
|
||||
LOGI("OPENSLES_DestroyEngine()");
|
||||
|
||||
// destroy output mix object, and invalidate all associated interfaces
|
||||
if (outputMixObject != NULL) {
|
||||
(*outputMixObject)->Destroy(outputMixObject);
|
||||
outputMixObject = NULL;
|
||||
}
|
||||
|
||||
// destroy engine object, and invalidate all associated interfaces
|
||||
if (engineObject != NULL) {
|
||||
(*engineObject)->Destroy(engineObject);
|
||||
engineObject = NULL;
|
||||
engineEngine = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int OPENSLES_CreateEngine(void)
|
||||
{
|
||||
const SLInterfaceID ids[1] = { SL_IID_VOLUME };
|
||||
const SLboolean req[1] = { SL_BOOLEAN_FALSE };
|
||||
SLresult result;
|
||||
|
||||
LOGI("openSLES_CreateEngine()");
|
||||
|
||||
// create engine
|
||||
result = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("slCreateEngine failed: %d", result);
|
||||
goto error;
|
||||
}
|
||||
LOGI("slCreateEngine OK");
|
||||
|
||||
// realize the engine
|
||||
result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("RealizeEngine failed: %d", result);
|
||||
goto error;
|
||||
}
|
||||
LOGI("RealizeEngine OK");
|
||||
|
||||
// get the engine interface, which is needed in order to create other objects
|
||||
result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("EngineGetInterface failed: %d", result);
|
||||
goto error;
|
||||
}
|
||||
LOGI("EngineGetInterface OK");
|
||||
|
||||
// create output mix
|
||||
result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 1, ids, req);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("CreateOutputMix failed: %d", result);
|
||||
goto error;
|
||||
}
|
||||
LOGI("CreateOutputMix OK");
|
||||
|
||||
// realize the output mix
|
||||
result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("RealizeOutputMix failed: %d", result);
|
||||
goto error;
|
||||
}
|
||||
return 1;
|
||||
|
||||
error:
|
||||
OPENSLES_DestroyEngine();
|
||||
return 0;
|
||||
}
|
||||
|
||||
// this callback handler is called every time a buffer finishes recording
|
||||
static void bqRecorderCallback(SLAndroidSimpleBufferQueueItf bq, void *context)
|
||||
{
|
||||
struct SDL_PrivateAudioData *audiodata = (struct SDL_PrivateAudioData *)context;
|
||||
|
||||
LOGV("SLES: Recording Callback");
|
||||
SDL_PostSemaphore(audiodata->playsem);
|
||||
}
|
||||
|
||||
static void OPENSLES_DestroyPCMRecorder(SDL_AudioDevice *device)
|
||||
{
|
||||
struct SDL_PrivateAudioData *audiodata = device->hidden;
|
||||
SLresult result;
|
||||
|
||||
// stop recording
|
||||
if (recorderRecord != NULL) {
|
||||
result = (*recorderRecord)->SetRecordState(recorderRecord, SL_RECORDSTATE_STOPPED);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("SetRecordState stopped: %d", result);
|
||||
}
|
||||
}
|
||||
|
||||
// destroy audio recorder object, and invalidate all associated interfaces
|
||||
if (recorderObject != NULL) {
|
||||
(*recorderObject)->Destroy(recorderObject);
|
||||
recorderObject = NULL;
|
||||
recorderRecord = NULL;
|
||||
recorderBufferQueue = NULL;
|
||||
}
|
||||
|
||||
if (audiodata->playsem) {
|
||||
SDL_DestroySemaphore(audiodata->playsem);
|
||||
audiodata->playsem = NULL;
|
||||
}
|
||||
|
||||
if (audiodata->mixbuff) {
|
||||
SDL_free(audiodata->mixbuff);
|
||||
}
|
||||
}
|
||||
|
||||
static int OPENSLES_CreatePCMRecorder(SDL_AudioDevice *device)
|
||||
{
|
||||
struct SDL_PrivateAudioData *audiodata = device->hidden;
|
||||
SLDataFormat_PCM format_pcm;
|
||||
SLDataLocator_AndroidSimpleBufferQueue loc_bufq;
|
||||
SLDataSink audioSnk;
|
||||
SLDataLocator_IODevice loc_dev;
|
||||
SLDataSource audioSrc;
|
||||
const SLInterfaceID ids[1] = { SL_IID_ANDROIDSIMPLEBUFFERQUEUE };
|
||||
const SLboolean req[1] = { SL_BOOLEAN_TRUE };
|
||||
SLresult result;
|
||||
int i;
|
||||
|
||||
if (!Android_JNI_RequestPermission("android.permission.RECORD_AUDIO")) {
|
||||
LOGE("This app doesn't have RECORD_AUDIO permission");
|
||||
return SDL_SetError("This app doesn't have RECORD_AUDIO permission");
|
||||
}
|
||||
|
||||
// Just go with signed 16-bit audio as it's the most compatible
|
||||
device->spec.format = SDL_AUDIO_S16;
|
||||
device->spec.channels = 1;
|
||||
//device->spec.freq = SL_SAMPLINGRATE_16 / 1000;*/
|
||||
|
||||
// Update the fragment size as size in bytes
|
||||
SDL_UpdatedAudioDeviceFormat(device);
|
||||
|
||||
LOGI("Try to open %u hz %u bit chan %u %s samples %u",
|
||||
device->spec.freq, SDL_AUDIO_BITSIZE(device->spec.format),
|
||||
device->spec.channels, (device->spec.format & 0x1000) ? "BE" : "LE", device->sample_frames);
|
||||
|
||||
// configure audio source
|
||||
loc_dev.locatorType = SL_DATALOCATOR_IODEVICE;
|
||||
loc_dev.deviceType = SL_IODEVICE_AUDIOINPUT;
|
||||
loc_dev.deviceID = SL_DEFAULTDEVICEID_AUDIOINPUT;
|
||||
loc_dev.device = NULL;
|
||||
audioSrc.pLocator = &loc_dev;
|
||||
audioSrc.pFormat = NULL;
|
||||
|
||||
// configure audio sink
|
||||
loc_bufq.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE;
|
||||
loc_bufq.numBuffers = NUM_BUFFERS;
|
||||
|
||||
format_pcm.formatType = SL_DATAFORMAT_PCM;
|
||||
format_pcm.numChannels = device->spec.channels;
|
||||
format_pcm.samplesPerSec = device->spec.freq * 1000; // / kilo Hz to milli Hz
|
||||
format_pcm.bitsPerSample = SDL_AUDIO_BITSIZE(device->spec.format);
|
||||
format_pcm.containerSize = SDL_AUDIO_BITSIZE(device->spec.format);
|
||||
format_pcm.endianness = SL_BYTEORDER_LITTLEENDIAN;
|
||||
format_pcm.channelMask = SL_SPEAKER_FRONT_CENTER;
|
||||
|
||||
audioSnk.pLocator = &loc_bufq;
|
||||
audioSnk.pFormat = &format_pcm;
|
||||
|
||||
// create audio recorder
|
||||
// (requires the RECORD_AUDIO permission)
|
||||
result = (*engineEngine)->CreateAudioRecorder(engineEngine, &recorderObject, &audioSrc, &audioSnk, 1, ids, req);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("CreateAudioRecorder failed: %d", result);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
// realize the recorder
|
||||
result = (*recorderObject)->Realize(recorderObject, SL_BOOLEAN_FALSE);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("RealizeAudioPlayer failed: %d", result);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
// get the record interface
|
||||
result = (*recorderObject)->GetInterface(recorderObject, SL_IID_RECORD, &recorderRecord);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("SL_IID_RECORD interface get failed: %d", result);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
// get the buffer queue interface
|
||||
result = (*recorderObject)->GetInterface(recorderObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &recorderBufferQueue);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("SL_IID_BUFFERQUEUE interface get failed: %d", result);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
// register callback on the buffer queue
|
||||
// context is '(SDL_PrivateAudioData *)device->hidden'
|
||||
result = (*recorderBufferQueue)->RegisterCallback(recorderBufferQueue, bqRecorderCallback, device->hidden);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("RegisterCallback failed: %d", result);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
// Create the audio buffer semaphore
|
||||
audiodata->playsem = SDL_CreateSemaphore(0);
|
||||
if (!audiodata->playsem) {
|
||||
LOGE("cannot create Semaphore!");
|
||||
goto failed;
|
||||
}
|
||||
|
||||
// Create the sound buffers
|
||||
audiodata->mixbuff = (Uint8 *)SDL_malloc(NUM_BUFFERS * device->buffer_size);
|
||||
if (audiodata->mixbuff == NULL) {
|
||||
LOGE("mixbuffer allocate - out of memory");
|
||||
goto failed;
|
||||
}
|
||||
|
||||
for (i = 0; i < NUM_BUFFERS; i++) {
|
||||
audiodata->pmixbuff[i] = audiodata->mixbuff + i * device->buffer_size;
|
||||
}
|
||||
|
||||
// in case already recording, stop recording and clear buffer queue
|
||||
result = (*recorderRecord)->SetRecordState(recorderRecord, SL_RECORDSTATE_STOPPED);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("Record set state failed: %d", result);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
// enqueue empty buffers to be filled by the recorder
|
||||
for (i = 0; i < NUM_BUFFERS; i++) {
|
||||
result = (*recorderBufferQueue)->Enqueue(recorderBufferQueue, audiodata->pmixbuff[i], device->buffer_size);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("Record enqueue buffers failed: %d", result);
|
||||
goto failed;
|
||||
}
|
||||
}
|
||||
|
||||
// start recording
|
||||
result = (*recorderRecord)->SetRecordState(recorderRecord, SL_RECORDSTATE_RECORDING);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("Record set state failed: %d", result);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
failed:
|
||||
return SDL_SetError("Open device failed!");
|
||||
}
|
||||
|
||||
// this callback handler is called every time a buffer finishes playing
|
||||
static void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void *context)
|
||||
{
|
||||
struct SDL_PrivateAudioData *audiodata = (struct SDL_PrivateAudioData *)context;
|
||||
|
||||
LOGV("SLES: Playback Callback");
|
||||
SDL_PostSemaphore(audiodata->playsem);
|
||||
}
|
||||
|
||||
static void OPENSLES_DestroyPCMPlayer(SDL_AudioDevice *device)
|
||||
{
|
||||
struct SDL_PrivateAudioData *audiodata = device->hidden;
|
||||
|
||||
// set the player's state to 'stopped'
|
||||
if (bqPlayerPlay != NULL) {
|
||||
const SLresult result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_STOPPED);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("SetPlayState stopped failed: %d", result);
|
||||
}
|
||||
}
|
||||
|
||||
// destroy buffer queue audio player object, and invalidate all associated interfaces
|
||||
if (bqPlayerObject != NULL) {
|
||||
(*bqPlayerObject)->Destroy(bqPlayerObject);
|
||||
|
||||
bqPlayerObject = NULL;
|
||||
bqPlayerPlay = NULL;
|
||||
bqPlayerBufferQueue = NULL;
|
||||
}
|
||||
|
||||
if (audiodata->playsem) {
|
||||
SDL_DestroySemaphore(audiodata->playsem);
|
||||
audiodata->playsem = NULL;
|
||||
}
|
||||
|
||||
if (audiodata->mixbuff) {
|
||||
SDL_free(audiodata->mixbuff);
|
||||
}
|
||||
}
|
||||
|
||||
static int OPENSLES_CreatePCMPlayer(SDL_AudioDevice *device)
|
||||
{
|
||||
/* If we want to add floating point audio support (requires API level 21)
|
||||
it can be done as described here:
|
||||
https://developer.android.com/ndk/guides/audio/opensl/android-extensions.html#floating-point
|
||||
*/
|
||||
if (SDL_GetAndroidSDKVersion() >= 21) {
|
||||
const SDL_AudioFormat *closefmts = SDL_ClosestAudioFormats(device->spec.format);
|
||||
SDL_AudioFormat test_format;
|
||||
while ((test_format = *(closefmts++)) != 0) {
|
||||
if (SDL_AUDIO_ISSIGNED(test_format)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!test_format) {
|
||||
// Didn't find a compatible format :
|
||||
LOGI("No compatible audio format, using signed 16-bit audio");
|
||||
test_format = SDL_AUDIO_S16;
|
||||
}
|
||||
device->spec.format = test_format;
|
||||
} else {
|
||||
// Just go with signed 16-bit audio as it's the most compatible
|
||||
device->spec.format = SDL_AUDIO_S16;
|
||||
}
|
||||
|
||||
// Update the fragment size as size in bytes
|
||||
SDL_UpdatedAudioDeviceFormat(device);
|
||||
|
||||
LOGI("Try to open %u hz %s %u bit chan %u %s samples %u",
|
||||
device->spec.freq, SDL_AUDIO_ISFLOAT(device->spec.format) ? "float" : "pcm", SDL_AUDIO_BITSIZE(device->spec.format),
|
||||
device->spec.channels, (device->spec.format & 0x1000) ? "BE" : "LE", device->sample_frames);
|
||||
|
||||
// configure audio source
|
||||
SLDataLocator_AndroidSimpleBufferQueue loc_bufq;
|
||||
loc_bufq.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE;
|
||||
loc_bufq.numBuffers = NUM_BUFFERS;
|
||||
|
||||
SLDataFormat_PCM format_pcm;
|
||||
format_pcm.formatType = SL_DATAFORMAT_PCM;
|
||||
format_pcm.numChannels = device->spec.channels;
|
||||
format_pcm.samplesPerSec = device->spec.freq * 1000; // / kilo Hz to milli Hz
|
||||
format_pcm.bitsPerSample = SDL_AUDIO_BITSIZE(device->spec.format);
|
||||
format_pcm.containerSize = SDL_AUDIO_BITSIZE(device->spec.format);
|
||||
|
||||
if (SDL_AUDIO_ISBIGENDIAN(device->spec.format)) {
|
||||
format_pcm.endianness = SL_BYTEORDER_BIGENDIAN;
|
||||
} else {
|
||||
format_pcm.endianness = SL_BYTEORDER_LITTLEENDIAN;
|
||||
}
|
||||
|
||||
switch (device->spec.channels) {
|
||||
case 1:
|
||||
format_pcm.channelMask = SL_SPEAKER_FRONT_LEFT;
|
||||
break;
|
||||
case 2:
|
||||
format_pcm.channelMask = SL_ANDROID_SPEAKER_STEREO;
|
||||
break;
|
||||
case 3:
|
||||
format_pcm.channelMask = SL_ANDROID_SPEAKER_STEREO | SL_SPEAKER_FRONT_CENTER;
|
||||
break;
|
||||
case 4:
|
||||
format_pcm.channelMask = SL_ANDROID_SPEAKER_QUAD;
|
||||
break;
|
||||
case 5:
|
||||
format_pcm.channelMask = SL_ANDROID_SPEAKER_QUAD | SL_SPEAKER_FRONT_CENTER;
|
||||
break;
|
||||
case 6:
|
||||
format_pcm.channelMask = SL_ANDROID_SPEAKER_5DOT1;
|
||||
break;
|
||||
case 7:
|
||||
format_pcm.channelMask = SL_ANDROID_SPEAKER_5DOT1 | SL_SPEAKER_BACK_CENTER;
|
||||
break;
|
||||
case 8:
|
||||
format_pcm.channelMask = SL_ANDROID_SPEAKER_7DOT1;
|
||||
break;
|
||||
default:
|
||||
// Unknown number of channels, fall back to stereo
|
||||
device->spec.channels = 2;
|
||||
format_pcm.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
|
||||
break;
|
||||
}
|
||||
|
||||
SLDataSink audioSnk;
|
||||
SLDataSource audioSrc;
|
||||
audioSrc.pFormat = (void *)&format_pcm;
|
||||
|
||||
SLAndroidDataFormat_PCM_EX format_pcm_ex;
|
||||
if (SDL_AUDIO_ISFLOAT(device->spec.format)) {
|
||||
// Copy all setup into PCM EX structure
|
||||
format_pcm_ex.formatType = SL_ANDROID_DATAFORMAT_PCM_EX;
|
||||
format_pcm_ex.endianness = format_pcm.endianness;
|
||||
format_pcm_ex.channelMask = format_pcm.channelMask;
|
||||
format_pcm_ex.numChannels = format_pcm.numChannels;
|
||||
format_pcm_ex.sampleRate = format_pcm.samplesPerSec;
|
||||
format_pcm_ex.bitsPerSample = format_pcm.bitsPerSample;
|
||||
format_pcm_ex.containerSize = format_pcm.containerSize;
|
||||
format_pcm_ex.representation = SL_ANDROID_PCM_REPRESENTATION_FLOAT;
|
||||
audioSrc.pFormat = (void *)&format_pcm_ex;
|
||||
}
|
||||
|
||||
audioSrc.pLocator = &loc_bufq;
|
||||
|
||||
// configure audio sink
|
||||
SLDataLocator_OutputMix loc_outmix;
|
||||
loc_outmix.locatorType = SL_DATALOCATOR_OUTPUTMIX;
|
||||
loc_outmix.outputMix = outputMixObject;
|
||||
audioSnk.pLocator = &loc_outmix;
|
||||
audioSnk.pFormat = NULL;
|
||||
|
||||
// create audio player
|
||||
const SLInterfaceID ids[2] = { SL_IID_ANDROIDSIMPLEBUFFERQUEUE, SL_IID_VOLUME };
|
||||
const SLboolean req[2] = { SL_BOOLEAN_TRUE, SL_BOOLEAN_FALSE };
|
||||
SLresult result;
|
||||
result = (*engineEngine)->CreateAudioPlayer(engineEngine, &bqPlayerObject, &audioSrc, &audioSnk, 2, ids, req);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("CreateAudioPlayer failed: %d", result);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
// realize the player
|
||||
result = (*bqPlayerObject)->Realize(bqPlayerObject, SL_BOOLEAN_FALSE);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("RealizeAudioPlayer failed: %d", result);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
// get the play interface
|
||||
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_PLAY, &bqPlayerPlay);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("SL_IID_PLAY interface get failed: %d", result);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
// get the buffer queue interface
|
||||
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &bqPlayerBufferQueue);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("SL_IID_BUFFERQUEUE interface get failed: %d", result);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
// register callback on the buffer queue
|
||||
// context is '(SDL_PrivateAudioData *)device->hidden'
|
||||
result = (*bqPlayerBufferQueue)->RegisterCallback(bqPlayerBufferQueue, bqPlayerCallback, device->hidden);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("RegisterCallback failed: %d", result);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
#if 0
|
||||
// get the volume interface
|
||||
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_VOLUME, &bqPlayerVolume);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("SL_IID_VOLUME interface get failed: %d", result);
|
||||
// goto failed;
|
||||
}
|
||||
#endif
|
||||
|
||||
struct SDL_PrivateAudioData *audiodata = device->hidden;
|
||||
|
||||
// Create the audio buffer semaphore
|
||||
audiodata->playsem = SDL_CreateSemaphore(NUM_BUFFERS - 1);
|
||||
if (!audiodata->playsem) {
|
||||
LOGE("cannot create Semaphore!");
|
||||
goto failed;
|
||||
}
|
||||
|
||||
// Create the sound buffers
|
||||
audiodata->mixbuff = (Uint8 *)SDL_malloc(NUM_BUFFERS * device->buffer_size);
|
||||
if (audiodata->mixbuff == NULL) {
|
||||
LOGE("mixbuffer allocate - out of memory");
|
||||
goto failed;
|
||||
}
|
||||
|
||||
for (int i = 0; i < NUM_BUFFERS; i++) {
|
||||
audiodata->pmixbuff[i] = audiodata->mixbuff + i * device->buffer_size;
|
||||
}
|
||||
|
||||
// set the player's state to playing
|
||||
result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PLAYING);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("Play set state failed: %d", result);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
failed:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int OPENSLES_OpenDevice(SDL_AudioDevice *device)
|
||||
{
|
||||
device->hidden = (struct SDL_PrivateAudioData *)SDL_calloc(1, sizeof(*device->hidden));
|
||||
if (device->hidden == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
|
||||
if (device->iscapture) {
|
||||
LOGI("OPENSLES_OpenDevice() for capture");
|
||||
return OPENSLES_CreatePCMRecorder(device);
|
||||
} else {
|
||||
int ret;
|
||||
LOGI("OPENSLES_OpenDevice() for playing");
|
||||
ret = OPENSLES_CreatePCMPlayer(device);
|
||||
if (ret < 0) {
|
||||
// Another attempt to open the device with a lower frequency
|
||||
if (device->spec.freq > 48000) {
|
||||
OPENSLES_DestroyPCMPlayer(device);
|
||||
device->spec.freq = 48000;
|
||||
ret = OPENSLES_CreatePCMPlayer(device);
|
||||
}
|
||||
}
|
||||
|
||||
if (ret != 0) {
|
||||
return SDL_SetError("Open device failed!");
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int OPENSLES_WaitDevice(SDL_AudioDevice *device)
|
||||
{
|
||||
struct SDL_PrivateAudioData *audiodata = device->hidden;
|
||||
|
||||
LOGV("OPENSLES_WaitDevice()");
|
||||
|
||||
// Wait for an audio chunk to finish
|
||||
return SDL_WaitSemaphore(audiodata->playsem);
|
||||
}
|
||||
|
||||
static int OPENSLES_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
|
||||
{
|
||||
struct SDL_PrivateAudioData *audiodata = device->hidden;
|
||||
|
||||
LOGV("======OPENSLES_PlayDevice()======");
|
||||
|
||||
// Queue it up
|
||||
const SLresult result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, buffer, buflen);
|
||||
|
||||
audiodata->next_buffer++;
|
||||
if (audiodata->next_buffer >= NUM_BUFFERS) {
|
||||
audiodata->next_buffer = 0;
|
||||
}
|
||||
|
||||
// If Enqueue fails, callback won't be called.
|
||||
// Post the semaphore, not to run out of buffer
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
SDL_PostSemaphore(audiodata->playsem);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// n playn sem
|
||||
// getbuf 0 - 1
|
||||
// fill buff 0 - 1
|
||||
// play 0 - 0 1
|
||||
// wait 1 0 0
|
||||
// getbuf 1 0 0
|
||||
// fill buff 1 0 0
|
||||
// play 0 0 0
|
||||
// wait
|
||||
//
|
||||
// okay..
|
||||
|
||||
static Uint8 *OPENSLES_GetDeviceBuf(SDL_AudioDevice *device, int *bufsize)
|
||||
{
|
||||
struct SDL_PrivateAudioData *audiodata = device->hidden;
|
||||
|
||||
LOGV("OPENSLES_GetDeviceBuf()");
|
||||
return audiodata->pmixbuff[audiodata->next_buffer];
|
||||
}
|
||||
|
||||
static int OPENSLES_CaptureFromDevice(SDL_AudioDevice *device, void *buffer, int buflen)
|
||||
{
|
||||
struct SDL_PrivateAudioData *audiodata = device->hidden;
|
||||
|
||||
// Copy it to the output buffer
|
||||
SDL_assert(buflen == device->buffer_size);
|
||||
SDL_memcpy(buffer, audiodata->pmixbuff[audiodata->next_buffer], device->buffer_size);
|
||||
|
||||
// Re-enqueue the buffer
|
||||
const SLresult result = (*recorderBufferQueue)->Enqueue(recorderBufferQueue, audiodata->pmixbuff[audiodata->next_buffer], device->buffer_size);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("Record enqueue buffers failed: %d", result);
|
||||
return -1;
|
||||
}
|
||||
|
||||
audiodata->next_buffer++;
|
||||
if (audiodata->next_buffer >= NUM_BUFFERS) {
|
||||
audiodata->next_buffer = 0;
|
||||
}
|
||||
|
||||
return device->buffer_size;
|
||||
}
|
||||
|
||||
static void OPENSLES_CloseDevice(SDL_AudioDevice *device)
|
||||
{
|
||||
// struct SDL_PrivateAudioData *audiodata = device->hidden;
|
||||
if (device->hidden) {
|
||||
if (device->iscapture) {
|
||||
LOGI("OPENSLES_CloseDevice() for capture");
|
||||
OPENSLES_DestroyPCMRecorder(device);
|
||||
} else {
|
||||
LOGI("OPENSLES_CloseDevice() for playing");
|
||||
OPENSLES_DestroyPCMPlayer(device);
|
||||
}
|
||||
|
||||
SDL_free(device->hidden);
|
||||
device->hidden = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static SDL_bool OPENSLES_Init(SDL_AudioDriverImpl *impl)
|
||||
{
|
||||
LOGI("OPENSLES_Init() called");
|
||||
|
||||
if (!OPENSLES_CreateEngine()) {
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
LOGI("OPENSLES_Init() - set pointers");
|
||||
|
||||
// Set the function pointers
|
||||
// impl->DetectDevices = OPENSLES_DetectDevices;
|
||||
impl->ThreadInit = Android_AudioThreadInit;
|
||||
impl->OpenDevice = OPENSLES_OpenDevice;
|
||||
impl->WaitDevice = OPENSLES_WaitDevice;
|
||||
impl->PlayDevice = OPENSLES_PlayDevice;
|
||||
impl->GetDeviceBuf = OPENSLES_GetDeviceBuf;
|
||||
impl->WaitCaptureDevice = OPENSLES_WaitDevice;
|
||||
impl->CaptureFromDevice = OPENSLES_CaptureFromDevice;
|
||||
impl->CloseDevice = OPENSLES_CloseDevice;
|
||||
impl->Deinitialize = OPENSLES_DestroyEngine;
|
||||
|
||||
// and the capabilities
|
||||
impl->HasCaptureSupport = SDL_TRUE;
|
||||
impl->OnlyHasDefaultOutputDevice = SDL_TRUE;
|
||||
impl->OnlyHasDefaultCaptureDevice = SDL_TRUE;
|
||||
|
||||
LOGI("OPENSLES_Init() - success");
|
||||
|
||||
// this audio target is available.
|
||||
return SDL_TRUE;
|
||||
}
|
||||
|
||||
AudioBootStrap OPENSLES_bootstrap = {
|
||||
"openslES", "OpenSL ES audio driver", OPENSLES_Init, SDL_FALSE
|
||||
};
|
||||
|
||||
void OPENSLES_ResumeDevices(void)
|
||||
{
|
||||
if (bqPlayerPlay != NULL) {
|
||||
// set the player's state to 'playing'
|
||||
SLresult result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PLAYING);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("OPENSLES_ResumeDevices failed: %d", result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OPENSLES_PauseDevices(void)
|
||||
{
|
||||
if (bqPlayerPlay != NULL) {
|
||||
// set the player's state to 'paused'
|
||||
SLresult result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PAUSED);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("OPENSLES_PauseDevices failed: %d", result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // SDL_AUDIO_DRIVER_OPENSLES
|
||||
38
vendor/sdl-3.0.0/src/audio/openslES/SDL_openslES.h
vendored
Normal file
38
vendor/sdl-3.0.0/src/audio/openslES/SDL_openslES.h
vendored
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifndef SDL_openslesaudio_h_
|
||||
#define SDL_openslesaudio_h_
|
||||
|
||||
#ifdef SDL_AUDIO_DRIVER_OPENSLES
|
||||
|
||||
void OPENSLES_ResumeDevices(void);
|
||||
void OPENSLES_PauseDevices(void);
|
||||
|
||||
#else
|
||||
|
||||
static void OPENSLES_ResumeDevices(void) {}
|
||||
static void OPENSLES_PauseDevices(void) {}
|
||||
|
||||
#endif
|
||||
|
||||
#endif // SDL_openslesaudio_h_
|
||||
1286
vendor/sdl-3.0.0/src/audio/pipewire/SDL_pipewire.c
vendored
Normal file
1286
vendor/sdl-3.0.0/src/audio/pipewire/SDL_pipewire.c
vendored
Normal file
File diff suppressed because it is too large
Load diff
43
vendor/sdl-3.0.0/src/audio/pipewire/SDL_pipewire.h
vendored
Normal file
43
vendor/sdl-3.0.0/src/audio/pipewire/SDL_pipewire.h
vendored
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifndef SDL_pipewire_h_
|
||||
#define SDL_pipewire_h_
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
#include <pipewire/pipewire.h>
|
||||
|
||||
struct SDL_PrivateAudioData
|
||||
{
|
||||
struct pw_thread_loop *loop;
|
||||
struct pw_stream *stream;
|
||||
struct pw_context *context;
|
||||
|
||||
Sint32 stride; // Bytes-per-frame
|
||||
int stream_init_status;
|
||||
|
||||
// Set in GetDeviceBuf, filled in AudioThreadIterate, queued in PlayDevice
|
||||
struct pw_buffer *pw_buf;
|
||||
};
|
||||
|
||||
#endif // SDL_pipewire_h_
|
||||
159
vendor/sdl-3.0.0/src/audio/ps2/SDL_ps2audio.c
vendored
Normal file
159
vendor/sdl-3.0.0/src/audio/ps2/SDL_ps2audio.c
vendored
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
#include "SDL_ps2audio.h"
|
||||
|
||||
#include <kernel.h>
|
||||
#include <audsrv.h>
|
||||
#include <ps2_audio_driver.h>
|
||||
|
||||
static int PS2AUDIO_OpenDevice(SDL_AudioDevice *device)
|
||||
{
|
||||
device->hidden = (struct SDL_PrivateAudioData *) SDL_calloc(1, sizeof(*device->hidden));
|
||||
if (device->hidden == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
|
||||
// These are the native supported audio PS2 configs
|
||||
switch (device->spec.freq) {
|
||||
case 11025:
|
||||
case 12000:
|
||||
case 22050:
|
||||
case 24000:
|
||||
case 32000:
|
||||
case 44100:
|
||||
case 48000:
|
||||
break; // acceptable value, keep it
|
||||
default:
|
||||
device->spec.freq = 48000;
|
||||
break;
|
||||
}
|
||||
|
||||
device->sample_frames = 512;
|
||||
device->spec.channels = device->spec.channels == 1 ? 1 : 2;
|
||||
device->spec.format = device->spec.format == SDL_AUDIO_S8 ? SDL_AUDIO_S8 : SDL_AUDIO_S16;
|
||||
|
||||
struct audsrv_fmt_t format;
|
||||
format.bits = device->spec.format == SDL_AUDIO_S8 ? 8 : 16;
|
||||
format.freq = device->spec.freq;
|
||||
format.channels = device->spec.channels;
|
||||
|
||||
device->hidden->channel = audsrv_set_format(&format);
|
||||
audsrv_set_volume(MAX_VOLUME);
|
||||
|
||||
if (device->hidden->channel < 0) {
|
||||
return SDL_SetError("Couldn't reserve hardware channel");
|
||||
}
|
||||
|
||||
// Update the fragment size as size in bytes.
|
||||
SDL_UpdatedAudioDeviceFormat(device);
|
||||
|
||||
/* Allocate the mixing buffer. Its size and starting address must
|
||||
be a multiple of 64 bytes. Our sample count is already a multiple of
|
||||
64, so spec->size should be a multiple of 64 as well. */
|
||||
const int mixlen = device->buffer_size * NUM_BUFFERS;
|
||||
device->hidden->rawbuf = (Uint8 *)SDL_aligned_alloc(64, mixlen);
|
||||
if (device->hidden->rawbuf == NULL) {
|
||||
return SDL_SetError("Couldn't allocate mixing buffer");
|
||||
}
|
||||
|
||||
SDL_memset(device->hidden->rawbuf, device->silence_value, mixlen);
|
||||
for (int i = 0; i < NUM_BUFFERS; i++) {
|
||||
device->hidden->mixbufs[i] = &device->hidden->rawbuf[i * device->buffer_size];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int PS2AUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
|
||||
{
|
||||
// this returns number of bytes accepted or a negative error. We assume anything other than buflen is a fatal error.
|
||||
return (audsrv_play_audio((char *)buffer, buflen) != buflen) ? -1 : 0;
|
||||
}
|
||||
|
||||
static int PS2AUDIO_WaitDevice(SDL_AudioDevice *device)
|
||||
{
|
||||
audsrv_wait_audio(device->buffer_size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static Uint8 *PS2AUDIO_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
|
||||
{
|
||||
Uint8 *buffer = device->hidden->mixbufs[device->hidden->next_buffer];
|
||||
device->hidden->next_buffer = (device->hidden->next_buffer + 1) % NUM_BUFFERS;
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static void PS2AUDIO_CloseDevice(SDL_AudioDevice *device)
|
||||
{
|
||||
if (device->hidden) {
|
||||
if (device->hidden->channel >= 0) {
|
||||
audsrv_stop_audio();
|
||||
device->hidden->channel = -1;
|
||||
}
|
||||
|
||||
if (device->hidden->rawbuf != NULL) {
|
||||
SDL_aligned_free(device->hidden->rawbuf);
|
||||
device->hidden->rawbuf = NULL;
|
||||
}
|
||||
SDL_free(device->hidden);
|
||||
device->hidden = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void PS2AUDIO_ThreadInit(SDL_AudioDevice *device)
|
||||
{
|
||||
/* Increase the priority of this audio thread by 1 to put it
|
||||
ahead of other SDL threads. */
|
||||
const int32_t thid = GetThreadId();
|
||||
ee_thread_status_t status;
|
||||
if (ReferThreadStatus(thid, &status) == 0) {
|
||||
ChangeThreadPriority(thid, status.current_priority - 1);
|
||||
}
|
||||
}
|
||||
|
||||
static void PS2AUDIO_Deinitialize(void)
|
||||
{
|
||||
deinit_audio_driver();
|
||||
}
|
||||
|
||||
static SDL_bool PS2AUDIO_Init(SDL_AudioDriverImpl *impl)
|
||||
{
|
||||
if (init_audio_driver() < 0) {
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
impl->OpenDevice = PS2AUDIO_OpenDevice;
|
||||
impl->PlayDevice = PS2AUDIO_PlayDevice;
|
||||
impl->WaitDevice = PS2AUDIO_WaitDevice;
|
||||
impl->GetDeviceBuf = PS2AUDIO_GetDeviceBuf;
|
||||
impl->CloseDevice = PS2AUDIO_CloseDevice;
|
||||
impl->ThreadInit = PS2AUDIO_ThreadInit;
|
||||
impl->Deinitialize = PS2AUDIO_Deinitialize;
|
||||
impl->OnlyHasDefaultOutputDevice = SDL_TRUE;
|
||||
return SDL_TRUE; // this audio target is available.
|
||||
}
|
||||
|
||||
AudioBootStrap PS2AUDIO_bootstrap = {
|
||||
"ps2", "PS2 audio driver", PS2AUDIO_Init, SDL_FALSE
|
||||
};
|
||||
42
vendor/sdl-3.0.0/src/audio/ps2/SDL_ps2audio.h
vendored
Normal file
42
vendor/sdl-3.0.0/src/audio/ps2/SDL_ps2audio.h
vendored
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifndef SDL_ps2audio_h_
|
||||
#define SDL_ps2audio_h_
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
|
||||
#define NUM_BUFFERS 2
|
||||
|
||||
struct SDL_PrivateAudioData
|
||||
{
|
||||
// The hardware output channel.
|
||||
int channel;
|
||||
// The raw allocated mixing buffer.
|
||||
Uint8 *rawbuf;
|
||||
// Individual mixing buffers.
|
||||
Uint8 *mixbufs[NUM_BUFFERS];
|
||||
// Index of the next available mixing buffer.
|
||||
int next_buffer;
|
||||
};
|
||||
|
||||
#endif // SDL_ps2audio_h_
|
||||
183
vendor/sdl-3.0.0/src/audio/psp/SDL_pspaudio.c
vendored
Normal file
183
vendor/sdl-3.0.0/src/audio/psp/SDL_pspaudio.c
vendored
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifdef SDL_AUDIO_DRIVER_PSP
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "../SDL_audiodev_c.h"
|
||||
#include "../SDL_sysaudio.h"
|
||||
#include "SDL_pspaudio.h"
|
||||
|
||||
#include <pspaudio.h>
|
||||
#include <pspthreadman.h>
|
||||
|
||||
static inline SDL_bool isBasicAudioConfig(const SDL_AudioSpec *spec)
|
||||
{
|
||||
return spec->freq == 44100;
|
||||
}
|
||||
|
||||
static int PSPAUDIO_OpenDevice(SDL_AudioDevice *device)
|
||||
{
|
||||
device->hidden = (struct SDL_PrivateAudioData *) SDL_calloc(1, sizeof(*device->hidden));
|
||||
if (device->hidden == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
|
||||
// device only natively supports S16LSB
|
||||
device->spec.format = SDL_AUDIO_S16LE;
|
||||
|
||||
/* PSP has some limitations with the Audio. It fully supports 44.1KHz (Mono & Stereo),
|
||||
however with frequencies different than 44.1KHz, it just supports Stereo,
|
||||
so a resampler must be done for these scenarios */
|
||||
if (isBasicAudioConfig(&device->spec)) {
|
||||
// The sample count must be a multiple of 64.
|
||||
device->sample_frames = PSP_AUDIO_SAMPLE_ALIGN(device->sample_frames);
|
||||
// The number of channels (1 or 2).
|
||||
device->spec.channels = device->spec.channels == 1 ? 1 : 2;
|
||||
const int format = (device->spec.channels == 1) ? PSP_AUDIO_FORMAT_MONO : PSP_AUDIO_FORMAT_STEREO;
|
||||
device->hidden->channel = sceAudioChReserve(PSP_AUDIO_NEXT_CHANNEL, device->sample_frames, format);
|
||||
} else {
|
||||
// 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11050, 8000
|
||||
switch (device->spec.freq) {
|
||||
case 8000:
|
||||
case 11025:
|
||||
case 12000:
|
||||
case 16000:
|
||||
case 22050:
|
||||
case 24000:
|
||||
case 32000:
|
||||
case 44100:
|
||||
case 48000:
|
||||
break; // acceptable, keep it
|
||||
default:
|
||||
device->spec.freq = 48000;
|
||||
break;
|
||||
}
|
||||
// The number of samples to output in one output call (min 17, max 4111).
|
||||
device->sample_frames = device->sample_frames < 17 ? 17 : (device->sample_frames > 4111 ? 4111 : device->sample_frames);
|
||||
device->spec.channels = 2; // we're forcing the hardware to stereo.
|
||||
device->hidden->channel = sceAudioSRCChReserve(device->sample_frames, device->spec.freq, 2);
|
||||
}
|
||||
|
||||
if (device->hidden->channel < 0) {
|
||||
return SDL_SetError("Couldn't reserve hardware channel");
|
||||
}
|
||||
|
||||
// Update the fragment size as size in bytes.
|
||||
SDL_UpdatedAudioDeviceFormat(device);
|
||||
|
||||
/* Allocate the mixing buffer. Its size and starting address must
|
||||
be a multiple of 64 bytes. Our sample count is already a multiple of
|
||||
64, so spec->size should be a multiple of 64 as well. */
|
||||
const int mixlen = device->buffer_size * NUM_BUFFERS;
|
||||
device->hidden->rawbuf = (Uint8 *)SDL_aligned_alloc(64, mixlen);
|
||||
if (device->hidden->rawbuf == NULL) {
|
||||
return SDL_SetError("Couldn't allocate mixing buffer");
|
||||
}
|
||||
|
||||
SDL_memset(device->hidden->rawbuf, device->silence_value, mixlen);
|
||||
for (int i = 0; i < NUM_BUFFERS; i++) {
|
||||
device->hidden->mixbufs[i] = &device->hidden->rawbuf[i * device->buffer_size];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int PSPAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
|
||||
{
|
||||
int rc;
|
||||
if (!isBasicAudioConfig(&device->spec)) {
|
||||
SDL_assert(device->spec.channels == 2);
|
||||
rc = sceAudioSRCOutputBlocking(PSP_AUDIO_VOLUME_MAX, (void *) buffer);
|
||||
} else {
|
||||
rc = sceAudioOutputPannedBlocking(device->hidden->channel, PSP_AUDIO_VOLUME_MAX, PSP_AUDIO_VOLUME_MAX, (void *) buffer);
|
||||
}
|
||||
return (rc == 0) ? 0 : -1;
|
||||
}
|
||||
|
||||
static int PSPAUDIO_WaitDevice(SDL_AudioDevice *device)
|
||||
{
|
||||
return 0; // Because we block when sending audio, there's no need for this function to do anything.
|
||||
}
|
||||
|
||||
static Uint8 *PSPAUDIO_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
|
||||
{
|
||||
Uint8 *buffer = device->hidden->mixbufs[device->hidden->next_buffer];
|
||||
device->hidden->next_buffer = (device->hidden->next_buffer + 1) % NUM_BUFFERS;
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static void PSPAUDIO_CloseDevice(SDL_AudioDevice *device)
|
||||
{
|
||||
if (device->hidden) {
|
||||
if (device->hidden->channel >= 0) {
|
||||
if (!isBasicAudioConfig(&device->spec)) {
|
||||
sceAudioSRCChRelease();
|
||||
} else {
|
||||
sceAudioChRelease(device->hidden->channel);
|
||||
}
|
||||
device->hidden->channel = -1;
|
||||
}
|
||||
|
||||
if (device->hidden->rawbuf != NULL) {
|
||||
SDL_aligned_free(device->hidden->rawbuf);
|
||||
device->hidden->rawbuf = NULL;
|
||||
}
|
||||
SDL_free(device->hidden);
|
||||
device->hidden = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void PSPAUDIO_ThreadInit(SDL_AudioDevice *device)
|
||||
{
|
||||
/* Increase the priority of this audio thread by 1 to put it
|
||||
ahead of other SDL threads. */
|
||||
const SceUID thid = sceKernelGetThreadId();
|
||||
SceKernelThreadInfo status;
|
||||
status.size = sizeof(SceKernelThreadInfo);
|
||||
if (sceKernelReferThreadStatus(thid, &status) == 0) {
|
||||
sceKernelChangeThreadPriority(thid, status.currentPriority - 1);
|
||||
}
|
||||
}
|
||||
|
||||
static SDL_bool PSPAUDIO_Init(SDL_AudioDriverImpl *impl)
|
||||
{
|
||||
impl->OpenDevice = PSPAUDIO_OpenDevice;
|
||||
impl->PlayDevice = PSPAUDIO_PlayDevice;
|
||||
impl->WaitDevice = PSPAUDIO_WaitDevice;
|
||||
impl->GetDeviceBuf = PSPAUDIO_GetDeviceBuf;
|
||||
impl->CloseDevice = PSPAUDIO_CloseDevice;
|
||||
impl->ThreadInit = PSPAUDIO_ThreadInit;
|
||||
impl->OnlyHasDefaultOutputDevice = SDL_TRUE;
|
||||
//impl->HasCaptureSupport = SDL_TRUE;
|
||||
//impl->OnlyHasDefaultCaptureDevice = SDL_TRUE;
|
||||
return SDL_TRUE;
|
||||
}
|
||||
|
||||
AudioBootStrap PSPAUDIO_bootstrap = {
|
||||
"psp", "PSP audio driver", PSPAUDIO_Init, SDL_FALSE
|
||||
};
|
||||
|
||||
#endif // SDL_AUDIO_DRIVER_PSP
|
||||
41
vendor/sdl-3.0.0/src/audio/psp/SDL_pspaudio.h
vendored
Normal file
41
vendor/sdl-3.0.0/src/audio/psp/SDL_pspaudio.h
vendored
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
#ifndef SDL_pspaudio_h_
|
||||
#define SDL_pspaudio_h_
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
|
||||
#define NUM_BUFFERS 2
|
||||
|
||||
struct SDL_PrivateAudioData
|
||||
{
|
||||
// The hardware output channel.
|
||||
int channel;
|
||||
// The raw allocated mixing buffer.
|
||||
Uint8 *rawbuf;
|
||||
// Individual mixing buffers.
|
||||
Uint8 *mixbufs[NUM_BUFFERS];
|
||||
// Index of the next available mixing buffer.
|
||||
int next_buffer;
|
||||
};
|
||||
|
||||
#endif // SDL_pspaudio_h_
|
||||
1032
vendor/sdl-3.0.0/src/audio/pulseaudio/SDL_pulseaudio.c
vendored
Normal file
1032
vendor/sdl-3.0.0/src/audio/pulseaudio/SDL_pulseaudio.c
vendored
Normal file
File diff suppressed because it is too large
Load diff
44
vendor/sdl-3.0.0/src/audio/pulseaudio/SDL_pulseaudio.h
vendored
Normal file
44
vendor/sdl-3.0.0/src/audio/pulseaudio/SDL_pulseaudio.h
vendored
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifndef SDL_pulseaudio_h_
|
||||
#define SDL_pulseaudio_h_
|
||||
|
||||
#include <pulse/pulseaudio.h>
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
|
||||
struct SDL_PrivateAudioData
|
||||
{
|
||||
// pulseaudio structures
|
||||
pa_stream *stream;
|
||||
|
||||
// Raw mixing buffer
|
||||
Uint8 *mixbuf;
|
||||
|
||||
int bytes_requested; // bytes of data the hardware wants _now_.
|
||||
|
||||
const Uint8 *capturebuf;
|
||||
int capturelen;
|
||||
};
|
||||
|
||||
#endif // SDL_pulseaudio_h_
|
||||
450
vendor/sdl-3.0.0/src/audio/qnx/SDL_qsa_audio.c
vendored
Normal file
450
vendor/sdl-3.0.0/src/audio/qnx/SDL_qsa_audio.c
vendored
Normal file
|
|
@ -0,0 +1,450 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2021 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
// !!! FIXME: can this target support hotplugging?
|
||||
|
||||
#include "../../SDL_internal.h"
|
||||
|
||||
#ifdef SDL_AUDIO_DRIVER_QNX
|
||||
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/time.h>
|
||||
#include <sched.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/neutrino.h>
|
||||
#include <sys/asoundlib.h>
|
||||
|
||||
#include "SDL3/SDL_timer.h"
|
||||
#include "SDL3/SDL_audio.h"
|
||||
#include "../../core/unix/SDL_poll.h"
|
||||
#include "../SDL_sysaudio.h"
|
||||
#include "SDL_qsa_audio.h"
|
||||
|
||||
// default channel communication parameters
|
||||
#define DEFAULT_CPARAMS_RATE 44100
|
||||
#define DEFAULT_CPARAMS_VOICES 1
|
||||
|
||||
#define DEFAULT_CPARAMS_FRAG_SIZE 4096
|
||||
#define DEFAULT_CPARAMS_FRAGS_MIN 1
|
||||
#define DEFAULT_CPARAMS_FRAGS_MAX 1
|
||||
|
||||
#define QSA_MAX_NAME_LENGTH 81+16 // Hardcoded in QSA, can't be changed
|
||||
|
||||
static int QSA_SetError(const char *fn, int status)
|
||||
{
|
||||
return SDL_SetError("QSA: %s() failed: %s", fn, snd_strerror(status));
|
||||
}
|
||||
|
||||
// !!! FIXME: does this need to be here? Does the SDL version not work?
|
||||
static void QSA_ThreadInit(SDL_AudioDevice *device)
|
||||
{
|
||||
// Increase default 10 priority to 25 to avoid jerky sound
|
||||
struct sched_param param;
|
||||
if (SchedGet(0, 0, ¶m) != -1) {
|
||||
param.sched_priority = param.sched_curpriority + 15;
|
||||
SchedSet(0, 0, SCHED_NOCHANGE, ¶m);
|
||||
}
|
||||
}
|
||||
|
||||
// PCM channel parameters initialize function
|
||||
static void QSA_InitAudioParams(snd_pcm_channel_params_t * cpars)
|
||||
{
|
||||
SDL_zerop(cpars);
|
||||
cpars->channel = SND_PCM_CHANNEL_PLAYBACK;
|
||||
cpars->mode = SND_PCM_MODE_BLOCK;
|
||||
cpars->start_mode = SND_PCM_START_DATA;
|
||||
cpars->stop_mode = SND_PCM_STOP_STOP;
|
||||
cpars->format.format = SND_PCM_SFMT_S16_LE;
|
||||
cpars->format.interleave = 1;
|
||||
cpars->format.rate = DEFAULT_CPARAMS_RATE;
|
||||
cpars->format.voices = DEFAULT_CPARAMS_VOICES;
|
||||
cpars->buf.block.frag_size = DEFAULT_CPARAMS_FRAG_SIZE;
|
||||
cpars->buf.block.frags_min = DEFAULT_CPARAMS_FRAGS_MIN;
|
||||
cpars->buf.block.frags_max = DEFAULT_CPARAMS_FRAGS_MAX;
|
||||
}
|
||||
|
||||
// This function waits until it is possible to write a full sound buffer
|
||||
static int QSA_WaitDevice(SDL_AudioDevice *device)
|
||||
{
|
||||
// Setup timeout for playing one fragment equal to 2 seconds
|
||||
// If timeout occurred than something wrong with hardware or driver
|
||||
// For example, Vortex 8820 audio driver stucks on second DAC because
|
||||
// it doesn't exist !
|
||||
const int result = SDL_IOReady(device->hidden->audio_fd,
|
||||
device->iscapture ? SDL_IOR_READ : SDL_IOR_WRITE,
|
||||
2 * 1000);
|
||||
switch (result) {
|
||||
case -1:
|
||||
SDL_LogError(SDL_LOG_CATEGORY_AUDIO, "QSA: SDL_IOReady() failed: %s", strerror(errno));
|
||||
return -1;
|
||||
case 0:
|
||||
device->hidden->timeout_on_wait = SDL_TRUE; // !!! FIXME: Should we just disconnect the device in this case?
|
||||
break;
|
||||
default:
|
||||
device->hidden->timeout_on_wait = SDL_FALSE;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int QSA_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
|
||||
{
|
||||
if (SDL_AtomicGet(&device->shutdown) || !device->hidden) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int towrite = buflen;
|
||||
|
||||
// Write the audio data, checking for EAGAIN (buffer full) and underrun
|
||||
while ((towrite > 0) && !SDL_AtomicGet(&device->shutdown));
|
||||
const int bw = snd_pcm_plugin_write(device->hidden->audio_handle, buffer, towrite);
|
||||
if (bw != towrite) {
|
||||
// Check if samples playback got stuck somewhere in hardware or in the audio device driver
|
||||
if ((errno == EAGAIN) && (bw == 0)) {
|
||||
if (device->hidden->timeout_on_wait) {
|
||||
return 0; // oh well, try again next time. !!! FIXME: Should we just disconnect the device in this case?
|
||||
}
|
||||
}
|
||||
|
||||
// Check for errors or conditions
|
||||
if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) {
|
||||
SDL_Delay(1); // Let a little CPU time go by and try to write again
|
||||
|
||||
// if we wrote some data
|
||||
towrite -= bw;
|
||||
buffer += bw * device->spec.channels;
|
||||
continue;
|
||||
} else if ((errno == EINVAL) || (errno == EIO)) {
|
||||
snd_pcm_channel_status_t cstatus;
|
||||
SDL_zero(cstatus);
|
||||
cstatus.channel = device->iscapture ? SND_PCM_CHANNEL_CAPTURE : SND_PCM_CHANNEL_PLAYBACK;
|
||||
|
||||
int status = snd_pcm_plugin_status(device->hidden->audio_handle, &cstatus);
|
||||
if (status < 0) {
|
||||
QSA_SetError("snd_pcm_plugin_status", status);
|
||||
return -1;
|
||||
} else if ((cstatus.status == SND_PCM_STATUS_UNDERRUN) || (cstatus.status == SND_PCM_STATUS_READY)) {
|
||||
status = snd_pcm_plugin_prepare(device->hidden->audio_handle, device->iscapture ? SND_PCM_CHANNEL_CAPTURE : SND_PCM_CHANNEL_PLAYBACK);
|
||||
if (status < 0) {
|
||||
QSA_SetError("snd_pcm_plugin_prepare", status);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
// we wrote all remaining data
|
||||
towrite -= bw;
|
||||
buffer += bw * device->spec.channels;
|
||||
}
|
||||
}
|
||||
|
||||
// If we couldn't write, assume fatal error for now
|
||||
return (towrite != 0) ? -1 : 0;
|
||||
}
|
||||
|
||||
static Uint8 *QSA_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
|
||||
{
|
||||
return device->hidden->pcm_buf;
|
||||
}
|
||||
|
||||
static void QSA_CloseDevice(SDL_AudioDevice *device)
|
||||
{
|
||||
if (device->hidden) {
|
||||
if (device->hidden->audio_handle != NULL) {
|
||||
#if _NTO_VERSION < 710
|
||||
// Finish playing available samples or cancel unread samples during capture
|
||||
snd_pcm_plugin_flush(device->hidden->audio_handle, device->iscapture ? SND_PCM_CHANNEL_CAPTURE : SND_PCM_CHANNEL_PLAYBACK);
|
||||
#endif
|
||||
snd_pcm_close(device->hidden->audio_handle);
|
||||
}
|
||||
|
||||
SDL_free(device->hidden->pcm_buf);
|
||||
SDL_free(device->hidden);
|
||||
device->hidden = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int QSA_OpenDevice(SDL_AudioDevice *device)
|
||||
{
|
||||
if (device->iscapture) {
|
||||
return SDL_SetError("SDL capture support isn't available on QNX atm"); // !!! FIXME: most of this code has support for capture devices, but there's no CaptureFromDevice, etc functions. Fill them in!
|
||||
}
|
||||
|
||||
SDL_assert(device->handle != NULL); // NULL used to mean "system default device" in SDL2; it does not mean that in SDL3.
|
||||
const Uint32 sdlhandle = (Uint32) ((size_t) device->handle);
|
||||
const uint32_t cardno = (uint32_t) (sdlhandle & 0xFFFF);
|
||||
const uint32_t deviceno = (uint32_t) ((sdlhandle >> 16) & 0xFFFF);
|
||||
const SDL_bool iscapture = device->iscapture;
|
||||
int status = 0;
|
||||
|
||||
// Initialize all variables that we clean on shutdown
|
||||
device->hidden = (struct SDL_PrivateAudioData *) SDL_calloc(1, (sizeof (struct SDL_PrivateAudioData)));
|
||||
if (device->hidden == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
|
||||
// Initialize channel transfer parameters to default
|
||||
snd_pcm_channel_params_t cparams;
|
||||
QSA_InitAudioParams(&cparams);
|
||||
|
||||
// Open requested audio device
|
||||
status = snd_pcm_open(&device->hidden->audio_handle, cardno, deviceno, iscapture ? SND_PCM_OPEN_CAPTURE : SND_PCM_OPEN_PLAYBACK);
|
||||
if (status < 0) {
|
||||
device->hidden->audio_handle = NULL;
|
||||
return QSA_SetError("snd_pcm_open", status);
|
||||
}
|
||||
|
||||
// Try for a closest match on audio format
|
||||
SDL_AudioFormat test_format = 0;
|
||||
const SDL_AudioFormat *closefmts = SDL_ClosestAudioFormats(device->spec.format);
|
||||
while ((test_format = *(closefmts++)) != 0) {
|
||||
// if match found set format to equivalent QSA format
|
||||
switch (test_format) {
|
||||
#define CHECKFMT(sdlfmt, qsafmt) case SDL_AUDIO_##sdlfmt: cparams.format.format = SND_PCM_SFMT_##qsafmt; break
|
||||
CHECKFMT(U8, U8);
|
||||
CHECKFMT(S8, S8);
|
||||
CHECKFMT(S16LSB, S16_LE);
|
||||
CHECKFMT(S16MSB, S16_BE);
|
||||
CHECKFMT(S32LSB, S32_LE);
|
||||
CHECKFMT(S32MSB, S32_BE);
|
||||
CHECKFMT(F32LSB, FLOAT_LE);
|
||||
CHECKFMT(F32MSB, FLOAT_BE);
|
||||
#undef CHECKFMT
|
||||
default: continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// assumes test_format not 0 on success
|
||||
if (test_format == 0) {
|
||||
return SDL_SetError("QSA: Couldn't find any hardware audio formats");
|
||||
}
|
||||
|
||||
device->spec.format = test_format;
|
||||
|
||||
// Set mono/stereo/4ch/6ch/8ch audio
|
||||
cparams.format.voices = device->spec.channels;
|
||||
|
||||
// Set rate
|
||||
cparams.format.rate = device->spec.freq;
|
||||
|
||||
// Setup the transfer parameters according to cparams
|
||||
status = snd_pcm_plugin_params(device->hidden->audio_handle, &cparams);
|
||||
if (status < 0) {
|
||||
return QSA_SetError("snd_pcm_plugin_params", status);
|
||||
}
|
||||
|
||||
// Make sure channel is setup right one last time
|
||||
snd_pcm_channel_setup_t csetup;
|
||||
SDL_zero(csetup);
|
||||
csetup.channel = iscapture ? SND_PCM_CHANNEL_CAPTURE : SND_PCM_CHANNEL_PLAYBACK;
|
||||
if (snd_pcm_plugin_setup(device->hidden->audio_handle, &csetup) < 0) {
|
||||
return SDL_SetError("QSA: Unable to setup channel");
|
||||
}
|
||||
|
||||
device->sample_frames = csetup.buf.block.frag_size;
|
||||
|
||||
// Calculate the final parameters for this audio specification
|
||||
SDL_UpdatedAudioDeviceFormat(device);
|
||||
|
||||
device->hidden->pcm_buf = (Uint8 *) SDL_malloc(device->buffer_size);
|
||||
if (device->hidden->pcm_buf == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
SDL_memset(device->hidden->pcm_buf, device->silence_value, device->buffer_size);
|
||||
|
||||
// get the file descriptor
|
||||
device->hidden->audio_fd = snd_pcm_file_descriptor(device->hidden->audio_handle, csetup.channel);
|
||||
if (device->hidden->audio_fd < 0) {
|
||||
return QSA_SetError("snd_pcm_file_descriptor", device->hidden->audio_fd);
|
||||
}
|
||||
|
||||
// Prepare an audio channel
|
||||
status = snd_pcm_plugin_prepare(device->hidden->audio_handle, csetup.channel)
|
||||
if (status < 0) {
|
||||
return QSA_SetError("snd_pcm_plugin_prepare", status);
|
||||
}
|
||||
|
||||
return 0; // We're really ready to rock and roll. :-)
|
||||
}
|
||||
|
||||
static SDL_AudioFormat QnxFormatToSDLFormat(const int32_t qnxfmt)
|
||||
{
|
||||
switch (qnxfmt) {
|
||||
#define CHECKFMT(sdlfmt, qsafmt) case SND_PCM_SFMT_##qsafmt: return SDL_AUDIO_##sdlfmt
|
||||
CHECKFMT(U8, U8);
|
||||
CHECKFMT(S8, S8);
|
||||
CHECKFMT(S16LSB, S16_LE);
|
||||
CHECKFMT(S16MSB, S16_BE);
|
||||
CHECKFMT(S32LSB, S32_LE);
|
||||
CHECKFMT(S32MSB, S32_BE);
|
||||
CHECKFMT(F32LSB, FLOAT_LE);
|
||||
CHECKFMT(F32MSB, FLOAT_BE);
|
||||
#undef CHECKFMT
|
||||
default: break;
|
||||
}
|
||||
return SDL_AUDIO_S16; // oh well.
|
||||
}
|
||||
|
||||
static void QSA_DetectDevices(SDL_AudioDevice **default_output, SDL_AudioDevice **default_capture)
|
||||
{
|
||||
// Detect amount of available devices
|
||||
// this value can be changed in the runtime
|
||||
int num_cards = 0;
|
||||
(void) snd_cards_list(NULL, 0, &alloc_num_cards);
|
||||
SDL_bool isstack = SDL_FALSE;
|
||||
int *cards = SDL_small_alloc(int, num_cards, &isstack);
|
||||
if (!cards) {
|
||||
return; // we're in trouble.
|
||||
}
|
||||
int overflow_cards = 0;
|
||||
const int total_num_cards = snd_cards_list(cards, num_cards, &overflow_cards);
|
||||
// if overflow_cards > 0 or total_num_cards > num_cards, it changed at the last moment; oh well, we lost some.
|
||||
num_cards = SDL_min(num_cards, total_num_cards); // ...but make sure it didn't _shrink_.
|
||||
|
||||
// If io-audio manager is not running we will get 0 as number of available audio devices
|
||||
if (num_cards == 0) { // not any available audio devices?
|
||||
SDL_small_free(cards, isstack);
|
||||
return;
|
||||
}
|
||||
|
||||
// Find requested devices by type
|
||||
for (int it = 0; it < num_cards; it++) {
|
||||
const int card = cards[it];
|
||||
for (uint32_t deviceno = 0; ; deviceno++) {
|
||||
int32_t status;
|
||||
char name[QSA_MAX_NAME_LENGTH];
|
||||
|
||||
status = snd_card_get_longname(card, name, sizeof (name));
|
||||
if (status == EOK) {
|
||||
snd_pcm_t *handle;
|
||||
|
||||
// Add device number to device name
|
||||
char fullname[QSA_MAX_NAME_LENGTH + 32];
|
||||
SDL_snprintf(fullname, sizeof (fullname), "%s d%d", name, (int) deviceno);
|
||||
|
||||
// Check if this device id could play anything
|
||||
SDL_bool iscapture = SDL_FALSE;
|
||||
status = snd_pcm_open(&handle, card, deviceno, SND_PCM_OPEN_PLAYBACK);
|
||||
if (status != EOK) { // no? See if it's a capture device instead.
|
||||
#if 0 // !!! FIXME: most of this code has support for capture devices, but there's no CaptureFromDevice, etc functions. Fill them in!
|
||||
status = snd_pcm_open(&handle, card, deviceno, SND_PCM_OPEN_CAPTURE);
|
||||
if (status == EOK) {
|
||||
iscapture = SDL_TRUE;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (status == EOK) {
|
||||
SDL_AudioSpec spec;
|
||||
SDL_AudioSpec *pspec = &spec;
|
||||
snd_pcm_channel_setup_t csetup;
|
||||
SDL_zero(csetup);
|
||||
csetup.channel = iscapture ? SND_PCM_CHANNEL_CAPTURE : SND_PCM_CHANNEL_PLAYBACK;
|
||||
|
||||
if (snd_pcm_plugin_setup(device->hidden->audio_handle, &csetup) < 0) {
|
||||
pspec = NULL; // go on without spec info.
|
||||
} else {
|
||||
spec.format = QnxFormatToSDLFormat(csetup.format.format);
|
||||
spec.channels = csetup.format.channels;
|
||||
spec.freq = csetup.format.rate;
|
||||
}
|
||||
|
||||
status = snd_pcm_close(handle);
|
||||
if (status == EOK) {
|
||||
// !!! FIXME: I'm assuming each of these values are way less than 0xFFFF. Fix this if not.
|
||||
SDL_assert(card <= 0xFFFF);
|
||||
SDL_assert(deviceno <= 0xFFFF);
|
||||
const Uint32 sdlhandle = ((Uint32) card) | (((Uint32) deviceno) << 16);
|
||||
SDL_AddAudioDevice(iscapture, fullname, pspec, (void *) ((size_t) sdlhandle));
|
||||
}
|
||||
} else {
|
||||
// Check if we got end of devices list
|
||||
if (status == -ENOENT) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SDL_small_free(cards, isstack);
|
||||
|
||||
// Try to open the "preferred" devices, which will tell us the card/device pairs for the default devices.
|
||||
snd_pcm_t handle;
|
||||
int cardno, deviceno;
|
||||
if (snd_pcm_open_preferred(&handle, &cardno, &deviceno, SND_PCM_OPEN_PLAYBACK) == 0) {
|
||||
snd_pcm_close(handle);
|
||||
// !!! FIXME: I'm assuming each of these values are way less than 0xFFFF. Fix this if not.
|
||||
SDL_assert(cardno <= 0xFFFF);
|
||||
SDL_assert(deviceno <= 0xFFFF);
|
||||
const Uint32 sdlhandle = ((Uint32) card) | (((Uint32) deviceno) << 16);
|
||||
*default_output = SDL_FindPhysicalAudioDeviceByHandle((void *) ((size_t) sdlhandle));
|
||||
}
|
||||
|
||||
if (snd_pcm_open_preferred(&handle, &cardno, &deviceno, SND_PCM_OPEN_CAPTURE) == 0) {
|
||||
snd_pcm_close(handle);
|
||||
// !!! FIXME: I'm assuming each of these values are way less than 0xFFFF. Fix this if not.
|
||||
SDL_assert(cardno <= 0xFFFF);
|
||||
SDL_assert(deviceno <= 0xFFFF);
|
||||
const Uint32 sdlhandle = ((Uint32) card) | (((Uint32) deviceno) << 16);
|
||||
*default_capture = SDL_FindPhysicalAudioDeviceByHandle((void *) ((size_t) sdlhandle));
|
||||
}
|
||||
}
|
||||
|
||||
static void QSA_Deinitialize(void)
|
||||
{
|
||||
// nothing to do here atm.
|
||||
}
|
||||
|
||||
static SDL_bool QSA_Init(SDL_AudioDriverImpl * impl)
|
||||
{
|
||||
impl->DetectDevices = QSA_DetectDevices;
|
||||
impl->OpenDevice = QSA_OpenDevice;
|
||||
impl->ThreadInit = QSA_ThreadInit;
|
||||
impl->WaitDevice = QSA_WaitDevice;
|
||||
impl->PlayDevice = QSA_PlayDevice;
|
||||
impl->GetDeviceBuf = QSA_GetDeviceBuf;
|
||||
impl->CloseDevice = QSA_CloseDevice;
|
||||
impl->Deinitialize = QSA_Deinitialize;
|
||||
|
||||
// !!! FIXME: most of this code has support for capture devices, but there's no CaptureFromDevice, etc functions. Fill them in!
|
||||
//impl->HasCaptureSupport = SDL_TRUE;
|
||||
|
||||
return SDL_TRUE;
|
||||
}
|
||||
|
||||
AudioBootStrap QSAAUDIO_bootstrap = {
|
||||
"qsa", "QNX QSA Audio", QSA_Init, 0
|
||||
};
|
||||
|
||||
#endif // SDL_AUDIO_DRIVER_QNX
|
||||
|
||||
40
vendor/sdl-3.0.0/src/audio/qnx/SDL_qsa_audio.h
vendored
Normal file
40
vendor/sdl-3.0.0/src/audio/qnx/SDL_qsa_audio.h
vendored
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2021 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
#include "../../SDL_internal.h"
|
||||
|
||||
#ifndef __SDL_QSA_AUDIO_H__
|
||||
#define __SDL_QSA_AUDIO_H__
|
||||
|
||||
#include <sys/asoundlib.h>
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
|
||||
struct SDL_PrivateAudioData
|
||||
{
|
||||
snd_pcm_t *audio_handle; // The audio device handle
|
||||
int audio_fd; // The audio file descriptor, for selecting on
|
||||
SDL_bool timeout_on_wait; // Select timeout status
|
||||
Uint8 *pcm_buf; // Raw mixing buffer
|
||||
};
|
||||
|
||||
#endif // __SDL_QSA_AUDIO_H__
|
||||
|
||||
359
vendor/sdl-3.0.0/src/audio/sndio/SDL_sndioaudio.c
vendored
Normal file
359
vendor/sdl-3.0.0/src/audio/sndio/SDL_sndioaudio.c
vendored
Normal file
|
|
@ -0,0 +1,359 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifdef SDL_AUDIO_DRIVER_SNDIO
|
||||
|
||||
// OpenBSD sndio target
|
||||
|
||||
#ifdef HAVE_STDIO_H
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SIGNAL_H
|
||||
#include <signal.h>
|
||||
#endif
|
||||
|
||||
#include <poll.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
#include "SDL_sndioaudio.h"
|
||||
|
||||
#ifdef SDL_AUDIO_DRIVER_SNDIO_DYNAMIC
|
||||
#endif
|
||||
|
||||
#ifndef INFTIM
|
||||
#define INFTIM -1
|
||||
#endif
|
||||
|
||||
#ifndef SIO_DEVANY
|
||||
#define SIO_DEVANY "default"
|
||||
#endif
|
||||
|
||||
static struct sio_hdl *(*SNDIO_sio_open)(const char *, unsigned int, int);
|
||||
static void (*SNDIO_sio_close)(struct sio_hdl *);
|
||||
static int (*SNDIO_sio_setpar)(struct sio_hdl *, struct sio_par *);
|
||||
static int (*SNDIO_sio_getpar)(struct sio_hdl *, struct sio_par *);
|
||||
static int (*SNDIO_sio_start)(struct sio_hdl *);
|
||||
static int (*SNDIO_sio_stop)(struct sio_hdl *);
|
||||
static size_t (*SNDIO_sio_read)(struct sio_hdl *, void *, size_t);
|
||||
static size_t (*SNDIO_sio_write)(struct sio_hdl *, const void *, size_t);
|
||||
static int (*SNDIO_sio_nfds)(struct sio_hdl *);
|
||||
static int (*SNDIO_sio_pollfd)(struct sio_hdl *, struct pollfd *, int);
|
||||
static int (*SNDIO_sio_revents)(struct sio_hdl *, struct pollfd *);
|
||||
static int (*SNDIO_sio_eof)(struct sio_hdl *);
|
||||
static void (*SNDIO_sio_initpar)(struct sio_par *);
|
||||
|
||||
#ifdef SDL_AUDIO_DRIVER_SNDIO_DYNAMIC
|
||||
static const char *sndio_library = SDL_AUDIO_DRIVER_SNDIO_DYNAMIC;
|
||||
static void *sndio_handle = NULL;
|
||||
|
||||
static int load_sndio_sym(const char *fn, void **addr)
|
||||
{
|
||||
*addr = SDL_LoadFunction(sndio_handle, fn);
|
||||
if (*addr == NULL) {
|
||||
return 0; // Don't call SDL_SetError(): SDL_LoadFunction already did.
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
// cast funcs to char* first, to please GCC's strict aliasing rules.
|
||||
#define SDL_SNDIO_SYM(x) \
|
||||
if (!load_sndio_sym(#x, (void **)(char *)&SNDIO_##x)) \
|
||||
return -1
|
||||
#else
|
||||
#define SDL_SNDIO_SYM(x) SNDIO_##x = x
|
||||
#endif
|
||||
|
||||
static int load_sndio_syms(void)
|
||||
{
|
||||
SDL_SNDIO_SYM(sio_open);
|
||||
SDL_SNDIO_SYM(sio_close);
|
||||
SDL_SNDIO_SYM(sio_setpar);
|
||||
SDL_SNDIO_SYM(sio_getpar);
|
||||
SDL_SNDIO_SYM(sio_start);
|
||||
SDL_SNDIO_SYM(sio_stop);
|
||||
SDL_SNDIO_SYM(sio_read);
|
||||
SDL_SNDIO_SYM(sio_write);
|
||||
SDL_SNDIO_SYM(sio_nfds);
|
||||
SDL_SNDIO_SYM(sio_pollfd);
|
||||
SDL_SNDIO_SYM(sio_revents);
|
||||
SDL_SNDIO_SYM(sio_eof);
|
||||
SDL_SNDIO_SYM(sio_initpar);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#undef SDL_SNDIO_SYM
|
||||
|
||||
#ifdef SDL_AUDIO_DRIVER_SNDIO_DYNAMIC
|
||||
|
||||
static void UnloadSNDIOLibrary(void)
|
||||
{
|
||||
if (sndio_handle != NULL) {
|
||||
SDL_UnloadObject(sndio_handle);
|
||||
sndio_handle = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int LoadSNDIOLibrary(void)
|
||||
{
|
||||
int retval = 0;
|
||||
if (sndio_handle == NULL) {
|
||||
sndio_handle = SDL_LoadObject(sndio_library);
|
||||
if (sndio_handle == NULL) {
|
||||
retval = -1; // Don't call SDL_SetError(): SDL_LoadObject already did.
|
||||
} else {
|
||||
retval = load_sndio_syms();
|
||||
if (retval < 0) {
|
||||
UnloadSNDIOLibrary();
|
||||
}
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static void UnloadSNDIOLibrary(void)
|
||||
{
|
||||
}
|
||||
|
||||
static int LoadSNDIOLibrary(void)
|
||||
{
|
||||
load_sndio_syms();
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif // SDL_AUDIO_DRIVER_SNDIO_DYNAMIC
|
||||
|
||||
static int SNDIO_WaitDevice(SDL_AudioDevice *device)
|
||||
{
|
||||
const SDL_bool iscapture = device->iscapture;
|
||||
|
||||
while (!SDL_AtomicGet(&device->shutdown)) {
|
||||
if (SNDIO_sio_eof(device->hidden->dev)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
const int nfds = SNDIO_sio_pollfd(device->hidden->dev, device->hidden->pfd, iscapture ? POLLIN : POLLOUT);
|
||||
if (nfds <= 0 || poll(device->hidden->pfd, nfds, 10) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
const int revents = SNDIO_sio_revents(device->hidden->dev, device->hidden->pfd);
|
||||
if (iscapture && (revents & POLLIN)) {
|
||||
break;
|
||||
} else if (!iscapture && (revents & POLLOUT)) {
|
||||
break;
|
||||
} else if (revents & POLLHUP) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int SNDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
|
||||
{
|
||||
// !!! FIXME: this should be non-blocking so we can check device->shutdown.
|
||||
// this is set to blocking, because we _have_ to send the entire buffer down, but hopefully WaitDevice took most of the delay time.
|
||||
if (SNDIO_sio_write(device->hidden->dev, buffer, buflen) != buflen) {
|
||||
return -1; // If we couldn't write, assume fatal error for now
|
||||
}
|
||||
#ifdef DEBUG_AUDIO
|
||||
fprintf(stderr, "Wrote %d bytes of audio data\n", written);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int SNDIO_CaptureFromDevice(SDL_AudioDevice *device, void *buffer, int buflen)
|
||||
{
|
||||
// We set capture devices non-blocking; this can safely return 0 in SDL3, but we'll check for EOF to cause a device disconnect.
|
||||
const size_t br = SNDIO_sio_read(device->hidden->dev, buffer, buflen);
|
||||
if ((br == 0) && SNDIO_sio_eof(device->hidden->dev)) {
|
||||
return -1;
|
||||
}
|
||||
return (int) br;
|
||||
}
|
||||
|
||||
static void SNDIO_FlushCapture(SDL_AudioDevice *device)
|
||||
{
|
||||
char buf[512];
|
||||
while (!SDL_AtomicGet(&device->shutdown) && (SNDIO_sio_read(device->hidden->dev, buf, sizeof(buf)) > 0)) {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
|
||||
static Uint8 *SNDIO_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
|
||||
{
|
||||
return device->hidden->mixbuf;
|
||||
}
|
||||
|
||||
static void SNDIO_CloseDevice(SDL_AudioDevice *device)
|
||||
{
|
||||
if (device->hidden) {
|
||||
if (device->hidden->dev != NULL) {
|
||||
SNDIO_sio_stop(device->hidden->dev);
|
||||
SNDIO_sio_close(device->hidden->dev);
|
||||
}
|
||||
SDL_free(device->hidden->pfd);
|
||||
SDL_free(device->hidden->mixbuf);
|
||||
SDL_free(device->hidden);
|
||||
device->hidden = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int SNDIO_OpenDevice(SDL_AudioDevice *device)
|
||||
{
|
||||
device->hidden = (struct SDL_PrivateAudioData *) SDL_calloc(1, sizeof(*device->hidden));
|
||||
if (device->hidden == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
|
||||
// !!! FIXME: we really should standardize this on a specific SDL hint.
|
||||
const char *audiodev = SDL_getenv("AUDIODEV");
|
||||
|
||||
// Capture devices must be non-blocking for SNDIO_FlushCapture
|
||||
device->hidden->dev = SNDIO_sio_open(audiodev != NULL ? audiodev : SIO_DEVANY,
|
||||
device->iscapture ? SIO_REC : SIO_PLAY, device->iscapture);
|
||||
if (device->hidden->dev == NULL) {
|
||||
return SDL_SetError("sio_open() failed");
|
||||
}
|
||||
|
||||
device->hidden->pfd = SDL_malloc(sizeof(struct pollfd) * SNDIO_sio_nfds(device->hidden->dev));
|
||||
if (device->hidden->pfd == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
|
||||
struct sio_par par;
|
||||
SNDIO_sio_initpar(&par);
|
||||
|
||||
par.rate = device->spec.freq;
|
||||
par.pchan = device->spec.channels;
|
||||
par.round = device->sample_frames;
|
||||
par.appbufsz = par.round * 2;
|
||||
|
||||
// Try for a closest match on audio format
|
||||
SDL_AudioFormat test_format;
|
||||
const SDL_AudioFormat *closefmts = SDL_ClosestAudioFormats(device->spec.format);
|
||||
while ((test_format = *(closefmts++)) != 0) {
|
||||
if (!SDL_AUDIO_ISFLOAT(test_format)) {
|
||||
par.le = SDL_AUDIO_ISLITTLEENDIAN(test_format) ? 1 : 0;
|
||||
par.sig = SDL_AUDIO_ISSIGNED(test_format) ? 1 : 0;
|
||||
par.bits = SDL_AUDIO_BITSIZE(test_format);
|
||||
|
||||
if (SNDIO_sio_setpar(device->hidden->dev, &par) == 0) {
|
||||
continue;
|
||||
}
|
||||
if (SNDIO_sio_getpar(device->hidden->dev, &par) == 0) {
|
||||
return SDL_SetError("sio_getpar() failed");
|
||||
}
|
||||
if (par.bps != SIO_BPS(par.bits)) {
|
||||
continue;
|
||||
}
|
||||
if ((par.bits == 8 * par.bps) || (par.msb)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!test_format) {
|
||||
return SDL_SetError("sndio: Unsupported audio format");
|
||||
}
|
||||
|
||||
if ((par.bps == 4) && (par.sig) && (par.le)) {
|
||||
device->spec.format = SDL_AUDIO_S32LE;
|
||||
} else if ((par.bps == 4) && (par.sig) && (!par.le)) {
|
||||
device->spec.format = SDL_AUDIO_S32BE;
|
||||
} else if ((par.bps == 2) && (par.sig) && (par.le)) {
|
||||
device->spec.format = SDL_AUDIO_S16LE;
|
||||
} else if ((par.bps == 2) && (par.sig) && (!par.le)) {
|
||||
device->spec.format = SDL_AUDIO_S16BE;
|
||||
} else if ((par.bps == 1) && (par.sig)) {
|
||||
device->spec.format = SDL_AUDIO_S8;
|
||||
} else if ((par.bps == 1) && (!par.sig)) {
|
||||
device->spec.format = SDL_AUDIO_U8;
|
||||
} else {
|
||||
return SDL_SetError("sndio: Got unsupported hardware audio format.");
|
||||
}
|
||||
|
||||
device->spec.freq = par.rate;
|
||||
device->spec.channels = par.pchan;
|
||||
device->sample_frames = par.round;
|
||||
|
||||
// Calculate the final parameters for this audio specification
|
||||
SDL_UpdatedAudioDeviceFormat(device);
|
||||
|
||||
// Allocate mixing buffer
|
||||
device->hidden->mixbuf = (Uint8 *)SDL_malloc(device->buffer_size);
|
||||
if (device->hidden->mixbuf == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
SDL_memset(device->hidden->mixbuf, device->silence_value, device->buffer_size);
|
||||
|
||||
if (!SNDIO_sio_start(device->hidden->dev)) {
|
||||
return SDL_SetError("sio_start() failed");
|
||||
}
|
||||
|
||||
return 0; // We're ready to rock and roll. :-)
|
||||
}
|
||||
|
||||
static void SNDIO_Deinitialize(void)
|
||||
{
|
||||
UnloadSNDIOLibrary();
|
||||
}
|
||||
|
||||
static void SNDIO_DetectDevices(SDL_AudioDevice **default_output, SDL_AudioDevice **default_capture)
|
||||
{
|
||||
*default_output = SDL_AddAudioDevice(SDL_FALSE, DEFAULT_OUTPUT_DEVNAME, NULL, (void *)0x1);
|
||||
*default_capture = SDL_AddAudioDevice(SDL_TRUE, DEFAULT_INPUT_DEVNAME, NULL, (void *)0x2);
|
||||
}
|
||||
|
||||
static SDL_bool SNDIO_Init(SDL_AudioDriverImpl *impl)
|
||||
{
|
||||
if (LoadSNDIOLibrary() < 0) {
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
impl->OpenDevice = SNDIO_OpenDevice;
|
||||
impl->WaitDevice = SNDIO_WaitDevice;
|
||||
impl->PlayDevice = SNDIO_PlayDevice;
|
||||
impl->GetDeviceBuf = SNDIO_GetDeviceBuf;
|
||||
impl->CloseDevice = SNDIO_CloseDevice;
|
||||
impl->WaitCaptureDevice = SNDIO_WaitDevice;
|
||||
impl->CaptureFromDevice = SNDIO_CaptureFromDevice;
|
||||
impl->FlushCapture = SNDIO_FlushCapture;
|
||||
impl->Deinitialize = SNDIO_Deinitialize;
|
||||
impl->DetectDevices = SNDIO_DetectDevices;
|
||||
|
||||
impl->HasCaptureSupport = SDL_TRUE;
|
||||
|
||||
return SDL_TRUE;
|
||||
}
|
||||
|
||||
AudioBootStrap SNDIO_bootstrap = {
|
||||
"sndio", "OpenBSD sndio", SNDIO_Init, SDL_FALSE
|
||||
};
|
||||
|
||||
#endif // SDL_AUDIO_DRIVER_SNDIO
|
||||
38
vendor/sdl-3.0.0/src/audio/sndio/SDL_sndioaudio.h
vendored
Normal file
38
vendor/sdl-3.0.0/src/audio/sndio/SDL_sndioaudio.h
vendored
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifndef SDL_sndioaudio_h_
|
||||
#define SDL_sndioaudio_h_
|
||||
|
||||
#include <poll.h>
|
||||
#include <sndio.h>
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
|
||||
struct SDL_PrivateAudioData
|
||||
{
|
||||
struct sio_hdl *dev; // The audio device handle
|
||||
Uint8 *mixbuf; // Raw mixing buffer
|
||||
struct pollfd *pfd; // Polling structures for non-blocking sndio devices
|
||||
};
|
||||
|
||||
#endif // SDL_sndioaudio_h_
|
||||
238
vendor/sdl-3.0.0/src/audio/vita/SDL_vitaaudio.c
vendored
Normal file
238
vendor/sdl-3.0.0/src/audio/vita/SDL_vitaaudio.c
vendored
Normal file
|
|
@ -0,0 +1,238 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifdef SDL_AUDIO_DRIVER_VITA
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "../SDL_audiodev_c.h"
|
||||
#include "../SDL_sysaudio.h"
|
||||
#include "SDL_vitaaudio.h"
|
||||
|
||||
#include <psp2/kernel/threadmgr.h>
|
||||
#include <psp2/audioout.h>
|
||||
#include <psp2/audioin.h>
|
||||
|
||||
#define SCE_AUDIO_SAMPLE_ALIGN(s) (((s) + 63) & ~63)
|
||||
#define SCE_AUDIO_MAX_VOLUME 0x8000
|
||||
|
||||
static int VITAAUD_OpenCaptureDevice(SDL_AudioDevice *device)
|
||||
{
|
||||
device->spec.freq = 16000;
|
||||
device->spec.channels = 1;
|
||||
device->sample_frames = 512;
|
||||
|
||||
SDL_UpdatedAudioDeviceFormat(device);
|
||||
|
||||
device->hidden->port = sceAudioInOpenPort(SCE_AUDIO_IN_PORT_TYPE_VOICE, 512, 16000, SCE_AUDIO_IN_PARAM_FORMAT_S16_MONO);
|
||||
|
||||
if (device->hidden->port < 0) {
|
||||
return SDL_SetError("Couldn't open audio in port: %x", device->hidden->port);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int VITAAUD_OpenDevice(SDL_AudioDevice *device)
|
||||
{
|
||||
int format, mixlen, i, port = SCE_AUDIO_OUT_PORT_TYPE_MAIN;
|
||||
int vols[2] = { SCE_AUDIO_MAX_VOLUME, SCE_AUDIO_MAX_VOLUME };
|
||||
SDL_AudioFormat test_format;
|
||||
const SDL_AudioFormat *closefmts;
|
||||
|
||||
device->hidden = (struct SDL_PrivateAudioData *)
|
||||
SDL_malloc(sizeof(*device->hidden));
|
||||
if (device->hidden == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
SDL_memset(device->hidden, 0, sizeof(*device->hidden));
|
||||
|
||||
closefmts = SDL_ClosestAudioFormats(device->spec.format);
|
||||
while ((test_format = *(closefmts++)) != 0) {
|
||||
if (test_format == SDL_AUDIO_S16LE) {
|
||||
device->spec.format = test_format;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!test_format) {
|
||||
return SDL_SetError("Unsupported audio format");
|
||||
}
|
||||
|
||||
if (device->iscapture) {
|
||||
return VITAAUD_OpenCaptureDevice(device);
|
||||
}
|
||||
|
||||
// The sample count must be a multiple of 64.
|
||||
device->sample_frames = SCE_AUDIO_SAMPLE_ALIGN(device->sample_frames);
|
||||
|
||||
// Update the fragment size as size in bytes.
|
||||
SDL_UpdatedAudioDeviceFormat(device);
|
||||
|
||||
/* Allocate the mixing buffer. Its size and starting address must
|
||||
be a multiple of 64 bytes. Our sample count is already a multiple of
|
||||
64, so spec->size should be a multiple of 64 as well. */
|
||||
mixlen = device->buffer_size * NUM_BUFFERS;
|
||||
device->hidden->rawbuf = (Uint8 *)SDL_aligned_alloc(64, mixlen);
|
||||
if (device->hidden->rawbuf == NULL) {
|
||||
return SDL_SetError("Couldn't allocate mixing buffer");
|
||||
}
|
||||
|
||||
// Setup the hardware channel.
|
||||
if (device->spec.channels == 1) {
|
||||
format = SCE_AUDIO_OUT_MODE_MONO;
|
||||
} else {
|
||||
format = SCE_AUDIO_OUT_MODE_STEREO;
|
||||
}
|
||||
|
||||
// the main port requires 48000Hz audio, so this drops to the background music port if necessary
|
||||
if (device->spec.freq < 48000) {
|
||||
port = SCE_AUDIO_OUT_PORT_TYPE_BGM;
|
||||
}
|
||||
|
||||
device->hidden->port = sceAudioOutOpenPort(port, device->sample_frames, device->spec.freq, format);
|
||||
if (device->hidden->port < 0) {
|
||||
SDL_aligned_free(device->hidden->rawbuf);
|
||||
device->hidden->rawbuf = NULL;
|
||||
return SDL_SetError("Couldn't open audio out port: %x", device->hidden->port);
|
||||
}
|
||||
|
||||
sceAudioOutSetVolume(device->hidden->port, SCE_AUDIO_VOLUME_FLAG_L_CH | SCE_AUDIO_VOLUME_FLAG_R_CH, vols);
|
||||
|
||||
SDL_memset(device->hidden->rawbuf, 0, mixlen);
|
||||
for (i = 0; i < NUM_BUFFERS; i++) {
|
||||
device->hidden->mixbufs[i] = &device->hidden->rawbuf[i * device->buffer_size];
|
||||
}
|
||||
|
||||
device->hidden->next_buffer = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int VITAAUD_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buffer_size)
|
||||
{
|
||||
return (sceAudioOutOutput(device->hidden->port, buffer) == 0) ? 0 : -1;
|
||||
}
|
||||
|
||||
// This function waits until it is possible to write a full sound buffer
|
||||
static int VITAAUD_WaitDevice(SDL_AudioDevice *device)
|
||||
{
|
||||
// !!! FIXME: we might just need to sleep roughly as long as playback buffers take to process, based on sample rate, etc.
|
||||
while (!SDL_AtomicGet(&device->shutdown) && (sceAudioOutGetRestSample(device->hidden->port) >= device->buffer_size)) {
|
||||
SDL_Delay(1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static Uint8 *VITAAUD_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
|
||||
{
|
||||
Uint8 *retval = device->hidden->mixbufs[device->hidden->next_buffer];
|
||||
device->hidden->next_buffer = (device->hidden->next_buffer + 1) % NUM_BUFFERS;
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void VITAAUD_CloseDevice(SDL_AudioDevice *device)
|
||||
{
|
||||
if (device->hidden) {
|
||||
if (device->hidden->port >= 0) {
|
||||
if (device->iscapture) {
|
||||
sceAudioInReleasePort(device->hidden->port);
|
||||
} else {
|
||||
sceAudioOutReleasePort(device->hidden->port);
|
||||
}
|
||||
device->hidden->port = -1;
|
||||
}
|
||||
|
||||
if (!device->iscapture && device->hidden->rawbuf != NULL) {
|
||||
SDL_aligned_free(device->hidden->rawbuf); // this uses SDL_aligned_alloc(), not SDL_malloc()
|
||||
device->hidden->rawbuf = NULL;
|
||||
}
|
||||
SDL_free(device->hidden);
|
||||
device->hidden = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int VITAAUD_WaitCaptureDevice(SDL_AudioDevice *device)
|
||||
{
|
||||
// there's only a blocking call to obtain more data, so we'll just sleep as
|
||||
// long as a buffer would run.
|
||||
const Uint64 endticks = SDL_GetTicks() + ((device->sample_frames * 1000) / device->spec.freq);
|
||||
while (!SDL_AtomicGet(&device->shutdown) && (SDL_GetTicks() < endticks)) {
|
||||
SDL_Delay(1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int VITAAUD_CaptureFromDevice(SDL_AudioDevice *device, void *buffer, int buflen)
|
||||
{
|
||||
int ret;
|
||||
SDL_assert(buflen == device->buffer_size);
|
||||
ret = sceAudioInInput(device->hidden->port, buffer);
|
||||
if (ret < 0) {
|
||||
return SDL_SetError("Failed to capture from device: %x", ret);
|
||||
}
|
||||
return device->buffer_size;
|
||||
}
|
||||
|
||||
static void VITAAUD_FlushCapture(SDL_AudioDevice *device)
|
||||
{
|
||||
// just grab the latest and dump it.
|
||||
sceAudioInInput(device->hidden->port, device->work_buffer);
|
||||
}
|
||||
|
||||
static void VITAAUD_ThreadInit(SDL_AudioDevice *device)
|
||||
{
|
||||
// Increase the priority of this audio thread by 1 to put it ahead of other SDL threads.
|
||||
SceUID thid;
|
||||
SceKernelThreadInfo info;
|
||||
thid = sceKernelGetThreadId();
|
||||
info.size = sizeof(SceKernelThreadInfo);
|
||||
if (sceKernelGetThreadInfo(thid, &info) == 0) {
|
||||
sceKernelChangeThreadPriority(thid, info.currentPriority - 1);
|
||||
}
|
||||
}
|
||||
|
||||
static SDL_bool VITAAUD_Init(SDL_AudioDriverImpl *impl)
|
||||
{
|
||||
impl->OpenDevice = VITAAUD_OpenDevice;
|
||||
impl->PlayDevice = VITAAUD_PlayDevice;
|
||||
impl->WaitDevice = VITAAUD_WaitDevice;
|
||||
impl->GetDeviceBuf = VITAAUD_GetDeviceBuf;
|
||||
impl->CloseDevice = VITAAUD_CloseDevice;
|
||||
impl->ThreadInit = VITAAUD_ThreadInit;
|
||||
impl->WaitCaptureDevice = VITAAUD_WaitCaptureDevice;
|
||||
impl->FlushCapture = VITAAUD_FlushCapture;
|
||||
impl->CaptureFromDevice = VITAAUD_CaptureFromDevice;
|
||||
|
||||
impl->HasCaptureSupport = SDL_TRUE;
|
||||
impl->OnlyHasDefaultOutputDevice = SDL_TRUE;
|
||||
impl->OnlyHasDefaultCaptureDevice = SDL_TRUE;
|
||||
|
||||
return SDL_TRUE;
|
||||
}
|
||||
|
||||
AudioBootStrap VITAAUD_bootstrap = {
|
||||
"vita", "VITA audio driver", VITAAUD_Init, SDL_FALSE
|
||||
};
|
||||
|
||||
#endif // SDL_AUDIO_DRIVER_VITA
|
||||
41
vendor/sdl-3.0.0/src/audio/vita/SDL_vitaaudio.h
vendored
Normal file
41
vendor/sdl-3.0.0/src/audio/vita/SDL_vitaaudio.h
vendored
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
#ifndef SDL_vitaaudio_h
|
||||
#define SDL_vitaaudio_h
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
|
||||
#define NUM_BUFFERS 2
|
||||
|
||||
struct SDL_PrivateAudioData
|
||||
{
|
||||
// The hardware input/output port.
|
||||
int port;
|
||||
// The raw allocated mixing buffer.
|
||||
Uint8 *rawbuf;
|
||||
// Individual mixing buffers.
|
||||
Uint8 *mixbufs[NUM_BUFFERS];
|
||||
// Index of the next available mixing buffer.
|
||||
int next_buffer;
|
||||
};
|
||||
|
||||
#endif // SDL_vitaaudio_h
|
||||
765
vendor/sdl-3.0.0/src/audio/wasapi/SDL_wasapi.c
vendored
Normal file
765
vendor/sdl-3.0.0/src/audio/wasapi/SDL_wasapi.c
vendored
Normal file
|
|
@ -0,0 +1,765 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifdef SDL_AUDIO_DRIVER_WASAPI
|
||||
|
||||
#include "../../core/windows/SDL_windows.h"
|
||||
#include "../../core/windows/SDL_immdevice.h"
|
||||
#include "../../thread/SDL_systhread.h"
|
||||
#include "../SDL_sysaudio.h"
|
||||
|
||||
#define COBJMACROS
|
||||
#include <audioclient.h>
|
||||
|
||||
#include "SDL_wasapi.h"
|
||||
|
||||
// These constants aren't available in older SDKs
|
||||
#ifndef AUDCLNT_STREAMFLAGS_RATEADJUST
|
||||
#define AUDCLNT_STREAMFLAGS_RATEADJUST 0x00100000
|
||||
#endif
|
||||
#ifndef AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY
|
||||
#define AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY 0x08000000
|
||||
#endif
|
||||
#ifndef AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM
|
||||
#define AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM 0x80000000
|
||||
#endif
|
||||
|
||||
// Some GUIDs we need to know without linking to libraries that aren't available before Vista.
|
||||
static const IID SDL_IID_IAudioRenderClient = { 0xf294acfc, 0x3146, 0x4483, { 0xa7, 0xbf, 0xad, 0xdc, 0xa7, 0xc2, 0x60, 0xe2 } };
|
||||
static const IID SDL_IID_IAudioCaptureClient = { 0xc8adbd64, 0xe71e, 0x48a0, { 0xa4, 0xde, 0x18, 0x5c, 0x39, 0x5c, 0xd3, 0x17 } };
|
||||
|
||||
|
||||
// WASAPI is _really_ particular about various things happening on the same thread, for COM and such,
|
||||
// so we proxy various stuff to a single background thread to manage.
|
||||
|
||||
typedef struct ManagementThreadPendingTask
|
||||
{
|
||||
ManagementThreadTask fn;
|
||||
void *userdata;
|
||||
int result;
|
||||
SDL_Semaphore *task_complete_sem;
|
||||
char *errorstr;
|
||||
struct ManagementThreadPendingTask *next;
|
||||
} ManagementThreadPendingTask;
|
||||
|
||||
static SDL_Thread *ManagementThread = NULL;
|
||||
static ManagementThreadPendingTask *ManagementThreadPendingTasks = NULL;
|
||||
static SDL_Mutex *ManagementThreadLock = NULL;
|
||||
static SDL_Condition *ManagementThreadCondition = NULL;
|
||||
static SDL_AtomicInt ManagementThreadShutdown;
|
||||
|
||||
static void ManagementThreadMainloop(void)
|
||||
{
|
||||
SDL_LockMutex(ManagementThreadLock);
|
||||
ManagementThreadPendingTask *task;
|
||||
while (((task = SDL_AtomicGetPtr((void **) &ManagementThreadPendingTasks)) != NULL) || !SDL_AtomicGet(&ManagementThreadShutdown)) {
|
||||
if (!task) {
|
||||
SDL_WaitCondition(ManagementThreadCondition, ManagementThreadLock); // block until there's something to do.
|
||||
} else {
|
||||
SDL_AtomicSetPtr((void **) &ManagementThreadPendingTasks, task->next); // take task off the pending list.
|
||||
SDL_UnlockMutex(ManagementThreadLock); // let other things add to the list while we chew on this task.
|
||||
task->result = task->fn(task->userdata); // run this task.
|
||||
if (task->task_complete_sem) { // something waiting on result?
|
||||
task->errorstr = SDL_strdup(SDL_GetError());
|
||||
SDL_PostSemaphore(task->task_complete_sem);
|
||||
} else { // nothing waiting, we're done, free it.
|
||||
SDL_free(task);
|
||||
}
|
||||
SDL_LockMutex(ManagementThreadLock); // regrab the lock so we can get the next task; if nothing to do, we'll release the lock in SDL_WaitCondition.
|
||||
}
|
||||
}
|
||||
SDL_UnlockMutex(ManagementThreadLock); // told to shut down and out of tasks, let go of the lock and return.
|
||||
}
|
||||
|
||||
int WASAPI_ProxyToManagementThread(ManagementThreadTask task, void *userdata, int *wait_on_result)
|
||||
{
|
||||
// We want to block for a result, but we are already running from the management thread! Just run the task now so we don't deadlock.
|
||||
if ((wait_on_result != NULL) && (SDL_ThreadID() == SDL_GetThreadID(ManagementThread))) {
|
||||
*wait_on_result = task(userdata);
|
||||
return 0; // completed!
|
||||
}
|
||||
|
||||
if (SDL_AtomicGet(&ManagementThreadShutdown)) {
|
||||
return SDL_SetError("Can't add task, we're shutting down");
|
||||
}
|
||||
|
||||
ManagementThreadPendingTask *pending = SDL_calloc(1, sizeof(ManagementThreadPendingTask));
|
||||
if (!pending) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
|
||||
pending->fn = task;
|
||||
pending->userdata = userdata;
|
||||
|
||||
if (wait_on_result) {
|
||||
pending->task_complete_sem = SDL_CreateSemaphore(0);
|
||||
if (!pending->task_complete_sem) {
|
||||
SDL_free(pending);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
pending->next = NULL;
|
||||
|
||||
SDL_LockMutex(ManagementThreadLock);
|
||||
|
||||
// add to end of task list.
|
||||
ManagementThreadPendingTask *prev = NULL;
|
||||
for (ManagementThreadPendingTask *i = SDL_AtomicGetPtr((void **) &ManagementThreadPendingTasks); i != NULL; i = i->next) {
|
||||
prev = i;
|
||||
}
|
||||
|
||||
if (prev != NULL) {
|
||||
prev->next = pending;
|
||||
} else {
|
||||
SDL_AtomicSetPtr((void **) &ManagementThreadPendingTasks, pending);
|
||||
}
|
||||
|
||||
// task is added to the end of the pending list, let management thread rip!
|
||||
SDL_SignalCondition(ManagementThreadCondition);
|
||||
SDL_UnlockMutex(ManagementThreadLock);
|
||||
|
||||
if (wait_on_result) {
|
||||
SDL_WaitSemaphore(pending->task_complete_sem);
|
||||
SDL_DestroySemaphore(pending->task_complete_sem);
|
||||
*wait_on_result = pending->result;
|
||||
if (pending->errorstr) {
|
||||
SDL_SetError("%s", pending->errorstr);
|
||||
SDL_free(pending->errorstr);
|
||||
}
|
||||
SDL_free(pending);
|
||||
}
|
||||
|
||||
return 0; // successfully added (and possibly executed)!
|
||||
}
|
||||
|
||||
static int ManagementThreadPrepare(void)
|
||||
{
|
||||
if (WASAPI_PlatformInit() == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ManagementThreadLock = SDL_CreateMutex();
|
||||
if (!ManagementThreadLock) {
|
||||
WASAPI_PlatformDeinit();
|
||||
return -1;
|
||||
}
|
||||
|
||||
ManagementThreadCondition = SDL_CreateCondition();
|
||||
if (!ManagementThreadCondition) {
|
||||
SDL_DestroyMutex(ManagementThreadLock);
|
||||
ManagementThreadLock = NULL;
|
||||
WASAPI_PlatformDeinit();
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char *errorstr;
|
||||
SDL_Semaphore *ready_sem;
|
||||
} ManagementThreadEntryData;
|
||||
|
||||
static int ManagementThreadEntry(void *userdata)
|
||||
{
|
||||
ManagementThreadEntryData *data = (ManagementThreadEntryData *)userdata;
|
||||
|
||||
if (ManagementThreadPrepare() < 0) {
|
||||
data->errorstr = SDL_strdup(SDL_GetError());
|
||||
SDL_PostSemaphore(data->ready_sem); // unblock calling thread.
|
||||
return 0;
|
||||
}
|
||||
|
||||
SDL_PostSemaphore(data->ready_sem); // unblock calling thread.
|
||||
ManagementThreadMainloop();
|
||||
|
||||
WASAPI_PlatformDeinit();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int InitManagementThread(void)
|
||||
{
|
||||
ManagementThreadEntryData mgmtdata;
|
||||
SDL_zero(mgmtdata);
|
||||
mgmtdata.ready_sem = SDL_CreateSemaphore(0);
|
||||
if (!mgmtdata.ready_sem) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
SDL_AtomicSetPtr((void **) &ManagementThreadPendingTasks, NULL);
|
||||
SDL_AtomicSet(&ManagementThreadShutdown, 0);
|
||||
ManagementThread = SDL_CreateThreadInternal(ManagementThreadEntry, "SDLWASAPIMgmt", 256 * 1024, &mgmtdata); // !!! FIXME: maybe even smaller stack size?
|
||||
if (!ManagementThread) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
SDL_WaitSemaphore(mgmtdata.ready_sem);
|
||||
SDL_DestroySemaphore(mgmtdata.ready_sem);
|
||||
|
||||
if (mgmtdata.errorstr) {
|
||||
SDL_WaitThread(ManagementThread, NULL);
|
||||
ManagementThread = NULL;
|
||||
SDL_SetError("%s", mgmtdata.errorstr);
|
||||
SDL_free(mgmtdata.errorstr);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void DeinitManagementThread(void)
|
||||
{
|
||||
if (ManagementThread) {
|
||||
SDL_AtomicSet(&ManagementThreadShutdown, 1);
|
||||
SDL_LockMutex(ManagementThreadLock);
|
||||
SDL_SignalCondition(ManagementThreadCondition);
|
||||
SDL_UnlockMutex(ManagementThreadLock);
|
||||
SDL_WaitThread(ManagementThread, NULL);
|
||||
ManagementThread = NULL;
|
||||
}
|
||||
|
||||
SDL_assert(SDL_AtomicGetPtr((void **) &ManagementThreadPendingTasks) == NULL);
|
||||
|
||||
SDL_DestroyCondition(ManagementThreadCondition);
|
||||
SDL_DestroyMutex(ManagementThreadLock);
|
||||
ManagementThreadCondition = NULL;
|
||||
ManagementThreadLock = NULL;
|
||||
SDL_AtomicSet(&ManagementThreadShutdown, 0);
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
SDL_AudioDevice **default_output;
|
||||
SDL_AudioDevice **default_capture;
|
||||
} mgmtthrtask_DetectDevicesData;
|
||||
|
||||
static int mgmtthrtask_DetectDevices(void *userdata)
|
||||
{
|
||||
mgmtthrtask_DetectDevicesData *data = (mgmtthrtask_DetectDevicesData *)userdata;
|
||||
WASAPI_EnumerateEndpoints(data->default_output, data->default_capture);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void WASAPI_DetectDevices(SDL_AudioDevice **default_output, SDL_AudioDevice **default_capture)
|
||||
{
|
||||
int rc;
|
||||
// this blocks because it needs to finish before the audio subsystem inits
|
||||
mgmtthrtask_DetectDevicesData data = { default_output, default_capture };
|
||||
WASAPI_ProxyToManagementThread(mgmtthrtask_DetectDevices, &data, &rc);
|
||||
}
|
||||
|
||||
static int mgmtthrtask_DisconnectDevice(void *userdata)
|
||||
{
|
||||
SDL_AudioDeviceDisconnected((SDL_AudioDevice *)userdata);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void WASAPI_DisconnectDevice(SDL_AudioDevice *device)
|
||||
{
|
||||
int rc; // block on this; don't disconnect while holding the device lock!
|
||||
WASAPI_ProxyToManagementThread(mgmtthrtask_DisconnectDevice, device, &rc);
|
||||
}
|
||||
|
||||
static SDL_bool WasapiFailed(SDL_AudioDevice *device, const HRESULT err)
|
||||
{
|
||||
if (err == S_OK) {
|
||||
return SDL_FALSE;
|
||||
} else if (err == AUDCLNT_E_DEVICE_INVALIDATED) {
|
||||
device->hidden->device_lost = SDL_TRUE;
|
||||
} else {
|
||||
device->hidden->device_dead = SDL_TRUE;
|
||||
}
|
||||
|
||||
return SDL_TRUE;
|
||||
}
|
||||
|
||||
static int mgmtthrtask_ReleaseCaptureClient(void *userdata)
|
||||
{
|
||||
IAudioCaptureClient_Release((IAudioCaptureClient *)userdata);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mgmtthrtask_ReleaseRenderClient(void *userdata)
|
||||
{
|
||||
IAudioRenderClient_Release((IAudioRenderClient *)userdata);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mgmtthrtask_ResetWasapiDevice(void *userdata)
|
||||
{
|
||||
SDL_AudioDevice *device = (SDL_AudioDevice *)userdata;
|
||||
|
||||
if (!device || !device->hidden) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (device->hidden->client) {
|
||||
IAudioClient_Stop(device->hidden->client);
|
||||
IAudioClient_Release(device->hidden->client);
|
||||
device->hidden->client = NULL;
|
||||
}
|
||||
|
||||
if (device->hidden->render) {
|
||||
// this is silly, but this will block indefinitely if you call it from SDLMMNotificationClient_OnDefaultDeviceChanged, so
|
||||
// proxy this to the management thread to be released later.
|
||||
WASAPI_ProxyToManagementThread(mgmtthrtask_ReleaseRenderClient, device->hidden->render, NULL);
|
||||
device->hidden->render = NULL;
|
||||
}
|
||||
|
||||
if (device->hidden->capture) {
|
||||
// this is silly, but this will block indefinitely if you call it from SDLMMNotificationClient_OnDefaultDeviceChanged, so
|
||||
// proxy this to the management thread to be released later.
|
||||
WASAPI_ProxyToManagementThread(mgmtthrtask_ReleaseCaptureClient, device->hidden->capture, NULL);
|
||||
device->hidden->capture = NULL;
|
||||
}
|
||||
|
||||
if (device->hidden->waveformat) {
|
||||
CoTaskMemFree(device->hidden->waveformat);
|
||||
device->hidden->waveformat = NULL;
|
||||
}
|
||||
|
||||
if (device->hidden->activation_handler) {
|
||||
WASAPI_PlatformDeleteActivationHandler(device->hidden->activation_handler);
|
||||
device->hidden->activation_handler = NULL;
|
||||
}
|
||||
|
||||
if (device->hidden->event) {
|
||||
CloseHandle(device->hidden->event);
|
||||
device->hidden->event = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ResetWasapiDevice(SDL_AudioDevice *device)
|
||||
{
|
||||
int rc;
|
||||
WASAPI_ProxyToManagementThread(mgmtthrtask_ResetWasapiDevice, device, &rc);
|
||||
}
|
||||
|
||||
static int mgmtthrtask_ActivateDevice(void *userdata)
|
||||
{
|
||||
return WASAPI_ActivateDevice((SDL_AudioDevice *)userdata);
|
||||
}
|
||||
|
||||
static int ActivateWasapiDevice(SDL_AudioDevice *device)
|
||||
{
|
||||
// this blocks because we're either being notified from a background thread or we're running during device open,
|
||||
// both of which won't deadlock vs the device thread.
|
||||
int rc = -1;
|
||||
return ((WASAPI_ProxyToManagementThread(mgmtthrtask_ActivateDevice, device, &rc) < 0) || (rc < 0)) ? -1 : 0;
|
||||
}
|
||||
|
||||
// do not call when holding the device lock!
|
||||
static SDL_bool RecoverWasapiDevice(SDL_AudioDevice *device)
|
||||
{
|
||||
ResetWasapiDevice(device); // dump the lost device's handles.
|
||||
|
||||
// This handles a non-default device that simply had its format changed in the Windows Control Panel.
|
||||
if (ActivateWasapiDevice(device) == -1) {
|
||||
WASAPI_DisconnectDevice(device);
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
device->hidden->device_lost = SDL_FALSE;
|
||||
|
||||
return SDL_TRUE; // okay, carry on with new device details!
|
||||
}
|
||||
|
||||
// do not call when holding the device lock!
|
||||
static SDL_bool RecoverWasapiIfLost(SDL_AudioDevice *device)
|
||||
{
|
||||
if (SDL_AtomicGet(&device->shutdown)) {
|
||||
return SDL_FALSE; // already failed.
|
||||
} else if (device->hidden->device_dead) { // had a fatal error elsewhere, clean up and quit
|
||||
IAudioClient_Stop(device->hidden->client);
|
||||
WASAPI_DisconnectDevice(device);
|
||||
SDL_assert(SDL_AtomicGet(&device->shutdown)); // so we don't come back through here.
|
||||
return SDL_FALSE; // already failed.
|
||||
} else if (SDL_AtomicGet(&device->zombie)) {
|
||||
return SDL_FALSE; // we're already dead, so just leave and let the Zombie implementations take over.
|
||||
} else if (!device->hidden->client) {
|
||||
return SDL_TRUE; // still waiting for activation.
|
||||
}
|
||||
|
||||
return device->hidden->device_lost ? RecoverWasapiDevice(device) : SDL_TRUE;
|
||||
}
|
||||
|
||||
static Uint8 *WASAPI_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
|
||||
{
|
||||
// get an endpoint buffer from WASAPI.
|
||||
BYTE *buffer = NULL;
|
||||
|
||||
if (device->hidden->render) {
|
||||
if (WasapiFailed(device, IAudioRenderClient_GetBuffer(device->hidden->render, device->sample_frames, &buffer))) {
|
||||
SDL_assert(buffer == NULL);
|
||||
if (device->hidden->device_lost) { // just use an available buffer, we won't be playing it anyhow.
|
||||
*buffer_size = 0; // we'll recover during WaitDevice and try again.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (Uint8 *)buffer;
|
||||
}
|
||||
|
||||
static int WASAPI_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
|
||||
{
|
||||
if (device->hidden->render != NULL) { // definitely activated?
|
||||
// WasapiFailed() will mark the device for reacquisition or removal elsewhere.
|
||||
WasapiFailed(device, IAudioRenderClient_ReleaseBuffer(device->hidden->render, device->sample_frames, 0));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int WASAPI_WaitDevice(SDL_AudioDevice *device)
|
||||
{
|
||||
// WaitDevice does not hold the device lock, so check for recovery/disconnect details here.
|
||||
while (RecoverWasapiIfLost(device) && device->hidden->client && device->hidden->event) {
|
||||
DWORD waitResult = WaitForSingleObjectEx(device->hidden->event, 200, FALSE);
|
||||
if (waitResult == WAIT_OBJECT_0) {
|
||||
const UINT32 maxpadding = device->sample_frames;
|
||||
UINT32 padding = 0;
|
||||
if (!WasapiFailed(device, IAudioClient_GetCurrentPadding(device->hidden->client, &padding))) {
|
||||
//SDL_Log("WASAPI EVENT! padding=%u maxpadding=%u", (unsigned int)padding, (unsigned int)maxpadding);*/
|
||||
if (device->iscapture && (padding > 0)) {
|
||||
break;
|
||||
} else if (!device->iscapture && (padding <= maxpadding)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (waitResult != WAIT_TIMEOUT) {
|
||||
//SDL_Log("WASAPI FAILED EVENT!");*/
|
||||
IAudioClient_Stop(device->hidden->client);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int WASAPI_CaptureFromDevice(SDL_AudioDevice *device, void *buffer, int buflen)
|
||||
{
|
||||
BYTE *ptr = NULL;
|
||||
UINT32 frames = 0;
|
||||
DWORD flags = 0;
|
||||
|
||||
while (device->hidden->capture) {
|
||||
const HRESULT ret = IAudioCaptureClient_GetBuffer(device->hidden->capture, &ptr, &frames, &flags, NULL, NULL);
|
||||
if (ret == AUDCLNT_S_BUFFER_EMPTY) {
|
||||
return 0; // in theory we should have waited until there was data, but oh well, we'll go back to waiting. Returning 0 is safe in SDL3.
|
||||
}
|
||||
|
||||
WasapiFailed(device, ret); // mark device lost/failed if necessary.
|
||||
|
||||
if (ret == S_OK) {
|
||||
const int total = ((int)frames) * device->hidden->framesize;
|
||||
const int cpy = SDL_min(buflen, total);
|
||||
const int leftover = total - cpy;
|
||||
const SDL_bool silent = (flags & AUDCLNT_BUFFERFLAGS_SILENT) ? SDL_TRUE : SDL_FALSE;
|
||||
|
||||
SDL_assert(leftover == 0); // according to MSDN, this isn't everything available, just one "packet" of data per-GetBuffer call.
|
||||
|
||||
if (silent) {
|
||||
SDL_memset(buffer, device->silence_value, cpy);
|
||||
} else {
|
||||
SDL_memcpy(buffer, ptr, cpy);
|
||||
}
|
||||
|
||||
WasapiFailed(device, IAudioCaptureClient_ReleaseBuffer(device->hidden->capture, frames));
|
||||
|
||||
return cpy;
|
||||
}
|
||||
}
|
||||
|
||||
return -1; // unrecoverable error.
|
||||
}
|
||||
|
||||
static void WASAPI_FlushCapture(SDL_AudioDevice *device)
|
||||
{
|
||||
BYTE *ptr = NULL;
|
||||
UINT32 frames = 0;
|
||||
DWORD flags = 0;
|
||||
|
||||
// just read until we stop getting packets, throwing them away.
|
||||
while (!SDL_AtomicGet(&device->shutdown) && device->hidden->capture) {
|
||||
const HRESULT ret = IAudioCaptureClient_GetBuffer(device->hidden->capture, &ptr, &frames, &flags, NULL, NULL);
|
||||
if (ret == AUDCLNT_S_BUFFER_EMPTY) {
|
||||
break; // no more buffered data; we're done.
|
||||
} else if (WasapiFailed(device, ret)) {
|
||||
break; // failed for some other reason, abort.
|
||||
} else if (WasapiFailed(device, IAudioCaptureClient_ReleaseBuffer(device->hidden->capture, frames))) {
|
||||
break; // something broke.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void WASAPI_CloseDevice(SDL_AudioDevice *device)
|
||||
{
|
||||
if (device->hidden) {
|
||||
ResetWasapiDevice(device);
|
||||
SDL_free(device->hidden->devid);
|
||||
SDL_free(device->hidden);
|
||||
device->hidden = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int mgmtthrtask_PrepDevice(void *userdata)
|
||||
{
|
||||
SDL_AudioDevice *device = (SDL_AudioDevice *)userdata;
|
||||
|
||||
/* !!! FIXME: we could request an exclusive mode stream, which is lower latency;
|
||||
!!! it will write into the kernel's audio buffer directly instead of
|
||||
!!! shared memory that a user-mode mixer then writes to the kernel with
|
||||
!!! everything else. Doing this means any other sound using this device will
|
||||
!!! stop playing, including the user's MP3 player and system notification
|
||||
!!! sounds. You'd probably need to release the device when the app isn't in
|
||||
!!! the foreground, to be a good citizen of the system. It's doable, but it's
|
||||
!!! more work and causes some annoyances, and I don't know what the latency
|
||||
!!! wins actually look like. Maybe add a hint to force exclusive mode at
|
||||
!!! some point. To be sure, defaulting to shared mode is the right thing to
|
||||
!!! do in any case. */
|
||||
const AUDCLNT_SHAREMODE sharemode = AUDCLNT_SHAREMODE_SHARED;
|
||||
|
||||
IAudioClient *client = device->hidden->client;
|
||||
SDL_assert(client != NULL);
|
||||
|
||||
#if defined(__WINRT__) || defined(__GDK__) // CreateEventEx() arrived in Vista, so we need an #ifdef for XP.
|
||||
device->hidden->event = CreateEventEx(NULL, NULL, 0, EVENT_ALL_ACCESS);
|
||||
#else
|
||||
device->hidden->event = CreateEventW(NULL, 0, 0, NULL);
|
||||
#endif
|
||||
|
||||
if (device->hidden->event == NULL) {
|
||||
return WIN_SetError("WASAPI can't create an event handle");
|
||||
}
|
||||
|
||||
HRESULT ret;
|
||||
|
||||
WAVEFORMATEX *waveformat = NULL;
|
||||
ret = IAudioClient_GetMixFormat(client, &waveformat);
|
||||
if (FAILED(ret)) {
|
||||
return WIN_SetErrorFromHRESULT("WASAPI can't determine mix format", ret);
|
||||
}
|
||||
SDL_assert(waveformat != NULL);
|
||||
device->hidden->waveformat = waveformat;
|
||||
|
||||
SDL_AudioSpec newspec;
|
||||
newspec.channels = (Uint8)waveformat->nChannels;
|
||||
|
||||
// Make sure we have a valid format that we can convert to whatever WASAPI wants.
|
||||
const SDL_AudioFormat wasapi_format = SDL_WaveFormatExToSDLFormat(waveformat);
|
||||
|
||||
SDL_AudioFormat test_format;
|
||||
const SDL_AudioFormat *closefmts = SDL_ClosestAudioFormats(device->spec.format);
|
||||
while ((test_format = *(closefmts++)) != 0) {
|
||||
if (test_format == wasapi_format) {
|
||||
newspec.format = test_format;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!test_format) {
|
||||
return SDL_SetError("%s: Unsupported audio format", "wasapi");
|
||||
}
|
||||
|
||||
REFERENCE_TIME default_period = 0;
|
||||
ret = IAudioClient_GetDevicePeriod(client, &default_period, NULL);
|
||||
if (FAILED(ret)) {
|
||||
return WIN_SetErrorFromHRESULT("WASAPI can't determine minimum device period", ret);
|
||||
}
|
||||
|
||||
DWORD streamflags = 0;
|
||||
|
||||
/* we've gotten reports that WASAPI's resampler introduces distortions, but in the short term
|
||||
it fixes some other WASAPI-specific quirks we haven't quite tracked down.
|
||||
Refer to bug #6326 for the immediate concern. */
|
||||
#if 1
|
||||
// favor WASAPI's resampler over our own
|
||||
if ((DWORD)device->spec.freq != waveformat->nSamplesPerSec) {
|
||||
streamflags |= (AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY);
|
||||
waveformat->nSamplesPerSec = device->spec.freq;
|
||||
waveformat->nAvgBytesPerSec = waveformat->nSamplesPerSec * waveformat->nChannels * (waveformat->wBitsPerSample / 8);
|
||||
}
|
||||
#endif
|
||||
|
||||
newspec.freq = waveformat->nSamplesPerSec;
|
||||
|
||||
streamflags |= AUDCLNT_STREAMFLAGS_EVENTCALLBACK;
|
||||
ret = IAudioClient_Initialize(client, sharemode, streamflags, 0, 0, waveformat, NULL);
|
||||
if (FAILED(ret)) {
|
||||
return WIN_SetErrorFromHRESULT("WASAPI can't initialize audio client", ret);
|
||||
}
|
||||
|
||||
ret = IAudioClient_SetEventHandle(client, device->hidden->event);
|
||||
if (FAILED(ret)) {
|
||||
return WIN_SetErrorFromHRESULT("WASAPI can't set event handle", ret);
|
||||
}
|
||||
|
||||
UINT32 bufsize = 0; // this is in sample frames, not samples, not bytes.
|
||||
ret = IAudioClient_GetBufferSize(client, &bufsize);
|
||||
if (FAILED(ret)) {
|
||||
return WIN_SetErrorFromHRESULT("WASAPI can't determine buffer size", ret);
|
||||
}
|
||||
|
||||
/* Match the callback size to the period size to cut down on the number of
|
||||
interrupts waited for in each call to WaitDevice */
|
||||
const float period_millis = default_period / 10000.0f;
|
||||
const float period_frames = period_millis * newspec.freq / 1000.0f;
|
||||
const int new_sample_frames = (int) SDL_ceilf(period_frames);
|
||||
|
||||
// Update the fragment size as size in bytes
|
||||
if (SDL_AudioDeviceFormatChangedAlreadyLocked(device, &newspec, new_sample_frames) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
device->hidden->framesize = SDL_AUDIO_FRAMESIZE(device->spec);
|
||||
|
||||
if (device->iscapture) {
|
||||
IAudioCaptureClient *capture = NULL;
|
||||
ret = IAudioClient_GetService(client, &SDL_IID_IAudioCaptureClient, (void **)&capture);
|
||||
if (FAILED(ret)) {
|
||||
return WIN_SetErrorFromHRESULT("WASAPI can't get capture client service", ret);
|
||||
}
|
||||
|
||||
SDL_assert(capture != NULL);
|
||||
device->hidden->capture = capture;
|
||||
ret = IAudioClient_Start(client);
|
||||
if (FAILED(ret)) {
|
||||
return WIN_SetErrorFromHRESULT("WASAPI can't start capture", ret);
|
||||
}
|
||||
|
||||
WASAPI_FlushCapture(device); // MSDN says you should flush capture endpoint right after startup.
|
||||
} else {
|
||||
IAudioRenderClient *render = NULL;
|
||||
ret = IAudioClient_GetService(client, &SDL_IID_IAudioRenderClient, (void **)&render);
|
||||
if (FAILED(ret)) {
|
||||
return WIN_SetErrorFromHRESULT("WASAPI can't get render client service", ret);
|
||||
}
|
||||
|
||||
SDL_assert(render != NULL);
|
||||
device->hidden->render = render;
|
||||
ret = IAudioClient_Start(client);
|
||||
if (FAILED(ret)) {
|
||||
return WIN_SetErrorFromHRESULT("WASAPI can't start playback", ret);
|
||||
}
|
||||
}
|
||||
|
||||
return 0; // good to go.
|
||||
}
|
||||
|
||||
// This is called once a device is activated, possibly asynchronously.
|
||||
int WASAPI_PrepDevice(SDL_AudioDevice *device)
|
||||
{
|
||||
int rc = 0;
|
||||
return (WASAPI_ProxyToManagementThread(mgmtthrtask_PrepDevice, device, &rc) < 0) ? -1 : rc;
|
||||
}
|
||||
|
||||
static int WASAPI_OpenDevice(SDL_AudioDevice *device)
|
||||
{
|
||||
// Initialize all variables that we clean on shutdown
|
||||
device->hidden = (struct SDL_PrivateAudioData *) SDL_calloc(1, sizeof(*device->hidden));
|
||||
if (device->hidden == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
} else if (ActivateWasapiDevice(device) < 0) {
|
||||
return -1; // already set error.
|
||||
}
|
||||
|
||||
/* Ready, but possibly waiting for async device activation.
|
||||
Until activation is successful, we will report silence from capture
|
||||
devices and ignore data on playback devices. Upon activation, we'll make
|
||||
sure any bound audio streams are adjusted for the final device format. */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void WASAPI_ThreadInit(SDL_AudioDevice *device)
|
||||
{
|
||||
WASAPI_PlatformThreadInit(device);
|
||||
}
|
||||
|
||||
static void WASAPI_ThreadDeinit(SDL_AudioDevice *device)
|
||||
{
|
||||
WASAPI_PlatformThreadDeinit(device);
|
||||
}
|
||||
|
||||
static int mgmtthrtask_FreeDeviceHandle(void *userdata)
|
||||
{
|
||||
WASAPI_PlatformFreeDeviceHandle((SDL_AudioDevice *)userdata);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void WASAPI_FreeDeviceHandle(SDL_AudioDevice *device)
|
||||
{
|
||||
int rc;
|
||||
WASAPI_ProxyToManagementThread(mgmtthrtask_FreeDeviceHandle, device, &rc);
|
||||
}
|
||||
|
||||
static int mgmtthrtask_DeinitializeStart(void *userdata)
|
||||
{
|
||||
WASAPI_PlatformDeinitializeStart();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void WASAPI_DeinitializeStart(void)
|
||||
{
|
||||
int rc;
|
||||
WASAPI_ProxyToManagementThread(mgmtthrtask_DeinitializeStart, NULL, &rc);
|
||||
}
|
||||
|
||||
static void WASAPI_Deinitialize(void)
|
||||
{
|
||||
DeinitManagementThread();
|
||||
}
|
||||
|
||||
static SDL_bool WASAPI_Init(SDL_AudioDriverImpl *impl)
|
||||
{
|
||||
if (InitManagementThread() < 0) {
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
impl->DetectDevices = WASAPI_DetectDevices;
|
||||
impl->ThreadInit = WASAPI_ThreadInit;
|
||||
impl->ThreadDeinit = WASAPI_ThreadDeinit;
|
||||
impl->OpenDevice = WASAPI_OpenDevice;
|
||||
impl->PlayDevice = WASAPI_PlayDevice;
|
||||
impl->WaitDevice = WASAPI_WaitDevice;
|
||||
impl->GetDeviceBuf = WASAPI_GetDeviceBuf;
|
||||
impl->WaitCaptureDevice = WASAPI_WaitDevice;
|
||||
impl->CaptureFromDevice = WASAPI_CaptureFromDevice;
|
||||
impl->FlushCapture = WASAPI_FlushCapture;
|
||||
impl->CloseDevice = WASAPI_CloseDevice;
|
||||
impl->DeinitializeStart = WASAPI_DeinitializeStart;
|
||||
impl->Deinitialize = WASAPI_Deinitialize;
|
||||
impl->FreeDeviceHandle = WASAPI_FreeDeviceHandle;
|
||||
|
||||
impl->HasCaptureSupport = SDL_TRUE;
|
||||
|
||||
return SDL_TRUE;
|
||||
}
|
||||
|
||||
AudioBootStrap WASAPI_bootstrap = {
|
||||
"wasapi", "WASAPI", WASAPI_Init, SDL_FALSE
|
||||
};
|
||||
|
||||
#endif // SDL_AUDIO_DRIVER_WASAPI
|
||||
73
vendor/sdl-3.0.0/src/audio/wasapi/SDL_wasapi.h
vendored
Normal file
73
vendor/sdl-3.0.0/src/audio/wasapi/SDL_wasapi.h
vendored
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifndef SDL_wasapi_h_
|
||||
#define SDL_wasapi_h_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
|
||||
struct SDL_PrivateAudioData
|
||||
{
|
||||
WCHAR *devid;
|
||||
WAVEFORMATEX *waveformat;
|
||||
IAudioClient *client;
|
||||
IAudioRenderClient *render;
|
||||
IAudioCaptureClient *capture;
|
||||
HANDLE event;
|
||||
HANDLE task;
|
||||
SDL_bool coinitialized;
|
||||
int framesize;
|
||||
SDL_bool device_lost;
|
||||
SDL_bool device_dead;
|
||||
void *activation_handler;
|
||||
};
|
||||
|
||||
// win32 and winrt implementations call into these.
|
||||
int WASAPI_PrepDevice(SDL_AudioDevice *device);
|
||||
void WASAPI_DisconnectDevice(SDL_AudioDevice *device); // don't hold the device lock when calling this!
|
||||
|
||||
|
||||
// BE CAREFUL: if you are holding the device lock and proxy to the management thread with wait_until_complete, and grab the lock again, you will deadlock.
|
||||
typedef int (*ManagementThreadTask)(void *userdata);
|
||||
int WASAPI_ProxyToManagementThread(ManagementThreadTask task, void *userdata, int *wait_until_complete);
|
||||
|
||||
// These are functions that are implemented differently for Windows vs WinRT.
|
||||
// UNLESS OTHERWISE NOTED THESE ALL HAPPEN ON THE MANAGEMENT THREAD.
|
||||
int WASAPI_PlatformInit(void);
|
||||
void WASAPI_PlatformDeinit(void);
|
||||
void WASAPI_PlatformDeinitializeStart(void);
|
||||
void WASAPI_EnumerateEndpoints(SDL_AudioDevice **default_output, SDL_AudioDevice **default_capture);
|
||||
int WASAPI_ActivateDevice(SDL_AudioDevice *device);
|
||||
void WASAPI_PlatformThreadInit(SDL_AudioDevice *device); // this happens on the audio device thread, not the management thread.
|
||||
void WASAPI_PlatformThreadDeinit(SDL_AudioDevice *device); // this happens on the audio device thread, not the management thread.
|
||||
void WASAPI_PlatformDeleteActivationHandler(void *handler);
|
||||
void WASAPI_PlatformFreeDeviceHandle(SDL_AudioDevice *device);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // SDL_wasapi_h_
|
||||
195
vendor/sdl-3.0.0/src/audio/wasapi/SDL_wasapi_win32.c
vendored
Normal file
195
vendor/sdl-3.0.0/src/audio/wasapi/SDL_wasapi_win32.c
vendored
Normal file
|
|
@ -0,0 +1,195 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
/* This is code that Windows uses to talk to WASAPI-related system APIs.
|
||||
This is for non-WinRT desktop apps. The C++/CX implementation of these
|
||||
functions, exclusive to WinRT, are in SDL_wasapi_winrt.cpp.
|
||||
The code in SDL_wasapi.c is used by both standard Windows and WinRT builds
|
||||
to deal with audio and calls into these functions. */
|
||||
|
||||
#if defined(SDL_AUDIO_DRIVER_WASAPI) && !defined(__WINRT__)
|
||||
|
||||
#include "../../core/windows/SDL_windows.h"
|
||||
#include "../../core/windows/SDL_immdevice.h"
|
||||
#include "../SDL_sysaudio.h"
|
||||
|
||||
#include <audioclient.h>
|
||||
|
||||
#include "SDL_wasapi.h"
|
||||
|
||||
// handle to Avrt.dll--Vista and later!--for flagging the callback thread as "Pro Audio" (low latency).
|
||||
static HMODULE libavrt = NULL;
|
||||
typedef HANDLE(WINAPI *pfnAvSetMmThreadCharacteristicsW)(LPCWSTR, LPDWORD);
|
||||
typedef BOOL(WINAPI *pfnAvRevertMmThreadCharacteristics)(HANDLE);
|
||||
static pfnAvSetMmThreadCharacteristicsW pAvSetMmThreadCharacteristicsW = NULL;
|
||||
static pfnAvRevertMmThreadCharacteristics pAvRevertMmThreadCharacteristics = NULL;
|
||||
|
||||
static SDL_bool immdevice_initialized = SDL_FALSE;
|
||||
|
||||
// Some GUIDs we need to know without linking to libraries that aren't available before Vista.
|
||||
static const IID SDL_IID_IAudioClient = { 0x1cb9ad4c, 0xdbfa, 0x4c32, { 0xb1, 0x78, 0xc2, 0xf5, 0x68, 0xa7, 0x03, 0xb2 } };
|
||||
|
||||
static int mgmtthrtask_AudioDeviceDisconnected(void *userdata)
|
||||
{
|
||||
SDL_AudioDeviceDisconnected((SDL_AudioDevice *)userdata);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void WASAPI_AudioDeviceDisconnected(SDL_AudioDevice *device)
|
||||
{
|
||||
// don't wait on this, IMMDevice's own thread needs to return or everything will deadlock.
|
||||
WASAPI_ProxyToManagementThread(mgmtthrtask_AudioDeviceDisconnected, device, NULL);
|
||||
}
|
||||
|
||||
static int mgmtthrtask_DefaultAudioDeviceChanged(void *userdata)
|
||||
{
|
||||
SDL_DefaultAudioDeviceChanged((SDL_AudioDevice *) userdata);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void WASAPI_DefaultAudioDeviceChanged(SDL_AudioDevice *new_default_device)
|
||||
{
|
||||
// don't wait on this, IMMDevice's own thread needs to return or everything will deadlock.
|
||||
WASAPI_ProxyToManagementThread(mgmtthrtask_DefaultAudioDeviceChanged, new_default_device, NULL);
|
||||
}
|
||||
|
||||
int WASAPI_PlatformInit(void)
|
||||
{
|
||||
const SDL_IMMDevice_callbacks callbacks = { WASAPI_AudioDeviceDisconnected, WASAPI_DefaultAudioDeviceChanged };
|
||||
if (FAILED(WIN_CoInitialize())) {
|
||||
return SDL_SetError("CoInitialize() failed");
|
||||
} else if (SDL_IMMDevice_Init(&callbacks) < 0) {
|
||||
return -1; // Error string is set by SDL_IMMDevice_Init
|
||||
}
|
||||
|
||||
immdevice_initialized = SDL_TRUE;
|
||||
|
||||
libavrt = LoadLibrary(TEXT("avrt.dll")); // this library is available in Vista and later. No WinXP, so have to LoadLibrary to use it for now!
|
||||
if (libavrt) {
|
||||
pAvSetMmThreadCharacteristicsW = (pfnAvSetMmThreadCharacteristicsW)GetProcAddress(libavrt, "AvSetMmThreadCharacteristicsW");
|
||||
pAvRevertMmThreadCharacteristics = (pfnAvRevertMmThreadCharacteristics)GetProcAddress(libavrt, "AvRevertMmThreadCharacteristics");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void StopWasapiHotplug(void)
|
||||
{
|
||||
if (immdevice_initialized) {
|
||||
SDL_IMMDevice_Quit();
|
||||
immdevice_initialized = SDL_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
void WASAPI_PlatformDeinit(void)
|
||||
{
|
||||
if (libavrt) {
|
||||
FreeLibrary(libavrt);
|
||||
libavrt = NULL;
|
||||
}
|
||||
|
||||
pAvSetMmThreadCharacteristicsW = NULL;
|
||||
pAvRevertMmThreadCharacteristics = NULL;
|
||||
|
||||
StopWasapiHotplug();
|
||||
|
||||
WIN_CoUninitialize();
|
||||
}
|
||||
|
||||
void WASAPI_PlatformDeinitializeStart(void)
|
||||
{
|
||||
StopWasapiHotplug();
|
||||
}
|
||||
|
||||
void WASAPI_PlatformThreadInit(SDL_AudioDevice *device)
|
||||
{
|
||||
// this thread uses COM.
|
||||
if (SUCCEEDED(WIN_CoInitialize())) { // can't report errors, hope it worked!
|
||||
device->hidden->coinitialized = SDL_TRUE;
|
||||
}
|
||||
|
||||
// Set this thread to very high "Pro Audio" priority.
|
||||
if (pAvSetMmThreadCharacteristicsW) {
|
||||
DWORD idx = 0;
|
||||
device->hidden->task = pAvSetMmThreadCharacteristicsW(L"Pro Audio", &idx);
|
||||
} else {
|
||||
SDL_SetThreadPriority(device->iscapture ? SDL_THREAD_PRIORITY_HIGH : SDL_THREAD_PRIORITY_TIME_CRITICAL);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void WASAPI_PlatformThreadDeinit(SDL_AudioDevice *device)
|
||||
{
|
||||
// Set this thread back to normal priority.
|
||||
if (device->hidden->task && pAvRevertMmThreadCharacteristics) {
|
||||
pAvRevertMmThreadCharacteristics(device->hidden->task);
|
||||
device->hidden->task = NULL;
|
||||
}
|
||||
|
||||
if (device->hidden->coinitialized) {
|
||||
WIN_CoUninitialize();
|
||||
device->hidden->coinitialized = SDL_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
int WASAPI_ActivateDevice(SDL_AudioDevice *device)
|
||||
{
|
||||
IMMDevice *immdevice = NULL;
|
||||
if (SDL_IMMDevice_Get(device, &immdevice, device->iscapture) < 0) {
|
||||
device->hidden->client = NULL;
|
||||
return -1; // This is already set by SDL_IMMDevice_Get
|
||||
}
|
||||
|
||||
// this is _not_ async in standard win32, yay!
|
||||
HRESULT ret = IMMDevice_Activate(immdevice, &SDL_IID_IAudioClient, CLSCTX_ALL, NULL, (void **)&device->hidden->client);
|
||||
IMMDevice_Release(immdevice);
|
||||
|
||||
if (FAILED(ret)) {
|
||||
SDL_assert(device->hidden->client == NULL);
|
||||
return WIN_SetErrorFromHRESULT("WASAPI can't activate audio endpoint", ret);
|
||||
}
|
||||
|
||||
SDL_assert(device->hidden->client != NULL);
|
||||
if (WASAPI_PrepDevice(device) == -1) { // not async, fire it right away.
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0; // good to go.
|
||||
}
|
||||
|
||||
void WASAPI_EnumerateEndpoints(SDL_AudioDevice **default_output, SDL_AudioDevice **default_capture)
|
||||
{
|
||||
SDL_IMMDevice_EnumerateEndpoints(default_output, default_capture);
|
||||
}
|
||||
|
||||
void WASAPI_PlatformDeleteActivationHandler(void *handler)
|
||||
{
|
||||
// not asynchronous.
|
||||
SDL_assert(!"This function should have only been called on WinRT.");
|
||||
}
|
||||
|
||||
void WASAPI_PlatformFreeDeviceHandle(SDL_AudioDevice *device)
|
||||
{
|
||||
SDL_IMMDevice_FreeDeviceHandle(device);
|
||||
}
|
||||
|
||||
#endif // SDL_AUDIO_DRIVER_WASAPI && !defined(__WINRT__)
|
||||
360
vendor/sdl-3.0.0/src/audio/wasapi/SDL_wasapi_winrt.cpp
vendored
Normal file
360
vendor/sdl-3.0.0/src/audio/wasapi/SDL_wasapi_winrt.cpp
vendored
Normal file
|
|
@ -0,0 +1,360 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
// This is C++/CX code that the WinRT port uses to talk to WASAPI-related
|
||||
// system APIs. The C implementation of these functions, for non-WinRT apps,
|
||||
// is in SDL_wasapi_win32.c. The code in SDL_wasapi.c is used by both standard
|
||||
// Windows and WinRT builds to deal with audio and calls into these functions.
|
||||
|
||||
#if defined(SDL_AUDIO_DRIVER_WASAPI) && defined(__WINRT__)
|
||||
|
||||
#include <Windows.h>
|
||||
#include <windows.ui.core.h>
|
||||
#include <windows.devices.enumeration.h>
|
||||
#include <windows.media.devices.h>
|
||||
#include <wrl/implements.h>
|
||||
#include <collection.h>
|
||||
|
||||
extern "C" {
|
||||
#include "../../core/windows/SDL_windows.h"
|
||||
#include "../SDL_sysaudio.h"
|
||||
}
|
||||
|
||||
#define COBJMACROS
|
||||
#include <mmdeviceapi.h>
|
||||
#include <audioclient.h>
|
||||
|
||||
#include "SDL_wasapi.h"
|
||||
|
||||
using namespace Windows::Devices::Enumeration;
|
||||
using namespace Windows::Media::Devices;
|
||||
using namespace Windows::Foundation;
|
||||
using namespace Microsoft::WRL;
|
||||
|
||||
static Platform::String ^ SDL_PKEY_AudioEngine_DeviceFormat = L"{f19f064d-082c-4e27-bc73-6882a1bb8e4c} 0";
|
||||
|
||||
|
||||
static SDL_bool FindWinRTAudioDeviceCallback(SDL_AudioDevice *device, void *userdata)
|
||||
{
|
||||
return (SDL_wcscmp((LPCWSTR) device->handle, (LPCWSTR) userdata) == 0) ? SDL_TRUE : SDL_FALSE;
|
||||
}
|
||||
|
||||
static SDL_AudioDevice *FindWinRTAudioDevice(LPCWSTR devid)
|
||||
{
|
||||
return SDL_FindPhysicalAudioDeviceByCallback(FindWinRTAudioDeviceCallback, (void *) devid);
|
||||
}
|
||||
|
||||
class SDL_WasapiDeviceEventHandler
|
||||
{
|
||||
public:
|
||||
SDL_WasapiDeviceEventHandler(const SDL_bool _iscapture);
|
||||
~SDL_WasapiDeviceEventHandler();
|
||||
void OnDeviceAdded(DeviceWatcher ^ sender, DeviceInformation ^ args);
|
||||
void OnDeviceRemoved(DeviceWatcher ^ sender, DeviceInformationUpdate ^ args);
|
||||
void OnDeviceUpdated(DeviceWatcher ^ sender, DeviceInformationUpdate ^ args);
|
||||
void OnEnumerationCompleted(DeviceWatcher ^ sender, Platform::Object ^ args);
|
||||
void OnDefaultRenderDeviceChanged(Platform::Object ^ sender, DefaultAudioRenderDeviceChangedEventArgs ^ args);
|
||||
void OnDefaultCaptureDeviceChanged(Platform::Object ^ sender, DefaultAudioCaptureDeviceChangedEventArgs ^ args);
|
||||
void WaitForCompletion();
|
||||
|
||||
private:
|
||||
SDL_Semaphore *completed_semaphore;
|
||||
const SDL_bool iscapture;
|
||||
DeviceWatcher ^ watcher;
|
||||
Windows::Foundation::EventRegistrationToken added_handler;
|
||||
Windows::Foundation::EventRegistrationToken removed_handler;
|
||||
Windows::Foundation::EventRegistrationToken updated_handler;
|
||||
Windows::Foundation::EventRegistrationToken completed_handler;
|
||||
Windows::Foundation::EventRegistrationToken default_changed_handler;
|
||||
};
|
||||
|
||||
SDL_WasapiDeviceEventHandler::SDL_WasapiDeviceEventHandler(const SDL_bool _iscapture)
|
||||
: iscapture(_iscapture), completed_semaphore(SDL_CreateSemaphore(0))
|
||||
{
|
||||
if (!completed_semaphore) {
|
||||
return; // uhoh.
|
||||
}
|
||||
|
||||
Platform::String ^ selector = _iscapture ? MediaDevice::GetAudioCaptureSelector() : MediaDevice::GetAudioRenderSelector();
|
||||
Platform::Collections::Vector<Platform::String ^> properties;
|
||||
properties.Append(SDL_PKEY_AudioEngine_DeviceFormat);
|
||||
watcher = DeviceInformation::CreateWatcher(selector, properties.GetView());
|
||||
if (!watcher)
|
||||
return; // uhoh.
|
||||
|
||||
// !!! FIXME: this doesn't need a lambda here, I think, if I make SDL_WasapiDeviceEventHandler a proper C++/CX class. --ryan.
|
||||
added_handler = watcher->Added += ref new TypedEventHandler<DeviceWatcher ^, DeviceInformation ^>([this](DeviceWatcher ^ sender, DeviceInformation ^ args) { OnDeviceAdded(sender, args); });
|
||||
removed_handler = watcher->Removed += ref new TypedEventHandler<DeviceWatcher ^, DeviceInformationUpdate ^>([this](DeviceWatcher ^ sender, DeviceInformationUpdate ^ args) { OnDeviceRemoved(sender, args); });
|
||||
updated_handler = watcher->Updated += ref new TypedEventHandler<DeviceWatcher ^, DeviceInformationUpdate ^>([this](DeviceWatcher ^ sender, DeviceInformationUpdate ^ args) { OnDeviceUpdated(sender, args); });
|
||||
completed_handler = watcher->EnumerationCompleted += ref new TypedEventHandler<DeviceWatcher ^, Platform::Object ^>([this](DeviceWatcher ^ sender, Platform::Object ^ args) { OnEnumerationCompleted(sender, args); });
|
||||
if (iscapture) {
|
||||
default_changed_handler = MediaDevice::DefaultAudioCaptureDeviceChanged += ref new TypedEventHandler<Platform::Object ^, DefaultAudioCaptureDeviceChangedEventArgs ^>([this](Platform::Object ^ sender, DefaultAudioCaptureDeviceChangedEventArgs ^ args) { OnDefaultCaptureDeviceChanged(sender, args); });
|
||||
} else {
|
||||
default_changed_handler = MediaDevice::DefaultAudioRenderDeviceChanged += ref new TypedEventHandler<Platform::Object ^, DefaultAudioRenderDeviceChangedEventArgs ^>([this](Platform::Object ^ sender, DefaultAudioRenderDeviceChangedEventArgs ^ args) { OnDefaultRenderDeviceChanged(sender, args); });
|
||||
}
|
||||
watcher->Start();
|
||||
}
|
||||
|
||||
SDL_WasapiDeviceEventHandler::~SDL_WasapiDeviceEventHandler()
|
||||
{
|
||||
if (watcher) {
|
||||
watcher->Added -= added_handler;
|
||||
watcher->Removed -= removed_handler;
|
||||
watcher->Updated -= updated_handler;
|
||||
watcher->EnumerationCompleted -= completed_handler;
|
||||
watcher->Stop();
|
||||
watcher = nullptr;
|
||||
}
|
||||
|
||||
if (completed_semaphore) {
|
||||
SDL_DestroySemaphore(completed_semaphore);
|
||||
completed_semaphore = nullptr;
|
||||
}
|
||||
|
||||
if (iscapture) {
|
||||
MediaDevice::DefaultAudioCaptureDeviceChanged -= default_changed_handler;
|
||||
} else {
|
||||
MediaDevice::DefaultAudioRenderDeviceChanged -= default_changed_handler;
|
||||
}
|
||||
}
|
||||
|
||||
void SDL_WasapiDeviceEventHandler::OnDeviceAdded(DeviceWatcher ^ sender, DeviceInformation ^ info)
|
||||
{
|
||||
/* You can have multiple endpoints on a device that are mutually exclusive ("Speakers" vs "Line Out" or whatever).
|
||||
In a perfect world, things that are unplugged won't be in this collection. The only gotcha is probably for
|
||||
phones and tablets, where you might have an internal speaker and a headphone jack and expect both to be
|
||||
available and switch automatically. (!!! FIXME...?) */
|
||||
|
||||
SDL_assert(sender == this->watcher);
|
||||
char *utf8dev = WIN_StringToUTF8(info->Name->Data());
|
||||
if (utf8dev) {
|
||||
SDL_AudioSpec spec;
|
||||
SDL_zero(spec);
|
||||
|
||||
Platform::Object ^ obj = info->Properties->Lookup(SDL_PKEY_AudioEngine_DeviceFormat);
|
||||
if (obj) {
|
||||
IPropertyValue ^ property = (IPropertyValue ^) obj;
|
||||
Platform::Array<unsigned char> ^ data;
|
||||
property->GetUInt8Array(&data);
|
||||
WAVEFORMATEXTENSIBLE fmt;
|
||||
SDL_zero(fmt);
|
||||
SDL_memcpy(&fmt, data->Data, SDL_min(data->Length, sizeof(WAVEFORMATEXTENSIBLE)));
|
||||
spec.channels = (Uint8)fmt.Format.nChannels;
|
||||
spec.freq = fmt.Format.nSamplesPerSec;
|
||||
spec.format = SDL_WaveFormatExToSDLFormat((WAVEFORMATEX *)&fmt);
|
||||
}
|
||||
|
||||
LPWSTR devid = SDL_wcsdup(info->Id->Data());
|
||||
if (devid) {
|
||||
SDL_AddAudioDevice(this->iscapture, utf8dev, spec.channels ? &spec : NULL, devid);
|
||||
}
|
||||
SDL_free(utf8dev);
|
||||
}
|
||||
}
|
||||
|
||||
void SDL_WasapiDeviceEventHandler::OnDeviceRemoved(DeviceWatcher ^ sender, DeviceInformationUpdate ^ info)
|
||||
{
|
||||
SDL_assert(sender == this->watcher);
|
||||
WASAPI_DisconnectDevice(FindWinRTAudioDevice(info->Id->Data()));
|
||||
}
|
||||
|
||||
void SDL_WasapiDeviceEventHandler::OnDeviceUpdated(DeviceWatcher ^ sender, DeviceInformationUpdate ^ args)
|
||||
{
|
||||
SDL_assert(sender == this->watcher);
|
||||
}
|
||||
|
||||
void SDL_WasapiDeviceEventHandler::OnEnumerationCompleted(DeviceWatcher ^ sender, Platform::Object ^ args)
|
||||
{
|
||||
SDL_assert(sender == this->watcher);
|
||||
if (this->completed_semaphore) {
|
||||
SDL_PostSemaphore(this->completed_semaphore);
|
||||
}
|
||||
}
|
||||
|
||||
void SDL_WasapiDeviceEventHandler::OnDefaultRenderDeviceChanged(Platform::Object ^ sender, DefaultAudioRenderDeviceChangedEventArgs ^ args)
|
||||
{
|
||||
SDL_assert(!this->iscapture);
|
||||
SDL_DefaultAudioDeviceChanged(FindWinRTAudioDevice(args->Id->Data()));
|
||||
}
|
||||
|
||||
void SDL_WasapiDeviceEventHandler::OnDefaultCaptureDeviceChanged(Platform::Object ^ sender, DefaultAudioCaptureDeviceChangedEventArgs ^ args)
|
||||
{
|
||||
SDL_assert(this->iscapture);
|
||||
SDL_DefaultAudioDeviceChanged(FindWinRTAudioDevice(args->Id->Data()));
|
||||
}
|
||||
|
||||
void SDL_WasapiDeviceEventHandler::WaitForCompletion()
|
||||
{
|
||||
if (this->completed_semaphore) {
|
||||
SDL_WaitSemaphore(this->completed_semaphore);
|
||||
SDL_DestroySemaphore(this->completed_semaphore);
|
||||
this->completed_semaphore = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static SDL_WasapiDeviceEventHandler *playback_device_event_handler;
|
||||
static SDL_WasapiDeviceEventHandler *capture_device_event_handler;
|
||||
|
||||
int WASAPI_PlatformInit(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void StopWasapiHotplug(void)
|
||||
{
|
||||
delete playback_device_event_handler;
|
||||
playback_device_event_handler = nullptr;
|
||||
delete capture_device_event_handler;
|
||||
capture_device_event_handler = nullptr;
|
||||
}
|
||||
|
||||
void WASAPI_PlatformDeinit(void)
|
||||
{
|
||||
StopWasapiHotplug();
|
||||
}
|
||||
|
||||
void WASAPI_PlatformDeinitializeStart(void)
|
||||
{
|
||||
StopWasapiHotplug();
|
||||
}
|
||||
|
||||
|
||||
void WASAPI_EnumerateEndpoints(SDL_AudioDevice **default_output, SDL_AudioDevice **default_capture)
|
||||
{
|
||||
Platform::String ^ defdevid;
|
||||
|
||||
// DeviceWatchers will fire an Added event for each existing device at
|
||||
// startup, so we don't need to enumerate them separately before
|
||||
// listening for updates.
|
||||
playback_device_event_handler = new SDL_WasapiDeviceEventHandler(SDL_FALSE);
|
||||
playback_device_event_handler->WaitForCompletion();
|
||||
defdevid = MediaDevice::GetDefaultAudioRenderId(AudioDeviceRole::Default);
|
||||
if (defdevid) {
|
||||
*default_output = FindWinRTAudioDevice(defdevid->Data());
|
||||
}
|
||||
|
||||
capture_device_event_handler = new SDL_WasapiDeviceEventHandler(SDL_TRUE);
|
||||
capture_device_event_handler->WaitForCompletion();
|
||||
defdevid = MediaDevice::GetDefaultAudioCaptureId(AudioDeviceRole::Default);
|
||||
if (defdevid) {
|
||||
*default_capture = FindWinRTAudioDevice(defdevid->Data());
|
||||
}
|
||||
}
|
||||
|
||||
class SDL_WasapiActivationHandler : public RuntimeClass<RuntimeClassFlags<ClassicCom>, FtmBase, IActivateAudioInterfaceCompletionHandler>
|
||||
{
|
||||
public:
|
||||
SDL_WasapiActivationHandler() : completion_semaphore(SDL_CreateSemaphore(0)) { SDL_assert(completion_semaphore != NULL); }
|
||||
STDMETHOD(ActivateCompleted)(IActivateAudioInterfaceAsyncOperation *operation);
|
||||
void WaitForCompletion();
|
||||
private:
|
||||
SDL_Semaphore *completion_semaphore;
|
||||
};
|
||||
|
||||
void SDL_WasapiActivationHandler::WaitForCompletion()
|
||||
{
|
||||
if (completion_semaphore) {
|
||||
SDL_WaitSemaphore(completion_semaphore);
|
||||
SDL_DestroySemaphore(completion_semaphore);
|
||||
completion_semaphore = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
HRESULT
|
||||
SDL_WasapiActivationHandler::ActivateCompleted(IActivateAudioInterfaceAsyncOperation *async)
|
||||
{
|
||||
// Just set a flag, since we're probably in a different thread. We'll pick it up and init everything on our own thread to prevent races.
|
||||
SDL_PostSemaphore(completion_semaphore);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
void WASAPI_PlatformDeleteActivationHandler(void *handler)
|
||||
{
|
||||
((SDL_WasapiActivationHandler *)handler)->Release();
|
||||
}
|
||||
|
||||
int WASAPI_ActivateDevice(SDL_AudioDevice *device)
|
||||
{
|
||||
LPCWSTR devid = (LPCWSTR) device->handle;
|
||||
SDL_assert(devid != NULL);
|
||||
|
||||
ComPtr<SDL_WasapiActivationHandler> handler = Make<SDL_WasapiActivationHandler>();
|
||||
if (handler == nullptr) {
|
||||
return SDL_SetError("Failed to allocate WASAPI activation handler");
|
||||
}
|
||||
|
||||
handler.Get()->AddRef(); // we hold a reference after ComPtr destructs on return, causing a Release, and Release ourselves in WASAPI_PlatformDeleteActivationHandler(), etc.
|
||||
device->hidden->activation_handler = handler.Get();
|
||||
|
||||
IActivateAudioInterfaceAsyncOperation *async = nullptr;
|
||||
const HRESULT ret = ActivateAudioInterfaceAsync(devid, __uuidof(IAudioClient), nullptr, handler.Get(), &async);
|
||||
|
||||
if (FAILED(ret) || async == nullptr) {
|
||||
if (async != nullptr) {
|
||||
async->Release();
|
||||
}
|
||||
handler.Get()->Release();
|
||||
return WIN_SetErrorFromHRESULT("WASAPI can't activate requested audio endpoint", ret);
|
||||
}
|
||||
|
||||
// !!! FIXME: the problems in SDL2 that needed this to be synchronous are _probably_ solved by SDL3, and this can block indefinitely if a user prompt is shown to get permission to use a microphone.
|
||||
handler.Get()->WaitForCompletion(); // block here until we have an answer, so this is synchronous to us after all.
|
||||
|
||||
HRESULT activateRes = S_OK;
|
||||
IUnknown *iunknown = nullptr;
|
||||
const HRESULT getActivateRes = async->GetActivateResult(&activateRes, &iunknown);
|
||||
async->Release();
|
||||
if (FAILED(getActivateRes)) {
|
||||
return WIN_SetErrorFromHRESULT("Failed to get WASAPI activate result", getActivateRes);
|
||||
} else if (FAILED(activateRes)) {
|
||||
return WIN_SetErrorFromHRESULT("Failed to activate WASAPI device", activateRes);
|
||||
}
|
||||
|
||||
iunknown->QueryInterface(IID_PPV_ARGS(&device->hidden->client));
|
||||
if (!device->hidden->client) {
|
||||
return SDL_SetError("Failed to query WASAPI client interface");
|
||||
}
|
||||
|
||||
if (WASAPI_PrepDevice(device) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void WASAPI_PlatformThreadInit(SDL_AudioDevice *device)
|
||||
{
|
||||
// !!! FIXME: set this thread to "Pro Audio" priority.
|
||||
SDL_SetThreadPriority(device->iscapture ? SDL_THREAD_PRIORITY_HIGH : SDL_THREAD_PRIORITY_TIME_CRITICAL);
|
||||
}
|
||||
|
||||
void WASAPI_PlatformThreadDeinit(SDL_AudioDevice *device)
|
||||
{
|
||||
// !!! FIXME: set this thread to "Pro Audio" priority.
|
||||
}
|
||||
|
||||
void WASAPI_PlatformFreeDeviceHandle(SDL_AudioDevice *device)
|
||||
{
|
||||
SDL_free(device->handle);
|
||||
}
|
||||
|
||||
#endif // SDL_AUDIO_DRIVER_WASAPI && defined(__WINRT__)
|
||||
Loading…
Add table
Add a link
Reference in a new issue