ta9mar

最近の工作事情

 工作  最近の工作事情 はコメントを受け付けていません。
12月 232016
 

■前置き
この記事は松村Advent Calendar 2016 23日目です。

苗字がおなじなら誰でも参加OK、技術ネタ以外もOKって事なのですが、ハードル低くて助かったような、逆にネタに困るような (そして主催者が高性能すぎて記事が出るたびに、どんどんハードル高くなってるような…)

ふと自分のブログをみたら3年何も書いてませんでした。
人間一度手を止めるとここまで動かさなくなるのかと。本当にびっくりです。
思えばここ数年は家でプログラムそんなに書いた覚えもありません。

自分でも何をしていたのか覚えていないので、SNSを掘り返して1年何やっていたか思い返してみましたが…..大して面白い事書いてませんでした(^^;
大いに反省しつつ、その中であまり周りがやってないネタを探した結果がこれです。

■レーザーカッター購入
夏頃レーザーカッター(レーザー加工機)を導入しました。
http://www.smartdiys.com/fabool-laser-mini/

レザーカッターは興味があって以前にも購入検討しましたが、10万円台で安い!という世界だったので見送っていましたが、春頃にクラウドファンディングしていて5万円で買えるのが衝撃的で速攻ポチった記憶があります。

レーザーカッターというと、プラ/アクリル板を切断したり、木材を切断するイメージがあるのですが、安いこいつは1.6Wレーザーで紙くらいしか満足に切れません。

初めは百均で色紙を買ってきて、焼き切って遊んでいました。
細かい切り絵や、文字の切り抜き、絵(画像)を焼いたりしました。

文字については、普通のフォントでは切れないことに気づき、ステンシルフォントなるものを使うのだと学んだりしました。普通のフォントだとAの真ん中の△が落ちて残らないんですね。。。

 

言ってしまえば「ハサミで切る代わりに機械が正確に切ってくれるだけ」なのですが、目の前にすると結構な衝撃です。
30年程前手書きの年賀状がプリンターで印刷できるようになったようなあの衝撃です。(小学生にしてドットインパクトプリンターで年賀状印刷して出してました)
そんな中、ふと昔やっていた革工作を思い出し、レーザーカッターを使ってみました。

■革×レーザーカッター
革工作(といっても素人)ですが、おおまかな行程として
①型紙を作る、②型紙通り革に線を引く、③線の通り革を切る、④縫う穴をあける、⑤縫ったりボタン付けたり
という手順を踏みますが、レーザーを使うと①②③が楽にできると考えました。

具体的には子供が小学生になり、工作や縫物が好きなようなので、楽して準備して縫うところをやらせてあげようと思った訳です。

試行錯誤の結果、簡単に準備ができ、娘はがんばって財布を縫って完成させることができました。

■工程の改善
・型紙を作る工程
これまで本やネットに載っている型紙を印刷したり、自分で画用紙に線を引いて作っていました。

この工程はそのままInkscapeやIllustratorで線を引いて作成するようになりました。

・革に線を引く工程
これまでは革に銀ペン等で線を引いていました。正確に線を引く必要があるため大きめのL型定規等を使い線を引きます。形がゆがむと困るので四角を書くだけでも直角や平行にすごく気を使い、時間がかかります。

この工程は、型紙ファイルをそのままsvgファイルにして、レーザーで革に焼きいれて線を引くようにしました。直角も平行も拡大も縮小も一瞬で完璧です。
さらに2つ3つ同じ物を作るのも一撃です。これまでの苦労is何?という感じです。

・革を切る工程
革包丁やカッターで切断します。素人なので直線は定規を当てたりして慎重に。これも時間がかかります。

この工程は、レーザーカッターの本領発揮で革を焼き切る事で、サクッと完了します。
超革命的…と思いきや、適用が難しい場合がありました。

濃い色の革であれば、問題なく焼き切って革命完了です。
ただ、切断面は焦げて触れると指が真っ黒になるくらいなので、後処理が必要なのと色が焦げ茶色になる事を許容する必要があります。

僕が好んで使うヌメ革のような薄い色の革の場合、切断面だけではなく周辺が焦げ色のムラができてしまいました。これは使えず革命失敗です。

・その他の工程
以降の工程はレーザーでは変わりません。
菱目打ちという工具で縫い穴をあけたり、無心で縫うのは時間がかかりますが楽しいです。

変わらないと書きましたが、一点レーザーにより革命的なポイントがあります。
焼き入れで文字や絵(画像)が書ける点です。

名前やロゴを入れたりするのは、これまでは型や焼き印等を作るしかありませんでした。
焼き印も欲しくて検討したことがありますが、数千円から万した気がします。そして文字やサイズを変えたければ買い直しです。
これがレーザーでは自由に絵や文字・サイズ変更可能で焼き入れできます。凄くスゴイ!

■まとめ
・レーザーカッターはいいぞ!一家に一台ほしいよね
・レーザーx革は時間のかかる工程をかなり省略でき革命的!
・革楽しいのでみんなやってみよう!素晴らしき工作ライフ!

ちなみに最近は会社の50W位のレーザーカッター使わせてもらって修行中ですが、アクリル板切りまくれるの凄い便利で、またレベルの違う工作の世界が広がります。
これで革や紙やったら燃えると思いますので、低出力のレーザーカッターはまだまだ出番があります。安全で扱いやすいですし。

■余談
娘の自作小銭入れを見たお友達がやってみたいという事で、ご兄弟3人分お財布作ることになりました。
まさに今日12/23日、夕方まで頑張りました。革の色、糸の色、ボタンの色を選んでもらって、縫うところから教えながら作成してもらいました。

分かったことは、小学生低学年には飽きずに縫いきることは難しい、という事。結局最後は大人が縫いましたw(でも菱目打ち、ボタン(バネホック)打ちは楽しそうだった)
当然向き不向きがあり、うちの子はたまたま忍耐強い方のようです。。。

■宣伝
そういえば昔作ったクリスマス向けハンドベルアプリ、googleplayから消えてたの再公開しました。
Androiderはクリスマスに遊んでみてね!

https://play.google.com/store/apps/details?id=org.ydeb.android.yokooto.handbell2011w

 

eVY1 shieldでシリアル通信で歌詞指定&MML再生(その2)

 Android, PIC/arduino  eVY1 shieldでシリアル通信で歌詞指定&MML再生(その2) はコメントを受け付けていません。
11月 192013
 

MML再生をパワーアップさせてみました。

androidの会横浜支部で発表・実演してきました。

■発表資料です

■使用の前に
・arduinoとeVY1shieldソフトウェアシリアルを使用します。
eYV1shieldのJP2を2-3に変更してください。
・(ハードウェアの)シリアル通信でコマンド入力します。本サンプルでは9600bpsで改行コードは[LFのみ]です
arduinoコンソールから演奏できます。

・MMLの書き方、歌詞の書き方、サンプルデータは資料をご覧ください。

・さらにBluetoothモジュール(手持ちのRBT-001)をつけて、Android(BluetoothchatをSPP化したもの)から演奏できることは確認済みです。(iOSは開発できないので未確認)

 

■スケッチ(コード汚くてすみません・・・)

#include <avr/pgmspace.h>
#include <SoftwareSerial.h>

#define CHANNEL 0 //(0:eVocaloid 1-15:MIDI(9:drum) )
#define BUF_SIZE 512

typedef struct {
     //midi event
  int type;
  int channel;
  int note;
  int velocity;
  int length;
  int param1;
  int time;
} midi_message_t;

 //func
void send_midi_message(midi_message_t midi_message);

/*
 * 注意:
 * SoftwareSerial使用の為eYV1shieldのJP2を2-3に変更する事
 */

int led = 13;
//SW-Serial: eVY1 shield
SoftwareSerial swSerial(2, 3);

void setup() {
  // initialize the digital pin as an output.
  pinMode(led, OUTPUT);
  //  Set MIDI baud rate:
  swSerial.begin(31250);
  // HW-Serial: console
  Serial.begin(9600);
  delay(1000);

}

char recv_buf[BUF_SIZE]; // 受信用バッファ
char *p_buf=recv_buf;//受信バッファポインタ(初期値recv_buf先頭アドレス)

void loop(){
  //シリアル受信処理
  while(Serial.available()>0){
    *p_buf = Serial.read();//1byte読み込みバッファに書き込み

    if(*p_buf=='\n' /*|| *p_buf=='\''*/){
      *p_buf='\0';// \0:null(文字列終端)で改行を上書き

      receive_data(recv_buf);//受信データ処理

      p_buf=recv_buf;//受信バッファポインタ初期値化
    }else{
      p_buf++;
    }
  }
}

//serialコマンドデータ受信
void receive_data(char *recvdata) {
  if(strlen(recvdata)!=0){
     if(*recvdata=='#'){
       //歌詞設定
       lylic_send(recvdata);
       Serial.println("OK");
     }else{
       //mml再生
       int ret=play_mml2(recvdata);
       if(ret==0){
         Serial.println("OK");
       }else{
         Serial.print("ERR:");
         Serial.println(ret);
       }
     }
  }
}

//lylic send
void lylic_send(char *str){

  //Phonetic symbols hedaer f04379090050は固定
  swSerial.write((byte)0xF0);
  swSerial.write((byte)0x43);
  swSerial.write((byte)0x79);
  swSerial.write((byte)0x09);
  swSerial.write((byte)0x00);
  swSerial.write((byte)0x50);
  swSerial.write((byte)0x10);//10:replace 11:append

   Serial.print("lylic:[");
  for(int i=1;i<strlen(str)+1;i++){
     swSerial.write(*(str+i));
     Serial.print(*(str+i));
  }
   Serial.println("]");

  //footer
  swSerial.write((byte)0x00);//end of list
  swSerial.write((byte)0xF7);

}

//----------------------------
#define MAX_CHANNEL  15
#define RESOLUTION 480
#define MML_CHAR  "CDEFGABR#+-1234567890.&O><LV@TI "
#define MML_CMD_SOUND  "CDEFGABR" //音符・休符
#define MML_CMD_ETC  "O><LV@T&I " //その他制御コマンド TODO: PNQ,は非優先で実装
//#define MML_CMD  MML_CMD_SOUND+MML_CMD_ETC; //全コマンド
#define MML_OPT_SOUND  "#+-" //CMDの後ろに付くオプション
#define MML_OPT_LENGTH "." //CMDの後ろに付くオプション TODO:&は別途実装
#define MML_NUM "1234567890" //数字

//音色(MIDIノートナンバー)
#define NOTE_C  0
#define NOTE_D	2
#define NOTE_E	4
#define NOTE_F	5
#define NOTE_G	7
#define NOTE_A	9
#define NOTE_B	11

//イベントタイプ
#define TYPE_ETC 0
#define TYPE_NOTE  1
#define TYPE_REST  2
#define TYPE_INSTRUMENT  3
#define TYPE_TEMPO 4

int play_mml2(char *mmlstr){
  int pos=0;
  int mml_L=4;//Length(1-64?)デフォルト4(4分音符)
  int mml_O=4;//Octave(0-9)デフォルト4
  int mml_T=120;//Tempo(1-255)デフォルト120
  int mml_V=15;//volume(0-15)デフォルト15?
  int mml_AT=0;//音色(GM:0-127)デフォルト0
  char command_buf[16];

  midi_message_t midimessage;

   Serial.println("mml:");
   Serial.print(mmlstr);
   Serial.println("");

while (pos < strlen(mmlstr)) {
  *(mmlstr+pos)=toupper(*(mmlstr+pos));//小文字→大文字
  sprintf(command_buf,"%c",*(mmlstr+pos));

  // ①コマンド文字チェック
  if (strstr((char *)MML_CMD_SOUND,command_buf) == NULL
     && strstr((char *)MML_CMD_ETC,command_buf) == NULL) {
    // コマンド以外文字
    return -1;
  }

  // ②タイプ判別確定
  if(strstr((char *)MML_CMD_SOUND,command_buf) != NULL){ // 音符・休符
    midimessage.type = TYPE_NOTE;
    midimessage.channel=CHANNEL;	//暫定channel=0固定
    midimessage.velocity=(127*mml_V/15);	//ボリューム設定

    Serial.print("*");
    switch (*(mmlstr+pos)) {
      case 'C':
        midimessage.note = NOTE_C + ((mml_O+1) * 12);
        break;
      case 'D':
        midimessage.note = NOTE_D + ((mml_O+1) * 12);
        break;
      case 'E':
        midimessage.note = NOTE_E + ((mml_O+1) * 12);
        break;
      case 'F':
        midimessage.note = NOTE_F + ((mml_O+1) * 12);
        break;
      case 'G':
        midimessage.note = NOTE_G + ((mml_O+1) * 12);
        break;
      case 'A':
        midimessage.note = NOTE_A + ((mml_O+1) * 12);
        break;
      case 'B':
        midimessage.note = NOTE_B + ((mml_O+1) * 12);
        break;
      case 'R':
        midimessage.type = TYPE_REST;
        break;
      default:
        //error
        return -2;
    }

    // 音長仮決定(明示的指定、付点などで後で変更される可能性有り)
    midimessage.length = (RESOLUTION * 4) / mml_L;// 音長=1小節の時間/n分音符
    pos++;
  } else if(strstr((char *)MML_CMD_ETC,command_buf) != NULL){ // その他

    Serial.print("*");
    switch (*(mmlstr+pos)) {
      case 'O': // オクターブ(MIDIobj無し)
      {
        midimessage.type = TYPE_ETC;
        pos++;
	int tmpOct = atoi(mmlstr+pos);
	if (tmpOct < 1 || tmpOct > 9) {
	  // 範囲外
	  return -3;

        }
        mml_O = tmpOct;
        char buf[16];
        pos += strlen(itoa(tmpOct,buf,10));// 桁数分

        Serial.print("*");//一桁固定
      }
      break;
    case '>': // オクターブ(MIDIobj無し)
      midimessage.type = TYPE_ETC;
      if (mml_O < 9)
        mml_O++;
      pos++;
      break;
    case '<': // オクターブ(MIDIobj無し)
      midimessage.type = TYPE_ETC;
      if (mml_O > 0)
        mml_O--;
      pos++;
      break;
    case 'L': // 音長(MIDIobj無し)
    {
      midimessage.type = TYPE_ETC;
      pos++;
      int tmpLen = atoi(mmlstr+pos);

      if (tmpLen < 1 || tmpLen > 128) {
      // 範囲外
        return -4;
      }
      mml_L = tmpLen;
      char buf[16];
      pos += strlen(itoa(tmpLen,buf,10));// 桁数分
      for(int i=0;i<strlen(itoa(tmpLen,buf,10));i++){
          Serial.print("*");
      }
    }
    break;
    case 'V': // 音量(MIDIobj無し)
    {
      midimessage.type = TYPE_ETC;
      pos++;
      int tmpVol = atoi(mmlstr+pos);

      if (tmpVol < 0 || tmpVol > 15) {
        return -5;
      }
      mml_V = tmpVol;

      char buf[16];
      pos += strlen(itoa(tmpVol,buf,10));// 桁数分
      for(int i=0;i<strlen(itoa(tmpVol,buf,10));i++){
          Serial.print("*");
      }
    }
    break;
    case '@': // Instrument(楽器)変更
	midimessage.type = TYPE_INSTRUMENT;
	// 数値は後処理で読み込み
	pos++;
	break;
    case 'T': // TEMPO
      midimessage.type = TYPE_TEMPO;
      // 数値は後処理で読み込み
      pos++;
      break;
    case '&': // タイ・スラー
      //TODO: 実装未定
      midimessage.type = TYPE_ETC;
      pos++;
      break;
    case ' ': // 半角スペース
      //なにもしないで読み飛ばし
      midimessage.type = TYPE_ETC;
      pos++;
      break;
    default:
      return -6;

    }

  }

  // ③タイプごとの後処理
  /*
   * 音符時 オプションで半音、音長指定、付点がある
   *  Cの例:C C+ C+4 C+. C+4.
   *
   *  休符時、音長指定、付点がある
   *  例:R R4 R4.
   */

  if(strlen(mmlstr)>pos){

    *(mmlstr+pos)=toupper(*(mmlstr+pos));//小文字→大文字
    sprintf(command_buf,"%c",*(mmlstr+pos));

    // オプション(#+-)文字チェック(音符の後のみ)
    if (midimessage.type == TYPE_NOTE
        && strstr((char *)MML_OPT_SOUND,command_buf) != NULL) {
        Serial.print("*");
        // コマンドオプション文字
        switch (*(mmlstr+pos)) {
          case '#': // シャープ
          case '+': // シャープ
            if (midimessage.note < 127)
              midimessage.note++;
            break;
          case '-': // フラット
            if (midimessage.note > 0)
              midimessage.note--;
            break;
          default:
            return -7;
        }
        pos++;
      }

      // 音長(数値)処理(音符・休符の後のみ)
      sprintf(command_buf,"%c",*(mmlstr+pos));
      if ((midimessage.type == TYPE_NOTE || midimessage.type == TYPE_REST)
          && strstr((char *)MML_NUM,command_buf) != NULL) {

        int tmpLength = atoi(mmlstr+pos);

        if (tmpLength < 0 || tmpLength > RESOLUTION) {
          // 範囲外
          return -8;
        }
        if(tmpLength!=0){
          midimessage.length = (RESOLUTION * 4) / tmpLength;// 音長=1小節の時間/n分音符
        }
        char buf[16];
        pos += strlen(itoa(tmpLength,buf,10));// 桁数分
        for(int i=0;i<strlen(itoa(tmpLength,buf,10));i++){
            Serial.print("*");
        }
      }

      // 付点(.)処理(音符・休符の後のみ)
      sprintf(command_buf,"%c",*(mmlstr+pos));
      if ((midimessage.type == TYPE_NOTE || midimessage.type == TYPE_REST)
          && strstr((char *)MML_OPT_LENGTH,command_buf) != NULL) {
        midimessage.length *= 1.5;
        pos++;
        Serial.print("*");//一桁固定
      }

      // テンポ指定
      if(midimessage.type == TYPE_TEMPO
          && strstr((char *)MML_NUM,command_buf) != NULL) {

        midimessage.param1 = atoi(mmlstr+pos);

        if (midimessage.param1 < 1 || midimessage.param1 > 255) {
          // 範囲外
          return -9;
        }
        char buf[16];
        pos += strlen(itoa(midimessage.param1,buf,10));// 桁数分

        mml_T=midimessage.param1;
       for(int i=0;i<strlen(itoa(midimessage.param1,buf,10));i++){
          Serial.print("*");
      }
     }

      //音色指定
      if (midimessage.type == TYPE_INSTRUMENT ){
        midimessage.param1 = atoi(mmlstr+pos);

        if (midimessage.param1 < 0 || midimessage.param1 > 127) {
          // 範囲外
          return -10;
        }
        char buf[16];
        pos += strlen(itoa(midimessage.param1,buf,10));// 桁数分
              for(int i=0;i<strlen(itoa(midimessage.param1,buf,10));i++){
          Serial.print("*");
      }

      }
    }

    //イベント送信
    if(midimessage.type==TYPE_NOTE || midimessage.type==TYPE_INSTRUMENT){
      send_midi_message(midimessage);
    }

    //rest(何もしない)
    if(midimessage.type==TYPE_NOTE || midimessage.type==TYPE_REST){
      delay((60000/mml_T)*midimessage.length/480);    //次の音時間までwait
   }

    //note_off処理
    if(midimessage.type==TYPE_NOTE){
      midimessage.velocity=0;//Note Off
      send_midi_message(midimessage);

    }
}
  //loop end
  Serial.println("");
  return 0;
}

void send_midi_message(midi_message_t midi_message) {

  switch(midi_message.type){
    case TYPE_NOTE:
      //NOTE-ON:0x9n
      swSerial.write((byte)(0x90|midi_message.channel));
      swSerial.write((byte)midi_message.note);
      swSerial.write((byte)midi_message.velocity);
      break;

    case TYPE_INSTRUMENT:
      //ProgramChange:0xCn
      swSerial.write((byte)(0xc0|midi_message.channel));
      swSerial.write((byte)midi_message.param1);
      break;
    default:
      break;
  }

}
11月 052013
 

arduinoスケッチのサンプルを少し改造して、シリアル入力で演奏できるように改造しました。
arduinoコンソールで演奏、有線シリアル通信での演奏、BluetoothモジュールをつければAndroidやiPhoneからSPPで無線再生できると思います。

本サンプルは下記2つをシリアルで行うことが出来ます。
①歌詞の指定(PA)
②音符の演奏(MML)

動画はarduinoコンソールで演奏した例

■使用の前に
・arduinoとeVY1shieldソフトウェアシリアルを使用します。
eYV1shieldのJP2を2-3に変更してください。
・(ハードウェアの)シリアル通信でコマンド入力します。
本サンプルでは9600bpsで改行コードは[LFのみ]です

■歌詞指定
#のあとに発声(Phonetic symbols)を入力します

アイウエオの5音例 ⇒ #a,i,M,e,o

歌詞指定については、YMW820(NSX-1) MIDI仕様書:日本語eVocaloid™Phonetic Alphabet(PA)を参照の事

Yamaha-WebMusic GitHubPage(現時点では一番下のSpecにあります)
http://yamaha-webmusic.github.io/nsx1-apps/manual/

PDF直リンク
http://yamaha-webmusic.github.io/nsx1-apps/specs/ANMW820A-001-10-j.pdf

■音符再生
再生はMMLで指定します。

ド:C レ:D ミ:E ファ:F ソ:G ラ:A シ:B で指定します。
半音や付点・オクターブ変更など何も出来ません。
音長は全て500msで固定になっています

ドレミファソの例 ⇒ CDEFG

先に歌詞指定をしてから、音符再生すると歌います。

基本的に元のかえるの歌サンプルそのままです。

#include <avr/pgmspace.h>
#include <SoftwareSerial.h>

#define CHANNEL 0 //(0:eVocaloid 1-15:MIDI(9:drum) )

/*
 * 注意:
 * SoftwareSerial使用の為eYV1shieldのJP2を2-3に変更する事
 */

int led = 13;
//SW-Serial: eVY1 shield
SoftwareSerial swSerial(2, 3);

void setup() {

  // initialize the digital pin as an output.
  pinMode(led, OUTPUT);
  //  Set MIDI baud rate:
  swSerial.begin(31250);
  // HW-Serial: console
  Serial.begin(9600);
  //delay(1000);

}

char recv_buf[32]; // 受信用バッファ32bytes
char *p_buf=recv_buf;//受信バッファポインタ(初期値recv_buf先頭アドレス)

void loop(){
  //シリアル受信処理
  while(Serial.available()>0){
    *p_buf = Serial.read();//1byte読み込みバッファに書き込み

    if(*p_buf=='\n'){
      *p_buf='\0';// \0:null(文字列終端)で改行を上書き

      receive_data(recv_buf);//受信データ処理

      p_buf=recv_buf;//受信バッファポインタ初期値化
    }else{
      p_buf++;
    }

  }
}

//serialコマンドデータ受信
void receive_data(char *recvdata) {
  if(strlen(recvdata)!=0){
     if(*recvdata=='#'){
       //歌詞設定
       lylic_send(recvdata);
     }else{
       //mml再生
       play_mml(recvdata);
     }
  }
}

//mml再生
void play_mml(char *recvdata) {

  int key = 0x3c; //C
  int wait =500;
  int oct = 0;

  char mml[256];
  strcpy(mml,recvdata);

  int mml_len = strlen(mml) / sizeof(mml[0]);

   Serial.print("mml:[");
   Serial.print(mml);
   Serial.println("]");

  for(int i = 0;i< mml_len ;i++){     char c = mml[i];     if(c >= 'a' && c <='z') c-= 0x20;     if((c>='A' && c<='Z')||c=='<'||c=='>'){
       //key send
       if(i!=0){
         Short_Message((byte)(0x90 | CHANNEL),key,0x7f);//note on
         delay(wait);
         Short_Message((byte)(0x80 | CHANNEL),key,0x7f);//note off
       }
       key= 0x3c;

        switch(c) {
            case 'C': break;
            case 'D': key+=2; break;
            case 'E': key+=4; break;
            case 'F': key+=5; break;
            case 'G': key+=7; break;
            case 'A': key+=9; break;
            case 'B': key+=11; break;
            default: break;
        }
    }
  }
  Short_Message((byte)(0x90 | CHANNEL),key,0x7f);
  delay(wait);
  Short_Message((byte)(0x80 | CHANNEL),key,0x7f);//note off
}

void Short_Message(int cmd, int d1, int d2) {
  swSerial.write((byte)cmd);
  swSerial.write((byte)d1);
  swSerial.write((byte)d2);
}

//lylic send
void lylic_send(char *str){

  //Phonetic symbols hedaer f04379090050は固定
  swSerial.write((byte)0xF0);
  swSerial.write((byte)0x43);
  swSerial.write((byte)0x79);
  swSerial.write((byte)0x09);
  swSerial.write((byte)0x00);
  swSerial.write((byte)0x50);
  swSerial.write((byte)0x10);//10:replace 11:append

   Serial.print("lylic:[");
  for(int i=1;i<strlen(str)+1;i++){
     swSerial.write(*(str+i));
     Serial.print(*(str+i));
  }
   Serial.println("]");

  //footer
  swSerial.write((byte)0x00);//end of list
  swSerial.write((byte)0xF7);

}

■その他
・歌詞(発音)指定について、
元サンプルには文字→発音の変換テーブルが付いていましたが、50音の単音、濁音、半濁音のみで、
仕様書と比較するとテーブルが全て用意されている訳ではなく、自由度が低くなるのでローレベルで指定するようにしました。
(たとえばシェとかがないです)

スペースで区切ると1つの音符に対して、連続発音で指定できました
“a i”で”あい”のような。

元々ボカロっている人じゃないので少し難しいです。

・CHANNELで0をdefineしてますが、MIDIのチャネルをあらわしています。
0が特別にeVocaloidに割り当てられていて、その他チャネルが普通のMIDIのようです。

1~8,10~15にするとピアノの音がなります。(0以外の場合歌詞指定は不要です)
音色変更機能は未実装ですのでピアノから変更できません。

9にするとドラムセットです。

・一部ちゃんと音が出ない発音がありますので、本サンプルを信用しないでください(^^;
バグだと思うのですが解析する時間がとれず・・・

・元のサンプルでNOTE ON音鳴らした後、NOTE OFFしてない等気に食わない部分は微修正してます
MMLについては、これしか出来ないのに不満爆発なので過去に作ったやつを移植したいです。
いつかMMLで楽器+eVocaloid同時演奏もしたいですね。

11月 012013
 

YAMAHAのボーカロイドチップNSX-1にVY1簡易版歌声データを載せたっぽい、ボカロシールドが発売されたので実験用に購入。

http://www.switch-science.com/catalog/1490/

USBでPCと繋ぐとUSB-MIDIとして認識。PCでMIDIファイルを再生するだけで遊ぶことが出来ます。Arduinoなくても遊べますね。
(Win7やVistaだとMIDIマッパーが切り替えできなくなってるので、MIDISelectionとか切り替えるツール使ってください)

■サンプルMIDIデータ

サンプルの故郷(ふるさと)のMIDIデータが凄く良い出来でびっくり。これは一見(一聴)の価値ありです。

こんな小さいボードで、MIDI音源(YAMAHA XG音源)で楽器鳴らすのと、歌声を同時に鳴らせている事に衝撃。
サンプル曲、歌声が和音に聞こえるような(2音同時発声できるとは思えないしエフェクターなのかな)

この衝撃は、PC-98で86ボードってサウンドボード出て、PCMで声なった!なんだこれ!?ってなったの思い出しました。

XG音源はまあこのサイズでの合成なので期待してませんでしたが、クソと評判のWindows標準「Microsoft GS Wavetable Synth」より断然良い音をしています。(でも若干音色にもよる)

MIDIデータに歌詞埋め込まれてますが、結果から言うとSysEx(システムエクスクルーシブ)なので、他の楽器では無視されて普通に再生できます。

■arduino用のサンプルコード

かえるのうたを歌うサンプルコード。

・歌詞を送って、MIDIで音符を再生すると、その音階で歌ってくれる感じ。
・通信は31250bpsなのでいわゆるMIDIそのまま
・音鳴らす時はMIDIのNote ONメッセージ(鍵盤押した)コマンド3byte送ってる
・歌詞は0xf0で始まって0xf7で終わるSysExメッセージ。発音を文字列で送るけど単なるローマ字ではない部分ある。ボカロ触った事ないからよく分からない。

つまり完全にMIDIじゃねーの

音階部分はMML(ドレミファをCDEFって書くやつ)だったけど、完全に手抜きだったので・・・
(サンプルが複雑にならないようにだと思うけど)
過去何度も作ったので時間あれば移植したい

■eVY1 shield + MIDI shild

そういえばMIDI-IFのシールド買ってたなーと載せてみた。

arduinoにeVY1シールド載せてその上にMIDIシールドの3段構成。

元のサンプルはシリアル通信でeVY1とやり取りしてるが、MIDIシールドが使っちゃうので、ソフトウェアシリアル使うように変更。
(eVY1シールド上のジャンパでソフトウェアシリアルにピン変更できるようになってます)

でハードシリアルから入った31250bpsデータをそのままソフトウェアシリアルに1バイトずつ流しだすだけで完成。

歌詞をソース直に書いてるので、楽器としてはイマイチ。
「ラ~」固定にしたら汎用性ありそうだけど面白くなかった。

音声入力などでサッと歌詞登録して、すぐ鍵盤演奏するくらいじゃないと使い物にはならない感

ソースはこんな感じ

#include <avr/pgmspace.h>
#include <SoftwareSerial.h>

/*
 * 注意:
 * SoftwareSerial使用の為eYV1shieldのJP2を2-3に変更する事
 */

int led = 13;
//SW-Serial: eVY1 shield
SoftwareSerial swSerial(2, 3);

void setup() {

  // initialize the digital pin as an output.
  pinMode(led, OUTPUT);
  //  Set MIDI baud rate:
  swSerial.begin(31250);
  // HW-Serial: MIDI-IN
  Serial.begin(31250);
  delay(5000);
  set_lylic();

}

//変更しない
const char* phoneticSymbols[] = {
	"a", "i", "M", "e", "o",		// あいうえお0-4
	"k a", "k' i", "k M", "k e", "k o",	// かきくけこ5-9
	"s a", "S i", "s M", "s e", "s o", // さしすえそ10-14
	"t a", "tS i", "ts M", "t e", "t o",	// たちつてと15-19
	"n a", "J i", "n M", "n e", "n o",	//なにぬねの20-24
	"h a", "C i", "p\\ M", "h e", "h o",	// はひふへほ25-29
	"m a", "m' i", "m M", "m e", "m o", // まみむめも30-34
	"j a","i", "j M","e","j o",//やいゆえよ35-39
	"4 a", "4' i", "4 M", "4 e", "4 o", // らりるれろ40-44
	"w a","w o","N\\","","",// わをん 45-49
	"g a", "g' i", "g M", "g e", "g o",//がぎぐげご 50-54
	"dz a", "dZ i", "dz M", "dz e", "dz o",//ざじずぜぞ55-59
	"d a", "dZ i", "dz M", "d e", "d o",//だじづでど60-64
	"b a", "b' i", "b M", "b e", "b o",//ばびぶべぼ	65-69
        "p a", "p' i", "p M", "p e", "p o"//ぱぴぷぺぽ70-74
};

//歌詞
//int lylics[]={40}; //”ら”固定
int lylics[]={52,44,4,4,4,4,4, 4,4,4,4,4, 4,4,4,4,4, 41,0
              ,1,47, 3,7, 11,3,42, 11,12, 63,4
             ,52,44,4,4,4,4,4, 4,4,4,4,4, 4,4,4,4,4, 41,0
              ,1,47, 3,7, 11,3,42, 11,12, 63,3,4       };//あめのみつかいの

void loop(){

  //シリアル受信処理
  while(Serial.available()>0){
    swSerial.write((byte)Serial.read());//そのまま転送出力
  }

}

void set_lylic() {
  int lylic_len = sizeof(lylics) / sizeof(lylics[0]);
  lylic_send(lylic_len);
}

//lylic send
void lylic_send(int num){
  //hedaer
  swSerial.write((byte)0xF0);
  swSerial.write((byte)0x43);
  swSerial.write((byte)0x79);
  swSerial.write((byte)0x09);
  swSerial.write((byte)0x00);
  swSerial.write((byte)0x50);
  swSerial.write((byte)0x10);

  for(int i=0;i<num;i++){
    if(i != 0) swSerial.write((byte)0x2c);
    swSerial.write(phoneticSymbols[lylics[i]]);
  }
  //footer
  swSerial.write((byte)0x00);
  swSerial.write((byte)0xF7);

}

すぐにできる!Android用艦これプレイヤーを作ろう!

 Android  すぐにできる!Android用艦これプレイヤーを作ろう! はコメントを受け付けていません。
8月 282013
 

タイトルは釣りですが、ただのWebViewサンプルレベルなので。
本当のタイトルは「Webviewでflash動かすメモ」です。ごめんなさい。

初めに。
flashplayerはadobeのサイトから4.0用のapkを落として入れておく(4.3でも使えました)
ただGalaxyS4は、なぜかFlashPlayerが途中で止まってしまい、動きませんでしたのでダメぽいです。

手持ちでの動作確認済み:
GalaxyNote(4.1), GalaxyNexus(4.3), Nexus7(4.3), XperiaZ(4.1)

あとflashplayerの仕様なのか、タップするとダブルタップになるようで、補給とかチェックボックスがON→OFFってなります。
ロングタップすると良いです。

■前準備

File>New Project>Android – Android Application project
1
ICS以降を指定しました。ICS未満は絶滅した方がいい。

あとは全部デフォルトのまま進む。
ビルドしてサンプルが動き、Hello,worldが表示されることを確認。

■AndroidManifest
・通信が必要なのでandroid.permission.INTERNETをつける。
・MainActivityにandroid:screenOrientation=”sensorLandscape”つける(横向き固定にしたい場合)
・画面回転などで再描画かかると、WebViewがリロードされてしまうので
android:configChanges=”keyboardHidden|orientation|screenSize”を書いておく。

横画面固定にして念のためkeyboardHidden|orientation書いて安心してたら、画面ロック⇒復帰で再描画かかってハマったよ。screenSize書いたら大丈夫になった。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.akagiplus"
android:versionCode="1"
android:versionName="1.0" >

<uses-sdk
android:minSdkVersion="14"
android:targetSdkVersion="14" />
<uses-permission android:name="android.permission.INTERNET"/>

<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name="com.example.akagiplus.MainActivity"
android:label="@string/app_name"
android:screenOrientation="sensorLandscape"
android:configChanges="keyboardHidden|orientation|screenSize" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

■レイアウト
/res/layout/activity_main.xmlをひらく
Hello,worldのTextBox消して、全面WebViewにする。

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" >

    <WebView
android:id="@+id/webView1"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true" />
</RelativeLayout>

WebViewの余白が邪魔なので、android:paddingXXXXX4行は消したほうがいいです。

■MainActivity

/src配下のMainActivity.javaを開く。

●onCreate()で
・WebViewを取得、各種設定をする
・画面広くして表示したいページ(艦これのURL)を指定する。
・javascriptとflashplayer(別途インストール必要)を有効にする。
・UAをPCのchromeと同じに偽装(AndroidのUAだと艦これが動かないから)

onCreateの処理書けば、もうDMMのログインページが出るはず。
ログインしたらちゃんと遊べます。
device-2013-08-28-005410

public class MainActivity extends Activity {
	private WebView mWebView;
	private Activity mActivity;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		mActivity=this;

		// タイトルバーを消す(画面広くしたい)
		requestWindowFeature(Window.FEATURE_NO_TITLE);
		// ステータスバーを消す(画面広くしたい)
		getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);

		setContentView(R.layout.activity_main);

	    String url ="http://www.dmm.com/netgame/social/-/gadgets/=/app_id=854854/"; // 艦これのURL

	    //WebViewの各種設定
	    mWebView = (WebView) findViewById(R.id.webView1);
	    mWebView.setVerticalScrollbarOverlay(true);
	    mWebView.getSettings().setLoadWithOverviewMode(true);
	    mWebView.getSettings().setUseWideViewPort(true);

	    mWebView.getSettings().setPluginState(PluginState.ON); //flash
	    mWebView.getSettings().setJavaScriptEnabled(true); //js
	    mWebView.getSettings().setBuiltInZoomControls(true); //+-のズーム表示
	    mWebView.getSettings().setUserAgentString("Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.95 Safari/537.36");

	    mWebView.setWebViewClient(new WebViewClient());
	    mWebView.loadUrl(url);

	}

	@Override
	protected void onResume() {
       super.onResume();
       mWebView.onResume();
	}

	@Override
	protected void onPause() {
        mWebView.onPause();
	    super.onPause();
	}

	@Override
	protected void onDestroy(){
		mWebView.stopLoading();
		mWebView.setWebViewClient(null);
		mWebView.setWebChromeClient(null);
		mWebView.removeAllViews();
		mWebView.destroy();
		mWebView = null;
		super.onDestroy();
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}

}

