aboutsummaryrefslogtreecommitdiff
path: root/utils/midiplay/adlmidiplay.cpp
diff options
context:
space:
mode:
authorWohlstand <admin@wohlnet.ru>2017-10-20 04:28:53 +0300
committerWohlstand <admin@wohlnet.ru>2017-10-20 04:28:53 +0300
commiteb45a7913d83fe2a3dc03033230950e78fb9755d (patch)
treed7318e79b08404f293fb99bb22e0ed202e07d890 /utils/midiplay/adlmidiplay.cpp
parente95cde9fea4f9daf3e19492021b8a153acae0404 (diff)
downloadlibADLMIDI-eb45a7913d83fe2a3dc03033230950e78fb9755d.tar.gz
libADLMIDI-eb45a7913d83fe2a3dc03033230950e78fb9755d.tar.bz2
libADLMIDI-eb45a7913d83fe2a3dc03033230950e78fb9755d.zip
Added CMake support
Diffstat (limited to 'utils/midiplay/adlmidiplay.cpp')
-rw-r--r--utils/midiplay/adlmidiplay.cpp338
1 files changed, 338 insertions, 0 deletions
diff --git a/utils/midiplay/adlmidiplay.cpp b/utils/midiplay/adlmidiplay.cpp
new file mode 100644
index 0000000..7c8766d
--- /dev/null
+++ b/utils/midiplay/adlmidiplay.cpp
@@ -0,0 +1,338 @@
+
+#include <vector>
+#include <string>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <deque>
+#include <signal.h>
+#define SDL_MAIN_HANDLED
+#include <SDL2/SDL.h>
+
+#include <adlmidi.h>
+
+#include "wave_writer.h"
+
+class MutexType
+{
+ SDL_mutex *mut;
+public:
+ MutexType() : mut(SDL_CreateMutex()) { }
+ ~MutexType()
+ {
+ SDL_DestroyMutex(mut);
+ }
+ void Lock()
+ {
+ SDL_mutexP(mut);
+ }
+ void Unlock()
+ {
+ SDL_mutexV(mut);
+ }
+};
+
+
+static std::deque<short> AudioBuffer;
+static MutexType AudioBuffer_lock;
+
+static void SDL_AudioCallbackX(void *, Uint8 *stream, int len)
+{
+ SDL_LockAudio();
+ short *target = (short *) stream;
+ AudioBuffer_lock.Lock();
+ /*if(len != AudioBuffer.size())
+ fprintf(stderr, "len=%d stereo samples, AudioBuffer has %u stereo samples",
+ len/4, (unsigned) AudioBuffer.size()/2);*/
+ unsigned ate = (unsigned)len / 2; // number of shorts
+ if(ate > AudioBuffer.size())
+ ate = (unsigned)AudioBuffer.size();
+ for(unsigned a = 0; a < ate; ++a)
+ {
+ target[a] = AudioBuffer[a];
+ }
+ AudioBuffer.erase(AudioBuffer.begin(), AudioBuffer.begin() + ate);
+ AudioBuffer_lock.Unlock();
+ SDL_UnlockAudio();
+}
+
+static bool is_number(const std::string &s)
+{
+ std::string::const_iterator it = s.begin();
+ while(it != s.end() && std::isdigit(*it)) ++it;
+ return !s.empty() && it == s.end();
+}
+
+static void printError(const char *err)
+{
+ std::fprintf(stderr, "\nERROR: %s\n\n", err);
+ std::fflush(stderr);
+}
+
+static int stop = 0;
+static void sighandler(int dum)
+{
+ if((dum == SIGINT)
+ || (dum == SIGTERM)
+ #ifndef _WIN32
+ || (dum == SIGHUP)
+ #endif
+ )
+ stop = 1;
+}
+
+int main(int argc, char **argv)
+{
+ if(argc < 2 || std::string(argv[1]) == "--help" || std::string(argv[1]) == "-h")
+ {
+ std::printf(
+ "Usage: adlmidi <midifilename> [ <options> ] [ <bank> [ <numcards> [ <numfourops>] ] ]\n"
+ " -p Enables adlib percussion instrument mode\n"
+ " -t Enables tremolo amplification mode\n"
+ " -v Enables vibrato amplification mode\n"
+ " -s Enables scaling of modulator volumes\n"
+ " -nl Quit without looping\n"
+ " -w Write WAV file rather than playing\n"
+ "\n"
+ "Where <bank> - number of embeeded bank or filepath to custom WOPL bank file\n"
+ "\n"
+ "Note: To create WOPL bank files use OPL Bank Editor you can get here: \n"
+ "https://github.com/Wohlstand/OPL3BankEditor\n"
+ "\n"
+ );
+
+ int banksCount = adl_getBanksCount();
+ const char *const *banknames = adl_getBankNames();
+
+ if(banksCount > 0)
+ {
+ std::printf(" Available embedded banks by number:\n\n");
+
+ for(int a = 0; a < banksCount; ++a)
+ std::printf("%10s%2u = %s\n", a ? "" : "Banks:", a, banknames[a]);
+
+ std::printf(
+ "\n"
+ " Use banks 2-5 to play Descent \"q\" soundtracks.\n"
+ " Look up the relevant bank number from descent.sng.\n"
+ "\n"
+ " The fourth parameter can be used to specify the number\n"
+ " of four-op channels to use. Each four-op channel eats\n"
+ " the room of two regular channels. Use as many as required.\n"
+ " The Doom & Hexen sets require one or two, while\n"
+ " Miles four-op set requires the maximum of numcards*6.\n"
+ "\n"
+ );
+ }
+ else
+ {
+ std::printf(" This build of libADLMIDI has no embedded banks!\n\n");
+ }
+
+ return 0;
+ }
+
+ //const unsigned MaxSamplesAtTime = 512; // 512=dbopl limitation
+ // How long is SDL buffer, in seconds?
+ // The smaller the value, the more often SDL_AudioCallBack()
+ // is called.
+ const double AudioBufferLength = 0.08;
+ // How much do WE buffer, in seconds? The smaller the value,
+ // the more prone to sound chopping we are.
+ const double OurHeadRoomLength = 0.1;
+ // The lag between visual content and audio content equals
+ // the sum of these two buffers.
+ SDL_AudioSpec spec;
+ SDL_AudioSpec obtained;
+
+ spec.freq = 44100;
+ spec.format = AUDIO_S16SYS;
+ spec.channels = 2;
+ spec.samples = Uint16((double)spec.freq * AudioBufferLength);
+ spec.callback = SDL_AudioCallbackX;
+
+ ADL_MIDIPlayer *myDevice;
+ myDevice = adl_init(44100);
+ if(myDevice == NULL)
+ {
+ printError("Failed to init MIDI device!\n");
+ return 1;
+ }
+
+ bool recordWave = false;
+
+ adl_setLoopEnabled(myDevice, 1);
+
+ while(argc > 2)
+ {
+ bool had_option = false;
+
+ if(!std::strcmp("-p", argv[2]))
+ adl_setPercMode(myDevice, 1);
+ else if(!std::strcmp("-v", argv[2]))
+ adl_setHVibrato(myDevice, 1);
+ else if(!std::strcmp("-w", argv[2]))
+ {
+ recordWave = true;
+ adl_setLoopEnabled(myDevice, 0);//Disable loop while record WAV
+ }
+ else if(!std::strcmp("-t", argv[2]))
+ adl_setHTremolo(myDevice, 1);
+ else if(!std::strcmp("-nl", argv[2]))
+ adl_setLoopEnabled(myDevice, 0);
+ else if(!std::strcmp("-s", argv[2]))
+ adl_setScaleModulators(myDevice, 1);
+ else break;
+
+ std::copy(argv + (had_option ? 4 : 3), argv + argc,
+ argv + 2);
+ argc -= (had_option ? 2 : 1);
+ }
+
+ if(!recordWave)
+ {
+ // Set up SDL
+ if(SDL_OpenAudio(&spec, &obtained) < 0)
+ {
+ std::fprintf(stderr, "\nERROR: Couldn't open audio: %s\n\n", SDL_GetError());
+ std::fflush(stderr);
+ //return 1;
+ }
+ if(spec.samples != obtained.samples)
+ {
+ std::fprintf(stderr, "Wanted (samples=%u,rate=%u,channels=%u); obtained (samples=%u,rate=%u,channels=%u)\n",
+ spec.samples, spec.freq, spec.channels,
+ obtained.samples, obtained.freq, obtained.channels);
+ std::fflush(stderr);
+ }
+ }
+
+ if(argc >= 3)
+ {
+ if(is_number(argv[2]))
+ {
+ int bankno = std::atoi(argv[2]);
+ if(adl_setBank(myDevice, bankno) != 0)
+ {
+ printError(adl_errorString());
+ return 1;
+ }
+ }
+ else
+ {
+ std::fprintf(stdout, "Loading custom bank file %s...", argv[2]);
+ std::fflush(stdout);
+ if(adl_openBankFile(myDevice, argv[2]) != 0)
+ {
+ std::fprintf(stdout, "FAILED!\n");
+ std::fflush(stdout);
+ printError(adl_errorString());
+ return 1;
+ }
+ std::fprintf(stdout, "OK!\n");
+ std::fflush(stdout);
+ }
+ }
+
+ if(argc >= 4)
+ {
+ if(adl_setNumCards(myDevice, std::atoi(argv[3])) != 0)
+ {
+ printError(adl_errorString());
+ return 1;
+ }
+ std::fprintf(stdout, "Number of cards %s\n", argv[3]);
+ }
+ else
+ {
+ // 4 chips by default
+ if(adl_setNumCards(myDevice, 4) != 0)
+ {
+ printError(adl_errorString());
+ return 1;
+ }
+ }
+
+ if(argc >= 5)
+ {
+ if(adl_setNumFourOpsChn(myDevice, std::atoi(argv[4])) != 0)
+ {
+ printError(adl_errorString());
+ return 1;
+ }
+ std::fprintf(stdout, "Number of four-ops %s\n", argv[4]);
+ }
+
+ if(adl_openFile(myDevice, argv[1]) != 0)
+ {
+ printError(adl_errorString());
+ return 2;
+ }
+
+ signal(SIGINT, sighandler);
+ signal(SIGTERM, sighandler);
+ #ifndef _WIN32
+ signal(SIGHUP, sighandler);
+ #endif
+
+ if(!recordWave)
+ {
+ SDL_PauseAudio(0);
+
+ while(!stop)
+ {
+ short buff[4096];
+ size_t got = (size_t)adl_play(myDevice, 4096, buff);
+ if(got <= 0)
+ break;
+
+ AudioBuffer_lock.Lock();
+ size_t pos = AudioBuffer.size();
+ AudioBuffer.resize(pos + got);
+ for(size_t p = 0; p < got; ++p)
+ AudioBuffer[pos + p] = buff[p];
+ AudioBuffer_lock.Unlock();
+
+ const SDL_AudioSpec &spec = obtained;
+ while(AudioBuffer.size() > spec.samples + (spec.freq * 2) * OurHeadRoomLength)
+ {
+ SDL_Delay(1);
+ }
+ }
+
+ SDL_CloseAudio();
+ }
+ else
+ {
+ std::string wave_out = std::string(argv[1]) + ".wav";
+ std::fprintf(stdout, "Recording WAV file %s...\n", wave_out.c_str());
+ std::fflush(stdout);
+
+ if(wave_open(spec.freq, wave_out.c_str()) == 0)
+ {
+ wave_enable_stereo();
+ while(!stop)
+ {
+ short buff[4096];
+ size_t got = (size_t)adl_play(myDevice, 4096, buff);
+ if(got <= 0)
+ break;
+ wave_write(buff, (long)got);
+ }
+ wave_close();
+
+ std::fprintf(stdout, "Completed!\n");
+ std::fflush(stdout);
+ }
+ else
+ {
+ adl_close(myDevice);
+ return 1;
+ }
+ }
+
+ adl_close(myDevice);
+
+ return 0;
+}
+