Category Archives: Uncategorized

WAV file code completed!

I know it has been a few days since I wrote about designing C++ classes to read and write .WAV audio files, but stuff came up and I didn’t have time to finish it until today. But I finished it and it does work!

The approach I finally used was to break reading and writing into separate classes.

I defined structures for all the RIFF chunks that I know about.

struct fmtChunk // ID = "fmt "
{
 u16 AudioFormat; //PCM=1
 u16 Channels; //1=Mono, 2=Stereo
 u32 SampleRate; //Sample Rate in Hz
 u32 ByteRate; //SampleRate * Channels *
               //BitsPerSample/8
 u16 BlockAlign; //NumChannels * BitsPerSample/8 
 u16 BitsPerSample; //8,16, etc.
};

Originally I stored the headers as string macros, but since the Chunk IDs are 4 characters with no 0 for termination, it is hard to work with them using many of the normal C functions, so I made hex versions of them also.

#define fmtChunkID "fmt "
#define fmtChunkIDInt (0x20746d66)

For reading, I read the whole file into memory and keep it.

 void * AllData;

I parse every type of header that I know about, and keep pointers to anything that I think I will use.

 fmtChunk * fmt;
 factChunk * fact;
 samplerChunk * smpl;
 sampleLoop * loops;
 instrumentChunk * inst;
 cueChunk * cue;
 cuePoint * cuepoints; 
 void * data;//sample data

Some data I copy out of the header and keep as class members, others I just reference in their structures using the pointers I saved.

size_t size; //whole file size, determine at read time
u32 sampleDataStart; //first byte of sample data
u32 sampleDataLength; //length of sample data in bytes
u32 sampleCount; //sample count in samples
u16 loopCount; //number of loops
u16 cuePointCount; // number of cue points

Then I set up a bunch of methods to access all of the useful data.

void * AllSamples();
void * Sample(u32 SampleNum, u16 Channel=0);
u16 AudioFormat();
u16 Channels(); //1=Mono, 2=Stereo
u32 SampleRate(); //Sample Rate in Hz... 44100 for CD
...

The methods sometimes have to do a little math to find the data, and I might need to clean it up a little someday, but I think it works ok.

//length of loop in samples
u32 WAVfileIn::LoopLengthSamples(u16 index)
{
 if ( AllData && data && loops && loopCount && index < loopCount )
 {
 return( (loops[index].End - loops[index].Start) / (fmt->Channels * fmt->BitsPerSample/8) );
 }
 return( 0 );
}

The “Read” function is pretty large, since it parses every type of header.  Since I am using 32 bit integers instead of strings, I can use a big “switch/case” statement to check for all the types.

