AndroidからPCへタップ座標値をUDPで送信（透過処理を使ったアイコンの切替）

■プログラムの概要
　・前回投稿（＊）の改良版です。透過処理により、１つのアイコン画像だけで、あたかも
　　２つのアイコンを**切り替えているように見せる**方法を紹介します。
　　背景画像にデフォルトの画像を描画しておき、その真上にアイコン画像（下記コードの　
　　**setAlpha**を検索下さい）を配置し、タップに応じて**透過制御**すればOKです。
　　（＊）https://qiita.com/voibow/items/bf8c66efbcfe803d5053
　・用途：電子楽器。
　　※YouTube動画リンク先：**https://www.youtube.com/watch?v=Y2-1u_X-9jc**

***
■Android側のプログラムのソースコード
　（補足）各種アイコン（piston_icon.png等）や背景画像（bg.jpg）は、事前にpptで作成。
　　　　　作成方法：https://qiita.com/voibow/items/6f273a903c648beff77d
```java:activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#fed"
    tools:context="com.example.Piston_20220619.MainActivity">

    <ImageView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:contentDescription="image1"
        android:scaleType="centerCrop"
        android:src="@drawable/bg" />

    <TextView
        android:id="@+id/text_view_apt"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="185dp"
        android:layout_marginLeft="110dp"
        android:textSize="50sp"
        app:layout_constraintDimensionRatio="w,1:1"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/text_view_cav"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="0dp"
        android:layout_marginLeft="25dp"
        android:textSize="50sp"
        app:layout_constraintDimensionRatio="w,1:1"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/text_view_flt"
        android:layout_width="100dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="0dp"
        android:layout_marginLeft="320dp"
        android:textSize="25sp"
        app:layout_constraintDimensionRatio="w,1:1"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/text_view_atk"
        android:layout_width="100dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="90dp"
        android:layout_marginLeft="320dp"
        android:textSize="25sp"
        app:layout_constraintDimensionRatio="w,1:1"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/text_view_oct"
        android:layout_width="100dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="180dp"
        android:layout_marginLeft="320dp"
        android:textSize="25sp"
        app:layout_constraintDimensionRatio="w,1:1"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

   <TextView
        android:id="@+id/text_view_pst1"
        android:layout_width="150dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="280dp"
        android:layout_marginLeft="100dp"
        android:textSize="27sp"
        app:layout_constraintDimensionRatio="w,1:1"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/text_view_pst2"
        android:layout_width="150dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="315dp"
        android:layout_marginLeft="100dp"
        android:textSize="27sp"
        app:layout_constraintDimensionRatio="w,1:1"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/text_view_x0"
        android:layout_width="100dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="243dp"
        android:layout_marginLeft="125dp"
        android:textSize="20sp"
        android:textColor="#ffffff"
        app:layout_constraintDimensionRatio="w,1:1"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/text_view_y0"
        android:layout_width="100dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="243dp"
        android:layout_marginLeft="225dp"
        android:textSize="20sp"
        android:textColor="#ffffff"
        app:layout_constraintDimensionRatio="w,1:1"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/text_view_x1"
        android:layout_width="100dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="62dp"
        android:layout_marginLeft="420dp"
        android:textSize="20sp"
        android:textColor="#ffffff"
        app:layout_constraintDimensionRatio="w,1:1"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/text_view_y1"
        android:layout_width="100dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="62dp"
        android:layout_marginLeft="520dp"
        android:textSize="20sp"
        android:textColor="#ffffff"
        app:layout_constraintDimensionRatio="w,1:1"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/text_view_x2"
        android:layout_width="100dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="155dp"
        android:layout_marginLeft="420dp"
        android:textSize="20sp"
        android:textColor="#ffffff"
        app:layout_constraintDimensionRatio="w,1:1"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/text_view_y2"
        android:layout_width="100dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="155dp"
        android:layout_marginLeft="520dp"
        android:textSize="20sp"
        android:textColor="#ffffff"
        app:layout_constraintDimensionRatio="w,1:1"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <com.example.Piston_20220619.CustomImageView
        android:id="@+id/image_view_glitter"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:layout_marginLeft="0dp"
        android:layout_marginTop="140dp"
        android:src="@drawable/glitter_icon"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <com.example.Piston_20220619.CustomImageView
        android:id="@+id/image_view_flutter"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:layout_marginLeft="350dp"
        android:layout_marginTop="0dp"
        android:src="@drawable/flutter_icon"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <com.example.Piston_20220619.CustomImageView
        android:id="@+id/image_view_attack"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:layout_marginLeft="350dp"
        android:layout_marginTop="95dp"
        android:src="@drawable/attack_icon"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <com.example.Piston_20220619.CustomImageView
        android:id="@+id/image_view_tona_left"
        android:layout_width="70dp"
        android:layout_height="70dp"
        android:layout_marginLeft="-20dp"
        android:layout_marginTop="277dp"
        android:src="@drawable/tona_left_icon"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <com.example.Piston_20220619.CustomImageView
        android:id="@+id/image_view_tona_right"
        android:layout_width="70dp"
        android:layout_height="70dp"
        android:layout_marginLeft="38dp"
        android:layout_marginTop="277dp"
        android:src="@drawable/tona_right_icon"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <com.example.Piston_20220619.CustomImageView
        android:id="@+id/image_view_piston1"
        android:layout_width="92dp"
        android:layout_height="100dp"
        android:layout_marginLeft="251dp"
        android:layout_marginTop="275dp"
        android:src="@drawable/piston_icon"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <com.example.Piston_20220619.CustomImageView
        android:id="@+id/image_view_piston2"
        android:layout_width="92dp"
        android:layout_height="100dp"
        android:layout_marginLeft="373dp"
        android:layout_marginTop="275dp"
        android:src="@drawable/piston_icon"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <com.example.Piston_20220619.CustomImageView
        android:id="@+id/image_view_piston3"
        android:layout_width="92dp"
        android:layout_height="100dp"
        android:layout_marginLeft="495dp"
        android:layout_marginTop="275dp"
        android:src="@drawable/piston_icon"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
   </RelativeLayout>
```

