Skip to content

Commit 4245723

Browse files
committed
wip
1 parent 352584b commit 4245723

File tree

16 files changed

+394
-89
lines changed

16 files changed

+394
-89
lines changed

app/src/main/cpp/NativeTrack.cpp

+262-32
Large diffs are not rendered by default.

app/src/main/cpp/aosp_stubs.h

+4-5
Original file line numberDiff line numberDiff line change
@@ -146,11 +146,10 @@ namespace android {
146146
Buffer() {
147147
ALOGE("if you see this, expect a segfault. this class Buffer never was supposed to be instantiated");
148148
}
149-
[[nodiscard]] size_t size() const { return mSize; }
150-
[[nodiscard]] size_t getFrameCount() const { return frameCount; }
151-
[[nodiscard]] uint8_t * data() const { return ui8; }
149+
// [[nodiscard]] size_t size() const { return mSize; }
150+
// [[nodiscard]] size_t getFrameCount() const { return frameCount; }
151+
// [[nodiscard]] uint8_t * data() const { return ui8; }
152152
size_t frameCount = 0;
153-
private:
154153
size_t mSize = 0;
155154
union {
156155
void* raw = nullptr;
@@ -163,7 +162,7 @@ namespace android {
163162
friend AudioTrack;
164163
public:
165164
IAudioTrackCallback() {
166-
ALOGI("hi from IAudioTrackCallback ctor");
165+
// TODO do we need to call the aosp ctor here?
167166
}
168167
// This event only occurs for TRANSFER_CALLBACK.
169168
virtual inline size_t

app/src/main/cpp/gramophone.cpp

+17
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,20 @@
1+
/*
2+
* Copyright (C) 2025 nift4
3+
*
4+
* Gramophone is free software: you can redistribute it and/or modify
5+
* it under the terms of the GNU General Public License as published by
6+
* the Free Software Foundation, either version 3 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* Gramophone is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License
15+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
16+
*/
17+
118
#define LOG_TAG "AudioTrackHalInfo(JNI)"
219

320
#include <jni.h>

app/src/main/kotlin/org/akanework/gramophone/logic/GramophonePlaybackService.kt

+6-1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ package org.akanework.gramophone.logic
2020
import android.annotation.SuppressLint
2121
import android.app.PendingIntent
2222
import android.bluetooth.BluetoothCodecStatus
23+
import android.bluetooth.BluetoothProfile
2324
import android.content.BroadcastReceiver
2425
import android.content.Context
2526
import android.content.Intent
@@ -168,6 +169,7 @@ class GramophonePlaybackService : MediaLibraryService(), MediaSessionService.Lis
168169
private var audioTrackInfo: AudioTrackInfo? = null
169170
private var btInfo: BtCodecInfo? = null
170171
private var bitrate: Long? = null
172+
private var proxy: BtCodecInfo.Companion.Proxy? = null
171173
private var audioTrackInfoCounter = 0
172174
private var audioTrackReleaseCounter = 0
173175
private val lyricsFetcher = CoroutineScope(Dispatchers.IO.limitedParallelism(1))
@@ -440,7 +442,7 @@ class GramophonePlaybackService : MediaLibraryService(), MediaSessionService.Lis
440442
ContextCompat.RECEIVER_EXPORTED
441443
)
442444
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O /* before 8, only sbc was supported */) {
443-
BtCodecInfo.getCodec(this) {
445+
proxy = BtCodecInfo.getCodec(this) {
444446
Log.d(TAG, "first bluetooth codec config $btInfo")
445447
btInfo = it
446448
sendDebouncedFormatChange()
@@ -498,6 +500,9 @@ class GramophonePlaybackService : MediaLibraryService(), MediaSessionService.Lis
498500
mediaSession!!.player.release()
499501
mediaSession = null
500502
internalPlaybackThread.quitSafely()
503+
proxy?.let {
504+
it.adapter.closeProfileProxy(BluetoothProfile.A2DP, it.a2dp)
505+
}
501506
LyricWidgetProvider.update(this)
502507
super.onDestroy()
503508
}

app/src/main/kotlin/org/akanework/gramophone/logic/utils/BtCodecInfo.kt

+48-41
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package org.akanework.gramophone.logic.utils
33
import android.Manifest
44
import android.annotation.SuppressLint
55
import android.bluetooth.BluetoothA2dp
6+
import android.bluetooth.BluetoothAdapter
67
import android.bluetooth.BluetoothCodecConfig
78
import android.bluetooth.BluetoothCodecStatus
89
import android.bluetooth.BluetoothDevice
@@ -25,7 +26,6 @@ data class BtCodecInfo(val codec: String?, val sampleRateHz: Int?, val channelCo
2526
private const val TAG = "BtCodecInfo"
2627

2728
@RequiresApi(Build.VERSION_CODES.O)
28-
// TODO test stability
2929
fun fromCodecConfig(codecConfig: BluetoothCodecConfig?): BtCodecInfo? {
3030
if (codecConfig == null) return null
3131
try {
@@ -48,9 +48,9 @@ data class BtCodecInfo(val codec: String?, val sampleRateHz: Int?, val channelCo
4848
}
4949
when (name) {
5050
// P returns without space, some newer versions added space
51-
"LDHCV2" -> "LHDC V2"
52-
"LDHCV3" -> "LHDC V3"
53-
"LDHCV5" -> "LHDC V5"
51+
"LHDCV2" -> "LHDC V2"
52+
"LHDCV3" -> "LHDC V3"
53+
"LHDCV5" -> "LHDC V5"
5454
// other known ones: aptX Adaptive, aptX TWS+
5555
else -> name
5656
}
@@ -114,46 +114,53 @@ data class BtCodecInfo(val codec: String?, val sampleRateHz: Int?, val channelCo
114114

115115
// TODO test stability
116116
@RequiresApi(Build.VERSION_CODES.O)
117-
fun getCodec(context: Context, callback: (BtCodecInfo?) -> Unit) {
117+
fun getCodec(context: Context, callback: (BtCodecInfo?) -> Unit): Proxy? {
118118
val adapter = ContextCompat.getSystemService(context, BluetoothManager::class.java)?.adapter
119-
// TODO stop leaking
120-
if (adapter?.getProfileProxy(context, object : BluetoothProfile.ServiceListener {
121-
override fun onServiceConnected(profile: Int, proxy: BluetoothProfile) {
122-
val a2dp = proxy as BluetoothA2dp
123-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && ContextCompat.checkSelfPermission(
124-
context,
125-
Manifest.permission.BLUETOOTH_CONNECT
126-
) != PackageManager.PERMISSION_GRANTED) {
127-
Log.w(TAG, "missing bluetooth permission")
128-
callback(null)
129-
return
130-
}
131-
val cd = a2dp.connectedDevices
132-
val device = if (cd.size <= 1) cd.firstOrNull() else try {
133-
BluetoothA2dp::class.java.getMethod("getActiveDevice").invoke(a2dp) as BluetoothDevice?
134-
} catch (t: Throwable) {
135-
Log.e(TAG, Log.getStackTraceString(t))
136-
callback(null)
137-
return
138-
}
139-
if (device == null) return
140-
@SuppressLint("NewApi")
141-
val codec = try {
142-
BluetoothA2dp::class.java.getMethod("getCodecStatus", BluetoothDevice::class.java)
143-
.invoke(a2dp, device) as BluetoothCodecStatus?
144-
} catch (t: Throwable) {
145-
Log.e(TAG, Log.getStackTraceString(t))
146-
null
147-
}?.codecConfig
148-
callback(fromCodecConfig(codec))
149-
}
150-
151-
override fun onServiceDisconnected(profile: Int) {
152-
// do nothing
153-
}
154-
}, BluetoothProfile.A2DP) != true) {
119+
?: return null
120+
val sl = Proxy(adapter, callback, context)
121+
if (adapter.getProfileProxy(context, sl, BluetoothProfile.A2DP) != true) {
155122
Log.e(TAG, "getProfileProxy error")
156123
callback(null)
124+
return null
125+
}
126+
return sl
127+
}
128+
129+
class Proxy(val adapter: BluetoothAdapter, private val callback: (BtCodecInfo?) -> Unit,
130+
private val context: Context) : BluetoothProfile.ServiceListener {
131+
var a2dp: BluetoothA2dp? = null
132+
override fun onServiceConnected(profile: Int, proxy: BluetoothProfile) {
133+
a2dp = proxy as BluetoothA2dp
134+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && ContextCompat.checkSelfPermission(
135+
context,
136+
Manifest.permission.BLUETOOTH_CONNECT
137+
) != PackageManager.PERMISSION_GRANTED) {
138+
Log.w(TAG, "missing bluetooth permission")
139+
callback(null)
140+
return
141+
}
142+
val cd = a2dp!!.connectedDevices
143+
val device = if (cd.size <= 1) cd.firstOrNull() else try {
144+
BluetoothA2dp::class.java.getMethod("getActiveDevice").invoke(a2dp) as BluetoothDevice?
145+
} catch (t: Throwable) {
146+
Log.e(TAG, Log.getStackTraceString(t))
147+
callback(null)
148+
return
149+
}
150+
if (device == null) return
151+
@SuppressLint("NewApi")
152+
val codec = try {
153+
BluetoothA2dp::class.java.getMethod("getCodecStatus", BluetoothDevice::class.java)
154+
.invoke(a2dp, device) as BluetoothCodecStatus?
155+
} catch (t: Throwable) {
156+
Log.e(TAG, Log.getStackTraceString(t))
157+
null
158+
}?.codecConfig
159+
callback(fromCodecConfig(codec))
160+
}
161+
162+
override fun onServiceDisconnected(profile: Int) {
163+
a2dp = null
157164
}
158165
}
159166
}

app/src/main/kotlin/org/akanework/gramophone/logic/utils/NativeTrack.kt

+47
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,12 @@ import android.content.Context
44
import android.os.Build
55
import android.os.Parcel
66
import android.util.Log
7+
import java.nio.ByteBuffer
78

89
class NativeTrack(context: Context) {
10+
companion object {
11+
private const val TAG = "NativeTrack.kt"
12+
}
913
val ptr: Long
1014
var myState = State.NOT_SET
1115
private set
@@ -73,4 +77,47 @@ class NativeTrack(context: Context) {
7377
RELEASED, // release() called
7478
ALIVE, // ready to use
7579
}
80+
81+
@Suppress("unused") // called from native, on callback thread (not main thread!)
82+
private fun onUnderrun() {
83+
Log.i(TAG, "onUnderrun called")
84+
}
85+
@Suppress("unused") // called from native, on callback thread (not main thread!)
86+
private fun onMarker(markerPosition: Int) {
87+
Log.i(TAG, "onMarker called: $markerPosition")
88+
}
89+
@Suppress("unused") // called from native, on callback thread (not main thread!)
90+
private fun onNewPos(newPos: Int) {
91+
Log.i(TAG, "onNewPos called: $newPos")
92+
}
93+
@Suppress("unused") // called from native, on callback thread (not main thread!)
94+
private fun onStreamEnd() {
95+
Log.i(TAG, "onStreamEnd called")
96+
}
97+
@Suppress("unused") // called from native, on callback thread (not main thread!)
98+
private fun onNewIAudioTrack() {
99+
Log.i(TAG, "onNewIAudioTrack called")
100+
}
101+
@Suppress("unused") // called from native, on callback thread (not main thread!)
102+
private fun onNewTimestamp(timestamp: Int, timeSec: Long, timeNanoSec: Long) {
103+
Log.i(TAG, "onNewTimestamp called: timestamp=$timestamp timeSec=$timeSec timeNanoSec=$timeNanoSec")
104+
}
105+
@Suppress("unused") // called from native, on callback thread (not main thread!)
106+
private fun onLoopEnd(loopsRemaining: Int) {
107+
Log.i(TAG, "onLoopEnd called")
108+
}
109+
@Suppress("unused") // called from native, on callback thread (not main thread!)
110+
private fun onBufferEnd() {
111+
Log.i(TAG, "onBufferEnd called")
112+
}
113+
@Suppress("unused") // called from native, on callback thread (not main thread!)
114+
private fun onMoreData(frameCount: Long, buffer: ByteBuffer): Long {
115+
// Be careful to not hold a reference to the buffer after returning. It immediately becomes invalid!
116+
Log.i(TAG, "onMoreData called: frameCount=$frameCount sizeBytes=${buffer.capacity()}")
117+
return 0
118+
}
119+
@Suppress("unused") // called from native, on callback thread (not main thread!)
120+
private fun onCanWriteMoreData(frameCount: Long, sizeBytes: Long) {
121+
Log.i(TAG, "onCanWriteMoreData called: frameCount=$frameCount sizeBytes=$sizeBytes")
122+
}
76123
}

app/src/main/res/values-ca/strings.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -388,7 +388,7 @@
388388
<string name="spk_encoding_dra">DRA</string>
389389
<string name="spk_encoding_dts_hd_ma">Àudio mestre DTS-HD</string>
390390
<string name="spk_encoding_aptx">aptX</string>
391-
<string name="spk_encoding_lhdc_ll">LLAC (LDHC-LL)</string>
391+
<string name="spk_encoding_lhdc_ll">LLAC (LHDC-LL)</string>
392392
<string name="spk_encoding_aac_ltp">AAC-LTP</string>
393393
<string name="spk_encoding_aac_ld">AAC-LD</string>
394394
<string name="spk_encoding_aptx_twsp">aptX TWS+</string>

app/src/main/res/values-de/strings.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -325,7 +325,7 @@
325325
<string name="spk_encoding_ldac">LDAC</string>
326326
<string name="spk_encoding_celt">CELT</string>
327327
<string name="spk_encoding_lhdc">LHDC</string>
328-
<string name="spk_encoding_lhdc_ll">LLAC (LDHC-LL)</string>
328+
<string name="spk_encoding_lhdc_ll">LLAC (LHDC-LL)</string>
329329
<string name="spk_encoding_aptx_twsp">aptX TWS+</string>
330330
<string name="spk_encoding_mpegh">MPEG-H</string>
331331
<string name="spk_encoding_iec60958">IEC 60958</string>

app/src/main/res/values-et/strings.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -357,7 +357,7 @@
357357
<string name="mix_port_flag_hw_av_sync">Raudvaraline A/V sünkroniseerimine</string>
358358
<string name="mix_port_flag_direct">Otseväljund</string>
359359
<string name="spk_encoding_aac_latm">AAC LATMis</string>
360-
<string name="spk_encoding_lhdc_ll">LLAC (LDHC-LL)</string>
360+
<string name="spk_encoding_lhdc_ll">LLAC (LHDC-LL)</string>
361361
<string name="spk_encoding_mat_1_0">Dolby MAT 1.0</string>
362362
<string name="spk_encoding_mat_2_1">Dolby MAT 2.1</string>
363363
<string name="spk_encoding_aptx">aptX</string>

app/src/main/res/values-fa/strings.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -356,7 +356,7 @@
356356
<string name="spk_encoding_mpegh_sub_lc_l4">MPEG-H Low Complexity L4</string>
357357
<string name="spk_encoding_amr_wb">AMR-WB</string>
358358
<string name="spk_encoding_aac">AAC</string>
359-
<string name="spk_encoding_lhdc_ll">LLAC (LDHC-LL)</string>
359+
<string name="spk_encoding_lhdc_ll">LLAC (LHDC-LL)</string>
360360
<string name="spk_encoding_lc3">LC3</string>
361361
<string name="spk_encoding_aac_ssr">AAC-SSR</string>
362362
<string name="spk_encoding_mpegh_sub_lc_l3">MPEG-H Low Complexity L3</string>

app/src/main/res/values-gl/strings.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -353,7 +353,7 @@
353353
<string name="spk_encoding_celt">CELT</string>
354354
<string name="spk_encoding_aptx_adaptive">aptX Adaptive</string>
355355
<string name="spk_encoding_lhdc">LHDC</string>
356-
<string name="spk_encoding_lhdc_ll">LLAC (LDHC-LL)</string>
356+
<string name="spk_encoding_lhdc_ll">LLAC (LHDC-LL)</string>
357357
<string name="spk_encoding_aptx_twsp">aptX TWS+</string>
358358
<string name="spk_encoding_lc3">LC3</string>
359359
<string name="spk_encoding_mpegh">MPEG-H</string>

app/src/main/res/values-ja/strings.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -368,7 +368,7 @@
368368
<string name="spk_encoding_dsd">ダイレクトストリームデジタル (DSD)</string>
369369
<string name="spk_encoding_evrc">EVRC</string>
370370
<string name="spk_encoding_aac_adts">AAC in ADTS</string>
371-
<string name="spk_encoding_lhdc_ll">LLAC (LDHC-LL)</string>
371+
<string name="spk_encoding_lhdc_ll">LLAC (LHDC-LL)</string>
372372
<string name="spk_encoding_mpegh">MPEG-H</string>
373373
<string name="spk_encoding_aac_adts_eld">AAC-ELD in ADTS</string>
374374
<string name="spk_encoding_aptx_twsp">aptX TWS+</string>

app/src/main/res/values-ko/strings.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,7 @@
322322
<string name="mix_port_flag_primary">Primary output</string>
323323
<string name="audio_not_initialized">오디오 재생 시스템이 초기화되지 않았습니다. 아무거나 재생해 보세요!</string>
324324
<string name="mix_port_flag_bit_perfect">비트 퍼펙트</string>
325-
<string name="spk_encoding_lhdc_ll">LLAC (LDHC-LL)</string>
325+
<string name="spk_encoding_lhdc_ll">LLAC (LHDC-LL)</string>
326326
<string name="mix_port_flag_ultrasound">Ultrasound</string>
327327
<string name="mix_port_flag_iec958_nonaudio">IEC 958 (non-audio)</string>
328328
<string name="spk_encoding_aac">AAC</string>

app/src/main/res/values-pl/strings.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -366,7 +366,7 @@
366366
<string name="spk_encoding_qcelp">QCELP</string>
367367
<string name="spk_encoding_dra">DRA</string>
368368
<string name="spk_encoding_sbc">SBC</string>
369-
<string name="spk_encoding_lhdc_ll">LLAC (LDHC-LL)</string>
369+
<string name="spk_encoding_lhdc_ll">LLAC (LHDC-LL)</string>
370370
<string name="spk_encoding_aac_adts_ltp">AAC-LTP w ADTS</string>
371371
<string name="what_were_you_doing">Co robiłeś/aś w Gramophone, gdy wystąpiła ta awaria? Podaj jak najwięcej szczegółów, aby zwiększyć szanse na naprawienie problemu.\n\nPamiętaj, że w przypadku zgłoszeń błędów preferowany jest język angielski, ale spróbuję użyć tłumaczenia maszynowego, jeśli wyślesz zgłoszenie w innym języku.</string>
372372
<string name="spk_encoding_lc3">LC3</string>

app/src/main/res/values-uk/strings.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,7 @@
318318
<string name="spk_encoding_sbc">SBC</string>
319319
<string name="spk_encoding_ldac">LDAC</string>
320320
<string name="spk_encoding_celt">CELT</string>
321-
<string name="spk_encoding_lhdc_ll">LLAC (LDHC-LL)</string>
321+
<string name="spk_encoding_lhdc_ll">LLAC (LHDC-LL)</string>
322322
<string name="spk_encoding_iec60958">IEC 60958</string>
323323
<string name="spk_encoding_aptx_adaptive_qlea">aptX Adaptive через LE Audio</string>
324324
<string name="spk_encoding_aac_adts_ltp">AAC-LTP в ADTS</string>

app/src/main/res/values/strings.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -368,7 +368,7 @@
368368
<string name="spk_encoding_celt">CELT</string>
369369
<string name="spk_encoding_aptx_adaptive">aptX Adaptive</string>
370370
<string name="spk_encoding_lhdc">LHDC</string>
371-
<string name="spk_encoding_lhdc_ll">LLAC (LDHC-LL)</string>
371+
<string name="spk_encoding_lhdc_ll">LLAC (LHDC-LL)</string>
372372
<string name="spk_encoding_aptx_twsp">aptX TWS+</string>
373373
<string name="spk_encoding_lc3">LC3</string>
374374
<string name="spk_encoding_mpegh">MPEG-H</string>

0 commit comments

Comments
 (0)