VLC Tcl Extension edit
bll 2016-12-28 : One full day's work. This is a Tcl interface to libvlc, which comes with the VLC media player. The tighter interface gives better control over song position, time position and player state.I only need audio, and don't use playlists. The media sub-command only handles file paths right now. There's quite a bit that could still be implemented. But most of that implementation would be fairly straightforward.I have given up trying to get callbacks working. Calling Tcl code from the VLC event handler does not seem to work properly on Windows.2017-1-3: Fix for XP and Vista2017-6-12: Fixed exit callback; Added commands to get device list and set device.I have the dynamic libraries built for VLC 2.1.x and 2.2.x for Windows 32/64, Linux 32/64 and Mac OS X if you want a pre-built copy.See Also: MPV Tcl ExtensionJJM 2017-01-03: It might be nice to put this in a Fossil repository someplace?  It looks quite useful.bll 2017-1-3: Feel free.  It's a little basic at the moment as I only need audio, but adding the non-file media type, playlist management and video shouldn't be difficult.   It is working well -- I have been using the telnet interface to VLC for the last five years, and this is rather better.DG 17-03-10 Super SWEET!  Just what I was looking for :)
test.tcl#!/usr/bin/tclsh
set os $::tcl_platform(os)
if { [regexp -nocase {^win} $os] } {
  set os Windows
}
load [pwd]/$os/tclvlc64[info sharedlibextension]
tclvlc init --intf dummy --no-video --ignore-config \
    --no-media-library --no-playlist-autostart \
    --no-loop --no-random --no-repeat --quiet --play-and-stop \
    --audio-filter scaletempo
if { $os eq "Linux" } {
  tclvlc media /home/bll/sources/ballroomdj/test.dir/test-files/counter.mp3
}
if { $os eq "Darwin" } {
  tclvlc media /Users/bll/Desktop/BallroomDJ.app/Contents/MacOS/test.dir/test-files/counter.mp3
}
if { $os eq "Windows" } {
  tclvlc media [file nativename {C:/Users/bll/Desktop/BallroomDJ/test.dir/test-files/counter.mp3}]
}
tclvlc play
set state [tclvlc state]
while { $state eq "opening" || $state eq "buffering" } {
  after 100
  set state [tclvlc state]
}
tclvlc seek 0.0387
set ::x 0
after 1000 set ::x 1
vwait ::x
tclvlc close
exittclvlc.c/*
 * Copyright 2016-2017 Brad Lanam Walnut Creek CA US
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <memory.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <tcl.h>
#include <vlc/vlc.h>
#include <vlc/libvlc_version.h>
#ifdef COMP_WINDOWS
# include <windows.h>
#endif
typedef struct { char *name; Tcl_ObjCmdProc *proc; } EnsembleData;
typedef struct {
  libvlc_state_t        state;
  const char *          name;
} stateMap_t;
static const stateMap_t stateMap[] = {
  { libvlc_NothingSpecial,  "idle" },
  { libvlc_Opening,         "opening" },
  { libvlc_Buffering,       "buffering" },
  { libvlc_Playing,         "playing" },
  { libvlc_Paused,          "paused" },
  { libvlc_Stopped,         "stopped" },
  { libvlc_Ended,           "ended" },
  { libvlc_Error,           "error" }
};
#define stateMapMax (sizeof(stateMap)/sizeof(stateMap_t))
typedef struct {
  Tcl_Interp            *interp;
  libvlc_instance_t     *inst;
  char                  version [40];
  libvlc_media_player_t *mp;
  libvlc_state_t        state;
  int                   argc;
  const char            **argv;
  const char            *device;
} vlcData_t;
const char *
stateToStr (
  libvlc_state_t    state
  )
{
  int        i;
  const char *tptr;
  tptr = "";
  for (i = 0; i < stateMapMax; ++i) {
    if (state == stateMap[i].state) {
      tptr = stateMap[i].name;
      break;
    }
  }
  return tptr;
}
libvlc_state_t
stateToValue (
  char *name
  )
{
  int   i;
  libvlc_state_t state;
  state = libvlc_NothingSpecial;
  for (i = 0; i < stateMapMax; ++i) {
    if (strcmp (name, stateMap[i].name) == 0) {
      state = stateMap[i].state;
      break;
    }
  }
  return state;
}
void
vlcEventHandler (
  const struct libvlc_event_t *event,
  void *cd
  )
{
  vlcData_t     *vlcData = (vlcData_t *) cd;
  int           objc;
  Tcl_Obj       **objv;
  Tcl_Obj       *tobj;
  if (event->type == libvlc_MediaStateChanged) {
    vlcData->state = event->u.media_state_changed.new_state;
  }
}
int
vlcDurationCmd (
  ClientData cd,
  Tcl_Interp* interp,
  int objc,
  Tcl_Obj * const objv[]
  )
{
  int               rc;
  libvlc_time_t     tm;
  double            dtm;
  vlcData_t *vlcData = (vlcData_t *) cd;
  if (objc != 1) {
    Tcl_WrongNumArgs(interp, 1, objv, "");
    return TCL_ERROR;
  }
  rc = TCL_OK;
  if (vlcData->inst == NULL || vlcData->mp == NULL) {
    rc = TCL_ERROR;
  } else {
    tm = libvlc_media_player_get_length (vlcData->mp);
    dtm = (double) tm / 1000.0;
    Tcl_SetObjResult (interp, Tcl_NewDoubleObj (dtm));
  }
  return rc;
}
int
vlcGetTimeCmd (
  ClientData cd,
  Tcl_Interp* interp,
  int objc,
  Tcl_Obj * const objv[]
  )
{
  int       rc;
  libvlc_time_t     tm;
  double            dtm;
  vlcData_t *vlcData = (vlcData_t *) cd;
  if (objc != 1) {
    Tcl_WrongNumArgs(interp, 1, objv, "");
    return TCL_ERROR;
  }
  rc = TCL_OK;
  if (vlcData->inst == NULL || vlcData->mp == NULL) {
    rc = TCL_ERROR;
  } else {
    tm = libvlc_media_player_get_time (vlcData->mp);
    dtm = (double) tm / 1000.0;
    Tcl_SetObjResult (interp, Tcl_NewDoubleObj (dtm));
  }
  return rc;
}
int
vlcIsPlayCmd (
  ClientData cd,
  Tcl_Interp* interp,
  int objc,
  Tcl_Obj * const objv[]
  )
{
  int       rc;
  int       rval;
  vlcData_t *vlcData = (vlcData_t *) cd;
  if (objc != 1) {
    Tcl_WrongNumArgs(interp, 1, objv, "");
    return TCL_ERROR;
  }
  rc = TCL_OK;
  rval = 0;
  if (vlcData->inst == NULL || vlcData->mp == NULL) {
    rc = TCL_ERROR;
  } else {
    /*
     * In order to match the implementation of VLC's internal
     * isplaying command, return true if the player is paused
     */
    if (vlcData->state == libvlc_Opening ||
        vlcData->state == libvlc_Buffering ||
        vlcData->state == libvlc_Playing ||
        vlcData->state == libvlc_Paused) {
      rval = 1;
    }
    Tcl_SetObjResult (interp, Tcl_NewIntObj (rval));
  }
  return rc;
}
int
vlcMediaCmd (
  ClientData cd,
  Tcl_Interp* interp,
  int objc,
  Tcl_Obj * const objv[]
  )
{
  int               rc;
  libvlc_media_t    *m;
  libvlc_event_manager_t    *em;
  vlcData_t         *vlcData = (vlcData_t *) cd;
  char              *fn;
  struct stat       statinfo;
  if (objc != 2) {
    Tcl_WrongNumArgs(interp, 1, objv, "mediapath");
    return TCL_ERROR;
  }
  rc = TCL_OK;
  if (vlcData->inst == NULL || vlcData->mp == NULL) {
    rc = TCL_ERROR;
  } else {
    fn = Tcl_GetString(objv[1]);
    if (stat (fn, &statinfo) != 0) {
      rc = TCL_ERROR;
      return rc;
    }
    m = libvlc_media_new_path (vlcData->inst, fn);
    libvlc_media_player_set_rate (vlcData->mp, 1.0);
    em = libvlc_media_event_manager (m);
    libvlc_event_attach (em, libvlc_MediaStateChanged,
        &vlcEventHandler, vlcData);
    libvlc_media_player_set_media (vlcData->mp, m);
    /* on mac os x, the device has to be set after the media is set */
    if (vlcData->device != NULL) {
      libvlc_audio_output_device_set (vlcData->mp, NULL, vlcData->device);
    }
    libvlc_media_release (m);
  }
  return rc;
}
int
vlcPauseCmd (
  ClientData cd,
  Tcl_Interp* interp,
  int objc,
  Tcl_Obj * const objv[]
  )
{
  int   rc;
  vlcData_t     *vlcData = (vlcData_t *) cd;
  if (objc != 1) {
    Tcl_WrongNumArgs(interp, 1, objv, "");
    return TCL_ERROR;
  }
  rc = TCL_OK;
  if (vlcData->inst == NULL || vlcData->mp == NULL) {
    rc = TCL_ERROR;
  } else {
    if (vlcData->state == libvlc_Opening ||
        vlcData->state == libvlc_Buffering) {
      ;
    } else if (vlcData->state == libvlc_Playing) {
      libvlc_media_player_set_pause (vlcData->mp, 1);
    } else if (vlcData->state == libvlc_Paused) {
      libvlc_media_player_set_pause (vlcData->mp, 0);
    }
  }
  return rc;
}
int
vlcPlayCmd (
  ClientData cd,
  Tcl_Interp* interp,
  int objc,
  Tcl_Obj * const objv[]
  )
{
  int   rc;
  vlcData_t     *vlcData = (vlcData_t *) cd;
  if (objc != 1) {
    Tcl_WrongNumArgs(interp, 1, objv, "");
    return TCL_ERROR;
  }
  rc = TCL_OK;
  if (vlcData->inst == NULL || vlcData->mp == NULL) {
    rc = TCL_ERROR;
  } else {
    libvlc_media_player_play (vlcData->mp);
  }
  return rc;
}
int
vlcRateCmd (
  ClientData cd,
  Tcl_Interp* interp,
  int objc,
  Tcl_Obj * const objv[]
  )
{
  int       rc;
  vlcData_t *vlcData = (vlcData_t *) cd;
  float     rate;
  double    d;
  if (objc != 1 && objc != 2) {
    Tcl_WrongNumArgs(interp, 1, objv, "?rate?");
    return TCL_ERROR;
  }
  rc = TCL_OK;
  if (vlcData->inst == NULL || vlcData->mp == NULL) {
    rc = TCL_ERROR;
  } else {
    if (objc == 2 && vlcData->state == libvlc_Playing) {
      rc = Tcl_GetDoubleFromObj (interp, objv[1], &d);
      if (rc == TCL_OK) {
        rate = (float) d;
        libvlc_media_player_set_rate (vlcData->mp, rate);
      }
    }
    rate = libvlc_media_player_get_rate (vlcData->mp);
    Tcl_SetObjResult (interp, Tcl_NewDoubleObj ((double) rate));
  }
  return rc;
}
int
vlcSeekCmd (
  ClientData cd,
  Tcl_Interp* interp,
  int objc,
  Tcl_Obj * const objv[]
  )
{
  int               rc;
  libvlc_time_t     tm;
  double            dtm;
  vlcData_t         *vlcData = (vlcData_t *) cd;
  float             pos;
  double            d;
  if (objc != 1 && objc != 2) {
    Tcl_WrongNumArgs(interp, 1, objv, "?position?");
    return TCL_ERROR;
  }
  rc = TCL_OK;
  if (vlcData->inst == NULL || vlcData->mp == NULL) {
    rc = TCL_ERROR;
  } else {
    if (objc == 2 && vlcData->state == libvlc_Playing) {
      tm = libvlc_media_player_get_length (vlcData->mp);
      dtm = (double) tm / 1000.0;
      rc = Tcl_GetDoubleFromObj (interp, objv[1], &d);
      d = d / dtm;
      if (rc == TCL_OK) {
        pos = (float) d;
        libvlc_media_player_set_position (vlcData->mp, pos);
      }
    }
    pos = libvlc_media_player_get_position (vlcData->mp);
    Tcl_SetObjResult (interp, Tcl_NewDoubleObj ((double) pos));
  }
  return rc;
}
int
vlcStateCmd (
  ClientData cd,
  Tcl_Interp* interp,
  int objc,
  Tcl_Obj * const objv[]
  )
{
  int               rc;
  libvlc_state_t    plstate;
  vlcData_t         *vlcData = (vlcData_t *) cd;
  rc = TCL_OK;
  if (vlcData->inst == NULL || vlcData->mp == NULL) {
    rc = TCL_ERROR;
  } else {
    plstate = vlcData->state;
    Tcl_SetObjResult (interp, Tcl_NewStringObj (stateToStr(plstate), -1));
  }
  return rc;
}
int
vlcStopCmd (
  ClientData cd,
  Tcl_Interp* interp,
  int objc,
  Tcl_Obj * const objv[]
  )
{
  int   rc;
  vlcData_t     *vlcData = (vlcData_t *) cd;
  rc = TCL_OK;
  if (vlcData->inst == NULL || vlcData->mp == NULL) {
    rc = TCL_ERROR;
  } else {
    libvlc_media_player_stop (vlcData->mp);
  }
  return rc;
}
int
vlcHaveAudioDevListCmd (
  ClientData cd,
  Tcl_Interp* interp,
  int objc,
  Tcl_Obj * const objv[]
  )
{
  int       rc;
  rc = 0;
#if LIBVLC_VERSION_INT >= LIBVLC_VERSION(2,2,0,0)
  rc = 1;
#endif
  Tcl_SetObjResult (interp, Tcl_NewBooleanObj (rc));
  return TCL_OK;
}
#if LIBVLC_VERSION_INT >= LIBVLC_VERSION(2,2,0,0)
int
vlcAudioDevSetCmd (
  ClientData cd,
  Tcl_Interp* interp,
  int objc,
  Tcl_Obj * const objv[]
  )
{
  vlcData_t     *vlcData = (vlcData_t *) cd;
  int           rc;
  char          *dev;
  if (objc != 2) {
    Tcl_WrongNumArgs(interp, 1, objv, "deviceid");
    return TCL_ERROR;
  }
  rc = TCL_OK;
  if (vlcData->inst == NULL || vlcData->mp == NULL) {
    rc = TCL_ERROR;
  } else {
    dev = Tcl_GetString(objv[1]);
    if (vlcData->device != NULL) {
      free ((void *) vlcData->device);
    }
    vlcData->device = NULL;
    if (strlen (dev) > 0) {
      vlcData->device = strdup (dev);
    }
  }
  return rc;
}
int
vlcAudioDevListCmd (
  ClientData cd,
  Tcl_Interp* interp,
  int objc,
  Tcl_Obj * const objv[]
  )
{
  vlcData_t                     *vlcData = (vlcData_t *) cd;
  Tcl_Obj                       *lobj;
  Tcl_Obj                       *sobj;
  libvlc_audio_output_device_t  *adevlist;
  libvlc_audio_output_device_t  *adevlistptr;
  if (vlcData->inst == NULL || vlcData->mp == NULL ||
      strcmp (vlcData->version, "2.2.0") < 0) {
    return TCL_ERROR;
  }
  lobj = Tcl_NewListObj (0, NULL);
  adevlist = libvlc_audio_output_device_enum (vlcData->mp);
  adevlistptr = adevlist;
  while (adevlistptr != (libvlc_audio_output_device_t *) NULL) {
    sobj = Tcl_NewStringObj (adevlistptr->psz_device, -1);
    Tcl_ListObjAppendElement (interp, lobj, sobj);
    sobj = Tcl_NewStringObj (adevlistptr->psz_description, -1);
    Tcl_ListObjAppendElement (interp, lobj, sobj);
    adevlistptr = adevlistptr->p_next;
  }
  libvlc_audio_output_device_list_release (adevlist);
  Tcl_SetObjResult (interp, lobj);
  return TCL_OK;
}
#endif
int
vlcVersionCmd (
  ClientData cd,
  Tcl_Interp* interp,
  int objc,
  Tcl_Obj * const objv[]
  )
{
  vlcData_t     *vlcData = (vlcData_t *) cd;
  Tcl_SetObjResult (interp, Tcl_NewStringObj (vlcData->version, -1));
  return TCL_OK;
}
void
vlcClose (
  vlcData_t     *vlcData
  )
{
  int   i;
  if (vlcData->mp != NULL) {
    libvlc_media_player_stop (vlcData->mp);
    libvlc_media_player_release (vlcData->mp);
    vlcData->mp = NULL;
  }
  if (vlcData->inst != NULL) {
    libvlc_release (vlcData->inst);
    vlcData->inst = NULL;
  }
  if (vlcData->argv != NULL) {
    for (i = 0; i < vlcData->argc; ++i) {
      ckfree (vlcData->argv[i]);
    }
    ckfree (vlcData->argv);
    vlcData->argv = NULL;
  }
  if (vlcData->device != NULL) {
    free ((void *) vlcData->device);
    vlcData->device = NULL;
  }
  vlcData->state = libvlc_NothingSpecial;
}
void
vlcExitHandler (
  void *cd
  )
{
  vlcData_t     *vlcData = (vlcData_t *) cd;
  vlcClose (vlcData);
  ckfree (cd);
}
int
vlcReleaseCmd (
  ClientData cd,
  Tcl_Interp* interp,
  int objc,
  Tcl_Obj * const objv[]
  )
{
  vlcData_t     *vlcData = (vlcData_t *) cd;
  vlcClose (vlcData);
  return TCL_OK;
}
int
vlcInitCmd (
  ClientData cd,
  Tcl_Interp* interp,
  int objc,
  Tcl_Obj * const objv[]
  )
{
  char          *tptr;
  char          *nptr;
  int           rc;
  int           i;
  int           len;
  vlcData_t     *vlcData = (vlcData_t *) cd;
  vlcData->argv = (const char **) ckalloc (sizeof(const char *) * (size_t) (objc + 1));
  for (i = 0; i < objc; ++i) {
    tptr = Tcl_GetStringFromObj (objv[i], &len);
    nptr = (char *) ckalloc (len+1);
    strcpy (nptr, tptr);
    vlcData->argv[i] = nptr;
  }
  vlcData->argc = objc;
  vlcData->argv[objc] = NULL;
  rc = TCL_ERROR;
  strcpy (vlcData->version, libvlc_get_version ());
  tptr = strchr (vlcData->version, ' ');
  if (tptr != NULL) {
    *tptr = '\0';
  }
  if (vlcData->inst == NULL) {
    vlcData->inst = libvlc_new (objc, vlcData->argv);
  }
  if (vlcData->inst != NULL && vlcData->mp == NULL) {
    vlcData->mp = libvlc_media_player_new (vlcData->inst);
  }
  if (vlcData->inst != NULL && vlcData->mp != NULL) {
    rc = TCL_OK;
  }
  return rc;
}
static const EnsembleData vlcCmdMap[] = {
#if LIBVLC_VERSION_INT >= LIBVLC_VERSION(2,2,0,0)
  { "audiodevlist", vlcAudioDevListCmd },
  { "audiodevset",  vlcAudioDevSetCmd },
#endif
  { "close",        vlcReleaseCmd },
  { "duration",     vlcDurationCmd },
  { "gettime",      vlcGetTimeCmd },
  { "init",         vlcInitCmd },
  { "haveaudiodevlist", vlcHaveAudioDevListCmd },
  { "isplay",       vlcIsPlayCmd },
  { "media",        vlcMediaCmd },
  { "pause",        vlcPauseCmd },
  { "play",         vlcPlayCmd },
  { "rate",         vlcRateCmd },
  { "seek",         vlcSeekCmd },
  { "state",        vlcStateCmd },
  { "stop",         vlcStopCmd },
  { "version",      vlcVersionCmd },
  { NULL, NULL }
};
int
Tclvlc_Init (Tcl_Interp *interp)
{
  Tcl_Namespace *nsPtr = NULL;
  Tcl_Command   ensemble = NULL;
  Tcl_Obj       *dictObj = NULL;
  Tcl_DString   ds;
  vlcData_t     *vlcData;
  int           i;
  int           rc;
  const char    *nsName = "::tcl::tclvlc";
  const char    *cmdName = nsName + 5;
#ifdef USE_TCL_STUBS
  if (!Tcl_InitStubs (interp,"8.3",0)) {
    return TCL_ERROR;
  }
#else
  if (!Tcl_PkgRequire (interp,"Tcl","8.3",0)) {
    return TCL_ERROR;
  }
#endif
#ifdef COMP_WINDOWS
  /* https://forum.videolan.org/viewtopic.php?t=135009 */
  {
    OSVERSIONINFO osvi;
    memset (&osvi, 0, sizeof(OSVERSIONINFO));
    osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
    GetVersionEx (&osvi);
    if ((osvi.dwMajorVersion < 6) ||
        (osvi.dwMajorVersion == 6) && (osvi.dwMinorVersion == 0)) {
      SetErrorMode(SEM_FAILCRITICALERRORS);
    }
  }
#endif
  vlcData = (vlcData_t *) ckalloc (sizeof (vlcData_t));
  vlcData->interp = interp;
  vlcData->inst = NULL;
  vlcData->mp = NULL;
  vlcData->argv = NULL;
  vlcData->state = libvlc_NothingSpecial;
  vlcData->device = NULL;
  Tcl_CreateExitHandler (vlcExitHandler, vlcData);
  nsPtr = Tcl_FindNamespace(interp, nsName, NULL, 0);
  if (nsPtr == NULL) {
    nsPtr = Tcl_CreateNamespace(interp, nsName, NULL, 0);
    if (nsPtr == NULL) {
      Tcl_Panic ("failed to create namespace: %s\n", nsName);
    }
  }
  ensemble = Tcl_CreateEnsemble(interp, cmdName, nsPtr, TCL_ENSEMBLE_PREFIX);
  if (ensemble == NULL) {
    Tcl_Panic ("failed to create ensemble: %s\n", cmdName);
  }
  Tcl_DStringInit (&ds);
  Tcl_DStringAppend (&ds, nsName, -1);
  dictObj = Tcl_NewObj();
  for (i = 0; vlcCmdMap[i].name != NULL; ++i) {
    Tcl_Obj *nameObj;
    Tcl_Obj *fqdnObj;
    nameObj = Tcl_NewStringObj (vlcCmdMap[i].name, -1);
    fqdnObj = Tcl_NewStringObj (Tcl_DStringValue(&ds), Tcl_DStringLength(&ds));
    Tcl_AppendStringsToObj (fqdnObj, "::", vlcCmdMap[i].name, NULL);
    Tcl_DictObjPut (NULL, dictObj, nameObj, fqdnObj);
    if (vlcCmdMap[i].proc) {
      Tcl_CreateObjCommand (interp, Tcl_GetString (fqdnObj),
           vlcCmdMap[i].proc, (ClientData) vlcData, NULL);
    }
  }
  if (ensemble) {
    Tcl_SetEnsembleMappingDict (interp, ensemble, dictObj);
  }
  Tcl_DStringFree(&ds);
  Tcl_PkgProvide (interp, cmdName+2, "0.1");
  return TCL_OK;
}
mkvlc.sh#!/bin/sh
tv=8.6
slext=.so
os=`uname -s`
arch=`uname -m`
bits=64
case $arch in
  i?86*)
    bits=32
    ;;
