OSStatus AUBase::DoRender()

in Source/AUBase.cpp [1228:1337]


OSStatus AUBase::DoRender(AudioUnitRenderActionFlags& ioActionFlags,
	const AudioTimeStamp& inTimeStamp, UInt32 inBusNumber, UInt32 inFramesToProcess,
	AudioBufferList& ioData)
{
	const auto errorExit = [this](OSStatus error) {
		AUSDK_LogError("  from %s, render err: %d", GetLoggingString(), static_cast<int>(error));
		SetRenderError(error);
		return error;
	};

	OSStatus theError = noErr;

	[[maybe_unused]] const DenormalDisabler denormalDisabler;

	try {
		AUSDK_Require(IsInitialized(), errorExit(kAudioUnitErr_Uninitialized));
		if (inFramesToProcess > mMaxFramesPerSlice) {
			static UInt64 lastTimeMessagePrinted = 0;
			const UInt64 now = HostTime::current();
			if (static_cast<double>(now - lastTimeMessagePrinted) >
				HostTime::frequency()) { // not more than once per second.
				lastTimeMessagePrinted = now;
				AUSDK_LogError("kAudioUnitErr_TooManyFramesToProcess : inFramesToProcess=%u, "
							   "mMaxFramesPerSlice=%u",
					static_cast<unsigned>(inFramesToProcess),
					static_cast<unsigned>(mMaxFramesPerSlice));
			}
			return errorExit(kAudioUnitErr_TooManyFramesToProcess);
		}
		AUSDK_Require(!UsesFixedBlockSize() || inFramesToProcess == GetMaxFramesPerSlice(),
			errorExit(kAudio_ParamError));

		auto& output = Output(inBusNumber); // will throw if non-existant
		if (ASBD::NumberChannelStreams(output.GetStreamFormat()) != ioData.mNumberBuffers) {
			AUSDK_LogError(
				"ioData.mNumberBuffers=%u, "
				"ASBD::NumberChannelStreams(output.GetStreamFormat())=%u; kAudio_ParamError",
				static_cast<unsigned>(ioData.mNumberBuffers),
				static_cast<unsigned>(ASBD::NumberChannelStreams(output.GetStreamFormat())));
			return errorExit(kAudio_ParamError);
		}

		const unsigned expectedBufferByteSize =
			inFramesToProcess * output.GetStreamFormat().mBytesPerFrame;
		for (unsigned ibuf = 0; ibuf < ioData.mNumberBuffers; ++ibuf) {
			AudioBuffer& buf = ioData.mBuffers[ibuf]; // NOLINT
			if (buf.mData != nullptr) {
				// only care about the size if the buffer is non-null
				if (buf.mDataByteSize < expectedBufferByteSize) {
					// if the buffer is too small, we cannot render safely. kAudio_ParamError.
					AUSDK_LogError("%u frames, %u bytes/frame, expected %u-byte buffer; "
								   "ioData.mBuffers[%u].mDataByteSize=%u; kAudio_ParamError",
						static_cast<unsigned>(inFramesToProcess),
						static_cast<unsigned>(output.GetStreamFormat().mBytesPerFrame),
						expectedBufferByteSize, ibuf, static_cast<unsigned>(buf.mDataByteSize));
					return errorExit(kAudio_ParamError);
				}
				// Some clients incorrectly pass bigger buffers than expectedBufferByteSize.
				// We will generally set the buffer size at the end of rendering, before we return.
				// However we should ensure that no one, DURING rendering, READS a
				// potentially incorrect size. This can lead to doing too much work, or
				// reading past the end of an input buffer into unmapped memory.
				buf.mDataByteSize = expectedBufferByteSize;
			}
		}

		if (WantsRenderThreadID()) {
			mRenderThreadID = std::this_thread::get_id();
		}

		if (mRenderCallbacksTouched) {
			AudioUnitRenderActionFlags flags = ioActionFlags | kAudioUnitRenderAction_PreRender;
			mRenderCallbacks.foreach ([&](const RenderCallback& rc) {
				(*static_cast<AURenderCallback>(rc.mRenderNotify))(rc.mRenderNotifyRefCon, &flags,
					&inTimeStamp, inBusNumber, inFramesToProcess, &ioData);
			});
		}

		theError =
			DoRenderBus(ioActionFlags, inTimeStamp, inBusNumber, output, inFramesToProcess, ioData);

		SetRenderError(theError);

		if (mRenderCallbacksTouched) {
			AudioUnitRenderActionFlags flags = ioActionFlags | kAudioUnitRenderAction_PostRender;

			if (theError != noErr) {
				flags |= kAudioUnitRenderAction_PostRenderError;
			}

			mRenderCallbacks.foreach ([&](const RenderCallback& rc) {
				(*static_cast<AURenderCallback>(rc.mRenderNotify))(rc.mRenderNotifyRefCon, &flags,
					&inTimeStamp, inBusNumber, inFramesToProcess, &ioData);
			});
		}

		// The vector's being emptied
		// because these events should only apply to this Render cycle, so anything
		// left over is from a preceding cycle and should be dumped.  New scheduled
		// parameters must be scheduled from the next pre-render callback.
		if (!mParamEventList.empty()) {
			mParamEventList.clear();
		}
	} catch (const OSStatus& err) {
		return errorExit(err);
	} catch (...) {
		return errorExit(-1);
	}
	return theError;
}