Subject: [stella] music compression From: Schwerin <schwerin@xxxxxxxx> Date: Mon, 5 Apr 1999 00:28:36 -0400 |
>Have you considered creating your own waveforms? Yes. Sorry I left out the juicy bits about the design of the sound engine. I figured I would tinker with it first, but I haven't updated it since my original post so I might as well describe what it is as of now. The sound code is a loop consisting of two parts. The top of the loop computes the sound output, and updates AUDV0 and AUDV1 (AUDCx and AUDFx are not changed, they are set to output a constant high line level). This results in two 4 bit channels. There are two "voices" per channel, and the voices are mixed together in software. The bottom of the loop updates the four pitch registers for the voices. This part of the loop is "application specific". In a music program, this is where you would do the overhead involved in reading from the song data and writing to the voice registers. The pitch registers are "latched" in a sense; you can leave them alone and they retain the previous value. In review: Sound Loop: Compute_Volumes() Set_Pitches() {repeat} Assuming that Compute_Volumes() and Set_Pitches() are each written so that they always take the same amount of time to run, the highest pitch available to us is some function of the total time of the loop. The current version requires between 1 and 2 scanlines. In this version, Set_Pitches() leaves the latch values unaffected, and just polls the joystick to see if the user wants to exit out of the sound playback mode. I believe it would be reasonable to update the sound every other scanline, leaving one line for sound computation and one line for computing desired pitch. The sound engine uses a waveform lookup table to generate sound. The table is 256 bytes in length, and page aligned. The table is one cycle of a sine wave. The sine wave is 3-bit sampled, one sample per byte (so the values range from 0 - 6). I used 0 - 6 because I think it samples well. You can use a different wave table if you wish, such as sawtooth or squarewave, or random, or whatever. All you need to do is use different data. The current sound engine uses that one table to generate all four voices, but this could be modified to have different instruments for each voice. My sine data is listed at the end of this post. The sound generation is a pretty standard kind of table scanning. Each voice gets its value from a lookup pointer to the wavetable. The pointer has it's own position and speed (frequency) in the table. Each "sound update" (SUP from now on), the position is incremented by the freq value. The wavetable is read at the new address. If the address goes beyond the table it wraps around to the front. If you decrease the frequency, the position moves in smaller increments and takes more time to reach the end of the table. Each SUP, voice0 and voice1 pointers are updated. The 3-bit lookup for voice0 and voice1 are added to produce a 4-bit value. This is stored in AUDV0. Voice2 and voice3 go into AUDV1. The "fancy" part of this table lookup is that the frequency is a 16 bit fixed point value, with the lower 8 bits as the fractional part. The position is also a 16 bit fixed value. The top half is used to index the wavetable. The bottom half is used to keep track of a "fractional" position. A simple frequency is 256. The pointer dutifully walks through the table, one address every SUP. The lowest frequency is 1. The pointer walks one address every 256 SUPs. At frequency 0, the pointer doesn't move and the voice is stuck at some constant volume value. The highest frequency theoretically is $7F00 which walks half the table every SUP, although it's safer to stick lower values which don't have such large skips. Frequencies above $8000 produce aliasing effects which end up sounding just like lower tones than what you want. The table is read "backwards". The fractional math has the benefit of a greater choice of pitches, but the drawback is that extra noise is introduced when you have a fractional frequency. The reason for this is that wave "samples" are stretched exactly the same number of times for frequencies like 256,512,768 etc and have a nice symmetry. For fractional frequencies the stretching alternates from long to short, more noticable in high tones. Obviously the best thing would be to have a fast processor (hey, don't boo me, I like 1 Mhz, I'm just being academic here). The next best thing is to have a fast sound loop called %100 of the time. This increases the "sample rate" and the quality of higher tones. Even without either of these, lower tones can come out clear. I have played chords on this engine. My current demo puts the 4 frequency values on the screen for joystick interface. You tweak them and listen to the results. It works. It's not the most beautiful sounding; especially if you have to spend half a minute setting up a chord, wishing to get to the next chord already. I still want to hear what it will sound like with music. I think a more rewarding exercise would be some kind of background game synthesizer with one channel for drums or effects and one for a melody. For the melody channel, get into stuff like note attack and decay and use samples or synthesis for an instrument specific sound beyond "Beep". For the drum channel use 4-bit samples and/or bursts of white noise. Any volunteers out there to collect some sound samples or write a program to automate this? I'm not sure what sampling rate, but probably 1 sample for every 2 scanlines is in the ballpark. (One line for sound, one for game logic and graphics). What would be great would be something that could take standard sound files like .wav for instance, and convert it into a hex listing of values from $00 to $0F. Of course this kind of thing requires a lot of hand tweaking. If anyone out there can convert or knows of programs to convert sound files into raw data, I can write a convert to source program. Also, if the music is always the same, the music can be presampled. Instead of reading a wavetable for each voice, read a wavetable for each channel. The chords get mashed together at assembly time and not in real-time. Or do peicemeal sampling of bits and pieces of a song and trigger parts when needed. The limitation here is memory storage, and lack of appropriate tools to design music & soundtracks. Anyway, I've rambled long enough...... > This allows you to use any >frequency you want. If you only update the sound registers once per line, >you get 0 to 15.7kHz, or 8 (resonable) octaves. If you update every other >scanline, you get a top end of 7.85kHz...still not bad. A synthesis model like mine requires 1 to 2 lines of compute time. A strictly 2 chan. wavetable sample model can be pulled off in 1 line (I think). > >Also, let's assume that no voice ever jumps more than an octave. If you >use relative motion, with chromatic scales, you need 3.5 bits of storage >for each voice, and you give each voice a total range of 4 octaves Can you explain this? Assume you stay the same, that's "0" Assume you go up 1 half step, thats "1" Assume you go up 11 half steps, thats "11" Assume you go down 1 half step, thats "12" Assume you go down 11 half steps, thats "22" So you need a value from 0 to 22, if 0 - 31 is 5 bits, then I'll buy that 0-22 is 4.5 bits but not 3.5 4.5 x 4:That's 18 bits per beat, which more than my "16 bits uncompressed" and way more than my 2.5 bits per beat goal. >(conservative 2-scanline approach). Also, since (I assume) you're doing >2 voices per channel, if you're just re-writing the volume data for each >channel, you can store the information in terms of channels instead of >voices. Doing this gives double the storage resolution, or 1.75 bits >per voice, per beat. Are you confusing the wavetable sample with note information? I don't see how I can combine the musical information for two voices into one channel. (Other than wavetable precomputation, which is a different matter than musical note data for a song). > >This all assumes infinite clock cycles with which to work. :) Right now, >I'm way too tired to do the math and get the *real* numbers. But it might >give you a different way of thinking about the problem. > Or maybe it's time to think about a different problem, like a more samples oriented engine as described above. But thank you for posting it and please help me understand some of your ideas on this. >"So why didn't he try to give feedback before my engine was locked >in place!???" - Andrew > "Where were you when the page was blank??" is my favorite variety of this phrase. For the intrepid, here is the loop. I took out my interface code because I'm shy on releasing it before it's improved. -Andrew *** "Quad" 4 voice wavetable based synthesiser *** *** Set the position variables to 0 on startup *** Set and change the freq variables for different pitches *** pos and freq are 16 bit Update_Sound: lda Pos0 clc adc Freq0 sta Pos0 lda Pos0+1 adc Freq0+1 sta Pos0+1 lda Pos1 clc adc Freq1 sta Pos1 lda Pos1+1 adc Freq1+1 sta Pos1+1 lda Pos2 clc adc Freq2 sta Pos2 lda Pos2+1 adc Freq2+1 sta Pos2+1 lda Pos3 clc adc Freq3 sta Pos3 lda Pos3+1 adc Freq3+1 sta Pos3+1 ldx Pos0+1 lda Wave,X sta temp ldx Pos1+1 lda Wave,X clc adc temp sta AUDV0 ldx Pos2+1 lda Wave,X sta temp ldx Pos3+1 lda Wave,X clc adc temp sta AUDV1 *** 256 bytes of 3 bit sampled sine wave *** Wave: .byte $03,$03,$03,$03,$03,$03,$03,$04 .byte $04,$04,$04,$04,$04,$04,$04,$04 .byte $04,$04,$04,$05,$05,$05,$05,$05 .byte $05,$05,$05,$05,$05,$05,$05,$05 .byte $05,$05,$06,$06,$06,$06,$06,$06 .byte $06,$06,$06,$06,$06,$06,$06,$06 .byte $06,$06,$06,$06,$06,$06,$06,$06 .byte $06,$06,$06,$06,$06,$06,$06,$06 .byte $06,$06,$06,$06,$06,$06,$06,$06 .byte $06,$06,$06,$06,$06,$06,$06,$06 .byte $06,$06,$06,$06,$06,$06,$06,$06 .byte $06,$06,$06,$06,$06,$06,$06,$05 .byte $05,$05,$05,$05,$05,$05,$05,$05 .byte $05,$05,$05,$05,$05,$05,$04,$04 .byte $04,$04,$04,$04,$04,$04,$04,$04 .byte $04,$04,$03,$03,$03,$03,$03,$03 .byte $03,$03,$03,$03,$03,$03,$03,$02 .byte $02,$02,$02,$02,$02,$02,$02,$02 .byte $02,$02,$02,$01,$01,$01,$01,$01 .byte $01,$01,$01,$01,$01,$01,$01,$01 .byte $01,$01,$00,$00,$00,$00,$00,$00 .byte $00,$00,$00,$00,$00,$00,$00,$00 .byte $00,$00,$00,$00,$00,$00,$00,$00 .byte $00,$00,$00,$00,$00,$00,$00,$00 .byte $00,$00,$00,$00,$00,$00,$00,$00 .byte $00,$00,$00,$00,$00,$00,$00,$00 .byte $00,$00,$00,$00,$00,$00,$00,$00 .byte $00,$00,$00,$00,$00,$00,$00,$01 .byte $01,$01,$01,$01,$01,$01,$01,$01 .byte $01,$01,$01,$01,$01,$01,$02,$02 .byte $02,$02,$02,$02,$02,$02,$02,$02 .byte $02,$02,$02,$02,$03,$03,$03,$03 -- Archives (includes files) at http://www.biglist.com/lists/stella/archives/ Unsub & more at http://www.biglist.com/lists/stella/
Current Thread |
---|
|
<- Previous | Index | Next -> |
---|---|---|
Re: [stella] music compression, Chris Wilkson | Thread | Re: [stella] music compression, cwilkson |
Re: [stella] music compression, Chris Wilkson | Date | Re: [stella] music compression, cwilkson |
Month |