Music 120

Week 5 Online Tutorial

Introduction to Audio/Multimedia Application Programming

Announcements  |  Course Info  |  Weekly Schedule  |  Tutorials  |  Links

Week 5 Online Tutorial: Cocoa GUI Programming with Stk

0. Currency Converter

Apple's Developer Webpage has a really nice Cocoa application tutorial, which is to make a simple currency converter using Xcode and Interface Builder.
If you are not familiar with Cocoa and GUI programming, I strongly suggest that you read through it and create the application by following step-by-step instructions: much of them will become extremely useful for our BeeThree example.
Also note that you can find pointers to the corresponding chapters of the Currency Converter tutorial throughout this page.

1. New Cocoa App Project

Run Xcode. Select File > New Project to open an Assistant.
Choose Cocoa Application, and click Next.
Type in its name (BeeThree) and location.
Voila! Here's our new Xcode project BeeThree.

2. MainMenu.nib & Interface Builder

Locate MainMenu.nib under "Resources" folder on left. Double-click it to open Interface Builder.
Design your GUI by dragging objects from the palette onto the Window. Refer to this page for details of creating interface.
For BeeThree, we want to have:
  • a pitch slider
  • a pitch value field
  • a start (sound) button, and
  • a stop (sound) button

3. Controller & Actions/Outlets

Now we need a Controller class which can take in user action from GUI, update GUI and make sound as well.
Go to Classes tab of the MainMenu.nib panel. Choose NSObject, and press Enter to create a new class named BeeThreeController.
Carefully think what Actions/Outlets the program would need. Using Inspector (Tools > Show Inspector), change its attribute so that it has two outlets and four actions:
  • Outlet: _pitchSlider and _pitchValueField
  • Action: pitchSliderMoved, pitchValueEntered, startSound, and stopSound
Check this page to see how they did this with the Currency Converter.

4. Controller Instance & Connections

Instantiate BeeThreeController by choosing (Classes > Instantiate BeeThree Controller). An instance of BeeThreeController will show up under the Instances tab of the MainMenu.nib panel.
Now, make connections for the outlets and actions by Ctrl-dragging between the instance and GUI elements:
  • Outlets from the BeeThreeController instance to the GUI, and
  • Actions in the opposite direction.
This page should be helpful.

5. Source Codes

Select BeeThreeController under the Classes tab, and generate its source files (Classes > Create Files for BeeThreeController). This will create BeeThreeController.h and BeeThreeController.m.
Now go back to the Xcode. Find the files under the Other Sources folder, then modify the extension .m to .mm so that it can be recognized as an ObjC++ file. This is necessary because we are using Stk (via StkX), which is based on C++.

6. Using StkX

To link StkX, StkX.framework folder should be added to the project.
Unfold Frameworks, Ctrl-click on the Linked Framewkorks and choose Add > Existing Frameworks. Then locate the StkX.framework (most probably /Library/Frameworks/StkX.framework). Add it with default settings.
You also need to make sure that it's in one of Xcode's Framework Search Paths.
Double-click BeeThree on the left side to open the Project "BeeThree" Info panel, and you will find the Framework Search Paths setting. Depending on the location of your StkX framework, you may need to add its path here.
In addition, the StkX framework header needs to be imported by adding the following line to BeeThreeController.h.
#import <StkX/StkX.h>

7. Implementations

This BeeThree application can be considered as a GUI version of ex11. You might want to compare their source codes to see the process flow.

Header: Stk class instance variables

We need these instance variables in BeeThreeController.h.
BeeThree *_beeThree;
RtAudio *_dac;

- (void)awakeFromNib:

As its name suggests, - (void)awakeFromNib: is the method that is processed when GUI is loaded. Therefore, audio setups as well as GUI parameters can be initialized here:
- (void)awakeFromNib
  // Set initial slider value
  [_pitchSlider setFloatValue:440.0];

  // Initialize pitch field value using slider value
  [_pitchValueField setFloatValue:[_pitchSlider floatValue]];

  // Initialize audio settings

  RtAudioFormat format = ( sizeof(StkFloat) == 8 ) ? RTAUDIO_FLOAT64:RTAUDIO_FLOAT32;
  int bufferSize = RT_BUFFER_SIZE;
  _dac = new RtAudio(0, 1, 0, 0, format, (int)Stk::sampleRate(), &bufferSize, 4);

  _beeThree = new BeeThree();
  _beeThree->noteOn( [_pitchSlider floatValue], 0.7 );
For more info on Objective-C, you might want to check this page.
Also, refer to Application Kit Reference for Objective-C for details of the methods.

IBAction methods (1): startSound & stopSound

These methods are invoked when the Start and Stop buttons are pressed, respectively.
// Set stream callback, and start audio stream
- (IBAction)startSound:(id)sender
  _dac->setStreamCallback(&tick, (void *)_beeThree);

// Stop audio stream, and cancel stream callback
- (IBAction)stopSound:(id)sender

IBAction methods (2): pitchSliderMoved & pitchValueEntered

When the slider is moved, we need to
  • update the pitch value in the text field, and
  • set the frequency of _beeThree.
Similarly, when a new pitch value is entered into the text field, we should
  • update the pitch value of the slider, and
  • set the frequency of _beeThree.
- (IBAction)pitchSliderMoved:(id)sender
  // Update the pitch value in the text field
  [_pitchValueField setFloatValue:[sender floatValue]];

  // Set the frequency value of _beeThree
  _beeThree->setFrequency([sender floatValue]);

- (IBAction)pitchValueEntered:(id)sender
  // Update the pitch value of the slider
  [_pitchSlider setFloatValue:[sender floatValue]];

  // Set the frequency value of _beeThree
  _beeThree->setFrequency([sender floatValue]);

- (void)dealloc

- (void)dealloc corresponds to the destructor (~) of C++. We must be ready to exit the program.
- (void)dealloc {
  // Close RtAudio stream

  // Delete instance variables
  delete _dac;
  delete _beeThree;

  [super dealloc];

Callback function

This is identical to the callback we have seen in ex11.
int tick(char *buffer, int bufferSize, void *dataPointer) {
  BeeThree *beeThree = (BeeThree *) dataPointer;
  StkFloat *samples = (StkFloat *) buffer;

  for ( int i=0; i<bufferSize; i++ )
    *samples++ = beeThree->tick();

  return 0;

8. Compile & Run!

Click the Build icon on the toolbar to compile the project. If compiles successfully, you can test the program without leaving Xcode by clicking Build and Go button.


Links Summary

Announcements  |  Course Info  |  Weekly Schedule  |  Tutorials  |  Links

Music 120 / Fall 2006 / CCRMA, Stanford University
Woon Seung Yeo
Last updated: Sun, 01 Oct 2006 22:56:24 -0700