LoginSignup
8
9

More than 5 years have passed since last update.

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

Posted at

すこし新しい方です

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();       
    }
}
8
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
9