Daqarta
Data AcQuisition And Real-Time Analysis
Scope - Spectrum - Spectrogram - Signal Generator
Software for Windows
Science with your Sound Card!
The following is from the Daqarta Help system:

Features:

Oscilloscope

Spectrum Analyzer

8-Channel
Signal Generator

(Absolutely FREE!)

Spectrogram

Pitch Tracker

Pitch-to-MIDI

DaqMusiq Generator
(Free Music... Forever!)

Engine Simulator

LCR Meter

Remote Operation

DC Measurements

True RMS Voltmeter

Sound Level Meter

Frequency Counter
    Period
    Event
    Spectral Event

    Temperature
    Pressure
    MHz Frequencies

Data Logger

Waveform Averager

Histogram

Post-Stimulus Time
Histogram (PSTH)

THD Meter

IMD Meter

Precision Phase Meter

Pulse Meter

Macro System

Multi-Trace Arrays

Trigger Controls

Auto-Calibration

Spectral Peak Track

Spectrum Limit Testing

Direct-to-Disk Recording

Accessibility

Applications:

Frequency response

Distortion measurement

Speech and music

Microphone calibration

Loudspeaker test

Auditory phenomena

Musical instrument tuning

Animal sound

Evoked potentials

Rotating machinery

Automotive

Product test

Contact us about
your application!

BMP Image to WAV Sound and Spectrogram Image Mini-Apps

Introduction:

This pair of macro mini-apps allows you to convert a photo or other bitmapped image into a .WAV sound file, whose spectrogram then yields the original image in the original colors. Please note that this method uses a perfectly standard spectrogram that simply uses a specific color palette and a carefully matched frequency and amplitude range. The trick is in creating the sound file itself, to produce the needed frequencies and amplitudes.

(Note: The Txt2MIDI MIDI setup is a much simpler embodiment of this concept of sound creation to generate a specific spectrogram image. The MIDI synth is used to create tone patterns that form scrolling dot-matrix spectrogram text from messages entered into Daqarta Notes.)

Consider a typical Daqarta Spectrogram: It creates an image of sound, with time on the horizontal and frequency on the vertical, and with intensity encoded as color. For example, with the default palette a bright red region near the upper left of a spectrogram indicates a loud high-frequency tone early in the sound. Dark magenta at the same position would be a very soft tone at that time and frequency.

The sound may have many simultaneous frequency components with various amplitudes at each moment in time (each spectrogram column), such that different pixels in each column have different colors.

Thus, to create a specific image, we need to produce sound with a specific array of frequencies and amplitudes at each point in time. The BMP_to_WAV macro does just that, creating a .WAV file which also encodes the full palette plus reference information into a sound "preamble". That information can be read by the companion WAV_to_BMP macro that plays the sound and shows the image on the spectrogram in the original colors.


Basic Theory:

The BMP_to_WAV mini-app analyzes the original image by copying its palette and associating each color number (typically 0-255) with a relative loudness in dB (0-31 default). Then it reads the palette color number used by each pixel in a vertical column of the image, and stores the associated dB value in an array that represents the spectrum of that column. It consists of amplitudes at each frequency to be shown in the corresponding column of the ultimate spectrogram, from low frequencies to high.

The array is then converted into a 1024-sample waveform segment using an Inverse Fast Fourier Transform (IFFT), the opposite of the forward FFT that Daqarta uses to find the spectrum of a sound, which is one column of a spectrogram. That waveform segment is sent to a .WAV file. These operations are performed by the _IFFT_Buf1_WAV macro subroutine.

Each additional pixel column is subsequently handled the same way until the entire image is encoded in a single .WAV file. The image is thus represented by 1024 waveform samples per pixel column, times 512 columns (for an image that just fills the width of the display area), or 524288 total samples... about 11 seconds of sound at the default 48000 Hz sample rate. In addition, BMP_to_WAV includes an 8192-sample "preamble" waveform ahead of the image waveform. This holds a synchronization pulse plus encoded format identification, certain parameter values, a "perfect" flat reference spectrum, and the RGB (Red, Green, Blue) specifications for each of the palette colors to be used.

The companion WAV_to_BMP mini-app allows you to play a .WAV file previously created by BMP_to_WAV. You can hear the sound as well as see the image being reconstructed by the Daqarta Spectrogram.

WAV_to_BMP reads the preamble to verify the format, and uses the sync pulse to adjust the playback analysis so that each 1024-sample frame will be properly aligned to produce the correct spectrogram column.

If you are using sound produced by a speaker and monitored by a microphone (instead of simply monitoring the raw waveform sent to the speaker), WAV_to_BMP will use the reference spectrum in the preamble to correct for speaker and microphone irregularities that affect the overall frequency response.

It also decodes the color palette from the preamble and installs it for use by the spectrogram.

Most importantly, it carefully sets the dB range of the spectrogram to exactly match that used by BMP_to_WAV, so that the loudness at each pixel frequency produces the proper original color. Since by default BMP_to_WAV encodes 256 colors into a 31 dB range, adjacent colors are represented by a loudness difference of only 0.12 dB. This would present only a minor problem in range settting on a monochrome image, but for color images it is absolutely critical.

To understand why, consider that in a monochrome image ("black and white", also known as "grayscale") the palette is a continuum of "256 shades of gray". The difference between adjacent shades is nearly undetectable. (You can see this for yourself by opening the Sgram/PT dialog, clicking on Color Palette at the top, then Load Pal, then select GrayScale.PAL.)

In such a case, shifting the dB range up or down simply changes the overall brightness of the image, while using a smaller or larger range just changes the contrast. Casual adjustment "by eye" is fine.

