Mercurial > audlegacy
diff Plugins/Input/modplug/modplugbmp.cpp @ 278:37316876ef6e trunk
[svn] Use modplug instead of mikmod. Supports more formats & compressed files.
author | chainsaw |
---|---|
date | Sat, 10 Dec 2005 14:31:13 -0800 |
parents | |
children | 3a2771d4140e |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/Input/modplug/modplugbmp.cpp Sat Dec 10 14:31:13 2005 -0800 @@ -0,0 +1,839 @@ +/* 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 <libmodplug/stdafx.h> +#include <libmodplug/sndfile.h> +#include "stddefs.h" +#include "archive/open.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; + + 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) +{ + fstream lConfigFile; + string lField, lValue; + string lConfigFilename; + bool lValueB; + char junk; + + //I chose to use a separate config file to avoid conflicts + lConfigFilename = g_get_home_dir(); + lConfigFilename += "/.bmp/modplug-bmp.conf"; + lConfigFile.open(lConfigFilename.c_str(), ios::in); + + if(!lConfigFile.is_open()) + return; + + while(!lConfigFile.eof()) + { + lConfigFile >> lField; + if(lField[0] == '#') //allow comments + { + do + { + lConfigFile.read(&junk, 1); + } + while(junk != '\n'); + } + else + { + if(lField == "reverb_depth") + lConfigFile >> mModProps.mReverbDepth; + else if(lField == "reverb_delay") + lConfigFile >> mModProps.mReverbDelay; + else if(lField == "megabass_amount") + lConfigFile >> mModProps.mBassAmount; + else if(lField == "megabass_range") + lConfigFile >> mModProps.mBassRange; + else if(lField == "surround_depth") + lConfigFile >> mModProps.mSurroundDepth; + else if(lField == "surround_delay") + lConfigFile >> mModProps.mSurroundDelay; + else if(lField == "preamp_volume") + lConfigFile >> mModProps.mPreampLevel; + else if(lField == "loop_count") + lConfigFile >> mModProps.mLoopCount; + else + { + lConfigFile >> lValue; + if(lValue == "on") + lValueB = true; + else + lValueB = false; + + if(lField == "surround") + mModProps.mSurround = lValueB; + else if(lField == "oversampling") + mModProps.mOversamp = lValueB; + else if(lField == "reverb") + mModProps.mReverb = lValueB; + else if(lField == "megabass") + mModProps.mMegabass = lValueB; + else if(lField == "noisereduction") + mModProps.mNoiseReduction = lValueB; + else if(lField == "volumeramping") + mModProps.mVolumeRamp = lValueB; + else if(lField == "fastinfo") + mModProps.mFastinfo = lValueB; + else if(lField == "use_filename") + mModProps.mUseFilename = lValueB; + else if(lField == "preamp") + mModProps.mPreamp = lValueB; + + else if(lField == "channels") + { + if(lValue == "mono") + mModProps.mChannels = 1; + else + mModProps.mChannels = 2; + } + else if(lField == "frequency") + { + if(lValue == "22050") + mModProps.mFrequency = 22050; + else if(lValue == "11025") + mModProps.mFrequency = 11025; + else + mModProps.mFrequency = 44100; + } + else if(lField == "bits") + { + if(lValue == "8") + mModProps.mBits = 8; + else + mModProps.mBits = 16; + } + else if(lField == "resampling") + { + if(lValue == "nearest") + mModProps.mResamplingMode = SRCMODE_NEAREST; + else if(lValue == "linear") + mModProps.mResamplingMode = SRCMODE_LINEAR; + else if(lValue == "spline") + mModProps.mResamplingMode = SRCMODE_SPLINE; + else + mModProps.mResamplingMode = SRCMODE_POLYPHASE; + } + } //if(numerical value) else + } //if(comment) else + } //while(!eof) + + lConfigFile.close(); +} + +bool ModplugXMMS::CanPlayFile(const string& aFilename) +{ + string lExt; + uint32 lPos; + + 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 == ".669") + return true; + 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 == ".it") + return true; + if (lExt == ".mdl") + return true; + if (lExt == ".med") + return true; + if (lExt == ".mod") + return true; + if (lExt == ".mtm") + return true; + if (lExt == ".okt") + return true; + if (lExt == ".ptm") + return true; + if (lExt == ".s3m") + return true; + if (lExt == ".stm") + return true; + if (lExt == ".ult") + return true; + if (lExt == ".umx") //Unreal rocks! + return true; + if (lExt == ".xm") + return true; + if (lExt == ".j2b") + return true; + if (lExt == ".mt2") + return true; + if (lExt == ".psm") + 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 == ".rar") + 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] *= 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] *= 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; + + mOutPlug->write_audio + ( + mBuffer, + mBufSize + ); + mInPlug->add_vis_pcm + ( + mPlayed, + mFormat, + lChannels, + mBufSize, + mBuffer + ); + + 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(), + mModProps.mFrequency / 1000, + 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) +{ + fstream lConfigFile; + string lConfigFilename; + + 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); + + lConfigFilename = g_get_home_dir(); + lConfigFilename += "/.bmp/modplug-bmp.conf"; + lConfigFile.open(lConfigFilename.c_str(), ios::out); + + lConfigFile << "# Modplug BMP plugin config file\n" + << "# Modplug (C) 1999 Olivier Lapicque\n" + << "# XMMS port (C) 1999 Kenton Varda\n" + << "# BMP port (C) 2004 Theofilos Intzoglou" << endl; + + lConfigFile << "# ---Effects---" << endl; + lConfigFile << "reverb " << Bool2OnOff(mModProps.mReverb) << endl; + lConfigFile << "reverb_depth " << mModProps.mReverbDepth << endl; + lConfigFile << "reverb_delay " << mModProps.mReverbDelay << endl; + lConfigFile << endl; + lConfigFile << "surround " << Bool2OnOff(mModProps.mSurround) << endl; + lConfigFile << "surround_depth " << mModProps.mSurroundDepth << endl; + lConfigFile << "surround_delay " << mModProps.mSurroundDelay << endl; + lConfigFile << endl; + lConfigFile << "megabass " << Bool2OnOff(mModProps.mMegabass) << endl; + lConfigFile << "megabass_amount " << mModProps.mBassAmount << endl; + lConfigFile << "megabass_range " << mModProps.mBassRange << endl; + lConfigFile << endl; + lConfigFile << "oversampling " << Bool2OnOff(mModProps.mOversamp) << endl; + lConfigFile << "noisereduction " << Bool2OnOff(mModProps.mNoiseReduction) << endl; + lConfigFile << "volumeramping " << Bool2OnOff(mModProps.mVolumeRamp) << endl; + lConfigFile << "fastinfo " << Bool2OnOff(mModProps.mFastinfo) << endl; + lConfigFile << "use_filename " << Bool2OnOff(mModProps.mUseFilename) << endl; + lConfigFile << "loop_count " << mModProps.mLoopCount << endl; + lConfigFile << endl; + lConfigFile << "preamp " << Bool2OnOff(mModProps.mPreamp) << endl; + lConfigFile << "preamp_volume " << mModProps.mPreampLevel << endl; + lConfigFile << endl; + + lConfigFile << "# ---Quality---" << endl; + lConfigFile << "channels "; + if(mModProps.mChannels == 1) + lConfigFile << "mono" << endl; + else + lConfigFile << "stereo" << endl; + lConfigFile << "bits " << (int)mModProps.mBits << endl; + lConfigFile << "frequency " << mModProps.mFrequency << endl; + lConfigFile << "resampling "; + switch(mModProps.mResamplingMode) + { + case SRCMODE_NEAREST: + lConfigFile << "nearest" << endl; + break; + case SRCMODE_LINEAR: + lConfigFile << "linear" << endl; + break; + case SRCMODE_SPLINE: + lConfigFile << "spline" << endl; + break; + default: + case SRCMODE_POLYPHASE: + lConfigFile << "fir" << endl; + break; + }; + + lConfigFile.close(); +} + +ModplugXMMS gModplugXMMS;