Click here to Skip to main content
15,921,279 members
Articles / Desktop Programming / Win32

A Flexible Direct2D Pianoroll for Your Music Apps

Rate me:
Please Sign up or sign in to vote.
5.00/5 (11 votes)
9 May 2019CPOL5 min read 23.5K   8   4
Create music easily

Image 1


This is one of my oldest projects, now available as an open source. A piano roll is an easy way to compose music without knowledge of musical score elements.


  • Notes support channel (0-15), velocity (0-127) , layer (unlimited)
  • Moving, Resizing with snap controls
  • Supports diatonic movement through specified Key and Mode (Major/Minor)
  • Unlimited undo/redo
  • Unlimited layers
  • Piano side (Left/Right/Bottom/None)
  • Callbacks
  • Tools - Auto, Eraser, Single click entry, Quantizer
  • Serialization/Deserialization to XML
  • Key per measure
  • Tempo per measure
  • Time signature per measure
  • MIDI export
  • Partial MIDI import (in progress)
  • Non-note MIDI notes
  • Meta events (Raw hex and specific items)
  • Aftertouch events
  • Pitch shift events
  • Note quantization
  • Markers
  • Long text show
  • Chromatic/Diatonic transposes
  • Record from MIDI in device
  • Streaming callback
  • Configurable colors
  • Part mode


The sample project demonstrates:

  • The control
  • Save/Load XML
  • Save MIDI
  • Play at run time through midi Out
  • Record from midi In

You can also check my roll example in VSTX to see a tool that composes music through VST instruments, using this pianoroll.

Using Pianoroll

All you need is to include "pianoroll.hpp" to your project, then:

// Instantiate

    case WM_KEYDOWN:



        virtual HRESULT NoteAdded(PIANOROLL* pr, NOTE*) = 0;
        virtual HRESULT NoteRemoved(PIANOROLL* pr, NOTE*) = 0;
        virtual void RedrawRequest(PIANOROLL* pr, unsigned long long param) = 0;
        virtual HRESULT OnNoteChange(PIANOROLL* pr, NOTE* oldn, NOTE* newn) = 0;
        virtual HRESULT OnNoteSelect(PIANOROLL* pr, NOTE* oldn, bool) = 0;
        virtual void OnPianoOn(PIANOROLL*, int n) = 0;
        virtual void OnPianoOff(PIANOROLL*, int off) = 0;


The mandatory callback is the RedrawRequest. When this function is called, call PIANOROLL::Paint(), passing your ID2D1RenderTarget so the control redraws itself.

Measures, Keys and Times

Key setting is used to allow the control to move notes within a specific scale (if you hold Shift while moving, notes are moved chromatically instead). For example, if the current key is D major and you press the minus key on a D note, this moves to C#.

Times allows a measure to have a different number of beats (default 4).

Keys and beats are a per measure setting. The interesting thing in the Key is the scale creation function which uses the known MMmMMMm or MmMMm3m formats for major/minor to create the scale based on the key:

void CreateScale()
        unsigned int fi = 0x48; // C
        if (k > 0)
            fi = (7 * k) % 12;

        if (m == 1)
            fi -= 3;

        fi = fi % 12;
        if (m == 1)
            Scale.push_back(fi + 2);
            Scale.push_back(fi + 3);
            Scale.push_back(fi + 5);
            Scale.push_back(fi + 7);
            Scale.push_back(fi + 8);
            Scale.push_back(fi + 11);
            if (m == 0)
                Scale.push_back(fi + 2);
                Scale.push_back(fi + 4);
                Scale.push_back(fi + 5);
                Scale.push_back(fi + 7);
                Scale.push_back(fi + 9);
                Scale.push_back(fi + 11);

        for (auto& e : Scale)
            e = e % 12;


Notes can belong to a certain layer (the default is layer 1). When working with layers, notes that belong to a layer different than the current layer cannot be modified except by the command that changes layers.


Notes have configurable velocity, channel and layer. Notes have a starting position (measure + beat) and a duration:

class NOTE

        int midi = 0;
        int Selected = 0;
        POSITION p;
        FRACTION d;
        int vel = 127;
        int ch = 0;
        int layer = 0;

In addition, notes can contain a non note event, like an instrument patch, pitch bend, sysex event or controller message. When using events which carry no note information, you can put them anywhere (use control+ double click).



The control creates a simple piano that allows you to test notes. The piano can be on side (left/right) or at the bottom. The control also provides a piano-only drawing (this is used in my VSTX library for VST instruments testing).

Drawing Technology

The control uses your own Direct2D rendering target, so you can draw it wherever you like (as a HWND or even as part of an image for a screenshot or in a printer HDC).

Learn more about Direct2D in my older article.

The control will notify you when it needs redrawing.

Zooming, Moving, Snapping and Scrolling

