240 likes | 247 Views
This tool allows users to view waveform data graphically in .wav files with horizontal scrolling. It utilizes CRTC registers for simulation and pre-computes a large graphical image for smooth scrolling.
E N D
A viewer for .wav files On using some CRTC registers to simulate horizontal scrolling through a large data-file
‘waveview’ • We recorded brief samples of our voices • We saved the waveform data in .wav files • Now we consider how to create a useful tool for viewing that sound data graphically
VESA Display Mode • We selected our SuperVGA display mode: • pixel-resolution: 1024-by-768 • color-depth: 8 bits-per-pixel (256 colors) • Color-depth was selected for programming ease (simple to compute pixel-addresses) • Pixel-resolution was a ‘trade-off’ between showing lots of data simultaneously and being able to update the screen quickly
Vertical ‘bands’ • We focus on unsigned 8-bit pulse-codes • These values range from 0 to 255 (=0xFF) • The mid-value (0x80) represents ‘silence’ • For pulse-codes above 0x80 we will draw an upward band, for pulse-codes below 0x80 we will draw a downward band 0xFF silence = 0x80 0x00
How to draw a band int ymin = (vres–256)/2, ymax = ymin+256; int pcm = 255 - wavedata[ x ]; for (int y = ymin; y < ymax; y++) { if (((y<=0x80)&&(pcm<=y)&&(y<=0x80)) ||((y>=0x80)&&(pcm>=y)&&(y>=0x80))) vram[ y * hres + x ] = fgcolor; else vram[ y * hres + x ] = bgcolor; }
Using ‘mmap()’ • Instead of using the ‘read()’ system-call to transfer the raw waveform data from disk to memory, we elected to use ‘mmap()’ to map the .wav file to user’s address-space • This would make it possible to avoid any memory-allocation calls, or repetitious forward and backward ‘lseek()’ operations, as our user scrolls through the file’s data
Pre-computing our image • During development, however, we decided to ‘pre-compute’ the very large graphical image, storing it in memory allocated from our program’s heap using the ‘calloc()’ call • This reduced the amount of computation that had to be done during view ‘scrolling’ • But it requires us to set up some machinery for keeping track of a large pre-computed ‘virtual’ image and the much smaller ‘visible’ image
Screen as a moving ‘window’ srcmin srcnow srcmax screen-width Virtual image (too big to see all at once) Visual image (limited by screen’s width) User can shift the window left or right using arrow-keys VRAM
Scrolling was ‘slow’ • During development we discovered that our plan for horizontal scrolling was just not fast enough • So we ‘tweaked’ it by making use of two of the CRT Controller’s hardware registers: • The CRTC_PITCH register • The CRTC_START register • Access is Radeon-specific (non-portable)
The ‘pitch’ concept • Pixel-values are arranged linearly by rows • The separation-distance between any two vertically adjacent pixels is constant (and normally is equal to the length of a visible scanline – this constant is called the ‘pitch’ • But this ‘pitch’ is a programmable value • We decided to double its value, so every scanline would be twice its normal length
Initial image relationships CRTC_PITCH CRTC_START Actual image in VRAM is 2048-by-768 V R A M 768 scan lines Apparent image on display screen is 1024-by-768 This part of the VRAM image is not currently being displayed
After user hits right-arrow CRTC_PITCH CRTC_START Actual image in VRAM is 2048-by-768 V R A M 768 scan lines Apparent image on display screen is 1024-by-768 These parts of the VRAM image are not currently being displayed
Illusion of a full-file image • We want the user to believe there’s a total continuous horizontal image of all the .wav file’s pulse-code data available for viewing if the right-arrow key is hit enough times! • But only portion of this data can be shown at one time (due to screen’s limited width)
The reality is different • But our VRAM is limited in size (16MB), so there’s only room for part of the full image • However, the full pre-computed image can fit within the enormous Linux heap space, with its parts copied as needed into VRAM • We want rapid -- and smooth – scrolling whenever the user hits right or left arrow
So here’s how we do it srcmin srcnow srcmax HEAP-MEMORY CRTC_START Full pre-computed image VRAM-MEMORY Part of the image now visible dstmin dstnow dstmax When user hits right-arrow, a bit more of the pre-computed image is copied from the heap into VRAM, and then the CRTC_START value is advanced, so the user thinks the screen is scrolling horizontally over the full image
We also ‘prepare’ for restarting • Each time the view has scrolled rightward, we copy the newly visible vertical bands at the right side of the screen to the region of VRAM which has just gone out-of-view (a relatively small amount of copying time) • By the time the screen-image as scrolled to the right-hand half of the VRAM lines, we’ll have two totally identical images • So a user won’t notice if CRTC_START is reset
No visible effect dstnow CRTC_START dstmin dstmax With repeated left and right images in VRAM, there will be no perceptible change in what’s seen when the CRTC_START register is changed from dstmax to dstmin
Any improvements? • After we wrote ‘waveview.cpp’, we realized that an improvement could readily be made, merely by changing a few lines of our code • Presently ‘waveview’ does some ‘busy-waiting’ after it changes the CRTC_START value, to delay drawing to still-visible areas of VRAM until the next vertical retrace cycle has begun • This was necessary to prevent our users from seeing those updates to off-screen memory
Busy-waiting is wasteful • But we could alternatively delay drawing to the still-visible areas of video memory by letting our program ‘sleep’ instead of doing nothing but use up CPU cycles (wasteful!) • The sleep time should be quite brief – just long enough to be sure that a new vertical refresh cycle has started, and thus that the new CRTC_START value has taken effect
Use ‘nanosleep()’ • The screen-refresh rate is approximately 60-frames per second, so if we sleep for 1/60-th of a second, we can be sure that the next refresh-cycle is underway -- and allow other processes to run in the interim • By calling the ‘nanosleep()’ function we can put our task to sleep for 1/60 seconds (i.e. for 16666667 nanoseconds)
Programming details #include <time.h> // for nanosleep() struct timespec ts = { 0, 16666667 }; nanosleep( &ts, NULL ); // see the ‘man’ page if you want more info
In-class exercise #1 • See if you can implement the improvement just discussed – replacing the busy-waiting while-loops with a call to ‘nanosleep()’ • Try the experiment of sleeping for a briefer time-interval than 1/60-th second: will you see evidence of flashing screen updates at the right-hand edge (or left-hand edge) of your display screen?
In-class exercise #2 • Presently the ‘waveview’ tool scrolls the displayed image by 8-pixels, right or left, each time the user hits these arrow-keys • Can you easily adjust this pixel-shift size so that the shifts occur in bigger jumps? • If not, then decide how the programming could have been done to make this work
In-class exercise #3 • Can you modify the ‘waveview’ program so that a greater fraction of the .wav data could be viewed on the screen at once? • Do you think it would be better to simply omit some of the data (e.g., only showing alternate pulse-codes), or to ‘average’ two consecutive pulse-codes (as we did in an exercise on our midterm exam)?