[Interest] is it ok to push audio on a background thread, not a timer?

David M. Cotter dave at kjams.com
Wed May 20 04:03:39 CEST 2020


>> but since this i_auP is NOT a QObject, it's my own class, that i'm fine to
>> allocate it in one thread (main) and then use it on another thread (audio
>> pump thread) (provided and assuming i am careful to mutex any access from
>> the main thread)
> 
> i_auP is not a QObject, but i_auP->i_qGeneratorP is.
> 
> How was *that* pointer obtained?
sorry, i thought i clarified that, saying that in addition to "i_qOutputP", i said:

> they are both QObjects, and are allocated with "new" during i_auP->Start()

which is on the background thread during  run(), the thread where they'll be used. not created on the main thread.

>>> Are you saying that this calls a function that returned null?
>> sorry, what is "this" and what function are you referring to that returns
>> null?
> The code I had pasted:
>                i_auP(in_thiz->GetCUnit_Out())
> 
> Since you initialise i_auP using the same function (GetCUnit_Out) that later 
> you initialise auP in render() and then use auP->i_qGeneratorP, I have to ask 
> if at this time the generator pointer is already valid.
yes

the only QObjects are the thread object ( CT_AudioRender), and i_qGeneratorP and i_qOutputP

all other object are guaranteed to not be QObjects and guaranteed to correctly exist.

func GetCUnit_Out() just returns an existing, already allocated custom CAudioUnit object, that is not a QObject. it is guaranteed to exist before this code is executed.

the code executes, the pump starts, the render call calls back into my generator source, requests bytes, and sends them into the output audio device.
it just produces silence on windows (works fine on mac)


if you want the rest of the code here it is:

OSStatus		CAudioUnit::Start(
#if _QT_
	QIODevice **ioPP
#endif
) {
	OSStatus	err = noErr;
	
	if (i_name == kOutputPlatStr) {

		#if OPT_AUDIO_UNITS
			ERR(AudioOutputUnitStart(i_auRef));

		#elif _QT_
			ConfigureAsOutput();

			CF_ASSERT(HasSource());

			i_qGeneratorP->start();

			//	push mode, returns the generator we'll use??
			*ioPP = i_qGeneratorP->i_qOutputP->start();

			i_qGeneratorP->i_qOutputP->resume();
		#endif

		if (!err) {
			i_output_startedB = true;
		}
	} else {
		CF_ASSERT(0);
		err = kAudioUnitErr_NoConnection;
	}
	
	return err;
}

void	CAudioUnit::ConfigureAsOutput()
{
	CAPlayThroughController		*controllerP = CAPlayThroughController::Get();

	if (controllerP == NULL) {
		PostAlert(SSLocalize("There was a problem initializing audio output, you may want to quit and re-run", "oops").utf8Z());
	} else {
		controllerP->Reset(true);
		kJ_AudioDeviceID	devID = controllerP->GetOuputID();
		SetProperty(kAudioOutputUnitProperty_CurrentDevice, devID);
	}
}

 void		CAudioUnit::SetProperty(AudioUnitPropertyID paramID, const SuperString& str)
{
	CF_ASSERT(paramID == kAudioOutputUnitProperty_CurrentDevice);

	if (paramID == kAudioOutputUnitProperty_CurrentDevice) {
		CF_ASSERT(!HasSource());
		i_qGeneratorP = new QAudioGenerator(this);
		i_qGeneratorP->init(str);
	}
}

 void QAudioGenerator::init(kJ_AudioDeviceID devID)
{
	OSStatus					err = noErr;
	CAPlayThroughController		*controllerP = CAPlayThroughController::Get();
	QAudioDeviceInfo			devInfo(QAudioDevice_GetFromID(QAudio::AudioOutput, devID));

	CF_ASSERT(i_outUnitP);

	QAudioFormat				format;

	if (Is_ASBD_Blank(i_outUnitP->i_inASBD)) {
		Get_ASBD_Generic(&i_outUnitP->i_inASBD);
	}

	format = QAudioFormat_From_ASBD(i_outUnitP->i_inASBD);

	if (!devInfo.isFormatSupported(format)) {
		LogAudioDevice(devInfo);
		format = devInfo.nearestFormat(format);
		QAudioFormat_To_ASBD(format, i_outUnitP->i_inASBD);
	}

	i_qOutputP.reset(new QAudioOutput(devInfo, format));
}







More information about the Interest mailing list