
221220000 张三 Linux zzy 5.15.146.1-microsoft-standard-WSL2 #1 SMP Thu Jan 11 04:09:03 UTC 2024 x86_64 x86_64 x86_64 GNU/Linux 21:14:43 up 3 days, 16:20, 1 user, load average: 0.53, 0.40, 0.61
106 lines
3.1 KiB
C
106 lines
3.1 KiB
C
/***************************************************************************************
|
|
* Copyright (c) 2014-2022 Zihao Yu, Nanjing University
|
|
*
|
|
* NEMU is licensed under Mulan PSL v2.
|
|
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
* You may obtain a copy of Mulan PSL v2 at:
|
|
* http://license.coscl.org.cn/MulanPSL2
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
|
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
|
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
|
*
|
|
* See the Mulan PSL v2 for more details.
|
|
***************************************************************************************/
|
|
|
|
#include <common.h>
|
|
#include <device/map.h>
|
|
#include <SDL2/SDL.h>
|
|
|
|
enum {
|
|
reg_freq,
|
|
reg_channels,
|
|
reg_samples,
|
|
reg_sbuf_size,
|
|
reg_init,
|
|
reg_count,
|
|
nr_reg
|
|
};
|
|
|
|
static uint8_t *sbuf = NULL;
|
|
static uint32_t *audio_base = NULL;
|
|
static uint32_t pos_read = 0;
|
|
|
|
#define MIN(a, b) ((a) < (b) ? (a) : (b))
|
|
|
|
static void audio_callback(void *userdata, uint8_t *stream, int len) {
|
|
uint32_t count = audio_base[reg_count];
|
|
uint32_t read_cnt = MIN(count, len);
|
|
uint32_t to_end_cnt = audio_base[reg_sbuf_size] - pos_read;
|
|
int32_t out_bound_cnt = read_cnt - to_end_cnt;
|
|
memset(stream, 0, len);
|
|
if (out_bound_cnt > 0) {
|
|
memcpy(stream, sbuf + pos_read, to_end_cnt);
|
|
memcpy(stream + to_end_cnt, sbuf, out_bound_cnt);
|
|
} else {
|
|
memcpy(stream, sbuf + pos_read, read_cnt);
|
|
}
|
|
pos_read += read_cnt;
|
|
pos_read %= audio_base[reg_sbuf_size];
|
|
// if (len > read_cnt) memset(stream + read_cnt, 0, len - read_cnt);
|
|
audio_base[reg_count] -= read_cnt;
|
|
}
|
|
|
|
static inline void init_audio_dev() {
|
|
SDL_AudioSpec s = {};
|
|
s.format = AUDIO_S16SYS; // 假设系统中音频数据的格式总是使用16位有符号数来表示
|
|
s.userdata = NULL; // 不使用
|
|
s.freq = audio_base[reg_freq];
|
|
s.channels = audio_base[reg_channels];
|
|
s.samples = audio_base[reg_samples];
|
|
s.callback = audio_callback;
|
|
|
|
assert(SDL_InitSubSystem(SDL_INIT_AUDIO) == 0);
|
|
assert(SDL_OpenAudio(&s, NULL) == 0);
|
|
SDL_PauseAudio(0);
|
|
}
|
|
|
|
static void audio_io_handler(uint32_t offset, int len, bool is_write) {
|
|
assert(offset % 4 == 0);
|
|
int idx = offset / 4;
|
|
switch (idx) {
|
|
case reg_freq:
|
|
case reg_channels:
|
|
case reg_samples:
|
|
break;
|
|
case reg_sbuf_size:
|
|
assert(is_write == false);
|
|
break;
|
|
case reg_init:
|
|
if (is_write && audio_base[reg_init] != 0) {
|
|
init_audio_dev();
|
|
}
|
|
break;
|
|
case reg_count:
|
|
// TODO: must be add a lock here
|
|
break;
|
|
default:
|
|
assert(0);
|
|
}
|
|
}
|
|
|
|
void init_audio() {
|
|
uint32_t space_size = sizeof(uint32_t) * nr_reg;
|
|
audio_base = (uint32_t *)new_space(space_size);
|
|
audio_base[reg_sbuf_size] = CONFIG_SB_SIZE;
|
|
audio_base[reg_init] = 0;
|
|
#ifdef CONFIG_HAS_PORT_IO
|
|
add_pio_map ("audio", CONFIG_AUDIO_CTL_PORT, audio_base, space_size, audio_io_handler);
|
|
#else
|
|
add_mmio_map("audio", CONFIG_AUDIO_CTL_MMIO, audio_base, space_size, audio_io_handler);
|
|
#endif
|
|
|
|
sbuf = (uint8_t *)new_space(CONFIG_SB_SIZE);
|
|
add_mmio_map("audio-sbuf", CONFIG_SB_ADDR, sbuf, CONFIG_SB_SIZE, NULL);
|
|
}
|