Streaming PCM Audio from SD Card?

All aspects of programming on the Commander X16.
Ed Minchau
Posts: 508
Joined: Sat Jul 11, 2020 3:30 pm

Re: Streaming PCM Audio from SD Card?

Post by Ed Minchau »

For my video demos I'm using 12970 Hz 16 bit mono PCM, loading 2594 bytes (1/10th of a second) from a serial file every time the AFLOW interrupt is triggered. AFLOW gets triggered when the PCM buffer only has 1024 bytes left, so that additional 2594 bytes doesn't overflow the buffer.

I use MACPTR to load the data into VRAM (12 calls of 200 bytes each, then one call of 194 bytes), and then use a separate subroutine to push the data from VRAM to PCM_AUDIO. MACPTR doesn't like pushing the data directly to PCM_AUDIO, which I assume is something to do with timing.
kelli217
Posts: 541
Joined: Sun Jul 05, 2020 11:27 pm

Re: Streaming PCM Audio from SD Card?

Post by kelli217 »

Sorry... just a nitpick:

s/BIOS/KERNAL/
Dacobi
Posts: 292
Joined: Sun Dec 19, 2021 1:44 am
Location: Samsø, Denmark
Contact:

Re: Streaming PCM Audio from SD Card?

Post by Dacobi »

I now have something that's almost working, streaming PCM from one open file, at least regarding the PCM audio part.
I still run into problems using "-sdcard" and also problems with VRAM corruption that affects my menu sprites and bitmap background. I think maybe I'm doing something wrong regarding the synchronisation between the various interrupts.

My approach is to use RAM Bank 1 as a cyclic buffer, where I first open the file, fill the FIFO and fill RAM Bank 1 with sample data. I then start the PCM playback and run an audio update function in VBLANK interrupt that loads up to 255 bytes into a load index in RAM Bank 1.

My code is as follows.

Audio load function:

Code: Select all

void load_audio(){
	int bi;
	
	cbm_k_setnam("sound1.bin");
	cbm_k_setlfs(2,8,0);
	cbm_k_open();
	
	cbm_k_ckout(2);
		  	
	while(!(PCMCTRL & 0x80)){

		sample_load = cbm_k_acptr(); //sample_load is unsigned char
        	__asm__("lda %v",sample_load); 
        	__asm__("sta $9f3d");

	}

	RAM_BANK = 1;
	for(bi = 0; bi < 8192; bi++){
		BANK_RAM[bi] = cbm_k_acptr();
	}
			
	load_index = 0; //int
	sample_index = 0; //int
	sample_max = 8191;
}
AFLOW interrupt:

Code: Select all

void maflow(void){
	
   RAM_BANK = 1;
   while(!(PCMCTRL & 0x80)){

	sample_load = BANK_RAM[sample_index];
        __asm__("lda %v",sample_load);
        __asm__("sta $9f3d");


	sample_index++;
	             
        if(sample_index > sample_max){
	     sample_index = 0;
        }
   }             
}	
Audio update function called in VBLANK interrupt:

Code: Select all

void update_audio(){
    int bcount;

    if(load_index != sample_index) {

	if(!(VERA_ISR & 0x8)){

	   RAM_BANK = 1;	
	   bcount = 0;

	   while(load_index != sample_index){
		BANK_RAM[load_index] = cbm_k_acptr();
		load_index++;
		
		if(load_index > sample_max){
			load_index = 0;
		}
		
		bcount++;
		
		if(bcount > 255){
			break;
		}
	  }

	}	
    }
}
Without "-sdcard" PCM audio works perfectly, but with "-sdcard" the PCM audio is still continuous but samples are played in chunks that are not "in the correct order". It sounds like playback jumps back and forth within the audio track.
My guess is that update_audio takes to long and is interrupted by AFLOW interrupt?

However there's also a general problem with VRAM corruptions which goes away if I remove all calls to audio load and update. Sometimes menu sprites and bitmap background gets corrupted and in some cases the program crashes all together.
Maybe something with AFLOW and VBLANKs update_audio that interrupt each other?

I guess maybe the fact that I'm using C hurts performance enough that update_audio takes to long?

Edit: the corruption seems to come from update_audio(), but not the actual loading of data, only the length of time the functions takes to complete. If I disable the call to update_audio in VBLANK the corruption disappears. If I modify update_audio so it no longer writes data to RAM Bank 1, but still reads data from the file the corruption is still there.
User avatar
Daedalus
Posts: 231
Joined: Fri Nov 11, 2022 3:03 am

