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
$99.02
Apple Inc.
+1.35
MSFT
$43.97
Microsoft Corpora
-0.53
GOOG
$590.60
Google Inc.
+1.58

MacTech Search:
Community Search:

Software Updates via MacUpdate

Acorn 4.4 - Bitmap image editor. (Demo)
Acorn is a new image editor built with one goal in mind - simplicity. Fast, easy, and fluid, Acorn provides the options you'll need without any overhead. Acorn feels right, and won't drain your bank... Read more
Bartender 1.2.20 - Organize your menu ba...
Bartender lets you organize your menu bar apps. Features: Lets you tidy your menu bar apps how you want. See your menu bar apps when you want. Hide the apps you need to run, but do not need to... Read more
TotalFinder 1.6.2 - Adds tabs, hotkeys,...
TotalFinder is a universally acclaimed navigational companion for your Mac. Enhance your Mac's Finder with features so smart and convenient, you won't believe you ever lived without them. Tab-based... Read more
Vienna 3.0.0 RC 2 :be5265e: - RSS and At...
Vienna is a freeware and Open-Source RSS/Atom newsreader with article storage and management via a SQLite database, written in Objective-C and Cocoa, for the OS X operating system. It provides... Read more
VLC Media Player 2.1.5 - Popular multime...
VLC Media Player is a highly portable multimedia player for various audio and video formats (MPEG-1, MPEG-2, MPEG-4, DivX, MP3, OGG, ...) as well as DVDs, VCDs, and various streaming protocols. It... Read more
Default Folder X 4.6.7 - Enhances Open a...
Default Folder X attaches a toolbar to the right side of the Open and Save dialogs in any OS X-native application. The toolbar gives you fast access to various folders and commands. You just click... Read more
TinkerTool 5.3 - Expanded preference set...
TinkerTool is an application that gives you access to additional preference settings Apple has built into Mac OS X. This allows to activate hidden features in the operating system and in some of the... Read more
Audio Hijack Pro 2.11.0 - Record and enh...
Audio Hijack Pro drastically changes the way you use audio on your computer, giving you the freedom to listen to audio when you want and how you want. Record and enhance any audio with Audio Hijack... Read more
Intermission 1.1.1 - Pause and rewind li...
Intermission allows you to pause and rewind live audio from any application on your Mac. Intermission will buffer up to 3 hours of audio, allowing users to skip through any assortment of audio... Read more
Autopano Giga 3.6 - Stitch multiple imag...
Autopano Giga allows you to stitch 2, 20, or 2,000 images. Version 3.0 integrates impressive new features that will definitely make you adopt Autopano Pro or Autopano Giga: Choose between 9... Read more

Latest Forum Discussions

See All

