Beispiel #1
0
bool
PAEngine::initHardware(IOService *provider)
{
	UInt32 sampleRates[] = SAMPLERATES;
	IOAudioSampleRate sampleRate;

	debugFunctionEnter();

	device = OSDynamicCast(PADevice, provider);
	if (!device)
		return false;

	if (!super::initHardware(provider))
		return false;

	virtualDeviceArray = OSArray::withCapacity(1);
	if (!virtualDeviceArray) {
		IOLog("%s(%p)::%s unable to allocate memory\n", getName(), this, __func__);
		return false;
	}
	
	sampleRate.whole = sampleRates[0];
	sampleRate.fraction = 0;
	setSampleRate(&sampleRate);
	setNewSampleRate(sampleRate.whole);

	setDescription(info->name);
	setNumSampleFramesPerBuffer(NUM_SAMPLE_FRAMES);

	info->audioBufferSize = AUDIO_BUFFER_SIZE * nStreams;

	audioInBuf  = IOBufferMemoryDescriptor::withCapacity(info->audioBufferSize, kIODirectionInOut);
	audioOutBuf = IOBufferMemoryDescriptor::withCapacity(info->audioBufferSize, kIODirectionInOut);
	
	if (!audioInBuf || !audioOutBuf) {
		IOLog("%s(%p)::%s unable to allocate memory\n", getName(), this, __func__);
		return false;
	}

	audioInBuf->prepare();
	audioOutBuf->prepare();

	for (UInt32 i = 0; i < nStreams; i++) {
		IOAudioStream *stream;
		char *streamBuf;

		if (i * CHANNELS_PER_STREAM < channelsIn) {
			streamBuf = (char *) audioInBuf->getBytesNoCopy() + (i * AUDIO_BUFFER_SIZE);
			stream = createNewAudioStream(kIOAudioStreamDirectionInput, streamBuf);
			if (!stream) {
				IOLog("%s(%p)::%s failed to create audio streams\n", getName(), this, __func__);
				return false;
			}

			addAudioStream(stream);
			audioStream[i * 2] = stream;
			stream->release();
		}

		if (i * CHANNELS_PER_STREAM < channelsOut) {
			streamBuf = (char *) audioOutBuf->getBytesNoCopy() + (i * AUDIO_BUFFER_SIZE);
			stream = createNewAudioStream(kIOAudioStreamDirectionOutput, streamBuf);
			if (!stream) {
				IOLog("%s(%p)::%s failed to create audio streams\n", getName(), this, __func__);
				return false;
			}

			addAudioStream(stream);
			audioStream[(i * 2) + 1] = stream;
			stream->release();
		}
	}

	if (info->audioContentType == kPADeviceAudioContentMixdown)
		return addVirtualDevice(info, audioInBuf, audioOutBuf, this) == kIOReturnSuccess;

	return true;
}
Beispiel #2
0
bool	AREngine::CreateStreams(IOAudioSampleRate* outInitialSampleRate, UInt32* outNumberChannels)
{
	//	set the return values
	bool theAnswer = true;
	*outNumberChannels = 0;
	
	//	set up some local variables
	OSArray* theFormatArray = NULL;
	OSArray* theSampleRateArray = NULL;
	OSString* theOSString = NULL;
	UInt32 theNumberStreams = NUM_STREAMS;
	OSNumber* theOSNumber = NULL;
	
	//	get the array of formats
	theFormatArray = OSDynamicCast(OSArray, getProperty(FORMATS_KEY));
	FailIfNULLWithAction(theFormatArray, theAnswer = false, Done, "AREngine::CreateStreams: Couldn't get the format array");
	
	//	get the array of sample rates
	theSampleRateArray = OSDynamicCast(OSArray, getProperty(SAMPLE_RATES_KEY));
	FailIfNULLWithAction(theSampleRateArray, theAnswer = false, Done, "AREngine::CreateStreams: Couldn't get the sample rate array");
	
	//	get the description
	theOSString = OSDynamicCast(OSString, getProperty(DESCRIPTION_KEY));
	if(theOSString != NULL)
	{
		setDescription(theOSString->getCStringNoCopy());
	}
	
	//	get the number of streams
	theOSNumber = OSDynamicCast(OSNumber, getProperty(NUM_STREAMS_KEY));
	if(theOSNumber != NULL)
	{
		theNumberStreams = theOSNumber->unsigned32BitValue();
	}
	
	//	make the streams
	for(UInt32 theStreamNumber = 0; theStreamNumber < theNumberStreams; ++theStreamNumber)
	{
		//	initialize some local variables
		bool theResult = false;
		UInt32 theMaxBitWidth = 0;
		UInt32 theMaxNumberChannels = 0;
		IOAudioStream* theInputStream = NULL;
		IOAudioStream* theOutputStream = NULL;
		OSCollectionIterator* theFormatIterator = NULL;
		OSCollectionIterator* theSampleRateIterator = NULL;
		OSDictionary* theFormatDictionary = NULL;
		IOAudioSampleRate theSampleRate = { 0, 0 };
		IOAudioStreamFormat theInitialFormat;
		bool theInitialFormatSet = false;
		char theInputStreamName[32];
		char theOutputStreamName[32];
		UInt32 theStreamBufferSize = 0;

		//	allocate and initialize the input stream
		if(theNumberStreams > 1)
		{
			snprintf(theInputStreamName, 32, "Input Stream #%ld", theStreamNumber + 1);
		}
		else
		{
			snprintf(theInputStreamName, 32, "Input Stream");
		}
		theInputStream = new IOAudioStream;
		FailIfNULLWithAction(theInputStream, theAnswer = false, Error, "AREngine::CreateStreams: couldn't create the input stream");
		theResult = theInputStream->initWithAudioEngine(this, kIOAudioStreamDirectionInput, *outNumberChannels + 1, theInputStreamName);
		FailIfWithAction(!theResult, theAnswer = false, Error, "AREngine::CreateStreams: couldn't initialize the input stream");

		//	allocate and initialize the output stream
		if(theNumberStreams > 1)
		{
			snprintf(theOutputStreamName, 32, "Output Stream #%ld", theStreamNumber + 1);
		}
		else
		{
			snprintf(theOutputStreamName, 32, "Output Stream");
		}
		theOutputStream = new IOAudioStream;
		FailIfNULLWithAction(theOutputStream, theAnswer = false, Error, "AREngine::CreateStreams: couldn't create the output stream");
		theResult = theOutputStream->initWithAudioEngine(this, kIOAudioStreamDirectionOutput, *outNumberChannels + 1, theOutputStreamName);
		FailIfWithAction(!theResult, theAnswer = false, Error, "AREngine::CreateStreams: couldn't initialize the output stream");

		//	make an iterator for the format array
		theFormatIterator = OSCollectionIterator::withCollection(theFormatArray);
		FailIfNULLWithAction(theFormatIterator, theAnswer = false, Error, "AREngine::CreateStreams: couldn't create the format iterator");
		
		//	make an iterator for the sample rate array
		theSampleRateIterator = OSCollectionIterator::withCollection(theSampleRateArray);
		FailIfNULLWithAction(theSampleRateIterator, theAnswer = false, Error, "AREngine::CreateStreams: couldn't create the sample rate iterator");

		//	iterate through the formats
		theFormatIterator->reset();
		theFormatDictionary = (OSDictionary*)theFormatIterator->getNextObject();
		while(theFormatDictionary != NULL)
		{
			//	make sure we have a dictionary
			if(OSDynamicCast(OSDictionary, theFormatDictionary) != NULL)
			{
				//	convert the dictionary into something we can deal with
				IOAudioStreamFormat theFormat;
				FailIfNULLWithAction(IOAudioStream::createFormatFromDictionary(theFormatDictionary, &theFormat), theAnswer = false, Error, "AREngine::CreateStreams: couldn't make a format out of the dictionary");
				
				//	make sure the initial format is set
				if(!theInitialFormatSet)
				{
					theInitialFormat = theFormat;
				}
				
				//	iterate through the sample rates
				theSampleRateIterator->reset();
				theOSNumber = (OSNumber*)theSampleRateIterator->getNextObject();
				while(theOSNumber != NULL)
				{
					//	make sure we have a number
					if(OSDynamicCast(OSNumber, theOSNumber) != NULL)
					{
						//	get the sample rate
						theSampleRate.whole = theOSNumber->unsigned32BitValue();
						
						//	make sure the initial sample rate is set
						if(outInitialSampleRate->whole == 0)
						{
							outInitialSampleRate->whole = theSampleRate.whole;
						}

						//	add the format to the input stream
						theInputStream->addAvailableFormat(&theFormat, &theSampleRate, &theSampleRate);
						
						//	add the format to the output stream
						theOutputStream->addAvailableFormat(&theFormat, &theSampleRate, &theSampleRate);
						
						//	track a few things
						theMaxNumberChannels = (theFormat.fNumChannels > theMaxNumberChannels) ? theFormat.fNumChannels : theMaxNumberChannels;
						theMaxBitWidth = (theFormat.fBitWidth > theMaxBitWidth) ? theFormat.fBitWidth : theMaxBitWidth;
					}
					
					//	go to the next sample rate
					theOSNumber = (OSNumber*)theSampleRateIterator->getNextObject();
				}
			}
			
			//	go to the next format
			theFormatDictionary = (OSDictionary*)theFormatIterator->getNextObject();
		}
		
		//	calculate the size of the stream buffer
		theStreamBufferSize = mBlockSize * mNumberBlocks * theMaxNumberChannels * theMaxBitWidth / 8;
		
		//	allocate the buffers if necessary
		if(mOutputBuffer == NULL)
		{
			//	calculate the size
			mOutputBufferSize = theStreamBufferSize * theNumberStreams;
			
			//	allocate the output buffer
			mOutputBuffer = (void*)IOMallocAligned(mOutputBufferSize, PAGE_SIZE);
			FailIfNULLWithAction(mOutputBuffer, theAnswer = false, Error, "AREngine::CreateStreams: couldn't allocate the output buffer");
			
			//	the input size is the same as the output size
			mInputBufferSize = mOutputBufferSize;
			
			//	allocate the input buffer
			mInputBuffer = mOutputBuffer;
		}
		
		//	set some info about the stream
		theInputStream->setTerminalType(INPUT_UNDEFINED);
		theOutputStream->setTerminalType(OUTPUT_UNDEFINED);
		
		//	set the initial stream formats
		theInputStream->setFormat(&theInitialFormat, false);
		theOutputStream->setFormat(&theInitialFormat, false);
		
		//	set the data buffer for the streams
		theInputStream->setSampleBuffer(&((UInt8*)mInputBuffer)[theStreamBufferSize * theStreamNumber], theStreamBufferSize);
		theOutputStream->setSampleBuffer(&((UInt8*)mOutputBuffer)[theStreamBufferSize * theStreamNumber], theStreamBufferSize);
		
		//	add the streams to the engine
		addAudioStream(theInputStream);
		theInputStream->release();
		theInputStream = NULL;

		addAudioStream(theOutputStream);
		theOutputStream->release();
		theOutputStream = NULL;

		theFormatIterator->release();
		theFormatIterator = NULL;
		
		theSampleRateIterator->release();
		theSampleRateIterator = NULL;

		*outNumberChannels += theMaxNumberChannels;

		continue;

Error:
		if(theInputStream)
		{
			theInputStream->release();
		}

		if(theOutputStream)
		{
			theOutputStream->release();
		}

		if(theFormatIterator)
		{
			theFormatIterator->release();
		}

		if(theSampleRateIterator)
		{
			theSampleRateIterator->release();
		}

		goto Done;
	}
	
Done:
	return theAnswer;
}
IOAudioStream *kXAudioEngine::createNewAudioStream(int chn,IOAudioStreamDirection direction, UInt32 sampleBufferSize)
{
    IOAudioStream *audioStream = new IOAudioStream;
    
    if (audioStream) 
    {
        if (!audioStream->initWithAudioEngine(this, direction, 1)) 
		{
            audioStream->release();
        } else {
            IOAudioSampleRate rate;
			
			void *sampleBuffer=NULL;
			
			IOAudioStreamFormat format = {
				1,												// num channels
				kIOAudioStreamSampleFormatLinearPCM,			// sample format
				kIOAudioStreamNumericRepresentationSignedInt,	// numeric format
				bps,											// bit depth
				bps,											// bit width
				kIOAudioStreamAlignmentHighByte,				// high byte aligned - unused because bit depth == bit width
				kIOAudioStreamByteOrderLittleEndian,			// little endian
				true,											// format is mixable
				0												// driver-defined tag - unused by this driver
			};
			
			kx_voice_buffer buffer;
			bzero(&buffer, sizeof(buffer));
			
			buffer.desc = my_alloc_contiguous(sampleBufferSize, &(buffer.addr), &(buffer.physical));
			
			if (!buffer.desc)
				return NULL;
			
			buffer.size=sampleBufferSize;
			buffer.that=this;
			buffer.notify=-n_frames;
			
			sampleBuffer=buffer.addr;
			
				// allocate memory:
			if(direction==kIOAudioStreamDirectionOutput)
			{				
				int need_notifier=VOICE_OPEN_NOTIMER;
				if(chn==0) // first voice?
					need_notifier|=VOICE_OPEN_NOTIFY; // half/full buffer
				
				int mapping[]=
				{ //2,3,4,5,6,7,8,9 - kX:  front, rear, center+lfe, back
				  //1,2,3,4,5,6,7,8 - OSX: front, center+lfe, rear, back
					2,3,6,7,4,5,8,9 };
					// wave 2/3 - front
					// wave 6/7 - center+lfe
					// wave 4/5 - rear
					// 8/9 - rear center/etc.
				
				int i=kx_allocate_multichannel(hw,bps,sampling_rate,need_notifier,&buffer,DEF_ASIO_ROUTING+mapping[chn]); // start with 2/3
				
				if(i>=0)
				{
					hw->voicetable[i].asio_id=this;
					hw->voicetable[i].asio_mdl=0;
					hw->voicetable[i].asio_user_addr=0;
					hw->voicetable[i].asio_kernel_addr=0;
					hw->voicetable[i].asio_channel=chn;
					
					if(chn==0) // first?
					{
						hw->asio_notification_krnl.n_voice=i;
						hw->asio_notification_krnl.semi_buff=(hw->voicetable[i].param.endloop-hw->voicetable[i].param.startloop)/2;
					}
				}
			}
			else
				if(direction==kIOAudioStreamDirectionInput)
				{					
					format.fNumChannels = 8;
					
					hw->mtr_buffer.desc = buffer.desc;
					hw->mtr_buffer.addr = buffer.addr;
					hw->mtr_buffer.size = buffer.size;
					hw->mtr_buffer.dma_handle = buffer.physical; 
					
					//kx_writeptr(hw,FXIDX,0,0);

					//debug("createNewAudioStream FXBA:\n");
					//kx_writeptr_prof(hw, FXBA, 0, hw->mtr_buffer.dma_handle);
					kx_writeptr(hw, FXBA, 0, hw->mtr_buffer.dma_handle);

					dword ch = (1 << (format.fNumChannels * 2)) - 1;	// 24bit needs 2 physical channels
					debug("createNewAudioStream FXWCH/FXWC_K1: %x\n", ch);

					if(hw->is_10k2)
					{
						kx_writeptr(hw,FXWCL, 0, 0);
						kx_writeptr(hw,FXWCH, 0, ch);
					}
					else
						kx_writeptr(hw,FXWC_K1, 0, ch << 16);
				}
			
				// As part of creating a new IOAudioStream, its sample buffer needs to be set
				// It will automatically create a mix buffer should it be needed
			if(sampleBuffer && sampleBufferSize)
				audioStream->setSampleBuffer(sampleBuffer, sampleBufferSize);
            
				// This device only allows a single format and a choice of 2 different sample rates
            rate.fraction = 0;
            rate.whole = sampling_rate;
            audioStream->addAvailableFormat(&format, &rate, &rate);
            
				// Finally, the IOAudioStream's current format needs to be indicated
            audioStream->setFormat(&format);
        }
    }
    
    return audioStream;
}
Beispiel #4
0
bool PhantomAudioEngine::createAudioStreams(IOAudioSampleRate *initialSampleRate)
{
    bool result = false;
    OSNumber *number;
    UInt32 numStreams, streamNum;
    OSArray *formatArray, *sampleRateArray;
    UInt32 startingChannelID = 1;
    IOAudioControl *control;
    OSString *desc;
    OSBoolean *boolean;
    bool separateStreamBuffers = FALSE, separateInputBuffers = FALSE;
    
    desc = OSDynamicCast(OSString, getProperty(DESCRIPTION_KEY));
    if (desc) {
        setDescription(desc->getCStringNoCopy());
    }
    
    number = OSDynamicCast(OSNumber, getProperty(NUM_STREAMS_KEY));
    if (number) {
        numStreams = number->unsigned32BitValue();
    } else {
        numStreams = NUM_STREAMS;
    }
    
    formatArray = OSDynamicCast(OSArray, getProperty(FORMATS_KEY));
    if (formatArray == NULL) {
        goto Done;
    }
    
    sampleRateArray = OSDynamicCast(OSArray, getProperty(SAMPLE_RATES_KEY));
    if (sampleRateArray == NULL) {
        goto Done;
    }
    
    boolean = OSDynamicCast(OSBoolean, getProperty(SEPARATE_STREAM_BUFFERS_KEY));
    if (boolean != NULL) {
        separateStreamBuffers = boolean->getValue();
    }
    
    boolean = OSDynamicCast(OSBoolean, getProperty(SEPARATE_INPUT_BUFFERS_KEY));
    if (boolean != NULL) {
        separateInputBuffers = boolean->getValue();
    }
    
    if (separateStreamBuffers) {
        IOLog("PhantomAudioEngine::createAudioStreams() - Creating a separate buffer for each stream.\n");
    } else {
        IOLog("PhantomAudioEngine::createAudioStreams() - Sharing one buffer among all streams.\n");
    }
    
    if (separateInputBuffers) {
        IOLog("PhantomAudioEngine::createAudioStreams() - Creating separate buffers for input and output.\n");
    } else {
        IOLog("PhantomAudioEngine::createAudioStreams() - Sharing input and output buffers.\n");
    }
    
    for (streamNum = 0; streamNum < numStreams; streamNum++) {
        IOAudioStream *inputStream = NULL, *outputStream = NULL;
        UInt32 maxBitWidth = 0;
        UInt32 maxNumChannels = 0;
        OSCollectionIterator *formatIterator = NULL, *sampleRateIterator = NULL;
        OSDictionary *formatDict;
        IOAudioSampleRate sampleRate;
        IOAudioStreamFormat initialFormat;
        bool initialFormatSet;
        UInt32 channelID;
        char outputStreamName[20], inputStreamName[20];
        UInt32 streamBufferSize;
        
        initialFormatSet = false;
        
        sampleRate.whole = 0;
        sampleRate.fraction = 0;
                
        inputStream = new IOAudioStream;
        if (inputStream == NULL) {
            goto Error;
        }
        
        outputStream = new IOAudioStream;
        if (outputStream == NULL) {
            goto Error;
        }
        
        sprintf(inputStreamName, "Input Stream #%ld", streamNum + 1);
        sprintf(outputStreamName, "Output Stream #%ld", streamNum + 1);

        if (!inputStream->initWithAudioEngine(this, kIOAudioStreamDirectionInput, startingChannelID, inputStreamName) ||
            !outputStream->initWithAudioEngine(this, kIOAudioStreamDirectionOutput, startingChannelID, outputStreamName)) {
            goto Error;
        }
        
        formatIterator = OSCollectionIterator::withCollection(formatArray);
        if (!formatIterator) {
            goto Error;
        }
        
        sampleRateIterator = OSCollectionIterator::withCollection(sampleRateArray);
        if (!sampleRateIterator) {
            goto Error;
        }
        
        formatIterator->reset();
        while (formatDict = (OSDictionary *)formatIterator->getNextObject()) {
            IOAudioStreamFormat format;
            
            if (OSDynamicCast(OSDictionary, formatDict) == NULL) {
                goto Error;
            }
            
            if (IOAudioStream::createFormatFromDictionary(formatDict, &format) == NULL) {
                goto Error;
            }
            
            if (!initialFormatSet) {
                initialFormat = format;
            }
            
            sampleRateIterator->reset();
            while (number = (OSNumber *)sampleRateIterator->getNextObject()) {
                if (!OSDynamicCast(OSNumber, number)) {
                    goto Error;
                }
                
                sampleRate.whole = number->unsigned32BitValue();
                
                inputStream->addAvailableFormat(&format, &sampleRate, &sampleRate);
                if (format.fBitDepth == 24) {
                    IOAudioStream::AudioIOFunction functions[2];
                    functions[0] = process24BitSamples;
                    functions[1] = clip24BitSamples;
                    //outputStream->addAvailableFormat(&format, &sampleRate, &sampleRate, functions, 2);
                    outputStream->addAvailableFormat(&format, &sampleRate, &sampleRate, (IOAudioStream::AudioIOFunction)clip24BitSamples);
					if (format.fNumericRepresentation == kIOAudioStreamSampleFormatLinearPCM && format.fIsMixable == TRUE) {
						format.fIsMixable = FALSE;
						outputStream->addAvailableFormat(&format, &sampleRate, &sampleRate, (IOAudioStream::AudioIOFunction)clip24BitSamples);
					}
                } else if (format.fBitDepth == 16) {
                    IOAudioStream::AudioIOFunction functions[2];
                    functions[0] = process16BitSamples;
                    functions[1] = clip16BitSamples;
                    //outputStream->addAvailableFormat(&format, &sampleRate, &sampleRate, functions, 2);
                    outputStream->addAvailableFormat(&format, &sampleRate, &sampleRate, (IOAudioStream::AudioIOFunction)clip16BitSamples);
					if (format.fNumericRepresentation == kIOAudioStreamSampleFormatLinearPCM && format.fIsMixable == TRUE) {
						format.fIsMixable = FALSE;
						outputStream->addAvailableFormat(&format, &sampleRate, &sampleRate, (IOAudioStream::AudioIOFunction)clip24BitSamples);
					}
                } else {
                    outputStream->addAvailableFormat(&format, &sampleRate, &sampleRate);
					if (format.fNumericRepresentation == kIOAudioStreamSampleFormatLinearPCM && format.fIsMixable == TRUE) {
						format.fIsMixable = FALSE;
						outputStream->addAvailableFormat(&format, &sampleRate, &sampleRate, (IOAudioStream::AudioIOFunction)clip24BitSamples);
					}
                }
                
                if (format.fNumChannels > maxNumChannels) {
                    maxNumChannels = format.fNumChannels;
                }
                
                if (format.fBitWidth > maxBitWidth) {
                    maxBitWidth = format.fBitWidth;
                }
                
                if (initialSampleRate->whole == 0) {
                    initialSampleRate->whole = sampleRate.whole;
                }
            }
        }
        
        streamBufferSize = blockSize * numBlocks * maxNumChannels * maxBitWidth / 8;
        
        if (outputBuffer == NULL) {
            if (separateStreamBuffers) {
                outputBufferSize = streamBufferSize * numStreams;
            } else {
                outputBufferSize = streamBufferSize;
            }

            outputBuffer = (void *)IOMalloc(outputBufferSize);
            if (!outputBuffer) {
                IOLog("Error allocating output buffer - %lu bytes.\n", outputBufferSize);
                goto Error;
            }
            
            inputBufferSize = outputBufferSize;
            
            if (separateInputBuffers) {
                inputBuffer = (void *)IOMalloc(inputBufferSize);
                if (!inputBuffer) {
                    IOLog("Error allocating input buffer - %lu bytes.\n", inputBufferSize);
                    goto Error;
                }
            } else {
                inputBuffer = outputBuffer;
            }
        }
        
        inputStream->setFormat(&initialFormat);
        outputStream->setFormat(&initialFormat);
        
        if (separateStreamBuffers) {
            inputStream->setSampleBuffer(&((UInt8 *)inputBuffer)[streamBufferSize * streamNum], streamBufferSize);
            outputStream->setSampleBuffer(&((UInt8 *)outputBuffer)[streamBufferSize * streamNum], streamBufferSize);
        } else {
            inputStream->setSampleBuffer(inputBuffer, streamBufferSize);
            outputStream->setSampleBuffer(outputBuffer, streamBufferSize);
        }
        addAudioStream(inputStream);
        inputStream->release();
        
        addAudioStream(outputStream);
        outputStream->release();
        
        formatIterator->release();
        sampleRateIterator->release();
        
        for (channelID = startingChannelID; channelID < (startingChannelID + maxNumChannels); channelID++) {
            char channelName[20];
            
            sprintf(channelName, "Channel %lu", channelID);
            
            control = IOAudioLevelControl::createVolumeControl(65535,
                                                                0,
                                                                65535,
                                                                (-22 << 16) + (32768),
                                                                0,
                                                                channelID,
                                                                channelName,
                                                                0,
                                                                kIOAudioControlUsageOutput);
            if (!control) {
                goto Error;
            }
            
            control->setValueChangeHandler((IOAudioControl::IntValueChangeHandler)PhantomAudioDevice::volumeChangeHandler, audioDevice);
            addDefaultAudioControl(control);
            control->release();
            
            control = IOAudioToggleControl::createMuteControl(false,
                                                                channelID,
                                                                channelName,
                                                                0,
                                                                kIOAudioControlUsageOutput);
            if (!control) {
                goto Error;
            }
            
            control->setValueChangeHandler((IOAudioControl::IntValueChangeHandler)PhantomAudioDevice::outputMuteChangeHandler, audioDevice);
            addDefaultAudioControl(control);
            control->release();
                                                                
            control = IOAudioLevelControl::createVolumeControl(65535,
                                                                0,
                                                                65535,
                                                                (-22 << 16) + (32768),
                                                                0,
                                                                channelID,
                                                                channelName,
                                                                0,
                                                                kIOAudioControlUsageInput);
            if (!control) {
                goto Error;
            }
            
            control->setValueChangeHandler((IOAudioControl::IntValueChangeHandler)PhantomAudioDevice::gainChangeHandler, audioDevice);
            addDefaultAudioControl(control);
            control->release();
            
            control = IOAudioToggleControl::createMuteControl(false,
                                                                channelID,
                                                                channelName,
                                                                0,
                                                                kIOAudioControlUsageInput);
            if (!control) {
                goto Error;
            }
            
            control->setValueChangeHandler((IOAudioControl::IntValueChangeHandler)PhantomAudioDevice::inputMuteChangeHandler, audioDevice);
            addDefaultAudioControl(control);
            control->release();
            
            control = IOAudioToggleControl::createMuteControl(true,
                                                                channelID,
                                                                channelName,
                                                                0,
                                                                kIOAudioControlUsagePassThru);
            if (!control) {
                goto Error;
            }
            
            control->setValueChangeHandler((IOAudioControl::IntValueChangeHandler)PhantomAudioDevice::passThruChangeHandler, audioDevice);
            addDefaultAudioControl(control);
            control->release();
        }
        
        startingChannelID += maxNumChannels;
        
        continue;

Error:

        IOLog("PhantomAudioEngine[%p]::createAudioStreams() - ERROR\n", this);
    
        if (inputStream) {
            inputStream->release();
        }
        
        if (outputStream) {
            outputStream->release();
        }
        
        if (formatIterator) {
            formatIterator->release();
        }
        
        if (sampleRateIterator) {
            sampleRateIterator->release();
        }
        
        goto Done;
    }
    
    control = IOAudioLevelControl::createVolumeControl(65535,
                                                        0,
                                                        65535,
                                                        (-22 << 16) + (32768),
                                                        0,
                                                        kIOAudioControlChannelIDAll,
                                                        kIOAudioControlChannelNameAll,
                                                        0,
                                                        kIOAudioControlUsageOutput);
    if (!control) {
        goto Done;
    }
    
    control->setValueChangeHandler((IOAudioControl::IntValueChangeHandler)PhantomAudioDevice::volumeChangeHandler, audioDevice);
    addDefaultAudioControl(control);
    control->release();
    
    control = IOAudioToggleControl::createMuteControl(false,
                                                        kIOAudioControlChannelIDAll,
                                                        kIOAudioControlChannelNameAll,
                                                        0,
                                                        kIOAudioControlUsageOutput);
    if (!control) {
        goto Done;
    }
    
    control->setValueChangeHandler((IOAudioControl::IntValueChangeHandler)PhantomAudioDevice::outputMuteChangeHandler, audioDevice);
    addDefaultAudioControl(control);
    control->release();
                                                        
    control = IOAudioLevelControl::createVolumeControl(65535,
                                                        0,
                                                        65535,
                                                        (-22 << 16) + (32768),
                                                        0,
                                                        kIOAudioControlChannelIDAll,
                                                        kIOAudioControlChannelNameAll,
                                                        0,
                                                        kIOAudioControlUsageInput);
    if (!control) {
        goto Done;
    }
    
    control->setValueChangeHandler((IOAudioControl::IntValueChangeHandler)PhantomAudioDevice::gainChangeHandler, audioDevice);
    addDefaultAudioControl(control);
    control->release();
    
    control = IOAudioToggleControl::createMuteControl(false,
                                                        kIOAudioControlChannelIDAll,
                                                        kIOAudioControlChannelNameAll,
                                                        0,
                                                        kIOAudioControlUsageInput);
    if (!control) {
        goto Done;
    }
    
    control->setValueChangeHandler((IOAudioControl::IntValueChangeHandler)PhantomAudioDevice::inputMuteChangeHandler, audioDevice);
    addDefaultAudioControl(control);
    control->release();
    
    control = IOAudioToggleControl::createMuteControl(true,
                                                        kIOAudioControlChannelIDAll,
                                                        kIOAudioControlChannelNameAll,
                                                        0,
                                                        kIOAudioControlUsagePassThru);
    if (!control) {
        goto Done;
    }
    
    control->setValueChangeHandler((IOAudioControl::IntValueChangeHandler)PhantomAudioDevice::passThruChangeHandler, audioDevice);
    addDefaultAudioControl(control);
    control->release();

    result = true;
    
Done:

    if (!result) {
        IOLog("PhantomAudioEngine[%p]::createAudioStreams() - failed!\n", this);
    }

    return result;
}