1 /*	SCCS Id: @(#)amisnd.c	3.2	2000/01/12*/
2 /* 	Copyright (c) 1992, 1993, 1995 by Gregg Wonderly */
3 /* NetHack may be freely redistributed.  See license for details. */
4 
5 /*
6  * This file contains music playing code.
7  *
8  * If we were REALLY determined, we would make the sound play
9  * asynchronously, but I'll save that one for a rainy day...
10  */
11 
12 #include "hack.h"
13 #include "dlb.h"
14 
15 #undef red
16 #undef blue
17 #undef green
18 #include <exec/types.h>
19 #include <exec/memory.h>
20 #include <exec/io.h>
21 #include <devices/audio.h>
22 #include <dos/dos.h>
23 #include <dos/dosextens.h>
24 #include <graphics/gfxbase.h>
25 
26 #include <proto/exec.h>
27 #include <clib/alib_protos.h>
28 #include <proto/dos.h>
29 #include <proto/graphics.h>
30 
31 #include <stdio.h>
32 #include <stdlib.h>
33 
34 #define	AMII_AVERAGE_VOLUME	60
35 
36 int amii_volume = AMII_AVERAGE_VOLUME;
37 
38 typedef struct VHDR
39 {
40 	char name[4];
41 	long len;
42 	unsigned long oneshot, repeat, samples;
43 	UWORD freq;
44 	UBYTE n_octaves, compress;
45 	LONG volume;
46 } VHDR;
47 
48 typedef struct IFFHEAD
49 {
50 	char FORM[4];
51 	long flen;
52 	char _8SVX[4];
53 	VHDR vhdr;
54 	char NAME[4];
55 	long namelen;
56 } IFFHEAD;
57 
58 extern struct GfxBase *GfxBase;
59 
60 UBYTE whichannel[] = { 1, 2, 4, 8 };
61 void makesound( char *, char *, int vol);
62 void amii_speaker( struct obj *instr, char *melody, int vol );
63 
64 /* A major scale in indexs to freqtab... */
65 int notetab[] = { 0, 2, 4, 5, 7, 9, 11, 12 };
66 
67 /* Frequencies for a scale starting at one octave below 'middle C' */
68 long freqtab[] = {
69 	220,	/*A */
70 	233,	/*Bb*/
71 	246,	/*B */
72 	261,	/*C */
73 	277,	/*Db*/
74 	293,	/*D */
75 	311,	/*Eb*/
76 	329,	/*E */
77 	349,	/*F */
78 	370,	/*Gb*/
79 	392,	/*G */
80 	415,	/*Ab*/
81 	440,	/*A */
82 };
83 
84 #ifdef	TESTING
main(argc,argv)85 main( argc, argv )
86 	int argc;
87 	char **argv;
88 {
89 	makesound( "wooden_flute", "AwBwCwDwEwFwGwawbwcwdwewfwgw", 60 );
90 	makesound( "wooden_flute", "AhBhChDhEhFhGhahbhchdhehfhgh", 60 );
91 	makesound( "wooden_flute", "AqBqCqDqEqFqGqaqbqcqdqeqfqgq", 60 );
92 	makesound( "wooden_flute", "AeBeCeDeEeFeGeaebecedeeefege", 60 );
93 	makesound( "wooden_flute", "AxBxCxDxExFxGxaxbxcxdxexfxgx", 60 );
94 	makesound( "wooden_flute", "AtBtCtDtEtFtGtatbtctdtetftgt", 60 );
95 	makesound( "wooden_flute", "AtBtCtDtEtFtGtatbtctdtetftgt", 60 );
96 	makesound( "wooden_flute", "AtBtCtDtEtFtGtatbtctdtetftgt", 60 );
97 	makesound( "wooden_flute", "AtBtCtDtEtFtGtatbtctdtetftgt", 60 );
98 	makesound( "wooden_flute", "AtBtCtDtEtFtGtatbtctdtetftgt", 60 );
99 	makesound( "wooden_flute", "AtBtCtDtEtFtGtatbtctdtetftgt", 60 );
100 	makesound( "wooden_flute", "AtBtCtDtEtFtGtatbtctdtetftgt", 60 );
101 	makesound( "wooden_flute", "AtBtCtDtEtFtGtatbtctdtetftgt", 60 );
102 	makesound( "wooden_flute", "AtBtCtDtEtFtGtatbtctdtetftgt", 60 );
103 }
104 #else
105 void
amii_speaker(struct obj * instr,char * melody,int vol)106 amii_speaker( struct obj *instr, char *melody, int vol )
107 {
108 	int typ = instr->otyp;
109 	char * actualn = (char *)OBJ_NAME( objects[typ] ) ;
110 
111 	/* Make volume be relative to users volume level, with 60 being the
112 	 * average level that will be passed to us.
113 	 */
114 	vol = vol * amii_volume / AMII_AVERAGE_VOLUME;
115 
116 	makesound( actualn, melody, vol );
117 }
118 #endif
119 
120 void
makesound(char * actualn,char * melody,int vol)121 makesound ( char *actualn , char * melody, int vol )
122 {
123 	char *t;
124 	int c, cycles, dot, dlay;
125 	dlb *stream = 0;
126 	IFFHEAD iffhead;
127 	struct IOAudio *AudioIO = 0;
128 	struct MsgPort *AudioMP = 0;
129 	struct Message *AudioMSG = 0;
130 	ULONG device = -1;
131 	BYTE *waveptr = 0;
132 	LONG frequency=440, duration=1, clock, samp, samples, samcyc=1;
133 	unsigned char name [ 100 ] ;
134 
135 	if ( flags.silent )
136 		return;
137 
138 	if( GfxBase->DisplayFlags & PAL )
139 		clock = 3546895;
140 	else
141 		clock = 3579545;
142 
143 	/*
144 	 * Convert type to file name - if there's nothing to play we
145 	 * shouldn't be here in the first place.
146 	 */
147 	strncpy(name, actualn,sizeof(name) ) ;
148 	for( t = strchr( name, ' ' ); t; t = strchr( name, ' ' ) )
149 		*t = '_';
150 	if( (stream = dlb_fopen( name, "r" )) == NULL )
151 	{
152 	    perror( name );
153 	    return;
154 	}
155 
156 	AudioIO = (struct IOAudio *)
157 		AllocMem( sizeof( struct IOAudio ), MEMF_PUBLIC|MEMF_CLEAR );
158 	if( AudioIO == 0 )
159 		goto killaudio;
160 
161 	AudioMP = CreatePort( NULL, 0 );
162 	if( AudioMP == 0 )
163 		goto killaudio;
164 
165 	AudioIO->ioa_Request.io_Message.mn_ReplyPort = AudioMP;
166 	AudioIO->ioa_Request.io_Message.mn_Node.ln_Pri = 0;
167 	AudioIO->ioa_Request.io_Command = ADCMD_ALLOCATE;
168 	AudioIO->ioa_Request.io_Flags = ADIOF_NOWAIT;
169 	AudioIO->ioa_AllocKey = 0;
170 	AudioIO->ioa_Data = whichannel;
171 	AudioIO->ioa_Length = sizeof( whichannel );
172 
173 	device = OpenDevice( AUDIONAME, 0L, (struct IORequest *)AudioIO, 0L );
174 	if( device != 0 )
175 		goto killaudio;
176 
177 	if( dlb_fread( (genericptr_t)&iffhead, sizeof( iffhead ), 1, stream ) != 1 )
178 		goto killaudio;
179 
180 	/* This is an even number of bytes long */
181 	if( dlb_fread( name, (iffhead.namelen+1) & ~1, 1, stream ) != 1 )
182 		goto killaudio;
183 
184 	if( dlb_fread( (genericptr_t)&samples, 4, 1, stream ) != 1 )
185 		goto killaudio;
186 
187 	if( dlb_fread( (genericptr_t)&samples, 4, 1, stream ) != 1 )
188 		goto killaudio;
189 
190 	waveptr = AllocMem( samples, MEMF_CHIP|MEMF_PUBLIC );
191 	if( !waveptr )
192 		goto killaudio;
193 
194 	if( dlb_fread( waveptr, samples, 1, stream ) != 1 )
195 		goto killaudio;
196 
197 	while( melody[0] && melody[1] )
198 	{
199 		c = *melody++;
200 		duration = *melody++;
201 		dot = 0;
202 		if( *melody == '.' )
203 		{
204 			dot = 1;
205 			++melody;
206 		}
207 		switch( duration )
208 		{
209 			case 'w': dlay = 3; duration = 1; cycles = 1; break;
210 			case 'h': dlay = 3; duration = 2; cycles = 1; break;
211 			case 'q': dlay = 2; duration = 4; cycles = 1; break;
212 			case 'e': dlay = 1; duration = 8; cycles = 1; break;
213 			case 'x': dlay = 0; duration = 16; cycles = 1; break;
214 			case 't': dlay = 0; duration = 32; cycles = 1; break;
215 			default: goto killaudio;  /* unrecognized duration */
216 		}
217 
218 		/* Lower case characters are one octave above upper case */
219 		switch( c )
220 		{
221 			case 'a': case 'b': case 'c':
222 			case 'd': case 'e': case 'f': case 'g':
223 				c -= 'a' - 7;
224 				break;
225 
226 			case 'A': case 'B': case 'C':
227 			case 'D': case 'E': case 'F': case 'G':
228 				c -= 'A';
229 				break;
230 
231 			default:
232 				continue;
233 		}
234 
235 		samcyc = samples;
236 
237 		/* lowercase start at middle 'C', upper case are one octave below */
238 		frequency = c > 7 ? freqtab[notetab[c%7]]*2 : freqtab[notetab[c]];
239 
240 		/* We can't actually do a dotted whole note unless we add code for a real
241 		 * 8SVX sample which includes sustain sample information to tell us how
242 		 * to hold the note steady...  So when duration == 1, ignore 'dot'...
243 		 */
244 		if( dot && duration > 1 )
245 			samp = ((samples / duration) * 3) / 2;
246 		else
247 			samp = samples / duration;
248 
249 		/* Only use some of the samples based on frequency */
250 		samp = frequency * samp / 880;
251 
252 		/* The 22khz samples are middle C samples, so adjust the play
253 		 * back frequency accordingly
254 		 */
255 		frequency = (frequency * (iffhead.vhdr.freq*2)/3) / 440L;
256 
257 		AudioIO->ioa_Request.io_Message.mn_ReplyPort = AudioMP;
258 		AudioIO->ioa_Request.io_Command = CMD_WRITE;
259 		AudioIO->ioa_Request.io_Flags = ADIOF_PERVOL;
260 		AudioIO->ioa_Data = (BYTE *)waveptr;
261 		AudioIO->ioa_Length = samp;
262 
263 		/* The clock rate represents the unity rate, so dividing by
264 		 * the frequency gives us a period ratio...
265 		 */
266 /*printf( "clock: %ld, freq: %ld, div: %ld\n", clock, frequency, clock/frequency );*/
267 		AudioIO->ioa_Period = clock/frequency;
268 		AudioIO->ioa_Volume = vol;
269 		AudioIO->ioa_Cycles = cycles;
270 
271 		BeginIO( (struct IORequest *)AudioIO );
272 		WaitPort( AudioMP );
273 		AudioMSG = GetMsg( AudioMP );
274 		if( dlay )
275 			Delay( dlay );
276 	}
277 
278 	killaudio:
279 	if( stream ) dlb_fclose( stream );
280 	if( waveptr ) FreeMem( waveptr, samples );
281 	if( device == 0 ) CloseDevice( (struct IORequest *)AudioIO );
282 	if( AudioMP ) DeletePort( AudioMP );
283 	if( AudioIO ) FreeMem( AudioIO, sizeof( *AudioIO ) );
284 }
285