home *** CD-ROM | disk | FTP | other *** search
/ Mac Easy 2013 March / MAC_easy_03_2013.iso / Multimedia / audacity-macosx-ub-2.0.3.dmg / Audacity / plug-ins / sample-data-export.ny < prev    next >
Encoding:
Audacity Nyquits plug-in  |  2013-01-18  |  18.5 KB  |  588 lines

  1. ;nyquist plug-in
  2. ;version 3
  3. ;type analyze
  4. ;name "Sample Data Export..."
  5. ;action "Analyzing..."
  6. ;categories "http://lv2plug.in/ns/lv2core#AnalyserPlugin"
  7. ;info "by Steve Daulton. Released under GPL v2." 
  8.  
  9. ;; sample-data-export.ny by Steve Daulton June 2012.
  10. ;; Updated July 16 2012.
  11. ;; Released under terms of the GNU General Public License version 2:
  12. ;; http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  13. ;;
  14. ;; For information about writing and modifying Nyquist plug-ins:
  15. ;; http://wiki.audacityteam.org/wiki/Nyquist_Plug-ins_Reference
  16.  
  17. ;control number "Limit output to first" string "samples" "100" 
  18. ;control units "Measurement scale" choice "dB,Linear" 0 
  19. ;control fileformat "File data format" choice "Sample List (txt),Indexed List (txt),Time Indexed (txt),Data (csv),Web Page (html)" 0 
  20. ;control header "Include header information" choice "None,Minimal,Standard,All" 2 
  21. ;control optext "Optional header text" string ""  
  22. ;control chan "Channel layout for stereo" choice "L-R on Same Line,Alternate Lines,L Channel First" 0   
  23. ;control messages "Show messages" choice "Yes,Errors Only,None" 0 
  24. ;control filename "File name" string "" "sample-data"
  25. ;control path "Output folder" string "" "Home directory"
  26. ;control owrite "Allow files to be overwritten" choice "No,Yes" 0 
  27.  
  28.  
  29. ;; To enable L/R prefix before alternate L/R channels 
  30. ;; (text output with header only)
  31. ;; remove the semicolon from the start of the next line:
  32. ;(setq LR-prefix '("L: " "R: "))
  33.  
  34. (when (not (boundp 'LR-prefix))(setq LR-prefix nil))
  35.  
  36. (setq default-filename "sample-data")       ; default filename
  37. (setq err "")                               ; initialise error mesaage
  38.  
  39. (setq *float-format* "%1.5f")               ; 5 decimal places
  40. (when (equal (string-trim " .,\/" number) "")
  41.       (setq number "100"))                  ; default=100
  42.  
  43.  
  44. (defun add-error (e-string)
  45.   (setq err (strcat err e-string "\n")))
  46.  
  47.  
  48. ;;; stereo peak
  49. (defun stereomax (snd)
  50.   (if (arrayp s)
  51.       (max (peak (aref s 0) number)(peak (aref s 1) number))
  52.       (peak s number)))
  53.  
  54.  
  55. ;;; stereo rms
  56. (defun srms (snd)
  57.   (if (arrayp snd)
  58.       (let* ((sql (mult (aref s 0)(aref s 0)))
  59.              (sqr (mult (aref s 1)(aref s 1)))
  60.              (avgsq (mult 0.5 (sum sql sqr)))
  61.              (avgsq (snd-avg avgsq number number op-average)))
  62.         (lin-to-db (peak (snd-sqrt avgsq) 1)))
  63.       (let* ((sndsq (mult snd snd))
  64.              (avgsq (snd-avg sndsq number number op-average)))
  65.         (lin-to-db (peak (snd-sqrt avgsq) 1)))))
  66.       
  67.  
  68. ;;; dc off-set mono
  69. (defun dc-off-mon (sig len)
  70. (let* ((total 0)
  71.        (sig (snd-copy sig)))
  72.   (dotimes (num (truncate len))
  73.     (setq total (+ total (snd-fetch sig))))
  74.   (/ total (float len))))
  75.  
  76.  
  77. ;;; compute dc offsets (mono/stereo)
  78. (defun dc-off (sig)
  79.   (if (arrayp sig)
  80.       (let ((lin0 (dc-off-mon (aref sig 0) number))
  81.             (lin1 (dc-off-mon (aref sig 1) number)))
  82.         (list lin0 (lin-to-db (abs lin0)) lin1 (lin-to-db (abs lin1))))
  83.       (let ((lin (dc-off-mon sig number)))
  84.         (list lin (lin-to-db (abs lin))))))
  85.  
  86.  
  87. (defun checknumber ()
  88.   (setq number (min number len))
  89.   (if (< number 1)
  90.       (add-error "No samples selected."))
  91.   (if (> number 1000000)
  92.       (add-error "Too many samples selected.\nSet limit to less than 1 million"))
  93.   (setq number (truncate number)))
  94.  
  95.  
  96. ;;; home directory
  97. (defun home ()
  98.   (if (windowsp)
  99.       (get-env "UserProfile")               ; Windows
  100.       (get-env "HOME")))                    ; Mac / Linux
  101.       
  102.  
  103. ;;; Check if Windows
  104. (defun windowsp ()
  105.   (char= #\\ *file-separator*))
  106.  
  107.  
  108. ;;; Windows safe linear-to-db
  109. (setf ln10over20 (/ (log 10.0) 20))
  110. (defun lin-to-db (val)
  111.   (if (= val 0)
  112.     "[-inf]"
  113.     (/ (log val) ln10over20)))
  114.  
  115.  
  116. ;;; Check if Mac
  117. (defun macp ()
  118.   (string-equal (subseq (get-env "HOME") 0 6) "/Users"))
  119.  
  120.  
  121. ;;; check if file exists
  122. (defun filep (fname ext &optional (fnum ""))
  123.   (let ((fname (format nil "~a~a~a" fname fnum ext)))
  124.     (if (open fname) T nil)))
  125.  
  126.  
  127. (defun makefilename (fname ext)
  128.   ;; avoid overwriting files
  129.   (if (and (= owrite 0)(filep fname ext))
  130.       (do ((num 1 (1+ num)))
  131.           ((not (filep fname ext num))
  132.            (format nil "~a~a~a" fname num ext)))
  133.       (strcat fname ext)))
  134.  
  135.  
  136. ;;; get sample and convert to dB if required
  137. (defun snd-get (snd &optional (dB 0))
  138.   (if (= dB 0)                              ; dB scale
  139.       (lin-to-db (abs (snd-fetch snd)))
  140.       (snd-fetch snd)))                     ; linear scale
  141.  
  142.  
  143. ;; fileformat  0=Text List, 1=Indexed List, 2=Time Indexed, 3=CSV, 
  144. ;; (4=html but not used here).
  145. ;; Optional 'same' [line] argument is either 'true' or 'nil'
  146. (defun formatprint (val snd &optional same)
  147.   (case fileformat
  148.     (0 (format fp "~a~a"                    ; text list
  149.                   (snd-get snd units)
  150.                   (if same "\t" "\n")))
  151.     (1 (format fp "~a\t~a~a"                ; indexed list
  152.                   val 
  153.                   (snd-get snd units)
  154.                   (if same "\t" "\n")))
  155.     (2 (format fp "~a\t~a~a"                ; time index
  156.                   (/ (1- val) *sound-srate*)
  157.                   (snd-get snd units)
  158.                   (if same "\t" "\n")))
  159.     (3 (format fp "~a~a"                    ; csv
  160.                   (snd-get snd units)
  161.                   (if (or (= chan 2) same) "," "\n")))))
  162.   
  163.  
  164. ;;; Print sample data to file
  165. (defun print-text (s-in)
  166.   (do ((n 1 (1+ n)))
  167.       ((> n number))
  168.     (if (arrayp s-in)                       ; Stereo (alternate lines)
  169.         (progn
  170.           ;; option to prefix alternate lines with L/R 
  171.           (when LR-prefix
  172.             (unless (or (= header 0)(= fileformat 3))
  173.               (format fp "~a" (first LR-prefix))))
  174.           (if (= chan 0)                    ; IF 'Same Line' then "True"
  175.             (formatprint n (aref s-in 0) T)
  176.             (formatprint n (aref s-in 0)))
  177.           (when LR-prefix
  178.             (unless (or (= header 0)(= fileformat 3))
  179.               (format fp "~a" (second LR-prefix))))
  180.           (formatprint n (aref s-in 1)))
  181.         (formatprint n s-in))))
  182.  
  183.  
  184. ;; Print to file
  185. (defun printdata ()
  186.   (case header
  187.     (0 (format t (normhead))(format fp (nohead)))
  188.     (1 (format t (normhead))(format fp (minhead)))
  189.     (2 (format t (normhead))(format fp (normhead)))
  190.     (3 (format t (normhead))(format fp (fullhead))))
  191.   ;; Stereo and left channel first
  192.   (if (and (arrayp s)(= chan 2))
  193.       (progn
  194.         (unless (= header 0)                ; Don't print 'channel' if no header
  195.           (format fp "Left Channel.~%~%"))
  196.         (print-text (aref s 0))
  197.         (if (= header 0)                    ; Don't print 'channel' if no header
  198.             (format fp "~%")
  199.             (format fp "~%~%Right Channel.~%~%"))
  200.         (print-text (aref s 1))
  201.         (close fp)
  202.         (if (= messages 0)
  203.             (format nil "~aData written to:~%~a~a~a" 
  204.                     (normhead) path fileseparator filename)
  205.             (s-rest 0)))
  206.       ;; mono or alternate
  207.       (progn
  208.         (print-text s)
  209.         (close fp)
  210.         (if (= messages 0)
  211.             (format nil "~aData written to:~%~a~a~a"
  212.                     (normhead) path fileseparator filename)
  213.             (s-rest 0)))))
  214.  
  215.  
  216. ;;; File destination processing
  217. (defun filewriter ()
  218.   ;; Set file extension
  219.   (setq FileExt
  220.     (case fileformat
  221.       (3 ".csv")
  222.       (4 ".html")
  223.       (T ".txt")))
  224.   ; file separator as string
  225.   (setq fileseparator (format nil "~a" *file-separator*))
  226.   ;; strip file separator and spaces
  227.   (let ((stuff (format nil " ~a" *file-separator*)))
  228.     (setq filename (string-left-trim stuff filename))
  229.     (setq path (string-right-trim stuff path)))
  230.   ;; strip file extension if present
  231.   (if (and (>= (length filename)(length FileExt))
  232.            (string-equal filename FileExt :start1 (- (length filename)(length FileExt))))
  233.     (setq filename (subseq filename 0  (- (length filename)(length FileExt)))))
  234.   ;; replace ~/ on Linux/Max
  235.   (if (and (>= (length path) 2)
  236.            (not (windowsp)))
  237.       (if (string-equal path "~/" :end1 2)
  238.           (setq path (strcat (home)(subseq path 1)))))
  239.   ;; If path not set use home directory
  240.   (if (or (string-equal path "Home directory")
  241.           (string-equal path ""))
  242.       (setq path (home)))
  243.   ;; if file name not set use default
  244.   (if (string-equal filename "")
  245.       (setq filename default-filename))
  246.   (setdir (strcat path fileseparator))      ; set target directory
  247.   ;; set file pointer or error
  248.   (let ((realdir (string-right-trim fileseparator (setdir "."))))
  249.     (if (or (string= path realdir)
  250.             (and  (or (windowsp)            ; case insensitive
  251.                       (macp))               ; assume case insensitive
  252.                   (string-equal path realdir)))
  253.         ;; makefilename or error
  254.         (setq filename (makefilename filename FileExt))
  255.         (add-error (format nil "Output folder \"~a~a\" cannot be accessed." 
  256.                            path fileseparator))))
  257.   ;; check if file is writeable
  258.   (when (= (length err) 0)
  259.     ;Open file for output
  260.     (setq fp (open filename :direction :output))
  261.     ;check file is writeable
  262.     (if (not fp)
  263.         (add-error (format nil "\"~a~a~a\" cannot be written."
  264.                            path fileseparator filename)))))
  265.  
  266.  
  267. ;;; Header text
  268.  
  269. (defun nohead ()
  270.   (if (> (length optext) 0)
  271.       (format nil "~a~%~a~%" 
  272.               optext 
  273.               (get 'info 'chan-order))
  274.       ""))
  275.  
  276.  
  277. (defun minhead ()
  278.   (format nil 
  279. "Sample Rate: ~a Hz.  Sample values on ~a scale.~%~a~%~a" 
  280.   (get 'info 'srate)                        ; sample rate
  281.   (get 'info 'units)                        ; units
  282.   (get 'info 'chan-order)                   ; Channel Order
  283.   (if (> (length optext) 0)
  284.       (format nil "~a~%~%~%" optext)        ; optional text
  285.       (format nil "~%"))))                  ; no optional text
  286.  
  287.  
  288. (defun normhead ()
  289.   (if (= fileformat 4)                      ; html
  290.     (format nil
  291. "~a   ~a~%~aSample Rate: ~a Hz.~%Length processed: ~a samples ~a seconds.~a" 
  292.       filename                              ; file name
  293.       (get 'info 'channels)                 ; mono/stereo
  294.       (get 'info 'chan-order)               ; Channel Order
  295.       (get 'info 'srate)                    ; sample rate
  296.       number                                ; number of samples
  297.       (get 'info 'duration)                 ; duration (seconds)
  298.       (if (> (length optext)0)
  299.           (format nil "~%~a~%~%~%" optext)  ; optional text
  300.           (format nil "~%~%~%")))           ; no optional text
  301.     (format nil
  302. "~a   ~a~%~aSample Rate: ~a Hz. Sample values on ~a scale.~%~
  303. Length processed: ~a samples ~a seconds.~a" 
  304.       filename                              ; file name
  305.       (get 'info 'channels)                 ; mono/stereo
  306.       (get 'info 'chan-order)               ; Channel Order
  307.       (get 'info 'srate)                    ; sample rate
  308.       (get 'info 'units)                    ; units
  309.       number                                ; number of samples
  310.       (get 'info 'duration)                 ; duration (seconds)
  311.       (if (> (length optext)0)
  312.           (format nil "~%~a~%~%~%" optext)  ; optional text
  313.           (format nil "~%~%~%")))))         ; no optional text
  314.  
  315.  
  316. (defun fullhead ()
  317.   (format nil
  318. "~a~%Sample Rate: ~a Hz. Sample values on ~a scale. ~a.~%~aLength processed: ~a ~
  319. samples, ~a seconds.~%Peak amplitude: ~a (lin) ~a dB.  Unweighted rms: ~a dB.~%~
  320. DC offset: ~a~a" 
  321.   filename                                  ; file name
  322.   (get 'info 'srate)                        ; sample rate
  323.   (get 'info 'units)                        ; units
  324.   (get 'info 'channels)                     ; mono/stereo
  325.   (get 'info 'chan-order)                   ; Channel Order
  326.   number                                    ; number of samples
  327.   (get 'info 'duration)                     ; duration (seconds)
  328.   (setq smax (stereomax s))                 ; peak amplitude linear
  329.   (lin-to-db smax)                          ; peak amplitude dB
  330.   (srms s)                                  ; rms
  331.   (let ((vals (dc-off s)))                  ; DC offset
  332.     (if (= (length vals) 2) ; mono
  333.         (format nil "~a linear, ~a dB." 
  334.                 (first vals)(second vals))
  335.         (format nil "Left: ~a lin, ~a dB | Right: ~a lin, ~a dB."
  336.                 (first vals)(second vals)(third vals)(fourth vals))))
  337.   (if (> (length optext)0)
  338.       (format nil "~%~a~%~%~%" optext)      ; optional text
  339.       (format nil "~%~%~%"))))              ; no optional text
  340.  
  341.  
  342. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  343. ;;        HTML Output         ;;
  344. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  345.  
  346. (defun html-head ()
  347. "<!DOCTYPE html>
  348. <html>
  349. <head>
  350. <meta name=\"generator\" content=
  351. \"Sample Data Export by Steve Daulton, (http://www.easyspacepro.com). Released under GPL v2.\">
  352. <meta name=\"description\" content=\"Sample Printer, Free Audacity plug-in\" />
  353. <meta name=\"keywords\" content=\"sample printer,Audacity,plug-ins,plugins,effects,audio,audio processing,music,analize,\" />
  354. <meta name=\"author\" content=\"Steve Daulton\" />
  355. <meta charset=\"UTF-8\">
  356.  
  357. <style type=\"text/css\">
  358. body {
  359.   margin: 1em 5%;
  360.   background-color: #dda;
  361.   font-family:Arial,Helvetica,sans-serif;
  362.   }
  363. table,th,td {
  364.   background-color: #fff;
  365.   border:1px solid black;
  366.   text-align: center;
  367. }
  368. table {
  369.   width: auto;
  370.   border: 2px;
  371.   border-style:ridge;
  372.   border-collapse:collapse;
  373. }
  374. td {
  375.   text-align: right;
  376.   padding-right: 0.5em;
  377. }
  378. tr:hover td {
  379.   background-color:#fcd;
  380. }
  381. th {
  382.   padding: 0 0.5em;
  383.   background-color: #ddf;
  384.   border-bottom-width: 2px;
  385.   border-bottom-style:ridge;
  386. }
  387. h1 {
  388.   font-size: 1.6em;
  389.   color: #633;
  390. }
  391. h2 {
  392.   font-size: 1.4em;
  393.   color: #633;
  394. }
  395. h3 {
  396.   font-size: 1em;
  397.   color: #366;
  398. }
  399. h4 {
  400.   font-size: 1em;
  401.   color: #000;
  402. }
  403. ul {
  404.   position:relative;
  405.   top: -0.5em;
  406.   }
  407. #footer {
  408.   font-size: 0.8em;
  409.   position:relative;
  410.   top: 0.5em;
  411.   left: 2%;
  412.   }
  413. #footer span {
  414.   font-style:italic;
  415.   font-weight: bold;
  416.   color: #633;
  417.   }
  418. #footer a:link,a:visited {
  419.   color: #639;
  420.   text-decoration: none;
  421.   }
  422. #footer a:hover,a:active {
  423.   text-decoration: underline;
  424.   color: blue;
  425.   }
  426. </style>
  427. <title>Sample Data Export</title>
  428. </head>
  429. ")
  430.  
  431.  
  432. ;;; document headings
  433. (defun doc-head ()
  434.   (format nil
  435. "<body>
  436. <h1>Sample Data Export - ~a</h1>
  437. ~a
  438. <h4>~a.   ~a samples.    ~a seconds.<br></h4>
  439. <h3>Audio data analysis:</h3>
  440. <ul>
  441. <li><b>Sample Rate:</b>   ~a Hz.</li>
  442. <li><b>Peak Amplitude:</b>   ~a (lin)   ~a dB.</li>
  443. <li><b>RMS</b> (unweighted):   ~a dB.</li>
  444. <li><b>DC Offset:</b>   ~a</li>
  445. </ul>
  446. "
  447.   (string-right-trim ".html" filename)
  448.   (format nil "<h2>~a</h2>" optext)         ; Optional heading
  449.   (get 'info 'channels)                     ; mono/stereo
  450.   number                                    ; number of samples
  451.   (get 'info 'duration)                     ; duration (seconds)
  452.   (get 'info 'srate)                        ; sample rate
  453.   (setq smax (stereomax s))                 ; peak amplitude linear
  454.   (lin-to-db smax)                          ; peak amplitude dB
  455.   (srms s)                                  ; rms
  456.   (let ((vals (dc-off s)))                  ; DC offset
  457.     (if (= (length vals) 2) ; mono
  458.         (format nil "~a linear,   ~a dB." 
  459.                 (first vals)(second vals))
  460.         (format nil "Left: ~a lin, ~a dB | Right: ~a linear,   ~a dB."
  461.                 (first vals)(second vals)(third vals)(fourth vals))))))
  462.  
  463.  
  464. ;;; table headings  (mono)
  465. (defun table-head-mono ()
  466. "<table title=\"sample data\">
  467. <tr>
  468. <th>Sample #</th>
  469. <th>Seconds</th>
  470. <th>Value (linear)</th>
  471. <th>Value (dB)</th>
  472. </tr>")
  473.  
  474.  
  475. ;;; table headings (stereo)
  476. (defun table-head-stereo ()
  477. "<table title=\"audio sample value analysis\">
  478. <tr>
  479. <th>Sample #</th>
  480. <th>Seconds </th>
  481. <th>Left (linear)</th>
  482. <th>Right (linear)</th>
  483. <th>Left (dB)</th>
  484. <th>Right (dB)</th>
  485. </tr>")
  486.  
  487.  
  488. (defun html-foot ()
  489. "</table>
  490. <p id=\"footer\">Produced with <span>Sample Data Export</span> for
  491. <a href=\"http://audacity.sourceforge.net/\">Audacity</a> by Steve
  492. Daulton (<a href=
  493. \"http://www.easyspacepro.com\">www.easyspacepro.com</a>)</p>
  494. </body>
  495. </html>")
  496.  
  497.  
  498. ;;; html generator
  499. (defun make-htm (id val1 &optional val2)
  500.   (if val2
  501.       ;; stereo
  502.       (let ((time (/ (1- id) *sound-srate*))
  503.             (db1 (lin-to-db (abs val1)))
  504.             (db2 (lin-to-db (abs val2))))
  505.         (format fp 
  506.           "<tr>~%<td>~a</td>~%<td>~a</td>~%<td>~a</td>~%<td>~a</td>~%~
  507.           <td>~a</td>~%<td>~a</td>~%</tr>~%" 
  508.           id time val1 val2 db1 db2))
  509.       ;; mono
  510.       (let ((time (/ (1- id) *sound-srate*))
  511.             (db (lin-to-db (abs val1))))
  512.         (format fp 
  513.           "<tr>~%<td>~a</td>~%<td>~a</td>~%<td>~a</td>~%<td>~a</td>~%</tr>~%" 
  514.           id time val1 db))))
  515.  
  516.  
  517. (defun printhtml ()
  518.   (format t (normhead))
  519.   (format fp (html-head))
  520.   (format fp (doc-head))
  521.   (if (arrayp s)
  522.       (progn
  523.         (format fp (table-head-stereo))
  524.         (do ((i 1 (1+ i)))
  525.             ((> i number))
  526.           (make-htm i
  527.                     (snd-fetch (aref s 0))
  528.                     (snd-fetch (aref s 1)))))
  529.       (progn
  530.         (format fp (table-head-mono))
  531.         (do ((i 1 (1+ i)))
  532.             ((> i number))
  533.           (make-htm i (snd-fetch s)))))
  534.   (format fp (html-foot))
  535.   (close fp)
  536.     (if (= messages 0)
  537.         (format nil "~aData written to:~%~a~a~a"
  538.                (normhead) path fileseparator filename)
  539.         (s-rest 0)))
  540. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  541. ;;       END OF HTML          ;;
  542. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  543.  
  544.  
  545. ;;; basic info for headers
  546. (defun put-head-info ()
  547.   (putprop 'info (truncate *sound-srate*) 'srate)
  548.   (putprop 'info (if (= units 0) "dB" "linear") 'units)
  549.   (putprop 'info (/ number *sound-srate*) 'duration)
  550.   (putprop 'info 
  551.     (if (arrayp s) 
  552.         "2 channels (stereo)""1 channel (mono)")
  553.         'channels)
  554.   ;; stereo sample order
  555.   (putprop 'info
  556.     (cond
  557.       ((and (= fileformat 3)(= chan 0))     ; csv, channel in column
  558.         "One column per channel.\n")
  559.       ((and (= fileformat 3)(= chan 2))     ; csv, channel in row
  560.         "One row per channel.\n")
  561.       ((or (soundp s)(= fileformat 4))      ; mono soundor HTML
  562.         "")
  563.       ((= chan 0) "Left channel then Right channel on same line.\n")
  564.       ((= chan 1) "Left and right channels on alternate lines.\n")
  565.       ((= chan 2) "Left channel first then right channel.\n")
  566.       (T "Unspecified channel order"))
  567.     'chan-order))
  568.  
  569.  
  570. ;;; get number from string
  571. (setq number (read (make-string-input-stream number)))
  572. (if (numberp number)
  573.     (checknumber)
  574.     (add-error (format nil "~a is not a number." number)))
  575.  
  576. (filewriter)
  577. (if (> (length err) 0)
  578.   ;; output error message if enabled
  579.   (if (= messages 2)
  580.     (s-rest 0)                              ; return nul sound
  581.     (format nil "Error.~%~a" err))          ; return errors
  582.   ;; else print to file
  583.   (progn
  584.     (put-head-info)                         ; put basic info for headers
  585.     (if (= fileformat 4)
  586.         (printhtml)                         ; html output
  587.         (printdata))))                      ; text output
  588.