Android

Android Instant Appsとは | SEO研究所サクラサクラボ

へ〜、こんな昨日あるんだ(^^;)

このエントリーをはてなブックマークに追加 Clip to Evernote
ブログランキング・にほんブログ村へ
にほんブログ村

PSGぽい音を出すプログラムを作ってみた。ボタンを押すと440Hz(いわゆるラ)の音がなって、もう一度おすとまるようなプログラムだ。

package jp.hemohemo.testaudiotrack;

import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTrack;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {
    private AudioTrack audioTrack;

    private final int SAMPLING_RATE = 44100;        // サンプリング周波数
    private double angle = 0;                       // 音の角度?
    private boolean noteon = false;                 // trueの間音がなる。
    private final double freq = 440;                   // ならす音の周波数(440Hz,いわゆるラの音)
    private final short vol = 2000;                 // ボリューム
    private short []buf;                            // 波形データを入れるバッファ
    private final int frames = SAMPLING_RATE / 10;  // 100msのフレーム数(100ms単位でデータを更新することにする)

    private TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        textView = (TextView)findViewById(R.id.textView);

        // バッファの最低サイスを求める。
        int bufSize = AudioTrack.getMinBufferSize(SAMPLING_RATE,
                AudioFormat.CHANNEL_OUT_STEREO,
                AudioFormat.ENCODING_PCM_16BIT);

        // 最低200ms分のバッファになるようにbufSizeを調整 ... (1)
        if(bufSize < frames * 2 * 2 * 2) {
            // バッファサイズを調整
            bufSize = frames * 2 * 2 * 2;
        }
        System.out.println("bufSize:" + bufSize);

        // AudioTrackを用意 ... (2)
        // ↓このコンストラクタはAndroid 8.0(API Level 26)で非推奨になっている。
        audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,  // 音楽ストリーム
                    SAMPLING_RATE,      // サンプリングレート
                AudioFormat.CHANNEL_OUT_STEREO,      // ステレオ
                AudioFormat.ENCODING_PCM_16BIT,     // 16bit PCM
                bufSize,
                AudioTrack.MODE_STREAM);

        // 100ms単位でイベント(onPeriodicNotification)を発生するように設定する。
        audioTrack.setPositionNotificationPeriod(frames);

        // 波形を生成するメモリを用意(100ms分) ... (3)
        buf = new short[frames * 2];

        // イベントでの処理を設定
        audioTrack.setPlaybackPositionUpdateListener(new AudioTrack.OnPlaybackPositionUpdateListener() {
            @Override
            public void onMarkerReached(AudioTrack track) {
                System.out.println("onMarkerReached");
            }

            @Override
            public void onPeriodicNotification(AudioTrack track) {
                System.out.println("onPeriodicNotification");
                // 100ms分の波形データを用意する
                getData(buf);
                // AudioTrackに書き込む
                track.write(buf, 0, buf.length);
            }
        });

        // 波形をとりあえず生成する。
        getData(buf);
        // AudioTrackのバッファを埋める。 ... (4)
        // よくわからないが、これをしておかないと100msごとのイベントが発生しない。
        int ret;
        do {
            ret = audioTrack.write(buf, 0, buf.length);
        } while(ret == buf.length);
        
        textView.setText("OFF");

        // ボタンをクリックした時の処理を設定
        Button button = (Button)findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                noteon = !noteon;
                angle = 0;
                if(noteon) {
                    textView.setText("ON");
                } else {
                    textView.setText("OFF");
                }
            }
        });

        // AudioTrackを再生開始
        audioTrack.play();
    }

    // 波形データを用意する。 ... (5)
    private void getData(short []buf) {
        if(noteon) {
            for(int i = 0; i < buf.length / 2; i++) {
                if(angle < 180) {
                    buf[i * 2 + 0] = vol;       // 左チャンネル
                    buf[i * 2 + 1] = vol;       // 右チャンネル
                } else {
                    buf[i * 2 + 0] = (short)-vol;
                    buf[i * 2 + 1] = (short)-vol;
                }
                // angleに1フレームで動く角度を加算する
                angle += (double)360 * freq / SAMPLING_RATE;
                if(angle > 360) {
                    angle = angle - (int)(angle / 360) * 360;
                }
            }
        } else {
            for(int i = 0; i < buf.length; i++) {
                buf[i] = 0;
            }
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();

        if(audioTrack.getPlayState() == AudioTrack.PLAYSTATE_PLAYING) {
            audioTrack.stop();
            audioTrack.release();
        }
    }
}

(1) 最低200ms分のサイズを計算している。framesは100msのフレーム数なので2倍、さらにステレオなので2倍、さらに16ビットPCMなんで2倍してバッファサイズを計算している。
(2) コメントに書いているとおりコンストラクタはAndroid 8.0(API Level 26)で非推奨になっているが、最低レベルをAPI Level 14にしているので、まっ、いいか(^^;)
(3) ここもステレオなんで2倍。あと、shortの配列を用意しているので16ビットPCMなんで2倍はいらない。
(4) コメントにも書いたがAudioTrackのバッファを埋めとかないといけないみたい。
(5) ステレオなら配列の偶数バイト目が左チャンネルで奇数バイト目が右チャンネルになる。


このエントリーをはてなブックマークに追加 Clip to Evernote
ブログランキング・にほんブログ村へ
にほんブログ村

Android の Chrome で開発者ツールを使う方法 - Qiita
このエントリーをはてなブックマークに追加 Clip to Evernote
ブログランキング・にほんブログ村へ
にほんブログ村

こんな画面を作って、

001

上側のテキストボックスに文字を入力しようとするとソフトウエアキーボードが出て、下のBUTTONが隠れてしまう。

002

こんな感じにボタンを隠したくない場合は、

003

AndroidManifest.xmlの対象のactivityタグにandroid:windowSoftInputMode="adjustResize"を追加する。こんな感じ。

        <activity android:name=".MainActivity" android:windowSoftInputMode="adjustResize">

これで隠れなくなる。


このエントリーをはてなブックマークに追加 Clip to Evernote
ブログランキング・にほんブログ村へ
にほんブログ村

[Android] Android Stuido にjarライブラリーを追加 Jsoup HTML parser を試す | Androidアプリ開発
このエントリーをはてなブックマークに追加 Clip to Evernote
ブログランキング・にほんブログ村へ
にほんブログ村

↑このページのトップヘ