The control provides unlimited moving between measures and unlimited zoom in or zoom out. The control also allows note moving to be snapped to beats. This is configurable to allow denominations of the beat in order to move more precisely. If you want non snapping while moving, move the notes while holding Shift. You can scroll using the arrow keys or the top scroll bar.


The control can output a vector of streamable notes (which have absolute time formats) for your midi sequencer.


All internal manipulations of the library are done with fractions, so no information is lost on floating point operations.

        ssize_t n = 0;
        ssize_t d = 1;
     ... operators to multiply, add, compare etc


Chromatic transposing is easy, we add or remove 1 midi note to a note. Diatonic transposition is more complex, we have to take the current key and mode into accout and then check if the note belongs to the new scale.



The control uses my XML library to serialize. You can then reload the control as needed.


A simple MIDI class is provided with the pianoroll that allows to write MIDI file data. MIDI writes have a variable length format:

void WriteVarLen(long value, vector<unsigned char>& b)
            unsigned long long buffer = value & 0x7f;
            while ((value >>= 7) > 0)
                buffer <<= 8;
                buffer |= 0x80;
                buffer += (value & 0x7f);

            for (;;)
                if (buffer & 0x80)
                    buffer >>= 8;

The pianoroll creates a multiple-track MIDI file (each layer becomes a track). Keys, text, markers, time changes and tempo changes are included in the file.



  • A : auto tool
  • E : eraser tool
  • I : single entry tool
  • Q : quantize tool
  • 1,2,3,4 (up row) : Select beat duration for next note
  • Shift+1,2,3,4 (up row) : Beat duration 1/8, 1/16, 1/32, 1/64
  • < and > : Change selected items velocity
  • Ctrl+ < and > : Velocity off/full
  • < and > when pitch shift: Up/Down pitch shift (combine with Ctrl/Shift/Alt)
  • +/- : Change selected items position diatonically
  • Shift +/- : Change selected items position chromatically
  • Numpad +/-/* : Zoom in,out,all
  • Shift + Arrows up/down : Change channel
  • Alt + Arrows up/down : Change layer
  • / and \ : Enlarge/reduce notes
  • D,H,': Double/Half/+1/2 notes
  • Ctrl+Q : Quantize notes
  • Ctrl+G : Go to measure
  • J : Join notes
  • Ctrl+1...6 : Snapping resolution
  • Alt+1-9 Numpad : Toggle layers
  • 1-9 Numpad : Next layer
  • Ctrl+A : Select all
  • Ctrl+C : Copy
  • Ctrl+T : Diatonic transpose
  • Ctrl+Shift+T : Chromatic transpose
  • Ctrl+V : Paste to last clicked measure
  • Ctrl+Z : Undo
  • Ctrl+Y : Redo
  • [,] : Next/Prev marker
  • Right/Left arrow : move roll
  • Del : Delete selected notes
  • Ctrl+Home : Scroll to start
  • X,Z : Move left,right
  • P : Toggle part mode
  • Alt+P : Next part


  • Dblclick : insert note (auto tool)
  • Control + Dblclick : insert non note event
  • Control + Shift + Dblclick : insert aftertouch
  • Dblclick on note : remove note
  • Click on note : select/unselect (quantize in quantize tool)
  • Drag outside note: select (insert in single entry tool, delete in eraser tool, quantize in quantize tool)
  • Drag/Resize note


  • 9/5/2019: More controls, quantization 
  • 5/5/2019: keyboard shortcuts, markers, pitch bend, after touch, patch and other new features.
  • 1/5/2019: First release


This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Written By
Software Developer
Greece Greece
I'm working in C++, PHP , Java, Windows, iOS, Android and Web (HTML/Javascript/CSS).

I 've a PhD in Digital Signal Processing and Artificial Intelligence and I specialize in Pro Audio and AI applications.

My home page:

Comments and Discussions

Questionhey this is really cool Pin
honey the codewitch1-May-19 4:03
mvahoney the codewitch1-May-19 4:03 
AnswerRe: hey this is really cool Pin
Michael Chourdakis1-May-19 4:09
mvaMichael Chourdakis1-May-19 4:09 
QuestionGreat !!! Pin
Serge Desmedt30-Apr-19 23:26
professionalSerge Desmedt30-Apr-19 23:26 
This is a great project.

It just so happens I've seen this video on youtube displaying Phillip Glass's Koyaanisqatsi displayed in a similar fashion.

It's makes the music seem like an interplay of mathematical series, which made me think it would be nice to have a program which converts mathematical series to key-strokes on a piano/organ.

I suppose your program would be a perfect fit as the basis.
AnswerRe: Great !!! Pin
Michael Chourdakis1-May-19 4:13
mvaMichael Chourdakis1-May-19 4:13 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.