# 第一週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&¤tAngle<0.5)value=(0.5-currentAngle)*level;
else if(currentAngle>=0.5&¤tAngle<=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));
}
```
# 波形圖
* 方波 ◽️

* 鋸齒波🪚

* 三角波🍰

# 第二週進度
內容:加入了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&¤tAngle<0.5)value=(0.5-currentAngle)*level * adsrValue;
else if(currentAngle>=0.5&¤tAngle<=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&¤tAngle<0.5) value=(0.5-currentAngle)*level * adsrValue;
else if(currentAngle>=0.5&¤tAngle<=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());
```