How to detect input data through the audio jack? - android

I know that some devices headphone ports (maybe all of them? any reference here would be good) have 3 channels, for stereo sound and microphone. So I was wondering if it's possible to detect something like this pedal on these devices.
If it's possible, using the audio recorder would be enough? I'm studying possibilities for an app here, and this sounds promising if possible.
Thanks
EDIT
I was searching more about it and I was wondering if the pedal would have the same effect as those headsets with buttons. It's deadly easy to override their effects, I created a simple app that does it and now I'm waiting for my pedal to try. Any thoughts about it ?
UPDATE
I just tried it in the pedal and it doesn't work. Although we managed to use the pedal to pause a music app. The device was running Cyanogen though. The voice recorder also didn't detect anything on the key press. Need suggestions on how to detect it :).
UPDATE
AS the bounty is about to expire, a minor update. The last link provided by #Nick.T got some helpful info but I'm still not able to create a sample to detect the pedal. So I made a small change on the title question, and any guidance on that direction would be very much appreciated!

It's not clear if this will work without having the actual hardware in my hands, but... Don't plug the jack all the way into the socket. The tip of the jack should then connect to pin 2, left audio out, of this diagram:
http://pinoutsguide.com/CellularPhones-P-W/samsung_galaxy_headset_pinout.shtml
and the ring should connect to pin 3, microphone in. If you push the jack all the way in, the ground pin on the jack shorts out the microphone input and you won't detect anything - pictures at https://en.wikipedia.org/wiki/File:Photo-audiojacks.jpg show how the connections will mate.
Play some audio out the Left channel, record it on the mic channel, and measure the amplitude. That may - if it's all wired up right - tell you the pedal position. If the far end of the potentiometer is connected to the ring of the jack, that's not going to work.
That all sounds rather Heath Robinson. You could buy an inline 3-pin female jack socket and a 4-pin male jack plug and wire the two together to get whatever pinout you need. No electronics; just connectors and bits of wire! Or use a cable like this one:
http://www.ebay.co.uk/itm/1mt-3-5mm-4-Pin-Jack-Male-Plug-4-Pole-Jack-female-Socket-Extension-Lead-Cable-/251172651017?pt=UK_Computing_Sound_Vision_Audio_Cables_Adapters&hash=item3a7b0e8009&_uhb=1
and a sharp knife and some stickytape.
The other problem would come if the phone refuses to send audio to the jack when the jack isn't plugged fully home - there's an extra contact in the socket that detects that mechanically. But it would work if you made 3-pin to 4-pin adapter.
Or, just cut the 3.5mm jack off your (shiny new) expression pedal. Replace it with the 4-pin connector off one of those broken phone headsets you have lying around.

According to your last update, it looks like the pedal reacts like the button on a hands free headset. So as to say, to play, pause and skip music or pickup/hangup a call.
If the basics on the API don't give you a way to get hold of it (which I doubt) then you could move down to the NDK and see if it can be catched by the native level.
I looked it up and thought this might help you find your way :
Audio Hardware
Never know...

There are two potential paths of possibility here.
One would be to use the headset (or perhaps more specifically microphone) presence detection circuitry, and read that status via the appropriate API. This would be fairly simple (just have to figure out what property of a real headset is being measured). However, there is a good chance that the response may be fairly slow, as the phone is likely to go through some audio configuration changes and will not want to do that until it is "sure" that the headset has been connected or disconnected. Also the simplest non-microphone headset detection may be via a mechanical mechanism in the jack which could be difficult to electrically actuate across models.
A more complicated idea would be to couple some sound into the microphone input, and detect that in software. However, it will have to be an oscillating signal, as the microphone bandwidth typically does not range down to DC. If you just apply a steady voltage, you would get a "click" transient when it changes, but not otherwise. Injecting an audio frequency signal would require a power source - with careful design you may be able to steal enough power from the microphone bias supply to run a small oscillator. But perhaps simpler would be to have software output an audio tone on the speaker, and have your pedal switch couple this back into the microphone through a small capacitor.

