2017年12月

とりあえず、お決まりの"Hello World"だ。

fun main(args:Array<String>) {
    println("Hello world.")
}

ふむふむ、ちょっと読み解いていく。

関数宣言はfunではじめて関数名(引数リスト)みたいだ。
変数の型は変数名のあとに:につづけて書く。
Javaだと、メインクラスでmainメソッドを用意するするんだけどC言語みたいにmain関数を用意するみたいだ。
println関数が出てくるけど、importやincludeみたいなのがないんだよな~
調べてみるとprintlnはkotlin.ioパッケージに含まれていて、デフォルトでインポートされているようで、他にも

kotlin.*
kotlin.annotation.*
kotlin.collections.*
kotlin.comparisons.*
kotlin.io.*
kotlin.ranges.*
kotlin.sequences.*
kotlin.text.*

が、インポートされている。

あと、分のあとの;いらん。あってもエラーにならないのでつい付けてしまっても問題なさそう。1行に複数の文を書くときは、;で区切る。


このエントリーをはてなブックマークに追加 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
ブログランキング・にほんブログ村へ
にほんブログ村

特定の要素を全画面(フルスクリーン)にするFullscreen API
このエントリーをはてなブックマークに追加 Clip to Evernote
ブログランキング・にほんブログ村へ
にほんブログ村

[React][TypeScript] Hello World的なもの(1)は、動きもなくてつまらないので、他でも作ってるボタンを押したら「こんにちは!」って表示するやつを作ってみた。

003

コンポーネントにする必要なさそうなんだけど、「ボタンをおして!」を表示しているコンポーネントを作る。(Label.tsx)

import * as React from 'react';

export interface LabelProps {
    text:string;
}

// ラベルのコンポーネント
export class Label extends React.Component<LabelProps, {}>  {
    render() {
        return <span>{this.props.text}</span>;
    }
}

React.Componentを継承してLabelコンポーネントを作る。React.Componentのテンプレートで指定する1番目の型がpopsの型で2番目の型がstateの型になる。

ボタンとラベルコンポーネントがあるルートになるAppコンポーネントを作る。(App.tsx)

import * as React from 'react';
import { Label } from "./Label";

interface AppState {
    message:string;
}

export class App extends React.Component<{}, AppState>  {
    constructor(props:any) {
        super(props);
        this.state = {
            message:"ボタンを押して!"
        };

        this.onClick = this.onClick.bind(this);
    }

    // ボタンを押した時の処理
    onClick(e:any) {
        this.setState({message:"こんにちは!"});
    }
    render() {
        return <div>
            <p><Label text={this.state.message} /></p>
            <p><button type="button" onClick={this.onClick}>Button</button></p>
            </div>;
    }
}

ボタンを押した時の処理でthis.setStateをして表示するメッセージを更新している。
これを表示するhtmlファイルを作る。(index.html)

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
</head>
<body>
    <div id="root"></div>
    <script src="dist/app.js"></script>
</body>
</html>

htmlのrootにAppを割り当てるところを用いする。(index.tsx)

import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { App } from "./App";

ReactDOM.render(
    <App />,
    document.getElementById('root')
);

ソース以外のビルド環境は[React][TypeScript] Hello World的なもの(1)で作ったのを使う。




↑このページのトップヘ