Carny_Priest

[UPDATE] Visual Pinball mini DMD/B2S screen for apron now with Instruction Card splash viewer

Recommended Posts

Hi, following up on this thread,

http://www.gameex.info/forums/topic/15882-visual-pinball-mini-dmd-screen-3-b2s-for-apron/

, I've added a new feature: a flash viewer that displays instruction and score cards associated with the table.

Requires ffmpeg and Adobe flashplayer

http://ffmpeg.zeranoe.com/builds/

https://get.adobe.com/flashplayer/

As a demo, here's a crappy phone video

https://dl.dropboxusercontent.com/u/45430846/afm.MOV

This is a kludgy solution requiring multiple scripts, but it supports swf and png formats (same as PBX) including animated instruction cards.

The downside is that all of the instruction card media that is available on the FTP or at VPForums are oriented for viewing on the BG screen which is usually configured for landscape. VP Cabinet tables are rotated in the program to be viewed in portrait but the screen back buffer is still oriented in landscape. This means that the media that I want to display on the apron must also be rotated. Sadly, I found a high percentage of the media could not be directly converted and rotated. So, I could not just simply convert files from the PBX Instruction Cards folder on-the-fly. I had to do screen caps and rotate the resulting images and store them in a folder prior to running the script. I have a batch script that will be included in this post. The output will need to be stored in a separate folder, and secondary score or price cards should be stored in a new folder named "Score Cards".

By default I scale the images using the aspect ratio for Stern and latter day Williams/Bally as reference here:

http://www.pinballrebel.com/pinball/cards/Card_sizes.htm

It is possible to properly scale and display all the rotated images by manufacturer, but that might be a future update.

Similar to the previous script, the correct media is displayed based on matching file names to entries in VisualPinball.xml. Matching images for the table are presented in a loop until VP is closed.

I've attached a generic Instructions card and a Score card which I display at the beginning of the loop. In my collection, I have an instruction card for every table installed. So, between the generic card and the card associated with the table, I always expect that there will be multiple images to loop. Instruction cards are displayed on the left side of the apron.

I don't have score or price cards for most tables, so the generic price card would be all that there is to loop. Rather than loop the same image, I have some code to check the number of files in the loop and if it is just 1 image then the program will not loop. Score cards are displayed on the right side of the apron.

The script assumes the playfield aspect ratio is 1920x1080. If your screen is something different then the script must be tweaked to properly scale the images. I have not tested against all tables, but hopefully the images are not covering any play areas. The X,Y coordinates for displaying the images are user configurable. Consider where the instruction and score card images will be displayed, and leave room for the mini-DMD which will be automatically placed below the instruction and score card.

The scaling of images is automated based on the the width of DMD defined in the VPM registry or in screenres.txt along with the Filter setting in the User Input section at the top of the script, so that the card images will be lined up directly over the mini-DMD. Card_Width is a failsafe setting in case there happens to be a table installed that does not use VPM and B2S is set to False in the xml. I haven't tested it. I'm not sure I have that situation in my install.

The amount of time each images will be displayed before looping to the next is also user configurable at the top of the script.

This is definitely for advanced users. This requires a custom xml as explained in the thread for the previous script. I may also be using different VP executables, so the script may require tweaking to fit another install.

