albe
February 26, 2023, 5:19pm
1
Hi,
I am new with Nyquist.
I am looking of method to convert time in seconds (float format) into human readable format “00:00:27.765300”.
Is there (in Nyquist) a method to do it easy or do I need to write a function where I will divide by 60 and count everything by myself?
steve
February 26, 2023, 5:53pm
2
I may be able to save you some time, I’m sure I wrote a function for that somewhere…
Ah yes, here it is:
(defun format-time(sec &optional (n 3))
;; Return time formatted to hh:mm:ss + n decimal places.
(unless (and (numberp sec) (numberp n))
(error "format-time arguments must be numbers."))
(flet ((pad (x) (if (< x 10) (format nil "0~a" x) x)))
(let* ((hh (truncate (/ sec 3600)))
(mm (truncate (/ sec 60)))
(ss (- sec (* mm 60)))
(old-format *float-format*)
rslt)
(setf mm (- mm (* hh 60)))
(setf *float-format* (format nil "%.~af" n))
(setf rslt (format nil "~a:~a:~a" hh (pad mm) (pad ss)))
(setf *float-format* old-format)
rslt)))
albe
February 27, 2023, 5:20pm
3
That’s great! Thanks a lot Steve, it works perfect.
steve
February 27, 2023, 5:23pm
4
Did you manage to work out how to set the number of decimal places for fractional seconds?
albe
February 27, 2023, 9:09pm
5
Yes, it is easy, I use
(format-time (first data-1) 4)
but default=3 is good for my use case. I have spent a couple of hours to understand Lisp’s syntax
Based on “Export labels” script, I wrote a version for my use case. I have found comma2dot function on this forum that I transformed to dot2comma and use in my script.
(defun dot2comma (txt)
;; Replace dot with comma if only one dot
(let ((c (string-search "." txt)))
(if c
(let ((rest (subseq txt (1+ c))))
(if (string-search "." rest)
txt
(strcat (subseq txt 0 c) "," rest)))
txt)))
(defun format-time(sec &optional (n 3))
;; Return time formatted to hh:mm:ss + n decimal places.
(unless (and (numberp sec) (numberp n))
(error "format-time arguments must be numbers."))
(flet ((pad (x) (if (< x 10) (format nil "0~a" x) x)))
(let* ((hh (truncate (/ sec 3600)))
(mm (truncate (/ sec 60)))
(ss (- sec (* mm 60)))
(old-format *float-format*)
rslt)
(setf mm (- mm (* hh 60)))
(setf *float-format* (format nil "%.~af" n))
(setf rslt (format nil "~a:~a:~a" hh (pad mm) (pad ss)))
(setf *float-format* old-format)
(setf rslt-comma (dot2comma rslt))
rslt-comma)))
(defun format-data (tracknum trackname data-1 data-2)
;; Return formatted label data for one label.
(setf trk-time-data
(format nil "~a~a~a~a"
tracknum
trackname
(if (= hastimes 0) ;data-1 times
(format nil "~a~a~a" (format-time (first data-1)) sep (format-time (second data-1)))
"")
(if (= hastimes 0) ;data-2 times
(format nil "~a~a~a~a" sep (format-time (first data-2)) sep (format-time (second data-2)))
"")))
(if (= haslabel 0)
(strcat trk-time-data (format nil "~a~s~a~s~%" sep (third data-1) sep (third data-2)))
(strcat trk-time-data "~%"))
)
(defun export-labels ()
;; Main function.
(let ((info (aud-get-info "labels"))
(tracknames (track-names))
(trackname "")
(tracknum "")
label-string
(output ""))
(dolist (trk info)
(when (= hasnumber 0)
(setf tracknum (format nil "~a~a" (1+ (first trk)) sep)))
(when (= hasname 0)
(setf trackname (format nil "~s~a" (nth (first trk) tracknames) sep)))
(setq trklabels (second trk))
(if (> (rem (length trklabels) 2) 0)
(return-from export-labels (format nil "Error: Not even number of labels.")))
(dotimes (i (/ (length trklabels) 2))
(setq indx (* i 2))
(setq label-1 (nth indx trklabels))
(setq label-2 (nth (1+ indx) trklabels))
(setf label-string (format-data tracknum trackname label-1 label-2))
(setf output (strcat output label-string))))
(write output filename)))
Without track name/num and label strings the script prints times in format (two labels times in one line):
0:01:04,850 0:01:07,250 0:01:15,910 0:01:19,900
0:01:25,480 0:01:27,750 0:01:32,620 0:01:34,700
Thanks of that I can export labels times from Audacity and import to Excel in a few mouse clicks.
albe
February 28, 2023, 9:23am
6
Digging in forum about decimal separator I found that audacity developers decided to use always dot as a decimal separator. So I want to replace dot2comma function with more “generic” version but I got error in “if”:
error: bad argument type - #\,
Function: #<Subr-=: #000002BC90D90E88>
Arguments:
#\,
"."
Function: #<FSubr-IF: #000002BC90D8FAA8>
Arguments:
(= *DECIMAL-SEPARATOR* ".")
(RETURN TXT)
Function: #<Closure-STR-CONVERT-DECIMAL-SEP: #000002BC90CAB898>
function is
(defun str-convert-decimal-sep (txt)
;; Replace dot with decimal separator if only one dot
(if (= *decimal-separator* ".") (return txt))
(let ((c (string-search "." txt)))
(if c
(let ((rest (subseq txt (1+ c))))
(if (string-search "." rest)
txt
(strcat (subseq txt 0 c) *decimal-separator* rest)))
txt)))
Why decimal-separator is “#,” or “#.” and how to fix that error?
I will be grateful for your help.
steve
February 28, 2023, 11:06am
7
In XLISP / Nyquist, “characters” and “strings” are different data types.
(See: XLISP: An Object-oriented Lisp )
decimal-separator is type “char” (a character).
“.” is a string (type “string”).
The equality operator “=” is for comparing numeric types.
For comparing other types, you need to use the appropriate comparison function: string=, string-equal, char=, char-equal.
You can’t compare apples to oranges, or strings to chars.
One way to compare a string and a char is to compare the string representation of the char:
(print (string= (format nil "~a" *decimal-separator*) ".")) ; True
or you could compare decimal-separator with the character #\.
(print (char= *decimal-separator* #\.)) ; True
The functions mentioned in this post are all documented in the XLISP manual: XLISP Language Reference
(DECIMAL-SEPARATOR is not standard to Nyquist or XLISP, but is specific to Audacity’s version of Nyquist.)
albe
February 28, 2023, 7:16pm
8
thank you for in-depth explanation.