winamp/Src/Plugins/General/gen_ff/wa2core.cpp

931 lines
20 KiB
C++

#include <precomp.h>
#include "wa2core.h"
#include "wa2frontend.h"
#include "../winamp/wa_ipc.h"
#include "wa2pledit.h"
#include "../nu/AutoWide.h"
#include "gen.h"
#include "../nu/ns_wc.h"
#include "../Agave/Language/api_language.h"
#include "../Winamp/in2.h"
#include "resource.h"
#include "../nu/AutoChar.h"
#include <shlwapi.h>
// {72409F84-BAF1-4448-8211-D84A30A1591A}
static const GUID eqConfigGroupGUID =
{ 0x72409f84, 0xbaf1, 0x4448, { 0x82, 0x11, 0xd8, 0x4a, 0x30, 0xa1, 0x59, 0x1a } };
// -----------------------------------------------------------------------------------------------------------------
// Core implementation for wa2
// -----------------------------------------------------------------------------------------------------------------
#define TIMER_POLL 0x8971
Core *g_Core = NULL;
api_core *g_core = NULL;
int g_coreref = 0;
// this is called by the library when api->core_create is called
api_core *createCustomCoreApi()
{
g_coreref++;
if (g_core == NULL) { g_Core = new Core(); g_core = g_Core; }
return g_core;
}
// this is called when api->core_destroy is called
void destroyCustomCoreApi(api_core *core)
{
if (--g_coreref == 0)
{
delete g_Core;
g_Core = NULL;
g_core = NULL;
}
}
Core::Core()
{
waServiceFactory *sf = WASABI_API_SVC->service_getServiceByGuid(Agave::AgaveConfigGUID);
if (sf)
config = (Agave::api_config *)sf->getInterface();
m_lastpeentry = -1;
m_laststatus = -1;
m_lastpos = -1;
m_lastvol = -1;
m_lastpan = -1;
m_lasttitle = L"";
m_lastsamplerate = -1;
m_lastchan = -1;
m_lastbitrate = -1;
for (int i = 0;i < 10;i++)
m_lasteqband[i] = 0xdead;
m_lasteq = -1;
m_lasteqauto = -1;
m_lasteqpreamp = -1;
m_lastfreqband = -1;
// timerclient_setTimer(TIMER_POLL, 250);
gotCallback(IPC_CB_MISC_TITLE, 1);
gotCallback(IPC_CB_MISC_VOLUME, 1);
gotCallback(IPC_CB_MISC_STATUS, 1);
gotCallback(IPC_CB_MISC_EQ, 1);
gotCallback(IPC_CB_MISC_INFO, 1);
gotCallback(IPC_CB_MISC_TITLE_RATING, 1);
}
Core::~Core()
{
waServiceFactory *sf = WASABI_API_SVC->service_getServiceByGuid(Agave::AgaveConfigGUID);
if (sf)
sf->releaseInterface(config);
//timerclient_killTimer(TIMER_POLL);
}
void Core::addCallback(CoreCallback *cb)
{
if (cb == NULL) return ;
if (callbacks.haveItem(cb)) return ;
callbacks.addItem(cb);
cb->ccb_notify(CoreCallback::REGISTER);
}
void Core::delCallback(CoreCallback *cb)
{
if (cb == NULL) return ;
if (callbacks.haveItem(cb))
{
cb->ccb_notify(CoreCallback::DEREGISTER);
callbacks.removeItem(cb);
}
}
void Core::sendCoreCallback(int message, int param1, int param2)
{
ReentryFilter filter(&rf, message);
int l = callbacks.getNumItems();
for (int i = 0; i < l;i++)
callbacks[i]->ccb_notify(message, param1, param2);
}
int Core::getStatus()
{
return m_laststatus;
}
void Core::gotCallback(int wParam, int forcecb)
{
if(wParam == IPC_CB_MISC_TITLE_RATING){
forcecb = 1;
}
switch (wParam)
{
case IPC_CB_MISC_TITLE:
case IPC_CB_MISC_TITLE_RATING:
{
// check for title change
wa2.invalidateCache();
StringW cur_title=wa2.GetCurrentTitle();
if (cur_title.isempty())
cur_title = L"";
StringW cur_file = wa2.GetCurrentFile();
if (!m_lastfile.iscaseequal(cur_file) || forcecb)
{
m_lastfile.swap(cur_file);
sendCoreCallback(CoreCallback::URLCHANGE, (intptr_t)m_lastfile.getValue());
}
if (!m_lasttitle.iscaseequal(cur_title) || forcecb)
{
m_lasttitle.swap(cur_title);
//Martin> i dunno why we send a INFOCHANGE Callback here, but if we want to send this
// callback try to deliver the correct data
//sendCoreCallback((forcecb==2?CoreCallback::INFOCHANGE:CoreCallback::TITLECHANGE),
// (int)m_lasttitle.getValue(),(wParam == IPC_CB_MISC_TITLE_RATING));
if (forcecb==2) // this has led to INFOCHANGE Callback
{
sendCoreCallback(CoreCallback::INFOCHANGE, (intptr_t)getSongInfoText(), (wParam == IPC_CB_MISC_TITLE_RATING));
}
sendCoreCallback(CoreCallback::TITLECHANGE, (intptr_t)m_lasttitle.getValue(), (wParam == IPC_CB_MISC_TITLE_RATING));
}
int plentry = wa2.getCurPlaylistEntry();
int curpeentry = plentry + 1;
if (curpeentry != m_lastpeentry)
{
Wa2PlaylistEditor::_onNewCurrentIndex(curpeentry);
m_lastpeentry = curpeentry;
}
}
break;
case IPC_CB_MISC_STATUS:
{
int cur_status;
int resume = 0;
if (wa2.isPaused()) cur_status = STATUS_PAUSE;
else if (wa2.isPlaying())
{
if (m_laststatus == STATUS_PAUSE)
resume = 1;
else
{
m_lastsamplerate = -1;
m_lastbitrate = -1;
m_lastchan = -1;
}
cur_status = STATUS_PLAY;
}
else cur_status = STATUS_STOP;
if (m_laststatus != cur_status || forcecb)
{
m_laststatus = cur_status;
switch (cur_status)
{
case STATUS_PLAY: sendCoreCallback(resume ? CoreCallback::UNPAUSED : CoreCallback::STARTED); break;
case STATUS_STOP:
{
m_lastsamplerate = 0;
m_lastchan = 0;
m_lastbitrate = 0;
sendCoreCallback(CoreCallback::STOPPED);
break;
}
case STATUS_PAUSE: sendCoreCallback(CoreCallback::PAUSED); break;
}
}
}
break;
case IPC_CB_MISC_VOLUME:
{
// check for volume change
int cur_vol = wa2.getVolume();
if (m_lastvol != cur_vol || forcecb)
{
m_lastvol = cur_vol;
sendCoreCallback(CoreCallback::VOLCHANGE, cur_vol);
}
int cur_pan = wa2.getPanning();
if (m_lastpan != cur_pan || forcecb)
{
m_lastpan = cur_pan;
sendCoreCallback(CoreCallback::PANCHANGE, cur_pan);
}
}
break;
case IPC_CB_MISC_EQ:
{
// check if the band frequencies changed (winamp vs ISO)
int new_freqband = config->GetInt(eqConfigGroupGUID, L"frequencies", 0);
if (m_lastfreqband != new_freqband)
{
bool sendCallback = m_lastfreqband != -1;
m_lastfreqband = new_freqband; // set before the callback so we don't run into any issues if the callback triggers another EQ change message
if (sendCallback)
sendCoreCallback(CoreCallback::EQFREQCHANGE, new_freqband, 0);
}
// check for eq change
for (int i = 0;i < 10;i++)
{
int cur_band = wa2.getEqData(WA2_EQDATA_FIRSTBAND + i);
if (cur_band == 63 - ((m_lasteqband[i] + 127) / 4)) cur_band = m_lasteqband[i];
else cur_band = (63 - cur_band) * 4 - 127;
if (m_lasteqband[i] != cur_band || forcecb)
{
m_lasteqband[i] = cur_band;
sendCoreCallback(CoreCallback::EQBANDCHANGE, i, cur_band);
}
}
int cur_eq = wa2.getEqData(WA2_EQDATA_ENABLED);
if (m_lasteq != cur_eq)
{
m_lasteq = cur_eq;
sendCoreCallback(CoreCallback::EQSTATUSCHANGE, cur_eq);
}
int cur_eqauto = wa2.getEqData(WA2_EQDATA_AUTO);
if (m_lasteqauto != cur_eqauto || forcecb)
{
m_lasteqauto = cur_eqauto;
sendCoreCallback(CoreCallback::EQAUTOCHANGE, cur_eqauto);
}
int cur_eqpreamp = wa2.getEqData(WA2_EQDATA_PREAMP);
if (cur_eqpreamp == 63 - ((cur_eqpreamp + 127) / 4)) { /*cur_eqpreamp = cur_eqpreamp;*/ }
else cur_eqpreamp = (63 - cur_eqpreamp) * 4 - 127;
if (m_lasteqpreamp != cur_eqpreamp || forcecb)
{
m_lasteqpreamp = cur_eqpreamp;
sendCoreCallback(CoreCallback::EQPREAMPCHANGE, cur_eqpreamp);
}
}
break;
case IPC_CB_MISC_INFO:
{
int realrate = wa2.getSamplerate();
int brate = wa2.getBitrate();
int ch = wa2.getChannels();
int any = 0;
if (realrate != m_lastsamplerate || forcecb)
{
m_lastsamplerate = realrate;
sendCoreCallback(CoreCallback::SAMPLERATECHANGE, realrate);
any = 1;
}
if (brate != m_lastbitrate || forcecb)
{
m_lastbitrate = brate;
sendCoreCallback(CoreCallback::BITRATECHANGE, m_lastbitrate);
any = 1;
}
if (ch != m_lastchan || forcecb)
{
m_lastchan = ch;
sendCoreCallback(CoreCallback::CHANNELSCHANGE, m_lastchan);
any = 1;
}
//DebugString("Got IPC_CB_MISC_INFO callback, numchans = %d, samplerate = %d, bitrate = %d\n", m_lastchan, m_lastsamplerate, m_lastbitrate);
if (any)
{
StringW txt = getSongInfoText();
sendCoreCallback(CoreCallback::INFOCHANGE, (intptr_t)txt.getValue());
}
break;
}
}
}
StringW infotext;
const wchar_t *Core::getSongInfoText()
{
infotext = L"";
int srate = wa2.getSamplerate();
int brate = wa2.getBitrate();
int ch = wa2.getChannels();
if (srate == 0 && brate == 0)
{
return L"";
}
infotext = StringPrintfW(L"%dkbps", brate);
if (ch == 1)
{
infotext += L" mono";
}
else if (ch == 2)
{
infotext += L" stereo";
}
else if (ch > 2)
{
infotext += StringPrintfW(L" %d Channels", ch);
}
infotext += StringPrintfW(L" %.1fkHz", (float)srate/1000.0f);
if (wa2.isPlayingVideo())
{
infotext.prepend(L"Video ");
}
return infotext;
}
StringW infotextTranslated;
const wchar_t *Core::getSongInfoTextTranslated()
{
infotextTranslated = L"";
int srate = wa2.getSamplerate();
int brate = wa2.getBitrate();
int ch = wa2.getChannels();
if (srate == 0 && brate == 0)
{
return L"";
}
infotextTranslated = StringPrintfW(L"%d%s", brate,WASABI_API_LNGSTRINGW(IDS_KBPS));
if (ch == 1)
{
infotextTranslated += WASABI_API_LNGSTRINGW(IDS_MONO);
}
else if (ch == 2)
{
infotextTranslated += WASABI_API_LNGSTRINGW(IDS_STEREO);
}
else if (ch > 2)
{
infotextTranslated += StringPrintfW(WASABI_API_LNGSTRINGW(IDS_X_CHANNELS), ch);
}
infotextTranslated += StringPrintfW(L" %.1f", (float)srate/1000.0f);
infotextTranslated += WASABI_API_LNGSTRINGW(IDS_KHZ);// (doing this in the StringPrintfW(..) above causes a crash even in a seperate buffer)
if (wa2.isPlayingVideo())
{
infotextTranslated.prepend(StringPrintfW(L"%s ",WASABI_API_LNGSTRINGW(IDS_VIDEO)));
}
return infotextTranslated;
}
void Core::userButton(int button)
{
int mod = Std::keyDown(VK_SHIFT) ? WA2_USERBUTTONMOD_SHIFT : (Std::keyDown(VK_SHIFT) ? WA2_USERBUTTONMOD_CTRL : WA2_USERBUTTONMOD_NONE);
switch (button)
{
case UserButton::PLAY: wa2.userButton(WA2_USERBUTTON_PLAY, mod); break;
case UserButton::STOP: wa2.userButton(WA2_USERBUTTON_STOP, mod); break;
case UserButton::PAUSE: wa2.userButton(WA2_USERBUTTON_PAUSE, mod); break;
case UserButton::NEXT: wa2.userButton(WA2_USERBUTTON_NEXT, mod); break;
case UserButton::PREV: wa2.userButton(WA2_USERBUTTON_PREV, mod); break;
}
}
void Core::setVolume(int vol)
{
wa2.setVolume(vol);
}
int Core::getVolume()
{
return wa2.getVolume();
}
void Core::setPosition(int ms)
{
wa2.seekTo(ms);
sendCoreCallback(CoreCallback::SEEKED, ms);
}
int Core::getPosition()
{
if (m_laststatus == STATUS_STOP) return -1;
return wa2.getPosition();
}
int Core::getLength()
{
if (m_laststatus == STATUS_STOP) return -1;
return wa2.getLength();
}
void Core::setPanning(int p)
{
if (p > 127) p = 127;
wa2.setPanning(p);
}
int Core::getPanning()
{
return wa2.getPanning();
}
void Core::setShuffle(int shuffle)
{
wa2.setShuffle(shuffle);
}
int Core::getShuffle()
{
return wa2.getShuffle();
}
void Core::setRepeat(int repeat)
{
static int myset = 0;
if (!myset)
{
myset = 1;
int manadv = 0;
int rep = 0;
if (repeat == 0)
{
rep = 0;
manadv = 0;
}
else if (repeat > 0)
{
rep = 1;
manadv = 0;
}
else if (repeat < 0)
{
rep = 1;
manadv = 1;
}
if (!!wa2.getRepeat() != !!rep) wa2.setRepeat(rep);
if (!!wa2.getManualPlaylistAdvance() != !!manadv) wa2.setManualPlaylistAdvance(manadv);
myset = 0;
}
}
int Core::getRepeat()
{
int manadv = wa2.getManualPlaylistAdvance();
int rep = wa2.getRepeat();
int _v = (rep && manadv) ? -1 : rep;
return _v;
}
int Core::getSamplerate(int wa2_getinfo)
{
return wa2.getInfo(WA2_GETINFO_SAMPLERATE);
}
int Core::getBitrate(int wa2_getinfo)
{
return wa2.getInfo(WA2_GETINFO_BITRATE);
}
int Core::getChannels(int wa2_getinfo)
{
return wa2.getInfo(WA2_GETINFO_CHANNELS);
}
int Core::getEqBand(int band)
{
// int v = 63-wa2.getEqData(WA2_EQDATA_FIRSTBAND+band) * 4 - 127;
// v = MIN(-127, v);
// v = MAX(127, v);
return m_lasteqband[band]; //(v);
}
void Core::setEqBand(int band, int val)
{
val = MIN(127, MAX( -127, val));
m_lasteqband[band] = val;
int v = 63 - ((val + 127) / 4);
v = MIN(63, v);
v = MAX(0, v);
wa2.setEqData(WA2_EQDATA_FIRSTBAND + band, v);
sendCoreCallback(CoreCallback::EQBANDCHANGE, band, val);
}
int Core::getEQStatus()
{
return wa2.getEqData(WA2_EQDATA_ENABLED);
}
void Core::setEQStatus(int enable)
{
wa2.setEqData(WA2_EQDATA_ENABLED, enable);
}
int Core::getEQAuto()
{
return wa2.getEqData(WA2_EQDATA_AUTO);
}
void Core::setEQAuto(int enable)
{
wa2.setEqData(WA2_EQDATA_AUTO, enable);
}
int Core::getEQPreamp()
{
// int v = 63-wa2.getEqData(WA2_EQDATA_PREAMP) * 4 - 127;
// v = MIN(-127, v);
// v = MAX(127, v);
return m_lasteqpreamp;
}
void Core::setEQPreamp(int val)
{
val = MIN(127, MAX( -127, val));
m_lasteqpreamp = val;
int v = 63 - ((val + 127) / 4);
v = MIN(63, v);
v = MAX(0, v);
wa2.setEqData(WA2_EQDATA_PREAMP, v);
sendCoreCallback(CoreCallback::EQPREAMPCHANGE, val);
}
const wchar_t *Core::getTitle()
{
return m_lasttitle; // faster to use our cached ver
// int plentry = wa2.getCurPlaylistEntry();
// if (plentry == -1) return NULL;
// return wa2.getTitle(plentry);
}
void Core::setTitle(const wchar_t * new_title)
{
StringW title = new_title;
if(title.isempty())
title = L"";
m_lasttitle = title;
}
const wchar_t *Core::getPlaystring()
{
m_playstring = wa2.GetCurrentFile();
if (m_playstring.getValue() == NULL) m_playstring = L"";
return m_playstring;
}
int Core::getCurPlaylistEntry()
{
return wa2.getCurPlaylistEntry();
}
// -----------------------------------------------------------------------------------------------------------------
// api_core implementation
// -----------------------------------------------------------------------------------------------------------------
// this pointer is set automagically by ApiInit
api_core *coreApi = NULL;
const wchar_t *Core::core_getSupportedExtensions()
{
return L"mp3\x0";
}
const wchar_t *Core::core_getExtSupportedExtensions()
{
return L"mp3\x0";
}
CoreToken Core::core_create()
{
return 0;
}
int Core::core_free(CoreToken core)
{
return 0;
}
int Core::core_setNextFile(CoreToken core, const wchar_t *playstring)
{
return 0;
}
int Core::core_getStatus(CoreToken core)
{
switch (getStatus())
{
case STATUS_STOP : return 0;
case STATUS_PLAY : return 1;
case STATUS_PAUSE : return -1;
}
return 0; // dunno, stopped i guess...
}
const wchar_t *Core::core_getCurrent(CoreToken core)
{
return getPlaystring();
}
int Core::core_getCurPlaybackNumber(CoreToken core)
{
return getCurPlaylistEntry();
}
int Core::core_getPosition(CoreToken core)
{
return getPosition();
}
int Core::core_getWritePosition(CoreToken core)
{
return getPosition();
}
int Core::core_setPosition(CoreToken core, int ms)
{
setPosition(ms);
return 1;
}
int Core::core_getLength(CoreToken core)
{
return getLength();
}
int Core::core_getPluginData(const wchar_t *playstring, const wchar_t *name, wchar_t *data, int data_len, int data_type)
{
return 0;
}
unsigned int Core::core_getVolume(CoreToken core)
{
return getVolume();
}
void Core::core_setVolume(CoreToken core, unsigned int vol)
{
setVolume(vol);
}
int Core::core_getPan(CoreToken core)
{
return getPanning();
}
void Core::core_setPan(CoreToken core, int val)
{
setPanning(val);
}
void Core::core_addCallback(CoreToken core, CoreCallback *cb)
{
addCallback(cb);
}
void Core::core_delCallback(CoreToken core, CoreCallback *cb)
{
delCallback(cb);
}
int Core::core_getVisData(CoreToken core, void *dataptr, int sizedataptr)
{
int ret = 75*2;
// added this to attempt to cope with some poor / old input plug-ins
// which keep cropping up in the plug-in crash reports from users...
try
{
// todo
if (wa2.export_sa_get)
{
if (sizedataptr >= (75*2 + 8))
wa2.export_sa_get((char *)dataptr);
else
{
char data[75*2 + 8];
char *p = wa2.export_sa_get(data);
if (p) memcpy(dataptr, p, min(2*75, sizedataptr));
}
}
else if (wa2.export_sa_get_deprecated)
{
char *p = wa2.export_sa_get_deprecated();
if (p) memcpy(dataptr, p, min(2*75, sizedataptr));
}
}
catch(...)
{
ret = 0;
}
return ret;
}
int Core::core_getLeftVuMeter(CoreToken core)
{
if (wa2.export_vu_get)
{
int vu = wa2.export_vu_get(0);
if (vu != -1)
return vu;
}
if (wa2.export_sa_get)
{
char data[75*2 + 8] = {0};
char *p = (char *)wa2.export_sa_get(data);
if (!p) return 0;
int m = 0;
for (int i = 75;i < 150;i++) // this is very gay but workish
m = max(abs(m), p[i]);
return MIN(255, m*16);
}
else if (wa2.export_sa_get_deprecated)
{
char *p = (char *)wa2.export_sa_get_deprecated();
if (!p) return 0;
int m = 0;
for (int i = 75;i < 150;i++) // this is very gay but workish
m = max(abs(m), p[i]);
return MIN(255, m*16);
}
else return 0;
}
int Core::core_getRightVuMeter(CoreToken core)
{
if (wa2.export_vu_get)
{
int vu = wa2.export_vu_get(1);
if (vu == -1)
vu = wa2.export_vu_get(0);
if (vu != -1)
return vu;
}
return core_getLeftVuMeter(core);
}
int Core::core_registerSequencer(CoreToken core, ItemSequencer *seq)
{
return 0;
}
int Core::core_deregisterSequencer(CoreToken core, ItemSequencer *seq)
{
return 0;
}
void Core::core_userButton(CoreToken core, int button)
{
userButton(button);
}
int Core::core_getEqStatus(CoreToken core)
{
return getEQStatus();
}
void Core::core_setEqStatus(CoreToken core, int enable)
{
setEQStatus(enable);
}
int Core::core_getEqPreamp(CoreToken core)
{
return getEQPreamp();
}
void Core::core_setEqPreamp(CoreToken core, int pre)
{
setEQPreamp(pre);
}
int Core::core_getEqBand(CoreToken core, int band)
{
return getEqBand(band);
}
void Core::core_setEqBand(CoreToken core, int band, int val)
{
setEqBand(band, val);
}
int Core::core_getEqAuto(CoreToken core)
{
return getEQAuto();
}
void Core::core_setEqAuto(CoreToken core, int enable)
{
setEQAuto(enable);
}
void Core::core_setCustomMsg(CoreToken core, const wchar_t *text)
{}
void Core::core_registerExtension(const wchar_t *extensions, const wchar_t *extension_name, const wchar_t *family)
{}
const wchar_t *Core::core_getDecoderName(const wchar_t *filename)
{
wchar_t fn[MAX_PATH+3] = {0};
WCSNPRINTF(fn, MAX_PATH, L"hi.%s", filename);
In_Module *player = (In_Module *)wa2.CanPlay(fn);
if (player)
{
// cope nicely with the 5.64+ input plug-ins with unicode descriptions
static wchar_t decoder_name[512];
int ver = ((player->version & ~IN_UNICODE) & ~IN_INIT_RET);
if (ver == IN_VER)
{
StringCchCopyW(decoder_name, 512, (wchar_t *)player->description);
}
else
{
MultiByteToWideCharSZ(CP_ACP, 0, player->description, -1, decoder_name, 512);
}
return decoder_name;
}
return NULL;
}
const wchar_t *Core::core_getExtensionFamily(const wchar_t *extension)
{
wchar_t fn[MAX_PATH] = {0};
WCSNPRINTF(fn, MAX_PATH, L"hi.%s", extension);
In_Module *player = (In_Module *)wa2.CanPlay(fn);
if (player)
{
/*
char *p = player->FileExtensions;
const wchar_t *realExt = PathFindExtensionW(fn);
if (realExt && *realExt)
realExt++;
AutoChar ext(realExt);
while (p && *p)
{
char *b = p;
char *c;
do
{
char d[20] = {0};
lstrcpyn(d, b, 15);
if ((c = strstr(b, ";")))
{
if ((c-b)<15)
d[c - b] = 0;
}
if (!_stricmp(ext, d))
{
p += lstrlen(p) + 1; // skip to the name
MultiByteToWideCharSZ(CP_ACP, 0, p, -1, family, 512);
return family;
}
b = c + 1;
}
while (c);
p += lstrlen(p) + 1;
if (!*p) break;
p += lstrlen(p) + 1;
}
*/
static wchar_t family[512];
MultiByteToWideCharSZ(CP_ACP, 0, player->description, -1, family, 512);
return family;
}
return NULL;
}
void Core::core_unregisterExtension(const wchar_t *extensions)
{}
const wchar_t *Core::core_getTitle(CoreToken core)
{
return getTitle();
}
void Core::core_setTitle(const wchar_t *new_title)
{
setTitle(new_title);
}
int Core::core_getRating()
{
return wa2.getCurTrackRating();
}
void Core::core_setRating(int newRating)
{
wa2.setCurTrackRating(newRating);
}