;;; -*- syntax: common-lisp; package: clm; base: 10; mode: lisp -*-
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; File:             trim-silence.ins
;;;
;;; Purpose:          Writes a new sound file with the silence at the beginning
;;;                   of the input file cut off. Silence threshold is
;;;                   configurable. 
;;;
;;; Author:           Michael Edwards - m@michael-edwards.org
;;;
;;; $$ Last modified: 11:42:56 Tue Jul  5 2011 BST
;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


(defmacro db2amp (db)
  `(expt 10.0 (/ ,db 20)))


(defmacro end-silence ()
  `(loop for i from 0 below num-samps do
        (setf inAval (readin inA))
        (when stereo-input
          (setf inBval (readin inB)))
        (unless (and (< inAval minamp)
                     (< inBval minamp))
          (return i))))

;;; no final / on dirs
(defun dir-trim-silence (indir outdir &key (minDB -100) (extension ".wav"))
  (let ((files (directory (format nil "~a/*~a" indir extension))))
    (setf files (loop for f in files collect (namestring f)))
    (loop for f in files
         for outf = (format nil "~a/~a~a" outdir (pathname-name f) extension)
       do
         (with-sound (:output outf :channels (sound-chans f)
                              :statistics nil :play nil :srate (sound-srate f))
           (trim-silence f minDB)))))

(definstrument get-samples
    (file start stop &optional (channel 0))
  (let* ((nd (floor (* *srate* (- stop start))))
         (samples (make-double-float-array nd))
         (rd (make-readin :file file :channel channel 
                          :start (floor (* *srate* start)))))
    (run*
     (samples)
     (loop for i below nd do
          (setf (aref samples i) (readin rd))))
    samples))

(definstrument trim-silence
    (file &optional (minDB -100))
  (let* ((stereo-input (= 2 (sound-chans file)))
         (inA (make-readin :file file))
         (inB (when stereo-input (make-readin :file file :channel 1)))
         (num-samps (sound-frames file))
         (inAval 0.0)
         (inBval 0.0)
         (start 0)
         (stop 0)
         (minamp (db2amp minDB))
         (num-samps (sound-frames file)))
    (run* (start stop)
     (progn
       (setf start (end-silence)
             (mus-location inA) (1- num-samps)
             (mus-increment inA) -1)
       (when stereo-input
         (setf (mus-location inB) (1- num-samps)
               (mus-increment inB) -1))
       (setf stop (- num-samps (end-silence))
             (mus-location inA) start
             (mus-increment inA) 1)
       (when stereo-input
         (setf (mus-location inB) start
               (mus-increment inB) 1))
       (unless (and start stop)
         (error "couldn't detect end/start of silence: start = ~a stop = ~a"
                start stop))
       (loop for i from start to stop and j from 0 do
            (outa j (readin inA))
            (when stereo-input
              (outb j (readin inB))))))
     (format t "~&Trimmed silence from 0 to ~a (~,3f secs) ~
                ~%and from ~a to ~a (~,3f secs)~%" 
             start (/ start *srate*) stop num-samps 
             (/ (- num-samps stop) *srate*))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
#|

(dir-trim-silence "/projects/mpc/snd" "/projects/mpc/snd/trim" -90)

(with-sound (:srate 48000)
  (trim-silence "/projects/mpc/snd/big-nails-spinning-barns.wav"))

(get-samples "/projects/mpc/snd/platter-b4-fall-princey-window.wav" 39.8 41.2)

|#
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; EOF trim-silence.ins
