TweetFollow Us on Twitter

Sound Made Simple
Volume Number:2
Issue Number:2
Column Tag:Pascal Procedures

Mac Sound Made Simple

By Alan Wootton, President, Top-Notch Productions, MacTutor Contributing Editor

This month we will attempt to coax some sounds from the Mac. We will try all three of the sound producing modes on the Mac. And, since there is a problem, I will present what I think is a better Pascal interface to the Sound Driver. Finally, I will introduce a method of saving sound effects in a standard form for later use.

The Sound Driver

The first source of information should be the Sound Driver chapter of Inside Macintosh. The first apparent fact about the Sound Driver is that it supports three different forms of synthesis. All three modes are reached by calling PROCEDURE StartSound( synthRec : Ptr ; NumBytes : LONGINT ; completionRtn : ProcPtr ) with synthRec set to the proper values. Since StartSound is defined in all three Pascals we are likely to use (Lisa Pascal, TML Pascal, and MacPascal; On-Stage Pascal was not available at the time of this writing), we will work in MacPascal and assume that our work is easily transportable to the others. As a driver, the Sound Driver will accept standard Device Manager control calls. There are predefined routines that make the driver calls for us in most cases. The Sound Driver is in the ROM and never needs an Open call (or a Close). As drivers go, it is fairly simple to deal with, so we won't go into extreme detail. Advanced users will want to use completion routines and other driver features.

Before we start making our first sound, it will be worthwhile to acknowledge how it is that the Mac can make sound at all. There is a special area in RAM where sound information is stored (much as there is a designated area of RAM that holds the screen information). Every time the video circuitry has completed painting one line on the screen, a byte is fetched from the sound RAM, and sent to a digital-to-analog converter. From there, the analog signal is sent to the speaker. A sound byte is processed 370 times for each 1/60th of a second. There are 60 'ticks', and also 60 frames of Mac video every second. This means that every second, 60*370=22,200 bytes, are processed by the sound circuitry. The Sound Bytes are meant to represent an output voltage at the speaker, with 255 being greatest and 0 being smallest (we will use 128 for a neutral point). So you see, the Mac is not much of a synthesizer at all. All it really can do is play back data. Any other functions desired will have to be provided for in software. Every tick the same 370 bytes in memory are used, so unless we want to hear the same thing over and over, 60 times per second, we will have to replace all 370 bytes repeatedly. If we had to do this ourselves we'd be talking assembly language now. Fortunately, the Sound Driver is in assembly language and it will do this for us. And it will do it three different ways.

Our main goal is to simply get program shells of the three sound modes in action, and to defer until later those data structures and routines one may use in a complex application.

The Square Wave mode

