A separate AudioManager for each available sound card?


#1

Hi Jules.

I wonder, is the code below correct from your point of view. The point is that this code hangs my computer by using 100% of CPU time. But when I use the default AudioManager settings (just create it and use) it seems everything goes OK. The strange thing is the code works fine sometimes in XP but never in Vista. Are anyone able to explain what’s wrong with the following code:

...

	OwnedArray <AudioIODeviceType> types;
	{
		AudioDeviceManager().createAudioDeviceTypes(types);
	}

	const int typesSize = types.size();

	if(typesSize)
	{
		int idx = 0;
		for (; idx < typesSize; idx++)
		{
			if (types[idx]->getTypeName() ==
#ifdef JUCE_WIN32
																T("DirectSound")
#elif defined (JUCE_LINUX)
																T("ALSA")
#elif defined (JUCE_MACOSX)
																T("CoreAudio")
#endif
															)
			{
				types[idx]->scanForDevices();
				_deviceNames = types[idx]->getDeviceNames(false);

				break;
			}
		}

		if (idx == typesSize && !_deviceNames.size())
			_deviceNames = types[0]->getDeviceNames(false);

#ifdef JUCE_WIN32
	if (_deviceNames.size() > 1)
        _deviceNames.remove(0);
#endif

	}
	else
	{
        return false; //no sound cards found
	}

	const int nrOfDevices = _deviceNames.size();

	for(int idx = 0; idx < nrOfDevices; idx++)
	{
		_audioDeviceManagers.add(new AudioDeviceManager);
		_audioSourcePlayers.add(new AudioSourcePlayer);
		_mixerAudioSources.add(new MixerAudioSource);

		_audioDeviceManagers[idx]->initialise(0, 2, 0, false, _deviceNames[idx]);

		//_audioDeviceManagers[idx]->setAudioDevice(_deviceNames[idx], 960, 48000, 0, 0, true);
		AudioDeviceManager::AudioDeviceSetup setup;

		setup.bufferSize = 960;
		setup.sampleRate = 48000.0;
		setup.outputDeviceName = _deviceNames[idx];

		_audioDeviceManagers[idx]->setAudioDeviceSetup(setup, true);

		_audioSourcePlayers[idx]->setSource(_mixerAudioSources[idx]);
		_audioDeviceManagers[idx]->setAudioCallback(_audioSourcePlayers[idx]);

		_deviceMenu.addItem(idx + 1, _deviceNames[idx], true, false, _deviceColours[idx % 8]);
	}
...

Maybe I’m wrong… Might be I should use another way to get the functionality I want. I want to initialize all available sound cards at start up time and use each one independently for separate audio tracks. I want one track to play to one sound card and another track to play to another sound card simultaneously. Any help?


#2

Well, I can’t see any obvious problems with it, though maybe the OS is struggling because all these audio managers are trying to open multiple instances of the same devices? Where exactly is it hanging?

In a situation like this, it might be better to forget the device manager, and just use the devices directly. The manager provides an easy way of switching between devices, but if you’re choosing the device manually, it’ll only add a layer of beurocracy.


#3

The “hang” happens after some number of calls to addInputSource to add sources to a mixer:

…the application still continues to run as if everything’s OK, but two threads consumes (two sound cards… see the interdependence) 100% of CPU time like a hungry beast devours a hot fresh flesh and an instance back, it tries to understand the moment could be avoided if the universe and all other stuff would be arranged the way people consider it perfect (sorry, I’m still drunk ;)).

Thank you, Julian. I’ll try to use device classes outside AudioDeviceManager. I’ll tell you what it’s run out to…

…about AudioDeviceManger and its bureaucracy you’ve mentioned, yes, you’re right. It feels like shooting sparrows from a bazooka :slight_smile:


#4

Well, Julian, I’ve discovered the root of all evil at last :wink:

The void DSoundAudioIODevice::resync() function is the culprit that causes my computer (only mine?) to consume 100% of its CPU time.

The way is that: Sometimes (especially on first run), Windows spends more time to open a DirectSound device with some untypical (unprepared) params (e.g. 48000 Hz instead of 44100 or something) or if the OS is busy enough to accomplish the task in time you expect it to do (your latencyMs variable). In this case, in your thread void DSoundAudioIODevice::run() function, your code mistakenly presumes a quasi desynchronization even if no actual sound manipulations were performed… So… take a deep breath, Julian :)… at that point you call resync() function that closes all opened output channels (opened milliseconds ago) and tries to open them again but it fails with the error “No driver” (according to your getDSErrorMessage function) on creating a primary buffer (!). As a side effect, this function somehow removes all channels from the outChans array and cotinues to run inside the main thread loop in vain consuming 100% of a CPU time.

I’ve found 3 ways to solve the problem, but I liked the last one. The idea was to simplify the resync function itself. So, the code is:

void resync()
    {
        if (! threadShouldExit())
        {
            sleep (5);

            int i(0);

            for (; i < outChans.size(); ++i)
                outChans.getUnchecked(i)->synchronisePosition();

            for (i = 0; i < inChans.size(); ++i)
                inChans.getUnchecked(i)->synchronisePosition();
        }
    }

I removed dangerous and senseless code like SetThreadPriority (GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL); and SetPriorityClass (GetCurrentProcess(), REALTIME_PRIORITY_CLASS);. Why dangerous? Because, in Windows, the task scheduler becomes irresponsible causing the whole system to hang. Why senseless? Because, in Windows, it’s much more costly to switch thread priority for too short tasks than let the code flow without that switch. That’s the reality.

…now, everything works like a charm for… 2 testing hours.

I think, Julian, you should do something about it.


#5

Right - thanks for the helpful info there. I added those priority changes fairly recently in an attempt to get a more reliable sync-up between input and output devices. It helped to do that to some extent, but if it’s causing serious problems like this then I’ll certainly get rid of it. Thanks!


#6

I wouldlike to know, if its possible to set or initialise the devices samplerate (depending on that of my audiofile). (Please don´t ask for the reason :wink: ) I can´t / don´t want to use the 3rd param of AudioTransportSource.setSource.

Thanks for your help!


#7

Of course it’s possible, but it’s also probably a really dumb idea. What happens when you want to play a 48K wave on a device that only supports 44.1?


#8

If the initialised samplerate mismatches that of my audiofile, I´ll give a message alert and won´t play that file.

Would you tell me, how to initialise/set it, please?
thanks!


#9

AudioDeviceManager::setAudioDeviceSetup()

Either you’re working on a very specific niche product, or god help anyone who has to use your software!


#10

Ok, and one additional thing. if I´d allow the user to change his device, I will first getAudioDeviceSetup(), set sampleRate in struct AudioDeviceSetup and finally setAudioDeviceSetup(). I will do this in one of the callbacks AudioDeviceAboutToStart() or prepareToPlay().

I appreciate your warnings, this helps me to learn better. But this way is mandotory by using the SDK, as I mentioned before. For its algorithm it is required.

thx


#11

So you’re going to change the sample rate in the callback which is called by the audio device when it has finished initialising its sample rate. Brilliant. Good luck with that.

For the last time, I don’t want to hear any more of this bollocks about audio devices.

Why aren’t you asking us how to make the output of your SDK match the rate that your AudioSource is being asked to produce? Or how to convert the fixed-size blocks from your SDK into the actual sizes that you need? Those are the real problems you’re trying to solve, and neither are very hard.

Audio devices are irrelevant to what happens inside an AudioSource. Forget them. Do not ask me about them ever again!


#12

:o okey, no prob.
I will think my design over.