mirror of
https://github.com/StewBC/mminer-apple2.git
synced 2024-12-28 00:29:41 +00:00
Audio Fixed
This commit is contained in:
parent
1496ca5762
commit
9a8454ab9e
@ -69,6 +69,23 @@ mRGB palette[8] = {
|
|||||||
{0XFF, 0XFF, 0XFF}, // White
|
{0XFF, 0XFF, 0XFF}, // White
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Global variables to emulate speaker clicks
|
||||||
|
#define SAMPLE_RATE 44100
|
||||||
|
enum {
|
||||||
|
SAMPLE_PREVIOUS,
|
||||||
|
OUTPUT_PREVIOUS,
|
||||||
|
SAMPLE_CURRENT,
|
||||||
|
NUM_SAMPLES,
|
||||||
|
};
|
||||||
|
struct A2SPEAKER {
|
||||||
|
float speaker_state;
|
||||||
|
float sample_rate;
|
||||||
|
float samples[NUM_SAMPLES];
|
||||||
|
};
|
||||||
|
typedef struct A2SPEAKER A2SPEAKER;
|
||||||
|
// Speaker should be part of MACHINE but I don't want to modify 6502.?
|
||||||
|
A2SPEAKER speaker;
|
||||||
|
|
||||||
// Global handles for SDL rendering
|
// Global handles for SDL rendering
|
||||||
SDL_Window *window;
|
SDL_Window *window;
|
||||||
SDL_Renderer *renderer;
|
SDL_Renderer *renderer;
|
||||||
@ -77,66 +94,6 @@ SDL_Texture *texture;
|
|||||||
int screen_updated = TARGET_FPS; // Counter - at TARGET_FPS forces screen update
|
int screen_updated = TARGET_FPS; // Counter - at TARGET_FPS forces screen update
|
||||||
int active_page = 0x4000; // 0x2000 or 0x4000 - active hires memory page
|
int active_page = 0x4000; // 0x2000 or 0x4000 - active hires memory page
|
||||||
|
|
||||||
// Global variables to emulate speaker clicks
|
|
||||||
#define SAMPLE_RATE 44100
|
|
||||||
int audio_paused = 1; // Audio is paused (1) or playing (0)
|
|
||||||
float speaker_state = 1.0; // Tracks whether the speaker is in the "high" or "low" state
|
|
||||||
double frequency = 440.0; // Frequency in Hz (dynamically calculated)
|
|
||||||
Uint64 last_toggle_time = 0; // Last time the speaker was toggled (in performance counter ticks)
|
|
||||||
Uint64 frequency_ticks = 0; // Frequency of the performance counter
|
|
||||||
|
|
||||||
// Function that gets called when the Apple II code toggles the speaker
|
|
||||||
void speaker_toggle() {
|
|
||||||
// Get the current time in performance counter ticks
|
|
||||||
Uint64 current_time = SDL_GetPerformanceCounter();
|
|
||||||
|
|
||||||
// If this isn't the first toggle, calculate the time difference (period)
|
|
||||||
if (last_toggle_time != 0) {
|
|
||||||
Uint64 period_ticks = current_time - last_toggle_time; // Time between toggles in ticks
|
|
||||||
double period_us = (double)period_ticks * 1000000.0 / (double)frequency_ticks; // Convert ticks to microseconds
|
|
||||||
|
|
||||||
if (period_us > 0) {
|
|
||||||
// Calculate frequency in Hz (1000000 microseconds = 1 second,
|
|
||||||
// so frequency = 1000000 / period_us)
|
|
||||||
frequency = 1000000.0 / period_us;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the last toggle time
|
|
||||||
last_toggle_time = current_time;
|
|
||||||
|
|
||||||
// Toggle the speaker state between 1 (high) and -1 (low)
|
|
||||||
speaker_state = -speaker_state;
|
|
||||||
|
|
||||||
// (re)start the SDL audio
|
|
||||||
if(audio_paused) {
|
|
||||||
audio_paused = 0;
|
|
||||||
SDL_PauseAudio(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SDL audio callback to generate the square wave
|
|
||||||
void audio_callback(void* userdata, Uint8* stream, int len) {
|
|
||||||
float* buffer = (float*)stream; // Output buffer (32-bit float samples)
|
|
||||||
int length = len / sizeof(float); // Number of float samples to generate
|
|
||||||
static int sample_index = 0;
|
|
||||||
|
|
||||||
// Calculate the number of samples per toggle based on the frequency
|
|
||||||
int samples_per_toggle = (int)(SAMPLE_RATE / (frequency / 1.75));
|
|
||||||
|
|
||||||
for (int i = 0; i < length; i += 2) { // Increment by 2 because of stereo (2 channels)
|
|
||||||
// Stereo output: Set both left (i) and right (i + 1) channels
|
|
||||||
buffer[i] = speaker_state; // Left channel
|
|
||||||
buffer[i + 1] = speaker_state; // Right channel
|
|
||||||
|
|
||||||
// Toggle speaker state after samples_per_toggle samples
|
|
||||||
if (++sample_index >= samples_per_toggle) {
|
|
||||||
sample_index = 0; // Reset the sample index
|
|
||||||
speaker_state = -speaker_state; // Toggle speaker state (square wave generation)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Present the selected HiRes screen (page)
|
// Present the selected HiRes screen (page)
|
||||||
void show_screen(uint16_t page) {
|
void show_screen(uint16_t page) {
|
||||||
int x, y;
|
int x, y;
|
||||||
@ -196,7 +153,7 @@ uint8_t io_read_callback(MACHINE *m, uint16_t address) {
|
|||||||
RAM_MAIN[KBD] &= 0x7F;
|
RAM_MAIN[KBD] &= 0x7F;
|
||||||
break;
|
break;
|
||||||
case SPEAKER:
|
case SPEAKER:
|
||||||
speaker_toggle();
|
speaker.speaker_state = 1.0f - speaker.speaker_state;
|
||||||
break;
|
break;
|
||||||
case LOWSCR:
|
case LOWSCR:
|
||||||
active_page = 0x2000;
|
active_page = 0x2000;
|
||||||
@ -289,12 +246,14 @@ int init_sdl() {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create RGB surface
|
||||||
surface = SDL_CreateRGBSurface(0, 140, 192, 32, 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000);
|
surface = SDL_CreateRGBSurface(0, 140, 192, 32, 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000);
|
||||||
if(surface == NULL) {
|
if(surface == NULL) {
|
||||||
printf("Surface could not be created! SDL_Error: %s\n", SDL_GetError());
|
printf("Surface could not be created! SDL_Error: %s\n", SDL_GetError());
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create texture for pixel rendering
|
||||||
texture = SDL_CreateTextureFromSurface(renderer, surface);
|
texture = SDL_CreateTextureFromSurface(renderer, surface);
|
||||||
if(texture == NULL) {
|
if(texture == NULL) {
|
||||||
printf("Texture could not be created! SDL_Error: %s\n", SDL_GetError());
|
printf("Texture could not be created! SDL_Error: %s\n", SDL_GetError());
|
||||||
@ -310,20 +269,19 @@ int init_sdl() {
|
|||||||
wanted_spec.format = AUDIO_F32SYS;
|
wanted_spec.format = AUDIO_F32SYS;
|
||||||
wanted_spec.channels = 2; // Stereo sound
|
wanted_spec.channels = 2; // Stereo sound
|
||||||
wanted_spec.samples = 4096; // Buffer size
|
wanted_spec.samples = 4096; // Buffer size
|
||||||
wanted_spec.callback = audio_callback;
|
wanted_spec.callback = NULL; // No callback
|
||||||
|
|
||||||
// Open the audio device
|
// Open the audio device
|
||||||
if (SDL_OpenAudio(&wanted_spec, &obtained_spec) < 0) {
|
if (SDL_OpenAudio(&wanted_spec, &obtained_spec) < 0) {
|
||||||
printf("Couldn't open audio! SDL_Error: %s\n", SDL_GetError());
|
printf("Couldn't open audio! SDL_Error: %s\n", SDL_GetError());
|
||||||
SDL_Quit();
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start with Audio paused
|
// Set up the speaker now that the obtained freq is known
|
||||||
SDL_PauseAudio(1);
|
memset(&speaker, 0, sizeof(speaker));
|
||||||
|
memset(speaker.samples, 0, NUM_SAMPLES * sizeof(float));
|
||||||
// Init the global
|
// The 1.5 is a fudge number to make sure the audio doesn't lag
|
||||||
frequency_ticks = SDL_GetPerformanceFrequency(); // Ticks per second
|
speaker.sample_rate = ((float)CPU_FREQUENCY / obtained_spec.freq) + 1.5f;
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@ -331,15 +289,25 @@ int init_sdl() {
|
|||||||
int main(int argc, char* argv[]) {
|
int main(int argc, char* argv[]) {
|
||||||
int quit = 0;
|
int quit = 0;
|
||||||
SDL_Event e;
|
SDL_Event e;
|
||||||
|
float sample_cycles;
|
||||||
Uint64 start_time, end_time;
|
Uint64 start_time, end_time;
|
||||||
|
Uint64 ticks_per_clock_cycle = SDL_GetPerformanceFrequency() / CPU_FREQUENCY; // Ticks per microsecond
|
||||||
|
|
||||||
if(!init_sdl()) {
|
if(!init_sdl()) {
|
||||||
|
SDL_Quit();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make this machine an Apple II and load Manic Miner into RAM
|
// Make this machine an Apple II and load Manic Miner into RAM
|
||||||
AppleII_configure(&m);
|
AppleII_configure(&m);
|
||||||
Uint64 ticks_per_clock_cycle = frequency_ticks / CPU_FREQUENCY; // Ticks per microsecond
|
|
||||||
|
// Start the audio
|
||||||
|
SDL_PauseAudio(0);
|
||||||
|
|
||||||
|
// After init_sdl that sets up speaker.sample_rate
|
||||||
|
sample_cycles = speaker.sample_rate;
|
||||||
|
const float alpha = 0.98f; // High pass filter coefficient
|
||||||
|
const float beta = 0.98f; // Low pass filter coefficient
|
||||||
|
|
||||||
// Start running the sim loop
|
// Start running the sim loop
|
||||||
while (!quit) {
|
while (!quit) {
|
||||||
@ -365,6 +333,27 @@ int main(int argc, char* argv[]) {
|
|||||||
// Step the sim one cycle
|
// Step the sim one cycle
|
||||||
machine_step(&m);
|
machine_step(&m);
|
||||||
cycles++;
|
cycles++;
|
||||||
|
speaker.samples[SAMPLE_CURRENT] += speaker.speaker_state;
|
||||||
|
if(--sample_cycles <= 0.0f) {
|
||||||
|
float output_previous = speaker.samples[OUTPUT_PREVIOUS];
|
||||||
|
float sample_previous = speaker.samples[SAMPLE_PREVIOUS];
|
||||||
|
float sample_current = speaker.samples[SAMPLE_CURRENT];
|
||||||
|
// Calculate a high pass and low pass filtered sample
|
||||||
|
float high_pass_result = alpha * (output_previous + sample_current - sample_previous);
|
||||||
|
float filter_result = beta * high_pass_result + (1 - beta) * output_previous;
|
||||||
|
// Save the current sample of next time
|
||||||
|
speaker.samples[SAMPLE_PREVIOUS] = sample_current;
|
||||||
|
// And make the filtered sample prev and also use as left and right for SDL
|
||||||
|
speaker.samples[OUTPUT_PREVIOUS] = filter_result;
|
||||||
|
speaker.samples[SAMPLE_CURRENT] = filter_result;
|
||||||
|
// Queue the stero samples
|
||||||
|
SDL_QueueAudio(1, &speaker.samples[OUTPUT_PREVIOUS], 2 * sizeof(float));
|
||||||
|
// Start a new sample
|
||||||
|
speaker.samples[SAMPLE_CURRENT] = 0.0f;
|
||||||
|
// Reset when the next samples will be queued
|
||||||
|
sample_cycles += speaker.sample_rate;
|
||||||
|
|
||||||
|
}
|
||||||
} while(m.cpu.instruction_cycle != -1);
|
} while(m.cpu.instruction_cycle != -1);
|
||||||
|
|
||||||
// If a call was made to the MLI, it's to QUIT
|
// If a call was made to the MLI, it's to QUIT
|
||||||
@ -379,11 +368,6 @@ int main(int argc, char* argv[]) {
|
|||||||
|
|
||||||
// When the speaker is not being toggled, it needs to turn off
|
// When the speaker is not being toggled, it needs to turn off
|
||||||
end_time = SDL_GetPerformanceCounter();
|
end_time = SDL_GetPerformanceCounter();
|
||||||
if(!audio_paused && end_time - last_toggle_time > (frequency_ticks / 32)) {
|
|
||||||
audio_paused = 1;
|
|
||||||
SDL_PauseAudio(1);
|
|
||||||
end_time = SDL_GetPerformanceCounter();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to lock the SIM to the Apple II 1.023 MHz
|
// Try to lock the SIM to the Apple II 1.023 MHz
|
||||||
while ((end_time - start_time) < (ticks_per_clock_cycle * cycles)) {
|
while ((end_time - start_time) < (ticks_per_clock_cycle * cycles)) {
|
||||||
|
Loading…
Reference in New Issue
Block a user