diff --git a/dist/res/config/entry_types/types_audio.txt b/dist/res/config/entry_types/types_audio.txt index 67e104a02..39b6c234f 100644 --- a/dist/res/config/entry_types/types_audio.txt +++ b/dist/res/config/entry_types/types_audio.txt @@ -166,6 +166,7 @@ entry_types format = snd_speaker; export_ext = "lmp"; reliability = 100; + editor = audio; } snd_wav : sound diff --git a/src/ArchivePanel.cpp b/src/ArchivePanel.cpp index 7a8610139..4200ee297 100644 --- a/src/ArchivePanel.cpp +++ b/src/ArchivePanel.cpp @@ -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" || diff --git a/src/AudioEntryPanel.cpp b/src/AudioEntryPanel.cpp index 82226ae8f..732063289 100644 --- a/src/AudioEntryPanel.cpp +++ b/src/AudioEntryPanel.cpp @@ -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(); diff --git a/src/Conversions.cpp b/src/Conversions.cpp index cc31c849b..6fcc79ee7 100644 --- a/src/Conversions.cpp +++ b/src/Conversions.cpp @@ -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; +} diff --git a/src/Conversions.h b/src/Conversions.h index 23abc694d..e9e39b09b 100644 --- a/src/Conversions.h +++ b/src/Conversions.h @@ -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);