まるもの雑記

なにか作ったりとかゲームとか。

Javaによるwavファイルの書き出し

書き出しのしかた

javax.sound.sampled.AudioSystem

のwriteを使えば書けるようです。

書き出す前に長さを決めてデータを流し込むイメージで

ちょっと調べてみた感じだとstopまで書き出すみたいな使い方は出来なそうです。

内部的には指定した長さまでfor分を回しているようなのですが

その関数はprivateになっている。

 

AudioFormatを指定してから

audioInputStream = new AudioInputStream(bufInput, format, frameLenght)

AudioSystem.write(audioInputStream, Type.WAVE, outputFile)

という順番。

 

前作ったときはバイナリで中間ファイルを作ってから

ByteArrayInputStreamを使って出力してたのですが

OutputStreamとInputStreamを繋げる

PipedInputStream/PipedOutputStream

というのがあるようなので使ってみます

 

PipedInputStream/PipedOutputStream

こんな風に使う模様。

PipedOutputStream outputStream = new PipedOutputStream();
PipedInputStream inputStream = new PipedInputStream();
outputStream.connect(inputStream);

そのままだと遅いとのことなのでバッファリングします。

bufOutput = new BufferedOutputStream(outputStream);
bufInput = new BufferedInputStream(inputStream);

入出力両方行う必要はたぶんないと思いますが

これで無事動いているのであまり深く考えないようにする。

 

出力方法

readerを全ch1サンプルずつ読むように作ったので

書くほうも読んだ分書いていけるようにする。

 

形式を決めて new してから

put(values) していって

最後にclose

 

とりあえずこれでテスト信号が出来るようになったので

まずはよしとします。

 

public class WavFileWriter extends Thread {
    private AudioInputStream audioInputStream;
    private File outputFile;

    private BufferedOutputStream bufOutput = null;
    private BufferedInputStream bufInput = null;

    private int MAX_VALUE;
    private int MIN_VALUE;

    private ByteBuffer bb;

    private int byteOffset;
    private int sampleSizeInByte;

    private static final int INT_BYTE_LENGTH = 4;

    private int n;// write values

