TheMole Posted June 11, 2023 Share Posted June 11, 2023 (edited) I'm trying to play back some pre-calculated wave files in hopes of getting digital sound samples working. I'm using the following playback code (apologies for the wierd assembly syntax, this is running as embedded assembly in a C program): // Set up sound chip for sample playback __asm__( // Load sound chip address in r5 " li r5, 0x8400 \n" // Mute channels 1 & 2 " li r6, 0x9fbf \n" " movb r6, *r5 \n" " swpb r6 \n" " movb r6, *r5 \n" // Mute channel 3 & noise channel " li r6, 0xdfff \n" " movb r6, *r5 \n" " swpb r6 \n" " movb r6, *r5 \n" // Set channel 1 to max frequency (div by 1) " li r6, 0x8100 \n" " movb r6, *r5 \n" " swpb r6 \n" " movb r6, *r5 \n" ); // Push samples __asm__( " li r4, 0x6000 \n" // Cartridge space " li r5, 0x8400 \n" // Sound chip address " li r6, 0x2000 \n" // Play 8192 samples from cartridge bank "loop: \n" " movb *r4+, *r5 \n" " dec r6 \n" " jne loop \n" The sample data lives in cartridge space, starting at 0x6000. The code above is meant to just blast through all 8192 bytes in the bank and push them to the sound chip, which is hopefully setup correctly. The sample data looks as follows, which I believe looks correct (the 9 in the higher nibble indicating that I'm toggling the attenuation of channel 1 with the value of the lower nibble): 98 94 94 94 93 95 99 99 99 99 98 9A 9A 98 96 95 94 94 94 97 9A 9A 9A 9A 99 96 94 95 99 98 99 97 94 95 95 94 99 9A 99 98 98 9A 9A 9A 99 99 97 94 94 95 97 99 9B 9B 98 94 94 94 94 98 99 99 98 95 96 98 98 97 98 95 97 9A 9A 9B 9B 9B 98 95 94 93 94 95 99 99 99 99 98 95 95 99 99 98 97 9A 9B 9A 9A 9A 99 95 95 94 94 96 99 95 94 94 94 95 9A 9B 9B 9B 98 98 98 95 99 9B 9A 99 98 97 95 95 98 99 95 94 95 94 95 9A 9A 99 98 99 98 98 99 9B 9B 9A 98 94 93 93 93 95 9A 9B 9A 97 94 97 96 95 98 9A 9B 9A 9A 99 99 98 94 94 95 95 96 99 9A 99 97 98 98 98 95 96 98 98 99 9A 9A 99 97 94 94 96 98 9A 9A 98 99 95 93 94 95 95 9A 9B 9A 9A 99 96 95 94 95 9A 9A 9A 99 98 99 99 99 97 95 94 94 94 94 98 9A 9A 9A 9A 95 94 94 95 98 9A 99 98 9A 98 95 96 95 96 98 95 95 95 98 99 9A 9B 9A 99 98 95 94 96 98 99 98 99 98 96 97 99 9A 98 97 95 94 94 98 99 98 9A 9A 96 99 98 98 98 95 95 98 9A 99 97 95 98 ... However, when running this in js99er (I don't have access to a real console here in Hong Kong) I just hear a few rapid low thumps. I haven't done a precise calculation of the number of cycles between each sample, but I would've figured that if it's off by a little I'd still be able to recognize the audio even if it wasn't playing at exactly the correct speed. The sample has a samplerate of ~11khz. Any thoughts on what I could be doing wrong? Edited June 11, 2023 by TheMole alignment in source code for readability 1 Quote Link to comment Share on other sites More sharing options...
Asmusr Posted June 11, 2023 Share Posted June 11, 2023 1 hour ago, TheMole said: However, when running this in js99er (I don't have access to a real console here in Hong Kong) I just hear a few rapid low thumps. I haven't done a precise calculation of the number of cycles between each sample, but I would've figured that if it's off by a little I'd still be able to recognize the audio even if it wasn't playing at exactly the correct speed. The sample has a samplerate of ~11khz. Any thoughts on what I could be doing wrong? That won't work in JS99er. The audio process is generating 1024 samples at a time from the PSG in it's current state. The only way you will be able to generate digitized sound in JS99er is by toggling the audio gate (cru bit 24) like in Perfect Push. You can try in Classic99 instead where I think it works because of Dragon's Lair 1 Quote Link to comment Share on other sites More sharing options...
Tursi Posted June 11, 2023 Share Posted June 11, 2023 Yep, that should work. I just did something similar again recently. Classic99 does this by simulating a separate DAC channel, any audio channel running more than 22khz or something (I forget the cutoff) switches to DAC mode, and the DAC channel is updated based on the CPU time, with the data transferred to the output buffer once a frame. I actually use the same output buffer for the audio gate and the "dac mode" output. There's a WAV file conversion tool in my video conversion toolchain that will output the bytes you've got there, if you need it. 1 Quote Link to comment Share on other sites More sharing options...
Tursi Posted June 11, 2023 Share Posted June 11, 2023 This is the actual code I used for 5500hz playback. In this case I left interrupts enabled, but in truth you don't hear the 60hz interrupts. However, it might affect the overall playback rate, since I tuned it based on the overall play time. Spoiler * this code is at >8330 and handles the sound chip write clr *r2 movb *r5+,@>8400 clr @>6000 b *r11 ***** some deleted data - but it copies the above to >8330 to read samples from a banked ROM ***** Naturally the extra jump and extra paging of the cart reduces the maximum sample rate ;) ***** WP at >8300 * init sound chip li r0,>9FBF movb r0,@>8400 swpb r0 movb r0,@>8400 li r0,>CFFF movb r0,@>8400 swpb r0 movb r0,@>8400 li r0,>8100 movb r0,@>8400 swpb r0 movb r0,@>8400 * set up the music playback LI R2,>601c * start page LI R3,>6058 * last page LI R4,7042 * bytes on last page LI R5,>6000 * current address LP1 * allow ints to happen LIMI 2 LIMI 0 * RESET SCREEN TIMEOUT (any value ok) LI R0,>4000 MOV R0,@>83D6 * play samples - low sample rate gives us some freedom bl @>8330 * do some delay - hand tuned, within 0.5 seconds of total length (46.3s) in Classic99 li r0,7 dly dec r0 jne dly * update pointers C r2,r3 * last page? jne notlast * jump if not dec r4 * count down last bytes jne lp1 * jump if not done finish limi 2 * just wait and let the user quit limi 0 jmp finish notlast ci r5,>8000 * check if done this page jne lp1 * jump if not inct r2 * next page li r5,>6000 * reset address jmp lp1 * loop around 1 Quote Link to comment Share on other sites More sharing options...
TheMole Posted June 11, 2023 Author Share Posted June 11, 2023 2 hours ago, Asmusr said: That won't work in JS99er. The audio process is generating 1024 samples at a time from the PSG in it's current state. The only way you will be able to generate digitized sound in JS99er is by toggling the audio gate (cru bit 24) like in Perfect Push. You can try in Classic99 instead where I think it works because of Dragon's Lair Ah, thanks, that would explain some things, indeed ! Unfortunately, I can't easily run Classic99 on my mac, but I'll give it a go in Mame when I get a chance... Quote Link to comment Share on other sites More sharing options...
TheMole Posted June 11, 2023 Author Share Posted June 11, 2023 1 hour ago, Tursi said: There's a WAV file conversion tool in my video conversion toolchain that will output the bytes you've got there, if you need it. Indeed, I saw that, and I did steal your 8-to-4 bit mapping table to use in my own tool :). 1 Quote Link to comment Share on other sites More sharing options...
Tursi Posted June 11, 2023 Share Posted June 11, 2023 2 hours ago, TheMole said: Ah, thanks, that would explain some things, indeed ! Unfortunately, I can't easily run Classic99 on my mac, but I'll give it a go in Mame when I get a chance... Maybe it's improved, but last time I checked MAME can't handle digitized audio on the TI either. Quote Link to comment Share on other sites More sharing options...
+mizapf Posted June 11, 2023 Share Posted June 11, 2023 I don't have the Audio gate trick, but the Dragon's Lair demo does have sound, also the Spaceballs video etc. 3 Quote Link to comment Share on other sites More sharing options...
Tursi Posted June 11, 2023 Share Posted June 11, 2023 12 minutes ago, mizapf said: I don't have the Audio gate trick, but the Dragon's Lair demo does have sound, also the Spaceballs video etc. Ah, excellent, I'm glad to hear that 2 Quote Link to comment Share on other sites More sharing options...
TheMole Posted June 12, 2023 Author Share Posted June 12, 2023 13 hours ago, mizapf said: I don't have the Audio gate trick, but the Dragon's Lair demo does have sound, also the Spaceballs video etc. Confirmed working in Mame! Thanks everyone for the help... 2 Quote Link to comment Share on other sites More sharing options...
retrodroid Posted June 18, 2023 Share Posted June 18, 2023 On 6/11/2023 at 7:03 AM, TheMole said: Ah, thanks, that would explain some things, indeed ! Unfortunately, I can't easily run Classic99 on my mac, but I'll give it a go in Mame when I get a chance... I use C99 on my M2 mac under Wine (you need the 64-bit C99 build). Works well enough, you do lose some of the fancy graphics features and such that really on windows libraries, but the basics are solid. 3 Quote Link to comment Share on other sites More sharing options...
TheMole Posted June 19, 2023 Author Share Posted June 19, 2023 Yeah, that could work indeed, but since there are options that don't require two layers of emulation/translation (wine and rosetta) I'll probably keep using Mame and js99er.net for now. Thanks for the confirmation though, good to know! 1 Quote Link to comment Share on other sites More sharing options...
Asmusr Posted June 21, 2023 Share Posted June 21, 2023 On 6/11/2023 at 1:51 PM, Tursi said: Classic99 does this by simulating a separate DAC channel, any audio channel running more than 22khz or something (I forget the cutoff) switches to DAC mode, and the DAC channel is updated based on the CPU time, with the data transferred to the output buffer once a frame. I actually use the same output buffer for the audio gate and the "dac mode" output. I guess I could do the same. How do you detect that the audio channel running more than 22khz? Is it just that if a volume change takes place less than x CPU cycles after the last one you add it to the output buffer? Quote Link to comment Share on other sites More sharing options...
Tursi Posted June 24, 2023 Share Posted June 24, 2023 On 6/21/2023 at 10:14 AM, Asmusr said: I guess I could do the same. How do you detect that the audio channel running more than 22khz? Is it just that if a volume change takes place less than x CPU cycles after the last one you add it to the output buffer? Nah, you can't play samples unless you set the channel frequency higher than audible frequencies. Normally, it's set to a count of 1 (roughly 48khz). However, on the Coleco at least we found some software that set a lower frequency. So I just set a somewhat arbitrary cutoff above the human hearing rate. Once the frequency is set that high (anything more than half your output sample rate), you aren't going to be generating audible tones anyway, and if you run the emulator sound at that rate, you'll get unpleasant noise anyway as the output frequency aliases with the emulated frequency. So, if the sound channel is set above that magic frequency, then I start treating it as a DAC instead of a tone generator. Classic99 counts against CPU cycles, and when the volume changes, we fill in the digital output buffer with as many samples as should have elapsed for the previous volume. At the end of a frame, then we finish filling out the buffer for 1/60th of a second, and forward that to the audio system. So the rate of volume change doesn't have any impact on whether it's a DAC channel, it's just used as the value to fill the output buffer. By spacing out the samples against the CPU speed, we get samples in more or less the right spacing. By blocking it out at 1/60th of a second at a time, it stays in sync with the rest of the audio generation. To labor the point with an example. At the start of any frame, I clear the DAC buffer (after transferring it to the audio system.) I remember the number of CPU cycles that we start the frame at. When the volume changes, I check the frequency. If it's above the cutoff, then we are in DAC mode, so I look at how many CPU cycles have executed since the top of frame. That tells me how far into the single-frame DAC buffer I am. I take the old volume, and fill the buffer with samples aligned to that volume (with the number of samples based on the number of CPU cycles and the output frequency). Then I remember where I left off, and remember the new volume level. Hopefully one of those two explanations makes sense. If you try to play samples by modulating the volume with an audible frequency, you actually hear that audible frequency (though the sample may also be recognizable too...). That was how I first figured out the high frequency requirement myself - in my first test I heard the tone, and realized I could set the tone generator to higher than the human hearing limit. 6 Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.