|
|
1.1 root 1: /*
1.1.1.5 root 2: Hatari - sound.c
3:
4: This file is distributed under the GNU Public License, version 2 or at
5: your option any later version. Read the file gpl.txt for details.
1.1 root 6:
1.1.1.11 root 7: This is where we emulate the YM2149. To obtain cycle-accurate timing we store
8: the current cycle time and this is incremented during each instruction.
9: When a write occurs in the PSG registers we take the difference in time and
10: generate this many samples using the previous register data.
11: Now we begin again from this point. To make sure we always have 1/50th of
12: samples we update the buffer generation every 1/50th second, just in case no
13: write took place on the PSG.
14: NOTE: If the emulator runs slower than 50fps it cannot update the buffers,
15: but the sound thread still needs some data to play to prevent a 'pop'. The
16: ONLY feasible solution is to play the same buffer again. I have tried all
17: kinds of methods to play the sound 'slower', but this produces un-even timing
18: in the sound and it simply doesn't work. If the emulator cannot keep the
19: speed, users will have to turn off the sound - that's it.
1.1.1.12 root 20:
21: The new version of the sound core uses some code/ideas from the following GPL projects :
1.1.1.15 root 22: - tone and noise steps computations are from StSound 1.2 by Arnaud Carré (Leonard/Oxygene)
1.1.1.12 root 23: - 5 bits volume table and 16*16*16 combinations of all volume are from Sc68 by Benjamin Gerard
24: - 4 bits to 5 bits volume interpolation from 16*16*16 to 32*32*32 from YM blep synthesis by Antti Lankila
25:
1.1.1.16 root 26: Special case for per==0 : as measured on a real STF, when tone/noise/env's per==0, we get
27: the same sound output as when per==1.
28:
29:
1.1 root 30: */
1.1.1.12 root 31:
32: /* 2008/05/05 [NP] Fix case where period is 0 for noise, sound or envelope. */
33: /* In that case, a real ST sounds as if period was in fact 1. */
34: /* (fix buggy sound replay in ESwat that set volume<0 and trigger */
35: /* a badly initialised envelope with envper=0). */
36: /* 2008/07/27 [NP] Better separation between accesses to the YM hardware registers */
37: /* and the sound rendering routines. Use Sound_WriteReg() to pass */
38: /* all writes to the sound rendering functions. This allows to */
39: /* have sound.c independant of psg.c (to ease replacement of */
40: /* sound.c by another rendering method). */
41: /* 2008/08/02 [NP] Initial convert of Ym2149Ex.cpp from C++ to C. */
42: /* Remove unused part of the code (StSound specific). */
43: /* 2008/08/09 [NP] Complete integration of StSound routines into sound.c */
44: /* Set EnvPer=3 if EnvPer<3 (ESwat buggy replay). */
45: /* 2008/08/13 [NP] StSound was generating samples in the range 0-32767, instead */
46: /* of really signed samples between -32768 and 32767, which could */
47: /* give incorrect results in many case. */
48: /* 2008/09/06 [NP] Use sc68 volumes table for a more accurate mixing of the voices */
49: /* All volumes are converted to 5 bits and the table contains */
50: /* 32*32*32 values. Samples are signed and centered to get the */
51: /* biggest amplitude possible. */
52: /* Faster mixing routines for tone+volume+envelope (don't use */
53: /* StSound's version anymore, it gave problem with some GCC). */
54: /* 2008/09/17 [NP] Add ym_normalise_5bit_table to normalise the 32*32*32 table and */
55: /* to optionally center 16 bit signed sample. */
56: /* Possibility to mix volumes using a table measured on ST or a */
57: /* linear mean of the 3 channels' volume. */
58: /* Default mixing set to YM_LINEAR_MIXING. */
59: /* 2008/10/14 [NP] Full support for 5 bits volumes : envelopes are generated with */
60: /* 32 volumes per pattern as on a real YM-2149. Fixed volumes */
61: /* on 4 bits are converted to their 5 bits equivalent. This should */
62: /* give the maximum accuracy possible when computing volumes. */
63: /* New version of Ym2149_EnvStepCompute to handle 5 bits volumes. */
64: /* Function YM2149_EnvBuild to compute the 96 volumes that define */
65: /* a single envelope (32 initial volumes, then 64 repeated values).*/
66: /* 2008/10/26 [NP] Correctly save/restore all necessary variables in */
67: /* Sound_MemorySnapShot_Capture. */
68: /* 2008/11/23 [NP] Clean source, remove old sound core. */
1.1.1.17! root 69: /* 2011/11/03 [DS] Stereo DC filtering which accounts for DMA sound. */
1.1.1.12 root 70:
71:
72:
1.1.1.14 root 73: const char Sound_fileid[] = "Hatari sound.c : " __DATE__ " " __TIME__;
1.1.1.5 root 74:
75: #include <SDL_types.h>
1.1 root 76:
77: #include "main.h"
78: #include "audio.h"
1.1.1.10 root 79: #include "cycles.h"
1.1.1.15 root 80: #include "configuration.h"
1.1.1.9 root 81: #include "dmaSnd.h"
1.1.1.15 root 82: #include "crossbar.h"
1.1 root 83: #include "file.h"
1.1.1.15 root 84: #include "cycInt.h"
1.1.1.8 root 85: #include "log.h"
1.1 root 86: #include "memorySnapShot.h"
87: #include "psg.h"
88: #include "sound.h"
1.1.1.16 root 89: #include "screen.h"
1.1 root 90: #include "video.h"
91: #include "wavFormat.h"
92: #include "ymFormat.h"
1.1.1.15 root 93: #include "avi_record.h"
1.1.1.16 root 94: #include "clocks_timings.h"
1.1 root 95:
96:
97:
1.1.1.12 root 98: /*--------------------------------------------------------------*/
99: /* Definition of the possible envelopes shapes (using 5 bits) */
100: /*--------------------------------------------------------------*/
101:
102: #define ENV_GODOWN 0 /* 31 -> 0 */
103: #define ENV_GOUP 1 /* 0 -> 31 */
104: #define ENV_DOWN 2 /* 0 -> 0 */
105: #define ENV_UP 3 /* 31 -> 31 */
106:
107: /* To generate an envelope, we first use block 0, then we repeat blocks 1 and 2 */
108: static const int YmEnvDef[ 16 ][ 3 ] = {
109: { ENV_GODOWN, ENV_DOWN, ENV_DOWN } , /* 0 \___ */
110: { ENV_GODOWN, ENV_DOWN, ENV_DOWN } , /* 1 \___ */
111: { ENV_GODOWN, ENV_DOWN, ENV_DOWN } , /* 2 \___ */
112: { ENV_GODOWN, ENV_DOWN, ENV_DOWN } , /* 3 \___ */
113: { ENV_GOUP, ENV_DOWN, ENV_DOWN } , /* 4 /___ */
114: { ENV_GOUP, ENV_DOWN, ENV_DOWN } , /* 5 /___ */
115: { ENV_GOUP, ENV_DOWN, ENV_DOWN } , /* 6 /___ */
116: { ENV_GOUP, ENV_DOWN, ENV_DOWN } , /* 7 /___ */
117: { ENV_GODOWN, ENV_GODOWN, ENV_GODOWN } , /* 8 \\\\ */
118: { ENV_GODOWN, ENV_DOWN, ENV_DOWN } , /* 9 \___ */
119: { ENV_GODOWN, ENV_GOUP, ENV_GODOWN } , /* A \/\/ */
120: { ENV_GODOWN, ENV_UP, ENV_UP } , /* B \--- */
121: { ENV_GOUP, ENV_GOUP, ENV_GOUP } , /* C //// */
122: { ENV_GOUP, ENV_UP, ENV_UP } , /* D /--- */
123: { ENV_GOUP, ENV_GODOWN, ENV_GOUP } , /* E /\/\ */
124: { ENV_GOUP, ENV_DOWN, ENV_DOWN } , /* F /___ */
125: };
126:
127:
128: /* Buffer to store the 16 envelopes built from YmEnvDef */
129: static ymu16 YmEnvWaves[ 16 ][ 32 * 3 ]; /* 16 envelopes with 3 blocks of 32 volumes */
1.1.1.3 root 130:
1.1 root 131:
1.1.1.12 root 132:
133: /*--------------------------------------------------------------*/
134: /* Definition of the volumes tables (using 5 bits) and of the */
135: /* mixing parameters for the 3 voices. */
136: /*--------------------------------------------------------------*/
137:
138: /* Table of unsigned 5 bit D/A output level for 1 channel as measured on a real ST (expanded from 4 bits to 5 bits) */
139: /* Vol 0 should be 310 when measuread as a voltage, but we set it to 0 in order to have a volume=0 matching */
140: /* the 0 level of a 16 bits unsigned sample (no sound output) */
141: static const ymu16 ymout1c5bit[ 32 ] =
142: {
143: 0 /*310*/, 369, 438, 521, 619, 735, 874, 1039,
144: 1234, 1467, 1744, 2072, 2463, 2927, 3479, 4135,
145: 4914, 5841, 6942, 8250, 9806,11654,13851,16462,
146: 19565,23253,27636,32845,39037,46395,55141,65535
1.1 root 147: };
148:
1.1.1.12 root 149: /* Convert a constant 4 bits volume to the internal 5 bits value : */
150: /* volume5=volume4*2+1, except for volumes 0 and 1 which become 0 and 2, */
151: /* in order to map [0,15] into [0,31] (O must remain 0, and 15 must give 31) */
152: static const ymu16 YmVolume4to5[ 16 ] = { 0,2,5,7,9,11,13,15,17,19,21,23,25,27,29,31 };
1.1 root 153:
1.1.1.12 root 154: /* Table of unsigned 4 bit D/A output level for 3 channels as measured on a real ST */
155: static ymu16 volumetable_original[ 16 * 16 * 16 ] =
156: #include "ym2149_fixed_vol.h"
1.1.1.5 root 157:
1.1.1.12 root 158: /* Corresponding table interpolated to 5 bit D/A output level (16 bits unsigned) */
159: static ymu16 ymout5_u16[ 32 * 32 * 32 ];
160:
161: /* Same table, after conversion to signed results (same pointer, with different type) */
162: static yms16 *ymout5 = (yms16 *)ymout5_u16;
163:
164:
165:
166: /*--------------------------------------------------------------*/
167: /* Other constants / macros */
168: /*--------------------------------------------------------------*/
169:
170: /* Number of generated samples per frame (eg. 44Khz=882) */
1.1.1.16 root 171: #define SAMPLES_PER_FRAME (nAudioFrequency/nScreenRefreshRate)
1.1.1.12 root 172:
173: /* Current sound replay freq (usually 44100 Hz) */
1.1.1.14 root 174: #define YM_REPLAY_FREQ nAudioFrequency
1.1.1.12 root 175:
1.1.1.16 root 176: /* YM-2149 clock on all Atari models is 2 MHz */
177: #define YM_ATARI_CLOCK (MachineClocks.YM_Freq)
1.1.1.12 root 178:
179:
180: /* Merge/read the 3 volumes in a single integer (5 bits per volume) */
181: #define YM_MERGE_VOICE(C,B,A) ( (C)<<10 | (B)<<5 | A )
182: #define YM_MASK_1VOICE 0x1f
183: #define YM_MASK_A 0x1f
184: #define YM_MASK_B (0x1f<<5)
185: #define YM_MASK_C (0x1f<<10)
186:
187:
188: /* Constants for YM2149_Normalise_5bit_Table */
189: #define YM_OUTPUT_LEVEL 0x7fff /* amplitude of the final signal (0..65535 if centered, 0..32767 if not) */
1.1.1.14 root 190: #define YM_OUTPUT_CENTERED false
1.1.1.12 root 191:
192:
193:
194: /*--------------------------------------------------------------*/
195: /* Variables for the YM2149 emulator (need to be saved and */
196: /* restored in memory snapshots) */
197: /*--------------------------------------------------------------*/
198:
199: static ymu32 stepA , stepB , stepC;
200: static ymu32 posA , posB , posC;
201: static ymu32 mixerTA , mixerTB , mixerTC;
202: static ymu32 mixerNA , mixerNB , mixerNC;
203:
204: static ymu32 noiseStep;
205: static ymu32 noisePos;
206: static ymu32 currentNoise;
207: static ymu32 RndRack; /* current random seed */
208:
209: static ymu32 envStep;
210: static ymu32 envPos;
211: static int envShape;
212:
213: static ymu16 EnvMask3Voices = 0; /* mask is 0x1f for voices having an active envelope */
214: static ymu16 Vol3Voices = 0; /* volume 0-0x1f for voices having a constant volume */
215: /* volume is set to 0 if voice has an envelope in EnvMask3Voices */
216:
217:
218: /* Global variables that can be changed/read from other parts of Hatari */
219: Uint8 SoundRegs[ 14 ];
220:
1.1.1.16 root 221: int YmVolumeMixing = YM_TABLE_MIXING;
1.1.1.14 root 222: bool UseLowPassFilter = false;
1.1.1.12 root 223:
224: bool bEnvelopeFreqFlag; /* Cleared each frame for YM saving */
225:
226: Sint16 MixBuffer[MIXBUFFER_SIZE][2];
227: int nGeneratedSamples; /* Generated samples since audio buffer update */
228: static int ActiveSndBufIdx; /* Current working index into above mix buffer */
1.1.1.16 root 229: static int ActiveSndBufIdxAvi; /* Current working index to save an AVI audio frame */
230:
231: static yms64 SamplesPerFrame_unrounded = 0; /* Number of samples for the current VBL, with simulated fractional part */
232: static int SamplesPerFrame; /* Number of samples to generate for the current VBL */
233: static int CurrentSamplesNb = 0; /* Number of samples already generated for the current VBL */
1.1.1.12 root 234:
1.1.1.16 root 235: bool Sound_BufferIndexNeedReset = false;
1.1.1.12 root 236:
237:
238: /*--------------------------------------------------------------*/
239: /* Local functions prototypes */
240: /*--------------------------------------------------------------*/
241:
1.1.1.17! root 242: static ymsample LowPassFilter (ymsample x0);
1.1.1.12 root 243:
244: static int volumetable_get (int i, int j, int k);
245: static void volumetable_set (ymu16 *volumetable, int i, int j, int k, int val);
246: static int volumetable_interpolate (int y1, int y2);
247: static void interpolate_volumetable (ymu16 *out);
248:
1.1.1.17! root 249: static void YM2149_BuildModelVolumeTable(ymu16 *out);
1.1.1.12 root 250: static void YM2149_BuildLinearVolumeTable(ymu16 *out);
251: static void YM2149_Normalise_5bit_Table(ymu16 *in_5bit , yms16 *out_5bit, unsigned int Level, bool DoCenter);
252:
253: static void YM2149_EnvBuild (void);
1.1.1.16 root 254: static void Ym2149_BuildVolumeTable (void);
1.1.1.12 root 255: static void Ym2149_Init (void);
256: static void Ym2149_Reset (void);
257:
258: static ymu32 YM2149_RndCompute (void);
259: static ymu32 Ym2149_ToneStepCompute (ymu8 rHigh , ymu8 rLow);
260: static ymu32 Ym2149_NoiseStepCompute (ymu8 rNoise);
261: static ymu32 Ym2149_EnvStepCompute (ymu8 rHigh , ymu8 rLow);
262: static ymsample YM2149_NextSample (void);
263:
1.1.1.16 root 264: static int Sound_SetSamplesPassed(bool FillFrame);
265: static void Sound_GenerateSamples(int SamplesToGenerate);
1.1.1.12 root 266:
267:
268:
269: /*--------------------------------------------------------------*/
1.1.1.17! root 270: /* DC Adjuster */
1.1.1.12 root 271: /*--------------------------------------------------------------*/
272:
1.1.1.17! root 273: /**
! 274: * 6dB/octave first order HPF fc = (1.0-0.998)*44100/(2.0*pi)
! 275: * Z pole = 0.99804 --> FS = 44100 Hz : fc=13.7 Hz (11 Hz meas)
! 276: * a = (int32_t)(32768.0*(1.0 - pole)) : a = 64 !!!
! 277: * Input range: -32768 to 32767 Maximum step: +65536 or -65472
! 278: */
! 279: ymsample Subsonic_IIR_HPF_Left(ymsample x0)
1.1.1.12 root 280: {
1.1.1.17! root 281: static yms32 x1 = 0, y1 = 0, y0 = 0;
1.1.1.14 root 282:
1.1.1.17! root 283: y1 += ((x0 - x1)<<15) - (y0<<6); /* 64*y0 */
! 284: y0 = y1>>15;
! 285: x1 = x0;
1.1.1.12 root 286:
1.1.1.17! root 287: return y0;
1.1.1.12 root 288: }
289:
290:
1.1.1.17! root 291: ymsample Subsonic_IIR_HPF_Right(ymsample x0)
1.1 root 292: {
1.1.1.17! root 293: static yms32 x1 = 0, y1 = 0, y0 = 0;
1.1.1.12 root 294:
1.1.1.17! root 295: y1 += ((x0 - x1)<<15) - (y0<<6); /* 64*y0 */
! 296: y0 = y1>>15;
! 297: x1 = x0;
1.1.1.12 root 298:
1.1.1.17! root 299: return y0;
1.1.1.12 root 300: }
301:
302:
1.1.1.17! root 303: /*--------------------------------------------------------------*/
! 304: /* Low Pass Filter routines. */
! 305: /*--------------------------------------------------------------*/
1.1.1.12 root 306:
1.1.1.17! root 307: /**
! 308: * Get coefficients for different Fs (C10 is in ST only):
! 309: * Wc = 2*M_PI*4895.1;
! 310: * Fs = 44100;
! 311: * warp = Wc/tanf((Wc/2)/Fs);
! 312: * b = Wc/(warp+Wc);
! 313: * a = (Wc-warp)/(warp+Wc);
! 314: *
! 315: * #define B_z (yms32)( 0.2667*(1<<15))
! 316: * #define A_z (yms32)(-0.4667*(1<<15))
! 317: *
! 318: * y0 = (B_z*(x0 + x1) - A_z*y0) >> 15;
! 319: * x1 = x0;
! 320: *
! 321: * The Lowpass Filter formed by C10 = 0.1 uF
! 322: * and
! 323: * R8=1k // 1k*(65119-46602/65119) // R9=10k // R10=5.1k //
! 324: * (R12=470)*(100=Q1_HFE) = 206.865 ohms when YM2149 is High
! 325: * and
! 326: * R8=1k // R9=10k // R10=5.1k // (R12=470)*(100=Q1_HFE)
! 327: * = 759.1 ohms when YM2149 is Low
! 328: * High corner is 1/(2*pi*(0.1*10e-6)*206.865) fc = 7693.7 Hz
! 329: * Low corner is 1/(2*pi*(0.1*10e-6)*795.1) fc = 2096.6 Hz
! 330: * Notes:
! 331: * - using STF reference designators R8 R9 R10 C10 (from dec 1986 schematics)
! 332: * - using corresponding numbers from psgstrep and psgquart
! 333: * - 65119 is the largest value in Paulo's psgstrep table
! 334: * - 46602 is the largest value in Paulo's psgquart table
! 335: * - this low pass filter uses the highest cutoff frequency
! 336: * on the STf (a slightly lower frequency is reasonable).
! 337: *
! 338: * A first order lowpass filter with a high cutoff frequency
! 339: * is used when the YM2149 pulls high, and a lowpass filter
! 340: * with a low cutoff frequency is used when R8 pulls low.
! 341: */
! 342: static ymsample LowPassFilter(ymsample x0)
! 343: {
! 344: static yms32 y0 = 0, x1 = 0;
! 345:
! 346: if (x0 >= y0)
! 347: /* YM Pull up: fc = 7586.1 Hz (44.1 KHz), fc = 8257.0 Hz (48 KHz) */
! 348: y0 = (3*(x0 + x1) + (y0<<1)) >> 3;
! 349: else
! 350: /* R8 Pull down: fc = 1992.0 Hz (44.1 KHz), fc = 2168.0 Hz (48 KHz) */
! 351: y0 = ((x0 + x1) + (6*y0)) >> 3;
1.1.1.12 root 352:
1.1.1.17! root 353: x1 = x0;
! 354: return y0;
1.1.1.12 root 355: }
356:
357:
358:
359: /*--------------------------------------------------------------*/
360: /* Build the volume conversion table used to simulate the */
361: /* behaviour of DAC used with the YM2149 in the atari ST. */
362: /* The final 32*32*32 table is built using a 16*16*16 table */
363: /* of all possible fixed volume combinations on a ST. */
364: /*--------------------------------------------------------------*/
365:
366: static int volumetable_get(int i, int j, int k)
367: {
368: /* access at boundary finds the last value instead of the first */
369: if (i == 16)
370: i = 15;
371: if (j == 16)
372: j = 15;
373: if (k == 16)
374: k = 15;
1.1.1.14 root 375:
1.1.1.12 root 376: return volumetable_original[i + 16 * j + 16 * 16 * k];
377: }
378:
379: static void volumetable_set(ymu16 *volumetable, int i, int j, int k, int val)
380: {
381: volumetable[i + 32 * j + 32 * 32 * k] = val;
382: }
383:
384: /* the table is exponential in nature. These weighing factors approximate
385: * that the value in-between needs to be closer to the lower value in y2 */
386: static int volumetable_interpolate(int y1, int y2)
387: {
388: int erpolate;
389: erpolate = (y1 * 4 + y2 * 6) / 10u;
390: if (erpolate > 65535)
1.1.1.11 root 391: {
1.1.1.12 root 392: // fprintf ( stderr , "sature>:%d %d %d\n",erpolate,y1,y2 );
393: erpolate = 65535;
1.1.1.11 root 394: }
1.1.1.12 root 395: if (erpolate < 0)
396: {
397: // fprintf ( stderr , "sature<:%d %d %d\n",erpolate,y1,y2 );
398: erpolate = 0;
399: }
400: return erpolate;
401: }
1.1.1.11 root 402:
1.1.1.12 root 403: static void interpolate_volumetable(ymu16 *out)
404: {
405: int i, j, k;
406: int i1, i2;
407:
408: /* we are doing 4-dimensional interpolation here. For each
409: * known measurement point, we must find 8 new values. These values occur
410: * as follows:
411: *
412: * - one at the exact same position
413: * - one half-way in i direction
414: * - one half-way in j direction
415: * - one half-way in k direction
416: * - one half-way in i+j direction
417: * - one half-way in i+k direction
418: * - one half-way in j+k direction
419: * - one half-way in i+j+k direction
420: *
421: * The algorithm currently is very simplistic. Probably more points should be
422: * weighted in the multicomponented directions, for instance i+j+k should be
423: * an average of all surrounding data points. This probably doesn't matter much,
424: * though. This is because the only way to reach those locations is to modulate
425: * more than one voice with the envelope, and this is rare.
426: */
427:
428: for (i = 0; i < 16; i++)
1.1.1.11 root 429: {
1.1.1.12 root 430: for (j = 0; j < 16; j++)
431: {
432: for (k = 0; k < 16; k++)
433: {
434: i1 = volumetable_get(i, j, k);
435: /* copy value unchanged to new position */
436: volumetable_set(out,i*2, j*2, k*2, i1);
1.1.1.14 root 437:
1.1.1.12 root 438: /* interpolate in i direction */
439: i2 = volumetable_get(i + 1, j, k);
440: volumetable_set(out,i*2 + 1, j*2, k*2, volumetable_interpolate(i1, i2));
1.1.1.14 root 441:
1.1.1.12 root 442: /* interpolate in j direction */
443: i2 = volumetable_get(i, j+1, k);
444: volumetable_set(out,i*2, j*2 + 1, k*2, volumetable_interpolate(i1, i2));
1.1.1.14 root 445:
1.1.1.12 root 446: /* interpolate in k direction */
447: i2 = volumetable_get(i, j, k+1);
448: volumetable_set(out,i*2, j*2, k*2+1, volumetable_interpolate(i1, i2));
449:
450: /* interpolate in i + j direction */
451: i2 = volumetable_get(i + 1, j + 1, k);
452: volumetable_set(out,i*2 + 1, j*2 + 1, k*2, volumetable_interpolate(i1, i2));
1.1.1.14 root 453:
1.1.1.12 root 454: /* interpolate in i + k direction */
455: i2 = volumetable_get(i + 1, j, k + 1);
456: volumetable_set(out,i*2 + 1, j*2, k*2 + 1, volumetable_interpolate(i1, i2));
1.1.1.14 root 457:
1.1.1.12 root 458: /* interpolate in j + k direction */
459: i2 = volumetable_get(i, j + 1, k + 1);
460: volumetable_set(out,i*2, j*2 + 1, k*2 + 1, volumetable_interpolate(i1, i2));
461:
462: /* interpolate in i + j + k direction */
463: i2 = volumetable_get(i + 1, j + 1, k + 1);
464: volumetable_set(out,i*2 + 1, j*2 + 1, k*2 + 1, volumetable_interpolate(i1, i2));
465: }
466: }
1.1.1.11 root 467: }
1.1 root 468: }
469:
1.1.1.5 root 470:
1.1.1.12 root 471:
472:
1.1.1.2 root 473: /*-----------------------------------------------------------------------*/
1.1.1.11 root 474: /**
1.1.1.12 root 475: * Build a linear version of the conversion table.
476: * We use the mean of the 3 volumes converted to 16 bit values
477: * (each value of ymout1c5bit is in [0,65535])
1.1.1.11 root 478: */
1.1.1.12 root 479:
480: static void YM2149_BuildLinearVolumeTable(ymu16 *out)
1.1 root 481: {
1.1.1.12 root 482: int i, j, k;
483: int res;
484:
485: for (i = 0; i < 32; i++)
486: for (j = 0; j < 32; j++)
487: for (k = 0; k < 32; k++)
488: {
489: res = ( ymout1c5bit[ i ] + ymout1c5bit[ j ] + ymout1c5bit[ k ] ) / 3;
490: volumetable_set ( out, i, j, k, res );
491: }
492: }
493:
494:
495:
1.1 root 496:
1.1.1.12 root 497: /*-----------------------------------------------------------------------*/
498: /**
1.1.1.17! root 499: * Build a circuit analysed version of the conversion table.
! 500: * David Savinkoff designed this algorithm by analysing data
! 501: * measured by Paulo Simoes and Benjamin Gerard.
! 502: * The numbers are arrived at by assuming a current steering
! 503: * resistor ladder network and using the voltage divider rule.
! 504: */
! 505:
! 506: static void YM2149_BuildModelVolumeTable(ymu16 *out)
! 507: {
! 508: #define Quartet 0 /* 1 = Compile for Quartet Mode */
! 509:
! 510: #define FOURTH2 1.189207115002721067 /* Fourth root of two */
! 511: #define SQRT3 1.732050807568877294 /* Square root of three */
! 512: /* R8=1k (pulldown) + YM//1K (Quartet pullup)*/
! 513: #define YM_Gth 0.7113248654051871177 /* = (1//(3^1/2 - 1) + 1)/2 */
! 514:
! 515: #if Quartet
! 516: /* Quartet Mode (MaxVol is 46602 in Paulo Simoes table) */
! 517: #define MaxVol (56700.0*YM_Gth) /* Quartet Mode Maximum value */
! 518: #define WARP (6.0*(1.0+YM_Gth)) /* Quartet Mode WARP (50% duty PWM) */
! 519: #else
! 520: /* Normal Mode (Maxvol is 65119 in Simoes' table, 65535 in Gerard's */
! 521: #define MaxVol 56700.0 /* Normal Mode Maximum value in table */
! 522: #define WARP SQRT3 /* Normal Mode WARP */
! 523: #endif
! 524:
! 525: double conductance;
! 526: double conductance_[32];
! 527: int i, j, k;
! 528: int res;
! 529:
! 530: /**
! 531: * YM2149 and R8=1k follows (2^-1/4)^(n-31) better when 2 voices are
! 532: * summed (A+B or B+C or C+A) rather than individually (A or B or C):
! 533: * conductance = 2.0/3.0/(1.0-1.0/SQRT3)-2.0/3.0;
! 534: * When taken into consideration with three voices.
! 535: *
! 536: * Note that the YM2149 does not use laser trimmed resistances, thus
! 537: * has offsets that are added and/or multiplied with (2^-1/4)^(n-31).
! 538: */
! 539: conductance = 2.0/3.0/(1.0-1.0/WARP)-2.0/3.0;
! 540:
! 541: /**
! 542: * Because the YM current output (voltage source with series resistances)
! 543: * is connected to a grounded resistor to develop the output voltage
! 544: * (instead of a current to voltage converter), the output transfer
! 545: * function is not linear. Thus:
! 546: * 2.0*conductance_[n] = 1.0/(1.0-1.0/FOURTH2/(1.0/conductance + 1.0))-1.0;
! 547: */
! 548: for (i = 31; i >= 1; i--)
! 549: {
! 550: conductance_[i] = conductance/2.0;
! 551: conductance = 1.0/(1.0-1.0/FOURTH2/(1.0/conductance + 1.0))-1.0;
! 552: }
! 553: conductance_[0] = 1.0e-8;
! 554:
! 555: /* Sum the conductances as a function of a voltage divider: Vout=Vin*(Rout/Rout+Rin) */
! 556: for (i = 0; i < 32; i++)
! 557: for (j = 0; j < 32; j++)
! 558: for (k = 0; k < 32; k++)
! 559: {
! 560: res = (int)(0.5+(MaxVol*WARP)/(1.0 +
! 561: 1.0/(conductance_[i]+conductance_[j]+conductance_[k])));
! 562: volumetable_set ( out, i, j, k, res );
! 563: }
! 564: }
! 565:
! 566:
! 567:
! 568:
! 569: /*-----------------------------------------------------------------------*/
! 570: /**
1.1.1.12 root 571: * Normalise and optionally center the volume table used to
572: * convert the 3 volumes to a final signed 16 bit sample.
573: * This allows to adapt the amplitude/volume of the samples and
574: * to convert unsigned values to signed values.
575: * - in_5bit contains 32*32*32 unsigned values in the range
576: * [0,65535].
577: * - out_5bit will contain signed values
578: * Possible values are :
579: * Level=65535 and DoCenter=TRUE -> [-32768,32767]
1.1.1.14 root 580: * Level=32767 and DoCenter=false -> [0,32767]
1.1.1.12 root 581: */
582:
583: static void YM2149_Normalise_5bit_Table(ymu16 *in_5bit , yms16 *out_5bit, unsigned int Level, bool DoCenter)
584: {
585: if ( Level )
1.1.1.11 root 586: {
1.1.1.14 root 587: int h;
1.1.1.12 root 588: int Max = in_5bit[0x7fff];
589: int Center = Level>>1;
1.1.1.14 root 590: //fprintf ( stderr , "level %d max %d center %d\n" , Level, Max, Center );
591:
1.1.1.12 root 592: /* Change the amplitude of the signal to 'level' : [0,max] -> [0,level] */
593: /* Then optionally center the signal around Level/2 */
594: /* This means we go from sthg like [0,65535] to [-32768, 32767] if Level=65535 and DoCenter=TRUE */
595: for (h=0; h<32*32*32; h++)
596: {
597: int tmp = in_5bit[h], res;
598: res = tmp * Level / Max;
1.1.1.14 root 599:
1.1.1.12 root 600: if ( DoCenter )
601: res -= Center;
602:
603: out_5bit[h] = res;
1.1.1.14 root 604: //fprintf ( stderr , "h %d in %d out %d\n" , h , tmp , res );
1.1.1.12 root 605: }
1.1.1.11 root 606: }
1.1 root 607: }
608:
1.1.1.5 root 609:
1.1.1.12 root 610:
611:
1.1.1.2 root 612: /*-----------------------------------------------------------------------*/
1.1.1.11 root 613: /**
1.1.1.12 root 614: * Precompute all 16 possible envelopes.
615: * Each envelope is made of 3 blocks of 32 volumes.
1.1.1.11 root 616: */
1.1.1.12 root 617:
618: static void YM2149_EnvBuild ( void )
1.1 root 619: {
1.1.1.12 root 620: int env;
621: int block;
622: int vol=0 , inc=0;
623: int i;
1.1 root 624:
1.1.1.12 root 625:
626: for ( env=0 ; env<16 ; env++ ) /* 16 possible envelopes */
627: for ( block=0 ; block<3 ; block++ ) /* 3 blocks to define an envelope */
628: {
1.1.1.14 root 629: switch ( YmEnvDef[ env ][ block ] )
630: {
1.1.1.12 root 631: case ENV_GODOWN : vol=31 ; inc=-1 ; break;
632: case ENV_GOUP : vol=0 ; inc=1 ; break;
633: case ENV_DOWN : vol=0 ; inc=0 ; break;
634: case ENV_UP : vol=31 ; inc=0 ; break;
1.1.1.14 root 635: }
636:
1.1.1.12 root 637: for ( i=0 ; i<32 ; i++ ) /* 32 volumes per block */
638: {
639: YmEnvWaves[ env ][ block*32 + i ] = YM_MERGE_VOICE ( vol , vol , vol );
640: vol += inc;
641: }
642: }
643: }
644:
645:
646:
647: /*-----------------------------------------------------------------------*/
648: /**
1.1.1.16 root 649: * Depending on the YM mixing method, build the table used to convert
650: * the 3 YM volumes into a single sample.
1.1.1.12 root 651: */
652:
1.1.1.16 root 653: static void Ym2149_BuildVolumeTable(void)
1.1.1.12 root 654: {
655: /* Depending on the volume mixing method, we use a table based on real measures */
656: /* or a table based on a linear volume mixing. */
1.1.1.17! root 657: if ( YmVolumeMixing == YM_MODEL_MIXING )
! 658: YM2149_BuildModelVolumeTable(ymout5_u16); /* create 32*32*32 circuit analysed model of the volume table */
! 659: else if ( YmVolumeMixing == YM_TABLE_MIXING )
1.1.1.16 root 660: interpolate_volumetable(ymout5_u16); /* expand the 16*16*16 values in volumetable_original to 32*32*32 */
1.1.1.12 root 661: else
662: YM2149_BuildLinearVolumeTable(ymout5_u16); /* combine the 32 possible volumes */
663:
664: /* Normalise/center the values (convert from u16 to s16) */
665: YM2149_Normalise_5bit_Table ( ymout5_u16 , ymout5 , YM_OUTPUT_LEVEL , YM_OUTPUT_CENTERED );
1.1.1.16 root 666: }
667:
668:
669:
670: /*-----------------------------------------------------------------------*/
671: /**
672: * Init some internal tables for faster results (env, volume)
673: * and reset the internal states.
674: */
675:
676: static void Ym2149_Init(void)
677: {
678: /* Build the 16 envelope shapes */
679: YM2149_EnvBuild();
680:
681: /* Build the volume conversion table */
682: Ym2149_BuildVolumeTable();
1.1.1.12 root 683:
684: /* Reset YM2149 internal states */
685: Ym2149_Reset();
686: }
687:
688:
689:
690: /*-----------------------------------------------------------------------*/
691: /**
1.1.1.16 root 692: * Reset all ym registers as well as the internal variables
1.1.1.12 root 693: */
694:
695: static void Ym2149_Reset(void)
696: {
697: int i;
1.1.1.14 root 698:
1.1.1.12 root 699: for ( i=0 ; i<14 ; i++ )
700: Sound_WriteReg ( i , 0 );
701:
702: Sound_WriteReg ( 7 , 0xff );
703:
1.1.1.16 root 704: posA = 0;
705: posB = 0;
706: posC = 0;
707:
1.1.1.12 root 708: currentNoise = 0xffff;
1.1.1.14 root 709:
1.1.1.12 root 710: RndRack = 1;
1.1.1.14 root 711:
1.1.1.12 root 712: envShape = 0;
713: envPos = 0;
714: }
715:
716:
717:
718: /*-----------------------------------------------------------------------*/
719: /**
720: * Returns a pseudo random value, used to generate white noise.
721: */
722:
723: static ymu32 YM2149_RndCompute(void)
724: {
725: ymu32 bit;
1.1.1.14 root 726:
1.1.1.12 root 727: bit = (RndRack&1) ^ ((RndRack>>2)&1);
728: RndRack = (RndRack>>1) | (bit<<16);
729: return (bit ? 0 : 0xffff);
730: }
731:
732:
733:
734: /*-----------------------------------------------------------------------*/
735: /**
1.1.1.16 root 736: * Compute tone's step based on the input period.
737: * Although for tone we should have the same result when per==0 and per==1,
738: * this gives some very sharp and unpleasant sounds in the emulation.
739: * To get a better sound, we consider all per<=5 to give step=0, which will
740: * produce a constant output at value '1'. This should be handled with some
741: * proper filters to remove high frequencies as on a real ST (where per<=9
742: * gives nearly no audible sound).
743: * A common replay freq of 44.1 kHz will also not be high enough to correctly
744: * render possible tone's freq of 125 or 62.5 kHz (when per==1 or per==2)
1.1.1.12 root 745: */
746:
1.1.1.16 root 747: #define NEWSTEP
748: #ifndef NEWSTEP
1.1.1.12 root 749: static ymu32 Ym2149_ToneStepCompute(ymu8 rHigh , ymu8 rLow)
750: {
751: int per;
1.1.1.14 root 752: yms64 step;
1.1.1.12 root 753:
754: per = rHigh&15;
755: per = (per<<8)+rLow;
1.1.1.14 root 756: if (per <= 5)
1.1.1.12 root 757: return 0;
758:
1.1.1.14 root 759: step = YM_ATARI_CLOCK;
1.1.1.12 root 760: step <<= (15+16-3);
761: step /= (per * YM_REPLAY_FREQ);
762:
763: return step;
764: }
1.1.1.16 root 765: #else
766: static ymu32 Ym2149_ToneStepCompute(ymu8 rHigh , ymu8 rLow)
767: {
768: int per;
769: yms64 step;
770:
771: per = rHigh&15;
772: per = (per<<8)+rLow;
773:
774: #if 0 /* need some high freq filters for this to work correctly */
775: if ( per == 0 )
776: per = 1; /* result for Per=0 is the same as for Per=1 */
777: #else
778: if ( per <= 5 )
779: return 0; /* discard too high frequencies, they give a very bad sound */
780: #endif
781:
782: step = YM_ATARI_CLOCK;
783: step <<= 24;
784:
785: step /= (per * 8 * YM_REPLAY_FREQ); /* 0x5ab9 < step < 0x5ab3f46 at 44.1 kHz */
786:
787: return step;
788: }
789: #endif
1.1.1.12 root 790:
1.1.1.16 root 791: /*-----------------------------------------------------------------------*/
792: /**
793: * Compute noise's step based on the input period.
794: * On a real STF, we get the same result when per==0 and per==1.
795: * A common replay freq of 44.1 kHz will not be high enough to correctly
796: * render possible noise's freq of 125 or 62.5 kHz (when per==1 or per==2).
797: * With a random wave such as noise, this means that with a replay freq
798: * of 44.1 kHz, per==1 and per==2 (as well as per==3) will sound the same :
799: * per==1 step=0x2d59fa3 freq=125 kHz
800: * per==2 step=0x16acfd1 freq=62.5 kHz
801: * per==3 step=0x0f1dfe1 freq=41.7 kHz
802: */
1.1.1.12 root 803:
1.1.1.16 root 804: #ifndef NEWSTEP
1.1.1.12 root 805: static ymu32 Ym2149_NoiseStepCompute(ymu8 rNoise)
806: {
807: int per;
1.1.1.14 root 808: yms64 step;
1.1.1.12 root 809:
810: per = (rNoise&0x1f);
811: if (per<3)
812: return 0;
813:
1.1.1.14 root 814: step = YM_ATARI_CLOCK;
1.1.1.12 root 815: step <<= (16-1-3);
816: step /= (per * YM_REPLAY_FREQ);
817:
818: return step;
1.1 root 819: }
1.1.1.16 root 820: #else
821: static ymu32 Ym2149_NoiseStepCompute(ymu8 rNoise)
822: {
823: int per;
824: yms64 step;
825:
826: per = (rNoise&0x1f);
1.1 root 827:
1.1.1.16 root 828: if ( per == 0 )
829: per = 1; /* result for Per=0 is the same as for Per=1 */
830:
831: step = YM_ATARI_CLOCK;
832: step <<= 24;
833:
834: step /= (per * 16 * YM_REPLAY_FREQ); /* 0x17683f < step < 0x2d59fa3 at 44.1 kHz */
835:
836: return step;
837: }
838: #endif
1.1.1.5 root 839:
1.1.1.2 root 840: /*-----------------------------------------------------------------------*/
1.1.1.11 root 841: /**
1.1.1.12 root 842: * Compute envelope's step. The envelope is made of different patterns
843: * of 32 volumes. In each pattern, the volume is changed at frequency
844: * Fe = MasterClock / ( 8 * EnvPer ).
845: * In our case, we use a lower replay freq ; between 2 consecutive calls
846: * to envelope's generation, the internal counter will advance 'step'
847: * units, where step = MasterClock / ( 8 * EnvPer * YM_REPLAY_FREQ )
848: * As 'step' requires floating point to be stored, we use left shifting
849: * to multiply 'step' by a fixed amount. All operations are made with
850: * shifted values ; to get the final value, we must right shift the
851: * result. We use '<<24', which gives 8 bits for the integer part, and
852: * the equivalent of 24 bits for the fractional part.
853: * Since we're using large numbers, we temporarily use 64 bits integer
854: * to avoid overflow and keep largest precision possible.
1.1.1.16 root 855: * On a real STF, we get the same result when per==0 and per==1.
1.1.1.11 root 856: */
1.1.1.12 root 857:
858: static ymu32 Ym2149_EnvStepCompute(ymu8 rHigh , ymu8 rLow)
1.1 root 859: {
1.1.1.12 root 860: yms64 per;
1.1.1.14 root 861: yms64 step;
1.1 root 862:
1.1.1.12 root 863: per = rHigh;
864: per = (per<<8)+rLow;
865:
1.1.1.14 root 866: step = YM_ATARI_CLOCK;
1.1.1.12 root 867: step <<= 24;
1.1.1.16 root 868:
869: if ( per == 0 )
870: per = 1; /* result for Per=0 is the same as for Per=1 */
871:
872: step /= (8 * per * YM_REPLAY_FREQ); /* 0x5ab < step < 0x5ab3f46 at 44.1 kHz */
1.1.1.12 root 873:
874: return step;
875: }
876:
877:
878:
879: /*-----------------------------------------------------------------------*/
880: /**
881: * Main function : compute the value of the next sample.
882: * Mixes all 3 voices with tone+noise+env and apply low pass
883: * filter if needed.
1.1.1.16 root 884: * All operations are done with integer math, using <<24 to simulate
885: * floating point precision : upper 8 bits are the integer part, lower 24
886: * are the fractional part.
887: * Tone is a square wave with 2 states 0 or 1. If integer part of posX is
888: * even (bit24=0) we consider output is 0, else (bit24=1) we consider
889: * output is 1. This gives the value of bt for one voice after extending it
890: * to all 0 bits or all 1 bits using a '-'
1.1.1.12 root 891: */
892:
1.1.1.16 root 893: #ifndef NEWSTEP
1.1.1.12 root 894: static ymsample YM2149_NextSample(void)
895: {
896: ymsample sample;
897: int bt;
898: ymu32 bn;
899: ymu16 Env3Voices;
900: ymu16 Tone3Voices;
901:
902:
903: /* Noise value : 0 or 0xffff */
904: if ( noisePos&0xffff0000 )
905: {
906: currentNoise ^= YM2149_RndCompute();
907: noisePos &= 0xffff;
908: }
909: bn = currentNoise; /* 0 or 0xffff */
910:
911: /* Get the 5 bits volume corresponding to the current envelope's position */
912: Env3Voices = YmEnvWaves[ envShape ][ envPos>>24 ]; /* integer part of envPos is in bits 24-31 */
913: Env3Voices &= EnvMask3Voices; /* only keep volumes for voices using envelope */
914:
915: //fprintf ( stderr , "env %x %x %x\n" , Env3Voices , envStep , envPos );
916:
917: /* Tone3Voices will contain the output state of each voice : 0 or 0x1f */
918: bt = ((((yms32)posA)>>31) | mixerTA) & (bn | mixerNA); /* 0 or 0xffff */
919: Tone3Voices = bt & YM_MASK_1VOICE; /* 0 or 0x1f */
920: bt = ((((yms32)posB)>>31) | mixerTB) & (bn | mixerNB);
921: Tone3Voices |= ( bt & YM_MASK_1VOICE ) << 5;
922: bt = ((((yms32)posC)>>31) | mixerTC) & (bn | mixerNC);
923: Tone3Voices |= ( bt & YM_MASK_1VOICE ) << 10;
924:
925: /* Combine fixed volumes and envelope volumes and keep the resulting */
926: /* volumes depending on the output state of each voice (0 or 0x1f) */
927: Tone3Voices &= ( Env3Voices | Vol3Voices );
928:
929: /* D/A conversion of the 3 volumes into a sample using a precomputed conversion table */
930: sample = ymout5[ Tone3Voices ]; /* 16 bits signed value */
931:
932:
933: /* Increment positions */
934: posA += stepA;
935: posB += stepB;
936: posC += stepC;
937: noisePos += noiseStep;
1.1.1.14 root 938:
1.1.1.12 root 939: envPos += envStep;
940: if ( envPos >= (3*32) << 24 ) /* blocks 0, 1 and 2 were used (envPos 0 to 95) */
941: envPos -= (2*32) << 24; /* replay/loop blocks 1 and 2 (envPos 32 to 95) */
942:
943: /* Apply low pass filter ? */
944: if ( UseLowPassFilter )
1.1.1.11 root 945: {
1.1.1.14 root 946: sample = LowPassFilter(sample);
1.1.1.11 root 947: }
1.1.1.12 root 948:
949: return sample;
1.1 root 950: }
1.1.1.16 root 951: #else
952: static ymsample YM2149_NextSample(void)
953: {
954: ymsample sample;
955: ymu32 bt;
956: ymu32 bn;
957: ymu16 Env3Voices; /* 0x00CCBBAA */
958: ymu16 Tone3Voices; /* 0x00CCBBAA */
1.1 root 959:
960:
1.1.1.16 root 961: /* Noise value : 0 or 0xffff */
962: if ( noisePos&0xff000000 ) /* integer part > 0 */
963: {
964: currentNoise ^= YM2149_RndCompute();
965: noisePos &= 0xffffff; /* keep fractional part of noisePos */
966: }
967: bn = currentNoise; /* 0 or 0xffff */
968:
969: /* Get the 5 bits volume corresponding to the current envelope's position */
970: Env3Voices = YmEnvWaves[ envShape ][ envPos>>24 ]; /* integer part of envPos is in bits 24-31 */
971: Env3Voices &= EnvMask3Voices; /* only keep volumes for voices using envelope */
972:
973: //fprintf ( stderr , "env %x %x %x\n" , Env3Voices , envStep , envPos );
974:
975: /* Tone3Voices will contain the output state of each voice : 0 or 0x1f */
976: bt = -( (posA>>24) & 1); /* 0 if bit24=0 or 0xffffffff if bit24=1 */
977: bt = (bt | mixerTA) & (bn | mixerNA); /* 0 or 0xffff */
978: Tone3Voices = bt & YM_MASK_1VOICE; /* 0 or 0x1f */
979: bt = -( (posB>>24) & 1);
980: bt = (bt | mixerTB) & (bn | mixerNB);
981: Tone3Voices |= ( bt & YM_MASK_1VOICE ) << 5;
982: bt = -( (posC>>24) & 1);
983: bt = (bt | mixerTC) & (bn | mixerNC);
984: Tone3Voices |= ( bt & YM_MASK_1VOICE ) << 10;
985:
986: /* Combine fixed volumes and envelope volumes and keep the resulting */
987: /* volumes depending on the output state of each voice (0 or 0x1f) */
988: Tone3Voices &= ( Env3Voices | Vol3Voices );
989:
990: /* D/A conversion of the 3 volumes into a sample using a precomputed conversion table */
991: sample = ymout5[ Tone3Voices ]; /* 16 bits signed value */
992:
993:
994: /* Increment positions */
995: posA += stepA;
996: posB += stepB;
997: posC += stepC;
998: noisePos += noiseStep;
999:
1000: envPos += envStep;
1001: if ( envPos >= (3*32) << 24 ) /* blocks 0, 1 and 2 were used (envPos 0 to 95) */
1002: envPos -= (2*32) << 24; /* replay/loop blocks 1 and 2 (envPos 32 to 95) */
1003:
1004: /* Apply low pass filter ? */
1005: if ( UseLowPassFilter )
1006: {
1007: sample = LowPassFilter(sample);
1008: }
1009:
1010: return sample;
1011: }
1012: #endif
1013:
1.1.1.12 root 1014:
1.1.1.2 root 1015: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1016: /**
1.1.1.12 root 1017: * Update internal variables (steps, volume masks, ...) each
1018: * time an YM register is changed.
1.1.1.11 root 1019: */
1.1.1.16 root 1020: #ifndef NEWSTEP
1021: #define BIT_SHIFT 31
1022: #else
1023: #define BIT_SHIFT 24
1024: #endif
1.1.1.12 root 1025: void Sound_WriteReg( int reg , Uint8 data )
1.1.1.7 root 1026: {
1.1.1.12 root 1027: switch (reg)
1028: {
1029: case 0:
1030: SoundRegs[0] = data;
1031: stepA = Ym2149_ToneStepCompute ( SoundRegs[1] , SoundRegs[0] );
1.1.1.16 root 1032: if (!stepA) posA = 1u<<BIT_SHIFT; // Assume output always 1 if 0 period (for Digi-sample)
1.1.1.12 root 1033: break;
1034:
1035: case 1:
1036: SoundRegs[1] = data & 0x0f;
1037: stepA = Ym2149_ToneStepCompute ( SoundRegs[1] , SoundRegs[0] );
1.1.1.16 root 1038: if (!stepA) posA = 1u<<BIT_SHIFT; // Assume output always 1 if 0 period (for Digi-sample)
1.1.1.12 root 1039: break;
1040:
1041: case 2:
1042: SoundRegs[2] = data;
1043: stepB = Ym2149_ToneStepCompute ( SoundRegs[3] , SoundRegs[2] );
1.1.1.16 root 1044: if (!stepB) posB = 1u<<BIT_SHIFT; // Assume output always 1 if 0 period (for Digi-sample)
1.1.1.12 root 1045: break;
1.1.1.7 root 1046:
1.1.1.12 root 1047: case 3:
1048: SoundRegs[3] = data & 0x0f;
1049: stepB = Ym2149_ToneStepCompute ( SoundRegs[3] , SoundRegs[2] );
1.1.1.16 root 1050: if (!stepB) posB = 1u<<BIT_SHIFT; // Assume output always 1 if 0 period (for Digi-sample)
1.1.1.12 root 1051: break;
1052:
1053: case 4:
1054: SoundRegs[4] = data;
1055: stepC = Ym2149_ToneStepCompute ( SoundRegs[5] , SoundRegs[4] );
1.1.1.16 root 1056: if (!stepC) posC = 1u<<BIT_SHIFT; // Assume output always 1 if 0 period (for Digi-sample)
1.1.1.12 root 1057: break;
1058:
1059: case 5:
1060: SoundRegs[5] = data & 0x0f;
1061: stepC = Ym2149_ToneStepCompute ( SoundRegs[5] , SoundRegs[4] );
1.1.1.16 root 1062: if (!stepC) posC = 1u<<BIT_SHIFT; // Assume output always 1 if 0 period (for Digi-sample)
1.1.1.12 root 1063: break;
1064:
1065: case 6:
1066: SoundRegs[6] = data & 0x1f;
1067: noiseStep = Ym2149_NoiseStepCompute ( SoundRegs[6] );
1068: if (!noiseStep)
1069: {
1070: noisePos = 0;
1071: currentNoise = 0xffff;
1072: }
1073: break;
1074:
1075: case 7:
1076: SoundRegs[7] = data & 0x3f; /* ignore bits 6 and 7 */
1077: mixerTA = (data&(1<<0)) ? 0xffff : 0;
1078: mixerTB = (data&(1<<1)) ? 0xffff : 0;
1079: mixerTC = (data&(1<<2)) ? 0xffff : 0;
1080: mixerNA = (data&(1<<3)) ? 0xffff : 0;
1081: mixerNB = (data&(1<<4)) ? 0xffff : 0;
1082: mixerNC = (data&(1<<5)) ? 0xffff : 0;
1083: break;
1084:
1085: case 8:
1086: SoundRegs[8] = data & 0x1f;
1087: if ( data & 0x10 )
1088: {
1089: EnvMask3Voices |= YM_MASK_A; /* env ON */
1090: Vol3Voices &= ~YM_MASK_A; /* fixed vol OFF */
1091: }
1092: else
1093: {
1094: EnvMask3Voices &= ~YM_MASK_A; /* env OFF */
1095: Vol3Voices &= ~YM_MASK_A; /* clear previous vol */
1096: Vol3Voices |= YmVolume4to5[ SoundRegs[8] ]; /* fixed vol ON */
1097: }
1098: break;
1.1.1.14 root 1099:
1.1.1.12 root 1100: case 9:
1101: SoundRegs[9] = data & 0x1f;
1102: if ( data & 0x10 )
1103: {
1104: EnvMask3Voices |= YM_MASK_B; /* env ON */
1105: Vol3Voices &= ~YM_MASK_B; /* fixed vol OFF */
1106: }
1107: else
1108: {
1109: EnvMask3Voices &= ~YM_MASK_B; /* env OFF */
1110: Vol3Voices &= ~YM_MASK_B; /* clear previous vol */
1111: Vol3Voices |= ( YmVolume4to5[ SoundRegs[9] ] ) << 5; /* fixed vol ON */
1112: }
1113: break;
1.1.1.14 root 1114:
1.1.1.12 root 1115: case 10:
1116: SoundRegs[10] = data & 0x1f;
1117: if ( data & 0x10 )
1118: {
1119: EnvMask3Voices |= YM_MASK_C; /* env ON */
1120: Vol3Voices &= ~YM_MASK_C; /* fixed vol OFF */
1121: }
1122: else
1123: {
1124: EnvMask3Voices &= ~YM_MASK_C; /* env OFF */
1125: Vol3Voices &= ~YM_MASK_C; /* clear previous vol */
1126: Vol3Voices |= ( YmVolume4to5[ SoundRegs[10] ] ) << 10; /* fixed vol ON */
1127: }
1128: break;
1129:
1130: case 11:
1131: SoundRegs[11] = data;
1132: envStep = Ym2149_EnvStepCompute ( SoundRegs[12] , SoundRegs[11] );
1133: break;
1134:
1135: case 12:
1136: SoundRegs[12] = data;
1137: envStep = Ym2149_EnvStepCompute ( SoundRegs[12] , SoundRegs[11] );
1138: break;
1139:
1140: case 13:
1141: SoundRegs[13] = data & 0xf;
1142: envPos = 0; /* when writing to EnvShape, we must reset the EnvPos */
1143: envShape = SoundRegs[13];
1.1.1.14 root 1144: bEnvelopeFreqFlag = true; /* used for YmFormat saving */
1.1.1.12 root 1145: break;
1146:
1147: }
1148: }
1149:
1150:
1151:
1152: /*-----------------------------------------------------------------------*/
1153: /**
1154: * Init random generator, sound tables and envelopes
1155: * (called only once when Hatari starts)
1156: */
1157: void Sound_Init(void)
1158: {
1159: /* Build volume/env tables, ... */
1160: Ym2149_Init();
1.1.1.14 root 1161:
1.1.1.11 root 1162: Sound_Reset();
1.1.1.7 root 1163: }
1164:
1165:
1166: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1167: /**
1.1.1.12 root 1168: * Reset the sound emulation (called from Reset_ST() in reset.c)
1.1.1.11 root 1169: */
1.1.1.7 root 1170: void Sound_Reset(void)
1171: {
1.1.1.11 root 1172: /* Lock audio system before accessing variables which are used by the
1173: * callback function, too! */
1174: Audio_Lock();
1.1.1.9 root 1175:
1.1.1.11 root 1176: /* Clear sound mixing buffer: */
1.1.1.12 root 1177: memset(MixBuffer, 0, sizeof(MixBuffer));
1.1.1.7 root 1178:
1.1.1.11 root 1179: /* Clear cycle counts, buffer index and register '13' flags */
1180: Cycles_SetCounter(CYCLES_COUNTER_SOUND, 0);
1.1.1.14 root 1181: bEnvelopeFreqFlag = false;
1182:
1.1.1.11 root 1183: CompleteSndBufIdx = 0;
1184: /* We do not start with 0 here to fake some initial samples: */
1185: nGeneratedSamples = SoundBufferSize + SAMPLES_PER_FRAME;
1186: ActiveSndBufIdx = nGeneratedSamples % MIXBUFFER_SIZE;
1.1.1.16 root 1187: SamplesPerFrame = SAMPLES_PER_FRAME;
1188: CurrentSamplesNb = 0;
1189: ActiveSndBufIdxAvi = ActiveSndBufIdx;
1.1.1.15 root 1190: //fprintf ( stderr , "Sound_Reset SoundBufferSize %d SAMPLES_PER_FRAME %d nGeneratedSamples %d , ActiveSndBufIdx %d\n" ,
1191: // SoundBufferSize , SAMPLES_PER_FRAME, nGeneratedSamples , ActiveSndBufIdx );
1.1.1.7 root 1192:
1.1.1.12 root 1193: Ym2149_Reset();
1.1.1.9 root 1194:
1.1.1.11 root 1195: Audio_Unlock();
1.1.1.7 root 1196: }
1197:
1198:
1199: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1200: /**
1201: * Reset the sound buffer index variables.
1.1.1.16 root 1202: * Very important : this function should only be called by setting
1203: * Sound_BufferIndexNeedReset=true ; sound buffer index should be reset
1204: * only after the sound for the whole VBL was updated (CurrentSamplesNb returns to 0)
1205: * else it will alter the value of DMA Frame Count ($ff8909/0b/0d) and
1206: * could cause crashes in some programs.
1.1.1.11 root 1207: */
1.1.1.9 root 1208: void Sound_ResetBufferIndex(void)
1.1.1.7 root 1209: {
1.1.1.11 root 1210: Audio_Lock();
1211: nGeneratedSamples = SoundBufferSize + SAMPLES_PER_FRAME;
1212: ActiveSndBufIdx = (CompleteSndBufIdx + nGeneratedSamples) % MIXBUFFER_SIZE;
1.1.1.16 root 1213: SamplesPerFrame = SAMPLES_PER_FRAME;
1214: CurrentSamplesNb = 0;
1215: ActiveSndBufIdxAvi = ActiveSndBufIdx;
1.1.1.15 root 1216: //fprintf ( stderr , "Sound_ResetBufferIndex SoundBufferSize %d SAMPLES_PER_FRAME %d nGeneratedSamples %d , ActiveSndBufIdx %d\n" ,
1217: // SoundBufferSize , SAMPLES_PER_FRAME, nGeneratedSamples , ActiveSndBufIdx );
1.1.1.11 root 1218: Audio_Unlock();
1.1.1.7 root 1219: }
1220:
1221:
1222: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1223: /**
1224: * Save/Restore snapshot of local variables('MemorySnapShot_Store' handles type)
1225: */
1.1.1.12 root 1226: void Sound_MemorySnapShot_Capture(bool bSave)
1.1.1.7 root 1227: {
1.1.1.11 root 1228: /* Save/Restore details */
1.1.1.12 root 1229: MemorySnapShot_Store(&stepA, sizeof(stepA));
1230: MemorySnapShot_Store(&stepB, sizeof(stepB));
1231: MemorySnapShot_Store(&stepC, sizeof(stepC));
1232: MemorySnapShot_Store(&posA, sizeof(posA));
1233: MemorySnapShot_Store(&posB, sizeof(posB));
1234: MemorySnapShot_Store(&posC, sizeof(posC));
1235:
1236: MemorySnapShot_Store(&mixerTA, sizeof(mixerTA));
1237: MemorySnapShot_Store(&mixerTB, sizeof(mixerTB));
1238: MemorySnapShot_Store(&mixerTC, sizeof(mixerTC));
1239: MemorySnapShot_Store(&mixerNA, sizeof(mixerNA));
1240: MemorySnapShot_Store(&mixerNB, sizeof(mixerNB));
1241: MemorySnapShot_Store(&mixerNC, sizeof(mixerNC));
1242:
1243: MemorySnapShot_Store(&noiseStep, sizeof(noiseStep));
1244: MemorySnapShot_Store(&noisePos, sizeof(noisePos));
1245: MemorySnapShot_Store(¤tNoise, sizeof(currentNoise));
1246: MemorySnapShot_Store(&RndRack, sizeof(RndRack));
1247:
1248: MemorySnapShot_Store(&envStep, sizeof(envStep));
1249: MemorySnapShot_Store(&envPos, sizeof(envPos));
1250: MemorySnapShot_Store(&envShape, sizeof(envShape));
1.1.1.14 root 1251:
1.1.1.12 root 1252: MemorySnapShot_Store(&EnvMask3Voices, sizeof(EnvMask3Voices));
1253: MemorySnapShot_Store(&Vol3Voices, sizeof(Vol3Voices));
1.1.1.14 root 1254:
1.1.1.12 root 1255: MemorySnapShot_Store(SoundRegs, sizeof(SoundRegs));
1256:
1.1.1.14 root 1257: // MemorySnapShot_Store(&YmVolumeMixing, sizeof(YmVolumeMixing));
1258: // MemorySnapShot_Store(&UseLowPassFilter, sizeof(UseLowPassFilter));
1.1.1.7 root 1259: }
1260:
1261:
1262: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1263: /**
1264: * Find how many samples to generate and store in 'nSamplesToGenerate'
1265: * Also update sound cycles counter to store how many we actually did
1266: * so generates set amount each frame.
1.1.1.16 root 1267: * If FillFrame is true, this means we reach the end of the VBL and me must
1268: * add as many samples as necessary to get a total of SamplesPerFrame
1269: * for this VBL.
1.1.1.11 root 1270: */
1.1.1.16 root 1271: static int Sound_SetSamplesPassed(bool FillFrame)
1.1 root 1272: {
1.1.1.11 root 1273: int nSoundCycles;
1.1.1.16 root 1274: int SamplesToGenerate; /* How many samples are needed for this time-frame */
1275:
1276: nSoundCycles = Cycles_GetCounter(CYCLES_COUNTER_VIDEO);
1.1.1.11 root 1277:
1.1.1.16 root 1278: /* example : 160256 cycles per VBL, 44Khz = 882 samples per VBL at 50 Hz */
1279: /* 882/160256 samples per cpu clock cycle */
1.1.1.11 root 1280:
1.1.1.16 root 1281: /* Total number of samples that we should have at this point of the VBL */
1282: SamplesToGenerate = nSoundCycles * SamplesPerFrame
1283: / ClocksTimings_GetCyclesPerVBL ( ConfigureParams.System.nMachineType , nScreenRefreshRate );
1.1.1.11 root 1284:
1.1.1.16 root 1285: //if (SamplesToGenerate > SamplesPerFrame )
1286: //fprintf ( stderr , "over run %d %d\n" , SamplesPerFrame , SamplesToGenerate );
1.1.1.11 root 1287:
1.1.1.16 root 1288: if (SamplesToGenerate > SamplesPerFrame)
1289: SamplesToGenerate = SamplesPerFrame;
1290:
1291: SamplesToGenerate -= CurrentSamplesNb; /* don't count samples that were already generated up to now */
1292: if ( SamplesToGenerate < 0 )
1293: SamplesToGenerate = 0;
1294:
1295:
1296: /* If we're called from the VBL interrupt (FillFrame==true), we must ensure we have */
1297: /* an exact total of SamplesPerFrame samples during a full VBL (we take into account */
1298: /* the samples that were already generated during this VBL) */
1299: if ( FillFrame )
1300: {
1301: SamplesToGenerate = SamplesPerFrame - CurrentSamplesNb; /* how many samples are missing to reach SamplesPerFrame */
1302: if ( SamplesToGenerate < 0 )
1303: SamplesToGenerate = 0;
1304: }
1.1.1.11 root 1305:
1.1.1.16 root 1306: /* Check we don't fill the sound's ring buffer before it's played by Audio_Callback() */
1307: /* This should never happen, except if the system suffers major slowdown due to other */
1308: /* processes or if we run in fast forward mode. */
1309: /* In the case of slowdown, we set Sound_BufferIndexNeedReset to "resync" the working */
1310: /* buffer's index ActiveSndBufIdx with the system buffer's index CompleteSndBufIdx. */
1311: /* In the case of fast forward, we do nothing here, Sound_BufferIndexNeedReset will be */
1312: /* set when the user exits fast forward mode. */
1313: if ( ( SamplesToGenerate > MIXBUFFER_SIZE - nGeneratedSamples ) && ( ConfigureParams.System.bFastForward == false )
1314: && ( ConfigureParams.Sound.bEnableSound == true ) )
1.1.1.11 root 1315: {
1.1.1.16 root 1316: Log_Printf ( LOG_WARN , "Your system is too slow, some sound samples were not correctly emulated\n" );
1317: Sound_BufferIndexNeedReset = true;
1.1.1.11 root 1318: }
1.1.1.16 root 1319:
1320: //fprintf ( stderr , "vbl %d hbl %d samp_gen %d / %d frac %lx\n" , nVBLs , nHBL , SamplesToGenerate , SamplesPerFrame , (long int)SamplesPerFrame_unrounded );
1321:
1322: return SamplesToGenerate;
1.1 root 1323: }
1324:
1.1.1.5 root 1325:
1.1.1.2 root 1326: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1327: /**
1328: * Generate samples for all channels during this time-frame
1329: */
1.1.1.16 root 1330: static void Sound_GenerateSamples(int SamplesToGenerate)
1.1 root 1331: {
1.1.1.12 root 1332: int i;
1333: int idx;
1.1.1.14 root 1334:
1.1.1.16 root 1335: if (SamplesToGenerate <= 0)
1.1.1.12 root 1336: return;
1.1.1.14 root 1337:
1.1.1.15 root 1338: if (ConfigureParams.System.nMachineType == MACHINE_FALCON)
1.1.1.11 root 1339: {
1.1.1.16 root 1340: for (i = 0; i < SamplesToGenerate; i++)
1.1.1.15 root 1341: {
1342: idx = (ActiveSndBufIdx + i) % MIXBUFFER_SIZE;
1.1.1.17! root 1343: MixBuffer[idx][0] = MixBuffer[idx][1] = Subsonic_IIR_HPF_Left( YM2149_NextSample() );
1.1.1.15 root 1344: }
1345: /* If Falcon emulation, crossbar does the job */
1.1.1.16 root 1346: Crossbar_GenerateSamples(ActiveSndBufIdx, SamplesToGenerate);
1.1.1.11 root 1347: }
1.1.1.15 root 1348: else if (ConfigureParams.System.nMachineType != MACHINE_ST)
1349: {
1.1.1.16 root 1350: for (i = 0; i < SamplesToGenerate; i++)
1.1.1.15 root 1351: {
1352: idx = (ActiveSndBufIdx + i) % MIXBUFFER_SIZE;
1353: MixBuffer[idx][0] = MixBuffer[idx][1] = (YM2149_NextSample() >> 1);
1354: }
1.1.1.17! root 1355: /* If Ste or TT emulation, DmaSnd does mixing and filtering */
1.1.1.16 root 1356: DmaSnd_GenerateSamples(ActiveSndBufIdx, SamplesToGenerate);
1.1.1.15 root 1357: }
1358: else if (ConfigureParams.System.nMachineType == MACHINE_ST)
1359: {
1.1.1.16 root 1360: for (i = 0; i < SamplesToGenerate; i++)
1.1.1.15 root 1361: {
1362: idx = (ActiveSndBufIdx + i) % MIXBUFFER_SIZE;
1.1.1.17! root 1363: MixBuffer[idx][0] = MixBuffer[idx][1] = Subsonic_IIR_HPF_Left( YM2149_NextSample() );
1.1.1.15 root 1364: }
1365: }
1.1.1.12 root 1366:
1.1.1.16 root 1367: ActiveSndBufIdx = (ActiveSndBufIdx + SamplesToGenerate) % MIXBUFFER_SIZE;
1368: nGeneratedSamples += SamplesToGenerate;
1369: CurrentSamplesNb += SamplesToGenerate; /* number of samples generated for current VBL */
1.1 root 1370: }
1371:
1.1.1.5 root 1372:
1.1.1.2 root 1373: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1374: /**
1375: * This is called to built samples up until this clock cycle
1.1.1.16 root 1376: * Sound_Update can be called several times during a VBL ; we must ensure
1377: * that we generate exactly SamplesPerFrame samples between 2 calls
1378: * to Sound_Update_VBL.
1.1.1.11 root 1379: */
1.1.1.16 root 1380: void Sound_Update(bool FillFrame)
1.1 root 1381: {
1.1.1.11 root 1382: int OldSndBufIdx = ActiveSndBufIdx;
1.1.1.16 root 1383: int SamplesToGenerate;
1.1.1.5 root 1384:
1.1.1.11 root 1385: /* Make sure that we don't interfere with the audio callback function */
1386: Audio_Lock();
1.1.1.6 root 1387:
1.1.1.16 root 1388: /* Find how many samples to generate */
1389: SamplesToGenerate = Sound_SetSamplesPassed( FillFrame );
1390:
1.1.1.11 root 1391: /* And generate */
1.1.1.16 root 1392: Sound_GenerateSamples( SamplesToGenerate );
1.1 root 1393:
1.1.1.11 root 1394: /* Allow audio callback function to occur again */
1395: Audio_Unlock();
1.1.1.6 root 1396:
1.1.1.11 root 1397: /* Save to WAV file, if open */
1398: if (bRecordingWav)
1.1.1.16 root 1399: WAVFormat_Update(MixBuffer, OldSndBufIdx, SamplesToGenerate);
1.1 root 1400: }
1401:
1.1.1.5 root 1402:
1.1.1.2 root 1403: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1404: /**
1.1.1.16 root 1405: * On the end of each VBL, complete audio buffer to reach SamplesPerFrame samples.
1406: * As Sound_Update(false) could be called several times during the VBL, the audio
1407: * buffer might be already partially filled.
1408: * We must first complete the buffer using the same value of SamplesPerFrame
1409: * by calling Sound_Update(true) ; then we can compute a new value for
1410: * SamplesPerFrame that will be used for the next VBL to come.
1.1.1.11 root 1411: */
1.1.1.5 root 1412: void Sound_Update_VBL(void)
1.1 root 1413: {
1.1.1.16 root 1414: Sound_Update(true); /* generate as many samples as needed to fill this VBL */
1415: //fprintf ( stderr , "vbl done %d %d\n" , SamplesPerFrame , CurrentSamplesNb );
1.1.1.5 root 1416:
1.1.1.16 root 1417: CurrentSamplesNb = 0; /* VBL is complete, reset counter for next VBL */
1418:
1419: /*Compute a fractional equivalent of SamplesPerFrame for the next VBL, to avoid rounding propagation */
1420: SamplesPerFrame_unrounded += (yms64) ClocksTimings_GetSamplesPerVBL ( ConfigureParams.System.nMachineType ,
1421: nScreenRefreshRate , nAudioFrequency );
1422: SamplesPerFrame = SamplesPerFrame_unrounded >> 28; /* use integer part */
1423: SamplesPerFrame_unrounded &= 0x0fffffff; /* keep fractional part in the lower 28 bits */
1424:
1425: /* Reset sound buffer if needed (after pause, fast forward, slow system, ...) */
1426: if ( Sound_BufferIndexNeedReset )
1427: {
1428: Sound_ResetBufferIndex ();
1429: Sound_BufferIndexNeedReset = false;
1430: }
1431:
1432: /* Record AVI audio frame is necessary */
1.1.1.15 root 1433: if ( bRecordingAvi )
1434: {
1.1.1.16 root 1435: int Len;
1.1.1.15 root 1436:
1.1.1.16 root 1437: Len = ActiveSndBufIdx - ActiveSndBufIdxAvi; /* number of generated samples for this frame */
1438: if ( Len < 0 )
1439: Len += MIXBUFFER_SIZE; /* end of ring buffer was reached */
1.1.1.15 root 1440:
1.1.1.16 root 1441: Avi_RecordAudioStream ( MixBuffer , ActiveSndBufIdxAvi , Len );
1.1.1.15 root 1442: }
1443:
1.1.1.16 root 1444: ActiveSndBufIdxAvi = ActiveSndBufIdx; /* save new position for next AVI audio frame */
1445:
1.1.1.11 root 1446: /* Clear write to register '13', used for YM file saving */
1.1.1.14 root 1447: bEnvelopeFreqFlag = false;
1.1 root 1448: }
1449:
1450:
1.1.1.2 root 1451: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1452: /**
1453: * Start recording sound, as .YM or .WAV output
1454: */
1.1.1.12 root 1455: bool Sound_BeginRecording(char *pszCaptureFileName)
1.1 root 1456: {
1.1.1.12 root 1457: bool bRet;
1.1.1.7 root 1458:
1.1.1.11 root 1459: if (!pszCaptureFileName || strlen(pszCaptureFileName) <= 3)
1460: {
1461: Log_Printf(LOG_ERROR, "Illegal sound recording file name!\n");
1.1.1.14 root 1462: return false;
1.1.1.11 root 1463: }
1464:
1465: /* Did specify .YM or .WAV? If neither report error */
1466: if (File_DoesFileExtensionMatch(pszCaptureFileName,".ym"))
1467: bRet = YMFormat_BeginRecording(pszCaptureFileName);
1468: else if (File_DoesFileExtensionMatch(pszCaptureFileName,".wav"))
1469: bRet = WAVFormat_OpenFile(pszCaptureFileName);
1470: else
1471: {
1472: Log_AlertDlg(LOG_ERROR, "Unknown Sound Recording format.\n"
1473: "Please specify a .YM or .WAV output file.");
1.1.1.14 root 1474: bRet = false;
1.1.1.11 root 1475: }
1476:
1477: return bRet;
1.1 root 1478: }
1479:
1.1.1.5 root 1480:
1.1.1.2 root 1481: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1482: /**
1483: * End sound recording
1484: */
1.1.1.7 root 1485: void Sound_EndRecording(void)
1.1 root 1486: {
1.1.1.11 root 1487: /* Stop sound recording and close files */
1488: if (bRecordingYM)
1489: YMFormat_EndRecording();
1490: if (bRecordingWav)
1491: WAVFormat_CloseFile();
1.1 root 1492: }
1493:
1.1.1.6 root 1494:
1.1.1.2 root 1495: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1496: /**
1497: * Are we recording sound data?
1498: */
1.1.1.12 root 1499: bool Sound_AreWeRecording(void)
1.1 root 1500: {
1.1.1.11 root 1501: return (bRecordingYM || bRecordingWav);
1.1 root 1502: }
1.1.1.12 root 1503:
1.1.1.16 root 1504:
1505: /*-----------------------------------------------------------------------*/
1506: /**
1507: * Rebuild volume conversion table
1508: */
1509: void Sound_SetYmVolumeMixing(void)
1510: {
1511: /* Build the volume conversion table */
1512: Ym2149_BuildVolumeTable();
1513: }
1514:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.