{"action":"created","issue":{"url":"https://api.github.com/repos/andrewrk/libsoundio/issues/127","repository_url":"https://api.github.com/repos/andrewrk/libsoundio","labels_url":"https://api.github.com/repos/andrewrk/libsoundio/issues/127/labels{/name}","comments_url":"https://api.github.com/repos/andrewrk/libsoundio/issues/127/comments","events_url":"https://api.github.com/repos/andrewrk/libsoundio/issues/127/events","html_url":"https://github.com/andrewrk/libsoundio/pull/127","id":186245662,"number":127,"title":"Android backend (input and output)","user":{"login":"ligfx","id":594093,"avatar_url":"https://avatars.githubusercontent.com/u/594093?v=3","gravatar_id":"","url":"https://api.github.com/users/ligfx","html_url":"https://github.com/ligfx","followers_url":"https://api.github.com/users/ligfx/followers","following_url":"https://api.github.com/users/ligfx/following{/other_user}","gists_url":"https://api.github.com/users/ligfx/gists{/gist_id}","starred_url":"https://api.github.com/users/ligfx/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ligfx/subscriptions","organizations_url":"https://api.github.com/users/ligfx/orgs","repos_url":"https://api.github.com/users/ligfx/repos","events_url":"https://api.github.com/users/ligfx/events{/privacy}","received_events_url":"https://api.github.com/users/ligfx/received_events","type":"User","site_admin":false},"labels":[],"state":"closed","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":13,"created_at":"2016-10-31T10:04:51Z","updated_at":"2016-12-23T19:31:03Z","closed_at":"2016-12-23T19:30:12Z","pull_request":{"url":"https://api.github.com/repos/andrewrk/libsoundio/pulls/127","html_url":"https://github.com/andrewrk/libsoundio/pull/127","diff_url":"https://github.com/andrewrk/libsoundio/pull/127.diff","patch_url":"https://github.com/andrewrk/libsoundio/pull/127.patch"},"body":"A.K.A. make libsoundio work on all major OSes (does the CoreAudio backend work on iOS?)\r\nA.K.A. better than SDL\r\nA.K.A. 500 lines and some hard work gets you surprisingly far\r\nA.K.A. The Cubs are still going to win the World Series, I just know it\r\n\r\nCouple points:\r\n- Android has a special audio state it calls the \"fast track,\" which skips software mixing and ties to reduce latency as much as possible. The keys to this are matching the device's natural sample rate and buffer size.\r\n\r\n  Unfortunately, there's no officially supported way to get this information outside of a JVM running inside an Android Activity. There are varying degrees of hacks around this, but they could be disappeared anytime or, worse, crash the entire process (ex. calling `JNI_GetCreatedJavaVMs` on Android without a pre-existing JVM raises an assertion error, so you can't easily opportunistically attach to a JVM. Even if you did get around it, there's still no officially supported way to get an Activity handle from the global context.)\r\n\r\n  In an ideal world, the backend would default to the fastest sample rate and a proper multiple of buffer size. Failing that, sample rate can be set manually and buffer size can be requested proportional to latency and sample rate.\r\n\r\n- I only implemented support for output devices. If that's a deal-breaker, I can go read-up on how input devices work on Android. They have some quirks that make them different from output devices and might possibly need JVM access (with the problems inherent in that, as described above).\r\n\r\n- This backend does not support calling `soundio_outstream_begin_write/soundio_outstream_end_write` multiple times in a single write callback. This part of the libsoundio API has always seemed weird to me, and it doesn't map well onto OpenSL ES. If you do call the functions more than once, you'll get an error because the backend buffers are full.\r\n\r\n- No underflow callbacks. I couldn't figure this one out. There's an interface called `SLPrefetchStatusItf` that looks like it could be handy here, but in local testing it only raised events when a write callback was not in the process of being called, which made it pretty much useless (since by the time the callback ends, the underflow state has been \"fixed\"). It also seems to randomly cause assertion errors when used with Android's buffer queue implementation, and nobody on the Internet knows why.\r\n\r\n  I thought about implementing an additional thread to intercept write callbacks from OpenSL ES, but then you'd lose out on the special thread priority that audio callbacks get and it complicates the code.\r\n\r\n- Requires API level 21 / Android 5.0 Lollipop or greater. The main feature this brings is floating-point support. The rest of the codebase doesn't work with API levels lower than that anyways.\r\n\r\n- Android doesn't have a `/tmp` folder, but it does follow the POSIX convention of pointing the TMPDIR environment variable to an appropriate location. I just added this location to `os.c` for the sake of simplicity, though ideally it would read the right environment variables.\r\n\r\n- Testing this out is a little hairy depending on your system, but something like this plopped in at the top of `CMakeLists.txt` (before `project(...)`) did the trick for me:\r\n\r\n  ```cmake\r\nset(BUILD_DYNAMIC_LIBS OFF CACHE BOOL \"\" FORCE)\r\nset(CMAKE_SYSROOT /usr/local/Cellar/android-ndk/r13/platforms/android-24/arch-arm)\r\nset(CMAKE_C_COMPILER /usr/local/Cellar/android-ndk/r13/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-gcc)\r\nset(CMAKE_C_FLAGS \"${CMAKE_C_FLAGS} -fPIC\")\r\nset(CMAKE_EXE_LINKER_FLAGS \"${CMAKE_EXE_LINKER_FLAGS} -pie\")\r\n```\r\n\r\n   Follow that up with a\r\n\r\n   ```bash\r\nadb push sio_sine /data/local/tmp && adb shell /data/local/tmp/sio_sine\r\n```\r\n\r\n   and you'll hear the sweet dulcet tones of a sine wave coming from your device's speakers.\r\n\r\n- Overall, the libsoundio API was a pleasure to work with! It's verbose in many places, but I never ran into any problems mapping OpenSL ES onto the abstraction model or figuring out where things belonged.\r\n\r\n- I took a look at the v2 branch, I think the only change that affect this is the `int` to `enum SoundIoError` change in function prototypes.\r\n\r\nEDIT: Added support for input devices.\r\n\r\n- Supposedly, not all devices support all sample rates. However, on my LG G4 all sample rates seem to be supported. I'm not sure what will happen if you try to open a device with an unsupported sample rate. Anecdotal evidence on the internet says sometimes it immediately fails and sometimes it just returns corrupted audio.\r\n\r\n- Is it useful to know if a device is currently unavailable due to permissions problems? I'm leaning towards yes, though it's not straightforward to figure out from the backend. On my device, the error code for lacking permissions was SL_RESULT_CONTENT_UNSUPPORTED, but according to the spec it should probably be SL_RESULT_PERMISSION_DENIED.\r\n\r\n- Android lets you make requests for different recording profiles: GENERIC, CAMCORDER, VOICE_COMMUNICATION, and VOICE_RECOGNITION. The camcorder request could be exposed as a separate device (and probably should, though it's not guaranteed to actually be different), but what about the VOICE_* options? They might be too high-level for this project, and more appropriate for an effects layer.\r\n\r\n- Recording floating-point data is supported starting in API level 23 / Android 6.0 Marshmallow. I put a check into the backend init function to try opening a device in that format, which seems to work (tested on an emulator running API level 21).\r\n\r\n- Recording is much more difficult to test, because command-line processes don't have permissions to access input devices. To try this out I ended up creating a [NativeActivity](https://developer.android.com/reference/android/app/NativeActivity.html) with copy-pasted code from `sio_record`. This worked on my real device, but not on the emulator because I couldn't figure out where I had permission to write files. Should there be a small sample app for testing (probably similar to `sio_microphone`), or is that too complicated?\r\n"},"comment":{"url":"https://api.github.com/repos/andrewrk/libsoundio/issues/comments/269035515","html_url":"https://github.com/andrewrk/libsoundio/pull/127#issuecomment-269035515","issue_url":"https://api.github.com/repos/andrewrk/libsoundio/issues/127","id":269035515,"user":{"login":"andrewrk","id":106511,"avatar_url":"https://avatars.githubusercontent.com/u/106511?v=3","gravatar_id":"","url":"https://api.github.com/users/andrewrk","html_url":"https://github.com/andrewrk","followers_url":"https://api.github.com/users/andrewrk/followers","following_url":"https://api.github.com/users/andrewrk/following{/other_user}","gists_url":"https://api.github.com/users/andrewrk/gists{/gist_id}","starred_url":"https://api.github.com/users/andrewrk/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/andrewrk/subscriptions","organizations_url":"https://api.github.com/users/andrewrk/orgs","repos_url":"https://api.github.com/users/andrewrk/repos","events_url":"https://api.github.com/users/andrewrk/events{/privacy}","received_events_url":"https://api.github.com/users/andrewrk/received_events","type":"User","site_admin":false},"created_at":"2016-12-23T19:31:03Z","updated_at":"2016-12-23T19:31:03Z","body":"Alright, let's get some testing going and then release time :tada: "}}
{"actor":{"display_login":"andrewrk"}}