#NoEnv
#SingleInstance force
#Include, %A_ScriptDir%\xpath.ahk
SetTitleMatchMode, 2
DetectHiddenWindows, On
; USER INPUTS
PinballX_Path=c:\PinballX
FFMPEG_Path=c:\ffmpeg\bin
CARD_Path=%A_ScriptDir%\Instruction Cards
SCORE_Path=%A_ScriptDir%\Score Cards
Nudge991=300
NudgePM=75
Filter="scale=iw*(1/3):-1,transpose=3"
Mirror1PosX=1675
Mirror1PosY=767
Mirror2PosX=1675
Mirror2PosY=127
CardWidth=768
DisplayCardTime=15000
; roms for lowest latency but possibly with artifacts
arrayf:=["mb_106b", "tz_92", "taf_l7", "frankst"]
; ***********************************************
Table=%1% ;Table filename
SplitPath, Table,,,, XTable,
; Read description tag from xml - Thanks horseyhorsey - http://www.gameex.info/forums/topic/14632-multiple-exe-for-vp/#entry129111
databaseFile=%PinballX_Path%\Databases\Visual Pinball\Visual Pinball.xml
xpath_load(dbXML, databaseFile ) ; need to read the existing xml otherwise xpath deletes all existing nodes
exe:= XPath(dbXML, "/menu/game[@name= . XTable . ]/exe/text()")
rom:= XPath(dbXML, "/menu/game[@name= . XTable . ]/rom/text()")
B2S:= XPath(dbXML, "/menu/game[@name= . XTable . ]/B2S/text()")
description:= XPath(dbXML, "/menu/game[@name= . XTable . ]/description/text()")
If exe=vpinball-9.9.1-plunger-mods
{
RegWrite, REG_DWORD,HKCU, Software\Visual Pinball\DX9\Player, PBWAccelGainY, %Nudge991%
RegWrite, REG_DWORD,HKCU, Software\Visual Pinball\DX9\Player, PBWAccelGainX, %Nudge991%
}
If exe=VP_physmod2
{
RegWrite, REG_DWORD, HKCU, Software\Visual Pinball\DX9\Player, PBWAccelGainY, %NudgePM%
RegWrite, REG_DWORD, HKCU, Software\Visual Pinball\DX9\Player, PBWAccelGainX, %NudgePM%
}
IniRead, TablePath, %PinballX_Path%\Config\PinballX.ini, VisualPinball, TablePath
StringReplace, XTablePath, TablePath, \Tables
RegRead, XDMD_width, HKCU, Software\Freeware\Visual PinMame\%rom%, dmd_width
RegRead, XDMD_height, HKCU, Software\Freeware\Visual PinMame\%rom%, dmd_height
RegRead, DMD_X, HKCU, Software\Freeware\Visual PinMame\%rom%, dmd_pos_x
RegRead, XDMD_Y, HKCU, Software\Freeware\Visual PinMame\%rom%, dmd_pos_y
DMD_width:=XDMD_width-3
DMD_height:=XDMD_height+3
DMD_Y:=XDMD_Y-2
; Hardcode
If rom=simpprty
{
DMD_width:=768
DMD_height:=300
DMD_X:=3840
DMD_Y:=123
}
If rom=ripleys
{
DMD_width:=768
DMD_height:=300
DMD_X:=3840
DMD_Y:=123
}
If rom=wpt_140a
{
DMD_width:=768
DMD_height:=300
DMD_X:=3840
DMD_Y:=123
}
If rom=taf_l7
{
DMD_width:=768
DMD_height:=244
DMD_X:=3840
DMD_Y:=198
}
If B2S=True
{
FileReadLine, BG_width, %TablePath%\ScreenRes.txt, 3
FileReadLine, DMD_width, %TablePath%\ScreenRes.txt, 8
FileReadLine, XDMD_height, %TablePath%\ScreenRes.txt, 9
FileReadLine, XDMD_X, %TablePath%\ScreenRes.txt, 10
FileReadLine, XDMD_Y, %TablePath%\ScreenRes.txt, 11
DMD_height:=XDMD_height+6
DMD_X:=BG_width+XDMD_X ;Reference to Top Left of Playfield screen
DMD_Y:=XDMD_Y-2
}
; Have the information we need to run Card splash screens
Run, %A_ScriptDir%\DisplayCard1.exe "%CARD_Path%" "%Filter%" %Mirror1PosX% %Mirror1PosY% %Mirror2PosX% %Mirror2PosY% "%description%" "%XTable%" %DMD_width% %CardWidth% %DisplayCardTime%
Run, %A_ScriptDir%\DisplayCard2.exe "%SCORE_Path%" "%Filter%" %Mirror1PosX% %Mirror1PosY% %Mirror2PosX% %Mirror2PosY% "%description%" "%XTable%" %DMD_width% %CardWidth% %DisplayCardTime%
StringGetPos, pos, Filter, )
Scale:=SubStr(Filter, 12, pos-11)
StringGetPos, pos1, Scale, /
If ErrorLevel=0
{
Num:=SubStr(Scale, 1, pos1)
Denom:=SubStr(Scale, pos1+2)
Num1:=Num
Denom1:=Denom
Scale:=Num1/Denom1
}
; DMD/B2S border
BOX1_width:=Floor(DMD_width*Scale+23*2)
BOX1_height:=Floor(DMD_height*Scale+4*2)
BOX2_width:=BOX1_width-21
BOX2_height:=BOX1_height-4
; Card border - want to retain the same width and scale the height to preserve the aspect ratio
If DMD_width>0
CARD1_height:=Floor(DMD_width*Scale*382/700+4*2)
Else
CARD1_height:=Floor(CardWidth*Scale*382/700+4*2)
; Position DMD/B2S adjacent to Card border
Mirror3PosX:=Mirror1PosX+Card1_height
Mirror4PosX:=Mirror2PosX+Card1_height
; ***********************************************
; Disable Aero if Enabled
RegRead, DWMComp, HKCU, Software\Microsoft\Windows\DWM, Composition
Aero=%DWMComp%
If Aero=1
Run, sc stop uxsms
;Run, dc2.exe -configure="%A_ScriptDir%\dc2config\VPMSettings.xml"
; Mirror and flip DMD for apron
If DMD_width>0
{
Loop % arrayf.MaxIndex()
{
l_table = % arrayf[A_Index]
if( "X" l_table = "X" rom )
{
streamx264=1
break
}
}
If streamx264=1
{
Run, %FFMPEG_Path%\ffmpeg -f gdigrab -framerate 150 -offset_x %DMD_X% -offset_y %DMD_Y% -video_size %DMD_width%x%DMD_height% -i desktop -vf %Filter% -c:v libx264 -preset ultrafast -tune zerolatency -qp 0 -f mpegts -threads 8 udp://localhost:1234,, Hide
Run, %FFMPEG_Path%\ffmpeg -f gdigrab -framerate 150 -offset_x %DMD_X% -offset_y %DMD_Y% -video_size %DMD_width%x%DMD_height% -i desktop -vf %Filter% -c:v libx264 -preset ultrafast -tune zerolatency -qp 0 -f mpegts -threads 8 udp://localhost:5678,, Hide
}
Else
{
Run, %FFMPEG_Path%\ffmpeg -f gdigrab -framerate 150 -offset_x %DMD_X% -offset_y %DMD_Y% -video_size %DMD_width%x%DMD_height% -i desktop -vf %Filter% -c:v mpeg4 -qscale:v 1 -f mpegts -threads 8 udp://localhost:1234,, Hide
Run, %FFMPEG_Path%\ffmpeg -f gdigrab -framerate 150 -offset_x %DMD_X% -offset_y %DMD_Y% -video_size %DMD_width%x%DMD_height% -i desktop -vf %Filter% -c:v mpeg4 -qscale:v 1 -f mpegts -threads 8 udp://localhost:5678,, Hide
}
Sleep, 1000
Run, %FFMPEG_Path%\ffplay -an -sn -i -fflags nobuffer udp://localhost:1234?listen,, Hide
Run, %FFMPEG_Path%\ffplay -an -sn -i -fflags nobuffer udp://localhost:5678?listen,, Hide
Loop, 2
{
checkwindowagain1:
IfWinExist, udp://localhost:1234?listen
WinMove, udp://localhost:1234?listen,, %Mirror3PosX%, %Mirror1PosY%
Else
Goto, checkwindowagain1
}
Loop, 2
{
checkwindowagain2:
IfWinExist, udp://localhost:5678?listen
WinMove, udp://localhost:5678?listen,, %Mirror4PosX%, %Mirror2PosY%
Else
Goto, checkwindowagain2
}
}
Run, %XTablePath%\%exe%.exe -play "%TablePath%\%Table%"
WinWaitActive, ahk_class VPPlayer
Sleep, 1000
WinSet, AlwaysOnTop,, udp://localhost:1234?listen
WinSet, AlwaysOnTop,, udp://localhost:5678?listen
If DMD_width>0
{
; draw a black border to hide the window borders for the mirror
Gui, 1: +LastFound +AlwaysOnTop -Caption +ToolWindow
Gui, 1: Color, 000000
WinSet, Region, 0-0 %BOX1_height%-0 %BOX1_height%-%BOX1_width% 0-%BOX1_width% 0-0 4-24 %BOX2_height%-24 %BOX2_height%-%BOX2_width% 4-%BOX2_width% 4-24
Gui, 1: Show, W%BOX1_height% H%BOX1_width% X%Mirror3PosX% Y%Mirror1PosY% NoActivate
Gui, 2: +LastFound +AlwaysOnTop -Caption +ToolWindow
Gui, 2: Color, 000000
WinSet, Region, 0-0 %BOX1_height%-0 %BOX1_height%-%BOX1_width% 0-%BOX1_width% 0-0 4-24 %BOX2_height%-24 %BOX2_height%-%BOX2_width% 4-%BOX2_width% 4-24
Gui, 2: Show, W%BOX1_height% H%BOX1_width% X%Mirror4PosX% Y%Mirror2PosY% NoActivate
}
; ***********************************************
checkVPPlayer:
IfWinNotExist, Visual Pinball
{
Run, taskkill /IM ffplay.exe
Run, taskkill /IM ffmpeg.exe /F
If DMD_width>0
{
Gui, 1: Destroy
Gui, 2: Destroy
}
Process, Close, DisplayCard1.exe
Process, Close, DisplayCard2.exe
;Run, dc2.exe -configure="%A_ScriptDir%\dc2config\4KSettings.xml"
; Enable Aero if Enabled at the start of script execution
If Aero=1
Run, sc start uxsms
}
else
{
Sleep, 1000
Goto, checkVPPlayer
}
ExitApp
; ***********************************************
; CloseVP from FPLaunch
CloseVP:
;toLog("CloseVP Called")
;Hotkey, %exitScriptKey%, Off
GoSub rosveClose
;GoSub bigbossClose ; change loops completely, put all data in xml !!! too slow
;Visual Pinball must be closed this way instead of killing process
;or it wil not save your last game information.i.e score/credtis
;DetectHiddenWindows on ;Or next line will not work
;Loop, 4
;Gui, %A_Index%: Destroy
;This line fixes where the VP Window flashes real quick
;when closing the window for a cleaner exit
; win hide removed cause vp should be minimized, not hidden
;WinHide, ahk_class VPinball
;WinShow, ahk_class VPinball
WinMinimize, ahk_class VPinball
WinClose, ahk_class VPinball
WinWaitClose ahk_class VPinball
; bigbossclose won't get executed cause main thread will exit
;GoSub bigbossClose
; there's no need for exitscript cause main thread will do it
;Goto ExitScript
return
rosveClose:
;toLog("rosveClose Called")
; rosve
; --------------------------------------------------------------
; --- Close the animated backglass -----------------------
; --------------------------------------------------------------
; b2s close bug fix
StringRight, ending, XTable, 3
if (ending = "B2S")
{
WinKill, Form1
}
return
; ***********************************************
#IfWinActive ahk_class VPPlayer
; Interlock Switch
Joy7::
Send {End}
SetTimer, WaitForJoy7, 10
Return
WaitForJoy7:
if GetKeyState("Joy7")
return
Send {End}
SetTimer, WaitForJoy7, off
Return
; Exit Table
Joy8::
Gosub CloseVP
Return
#IfWinActive

This is a secondary script for looping instruction cards. It must be compiled and named DisplayCard1.exe

#NoEnv
#SingleInstance force
SetTitleMatchMode, 2
DetectHiddenWindows, On
CARD_Path=%1%
Filter=%2%
Mirror1PosX=%3%
Mirror1PosY=%4%
Mirror2PosX=%5%
Mirror2PosY=%6%
description=%7%
XTable=%8%
DMD_width=%9%
CardWidth=%10%
DisplayCardTime=%11%
IfExist, %CARD_Path%\%description%*
cardmatch:=1
IfExist, %CARD_Path%\%XTable%*
cardmatch:=1
If cardmatch=1
{
StringGetPos, pos, Filter, )
Scale:=SubStr(Filter, 11, pos-10)
StringGetPos, pos1, Scale, /
If ErrorLevel=0
{
Num:=SubStr(Scale, 1, pos1)
Denom:=SubStr(Scale, pos1+2)
Num1:=Num
Denom1:=Denom
Scale:=Num1/Denom1
}
; Card border - want to retain the same width and scale the height to preserve the aspect ratio
If !(DMD_width>0)
DMD_width:=CardWidth
CARD1_width:=Floor(DMD_width*Scale+23*2)
CARD1_height:=Floor(DMD_width*Scale*382/700+4*2)
CARD2_width:=CARD1_width-23
CARD2_height:=CARD1_height-4
GUI4W:=CARD2_width-23
GUI4H:=CARD2_height-4
GUI4X:=Mirror1PosX+4
GUI4Y:=Mirror1PosY+23
; Show instruction cards
Gui, 3: +LastFound -Caption +AlwaysOnTop -SysMenu
GUI3_ID:=WinExist()
Gui, 3: Color, 000000
WinSet, Region, 0-0 %CARD1_height%-0 %CARD1_height%-%CARD1_width% 0-%CARD1_width% 0-0 4-24 %CARD2_height%-24 %CARD2_height%-%CARD2_width% 4-%CARD2_width% 4-24
CardList1=%CARD_Path%\Instructions.png`n
Loop, Files, %CARD_Path%\%description%*
CardList1=%CardList1%%A_LoopFileFullPath%`n
Loop, Files, %CARD_Path%\%XTable%*
CardList1=%CardList1%%A_LoopFileFullPath%`n
WinWaitActive, ahk_class VPPlayer
Loop
{
Loop, Parse, CardList1, `n
{
If A_LoopField= ; Ignore the blank item.
Continue
getSwfFromDisk=%A_LoopField%
GoSub DisplayCard
}
}
}
ExitApp
; ***********************************************
DisplayCard:
Gui, 4: +LastFound -Caption +AlwaysOnTop -SysMenu
GUI4_ID:=WinExist()
Gui, 4: Color, 000000
Gui, 3: Show, W%CARD1_height% H%CARD1_width% X%Mirror1PosX% Y%Mirror1PosY% NoActivate Hide
Gui, 4: Show, W%GUI4H% H%GUI4W% X%GUI4X% Y%GUI4Y% NoActivate Hide
Gui, 4: Margin, 0 ,0
DllCall("AnimateWindow","UInt",GUI3_ID,"Int",1,"UInt","0xa0000")
DllCall("AnimateWindow","UInt",GUI4_ID,"Int",1000,"UInt","0xa0000")
Gui, 4: Add, ActiveX, W%GUI4H% H%GUI4W% vpwb, ShockwaveFlash.ShockwaveFlash
pwb.Movie := "file:///" . getSwfFromDisk
pwb.Scale := "exactfit"
Sleep, %DisplayCardTime%
Control, Hide,, MacromediaFlashPlayerActiveX1, ahk_id %GUI2_ID%
DllCall("AnimateWindow","UInt",GUI3_ID,"Int",750,"UInt","0x90000")
DllCall("AnimateWindow","UInt",GUI4_ID,"Int",750,"UInt","0x90000")
Gui, 4: Destroy
Return

This is a secondary script for looping score cards. It must be compiled and named DisplayCard2.exe