I finally managed to read the pedal input. #emrys57 is right, replacing the jack with the 4-pin connector, triggered the input the same way microphones with a hook button do. However occasionally it seems to trigger the volume up and the volume down keys as well. I suppose this is related to me replacing the 4-pin jack with a knife and tape.
It was fairly simple to override onKeyUp. Be aware that you also have to override onKeyDown to prevent the default behavior:
#Override
public boolean onKeyDown(int keyCode,KeyEvent event){
int action = event.getAction();
if (keyCode == KeyEvent.KEYCODE_VOLUME_UP
|| keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
|| keyCode == KeyEvent.KEYCODE_HEADSETHOOK) {
if (action == KeyEvent.ACTION_UP) {
Log.d(TAG, "action_up");
clickStart(null);
return true;
} else if (action == KeyEvent.ACTION_DOWN) {
Log.d(TAG, "action_down");
return true;
} else {
Log.d(TAG, "action:" + action);
return true;
}
}
return false;
}
#Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
int action = event.getAction();
Log.d(TAG, "onKeyDown!");
if (keyCode == KeyEvent.KEYCODE_VOLUME_UP
|| keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
|| keyCode == KeyEvent.KEYCODE_HEADSETHOOK) {
if (action == KeyEvent.ACTION_UP) {
Log.d(TAG, "action_up");
clickStart(null);
return true;
} else if (action == KeyEvent.ACTION_DOWN) {
Log.d(TAG, "action_down");
return false;
} else {
Log.d(TAG, "action:" + action);
return true;
}
}
if (keyCode == KeyEvent.KEYCODE_BACK) {
finish();
return true;
}
Log.d(TAG, "returning false");
return false;
}

Related

Android charging or plugged to a power source states

I have an dashcam which automatically starts and stops recording based on charging (start on charging and stop when it stops charging)
So I have something like this at the BroadcastReceiver.onReceive
Intent.ACTION_POWER_CONNECTED -> {
startRecording()
}
Intent.ACTION_POWER_DISCONNECTED -> {
stopRecording()
}
Some users reported that recording stops when their device becomes fully charged though it is still plugged to a power source via USB.
So it seems BatteryManager sends the event that the power is not connected.
What can I do in this case to know for sure that it is still plugged to a source power?
Mb should I use the following solution?
Intent.ACTION_BATTERY_CHANGED -> {
val plugged = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0
// conditionally it looks like this: if (plugged) startRec() else stopRec()
}

Why AudioManager works for Phone, but not for Android TV emulator?

As one can read here, I try to adjust the volume. Here I set the AudioManager:
audioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager
and I react on a onKeyDown() like:
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
when (event?.keyCode) {
KeyEvent.KEYCODE_VOLUME_UP -> {
adjustVolume()
audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC, AudioManager.ADJUST_RAISE, AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE)
return true
}
KeyEvent.KEYCODE_VOLUME_DOWN -> {
adjustVolume()
audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC, AudioManager.ADJUST_LOWER, AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE)
return true
}
else -> {
return super.onKeyDown(keyCode, event)
}
}
}
and adjustVolume() displays then the volume value. This works so far on phones. I can adjust the volume with my own UI and I can see log output.
But when I run this app on a Android TV Emulator, linked code does not work and I see stock UI for volume.
According to #I~nigos answer, the kernel "consumes" the keyevent already at TVs hardware level. It was proposed to handles the sound from internal mixer.
However, how can I intercept the sound level. Also by intercepting the internal mixer.. But how?
Android TV does not expose volume_up/volume_down buttons to the application.
The TV controls are shown as a D-Pad: see D-pad minimum controls
This up/down events from the remote / hardware buttons are handled by the TV hardware before they get to Android (makes sense since the TV might have other functions that depend on).
You can control you app volume output, and raise/lower volume and mute the TV from ADB; but you cannot get this events.
How to handle sound on TVs? The TV has internally a mixer. Output an appropiate sound level (not at 100% to avoid distorsion in some equipment).

Check if Android device has an internal speaker

How to check if a device running Android has a speaker on it or not? Meaning is it able to play audio?
Are there any Configuration qualifiers for it? and what about programmatically
EDIT: just bought an Android Wear watch and it does NOT have a speaker so not sure how I would check this
Since API level 21 (most Android Wear based on this level), Android provide a feature, PackageManager.FEATURE_AUDIO_OUTPUT, witch can be used to detect whether there is a way to output the audio.
I tested this feature on my MOTO 360 (no speaker), it don't has this feature, and Ticwatch (with speaker) do have this feature.
But when I connected a Bluetooth headset to the MOTO 360, it still don't have this feature, this confused me.
So I use AudioManager.isBluetoothA2dpOn() for further check.
The detection code can be like this:
public boolean hasAudioOutput() {
AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
PackageManager packageManager = getPackageManager();
if (audioManager.isBluetoothA2dpOn()) {
// Adjust output for Bluetooth.
return true;
} else if (audioManager.isBluetoothScoOn()) {
// Adjust output for Bluetooth of sco.
return true;
} else if (audioManager.isWiredHeadsetOn()) {
// Adjust output for headsets
return true;
} else if (audioManager.isSpeakerphoneOn()) {
// Adjust output for Speakerphone.
return true;
} else if (packageManager.hasSystemFeature(PackageManager.FEATURE_AUDIO_OUTPUT)) {
// Has internal speaker or other form of audio output.
return true;
} else {
// No device for audio output.
return false;
}
}
Android API doesn't have any such qualifiers neither there is any library method.
But just a raw idea, try playing some audio and simultaneously record through the mic.
Check if they are same.
This is not a fool proof way, but just a thought!
Probably most android phones have a speaker i guess :P /.. But if you are asking if you can check whether its connected to a speaker or headphones or something like that..then you can use
if (isBluetoothA2dpOn()) {
// Adjust output for Bluetooth.
} else if (isSpeakerphoneOn()) {
// Adjust output for Speakerphone.
} else if (isWiredHeadsetOn()) {
// Adjust output for headsets
} else {
// If audio plays and noone can hear it, is it still playing?
}
SOURCE: http://developer.android.com/training/managing-audio/audio-output.html#CheckHardware

