MinGW – wxWidgets – MIDIPanel - 2

LastUpdate 2005.09.04 Yoshiaki Ueda
[目次へ戻る]

 さあ、キーボードを作ろう。(yfMIDIKeyboardPanel)

■サイザーを駆使してキーボード配列をつくる


yfMIDIKeyboardPanel::yfMIDIKeyboardPanel(yfMIDIOutDevice& outdevice,
        wxWindow* parent, wxWindowID id)
    : wxPanel(parent, id), m_outdevice(outdevice)
{
    wxBoxSizer* topsizer = new wxBoxSizer(wxVERTICAL);

    CreateKeyPanel(topsizer);
    CreateSubPanel(topsizer);

    SetSizer(topsizer);
    topsizer->SetSizeHints(this);   
}

void yfMIDIKeyboardPanel::CreateKeyPanel(wxSizer* sizer)
{
    m_tone.SetIndexC3(IDC_C3_BUTTON);
   
    wxStaticBox* staticbox = new wxStaticBox(this, -1, wxT(""));
    wxStaticBoxSizer* frame = new wxStaticBoxSizer(staticbox, wxVERTICAL);

    wxBoxSizer* black = new wxBoxSizer(wxHORIZONTAL);
    wxBoxSizer* white = new wxBoxSizer(wxHORIZONTAL);

    CreateHideButton(REDUCE_HALF, black);
    CreateToneButton(IDC_C2_BUTTON, white);
    CreateToneButton(IDC_CS2_BUTTON, black);
    CreateToneButton(IDC_D2_BUTTON, white);
    CreateToneButton(IDC_DS2_BUTTON, black);
    CreateToneButton(IDC_E2_BUTTON, white);

    //途中省略

    frame->Add(black,1,wxALIGN_CENTRE|wxALL|wxEXPAND);
    frame->Add(white,1,wxALIGN_CENTRE|wxALL|wxEXPAND);

    sizer->Add(frame,1,wxALIGN_CENTRE|wxALL|wxEXPAND,2);
}

void yfMIDIKeyboardPanel::CreateToneButton(UINT id, wxSizer* sizer)
{
    yfDoubleEventButton* button = new yfDoubleEventButton(this, id,
                    m_tone.GetCaptureFromIndex(id));
    button->SetSizeHints(26,-1);
    button->SetDownEvent(EVT_TONE_BUTTON_DOWN);
    button->SetUpEvent(EVT_TONE_BUTTON_UP);
    sizer->Add(button,2,wxALIGN_CENTRE_VERTICAL|wxALL,2);
}

void yfMIDIKeyboardPanel::CreateHideButton(UINT reduce, wxSizer* sizer)
{
    wxWindow* window = new wxWindow(this, -1);
    window->SetSizeHints(26/reduce,-1);
    sizer->Add(window,2/reduce,wxALIGN_CENTRE_VERTICAL|wxALL,2/reduce);
}

●縦方向のサイザーにAddするときはwxEXPANDを指定しないと、横方向に伸縮しない。

■いくつものボタンのクリックを受ける


    EVT_COMMAND_RANGE(IDC_C2_BUTTON, IDC_B4_BUTTON,
            EVT_TONE_BUTTON_DOWN, yfMIDIKeyboardPanel::OnToneButtonDown)

■MIDIのノートナンバーを便利に扱えるクラスを作る


class yfMIDINoteNumber
{
    int m_octave;
    unsigned m_index;
public:
    const static unsigned NOTE_CENTER    = 60;
    const static unsigned OCTAVE_CENTER    = 3;
    yfMIDINoteNumber();
    static wxString GetCapture(unsigned tone);
    static wxString GetNoteCapture(unsigned tone);
    static wxString GetOctaveCapture(unsigned tone);
    static int ToOctave(unsigned tone);
    static unsigned ToNoteOffset(unsigned tone);
    //octave
    void SetOctaveShift(int octave);
    int GetOctaveShift(void) const;
    unsigned OctaveShift(unsigned tone) const;
    //index
    void SetIndexC3(unsigned index);
    unsigned GetIndexC3(unsigned index) const;
    unsigned GetNoteNumber(unsigned index) const;
    wxString GetCaptureFromIndex(unsigned index) const;
};
yfMIDINoteNumber::yfMIDINoteNumber()
    : m_octave(0), m_index(NOTE_CENTER)
{
}

wxString yfMIDINoteNumber::GetCapture(unsigned tone)
{
    return GetNoteCapture(tone) + GetOctaveCapture(tone);
}

wxString yfMIDINoteNumber::GetNoteCapture(unsigned tone)
{
    const static wxString capture[] = {
        wxT("C"), wxT("C#"), wxT("D"), wxT("D#"), wxT("E"),
        wxT("F"), wxT("F#"), wxT("G"), wxT("G#"), wxT("A"), wxT("A#"), wxT("B")
        };
    return capture[ToNoteOffset(tone)];
}

wxString yfMIDINoteNumber::GetOctaveCapture(unsigned tone)
{
    return wxString::Format(wxT("%d"), ToOctave(tone));
}

int yfMIDINoteNumber::ToOctave(unsigned tone)
{
    if(tone>127) tone = 127;
    return tone / 12 - 2;
}

unsigned yfMIDINoteNumber::ToNoteOffset(unsigned tone)
{
    return tone % 12;
}

