Dennis Nedry
Well-known member
I have it to the point where it sounds good now, but it is not perfect by any means when you start comparing the waveform to an actual recording. It's in the very nature of this integration-type decompression to propagate the slightest error into something huge. I believe that it could be perfected, assuming that the sound chip accomplishes its decompression with a completely digital circuit, but I also believe that it's good to realize the point when something becomes good enough to consider moving on.
Here is the latest version of audio and code:
https://sites.google.com/site/benboldt/files/Q700DataOut6.aif
Here is the latest version of audio and code:
https://sites.google.com/site/benboldt/files/Q700DataOut6.aif
Code:
#include
#include
#include
using namespace std;
// Global variable definitions
FILE *f = fopen("Q700RawSoundData2", "rb");
FILE *w = fopen("Q700DataOut", "wb");
unsigned int cosine_pointer;
int8_t packet[15];
int8_t prev_type;
int8_t nibble_1, nibble_2;
int16_t raster_1, raster_2;
int16_t sample;
int16_t envelope;
int16_t accumulator;
void speak(int8_t type, int16_t raster)
{
int16_t cosine_wave[21] = {27073, 32767, 27073, 11971, -7291, -24020,
-32401, -29522, -16384, 2449, 20430,
31311, 31311, 20430, 2449, -16384,
-29522, -32401, -24020, -7291, 11971};
int32_t calc;
if(cosine_pointer > 20) cosine_pointer = 0; // Loop cosine pointer as necessary.
// Filter for max raster depending on type.
if(0x20 == type) // Damp Pulse
{
if(raster > 9238) raster = 9238;
else if(raster < -9238) raster = -9238;
}
else if(0x30 == type) // Enveloped Cosine
{
if(raster > 20797) raster = 20797;
else if(raster < -20797) raster = -20797;
}
envelope += raster;
if(0 != raster)
{
cosine_pointer = 0;
}
switch(type)
{
case 0x00: // Direct samples
sample = raster;
fwrite(&sample, 2, 1, w);
break;
case 0x10: // Envelope Only
fwrite(&envelope, 2, 1, w);
sample = envelope; // Record this sample for next time.
break;
case 0x20: // Damp Pulse
accumulator += raster;
calc = sample + accumulator;
if(calc > 32767)
{
cerr << "0x20 Sample Overflow!" << calc << "\n";
//calc = 32767;
}
else if(calc < -32768)
{
cerr << "0x20 Sample Underflow!" << calc << "\n";
//calc = -32768;
}
sample = calc;
sample = (sample * 7) / 8;
fwrite(&sample, 2, 1, w);
break;
case 0x30: // Enveloped Cosine
sample = (cosine_wave[cosine_pointer] * envelope) / 32767;
fwrite(&sample, 2, 1, w);
break;
default:
cerr << "Error when parsing sound characteristic nibble.\n";
break;
}
envelope = (envelope * 15) / 16; // Envelope degrades: 15/16 times previous value.
accumulator = (accumulator * 7) / 8; // Accumulator degrades: 7/8 times previous value.
cosine_pointer++; // Keep the cosine wave rolling
}
int main(int argc, char *argv[])
{
// Init variables
prev_type = 0x00; // Init prev_type.
cosine_pointer = 0; // Init cosine pointer.
envelope = 0;
sample = 0;
while (fread(packet, 1, 15, f)) // Read in entire packets until EOF.
{
if(0x30 != prev_type) cosine_pointer = 1; // Start at peak of cosine wave.
envelope = sample; // Set the envelope to the current position of the sample when changing packet types.
for(int i = 1; i < 15; i++) // for each packet data byte
{
// Separate and sign extend each nibble.
nibble_1 = packet[i] & 0x0F; // Least significant nibble plays first.
nibble_2 = (packet[i] & 0xF0) >> 4;
if(nibble_1 & 0x08) nibble_1 |= 0xF0; // Sign extend
if(nibble_2 & 0x08) nibble_2 |= 0xF0;
// Determine decompressed value based on data nibble and header nibble
raster_1 = (nibble_1 * 4096);
for(int j = (packet[0] & 0x0F); j != 0; j--) raster_1 /= 2; // Can't shift because of signed numbers.
raster_2 = (nibble_2 * 4096);
for(int j = (packet[0] & 0x0F); j != 0; j--) raster_2 /= 2; // Can't shift because of signed numbers.
speak(packet[0] & 0x30, raster_1);
speak(packet[0] & 0x30, raster_2);
}
prev_type = packet[0] & 0x30;
}
fclose(f);
fclose(w);
}