# 第一週HW1進度🎵🎶 * 作業內容:將 sine wave 合成器⏩改成方波、三角波、鋸齒波 * **SynthVoice.h**: ``` bool isPlaying = false; //新增一個變數紀錄是否在playing ``` * **SynthVoice.cpp**: ``` void SynthVoice::startNote (int midiNoteNumber, float velocity, juce::SynthesiserSound* sound, int currentPitchWheelPosition) { noteMidiNumber = midiNoteNumber; frequency = juce::MidiMessage::getMidiNoteInHertz(midiNoteNumber); currentAngle = 0.f; angleIncrement = frequency / getSampleRate(); tailOff = 0.0; value = 0; isPlaying = true; //設為true } ``` * Sawtooth wave ``` if (isPlaying) //利用isplaying可以去除雜音 { //Sawtooth wave if (tailOff > 0.0) { for (int i = startSample; i < (startSample + numSamples); i++) { if (currentAngle<0.5) value = currentAngle * level; else value = (currentAngle-0.5) * level; if (currentAngle>=1) currentAngle-=1; outputBuffer.addSample(0, i, value); outputBuffer.addSample(1, i, value); currentAngle += angleIncrement; tailOff *= 0.99; if (tailOff <= 0.05) { clearCurrentNote(); angleIncrement = 0.0; level = 0.0; isPlaying = false; break; } } } else { for (int i = startSample; i < (startSample + numSamples); i++) { if (currentAngle<0.5) value = currentAngle * level; else value = (currentAngle-0.5) * level; if (currentAngle>=1) currentAngle-=1; outputBuffer.addSample(0, i, value); outputBuffer.addSample(1, i, value); currentAngle += angleIncrement; } } ``` * Triangle wave(這邊只紀錄波形差異) ``` if (currentAngle<0.25)value=currentAngle * level; else if(currentAngle>=0.25&&currentAngle<0.5)value=(0.5-currentAngle)*level; else if(currentAngle>=0.5&&currentAngle<=0.75)value=-1*(currentAngle-0.5) * level; else value=-1*(1-currentAngle) * level; logger(std::to_string(value)); ``` * Rectengular wave(這邊只紀錄波形差異) ``` if(currentAngle < 0.5){ value = 1 * level; logger(std::to_string(value)); } else { value = -1 * level; logger(std::to_string(value)); } ``` # 波形圖 * 方波 ◽️ ![](https://i.imgur.com/g1YVnDX.png) * 鋸齒波🪚 ![](https://i.imgur.com/Bvpcjmo.png) * 三角波🍰 ![](https://i.imgur.com/Orkhtok.png) # 第二週進度 內容:加入了Slider, ComboBox, Spectrum 顯示 ## synthVoice.h - ADSR - ```public: void setADSR (float attack, float decay, float sustain, float release); private: juce::ADSR adsr; juce::ADSR::Parameters adsrParameters;``` - 接收 which wave param ```int wavecomboID;``` ## synthVoice.cpp ### renderNextBlock() #### ADSR (voice) ```adsr.setParameters(adsrParameters); // for loop float adsrValue = adsr.getNextSample(); if(adsrValue <= 0.005) isPlaying = false; // function for setting ADSR void SynthVoice::setADSR (float attack, float decay, float sustain, float release) { adsrParameters.attack = attack; adsrParameters.decay = decay; adsrParameters.sustain = sustain; adsrParameters.release = release; } ``` #### 控制不同的 wave voice (voice) ``` //for loop if (wavecomboID== 1){ value = std::sin(currentAngle) * level * adsrValue; currentAngle += angleIncrement; if (currentAngle >= juce::MathConstants<float>::twoPi)currentAngle -= juce::MathConstants<float>::twoPi; } //square else if(wavecomboID == 2){ currentAngle=currentAngle / juce::MathConstants<float>::twoPi; if (currentAngle<0.25)value=currentAngle * level* adsrValue; else if(currentAngle>=0.25&&currentAngle<0.5)value=(0.5-currentAngle)*level * adsrValue; else if(currentAngle>=0.5&&currentAngle<=0.75)value=-1*(currentAngle-0.5) * level* adsrValue; else value=-1*(1-currentAngle) * level * adsrValue; ``` if (currentAngle>=1) currentAngle-=1; } //triangle else if(wavecomboID == 3){ currentAngle=currentAngle / juce::MathConstants<float>::twoPi; if (currentAngle<0.5) value = currentAngle * level * adsrValue; else value = (currentAngle-0.5) * level * adsrValue; if (currentAngle>=1) currentAngle-=1; } //sawtooth else{ currentAngle=currentAngle / juce::MathConstants<float>::twoPi; if (currentAngle<0.25) value=currentAngle * level * adsrValue; else if(currentAngle>=0.25&&currentAngle<0.5) value=(0.5-currentAngle)*level * adsrValue; else if(currentAngle>=0.5&&currentAngle<=0.75) value=-1*(currentAngle-0.5) * level * adsrValue; else value=-1*(1-currentAngle) * level * adsrValue; if (currentAngle>=1) currentAngle-=1; }//function for getting waveComboID void SynthVoice::setwavecombo(int wavecomboID){wavecomboID = wavecomboID;} ## SliderController.h - UI stuff ``` class SliderController : public juce::Component { public: SliderController(SineWaveSynthesizerAudioProcessor&); ~SliderController() override; void paint (juce::Graphics&) override; void resized() override; private: //ADSR slider SineWaveSynthesizerAudioProcessor& processor; RotarySliderWithLabel levelSlider; RotarySliderWithLabel attackSlider; RotarySliderWithLabel decaySlider; RotarySliderWithLabel sustainSlider; RotarySliderWithLabel releaseSlider; juce::ComboBox waveFormComboBox; //add combo box in controller block std::vector<RotarySliderWithLabel*> sliders {&levelSlider, &attackSlider, &decaySlider, &sustainSlider, &releaseSlider}; std::unique_ptr<juce::AudioProcessorValueTreeState::ComboBoxAttachment> comboBoxAttachment; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SliderController)};``` ## SliderController.cpp (UI) 分別有兩個class ### 1.RotarySliderWithLabel slider controller 利用processor去設定slider長什麼樣子 // processor 設定 slider的樣子 processor(audioProcessor) { setLookAndFeel(&lnf); slider.setSliderStyle(juce::Slider::SliderStyle::RotaryHorizontalVerticalDrag); slider.setTextBoxStyle(juce::Slider::TextBoxBelow, true, slider.getTextBoxWidth(), slider.getTextBoxHeight()); title.setText(titleName, juce::dontSendNotification); title.setJustificationType(juce::Justification::centred); attachment = std::make_unique<SliderAttachment>(processor.tree, juce::String{titleName}.toLowerCase(), slider); addAndMakeVisible(title); addAndMakeVisible(slider); } 另外還需要paint跟resize function ### 2.SliderController slider controller控制UI下方的那塊區域,這塊區域放置slider跟combo box SliderController::SliderController(SineWaveSynthesizerAudioProcessor& p): processor(p), levelSlider(p, "Level"), attackSlider(p, "Attack"), decaySlider(p, "Decay"), sustainSlider(p, "Sustain"), releaseSlider(p, "Release"), waveFormComboBox("wavecomboID") { //slider for(auto& slider : sliders) { addAndMakeVisible(slider); } // combo box waveFormComboBox.addItem("sine", 1); waveFormComboBox.addItem("square", 2); waveFormComboBox.addItem("triangle", 3); waveFormComboBox.addItem("sawtooth", 4); addAndMakeVisible(waveFormComboBox); comboBoxAttachment = std::make_unique<juce::AudioProcessorValueTreeState::ComboBoxAttachment>(processor.tree, "wavecomboID", waveFormComboBox); } ### In controller的 resize **FlexBox Class:** Represents a FlexBox container, which contains and manages the layout of a set of FlexItem objects. **FlexItem Class:** Describes the properties of an item inside a FlexBox container juce::FlexBox flexBox;//宣告一個flexBox container // add flexItem into flexBox for(auto& slider : sliders) { juce::FlexItem item {*slider}; flexBox.items.add(item.withFlex(1.0));//以比例計算,也就是如果有4個1 -> 每個都佔 1/4 } juce::FlexItem item {waveFormComboBox}; flexBox.items.add(item.withFlex(1.0)); flexBox.performLayout(getLocalBounds()); for(auto& slider : sliders) { slider->setBounds(slider->getBounds().reduced(5)); } waveFormComboBox.setBounds(waveFormComboBox.getBounds().reduced(5)); ``` ### PluginEditor.cpp - 負責UI排版 ``` void SineWaveSynthesizerAudioProcessorEditor::resized() { auto area = getLocalBounds(); juce::FlexBox upFlexBox; //控制上面那塊 分成兩塊,左邊波形右邊頻譜 upFlexBox.flexDirection = juce::FlexBox::Direction::row; upFlexBox.items.add(juce::FlexItem(waveform).withFlex(1.0f)); upFlexBox.items.add(juce::FlexItem(spectrum).withFlex(1.0f)); juce::FlexBox bottomFlexBox; //下面整塊放 slider controller 的東西 bottomFlexBox.flexDirection = juce::FlexBox::Direction::row; bottomFlexBox.items.add(juce::FlexItem(controller).withFlex(1.0f)); juce::FlexBox flexBox; flexBox.flexDirection = juce::FlexBox::Direction::column; flexBox.items.add(juce::FlexItem(upFlexBox).withFlex(3.0f)); flexBox.items.add(juce::FlexItem(bottomFlexBox).withFlex(1.0f)); flexBox.performLayout(area.reduced(10)); for (auto& comp : subComponents) { comp->setBounds(comp->getBounds().reduced(3)); } } ``` ### PluginEditor.h 宣告所有的物件 ``` private: SineWaveSynthesizerAudioProcessor& audioProcessor; Oscilloscope waveform; FrequencySpectrum spectrum; SliderController controller; std::vector<juce::Component*> subComponents {&waveform, &spectrum, &controller}; ``` ### PluginProcessor.h - processor負責把UI跟Voice接起來 ``` //class SineWaveSynthesizerAudioProcessor : public juce::AudioProcessor //宣告一個tree state物件 tree juce::AudioProcessorValueTreeState tree; //宣告一個合成器物件 mySynth juce::Synthesiser mySynth; ``` ### PluginProcessor.cpp ``` // SineWaveSynthesizerAudioProcessor:AudioProcessor 吃的tree 參數 tree(*this, nullptr, "PARAM", { std::make_unique<juce::AudioParameterFloat>("level", "Level", juce::NormalisableRange<float>(0.0f, 1.0f, 0.1f), 0.5f, juce::String(), juce::AudioProcessorParameter::genericParameter, [](float value, int){return juce::String(value);}, [](juce::String text){ return text.getFloatValue();}), std::make_unique<juce::AudioParameterFloat>("attack", "Attack", juce::NormalisableRange<float>(0.0f, 1.0f, 0.1f), 0.5f, juce::String(), juce::AudioProcessorParameter::genericParameter, [](float value, int){return juce::String(value) + " s";}, [](juce::String text){ return text.getFloatValue();}), std::make_unique<juce::AudioParameterFloat>("decay", "Decay", juce::NormalisableRange<float>(0.0f, 1.0f, 0.1f), 0.5f, juce::String(), juce::AudioProcessorParameter::genericParameter, [](float value, int){return juce::String(value) + " s";}, [](juce::String text){ return text.getFloatValue();}), std::make_unique<juce::AudioParameterFloat>("sustain", "Sustain", juce::NormalisableRange<float>(0.0f, 1.0f, 0.1f), 0.5f, juce::String(), juce::AudioProcessorParameter::genericParameter, [](float value, int){return juce::String(value);}, [](juce::String text){ return text.getFloatValue();}), std::make_unique<juce::AudioParameterFloat>("release", "Release", juce::NormalisableRange<float>(0.0f, 1.0f, 0.1f), 0.5f, juce::String(), juce::AudioProcessorParameter::genericParameter, [](float value, int){return juce::String(value) + " s";}, [](juce::String text){ return text.getFloatValue();}), std::make_unique < juce::AudioParameterChoice>("wavecomboID", "wavecomboID", juce::StringArray({ "Sine","Square","Triangle","Saw" }),0) // void SineWaveSynthesizerAudioProcessor::processBlock中 把 level, ADSR, waveCombo參數load進來 auto* myVoice = dynamic_cast<SynthVoice*>(mySynth.getVoice(i)); myVoice->setLevel(tree.getRawParameterValue("level")->load()); myVoice->setADSR(tree.getRawParameterValue("attack")->load(), tree.getRawParameterValue("decay")->load(), tree.getRawParameterValue("sustain")->load(), tree.getRawParameterValue("release")->load()); myVoice->setwavecombo(tree.getParameterAsValue("wavecomboID").getValue()); ```