Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

I495 #498

Merged
merged 8 commits into from
Jun 1, 2020
Merged

I495 #498

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 3 additions & 11 deletions ld-decode
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ parser.add_argument('-n', '--ntsc', dest='ntsc', action='store_true', help='sour
parser.add_argument('-m', '--MTF', metavar='mtf', type=float, default=None, help='mtf compensation multiplier')
parser.add_argument('--MTF_offset', metavar='mtf_offset', type=float, default=None, help='mtf compensation offset')
parser.add_argument('-j', '--NTSCJ', dest='ntscj', action='store_true', help='source is in NTSC-J (IRE 0 black) format')
parser.add_argument('--noAGC', dest='noAGC', action='store_true', default=False, help='Disable AGC')
parser.add_argument('--noDOD', dest='nodod', action='store_true', default=False, help='disable dropout detector')
parser.add_argument('--noEFM', dest='noefm', action='store_true', default=False, help='Disable EFM front end')
parser.add_argument('--daa', dest='daa', action='store_true', default=False, help='Disable analog audio decoding')
Expand Down Expand Up @@ -58,7 +59,8 @@ if args.pal and args.ntsc:
print("ERROR: Can only be PAL or NTSC")
exit(1)

extra_options = {}
extra_options = {'useAGC': not args.noAGC}

if args.WibbleRemover:
extra_options['WibbleRemover'] = True
if args.lowband:
Expand Down Expand Up @@ -112,16 +114,6 @@ if len(DecoderParamsOverride.keys()):
if args.verboseVITS:
ldd.verboseVITS = True

def write_json(ldd, outname):
jsondict = ldd.build_json(ldd.curfield)

fp = open(outname + '.tbc.json.tmp', 'w')
json.dump(jsondict, fp, indent=4 if args.verboseVITS else None)
fp.write('\n')
fp.close()

os.rename(outname + '.tbc.json.tmp', outname + '.tbc.json')

done = False

while not done and ldd.fields_written < (req_frames * 2):
Expand Down
34 changes: 20 additions & 14 deletions lddecode/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -1108,7 +1108,8 @@ def setparams(self, params):
def dsa_rescale(infloat):
return int(np.round(infloat * 32767 / 150000))

# right now defualt is 16/48, so not optimal :)
# Downscales to 16bit/44.1khz. It might be nice when analog audio is better to support 24/96,
# but if we only support one output type, matching CD audio/digital sound is greatly preferable.
def downscale_audio(audio, lineinfo, rf, linecount, timeoffset = 0, freq = 48000.0, scale=64):
failed = False

Expand All @@ -1126,8 +1127,7 @@ def downscale_audio(audio, lineinfo, rf, linecount, timeoffset = 0, freq = 48000

# XXX:
# The timing handling can sometimes go outside the bounds of the known line #'s.
# This is a quick-ish fix that should work OK, but may need to be reworked if
# analog audio is weak for some reason
# This is a quick-ish fix that should work OK but may affect quality slightly.
if linenum < 0:
lineloc_cur = int(lineinfo[0] + (rf.linelen * linenum))
lineloc_next = lineloc_cur + rf.linelen
Expand All @@ -1144,7 +1144,7 @@ def downscale_audio(audio, lineinfo, rf, linecount, timeoffset = 0, freq = 48000
sampleloc += (lineloc_next - lineloc_cur) * (linenum - np.floor(linenum))

swow[i] = (lineloc_next - lineloc_cur) / rf.linelen
# There's almost *no way* the disk is spinning more than 5% off, so mask TBC errors here
# There's almost *no way* the disk is spinning more than 1.5% off, so mask TBC errors here
# to reduce pops
if i and np.abs(swow[i] - swow[i - 1]) > .015:
swow[i] = swow[i - 1]
Expand All @@ -1171,8 +1171,6 @@ def downscale_audio(audio, lineinfo, rf, linecount, timeoffset = 0, freq = 48000

failed = True

#print(locs[len(arange)-1], len(audio['audio_left']), np.min(output), np.max(output), swow[len(arange) - 1], linenum)

np.clip(output, -32766, 32766, out=output16)

return output16, arange[-1] - frametime
Expand Down Expand Up @@ -2574,6 +2572,8 @@ def __init__(self, fname_in, fname_out, freader, analog_audio = 0, digital_audio
self.frameNumber = None

self.autoMTF = True
# Python 3.6 doesn't support .get with default=
self.useAGC = extra_options['useAGC'] if 'useAGC' in extra_options else True

self.verboseVITS = False

Expand Down Expand Up @@ -2747,16 +2747,18 @@ def readfield(self, initphase = False):

if not self.checkMTF(f, self.prevfield):
redo = True

sync_hz, ire0_hz = self.detectLevels(f)
sync_ire_diff = np.abs(self.rf.hztoire(sync_hz) - self.rf.SysParams['vsync_ire'])

if (sync_ire_diff > 2) or (np.abs(self.rf.hztoire(ire0_hz)) > 2):
redo = True
# Perform AGC changes on first fields only to prevent luma mismatch intra-field
if self.useAGC and f.isFirstField:
sync_hz, ire0_hz = self.detectLevels(f)
sync_ire_diff = np.abs(self.rf.hztoire(sync_hz) - self.rf.SysParams['vsync_ire'])

if (sync_ire_diff > 2) or (np.abs(self.rf.hztoire(ire0_hz)) > 2):
redo = True

self.rf.SysParams['ire0'] = ire0_hz
# Note that vsync_ire is a negative number, so (sync_hz - ire0_hz) is correct
self.rf.SysParams['hz_ire'] = (sync_hz - ire0_hz) / self.rf.SysParams['vsync_ire']
self.rf.SysParams['ire0'] = ire0_hz
# Note that vsync_ire is a negative number, so (sync_hz - ire0_hz) is correct
self.rf.SysParams['hz_ire'] = (sync_hz - ire0_hz) / self.rf.SysParams['vsync_ire']

if adjusted == False and redo == True:
self.demodcache.flushvideo()
Expand Down Expand Up @@ -3102,6 +3104,8 @@ def seek_getframenr(self, startfield):
f, offset = self.decodefield(initphase = True)

if f is None:
# If given an invalid starting location (i.e. seeking to a frame in an already cut raw file),
# go back to the beginning and try again.
if startfield != 0:
startfield = 0
self.roughseek(startfield)
Expand All @@ -3112,6 +3116,8 @@ def seek_getframenr(self, startfield):
self.curfield = f
self.fdoffset += offset

# Two fields are needed to be sure to have sufficient Philips code data
# to determine frame #.
if self.prevfield is not None and f.valid:
fnum = self.decodeFrameNumber(self.prevfield, self.curfield)

Expand Down
98 changes: 97 additions & 1 deletion lddecode/plot_utils.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,22 @@
#!/usr/bin/python3

import io
from io import BytesIO
import re
import subprocess
import sys

import numpy as np
import scipy as sp
import scipy.signal as sps
import sys

import matplotlib.pyplot as plt

# To support image displays
from PIL import Image
import IPython.display
from IPython.display import HTML

pi = np.pi
tau = np.pi * 2

Expand Down Expand Up @@ -130,4 +141,89 @@ def doplot2(B, A, B2, A2, freq = (315.0/88.0) * 8.0):
def BA_to_FFT(B, A, blocklen):
return np.complex64(sps.freqz(B, A, blocklen, whole=True)[1])

# Notebook support functions follow

# Draws a uint16 image, downscaled to uint8
def draw_raw_bwimage(bm, x = 2800, y = 525, hscale = 1, vscale = 2, outsize = None):
if y is None:
y = len(bm) // x

if outsize is None:
outsize = (x * hscale, y * vscale)

bmf = np.uint8(bm[0:x*y] / 256.0)
# print(bmf.shape)
if x is not None:
bms = (bmf.reshape(len(bmf)//x, -1))
else:
bms = bmf

# print(bms.dtype, bms.shape, bms[:][0:y].shape)
im = Image.fromarray(bms[0:y])
im = im.resize(outsize)
b = BytesIO()
im.save(b, format='png')
return IPython.display.Image(b.getvalue())

def draw_field(field):
return draw_raw_bwimage(field.dspicture, field.outlinelen, field.outlinecount)

class RGBoutput:
def __init__(self, outname):
try:
rv = subprocess.run('ld-chroma-decoder {0}.tbc {0}.rgb'.format(outname), capture_output=True, shell=True)
except:

return None

if rv.returncode != 0:
print("Failed to run ld-chroma-decoder: ", rv.returncode)
print(rv.stderr.split('\n'))

stderr = rv.stderr.decode('utf-8')

outres = re.search('trimmed to ([0-9]{3}) x ([0-9]{3})', stderr)
outframes = re.search('complete - ([0-9]*) frames', stderr)

if outres is None or outframes is None:
print("Error, did not decode correctly")

self.x, self.y = [int(v) for v in outres.groups()]
self.numframes = int(outframes.groups()[0])

with open('{0}.rgb'.format(outname), 'rb') as fd:
# 3 colors, 2 bytes/color
raw = fd.read((self.x * self.y * 3 * 2 * self.numframes))

self.rgb = np.frombuffer(raw, 'uint16', len(raw)//2)

def lineslice(self, frame, line):
if line >= self.y or frame > self.numframes:
return None

def getslice(offset):
return slice(((self.y * frame) + line) * (self.x * 3) + offset, ((self.y * frame) + line + 1) * (self.x * 3) + offset, 3)

return [getslice(offset) for offset in range(3)]

def plotline(self, frame, line):
sl = self.lineslice(frame, line)

if sl is None:
return None

plt.plot(self.rgb[sl[0]], 'r')
plt.plot(self.rgb[sl[1]], 'g')
plt.plot(self.rgb[sl[2]], 'b')

def display(self, framenr, scale = 1):
begin = self.lineslice(framenr, 0)[0].start
end = self.lineslice(framenr, self.y - 1)[0].stop

rawimg = self.rgb[begin:end].reshape((self.y, self.x, 3))
rawimg_u8 = np.uint8(rawimg // 256)
im = Image.fromarray(rawimg_u8)
b = BytesIO()
im.save(b, format='png')

return IPython.display.Image(b.getvalue(), width=int(self.x * scale), height=int(self.y * scale))
13 changes: 12 additions & 1 deletion lddecode/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import getopt
import io
from io import BytesIO
import json
import os
import sys
import subprocess
Expand Down Expand Up @@ -797,6 +798,16 @@ def db_to_lev(db):
def lev_to_db(rlev):
return 20 * np.log10(rlev)

# Write the .tbc.json file (used by lddecode and notebooks)
def write_json(ldd, outname):
jsondict = ldd.build_json(ldd.curfield)

fp = open(outname + '.tbc.json.tmp', 'w')
json.dump(jsondict, fp, indent=4 if ldd.verboseVITS else None)
fp.write('\n')
fp.close()

os.rename(outname + '.tbc.json.tmp', outname + '.tbc.json')

if __name__ == "__main__":
print("Nothing to see here, move along ;)")
print("Nothing to see here, move along ;)")
Loading