4. Код программы
#import "ToneGeneratorViewController.h"
#import <AudioToolbox/AudioToolbox.h>
#import "SettingsModel.h"
OSStatus RenderTone(
void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList *ioData)
{
const double amplitude = 1;
ToneGeneratorViewController *viewController = (ToneGeneratorViewController *)inRefCon;
double theta = viewController->theta;
double theta_increment = 2.0 * M_PI * viewController->frequency / viewController->sampleRate;
const int channel = 0;
Float32 *buffer = (Float32 *)ioData->mBuffers[channel].mData;
for (UInt32 frame = 0; frame < inNumberFrames; frame++)
{
if ([viewController.model factor]) {
buffer[frame] = sin(theta) * amplitude;
theta += theta_increment;
if (theta > 2.0 * M_PI)
{
theta -= 2.0 * M_PI;
}
} else {
buffer[frame] = 0;
theta = 0;
}
}
viewController->theta = theta;
return noErr;
}
void ToneInterruptionListener(void *inClientData, UInt32 inInterruptionState)
{
ToneGeneratorViewController *viewController = (ToneGeneratorViewController *)inClientData;
[viewController stop];
}
@interface ToneGeneratorViewController()
@property (retain) UITabBarItem *tabBarItem;
@end
@implementation ToneGeneratorViewController
@synthesize model;
@synthesize frequencySlider, playButton, frequencyLabel, speedLabel, speedSlider;
@synthesize tabBarItem;
- (MorseModel *)model {
if (!model) {
model = [[MorseModel alloc] init];
model.dotsInPauseBetweenSymbols = 3;
model.dotsInPauseBetweenGroups = 7;
model.controller = self;
model.morseCode = [SettingsModel dictionary];
}
return model;
}
- (void)setup {
self.title = @"Morse Generator";
UIBarButtonItem *backButton = [[UIBarButtonItem alloc] initWithTitle:@"Back"
style:UIBarButtonItemStylePlain target:nil action:nil];
self.navigationItem.backBarButtonItem = backButton;
[backButton release];
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
if ((self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])) {
[self setup];
}
return self;
}
- (void)awakeFromNib {
[self setup];
}
- (IBAction)frequencyChanged:(UISlider *)slider {
frequency = slider.value;
self.frequencyLabel.text = [NSString stringWithFormat:@"%4.0f Hz", frequency];
[SettingsModel setFrequency:frequency];
}
- (IBAction)speedChanged:(UISlider *)slider {
int newSpeed = slider.value;
self.model.speed = newSpeed;
self.speedLabel.text = [NSString stringWithFormat:@"%d WPM", newSpeed];
[SettingsModel setSpeed:newSpeed];
}
- (void)createToneUnit
{
AudioComponentDescription defaultOutputDescription;
defaultOutputDescription.componentType = kAudioUnitType_Output;
defaultOutputDescription.componentSubType = kAudioUnitSubType_RemoteIO;
defaultOutputDescription.componentManufacturer = kAudioUnitManufacturer_Apple;
defaultOutputDescription.componentFlags = 0;
defaultOutputDescription.componentFlagsMask = 0;
AudioComponent defaultOutput = AudioComponentFindNext(NULL, &defaultOutputDescription);
NSAssert(defaultOutput, @"Can't find default output");
OSErr err = AudioComponentInstanceNew(defaultOutput, &toneUnit);
NSAssert1(toneUnit, @"Error creating unit: %ld", err);
AURenderCallbackStruct input;
input.inputProc = RenderTone;
input.inputProcRefCon = self;
err = AudioUnitSetProperty(toneUnit,
kAudioUnitProperty_SetRenderCallback,
kAudioUnitScope_Input,
0,
&input,
sizeof(input));
NSAssert1(err == noErr, @"Error setting callback: %ld", err);
const int four_bytes_per_float = 4;
const int eight_bits_per_byte = 8;
AudioStreamBasicDescription streamFormat;
streamFormat.mSampleRate = 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 (toneUnit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input,
0,
&streamFormat,
sizeof(AudioStreamBasicDescription));
NSAssert1(err == noErr, @"Error setting stream format: %ld", err);
}
- (void)stop {
if (isRunning) {
isRunning = NO;
isPaused = NO;
AudioOutputUnitStop(toneUnit);
[playButton setTitle:NSLocalizedString(@"Play", nil) forState:0];
[self.model reset];
}
}
- (IBAction)stopPlay:(UIButton *)sender {
[self stop];
}
- (IBAction)playAgain:(UIButton *)sender {
[self stopPlay:sender];
NSRunLoop* myRunLoop = [NSRunLoop currentRunLoop];
NSDate* futureDate = [NSDate dateWithTimeIntervalSinceNow:0.4];
NSTimer* myTimer = [[NSTimer alloc] initWithFireDate:futureDate
interval:0.1
target:self
selector:@selector(togglePlay:)
userInfo:nil
repeats:NO];
[myRunLoop addTimer:myTimer forMode:NSDefaultRunLoopMode];
[myTimer release];
}
- (IBAction)togglePlay:(UIButton *)selectedButton
{
if (!isRunning) {
isRunning = YES;
[self.model calculateData:SIZE];
isPaused = YES;
}
if (isPaused) {
if ([SettingsModel textChanged]) {
[self.model reset];
self.model.text = [[NSUserDefaults standardUserDefaults] objectForKey:@"MorseText"];
[self.model calculateData:SIZE];
[SettingsModel setTextChanged:NO];
}
isPaused = NO;
OSErr err = AudioOutputUnitStart(toneUnit);
NSAssert1(err == noErr, @"Error starting unit: %ld", err);
[playButton setTitle:NSLocalizedString(@"Pause", nil) forState:0];
} else {
isPaused = YES;
AudioOutputUnitStop(toneUnit);
[playButton setTitle:NSLocalizedString(@"Play", nil) forState:0];
}
}
- (void)viewDidLoad {
[super viewDidLoad];
self.speedSlider.value = [SettingsModel speed];
[self speedChanged:self.speedSlider];
self.frequencySlider.value = [SettingsModel frequency];
[self frequencyChanged:self.frequencySlider];
sampleRate = 44100;
OSStatus result = AudioSessionInitialize(NULL, NULL, ToneInterruptionListener, self);
if (result == kAudioSessionNoError)
{
UInt32 sessionCategory = kAudioSessionCategory_MediaPlayback;
AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, sizeof(sessionCategory), &sessionCategory);
}
AudioSessionSetActive(true);
[self createToneUnit];
dispatch_queue_t initQueue = dispatch_queue_create("Init toneUnit", NULL);
dispatch_async(initQueue, ^{
OSErr err = AudioUnitInitialize(toneUnit);
NSAssert1(err == noErr, @"Error initializing unit: %ld", err);
});
if (self.textChanged) {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSArray *morseText = [MorseModel getMorseText:[defaults objectForKey:@"VisibleText"]
withDictionary:[SettingsModel dictionary]];
self.model.text = morseText;
self.textChanged = NO;
[defaults setObject:morseText forKey:@"MorseText"];
[defaults synchronize];
} else {
self.model.text = [[NSUserDefaults standardUserDefaults] objectForKey:@"MorseText"];
}
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
}
- (void)viewDidUnload {
self.frequencyLabel = nil;
self.playButton = nil;
self.frequencySlider = nil;
self.speedLabel = nil;
self.speedSlider = nil;
AudioUnitUninitialize(toneUnit);
AudioComponentInstanceDispose(toneUnit);
toneUnit = nil;
AudioSessionSetActive(false);
}
// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations.
return YES;
}
- (void)dealloc {
[model release];
[super dealloc];
}
@end