The square wave mode of synthesis appears to be the simplest. All the Sound Driver has to do, 60 times per second, is fill the sound buffer (370 bytes) with alternating values. If every other byte were 0 and 255, then we would get the loudest sound and the highest note (370/2 alternations per tick* 60 ticks per sec = 11,100 Hz). If one tick the 370 bytes were all 128, and the next tick they were all 129, we would get a very quiet note at 30 Hz. (You probably couldn't hear it from the Mac speaker). I don't think this is actually how the Sound Driver does it, but you get the idea.

Here is a MacPascal program that should get some different tones using the Square Wave Mode.

program Square_Wave_Scale;
   const
      notecount = 31;
   var
 i : integer;
 MySWSynthRec : record
 mode : integer;
 tones : array[1..notecount] of record
        count, amplitude, duration : integer;
         end;{ of tones }
   end;{ of mySWSynthRec }

begin
   with MySWSynthRec do
 begin
    mode := SWMode;
    for i := 1 to notecount do
    with tones[i] do
 begin
    count := i * 64 + 256;
    amplitude := 255 - i * 8;
    duration := 30;{ 1/2 sec. }
 end;
    StartSound(@MySWSynthRec,  sizeof(MySWSynthRec), nil);
    ShowText;
    Writeln('press mouse to stop');
    repeat
    until button;
 end;
   StopSound;
end.

The problem is that Square_Wave_scale does not work. The SWSynthRec is filled out correctly; the mode is set, and the tones are filled in. All is well, but there seems to be something wrong with StartSound. The likely culprit is the CompletionPtr parameter. According to the MacPascal manual, you pass the address of a procedure if you want that procedure to be executed when the sound is done. MacPascal will (currently) not allow you to get the address of any of its procedures. If you want the sound to start, and then finish before the program resumes, you are to use pointer(-1). When I did that the Mac locked up, and I had to reboot. If you want the sound to start, and then for the program to resume before the sound is done (seems like a good idea), use nil. When I did that nothing happened. Perhaps we should junk StartSound.

This brings up an interesting point. Clearly the point of this completion routine business is to be able to splice two sounds front to back with no seam. If our program has to wait for the first sound to finish, then there will be a delay (especially in MacPascal) before the next sound can be started. StartSound is going to have to make a Device Manager call (in this case PBWrite) to start the sound. To do that it will need to use a Parameter Block Record. What happens to that record when a second sound is started if the first is not done?

What must be done is to create our own version of StartSound. We will use two parameter block records. That way one can be filled out and then passed to the device managers I/O queue while the other is busy. Our sounds will follow one another naturally without much fuss or muss. The predefined procedure 'Generic' will be used to make a Device Manager call. Let's cover this strategy in more detail.

First, declare the procedure. We will call it myStartSound, and use the same parameter list, even though completionRtn will not be used.

Second, the Parameter Block Record. Instead of using the entire record declaration, found in the Device Manager chapter of Inside Macintosh, we will use an array of integers and use BlockMove to move data into the record. Note that the numbers used alongside the Device Manager routine descriptions must be divided by two to get an index into the array of words. Only ioRefNum, ioBuffer, and ioReqCount are used, so this is reasonable. You must declare the parameter blocks at the outermost level possible, ie. they must not go away because the procedure they are defined in is done. Also, note that MacPascal erases them to zero for us. In another Pascal you should do that during initialization.

Third, find a method of choosing which parameter block to use. The boolean variable AUsed is used to indicate which Parameter block was used last. It is toggled with every call. Once we know which block we are using, its ioResult field is checked to make sure it is done before modifying it.

Finally, we need a method of making a Device Manager Write call. The Sound Manager chapter tells us to set ioRefNum to -4, to set ioBuffer to point to the SynthRec, and to set ioReqCount to NumBytes. To make the call we declare an array of 13 longints which Generic will use to set the 68000 registers (A0 to A4 then D0 to D7. See Advanced Mac'ing, MacTutor, Vol.1 No. 5 for the introduction of Generic) for the toolbox call. The first longint in the array is A0 and that is set to the address of the Parameter Block. The number of the PBWrite toolbox routine is $A003, and we add $400 to it which causes asyncronous operation.

If you add the following to Square_Wave_Scale then it will work properly.

{ These two types are needed by MyStartSound }
   type
      Ptr = ^integer;
      ParamBlockFake = array[0..30] of integer;

{ put these variables in with the rest of the global var's }
{ var }
 blockA, blockB : ParamBlockFake;{ for myStartSound }
 AUsed : boolean;{ for MyStartSound }

{ use this procedure everywhere you might }
{ otherwise use MySound }
procedure MyStartSound (SynthRec : ptr;
               numbytes : longint;
     CompletionRtn : Ptr);
 { CompletionRtn ignored }
   var
 regs : array[0..12] of longint; { for generic }
 BlockPtr : ^ParamBlockFake;
   begin
 if Aused then
    BlockPtr := @BlockA
 else
    BlockPtr := @BlockB;
 Aused := not Aused;

 while BlockPtr^[8] <> 0 do { wait for ioResult to clear}
 ;
 BlockPtr^[12] := -4;{ set ioRefNum }
 BlockMove(@SynthRec, @BlockPtr^[16], 4);{ ioBuffer }
 BlockMove(@numbytes, @BlockPtr^[18], 4);{ioReqCount}

 { The following two lines do PBWrite(BlockPtr,true) }
 regs[0] := ord(BlockPtr);{ set A0 for Generic }
 Generic($A403, regs);{ Write,async }
   end;

Please note that all the previous technical talk you just encountered is for the sole purpose of creating a new version of StartSound. If you didn't understand it, don't worry. Just use myStartSound everywhere you would use StartSound, and set completionRtn to nil. What we have actually done is to simplify the situation, with the small cost of having to paste myStartSound into your sound programs.

The Four Tone mode

The Four Tone mode is a little more complicated. It provides a more complex synthesis function as well. The idea is that you provide a waveform table of bytes for each of four sounds. The Sound Driver will move through the tables, at a rate you specify, sum the four values for each byte, and place the result in the 370 byte sound buffer. Since it must sum 370*4 bytes 60 times per second, in addition to calculating which byte from which table to use, this takes about 1/120th of a second. When the Mac is making four tone sounds it appears to run at half speed.

The waveform tables are 256 bytes long. You may put into them any numbers you wish, thereby producing different sounds. We will talk more about waveforms later. The Sound Driver does not simply put the first byte in a wave table into the first byte of the sound buffer, and the second in the second, etc. What it seems to do is add the fixed (fixed numbers have an integer in the upper half, and the lower half is a fraction) quantity Rate to the (fixed?) quantity Phase, and then to take the result mod 256 as the index into the wave table. This means that a Rate of 2 will use every other byte in the wave table and a Rate of 1/2 will use every byte twice.

From a programming point of view all you have to do is fill out the data structures and call StartSound. The application point of view is left up to you. Let me now present a program that will make a single, pure tone using the Four Tone mode.

Program  Four_Tone_Test;
   uses
 Sane;
   type{ for MyStartSound }
 Ptr = ^integer;
 ParamBlockFake = array[0..30] of integer;
   var
 rate1 : integer;
 MyFTSynth : FTSynthRec;
 myFTSound : FTSoundRec;
 SinWave : packed array[0..255] of char;

 blockA, blockB : ParamBlockFake;{ for myStartSound }
 AUsed : boolean;{ for MyStartSound }

{ Paste myStartSound here }

{ Fill the array SinWave with bytes (chars) }
{ representing one cycle of a sine wave }
{ note that numbers are from 0 to 255 so that }
{ 128 is 'zero' }
   procedure FillSinWave;
 var
     i : integer;
    f, pi : extended;
   begin
 pi := arcTan(1) * 4;
 f := 2 * pi / 256;
 for i := 0 to 255 do
    SinWave[i] := chr(Num2Integer(sin(i * f) * 120 + 128));
   end;

begin { of main }
   FillSinWave;
   ShowText;

   MyFTSynth.mode := FTMode;
   MyFTSynth.SndRec := @MyFTSound;
   { Note that all MacPascal Records are initialised }
   { by the system to zero.  In another Pascal you may }
   { have to remember to initialize everything }
   { ie. when we start all rates are 0 }

   with MyFTSound do
 begin
    Duration := 1000;
    Sound1Wave := @SinWave[0];
    MyStartSound(@MyFTSynth, Sizeof(MyFTSynth), nil);
    rate1 := 1024;
 repeat
    Duration := 1000;
    Sound1Rate := FixRatio(rate1, 256);
    rate1 := rate1 + rate1 div 16;
    writeln('The rate is', rate1, ' div 256, = ',
                                             FixRatio(rate1, 256));
    if rate1 >= 2048 then
 rate1 := 1024;
 until Button;
    end;
 StopSound;
end.   

The Sane function Sin which you might normally think of as being used to find the vertical component of an angle (the angle is in radians, ie. 2*pi=360°, where pi is the ratio of the diameter to the circumference of a circle) is also used to produce the numbers required for a pure musical tone. Certain California Types might wax philosophical about this duality of function in nature, but we won't do that here (do California Types eat quiche?, do Real Programmers wax philosophical?). If you divide 2*pi by 256 and then multiply by a loop index that goes from 0 to 255, then you will get numbers corresponding to one complete waveform. This is what it looks like graphically (see fig. 1 top of next page).

Note that when you are using Four Tone mode, you don't have to call StartSound over and over if you don't want. If you keep the duration large then you can simply change the values for each of the four tones 'on the fly'. Remember, that while the sound is still going, the data structures belong to the Sound Driver. You may modify them, but do not delete them (like if they were declared inside a procedure that ended, or if you allocated them on the heap and then later disposed of them). When you are done, call StopSound, and all records being used are released.

Now that we have the Four Tone synthesizer working, try this program for a more interesting result. Sorry, but unlike graphic programs, there is no easy way to publish what the output of these programs is like. You have to type them in and see (I mean hear) for yourself.

program Four_part_sound;
   uses
 sane;
   const
 BaseNote = 1024;
   type
 Ptr = ^integer;
 ParamBlockFake = array[0..30] of integer;
   var
 rate1, rate2, rate3, rate4 : integer;
 MyFTSynth : FTSynthRec;
 myFTSound : FTSoundRec;
 SinWave : packed array[0..255] of char;

 blockA, blockB : ParamBlockFake;{ for myStartSound }
 AUsed : boolean;{ for MyStartSound }

{ Paste myStartSound here }

{ Paste FillSinWave here }

begin
   FillSinWave;
   ShowText;

   MyFTSynth.mode := FTMode;
   MyFTSynth.SndRec := @MyFTSound;

   with MyFTSound do
 begin
    Duration := 1000;{ will be made infinite later }

    Sound1Wave := @SinWave[0];
    Sound2Wave := @SinWave[0];
    Sound3Wave := @SinWave[0];
    Sound4Wave := @SinWave[0];

    MyStartSound(@MyFTSynth, Sizeof(MyFTSynth), nil);

    Rate1 := BaseNote;
    Rate2 := BaseNote;
    Rate3 := BaseNote;
    Rate4 := BaseNote;
    repeat
 begin
    Duration := 1000;{ keep going forever }
    Rate1 := Rate1 + Rate1 div 8;
    Sound1Rate := FixRatio(Rate1, 256);
    if Rate1 > 2 * BaseNote then
 begin
    Rate1 := BaseNote;
    Rate2 := Rate2 + Rate2 div 8;
    Sound2Rate := FixRatio(Rate2, 256);
    if Rate2 > 2 * BaseNote then
 begin
    Rate2 := BaseNote;
    Rate3 := Rate3 + Rate3 div 8;
    Sound3Rate := FixRatio(Rate3, 256);
    if Rate3 > 2 * BaseNote then
 begin
    Rate3 := BaseNote;
    Rate4 := Rate4 + Rate4 div 8;
    Sound4Rate := FixRatio(Rate4, 256);
     if Rate4 > 2 * BaseNote then
 Rate4 := BaseNote;
 end;{ rate3 }
 end;{ rate2 }
 end;{ rate 1 }
    end
 until button;
   end;{ with MyFTSound }
   StopSound;
end.

You may find it interesting to experment with different waveforms in this program. Those musical types among us will probably be able to modify the numbers and get a proper musical scale.

The Free Form mode

With the Free Form synthesizer you are able to play a digitized sound of almost any length and composition. The data used is simply an array with the sound bytes in it. Astonishingly realistic sounds can be produced. Some popular games for the Mac use this mode to create sound effects that are derived from recordings of real sounds.

In the examples I present we will calculate the data to produce and entire second of sound, ie. 22,200 bytes. Since it can take some time to calculate so many bytes, we will break the task into two parts. One program will calculate the bytes, and another will be used to play them. Once you have spent a long time producing a complicated sound effect it would be nice to have a place to put it until you want it again. The natural place would be the Scrapbook. In order to get to the Scrapbook it is necessary to first make it to the clipboard. In the Oct. 85 (Vol.1, No.11) issue of MacTutor I presented a routine to save Quickdraw pictures of type 'PICT' on the clipboard. The name of this was 'Pict_to_Clip'. Allow me to present Wave_to_Clip. You will find it embedded in the following program that calculates one second of a pure tone.

program FFSine_Wave_to_Clip;
   uses
 Sane;
   const
 WTSize = 22200;{ 370*60=one second of sound }
   type
 ptr = ^integer;
 Handle = ^ptr;

 MySynthH = ^MySynthP;
 MySynthP = ^MySynthRec;

 mySynthRec = record
    mode : integer;
    rate : fixed;
    WaveBytes : packed array[0..22199] of char;
 end;

   var
 waveH : MySynthH;
 i, n, ticks, samples : integer;
 pi, Freq : extended;

   procedure Wave_to_Clip (waveH : MySynthH;
          name : str255);
 var
    TheType, llength, lll : longint;
    str : str255;
 { The Hlock that is predefined does not work!!! }
 procedure Hlock (H : Handle);
    var
    regs : array[0..12] of longint;
 begin
    regs[0] := ord(h);{ set A0 }
    Generic($A029, regs);
 end;{ of Hlock }
   begin{ of Wave_to_clip }
 lll := LinlineF($A9FC);{ ZeroScrap }
 str := 'WAVE';
 BlockMove(@str[1], @TheType, 4);{ TheType:='WAVE' }
 llength := GetHandleSize(waveH);
 Hlock(pointer(ord(waveH)));
 lll := LinlineF($A9FE, llength, theType, waveH^);
 { PutScrap}
 Hunlock(pointer(ord(waveH)));

 llength := Length(name);
 str := 'TEXT';
 BlockMove(@str[1], @TheType, 4);{ TheType:='TEXT' }
 lll := LinlineF($A9FE, llength, theType, @name[1]);
 { PutScrap}
   end;

begin
   waveH := NewHandle(WTSize + 6);
   waveH^^.mode := FFMode;
   waveH^^.rate := FixRatio(1, 1);

   pi := arcTan(1) * 4;
   Freq := 2 * pi / 22200 * 1024;{1024 Hz }

   ShowDrawing;
   textmode(srcCopy);
   i := 0;
   for ticks := 1 to 60 do
 begin
    moveto(1, 256 - n);
    for samples := 1 to 370 do
 begin
    n := num2integer(sin(i * Freq) * 64 + 128);
    waveH^^.WaveBytes[i] := chr(n);
    lineto(samples, 256 - n);
    i := i + 1;
 end;
    DrawString(Stringof(ticks));
 end;
   Wave_to_Clip(waveH, 'Simple tone');
   DisposeHandle(waveH);
end.

The program FFSine_Wave_to_Clip first allocates, from the heap, a handle to some data of the correct size. The mode and rate fields (rate works the same as in Four Tone mode) are set and then a sine wave for 22,200 bytes is calculated. The sine wave calculation is much the same as when we calculated a wave table for Four Tone mode. The index, i, changes from 0 to 22199 and is multiplied by a frequency to obtain exactly 1024 full cycles. The purpose of using two nested loops, one for the samples, and for the ticks, is so that we can present a graphical depiction of what is happening (one ticks worth at a time). When the WaveBytes array is filled we pass it to Wave_to_Clip.

The first thing Wave_to_Clip does is to empty the clipboard of any prevoius data. This is the purpose of the ZeroScrap call (refer to the Scrap Manager chapter of Inside Mac). Then a BlockMove is done to move the ASCII bytes for 'WAVE' into a 4 byte data structure (ResType=packed array [0..3] of char is not assignable in MacPascal). In another Pascal you could just say TheType:='WAVE'. Then the length of the data is found and the data is put into the scrap, using PutScrap. When I first started doing this I ended up with lots of Scrapbook items of type 'WAVE', and the Scrapbook window would say "no data of type TEXT or PICT is available". There was no way of knowing what each Scrapbook item was. Then I found that if you put some TEXT on the scrap, then it would go along for the ride. This way the WAVEs in your scrapbook can have names. Therefore, when you call Wave_To_Clip you pass a string that will used to identify that wave.

Now that we have a sound in our Scrapbook let's look at a program to play it.

program Play_Clip_Wave;
   type
 Ptr = ^integer;
 ParamBlockFake = array[0..30] of integer;

 MySynthH = ^MySynthP;
 MySynthP = ^FFSynthRec;

   var
 waveH : MySynthH;

 TheType, offset, Size : Longint;
 str : str255;

 blockA, blockB : ParamBlockFake;
 AUsed : boolean;

{ Paste MyStartSound here }

begin
   ShowText;
   waveH := NewHandle(12);
   str := 'WAVE';
   BlockMove(@str[1], @TheType, 4);{ TheType:='WAVE' }
   size := LinlineF($A9FD, waveH, TheType, @offset);
 { GetScrap }

   if size > 0 then
 begin
    repeat
 myStartSound(waveH^, size - 6, nil);
    until button;
    StopSound;
          DisposeHandle(waveH);
 end
   else
 writeln('no scrap of type ''WAVE'' is available');
end. 

As you can see there is hardly anything to it. Once TheType is set up we call GetScrap which fills the handle with the data and returns the size of the data. If no data was returned then size is zero. This happens when you forget to copy the sound from the Scrapbook before you start.

I found two important things in the development of this routine. First of all, unless the length of the sound is an exact number of ticks, there will be a nasty click between each StartSound. This is because if the Sound Driver runs out of data, part way through the 370 bytes it is filling, it does not get the next sound right away. It waits until the next tick to pull the next sound command from the Device Managers I/O queue. Secondly, the examples in the Sound Driver chapter always pass sizeof(waveH^^) to StartSound. This is very wrong. You should pass the length of the actual sound data and not include the mode and rate field in the length. This is why we pass size-6 to myStartSound. Between the two of these I underwent a lot of grief (and missed my deadline) just to get the smooth, seamless, tone that FFSin_Wave_to_Clip and Play_Clip_Wave will make together.

Want to make a different sound? Just change the calculation. If you put this (below) in the nested loops you get a loud hiss, like the inside of a jet, or an FM radio tuned to nothing.

 n := random mod 256;
 waveH^^.WaveBytes[i] := chr(n);

I tried a different version of this that is much less wild. Instead of purely random numbers this (below) produces a wave form like a 'drunks walk'.

 repeat
    n := n - (random div 8192);
 until (n >= 0) and (n <= 255);
 waveH^^.WaveBytes[i] := chr(n);

Surprisingly, it sounded almost the same as the first, only softer. Also, since no provisions were made for the wave to end at the same value at which it started (it wanders all around), there is a faint click every second. Various combinations of random noise and pure tones might produce interesting effects. Also, you could use these methods to test methods of digital filtering (although not exactly in real time), or novel forms of music synthesis. Novel waveforms could also be used, and tested, in the Four Tone mode.

Fig. 1 Sound Representation

As a final exercise I have attempted to produce the "endless scale" effect. The effect is supposed to be one of a sound continuously increasing in pitch without ever going off the scale. A sort of aural illusion. The way you produce it is to combine many tones, some high, and some low. When they all are mixed (added) together they sound like one single noise. Then, as they all smoothly move together upward in pitch, the higher notes are faded out as new low notes are faded in. In my attempt I use one pure tone for each of 5 octaves (musically, two tones are an octave apart if the frequency of one is twice the frequency of the other). I had to work quite a while on the math to get it so that the last few cycles of the wave exactly match those of the first few. I did it, but the result is a less compelling illusion than I had hoped. Even though there is no definable place where the sound starts again, or drops lower. If you want to see for yourself then try the following program. Be warned, it takes 105 minutes to do the entire calculation. It would be much faster if I had used tables for the sine calculations.

I hoped you enjoyed this months expedition into the world of Macintosh sound, and I'll see you next month.

program FF_Endless_Scale_to_Clip;
   uses
 sane;
   const
   WTSize = 22200;{ 370*60=one second of sound }
   type
 ptr = ^integer;
 Handle = ^ptr;
 MySynthH = ^MySynthP;
 MySynthP = ^MySynthRec;

 mySynthRec = record
    mode : integer;
      rate : fixed;
      WaveBytes : packed array[0..22199] of char;
 { WTSize bytes }
 end;
   var
 waveH : MySynthH;
 j, n, ticks, retraces : integer;
 i, pi, pi_div2, pi_div2_5, T : extended;
 angle, inc, Octave, period, Period_div2 : extended;

 { Paste Wave_to_Clip here }

begin
   waveH := NewHandle(WTSize + 6);{ size of mySynthRec }
   waveH^^.mode := FFMode;
   waveH^^.rate := FixRatio(1, 1);

   pi := arcTan(1) * 4;
   pi_div2_5 := pi / 2.5;
   pi_div2 := pi / 2;
   Period := 1 / 22200;
   Period_div2 := period / 2;
   inc := 2 * pi / 22200 * 256;{ 256 cycles/sec }
   angle := 0;

   ShowDrawing;
   textmode(srcCopy);
   n := 128;
   for ticks := 0 to 59 do{ 60 ticks }
 begin
    moveto(0, 256 - n);
    for retraces := 0 to 369 do{ 370 retraces }
 begin
    i := retraces + ticks * 370;
    angle := inc * (i + (i * i + i) * period_div2);
    T := 0;
    Octave := Log2(1 + i * period) * pi_div2_5 - pi_div2;

    for j := 0 to 4 do
 begin
    T := T + Sin(angle) * (1 + Sin(Octave));
    angle := angle + angle;
    Octave := Octave + pi_div2_5;
 end;

 n := num2integer(T * 32 + 128);
 waveH^^.WaveBytes[num2integer(i)] := chr(n);
 lineto(retraces, 256 - n);
    end;{ of retraces loop }
 DrawString(Stringof(ticks));
   end;{ of ticks loop }
   Wave_to_Clip(waveH, 'Endless Scale Waveform');
   DisposeHandle(waveH);
end.
 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Artlantis Studio 5.1.2.7 - 3D rendering...
Artlantis Studio is a unique and ideal tool for performing very high resolution rendering easily and in real time. The new FastRadiosity engine now lets you compute images in radiosity-even in... Read more
MacUpdate Desktop 6.0.5 - Search and ins...
MacUpdate Desktop 6 brings seamless 1-click installs and version updates to your Mac. With a free MacUpdate account and MacUpdate Desktop 6, Mac users can now install almost any Mac app on macupdate.... Read more
BitTorrent Sync 2.0.82 - Sync files secu...
BitTorrent Sync allows you to sync unlimited files between your own devices, or share a folder with friends and family to automatically sync anything. File transfers are encrypted. Your information... Read more
Google Drive 1.20 - File backup and shar...
Google Drive is a place where you can create, share, collaborate, and keep all of your stuff. Whether you're working with a friend on a joint research project, planning a wedding with your fiancé, or... Read more
Simon 4.0.3 - Monitor changes and crashe...
Simon monitors websites and alerts you of crashes and changes. Select pages to monitor, choose your alert options, and customize your settings. Simon does the rest. Keep a watchful eye on your... Read more
Vitamin-R 2.23 - Personal productivity t...
Vitamin-R creates the optimal conditions for your brain to work at its best by structuring your work into short bursts of distraction-free, highly focused activity alternating with opportunities for... Read more
iDefrag 5.0.0 - Disk defragmentation and...
iDefrag helps defragment and optimize your disk for improved performance. Features include: Supports HFS and HFS+ (Mac OS Extended). Supports case sensitive and journaled filesystems. Supports... Read more
PCalc 4.2 - Full-featured scientific cal...
PCalc is a full-featured, scriptable scientific calculator with support for hexadecimal, octal, and binary calculations, as well as an RPN mode, programmable functions, and an extensive set of unit... Read more
FileZilla 3.10.2 - Fast and reliable FTP...
FileZilla (ported from Windows) is a fast and reliable FTP client and server with lots of useful features and an intuitive interface. Version 3.10.2: Note: Now requires a 64-bit Intel processor.... Read more
The Hit List 1.1.11 - Advanced reminder...
The Hit List manages the daily chaos of your modern life. It's easy to learn - it's as easy as making lists. And it's powerful enough to let you plan, then forget, then act when the time is right.... Read more

Warner Bros. Interactive Entertainment A...
Warner Bros. has some exciting games coming down the pipe! | Read more »
GDC 2015 – Star Trek Timelines will Prob...
GDC 2015 – Star Trek Timelines will Probably Make Your Inner Trekkie Squeal With Glee Posted by Rob Rich on March 4th, 2015 [ permalink ] Any popular fictional universe has its fair share of fan fiction – where belo | Read more »
Protect Yourself from an Onslaught of Ca...
Surprise Attack Games has announced a Cat-astrophic new physics puzzler called Fort Meow! In the game, a young girl named Nia finds her grandfather’s journal which triggers an all mighty feline attack! Why do the cats want the journal? Who knows,... | Read more »
GDC 2015 – Jelly Reef will be Game Oven’...
GDC 2015 – Jelly Reef will be Game Oven’s Last Hurrah, and it Seems like a Good Note to Go Out on Posted by Rob Rich on March 4th, 2015 [ permalink ] It’s sad knowing that Game Oven ( | Read more »
daWindci Deluxe Review
daWindci Deluxe Review By Campbell Bird on March 4th, 2015 Our Rating: :: BLUSTERY PUZZLESUniversal App - Designed for iPhone and iPad This updated puzzle game offers some creative gameplay and new mechanics, but still suffers from... | Read more »
Dungeon Hunter 5 Coming on March 12
Gameloft has excitedly announced that Dungeon Hunter 5 is on its way! Once again, you will adventure across the land of Valenthia exploring dungeons and fighting monsters. The game will have a new asynchronous multiplayer mode called Strongholds... | Read more »
GDC 2015 – The Sandbox 2 is Coming, and...
GDC 2015 – The Sandbox 2 is Coming, and Now it has Textures! | Read more »
Warner Bros. Interactive Announces Mort...
Mortal Kombat X, by Warner Bros. and NetherRealm Studios, will be a a free-to-play fighting/card-battle Mortal Kombat game. The game promises card collecting, multiplayer team combat, classic characters such as Scorpion, Sub-Zero and Raiden, and the... | Read more »
GDC 2015 – Piloteer is Whitaker Trebella...
GDC 2015 – Piloteer is Whitaker Trebella’s Latest Project, and it’s Definitely Something DIfferent Posted by Rob Rich on March 3rd, 2015 [ permalink ] You know | Read more »
PangoLand Review
PangoLand Review By Amy Solomon on March 3rd, 2015 Our Rating: :: COME VISIT PANGO AND FRIENDSUniversal App - Designed for iPhone and iPad PangoLand is an open-ended world full of familiar characters, bright colors and interactive... | Read more »

Price Scanner via MacPrices.net

iPad: A More Positive Outlook – The ‘Book Mys...
It’s good to hear someone saying positive things about the iPad. I’ve been trying to bend my mind around how Apple’s tablet could have gone from zero to bestselling personal computing device on the... Read more
Mac Pros on sale for up to $279 off MSRP
Amazon has Mac Pros in stock and on sale for up to $279 off MSRP. Shipping is free: - 4-Core Mac Pro: $2725.87, $273 off MSRP (9%) - 6-Core Mac Pro: $3719.99, $279 off MSRP (7%) Read more
Sale! 13-inch Retina MacBook Pros for up to $...
B&H Photo has 13″ Retina MacBook Pros on sale for up to $205 off MSRP. Shipping is free, and B&H charges NY sales tax only: - 13″ 2.6GHz/128GB Retina MacBook Pro: $1219.99 save $80 - 13″ 2.... Read more
Another Tranche Of IBM MobileFirst For iOS Ap...
IBM has announced the next expansion phase for  its IBM MobileFirst for iOS portfolio, with a troika of new apps to address key priorities for the Banking and Financial Services, Airline and Retail... Read more
Sale! 15-inch Retina MacBook Pros for up to $...
B&H Photo has the new 2014 15″ Retina MacBook Pros on sale for up to $250 off MSRP for a limited time. Shipping is free, and B&H charges NY sales tax only: - 15″ 2.2GHz Retina MacBook Pro: $... Read more
WaterField Designs Introduces the Minimalist...
With Apple Pay gaining popularity, Android Pay coming in May 2015, and loyalty cards and receipts that can be accessed from smartphones, San Francisco’s WaterField Designs observes that it may be... Read more
Sale! 15-inch 2.2GHz Retina MacBook Pro for $...
 Best Buy has the 15″ 2.2GHz Retina MacBook Pro on sale for $1774.99 $1799.99, or $225 off MSRP. Choose free home shipping or free local store pickup (if available). Price valid for online orders... Read more
13-inch 2.5GHz MacBook Pro (refurbished) avai...
The Apple Store has Apple Certified Refurbished 13″ 2.5GHz MacBook Pros available for $170 off the cost of new models. Apple’s one-year warranty is standard, and shipping is free: - 13″ 2.5GHz... Read more
13-inch 2.5GHz MacBook Pro on sale for $100 o...
B&H Photo has the 13″ 2.5GHz MacBook Pro on sale for $999.99 including free shipping plus NY sales tax only. Their price is $100 off MSRP. Read more
27-inch 3.5GHz 5K iMac in stock today and on...
 B&H Photo has the 27″ 3.5GHz 5K iMac in stock today and on sale for $2299 including free shipping plus NY sales tax only. Their price is $200 off MSRP, and it’s the lowest price available for... Read more

Jobs Board

*Apple* Solutions Consultant - Retail Sales...
**Job Summary** As an Apple Solutions Consultant (ASC) you are the link between our customers and our products. Your role is to drive the Apple business in a retail Read more
Position Opening at *Apple* - Apple (United...
…Summary** As a Specialist, you help create the energy and excitement around Apple products, providing the right solutions and getting products into customers' hands. You Read more
Position Opening at *Apple* - Apple (United...
**Job Summary** The Apple Store is a retail environment like no other - uniquely focused on delivering amazing customer experiences. As an Expert, you introduce people Read more
*Apple* Solutions Consultant - Retail Sales...
**Job Summary** As an Apple Solutions Consultant (ASC) you are the link between our customers and our products. Your role is to drive the Apple business in a retail Read more
*Apple* Pay Automation Engineer - iOS System...
**Job Summary** At Apple , great ideas have a way of becoming great products, services, and customer experiences very quickly. Bring passion and dedication to your job Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.