Mercurial > audlegacy-plugins
view src/modplug/modplugbmp.cxx @ 972:cf7021ca4e7b trunk
[svn] Add lastfm:// transport, an abstract VFS class which derives from curl
to provide lastfm radio support. Written by majeru with some cleanups
by me. Most last.fm metadata support isn't yet implemented, however, and
will need to be done by majeru. ;)
| author | nenolod |
|---|---|
| date | Sun, 22 Apr 2007 04:16:08 -0700 |
| parents | 94b22cc75eb8 |
| children | 833a7d4ea063 |
line wrap: on
line source
/* Modplug XMMS Plugin * Authors: Kenton Varda <temporal@gauge3d.org> * * This source code is public domain. */ #include <fstream> #include <unistd.h> #include <math.h> #include "modplugbmp.h" #include "stdafx.h" #include "sndfile.h" #include "stddefs.h" #include "archive/open.h" #include "audacious/configdb.h" #include "audacious/vfs.h" extern "C" { #include "audacious/output.h" } // ModplugXMMS member functions =============================== // operations ---------------------------------------- ModplugXMMS::ModplugXMMS() { mSoundFile = new CSoundFile; } ModplugXMMS::~ModplugXMMS() { delete mSoundFile; } ModplugXMMS::Settings::Settings() { mSurround = true; mOversamp = true; mReverb = false; mMegabass = false; mNoiseReduction = true; mVolumeRamp = true; mFastinfo = true; mUseFilename = false; mGrabAmigaMOD = true; mChannels = 2; mFrequency = 44100; mBits = 16; mResamplingMode = SRCMODE_POLYPHASE; mReverbDepth = 30; mReverbDelay = 100; mBassAmount = 40; mBassRange = 30; mSurroundDepth = 20; mSurroundDelay = 20; mPreamp = false; mPreampLevel = 0.0f; mLoopCount = 0; //don't loop } void ModplugXMMS::Init(void) { ConfigDb *db; db = bmp_cfg_db_open(); bmp_cfg_db_get_bool(db,"modplug","Surround", &mModProps.mSurround); bmp_cfg_db_get_bool(db,"modplug","Oversampling", &mModProps.mOversamp); bmp_cfg_db_get_bool(db,"modplug","Megabass", &mModProps.mMegabass); bmp_cfg_db_get_bool(db,"modplug","NoiseReduction", &mModProps.mNoiseReduction); bmp_cfg_db_get_bool(db,"modplug","VolumeRamp", &mModProps.mVolumeRamp); bmp_cfg_db_get_bool(db,"modplug","Reverb", &mModProps.mReverb); bmp_cfg_db_get_bool(db,"modplug","FastInfo", &mModProps.mFastinfo); bmp_cfg_db_get_bool(db,"modplug","UseFileName", &mModProps.mUseFilename); bmp_cfg_db_get_bool(db,"modplug","GrabAmigaMOD", &mModProps.mGrabAmigaMOD); bmp_cfg_db_get_bool(db,"modplug","PreAmp", &mModProps.mPreamp); bmp_cfg_db_get_float(db,"modplug","PreAmpLevel", &mModProps.mPreampLevel); bmp_cfg_db_get_int(db,"modplug", "Channels", &mModProps.mChannels); bmp_cfg_db_get_int(db,"modplug", "Bits", &mModProps.mBits); bmp_cfg_db_get_int(db,"modplug", "Frequency", &mModProps.mFrequency); bmp_cfg_db_get_int(db,"modplug", "ResamplineMode", &mModProps.mResamplingMode); bmp_cfg_db_get_int(db,"modplug", "ReverbDepth", &mModProps.mReverbDepth); bmp_cfg_db_get_int(db,"modplug", "ReverbDelay", &mModProps.mReverbDelay); bmp_cfg_db_get_int(db,"modplug", "BassAmount", &mModProps.mBassAmount); bmp_cfg_db_get_int(db,"modplug", "BassRange", &mModProps.mBassRange); bmp_cfg_db_get_int(db,"modplug", "SurroundDepth", &mModProps.mSurroundDepth); bmp_cfg_db_get_int(db,"modplug", "SurroundDelay", &mModProps.mSurroundDelay); bmp_cfg_db_get_int(db,"modplug", "LoopCount", &mModProps.mLoopCount); bmp_cfg_db_close(db); } bool ModplugXMMS::CanPlayFileFromVFS(const string& aFilename, VFSFile *file) { string lExt; uint32 lPos; gchar magic[4]; vfs_fread(magic, 1, 4, file); if (!memcmp(magic, UMX_MAGIC, 4)) return true; if (!memcmp(magic, XM_MAGIC, 4)) return true; if (!memcmp(magic, M669_MAGIC, 2)) return true; if (!memcmp(magic, IT_MAGIC, 4)) return true; if (!memcmp(magic, MTM_MAGIC, 4)) return true; if (!memcmp(magic, PSM_MAGIC, 4)) return true; vfs_fseek(file, 44, SEEK_SET); vfs_fread(magic, 1, 4, file); if (!memcmp(magic, S3M_MAGIC, 4)) return true; vfs_fseek(file, 1080, SEEK_SET); vfs_fread(magic, 1, 4, file); if (!memcmp(magic, MOD_MAGIC_FASTTRACKER6, 4)) return true; if (!memcmp(magic, MOD_MAGIC_FASTTRACKER8, 4)) return true; if(mModProps.mGrabAmigaMOD) { if (!memcmp(magic, MOD_MAGIC_PROTRACKER4, 4)) return true; if (!memcmp(magic, MOD_MAGIC_PROTRACKER4X, 4)) return true; if (!memcmp(magic, MOD_MAGIC_NOISETRACKER, 4)) return true; if (!memcmp(magic, MOD_MAGIC_STARTRACKER4, 4)) return true; if (!memcmp(magic, MOD_MAGIC_STARTRACKER8, 4)) return true; if (!memcmp(magic, MOD_MAGIC_STARTRACKER4X, 4)) return true; if (!memcmp(magic, MOD_MAGIC_STARTRACKER8X, 4)) return true; if (!memcmp(magic, MOD_MAGIC_FASTTRACKER4, 4)) return true; if (!memcmp(magic, MOD_MAGIC_OKTALYZER8, 4)) return true; if (!memcmp(magic, MOD_MAGIC_OKTALYZER8X, 4)) return true; if (!memcmp(magic, MOD_MAGIC_TAKETRACKER16, 4)) return true; if (!memcmp(magic, MOD_MAGIC_TAKETRACKER32, 4)) return true; } /* end of if(mModProps.mGrabAmigaMOD) */ /* We didn't find the magic bytes, fall back to extension check */ lPos = aFilename.find_last_of('.'); if((int)lPos == -1) return false; lExt = aFilename.substr(lPos); for(uint32 i = 0; i < lExt.length(); i++) lExt[i] = tolower(lExt[i]); if (lExt == ".amf") return true; if (lExt == ".ams") return true; if (lExt == ".dbm") return true; if (lExt == ".dbf") return true; if (lExt == ".dsm") return true; if (lExt == ".far") return true; if (lExt == ".mdl") return true; if (lExt == ".stm") return true; if (lExt == ".ult") return true; if (lExt == ".j2b") return true; if (lExt == ".mt2") return true; if (lExt == ".mdz") return true; if (lExt == ".mdr") return true; if (lExt == ".mdgz") return true; if (lExt == ".mdbz") return true; if (lExt == ".s3z") return true; if (lExt == ".s3r") return true; if (lExt == ".s3gz") return true; if (lExt == ".xmz") return true; if (lExt == ".xmr") return true; if (lExt == ".xmgz") return true; if (lExt == ".itz") return true; if (lExt == ".itr") return true; if (lExt == ".itgz") return true; if (lExt == ".dmf") return true; if (lExt == ".zip") return ContainsMod(aFilename); if (lExt == ".gz") return ContainsMod(aFilename); if (lExt == ".bz2") return ContainsMod(aFilename); return false; } void* ModplugXMMS::PlayThread(void* arg) { ((ModplugXMMS*)arg)->PlayLoop(); return NULL; } void ModplugXMMS::PlayLoop() { uint32 lLength; //the user might change the number of channels while playing. // we don't want this to take effect until we are done! uint8 lChannels = mModProps.mChannels; while(!mStopped) { if(!(lLength = mSoundFile->Read( mBuffer, mBufSize))) { //no more to play. Wait for output to finish and then stop. while((mOutPlug->buffer_playing()) && (!mStopped)) usleep(10000); break; } if(mModProps.mPreamp) { //apply preamp if(mModProps.mBits == 16) { uint n = mBufSize >> 1; for(uint i = 0; i < n; i++) { short old = ((short*)mBuffer)[i]; ((short*)mBuffer)[i] *= (short int)mPreampFactor; // detect overflow and clip! if ((old & 0x8000) != (((short*)mBuffer)[i] & 0x8000)) ((short*)mBuffer)[i] = old | 0x7FFF; } } else { for(uint i = 0; i < mBufSize; i++) { uchar old = ((uchar*)mBuffer)[i]; ((uchar*)mBuffer)[i] *= (short int)mPreampFactor; // detect overflow and clip! if ((old & 0x80) != (((uchar*)mBuffer)[i] & 0x80)) ((uchar*)mBuffer)[i] = old | 0x7F; } } } if(mStopped) break; //wait for buffer space to free up. while(((mOutPlug->buffer_free() < (int)mBufSize)) && (!mStopped)) usleep(10000); if(mStopped) break; produce_audio ( mPlayed, mFormat, lChannels, mBufSize, mBuffer, NULL ); mPlayed += mBufTime; } // mOutPlug->flush(0); mOutPlug->close_audio(); //Unload the file mSoundFile->Destroy(); delete mArchive; if (mBuffer) { delete [] mBuffer; mBuffer = NULL; } mPaused = false; mStopped = true; g_thread_exit(NULL); } void ModplugXMMS::PlayFile(const string& aFilename) { mStopped = true; mPaused = false; //open and mmap the file mArchive = OpenArchive(aFilename); if(mArchive->Size() == 0) { delete mArchive; return; } if (mBuffer) delete [] mBuffer; //find buftime to get approx. 512 samples/block mBufTime = 512000 / mModProps.mFrequency + 1; mBufSize = mBufTime; mBufSize *= mModProps.mFrequency; mBufSize /= 1000; //milliseconds mBufSize *= mModProps.mChannels; mBufSize *= mModProps.mBits / 8; mBuffer = new uchar[mBufSize]; if(!mBuffer) return; //out of memory! CSoundFile::SetWaveConfig ( mModProps.mFrequency, mModProps.mBits, mModProps.mChannels ); CSoundFile::SetWaveConfigEx ( mModProps.mSurround, !mModProps.mOversamp, mModProps.mReverb, true, mModProps.mMegabass, mModProps.mNoiseReduction, false ); // [Reverb level 0(quiet)-100(loud)], [delay in ms, usually 40-200ms] if(mModProps.mReverb) { CSoundFile::SetReverbParameters ( mModProps.mReverbDepth, mModProps.mReverbDelay ); } // [XBass level 0(quiet)-100(loud)], [cutoff in Hz 10-100] if(mModProps.mMegabass) { CSoundFile::SetXBassParameters ( mModProps.mBassAmount, mModProps.mBassRange ); } // [Surround level 0(quiet)-100(heavy)] [delay in ms, usually 5-40ms] if(mModProps.mSurround) { CSoundFile::SetSurroundParameters ( mModProps.mSurroundDepth, mModProps.mSurroundDelay ); } CSoundFile::SetResamplingMode(mModProps.mResamplingMode); mSoundFile->SetRepeatCount(mModProps.mLoopCount); mPreampFactor = exp(mModProps.mPreampLevel); mPaused = false; mStopped = false; mSoundFile->Create ( (uchar*)mArchive->Map(), mArchive->Size() ); mPlayed = 0; bool useFilename = mModProps.mUseFilename; if(!useFilename) { strncpy(mModName, mSoundFile->GetTitle(), 100); for(int i = 0; mModName[i] == ' ' || mModName[i] == 0; i++) { if(mModName[i] == 0) { useFilename = true; //mod name is blank -- use filename break; } } } if(useFilename) { strncpy(mModName, strrchr(aFilename.c_str(), '/') + 1, 100); char* ext = strrchr(mModName, '.'); if(ext) *ext = '\0'; } mInPlug->set_info ( mModName, mSoundFile->GetSongTime() * 1000, mSoundFile->GetNumChannels() * 1000, mModProps.mFrequency, mModProps.mChannels ); mStopped = mPaused = false; if(mModProps.mBits == 16) mFormat = FMT_S16_NE; else mFormat = FMT_U8; mOutPlug->open_audio ( mFormat, mModProps.mFrequency, mModProps.mChannels ); mDecodeThread = g_thread_create( (GThreadFunc)PlayThread, this, TRUE, NULL ); } void ModplugXMMS::Stop(void) { if(mStopped) return; mStopped = true; mPaused = false; g_thread_join(mDecodeThread); } void ModplugXMMS::Pause(bool aPaused) { if(aPaused) mPaused = true; else mPaused = false; mOutPlug->pause(aPaused); } void ModplugXMMS::Seek(float32 aTime) { uint32 lMax; uint32 lMaxtime; float32 lPostime; if(aTime > (lMaxtime = mSoundFile->GetSongTime())) aTime = lMaxtime; lMax = mSoundFile->GetMaxPosition(); lPostime = float(lMax) / lMaxtime; mSoundFile->SetCurrentPos(int(aTime * lPostime)); mOutPlug->flush(int(aTime * 1000)); mPlayed = uint32(aTime * 1000); } float32 ModplugXMMS::GetTime(void) { if(mStopped) return -1; return (float32)mOutPlug->output_time() / 1000; } void ModplugXMMS::GetSongInfo(const string& aFilename, char*& aTitle, int32& aLength) { aLength = -1; fstream lTestFile; string lError; bool lDone; lTestFile.open(aFilename.c_str(), ios::in); if(!lTestFile) { lError = "**no such file: "; lError += strrchr(aFilename.c_str(), '/') + 1; aTitle = new char[lError.length() + 1]; strcpy(aTitle, lError.c_str()); return; } lTestFile.close(); if(mModProps.mFastinfo) { if(mModProps.mUseFilename) { //Use filename as name aTitle = new char[aFilename.length() + 1]; strcpy(aTitle, strrchr(aFilename.c_str(), '/') + 1); *strrchr(aTitle, '.') = '\0'; return; } fstream lModFile; string lExt; uint32 lPos; lDone = true; // previously ios::nocreate was used (X Standard C++ Library) lModFile.open(aFilename.c_str(), ios::in); lPos = aFilename.find_last_of('.'); if((int)lPos == 0) return; lExt = aFilename.substr(lPos); for(uint32 i = 0; i < lExt.length(); i++) lExt[i] = tolower(lExt[i]); if (lExt == ".mod") { lModFile.read(mModName, 20); mModName[20] = 0; } else if (lExt == ".s3m") { lModFile.read(mModName, 28); mModName[28] = 0; } else if (lExt == ".xm") { lModFile.seekg(17); lModFile.read(mModName, 20); mModName[20] = 0; } else if (lExt == ".it") { lModFile.seekg(4); lModFile.read(mModName, 28); mModName[28] = 0; } else lDone = false; //fall back to slow info lModFile.close(); if(lDone) { for(int i = 0; mModName[i] != 0; i++) { if(mModName[i] != ' ') { aTitle = new char[strlen(mModName) + 1]; strcpy(aTitle, mModName); return; } } //mod name is blank. Use filename instead. aTitle = new char[aFilename.length() + 1]; strcpy(aTitle, strrchr(aFilename.c_str(), '/') + 1); *strrchr(aTitle, '.') = '\0'; return; } } Archive* lArchive; CSoundFile* lSoundFile; const char* lTitle; //open and mmap the file lArchive = OpenArchive(aFilename); if(lArchive->Size() == 0) { lError = "**bad mod file: "; lError += strrchr(aFilename.c_str(), '/') + 1; aTitle = new char[lError.length() + 1]; strcpy(aTitle, lError.c_str()); delete lArchive; return; } lSoundFile = new CSoundFile; lSoundFile->Create((uchar*)lArchive->Map(), lArchive->Size()); if(!mModProps.mUseFilename) { lTitle = lSoundFile->GetTitle(); for(int i = 0; lTitle[i] != 0; i++) { if(lTitle[i] != ' ') { aTitle = new char[strlen(lTitle) + 1]; strcpy(aTitle, lTitle); goto therest; //sorry } } } //mod name is blank, or user wants the filename to be used as the title. aTitle = new char[aFilename.length() + 1]; strcpy(aTitle, strrchr(aFilename.c_str(), '/') + 1); *strrchr(aTitle, '.') = '\0'; therest: aLength = lSoundFile->GetSongTime() * 1000; //It wants milliseconds!?! //unload the file lSoundFile->Destroy(); delete lSoundFile; delete lArchive; } void ModplugXMMS::SetInputPlugin(InputPlugin& aInPlugin) { mInPlug = &aInPlugin; } void ModplugXMMS::SetOutputPlugin(OutputPlugin& aOutPlugin) { mOutPlug = &aOutPlugin; } const ModplugXMMS::Settings& ModplugXMMS::GetModProps() { return mModProps; } const char* ModplugXMMS::Bool2OnOff(bool aValue) { if(aValue) return "on"; else return "off"; } void ModplugXMMS::SetModProps(const Settings& aModProps) { ConfigDb *db; mModProps = aModProps; // [Reverb level 0(quiet)-100(loud)], [delay in ms, usually 40-200ms] if(mModProps.mReverb) { CSoundFile::SetReverbParameters ( mModProps.mReverbDepth, mModProps.mReverbDelay ); } // [XBass level 0(quiet)-100(loud)], [cutoff in Hz 10-100] if(mModProps.mMegabass) { CSoundFile::SetXBassParameters ( mModProps.mBassAmount, mModProps.mBassRange ); } else //modplug seems to ignore the SetWaveConfigEx() setting for bass boost { CSoundFile::SetXBassParameters ( 0, 0 ); } // [Surround level 0(quiet)-100(heavy)] [delay in ms, usually 5-40ms] if(mModProps.mSurround) { CSoundFile::SetSurroundParameters ( mModProps.mSurroundDepth, mModProps.mSurroundDelay ); } CSoundFile::SetWaveConfigEx ( mModProps.mSurround, !mModProps.mOversamp, mModProps.mReverb, true, mModProps.mMegabass, mModProps.mNoiseReduction, false ); CSoundFile::SetResamplingMode(mModProps.mResamplingMode); mPreampFactor = exp(mModProps.mPreampLevel); db = bmp_cfg_db_open(); bmp_cfg_db_set_bool(db,"modplug","Surround", mModProps.mSurround); bmp_cfg_db_set_bool(db,"modplug","Oversampling", mModProps.mOversamp); bmp_cfg_db_set_bool(db,"modplug","Megabass", mModProps.mMegabass); bmp_cfg_db_set_bool(db,"modplug","NoiseReduction", mModProps.mNoiseReduction); bmp_cfg_db_set_bool(db,"modplug","VolumeRamp", mModProps.mVolumeRamp); bmp_cfg_db_set_bool(db,"modplug","Reverb", mModProps.mReverb); bmp_cfg_db_set_bool(db,"modplug","FastInfo", mModProps.mFastinfo); bmp_cfg_db_set_bool(db,"modplug","UseFileName", mModProps.mUseFilename); bmp_cfg_db_set_bool(db,"modplug","GrabAmigaMOD", mModProps.mGrabAmigaMOD); bmp_cfg_db_set_bool(db,"modplug","PreAmp", mModProps.mPreamp); bmp_cfg_db_set_float(db,"modplug","PreAmpLevel", mModProps.mPreampLevel); bmp_cfg_db_set_int(db,"modplug", "Channels", mModProps.mChannels); bmp_cfg_db_set_int(db,"modplug", "Bits", mModProps.mBits); bmp_cfg_db_set_int(db,"modplug", "Frequency", mModProps.mFrequency); bmp_cfg_db_set_int(db,"modplug", "ResamplineMode", mModProps.mResamplingMode); bmp_cfg_db_set_int(db,"modplug", "ReverbDepth", mModProps.mReverbDepth); bmp_cfg_db_set_int(db,"modplug", "ReverbDelay", mModProps.mReverbDelay); bmp_cfg_db_set_int(db,"modplug", "BassAmount", mModProps.mBassAmount); bmp_cfg_db_set_int(db,"modplug", "BassRange", mModProps.mBassRange); bmp_cfg_db_set_int(db,"modplug", "SurroundDepth", mModProps.mSurroundDepth); bmp_cfg_db_set_int(db,"modplug", "SurroundDelay", mModProps.mSurroundDelay); bmp_cfg_db_set_int(db,"modplug", "LoopCount", mModProps.mLoopCount); bmp_cfg_db_close(db); } ModplugXMMS gModplugXMMS;
