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.
- {tictacsync-0.99a0/tictacsync.egg-info → tictacsync-1.4.3b0}/PKG-INFO +5 -5
- {tictacsync-0.99a0 → tictacsync-1.4.3b0}/README.md +2 -2
- {tictacsync-0.99a0 → tictacsync-1.4.3b0}/setup.py +7 -4
- {tictacsync-0.99a0 → tictacsync-1.4.3b0}/tictacsync/device_scanner.py +62 -272
- {tictacsync-0.99a0 → tictacsync-1.4.3b0}/tictacsync/entry.py +57 -166
- tictacsync-1.4.3b0/tictacsync/mamconf.py +157 -0
- tictacsync-1.4.3b0/tictacsync/mamdav.py +642 -0
- tictacsync-1.4.3b0/tictacsync/mamreap.py +481 -0
- tictacsync-1.4.3b0/tictacsync/mamsync.py +343 -0
- {tictacsync-0.99a0 → tictacsync-1.4.3b0}/tictacsync/multi2polywav.py +4 -3
- {tictacsync-0.99a0 → tictacsync-1.4.3b0}/tictacsync/timeline.py +153 -96
- {tictacsync-0.99a0 → tictacsync-1.4.3b0}/tictacsync/yaltc.py +359 -31
- {tictacsync-0.99a0 → tictacsync-1.4.3b0/tictacsync.egg-info}/PKG-INFO +5 -5
- {tictacsync-0.99a0 → tictacsync-1.4.3b0}/tictacsync.egg-info/SOURCES.txt +4 -2
- tictacsync-1.4.3b0/tictacsync.egg-info/entry_points.txt +7 -0
- tictacsync-0.99a0/tictacsync/remergemix.py +0 -259
- tictacsync-0.99a0/tictacsync/remrgmx.py +0 -116
- tictacsync-0.99a0/tictacsync.egg-info/entry_points.txt +0 -4
- {tictacsync-0.99a0 → tictacsync-1.4.3b0}/LICENSE +0 -0
- {tictacsync-0.99a0 → tictacsync-1.4.3b0}/setup.cfg +0 -0
- {tictacsync-0.99a0 → tictacsync-1.4.3b0}/tictacsync/__init__.py +0 -0
- {tictacsync-0.99a0 → tictacsync-1.4.3b0}/tictacsync.egg-info/dependency_links.txt +0 -0
- {tictacsync-0.99a0 → tictacsync-1.4.3b0}/tictacsync.egg-info/not-zip-safe +0 -0
- {tictacsync-0.99a0 → tictacsync-1.4.3b0}/tictacsync.egg-info/requires.txt +0 -0
- {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:
|
|
4
|
-
Summary:
|
|
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 ::
|
|
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
|
|
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/
|
|
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
|
|
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/
|
|
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
|
-
'
|
|
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 = '
|
|
37
|
-
description = "
|
|
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 ::
|
|
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
|
|
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
|
-
|
|
214
|
-
|
|
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
|
|
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
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
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
|
|
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
|
-
#
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
for
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
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
|
|