[comp.sys.mac.programmer] Question for MacRecorder users

ewa@beowulf.ucsd.edu (Eric Anderson) (05/11/91)

Does anyone know if it is possible to use MacRecorder (from Farallon) to
perform instant processing of live audio?  For example, a VU meter, or
even a frequency analyzer?  The basic question is, can samples be read
continuously from MacRecorder, or is there a big buffering delay somewhere
along the path?

How about the microphone port built in to the newer Macs?

Please respond by mail (or mail and posting).  Thanks!

Eric Anderson                                      ewa@cs.ucsd.edu
Computer Systems Laboratory                        (619)534-8604
Department of Computer Science and Engineering
University of California, San Diego                                             
9500 Gilman Drive 0114
La Jolla, CA  92093-0114                                              

trebor@lkbreth.foretune.co.jp (Robert J Woodhead) (05/12/91)

Boy is this your lucky day.  As it happens I asked the same
question here not a week ago.  Several people sent me copies
of the MacRecorder hackers toolkit.  There are several
versions of this floating around, all in C.  As it happens,
I program mostly in Pascal, so I converted the stuff into
PASCAL, and also wrote a software VU meter function.

Below is the basic software in MPW Assembler and PASCAL to
do what you want.  Hope it helps.

** I HEREBY PLACE THIS CODE IN THE PUBLIC DOMAIN **
** Robert J Woodhead, 5/12/1991                  **

;
; assembly language interface to the Macrecorder
; modified by RJW from Macrecorder hackers toolkit
;

         machine  mc68020
         mc68881
         
;
; function flip(x:integer):integer;
;
;   flips the bits in the byte x and returns the
;   new bits
;

flip     FUNC     EXPORT

retAddr  equ      A0    ; Pascal return address
param    equ      D1    ; x, our parameter

;
; start of code, stack is as follows:
;
;  tos-> return address ; pointer
;        param          ; word
;        func result    ; word
;

         move.l   (sp)+,retAddr     ; save return address
         move.w   (sp)+,param       ; and parameter
         
         moveq    #7,D2             ; loop counter
         moveq    #0,D0             ; our result

loop_top roxl.b   #1,D1             ; get a bit from source
         roxr.b   #1,D0             ; put into destination
         dbra     d2,loop_top       ; and loop
         
         move.w   D0,(sp)           ; our result
         jmp      (retAddr)
         
         ENDF
         
;
; function mono22(fromwhere:ptr; outbuf:ptr; fliptable:ptr; len:longint):longint;
;
;  reads the macRecorder from port fromwhere and puts the bytes in outbuf.
;  Uses tables fliptable to correct the bytes.  Records a maximum of len
;  bytes.  Returns the number of bytes read.
;

mono22   FUNC     EXPORT

;
; start of code, stack is as follows:
;
;  tos-> return address ; pointer
;        len            ; longint
;        fliptable      ; pointer
;        outbuf         ; pointer
;        fromwhere      ; pointer
;        func result    ; longint
;

retAddr  equ      A0
len      equ      D1
fliptab  equ      A1
outbuf   equ      A2
from     equ      A3
toutbuf  equ      D0    ; temp outbuf area
tfrom    equ      D2    ; temp tfrom area

result   equ      D0    ; return result
sound    equ      D2    ; sound byte we get
timer    equ      D3    ; watchdog timer
savelen  equ      D4    ; saved copy of length

         move.l   (sp)+,retAddr        ; return address
         move.l   (sp)+,len
         move.l   (sp)+,fliptab
         move.l   (sp)+,toutbuf        ; temp location
         move.l   (sp)+,tfrom          ; temp location
         movem.l  A2-A3/D2-D4,-(sp)    ; save regs we use
         move.l   toutbuf,outbuf       ; put pointers into aregs
         move.l   tfrom,from           ; address of sccRd
         
         moveq    #-1,result           ; result code, assume failure
         moveq    #0,sound             ; sound byte holder
         move.l   len,savelen          ; # of bytes to get
         
reset    move.l   #50000,timer         ; timeout

