instagramの写真をグリッチしまくるWEBサービスをつくった
instagramのAPIが公開された直後、なんとかstagramってWEBサービスがいくつか出てきて、僕は面白いなーって見てるだけだったんだけど、ちょっと思いついたので今更ながら作ってみました。
ソースコードとか
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を分割する (すべての自炊派バンドマンに捧げる)
やりかた
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
雑感
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シーケンサーを作っています。
SiONのMMLを
このあたりのMMLは結構聴ける(適宜コピペしてお試しください)
- http://mmltalks.appspot.com/m/RaKRP
- http://mmltalks.appspot.com/m/DIZpo
- http://mmltalks.appspot.com/m/jVIoz (FM音源が惜しい)
で、
で、表題の件ですがJavaScriptでリアルタイムに音を鳴らすには以下の3つの方法があります。
簡単なサンプルをあげています → サンプル ソース
*Operaでは何故か動きません。(MMLシーケンサーの方は何故か動く)
2011/09/18 追記
OSX Lion Chrome 14で Web Audio API がクラッシュする問題が解決されているみたいです。
(OSX版Google 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; };
Pythonで英語CDを分割してみた
蒸し暑くなってきて、もともと少ないやる気がさらに無くなってきたのだけど、
多少は何かしないといけないなぁと思って、なんとなくDUO3.0の例文でも覚えようかと思った。
で、英語CDにありがちな1トラックにいくつかのセンテンスが入っているのが鬱陶しいので、
とりあえず分割ファイルをつくろうと思ってスクリプトを書いてみた。
やり方はいたって単純でレベルの低い部分がしばらく続いたら分割する。
こんな感じでやった
- iTunesでwavエンコーダを使ってCDからwavファイルを作る。
- 設定ファイルを作る
- 実行
設定ファイルは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 しゃらくさいリス 入札
適当に組み合わせて普段耳にしないようなフレーズが出来上がっても、なんか知らないけどドラマが垣間見えるような気がして面白い。
これってハライチの漫才なんかと同じアプローチで、ボットの場合はノリツッコミをしてくれる人はいないけど、読み手の想像力にすべて委ねるような無責任さも悪くないなーと思います。
リプライに反応する機能もつけたので、空リプライでも送ってあげるとなんか適当なことを言います。