Showing posts with label ideas. Show all posts
Showing posts with label ideas. Show all posts

Thursday, August 27, 2020

LaserDisc Capture Device Ecosystem


Diagram of the capture system

I've been doing a bit off and on with my Apple IIc (Early model, upgraded to Rom4X) and my LaserDisc player (Pioneer CLD-V2400), now that I have it all working.   I've created a video switching device (less prototype-ish version of it coming soon) and have made a few different serial interface adapters so I can control the LDP from the Apple IIc, Tandy "Model T" line, and my Amiga.  I plan on having stub BASIC programs for each in github eventually, here on my LaserDisc catch-all repository.

But this brings up the issue that it's a niche project for a very esoteric and specific audience.  That is to say, vintage computer enthusiasts WHO ALSO have a LaserDisc player, WHO ALSO have specific LaserDisc titles... It's the Sega 32X issue all over again. ;)

So... how to bring playability ("play" in the sense of "playing games created" as well as "playing the video discs") to a larger subset of the vintage computer enthusiasts... and also reduce reliance on super heavy players with large spinning plastic discs in them...  Of course the answer is Raspberry Pi!

The Idea 

The basic idea is to use a Raspberry Pi (Zero hopefully) as a standalone LaserDisc Player simulator.  More on that specifically in a future post, but the goal of this post is to talk about getting content for this simulator.  And, yes, there are LD simulators/emulators out there, like "Daphne", but they're generally for Arcade machine use, and simulate older players (like for Dragon's Lair), and they work VERY well, but they're quite expensive.  This will be a solution that may not work for arcade machine applications, but for home/game/toy/experimenter use, it will be super-cheap (no more than $20 total for the Pi + Serial interface... lowering the bar of entry substantially.

So for capturing a disc, One method might be to just capture the audio and composite output of a disc, and have the PI run something like the VLC player application, which might still be the solution, but I thought I'd experiment with making a custom player, since it will probably be cumbersome to do this with VLC, being how there is a large level of control needed.

The issue comes in when you realize that playback requires frame-specific actions being done with the disc itself.  This is usually fine, but for some content, there's a 2-3 pulldown put in to convert the original 24fps film content to 29.97fps NTSC composite-video LaserDisc content.  The short-short version of this is that if you were to just step through frame by frame, you'd see repeated frames, as well as some frames that are interleaved with each other.  This is not what consumers expect when they hit frame-forward or frame-backwards on their player.  Instead, when you use those functions, the player jumps around, skipping duplicated as well as interleaved frames.  This is handled invisibly by the player, as it accesses the frames from the disc, which are marked as being "full frames".  (this is a gross overview, but generally correct-ish.)

So you can capture the video stream, and play that back via VLC (or other media player) but the results won't be accurate to a LDP.  You'll have to manually skip frames, and offset per the 2-3 pulldown... it gets messy, but you can get an "okayish" approximation.  This is what my Javascript "Rollercoaster" laserdisc simulation did.  And it worked decently.  But it's not good enough.

The Domesday Duplicator Project

I had the idea to capture discs in a more "raw" form, so I started looking into what was available for this.  I looked into the Domesday Duplicator for capturing LaserDiscs, and use their "ld-decode" software to decode it and play it back, since it would be ideal.  I could capture the raw RF content stored on the LD, and play it back exactly as a LDP.  There are some issues with this though...

First of all, I do not have the right LaserDisc player to capture the content, nor full documentation about the hack necessary, nor the expensive hardware needed to sample the RF stream... It is a substantial overhead to capture discs.  Perfect for their archival use, but it's massive overkill for playing some homebrew games.

Secondly, for their captures, each side of a disk requires approximately 103 gigabytes of storage space, which is inconvenient. ;)

My Solution

The solution I decided to run with is to simply step through the disc, frame by frame, and capture each composite video frame individually, then capture the audio separately, and sync them back up in the playback application.

Video would be captured frame-specific on the disc... that is to say only unique 480p-ish frames will be captured, so file "32908.png" will be frame 32908.  No need to convert or deal with if a title has 
a 3-2 pulldown, or it's 29.97 fps, or whatever...  Playback of video might be odd, but this could always be augmented with video files for playback of segments, using these files as still-frame files.