loop     subq.l   #1,timer             ; decrement timeout
         bmi.s    timeout
         
         btst     #0,(from)            ; data yet?
         beq.s    loop
         
         move.b   4(from),sound              ; get the byte
         move.b   0(fliptab,sound),(outbuf)+ ; and save it
         
         subq.l   #1,len               ; one less to do
         bne.s    reset                ; and get another
         
exit     sub.l    len,savelen          ; figure out how many done
         move.l   savelen,result       ; save in return result
         
timeout  movem.l  (sp)+,A2-A3/D2-D4    ; restore registers
         move.l   result,(sp)          ; fix return result
         jmp      (A0)                 ; and done

         ENDF

;
; function lvl22(fromwhere:ptr; fliptable:ptr; len:longint):longint;
;
;  reads the macRecorder from port fromwhere and figures intensity.
;  Uses tables fliptable to correct the bytes.  Records a maximum of len
;  bytes.  Returns the hi+lo-256 of the bytes read.
;

lvl22 FUNC     EXPORT

;
; start of code, stack is as follows:
;
;  tos-> return address ; pointer
;        len            ; longint
;        fliptable      ; pointer
;        fromwhere      ; pointer
;        func result    ; longint
;

retAddr  equ      A0
len      equ      D1
fliptab  equ      A1
from     equ      A3
tfrom    equ      D2    ; temp tfrom area

result   equ      D0    ; return result
sound    equ      D2    ; sound byte we get
timer    equ      D3    ; watchdog timer
savelen  equ      D4    ; saved copy of length
lowlvl   equ      D5    ; lowest level seen
hilvl    equ      D6    ; highest level seen
flag     equ      D7    ; ignore 1st byte flag

         move.l   (sp)+,retAddr        ; return address
         move.l   (sp)+,len
         move.l   (sp)+,fliptab
         move.l   (sp)+,tfrom          ; temp location
         movem.l  A2-A3/D2-D7,-(sp)    ; save regs we use
         move.l   tfrom,from           ; address of sccRd
         
         move.l   #256,result          ; result code, assume nothing
         moveq    #0,sound             ; sound byte holder
         move.l   len,savelen          ; # of bytes to get
         move.l   #128,hilvl           ; init counters
         move.l   #128,lowlvl
         
reset    move.l   #50000,timer         ; timeout

loop     subq.l   #1,timer             ; decrement timeout
         bmi.s    timeout
         
         btst     #0,(from)            ; data yet?
         beq.s    loop
         
         move.b   4(from),sound              ; get the byte
         move.b   0(fliptab,sound),sound     ; xlate it
         
         cmp.l    len,savelen          ; equal lengths, ignore
         beq.s    lbot                 ; so exit
         
         cmp.b    sound,hilvl          ; compare hi levels
         bmi.s    hipast
         move.b   sound,hilvl
         
hipast   cmp.b    lowlvl,sound         ; and low levels
         bmi.s    lbot
         move.b   sound,lowlvl
         
lbot     subq.l   #1,len               ; one less to do
         bne.s    reset                ; and get another
         
exit     move.l   lowlvl,result        ; add the two quantities
         add.l    hilvl,result         ; save in return result
         
timeout  movem.l  (sp)+,A2-A3/D2-D7    ; restore registers
         move.l   result,(sp)          ; fix return result
         jmp      (A0)                 ; and done

         ENDF

;
; procedure sccInit(scc:ptr)
;
;  sets up the scc for proper reading and writing
;

         MACRO
         sccwait
         movem.l  A2-A5/D0-D7,-(sp)    ; waste some time
         movem.l  (sp)+,A2-A5/D0-D7    ; noted comment in IM3:25 about
         movem.l  A2-A5/D0-D7,-(sp)    ; accessing scc to quickly...
         movem.l  (sp)+,A2-A5/D0-D7    ;
         ENDM
         
         MACRO
         sccpoke  &reg,&data           ; poke reg & data into the scc
         move.b   &reg,(A1)
         move.b   &data,(A1)
         sccwait
         ENDM
         
