「テープ起こし」を手助けするスクリプトを perl で作ってみる

| 0 Comments | 0 TrackBacks

ボイスレコーダーで 2 時間とか録音したのはいいけど、これどうやってテキストデータにするの?という事態が勃発しました。キーボードにシーク動作とか割り当てて、自動リピートとかあれば便利だなぁと思い、perl で書いてみました。

Alt + Tab で他のウィンドと行き来したあとで、全くキーを受け取らなくなる現象があってハマリました。GetEvents で入力があるか確認してから Input で入力内容を得るとハマらずに済みます。シーク時に Win32::MediaPlayer まわりに与える時間は ms に直して渡しました。

「テープ起こし」に関するブックマークコメントより - blog.woremacx.com について関連エントリを書きました

今回のスクリプトのコンセプト

  • 自動的に A-B リピートになる
  • 4 種類の移動量でシークできる
  • perl で書いてあるため、perl の環境さえ入っていれば容易に改造できる

使い方

開くファイルをスクリプトの引数として起動します。起動したら、勝手に 10 秒リピートになっています。

  • q: 終了
  • j: 5 秒後
  • k: 10 秒後
  • l: 30 秒後
  • ;: 5 分後
  • f: 5 秒前
  • d: 10 秒前
  • s: 30 秒前
  • a: 5 分前
  • t: リピートするかのトグル
  • u: リピート秒数--
  • i: リピート秒数++

コード

coderepos にもうpしました repeat-audio-player.pl

#!/usr/bin/perl

use strict;
use warnings;

use threads;
use Thread::Queue;
use Win32::Console;
use Win32::MediaPlayer;
use Time::HiRes ();

sub keywatcher {
    my ($queue) = shift;

    my $console = new Win32::Console(STD_INPUT_HANDLE);
    $console->Mode(ENABLE_PROCESSED_INPUT);

    #     my $mode = $console->Mode();
    #     print "ENABLE_LINE_INPUT\n" if $mode & ENABLE_LINE_INPUT;
    #     print "ENABLE_ECHO_INPUT\n" if $mode & ENABLE_ECHO_INPUT;
    #     print "ENABLE_PROCESSED_INPUT\n" if $mode & ENABLE_PROCESSED_INPUT;
    #     print "ENABLE_WINDOW_INPUT\n" if $mode & ENABLE_WINDOW_INPUT;

    while (1) {
        while ($console->GetEvents) {
            my @event = $console->Input;
            # 0: event type: 1 for keyboard
            # 1: key down: TRUE if the key is being pressed, FALSE if the key is being released
            # 2: repeat count: the number of times the key is being held down
            # 3: virtual keycode: the virtual key code of the key
            # 4: virtual scancode: the virtual scan code of the key
            # 5: char: the ASCII code of the character (if the key is a character key, 0 otherwise)
            # 6: control key state: the state of the control keys (SHIFTs, CTRLs, ALTs, etc.)

            next unless $event[1];

            my $c = lc(chr($event[5]));
            $queue->enqueue($c);
            return 0 if $c eq 'q';
        }
    }

    return 0;
}

sub worker {
    my ($queue, $file) = @_;

    my $wp = Win32::MediaPlayer->new;
    $wp->load($file) or die;
    $wp->play;

    my $state = {
        mute => 0,
        playing => 1,
        pos => 0,
        len => res_to_sec($wp->length(0)),
        base => 0,
        repeat => 1,
        repeat_dur => 10,
    };

    my %keymap = (
        j => 5,
        k => 10,
        l => 30,
        ';' => 300,
        f => -5,
        d => -10,
        s => -30,
        a => -300,
    );

    printf "repeats each %d secs\n", $state->{repeat_dur};
    my $console = new Win32::Console(STD_OUTPUT_HANDLE);
    my $seek = 0;
    while (1) {
        $state->{len} = res_to_sec($wp->length(0));
        $state->{pos} = res_to_sec($wp->pos(0));

        if ($seek) {
            $wp->pause;
            $wp->seek($state->{base} * 1000);
            $wp->resume;
            $seek = 0;
            next;
        }

        my $buf = sprintf "%s / %s\r", sec_to_hms($state->{pos}), sec_to_hms($state->{len});
        $console->Write($buf);
        Time::HiRes::sleep(0.5);

        my $repeat_state = sub {
            printf "\n";
            printf "repeat %s\n", $state->{repeat} ? "on" : "off";
            printf "repeats each %d secs\n", $state->{repeat_dur} if $state->{repeat};
        };

        if ($state->{repeat} &&
            $state->{pos} >= ($state->{base} + $state->{repeat_dur})) {
            $seek++;
        }

        while ($queue->pending) {
            my $c = $queue->dequeue;

            if (defined($keymap{$c})) {
                unless ($state->{repeat}) {
                    $state->{base} = $state->{pos};
                }
                $state->{base} += $keymap{$c};
                $seek++;
            } elsif ($c eq 't') {
                $state->{repeat} = $state->{repeat} ? 0 : 1;
                $repeat_state->();
            } elsif ($c eq 'r') {
                $repeat_state->();
            } elsif ($c eq 'u') {
                $state->{repeat_dur}--;
                $state->{repeat_dur} = 1 if $state->{repeat_dur} < 1;
                $repeat_state->();
            } elsif ($c eq 'i') {
                $state->{repeat_dur}++;
                $repeat_state->();
            } elsif ($c eq 'p') {
                $seek++;
            }

            return 0 if $c eq 'q';
        }
    }

    return 0;
}

sub main {
    my $file = shift(@ARGV);
    die unless $file;

    my $queue = new Thread::Queue;
    my $t1 = threads->new(\&keywatcher, $queue);
    my $t2 = threads->new(\&worker, $queue, $file);
    $t1->join;
    $t2->join;

    print "done.\n";
}

main;

sub res_to_sec {
    my $param = shift;
    return int($param/1000);
}

sub res_to_hms {
    my $param = shift;
    return sec_to_hms(res_to_sec($param));
}

sub sec_to_hms {
    my $total = shift;

    my $sec = $total % 60;
    my $min = ($total - $sec) / 60;
    my $hour = int($min / 60);
    $min = $min % 60;

    return sprintf "%02d:%02d:%02d", $hour, $min, $sec;
}

sub sec_to_ms {
    my $total = shift;

    my $sec = $total % 60;
    my $min = ($total - $sec) / 60;

    return sprintf "%02d:%02d", $min, $sec;
}
OLYMPUS Voice-Trek V-61
OLYMPUS Voice-Trek V-61
posted with amazlet at 08.11.16
オリンパス (2007-03-23)
売り上げランキング: 283
おすすめ度の平均: 4.5
4 いろいろ使えます。
5 ライブの録音・再生に大活躍
OLYMPUS ICレコーダー Voice-Trek DS-60
オリンパス (2007-09-14)
売り上げランキング: 1822
おすすめ度の平均: 5.0
5 音楽用にも
5 買うならコレ
5 値段にはわけがある
5 クリアーな音質です

No TrackBacks

TrackBack URL: http://blog.woremacx.com/MT/mt-tb.cgi/268

Leave a comment