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