```java:MainActivity.java
//参照サイト：https://www.wabiapp.com/WabiSampleSource/windows/udp_server.html

package com.example.Piston_20220619;

import androidx.appcompat.app.AppCompatActivity;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import android.widget.TextView;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

public class MainActivity extends AppCompatActivity
        implements View.OnTouchListener {

    // 各種変数宣言
    CustomImageView[] cImageView = new CustomImageView[5];
    TextView textView_apt, textView_cav, textView_flt;
　　TextView textView_atk, textView_oct, textView_pst1, textView_pst2;
    TextView textView_x0,textView_y0,textView_x1,textView_y1;//デバッグ用途
    TextView textView_x2,textView_y2;//デバッグ用途
    // タップ座標、エリア
    int[] newDx = new int[10];    int[] newDy = new int[10];
    int[] preDx = new int[10];    int[] preDy = new int[10];
    int[] max_x = new int[10];    int[] max_y = new int[10];
    int[] min_x = new int[10];    int[] min_y = new int[10];
    int tap_area;  boolean oct_button;
    boolean tona_left_state = false;    boolean tona_right_state = false;
    int piston_mode = 0, piston_state = 0;
    // パソコンに送信する値
    int posi_apt, posi_cav, posi_flt, posi_atk, posi_oct, stat_pst=0;
    String str_apt, str_cav, str_flt, str_atk, str_oct, str_pst1, str_pst2;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // 横画面に固定する
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
        // 画像表示
        textView_apt = findViewById(R.id.text_view_apt);
        textView_cav = findViewById(R.id.text_view_cav);
        textView_flt = findViewById(R.id.text_view_flt);
        textView_atk = findViewById(R.id.text_view_atk);
        textView_oct = findViewById(R.id.text_view_oct);
        textView_pst1 = findViewById(R.id.text_view_pst1);
        textView_pst2 = findViewById(R.id.text_view_pst2);
        cImageView[0] = this.findViewById(R.id.image_view_glitter);
        cImageView[1] = this.findViewById(R.id.image_view_flutter);
        cImageView[2] = this.findViewById(R.id.image_view_attack);
        cImageView[3] = this.findViewById(R.id.image_view_octave0);
        cImageView[3].setAlpha(0.0f);//0：透明
        cImageView[4] = this.findViewById(R.id.image_view_tona_left);
        cImageView[5] = this.findViewById(R.id.image_view_tona_right);
        cImageView[4].setAlpha(0.0f);//0：透明
        cImageView[5].setAlpha(0.0f);//0：透明
        cImageView[6] = this.findViewById(R.id.image_view_piston1);
        cImageView[7] = this.findViewById(R.id.image_view_piston2);
        cImageView[8] = this.findViewById(R.id.image_view_piston3);
        cImageView[6].setAlpha(0.0f);//0：透明
        cImageView[7].setAlpha(0.0f);//0：透明
        cImageView[8].setAlpha(0.0f);//0：透明
        cImageView[0].setOnTouchListener(this);
        cImageView[1].setOnTouchListener(this);
        cImageView[2].setOnTouchListener(this);
        cImageView[3].setOnTouchListener(this);
        cImageView[4].setOnTouchListener(this);
        cImageView[5].setOnTouchListener(this);
        cImageView[6].setOnTouchListener(this);
        cImageView[7].setOnTouchListener(this);
        cImageView[8].setOnTouchListener(this);
        // デバッグ表示用の変数定義
        textView_x0 = findViewById(R.id.text_view_x0);
        textView_y0 = findViewById(R.id.text_view_y0);
        textView_x1 = findViewById(R.id.text_view_x1);
        textView_y1 = findViewById(R.id.text_view_y1);
        textView_x2 = findViewById(R.id.text_view_x2);
        textView_y2 = findViewById(R.id.text_view_y2);
        // 初期設定（画面の各エリア範囲を規定）
        max_x[0]=720; min_x[0]=200; max_y[0]=700; min_y[0]=200;//glitterエリア
        max_x[1]=1600; min_x[1]=1300; max_y[1]=200; min_y[1]=100;//flutterエリア
        max_x[2]=1600; min_x[2]=1300; max_y[2]=550; min_y[2]=350;//attackエリア
        max_x[3]=1600; min_x[3]=1300; max_y[3]=750; min_y[3]=650;//octaveエリア
        max_x[4]=100; min_x[4]=0; max_y[4]=1200; min_y[4]=850;//tonality_leftエリア
        max_x[5]=650; min_x[5]=500; max_y[5]=1200; min_y[5]=850;//tonality_rightエリア
        max_x[6]=1100; min_x[6]=950; max_y[6]=1200; min_y[6]=850;//piston1エリア
        max_x[7]=1400; min_x[7]=1200; max_y[7]=1200; min_y[7]=850;//piston2エリア
        max_x[8]=1700; min_x[8]=1500; max_y[8]=1200; min_y[8]=850;//piston3エリア
        tap_area = -1;//どのエリアでもない
        oct_button = false;// 通常ピッチ（octシフトしていない）
        // 初期表示
        String str_apt = "apt=" + 100;
        textView_apt.setText(str_apt);
        String str_cav = "cav=" + 100;
        textView_cav.setText(str_cav);
        String str_flt = "flt=" + 0;
        textView_flt.setText(str_flt);
        String str_atk = "atk=" + 0;
        textView_atk.setText(str_atk);
        String str_oct = "oct=" + 0;
        textView_oct.setText(str_oct);
        str_pst1 = "オートマ";
        textView_pst1.setTextColor(Color.rgb(188, 226, 232));//水色
        textView_pst1.setText(str_pst1);
    }

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        //タップ座標＠アイコンの取得、各アイコンのエリア規定
        newDx[0] = Math.min(Math.max((int)event.getRawX(),min_x[0]),max_x[0]);
        newDy[0] = Math.min(Math.max((int)event.getRawY(),min_y[0]),max_y[0]);
        newDx[1] = Math.min(Math.max((int)event.getRawX(),min_x[1]),max_x[1]);
        newDy[1] = Math.min(Math.max((int)event.getRawY(),min_y[1]),max_y[1]);
        newDx[2] = Math.min(Math.max((int)event.getRawX(),min_x[2]),max_x[2]);
        newDy[2] = Math.min(Math.max((int)event.getRawY(),min_y[2]),max_y[2]);
        newDx[3] = Math.min(Math.max((int)event.getRawX(),min_x[3]),max_x[3]);
        newDy[3] = Math.min(Math.max((int)event.getRawY(),min_y[3]),max_y[3]);
        newDx[4] = Math.min(Math.max((int)event.getRawX(),min_x[4]),max_x[4]);
        newDy[4] = Math.min(Math.max((int)event.getRawY(),min_y[4]),max_y[4]);
        newDx[5] = Math.min(Math.max((int)event.getRawX(),min_x[5]),max_x[5]);
        newDy[5] = Math.min(Math.max((int)event.getRawY(),min_y[5]),max_y[5]);
        newDx[6] = Math.min(Math.max((int)event.getRawX(),min_x[6]),max_x[6]);
        newDy[6] = Math.min(Math.max((int)event.getRawY(),min_y[6]),max_y[6]);
        newDx[7] = Math.min(Math.max((int)event.getRawX(),min_x[7]),max_x[7]);
        newDy[7] = Math.min(Math.max((int)event.getRawY(),min_y[7]),max_y[7]);
        newDx[8] = Math.min(Math.max((int)event.getRawX(),min_x[8]),max_x[8]);
        newDy[8] = Math.min(Math.max((int)event.getRawY(),min_y[8]),max_y[8]);

        if(newDx[0]<max_x[0] && newDx[0]>=min_x[0] && newDy[0]<max_y[0] && newDy[0]>=min_y[0]) {
            tap_area = 0;//glitterエリア
            // 【デバッグ表示】newDxなど
            String str_x0 = "x0=" + newDx[0];
            textView_x0.setText(str_x0);
            String str_y0 = "y0=" + newDy[0];
            textView_y0.setText(str_y0);
        }
        else if(newDx[1]<max_x[1] && newDx[1]>=min_x[1] && newDy[1]<max_y[1] && newDy[1]>=min_y[1]) {
            tap_area = 1;//flutterエリア
            // 【デバッグ表示】newDxなど
            String str_x1 = "x1="  + newDx[1];
            textView_x1.setText(str_x1);
            String str_y1 = "y1=" + newDy[1];
            textView_y1.setText(str_y1);
        }
        else if(newDx[2]<max_x[2] && newDx[2]>=min_x[2] && newDy[2]<max_y[2] && newDy[2]>=min_y[2]) {
            tap_area = 2;//attackエリア
            // 【デバッグ表示】newDxなど
            String str_x2 = "x2="  + newDx[2];
            textView_x2.setText(str_x2);
            String str_y2 = "y2=" + newDy[2];
            textView_y2.setText(str_y2);
        }
        else if(newDx[3]<max_x[3] && newDx[3]>=min_x[3] && newDy[3]<max_y[3] && newDy[3]>=min_y[3]) {
            tap_area = 3;//octaveエリア
        }
        else if(newDx[4]<max_x[4] && newDx[4]>=min_x[4] && newDy[4]<max_y[4] && newDy[4]>=min_y[4]) {
            tap_area = 4;//tonality_leftエリア
        }
        else if(newDx[5]<max_x[5] && newDx[5]>=min_x[5] && newDy[5]<max_y[5] && newDy[5]>=min_y[5]) {
            tap_area = 5;//tonality_rightエリア
        }
        else if(newDx[6]<max_x[6] && newDx[6]>=min_x[6] && newDy[6]<max_y[6] && newDy[6]>=min_y[6]) {
            tap_area = 6;//piston1エリア
        }
        else if(newDx[7]<max_x[7] && newDx[7]>=min_x[7] && newDy[7]<max_y[7] && newDy[7]>=min_y[7]) {
            tap_area = 7;//piston2エリア
        }
        else if(newDx[8]<max_x[8] && newDx[8]>=min_x[8] && newDy[8]<max_y[8] && newDy[8]>=min_y[8]) {
            tap_area = 8;//piston3エリア
        }
        else {
            tap_area = -1;//どのエリアでもない
        }

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:// オクターブ切替のみタッチ制御にする
                if(tap_area == 3) {
                    if(oct_button) {
                        cImageView[3].setImageResource(R.drawable.oct_down_icon);
                        cImageView[3].setAlpha(0.0f);//0：透明 ～ 1：非透明
                        oct_button = !oct_button;
                        posi_oct = 0;
                    }
                    else {
                        cImageView[3].setImageResource(R.drawable.oct_down_icon);
                        cImageView[3].setAlpha(1.0f);//0：透明 ～ 1：非透明
                        oct_button = !oct_button;
                        posi_oct = 1;
                    }
                   // パラメータ値の画面表示
                    String str_oct = "oct=" + posi_oct;
                    textView_oct.setText(str_oct);
                }
                else if(tap_area == 4) {//tonality_leftをON
                     tona_left_state = true;
                }
                else if(tap_area == 5) {//tonality_rightをON
                    tona_right_state = true;
                }
                else if(tap_area == 6 && piston_mode >= 0x80) {//piston1をON＠マニュアルモード
                    piston_state |= 4;
                    cImageView[6].setAlpha(1.0f);//0：透明 ～ 1：非透明
                }
                else if(tap_area == 7 && piston_mode >= 0x80) {//piston2をON＠マニュアルモード
                    piston_state |= 2;
                    cImageView[7].setAlpha(1.0f);//0：透明 ～ 1：非透明
                }
                else if(tap_area == 8 && piston_mode >= 0x80) {//piston3をON＠マニュアルモード
                    piston_state |= 1;
                    cImageView[8].setAlpha(1.0f);//0：透明 ～ 1：非透明
                }
                // piston_mode判定 ※ACTION_DOWNの瞬間に判定する（ACTION_UPでは判定しない）。
                if(tap_area == 4 || tap_area == 5) {
                    // マニュアルモードに設定
                    if ((tona_left_state && !tona_right_state) || (!tona_left_state && tona_right_state)) {
                        cImageView[4].setAlpha(1.0f);//0：透明 ～ 1：非透明
                        cImageView[5].setAlpha(1.0f);//0：透明 ～ 1：非透明
                        if(piston_mode == 0) { piston_mode = 0x80+6; } //A#3(B♭)
                        else {
                            if(tap_area == 4) { piston_mode = Math.max(piston_mode-1,0x80); }
                            else { piston_mode = Math.min(piston_mode+1,0x80+11); }
                        }
                        switch(piston_mode & 0x7F) {//MSb：マニュアルフラグ
                            case 0: str_pst2 = "key=E3"; break;
                            case 1: str_pst2 = "key=F3"; break;
                            case 2: str_pst2 = "key=F#3"; break;
                            case 3: str_pst2 = "key=G3"; break;
                            case 4: str_pst2 = "key=G#3"; break;
                            case 5: str_pst2 = "key=A3"; break;
                            case 6: str_pst2 = "key=A#3(B♭)"; break;
                            case 7: str_pst2 = "key=B3"; break;
                            case 8: str_pst2 = "key=C4"; break;
                            case 9: str_pst2 = "key=C#4"; break;
                            case 10: str_pst2 = "key=D4"; break;
                            case 11: str_pst2 = "key=D#4"; break;
                            default: str_pst2 = "設定失敗"; break;
                        }
                        str_pst1 = "マニュアル";
                        textView_pst1.setTextColor(Color.rgb(255, 255, 0));//黄色
                        textView_pst1.setText(str_pst1);
                        textView_pst2.setTextColor(Color.rgb(255, 255, 0));//黄色
                        textView_pst2.setText(str_pst2);

                    }
                    // オートマモードに設定 ＠tona_leftとtona_right両方ON
                    else {
                        piston_mode = 0;
                        cImageView[4].setAlpha(0.0f);//0：透明 ～ 1：非透明
                        cImageView[5].setAlpha(0.0f);//0：透明 ～ 1：非透明
                        str_pst1 = "オートマ";
                        textView_pst1.setTextColor(Color.rgb(188, 226, 232));//水色
                        textView_pst1.setText(str_pst1);
                        str_pst2 = "";
                        textView_pst2.setText(str_pst2);
                    }
                }
//                Log.d("★","piston_mode="+Integer.toBinaryString(piston_mode));
                break;
            case MotionEvent.ACTION_UP:
                if(tap_area == 4) {//tonality_leftをOFF
                    tona_left_state = false;
                }
                if(tap_area == 5) {//tonality_rightをOFF
                    tona_right_state = false;
                }
                if(tap_area == 6) {//piston1をOFF
                    piston_state &= 3;
                    cImageView[6].setAlpha(0.0f);//0：透明 ～ 1：非透明
                }
                if(tap_area == 7) {//piston2をOFF
                    piston_state &= 5;
                    cImageView[7].setAlpha(0.0f);//0：透明 ～ 1：非透明
                }
                if(tap_area == 8) {//piston3をOFF
                    piston_state &= 6;
                    cImageView[8].setAlpha(0.0f);//0：透明 ～ 1：非透明
                }
                break;
            case MotionEvent.ACTION_MOVE:
                int dx,dy,imgW,imgH;
                // glitterエリアでの処理
                if(tap_area == 0) {
                    // 画像タップ位置を決定
                    dx = cImageView[0].getLeft() + (newDx[0] - preDx[0]);
                    dy = cImageView[0].getTop() + (newDy[0] - preDy[0]);
                    imgW = dx + cImageView[0].getWidth();
                    imgH = dy + cImageView[0].getHeight();
                    cImageView[0].layout(dx, dy, imgW, imgH);
                    // 画像タップ位置に基づき、パラメータ値を生成（線形補間）
                    float nume_apt=(float)(newDx[0]-min_x[0]);//分子
                    float deno_apt=(float)(max_x[0]-min_x[0]);//分母
                    float tilt_apt=-50;//傾き
                    float inter_apt=100;//切片
                    float nume_cav=(float)(newDy[0]-min_y[0]);//分子
                    float deno_cav=(float)(max_y[0]-min_y[0]);//分母
                    float tilt_cav=50;//傾き
                    float inter_cav=50;//切片
                    posi_apt = (int)Math.round((tilt_apt*nume_apt/deno_apt+inter_apt));//100~50を表示
                    posi_cav = (int)Math.round((tilt_cav*nume_cav/deno_cav+inter_cav));//50-100を表示
                    // パラメータ値の画面表示
                    String str_apt = "apt=" + posi_apt;
                    textView_apt.setText(str_apt);
                    String str_cav = "cav=" + posi_cav;
                    textView_cav.setText(str_cav);
//                    Log.d("●onTouch●", "ACTION_MOVE: dx=" + dx + ", dy=" + dy);
                }
                // フラッターエリアの処理
                else if(tap_area == 1) {
                    // 画像タップ位置を決定
                    dx = cImageView[1].getLeft() + (newDx[1] - preDx[1]);
                    dy = cImageView[1].getTop() + (newDy[1] - preDy[1]);
                    imgW = dx + cImageView[1].getWidth();
                    imgH = dy + cImageView[1].getHeight();
                    cImageView[1].layout(dx, dy, imgW, imgH);
                    // 画像タップ位置に基づき、パラメータ値を生成（線形補間）
                    float nume_flt=(float)(newDx[1]-min_x[1]);//分子
                    float deno_flt=(float)(max_x[1]-min_x[1]);//分母
                    float tilt_flt=10;//傾き
                    float inter_flt=0;//切片
                    posi_flt = (int)Math.round((tilt_flt*nume_flt/deno_flt+inter_flt));//0~10を表示
                    // パラメータ値の画面表示
                    String str_flt = "flt=" + posi_flt;
                    textView_flt.setText(str_flt);
                }
                // アタックエリアの処理
                else if(tap_area == 2) {
                    // 画像タップ位置を決定
                    dx = cImageView[2].getLeft() + (newDx[2] - preDx[2]);
                    dy = cImageView[2].getTop() + (newDy[2] - preDy[2]);
                    imgW = dx + cImageView[2].getWidth();
                    imgH = dy + cImageView[2].getHeight();
                    cImageView[2].layout(dx, dy, imgW, imgH);
                    // 画像タップ位置に基づき、パラメータ値を生成（線形補間）
                    float nume_atk=(float)(newDx[2]-min_x[2]);//分子
                    float deno_atk=(float)(max_x[2]-min_x[2]);//分母
                    float tilt_atk=10;//傾き
                    float inter_atk=0;//切片
                    posi_atk = (int)Math.round((tilt_atk*nume_atk/deno_atk+inter_atk));//0~10を表示
                    // パラメータ値の画面表示
                    String str_atk = "atk=" + posi_atk;
                    textView_atk.setText(str_atk);
                }
                break;
            default:
                break;
        }// switch (event.getAction())
        // stat_pstの生成
        stat_pst = piston_mode | piston_state<<4;
        // タッチした位置を古い位置とする
        // アイコン座標の移動処理を行うエリアのみ下記処理を行う。
        if(tap_area == 0) {
            preDx[0] = newDx[0];
            preDy[0] = newDy[0];
        }
        else if(tap_area == 1) {
            preDx[1] = newDx[1];
            preDy[1] = newDy[1];
        }
        else if(tap_area == 2) {
            preDx[2] = newDx[2];
            preDy[2] = newDy[2];
        }

        // UDP送信スレッド
        new Thread(new Runnable() {
            public void run() {
                try {
                    // ソケットオープン
                    DatagramSocket sendUdpSocket = new DatagramSocket();
                    InetAddress IPAddress = InetAddress.getByName(“hogehoge");
                    int port = hogehoge;//ポート番号
                    // 送信フォーム：apt,cav,flt
                    String[] s = new String[5];
                    s[0] = String.valueOf(posi_apt);
                    s[1] = String.valueOf(posi_cav);
                    s[2] = String.valueOf(posi_flt);
                    s[3] = String.valueOf(posi_atk);
                    s[4] = String.valueOf(posi_oct);
                    s[5] = String.valueOf(stat_pst);
                    String str = s[0] + "," + s[1]+ "," + s[2] + "," + s[3] + "," + s[4] + "," + s[5];
                    byte[] sendData = str.getBytes("UTF-8");

                    // 送信先と送信データを設定 
                    DatagramPacket sendPacket = new DatagramPacket(
                            sendData, sendData.length, IPAddress, port);
                    // テキストデータを送信
                    sendUdpSocket.send(sendPacket);
                    // ソケットを閉じる　
                    sendUdpSocket.close();

                } catch (IOException e) {
                    System.out.println(e.getMessage());
                }
            }
        }).start();
        // 以上、UDP送信
        return true;
    }
}
```