Traps n’ Gemstones Review
Traps n’ Gemstones Review By Campbell Bird on July 28th, 2014 Our Rating: :: CASTLEVANIA JONESUniversal App - Designed for iPhone and iPad Fight mummies, dig tunnels, and ride a runaway minecart to discover ancient secrets in this... | Read more »
The Phantom PI Mission Apparition Review
The Phantom PI Mission Apparition Review By Jordan Minor on July 28th, 2014 Our Rating: :: GHOSTS BUSTEDUniversal App - Designed for iPhone and iPad The Phantom PI is an exceedingly clever and well-crafted adventure game.   | Read more »
More Stubies Are Coming Your Way in a Ne...
More Stubies Are Coming Your Way in a New Update Posted by Jessica Fisher on July 28th, 2014 [ permalink ] Universal App - Designed for iPhone and iPad | Read more »
The Great Prank War Review
The Great Prank War Review By Nadia Oxford on July 28th, 2014 Our Rating: :: PRANKING IS SERIOUS BUSINESSUniversal App - Designed for iPhone and iPad Though short, The Great Prank War offers an interesting and fun mix of action and... | Read more »
Marvel Contest of Champions Announced at...
Marvel Contest of Champions Announced at Comic-Con Posted by Jennifer Allen on July 28th, 2014 [ permalink ] Announced over the weekend at San Diego Comic-Con was the fairly exciting looking Marvel Contest of Champions. | Read more »
Teenage Mutant Ninja Turtles Review
Teenage Mutant Ninja Turtles Review By Jennifer Allen on July 28th, 2014 Our Rating: :: DULL SWIPINGUniversal App - Designed for iPhone and iPad The pizza power is weak when it comes to this Teenage Mutant Ninja Turtles game.   | Read more »
Exploration Focused Puzzle Game Beatbudd...
Exploration Focused Puzzle Game Beatbuddy Set to Make Transition from PC to iOS this September Posted by Jennifer Allen on July 28th, 2014 [ permalink ] | Read more »
PlanetHD
PlanetHD By Nadia Oxford on July 28th, 2014 Our Rating: :: SPACE MADNESSUniversal App - Designed for iPhone and iPad PlanetHD will keep players busy for a while, though its unpredictable physics are a handful to deal with.   | Read more »
This Week at 148Apps: July 21-25, 2014
Another Week of Expert App Reviews   At 148Apps, we help you sort through the great ocean of apps to find the ones we think you’ll like and the ones you’ll need. Our top picks become Editor’s Choice, our stamp of approval for apps with that little... | Read more »
Reddme for iPhone - The Reddit Client (...
Reddme for iPhone - The Reddit Client 1.0 Device: iOS iPhone Category: News Price: $.99, Version: 1.0 (iTunes) Description: Reddme for iPhone is an iOS 7-optimized Reddit client that offers a refreshing new way to experience Reddit... | Read more »

Price Scanner via MacPrices.net

13-inch 2.5GHz MacBook Pro on sale for $1099,...
Best Buy has the 13″ 2.5GHz MacBook Pro available for $1099.99 on their online store. Choose free shipping or free instant local store pickup (if available). Their price is $100 off MSRP. Price is... Read more
Roundup of Apple refurbished MacBook Pros, th...
The Apple Store has Apple Certified Refurbished 13″ and 15″ MacBook Pros available for up to $400 off the cost of new models. Apple’s one-year warranty is standard, and shipping is free. Their prices... Read more
Record Mac Shipments In Q2/14 Confound Analys...
A Seeking Alpha Trefis commentary notes that Apple’s fiscal Q3 2014 results released July 22, beat market predictions on earnings, although revenues were slightly lower than anticipated. Apple’s Mac’... Read more
Intel To Launch Core M Silicon For Use In Not...
Digitimes’ Monica Chen and Joseph Tsai, report that Intel will launch 14nm-based Core M series processors specifically for use in fanless notebook/tablet 2-in-1 models in Q4 2014, with many models to... Read more
Apple’s 2014 Back to School promotion: $100 g...
 Apple’s 2014 Back to School promotion includes a free $100 App Store Gift Card with the purchase of any new Mac (Mac mini excluded), or a $50 Gift Card with the purchase of an iPad or iPhone,... Read more
iMacs on sale for $150 off MSRP, $250 off for...
Best Buy has iMacs on sale for up to $160 off MSRP for a limited time. Choose free home shipping or free instant local store pickup (if available). Prices are valid for online orders only, in-store... Read more
Mac minis on sale for $100 off MSRP, starting...
Best Buy has Mac minis on sale for $100 off MSRP. Choose free shipping or free instant local store pickup. Prices are for online orders only, in-store prices may vary: 2.5GHz Mac mini: $499.99 2.3GHz... Read more
Global Tablet Market Grows 11% in Q2/14 Notwi...
Worldwide tablet sales grew 11.0 percent year over year in the second quarter of 2014, with shipments reaching 49.3 million units according to preliminary data from the International Data Corporation... Read more
New iPhone 6 Models to Have Staggered Release...
Digitimes’ Cage Chao and Steve Shen report that according to unnamed sources in Apple’s upstream iPhone supply chain, the new 5.5-inch iPhone will be released several months later than the new 4.7-... Read more
New iOS App Helps People Feel Good About thei...
Mobile shoppers looking for big savings at their favorite stores can turn to the Goodshop app, a new iOS app with the latest coupons and deals at more than 5,000 online stores. In addition to being a... Read more

Jobs Board

*Apple* Solutions Consultant (ASC) - Apple (...
**Job Summary** The ASC is an Apple employee who serves as an Apple brand ambassador and influencer in a Reseller's store. The ASC's role is to grow Apple Read more
Sr. Product Leader, *Apple* Store Apps - Ap...
**Job Summary** Imagine what you could do here. At Apple , great ideas have a way of becoming great products, services, and customer experiences very quickly. Bring Read more
*Apple* Solutions Consultant (ASC) - Apple (...
**Job Summary** The ASC is an Apple employee who serves as an Apple brand ambassador and influencer in a Reseller's store. The ASC's role is to grow Apple Read more
*Apple* Solutions Consultant (ASC) - Apple (...
**Job Summary** The ASC is an Apple employee who serves as an Apple brand ambassador and influencer in a Reseller's store. The ASC's role is to grow Apple Read more
WW Sales Program Manager, *Apple* Online St...
**Job Summary** Imagine what you could do here. At Apple , great ideas have a way of becoming great products, services, and customer experiences very quickly. Bring Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.