To play audio on android: possible?


#1

It seems the most basic way to play an audio file, using audio::Voice is broken on android. The line

        mAudioPlayer = audio::Voice::create( audio::load( loadAsset( "grom.mp3" ) ) );

breaks with a crash

10-29 16:22:48.136 19083-19213/ru.scriptum.at I/cinder: |info   | virtual size_t cinder::audio::android::DeviceManagerOpenSl::getSampleRate(const DeviceRef&)[138] result: 48000                           
                                                        --------- beginning of crash
10-29 16:22:48.137 19083-19213/ru.scriptum.at A/libc: Fatal signal 11 (SIGSEGV), code 1, fault addr 0x20 in tid 19213 (Thread-1652)

What’s the way to play on audio on Android, if there is one?

Thanks.


#2

Sample playback via BufferPlayerNode and FilePlayerNode should both work on android, at least they did about a year ago. Unfortunately debugging C++ is extremely difficult on Android, as it doesn’t yet give you proper stacktraces about where the crash happened. As you hit a C segment fault, the only way I was able to debug that was to place log statements throughout libcinder until you narrow down what call is causing the crash. You can start in your app - first check if the asset loads, then if the audio::SourceFile loads, then if you can create the Voice, and so on. Also a try/catch might make things more informative.


#3

@rich.e

I looked at the BufferPlayer sample and noticed that it uses the “ogg” format. So I converted my mp3 file into ogg, without changing anything else in my sample above, and that kinda fixed the problem. I now get the distorted sound as if the original sound was converted into rhythmic puffs. Once the file was done, it would start spewing and endless sequence of lines such as

10-29 20:09:59.306 12526-12811/ru.scriptum.at W/asset: seek out of range: want 291879203, end=453177

I will continue trying BufferPlayerNode and FilePlayerNode, in the meantime do you remember what audio formats these support?

Thanks.


#4

Tried playing it like this:

        auto ctx = audio::master();
        audio::SourceFileRef sourceFile = audio::load( loadAsset( "grom.ogg" ) );

        mFilePlayer = ctx->makeNode( new audio::FilePlayerNode( sourceFile ) );
        mFilePlayer >> ctx->getOutput();
        mFilePlayer->start();
        ctx->enable();

and get exact same behaviour as above: puffing and endless log spam once the playback is finished.

–8


#5

Out of BufferPlayer, FilePlayer and Voice only BufferPlayer works as expected with ogg files. The issue with BufferPlayer though is that it takes dozens of seconds to load the file, before it can play it. In my app I need to play a handful of files immediately upon receiving an OSC command.

So am creating a buffer node for each file, preload them on startup in a background thread, and it seems to work so far. If the tests with bigger files and larger number of files start failing I am going to implement native calls to standard android audioplayer.

–8


#6

Hm, I’m not sure what’s up with FilePlayerNode on android - I did use it on an android port I did about a year ago without issues. Can you try it in ‘non-async’ mode (using this constructor)? I’d also try hooking it up to a MonitorNode so you can visualize the samples.


#7

Oh and I forgot to mention, as you discovered only ogg files are currently supported on android.


#8

FilePlayer node with the sync constructor behaves the same way. MonitorNode simply shows what I hear: periodical bursts of values, interleaved with periods of silence. I tested this on two phone models.

–8


#9

Oh. I really have no idea what problem you’re running into, then. My only experience with android audio was on the one project where I did the initial ci::audio android backend, and FilePlayerNode worked at that time. You’ll have to do some investigating on you’re end.

If you’re audio files aren’t very large I’d recommend loading them async and using a BufferPlayerNode anyway.


#10

The BufferPlayerNode works great so far. The only issue is that it takes about a minute to load the pretty small files (together a little over 1 mb).

For the posteriority, this is how I load them on a background thread, which does not block the app start.

static const vector<std::string> audioFiles = {"chime.ogg", "grom.ogg", "Numb.ogg", "siren.ogg", "Radiohead.ogg"};
std::future<void> pending_future;

void loadAudio(vector<audio::BufferPlayerNodeRef>& mBufferPlayerNodes) {
    ci::ThreadSetup thread_setup;
    auto ctx = audio::Context::master();

    for (auto file: audioFiles) {
        try {
            audio::SourceFileRef sourceFile = audio::load( loadAsset(file), ctx->getSampleRate() );

            audio::BufferRef buffer = sourceFile->loadBuffer();

            audio::BufferPlayerNodeRef bufferPlayerNode = ctx->makeNode( new audio::BufferPlayerNode( buffer ) );
            //audio::GainNodeRef gain = ctx->makeNode( new audio::GainNode( 0.5f ) );

            mBufferPlayerNode >> /*gain >>*/ ctx->getOutput();
            mBufferPlayerNodes.push_back(bufferPlayerNode);
        }
        catch( std::exception &exc ) {
            CI_LOG_E( "Error loading audio: " << exc.what() );
        }
	}
	console()<< "loaded "<<mBufferPlayerNodes.size()<<" audio buffers "<<getElapsedSeconds() << endl;
    audio::Context::master()->enable();
}

void AugmentedTheatre::setup()

    auto f = std::async(std::launch::async, loadAudio, std::ref(mBufferPlayerNodes));

//move the future into a global scope so that it does not block main thread
    pending_future = std::move(f);
}


#11

Note that it is only safe to be making graph connections from one thread - so if you’re manipulating the Graph at all from the main thread, you could run into race conditions.

What I do is set up all of my Node graph ahead of time, and load the buffers async. Whenever they are ready, I pop them into an available BufferPlayerNode, which you could do with the App’s dispatchAsync() for example.

A minute seems long to me for 1mb of audio files, but then you’re on android so I don’t really know. You could break it up into loading just the sound effects files first, and the soundtrack second, so that at least you can proceed before the big one is finished. Really FilePlayerNode was designed to do things like play larger files (commonly soundtracks), unfortunate that one isn’t working for you.


#12

I am not manipulating the graph in any way after it is constructed. I like this structure because it allows playing all of the sounds in parallel if needed.

Will keep in mind dispatchAsync(), thanks.

–8