0

I'm toying around with creating a pure Java audio mixing library, preferably one that can be used with Android, not entirely practical but definitely an interesting thing to have. I'm sure it's been done already, but just for my own learning experience I am trying to do this with wav files since there are usually no compression models to work around.

Given the nature of java.io, it defines many InputStream type of classes. Each implements operations that are primarily for reading data from some underlying resource. What you do with data afterward, dump it or aggregate it in your own address space, etc, is up to you. I want this to be purely Java, e.g. works on anything (no JNI necessary), optimized for low memory configurations, and simple to extend.

I understand the nature of the RIFF format and how to assemble the PCM sample data, but I'm at a loss for the best way of managing the memory required for inflating the files into memory. Using a FileInputStream, only so much of the data is read at a time, based on the underlying file system and how the read operations are invoked. FileInputStream doesn't furnish a method of indexing where in the file you are so that retrieving streams for mixing later is not possible. My goal would be to inflate the RIFF document into Java objects that allow for reading and writing of the appropriate regions of the underlying chunk.

If I allocate space for the entire thing, e.g. all PCM sample data, that's like 50 MB per average song. On a typical smart phone or tablet, how likely is it that this will affect overall performance? Would I be better off coming up with my own InputStream type that maybe keeps track of where the chunks are in the InputStream? For file's this will result in lots of blocking when fetching PCM samples, but will still cut down on the overall memory footprint on the system.

Joey Carson
  • 2,973
  • 7
  • 36
  • 60

1 Answers1

1

I'm not sure I understand all of your question, but I'll answer what I can. Feel free to clarify in the comments, and I'll edit.

Don't keep all file data in in memory for a DAW-type app, or any file/video player that expects to play large files. This might work on some devices depending on the memory model, but you are asking for trouble.

Instead, read the required section of the file as needed (ie on demand). It'a actually a bit more complex than that because you don't want to read the file in the audio playback thread (you don't want audio playback, which is low latency, to depend on file IO, which is high-latency). To get around that, you may have to buffer some of the file in advance. (it depends on whether you are using a callback or blocking model)

Using FileInputStream works fine, you'll just have to keep track of where everything is in the file yourself (this involves converting milliseconds or whatever to samples to bytes and taking into account the size of the header[1]). A slightly better option is RandomAccessFile because it allows you to jump arround.

My slides from a talk on programing audio software might help, especially if you are confused by callback v blocking: http://blog.bjornroche.com/2011/11/slides-from-fundamentals-of-audio.html

[1] or, more correctly, knowing the offset of the audio data in the file.

Bjorn Roche
  • 11,279
  • 6
  • 36
  • 58
  • Thanks Bjorn, RandomAccessFile is exactly the data structure I'm looking for! Also, I definitely wouldn't read in the file in a playback thread due to blocking it. Rather I want to create a data structure to access the chunk regions in order to mix or filter their samples, and subsequently write new wav files. I just wanted to avoid reading the whole thing into memory, and wasn't seeing a way to do it using conventional input streams and byte buffers. I will definitely check out the your slides as well. Thanks a bunch. – Joey Carson May 27 '12 at 04:27
  • You can use FileInputStream, it just forces you to reopen if you want to go back to an earlier point in the file. In my experience, the performance hit for this is not a killer, but I've never tested it and YMMV. Obviously, RandomAccessFile is a more natural way to work. – Bjorn Roche May 27 '12 at 14:30