-
Notifications
You must be signed in to change notification settings - Fork 63
Fix badly architectured audio file reads #1747
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
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -31,6 +31,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
#ifndef AUDIO_DATA_H | ||
#define AUDIO_DATA_H | ||
#include <memory> | ||
#include <vector> | ||
|
||
namespace Audio { | ||
|
||
|
@@ -40,15 +41,13 @@ struct AudioData { | |
, byteDepth{0} | ||
, numberOfChannels{0} | ||
, size{0} | ||
, rawSamples{nullptr} | ||
{} | ||
|
||
AudioData(int sampleRate, int byteDepth, int numberOfChannels, int size, const char* rawSamples) | ||
AudioData(int sampleRate, int byteDepth, int numberOfChannels, int newSize = 0 ) | ||
: sampleRate{sampleRate} | ||
, byteDepth{byteDepth} | ||
, numberOfChannels{numberOfChannels} | ||
, size{size} | ||
, rawSamples{rawSamples} | ||
, size( newSize ) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We shouldn't need |
||
{} | ||
|
||
AudioData(AudioData&& that) | ||
|
@@ -64,8 +63,8 @@ struct AudioData { | |
const int sampleRate; | ||
const int byteDepth; | ||
const int numberOfChannels; | ||
const int size; | ||
std::unique_ptr<const char[]> rawSamples; | ||
int size; | ||
std::vector<char> rawSamples; | ||
}; | ||
} // namespace Audio | ||
#endif |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -151,20 +151,43 @@ AudioData LoadOggCodec(std::string filename) | |
int sampleRate = oggInfo->rate; | ||
int numberOfChannels = oggInfo->channels; | ||
|
||
char buffer[4096]; | ||
/* The largest ogg file we have so far is res-soundtrack/sound/ui/heartbeat.ogg | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The 2 comments I made on OpusCodec apply here as well. Also when I logged it, heartbeat.ogg was 49M samples / 98MB. |
||
It uncompress to ~30mb. Use 64mb here just in case */ | ||
static constexpr uint32_t MAX_USED_FILE_SIZE = 64 * 1024 * 1024; | ||
static constexpr uint32_t MAX_READ_SIZE = 1 * 1024 * 1024; | ||
|
||
static char buffer[MAX_USED_FILE_SIZE]; | ||
uint32_t bufferPointer = 0; | ||
uint32_t rawSamplesPointer = 0; | ||
|
||
int bytesRead = 0; | ||
int bitStream = 0; | ||
|
||
std::vector<char> samples; | ||
AudioData out { sampleRate, sampleWidth, numberOfChannels }; | ||
|
||
while ((bytesRead = ov_read(vorbisFile.get(), buffer + bufferPointer, MAX_READ_SIZE, 0, sampleWidth, 1, &bitStream)) > 0) { | ||
bufferPointer += bytesRead; | ||
|
||
if ( MAX_USED_FILE_SIZE - bufferPointer <= MAX_READ_SIZE ) { | ||
out.rawSamples.resize( out.rawSamples.capacity() + bufferPointer ); | ||
std::copy_n( buffer, bufferPointer, out.rawSamples.data() ); | ||
|
||
while ((bytesRead = ov_read(vorbisFile.get(), buffer, 4096, 0, sampleWidth, 1, &bitStream)) > 0) { | ||
std::copy_n(buffer, bytesRead, std::back_inserter(samples)); | ||
rawSamplesPointer += bufferPointer; | ||
bufferPointer = 0; | ||
} | ||
} | ||
|
||
if ( bufferPointer ) { | ||
out.rawSamples.resize( out.rawSamples.capacity() + bufferPointer ); | ||
std::copy_n( buffer, bufferPointer, out.rawSamples.data() ); | ||
rawSamplesPointer += bufferPointer; | ||
} | ||
|
||
out.size = rawSamplesPointer; | ||
|
||
ov_clear(vorbisFile.get()); | ||
|
||
char* rawSamples = new char[samples.size()]; | ||
std::copy_n(samples.data(), samples.size(), rawSamples); | ||
return AudioData(sampleRate, sampleWidth, numberOfChannels, samples.size(), rawSamples); | ||
return out; | ||
} | ||
|
||
} //namespace Audio |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -145,25 +145,43 @@ AudioData LoadOpusCodec(std::string filename) | |
int sampleRate = 48000; | ||
int numberOfChannels = opusInfo->channel_count; | ||
|
||
// The buffer is big enough to hold 120ms worth of samples per channel | ||
opus_int16* buffer = new opus_int16[numberOfChannels * 5760]; | ||
/* The largest opus file we have so far is sound/thunder/weather.opus | ||
It uncompress to ~7mb. Use 64mb here just in case */ | ||
static constexpr uint32_t MAX_USED_FILE_SIZE = 64 * 1024 * 1024; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 64M of int16 is 128MB |
||
static constexpr uint32_t MAX_READ_SIZE = 1 * 1024 * 1024; | ||
|
||
static opus_int16 buffer[MAX_USED_FILE_SIZE]; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Having the extra 64M of memory be allocated permanently seems suboptimal. Why not reserve 64M in the vector and call There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How would that ever work? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Reserve MAX_USED_FILE_SIZE once at the beginning, then There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That is straight up worse than the static buffer since it generates an extra ~300mb because each sound's samples have to be >= MAX_READ_SIZE. Reducing MAX_READ_SIZE would defeat the point. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Additionally, with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yeah you'd have to
Hence But anyway, I thought of another solution that allows using the current version of the code mostly unchanged: use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
uint32_t bufferPointer = 0; | ||
uint32_t rawSamplesPointer = 0; | ||
|
||
int samplesPerChannelRead = 0; | ||
|
||
std::vector<opus_int16> samples; | ||
AudioData out { sampleRate, sampleWidth, numberOfChannels }; | ||
|
||
while ((samplesPerChannelRead = | ||
op_read(opusFile, buffer, sampleWidth * numberOfChannels * 5760, nullptr)) > 0) { | ||
std::copy_n(buffer, samplesPerChannelRead * numberOfChannels, std::back_inserter(samples)); | ||
op_read(opusFile, buffer + bufferPointer, sampleWidth * numberOfChannels * 5760, nullptr)) > 0) { | ||
bufferPointer += samplesPerChannelRead * numberOfChannels; | ||
|
||
if ( MAX_USED_FILE_SIZE - bufferPointer <= MAX_READ_SIZE ) { | ||
out.rawSamples.resize( out.rawSamples.capacity() + bufferPointer ); | ||
std::copy_n( buffer, bufferPointer, out.rawSamples.data() ); | ||
|
||
rawSamplesPointer += bufferPointer; | ||
bufferPointer = 0; | ||
} | ||
} | ||
|
||
delete[] buffer; | ||
op_free(opusFile); | ||
if ( bufferPointer ) { | ||
out.rawSamples.resize( out.rawSamples.capacity() + bufferPointer ); | ||
std::copy_n( buffer, bufferPointer, out.rawSamples.data() ); | ||
rawSamplesPointer += bufferPointer; | ||
} | ||
|
||
char* rawSamples = new char[sampleWidth * samples.size()]; | ||
std::copy_n(reinterpret_cast<char*>(samples.data()), sampleWidth * samples.size(), rawSamples); | ||
out.size = rawSamplesPointer; | ||
|
||
op_free(opusFile); | ||
|
||
return AudioData(sampleRate, sampleWidth, numberOfChannels, samples.size() * sampleWidth, | ||
rawSamples); | ||
return out; | ||
} | ||
|
||
} //namespace Audio |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -113,11 +113,11 @@ AudioData LoadWavCodec(std::string filename) | |
return AudioData(); | ||
} | ||
|
||
char* data = new char[size]; | ||
AudioData out { sampleRate, byteDepth, numChannels, size }; | ||
out.rawSamples.reserve( size ); | ||
std::copy_n(audioFile.data() + dataOffset + 8, size, out.rawSamples.data()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
||
std::copy_n(audioFile.data() + dataOffset + 8, size, data); | ||
|
||
return AudioData(sampleRate, byteDepth, numChannels, size, data); | ||
return out; | ||
} | ||
|
||
} // namespace Audio |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like this leaks
data
. Though the existing code is an unused and broken mess that freesdata
using the wrong type