For video capture, I'm using my Canon Optura Xi DV camcorder as a video-firewire bridge, and capturing the video frames using imagesnap, which can save jpeg or png images captured from standard Mac video sources, including firewire-DV connected devices.  It could just as easily been done through some USB Composite AV capture device, but I do not have access to one.

Each of the pairs of audio tracks will be captured directly from the analog audio output from the player.  (I have not done this part, but I plan on using sox to do this, probably, although some tests I've done so far have had dropouts while recording... which is less than ideal... audio capture could be accomplished using a more modern computer or recording device...)

The diagram at the top of this post shows this workflow.

For controlling the LDP, I have a simple python script that connects using the standard serial libraries to a USB-Serial interface, which is wired to my player via RS/232 (4800 baud).  I don't have a store-bought USB serial device, but any of them should work fine.  I decided to just throw together something using stuff I had, which can be seen in this image:

This is my USB-RS232 interface. There are many functionally like it, but this one is mine.

There really is nothing special to this, it's just a USB-RS232 adapter.... using an old USB-Ipod serial dock adapter, FTDI extension wire, and then a cobbled-together TTL-RS232 level shifter. :)

Results

All of this is working as expected. (python script will be posted to github soon.)  I have the LDP spin up the disk ("PL"), seek to the lead-out (end) of the side ("FRLOSE"), get the frame count ("F?"), then seek back to frame 0 ("FR00000SE").  Most discs seem to have between 32,000 and 45,000 frames per side, providing about 30 minutes of content.

Each frame of 704x480 (I know, i was expecting 720x480, but this is what I got.) as JPG is about 140 kilobytes, and as PNG is about 600 kilobytes.  The issue with it though is that it was slow to capture.  The frame seek and stepping was quick, but the capture tool was slow, capturing about 27 PNG frames per minute, or about 35 JPG files per minute.  This would be hours to capture a single side of a disc.  Which is fine, since it's completely automated.  It would produce about 7 gigabytes of video frames per side.

I experimented with capturing audio as well.  I couldn't capture from the firewire/DV stream, but I just hooked the LDP directly in to the Mac Mini I was testing with.  I used SOX to capture some content, and it had a few dropouts.  This could have been because the computer is ancient... So more tests are necessary on that front.

The plan for the capture software is to have it query the LDP to get more meta-content out of the disc as well.  It can get data about the disc, player serial number, number of audio tracks, etc.  From that it could completely automate capturing each of the two potential pairs of  audio streams, or as individual tracks, depending on the needs of the disc... as well as all video frames, and perhaps a DV stream as well... maybe.  


Remember to set the DV Camcorder/Bridge for "video in"

Currently, I've got the software doing the following process:

  • Initialize the player:
    • "PL" Spin up the disc, and "Play"
    • "FRLOSE" When that's done, seek to the frame at the lead-out at the end of the disk
    • "F?" Query to get the current frame number (the number of frames)
    • "FR1SE" seek back to frame 1 on the disc (*)
    • "SR" Step in reverse, back 1 step (*)
  • For each frame:
    • "FRxSE" seek to frame x (the current frame number)
    • run: "imagesnap outdir/x.png" to capture a video frame to a PNG file


This is currently working.  Eventually, I'll add in the additional commands to query available audio tracks, and run "sox -d discname_audio.wav" to capture that.

Saturday, July 28, 2018

Digital Logic in Javascript...


I needed some time off from my main project right now, and I had this bug in my brain to try out some of this, so I did a thing.  It uses javascript, jquery, bootstrap, and css stuff to do the whole thing. I use some of this at work, so this was a great chance to learn a bit more about that tech.

So here's a link to the quickly and horribly named "Logicr", pronounced "lä-jik-ər".

It's essentially a tiny javascript library/set of classes that simulate digital logic.  I'm not going to start drawing out diagrams of class hierarchies because that would take too long to do.  Instead, i'll quickly explain a bit of it, and then explain what the interface is showing.

At the bottom level, there are objects that are "pins".  This is a thing that can be set with a low, high, or floating data level.  I made the level actually a value from 1-100, below 25 is "low", above 75 is "high" and in the middle is "floating".  When you read them, you get back a true/false value, and you can write the 0..100 level, or true/false.  I made it this way so that i can eventually simulate pullups/pulldowns in the circuits.

Those pins are included in "nodes", which have a collection of inputs and outputs.  One example of a node is a logic gate.  A typical gate has two inputs, A, B, and one output, Y.  When the nodes get their update() call, they do whatever math is necessary to read in from their inputs and set their outputs.   Currently the system supports the basic 1-2 input logic gates: Not, And, Nand, Or, Nor, Xor, as well as one-pin output "sources" that generate High, Low, and Float output.

Another node is a "clock".  This one toggles its output once a second (1000ms) between high and low levels.

Another type of thing in the system is a "wire".  Wires are just lists of connections "from" and "to" pins on nodes in the system.

In the above example, there's a clock node (XTAL1), whose output Y goes to the first LED, named "clk".  There's a second wire from XTAL1's output that goes to the input of a Not gate (Inverter), in-turn, whose output goes to the second LED, "!clk".

You may notice that they don't change instantaneously.  That's because the system is set up to update all of the nodes first, then update all of the wires.  This prevented having to actually generate a real directed graph, and deal with loops and recursion and loops and recursion and stuff.  It probably simulates propagation delay on real parts, but i'm sure it has its own unique quirks.

So every 50ms, it goes through and tells all of the nodes to do their math, then immediately goes through the list of wires, reading from their FROM and writing to their TO.

So for the clock circuit:

  • Nodes:
    • Clock XTAL1 running at 1000ms
    • LED D1, blue, labelled "clk"
    • LED D2, blue, labelled "!clk"
    • Not gate IC1
  • Wires
    • XTAL1.Y to D1.A -- connects the clock to the first LED
    • XTAL1.Y to IC1.A -- connects the clock to the not gate
    • IC1.Y to D2.A -- connects the not gate to the second LED

That's the basics of it.

But there's more.

I added a few input devices and output devices.  First is the switch.  The switch is essentially a html checkbox at its core.  When the switch node gets called to update, it reads the value directly out of the checkbox widget in the browser, and sets its output pin to the appropriate value.  Similarly, the LEDs are just a "div" that's made round and the right color (dim/bright red, etc) via CSS.  When the LED nodes are updated, they read the value from their input pin, then apply the appropriate "on" or "off" class to their html div.  The browser and CSS take care of the rest.

There's a couple other things happening in the circuit there too...

The amber switches are the A and B inputs to an XOR gate.  The A and B switches also go to the amber LEDs with the same labels, so that you can see what's going on there.  The output from the XOR gate goes to the green LED labelled Y.

The four red switches labelled 0x01, 0x02, 0x04, and 0x08 are run into a sort-of ROM, implemented as just a simple 16 value lookup table.  The ROM has 4 inputs and 8 outputs.  The 8 outputs are wired to the red seven-segment display node's "a"-"g" inputs, which light up the segments of the display on the web interface.  As you toggle the switches, the appropriate number is displayed.  The ROM data is basically a lookup table of which segments to drive for each value 0x0..0x9,0xA-0xF.  The equivalent of the 74LS47 ic chip.  I was originally going to build this circuit using logic gates, but it got really complex...  I may still do it eventually, once i have a better editor.

Mess with it...

In the interface, if you scroll down, you'll see an editor section at the bottom. Press the "Load Default" button to fill the text box with the circuit description that's running, and you can see all of the logic nodes and wire connections. Press the "Run Circuit" button to read in the JSON content from that window and rebuild the internal logic to use it.  The content there has to be valid JSON, otherwise it won't work.  The "Run" button will have a green border when it's valid, red when it's not.

Future

So yeah. This was meant to be a quick test to see how well it could quickly be done, and expanded upon for a possible future project that I've been thinking about for a while. There are still more component classes I want to add, such as "packages" which can group together circuits... for example, creating flip-flops using gate logic, or building that 7-segment decoder, or more complex things...

I also would like to make an in-browser editor of the circuits.  I've been looking at jsPlumb as well as a few other solutions for doing "create movable boxes with connection magnets that are connected with wires" type of things.  I may just spin up something of my own, as having draggable boxes is fairly easy to do using CSS/JS and then connections are just SVG lines between them, etc...  Although i'm sure the nuances of the interface make it more complex than it seems...

Oh. and this is running in my tiny page thingy, llmin... It's basically a wrapper for a page that sets up jQuery, bootstrap, and some other stuff, so that I don't have to create that boilerplate every time I want to make a page to test stuff. ;)

