@@ -8,6 +8,7 @@ import android.media.AudioManager
88import android.media.AudioMetadataReadMap
99import android.media.AudioRouting
1010import android.media.AudioTrack
11+ import android.media.VolumeShaper
1112import android.os.Build
1213import android.os.Parcel
1314import android.util.Log
@@ -19,15 +20,15 @@ import java.nio.ByteBuffer
1920
2021/*
2122 * Exposes entire API surface of AudioTrack.cpp, with some minor exceptions:
22- * - Tuner API related features TODO let's bring that back
2323 * - setCallerName/getCallerName because I want to avoid offset hardcoding, and it's only used for metrics
2424 * None of those will impose any limitations for music playback.
2525 */
2626class NativeTrack (context : Context , attributes : AudioAttributes , streamType : Int , sampleRate : Int ,
2727 format : AudioFormatDetector .Encoding , channelMask : Int , frameCount : Int? , trackFlags : Int ,
2828 sessionId : Int , maxRequiredSpeed : Float , selectedDeviceId : Int? , bitRate : Int , durationUs : Long ,
2929 hasVideo : Boolean , smallBuf : Boolean , isStreaming : Boolean , offloadBufferSize : Int ,
30- notificationFrames : Int , doNotReconnect : Boolean , transferMode : Int ) {
30+ notificationFrames : Int , doNotReconnect : Boolean , transferMode : Int , contentId : Int , syncId : Int ,
31+ encapsulationMode : Int ) {
3132 companion object {
3233 private const val TAG = " NativeTrack.kt"
3334
@@ -213,22 +214,27 @@ class NativeTrack(context: Context, attributes: AudioAttributes, streamType: Int
213214 offloadBufferSize = 0 ,
214215 notificationFrames = 0 ,
215216 doNotReconnect = false ,
216- transferMode = 3
217+ transferMode = 3 ,
218+ contentId = 0 ,
219+ syncId = 0 ,
220+ encapsulationMode = 0
217221 )
218222 }
219223 }
220224 private var sessionId: Int
221225 val ptr: Long
222226 var myState: State
223- // proxy limitations: a lot of fields not initialized (mSampleRate, mAudioFormat, mChannelMask, ...) which can
224- // cause some internal checks in various methods to fail; stream event callback is no-op. however, it does:
227+ // proxy limitations: a lot of fields not initialized (mSampleRate, mAudioFormat, mOffloaded, ...) which can
228+ // cause some internal checks in various methods to fail; stream event and playback position callbacks both
229+ // are no-op; we MUST call play(), pause(), stop() and don't use the native methods ourselves for this to work;
230+ // we must also not cache playing/paused/stopped/volume ourselves because it may change under our feet.
231+ // however, it does:
225232 // - register (and overwrite) any codec format listeners on native side ; good for us because we can't register
226233 // one in a normal way due to dependence on volatile offsets. i.e. with proxy we get codec format listeners!
227234 // - register player base (which we really should have on N+ to be a nice citizen and have stuff like ducking)
228235 // - register (and overwrite) routing callback, which is meh but we can just use the java one, it don't hurt
229- // - volume shapers! these would be near-impossible using the native API because it's all inline.
236+ // - allow for volume shapers! these would be near-impossible using the native API because it's all inline.
230237 // it's a bit fiddly, but we get all possibilities of a native AudioTrack and a Java one - combined.
231- // however: we MUST call play(), pause(), stop() and don't use the native methods ourselves for this to work.
232238 // reminder: do not call write() or any other standard APIs as we break a lot of assumptions. + proxy is not
233239 // always available (i.e. L/M), hence native methods are preferable where we can.
234240 private val proxy: AudioTrack ?
@@ -304,7 +310,8 @@ class NativeTrack(context: Context, attributes: AudioAttributes, streamType: Int
304310 hasVideo = hasVideo, smallBuf = smallBuf, isStreaming = isStreaming, bitWidth = bitWidth,
305311 offloadBufferSize = offloadBufferSize, usage = usage, contentType = contentType,
306312 attrFlags = attrFlags, notificationFrames = notificationFrames, doNotReconnect = doNotReconnect,
307- transferMode = transferMode)
313+ transferMode = transferMode, contentId = contentId, syncId = syncId,
314+ encapsulationMode = encapsulationMode)
308315 } catch (t: Throwable ) {
309316 try {
310317 dtor(ptr)
@@ -377,7 +384,8 @@ class NativeTrack(context: Context, attributes: AudioAttributes, streamType: Int
377384 selectedDeviceId : Int , bitRate : Int , durationUs : Long , hasVideo : Boolean ,
378385 smallBuf : Boolean , isStreaming : Boolean , bitWidth : Int , offloadBufferSize : Int ,
379386 usage : Int , contentType : Int , attrFlags : Int , notificationFrames : Int ,
380- doNotReconnect : Boolean , transferMode : Int ): Int
387+ doNotReconnect : Boolean , transferMode : Int , contentId : Int , syncId : Int ,
388+ encapsulationMode : Int ): Int
381389 private external fun getRealPtr (@Suppress(" unused" ) ptr : Long ): Long
382390 private external fun flagsFromOffset (@Suppress(" unused" ) ptr : Long ): Int
383391 private external fun notificationFramesActFromOffset (@Suppress(" unused" ) ptr : Long ): Int
@@ -479,6 +487,11 @@ class NativeTrack(context: Context, attributes: AudioAttributes, streamType: Int
479487 return channelCount() * bps
480488 }
481489
490+ @RequiresApi(Build .VERSION_CODES .O )
491+ fun createVolumeShaper (config : VolumeShaper .Configuration ): VolumeShaper {
492+ return proxy!! .createVolumeShaper(config)
493+ }
494+
482495 class NativeTrackException : Exception {
483496 constructor (message: String ) : super (message)
484497 constructor (message: String , cause: Throwable ) : super (message, cause)
0 commit comments