How to Make a Voice Control Game on iPhone

Some of you might play several game using voice to control the game, say “ahhh” to move the role and shout “pah” to shoot something out…

If you hadn’t played a game like that, you must try my game: Voice Fighter

This article will teach you how to use the iPhone SDK to develop the voice control part of these kinds of game. You can use the code from this article to make your own game. But this article will not show you how to make the graphic part of the game. You can use Cocos2d or other game engine to make the rest of the game.

“Voice Control” in this point is not about the “speech recognition” but like the “voice operated switch”. These games receive the information from microphone and do some simple analysis,We will use AVAudioRecorder to receive the information. You must be aware that doing this work has a lot of ways, you can find more information in iphone SDK documents, as I know use AVAudioRecorder is the simplest way, so let’s start!

Here is the class(Objective-C) from my game Voice Fighter:

AVMicInput.h

//
//  AVMicInput.h
//  voicefighter
//
//  Created by Chengzhi Yang on 11-6-25.
//  http://codeand.me.
//  Copyright 2011 All rights reserved.
//
#import <Foundation/Foundation.h>
#import <AVFoundation/AVFoundation.h>
#import <CoreAudio/CoreAudioTypes.h>

@interface AVMicInput : NSObject {
    AVAudioRecorder *recorder;
    NSTimer *levelTimer;
}

-(float) micAveragePower;

@end

AVMicInput.m

//
//  AVMicInput.m
//  voicefighter
//
//  Created by Chengzhi Yang on 11-6-25.
//  http://codeand.me.
//  Copyright 2011 All rights reserved.
#import "AVMicInput.h"
@implementation AVMicInput
-(id) init
{
	if( (self=[super init]) ) {
        NSURL *url = [NSURL fileURLWithPath:@"/dev/null"];

        NSDictionary *settings = [NSDictionary dictionaryWithObjectsAndKeys:
                                  [NSNumber numberWithFloat: 44100.0],                 AVSampleRateKey,
                                  [NSNumber numberWithInt: kAudioFormatAppleLossless], AVFormatIDKey,
                                  [NSNumber numberWithInt: 1],                         AVNumberOfChannelsKey,
                                  [NSNumber numberWithInt: AVAudioQualityMin],         AVEncoderAudioQualityKey,
                                  nil];
        NSError *error;

        recorder = [[AVAudioRecorder alloc] initWithURL:url settings:settings error:&error];

        if (recorder) {
            [recorder prepareToRecord];
            recorder.meteringEnabled = YES;
            [recorder record];
        } else
            NSLog(@"%@",[error description]);
	}
	return self;
}

- (void) dealloc {
    [recorder dealloc];
    [super dealloc];
}

-(float) micAveragePower
{
    [recorder updateMeters];
    float avgPower = [recorder averagePowerForChannel:0];
    //NSLog(@"%f",avgPower);
    return avgPower;
}

@end

You can add AVMicInput.h and  AVMicInput.m to your project without any change. This class will setup the AVAudioRecorder for you. You may notice the strings “/dev/null”. In Unix-like operating systems, “/dev/null” is a special file that discards all data written to it (but reports that the write operation succeeded) and provides no data to any process that reads from it [1], iPhone OS is one of Unix-like operating systems. Other codes will explain in rest of this article.

Put the following code to your initializer to initialize the AVMicInput class:

AVMicInput *avMicIn;
avMicIn = [[AVMicInput alloc] init];

In your main loop or where you want to get the information from microphone add the following code:

float micIn = [avMicIn micAveragePower];

If the AVMicInput was correctly initialized, micAveragePower will return a float that is the immediate dB(Decibel) information(you can also understand the information into digital signal) from microphone, the range of the return value micIn is [-120, 0].  -120 is the absolutely quiet, 0 is the maximum value(if the voice is very loud, such as a shout, you could get 0). I make a list to help you understand the micIn:
1. If your microphone was broken, iPhone or your Mac, you will always get -120 on your iphone or simulator.
2. In most cases, a silent environment. The range of micIn is about -30 to -50, you can test by yourself.
3. If you say “ahhh” or say something softly, the range of micIn is about -30 to -10.
4. if you speak loudly or shout, the range of micIn is  -10 to 0.

In my game Voice Fighter, through a lot of test and tweak, I used the below code to determine which state of value I got, is “ahhh” or “pah”(It’s not necessary to use these two incantation, “HA”, “PEW”, “BANG”, “KAMEHAMEHA”… is available)

if ( micIn > -6 ){
    //We got a "Pah"!
} else if ( micIn >= -25 && micIn < -6  ) {
    //we got a "Ahhhhhh"
}

You can tweak the threshold by your self.

Others you need know:
1. For now time, most iOS device has a microphone. I’m write the Voice Fighter use a ipod touch 4, but the older generation ipod may need a plug-in microphone to let you play with the microphone.

2. If you use Cocos2d to make your game or in your own engine you used the cocosDension to play the sound like this: [[SimpleAudioEngine sharedEngine] playEffect:@”1.aifc”]; You will noticed if you play a sound the AVAudioRecorder will stop work. For this situation, You can solve the problem by this method:
(1). Add #import “CDAudioManager.h” to AppDelegate.m .
(2). Add [CDAudioManager initAsynchronously:kAMM_PlayAndRecord]; to – (void) applicationDidFinishLaunching:(UIApplication*)application .
And you can find more solution in here. In that forum czyang is me :)

3. If you want to make a voice control game on Android platform please think twice. Theoretically make a voice control game on Android could use the same way, but I was made the Voice Fighter for Android(here) in same way. The input of microphone was totally different! Thanks to separate hardware on android. Each type of android phone have different hardware, and information from microphone on these device may have a huge different. The Voice Fighter for Android just working normally on a bit type of android phone, so I got a lot of 1 star in market. For now I don’t have a solution about this problem.

Now you got all of the “voice control” things. You can use these code to make a voice control game by your self.

If you feel this article is good for you, don’t forget to buy a Voice Fighter or tell your friends about this game for support me :)

BTW, Now I have a few promo code of voice fighter left. I will giveaway 5 of them. If you want to get one, please fill your email correctly and leave a reply follow this article with “Show me your code”. I will send the promo code to your email in 24 hours.

4 comments

  1. fusky 说道:

    Show me your code

  2. lambent 说道:

    I’m using cocos2d and pretty new in the objective-c world.

    I tried to initialize your AVMicInput class under -(id) init, and use your (float) method under my -(void) update:(ccTime) dt. However it reported an error saying that I have use of underclared identifier “avMicIn” under -(void) update.

    I can fix this if I put the AVMicInput alloc/init under my -(void) update, but it seems like it’s not a good idea to keep running this alloc/init every frame.

    What should I do to properly alloc/init the AVMicInput while I can still use the (float) method to get the mic avg power under other methods?

    Thanks.

    • Me 说道:

      I think you need understand the variable’s scope in the Objective-c, it’s just like C or Java. You can only use a variable under it’s scope. In this situation, you need declare “AVMicInput *avMicIn;” out of your “init” and “update” method. Declare the avMicIn as a member variable just ok.

      如果你懂中文的话:
      也就是说要在init 和update函数之外的地方声明那个 avMicIn 的变量,不然会出现你所说的问题,如果你把 avMicIn的声明放在 update里面的话,可能会出现内存方面的问题。

发表评论

电子邮件地址不会被公开。 必填项已用 * 标注