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.
 
AAPL
$111.78
Apple Inc.
-0.87
MSFT
$47.66
Microsoft Corpora
+0.14
GOOG
$516.35
Google Inc.
+5.25

MacTech Search:
Community Search:

Software Updates via MacUpdate

LibreOffice 4.3.5.2 - Free Open Source o...
LibreOffice is an office suite (word processor, spreadsheet, presentations, drawing tool) compatible with other major office suites. The Document Foundation is coordinating development and... Read more
CleanApp 5.0.0 Beta 5 - Application dein...
CleanApp is an application deinstaller and archiver.... Your hard drive gets fuller day by day, but do you know why? CleanApp 5 provides you with insights how to reclaim disk space. There are... Read more
Monolingual 1.6.2 - Remove unwanted OS X...
Monolingual is a program for removing unnecesary language resources from OS X, in order to reclaim several hundred megabytes of disk space. It requires a 64-bit capable Intel-based Mac and at least... Read more
NetShade 6.1 - Browse privately using an...
NetShade is an Internet security tool that conceals your IP address on the web. NetShade routes your Web connection through either a public anonymous proxy server, or one of NetShade's own dedicated... Read more
calibre 2.13 - Complete e-library manage...
Calibre is a complete e-book library manager. Organize your collection, convert your books to multiple formats, and sync with all of your devices. Let Calibre be your multi-tasking digital librarian... Read more
Mellel 3.3.7 - Powerful word processor w...
Mellel is the leading word processor for OS X and has been widely considered the industry standard since its inception. Mellel focuses on writers and scholars for technical writing and multilingual... Read more
ScreenFlow 5.0.1 - Create screen recordi...
Save 10% with the exclusive MacUpdate coupon code: AFMacUpdate10 Buy now! ScreenFlow is powerful, easy-to-use screencasting software for the Mac. With ScreenFlow you can record the contents of your... Read more
Simon 4.0 - Monitor changes and crashes...
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
BBEdit 11.0.2 - Powerful text and HTML e...
BBEdit is the leading professional HTML and text editor for the Mac. Specifically crafted in response to the needs of Web authors and software developers, this award-winning product provides a... Read more
ExpanDrive 4.2.1 - Access cloud storage...
ExpanDrive builds cloud storage in every application, acts just like a USB drive plugged into your Mac. With ExpanDrive, you can securely access any remote file server directly from the Finder or... Read more

Latest Forum Discussions

See All

Make your own Tribez Figures (and More)...
Make your own Tribez Figures (and More) with Toyze Posted by Jessica Fisher on December 19th, 2014 [ permalink ] Universal App - Designed for iPhone and iPad | Read more »
So Many Holiday iOS Sales Oh My Goodness...
The holiday season is in full-swing, which means a whole lot of iOS apps and games are going on sale. A bunch already have, in fact. Naturally this means we’re putting together a hand-picked list of the best discounts and sales we can find in order... | Read more »
It’s Bird vs. Bird in the New PvP Mode f...
It’s Bird vs. Bird in the New PvP Mode for Angry Birds Epic Posted by Jessica Fisher on December 19th, 2014 [ permalink ] Universal App - Designed for iPhone and iPad | Read more »
Telltale Games and Mojang Announce Minec...
Telltale Games and Mojang Announce Minecraft: Story Mode – A Telltale Games Series Posted by Jessica Fisher on December 19th, 2014 [ permalink ] | Read more »
WarChest and Splash Damage Annouce Their...
WarChest and Splash Damage Annouce Their New Game: Tempo Posted by Jessica Fisher on December 19th, 2014 [ permalink ] WarChest Ltd and Splash Damage Ltd are teaming up again to work | Read more »
BulkyPix Celebrates its 6th Anniversary...
BulkyPix Celebrates its 6th Anniversary with a Bunch of Free Games Posted by Jessica Fisher on December 19th, 2014 [ permalink ] BulkyPix has | Read more »
Indulge in Japanese cuisine in Cooking F...
Indulge in Japanese cuisine in Cooking Fever’s new sushi-themed update Posted by Simon Reed on December 19th, 2014 [ permalink ] Lithuanian developer Nordcurrent has yet again updated its restaurant simulat | Read more »
Badland Daydream Level Pack Arrives to C...
Badland Daydream Level Pack Arrives to Celebrate 20 Million Downloads Posted by Ellis Spice on December 19th, 2014 [ permalink ] | Read more »
Far Cry 4, Assassin’s Creed Unity, Desti...
Far Cry 4, Assassin’s Creed Unity, Destiny, and Beyond – AppSpy Takes a Look at AAA Companion Apps Posted by Rob Rich on December 19th, 2014 [ permalink ] These day | Read more »
A Bunch of Halfbrick Games Are Going Fre...
A Bunch of Halfbrick Games Are Going Free for the Holidays Posted by Ellis Spice on December 19th, 2014 [ permalink ] Universal App - Designed for iPhone and iPad | Read more »

