JUCE学习笔记08-合成器(一)振荡器与包络发生器

本文介绍如何使用JUCE创建合成器,包括从SynthesiserSound和SynthesiserVoice类派生子类,实现声音播放判断及音频流生成。通过引入Maximilian扩展,实现振荡器声音生成和包络控制,详细解析了振荡器和包络发生器模块的实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

JUCE学习笔记08——合成器(一)振荡器与包络发生器

知识点:

1、合成器类
2、扩展库

目标:

利用外部扩展的码实现合成器基本模块中的振荡器与包络发生器

内容:

一、创建合成器类:分别从SynthesiserSound和SynthesiserVoice类派生子类,并实现父类的纯虚函数
 //SynthSound.h  决定合成器是否可以播放给定的音符号和midi通道号的声音,如果可以播放则返回true,简单理解为该midi音符是否可播放
 class SynthSound : public SynthesiserSound
{
public:
	bool appliesToNote(int /*midiNoteNumber*/)
	{
         return true;//决定传入的音符号是否可播放
	}

	bool appliesToChannel(int /*midiChannel*/)
	{
         return true;//决定该通道号的音符是否可播放
	}
     //...
 };
 //SynthVoice.h  表示用来播放SynthesiserSound的一个声部(触发声音的载体),简单理解为生成音频流的组件,每个Voice播放一个Sound,代表合成器的复音数
 class SynthVoice : public SynthesiserVoice
{
public:
	bool canPlaySound(SynthesiserSound* sound)
	{
         return dynamic_cast<SynthSound*>(sound) != nullptr;检测并动态转换传入的声音类型,转换成功返回true
	}

	void startNote(int midiNoteNumber, float velocity, SynthesiserSound* sound, int currentPitchWheelPosition)
	{
         frequency = MidiMessage::getMidiNoteInHertz(midiNoteNumber);//音符输入时获取输入的midi信息中的音符号转换为频率,用于设置振荡器的声音频率
	}

	void stopNote(float velocity, bool allowTailOff)
	{
     
	}
 

	void pitchWheelMoved(int pitchWheelValue)
	{
	}

	void controllerMoved(int controllerNumber, int newControllerValue)
	{
	}

	void renderNextBlock(AudioBuffer<float> &outputBuffer, int starSample, int numSample)
	{
     //缓冲区生成音频缓数据,在处理器类的音频块处理processBlock()中调用
	}
 private:
	double frequency;
     //...
 };
二、 音频处理器类中的主要方法
//构造函数
YhySynthDemoAudioProcessor::YhySynthDemoAudioProcessor()
    //...预处理块
{
	mySynth.clearVoices(); //先清理Voice对象
	for (int i = 0; i < 5; ++i)
		mySynth.addVoice(new SynthVoice());  //创建Voice(复音数)
	mySynth.clearSounds();  //清空Sound
	mySynth.addSound(new SynthSound());
}

 //prepareToPlay方法在音频通道设置完成时调用,这里主要对采样率做设置,防止在变更设备或采样率时对输出音高的影响
 void YhySynthDemoAudioProcessor::prepareToPlay (double sampleRate, int samplesPerBlock)
{
	ignoreUnused(samplesPerBlock);
	auto lastSampleRate = sampleRate;
	mySynth.setCurrentPlaybackSampleRate(lastSampleRate);
}
 //缓存块处理
 void YhySynthDemoAudioProcessor::processBlock (AudioBuffer<float>& buffer, MidiBuffer& midiMessages)
{
	buffer.clear();
	mySynth.renderNextBlock(buffer, midiMessages, 0, buffer.getNumSamples());//由合成器Voice中的renderNextBlock函数生成
}
三、振荡器模块——引入Maximilian扩展(下载地址:https://github.com/micknoise/Maximilian),使用其提供的基本波形振荡器实现声音的生成
1.Juce工程的导入页设置头文件搜索路径

在这里插入图片描述

2.将扩展的源码文件/目录拖拽至JUCE的文件浏览器(可将没必要的文件删除,仅留用到的部分)

在这里插入图片描述

3.IDE中打开

在这里插入图片描述

4.引入maximillian头文件
//SynthVoice.h
#pragma once
#include "../JuceLibraryCode/JuceHeader.h"
#include "SynthSound.h"
#include "../extras/Maximilian/maximilian.h" 
5.创建maxi振荡器对象以及存放midi输入时的力度信息
maxiOsc myOsc1;
 double level;

void startNote(int midiNoteNumber, float velocity, SynthesiserSound* sound, int currentPitchWheelPosition)
	{
		frequency = MidiMessage::getMidiNoteInHertz(midiNoteNumber);
		level = velocity; //获取力度信息
	}
6.生成缓冲区填充数据
void renderNextBlock(AudioBuffer<float> &outputBuffer, int startSample, int numSample)
	{
		for (auto sample=0;sample<numSample;++sample)//将maxiOsc对象生成的音频数据逐样本地填充各个通道的到缓冲区
		{
			auto myWave = myOsc1.square(frequency)*level;//获取的力度信息用于控制声音的大小(midi的力度响应)
			for (auto channel = 0; channel < outputBuffer.getNumChannels(); ++channel)
			{
				outputBuffer.addSample(channel, startSample, myWave);
			}
			++startSample;
		}
	}
7.设置音符停止方法(音符输入结束),这里只是简单的处理音符结束时将音量设置为0,更多功能由Maximilian中的Envelope类实现
void stopNote(float velocity, bool allowTailOff)
{
	level = 0;
}
四、包络发生器模块(Envelope)—— 控制音符播放过程中的音量
1.合成器中创建包络发生器对象(Maximilian中提供该类)
  maxiEnv myEnv1;
2.设置包络的触发(attack)与释放(release)的节点:通过maxiEnv类的成员变量trigger设置,当trigger=1时触发attack,不为1时触发release
void startNote(int midiNoteNumber, float velocity, SynthesiserSound* sound, int currentPitchWheelPosition)
	{
		frequency = MidiMessage::getMidiNoteInHertz(midiNoteNumber);
		level = velocity;
		myEnv1.trigger = 1;//接收到音符开信息时触发包络启动
	}


void stopNote(float velocity, bool allowTailOff)
	{
		myEnv1.trigger = 0;//接收到音符关信息时触发包络释放(值为非1时)
	}
3.设置包络发生器的ADSR值并应用到缓存区中的信号
void renderNextBlock(AudioBuffer<float> &outputBuffer, int startSample, int numSample)
	{
		myEnv1.setAttack(500);   //A
		myEnv1.setDecay(100);    //D
		myEnv1.setSustain(0.8);  //S
		myEnv1.setRelease(500);  //R
		for (auto sample=0;sample<numSample;++sample)
		{
			auto myWave = myOsc1.sinewave(frequency)*level;
			auto soundOfEnv= myEnv1.adsr(myWave, myEnv1.trigger);//对振荡器的音频信号应用包络
			for (auto channel = 0; channel < outputBuffer.getNumChannels(); ++channel)
			{
				outputBuffer.addSample(channel, startSample, soundOfEnv);//包络处理后的音频信号传入缓存中
			}
			++startSample;
		}
	}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值