-
Notifications
You must be signed in to change notification settings - Fork 87
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Could You provide example of decoding audio file to buffer compatible with SFML:: #146
Comments
Hi, will try to prepare in near two days. Installing avcpp via vcpkg is a good case :) |
Look into attached sample: sfml-audio-pcm16.zip |
Thanks a lot for the example!
BTW. About vcpkg: when we install:
PS: I'm creating article for site cpp0x.pl (article in Polish) where I'm trying to find IMO best way how to play music from C++ for programmers who don't know much about playing musics, codecs, etc. (article mostly for students). Can I paste Your code in the article? |
vcpkg support was not introduced by me. I have no time to dive deeply into details of the vcpkg work and ports Features supports (and its transitivity).
Sure! I suggest only remove debug output just to reduce lines of code. |
I've created issue for VCPKG microsoft/vcpkg#43345 - as I remember community is adding this fast.
#include <iostream>
#include <vector>
#include <thread>
#include <chrono>
#include <filesystem>
#include <optional>
#include <SFML/Audio.hpp>
#include "avcpp/av.h"
#include "avcpp/format.h"
#include "avcpp/formatcontext.h"
#include "avcpp/codec.h"
#include "avcpp/codeccontext.h"
#include "avcpp/audioresampler.h"
struct AudioFileData
{
int channels;
int sampleRate;
std::vector < sf::Int16 > pcmAudioBuffer;
bool successReading;
};
AudioFileData decodeAudioWithAvcpp(const std::filesystem::path& filePath);
void playSound( const AudioFileData & audioFileData );
int main( int argc, char * * argv ) {
if( argc < 2 ) {
std::cerr << "Usage: " << argv[ 0 ] << " <audio file>" << std::endl;
return 1;
}
const std::string filePath = argv[ 1 ];
AudioFileData audioData = decodeAudioWithAvcpp( filePath );
if( audioData.pcmAudioBuffer.empty() )
std::cerr << "Failed to decode or play audio from file " << filePath << std::endl;
else
playSound( audioData );
}
AudioFileData decodeAudioWithAvcpp(const std::filesystem::path& filePath)
{
/// Reference to https://github.com/h4tr3d/avcpp/blob/master/example/api2-samples/api2-decode-audio.cpp
std::error_code ec;
std::optional<size_t> audioStream;
av::init();
av::setFFmpegLoggingLevel(AV_LOG_DEBUG);
av::FormatContext ictx;
av::Stream ast;
av::AudioDecoderContext adec;
ictx.openInput(filePath); // exception on fail
ictx.findStreamInfo(); // process input and collect streams information
/// Lookup first audio stream
for (auto i = 0u; i < ictx.streamsCount(); ++i) {
auto st = ictx.stream(i);
if (st.isAudio()) {
audioStream = i;
ast = st;
break;
}
}
if (ast.isNull()) {
std::cerr << "Audio stream not found\n";
exit(1);
}
if (ast.isValid()) {
adec = av::AudioDecoderContext(ast);
adec.open(av::Codec()); // throw on error
}
/// Resample: required to convert into proper SFML format. SFML supports only PCM_S16 samples
av::AudioResampler resampler(adec.channelLayout(), adec.sampleRate(), AV_SAMPLE_FMT_S16,
adec.channelLayout(), adec.sampleRate(), adec.sampleFormat());
/// Output data
AudioFileData audioFileData;
audioFileData.sampleRate = adec.sampleRate();
audioFileData.channels = adec.channels();
/// Process input
while (true) {
auto pkt = ictx.readPacket(ec);
// handle in proper way, like EGAIN
if (ec) {
std::clog << "Packet reading error: " << ec << ", " << ec.message() << std::endl;
break;
}
// EOF
if (!pkt) {
break;
}
// Skip non-audio packets
if (pkt.streamIndex() != *audioStream)
continue;
std::clog << "Read packet: " << pkt.pts() << " / " << pkt.pts().seconds() << " / " << pkt.timeBase() << " / st: " << pkt.streamIndex() << std::endl;
auto samples = adec.decode(pkt);
std::clog << " Samples [in]: " << samples.samplesCount()
<< ", ch: " << samples.channelsCount()
<< ", freq: " << samples.sampleRate()
<< ", name: " << samples.channelsLayoutString()
<< ", pts: " << samples.pts().seconds()
<< ", ref=" << samples.isReferenced() << ":" << samples.refCount()
<< std::endl;
// skip empty frames if any
if (!samples)
continue;
// Resample it
resampler.push(samples);
/// Pop resampler data: in common way, single input samples frame can produce multiple output. Or empty, if single output frame
/// requires more input one.
/// Note, we can only push data here and process it at once on the Flushing. But too much more memory is needed.
auto ouSamples = av::AudioSamples::null();
while ((ouSamples = resampler.pop(samples.samplesCount(), ec))) {
std::clog << " Samples [ou]: " << ouSamples.samplesCount()
<< ", ch: " << ouSamples.channelsCount()
<< ", freq: " << ouSamples.sampleRate()
<< ", name: " << ouSamples.channelsLayoutString()
<< ", pts: " << ouSamples.pts().seconds()
<< ", ref=" << ouSamples.isReferenced() << ":" << ouSamples.refCount()
<< ", size=" << ouSamples.size()
<< std::endl;
// UB due to strict aliasing...
audioFileData.pcmAudioBuffer.insert(std::end(audioFileData.pcmAudioBuffer),
(sf::Int16 *) ouSamples.data(),
(sf::Int16 *) ouSamples.data() + ouSamples.size() / sizeof( sf::Int16 ) );
}
}
/// Flush resampler
{
while (resampler.delay()) {
auto ouSamples = resampler.pop(0, ec); // request all remain data at once
if (ec) {
std::clog << "Resampling status: " << ec << ", text: " << ec.message() << std::endl;
break;
} else if (!ouSamples) {
break;
} else {
std::clog << " Samples [ou]: " << ouSamples.samplesCount()
<< ", ch: " << ouSamples.channelsCount()
<< ", freq: " << ouSamples.sampleRate()
<< ", name: " << ouSamples.channelsLayoutString()
<< ", pts: " << ouSamples.pts().seconds()
<< ", ref=" << ouSamples.isReferenced() << ":" << ouSamples.refCount()
<< ", size=" << ouSamples.size()
<< std::endl;
// UB due to strict aliasing...
audioFileData.pcmAudioBuffer.insert(std::end(audioFileData.pcmAudioBuffer),
(sf::Int16 *) ouSamples.data(),
(sf::Int16 *) ouSamples.data() + ouSamples.size() / sizeof( sf::Int16 ) );
}
}
}
return audioFileData;
}
void playSound( const AudioFileData & audioFileData ) {
sf::SoundBuffer soundBuffer;
if( !soundBuffer.loadFromSamples( audioFileData.pcmAudioBuffer.data(), audioFileData.pcmAudioBuffer.size(), audioFileData.channels, audioFileData.sampleRate ) ) {
std::cerr << "Error: Could not load buffer for playback" << std::endl;
return;
}
sf::Sound sound;
sound.setBuffer( soundBuffer );
sound.play();
std::cout << "Playing file, duration: " << soundBuffer.getDuration().asSeconds() << " seconds" << std::endl;
while( sound.getStatus() == sf::Sound::Playing ) {
std::this_thread::sleep_for( std::chrono::milliseconds( 500 ) );
}
std::cout << "Playback finished." << std::endl;
} I'll let You know when my article will be published here: https://cpp0x.pl/ - it will be in Polish, but hopefully many people will found out about You great work! |
I have example how to do something with FFMPEG and SFML - how to play almost any audio/video. I have code which is decoding audio/video file with ffmpeg to array then the code is sending the array to SFML which is playing the music.
I want to show to students really simple code, which does not require knowledge about audio in programming, that is why I want to change my code from using ffmpeg directly to use AVcpp.
I found example: https://github.com/h4tr3d/avcpp/blob/master/example/api2-samples/api2-decode-audio.cpp but I can't adapt its to work with my code.
Here is code which is playing music with ffmpeg + SFML:
I know that SFML can play few audio formats, but I want to play various formats. Just SFML is IMO easiest code to play audio file:
Fastest way to install with vcpkg
Then sample CMakeLists.txt:
As I know to install avcpp with vcpkg we need just:
vcpkg install avcpp
but probably You have better way to install the library:D.
BTW. Really good job with the library avcpp. I'd love to use its, but I need help.
The text was updated successfully, but these errors were encountered: