月別アーカイブ: 2012年8月

前後の無音をトリムするNyquistプラグイン

 

Audacityで数千のwavファイルの前後の無音をトリムする必要があったので、Lispで動くらしいNyquistプラグインってので対応した。ネットに落ちてた元ネタに設定項目を追加した改造版。

;nyquist plug-in
;version 1
;type process
;categories "http://lv2plug.in/ns/lv2core/#UtilityPlugin"
;name "Trim Silence jamadam..."
;action "Trimming..."
;info "by Steve Daulton (www.easyspacepro.com). Released under GPL v2.\n\nTrims silence from the beginning and end of the selection.\n"

;control thresh "Silence Threshold (dB)" real "" -48 -100 0
;control beforedur "Starting point [seconds before sound starts]" real "" 0.1 0.0 1.0
;control afterdur "Ending point [seconds after sound ends]" real "" 0.1 0.0 1.0

;; TrimSilence.ny by Steve Daulton. Aug 2011.
;; Released under terms of the GNU General Public License version 2:
;; http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
;; Requires Audacity 1.3.8 or later.

;convert threhold to linear
(setq thresh (db-to-linear (min 0 thresh)))

;modulo
(defun mod (x y)
    (setq y (float y))
    (round (* y(-(/ x y)(truncate(/ x y))))))
    
;convert to hh:mm:ss
(defun to-hhmmss (seconds)
    (let* ((hh (truncate (/ seconds 3600)))
                (mm (truncate (/ (mod seconds 3600) 60)))
                (ss (mod seconds 60)))
        (format nil "~ah:~am:~as" hh mm ss)))

(if (< len ny:all) ;max length that can be processed
    (let*
            ;make stereo sound mono
            ((mysound (if (arrayp s)(s-max (aref s 0)(aref s 1)) s))
            (start-count 0)
            (end-count 0)
            (flag 0)
            ;ratio provides tighter trimming for short selections
            ;while maintaining reasonable speed for long selections
            (ratio (max 10(min 200 (round (/ len 100000.0)))))
            (my-srate (/ *sound-srate* ratio))
            (mysound (snd-avg mysound ratio ratio op-peak))
            (samples (snd-length mysound ny:all)))
        ;loop through samples
        (dotimes (i samples)
            (setq new (snd-fetch mysound))
            (cond 
                ((= flag 0)
                    ;count initial silence
                    (if (<= new thresh)
                        (setq start-count (1+ start-count))
                        (setq flag 1)))
                (T
                    ;count final silence
                    (if (<= new thresh)
                        (setq end-count (1+ end-count))
                        (setq end-count 0)))))

        (let ((start (/ start-count my-srate))
                    (end (-(get-duration 1)(/ end-count my-srate))))
            ;ensure at least 1 sample remains
            (if (>= start (get-duration 1))
                (setq start (/(1- len)*sound-srate*)))
            ;leave some silence for safe
            (setq start (- start beforedur))
            (setq end (+ end afterdur))
            ;trim
            (multichan-expand #'extract-abs start end (cue s))
            ))
    ;OR print error message
    (format nil 
"Error.\nMaximum selected duration at ~a Hz is ~a.~%Selected track is ~a.~%"
    (round *sound-srate*)
    (to-hhmmss (/ ny:all *sound-srate*))
    (to-hhmmss (get-duration 1))))

2つのサイトが完全に一致するかチェックする

 

ほーむぺーじ制作な現場の話。

ここ数年でフレームワークやテンプレートエンジンの乗り換えとかアップグレードとかを何度となく繰り返してます。サイトの機能拡充をしようと思ったらシステムが古いので、一旦、最新のシステムに乗せ換えてから作業に取りかかりたい。でもレガシーウェブ制作なので、テストケースなんてないし、全部目視で確認するのは辛い。

新システムで旧システムと同じ動作を再現できたことを確認するためにCompBotというのを使ってます。Mojolicious + Text::Diffで2つのサイトのリンクを辿りながら差分を検出するモジュールです。
https://github.com/jamadam/CompBot

テスト全体は下記のような感じになります。diff.tとでもして保存します。

use strict;
use warnings;
use utf8;
use CompBot;

my $cbot = CompBot->new;
$cbot->url_match(qr{dev.example.com});
$cbot->url_translate(sub {
    my $url = shift;
    $url->host('example.com');
    return $url;
});
$cbot->start('http://dev.example.com/');

実行。

$ perl diff.t

CompBotは、対応する2つのページのレスポンスボディをText::Diffにかけて、その結果をTest::Moreに投げてるので下記のような出力が得られます。

ok 666 - exact match for http://dev.example.com/menu/list_s.html?sh=1301&ty=7
ok 667 - exact match for http://dev.example.com/menu/detail_s.html?sh=1873&ty=11&id=131
ok 668 - exact match for http://dev.example.com/shop/bm_shop.html?cond1=1992
ok 669 - exact match for http://dev.example.com/menu/detail_s.html?sh=110&ty=11&id=125
ok 670 - exact match for http://dev.example.com/menu/list_s.html?sh=1873&ty=7
ok 671 - exact match for http://dev.example.com/menu/list_s.html?sh=1873&ty=8
not ok 672 - exact match for http://dev.example.com/menu/list_m.html?ty=1
#   Failed test 'exact match for http://dev.example.com/menu/list_m.html?ty=1'
#   at /lib/CompBot.pm line 69.
#          got: '@@ -360,8 +360,7 @@
#  <span class="weight">100g</span>
#  </div>
#  </td>
# -</tr>
#  </table>
#  <div id="sidemenu">
#  <!-- a -->
#  <!-- b -->
#  
# '
#     expected: ''
ok 673 - exact match for http://dev.example.com/menu/detail_s.html?sh=1733&ty=6&id=88
ok 674 - exact match for http://dev.example.com/menu/detail_s.html?sh=1655&ty=0&id=23
ok 675 - exact match for http://dev.example.com/menu/detail_s.html?sh=203&ty=2&id=251
ok 676 - exact match for http://dev.example.com/menu/detail_s.html?sh=1053&ty=11&id=129

サイトAとサイトBを比較する場合、サイトAから検出したURLをurl_translateで加工してサイトB用のURLを作ります。Mojo::URLインスタンスが渡ってくるので、ホスト名をスイッチするなりパスを書き換えるなり、ポート番号を変えるなりしてreturnします。

$cbot->url_translate(sub {
    my $url = shift; # Mojo::URLインスタンス
    $url->host('example.com');
    return $url;
});

レスポンスボディは比較前に改変できます。新/旧のテンプレートエンジンでインデントスタイルとかが一致しないと散々な結果になるので、正規表現で無理やり一致させたりします。あと、Mojo::DOMを通すとHTMLタグが正規化されて、どうでもいい差分が吸収されるっぽいです。この辺テキトー。

$cbot->preprocess_a(\&preprocess);
$cbot->preprocess_b(\&preprocess);

use Mojo::DOM;

sub preprocess {
    my $body = shift;
    $body =~ s{[ \t]+$}{}gm;
    $body =~ s{^[ \t]+}{}gms;
    $body =~ s{\n+}{\n}gms;
    $body = Mojo::DOM->new($body)->to_xml;
    return $body;
}    

text/*なレスポンスは全部Text::Diffの対象になります。画像は単純にレスポンスボディを比較します。画像やCSSなどはあまり動的に書き出さないので、拡張子で対象から外すこともできます。あと、URLでフィルタもできます。

$cbot->extension_not([qw{jpg png gif css js}]);

$cbot->url_match(qr{dev.example.com/});
$cbot->url_not_match(qr{dev.example.com/test1/});
$cbot->url_not_match(qr{dev.example.com/test2/});

パラメータ含めると何万ページにもなったりして、全部チェックする気はありません。サンプリング検査的に実行したいのでキューをシャッフルします。

$cbot->shuffle(1);

これで、OKが1000件くらい並べば、正しく乗せ換えできたっぽいなあ、と少し安心できます。テキトーです。