#NoEnv
#SingleInstance force
SetTitleMatchMode, 2
DetectHiddenWindows, On
SCORE_Path=%1%
Filter=%2%
Mirror1PosX=%3%
Mirror1PosY=%4%
Mirror2PosX=%5%
Mirror2PosY=%6%
description=%7%
XTable=%8%
DMD_width=%9%
CardWidth=%10%
DisplayCardTime=%11%
IfExist, %SCORE_Path%\%description%*
cardmatch:=1
IfExist, %SCORE_Path%\%XTable%*
cardmatch:=1
IfExist, %SCORE_Path%\Score.png
cardmatch:=1
If cardmatch=1
{
StringGetPos, pos, Filter, )
Scale:=SubStr(Filter, 11, pos-10)
StringGetPos, pos1, Scale, /
If ErrorLevel=0
{
Num:=SubStr(Scale, 1, pos1)
Denom:=SubStr(Scale, pos1+2)
Num1:=Num
Denom1:=Denom
Scale:=Num1/Denom1
}
; Card border - want to retain the same width and scale the height to preserve the aspect ratio
If !(DMD_width>0)
DMD_width:=CardWidth
CARD1_width:=Floor(DMD_width*Scale+23*2)
CARD1_height:=Floor(DMD_width*Scale*382/700+4*2)
CARD2_width:=CARD1_width-23
CARD2_height:=CARD1_height-4
GUI4W:=CARD2_width-23
GUI4H:=CARD2_height-4
GUI4X:=Mirror2PosX+4
GUI4Y:=Mirror2PosY+23
; Show instruction cards
Gui, 3: +LastFound -Caption +AlwaysOnTop -SysMenu
GUI3_ID:=WinExist()
Gui, 3: Color, 000000
WinSet, Region, 0-0 %CARD1_height%-0 %CARD1_height%-%CARD1_width% 0-%CARD1_width% 0-0 4-24 %CARD2_height%-24 %CARD2_height%-%CARD2_width% 4-%CARD2_width% 4-24
CardList1=%SCORE_Path%\Score.png`n
Loop, Files, %SCORE_Path%\%description%*
CardList1=%CardList1%%A_LoopFileFullPath%`n
Loop, Files, %SCORE_Path%\%XTable%*
CardList1=%CardList1%%A_LoopFileFullPath%`n
CntFile=0
Loop, Parse, CardList1, `n
{
If A_LoopField= ; Ignore the blank item.
Continue
CntFile:=CntFile+1
}
If CntFile=1
DisplayCardTime=2147483647
WinWaitActive, ahk_class VPPlayer
Loop
{
Loop, Parse, CardList1, `n
{
If A_LoopField= ; Ignore the blank item.
Continue
getSwfFromDisk=%A_LoopField%
GoSub DisplayCard
}
}
}
ExitApp
; ***********************************************
DisplayCard:
Gui, 4: +LastFound -Caption +AlwaysOnTop -SysMenu
GUI4_ID:=WinExist()
Gui, 4: Color, 000000
Gui, 3: Show, W%CARD1_height% H%CARD1_width% X%Mirror2PosX% Y%Mirror2PosY% NoActivate Hide
Gui, 4: Show, W%GUI4H% H%GUI4W% X%GUI4X% Y%GUI4Y% NoActivate Hide
Gui, 4: Margin, 0 ,0
DllCall("AnimateWindow","UInt",GUI3_ID,"Int",1,"UInt","0xa0000")
DllCall("AnimateWindow","UInt",GUI4_ID,"Int",1000,"UInt","0xa0000")
Gui, 4: Add, ActiveX, W%GUI4H% H%GUI4W% vpwb, ShockwaveFlash.ShockwaveFlash
pwb.Movie := "file:///" . getSwfFromDisk
pwb.Scale := "exactfit"
Sleep, %DisplayCardTime%
Control, Hide,, MacromediaFlashPlayerActiveX1, ahk_id %GUI2_ID%
DllCall("AnimateWindow","UInt",GUI3_ID,"Int",750,"UInt","0x90000")
DllCall("AnimateWindow","UInt",GUI4_ID,"Int",750,"UInt","0x90000")
Gui, 4: Destroy
Return

And here is the ffmpeg batch converter for rotating instruction cards for use in the viewer. You define where the source images are and the script will convert and save the images in %A_ScriptDir%\rotate.
arrayc is where you define any animated instruction cards. There are not many at all that I could find. STAT at VPF created the two I put into the array. When the script encounters these files during processing ffmpeg will make a 30 second capture, rotate, and save as an swf file.

#NoEnv
#SingleInstance force
; USER INPUTS
FFMPEG_Path=c:\ffmpeg\bin
CARD_Path=c:\PinballX\Media\Instruction Cards
; rotated animated instruction cards must be located in the same folder as this script/executable
arrayc:=["Attack from Mars (Bally 1995) 3.swf", "Torpedo Alley (Data East 1988).swf"]
CardList1= ; Initialize to be blank.
Loop, Files, %CARD_Path%\*.*
CardList1=%CardList1%%A_LoopFileFullPath%`n
Loop, Parse, CardList1, `n
{
If A_LoopField= ; Ignore the blank item.
Continue
SplitPath, A_LoopField, XCardfullname,, ext, XCard,
AniMatch=0
Loop % arrayc.MaxIndex()
{
l_card = % arrayc[A_Index]
If( "X" l_card = "X" XCardfullname )
{
Gui, 2: +LastFound -Caption +AlwaysOnTop -SysMenu
Gui, 2: Show, W700 H382 X0 Y0
Gui, 2: Margin, 0 ,0
Gui, 2: Add, ActiveX, W700 H382 vpwb, ShockwaveFlash.ShockwaveFlash
getSwfFromDisk=%CARD_Path%\%XCardfullname%
pwb.Movie := "file:///" . getSwfFromDisk
pwb.Scale := "exactfit"
Run, %FFMPEG_Path%\ffmpeg -t 30 -f gdigrab -framerate 30 -offset_x 0 -offset_y 0 -video_size 700x382 -i desktop -c:v libx264 -preset ultrafast -qp 0 -threads 8 "%A_ScriptDir%\IC.mkv",, Hide
Process, WaitClose, ffmpeg.exe, 40
Process, Close, %ErrorLevel%
Gui, 2: Destroy
Run, %FFMPEG_Path%\ffmpeg -y -i "%A_ScriptDir%\IC.mkv" -vf "transpose=2" "%A_ScriptDir%\rotate\%XCardfullname%"
Process, WaitClose, ffmpeg.exe, 60
Process, Close, %ErrorLevel%
FileDelete, %A_ScriptDir%\IC.mkv
AniMatch=1
Break
}
}
If AniMatch=0
{
Gui, 2: +LastFound -Caption +AlwaysOnTop -SysMenu
Gui, 2: Show, W700 H382 X0 Y0
Gui, 2: Margin, 0 ,0
Gui, 2: Add, ActiveX, W700 H382 vpwb, ShockwaveFlash.ShockwaveFlash
getSwfFromDisk=%A_LoopField%
pwb.Movie := "file:///" . getSwfFromDisk
pwb.Scale := "exactfit"
Run, %FFMPEG_Path%\ffmpeg -t 1 -f gdigrab -framerate 30 -offset_x 0 -offset_y 0 -video_size 700x382 -i desktop -c:v libx264 -preset ultrafast -qp 0 -threads 8 "%A_ScriptDir%\IC.mkv",, Hide
Process, WaitClose, ffmpeg.exe, 5
Process, Close, %ErrorLevel%
Gui, 2: Destroy
Run, %FFMPEG_Path%\ffmpeg -y -i "%A_ScriptDir%\IC.mkv" -ss 00:00:00.500 -vf "transpose=2" -vframes 1 "%A_ScriptDir%\rotate\%XCard%.png"
Process, WaitClose, ffmpeg.exe, 5
Process, Close, %ErrorLevel%
FileDelete, %A_ScriptDir%\IC.mkv
}
}
Gui, 2: Destroy
; Clean up
FileDelete, %A_ScriptDir%\IC.mkv
ExitApp

I originally pursued this solution hoping that the text on the instruction card images would be more legible than what VP tends to render. But I have a standard width table and a 39" screen. It's still a pretty small area, so I don't think it looks much better or clearer than what VP was already doing. But since any sort of video or image content can be displayed, it becomes another creative platform for people to do interesting mods. I hope to see more animated cards or custom backgrounds for the mirrored DMD/B2S:
This would be possible with some tweaking to the main script:
Cheers!

post-25956-0-90453800-1435426620_thumb.p

post-25956-0-68928900-1435426633_thumb.p

  • Like 2

Share this post


Link to post
Share on other sites

Thanks for sharing this! I am sure it will help quite a few! I hope you don't mind but I moved this to the User Projects subform with a link from the General forum.

Share this post


Link to post
Share on other sites

Bug fixes -

Can now match on tables with commas in the description tag

No more missing arguments in launching the DisplayCard scripts

Changed window for instruction cards and score cards to 700x400. Images displayed in "default" mode which retains the aspect ratio of the source image.

Re-scaled the initial Instruction.png card. Added a Table Info.png card. Score cards now loop after an initial Table Info card in displayed. Both are zipped in attached archive. The file also includes some price cards that I pulled from Stern and Inkochnito. I colored the Stern cards to match their spec.

Did a little tweaking to the exit routine. But it basically functions the same way as before.

There some customizations for my own setup. I'm running VP in 4K and downsampling using NVidia DSR.

I call the first script VPLaunch

#NoEnv
#SingleInstance force
#Include, %A_ScriptDir%\xpath.ahk
SetTitleMatchMode, 2
DetectHiddenWindows, On
; USER INPUTS
PinballX_Path=c:\PinballX
antimicro_Path=c:\antimicro
FFMPEG_Path=c:\ffmpeg\bin
CARD_Path=%A_ScriptDir%\Instruction Cards
SCORE_Path=%A_ScriptDir%\Score Cards
Nudge991=300
NudgePM=75
Filter="scale=iw*(2/3):-1,transpose=3"
Mirror1PosX=3370
Mirror1PosY=1534
Mirror2PosX=3370
Mirror2PosY=254
DisplayCardTime=15000
; roms for lowest latency but possibly with artifacts
arrayf:=["mb_106b", "tz_92", "taf_l7", "frankst"]
; ***********************************************
Table=%1% ;Table filename
SplitPath, Table,,,, XTable,
; Read description tag from xml - Thanks horseyhorsey - http://www.gameex.info/forums/topic/14632-multiple-exe-for-vp/#entry129111
databaseFile=%PinballX_Path%\Databases\Visual Pinball\Visual Pinball.xml
xpath_load(dbXML, databaseFile ) ; need to read the existing xml otherwise xpath deletes all existing nodes
exe:= XPath(dbXML, "/menu/game[@name= . XTable . ]/exe/text()")
rom:= XPath(dbXML, "/menu/game[@name= . XTable . ]/rom/text()")
B2S:= XPath(dbXML, "/menu/game[@name= . XTable . ]/B2S/text()")
description:= XPath(dbXML, "/menu/game[@name= . XTable . ]/description/text()")
StringReplace, description, description,,,`,,
If exe=vpinball991
{
RegWrite, REG_DWORD,HKCU, Software\Visual Pinball\DX9\Player, PBWAccelGainY, %Nudge991%
RegWrite, REG_DWORD,HKCU, Software\Visual Pinball\DX9\Player, PBWAccelGainX, %Nudge991%
}
If exe=vpinball-9.9.1-plunger-mods
{
RegWrite, REG_DWORD,HKCU, Software\Visual Pinball\DX9\Player, PBWAccelGainY, %Nudge991%
RegWrite, REG_DWORD,HKCU, Software\Visual Pinball\DX9\Player, PBWAccelGainX, %Nudge991%
}
If exe=VP_physmod2
{
RegWrite, REG_DWORD, HKCU, Software\Visual Pinball\DX9\Player, PBWAccelGainY, %NudgePM%
RegWrite, REG_DWORD, HKCU, Software\Visual Pinball\DX9\Player, PBWAccelGainX, %NudgePM%
}
IniRead, TablePath, %PinballX_Path%\Config\PinballX.ini, VisualPinball, TablePath
StringReplace, XTablePath, TablePath, \Tables
RegRead, XDMD_width, HKCU, Software\Freeware\Visual PinMame\%rom%, dmd_width
RegRead, XDMD_height, HKCU, Software\Freeware\Visual PinMame\%rom%, dmd_height
RegRead, DMD_X, HKCU, Software\Freeware\Visual PinMame\%rom%, dmd_pos_x
RegRead, XDMD_Y, HKCU, Software\Freeware\Visual PinMame\%rom%, dmd_pos_y
DMD_width:=XDMD_width-3
DMD_height:=XDMD_height+3
DMD_Y:=XDMD_Y-2
; Hardcode
If rom=hirolcas
{
DMD_width:=768
DMD_height:=300
DMD_X:=7680
DMD_Y:=123
}
If rom=simpprty
{
DMD_width:=768
DMD_height:=300
DMD_X:=7680
DMD_Y:=123
}
If rom=ripleys
{
DMD_width:=768
DMD_height:=300
DMD_X:=7680
DMD_Y:=123
}
If rom=wpt_140a
{
DMD_width:=768
DMD_height:=300
DMD_X:=7680
DMD_Y:=123
}
If rom=taf_l7
{
DMD_width:=768
DMD_height:=244
DMD_X:=7680
DMD_Y:=198
}
If B2S=True
{
FileReadLine, BG_width, %TablePath%\ScreenRes.txt, 3
FileReadLine, DMD_width, %TablePath%\ScreenRes.txt, 8
FileReadLine, XDMD_height, %TablePath%\ScreenRes.txt, 9
FileReadLine, XDMD_X, %TablePath%\ScreenRes.txt, 10
FileReadLine, XDMD_Y, %TablePath%\ScreenRes.txt, 11
DMD_height:=XDMD_height+6
DMD_X:=BG_width+XDMD_X ;Reference to Top Left of Playfield screen
DMD_Y:=XDMD_Y-2
}
FileReadLine, CardWidth, %TablePath%\ScreenRes.txt, 8
If !(DMD_width>0)
DMD_width:=0
; Have the information we need to run Card splash screens
Run, %A_ScriptDir%\DisplayCard1.exe "%CARD_Path%" "%Filter%" %Mirror1PosX% %Mirror1PosY% %Mirror2PosX% %Mirror2PosY% "%description%" "%XTable%" %DMD_width% %CardWidth% %DisplayCardTime%
Run, %A_ScriptDir%\DisplayCard2.exe "%SCORE_Path%" "%Filter%" %Mirror1PosX% %Mirror1PosY% %Mirror2PosX% %Mirror2PosY% "%description%" "%XTable%" %DMD_width% %CardWidth% %DisplayCardTime%
StringGetPos, pos, Filter, )
Scale:=SubStr(Filter, 12, pos-11)
StringGetPos, pos1, Scale, /
If ErrorLevel=0
{
Num:=SubStr(Scale, 1, pos1)
Denom:=SubStr(Scale, pos1+2)
Num1:=Num
Denom1:=Denom
Scale:=Num1/Denom1
}
; DMD/B2S border
BOX1_width:=Floor(DMD_width*Scale+23*2)
BOX1_height:=Floor(DMD_height*Scale+4*2)
BOX2_width:=BOX1_width-23
BOX2_height:=BOX1_height-4
; Card border - want to retain the same width and scale the height to preserve the aspect ratio
; we'll see how 700px x 400px goes
If DMD_width>0
CARD1_height:=Floor(DMD_width*Scale*400/700+4*2)
Else
CARD1_height:=Floor(CardWidth*Scale*400/700+4*2)
; Position DMD/B2S adjacent to Card border
Mirror3PosX:=Mirror1PosX+Card1_height
Mirror4PosX:=Mirror2PosX+Card1_height
; ***********************************************
Run, %antimicro_Path%\antimicro --no-tray --hidden --profile "%antimicro_Path%\profiles\VP.joystick.amgp"
; Disable Aero if Enabled
RegRead, DWMComp, HKCU, Software\Microsoft\Windows\DWM, Composition
Aero=%DWMComp%
If Aero=1
Run, sc stop uxsms,, Hide
Run, dc2.exe -configure="%A_ScriptDir%\dc2config\VPMSettings.xml",, Hide
; Mirror and flip DMD for apron
If DMD_width>0
{
Loop % arrayf.MaxIndex()
{
l_table = % arrayf[A_Index]
if( "X" l_table = "X" rom )
{
streamx264=1
break
}
}
If streamx264=1
{
Run, %FFMPEG_Path%\ffmpeg -f gdigrab -framerate 150 -offset_x %DMD_X% -offset_y %DMD_Y% -video_size %DMD_width%x%DMD_height% -i desktop -vf %Filter% -c:v libx264 -preset ultrafast -tune zerolatency -qp 0 -f mpegts -threads 8 udp://localhost:1234,, Hide
Run, %FFMPEG_Path%\ffmpeg -f gdigrab -framerate 150 -offset_x %DMD_X% -offset_y %DMD_Y% -video_size %DMD_width%x%DMD_height% -i desktop -vf %Filter% -c:v libx264 -preset ultrafast -tune zerolatency -qp 0 -f mpegts -threads 8 udp://localhost:5678,, Hide
}
Else
{
Run, %FFMPEG_Path%\ffmpeg -f gdigrab -framerate 150 -offset_x %DMD_X% -offset_y %DMD_Y% -video_size %DMD_width%x%DMD_height% -i desktop -vf %Filter% -c:v mpeg4 -qscale:v 1 -f mpegts -threads 8 udp://localhost:1234,, Hide
Run, %FFMPEG_Path%\ffmpeg -f gdigrab -framerate 150 -offset_x %DMD_X% -offset_y %DMD_Y% -video_size %DMD_width%x%DMD_height% -i desktop -vf %Filter% -c:v mpeg4 -qscale:v 1 -f mpegts -threads 8 udp://localhost:5678,, Hide
}
Sleep, 1000
Run, %FFMPEG_Path%\ffplay -an -sn -i -fflags nobuffer udp://localhost:1234?listen,, Hide
Run, %FFMPEG_Path%\ffplay -an -sn -i -fflags nobuffer udp://localhost:5678?listen,, Hide
Loop, 2
{
checkwindowagain1:
IfWinExist, udp://localhost:1234?listen
WinMove, udp://localhost:1234?listen,, %Mirror3PosX%, %Mirror1PosY%
Else
Goto, checkwindowagain1
}
Loop, 2
{
checkwindowagain2:
IfWinExist, udp://localhost:5678?listen
WinMove, udp://localhost:5678?listen,, %Mirror4PosX%, %Mirror2PosY%
Else
Goto, checkwindowagain2
}
}
Run, %XTablePath%\%exe%.exe -play "%TablePath%\%Table%",, Min
WinWaitActive, ahk_class VPPlayer
Sleep, 1000
WinSet, AlwaysOnTop,, udp://localhost:1234?listen
WinSet, AlwaysOnTop,, udp://localhost:5678?listen
If DMD_width>0
{
; draw a black border to hide the window borders for the mirror
Gui, 1: +LastFound +AlwaysOnTop -Caption +ToolWindow
Gui, 1: Color, 000000
WinSet, Region, 0-0 %BOX1_height%-0 %BOX1_height%-%BOX1_width% 0-%BOX1_width% 0-0 4-24 %BOX2_height%-24 %BOX2_height%-%BOX2_width% 4-%BOX2_width% 4-24
Gui, 1: Show, W%BOX1_height% H%BOX1_width% X%Mirror3PosX% Y%Mirror1PosY% NoActivate
Gui, 2: +LastFound +AlwaysOnTop -Caption +ToolWindow
Gui, 2: Color, 000000
WinSet, Region, 0-0 %BOX1_height%-0 %BOX1_height%-%BOX1_width% 0-%BOX1_width% 0-0 4-24 %BOX2_height%-24 %BOX2_height%-%BOX2_width% 4-%BOX2_width% 4-24
Gui, 2: Show, W%BOX1_height% H%BOX1_width% X%Mirror4PosX% Y%Mirror2PosY% NoActivate
}
; ***********************************************
checkVPPlayer:
IfWinNotExist, Visual Pinball
{
Run, taskkill /im ffplay.exe,, Hide
Run, taskkill /im ffmpeg.exe /f,, Hide
If DMD_width>0
{
Gui, 1: Destroy
Gui, 2: Destroy
}
Process, Close, DisplayCard1.exe
If ErrorLevel
Process, Close, %ErrorLevel%
Process, Close, DisplayCard2.exe
If ErrorLevel
Process, Close, %ErrorLevel%
Run, dc2.exe -configure="%A_ScriptDir%\dc2config\4KSettings.xml"
; Enable Aero if Enabled at the start of script execution
If Aero=1
Run, sc start uxsms
Run, taskkill /im antimicro.exe /f,, Hide
}
else
{
Sleep, 1000
Goto, checkVPPlayer
}
ExitApp
; ***********************************************
; CloseVP from FPLaunch
CloseVP:
;toLog("CloseVP Called")
;Hotkey, %exitScriptKey%, Off
GoSub rosveClose
;GoSub bigbossClose ; change loops completely, put all data in xml !!! too slow
;Visual Pinball must be closed this way instead of killing process
;or it wil not save your last game information.i.e score/credtis
;DetectHiddenWindows on ;Or next line will not work
;Loop, 4
;Gui, %A_Index%: Destroy
;This line fixes where the VP Window flashes real quick
;when closing the window for a cleaner exit
; win hide removed cause vp should be minimized, not hidden
;WinHide, ahk_class VPinball
;WinShow, ahk_class VPinball
WinMinimize, ahk_class VPinball
WinClose, ahk_class VPinball
WinWaitClose ahk_class VPinball
; bigbossclose won't get executed cause main thread will exit
;GoSub bigbossClose
; there's no need for exitscript cause main thread will do it
;Goto ExitScript
return
rosveClose:
;toLog("rosveClose Called")
; rosve
; --------------------------------------------------------------
; --- Close the animated backglass -----------------------
; --------------------------------------------------------------
; b2s close bug fix
StringRight, ending, XTable, 3
if (ending = "B2S")
{
WinKill, Form1
}
return
; ***********************************************
#IfWinActive ahk_class VPPlayer
; Interlock Switch
i::
Send {End}
SetTimer, WaitForJoy7, 10
Return
WaitForJoy7:
if GetKeyState("Joy7")
return
Send {End}
SetTimer, WaitForJoy7, off
Return
; Exit Table
e::
Gosub CloseVP
Return
#IfWinActive

