Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

The Archive Base

The Archive Base Logo The Archive Base Logo

The Archive Base Navigation

  • SEARCH
  • Home
  • About Us
  • Blog
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • Add group
  • Groups page
  • Feed
  • User Profile
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Buy Points
  • Users
  • Help
  • Buy Theme
  • SEARCH
Home/ Questions/Q 7752785
In Process

The Archive Base Latest Questions

Editorial Team
  • 0
Editorial Team
Asked: June 1, 20262026-06-01T11:53:16+00:00 2026-06-01T11:53:16+00:00

I’m creating a old school music emulator for the old GWBasic PLAY command. To

  • 0

I’m creating a old school music emulator for the old GWBasic PLAY command. To that end I have a tone generator and a music player. Between each of the notes played I’m getting a chirp sound that mucking things up. Below are both of my classes:

ToneGen.h

#import <Foundation/Foundation.h>

@interface ToneGen : NSObject
@property (nonatomic) id delegate;
@property (nonatomic) double frequency;
@property (nonatomic) double sampleRate;
@property (nonatomic) double theta;
- (void)play:(float)ms;
- (void)play;
- (void)stop;
@end

ToneGen.m

#import <AudioUnit/AudioUnit.h>
#import "ToneGen.h"

OSStatus RenderTone(
                    void *inRefCon, 
                    AudioUnitRenderActionFlags  *ioActionFlags, 
                    const AudioTimeStamp        *inTimeStamp, 
                    UInt32                      inBusNumber, 
                    UInt32                      inNumberFrames, 
                    AudioBufferList             *ioData);
void ToneInterruptionListener(void *inClientData, UInt32 inInterruptionState);


@interface ToneGen()
@property (nonatomic) AudioComponentInstance toneUnit;
@property (nonatomic) NSTimer *timer;
- (void)createToneUnit;
@end


@implementation ToneGen
@synthesize toneUnit = _toneUnit;
@synthesize timer = _timer;
@synthesize delegate = _delegate;
@synthesize frequency = _frequency;
@synthesize sampleRate = _sampleRate;
@synthesize theta = _theta;

- (id) init
{
    self = [super init];
    if (self)
    {
        self.sampleRate = 44100;
        self.frequency = 1440.0f;
        return self;
    }
    return nil;
}

