This program generates sound by using FM tone generator YMF825 via SPI.

Dependencies:   microbit

Files at this revision

API Documentation at this revision

Comitter:
hasebems
Date:
Fri Jan 05 22:58:49 2018 +0000
Commit message:
???????????????

Changed in this revision

fmasgn.c Show annotated file Show diff for this revision Revisions of this file
fmasgn.h Show annotated file Show diff for this revision Revisions of this file
fmif.c Show annotated file Show diff for this revision Revisions of this file
fmif.h Show annotated file Show diff for this revision Revisions of this file
fmnote.c Show annotated file Show diff for this revision Revisions of this file
fmnote.h Show annotated file Show diff for this revision Revisions of this file
fmpart.c Show annotated file Show diff for this revision Revisions of this file
fmpart.h Show annotated file Show diff for this revision Revisions of this file
fmsd1.h Show annotated file Show diff for this revision Revisions of this file
fmsd1_micro.cpp Show annotated file Show diff for this revision Revisions of this file
fmtone.c Show annotated file Show diff for this revision Revisions of this file
fmtone.h Show annotated file Show diff for this revision Revisions of this file
fmtype.h Show annotated file Show diff for this revision Revisions of this file
fmvoice.c Show annotated file Show diff for this revision Revisions of this file
fmvoice.h Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
microbit.lib Show annotated file Show diff for this revision Revisions of this file
diff -r 000000000000 -r c54d59d6fb78 fmasgn.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fmasgn.c	Fri Jan 05 22:58:49 2018 +0000
@@ -0,0 +1,131 @@
+//	fmasgn.c
+#include "fmtype.h"
+#include "fmasgn.h"
+#include "fmvoice.h"
+
+//	Variable
+static Fmvoice	_fmvc[MAX_FM_VOICE];
+static Fmvoice*	_firstEmptyVc;		//	old
+static Fmvoice*	_lastEmptyVc;		//	new
+static Fmvoice*	_firstOccupiedVc;	//	old
+static Fmvoice*	_lastOccupiedVc;	//	new
+
+//	Prototype
+static void	setToEmptyList( Fmvoice* prevVc, Fmvoice* rlsVc );
+
+//	setter
+void Asgn_setFirstEmptyVc( Fmvoice* vc ){ _firstEmptyVc = vc; }
+void Asgn_setLastEmptyVc( Fmvoice* vc ){ _lastEmptyVc = vc; }
+
+//	getter
+Fmvoice* Asgn_voice( int num ){ return &(_fmvc[num]); }
+Fmvoice* Asgn_firstEmptyVc( void ){ return _firstEmptyVc; }
+Fmvoice* Asgn_lastEmptyVc( void ){ return _lastEmptyVc; }
+
+void Asgn_init( void )
+{
+	int		i;
+
+	for ( i=0; i<MAX_FM_VOICE; i++) {
+		Fmvoice_init(&_fmvc[i]);
+	}
+
+	_firstOccupiedVc = 0;
+	_lastOccupiedVc = 0;
+
+	for ( i=0; i<MAX_FM_VOICE-1; i++ ){
+		Fmvoice_setVoiceNum(&_fmvc[i], i);
+		Fmvoice_setNextVc(&_fmvc[i], &_fmvc[i + 1]);
+	}
+
+	//	for No.MAX_FM_VOICE-1
+	Fmvoice_setVoiceNum(&_fmvc[MAX_FM_VOICE - 1], MAX_FM_VOICE - 1);
+	Fmvoice_setNextVc(&_fmvc[MAX_FM_VOICE - 1], FMNULL);
+
+	_firstEmptyVc = &_fmvc[0];
+	_lastEmptyVc = &_fmvc[MAX_FM_VOICE-1];
+}
+bool Asgn_chkEmpty( void )
+{
+	if ( _firstEmptyVc == FMNULL ){ return false; }
+	else { return true; }
+}
+Fmvoice* Asgn_getEmptyVc( void )
+{
+	if ( _firstEmptyVc != FMNULL ){
+		Fmvoice* ret = _firstEmptyVc;
+		_firstEmptyVc = Fmvoice_nextVc(_firstEmptyVc);
+		if ( _lastOccupiedVc != FMNULL ){
+			Fmvoice_setNextVc(_lastOccupiedVc, ret);
+		}
+		_lastOccupiedVc = ret;
+		if ( _firstOccupiedVc == FMNULL ){
+			_firstOccupiedVc = ret;
+		}
+		return ret;
+	}
+	else {
+		FMASSERT(0);
+		return FMNULL;
+	}
+}
+void Asgn_releaseOneVc( void )
+{
+	if ( _firstOccupiedVc == FMNULL ){
+		FMASSERT(0);
+		return;
+	}
+
+	//	Search keyoffed Voice
+	Fmvoice* rlsVc = _firstOccupiedVc;
+	Fmvoice* prevVc = FMNULL;
+	while (rlsVc != FMNULL ){
+		if ( Fmvoice_isKeyon(rlsVc) == false ){
+			break;
+		}
+		prevVc = rlsVc;
+		rlsVc = Fmvoice_nextVc(rlsVc);
+	}
+
+	//	if no keyoffed vc, select first one.
+	if ( rlsVc == FMNULL ){
+		rlsVc = _firstOccupiedVc;
+	}
+
+	setToEmptyList(prevVc,rlsVc);
+}
+void Asgn_releaseParticularVc( Fmvoice* pVc )
+{
+	//	Search pVc & its prevVc
+	Fmvoice* rlsVc = _firstOccupiedVc;
+	Fmvoice* prevVc = FMNULL;
+	while ( rlsVc != FMNULL ){
+		if ( pVc == rlsVc ){
+			break;
+		}
+		prevVc = rlsVc;
+		rlsVc = Fmvoice_nextVc(rlsVc);
+	}
+	setToEmptyList(prevVc,rlsVc);
+}
+static void setToEmptyList( Fmvoice* prevVc, Fmvoice* rlsVc )
+{
+	//	Release from Occupied list
+	if ( rlsVc == _firstOccupiedVc ){
+		_firstOccupiedVc = Fmvoice_nextVc(rlsVc);
+	}
+	if ( rlsVc == _lastOccupiedVc ){
+		_lastOccupiedVc = prevVc;
+	}
+	if ( prevVc != FMNULL ){
+		Fmvoice_setNextVc(prevVc, Fmvoice_nextVc(rlsVc));
+	}
+	Fmvoice_release(rlsVc);
+
+	//	Set Empty list
+	Fmvoice_setNextVc(_lastEmptyVc, rlsVc);
+	_lastEmptyVc = rlsVc;
+	if ( _firstEmptyVc == FMNULL ){
+		_firstEmptyVc = rlsVc;
+	}
+}
diff -r 000000000000 -r c54d59d6fb78 fmasgn.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fmasgn.h	Fri Jan 05 22:58:49 2018 +0000
@@ -0,0 +1,24 @@
+#ifndef FMASGN_H
+#define FMASGN_H
+#include <stdbool.h>
+#include "fmvoice.h"
+
+#define	MAX_FM_VOICE 16
+
+//	public
+extern void Asgn_init( void );
+extern bool Asgn_chkEmpty( void );
+extern Fmvoice* Asgn_getEmptyVc( void );
+extern void Asgn_releaseOneVc( void );
+extern void Asgn_releaseParticularVc( Fmvoice* pvc );
+
+//	setter
+extern void Asgn_setFirstEmptyVc( Fmvoice* vc );
+extern void Asgn_setLastEmptyVc( Fmvoice* vc );
+
+//	getter
+extern Fmvoice* Asgn_voice( int num );
+extern Fmvoice* Asgn_firstEmptyVc( void );
+extern Fmvoice* Asgn_lastEmptyVc( void );
+
+#endif
diff -r 000000000000 -r c54d59d6fb78 fmif.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fmif.c	Fri Jan 05 22:58:49 2018 +0000
@@ -0,0 +1,84 @@
+// fmif.c
+#include	"fmpart.h"
+
+//	Variable
+static Part				_part;
+static unsigned char	_midiCmdCounter;
+static unsigned char	_midiStatus;
+static unsigned char	_midiDataByte1;
+static unsigned char	_midiDataByte2;
+
+//	Prototype
+static void receiveDataByte( unsigned char byteStream );
+static void	generateMidiCmd(void);
+
+void Fmdriver_init( void )
+{
+	_midiCmdCounter = 0;
+	_midiStatus = 0;
+	_midiDataByte1 = 0;
+	_midiDataByte2 = 0;
+
+	Part_init(&_part);
+	Part_pc(&_part,0);
+}
+void Fmdriver_sendMidi( unsigned char byteStream )
+{
+	if ( byteStream & 0x80 ){
+		if ( byteStream == 0xf7 ){
+			Tone_sendTone();
+			_midiStatus = 0;
+			_midiCmdCounter = 0;
+		}
+		else {
+			_midiStatus = byteStream;
+			_midiCmdCounter = 1;
+		}
+	}
+	else if ( _midiStatus == 0xf0 ){
+		Tone_setToneExc(byteStream,_midiCmdCounter);
+		_midiCmdCounter += 1;
+	}
+	else if ( _midiStatus != 0 ){
+		receiveDataByte(byteStream);
+	}
+}
+static void receiveDataByte( unsigned char byteStream )
+{
+	switch (_midiCmdCounter){
+		case 0: case 1:{
+			_midiDataByte1 = byteStream;
+			switch ( _midiStatus & 0xf0 ){
+				case 0xc0: case 0xd0:{
+					_midiCmdCounter = 0;
+					generateMidiCmd();
+					break;
+				}
+				default:{
+					_midiCmdCounter = 2;
+					break;
+				}
+			}
+			break;
+		}
+		case 2:{
+			_midiDataByte2 = byteStream;
+			_midiCmdCounter = 0;
+			generateMidiCmd();
+			break;
+		}
+		default: break;
+	}
+}
+static void generateMidiCmd( void )
+{
+	switch ( _midiStatus ){	//	receive only MIDI ch.1
+		case 0x80: Part_note( &_part, _midiDataByte1, 0 ); break;
+		case 0x90: Part_note( &_part, _midiDataByte1, _midiDataByte2 ); break;
+		case 0xb0: Part_cc( &_part, _midiDataByte1, _midiDataByte2 ); break;
+		case 0xc0: Part_pc( &_part, _midiDataByte1 ); break;
+		case 0xe0: Part_pbend( &_part, _midiDataByte1, _midiDataByte2 ); break;
+		default: break;
+	}
+}
+
diff -r 000000000000 -r c54d59d6fb78 fmif.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fmif.h	Fri Jan 05 22:58:49 2018 +0000
@@ -0,0 +1,6 @@
+#ifndef FMIF_H
+#define FMIF_H
+//	public
+extern void Fmdriver_init( void );
+extern void Fmdriver_sendMidi( unsigned char byteStream );
+#endif
diff -r 000000000000 -r c54d59d6fb78 fmnote.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fmnote.c	Fri Jan 05 22:58:49 2018 +0000
@@ -0,0 +1,103 @@
+// fmnote.c
+#include	"fmtype.h"
+#include	"fmnote.h"
+#include	"fmpart.h"
+#include	"fmtone.h"
+#include	"fmasgn.h"
+
+//	Prototype
+static void		obtainNecessaryVoice(Note* _this);
+
+//	setter
+void Note_setPrevPtr( Note* _this, Note* pn ){ _this->_prevPtr = pn; }
+void Note_setNextPtr( Note* _this, Note* nn ){ _this->_nextPtr = nn; }
+void Note_setPart( Note* _this, void* pt ){ _this->_parent = pt; }
+void Note_setHold( Note* _this, bool hold ){ _this->_hold = hold; }
+//	getter
+bool Note_isInUse( Note* _this ){ return _this->_inUse; }
+bool Note_isKeyOn( Note* _this ){ return _this->_keyon; }
+bool Note_isHeld( Note* _this ){ return _this->_hold; }
+Note* Note_prevPtr( Note* _this ){ return _this->_prevPtr;}
+Note* Note_nextPtr( Note* _this ){ return _this->_nextPtr;}
+unsigned char Note_note(Note* _this){ return _this->_note; }
+unsigned char Note_velocity(Note* _this){ return _this->_velocity; }
+
+void Note_init( Note* _this )
+{
+	_this->_prevPtr = 0;
+	_this->_nextPtr = 0;
+	_this->_inUse = false;
+	_this->_keyon = false;
+	_this->_hold = false;
+	_this->_note = 0;
+	_this->_parent = 0;
+	_this->_velocity = 0;
+}
+
+bool Note_keyon( Note* _this, ToneData* newTone, unsigned char newNote, unsigned char newVelocity )
+{
+	if ( _this->_parent == FMNULL){ return false; }
+
+	_this->_note = newNote;
+	_this->_velocity = newVelocity;
+
+	//	obtain necessary voices
+	obtainNecessaryVoice( _this);
+
+	Fmvoice_keyon(_this->_vc, _this, _this->_parent, newTone, newNote, newVelocity);
+
+	//	set variables
+	_this->_keyon = true;
+	_this->_inUse = true;
+	_this->_hold = false;
+	return true;
+}
+void Note_keyoff( Note* _this )
+{
+	Fmvoice_keyoff( _this->_vc );
+	_this->_keyon = false;
+	_this->_hold = false;
+}
+void Note_damp( Note* _this )
+{
+	if (_this->_vc != FMNULL) {
+		Asgn_releaseParticularVc(_this->_vc);
+	}
+}
+void Note_releaseVc( Note* _this, Fmvoice* rlsVc )
+{
+	if ( rlsVc == _this->_vc ) {
+		_this->_vc = FMNULL;
+	}
+	Note_release(_this);
+}
+void Note_release( Note* _this )
+{
+	if (_this->_parent == FMNULL){ return; }
+	Part_releaseNote((Part*)_this->_parent,_this);
+	_this->_prevPtr = _this->_nextPtr = FMNULL;
+	_this->_keyon = false;
+	_this->_inUse = false;
+	_this->_hold = false;
+}
+void Note_chgVibDpt( Note* _this )
+{
+	if (_this->_parent == FMNULL){ return; }
+	Part* pt = (Part*)_this->_parent;
+	if (pt == FMNULL) { return; }
+	Fmvoice_chgVibDpt(_this->_vc, Part_cc1(pt));
+}
+void Note_chgPit( Note* _this )
+{
+	if (_this->_parent == FMNULL) { return; }
+	Part* pt = (Part*)_this->_parent;
+	if (pt == FMNULL) { return; }
+	Fmvoice_chgPit(_this->_vc, Part_pb(pt));
+}
+static void obtainNecessaryVoice(Note* _this)
+{
+	while (Asgn_chkEmpty() != true) {
+		Asgn_releaseOneVc();
+	} 
+	_this->_vc = Asgn_getEmptyVc();
+}
diff -r 000000000000 -r c54d59d6fb78 fmnote.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fmnote.h	Fri Jan 05 22:58:49 2018 +0000
@@ -0,0 +1,44 @@
+#ifndef FMNOTE_H
+#define FMNOTE_H
+#include "fmtone.h"
+#include "fmvoice.h"
+
+//	Note Class
+typedef struct _Note Note;
+struct _Note {
+	unsigned char	_note;
+	unsigned char	_velocity;
+	bool			_inUse;
+	bool			_keyon;
+	bool			_hold;
+
+	Note*			_prevPtr;
+	Note*			_nextPtr;
+	void*			_parent;
+	Fmvoice*		_vc;
+};
+
+//	public
+extern void Note_init( Note* _this );
+extern bool Note_keyon( Note* _this, ToneData* newTone, unsigned char note, unsigned char velocity );
+extern void Note_keyoff( Note* _this );
+extern void Note_releaseVc( Note* _this, Fmvoice* rlsVc );
+extern void Note_release( Note* _this );
+extern void Note_damp( Note* _this );
+extern void Note_chgVibDpt( Note* _this );
+extern void Note_chgPit( Note* _this );
+
+//	setter
+extern void Note_setPrevPtr( Note* _this, Note* pn );
+extern void Note_setNextPtr( Note* _this, Note* nn );
+extern void Note_setPart( Note* _this, void* pt );
+extern void Note_setHold( Note* _this, bool hold );
+//	getter
+extern bool Note_isInUse( Note* _this );
+extern bool Note_isKeyOn( Note* _this );
+extern bool Note_isHeld( Note* _this );
+extern Note* Note_prevPtr( Note* _this );
+extern Note* Note_nextPtr( Note* _this );
+extern unsigned char Note_note(Note* _this);
+extern unsigned char Note_velocity(Note* _this);
+#endif
diff -r 000000000000 -r c54d59d6fb78 fmpart.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fmpart.c	Fri Jan 05 22:58:49 2018 +0000
@@ -0,0 +1,164 @@
+//	fmpart.c
+#include	"fmtype.h"
+#include	"fmpart.h"
+#include	"fmsd1.h"
+#include	"fmasgn.h"
+#include	"fmtone.h"
+
+//	Prototype
+static Note* getNote(Part* _this);
+
+//	getter
+unsigned char Part_cc1(Part* _this){ return _this->_cc1; }
+unsigned char Part_cc7(Part* _this){ return _this->_cc7; }
+unsigned short Part_pb(Part* _this){ return _this->_pbvalue; }
+unsigned char Part_toneNumber(Part* _this){ return _this->_toneNumber; }
+
+void Part_init( Part* _this )
+{
+	int i;
+
+	Asgn_init();
+	Tone_init();
+
+	_this->_topNt = 0;
+	_this->_endNt = 0;
+	_this->_cc1 = 0;
+	_this->_cc7 = 100;
+	_this->_cc64 = 0;
+	_this->_pbvalue = 0x2000;
+	_this->_toneNumber = 0;
+
+	for ( i=0; i<MAX_NOTE_OBJECT; i++ ){
+		Note_init(&_this->_note[i]);
+		Note_setPart(&_this->_note[i], (void*)_this);
+	}
+}
+void Part_note( Part* _this, unsigned char note, unsigned char velocity )
+{
+	if ( velocity != 0 ){
+		//	keyon
+		Note* newNt = getNote(_this);
+		if ( Note_keyon(newNt,&_this->_tone,note,velocity) == true ){
+			if ( _this->_endNt != FMNULL ){
+				Note_setNextPtr( _this->_endNt,newNt );
+				Note_setPrevPtr( newNt,_this->_endNt );
+			}
+			_this->_endNt = newNt;
+			if ( _this->_topNt ==FMNULL ){
+				_this->_topNt = newNt;
+			}
+		}
+	}
+	else {
+		//	keyoff
+		Note* nt = _this->_topNt;
+		while( nt != 0 ){
+			if (( Note_note(nt) == note ) &&
+				( Note_isKeyOn(nt) == true ) &&
+				( Note_isHeld(nt) == false )){
+				if ( _this->_cc64 < 64 ){
+					Note_keyoff(nt);
+				}
+				else {
+					Note_setHold(nt,true);
+				}
+				break;
+			}
+			nt = Note_nextPtr(nt);
+		}
+	}
+}
+void Part_releaseNote( Part* _this, Note* nt )
+{
+	Note* prevPtr = Note_prevPtr(nt);
+	if ( _this->_endNt == nt ){
+		_this->_endNt = prevPtr;
+	}
+	if ( prevPtr != FMNULL ){
+		Note_setNextPtr(prevPtr, Note_nextPtr(nt));
+	}
+
+	Note* nextPtr = Note_nextPtr(nt);
+	if ( _this->_topNt == nt ){
+		_this->_topNt = nextPtr;
+	}
+	if ( nextPtr != FMNULL ){
+		Note_setPrevPtr(nextPtr, Note_prevPtr(nt));
+	}
+}
+void Part_cc( Part* _this, unsigned char ccnum, unsigned char value )
+{
+	Note* nt = _this->_topNt;
+
+	//	Limit
+	if (value > 127) { value = 127; }
+
+	switch (ccnum) {
+		case 1: {
+			_this->_cc1 = value;
+			while ( nt != FMNULL ) {
+				Note_chgVibDpt(nt);
+				nt = Note_nextPtr(nt);
+			}
+			break;
+		}
+		case 7: {
+			_this->_cc7 = value;
+			writeSingle( REG_MASTER_VOL, (value<<1)&0xfc );
+			break;
+		}
+		case 64: {
+			_this->_cc64 = value;
+			if ( value < 64 ){
+				while ( nt != FMNULL ) {
+					if ( Note_isHeld(nt) == true ){
+						Note_keyoff(nt);
+					}
+					nt = Note_nextPtr(nt);
+				}
+			}
+			break;
+		}
+		default: break;
+	}
+}
+void Part_pc( Part* _this, unsigned char num )
+{
+	Note* nt = _this->_topNt;
+	Note* nextNt;
+
+	//	Damp
+	while ( nt != FMNULL ) {
+		nextNt = Note_nextPtr(nt);
+		Note_damp(nt);
+		nt = nextNt;
+	}
+
+	_this->_toneNumber = num;
+}
+void Part_pbend( Part* _this, unsigned char lsb, unsigned char msb )
+{
+	Note* nt = _this->_topNt;
+	_this->_pbvalue = (msb<<7)|(lsb&0x7f);
+	while ( nt != FMNULL ) {
+		Note_chgPit(nt);
+		nt = Note_nextPtr(nt);
+	}
+}
+static Note* getNote( Part* _this )
+{
+	int	i;
+	Note* newNt = 0;
+	for ( i=0; i<MAX_NOTE_OBJECT; i++ ){
+		newNt = &(_this->_note[i]);
+		if ( Note_isInUse(newNt) ==  false ){
+			return newNt;
+		}
+	}
+	//	if nothing
+	newNt = _this->_topNt;
+	Note_damp(newNt);
+	return newNt;
+}
+
diff -r 000000000000 -r c54d59d6fb78 fmpart.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fmpart.h	Fri Jan 05 22:58:49 2018 +0000
@@ -0,0 +1,39 @@
+#ifndef FMPART_H
+#define FMPART_H
+#include	"fmnote.h"
+#include	"fmtone.h"
+#include	"fmasgn.h"
+
+#define MAX_NOTE_OBJECT 20
+
+//	Part Class
+typedef struct _Part Part;
+struct _Part{
+
+	ToneData	_tone;
+	Note		_note[MAX_NOTE_OBJECT];
+
+	Note*		_topNt;	//	old
+	Note*		_endNt;	//	latest
+
+	unsigned char _cc1;
+	unsigned char _cc7;
+	unsigned char _cc64;
+	unsigned short  _pbvalue;	//	0 - 16383
+	unsigned char _toneNumber;
+};
+
+//	public
+extern void Part_init( Part* _this );
+extern void Part_note( Part* _this, unsigned char note, unsigned char velocity );
+extern void Part_cc( Part* _this, unsigned char ccnum, unsigned char value );
+extern void Part_pbend( Part* _this, unsigned char lsb, unsigned char msb );
+extern void Part_pc( Part* _this, unsigned char num );
+extern void Part_releaseNote( Part* _this, Note* nt);
+
+//	getter
+extern unsigned char Part_cc1( Part* _this );
+extern unsigned char Part_cc7( Part* _this );
+extern unsigned short Part_pb( Part* _this );
+extern unsigned char Part_toneNumber( Part* _this );
+#endif
diff -r 000000000000 -r c54d59d6fb78 fmsd1.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fmsd1.h	Fri Jan 05 22:58:49 2018 +0000
@@ -0,0 +1,22 @@
+#ifndef FMSD1_H
+#define FMSD1_H
+
+//	public
+void initSPI( void );
+void initSD1( void );
+void delayMs( int ms );
+void writeSingle( unsigned char adrs, unsigned char data );
+void writeBurst( unsigned char adrs, unsigned char* data, int count );
+
+static const unsigned char REG_TOP_ADRS = 12;
+static const unsigned char REG_VOL = 0;
+static const unsigned char REG_BLK = 1;
+static const unsigned char REG_FNUM = 2;
+static const unsigned char REG_CTRL = 3;
+static const unsigned char REG_CH_VOL = 4;
+static const unsigned char REG_XVB = 5;
+static const unsigned char REG_INT = 6;
+static const unsigned char REG_FRC = 7;
+static const unsigned char REG_MASTER_VOL = 25;
+#endif
+
diff -r 000000000000 -r c54d59d6fb78 fmsd1_micro.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fmsd1_micro.cpp	Fri Jan 05 22:58:49 2018 +0000
@@ -0,0 +1,89 @@
+#include "MicroBit.h"
+
+SPI ymf825(MOSI, MISO, SCK);
+MicroBitPin ss(MICROBIT_ID_IO_P16,MICROBIT_PIN_P16,PIN_CAPABILITY_DIGITAL);
+MicroBitPin rst(MICROBIT_ID_IO_P8,MICROBIT_PIN_P8,PIN_CAPABILITY_DIGITAL);
+
+//extern MicroBit uBit;
+//#define   ss    uBit.io.P16
+//#define   rst   uBit.io.P8
+
+void writeSingleCPP( unsigned char adrs, unsigned char data )
+{
+    ss.setDigitalValue(0);  //  select slave
+    ymf825.write(adrs);
+    ymf825.write(data);
+    ss.setDigitalValue(1);
+    wait_ms(1);
+}
+extern "C" void writeSingle( unsigned char adrs, unsigned char data ){ writeSingleCPP(adrs,data);}
+extern "C" void writeBurst( unsigned char adrs, unsigned char* data, int count )
+{
+    ss.setDigitalValue(0);
+    ymf825.write(adrs);
+    for (int i = 0; i<count; i++) {
+        ymf825.write(*(data + i));
+    }
+    ss.setDigitalValue(1);
+    wait_ms(1);
+}
+extern "C" void delayMs( int ms ){ wait_ms(ms);}
+
+void initSPI(void)
+{
+    //  init SPI
+    ymf825.format(8);   //  mode=0
+    ymf825.frequency(4000000);
+
+}
+void initSD1( void )
+{
+    //  1.  powerOnReset( void );
+    rst.setDigitalValue(0);
+
+    //  2. wait 100usec
+    wait_us(100);
+
+    //  3.  RST_N : high
+    rst.setDigitalValue(1);
+
+    //  4.  DRV_SEL : low
+    writeSingleCPP(29, 1);  //  3.3v
+
+    //  5.  AP0 : 0
+    writeSingleCPP(2, 0x0e);
+
+    //  6.  wait for Quarz stability
+    wait_ms(1);
+
+    //  7.  CLKE : "1"
+    writeSingleCPP(0, 0x01);
+
+    //  8.  ALRST : low
+    writeSingleCPP(1, 0x00);
+
+    //  9.  SFTRST  :   0xa3
+    writeSingleCPP(26, 0xa3);
+    wait_ms(1);
+
+    //  10. SFTRST  :   0x00
+    writeSingleCPP(26, 0x00);
+
+    //  11. wait 30msec
+    wait_ms(30);
+
+    //  12. AP1 AP3: "0"
+    writeSingleCPP(2, 0x04);
+
+    //  13. wait 10usec
+    wait_us(10);
+
+    //  14. AP2: "0"
+    writeSingleCPP(2, 0x00);
+
+    writeSingleCPP(25, 0xcc);   //  -32[dB]
+    writeSingleCPP(27, 0x3f);   //  set itp max
+    writeSingleCPP(20, 0x00);   //  set itp on
+    writeSingleCPP(3, 0x01);    //  amp gain(6.5[dB])
+    writeSingleCPP(9, 0xf8);    //  Sequencer Volume
+}
\ No newline at end of file
diff -r 000000000000 -r c54d59d6fb78 fmtone.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fmtone.c	Fri Jan 05 22:58:49 2018 +0000
@@ -0,0 +1,189 @@
+//	fmtone.c
+#include	"fmtype.h"
+#include	"fmtone.h"
+#include	"fmsd1.h"
+
+#define		IMMUTABLE_TONE_MAX		8
+#define		MUTABLE_TONE_MAX		IMMUTABLE_TONE_MAX
+#define		AVAILABLE_TONE_NUMBER	(IMMUTABLE_TONE_MAX+MUTABLE_TONE_MAX)
+#define		MAX_EXCLUSIVE_HEADER_SIZE	5
+
+#define 	MAX_ELEMENT_PRM			2
+#define		OPERATOR_PRM_REG_SZ		7
+#define 	MAX_TONE_PRM_SZ 		(MAX_FM_OPERATOR*OPERATOR_PRM_REG_SZ + MAX_ELEMENT_PRM)
+
+typedef enum {
+	WAIT_DATA,
+	DURING_SETTING,
+	SET_STATE_MAX
+} SET_STATE;
+
+//	Variable
+static SET_STATE _toneSetState;
+static int _tprmIndex;
+static ToneData _userTone[MUTABLE_TONE_MAX];
+static const ToneData TPRM[IMMUTABLE_TONE_MAX] = {
+
+	{	//	GrandPiano
+		0x0b,	//	VoiceCommon
+		{ //  KC | AR | DR | SR | RR | SL | TL | VB | PT | WS
+			{0x01,0x0f,0x07,0x00,0x06,0x0f,0x27,0x00,0x01,0x08},	// op1
+			{0x07,0x0e,0x03,0x02,0x03,0x02,0x28,0x00,0x05,0x00},	// op2
+			{0x00,0x0d,0x01,0x01,0x04,0x03,0x22,0x01,0x01,0x00},	// op3
+			{0x06,0x0d,0x02,0x02,0x06,0x04,0x00,0x01,0x01,0x00}		// op4
+		}
+	},
+	{	// E.Piano
+		0x0d,	//	VoiceCommon
+		{ //  KC | AR | DR | SR | RR | SL | TL | VB | PT | WS
+			{0x54,0x0f,0x04,0x05,0x0c,0x0b,0x23,0x44,0x07,0x12},	// op1
+			{0x02,0x0f,0x02,0x01,0x08,0x0f,0x04,0x45,0x01,0x00},	// op2
+			{0x25,0x0f,0x00,0x01,0x0b,0x01,0x12,0x44,0x01,0x00},	// op3
+			{0x04,0x0f,0x02,0x01,0x07,0x0f,0x04,0x41,0x01,0x00}		// op4
+		}
+	},
+	{	//	TenorSax
+		0x0d,	//	VoiceCommon
+		{ //  KC | AR | DR | SR | RR | SL | TL | VB | PT | WS
+			{0x36,0x07,0x03,0x00,0x00,0x00,0x05,0x44,0x01,0x01},	// op1
+			{0x00,0x07,0x02,0x00,0x09,0x00,0x0f,0x43,0x01,0x08},	// op2
+			{0x36,0x07,0x03,0x00,0x00,0x00,0x08,0x44,0x01,0x09},	// op3
+			{0x02,0x07,0x02,0x00,0x09,0x00,0x0d,0x43,0x01,0x00}		// op4
+		}
+	},
+	{	//	PickBass
+		0x0b,	//	VoiceCommon
+		{ //  KC | AR | DR | SR | RR | SL | TL | VB | PT | WS
+			{0x56,0x0f,0x07,0x02,0x03,0x01,0x13,0x44,0x01,0x00},	// op1
+			{0x04,0x0c,0x0b,0x04,0x06,0x07,0x15,0x44,0x07,0x00},	// op2
+			{0x06,0x0f,0x09,0x02,0x06,0x02,0x17,0x44,0x02,0x00},	// op3
+			{0x04,0x0b,0x02,0x06,0x08,0x06,0x00,0x44,0x01,0x00}		// op4
+		}
+	},
+	{	//	TnklBell
+		0x0d,	//	VoiceCommon
+		{ //  KC | AR | DR | SR | RR | SL | TL | VB | PT | WS
+			{0x31,0x0f,0x06,0x03,0x04,0x05,0x10,0x44,0x0e,0x00},	// op1
+			{0x02,0x0c,0x06,0x07,0x06,0x0e,0x0b,0x44,0x02,0x00},	// op2
+			{0x00,0x0c,0x06,0x02,0x02,0x05,0x1e,0x44,0x77,0x01},	// op3
+			{0x00,0x0f,0x05,0x04,0x05,0x0d,0x01,0x54,0x06,0x00}		// op4
+		}
+	},
+	{	//	NewAgePd
+		0x0d,	//	VoiceCommon
+		{ //  KC | AR | DR | SR | RR | SL | TL | VB | PT | WS
+			{0x54,0x0f,0x0f,0x03,0x03,0x00,0x26,0x44,0x07,0x01},	// op1
+			{0x02,0x0f,0x07,0x04,0x04,0x00,0x0b,0x44,0x05,0x00},	// op2
+			{0x62,0x06,0x01,0x00,0x01,0x00,0x18,0x03,0x71,0x01},	// op3
+			{0x02,0x08,0x01,0x00,0x05,0x01,0x00,0x03,0x01,0x00}		// op4
+		}
+	},
+	{	//	Rim Shot
+		0x0d,	//	VoiceCommon
+		{ //  KC | AR | DR | SR | RR | SL | TL | VB | PT | WS
+			{0x7c,0x0f,0x00,0x05,0x05,0x00,0x05,0x44,0x0c,0x02},	// op1
+			{0x0c,0x0f,0x07,0x07,0x07,0x07,0x00,0x44,0x0b,0x00},	// op2
+			{0x08,0x0f,0x0a,0x06,0x06,0x08,0x00,0x44,0x0c,0x00},	// op3
+			{0x08,0x0f,0x07,0x07,0x07,0x07,0x00,0x44,0x07,0x02}		// op4
+		}
+	},
+	{	//	Castanet
+		0x0d,	//	VoiceCommon
+		{ //  KC | AR | DR | SR | RR | SL | TL | VB | PT | WS
+			{0x68,0x0f,0x07,0x05,0x09,0x0f,0x02,0x44,0x07,0x01},	// op1
+			{0x0c,0x0a,0x08,0x05,0x0f,0x0f,0x00,0x44,0x05,0x06},	// op2
+			{0x08,0x0f,0x05,0x06,0x05,0x00,0x27,0x44,0x02,0x05},	// op3
+			{0x08,0x0c,0x0a,0x09,0x09,0x0a,0x14,0x44,0x05,0x00}		// op4
+		}
+	}
+};
+const unsigned char tExcCheck[MAX_EXCLUSIVE_HEADER_SIZE] = {
+	0x43,	//	Exclusive:1, Yamaha ID
+	0x7f,	//	Exclusive:2, Make/DIY ID1
+	0x02,	//	Exclusive:3, Make/DIY ID2
+	0x00,	//	Exclusive:4, YMF825 ID
+	0x00	//	Exclusive:5, reserved
+};
+void Tone_init( void )
+{
+	int i;
+	for ( i=0; i<MUTABLE_TONE_MAX; i++ ){
+		_userTone[i] = TPRM[i];
+	}
+	_toneSetState = WAIT_DATA;
+	_tprmIndex = 0;
+
+	Tone_sendTone();
+}
+void Tone_setToneExc( unsigned char data, int excNum )
+{
+	if ( _toneSetState == WAIT_DATA ){
+		if (( excNum == 1 ) && ( data == tExcCheck[0] )){
+			_toneSetState = DURING_SETTING;
+		}
+	}
+	else if ( _toneSetState == DURING_SETTING ){
+		if ( excNum-1 < MAX_EXCLUSIVE_HEADER_SIZE ){
+		 	if ( data != tExcCheck[excNum-1] ){ _toneSetState = WAIT_DATA; }
+		}
+		else if ( excNum == 6 ){
+			if ( data < MUTABLE_TONE_MAX ){ _tprmIndex = data; }
+			else { _toneSetState = WAIT_DATA; }
+		}
+		else if ( excNum == 7 ){
+			_userTone[_tprmIndex].voiceCommon = data;
+		}
+		else if ( excNum < 48 ){
+				_userTone[_tprmIndex].opPrm[(excNum-8)/MAX_OPERATOR_PRM][(excNum-8)%MAX_OPERATOR_PRM] = data;
+			}
+		else { _toneSetState = WAIT_DATA; }
+	}
+}
+void Tone_sendTone( void )
+{
+	int	i,j;
+	unsigned char regImage[MAX_TONE_PRM_SZ*AVAILABLE_TONE_NUMBER + 1 + 4]; // 485
+
+	//	top
+	regImage[0] = 0x80 + AVAILABLE_TONE_NUMBER;
+
+	for ( i=0; i<AVAILABLE_TONE_NUMBER; i++ ){
+		unsigned char* riPtr = &regImage[MAX_TONE_PRM_SZ*i + 1];
+
+		ToneData* td;
+		if ( i < IMMUTABLE_TONE_MAX ){
+			td = (ToneData*)&(TPRM[i]);
+		}
+		else {
+			td = (ToneData*)&(_userTone[i-IMMUTABLE_TONE_MAX]);
+		}
+
+		riPtr[0] = (td->voiceCommon & 0x60)>>5;
+		riPtr[1] = ((td->voiceCommon & 0x18)<<3) | (td->voiceCommon & 0x07);
+
+		for ( j=0; j<MAX_FM_OPERATOR; j++ ){
+			riPtr[MAX_ELEMENT_PRM+OPERATOR_PRM_REG_SZ*j+0] = (td->opPrm[j][3] << 4) | (td->opPrm[j][0] & 0x08) | ((td->opPrm[j][0] & 0x04)>>2);
+			riPtr[MAX_ELEMENT_PRM+OPERATOR_PRM_REG_SZ*j+1] = (td->opPrm[j][4] << 4) | td->opPrm[j][2];
+			riPtr[MAX_ELEMENT_PRM+OPERATOR_PRM_REG_SZ*j+2] = (td->opPrm[j][1] << 4) | td->opPrm[j][5];
+			riPtr[MAX_ELEMENT_PRM+OPERATOR_PRM_REG_SZ*j+3] = (td->opPrm[j][6] << 2) | (td->opPrm[j][0] & 0x03);
+			riPtr[MAX_ELEMENT_PRM+OPERATOR_PRM_REG_SZ*j+4] = td->opPrm[j][7];
+			riPtr[MAX_ELEMENT_PRM+OPERATOR_PRM_REG_SZ*j+5] = ((td->opPrm[j][8] & 0x0f) << 4) | ((td->opPrm[j][8] & 0xf0) >> 4);
+			riPtr[MAX_ELEMENT_PRM+OPERATOR_PRM_REG_SZ*j+6] = (td->opPrm[j][9] << 3) | ((td->opPrm[j][0] & 0x70) >> 4);
+		}
+	}
+
+	//	end 
+	regImage[MAX_TONE_PRM_SZ*AVAILABLE_TONE_NUMBER + 1] = 0x80;
+	regImage[MAX_TONE_PRM_SZ*AVAILABLE_TONE_NUMBER + 2] = 0x03;
+	regImage[MAX_TONE_PRM_SZ*AVAILABLE_TONE_NUMBER + 3] = 0x81;
+	regImage[MAX_TONE_PRM_SZ*AVAILABLE_TONE_NUMBER + 4] = 0x80;
+
+	//	 Soft Reset
+//    writeSingle(26,0xa3);
+//    writeSingle(26,0x00);
+	writeSingle(8,0xf6);
+	delayMs(1);
+	writeSingle(8,0x00);
+
+	writeBurst( 7, regImage, MAX_TONE_PRM_SZ*AVAILABLE_TONE_NUMBER + 5 );
+}
diff -r 000000000000 -r c54d59d6fb78 fmtone.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fmtone.h	Fri Jan 05 22:58:49 2018 +0000
@@ -0,0 +1,18 @@
+#ifndef FMTONE_H
+#define FMTONE_H
+
+#define MAX_FM_OPERATOR		4
+#define MAX_OPERATOR_PRM 	10
+
+//	ToneData Class
+typedef struct {
+	unsigned char		voiceCommon;				// BO(2) | LFO(2) | ALG(3)
+	unsigned char		opPrm[MAX_FM_OPERATOR][MAX_OPERATOR_PRM];
+} ToneData;
+
+//	public
+extern void Tone_init( void );
+extern void Tone_setToneExc( unsigned char data, int excNum );
+extern void Tone_sendTone( void );
+#endif
+
diff -r 000000000000 -r c54d59d6fb78 fmtype.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fmtype.h	Fri Jan 05 22:58:49 2018 +0000
@@ -0,0 +1,8 @@
+#ifndef FMTYPE_H
+#define FMTYPE_H
+
+#define	FMNULL	((void*)0)
+
+#define	FMASSERT(cd)	if(!(cd)){while(1);}
+
+#endif
diff -r 000000000000 -r c54d59d6fb78 fmvoice.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fmvoice.c	Fri Jan 05 22:58:49 2018 +0000
@@ -0,0 +1,140 @@
+//	fmvoice.c
+#include	"fmtype.h"
+#include	"fmvoice.h"
+#include	"fmtone.h"
+#include	"fmnote.h"
+#include	"fmsd1.h"
+#include	"fmpart.h"
+
+//	setter
+void Fmvoice_setVoiceNum( Fmvoice* _this, unsigned char vn ){ _this->_vnum = vn; }
+void Fmvoice_setNextVc( Fmvoice* _this, Fmvoice* vc ){ _this->_nextVc = vc; }
+
+//	getter
+unsigned char Fmvoice_vnum( Fmvoice* _this ){ return _this->_vnum; }
+bool Fmvoice_isKeyon( Fmvoice* _this ){ return _this->_keyon;}
+Fmvoice* Fmvoice_nextVc( Fmvoice* _this ){ return _this->_nextVc; }
+
+void Fmvoice_init( Fmvoice* _this )
+{
+	_this->_keyon = (false);
+	_this->_nextVc = FMNULL;
+}
+void Fmvoice_keyon( Fmvoice* _this, void* nt, void* pt, void* tn, unsigned char note, unsigned char vel )
+{
+	Part* ptp = (Part*)pt;
+
+	_this->_parent = nt;
+	_this->_keyon = true;
+	_this->_nextVc = FMNULL;
+
+	//	set voice number
+	writeSingle( 11, _this->_vnum );
+
+	//	Damp
+	writeSingle( REG_TOP_ADRS+REG_CTRL, 0x30 );
+
+	//	FNUM,BLOCK
+	Fmvoice_setBasicPit( _this, note );
+
+	//	Vol
+	writeSingle( REG_TOP_ADRS+REG_VOL, vel&0xfc );
+
+	//	ChVol
+	writeSingle( REG_TOP_ADRS+REG_CH_VOL, 0x71 );
+
+	//	XVB
+	Fmvoice_chgVibDpt(_this, Part_cc1(ptp));
+
+	//	INT, FRAC
+	Fmvoice_chgPit(_this, Part_pb(ptp));
+
+	//	KeyOn, ToneNum
+	writeSingle( REG_TOP_ADRS+REG_CTRL, 0x40 + Part_toneNumber(ptp) );
+}
+void Fmvoice_keyoff( Fmvoice* _this )
+{
+	_this->_keyon = false;
+	writeSingle( 11, _this->_vnum );
+	writeSingle( REG_TOP_ADRS+REG_CTRL, 0 );
+}
+void Fmvoice_release( Fmvoice* _this )
+{
+	_this->_keyon = false;
+	if ( _this->_parent != FMNULL ){
+		Note* nt = (Note*)_this->_parent;
+		Note_releaseVc(nt,_this);
+	}
+	_this->_nextVc = FMNULL;
+
+	writeSingle( 11, _this->_vnum );
+	writeSingle( REG_TOP_ADRS+REG_CTRL, 0x30 );
+}
+void Fmvoice_chgPit( Fmvoice* _this, unsigned short pb )
+{
+	static const unsigned short tPitTbl[256] = {
+	256,257,259,260,262,263,264,266,267,269,270,272,273,275,276,278,
+	279,281,282,284,285,287,288,290,292,293,295,296,298,300,301,303,
+	304,306,308,309,311,313,314,316,318,320,321,323,325,327,328,330,
+	332,334,336,337,339,341,343,345,347,349,350,352,354,356,358,360,
+	362,364,366,368,370,372,374,376,378,380,382,384,386,388,391,393,
+	395,397,399,401,403,406,408,410,412,415,417,419,421,424,426,428,
+	431,433,435,438,440,442,445,447,450,452,454,457,459,462,464,467,
+	470,472,475,477,480,482,485,488,490,493,496,498,501,504,506,509,
+	512,515,518,520,523,526,529,532,535,538,540,543,546,549,552,555,
+	558,561,564,567,571,574,577,580,583,586,589,593,596,599,602,606,
+	609,612,616,619,622,626,629,632,636,639,643,646,650,653,657,660,
+	664,668,671,675,679,682,686,690,693,697,701,705,709,712,716,720,
+	724,728,732,736,740,744,748,752,756,760,764,769,773,777,781,785,
+	790,794,798,803,807,811,816,820,825,829,834,838,843,847,852,856,
+	861,866,870,875,880,885,890,894,899,904,909,914,919,924,929,934,
+	939,944,949,954,960,965,970,975,981,986,991,997,1002,1007,1013,1023	};
+
+	unsigned char reg[2];
+	unsigned char pit = pb/64;
+	reg[1] = (unsigned char)((tPitTbl[pit]<<1) & 0x007e);
+	reg[0] = (unsigned char)(((tPitTbl[pit]<<2) & 0x1f00)>>8);
+
+	writeSingle( 11, _this->_vnum );
+	writeSingle( REG_TOP_ADRS+REG_INT, reg[0] );
+	writeSingle( REG_TOP_ADRS+REG_FRC, reg[1] );
+}
+void Fmvoice_chgVibDpt( Fmvoice* _this, unsigned char vibDpt )
+{
+	vibDpt = vibDpt >>4;
+	if ( vibDpt == 0 ){ vibDpt = 1;}
+	writeSingle( 11, _this->_vnum );
+	writeSingle( REG_TOP_ADRS+REG_XVB, vibDpt );
+}
+void Fmvoice_setBasicPit( Fmvoice* _this, unsigned char note )
+{
+	static const unsigned short tFreq[240] = {
+	347,348,349,350,351,352,353,354,355,356,	357,358,359,360,361,362,363,365,366,367,
+	368,369,370,371,372,373,374,375,376,377,	378,380,381,382,383,384,385,386,387,388,
+	390,391,392,393,394,395,396,397,399,400,	401,402,403,404,406,407,408,409,410,412,
+	413,414,415,416,417,419,420,421,422,424,	425,426,427,428,430,431,432,433,435,436,
+	437,439,440,441,442,444,445,446,447,449,	450,451,453,454,455,457,458,459,461,462,
+	463,465,466,467,469,470,471,473,474,475,	477,478,480,481,482,484,485,487,488,489,
+	491,492,494,495,496,498,499,501,502,504,	505,507,508,510,511,513,514,515,517,518,
+	520,521,523,524,526,528,529,531,532,534,	535,537,538,540,541,543,545,546,548,549,
+	551,552,554,556,557,559,561,562,564,565,	567,569,570,572,574,575,577,579,580,582,
+	584,585,587,589,590,592,594,596,597,599,	601,602,604,606,608,609,611,613,615,617,
+	618,620,622,624,626,627,629,631,633,635,	636,638,640,642,644,646,648,649,651,653,
+	655,657,659,661,663,665,667,668,670,672,	674,676,678,680,682,684,686,688,690,692};
+
+	signed short realNote = note;
+	while ( realNote > 128 ){ realNote -= 12;}
+	while ( realNote < 0 ){ realNote += 12;}
+
+	int tblIndex = (realNote % 12) * 20;
+
+	unsigned char reg[2];
+	unsigned short fr = tFreq[tblIndex];
+	unsigned char oct = realNote/12 - 1;
+	reg[0] = (unsigned char)(((fr & 0x0380)>>4) | oct);
+	reg[1] = (unsigned char)(fr & 0x007f);
+
+	writeSingle( 11, _this->_vnum );
+	writeSingle( REG_TOP_ADRS+REG_BLK, reg[0] );
+	writeSingle( REG_TOP_ADRS+REG_FNUM, reg[1] );
+}
diff -r 000000000000 -r c54d59d6fb78 fmvoice.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fmvoice.h	Fri Jan 05 22:58:49 2018 +0000
@@ -0,0 +1,31 @@
+#ifndef FMVOICE_H
+#define FMVOICE_H
+#include	<stdbool.h>
+
+//	Fmvoice Class
+typedef struct _Fmvoice Fmvoice;
+struct _Fmvoice {
+	unsigned char	_vnum;
+	bool			_keyon;
+	Fmvoice*		_nextVc;
+	void*			_parent;
+};
+
+//	public
+extern void Fmvoice_init( Fmvoice* _this );
+extern void Fmvoice_keyon( Fmvoice* _this, void* nt, void* pt, void* tn, unsigned char note, unsigned char vel );
+extern void Fmvoice_keyoff( Fmvoice* _this );
+extern void Fmvoice_release( Fmvoice* _this );
+extern void Fmvoice_chgVibDpt( Fmvoice* _this, unsigned char vibDpt );
+extern void Fmvoice_chgPit( Fmvoice* _this, unsigned short pb );
+extern void Fmvoice_setBasicPit( Fmvoice* _this, unsigned char note );
+
+//	setter
+extern void Fmvoice_setVoiceNum( Fmvoice* _this, unsigned char vn );
+extern void Fmvoice_setNextVc( Fmvoice* _this, Fmvoice* vc );
+
+//	getter
+extern unsigned char Fmvoice_vnum( Fmvoice* _this );
+extern bool Fmvoice_isKeyon( Fmvoice* _this );
+extern Fmvoice* Fmvoice_nextVc( Fmvoice* _this );
+#endif
diff -r 000000000000 -r c54d59d6fb78 main.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Fri Jan 05 22:58:49 2018 +0000
@@ -0,0 +1,58 @@
+#include "MicroBit.h"
+
+extern "C" {
+#include    "fmif.h"
+};
+#include    "fmsd1.h"
+
+//MicroBit uBit;
+MicroBitDisplay uBitDisplay;
+MicroBitMessageBus uBitMessageBus;
+MicroBitButton buttonA(MICROBIT_PIN_BUTTON_A, MICROBIT_ID_BUTTON_A);
+//MicroBitI2C i2c(I2C_SDA0, I2C_SCL0);
+
+int tnum = 60;
+
+const ManagedString tname[12] = {
+    "C","c","D","d","E","F","f","G","g","A","a","B"
+};
+
+void onButtonA(MicroBitEvent)
+{
+    uBitDisplay.print(tname[tnum-60]);
+}
+
+int main( void )
+{
+//    uBit.init();
+
+    initSPI();
+    initSD1();
+    Fmdriver_init();
+
+    Fmdriver_sendMidi(0xc0);
+    Fmdriver_sendMidi(0x04);
+
+    uBitMessageBus.listen(MICROBIT_ID_BUTTON_A, MICROBIT_BUTTON_EVT_CLICK, onButtonA);
+
+    while(1){
+//        if ( buttonA.isPressed() ){
+//            Fmdriver_sendMidi(0x90);
+//            Fmdriver_sendMidi(0x30);
+//            Fmdriver_sendMidi(0x7f);
+//        }
+
+        Fmdriver_sendMidi(0x90);
+        Fmdriver_sendMidi(tnum);
+        Fmdriver_sendMidi(0x7f);
+        wait_ms(200);
+        Fmdriver_sendMidi(0x90);
+        Fmdriver_sendMidi(tnum);
+        Fmdriver_sendMidi(0);
+        wait_ms(200);
+        tnum++;
+        if ( tnum>72 ){ tnum=60;}
+    }
+
+    return 0;
+}
\ No newline at end of file
diff -r 000000000000 -r c54d59d6fb78 microbit.lib
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/microbit.lib	Fri Jan 05 22:58:49 2018 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/teams/Lancaster-University/code/microbit/#4b89e7e3494f