A very flexible function which allows to tranpose octaves and semitones seperately can be found in the sequencer application (seq_event.inc, SEQ_EVNT_Transpose) – but it’s very compact and maybe too complicated to understand the basics.
Normaly, all what you have to do is to add the transpose value (which could also be negative) to the second byte of a Note event. And you need to write a short button handler which allows you to change this transpose value. And maybe also a routine which displays the value on screen.