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
aamf
July 8, 2022, 2:11pm
3
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.
Hi there. I’m on develop branch and I’m facing new problem with Android (gradle 5.4.1, plug-in 3.5.2)
While loading file createInputStream always returns nullptr. I have RuntimePermissions and I see the permission dialog, and all is granted. I’ve tried using URL::createInputStream - no luck.
File::existsAsFile() returns true. So everything is fine, but while creating input stream i’m tracking result with error “Permission denied”.
So I’ve checked AudioPlaybackDemo and there is the same issue.…
Please let us know if you get it working.
@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();
}
kob-j
July 9, 2022, 4:44am
8
Try to ask Permissions in the Android Studio manifest like adding this:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
aamf
July 9, 2022, 2:10pm
9
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