sccInit  PROC     EXPORT

         move.l   (sp)+,A0             ; return address
         move.l   (sp)+,A1             ; SCC address
         
         sccpoke  #9,#2                ; do the poking...
         sccpoke  #4,#12
         sccpoke  #1,#1
         sccpoke  #3,#193
         sccpoke  #5,#122
         sccpoke  #11,#48
         sccpoke  #14,#1
         sccpoke  #15,#8
         sccpoke  #64,#64
         sccpoke  #9,#10
         
         jmp      (A0)                 ; return
         
         ENDP
         
;
; procedure intOn;
; procedure intOff;
;
;  turn interrupts on and off
;

intOn    PROC     EXPORT               ; turn interrupts on

         move.l   (sp)+,A0             ; return address
         andi     #$F8FF,SR            ; change status register
         jmp      (A0)
         
         ENDP
         
intOff   PROC     EXPORT

         move.l   (sp)+,A0
         ori      #$0700,SR
         jmp      (A0)
         
         ENDP
         
         END

{[n+,u+,r+,d+,#+,j=13-/40/1o,t=2,o=95] PasMat formatting options}

(* Sample the MacRecorder *)

{$R-}
PROGRAM sound;

USES
   Memtypes,
   Quickdraw,
   OSIntf,
   ToolIntf,
   PackIntf,
   CursorCtl,                         { for the spinning cursor}
   Signal,                            { to handle command-period}
   PasLibIntf,                        { for standard I/O, etc.}
   IntEnv;                            { for argV and argC}

type
   fliparr = packed array[0..255] of char;
   bufarr  = packed array[0..16385] of char;
   cptr    = ^bufarr;
   lptr    = ^longint;
   
var
   i,j,k,a1,a2:integer;
   l,t:        longint;
   sccRd,
   sccWr:      ptr;
   fliptab:    fliparr;
   buf:        bufarr;
   
FUNCTION flip(i:integer):integer; EXTERNAL;
FUNCTION mono22(from:ptr; buffer:ptr; fliptable:ptr; len:longint):longint; EXTERNAL;
PROCEDURE sccInit(sccWr:ptr); EXTERNAL;
PROCEDURE intOn; EXTERNAL;
PROCEDURE intOff; EXTERNAL;

PROCEDURE buildFlipTable;

var i:integer;

BEGIN

   for i:=0 to 255 do
      fliptab[i]:=chr(flip(i));
      
END;

BEGIN

   Writeln('Test of input from MacRecorder');
   
   writeln('Initializing fliptable');
   buildFlipTable;
   
   writeln('Initializing SCC');
   sccRd:=Pointer(lptr($1D8)^+2);
   sccWr:=Pointer(lptr($1DC)^+2);
   
   writeln('SCCrd $1D8 -> ',lptr($1D8)^,' = ',longint(sccRd));
   writeln('SCCwr $1DC -> ',lptr($1DC)^,' = ',longint(sccWr));
   
   intOff;
   sccInit(sccWr);
   intOn;
   
   l:=256;
   
   for j:=0 to l-1 do
      buf[j]:=chr(0);
      
   Writeln('GO!');
   
   intOff;
   l:=mono22(sccRd,@buf,@fliptab,l);
   intOn;
   
   Writeln('Done!');
   
   if (l<1) then
      writeln('Error, could not read!')   
   else 
      begin
         a1:=255; a2:=0;
         
         for j:=0 to l-1 do
            begin
               write(ord(buf[j]):4);
               if ((j mod 10) = 9) then
                  writeln;
               t:=t+ord(buf[j]);
               if ord(buf[j])>a2 then a2:=ord(buf[j]);
               if ord(buf[j])<a1 then a1:=ord(buf[j]);
            end;
            
         t:=t div l;
         
         writeln;
         writeln;
         writeln('Statistics : bytes = ',l,' lo = ',a1,' hi = ',a2,' avg = ',t);
      end;
      
END.

Using the LVL22 function should also be pretty easy.  I built a video VU meter
that output onto my CS2 in an hour or so.  Hope this helps.

-- 
+--------------------------------------------------------------------------+
| Robert J. Woodhead, Biar Games / AnimEigo, Incs.   trebor@foretune.co.jp |
| "The Force. It surrounds us; It enfolds us; It gets us dates on Saturday |
| Nights." -- Obi Wan Kenobi, Famous Jedi Knight and Party Animal.         |