//octave
void yfMIDINoteNumber::SetOctaveShift(int octave)
{
    m_octave = octave;
}

int yfMIDINoteNumber::GetOctaveShift(void) const
{
    return m_octave;
}

unsigned yfMIDINoteNumber::OctaveShift(unsigned tone) const
{
    int value;
    if(tone>127)
        value = 127;
    else
        value = tone;
    value += m_octave*12;
    if(value<0) value = 0;
    if(value>127) value = 127;
    return value;
}

//index
void yfMIDINoteNumber::SetIndexC3(unsigned index)
{
    m_index = index;
}

unsigned yfMIDINoteNumber::GetIndexC3(unsigned index) const
{
    return m_index;
}

unsigned yfMIDINoteNumber::GetNoteNumber(unsigned index) const
{
    long value = (long)index - (long)m_index + NOTE_CENTER;
    if(value<0) value = 0;
    if(value>127) value = 127;
    return OctaveShift(value);
}

wxString yfMIDINoteNumber::GetCaptureFromIndex(unsigned index) const
{
    return GetCapture(GetNoteNumber(index));
}

■サブパネルも配置する


void yfMIDIKeyboardPanel::CreateSubPanel(wxSizer* sizer)
{
    wxBoxSizer* frame = new wxBoxSizer(wxHORIZONTAL);

    m_velocitySlider  = new yfSlideValue(this, IDC_SLIDER_VELOCITY,
            wxT("Velocity"), 127, 0, 127);
    m_velocitySlider->GetSlider()->SetPageSize(16);
    frame->Add(m_velocitySlider,3,wxALIGN_CENTRE_VERTICAL);

    m_offVelocitySlider = new yfSlideValue(this, IDC_SLIDER_OFF_VELOCITY, wxT("Off velocity"), 127, 0, 127);
    m_offVelocitySlider->GetSlider()->SetPageSize(16);
    frame->Add(m_offVelocitySlider,2,wxALIGN_CENTRE_VERTICAL);

    m_octaveSlider = new yfSlideValue(this, IDC_SLIDER_OCTAVE, wxT("Octave"), 3, 0, 7);
    m_octaveSlider->GetSlider()->SetPageSize(1);
    frame->Add(m_octaveSlider,2,wxALIGN_CENTRE_VERTICAL);

    wxBoxSizer* subframe = new wxBoxSizer(wxVERTICAL);

    //channel
    wxStaticText* statcText = new wxStaticText(this, -1, wxT("Channel"));
    subframe->Add(statcText,1,wxALIGN_CENTRE_VERTICAL|wxEXPAND|wxALL,10);
   
    m_channelBox = new wxComboBox(this, -1, wxT("Channel"));
    for(int i=1; i<=16; i++){
        m_channelBox->Append( wxString::Format(wxT("%d"),i) );
    }
    m_channelBox->SetSelection(0);
    subframe->Add(m_channelBox,1,wxEXPAND|wxALL,10);
   
    frame->Add(subframe,1,wxALIGN_CENTRE_VERTICAL|wxEXPAND);

    sizer->Add(frame,1,wxALIGN_CENTRE|wxEXPAND);
}

■MIDIデータを生成する便利なクラスを作る(yfMIDIDataMaker)


class yfMIDIDataMaker
{
public:
    static DWORD NoteOn(int channel, int note, int velocity);
    static DWORD NoteOff(int channel, int note, int velocity);
};
inline static void Limit(int& value, int min, int max)
{
    if(value<min) value = min;
    if(value>max) value = max;
}

inline static DWORD ShortMsg(BYTE status, BYTE data1, BYTE data2)
{
    return MAKELONG(MAKEWORD(status,data1),MAKEWORD(data2,0));
}

inline static DWORD ShortMsg(BYTE data1, BYTE data2)
{
    return MAKELONG(MAKEWORD(data1,data2),MAKEWORD(0,0));
}

DWORD yfMIDIDataMaker::NoteOn(int channel, int note, int velocity)
{
    Limit(channel,1,16);
    Limit(note,0,127);
    Limit(velocity,0,127);
    return ShortMsg(0x90|channel-1, note, velocity);
}

DWORD yfMIDIDataMaker::NoteOff(int channel, int note, int velocity)
{
    Limit(channel,1,16);
    Limit(note,0,127);
    Limit(velocity,0,127);
    return ShortMsg(0x80|channel-1, note, velocity);
}

■トーンボタンのイベントを拾いMIDIメッセージを出す


void yfMIDIKeyboardPanel::OnToneButtonDown(wxCommandEvent& event)
{
    try{
        m_note.SetOctave(GetOctave()-yfMIDINoteNumber::OCTAVE_CENTER);
        unsigned noteNumber = m_note.GetNoteNumber(event.GetId());
        m_outdevice.OutShortMsg(
            yfMIDIDataMaker::NoteOn(GetChannel(), noteNumber, GetVelocity()) );
    }catch(...){
        ::wxMessageBox(wxT("OutShortMsg - error ???"));
    };
}

 例外を補足出来るようにしているが、これはうまくいっていない。この原因追求はまた別の機会に。
 これでとりあえず、音楽キーボードまがいのボタンを押すと、MIDIから音が出る。ベロシティーも変化する。