Best way to incorporate wav audio into a module?

So I assume you used this then, but adjusted a few things:

//---------------------------------------------------------------
        // 1. Set a file path to an audio file on your machine
        const std::string inputFilePath = std::string (PROJECT_BINARY_DIR) + "/test-audio.wav";

Like PROJECT_BINARY_DIR to the root of your whole plugin, and /test-audio.wav to the path of your file, stemming from the plugin root, again?

Unless I’m misunderstanding you, just having the AudioFile.h in your project allows for all this to work, or your project is currently a work in progress, and the AudioFile.h will be included in the future, so this can work?

To use AudioFile.h you need at least some code like that:

#include "AudioFile.h"

struct MyModule : Module {
	AudioFile<float> audioFile;
	void process(const ProcessArgs& args) override;
};

MyModule::MyModule() { 
	audioFile.load(asset::plugin(pluginInstance, "samples/my_sample.wav"));
}

void MyModule::process(const ProcessArgs& args) {
	// some code here to read from audioFile.samples[][]
}

Of course, your module will need a lot more like parameters, ports and so on.

And you should make audioFile a singleton to avoid loading the same audio-data each time the user puts your module into the rack.

1 Like

This is pretty clever! :point_up:

1 Like

Ahornberg didn’t invent that :wink:

1 Like

This might be obvious to us old-school programmers and forgive me if you already know this: Although there’s no super-simple, built-in sample loading and playback libraries in VCV Rack, it’s commonplace for developers to create “wrappers” around libraries such as AudioFile.h in order to simplify your main code.

For example, I have a C++ structure called “sample.hpp” which can be used like:

#include "sample.hpp"
Sample my_sample;
my_sample->load("/res/snare.wav");

