Last active
November 6, 2019 09:44
-
-
Save tripulse/5d15b5a938e9209cbe5ae0b216fbfe49 to your computer and use it in GitHub Desktop.
A simple encoder which encodes a "sine wave" with arbitrary frequency, amplitude, phase and samplerate into a MPEG Layer III file using "libshine".
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
#include <cmath> | |
#include <istream> | |
#include <vector> | |
#include <shine/layer3.h> | |
namespace Generators { | |
/* PI constant of Archimedes. Used to generate | |
sinusoids in this context. */ | |
constexpr double PI = 3.14159265358979323846; | |
template<typename T> T maprange | |
(T x, T a, T b, T c, T d) { | |
return b + (x-a)*(d-c)/(b-a); | |
} | |
template<typename T> class SineWave { | |
public: | |
size_t sample_rate; | |
double phase; | |
double frequency; | |
std::vector<T> RenderSamples() | |
{ | |
/* Holds all the sample data with <T> sampleformat. */ | |
std::vector<T> _samples; | |
/* Resize the vector to the sample_rate because, | |
it's the samplerate is it's limit */ | |
_samples.resize(sample_rate); | |
/* Limits of integers in the sampleformat */ | |
std::numeric_limits<T> sfmt_lims; | |
for (auto s = 0; s < sample_rate; ++s) { | |
_samples[s] = (T)maprange<double>(std::sin((2.0f * PI * | |
((double)s / (double)sample_rate)) | |
- phase | |
), -1, +1, | |
sfmt_lims.lowest(), | |
sfmt_lims.max()); | |
} | |
return _samples; | |
} | |
}; | |
} | |
/* Directly wraps a type of `CharT` into a StreamBuf | |
NOTE: this doesn't implement any protection, so | |
segfaults can happen. */ | |
template<typename CharT, typename TraitsT = std::char_traits<CharT>> | |
class CharTypeIntoStreamBuf: public std::basic_streambuf<CharT, TraitsT> { | |
public: | |
CharTypeIntoStreamBuf(CharT *raw_data, size_t raw_size) { | |
this->setg(raw_data, raw_data, raw_data + raw_size); | |
} | |
}; | |
int main() { | |
shine_t encoder; | |
shine_config_t encoder_config; | |
/* Set the MP3 file to output with the default configuration. | |
The configuration of the PCM stream is only modified. */ | |
shine_set_config_mpeg_defaults(&encoder_config.mpeg); | |
/* Storing sinewave into streo doesn't make sense, | |
and generator also outputs MONO signals. */ | |
encoder_config.wave.channels = PCM_MONO; | |
/* Amount of PCM data in bytes the encoder would process | |
on each encode call. */ | |
int buffer_size = shine_samples_per_pass(encoder); | |
/* Create a source to node retrieve the data from | |
the sinusoid is rendered into PCM samples. */ | |
Generators::SineWave<int16_t> osc_sinewave; | |
osc_sinewave.frequency = 200.0f; | |
osc_sinewave.phase = 0.0f; | |
osc_sinewave.sample_rate = encoder_config.wave.samplerate; | |
/* Stores the PCM buffer with 16-bit singed samples. */ | |
std::vector<int16_t> pcm_buffer; | |
std::vector<int16_t> _tmp_samplebuf = osc_sinewave.RenderSamples(); | |
/* Generate a 30 second sinewave. And, render it to | |
16-bit signed samples. */ | |
constexpr uint32_t | |
DURATION = 30; | |
for(auto pos = 0U; pos < DURATION; ++pos) { | |
_tmp_samplebuf = osc_sinewave.RenderSamples(); | |
pcm_buffer.insert(pcm_buffer.end(), _tmp_samplebuf.begin(), _tmp_samplebuf.end()); | |
} | |
/* Wrap the samples into a filestream to read a certian | |
amount of data and automatically advance the read head. | |
*/ | |
CharTypeIntoStreamBuf<char> _pcm_bufio ((char*)pcm_buffer.data(), pcm_buffer.size()); | |
std::istream pcm_bufio (&_pcm_bufio); | |
char* pcm_reader_buffer = new char [buffer_size]; | |
while (1) | |
{ | |
int written_bytes = 0; | |
pcm_bufio.read(pcm_reader_buffer, buffer_size); | |
// Encode the RAW PCM buffer into MPEG Layer III format. | |
unsigned char* mpeg_out = | |
shine_encode_buffer_interleaved(encoder, (int16_t*)pcm_reader_buffer, &written_bytes); | |
/* Write encoded MPEG data into STDOUT. The data | |
could be piped to a program or be written to a file. */ | |
std::fwrite(mpeg_out, 1, written_bytes, stdout); | |
/* Break if everything is encoded. */ | |
if(written_bytes == 0) | |
break; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment