instagramの写真をグリッチしまくるWEBサービスをつくった

instagramAPIが公開された直後、なんとかstagramってWEBサービスがいくつか出てきて、僕は面白いなーって見てるだけだったんだけど、ちょっと思いついたので今更ながら作ってみました。


GLISTAGRAM


なに?

instagramの写真をグリッチしながら表示するビューアーです。
適当にユーザ名やタグをいれて go すれば、画面全体にギャギャギャと表示されます。

ソースコードとか

node.js on herokuで書きました。初めてのnode.jsで初めてのheroku。
初めてのCoffeeScriptにもチャレンジしたけど、CoffeeScriptのままherokuにデプロイできなさそうなので書くだけ書いて捨てました。


稚拙ながらGitHubソースコードを置いてあります。
https://github.com/mohayonao/GLISTAGRAM

参考にしたもの

listagram
http://listagr.am/


nekostagram
http://nekostagram.heroku.com/


Heroku + Node.js + ExpressでHelloWorldを書いてみた。
http://www.eiplab.com/2011/06/heroku-node-js-express-helloworld/


サケグリッチ
http://blog.kzfmix.com/entry/1304644326


mala's gist: 77891
https://gist.github.com/77891


盆をリリースしました
http://d.hatena.ne.jp/hitode909/20110813/1313228606

PythonでPDFを分割する (すべての自炊派バンドマンに捧げる)

やりたいこと

バンドスコアをスキャンする → 楽曲ごとに分割したPDFにしたい

やりかた

pyPdfっていうモジュールを使うとできる。

sudo easy_install pypdf

サンプルを参考に練習がてら書いた。

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from pyPdf import PdfFileWriter, PdfFileReader

def main():
    """sample.pdf を 3ページごとに分割する"""
    src = 'sample.pdf'
    div = 3
    
    src = PdfFileReader(file(src, 'rb'))
    items = [ range(i, min(i + div, src.numPages))
              for i in xrange(0, src.numPages, div) ]
    
    for i, lst  in enumerate(items):
        print i, lst
        dst = PdfFileWriter()
        for j in lst:
            dst.addPage(src.getPage(j))
        out = file('%03d.pdf' % i, 'wb')
        dst.write(out)
        out.close()

if __name__ == '__main__':
    main()

なにこれ超簡単。

ちゃんと書いた

分割用の目次(YAML)を定義して、それをつかって分割させる。こんな感じのやつ。

indexes:
  # 書式
  # (a[,b]) filename
  
  # (a) filename => 'ページa のみ抽出'
  - (  1     ) foo    # p. 1 only

  # (a,b) filename => 'ページa から ページb までを抽出'
  - (  5,   8) bar    # from p. 5 to p. 8

  # b を省略 => '次の項目まで抽出'
  - (  9,    ) baz    # from p. 9 to p.11

  # 最後の要素で b を省略 => '最後のページまで抽出'
  - ( 12,    ) hoge   # from p.12 to last
  
# 分割対象の最後のページ(省略した場合はPDFの最後のページ)
lastPage: 9999 

他にファイル名の前後につける文字や、元のPDFが何ページから始まるかを指定できるようにしてあげる。


あとはコードを書く。

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import os, re, optparse

import yaml
import pyPdf

def getprofile(profile):
    profile = yaml.load(open(profile))
    prefix = profile.get('prefix', '')
    suffix = profile.get('suffix', '')
    fnum = profile.get('fnum',  0)
    lastPage = profile.get('lastPage')
    indexes = profile.get('indexes', [])
    
    fname = ' '.join([prefix, '%(name)s', suffix]).strip()

    return indexes, lastPage, (fname, fnum)

def makeindexes(indexes):
    x = re.compile(r'\(\s*([0-9]+)\s*(?:(,)\s*([0-9]+)?\s*)?\)\s*(.*)$')
    
    tmp = []
    for i in xrange(len(indexes)):
        m = x.match(indexes[i])
        if not m:
            print 'Syntax Error: line=%d\n  - %s' % (i+1, indexes[i])
            exit(1)
        start = stop = int(m.group(1))
        if m.group(2):
            stop = int(m.group(3)) if m.group(3) else None
        name  = m.group(4).strip()
        tmp.append((start, stop, name))

    return tmp

def main():
    parser = optparse.OptionParser(usage='%prog [Options] src')
    parser.add_option("-o", "--offset", type="int", default=0)
    parser.add_option("-p", "--profile")
    (opts, args) = parser.parse_args()
    
    if not args:
        parser.print_help()
        exit(0)
    
    src = args[0]
    profile = opts.profile or src
    offset  = opts.offset
    
    if not src.lower().endswith('pdf'):
        src += '.pdf'
    
    if not profile.lower().endswith('yaml'):
        profile += '.yaml'
    
    indexes, lastPage, (fname, fnum) = getprofile(profile)
    indexes = makeindexes(indexes)
    
    print 'Divide PDF: %s (using %s)' % (src, profile)
    
    src = pyPdf.PdfFileReader(file(src, 'rb'))
    if lastPage is None:
        lastPage = src.numPages
    else:
        lastPage = min(lastPage, src.numPages)
    
    for i, (start, stop, name) in enumerate(indexes):
        dst = pyPdf.PdfFileWriter()
        if stop is None:
            if i < len(indexes) -1:
                stop = indexes[i+1][0] - 1
            else:
                stop = lastPage
        
        filename = fname % dict(fnum=(fnum+i), name=name)
        print '  page(%4d-%4d) => %s.pdf' % (start, stop, filename)
        for j in xrange(start, stop+1):
            if j + offset < lastPage:
                dst.addPage(src.getPage(j + offset - 1))
        
        out = file('%s.pdf' % filename, 'wb')
        dst.write(out)
        out.close()

if __name__ == '__main__':
    main()
prefix: 'The Beatles %(fnum)02d'
fnum: 1

indexes:
  - (  2,) LOVE ME DO
  - (  8,) PLEASE PLEASE ME
  - ( 14,) FROM ME TO YOU
  - ( 19,) SHE LOVES YOU
  - ( 26,) I WANT TO HOLD YOUR HAND
  - ( 33,) ALL MY LOVING
  - ( 39,) CAN'T BUY ME LOVE
  - ( 48,) A HARD DAY'S NIGHT
  - ( 56,) AND I LOVE HER
  - ( 62,) EIGHT DAYS A WEEK
  - ( 67,) I FEEL FINE
  - ( 75,) TICKET TO RIDE
  - ( 82,) YESTERDAY
  - ( 84,) HELP
  - ( 91,) YOU'VE GOT TO HIDE YOUR LOVE AWAY
  - ( 95,) WE CAN WORK IT OUT
  - (101,) DAY TRIPPER
  - (111,) DRIVE MY CAR
  - (118,) NORWEGIAN WOOD
  - (122,) NOWHERE MAN
  - (128,) MICHELLE
  - (136,) IN MY LIFE
  - (143,) GIRL
  - (149,) PAPERBACK WRITER
  - (155,) ELEANOR RIGBY
  - (160,) YELLOW SUBMARINE

https://gist.github.com/1177605

雑感

Pdfっていう書き方ダサいですよね?

Dropboxを使ってiPhoneから簡単に編集できるサイトをつくる

なんとなく考えてみた。

手順

1. Dropboxのpublicなフォルダに以下のHTMLを置く

<html>
  <head>
    <meta charset='utf-8'/>
  </head>
  <body></body>
  
  <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.min.js"></script>
  <script type="text/javascript" src="./showdown.js"></script>
  <script type="text/javascript">
    $(function() {
        $.get("./markdown.md",
              function(data) {
                  $("body").html(new Showdown.converter().makeHtml(data));
                  document.title = $("h1:first").text();
                  $("a").each(function(){$(this).attr("target","_blank");});
              });
    });
  </script>
</html>


2. showdown.js を置く
同じフォルダに http://pamgau.net/showdown/ ここからゲットした showdown.js を置く。


3. markdown.md を置く
同じフォルダにmarkdown形式で書かれたファイルを置く。


4. 画像ファイルとかを置く
必要であれば同じフォルダに画像ファイルとかを置く。


5. 編集する
iPhoneの Nebulous Lite っていう 無料アプリを使って markdown.md を編集する。

応用

見栄え良くしたかったらcss書いても良い。

難点

けっこう面倒くさい

JavaScriptでリアルタイムに音を鳴らす方法を3つほど

最近、JavaScriptで動くMMLシーケンサーを作っています。


SiONMMLパク参考にしているので、MMLTalks(http://mmltalks.appspot.com/)あたりのMMLをコピペすると、それなりに鳴ってくれる場合があります。思い通りになっていない部分がかなり多いですが、、

このあたりのMMLは結構聴ける(適宜コピペしてお試しください)

で、

で、表題の件ですがJavaScriptでリアルタイムに音を鳴らすには以下の3つの方法があります。

  • Audio Data APIを使う(Firefox 5.0以降)
  • Web Audio APIを使う(Chrome, Safari)
  • wavデータを直接放り込む(上記に加えてOperaも動く)


簡単なサンプルをあげています → サンプル ソース
*Operaでは何故か動きません。(MMLシーケンサーの方は何故か動く)



2011/09/18 追記
OSX Lion Chrome 14で Web Audio API がクラッシュする問題が解決されているみたいです。
(OSXGoogle Chrome 14.0.835.163で確認)



注意
現状、OSX Lion の Chrome を使っている人は Web Audio API を使うとタブがクラッシュします。

準備

とりあえず鳴らす音を作るためのクラス。本質的な部分ではないのでかなり適当。要は2つのサイン波が適当にミックスされて鳴る。サンプルでプチプチ鳴るのはここの作りがマズイから。

var samplerate = 48000, channel = 1, stream_length = 4096;

var sinwave = function(frequency) {
    this.phase = 0.0;
    this.phaseStep = frequency / samplerate;
};

sinwave.prototype.next = function() {
    var retval = Math.sin(2 * Math.PI * this.phase);
    this.phase += this.phaseStep;
    return retval;
};

var StreamGenerator = function() {
    this.gen1  = new sinwave(440);
    this.gen2  = new sinwave(660);
    this.phase = new sinwave(880);
};

StreamGenerator.prototype.next = function() {
    var stream = [];
    var i, imax;
    var g1 = this.gen1, g2 = this.gen2;
    var v1 = this.phase.next() / 2.0 + 0.5, v2 = 1.0 - v1;
    for (i = 0, imax = stream_length; i < imax; i++) {
        stream[i] = (g1.next() * v1 + g2.next() * v2);
    }
    return stream;
};

Audio Data APIを使う

対応ブラウザ: Firefox 5.0以降
仕様等についてはこちら→ https://wiki.mozilla.org/Audio_Data_API

セットアップ

Audio を new して mozSetup でチャネル数とサンプリング周波数を指定する。

audio = new Audio();
audio.mozSetup(channel, samplerate);
再生

mozWriteAudio に Float32Array を突っ込んで呼び出す。
mozWriteAudioはノンブロックなメソッドなので、ストリーム再生みたいな用途の場合は setInterval で定期的に呼び出してはstreamを与えてやる。

stream = new Float32Array();
audio.mozWriteAudio(stream);

ステレオのデータは、stream配列に交互にデータを入れる。

stream = new Float32Array([L, R, L, R, L, R, .... ]);
ここがイライラする

setIntervalで呼び出しまくるのは面倒くさいのでどうにかしてほしい。

簡単な例

準備で作ったジェネレータをつかって再生するためのクラス。

/**
 * Audio Data API用のプレイヤー (Firefox 5.0-)
 */
var MozPlayer = function() {
    this.audio = new Audio();
    this.audio.mozSetup(channel, samplerate);
    this.timerId = null;
    this.isPlaying = false;
};

MozPlayer.prototype.play = function(gen) {
    var self = this;
    if (this.timerId === null) {
        this.timerId = setInterval(function() {
            var s = new Float32Array(gen.next());
            self.audio.mozWriteAudio(s);
        }, stream_length / samplerate * 1000);
    }
    this.isPlaying = true;
};

MozPlayer.prototype.stop = function() {
    if (this.timerId !== null) {
        clearInterval(this.timerId);
    }
    this.isPlaying = false;
};

Web Audio APIを使う

対応ブラウザ: Chrome, Safari (ただしどちらもOSX版では現状使えない)
仕様等についてはこちら→ https://dvcs.w3.org/hg/audio/raw-file/tip/webaudio/specification.html


Chromeの場合は、アドレスバーに about:flags と入力した先で、ウェブオーディオを有効にすると使えるようになる。
Safariの場合は、それ用のSafariがあってダウンロードできたんだけど、今はできない。そのうちできるようになるでしょう。

セットアップ

webkitAudioContext を new して createJavaScriptNode を呼び出す。
サンプリング周波数は固定で、stream_length はあまり大きい値は設定できないみたい。
(準備のときに samplerate = 48000, stream_length = 4096 にしているのはこれに合わせているため)

context = new webkitAudioContext();
node = context.createJavaScriptNode(stream_length, 1, channel);
console.log(context.sampleRate);
再生

node.connect(context.destination) を呼ぶと、
再生のタイミングで node.onaudioprocess が呼び出されるので、その中で arguments[0].outputBuffer.getChannelData して
そいつにデータを代入する。onaudioprocess は定期的に呼び出されるので楽チン。

node.onaudioprocess = function(event) {
  var data = event.outputBuffer.getChannelData(0);
  var s = gen.next();
  var i = data.length;
  while (i--) data[i] = s[i];
};
node.connect(context.destination);
簡単な例

準備で作ったジェネレータをつかって再生するためのクラス。

/**
 * Web Audio API用のプレイヤー (Chrome, Safari)
 */
var WebkitPlayer = function() {
    this.context = new webkitAudioContext();
    this.node = this.context.createJavaScriptNode(stream_length, 1, channel);
    this.isPlaying = false;
};

WebkitPlayer.prototype.play = function(gen) {
    var self = this;
    this.node.onaudioprocess = function(event) {
        var data = event.outputBuffer.getChannelData(0);
        var s = gen.next();
        var i = data.length;
        while (i--) data[i] = s[i];
    };
    this.node.connect(this.context.destination);
    this.isPlaying = true;
};

WebkitPlayer.prototype.stop = function() {
    this.node.disconnect();
    this.isPlaying = false;
};

wavデータを直接放り込む

対応ブラウザ: Chrome, Opera, Firefox(変な感じになる)
詳しくはここに書いてある→ http://d.hatena.ne.jp/yanagia/20100323/1269334226

セットアップ

とくになし

再生

このあたり http://www.kk.iij4u.or.jp/~kondo/wave/ を参考にWAVファイルヘッダと再生バイトデータを作る。

wav = btoa(wavheader + bytes); // ややこしい処理
audio = new Audio("data:audio/wav;base64," + wav);
audio.play();

Audio Data APIと同じくsetIntervalなりで定期的に呼び出してやる必要がある。
ステレオのデータは、Audio Data APIと同じようにbytes内のデータが交互になるようにする。

ここがイライラする

これってストリーム再生じゃなくて細かいwavファイルを大量につくって再生しているだけなんで醜い。しかも合間合間でブチブチなったりする。ブチブチを少なくするためにファイルごとの再生時間を長くするともっさりする。嫌い。


あと、これはJavaScriptの問題なんだけど、たとえば3秒ごとに再生したくて

setInterval(func, 3 * 1000);

とすると、音が出始めるまでに3秒かかる。

func();
setInterval(func, 3 * 1000);

ってやってみると、最初と二回目の再生のタイミングがずれて気持ち悪い感じになる。
それと、OperaでFloat32Array使えないのどうにかなりませんかね。

簡単な例

準備で作ったジェネレータをつかって再生するためのクラス。

/**
 * Audioタグを使うプレイヤー (Chrome, Firefox, Opera)
 */
var HTML5AudioPlayer = function() {
    this.audio = null;
    this.timerId = null;
    this.isPlaying = false;
};

HTML5AudioPlayer.prototype.wavheader = function(samples) {
    var waveBytes = samples * channel * 2,
        l1 = waveBytes - 8,
        l2 = l1 - 36,
        retval = String.fromCharCode(
            0x52, 0x49, 0x46, 0x46, // 'RIFF'
            (l1 >>  0) & 0xFF, (l1 >>  8) & 0xFF,
            (l1 >> 16) & 0xFF, (l1 >> 24) & 0xFF,
            0x57, 0x41, 0x56, 0x45, // 'WAVE'
            0x66, 0x6D, 0x74, 0x20, // 'fmt '
            0x10, 0x00, 0x00, 0x00, // byte length
            0x01, 0x00, // linear pcm
            channel, 0x00, // channel
            (samplerate >>  0) & 0xFF,
            (samplerate >>  8) & 0xFF,
            (samplerate >> 16) & 0xFF,
            (samplerate >> 24) & 0xFF,
            ((samplerate*channel*2) >>  0) & 0xFF,
            ((samplerate*channel*2) >>  8) & 0xFF,
            ((samplerate*channel*2) >> 16) & 0xFF,
            ((samplerate*channel*2) >> 24) & 0xFF,
            0x04, 0x00, // block size
            0x10, 0x00, // 16bit
            0x64, 0x61, 0x74, 0x61, //'data'
            (l2 >>  0) & 0xFF, (l2 >>  8) & 0xFF,
            (l2 >> 16) & 0xFF, (l2 >> 24) & 0xFF);
    return retval;
};

HTML5AudioPlayer.prototype.play = function(gen) {
    var self = this;
    var itercount = 20;
    if (this.timerId === null) {
        this.timerId = setInterval(function() {
            var bytes = [], s, x, wav;
            var i, imax, j, jmax;
            for (i = 0, imax = itercount; i < imax; i++) {
                s = gen.next();
                for (j = 0, jmax = s.length; j < jmax; j++) {
                    x = (s[j] * 32767.0) >> 0;
                    bytes.push(String.fromCharCode(x & 0xFF, (x >> 8) & 0xFF));
                }
            }
            wav = btoa(self.wavheader(bytes.length) + bytes.join(''));
            self.audio = new Audio("data:audio/wav;base64," + wav);
            self.audio.play();
        }, stream_length * itercount / samplerate * 1000);
    }
    this.isPlaying = true;
};

HTML5AudioPlayer.prototype.stop = function() {
    if (this.timerId !== null) {
        clearInterval(this.timerId);
    }
    if (this.audio !== null) {
        this.audio.pause();
        this.audio = null;
    }
    this.isPlaying = false;
};
まとめ

ちょっと音を出したいだけなんだけど、やり方がいろいろあるのはだるいので一つにしてほしいですね。
僕は Web Audio API が好みなんだけど、前述のとおり OSX Lion で動かないので、非常に悲しい。

Firefox Chrome Opera Safari IE9.0
Audio Data API 使える 使えない 使えない 使えない 使えない
Web Audio API 使えない 使え(る│た) 使えない 使えた 使えない
HTML5 Audio 使える 使える 使える 使えない? 知らん

Pythonで英語CDを分割してみた

蒸し暑くなってきて、もともと少ないやる気がさらに無くなってきたのだけど、
多少は何かしないといけないなぁと思って、なんとなくDUO3.0の例文でも覚えようかと思った。


で、英語CDにありがちな1トラックにいくつかのセンテンスが入っているのが鬱陶しいので、
とりあえず分割ファイルをつくろうと思ってスクリプトを書いてみた。


やり方はいたって単純でレベルの低い部分がしばらく続いたら分割する。

こんな感じでやった

  1. iTunesでwavエンコーダを使ってCDからwavファイルを作る。
  2. 設定ファイルを作る
  3. 実行


設定ファイルはYAMLで書く。
src_path と dst_path は必須で、それ以外はオプション。

src_path:
  wavファイルのあるフォルダ

dst_path:
  分割したファイルを出力するフォルダ

params:
  分割するためのパラメータ

profiles:
  分割するための情報
paramsの説明

分割するためのパラメータをPythonのタプルの書式で書く。

  • 無音の音量レベル(0-32768)
  • 無音の継続時間(秒) … これより短い場合は無音部分とみなされない
  • 音声の継続時間(秒) … これより短い場合は捨てる
params:
  (75, 0.50, 1.0)
profilesの説明

各トラック毎の音声開始時間(秒)と終了時間(秒)をタプルの書式で書く。
profilesが無ければ、paramsの設定でprofilesを作成する。(あらかじめどの区間で区切るかが分かっているときに使う)

profiles:
  -
    - (  3.244,   7.142) #  1
    - (  7.142,  12.462) #  2
    - ( 12.462,  20.028) #  3
    - ( 20.028,  26.871) #  4
    - ( 26.871,  31.658) #  5
    - ( 31.658,  37.434) #  6
    - ( 37.434,  44.912) #  7
    - ( 45.150,  49.823) #  8
    - ( 49.823,  57.293) #  9


paramsだけだと細かくなりすぎてしまう。
妥協すまいとトラックごとにパラメータ調整できるようにとか色々やってみたけど、
半日くらいかけてバシっとキマる設定を探すのは諦めて、profilesを指定できる方法にしてみた。


今はDUO3.0用の profiles を作っています。まだ英文は覚えていない。本末転倒。

ソースコード

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import os
import sys
import math
import wave
import yaml

def bytes_to_shorts(lst):
    it = iter(lst)
    while True:
        a = 0x00ff & ord(it.next())
        b = 0x00ff & ord(it.next())
        x = ((b << 8 ) + a)
        if x & 0x8000:
            x = -(0x10000 - x)
        yield x

def split_2ch(it):
    while True:
        l = it.next()
        r = it.next()
        yield l, r

def pp(id, begin, end):
    print '    - (%7.3f, %7.3f) # %2d' % (begin, end, id)
    
def make_profile(filepath,
                 silent_lv=150, min_silent_interval=0.5, min_voice_interval=2.0):
    
    src = wave.open(filepath, 'rb')
    params = src.getparams()
    nchannles, sampwidth, framerate = params[:3]
    
    def iterator(src, buf_size = 1024):
        read_bytes = buf_size * (nchannles * sampwidth)
        
        while True:
            buf = src.readframes(buf_size)
            
            for l, r in split_2ch(bytes_to_shorts(buf)):
                yield (l + r) / 2
                
            if len(buf) != read_bytes:
                break
    
    print 'make_profile'
    id = 0
    result = []
    beginOfVoice = beginOfSilent = endOfSilent = None
    for i, x in enumerate(iterator(src)):
        if abs(x) <= silent_lv:
            if beginOfSilent is None:
                beginOfSilent = i
            else:
                endOfSilent   = i
        else:
            if beginOfVoice is None:
                beginOfVoice  = i
            else:
                if beginOfSilent is not None and endOfSilent is not None:
                    silent_interval = (endOfSilent - beginOfSilent) / float(framerate)
                    if silent_interval >= min_silent_interval:
                        voice_interval = (beginOfSilent - beginOfVoice) / float(framerate)
                        if voice_interval >= min_voice_interval:
                            begin = beginOfVoice / float(framerate)
                            end   = endOfSilent  / float(framerate)
                            result.append((begin, end))
                            pp(id, begin, end)
                            
                            id += 1
                            beginOfVoice = None
                        else:
                            beginOfVoice = None
                    beginOfSilent = endOfSilent = None
    else:
        if beginOfSilent is not None and endOfSilent is not None:
            silent_interval = (endOfSilent - beginOfSilent) / float(framerate)
            if silent_interval >= min_silent_interval:
                voice_interval = (beginOfSilent - beginOfVoice) / float(framerate)
                if voice_interval >= min_voice_interval:
                    begin = beginOfVoice / float(framerate)
                    end   = endOfSilent  / float(framerate)
                    result.append((begin, end))
                    pp(id, begin, end)
    src.close()
    return result

def write_waves(filepath, profile, dst_path):
    src = wave.open(filepath, 'rb')   
    params = src.getparams()
    nchannles, sampwidth, framerate = params[:3]
    
    result = []
    for i, (begin, end) in enumerate(profile):
        
        beginOfVoice = int(math.ceil(begin * float(framerate)))
        endOfVoice   = int(math.ceil(end   * float(framerate)))
        
        src.setpos(beginOfVoice)
        dat = src.readframes(endOfVoice - beginOfVoice)

        filename = os.path.basename(filepath)
        root, ext = os.path.splitext(filename)
        
        filename = '%s_%02d%s' % (root, i, ext)
        filepath2 = os.path.join(dst_path, filename)
        
        dst = wave.open(filepath2, 'wb')
        dst.setparams(src.getparams())
        dst.writeframes(dat)
        dst.close()
           
        result.append((filepath2, begin, end))
       
    src.close()
    return result

def main(argc, argv):
    
    for f in argv[1:]:
        settings = yaml.load(open(f))
        src_path = settings['src_path']
        dst_path = settings['dst_path']
        
        targets  = settings.get('targets')
        if not isinstance(targets, list):
            targets = None
            
        params = settings.get('params')
        if params:
            params = eval(params)
        else:
            params = (100, 0.50, 2.0)

        profiles = settings.get('profiles')
        
        file_no = 0
        for filename in os.listdir(src_path):
            if not filename.endswith('wav'):
                continue

            file_no += 1
            if targets and filename not in targets:
                continue            
            
            filepath = os.path.join(src_path, filename)
            print '%s' % filepath
            
            if profiles and file_no <= len(profiles):
                profile = [ eval(x) for x in profiles[file_no - 1] ]
            else:
                profile = make_profile(filepath, *params)
                
            file_list = write_waves(filepath, profile, dst_path)
            for output, begin, end in file_list:
                print ' %10s: %7.3f, %7.3f' % (output, begin, end)


if __name__ == '__main__':
    main(len(sys.argv), sys.argv)

ひさしぶりにツイッターボットをつくった

適当に言葉を作り続けるボットをつくった
あれなそれ (arenasore) on Twitter


ペニーオークションでまた入札暴走wwwww 定価3750円の商品が60705円www

何日か前にこの記事を読んで、下にあるようなさくらアカウントのIDが何気に面白いなーと気になっていたので、真似して形容詞と名詞を適当に組み合わせてポストするツイッターボットを作ってみた。

¥60,705 抱きとめるスフィンクス 入札
¥60,690 味気ないコオロギ 入札
¥60,675 抱きとめるスフィンクス 入札
¥60,660 しゃらくさいリス 入札
¥60,645 味気ないコオロギ 入札
¥60,630 しゃらくさいリス 入札
¥60,615 抱きとめるスフィンクス 入札
¥60,600 味気ないコオロギ 入札
¥60,585 抱きとめるスフィンクス 入札
¥60,570 しゃらくさいリス 入札

適当に組み合わせて普段耳にしないようなフレーズが出来上がっても、なんか知らないけどドラマが垣間見えるような気がして面白い。
これってハライチの漫才なんかと同じアプローチで、ボットの場合はノリツッコミをしてくれる人はいないけど、読み手の想像力にすべて委ねるような無責任さも悪くないなーと思います。
リプライに反応する機能もつけたので、空リプライでも送ってあげるとなんか適当なことを言います。

ポストの例

Audio Data APIを使ったMMLシーケンサー、マニュアルも書いた。

MMLを拡張しまくったので、その分のマニュアルも書いた。


MMLSequencer.js


音源は VCO - VCF - VCA のアナログシンセのモデルを採用していて、
オシレータが2基、LFOが1基、ADSRタイプのエンベロープが3基ある。


LFOエンベロープはパッチコマンドで別のモジュールに繋げてその値を時間で操作したりすることができる。
例えば初期設定ではLFOはVCOのFMに繋がっていてモジュレーションの効果に使っているけど、それをVCFのカットオフに繋いでワウみたいな効果が出せたりする(ハズ)。


正直、自分でも使いこなせなくてちゃんと動いているのかよく分からないけど、そこそこな音は出ていると思う。

しつこくデモ動画を掲載

BEHIND THE MASK (YMO)

TO MAKE THE END OF BATTLE (Ys2オープニング)