Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4562b3b9 authored by jiabin's avatar jiabin
Browse files

Use standard containers for audio stuff in audio framework

1. Add SampleRateSet, FormatSet, ChannelMaskSet, FormatVector
using std::vector and std::set in libaudiofoundation.
2. Use SampleRateSet, FormatSet, ChannelMaskSet, FormatVector in
audio framework.

Bug: 135621476
Test: make, CTS for AudioRecord, AudioTrack, AudioManagerTest
Test: play and record audio
Change-Id: Ic04e637bcc6ba9df84c5e7561ec3c03f18a7d242
Merged-In: Ic04e637bcc6ba9df84c5e7561ec3c03f18a7d242
parent 41502b24
Loading
Loading
Loading
Loading
+52 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2019 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#pragma once

#include <set>
#include <vector>

#include <system/audio.h>

namespace android {

using ChannelMaskSet = std::set<audio_channel_mask_t>;
using FormatSet = std::set<audio_format_t>;
using SampleRateSet = std::set<uint32_t>;

using FormatVector = std::vector<audio_format_t>;

static inline ChannelMaskSet asInMask(const ChannelMaskSet& channelMasks) {
    ChannelMaskSet inMaskSet;
    for (const auto &channel : channelMasks) {
        if (audio_channel_mask_out_to_in(channel) != AUDIO_CHANNEL_INVALID) {
            inMaskSet.insert(audio_channel_mask_out_to_in(channel));
        }
    }
    return inMaskSet;
}

static inline ChannelMaskSet asOutMask(const ChannelMaskSet& channelMasks) {
    ChannelMaskSet outMaskSet;
    for (const auto &channel : channelMasks) {
        if (audio_channel_mask_in_to_out(channel) != AUDIO_CHANNEL_INVALID) {
            outMaskSet.insert(audio_channel_mask_in_to_out(channel));
        }
    }
    return outMaskSet;
}

} // namespace android
 No newline at end of file
+11 −17
Original line number Original line Diff line number Diff line
@@ -17,10 +17,11 @@
#ifndef ANDROID_TYPE_CONVERTER_H_
#ifndef ANDROID_TYPE_CONVERTER_H_
#define ANDROID_TYPE_CONVERTER_H_
#define ANDROID_TYPE_CONVERTER_H_


#include <set>
#include <string>
#include <string>
#include <string.h>
#include <string.h>

#include <vector>
#include <vector>

#include <system/audio.h>
#include <system/audio.h>
#include <utils/Log.h>
#include <utils/Log.h>
#include <utils/Vector.h>
#include <utils/Vector.h>
@@ -42,37 +43,37 @@ struct DefaultTraits
    }
    }
};
};
template <typename T>
template <typename T>
struct VectorTraits
struct SortedVectorTraits
{
{
    typedef T Type;
    typedef T Type;
    typedef Vector<Type> Collection;
    typedef SortedVector<Type> Collection;
    static void add(Collection &collection, Type value)
    static void add(Collection &collection, Type value)
    {
    {
        collection.add(value);
        collection.add(value);
    }
    }
};
};
template <typename T>
template <typename T>
struct SortedVectorTraits
struct SetTraits
{
{
    typedef T Type;
    typedef T Type;
    typedef SortedVector<Type> Collection;
    typedef std::set<Type> Collection;
    static void add(Collection &collection, Type value)
    static void add(Collection &collection, Type value)
    {
    {
        collection.add(value);
        collection.insert(value);
    }
    }
};
};


