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