Monday, July 13, 2015

Retro Challenge 2015/7 Update 2: Keyboard And Joysticks

Space Invaders running in Stella on my Mac.  Jasper loved it!
(He also liked the green light-up USB cable.  That's my kid!)

As of last night, I have finished code for the joystick-to-keypress controller, as well as the ability to save the configuration in the EEPROM in the ATmega chip. As usual, the code is all available here on github.  What this basically means is that moving the joystick will send down keypress codes to the host computer... so the computer thinks that a keyboard is being used.  This means that applications like Stella, an Atari 2600 VCS emulator, which expect you to use the arrow keys and space bar in place of a real joystick, can see those key presses when you move your actual Atari joystick!

You can now pick from a list of keyboard mappings, including:

  • Stella (Atari 2600 emulator) (shown above)
  • Vice (Commodore 64 emulator)
  • Mame (Arcade game emulator)
  • WASD (general PC game usage)
  • HJKL (vi cursor movements (because why not!))

I have also implemented a basic settings system that saves settings to the EEPROM within the ATmega32u4 chip.  It currently has "slots" for a few things, but could be expanded with the above joystick-to-keyboard mapping, to send other keypresses from joystick movements or what have you.

The settings system works with 4 bytes in the EEPROM to determine what's going on with respect to saved settings. The first 3 bytes are a sentinel.  If the code does not read the sentinel, it will assume it's on a new device, and will install default values to the EEPROM.  The current sentinel are the three hex bytes: { 0x53, 0x44, 0x4c } which are the ASCII string "SDL", my initials. ;)  Next at setting slot [3] is the settings version number.  Currently this is just '1'.  Settings slot [4] contains the usage mode for the interface port. (Mouse/keyboard mapping, etc).

