Academic Company Events NI Developer Zone Support Solutions Products & Services Contact NI MyNI

Document Type: Example Program
NI Supported: Yes
Publish Date: Aug 13, 2008


Feedback


Yes No

Related Categories

Related Links - Developer Zone

Related Links - Products and Services

NI Musical Instrument Tuner (LabVIEW Objects as Reference Objects)

0 ratings | 0.00 out of 5
Print

Overview

The niMusicalInstrumentTuner VI is an aid for tuning musical instruments. It exemplifies how to use LabVIEW objects as reference objects. Because the VI is designed as an example, it does not have the robust data checking or other amenities a full-blown application might have. The accuracy of the clock on your sound card also limits the VI.

Overview

A reference object is an entity you access using a reference of some sort. Because LabVIEW is a dataflow language, in LabVIEW you normally reference objects by value. A wire is an actual copy of the data. If you split a wire, you get a completely new copy of the data. In contrast, a reference object consists of two parts - the actual data and a reference to that data. You copy the reference and only one copy of the data exists. This is useful for interprocess communications and large data applications.

Object-oriented programmers in LabVIEW usually think of LabVIEW class objects as reference objects. This is because the GOOP 1.0 Toolkit, the first object-oriented methodology for LabVIEW, used a reference object paradigm. Object-oriented packages for LabVIEW have followed the GOOP Toolkit's lead ever since. However, native LabVIEW class objects, introduced in LabVIEW 8.2, are not reference objects because LabVIEW is a data flow language. You can easily create reference objects, though, from native LabVIEW objects using other LabVIEW concepts and functions.

This example demonstrates one option for creating reference objects using LabVIEW class objects. Refer to the niMusicalInstrumentTuner VI to view the code discussed.


Reference Object Use In LabVIEW

Creation

You can create LabVIEW reference objects in several ways. Using LabVIEW 2 globals or single–element queues are two good methods to create LabVIEW reference objects. This example focuses on the single–element queue method because the queue method is lesser known and has performance advantages in most applications over the LabVIEW 2 global method.

To create the reference object, you can complete the following steps.

  1. Create a LabVIEW class object. A LabVIEW class object is an instance of a LabVIEW class on the block diagram.
    Note: The class object does not need to contain data. You can modify the class object after you create the reference object from it.
  2. Create another LabVIEW class object which you will use to create the reference object. The data for the reference object is a reference to a single–element queue containing as its data the first LabVIEW class object you created.
  3. Initialize the reference object so the queue reference is valid. You can do this by creating and using an initialize or create VI.
  4. Destroy the reference object by creating and using a close, finalize, or destroy VI.

Refer to the niMusicalInstrumentTuner project for an example of creating and using reference objects with LabVIEW classes. The project contains three LabVIEW objects:

  • NoteCalculations class—A LabVIEW class.
  • Sound class—A LabVIEW class.
  • Tuner class—A LabVIEW class that references the NoteCalculations and Sound classes.

Open Tuner.lvclass:Initialize.vi and note how the references are created in the VI. A queue is created with a single element. To create the reference, the element is initialized with the LabVIEW class object. The Tuner class contains two such references.

A critical point of a reference object is that its data never changes. The data should only be unchanging references. In the niMusicalInstrumentTuner VI, the data for the Tuner class never changes after initialization. The data in the private data control of the Tuner class (Tuner.ctl) are only references to queues. The queues contain the actual data. This allows you to use and copy the reference object in multiple places. The references never change. The data and/or the objects the references point to change instead.

Design Considerations

The single–element queue offers automatic race condition protection in addition to being a convenient LabVIEW class object repository. To use a single–element queue, simply dequeue the class object and use it as you normally use queued objects. The queue blocks reading until the object is enqueued again, since the queue is empty. Refer to the Tuner.lvclass:Get_nDataPoints.vi for an example of using queues with LabVIEW classes. Tuner.lvclass:Get_nDataPoints.vi is a reference wrapper around Sound.lvclass:Get_nDataPoints.vi. LabVIEW uses the reference to the single-element queue in sound to access an instance of the Sound class, which LabVIEW then uses to query for the number of data points. LabVIEW then enqueues the reference to allow its use elsewhere.

Since the queue functions similarly to a mutex or semaphore, deadlocks are possible with improper use. There are two common problems.

  1. You cannot call a VI which dequeues the reference object inside a VI in which the object is already dequeued. This situation might occur inadvertently when you call a VI containing a subVI that dequeues the reference after you already dequeued the reference in the main VI.
  2. If an error occurs between the dequeue and enqueue, and you are using error chaining, the enqueue does not occur and the next call to the dequeue hangs. You can handle this in a variety of ways. The easiest way is to route the error wires around the dequeue and enqueue. If you do this, you might consider wiring the dequeue error wire to the enqueue. You might also consider merging the queue errors and the main error stream at the end of the VI to get a complete error report.

