ResidualVM logo ResidualVM website - Forums - Contact us BuildBot - Doxygen - Wiki curved edge

windows.cpp

Go to the documentation of this file.
00001 /* ScummVM - Graphic Adventure Engine
00002  *
00003  * ScummVM is the legal property of its developers, whose names
00004  * are too numerous to list here. Please refer to the COPYRIGHT
00005  * file distributed with this source distribution.
00006  *
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License
00009  * as published by the Free Software Foundation; either version 2
00010  * of the License, or (at your option) any later version.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00020  *
00021  */
00022 
00023 // Disable symbol overrides so that we can use system headers.
00024 #define FORBIDDEN_SYMBOL_ALLOW_ALL
00025 
00026 #include "common/scummsys.h"
00027 
00028 #if defined(WIN32) && !defined(_WIN32_WCE)
00029 
00030 #define WIN32_LEAN_AND_MEAN
00031 #include <windows.h>
00032 
00033 #include "audio/musicplugin.h"
00034 #include "audio/mpu401.h"
00035 #include "common/config-manager.h"
00036 #include "common/translation.h"
00037 #include "common/textconsole.h"
00038 #include "common/error.h"
00039 
00040 #include <mmsystem.h>
00041 
00043 //
00044 // Windows MIDI driver
00045 //
00047 
00048 class MidiDriver_WIN : public MidiDriver_MPU401 {
00049 private:
00050     MIDIHDR _streamHeader;
00051     byte _streamBuffer[266];    // SysEx blocks should be no larger than 266 bytes
00052     HANDLE _streamEvent;
00053     HMIDIOUT _mo;
00054     bool _isOpen;
00055     int _device;
00056 
00057     void check_error(MMRESULT result);
00058 
00059 public:
00060     MidiDriver_WIN(int deviceIndex) : _isOpen(false), _device(deviceIndex) { }
00061     int open();
00062     bool isOpen() const { return _isOpen; }
00063     void close();
00064     void send(uint32 b);
00065     void sysEx(const byte *msg, uint16 length);
00066 };
00067 
00068 int MidiDriver_WIN::open() {
00069     if (_isOpen)
00070         return MERR_ALREADY_OPEN;
00071 
00072     _streamEvent = CreateEvent(NULL, true, true, NULL);
00073     MMRESULT res = midiOutOpen((HMIDIOUT *)&_mo, _device, (DWORD_PTR)_streamEvent, 0, CALLBACK_EVENT);
00074     if (res != MMSYSERR_NOERROR) {
00075         check_error(res);
00076         CloseHandle(_streamEvent);
00077         return MERR_DEVICE_NOT_AVAILABLE;
00078     }
00079 
00080     _isOpen = true;
00081     return 0;
00082 }
00083 
00084 void MidiDriver_WIN::close() {
00085     if (!_isOpen)
00086         return;
00087     _isOpen = false;
00088     MidiDriver_MPU401::close();
00089     midiOutUnprepareHeader(_mo, &_streamHeader, sizeof(_streamHeader));
00090     check_error(midiOutClose(_mo));
00091     CloseHandle(_streamEvent);
00092 }
00093 
00094 void MidiDriver_WIN::send(uint32 b) {
00095     assert(_isOpen);
00096 
00097     union {
00098         DWORD dwData;
00099         BYTE bData[4];
00100     } u;
00101 
00102     u.bData[3] = (byte)((b & 0xFF000000) >> 24);
00103     u.bData[2] = (byte)((b & 0x00FF0000) >> 16);
00104     u.bData[1] = (byte)((b & 0x0000FF00) >> 8);
00105     u.bData[0] = (byte)(b & 0x000000FF);
00106 
00107     check_error(midiOutShortMsg(_mo, u.dwData));
00108 }
00109 
00110 void MidiDriver_WIN::sysEx(const byte *msg, uint16 length) {
00111     if (!_isOpen)
00112         return;
00113 
00114     if (WaitForSingleObject (_streamEvent, 2000) == WAIT_TIMEOUT) {
00115         warning ("Could not send SysEx - MMSYSTEM is still trying to send data");
00116         return;
00117     }
00118 
00119     assert(length+2 <= 266);
00120 
00121     midiOutUnprepareHeader(_mo, &_streamHeader, sizeof(_streamHeader));
00122 
00123     // Add SysEx frame
00124     _streamBuffer[0] = 0xF0;
00125     memcpy(&_streamBuffer[1], msg, length);
00126     _streamBuffer[length+1] = 0xF7;
00127 
00128     _streamHeader.lpData = (char *)_streamBuffer;
00129     _streamHeader.dwBufferLength = length + 2;
00130     _streamHeader.dwBytesRecorded = length + 2;
00131     _streamHeader.dwUser = 0;
00132     _streamHeader.dwFlags = 0;
00133 
00134     MMRESULT result = midiOutPrepareHeader(_mo, &_streamHeader, sizeof(_streamHeader));
00135     if (result != MMSYSERR_NOERROR) {
00136         check_error (result);
00137         return;
00138     }
00139 
00140     ResetEvent(_streamEvent);
00141     result = midiOutLongMsg(_mo, &_streamHeader, sizeof(_streamHeader));
00142     if (result != MMSYSERR_NOERROR) {
00143         check_error(result);
00144         SetEvent(_streamEvent);
00145         return;
00146     }
00147 }
00148 
00149 void MidiDriver_WIN::check_error(MMRESULT result) {
00150     char buf[200];
00151     if (result != MMSYSERR_NOERROR) {
00152         midiOutGetErrorText(result, buf, 200);
00153         warning("MM System Error '%s'", buf);
00154     }
00155 }
00156 
00157 
00158 // Plugin interface
00159 
00160 class WindowsMusicPlugin : public MusicPluginObject {
00161 public:
00162     const char *getName() const {
00163         return _s("Windows MIDI");
00164     }
00165 
00166     const char *getId() const {
00167         return "windows";
00168     }
00169 
00170     MusicDevices getDevices() const;
00171     Common::Error createInstance(MidiDriver **mididriver, MidiDriver::DeviceHandle = 0) const;
00172 };
00173 
00174 MusicDevices WindowsMusicPlugin::getDevices() const {
00175     MusicDevices devices;
00176     int numDevs = midiOutGetNumDevs();
00177     MIDIOUTCAPS tmp;
00178 
00179     Common::StringArray deviceNames;
00180     for (int i = 0; i < numDevs; i++) {
00181         if (midiOutGetDevCaps(i, &tmp, sizeof(MIDIOUTCAPS)) != MMSYSERR_NOERROR)
00182             break;
00183         deviceNames.push_back(tmp.szPname);
00184     }
00185 
00186     // Limit us to the number of actually retrieved devices.
00187     numDevs = deviceNames.size();
00188 
00189     // Check for non-unique device names. This may happen if someone has devices with identical
00190     // names (e. g. more than one USB device of the exact same hardware type). It seems that this
00191     // does happen in reality sometimes. We generate index numbers for these devices.
00192     // This is not an ideal solution, since this index could change whenever another USB
00193     // device gets plugged in or removed, switched off or just plugged into a different port.
00194     // Unfortunately midiOutGetDevCaps() does not generate any other unique information
00195     // that could be used. Our index numbers which match the device order should at least be
00196     // a little more stable than just using the midiOutGetDevCaps() device ID, since a missing
00197     // device (e.g. switched off) should actually not be harmful to our indices (as it would be
00198     // when using the device IDs). The cases where users have devices with identical names should
00199     // be rare enough anyway.
00200     Common::Array<int> nonUniqueIndex;
00201     for (int i = 0; i < numDevs; i++) {
00202         int match = -1;
00203         for (int ii = 0; ii < i; ii++) {
00204             if (deviceNames[i] == deviceNames[ii]) {
00205                 if (nonUniqueIndex[ii] == -1)
00206                     nonUniqueIndex[ii] = 0;
00207                 if (++match == 0)
00208                     ++match;
00209             }
00210         }
00211         nonUniqueIndex.push_back(match);
00212     }
00213 
00214     // We now add the index number to the non-unique device names to make them unique.
00215     for (int i = 0; i < numDevs; i++) {
00216         if (nonUniqueIndex[i] != -1)
00217             deviceNames[i] = Common::String::format("%s - #%.02d", deviceNames[i].c_str(), nonUniqueIndex[i]);
00218     }
00219 
00220     for (Common::StringArray::iterator i = deviceNames.begin(); i != deviceNames.end(); ++i)
00221         // There is no way to detect the "MusicType" so I just set it to MT_GM
00222         // The user will have to manually select his MT32 type device and his GM type device.
00223         devices.push_back(MusicDevice(this, *i, MT_GM));
00224 
00225     return devices;
00226 }
00227 
00228 Common::Error WindowsMusicPlugin::createInstance(MidiDriver **mididriver, MidiDriver::DeviceHandle dev) const {
00229     int devIndex = 0;
00230     bool found = false;
00231 
00232     if (dev) {
00233         MusicDevices i = getDevices();
00234         for (MusicDevices::iterator d = i.begin(); d != i.end(); d++) {
00235             if (d->getCompleteId().equals(MidiDriver::getDeviceString(dev, MidiDriver::kDeviceId))) {
00236                 found = true;
00237                 break;
00238             }
00239             devIndex++;
00240         }
00241     }
00242 
00243     *mididriver = new MidiDriver_WIN(found ? devIndex : 0);
00244     return Common::kNoError;
00245 }
00246 
00247 //#if PLUGIN_ENABLED_DYNAMIC(WINDOWS)
00248     //REGISTER_PLUGIN_DYNAMIC(WINDOWS, PLUGIN_TYPE_MUSIC, WindowsMusicPlugin);
00249 //#else
00250     REGISTER_PLUGIN_STATIC(WINDOWS, PLUGIN_TYPE_MUSIC, WindowsMusicPlugin);
00251 //#endif
00252 
00253 #endif


Generated on Sat Jun 22 2019 05:00:38 for ResidualVM by doxygen 1.7.1
curved edge   curved edge