top of page

March 24th

  • jesseemmothmusik
  • 24 mars
  • 4 min läsning

Last week or so I’ve taken my application to a finished state. The word finished in this case means that the user can start the program, do stuff with audio, save changes and close the program in the end. The word finished does however not mean that the application is finished at all(it’s confusing I know..).

I’ve implemented the stuff that needs to be there but the process of polishing the looks, the feel, the speed and the reliability hasn’t started yet.


Exporting from the app

The application can now export 16bit 44,1khz .wav files via the user interface. I tried to keep in mind that the future will most likely demand more options regarding this from the program and I therefore spent a lot of time making sure that such expansion to more formats would be easy. The general idea of the export action is to render the properties of a region into a new file while only updating what needs to be updated in that file (the file name, the data chunk and the sizes). This is where the choices of design will have an impact; if the audio regions where to keep a local copy of an audio buffer and all the info needed for exporting the development of export functionality would be very easy but the memory usage would not expand well. 

From my definition of what an audio region is I get the benefit of only keeping one copy of an audio buffer loaded in memory at all times and always referring to this with different settings through the audio regions. As you can imagine, this will lead to more work being done once we actually decide to export something. When the user hits the export button this is what happens:

  • The region instance asks to be written to a file through the global WaveAssetHandler(WAH)

  • The WAH gets the properties from the audio region and applies them onto a new copy of the region through it’s DSPEngine.

  • The WAH gets a copy of the WavInfo from the original asset pointed at by the audio region and replaces the file size and data size in the copy.

  • Finally the WAH uses it’s member WaveHandler to write to a file.


I think this separates the different tasks well into various places in the code base. The WAH worries about everything concerning the assets; the DSPEngine is just a calculator that applies changes to an audio buffer; the WaveHandler loads and writes a buffer and some info into a file without caring about their origin.


The UI for file exporting
The UI for file exporting


Saving changes

Now what about if the user changes their mind about a fade time or a gain change? Once a file is exported there is no way to get back to the audio region from that exported file! Without being able to open up the changes we made on a later occasion the application would not be very useful at all.

So, we need to store data about the audio regions somehow and then be able to open it up again. My way of doing this is right now through simply writing to a binary file and telling the user that this is a Scene with the extension .wio (for WAVEIO). The data written to the file is: 

  • Amount of regions in the scene

And per every region:

  • Position on screen

  • Size of the region ID

  • Region ID

  • Region properties

When the scene is loaded this is all we need to get the application back to the saved state. 


ree

One note about the scene structure is that the data saved and loaded is useful for a game engine! This enables a game to play a lot of variations of a single audio file.


Commands

Since the application is about editing regions the user expects to be able to undo, redo, copy and paste. My way of solving this is through a small command interface and through a list on the stack in which the program stores the commands and keeps an index to the current command. Undoing an action is just stepping backwards in the list and calling the member function Undo on that command.


Pure virtual interface for all commands
Pure virtual interface for all commands

I’m sure there are better solutions to this out there. This design results in a lot of class implementations (one for every single type of action the user should be able to undo) which is good in the sense that the functionality is split up well in the code-base. It will however result in a lot of typing, a lot of includes, a lot of duplicated code.


Undo / Redo
Undo / Redo

Stopping in time

I'm approaching a phase in the project where there has to be a stop for new functionality. Although all I want to do is feed this application with cool DSP-effects, intrinsic functions for super fast buffer calculations, better looking UI and so on - I need to think about having a stable product in the end of next week.

There are a few things I need to implement and fix before I start polishing. The user should be able to merge/glue regions together which will probably just involve rendering to a new file that is the sum of the two regions. I also want the user to be able to split the regions very precisely with the mouse (a bug has evolved in this functionality since it was first implemented). 

After this I will have an official “content stop” and be miserable for some days because of the apparent sad tasks of finding and fixing problems with code I’ve written (code which I convince myself is perfect but that I really know needs attention and refactoring…)




 
 

Senaste inlägg

Visa alla
bottom of page