Re: Streaming PCM Audio from SD Card?

Post by Daedalus »

Try moving your cyclic buffer into the top end of "normal" ram.

As to speed, I'm guessing here, not because I'm not familiar with C (I have WAY more experience with C than I do with 65c02 assembly.), but because I've never used C on the X16 so I'm not familiar with this implementation's precise mechanics.

But my gut feel is that your problem is here:
BANK_RAM[load_index] = cbm_k_acptr();

Or it could just be a simple bug I'm not seeing. Either way, moving the buffer out of the way of the "bank switching train" seems like a good isolation step. I don't think you should need this big of a buffer. So long as you're not loading other huge files all the time, you should be able to hold the frame rate.

There's a lot going on all at once, VBLANK and AFLOW, of course, share the same interrupt. AFLOW has a different disable mechanic than the others in that it's buffer has to get full enough.
Dacobi
Posts: 292
Joined: Sun Dec 19, 2021 1:44 am
Location: Samsø, Denmark
Contact:

Re: Streaming PCM Audio from SD Card?

Post by Dacobi »

But my gut feel is that your problem is here:
BANK_RAM[load_index] = cbm_k_acptr();
I don't know what you mean?

Anyway, changing the buffer to normal memory didn't change anything. Neither did changing the pointer from char[] to char* or changing the size to 4096 bytes.

It still runs fine without "-sdcard" and is skipping with it. However running the emulator with "-mhz 14" causes the audio to work fine with "-sdcard" so it must be a problem with speed/update taking to long.
Dacobi
Posts: 292
Joined: Sun Dec 19, 2021 1:44 am
Location: Samsø, Denmark
Contact:

Re: Streaming PCM Audio from SD Card?

Post by Dacobi »

I just found something interesting. My memory corruption has nothing to do with actually writing any data to my buffer but only whether I call cbm_k_acptr() in update_audio. If I call cbm_k_acptr() but don't write the value to the buffer I still get memory corruption and without call cbm_k_acptr() there's no corruption.

Edit: turns out to be not entirely the case. The only factor seems to be the amount of time update_audio takes.

Edit2: Just saw another weirdness. When PCM audio is enabled I have a small red text char in the top right corner of the screen, while my menu is rendering in bitmap mode with sprites. This is not present when the menu renders without PCM.
User avatar
Daedalus
Posts: 231
Joined: Fri Nov 11, 2022 3:03 am

Re: Streaming PCM Audio from SD Card?

Post by Daedalus »

Does it look like a little LED (8 pixels wide, 4 pixels high.)? If so, that's the emulator showing you the "activity light" that would be on the actual hardware. Red solid means "I'm busy loading from disk" and blinking means "Disk Error!"

But if it's a text character that's changing, I have no idea what that means.
Dacobi
Posts: 292
Joined: Sun Dec 19, 2021 1:44 am
Location: Samsø, Denmark
Contact:

Re: Streaming PCM Audio from SD Card?

Post by Dacobi »

that's the emulator showing you the "activity light"
Ahhh :lol: problem solved
Dacobi
Posts: 292
Joined: Sun Dec 19, 2021 1:44 am
Location: Samsø, Denmark
Contact:

Re: Streaming PCM Audio from SD Card?

Post by Dacobi »

I guess the only thing left to try would be writing the update_audio and maybe aflow functions in asm. I just have very little experience with 6502 asm, especially regarding 16 bit values/addresses.
Dacobi
Posts: 292
Joined: Sun Dec 19, 2021 1:44 am
Location: Samsø, Denmark
Contact:

Re: Streaming PCM Audio from SD Card?

Post by Dacobi »

I now have a solution that solves the sdcard and C speed issues, but is very janky and causes some noise in the PCM audio.

My approach is to open the file, fill the FIFO and then bypass the AFLOW interrupt entirely. Instead I start playback, wait a few frames and then load 200 bytes each VBLANK directly into the FIFO using inline asm and MACPTR.

PCM audio and my menu is now smooth both with and without "-sdcard".
The only problem now is some noise in the PCM audio, which I guess is because I load form the file directly into the FIFO without checking for AFLOW interrupt?

I'm going to try if I can expand my inline update_audio function to write to a buffer instead of directly to the FIFO.
Post Reply