DisplayCard1

#NoEnv
#SingleInstance force
SetTitleMatchMode, 2
DetectHiddenWindows, On
CARD_Path=%1%
Filter=%2%
Mirror1PosX=%3%
Mirror1PosY=%4%
Mirror2PosX=%5%
Mirror2PosY=%6%
description=%7%
XTable=%8%
DMD_width=%9%
CardWidth=%10%
DisplayCardTime=%11%
IfExist, %CARD_Path%\%description%*
cardmatch:=1
IfExist, %CARD_Path%\%XTable%*
cardmatch:=1
If cardmatch=1
{
StringGetPos, pos, Filter, )
Scale:=SubStr(Filter, 11, pos-10)
StringGetPos, pos1, Scale, /
If ErrorLevel=0
{
Num:=SubStr(Scale, 1, pos1)
Denom:=SubStr(Scale, pos1+2)
Num1:=Num
Denom1:=Denom
Scale:=Num1/Denom1
}
; Card border - want to retain the same width and scale the height to preserve the aspect ratio
; we'll see how 700px x 400px goes
If !(DMD_width>0)
DMD_width:=CardWidth
CARD1_width:=Floor(DMD_width*Scale+23*2)
CARD1_height:=Floor(DMD_width*Scale*400/700+4*2)
CARD2_width:=CARD1_width-23
CARD2_height:=CARD1_height-4
GUI4W:=CARD2_width-23
GUI4H:=CARD2_height-4
GUI4X:=Mirror1PosX+4
GUI4Y:=Mirror1PosY+23
; Show instruction cards
Gui, 3: +LastFound -Caption +AlwaysOnTop -SysMenu
GUI3_ID:=WinExist()
Gui, 3: Color, 000000
WinSet, Region, 0-0 %CARD1_height%-0 %CARD1_height%-%CARD1_width% 0-%CARD1_width% 0-0 4-24 %CARD2_height%-24 %CARD2_height%-%CARD2_width% 4-%CARD2_width% 4-24
CardList1=%CARD_Path%\Instructions.png`n
Loop, Files, %CARD_Path%\%description%*
CardList1=%CardList1%%A_LoopFileFullPath%`n
Loop, Files, %CARD_Path%\%XTable%*
CardList1=%CardList1%%A_LoopFileFullPath%`n
WinWaitActive, ahk_class VPPlayer
Loop
{
Loop, Parse, CardList1, `n
{
If A_LoopField= ; Ignore the blank item.
Continue
getSwfFromDisk=%A_LoopField%
GoSub DisplayCard
}
}
}
ExitApp
; ***********************************************
DisplayCard:
Gui, 4: +LastFound -Caption +AlwaysOnTop -SysMenu
GUI4_ID:=WinExist()
Gui, 4: Color, 000000
Gui, 3: Show, W%CARD1_height% H%CARD1_width% X%Mirror1PosX% Y%Mirror1PosY% NoActivate Hide
Gui, 4: Show, W%GUI4H% H%GUI4W% X%GUI4X% Y%GUI4Y% NoActivate Hide
Gui, 4: Margin, 0 ,0
DllCall("AnimateWindow","UInt",GUI3_ID,"Int",1,"UInt","0xa0000")
DllCall("AnimateWindow","UInt",GUI4_ID,"Int",1000,"UInt","0xa0000")
Gui, 4: Add, ActiveX, W%GUI4H% H%GUI4W% vpwb, ShockwaveFlash.ShockwaveFlash
pwb.movie := "file:///" . getSwfFromDisk
pwb.bgcolor := "#000000"
pwb.scale := "default" ;noborder/default/exactfit/noscale
;pwb.wmode := "direct" ;window/opaque/direct/transparent/gpu
Sleep, %DisplayCardTime%
Control, Hide,, MacromediaFlashPlayerActiveX1, ahk_id %GUI2_ID%
DllCall("AnimateWindow","UInt",GUI3_ID,"Int",750,"UInt","0x90000")
DllCall("AnimateWindow","UInt",GUI4_ID,"Int",750,"UInt","0x90000")
Gui, 4: Destroy
Return

