A viewer for .wav files On using some CRTC registers to

advertisement
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)
VRAM
User can shift
the window
left or right
using arrow-keys
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 These parts of the
display screen is
VRAM image
1024-by-768
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
dstmin
dstnow
dstmax
Part of the image now visible
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)?
Download