Line data Source code
1 : /* Copyright (C) 2004-2024 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 / buffer_->sample_rate)); 50 : 51 0 : updatePlaybackScale_++; 52 : } 53 : 54 60 : AudioFile::AudioFile(const std::string& fileName, unsigned int sampleRate, AVSampleFormat sampleFormat) 55 : : AudioLoop(AudioFormat(sampleRate, 1, sampleFormat)) 56 60 : , filepath_(fileName) 57 60 : , updatePlaybackScale_(0) 58 : { 59 60 : std::list<std::shared_ptr<AudioFrame>> buf; 60 60 : size_t total_samples = 0; 61 : 62 60 : auto start = std::chrono::steady_clock::now(); 63 60 : Resampler r {}; 64 : auto decoder = std::make_unique<MediaDecoder>( 65 0 : [&r, this, &buf, &total_samples](const std::shared_ptr<MediaFrame>& frame) mutable { 66 0 : auto resampled = r.resample(std::static_pointer_cast<AudioFrame>(frame), format_); 67 0 : total_samples += resampled->getFrameSize(); 68 0 : buf.emplace_back(std::move(resampled)); 69 60 : }); 70 60 : DeviceParams dev; 71 60 : dev.input = fileName; 72 60 : dev.name = fileName; 73 : 74 60 : if (decoder->openInput(dev) < 0) 75 60 : throw AudioFileException("Unable to open file: " + fileName); 76 : 77 0 : if (decoder->setupAudio() < 0) 78 0 : throw AudioFileException("Decoder setup failed: " + fileName); 79 : 80 0 : while (decoder->decode() != MediaDemuxer::Status::EndOfFile) 81 : ; 82 : 83 0 : buffer_->nb_samples = total_samples; 84 0 : buffer_->format = format_.sampleFormat; 85 0 : buffer_->sample_rate = format_.sample_rate; 86 0 : av_channel_layout_default(&buffer_->ch_layout, format_.nb_channels); 87 0 : av_frame_get_buffer(buffer_.get(), 0); 88 : 89 0 : size_t outPos = 0; 90 0 : for (auto& frame : buf) { 91 0 : av_samples_copy(buffer_->data, frame->pointer()->data, outPos, 0, frame->getFrameSize(), format_.nb_channels, format_.sampleFormat); 92 0 : outPos += frame->getFrameSize(); 93 : } 94 0 : auto end = std::chrono::steady_clock::now(); 95 0 : auto audioDuration = std::chrono::duration<double>(total_samples/(double)format_.sample_rate); 96 0 : JAMI_LOG("AudioFile: loaded {} samples ({}) as {} in {} from {:s}", 97 : total_samples, audioDuration, format_.toString(), dht::print_duration(end-start), fileName); 98 360 : } 99 : 100 : } // namespace jami