jam-cloud/web/app/assets/javascripts/react-components/helpers/MixerHelper.js.coffee

1005 lines
34 KiB
CoffeeScript

context = window
logger = context.JK.logger
ChannelGroupIds = context.JK.ChannelGroupIds
CategoryGroupIds = context.JK.CategoryGroupIds
MIX_MODES = context.JK.MIX_MODES;
@MixerHelper = class MixerHelper
constructor: (@session, @masterMixers, @personalMixers, @metro, @noAudioUsers, @mixMode) ->
@mixMode = MIX_MODES.PERSONAL # TODO - remove mixMode from MixerHelper? Or at least stop using it in most functions
@app = @session.app
@mixersByResourceId = {}
@mixersByTrackId = {}
@allMixers = {}
@currentMixerRangeMin = null
@currentMixerRangeMax = null
@mediaSummary = {}
@mediaTrackGroups = [ChannelGroupIds.MediaTrackGroup, ChannelGroupIds.JamTrackGroup,
ChannelGroupIds.MetronomeGroup]
@muteBothMasterAndPersonalGroups = [ChannelGroupIds.AudioInputMusicGroup, ChannelGroupIds.MediaTrackGroup,
ChannelGroupIds.JamTrackGroup, ChannelGroupIds.MetronomeGroup]
@vuStats = {}
@shouldCollectVuStats = false
@simulatedMusicCategoryMixers = {}
@simulatedChatCategoryMixers = {}
@organize()
organize: () ->
for masterMixer in @masterMixers
@allMixers['M' + masterMixer.id] = masterMixer; # populate allMixers by mixer.id
# populate mixer pair
mixerPair = {}
@mixersByResourceId[masterMixer.rid] = mixerPair
@mixersByTrackId[masterMixer.id] = mixerPair
mixerPair.master = masterMixer;
for personalMixer in @personalMixers
@allMixers['P' + personalMixer.id] = personalMixer
# populate other side of mixer pair
mixerPair = @mixersByResourceId[personalMixer.rid]
unless mixerPair
if personalMixer.group_id != ChannelGroupIds.MonitorGroup
logger.warn("there is no master version of ", personalMixer)
mixerPair = {}
@mixersByResourceId[personalMixer.rid] = mixerPair
@mixersByTrackId[personalMixer.id] = mixerPair;
mixerPair.personal = personalMixer;
@groupTypes()
@chatMixer = @resolveChatMixer()
groupTypes: () ->
localMediaMixers = @mixersForGroupIds(@mediaTrackGroups, MIX_MODES.MASTER)
peerLocalMediaMixers = @mixersForGroupId(ChannelGroupIds.PeerMediaTrackGroup, MIX_MODES.MASTER)
#logger.debug("localMediaMixers", localMediaMixers)
#logger.debug("peerLocalMediaMixers", peerLocalMediaMixers)
# get the server data regarding various media tracks
recordedBackingTracks = @session.recordedBackingTracks()
backingTracks = @session.backingTracks()
recordedJamTracks = @session.recordedJamTracks()
jamTracks = @session.jamTracks()
###
with mixer info, we use these to decide what kind of tracks are open in the backend
each mixer has a media_type field, which describes the type of media track it is.
* JamTrack
* BackingTrack
* RecordingTrack
* MetronomeTrack
* "" - adhoc track (not supported visually)
it is supposed to be the case that there are only one type of track open at a time, however, that's a business policy/logic
constraint; and may be buggy. **So, we should render whatever we have, so that it's obvious what's really going on.**
so, let's group up all mixers by type, and then ask them to be rendered
###
@recordingTrackMixers = []
@backingTrackMixers = []
@jamTrackMixers = []
@metronomeTrackMixers = []
@adhocTrackMixers = []
groupByType = (mixers, isLocalMixer) =>
for mixer in mixers
mediaType = mixer.media_type
groupId = mixer.group_id
if mediaType == 'MetronomeTrack' || groupId == ChannelGroupIds.MetronomeGroup
# Metronomes come across with a blank media type, so check group_id:
@metronomeTrackMixers.push(mixer)
else if mediaType == null || mediaType == "" || mediaType == 'RecordingTrack'
# additional check; if we can match an id in backing tracks or recorded backing track,
# we need to remove it from the recorded track set, but move it to the backing track set
isJamTrack = false;
if jamTracks
# check if the ID matches that of an open jam track
for jamTrack in jamTracks
if mixer.id == jamTrack.id
isJamTrack = true;
break
if !isJamTrack && recordedJamTracks
# then check if the ID matches that of a open, recorded jam track
for recordedJamTrack in recordedJamTracks
if mixer.id == recordedJamTrack.id
isJamTrack = true
break
if isJamTrack
@jamTrackMixers.push(mixer)
else
isBackingTrack = false
if recordedBackingTracks
for recordedBackingTrack in recordedBackingTracks
if mixer.id == 'L' + recordedBackingTrack.client_track_id
isBackingTrack = true
break
if backingTracks
for backingTrack in backingTracks
if mixer.id == 'L' + backingTrack.client_track_id
isBackingTrack = true
break
if isBackingTrack
@backingTrackMixers.push(mixer)
else
# couldn't resolve this as a JamTrack or Backing track, must be a normal recorded file
@recordingTrackMixers.push(mixer)
else if mediaType == 'PeerMediaTrack' || mediaType == 'BackingTrack'
@backingTrackMixers.push(mixer)
else if mediaType == 'JamTrack'
@jamTrackMixers.push(mixer);
else if mediaType == null || mediaType == "" || mediaType == 'RecordingTrack'
# mediaType == null is for backwards compat with older clients. Can be removed soon
@recordingTrackMixers.push(mixer)
else
logger.warn("Unknown track type: " + mediaType)
@adhocTrackMixers.push(mixer)
groupByType(localMediaMixers, true);
groupByType(peerLocalMediaMixers, false);
###
if recordingTrackMixers.length > 0
renderRecordingTracks(recordingTrackMixers)
if backingTrackMixers.length > 0
renderBackingTracks(backingTrackMixers)
if jamTrackMixers.length > 0
renderJamTracks(jamTrackMixers);
if metronomeTrackMixers.length > 0 && @session.jamTracks() == null && @session.recordedJamTracks() == null
renderMetronomeTracks(metronomeTrackMixers);
checkMetronomeTransition();
###
@backingTracks = @resolveBackingTracks()
@jamTracks = @resolveJamTracks()
@recordedTracks = @resolveRecordedTracks()
@metronome = @resolveMetronome()
if @adhocTrackMixers.length > 0
logger.warn("some tracks are open that we don't know how to show")
@mediaSummary =
recordingOpen: @recordedTracks.length > 0
jamTrackOpen: @jamTracks.length > 0
backingTrackOpen: @backingTracks.length > 0
metronomeOpen: @session.isMetronomeOpen()
# figure out if any media is open
mediaOpenSummary = false
for mediaType, mediaOpen of @mediaSummary
mediaOpenSummary = true if mediaOpen
@mediaSummary.mediaOpen = mediaOpenSummary
# figure out if we opened any media
isOpener = false
if @mediaSummary.recordingOpen
isOpener = @recordedTracks[0].isOpener
else if @mediaSummary.jamTrackOpen
isOpener = @jamTracks[0].isOpener
else if @mediaSummary.backingTrackOpen
isOpener = @backingTracks[0].isOpener
@mediaSummary.isOpener = isOpener
@prepareSimulatedMixers()
# this method is pretty complicated because it forks on a key bit of state:
# sessionModel.isPlayingRecording()
# a backing track opened as part of a recording has a different behavior and presence on the server (recording.recorded_backing_tracks)
# than a backing track opend ad-hoc (connection.backing_tracks)
resolveBackingTracks: () ->
backingTracks = []
return backingTracks unless @backingTrackMixers.length > 0
# find both client and server representation of the backing track
serverBackingTracks = []
backingTrackMixers = @backingTrackMixers
if @session.isPlayingRecording()
backingTrackMixers = context._.filter(backingTrackMixers, (mixer) -> return mixer.managed || !mixer.managed?)
serverBackingTracks = @session.recordedBackingTracks()
else
serverBackingTracks = @session.backingTracks();
backingTrackMixers = context._.filter(backingTrackMixers, (mixer) -> return !mixer.managed)
if backingTrackMixers.length > 1
logger.error("multiple, managed backing track mixers encountered", backingTrackMixers)
@app.notify({
title: "Multiple Backing Tracks Encountered",
text: "Only one backing track can be open a time.",
icon_url: "/assets/content/icon_alert_big.png"
});
return backingTracks;
# we don't render backing tracks unless we have server data to accompany
if !serverBackingTracks? || serverBackingTracks.length == 0
return backingTracks
noCorrespondingTracks = false
for mixer in backingTrackMixers
# find the track or tracks that correspond to the mixer
correspondingTracks = []
noCorrespondingTracks = false
if @session.isPlayingRecording()
for backingTrack in serverBackingTracks
# occurs if this client is the one that opened the track, # occurs if this client is a remote participant
if mixer.persisted_track_id == backingTrack.client_track_id || mixer.id == 'L' + backingTrack.client_track_id
correspondingTracks.push(backingTrack)
else
# if this is just an open backing track, then we can assume that the 1st backingTrackMixer is ours
correspondingTracks.push(serverBackingTracks[0])
if correspondingTracks.length == 0
noCorrespondingTracks = true
logger.debug("renderBackingTracks: could not map backing tracks")
@app.notify({
title: "Unable to Open Backing Track",
text: "Could not correlate server and client tracks",
icon_url: "/assets/content/icon_alert_big.png"
});
break
# now we have backing track and mixer in hand; we can render
serverBackingTrack = correspondingTracks[0]
oppositeMixer = @getMixerByResourceId(mixer.rid, MIX_MODES.PERSONAL);
isOpener = mixer.group_id == ChannelGroupIds.MediaTrackGroup
data =
isOpener: isOpener
shortFilename: context.JK.getNameOfFile(serverBackingTrack.filename)
instrumentIcon: context.JK.getInstrumentIcon45(serverBackingTrack.instrument_id)
photoUrl: "/assets/content/icon_recording.png"
showLoop: isOpener && !@session.isPlayingRecording()
track: serverBackingTrack
mixers: @mediaMixers(mixer, isOpener)
backingTracks.push(data)
backingTracks
resolveJamTracks: () ->
_jamTracks = []
return _jamTracks unless @jamTrackMixers.length > 0
jamTrackMixers = @jamTrackMixers.slice();
jamTracks = []
jamTrackName = null;
if @session.isPlayingRecording()
# only return managed mixers for recorded backing tracks
jamTracks = @session.recordedJamTracks()
jamTrackName = @session.recordedJamTrackName()
else
# only return un-managed (ad-hoc) mixers for normal backing tracks
jamTracks = @session.jamTracks()
jamTrackName = @session.jamTrackName()
# pluck the 1st mixer, and assume that all other mixers in this group are of the same type (between JamTrack vs Peer)
# if it's a locally opened track (JamTrackGroup), then we can say this person is the opener
isOpener = jamTrackMixers[0].group_id == ChannelGroupIds.JamTrackGroup;
if jamTracks
noCorrespondingTracks = false
for jamTrack in jamTracks
mixer = null
preMasteredClass = ""
# find the track or tracks that correspond to the mixer
correspondingTracks = []
for matchMixer in @jamTrackMixers
if matchMixer.id == jamTrack.id
correspondingTracks.push(jamTrack)
mixer = matchMixer
if correspondingTracks.length == 0
noCorrespondingTracks = true
logger.error("could not correlate jam tracks", jamTrackMixers, jamTracks)
@app.notify({
title: "Unable to Open JamTrack",
text: "Could not correlate server and client tracks",
icon_url: "/assets/content/icon_alert_big.png"})
return _jamTracks
#jamTracks = $.grep(jamTracks, (value) =>
# $.inArray(value, correspondingTracks) < 0
#)
# prune found mixers
jamTrackMixers.splice(mixer);
oneOfTheTracks = correspondingTracks[0];
instrumentIcon = context.JK.getInstrumentIcon24(oneOfTheTracks.instrument.id);
part = oneOfTheTracks.part
instrumentName = oneOfTheTracks.instrument.description
if part?
trackName = "#{instrumentName}: #{part}"
else
trackName = instrumentName
data =
name: jamTrackName
trackName: trackName
part: part
isOpener: isOpener
instrumentIcon: instrumentIcon
track: oneOfTheTracks
mixers: @mediaMixers(mixer, isOpener)
_jamTracks.push(data)
_jamTracks
resolveRecordedTracks: () ->
recordedTracks = []
return recordedTracks unless @recordingTrackMixers.length > 0
serverRecordedTracks = @session.recordedTracks()
isOpener = @recordingTrackMixers[0].group_id == ChannelGroupIds.MediaTrackGroup
# using the server's info in conjuction with the client's, draw the recording tracks
if serverRecordedTracks
recordingName = @session.recordingName()
noCorrespondingTracks = false
for mixer in @recordingTrackMixers
preMasteredClass = ""
correspondingTracks = []
for recordedTrack in serverRecordedTracks
if mixer.id.indexOf("L") == 0
if mixer.id.substring(1) == recordedTrack.client_track_id
correspondingTracks.push(recordedTrack)
else if mixer.id.indexOf("C") == 0
if mixer.id.substring(1) == recordedTrack.client_id
correspondingTracks.push(recordedTrack)
preMasteredClass = "pre-mastered-track"
else
# this should not be possible
alert("Invalid state: the recorded track had neither persisted_track_id or persisted_client_id")
if correspondingTracks.length == 0
noCorrespondingTracks = true
logger.debug("unable to correlate all recorded tracks", recordingMixers, serverRecordedTracks)
@app.notify({
title: "Unable to Open Recording",
text: "Could not correlate server and client tracks",
icon_url: "/assets/content/icon_alert_big.png"});
return recordedTracks
serverRecordedTracks = $.grep(serverRecordedTracks,
(value) =>
$.inArray(value, correspondingTracks) < 0
)
oneOfTheTracks = correspondingTracks[0]
instrumentIcon = context.JK.getInstrumentIcon24(oneOfTheTracks.instrument_id)
userName = oneOfTheTracks.user.name
userName = oneOfTheTracks.user.first_name + ' ' + oneOfTheTracks.user.last_name unless userName?
data =
recordingName: recordingName
isOpener: isOpener
userName: userName
instrumentIcon: instrumentIcon
track: oneOfTheTracks
mixers: @mediaMixers(mixer, isOpener)
recordedTracks.push(data)
recordedTracks
resolveMetronome: () ->
return null if @metronomeTrackMixers.length == 0
mixer = @metronomeTrackMixers[0]
instrumentIcon = "/assets/content/icon_metronome.png"
metronome =
instrumentIcon: instrumentIcon
mixers: @mediaMixers(mixer, true)
metronome
resolveChatMixer: () ->
masterChatMixers = @mixersForGroupId(ChannelGroupIds.AudioInputChatGroup, MIX_MODES.MASTER);
return null if masterChatMixers.length == 0
personalChatMixers = @mixersForGroupId(ChannelGroupIds.AudioInputChatGroup, MIX_MODES.PERSONAL);
if personalChatMixers.length == 0
logger.warn("unable to find personal mixer for voice chat");
return null
masterChatMixer = masterChatMixers[0];
personalChatMixer = personalChatMixers[0];
{
master: {
mixer: masterChatMixer
muteMixer: masterChatMixer
vuMixer: masterChatMixer
oppositeMixer: personalChatMixer
}
personal: {
mixer: personalChatMixer
muteMixer: personalChatMixer
vuMixer: personalChatMixer
oppositeMixer: masterChatMixer
}
}
# supply the master mixer of a media track, and this function will harvest out the rest
mediaMixers:(masterMixer, isOpener) ->
personalMixer = if isOpener then @getMixerByResourceId(masterMixer.rid, MIX_MODES.PERSONAL) else null
personalVuMixer = if isOpener then personalMixer else masterMixer
{
isOpener: isOpener
master: {
mixer: masterMixer
muteMixer: masterMixer
vuMixer: masterMixer
}
personal: {
mixer: personalMixer
muteMixer: personalMixer
vuMixer: personalVuMixer
}
}
mixersForGroupIds: (groupIds, mixMode) ->
foundMixers = []
mixers = if mixMode == MIX_MODES.MASTER then @masterMixers else @personalMixers;
for mixer in mixers
for groupId in groupIds
if mixer.group_id == groupId
foundMixers.push(mixer)
foundMixers
mixersForGroupId: (groupId, mixMode) ->
foundMixers = [];
mixers = if mixMode == MIX_MODES.MASTER then @masterMixers else @personalMixers;
for mixer in mixers
if mixer.group_id == groupId
foundMixers.push(mixer)
foundMixers
mixerForGroupId: (groupId, mixMode) ->
mixers = @mixersForGroupId(groupId, mixMode)
if mixers? && mixers.length > 0
mixers[0]
else
null
getMixer: (mixerId, mode) ->
mode = @mixMode unless mode?
@allMixers[(if mode then 'M' else 'P') + mixerId]
getMixerByTrackId: (trackId, mode) ->
mixerPair = @mixersByTrackId[trackId]
return null unless mixerPair
if mode == undefined
return mixerPair
else
if mode == MIX_MODES.MASTER
return mixerPair.master
else
return mixerPair.personal
groupedMixersForClientId: (clientId, groupIds, usedMixers, mixMode) ->
foundMixers = {};
mixers = if mixMode == MIX_MODES.MASTER then @masterMixers else @personalMixers;
for mixer in mixers
unless mixer?
#logger.debug("empty mixer: ", mixers)
continue
if mixer.client_id == clientId
for groupId in groupIds
if mixer.group_id == groupId
if (mixer.groupId != ChannelGroupIds.UserMusicInputGroup) && !(mixer.id of usedMixers)
mixers = foundMixers[mixer.group_id]
if !mixers
mixers = []
foundMixers[mixer.group_id] = mixers
mixers.push(mixer)
foundMixers
getMixerByResourceId:(resourceId, mode) ->
mixerPair = @mixersByResourceId[resourceId];
return null if(!mixerPair)
if !mode?
return mixerPair;
else
if mode == MIX_MODES.MASTER
return mixerPair.master
else
return mixerPair.personal
findMixerForTrack: (client_id, track, myTrack, mode = MIX_MODES.PERSONAL) ->
mixer = null # what is the best mixer for this track/client ID?
oppositeMixer = null # what is the corresponding mixer in the opposite mode?
vuMixer = null
muteMixer = null
if myTrack
# when it's your track, look it up by the backend resource ID
mixer = @getMixerByTrackId(track.client_track_id, mode)
vuMixer = mixer
muteMixer = mixer
# sanity checks
if mixer && mixer.group_id != ChannelGroupIds.AudioInputMusicGroup
logger.error("found local mixer that was not of groupID: AudioInputMusicGroup", mixer)
if mixer
# find the matching AudioInputMusicGroup for the opposite mode
oppositeMixer = @getMixerByTrackId(track.client_track_id, !mode)
if mode == MIX_MODES.PERSONAL
muteMixer = oppositeMixer; # make the master mixer the mute mixer
# sanity checks
if !oppositeMixer
logger.error("unable to find opposite mixer for local mixer", mixer)
else if oppositeMixer.group_id != ChannelGroupIds.AudioInputMusicGroup
logger.error("found local mixer in opposite mode that was not of groupID: AudioInputMusicGroup", mixer, oppositeMixer)
else
logger.debug("local track is not present: ", track, @allMixers)
else
switch mode
when MIX_MODES.MASTER
# when it's a remote track and in master mode, we should find the PeerAudioInputMusicGroup
mixer = @getMixerByTrackId(track.client_track_id, MIX_MODES.MASTER)
# sanity check
if mixer && mixer.group_id != ChannelGroupIds.PeerAudioInputMusicGroup
logger.warn("master: found remote mixer that was not of groupID: PeerAudioInputMusicGroup", client_id, track.client_track_id, mixer)
vuMixer = mixer
muteMixer = mixer
if mixer
# we should be able to find a UserMusicInputGroup for this clientId in personal mode
oppositeMixers = @groupedMixersForClientId(client_id, [ ChannelGroupIds.UserMusicInputGroup], {}, MIX_MODES.PERSONAL)
if oppositeMixers[ChannelGroupIds.UserMusicInputGroup]
oppositeMixer = oppositeMixers[ChannelGroupIds.UserMusicInputGroup][0]
if !oppositeMixer
logger.warn("unable to find UserMusicInputGroup corresponding to PeerAudioInputMusicGroup mixer", mixer )
when MIX_MODES.PERSONAL
mixers = @groupedMixersForClientId(client_id, [ ChannelGroupIds.UserMusicInputGroup], {}, MIX_MODES.PERSONAL)
if mixers[ChannelGroupIds.UserMusicInputGroup]
mixer = mixers[ChannelGroupIds.UserMusicInputGroup][0]
vuMixer = mixer
muteMixer = mixer
if mixer
# now grab the PeerAudioInputMusicGroup in master mode to satisfy the 'opposite' mixer
oppositeMixer = @getMixerByTrackId(track.client_track_id, MIX_MODES.MASTER)
if !oppositeMixer
logger.debug("personal: unable to find a PeerAudioInputMusicGroup master mixer matching a UserMusicInput", client_id, track.client_track_id)
else if oppositeMixer.group_id != ChannelGroupIds.PeerAudioInputMusicGroup
logger.error("personaol: found remote mixer that was not of groupID: PeerAudioInputMusicGroup", client_id, track.client_track_id, mixer)
#vuMixer = oppositeMixer; # for personal mode, use the PeerAudioInputMusicGroup's VUs
{
mixer: mixer,
oppositeMixer: oppositeMixer,
vuMixer: vuMixer,
muteMixer: muteMixer
}
mute: (mixerId, mode, muting) ->
mode = @mixMode unless mode?
@fillTrackVolumeObject(mixerId, mode)
context.trackVolumeObject.mute = muting
context.jamClient.SessionSetControlState(mixerId, mode)
# keep state of mixer in sync with backend
mixer = @getMixer(mixerId, mode)
mixer.mute = muting
getOriginalVolume: (mixers, gainType) ->
originalVolume = null
if gainType == 'music'
# find a non media volL to compare against for later 'relative move'
for mixer in mixers
if mixer.name != CategoryGroupIds.UserMedia && mixer.name != CategoryGroupIds.MediaTrack
originalVolume = mixer.volume_left
break
else
originalVolume = mixers[0].volume_left
originalVolume
faderChanged: (data, mixers, gainType) ->
mixers = [mixers] unless $.isArray(mixers)
originalVolume = @getOriginalVolume(mixers, gainType)
for mixer in mixers
broadcast = !(data.dragging) # If fader is still dragging, don't broadcast
mixer = @fillTrackVolumeObject(mixer.id, mixer.mode, broadcast)
relative = gainType == 'music' && (mixer.name == CategoryGroupIds.UserMedia || mixer.name == CategoryGroupIds.MediaTrack)
@setMixerVolume(mixer, data.percentage, relative, originalVolume)
# keep state of mixer in sync with backend
mixer = @getMixer(mixer.id, mixer.mode)
mixer.volume_left = context.trackVolumeObject.volL
#if groupId == ChannelGroupIds.UserMusicInputGroup
# # there may be other mixers with this same ID in the case of a Peer Music Stream, so update them as well
# context.JK.FaderHelpers.setFaderValue(mixerId, data.percentage)
initGain: (mixer) ->
if $.isArray(mixer)
mixer = mixer[0]
gainPercent = context.JK.FaderHelpers.convertAudioTaperToPercent(mixer.volume_left)
context.JK.FaderHelpers.setFaderValue(mixer.id, gainPercent)
context.JK.FaderHelpers.showFader(mixer.id)
panChanged: (data, mixers, groupId) ->
mixers = [mixers] unless $.isArray(mixers)
# media tracks are the only controls that sometimes set two mixers right now
for mixer in mixers
broadcast = !(data.dragging) # If fader is still dragging, don't broadcast
mixer = @fillTrackVolumeObject(mixer.id, mixer.mode, broadcast)
@setMixerPan(mixer, data.percentage)
# keep state of mixer in sync with backend
mixer = @getMixer(mixer.id, mixer.mode)
mixer.pan = context.trackVolumeObject.pan
initPan: (mixer) ->
panPercent= context.JK.PanHelpers.convertPanToPercent(mixer.pan)
context.JK.FaderHelpers.setFaderValue(mixer.id, panPercent, Math.abs(mixer.pan))
context.JK.FaderHelpers.showFader(mixer.id)
setMixerPan: (mixer, panPercent) ->
context.trackVolumeObject.pan = context.JK.PanHelpers.convertPercentToPan(panPercent);
context.jamClient.SessionSetControlState(mixer.id, mixer.mode);
loopChanged: (mixer, shouldLoop) ->
@fillTrackVolumeObject(mixer.id, mixer.mode)
context.trackVolumeObject.loop = shouldLoop
context.jamClient.SessionSetControlState(mixer.id, mixer.mode)
# keep state of mixer in sync with backend
mixer = @getMixer(mixer.id, mixer.mode)
mixer.loop = context.trackVolumeObject.loop
setMixerVolume: (mixer, volumePercent, relative, originalVolume) ->
###
// The context.trackVolumeObject has been filled with the mixer values
// that go with mixerId, and the range of that mixer
// has been set in currentMixerRangeMin-Max.
// All that needs doing is to translate the incoming percent
// into the real value ont the sliders range. Set Left/Right
// volumes on trackVolumeObject, and call SetControlState to stick.
###
newVolume = context.JK.FaderHelpers.convertPercentToAudioTaper(volumePercent);
if relative
context.trackVolumeObject.volL = context.trackVolumeObject.volL + (newVolume - originalVolume)
context.trackVolumeObject.volR = context.trackVolumeObject.volR + (newVolume - originalVolume)
# keep within range
if context.trackVolumeObject.volL < -80
context.trackVolumeObject.volL = -80
else if context.trackVolumeObject.volL > 20
context.trackVolumeObject.volL = 20
if context.trackVolumeObject.volR < -80
context.trackVolumeObject.volR = -80
else if context.trackVolumeObject.volR > 20
context.trackVolumeObject.volR = 20
else
context.trackVolumeObject.volL = newVolume
context.trackVolumeObject.volR = newVolume
context.jamClient.SessionSetControlState(mixer.id, mixer.mode);
percentFromMixerValue: (min, max, value) ->
try
range = Math.abs(max - min)
magnitude = value - min
percent = Math.round(100*(magnitude/range))
percent
catch err
0
percentToMixerValue:(min, max, percent) ->
range = Math.abs(max - min);
multiplier = percent/100; # Change 85 into 0.85
value = min + (multiplier * range);
# Protect against percents < 0 and > 100
if value < min
value = min;
if value > max
value = max;
return value;
fillTrackVolumeObject: (mixerId, mode, broadcast) ->
_broadcast = true
if broadcast?
_broadcast = broadcast
mixer = @getMixer(mixerId, mode)
context.trackVolumeObject.clientID = mixer.client_id
context.trackVolumeObject.broadcast = _broadcast
context.trackVolumeObject.master = mixer.master
context.trackVolumeObject.monitor = mixer.monitor
context.trackVolumeObject.mute = mixer.mute
context.trackVolumeObject.name = mixer.name
context.trackVolumeObject.record = mixer.record
context.trackVolumeObject.volL = mixer.volume_left
context.trackVolumeObject.pan = mixer.pan
# today we treat all tracks as mono, but this is required to make a stereo track happy
# context.trackVolumeObject.volR = mixer.volume_right;
context.trackVolumeObject.volR = mixer.volume_left;
context.trackVolumeObject.loop = mixer.loop;
# trackVolumeObject doesn't have a place for range min/max
@currentMixerRangeMin = mixer.range_low;
@currentMixerRangeMax = mixer.range_high;
mixer
collectStats: (mixer) ->
mixerStats = @vuStats[mixer.id]
unless mixerStats?
mixerStats = {count: 0, group_name: context.JK.groupIdDisplay(mixer)}
@vuStats[mixer.id] = mixerStats
mixerStats.count++
dumpVUStats: () ->
# to use: check MixerStore for setInterval in cstr
logger.debug("VU STAT DUMP")
for mixerId, mixerStat of @vuStats
logger.debug("VU STAT: #{mixerState.group_name} count=#{mixerStat.count}")
updateVU: (mixerId, mode, leftValue, leftClipping, rightValue, rightClipping) ->
mixer = @getMixer(mixerId, mode)
if mixer?
@collectStats(mixer) if @shouldCollectVuStats
context.JK.VuHelpers.updateVU3(mixer, leftValue, leftClipping, rightValue, rightClipping)
###
if mixer
if mixer.stereo # // stereo track
if mixerId.substr(-4) == "_vul"
context.JK.VuHelpers.updateVU2('vul', mixer, value)
else
context.JK.VuHelpers.updateVU2('vur', mixer, value)
else
if mixerId.substr(-4) == "_vul"
# Do the left
context.JK.VuHelpers.updateVU2('vul', mixer, value)
# Do the right
context.JK.VuHelpers.updateVU2('vur', mixer, value)
###
getTrackInfo: () ->
context.JK.TrackHelpers.getTrackInfo(context.jamClient, @masterMixers)
getGroupMixer: (categoryId, mode) ->
groupId = if mode == MIX_MODES.MASTER then ChannelGroupIds.MasterCatGroup else ChannelGroupIds.MonitorCatGroup
mixers = @mixersForGroupId(groupId, mode)
if mixers.length == 0
#logger.warn("could not find mixer with group ID: " + groupId + ', mode:' + mode)
return null
found = null
for mixer in mixers
if mixer.name == categoryId
found = mixer
break
unless found?
logger.warn("could not find mixer with categoryId: " + categoryId)
return null
else
{
mixer: found,
muteMixer : found,
vuMixer: found,
oppositeMixer: found
}
prepareSimulatedMixers: () ->
@simulatedMusicCategoryMixers[MIX_MODES.MASTER] = @getSimulatedMusicCategoryMixer(MIX_MODES.MASTER)
@simulatedMusicCategoryMixers[MIX_MODES.PERSONAL] = @getSimulatedMusicCategoryMixer(MIX_MODES.PERSONAL)
@simulatedChatCategoryMixers[MIX_MODES.MASTER] = @getSimulatedChatCategoryMixer(MIX_MODES.MASTER)
@simulatedChatCategoryMixers[MIX_MODES.PERSONAL] = @getSimulatedChatCategoryMixer(MIX_MODES.PERSONAL)
getSimulatedMusicCategoryMixer: (mode) ->
myInputs = @getAudioInputCategoryMixer(mode)?.mixer
peerInputs = @getUserMusicCategoryMixer(mode)?.mixer
myMedia= @getMediaCategoryMixer(mode)?.mixer
peerMedia = @getUserMediaCategoryMixer(mode)?.mixer
metronome = @getMetronomeCategoryMixer(mode)?.mixer
output = @getOutputMixer(mode)
oppositeOutput = @getOutputMixer(!mode)
# if myInputs category is missing, all categories are missing (seen when audio is first starting)
if myInputs
{
first: myInputs
mixer: [myInputs, peerInputs, myMedia, peerMedia, metronome]
muteMixer: [myInputs, peerInputs, myMedia, peerMedia, metronome]
vuMixer: output
}
else
null
getSimulatedChatCategoryMixer: (mode) ->
myChats = @getChatCategoryMixer(mode)?.mixer
peerChats = @getUserChatCategoryMixer(mode)?.mixer
if myChats
{
first: myChats
mixer: [myChats, peerChats]
muteMixer: [myChats, peerChats]
vuMixer: myChats
}
else
null
getAudioInputCategoryMixer: (mode) ->
@getGroupMixer(CategoryGroupIds.AudioInputMusic, mode)
getChatCategoryMixer: (mode) ->
@getGroupMixer(CategoryGroupIds.AudioInputChat, mode)
getUserChatCategoryMixer: (mode) ->
@getGroupMixer(CategoryGroupIds.UserChat, mode)
getMediaCategoryMixer: (mode) ->
@getGroupMixer(CategoryGroupIds.MediaTrack, mode)
getUserMediaCategoryMixer: (mode) ->
@getGroupMixer(CategoryGroupIds.UserMedia, mode)
getUserMusicCategoryMixer: (mode) ->
@getGroupMixer(CategoryGroupIds.UserMusic, mode)
getMetronomeCategoryMixer: (mode) ->
@getGroupMixer(CategoryGroupIds.Metronome, mode)
getOutputCategoryMixer: (mode) ->
if mode == MIX_MODES.MASTER
@getGroupMixer(CategoryGroupIds.MasterCatGroup, mode)
else
@getGroupMixer(CategoryGroupIds.MonitorCatGroup, mode)
getOutputMixer: (mode) ->
if mode == MIX_MODES.MASTER
@mixerForGroupId(ChannelGroupIds.MasterGroup, mode)
else
@mixerForGroupId(ChannelGroupIds.MonitorGroup, mode)
refreshMixer: (mixers) ->
return null unless mixers? && mixers.mixer?
updateMixers = null
if $.isArray(mixers.mixer)
if mixers.mixer.length > 0
updateMixers = []
for mixer in mixers.mixer
updateMixers.push(@getMixer(mixer.id, mixer.mode))
else
updateMixers = @getMixer(mixers.mixer.id, mixers.mixer.mode)
updatedVUMixers = null
if $.isArray(mixers.vuMixer)
updatedVUMixers = []
for vuMixer in mixers.vuMixer
updateVUMixers.push(@getMixer(vuMixer.id, vuMixer.mode))
else
updateVUMixers = @getMixer(mixers.vuMixer.id, mixers.vuMixer.mode)
updateMuteMixers = null
if $.isArray(mixers.muteMixer)
updateMuteMixers = []
for muteMixer in mixers.muteMixer
updateMuteMixers.push(@getMixer(muteMixer.id, muteMixer.mode))
else
updateMuteMixers = @getMixer(mixers.muteMixer.id, mixers.muteMixer.mode)
oppositeMixer = if mixers.oppositeMixer then @getMixer(mixers.oppositeMixer.id, mixers.oppositeMixer.mode) else null
if updateMixers
{
mixer: updateMixers
vuMixer: updateVUMixers
muteMixer: updateMuteMixers
oppositeMixer: oppositeMixer
}
else
return null
recordingName: () ->
@session.recordingName()
jamTrackName: () ->
@session.jamTrackName()