Skip to content

Commit

Permalink
Merge pull request #586 from happycube/chad-12.26.20
Browse files Browse the repository at this point in the history
Add RF TBC .ldf output
  • Loading branch information
happycube authored Dec 28, 2020
2 parents 89e206f + 726ec14 commit 82bddfc
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 22 deletions.
4 changes: 1 addition & 3 deletions ld-cut
Original file line number Diff line number Diff line change
Expand Up @@ -90,9 +90,7 @@ if makelds:
process = subprocess.Popen(['ld-lds-converter', '-o', outname, '-p'], stdin=subprocess.PIPE)
fd = process.stdin
elif makeldf:
corecmd = "ffmpeg -y -hide_banner -loglevel error -f s16le -ar 40k -ac 1 -i - -acodec flac -f ogg".split(' ')
process = subprocess.Popen([*corecmd, '-compression_level', str(args.ldfcomp), outname], stdin=subprocess.PIPE)
fd = process.stdin
process, fd = ldf_pipe(outname, args.ldfcomp)
else:
fd = open(args.outfile, 'wb')

Expand Down
17 changes: 14 additions & 3 deletions ld-decode
Original file line number Diff line number Diff line change
Expand Up @@ -43,19 +43,24 @@ parser.add_argument('--daa', dest='daa', action='store_true', default=False, hel
parser.add_argument('--ignoreleadout', dest='ignoreleadout', action='store_true', default=False, help='continue decoding after lead-out seen')
parser.add_argument('--verboseVITS', dest='verboseVITS', action='store_true', default=False, help='Enable additional JSON fields')

parser.add_argument('--RF_TBC', dest='RF_TBC', action='store_true', default=False, help="Create a .tbc.ldf file with TBC'd RF")

parser.add_argument('--lowband', dest='lowband', action='store_true', default=False, help='Use more restricted RF settings for noisier disks')

parser.add_argument('--NTSC_color_notch_filter', dest='NTSC_color_notch_filter', action='store_true', default=False, help='Mitigate interference from analog audio in reds in NTSC captures')
parser.add_argument('--V4300D_notch_filter', dest='V4300D_notch_filter', action='store_true', default=False, help='LD-V4300D PAL/digital audio captures: remove spurious ~8.5mhz signal')

parser.add_argument('-d', '--deemp_adjust', metavar='deemp_adjust', type=float, default=1.0, help='mtf compensation multiplier')
parser.add_argument('-d', '--deemp_adjust', metavar='deemp_adjust', type=float, default=1.0, help='Deemphasis level multiplier')
parser.add_argument('--deemp_low', metavar='deemp_low', type=float, default=0, help='Deemphasis low coefficient')
parser.add_argument('--deemp_high', metavar='deemp_high', type=float, default=0, help='Deemphasis high coefficient')

parser.add_argument('-t', '--threads', metavar='threads', type=int, default=5, help='number of CPU threads to use')

parser.add_argument('-f', '--frequency', dest='inputfreq', metavar='FREQ', type=parse_frequency, default=None, help='RF sampling frequency in source file (default is 40MHz)')

parser.add_argument('--video_bpf_high', dest='vbpf_high', metavar='FREQ', type=parse_frequency, default=None, help='Video BPF high end frequency')
parser.add_argument('--video_lpf', dest='vlpf', metavar='FREQ', type=parse_frequency, default=None, help='Video low-pass filter frequency')

parser.add_argument('--video_lpf_order', dest='vlpf_order', type=int, default=-1, help='Video low-pass filter order')

args = parser.parse_args()
#print(args)
Expand All @@ -69,7 +74,10 @@ if args.pal and args.ntsc:
print("ERROR: Can only be PAL or NTSC")
exit(1)

extra_options = {'useAGC': not args.noAGC, 'deemp_level': (args.deemp_adjust, args.deemp_adjust)}
extra_options = {'useAGC': not args.noAGC,
'write_RF_TBC': args.RF_TBC,
'deemp_mult': (args.deemp_adjust, args.deemp_adjust),
'deemp_coeff': (args.deemp_low, args.deemp_high)}

if vid_standard == 'NTSC' and args.NTSC_color_notch_filter:
extra_options['NTSC_ColorNotchFilter'] = True
Expand Down Expand Up @@ -125,6 +133,9 @@ if args.vbpf_high is not None:
if args.vlpf is not None:
DecoderParamsOverride['video_lpf_freq'] = args.vlpf * 1000000

if args.vlpf_order >= 1:
DecoderParamsOverride['video_lpf_order'] = args.vlpf_order

if len(DecoderParamsOverride.keys()):
ldd.demodcache.setparams(DecoderParamsOverride)

Expand Down
86 changes: 70 additions & 16 deletions lddecode/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -363,9 +363,17 @@ def __init__(self, inputfreq = 40, system = 'NTSC', blocklen = 32*1024, decode_d

self.SysParams['analog_audio'] = has_analog_audio

self.deemp_level = extra_options.get('deemp_level')
if self.deemp_level is None:
self.deemp_level = (1.0, 1.0)
self.deemp_mult = extra_options.get('deemp_mult', (1.0, 1.0))

deemp = list(self.DecoderParams['video_deemp'])

if extra_options.get('deemp_low', 0):
deemp[0] = extra_options.get['deemp_low']

if extra_options.get('deemp_high', 0):
deemp[1] = extra_options.get['deemp_high']

self.DecoderParams['video_deemp'] = deemp

linelen = self.freq_hz/(1000000.0/self.SysParams['line_period'])
self.linelen = int(np.round(linelen))
Expand Down Expand Up @@ -480,6 +488,9 @@ def computevideofilters(self):
video_lpf = sps.butter(DP['video_lpf_order'], DP['video_lpf_freq']/self.freq_hz_half, 'low')
SF['Fvideo_lpf'] = filtfft(video_lpf, self.blocklen)

video_bpf = sps.butter(2, [3900000/self.freq_hz_half, 4500000/self.freq_hz_half], 'bandpass')
SF['Fvideo_bpf'] = filtfft(video_bpf, self.blocklen)

if self.system == 'NTSC' and self.NTSC_ColorNotchFilter:
video_notch = sps.butter(3, [DP['video_lpf_freq']/1000000/self.freq_half, 5.0/self.freq_half], 'bandstop')
SF['Fvideo_lpf'] *= filtfft(video_notch, self.blocklen)
Expand All @@ -489,16 +500,16 @@ def computevideofilters(self):

# The deemphasis filter
deemp1, deemp2 = DP['video_deemp']
deemp1 *= self.deemp_level[0]
deemp2 *= self.deemp_level[1]
deemp1 *= self.deemp_mult[0]
deemp2 *= self.deemp_mult[1]
SF['Fdeemp'] = filtfft(emphasis_iir(deemp1, deemp2, self.freq_hz), self.blocklen)

# The direct opposite of the above, used in test signal generation
SF['Femp'] = filtfft(emphasis_iir(deemp2, deemp1, self.freq_hz), self.blocklen)

# Post processing: lowpass filter + deemp
SF['FVideo'] = SF['Fvideo_lpf'] * SF['Fdeemp']
SF['FVideo'] = (SF['Fvideo_lpf'] * SF['Fdeemp']) #+ (SF['Fvideo_bpf'] / 10)

# additional filters: 0.5mhz and color burst
# Using an FIR filter here to get a known delay
F0_5 = sps.firwin(65, [0.5/self.freq_half], pass_zero=True)
Expand Down Expand Up @@ -2036,6 +2047,37 @@ def downscale(self, lineinfo = None, linesout = None, outwidth = None, channel='

return dsout, self.dsaudio, self.efmout

def rf_tbc(self, linelocs = None):
''' This outputs a TBC'd version of the input RF data, mostly intended
to assist in audio processing. Outputs a uint16 array.
'''

# Convert raw RF to floating point to help the scaler
fdata = self.data['input'].astype(np.float)

if linelocs is None:
linelocs = self.linelocs

# Ensure that the output line length is an integer
linelen = int(round(self.inlinelen))

# Adjust for the demodulation/filtering delays
delay = self.rf.delays['video_white']

# For output consistency reasons, linecount is set to 313 (i.e. 626 lines)
# in PAL mode. This needs to be corrected for RF TBC.
lc = self.linecount
if self.rf.system == 'PAL' and not self.isFirstField:
lc = 312

output = []

for l in range(self.lineoffset, self.lineoffset + lc):
scaled = scale(fdata, linelocs[l] - delay, linelocs[l + 1] - delay, linelen)
output.append(np.round(scaled).astype(np.int16))

return np.concatenate(output)

def decodephillipscode(self, linenum):
linestart = self.linelocs[linenum]
data = self.data['video']['demod']
Expand Down Expand Up @@ -2721,6 +2763,7 @@ def __init__(self, fname_in, fname_out, freader, _logger, est_frames = None, ana

self.analog_audio = int(analog_audio * 1000)
self.digital_audio = digital_audio
self.write_rf_tbc = extra_options.get('write_RF_TBC', False)

self.has_analog_audio = True
if system == 'PAL':
Expand All @@ -2731,18 +2774,21 @@ def __init__(self, fname_in, fname_out, freader, _logger, est_frames = None, ana

self.lastvalidfield = {False: None, True: None}

self.outfile_video = None
self.outfile_audio = None
self.outfile_efm = None
self.ffmpeg_rftbc, self.outfile_rftbc = None, None

if fname_out is not None:
self.outfile_video = open(fname_out + '.tbc', 'wb')
self.outfile_audio = open(fname_out + '.pcm', 'wb') if self.analog_audio else None
self.outfile_efm = open(fname_out + '.efm', 'wb') if self.digital_audio else None

if digital_audio:
if self.analog_audio:
self.outfile_audio = open(fname_out + '.pcm', 'wb')
if self.digital_audio:
# feed EFM stream into ld-ldstoefm
self.efm_pll = efm_pll.EFM_PLL()
else:
self.outfile_video = None
self.outfile_audio = None
self.outfile_efm = None
self.outfile_efm = open(fname_out + '.efm', 'wb')
if self.write_rf_tbc:
self.ffmpeg_rftbc, self.outfile_rftbc = ldf_pipe(fname_out + '.tbc.ldf')

self.fname_out = fname_out

Expand Down Expand Up @@ -2802,8 +2848,13 @@ def __del__(self):
def close(self):
''' deletes all open files, so it's possible to pickle an LDDecode object '''

try:
self.ffmpeg_rftbc.kill()
except:
pass

# use setattr to force file closure by unlinking the objects
for outfiles in ['infile', 'outfile_video', 'outfile_audio', 'outfile_json', 'outfile_efm']:
for outfiles in ['infile', 'outfile_video', 'outfile_audio', 'outfile_json', 'outfile_efm', 'outfile_rftbc']:
setattr(self, outfiles, None)

self.demodcache.end()
Expand Down Expand Up @@ -2869,6 +2920,9 @@ def writeout(self, dataset):
self.outfile_video.write(picture)
self.fields_written += 1

if self.outfile_rftbc is not None:
self.outfile_rftbc.write(f.rf_tbc())

if audio is not None and self.outfile_audio is not None:
self.outfile_audio.write(audio)

Expand Down
6 changes: 6 additions & 0 deletions lddecode/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,12 @@ def read(self, infile, sample, readlen):
def __call__(self, infile, sample, readlen):
return self.read(infile, sample, readlen)

def ldf_pipe(outname, compression_level = 6):
corecmd = "ffmpeg -y -hide_banner -loglevel error -f s16le -ar 40k -ac 1 -i - -acodec flac -f ogg".split(' ')
process = subprocess.Popen([*corecmd, '-compression_level', str(compression_level), outname], stdin=subprocess.PIPE)

return process, process.stdin

# Git helpers

def get_git_info():
Expand Down

0 comments on commit 82bddfc

Please sign in to comment.