Permalink
Browse files

PC speaker sound support. Yep.

  • Loading branch information...
Gaerzi committed Jan 25, 2014
1 parent 8a9270e commit 1f9a6f339b15fe1f505e7ebc7dd193b956bbc0bf
Showing with 145 additions and 3 deletions.
  1. +1 −0 dist/res/config/entry_types/types_audio.txt
  2. +4 −0 src/ArchivePanel.cpp
  3. +5 −3 src/AudioEntryPanel.cpp
  4. +134 −0 src/Conversions.cpp
  5. +1 −0 src/Conversions.h
@@ -166,6 +166,7 @@ entry_types
format = snd_speaker;
export_ext = "lmp";
reliability = 100;
+ editor = audio;
}
snd_wav : sound
View
@@ -1871,6 +1871,9 @@ bool ArchivePanel::dSndWavConvert()
if (selection[a]->getType()->getFormat() == "snd_doom" ||
selection[a]->getType()->getFormat() == "snd_doom_mac")
worked = Conversions::doomSndToWav(selection[a]->getMCData(), wav);
+ // Or Doom Speaker sound format
+ else if (selection[a]->getType()->getFormat() == "snd_speaker")
+ worked = Conversions::spkSndToWav(selection[a]->getMCData(), wav);
// Or Jaguar Doom sound format
else if (selection[a]->getType()->getFormat() == "snd_jaguar")
worked = Conversions::jagSndToWav(selection[a]->getMCData(), wav);
@@ -2718,6 +2721,7 @@ void ArchivePanel::onEntryListRightClick(wxListEvent& e)
if (!dsnd_selected)
{
if (selection[a]->getType()->getFormat() == "snd_doom" ||
+ selection[a]->getType()->getFormat() == "snd_speaker" ||
selection[a]->getType()->getFormat() == "snd_wolf" ||
selection[a]->getType()->getFormat() == "snd_doom_mac" ||
selection[a]->getType()->getFormat() == "snd_jaguar" ||
View
@@ -211,6 +211,8 @@ bool AudioEntryPanel::open()
if (entry->getType()->getFormat() == "snd_doom" || // Doom Sound -> WAV
entry->getType()->getFormat() == "snd_doom_mac")
Conversions::doomSndToWav(mcdata, convdata);
+ else if (entry->getType()->getFormat() == "snd_speaker") // Doom PC Speaker Sound -> WAV
+ Conversions::spkSndToWav(mcdata, convdata);
else if (entry->getType()->getFormat() == "snd_wolf") // Wolfenstein 3D Sound -> WAV
Conversions::wolfSndToWav(mcdata, convdata);
else if (entry->getType()->getFormat() == "snd_voc") // Creative Voice File -> WAV
@@ -284,7 +286,7 @@ bool AudioEntryPanel::openAudio(MemChunk& audio, string filename)
// Load into buffer
if (sound_buffer->loadFromMemory((const char*)audio.getData(), audio.getSize()))
{
- wxLogMessage("opened as sound");
+ LOG_MESSAGE(3, "opened as sound");
// Bind to sound
sound.setBuffer(*sound_buffer);
audio_type = AUTYPE_SOUND;
@@ -299,13 +301,13 @@ bool AudioEntryPanel::openAudio(MemChunk& audio, string filename)
}
else if (music.openFromMemory((const char*)audio.getData(), audio.getSize()))
{
- wxLogMessage("opened as music");
+ LOG_MESSAGE(3, "opened as music");
// Couldn't open the audio as a sf::SoundBuffer, try sf::Music instead
audio_type = AUTYPE_MUSIC;
// Enable play controls
setAudioDuration(music.getDuration().asMilliseconds());
- wxLogMessage("duration: %dms", music.getDuration().asMilliseconds());
+ //wxLogMessage("duration: %dms", music.getDuration().asMilliseconds());
btn_play->Enable();
btn_stop->Enable();
View
@@ -29,6 +29,7 @@
*******************************************************************/
#include "Main.h"
#include "Archive.h"
+#include "MathStuff.h"
#include "Conversions.h"
#include "ArchiveEntry.h"
#include "mus2mid/mus2mid.h"
@@ -80,6 +81,13 @@ struct jsnd_header_t
uint32_t decay;
};
+// For speaker sound conversion
+struct spksnd_header_t
+{
+ uint16_t zero;
+ uint16_t samples;
+};
+
/*******************************************************************
* FUNCTIONS
*******************************************************************/
@@ -699,3 +707,129 @@ bool Conversions::gmidToMidi(MemChunk& in, MemChunk& out)
return true;
}
+
+/* Conversions::spkSndToWav
+ * Converts Doom PC speaker sound data [in] to wav format, written
+ * to [out]
+ *******************************************************************/
+#define ORIG_RATE 140.0
+#define FACTOR 315 // 315*140 = 44100
+#define FREQ 1193170.0
+#define RATE (ORIG_RATE * FACTOR)
+uint16_t counters[128] =
+{
+ 0, 6818, 6628, 6449, 6279, 6087, 5906, 5736,
+ 5575, 5423, 5279, 5120, 4971, 4830, 4697, 4554,
+ 4435, 4307, 4186, 4058, 3950, 3836, 3728, 3615,
+ 3519, 3418, 3323, 3224, 3131, 3043, 2960, 2875,
+ 2794, 2711, 2633, 2560, 2485, 2415, 2348, 2281,
+ 2213, 2153, 2089, 2032, 1975, 1918, 1864, 1810,
+ 1757, 1709, 1659, 1612, 1565, 1521, 1478, 1435,
+ 1395, 1355, 1316, 1280, 1242, 1207, 1173, 1140,
+ 1107, 1075, 1045, 1015, 986, 959, 931, 905,
+ 879, 854, 829, 806, 783, 760, 739, 718,
+ 697, 677, 658, 640, 621, 604, 586, 570,
+ 553, 538, 522, 507, 493, 479, 465, 452,
+ 439, 427, 415, 403, 391, 380, 369, 359,
+ 348, 339, 329, 319, 310, 302, 293, 285,
+ 276, 269, 261, 253, 246, 239, 232, 226,
+ 219, 213, 207, 201, 195, 190, 184, 179
+};
+bool Conversions::spkSndToWav(MemChunk& in, MemChunk& out)
+{
+ // --- Read Doom sound ---
+
+ // Read doom sound header
+ spksnd_header_t header;
+ in.seek(0, SEEK_SET);
+ in.read(&header, 4);
+
+ // Format checks
+ if (header.zero != 0) // Check for magic number
+ {
+ Global::error = "Invalid Doom PC Speaker Sound";
+ return false;
+ }
+ if (header.samples > (in.getSize() - 4) || header.samples <= 4) // Check for sane values
+ {
+ Global::error = "Invalid Doom PC Speaker Sound";
+ return false;
+ }
+
+ // Read samples
+ uint8_t* osamples = new uint8_t[header.samples];
+ uint8_t* nsamples = new uint8_t[header.samples*FACTOR];
+ in.read(osamples, header.samples);
+
+ // Convert counter values to sample values
+ for (int s = 0; s < header.samples; ++s)
+ {
+ if (osamples[s] > 127)
+ {
+ wxLogMessage("Invalid PC Speaker counter value: %d > 127", osamples[s]);
+ delete[] osamples;
+ delete[] nsamples;
+ return false;
+ }
+ if (osamples[s] > 0)
+ {
+ // First, convert counter value to frequency in Hz
+ double f = FREQ / (double)counters[osamples[s]];
+ //double f = FREQ / (440.0 * pow((double)2.0, (96-osamples[s])/24));
+
+ // Then write a bunch of samples because WAVs at 140 Hz
+ // just plain don't work at all -- I know, I tried.
+ for (int i = 0; i < FACTOR; ++i)
+ {
+ // Finally, convert frequency into sample value
+ int pos = (s*FACTOR) + i;
+ double time = (double)pos/(double)RATE;
+ double sample = 1.0 + sin(time * 2.0 * PI * f);
+ nsamples[pos] = (uint8_t)(sample*128.0);
+ }
+ }
+ else memset(nsamples + (s*FACTOR), 0, FACTOR);
+ }
+
+ // --- Write WAV ---
+
+ wav_chunk_t whdr, wdhdr;
+ wav_fmtchunk_t fmtchunk;
+
+ // Setup data header
+ char did[4] = { 'd', 'a', 't', 'a' };
+ memcpy(&wdhdr.id, &did, 4);
+ wdhdr.size = header.samples * FACTOR;
+
+ // Setup fmt chunk
+ char fid[4] = { 'f', 'm', 't', ' ' };
+ memcpy(&fmtchunk.header.id, &fid, 4);
+ fmtchunk.header.size = 16;
+ fmtchunk.tag = 1;
+ fmtchunk.channels = 1;
+ fmtchunk.samplerate = RATE;
+ fmtchunk.datarate = RATE;
+ fmtchunk.blocksize = 1;
+ fmtchunk.bps = 8;
+
+ // Setup main header
+ char wid[4] = { 'R', 'I', 'F', 'F' };
+ memcpy(&whdr.id, &wid, 4);
+ whdr.size = wdhdr.size + fmtchunk.header.size + 8;
+
+ // Write chunks
+ out.write(&whdr, 8);
+ out.write("WAVE", 4);
+ out.write(&fmtchunk, sizeof(wav_fmtchunk_t));
+ out.write(&wdhdr, 8);
+ out.write(nsamples, header.samples * FACTOR);
+
+ // Ensure data ends on even byte boundary
+ if (header.samples % 2 != 0)
+ out.write("\0", 1);
+
+ delete[] osamples;
+ delete[] nsamples;
+
+ return true;
+}
View
@@ -7,6 +7,7 @@ class ArchiveEntry;
namespace Conversions
{
bool wavToDoomSnd(MemChunk& in, MemChunk& out);
+ bool spkSndToWav(MemChunk& in, MemChunk& out);
bool doomSndToWav(MemChunk& in, MemChunk& out);
bool wolfSndToWav(MemChunk& in, MemChunk& out);
bool jagSndToWav(MemChunk& in, MemChunk& out);

0 comments on commit 1f9a6f3

Please sign in to comment.