This is a set of drivers (currently, just one driver) that encode light states as text and stream the result to the desired output. By making the light data available as a readable stream, light control can be performed by another program. Such a program can be implemented using any language/platform that supports driving the desired output device (and reading from an input stream). If C++ isn't your thing, or if learning the guts of StepMania seems a little much just to implement a lights driver, you're in the right place.
You'll need a working StepMania build with the driver
LightsDriver_SextetStreamToFile
enabled.
For this test, you'll run a script, SextetStreamStdinTest
, to verify
that the driver is set up properly. The script is a simple example of a
lighting program, a program that will receive input from StepMania and
produce output using the data. Because the script is for testing and
diagnostics, the is text in a console window. Actual lighting programs
receive input in the same way, but will do different things with the
output, such as sending commands out over a hardware interface
(serial/parallel/USB) to control physical lights.
You'll need the perl script
SextetStreamStdinTest.pl
.
Set the executable bit (chmod ug+x SextetStreamStdinTest.pl
).
We assume that $SM
is the root StepMania directory.
In Preferences.ini
, set:
LightsDriver=SextetStreamToFile
SextetStreamOutputFilename=Data/StepMania-Lights-SextetStream.out
Create the FIFO:
mkfifo "$SM/Data/StepMania-Lights-SextetStream.out"
Run the test stdin-based lighting program, using the FIFO as input:
./SextetStreamStdinTest.pl < "$SM/Data/StepMania-Lights-SextetStream.out"
While the lighting program is running, start StepMania. (When using the test lighting program, windowed mode may be needed to keep the program visible.) The lighting program should display the light information.
When StepMania closes, the FIFO connection ends, causing the lighting program to exit as well.
If you have a microcontroller running firmware that understands the
SextetStream protocol, you can use socat
as the lighting program to
pipe the data directly to the device via a serial port:
socat "$SM/Data/StepMania-Lights-SextetStream.out" /dev/ttyUSB0,raw,echo=0,b115200
Substitute your actual serial port device for /dev/ttyUSB0
and the
actual port speed for 115200
. (But note that running at too low a
speed may result in the output blocking, causing StepMania to freeze or
stutter).
TODO: Supply example Arduino/PIC/... firmware.
TODO: Supply example of full-duplex in cooperation with the SextetStream input driver.
FIXME: This has not been tested at all. The following is a guess at a synopsis of how it should work if you're lucky enough for everything to have fallen into place just so. (Please amend this message if you manage to get this running consistently on Mac OS X.)
The instructions for Linux should work equally well for Mac OS X.
TODO: Serial examples.
FIXME: This has not been tested at all. The following is a guess at a synopsis of how it should work if you're lucky enough for everything to have fallen into place just so. (Please amend this message if you manage to get this running consistently on Windows.)
You'll need:
- The Windows Script Host script
SextetStreamStdinTest.wsf
, the lighting program createAndReadPipe
, to work with a named pipe from the console.
In Preferences.ini
, set:
LightsDriver=SextetStreamToFile
SextetStreamOutputFilename=\\.\pipe\StepMania-Lights-SextetStream
Use createAndReadPipe
to create the named pipe, feeding the output
into the test stdin-based lighting program:
createAndReadPipe StepMania-Lights-SextetStream | cscript //nologo SextetStreamStdinTest.wsf
While the lighting program is running, start StepMania. (When using the test lighting program, windowed mode may be needed to keep the program visible.) The lighting program should display the light information.
When StepMania closes, the pipe connection ends, causing the lighting program to exit as well.
TODO: Serial examples.
Values are encoded six bits at a time (hence "sextet").
Data is packed into the low 6 bits of a byte, then the two high bits are set in such a way that the result is printable, non-whitespace ASCII:
0x00-0x0F -> 0x40-0x4F
0x10-0x1F -> 0x50-0x5F
0x20-0x2F -> 0x60-0x6F
0x30-0x3F -> 0x30-0x3F
(0x20-0x2F and 0x70-0x7F are avoided since they contain control or whitespace characters.)
The characters are made printable so that attempting to read the output or pass it through a text-oriented channel will not cause problems.
In any case, decoding these values is trivial and involves no
lookups—just do & 0x3F
. (And even that may be optional, depending on
the application.)
The driver repeatedly outputs a serialization of the current light state
followed by an LF (0xA, \n
), at least once each time any light should
change (and currently no more often than that).
Currently, this message is 13 data bytes followed by LF. The following information is contained:
-
Byte 0
- 0x01 Marquee upper-left
- 0x02 Marquee upper-right
- 0x04 Marquee lower-left
- 0x08 Marquee lower-right
- 0x10 Bass left
- 0x20 Bass right
-
Byte 1
- 0x01 Player 1 menu left
- 0x02 Player 1 menu right
- 0x04 Player 1 menu up
- 0x08 Player 1 menu down
- 0x10 Player 1 start
- 0x20 Player 1 select
-
Byte 2
- 0x01 Player 1 back
- 0x02 Player 1 coin
- 0x04 Player 1 operator
- 0x08 Player 1 effect up
- 0x10 Player 1 effect down
- 0x20 (reserved)
-
Byte 3
- 0x01 Player 1 #1
- 0x02 Player 1 #2
- 0x04 Player 1 #3
- 0x08 Player 1 #4
- 0x10 Player 1 #5
- 0x20 Player 1 #6
-
Byte 4
- 0x01 Player 1 #7
- 0x02 Player 1 #8
- 0x04 Player 1 #9
- 0x08 Player 1 #10
- 0x10 Player 1 #11
- 0x20 Player 1 #12
-
Byte 5
- 0x01 Player 1 #13
- 0x02 Player 1 #14
- 0x04 Player 1 #15
- 0x08 Player 1 #16
- 0x10 Player 1 #17
- 0x20 Player 1 #18
-
Byte 6
- 0x01 Player 1 #19
- 0x02 (reserved)
- 0x04 (reserved)
- 0x08 (reserved)
- 0x10 (reserved)
- 0x20 (reserved)
-
Byte 7
- 0x01 Player 2 menu left
- 0x02 Player 2 menu right
- 0x04 Player 2 menu up
- 0x08 Player 2 menu down
- 0x10 Player 2 start
- 0x20 Player 2 select
-
Byte 8
- 0x01 Player 2 back
- 0x02 Player 2 coin
- 0x04 Player 2 operator
- 0x08 Player 2 effect up
- 0x10 Player 2 effect down
- 0x20 (reserved)
-
Byte 9
- 0x01 Player 2 #1
- 0x02 Player 2 #2
- 0x04 Player 2 #3
- 0x08 Player 2 #4
- 0x10 Player 2 #5
- 0x20 Player 2 #6
-
Byte 10
- 0x01 Player 2 #7
- 0x02 Player 2 #8
- 0x04 Player 2 #9
- 0x08 Player 2 #10
- 0x10 Player 2 #11
- 0x20 Player 2 #12
-
Byte 11
- 0x01 Player 2 #13
- 0x02 Player 2 #14
- 0x04 Player 2 #15
- 0x08 Player 2 #16
- 0x10 Player 2 #17
- 0x20 Player 2 #18
-
Byte 12
- 0x01 Player 2 #19
- 0x02 (reserved)
- 0x04 (reserved)
- 0x08 (reserved)
- 0x10 (reserved)
- 0x20 (reserved)
Above, Player 1 #n
and Player 2 #n
refer to StepMania's internal
GAME_BUTTON_CUSTOM_nn
values, whose meaning depends on the game.
Currently, these mappings are:
-
dance
- 1 Pad left
- 2 Pad right
- 3 Pad up
- 4 Pad down
- 5 Pad up-left
- 6 Pad up-right
-
techno: Same as dance, plus
- 7 Pad center
- 8 Pad down-left
- 9 Pad down-right
-
pump
- 1 Pad up-left
- 2 Pad up-right
- 3 Pad center
- 4 Pad down-left
- 5 Pad down-right
-
kb7
- 1-7 Keys 1-7
-
ez2
- 1 Foot upper-left
- 2 Foot upper-right
- 3 Foot down
- 4 Hand upper-left
- 5 Hand upper-right
- 6 Hand lower-left
- 7 Hand lower-right
-
para
- 1 Button left
- 2 Button up-left
- 3 Button up
- 4 Button up-right
- 5 Button right
-
ds3ddx
- 1 Hand left
- 2 Foot down-left
- 3 Foot up-left
- 4 Hand up
- 5 Hand down
- 6 Foot up-right
- 7 Foot down-right
- 8 Hand right
-
beat
- 1-7 Keys 1-7
- 8 Scratch up
- 9 Scratch down
-
maniax
- 1 Sensor over-left
- 2 Sensor over-right
- 3 Sensor under-left
- 4 Sensor under-right
-
popn
- 1 Button left white
- 2 Button left yellow
- 3 Button left green
- 4 Button left blue
- 5 Button center
- 6 Button right blue
- 7 Button right green
- 8 Button right yellow
- 9 Button right white
The current implementation of this driver uses a RageFile
for output.
Therefore, any RageFile
-based filesystem abstractions are applied.
Please keep this in mind when specifying the input path.
FIXME: This behavior is probably permanent, but if it is not possible to
open a Windows pipe using RageFile
, RageFile
should probably change
so that it is.
The output of this driver streams to a file. At face value, this is not too useful. The actual intent is for the system operator to create a named fifo or named pipe that is being listened to by some already running program, such as a program that sends light data out over a serial connection. It just happens that opening a named fifo for writing can be accomplished in the same fashion as opening a file for writing. Since there's an easy, platform-ignorant way to do that, that's what we've done.
Note that this uses blocking I/O, so your light processing program must make sure to empty the fifo as quickly as it gets filled. If this driver's write blocks, parts of the application stall. If the program is non-trivial, using a separate thread dedicated to reading may be useful.
How you actually start up the other end of the connection depends on your platform.
mkfifo
is used to create a named FIFO. After this is done, two
programs (one reading and one writing) open the FIFO as if it were some
ordinary file and use ordinary file I/O to read or write.
For this driver, the lighting program and StepMania open the file for read and write, respectively.
Lighting programs accepting input on stdin work trivially; the FIFO is simply redirected into the program's stdin.
The client side of a Windows named pipe is fairly ordinary; the path to an already-open pipe can be opened and used as if it were an ordinary file. For this driver, StepMania is the client.
The server side of the pipe is a little trickier. Special Windows API calls are needed to create the pipe and then wait for the client to connect. Unlike in Linux, the FIFO goes away when it is closed. For this driver, the lighting program is the server.
Lighting programs accepting input on stdin do not work trivially because
named pipes are not eligible to be created or redirected on the command
line. A bridging program such as createAndReadPipe
can be used to
create a pipe and relay all of its input to stdout, which can then be
redirected.
A Windows-specific lighting program might also just create and read a named pipe by itself.
Copyright © 2014 Peter S. May
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.