(This isn’t real code, but I hope that you get the idea. My real sample code is here: https://github.com/clone45/voxglitch/blob/master/src/Common/sample.hpp)

Behind the scenes, sample.hpp includes AudioFile.h and calls its functions, but all of that complexity is hidden from me.

Coding these may be a bit challenging at first, but then life gets easy! This is not only convenient, but if AudioFile.h ever got replaced by something better, faster, and stronger, you could potentially swap it out and replace it with the new thing without much difficulty.

1 Like

After all this, it’s pretty easy to read in an audio file, dump it out as a c++ source code for an array. array of longs or shorts. For short samples it’ll be fine.

I actually do this in a C++ program with Javascript source. A 5mb Javascript program I can call from c++.

1 Like

For the OP, reading in an audio file doesn’t seem to be easy. Otherwise he would not have asked for help. In his first post he wrote:

My first module did nothing else than dividing a CV input by 1.375 and just at the moment after I released it, someone asked me to convert this constant of 1.375 to an adjustable value. This is why my first answer here was: Maybe give the user the ability to load his own samples.

At the end it’s up to the OP to make the decision between using hard-coded samples and by the user replaceable samples.

Beside all this, the OP tries to learn C++ by coding a VCV module. We all know nothing about what his module will provide to the user. In the VCV library, there are already lots of good sample-players and drum modules based on samples.

Maybe @CircadianSound could tell us more about the benefits he will bring to the users of his module and also more about his level of coding skills, so that we can give more useful help here.

1 Like

Ah…the humble Singleton. Terminology harkening back to the days of a great book by Erich Gamma (et al): Design patterns : elements of reusable object-oriented software (1994)

2 Likes

I used the term “singleton” simply because it is a well known term since decades and by “singleton” I simply mean “some code that guarantees that a particular sample is loaded once and only once, regardless how many instances of the module are generated by the user”.

By “singleton” I do not mean “implementing the singleton-pattern exactly as described in the book Design Patterns” because IMO there’s no need for a separate singleton class. A static member variable in the derived Module class (or struct) that holds the audio-data should do the job pretty well. The only thing needed then is to check in the module’s constructor if the audio-data is already loaded, and if not, just load it.

1 Like

there is an example on HOW to embed BINARY into code: the BEFACO spring reverb uses it

(of course I prefer always audio on file, with loads on demand)

2 Likes

Thank you for clearing that up and providing another nudge in the right direction. This all helps quite a lot, and looks like a Rack module implementation that might work for my purposes. At this point I feel like I might be able to test and see if I can get a simple sample loaded to start. Then I could build on that.

With your cpp snippet above, I am able to see where .load comes, from Adam Stark’s code, but am failing to see where .sample comes from? So I’m just trying to trace and connect the flow of information to better wrap my head around all this. Again, I appreciate you taking the time to provide insight and answer all my questions :slight_smile:

And thank you for mentioning the use of a singleton. I did a search about how to implement this after I read your post, to learn how I would go about implementing this. From the looks of it, it seems to rely on the use of pointers. I’ll continue reading up on singletons more and see how I could relate that to what I’ve learned so far. I would have to figure out how to code that in to audioFile.

This is great, thank you! I also got a chance to look at your code too, and have to say, seeing different ways to call on AudioFile.h does help to better make sense of all this.

Your code also seems to be split up into a lot of headers, with very minimal code in the actual .cpp files. Which makes it a little difficult for a noob such as myself to trace where things are coming from. But! Your example above does seem quite streamlined. You did say it wasn’t actual code though, just an example? I wonder if I could call on all my samples with just this example above. Of course, I’d need the proper includes.

I will look at your code more. Is there a certain module you’d recommend to study over others? I tend to trace things by checking the includes, if I can’t find a reference in the actual .cpp file. I should also play with your modules more as well, they seem right up my alley :slight_smile:

I also see you have dr_wav in your code too, so looks like you’re using that as well.

Hi Kent :slight_smile: thank you for chiming in, and thank you for bringing this up. I will most likely be organizing my samples into arrays for the purposes of playback.

Are you referring to the conversion of audio samples (yes they are going to be very short samples) or reading samples from a directory?

I would love to learn more about your implementation mentioned above, if you’re open to sharing?

Yes, this all might seem quite trivial to the seasoned coder, but I have very minimal coding experience and am just starting to learn C++ for the purposes of having a go at realizing some module ideas I would like to develop.

RE: Hard coded vs. read from a directory. This is something that I haven’t decided on just yet. There seems to be more examples here which rely on reading from a directory, and I’ve yet to find a solution to convert my samples to headers to be included. I would prefer to hard code the samples, as they are the basis for my “VCO/Generator” in my module design. There’s really no intension to allow the user to replace or have access to the samples.

You’re right, there are a lot of samplers and drum modules available in the library. I’ve always been fascinated with percussion sound design, and it’s always been one of my main area’s of focus when working with synthesis. So I have some ideas floating around that I’d like to realize in the form of Rack modules.

The intension is to have the module’s sonic character based on samples I recorded in my studio for the purposes of a Rack Extension (IDT) I was working on back in 2017, which has since been abandoned (I may still revisit my RE projects at some point in the future).

So, I do have very minimal coding experience, mainly with the RE2DGUI .json and IDT (GorillaEngine) scripting, but nothing really beyond that. I’ve looked at Rack module’s source a bit over these last few years, and none of it made sense to me until I starting learning about it, maybe a couple weeks ago. I can build Rack from source, and compile Rack plugins, but that’s about it at this time.

So my experience level is currently set to noob, with maybe 30-40 episodes of The Cherno’s C++ YouTube series viewed, and all of the contributors to this thread helping me put the pieces together. I do understand the purpose of a header file, I do understand that there are different sections to a module’s .cpp file, like declaring the audio or cv I/O, and the parameters, and then initializing them, and then the widget section for interfacing with the GUI.

With all this being said, I don’t mind being taught stuff I already know, as an exercise in practice. It’s important to retain some humility, as there are several ways to do things, and someone could always suggest a better way to do things. I believe this across the board, and not just with this subject. So I’m quite open to new ideas :slight_smile:

2 Likes

Now that is an attitude I applaud, more of this is needed in the world, a whole lot more!

2 Likes

I strongly recommend learning the basics of C++ so that you can clearly read and understand other people’s code.

The examples.cpp by Adam Stark shows clearly how to use his code. If you can’t read and understand and adapt it for your needs, then put your plans for coding VCV modules aside for a while and take a C++ crash course or focus on simpler projects only for the purpos of understanding what is “by value”, “by reference”, what is a pointer, a class, a member of a class, inheritance, overriding methods but also all the basics like different kind of loops and conditions.

I’m sorry, but IMO this forum is not for teaching the C++ language. There may be better places on the web for that.

That’s fair. Like I said, I am in the process of learning the basics of C++, and have started to grasp most of which you mentioned in the C++ series I’m watching, which I find to be done quite well.

So I revisit source code as I’m watching, and things like; int, float, class, struct, functions, conditional statements, loops, arrays, namespaces, pointers, references, inheritance, strings, constructors, and destructors have all started to make more and more sense as I revisit and review Rack source code and the API. I actually spent a while doing some research and was quite hesitant to start asking here. I resorted to that when I couldn’t find answers on my own.

Like I said, I did have a bit of coding knowledge coming into all this, but nothing I would necessarily be confident about. So I seek advice here when trying to relate all that I’m learning and try to apply that to coding for Rack modules. So you could say I got stuck on how to implement sample loading, after reviewing a lot of source code and Google searches.

You’ve already provided a lot of very good advice, and helped break things down that I wasn’t grasping. So thank you for that, and I appreciate the time you’ve taken to respond here :slight_smile: I have cloned Adam Stark’s AudioFile the moment you mentioned it, and have been studying it since. You have been very helpful.

1 Like

Thank you @synthi! I will definitely review this :slight_smile:

There is no conflict. Both views can coexist peacefully. :wink:

Basically, it’s just about guaranteeing some single source of truth/state concept. And there are all sorts of ways to describe and implement that.

The book is just coming from a specific perspective (Object Oriented Analysis and Design), where we have concepts like classes (abstractions of knowledge and behaviour) and objects (concrete instantiations of some class actually fulfilling some responsibility in a system).

1 Like

I’m all for using the simplest thing possible, and a global variable can be a great solution.

In the past I’ve never implemented something I called “a singleton” (and, yes, I have read the GOF book a few times). But I do tend to use reference counted pointers, For a few reasons:

  • If I run more than one instance of my plugin I want them to share heavy objects, but I don’t want to worry about what happens when instance A wants wave foo, but instance B wants wave bar.
  • Slightly worried about threading issues - what if one instance is being instantiated while the other one is processing?
  • In SFZ player I load wavs, but an SFZ file can easily reference hundreds of wave files, so you needs a map of which files have been loaded and needs to track how many times each was loaded. I don’t know what I did there (and I don’t think I shared them between instances), but it sounds like a map where the keys are file paths and the values are shared pointers ( std::map< std::string, std::shared_ptr>)
  • In many modules I share filter responses and function lookups between instances. While some globals might work, I made a global ObjectCache object that knows how to make these heavy resources, manages their lifetimes, makes sure they can go away. Also unit tests make sure that thing is working. Although It’s not very fancy, mostly a bunch of shared_ptr and weak_ptr.
2 Likes