音階が見えるビジュアライザー３

すこし新しい方です

```Siv3D:Main.cpp
//はむくん ビジュアライザー 試作
# include <Siv3D.hpp>

void createGrad(Image& image)
{
	const Vec2 center = Vec2(image.width, image.height) / 2;

	const double maxLength = center.x;

	Color col;

	for (int y = 0; y < image.height; ++y)
	{
		for (int x = 0; x < image.width; ++x)
		{
			const Vec2 pos(x, y);

			const double length = (center - pos).length();

			const double value = length / maxLength;

			col = ColorF(1.0 - Pow(value, 2.0));

			image[y][x] = col;
		}
	}
}

class SoundPlayer
{
private:

	GUI m_guiBasic;

	GUI m_guiSettings;

	Sound m_sound;

	Texture m_gradTexture;

	Texture m_backTexture;

	const Font m_font;

	const Vec2 m_centerPos;

	static const double m_freq[];

	static const bool m_isWhite[];

	static const String m_name[];

	bool m_isRecording;

	bool m_isShowingSetting;

	int m_hideSettingCount;
public:

	SoundPlayer()
		: m_guiBasic{ GUISkin::Default({ 0, 0, 0 }) }, m_guiSettings(m_guiBasic.skin), 
		m_font(10), m_centerPos(320, 190), m_isRecording(false), m_isShowingSetting(false)
	{
		Image image(10, 10);

		createGrad(image);

		m_gradTexture = Texture(image);

		m_guiBasic.addSlider(L"Slider", { 0.0, 1.0, 0.0, 500 });

		m_guiBasic.addNewLine();

		m_guiBasic.addButton(L"PlayButton", { L"再生" });

		m_guiBasic.addButton(L"PauseButton", { L"停止" });

		m_guiBasic.addButton(L"OpenButton", { L"ファイルを開く" });

		m_guiBasic.addButton(L"UseMic", { L"マイク" });

		m_guiBasic.setPos((Window::Width() - m_guiBasic.rect.w) / 2, 380);

		m_guiSettings.addText(L"setting",{ L"設定" });

		m_guiSettings.addNewLine();
		
		m_guiSettings.addButton(L"OpenPicture", { L"背景画像" });

		m_guiSettings.addNewLine();

		m_guiSettings.addText({ L"画像明るさ" });

		m_guiSettings.addNewLine();

		m_guiSettings.addSlider(L"Brightness", { 0.0, 1.0});

		m_guiSettings.addNewLine();

		m_guiSettings.addToggleSwitch(L"scale", { true });

		m_guiSettings.addText(L"音階表", { L"音階表" });

		m_guiSettings.addNewLine();

		m_guiSettings.addToggleSwitch(L"CircleOfFifth", { false });

		m_guiSettings.addText(L"godoken", { L"五度圏" });

		m_guiSettings.addNewLine();

		m_guiSettings.addText({ L"光強度" });

		m_guiSettings.addNewLine();

		m_guiSettings.addSlider(L"LightStrength", { 0.0, 1.0, 0.5 });

		m_guiSettings.addNewLine();

		m_guiSettings.addText({ L"色合い" });

		m_guiSettings.addNewLine();

		m_guiSettings.addSlider(L"HSVStart", { 0.0, 1.0 , 0.0});

		m_hideSettingCount = 0;


	}

	void update()
	{
		m_guiBasic.button(L"PlayButton").setEnabled(m_sound && !m_sound.isPlaying);

		m_guiBasic.button(L"PauseButton").setEnabled(m_sound.isPlaying);

		m_guiBasic.button(L"UseMic").setEnabled(!m_isRecording );

		m_guiBasic.slider(L"Slider").setEnabled(!m_isRecording);

		if (m_guiBasic.button(L"PlayButton").pushed)
		{
			m_sound.play();

			m_isRecording = false;

			Recorder::Stop();
		}
		else if (m_guiBasic.button(L"PauseButton").pushed)
		{
			m_sound.pause();

			m_isRecording = false;

			Recorder::Stop();
		}
		else if (m_guiBasic.button(L"OpenButton").pushed)
		{
			m_sound.pause();

			m_isRecording = false;

			Recorder::Stop();

			m_sound = Dialog::OpenSound();
		}

		if (m_guiBasic.slider(L"Slider").hasChanged)
		{
			m_sound.setPosSec(m_sound.lengthSec*m_guiBasic.slider(L"Slider").value);
		}

		m_guiBasic.slider(L"Slider").setValue(m_sound.posSec / m_sound.lengthSec);

		if (m_guiBasic.button(L"UseMic").pushed)
		{
			if (!Recorder::Start()){

			}
			else
			{
				m_isRecording = true;

				m_sound.pause();
			}
		}

		if (m_guiSettings.button(L"OpenPicture").pushed)
		{
			m_backTexture = Dialog::OpenTexture();	
		}

		//設定のGUIを隠したりする処理
		const Rect guiRect = m_guiSettings.rect;

		const Point nowPos(guiRect.pos);

		const Point showPos(0, 0);

		const Point remainPos(10, 0);

		const Point hidePos(remainPos - Point(guiRect.size.x, 0));

		const double mass = 0.9;

		if (m_isShowingSetting)
		{
			if (!guiRect.mouseOver)
			{
				++m_hideSettingCount;
			}
			else
			{
				m_hideSettingCount = 0;
			}
			
			if (m_hideSettingCount > 60)
			{
				m_isShowingSetting = false;
			}
		}
		else
		{
			if (Rect(0,0,remainPos.x, guiRect.size.y).mouseOver)
			{
				m_isShowingSetting = true;
			}
		}

		if (m_isShowingSetting)
		{
			m_guiSettings.setPos(((nowPos - showPos) * mass + showPos).asPoint());
		}
		else
		{
			m_guiSettings.setPos(((nowPos - hidePos) * mass + hidePos).asPoint());
		}

	}

	void drawVisualizer() 
	{		

		if (m_backTexture)
		{
			Vec2 size;

			const double windowRatio = double(Window::Size().x) / double(Window::Size().y);

			const double texRatio = double(m_backTexture.width) / double(m_backTexture.height);

			if (windowRatio > texRatio)
			{
				size = Vec2(Window::Size().x, double(Window::Size().x) / texRatio);
			}
			else
			{
				size = Vec2(double(Window::Size().y) * texRatio, Window::Size().y);
			}

			RectF(size)(m_backTexture).drawAt(Window::Size() / 2,ColorF(m_guiSettings.slider(L"Brightness").value));
		}

		if (m_guiSettings.toggleSwitch(L"scale").isRight)
		{
			drawBackGround();
		}

		if (m_isRecording)
		{
			Waving::FFT(Recorder::GetWave(), Recorder::GetPos());
		}
		else
		{
			if (m_sound.isPlaying)Waving::FFT(m_sound);
		}	

		const float* p = Waving::FFTBuffer();

		for (int i = 0; i<1760; ++i)
		{
			const double di = i;

			const double radian = getRad(di);

			const double radius = 0.0;

			const double radiusOuter = 180.0;

			const double radianWidth = Max((radian)-getRad(di - 1),0.0);

			const double width = Clamp(radiusOuter * (radianWidth), 0.0,50.0);

			const double size = Pow(p[i], 0.6f) * 800 * Log(di)/Log(10.0);

			const double sliderStrength = Pow(10.0,m_guiSettings.slider(L"LightStrength").value * 2.0);

			const double strength = sliderStrength * Pow(size , 2.0) * Log( di ) / 10000000.0;

			const Vec2 pos = getPosFromRad(radian) * (radius + size / 2.0) + m_centerPos;

			const double hsvStart = m_guiSettings.slider(L"HSVStart").value * 255;

			const Color col = HSV(240 - Log(di) * 100 + hsvStart, 1.0, strength);

			const Color colPie = HSV(240 - Log(di) * 100, 1.0, strength);

			RectF(width, size).setCenter(pos).rotate(radian)(m_gradTexture).draw(col);//{ Palette::Black, Palette::Black, col, col });
			//Circle(m_centerPos, size).drawPie(radian - (radianWidth) / 2.0, radianWidth, colPie);

			const Vec2 posOuter = getPosFromRad(radian) * (radiusOuter) + m_centerPos;

			RectF(width * 2, width * 2).setCenter(posOuter).rotate(radian)(m_gradTexture).draw(col);//{ Palette::Black, Palette::Black, col, col });
		}
		
		if (m_isRecording && Recorder::IsEnd()) // 録音が終了
		{
			Recorder::Restart();
		}
		
		Rect(Window::Size())(m_gradTexture).draw(ColorF(0.0, 0.3, 0.3));
		
	}

	void drawBackGround()
	{
		for (int i = 0; i < 12; ++i)
		{
			const double startRad = getRad((m_freq[i] + m_freq[i + 1]) / 2.0) + 0.01;

			const double angle = getRad((m_freq[i + 1] + m_freq[i + 2]) / 2.0) - startRad;

			const Vec2 pos = getPosFromRad(getRad(m_freq[i])) * (160.0) + m_centerPos;

			if (m_isWhite[i])
			{
				//Circle(m_centerPos, 180).drawPie(startRad, angle, Color(50));
			}
			else
			{
				//Circle(m_centerPos, 180).drawPie(startRad, angle, Color(20));
			}

			m_font.drawCenter(m_name[i], pos, Color(70));
		}

		for (int i = 0; i < 5; ++i)
		{
			Circle(m_centerPos, 170 + i * 3).drawFrame(1.0, 0.0, Color(70));
		}
	}

	double getRad(double freq) 
	{
		if (!m_guiSettings.toggleSwitch(L"CircleOfFifth").isRight)
		{
			return 2.0 * Pi * Log(freq) / Log(2.0) + 2.4;
		}
		else
		{
			return getRadCircleOfFifth(freq);
		}
	}

	double getRadCircleOfFifth(double freq)
	{
		const double startRad = -0.7;

		const double rad = 2.0 * Pi * Log(freq) / Log(2.0) + startRad;

		const double baseRad = 2.0 * Pi * Log((m_freq[0] + m_freq[1]) / 2.0) / Log(2.0) + startRad;


		if (int((rad - baseRad) / (2.0 * Pi / 12.0) + 100) % 2 == 0)
		{ 
			return rad;
		}
		else
		{
			return rad + Pi;
		}
	}

	Vec2 getPosFromRad(double rad)
	{
		return Vec2(Sin(rad), -Cos(rad));
	}
};

const double SoundPlayer::m_freq[] = {
	440.000000,
	466.163762,
	493.883301,
	523.251131,
	554.365262,
	587.329536,
	622.253967,
	659.255114,
	698.456463,
	739.988845,
	783.990872,
	830.609395,
	880.000000,
	932.328
};

const bool SoundPlayer::m_isWhite[] = {
	false, true, true, false, true, false, true, false, true, true, false, true
};

const String SoundPlayer::m_name[] = {
	L"D", L"D#", L"E", L"F", L"F#", L"G", L"G#", L"A", L"A#", L"B", L"C", L"C#"
};

void Main()
{
	SoundPlayer soundPlayer;

	Graphics::Set2DSamplerState(SamplerState::Clamp());

	Graphics::Set2DBlendState(BlendState::Additive());

	Graphics::SetBackground({ 0, 0, 0 });

	Window::SetTitle(L"はむビジュアライザー");
		
	while (System::Update())
	{
		soundPlayer.update();

		soundPlayer.drawVisualizer();		
	}
}
```