esac
inc=-I/usr/include/tcl${tv}
lib=
lv=$tv
if [ $os = "Darwin" ]; then
  slext=.dylib
  # for MacPorts tcl
  inc=-I/opt/local/include
  lib=-L/opt/local/lib
  inc+=" -I/Applications/VLC.app/Contents/MacOS/include"
  lib+=" -L/Applications/VLC.app/Contents/MacOS/lib"
fi
${CC:-cc} -O -shared -fPIC -o tclvlc${slext} $inc -DUSE_TCL_STUBS tclvlc.c $lib -ltclstub${lv} -lvlc
test -d $os || mkdir $os
if [ -f tclvlc${slext} ]; then
  echo "tclvlc success"
  mv -f tclvlc${slext} $os/tclvlc${bits}${slext}
fi
if [ $os = "Darwin" ]; then
  install_name_tool -change "@loader_path/lib/libvlc.5.dylib" "/Applications/VLC.app/Contents/MacOS/lib/libvlc.5.dylib" Darwin/*
fiwinmkvlc.sh#!/bin/bash
test -d Windows || mkdir Windows
case $MSYSTEM in
  *32)
    gcc -m32 -shared -static-libgcc -DCOMP_WINDOWS -o Windows/tclvlc32.dll \
        -I'/home/bll/vlc/vlc-2.2.4/include' \
        '-Wl,-rpath=/c/Program Files (x86)/VideoLAN/vlc' \
        -I$HOME/local-32/include -DUSE_TCL_STUBS tclvlc.c \
        -L$HOME/local-32/lib -ltclstub86 \
        -L'/c/Program Files (x86)/VideoLAN/vlc' -lvlc
    ;;
  *64)
    gcc -m64 -shared -static-libgcc -DCOMP_WINDOWS -o Windows/tclvlc64.dll \
        -I'/home/bll/vlc/vlc-2.2.4/include' \
        '-Wl,-rpath=/c/Program Files/VideoLAN/vlc' \
        -I$HOME/local-64/include -DUSE_TCL_STUBS tclvlc.c \
        -L$HOME/local-64/lib -ltclstub86 \
        -L'/c/Program Files/VideoLAN/vlc' -lvlc
    ;;
esac