- (void)play:(float)ms
{
    [self play];
    self.timer = [NSTimer scheduledTimerWithTimeInterval:(ms / 100) 
                                                  target:self 
                                                selector:@selector(stop) 
                                                userInfo:nil 
                                                 repeats:NO];
    [[NSRunLoop mainRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
}

- (void)play
{
    if (!self.toneUnit)
    {
        [self createToneUnit];

        // Stop changing parameters on the unit
        OSErr err = AudioUnitInitialize(self.toneUnit);
        if (err)
            DLog(@"Error initializing unit");

        // Start playback
        err = AudioOutputUnitStart(self.toneUnit);
        if (err)
            DLog(@"Error starting unit");
    }
}

- (void)stop
{
    [self.timer invalidate];
    self.timer = nil;

    if (self.toneUnit)
    {
        AudioOutputUnitStop(self.toneUnit);
        AudioUnitUninitialize(self.toneUnit);
        AudioComponentInstanceDispose(self.toneUnit);
        self.toneUnit = nil;
    }

    if(self.delegate && [self.delegate respondsToSelector:@selector(toneStop)]) {
        [self.delegate performSelector:@selector(toneStop)];
    }
}

- (void)createToneUnit
{
    AudioComponentDescription defaultOutputDescription;
    defaultOutputDescription.componentType = kAudioUnitType_Output;
    defaultOutputDescription.componentSubType = kAudioUnitSubType_DefaultOutput;
    defaultOutputDescription.componentManufacturer = kAudioUnitManufacturer_Apple;
    defaultOutputDescription.componentFlags = 0;
    defaultOutputDescription.componentFlagsMask = 0;

    // Get the default playback output unit
    AudioComponent defaultOutput = AudioComponentFindNext(NULL, &defaultOutputDescription);
    if (!defaultOutput)
        DLog(@"Can't find default output");

    // Create a new unit based on this that we'll use for output
    OSErr err = AudioComponentInstanceNew(defaultOutput, &_toneUnit);
    if (err)
        DLog(@"Error creating unit");

    // Set our tone rendering function on the unit
    AURenderCallbackStruct input;
    input.inputProc = RenderTone;
    input.inputProcRefCon = (__bridge void*)self;
    err = AudioUnitSetProperty(self.toneUnit, 
                               kAudioUnitProperty_SetRenderCallback, 
                               kAudioUnitScope_Input,
                               0, 
                               &input, 
                               sizeof(input));
    if (err)
        DLog(@"Error setting callback");

    // Set the format to 32 bit, single channel, floating point, linear PCM
    const int four_bytes_per_float = 4;
    const int eight_bits_per_byte = 8;
    AudioStreamBasicDescription streamFormat;
    streamFormat.mSampleRate = self.sampleRate;
    streamFormat.mFormatID = kAudioFormatLinearPCM;
    streamFormat.mFormatFlags =
    kAudioFormatFlagsNativeFloatPacked | kAudioFormatFlagIsNonInterleaved;
    streamFormat.mBytesPerPacket = four_bytes_per_float;
    streamFormat.mFramesPerPacket = 1;  
    streamFormat.mBytesPerFrame = four_bytes_per_float;     
    streamFormat.mChannelsPerFrame = 1; 
    streamFormat.mBitsPerChannel = four_bytes_per_float * eight_bits_per_byte;
    err = AudioUnitSetProperty (self.toneUnit,
                                kAudioUnitProperty_StreamFormat,
                                kAudioUnitScope_Input,
                                0,
                                &streamFormat,
                                sizeof(AudioStreamBasicDescription));
    if (err)
        DLog(@"Error setting stream format");
}

@end


OSStatus RenderTone(
                    void *inRefCon, 
                    AudioUnitRenderActionFlags  *ioActionFlags, 
                    const AudioTimeStamp        *inTimeStamp, 
                    UInt32                      inBusNumber, 
                    UInt32                      inNumberFrames, 
                    AudioBufferList             *ioData)

{
    // Fixed amplitude is good enough for our purposes
    const double amplitude = 0.25;

    // Get the tone parameters out of the view controller
    ToneGen *toneGen = (__bridge ToneGen *)inRefCon;
    double theta = toneGen.theta;
    double theta_increment = 2.0 * M_PI * toneGen.frequency / toneGen.sampleRate;

    // This is a mono tone generator so we only need the first buffer
    const int channel = 0;
    Float32 *buffer = (Float32 *)ioData->mBuffers[channel].mData;

    // Generate the samples
    for (UInt32 frame = 0; frame < inNumberFrames; frame++) 
    {
        buffer[frame] = sin(theta) * amplitude;

        theta += theta_increment;
        if (theta > 2.0 * M_PI)
        {
            theta -= 2.0 * M_PI;
        }
    }

    // Store the theta back in the view controller
    toneGen.theta = theta;

    return noErr;
}

void ToneInterruptionListener(void *inClientData, UInt32 inInterruptionState)
{
    ToneGen *toneGen = (__bridge ToneGen *)inClientData;
    [toneGen stop];
}

Music.h

#import <Foundation/Foundation.h>

@interface Music : NSObject
- (void) play:(NSString *)music;
- (void) stop;
@end

Music.m

#import "Music.h"
#import "ToneGen.h"

@interface Music()
@property (nonatomic, readonly) ToneGen *toneGen;
@property (nonatomic, assign) int octive;
@property (nonatomic, assign) int tempo;
@property (nonatomic, assign) int length;

@property (nonatomic, strong) NSData *music;
@property (nonatomic, assign) int dataPos;
@property (nonatomic, assign) BOOL isPlaying;

- (void)playNote;
@end

@implementation Music
@synthesize toneGen = _toneGen;
- (ToneGen*)toneGen
{
    if (_toneGen == nil)
    {
        _toneGen = [[ToneGen alloc] init];
        _toneGen.delegate = self;
    }
    return _toneGen;
}
@synthesize octive = _octive;
- (void)setOctive:(int)octive
{
    // Sinity Check
    if (octive < 0)
        octive = 0;
    if (octive > 6)
        octive = 6;
    _octive = octive;
}
@synthesize tempo = _tempo;
- (void)setTempo:(int)tempo
{
    // Sinity Check
    if (tempo < 30)
        tempo = 30;
    if (tempo > 255)
        tempo = 255;
    _tempo = tempo;
}
@synthesize length = _length;
- (void)setLength:(int)length
{
    // Sinity Check
    if (length < 1)
        length = 1;
    if (length > 64)
        length = 64;
    _length = length;
}
@synthesize music = _music;
@synthesize dataPos = _dataPos;
@synthesize isPlaying = _isPlaying;


- (id)init
{
    self = [super init];
    if (self)
    {
        self.octive = 4;
        self.tempo = 120;
        self.length = 1;
        return self;
    }
    return nil;
}

- (void) play:(NSString *)music
{
    DLog(@"%@", music);
    self.music = [[music stringByReplacingOccurrencesOfString:@"+" withString:@"#"]
                  dataUsingEncoding: NSASCIIStringEncoding];
    self.dataPos = 0;
    self.isPlaying = YES;
    [self playNote];
}

- (void)stop
{
    self.isPlaying = NO;
}

- (void)playNote
{
    if (!self.isPlaying)
        return;

    if (self.dataPos > self.music.length || self.music.length == 0) {
        self.isPlaying = NO;
        return;
    }

    unsigned char *data = (unsigned char*)[self.music bytes];
    unsigned int code = (unsigned int)data[self.dataPos];
    self.dataPos++;

    switch (code) {
        case 65: // A
        case 66: // B
        case 67: // C
        case 68: // D
        case 69: // E
        case 70: // F
        case 71: // G
            {
                // Peak at the next char to look for sharp or flat
                bool sharp = NO;
                bool flat = NO;
                if (self.dataPos < self.music.length) {
                    unsigned int peak = (unsigned int)data[self.dataPos];
                    if (peak == 35) // #
                    {
                        self.dataPos++;
                        sharp = YES;
                    }
                    else if (peak == 45)  // -
                    {
                        self.dataPos++;
                        flat = YES;
                    }
                }

                // Peak ahead for a length changes
                bool look = YES;
                int count = 0;
                int newLength = 0;
                while (self.dataPos < self.music.length && look) {
                    unsigned int peak = (unsigned int)data[self.dataPos];
                    if (peak >= 48 && peak <= 57)
                    {
                        peak -= 48;
                        int n = (count * 10);
                        if (n == 0) { n = 1; }
                        newLength += peak * n;
                        self.dataPos++;
                    } else {
                        look = NO;
                    }
                }

                // Pick the note length
                int length = self.length;
                if (newLength != 0)
                {
                    DLog(@"InlineLength: %d", newLength);
                    length = newLength;
                }


                // Create the note string
                NSString *note = [NSString stringWithFormat:@"%c", code];
                if (sharp)
                    note = [note stringByAppendingFormat:@"#"];
                else if (flat)
                    note = [note stringByAppendingFormat:@"-"];

                // Set the tone generator freq
                [self setFreq:[self getNoteNumber:note]];

                // Play the note
                [self.toneGen play:(self.tempo / length)];
            }
            break;

        case 76: // L (length)
        {
            bool look = YES;
            int newLength = 0;
            while (self.dataPos < self.music.length && look) {
                unsigned int peak = (unsigned int)data[self.dataPos];
                if (peak >= 48 && peak <= 57)
                {
                    peak -= 48;
                    newLength = newLength * 10 + peak;
                    self.dataPos++;
                } else {
                    look = NO;
                }
            }
            self.length = newLength;
            DLog(@"Length: %d", self.length);
            [self playNote];
        }
            break;

        case 79: // O (octive)
            {
                bool look = YES;
                int newOctive = 0;
                while (self.dataPos < self.music.length && look) {
                    unsigned int peak = (unsigned int)data[self.dataPos];
                    if (peak >= 48 && peak <= 57)
                    {
                        peak -= 48;
                        newOctive = newOctive * 10 + peak;
                        self.dataPos++;
                    } else {
                        look = NO;
                    }
                }
                self.octive = newOctive;
                DLog(@"Octive: %d", self.self.octive);
                [self playNote];
            }
            break;

        case 84: // T (tempo)
            {
                bool look = YES;
                int newTempo = 0;
                while (self.dataPos < self.music.length && look) {
                    unsigned int peak = (unsigned int)data[self.dataPos];
                    if (peak >= 48 && peak <= 57)
                    {
                        peak -= 48;
                        newTempo = newTempo * 10 + peak;
                        self.dataPos++;
                    } else {
                        look = NO;
                    }
                }
                self.tempo = newTempo;
                DLog(@"Tempo: %d", self.self.tempo);
                [self playNote];
            }
            break;

        default:
            [self playNote];
            break;
    }
}


- (int)getNoteNumber:(NSString*)note
{
    note = [note uppercaseString];
    DLog(@"%@", note);

    if ([note isEqualToString:@"A"])
        return 0;
    else if ([note isEqualToString:@"A#"] || [note isEqualToString:@"B-"])
        return 1;
    else if ([note isEqualToString:@"B"] || [note isEqualToString:@"C-"])
        return 2;
    else if ([note isEqualToString:@"C"] || [note isEqualToString:@"B#"])
        return 3;
    else if ([note isEqualToString:@"C#"] || [note isEqualToString:@"D-"])
        return 4;
    else if ([note isEqualToString:@"D"])
        return 5;
    else if ([note isEqualToString:@"D#"] || [note isEqualToString:@"E-"])
        return 6;
    else if ([note isEqualToString:@"E"] || [note isEqualToString:@"F-"])
        return 7;
    else if ([note isEqualToString:@"F"] || [note isEqualToString:@"E#"])
        return 8;
    else if ([note isEqualToString:@"F#"] || [note isEqualToString:@"G-"])
        return 9;
    else if ([note isEqualToString:@"G"])
        return 10;
    else if ([note isEqualToString:@"G#"])
        return 11;
}

- (void)setFreq:(int)note
{
    float a = powf(2, self.octive);
    float b = powf(1.059463, note);
    float freq = roundf((275.0 * a * b) / 10);
    self.toneGen.frequency = freq;
}

- (void)toneStop
{
    [self playNote];
}

@end

To play little tune create a Music object and play…

[self.music play:@"T180 DF#A L2 A L4 O4 AA P4 F#F# P4 O3 D DF#A L2 A L4 O4 AA P4 GG P4 O3 C#C#EB L2 B L4 O4 BB P4 GG P4 O3 C#C#EB L2 B L4 O4 BB P4 F+F+ P4 O3 DDF#A L2 O4 D L4 O5 DD P4O4 AA P4 O3 DDF#A L2 O4 D L4 O5 DD P4O4 BB P4 EEG L8 B P8 ML B1 L4 MN G#A ML L3 O5 F#1L4 MN D O4 F# ML L2 F# MN L4 E ML L2 B MN L4 AD P8 D8 D4"];

Any idea on how to remove the chirp between notes?

  • 1 1 Answer
  • 0 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

1 Answer

  • Voted
  • Oldest
  • Recent
  • Random
  1. Editorial Team
    Editorial Team
    2026-06-01T11:53:17+00:00Added an answer on June 1, 2026 at 11:53 am

    I think that the bit where you stop audio output between notes is the culprit:

    if (self.toneUnit)
    {
        AudioOutputUnitStop(self.toneUnit);
        AudioUnitUninitialize(self.toneUnit);
        AudioComponentInstanceDispose(self.toneUnit);
        self.toneUnit = nil;
    }
    

    Just leave the tone unit active and you’ll have less chirping. You’ll need some other way to generate silence, probably by having RenderTone continue to run but generate amplitude zero.

    I was able to eliminate the slight chirps that remained by having it, on a frequency change, fade the amplitude down to nothing, update the frequenmcy, and fade back in again. This is of course what the old PC speaker couldn’t do (except for a few people who rapidly switched it on again), but with a very rapid fade you can probably get the old-school effect without the chirps.

    Here’s my fading RenderTone function (currently using evil global variables):

    double currentFrequency=0;
    double currentSampleRate=0;
    double currentAmplitude=0;
    
    OSStatus RenderTone(
                        void *inRefCon, 
                        AudioUnitRenderActionFlags  *ioActionFlags, 
                        const AudioTimeStamp        *inTimeStamp, 
                        UInt32                      inBusNumber, 
                        UInt32                      inNumberFrames, 
                        AudioBufferList             *ioData)
    
    {
        // Fixed amplitude is good enough for our purposes
        const double amplitude = 0.5;
    
        // Get the tone parameters out of the view controller
        ToneGen *toneGen = (__bridge ToneGen *)inRefCon;
        double theta = toneGen.theta;
    
        BOOL fadingOut = NO;
        if ((currentFrequency != toneGen.frequency) || (currentSampleRate != toneGen.sampleRate))
        {
            if (currentAmplitude > DBL_EPSILON)
            {
                fadingOut = YES;
            }
            else
            {
                currentFrequency = toneGen.frequency;
                currentSampleRate = toneGen.sampleRate;
            }
        }
    
        double theta_increment = 2.0 * M_PI * currentFrequency /currentSampleRate;
    
        // This is a mono tone generator so we only need the first buffer
        const int channel = 0;
        Float32 *buffer = (Float32 *)ioData->mBuffers[channel].mData;
    
        // Generate the samples
        for (UInt32 frame = 0; frame < inNumberFrames; frame++) 
        {
            buffer[frame] = sin(theta) * currentAmplitude;
            //NSLog(@"amplitude = %f", currentAmplitude);
    
            theta += theta_increment;
            if (theta > 2.0 * M_PI)
            {
                theta -= 2.0 * M_PI;
            }
            if (fadingOut)
            {
                if (currentAmplitude > 0)
                {
                    currentAmplitude -= 0.001;
                    if (currentAmplitude < 0)
                        currentAmplitude = 0;
                }
            }
            else
            {
                if (currentAmplitude < amplitude)
                {
                    currentAmplitude += 0.001;
                    if (currentAmplitude > amplitude)
                        currentAmplitude = amplitude;
                }
            }
    
        }
    
        // Store the theta back in the view controller
        toneGen.theta = theta;
    
        return noErr;
    }
    
    • 0
    • Reply
    • Share
      Share
      • Share on Facebook
      • Share on Twitter
      • Share on LinkedIn
      • Share on WhatsApp
      • Report

Sidebar

Related Questions

I have a string like this: La Torre Eiffel paragonata all&#8217;Everest What PHP function
I have a French site that I want to parse, but am running into
I'm parsing an RSS feed that has an &#8217; in it. SimpleXML turns this
link Im having trouble converting the html entites into html characters, (&# 8217;) i
That's pretty much it. I'm using Nokogiri to scrape a web page what has
I have just tried to save a simple *.rtf file with some websites and
I have a jquery bug and I've been looking for hours now, I can't
this is what i have right now Drawing an RSS feed into the php,
I've got a string that has curly quotes in it. I'd like to replace
I have this code to decode numeric html entities to the UTF8 equivalent character.

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help
  • SEARCH

Footer

© 2021 The Archive Base. All Rights Reserved
With Love by The Archive Base

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.