iOS AUv3 and streaming internet audio

projucer
ios

#1

I am working on an iOS AUv3 plugin that ideally will be able to playback streaming audio from the network ( internet radio specifically ).
That’s literally all I want it to do .
It would later be added as a generator AU in a DAW where its output could be fed into other effects audio units. ( where it would get interesting ) .

Would this possible with to do using JUCE with existing Modules and classes?

This is an odd plug-in as it is not a synth and not an effect …

I am a little afraid the answer is “yes” but you will have to roll your own C++ code in order for it to work. :laughing:

Seems similar to this question from @daniel , but the other way around?


#2

I haven’t done that, but I think you can use an WebInputStream into an AudioFormatReader to an AudioFormatReaderSource for that purpose.
Ideally decouple it from the audio thread using a BufferingAudioSource.

Let us know, if that works out of the box…

Yes, my question was the opposite direction, providing a stream to be played back in a standard player.


#3

@Daniel

thanks … I’ll be sure to get lost in the weeds trying , but thanks for the tips!


#4

I gave it a spin, seems like the MP3Format is not really made for streaming, it will always fail, because the stream returns zero length…

BTW, if MP3AudioFormat is not found, enable it in the Projucer in the module juce_audio_formats

Here is my code anyway, maybe you or somebody else gets it to work, like another server type or something:


#include "../JuceLibraryCode/JuceHeader.h"

//==============================================================================
class MainContentComponent   : public AudioAppComponent,
                               public Button::Listener
{
public:
    //==============================================================================
    MainContentComponent() :
    tune (TRANS ("Tune")),
    stop (TRANS ("Stop")),
    play (TRANS ("PLay"))
    {
        thread = new TimeSliceThread ("Radio Thread");

        url.setText ("https://listen.radionomy.com/Jazz4ever", dontSendNotification);
        url.setEditable (true);
        addAndMakeVisible (url);
        addAndMakeVisible (tune);
        addAndMakeVisible (stop);
        addAndMakeVisible (play);
        tune.addListener (this);
        stop.addListener (this);
        play.addListener (this);

        setSize (200, 400);

        thread->startThread (5);

        // specify the number of input and output channels that we want to open
        setAudioChannels (0, 2);

    }

    ~MainContentComponent()
    {
        shutdownAudio();

        thread->stopThread (1000);
    }
    //==============================================================================

    void buttonClicked (Button* b) override
    {
        if (b == &tune)
            tuneIn (url.getText());
        else if (b == &stop)
            radio.stop ();
        else if (b == &play)
            radio.start ();
    }

    void tuneIn (const String& address)
    {
        radio.setSource (nullptr);

        MP3AudioFormat format;
        auto* stream = new WebInputStream (address, false);
        auto* reader = format.createReaderFor (stream, true);

        if (reader)
        {
            auto* readerSource = new AudioFormatReaderSource (reader, true);
            radio.setSource (readerSource,
                             jmax (480000., reader->sampleRate * 10),
                             thread,
                             reader->sampleRate,
                             reader->numChannels);
        }
        else
        {
            radio.setSource (nullptr);
        }
    }

    //==============================================================================
    void prepareToPlay (int samplesPerBlockExpected, double sampleRate) override
    {
        radio.prepareToPlay (samplesPerBlockExpected, sampleRate);
    }

    void getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill) override
    {
        radio.getNextAudioBlock (bufferToFill);
    }

    void releaseResources() override
    {
        radio.releaseResources();
    }

    //==============================================================================
    void paint (Graphics& g) override
    {
        // (Our component is opaque, so we must completely fill the background with a solid colour)
        g.fillAll (getLookAndFeel().findColour (ResizableWindow::backgroundColourId));

        // You can add your drawing code here!
    }

    void resized() override
    {
        auto box = getLocalBounds().reduced (10);
        auto h   = box.getHeight() / 10;

        url.setBounds (box.removeFromTop (h).reduced (0, 3));

        auto buttonRow = box.removeFromTop (h).reduced (0, 3);
        auto w   = buttonRow.getWidth () / 3;
        tune.setBounds (buttonRow.removeFromLeft (w));
        stop.setBounds (buttonRow.removeFromLeft (w));
        play.setBounds (buttonRow);
    }


private:
    //==============================================================================

    // Your private member variables go here...
    ScopedPointer<TimeSliceThread>      thread;

    AudioTransportSource radio;
    Label url;
    TextButton tune;
    TextButton stop;
    TextButton play;


    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainContentComponent)
};


// (This function is called by the app startup code to create our main component)
Component* createMainContentComponent()     { return new MainContentComponent(); }


#5

mp3 format not made for streaming? So, how does Dropbox let you start listening on a shared mp3 via the browser? I guess it’s caching it and letting that cached version play?


#6

I think it may have been a reference to the built-in MP3AudioFormat class in JUCE?.. thanks @daniel for the code example btw — I appreciate the effort.

I also found this old thread … which indicates maybe this isn’t possible anyway … the post is pretty old, and maybe the answer is different several years on ?


#7

Yes, that’s what I think. The question is (@jules), if the MP3AudioFormat could be extended to allow a stream, when the length is 0? I tried a bit around, but it seems that length=0 is so often used as error return… but I think the mp3 specs shouldn’t be too much different, if you play a stream rather than a file.

EDIT: regarding the link: yes, the post from 2010 predates the MP3AudioFormat class from 2011 (github commit 29-11-2011)

Good luck, I might look in again after X-mas…


#8

Yeah, it is surely made for streaming too. I was talking about the specific implementation MP3AudioFormat. Should have made that more clear…