tictacsync 0.99a0__tar.gz → 1.4.3b0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of tictacsync might be problematic. Click here for more details.

Files changed (25) hide show
  1. {tictacsync-0.99a0/tictacsync.egg-info → tictacsync-1.4.3b0}/PKG-INFO +5 -5
  2. {tictacsync-0.99a0 → tictacsync-1.4.3b0}/README.md +2 -2
  3. {tictacsync-0.99a0 → tictacsync-1.4.3b0}/setup.py +7 -4
  4. {tictacsync-0.99a0 → tictacsync-1.4.3b0}/tictacsync/device_scanner.py +62 -272
  5. {tictacsync-0.99a0 → tictacsync-1.4.3b0}/tictacsync/entry.py +57 -166
  6. tictacsync-1.4.3b0/tictacsync/mamconf.py +157 -0
  7. tictacsync-1.4.3b0/tictacsync/mamdav.py +642 -0
  8. tictacsync-1.4.3b0/tictacsync/mamreap.py +481 -0
  9. tictacsync-1.4.3b0/tictacsync/mamsync.py +343 -0
  10. {tictacsync-0.99a0 → tictacsync-1.4.3b0}/tictacsync/multi2polywav.py +4 -3
  11. {tictacsync-0.99a0 → tictacsync-1.4.3b0}/tictacsync/timeline.py +153 -96
  12. {tictacsync-0.99a0 → tictacsync-1.4.3b0}/tictacsync/yaltc.py +359 -31
  13. {tictacsync-0.99a0 → tictacsync-1.4.3b0/tictacsync.egg-info}/PKG-INFO +5 -5
  14. {tictacsync-0.99a0 → tictacsync-1.4.3b0}/tictacsync.egg-info/SOURCES.txt +4 -2
  15. tictacsync-1.4.3b0/tictacsync.egg-info/entry_points.txt +7 -0
  16. tictacsync-0.99a0/tictacsync/remergemix.py +0 -259
  17. tictacsync-0.99a0/tictacsync/remrgmx.py +0 -116
  18. tictacsync-0.99a0/tictacsync.egg-info/entry_points.txt +0 -4
  19. {tictacsync-0.99a0 → tictacsync-1.4.3b0}/LICENSE +0 -0
  20. {tictacsync-0.99a0 → tictacsync-1.4.3b0}/setup.cfg +0 -0
  21. {tictacsync-0.99a0 → tictacsync-1.4.3b0}/tictacsync/__init__.py +0 -0
  22. {tictacsync-0.99a0 → tictacsync-1.4.3b0}/tictacsync.egg-info/dependency_links.txt +0 -0
  23. {tictacsync-0.99a0 → tictacsync-1.4.3b0}/tictacsync.egg-info/not-zip-safe +0 -0
  24. {tictacsync-0.99a0 → tictacsync-1.4.3b0}/tictacsync.egg-info/requires.txt +0 -0
  25. {tictacsync-0.99a0 → tictacsync-1.4.3b0}/tictacsync.egg-info/top_level.txt +0 -0
@@ -1,11 +1,11 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: tictacsync
3
- Version: 0.99a0
4
- Summary: command for syncing audio video recordings
3
+ Version: 1.4.3b0
4
+ Summary: commands for syncing audio video recordings
5
5
  Home-page: https://tictacsync.org/
6
6
  Author: Raymond Lutz
7
7
  Author-email: lutzrayblog@mac.com
8
- Classifier: Development Status :: 2 - Pre-Alpha
8
+ Classifier: Development Status :: 4 - Beta
9
9
  Classifier: Environment :: Console
10
10
  Classifier: Intended Audience :: End Users/Desktop
11
11
  Classifier: License :: OSI Approved :: MIT License
@@ -23,7 +23,7 @@ License-File: LICENSE
23
23
 
24
24
  # tictacsync
25
25
 
26
- ## Warning: this is at pre-alpha stage
26
+ ## Warning: this is at beta stage
27
27
 
28
28
  Unfinished sloppy code ahead, but should run without errors. Some functionalities are still missing. Don't run the code without parental supervision. Suggestions and enquiries are welcome via the [lists hosted on sourcehut](https://sr.ht/~proflutz/TicTacSync/lists).
29
29
 
