ゲーム開発
Unity
UnrealEngine
C++
Blender
Houdini
ゲーム数学
ゲームAI
グラフィックス
サウンド
アニメーション
GBDK
制作日記
IT関連
ツール開発
フロントエンド関連
サーバサイド関連
WordPress関連
ソフトウェア設計
おすすめ技術書
音楽
DTM
楽器・機材
ピアノ
ラーメン日記
四コマ漫画
その他
おすすめアイテム
おもしろコラム
  • ゲーム開発
    • Unity
    • UnrealEngine
    • C++
    • Blender
    • Houdini
    • ゲーム数学
    • ゲームAI
    • グラフィックス
    • サウンド
    • アニメーション
    • GBDK
    • 制作日記
  • IT関連
    • ツール開発
    • フロントエンド関連
    • サーバサイド関連
    • WordPress関連
    • ソフトウェア設計
    • おすすめ技術書
  • 音楽
    • DTM
    • 楽器・機材
    • ピアノ
  • ラーメン日記
    • 四コマ漫画
      • その他
        • おすすめアイテム
        • おもしろコラム
      1. ホーム
      2. 20240322_01_juce_distortion

      【JUCE】DTMプラグインを作ってみる 〜ディストーション編〜【VST/AU】

      サウンドC++DTMJUCE
      2024-03-22

      マイケル
      マイケル
      みなさんこんにちは! マイケルです!
      エレキベア
      エレキベア
      こんにちクマ~~
      マイケル
      マイケル
      突然ですが今日は何と、DTMプラグインの開発に挑戦してみました! その名も・・・
      20240322_01_juce_distortion_01
      マイケル
      マイケル
      渡邉ディストーション です!!
      エレキベア
      エレキベア
      ええぇ・・・
      マイケル
      マイケル
      これはJUCEというフレームワークを使用して開発したもので、 2種類の歪みを搭載したシンプルなディストーションです! 下記のGitHubリポジトリにコードを格納していますので、よければこちらもご参照ください!

      GitHub - juce-plugin-distortion

      エレキベア
      エレキベア
      JUCE・・・聞いたことがある気がするクマ C++で書けるのクマね
      マイケル
      マイケル
      今回初めて触ってみたけど、プラグイン開発に必要な機能があらかじめ揃ってたので 一度使い方を覚えたら低コストで作成できて便利だなと思いました! 早速みていきましょう!

      JUCEフレームワークとは

      マイケル
      マイケル
      JUCEはオーディオアプリやプラグインを開発するためのフレームワークで、C++で記述することができます。 オーディオ機能やUIコンポーネントが充実していたり、VST3やAU形式などマルチプラットフォームで出力できる点が魅力です!
      20240322_01_juce_distortion_03

      JUCE - 公式サイト

      エレキベア
      エレキベア
      オーディオ開発版Unityみたいな感じクマね
      マイケル
      マイケル
      実装するにあたり、下記の公式チュートリアルと JUCEJAPANシリーズの書籍を参考にさせていただきました! こちらはKindleUnlimitdで読めるのでおすすめです!

      JUCE - チュートリアル

      JUCE JAPAN vol.1: JUCEではじめるVST/AUプラグイン制作...

      エレキベア
      エレキベア
      日本語の情報が少ないクマから解説本があるのはありがたいクマね

      ディストーションプラグインの開発

      マイケル
      マイケル
      それでは今回行った実装の内容について紹介します!

      パラメータの実装

      マイケル
      マイケル
      まずはプラグイン内で使用するパラメータと関連する関数について定義します。 今回用意が必要なのは ・InputVolume:入力ボリューム値 ・Gain:歪み量 ・OutputVolume:出力ボリューム値 ・Special:歪み切り替えフラグ のパラメータになります。
      class Juce_plugin_distortionAudioProcessor  : public juce::AudioProcessor
      {
      
      ・・・略・・・
      
          // パラメータ定義
          enum Parameters
          {
              MasterBypass = 0,  // バイパスのON/OFF切り替え
              InputVolume,       // 入力ボリューム調整
              Gain,              // 歪み量(クリッピング閾値)
              OutputVolume,      // 出力ボリューム調整
              Special,           // スペシャル
              TotalParameterNum, // パラメータの合計数
          };
      
          // パラメータ項目数
          int getNumParameters() override;
      
          // パラメータの値
          float getParameter(int index) override;
      
          // パラメータのID
          juce::String getParameterID(int index) override;
      
          // パラメータの表示名
          const juce::String getParameterName(int index) override;
      
          // パラメータの表示内容
          const juce::String getParameterText(int index) override;
      
      private:
          juce::AudioProcessorValueTreeState _parameters;
          std::atomic<float>* _masterBypassParameter = nullptr;
          std::atomic<float>* _inputVolumeParameter = nullptr;
          std::atomic<float>* _gainParameter = nullptr;
          std::atomic<float>* _outputVolumeParameter = nullptr;
          std::atomic<float>* _specialParameter = nullptr;
      
          //==============================================================================
          JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Juce_plugin_distortionAudioProcessor)
      };
      
      ▲パラメータと関数の定義
      マイケル
      マイケル
      実際のパラメータの値や表示名といった情報は ・getParameter ・getParameterName といった関数をoverrideすることで定義します。 パラメータの初期化と各関数の実装は下記のようになりました。
      Juce_plugin_distortionAudioProcessor::Juce_plugin_distortionAudioProcessor() : 
      #ifndef JucePlugin_PreferredChannelConfigurations
           AudioProcessor (BusesProperties()
                           #if ! JucePlugin_IsMidiEffect
                            #if ! JucePlugin_IsSynth
                             .withInput  ("Input",  juce::AudioChannelSet::stereo(), true)
                            #endif
                             .withOutput ("Output", juce::AudioChannelSet::stereo(), true)
                           #endif
                             ),
      #endif
          _parameters (*this, nullptr, juce::Identifier("WatanabeDistotion"),
              {
                  // define parameters.
                  // * volume slider: 0.0 ~ 1.5 => -100dB ~  7dB
                  // * gain slider:   1.0 ~ 2.0 =>    0dB ~ 12dB
                  std::make_unique<juce::AudioParameterFloat>(getParameterID(MasterBypass), getParameterName(MasterBypass), 0.0f, 1.0f, 0.0f),
                  std::make_unique<juce::AudioParameterFloat>(getParameterID(InputVolume),  getParameterName(InputVolume),  0.0f, 1.5f, 1.0f),
                  std::make_unique<juce::AudioParameterFloat>(getParameterID(Gain),         getParameterName(Gain),         1.0f, 2.0f, 1.0f),
                  std::make_unique<juce::AudioParameterFloat>(getParameterID(OutputVolume), getParameterName(OutputVolume), 0.0f, 1.5f, 1.0f),
                  std::make_unique<juce::AudioParameterFloat>(getParameterID(Special),      getParameterName(Special),      0.0f, 1.0f, 0.0f),
              })
      { 
          // set default values.
          _masterBypassParameter = _parameters.getRawParameterValue(getParameterID(MasterBypass));
          _inputVolumeParameter  = _parameters.getRawParameterValue(getParameterID(InputVolume));
          _gainParameter         = _parameters.getRawParameterValue(getParameterID(Gain));
          _outputVolumeParameter = _parameters.getRawParameterValue(getParameterID(OutputVolume));
          _specialParameter      = _parameters.getRawParameterValue(getParameterID(Special));
      }
      
      ・・・略・・・
      
      int Juce_plugin_distortionAudioProcessor::getNumParameters()
      {
          return TotalParameterNum;
      }
      
      float Juce_plugin_distortionAudioProcessor::getParameter(int index)
      {
          switch (index)
          {
          case MasterBypass:
              return (float)*_masterBypassParameter;
          case InputVolume:
              return (float)*_inputVolumeParameter;
          case Gain:
              return (float)*_gainParameter;
          case OutputVolume:
              return (float)*_outputVolumeParameter;
          case Special:
              return (float)*_specialParameter;
          default:
              return -1.0f;
          }
      }
      
      juce::String Juce_plugin_distortionAudioProcessor::getParameterID(int index)
      {
          switch (index)
          {
          case MasterBypass:
              return std::to_string(MasterBypass);
          case InputVolume:
              return std::to_string(InputVolume);
          case Gain:
              return std::to_string(Gain);
          case OutputVolume:
              return std::to_string(OutputVolume);
          case Special:
              return std::to_string(Special);
          default:
              return "";
          }
      }
      
      const juce::String Juce_plugin_distortionAudioProcessor::getParameterName(int index)
      {
          switch (index)
          {
          case MasterBypass:
              return "BYPASS";
          case InputVolume:
              return "In";
          case Gain:
              return "Gain";
          case OutputVolume:
              return "Out";
          case Special:
              return "Special";
          default:
              return "";
          }
      }
      
      const juce::String Juce_plugin_distortionAudioProcessor::getParameterText(int index)
      {
          switch (index)
          {
          case MasterBypass:
          case Special:
              return getParameterName(index);
          case InputVolume:
          case OutputVolume:
              return getParameterName(index) + "\n" + juce::String(juce::Decibels::gainToDecibels(pow(getParameter(index), 2)), 1) + "\ndB";
          case Gain:
              return getParameterName(index) + "\n" + juce::String(juce::Decibels::gainToDecibels(pow(getParameter(index), 2)), 1) + " dB";
          default:
              return "";
          }
      }
      
      ▲パラメータ関連の処理
      エレキベア
      エレキベア
      各パラメータに関する情報を設定するのクマね

      UIの実装

      マイケル
      マイケル
      次に下記のようなUIを実装してみます。
      20240322_01_juce_distortion_01
      ▲今回作成するプラグインの見た目

      マイケル
      マイケル
      スライダー等の基本的なUIコンポーネントは用意されていますが、 背景画像は自分で用意しなければなりません。 今回は「渡邉」感を押し出した画像を用意しました!
      20240322_01_juce_distortion_04
      ▲渡邉感を押し出した画像

      20240322_01_juce_distortion_05
      ▲画像はProducer上で登録することで使用できるようになる

      エレキベア
      エレキベア
      (これは渡邉感なのか・・・?)
      マイケル
      マイケル
      そして実際のUI実装は下記のようになりました。 似たようなコンポーネントは関数化しておくとコードがスッキリします。
      class Juce_plugin_distortionAudioProcessorEditor  : public juce::AudioProcessorEditor,
          private juce::Timer
      {
      
      ・・・略・・・
      
      private:
      
      ・・・略・・・
      
          // UIコンポーネント
          juce::Slider _inputVolumeSlider;
          juce::Slider _gainSlider;
          juce::Slider _outputVolumeSlider;
          juce::Label _inputVolumeLabel;
          juce::Label _gainLabel;
          juce::Label _outputVolumeLabel;
          juce::ToggleButton _specialToggle;
      
          // UIコンポーネント初期化処理
          void initSliderComponent(juce::Slider* slider, juce::Slider::SliderStyle style);
          void initLabelComponent(juce::Label* label, juce::String text);
          void initToggleButtonComponent(juce::ToggleButton* toggleButton, juce::String text);
      
      ・・・略・・・
      
      };
      
      ▲UIコンポーネントの定義
      Juce_plugin_distortionAudioProcessorEditor::Juce_plugin_distortionAudioProcessorEditor (Juce_plugin_distortionAudioProcessor& p, juce::AudioProcessorValueTreeState& vts)
          : AudioProcessorEditor (&p), audioProcessor (p), valueTreeState (vts)
      {
          setSize (420, 300);
      
          // init ui components.
          initSliderComponent(&_inputVolumeSlider, juce::Slider::LinearVertical);
          initSliderComponent(&_gainSlider, juce::Slider::RotaryHorizontalVerticalDrag);
          initSliderComponent(&_outputVolumeSlider, juce::Slider::LinearVertical);
          initLabelComponent(&_inputVolumeLabel, processor.getParameterText(Juce_plugin_distortionAudioProcessor::InputVolume));
          initLabelComponent(&_gainLabel, processor.getParameterText(Juce_plugin_distortionAudioProcessor::Gain));
          initLabelComponent(&_outputVolumeLabel, processor.getParameterText(Juce_plugin_distortionAudioProcessor::OutputVolume));
          initToggleButtonComponent(&_specialToggle, processor.getParameterText(Juce_plugin_distortionAudioProcessor::Special));
      
          // display window.
          addAndMakeVisible(&_inputVolumeSlider);
          addAndMakeVisible(&_gainSlider);
          addAndMakeVisible(&_outputVolumeSlider);
          addAndMakeVisible(&_inputVolumeLabel);
          addAndMakeVisible(&_gainLabel);
          addAndMakeVisible(&_outputVolumeLabel);
          addAndMakeVisible(&_specialToggle);
      }
      
      Juce_plugin_distortionAudioProcessorEditor::~Juce_plugin_distortionAudioProcessorEditor()
      {
      }
      
      //==============================================================================
      void Juce_plugin_distortionAudioProcessorEditor::paint (juce::Graphics& g)
      {
          auto backgroundImage = juce::ImageCache::getFromMemory(BinaryData::bg_plugin_distortion_png, BinaryData::bg_plugin_distortion_pngSize);
          g.drawImageAt(backgroundImage, 0, 0);
      }
      
      void Juce_plugin_distortionAudioProcessorEditor::resized()
      {
          auto contentHeight = 160;
          auto contentPosY = 48;
          auto labelHight = 60;
          auto labelPosY = contentHeight + contentPosY;
      
          _inputVolumeSlider
              .setBounds(300, contentPosY, 60, contentHeight);
          _gainSlider
              .setBounds(76, contentPosY, 140, contentHeight);
          _outputVolumeSlider
              .setBounds(348, contentPosY, 60, contentHeight);
          _inputVolumeLabel
              .setBounds(300, labelPosY, 60, labelHight);
          _gainLabel
              .setBounds(76, labelPosY, 140, labelHight);
          _outputVolumeLabel
              .setBounds(348, labelPosY, 60, labelHight);
          _specialToggle
              .setBounds(12, 8, 140, 30);
      }
      
      void Juce_plugin_distortionAudioProcessorEditor::initSliderComponent(juce::Slider* slider, juce::Slider::SliderStyle style)
      {
          (*slider).setSliderStyle(style);
          (*slider).setTextBoxStyle(juce::Slider::NoTextBox, false, 80, 20);
          (*slider).setColour(juce::Slider::backgroundColourId, juce::Colours::lightgrey);
          (*slider).setColour(juce::Slider::trackColourId, juce::Colours::darkgrey);
          (*slider).setColour(juce::Slider::rotarySliderOutlineColourId, juce::Colours::lightgrey);
          (*slider).setColour(juce::Slider::rotarySliderFillColourId, juce::Colours::darkgrey);
      }
      
      void Juce_plugin_distortionAudioProcessorEditor::initLabelComponent(juce::Label* label, juce::String text)
      {
          (*label).setFont(juce::Font(16.0f, juce::Font::plain));
          (*label).setJustificationType(juce::Justification::centredTop);
          (*label).setEditable(false, false, false);
          (*label).setColour(juce::Label::textColourId, juce::Colours::white);
          (*label).setText(text, juce::dontSendNotification);
      }
      
      void Juce_plugin_distortionAudioProcessorEditor::initToggleButtonComponent(juce::ToggleButton* toggleButton, juce::String text)
      {
          (*toggleButton).setButtonText(text);
          (*toggleButton).setColour(juce::ToggleButton::textColourId, juce::Colours::white);
      }
      
      ▲UIの配置
      エレキベア
      エレキベア
      コードベースでUIを構築できるのクマね

      パラメータとUIの紐付け

      マイケル
      マイケル
      そして次は用意したパラメータとUIを紐付けます。 Editor -> Processor への値受け渡しは各Attachment変数とIDを紐づけるだけでよいのですが、Processor -> Editor への反映はタイマーにて監視する必要があります。 こちらの実装は下記のようになります。
      juce::AudioProcessorEditor* Juce_plugin_distortionAudioProcessor::createEditor()
      {
          return new Juce_plugin_distortionAudioProcessorEditor (*this, _parameters);
      }
      
      ▲Processorの実態をEditorに渡す
      class Juce_plugin_distortionAudioProcessorEditor  : public juce::AudioProcessorEditor,
          private juce::Timer
      {
      
      ・・・略・・・
      
      private:
      
      ・・・略・・・
      
          std::unique_ptr<juce::AudioProcessorValueTreeState::SliderAttachment> _inputVolumeSliderAttachment;
          std::unique_ptr<juce::AudioProcessorValueTreeState::SliderAttachment> _gainSliderAttachment;
          std::unique_ptr<juce::AudioProcessorValueTreeState::SliderAttachment> _outputVolumeSliderAttachment;
          std::unique_ptr<juce::AudioProcessorValueTreeState::ButtonAttachment> _specialToggleAttachment;
      
      
      ・・・略・・・
      
          // タイマーによる変更監視:Processor->Editor
          void timerCallback() override;
      
          JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Juce_plugin_distortionAudioProcessorEditor)
      };
      
      ▲Processorの実態受け取りとAttachment変数の定義
      Juce_plugin_distortionAudioProcessorEditor::Juce_plugin_distortionAudioProcessorEditor (Juce_plugin_distortionAudioProcessor& p, juce::AudioProcessorValueTreeState& vts)
          : AudioProcessorEditor (&p), audioProcessor (p), valueTreeState (vts)
      {
      
      ・・・略・・・
      
          // linking ui components and parameters.
          _inputVolumeSliderAttachment.reset(new juce::AudioProcessorValueTreeState::SliderAttachment(
              valueTreeState,
              audioProcessor.getParameterID(Juce_plugin_distortionAudioProcessor::Parameters::InputVolume),
              _inputVolumeSlider));
          _gainSliderAttachment .reset(new juce::AudioProcessorValueTreeState::SliderAttachment(
              valueTreeState,
              audioProcessor.getParameterID(Juce_plugin_distortionAudioProcessor::Parameters::Gain),
              _gainSlider));
          _outputVolumeSliderAttachment.reset(new juce::AudioProcessorValueTreeState::SliderAttachment(
              valueTreeState,
              audioProcessor.getParameterID(Juce_plugin_distortionAudioProcessor::Parameters::OutputVolume),
              _outputVolumeSlider));
          _specialToggleAttachment.reset(new juce::AudioProcessorValueTreeState::ButtonAttachment(
              valueTreeState,
              audioProcessor.getParameterID(Juce_plugin_distortionAudioProcessor::Parameters::Special),
              _specialToggle));
      
      ・・・略・・・
      
          // start timer monitoring.
          startTimer(30);
      }
      
      ・・・略・・・
      
      void Juce_plugin_distortionAudioProcessorEditor::timerCallback()
      {
          _inputVolumeSlider
              .setValue(processor.getParameter(Juce_plugin_distortionAudioProcessor::InputVolume), juce::dontSendNotification);
          _gainSlider
              .setValue(processor.getParameter(Juce_plugin_distortionAudioProcessor::Gain), juce::dontSendNotification);
          _outputVolumeSlider
              .setValue(processor.getParameter(Juce_plugin_distortionAudioProcessor::OutputVolume), juce::dontSendNotification);
          _specialToggle
              .setToggleState(processor.getParameter(Juce_plugin_distortionAudioProcessor::Special) == 1.0f, juce::dontSendNotification);
      
          _inputVolumeLabel
              .setText(processor.getParameterText(Juce_plugin_distortionAudioProcessor::InputVolume), juce::dontSendNotification);
          _gainLabel
              .setText(processor.getParameterText(Juce_plugin_distortionAudioProcessor::Gain), juce::dontSendNotification);
          _outputVolumeLabel
              .setText(processor.getParameterText(Juce_plugin_distortionAudioProcessor::OutputVolume), juce::dontSendNotification);
      }
      
      UIとパラメータの紐付け
      エレキベア
      エレキベア
      Editor側からProcessor側に依存する構成クマね

      歪みの実装

      マイケル
      マイケル
      そしてメインとなる歪みの実装をしてみます。 波形に対する処理はProcessor側のprocessBlock関数に記述します。 通常の歪みはシンプルに波形をしきい値でクリッピングするという方法で実装してみました。
      20240322_01_juce_distortion_06
      ▲しきい値によるクリッピング

      void Juce_plugin_distortionAudioProcessor::processBlock (juce::AudioBuffer<float>& buffer, juce::MidiBuffer& midiMessages)
      {
          // get IN/OUT chennels.
          auto totalNumInputChannels  = getTotalNumInputChannels();
          auto totalNumOutputChannels = getTotalNumOutputChannels();
      
          // clear buffer.
          for (auto i = totalNumInputChannels; i < totalNumOutputChannels; ++i)
              buffer.clear (i, 0, buffer.getNumSamples());
      
          // check bypass.
          if (getParameter(MasterBypass) == 1.0f)
          {
              return;
          }
      
          // apply input volume.
          buffer.applyGain(pow(getParameter(InputVolume), 2));
      
          // gain to threshold.
          auto gainDecibel = juce::Decibels::gainToDecibels(pow(getParameter(Gain), 2));
          auto threshold = juce::Decibels::decibelsToGain(gainDecibel * -1.0);
      
          // apply distortion.
          for (auto channel = 0; channel < totalNumInputChannels; ++channel)
          {
              auto* channelData = buffer.getWritePointer (channel);
              for (auto buffNum = 0; buffNum < buffer.getNumSamples(); buffNum++)
              {
      
      ・・・略・・・
      
                  // clipping by threshold.
                  if (channelData[buffNum] >= threshold)
                  {
                      channelData[buffNum] = threshold;
                  }
                  else if (channelData[buffNum] <= -threshold)
                  {
                      channelData[buffNum] = -threshold;
                  }
                  channelData[buffNum] *= (1 / threshold);
              }
          }
      
          // apply output volume.
          buffer.applyGain(pow(getParameter(OutputVolume), 2) * 2.0f);
      }
      
      ▲しきい値によるクリッピング(実装)
      エレキベア
      エレキベア
      歪みは波形をクリッピングすることで歪ませることが出来るのクマね 短形波に近づけるようなイメージクマか・・・
      マイケル
      マイケル
      そしてSpecialフラグON時の歪みは下記のような独自の関数を用いてクリッピングしてみました。 -1~1の間を見ると緩やかに変化していることが分かります。 このように独自の関数を用いて歪ませる、といった処理もよく見られるようです。
      20240322_01_juce_distortion_02
      ▲独自関数によるクリッピング

      void Juce_plugin_distortionAudioProcessor::processBlock (juce::AudioBuffer<float>& buffer, juce::MidiBuffer& midiMessages)
      {
      
      ・・・略・・・
      
          // apply distortion.
          for (auto channel = 0; channel < totalNumInputChannels; ++channel)
          {
              auto* channelData = buffer.getWritePointer (channel);
              for (auto buffNum = 0; buffNum < buffer.getNumSamples(); buffNum++)
              {
                  // special effect.
                  if (getParameter(Special) == 1.0f)
                  {
                      channelData[buffNum] *= (gainDecibel/2.0);
                      channelData[buffNum] = tanh(5.0 * channelData[buffNum] / 2);
                      continue;
                  }
      
      ・・・略・・・
      
              }
          }
      
      ・・・略・・・
      
      }
      
      ▲独自関数によるクリッピング(実装)
      エレキベア
      エレキベア
      音に正解がないのがまた難しいところクマね・・・
      マイケル
      マイケル
      なお、関数の確認には下記のようなサイトが役に立ちます! 可視化できるので、いろいろな式を確認するだけでも面白いですね!

      desmos calculator

      パラメータ値の保存

      マイケル
      マイケル
      最後に、パラメータ値の保存/読込を行うには、getStateInformation関数に処理を記述します。 XML形式で保存しているようですが、この辺りは定型文として書いておけばいいかなと思います!
      void Juce_plugin_distortionAudioProcessor::getStateInformation (juce::MemoryBlock& destData)
      {
          // load parameter values.
          auto state = _parameters.copyState();
          std::unique_ptr<juce::XmlElement> xml(state.createXml());
          copyXmlToBinary(*xml, destData);
      }
      
      void Juce_plugin_distortionAudioProcessor::setStateInformation (const void* data, int sizeInBytes)
      {
          // save parameter values.
          std::unique_ptr<juce::XmlElement> xmlState(getXmlFromBinary(data, sizeInBytes));
      
          if (xmlState.get() != nullptr)
              if (xmlState->hasTagName(_parameters.state.getType()))
                  _parameters.replaceState(juce::ValueTree::fromXml(*xmlState));
      }
      
      ▲パラメータ値の保存
      エレキベア
      エレキベア
      これでプラグイン作成の完了クマ〜〜〜

      動作確認

      マイケル
      マイケル
      では改めて作ったプラグインをDAWソフト上で読み込んで確認してみましょう!
      マイケル
      マイケル
      しきい値でクリッピングしたのに加えて、独自関数でクリッピングした歪みはより強くハイゲイン風のサウンドになっていますね! いい音とまではいかないですが、調整することでより自分の好みの音に近づけていけそうです!
      エレキベア
      エレキベア
      うへ〜〜〜中々面白いクマね

      おわりに

      マイケル
      マイケル
      というわけで今回はディストーションプラグインの開発に挑戦してみました! どうだったかな??
      エレキベア
      エレキベア
      プラグインの内部で行っている処理がイメージできて面白かったクマ〜〜〜
      マイケル
      マイケル
      今回の記事でサウンドプラグラミングに興味を持った方は、下記のような書籍もおすすめです! 自分もまだ読んでいる途中ですが、より詳しく音作りのプログラミングについて言及されています!

      Pythonではじめる 音のプログラミング: コンピュータミュージックの信号処理

      エレキベア
      エレキベア
      音の世界も奥が深いクマ・・・
      マイケル
      マイケル
      しかし改めて既存のプラグインを聞いてみると、その完成度に驚きますね・・・ だけど自作プラグインだと波形レベルで自分好みにいじれるし、何よりオリジナルだとかなり愛着が湧きました。
      マイケル
      マイケル
      その内、シンセサイザあたりにも挑戦してみたいなと思っています! それでは今日はこの辺で!! アデューー!!
      エレキベア
      エレキベア
      クマ〜〜〜〜〜

      【JUCE】DTMプラグインを作ってみる 〜ディストーション編〜【VST/AU】〜完〜


      サウンドC++DTMJUCE
      2024-03-22

      関連記事
      【UE5】第三回 ミニゲーム制作で学ぶUnrealC++ 〜UI・仕上げ実装 編〜
      2024-05-18
      【UE5】第二回 ミニゲーム制作で学ぶUnrealC++ 〜キャラクター・ゲーム実装 編〜
      2024-05-18
      【UE5】第一回 ミニゲーム制作で学ぶUnrealC++ 〜UnrealC++の概要 編〜
      2024-05-18
      【ゲーム数学】第九回 p5.jsで学ぶゲーム数学「フーリエ解析」
      2024-05-12
      【Unity】第二回 Wwiseを使用したサウンド制御 〜インタラクティブミュージック編〜
      2024-03-30
      【Unity】第一回 Wwiseを使用したサウンド制御 〜基本動作編〜
      2024-03-30
      【Unity】第二回 CRI ADXを使用したサウンド制御 〜インタラクティブミュージック編〜
      2024-03-28
      【Unity】第一回 CRI ADXを使用したサウンド制御 〜基本動作、周波数解析編〜
      2024-03-28