73 |
} |
} |
74 |
FrameOffset = 0; // just for streaming compressed samples |
FrameOffset = 0; // just for streaming compressed samples |
75 |
|
|
76 |
LoopStart /= FrameSize; // convert to sample points |
LoopSize = LoopEnd - LoopStart; |
|
LoopEnd /= FrameSize; // convert to sample points |
|
|
LoopSize = LoopEnd - LoopStart; |
|
77 |
} |
} |
78 |
|
|
79 |
/// Scans compressed samples for mandatory informations (e.g. actual number of total sample points). |
/// Scans compressed samples for mandatory informations (e.g. actual number of total sample points). |
313 |
} |
} |
314 |
|
|
315 |
/** |
/** |
316 |
|
* Reads \a SampleCount number of sample points from the position stored |
317 |
|
* in \a pPlaybackState into the buffer pointed by \a pBuffer and moves |
318 |
|
* the position within the sample respectively, this method honors the |
319 |
|
* looping informations of the sample (if any). The sample wave stream |
320 |
|
* will be decompressed on the fly if using a compressed sample. Use this |
321 |
|
* method if you don't want to load the sample into RAM, thus for disk |
322 |
|
* streaming. All this methods needs to know to proceed with streaming |
323 |
|
* for the next time you call this method is stored in \a pPlaybackState. |
324 |
|
* You have to allocate and initialize the playback_state_t structure by |
325 |
|
* yourself before you use it to stream a sample: |
326 |
|
* |
327 |
|
* <i> |
328 |
|
* gig::playback_state_t playbackstate; <br> |
329 |
|
* playbackstate.position = 0; <br> |
330 |
|
* playbackstate.reverse = false; <br> |
331 |
|
* playbackstate.loop_cycles_left = pSample->LoopPlayCount; <br> |
332 |
|
* </i> |
333 |
|
* |
334 |
|
* You don't have to take care of things like if there is actually a loop |
335 |
|
* defined or if the current read position is located within a loop area. |
336 |
|
* The method already handles such cases by itself. |
337 |
|
* |
338 |
|
* @param pBuffer destination buffer |
339 |
|
* @param SampleCount number of sample points to read |
340 |
|
* @param pPlaybackState will be used to store and reload the playback |
341 |
|
* state for the next ReadAndLoop() call |
342 |
|
* @returns number of successfully read sample points |
343 |
|
*/ |
344 |
|
unsigned long Sample::ReadAndLoop(void* pBuffer, unsigned long SampleCount, playback_state_t* pPlaybackState) { |
345 |
|
unsigned long samplestoread = SampleCount, totalreadsamples = 0, readsamples, samplestoloopend; |
346 |
|
uint8_t* pDst = (uint8_t*) pBuffer; |
347 |
|
|
348 |
|
SetPos(pPlaybackState->position); // recover position from the last time |
349 |
|
|
350 |
|
if (this->Loops && GetPos() <= this->LoopEnd) { // honor looping if there are loop points defined |
351 |
|
|
352 |
|
switch (this->LoopType) { |
353 |
|
|
354 |
|
case loop_type_bidirectional: { //TODO: not tested yet! |
355 |
|
do { |
356 |
|
// if not endless loop check if max. number of loop cycles have been passed |
357 |
|
if (this->LoopPlayCount && !pPlaybackState->loop_cycles_left) break; |
358 |
|
|
359 |
|
if (!pPlaybackState->reverse) { // forward playback |
360 |
|
do { |
361 |
|
samplestoloopend = this->LoopEnd - GetPos(); |
362 |
|
readsamples = Read(&pDst[totalreadsamples * this->FrameSize], Min(samplestoread, samplestoloopend)); |
363 |
|
samplestoread -= readsamples; |
364 |
|
totalreadsamples += readsamples; |
365 |
|
if (readsamples == samplestoloopend) { |
366 |
|
pPlaybackState->reverse = true; |
367 |
|
break; |
368 |
|
} |
369 |
|
} while (samplestoread && readsamples); |
370 |
|
} |
371 |
|
else { // backward playback |
372 |
|
|
373 |
|
// as we can only read forward from disk, we have to |
374 |
|
// determine the end position within the loop first, |
375 |
|
// read forward from that 'end' and finally after |
376 |
|
// reading, swap all sample frames so it reflects |
377 |
|
// backward playback |
378 |
|
|
379 |
|
unsigned long swapareastart = totalreadsamples; |
380 |
|
unsigned long loopoffset = GetPos() - this->LoopStart; |
381 |
|
unsigned long samplestoreadinloop = Min(samplestoread, loopoffset); |
382 |
|
unsigned long reverseplaybackend = GetPos() - samplestoreadinloop; |
383 |
|
|
384 |
|
SetPos(reverseplaybackend); |
385 |
|
|
386 |
|
// read samples for backward playback |
387 |
|
do { |
388 |
|
readsamples = Read(&pDst[totalreadsamples * this->FrameSize], samplestoreadinloop); |
389 |
|
samplestoreadinloop -= readsamples; |
390 |
|
samplestoread -= readsamples; |
391 |
|
totalreadsamples += readsamples; |
392 |
|
} while (samplestoreadinloop && readsamples); |
393 |
|
|
394 |
|
SetPos(reverseplaybackend); // pretend we really read backwards |
395 |
|
|
396 |
|
if (reverseplaybackend == this->LoopStart) { |
397 |
|
pPlaybackState->loop_cycles_left--; |
398 |
|
pPlaybackState->reverse = false; |
399 |
|
} |
400 |
|
|
401 |
|
// reverse the sample frames for backward playback |
402 |
|
SwapMemoryArea(&pDst[swapareastart * this->FrameSize], (totalreadsamples - swapareastart) * this->FrameSize, this->FrameSize); |
403 |
|
} |
404 |
|
} while (samplestoread && readsamples); |
405 |
|
break; |
406 |
|
} |
407 |
|
|
408 |
|
case loop_type_backward: { // TODO: not tested yet! |
409 |
|
// forward playback (not entered the loop yet) |
410 |
|
if (!pPlaybackState->reverse) do { |
411 |
|
samplestoloopend = this->LoopEnd - GetPos(); |
412 |
|
readsamples = Read(&pDst[totalreadsamples * this->FrameSize], Min(samplestoread, samplestoloopend)); |
413 |
|
samplestoread -= readsamples; |
414 |
|
totalreadsamples += readsamples; |
415 |
|
if (readsamples == samplestoloopend) { |
416 |
|
pPlaybackState->reverse = true; |
417 |
|
break; |
418 |
|
} |
419 |
|
} while (samplestoread && readsamples); |
420 |
|
|
421 |
|
if (!samplestoread) break; |
422 |
|
|
423 |
|
// as we can only read forward from disk, we have to |
424 |
|
// determine the end position within the loop first, |
425 |
|
// read forward from that 'end' and finally after |
426 |
|
// reading, swap all sample frames so it reflects |
427 |
|
// backward playback |
428 |
|
|
429 |
|
unsigned long swapareastart = totalreadsamples; |
430 |
|
unsigned long loopoffset = GetPos() - this->LoopStart; |
431 |
|
unsigned long samplestoreadinloop = (this->LoopPlayCount) ? Min(samplestoread, pPlaybackState->loop_cycles_left * LoopSize - loopoffset) |
432 |
|
: samplestoread; |
433 |
|
unsigned long reverseplaybackend = this->LoopStart + Abs((loopoffset - samplestoreadinloop) % this->LoopSize); |
434 |
|
|
435 |
|
SetPos(reverseplaybackend); |
436 |
|
|
437 |
|
// read samples for backward playback |
438 |
|
do { |
439 |
|
// if not endless loop check if max. number of loop cycles have been passed |
440 |
|
if (this->LoopPlayCount && !pPlaybackState->loop_cycles_left) break; |
441 |
|
samplestoloopend = this->LoopEnd - GetPos(); |
442 |
|
readsamples = Read(&pDst[totalreadsamples * this->FrameSize], Min(samplestoreadinloop, samplestoloopend)); |
443 |
|
samplestoreadinloop -= readsamples; |
444 |
|
samplestoread -= readsamples; |
445 |
|
totalreadsamples += readsamples; |
446 |
|
if (readsamples == samplestoloopend) { |
447 |
|
pPlaybackState->loop_cycles_left--; |
448 |
|
SetPos(this->LoopStart); |
449 |
|
} |
450 |
|
} while (samplestoreadinloop && readsamples); |
451 |
|
|
452 |
|
SetPos(reverseplaybackend); // pretend we really read backwards |
453 |
|
|
454 |
|
// reverse the sample frames for backward playback |
455 |
|
SwapMemoryArea(&pDst[swapareastart * this->FrameSize], (totalreadsamples - swapareastart) * this->FrameSize, this->FrameSize); |
456 |
|
break; |
457 |
|
} |
458 |
|
|
459 |
|
default: case loop_type_normal: { |
460 |
|
do { |
461 |
|
// if not endless loop check if max. number of loop cycles have been passed |
462 |
|
if (this->LoopPlayCount && !pPlaybackState->loop_cycles_left) break; |
463 |
|
samplestoloopend = this->LoopEnd - GetPos(); |
464 |
|
readsamples = Read(&pDst[totalreadsamples * this->FrameSize], Min(samplestoread, samplestoloopend)); |
465 |
|
samplestoread -= readsamples; |
466 |
|
totalreadsamples += readsamples; |
467 |
|
if (readsamples == samplestoloopend) { |
468 |
|
pPlaybackState->loop_cycles_left--; |
469 |
|
SetPos(this->LoopStart); |
470 |
|
} |
471 |
|
} while (samplestoread && readsamples); |
472 |
|
break; |
473 |
|
} |
474 |
|
} |
475 |
|
} |
476 |
|
|
477 |
|
// read on without looping |
478 |
|
if (samplestoread) do { |
479 |
|
readsamples = Read(&pDst[totalreadsamples * this->FrameSize], samplestoread); |
480 |
|
samplestoread -= readsamples; |
481 |
|
totalreadsamples += readsamples; |
482 |
|
} while (readsamples && samplestoread); |
483 |
|
|
484 |
|
// store current position |
485 |
|
pPlaybackState->position = GetPos(); |
486 |
|
|
487 |
|
return totalreadsamples; |
488 |
|
} |
489 |
|
|
490 |
|
/** |
491 |
* Reads \a SampleCount number of sample points from the current |
* Reads \a SampleCount number of sample points from the current |
492 |
* position into the buffer pointed by \a pBuffer and increments the |
* position into the buffer pointed by \a pBuffer and increments the |
493 |
* position within the sample. The sample wave stream will be |
* position within the sample. The sample wave stream will be |