@@ -58,7 +58,7 @@ Then pip install the syncing program:
58
58
  This should install python dependencies _and_ the `tictacsync` command.
59
59
  ## Usage
60
60
 
61
- Download multiple sample files [here](https://nuage.lutz.quebec/s/P3gbZR4GgGy8xQp/download/dailies1_1.zip) (625 MB, sorry) unzip and run:
61
+ Download multiple sample files [here](https://nuage.lutz.quebec/s/4jw4xgqysLPS8EQ/download/dailies1_3.zip) (700+ MB, sorry) unzip and run:
62
62
 
63
63
  > tictacsync dailies/loose
64
64
  The program `tictacsync` will recursively scan the directory given as argument, find all audio that coincide with any video and merge them into a subfolder named `SyncedMedia`. When the argument is an unique media file (not a directory), no syncing will occur but the decoded starting time will be printed to stdout:
@@ -1,6 +1,6 @@
1
1
  # tictacsync
2
2
 
3
- ## Warning: this is at pre-alpha stage
3
+ ## Warning: this is at beta stage
4
4
 
5
5
  Unfinished sloppy code ahead, but should run without errors. Some functionalities are still missing. Don't run the code without parental supervision. Suggestions and enquiries are welcome via the [lists hosted on sourcehut](https://sr.ht/~proflutz/TicTacSync/lists).
6
6
 
@@ -35,7 +35,7 @@ Then pip install the syncing program:
35
35
  This should install python dependencies _and_ the `tictacsync` command.
36
36
  ## Usage
37
37
 
38
- Download multiple sample files [here](https://nuage.lutz.quebec/s/P3gbZR4GgGy8xQp/download/dailies1_1.zip) (625 MB, sorry) unzip and run:
38
+ Download multiple sample files [here](https://nuage.lutz.quebec/s/4jw4xgqysLPS8EQ/download/dailies1_3.zip) (700+ MB, sorry) unzip and run:
39
39
 
40
40
  > tictacsync dailies/loose
41
41
  The program `tictacsync` will recursively scan the directory given as argument, find all audio that coincide with any video and merge them into a subfolder named `SyncedMedia`. When the argument is an unique media file (not a directory), no syncing will occur but the decoded starting time will be printed to stdout:
@@ -29,12 +29,15 @@ setup(
29
29
  entry_points = {
30
30
  "console_scripts": [
31
31
  'tictacsync = tictacsync.entry:main',
32
- 'remergemix = tictacsync.remergemix:main',
32
+ 'mamreap = tictacsync.mamreap:main',
33
+ 'mamdav = tictacsync.mamdav:main',
34
+ 'mamsync = tictacsync.mamsync:main',
35
+ 'mamconf = tictacsync.mamconf:main',
33
36
  'multi2polywav = tictacsync.multi2polywav:main',
34
37
  ]
35
38
  },
36
- version = '0.99a',
37
- description = "command for syncing audio video recordings",
39
+ version = '1.4.3-beta',
40
+ description = "commands for syncing audio video recordings",
38
41
  long_description_content_type='text/markdown',
39
42
  long_description = long_descr,
40
43
  include_package_data=True,
@@ -43,7 +46,7 @@ setup(
43
46
  author_email = "lutzrayblog@mac.com",
44
47
  url ='https://tictacsync.org/',
45
48
  classifiers=[
46
- 'Development Status :: 2 - Pre-Alpha',
49
+ 'Development Status :: 4 - Beta',
47
50
  'Environment :: Console',
48
51
  'Intended Audience :: End Users/Desktop',
49
52
  'License :: OSI Approved :: MIT License',
@@ -3,8 +3,6 @@
3
3
  # while inotifywait --recursive -e close_write . ; do python entry.py tests/multi2/; done
4
4
  # above for linux
5
5
 
6
- TRACKSFN = 'tracks.txt'
7
- SILENT_TRACK_TOKENS = '-0n'
8
6
 
9
7
  av_file_extensions = \
10
8
  """webm mkv flv flv vob ogv ogg drc gif gifv mng avi MTS M2TS TS mov qt
@@ -18,8 +16,10 @@ M4V SVI 3GP 3G2 MXF ROQ NSV FLV F4V F4P F4A F4B 3GP AA AAC AAX ACT AIFF ALAC
18
16
  AMR APE AU AWB DSS DVF FLAC GSM IKLAX IVS M4A M4B M4P MMF MP3 MPC MSV NMF
19
17
  OGG OGA MOGG OPUS RA RM RAW RF64 SLN TTA VOC VOX WAV WMA WV WEBM 8SVX CDA MOV AVI BWF""".split()
20
18
 
19
+ audio_ext = 'aiff wav mp3'.split()
20
+
21
21
  from dataclasses import dataclass
22
- import ffmpeg, os, sys
22
+ import ffmpeg, os, sys, shutil
23
23
  from os import listdir
24
24
  from os.path import isfile, join, isdir
25
25
  from collections import namedtuple
@@ -28,7 +28,7 @@ from pprint import pformat
28
28
  # from collections import defaultdict
29
29
  from loguru import logger
30
30
  # import pathlib, os.path
31
- import sox, tempfile
31
+ import sox, tempfile, platformdirs, filecmp
32
32
  # from functools import reduce
33
33
  from rich import print
34
34
  from itertools import groupby
@@ -36,8 +36,14 @@ from itertools import groupby
36
36
  # import distance
37
37
  try:
38
38
  from . import multi2polywav
39
+ from . import mamsync
40
+ from . import mamconf
41
+ from . import yaltc
39
42
  except:
40
43
  import multi2polywav
44
+ import mamsync
45
+ import mamconf
46
+ import yaltc
41
47
 
42
48
  MCCDIR = 'SyncedMulticamClips'
43
49
  SYNCEDFOLDER = 'SyncedMedia'
@@ -58,10 +64,9 @@ def print_grby(grby):
58
64
  print('\ngrouped by %s:'%key)
59
65
  for e in keylist:
60
66
  print(' ', e)
61
-
62
67
  @dataclass
63
68
  class Tracks:
64
- # track numbers start at 1 for first track (as needed by sox)
69
+ # track numbers start at 1 for first track (as needed by sox,1 Based Index)
65
70
  ttc: int # track number of TicTacCode signal
66
71
  unused: list # of unused tracks
67
72
  stereomics: list # of stereo mics track tuples (Lchan#, Rchan#)
@@ -69,7 +74,9 @@ class Tracks:
69
74
  others: list #of all other tags: (tag, track#) tuples
70
75
  rawtrx: list # list of strings read from file
71
76
  error_msg: str # 'None' if none
72
- lag_values: list # list of lag in ms, entry is None if not specified.
77
+ lag_values: list # list of lags in ms, entry is None if not specified.
78
+ # UTC_timestamp: str # to the nearest minute ISO 8601 date and time e.g.: "2007-04-05T14:30Z"
79
+
73
80
 
74
81
  @dataclass
75
82
  class Device:
@@ -210,10 +217,8 @@ class Scanner:
210
217
  top_directory : string
211
218
  String of path where to start searching for media files.
212
219
 
213
- # top_dir_has_multicam : bool
214
- # If top dir is folder structures AND more than on cam
215
-
216
- found_media_files: list of Media objects
220
+ found_media_files: list of dataclass Media instances encapsulating
221
+ the pathlibPath and the device (of Device dataclass).
217
222
  """
218
223
 
219
224
  def __init__(
@@ -244,16 +249,7 @@ class Scanner:
244
249
  CAMs = [d for d in devices if d.dev_type == 'CAM']
245
250
  return len(set(CAMs))
246
251
 
247
- def get_device_in_path(self, a_media):
248
- """
249
- Return a device name taken from folders in path of a_media if and only
250
- if all media files below it are from the same device. Goes up max two
251
- levels from media; e.g., in /FOLDER2/FOLDER1/DSC00234.MOV will test up
252
- to FOLDER2 as candidate.
253
- """
254
-
255
-
256
- def scan_media_and_build_devices_UID(self, recursive=True):
252
+ def scan_media_and_build_devices_UID(self, synced_root = None):
257
253
  """
258
254
  Scans Scanner.top_directory recursively for files with known audio-video
259
255
  extensions. For each file found, a device fingerprint is obtained from
@@ -269,20 +265,36 @@ class Scanner:
269
265
  Sets Scanner.input_structure = 'loose'|'ordered'
270
266
 
271
267
  """
272
- files = Path(self.top_directory).rglob('*.*')
273
- def _last4letters(part):
274
- return
275
-
276
- paths = [
277
- p
278
- for p in files
279
- if p.suffix[1:] in av_file_extensions
280
- and SYNCEDFOLDER not in p.parts # SyncedMedia
281
- and MCCDIR not in p.parts # SyncedMulticamClips
282
- and '_ISO' not in [part[-4:] for part in p.parts] # exclude ISO wav files
283
- ]
284
- logger.debug('found media files %s'%paths)
285
- parents = [p.parent for p in paths]
268
+ logger.debug(f'on entry synced_root: {synced_root}')
269
+ if synced_root != None: # mam mode
270
+ p = Path(platformdirs.user_data_dir('mamsync', 'plutz'))/mamconf.LOG_FILE
271
+ with open(p, 'r') as fh:
272
+ done = set(fh.read().split()) # sets of strings of abs path
273
+ logger.debug(f'done clips: {pformat(done)}')
274
+ files = Path(self.top_directory).rglob('*')
275
+ clip_paths = []
276
+ some_done = False
277
+ for raw_path in files:
278
+ if raw_path.suffix[1:] in av_file_extensions:
279
+ if SYNCEDFOLDER not in raw_path.parts: # SyncedMedia
280
+ if MCCDIR not in raw_path.parts: # SyncedMulticamClips
281
+ if '_ISO' not in [part[-4:] for part in raw_path.parts]: # exclude ISO wav files
282
+ if synced_root != None and str(raw_path) in done:
283
+ logger.debug(f'{raw_path} done')
284
+ some_done = True
285
+ continue
286
+ else:
287
+ clip_paths.append(raw_path)
288
+ if some_done:
289
+ print('Somme media files were already synced...')
290
+ logger.debug('found media files %s'%clip_paths)
291
+ if len(clip_paths) == 0:
292
+ print('No media found, bye.')
293
+ sys.exit(0)
294
+ # self.found_media_files = []
295
+ # self.input_structure = 'loose'
296
+ # return
297
+ parents = [p.parent for p in clip_paths]
286
298
  logger.debug('found parents %s'%pformat(parents))
287
299
  # True if all elements are identical
288
300
  AV_files_have_same_parent = parents.count(parents[0]) == len(parents)
@@ -296,7 +308,7 @@ class Scanner:
296
308
  # check later if inside each folder, media have same device
297
309
  # for now, we'll guess structure is 'ordered'
298
310
  self.input_structure = 'ordered'
299
- for p in paths:
311
+ for p in clip_paths:
300
312
  new_media = media_at_path(self.input_structure, p) # dev UID set here
301
313
  self.found_media_files.append(new_media)
302
314
  # for non UIDed try building UID from filenam
@@ -338,17 +350,19 @@ class Scanner:
338
350
  logger.debug('Scanner.found_media_files = %s'%pformat(self.found_media_files))
339
351
  if self.input_structure == 'ordered':
340
352
  self._confirm_folders_have_same_device()
341
- # self._use_folder_as_device_name()
342
- devices = set([m.device for m in self.found_media_files])
343
- audio_devices = [d for d in devices if d.dev_type == 'REC']
344
- for recorder in audio_devices:
345
- # process tracks.txt for audio recorders
346
- recorder.tracks = self._get_tracks_from_file(recorder)
347
- # logging only:
348
- if recorder.tracks:
349
- if not all([lv == None for lv in recorder.tracks.lag_values]):
350
- logger.debug('%s has lag_values %s'%(
351
- recorder.name, recorder.tracks.lag_values))
353
+ # [TODO] move this where Recordings have been TCed for any tracks timestamps
354
+ # <begin>
355
+ # devices = set([m.device for m in self.found_media_files])
356
+ # audio_devices = [d for d in devices if d.dev_type == 'REC']
357
+ # for recorder in audio_devices:
358
+ # # process tracks.txt for audio recorders
359
+ # recorder.tracks = self._get_tracks_from_file(recorder)
360
+ # # logging only:
361
+ # if recorder.tracks:
362
+ # if not all([lv == None for lv in recorder.tracks.lag_values]):
363
+ # logger.debug('%s has lag_values %s'%(
364
+ # recorder.name, recorder.tracks.lag_values))
365
+ # </end>
352
366
  # check if device is in fact two parents up (and parent = ROLLxx):
353
367
  # Group media by folder 2up and verify all media for each
354
368
  # group have same device.
@@ -375,11 +389,10 @@ class Scanner:
375
389
  for m in self.found_media_files:
376
390
  if m.device.UID == UID:
377
391
  m.device.name = name
378
- # logger.debug('renamed device media: %s'%pformat(self.found_media_files))
379
392
  no_name_devices = [m.device for m in self.found_media_files
380
393
  if not m.device.name]
381
394
  # possible if self.input_structure == 'loose'
382
- def _try_name_from_metadata(media):
395
+ def _try_name_from_metadata(media): # unused for now
383
396
  # search model and make from fprobe
384
397
  file = Path(media.path)
385
398
  logger.debug('trying to find maker model for %s'%file)
@@ -395,7 +408,6 @@ class Scanner:
395
408
  # could reside in ['format','tags','com.apple.quicktime.model'],
396
409
  # or ['format','tags','model'],
397
410
  # or ['streams'][0]['tags']['vendor_id']) :-(
398
-
399
411
  for anon_dev in no_name_devices:
400
412
  medias = self.get_media_for_device(anon_dev)
401
413
  guess_name = _try_name_from_files(medias)
@@ -409,50 +421,6 @@ class Scanner:
409
421
  if not any(dev_is_REC): # no audio recordings!
410
422
  print('\rNo audio recording found, nothing to sync, bye.')
411
423
  sys.exit(0)
412
- # print('devices 312 %s'%set([m.device for m in self.found_media_files]))
413
-
414
- def _get_tracks_from_file(self, device) -> Tracks:
415
- """
416
- Look for eventual track names in TRACKSFN file, stored inside the
417
- recorder folder alongside the audio files. If there, returns a Tracks
418
- object, if not returns None.
419
- """
420
- source_audio_folder = device.folder
421
- tracks_file = source_audio_folder/TRACKSFN
422
- track_names = False
423
- a_recording = [m for m in self.found_media_files
424
- if m.device == device][0]
425
- logger.debug('a_recording for device %s : %s'%(device, a_recording))
426
- nchan = sox.file_info.channels(str(a_recording.path))
427
- if os.path.isfile(tracks_file):
428
- logger.debug('found file: %s'%(TRACKSFN))
429
- tracks = self._parse_track_values(tracks_file)
430
- if tracks.error_msg:
431
- print('\nError parsing [gold1]%s[/gold1] file: %s, quitting.\n'%
432
- (tracks_file, tracks.error_msg))
433
- sys.exit(1)
434
- logger.debug('parsed tracks %s'%tracks)
435
- ntracks = 2*len(tracks.stereomics)
436
- ntracks += len(tracks.mix)
437
- ntracks += len(tracks.unused)
438
- ntracks += len(tracks.others)
439
- ntracks += 1 # for ttc track
440
- logger.debug(' n chan: %i n tracks file: %i'%(nchan, ntracks))
441
- if ntracks != nchan:
442
- print('\nError parsing %s content'%tracks_file)
443
- print('incoherent number of tracks, %i vs %i quitting\n'%
444
- (nchan, ntracks))
445
- sys.exit(1)
446
- err_msg = tracks.error_msg
447
- if err_msg != None:
448
- print('\nError, quitting: in file %s, %s'%(tracks_file, err_msg))
449
- raise Exception
450
- else:
451
- logger.debug('tracks object%s'%tracks)
452
- return tracks
453
- else:
454
- logger.debug('no tracks.txt file found')
455
- return None
456
424
 
457
425
  def _confirm_folders_have_same_device(self):
458
426
  """
@@ -590,184 +558,6 @@ class Scanner:
590
558
  logger.debug('n_CAM_folder %i'%n_CAM_folder)
591
559
  return
592
560
 
593
- def _parse_track_values(self, tracks_file) -> Tracks:
594
- """
595
- read track names for naming separated ISOs
596
- from tracks_file.
597
-
598
- tokens looked for: mix; mix L; mix R; 0 and TC
599
-
600
- repeating "mic*" pattern signals a stereo track
601
- and entries will correspondingly panned into
602
- a stero mix named mixL.wav and mixL.wav
603
-
604
- mic L # spaces are ignored |
605
- mic R | stereo pair
606
- micB L
607
- micB R
608
-
609
- Returns: a Tracks instance:
610
- # track numbers start at 1 for first track (as needed by sox)
611
- ttc: int # track number of TicTacCode signal
612
- unused: list # of unused tracks
613
- stereomics: list # of stereo mics track tuples (Lchan#, Rchan#)
614
- mix: list # of mixed tracks, if a pair, order is L than R
615
- others: list #of all other tags: (tag, track#) tuples
616
- rawtrx: list # list of strings read from file
617
- error_msg: str # 'None' if none
618
- e.g.: Tracks( ttc=2,
619
- unused=[],
620
- stereomics=[('mic', (4, 3)), ('mic2', (6, 5))],
621
- mix=[], others=[('clics', 1)],
622
- rawtrx=['clics', 'TC', 'micL', 'micR', 'mic2L;1000', 'mic2R;1000', 'mixL', 'mixR'],
623
- error_msg=None, lag_values=[None, None, None, None, '1000', '1000', None, None])
624
- """
625
- def _WOspace(chaine):
626
- ch = [c for c in chaine if c != ' ']
627
- return ''.join(ch)
628
- # def _WO_LR(chaine):
629
- # ch = [c for c in chaine if c not in 'LR']
630
- # return ''.join(ch)
631
- # def _seemsStereoMic(tag):
632
- # # is tag likely a stereo pair tag?
633
- # # should starts with 'mic' and ends with 'l' or 'r'
634
- # return tag[1:4]=='mic' and tag[0] in 'lr'
635
- file=open(tracks_file,"r")
636
- whole_txt = file.read()
637
- logger.debug('file %s all_lines:\n%s'%(tracks_file, whole_txt))
638
- tracks_lines_wspaces = [l.split('#')[0] for l in whole_txt.splitlines()
639
- if len(l) > 0 ]
640
- tracks_lines = [_WOspace(l) for l in tracks_lines_wspaces if len(l) > 0 ]
641
- rawtrx = [l for l in tracks_lines_wspaces if len(l) > 0 ]
642
- # add index with tuples, starting at 1
643
- logger.debug('tracks_lines whole: %s'%tracks_lines)
644
- def _detach_lag_value(line):
645
- # look for ";number" ending any line, returns a two-list
646
- splt = line.split(';')
647
- if len(splt) == 1:
648
- splt += [None]
649
- if len(splt) != 2:
650
- # error
651
- print('\nText error in %s, line %s has too many ";"'%(
652
- tracks_file, line))
653
- return splt
654
- tracks_lines, lag_values = zip(*[_detach_lag_value(l) for l
655
- in tracks_lines])
656
- lag_values = [e for e in lag_values] # from tuple to list
657
- # logger.debug('tracks_lines WO lag: %s'%tracks_lines)
658
- tracks_lines = [l.lower() for l in tracks_lines]
659
- logger.debug('tracks_lines lower case: %s'%tracks_lines)
660
- # print(lag_values)
661
- logger.debug('lag_values: %s'%lag_values)
662
- tagsWOl_r = [e[:-1] for e in tracks_lines] # skip last letter
663
- logger.debug('tags WO LR letter %s'%tagsWOl_r)
664
- # find idx of start of pairs
665
- # ['clics', 'TC', 'micL', 'micR', 'mic2L', 'mic2R', 'mixL', 'mixR']
666
- def _micOrmix(a,b):
667
- # test if same and mic mic or mix mix
668
- if len(a) == 0:
669
- return False
670
- return (a == b) and (a in 'micmix')
671
- pair_idx_start =[i for i, same in enumerate([_micOrmix(a,b) for a,b
672
- in zip(tagsWOl_r,tagsWOl_r[1:])]) if same]
673
- logger.debug('pair_idx_start %s'%pair_idx_start)
674
- def LR_OK(idx):
675
- # in tracks_lines, check if idx ends a LR pair
676
- # delays, if any, have been removed
677
- a = tracks_lines[idx][-1]
678
- b = tracks_lines[idx+1][-1]
679
- return a+b in ['lr', 'rl']
680
- LR_OKs = [LR_OK(p) for p in pair_idx_start]
681
- logger.debug('LR_OKs %s'%LR_OKs)
682
- if not all(LR_OKs):
683
- print('\nError in %s'%tracks_file)
684
- print('Some tracks are paired but not L and R: %s'%rawtrx)
685
- print('quitting...')
686
- quit()
687
- complete_pairs_idx = pair_idx_start + [i + 1 for i in pair_idx_start]
688
- singles = set(range(len(tracks_lines))).difference(complete_pairs_idx)
689
- logger.debug('complete_pairs_idx %s'%complete_pairs_idx)
690
- logger.debug('singles %s'%singles)
691
- singles_tag = [tracks_lines[i] for i in singles]
692
- logger.debug('singles_tag %s'%singles_tag)
693
- n_tc_token = sum([t == 'tc' for t in singles_tag])
694
- logger.debug('n tc tags %s'%n_tc_token)
695
- if n_tc_token == 0:
696
- print('\nError in %s'%tracks_file)
697
- print('with %s'%rawtrx)
698
- print('no TC track found, quitting...')
699
- quit()
700
- if n_tc_token > 1:
701
- print('\nError in %s'%tracks_file)
702
- print('with %s'%rawtrx)
703
- print('more than one TC track, quitting...')
704
- quit()
705
- output_tracks = Tracks(None,[],[],[],[],rawtrx,None,[])
706
- output_tracks.ttc = tracks_lines.index('tc') + 1 # 1st = 1
707
- logger.debug('ttc_chan %s'%output_tracks.ttc)
708
- zeroed = [i+1 for i, t in enumerate(tracks_lines) if t == '0']
709
- logger.debug('zeroed %s'%zeroed)
710
- output_tracks.unused = zeroed
711
- output_tracks.others = [(st, tracks_lines.index(st)+1) for st
712
- in singles_tag if st not
713
- in ['tc', 'monomix', '0']]
714
- logger.debug('output_tracks.others %s'%output_tracks.others)
715
- # check for monomix
716
- if 'monomix' in tracks_lines:
717
- output_tracks.mix = [tracks_lines.index('monomix')+1]
718
- else:
719
- output_tracks.mix = []
720
- # check for stereo mix
721
- def _findLR(i_first):
722
- # returns L R indexes (+1 for sox non zero based indexing)
723
- i_2nd = i_first + 1
724
- a = tracks_lines[i_first][-1] # l|r at end
725
- b = tracks_lines[i_2nd][-1] # l|r at end
726
- if a == 'l':
727
- if b == 'r':
728
- # sequence is mixL mixR
729
- return i_first+1, i_2nd+1
730
- else:
731
- print('\nError in %s'%tracks_file)
732
- print('with %s'%rawtrx)
733
- print('can not find stereo mix')
734
- quit()
735
- elif a == 'r':
736
- if b == 'l':
737
- # sequence is mixR mixL
738
- return i_2nd+1, i_first+1
739
- else:
740
- print('\nError in %s'%tracks_file)
741
- print('with %s'%rawtrx)
742
- print('can not find stereo mix')
743
- quit()
744
- logger.debug('for now, output_tracks.mix %s'%output_tracks.mix)
745
- mix_pair = [p for p in pair_idx_start if tracks_lines[p][1:] == 'mix']
746
- if len(mix_pair) == 1:
747
- # one stereo mix, remove it from other pairs
748
- i = mix_pair[0]
749
- LR_pair = _findLR(i)
750
- logger.debug('LR_pair %s'%str(LR_pair))
751
- pair_idx_start.remove(i)
752
- # consistency check
753
- if output_tracks.mix != []:
754
- # already found a mono mix above!
755
- print('\nError in %s'%tracks_file)
756
- print('with %s'%rawtrx)
757
- print('found a mono mix AND a stereo mix')
758
- quit()
759
- output_tracks.mix = LR_pair
760
- logger.debug('finally, output_tracks.mix %s'%str(output_tracks.mix))
761
- logger.debug('remaining pairs %s'%pair_idx_start)
762
- # those are stereo pairs
763
- stereo_pairs = []
764
- for first_in_pair in pair_idx_start:
765
- suffix = tracks_lines[first_in_pair][:-1]
766
- stereo_pairs.append((suffix, _findLR(first_in_pair)))
767
- logger.debug('stereo_pairs %s'%stereo_pairs)
768
- output_tracks.stereomics = stereo_pairs
769
- logger.debug('finished: %s'%output_tracks)
770
- return output_tracks
771
561
 
772
562
 
773
563