Line data Source code
1 : /* Copyright (C) 2004-2026 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/jami_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_ERROR("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 0 : }
110 :
111 : } // namespace jami
|