Following up (or should have been preceding) my last post, I am now wondering if I am on the wrong path.
I want to play midi file data (not audio) to an external device, such as a midi enabled keyboard, and have the midi data play the keyboard.
As per my last post, I have:
MediaPlayer mediaPlayer;
String music = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC).getAbsolutePath();
mediaPlayer = MediaPlayer.create(MainActivity.this, Uri.parse(music + "/test.mid"));
mediaPlayer.start();
When I run this, it just play the midi file as audio.
Later, the next part is sending this out (hopefully) by Bluetooth, ie code sometime like form the suggestion from my last post:
AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
AudioDeviceInfo[] devices = audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS);
for (AudioDeviceInfo device : devices) {
int type = device.getType();
if (device.getType() == AudioDeviceInfo.TYPE_BLE_HEADSET ) {
mediaPlayer.setPreferredDevice(device);
break;
}
}
However, is the above going to midi data, or just the generated audio? Can MediaPlayerMediaPlayer be used for this, or do I need to use something completely different such as java-midi or the MidiManager as used in these examples.
I have not got any Bluetooth midi receiver device yet (ie to play into the keyboard), as I do not want to purchase something like this, for example this, though it only claim to support iOS, but isn't Bluetooth universal?, At any rate, that is a separate topic, this is just about actually playing out the midi data
I only ever want to replay a file, I never want to actually generate the midi events, so was hoping something simpler like MediaPlayer would do what I am after, but is this correct?
Related
I am new to Android development, and would like to know if it is possible to send a midi file data out of Bluetooth?
I am using the following to load and start a midi file..
MediaPlayer mediaPlayer;
String music = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC).getAbsolutePath();
mediaPlayer = MediaPlayer.create(MainActivity.this, Uri.parse(music + "/test.mid"));
mediaPlayer.start();
After requesting permissions etc, this will start playing the midi file on my device.
My next step it to send this out via Bluetooth, i.e. I want just the midi going to blue tooth and NOT any other audio that may be playing on my device (in another application).
How can this be done (if it can be done)?
Edit 1
Just a bit more info that may have not been clear.
What I am after is sending midi data, NOT midi audio. Ie I want to load a midi file, and then send via Bluetooth to a Bluetooth midi cable like this, which is plugged into a keyboard, and have the midi file play the keyboard.
I am trying to record both Uplink and Downlink voice using Android. Regardless the law and everything, i am already aware, so please do not put comments related to the law.
The code below works fine, except when i mute the microphone, it wont record the downlink voice.
I am using Android 8.1. I've tried using a third party app called ACR on the same device, and it works fine, while i am muted, it still records the downlink voice.
val audioManager = applicationContext.getSystemService(Context.AUDIO_SERVICE) as AudioManager
val maximumVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_VOICE_CALL)
audioManager.setStreamVolume(AudioManager.STREAM_VOICE_CALL, maximumVolume, 0)
val audioSource = MediaRecorder.AudioSource.MIC
val mediaRecorder = MediaRecorder()
mediaRecorder.apply {
setAudioSource(audioSource)
setOutputFormat(MediaRecorder.OutputFormat.MPEG_4)
setAudioEncoder(MediaRecorder.AudioEncoder.AAC)
setAudioChannels(audioChannels)
setAudioSamplingRate(audioSamplingRate)
setAudioEncodingBitRate(audioEncodingBitRate)
setOutputFile(path)
prepare()
start()
This is not an issue. You set the MediaRecorder to use MIC as input, so if you MUTE the microphone it's obliviously that the input signat is lost/muted. When you use "downlink" word I expected to see a different input source as VOICECALL or DOWNLINK instead of MIC. Trying to record a voicecall using the MIC it's wrong in my opinion because: (1) you have to set max volume to speaker and redirect the voicecall through it (2) while recording a voicecall from the MIC the caller hears ALL what it happens around your device and all what you're saying to other people (3) this method records much noise and echoes. The right way is to record from VOICECALL but most of new devices (using newer Android version) prevents to record from this source and allows it only at System Apps. ACR uses a workaround by calling hidden API methods, but this method could stop stop work at any time due to Android updates.
I have my phone connected to a Bluetooth speaker and the headphones plugged in. Now I'd like to play audio through the Bluetooth speaker.
When I set the audio stream to AudioManager.STREAM_MUSIC it just plays over the headphones.
It doesn't matter if it plays on the headphones as well but I need it to play on the Bluetooth speaker.
How is this possible? The app SoundAbout manages to do that so there must be a way.
EDIT: When I plug in the headphones and only afterwards connect to the Bluetooth speakers all audio plays through the Bluetooth speakers which I want. But I can't expect the user to find that out and before having to show them a complicated message I'd rather find out a way to make the sound always play through BT speakers when connected to some.
Thanks
(Note this is not the same question as this: How to Play audio through speaker even when headset is plugged in?
I want it to play on Bluetooth speakers, not on the integrated speaker of the phone.)
Solution
Suppose you already tested STREAM_RING on your new instance of media player and not directly setting stream type, and it didn't work out, You need a correct profile for your bluetooth device.
Take a look at this article
Read the "Implementing HAL" section, there is alot of source for different profile that you may be able to use.
There is also an easy solution which is to change your device profile to HEADSET in your getServiceConnected() method, it will turn into a Stay connected device but the output will become mono! As I recall, Which is a shame for speakers, A2DP also may not be supported in some hardwares and still interrupted by wire headsets.
I suggest to create a new profile and use it, a little bit tricky working with HAL but will worth it,
Sorry that I can not provide a source code for you at the moment.
If you have your routing logic within your application then based on that you can decide on which output the audio to be played.
I have a test app written for the exact purpose.
My Github Link
You can also route audio as you want based on a requirement.You can refer this github link for routing
Bluetooth connection may work with below state is true .
After receive BluetoothA2dp.STATE_CONNECTED, you can play music as normal.
Java Code Examples for android.bluetooth.BluetoothA2dp.STATE_CONNECTED
public BluetoothHandsfree(Context context, CallManager cm) {
mCM = cm;
mContext = context;
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
boolean bluetoothCapable = (adapter != null);
mHeadset = null; // nothing connected yet
mA2dp = new BluetoothA2dp(mContext);
mA2dpState = BluetoothA2dp.STATE_DISCONNECTED;
mA2dpDevice = null;
mA2dpSuspended = false;
mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
mStartCallWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
TAG + ":StartCall");
mStartCallWakeLock.setReferenceCounted(false);
mStartVoiceRecognitionWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
TAG + ":VoiceRecognition");
mStartVoiceRecognitionWakeLock.setReferenceCounted(false);
mLocalBrsf = BRSF_AG_THREE_WAY_CALLING |
BRSF_AG_EC_NR |
BRSF_AG_REJECT_CALL |
BRSF_AG_ENHANCED_CALL_STATUS;
if (sVoiceCommandIntent == null) {
sVoiceCommandIntent = new Intent(Intent.ACTION_VOICE_COMMAND);
sVoiceCommandIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
if (mContext.getPackageManager().resolveActivity(sVoiceCommandIntent, 0) != null &&
BluetoothHeadset.isBluetoothVoiceDialingEnabled(mContext)) {
mLocalBrsf |= BRSF_AG_VOICE_RECOG;
}
mBluetoothPhoneState = new BluetoothPhoneState();
mUserWantsAudio = true;
mPhonebook = new BluetoothAtPhonebook(mContext, this);
mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
cdmaSetSecondCallState(false);
if (bluetoothCapable) {
resetAtState();
}
}
please find below links : with sample codes it may help you.
Java Code Examples for android.bluetooth.BluetoothHeadset
Programmatically connect to paired Bluetooth speaker and play audio
You need to instantiate a new object of class MediaPlayer and use the following method on it
amediaplayer.setAudioStreamType(AudioManager.STREAM_RING)
Do not forget to check authorization to use bluetooth, you are unable to send anything to speaker via bluetooth without user privilege as you know.
Audiomanager overrides and routes audio to the latest connected device(either wired headset or bluetooth headset). In android, we do not have any option to override this setting unless it is a system app and route the audio wherever we wish to route. However, you can use reflection apis and override this setting. Audiomanager suspends bluetooth connection route(if already connected)if wired headset is connected and vice versa. You can look at the code here.
Hence using reflection apis you can toggle bluetooth audio route by invoking this method.
In my Android app, the user gets to specifically select the audio output between internal speakers and earphone. Here is my routine to turn the speaker phone on:
static void useSpeaker(Context ctx) {
AudioManager am = (AudioManager)ctx.getSystemService(Context.AUDIO_SERVICE);
am.setMode(AudioManager.MODE_NORMAL);
am.setSpeakerphoneOn(true);
}
Once the audio is set to speakers, even if you plug in the earphone, the output still goes through the speaker. I have verified on a number of different tablets and phones that this logic works.
However, on one device, once the earphone is plugged in, the audio output automatically switches from speakers to earphone.
I am wondering if there is something else that I need to take care of in the code. Or, is it just this device ignoring my directive? Regards.
After I use the Android AudioManager to set SCO ON and speakerphone ON, then I return it back to where it started, the media stream will then stay on earpiece instead of the phones speaker. Here is where I make the changes to route a stream through the SCO speakerphone (which all works great)
if (am2.isBluetoothScoAvailableOffCall()) {
am2.startBluetoothSco();
}
if(!am2.isSpeakerphoneOn()){
speakerPhoneWasOn = false;
am2.setSpeakerphoneOn(true);
}
myHash.put(TextToSpeech.Engine.KEY_PARAM_STREAM,
String.valueOf(AudioManager.STREAM_VOICE_CALL));
am2.requestAudioFocus(null, AudioManager.STREAM_VOICE_CALL,
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
Then I try to change it all back after my message is read (I am using TTS)
if(SMSstream == 1){
if (am2.isBluetoothScoAvailableOffCall()) {
am2.stopBluetoothSco();
}
if(!speakerPhoneWasOn){
am2.setSpeakerphoneOn(false);
}
}
else{
}
am2.setMode(OLD_AUDIO_MODE);
am2.abandonAudioFocus(null);
But it stays with the phone earpiece instead of the speaker until I reboot. I saw a few post that had the opposite problem but none with this issue. I have a Droid 3 with Android 2.3.4.
You can see the whole project and source code here: http://code.google.com/p/a2dpvolume/
OK, I finally fixed this problem. Its not pretty. There is some sort of bug in the AudioManager I believe. Even after abandoning focus it would leave the device streams in a mess. Unless the last TTS was read over the music stream just before abandoning focus, it would route streams wrong, mute streams, etc. So, I just have it read a single period if a SMS had been read over any stream except the music stream before abandoning focus. I also cleaned up the order I called things in, and fixed some of the phone mode, etc after reading a TTS. Strangely it seems to work well.