But in a color image the 256 palette colors are distinct, and typically not in a useful continuum. (You'll see how discontinuous the palette is when you run either BMP_to_WAV or WAV_to_BMP.)

Consider a medium red pixel from the lip region of a face: If the range is off by even a tiny amount, an adjacent palette color may appear instead. But it is unlikely to be a slightly lighter or darker shade of red, and could easily be green or blue!


Original Image Requirements:

There are a few requirements for any image you use. The first is due to fact that the Daqarta display area limits the spectrogram to only 512 horizontal time points, and 256 discrete frequencies on the vertical axis (without combining adjacent values or interpolating, which would be an absolute disaster due to the palette issues noted above). So the image is restricted to 512 pixels wide by 256 pixels tall. If you use a taller image, only the bottom 256 pixels will be encoded; if you use a wider image you'll see the whole thing, but not all at one time; it will just scroll past like a panoramic scan.

Second, Daqarta uses a palette of 256 colors to represent intensity, as shown via the vertical color bar at the right edge of the spectrogram. So the image must be limited to no more than 256 different colors. (They don't have to be the same colors as the default palette.)

Third, the image must be in 256-color "bitmapped" format, with a .BMP file extension. Also, for ease in processing, the file image must be rotated clockwise by 90 degrees from the desired viewing angle.

These requirements mean that some image manipulation will be needed ahead of time. If you are starting with a .JPG photo, you will need image manipulation software like GIMP (free) or Photoshop (pricey) or the equivalent. Both of these are ridiculously difficult to learn. An excellent compromise is IrfanView, which is free for non-commerical use and is ridiculously easy to use for what's needed here.

If you are starting with artwork such as a logo or drawing that already has a limited number of colors, Windows Paint is fine; photos, however, will come out as "art", like comic or graphic novel art.


Prepared Images Included With Daqarta:

These are all derived from the same "SpectroGramma" original:

This image was chosen as an "acid test" due to the wide brightness and color range, plus the shadowed facial gradients. The bright white building columns represent many simultaneous high-amplitude tones in their respective spectrogram columns.

These images were prepared with different color resolutions in GIMP. The lower resolutions may be helpful for reduced sensitivity to frequency response variations, distortion, and/or room noise if you are using acoustic WAV_to_BMP... see the Acoustic WAV_to_BMP subtopic below for more details. Otherwise, the 256-color version will give the best image.

    SpectroGramma256.BMP
    SpectroGramma128.BMP
    SpectroGramma64.BMP
    SpectroGramma32.BMP

Image Preparation Using GIMP:

GIMP (GNU Image Manipulation Program) is free, open-source software. It is very powerful, akin to expensive programs like Adobe Photoshop, and likewise has a very steep learning curve. But you only need a few of its vast array of features in order to prepare images for BMP_to_WAV, and if you carefully follow the steps below you should have no problems. Be forewarned, however, that if you start exploring the feature terrain you can easily end up lost in the deep woods!

Download and install GIMP for Windows from <https://www.gimp.org/downloads> . (There are versions there for Linux and other systems; you'll need to scroll down to get the version for Windows.) Note that the first time you run GIMP the startup progress bar will stall for a LONG time while it searches your computer for fonts, maybe a minute or more. This is perfectly normal.

Once GIMP is up and running, click on the little dotted rectangle at the upper left of the tool dialog so you will be ready to select rectangular regions (instead of painting on them with the default brush tool).

Then click File in the menu bar and select Open. Double-click on locations or drives in the left-hand Places panel to select the location of the image you want to use, then browse for the proper folder in the center panel. Once you are in that folder, you can single-click on a file name to see a small preview in the right panel. Click Open when you find the one you want.

When the file opens you will see the entire image, resized as needed to fit your screen. The actual image dimensions in pixels are shown in the title bar at the top of the frame, and the percentage it has been reduced for viewing will be shown at the bottom. Your goal now will be to crop and/or shrink the actual image (not just the view of it) to fit into 512 by 256 pixels.

As you move the cursor over the image, a readout at the lower left will show its X and Y coordinates. You can use those to get an idea of how small 512 by 256 pixels looks as a fraction of the whole image; almost certainly it won't include enough of the region of interest, so you'll need to shrink the image to fit. This will essentially merge adjacent pixels; you'll get fewer total pixels, so 512 by 256 will cover a larger portion of the image.

Click on Image in the menu bar and select Scale Image. You'll see Width and Height controls showing the current dimensions in pixels, with a little chain symbol next to the controls. That means that changing either Width or Height will change the other to keep the same proportions, which is what you want.

For example, if you have an original 4000 x 3000 (12 MP) image, reducing it to 25% (1000 x 750) is probably a good starting point. You can set Width to 1000 and Height will automatically go to 750. Alternatively, you can change the default 'pixels' units to 'percent' and just enter 25 in either Width or Height.

Since the target dimensions are 512 by 256 (a 2:1 proportion that probably doesn't match your original image), you will have to crop the image as well; allow for that when you are selecting the Scale Image dimensions. Alternatively, you can crop first and scale afterwards, or even use multiple passes of each.

After the Scale Image operation, you can re-enlarge the view via the percent control at the bottom of the frame.

To crop the resulting image, click at the upper left of the region you want to keep, and drag to the lower right. A thin rectangle will show the drag region, changing to a "crawling ants" rectangle when you release the mouse button. If you don't like that rectangle, you can start another only if its upper left corner is outside of the current one; otherwise, click on Select - None. (Alternatively, you can click on Edit - Undo Rect Select.) Then you can select another rectangle as described.

When you have the rectangle you want, click on Image in the menu bar again, and select Crop To Selection. After the crop the image will still have the "crawling ants" selection rectangle around the part you've kept. Click the Select menu and chose None to get rid of those.

Note that if you go too far or otherwise screw up at any step, you can always use the Edit menu to undo what you've done, no matter what it was... even back many steps.

When you have the desired image, the next step is to rotate it clockwise by 90 degrees. Click on Image in the menu bar and hover the mouse over Transform, then click "Rotate 90 degees CW" in the submenu that appears.

Once again click on Image in the menu bar, but now hover over Mode and select Indexed. In the dialog that appears, accept the default "Generate optimum palette" with "Maximum number of colors: 256", and set "Color dithering" to "Floyd-Steinberg (normal)". Then click OK.

Note: See the Acoustic WAV_to_BMP subtopic below for special Mode and color number considerations if you plan to send and receive this image acoustically when you subsequently run WAV_to_BMP.

Finally you are ready to save the finished file. Click on File in the menu bar and select Export As. At the top of the dialog that opens, the Name item will show the name of the original image file, such as "100_1234.JPG" (if it came from a camera, for example). You must change this name to have a .BMP extension, and you may want to give it a meaningful name as well. You also need to save it in the proper folder for BMP_to_WAV to find, so click the '[+]' button next to "Browse for folders" and navigate to Documents - Daqarta - User_Data before clicking the Save button.


Image Preparation Using IrfanView:

Download and install IrfanView (www.irfanview.com).

IrfanView is free for non-commerical use, but please consider encouraging the author in his excellent work by registering... it's only $US 12.00.

Click File - Open and navigate to the image of your choice.

Click Image - Info and note the size. It must be at least 512 x 256 (512 pixels wide by 256 high). If it is larger only the lower left corner will be used... probably not what you want.

The first step is to reduce the size to 512 x 256, or something only a little larger.

Begin by selecting the area of interest. Click on one corner of the desired area and drag the selection rectangle diagonally to the opposite corner, noting the size in the title bar (the pixel numbers on the right, not the XY coordinates on the left) to make sure your selection area is at least 512 x 256. IMPORTANT: If you are in the process of dragging the selection rectangle and decide it's not where you want it, do NOT hit ESC to start over... that will close the program without warning! Instead, just click somewhere outside the current rectangle to close it, then start a new selection rectangle. (Clicking inside the current rectangle will just magnify it, which is probably not what you want to do.)

When creating the above selection, try to make the width about twice the height.

When you have the desired area selected, click Edit - Crop selection (Cut out) or hit the CTRL+Y keys. The selected area will be enlarged to fill the display area.

If the cropped section is still much larger than 512 x 256 go to Image - Resize/Resample. You'll find several options there; use Set new size on the left side. The Preserve aspect ratio (proportional) box should be checked by default. Enter 512 in the Width window, and the Height will change to keep the same proportions. If the Height becomes less than 256, then set Height to 256 and Width will be larger than 512. Click OK at the bottom and the image will be resized.

Now go to Image and click Rotate Right (clockwise).

Next go to Image - Decrease Color Depth and select 256 Colors (8 BPP), then click OK.

Finally, click File - Save As. Under Save as type select BMP - Windows Bitmap. Enter the desired file name and navigate to the desired destination folder before clicking Save. Use Documents - Daqarta - User_Data to make access simpler when running BMP_to_WAV, otherwise you'll have to navigate at run time.


Image Preparation Using Windows Paint:

First of all, please note that Paint does a rather poor job at reducing a full-color photo image to 256 colors, because it does no color dithering. It just quantizes directly, leaving a "posterized" image that looks somewhat cartoonish, with distinct regions of solid color. That's perfectly fine for artwork that uses solid colors, but usually not for photos of faces (for example), except maybe for impressionistic art.

But since you already have Paint, and it is simple to use, you may want to give it a try even on a photo. We'll assume here that you are starting with a large multi-megapixel photo.

Start Paint. If you are not a regular Paint user, you can find in in Start - All Programs - Accessories. (If you right-click it there you can choose Send To - Desktop to create a shortcut for future use.)


Windows Vista and later:

Click the File menu icon (upper left) and select Open, then navigate to your photo.

Go to File - Properties - Units and set Pixels.

Go to View and click Zoom Out until you can see the whole image.

Click Home - Resize and set Horizontal and Vertical to 25%. That's a good starting point for photos, which will take a 4000 x 3000 (12 MP) image down to 1000 x 750.

While in Home, click Select - Rectangular Selection.

Click the upper left corner of the desired region of the image and drag to the lower right to select. The cursor coordinates are shown at the lower left, while the selection dimensions are shown just to the right of that. Use the latter as a guide to selecting a 512 x 256 region. When you release the mouse button, the selection size readout vanishes and the selected rectangle remains.

If you don't like the results of your first attempt, you can just repeat the process; as soon as you click the mouse button to designate a new upper left, the original selection rectangle vanishes and you will be starting a new selection.

If a 512 x 256 selection doesn't show enough of the image, you can go back to Home - Resize for further reduction. You can go to View - Zoom In or Zoom Out as needed.

Hitting an exact selection size can be awkward. Don't worry if your selection is a little larger than 512 x 256, just don't make it smaller than that. Once you are satisfied, click on Home - Crop.

If you have overshot the 512 x 256 dimensions and want to trim to exact values, go to File - Properties and set Width and Height.

Now go to Home - Rotate - Right 90 degrees.

Finally, go to File - Save As - BMP Picture and set "256 color bitmap" in the "Save as type" drop-down, then type your chosen file name in the line above; the .BMP extension will already be filled in. Then navigate to Documents - Daqarta - User_Data and click the Save button. You'll get a prompt that says "Saving into this format may cause some loss of color information. Do you want to continue?". Click Yes and you are done.


Windows XP:

Click File - Open and navigate to your photo. The first thing you'll note is that (unlike GIMP), the view will not be reduced to fit the screen, and in fact (unlike Paint in Vista and later) there is no way to view the whole image without actually altering it. That's OK, though, since you need to alter it anyway.

Click on the dotted rectangle at the upper right of the tool dialog so you will be ready to select rectangular regions, instead of drawing on them with the default pencil tool.

Now click on Image in the menu bar and select Stretch/Skew. In the top Stretch section of the dialog, set both Horizontal and Vertical to the same percentage. 25% is a good starting point, which will take a 4000 x 3000 (12 MP) image down to 1000 x 750.

Now click at the upper left corner of the region you want to use, and drag to the lower right. While you are doing this, the current selection size is shown at the lower right of the image area, so you can use this as a guide to selecting a 512 x 256 region. When you release the mouse button, the selection size readout vanishes and the selected rectangle remains.

If you don't like the results of your first attempt, you can just repeat the process; as soon as you click the mouse button, the original selection rectangle vanishes and you will be starting fresh.

If a 512 x 256 selection doesn't show enough of the image, you can go back to Stretch/Skew for further reduction.

Don't worry if your selection is a little larger than 512 x 256, but don't make it smaller than that. Once you are satisfied, hit CTRL+C to copy the selected region to the Windows Clipboard.

Now go to File - New and hit CTRL+V to paste your selection into the empty image area, where it will appear at the upper left corner.

At this point Paint still regards the image size as 1000 x 750 (in the above example), with the remainder being white space. Click on Image - Attributes and set Width to 512 and Height to 256 to crop to that final size, removing not only the white space but also any excess from your selection.

Next, go to Image - Flip/Rotate and select "Rotate by angle". The default selection is 90 degrees, which is what you want, so click OK.

Finally, go to File - Save As. Down at the bottom of the Save As dialog, change the "Save as type:" selection to "256 Color Bitmap" using the drop-down control. Type your chosen file name in the line above. Don't include the extension, which Paint will fill in as .BMP. Then navigate to Documents - Daqarta - User_Data and click the Save button. You'll get a prompt that says "Saving into this format may cause some loss of color information. Do you want to continue?". Click Yes and you are done.


Operation - BMP_to_WAV:

Once you have a prepared .BMP image in Daqarta's User_Data folder, run BMP_to_WAV by hitting the F8 key followed by the SHIFT+G keys. Note that this macro ID is case-sensitive because the unshifted g key is used to run WAV_to_BMP.

Alternatively, hit CTRL+F8 to open the Macro Dialog and double-click on BMP_to_WAV in the Macro List.

You will be presented with a file Open dialog showing all the .BMP files in the User_Data folder, including the four "SpectroGramma" files included with Daqarta. Select the desired file and click Open, or just double-click the file name.

If you attempt to open a file that is not a true .BMP format, you'll get a "Not a .BMP file" message and the macro will be cancelled.

If you get an "Unsupported .BMP format" message, it means the file was a .BMP type, but used 16 colors or less, or more than 256 colors.

Otherwise, a message box will appear that gives the full path and name of the file, plus the image dimensions in pixels and the number of colors, along with the message "Writing .WAV file, please wait...". An empty black spectrogram screen will be shown, with its palette colors at the right of the display area replaced by those from the .BMP file.

The file progress is shown on the message line just above the Generator title window (above the main trace area). It will be of the form "123456 bytes written to MyFile.WAV", where the count will go up in a blur, and end at just over 1 million in about 20 seconds. (That assumes the image to be shown is 512 pixels wide, otherwise the final count and time go up proportionally.)

Once the file is saved, you can use WAV_to_BMP to play it and see the live spectrogram. However, if you are curious about the inner workings of the file, you can examine it by clicking the DD/Open button in the Daqarta toolbar and selecting that file. (Otherwise, just skip over the following section to the Operation - WAV_to_BMP subtopic.)

You'll see the color spectrogram as it would be shown during WAV_to_BMP, but without sound. Note that opening the file directly like this only shows the correct colors if you have just created it, since BMP_to_WAV sets the spectrogram palette directly from the bitmap file for this purpose. If you use DD/Open on a file created previously, the palette won't match.

Besides the image, you'll also notice some "garbage" in the first 8 columns, which represents the preamble that encodes calibration data and palette colors as sound. The final 8 columns will be missing, but you can see them by hitting the End key to show the end of the file instead of the start. (The Home key returns to the start.)

You can toggle Sgram/PT off to see the raw waveform. Hit the Home key if you have already hit End, and you will see the sync, ID, and parameter pulses. You can advance through the file via using ALT+> to move one 1024-sample frame (spectrogram column) at a time. (The file position readout below the right end of the display area shows the first sample of the current display frame.)

After the initial frame, Spectrum mode is more useful. The next frame, starting at sample 1024, will have a rectangular spectrum that represents the maximum amplitude (palette color 255, typically bright white) used for each frequency in the spectrogram column. The frequency range extends from spectral line 10 at 468.75 Hz to spectral line 265 at 12421.875 Hz, representing the 256 spectral lines used to display the 256 pixels in each column of the spectrogram. (This particular 256-line range was chosen as a compromise for modest speaker systems. See the Acoustic WAV_to_BMP subtopic for how to change that range.)

The purpose of this maximum amplitude reference spectrum is to allow on-the-fly frequency response calibration by WAV_to_BMP, as discussed under that subtopic.

The top of the rectangle is at -45.156 dB, not 0 dB. That's because 0 dB is the maximum amplitude that a .WAV file can hold at a single frequency, but here we have 256 simultaneous tones. That would normally require that the level of each be reduced to -48.16 dB or 20 * log10(1/256), but BMP_to_WAV uses a method (Newman phase algorithm) such that the tones sum to a smaller maximum waveform peak. That allows the level to be increased by about 3 dB for a better signal-to-noise ratio when using actual sound.

You can see the waveform peak by toggling Spectrum off. The highest peak is at the very start of the waveform, at sample 1024. Since this is the very first sample in the reference frame, it is adjacent to the left boundary of the display area and may be hard to see. You can use the unshifted < key to move the start of the waveform display a little earlier for easier viewing. Be sure to set it back to 1024 before toggling again to Spectrum mode, or you won't see the nice rectangular spectrum.

The next 6 frames encode the palette colors in bBgGrR order, 4 bits per frame. For example, the first of these frames (b) encodes the low 4 bits of each blue component in the palette, while the next (B) encodes the high 4 blue bits. This 4-bit encoding was used because it only requires 16 different levels, compared to 256 levels for a full 8-bit encoding. That means that sound levels can be kept higher to reduce the influence of noise while acquiring the critical palette data. (This is only an issue when WAV_to_BMP uses an Input channel, since acoustic data is typically quite noisy.)

Finally, starting at sample 8192, are the frames of bitmap data. Each frame consists of 1024 samples containing 256 simultaneous tones, the amplitudes of which encode the palette color numbers for the corresponding pixels in one column of the spectrogram.


Operation - WAV_to_BMP:

After running BMP_to_WAV to turn your .BMP image into a .WAV file in Daqarta's User_Data folder, you can play (hear and see) that file with the WAV_to_BMP macro.

To run WAV_to_BMP, hit the F8 key followed by an unshifted g key. Note that this macro ID is case-sensitive because the preceding BMP_to_WAV uses SHIFT+G.

Alternatively, hit CTRL+F8 to open the Macro Dialog and double-click on WAV_to_BMP in the Macro List.

Note that by default WAV_to_BMP assumes you simply want to hear the sound as well as see the resulting image via the Daqarta Spectrogram. That is by far the simplest approach, and will work even if the trial period expires and you have not purchased a Daqarta license. You'll see Daqarta's output Volume dialog, plus a message box that says "Unmute and adjust Volume for comfortable listening. Then hit 'w' key to select .WAV file. (Any other restores default palette and exits.)".

Sound output is muted here by default, so if you only want to see the spectrogram image you can just hit the 'w' key. Otherwise, when you unmute the volume you will hear a 440 Hz tone while you adjust the volume as desired.

Alternatively, if you want to try your hand at sending and receiving the image acoustically, you should make sure the Input button is active before you run WAV_to_BMP. Note that getting a decent image will be a serious challenge for your overall sound system... see the Acoustic WAV_to_BMP subtopic below for more infomation.

In this case, besides the output volume dialog, you'll also see the Input controls dialog. The message will be "Unmute and adjust Volume and Input Range for large undistorted yellow Input signal. Then hit 'w' key to select .WAV file. (Any other restores default palette and exits.)".

The Input controls should be set for the appropriate input line, either Mic or Line In, and only the Left input button at the top of the dialog should be active.

When you unmute the output volume, you'll hear the 440 Hz tone as well as see its Input spectrum, which ideally should have a single peak near the left end of the display. You may see a whole forest of spikes, which indicates excessive distortion. In this case, reduce the output volume and/or set a lower (more-negative) Input level until the extra spikes just go away. Otherwise, if you don't see the distortion peaks, raise the volume and/or Input level until you just see them, then back off until they go away.

In either case, once you hit the 'w' key you'll see the spectrum of the "preamble" sound waveform. At this point, the macro has loaded the WAV2BMP.GEN Generator setup, and into that has loaded the selected .WAV file as a Play waveform source. The macro now proceeds to play each of the first eight 1024-sample preamble frames multiple times while calibrating the system.

The initial screen will show the spectrum of the biphasic pulses used for sync, ID, and parameters. Actually, by the time this spectrum appears, the ID and parameter pulses have already been analyzed directly from the digital waveform, so the only thing this "live" signal is used for is the initial sync pulse at the start of the frame, and that is analyzed as the raw waveform from which the spectrum is derived. The sync pulse is used to adjust the Trigger Delay to compensate for acoustic delay between output and input if you are using acoustic input mode, but that adjustment doesn't hurt anything for output-only mode.

Then the rectangular full-scale reference spectrum appears, and is averaged while being repeated 32 times. This is to reduce the influence of noise in case this is an acoustic input signal. If it is, then the next step will be to create a mirror-image spectrum curve to compensate for the overall frequency response of the system, including speakers and microphone. (This step isn't used for output-only operation, since the direct digital signal has no response problems to correct.)

Then each of the 6 palette frames is repeated 32 times and averaged as above, to decode the 4-bit bBgGrR data from each frame into the 24 bit RGB values describing the 256-color palette. Again, this averaging isn't really needed for output-only operation, but for acoustic inputs it helps insure that the palette colors are not corrupted by noise. The decoded palette is then set for Spectrogram mode.

Finally, the macro sets the Play From and Play To parameters to omit the preamble and just play the remainder of the .WAV file repeatedly. Spectrogram mode is activated and the dB range of the palette is set based on the preamble parameters. The image slowly scrolls into view from right to left and repeats indefinitely. Hit Pause (ALT+P) if you want to freeze the image at any time; sound output is not paused.

The sound you hear is being used to create the spectrogram in real time. The frequency range extends above 12 kHz when the default 48000 Hz sample rate is used. In output-only mode, you can reduce the sample rate during playback to lower the pitch into a more audible range. This has no effect on image quality, only the speed at which the image appears.

In acoustic input mode, however, this will mess up the automatic speaker/microphone frequency response compensation that was done at the start. You'll need to re-run WAV_to_BMP with the new sample rate in order to get a new response correction.


Acoustic WAV_to_BMP:

As noted under the WAV_to_BMP subtopic above, that macro assumes you simply want to hear the sound as well as see the resulting image via the Daqarta Spectrogram. That approach doesn't use the sound card Input; it just plays the sound via the Generator, and displays the spectrogram of the raw waveform data that is being sent to the sound card output. This gives essentially perfect reproduction of the original .BMP image.

It is vastly more challenging to get a decent image from the acoustic waveform that has been generated by a sound system and monitored by a microphone via the sound card Input. This is not only a test of your sound system, microphone, and sound card, but also of your troubleshooting abilities and your ingenuity.

Note: The Duplex Delay settings for your sound card must have been previously determined by the Auto-Calibration process. This compensates for inherent sound card time lags between output and input; WAV_to_BMP can then properly compensate for acoustic lags by using the sync pulse at the start of the "sound preamble" in the .WAV file to adjust the Trigger Delay. This is needed to insure that 1024-sample "frames" of the acquired waveform are aligned with the starts of spectrogram (and hence .BMP image) columns.

The whole concept of BMP_to_WAV and WAV_to_BMP is to store and later recover a short 1024-sample tone for each pixel of the bitmap. 256 simultaneous tones are used for the 256 pixels in each column of the image. The frequency of each tone encodes the pixel's vertical position, the time of the tone start encodes the horizontal, and the loudness encodes the color. Getting the specified frequency presents no problem, as long as the frame is properly synchronized... which also insures the proper horizontal position. The weakest link is measuring the correct loudness for each and every tone.

The overall frequency response must be absolutely flat, since errors of a fraction of a dB at any frequency can cause the wrong color to be rendered at that frequency, which corresponds to a given horizontal line on the spectrogram.

For example, if the response is down by 0.12 dB at a certain frequency (using the default parameters with 256 colors), the spectrogram will show the next-lower palette color from the original. In a .BMP color palette, colors have little (if any) correlation with their neighbors, so there will be a glaring discrepancy. And since this is an overall frequency response problem, every pixel in that row (same frequency) will be off by the same amount. Furthermore, since the original image pixels in that row are typically different colors, you won't see a streak of a certain wrong color, but a streak of "wrongness" having apparently random colors.

The same thing happens at every frequency, so without a perfect frequency response you get a perfect mess.

WAV_to_BMP attempts to deal with this automatically by using the preamble reference spectrum that was saved by BMP_to_WAV. It's a 256-tone sound that has all frequencies at the same maximum level, which would otherwise be displayed as the top color in the spectrogram palette. If the overall sound system had perfect frequency response (at least over the chosen 12 kHz range used here), the measured spectrum would be perfectly flat. WAV_to_BMP measures the deviation at each frequency and creates a mirror spectrum. For example, if the measured response is -1 dB at some point, the mirror will be +1 dB at that point. This mirror is then installed as a Spectrum Curve that perfectly flattens the overall response.

But there are definite limits to this approach. First, if there is a deep dip in the speaker response, or steep roll-off near the high or low ends, the softer sound will have a greater percentage of contaminating background noise. The flattened signal will thus have larger errors in these regions. Second, the slopes and locations of sharp dips and peaks can vary over time due to changes in the temperature of the air and the mechanical properties of the speaker components. But the Spectrum Curve is only created once; it doesn't adapt.

Another big problem is distortion, especially by the speaker but also by the microphone, and possibly even by the sound card itself. With the default sound level range of 31 dB, the overall sound system would require distortion of less than 3 percent to prevent the loudest tones from contaminating the softest. That's pretty easily exceeded by most woofers just for simple harmonic distortion of single loud tones, but here we can have many high-level tones at once (bright pixels in the same column), causing special concern for intermodulation distortion. BMP_to_WAV uses a 12 kHz frequency range starting at 468.75 Hz to avoid woofer distortion. (Midrange and tweeter drivers tend to have less problems in this department, since their diaphragms don't have to move as much to generate the same sound levels.)

One way to reduce distortion is to reduce the overall sound level, but unfortunately this brings the signal closer to the background noise. When you start WAV_to_BMP with the Input active, you are prompted to adjust the output volume and the input range while monitoring the spectrum of a 440 Hz reference tone.

The goal here is to get the loudest possible sound with the least distortion. Another way to put this is that you want the biggest difference between the 440 Hz peak and everything else. Once you reduce the distortion peaks into the noise background, further level reductions will be counter-productive, reducing the signal relative to the constant-level noise.

Other acoustic problems can arise from room reverberation, which in this application is just as much a problem as background noise.

A related but less serious issue is multiple sound paths from different speaker drivers. Ideally, you want all the sound for one vertical spectrogram column to arrive pretty much at the same time, not with some components arriving later due to different acoustic delays. For this reason, when Input is active at the start of WAV_To_BMP the Generator output is switched to Left Solo mode. This means only the Left output channel will be active; the Right channel will receive only null data for silence. (Selecting only the Left Generator output enable without Solo would give mono mode, which sends the Left signal to both outputs.) So, make sure your microphone is aimed at the Left speaker.

Noise, distortion, and frequency response changes will all contribute to deviations in the received sound level at any given pixel frequency. As noted earlier, tiny level differences can cause big color differences. With the default settings, BMP_to_WAV encodes 256 levels (one for each palette color) into a 31 dB range, leaving only 0.12 dB between colors. If we could increase that spacing, the process would be less sensitive to small level errors.

One way would be to increase the dB range in use. The problem is that due to the presence of 256 simultaneous tones, the maximum level of each has to be about 45 dB lower than the full-scale tone that could be used in a single-tone application. So if we increase the range, it has to be to make the softest tones softer. If the softest tone (bottom palette color) is 31 dB below the -45 dB peaks, it is around -76 dB relative to full scale. That's pretty soft, and likely to be near the background acoustic noise level. Thus, increasing the available range has a cost in terms of signal to noise ratio.

An alternative way to get a larger dB spacing between colors is to use less colors over the same loudness range. Although the Daqarta Spectrogram always uses a palette of 256 colors, there is no reason they have to be different colors. BMP_to_WAV and WAV_to_BMP detect the number of colors actually used by the image; if that number is 128 or less, the palette colors are "doubled up" so that the color bars in the palette (as shown on the right edge of the spectrogram) are twice as wide. With only 64 colors, they are 4 times as wide, and with 32 colors they are 8 times as wide.

That means the process is less sensitive to noise, distortion, and other errors, but of course it means the target image quality is reduced by using the smaller palette. Nevertheless, this controlled degradation is usually much more acceptable than random color speckles and other corruption due to error sensitivity.

To prepare an image with less colors, you will need to use GIMP or similar software... Windows Paint can't do it. Refer to the Image Preparation Using GIMP subtopic above. Where you select Image - Mode - Indexed, change the "Maximum number of colors: 256" to 128, 64, or 32 and proceed as before.

Alternatively, instead of choosing Image - Mode - Indexed, you can use Image - Mode - Grayscale. Although you will lose the colors, a black and white image is much less sensitive to errors. The reason for this is discussed in Basic Theory.

The default 31 dB loudness range can be adjusted by changing the QR parameter at the start of BMP_to_WAV. A smaller range means the colors are closer together, but the tones stay louder. This keeps them farther above the background noise, but since they are closer together they are more sensitive to that noise by the same amount; there is no net improvement in image quality.

Another way that might improve image quality is to change the frequency range of the sound to be a better match to your speaker response.

You can adjust the frequency range in two ways. The first is by changing the U0 parameter from its default of 10. That sets the lowest tone frequency according to SmplRate / 1024 * U0. With the default rate of 48000 Hz each step of U0 raises that frequency by 46.875 Hz; with U0=10 it is 468.75 Hz. The highest frequency will be 255 steps higher than U0, or 12421.875 Hz.

The other way to change the frequency range is by changing the sample rate. If you cut it in half to 24000 Hz, the default U0 will give a range from 234.375 to 6210.9375 Hz. Cutting to 12000 Hz will give 117.1875 to 3105.46875 Hz.

You don't need to re-run BMP_to_WAV when you change the sample rate, only if you change U0. The .WAV file can be played back at any sample rate, as long as you set that rate before you run WAV_to_BMP.

Note that each time you cut the sample rate in half, the time to display the image doubles.


Restoring The Default Palette Afterwards:

When you run either BMP_to_WAV or WAV_to_BMP the Spectrogram palette is replaced with that of the .BMP image. You can quickly restore Daqarta's default palette by running WAV_to_BMP and hitting any key other than 'w' when prompted. That restores the Default.PAL before cancelling the macro.

(The same thing happens with BMP_to_WAV if you cancel the file open dialog, or attempt to load a non-BMP file.)


BMP_to_WAV Macro Listing:

;<Help=H491E
QR=31              ;0-31 dB bitmap range
QP=30              ;0-30 dB palette nybble range
U0=10              ;Bottom spectral line (468.75 Hz)

Close=             ;Close any open file
UserUnits=0        ;No User Units
Gen=0              ;Generator off
E.IF.Input=        ;Input enabled?
    InL=1              ;Stereo Input for later DD/Open
    InR=1
    Input=0            ;Input off
ENDIF.
Sgram=1            ;Empty Sgram on
PitchTrk=0         ;Pitch Track off
XpandMin=U0 * SmplRate / 1024  ;Set Sgram freq range
XpandMax=(255 + U0) * SmplRate / 1024
Xpand=1            ;Show only above range
Msg="<D(60,60)"    ;Set Msg position

;Get file and verify format:
Buf0#N=1           ;Read 1 channel
Buf0#W=16          ;16-bit data
Buf0#H=0           ;No header
Buf0="<LoadDAT:*.BMP"  ;Open BMP file
IF.Buf0[0]=!h4D42      ;'BM' for BitMap file?
    Msg="Not a .BMP file."
    A.LoadPAL="Default"    ;Restore default Sgram palette
    LoopBreak=-1           ;Exit macro if not
ENDIF.
UF=0                       ;No format errors found yet
IF.Buf0[7]=!40             ;BITMAPINFO header size test
    UF=1                       ;Set error flag if wrong size
ENDIF.
IF.Buf0[13]=!1             ;Bit Planes = 1 ?
    UF=1
ENDIF.
IF.Buf0[14]=!8             ;8 bits/pixel (256-color indexed) ?
    UF=1
ENDIF.
IF.(Buf0[15]+Buf0[16])=!0  ;Compression = 0 ?
    UF=1
ENDIF.
IF.UF=1                    ;Any format errors?
    Msg="Unsupported .BMP format."
    LoopBreak=-1           ;Exit macro if so
ENDIF.
QW=Buf0[9]                 ;Bitmap width in pixels
Qw=QW                      ;Assumed Sgram height
IF.Qw=>256                 ;Limit to 256
    Qw=256
ENDIF.
QH=abs(Buf0[11])           ;Height in pixels (neg = top down)
GetFilePath=1              ;Copy file path

;Re-open file to read palette data:
UO=Buf0[5]                 ;Offset to bitmap
Buf0#H=UO - 1024           ;Offset to palette, skip as 'header'
Buf0#N=1                   ;Read 1 channel
Buf0#W=32                  ;32-bit data
A.Buf0="<LoadDAT:" + FileName  ;Re-open BMP file with new params

;Determine number of active palette colors:
UI=255                     ;Max palette index
WHILE.UI=>0                ;Work from top down
    IF.Buf0[UI]=>0             ;Look for first non-zero entry
        LoopBreak=2                ;Found highest entry in use
    ENDIF.
    UI=UI-1                    ;Else next-lower palette entry
WEND.
QN=UI+1                        ;1-based palette size = top + 1

Msg=FileName +n +n + QW + " x " + QH + " pixels, " _
  +QN + " colors." _
  +n +n + "Writing .WAV file, please wait... "

;Widen palette bars if 128 colors or less:
Buf1="<=B0"        ;Palette copy for widening
Buf3="<=B0"        ;Copy for WAV preamble
QJ=int(256 / QN)   ;Number of adjacent duplicates
UI=0
WHILE.UI=<QN       ;Do all colors
    C=Buf1[UI]         ;Get RGB color
    UJ=0
    WHILE.UJ=<QJ       ;Create QJ adjacent copies in Buf0
        Buf0[QJ*UI + UJ]=C
        UJ=UJ+1
    WEND.
    UI=UI+1
WEND.
Buf0#Ps=0          ;Set palette for Sgram
Sgram=1            ;Re-activate to show new palette at right

;Set dB value for each palette color, store in Buf2:
K=32767            ;Full-scale WAV amplitude
UI=0
WHILE.UI=<QN       ;Do all colors
    D=UI * (QR / (QN-1)) - QR      ;Bottom color = -R, top = 0 dB
    Buf2[UI]=exp10(D / 20) * K     ;Convert dB to amplitude
    UI=UI+1
WEND.

;Pre-compute Newman phases in Buf4 for each pixel freq.
;(Reduces waveform peaks to allow higher levels.)
Buf4="<=(0)"                       ;Clear Buf4
UI=U0                              ;Initial index
UN=256 + U0                        ;Limit index
WHILE.UI=<UN
    T=tan(pi * (UI-1)^2 / UN)
    X=1 / sqrt(1 + T^2)
    Buf4[2*UI]=X                       ;Re part
    Buf4[2*UI + 1]=X * T               ;Im part
    UI=UI+1
WEND.

;Data for "preamble" sync/info waveform frame:
Buf0[0]=hB2AF          ;ID=BMP 2 Audio Format
Buf0[1]=QN             ;Number of colors
Buf0[2]=QR             ;Bitmap dB range
Buf0[3]=U0             ;Bottom spectral line
Buf0[4]=QP             ;Palette nybble dB range
L=1024                 ;0, 1K, 2K, ... 15K output levels

;Create sync pulse:
Buf1="<=(0)"           ;Clear output wave Buf1
UN=0                   ;Waveform Buf1 index
A=20 * L               ;20K initial pulse larger than rest
WHILE.UN=<4            ;Biphasic sync pulse, 4 samples/phase
    Buf1[UN]=A         ;Pos phase
    Buf1[UN+4]=-A      ;Neg phase 4 samples later
    A=0.9 * A          ;Decay so peak = leading edge
    UN=UN+1            ;Next sample
WEND.

;Save ID and info data in preamble waveform frame:
UN=UN+4                ;Start of ID data
UI=0                   ;Value (word) index
WHILE.UI=<5            ;Send 5 data words
    A=Buf0[UI]         ;Get 16-bit data to send
    UJ=0               ;Nybble counter
    WHILE.UJ=<4        ;Send 16 bits as 4 nybbles, low to high
        UA=A & 15      ;Send only low nybble
        UK=0           ;Sample counter
        WHILE.UK=<4    ;4 samples per phase
            Buf1[UN+UK]=UA * L     ;Pos phase
            Buf1[UN+UK+4]=-UA * L  ;Neg phase
            UK=UK+1        ;Next sample in nybble
        WEND.
        UN=UN+8        ;Start sample of next nybble
        A=int(A >> 4)  ;Shift down by one nybble
        UJ=UJ+1        ;Next nybble
    WEND.
    UI=UI+1            ;Next data value
WEND.
A.Buf1="<SaveWAV:" + FileName?P    ;Save waveform frame

;Send 0 dB reference frame, all spectral lines (1 pixel column):
Buf1="<=(0)"
UI=U0                          ;Start spectral line index
US=1
D=(QN-1) * (QR / (QN-1)) - QR  ;Max pixel color = 0 dB
D=exp10(D / 20) * K            ;Convert dB to amplitude
WHILE.UI=<Qw + U0              ;Do all spectral lines
    Buf1[2*UI]=D                   ;Re part
    Buf1[2*UI+1]=D                 ;Im part
    UI=UI+1
WEND.
@_IFFT_Buf1_WAV                ;Take IFFT and append to WAV file

;Send RGB palette as 6 frames, lo-hi nybbles (bBgGrR):
;Spectral lines hold one nybble for each color in the palette.
US=0
WHILE.US=<(4 * 6)              ;6 frames, 4 bit shifts each
    Buf1="<=(0)"               ;Clear output wave buffer
    UI=0
    WHILE.UI=<QN               ;Do all colors
        D=(int(Buf3[UI] >> US) & 15) * QP/15 - QP ;0-15 nybble = 0-30 dB
        D=exp10(D / 20) * K        ;Convert dB to amplitude
        Buf1[2*(UI+U0)]=D          ;Re part
        Buf1[2*(UI+U0)+1]=D        ;Im part
        UI=UI+1
    WEND.
    @_IFFT_Buf1_WAV            ;Take IFFT and append to WAV file
    US=US + 4                  ;Next nybble shift amount
WEND.

;Send BMP pixel values as spectral lines:
Buf0#H=UO                  ;Offset to bitmap
Buf0#WU=8                  ;8 bits per pixel, unsigned
UW=4 * ceil(QW/4)          ;BMP rows are multiples of 4 bytes
UR=0
WHILE.UR=<QH               ;Do all columns
    Buf0#L=UW * (QH-1 - UR)        ;Set file posn
    Buf0#N=1                       ;1 channel
    A.Buf0="<LoadDAT:" + FileName  ;Pixel column (indexes 0-255)
    Buf1="<=(0)"                   ;Clear output wave buffer
    UI=U0                          ;Start spectral line index
    WHILE.UI=<Qw + U0              ;Do all spectral lines
        UX=Buf0[UI-U0]                 ;Byte value 0-255
        D=Buf2[UX]                     ;Get dB value
        Buf1[2*UI]=D                   ;Re part
        Buf1[2*UI+1]=D                 ;Im part
        UI=UI+1                        ;Next spectral line
    WEND.
    @_IFFT_Buf1_WAV                ;Take IFFT and append to WAV file
    UR=UR+1                        ;Next column
WEND.

;Set Spectrogram range for file viewing:
D=-45.156                      ;'0 dB' ref spectrum level
G=20 * log10(Range?0)          ;Input range dB for later DD/Open
S=(QR+1) / QN                  ;dB per palette color
YlogScrnTop=D + G + S/2        ;Set Sgram range
YlogScrnBot=D + G - QR + S/4
DDiskReadStep=1024             ;File read samples per Sgram column

Msg=                           ;Remove message

_IFFT_Buf1_WAV Macro Subroutine:

;<Help=H491E
;On entry, Buf1 = complex spectrum, Buf4 = phase factors
Buf1="<*B4)"               ;Apply phase factors
Buf1="<*(65536)"           ;Scaling for IFFT
Buf1="<iB1"                ;IFFT to get waveform
Buf1="<*(1.414 / 16384)"   ;Scaling for WAV range
Buf1#N=1                   ;1 channel
A.Buf1="<SAveWAV:" + FileName?P    ;Append to WAV file

WAV_to_BMP Macro Listing:

;<Help=H491E
UC=2               ;Assume output-only (Left Out chan)
E.IF.Input=        :Input enabled?
    IF.Input=1         ;Input already active?
        InL=1              ;Stereo Input
        InR=1                  ;Right Input on, but ignored
        RI.Disp=0          ;Right In display off
        UC=0               ;Left In chan
    ENDIF.
ENDIF.
T=100m             ;Settling time after changes
Close=             ;Close any open file
UserUnits=0        ;No User Units
SmplSec=0          ;Trigger units in samples
TrigDelay=-100     ;Default, will auto-adjust
TrigMode=GenSync   ;Sync to Gen frame rate
Trig=1             ;Trigger on
Spect=1            ;Show Spectrum
SpectWindOn=0      ;No window function
PSD=0              ;No PSD
Ylog=1             ;Ylog (dB) axis
YlogScrnTop=6      ;+6 to -90 dB axis
YlogScrnBot=-90
SpavgMode=Lin      ;Use linear Spectrum average
SpavgFrames=32     ;32 frames (change as needed)
A.CurveFile0=0     ;No Spectrum Curve 0
A.LoadGEN="WAV2BMP"    ;Load Generator setup
Gen=1              ;Generator on
GenVolDlg=1        ;Open Volume slider dialog
Msg="<D(60,60)"    ;Set Msg position
IF.UC=<2           ;If Input active:
    GenSoloL=1         ;Left Out only
    InputDlg=1         ;Open Input dialog
    Input=1            ;Restart Input (closed by Gen load)
    Msg="Unmute and adjust Volume and Input Range" _
    +n + "for large undistorted yellow Input signal." _
    +n + "Then hit 'w' key to select .WAV file." _
    +n + "(Any other restores default palette and exits.)"
    WaitKey=128        ;Wait for any key
    GenVolDlg=0        ;Close volume slider dialog
    InputDlg=0         ;Close input dialog
ELSE.              ;If output only:
    Msg="Unmute and adjust Volume for comfortable listening." _
    +n + "Then hit 'w' key to select .WAV file." _
    +n + "(Any other restores default palette and exits.)"
    WaitKey=128        ;Wait for any key
ENDIF.
E.IF.MasterMute=       ;Master Mute present?
    QM=MasterMute          ;Save Mute state
ENDIF.
Msg=               ;Close message
Gen=0              ;Generator off temporarily
IF.Key?#=!"w"      ;Not 'w' key?
    A.LoadPAL="Default"    ;Restore default Sgram palette
    LoopBreak=-1           ;Exit macro if not
ENDIF.
PrefVolChg#X=0     ;No exit prompt for above volume changes
L.0.Play0="*.WAV"      ;Select .WAV file for Play0

;Get params from first 1024 samples of WAV file:
Buf0="<=P0"            ;Copy start of Play0 waveform to Buf0
Buf1="<=(0)"           ;Clear Buf1
U1=0                   ;Buf1 parameter index
U0=8                   ;Buf0 sample index, skip sync pulse
WHILE.U1=<5            ;Get 5 param words
    UN=0                   ;Nybble index
    UW=0                   ;Word value
    WHILE.UN=<4            ;4 nybbles per param word
        UA=0                   ;Average nybble value
        UI=0                   ;Nybble sample index
        WHILE.UI=<4            ;4 samples per pos pulse phase
            UA=UA+Buf0[U0+UI]      ;Add pos samples to total
            UI=UI+1                ;Next nybble sample
        WEND.
        WHILE.UI=<8            ;4 more samples per neg phase
            UA=UA-Buf0[U0+UI]      ;Subtract neg samples from total
            UI=UI+1                ;Next nybble sample
        WEND.
        U0=U0+8                ;Sample at start of next nybble (pos)
        UW=UW + abs(UA) / (8 * 1024) << (4 * UN)   ;Nybble to word
        UN=UN+1                ;Next nybble
    WEND.                  ;Get all 4 nybbles
    Buf1[U1]=UW            ;Save param word
    U1=U1+1                ;Next param word
WEND.                  ;Get all 4 params
IF.Buf1(0)=!hB2AF      ;ID param = "BMP 2 Audio Format"?
    Msg="Not a BMP_to_WAV file."
    LoopBreak=-1           ;Exit if not
ENDIF.

;Get parameters from Buf1:
QN=Buf1[1]             ;Number of palette colors
QR=Buf1[2]             ;Bitmap dB range = 0 to -QR
S=(QR+1) / QN          ;dB per step
U0=Buf1[3]             ;Bottom spectral line #
QP=Buf1[4]             ;Palette nybble dB range

;Use initial sync pulse to adjust timing:
L.0.PlayFrom=0         ;Play 1st frame repeatedly
L.0.PlayTo=1023
Gen=1                  ;Output on
IF.UC=<2               ;If Input active at start,
    Input=1                ;Input on also
ENDIF.
WaitSecs=500m          ;Wait for sound output
Buf0="<=W(UC)"         ;Capture waveform
P=Buf0?p               ;Scan for pos peak (ignore amplitude P)
A=Posn?p               ;Position of pos peak
P=Buf0?n               ;Scan for neg peak
B=Posn?p               ;Position of neg peak
IF.B=<A                ;If neg before pos, polarity inverted
    A=B                    ;A = leading edge
ENDIF.
TrigDelay=TrigDelay + A    ;Adjust so leading edge is frame start

;Get averaged 0 dB reference spectrum:
L.0.PlayTo=2047        ;Play second frame
L.0.PlayFrom=1024
WaitSecs=T             ;Wait for sound start
Ch=UC                  ;Set channel (0=Left In, 2=Left Out)
Ch.SpectCurve=0        ;Cancel any Spectrum Curve on channel
K=log10(2)             ;Constant for Buf log10
Avg=1                  ;Start spectrum average
WaitAvg=               ;Wait for average to finish
D=sSig(U0,255+U0) / 32767          ;Spectrum sigma over band
D=20 * log10(D) - 10 * log10(256)  ;Avg dB in band
Buf0="<=(0)"           ;Clear Buf0 for spectrum correction
IF.UC=<2               ;If Input, create response correction curve
    Buf0="<=A(UC)"         ;Copy averaged spectrum (magnitude)
    Buf0="<*((2^(-15)) / 32767)"   ;Apply scaling
    Buf0="<l(K)"           ;Log10 of spectrum
    Buf0="<*(20)"          ;Spectrum in dB
    Buf0="<~(D)"           ;Reflect curve about average value
    Buf0="<o(U0,(255+U0))" ;Crop to keep only active range
    Buf0="<uV0"            ;Upload as Memory Curve 0
    Ch.Curve0=1            ;Apply Curve0 to Input channel
ENDIF.
Pause=0                ;UnPause after prior average

;Recover the palette from 6 nybble frames in bBgGrR order:
Buf2="<=(0)"           ;Clear palette working copy
UP=0                   ;Nybble frame counter
WHILE.UP=<6            ;Do all 6 frames
    L.0.PlayTo=3071 + UP * 1024    ;End of nybble frame
    L.0.PlayFrom=2048 + UP * 1024  ;Start of frame
    WaitSecs=T             ;Wait for sound start
    Avg=1                  ;Start spectrum average
    WaitAvg=               ;Wait for average to finish
    Buf1="<=A(UC)"         ;Copy averaged spectrum (magnitude)
    Buf1="<*((2^(-15)) / 32767)"   ;Apply scaling
    Buf1="<l(K)"           ;Log10 of spectrum
    Buf1="<*(20)"          ;Spectrum in dB
    Buf1="<+B0"            ;Add Buf0 to correct to flat spectrum
    Buf1="<-(D - QP)"      ;0 dB becomes +QP (+30) value
    Buf1="</(QP/15)"       ;Nybble value (0 dB = +15)
    UI=0                   ;Palette color counter
    WHILE.UI=<QN           ;For all colors, apply nybble to dword
        Buf2[UI]=Buf2[UI] + cint(Buf1[UI+U0]) << (4 * UP)
        UI=UI+1                ;Next palette color
    WEND.
    Pause=0                ;UnPause from above average
    UP=UP+1                ;Next nybble frame
WEND.

;Widen palette bars if 128 colors or less:
QJ=int(256 / QN)           ;Width multiplier
UI=0                       ;Palette color counter
WHILE.UI=<QN               ;Do all colors
    C=Buf2[UI]                 ;Get palette color
    UJ=0
    WHILE.UJ=<QJ               ;Do each width multiple
        Buf0[QJ*UI + UJ]=C         ;Color to final palette
        UJ=UJ+1                    ;Next width multiple
    WEND.
    UI=UI+1                    ;Next palette color
WEND.
Buf0#Ps=0                  ;Set palette for Sgram

;Set Generator to play image portion of .WAV as Sgram:
L.0.PlayTo=1G              ;Set end too large, limits as needed
L.0.PlayFrom=8192          ;Start of image portion
E.IF.MasterMute=           ;Master Mute present?
    MasterMute=QM              ;Unmute sound output as needed
ENDIF.
SgMode=Scroll              ;Set Sgram for scroll mode
Sgram=1                    ;Start Spectrogram
PitchTrk=0                 ;No Pitch Track
XpandMin=U0 * SmplRate / 1024      ;Set active freq range
XpandMax=(255 + U0) * SmplRate / 1024
Xpand=1                    ;Show only active range
G=20 * log10(Range?V)      ;Get total gain of L.In or L.Out
YlogScrnTop=D + G + S/2    ;Set Sgram top color dB
YlogScrnBot=D + G - QR + S/4   ;Bottom color dB

See also Macro Examples and Mini-Apps

GO:

Questions? Comments? Contact us!

We respond to ALL inquiries, typically within 24 hrs.
INTERSTELLAR RESEARCH:
Over 35 Years of Innovative Instrumentation
© Copyright 2007 - 2023 by Interstellar Research
All rights reserved