fmopl_midi.cc

Go to the documentation of this file.
00001 /*
00002 Copyright (C) 2000, 2001, 2002  Ryan Nunn
00003 
00004 This program is free software; you can redistribute it and/or
00005 modify it under the terms of the GNU General Public License
00006 as published by the Free Software Foundation; either version 2
00007 of the License, or (at your option) any later version.
00008 
00009 This program is distributed in the hope that it will be useful,
00010 but WITHOUT ANY WARRANTY; without even the implied warranty of
00011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012 GNU General Public License for more details.
00013 
00014 You should have received a copy of the GNU General Public License
00015 along with this program; if not, write to the Free Software
00016 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
00017 */
00018 
00019 #ifdef HAVE_CONFIG_H
00020 #  include <config.h>
00021 #endif
00022 
00023 #ifdef USE_FMOPL_MIDI
00024 
00025 #include <string>
00026 
00027 #include "Audio.h"
00028 #include "fmopl_midi.h"
00029 #include "fmopldrv.h"
00030 #include "xmidi.h"
00031 #include "utils.h"
00032 #include "Configuration.h"
00033 #include "SDL_mixer.h"
00034 
00035 extern  Configuration *config;
00036 
00037 #define FMOPL_MESSAGE_NO_MESSAGE    0
00038 #define FMOPL_MESSAGE_PLAY        1
00039 #define FMOPL_MESSAGE_PLAY_REPEAT   2
00040 #define FMOPL_MESSAGE_STOP        3
00041 #define FMOPL_MESSAGE_INIT        4
00042 
00043 const unsigned short FMOpl_Midi::centre_value = 0x2000;
00044 const unsigned char FMOpl_Midi::fine_value = centre_value & 127;
00045 const unsigned char FMOpl_Midi::coarse_value = centre_value >> 7;
00046 const unsigned short FMOpl_Midi::combined_value = (coarse_value << 8) | fine_value;
00047 
00048 //#define DO_SMP_TEST
00049 
00050 #ifdef DO_SMP_TEST
00051 #define giveinfo() std::cerr << __FILE__ << ":" << __LINE__ << std::endl; std::cerr.flush();
00052 #else
00053 #define giveinfo()
00054 #endif
00055 
00056 using std::string;
00057 using std::cout;
00058 using std::cerr;
00059 using std::endl;
00060 
00061 class RingBuffer16
00062 {
00063   uint32    write_pos;
00064   uint32    read_pos;
00065   sint16    *buf;
00066   uint32    size;
00067   SDL_mutex *mutex;
00068 
00069 public:
00070   void LockWrite(uint32 total, sint16 *&buf1, uint32 &size1, sint16 *&buf2, uint32 &size2);
00071   void LockRead(uint32 total, sint16 *&buf1, uint32 &size1, sint16 *&buf2, uint32 &size2);
00072   void Unlock();
00073 
00074   RingBuffer16(uint32 s);
00075   ~RingBuffer16();
00076 };
00077 
00078 RingBuffer16::RingBuffer16(uint32 s) : write_pos(0), read_pos(0), size(s)
00079 {
00080   buf = new sint16[size];
00081   std::memset(buf, 0, sizeof(sint16)*size);
00082   mutex = SDL_CreateMutex();
00083 }
00084 
00085 RingBuffer16::~RingBuffer16()
00086 {
00087   SDL_DestroyMutex(mutex);
00088   delete [] buf;
00089 }
00090 
00091 void RingBuffer16::Unlock()
00092 {
00093   // Unlock it
00094   SDL_mutexV(mutex);
00095 }
00096 
00097 void RingBuffer16::LockRead(uint32 total, sint16 *&buf1, uint32 &size1, sint16 *&buf2, uint32 &size2)
00098 {
00099   // Lock it
00100   SDL_mutexP(mutex);
00101 
00102   // Too many samples
00103   if (size < total) {
00104     size1 = 0;
00105     buf1 = 0;
00106     size2 = 0;
00107     buf2 = 0;
00108     return;
00109   }
00110 
00111   // It will run over the end of the ring buffer. Need to return end and start
00112   if (read_pos + total >= size) {
00113 
00114     // Buffer 1
00115     buf1 = buf + read_pos;
00116     size1 = size - read_pos;
00117 
00118     // Increment read_pos
00119     read_pos += total;
00120     read_pos -= size;
00121 
00122     // Buffer 2
00123     buf2 = buf;
00124     size2 = read_pos;
00125 
00126     return;
00127   }
00128 
00129   // Handle it normally
00130   buf1 = buf + read_pos;
00131   size1 = total;
00132   buf2 = 0;
00133   size2 = 0;
00134   read_pos += total;
00135 
00136 }
00137 
00138 void RingBuffer16::LockWrite(uint32 total, sint16 *&buf1, uint32 &size1, sint16 *&buf2, uint32 &size2)
00139 {
00140   // Lock it
00141   SDL_mutexP(mutex);
00142 
00143   // Too many samples
00144   if (size < total) {
00145     size1 = 0;
00146     buf1 = 0;
00147     size2 = 0;
00148     buf2 = 0;
00149     return;
00150   }
00151 
00152   // It will run over the end of the ring buffer. Need to return end and start
00153   if (write_pos + total >= size) {
00154 
00155     // Buffer 1
00156     buf1 = buf + write_pos;
00157     size1 = size - write_pos;
00158 
00159     // Increment read_pos
00160     write_pos += total;
00161     write_pos -= size;
00162 
00163     // Buffer 2
00164     buf2 = buf;
00165     size2 = write_pos;
00166 
00167     return;
00168   }
00169 
00170   // Handle it normally
00171   buf1 = buf + write_pos;
00172   size1 = total;
00173   buf2 = 0;
00174   size2 = 0;
00175   write_pos += total;
00176 }
00177 
00178 FMOpl_Midi::FMOpl_Midi()
00179 {
00180   giveinfo();
00181   playing = false;
00182   is_available = false;
00183   giveinfo();
00184   init_device();
00185   giveinfo();
00186 }
00187 
00188 FMOpl_Midi::~FMOpl_Midi()
00189 {
00190   giveinfo();
00191   if (!is_available) return;
00192 
00193   deinit_device();
00194 }
00195 
00196 void FMOpl_Midi::deinit_device()
00197 {
00198   // Un Hook SDL
00199   is_available = false;
00200   Mix_HookMusic (NULL, NULL);
00201 #ifdef PENTAGRAM
00202   Mix_CloseAudio();
00203 #endif
00204 
00205   // Delay a fraction
00206   SDL_Delay(10);
00207 
00208   // Lock it
00209   SDL_mutexP(mutex);
00210 
00211   // Free everything
00212   if (evntlist) evntlist->DecerementCounter();
00213   evntlist = NULL;
00214 
00215   // Free new list if it exists
00216   if (new_list) new_list->DecerementCounter();
00217   new_list = NULL;
00218   comMessage_priv = FMOPL_MESSAGE_NO_MESSAGE;
00219 
00220   // Reset the reasonable defaults
00221   repeat = false;
00222   aim = 0;
00223   diff = 0;
00224   last_tick = 0;
00225   evntlist = NULL;
00226   event = NULL;
00227   notes_on.clear();
00228   loop_num = -1;    
00229   std::memset(volumes, 64, sizeof(volumes));
00230   std::memset(balances, 64, sizeof(balances));
00231   generate_rem = 0;
00232   total_sample_ticks = 0;
00233 
00234   // Left
00235   delete opl;
00236   opl = 0;
00237   delete buffer;
00238   buffer = 0;
00239 
00240   // Right
00241   delete opl_right;
00242   opl_right = 0;
00243   delete buffer_right;
00244   buffer_right = 0;
00245 
00246   // Unlock it
00247   SDL_mutexV(mutex);
00248 
00249   // Kill it
00250   SDL_DestroyMutex(mutex);
00251   SDL_DestroyMutex(comMutex);
00252   mutex = NULL;
00253   comMutex = NULL;
00254 }
00255 
00256 void FMOpl_Midi::init_device()
00257 {
00258   string s;
00259 
00260   Audio *audio = Audio::get_ptr();
00261     
00262   // Dual Opl mode
00263   if (audio->is_stereo())
00264   {
00265     config->value("config/audio/midi/dual_opl",s,"yes");
00266     if (s == "yes") dual = true;
00267     else {
00268       s = "no";
00269       dual = false;
00270     }
00271 
00272     std::cout << "Dual OPL mode: " << s << endl;
00273 
00274     // Make sure it's in the config
00275     config->set("config/audio/midi/dual_opl",s,true);
00276   }
00277   else
00278   {
00279     dual = false;
00280   }
00281 
00282   // Now setup all our settings
00283   playing = false;
00284   generate_rem = 0;
00285   is_available = true;
00286   comMessage_priv = FMOPL_MESSAGE_NO_MESSAGE;
00287   new_list = NULL;
00288 
00289   // Create Left
00290   buffer = new RingBuffer16(44096);
00291   opl = new OplDriver;
00292   opl->open(audio->get_sample_rate());
00293 
00294   // Create right, if required
00295   if (dual) {
00296     buffer_right = new RingBuffer16(44096);
00297     opl_right = new OplDriver;
00298     opl_right->open(audio->get_sample_rate());
00299   }
00300   else {
00301     opl_right = 0;
00302     buffer_right = 0;
00303   }
00304 
00305   // Setup some reasonable defaults
00306   repeat = false;
00307   aim = 0;
00308   diff = 0;
00309   last_tick = 0;
00310   evntlist = NULL;
00311   event = NULL;
00312   notes_on.clear();
00313   loop_num = -1;    
00314   std::memset(volumes, 64, sizeof(volumes));
00315   std::memset(balances, 64, sizeof(balances));
00316 
00317   generate_rem = 0;
00318   total_sample_ticks = 0;
00319   global_volume = 255;
00320 
00321   // Create the Mutexes
00322   mutex = SDL_CreateMutex();
00323   comMutex = SDL_CreateMutex();
00324 
00325   // Hook the process
00326 #ifdef PENTAGRAM
00327   Mix_OpenAudio(audio->get_sample_rate(), AUDIO_S16SYS, 2, 4096);
00328 #endif
00329   Mix_HookMusic (mixer_hook_static, (void*) this);
00330 }
00331 
00332 inline void FMOpl_Midi::send_vol_or_balance(uint32 chan) {
00333 
00334   // Single OPL mode only
00335   if (!dual) return;
00336 
00337   int left = volumes[chan];
00338   int right = volumes[chan];
00339 
00340   // is left
00341   if (balances[chan] < 64) {
00342     right *= balances[chan];
00343     right >>= 6;  // right /= 64;
00344   }
00345   // is right
00346   else if (balances[chan] > 64) {
00347     left *= 127 - balances[chan];
00348     left >>= 6;   // left /= 64;
00349   }
00350 
00351   opl->send(chan | (MIDI_STATUS_CONTROLLER << 4)|(7 << 8) | (left<<16));
00352   opl_right->send(chan | (MIDI_STATUS_CONTROLLER << 4)|(7 << 8) | (right<<16));
00353 }
00354 
00355 inline void FMOpl_Midi::send(uint32 b) {
00356 
00357   // Volume
00358   if (dual && (b&0xFFF0) == ((MIDI_STATUS_CONTROLLER << 4)|(7 << 8)))
00359   {
00360     int chan = b&0xF;
00361     volumes[chan] = (b >> 16) & 0x7F;
00362     send_vol_or_balance(chan);
00363   }
00364   // Balance
00365   else if (dual && (b&0xFFF0) == ((MIDI_STATUS_CONTROLLER << 4)|(10 << 8)))
00366   {
00367     int chan = b&0xF;
00368     balances[chan] = (b >> 16) & 0x7F;
00369     send_vol_or_balance(chan);
00370   }
00371   else {
00372     opl->send(b);
00373     if (opl_right) opl_right->send(b);
00374   }
00375 }
00376 
00377 uint32 FMOpl_Midi::GenerateSamples(uint32 count_required, uint32 sample_rate) {
00378 
00379   uint32 amount_generated = 0;
00380 
00381   uint32 inc = 1;
00382   uint32 gen = OPL_NUM_SAMPLES_PER_PASS;
00383 
00384   if (sample_rate == 44100) gen *= 2;
00385   else if (sample_rate == 11025) inc *= 2;
00386   else if (sample_rate != 22050) 
00387   {
00388     // Approximate it!
00389     inc = 6;
00390     gen = (OPL_TIME_PER_PASS*sample_rate)/(120*OPL_TICK_MULTIPLIER*inc);
00391   }
00392 
00393   while (amount_generated < count_required) {
00394 
00395     HandleStop();
00396     PlayNotes();
00397     HandlePlay();
00398 
00399     GetSamples(gen);
00400     amount_generated += gen;
00401     wmoClockIncTime(inc);
00402   }
00403 
00404   return amount_generated - count_required;
00405 }
00406 
00407 void FMOpl_Midi::PlayNotes()
00408 {
00409   // Handle note off's here
00410   while (midi_event *note = notes_on.PopTime(wmoGetRealTime()))
00411     send(note->status | (note->data[0] << 8));
00412 
00413   while (event)
00414   {
00415     aim = (event->time-last_tick)*OPL_TICK_MULTIPLIER;
00416 
00417     //printf ("aim %i time %i\n", aim, wmoGetTime ());
00418 
00419     diff = aim - wmoGetTime ();
00420 
00421     if (diff > 0) break;
00422 
00423     last_tick = event->time;
00424     wmoAddOffset(aim);
00425   
00426       // XMIDI For Loop
00427     if ((event->status >> 4) == MIDI_STATUS_CONTROLLER && event->data[0] == XMIDI_CONTROLLER_FOR_LOOP)
00428     {
00429       if (loop_num < XMIDI_MAX_FOR_LOOP_COUNT) loop_num++;
00430 
00431       loop_count[loop_num] = event->data[1];
00432       loop_event[loop_num] = event;
00433 
00434     } // XMIDI Next/Break
00435     else if ((event->status >> 4) == MIDI_STATUS_CONTROLLER && event->data[0] == XMIDI_CONTROLLER_NEXT_BREAK)
00436     {
00437       if (loop_num != -1)
00438       {
00439         if (event->data[1] < 64)
00440         {
00441           loop_num--;
00442         }
00443       }
00444       event = NULL;
00445 
00446     } // XMIDI Callback Trigger
00447     else if ((event->status >> 4) == MIDI_STATUS_CONTROLLER && event->data[0] == XMIDI_CONTROLLER_CALLBACK_TRIG)
00448     {
00449       // TODO
00450     } // Not SysEx
00451     else if (event->status < 0xF0)
00452     {
00453       int type = event->status >> 4;
00454 
00455       if ((type != MIDI_STATUS_NOTE_ON || event->data[1]) && type != MIDI_STATUS_NOTE_OFF) {
00456         if (type == MIDI_STATUS_NOTE_ON) {
00457           notes_on.Remove(event);
00458           notes_on.Push (event, event->duration * OPL_TICK_MULTIPLIER + wmoGetStart());
00459         }
00460 
00461         send(event->status | (event->data[0] << 8) | (event->data[1] << 16));
00462       }
00463     }
00464   
00465     if (event) event = event->next;
00466 
00467     // Lock com system
00468     int &comMessage = LockComs();
00469 
00470     if (!event || comMessage != FMOPL_MESSAGE_NO_MESSAGE)
00471     {
00472       bool clean = !repeat || (comMessage != FMOPL_MESSAGE_NO_MESSAGE) || last_tick == 0;
00473 
00474       if (clean)
00475       {
00476         playing = false;
00477         if (comMessage == FMOPL_MESSAGE_STOP)
00478           comMessage = FMOPL_MESSAGE_NO_MESSAGE;
00479 
00480         // Handle note off's here
00481         while (midi_event *note = notes_on.Pop())
00482           send(note->status | (note->data[0] << 8));
00483 
00484         // Clean up
00485         for (int i = 0; i < 16; i++) reset_channel (i); 
00486         //midiOutReset (midi_port);
00487         if (evntlist) evntlist->DecerementCounter();
00488         evntlist = NULL;
00489         event = NULL;
00490 
00491         loop_num = -1;
00492         wmoInitClock ();
00493       }
00494 
00495       last_tick = 0;
00496 
00497       if (evntlist)
00498       {
00499         if (loop_num == -1) event = evntlist->events;
00500         else
00501         {
00502           event = loop_event[loop_num]->next;
00503           last_tick = loop_event[loop_num]->time;
00504 
00505           if (loop_count[loop_num])
00506             if (!--loop_count[loop_num])
00507               loop_num--;
00508         }
00509       }
00510     }
00511 
00512     // UnLock com system
00513     UnlockComs();
00514   }
00515 
00516 }
00517 
00518 void FMOpl_Midi::HandlePlay()
00519 {
00520   // Lock com system
00521   int &comMessage = LockComs();
00522 
00523   // Got issued a music play command
00524   // set up the music playing routine
00525   if (comMessage == FMOPL_MESSAGE_PLAY || comMessage == FMOPL_MESSAGE_PLAY_REPEAT)
00526   {
00527     // Handle note off's here
00528     while (midi_event *note = notes_on.Pop())
00529       send(note->status | (note->data[0] << 8));
00530 
00531     // Manual Reset since I don't trust midiOutReset()
00532     giveinfo();
00533     for (int i = 0; i < 16; i++) reset_channel (i);
00534 
00535     if (evntlist) evntlist->DecerementCounter();
00536     evntlist = NULL;
00537     event = NULL;
00538     playing = false;
00539 
00540   
00541     giveinfo();
00542     evntlist = new_list;
00543     repeat = (comMessage == FMOPL_MESSAGE_PLAY_REPEAT);
00544 
00545     giveinfo();
00546     new_list = NULL;
00547     giveinfo();
00548     comMessage = FMOPL_MESSAGE_NO_MESSAGE;
00549     
00550     giveinfo();
00551     if (evntlist) event = evntlist->events;
00552     else event = 0;
00553 
00554     giveinfo();
00555     last_tick = 0;
00556     
00557     giveinfo();
00558     wmoInitClock ();
00559 
00560     // Reset XMIDI Looping
00561     loop_num = -1;
00562 
00563     giveinfo();
00564     playing = true;
00565   }
00566 
00567   // Unlock com system
00568   UnlockComs();
00569 }
00570 
00571 void FMOpl_Midi::HandleStop()
00572 {
00573   // Lock com system
00574   int &comMessage = LockComs();
00575 
00576   if (comMessage == FMOPL_MESSAGE_STOP)
00577   {
00578     giveinfo();
00579     playing = false;
00580     comMessage = FMOPL_MESSAGE_NO_MESSAGE;
00581 
00582     // Handle note off's here
00583     while (midi_event *note = notes_on.Pop())
00584       send(note->status | (note->data[0] << 8));
00585 
00586     giveinfo();
00587     // Clean up
00588     for (int i = 0; i < 16; i++) reset_channel (i); 
00589     //midiOutReset (midi_port);
00590     giveinfo();
00591     if (evntlist) evntlist->DecerementCounter();
00592     giveinfo();
00593     evntlist = NULL;
00594     event = NULL;
00595     giveinfo();
00596 
00597     // If stop was requested, we are ready to receive another song
00598 
00599     loop_num = -1;
00600 
00601     wmoInitClock ();
00602     last_tick = 0;
00603   }
00604 
00605   // Unlock com system
00606   UnlockComs();
00607 }
00608 
00609 void FMOpl_Midi::reset_channel (int i)
00610 {
00611   // Bank Select (General Midi)
00612   send(i | (MIDI_STATUS_CONTROLLER << 4) | (0 << 8) | (127 << 16));
00613 
00614   // Modulation Wheel
00615   send(i | (MIDI_STATUS_CONTROLLER << 4) | (1 << 8) | (coarse_value << 16));
00616   
00617   // Volume 
00618   send(i | (MIDI_STATUS_CONTROLLER << 4) | (7 << 8) | (coarse_value << 16));
00619 
00620   // Balance
00621   send(i | (MIDI_STATUS_CONTROLLER << 4) | (10 << 8) | (coarse_value << 16));
00622 
00623   // Expression
00624   send(i | (MIDI_STATUS_CONTROLLER << 4) | (11 << 8) | (127 << 16));
00625 
00626   // XMIDI Bank Select
00627   send(i | (MIDI_STATUS_CONTROLLER << 4) | (XMIDI_CONTROLLER_BANK_CHANGE << 8) | (0 << 16));
00628 
00629   // All notes off
00630   send(i | (MIDI_STATUS_CONTROLLER << 4) | (123 << 8));
00631 
00632   // Instrument
00633   send(i | (MIDI_STATUS_PROG_CHANGE << 4) | (0 << 8));
00634 
00635   // Pitch Wheel
00636   send(i | (MIDI_STATUS_PITCH_WHEEL << 4) | (combined_value << 8));
00637   
00638 }
00639 
00640 // Lock the coms
00641 int &FMOpl_Midi::LockComs()
00642 {
00643   SDL_mutexP(comMutex);
00644   return comMessage_priv;
00645 }
00646 
00647 // Unlock the coms
00648 void FMOpl_Midi::UnlockComs()
00649 {
00650   SDL_mutexV(comMutex);
00651 }
00652 
00653 // Clead coms
00654 void FMOpl_Midi::ClearComs()
00655 {
00656   comMessage_priv = FMOPL_MESSAGE_NO_MESSAGE;
00657 
00658   // Free everything
00659   if (new_list) new_list->DecerementCounter();
00660   new_list = NULL;
00661 }
00662 
00663 void FMOpl_Midi::start_track (XMIDIEventList *xmidi, bool repeat)
00664 {
00665   giveinfo();
00666   if (!is_available)
00667     init_device();
00668 
00669   giveinfo();
00670   if (!is_available)
00671     return;
00672 
00673   giveinfo();
00674 
00675   // Lock and clear the com system
00676   int &comMessage = LockComs();
00677   ClearComs();
00678 
00679   // Increment the list counter
00680   giveinfo();
00681   xmidi->IncerementCounter();
00682   new_list = xmidi;
00683   
00684   // Set the com message
00685   if (repeat) comMessage = FMOPL_MESSAGE_PLAY_REPEAT;
00686   else comMessage = FMOPL_MESSAGE_PLAY;
00687 
00688   // Unlock the coms
00689   UnlockComs();
00690 }
00691 
00692 void FMOpl_Midi::stop_track(void)
00693 {
00694   giveinfo();
00695   if (!is_available) return;
00696 
00697   giveinfo();
00698 
00699   // Lock and clear the com system
00700   int &comMessage = LockComs();
00701   ClearComs();
00702 
00703   // If we are playing, we need to issue a stop com
00704   if (playing) comMessage = FMOPL_MESSAGE_STOP;
00705 
00706   // Unlock the coms
00707   UnlockComs();
00708 }
00709 
00710 bool FMOpl_Midi::is_playing(void)
00711 {
00712   giveinfo();
00713 
00714   int &comMessage = LockComs();
00715 
00716   // If a stop command has been issued, we'll return and say that we have actually stopped
00717   // even though, there may still be some output.
00718   bool ret = playing && comMessage != FMOPL_MESSAGE_STOP;
00719 
00720   UnlockComs();
00721 
00722   return ret;
00723 }
00724 
00725 const char *FMOpl_Midi::copyright(void)
00726 {
00727   giveinfo();
00728   return "Internal Emulated FM Opl Midi Synth.";
00729 }
00730 
00731 void FMOpl_Midi::mixer_hook_static(void *udata, Uint8 *stream, int len)
00732 {
00733   ((FMOpl_Midi *)udata)->mixer_hook(stream, len);
00734 }
00735 
00736 void FMOpl_Midi::mixer_hook(Uint8 *stream, int len)
00737 {
00738   // Lock the mutex
00739   SDL_mutexP(mutex);
00740 
00741   // Looks like we are shutting down
00742   if (!is_available) return;
00743 
00744   Audio *audio = Audio::get_ptr();
00745 
00746   // Generate the music
00747   if (audio->is_stereo())
00748     generate_rem = GenerateSamples(len/4-generate_rem, audio->get_sample_rate());
00749   else
00750     generate_rem = GenerateSamples(len/2-generate_rem, audio->get_sample_rate());
00751 
00752   // Copy the buffers into SDL_Mixer's buffer
00753   sint16* stream16 = (sint16*) stream;
00754 
00755   sint16* b1 = 0;
00756   sint16* b2 = 0;
00757   uint32  size1 = 0;
00758   uint32  size2 = 0;
00759   int i = 0;
00760 
00761   // Lock it (Left or Mono)
00762   if (audio->is_stereo())
00763     buffer->LockRead(len/4, b1, size1, b2, size2);
00764   else
00765     buffer->LockRead(len/2, b1, size1, b2, size2);
00766 
00767   sint16* b1_r = b1;
00768   sint16* b2_r = b2;
00769   uint32  size1_r = size1;
00770   uint32  size2_r = size2;
00771 
00772   // Right, if in dual mode
00773   if (dual) buffer_right->LockRead(len/4, b1_r, size1_r, b2_r, size2_r);
00774 
00775   // Copy the samples
00776   if (global_volume >= 255) {
00777 
00778     if (audio->is_stereo()) {
00779 
00780       for (i = 0; i < size1; ++i) {
00781         stream16[i<<1] = b1[i];
00782         stream16[(i<<1)+1] = b1_r[i];
00783       }
00784 
00785       for (i = 0; i < size2; ++i) {
00786         stream16[(i+size1)<<1] = b2[i];
00787         stream16[((i+size1)<<1)+1] = b2_r[i];
00788       }
00789     }
00790     else {
00791 
00792       for (i = 0; i < size1; ++i) {
00793         stream16[i] = b1[i];
00794       }
00795 
00796       for (i = 0; i < size2; ++i) {
00797         stream16[i+size1] = b2[i];
00798       }
00799     }
00800   }
00801   else if (global_volume && dual) {
00802     for (i = 0; i < size1; ++i) {
00803       stream16[i<<1] = (b1[i]*global_volume)/255;
00804       stream16[(i<<1)+1] = (b1_r[i]*global_volume)/255;
00805     }
00806 
00807     for (i = 0; i < size2; ++i) {
00808       stream16[(i+size1)<<1] = (b2[i]*global_volume)/255;
00809       stream16[((i+size1)<<1)+1] = (b2_r[i]*global_volume)/255;
00810     }
00811   }
00812   else if (global_volume) {
00813     if (audio->is_stereo()) {
00814 
00815       for (i = 0; i < size1; ++i) {
00816         stream16[(i<<1)+1] = stream16[i<<1] = (b1[i]*global_volume)/255;
00817       }
00818 
00819       for (i = 0; i < size2; ++i) {
00820         stream16[((i+size1)<<1)+1] = stream16[(i+size1)<<1] = (b2[i]*global_volume)/255;
00821       }
00822     }
00823     else {
00824 
00825       for (i = 0; i < size1; ++i) {
00826         stream16[i] = (b1[i]*global_volume)/255;
00827       }
00828 
00829       for (i = 0; i < size2; ++i) {
00830         stream16[i+size1] = (b2[i]*global_volume)/255;
00831       }
00832     }
00833   }
00834 
00835   // Unlock right first
00836   if (dual) buffer_right->Unlock();
00837 
00838   // Unlock it
00839   buffer->Unlock();
00840 
00841   // Unlock the mutex
00842   SDL_mutexV(mutex);
00843 
00844 }
00845 
00846 void FMOpl_Midi::GetSamples(uint32 samples)
00847 {
00848   // Got samples
00849   if (samples) {
00850 
00851     // Do the lock and generate the samples
00852     sint16* b1 = 0;
00853     sint16* b2 = 0;
00854     uint32  size1 = 0;
00855     uint32  size2 = 0;
00856 
00857     // Lock left
00858     buffer->LockWrite(samples, b1, size1, b2, size2);
00859 
00860     //  printf ("Generating %i samples\n", samples);
00861 
00862     if (size1 && b1) opl->generate_samples(b1, size1);
00863     if (size2 && b2) opl->generate_samples(b2, size2);
00864 
00865     if (dual) {
00866       // Lock it (right)
00867       buffer_right->LockWrite(samples, b1, size1, b2, size2);
00868 
00869       if (size1 && b1) opl_right->generate_samples(b1, size1);
00870       if (size2 && b2) opl_right->generate_samples(b2, size2);
00871 
00872       // Unlock (right)
00873       buffer_right->Unlock();
00874     }
00875 
00876     // Unlock left
00877     buffer->Unlock();
00878 
00879   }
00880 }
00881 
00882 void FMOpl_Midi::load_patches(bool force_xmidi)
00883 {
00884   // Lock the mutex
00885   SDL_mutexP(mutex);
00886 
00887   if (opl) opl->LoadMT32Bank(force_xmidi);
00888   if (opl_right) opl_right->LoadMT32Bank(force_xmidi);
00889 
00890   // Unlock the mutex
00891   SDL_mutexV(mutex);
00892 }
00893 
00894 bool FMOpl_Midi::is_fm_synth()
00895 {
00896   return true;
00897 }
00898 
00899 bool FMOpl_Midi::use_gs127() 
00900 { 
00901   return true; 
00902 }
00903 
00904 //
00905 // PSMDEX - Pentagram Streaming Midi Driver Extensions
00906 //
00907 
00908 int FMOpl_Midi::max_streams()
00909 {
00910   return 1;
00911 }
00912 
00913 void FMOpl_Midi::start_stream(int str_num, XMIDIEventList *eventlist, bool repeat, bool activate, int vol)
00914 {
00915   stop_track();
00916   set_volume(0, vol);
00917   start_track(eventlist, repeat);
00918 }
00919 
00920 void FMOpl_Midi::activate_stream(int str_num)
00921 {
00922 
00923 }
00924 
00925 void FMOpl_Midi::stop_stream(int str_num)
00926 {
00927   stop_track();
00928 }
00929 
00930 void FMOpl_Midi::set_volume(int str_num, int level)
00931 {
00932   if (!is_available) return;
00933 
00934   // Lock the mutex
00935   SDL_mutexP(mutex);
00936 
00937   // Set the volume
00938   global_volume = level;
00939 
00940   // Unlock the mutex
00941   SDL_mutexV(mutex);
00942 }
00943 
00944 bool FMOpl_Midi::is_playing(int str_num)
00945 {
00946   return is_playing();
00947 }
00948 
00949 int FMOpl_Midi::get_active()
00950 { 
00951   return 0;
00952 }
00953 
00954 /*
00955 
00956 what we need to do
00957 
00958   play notes and generate 5512 samples
00959 
00960   delay till we are past the amount of time it should have taken to generate those samples
00961 
00962   repeat
00963 
00964 what we will do for now
00965 
00966   play notes and generate 5513 samples (250ms)
00967 
00968   delay till we are 250ms ahead
00969 
00970   play notes and generate 5512 samples (250ms)
00971 
00972   delay till we are 250ms ahead
00973 
00974   repeat
00975 
00976 at audio->get_sample_rate() KHz there are 735 samples for every 4 ticks.
00977 at audio->get_sample_rate() KHz there are 147 samples for every 4/5 ticks
00978 at audio->get_sample_rate() KHz there are  98 samples for every 8/15 ticks
00979 at audio->get_sample_rate() KHz there are  49 samples for every 4/15 ticks
00980 
00981   The wmo clock 'should' be based on the 98 samples number. For every 98 samples,
00982   we increment the clock 15 units. Working out the midi tick is unit/8. The unit
00983   from the midi tick is tick*8
00984 
00985   I could base it on the 49 samples number too. For every 49 samples we increment
00986   the clock 15 units aslso. The midi tick being then is unit/4. The unit from the
00987   midi tick is tick*4
00988 
00989 
00990 The generate samples function:
00991 
00992   uint32 generate_sample (uint32 count_required) {
00993 
00994   uint32 amount_generated = 0;
00995 
00996   while (amount_generated < count_required) {
00997 
00998     while_event_loop();
00999 
01000     get_opls_to_generate_samples(NUM_SAMPLES_PER_PASS);
01001 
01002     amount_generated += NUM_SAMPLES_PER_PASS;
01003     wmoClockIncTime(TIME_PER_PASS);
01004   }
01005 
01006   return amount_generated;
01007   }
01008 
01009 */
01010 #endif //USE_FMOPL_MIDI

Generated on Mon Jul 9 14:42:40 2007 for ExultEngine by  doxygen 1.5.1