using SampleRateTraits = SortedVectorTraits<uint32_t>;
using SampleRateTraits = SetTraits<uint32_t>;
using DeviceTraits = DefaultTraits<audio_devices_t>;
using DeviceTraits = DefaultTraits<audio_devices_t>;
struct OutputDeviceTraits : public DeviceTraits {};
struct OutputDeviceTraits : public DeviceTraits {};
struct InputDeviceTraits : public DeviceTraits {};
struct InputDeviceTraits : public DeviceTraits {};
using ChannelTraits = SortedVectorTraits<audio_channel_mask_t>;
using ChannelTraits = SetTraits<audio_channel_mask_t>;
struct OutputChannelTraits : public ChannelTraits {};
struct OutputChannelTraits : public ChannelTraits {};
struct InputChannelTraits : public ChannelTraits {};
struct InputChannelTraits : public ChannelTraits {};
struct ChannelIndexTraits : public ChannelTraits {};
struct ChannelIndexTraits : public ChannelTraits {};
using InputFlagTraits = DefaultTraits<audio_input_flags_t>;
using InputFlagTraits = DefaultTraits<audio_input_flags_t>;
using OutputFlagTraits = DefaultTraits<audio_output_flags_t>;
using OutputFlagTraits = DefaultTraits<audio_output_flags_t>;
using FormatTraits = VectorTraits<audio_format_t>;
using FormatTraits = DefaultTraits<audio_format_t>;
using GainModeTraits = DefaultTraits<audio_gain_mode_t>;
using GainModeTraits = DefaultTraits<audio_gain_mode_t>;
using StreamTraits = DefaultTraits<audio_stream_type_t>;
using StreamTraits = DefaultTraits<audio_stream_type_t>;
using AudioModeTraits = DefaultTraits<audio_mode_t>;
using AudioModeTraits = DefaultTraits<audio_mode_t>;
@@ -259,6 +260,7 @@ template <typename T, std::enable_if_t<std::is_same<T, audio_content_type_t>::va
                                    || std::is_same<T, audio_source_t>::value
                                    || std::is_same<T, audio_source_t>::value
                                    || std::is_same<T, audio_stream_type_t>::value
                                    || std::is_same<T, audio_stream_type_t>::value
                                    || std::is_same<T, audio_usage_t>::value
                                    || std::is_same<T, audio_usage_t>::value
                                    || std::is_same<T, audio_format_t>::value
                                    , int> = 0>
                                    , int> = 0>
static inline std::string toString(const T& value)
static inline std::string toString(const T& value)
{
{
@@ -291,14 +293,6 @@ static inline std::string toString(const audio_devices_t& devices)
    return result;
    return result;
}
}


// TODO: Remove when FormatTraits uses DefaultTraits.
static inline std::string toString(const audio_format_t& format)
{
    std::string result;
    return TypeConverter<VectorTraits<audio_format_t>>::toString(format, result)
            ? result : std::to_string(static_cast<int>(format));
}

