Vim Tips Wiki
Register
(cleanup of names and some fixes)
(cleanup, fixes, added hms time functions)
Line 108: Line 108:
 
</pre>
 
</pre>
   
  +
== ~/vim/vlc.pl ==
==Keep this perl file in your path==
 
 
<pre>
 
<pre>
 
#!/usr/bin/perl -w
 
#!/usr/bin/perl -w
# was telnet.pl from perlmonks, modified to work with vlc remote control.
+
# telnet.pl from perlmonks, modified to work with vlc remote control.
# GPL(C) modifed by moshahmed on 2013-08-22
+
# AUTHOR: GPL(C) moshahmed/at/gmail 2013-08-22
  +
# $Id: vlc.pl,v 1.1 2013-08-26 16:44:44 a Exp $
 
use strict;
 
use strict;
 
use IO::Socket;
 
use IO::Socket;
Line 122: Line 123:
 
my $USAGE=q{
 
my $USAGE=q{
 
USAGE: $0 [options] -cmd=VLC_CMDS
 
USAGE: $0 [options] -cmd=VLC_CMDS
WHAT: control vlc from Vim
+
WHAT: control vlc from vim
 
Options:
 
Options:
 
-port=NN .. default ($port)
 
-port=NN .. default ($port)
Line 130: Line 131:
 
VLC_CMDS:
 
VLC_CMDS:
 
play, pause, pause (toggle), stop,
 
play, pause, pause (toggle), stop,
get_length .. in seconds
+
get_length .. in h:m:seconds
get_time .. in seconds
+
get_time .. in h:m:seconds
 
get_file .. gets file:///path
 
get_file .. gets file:///path
set_time=1:2:3.40 .. for 1hour 2minutes 3seconds
+
set_time=1:2:3.40 .. for 1hour 2minutes 3seconds (integral seconds only)
 
set_file=file:///c:/tmp/happy.mp3
 
set_file=file:///c:/tmp/happy.mp3
 
};
 
};
Line 144: Line 145:
 
}elsif( m/^-port=(\d+)$/ ){ $port = $1;
 
}elsif( m/^-port=(\d+)$/ ){ $port = $1;
 
}elsif( m/^-cmd=(set_.+)=(.+)$/ ){ $cmd = $1; $value = $2;
 
}elsif( m/^-cmd=(set_.+)=(.+)$/ ){ $cmd = $1; $value = $2;
# hms_to_seconds(1:2:3.4) => 1h.2m.3.4s = 1*3600+2*60+3 = 3723.
+
$value = hms_to_seconds($value);
if ($cmd eq 'set_time' && $value =~ m,:, ) {
+
warn "$cmd=$value\n" if $debug;
if ( $value =~ m,^(\d+):(\d+):(\d+), ) { # trailing chars ignored
 
$value = $1 * 3600 + $2 * 60 + $3;
 
}elsif( $value =~ m,^(\d+):(\d+), ) {
 
$value = $1 * 60 + $2;
 
}
 
}
 
die "debug: -cmd=$cmd=$value\n" if $debug;
 
 
}elsif( m/^-cmd=(.+)$/ ){ $cmd = $1;
 
}elsif( m/^-cmd=(.+)$/ ){ $cmd = $1;
 
}elsif( m/^-linein$/ ){ $linein=1;
 
}elsif( m/^-linein$/ ){ $linein=1;
Line 172: Line 166:
 
DONE:
 
DONE:
 
while ( defined( $line = <$handle> ) ) {
 
while ( defined( $line = <$handle> ) ) {
if ($cmd eq 'get_length') {
+
if ($cmd eq 'get_length') {
 
if( $line =~ m,^\d+,) {
 
if( $line =~ m,^\d+,) {
print STDOUT $line;
+
print STDOUT seconds_to_hms($line);
 
last DONE;
 
last DONE;
 
}
 
}
 
}elsif($cmd eq 'get_time') {
 
}elsif($cmd eq 'get_time') {
 
if( $line =~ m,^\d+,) {
 
if( $line =~ m,^\d+,) {
print STDOUT $line;
+
print STDOUT seconds_to_hms($line);
 
last DONE;
 
last DONE;
 
}
 
}
Line 215: Line 209:
 
exit 0;
 
exit 0;
 
}
 
}
  +
  +
sub seconds_to_hms {
  +
my $hourz=int($_[0]/3600);
  +
my $leftover=$_[0] % 3600;
  +
my $minz=int($leftover/60);
  +
my $secz=int($leftover % 60);
  +
if ($hourz > 0){
  +
return sprintf ("%d:%d:%d", $hourz,$minz,$secz)
  +
}elsif( $minz > 0) {
  +
return sprintf ("%d:%d", $minz,$secz)
  +
}
  +
return $_[0];
  +
}
  +
  +
sub hms_to_seconds {
  +
# hms_to_seconds(1:2:3.4) => 1h.2m.3.4s = 1*3600+2*60+3 = 3723.
  +
my $hms = $_[0];
 
if ( $hms =~ m,^(\d+):(\d+):(\d+), ) { # ignore trailing chars.
 
return $1 * 3600 + $2 * 60 + $3;
 
}elsif( $hms =~ m,^(\d+):(\d+), ) {
 
return $1 * 60 + $2;
 
}
  +
return $hms;
  +
}
 
</pre>
 
</pre>
   

Revision as of 03:37, 27 August 2013

You can use Vim to control VLC and edit song lyrics or audio transcripts:

  • You can add audio timing information to each line of text transcript.
  • You can cue VLC audio to the time/line you are editing.

You will need:

  • Vim to edit text.
  • Perl to connect gvim to vlc via a socket.
  • VLC.

Below: Modify the path to Perl and Vim script according to the directory you save them in.

Setup vlc to listen on a socket

"   VLC setup: $ vlc > tools > prefs > main interface > enable RC,
"       Tcp-Input=[localhost:2150]  .. ~/vim/vlc.pl will open this socket.
"       Console: [donot_open] console

Vim setup

"   VIM Setup:
"     c:\> vim c:/atmp/happy.lrc
"     :so %
"     and cline is '[0:23.10] file:///C:/atmp/happy.mp3' .. press F9 to play from 23s
"   Music player key:
"     [F2] Play
"     [F3] Stop
"     [F4] Pause toggle
"   Lyric editor keys:
"     [F9]  Set filename/time from cline to vlc, or get filename/time to cline.
"     [F10] Get file from vlc into cline.
"     [F11] Get Time from vlc into cline.
" Notes:
"     To debug: change 'perl ~/vim/vlc.pl' to 'vimrun perl ~/vim/vlc.pl -debug'
" Testing:
"     Tested gvim73,cygwin perl 5, cygwin,vlc 2, on XP-PRO SP4.
"     1. file:///c:/atmp/happy.mp3 .. press F9 play this file in vlc.
"     2. [0:1:4] file:///c:/atmp/happy.mp3 .. press F9 play this file from 64 seconds.
"     3. 0:1:5 .. press F9 to cue/seek vlc to 65 second.
"     4. press F9, to add [time][filename playing in vlc] to this line.
" TODO: 
"     o Test Audio filenames with spaces, unicode characters
"     o Test on Linux
" ==============================================================================

nmap <F2>   :call system("perl ~/vim/vlc.pl -cmd=play")<CR>
nmap <F3>   :call system("perl ~/vim/vlc.pl -cmd=stop")<CR>
nmap <F4>   :call system("perl ~/vim/vlc.pl -cmd=pause")<CR>

nmap <F9>   :call Vlc_Set_File_Time()<CR>
nmap <F10>  :call Vlc_Get_Filename()<CR>
nmap <F11>  :call Vlc_Get_Time()<CR>
                                    

Source vlc.vim

" $Header: c:/cvs/repo/mosh/vim/vlc.vim,v 1.1 2013-08-26 17:04:52 a Exp $
" What: VLC remote control, for editing synchronizing lyrics.
" How:  Remote control vlc via tcp socket.
" AUTHOR: GPL(C) moshahmed/at/gmail   
" ==============================================================================
function! Vlc_Set_File_Time()

  " Look for filename on cline
  let l:matcher_file = matchlist(getline('.'),'\v<(file:///.*[.]\w+)>')
  if len(l:matcher_file) > 4
    :call system("perl ~/vim/vlc.pl -cmd=set_file=" . l:matcher_file[1])
  endif

  " Look for timestamp on cline, eg. [10] for 10 seconds, [1:1] for 61 seconds.
  let l:matcher_time = matchlist(getline('.'),'\v\[(\d.*)\]')
  if len(l:matcher_time) > 0
    :call system( "perl ~/vim/vlc.pl -cmd=set_time=" . l:matcher_time[1])
  else
    " Look for timestamp colon without brackets on cline, eg. 1:2 for 62 seconds.
    let l:matcher_time = matchlist(getline('.'),'\v(\d+:[0-9:.]+)')
    if len(l:matcher_time) > 0
      call system("perl ~/vim/vlc.pl -cmd=set_time=" . l:matcher_time[1])
    endif
  endif

  " If neither file or time on cline, insert them.
  if len(l:matcher_time) < 1 && len(l:matcher_file) < 1
    :call Vlc_Get_Filename()
    :call Vlc_Get_Time()
  endif
endfunction

" ==============================================================================
function! Vlc_Get_Filename()
  let l:fileis=system("perl ~/vim/vlc.pl -cmd=get_file")
  if  len(l:fileis) > 0
    exe ':s/^/'.l:fileis.'/'
  endif
endfunction

" ==============================================================================
function! Vlc_Get_Time()
  let l:timeis=system("perl ~/vim/vlc.pl -cmd=get_time")
  if  len(l:timeis) > 0
    let l:timeis=substitute(l:timeis, '[^0-9:.]', '', 'g')
    let l:timeis='['.l:timeis.']'
    exe ':s/^/'.l:timeis.'/'
  endif
endfunction
" ==============================================================================     

~/vim/vlc.pl

#!/usr/bin/perl -w
# telnet.pl from perlmonks, modified to work with vlc remote control.
# AUTHOR: GPL(C) moshahmed/at/gmail 2013-08-22
# $Id: vlc.pl,v 1.1 2013-08-26 16:44:44 a Exp $
use strict;
use IO::Socket;

my ( $host, $port, $kidpid, $handle, $line );
( $host, $port ) = ('127.0.0.1', 2150);
my ($verbose,$debug,$cmd,$value,$linein) = (0,0,'',,'');

my $USAGE=q{
USAGE: $0 [options] -cmd=VLC_CMDS
WHAT: control vlc from vim
Options:
    -port=NN  .. default ($port)
    -h      .. help.
    -v      .. verbose
    -debug  .. to debug, also use ./telnet.pl to communicate with vlc
VLC_CMDS:
    play, pause, pause (toggle), stop,
    get_length .. in h:m:seconds
    get_time   .. in h:m:seconds
    get_file   .. gets file:///path
    set_time=1:2:3.40  .. for 1hour 2minutes 3seconds (integral seconds only)
    set_file=file:///c:/tmp/happy.mp3
};

# Process args
while( $_ = $ARGV[0], defined($_) && m/^-/ ){ shift; last if /^--$/; if(0){
    }elsif( m/^-[h?]$/ ){ die $USAGE;
    }elsif( m/^-v$/    ){ $verbose++;
    }elsif( m/^-debug$/    ){ $debug++;
    }elsif( m/^-port=(\d+)$/ ){ $port = $1;
    }elsif( m/^-cmd=(set_.+)=(.+)$/ ){ $cmd = $1; $value = $2;
      $value = hms_to_seconds($value);
      warn "$cmd=$value\n" if $debug;
    }elsif( m/^-cmd=(.+)$/ ){ $cmd = $1;
    }elsif( m/^-linein$/ ){ $linein=1;
    }else{ die $USAGE,"Unknown option '$_'\n"; }
}

# create a tcp connection to the specified host and port
$handle = IO::Socket::INET->new(
    Proto    => "tcp", PeerAddr => $host, PeerPort => $port
  ) or die "can't connect to port $port on $host: $!";
  $handle->autoflush(1);    # so output gets there right away
print STDERR "[Connected to $host:$port]\n" if $verbose;

# split the program into two processes, identical twins
die "can't fork: $!" unless defined( $kidpid = fork() );

if ($kidpid) { # In parent, parse reply from vlc.
    # the if{} part runs only in the parent process
    DONE:
    while ( defined( $line = <$handle> ) ) {
      if    ($cmd eq 'get_length') { 
        if( $line =~ m,^\d+,) {
          print STDOUT seconds_to_hms($line);
          last DONE;
        }
      }elsif($cmd eq 'get_time') {
        if( $line =~ m,^\d+,) {
          print STDOUT seconds_to_hms($line);
          last DONE;
        }
      }elsif($cmd eq 'get_file') {
        if( $line =~ m,(file://.*)\s[()],i ) {
          my $filename = $1;
          print STDOUT $filename;
          last DONE;
        }
      }elsif($cmd eq 'status') {
        print STDOUT $line;
        last DONE if $line =~ m,returned,;
      }elsif($cmd eq 'play') { last DONE;
      }elsif($cmd eq 'pause') { last DONE;
      }elsif($cmd eq 'stop') { last DONE;
      }else{ print STDOUT $line;
        last DONE if $line =~ m,returned,;
      }
    } # DONE
    kill( "TERM", $kidpid );    # send SIGTERM to child
    exit 0;
} else {  # In child? send cmd to vlc
    if(     $cmd eq 'status' ){ print $handle "$cmd\n";
    }elsif( $cmd eq 'play' ){ print $handle "$cmd\n";
    }elsif( $cmd eq 'pause' ){ print $handle "$cmd\n";
    }elsif( $cmd eq 'stop' ){ print $handle "$cmd\n";
    }elsif( $cmd eq 'get_time' ){ print $handle "$cmd\n";
    }elsif( $cmd eq 'get_file' ){ print $handle
        "pause\nstatus\npause\nstatus\n";
    }elsif( $cmd eq 'get_length' ){ print $handle "play\nget_length\n";
    }elsif( $cmd eq 'set_time' ){ print $handle "seek $value\n";
    }elsif( $cmd eq 'set_file' ){ print $handle "clear\nadd $value\n";
    }else{                        print $handle "$cmd\n";
    }
    exit 0;
}

sub seconds_to_hms {
  my $hourz=int($_[0]/3600);
  my $leftover=$_[0] % 3600;
  my $minz=int($leftover/60);
  my $secz=int($leftover % 60);
  if ($hourz > 0){
    return sprintf ("%d:%d:%d", $hourz,$minz,$secz)
  }elsif( $minz > 0) {
    return sprintf ("%d:%d", $minz,$secz)
  }
  return $_[0];
}

sub hms_to_seconds {
    # hms_to_seconds(1:2:3.4) => 1h.2m.3.4s = 1*3600+2*60+3 = 3723.
    my $hms = $_[0];
    if ( $hms =~ m,^(\d+):(\d+):(\d+), ) { # ignore trailing chars.
      return $1 * 3600 + $2 * 60 + $3;
    }elsif( $hms =~ m,^(\d+):(\d+), ) {
      return $1 * 60 + $2;
    }
    return $hms;
}  

Comments

That's a lot of script for a tip. I have done some quick formatting, but have not thought about the proposed tip yet. One point is that the "Mosh" function names are a bit jarring and should be replaced with s:. JohnBeckett (talk) 05:16, August 24, 2013 (UTC)