Skip to content

Commit

Permalink
Merge pull request #4 from Boscillator/develop
Browse files Browse the repository at this point in the history
v0.0.3
  • Loading branch information
Boscillator authored Sep 20, 2020
2 parents fa75903 + c6144b0 commit e7cd0d9
Show file tree
Hide file tree
Showing 10 changed files with 184 additions and 24 deletions.
2 changes: 1 addition & 1 deletion .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@
[submodule "opus"]
path = opus
url = https://github.com/xiph/opus.git
branch = v1.1.2
branch = v1.1.2
18 changes: 10 additions & 8 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ add_subdirectory(opus)
# `project()` command. `project()` sets up some helpful variables that describe source/binary
# directories, and the current project version. This is a standard CMake command.

project(AUGER_PLUGIN VERSION 0.0.2)
project(AUGER_PLUGIN VERSION 0.0.3)

# If you've installed JUCE somehow (via a package manager, or directly using the CMake install
# target), you'll need to tell this project that it depends on the installed copy of JUCE. If you've
Expand All @@ -45,12 +45,12 @@ juce_add_plugin(AugerPlugin
# VERSION ... # Set this if the plugin version is different to the project version
# ICON_BIG ... # ICON_* arguments specify a path to an image file to use as an icon for the Standalone
# ICON_SMALL ...
# COMPANY_NAME ... # Specify the name of the plugin's author
# IS_SYNTH TRUE/FALSE # Is this a synth or an effect?
# NEEDS_MIDI_INPUT TRUE/FALSE # Does the plugin need midi input?
# NEEDS_MIDI_OUTPUT TRUE/FALSE # Does the plugin need midi output?
# IS_MIDI_EFFECT TRUE/FALSE # Is this plugin a MIDI effect?
# EDITOR_WANTS_KEYBOARD_FOCUS TRUE/FALSE # Does the editor need keyboard focus?
COMPANY_NAME "Boscillator Audio" # Specify the name of the plugin's author
IS_SYNTH FALSE # Is this a synth or an effect?
NEEDS_MIDI_INPUT FALSE # Does the plugin need midi input?
NEEDS_MIDI_OUTPUT FALSE # Does the plugin need midi output?
IS_MIDI_EFFECT FALSE # Is this plugin a MIDI effect?
EDITOR_WANTS_KEYBOARD_FOCUS FALSE # Does the editor need keyboard focus?
COPY_PLUGIN_AFTER_BUILD FALSE # Should the plugin be installed to a default location after building?
PLUGIN_MANUFACTURER_CODE Bosc # A four-character manufacturer id with at least one upper-case character
PLUGIN_CODE Augr # A unique four-character plugin id with at least one upper-case character
Expand All @@ -63,7 +63,8 @@ juce_add_plugin(AugerPlugin
# include all your JUCE module headers; if you're happy to include module headers directly, you
# probably don't need to call this.

juce_generate_juce_header(AugerPlugin)
#juce_add_module(${CMAKE_CURRENT_LIST_DIR}/JUCE/modules/juce_dsp)
# juce_generate_juce_header(AugerPlugin)

# `target_sources` adds source files to a target. We pass the target that needs the sources as the
# first argument, then a visibility parameter for the sources (PRIVATE is normally best practice,
Expand Down Expand Up @@ -113,6 +114,7 @@ target_compile_definitions(AugerPlugin
target_link_libraries(AugerPlugin PRIVATE
# AudioPluginData # If we'd created a binary data target, we'd link to it here
juce::juce_audio_utils
juce::juce_dsp
opus)

set_property(TARGET AugerPlugin PROPERTY CXX_STANDARD 20)
Expand Down
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
# auger
LoFi hold music VST.

Auger uses the opus codec to digitally compress audio in real time, for that
lofi VOIP sound. Making anything sound like hold music, discord or skype.
Auger adds a LoFi, broken, warmth to any sound source using bitrate reduction. Unlike a bit reduction effect, Auger
uses VOIP technology in an attempt to preserve as much of the signal as possible. This gives it the worn out sound of
hold music or a conference call.

![Screenshot of the window 95 inspired UI](./docs/screenshot.png)
![Screenshot of Auger](./docs/screenshot.png)
Binary file modified docs/screenshot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
26 changes: 25 additions & 1 deletion src/PluginEditor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ AudioPluginAudioProcessorEditor::AudioPluginAudioProcessorEditor (AudioPluginAud

AudioPluginAudioProcessorEditor::~AudioPluginAudioProcessorEditor()
{
processorRef.unattachAllSliders();
processorRef.unattachAllAttachements();
bitrateSlider.removeListener(this);
setLookAndFeel(nullptr);
}
Expand All @@ -39,6 +39,12 @@ void AudioPluginAudioProcessorEditor::paint (juce::Graphics& g)
// Create boarder for slider
drawSection(g, bitrateSlider.getTextFromValue(bitrateSlider.getValue()), 128, 128, 343, 58);

// Create Dry/Wet border
drawSection(g, "Dry/Wet", 305, 200, 167, 58);

// Create mode border
drawSection(g, "Mode", 128, 200, 167, 58);

}

void AudioPluginAudioProcessorEditor::drawWin95Window(juce::Graphics& g) const {
Expand All @@ -55,19 +61,37 @@ void AudioPluginAudioProcessorEditor::drawWin95Window(juce::Graphics& g) const {
void AudioPluginAudioProcessorEditor::resized()
{
bitrateSlider.setBounds(147, 141, 305, 40);
dryWetSlider.setBounds(319, 220, 132, 40);
modeBox.setBounds(147, 222, 132, 20);
}

void AudioPluginAudioProcessorEditor::configureLookAndFeel() {
setLookAndFeel(&_lookAndFeel);
}

void AudioPluginAudioProcessorEditor::addComponents() {
// Bitrate slider
bitrateSlider.setSliderStyle(juce::Slider::LinearHorizontal);
bitrateSlider.setTextBoxStyle(juce::Slider::NoTextBox, false, 90, 0);
bitrateSlider.setTextValueSuffix(" BPS");
bitrateSlider.addListener(this);
processorRef.attachSlider("bitrate", bitrateSlider);
addAndMakeVisible(bitrateSlider);

// Dry/wet slider
dryWetSlider.setSliderStyle(juce::Slider::LinearHorizontal);
dryWetSlider.setTextBoxStyle(juce::Slider::NoTextBox, false, 90, 0);
dryWetSlider.addListener(this);
processorRef.attachSlider("drywet", dryWetSlider);
addAndMakeVisible(dryWetSlider);

// Mode box
modeBox.addItem("Natural", 1);
modeBox.addItem("Harsh", 2);
modeBox.addItem("Metallic", 3);
modeBox.setSelectedItemIndex(0);
processorRef.attachComboBox("mode", modeBox);
addAndMakeVisible(modeBox);
}

void AudioPluginAudioProcessorEditor::drawSection(juce::Graphics& g, const juce::String& label, int x, int y, int width,
Expand Down
2 changes: 2 additions & 0 deletions src/PluginEditor.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ class AudioPluginAudioProcessorEditor : public juce::AudioProcessorEditor, publ

//==============================================================================
juce::Slider bitrateSlider;
juce::Slider dryWetSlider;
juce::ComboBox modeBox;

void sliderValueChanged(juce::Slider* slider) override;

Expand Down
84 changes: 74 additions & 10 deletions src/PluginProcessor.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#ifdef AUGER_DEBUG

#include <chrono>

#endif

#include <thread>
Expand All @@ -21,13 +23,18 @@ AudioPluginAudioProcessor::AudioPluginAudioProcessor()
_blockSizeAdapter(this, 2 * 480),
_parameters(*this, nullptr, juce::Identifier("AugerPlugin"),
{
std::make_unique<juce::AudioParameterInt>("bitrate", "BitRate", 8000, 40000, 16000)
std::make_unique<juce::AudioParameterInt>("bitrate", "BitRate", 8000, 40000, 16000),
std::make_unique<juce::AudioParameterFloat>("drywet", "Dry/Wet", 0.0f, 1.0f, 1.0f),
std::make_unique<juce::AudioParameterChoice>("mode", "Mode", juce::StringArray(
{"Natural", "Harsh", "Metallic"}), 0)
}) {
_parameters.addParameterListener("bitrate", this);
_parameters.addParameterListener("mode", this);
_drywet = _parameters.getRawParameterValue("drywet");
}

AudioPluginAudioProcessor::~AudioPluginAudioProcessor() {
unattachAllSliders();
unattachAllAttachements();
}

//==============================================================================
Expand Down Expand Up @@ -98,10 +105,23 @@ void AudioPluginAudioProcessor::prepareToPlay(double sampleRate, int samplesPerB
_encoder = opus_encoder_create(48000, 2, OPUS_APPLICATION_VOIP, &err);
_decoder = opus_decoder_create(48000, 2, &err);

// Set bitrate TODO: replace with parameters
int bitrate = (int)*_parameters.getRawParameterValue("bitrate");
// Set bitrate
int bitrate = (int) *_parameters.getRawParameterValue("bitrate");
opus_encoder_ctl(_encoder, OPUS_SET_BITRATE(bitrate));

// Configure dry/wet mixer
_dryWetMixer.setMixingRule(juce::dsp::DryWetMixingRule::balanced);
juce::dsp::ProcessSpec spec{sampleRate, static_cast<juce::uint32>(samplesPerBlock), 2};
_dryWetMixer.prepare(spec);
// _dryWetMixer.setWetLatency(_blockSizeAdapter.getChunkSize());

// Configure filter
_lowpassFilterLeft.setCoefficients(juce::IIRCoefficients::makeLowPass(sampleRate, 8000, 0.1));
_lowpassFilterRight.setCoefficients(juce::IIRCoefficients::makeLowPass(sampleRate, 8000, 0.1));

// Initalize processing mode
parameterChanged("mode", *_parameters.getRawParameterValue("mode"));

}

void AudioPluginAudioProcessor::releaseResources() {
Expand Down Expand Up @@ -130,15 +150,30 @@ bool AudioPluginAudioProcessor::isBusesLayoutSupported(const BusesLayout& layout
#endif
}

//==============================================================================
void AudioPluginAudioProcessor::processBlock(juce::AudioBuffer<float>& buffer,
juce::MidiBuffer& midiMessages) {
juce::ignoreUnused(midiMessages);

juce::ScopedNoDenormals noDenormals;

// Set up dry/wet mixer
_dryWetMixer.setWetMixProportion(*_drywet);
_dryWetMixer.pushDrySamples(buffer);

// Perform compression
interlace(buffer.getNumSamples(), buffer.getReadPointer(0), buffer.getReadPointer(1), _interlacedBuffer.data());
_blockSizeAdapter.process(_interlacedBuffer);
deinterlace(buffer.getNumSamples(), buffer.getWritePointer(0), buffer.getWritePointer(1), _interlacedBuffer.data());

// Filter samples to remove harshness
if (_useFilter) {
_lowpassFilterLeft.processSamples(buffer.getWritePointer(0), buffer.getNumSamples());
_lowpassFilterRight.processSamples(buffer.getWritePointer(1), buffer.getNumSamples());
}

// Remix dry signal
_dryWetMixer.mixWetSamples(buffer);
}

//==============================================================================
Expand Down Expand Up @@ -171,31 +206,60 @@ void AudioPluginAudioProcessor::setStateInformation(const void* data, int sizeIn
_parameters.replaceState(juce::ValueTree::fromXml(*xmlState));
}

//==============================================================================
void AudioPluginAudioProcessor::processChunk(std::span<float> chunk) {
// Encode and decode packet
unsigned char data[PACKET_SIZE];
size_t bytes = opus_encode_float(_encoder, chunk.data(), chunk.size() / 2, data, PACKET_SIZE);
opus_decode_float(_decoder, data, bytes, chunk.data(), chunk.size() / 2, 0);

// std::this_thread::sleep_for(std::chrono::microseconds(100));
}

//==============================================================================
void AudioPluginAudioProcessor::parameterChanged(const juce::String& parameterID, float newValue) {
if(_encoder == nullptr) return;
if(parameterID == "bitrate") {
if (_encoder == nullptr) return;
else if (parameterID == "bitrate") {
opus_encoder_ctl(_encoder, OPUS_SET_BITRATE((int) newValue));
} else if (parameterID == "mode") {
switch ((int) newValue) {
case 0:
updateFrameSize(480);
_useFilter = true;
break;
case 1:
updateFrameSize(480);
_useFilter = false;
break;
case 2:
updateFrameSize(120);
_useFilter = false;
break;
}
}
}

//==============================================================================
void AudioPluginAudioProcessor::attachSlider(const juce::String& parameterId, juce::Slider& slider) {
_sliderAttachments.push_back(std::make_unique<juce::AudioProcessorValueTreeState::SliderAttachment>(_parameters, parameterId, slider));
_sliderAttachments.push_back(
std::make_unique<juce::AudioProcessorValueTreeState::SliderAttachment>(_parameters, parameterId, slider));
}

void AudioPluginAudioProcessor::unattachAllSliders() {
void AudioPluginAudioProcessor::attachComboBox(const juce::String& parameterId, juce::ComboBox& comboBox) {
_comboBoxAttachements.push_back(
std::make_unique<juce::AudioProcessorValueTreeState::ComboBoxAttachment>(_parameters, parameterId,
comboBox));
}

void AudioPluginAudioProcessor::unattachAllAttachements() {
_sliderAttachments.clear();
_comboBoxAttachements.clear();
}

void AudioPluginAudioProcessor::updateFrameSize(size_t size) {
_blockSizeAdapter.setChunkSize(2 * size);
// _dryWetMixer.setWetLatency(size);
}


//==============================================================================
// This creates new instances of the plugin..
juce::AudioProcessor* JUCE_CALLTYPE createPluginFilter() {
Expand Down
14 changes: 13 additions & 1 deletion src/PluginProcessor.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma once

#include <juce_audio_processors/juce_audio_processors.h>
#include <juce_dsp/juce_dsp.h>
#include <opus.h>
#include "BlockSizeAdapter.h"
#define PACKET_SIZE 1024
Expand Down Expand Up @@ -50,19 +51,30 @@ class AudioPluginAudioProcessor : public juce::AudioProcessor, public BlockSize

//==============================================================================
void attachSlider(const juce::String& parameterId, juce::Slider& slider);
void unattachAllSliders();
void attachComboBox(const juce::String& parameterId, juce::ComboBox& comboBox);
void unattachAllAttachements();

private:
//==============================================================================
void updateFrameSize(size_t size);

// Processors ==================================================================

BlockSizeAdapter _blockSizeAdapter;
std::vector<float> _interlacedBuffer;
OpusEncoder* _encoder = nullptr;
OpusDecoder* _decoder = nullptr;
juce::dsp::DryWetMixer<float> _dryWetMixer;
juce::IIRFilter _lowpassFilterLeft;
juce::IIRFilter _lowpassFilterRight;

// Parameters ==================================================================
juce::AudioProcessorValueTreeState _parameters;
std::vector<std::unique_ptr<juce::AudioProcessorValueTreeState::SliderAttachment>> _sliderAttachments;
std::vector<std::unique_ptr<juce::AudioProcessorValueTreeState::ComboBoxAttachment>> _comboBoxAttachements;

std::atomic<float>* _drywet = nullptr;
std::atomic<bool> _useFilter = true;

//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioPluginAudioProcessor)
Expand Down
47 changes: 47 additions & 0 deletions src/Win95LookAndFeel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
Win95LookAndFeel::Win95LookAndFeel() {
setColour(juce::ResizableWindow::backgroundColourId, _colorLightBlue);
setColour(juce::Slider::trackColourId, _colorBlack);
setColour(juce::ComboBox::textColourId, _colorBlack);
setColour(juce::PopupMenu::textColourId, _colorBlack);
}

void Win95LookAndFeel::drawLinearSlider(juce::Graphics& g, int x, int y, int width, int height, float sliderPos,
Expand Down Expand Up @@ -35,10 +37,55 @@ void Win95LookAndFeel::drawLinearSlider(juce::Graphics& g, int x, int y, int wid
g.fillPath(p);
}

int Win95LookAndFeel::getMenuWindowFlags() {
return 0;
}

juce::Path Win95LookAndFeel::makeThumbPath(int x, int y) {
juce::Path p;
p.addRectangle(x - 4, y, 8, 18);
p.addTriangle(x - 4, y + 18, x, y + 24, x + 4, y + 18);
return p;
}

void Win95LookAndFeel::drawComboBox(juce::Graphics& graphics, int width, int height, bool isButtonDown, int buttonX,
int buttonY, int buttonW, int buttonH, juce::ComboBox& box) {
// Draw inner shadow
graphics.setColour(_colorBlack);
graphics.fillRect(0,0, width, height);

// Draw background
graphics.setColour(_colorWhite);
graphics.fillRect(1,1, width - 1, height - 1);

// Make buttons square
buttonH -= 6;
buttonY += 3;
buttonX = width - buttonH - 3;
buttonW = buttonH;

// Draw button

graphics.setColour(_colorWhite);
graphics.fillRect(buttonX, buttonY, buttonW, buttonH);

graphics.setColour(_colorBlack);
graphics.fillRect(buttonX + 1, buttonY + 1, buttonW, buttonH);

graphics.setColour(_colorLightGray);
graphics.fillRect(buttonX + 1, buttonY + 1, buttonW-1, buttonH-1);

// Draw the arrow on the button
juce::Path p;
p.addPolygon(juce::Point<float>(buttonX + buttonW/2, buttonY + buttonH/2), 3, 4, juce::float_Pi);
graphics.setColour(_colorBlack);
graphics.fillPath(p);
}

void Win95LookAndFeel::drawPopupMenuBackground(juce::Graphics& graphics, int width, int height) {
graphics.setColour(_colorBlack);
graphics.fillRect(0,0,width,height);
graphics.setColour(_colorLightGray);
graphics.fillRect(0,0,width - 2, height - 2);
}

Loading

0 comments on commit e7cd0d9

Please sign in to comment.