I attempted last week to get an Amiga Keyboard working with this.  I wired up an 8 position pin header to the interface board, using the Amiga 500 pinout:

  1. Data
  2. Clock
  3. Reset (toggled when Ctrl-A-A is pressed)
  4. +5v
  5. key (missing pin)
  6. Ground
  7. Status LED (power)
  8. In-Use LED (disk access)
I was going to just wire out the Data, Clock, +5 and ground but decided that it made more sense to use the actual A500 pinout. I ran into a problem on this though.  The interface can't provide enough power to drive the chips on the interface board.  I measured 4.5v at the pin header, then 3.2v down on the interface board itself.  This isn't good.  I will have to wire up a new interface connector with an external power connector, probably a USB-B jack, to provide dedicated power to the keyboard.

I did however find a phone being discarded, and retrieved the two 4P4C (RJ-22) connectors from it so that I can make a better A1000 keyboard jack.  The first one I made used an RJ-45 wall mount jack, which worked somewhat for RJ-22, but it was pretty hacked. ;)

Some thoughts for future additional expansion:

I'm currently going for the "cheap as chips" version of this, which is why there's no interface on it, and you have to connect via serial port to configure it.  This was all intentional to keep the build parts list down... to the point where the only necessary part is a D9 connector!  However, in using it, a few future revisions may offer more usability at the cost of parts expense:

  1. Multiple D9 jacks for multiple devices
    1. Ports dedicated for Mouse, Trackball, Multiple Joysticks, etc
    2. Could be just 2 that simulate Commodore/Amiga
    3. Could be 4 to simulate Atari 800 joystick ports
    4. Could be 1 for mouse, 5 for joysticks, so you never have to disconnect them.
    5. Would require a parallel-in, serial out shift register, or a port expander
    6. Expansion at the cost of a few cents, additional wiring, and time to read in the content
  2. (related) configuration for multiple ports
  3. User interface - Was thinking that this could be a variety of different methods:
    1. single pushbutton, Red/Green LED for indication codes (flashing colors)
    2. single or dual pushbutton, RGB color addressable LED for indication codes
      1. each could be wired directly to IO pins
    3. dual pushbutton, 7 segment LED display
      1. Could indicate use mode(s), etc
      2. 'C' for commodore mouse, 'A' for atari mouse
      3. '0'..'7' for joystick mode display
      4. Could flash multiple digits sequentially for info etc
      5. Would require LED driver chip
Obviously, all of these would require additional effort beyond the "let's get this working" and can be seen as stretch-stretch goals.  ;)