As a newbie I just want to load a sample and play it

But I’ve looked through most of the DSP demo and it seems quite contrary to the tutorials.
With main.h and MainComponent.h and they contain classes of differing types, like Thumbnail and AudioPlayer.
What do these do? The helps just tells me they manage stuff:-
AudioDeviceManager
AudioFormatManager
AudioFormatReader
AudioFormatReaderSource
AudioTransportSource
AudioSourcePlayer

All I want to do is load up a file and feed it through the processing block or print the waveform in the manner I want - dynamically.
edit I just saw the sample player tutorial. I presume I can make a copy of the AudioSourceChannelInfo and get whatever amount of samples I want?
What I’m really asking is how do I get at the Raw samples in float format?

Look here, this might’ve helped you:

www.juce.com/doc/tutorial_looping_audio_sample_buffer

Also here it’s discussed:

For the waveform you can use AudioVisualiserComponent

That’s great thanks, I think I need a bit more patience. Juce seems overwhelming at times, but I guess I’m a grass roots kinda guy, and I like to control everything myself, rather than have overly complex code do it for me - as it can get a bit much at times. Just looking at that ‘thumbnail’ code is enough to put me off forever! :slight_smile:

The “readFromAudioReader” example in your link is redacted - is there any other way of filling memory with sample data? It looks like that example file player is from a file and it resamples the sample rate.

You mean the Juce code itself? Why are you looking at that? As a newbie you should just use the code for now and worry about what the code internally does later, if you need to.

You mean the Juce code itself? Why are you looking at that? As a newbie you should just use the code for now and worry about what the code internally does later, if you need to.

I’m only a Juce newbie. I like to know how over engineered something is before using it. I know, insane right?

Sorry, I didn’t mention that readFromAudioReader is not available anymore. The AudioFormatReader read() is what you need. Just search the forum, there’s plenty of answers for this - here’s another thread:

I rewrote the code (but didn’t tested). Call read() and increment sourceStartSample in each iteration of processBlock by numReadSamples. Don’t forget to reset it when it goes to end, fill the rest of the outputBuffer with zeros when there’s not enough samples (or clear() at the beginning of iteration), etc.

AudioSampleBuffer outputBuffer;
String path = "someFile.wav";
int64 sourceStartSample = 0;
int numReadSamples = 512;

File soundFile = File(path);
AudioFormatManager manager;
manager.registerBasicFormats();
AudioFormatReader* reader = manager.createReaderFor(soundFile);
outputBuffer.setSize((int)reader->numChannels, (int)reader->lengthInSamples);
reader->read(&outputBuffer, 0, jmin((int)reader->lengthInSamples, numReadSamples), sourceStartSample, true, true);

Combine the FileChooser (from the tutorial) with this and you might get what you need.

Or you can implement it similary to how the SoundPlayer does.

About over engineering - one shouldn’t forget that JUCE is cross-platform and it doesn’t use any external libraries AFAIK.

1 Like

Yes, the folks assume, you start going through a tutorial. The API documentation is rather useful as reference.

All buffers are float or double buffers. So normally you don’t have to bother about conversion.

The main classes:

  • AudioSource: produces samples, either from files, from oscilators etc. Look at the inheritance diagram to get an idea

  • AudioProcessor: filters sample data. This is the base class for all DSP classes. They are also the base class for a DAW plugin, that receives and alters sample data.

  • AudioFormat: Base class for reading and writing audio files. For each audio format there is a subclass. To instanciate a format, the AudioFormatManager serves as factory.

  • AudioDeviceManager is used to open or select the output device. It is not needed, if you are developing plug-ins.

Most classes use aggregation, e.g. AudioFormatReaderSource is an AudioSource, that aggregates an AudioFormatReader to supply a block of audio sample data in every call to getNextAudioBlock().

For your initial question, you need 4 steps:

  • Get the AudioIODevice from the AudioDeviceManager::getCurrentAudioDevice()
  • create an AudioSourcePlayer
  • give it an AudioFormatReaderSource, you can use the AudioFormatManager to find the AudioFormat and call createReaderFor (format)
  • call AudioIODevice::start (audioSourcePlayer)

and done

I hope I could give you some entry points, what the main classes are (from my perspective). But it would exceed my possibilities, to explain the whole architecture (even if I knew it in full).

Your curiosity is much appreciated, but to give you more detail, I would need to know, what you are up to :wink:
If you are worried about performance, as far as I can tell, they try to keep it slim and performant.

Have fun with JUCE

2 Likes

@zonrobin That’s super nice of you to guide me like this. Thanks loads. :smile:

It’s just that sometimes less is more. I feel that people use Juce to escape the “boiler-plate” world, please don’t complicate it further… :slight_smile:

Also the API reference doesn’t mention that the sample gets sample rate converted. Which I believe AudioTransportSource does?

Yes, the AudioTransportSource can resample, if you set a sample rate in setSource:

sourceSampleRateToCorrectFor
if this is non-zero, it specifies the sample rate of the source, and playback will be sample-rate adjusted to maintain playback at the correct pitch. If this is 0, no sample-rate adjustment will be performed

I am completely with you, I find it also always very suspicous, if too many stuff happens in an obscure macro… :slight_smile:

1 Like

It gets to a point where simple ideas turn into a giant wrappers. When all we really want is to be is coders. To build on a simple buffer. That’s why I’ve always liked ‘portaudio’.

Also, check out the SoundPlayer class - all you need to do is also have an AudioDeviceManager and add the SoundPlayer's callback to the AudioDeviceManager.

You can obviously do that with JUCE too. If you just want to output audio into your audio hardware, it’s enough to just use the AudioDeviceManager and write an AudioIODeviceCallback subclass that delivers your buffer or a part of it into it. But it would be quite pointless to use JUCE only as a PortAudio replacement. (Unless you know the JUCE implementations for the audio hardware handling are better than what PortAudio has.)

Portaudio was just an example of simplicity that people may know. When I last used it there was really only one way of doing things. I don’t know about now, it may have turned into a monster!