Segmentation Fault

コアダンプの数だけ強くなれるよ。

Javascriptのcanvasと遅延処理でアニメーション

Javascriptのcanvasと遅延処理を組み合わせて簡単なアニメーションを描画してみる。夏なので簡単な花火っぽいアニメを作ってみた。

完成品

点火ボタンを押すとお粗末な花火が描画される(た~まや~)


花火風アニメの作り方

花火の描画

任意のサイズのキャンバスを作成して、キャンバスの中心から円形上に直線を描くことで実現する。

円形上に直線を描画するには下記のように三角関数を利用する。

var x0 = canvas.width/2;  //キャンバスの中心(x成分)
var y0 = canvas.height/2; //キャンバスの中心(y成分)
var len = 20;             //描画する直線の長さ
var div = 12;             //描画する直線の数
for(var i = 0; i < div; i++) {
   // tは描画時刻のこと
   var rad = (((360/div) * i) / 180) * Math.PI;
   var x1 = x0 + (len * t) * Math.sin(rad);
   var y1 = y0 + (len * t) * Math.cos(rad);
   var x2 = x0 + (len * (t + 1)) * Math.sin(rad);
   var y2 = y0 + (len * (t + 1)) * Math.cos(rad);
   
   ctx.moveTo(x1, y1);
   ctx.lineTo(x2, y2);
}


遅延処理

動きのあるアニメーションにするためには描画と遅延を組み合わせる必要がある。 遅延はwindow.setTimeout(callback(), msec)を使って描画処理を再帰的に呼び出すことで実現できる。

例えば、描画処理を100ミリ秒ごとに10回実行する処理は下記のように書くことができる。

function drawFireWork(t, ms) {
  // 
  // ここに描画処理
  // 
  if (t < 10) {
    window.setTimeout(function() {drawFireWork(t+1, ms)}, ms);
  }
}

function fire() {
  window.setTimeout(function() {drawFireWork(0, 100)}, 100);
}


ソースコード

下記は本ページに埋め込んだ花火風アニメのソースコード。

<html>
<head>
<canvas id="canv" width="400" height="400" style="background-color:rgb(230,230,230);"></canvas>
<script type="text/javascript">
  function drawFireWork(t, ms) {
    
    var canvas = document.getElementById("canv");
    
    if (canvas.getContext) {
      var ctx = canvas.getContext('2d');
      
      ctx.fillStyle = "rgb(230,230,230)";
      ctx.fillRect(0, 0, canvas.width, canvas.height);
      
      ctx.strokeStyle = "red"
      ctx.lineWidth = 2;
      ctx.beginPath();
      
      var x0 = canvas.width/2;
      var y0 = canvas.height/2;
      var len = 20;
      var div = 12;
      var rate = 0.9;
      for(var i = 0; i < div; i++) {
         var rad = (((360/div) * i) / 180) * Math.PI;
         var x1 = x0 + (len * t) * Math.sin(rad);
         var y1 = y0 + (len * t) * Math.cos(rad);
         var x2 = x0 + (len * (t + 1)) * Math.sin(rad);
         var y2 = y0 + (len * (t + 1)) * Math.cos(rad);
         
         ctx.moveTo(x1, y1);
         ctx.lineTo(x2, y2);
      }
      
      // 描画内容を実行する
      ctx.stroke();
      
      if (t < (x0 - len * (t+1))) {
        window.setTimeout(function() {drawFireWork(t+1, ms)}, ms);
      } else {
        ctx.fillStyle = "rgb(230,230,230)";
        ctx.fillRect(0, 0, canvas.width, canvas.height);
      }
    }
  }
  
  function fire() {
    window.setTimeout(function() {drawFireWork(0, 100)}, 100);
  }
</script>
</head>
<body>
  <p></p>
  <input type="button" value="点火" onclick="fire()">
</body>
</html>




Javascriptで画像を分割してシャッフルする

HTML5のcanvasとJavascriptを使って画像データを9つのブロックに分割し、ブロックをシャッフルして表示してみる。

シャッフル前

シャッフル後

ソースコード

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>画像を分割してシャッフル表示</title>
<script type="text/javascript">

function resize(x, y) {
  document.orig.width = x;
  document.orig.height = y;
}

function shuffleArray(array) {
  var n = array.length, t, i;

  while (n) {
    i = Math.floor(Math.random() * n--);
    t = array[n];
    array[n] = array[i];
    array[i] = t;
  }

  return array;
}

function shuffle() {
  //描画コンテキストの取得
  var canvas = document.getElementById('c');
  
  if (canvas.getContext) {
    var ctx = canvas.getContext('2d');
    ctx.fillStyle = "rgb(255,255,255)";
    ctx.fillRect(0, 0, 300, 300);
    
    var img = new Image();
    img.src = "http://freesozai.jp/sozai/roadsign/img/rds_001/1.png";
    
    img.onload = function() {
      img.width = 300;
      img.height = 300;
      var xmax = 3;
      var ymax = 3;
      var width = img.width/xmax;
      var height = img.height/ymax;
      var ary = [];
      var cnt = 0;
  
      for(var i = 0; i < xmax; i++){
        for(var j = 0; j < ymax; j++){
          ary[cnt++] = [i,j];
        }
      }
      
      ary = shuffleArray(ary);
      
      for(var i = 0; i < xmax; i++){
        for(var j = 0; j < ymax; j++){
          cnt--;
          var x = ary[cnt][0];
          var y = ary[cnt][1];
          ctx.drawImage(img, width * x, height * y, width, height, width * i, height * j, width, height);
        }
      }
      
    }
  }
}
</script>
</head>
<body onLoad="shuffle()">
  <h3>オリジナル</h3>
  <div><img src="http://freesozai.jp/sozai/roadsign/img/rds_001/1.png" name="orig" onload="resize(300,300)"></img></div>
  <h3>シャッフル後</h3>
  <canvas id="c" style="background-color:white;" width=300 height=300></canvas>
  
  <p></p>
  <input type="button" value="Shuffle" onclick="shuffle()">
</body>
</html>

参照

道路標識の一覧 - 無料で使えるEPSフリー素材集

JavaScript で配列のシャッフル - HAM MEDIA MEMO




次回はこれをベースに8ピースパズルを作成予定。



coincheckのAPIで仮想通貨の販売レートを表示する

下記で提供されているAPIを使ってpythonで仮想通貨の販売レートを表示してみる。

coincheck.com

事前準備

外部モジュールのrequestsが必要になるため事前にpipでインストールする。

[user@localhost coincheck]$ sudo /usr/local/bin/pip install requests


ソースコード

#!/usr/local/bin/python

import requests
import json

coins = [
            [1,  'BTC',  'btc_jpy'],
            [2,  'ETH',  'eth_jpy'],
            [3,  'DAO',  'dao_jpy'],
            [4,  'LSK',  'lsk_jpy'],
            [5,  'FCT',  'fct_jpy'],
            [6,  'XMR',  'xmr_jpy'],
            [7,  'REP',  'rep_jpy'],
            [8,  'XRP',  'xrp_jpy'],
            [9,  'ZEC',  'zec_jpy'],
            [10, 'XEM',  'xem_jpy'],
            [11, 'DASH', 'dash_jpy'],
        ]

urlbase = 'https://coincheck.com/api/rate/'

def main():
    for i in range(len(coins)):
        response = requests.get(urlbase+coins[i][2])
        if response.status_code != 200:
            raise Exception('return status code is {}'.format(response.status_code))

        rate = json.loads(response.text)

        print("%-4s : \%-10s" % (coins[i][1], rate['rate']))

if __name__ == "__main__":
    main()


結果

1 coins ⇔ 日本円 の販売レートを表示。

[user@localhost coincheck]$ python coincheck.py
BTC  : \410233.0
ETH  : \33844.23480699
DAO  : \338.442225
LSK  : \234.68313645
FCT  : \2107.38129735
XMR  : \5390.93169855
REP  : \2351.60868938
XRP  : \19.35870651
ZEC  : \24983.40682234
XEM  : \30.62478045
DASH : \22622.63349357



その他


本当はjavascriptで埋め込みたかったが何故かGETリクエストのステータスが404となりJSONデータを取得できず。同じソースコードでURLを他社APIに指定すると正常に取得できるのでサーバー側の問題か?



Python3サンプルコード集(その1)