ACTION_HEADSET_PLUG not documented extra strange "state"?

I'm doing some test with Intent.ACTION_HEADSET_PLUG.
Giving the fact that the following code should be the one who give the responses (From com.android.server.HeadsetObserver class 2.2.1 r1):
private final void sendIntent(int headset, int headsetState, int prevHeadsetState, String headsetName) {
if ((headsetState & headset) != (prevHeadsetState & headset)) {
// Pack up the values and broadcast them to everyone
Intent intent = new Intent(Intent.ACTION_HEADSET_PLUG);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
int state = 0;
int microphone = 0;
if ((headset & HEADSETS_WITH_MIC) != 0) {
microphone = 1;
}
if ((headsetState & headset) != 0) {
state = 1;
}
intent.putExtra("state", state);
intent.putExtra("name", headsetName);
intent.putExtra("microphone", microphone);
if (LOG) Slog.v(TAG, "Intent.ACTION_HEADSET_PLUG: state: "+state+" name: "+headsetName+" mic: "+microphone);
// TODO: Should we require a permission?
ActivityManagerNative.broadcastStickyIntent(intent, null);
}
}
And in the documentation they say: state - 0 for unplugged, 1 for plugged.
I strangely get two different state by plugging two different headsets:
0 = unplugged
1 = Headset with microphone
2 = Headset without microphone
The question is: where the State 2 (two) come from? Can someone enlighten me?
Thanks
I am using that extra state myself in one of my applications. One of your headsets has a mic the other doesn't. Also make sure you a plugging it in all the way, but don't break anything :)
0 - unplugged as in no headset attached to the device
1 - headset with microphone as in wired headset that had a mic so you can talk and the device uses it as a input as you talk
2 - a headset with no microphone as in your regular old stereo headset that you would normally hook up to your stereo system to listen to music with
This is extremely good info to verify that what was just connected is a wired headset that you expect to be able to talk in to and be heard correctly.

Control volume keys

In my application I have overridden the onKeyDown() and onKeyUp() functions in order to capture volume keys events. I use such events in order to control my application volume. I use the music stream to play my sounds. When detecting an event like this I also show a custom toast (similar to the one shown by Android). The problems I'm facing with this take are:
Android always plays a sound on volume keys events
That sound is played always at the same intensity.
What I'd like is to control the intensity at which the default sound is played (also the stream on which is played) in the following way: a louder sound for a higher volume and a lower sound for a low volume, if this is possible.
Or a way to disable playing that default sound and play my custom sound at the intensity I just set.
Actually the sound is played on onKeyUp(...), so you can simply overload the method in your activity when it gets called for the volume keys :
#Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
if ((keyCode == KeyEvent.KEYCODE_VOLUME_UP) || (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN)) {
return true;
}
return super.onKeyUp(keyCode, event);
}
This worked for me :)
It's strange cause I was writing similar functionality and Android seems to play louder sounds when you rise stream volume.
am.setStreamVolume(AudioManager.STREAM_MUSIC, progress,AudioManager.FLAG_PLAY_SOUND);
Thats what I used in my application. am is instance of AudioManager you can get by writing:
AudioManager am = (AlarmManager) getSystemService(AUDIO_SERVICE);
To disable sound you can replace AudioManager.FLAG_PLAY_SOUND with "0" value what should disable it.
I'm not sure if it is possible to replace those sound in AudioManager but you can play those custom sounds with MediaPlayer inside of your onKeyDown methods.
Hope this helps.

Categories

Resources