niMusicalInstrumentTuner

Overview

The niMusicalInstrumentTuner VI is an aid for tuning musical instruments. Because the VI is designed as an example, it does not have the robust data checking or other amenities a full-blown application might have. The accuracy of the clock on your sound card also limits the VI.

Use

To use the niMusicalInstrumentTuner VI:

  1. Ensure that a microphone is attached to your computer and is working.
  2. Run the niMusicalInstrumentTuner VI.
  3. Click the Processing button to start acquiring and analyzing sound information.
    The LED indicator on the button should light up.
  4. Click the Processing button again to stop acquiring data.
  5. Click the Stop button to stop the application.

If you need to change the frequency of the A note (for example to change from modern to baroque tuning or tune to a piano or harp), change the value of A Frequency (Hz) any time while the application is running. If you wish to change the default value of A Frequency (Hz) (for example to compensate for a known inaccuracy in your sound card), change the default value of Afrequency in NoteCalculations.lvclass:NoteCalculations.ctl.

When the VI is running, it collects waveforms from the sound card and analyzes these for the fundamental tone present. LabVIEW calculates the frequency of the tone, then determines the nearest musical note to that frequency. The Current Note and Notes indicators display the value of the nearest musical note. Once LabVIEW determines the nearest note, it calculates and displays the difference between the tone frequency and the note frequency as an error on the Error (cents) meter. A cent is 1/100th of the difference between adjacent half-steps on a musical scale. When the error is less than 1 cent, the In Tune LED lights up.

Program Structure

The niMusicalInstrumentTuner VI consists of an initialization phase followed by two loops. One loop is for the UI and is labelled commandLoop. The other loop, in Tuner.lvclass:CollectData.vi, is for data acquisition and a simple cleanup phase. To view the initialization phase, open the niMusicalInstrumentTuner VI. Initialization consists of three parts:

  1. Creating a user event for updating the display.
  2. Initializing the reference objects.
  3. Initializing the UI task queue.

LabVIEW passes references to the main UI loop and the data acquisition loop. The cleanup phase, in the single-frame sequence structure at the end, calls the Finalize VI of the reference object to clean up the queue references. The single-frame sequence ensures that both the UI and data acquisition loops are finished before LabVIEW destroys the queues.

The UI loop is on the main block diagram right after the initialization of the reference objects and the UI task queue. The main UI handler is a combination event handler and task handler (an event–driven state machine). The task handler is a queue based design with the tasks designated with a strict enumerated type definition. LabVIEW initializes the task queue with an Initialize task before the loop. At each iteration, if there is no task available, the queue times out immediately and waits for an event in the event handling task (WaitOnEvent). Each event launches one or more tasks, which execute synchronously before the next event is handled. You can separate the task handler and event handler into separate loops, but this might cause problems with asynchronous behavior.

The data acquisition loop is in Tuner.lvclass:CollectData.vi. To view the data acquisition loop, open the CollectData VI in the Tuner class. The data acquisition loop is a queue–based state machine with one state being the actual data acquisition loop. The state machine has four states:

  • Default—catches misspellings and unimplemented states during development. This state should never execute during run time.
  • Exit—exits the loop.
  • Run—contains the data acquisition loop, which acquires data continuously and derives the Fundamental frequency.
    At each loop iteration, LabVIEW checks Booleans in the Sound class object to determine if the system should transition to the Pause or Exit states.
  • Pause—polls to determine if the state should transition to the Run or Exit states.
    Note the time delay in the Pause selection to prevent the loop from using too much CPU time while it is in the Pause state.

Note: You normally should implement the acquisition VI as a queued state machine to avoid polling in the Pause state. To avoid unnecessary complexity, we did not implement this example in that manner.

 

Requirements


Filename: nimusicalinstrumenttuner.zip

Software Requirements


Application Software: LabVIEW Base Development System 8.2, LabVIEW Professional Development System 8.2, LabVIEW Full Development System 8.2

 
0 ratings | 0.00 out of 5
Print

Reader Comments | Submit a comment »

good article
although a little bit comment in the VI document would be helpful
- Feb 25, 2009

 

Legal
This example program (this "program") was developed by a National Instruments ("NI") Applications Engineer. Although technical support of this program may be made available by National Instruments, this program may not be completely tested and verified, and NI does not guarantee its quality in any way or that NI will continue to support this program with each new revision of related products and drivers. THIS EXAMPLE PROGRAM IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND AND SUBJECT TO CERTAIN RESTRICTIONS AS MORE SPECIFICALLY SET FORTH IN NI.COM'S TERMS OF USE (http://ni.com/legal/termsofuse/unitedstates/us/).