最近はC言語ばっかりでしたが、今後もソフトウェア屋をやるのであればPythonかRubyは慣れておきたいところ。Pythonを新たに始めるならPython3一択らしい(Python2は必要になったら調べれば事足りるため)。個人的に新しい言語を覚えるときは短いサンプルコードを見て作るのが一番早いので簡単なものからはじめてみた。

Python3のインストール

まずはPython3のインストールから。Linux環境(CentOS7)に下記のソースコード(tarball)をダウンロード後、ビルドしてインストールする。


Python Source Releases | Python.org


f:id:segmentation-fault:20170811151613p:plain


[user@localhost ~]$ sudo yum install zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel  gcc
[user@localhost Download]$ wget https://www.python.org/ftp/python/3.4.7/Python-3.4.7.tgz
[user@localhost Download]]$ tar zxvf Python-3.4.7.tgz
[user@localhost Python-3.4.7]$ cd Python-3.4.7
[user@localhost Python-3.4.7]$ make
[user@localhost Python-3.4.7]$ sudo make install
[user@localhost Python-3.4.7]$ sudo ln -s /usr/local/python/bin/python3 /usr/local/bin/python
[user@localhost Python-3.4.7]$ sudo ln -s /usr/local/python/bin/pip3.4 /usr/local/bin/pip
[user@localhost ~]$ python --version
Python 3.4.7


インストール後にpythonのバージョンを確認して一致していればOK。

サンプル集

Hello Worldその1
#!/usr/local/python

print("hello world")
[user@localhost basic]$ python hello1.py
hello world
Hello Worldその2
#!/usr/local/python

import sys

sys.stdout.write("hello world\n")
[user@localhost basic]$ python hello2.py
hello world
printで改行しない
#!/usr/local/python

print("hello world", end="")
[user@localhost basic]$ python nokaigyo.py
hello world[user@localhost basic]$
各種演算
#!/usr/local/python

