音を創るということ6(ソフト作成編)
プログラムの作成2回目です。おさらいのためにプログラムを見返していますが、もっと簡単にできないのかとか、出来るプログラマーだったら、〇〇指向とか〇〇プログラミングで見通しのよい作り方をするんだろうかとか、本当に余計なことを考えてしまいます。
■voiceパラメータのMAX値テーブル
//**************************************************************
//Table voice_max[165]
//**************************************************************
const uint8_t voice_max[165]={
99, // voice[0]= ; OP6_EG_RATE1
99, // voice[1]= ; OP6_EG_RATE2
99, // voice[2]= ; OP6_EG_RATE3
99, // voice[3]= ; OP6_EG_RATE4
99, // voice[4]= ; OP6_EG_LEVEL1
99, // voice[5]= ; OP6_EG_LEVEL2
99, // voice[6]= ; OP6_EG_LEVEL3
99, // voice[7]= ; OP6_EG_LEVEL4
99, // voice[8]= ; OP6_KEY_BOARD_LEVEL_SCALE_BREAK_POINT
99, // voice[9]= ; OP6_KEY_BOARD_LEVEL_SCALE_LEFT_DEPTH
99, // voice[10]= ; OP6_KEY_BOARD_LEVEL_SCALE_RIGHT_DEPTH
3, // voice[11]= ; OP6_KEY_BOARD_LEVEL_SCALE_LEFT_CURVE
3, // voice[12]= ; OP6_KEY_BOARD_LEVEL_SCALE_RIGHT_CURVE
7, // voice[13]= ; OP6_KEY_BOARD_RATE_SCALLING
3, // voice[14]= ; OP6_MOD SENSITIVITY_AMPLITUDE
7, // voice[15]= ; OP6_OPERATOR_KEY_VELOCITY_SENSITIVITY
99, // voice[16]= ; OP6_OPERATOR_OUTPUT_LEVEL
1, // voice[17]= ; OP6_OSCILLATOR_MODE
31, // voice[18]= ; OP6_OSCILLATOR_FREQUENCY_COARSE
99, // voice[19]= ; OP6_OSCILLATOR_FREQUENCY_FINE
14, // voice[20]= ; OP6_OSCILLATOR_DETUNE
99, // voice[21]= ; OP5_EG_RATE1
99, // voice[22]= ; OP5_EG_RATE2
99, // voice[23]= ; OP5_EG_RATE3
99, // voice[24]= ; OP5_EG_RATE4
99, // voice[25]= ; OP5_EG_LEVEL1
99, // voice[26]= ; OP5_EG_LEVEL2
99, // voice[27]= ; OP5_EG_LEVEL3
99, // voice[28]= ; OP5_EG_LEVEL4
99, // voice[29]= ; OP5_KEY_BOARD_LEVEL_SCALE_BREAK_POINT
99, // voice[30]= ; OP5_KEY_BOARD_LEVEL_SCALE_LEFT_DEPTH
99, // voice[31]= ; OP5_KEY_BOARD_LEVEL_SCALE_RIGHT_DEPTH
3, // voice[32]= ; OP5_KEY_BOARD_LEVEL_SCALE_LEFT_CURVE
3, // voice[33]= ; OP5_KEY_BOARD_LEVEL_SCALE_RIGHT_CURVE
7, // voice[34]= ; OP5_KEY_BOARD_RATE_SCALLING
3, // voice[35]= ; OP5_MOD SENSITIVITY_AMPLITUDE
7, // voice[36]= ; OP5_OPERATOR_KEY_VELOCITY_SENSITIVITY
99, // voice[37]= ; OP5_OPERATOR_OUTPUT_LEVEL
1, // voice[38]= ; OP5_OSCILLATOR_MODE
31, // voice[39]= ; OP5_OSCILLATOR_FREQUENCY_COARSE
99, // voice[40]= ; OP5_OSCILLATOR_FREQUENCY_FINE
14, // voice[41]= ; OP5_OSCILLATOR_DETUNE
99, // voice[42]= ; OP4_EG_RATE1
99, // voice[43]= ; OP4_EG_RATE2
99, // voice[44]= ; OP4_EG_RATE3
99, // voice[45]= ; OP4_EG_RATE4
99, // voice[46]= ; OP4_EG_LEVEL1
99, // voice[47]= ; OP4_EG_LEVEL2
99, // voice[48]= ; OP4_EG_LEVEL3
99, // voice[49]= ; OP4_EG_LEVEL4
99, // voice[50]= ; OP4_KEY_BOARD_LEVEL_SCALE_BREAK_POINT
99, // voice[51]= ; OP4_KEY_BOARD_LEVEL_SCALE_LEFT_DEPTH
99, // voice[52]= ; OP4_KEY_BOARD_LEVEL_SCALE_RIGHT_DEPTH
3, // voice[53]= ; OP4_KEY_BOARD_LEVEL_SCALE_LEFT_CURVE
3, // voice[54]= ; OP4_KEY_BOARD_LEVEL_SCALE_RIGHT_CURVE
7, // voice[55]= ; OP4_KEY_BOARD_RATE_SCALLING
3, // voice[56]= ; OP4_MOD SENSITIVITY_AMPLITUDE
7, // voice[57]= ; OP4_OPERATOR_KEY_VELOCITY_SENSITIVITY
99, // voice[58]= ; OP4_OPERATOR_OUTPUT_LEVEL
1, // voice[59]= ; OP4_OSCILLATOR_MODE
31, // voice[60]= ; OP4_OSCILLATOR_FREQUENCY_COARSE
99, // voice[61]= ; OP4_OSCILLATOR_FREQUENCY_FINE
14, // voice[62]= ; OP4_OSCILLATOR_DETUNE
99, // voice[63]= ; OP3_EG_RATE1
99, // voice[64]= ; OP3_EG_RATE2
99, // voice[65]= ; OP3_EG_RATE3
99, // voice[66]= ; OP3_EG_RATE4
99, // voice[67]= ; OP3_EG_LEVEL1
99, // voice[68]= ; OP3_EG_LEVEL2
99, // voice[69]= ; OP3_EG_LEVEL3
99, // voice[70]= ; OP3_EG_LEVEL4
99, // voice[71]= ; OP3_KEY_BOARD_LEVEL_SCALE_BREAK_POINT
99, // voice[72]= ; OP3_KEY_BOARD_LEVEL_SCALE_LEFT_DEPTH
99, // voice[73]= ; OP3_KEY_BOARD_LEVEL_SCALE_RIGHT_DEPTH
3, // voice[74]= ; OP3_KEY_BOARD_LEVEL_SCALE_LEFT_CURVE
3, // voice[75]= ; OP3_KEY_BOARD_LEVEL_SCALE_RIGHT_CURVE
7, // voice[76]= ; OP3_KEY_BOARD_RATE_SCALLING
3, // voice[77]= ; OP3_MOD SENSITIVITY_AMPLITUDE
7, // voice[78]= ; OP3_OPERATOR_KEY_VELOCITY_SENSITIVITY
99, // voice[79]= ; OP3_OPERATOR_OUTPUT_LEVEL
1, // voice[80]= ; OP3_OSCILLATOR_MODE
31, // voice[81]= ; OP3_OSCILLATOR_FREQUENCY_COARSE
99, // voice[82]= ; OP3_OSCILLATOR_FREQUENCY_FINE
14, // voice[83]= ; OP3_OSCILLATOR_DETUNE
99, // voice[84]= ; OP2_EG_RATE1
99, // voice[85]= ; OP2_EG_RATE2
99, // voice[86]= ; OP2_EG_RATE3
99, // voice[87]= ; OP2_EG_RATE4
99, // voice[88]= ; OP2_EG_LEVEL1
99, // voice[89]= ; OP2_EG_LEVEL2
99, // voice[90]= ; OP2_EG_LEVEL3
99, // voice[91]= ; OP2_EG_LEVEL4
99, // voice[92]= ; OP2_KEY_BOARD_LEVEL_SCALE_BREAK_POINT
99, // voice[93]= ; OP2_KEY_BOARD_LEVEL_SCALE_LEFT_DEPTH
99, // voice[94]= ; OP2_KEY_BOARD_LEVEL_SCALE_RIGHT_DEPTH
3, // voice[95]= ; OP2_KEY_BOARD_LEVEL_SCALE_LEFT_CURVE
3, // voice[96]= ; OP2_KEY_BOARD_LEVEL_SCALE_RIGHT_CURVE
7, // voice[97]= ; OP2_KEY_BOARD_RATE_SCALLING
3, // voice[98]= ; OP2_MOD SENSITIVITY_AMPLITUDE
7, // voice[99]= ; OP2_OPERATOR_KEY_VELOCITY_SENSITIVITY
99, // voice[100]= ; OP2_OPERATOR_OUTPUT_LEVEL
1, // voice[101]= ; OP2_OSCILLATOR_MODE
31, // voice[102]= ; OP2_OSCILLATOR_FREQUENCY_COARSE
99, // voice[103]= ; OP2_OSCILLATOR_FREQUENCY_FINE
14, // voice[104]= ; OP2_OSCILLATOR_DETUNE
99, // voice[105]= ; OP1_EG_RATE1
99, // voice[106]= ; OP1_EG_RATE2
99, // voice[107]= ; OP1_EG_RATE3
99, // voice[108]= ; OP1_EG_RATE4
99, // voice[109]= ; OP1_EG_LEVEL1
99, // voice[110]= ; OP1_EG_LEVEL2
99, // voice[111]= ; OP1_EG_LEVEL3
99, // voice[112]= ; OP1_EG_LEVEL4
99, // voice[113]= ; OP1_KEY_BOARD_LEVEL_SCALE_BREAK_POINT
99, // voice[114]= ; OP1_KEY_BOARD_LEVEL_SCALE_LEFT_DEPTH
99, // voice[115]= ; OP1_KEY_BOARD_LEVEL_SCALE_RIGHT_DEPTH
3, // voice[116]= ; OP1_KEY_BOARD_LEVEL_SCALE_LEFT_CURVE
3, // voice[117]= ; OP1_KEY_BOARD_LEVEL_SCALE_RIGHT_CURVE
7, // voice[118]= ; OP1_KEY_BOARD_RATE_SCALLING
3, // voice[119]= ; OP1_MOD SENSITIVITY_AMPLITUDE
7, // voice[120]= ; OP1_OPERATOR_KEY_VELOCITY_SENSITIVITY
99, // voice[121]= ; OP1_OPERATOR_OUTPUT_LEVEL
1, // voice[122]= ; OP1_OSCILLATOR_MODE
31, // voice[123]= ; OP1_OSCILLATOR_FREQUENCY_COARSE
99, // voice[124]= ; OP1_OSCILLATOR_FREQUENCY_FINE
14, // voice[125]= ; OP1_OSCILLATOR_DETUNE
99, // voice[126]= ; PITCH_EG_RATE1
99, // voice[127]= ; PITCH_EG_RATE2
99, // voice[128]= ; PITCH_EG_RATE3
99, // voice[129]= ; PITCH_EG_RATE4
99, // voice[130]= ; PITCH_EG_LEVEL1
99, // voice[131]= ; PITCH_EG_LEVEL2
99, // voice[132]= ; PITCH_EG_LEVEL3
99, // voice[133]= ; PITCH_EG_LEVEL4
31, // voice[134]= ; ALGORITHM_SELECT
7, // voice[135]= ; FEED_BACK
1, // voice[136]= ; OSCILLATOR_SYNC
99, // voice[137]= ; LFO_SPEED
99, // voice[138]= ; LFO_DELAY
99, // voice[139]= ; LFO_PMD
99, // voice[140]= ; LFO_AMD
1, // voice[141]= ; LFO_SYNC
4, // voice[142]= ; LFO_WAVE
7, // voice[143]= ; MOD_SENSITIVITY_PITCH
48, // voice[144]= ; TRANSPOSE
0x7f, // voice[145]= ; VOICE_NAME1
0x7f, // voice[146]= ; VOICE_NAME2
0x7f, // voice[147]= ; VOICE_NAME3
0x7f, // voice[148]= ; VOICE_NAME4
0x7f, // voice[149]= ; VOICE_NAME5
0x7f, // voice[150]= ; VOICE_NAME6
0x7f, // voice[151]= ; VOICE_NAME7
0x7f, // voice[152]= ; VOICE_NAME8
0x7f, // voice[153]= ; VOICE_NAME9
0x7f, // voice[154]= ; VOICE_NAME10
0x3F, // voice[155]= ; OPERATOR_ON/OFF
// D6:0 D5:OP1 D4:OP2 D3:OP3 D2:OP4 D1:OP5 D0:OP6
//以降はこの機器用のパラメータ
5, // voice[156] ;selectされたOPナンバー op1:5 op6:0
1, // voice[157] ;OP6
1, // voice[158] ;OP5
1, // voice[159] ;OP4
1, // voice[160] ;OP3
1, // voice[161] ;OP2
1, // voice[162] ;OP1
99, // voice[163] ;None
99, // voice[164] ;None
};
//**************************************************************
●DX7の取扱説明書の31ページに載っている各パラメータのMAX値をテーブルにしています。右回しでMAXで止めるようにしています。左回し一杯はもちろん0です。
●パラメータの156番以降はこの装置内だけで使用するために追加しました。パラメータ163と164は、レイアウト図の上から4個目、左から2個目RE用です。今回は使用していませんが、メニュー切り替え用に利用しようとしています。パラメータ157〜162もOPの操作用に確保したのですが、今のところ予定なしです。
●コメントが途切れていますが、取扱説明書を丸写ししただけです。
●パラメータの156番以降はこの装置内だけで使用するために追加しました。パラメータ163と164は、レイアウト図の上から4個目、左から2個目RE用です。今回は使用していませんが、メニュー切り替え用に利用しようとしています。パラメータ157〜162もOPの操作用に確保したのですが、今のところ予定なしです。
●コメントが途切れていますが、取扱説明書を丸写ししただけです。
■voice初期データのテーブル
//*************************************************************
// Table voice_init[165 ] voice初期データ
//*************************************************************
const uint8_t voice_init[165]={
0x63,0x63,0x63,0x63,0x63,0x63,0x63,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x63,0x63,0x63,
0x63,0x63,0x63,0x63,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x01,0x00,0x00,0x63,0x63,0x63,0x63,0x63,0x63,
0x63,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,
0x00,0x63,0x63,0x63,0x63,0x63,0x63,0x63,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x63,0x63,
0x63,0x63,0x63,0x63,0x63,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x63,0x63,0x63,0x63,0x63,
0x63,0x63,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x63,
0x00,0x01,0x00,0x00,0x63,0x63,0x63,0x63,0x32,0x32,0x32,0x32,
0x00,0x00,0x01,0x23,0x00,0x00,0x00,0x01,0x00,0x03,0x18,0x20,
0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3F, 0, 1, 1, 1,
1, 1, 1, 0, 0,
};
//*************************************************************
●電源投入時の最初のデータです。これをベースに設定値を書き換えていくのですが、電源投入時にDX7からのデータを受け付ければもっと音作りが楽になりそう。
●voice_maxとかvoice_initとかパラメータのデータが多いです。構造体を使えばもっとすっきりした形になるのだろうと思いつつ、最初の設計が試行錯誤で進んでいるので仕方ないね。開発初期段階の設計は本当に重要なのですよ。
●実はこのデータはDX7の初期時のデータを参考にしています。こういうデータは著作権に関係するのかなとファイル名だけは変えましたけど。
●voice_maxとかvoice_initとかパラメータのデータが多いです。構造体を使えばもっとすっきりした形になるのだろうと思いつつ、最初の設計が試行錯誤で進んでいるので仕方ないね。開発初期段階の設計は本当に重要なのですよ。
●実はこのデータはDX7の初期時のデータを参考にしています。こういうデータは著作権に関係するのかなとファイル名だけは変えましたけど。
■リングバッファ
//*************************************************************
// write_buff( ) と Read_buff( )
//*************************************************************
#define MAX 256
uint8_t data3;
uint8_t data4;
void write_buff(uint8_t data3){
if(!(rd_index == (wr_index+1) % MAX)){
buff[wr_index++] = data3;
if(wr_index == MAX) wr_index = 0;
}
}
uint8_t Read_buff(void){
if(!(rd_index == wr_index)){
data4 = buff[rd_index];
++ rd_index;
}
if(rd_index == MAX) rd_index = 0;
return data4;
}
//*************************************************************
●間違いを発見しました。write_buff は小文字なのに Read_buff は大文字でした。そのまま行きます。
●入力は割り込みで行っているので、メインルーチンの処理ではバッファが必要です。MAXが256では短いかなと思ってます。
●リングバッファをもうひとつ作っていました。詳細はこの下を見てください。
●入力は割り込みで行っているので、メインルーチンの処理ではバッファが必要です。MAXが256では短いかなと思ってます。
●リングバッファをもうひとつ作っていました。詳細はこの下を見てください。
■ main()
//*************************************************************
void main(void){
//OSC_init();
OSCCONbits.IRCF = 7; // 8MHz
OSCTUNEbits.PLLEN = 1; // PLL(x6)
OSCCONbits.SCS = 0;
//IO_init();
// 0が出力、1が入力
TRISA = 0b00010000;
TRISB = 0b10001111;
UCONbits.USBEN = 0;
UCFGbits.UTRDIS = 1;
TRISC = 0b11111000;
// 0がアナログ、1がデジタル
ANCON0 = 0b11111111;
ANCON1 = 0b00011111;
INTCON2bits.RBPU = 0;
RPINR16 = 10;
RPOR9 = 6;
// PRESCALE(0-8):6
T0CONbits.TMR0ON = 1;
T0CONbits.T08BIT = 1; // 8-bit timer
T0CONbits.T0CS = 0; // use internal-OSC
if (PRESCALE == 0) {
T0CONbits.PSA = 1; // Not use prescaler
} else {
T0CONbits.PSA = 0; // use prescaler
T0CONbits.T0PS = PRESCALE - 1;
}
TMR0 = T0COUT; //T0COUT=181
INTCONbits.TMR0IE = 1;
INTCONbits.TMR0IF = 0;
INTCON2bits.TMR0IP = 0;
INTCONbits.PEIE = 1;
rmidi_index = 0;
tmidi_index = 0;
LATA = 0b11101111;
LATC = 0b00000111;
LATB = 0b00110000;
//UART_Initialize();
TXSTA2bits.SYNC = 0;
TXSTA2bits.BRGH = 0;
TXSTA2bits.TXEN = 1;
TXSTA2bits.TX9 = 0;
RCSTA2bits.SPEN = 1;
RCSTA2bits.RX9 = 0;
RCSTA2bits.CREN = 1;
BAUDCON2bits.BRG16 = 0;
BAUDCON2bits.RXDTP = 0;
BAUDCON2bits.TXCKP = 0;
SPBRG2 = 23 ; // ボーレートを31250bpsに設定
INTCONbits.PEIE = 1;
INTCONbits.GIE = 1;
//Init_buff(); //初期データを編集バッファへ転送
for(bufno = 0;bufno < 165; bufno++){
edit_voice[bufno] = voice_init[bufno];
}
SelectNo = voice_init[156]; //voiceNoにOP1をセット
while(1){
In_oprate();
DX7_task();
}
}
//**************************************************************
●メインの前半は初期設定です。特別なことは何もしていません。
●編集で使うvoice_edit[ ]に初期データ voice_init[ ]を転送します。これがないと操作時に微妙に苦労するのです。
●後半は無限ループで回していますが、スイッチ(SW)とロータリーエンコーダ(RE)の処理を行なうIn_oprate() と、 実際の動作を行なうDX7_task()で構成しています。
●編集で使うvoice_edit[ ]に初期データ voice_init[ ]を転送します。これがないと操作時に微妙に苦労するのです。
●後半は無限ループで回していますが、スイッチ(SW)とロータリーエンコーダ(RE)の処理を行なうIn_oprate() と、 実際の動作を行なうDX7_task()で構成しています。
■DX7_task( )
//**************************************************************
// DX7_task( )
//**************************************************************
void DX7_task(void ){
taskloop = paramcount;
for(i = 0;i < taskloop; i++){
midichar[i] = Read_midibuff();
while(TX2IF == 0);
TXREG2 = midichar[i];
}
}
//**************************************************************
uint8_t midi_modify[7]={0xf0,0x43,0b00010000,00,00,00,0xf7};
void StoreMidi(void){
uint8_t add1;
uint8_t add2;
uint8_t i;
add2 = param & 0b01111111;
add1 = (param >> 7) & 0b00000011;
midi_modify[3] = add1;
midi_modify[4] = add2;
midi_modify[5] = data;
for(i = 0;i < 7; i++){
Write_midibuff(midi_modify[i]);
}
}
//**************************************************************
void Write_midibuff(uint8_t data1){
if(!(rmidi_index == (tmidi_index + 1) % MAX)){
midibuff[tmidi_index++] = data1;
if(tmidi_index == MAX) tmidi_index = 0;
}
}
uint8_t Read_midibuff(void){
if(!(rmidi_index == tmidi_index)){
data2 = midibuff[rmidi_index];
++ rmidi_index;
}
if(rmidi_index == MAX) rmidi_index = 0;
return data2;
}
//**************************************************************
●さり気なく、Write_midibuff とか Read_midibuff とかの関数を書いていますが、リングバッファはパラメータ入力用とこのmidi用の2種類あったのでした。
■SW・REの入力処理
//**************************************************************
// In_oprate
//**************************************************************
void In_oprate(void){
uint8_t tmp;
uint8_t p;
uint8_t row; //行
uint8_t column; //列
uint8_t count;
uint8_t swcnt;
uint8_t swand;
uint8_t swnum;
uint8_t padr;
for(swcnt=0;swcnt < 2;swcnt++ ){
op_flag_old = op_flag;
swand = And[swcnt];
for(swnum = 0 ;swnum < 8 ;swnum = swnum + 2){
if(swcnt == 0 ){
//ONの処理
if((swand >> swnum) & 0b00000001){
op_flag |= (0b00001000 >> (swnum >> 1));
} else {
//OFFの処理
op_flag &= ~(0b00001000 >> (swnum >> 1));
}
}
if(swcnt == 1 && swnum > 3 ){
if((swand >> swnum) & 0b00000001 ){ //ONの処理
if(swnum == 4){
op_flag |= 0b00100000; //ch_no= 1 bit= 4,6
}
if(swnum == 6){
op_flag |= 0b00010000;
}
} else { //OFFの処理
if(swnum == 4){
op_flag &= ~(0b00100000);
}
if(swnum == 6){
op_flag &= ~(0b00010000);
}
}
}
}
if(op_flag ^ op_flag_old){
padr = 155;
edit_voice[155] = op_flag;
param = padr;
data = edit_voice[padr];
StoreMidi();
}
}
INTCONbits.TMR0IE = 0; //TMR0 disable
ch_no = Read_buff();
Rot_R = Read_buff();
Exor = Read_buff();
INTCONbits.TMR0IE = 1; //TMR0 enable
for(count = 0 ;count < 8;count = count + 2){
if(ch_no == 0 ||( ch_no == 1 && count > 3)) continue;
if((Exor >> count) & 0b00000001){
if((Rot_R >> count) & 0b00000001){
boundry[ch_no] |= (0b00000001 << count);
if(((Rot_R >> count) >> 1) & 0b00000001 ){
cw_ccw[ch_no] |= (0b00000010 << count);
} else {
cw_ccw[ch_no] |= (0b00000001 << count);
}
} else { //立ち下がり
boundry[ch_no] |= (0b00000001 << (count + 1));
if(((Rot_R >> count) >> 1) & 0b00000001 ){
cw_ccw[ch_no] |= (0b00000001 << count);
} else {
cw_ccw[ch_no] |= (0b00000010 << count);
}
}
}
}
for(column = 0;column < 8 ;column =column + 2) {
//いずれかの入力に変化があったら
if(boundry[ch_no]){
p=0;
if(ch_no > 5){ //row=6,7,8,9,10,11
SelectNo = edit_voice[156]; //OP1-OP6を選択
p = SelectNo * 21;
if(ch_no == 6) {
if( column > 4) p = 0;
}
if((ch_no == 8) && (column > 6)) p = 0;
}
padr = no[ch_no][column] + p;
Rotcnt[column] = edit_voice[padr];
if(((boundry[ch_no] >> column) & 0b00000011) == 1){
//CW
if(((cw_ccw[ch_no] >> column) & 0b00000011) == 1){
Rotcnt[column]++;
if(Rotcnt[column] > voice_max[padr]) {
Rotcnt[column] = voice_max[padr];
}
}
//CCW
if(((cw_ccw[ch_no] >> column) & 0b00000011) == 2){
Rotcnt[column]--;
if(Rotcnt[column] > voice_max[padr] ) {
Rotcnt[column] = 0;
}
}
if(edit_voice[padr] ^ Rotcnt[column]){
edit_voice[padr] = Rotcnt[column];
timerstart = 1; //次の入力があるとタイマーを
periodtimer = 0; //リセットし、再カウント
param = padr; //columnの必要な部分のみ送信
data = edit_voice[padr];
StoreMidi();
}
boundry[ch_no] &= ~(0b00000011 << column );
cw_ccw[ch_no] &= ~(0b00000011 << column );
}
}
}
boundry[ch_no] = 0;
cw_ccw[ch_no] = 0;
ppp = 0;
}
//**************************************************************
●前回のno[12][8]のテーブルを参照して、まず1行目と2行目の半分(bit4からbit7)にあるスイッチ(以下SW)について、ビットシフト演算により偶数ビットのSWの状態をみます。そのON/OFF状態をオペレータフラグ voice[155]に反映します。
●1行の中にSWとREが混在している上に、3行目以降はRE処理も書いているのでややこしいです。
●2行目の半分(bit0からbit3)以降はREのvoiceパラメータ確定と回転方向の判別・数値増減を行います。1行に4個のREがあるので、for文も0、2、4、6をキーにチェックします。
●余談ですが、C で for文は for ( ; ; )のスタイルですが、perlだと for ( 0, 2, 4, 6)という書き方もできます。変数に$をつけるとかの約束がありますが、Cに似ているので結構抵抗が少ないです。その分、つまづくことは何時もなんですが。しかし、最近、人気無いですね。
●for文の変数ですが、最初はswnum、その後 countとなり、すぐcolumnって統一性がないのまるわかり。これが試行錯誤プログラミングなのだ。
●セレクトされているOP番号が決まれば、簡単な計算でそのOP番号に関連するパラメータアドレスを算出しています。ややこしいことをせずにベタ書きの方がひょっとして良かったのではと思う今日このごろ。
●最後のほうでタイマをカウントしている部分がありますが、これはいわゆるウォッチドッグタイマで、REの数値を増減後の一定時間経過後にMIDIデータ(音楽データ)を送信することを考えています。そう、キーボード弾かなくてもいいのです。でも今回のテーマには沿わないので実装はしていません。
●ソースリストはこんな形よりもファイルをダウンロード出来るようにしておくのがいいですよね。このSSブログには簡単にできるような説明がありませんでした。Githubで登録するのを考えましたが時間がとれない。これからの課題にします。何しろ明日はセンター試験の2日目なんで・・・(別に関係ないけど)。
●1行の中にSWとREが混在している上に、3行目以降はRE処理も書いているのでややこしいです。
●2行目の半分(bit0からbit3)以降はREのvoiceパラメータ確定と回転方向の判別・数値増減を行います。1行に4個のREがあるので、for文も0、2、4、6をキーにチェックします。
●余談ですが、C で for文は for ( ; ; )のスタイルですが、perlだと for ( 0, 2, 4, 6)という書き方もできます。変数に$をつけるとかの約束がありますが、Cに似ているので結構抵抗が少ないです。その分、つまづくことは何時もなんですが。しかし、最近、人気無いですね。
●for文の変数ですが、最初はswnum、その後 countとなり、すぐcolumnって統一性がないのまるわかり。これが試行錯誤プログラミングなのだ。
●セレクトされているOP番号が決まれば、簡単な計算でそのOP番号に関連するパラメータアドレスを算出しています。ややこしいことをせずにベタ書きの方がひょっとして良かったのではと思う今日このごろ。
●最後のほうでタイマをカウントしている部分がありますが、これはいわゆるウォッチドッグタイマで、REの数値を増減後の一定時間経過後にMIDIデータ(音楽データ)を送信することを考えています。そう、キーボード弾かなくてもいいのです。でも今回のテーマには沿わないので実装はしていません。
●ソースリストはこんな形よりもファイルをダウンロード出来るようにしておくのがいいですよね。このSSブログには簡単にできるような説明がありませんでした。Githubで登録するのを考えましたが時間がとれない。これからの課題にします。何しろ明日はセンター試験の2日目なんで・・・(別に関係ないけど)。
飛ばして進めたので忘れていることが多いようで、まとまりがないです。一応、MIDIコントローラの話はここまで。家の中を探索すると梅酒とかりん酒がありました。寒くなってきたのでちょっとだけ飲んでみよう。
※このブログで書かれた内容によって生じた損害等の一切の責任を負いかねますので、予めご了承下さい。
音を創るということ5(ソフト設計&作成編)
今回からプログラムの作成にはいります。
■開発環境について
■開発環境について
●対象マイコンはPIC18F27J53 クロック:内部 48MHz駆動
●利用した内蔵モジュールはタイマー(TIMER0)割り込みとEUSART(MIDI出力用)という一般的なものなので、他のマイコンでも動作するはず(多分)です。
●MPLAB X IDEはv5.25@linux mint 18.3
●使用コンパイラはXC8 (v.1.45) v.2.10では不明のエラーが出ましたが、解決策はあるようです。
●ライターはPICKIT3を使用しています。PICKIT4も出ていますが、お安いということでこれにしました。アリババでも扱っています(別にまわし者では・・・・)、専用の書き込みアダプターなんてのが付属していますね。付録とか粗品とかの言葉に弱い。純正品かどうかは不明です。
keyword is microchip pickit3 programmer
●C文法およびMPLAB操作方法などはちゃんとした別のところを参照して下さい。何しろ、フローチャートなんて書かないで、思いついたらメモに書きなぐりぱなし、ネットのを丸写しなもんで(車輪は何度も発明するな・・・ですね)。
●開発時の反省点としては、コメントとドキュメントは絶対に必要だと思いました。コードを書いて、デバッガで実行し、結果をみては修正するという、いきあたりばったりの試行錯誤プログラミング ではちょっと前の記憶が飛びやすいです。
●利用した内蔵モジュールはタイマー(TIMER0)割り込みとEUSART(MIDI出力用)という一般的なものなので、他のマイコンでも動作するはず(多分)です。
●MPLAB X IDEはv5.25@linux mint 18.3
●使用コンパイラはXC8 (v.1.45) v.2.10では不明のエラーが出ましたが、解決策はあるようです。
●ライターはPICKIT3を使用しています。PICKIT4も出ていますが、お安いということでこれにしました。アリババでも扱っています(別にまわし者では・・・・)、専用の書き込みアダプターなんてのが付属していますね。付録とか粗品とかの言葉に弱い。純正品かどうかは不明です。
keyword is microchip pickit3 programmer
●C文法およびMPLAB操作方法などはちゃんとした別のところを参照して下さい。何しろ、フローチャートなんて書かないで、思いついたらメモに書きなぐりぱなし、ネットのを丸写しなもんで(車輪は何度も発明するな・・・ですね)。
●開発時の反省点としては、コメントとドキュメントは絶対に必要だと思いました。コードを書いて、デバッガで実行し、結果をみては修正するという、いきあたりばったりの試行錯誤プログラミング ではちょっと前の記憶が飛びやすいです。
■処理の流れ
簡単に言うと、タイマー割り込み毎に入力を読み込み、メインルーチンでデータの範囲を適正化したうえでmidiデータに加工し、UARTを通じてMIDIフォーマット通りに送信するプログラムです。
簡単に言うと、タイマー割り込み毎に入力を読み込み、メインルーチンでデータの範囲を適正化したうえでmidiデータに加工し、UARTを通じてMIDIフォーマット通りに送信するプログラムです。
以下のプログラムリストの説明は、忘れないように覚え書きのために書いています。なので、多少わかりにくいところがあるかもしれません。
■リスト内パラメータの説明
//**************************************************************
//parameter
//**************************************************************
●使用しているパラメータの説明
7654 3210
・Rot_RB = XXXX XXXX
|||| ||||_RB0
|||| |||__RB1
|||| ||___RB2
|||| |____RB3
||||______RC4
|||_______RC5
||________RC6
|_________RC7
・Rot_RB_OLD 一回前のRot_RBデータ
・Rot_RB_buff[ch_no]一回前のRot_RBデータch_noごとに保存
・boundry[ch_no]:A相の波形変化
7 6 5 4 3 2 1 0 00 変化なし
| | | | 01 立ち上がり
[3] [2] [1] [0] 10 立ち下がり
11 SW用
・cw_ccw[ch_no] :回転方向
7 6 5 4 3 2 1 0 00 変化なし
| | | | 01 cw 時計回り
[3] [2] [1] [0] 10 ccw 反時計回り
11 未使用
7654 3210
・op_flag = 00XX XXXX
|| ||||_OP6
|| |||__OP5
|| ||___OP4
|| |____OP3
||______OP2
|_______OP1
・voice_max[] 各ボイスパラメータの規定されている上限値
0〜155+a
param MIDI出力用ボイスパラメータのNo
data MIDI出力用ボイスパラメータのデータ
//**************************************************************
●パラメータの名称ですが、その時々の気分で変わっています。前々から関数やフラグ類の名称の付け方にはこだわりがあって、例えば
InputRotaryEncoderHogeDoit();
なんていうのがかたまりであると読み飛ばし、無視、挙句にわからなくなるというクセがあるので、その時の気分で名称をつけて済ますというスタイルなのです。でも、やはり、可読性を重視すべきでコメントとドキュメントは絶対に必要と今頃反省しています。
InputRotaryEncoderHogeDoit();
なんていうのがかたまりであると読み飛ばし、無視、挙句にわからなくなるというクセがあるので、その時の気分で名称をつけて済ますというスタイルなのです。でも、やはり、可読性を重視すべきでコメントとドキュメントは絶対に必要と今頃反省しています。
■割り込み処理
//**************************************************************
// Interrupt Routine
//**************************************************************
void interrupt InterTimer( void ){
if(INTCONbits.TMR0IF && INTCONbits.TMR0IE){
INTCONbits.TMR0IF = 0; //基本タイマーは400uS
TMR0 = T0COUT;
ReadPort();
prsc1ms++; //以下はタイマーで使用
prscdelay++;
if( prsc1ms > 5){
prsc1ms = 0;
prscdelay = 0;
}
}
}
//**************************************************************
●割り込み処理ルーチンで一番心配なのが設定通りにタイマーが動作しているかです。ここでこけると、皆こけたがこわい。特にpicマイコンは設定がわかりにくいので、ロジアナで最初に確認しました。(こう書くと何となくリッチマンのようですが、あくまでプアーマンです)。タイマーの設定については設定値を算出してくれる親切なサイトもあるようです。
キーワードは、PIC18 TIMER0 設定値 計算
●タイマー割り込み時間は400usとしました。 ReadPort()という関数でスイッチの入力を読み取ります。先だっての実験から、ロータリーエンコーダーA相の最速ONタイミングは約5.4msでした。その時、B相が約2.6ms間同じ状態を維持しているので、ノイズ対策として3回一致のタイミングだと合計して半分の1.3ms以下であれば確実に入力確定すると考えました。400usなら合計800usなので、十分余裕があります(多分)。でも、その後、ノイズ対策はこれでは意味がないと思い至りました。
キーワードは、PIC18 TIMER0 設定値 計算
●タイマー割り込み時間は400usとしました。 ReadPort()という関数でスイッチの入力を読み取ります。先だっての実験から、ロータリーエンコーダーA相の最速ONタイミングは約5.4msでした。その時、B相が約2.6ms間同じ状態を維持しているので、ノイズ対策として3回一致のタイミングだと合計して半分の1.3ms以下であれば確実に入力確定すると考えました。400usなら合計800usなので、十分余裕があります(多分)。でも、その後、ノイズ対策はこれでは意味がないと思い至りました。
■スイッチ・RE入力処理 ReadPort()
■テーブルの説明
//*************************************************************
void ReadPort(void){
for(Ich_no = 0;Ich_no < 12;Ich_no++){
Scan_out(Ich_no);
Rot_RB_OLD = Rot_RB_buff[Ich_no];
Rot_RB = (~PORTB) & 0b00001111; //RBの下位4ビット
Rot_RC = (~PORTC) & 0b11110000; //RCの上位4ビット
Rot_RB = Rot_RB | Rot_RC;
Rot_RB_buff[Ich_no] = Rot_RB;
if(Ich_no == 0){
And[Ich_no] = Rot_RB & Rot_RB_OLD; //0SWの判定用
continue;
}
if(Ich_no == 1){
And[Ich_no] = Rot_RB & Rot_RB_OLD; //1SWの判定用
}
IExor = (Rot_RB ^ Rot_RB_OLD);
if( IExor ){
write_buff(Ich_no); //これら4個のパラメータを
write_buff(Rot_RB); //リングバッファにストア
write_buff(IExor);
}
}
Scan_out(66); //0〜11以外の数字
}
//**************************************************************
//**************************************************************
// Scan_out()
//**************************************************************
void Scan_out( uint8_t sn){
LATA = 0b11101111;
LATB = 0b00110000;
LATC = 0b00000111;
switch(sn){
case 0: LATA0=0; break;
case 1: LATA1=0; break;
case 2: LATA2=0; break;
case 3: LATA3=0; break;
case 4: LATA5=0; break;
case 5: LATA6=0; break;
case 6: LATA7=0; break;
case 7: LATC0=0; break;
case 8: LATC1=0; break;
case 9: LATC2=0; break;
case 10:LATB4=0; break;
case 11:LATB5=0; break;
default:
LATA = 0b11101111;
LATC = 0b00000111;
LATB = 0b00110000;
}
}
//**************************************************************
●スイッチ・RE入力は12行を一気に読み込んでいます。その後、スイッチの前処理をしています。スイッチとロータリーエンコーダ(RE)の入力加工はメインの方で行います。割り込みルーチンの処理時間が心配でしたが、タイマー割り込み時間の400usより少なかったです。
●相変わらず()でくくるのが多いです。Cの優先順位が覚えられません。わからない時は()でくくるのが鉄則。以降、一向に反省する気配はないでしょう。
●相変わらず()でくくるのが多いです。Cの優先順位が覚えられません。わからない時は()でくくるのが鉄則。以降、一向に反省する気配はないでしょう。
■テーブルの説明
//**************************************************************
// no[ ][ ] table
//**************************************************************
const uint8_t no[12][8]= { {159,159,160,160,161,161,162,162},
{137,137,142,142,157,157,158,158},
{141,141,140,140,139,139,138,138},
{129,129,128,128,127,127,126,126},
{163,163,144,144,135,135,134,134},
{133,133,132,132,131,131,130,130},
{13,13,14,14,143,143,156,156},
{17,17,8,8,15,15,16,16},
{20,20,19,19,18,18,136,136},
{3,3,2,2,1,1,0,0},
{10,10,9,9,12,12,11,11},
{7,7,6,6,5,5,4,4} };
//*************************************************************
●switch文がお気に入りです。if文で書くと、何となくスマートではないのですね。整理、整頓、清掃、清潔が身についているからでしょうか? ・・・いいえ、単にコスパ最高だからなんですわ。
● パネル(ケース)のスイッチ・RE位置とプログラム上のスイッチ・REの関係をここで決めています。テーブルの数値は、DX7の取扱説明書のp.30のDX共通 voice Parameterの番号です。RE1個あたりに2バイト使っています。レイアウトを変更する時に使用します。
●このリストの説明は次回にします。なんせ、メインルーチンが複雑な上に、これにともなうテーブルがまたでかい。op1からop6までの処理をきれいにまとめることができなかったからなのです。
● パネル(ケース)のスイッチ・RE位置とプログラム上のスイッチ・REの関係をここで決めています。テーブルの数値は、DX7の取扱説明書のp.30のDX共通 voice Parameterの番号です。RE1個あたりに2バイト使っています。レイアウトを変更する時に使用します。
●このリストの説明は次回にします。なんせ、メインルーチンが複雑な上に、これにともなうテーブルがまたでかい。op1からop6までの処理をきれいにまとめることができなかったからなのです。
今日の作業はここまで。ホッピーを焼酎で割ったのを飲みたいけど、飲み屋に行くとすぐ忘れてしまう。カルピスサワーで乾杯。
※このブログで書かれた内容によって生じた損害等の一切の責任を負いかねますので、予めご了承下さい。
音を創るということ4(製作編)
■部品リスト
$1は110円で換算しています。
$1は110円で換算しています。
部品名 | 数量 | 単価 | 小計(円) |
PIC18F27J53 | 1 | 270 | 270 |
PC900V | 1 | $0.266/10個 | 3 |
78N05 | 1 | 30 | 30 |
NJM2845 入手したのはリード付きだった |
1
|
50 | 50 |
1N4148W | 97 | $0.75/100個 | 80 |
ロータリーエンコーダ EC12E2420801 | 42 |
80
|
3360 |
ユニバーサル基板 片面115×150 | 1 | 814 | 814 |
スイッチ | 6 | 80 | 480 |
抵抗入りTr DTD114EA デジトラ10K-10K | 4 | 10 | 40 |
XH3P ベースポスト | 52 | $2.09/100個 | 120 |
XH3P ハウジング | 52 | $0.61/50個 | 70 |
XH3P ピン | 156 | $0.92/100個 | 158 |
ICソケット 6P | 1 | 15 | 15 |
ICソケット 28P 300mil | 1 | 70 | 70 |
ピンヘッダー2P&ジャンパー | 1 | 15 | 15 |
ピンヘッダー6P | 1 | 20 | 20 |
電解コンデンサ 6.3V 22uF | 1 | 10 | 10 |
電解コンデンサ 16V 100uF | 1 | 10 | 10 |
電解コンデンサ 50V 10uF | 1 | 10 | 10 |
積層セラミック 50V 0.1uF | 4 | 15 | 60 |
SMD抵抗 220 EIAJで3216サイズ | 5 | $3.45/1250個 | 1.5 |
SMD抵抗 280 E96 系列なのでE12の270で可 | 1 | $3.45/1250個 | 0.3 |
SMD抵抗 10K | 11 | $3.45/1250個 | 3.4 |
スズメッキ線 実測すると3m未満でした | 1 | 250 | 250 |
ケース タカチ YM-200 | 1 | 1400 | 1400 |
DINソケット(メス)5P | 3 | 60 | 180 |
電源プラグΦ2.1 | 1 | 60 | 60 |
つまみ(RE用) | 42 | $0.54/10個 | 250 |
DC9V 電源アダプター | 1j |
580
|
580 |
電線 AWG28 3色で各7m程度 | 3 | 630 | 1890 |
RE用六角ナット M9×0.75×2.5mm | 42 | 571/50個 | 480 |
RE用ワッシャー 9×16×1.5mm | 42 | 497/50個 | 418 |
合計 | 11185.7 |
●手持ちの部品を多数使用したので、リストの値段は秋月電子通商などの現在での価格を調査しました。
●SMD抵抗(コンデンサも)は詰め合わせパックで買うのがお得です。80種類×25個パックなどがあります。
keyword is SMD 1206 resistor kit assorted set Capacitor
●リストを見ると、アリババ(中国)から買うのがベストみたいですが、発注してから部品が到着するまで50〜60日かかる場合があると表示されています。つまり、時間を買うことに価値があるのかを見極めることです。
●RE用六角ナットとワッシャーはREの位置決めタブをケースに出さないためのものです。
●念の為にですが、つまみを紛失した時等のためにメンテナンス用部品も確保しています。
●SMD抵抗(コンデンサも)は詰め合わせパックで買うのがお得です。80種類×25個パックなどがあります。
keyword is SMD 1206 resistor kit assorted set Capacitor
●リストを見ると、アリババ(中国)から買うのがベストみたいですが、発注してから部品が到着するまで50〜60日かかる場合があると表示されています。つまり、時間を買うことに価値があるのかを見極めることです。
●RE用六角ナットとワッシャーはREの位置決めタブをケースに出さないためのものです。
●念の為にですが、つまみを紛失した時等のためにメンテナンス用部品も確保しています。
片面ユニバーサル基板にXH3Pのベースポストを配置していきます。レイアウトは行で12個、列で4個となります。XH3Pからの電線が集中するので、各ベースポスト間は余裕を持って配置しました。でも、マイコン他の部品のスペースがなくなるのがこわいです。
マイコン、PC900Vは後々のメンテナンスを考えてICソケットを使用しました。
表面の画像に逆流防止のダイオードが見えません。実は裏側(蛇の目パターン面)にハンダ付けしています。ダイオードの電極間のピッチはカタログ値で3.7mmあります。一方、蛇の目パターンはランドも含めると4mm弱となりますので、蛇の目パターンにダイオードを載せるとちょうどよい寸法となります(下のイメージの感じ)。
ハンダ付けの際は、フラックスをはんだ面に塗り、爪楊枝で軽く押さえながらはんだ付けをします。
キーワードは、はけ付き フラックス
とはいえ、完成後にテスターで導通チェックをしたところ、5個もはんだ付け不良が発生していました。5/96の不良です。52083ppmの不良では製品としてはちょっとですね。
抵抗、コンデンサなら3216とか3225(もちろんmmサイズ)のチップサイズがちょうどよい大きさです
余談となりますが、チップ部品を扱うとき(特に1608サイズ以下)にピンセットでつまむことがあります。ところが、普通のピンセットではつまむ時にはじいてしまう事故が意外に多いのです。そんな時は、通常は閉じているピンセットを使いましょう。100均でも扱っているので見つけたら即ゲットです。
キーワードは、逆作用 ピンセット
とはいえ、完成後にテスターで導通チェックをしたところ、5個もはんだ付け不良が発生していました。5/96の不良です。52083ppmの不良では製品としてはちょっとですね。
抵抗、コンデンサなら3216とか3225(もちろんmmサイズ)のチップサイズがちょうどよい大きさです
余談となりますが、チップ部品を扱うとき(特に1608サイズ以下)にピンセットでつまむことがあります。ところが、普通のピンセットではつまむ時にはじいてしまう事故が意外に多いのです。そんな時は、通常は閉じているピンセットを使いましょう。100均でも扱っているので見つけたら即ゲットです。
キーワードは、逆作用 ピンセット
何度もしつこいほど言いますが、REとスイッチの穴数は48個、それにMIDI端子が3個、電源アダプター穴が1個で、計52個もあります。これをハンドドリルで穴を開け、その後リーマで穴を拡げるという作業をするのは趣味の範囲を超えています。
そんな時にカインズホームの通販ページを見ていると、振動ドリルがお買い得品として1480円で売り出しているのを見つけました。恥ずかしながら、それまで、振動ドリルに電気ドリルの機能があるとは知らなかったのです。(決してカインズホームのまわし者ではありません。ただの電子工作員です。)
同じ頃、パーツ店で1520円のミニホールソーを見つけました。これらでREの9mmの穴はあっという間に開けることができました。
キーワードは、振動ドリル、ミニホールソー
MIDI端子(DIN5P)の穴径は14.8mm以上とあります。まず9mmの穴を開け、その後リーマーで穴を拡げました。これくらいなら体力を消耗するなんてことはないです。
キーワードは、振動ドリル、ミニホールソー
MIDI端子(DIN5P)の穴径は14.8mm以上とあります。まず9mmの穴を開け、その後リーマーで穴を拡げました。これくらいなら体力を消耗するなんてことはないです。
ケースに部品を取り付けている時に大きな失敗に気づきました。なんと、スイッチとDIN 5Pが当たってしまうのです。あと5mmスイッチを下にずらしておけばよかった。
■電装
何度もしつこいほど言いますが、XH3Pが52個で電線は146本あります。カシメの工具は株式会社エンジニアのPA-21を使用します。1本につき2回かしめると、きれいな状態になるようです。これを続けて作業に慣れると、なんだか段々ハイな気分になっていくのです。これってcaulking highっていうのかな。
何度もしつこいほど言いますが、XH3Pが52個で電線は146本あります。カシメの工具は株式会社エンジニアのPA-21を使用します。1本につき2回かしめると、きれいな状態になるようです。これを続けて作業に慣れると、なんだか段々ハイな気分になっていくのです。これってcaulking highっていうのかな。
■つまみ
部品を集めるのに、REのつまみを探すのが一番苦労しました。リアルのパーツ屋さんや通販部品のサイトなど、「パネルを埋め尽くすような数のつまみ」に合うようなつまみがないのです(日本語むずかしい、探しきれていないだけ)。アリババで見つけようと決めました。つまみはGoogleの英訳でknobと出ています。これを出発点としてさまよった結果、なんとか見つけることができました。
keyword is 360 Degrees Rotary Encoder D Half Shaft Hole Caps Knob
物の名前がわからないとなかなか進めないです。〇〇レンズのアプリに期待。
部品を集めるのに、REのつまみを探すのが一番苦労しました。リアルのパーツ屋さんや通販部品のサイトなど、「パネルを埋め尽くすような数のつまみ」に合うようなつまみがないのです(日本語むずかしい、探しきれていないだけ)。アリババで見つけようと決めました。つまみはGoogleの英訳でknobと出ています。これを出発点としてさまよった結果、なんとか見つけることができました。
keyword is 360 Degrees Rotary Encoder D Half Shaft Hole Caps Knob
物の名前がわからないとなかなか進めないです。〇〇レンズのアプリに期待。
正月は暖かだったです。気分は、「春なのにコスモスみたい」です(・・・・ちょっと違うな)。さぁ、冷たいビールで乾杯。
※このブログで書かれた内容によって生じた損害等の一切の責任を負いかねますので、予めご了承下さい。