major refactorings

Signed-off-by: kaguya3311 <kaguya3311@national.shitposting.agency>
This commit is contained in:
kaguya
2026-05-18 04:02:59 -04:00
parent f7aa6f913a
commit b28a6bcf29
211 changed files with 17699 additions and 8107 deletions
+173
View File
@@ -0,0 +1,173 @@
#include "pcm.h"
#include "hda.h"
#include "mm/pmm.h"
#include "mm/vmm.h"
#include "mm/memory.h"
#include "libk/stdio.h"
#include "libk/debug.h"
/* ── WAV chunk scanner ──────────────────────────────────────────────────────── */
/*
* Scan a buffer for RIFF/WAVE format. If it is a valid PCM WAV file, fills
* in *fmt_out, *data_offset and *data_size and returns true.
*
* WAV chunks are word-padded (each chunk rounds up to an even byte count), so
* we skip through them by (8 + ALIGN_UP(chunk_size, 2)) bytes at a time.
*/
static bool parse_wav(const uint8_t *buf, uint32_t buf_size,
hda_format_t *fmt_out,
uint32_t *data_offset,
uint32_t *data_size)
{
if (buf_size < 12) return false;
/* Check RIFF header */
if (buf[0]!='R'||buf[1]!='I'||buf[2]!='F'||buf[3]!='F') return false;
if (buf[8]!='W'||buf[9]!='A'||buf[10]!='V'||buf[11]!='E') return false;
uint32_t pos = 12;
bool got_fmt = false;
bool got_data = false;
hda_format_t fmt = {0};
while (pos + 8 <= buf_size) {
/* Read chunk ID and size (little-endian) */
const char *id = (const char *)(buf + pos);
uint32_t chunk_size = *(const uint32_t *)(buf + pos + 4);
pos += 8;
if (pos + chunk_size > buf_size) break; /* truncated */
if (id[0]=='f' && id[1]=='m' && id[2]=='t' && id[3]==' ') {
if (chunk_size < 16) { pos += chunk_size; continue; }
uint16_t audio_fmt = *(uint16_t *)(buf + pos + 0);
if (audio_fmt != 1) {
kprintf("[PCM] WAV audio format %u not supported (only PCM=1)\n",
audio_fmt);
return false;
}
fmt.channels = (uint8_t)*(uint16_t *)(buf + pos + 2);
fmt.sample_rate = *(uint32_t *)(buf + pos + 4);
fmt.bits_per_sample = (uint8_t)*(uint16_t *)(buf + pos + 14);
got_fmt = true;
} else if (id[0]=='d' && id[1]=='a' && id[2]=='t' && id[3]=='a') {
*data_offset = pos;
*data_size = chunk_size;
got_data = true;
break; /* no need to scan further */
}
/* Advance past chunk (WAV chunks are word-aligned) */
pos += chunk_size;
if (chunk_size & 1) pos++; /* skip pad byte */
}
if (!got_fmt || !got_data) return false;
*fmt_out = fmt;
return true;
}
/* ── Shared internal helper ─────────────────────────────────────────────────── */
static bool play_from_ext2(const char *filename,
bool force_fmt,
hda_format_t forced)
{
/* ── 1. Resolve path and read inode ─────────────────────────────────────── */
uint32_t inum = ext2_resolve_path(filename);
if (!inum) {
kprintf("[PCM] file not found: %s\n", filename);
return false;
}
ext2_inode_t inode;
if (!ext2_read_inode(inum, &inode)) {
kprintf("[PCM] failed to read inode for %s\n", filename);
return false;
}
uint32_t file_size = inode.i_size;
if (!file_size) {
kprintf("[PCM] %s is empty\n", filename);
return false;
}
/* ── 2. Allocate physically-contiguous DMA buffer ────────────────────────── */
/*
* pmm_alloc() returns a contiguous run of physical pages, which is exactly
* what HDA's DMA engine needs. We access the data through HHDM.
*/
uint32_t pages = (file_size + PAGE_SIZE - 1) / PAGE_SIZE;
void *phys = pmm_alloc(pages);
if (!phys) {
kprintf("[PCM] OOM: cannot allocate %u pages for %s\n", pages, filename);
return false;
}
uint8_t *virt = (uint8_t *)((uintptr_t)phys + MEM_PHYS_OFFSET);
/* Zero any padding at the end so stale bytes don't become noise */
memset(virt + file_size, 0, (size_t)(pages * PAGE_SIZE - file_size));
/* ── 3. Read file data ───────────────────────────────────────────────────── */
if (!ext2_read_file(&inode, virt)) {
kprintf("[PCM] failed to read %s\n", filename);
pmm_free(phys, pages);
return false;
}
/* ── 4. Determine format and audio data region ───────────────────────────── */
hda_format_t fmt;
uintptr_t audio_phys = (uintptr_t)phys;
uint32_t audio_size = file_size;
if (force_fmt) {
fmt = forced;
/* Treat the whole file as raw audio samples */
} else {
uint32_t data_offset = 0, data_size = 0;
if (parse_wav(virt, file_size, &fmt, &data_offset, &data_size)) {
/* Adjust the physical pointer to skip the WAV headers */
audio_phys += data_offset;
audio_size = data_size;
kprintf("[PCM] WAV: %u Hz %u-bit %u ch %u bytes of audio\n",
fmt.sample_rate, fmt.bits_per_sample, fmt.channels, audio_size);
} else {
/* Not a WAV assume raw 48 kHz, 16-bit stereo */
fmt.sample_rate = 48000;
fmt.bits_per_sample = 16;
fmt.channels = 2;
kprintf("[PCM] Raw PCM assumed: 48000 Hz 16-bit stereo %u bytes\n",
audio_size);
}
}
/* ── 5. Play ─────────────────────────────────────────────────────────────── */
bool ok = hda_play_pcm(audio_phys, audio_size, fmt);
if (ok)
hda_wait_complete(); /* blocks until the stream finishes */
else
kprintf("[PCM] hda_play_pcm failed\n");
/* ── 6. Free the DMA buffer ─────────────────────────────────────────────── */
pmm_free(phys, pages);
return ok;
}
/* ══════════════════════════════════════════════════════════════════════════════
* Public API
* ══════════════════════════════════════════════════════════════════════════════ */
bool pcm_play_file(const char *filename)
{
hda_format_t dummy = {0};
return play_from_ext2(filename, false, dummy);
}
bool pcm_play_raw(const char *filename, hda_format_t fmt)
{
return play_from_ext2(filename, true, fmt);
}