Vim Tips Wiki
(Insert TipProposed template + manual clean)
 
Line 1: Line 1:
  +
{{TipProposed
  +
|id=0
  +
|previous=0
  +
|next=0
  +
|created=August 23, 2013
  +
|complexity=basic
  +
|author=Vimsical
  +
|version=7.0
  +
|subpage=/2013
  +
|category1=
  +
|category2=
  +
}}
 
You can use Vim to control [[wikipedia:VLC media player|VLC]] and edit song lyrics or audio transcripts:
 
You can use Vim to control [[wikipedia:VLC media player|VLC]] and edit song lyrics or audio transcripts:
 
*You can add audio timing information to each line of text transcript.
 
*You can add audio timing information to each line of text transcript.
Line 17: Line 29:
 
</pre>
 
</pre>
   
== Vim setup ==
+
==Vim setup==
 
<pre>
 
<pre>
 
" VIM Setup:
 
" VIM Setup:
Line 39: Line 51:
 
" 3. 0:1:5 .. press F9 to cue/seek vlc to 65 second.
 
" 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.
 
" 4. press F9, to add [time][filename playing in vlc] to this line.
" TODO:
+
" TODO:
 
" o Test Audio filenames with spaces, unicode characters
 
" o Test Audio filenames with spaces, unicode characters
 
" o Test on Linux
 
" o Test on Linux
Line 51: Line 63:
 
nmap <F10> :call Vlc_Get_Filename()<CR>
 
nmap <F10> :call Vlc_Get_Filename()<CR>
 
nmap <F11> :call Vlc_Get_Time()<CR>
 
nmap <F11> :call Vlc_Get_Time()<CR>
 
 
</pre>
 
</pre>
   
Line 59: Line 70:
 
" What: VLC remote control, for editing synchronizing lyrics.
 
" What: VLC remote control, for editing synchronizing lyrics.
 
" How: Remote control vlc via tcp socket.
 
" How: Remote control vlc via tcp socket.
" AUTHOR: GPL(C) moshahmed/at/gmail
+
" AUTHOR: GPL(C) moshahmed/at/gmail
 
" ==============================================================================
 
" ==============================================================================
 
function! Vlc_Set_File_Time()
 
function! Vlc_Set_File_Time()
Line 67: Line 78:
 
let l:absfile = ''
 
let l:absfile = ''
 
if len(l:matcher_file) > 4
 
if len(l:matcher_file) > 4
:call system("perl ~/vim/vlc.pl -cmd=set_file=" . l:matcher_file[1])
+
call system("perl ~/vim/vlc.pl -cmd=set_file=" . l:matcher_file[1])
 
else
 
else
 
" Look for cfile (filename under cursor) in PWD
 
" Look for cfile (filename under cursor) in PWD
let l:cfile = expand("<cfile>")
+
let l:cfile = expand("<cfile>")
" let l:absfile = expand("<cfile>:p")
+
" let l:absfile = expand("<cfile>:p")
 
" if len(l:absfile) > 1 && filereadable('"'.l:absfile.'"')
 
" if len(l:absfile) > 1 && filereadable('"'.l:absfile.'"')
 
" endif
 
" endif
Line 77: Line 88:
 
let l:absfile = getcwd()."/".l:cfile
 
let l:absfile = getcwd()."/".l:cfile
 
if filereadable(l:absfile)
 
if filereadable(l:absfile)
:call system("perl ~/vim/vlc.pl -cmd=set_file=file:///".l:absfile)
+
call system("perl ~/vim/vlc.pl -cmd=set_file=file:///".l:absfile)
 
else
 
else
 
echo "Cannot read ".l:absfile
 
echo "Cannot read ".l:absfile
Line 88: Line 99:
 
let l:matcher_time = matchlist(getline('.'),'\v\[(\d.*)\]')
 
let l:matcher_time = matchlist(getline('.'),'\v\[(\d.*)\]')
 
if len(l:matcher_time) > 0
 
if len(l:matcher_time) > 0
:call system( "perl ~/vim/vlc.pl -cmd=set_time=" . l:matcher_time[1])
+
call system( "perl ~/vim/vlc.pl -cmd=set_time=" . l:matcher_time[1])
 
else
 
else
 
" Look for timestamp colon without brackets on cline, eg. 1:2 for 62 seconds.
 
" Look for timestamp colon without brackets on cline, eg. 1:2 for 62 seconds.
Line 99: Line 110:
 
" If neither filename or time on cline, insert them.
 
" If neither filename or time on cline, insert them.
 
if len(l:matcher_time) < 1 && len(l:matcher_file) < 1 && !filereadable(l:absfile)
 
if len(l:matcher_time) < 1 && len(l:matcher_file) < 1 && !filereadable(l:absfile)
:call Vlc_Get_Filename()
+
call Vlc_Get_Filename()
:call Vlc_Get_Time()
+
call Vlc_Get_Time()
 
endif
 
endif
 
endfunction
 
endfunction
Line 130: Line 141:
 
endif
 
endif
 
endfunction
 
endfunction
" ==============================================================================
+
" ==============================================================================
 
</pre>
 
</pre>
   
== ~/vim/vlc.pl ==
+
==~/vim/vlc.pl==
 
<pre>
 
<pre>
 
#!/usr/bin/perl -w
 
#!/usr/bin/perl -w
Line 192: Line 203:
 
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 seconds_to_hms($line);
 
print STDOUT seconds_to_hms($line);
Line 257: Line 268:
 
}
 
}
 
return $hms;
 
return $hms;
}
+
}
 
</pre>
 
</pre>
   

Latest revision as of 10:40, 31 January 2014

Proposed tip Please edit this page to improve it, or add your comments below (do not use the discussion page).

Please use new tips to discuss whether this page should be a permanent tip, or whether it should be merged to an existing tip.
created August 23, 2013 · complexity basic · author Vimsical · version 7.0

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+)>')
  let l:absfile = ''
  if len(l:matcher_file) > 4
    call system("perl ~/vim/vlc.pl -cmd=set_file=" . l:matcher_file[1])
  else
    " Look for cfile (filename under cursor) in PWD
    let l:cfile = expand("<cfile>")
    " let l:absfile = expand("<cfile>:p")
    " if len(l:absfile) > 1 && filereadable('"'.l:absfile.'"')
    " endif
    if len(l:cfile) > 4 && filereadable(expand(l:cfile))
      let l:absfile = getcwd()."/".l:cfile
      if filereadable(l:absfile)
        call system("perl ~/vim/vlc.pl -cmd=set_file=file:///".l:absfile)
      else
        echo "Cannot read ".l:absfile
        return
      endif
    endif
  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 filename or time on cline, insert them.
  if len(l:matcher_time) < 1 && len(l:matcher_file) < 1 && !filereadable(l:absfile)
    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
    " relfile=$(basename filename), insert shorter relative path if possible.
    " TODO: quote filenames; s/// fails if filename has commas, etc.
    let l:relfile=substitute(l:fileis, '^.*/', '', '')
    if filereadable(l:relfile)
      exe ':s,^, '.l:relfile.','
    else
      exe ':s,^,'.l:fileis.','
    endif
  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.'/'
    " TODO: try this:
    " :put ='"'.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; # read stdin (unused).
    }else{ die $USAGE,"Unknown option '$_'\n"; }
}

# foreach(@ARGV) { } .. process leftover args (unused).

# 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:%02d:02%d", $hourz,$minz,$secz)
  }
  # print 3 seconds as 00:03, so timestamp always has a colon.
  return sprintf ("%02d:%02d", $minz,$secz)
}

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)