■PC側のプログラムのソースコード
```c:udp_receive.cpp(winapi)
//参照サイト①：https://www.wabiapp.com/WabiSampleSource/windows/udp_server.html
//参照サイト②：https://www.kana-soft.com/tech/sample_0004.htm
//注意：▲部分はUDP処理と無関係なので、皆様の仕様に合わせて記述下さい。

//一般的なヘッダー
#include <windows.h>
#include <windowsX.h>
#include <stdio.h>
//▲その他、必要に応じて

//UDP関連
#pragma comment( lib, "ws2_32.lib" )//ライブラリ
#define WM_WINSOCKEVENT ( WM_USER + 100 )// WinSockからのイベント

//関数プロトタイプ、変数
static LRESULT CALLBACK	SoundProc( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam );
static void InitDialog( HWND hDlg );//▲
static HWND AppWnd;//▲アプリケーションのウインドウハンドル
static SOCKET oSocket;//UDP関連
//UDP受信するパラメータ
static float udp_apt, udp_cav, udp_flt, udp_atk, udp_oct;
static int udp_pst;
//▲その他、必要に応じて

int WINAPI	WinMain( HINSTANCE hInst, HINSTANCE hPreInst, LPSTR CmdLine, int show )
{
	//▲
	AppWnd   = NULL;
	DialogBox( hInst, MAKEINTRESOURCE(IDD_DIALOG_MAIN), NULL, (DLGPROC)SoundProc );
	return 0;
}

static LRESULT CALLBACK	SoundProc( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam )
{
	switch( msg ){
	case WM_INITDIALOG://初期化全般
		InitDialog(hDlg);//▲ダイアログボックスの初期化

		//UDP初期化
		WSAData wsaData;
		WSAStartup(MAKEWORD(2, 0), &wsaData);   //(2, 0)はwinsockのバージョン
		//ソケットの生成
		oSocket = socket(AF_INET, SOCK_DGRAM, 0);  //AF_INETはIPv4、SOCK_DGRAMはUDP通信
		// ソケットを非同期にする
		WSAAsyncSelect(oSocket, hDlg, WM_WINSOCKEVENT, FD_READ | FD_CLOSE);
		// アドレス等格納
		struct sockaddr_in oSockAddr;  //IPv4
		oSockAddr.sin_family = AF_INET;
		oSockAddr.sin_port = htons(hogehoge);   //通信ポート番号設定
		oSockAddr.sin_addr.S_un.S_addr = INADDR_ANY; // INADDR_ANY：全アドレス受信
		// バインド
		::bind(oSocket, (struct sockaddr*)&oSockAddr, sizeof(oSockAddr));
		OutputDebugString("hogehoge");
		break;

	case WM_WINSOCKEVENT://UDPデータ受信
	{
		switch (WSAGETSELECTEVENT(lParam)) {
		case FD_READ:
		{
			struct sockaddr_in oFromAddr;
			int sockaddr_in_size = sizeof(struct sockaddr_in);
			CHAR szData[30];//Android側からの単位送信長は30キャラ未満
			//受信
			::recvfrom(
				oSocket
				, (char*)szData
				, sizeof(szData) - 1
				, 0
				, (struct sockaddr*)&oFromAddr
				, &sockaddr_in_size
				);
			//カンマ区切りの受信データを変数にストア
			//受信データは、[数値(apt),数値(cav),数値(flt)･･･]の形式で受信
			sscanf(szData, "%f ,%f ,%f ,%f ,%f, %d", &udp_apt, &udp_cav, &udp_flt, &udp_atk, &udp_oct, &udp_pst);
			//apertureによる華やかさの制御
			float apt, cav;//いずれも50～100の範囲
			apt = (-0.02*udp_apt + 2)*2;//0～2の範囲
			synth.midi_aperture_event(apt);//音色制御処理のコール
			//cavityによる華やかさの制御
			cav = -0.02*udp_cav + 4;//2～3の範囲
			synth.midi_cavity_event(cav);//音色制御処理のコール
			// フラッター制御
			float flt;
			flt = udp_flt*0.1;//udp_flt：0～10　→　fltゲイン：0～1
			synth.midi_flutter_event(flt);
			// アタック制御
			float atk;
			atk = udp_atk*0.8;//udp_atk：0～10 → 0～8
			synth.midi_attack_event(atk);
			// オクターブ制御
			float oct;
			oct = udp_oct;//udp_ock：0/1
			synth.midi_octave_event(oct);
			// ピストン制御
			int pst;
			pst = udp_pst;
			synth.midi_piston_event(udp_pst);
//			OutputDebugString(szData);//★受信データの表示
		}
		break;
		}
	}
	break;
	//▲その他処理は必要に応じてcase文で追加（ダイアログボックスの制御など）
	default:
		break;
	}
	return FALSE;
}
```