print('2+1 =', 2+1)
print('10-3 =', 10-3)
print('7*4 =', 7*4)
print('5/2 =', 5/2)
print('5//2 =', 5//2)
print('10%3 =', 10%3)
print('2**10 =', 2**10)
[user@localhost basic]$ python ensan.py
2+1 = 3
10-3 = 7
7*4 = 28
5/2 = 2.5
5//2 = 2
10%3 = 1
2**10 = 1024
九九表
#!/usr/local/bin/python

for x in range(0,9):
    for y in range(0,9):
            print('{0}'.format('%2d ' % ((x+1) * (y+1))), end="")
    print('')
        
[user@localhost basic]$ python 99.py
 1  2  3  4  5  6  7  8  9
 2  4  6  8 10 12 14 16 18
 3  6  9 12 15 18 21 24 27
 4  8 12 16 20 24 28 32 36
 5 10 15 20 25 30 35 40 45
 6 12 18 24 30 36 42 48 54
 7 14 21 28 35 42 49 56 63
 8 16 24 32 40 48 56 64 72
 9 18 27 36 45 54 63 72 81
円周率を求める
#!/usr/local/bin/python

import sys
import math

#
# Machin's formula
# π/4 = 4 * Arctan(1/5) - Arrctan(1/239)
#

def arctan(x, k):
    arctanx = 0
    for n in range(0, k):
        arctanx += pow(-1, n) * (1/(2 * (n+1) - 1)) * pow(x, (2*(n+1) - 1))
    return arctanx

if __name__ == '__main__':
    argv = sys.argv

    if len(argv) != 2:
        print('usage : {0} <n>'.format(argv[0]))
        quit()

    for k in range(1, int(argv[1])):
        pi = 4 * (4 * arctan(1/5, k) - arctan(1/239, k))

    print(pi)
[user@localhost basic]$ python pi.py 10
3.141592653589836
hexdumpコマンド風
#!/usr/local/bin/python

import sys

if __name__ == '__main__':

    argv = sys.argv

    if len(argv) != 2:
        print('usage : {0} <file>'.format(argv[0]))
        quit()

    f = open(argv[1], 'rb')

    dat = f.read()

    for i in range(len(dat)):
        # 文字表示と改行
        if (i >= 1 and i % 16 == 0):
            print('|', end="");
            for n in range(16):
                if (0x20 <= int(dat[i+n-16]) and int(dat[i+n-16]) <= 0x7e):
                    print('%c' % dat[i+n-16], end="")
                else:
                    print('.', end="")
            print('|');

        # オフセット表示
        if (i == 0 or i % 16 == 0):
            print('%08x  ' % i, end='')

        # 16進数表示
        print('%02x ' % dat[i], end='')

        # 8byteで空白区切り
        if (i >= 1 and (i+1) % 8 == 0):
            print(' ', end="")

    # 最後に改行
    print('')

    f.close

[user@localhost basic]$ hexdump -C Windows_Error.wav |head
00000000  52 49 46 46 24 9e 02 00  57 41 56 45 66 6d 74 20  |RIFF$...WAVEfmt |
00000010  10 00 00 00 01 00 02 00  44 ac 00 00 10 b1 02 00  |........D.......|
00000020  04 00 10 00 64 61 74 61  00 9e 02 00 00 00 00 00  |....data........|
00000030  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|


[user@localhost basic]$ python file.py Windows_Error.wav > test.txt
[user@localhost basic]$ head test.txt
00000000  52 49 46 46 24 9e 02 00  57 41 56 45 66 6d 74 20  |RIFF$...WAVEfmt |
00000010  10 00 00 00 01 00 02 00  44 ac 00 00 10 b1 02 00  |........D.......|
00000020  04 00 10 00 64 61 74 61  00 9e 02 00 00 00 00 00  |....data........|
00000030  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
Unixドメインソケット

サーバ側

#!/usr/local/bin/python

import os
import sys
import socket

class UnixDomainServer:
    def __init__(self, socket_path):
        self.socket_path = socket_path

    def start(self):
        s = self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
        s.bind(self.socket_path)
        s.listen(1)
        try:
            while True:
                sys.stdout.write("wait connection\n")
                connection, address = s.accept()
                sys.stdout.write("connected\n")
                self.accepted(connection, address)
                sys.stdout.write("disconnect\n")
        finally:
            os.remove(self.socket_path)

    def accepted(self, connection, address):
        data = connection.recv(1024)
        sys.stdout.write("receive from client: {}\n".format(data.decode()))

if __name__ == '__main__':
    server = UnixDomainServer('./ud.sock')
    server.start()


クライアント側

#!/usr/local/bin/python

import sys
import socket


class UnixDomainClient:
    def __init__(self, socket_path):
        self.socket_path = socket_path

    def start(self):
        s = self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
        s.connect(self.socket_path)

    def send(self):
        message = "Hello"
        sys.stdout.write("send message to server : {0}\n".format(message))
        self.socket.send(message.encode())

    def close(self):
        self.socket.close()

if __name__ == '__main__':
    client = UnixDomainClient('./ud.sock')
    client.start()
    client.send()
    client.close()

[user@localhost basic]$ python unix_sever.py
wait connection
connected
receive from client: Hello
disconnect
wait connection

[user@localhost basic]$ python unix_client.py
send message to server : Hello



今は全くまとまりが無いですが、サンプルが増えたら整理できればと思います。

WAVE音声にディレイをかける

今回はWAVE音声にディレイ(残響系)のエフェクトをかけてみた。

ディレイはある時刻 n における値 S(n) に過去時刻の値 S(n-m) を加算合成(やまびこ)していくことで実現できる。ディレイのかかり具合は遅延時間、やまびこの回数、減衰率で決まる。



ソースコード(ディレイ処理部のみ)

#define DELAY_TIME_RATE (0.1)     /* 遅延時間 */
#define DELAY_FEEDBACK_COUNT (5)  /* やまびこの回数 */
#define ATTENUATION_RATE (0.5)    /* 減衰率 */
    int delay_time = wave1->fmt.sample_per_sec * DELAY_TIME_RATE;
    int data_block_max = wave1->data.chunk_size/sizeof(wave_stereo_t);
    wave_stereo_t* val1 = (wave_stereo_t* )&wave1->data.dat[0];
    wave_stereo_t* val2 = (wave_stereo_t* )&wave2->data.dat[0];

    for(int t = 0; t < data_block_max; t++) {
        for (int c = 1; c <= DELAY_FEEDBACK_COUNT; c++) {
            if (t - (delay_time * c) > 0) {
                val2[t].left += pow(ATTENUATION_RATE, c) * val1[t - delay_time * c].left;
                val2[t].right += pow(ATTENUATION_RATE, c) * val1[t - delay_time * c].right;
            }
        }
    }



ディレイ無し


ディレイ有り



位相反転でボーカルを消す(WAVEファイル)

WAVEファイルのいじり方が段々分かってきたので今回は歌入り楽曲データのボーカルを抜くプログラムを作成してみる。環境はLinux(CentOS7)、言語はC言語。

はじめに

音楽プレーヤー等にイヤホンを半挿しにするとボーカルが消える(音が小さくなる)現象をご存じの方は多いと思う。楽曲用のソフトウェアをいじる方は位相反転というワードのほうがしっくりくるかもしれない。

この現象は左右の音声波のどちらか一方が位相反転(プラスとマイナスが入れ替わる)して合成されることで発生している。大抵ボーカルの音声は真ん中(左右とも同程度の成分量)なので波の打消しによって波が小さく(音が小さく)なるという理屈。説明は下記を参考とさせていただく。

soundside.blog3.fc2.com


okwave.jp



対象の楽曲


著作権とか色々あるので下記の楽曲を使わせていただくことにした。

ボーカル素材|著作権フリーの無料音楽素材ダウンロードサイト「ミュージックノート」

f:id:segmentation-fault:20170808230324p:plain



ソースコード


ボーカル消去は下記の処理で実現する。合算値のオーバーフローを考慮してないので所々で変な音になるかもしれない。

    for(t = 0; t < wave->data.chunk_size/sizeof(wave_stereo_t); t++) {
        wave_stereo_t* val =
            (wave_stereo_t* )&wave->data.dat[t * sizeof(*val)];

        /* 片方を位相反転して合算する */
        val->right = val->right + val->left * (-1);

        /* 左右とも同値にする */
        val->left = val->right;
    }



ソースコード全体(vo_cancel.c)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <unistd.h>

/* RIFFチャンク */
typedef struct {
    char        chunk_id[4];        /* チャンク識別子('RIFF'固定) */
    uint32_t    chunk_size;         /* チャンクサイズ */
    char        format_type[4];     /* フォーマットタイプ('WAVE'固定) */
} RIFF_chunk_t;

/* fmt チャンク */
typedef struct {
    char        chunk_id[4];        /* チャンク識別子('fmt '固定) */
    uint32_t    chunk_size;         /* チャンクサイズ */
    uint16_t    format_type;        /* フォーマットタイプ */
    uint16_t    channel;            /* チャンネル数 */
    uint32_t    sample_per_sec;     /* サンプリング周波数 */
    uint32_t    byte_per_sec;       /* 1秒あたりバイト数  */
    uint16_t    block_size;         /* ブロックサイズ */
    uint16_t    bit_per_sample;     /* 量子化精度 */
} fmt_chunk_t;

/* dataチャンク */
typedef struct {
    char        chunk_id[4];        /* チャンク識別子('data')固定 */
    uint32_t    chunk_size;         /* チャンクサイズ(データ長) */
    uint8_t     dat[0];             /* データ(可変長) */
} data_chunk_t;

typedef struct {
    RIFF_chunk_t    riff;
    fmt_chunk_t     fmt;
    data_chunk_t    data;
} wave_format_t;

typedef struct {
    int16_t         left;
    int16_t         right;
} wave_stereo_t;

static int revertAndSynthsisWave(char* input_file, char* output_file);

int main(int argc, char*argv[])
{
    if (argc != 3) {
        fprintf(stderr, "usage:%s <input file> <output file>\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    int rc = revertAndSynthsisWave(argv[1], argv[2]);
    if (0 != rc) {
        fprintf(stderr, "revertAndSynthsisWave() failed(%d)\n", rc);
        exit(EXIT_FAILURE);
    }

    return 0;
}

static int revertAndSynthsisWave(char* input_file, char* output_file)
{
    int rc = -1;
    FILE* ifp = NULL;
    FILE* ofp = NULL;
    char buff[sizeof(wave_format_t)] = {0};
    wave_format_t* wave = NULL;
    int t = 0;

    if (access(input_file, R_OK) != 0) {
        perror("access");
        goto error_end;
    }

    ifp = fopen(input_file, "rb");
    if (NULL == ifp ) {
        perror("fopen");
        goto error_end;
    }

    if (fread(buff, 1, sizeof(buff), ifp) <= 0)  {
        perror("fread");
        goto error_close_end;
    }

    wave = (wave_format_t* )malloc(((wave_format_t* )buff)->riff.chunk_size);
    if (NULL == wave) {
        goto error_close_end;
    }

    *wave = *((wave_format_t* )buff);
    if (fread(wave->data.dat, 1, wave->data.chunk_size, ifp) <= 0) {
        perror("fread");
        goto error_free_end;
    }

    for(t = 0; t < wave->data.chunk_size/sizeof(wave_stereo_t); t++) {
        wave_stereo_t* val =
            (wave_stereo_t* )&wave->data.dat[t * sizeof(*val)];

        /* 片方を位相反転して合算する */
        val->right = val->right + val->left * (-1);

        /* 左右とも同値にする */
        val->left = val->right;
    }

    ofp = fopen(output_file, "wb");
    if (NULL == ofp) {
        perror("fopen");
        goto error_free_end;
    }

    fwrite((void*)wave, 1, sizeof(*wave)+wave->data.chunk_size, ofp);

    rc = 0;

    fclose(ofp);
 error_free_end:
    free(wave);
 error_close_end:
    fclose(ifp);
 error_end:

    return rc;
}


実行結果


加工前のnoise.wavからボーカルを消去したnoise_vocancel.wavを作成する。

[user@localhost vo_cancel]$ gcc -o vo_cancel vo_cancel.c
[user@localhost vo_cancel]$ ./vo_cancel noise.wav noise_vocancel.wav
[user@localhost vo_cancel]$ ls
noise_vocancel.wav  noise.wav  vo_cancel  vo_cancel.c



再生してみると下記のようになる。(wavだと都合が悪いのでmp3に変換しています)



ボーカル消去前


ボーカル消去後



WAVEファイルのノコギリ波を作って再生してみる


前回はWAVEファイルからデータを抽出し波形をプロットしてグラフ化した。

www.segmentation-fault.xyz



今回は逆にWAVEファイルを作成してちゃんと再生できるか試してみる。


ノコギリ波の作成


下記条件でノコギリ波を作成する。

# 構成要素
1 振幅 15000(レンジは-15000~15000)
2 振動数 50Hz
3 サンプリング周波数 44.1kHz
4 再生時間 3秒
5 チャンネル数 2(ステレオ)
6 量子化精度 16bit


上記ノコギリ波のサンプリング時刻 t における値 S(t) を式にすると次の通りになる。

 S(t) = (t \bmod (サンプリング周波数/振動数)) × ( (振幅×2)/(サンプリング周波数/振動数)) - 振幅


(サンプリング周波数/振動数)はノコギリ波の傾きを表す。

ノコギリ波生成プログラム


前回のソースを流用して下記の通り作成した。

sawtooth_wav.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <unistd.h>

#define WAVE_FORMAT_PCM     0x0001                      /* PCM */
#define CHANNEL_STEREO      0x0002                      /* ステレオ */
#define SAMPLING_PER_SEC    (0x0000AC44)                /* 44.1kHz */
#define SOUND_LENGTH        (SAMPLING_PER_SEC * 3)      /* 3sec */
#define BLOCK_SIZE          0x0004                      /* 4byte */
#define DATA_SIZE           (SOUND_LENGTH * BLOCK_SIZE) /* DATAサイズ */
#define AMPLITUDE           0x3A98                      /* 振幅は±15000 */
#define PERIOD              0x0032                      /* 50Hz */
#define BIT_PER_SAMPLE      0x0010                      /* 16bit */
#define SLOPE               (SAMPLING_PER_SEC/PERIOD)   /* 波の傾き */


/* RIFFチャンク */
typedef struct {
    char        chunk_id[4];        /* チャンク識別子('RIFF'固定) */
    uint32_t    chunk_size;         /* チャンクサイズ */
    char        format_type[4];     /* フォーマットタイプ('WAVE'固定) */
} RIFF_chunk_t;

/* fmt チャンク */
typedef struct {
    char        chunk_id[4];        /* チャンク識別子('fmt '固定) */
    uint32_t    chunk_size;         /* チャンクサイズ */
    uint16_t    format_type;        /* フォーマットタイプ */
    uint16_t    channel;            /* チャンネル数 */
    uint32_t    sample_per_sec;     /* サンプリング周波数 */
    uint32_t    byte_per_sec;       /* 1秒あたりバイト数  */
    uint16_t    block_size;         /* ブロックサイズ */
    uint16_t    bit_per_sample;     /* 量子化精度 */
} fmt_chunk_t;

/* dataチャンク */
typedef struct {
    char        chunk_id[4];        /* チャンク識別子('data')固定 */
    uint32_t    chunk_size;         /* チャンクサイズ(データ長) */
    uint8_t     dat[0];             /* データ(可変長) */
} data_chunk_t;

typedef struct {
    RIFF_chunk_t    riff;
    fmt_chunk_t     fmt;
    data_chunk_t    data;
} wave_format_t;

typedef struct {
    int16_t         left;
    int16_t         right;
} wave_stereo_t;


int main(int argc, char*argv[])
{
    if (argc != 2) {
        fprintf(stderr, "usage:%s <output file>\n", argv[0]);
        goto error_end;
    }

    FILE* fp = fopen(argv[1], "wb+");
    if (NULL == fp) {
        perror("fopen");
        goto error_end;
    }

    wave_format_t* wave =
        (wave_format_t* )malloc(sizeof(*wave) + DATA_SIZE);
    if (NULL == wave) {
        perror("malloc");
        goto error_close_end;
    }

    /* RIFF */
    memcpy(wave->riff.chunk_id, "RIFF", sizeof(wave->riff.chunk_id));
    wave->riff.chunk_size = sizeof(*wave)+DATA_SIZE;
    memcpy(wave->riff.format_type, "WAVE", sizeof(wave->riff.format_type));

    /* fmt */
    memcpy(wave->fmt.chunk_id, "fmt ", sizeof(wave->fmt.chunk_id));
    wave->fmt.chunk_size = sizeof(fmt_chunk_t) -
        (sizeof(wave->fmt.chunk_id)+sizeof(wave->fmt.chunk_size));
    wave->fmt.format_type = WAVE_FORMAT_PCM;
    wave->fmt.channel = CHANNEL_STEREO;
    wave->fmt.sample_per_sec = SAMPLING_PER_SEC;
    wave->fmt.byte_per_sec = SAMPLING_PER_SEC * BLOCK_SIZE;
    wave->fmt.block_size = BLOCK_SIZE;
    wave->fmt.bit_per_sample = BIT_PER_SAMPLE;

    /* data */
    memcpy(wave->data.chunk_id, "data", sizeof(wave->data.chunk_id));
    wave->data.chunk_size = DATA_SIZE;

    int t;
    for (t = 0; t < SOUND_LENGTH; t++) {
        wave_stereo_t* val =
            (wave_stereo_t* )&wave->data.dat[t*sizeof(wave_stereo_t)];

        val->left = (t % SLOPE) * ((AMPLITUDE*2)/SLOPE) - AMPLITUDE;
        val->right = (t % SLOPE) * ((AMPLITUDE*2)/SLOPE) - AMPLITUDE;
    }

    if (fwrite((void*)wave, 1, sizeof(*wave)+DATA_SIZE, fp) <= 0) {
        perror("fwrite");
    }

 error_close_end:
    fclose(fp);
 error_end:

    return 0;
}


波形表示と再生


波形表示には前回に作ったバイナリ(plotwav)とgnuplotを使う。

[user@localhost sawtooth]$ gcc -o sawtooth_wave sawtooth_wave.c
[user@localhost sawtooth]$ ./sawtooth_wave sawtooth.wav
[user@localhost sawtooth]$ cd ../
[user@localhost wav]$ plot/plotwav sawtooth/sawtooth.wav > sawtooth.plt
[user@localhost wav]$ gnuplot

        G N U P L O T
        Version 4.6 patchlevel 2    last modified 2013-03-14
        Build System: Linux x86_64

        Copyright (C) 1986-1993, 1998, 2004, 2007-2013
        Thomas Williams, Colin Kelley and many others

        gnuplot home:     http://www.gnuplot.info
        faq, bugs, etc:   type "help FAQ"
        immediate help:   type "help"  (plot window: hit 'h')

Terminal type set to 'x11'
gnuplot> plot [1:10000] "sawtooth.plt" using 1:2 with lines
gnuplot>


f:id:segmentation-fault:20170807232013p:plain



再生してみると雑音っぽい音がでる。(下記は便宜上mp3に変換しています)