Line data Source code
1 : /* Copyright (C) 2004-2025 Savoir-faire Linux Inc. 2 : * 3 : * Inspired by tonegenerator of 4 : * Laurielle Lea <laurielle.lea@savoirfairelinux.com> (2004) 5 : * Inspired by ringbuffer of Audacity Project 6 : * 7 : * This program is free software: you can redistribute it and/or modify 8 : * it under the terms of the GNU General Public License as published by 9 : * the Free Software Foundation, either version 3 of the License, or 10 : * (at your option) any later version. 11 : * 12 : * This program is distributed in the hope that it will be useful, 13 : * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 : * 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, see <https://www.gnu.org/licenses/>. 19 : */ 20 : #include <fstream> 21 : #include <cmath> 22 : #include <cstring> 23 : #include <vector> 24 : #include <climits> 25 : 26 : #include "libav_deps.h" 27 : #include "audiofile.h" 28 : #include "audio/resampler.h" 29 : #include "manager.h" 30 : #include "media_decoder.h" 31 : #include "client/ring_signal.h" 32 : 33 : #include "logger.h" 34 : 35 : namespace jami { 36 : 37 : void 38 0 : AudioFile::onBufferFinish() 39 : { 40 0 : if (buffer_->sample_rate == 0) { 41 0 : JAMI_ERR("Error unable to update playback slider, sampling rate is 0"); 42 0 : return; 43 : } 44 : 45 : // We want to send values in milisecond 46 0 : if ((updatePlaybackScale_ % 5) == 0) 47 0 : emitSignal<libjami::CallSignal::UpdatePlaybackScale>(filepath_, 48 0 : (unsigned) (1000lu * pos_ / buffer_->sample_rate), 49 0 : (unsigned) (1000lu * buffer_->nb_samples 50 0 : / buffer_->sample_rate)); 51 : 52 0 : updatePlaybackScale_++; 53 : } 54 : 55 55 : AudioFile::AudioFile(const std::string& fileName, unsigned int sampleRate, AVSampleFormat sampleFormat) 56 : : AudioLoop(AudioFormat(sampleRate, 1, sampleFormat)) 57 55 : , filepath_(fileName) 58 55 : , updatePlaybackScale_(0) 59 : { 60 55 : std::list<std::shared_ptr<AudioFrame>> buf; 61 55 : size_t total_samples = 0; 62 : 63 55 : auto start = std::chrono::steady_clock::now(); 64 55 : Resampler r {}; 65 : auto decoder = std::make_unique<MediaDecoder>( 66 0 : [&r, this, &buf, &total_samples](const std::shared_ptr<MediaFrame>& frame) mutable { 67 0 : auto resampled = r.resample(std::static_pointer_cast<AudioFrame>(frame), format_); 68 0 : total_samples += resampled->getFrameSize(); 69 0 : buf.emplace_back(std::move(resampled)); 70 55 : }); 71 55 : DeviceParams dev; 72 55 : dev.input = fileName; 73 55 : dev.name = fileName; 74 : 75 55 : if (decoder->openInput(dev) < 0) 76 55 : throw AudioFileException("Unable to open file: " + fileName); 77 : 78 0 : if (decoder->setupAudio() < 0) 79 0 : throw AudioFileException("Decoder setup failed: " + fileName); 80 : 81 0 : while (decoder->decode() != MediaDemuxer::Status::EndOfFile) 82 : ; 83 : 84 0 : buffer_->nb_samples = total_samples; 85 0 : buffer_->format = format_.sampleFormat; 86 0 : buffer_->sample_rate = format_.sample_rate; 87 0 : av_channel_layout_default(&buffer_->ch_layout, format_.nb_channels); 88 0 : av_frame_get_buffer(buffer_.get(), 0); 89 : 90 0 : size_t outPos = 0; 91 0 : for (auto& frame : buf) { 92 0 : av_samples_copy(buffer_->data, 93 0 : frame->pointer()->data, 94 : outPos, 95 : 0, 96 0 : frame->getFrameSize(), 97 0 : format_.nb_channels, 98 : format_.sampleFormat); 99 0 : outPos += frame->getFrameSize(); 100 : } 101 0 : auto end = std::chrono::steady_clock::now(); 102 0 : auto audioDuration = std::chrono::duration<double>(total_samples / (double) format_.sample_rate); 103 0 : JAMI_LOG("AudioFile: loaded {} samples ({}) as {} in {} from {:s}", 104 : total_samples, 105 : audioDuration, 106 : format_.toString(), 107 : dht::print_duration(end - start), 108 : fileName); 109 330 : } 110 : 111 : } // namespace jami