0
0

More than 1 year has passed since last update.

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

Last updated at Posted at 2022-07-18

■プログラムの概要
 ・前回投稿(*)の改良版です。透過処理により、1つのアイコン画像だけで、あたかも
  2つのアイコンを切り替えているように見せる方法を紹介します。
  背景画像にデフォルトの画像を描画しておき、その真上にアイコン画像(下記コードの 
  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

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>
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側のプログラムのソースコード

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)に描画されたものであり、(視認できませんが)それらの手前側に②の下辺部分に示した透明/非透明制御できるアイコンが配置されてます(起動時は透明)。◁▷部分をタッチすることで、前記透明アイコンが非透明化され、黄色の◁▷が出現します。同様にピストン部分をタッチすることで、押下状態のピストンが出現します。すなわち、各アイコン毎に2種類のアイコンを準備/切替制御しなくとも、「背景画像」と「アイコンの透過処理」のコンビネーションで、あたかも2つのアイコンが切り替わったかのように表示することが可能となるわけです。

 ●スクリーンショット①
起動時のスマホ画面(背景画像).png
 ●スクリーンショット②
マニュアルモード時の画面.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端末とピンマイクだけで電子楽器を実現できるはずですが、
レイテンシが許容範囲に収まるかどうかが最大の懸念事項です。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0