views:

188

answers:

4

I am writing an application needs to use large audio multi-samples, usually around 50 mb in size. One file contains approximately 80 individual short sound recordings, which can get played back by my application at any time. For this reason all the audio data gets loaded into memory for quick access.

However, when loading one of these files, it can take many seconds to put into memory, meaning my program if temporarily frozen. What is a good way to avoid this happening? It must be compatible with Windows and OS X. It freezes at this : myMultiSampleClass->open(); which has to do a lot of dynamic memory allocation and reading from the file using ifstream.

I have thought of two possible options:

  1. Open the file and load it into memory in another thread so my application process does not freeze. I have looked into the Boost library to do this but need to do quite a lot of reading before I am ready to implement. All I would need to do is call the open() function in the thread then destroy the thread afterwards.

  2. Come up with a scheme to make sure I don't load the entire file into memory at any one time, I just load on the fly so to speak. The problem is any sample could be triggered at any time. I know some other software has this kind of system in place but I'm not sure how it works. It depends a lot on individual computer specifications, it could work great on my computer but someone with a slow HDD/Memory could get very bad results. One idea I had was to load x samples of each audio recording into memory, then if I need to play, begin playback of the samples that already exist whilst loading the rest of the audio into memory.

Any ideas or criticisms? Thanks in advance :-)

A: 

You might want to consider a producer-consumer approach. This basically involved reading the sound data into a buffer using one thread, and streaming the data from the buffer to your sound card using another thread.

The data reader is the producer, and streaming the data to the sound card is the consumer. You need high-water and low-water marks so that, if the buffer gets full, the producer stops reading, and if the buffer gets low, the producer starts reading again.

A C++ Producer-Consumer Concurrency Template Library
http://www.bayimage.com/code/pcpaper.html

EDIT: I should add that this sort of thing is tricky. If you are building a sample player, the load on the system varies continuously as a function of which keys are being played, how many sounds are playing at once, how long the duration of each sound is, whether the sustain pedal is being pressed, and other factors such as hard disk speed and buffering, and amount of processor horsepower available. Some programming optimizations that you eventually employ will not be obvious at first glance.

Robert Harvey
probably not relevant in this case - he's not trying to stream audio, he has a multisample file where each sample can be played individually.
snemarch
@snemarch: Then you do it using multiple buffers over multiple threads.
Robert Harvey
A: 

I like solution 1 as a first attempt -- simple & to the point.

If you are under Windows, you can do asynchronous file operations -- what they call OVERLAPPED -- to tell the OS to load a file & let you know when it's ready.

John Dibling
I might do solution 1 after all, tried memory mapped files and they didn't work out too good. And my code needs to be completely platform independent, is there a way to do this overlapped loading on Mac OS X also?
orgazoid
+1  A: 

Use a memory mapped file. Loading time is initially "instant", and the overhead of I/O will be spread over time.

ergosys
Sounds good, but the page fault will typically happen at the worst possible time: when the audio decoder needs a sample. Producing clicks. It may work if it buffers well.
Hans Passant
An idea: memory-map the file, have a background thread pre-touch pages?
snemarch
The filesystem buffering/ read-ahead should take care of any pops or clicks.
gbrandt
@gbrandt: is read-ahead done for memory mapped files, though?
snemarch
@snemarch: Although I recall reading it somewher (at least for windows) that memory mapped files got buffered, I can't that info anywhere right now. So I may be incorrect as this is a 15+ year old memory. The HD cacheing probably reads ahead, but there is no guarantee it has read what you want.
gbrandt
Hi, I tried using memory mapped files, and it would appear that the files do not get buffered. The problem is I need to jump around the file to different offsets, sometime reading a few bytes from the beginning then a few bytes near the end within a very small amount of time and this causes major clicking/glitch issues. I read somewhere that memory mapped files should not be used in the critical process function of a program, and unfortunately, I need to access the information in the critical part of my program, therefore memory mapping is not appropriate in this situation.
orgazoid
@gbrandt: it definitely ought to read ahead, and I'd think it does. However, with a md5summer I did back on XP, I still got a #pf for each page processed... doesn't really say whether there's caching or not, though.
snemarch
A: 

i think the best solution is to load a small chunk or single sample of wave data at a time during playback using asynchronous I/O (as John Dibling mentioned) to a fixed size of playback buffer.

the strategy will be fill the playback buffer first then play (this will add small amount of delay but guarantees continuous playback), while playing the buffer, you can re-fill another playback buffer on different thread (overlapped), at least you need to have two playback buffer, one for playing and one for refill in the background, then switch it in real-time

later you can set how large the playback buffer size based on client PC performance (it will be trade off between memory size and processing power, fastest CPU will require smaller buffer thus lower delay).

uray
Any delay at all is unacceptable in my application so this method won't be a very good solution.
orgazoid