• R/O
  • SSH
  • HTTPS

mdr: Commit


Commit MetaInfo

Revision104 (tree)
Time2023-10-15 04:46:27
Authormateuszviste

Log Message

added the mdr_opl_imfeasy_ interface

Change Summary

Incremental Difference

--- trunk/inc/mdr/opl.h (revision 103)
+++ trunk/inc/mdr/opl.h (revision 104)
@@ -161,19 +161,50 @@
161161 * It is possible to mix IMF playback calls with manual notes, but you must *
162162 * take care to use only voices not used by your IMF audio. Typically games *
163163 * tend to use the voice #0 for sound effects and voices #1 to #8 for music. *
164- ****************************************************************************/
164+ * *
165+ * The IMF API comes in two version: the normal one, or "imfeasy". The easy *
166+ * version is easier to use, but requires to have the entire IMF audio file *
167+ * loaded in memory, while the normal (non-easy) allows for more flexibility *
168+ * in this regard, potentially allowing for playback of huge IMF files. *
169+ *****************************************************************************/
165170
171+/*** EASY INTERFACE ***/
172+
173+/* playback initialization, easy interface. imf points to the start of the IMF
174+ * file. The imf pointer must not be freed as long as playback is ongoing.
175+ * imflength is the size (in bytes) of the IMF data.
176+ * clock must be an incrementing value that wraps to 0 after 65535. The clock
177+ * speed will control the playback's tempo.
178+ * loopscount tells how many times the song will have to be looped (0 means
179+ * "loop forever").
180+ * returns 0 on success, non-zero otherwise. */
181+int mdr_opl_imfeasy_init(void *imf, unsigned short imflength, unsigned short clock, unsigned char loopscount);
182+
183+/* Playback of an IMF file preloaded via mdr_opl_imfeasy_init(). This function
184+ * must be called repeatedly at a high frequency for best playback quality.
185+ * Returns 0 on success, 1 if playback ended, -1 on error. */
186+int mdr_opl_imfeasy_play(unsigned short clock);
187+
188+/*** ADVANCED INTERFACE ***/
189+
166190 /* playback initialization, this function must be called immediately before
167191 * playback. imf points to the start of the IMF file and must contain at least
168192 * the first 6 bytes of the audio file.
169193 * clock must be an incrementing value that wraps to 0 after 65535.
170194 * the clock speed will control the playback's tempo.
171- * returns the amount of consumed bytes (4 or 6) */
195+ * returns the amount of consumed bytes (0, 4 or 6) */
172196 unsigned short mdr_opl_imf_init(void *imf, unsigned short clock);
173197
174-/* feeds the IMF playback routine with IMF data. returns the amount of bytes
175- * that have been consumed. this function must be called repeatedly at a high
176- * frequency for best playback quality. */
198+/* Playback, advanced version. Feeds the IMF playback routine with IMF data.
199+ * Returns the amount of bytes that have been consumed, hence the next call
200+ * should provide an imf pointer advanced by this many bytes (and imflen
201+ * decreased accordingly). Such approach might not be the most intuitive, but
202+ * it allows to load an imf song partially and provide only short chunks of
203+ * data for playback instead of having to buffer the entire song in memory.
204+ * For a simpler call that requires to buffer the entire IMF file in memory,
205+ * see mdr_opl_imf_playeasy().
206+ * This function must be called repeatedly at a high frequency for best
207+ * playback quality. */
177208 unsigned short mdr_opl_imf_play(void *imf, unsigned short imflen, unsigned short clock);
178209
179210 #endif
--- trunk/opl/opl-imf.c (revision 103)
+++ trunk/opl/opl-imf.c (nonexistent)
@@ -1,100 +0,0 @@
1-/*
2- * IMF reader/player
3- *
4- * This file is part of the Mateusz' DOS Routines (MDR): http://mdr.osdn.io
5- * Published under the terms of the MIT License, as stated below.
6- *
7- * Copyright (C) 2023 Mateusz Viste
8- *
9- * Permission is hereby granted, free of charge, to any person obtaining a copy
10- * of this software and associated documentation files (the "Software"), to
11- * deal in the Software without restriction, including without limitation the
12- * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
13- * sell copies of the Software, and to permit persons to whom the Software is
14- * furnished to do so, subject to the following conditions:
15- *
16- * The above copyright notice and this permission notice shall be included in
17- * all copies or substantial portions of the Software.
18- *
19- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
25- * IN THE SOFTWARE.
26- */
27-
28-#include <mdr/bios.h> /* mdr_bios_ticks() */
29-#include <mdr/opl.h>
30-
31-/* the IMF file format is a raw stream of OPL register-value-timing tuples.
32- * each tuple is 4 bytes long and consists of:
33- *
34- * unsigned char ; ADLIB REGISTER
35- * unsigned char ; ADLIB VALUE
36- * unsigned short ; DELAY
37- *
38- * The DELAY is provided as a number of "ticks", while the length of a tick
39- * is undefined and must be known in advance by the IMF-playing implementation.
40- * Commonly found values in games are 560 and 700 Hz.
41- */
42-
43-
44-struct IMFCHUNK {
45- unsigned char adreg;
46- unsigned char adval;
47- unsigned short delay;
48-};
49-
50-static unsigned short next_wakeup = 0;
51-
52-
53-/* playback initialization, this function must be called immediately before
54- * playback. imf points to the start of the IMF file and must contain at least
55- * the first 6 bytes of the audio file.
56- * clock must be an incrementing value that wraps to 0 after 65535.
57- * the clock speed will control the playback's tempo.
58- * returns the amount of consumed bytes (0, 4 or 6) */
59-unsigned short mdr_opl_imf_init(void *imf, unsigned short clock) {
60- next_wakeup = clock;
61- if (((short *)imf)[0] == 0) { /* type-0 IMF (no header, only the 4-bytes zeroed 1st instruction) */
62- return(4);
63- } else if (((short *)imf)[1] == 0) { /* type-1 (2-bytes header) */
64- return(6);
65- } else {
66- return(0);
67- }
68-}
69-
70-
71-/* feeds the IMF playback routine with IMF data. returns the amount of bytes
72- * that have been consumed. this function must be called repeatedly at a high
73- * frequency for best playback quality. */
74-unsigned short mdr_opl_imf_play(void *imf, unsigned short imflen, unsigned short clock) {
75- unsigned short consumedbytes = 0;
76- struct IMFCHUNK *imfnode = imf;
77-
78- while (imflen >= 4) {
79-
80- /* it's not time yet (unless timer wraped) */
81- if (clock < next_wakeup) {
82- /* detect time wrap */
83- if (next_wakeup - clock < 16384) return(consumedbytes);
84- } else if (clock - next_wakeup > 16384) {
85- return(consumedbytes);
86- }
87-
88- /* ignore writes to reg 0: such register does not exist on OPL2, it is
89- * likely some kind of marker in the IMF file */
90- if (imfnode->adreg != 0) mdr_opl_regwr(imfnode->adreg, imfnode->adval);
91-
92- next_wakeup += imfnode->delay;
93-
94- imfnode++;
95- consumedbytes += 4;
96- imflen -= 4;
97- }
98-
99- return(consumedbytes);
100-}
--- trunk/opl/oplimf.c (nonexistent)
+++ trunk/opl/oplimf.c (revision 104)
@@ -0,0 +1,106 @@
1+/*
2+ * IMF reader/player
3+ *
4+ * This file is part of the Mateusz' DOS Routines (MDR): http://mdr.osdn.io
5+ * Published under the terms of the MIT License, as stated below.
6+ *
7+ * Copyright (C) 2023 Mateusz Viste
8+ *
9+ * Permission is hereby granted, free of charge, to any person obtaining a copy
10+ * of this software and associated documentation files (the "Software"), to
11+ * deal in the Software without restriction, including without limitation the
12+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
13+ * sell copies of the Software, and to permit persons to whom the Software is
14+ * furnished to do so, subject to the following conditions:
15+ *
16+ * The above copyright notice and this permission notice shall be included in
17+ * all copies or substantial portions of the Software.
18+ *
19+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
25+ * IN THE SOFTWARE.
26+ */
27+
28+#include <mdr/opl.h>
29+
30+/* the IMF file format is a raw stream of OPL register-value-timing tuples.
31+ * each tuple is 4 bytes long and consists of:
32+ *
33+ * unsigned char ; ADLIB REGISTER
34+ * unsigned char ; ADLIB VALUE
35+ * unsigned short ; DELAY
36+ *
37+ * The DELAY is provided as a number of "ticks", while the length of a tick
38+ * is undefined and must be known in advance by the IMF-playing implementation.
39+ * Commonly found values in games are 560 and 700 Hz.
40+ */
41+
42+
43+struct IMFCHUNK {
44+ unsigned char adreg;
45+ unsigned char adval;
46+ unsigned short delay;
47+};
48+
49+static unsigned short next_wakeup = 0;
50+
51+
52+/* playback initialization, this function must be called immediately before
53+ * playback. imf points to the start of the IMF file and must contain at least
54+ * the first 6 bytes of the audio file.
55+ * clock must be an incrementing value that wraps to 0 after 65535.
56+ * the clock speed will control the playback's tempo.
57+ * returns the amount of consumed bytes (0, 4 or 6) */
58+unsigned short mdr_opl_imf_init(void *imf, unsigned short clock) {
59+ next_wakeup = clock;
60+ if (((short *)imf)[0] == 0) { /* type-0 IMF (no header, only the 4-bytes zeroed 1st instruction) */
61+ return(4);
62+ } else if (((short *)imf)[1] == 0) { /* type-1 (2-bytes header) */
63+ return(6);
64+ } else {
65+ return(0);
66+ }
67+}
68+
69+
70+/* Playback, advanced version. Feeds the IMF playback routine with IMF data.
71+ * Returns the amount of bytes that have been consumed, hence the next call
72+ * should provide an imf pointer advanced by this many bytes (and imflen
73+ * decreased accordingly). Such approach might not be the most intuitive, but
74+ * it allows to load an imf song partially and provide only short chunks of
75+ * data for playback instead of having to buffer the entire song in memory.
76+ * For a simpler call that requires to buffer the entire IMF file in memory,
77+ * see mdr_opl_imf_playeasy().
78+ * This function must be called repeatedly at a high frequency for best
79+ * playback quality. */
80+unsigned short mdr_opl_imf_play(void *imf, unsigned short imflen, unsigned short clock) {
81+ unsigned short consumedbytes = 0;
82+ struct IMFCHUNK *imfnode = imf;
83+
84+ while (imflen >= 4) {
85+
86+ /* it's not time yet (unless timer wraped) */
87+ if (clock < next_wakeup) {
88+ /* detect time wrap */
89+ if (next_wakeup - clock < 16384) return(consumedbytes);
90+ } else if (clock - next_wakeup > 16384) {
91+ return(consumedbytes);
92+ }
93+
94+ /* ignore writes to reg 0: such register does not exist on OPL2, it is
95+ * likely some kind of marker in the IMF file */
96+ if (imfnode->adreg != 0) mdr_opl_regwr(imfnode->adreg, imfnode->adval);
97+
98+ next_wakeup += imfnode->delay;
99+
100+ imfnode++;
101+ consumedbytes += 4;
102+ imflen -= 4;
103+ }
104+
105+ return(consumedbytes);
106+}
--- trunk/opl/oplimfez.c (nonexistent)
+++ trunk/opl/oplimfez.c (revision 104)
@@ -0,0 +1,79 @@
1+/*
2+ * IMF reader/player (easy interface, but requires to buffer the entire IMF)
3+ *
4+ * This file is part of the Mateusz' DOS Routines (MDR): http://mdr.osdn.io
5+ * Published under the terms of the MIT License, as stated below.
6+ *
7+ * Copyright (C) 2023 Mateusz Viste
8+ *
9+ * Permission is hereby granted, free of charge, to any person obtaining a copy
10+ * of this software and associated documentation files (the "Software"), to
11+ * deal in the Software without restriction, including without limitation the
12+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
13+ * sell copies of the Software, and to permit persons to whom the Software is
14+ * furnished to do so, subject to the following conditions:
15+ *
16+ * The above copyright notice and this permission notice shall be included in
17+ * all copies or substantial portions of the Software.
18+ *
19+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
25+ * IN THE SOFTWARE.
26+ */
27+
28+#include <mdr/opl.h>
29+
30+static unsigned char *imfptrorg;
31+static unsigned short imflenorg;
32+static unsigned char *imfptr;
33+static unsigned short imflen;
34+static unsigned char loops;
35+
36+/* playback initialization, easy interface. imf points to the start of the IMF
37+ * file. The imf pointer must not be freed as long as playback is ongoing.
38+ * imflength is the size (in bytes) of the IMF data.
39+ * clock must be an incrementing value that wraps to 0 after 65535. The clock
40+ * speed will control the playback's tempo.
41+ * loopscount tells how many times the song will have to be looped (0 means
42+ * "loop forever").
43+ * returns 0 on success, non-zero otherwise. */
44+int mdr_opl_imfeasy_init(void *imf, unsigned short imflength, unsigned short clock, unsigned char loopscount) {
45+ unsigned short bytes;
46+ if ((imflength < 8) || (imf == (void *)0)) return(-1);
47+
48+ imflenorg = imflength;
49+ loops = loopscount;
50+
51+ bytes = mdr_opl_imf_init(imf, clock);
52+ imfptrorg = ((unsigned char *)imf) + bytes;
53+ imflenorg = imflength - bytes;
54+ imfptr = imfptrorg;
55+ imflen = imflenorg;
56+ return(0);
57+}
58+
59+
60+/* Playback of an IMF file preloaded via mdr_opl_imfeasy_init(). This function
61+ * must be called repeatedly at a high frequency for best playback quality.
62+ * Returns 0 on success, 1 if playback ended, -1 on error. */
63+int mdr_opl_imfeasy_play(unsigned short clock) {
64+ unsigned short bytes;
65+
66+ if (imflen < 3) {
67+ if (imflenorg == 0) return(-1);
68+ if (loops == 1) return(1);
69+ if (loops > 0) loops--;
70+ imfptr = imfptrorg;
71+ imflen = imflenorg;
72+ }
73+
74+ bytes = mdr_opl_imf_play(imfptr, imflen, clock);
75+ imflen -= bytes;
76+ imfptr += bytes;
77+
78+ return(0);
79+}
--- trunk/readme.txt (revision 103)
+++ trunk/readme.txt (revision 104)
@@ -19,6 +19,7 @@
1919 DOS functions interacting with DOS
2020 KEYB basic functions to interact with the keyboard
2121 MOUSE mouse routines
22+OPL OPL2 (Adlib style) audio
2223 PCX parsing, loading and uncompressing PCX images
2324 RS232 writing to and reading from an RS-232 ("COM") port
2425 SBDIGI playing digitized sounds with a SoundBlaster-compatible card
Show on old repository browser