Retired Document
Important: This sample code may not represent best practices for current development. The project may use deprecated symbols and illustrate technologies and techniques that are no longer recommended.
Relevant replacement documents include:
Sources/Classes/DeviceChannelStrip.mm
/* Copyright: © Copyright 2005 Apple Computer, Inc. All rights reserved. |
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. |
("Apple") in consideration of your agreement to the following terms, and your |
use, installation, modification or redistribution of this Apple software |
constitutes acceptance of these terms. If you do not agree with these terms, |
please do not use, install, modify or redistribute this Apple software. |
In consideration of your agreement to abide by the following terms, and subject |
to these terms, Apple grants you a personal, non-exclusive license, under Apple’s |
copyrights in this original Apple software (the "Apple Software"), to use, |
reproduce, modify and redistribute the Apple Software, with or without |
modifications, in source and/or binary forms; provided that if you redistribute |
the Apple Software in its entirety and without modifications, you must retain |
this notice and the following text and disclaimers in all such redistributions of |
the Apple Software. Neither the name, trademarks, service marks or logos of |
Apple Computer, Inc. may be used to endorse or promote products derived from the |
Apple Software without specific prior written permission from Apple. Except as |
expressly stated in this notice, no other rights or licenses, express or implied, |
are granted by Apple herein, including but not limited to any patent rights that |
may be infringed by your derivative works or by other works in which the Apple |
Software may be incorporated. |
The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO |
WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED |
WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN |
COMBINATION WITH YOUR PRODUCTS. |
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR |
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE |
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION |
OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT |
(INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN |
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
*/ |
#import "DeviceChannelStrip.h" |
#import "SGAudioSettings.h" |
#import "SGAudio.h" |
#import <AudioToolbox/AudioFormat.h> |
#import "WhackedDebugMacros.h" |
#ifndef fieldOffset |
#define fieldOffset(type, field) ((size_t) &((type *) 0)->field) |
#endif |
static const AudioChannelLabel kDeviceChannelStripLabels[] = |
{ |
kAudioChannelLabel_Unknown, |
kAudioChannelLabel_Left, |
kAudioChannelLabel_Right, |
kAudioChannelLabel_Center, |
kAudioChannelLabel_LFEScreen, |
kAudioChannelLabel_LeftSurround, |
kAudioChannelLabel_RightSurround, |
kAudioChannelLabel_LeftCenter, |
kAudioChannelLabel_RightCenter, |
kAudioChannelLabel_CenterSurround, |
kAudioChannelLabel_LeftSurroundDirect, |
kAudioChannelLabel_RightSurroundDirect, |
kAudioChannelLabel_TopCenterSurround, |
kAudioChannelLabel_VerticalHeightLeft, |
kAudioChannelLabel_VerticalHeightCenter, |
kAudioChannelLabel_VerticalHeightRight, |
kAudioChannelLabel_TopBackLeft, |
kAudioChannelLabel_TopBackCenter, |
kAudioChannelLabel_TopBackRight, |
kAudioChannelLabel_RearSurroundLeft, |
kAudioChannelLabel_RearSurroundRight, |
kAudioChannelLabel_LeftWide, |
kAudioChannelLabel_RightWide, |
kAudioChannelLabel_LFE2, |
kAudioChannelLabel_Mono, |
kAudioChannelLabel_CenterSurroundDirect, |
// numbered discrete channel |
// kAudioChannelLabel_Discrete_0 = (1L<<16) | 0, |
}; |
@implementation DeviceChannelStrip |
/*________________________________________________________________________________________ |
*/ |
- (id)initWithSGAudioSettings:(SGAudioSettings*)settings channelNumber:(UInt32)num; |
{ |
self = [super init]; |
mParent = settings; |
mChannelNumber = num; |
mMutex = QTMLCreateMutex(); |
[NSBundle loadNibNamed:@"DeviceChannelStrip" owner:self]; |
[mEnableButton setTitle:[NSString stringWithFormat:@"Channel %0.2ld", mChannelNumber + 1]]; |
[mMeteringView setNumChannels:1]; |
[mMeteringView setHasClipIndicator:YES]; |
return self; |
} |
/*________________________________________________________________________________________ |
*/ |
- (void)startMetering |
{ |
[self stopMetering]; |
// update the level meters 20 times a second |
mUpdateMeterTimer = [[NSTimer alloc] initWithFireDate:[NSDate dateWithTimeIntervalSinceNow:.1] |
interval:1./20 target:self |
selector:@selector(updateLevelMeterTimer:) userInfo:nil repeats:YES]; |
[[NSRunLoop currentRunLoop] addTimer:mUpdateMeterTimer forMode:NSModalPanelRunLoopMode]; |
[[NSRunLoop currentRunLoop] addTimer:mUpdateMeterTimer forMode:NSEventTrackingRunLoopMode]; |
} |
/*________________________________________________________________________________________ |
*/ |
- (void)stopMetering |
{ |
if (mUpdateMeterTimer) |
{ |
[mUpdateMeterTimer invalidate]; |
[mUpdateMeterTimer release]; |
mUpdateMeterTimer = nil; |
} |
[mMeteringView setNeedsDisplay:YES]; |
} |
/*________________________________________________________________________________________ |
*/ |
- (void)dealloc |
{ |
//NSLog(@"[DeviceChannelStrip dealloc] %p, channel %lu", self, mChannelNumber); |
[self stopMetering]; |
[mStripView removeFromSuperviewWithoutNeedingDisplay]; |
[self invalidateChannelMap]; |
QTMLDestroyMutex(mMutex); |
[super dealloc]; |
} |
/*________________________________________________________________________________________ |
*/ |
- (NSView*)deviceChannelStripView |
{ |
return mStripView; |
} |
/*________________________________________________________________________________________ |
*/ |
- (UInt32)channelNumber |
{ |
BOOL useHardwareGain = [mParent usingHardwareGainControls]; |
return ( (useHardwareGain) ? mChannelNumber : mMyIndex ); |
} |
/*________________________________________________________________________________________ |
*/ |
- (NSSlider*)gainSlider |
{ |
return mGainSlider; |
} |
/*________________________________________________________________________________________ |
*/ |
- (NSTextField*)gainText |
{ |
return mGainText; |
} |
/*________________________________________________________________________________________ |
*/ |
- (BOOL)isEnabled |
{ |
return ([mEnableButton state] == NSOnState); |
} |
/*________________________________________________________________________________________ |
*/ |
- (void)setEnabled:(BOOL)enabled |
{ |
BOOL previousEnabled = [mEnableButton state] == NSOnState; |
if (previousEnabled != enabled) |
{ |
[mEnableButton setState:(enabled ? NSOnState : NSOffState)]; |
} |
} |
/*________________________________________________________________________________________ |
*/ |
- (BOOL)isSoloed |
{ |
return ([mSoloButton state] == NSOnState); |
} |
/*________________________________________________________________________________________ |
*/ |
- (BOOL)isMuted |
{ |
return ([mMuteButton state] == NSOnState); |
} |
/*________________________________________________________________________________________ |
*/ |
- (IBAction)toggleChannelEnabled:(id)sender |
{ |
#pragma unused(sender) |
// set the channel map. Channel map is a tricky property, because it |
// changes the device's number of active channels, which affects the |
// device's channel layout (which should always have the same number of |
// channels as the channel map). In order to keep Sequence Grabber from |
// hurting itself by having a mismatch between a user defined input layout and |
// a user defined channel map with differing numbers of channels, we need |
// to stop the channel, then set BOTH properties, then start the channel up again. |
OSStatus err = noErr; |
UInt32 numTotalChans = [[mParent recDeviceChannelStrips] count]; |
UInt32 numChansInMap = 0; |
SInt32 * map = (SInt32*)calloc(1, numTotalChans * sizeof(SInt32)); |
UInt32 layoutSize = fieldOffset(AudioChannelLayout, mChannelDescriptions[numTotalChans]); |
AudioChannelLayout * layout = (AudioChannelLayout*)calloc(1, layoutSize); |
[mParent stopChannelPreview]; |
// build up the map by querying each channel strip for enabled status |
for (UInt32 i = 0; i < numTotalChans; i++) |
{ |
if ( [[[mParent recDeviceChannelStrips] objectAtIndex:i] isEnabled] ) |
{ |
map[numChansInMap] = (SInt32)i; |
layout->mChannelDescriptions[numChansInMap++].mChannelLabel = |
[[[mParent recDeviceChannelStrips] objectAtIndex:i] channelLabel]; |
} |
} |
// set the new channel map |
BAILSETERR( [mParent setSGAudioPropertyWithClass:kQTPropertyClass_SGAudioRecordDevice |
id:kQTSGAudioPropertyID_ChannelMap |
size:numChansInMap * sizeof(SInt32) address:map] ); |
// set the new channel layout |
layout->mChannelLayoutTag = kAudioChannelLayoutTag_UseChannelDescriptions; |
layout->mNumberChannelDescriptions = numChansInMap; |
layoutSize = fieldOffset(AudioChannelLayout, mChannelDescriptions[numChansInMap]); |
BAILSETERR( [mParent setSGAudioPropertyWithClass:kQTPropertyClass_SGAudioRecordDevice |
id:kQTSGAudioPropertyID_ChannelLayout |
size:layoutSize address:layout] ); |
[mParent updateOutputFormat]; |
[mParent startChannelPreview]; |
[[mParent recDeviceChannelStrips] makeObjectsPerformSelector:@selector(updateAllUI)]; |
[mParent updateOutputControls:self]; |
bail: |
free(map); |
} |
/*________________________________________________________________________________________ |
*/ |
- (IBAction)toggleChannelMuted:(id)sender |
{ |
[self setChannelGain:sender]; |
} |
/*________________________________________________________________________________________ |
*/ |
- (IBAction)toggleChannelSoloed:(id)sender |
{ |
[self setChannelGain:sender]; |
} |
/*________________________________________________________________________________________ |
*/ |
- (void)updateChannelLabelPopUp:(id)sender |
{ |
#pragma unused(sender) |
// set up labels. We need to know how many device channels there are to |
// formulate the number of numbered discrete tags to show |
if ([self isEnabled] == NO) |
return; // nothing to do |
[mLabelPopUp removeAllItems]; |
AudioStreamBasicDescription asbd; |
[[mParent sgchan] getPropertyWithClass:kQTPropertyClass_SGAudioRecordDevice |
id:kQTSGAudioPropertyID_StreamFormat |
size:sizeof(asbd) |
address:&asbd sizeUsed:NULL]; |
// also need to get the record device layout, so we can find our current |
// selected label |
UInt32 layoutSize = 0; |
UInt32 flags = 0; |
AudioChannelLabel selectedTag = kAudioChannelLabel_Unknown; |
[[mParent sgchan] getPropertyInfoWithClass:kQTPropertyClass_SGAudioRecordDevice |
id:kQTSGAudioPropertyID_ChannelLayout |
type:NULL size:&layoutSize flags:&flags]; |
if (layoutSize && (flags & kComponentPropertyFlagCanGetNow)) |
{ |
AudioChannelLayout * pLayout = (AudioChannelLayout*)malloc(layoutSize); |
[[mParent sgchan] getPropertyWithClass:kQTPropertyClass_SGAudioRecordDevice |
id:kQTSGAudioPropertyID_ChannelLayout |
size:layoutSize |
address:pLayout sizeUsed:&layoutSize]; |
if (pLayout->mNumberChannelDescriptions == 0) |
{ |
// try to expand it |
UInt32 prop, specifier; |
UInt32 expandedLayoutSize = 0; |
if (pLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap) |
{ |
prop = kAudioFormatProperty_ChannelLayoutForBitmap; |
specifier = pLayout->mChannelBitmap; |
} |
else { |
prop = kAudioFormatProperty_ChannelLayoutForTag; |
specifier = pLayout->mChannelLayoutTag; |
} |
if (noErr == AudioFormatGetPropertyInfo(prop, sizeof(UInt32), &specifier, &expandedLayoutSize)) |
{ |
AudioChannelLayout * pExpandedLayout = (AudioChannelLayout*)calloc(1, expandedLayoutSize); |
AudioFormatGetProperty(prop, sizeof(UInt32), &specifier, &expandedLayoutSize, pExpandedLayout); |
free(pLayout); |
pLayout = pExpandedLayout; |
layoutSize = expandedLayoutSize; |
} |
} |
// get the channel map and find where we are |
UInt32 mapSize; |
SInt32 * map = NULL; |
[[mParent sgchan] getPropertyInfoWithClass:kQTPropertyClass_SGAudioRecordDevice |
id:kQTSGAudioPropertyID_ChannelMap |
type:NULL size:&mapSize flags:NULL]; |
if (mapSize) |
{ |
map = (SInt32*)malloc(mapSize); |
[[mParent sgchan] getPropertyWithClass:kQTPropertyClass_SGAudioRecordDevice |
id:kQTSGAudioPropertyID_ChannelMap |
size:mapSize |
address:map sizeUsed:&mapSize]; |
for (int i = 0; i < mapSize/sizeof(SInt32); i++) |
{ |
if (map[i] == mChannelNumber) |
{ |
selectedTag = pLayout->mChannelDescriptions[i].mChannelLabel; |
} |
} |
} |
if (map) |
free(map); |
free(pLayout); |
} |
UInt32 numBuiltInLabels = sizeof(kDeviceChannelStripLabels)/sizeof(AudioChannelLabel); |
BOOL foundMatch = NO; |
// add the built ins followed by the numbered discretes |
for (int i = 0; i < numBuiltInLabels + asbd.mChannelsPerFrame; i++) |
{ |
NSString * tempString = nil; |
UInt32 size = sizeof(tempString); |
AudioChannelDescription acd = {0}; |
acd.mChannelLabel = (i < numBuiltInLabels) |
? kDeviceChannelStripLabels[i] |
: (kAudioChannelLabel_Discrete_0 | (i - numBuiltInLabels)); |
AudioFormatGetProperty(kAudioFormatProperty_ChannelName, sizeof(acd), &acd, &size, &tempString); |
if (tempString) |
{ |
[mLabelPopUp addItemWithTitle:tempString]; |
[[mLabelPopUp lastItem] setTag:acd.mChannelLabel]; |
if (NO == foundMatch) |
{ |
if (acd.mChannelLabel == selectedTag) |
{ |
[mLabelPopUp selectItemWithTag:selectedTag]; |
foundMatch = YES; |
} |
} |
[tempString release]; |
} |
} |
if (NO == foundMatch) |
[mLabelPopUp selectItemWithTag:kAudioChannelLabel_Unknown]; |
} |
/*________________________________________________________________________________________ |
*/ |
- (IBAction)setChannelLabel:(id)sender |
{ |
#pragma unused(sender) |
UInt32 numChannels = [[mParent recDeviceChannelStrips] count]; |
UInt32 size = fieldOffset(AudioChannelLayout, mChannelDescriptions[numChannels]); |
AudioChannelLayout * pLayout = (AudioChannelLayout*)calloc(1, size); |
UInt32 numEnabledChannels = 0; |
pLayout->mChannelLayoutTag = kAudioChannelLayoutTag_UseChannelDescriptions; |
for (UInt32 i = 0; i < numChannels; i++) |
{ |
if ( [[[mParent recDeviceChannelStrips] objectAtIndex:i] isEnabled] ) |
pLayout->mChannelDescriptions[numEnabledChannels++].mChannelLabel = |
[[[mParent recDeviceChannelStrips] objectAtIndex:i] channelLabel]; |
} |
pLayout->mNumberChannelDescriptions = numEnabledChannels; |
size = fieldOffset(AudioChannelLayout, mChannelDescriptions[numEnabledChannels]); |
// set it! |
OSStatus err = noErr; |
[mParent stopChannelPreview]; |
BAILSETERR([mParent setSGAudioPropertyWithClass:kQTPropertyClass_SGAudioRecordDevice |
id:kQTSGAudioPropertyID_ChannelLayout |
size:size |
address:pLayout]); |
[mParent updateOutputFormat]; |
[mParent startChannelPreview]; |
[mParent updateOutputControls:self]; |
bail: |
free(pLayout); |
} |
/*________________________________________________________________________________________ |
*/ |
- (AudioChannelLabel)channelLabel |
{ |
// the label selected in the ui |
return [mLabelPopUp selectedTag]; |
} |
/*________________________________________________________________________________________ |
*/ |
- (BOOL)anotherChannelIsSoloed |
{ |
for (int i = 0; i < [[mParent recDeviceChannelStrips] count]; i++) |
{ |
DeviceChannelStrip * cur = [[mParent recDeviceChannelStrips] objectAtIndex:i]; |
if ((self != cur) && [cur isEnabled] && [cur isSoloed]) |
{ |
return YES; |
} |
} |
return NO; |
} |
/*________________________________________________________________________________________ |
*/ |
- (IBAction)setChannelGain:(id)sender |
{ |
#pragma unused(sender) |
if ([self isEnabled] == NO) |
return; |
{ |
BOOL useHardwareGain = [mParent usingHardwareGainControls]; |
ComponentPropertyClass theClass = (useHardwareGain) |
? kQTPropertyClass_SGAudioRecordDevice |
: kQTPropertyClass_SGAudio; |
// be careful. If we're using hardware gain, then the number of |
// levels is equal to the number of channels on the device. |
// If !useHardwareGain, it's equal to the number of channels in the |
// the channel map (the number of channels being collected from the |
// device by the sgaudiochannel |
UInt32 size, flags; |
if (noErr == [[mParent sgchan] getPropertyInfoWithClass:theClass |
id:kQTSGAudioPropertyID_PerChannelGain |
type:NULL |
size:&size flags:&flags] |
&& size && (flags & kComponentPropertyFlagCanSetNow)) |
{ |
Float32 * chanGains = (Float32*)malloc(size * sizeof(Float32)); |
UInt32 numChannelGains = size/sizeof(Float32); |
BOOL someoneIsSoloed = NO; |
// set all chanGains to -1, indicating that we wish to ignore them. |
// we will put non -1 values into the indeces we wish to set. |
for (int i = 0; i < size/sizeof(Float32); i++) |
{ |
chanGains[i] = -1.; |
} |
// loop through the channel strip instances once, see if any of them |
// are solo'ed. |
for (int i = 0; i < [[mParent recDeviceChannelStrips] count]; i++) |
{ |
DeviceChannelStrip * curstrip = [[mParent recDeviceChannelStrips] objectAtIndex:i]; |
if ( [curstrip isEnabled] && [curstrip isSoloed] ) |
{ |
someoneIsSoloed = YES; |
break; |
} |
} |
// loop through the channel strip array again, this time getting the appropriate |
// volume levels to set on the SGAudioChannel. |
for (int i = 0; i < [[mParent recDeviceChannelStrips] count]; i++) |
{ |
DeviceChannelStrip * curstrip = [[mParent recDeviceChannelStrips] objectAtIndex:i]; |
if ( [curstrip isEnabled] ) |
{ |
BOOL iAmMuted = [curstrip isMuted]; |
BOOL iAmSoloed = [curstrip isSoloed]; |
UInt32 myIndex = [curstrip channelNumber]; |
NSSlider * mySlider = [curstrip gainSlider]; |
NSTextField * myText = [curstrip gainText]; |
if (iAmMuted || (!iAmSoloed && someoneIsSoloed)) |
{ |
// mute this bad boy |
if (myIndex < numChannelGains) |
{ |
chanGains[myIndex] = 0.; |
} |
} |
else { |
// use the gain specified by the slider |
if (myIndex < numChannelGains) |
{ |
chanGains[myIndex] = [mySlider floatValue]; |
} |
} |
if ([mySlider isEnabled]) |
{ |
Float32 level = [mySlider floatValue]; |
BOOL showDB = [mParent showingGainAsDecibels]; |
if ( showDB ) |
[[mParent sgchan] getPropertyWithClass: theClass |
id: kQTSGAudioPropertyID_GainScalarToDecibels |
size: sizeof(level) |
address: &level sizeUsed:NULL]; |
[myText setStringValue:[NSString stringWithFormat:@"%.2f%s", level, |
(showDB ? " dB" : "")]]; |
} |
} |
} |
[mParent setSGAudioPropertyWithClass:theClass |
id:kQTSGAudioPropertyID_PerChannelGain |
size:size |
address:chanGains]; |
if (chanGains) |
free(chanGains); |
} |
} |
} |
/*________________________________________________________________________________________ |
*/ |
- (IBAction)updateGain:(id)sender |
{ |
#pragma unused(sender) |
BOOL useHardwareGain = [mParent usingHardwareGainControls]; |
ComponentPropertyClass theClass = (useHardwareGain) |
? kQTPropertyClass_SGAudioRecordDevice |
: kQTPropertyClass_SGAudio; |
UInt32 size = 0, flags = 0; |
Float32 * array = NULL; |
if (noErr == [[mParent sgchan] getPropertyInfoWithClass:theClass |
id:kQTSGAudioPropertyID_PerChannelGain |
type:NULL |
size:&size flags:&flags] |
&& size && (flags & kComponentPropertyFlagCanGetNow)) |
{ |
// be careful. If we're using hardware gain, then the number of |
// levels is equal to the number of channels on the device. |
// If !useHardwareGain, it's equal to the number of channels in the |
// the channel map (the number of channels being collected from the |
// device by the sgaudiochannel |
UInt32 myIndex = (useHardwareGain) ? mChannelNumber : mMyIndex; |
array = (Float32*)malloc(size); |
if ([mEnableButton state] == NSOnState) |
{ |
if (![self isMuted] && ![self anotherChannelIsSoloed]) |
{ |
[[mParent sgchan] getPropertyWithClass:theClass |
id:kQTSGAudioPropertyID_PerChannelGain |
size:size |
address:array sizeUsed:&size]; |
if (myIndex < size/sizeof(Float32)) |
{ |
Float32 level = array[myIndex]; |
BOOL showDB = [mParent showingGainAsDecibels]; |
[mGainSlider setFloatValue:level]; |
if ( showDB ) |
[[mParent sgchan] getPropertyWithClass: theClass |
id: kQTSGAudioPropertyID_GainScalarToDecibels |
size: sizeof(level) |
address: &level sizeUsed:NULL]; |
[mGainText setStringValue:[NSString stringWithFormat:@"%.2f%s", level, |
(showDB ? " dB" : "")]]; |
} |
} |
} |
else { |
// we are disabled |
[mGainSlider setEnabled:NO]; |
[mGainText setStringValue:@"N/A"]; |
} |
} |
else { |
// device doesn't support this operation |
[mGainSlider setEnabled:NO]; |
[mGainText setStringValue:@"N/A"]; |
} |
if (array) |
free(array); |
} |
/*________________________________________________________________________________________ |
*/ |
- (void)updateLevelMeterTimer:(NSTimer*)t |
{ |
#pragma unused(t) |
[self updateChannelLevel]; |
} |
/*________________________________________________________________________________________ |
*/ |
- (void)invalidateChannelMap |
{ |
QTMLGrabMutex(mMutex); |
if (mLevelsArray) |
free(mLevelsArray); |
mLevelsArray = NULL; |
QTMLReturnMutex(mMutex); |
} |
/*________________________________________________________________________________________ |
*/ |
- (void)updateChannelLevel |
{ |
Float32 amps[2] = { -FLT_MAX, -FLT_MAX }; |
QTMLGrabMutex(mMutex); |
if ([mEnableButton state] == NSOnState) |
{ |
SGAudio * myAudi = [mParent sgchan]; |
if (mLevelsArray == NULL) |
{ |
UInt32 size; |
[myAudi getPropertyInfoWithClass:kQTPropertyClass_SGAudioRecordDevice |
id:kQTSGAudioPropertyID_ChannelMap |
type:NULL size:&size flags:NULL]; |
if (size > 0) |
{ |
SInt32 * map = (SInt32 *)malloc(size); |
[myAudi getPropertyWithClass:kQTPropertyClass_SGAudioRecordDevice |
id:kQTSGAudioPropertyID_ChannelMap |
size:size |
address:map sizeUsed:&size]; |
for (int i = 0; i < size/sizeof(SInt32); i++) |
{ |
if (mChannelNumber == map[i]) |
{ |
mMyIndex = i; |
mLevelsArraySize = size; // SInt32 and Float32 are the same size |
mLevelsArray = (Float32*)malloc(mLevelsArraySize); |
break; |
} |
} |
free(map); |
} |
} |
if (mLevelsArray) // paranoia |
{ |
// get the avg power level |
if (noErr == [myAudi getPropertyWithClass:kQTPropertyClass_SGAudioRecordDevice |
id:kQTSGAudioPropertyID_AveragePowerLevels |
size:mLevelsArraySize |
address:mLevelsArray sizeUsed:NULL]) |
{ |
amps[0] = mLevelsArray[mMyIndex]; |
} |
// get the peak-hold level |
if (noErr == [myAudi getPropertyWithClass:kQTPropertyClass_SGAudioRecordDevice |
id:kQTSGAudioPropertyID_PeakHoldLevels |
size:mLevelsArraySize |
address:mLevelsArray sizeUsed:NULL]) |
{ |
amps[1] = mLevelsArray[mMyIndex]; |
} |
} |
} |
QTMLReturnMutex(mMutex); |
[mMeteringView updateMeters:amps]; |
} |
/*________________________________________________________________________________________ |
*/ |
- (void)updateAllUI |
{ |
BOOL enabledVal = ([mEnableButton state] == NSOnState); |
[self stopMetering]; |
[self invalidateChannelMap]; |
[self updateChannelLevel]; |
[mMeteringView setDirty:YES]; |
[self startMetering]; |
[mMuteButton setEnabled:enabledVal]; |
[mSoloButton setEnabled:enabledVal]; |
[self updateChannelLabelPopUp:self]; |
[mLabelPopUp setEnabled:enabledVal]; |
[mGainSlider setEnabled:enabledVal]; |
[self updateGain:mGainSlider]; |
} |
/*________________________________________________________________________________________ |
*/ |
@end |
Copyright © 2011 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2011-09-06