Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Revision 0:c54d59d6fb78, committed 2018-01-05
- Comitter:
- hasebems
- Date:
- Fri Jan 05 22:58:49 2018 +0000
- Commit message:
- ???????????????
Changed in this revision
--- /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;
+ }
+}
--- /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
--- /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;
+ }
+}
+
--- /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
--- /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();
+}
--- /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
--- /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;
+}
+
--- /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
--- /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 +
--- /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
--- /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 = ®Image[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 );
+}
--- /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
+
--- /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
--- /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] );
+}
--- /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
--- /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
--- /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