Skip to content

Commit

Permalink
Merge pull request #51 from daschuer/soundmanager_buffer
Browse files Browse the repository at this point in the history
get rid of hashtable look up for Soundmanager buffers
  • Loading branch information
daschuer committed Jul 22, 2013
2 parents ba6adc8 + 140b598 commit 831a145
Show file tree
Hide file tree
Showing 6 changed files with 79 additions and 71 deletions.
2 changes: 1 addition & 1 deletion src/engine/enginedeck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ void EngineDeck::receiveBuffer(AudioInput input, const short* pBuffer, unsigned
qWarning() << "EnginePassthrough got greater than stereo input. Not currently handled.";
}

const int samplesToWrite = nFrames * iChannels;
const unsigned int samplesToWrite = nFrames * iChannels;

// TODO(rryan) do we need to verify the input is the one we asked for? Oh well.
unsigned int samplesWritten = m_sampleBuffer.write(m_pConversionBuffer, samplesToWrite);
Expand Down
6 changes: 3 additions & 3 deletions src/sounddevice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,9 @@ void SoundDevice::setFramesPerBuffer(unsigned int framesPerBuffer) {
m_framesPerBuffer = framesPerBuffer;
}

SoundDeviceError SoundDevice::addOutput(const AudioOutput &out) {
SoundDeviceError SoundDevice::addOutput(const AudioOutputBuffer &out) {
//Check if the output channels are already used
foreach (AudioOutput myOut, m_audioOutputs) {
foreach (AudioOutputBuffer myOut, m_audioOutputs) {
if (out.channelsClash(myOut)) {
return SOUNDDEVICE_ERROR_DUPLICATE_OUTPUT_CHANNEL;
}
Expand All @@ -101,7 +101,7 @@ void SoundDevice::clearOutputs() {
m_audioOutputs.clear();
}

SoundDeviceError SoundDevice::addInput(const AudioInput &in) {
SoundDeviceError SoundDevice::addInput(const AudioInputBuffer &in) {
// DON'T check if the input channels are already used, there's no reason
// we can't send the same inputted samples to different places in mixxx.
// -- bkgood 20101108
Expand Down
8 changes: 4 additions & 4 deletions src/sounddevice.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ class SoundDevice {
virtual unsigned int getDefaultSampleRate() const = 0;
int getNumOutputChannels() const;
int getNumInputChannels() const;
SoundDeviceError addOutput(const AudioOutput &out);
SoundDeviceError addInput(const AudioInput &in);
SoundDeviceError addOutput(const AudioOutputBuffer& out);
SoundDeviceError addInput(const AudioInputBuffer& in);
void clearOutputs();
void clearInputs();
bool operator==(const SoundDevice &other) const;
Expand All @@ -74,8 +74,8 @@ class SoundDevice {
// The name of the audio API used by this device.
QString m_hostAPI;
unsigned int m_framesPerBuffer;
QList<AudioOutput> m_audioOutputs;
QList<AudioInput> m_audioInputs;
QList<AudioOutputBuffer> m_audioOutputs;
QList<AudioInputBuffer> m_audioInputs;
};

#endif
94 changes: 38 additions & 56 deletions src/soundmanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -147,22 +147,19 @@ void SoundManager::closeDevices() {
m_pClkRefDevice = NULL;
//m_requestBufferMutex.unlock();

m_outputBuffers.clear(); // anti-cruft (safe because outputs only have
// pointers to memory owned by EngineMaster)

foreach (AudioInput in, m_inputBuffers.keys()) {
// Need to tell all registered AudioDestinations for this AudioInput
// that the input was disconnected.
for (QHash<AudioInput, AudioDestination*>::const_iterator it =
m_registeredDestinations.find(in);
it != m_registeredDestinations.end() && it.key() == in; ++it) {
it != m_registeredDestinations.end() && it.key() == in; ++it) {
it.value()->onInputDisconnected(in);
}

short *buffer = m_inputBuffers[in];
SAMPLE *buffer = m_inputBuffers.value(in);
if (buffer != NULL) {
delete [] buffer;
m_inputBuffers[in] = buffer = NULL;
m_inputBuffers.insert(in, NULL);
}
}
m_inputBuffers.clear();
Expand Down Expand Up @@ -291,14 +288,17 @@ int SoundManager::setupDevices() {
m_pErrorDevice = device;
foreach (AudioInput in, m_config.getInputs().values(device->getInternalName())) {
isInput = true;
err = device->addInput(in);
if (err != OK) goto closeAndError;
if (!m_inputBuffers.contains(in)) {
// TODO(bkgood) look into allocating this with the frames per
// buffer value from SMConfig
m_inputBuffers[in] = new short[MAX_BUFFER_LEN];
// TODO(bkgood) look into allocating this with the frames per
// buffer value from SMConfig
AudioInputBuffer aib(in, new SAMPLE[MAX_BUFFER_LEN]);
err = device->addInput(aib);
if (err != OK) {
delete [] aib.getBuffer();
goto closeAndError;
}

m_inputBuffers.insert(in, aib.getBuffer());

// Check if any AudioDestination is registered for this AudioInput,
// and call the onInputConnected method.
for (QHash<AudioInput, AudioDestination*>::const_iterator it =
Expand All @@ -311,13 +311,14 @@ int SoundManager::setupDevices() {
isOutput = true;
// following keeps us from asking for a channel buffer EngineMaster
// doesn't have -- bkgood
if (m_registeredSources[out]->buffer(out) == NULL) {
const CSAMPLE* pBuffer = m_registeredSources.value(out)->buffer(out);
if (pBuffer == NULL) {
qDebug() << "AudioSource returned null for" << out.getString();
continue;
}
err = device->addOutput(out);
AudioOutputBuffer aob(out, pBuffer);
err = device->addOutput(aob);
if (err != OK) goto closeAndError;
m_outputBuffers[out] = m_registeredSources[out]->buffer(out);
if (out.getType() == AudioOutput::MASTER) {
m_pClkRefDevice = device;
} else if (out.getType() == AudioOutput::DECK
Expand Down Expand Up @@ -421,7 +422,7 @@ void SoundManager::checkConfig() {
}

void SoundManager::requestBuffer(
const QList<AudioOutput>& outputs, float* outputBuffer,
const QList<AudioOutputBuffer>& outputs, float* outputBuffer,
const unsigned long iFramesPerBuffer, const unsigned int iFrameSize,
SoundDevice* device, double streamTime /* = 0 */) {
Q_UNUSED(streamTime);
Expand All @@ -448,21 +449,12 @@ void SoundManager::requestBuffer(

static const float SHRT_CONVERSION_FACTOR = 1.0f/SHRT_MAX;

for (QList<AudioOutput>::const_iterator i = outputs.begin(),
for (QList<AudioOutputBuffer>::const_iterator i = outputs.begin(),
e = outputs.end(); i != e; ++i) {
const AudioOutput& out = *i;

QHash<AudioOutput, const CSAMPLE*>::const_iterator it =
m_outputBuffers.find(out);
if (it == m_outputBuffers.end()) {
continue;
}

const CSAMPLE* input = it.value();
if (input == NULL) {
continue;
}
const AudioOutputBuffer& out = *i;

// buffer is always !NULL
const CSAMPLE* pAudioOutputBuffer = out.getBuffer();
const ChannelGroup outChans = out.getChannelGroup();
const int iChannelCount = outChans.getChannelCount();
const int iChannelBase = outChans.getChannelBase();
Expand All @@ -476,7 +468,7 @@ void SoundManager::requestBuffer(
// this will make sure a sample from each channel is copied
for (int iChannel = 0; iChannel < iChannelCount; ++iChannel) {
outputBuffer[iFrameBase + iChannelBase + iChannel] =
input[iLocalFrameBase + iChannel] * SHRT_CONVERSION_FACTOR;
pAudioOutputBuffer[iLocalFrameBase + iChannel] * SHRT_CONVERSION_FACTOR;

// Input audio pass-through (useful for debugging)
//if (in)
Expand All @@ -487,7 +479,7 @@ void SoundManager::requestBuffer(
}
}

void SoundManager::pushBuffer(const QList<AudioInput>& inputs, short * inputBuffer,
void SoundManager::pushBuffer(const QList<AudioInputBuffer>& inputs, short* inputBuffer,
const unsigned long iFramesPerBuffer, const unsigned int iFrameSize) {
//This function is called a *lot* and is a big source of CPU usage.
//It needs to be very fast.
Expand All @@ -507,19 +499,19 @@ void SoundManager::pushBuffer(const QList<AudioInput>& inputs, short * inputBuff
// TODO(rryan): If we have two mono channels we still have to deinterleave.
// TODO(XXX): Is it worth hard-coding the iFrameSize == 1 case for microphones?
if (iFrameSize == 2) {
for (QList<AudioInput>::const_iterator i = inputs.begin(),
for (QList<AudioInputBuffer>::const_iterator i = inputs.begin(),
e = inputs.end(); i != e; ++i) {
const AudioInput& in = *i;
memcpy(m_inputBuffers[in], inputBuffer,
const AudioInputBuffer& in = *i;
memcpy(in.getBuffer(), inputBuffer,
sizeof(*inputBuffer) * iFrameSize * iFramesPerBuffer);
}
} else { //More than two channels of input (iFrameSize > 2)
// Do crazy deinterleaving of the audio into the correct m_inputBuffers.

for (QList<AudioInput>::const_iterator i = inputs.begin(),
for (QList<AudioInputBuffer>::const_iterator i = inputs.begin(),
e = inputs.end(); i != e; ++i) {
const AudioInput& in = *i;
short* pInputBuffer = m_inputBuffers[in];
const AudioInputBuffer& in = *i;
short* pInputBuffer = in.getBuffer();
ChannelGroup chanGroup = in.getChannelGroup();
int iChannelCount = chanGroup.getChannelCount();
int iChannelBase = chanGroup.getChannelBase();
Expand All @@ -542,26 +534,16 @@ void SoundManager::pushBuffer(const QList<AudioInput>& inputs, short * inputBuff
}
}

if (inputBuffer) {
for (QList<AudioInput>::ConstIterator i = inputs.begin(),
e = inputs.end(); i != e; ++i) {
const AudioInput& in = *i;

QHash<AudioInput, short*>::const_iterator input_it =
m_inputBuffers.find(in);

// Sanity check.
if (input_it == m_inputBuffers.end()) {
continue;
}
for (QList<AudioInputBuffer>::ConstIterator i = inputs.begin(),
e = inputs.end(); i != e; ++i) {
const AudioInputBuffer& in = *i;

short* pInputBuffer = input_it.value();
short* pInputBuffer = in.getBuffer();

for (QHash<AudioInput, AudioDestination*>::const_iterator it =
m_registeredDestinations.find(in);
it != m_registeredDestinations.end() && it.key() == in; ++it) {
it.value()->receiveBuffer(in, pInputBuffer, iFramesPerBuffer);
}
for (QHash<AudioInput, AudioDestination*>::const_iterator it =
m_registeredDestinations.find(in);
it != m_registeredDestinations.end() && it.key() == in; ++it) {
it.value()->receiveBuffer(in, pInputBuffer, iFramesPerBuffer);
}
}
}
Expand All @@ -570,7 +552,7 @@ void SoundManager::registerOutput(AudioOutput output, const AudioSource *src) {
if (m_registeredSources.contains(output)) {
qDebug() << "WARNING: AudioOutput already registered!";
}
m_registeredSources[output] = src;
m_registeredSources.insert(output, src);
emit(outputRegistered(output, src));
}

Expand Down
5 changes: 2 additions & 3 deletions src/soundmanager.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,13 +87,13 @@ class SoundManager : public QObject {

// Requests a buffer in the proper format, if we're prepared to give one.
void requestBuffer(
const QList<AudioOutput>& outputs, float* outputBuffer,
const QList<AudioOutputBuffer>& outputs, float* outputBuffer,
const unsigned long iFramesPerBuffer, const unsigned int iFrameSize,
SoundDevice *device, double streamTime = 0);

// Used by SoundDevices to "push" any audio from their inputs that they have
// into the mixing engine.
void pushBuffer(const QList<AudioInput>& inputs, short *inputBuffer,
void pushBuffer(const QList<AudioInputBuffer>& inputs, short *inputBuffer,
const unsigned long iFramesPerBuffer, const unsigned int iFrameSize);

void registerOutput(AudioOutput output, const AudioSource *src);
Expand All @@ -118,7 +118,6 @@ class SoundManager : public QObject {
#endif
QList<SoundDevice*> m_devices;
QList<unsigned int> m_samplerates;
QHash<AudioOutput, const CSAMPLE*> m_outputBuffers;
QHash<AudioInput, short*> m_inputBuffers;
// Clock reference, used to make sure the same device triggers buffer
// refresh every $latency-ms period
Expand Down
35 changes: 31 additions & 4 deletions src/soundmanagerutil.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,35 +89,62 @@ class AudioPath {
* channels on an audio interface.
*/
class AudioOutput : public AudioPath {
public:
public:
AudioOutput(AudioPathType type = INVALID, unsigned char channelBase = 0,
unsigned char index = 0);
virtual ~AudioOutput();
QDomElement toXML(QDomElement *element) const;
static AudioOutput fromXML(const QDomElement &xml);
static QList<AudioPathType> getSupportedTypes();
protected:
protected:
void setType(AudioPathType type);
};

// This class is required to add the buffer, without changing the hash used as ID
class AudioOutputBuffer : public AudioOutput {
public:
AudioOutputBuffer(const AudioOutput& out, const CSAMPLE* pBuffer)
: AudioOutput(out),
m_pBuffer(pBuffer) {

};
inline const CSAMPLE* getBuffer() const { return m_pBuffer; };
private:
const CSAMPLE* m_pBuffer;
};

/**
* @class AudioInput
* @extends AudioPath
* @brief A source of audio at a group of channels on an audio interface
* that is be processed in Mixxx.
*/
class AudioInput : public AudioPath {
public:
public:
AudioInput(AudioPathType type = INVALID, unsigned char channelBase = 0,
unsigned char index = 0);
virtual ~AudioInput();
QDomElement toXML(QDomElement *element) const;
static AudioInput fromXML(const QDomElement &xml);
static QList<AudioPathType> getSupportedTypes();
protected:
protected:
void setType(AudioPathType type);
};

// This class is required to add the buffer, without changing the hash used as ID
class AudioInputBuffer : public AudioInput {
public:
AudioInputBuffer(const AudioInput& id, SAMPLE* pBuffer)
: AudioInput(id),
m_pBuffer(pBuffer) {

};
inline SAMPLE* getBuffer() const { return m_pBuffer; };
private:
SAMPLE* m_pBuffer;
};


class AudioSource {
public:
virtual const CSAMPLE* buffer(AudioOutput output) const = 0;
Expand Down

0 comments on commit 831a145

Please sign in to comment.