DisplayCard2

#NoEnv
#SingleInstance force
SetTitleMatchMode, 2
DetectHiddenWindows, On
SCORE_Path=%1%
Filter=%2%
Mirror1PosX=%3%
Mirror1PosY=%4%
Mirror2PosX=%5%
Mirror2PosY=%6%
description=%7%
XTable=%8%
DMD_width=%9%
CardWidth=%10%
DisplayCardTime=%11%
IfExist, %SCORE_Path%\%description%*
cardmatch:=1
IfExist, %SCORE_Path%\%XTable%*
cardmatch:=1
If cardmatch=1
{
StringGetPos, pos, Filter, )
Scale:=SubStr(Filter, 11, pos-10)
StringGetPos, pos1, Scale, /
If ErrorLevel=0
{
Num:=SubStr(Scale, 1, pos1)
Denom:=SubStr(Scale, pos1+2)
Num1:=Num
Denom1:=Denom
Scale:=Num1/Denom1
}
; Card border - want to retain the same width and scale the height to preserve the aspect ratio
; we'll see how 700px x 400px goes
If !(DMD_width>0)
DMD_width:=CardWidth
CARD1_width:=Floor(DMD_width*Scale+23*2)
CARD1_height:=Floor(DMD_width*Scale*400/700+4*2)
CARD2_width:=CARD1_width-23
CARD2_height:=CARD1_height-4
GUI4W:=CARD2_width-23
GUI4H:=CARD2_height-4
GUI4X:=Mirror2PosX+4
GUI4Y:=Mirror2PosY+23
; Show instruction cards
Gui, 3: +LastFound -Caption +AlwaysOnTop -SysMenu
GUI3_ID:=WinExist()
Gui, 3: Color, 000000
WinSet, Region, 0-0 %CARD1_height%-0 %CARD1_height%-%CARD1_width% 0-%CARD1_width% 0-0 4-24 %CARD2_height%-24 %CARD2_height%-%CARD2_width% 4-%CARD2_width% 4-24
CardList1=%SCORE_Path%\Table Info.png`n
Loop, Files, %SCORE_Path%\%description%*
CardList1=%CardList1%%A_LoopFileFullPath%`n
Loop, Files, %SCORE_Path%\%XTable%*
CardList1=%CardList1%%A_LoopFileFullPath%`n
WinWaitActive, ahk_class VPPlayer
Loop
{
Loop, Parse, CardList1, `n
{
If A_LoopField= ; Ignore the blank item.
Continue
getSwfFromDisk=%A_LoopField%
GoSub DisplayCard
}
}
}
ExitApp
; ***********************************************
DisplayCard:
Gui, 4: +LastFound -Caption +AlwaysOnTop -SysMenu
GUI4_ID:=WinExist()
Gui, 4: Color, 000000
Gui, 3: Show, W%CARD1_height% H%CARD1_width% X%Mirror2PosX% Y%Mirror2PosY% NoActivate Hide
Gui, 4: Show, W%GUI4H% H%GUI4W% X%GUI4X% Y%GUI4Y% NoActivate Hide
Gui, 4: Margin, 0 ,0
DllCall("AnimateWindow","UInt",GUI3_ID,"Int",1,"UInt","0xa0000")
DllCall("AnimateWindow","UInt",GUI4_ID,"Int",1000,"UInt","0xa0000")
Gui, 4: Add, ActiveX, W%GUI4H% H%GUI4W% vpwb, ShockwaveFlash.ShockwaveFlash
pwb.movie := "file:///" . getSwfFromDisk
pwb.bgcolor := "#000000"
pwb.scale := "default" ;noborder/default/exactfit/noscale
;pwb.wmode := "direct" ;window/opaque/direct/transparent/gpu
Sleep, %DisplayCardTime%
Control, Hide,, MacromediaFlashPlayerActiveX1, ahk_id %GUI2_ID%
DllCall("AnimateWindow","UInt",GUI3_ID,"Int",750,"UInt","0x90000")
DllCall("AnimateWindow","UInt",GUI4_ID,"Int",750,"UInt","0x90000")
Gui, 4: Destroy
Return

