Skip to content

Commit

Permalink
V1.0.0
Browse files Browse the repository at this point in the history
  • Loading branch information
Tamakichi committed Mar 11, 2019
1 parent d0c64ca commit 8c84d8a
Show file tree
Hide file tree
Showing 5 changed files with 505 additions and 0 deletions.
324 changes: 324 additions & 0 deletions MML/MML.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,324 @@
//
// MMLクラスライブラリ V1.0
//

#include "MML.h"

#define c_putch(c) Serial.write(c)
#define isBreak() (false)

// note定義
const PROGMEM uint16_t mml_scale[] = {
4186, // C
4435, // C#
4699, // D
4978, // D#
5274, // E
5588, // F
5920, // F#
6272, // G
6643, // G#
7040, // A
7459, // A#
7902, // B
};

// mml_scaleテーブルのインデックス
#define MML_C_BASE 0
#define MML_CS_BASE 1
#define MML_D_BASE 2
#define MML_DS_BASE 3
#define MML_E_BASE 4
#define MML_F_BASE 5
#define MML_FS_BASE 6
#define MML_G_BASE 7
#define MML_GS_BASE 8
#define MML_A_BASE 9
#define MML_AS_BASE 10
#define MML_B_BASE 11

const uint8_t mml_scaleBase[] = {
MML_A_BASE, MML_B_BASE, MML_C_BASE, MML_D_BASE, MML_E_BASE, MML_F_BASE, MML_G_BASE,
};

void MML::init() {
if (func_init != 0)
func_init();
}

// TONE 周波数 [,音出し時間 [,ボリューム]]
// freq 周波数
// tm 音出し時間
// vol 音出し時間
void MML::tone(int16_t freq, int16_t tm,int16_t vol) {
//dev_tone(freq, tm, vol);
if (func_tone != 0)
func_tone(freq, tm, vol);
}

// NOTONE
void MML::notone() {
//dev_notone()
if (func_notone != 0)
func_notone();
}

// TEMPO テンポ
void MML::tempo(int16_t tempo) {
if ( (tempo < MML_MIN_TMPO) || (tempo > MML_MAX_TMPO) )
return;
common_tempo = tempo;
}

// 長さ引数の評価
// 引数
// ptr(IN) : 対象文字列格納アドレス
// len(OUT) : 評価した長さ
// 正常 0,1,2,4,8,16,32,64 (0は長さ指定なし)
// 異常 -1 (数値エラー)
// 戻り値
// 次の文字列へのポインタ
//
const char* MML::getParamLen(const char *ptr, int16_t& len) {
int16_t tmpLen = 0;
const char* tmpPtr = ptr;
while(isdigit(*ptr)) {
if (flgdbug) c_putch(*ptr); // デバッグ
tmpLen*= 10;
tmpLen+= *ptr - '0';
ptr++;
}
if (tmpPtr != ptr) {
// 長さ引数ありの場合、長さを評価
if ( !((tmpLen==1)||(tmpLen==2)||(tmpLen==4)||(tmpLen==8)||
(tmpLen==16)||(tmpLen==32)||(tmpLen==64)) ) {
// 長さ指定エラー
tmpLen = -1;
err = ERR_MML;
}
}
len = tmpLen;
return ptr;
}

// PLAY 文字列
const char* MML::play(const char* ptr, uint8_t flgTick) {
uint16_t freq; // 周波数
flgdbug = 0; // デバッグフラグ
uint16_t local_len = common_len ; // 個別長さ
uint8_t local_oct = common_oct ; // 個別高さ

int8_t scale = 0; // 音階
uint32_t duration; // 再生時間(msec)
uint8_t flgExtlen = 0;
uint8_t c; // 1文字取り出し用
int16_t tmpLen; // 音の長さ評価用

// MMLの評価
while(*ptr) {
if (flgdbug) c_putch(*ptr); // デバッグ

flgExtlen = 0;
local_len = common_len;
local_oct = common_oct;

//強制的な中断の判定
if (isBreak()) {
err = 0;
break;
//return;
}

// 空白はスキップ
if (*ptr == ' '|| *ptr == '&') {
ptr++;
continue;
}

// デバッグコマンド
if (*ptr == '?') {
flgdbug = 1;
ptr++;
continue;
}

c = toUpperCase(*ptr);
if ( ((c >= 'A') && (c <= 'G')) || (c == 'R') ) {
//**** 音階記号 A - Z,R
uint8_t flgR = 0;
if (c == 'R') {
flgR = 1;
ptr++;
} else {
scale = pgm_read_byte(&mml_scaleBase[c-'A']); // 音階コードの取得
ptr++;
if (*ptr == '#' || *ptr == '+') {
//** 個別の音階半音上げ # or +
if (flgdbug) c_putch(*ptr); // デバッグ
// 半音上げる
if (scale < MML_B_BASE) {
scale++;
} else {
if (local_oct < MML_MAX_OCT) {
scale = MML_B_BASE;
local_oct++;
}
}
ptr++;
} else if (*ptr == '-') {
//** 個別の音階半音下げ # or +
if (flgdbug) c_putch(*ptr); // デバッグ
// 半音下げる
if (scale > MML_C_BASE) {
scale--;
} else {
if (local_oct > 1) {
scale = MML_B_BASE;
local_oct--;
}
}
ptr++;
}
}

//** 個別の長さの指定
ptr = getParamLen(ptr, tmpLen);
if (tmpLen < 0) {
//return;
err = ERR_MML;
break;
} else if (tmpLen > 0) {
local_len = tmpLen;
}

//** 半音伸ばし
if (*ptr == '.') {
if (flgdbug) c_putch(*ptr); // デバッグ
ptr++;
flgExtlen = 1;
}

//** 音階の再生
duration = 240000/common_tempo/local_len; // 再生時間(msec)
if (flgExtlen)duration += duration>>1;

if (flgR) {
// 休符
if (flgTick) {
playduration = duration;
endTick = millis()+duration;
break;
} else {
delay(duration);
}
} else {
// 音符
freq = pgm_read_word(&mml_scale[scale])>>(MML_MAX_OCT-local_oct); // 再生周波数(Hz);
if (flgTick) {
playduration = duration;
endTick = millis()+duration;
tone(freq, 0, common_vol); // 音の再生(時間指定なし)
break;
} else {
tone(freq, (uint16_t)duration, common_vol); // 音の再生
}
}

} else if (c == 'L') { // グローバルな長さの指定
//**** 省略時長さ指定 L[n][.] ***********************************************
ptr++;
ptr = getParamLen(ptr, tmpLen);
if (tmpLen < 0) {
//return;
err = ERR_MML;
break;
} else if (tmpLen > 0) {
local_len = tmpLen;
common_len = tmpLen;

//** 半音伸ばし
if (*ptr == '.') {
if (flgdbug) c_putch(*ptr); // デバッグ
ptr++;
common_len += common_len>>1;
local_len = common_len;
}
} else {
// 引数省略時は、デフォルトを設定する
common_len = MML_len;
local_len = MML_len;
}
//**** ボリューム指定 Vn ***********************************************
} else if (c == 'V') { // グローバルなボリュームの指定
ptr++;
uint16_t tmpVol =0;
while(isdigit(*ptr)) {
if (flgdbug) c_putch(*ptr); // デバッグ
tmpVol*= 10;
tmpVol+= *ptr - '0';
ptr++;
}
if (tmpVol < 0 || tmpVol > MML_MAX_VOL) {
err = ERR_MML;
//return;
break;
}
common_vol = tmpVol;
//**** 音の高さ指定 On ***********************************************
} else if (c == 'O') { // グローバルなオクターブの指定
ptr++;
uint16_t tmpOct =0;
while(isdigit(*ptr)) {
if (flgdbug) c_putch(*ptr); // デバッグ
tmpOct*= 10;
tmpOct+= *ptr - '0';
ptr++;
}
if (tmpOct < 1 || tmpOct > MML_MAX_OCT) {
err = ERR_MML;
//return;
break;
}
common_oct = tmpOct;
local_oct = tmpOct;
} else if (c == '>') { // グローバルな1オクターブ上げる
if (common_oct < MML_MAX_OCT) {
common_oct++;
}
ptr++;
//**** 1オクターブ下げる < ***********************************************
} else if (c == '<') { // グローバルな1オクターブ下げる
if (common_oct > 1) {
common_oct--;
}
ptr++;
//**** テンポ指定 Tn ***********************************************
} else if (c == 'T') { // グローバルなテンポの指定
ptr++;
//** 長さの指定
uint32_t tmpTempo =0;
const char* tmpPtr = ptr;
while(isdigit(*ptr)) {
if (flgdbug) c_putch(*ptr); // デバッグ
tmpTempo*= 10;
tmpTempo+= *ptr - '0';
ptr++;
}
if (tmpPtr == ptr) {
err = ERR_MML;
//return;
break;
}
if (tmpTempo < MML_MIN_TMPO || tmpTempo > MML_MAX_TMPO) {
err = ERR_MML;
return 0;
}
common_tempo = tmpTempo;
} else {
err = ERR_MML;
//return;
break;
}
}
return ptr;
}
62 changes: 62 additions & 0 deletions MML/MML.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
//
// MMLクラスライブラリ V1.0
//