static inline std::string toString(const audio_attributes_t& attributes)
static inline std::string toString(const audio_attributes_t& attributes)
{
{
    std::ostringstream result;
    std::ostringstream result;
+5 −4
Original line number Original line Diff line number Diff line
@@ -143,8 +143,9 @@ public:
    AudioGains mGains; // gain controllers
    AudioGains mGains; // gain controllers


private:
private:
    void pickChannelMask(audio_channel_mask_t &channelMask, const ChannelsVector &channelMasks) const;
    void pickChannelMask(audio_channel_mask_t &channelMask,
    void pickSamplingRate(uint32_t &rate,const SampleRateVector &samplingRates) const;
                         const ChannelMaskSet &channelMasks) const;
    void pickSamplingRate(uint32_t &rate, const SampleRateSet &samplingRates) const;


    sp<HwModule> mModule;                 // audio HW module exposing this I/O stream
    sp<HwModule> mModule;                 // audio HW module exposing this I/O stream
    String8  mName;
    String8  mName;
+19 −58
Original line number Original line Diff line number Diff line
@@ -16,55 +16,15 @@


#pragma once
#pragma once


#include <vector>
#include <media/AudioContainers.h>

#include <system/audio.h>
#include <system/audio.h>
#include <utils/RefBase.h>
#include <utils/RefBase.h>
#include <utils/SortedVector.h>
#include <utils/String8.h>
#include <utils/String8.h>


#include "policy.h"
#include "policy.h"


namespace android {
namespace android {


typedef SortedVector<uint32_t> SampleRateVector;
typedef Vector<audio_format_t> FormatVector;

template <typename T>
bool operator== (const SortedVector<T> &left, const SortedVector<T> &right)
{
    if (left.size() != right.size()) {
        return false;
    }
    for (size_t index = 0; index < right.size(); index++) {
        if (left[index] != right[index]) {
            return false;
        }
    }
    return true;
}

template <typename T>
bool operator!= (const SortedVector<T> &left, const SortedVector<T> &right)
{
    return !(left == right);
}

class ChannelsVector : public SortedVector<audio_channel_mask_t>
{
public:
    ChannelsVector() = default;
    ChannelsVector(const ChannelsVector&) = default;
    ChannelsVector(const SortedVector<audio_channel_mask_t>& sv) :
            SortedVector<audio_channel_mask_t>(sv) {}
    ChannelsVector& operator=(const ChannelsVector&) = default;

    // Applies audio_channel_mask_out_to_in to all elements and returns the result.
    ChannelsVector asInMask() const;
    // Applies audio_channel_mask_in_to_out to all elements and returns the result.
    ChannelsVector asOutMask() const;
};

class AudioProfile : public virtual RefBase
class AudioProfile : public virtual RefBase
{
{
public:
public:
@@ -72,22 +32,22 @@ public:


    AudioProfile(audio_format_t format, audio_channel_mask_t channelMasks, uint32_t samplingRate);
    AudioProfile(audio_format_t format, audio_channel_mask_t channelMasks, uint32_t samplingRate);
    AudioProfile(audio_format_t format,
    AudioProfile(audio_format_t format,
                 const ChannelsVector &channelMasks,
                 const ChannelMaskSet &channelMasks,
                 const SampleRateVector &samplingRateCollection);
                 const SampleRateSet &samplingRateCollection);


    audio_format_t getFormat() const { return mFormat; }
    audio_format_t getFormat() const { return mFormat; }
    const ChannelsVector &getChannels() const { return mChannelMasks; }
    const ChannelMaskSet &getChannels() const { return mChannelMasks; }
    const SampleRateVector &getSampleRates() const { return mSamplingRates; }
    const SampleRateSet &getSampleRates() const { return mSamplingRates; }
    void setChannels(const ChannelsVector &channelMasks);
    void setChannels(const ChannelMaskSet &channelMasks);
    void setSampleRates(const SampleRateVector &sampleRates);
    void setSampleRates(const SampleRateSet &sampleRates);


    void clear();
    void clear();
    bool isValid() const { return hasValidFormat() && hasValidRates() && hasValidChannels(); }
    bool isValid() const { return hasValidFormat() && hasValidRates() && hasValidChannels(); }
    bool supportsChannels(audio_channel_mask_t channels) const
    bool supportsChannels(audio_channel_mask_t channels) const
    {
    {
        return mChannelMasks.indexOf(channels) >= 0;
        return mChannelMasks.count(channels) != 0;
    }
    }
    bool supportsRate(uint32_t rate) const { return mSamplingRates.indexOf(rate) >= 0; }
    bool supportsRate(uint32_t rate) const { return mSamplingRates.count(rate) != 0; }


    status_t checkExact(uint32_t rate, audio_channel_mask_t channels, audio_format_t format) const;
    status_t checkExact(uint32_t rate, audio_channel_mask_t channels, audio_format_t format) const;
    status_t checkCompatibleChannelMask(audio_channel_mask_t channelMask,
    status_t checkCompatibleChannelMask(audio_channel_mask_t channelMask,
@@ -98,8 +58,8 @@ public:
                                         uint32_t &updatedSamplingRate) const;
                                         uint32_t &updatedSamplingRate) const;


    bool hasValidFormat() const { return mFormat != AUDIO_FORMAT_DEFAULT; }
    bool hasValidFormat() const { return mFormat != AUDIO_FORMAT_DEFAULT; }
    bool hasValidRates() const { return !mSamplingRates.isEmpty(); }
    bool hasValidRates() const { return !mSamplingRates.empty(); }
    bool hasValidChannels() const { return !mChannelMasks.isEmpty(); }
    bool hasValidChannels() const { return !mChannelMasks.empty(); }


    void setDynamicChannels(bool dynamic) { mIsDynamicChannels = dynamic; }
    void setDynamicChannels(bool dynamic) { mIsDynamicChannels = dynamic; }
    bool isDynamicChannels() const { return mIsDynamicChannels; }
    bool isDynamicChannels() const { return mIsDynamicChannels; }
@@ -117,8 +77,8 @@ public:
private:
private:
    String8  mName;
    String8  mName;
    audio_format_t mFormat;
    audio_format_t mFormat;
    ChannelsVector mChannelMasks;
    ChannelMaskSet mChannelMasks;
    SampleRateVector mSamplingRates;
    SampleRateSet mSamplingRates;


    bool mIsDynamicFormat = false;
    bool mIsDynamicFormat = false;
    bool mIsDynamicChannels = false;
    bool mIsDynamicChannels = false;
@@ -126,13 +86,16 @@ private:
};
};




class AudioProfileVector : public Vector<sp<AudioProfile> >
class AudioProfileVector : public std::vector<sp<AudioProfile> >
{
{
public:
public:
    ssize_t add(const sp<AudioProfile> &profile);
    ssize_t add(const sp<AudioProfile> &profile);
    // This API is intended to be used by the policy manager once retrieving capabilities
    // This API is intended to be used by the policy manager once retrieving capabilities
    // for a profile with dynamic format, rate and channels attributes
    // for a profile with dynamic format, rate and channels attributes
    ssize_t addProfileFromHal(const sp<AudioProfile> &profileToAdd);
    ssize_t addProfileFromHal(const sp<AudioProfile> &profileToAdd);
    void appendProfiles(const AudioProfileVector& audioProfiles) {
        insert(end(), audioProfiles.begin(), audioProfiles.end());
    }


    status_t checkExactProfile(uint32_t samplingRate, audio_channel_mask_t channelMask,
    status_t checkExactProfile(uint32_t samplingRate, audio_channel_mask_t channelMask,
                               audio_format_t format) const;
                               audio_format_t format) const;
@@ -169,10 +132,8 @@ public:


private:
private:
    sp<AudioProfile> getProfileFor(audio_format_t format) const;
    sp<AudioProfile> getProfileFor(audio_format_t format) const;
    void setSampleRatesFor(const SampleRateVector &sampleRates, audio_format_t format);
    void setSampleRatesFor(const SampleRateSet &sampleRates, audio_format_t format);
    void setChannelsFor(const ChannelsVector &channelMasks, audio_format_t format);
    void setChannelsFor(const ChannelMaskSet &channelMasks, audio_format_t format);

    static int compareFormats(const sp<AudioProfile> *profile1, const sp<AudioProfile> *profile2);
};
};


bool operator == (const AudioProfile &left, const AudioProfile &right);
bool operator == (const AudioProfile &left, const AudioProfile &right);
+30 −48
Original line number Original line Diff line number Diff line
@@ -64,30 +64,19 @@ void AudioPort::toAudioPort(struct audio_port *port) const
{
{
    // TODO: update this function once audio_port structure reflects the new profile definition.
    // TODO: update this function once audio_port structure reflects the new profile definition.
    // For compatibility reason: flatening the AudioProfile into audio_port structure.
    // For compatibility reason: flatening the AudioProfile into audio_port structure.
    SortedVector<audio_format_t> flatenedFormats;
    FormatSet flatenedFormats;
    SampleRateVector flatenedRates;
    SampleRateSet flatenedRates;
    ChannelsVector flatenedChannels;
    ChannelMaskSet flatenedChannels;
    for (const auto& profile : mProfiles) {
    for (const auto& profile : mProfiles) {
        if (profile->isValid()) {
        if (profile->isValid()) {
            audio_format_t formatToExport = profile->getFormat();
            audio_format_t formatToExport = profile->getFormat();
            const SampleRateVector &ratesToExport = profile->getSampleRates();
            const SampleRateSet &ratesToExport = profile->getSampleRates();
            const ChannelsVector &channelsToExport = profile->getChannels();
            const ChannelMaskSet &channelsToExport = profile->getChannels();

            flatenedFormats.insert(formatToExport);
            flatenedRates.insert(ratesToExport.begin(), ratesToExport.end());
            flatenedChannels.insert(channelsToExport.begin(), channelsToExport.end());


            if (flatenedFormats.indexOf(formatToExport) < 0) {
                flatenedFormats.add(formatToExport);
            }
            for (size_t rateIndex = 0; rateIndex < ratesToExport.size(); rateIndex++) {
                uint32_t rate = ratesToExport[rateIndex];
                if (flatenedRates.indexOf(rate) < 0) {
                    flatenedRates.add(rate);
                }
            }
            for (size_t chanIndex = 0; chanIndex < channelsToExport.size(); chanIndex++) {
                audio_channel_mask_t channels = channelsToExport[chanIndex];
                if (flatenedChannels.indexOf(channels) < 0) {
                    flatenedChannels.add(channels);
                }
            }
            if (flatenedRates.size() > AUDIO_PORT_MAX_SAMPLING_RATES ||
            if (flatenedRates.size() > AUDIO_PORT_MAX_SAMPLING_RATES ||
                    flatenedChannels.size() > AUDIO_PORT_MAX_CHANNEL_MASKS ||
                    flatenedChannels.size() > AUDIO_PORT_MAX_CHANNEL_MASKS ||
                    flatenedFormats.size() > AUDIO_PORT_MAX_FORMATS) {
                    flatenedFormats.size() > AUDIO_PORT_MAX_FORMATS) {
@@ -102,23 +91,16 @@ void AudioPort::toAudioPort(struct audio_port *port) const
    port->num_sample_rates = flatenedRates.size();
    port->num_sample_rates = flatenedRates.size();
    port->num_channel_masks = flatenedChannels.size();
    port->num_channel_masks = flatenedChannels.size();
    port->num_formats = flatenedFormats.size();
    port->num_formats = flatenedFormats.size();
    for (size_t i = 0; i < flatenedRates.size(); i++) {
    std::copy(flatenedRates.begin(), flatenedRates.end(), port->sample_rates);
        port->sample_rates[i] = flatenedRates[i];
    std::copy(flatenedChannels.begin(), flatenedChannels.end(), port->channel_masks);
    }
    std::copy(flatenedFormats.begin(), flatenedFormats.end(), port->formats);
    for (size_t i = 0; i < flatenedChannels.size(); i++) {
        port->channel_masks[i] = flatenedChannels[i];
    }
    for (size_t i = 0; i < flatenedFormats.size(); i++) {
        port->formats[i] = flatenedFormats[i];
    }


    ALOGV("AudioPort::toAudioPort() num gains %zu", mGains.size());
    ALOGV("AudioPort::toAudioPort() num gains %zu", mGains.size());


    uint32_t i;
    port->num_gains = std::min(mGains.size(), (size_t) AUDIO_PORT_MAX_GAINS);
    for (i = 0; i < mGains.size() && i < AUDIO_PORT_MAX_GAINS; i++) {
    for (size_t i = 0; i < port->num_gains; i++) {
        port->gains[i] = mGains[i]->getGain();
        port->gains[i] = mGains[i]->getGain();
    }
    }
    port->num_gains = i;
}
}


void AudioPort::importAudioPort(const sp<AudioPort>& port, bool force __unused)
void AudioPort::importAudioPort(const sp<AudioPort>& port, bool force __unused)
@@ -162,7 +144,7 @@ status_t AudioPort::checkExactAudioProfile(const struct audio_port_config *confi
    return status;
    return status;
}
}


void AudioPort::pickSamplingRate(uint32_t &pickedRate,const SampleRateVector &samplingRates) const
void AudioPort::pickSamplingRate(uint32_t &pickedRate,const SampleRateSet &samplingRates) const
{
{
    pickedRate = 0;
    pickedRate = 0;
    // For direct outputs, pick minimum sampling rate: this helps ensuring that the
    // For direct outputs, pick minimum sampling rate: this helps ensuring that the
@@ -170,9 +152,9 @@ void AudioPort::pickSamplingRate(uint32_t &pickedRate,const SampleRateVector &sa
    // sink
    // sink
    if (isDirectOutput()) {
    if (isDirectOutput()) {
        uint32_t samplingRate = UINT_MAX;
        uint32_t samplingRate = UINT_MAX;
        for (size_t i = 0; i < samplingRates.size(); i ++) {
        for (const auto rate : samplingRates) {
            if ((samplingRates[i] < samplingRate) && (samplingRates[i] > 0)) {
            if ((rate < samplingRate) && (rate > 0)) {
                samplingRate = samplingRates[i];
                samplingRate = rate;
            }
            }
        }
        }
        pickedRate = (samplingRate == UINT_MAX) ? 0 : samplingRate;
        pickedRate = (samplingRate == UINT_MAX) ? 0 : samplingRate;
@@ -188,16 +170,16 @@ void AudioPort::pickSamplingRate(uint32_t &pickedRate,const SampleRateVector &sa
        // TODO: should mSamplingRates[] be ordered in terms of our preference
        // TODO: should mSamplingRates[] be ordered in terms of our preference
        // and we return the first (and hence most preferred) match?  This is of concern if
        // and we return the first (and hence most preferred) match?  This is of concern if
        // we want to choose 96kHz over 192kHz for USB driver stability or resource constraints.
        // we want to choose 96kHz over 192kHz for USB driver stability or resource constraints.
        for (size_t i = 0; i < samplingRates.size(); i ++) {
        for (const auto rate : samplingRates) {
            if ((samplingRates[i] > pickedRate) && (samplingRates[i] <= maxRate)) {
            if ((rate > pickedRate) && (rate <= maxRate)) {
                pickedRate = samplingRates[i];
                pickedRate = rate;
            }
            }
        }
        }
    }
    }
}
}


void AudioPort::pickChannelMask(audio_channel_mask_t &pickedChannelMask,
void AudioPort::pickChannelMask(audio_channel_mask_t &pickedChannelMask,
                                const ChannelsVector &channelMasks) const
                                const ChannelMaskSet &channelMasks) const
{
{
    pickedChannelMask = AUDIO_CHANNEL_NONE;
    pickedChannelMask = AUDIO_CHANNEL_NONE;
    // For direct outputs, pick minimum channel count: this helps ensuring that the
    // For direct outputs, pick minimum channel count: this helps ensuring that the
@@ -205,15 +187,15 @@ void AudioPort::pickChannelMask(audio_channel_mask_t &pickedChannelMask,
    // sink
    // sink
    if (isDirectOutput()) {
    if (isDirectOutput()) {
        uint32_t channelCount = UINT_MAX;
        uint32_t channelCount = UINT_MAX;
        for (size_t i = 0; i < channelMasks.size(); i ++) {
        for (const auto channelMask : channelMasks) {
            uint32_t cnlCount;
            uint32_t cnlCount;
            if (useInputChannelMask()) {
            if (useInputChannelMask()) {
                cnlCount = audio_channel_count_from_in_mask(channelMasks[i]);
                cnlCount = audio_channel_count_from_in_mask(channelMask);
            } else {
            } else {
                cnlCount = audio_channel_count_from_out_mask(channelMasks[i]);
                cnlCount = audio_channel_count_from_out_mask(channelMask);
            }
            }
            if ((cnlCount < channelCount) && (cnlCount > 0)) {
            if ((cnlCount < channelCount) && (cnlCount > 0)) {
                pickedChannelMask = channelMasks[i];
                pickedChannelMask = channelMask;
                channelCount = cnlCount;
                channelCount = cnlCount;
            }
            }
        }
        }
@@ -226,15 +208,15 @@ void AudioPort::pickChannelMask(audio_channel_mask_t &pickedChannelMask,
        if (mType != AUDIO_PORT_TYPE_MIX) {
        if (mType != AUDIO_PORT_TYPE_MIX) {
            maxCount = UINT_MAX;
            maxCount = UINT_MAX;
        }
        }
        for (size_t i = 0; i < channelMasks.size(); i ++) {
        for (const auto channelMask : channelMasks) {
            uint32_t cnlCount;
            uint32_t cnlCount;
            if (useInputChannelMask()) {
            if (useInputChannelMask()) {
                cnlCount = audio_channel_count_from_in_mask(channelMasks[i]);
                cnlCount = audio_channel_count_from_in_mask(channelMask);
            } else {
            } else {
                cnlCount = audio_channel_count_from_out_mask(channelMasks[i]);
                cnlCount = audio_channel_count_from_out_mask(channelMask);
            }
            }
            if ((cnlCount > channelCount) && (cnlCount <= maxCount)) {
            if ((cnlCount > channelCount) && (cnlCount <= maxCount)) {
                pickedChannelMask = channelMasks[i];
                pickedChannelMask = channelMask;
                channelCount = cnlCount;
                channelCount = cnlCount;
            }
            }
        }
        }
Loading