Juce Tutorial 11- Basic Wavetable Synthesis in Juce

แชร์
ฝัง
  • เผยแพร่เมื่อ 14 ม.ค. 2025

ความคิดเห็น • 67

  • @stulast
    @stulast 4 ปีที่แล้ว +11

    Great tutorial as always. Just a couple of points:
    1. Just to optimise the for loop in getNextAudioBlock - calculate the sample value first, then assign the result to each channel. This halves the computational cost of the for loop by only calculating the sample value once and I would think in audio processing, every optimisation you can do will help:
    for (int sample = 0; sample < bufferToFill.numSamples; sample++)
    {
    float aSampleValue = waveTable[(int)phase] * amplitude;
    leftChannel[sample] = aSampleValue;
    rightChannel[sample] = aSampleValue;
    phase = fmod((phase + increment), waveTableSize);
    }
    2. In JUCE 6 (at least) the double_Pi constant has been deprecated. Instead use juce::MathConstants::pi. Also, juce 6 requires us to use the "juce" namespace to prefix juce classes/methods etc. C++ convention requires us to avoid using "using namespace" and be specific about what we're using in our code to avoid clashes:
    for (int i = 0; i < waveTableSize; i++)
    {
    waveTable.insert(i, sin(2.0 * juce::MathConstants::pi * i / waveTableSize));
    }
    3. The prepareToPlay loop is not really the best place to instantiate member variables. As stated in the original guide comments, "...be careful - it will be called on the audio thread..." I think the best place to instatiate member variables is still the constructor, as part of a constructor member initializer list. That is the main purpose of the constructor. The only code I can think of for the prepareToPlay() method would be the calculation of the wavetable based on the current object state. , and I think in many cases, this could be offloaded onto the GUI thread. One of our goals in audio programming, after all, is to keep the audio thread as responsive and agile as possible, without running the risk of overrunning the buffer.

  • @SoundPeer
    @SoundPeer 2 ปีที่แล้ว +3

    Alright, so working in with JUCE 7.0.2 and Visual Studio 2022 I noticed that it gives you three files: Main.cpp, MainComponent.cpp, and MainComponent.h, unlike what Josh has in the video.
    So I've declared all those double variables (wtSize, frequency, phase...) within MainComponent.h as private members of the MainComponent class.
    I've also adjusted a couple of things, as per usual:
    juce::Array instead of Array
    juce::double_Pi instead of double_Pi
    Worked like a charm, could hear my 440 Hz sine. What a time to be alive... :D
    Thank you Josh, you're a godsend!

    • @camexplodesstuff2503
      @camexplodesstuff2503 2 ปีที่แล้ว

      Tanks for that, I was trying to figure out the juce::double_Pi, I was trying to use the math constants but it didn't like it.

    • @kornhausersound6235
      @kornhausersound6235 2 ปีที่แล้ว

      Do you mind showing this step by step? im having a hard time getting this to work

  • @EnyaKim-vv4vm
    @EnyaKim-vv4vm 9 หลายเดือนก่อน +1

    This algorithm is based on a model called linear interpolation,it is important to match curve by use the discrete point

  • @anthonybeckett352
    @anthonybeckett352 7 ปีที่แล้ว +2

    Going to have plenty to do this weekend. Thanks for continuing with the tutorials

  • @hegood
    @hegood 3 ปีที่แล้ว +4

    It was a real challenge for me to figure out why this code was working. So, here is a note to other people like me that need to have everything spelled out.
    This is really tough to precisely explain, but here is my best shot.
    It finally clicked when I asked myself what gives a sound a particular frequency. The answer is how long it takes to complete a cycle. In the case of this code, the cycle length is determined by the length of time (samples) between values of zero for the phase variable in ''getNextAudioBlock' . This is determined by both the increment size and the wavetable size. frequency * wavetable_size represents how many steps need to complete within one second (i.e.: 44100 samples, which is the sample rate). So, when you divide frequency * wave_tablesize by the sample rate, you are calculating how many steps need to complete per sample in order for all the steps to complete within the sample rate, i.e.: within one second. Naturally, this calculation also implicitly defines how many samples it takes in order for the phase to exceed the wavetable size and thus for the cycle to reset. So, when you are doing the fmod against the wavetable size, you are resetting the wavetable in exactly the time it takes to complete one cycle.
    My understanding of this was confirmed when I raised and lowered the frequency by fmod'ing by multiples of the wavetable size.

  • @ashenpall
    @ashenpall 2 ปีที่แล้ว +1

    Not sure what I’m doing wrong but I get no errors and no output. Tried changing to 0 inputs as someone else suggested and nothing.

  • @ashtonmills1
    @ashtonmills1 5 ปีที่แล้ว +6

    Hello. I was having an issue where I wasn't getting an output when running the application. I fiddled around and changed setAudioChannels to (0,2); which I guess makes sense because there only need to by outputs, no inputs. When I did that I heard my sine tone when running the app.
    Hope this helps anybody else who get stuck there, though I'm not sure why this would be an issue .

  • @DKDiveDude
    @DKDiveDude 5 ปีที่แล้ว +4

    Your link to the "Git Repo" shows a "404 Error Not Found"

  • @djasynchronous7777
    @djasynchronous7777 5 ปีที่แล้ว +2

    Thank You! I'm going to be hanging around here for a while ;)

  • @PanosLa
    @PanosLa 4 ปีที่แล้ว

    Damn it's so rewarding when you find mistakes. Not on your tutorial but I did a few as I was going along... Thanks for the tutorial!

  • @hidden4425
    @hidden4425 4 ปีที่แล้ว +1

    You know, this video yo made still teaches me thingd to this day.
    Edit - spelling corrections

  • @hidden4425
    @hidden4425 4 ปีที่แล้ว +1

    Its like everytime i watch there's something new.

  • @NeZversSounds
    @NeZversSounds 4 ปีที่แล้ว +2

    weird. I have no output, no error and all warnings are not important (like lossy double to float conversion). But I noticed that processing memory is fast growing into 3GB and up.

  • @drpotluck1024
    @drpotluck1024 2 ปีที่แล้ว

    the amount of satisfaction I had when I got my speakers to go *ooooooo* was intense

  • @andreilemieux9525
    @andreilemieux9525 5 ปีที่แล้ว +2

    im wanting to get into coding. and i want to eventually develop a basic rack extension for reason. but my goal is to make a wave table synth. but have the feature from NI massive, serum and other synths where you can have version of PWM. where you can bend the waveform? what concept is that look up NI massive bend + or - to see what I'm talking about. many synths have this feature but nobody really has a definition.

  • @GospelMusicians
    @GospelMusicians 7 ปีที่แล้ว +2

    Is there a GitHub Repo?

  • @vatochulukhadze3741
    @vatochulukhadze3741 4 ปีที่แล้ว

    Hey, whenever I try to run this program my build won't open and throw an error saying a certain memory could not be written. Build succeeds in VisualStudio and fails to open in the folder where it's saved basically.

  • @RNsay-u5c
    @RNsay-u5c 4 ปีที่แล้ว

    I got some problems while following this tutorial, when creating a new "Audio Application", header file .h comes along, not only served as Main.cpp and MainComponent.cpp.
    Therefore, the default-structure has gone different. Are there some settings to manipulate this problem?

    • @GamerHanzu
      @GamerHanzu 4 ปีที่แล้ว

      While creating a new project with projucer.exe you can modify this.
      When you choose youre IDE, there is a dropdown menu at the top, where you can switch between these options

    • @joshshuter7536
      @joshshuter7536 4 ปีที่แล้ว

      you can still follow the tutorial, do everything the same as the video except initialise all of your variables in the .h file under the private: section of the main component class.

  • @CheeriosXIII
    @CheeriosXIII 5 ปีที่แล้ว +1

    Maybe the solution for the channels is putting the channel for loop inside the sample for loop:
    (Sorry for my english, I'm very bad...)
    for (int sample=0; samplegetWritePointer(channel, bufferToFill.startSample);
    buffer[sample] = waveTable[phase]*amplitude;
    }
    phase = fmod(phase+increment, wtSize);
    }
    I think this works. I don't know if it's the most efficient way to do it, or if the float* buffer inside the loops is a bad implementation...
    Thanks for the tutorials!

  • @leonelgouveia7573
    @leonelgouveia7573 6 ปีที่แล้ว +2

    Hello! First of all, thank you so much for the tutorials, it is really very challenging to learn this without you :) My only question is: why do you not just use a simple C/C++ array ?(since you end up defining the length of it in a variable anyway) Surely for the task you are using it for here it must be more efficient?

  • @marksparrows
    @marksparrows 7 ปีที่แล้ว

    Why are you using the inbuilt array, wouldn't the standard, fixed size array be more efficient?

  • @robertosborne2147
    @robertosborne2147 7 ปีที่แล้ว

    How would you use the frequency value if you wanted to change the value using a slider? I have my frequency event value being transferred to a test slider but I want to use that value for a global value "frequency".
    I thought it would be cool to make a GUI for this project that had a slider for frequency and amplitude.

    • @robertosborne2147
      @robertosborne2147 7 ปีที่แล้ว +1

      The Audio Programmer thanks man! I'll be looking forward to that video coming out. Until then ill still try to figure it out! :)

  • @LamaLongboarder
    @LamaLongboarder 2 ปีที่แล้ว

    Hi, this is an old video so I'm not necessarily expecting any responses.. But I have a question relating to the increment calculation.
    I think I've wrapped my head around it but I see one potential issue that made it harder for me to understand.
    Based on the way the for loop goes through the cycle in the wavetable, I'd expect to see aliasing happen, because, if I understand correctly, for many frequencies the zero crossing points and the peaks and valleys of the sine wave actually will not be read out perfectly for each cycle. e.g. when I use 20000 hz, the increment becomes 426.666....67. 1024/426.666 = 2,4, so this indeed doesn't line up nicely with the zero crossing points of the wavetable.
    I think I can hear my theory confirmed when I change the frequency to 20000 hz and build. You would expect to hear nothing, as my ears really do not go beyond 16kHz, bu I can clearly hear a way lower- but softer - tone coming through my speakers when I build, and it is stopped as soon as I stop the build.
    Can anybody tell me if I'm correct in this, or is my math just off and am I hearing ghosts? :) to be clear, I'm not trying to nitpick, this is just for my better understanding of the process.
    Also, if I'm right in this, would anyone know a way to correct for this? Or is this just inherent in working with digital sample rates?

    • @LamaLongboarder
      @LamaLongboarder 2 ปีที่แล้ว

      With a bit of further testing: in the 20000 hz example, the tone I hear is indeed matching to what I would expect the aliasing to be, being exactly 4000hz, as the increment takes 5 cycles to get back to zero crossing, e.g. 20000/5 = 4000.

  • @DashGlitch
    @DashGlitch 6 ปีที่แล้ว

    Great tutorial, I get successful build but no audio from the application, I have checked that its not a setting or output problem, any suggestions? thanks in advance :)

    • @nickhart8062
      @nickhart8062 6 ปีที่แล้ว

      yeah i have the same.. i'm curious if it has anything to do with the fact that this is a slightly different file structure now that Juce has been updated since this video was made (there is a header file, etc)

    • @CamFilms1000
      @CamFilms1000 6 ปีที่แล้ว

      @@nickhart8062 either of you ever find a solution to this?

    • @StefAslan
      @StefAslan 5 ปีที่แล้ว

      if you are using windows you need to set the sample rate for your Playback & Recording devices through the control panel

    • @shanewirkes
      @shanewirkes 5 ปีที่แล้ว +1

      I had bufferToFill.clearActiveBufferRegion() automatically set in my getNextAudioBlock() method...commenting this line out gave me audio.

    • @shanewirkes
      @shanewirkes 5 ปีที่แล้ว

      @@CamFilms1000 I had bufferToFill.clearActiveBufferRegion() automatically set in my getNextAudioBlock() method...commenting this line out gave me audio.

  • @haowu3319
    @haowu3319 4 ปีที่แล้ว

    I got a question about the numSamples, is that the count number of samples of one channel or two channels? If the numSamples() returns 512, it means I have to process 512 samples or 1024(sum of two channels)

    • @haowu3319
      @haowu3319 4 ปีที่แล้ว

      @@TheAudioProgrammer Thanks! Great tutorials

  • @piyushsingh1985
    @piyushsingh1985 4 ปีที่แล้ว

    excellent tutorials, Only a suggestion to put setAudioChannels(0,2) as a correction. Also new JUCE frameworks are using auto* instead of float*const

  • @felipefs106
    @felipefs106 7 ปีที่แล้ว +1

    Hey!, Great tutorial as always..I have 2 questions:
    1) Why the wavetable size is 1024? any special reason?
    2) The increment means 1 cycle of the sinewave at 440hz inside a 1024 block?.

    • @felipefs106
      @felipefs106 7 ปีที่แล้ว +1

      Got it, thanks!

    • @mpmansell
      @mpmansell 7 ปีที่แล้ว +7

      Another good reason for keeping a wave table size as a power of 2 is that, should you wish to run an FFT over it (say, to remove potentially aliasing harmonics at higher frequencies), the FFT algorithm runs more efficiently if the table is a power of 2. See www.earlevel.com/main/2002/08/31/a-gentle-introduction-to-the-fft/ for a simple explanation.

  • @hidden4425
    @hidden4425 4 ปีที่แล้ว

    I want to ask why are there so many warning i am using visual studio so there are

    • @hidden4425
      @hidden4425 4 ปีที่แล้ว

      @@TheAudioProgrammer thanks, i just today and i really feel welcomed here can you please help me with my warnings problem should i ignore them or should i try to fix though i don't know how i am a big newbie i only know the basics my goal is to do audio and signal processing and synthesis.

    • @hidden4425
      @hidden4425 4 ปีที่แล้ว

      @@TheAudioProgrammer ah, i see.

    • @hidden4425
      @hidden4425 4 ปีที่แล้ว

      @@TheAudioProgrammer do you get them on xcode too?

  • @josetosu96
    @josetosu96 5 ปีที่แล้ว

    How could we do a square wavetable in juce like that?

    • @milespartridge2081
      @milespartridge2081 5 ปีที่แล้ว +1

      Well, following the logic that in here we filled the wavetable with one cycle of a sine wave, we need to fill it with one cycle of a square wave, so half 1's and half -1's. I wrote this and it seems to work, not viewed it through an oscilloscope to see if it's accurate or anything though:
      //square
      for (int i = 0; i

    • @alessandrozullo6523
      @alessandrozullo6523 4 ปีที่แล้ว

      @@milespartridge2081 what do you mean with "1's and a half - 1's" ?

  • @WouterStudioHD
    @WouterStudioHD 7 ปีที่แล้ว

    Hey Joshua, thank you for all this great information! I've learned so much in such a short time because of you.. There's one thing I can't get my head around.. Why is the fmod used in the phase increment? I do understand that the phase needs to increase somehow, but I don't understand why fmod is used.. Also, is this the only way it should/can be done?

    • @WouterStudioHD
      @WouterStudioHD 7 ปีที่แล้ว

      Thank you for your time, Joshua! Great explanation, I get it now!

  • @rewildxmusic
    @rewildxmusic 4 ปีที่แล้ว

    1 Question to ensure my understandably. In the getNextAudioBlock() fn where you Iterate over numSamples. You use the phase as an index and increase it,mod it over again. That means 1 buffer can have multiple cycles of sin wave right?

    • @rewildxmusic
      @rewildxmusic 4 ปีที่แล้ว

      and If I increase the sample rate value (if possible), Then the cycles per 1 buffer will be decrease right?

  • @talhazaheer7133
    @talhazaheer7133 2 ปีที่แล้ว

    The basic formula of audio is given by : Amplitude * sin(2 * double_Pi * frequency + phase)
    I did the same thing with another approach but I think my one is easier to implement
    //Method To Get Next Sine Value
    float MainComponent::process()
    {
    auto sample = amplitude * sin(angle);
    angle += offset;
    return sample;
    }
    void MainComponent::prepareToPlay (int samplesPerBlockExpected, double sampleRate)
    {
    frequency = 440.0f;
    amplitude = 0.25f;
    angle = 0;
    offset = (2 * double_Pi * frequency) / sampleRate;
    }
    void MainComponent::getNextAudioBlock (const juce::AudioSourceChannelInfo& bufferToFill)
    {
    float* const leftChannel = bufferToFill.buffer->getWritePointer(0, bufferToFill.startSample);
    float* const rightChannel = bufferToFill.buffer->getWritePointer(1, bufferToFill.startSample);
    std::cout

  • @jakubbujak2640
    @jakubbujak2640 7 ปีที่แล้ว

    I get error - "wtSine.exe has triggered a breakpoint." on line "jassert (audioError.isEmpty());"

    • @jakubbujak2640
      @jakubbujak2640 7 ปีที่แล้ว

      I'm pretty sure that I've done everything exactly the same as in the video. I get same error in white noise generator (tutorial 10). :(

    • @leonardkupper5057
      @leonardkupper5057 6 ปีที่แล้ว +2

      I know this comes a bit late, but for all people watching this & experiencing the problem, there is a way to fix it. First, look into the triggered breakpoint, probably the value of the audioError object is something like "{text={data=0x0000000003742860 "The input and output devices don't share a common sample rate!" } }"
      Go into your systems sound settings (on Windows: Control Panel > Hardware and Sound > Sound), choose the current playback device and change its sample rate (in the "Advanced" tab) to 44100Hz. (For me, the problem only occured when using my headphones, which were set to a sample rate of 96kHZ.)

  • @mbakutchalla
    @mbakutchalla 5 ปีที่แล้ว

    Here's the code for the channel output without declaring the left and right speaker seperately
    for (int sample = 0; sample < bufferToFill.numSamples; sample++)
    {
    for (int i = 0; i < bufferToFill.buffer->getNumChannels(); i++)
    {
    float* const currentChannel = bufferToFill.buffer->getWritePointer(i, bufferToFill.startSample);
    currentChannel[sample] = amplitude * waveTable[(int)phase];
    }
    phase = fmod(phase + increment, waveTableSize);
    }
    }

  • @GaussianWonder
    @GaussianWonder 6 ปีที่แล้ว

    For some reason i hear random clicks and noises when generating the sound. Any idea why?

    • @GaussianWonder
      @GaussianWonder 6 ปีที่แล้ว

      I found out what the problem was. It`s related to the audio driver. On Linux I have ALSA and pulseaudio. When I switched to windows with Asio4all it was fine. Now I have another question. Which version is more efficient. The one you presented in the video or @Kacper Sagnowski `s one. To me they do the exact same thing identically, the only difference being the getWritePointer() function which is called once. Is that such a big deal?

  • @MatkatMusic
    @MatkatMusic 7 ปีที่แล้ว

    @12:21 you definitely need to script out what you're trying to say before you record these types of videos.
    @13:59 4 lines up is the Array::add() method, which acts like std::vector::push_back(), Array::insert() does not act like std::vector::push_back()