switch ( head->ID32 )
{
 case fmtChunkIDInt:
  ...
  fmt = (fmtChunk*)at;
  ...
  break;
 case dataChunkIDInt:
 ...

I even made an “anonymous union” to allow me to reference the data as integers or as characters.  Accessing as characters is more useful when I want to print them for debugging and such.

union ID4
{
 char ID[4]; 
 u32 ID32;
};

I read the header in first, check the type, save pointers or data or whatever I need to do, and then increment the pointer to the end of the data using the size embedded in the pointer.

at+=head->Size;

If I ever need to add the ability to process additional chunks later, they are easy to add to the structures  and “case” statements.

For the .WAV output class, it takes in enough data to build the header in the “Open” function, with the intent of fixing it up later when the “Close” function is called and we know how big the data is.

bool WAVfileOut::Open( _TCHAR * filename, 
 u32 SampleRate, //Sample Rate in Hz... 44100 for CD
 u16 BitsPerSample, //8,16, etc.
 u16 Channels, //1=Mono, 2=Stereo
 u16 AudioFormat) //PCM=1 currently we don't do any compression, so leave this alone

After setting up all of the headers for the basic stuff  (RIFF, fmt, fact, data…) it is ready for “Write” calls to add samples to the file.

bool WAVfileOut::Write(void * SampleData, 
                       u32 SampleCount)

Along the way, it uses “ftell” to store the location of various “size” fields that will need to change when the final data size is known.

 factAt = ftell(f);

Then when “Close” is executed, I can easily find my way back to write the correct value in it.

fseek(f,factAt,SEEK_SET)

So writing a file looks like this:

WAVfileOut wavout;
wavout.Open( _TEXT("test1.wav"),
             wavin.SampleRate(),
             wavin.BitsPerSample(),
             wavin.Channels(),
             wavin.AudioFormat()
);
wavout.Write( wavin.AllSamples(), 
              wavin.SampleCount());
wavout.Close();

Structuring it like this allows me to output as I go.

Yeah, I suppose I could have used a stream class, but not every platform has those. But there is almost always something that resembles fread and fwrite…

Now, you’ll notice there is no way to write loops and such yet, but since there is space between Open and Write, and between Write and Close, adding extra functions to write that data would be very simple.

Now that I have classes to work with .WAV files, I can start working on the audio engine…

 

 

WAV files: How I design myself to a standstill, and how to get around that.

So, I need to write some code that writes windows .WAV audio files, and I’ve been looking into the format. I am sure there are some libraries out there for doing such a thing, but since I can’t use open-source with Nintendo ever, I thought it would be good to make my own.

While I am working on writing the files, I might as well add code to read the files as well, which is where it gets a little more tricky.

Evidently, a .WAV file is actually a “RIFF” container format that can hold just about any type of data, and while there is some fairly standard stuff that you need in an audio file, there is also a bunch of other stuff that MIGHT be there, and you have to code to at least properly skip the stuff you don’t need if you want to have any hope of reading many of the .WAV files out there.

Fortunately, the format is fairly well designed… each of these “chunks” starts with a 4 letter ID code followed by a 4 byte length that covers the rest of the data in that chunk. So you can pretty much look at the ID to see if it is useful, read the length, and then either read or seek past the entire chunk. That is pretty easy.

Now, how to arrange all this in memory so that an arbitrary block of code can find what it needs in all of this is the tricky bit.

One could build up some separate data structures to store all the chunk data, and write routines to search through it for what you want later, but that is an awful lot of work.

On the other hand, it might be easier just to read the whole block, and then just traverse all the header stuff looking for what you want when you are ready to use it. But this also has drawbacks, namely that you are possibly keeping a lot of extra data around, and when you need to access information about that data you lose time searching for the headers you need.

Or, I could write my code as a toolbox for parsing the .WAV file on your own terms so that you can get what you want, but that seems like it would assume future programmers who might use this would understand enough about the .WAV file format that they would know how and why to use said toolkit. Will anybody besides me ever use this?

I suppose I could have it parse the important bits I know that I will need, and then give the option to give a list of chunk IDs to not throw away and a mechanism to find them again? Are they really that important?

And suddenly, I realize that I have become paralyzed by the design decisions, trying to make a perfect system that I can use forever and never write again, which will only make sure that I make something so complicated that I will never finish it, and will never WANT to use it again!

So I step back. What do I need it to do, minimum?

  • Write a stereo .WAV audio file at 44.1kHz.

What would be secondary goals?

  • Allow the sampling rate to be adjusted and be able to read the Rhythm Core Alpha 1 & 2 sounds, which are mono and contain loop data.

Anything else?

  • I wanted to be able to write the file as I go, rather than build it all in RAM, so that perhaps if I ported to smaller platforms later, I wouldn’t need so much memory.

Is there anything I can do to make future expansion easier?

  • Structure the chunk reading loop so that there is a good place to put processing for additional chunks.

Ok, those are more reasonable goals. The system will write .WAV files based on a small number of options, and will read at least mono sound data and handle the chunk data for loop points.

Anything beyond that which is required later will be written later. It is important not to be too hung up on the concept that you might need to rewrite or reengineer code sometimes. It will happen. It is easier to do than to write an everything machine from the start, and probably easier to maintain as well.

In short, the moral is: Write today’s program. Get it done.  Consider tomorrow’s program to a reasonable degree, but don’t let it hang you up or bog you down.

Nintendo Questions?

I just posted a bunch of questions on Nintendo’s DSi developer forums. I wonder if they still read those?

I asked about opening an office in Japan to sell my software there. I had sent a direct e-mail a few days ago and got no response…

I asked about their recent DLC deal with Gamestop and how to get my product in that.

I asked how I can get my product distributed in Russia, now that they are doing that.

I reiterated my question about building unattended demo stands that never really got answered before.

I’ll give it a day and then start calling them on the phone…

Adobe Imageready…. missing in action!

The main SoftEgg web page has not been updated in some time… it works well and there wasn’t really any need. But in order to link this log, I need to add another item to the top navigation bar. Well, that was made in Adobe Imageready… Adobe’s competitor to Macromedia Fireworks, before they bought Macromedia. So now I am fishing around trying to find Imageready on some early Photoshop CDs or some such.

Imageready was a really great tool, letting you design all the rollover states for graphics-based buttons and slices for cutting up borders for websites and etc. Since I prefer to do liquid layouts that conform to browser page size rather than force text into a column in the middle, that is useful stuff!

Unfortunately, Fireworks was the more popular tool, and when Adobe bought Macromedia, Imageready was scrapped and NEVER MENTIONED AGAIN! Which is too bad, because if was completely superior to Fireworks except in one department: Fireworks could properly write an 8-bit transparent PNG file.

So, anyway, I have to go find my old copy of Imageready so that I can add one menu item to the SoftEgg website. C’est la vie.

Word up!

WordPress, that is!

While we knuckle down for the years-long development process for Rhythm Core Alpha 3, or whatever it turns out to be, we thought we’d share our thoughts and experiences on this handly log page.

We hope to have some juicy technical stuff, some interesting maker-y things, some cool musical insights, stories from our music shows and exhibitions, harrowing tales of business, thrift store finds, and hopefully kinda light on the deep philosophical thoughts and whinging… we’ll save that for “der Facebook”…

I suppose our first task is to get this blog to look like it belongs in the SoftEgg website…