Playing sound files tutorial not working on android

Hello
I’m trying to make this (JUCE: Tutorial: Build an audio player) tutorial work on android. The problem is: juce::AudioFormatManager.createReaderFor(file) returns nullptr (and so audio isn’t played by the app), despite the fact that my code logs that file exists and has .wav extension.
Some more info:

  • read from external storage is enabled in projucer
  • tried to change the path to an existing path on my emulator - /storage/emulated/0/Download/file_example_WAV_10MG.wav" - got the same result
  • also tried to use WavAudioFormat.createReaderFor, but got the same result

I also understand that this tutorial is suited for “mac, Linux, and windows”, but I need to process file audio in some way, and I didn’t find other sources of code examples to build upon them.

If I missed something in this description - please, let me know

1 Like

Please someone reply I am also stuck with this

The DemoRunner is probably the best source of code examples. I’d suggest compiling it for Android and trying out the AudioPlaybackDemo.h there. If that works as expected then look at the code to see how it was done. It can be a bit confusing at first but I’ve found that it’s a great way to learn. In the DemoRunner everything is in one place and, unlike the tutorials, everything is usually up to date and works on all platforms.

1 Like

@aamf will definitely try that out

1 Like

Last time I tried there was a problem with the AudioPlaybackDemo on Android too.

Please let us know if you get it working. :slight_smile:

@nofish @aamf it works when I compile demo for android

1 Like

@protsenkoai @aamf @nofish Finally I got it working on my code just these .h and .cpp files

MainComponent.h

#pragma once

#include <JuceHeader.h>

//==============================================================================
/*
    This component lives inside our window, and this is where you should put all
    your controls and content.
*/
class MainComponent  : public juce::Component
{
public:
    //==============================================================================
    MainComponent();
    ~MainComponent() override;

    //==============================================================================
//    void prepareToPlay (int samplesPerBlockExpected, double sampleRate) override;
//    void getNextAudioBlock (const juce::AudioSourceChannelInfo& bufferToFill) override;
//    void releaseResources() override;
    //==============================================================================
    void paint (juce::Graphics& g) override;
    void resized() override;
    void openButtonClicked();
    void playButtonClicked();
    bool loadURLIntoTransport (const juce::URL& audioURL);

    juce::AudioSourcePlayer audioSourcePlayer;
    juce::AudioTransportSource transportSource;
    std::unique_ptr<juce::FileChooser> chooser;
    juce::AudioFormatManager formatManager;
    juce::AudioDeviceManager audioDeviceManager;
    std::unique_ptr<juce::AudioFormatReaderSource> readerSource;
    juce::TimeSliceThread thread  { "audio file preview" };
    void startOrStop();
private:
    //==============================================================================
    // Your private member variables go here...


    juce::TextButton openButton;
    juce::TextButton playButton;

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainComponent)
};

MainComponent.cpp

#include "MainComponent.h"

#include <memory>

//==============================================================================

MainComponent::MainComponent()
{
    setSize (800, 600);

    addAndMakeVisible (&openButton);
    openButton.setButtonText ("Open...");
    openButton.onClick = [this] { openButtonClicked(); };

    addAndMakeVisible (&playButton);
    playButton.setButtonText ("Play");
    playButton.onClick = [this] { playButtonClicked(); };
    playButton.setColour (juce::TextButton::buttonColourId, juce::Colours::green);
    playButton.setEnabled (true);

    transportSource.addChangeListener(reinterpret_cast<juce::ChangeListener *>(this));
    formatManager.registerBasicFormats();

    openButton.setSize(300,40);
    playButton.setSize(300,40);

    openButton.setTopLeftPosition(50,100);
    playButton.setTopLeftPosition(50,145);

    thread.startThread (3);

    juce::RuntimePermissions::request (juce::RuntimePermissions::recordAudio,
                                 [this] (bool granted)
                                 {
                                     int numInputChannels = granted ? 2 : 0;
                                     audioDeviceManager.initialise (numInputChannels, 2, nullptr, true, {}, nullptr);
                                 });

    audioDeviceManager.addAudioCallback (&audioSourcePlayer);
    audioSourcePlayer.setSource (&transportSource);
}

