formats.cpp
1 /* 2 Copyright (C) 2009 Jonathon Fowler <jf@jonof.id.au> 3 Copyright (C) 2015 EDuke32 developers 4 Copyright (C) 2015 Voidpoint, LLC 5 6 This program is free software; you can redistribute it and/or 7 modify it under the terms of the GNU General Public License 8 as published by the Free Software Foundation; either version 2 9 of the License, or (at your option) any later version. 10 11 This program is distributed in the hope that it will be useful, 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 14 15 See the GNU General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with this program; if not, write to the Free Software 19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 20 21 */ 22 23 /** 24 * Raw, WAV, and VOC source support for MultiVoc 25 */ 26 27 #include "_multivc.h" 28 #include "compat.h" 29 #include "multivoc.h" 30 #include "pitch.h" 31 #include "pragmas.h" 32 33 static playbackstatus MV_GetNextWAVBlock(VoiceNode *voice) 34 { 35 if (voice->BlockLength == 0) 36 { 37 if (voice->Loop.Start == nullptr) 38 return NoMoreData; 39 40 voice->BlockLength = voice->Loop.Size; 41 voice->NextBlock = voice->Loop.Start; 42 voice->length = 0; 43 voice->position = 0; 44 } 45 46 voice->sound = voice->NextBlock; 47 voice->position -= voice->length; 48 voice->length = min(voice->BlockLength, 0x8000u); 49 voice->NextBlock += voice->length * ((voice->channels * voice->bits) >> 3); 50 voice->BlockLength -= voice->length; 51 voice->length <<= 16; 52 53 return KeepPlaying; 54 } 55 56 static playbackstatus MV_GetNextVOCBlock(VoiceNode *voice) 57 { 58 size_t blocklength = 0; 59 uint32_t samplespeed = 0; // XXX: compiler-happy on synthesis 60 uint32_t tc = 0; 61 unsigned BitsPerSample; 62 unsigned Channels; 63 unsigned Format; 64 65 if (voice->BlockLength > 0) 66 { 67 voice->position -= voice->length; 68 voice->sound += (voice->length >> 16) * ((voice->channels * voice->bits) >> 3); 69 voice->length = min(voice->BlockLength, 0x8000u); 70 voice->BlockLength -= voice->length; 71 voice->length <<= 16; 72 return KeepPlaying; 73 } 74 75 auto ptr = (uint8_t const *)voice->NextBlock; 76 77 voice->Paused = FALSE; 78 79 int voicemode = 0; 80 int blocktype = 0; 81 int lastblocktype = 0; 82 int packtype = 0; 83 84 int done = FALSE; 85 86 do 87 { 88 // Stop playing if we get a null pointer 89 if (ptr == nullptr) 90 { 91 done = 2; 92 break; 93 } 94 95 // terminator is not mandatory according to 96 // http://wiki.multimedia.cx/index.php?title=Creative_Voice 97 98 if ((uint32_t)(ptr - (uint8_t *)voice->rawdataptr) >= voice->rawdatasiz) 99 blocktype = 0; // fake a terminator 100 else 101 blocktype = *ptr; 102 103 if (blocktype != 0) 104 blocklength = ptr[1]|(ptr[2]<<8)|(ptr[3]<<16); 105 else 106 blocklength = 0; 107 // would need one byte pad at end of alloc'd region: 108 // blocklength = B_LITTLE32(*(uint32_t *)(ptr + 1)) & 0x00ffffff; 109 110 ptr += 4; 111 112 switch (blocktype) 113 { 114 case 0 : 115 end_of_data: 116 // End of data 117 if ((voice->Loop.Start == nullptr) || 118 ((intptr_t) voice->Loop.Start >= ((intptr_t) ptr - 4))) 119 { 120 done = 2; 121 } 122 else 123 { 124 voice->NextBlock = voice->Loop.Start; 125 voice->BlockLength = 0; 126 voice->position = 0; 127 return MV_GetNextVOCBlock(voice); 128 } 129 break; 130 131 case 1 : 132 // Sound data block 133 voice->bits = 8; 134 voice->channels = voicemode + 1; 135 if (lastblocktype != 8) 136 { 137 tc = (uint32_t)*ptr << 8; 138 packtype = *(ptr + 1); 139 } 140 141 ptr += 2; 142 blocklength -= 2; 143 144 samplespeed = 256000000L / (voice->channels * (65536 - tc)); 145 146 // Skip packed or stereo data 147 if ((packtype != 0) || (voicemode != 0 && voicemode != 1)) 148 ptr += blocklength; 149 else 150 done = TRUE; 151 152 if ((uint32_t)(ptr - (uint8_t *)voice->rawdataptr) >= voice->rawdatasiz) 153 goto end_of_data; 154 155 voicemode = 0; 156 break; 157 158 case 2 : 159 // Sound continuation block 160 samplespeed = voice->SamplingRate; 161 done = TRUE; 162 break; 163 164 case 3 : 165 // Silence 166 case 4 : 167 // Marker 168 case 5 : 169 // ASCII string 170 // All not implemented. 171 ptr += blocklength; 172 break; 173 174 case 6 : 175 // Repeat begin 176 if (voice->Loop.End == nullptr) 177 { 178 voice->Loop.Count = B_LITTLE16(*(uint16_t const *)ptr); 179 voice->Loop.Start = (char *)((intptr_t) ptr + blocklength); 180 } 181 ptr += blocklength; 182 break; 183 184 case 7 : 185 // Repeat end 186 ptr += blocklength; 187 if (lastblocktype == 6) 188 voice->Loop.Count = 0; 189 else 190 { 191 if ((voice->Loop.Count > 0) && (voice->Loop.Start != nullptr)) 192 { 193 ptr = (uint8_t const *) voice->Loop.Start; 194 195 if (voice->Loop.Count < 0xffff) 196 { 197 if (--voice->Loop.Count == 0) 198 voice->Loop.Start = nullptr; 199 } 200 } 201 } 202 break; 203 204 case 8 : 205 // Extended block 206 voice->bits = 8; 207 voice->channels = 1; 208 tc = B_LITTLE16(*(uint16_t const *)ptr); 209 packtype = *(ptr + 2); 210 voicemode = *(ptr + 3); 211 ptr += blocklength; 212 break; 213 214 case 9 : 215 // New sound data block 216 samplespeed = B_LITTLE32(*(uint32_t const *)ptr); 217 BitsPerSample = (unsigned)*(ptr + 4); 218 Channels = (unsigned)*(ptr + 5); 219 Format = (unsigned)B_LITTLE16(*(uint16_t const *)(ptr + 6)); 220 221 if ((BitsPerSample == 8) && (Channels == 1 || Channels == 2) && (Format == VOC_8BIT)) 222 { 223 ptr += 12; 224 blocklength -= 12; 225 voice->bits = 8; 226 voice->channels = Channels; 227 done = TRUE; 228 } 229 else if ((BitsPerSample == 16) && (Channels == 1 || Channels == 2) && (Format == VOC_16BIT)) 230 { 231 ptr += 12; 232 blocklength -= 12; 233 voice->bits = 16; 234 voice->channels = Channels; 235 done = TRUE; 236 } 237 else 238 { 239 ptr += blocklength; 240 } 241 242 // CAUTION: 243 // SNAKRM.VOC is corrupt! blocklength gets us beyond the 244 // end of the file. 245 if ((uint32_t)(ptr - (uint8_t *)voice->rawdataptr) >= voice->rawdatasiz) 246 goto end_of_data; 247 248 break; 249 250 default : 251 // Unknown data. Probably not a VOC file. 252 done = 2; 253 break; 254 } 255 256 lastblocktype = blocktype; 257 } 258 while (!done); 259 260 if (done != 2) 261 { 262 voice->NextBlock = (char const *)ptr + blocklength; 263 voice->sound = (char const *)ptr; 264 265 // CODEDUP multivoc.c MV_SetVoicePitch 266 voice->SamplingRate = samplespeed; 267 voice->RateScale = divideu64((uint64_t)voice->SamplingRate * voice->PitchScale, MV_MixRate); 268 269 // Multiply by MV_MIXBUFFERSIZE - 1 270 voice->FixedPointBufferSize = (voice->RateScale * MV_MIXBUFFERSIZE) - 271 voice->RateScale; 272 273 if (voice->Loop.End != nullptr) 274 { 275 if (blocklength > (uintptr_t)voice->Loop.End) 276 blocklength = (uintptr_t)voice->Loop.End; 277 else 278 voice->Loop.End = (char *)blocklength; 279 280 voice->Loop.Start = voice->sound + (uintptr_t)voice->Loop.Start; 281 voice->Loop.End = voice->sound + (uintptr_t)voice->Loop.End; 282 voice->Loop.Size = voice->Loop.End - voice->Loop.Start; 283 } 284 285 if (voice->bits == 16) 286 blocklength /= 2; 287 288 if (voice->channels == 2) 289 blocklength /= 2; 290 291 voice->position = 0; 292 voice->length = min<uint32_t>(blocklength, 0x8000u); 293 voice->BlockLength = blocklength - voice->length; 294 voice->length <<= 16; 295 296 MV_SetVoiceMixMode(voice); 297 298 return KeepPlaying; 299 } 300 301 return NoMoreData; 302 } 303 304 static playbackstatus MV_GetNextRAWBlock(VoiceNode *voice) 305 { 306 if (voice->BlockLength == 0) 307 { 308 if (voice->Loop.Start == NULL) 309 return NoMoreData; 310 311 voice->BlockLength = voice->Loop.Size; 312 voice->NextBlock = voice->Loop.Start; 313 voice->length = 0; 314 voice->position = 0; 315 } 316 317 voice->sound = voice->NextBlock; 318 voice->position -= voice->length; 319 voice->length = min(voice->BlockLength, 0x8000u); 320 voice->NextBlock += voice->length * (voice->channels * voice->bits / 8); 321 voice->BlockLength -= voice->length; 322 voice->length <<= 16; 323 324 return KeepPlaying; 325 } 326 327 int MV_PlayWAV3D(char *ptr, uint32_t length, int loophow, int pitchoffset, int angle, int distance, 328 int priority, fix16_t volume, intptr_t callbackval) 329 { 330 if (!MV_Installed) 331 return MV_Error; 332 333 if (distance < 0) 334 { 335 distance = -distance; 336 angle += MV_NUMPANPOSITIONS / 2; 337 } 338 339 int const vol = MIX_VOLUME(distance); 340 341 // Ensure angle is within 0 - 127 342 angle &= MV_MAXPANPOSITION; 343 344 return MV_PlayWAV(ptr, length, loophow, -1, pitchoffset, max(0, 255 - distance), 345 MV_PanTable[angle][vol].left, MV_PanTable[angle][vol].right, priority, volume, callbackval); 346 } 347 348 int MV_PlayWAV(char *ptr, uint32_t length, int loopstart, int loopend, int pitchoffset, int vol, 349 int left, int right, int priority, fix16_t volume, intptr_t callbackval) 350 { 351 if (!MV_Installed) 352 return MV_Error; 353 354 riff_header riff; 355 memcpy(&riff, ptr, sizeof(riff_header)); 356 riff.file_size = B_LITTLE32(riff.file_size); 357 riff.format_size = B_LITTLE32(riff.format_size); 358 359 if ((memcmp(riff.RIFF, "RIFF", 4) != 0) || (memcmp(riff.WAVE, "WAVE", 4) != 0) || (memcmp(riff.fmt, "fmt ", 4) != 0)) 360 return MV_SetErrorCode(MV_InvalidFile); 361 362 format_header format; 363 memcpy(&format, ptr + sizeof(riff_header), sizeof(format_header)); 364 format.wFormatTag = B_LITTLE16(format.wFormatTag); 365 format.nChannels = B_LITTLE16(format.nChannels); 366 format.nSamplesPerSec = B_LITTLE32(format.nSamplesPerSec); 367 format.nAvgBytesPerSec = B_LITTLE32(format.nAvgBytesPerSec); 368 format.nBlockAlign = B_LITTLE16(format.nBlockAlign); 369 format.nBitsPerSample = B_LITTLE16(format.nBitsPerSample); 370 371 data_header data; 372 memcpy(&data, ptr + sizeof(riff_header) + riff.format_size, sizeof(data_header)); 373 data.size = B_LITTLE32(data.size); 374 375 // Check if it's PCM data. 376 if (format.wFormatTag != 1 || (format.nChannels != 1 && format.nChannels != 2) || 377 ((format.nBitsPerSample != 8) && (format.nBitsPerSample != 16)) || memcmp(data.DATA, "data", 4) != 0) 378 return MV_SetErrorCode(MV_InvalidFile); 379 380 // Request a voice from the voice pool 381 382 auto voice = MV_AllocVoice(priority); 383 384 if (voice == nullptr) 385 return MV_SetErrorCode(MV_NoVoices); 386 387 voice->wavetype = FMT_WAV; 388 voice->bits = format.nBitsPerSample; 389 voice->channels = format.nChannels; 390 voice->GetSound = MV_GetNextWAVBlock; 391 392 int blocklen = data.size; 393 394 if (voice->bits == 16) 395 { 396 data.size &= ~1; 397 blocklen /= 2; 398 } 399 400 if (voice->channels == 2) 401 { 402 data.size &= ~1; 403 blocklen /= 2; 404 } 405 406 voice->rawdataptr = (uint8_t *)ptr; 407 voice->rawdatasiz = length; 408 voice->position = 0; 409 voice->BlockLength = blocklen; 410 voice->NextBlock = (char *)((intptr_t)ptr + sizeof(riff_header) + riff.format_size + sizeof(data_header)); 411 voice->priority = priority; 412 voice->callbackval = callbackval; 413 voice->Loop.Start = loopstart >= 0 ? voice->NextBlock : nullptr; 414 voice->Loop.End = nullptr; 415 voice->Loop.Count = 0; 416 voice->Loop.Size = loopend > 0 ? loopend - loopstart + 1 : blocklen; 417 418 MV_SetVoicePitch(voice, format.nSamplesPerSec, pitchoffset); 419 MV_SetVoiceVolume(voice, vol, left, right, volume); 420 MV_PlayVoice(voice); 421 422 return voice->handle; 423 } 424 425 int MV_PlayVOC3D(char *ptr, uint32_t length, int loophow, int pitchoffset, int angle, 426 int distance, int priority, fix16_t volume, intptr_t callbackval) 427 { 428 if (!MV_Installed) 429 return MV_Error; 430 431 if (distance < 0) 432 { 433 distance = -distance; 434 angle += MV_NUMPANPOSITIONS / 2; 435 } 436 437 int const vol = MIX_VOLUME(distance); 438 439 // Ensure angle is within 0 - 127 440 angle &= MV_MAXPANPOSITION; 441 442 return MV_PlayVOC(ptr, length, loophow, -1, pitchoffset, max(0, 255 - distance), 443 MV_PanTable[angle][vol].left, MV_PanTable[angle][vol].right, priority, volume, callbackval); 444 } 445 446 int MV_PlayVOC(char *ptr, uint32_t length, int loopstart, int loopend, int pitchoffset, int vol, 447 int left, int right, int priority, fix16_t volume, intptr_t callbackval) 448 { 449 if (!MV_Installed) 450 return MV_Error; 451 452 // Make sure it looks like a valid VOC file. 453 if (memcmp(ptr, "Creative Voice File", 19) != 0) 454 return MV_SetErrorCode(MV_InvalidFile); 455 456 // Request a voice from the voice pool 457 auto voice = MV_AllocVoice(priority); 458 459 if (voice == nullptr) 460 return MV_SetErrorCode(MV_NoVoices); 461 462 voice->rawdataptr = (uint8_t *)ptr; 463 voice->rawdatasiz = length; 464 voice->wavetype = FMT_VOC; 465 voice->bits = 8; 466 voice->channels = 1; 467 voice->GetSound = MV_GetNextVOCBlock; 468 voice->NextBlock = ptr + B_LITTLE16(*(uint16_t *)(ptr + 0x14)); 469 voice->PitchScale = PITCH_GetScale(pitchoffset); 470 voice->priority = priority; 471 voice->callbackval = callbackval; 472 voice->Loop = { loopstart >= 0 ? voice->NextBlock : nullptr, nullptr, 0, (uint32_t)(loopend - loopstart + 1) }; 473 474 MV_SetVoiceVolume(voice, vol, left, right, volume); 475 MV_PlayVoice(voice); 476 477 return voice->handle; 478 } 479 480 int MV_PlayRAW(char *ptr, uint32_t length, int rate, char *loopstart, char *loopend, int pitchoffset, int vol, 481 int left, int right, int priority, fix16_t volume, intptr_t callbackval) 482 { 483 if (!MV_Installed) 484 return MV_Error; 485 486 // Request a voice from the voice pool 487 auto voice = MV_AllocVoice(priority); 488 489 if (voice == nullptr) 490 return MV_SetErrorCode(MV_NoVoices); 491 492 voice->rawdataptr = (uint8_t *)ptr; 493 voice->rawdatasiz = length; 494 voice->wavetype = FMT_RAW; 495 voice->bits = 8; 496 voice->channels = 1; 497 voice->GetSound = MV_GetNextRAWBlock; 498 voice->NextBlock = ptr; 499 voice->position = 0; 500 voice->BlockLength = length; 501 voice->PitchScale = PITCH_GetScale(pitchoffset); 502 voice->priority = priority; 503 voice->callbackval = callbackval; 504 voice->Loop = { loopstart, loopend, 0, (uint32_t)(loopend - loopstart + 1) }; 505 voice->volume = volume; 506 507 MV_SetVoicePitch(voice, rate, pitchoffset); 508 MV_SetVoiceVolume(voice, vol, left, right, volume); 509 MV_PlayVoice(voice); 510 511 return voice->handle; 512 }