●onResume()とonPause()
webviewに止まってね、動いてねと指示だししないと、電源キー押して画面消しても動き続けます。
電車内で「か・ん・こ・れ!」など鳴り響いたらたまりませんね?

●onDestroy()
しっかりWebViewを殺しておきます。

●その他
onCreateOptionsMenuは使ってないので消していいです。

艦コレは通信エラー起こると「リロードしてください」となるので、メニューにリロードを作ってみるのもいいでしょう。

あとバックキー塞いどかないと、間違って押してえらいことになりますので、塞いでおきましょう。

適当な場所に追記します。

    @Override
    public boolean onKeyDown( int keyCode, KeyEvent event ) {
        if ( event.getAction() == KeyEvent.ACTION_DOWN
                && keyCode == KeyEvent.KEYCODE_BACK ) {

        	//終了ダイアログ
        	AlertDialog.Builder alertDialog=new AlertDialog.Builder(this);

            // ダイアログの設定
            alertDialog.setTitle("確認");      //タイトル設定
            alertDialog.setMessage("終了するのです?");  //内容(メッセージ)設定

            alertDialog.setPositiveButton("応", new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int which) {
                    // OKボタン押下時の処理
                	mActivity.finish();
                }
            });
            alertDialog.setNegativeButton("否", new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int which) {
                	//なにもしない
                }
            });

            alertDialog.show();

            return true;
        }
        return super.onKeyDown( keyCode, event );
    }

device-2013-08-28-010402

ばっちりガード!

 

■今後の課題

flashplayerのインストールチェックして、なければ
Uri uri = Uri.parse(“http://helpx.adobe.com/jp/flash-player/kb/228683.html#main_Android_Flash_Player______”);
でブラウザ起動してあげると親切ですね。

あと艦これのページのソースgetして、flash部分だけ全画面表示するように書き換えると良いと思うけど、時間かかりそうだったので諦めました。