#!python
import argparse
import os
import loristrck
from argparse import RawTextHelpFormatter
import sys


def msg(s):
    print(s, file=sys.stderr)


description = """
Analyzes a soundfile or loads a previously analyzed sdif file, packing the
partials into a number of simultaneous streams, in order to be resynthesized.

The result is a matrix of at most `maxoscil`*3+1 number of columns, the height of the
matrix being dependent on the length of the sound and the sampling interval used.

The matrix is saved as a .mtx file, which is a float32 or float64 wav file. It
includes a header with the following data:

    Header: [dataOffset=5, numRows, numColumns]

Then follow the data, which is a flat matrix where each row has the following 
format:

    time, freq0, amp0, bandwidth0, freq1, amp1, bandwidth1, ...

Where: 
    * time is the time of this row.
    * freqx, ampx, bandwidthx: the frequency, amplitude and bandwidth of partial x
    
The time of each row is regular, fitting in a grid t = t0 + row * dt

This is mostly thought to be used in tandem with csound's beadsynt opcode,
which performs band-enhanced-additive-synthesis in realtime

NB: if you need more control over the analysis step, first analyze the soundfile
    to obtain a sdif file, then use this utility to pack a matrix
    (see loristrck_analyze)
"""

parser = argparse.ArgumentParser(description=description,
                                 formatter_class=RawTextHelpFormatter)

parser.add_argument("-r", "--resolution", default=40, type=float,
                    help="Analysis resolution, in Hz (only for analysis)")

parser.add_argument("-w", "--winsize", default=None,
                    help="Window size (in Hz) (only valid if doing analysis)")

parser.add_argument("-o", "--outfile", default=None,
                    help="Name of the resulting .mtx file")

parser.add_argument("--minbps", default=2, type=int,
                    help="Min. number of breakpoints for a partial to be packed")

parser.add_argument("--minamp", default=-90, type=float)

parser.add_argument("--mindur", default=0, type=float, 
                    help="Min. duration of a partial to be included")

parser.add_argument("--maxoscil", default=100, type=int,
                    help="Max. number of active oscilators (0=infinite). Limit this in order to limit CPU usage")

parser.add_argument("--maxtracks", default=0, type=int,
                    help="Max. number of tracks (leave as 0 if table size/memory is not a problem)")

parser.add_argument("--dt", default=0, type=float,
                    help="A sampling interval, in seconds. "
                    "As a rule of thumb, dt should be somewhat smaller than ksmps/sr")

parser.add_argument("infile",
                    help="An infile can be a soundfile or a .sdif file")

args = parser.parse_args()

infile = args.infile
ext = os.path.splitext(infile)[1].lower()

if ext == '.sdif':
    partials, labels = loristrck.read_sdif(infile)
else:
    samples, sr = loristrck.util.sndreadmono(infile)
    winsize = float(args.winsize) if args.winsize else -1
    partials = loristrck.analyze(samples,
                                 sr=sr,
                                 resolution=args.resolution,
                                 windowsize=winsize)

if args.outfile is None:
    outfile = f"{os.path.splitext(infile)[0]}.mtx"
else:
    outfile = args.outfile
    ext = os.path.splitext(outfile)[1]
    if  ext != '.mtx':
        msg(f"Warning: The recommended extension is .mtx, got {ext}")

if args.dt > 0:
    dt = args.dt
else:
    dt = round(64 / 44100 * 0.9, 4)
    msg(f"Using dt = {dt*1000:.2f} ms")
    
selected_partials, rest = loristrck.util.select(partials,
                                                mindur=args.mindur,
                                                minamp=args.minamp,
                                                minbps=args.minbps)

loristrck.util.partials_save_matrix(selected_partials,
                                    dt=dt,
                                    outfile=outfile,
                                    maxactive=args.maxoscil,
                                    maxtracks=args.maxtracks)

print(outfile)