I have a workflow for creating media from custom cards that people publish on the Internet. I try to obtain or produce media with very high quality, and it is do-able here but with limitations. I was pleasantly surprised that the majority of images that I have worked with so far do have dimensions consistent with the manufacturer documented dimensions found here under Card Sizes:

http://www.pinballre.../pinball/cards/

So, producing images although still a manual process is pretty quick and easy.

I use GIMP 2. It will open just about all image types and import from PDF. Open an image or import a PDF page with images. I just drag and drop and select what I need in dialog windows.

Tools/Selection Tools/Rectangle Select. Use the default settings in the Tool Options dialog.

Use the mouse/cursor to draw a rectangle roughly around the image.

Image/Crop to Selection

Then Autocrop Image.

In a small portion of cases, the resulting image might require some additional cropping (if the image includes cut guides or was specifically providing larger borders for trimming printed reproductions. Use the mouse/cursor to draw a rectangle, and the size of the crop area can be fine tuned with the Position and Size options in the Tool Options dialog

After cropping, check Image/Print Size...

the dimensions should be consistent with a real card from that manufacturer. Most of the time it is. Sometimes you have to change the X/Y resolution. To retain the aspect ratio of the cropped image make sure the chain symbol is linked. Change the resolution until the print size is consistent with a real card. Click OK

File/Export As...

Choose a name. Select File Type. Use PNG image. Click Export. You will get a dialog where there are settings that can be toggled on/off. Just use the default as it appears and click Export. The file is ready for PinballX with no loss in quality. You could take the output to a printer. You'd probably still want to take the source file to the printer rather than the output though.

You could use the SWF files from the FTP. But they would still need to be rotated for use by the script. The batch converter I wrote can do it but it does it by taking a screen grab. There is some loss in quality. Probably better off just taking a high quality scan or reproduction and generating a PNG from the source and using that image rather than the SWF.
I have updated the batch converter. ffmpeg captures for SWF media are now processed at the highest quality settings to minimize image degradation. PNG images are rorated losslessly, The script uses convert.exe from ImageMagick
There are a number of other tools that can be used as well.

#NoEnv
#SingleInstance force
; USER INPUTS
FFMPEG_Path=c:\Users\MeltonE\Downloads\ffmpeg-20150606-git-f073764-win64-static\bin
CARD_Path=c:\Users\MeltonE\Downloads\ic2
ImageMagick_Path=c:\Users\MeltonE\Downloads\ImageMagick-6.9.1-Q16
; rotated animated instruction cards must be located in the same folder as this script/executable
arrayc:=["Attack from Mars (Bally 1995) 5.swf", "Torpedo Alley (Data East 1988).swf"]
CardList1= ; Initialize to be blank.
Loop, Files, %CARD_Path%\*.*
CardList1=%CardList1%%A_LoopFileFullPath%`n
Loop, Parse, CardList1, `n
{
If A_LoopField= ; Ignore the blank item.
Continue
SplitPath, A_LoopField, XCardfullname,, ext, XCard,
AniMatch=0
Loop % arrayc.MaxIndex()
{
l_card = % arrayc[A_Index]
If( "X" l_card = "X" XCardfullname )
{
Gui, 2: +LastFound -Caption +AlwaysOnTop -SysMenu
Gui, 2: Show, W700 H400 X0 Y0
Gui, 2: Margin, 0 ,0
Gui, 2: Add, ActiveX, W700 H400 vpwb, ShockwaveFlash.ShockwaveFlash
getSwfFromDisk=%CARD_Path%\%XCardfullname%
pwb.movie := "file:///" . getSwfFromDisk
pwb.scale := "default"
Run, %FFMPEG_Path%\ffmpeg -t 30 -f gdigrab -framerate 30 -offset_x 0 -offset_y 0 -video_size 700x400 -i desktop -c:v libx264 -preset ultrafast -qp 0 -threads 8 "%A_ScriptDir%\IC.mkv",, Hide
Process, WaitClose, ffmpeg.exe, 40
Process, Close, %ErrorLevel%
Gui, 2: Destroy
Run, %FFMPEG_Path%\ffmpeg -y -i "%A_ScriptDir%\IC.mkv" -vf "transpose=2" -qscale:v 1 "%A_ScriptDir%\rotate\%XCardfullname%"
Process, WaitClose, ffmpeg.exe, 60
Process, Close, %ErrorLevel%
FileDelete, %A_ScriptDir%\IC.mkv
AniMatch=1
Break
}
}
If AniMatch=0
If ext=swf
{
Gui, 2: +LastFound -Caption +AlwaysOnTop -SysMenu
Gui, 2: Show, W700 H400 X0 Y0
Gui, 2: Margin, 0 ,0
Gui, 2: Add, ActiveX, W700 H400 vpwb, ShockwaveFlash.ShockwaveFlash
getSwfFromDisk=%A_LoopField%
pwb.movie := "file:///" . getSwfFromDisk
pwb.scale := "default"
Run, %FFMPEG_Path%\ffmpeg -t 1 -f gdigrab -framerate 30 -offset_x 0 -offset_y 0 -video_size 700x400 -i desktop -c:v libx264 -preset ultrafast -qp 0 -threads 8 "%A_ScriptDir%\IC.mkv",, Hide
Process, WaitClose, ffmpeg.exe, 5
Process, Close, %ErrorLevel%
Gui, 2: Destroy
Run, %FFMPEG_Path%\ffmpeg -y -i "%A_ScriptDir%\IC.mkv" -ss 00:00:00.500 -vf "transpose=2" -vframes 1 -crf 0 "%A_ScriptDir%\rotate\%XCard%.png"
Process, WaitClose, ffmpeg.exe, 5
Process, Close, %ErrorLevel%
FileDelete, %A_ScriptDir%\IC.mkv
}
If ext=png
{
Run, %ImageMagick_Path%\convert -rotate 270 -background transparent "%CARD_Path%\%XCardfullname%" "%A_ScriptDir%\rotate\%XCardfullname%"
Process, WaitClose, convert.exe, 5
}
}
ExitApp

In the end, I'm very happy with how this turned out. Sadly, though generating two DMD mirrors and looping through images using two embedded ActiveX Flash controls is a little too much for my system when I am also doing 4K downsampling. Getting some stutter, sticky flippers, and sound doubling up. It looks like I've found the limit to what this machine can do. I'll probably walk it back a little bit and may just display on one side of the apron rather than both sides.

cards.zip

  • Like 1

Share this post


Link to post
Share on other sites

Hi, I completed generating media for my apron miniDMD and flash player project. This primarily involved using GIMP to trim images out of source files and making sure they are scaled close to the correct manufacturer dimensions so that they will be displayed correctly by the player. Again, the player loops through lists of instruction cards and coin/score cards that are associated with a specific table and displays them on the apron on either side of the table.

In the same way you can get PinballX to loop through sequences of cards by adding a number to the card's filename (Ex. Centaur.swf, Centaur 1.swf, Centaur 2.swf, etc.). You do the same thing here with series of images associated with a table. I arrange my cards so that every instruction card has some sort of coin card paired with it. For custom cards that are matched pairs, I make sure that their filenames end in the same number so that the player will display the set together at the same time.

I've uploaded some of my work on the FTP Other Uploads/Carny/MiniDMD and flash player. These are images of the stock factory cards. In pairing coin cards to instruction cards, I checked for accuracy largely by observing the ROM when I begin a table. I always just use the default factory settings. For Stern Pinball tables I tried to confirm using shot maps or flyers if available. Otherwise, if I can't figure it out I simply look and see what the table author chose to use.

Most files still require rotating for use in a cabinet. Use the batch converter script in the previous post. Some files are already rotated because they were rotated in the source that I captured them from.

I'm only including the stock factory cards here. The source is primarily Inkochnito's reproduction cards and Stern Pinball who provided a lot of reproductions for the Sega and Data East properties that they acquired. I processed a lot of custom cards as well, but the sources tend to ask people not to re-distribute and I will respect that. Otherwise, the content is freely available.

http://pinballcards.blogspot.com.au/

http://www.pinballcards.com/

http://www.pinballrebel.com/game/pins/instruction/index.htm

People post custom cards at Pinside as well.

For coin cards and my generic "Instructions" and "Table Info" cards, I've copied up the source files for the work

  • Like 1

Share this post


Link to post
Share on other sites

I just now got to checking into this, but it looks fantastic!

I'm almost afraid to ask, what system specs is this hitting limits on?

Share this post


Link to post
Share on other sites

Intel i5 3570k @ 3.4 GHz

8 Gb DDR3 RAM

NVidia GTX660

Samsung SSD 840 Series

I'm running VP using NVidia DSR technology downsampling from 4K. All cores get close to maxing out. I tried some CPU affinity settings to try to manage the load manually, but I don't see that I am getting better performance. I do get stutter and timing issues with flippers depending on the sum of textures that need to be rendered on the table, so I know I'll have to dial something back. Either go back to HD or mirror on one side of the apron and not both sides.

  • Like 1

Share this post


Link to post
Share on other sites

Reading threads trying to catch up on things a bit when i saw this and wow, now that's a cool idea!

Share this post


Link to post
Share on other sites

The fine Dozer version of Cirqus Voltaire shows that it is very doable to have VPX render a mirrored display with flashers. It would require modding the tables and scripts for this feature. Not a high priority for me at the moment. But very cool.

Share this post


Link to post
Share on other sites

Update:

Modified to work and look better for Win10 (using a basic or non-Aero theme)

Because of changes made to automate Visual Pinball, I can no longer use this launcher with PinballX's integrated Visual Pinball support. I run this launcher as a custom system with a custom system type.

Start up and exit code is more reliable

Tested with a recent static build release of ffmpeg (2016-09-29) and Nvidia driver (372.90) running on Windows 10 Anniversary Edition

Can be modified for VPX windowed full screen support

Display of Instruction cards and Score cards are better synchronized via cheap process cross-communication using a temporary ini file written to disc.

Launches VPX True Full Screen and automatically brings focus to Visual Pinball Player  (no support for mirrored DMD or splash viewer with true full screen, however)

I've fixed all cards for use with the splash viewer. On the FTP, Other Uploads/Carny/Z20160604/MiniDMDFlash

 

Edit: Update LaunchVP attachment 

PinballX.ini - don't need to monitor for any other executables

LaunchVP1mirror.ahk - fix bugs

Notes:

I still run an Intel i5 3570k, 8Gb RAM, with an NVidia GTX 660, 2GB VRAM. With my configuration, the overhead of running all of the extra processes to display everything on both sides of the apron is a little much for VP9 and too much for VPX. Based on the executable, the script runs the mirrors and splash viewer for VP9. And for the performance benefit, the script runs VPX with its Video Options set to use True Full Screen mode.

I've included a version of the script where the DMD mirror and splash viewer on the right hand side of the apron are disabled. That should be less of a performance hit if running on both sides of the apron causes slowdown or "sticky flippers" while in-game.

Because True Full Screen locks out all other processes that try to use the screen there is no way this solution will work (not on the playfield screen anyway).

Weird thing - If a dB2S is really large and/or takes a long time to load, such as the one released with Fire! or the big fantasy one for Monster Bash, there winds up being some sort of a conflict with PinballX where B2SBackglassServerEXE.exe is shutdown or crashes. I don't know if it is a timing issue or resource conflict. But if Task Manager is running at the same time, then dB2S backglasses always load successfully. It's hacky, but without knowing what else to do at this point, I programmatically start Task Manager before launching Visual Pinball and then close it down when quitting the table.

No assumptions are made about the dimensions required to capture the DMD. If the table is VPM, the dimensions are pulled from the registry folder for VPinMAME for the specific rom the table uses. If the table is UltraDMD, the dimensions are pulled from the registry folder for UltraDMD. If the table uses a third screen dB2S or older B2S, the dimensions are pulled from screenres.txt. Capture dimensions can otherwise be customized in the script. Information that tells the script if the table is VPM or B2S is stored in a custom xml that uses the standard rom and exe tags and adds a B2S tag. I've included my xml as an example. 

For me, latency on the DMD mirror was better with Win 7 than with Win 10.

This is probably the last update for this project. These days, I only install VPX tables. With my hardware, VPX runs great but not so good with DMD mirror and the splash viewer. As I replace the VP9 and Phymod5 versions of my favorites with shiny, new VPX versions I can see phasing this project out.

I'd expect that a cab with powerful hardware could still use a variation of this script to run a mirror and/or splash viewer using a fourth screen. Somebody had asked about routing something like this to fourth and fifth mini screens mounted on a false apron. Possible, I guess, if the hardware is state of the art. 

 

 

LaunchVP.zip

Share this post


Link to post
Share on other sites

Hi, I am quitting the splash viewer part of this project for now. The solution is not compatible with true full screen VPX plus I'm simply running out of drive space on my cabinet. Moving the media off gives me more storage space for tables!

Media will still be available on the FTP, Other Uploads/Carny/Z20160604

Inkochnito is responsible for pretty much all of the heavy lifting. Please support and donate to his site. He provides a great service:

http://www.pinballrebel.com/pinball/cards/

The work I've put into cutting and redrawing on Data East/Sega/Stern Pinball cards are free to use by the community without attribution. All sources are trademarks/copyrights of their respective owners to be privately used in PinballX or for other non-commercial (or at least non-profit) pinball-related applications. I hold no responsibility for their misuse.    

I will at least continue to provide redraws of media for the Media/Instruction Cards folder as I add them to my machine, but at this rate I don't expect to be adding very frequently.

Share this post


Link to post
Share on other sites

This is really cool.  Didn't find out about it until now.  Going to get it working in my cab, thanks for creating them.

Share this post


Link to post
Share on other sites

Thanks for sharing this project. will try it out this weekend

Share this post


Link to post
Share on other sites

It was too resource hungry to run with VPX on my system, but I still use it for VP9. I have not been publishing updates to my script. Any changes are largely in response to VPX updates as I use the one script to launch all tables for all versions of VP. I'll post it if you want it.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now