Price Scanner via MacPrices.net

The Apple Store offering free next-day shippi...
The Apple Store is now offering free next-day shipping on all in stock items if ordered before 12/23/14 at 10:00am PT. Local store pickup is also available within an hour of ordering for any in stock... Read more
It’s 1992 Again At Sony Pictures, Except For...
Techcrunch’s John Biggs interviewed a Sony Pictures Entertainment (SPE) employee, who quite understandably wished to remain anonymous, regarding post-hack conditions in SPE’s L.A office, explaining “... Read more
Holiday sales this weekend: MacBook Pros for...
 B&H Photo has new MacBook Pros on sale for up to $300 off MSRP as part of their Holiday pricing. Shipping is free, and B&H charges NY sales tax only: - 15″ 2.2GHz Retina MacBook Pro: $1699... Read more
Holiday sales this weekend: MacBook Airs for...
B&H Photo has 2014 MacBook Airs on sale for up to $120 off MSRP, for a limited time, for the Thanksgiving/Christmas Holiday shopping season. Shipping is free, and B&H charges NY sales tax... Read more
Holiday sales this weekend: iMacs for up to $...
B&H Photo has 21″ and 27″ iMacs on sale for up to $200 off MSRP including free shipping plus NY sales tax only. B&H will also include a free copy of Parallels Desktop software: - 21″ 1.4GHz... Read more
Holiday sales this weekend: Mac minis availab...
B&H Photo has new 2014 Mac minis on sale for up to $80 off MSRP. Shipping is free, and B&H charges NY sales tax only: - 1.4GHz Mac mini: $459 $40 off MSRP - 2.6GHz Mac mini: $629 $70 off MSRP... Read more
Holiday sales this weekend: Mac Pros for up t...
B&H Photo has Mac Pros on sale for up to $500 off MSRP. Shipping is free, and B&H charges sales tax in NY only: - 3.7GHz 4-core Mac Pro: $2599, $400 off MSRP - 3.5GHz 6-core Mac Pro: $3499, $... Read more
Save up to $400 on MacBooks with Apple Certif...
The Apple Store has Apple Certified Refurbished 2014 MacBook Pros and MacBook Airs available for up to $400 off the cost of new models. An Apple one-year warranty is included with each model, and... Read more
Save up to $300 on Macs, $30 on iPads with Ap...
Purchase a new Mac or iPad at The Apple Store for Education and take up to $300 off MSRP. All teachers, students, and staff of any educational institution qualify for the discount. Shipping is free,... Read more
iOS and Android OS Targeted by Man-in-the-Mid...
Cloud services security provider Akamai Technologies, Inc. has released, through the company’s Prolexic Security Engineering & Research Team (PLXsert), a new cybersecurity threat advisory. The... Read more

Jobs Board

*Apple* Store Leader Program (US) - Apple, I...
…Summary Learn and grow as you explore the art of leadership at the Apple Store. You'll master our retail business inside and out through training, hands-on experience, Read more
Project Manager, *Apple* Financial Services...
**Job Summary** Apple Financial Services (AFS) offers consumers, businesses and educational institutions ways to finance Apple purchases. We work with national and Read more
*Apple* Retail - Multiple Positions (US) - A...
Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, you're also the Read more
*Apple* Retail - Multiple Positions (US) - A...
Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, you're also the Read more
*Apple* Retail - Multiple Positions (US) - A...
Job Description: Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.