diff --git a/CircleOfFifths/AndroidManifest.xml b/CircleOfFifths/AndroidManifest.xml index 203ba742..41838ba5 100644 --- a/CircleOfFifths/AndroidManifest.xml +++ b/CircleOfFifths/AndroidManifest.xml @@ -1,5 +1,6 @@ + mEntries = new ArrayList<>(); + private List mValues = new ArrayList<>(); + + private CharSequence[] listToArray(List l) { + String[] array = new String[l.size()]; + l.toArray(array); + return array; + } + AudioDeviceList(PreferenceFragment prefFragment, String key) { + mPref = (ListPreference) prefFragment.findPreference(key); + mEntries.add("Default"); + mValues.add("-1"); + } + public void add(AudioDeviceInfo device) { + String name = device.getProductName().toString() + " " + typeToString(device.getType()); + mEntries.add(name); + mValues.add(Integer.toString(device.getId())); + mPref.setEntries(listToArray(mEntries)); + mPref.setEntryValues(listToArray(mValues)); + } + } + + private AudioDeviceList mInputDevices = null; + private AudioDeviceList mOutputDevices = null; + + /** + * @param context activity or service that calls this method + */ + public AudioDevices(Activity context) { + mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); + PreferenceFragment prefFragment = (PreferenceFragment) context.getFragmentManager().findFragmentByTag("prefFragment"); + Resources res = context.getResources(); + mInputDevices = new AudioDeviceList(prefFragment, res.getString(R.string.pref_key_indevice)); + mOutputDevices = new AudioDeviceList(prefFragment, res.getString(R.string.pref_key_outdevice)); + setupAudioDeviceCallback(); + } + + /** + * Converts the value from {@link AudioDeviceInfo#getType()} into a human + * readable string + * @param type One of the {@link AudioDeviceInfo}.TYPE_* values + * e.g. AudioDeviceInfo.TYPE_BUILT_IN_SPEAKER + * @return string which describes the type of audio device + */ + static String typeToString(int type){ + switch (type) { + case AudioDeviceInfo.TYPE_AUX_LINE: + return "auxiliary line-level connectors"; + case AudioDeviceInfo.TYPE_BLUETOOTH_A2DP: + return "Bluetooth device supporting the A2DP profile"; + case AudioDeviceInfo.TYPE_BLUETOOTH_SCO: + return "Bluetooth device typically used for telephony"; + case AudioDeviceInfo.TYPE_BUILTIN_EARPIECE: + return "built-in earphone speaker"; + case AudioDeviceInfo.TYPE_BUILTIN_MIC: + return "built-in microphone"; + case AudioDeviceInfo.TYPE_BUILTIN_SPEAKER: + return "built-in speaker"; + case AudioDeviceInfo.TYPE_BUS: + return "BUS"; + case AudioDeviceInfo.TYPE_DOCK: + return "DOCK"; + case AudioDeviceInfo.TYPE_FM: + return "FM"; + case AudioDeviceInfo.TYPE_FM_TUNER: + return "FM tuner"; + case AudioDeviceInfo.TYPE_HDMI: + return "HDMI"; + case AudioDeviceInfo.TYPE_HDMI_ARC: + return "HDMI audio return channel"; + case AudioDeviceInfo.TYPE_IP: + return "IP"; + case AudioDeviceInfo.TYPE_LINE_ANALOG: + return "line analog"; + case AudioDeviceInfo.TYPE_LINE_DIGITAL: + return "line digital"; + case AudioDeviceInfo.TYPE_TELEPHONY: + return "telephony"; + case AudioDeviceInfo.TYPE_TV_TUNER: + return "TV tuner"; + case AudioDeviceInfo.TYPE_USB_ACCESSORY: + return "USB accessory"; + case AudioDeviceInfo.TYPE_USB_DEVICE: + return "USB device"; + case AudioDeviceInfo.TYPE_WIRED_HEADPHONES: + return "wired headphones"; + case AudioDeviceInfo.TYPE_WIRED_HEADSET: + return "wired headset"; + default: + case AudioDeviceInfo.TYPE_UNKNOWN: + return "unknown"; + } + } + + private void setupAudioDeviceCallback(){ + // Note that we will immediately receive a call to onDevicesAdded with the list of + // devices which are currently connected. + mAudioManager.registerAudioDeviceCallback(new AudioDeviceCallback() { + @Override + public void onAudioDevicesAdded(AudioDeviceInfo[] addedDevices) { + for (AudioDeviceInfo device : addedDevices){ + if (device.isSource()) mInputDevices.add(device); + else if (device.isSink()) mOutputDevices.add(device); + } + } + + public void onAudioDevicesRemoved(AudioDeviceInfo[] removedDevices) { + } + }, null); + } +} diff --git a/PdCore/pd-core/src/main/java/org/puredata/android/io/PdAudio.java b/PdCore/pd-core/src/main/java/org/puredata/android/io/PdAudio.java index cc577ec2..0b7f8224 100644 --- a/PdCore/pd-core/src/main/java/org/puredata/android/io/PdAudio.java +++ b/PdCore/pd-core/src/main/java/org/puredata/android/io/PdAudio.java @@ -26,6 +26,8 @@ public class PdAudio { private static AudioWrapper audioWrapper = null; + private static int inputDeviceId = -1; + private static int outputDeviceId = -1; private static final Handler handler = new Handler(Looper.getMainLooper()); private static final Runnable pollRunner = new Runnable() { @Override @@ -77,6 +79,17 @@ protected int process(short[] inBuffer, short[] outBuffer) { } } + /** + * Set the audio input and output devices Id. Call it before startAudio(). + * + * @param inDeviceId id of the audio input device (-1 means the default device) + * @param outDeviceId id of the audio output device (-1 means the default device) + */ + public synchronized static void setDevicesId(int inDeviceId, int outDeviceId) { + inputDeviceId = inDeviceId; + outputDeviceId = outDeviceId; + } + /** * Starts the audio components. * @@ -85,6 +98,8 @@ protected int process(short[] inBuffer, short[] outBuffer) { public synchronized static void startAudio(Context context) { PdBase.computeAudio(true); if (PdBase.implementsAudio()) { + PdBase.setRecordingDeviceId(inputDeviceId); + PdBase.setPlaybackDeviceId(outputDeviceId); handler.post(pollRunner); PdBase.startAudio(); } else { diff --git a/PdCore/pd-core/src/main/java/org/puredata/android/service/PdPreferences.java b/PdCore/pd-core/src/main/java/org/puredata/android/service/PdPreferences.java index 61991f44..319922e7 100644 --- a/PdCore/pd-core/src/main/java/org/puredata/android/service/PdPreferences.java +++ b/PdCore/pd-core/src/main/java/org/puredata/android/service/PdPreferences.java @@ -8,6 +8,7 @@ package org.puredata.android.service; import org.puredata.android.io.AudioParameters; +import org.puredata.android.io.AudioDevices; import org.puredata.core.PdBase; import android.content.Context; @@ -16,6 +17,7 @@ import android.os.Bundle; import android.preference.PreferenceActivity; import android.preference.PreferenceManager; +import android.preference.PreferenceFragment; /** * @@ -27,13 +29,14 @@ */ public class PdPreferences extends PreferenceActivity { - @SuppressWarnings("deprecation") + public AudioDevices audioDevices = null; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); AudioParameters.init(this); initPreferences(getApplicationContext()); - addPreferencesFromResource(R.xml.preferences); + getFragmentManager().beginTransaction().replace(android.R.id.content, new MyPreferenceFragment(), "prefFragment").commit(); } @Override @@ -41,6 +44,18 @@ protected void onDestroy() { super.onDestroy(); } + public static class MyPreferenceFragment extends PreferenceFragment + { + @Override + public void onCreate(final Bundle savedInstanceState) + { + super.onCreate(savedInstanceState); + Resources res = getResources(); + addPreferencesFromResource(R.xml.preferences); + ((PdPreferences)getActivity()).audioDevices = new AudioDevices(getActivity()); + } + } + /** * If no preferences are available, initialize preferences with defaults suggested by {@link PdBase} or {@link AudioParameters}, in that order. * @@ -53,8 +68,10 @@ public static void initPreferences(Context context) { SharedPreferences.Editor editor = prefs.edit(); int srate = PdBase.suggestSampleRate(); editor.putString(res.getString(R.string.pref_key_srate), "" + ((srate > 0) ? srate : AudioParameters.suggestSampleRate())); + editor.putString(res.getString(R.string.pref_key_indevice), res.getStringArray(R.array.indevice_values)[0]); int nic = PdBase.suggestInputChannels(); editor.putString(res.getString(R.string.pref_key_inchannels), "" + ((nic > 0) ? nic : AudioParameters.suggestInputChannels())); + editor.putString(res.getString(R.string.pref_key_outdevice), res.getStringArray(R.array.outdevice_values)[0]); int noc = PdBase.suggestOutputChannels(); editor.putString(res.getString(R.string.pref_key_outchannels), "" + ((noc > 0) ? noc : AudioParameters.suggestOutputChannels())); editor.commit(); diff --git a/PdCore/pd-core/src/main/java/org/puredata/android/service/PdService.java b/PdCore/pd-core/src/main/java/org/puredata/android/service/PdService.java index 18dc6c42..f4accfb3 100644 --- a/PdCore/pd-core/src/main/java/org/puredata/android/service/PdService.java +++ b/PdCore/pd-core/src/main/java/org/puredata/android/service/PdService.java @@ -100,6 +100,18 @@ public synchronized void initAudio(int srate, int nic, int noc, float millis) th stopForeground(); Resources res = getResources(); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); + int in_id = -1; + int out_id = -1; + { + String s = prefs.getString(res.getString(R.string.pref_key_indevice), null); + if (s != null) { + in_id = Integer.parseInt(s); + } + s = prefs.getString(res.getString(R.string.pref_key_outdevice), null); + if (s != null) { + out_id = Integer.parseInt(s); + } + } if (srate < 0) { String s = prefs.getString(res.getString(R.string.pref_key_srate), null); if (s != null) { @@ -137,6 +149,7 @@ public synchronized void initAudio(int srate, int nic, int noc, float millis) th millis = 50.0f; // conservative choice } int tpb = (int) (0.001f * millis * srate / PdBase.blockSize()) + 1; + PdAudio.setDevicesId(in_id, out_id); PdAudio.initAudio(srate, nic, noc, tpb, true); sampleRate = srate; inputChannels = nic; @@ -213,6 +226,7 @@ public boolean onUnbind(Intent intent) { @Override public void onCreate() { super.onCreate(); + AudioParameters.init(this); if (!abstractionsInstalled) { try { diff --git a/PdCore/pd-core/src/main/jni/Application.mk b/PdCore/pd-core/src/main/jni/Application.mk index 1d0b95ea..fb60ca30 100644 --- a/PdCore/pd-core/src/main/jni/Application.mk +++ b/PdCore/pd-core/src/main/jni/Application.mk @@ -1,3 +1,4 @@ APP_PLATFORM := android-28 APP_OPTIM := release APP_ABI := armeabi-v7a arm64-v8a x86 x86_64 +APP_STL := c++_shared diff --git a/PdCore/pd-core/src/main/jni/libpd b/PdCore/pd-core/src/main/jni/libpd index d7d1e1ef..2ed8b39b 160000 --- a/PdCore/pd-core/src/main/jni/libpd +++ b/PdCore/pd-core/src/main/jni/libpd @@ -1 +1 @@ -Subproject commit d7d1e1ef6259583065d7bd0b5b37112fa29d2eb6 +Subproject commit 2ed8b39ba0a82c27b9da087dac01d8e5ff55ee85 diff --git a/PdCore/pd-core/src/main/res/values/audio.xml b/PdCore/pd-core/src/main/res/values/audio.xml index 80aef857..7be836c8 100644 --- a/PdCore/pd-core/src/main/res/values/audio.xml +++ b/PdCore/pd-core/src/main/res/values/audio.xml @@ -18,6 +18,12 @@ 44100 48000 + + Default + + + -1 + None Mono @@ -28,6 +34,12 @@ 1 2 + + Default + + + -1 + None Mono diff --git a/PdCore/pd-core/src/main/res/values/strings.xml b/PdCore/pd-core/src/main/res/values/strings.xml index 26d9d507..a808125b 100644 --- a/PdCore/pd-core/src/main/res/values/strings.xml +++ b/PdCore/pd-core/src/main/res/values/strings.xml @@ -5,9 +5,15 @@ SAMPLE_RATE Sample rate Sample rate for Pure Data + INPUT_DEVICE + Input device + Name of the input device INPUT_CHANNELS Input channels Number of input channels + OUTPUT_DEVICE + Output device + Name of the output device OUTPUT_CHANNELS Output channels Number of output channels diff --git a/PdCore/pd-core/src/main/res/xml/preferences.xml b/PdCore/pd-core/src/main/res/xml/preferences.xml index c33c0714..611484e9 100644 --- a/PdCore/pd-core/src/main/res/xml/preferences.xml +++ b/PdCore/pd-core/src/main/res/xml/preferences.xml @@ -5,9 +5,15 @@ android:entries="@array/srate_labels" android:key="@string/pref_key_srate" android:title="@string/pref_title_srate" android:summary="@string/pref_sum_srate"> + + diff --git a/PdTest/AndroidManifest.xml b/PdTest/AndroidManifest.xml index 5961975c..4140ee62 100644 --- a/PdTest/AndroidManifest.xml +++ b/PdTest/AndroidManifest.xml @@ -20,5 +20,6 @@ - + + diff --git a/PdTest/jni/Application.mk b/PdTest/jni/Application.mk index 1dfcd802..234df209 100644 --- a/PdTest/jni/Application.mk +++ b/PdTest/jni/Application.mk @@ -1,4 +1,4 @@ APP_OPTIM := release APP_ABI := armeabi-v7a arm64-v8a x86 x86_64 APP_ALLOW_MISSING_DEPS=true - +APP_STL := c++_shared diff --git a/README.md b/README.md index 5d85c7e8..243a6a60 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ allprojects { ```gradle dependencies { - implementation 'io.github.libpd.android:pd-core:1.3.0-SNAPSHOT' + implementation 'io.github.libpd.android:pd-core:1.4.0-SNAPSHOT' } ``` diff --git a/ScenePlayer/AndroidManifest.xml b/ScenePlayer/AndroidManifest.xml index cf6c4342..565d703c 100644 --- a/ScenePlayer/AndroidManifest.xml +++ b/ScenePlayer/AndroidManifest.xml @@ -10,6 +10,7 @@ + @@ -64,4 +65,4 @@ - \ No newline at end of file + diff --git a/ScenePlayer/jni/Application.mk b/ScenePlayer/jni/Application.mk index 1dfcd802..234df209 100644 --- a/ScenePlayer/jni/Application.mk +++ b/ScenePlayer/jni/Application.mk @@ -1,4 +1,4 @@ APP_OPTIM := release APP_ABI := armeabi-v7a arm64-v8a x86 x86_64 APP_ALLOW_MISSING_DEPS=true - +APP_STL := c++_shared diff --git a/Voice-O-Rama/AndroidManifest.xml b/Voice-O-Rama/AndroidManifest.xml index 75dab709..b4340e15 100644 --- a/Voice-O-Rama/AndroidManifest.xml +++ b/Voice-O-Rama/AndroidManifest.xml @@ -5,6 +5,7 @@ +