Line data Source code
1 : /*
2 : * Copyright (C) 2004-2024 Savoir-faire Linux Inc.
3 : *
4 : * Author: Yan Morin <yan.morin@savoirfairelinux.com>
5 : * Author: Adrien BĂ©raud <adrien.beraud@savoirfairelinux.com>
6 : *
7 : * Inspired by tonegenerator of
8 : * Laurielle Lea <laurielle.lea@savoirfairelinux.com> (2004)
9 : * Inspired by ringbuffer of Audacity Project
10 : *
11 : * This program is free software; you can redistribute it and/or modify
12 : * it under the terms of the GNU General Public License as published by
13 : * the Free Software Foundation; either version 3 of the License, or
14 : * (at your option) any later version.
15 : *
16 : * This program is distributed in the hope that it will be useful,
17 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 : * GNU General Public License for more details.
20 : *
21 : * You should have received a copy of the GNU General Public License
22 : * along with this program; if not, write to the Free Software
23 : * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 : */
25 : #include "tone.h"
26 : #include "logger.h"
27 : #include "ring_types.h"
28 : #include "string_utils.h"
29 :
30 : #include <vector>
31 : #include <cmath>
32 : #include <cstdlib>
33 :
34 : namespace jami {
35 :
36 405 : Tone::Tone(std::string_view definition, unsigned int sampleRate, AVSampleFormat sampleFormat)
37 405 : : AudioLoop(AudioFormat(sampleRate, 1, sampleFormat))
38 : {
39 405 : genBuffer(definition); // allocate memory with definition parameter
40 405 : }
41 :
42 : struct ParsedDefinition {
43 : unsigned total_samples;
44 : std::vector<std::tuple<unsigned, unsigned, unsigned>> frequencies;
45 : };
46 :
47 : ParsedDefinition
48 372 : parseDefinition(std::string_view definition, unsigned sampleRate)
49 : {
50 372 : ParsedDefinition parsed;
51 372 : parsed.total_samples = 0;
52 :
53 372 : std::string_view s; // portion of frequencyq
54 1023 : while (getline_full(definition, s, ',')) {
55 : // Sample string: "350+440" or "350+440/2000,244+655/2000"
56 : unsigned low, high, time;
57 : size_t count; // number of int for one sequence
58 :
59 : // The 1st frequency is before the first + or the /
60 651 : size_t pos_plus = s.find('+');
61 651 : size_t pos_slash = s.find('/');
62 651 : size_t len = s.length();
63 651 : size_t endfrequency = 0;
64 :
65 651 : if (pos_slash == std::string::npos) {
66 93 : time = 0;
67 93 : endfrequency = len;
68 : } else {
69 558 : time = to_int<unsigned>(s.substr(pos_slash + 1, len - pos_slash - 1), 0);
70 558 : endfrequency = pos_slash;
71 : }
72 :
73 : // without a plus = 1 frequency
74 651 : if (pos_plus == std::string::npos) {
75 279 : low = to_int<unsigned>(s.substr(0, endfrequency), 0);
76 279 : high = 0;
77 : } else {
78 372 : low = to_int<unsigned>(s.substr(0, pos_plus), 0);
79 372 : high = to_int<unsigned>(s.substr(pos_plus + 1, endfrequency - pos_plus - 1), 0);
80 : }
81 :
82 : // If there is time or if it's unlimited
83 651 : if (time == 0)
84 93 : count = sampleRate;
85 : else
86 558 : count = (sampleRate * time) / 1000;
87 :
88 651 : parsed.frequencies.emplace_back(low, high, count);
89 651 : parsed.total_samples += count;
90 : }
91 744 : return parsed;
92 0 : }
93 :
94 : void
95 405 : Tone::genBuffer(std::string_view definition)
96 : {
97 405 : if (definition.empty())
98 33 : return;
99 :
100 372 : auto [total_samples, frequencies] = parseDefinition(definition, format_.sample_rate);
101 372 : buffer_->nb_samples = total_samples;
102 372 : buffer_->format = format_.sampleFormat;
103 372 : buffer_->sample_rate = format_.sample_rate;
104 372 : av_channel_layout_default(&buffer_->ch_layout, format_.nb_channels);
105 372 : av_frame_get_buffer(buffer_.get(), 0);
106 :
107 372 : size_t outPos = 0;
108 1023 : for (auto& [low, high, count] : frequencies) {
109 651 : genSin(buffer_.get(), outPos, count, low, high);
110 651 : outPos += count;
111 : }
112 372 : }
113 :
114 : void
115 1179 : Tone::genSin(AVFrame* buffer, size_t outPos, unsigned nb_samples, unsigned lowFrequency, unsigned highFrequency)
116 : {
117 : static constexpr auto PI_2 = 3.141592653589793238462643383279502884L * 2.0L;
118 1179 : const double sr = (double) buffer->sample_rate;
119 1179 : const double dx_h = sr ? PI_2 * lowFrequency / sr : 0.0;
120 1179 : const double dx_l = sr ? PI_2 * highFrequency / sr : 0.0;
121 : static constexpr double DATA_AMPLITUDE_S16 = 2048;
122 : static constexpr double DATA_AMPLITUDE_FLT = 0.0625;
123 :
124 1179 : if (buffer->format == AV_SAMPLE_FMT_S16 || buffer->format == AV_SAMPLE_FMT_S16P) {
125 1179 : int16_t* ptr = ((int16_t*) buffer->data[0]) + outPos;
126 21097179 : for (size_t t = 0; t < nb_samples; t++) {
127 21096000 : ptr[t] = DATA_AMPLITUDE_S16 * (sin(t * dx_h) + sin(t * dx_l));
128 : }
129 1179 : } else if (buffer->format == AV_SAMPLE_FMT_FLT || buffer->format == AV_SAMPLE_FMT_FLTP) {
130 0 : float* ptr = ((float*) buffer->data[0]) + outPos;
131 0 : for (size_t t = 0; t < nb_samples; t++) {
132 0 : ptr[t] = (sin(t * dx_h) + sin(t * dx_l)) * DATA_AMPLITUDE_FLT;
133 : }
134 0 : } else {
135 0 : JAMI_ERROR("Unsupported sample format: {}", av_get_sample_fmt_name((AVSampleFormat)buffer->format));
136 : }
137 1179 : }
138 :
139 : } // namespace jami
|