#ifndef ___MML_h___
#define ___MML_h___

#include "Arduino.h"

// エラーメッセージ定義
#define ERR_MML 1

// デフォルト設定値
#define MML_tempo 120 // テンポ(32~255)
#define MML_len 4 // 長さ(1,2,4,8,16,32)
#define MML_oct 4 // 音の高さ(1~8)
#define MML_vol 15 // 音の大きさ(1~15)

// 引数範囲
#define MML_MAX_VOL 15 // 音の大きさ(1~15)
#define MML_MAX_OCT 8 // 音の高さ(1~8)
#define MML_MAX_TMPO 255 // テンポ(32~255)
#define MML_MIN_TMPO 32 // テンポ(32~255)

class MML {
private:
uint8_t err; // Error message index
uint16_t common_tempo = MML_tempo ; // 共通テンポ
uint16_t common_len = MML_len ; // 共通長さ
uint8_t common_oct = MML_oct ; // 共通高さ
uint8_t common_vol = MML_vol ; // 音の大きさ
uint8_t flgdbug; // デバッグ出力フラグ

char *text; // MML文テキスト
char *mml_ptr; // 再生位置
uint16_t playduration; // 処理中の音符・休符の長さ(ミリ秒)
uint32_t endTick; // 再生終了システム時間(ミリ秒)

void (*func_tone)(uint16_t freq, uint16_t tm, uint16_t vol);
void (*func_notone)(void);
void (*func_init)(void);

private:
const char* getParamLen(const char *ptr, int16_t& len);
public:
void attach_init(void (*func)(void)) { func_init = func; };
void attach_tone(void (*func)(uint16_t freq, uint16_t tm, uint16_t vol)) { func_tone = func; };
void attach_notone(void (*func)(void)) { func_notone = func; }
void init();
void init(void (*f1)(void),void (*f2)(uint16_t freq, uint16_t tm, uint16_t vol),void (*f3)(void))
{ func_init = f1; func_tone = f2; func_notone = f3; init(); };

void tone(int16_t freq, int16_t tm = 0,int16_t vol=15);
void notone();
void tempo(int16_t tempo);
const char* play(const char* ptr, uint8_t flgTick = 0);
void setText(const char* text);
uint8_t isPlay();

};

#endif
Loading

0 comments on commit 8c84d8a

Please sign in to comment.