Java tutorial
/* * Copyright (C) 2016 Netflix, Inc. * * This file is part of IMF Conversion Utility. * * IMF Conversion Utility is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * IMF Conversion Utility is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with IMF Conversion Utility. If not, see <http://www.gnu.org/licenses/>. */ package com.netflix.imfutility.itunes.audio; import com.netflix.imfutility.ConversionException; import com.netflix.imfutility.conversion.templateParameter.ContextInfoBuilder; import com.netflix.imfutility.conversion.templateParameter.context.SequenceTemplateParameterContext; import com.netflix.imfutility.conversion.templateParameter.context.TemplateParameterContextProvider; import com.netflix.imfutility.conversion.templateParameter.context.parameters.SequenceContextParameters; import com.netflix.imfutility.cpl.uuid.SequenceUUID; import com.netflix.imfutility.generated.conversion.SequenceType; import com.netflix.imfutility.generated.itunes.audiomap.AlternativeAudioType; import com.netflix.imfutility.generated.itunes.audiomap.AudioMapType; import com.netflix.imfutility.generated.itunes.audiomap.ChannelType; import com.netflix.imfutility.generated.itunes.audiomap.MainAudioType; import com.netflix.imfutility.generated.itunes.audiomap.Option1AType; import com.netflix.imfutility.generated.itunes.audiomap.Option2Type; import com.netflix.imfutility.generated.itunes.audiomap.Option3Type; import com.netflix.imfutility.generated.itunes.audiomap.Option4Type; import com.netflix.imfutility.generated.itunes.audiomap.Option5Type; import com.netflix.imfutility.generated.itunes.audiomap.Option6Type; import com.netflix.imfutility.itunes.audio.ChannelsMapper.LayoutType; import com.netflix.imfutility.itunes.locale.LocaleHelper; import com.netflix.imfutility.itunes.xmlprovider.LocalizedXmlProvider; import com.netflix.imfutility.util.StreamUtil; import com.netflix.imfutility.xml.XmlParser; import com.netflix.imfutility.xml.XmlParsingException; import org.apache.commons.lang3.tuple.Pair; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; import java.io.FileNotFoundException; import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Locale; import java.util.StringJoiner; import java.util.stream.Collectors; import java.util.stream.Stream; import static com.netflix.imfutility.itunes.ITunesConversionConstants.DEFAULT_LOCALE; import static com.netflix.imfutility.itunes.ITunesConversionXsdConstants.AUDIOMAP_PACKAGE; import static com.netflix.imfutility.itunes.ITunesConversionXsdConstants.AUDIOMAP_XML_SCHEME; import static com.netflix.imfutility.util.FFmpegAudioChannels.FC; import static com.netflix.imfutility.util.FFmpegAudioChannels.FL; import static com.netflix.imfutility.util.FFmpegAudioChannels.FR; import static com.netflix.imfutility.util.FFmpegAudioChannels.LFE; import static com.netflix.imfutility.util.FFmpegAudioChannels.SL; import static com.netflix.imfutility.util.FFmpegAudioChannels.SR; /** * Basic functionality for audiomap.xml handling. */ public class AudioMapXmlProvider implements LocalizedXmlProvider { /** * Internal. Describes AudioOption of iTunes audio asset configuration. */ public static class AudioOption extends ArrayList<LinkedHashMap<String, ChannelType>> { private String fileName = null; private String locale = null; public String getFileName() { return fileName; } public void setFileName(String fileName) { this.fileName = fileName; } public String getLocale() { return locale; } public void setLocale(String locale) { this.locale = locale; } } /** * Audio channel map: channelName -> CPL Virtual Track mapping. */ private class Channel extends SimpleEntry<String, ChannelType> { public Channel(String k, ChannelType v) { super(k, v); } } private static final String INTERMEDIATE_KEY_SEPARATOR = ":"; /** * Logger. */ private final Logger logger = LoggerFactory.getLogger(AudioMapXmlProvider.class); private final boolean customized; /** * Context provider. */ private final TemplateParameterContextProvider contextProvider; /** * Channel mapper used for default channel mapping (by order) or guessing channels layout by EssenceDescriptor. */ private final ChannelsMapper channelMapper; /** * Parsed AudioMapType from XML. */ private final AudioMapType audioMap; /** * CPL virtual tracks to channels number map. */ private final LinkedHashMap<String, Integer> virtualTracksChannels; /** * Contains intermediate flat indexes map of sequence uuid and sequence channels. * <p></p> * For example: 2 sequences with uuid1 channels 4 and uuid2 channels 2 are converted to * ["uuid1:1", "uuid1:2", uuid1:3, uuid1:4, "uuid2:1", "uuid2:2"] and indexOf of this array will specify correct * channel number of "amerge" channel order. */ private final ArrayList<String> sequencedTrackChannelNumbers; /** * Main audio configuration. */ private AudioOption mainAudio; /** * Additional audio configurations if exist. */ private ArrayList<AudioOption> alternativesAudio; /** * Constructor with default generated AudioMap on default language. * * @param contextProvider context provider * @throws FileNotFoundException if audioMapFile not found * @throws XmlParsingException if audiomap.xml is not validated by schema */ public AudioMapXmlProvider(TemplateParameterContextProvider contextProvider) throws FileNotFoundException, XmlParsingException { this(contextProvider, DEFAULT_LOCALE); } /** * Constructor with default generated AudioMap. * * @param contextProvider context provider * @param locale locale of generated main audio * @throws FileNotFoundException if audioMapFile not found * @throws XmlParsingException if audiomap.xml is not validated by schema */ public AudioMapXmlProvider(TemplateParameterContextProvider contextProvider, String locale) throws FileNotFoundException, XmlParsingException { this(null, contextProvider, locale); } /** * Constructor with default generated AudioMap. * * @param audiomapFile audio map file * @param contextProvider context provider * @throws FileNotFoundException if audioMapFile not found * @throws XmlParsingException if audiomap.xml is not validated by schema */ public AudioMapXmlProvider(File audiomapFile, TemplateParameterContextProvider contextProvider) throws FileNotFoundException, XmlParsingException { this(audiomapFile, contextProvider, DEFAULT_LOCALE); } /** * Constructor. * * @param audiomapFile audio map file * @param contextProvider context provider * @param locale locale of generated main audio * @throws FileNotFoundException if audioMapFile not found * @throws XmlParsingException if audiomap.xml is not validated by schema */ public AudioMapXmlProvider(File audiomapFile, TemplateParameterContextProvider contextProvider, String locale) throws FileNotFoundException, XmlParsingException { this.customized = audiomapFile != null; this.contextProvider = contextProvider; this.virtualTracksChannels = getVirtualTrackChannels(); this.sequencedTrackChannelNumbers = getSequencedTrackChannelNumbers(this.virtualTracksChannels); this.channelMapper = new ChannelsMapper(contextProvider); this.audioMap = customized ? loadAudioMapXml(audiomapFile) : generateDefaultAudioMap(locale); if (customized) { logger.info("AudioMap XML has been parsed successfully."); } else { logger.warn("No audiomap.xml specified as a command line argument. A default AudioMap was generated."); } } /** * Creates audio track. * * @param channels track channels * @return audio track */ private static LinkedHashMap<String, ChannelType> createTrack(Channel... channels) { return StreamUtil.createLinkedMap(channels); } /** * Initialize main and alternative audios. */ public void initAudio() { channelMapper.mapChannels(getMapperLayoutOptions()); this.mainAudio = getMainAudio(this.audioMap); this.alternativesAudio = getAlternativeAudios(this.audioMap); } @Override public Locale getLocale() { return LocaleHelper.fromITunesLocale(audioMap.getMainAudio().getLocale()); } @Override public void setLocale(Locale locale) { audioMap.getMainAudio().setLocale(LocaleHelper.toITunesLocale(locale)); } public boolean isCustomized() { return customized; } /** * Gets main audio file name. * * @return main audio file name */ public String getMainAudioFileName() { return mainAudio.getFileName(); } /** * Gets count of main audio tracks/streams. * * @return count of main audio tracks/streams */ public int getMainAudioTracks() { return mainAudio.size(); } /** * Gets additional audio count. * * @return additional audio count */ public int getAdditionalAudioCount() { return alternativesAudio.size(); } /** * Gets array of additional audio tracks numbers by order. * * @return array of additional audio tracks numbers by order */ public ArrayList<Integer> getAdditionalAudioTracks() { return alternativesAudio.stream().map(AudioOption::size).collect(Collectors.toCollection(ArrayList::new)); } /** * Gets array of additional audio file names by order. * * @return array of additional audio file names by order */ public ArrayList<String> getAdditionalAudioFileNames() { return alternativesAudio.stream().map(AudioOption::getFileName) .collect(Collectors.toCollection(ArrayList::new)); } /** * Gets array of conversion pan parameters by order. * * @return array of conversion pan parameters by order */ public ArrayList<String> getPanParameters() { ArrayList<String> panParams = new ArrayList<>(); // create main audio pan parameters mainAudio.forEach((t) -> { panParams.add(getPanParameter(t)); }); alternativesAudio.stream().flatMap(AudioOption::stream).forEach((t) -> { panParams.add(getPanParameter(t)); }); return panParams; } /** * Gets parsed audio map for test purposes. * * @return parsed audio map for test purposes */ AudioMapType getAudioMap() { return audioMap; } /** * Gets main audio for test purposes. * * @return main audio for test purposes */ AudioOption getMainAudio() { return mainAudio; } /** * Gets alternatives audio for test purposes. * * @return alternatives audio for test purposes */ public ArrayList<AudioOption> getAlternativesAudio() { return alternativesAudio; } /** * Gets pan parameter for track. * <p></p> * Example: pan=4c|c0=c0|c1=c1|c2=c2|c3=c3,aformat=channel_layouts=FL+FR+FC+LFE * * @param track audio track * @return pan parameter for track */ private String getPanParameter(LinkedHashMap<String, ChannelType> track) { int[] i = { 0 }; StringJoiner aformat = new StringJoiner("+", ",aformat=channel_layouts=", ""); // ,aformat=channel_layouts= StringBuilder panParameter = new StringBuilder("pan="); // pan= panParameter.append(track.size()).append("c"); // pan=4c track.forEach((chName, channel) -> { panParameter.append("|c").append(i[0]).append("="); // pan=4c|c0= // gets channel number in amerge channels sequence int sequencedChannel = sequencedTrackChannelNumbers.indexOf( getIntermediateKey(channel.getCPLVirtualTrackId(), channel.getCPLVirtualTrackChannel() - 1)); if (sequencedChannel == -1) { throw new ConversionException(String.format( "Audio Virtual TrackId \"%s\" with channel number \"%d\" was not found in CPL.", channel.getCPLVirtualTrackId(), channel.getCPLVirtualTrackChannel())); } panParameter.append("c").append(sequencedChannel); // pan=4c|c0=c0 aformat.add(chName); // ,aformat=channel_layouts=FL i[0]++; }); panParameter.append(aformat); // pan=4c|c0=c0|c1=c1|c2=c2|c3=c3,aformat=channel_layouts=FL+FR+FC+LFE return panParameter.toString(); } /** * Gets map of CPL virtual track UUID to virtual track channels number by order. * * @return map of CPL virtual track UUID to virtual track channels number by order */ private LinkedHashMap<String, Integer> getVirtualTrackChannels() { LinkedHashMap<String, Integer> result = new LinkedHashMap<>(); SequenceTemplateParameterContext sequenceContext = contextProvider.getSequenceContext(); for (SequenceUUID uuid : sequenceContext.getUuids(SequenceType.AUDIO)) { String channelsNum = sequenceContext.getParameterValue(SequenceContextParameters.CHANNELS_NUM, new ContextInfoBuilder().setSequenceType(SequenceType.AUDIO).setSequenceUuid(uuid).build()); result.put(uuid.getUuid(), Integer.valueOf(channelsNum)); } return result; } /** * Gets flat map of uuid + channel of track for each tracks and uuids: [uuid:cn1, uuid:cn2, ....]. * * @param virtualTracksChannels uuid to channels map * @return flat map of uuid + channel of track for each tracks and uuids: [uuid:cn1, uuid:cn2, ....] */ private ArrayList<String> getSequencedTrackChannelNumbers( LinkedHashMap<String, Integer> virtualTracksChannels) { ArrayList<String> res = new ArrayList<>(); virtualTracksChannels.forEach((String trackId, Integer trackChannelCount) -> { for (int i = 0; i < trackChannelCount; i++) { res.add(getIntermediateKey(trackId, i)); } }); return res; } /** * Gets intermediate key from track id and channel number -> trackId:channelNumber. * * @param trackId track id * @param channelNumber channel number * @return intermediate key from track id and channel number -> trackId:channelNumber */ private String getIntermediateKey(String trackId, Integer channelNumber) { return trackId + INTERMEDIATE_KEY_SEPARATOR + channelNumber.toString(); } private SimpleEntry<String, Integer> splitIntermediateKey(String key) { String uuid = key.substring(0, key.lastIndexOf(INTERMEDIATE_KEY_SEPARATOR)); String channelNumber = key.substring(key.lastIndexOf(INTERMEDIATE_KEY_SEPARATOR) + 1); return new SimpleEntry<>(uuid, Integer.valueOf(channelNumber)); } /** * Loads and validates audiomap.xml. * * @return AudioMapType with loaded and mapped audiomap.xml * @throws XmlParsingException an exception in case of audiomap.xml parsing error * @throws FileNotFoundException if the audioMapXml doesn't define an existing file. */ private AudioMapType loadAudioMapXml(File audioMapFile) throws XmlParsingException, FileNotFoundException { if (!audioMapFile.isFile()) { throw new FileNotFoundException( String.format("Invalid audiomap.xml file: '%s' not found", audioMapFile.getAbsolutePath())); } return XmlParser.parse(audioMapFile, new String[] { AUDIOMAP_XML_SCHEME }, AUDIOMAP_PACKAGE, AudioMapType.class); } /** * Generates a default audiomap basis on information of CPL virtual tracks. * * @return audiomap. */ private AudioMapType generateDefaultAudioMap(String locale) { AudioMapType audioMap = new AudioMapType(); audioMap.setMainAudio(new MainAudioType()); audioMap.getMainAudio().setLocale(locale); audioMap.getMainAudio().setName("main-audio.mov"); if (!fillDefaultAudioMapFromDescriptor(audioMap)) { setAudioMapDefaultOptions(audioMap); } return audioMap; } private boolean fillDefaultAudioMapFromDescriptor(AudioMapType audioMap) { String mainLang = audioMap.getMainAudio().getLocale(); List<Pair<SequenceUUID, Integer>> mainChannels = channelMapper.guessMainAudio(mainLang); if (mainChannels.isEmpty()) { return false; } if (mainChannels.size() == 8) { audioMap.getMainAudio().setOption3(createXmlOption3(mainChannels)); } else if (mainChannels.size() == 2) { audioMap.getMainAudio().setOption6(createXmlOption6(mainChannels)); } else { return false; } channelMapper.guessAlternatives(mainLang).entrySet().forEach(entry -> { AlternativeAudioType audio = new AlternativeAudioType(); audio.setLocale(entry.getKey()); audio.setName(String.format("audio_%s.mov", entry.getKey().replace("-", "_").toUpperCase())); audio.setOption6(createXmlOption6(entry.getValue())); audioMap.getAlternativeAudio().add(audio); }); return true; } private void setAudioMapDefaultOptions(AudioMapType audioMap) { if (sequencedTrackChannelNumbers.size() < 6) { audioMap.getMainAudio().setOption6(new Option6Type()); } else { audioMap.getMainAudio().setOption3(new Option3Type()); } } /** * Gets mainAudio as AudioOption from parsed XML type. * * @param audioMap parsed audio map * @return mainAudio as AudioOption from parsed XML type */ private AudioOption getMainAudio(AudioMapType audioMap) { AudioOption option; Object xmlOpt = getXmlMainAudioOption(audioMap.getMainAudio()); verifyOption(xmlOpt); option = audioOptionFromXmlOption(xmlOpt, audioMap.getMainAudio().getLocale(), true); option.setFileName(audioMap.getMainAudio().getName()); option.setLocale(audioMap.getMainAudio().getLocale()); return option; } /** * Gets array of additional audio from parsed XML type. * * @param audioMap parsed audio map * @return array of additional audio from parsed XML type */ private ArrayList<AudioOption> getAlternativeAudios(AudioMapType audioMap) { ArrayList<AudioOption> options = new ArrayList<>(); if (audioMap.getAlternativeAudio() == null) { return options; } audioMap.getAlternativeAudio().forEach((altAudio) -> { AudioOption option; Object xmlOpt = getXmlAlternativeAudioOption(altAudio); verifyOption(xmlOpt); option = audioOptionFromXmlOption(xmlOpt, altAudio.getLocale(), false); if (option != null) { option.setFileName(altAudio.getName()); option.setLocale(altAudio.getLocale()); options.add(option); } }); return options; } /** * Gets mapper options that contains expected layout for all audiomap opts, which aren't linked to related track explicitly. * * @return list of all options that aren't linked to related tracks */ private List<Pair<LayoutType, String>> getMapperLayoutOptions() { List<Pair<LayoutType, String>> options = new ArrayList<>(); MainAudioType mainAudio = audioMap.getMainAudio(); Object mainOption = getXmlMainAudioOption(mainAudio); verifyOption(mainOption); if (isOptionEmpty(mainOption)) { options.add(getMapperLayoutOption(mainOption, mainAudio.getLocale())); } audioMap.getAlternativeAudio().forEach(altAudio -> { Object altOption = getXmlAlternativeAudioOption(altAudio); verifyOption(altOption); if (isOptionEmpty(altOption)) { options.add(getMapperLayoutOption(altOption, altAudio.getLocale())); } }); return options; } /** * Gets option that defines expected audio layout and language. * * @param xmlOpt xml option type * @param lang expected language of audio * @return {@link Pair} of layout type and language */ private Pair<LayoutType, String> getMapperLayoutOption(Object xmlOpt, String lang) { LayoutType layout = null; if (xmlOpt instanceof Option1AType || xmlOpt instanceof Option2Type || xmlOpt instanceof Option3Type || xmlOpt instanceof Option4Type) { layout = LayoutType.SURROUND; } else if (xmlOpt instanceof Option5Type || xmlOpt instanceof Option6Type) { layout = LayoutType.STEREO; } return Pair.of(layout, lang); } /** * Gets existing main audio XML option as object. * * @param mainAudio main audio * @return existing main audio XML option as object */ private Object getXmlMainAudioOption(MainAudioType mainAudio) { Object opt = null; if (mainAudio.getOption1A() != null) { opt = mainAudio.getOption1A(); } else if (mainAudio.getOption2() != null) { opt = mainAudio.getOption2(); } else if (mainAudio.getOption3() != null) { opt = mainAudio.getOption3(); } else if (mainAudio.getOption4() != null) { opt = mainAudio.getOption4(); } else if (mainAudio.getOption5() != null) { opt = mainAudio.getOption5(); } else if (mainAudio.getOption6() != null) { opt = mainAudio.getOption6(); } else { // nothing } return opt; } /** * Gets existing alternative audio XML option as object. * * @param altAudio alternative audio * @return existing alternative audio XML option as object */ private Object getXmlAlternativeAudioOption(AlternativeAudioType altAudio) { Object opt = null; if (altAudio.getOption5() != null) { opt = altAudio.getOption5(); } else if (altAudio.getOption6() != null) { opt = altAudio.getOption6(); } else { // nothing } return opt; } /** * Create AudioOption from XML option type. * * @param xmlOpt xml option type * @return AudioOption */ private AudioOption audioOptionFromXmlOption(Object xmlOpt, String lang, boolean isMainAudio) { return (isOptionEmpty(xmlOpt)) ? generateAudioOption(xmlOpt, lang, isMainAudio) : createAudioOption(xmlOpt); } /** * Verify OptionXXX and throw ConversionException on error. * * @param opt parsed Option */ private void verifyOption(Object opt) { if (!isOptionEmpty(opt) && !hasOptionAllTracks(opt)) { throw new ConversionException("Audio Option should contain all tracks or be empty"); } } /** * Generates AudioOption when XML option type is empty. * * @param xmlOpt xml option * @param lang expected language of audio * @param isMainAudio defines whether option relates to main audio or alternative * @return AudioOption */ private AudioOption generateAudioOption(Object xmlOpt, String lang, boolean isMainAudio) { AudioOption option = null; ArrayList<ChannelType> channels = channelMapper.getChannels(getMapperLayoutOption(xmlOpt, lang)).stream() .map(pair -> { ChannelType channel = new ChannelType(); channel.setCPLVirtualTrackId(pair.getLeft().getUuid()); channel.setCPLVirtualTrackChannel(pair.getRight()); return channel; }).collect(Collectors.toCollection(ArrayList::new)); if (channels.isEmpty()) { if (isMainAudio) { throw new ConversionException("Main audio can't be processed. " + "Please check CPL or audiomap.xml, it may contain inappropriate layout or essence descriptors."); } else { logger.warn("Alternative audio for {} locale can't be processed.", lang); } return null; } if (xmlOpt instanceof Option1AType) { option = createOption1A(channels.toArray(new ChannelType[0])); } else if (xmlOpt instanceof Option2Type) { option = createOption2(channels.toArray(new ChannelType[0])); } else if (xmlOpt instanceof Option3Type) { option = createOption3(channels.toArray(new ChannelType[0])); } else if (xmlOpt instanceof Option4Type) { option = createOption4(channels.toArray(new ChannelType[0])); } else if (xmlOpt instanceof Option5Type) { option = createOption5(channels.toArray(new ChannelType[0])); } else if (xmlOpt instanceof Option6Type) { option = createOption6(channels.toArray(new ChannelType[0])); } else { // nothing } return option; } private Option3Type createXmlOption3(List<Pair<SequenceUUID, Integer>> channels) { Option3Type option3 = new Option3Type(); option3.setTrack1(new Option3Type.Track1()); option3.setTrack2(new Option3Type.Track2()); option3.getTrack1().setL(createXmlChannel(channels.get(0))); option3.getTrack1().setR(createXmlChannel(channels.get(1))); option3.getTrack1().setC(createXmlChannel(channels.get(2))); option3.getTrack1().setLFE(createXmlChannel(channels.get(3))); option3.getTrack1().setLs(createXmlChannel(channels.get(4))); option3.getTrack1().setRs(createXmlChannel(channels.get(5))); option3.getTrack2().setLt(createXmlChannel(channels.get(6))); option3.getTrack2().setRt(createXmlChannel(channels.get(7))); return option3; } private Option6Type createXmlOption6(List<Pair<SequenceUUID, Integer>> channels) { Option6Type option6 = new Option6Type(); option6.setTrack1(new Option6Type.Track1()); option6.getTrack1().setL(createXmlChannel(channels.get(0))); option6.getTrack1().setR(createXmlChannel(channels.get(1))); return option6; } /** * Create xml channel info form input pair. * * @param pair * @return xml ChannelType object */ private ChannelType createXmlChannel(Pair<SequenceUUID, Integer> pair) { ChannelType channel = new ChannelType(); channel.setCPLVirtualTrackId(pair.getLeft().getUuid()); channel.setCPLVirtualTrackChannel(pair.getRight()); return channel; } /** * Creates AudioOption from XML option type. * * @param xmlOpt xml option type * @return AudioOption */ private AudioOption createAudioOption(Object xmlOpt) { AudioOption option = null; if (xmlOpt instanceof Option1AType) { Option1AType opt1A = (Option1AType) xmlOpt; option = createOption1A(opt1A.getTrack1().getL(), opt1A.getTrack1().getR(), opt1A.getTrack1().getC(), opt1A.getTrack1().getLFE(), opt1A.getTrack1().getLs(), opt1A.getTrack1().getRs(), opt1A.getTrack2().getLt(), opt1A.getTrack3().getRt()); } else if (xmlOpt instanceof Option2Type) { Option2Type opt2 = (Option2Type) xmlOpt; option = createOption2(opt2.getTrack1().getL(), opt2.getTrack2().getR(), opt2.getTrack3().getC(), opt2.getTrack4().getLFE(), opt2.getTrack5().getLs(), opt2.getTrack6().getRs(), opt2.getTrack7().getLt(), opt2.getTrack8().getRt()); } else if (xmlOpt instanceof Option3Type) { Option3Type opt3 = (Option3Type) xmlOpt; option = createOption3(opt3.getTrack1().getL(), opt3.getTrack1().getR(), opt3.getTrack1().getC(), opt3.getTrack1().getLFE(), opt3.getTrack1().getLs(), opt3.getTrack1().getRs(), opt3.getTrack2().getLt(), opt3.getTrack2().getRt()); } else if (xmlOpt instanceof Option4Type) { Option4Type opt4 = (Option4Type) xmlOpt; option = createOption4(opt4.getTrack1().getL(), opt4.getTrack2().getR(), opt4.getTrack3().getC(), opt4.getTrack4().getLFE(), opt4.getTrack5().getLs(), opt4.getTrack6().getRs(), opt4.getTrack7().getLt(), opt4.getTrack7().getRt()); } else if (xmlOpt instanceof Option5Type) { Option5Type opt5 = (Option5Type) xmlOpt; option = createOption5(opt5.getTrack1().getL(), opt5.getTrack2().getR()); } else if (xmlOpt instanceof Option6Type) { Option6Type opt6 = (Option6Type) xmlOpt; option = createOption6(opt6.getTrack1().getL(), opt6.getTrack1().getR()); } else { // nothing } return option; } /** * Creates Option1A option. * * @param channels channels array to create by order * @return Option1A */ private AudioOption createOption1A(ChannelType... channels) { AudioOption option = new AudioOption(); option.add(createTrack(new Channel(FL.name(), channels[0]), new Channel(FR.name(), channels[1]), new Channel(FC.name(), channels[2]), new Channel(LFE.name(), channels[3]), new Channel(SL.name(), channels[4]), new Channel(SR.name(), channels[5]))); Stream.of(createTrack(new Channel(FL.name(), channels[6])), createTrack(new Channel(FR.name(), channels[7]))).forEach((m) -> { option.add(m); }); return option; } /** * Creates Option2 option. * * @param channels channels array to create by order * @return Option2 */ private AudioOption createOption2(ChannelType... channels) { AudioOption option = new AudioOption(); Stream.of(createTrack(new Channel(FL.name(), channels[0])), createTrack(new Channel(FR.name(), channels[1])), createTrack(new Channel(FC.name(), channels[2])), createTrack(new Channel(LFE.name(), channels[3])), createTrack(new Channel(SL.name(), channels[4])), createTrack(new Channel(SR.name(), channels[5])), createTrack(new Channel(FL.name(), channels[6])), createTrack(new Channel(FR.name(), channels[7]))).forEach((m) -> { option.add(m); }); return option; } /** * Creates Option3 option. * * @param channels channels array to create by order * @return Option3 */ private AudioOption createOption3(ChannelType... channels) { AudioOption option = new AudioOption(); option.add(createTrack(new Channel(FL.name(), channels[0]), new Channel(FR.name(), channels[1]), new Channel(FC.name(), channels[2]), new Channel(LFE.name(), channels[3]), new Channel(SL.name(), channels[4]), new Channel(SR.name(), channels[5]))); option.add(createTrack(new Channel(FL.name(), channels[6]), new Channel(FR.name(), channels[7]))); return option; } /** * Creates Option4 option. * * @param channels channels array to create by order * @return Option4 */ private AudioOption createOption4(ChannelType... channels) { AudioOption option = new AudioOption(); Stream.of(createTrack(new Channel(FL.name(), channels[0])), createTrack(new Channel(FR.name(), channels[1])), createTrack(new Channel(FC.name(), channels[2])), createTrack(new Channel(LFE.name(), channels[3])), createTrack(new Channel(SL.name(), channels[4])), createTrack(new Channel(SR.name(), channels[5]))).forEach((m) -> { option.add(m); }); option.add(createTrack(new Channel(FL.name(), channels[6]), new Channel(FR.name(), channels[7]))); return option; } /** * Creates Option5 option. * * @param channels channels array to create by order * @return Option5 */ private AudioOption createOption5(ChannelType... channels) { AudioOption option = new AudioOption(); Stream.of(createTrack(new Channel(FL.name(), channels[0])), createTrack(new Channel(FR.name(), channels[1]))).forEach((m) -> { option.add(m); }); return option; } /** * Creates Option6 option. * * @param channels channels array to create by order * @return Option6 */ private AudioOption createOption6(ChannelType... channels) { AudioOption option = new AudioOption(); option.add(createTrack(new Channel(FL.name(), channels[0]), new Channel(FR.name(), channels[1]))); return option; } /** * Checks that Option does not contain any tracks. * * @param opt option * @return true if empty */ private boolean isOptionEmpty(Object opt) { boolean empty = false; if (opt instanceof Option1AType) { Option1AType opt1A = (Option1AType) opt; empty = opt1A.getTrack1() == null && opt1A.getTrack2() == null && opt1A.getTrack3() == null; } else if (opt instanceof Option2Type) { Option2Type opt2 = (Option2Type) opt; empty = opt2.getTrack1() == null && opt2.getTrack2() == null && opt2.getTrack3() == null && opt2.getTrack4() == null && opt2.getTrack5() == null && opt2.getTrack6() == null && opt2.getTrack7() == null && opt2.getTrack8() == null; } else if (opt instanceof Option3Type) { Option3Type opt3 = (Option3Type) opt; empty = opt3.getTrack1() == null && opt3.getTrack2() == null; } else if (opt instanceof Option4Type) { Option4Type opt4 = (Option4Type) opt; empty = opt4.getTrack1() == null && opt4.getTrack2() == null && opt4.getTrack3() == null && opt4.getTrack4() == null && opt4.getTrack5() == null && opt4.getTrack6() == null && opt4.getTrack7() == null; } else if (opt instanceof Option5Type) { Option5Type opt5 = (Option5Type) opt; empty = opt5.getTrack1() == null && opt5.getTrack2() == null; } else if (opt instanceof Option6Type) { Option6Type opt6 = (Option6Type) opt; empty = opt6.getTrack1() == null; } else { // nothing } return empty; } /** * Checks that Option contains all tracks. * * @param opt option * @return true if contains all tracks */ private boolean hasOptionAllTracks(Object opt) { boolean allTracks = false; if (opt instanceof Option1AType) { Option1AType opt1A = (Option1AType) opt; allTracks = opt1A.getTrack1() != null && opt1A.getTrack2() != null && opt1A.getTrack3() != null; } else if (opt instanceof Option2Type) { Option2Type opt2 = (Option2Type) opt; allTracks = opt2.getTrack1() != null && opt2.getTrack2() != null && opt2.getTrack3() != null && opt2.getTrack4() != null && opt2.getTrack5() != null && opt2.getTrack6() != null && opt2.getTrack7() != null && opt2.getTrack8() != null; } else if (opt instanceof Option3Type) { Option3Type opt3 = (Option3Type) opt; allTracks = opt3.getTrack1() != null && opt3.getTrack2() != null; } else if (opt instanceof Option4Type) { Option4Type opt4 = (Option4Type) opt; allTracks = opt4.getTrack1() != null && opt4.getTrack2() != null && opt4.getTrack3() != null && opt4.getTrack4() != null && opt4.getTrack5() != null && opt4.getTrack6() != null && opt4.getTrack7() != null; } else if (opt instanceof Option5Type) { Option5Type opt5 = (Option5Type) opt; allTracks = opt5.getTrack1() != null && opt5.getTrack2() != null; } else if (opt instanceof Option6Type) { Option6Type opt6 = (Option6Type) opt; allTracks = opt6.getTrack1() != null; } else { // nothing } return allTracks; } }