***
■動作結果
【Android端末側】
・下記スクリーンショット①②の、下辺部分を見比べてください。①は起動直後の表示態様です。水色の◁▷や、リリース状態のピストンアイコンは、**事前に背景画像（bg.jpg）に描画**されたものであり、（視認できませんが）それらの手前側に②の下辺部分に示した**透明／非透明制御できるアイコンが配置**されてます（起動時は透明）。◁▷部分をタッチすることで、前記透明アイコンが非透明化され、黄色の◁▷が出現します。同様にピストン部分をタッチすることで、押下状態のピストンが出現します。すなわち、各アイコン毎に**２種類のアイコンを準備／切替制御しなくとも**、「背景画像」と「アイコンの透過処理」のコンビネーションで、あたかも２つのアイコンが**切り替わったかのように表示**することが可能となるわけです。

　●スクリーンショット①
![起動時のスマホ画面（背景画像）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2659855/134e32eb-17b3-0938-9a19-c971648b4954.png)
　●スクリーンショット②
![マニュアルモード時の画面.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2659855/353cf78c-3fae-ab15-f1ad-83122b426cd1.png)
・（余談）下記リンク先のYouTube動画のように、マニュアルモード（調性：B♭）にて、ピストンを操作しながら演奏ができます。マニュアルモードは運指を覚えないといけないので面倒ですが、音階移動時の不連続感（「バジングピッチ」と「管長制御」の時間的ずれ）を表現できます。一方、当該ずれをなくしスムーズに（歌うように）演奏したい場合はオートマモードの方が向いていると思います。
※YouTube動画リンク先：：**https://www.youtube.com/watch?v=Y2-1u_X-9jc**


【PC側】　
・注意：Android（スマホ）とPCのWiFiをONにして下さい。
・送信確認ツール（Wireshark）を起動し、上記通りAndroidを
　タップするとUDP送信ログが表示されます。
・winapiコードの「★受信データの表示」の行のコメントアウトを
　外し、各種アイコンをタップして動かすと、
　VisualStudioの出力ウインドウに、下記のような表示が出ればOKです。
　表示例：90,95,3,5,0,134ﾌﾌﾌﾌﾌﾌﾌﾌﾌ88,90,0,1,1,168ﾌﾌﾌﾌﾌﾌﾌﾌﾌ
・あとは、取得した数値(90等)を使って、各自が作成された音処理系
　に渡せば（音色はともかく）同じような動作が実現できるはずです。
・通信不良の場合、PCのIPアドレスやポート番号（hogehogeと記載した
　部分）の設定ミス、WiFi未接続などが原因と思われます。
　なおIPアドレスは、固定アドレス化しておいた方が便利です。

***
■今後の課題
winapi側（含む音処理）すべてをAndroid側にJNIベースで集約していく予定です。
これにより、Android端末とピンマイクだけで電子楽器を実現できるはずですが、
レイテンシが許容範囲に収まるかどうかが最大の懸念事項です。