RAW file in the AUDIO directory (and ONLY in the AUDIO directory) contain raw
sound data.
SDT files contain the offsets, length, and bitrate of the individual sounds.
The SDT file does NOT contain all information that is required to convert to
WAV.
The number of channels (1=mono, 2=stereo) as well as the bits per sample (8, 16)
must be provided from outside (i.e. hardcoded in the EXE file), and is NOT part
of the SDT file.
The values are:
Sdt {
Sounds[var] {
DWORD startOffset;
DWORD length;
DWORD bitrate
}
}
<?php
$sdtFile = 'D:\\GTADATA\\UK\\AUDIO\\VOCALCOM.SDT';
$rawFile = 'D:\\GTADATA\\UK\\AUDIO\\VOCALCOM.RAW';
$outDir = 'C:\\Temp\\';
if (!file_exists($sdtFile) || !file_exists($rawFile)) {
die("SDT or RAW file not found.\n");
}
if (!is_dir($outDir)) {
mkdir($outDir, 0777, true);
}
$sdtData = file_get_contents($sdtFile);
$rawData = file_get_contents($rawFile);
$entrySize = 12;
$totalEntries = intdiv(strlen($sdtData), $entrySize);
echo "Found
entries: $totalEntries\n";
$baseName = pathinfo($sdtFile, PATHINFO_FILENAME);
for ($i = 0; $i < $totalEntries; $i++) {
$entry = substr($sdtData, $i * $entrySize, $entrySize);
// Little Endian 3x uint32
$data = unpack("Voffset/Vlength/Vsamplerate", $entry);
$offset = $data['offset'];
$length = $data['length'];
$rate = $data['samplerate'];
if ($length <= 0) continue;
$audioData = substr($rawData, $offset, $length);
if (strlen($audioData) !== $length) {
echo "Warnung: Invalid
length at entry $i\n";
continue;
}
if (stripos($sdtFile,'LEVEL000') !== false) {
$numChannels = $i<3 ? 2 : 1;
$bitsPerSample = 16;
} else {
$numChannels = 1;
$bitsPerSample = 8;
}
$wavData = createWav($audioData, $rate, $numChannels, $bitsPerSample);
$index = str_pad($i + 1, 3, "0", STR_PAD_LEFT);
$outFile = "$outDir/{$baseName}-$index.wav";
file_put_contents($outFile, $wavData);
echo "Exportiert: $outFile (Rate: $rate Hz)\n";
}
function createWav($audioData, $sampleRate, $numChannels, $bitsPerSample)
{
$header = "RIFF"; // ChunkID
$header .= pack("V", 36 + strlen($audioData)); // ChunkSize
$header .= "WAVE"; // Format
$header .= "fmt "; // Subchunk1ID
$header .= pack("V", 16); // Subchunk1Size
$header .= pack("v", 1); // AudioFormat 1=PCM
$header .= pack("v", $numChannels); // NumChannels
$header .= pack("V", $sampleRate); // SampleRate
$header .= pack("V", $sampleRate * $numChannels * ($bitsPerSample / 8)); // ByteRate
$header .= pack("v", $numChannels * ($bitsPerSample / 8)); // BlockAlign
$header .= pack("v", $bitsPerSample); // BitsPerSample
// Space for extra parameters (if PCM, then doesn't exist):
//$header .= pack("v", strlen($ExtraParams)); // ExtraParamSize
//$header .= $ExtraParams; // ExtraParams
$header .= "data"; // Subchunk2ID
$header .= pack("V", strlen($audioData)); // Subchunk2Size
return $header . $audioData; // Data
}