NiAudioSample.cpp - sample program

    <b>Source file:</b> Click the following link to view the source code file:
        - NiAudioSample.cpp

    This section describes an OpenNI sample program for using the Audio interface.

    The documentation describes the sample program's code from the top of the program file(s) to bottom.

    This documentation item describes only OpenNI code features. Third party features, e.g., GL code, may be ignored. However, place holders as section headings appear in place of complete functions or large code blocks that are not OpenNI specific or key to understanding the logic of the sample program. These place holders are section headings with minimal text to enable the reader to identify the original program structure when studying the documentation.

    Every OpenNI feature is described the first time it appears in this sample program. Further appearances of the same feature are not described again.

    @section bkrec_macros Macro Declarations

        At the top of the program is a macro utility declaration, which calls OpenNI methods. It is described below. However, for the sake of conciseness, the rest of this documentation skips calls to this macro.

        The CHECK_RC() macro checks whether the most recent OpenNI operation was successful or returned an error result. On error, the @ref xn::xnGetStatusString "xnGetStatusString()" method converts the OpenNI error return code to the corresponding error string for printing, and the current function exits.
        @code
            #define CHECK_RC(rc, what) \
                if (rc != XN_STATUS_OK)                                     \
                {                                                           \
                    printf("%s failed: %s\n", what, xnGetStatusString(rc)); \
                    return rc;                                              \
                }
        @endcode

    @section rec_syn_sample_xml_path Declaration of File Path

        In the following definitions, SAMPLE_XML_PATH is for the path to an OpenNI XML script input file for building a production graph. The <i>production graph</i> is a network of <i>production nodes</i> and is the principal OpenNI object model. See @ref prod_graph for more about the production graph.
        @code
            #define SAMPLE_XML_PATH "../../../../Data/SamplesConfig.xml"
        @endcode

    @section aud_glb_dcl_blk_ref "Declaration Block" section

        The reader may find it convenient to study the global declaration block before continuing to study the code statements. The global declaration block is documented later in this section, corresponding to its position in the program file &ndash; see @ref aud_glb_dcl_blk.

    @section aud_decl_mode enum Mode

        The following declaration defines the  program mode, whether whether it is recording an OpenNI data generation session or whether  it is playing a recording.
        @code
            enum Mode
            {
                MODE_PLAY,
                MODE_RECORD,
            };
        @endcode

        This declaration is not OpenNI specific.

    @section aud_printusage printUsage() method

        This function is not OpenNI specific.

    @section aud_play play() method - Play Audio

        This routine gets an array of AudioGenerators, takes the first one, and constantly reads data from it and sends it to be played.

        The following code block gets the first AudioGenerator node's wave output mode. It is received in an xn::XnWaveOutputMode, which comprises: bits per sample, channel, and sample rate. This information is then used for setting up the audio output buffers for collecting the generated play data and for setting up the hardware audio output channel.
        @code
            XnWaveOutputMode waveMode;
            nRetVal = aGens[0].GetWaveOutputMode(waveMode);
            CHECK_RC(nRetVal, "Failed getting wave output mode");
        @endcode

        The above completes the initialization. The following code is the main program loop.

        The application calls an 'Update Data()' method, in this case the node's @ref xn::Generator::WaitAndUpdateData "WaitAndUpdateData", to make a new frame available for getting. The application can then get the data (for example, using a metadata GetData() method).
        @code
            nRetVal = aGens[0].WaitAndUpdateData();
        @endcode

        Assuming the above call succeeded, the application then gets the size of the audio data that was generated by the AudioGenerator node.
        @code
            XnUInt32 nBufferSize = aGens[0].GetDataSize();
        @endcode

        The next OpenNI statements copy the AudioGenerator node's data into a prepared buffer to be sent to the machine sound card. <code>xnOSMemCopy</code> is OpenNI memory copy routine.
        @code
            xnOSMemCopy(pHeader->lpData, aGens[0].GetAudioBuffer(), nBufferSize);
            pHeader->dwBufferLength = nBufferSize;
        @endcode

    @section aud_record record() method - Record Audio

        This method gets a list of audio generators, and records to a separate file the data generated by each generator .
        The following code block creates files named with the names of the AudioGenerator nodes.
        @code
            XN_FILE_HANDLE aFiles[nSupportedNodes];
            for (XnUInt32 i = 0; i < nNodes; ++i)
            {
                XnChar strFileName[XN_FILE_MAX_PATH];
                sprintf(strFileName, "audio.%s.pcm", aGens[i].GetName());
                nRetVal = xnOSOpenFile(strFileName, XN_OS_FILE_WRITE, &aFiles[i]);
                CHECK_RC(nRetVal, "Open file");
            }
        @endcode

        The application then calls an @ref conc_updating_data "'Update Data'" method, in this case xn::Context.WaitAnyUpdateAll(), to update all generator nodes in the context to the latest available data, first waiting for any of the nodes to have new data available. The first call is used just to flush the node.
        @code
            context.WaitAnyUpdateAll();
        @endcode

        The following loop checks which node has new data and writes it to a file. The IsDataNew() method returns whether a node's frame data was updated by the most recent call to the WaitAnyUpdateAll() method.
        @code
            XN_FILE_HANDLE aFiles[nSupportedNodes];
               ...
               ...
            for (XnUInt32 i = 0; i < nNodes; ++i)
            {
                if (aGens[i].IsDataNew())
                {
                    nRetVal = xnOSWriteFile(aFiles[i], aGens[i].GetAudioBuffer(), aGens[i].GetDataSize());
                    CHECK_RC(nRetVal, "Write to file");
                }
            }
        @endcode

    @section aud_mainprg Main Program

        @subsection aud_glb_dcl_blk "Declaration Block" section

            The declaration block at the top of the main program declares a @ref xn::Context object, an @ref xn::EnumerationErrors object, and an xn::ScriptNode object. The Context object is a workspace in which the application builds an OpenNI production graph. the @ref xn::EnumerationErrors object is for collecting errors from any of the OpenNI functions. Also declared is an OpenNI status flag for collecting return values from method calls. the @ref xn::ScriptNode object loads an XML script from a file or string, and then runs the XML script to build a production graph.

            @code
                XnStatus nRetVal = XN_STATUS_OK;
                Context context;
                EnumerationErrors errors;
                  ...
                ScriptNode scriptNode;
            @endcode

        @subsection aud_main_code Main Code Section

            In the following, the InitFromXmlFile() method is a shorthand combination of two other initialization methods &ndash; Init() and then RunXmlScriptFromFile() &ndash; which initializes the context object and then creates a production graph from an XML file.
            @code
                nRetVal = context.InitFromXmlFile(SAMPLE_XML_PATH, scriptNode);
            @endcode

            The following code block tests whether a production graph was created. If not, the reason is reported as a list of one or more errors in the <code>errors</code> object.
            @code
                if (nRetVal == XN_STATUS_NO_NODE_PRESENT)
                {
                    XnChar strError[1024];
                    errors.ToString(strError, 1024);
                    printf("%s\n", strError);
                    return (nRetVal);
                }
            @endcode

            The following code block searches for xn::AudioGenerator nodes in the production graph. The nodes are returned in the @ref xn::NodeInfoList object. This object is a list xn::NodeInfo objects, each containing information about a node found in the above  EnumerateExistingNodes() call.

            @code
                NodeInfoList list;
                nRetVal = context.EnumerateExistingNodes(list, XN_NODE_TYPE_AUDIO);
                CHECK_RC(nRetVal, "Enumerate audio nodes");
                AudioGenerator gens[nSupportedNodes];
                XnUInt32 nNodes = 0;
            @endcode

            The following declarations and statements get all the @ref xn::AudioGenerator nodes that were found by the EnumerateExistingNodes() call. The <code>gens</code> array receives all the nodes, and they are counted by the <code>nNodes</code> variable. (Currently, only a single AudioGenerator node is supported for playing at at once.)
            @code
                AudioGenerator gens[nSupportedNodes];
                XnUInt32 nNodes = 0;
                   ...
                for (NodeInfoList::Iterator it = list.Begin(); it != list.End(); ++it)
                {
                    NodeInfo info = *it;
                    nRetVal = info.GetInstance(gens[nNodes]);
                    CHECK_RC(nRetVal, "Get audio node");
                    nNodes++;
                }
            @endcode