    public WavFileWriter(File output, AudioFormat format, long frameLenght) {
        
        this.outputFile = output;

        try {
            PipedOutputStream outputStream = new PipedOutputStream();
            PipedInputStream inputStream = new PipedInputStream();
            outputStream.connect(inputStream);
            bufOutput = new BufferedOutputStream(outputStream);
            bufInput = new BufferedInputStream(inputStream);

            int sampleSizeInBit = format.getSampleSizeInBits();
            this.sampleSizeInByte = sampleSizeInBit / 8;

            this.bb = ByteBuffer.allocate(INT_BYTE_LENGTH);
            if (format.isBigEndian()) {
                this.bb.order(ByteOrder.BIG_ENDIAN);
                this.byteOffset = INT_BYTE_LENGTH - sampleSizeInByte;
            } else {
                this.bb.order(ByteOrder.LITTLE_ENDIAN);
                this.byteOffset = 0;
            }

            MAX_VALUE = (int) (Math.pow(2, sampleSizeInBit) - 1);
            MIN_VALUE = (int) (-Math.pow(2, sampleSizeInBit));

            audioInputStream = new AudioInputStream(bufInput, format, frameLenght);
            this.start();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void close() {
        try {
            bufOutput.flush();
            bufOutput.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void putFrame(int values) throws IOException {
        for (int ch = 0; ch < values.length; ch++) {
            n = Math.min(MAX_VALUE, values[ch]);
            n = Math.max(MIN_VALUE, n);
            this.bb.putInt(0, n);
            bufOutput.write(bb.array(), byteOffset, sampleSizeInByte);
        }
    }

    public void run() {
        try {
            AudioSystem.write(audioInputStream, Type.WAVE, outputFile);
            audioInputStream.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

 

 

確認

自分で作ったビューアーとライターで試すのも不安なので

フリーの波形表示ソフトを探したのですが

フリーでマルチチャンネルの波形表示が出来るソフトはよいのがなさそうでした。

 

こんな感じでサイン波を作れます(たぶん)。

long  = (long) (lengthInSec * sampleFreq);
double w = signalFreq / sampleFreq * 2 * Math.PI;
int[] values = new int[chCount];
AudioFormat format = new AudioFormat((float) sampleFreq, SAMPLE_SIZE_IN_BIT, chCount, SIGNED, ISBIGENDIAN);

WavFileWriter writer = new WavFileWriter(output, format, frameLength);
double a = Math.pow(10, gainInDB / 20) * (Math.pow(2, SAMPLE_SIZE_IN_BIT - 1) - 1);

try {
    int n;
    for (long i = 0; i < frameLength; i++) {
        n = (int) (Math.sin(w * i) * a);
        for (int c = 0; c < chCount; c++) {
            values[c] = n;
        }
        writer.putFrame(values);
    }
    writer.close();
} catch (Exception e) {
    e.printStackTrace();
}

再生するとそれっぽく聞こえるので良しとします。

 

人に見せられるようなコードは書けませんが

コード例がないと検索から来ても役に立たないので

乗せるようにしました。

WAVEファイルビューア

先日WAVが読み込めるようになったので

いろいろやってみるためにもまずはビューアの作成を進めます。

 

特殊なことはなにもないのですが

グラフの表示とか拡大縮小とかマウスの処理がただただめんどくさいし

設計のようなことをしないで作っているのでバグだらけなので

触っては直すを繰り返します。

 

表示の仕方

・1pixelに1サンプル以上表示できれば線で繋ぐ

・1pixelに数サンプル入る場合は最大最小の値を四角で書く

正しいかは知りませんがよく見たことがあるグラフっぽくなるので

これでいいことにします。

 

全体表示

0dBの信号を画面いっぱいに表示するため何も見えません。

f:id:marumorix:20141230201308j:plain

拡大表示

それっぽい。

f:id:marumorix:20141230201309j:plain

 

窓のサイズ変えたり拡大したり最初と最後を見に行ったりひたすら

変なところを直す。

 

画面の真ん中の時間を基準にして

頭と最後は画面半分までデータのないところを出すようにしました。

手間はかかるけど見た目が楽しくない。

f:id:marumorix:20141230201655j:plain

コンテンツの長さ=サンプル数/サンプリング周波数

なので最後がちょっと変ですがよい解決案が思いつかないのでこのままにします。

f:id:marumorix:20141230201658j:plain

般音源

サイン波を見ていても何も楽しくないので

前回さわったログオンの音を読み込んでみる。

ピーク値で大体-22dB程度。割と音が小さい印象。

f:id:marumorix:20141230202033j:plain

頭部分にある微妙な波形は何であるんだろう。

f:id:marumorix:20141230202035j:plain

 

いまさら気づいたけどグラフにマウスカーソルがいる。

JavaでWAVファイル読み込み

久しぶりに時間が出来たので

音声ファイルを触ってみます。

 

フォーマットの確認

とりあえずwindows7のログオンの時の音を読み込んでみる

 

AudioInputStream input = AudioSystem.getAudioInputStream(file);
AudioFormat format = input.getFormat();

でフォーマットを取れるので

これをこんな関数に入れてみる。

    public static String getFormatString(AudioFormat format) {
        StringBuilder sb = new StringBuilder();

        sb.append("[AudioFormat] frameSize:" + format.getFrameSize() + "byte");
        sb.append(" Fs:" + format.getSampleRate() + "Hz");
        sb.append(" " + format.getSampleSizeInBits() + "bit");
        sb.append(" " + format.getChannels() + "ch");

        if (format.isBigEndian()) {
            sb.append(" BigEndian");
        } else {
            sb.append(" LittleEndian");
        }

        if (format.getEncoding() == AudioFormat.Encoding.PCM_SIGNED) {
            sb.append(" SignedPCM");
        } else if (format.getEncoding() == AudioFormat.Encoding.PCM_UNSIGNED) {
            sb.append(" UnsignedPCM");
        } else {
            sb.append(" NoPCM");
        }

        return sb.toString();
    }

[AudioFormat] frameSize:4byte Fs:44100.0Hz 16bit 2ch LittleEndian SignedPCM

これがWindowsの標準フォーマットらしい。

CDから取り込んだwavファイルも同じ結果になったので

CDと同じフォーマットを使っているようです。

サンプルデータ

普通の音声データだと正しく読み込めたかわからないので

テスト信号発生ソフト WaveGene

を使って0dBのサイン派を作ってそれを読み込みます。

 

コード

javaのintは4byteですが

サンプリングデータは16bitなので2byte

この変換がめんどくさいですが試行錯誤の末下記になりました。

 

    public void readWavFile(File f) {
        try {
            AudioInputStream input = AudioSystem.getAudioInputStream(f);
            AudioFormat format = input.getFormat();
            BufferedInputStream bufInput = new BufferedInputStream(input);

            byte frameBuf = new byte[format.getFrameSize()];

            int sampleSizeInBytes = format.getSampleSizeInBits() / 8;
            int chCount = format.getChannels();

            byte
buf = new byte[BYTE_LENGTH_OF_INT];
            ByteBuffer bb;

            ByteOrder order;
            if (format.isBigEndian()) {
                order = ByteOrder.BIG_ENDIAN;
            } else {
                order = ByteOrder.LITTLE_ENDIAN;
            }

            int offset = BYTE_LENGTH_OF_INT - sampleSizeInBytes;
            int shift = BYTE_LENGTH_OF_INT * 8 - format.getSampleSizeInBits();

            int count = 0;
            int value = 0;

            while (bufInput.read(frameBuf) > 0) {
                for (int ch = 0; ch < chCount; ch++) {
                    for (int i = 0; i < sampleSizeInBytes; i++) {
                        buf[offset + i] = frameBuf[ch * sampleSizeInBytes + i];
                    }

                    bb = ByteBuffer.wrap(buf);
                    bb.order(order);
                    value = bb.getInt() >> shift;

                    if (ch == 0 && count < 100) {
                        System.out.println(value);
                        count++;
                    }
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }

    }

でてきた出力をエクセルに貼り付けると

f:id:marumorix:20141224151428j:plain

1kHzの信号で44.1kHzのFsなので1周期は44サンプル

16bitなのでピーク値は2^15-1=32767

というのが期待値ですが

こんな感じになっているのでちゃんと読めているように見えます。

 

BIgEndianのファイルがないのでそれは出来ているか確認できないのと

サンプル精度が8bitの倍数でないとおかしなことになりますが

16bitと24bitくらいしか目にしないので

とりあえずあとにします。

 

 

 

 

日本の人口

少子高齢化といわれていますが

あまり実感がわかないのでちょっと確認してみます

 

データはこちらからもらいました。

統計局ホームページ/日本の統計

書けって書いてあるのでこれも表記しておきましょう

出典 総務省統計局「日本の統計 2014」

ぐぐれば表はいくらでも出てきますが

恐らくソースはここが多いのでは?

 

 

総人口の推移

f:id:marumorix:20140728093756j:plain

まずはグラフ。

ピークは2008年の12808万人

約30年後の2045年には10221万人なので思ったより減ってませんが

約50年後の2065年には8136万人。

 

運がよければ生きていますがその間に人口が約2/3になるっていうのは

ちょっと衝撃的。

 

年齢別人口

でもまー人口そのものなんてそんなに問題じゃなくて

怖いのはこれ。f:id:marumorix:20140728094332j:plain

データはもともと

普通15歳以下、65歳以上、その間の3つに分かれていたのですが

働く人とそれ以外って意味で15歳以下+65歳以上ってのを足してみました。

 

約30年後には働ける年齢の人と養われる人の数がだいたい同じくらい。

 

年齢だけで分けたデータなので

もし奥さんが専業だったら自分一人の収入で

4人の生活を支えないといけないっていうことです。

当然そのような社会情勢であれば年金なんか

ほとんど期待できないでしょう。

 

雑感

収入も全体的には下がっていきそうなので

将来どうなるかかなり心配。

 

とはいうものの

そこまで悲観的になることもなく

労働力の一部はロボットに置き換わるだろうし

女性も男性と変わらない意識で働くようになるだろうし

高齢者が働くのも一般的になるでしょう。

なにも対策しないとだめってことですね。

 

データ

つけておきます。

統計局ホームページ/日本の統計−本書の内容

ここからエクセルがダウンロードできるので

それをグラフにしただけです。

 

f:id:marumorix:20140728095842j:plain

f:id:marumorix:20140728095901j:plain

事業規模別の収入の違い

お金の話ばっかり書いてますが

これを海外のデータと比べて見たいのでまずは日本のデータを羅列。

 

海外やる前に飽きそうな不安。

 

例によって情報ソースはこちら

民間給与実態統計調査結果|長期時系列データ|国税庁

 

大企業は高収入なのか

結果は明らかなのでまずはグラフを2つ

企業規模が大きいほど収入が高い。

f:id:marumorix:20140724224907j:plain

f:id:marumorix:20140724224905j:plain

 

まとめ

5000人以上の大企業は30人以下の中小企業と比べて

平均で150万、男性は200万ほど収入が高い

女性はそれほど変わらない。

 

雑感

収入だけを目安にするなら大企業に就職したほうが有利そうです。

平均で見ると差がありますが

女性も男性と同じ職種で入社すれば傾向は同じでしょう。

 

能力そのものよりは業種や規模含めて

入った会社で収入が決まるような気がしています。

 

いくつか日本の収入データについて書きましたが

ちょっと検索すればこんなデータを可視化した記事はいくらでもあります。

自分でやってみないと実感としてなかなか頭に入らないので

今回はエクセルで自分の見たいものを図にしてみました。

 

会社もいつ何があるかわからないので

実情はどんなものか知っておきたかったのですが

特に事業規模と男女の差が予想以上でした。

 

アジアの中では恐らく1番か2番くらいに日本の収入は高いので

今後も仕事の海外シフトは引き続き進むでしょう。

雇用も収入も減る傾向は止められない中で

どうやったらそれなりに幸せに生きていけるのか。

 

勝ち逃げできるだけの能力も資産もないですが

できれば苦労せず楽しくやっていきたい。

どうしたらよいものか。

年齢別収入の推移

平均収入の30年の推移

収入分布の30年の推移

を確認したので

今度は年齢別に見てみます。

 

データは前回同様

民間給与実態統計調査結果|長期時系列データ|国税庁

ここから持ってきたデータをグラフにしただけです。

 

2012年時点での平均値は

全体平均408万円 男性502万円 女性268万円

 

じゃあ40歳になったらいくらもらえるの?

親父のころはどうだったの?

という疑問を確認してみます。

全体平均は30年でどう変わったか

f:id:marumorix:20140724214458j:plain

ちなみに全体の平均年収は

1982年 320万円

1992年 455万円

2002年 448万円

2012年 408万円

で1997年の467万円をピークに現在は下がっています。

 

1992年から比べると勤続年数が上がっても収入が増えなくなっています。

1992年は 22歳入社で280万円 → 55歳で550万円

2012年は 22歳入社で240万円 → 55歳で490万円

というのがモデルケース。

 

昔はよかったらしいという情報は正しいようです。

毎年50万くらい違うと40年で2000万くらい。

うらやましい。

 

男女での比較

f:id:marumorix:20140724215939j:plain

男女でも見てみます。

ピーク近くの20年間と比較

女性はほとんど変わっていないことがわかります。

つまり全体の差はほぼ男性の収入減によるもの。

 

雑感

2012年では

男性は30から50で200万上がっているので

年昇給20万といいたいところですが

われわれの20年後がどうなっているかはまた別の問題です。

 

女性は昇給しないのではなくやめて別の仕事に就く人が多いのでしょうか?

また25歳以下での給料の差は大卒が増えたからかもしれません。

現在は2曲化して男性同様稼ぐ人と家事メインでパートみたいな人がいるので

このグラフは男性の形に多少近づいていくのではないでしょうか?

 

まとめ

男性は年齢に従って昇給 30歳の370万からざっくり1年20万

女性は年齢にかかわらず270万くらい

 

 

 

 

収入分布の推移

前回平均収入の推移を見てみたので

今度は分布を見てみます。

 

今回も1世代前、父親世代との比較ということで

30年を目安とします。

 

データの出展は国税庁

民間給与実態統計調査結果|長期時系列データ|国税庁

 

収入の分布データを割合に直して見ました。

全体平均

f:id:marumorix:20140720173839j:plain

30年前は300万円以下が約26%で700万円以上の人はほとんどいませんでした。

20年前からはそれほど変化がありませんが最新の青いグラフは

10年前の赤い部ラフに比べて100~300万円の人が増える一方

500万円以上の範囲で1~2%程度下がっています。

400万円以下の人は2002年で52% 2012年で59%です。

 ちなみに600万円以上は

2002年で22%、2012年で17%となったようです。

ここ10年で給料が下がったのは気のせいじゃないみたいです。

 

男女別の分布

f:id:marumorix:20140720175227j:plain

男性は400万、女性は200万円くらいがピーク値です。

男性は600万円以上の人が全体的に1~2%下がっていて

400万円以下の人が増えています。

女性は250万くらいにピーク値があったのが200万くらいに移動しました。

稼ぐ女性が増えたという認識があったのですがそれは誤りで

10年前から減っていないだけで増えてもいないようです。

減っていないので相対的に増えているとも言えるかもしれません。

 

2012年時点で

400万円以下は 男性43% 女性 82%

600万円以上は 男性26% 女性 4.8%

 

雑感

親世代より額面は上がっている

10年前より給料は下がった

女性は収入が低い人が多い。

 

主となる収入を夫が、家事を妻がというモデルがあることを考えると

低収入=貧困というわけでもないと思いますが

女性が一人で生活を成り立たせるのはやはり厳しそうです。