1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 /*
24  * Raw output support by Michael Pearce
25  * Alsa support by Nicolas Noble <nicolas@nobis-crew.org> copied from
26  *    both the QuickTime support and (vkeybd http://www.alsa-project.org/~iwai/alsa.html)
27  */
28 
29 // Disable symbol overrides so that we can use system headers.
30 #define FORBIDDEN_SYMBOL_ALLOW_ALL
31 
32 #include "common/scummsys.h"
33 
34 #if defined(USE_SEQ_MIDI)
35 
36 #include "common/error.h"
37 #include "common/textconsole.h"
38 #include "common/util.h"
39 #include "audio/musicplugin.h"
40 #include "audio/mpu401.h"
41 
42 #include <fcntl.h>
43 #include <unistd.h>
44 #include <errno.h>
45 
46 ////////////////////////////////////////
47 //
48 // Unix dev/sequencer driver
49 //
50 ////////////////////////////////////////
51 
52 #define SEQ_MIDIPUTC 5
53 
54 class MidiDriver_SEQ : public MidiDriver_MPU401 {
55 public:
56 	MidiDriver_SEQ();
57 	int open();
isOpen() const58 	bool isOpen() const { return _isOpen; }
59 	void close();
60 	void send(uint32 b);
61 	void sysEx(const byte *msg, uint16 length);
62 
63 private:
64 	bool _isOpen;
65 	int device, _device_num;
66 };
67 
MidiDriver_SEQ()68 MidiDriver_SEQ::MidiDriver_SEQ() {
69 	_isOpen = false;
70 	device = 0;
71 	_device_num = 0;
72 }
73 
open()74 int MidiDriver_SEQ::open() {
75 	char *device_name;
76 	char dev_seq[] = "/dev/sequencer";
77 
78 	if (_isOpen)
79 		return MERR_ALREADY_OPEN;
80 	_isOpen = true;
81 	device = 0;
82 
83 	device_name = getenv("RESIDUALVM_MIDI");
84 
85 	if (device_name == NULL) {
86 		warning("RESIDUALVM_MIDI environment variable not set, using /dev/sequencer");
87 		device_name = dev_seq;
88 	}
89 
90 	device = ::open((device_name), O_RDWR, 0);
91 
92 	if (device < 0) {
93 		warning("Cannot open rawmidi device %s - using /dev/null (no music will be heard)",
94 					device_name);
95 		device = (::open(("/dev/null"), O_RDWR, 0));
96 		if (device < 0)
97 			error("Cannot open /dev/null to dump midi output");
98 	}
99 
100 	if (getenv("RESIDUALVM_MIDIPORT"))
101 		_device_num = atoi(getenv("RESIDUALVM_MIDIPORT"));
102 	return 0;
103 }
104 
close()105 void MidiDriver_SEQ::close() {
106 	MidiDriver_MPU401::close();
107 	::close(device);
108 	_isOpen = false;
109 }
110 
send(uint32 b)111 void MidiDriver_SEQ::send(uint32 b) {
112 	unsigned char buf[256];
113 	int position = 0;
114 
115 	switch (b & 0xF0) {
116 	case 0x80:
117 	case 0x90:
118 	case 0xA0:
119 	case 0xB0:
120 	case 0xE0:
121 		buf[position++] = SEQ_MIDIPUTC;
122 		buf[position++] = (unsigned char)b;
123 		buf[position++] = _device_num;
124 		buf[position++] = 0;
125 		buf[position++] = SEQ_MIDIPUTC;
126 		buf[position++] = (unsigned char)((b >> 8) & 0x7F);
127 		buf[position++] = _device_num;
128 		buf[position++] = 0;
129 		buf[position++] = SEQ_MIDIPUTC;
130 		buf[position++] = (unsigned char)((b >> 16) & 0x7F);
131 		buf[position++] = _device_num;
132 		buf[position++] = 0;
133 		break;
134 	case 0xC0:
135 	case 0xD0:
136 		buf[position++] = SEQ_MIDIPUTC;
137 		buf[position++] = (unsigned char)b;
138 		buf[position++] = _device_num;
139 		buf[position++] = 0;
140 		buf[position++] = SEQ_MIDIPUTC;
141 		buf[position++] = (unsigned char)((b >> 8) & 0x7F);
142 		buf[position++] = _device_num;
143 		buf[position++] = 0;
144 		break;
145 	default:
146 		warning("MidiDriver_SEQ::send: unknown: %08x", (int)b);
147 		break;
148 	}
149 	if (write(device, buf, position) == -1)
150 		warning("MidiDriver_SEQ::send: write failed (%s)", strerror(errno));
151 }
152 
sysEx(const byte * msg,uint16 length)153 void MidiDriver_SEQ::sysEx(const byte *msg, uint16 length) {
154 	unsigned char buf [266*4];
155 	int position = 0;
156 	const byte *chr = msg;
157 
158 	assert(length + 2 <= 266);
159 
160 	buf[position++] = SEQ_MIDIPUTC;
161 	buf[position++] = 0xF0;
162 	buf[position++] = _device_num;
163 	buf[position++] = 0;
164 	for (; length; --length, ++chr) {
165 		buf[position++] = SEQ_MIDIPUTC;
166 		buf[position++] = (unsigned char) *chr & 0x7F;
167 		buf[position++] = _device_num;
168 		buf[position++] = 0;
169 	}
170 	buf[position++] = SEQ_MIDIPUTC;
171 	buf[position++] = 0xF7;
172 	buf[position++] = _device_num;
173 	buf[position++] = 0;
174 
175 	if (write(device, buf, position) == -1)
176 		warning("MidiDriver_SEQ::send: write failed (%s)", strerror(errno));
177 }
178 
179 
180 // Plugin interface
181 
182 class SeqMusicPlugin : public MusicPluginObject {
183 public:
getName() const184 	const char *getName() const {
185 		return "SEQ";
186 	}
187 
getId() const188 	const char *getId() const {
189 		return "seq";
190 	}
191 
192 	MusicDevices getDevices() const;
193 	Common::Error createInstance(MidiDriver **mididriver, MidiDriver::DeviceHandle = 0) const;
194 };
195 
getDevices() const196 MusicDevices SeqMusicPlugin::getDevices() const {
197 	MusicDevices devices;
198 	// TODO: Return a different music type depending on the configuration
199 	// TODO: List the available devices
200 	devices.push_back(MusicDevice(this, "", MT_GM));
201 	return devices;
202 }
203 
createInstance(MidiDriver ** mididriver,MidiDriver::DeviceHandle) const204 Common::Error SeqMusicPlugin::createInstance(MidiDriver **mididriver, MidiDriver::DeviceHandle) const {
205 	*mididriver = new MidiDriver_SEQ();
206 
207 	return Common::kNoError;
208 }
209 
210 //#if PLUGIN_ENABLED_DYNAMIC(SEQ)
211 	//REGISTER_PLUGIN_DYNAMIC(SEQ, PLUGIN_TYPE_MUSIC, SeqMusicPlugin);
212 //#else
213 	REGISTER_PLUGIN_STATIC(SEQ, PLUGIN_TYPE_MUSIC, SeqMusicPlugin);
214 //#endif
215 
216 #endif
217