MainComponent::~MainComponent()
{
    transportSource  .setSource (nullptr);
    audioSourcePlayer.setSource (nullptr);

    audioDeviceManager.removeAudioCallback (&audioSourcePlayer);
}


void MainComponent::paint (juce::Graphics& g)
{
    g.fillAll (getLookAndFeel().findColour (juce::ResizableWindow::backgroundColourId));
}

void MainComponent::resized()
{
}

bool MainComponent::loadURLIntoTransport (const juce::URL& audioURL)
{
    DBG("_X LOADING INTO TRANSPORT");
    transportSource.stop();
    transportSource.setSource (nullptr);
    readerSource.reset();

    juce::AudioFormatReader* reader = nullptr;


    if (audioURL.isLocalFile())
    {
        reader = formatManager.createReaderFor (audioURL.getLocalFile());
    }
    else
    {
        reader = formatManager.createReaderFor(audioURL.createInputStream(
                juce::URL::InputStreamOptions(juce::URL::ParameterHandling::inAddress)));
    }


    if (reader != nullptr)
    {
        readerSource = std::make_unique<juce::AudioFormatReaderSource> (reader, true);

        // ..and plug it into our transport source
        transportSource.setSource (readerSource.get(),
                                   32768,                   // tells it to buffer this many samples ahead
                                   &thread,                 // this is the background thread to use for reading-ahead
                                   reader->sampleRate);     // allows for sample rate correction

        return true;
    }

    return false;
}

void MainComponent::startOrStop()
{
    if (transportSource.isPlaying())
    {
        transportSource.stop();
    }
    else
    {
        transportSource.setPosition (0);
        transportSource.start();
    }
}

void MainComponent::openButtonClicked() {
    if (! juce::RuntimePermissions::isGranted (juce::RuntimePermissions::readExternalStorage))
    {
        SafePointer<MainComponent> safeThis (this);
        juce::RuntimePermissions::request (juce::RuntimePermissions::readExternalStorage, [safeThis] (bool granted) mutable
        {

        });
        return;
    }

    if (juce::FileChooser::isPlatformDialogAvailable())
    {
        chooser = std::make_unique<juce::FileChooser> ("Select an audio file...", juce::File(), "*.wav;*.mp3;*.aif");

        chooser->launchAsync (juce::FileBrowserComponent::openMode | juce::FileBrowserComponent::canSelectFiles,
                              [this] (const juce::FileChooser& fc) mutable
                              {
                                  if (fc.getURLResults().size() > 0)
                                  {
                                      auto u = fc.getURLResult();
                                      if(loadURLIntoTransport(u)){
                                          DBG("LOADED");
                                      }else{
                                          DBG("NOT LOADED");
                                      }
                                  }

                                  chooser = nullptr;
                              }, nullptr);
    }
    else
    {
        juce::NativeMessageBox::showAsync (juce::MessageBoxOptions()
                                                   .withIconType (juce::MessageBoxIconType::WarningIcon)
                                                   .withTitle ("Enable Code Signing")
                                                   .withMessage ("You need to enable code-signing for your iOS project and enable \"iCloud Documents\" "
                                                                 "permissions to be able to open audio files on your iDevice. See: "
                                                                 "https://forum.juce.com/t/native-ios-android-file-choosers"),
                                           nullptr);
    }
}

void MainComponent::playButtonClicked() {
    startOrStop();
}


Try to ask Permissions in the Android Studio manifest like adding this:

 <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

If using the Projucer this can be set from there, right? There’s a “Read From External Storage” item under the Android settings.

In android project manifest file you can add the permission