Project Overview
When first beginning this project, I knew that based on previous projects I had worked on, that I needed to enable the use of updating source files that were to be used by the tool. This meant that it became essential to utilize Python once again as the programming language. As mentioned within the overview, the project itself utilized a workflow I had not previously been acquainted with (CHOP's). For Houdini to both read and import audio files, it is necessary for those files to be imported into a node network that is unique to its standard predecessor of SOP's. The most difficult portion of this entire workflow ultimately came to being how to gather the information for the files' length and then ultimately convert that data into a readable attribute that could be read within the main SOP network. It eventually became evident that that final goal would not be straight-forward and would ultimately become the most difficult problem to solve.
Step 01: Generating the Python Network for Importing Files
Basing my knowledge on previous interfaces I had dealt with, this portion was overall straightforward compared to my first time around. This step ultimately entailed first testing a node network that would be imported into the node and then translating the information of that node and its parameters into Python script that could be read by Houdini.
The code that was used to produce the node tree for the imported files can be seen in the figure below.
It is important to note that certain nodes are declared outside of the for-loop. This is necessary for nodes in which you only want to create one copy. In this example, that is true for the "switch" nodes, the "export" node, and the "trim" node. The final node network as well as the desired parameter settings can be viewed in the figure below. The same case can be argued for the math nodes as well, but I felt that it would be best to leave them as they are so that the user can change them on the fly, but also be able to save that information in case they wish to go back to a previous song choice and all of their previous settings will still be there. As a mental note to my future self: this need will likely become obsolete when I reach the point in which I export the entire project as a .exe, but for now I wish to keep the network as it is in its current state.
It became imperative to utilize the trim node's "End" expression as a means for setting the duration of the timeline which will be discussed in greater detail in Step 04. Much time had been spent doing research on the correct means of returning the desired value. This value was ultimately reached by finding a single line value that can be viewed within the "trim1" module in the figure below.
Step 02: Generating a Drop-Down Menu that Updates Based on the Files that are Being Read
A second step that needed to be implemented regarded the automatic updating of the drop-down menu for the music selection that was made by the user. This was done via a string parameter that can be found within the "Parameter Interface Editor" window. The example shown below implements only files of .mp3 format. This particular format was chosen because it is the most universally acceptable format that can be read by multiple programs. Other formats such as .wav will likely be added in the future.
The characteristic difference with this Python code is the utilization of an array. This array is declared under the name "menu_list" by setting it as a null input with the use of square brackets. String values are added to the list via the ".append()" declaration. The first of these declarations assigns the iteration number (an integer value starting at 0) in which the value is to appear within the drop-down directory. This is the equivalent of the "token" value of the string. The second ".append()" is used for the string value showing the numerical order of the file in the directory as well as the file's name.
The second if statement in this example is used only to ensure that a file is not imported more than once into the array. This will need to be improved in the future as it does not currently deal with files ending with version numbers. The final portion of this step is to use a "return" statement to yield the final array that was implemented within "menuList."
Step 03: Translating the File Output Data to a Format that Houdini Can Understand
This step was the portion in which the technical difficulties had started to become relevant. When working with CHOP's it is necessary for the user to become acquainted with the fact that their specific means of declaring attributes is unique from the way Houdini does so. Although there does exist an "attribute" node within the CHOP network, it does not have a means of declaring a new attribute. In fact, CHOP networks don't even have a means of viewing attributes within a spreadsheet. This is largely where the confusion had begun. Instead of declaring custom attributes, there instead exist what HScript calls "Global Variables." Documentation for such variables can be viewed by following the link below.
http://www.sidefx.com/docs/houdini/network/expressions
In this case, it became necessary to understand the means by which to make these values readable within the SOP network. In this particular project, the main values that came to be of importance were "$T" and "$TLENGTH." As shown in the figure below, these values were used to return a value between the range of 0 and 1 as a representation of the ratio at which the animation were to complete. By setting the expression within the carve node seen below, this automatically produced a linear ramp within the timeline without the need for manually setting keyframes. This ensured that the process would be done automatically. The main problem with this came to be that the "$TLENGTH" value would remain constant unless the timeline were to be manually set to the desired duration. Luckily, the ramp would automatically reset each time the value was changed. In other words, this solved one of the major problems that had arisen.
Another set of values that needed to be utilized were the numerical points within the waveform that were to be read into the SOP network. To do this, the "export" node that was created within step one needs to have its values read into the "channel" SOP node. In this project, channel0 was deleted, therefore leaving channel1 as the data that was to be read into the SOP network. In the example below, the "export1" node was imported into the CHOP directory and the "Channel Scope" was set to channel1. The "Attribute Scope" is the portion that can vary depending on which axes the user wishes to affect. In this example, only the x-direction is the one which should be affected. Within other nodes, one would normally type "@P.x," but because the CHOP network does not utilize attributes, there is a different means in which the expression is written. As seen below, "P" is typed in to denote the "point" followed by parentheses. Within the parentheses, is a numerical integer that denotes the axis which is being used. Because this is using the x-axis, the user types in "0" (y = 1, z = 2).
Up to this point, the variables that will drive the animation have now been imported into the SOP network, the next step therefore is to read in these values and then utilize them in offsetting the animation. To do this, a "blendshapes" node is utilized. As long as the "export1" that had been produced within Step 01 has the correct input within the "node_path" and the "path" parameters of the node, then as soon as the "blendshapes" node is connected, there will be an override within the value for what in this case is labeled as "channel1." The example of the override can be seen highlighted in orange in the figure below.
The nodes that can be seen underneath the node labeled "blendshapes1" will be clarified within the next step.
Step 04: Set the Playbar to the Desired File Duration
This step marked the final major obstacle that needed to be overcome. Up to this point, the animation of the waveform matches the music at an arbitrary point in time. Unless the playbar length is manually set to the duration of the music file that is being read, the waveform itself will be out of sync with the music. To do this, the first step is to import the value that results from the expression that was created within the "trim1" node in Step 01. This is the point where the "attribcreate1" node is utilized. To do this, because the value already exists, all that needs to be done is there needs to be a link that is created within "value0" of the "attribcreate" node. In this example, all that needs to be typed in is:
ch("../chopnet1/trim1/end")
This will import the value that was already equated into the SOP network so that the end frame of the file can be read.
The final node that ensures that everything works as it should, is a "python" node. As seen within the example above, I decided to name this node "set_playbar_music." The example of the code that was ultimately utilized within this node can be viewed below. The most important portions of this code that returned the desired values are highlighted in color-coordinated labels. The first three lines were used to set the desired values within the "Audio Panel." As long as this node exists within the network, the values in this window will always be set to the desired values. Because this example uses the "CHOP" input rather than the "File" input, the songs will update based on which song was selected from the menu that was produced in Step 02.
The final line that came to be the most important for generating the length of the song was the portion that is highlighted in yellow. Understanding how to properly translate this value from what most are familiar with in HScript to Python became another obstacle that required a bit of research. Even though the entire expression is a single line, this had impeded progress for a significant duration. Once I managed to find the correct means of scripting it, the entire process finally worked as it was intended.
Conclusion
CHOP's networks exist within a completely separate ballpark from what most Houdini processes use. Whether they be SOP's, DOP's, VOP's, or any other operator, all of these processes have one thing in common, they all use attributes. CHOP's are different because they do not currently (at the time of this publication) have attributes, but instead utilize "Global Variables." It is then necessary to take those variables and convert them into a format that Houdini can understand. If this is done correctly, all of these values can be read into the network and utilized at their fullest.