/* * This file is part of zaniWok. * * zaniWok 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, or (at your option) * any later version. See the file COPYING for more information. * * zaniWok 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. */ #include #include #include #include #include #include "sound.h" /* Format of sound requiring no conversion */ #define CANON_FORMAT SND_FORMAT_LINEAR_16 #define CANON_RATE SND_RATE_LOW #define CANON_CHANNELS 2 /* 6502 clock rate (Hz) */ #define CLOCK_RATE (1023000.0/*/2*/) /* Convert from 6502 cycles to SoundKit samples */ #define CYCLES_TO_SAMPLES(cyc) (cyc * (CANON_RATE / CLOCK_RATE)) #define MAX_SAMPLE ((long) CYCLES_TO_SAMPLES (ULONG_MAX)) /* Values to store in a Sample to make it high/low */ #define SPEAKER_ON 0x2FFF2FFF/*0x2FFF*/ #define SPEAKER_OFF (~SPEAKER_ON + 0x00010001)/*(-SPEAKER_ON)*/ #define BUF_SIZE 65536 /* Amount of silence we truncate to */ #define SILENCE_THRESHOLD_SECS 0.5 #define SILENCE_THRESHOLD_SAMPLES ((int)(SILENCE_THRESHOLD_SECS * CANON_RATE)) /* Minimum number of samples we flush */ #define FLUSH_THRESHOLD_SAMPLES 512 /* Pointer to the samples in a SNDSoundStruct */ #define snd_data(s) ((Sample *)((void *)s + s->dataLocation)) static inline const long sampdiff (long b, long a) { if (b-a >= 0) return (b-a); else return (MAX_SAMPLE + (b-a)); } static int snd_finished (SNDSoundStruct *snd, int tag, int err) { int num_samples; SpeakerState *s; num_samples = snd->dataSize / sizeof (Sample); s = *((SpeakerState **)&snd->info[0]); mutex_lock (s->lock); SNDFree (snd); s->samples_queued -= num_samples; mutex_unlock (s->lock); return 0; } static inline void fill_samples (Sample *data, int count, const Sample value) { for (count-- ; count>=0 ; count--) *data++ = value; } static void do_flush_speaker (SpeakerState *s) { SNDSoundStruct *new; int err; long count; mutex_lock (s->lock); count = sampdiff (s->last_sample, s->base_sample); if (count > 0) { err = SNDAlloc (&new, count * sizeof (Sample), CANON_FORMAT, CANON_RATE, CANON_CHANNELS, sizeof s); assert (err == SND_ERR_NONE); *((SpeakerState **)&new->info[0]) = s; bcopy (s->buf, snd_data (new), count * sizeof (Sample)); err = SNDStartPlaying (new, ++s->tag, 1, 0, SND_NULL_FUN, snd_finished); assert (err == SND_ERR_NONE); s->samples_queued += count; s->base_sample = s->last_sample; } mutex_unlock (s->lock); } void check_flush_speaker (SpeakerState *s) { static int calls = 0; if (calls++ == 0) { long temp = s->last_sample - s->base_sample; if (temp >= FLUSH_THRESHOLD_SAMPLES || (temp > 0 && s->samples_queued > 0)) do_flush_speaker (s); else calls = 0; } else { calls %= 1; } } SpeakerState * init_speaker () { SpeakerState *s; /* Sanity check */ assert (SNDSamplesToBytes (1, CANON_CHANNELS, CANON_FORMAT) == sizeof (Sample)); s = (SpeakerState *) malloc (sizeof (SpeakerState)); s->speaker_is_on = 0; s->base_sample = 0; s->last_sample = 0; s->buf_size = BUF_SIZE; s->buf = (Sample *) malloc (s->buf_size * sizeof (Sample)); s->samples_queued = 0; s->tag = 0; s->lock = mutex_alloc (); return s; } int click_speaker (AccessType mode, MemoryElement *m, MachineState *state) { long current_sample, current_sample_relative, last_sample_relative; long diff; SpeakerState *s = state->speaker_state; mutex_lock (s->lock); current_sample = CYCLES_TO_SAMPLES (state->cycles); diff = sampdiff (current_sample, s->last_sample); if (diff > SILENCE_THRESHOLD_SAMPLES) { long mod; #if 0 if (s->samples_queued > 0) mod = diff - SILENCE_THRESHOLD_SAMPLES; else mod = diff - 1; /* catch little clicks */ #else mod = diff; #endif s->last_sample += mod; s->last_sample %= MAX_SAMPLE; s->base_sample += mod; s->base_sample %= MAX_SAMPLE; } current_sample_relative = sampdiff (current_sample, s->base_sample); if (current_sample_relative > BUF_SIZE) { #if 0 s->base_sample += current_sample_relative; s->base_sample %= MAX_SAMPLE; s->last_sample += current_sample_relative; s->last_sample = current_sample; s->last_sample %= MAX_SAMPLE; current_sample_relative = 0; #else s->base_sample = current_sample; s->last_sample = current_sample; current_sample_relative = 0; #endif } last_sample_relative = sampdiff (s->last_sample, s->base_sample); fill_samples (&s->buf[last_sample_relative], current_sample_relative - last_sample_relative, (s->speaker_is_on ? SPEAKER_ON : SPEAKER_OFF)); s->last_sample = current_sample; /* Toggle speaker state */ s->speaker_is_on = !s->speaker_is_on; #if 0 if (mode == WRITE) /* FIXME */ { /* set_sample (frame_data (frames[real_frame]), real_sample+1);*/ s->speaker_is_on = !s->speaker_is_on; } #endif mutex_unlock (s->lock); return 0; }