Adjust the audio and image of a time mapped frame
| 571 | |
| 572 | // Adjust the audio and image of a time mapped frame |
| 573 | void Clip::apply_timemapping(std::shared_ptr<Frame> frame) |
| 574 | { |
| 575 | // Check for valid reader |
| 576 | if (!reader) |
| 577 | // Throw error if reader not initialized |
| 578 | throw ReaderClosed("No Reader has been initialized for this Clip. Call Reader(*reader) before calling this method."); |
| 579 | |
| 580 | // Check for a valid time map curve |
| 581 | if (time.GetLength() > 1) |
| 582 | { |
| 583 | const std::lock_guard<std::recursive_mutex> lock(getFrameMutex); |
| 584 | |
| 585 | int64_t clip_frame_number = frame->number; |
| 586 | int64_t new_frame_number = adjust_frame_number_minimum(time.GetLong(clip_frame_number)); |
| 587 | |
| 588 | // create buffer |
| 589 | juce::AudioBuffer<float> *source_samples = nullptr; |
| 590 | |
| 591 | // Get delta (difference from this frame to the next time mapped frame: Y value) |
| 592 | double delta = time.GetDelta(clip_frame_number + 1); |
| 593 | const bool prev_is_increasing = time.IsIncreasing(clip_frame_number); |
| 594 | const bool is_increasing = time.IsIncreasing(clip_frame_number + 1); |
| 595 | |
| 596 | // Determine length of source audio (in samples) |
| 597 | // A delta of 1.0 == normal expected samples |
| 598 | // A delta of 0.5 == 50% of normal expected samples |
| 599 | // A delta of 2.0 == 200% of normal expected samples |
| 600 | int target_sample_count = Frame::GetSamplesPerFrame(adjust_timeline_framenumber(clip_frame_number), Reader()->info.fps, |
| 601 | Reader()->info.sample_rate, |
| 602 | Reader()->info.channels); |
| 603 | int source_sample_count = round(target_sample_count * fabs(delta)); |
| 604 | |
| 605 | // Determine starting audio location |
| 606 | AudioLocation location; |
| 607 | if (previous_location.frame == 0 || abs(new_frame_number - previous_location.frame) > 2 || prev_is_increasing != is_increasing) { |
| 608 | // No previous location OR gap detected |
| 609 | location.frame = new_frame_number; |
| 610 | location.sample_start = 0; |
| 611 | |
| 612 | // Create / Reset resampler |
| 613 | // We don't want to interpolate between unrelated audio data |
| 614 | if (resampler) { |
| 615 | delete resampler; |
| 616 | resampler = nullptr; |
| 617 | } |
| 618 | // Init resampler with # channels from Reader (should match the timeline) |
| 619 | resampler = new AudioResampler(Reader()->info.channels); |
| 620 | |
| 621 | // Allocate buffer of silence to initialize some data inside the resampler |
| 622 | // To prevent it from becoming input limited |
| 623 | juce::AudioBuffer<float> init_samples(Reader()->info.channels, 64); |
| 624 | init_samples.clear(); |
| 625 | resampler->SetBuffer(&init_samples, 1.0); |
| 626 | resampler->GetResampledBuffer(); |
| 627 | |
| 628 | } else { |
| 629 | // Use previous location |
| 630 | location = previous_location; |
nothing calls this directly
no test coverage detected