Skip to content

Commit 1121930

Browse files
authored
Oboe support (#167)
* update libpd to oboe branch, and link PdCore with oboe library * add new input/output audio device settings to preferences and audio logic * update demo apps for oboe changes - add MODIFY_AUDIO_SETTINGS permission, which oboe needs to give best results - add "APP_STL := c++_shared" to Application.mk when the app needs native compilation * bump version to 1.4.0
1 parent e0be3be commit 1121930

File tree

19 files changed

+236
-12
lines changed

19 files changed

+236
-12
lines changed

CircleOfFifths/AndroidManifest.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
3+
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
34
<application android:icon="@drawable/icon" android:label="@string/app_name">
45
<activity android:name=".CircleOfFifths" android:label="@string/app_name"
56
android:screenOrientation="portrait" android:theme="@style/DisableSoundEffects"

PdCore/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ allprojects {
2323
// These are specific to PdCore, but nexusPublishing needs them here:
2424
// https://github.com/gradle-nexus/publish-plugin/issues/84
2525
group = 'io.github.libpd.android'
26-
version = '1.3.0-SNAPSHOT'
26+
version = '1.4.0-SNAPSHOT'
2727

2828
// Create a Sonatype user token for these environment variables:
2929
// export ORG_GRADLE_PROJECT_sonatypeUsername="<tokenUsername>"

PdCore/pd-core/build.gradle

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ version = rootProject.version
1010

1111
dependencies {
1212
implementation 'androidx.legacy:legacy-support-v4:' + rootProject.androidxLegacySupportVersion
13+
implementation 'com.google.oboe:oboe:1.10.0'
1314
}
1415

1516
android {
@@ -26,12 +27,19 @@ android {
2627
}
2728

2829
buildFeatures {
29-
prefabPublishing = true
30+
// Export prefab, that will allow apps to compile externals
31+
prefabPublishing = true
32+
33+
// Import prefab: extract native libs (mainly Oboe), to be able to access the headers and to link to the libs
34+
prefab = true
3035
}
3136

3237
prefab {
3338
pd {
34-
headers = 'src/main/jni/libpd/pure-data/src'
39+
headers = 'src/main/jni/libpd/pure-data/src'
40+
}
41+
pdnativeoboe {
42+
headers = 'src/main/jni/libpd/jni/oboe'
3543
}
3644
}
3745

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
/**
2+
*
3+
* For information on usage and redistribution, and for a DISCLAIMER OF ALL
4+
* WARRANTIES, see the file, "LICENSE.txt," in this distribution.
5+
*
6+
*/
7+
8+
package org.puredata.android.io;
9+
import org.puredata.android.service.R;
10+
11+
import android.content.Context;
12+
import android.app.Activity;
13+
import android.media.AudioManager;
14+
import android.media.AudioDeviceCallback;
15+
import android.media.AudioDeviceInfo;
16+
import android.util.Log;
17+
import android.content.res.Resources;
18+
import android.preference.ListPreference;
19+
import android.preference.PreferenceFragment;
20+
21+
import java.util.ArrayList;
22+
import java.util.List;
23+
24+
public class AudioDevices {
25+
26+
private static final String TAG = "AudioDevices";
27+
private AudioManager mAudioManager;
28+
29+
private class AudioDeviceList {
30+
private ListPreference mPref;
31+
private List<String> mEntries = new ArrayList<>();
32+
private List<String> mValues = new ArrayList<>();
33+
34+
private CharSequence[] listToArray(List<String> l) {
35+
String[] array = new String[l.size()];
36+
l.toArray(array);
37+
return array;
38+
}
39+
AudioDeviceList(PreferenceFragment prefFragment, String key) {
40+
mPref = (ListPreference) prefFragment.findPreference(key);
41+
mEntries.add("Default");
42+
mValues.add("-1");
43+
}
44+
public void add(AudioDeviceInfo device) {
45+
String name = device.getProductName().toString() + " " + typeToString(device.getType());
46+
mEntries.add(name);
47+
mValues.add(Integer.toString(device.getId()));
48+
mPref.setEntries(listToArray(mEntries));
49+
mPref.setEntryValues(listToArray(mValues));
50+
}
51+
}
52+
53+
private AudioDeviceList mInputDevices = null;
54+
private AudioDeviceList mOutputDevices = null;
55+
56+
/**
57+
* @param context activity or service that calls this method
58+
*/
59+
public AudioDevices(Activity context) {
60+
mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
61+
PreferenceFragment prefFragment = (PreferenceFragment) context.getFragmentManager().findFragmentByTag("prefFragment");
62+
Resources res = context.getResources();
63+
mInputDevices = new AudioDeviceList(prefFragment, res.getString(R.string.pref_key_indevice));
64+
mOutputDevices = new AudioDeviceList(prefFragment, res.getString(R.string.pref_key_outdevice));
65+
setupAudioDeviceCallback();
66+
}
67+
68+
/**
69+
* Converts the value from {@link AudioDeviceInfo#getType()} into a human
70+
* readable string
71+
* @param type One of the {@link AudioDeviceInfo}.TYPE_* values
72+
* e.g. AudioDeviceInfo.TYPE_BUILT_IN_SPEAKER
73+
* @return string which describes the type of audio device
74+
*/
75+
static String typeToString(int type){
76+
switch (type) {
77+
case AudioDeviceInfo.TYPE_AUX_LINE:
78+
return "auxiliary line-level connectors";
79+
case AudioDeviceInfo.TYPE_BLUETOOTH_A2DP:
80+
return "Bluetooth device supporting the A2DP profile";
81+
case AudioDeviceInfo.TYPE_BLUETOOTH_SCO:
82+
return "Bluetooth device typically used for telephony";
83+
case AudioDeviceInfo.TYPE_BUILTIN_EARPIECE:
84+
return "built-in earphone speaker";
85+
case AudioDeviceInfo.TYPE_BUILTIN_MIC:
86+
return "built-in microphone";
87+
case AudioDeviceInfo.TYPE_BUILTIN_SPEAKER:
88+
return "built-in speaker";
89+
case AudioDeviceInfo.TYPE_BUS:
90+
return "BUS";
91+
case AudioDeviceInfo.TYPE_DOCK:
92+
return "DOCK";
93+
case AudioDeviceInfo.TYPE_FM:
94+
return "FM";
95+
case AudioDeviceInfo.TYPE_FM_TUNER:
96+
return "FM tuner";
97+
case AudioDeviceInfo.TYPE_HDMI:
98+
return "HDMI";
99+
case AudioDeviceInfo.TYPE_HDMI_ARC:
100+
return "HDMI audio return channel";
101+
case AudioDeviceInfo.TYPE_IP:
102+
return "IP";
103+
case AudioDeviceInfo.TYPE_LINE_ANALOG:
104+
return "line analog";
105+
case AudioDeviceInfo.TYPE_LINE_DIGITAL:
106+
return "line digital";
107+
case AudioDeviceInfo.TYPE_TELEPHONY:
108+
return "telephony";
109+
case AudioDeviceInfo.TYPE_TV_TUNER:
110+
return "TV tuner";
111+
case AudioDeviceInfo.TYPE_USB_ACCESSORY:
112+
return "USB accessory";
113+
case AudioDeviceInfo.TYPE_USB_DEVICE:
114+
return "USB device";
115+
case AudioDeviceInfo.TYPE_WIRED_HEADPHONES:
116+
return "wired headphones";
117+
case AudioDeviceInfo.TYPE_WIRED_HEADSET:
118+
return "wired headset";
119+
default:
120+
case AudioDeviceInfo.TYPE_UNKNOWN:
121+
return "unknown";
122+
}
123+
}
124+
125+
private void setupAudioDeviceCallback(){
126+
// Note that we will immediately receive a call to onDevicesAdded with the list of
127+
// devices which are currently connected.
128+
mAudioManager.registerAudioDeviceCallback(new AudioDeviceCallback() {
129+
@Override
130+
public void onAudioDevicesAdded(AudioDeviceInfo[] addedDevices) {
131+
for (AudioDeviceInfo device : addedDevices){
132+
if (device.isSource()) mInputDevices.add(device);
133+
else if (device.isSink()) mOutputDevices.add(device);
134+
}
135+
}
136+
137+
public void onAudioDevicesRemoved(AudioDeviceInfo[] removedDevices) {
138+
}
139+
}, null);
140+
}
141+
}

PdCore/pd-core/src/main/java/org/puredata/android/io/PdAudio.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
public class PdAudio {
2727

2828
private static AudioWrapper audioWrapper = null;
29+
private static int inputDeviceId = -1;
30+
private static int outputDeviceId = -1;
2931
private static final Handler handler = new Handler(Looper.getMainLooper());
3032
private static final Runnable pollRunner = new Runnable() {
3133
@Override
@@ -77,6 +79,17 @@ protected int process(short[] inBuffer, short[] outBuffer) {
7779
}
7880
}
7981

82+
/**
83+
* Set the audio input and output devices Id. Call it before startAudio().
84+
*
85+
* @param inDeviceId id of the audio input device (-1 means the default device)
86+
* @param outDeviceId id of the audio output device (-1 means the default device)
87+
*/
88+
public synchronized static void setDevicesId(int inDeviceId, int outDeviceId) {
89+
inputDeviceId = inDeviceId;
90+
outputDeviceId = outDeviceId;
91+
}
92+
8093
/**
8194
* Starts the audio components.
8295
*
@@ -85,6 +98,8 @@ protected int process(short[] inBuffer, short[] outBuffer) {
8598
public synchronized static void startAudio(Context context) {
8699
PdBase.computeAudio(true);
87100
if (PdBase.implementsAudio()) {
101+
PdBase.setRecordingDeviceId(inputDeviceId);
102+
PdBase.setPlaybackDeviceId(outputDeviceId);
88103
handler.post(pollRunner);
89104
PdBase.startAudio();
90105
} else {

PdCore/pd-core/src/main/java/org/puredata/android/service/PdPreferences.java

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
package org.puredata.android.service;
99

1010
import org.puredata.android.io.AudioParameters;
11+
import org.puredata.android.io.AudioDevices;
1112
import org.puredata.core.PdBase;
1213

1314
import android.content.Context;
@@ -16,6 +17,7 @@
1617
import android.os.Bundle;
1718
import android.preference.PreferenceActivity;
1819
import android.preference.PreferenceManager;
20+
import android.preference.PreferenceFragment;
1921

2022
/**
2123
*
@@ -27,20 +29,33 @@
2729
*/
2830
public class PdPreferences extends PreferenceActivity {
2931

30-
@SuppressWarnings("deprecation")
32+
public AudioDevices audioDevices = null;
33+
3134
@Override
3235
protected void onCreate(Bundle savedInstanceState) {
3336
super.onCreate(savedInstanceState);
3437
AudioParameters.init(this);
3538
initPreferences(getApplicationContext());
36-
addPreferencesFromResource(R.xml.preferences);
39+
getFragmentManager().beginTransaction().replace(android.R.id.content, new MyPreferenceFragment(), "prefFragment").commit();
3740
}
3841

3942
@Override
4043
protected void onDestroy() {
4144
super.onDestroy();
4245
}
4346

47+
public static class MyPreferenceFragment extends PreferenceFragment
48+
{
49+
@Override
50+
public void onCreate(final Bundle savedInstanceState)
51+
{
52+
super.onCreate(savedInstanceState);
53+
Resources res = getResources();
54+
addPreferencesFromResource(R.xml.preferences);
55+
((PdPreferences)getActivity()).audioDevices = new AudioDevices(getActivity());
56+
}
57+
}
58+
4459
/**
4560
* If no preferences are available, initialize preferences with defaults suggested by {@link PdBase} or {@link AudioParameters}, in that order.
4661
*
@@ -53,8 +68,10 @@ public static void initPreferences(Context context) {
5368
SharedPreferences.Editor editor = prefs.edit();
5469
int srate = PdBase.suggestSampleRate();
5570
editor.putString(res.getString(R.string.pref_key_srate), "" + ((srate > 0) ? srate : AudioParameters.suggestSampleRate()));
71+
editor.putString(res.getString(R.string.pref_key_indevice), res.getStringArray(R.array.indevice_values)[0]);
5672
int nic = PdBase.suggestInputChannels();
5773
editor.putString(res.getString(R.string.pref_key_inchannels), "" + ((nic > 0) ? nic : AudioParameters.suggestInputChannels()));
74+
editor.putString(res.getString(R.string.pref_key_outdevice), res.getStringArray(R.array.outdevice_values)[0]);
5875
int noc = PdBase.suggestOutputChannels();
5976
editor.putString(res.getString(R.string.pref_key_outchannels), "" + ((noc > 0) ? noc : AudioParameters.suggestOutputChannels()));
6077
editor.commit();

PdCore/pd-core/src/main/java/org/puredata/android/service/PdService.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,18 @@ public synchronized void initAudio(int srate, int nic, int noc, float millis) th
100100
stopForeground();
101101
Resources res = getResources();
102102
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
103+
int in_id = -1;
104+
int out_id = -1;
105+
{
106+
String s = prefs.getString(res.getString(R.string.pref_key_indevice), null);
107+
if (s != null) {
108+
in_id = Integer.parseInt(s);
109+
}
110+
s = prefs.getString(res.getString(R.string.pref_key_outdevice), null);
111+
if (s != null) {
112+
out_id = Integer.parseInt(s);
113+
}
114+
}
103115
if (srate < 0) {
104116
String s = prefs.getString(res.getString(R.string.pref_key_srate), null);
105117
if (s != null) {
@@ -137,6 +149,7 @@ public synchronized void initAudio(int srate, int nic, int noc, float millis) th
137149
millis = 50.0f; // conservative choice
138150
}
139151
int tpb = (int) (0.001f * millis * srate / PdBase.blockSize()) + 1;
152+
PdAudio.setDevicesId(in_id, out_id);
140153
PdAudio.initAudio(srate, nic, noc, tpb, true);
141154
sampleRate = srate;
142155
inputChannels = nic;
@@ -213,6 +226,7 @@ public boolean onUnbind(Intent intent) {
213226
@Override
214227
public void onCreate() {
215228
super.onCreate();
229+
216230
AudioParameters.init(this);
217231
if (!abstractionsInstalled) {
218232
try {
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
APP_PLATFORM := android-28
22
APP_OPTIM := release
33
APP_ABI := armeabi-v7a arm64-v8a x86 x86_64
4+
APP_STL := c++_shared

PdCore/pd-core/src/main/jni/libpd

PdCore/pd-core/src/main/res/values/audio.xml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,12 @@
1818
<item>44100</item>
1919
<item>48000</item>
2020
</string-array>
21+
<string-array name="indevice_labels">
22+
<item>Default</item>
23+
</string-array>
24+
<string-array name="indevice_values">
25+
<item>-1</item>
26+
</string-array>
2127
<string-array name="inchannels_labels">
2228
<item>None</item>
2329
<item>Mono</item>
@@ -28,6 +34,12 @@
2834
<item>1</item>
2935
<item>2</item>
3036
</string-array>
37+
<string-array name="outdevice_labels">
38+
<item>Default</item>
39+
</string-array>
40+
<string-array name="outdevice_values">
41+
<item>-1</item>
42+
</string-array>
3143
<string-array name="outchannels_labels">
3244
<item>None</item>
3345
<item>Mono</item>

0 commit comments

Comments
 (0)