diff --git a/atari800/src/votraxsnd.c b/atari800/src/votraxsnd.c index d70560a..6edf36e 100644 --- a/atari800/src/votraxsnd.c +++ b/atari800/src/votraxsnd.c @@ -267,8 +267,8 @@ void VOTRAXSND_Process(void *sndbuffer, int sndn) while (sndn > 0) { int amount = ((sndn > VTRX_BLOCK_SIZE) ? VTRX_BLOCK_SIZE : sndn); votrax_process(votrax_buffer, amount, temp_votrax_buffer); - if (bit16) mix((SWORD *)sndbuffer, votrax_buffer, amount, 128/4); - else mix8((UBYTE *)sndbuffer, votrax_buffer, amount, 128/4); + if (bit16) mix((SWORD *)sndbuffer, votrax_buffer, amount, POKEYSND_volume >> 3); + else mix8((UBYTE *)sndbuffer, votrax_buffer, amount, POKEYSND_volume >> 3); sndbuffer = (char *) sndbuffer + VTRX_BLOCK_SIZE*(bit16 ? 2 : 1)*((num_pokeys == 2) ? 2: 1); sndn -= VTRX_BLOCK_SIZE; } diff --git a/atari800/src/votraxsnd.h b/atari800/src/votraxsnd.h index 0b26675..6edf36e 100644 --- a/atari800/src/votraxsnd.h +++ b/atari800/src/votraxsnd.h @@ -1,15 +1,276 @@ -#ifndef VOTRAXSND_H_ -#define VOTRAXSND_H_ +/* + * votraxsnd.c - Atari800 interface to votrax.c + * + * Copyright (C) 2007-2010 Atari800 development team (see DOC/CREDITS) + * + * This file is part of the Atari800 emulator project which emulates + * the Atari 400, 800, 800XL, 130XE, and 5200 8-bit computers. + * + * Atari800 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Atari800 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Atari800; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#include +#include #include "atari.h" -#include "votrax.h" +#include "util.h" +#include "votraxsnd.h" +#ifdef PBI_XLD +#include "pbi_xld.h" +#endif +#ifdef VOICEBOX +#include "voicebox.h" +#endif +#include "pokey.h" +#include "log.h" -void VOTRAXSND_PutByte(UBYTE byte); -void VOTRAXSND_Init(int playback_freq, int n_pokeys, int b16); -void VOTRAXSND_Frame(void); -void VOTRAXSND_Process(void *sndbuffer, int sndn); -extern int VOTRAXSND_busy; -void VOTRAXSND_Reinit(void); -void VOTRAXSND_ModifyRatio(double factor); +/* Votrax */ + +#define VTRX_RATE 24500 + +static double ratio; +static int bit16; +#define VTRX_BLOCK_SIZE 1024 +SWORD *temp_votrax_buffer = NULL; +SWORD *votrax_buffer = NULL; +int VOTRAXSND_busy = FALSE; +static int votrax_sync_samples; +static int dsprate; +static int num_pokeys; +static int samples_per_frame; +/*if SYNCHRONIZED_SOUND is not used and the sound generation runs in a + * separate thread, then these variables are accessed in two different + * threads: */ +static int votrax_written = FALSE; +static int votrax_written_byte = 0x3f; + +void VOTRAXSND_PutByte(UBYTE byte) +{ + /* put byte to voice box */ + votrax_sync_samples = (int)((1.0/ratio)*(double)Votrax_Samples((votrax_written_byte&0x3f), (byte&0x3f), votrax_sync_samples)); + votrax_written = TRUE; + votrax_written_byte = byte; + if (!VOTRAXSND_busy) { + VOTRAXSND_busy = TRUE; +#ifdef PBI_XLD + if (PBI_XLD_enabled) { + PBI_XLD_votrax_busy_callback(TRUE); /* idle -> busy */ + } +#endif + } +} + +static void VOTRAXSND_busy_callback_async(int busy_status) +{ + return; + /* do nothing */ +} + +static int votraxsnd_enabled(void) +{ + if ( +#ifdef VOICEBOX + !VOICEBOX_enabled && +#endif +#ifdef PBI_XLD + !PBI_XLD_v_enabled && + +#endif + 1) return FALSE; + else return TRUE; +} + +/* called from POKEYSND_Init */ +void VOTRAXSND_Init(int playback_freq, int n_pokeys, int b16) +{ + static struct Votrax_interface vi; + int temp_votrax_buffer_size; + bit16 = b16; + dsprate = playback_freq; + num_pokeys = n_pokeys; + if (!votraxsnd_enabled()) return; + if (num_pokeys != 1 && num_pokeys != 2) { + Log_print("VOTRAXSND_Init: cannot handle num_pokeys=%d", num_pokeys); +#ifdef PBI_XLD + PBI_XLD_v_enabled = FALSE; +#endif +#ifdef VOICEBOX + VOICEBOX_enabled = FALSE; +#endif + return; + } + vi.num = 1; + vi.BusyCallback = VOTRAXSND_busy_callback_async; + Votrax_Stop(); + Votrax_Start((void *)&vi); + samples_per_frame = dsprate/(Atari800_tv_mode == Atari800_TV_PAL ? 50 : 60); + ratio = (double)VTRX_RATE/(double)dsprate; +#ifdef VOICEBOX + temp_votrax_buffer_size = (int)(VTRX_BLOCK_SIZE*ratio*(VOICEBOX_BASEAUDF+1) + 10); /* +10 .. little extra? */ +#else + temp_votrax_buffer_size = (int)(VTRX_BLOCK_SIZE*ratio + 10); /* +10 .. little extra? */ +#endif + free(temp_votrax_buffer); + temp_votrax_buffer = (SWORD *)Util_malloc(temp_votrax_buffer_size*sizeof(SWORD)); + free(votrax_buffer); + votrax_buffer = (SWORD *)Util_malloc(VTRX_BLOCK_SIZE*sizeof(SWORD)); + + VOTRAXSND_busy = FALSE; + votrax_sync_samples = 0; +} + +void VOTRAXSND_Reinit(void) +{ + if (dsprate) VOTRAXSND_Init(dsprate, num_pokeys, bit16); +} + +/* process votrax and interpolate samples */ +static void votrax_process(SWORD *v_buffer, int len, SWORD *temp_v_buffer) +{ + static SWORD last_sample; + static SWORD last_sample2; + static double startpos; + static int have; + int max_left_sample_index = (int)(startpos + (double)(len - 1)*ratio); + int pos = 0; + double fraction = 0; + int i; + int floor_next_pos; + + if (have == 2) { + temp_v_buffer[0] = last_sample; + temp_v_buffer[1] = last_sample2; + Votrax_Update(0, temp_v_buffer + 2, (max_left_sample_index + 1 + 1) - 2); + } + else if (have == 1) { + temp_v_buffer[0] = last_sample; + Votrax_Update(0, temp_v_buffer + 1, (max_left_sample_index + 1 + 1) - 1); + } + else if (have == 0) { + Votrax_Update(0, temp_v_buffer, max_left_sample_index + 1 + 1); + } + else if (have < 0) { + Votrax_Update(0, temp_v_buffer, -have); + Votrax_Update(0, temp_v_buffer, max_left_sample_index + 1 + 1); + } + + for (i = 0; i < len; i++) { + SWORD left_sample; + SWORD right_sample; + SWORD interp_sample; + pos = (int)(startpos + (double)i*ratio); + fraction = startpos + (double)i*ratio - (double)pos; + left_sample = temp_v_buffer[pos]; + right_sample = temp_v_buffer[pos+1]; + interp_sample = (int)(left_sample + fraction*(double)(right_sample-left_sample)); + v_buffer[i] = interp_sample; + } + floor_next_pos = (int)(startpos + (double)len*ratio); + startpos = (startpos + (double)len*ratio) - (double)floor_next_pos; + if (floor_next_pos == max_left_sample_index) + { + have = 2; + last_sample = temp_v_buffer[floor_next_pos]; + last_sample2 = temp_v_buffer[floor_next_pos+1]; + } + else if (floor_next_pos == max_left_sample_index + 1) { + have = 1; + last_sample = temp_v_buffer[floor_next_pos]; + } + else { + have = (floor_next_pos - (max_left_sample_index + 2)); + } +} + +/* 16 bit mixing */ +static void mix(SWORD *dst, SWORD *src, int sndn, int volume) +{ + SWORD s1, s2; + int val; + + while (sndn--) { + s1 = *src; + s1 = s1*volume/128; + s2 = *dst; + src++; + val = s1 + s2; + if (val > 32767) val = 32767; + if (val < -32768) val = -32768; + *dst++ = val; + if (num_pokeys == 2) { + dst++; + } + } +} + +/* 8 bit mixing */ +static void mix8(UBYTE *dst, SWORD *src, int sndn, int volume) +{ + SWORD s1, s2; + int val; + + while (sndn--) { + s1 = *src; + s1 = s1*volume/128; + s2 = ((int)(*dst) - 0x80)*256; + src++; + val = s1 + s2; + if (val > 32767) val = 32767; + if (val < -32768) val = -32768; + *dst++ = (UBYTE)((val/256) + 0x80); + if (num_pokeys == 2) { + dst++; + } + } +} + +void VOTRAXSND_Frame(void) +{ + if (!votraxsnd_enabled()) return; +#ifdef VOICEBOX + if (VOICEBOX_enabled && VOICEBOX_ii) { + double factor = (VOICEBOX_BASEAUDF+1.0)/(POKEY_AUDF[3]+1.0); + ratio = (double)VTRX_RATE/(double)dsprate * factor; + samples_per_frame = ((double)dsprate/(double)(Atari800_tv_mode == Atari800_TV_PAL ? 50 : 60)) / factor; + } +#endif + votrax_sync_samples -= samples_per_frame; + if (votrax_sync_samples <= 0 ) { + votrax_sync_samples = 0; + VOTRAXSND_busy = FALSE; +#ifdef PBI_XLD + PBI_XLD_votrax_busy_callback(FALSE); /* busy -> idle */ +#endif + } +} + +void VOTRAXSND_Process(void *sndbuffer, int sndn) +{ + if (!votraxsnd_enabled()) return; + + if(votrax_written) { + votrax_written = FALSE; + Votrax_PutByte(votrax_written_byte); + } + sndn /= num_pokeys; + while (sndn > 0) { + int amount = ((sndn > VTRX_BLOCK_SIZE) ? VTRX_BLOCK_SIZE : sndn); + votrax_process(votrax_buffer, amount, temp_votrax_buffer); + if (bit16) mix((SWORD *)sndbuffer, votrax_buffer, amount, POKEYSND_volume >> 3); + else mix8((UBYTE *)sndbuffer, votrax_buffer, amount, POKEYSND_volume >> 3); + sndbuffer = (char *) sndbuffer + VTRX_BLOCK_SIZE*(bit16 ? 2 : 1)*((num_pokeys == 2) ? 2: 1); + sndn -= VTRX_BLOCK_SIZE; + } +} -#endif /* VOTRAXSND_H_ */