ControlDoublePrivate::~ControlDoublePrivate() { s_qCOHashMutex.lock(); //qDebug() << "ControlDoublePrivate::s_qCOHash.remove(" << m_key.group << "," << m_key.item << ")"; s_qCOHash.remove(m_key); s_qCOHashMutex.unlock(); if (m_bPersistInConfiguration) { UserSettingsPointer pConfig = ControlDoublePrivate::s_pUserConfig; if (pConfig != NULL) { pConfig->set(m_key, QString::number(get())); } } }
// static void BansheeFeature::prepareDbPath(UserSettingsPointer pConfig) { m_databaseFile = pConfig->getValueString(ConfigKey("[Banshee]","Database")); if (!QFile::exists(m_databaseFile)) { // Fall back to default m_databaseFile = BansheeDbConnection::getDatabaseFile(); } }
TrackCollection::TrackCollection(UserSettingsPointer pConfig) : m_pConfig(pConfig), m_db(QSqlDatabase::addDatabase("QSQLITE")), // defaultConnection m_playlistDao(m_db), m_crateDao(m_db), m_cueDao(m_db), m_directoryDao(m_db), m_analysisDao(m_db, pConfig), m_libraryHashDao(m_db), m_trackDao(m_db, m_cueDao, m_playlistDao, m_crateDao, m_analysisDao, m_libraryHashDao, pConfig) { qDebug() << "Available QtSQL drivers:" << QSqlDatabase::drivers(); m_db.setHostName("localhost"); m_db.setDatabaseName(QDir(pConfig->getSettingsPath()).filePath("mixxxdb.sqlite")); m_db.setUserName("mixxx"); m_db.setPassword("mixxx"); bool ok = m_db.open(); qDebug() << "DB status:" << m_db.databaseName() << "=" << ok; if (m_db.lastError().isValid()) { qDebug() << "Error loading database:" << m_db.lastError(); } // Check for tables and create them if missing if (!checkForTables()) { // TODO(XXX) something a little more elegant exit(-1); } }
void ControlDoublePrivate::initialize() { double value = 0; if (m_bPersistInConfiguration) { UserSettingsPointer pConfig = ControlDoublePrivate::s_pUserConfig; if (pConfig != NULL) { // Assume toDouble() returns 0 if conversion fails. value = pConfig->getValueString(m_key).toDouble(); } } m_defaultValue.setValue(0); m_value.setValue(value); //qDebug() << "Creating:" << m_trackKey << "at" << &m_value << sizeof(m_value); if (m_bTrack) { // TODO(rryan): Make configurable. Stat::track(m_trackKey, static_cast<Stat::StatType>(m_trackType), static_cast<Stat::ComputeFlags>(m_trackFlags), m_value.getValue()); } }
void ControlDoublePrivate::initialize(double defaultValue) { double value = defaultValue; if (m_bPersistInConfiguration) { UserSettingsPointer pConfig = ControlDoublePrivate::s_pUserConfig; if (pConfig != nullptr) { value = pConfig->getValue(m_key, defaultValue); } } m_defaultValue.setValue(defaultValue); m_value.setValue(value); //qDebug() << "Creating:" << m_trackKey << "at" << &m_value << sizeof(m_value); if (m_bTrack) { // TODO(rryan): Make configurable. m_trackKey = "control " + m_key.group + "," + m_key.item; Stat::track(m_trackKey, static_cast<Stat::StatType>(m_trackType), static_cast<Stat::ComputeFlags>(m_trackFlags), m_value.getValue()); } }
VinylControlControl::VinylControlControl(QString group, UserSettingsPointer pConfig) : EngineControl(group, pConfig), m_bSeekRequested(false) { m_pControlVinylStatus = new ControlObject(ConfigKey(group, "vinylcontrol_status")); m_pControlVinylSpeedType = new ControlObject(ConfigKey(group, "vinylcontrol_speed_type")); //Convert the ConfigKey's value into a double for the CO (for fast reads). QString strVinylSpeedType = pConfig->getValueString(ConfigKey(group, "vinylcontrol_speed_type")); if (strVinylSpeedType == MIXXX_VINYL_SPEED_33) { m_pControlVinylSpeedType->set(MIXXX_VINYL_SPEED_33_NUM); } else if (strVinylSpeedType == MIXXX_VINYL_SPEED_45) { m_pControlVinylSpeedType->set(MIXXX_VINYL_SPEED_45_NUM); } else { m_pControlVinylSpeedType->set(MIXXX_VINYL_SPEED_33_NUM); } m_pControlVinylSeek = new ControlObject(ConfigKey(group, "vinylcontrol_seek")); connect(m_pControlVinylSeek, &ControlObject::valueChanged, this, &VinylControlControl::slotControlVinylSeek, Qt::DirectConnection); m_pControlVinylRate = new ControlObject(ConfigKey(group, "vinylcontrol_rate")); m_pControlVinylScratching = new ControlPushButton(ConfigKey(group, "vinylcontrol_scratching")); m_pControlVinylScratching->set(0); m_pControlVinylScratching->setButtonMode(ControlPushButton::TOGGLE); m_pControlVinylEnabled = new ControlPushButton(ConfigKey(group, "vinylcontrol_enabled")); m_pControlVinylEnabled->set(0); m_pControlVinylEnabled->setButtonMode(ControlPushButton::TOGGLE); m_pControlVinylWantEnabled = new ControlPushButton(ConfigKey(group, "vinylcontrol_wantenabled")); m_pControlVinylWantEnabled->set(0); m_pControlVinylWantEnabled->setButtonMode(ControlPushButton::TOGGLE); m_pControlVinylMode = new ControlPushButton(ConfigKey(group, "vinylcontrol_mode")); m_pControlVinylMode->setStates(3); m_pControlVinylMode->setButtonMode(ControlPushButton::TOGGLE); m_pControlVinylCueing = new ControlPushButton(ConfigKey(group, "vinylcontrol_cueing")); m_pControlVinylCueing->setStates(3); m_pControlVinylCueing->setButtonMode(ControlPushButton::TOGGLE); m_pControlVinylSignalEnabled = new ControlPushButton(ConfigKey(group, "vinylcontrol_signal_enabled")); m_pControlVinylSignalEnabled->set(1); m_pControlVinylSignalEnabled->setButtonMode(ControlPushButton::TOGGLE); m_pPlayEnabled = new ControlProxy(group, "play", this); }
void ColorSchemeParser::setupLegacyColorSchemes(QDomElement docElem, UserSettingsPointer pConfig) { QDomNode colsch = docElem.namedItem("Schemes"); if (!colsch.isNull() && colsch.isElement()) { QString schname = pConfig->getValueString(ConfigKey("[Config]","Scheme")); QDomNode sch = colsch.firstChild(); bool found = false; if (schname.isEmpty()) { // If no scheme stored, accept the first one in the file found = true; } while (!sch.isNull() && !found) { QString thisname = XmlParse::selectNodeQString(sch, "Name"); if (thisname == schname) { found = true; } else { sch = sch.nextSibling(); } } if (found) { QSharedPointer<ImgSource> imsrc = QSharedPointer<ImgSource>(parseFilters(sch.namedItem("Filters"))); WPixmapStore::setLoader(imsrc); WImageStore::setLoader(imsrc); WSkinColor::setLoader(imsrc); } else { WPixmapStore::setLoader(QSharedPointer<ImgSource>()); WImageStore::setLoader(QSharedPointer<ImgSource>()); WSkinColor::setLoader(QSharedPointer<ImgSource>()); } } else { WPixmapStore::setLoader(QSharedPointer<ImgSource>()); WImageStore::setLoader(QSharedPointer<ImgSource>()); WSkinColor::setLoader(QSharedPointer<ImgSource>()); } }
Library::Library( QObject* parent, UserSettingsPointer pConfig, mixxx::DbConnectionPoolPtr pDbConnectionPool, PlayerManagerInterface* pPlayerManager, RecordingManager* pRecordingManager) : m_pConfig(pConfig), m_pDbConnectionPool(pDbConnectionPool), m_pSidebarModel(new SidebarModel(parent)), m_pTrackCollection(new TrackCollection(pConfig)), m_pLibraryControl(new LibraryControl(this)), m_pMixxxLibraryFeature(nullptr), m_pPlaylistFeature(nullptr), m_pCrateFeature(nullptr), m_pAnalysisFeature(nullptr), m_scanner(pDbConnectionPool, m_pTrackCollection, pConfig) { QSqlDatabase dbConnection = mixxx::DbConnectionPooled(m_pDbConnectionPool); // TODO(XXX): Add a checkbox in the library preferences for checking // and repairing the database on the next restart of the application. if (pConfig->getValue(kConfigKeyRepairDatabaseOnNextRestart, false)) { kLogger.info() << "Checking and repairing database (if necessary)"; m_pTrackCollection->repairDatabase(dbConnection); // Reset config value pConfig->setValue(kConfigKeyRepairDatabaseOnNextRestart, false); } kLogger.info() << "Connecting database"; m_pTrackCollection->connectDatabase(dbConnection); qRegisterMetaType<Library::RemovalType>("Library::RemovalType"); m_pKeyNotation.reset(new ControlObject(ConfigKey(kConfigGroup, "key_notation"))); connect(&m_scanner, SIGNAL(scanStarted()), this, SIGNAL(scanStarted())); connect(&m_scanner, SIGNAL(scanFinished()), this, SIGNAL(scanFinished())); // Refresh the library models when the library (re)scan is finished. connect(&m_scanner, SIGNAL(scanFinished()), this, SLOT(slotRefreshLibraryModels())); // TODO(rryan) -- turn this construction / adding of features into a static // method or something -- CreateDefaultLibrary m_pMixxxLibraryFeature = new MixxxLibraryFeature(this, m_pTrackCollection,m_pConfig); addFeature(m_pMixxxLibraryFeature); addFeature(new AutoDJFeature(this, pConfig, pPlayerManager, m_pTrackCollection)); m_pPlaylistFeature = new PlaylistFeature(this, m_pTrackCollection, m_pConfig); addFeature(m_pPlaylistFeature); m_pCrateFeature = new CrateFeature(this, m_pTrackCollection, m_pConfig); addFeature(m_pCrateFeature); BrowseFeature* browseFeature = new BrowseFeature( this, pConfig, m_pTrackCollection, pRecordingManager); connect(browseFeature, SIGNAL(scanLibrary()), &m_scanner, SLOT(scan())); connect(&m_scanner, SIGNAL(scanStarted()), browseFeature, SLOT(slotLibraryScanStarted())); connect(&m_scanner, SIGNAL(scanFinished()), browseFeature, SLOT(slotLibraryScanFinished())); addFeature(browseFeature); addFeature(new RecordingFeature(this, pConfig, m_pTrackCollection, pRecordingManager)); addFeature(new SetlogFeature(this, pConfig, m_pTrackCollection)); m_pAnalysisFeature = new AnalysisFeature(this, pConfig, m_pTrackCollection); connect(m_pPlaylistFeature, SIGNAL(analyzeTracks(QList<TrackId>)), m_pAnalysisFeature, SLOT(analyzeTracks(QList<TrackId>))); connect(m_pCrateFeature, SIGNAL(analyzeTracks(QList<TrackId>)), m_pAnalysisFeature, SLOT(analyzeTracks(QList<TrackId>))); addFeature(m_pAnalysisFeature); //iTunes and Rhythmbox should be last until we no longer have an obnoxious //messagebox popup when you select them. (This forces you to reach for your //mouse or keyboard if you're using MIDI control and you scroll through them...) if (RhythmboxFeature::isSupported() && pConfig->getValue(ConfigKey(kConfigGroup,"ShowRhythmboxLibrary"), true)) { addFeature(new RhythmboxFeature(this, m_pTrackCollection)); } if (pConfig->getValue(ConfigKey(kConfigGroup,"ShowBansheeLibrary"), true)) { BansheeFeature::prepareDbPath(pConfig); if (BansheeFeature::isSupported()) { addFeature(new BansheeFeature(this, m_pTrackCollection, pConfig)); } } if (ITunesFeature::isSupported() && pConfig->getValue(ConfigKey(kConfigGroup,"ShowITunesLibrary"), true)) { addFeature(new ITunesFeature(this, m_pTrackCollection)); } if (TraktorFeature::isSupported() && pConfig->getValue(ConfigKey(kConfigGroup,"ShowTraktorLibrary"), true)) { addFeature(new TraktorFeature(this, m_pTrackCollection)); } // On startup we need to check if all of the user's library folders are // accessible to us. If the user is using a database from <1.12.0 with // sandboxing then we will need them to give us permission. QStringList directories = m_pTrackCollection->getDirectoryDAO().getDirs(); qDebug() << "Checking for access to user's library directories:"; foreach (QString directoryPath, directories) { QFileInfo directory(directoryPath); bool hasAccess = Sandbox::askForAccess(directory.canonicalFilePath()); qDebug() << "Checking for access to" << directoryPath << ":" << hasAccess; }
EngineMaster::EngineMaster(UserSettingsPointer pConfig, const char* group, EffectsManager* pEffectsManager, bool bEnableSidechain, bool bRampingGain) : m_pEngineEffectsManager(pEffectsManager ? pEffectsManager->getEngineEffectsManager() : NULL), m_bRampingGain(bRampingGain), m_masterGainOld(0.0), m_boothGainOld(0.0), m_headphoneMasterGainOld(0.0), m_headphoneGainOld(1.0), m_masterHandle(registerChannelGroup("[Master]")), m_headphoneHandle(registerChannelGroup("[Headphone]")), m_busLeftHandle(registerChannelGroup("[BusLeft]")), m_busCenterHandle(registerChannelGroup("[BusCenter]")), m_busRightHandle(registerChannelGroup("[BusRight]")) { m_bBusOutputConnected[EngineChannel::LEFT] = false; m_bBusOutputConnected[EngineChannel::CENTER] = false; m_bBusOutputConnected[EngineChannel::RIGHT] = false; m_bExternalRecordBroadcastInputConnected = false; m_pWorkerScheduler = new EngineWorkerScheduler(this); m_pWorkerScheduler->start(QThread::HighPriority); if (pEffectsManager) { pEffectsManager->registerChannel(m_masterHandle); pEffectsManager->registerChannel(m_headphoneHandle); pEffectsManager->registerChannel(m_busLeftHandle); pEffectsManager->registerChannel(m_busCenterHandle); pEffectsManager->registerChannel(m_busRightHandle); } // Master sample rate m_pMasterSampleRate = new ControlObject(ConfigKey(group, "samplerate"), true, true); m_pMasterSampleRate->set(44100.); // Latency control m_pMasterLatency = new ControlObject(ConfigKey(group, "latency"), true, true); m_pMasterAudioBufferSize = new ControlObject(ConfigKey(group, "audio_buffer_size")); m_pAudioLatencyOverloadCount = new ControlObject(ConfigKey(group, "audio_latency_overload_count"), true, true); m_pAudioLatencyUsage = new ControlPotmeter(ConfigKey(group, "audio_latency_usage"), 0.0, 0.25); m_pAudioLatencyOverload = new ControlPotmeter(ConfigKey(group, "audio_latency_overload"), 0.0, 1.0); // Master sync controller m_pMasterSync = new EngineSync(pConfig); // The last-used bpm value is saved in the destructor of EngineSync. double default_bpm = pConfig->getValue( ConfigKey("[InternalClock]", "bpm"), 124.0); ControlObject::getControl(ConfigKey("[InternalClock]","bpm"))->set(default_bpm); // Crossfader m_pCrossfader = new ControlPotmeter(ConfigKey(group, "crossfader"), -1., 1.); // Balance m_pBalance = new ControlPotmeter(ConfigKey(group, "balance"), -1., 1.); // Master gain m_pMasterGain = new ControlAudioTaperPot(ConfigKey(group, "gain"), -14, 14, 0.5); // Booth gain m_pBoothGain = new ControlAudioTaperPot(ConfigKey(group, "booth_gain"), -14, 14, 0.5); // Legacy: the master "gain" control used to be named "volume" in Mixxx // 1.11.0 and earlier. See Bug #1306253. ControlDoublePrivate::insertAlias(ConfigKey(group, "volume"), ConfigKey(group, "gain")); // VU meter: m_pVumeter = new EngineVuMeter(group); m_pMasterDelay = new EngineDelay(group, ConfigKey(group, "delay")); m_pHeadDelay = new EngineDelay(group, ConfigKey(group, "headDelay")); m_pBoothDelay = new EngineDelay(group, ConfigKey(group, "boothDelay")); m_pLatencyCompensationDelay = new EngineDelay(group, ConfigKey(group, "microphoneLatencyCompensation")); m_pNumMicsConfigured = new ControlObject(ConfigKey(group, "num_mics_configured")); // Headphone volume m_pHeadGain = new ControlAudioTaperPot(ConfigKey(group, "headGain"), -14, 14, 0.5); // Legacy: the headphone "headGain" control used to be named "headVolume" in // Mixxx 1.11.0 and earlier. See Bug #1306253. ControlDoublePrivate::insertAlias(ConfigKey(group, "headVolume"), ConfigKey(group, "headGain")); // Headphone mix (left/right) m_pHeadMix = new ControlPotmeter(ConfigKey(group, "headMix"),-1.,1.); m_pHeadMix->setDefaultValue(-1.); m_pHeadMix->set(-1.); // Master / Headphone split-out mode (for devices with only one output). m_pHeadSplitEnabled = new ControlPushButton(ConfigKey(group, "headSplit")); m_pHeadSplitEnabled->setButtonMode(ControlPushButton::TOGGLE); m_pHeadSplitEnabled->set(0.0); m_pTalkoverDucking = new EngineTalkoverDucking(pConfig, group); // Allocate buffers m_pHead = SampleUtil::alloc(MAX_BUFFER_LEN); m_pMaster = SampleUtil::alloc(MAX_BUFFER_LEN); m_pBooth = SampleUtil::alloc(MAX_BUFFER_LEN); m_pTalkover = SampleUtil::alloc(MAX_BUFFER_LEN); m_pTalkoverHeadphones = SampleUtil::alloc(MAX_BUFFER_LEN); m_pSidechainMix = SampleUtil::alloc(MAX_BUFFER_LEN); SampleUtil::clear(m_pHead, MAX_BUFFER_LEN); SampleUtil::clear(m_pMaster, MAX_BUFFER_LEN); SampleUtil::clear(m_pBooth, MAX_BUFFER_LEN); SampleUtil::clear(m_pTalkover, MAX_BUFFER_LEN); SampleUtil::clear(m_pTalkoverHeadphones, MAX_BUFFER_LEN); SampleUtil::clear(m_pSidechainMix, MAX_BUFFER_LEN); // Setup the output buses for (int o = EngineChannel::LEFT; o <= EngineChannel::RIGHT; ++o) { m_pOutputBusBuffers[o] = SampleUtil::alloc(MAX_BUFFER_LEN); SampleUtil::clear(m_pOutputBusBuffers[o], MAX_BUFFER_LEN); } m_ppSidechainOutput = &m_pMaster; // Starts a thread for recording and broadcast m_pEngineSideChain = bEnableSidechain ? new EngineSideChain(pConfig) : NULL; // X-Fader Setup m_pXFaderMode = new ControlPushButton( ConfigKey(EngineXfader::kXfaderConfigKey, "xFaderMode")); m_pXFaderMode->setButtonMode(ControlPushButton::TOGGLE); m_pXFaderCurve = new ControlPotmeter( ConfigKey(EngineXfader::kXfaderConfigKey, "xFaderCurve"), EngineXfader::kTransformMin, EngineXfader::kTransformMax); m_pXFaderCalibration = new ControlPotmeter( ConfigKey(EngineXfader::kXfaderConfigKey, "xFaderCalibration"), 0.3, 1., true); m_pXFaderReverse = new ControlPushButton( ConfigKey(EngineXfader::kXfaderConfigKey, "xFaderReverse")); m_pXFaderReverse->setButtonMode(ControlPushButton::TOGGLE); m_pKeylockEngine = new ControlObject(ConfigKey(group, "keylock_engine"), true, false, true); m_pKeylockEngine->set(pConfig->getValueString( ConfigKey(group, "keylock_engine")).toDouble()); // TODO: Make this read only and make EngineMaster decide whether // processing the master mix is necessary. m_pMasterEnabled = new ControlObject(ConfigKey(group, "enabled"), true, false, true); // persist = true m_pBoothEnabled = new ControlObject(ConfigKey(group, "booth_enabled")); m_pBoothEnabled->setReadOnly(); m_pMasterMonoMixdown = new ControlObject(ConfigKey(group, "mono_mixdown"), true, false, true); // persist = true m_pMicMonitorMode = new ControlObject(ConfigKey(group, "talkover_mix"), true, false, true); // persist = true m_pHeadphoneEnabled = new ControlObject(ConfigKey(group, "headEnabled")); m_pHeadphoneEnabled->setReadOnly(); // Note: the EQ Rack is set in EffectsManager::setupDefaults(); }
Library::Library(QObject* parent, UserSettingsPointer pConfig, PlayerManagerInterface* pPlayerManager, RecordingManager* pRecordingManager) : m_pConfig(pConfig), m_pSidebarModel(new SidebarModel(parent)), m_pTrackCollection(new TrackCollection(pConfig)), m_pLibraryControl(new LibraryControl(this)), m_pRecordingManager(pRecordingManager) { qRegisterMetaType<Library::RemovalType>("Library::RemovalType"); // TODO(rryan) -- turn this construction / adding of features into a static // method or something -- CreateDefaultLibrary m_pMixxxLibraryFeature = new MixxxLibraryFeature(this, m_pTrackCollection,m_pConfig); addFeature(m_pMixxxLibraryFeature); addFeature(new AutoDJFeature(this, pConfig, pPlayerManager, m_pTrackCollection)); m_pPlaylistFeature = new PlaylistFeature(this, m_pTrackCollection, m_pConfig); addFeature(m_pPlaylistFeature); m_pCrateFeature = new CrateFeature(this, m_pTrackCollection, m_pConfig); addFeature(m_pCrateFeature); BrowseFeature* browseFeature = new BrowseFeature( this, pConfig, m_pTrackCollection, m_pRecordingManager); connect(browseFeature, SIGNAL(scanLibrary()), parent, SLOT(slotScanLibrary())); connect(parent, SIGNAL(libraryScanStarted()), browseFeature, SLOT(slotLibraryScanStarted())); connect(parent, SIGNAL(libraryScanFinished()), browseFeature, SLOT(slotLibraryScanFinished())); addFeature(browseFeature); addFeature(new RecordingFeature(this, pConfig, m_pTrackCollection, m_pRecordingManager)); addFeature(new SetlogFeature(this, pConfig, m_pTrackCollection)); m_pAnalysisFeature = new AnalysisFeature(this, pConfig, m_pTrackCollection); connect(m_pPlaylistFeature, SIGNAL(analyzeTracks(QList<TrackId>)), m_pAnalysisFeature, SLOT(analyzeTracks(QList<TrackId>))); connect(m_pCrateFeature, SIGNAL(analyzeTracks(QList<TrackId>)), m_pAnalysisFeature, SLOT(analyzeTracks(QList<TrackId>))); addFeature(m_pAnalysisFeature); //iTunes and Rhythmbox should be last until we no longer have an obnoxious //messagebox popup when you select them. (This forces you to reach for your //mouse or keyboard if you're using MIDI control and you scroll through them...) if (RhythmboxFeature::isSupported() && pConfig->getValueString(ConfigKey("[Library]","ShowRhythmboxLibrary"),"1").toInt()) { addFeature(new RhythmboxFeature(this, m_pTrackCollection)); } if (pConfig->getValueString(ConfigKey("[Library]","ShowBansheeLibrary"),"1").toInt()) { BansheeFeature::prepareDbPath(pConfig); if (BansheeFeature::isSupported()) { addFeature(new BansheeFeature(this, m_pTrackCollection, pConfig)); } } if (ITunesFeature::isSupported() && pConfig->getValueString(ConfigKey("[Library]","ShowITunesLibrary"),"1").toInt()) { addFeature(new ITunesFeature(this, m_pTrackCollection)); } if (TraktorFeature::isSupported() && pConfig->getValueString(ConfigKey("[Library]","ShowTraktorLibrary"),"1").toInt()) { addFeature(new TraktorFeature(this, m_pTrackCollection)); } // On startup we need to check if all of the user's library folders are // accessible to us. If the user is using a database from <1.12.0 with // sandboxing then we will need them to give us permission. QStringList directories = m_pTrackCollection->getDirectoryDAO().getDirs(); qDebug() << "Checking for access to user's library directories:"; foreach (QString directoryPath, directories) { QFileInfo directory(directoryPath); bool hasAccess = Sandbox::askForAccess(directory.canonicalFilePath()); qDebug() << "Checking for access to" << directoryPath << ":" << hasAccess; }
Encoder::Format EncoderFactory::getSelectedFormat(UserSettingsPointer pConfig) const { return getFormatFor(pConfig->getValueString(ConfigKey(RECORDING_PREF_KEY, "Encoding"))); }