Skip to content

Commit

Permalink
Merge pull request #1 from daschuer/1.12-shoutcast-fixes
Browse files Browse the repository at this point in the history
Merged SoundNetworkDevice to integrate shoutcast work better for future use
  • Loading branch information
illuusio committed Aug 27, 2015
2 parents afcf020 + 7a03a67 commit 3dc4480
Show file tree
Hide file tree
Showing 18 changed files with 778 additions and 112 deletions.
2 changes: 2 additions & 0 deletions build/depends.py
Original file line number Diff line number Diff line change
Expand Up @@ -892,6 +892,8 @@ def sources(self, build):
"playermanager.cpp",
"samplerbank.cpp",
"sounddevice.cpp",
"sounddevicenetwork.cpp",
"engine/sidechain/enginenetworkstream.cpp",
"soundmanager.cpp",
"soundmanagerconfig.cpp",
"soundmanagerutil.cpp",
Expand Down
4 changes: 3 additions & 1 deletion src/dlgprefsound.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,9 @@ void DlgPrefSound::slotApply() {
*/
void DlgPrefSound::initializePaths() {
foreach (AudioOutput out, m_pSoundManager->registeredOutputs()) {
addPath(out);
if (!out.isHidden()) {
addPath(out);
}
}
foreach (AudioInput in, m_pSoundManager->registeredInputs()) {
addPath(in);
Expand Down
33 changes: 24 additions & 9 deletions src/engine/enginemaster.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ EngineMaster::EngineMaster(ConfigObject<ConfigValue>* _config,
bool bRampingGain)
: m_pEngineEffectsManager(pEffectsManager ? pEffectsManager->getEngineEffectsManager() : NULL),
m_bRampingGain(bRampingGain),
m_ppSidechain(&m_pMaster),
m_masterGainOld(0.0),
m_headphoneMasterGainOld(0.0),
m_headphoneGainOld(1.0),
Expand Down Expand Up @@ -153,7 +154,7 @@ EngineMaster::EngineMaster(ConfigObject<ConfigValue>* _config,
}

// Starts a thread for recording and shoutcast
m_pSideChain = bEnableSidechain ? new EngineSideChain(_config) : NULL;
m_pEngineSideChain = bEnableSidechain ? new EngineSideChain(_config) : NULL;

// X-Fader Setup
m_pXFaderMode = new ControlPushButton(
Expand All @@ -179,6 +180,7 @@ EngineMaster::EngineMaster(ConfigObject<ConfigValue>* _config,
m_pMasterTalkoverMix = new ControlObject(ConfigKey(group, "talkover_mix"),
true, false, true); // persist = true
m_pHeadphoneEnabled = new ControlObject(ConfigKey(group, "headEnabled"));
m_pHeadphoneEnabled = new ControlObject(ConfigKey(group, "sidechainEnabled"));


// Note: the EQ Rack is set in EffectsManager::setupDefaults();
Expand All @@ -195,7 +197,7 @@ EngineMaster::~EngineMaster() {
delete m_pHeadGain;
delete m_pTalkoverDucking;
delete m_pVumeter;
delete m_pSideChain;
delete m_pEngineSideChain;
delete m_pMasterDelay;
delete m_pHeadDelay;

Expand Down Expand Up @@ -245,6 +247,10 @@ const CSAMPLE* EngineMaster::getHeadphoneBuffer() const {
return m_pHead;
}

const CSAMPLE* EngineMaster::getSidechainBuffer() const {
return *m_ppSidechain;
}

void EngineMaster::processChannels(int iBufferSize) {
m_activeBusChannels[EngineChannel::LEFT].clear();
m_activeBusChannels[EngineChannel::CENTER].clear();
Expand Down Expand Up @@ -513,22 +519,22 @@ void EngineMaster::process(const int iBufferSize) {

// Submit master samples to the side chain to do shoutcasting, recording,
// etc. (cpu intensive non-realtime tasks)
CSAMPLE* pSidechain = m_pMaster;
if (m_pSideChain != NULL) {
m_ppSidechain = &m_pMaster;
if (m_pEngineSideChain != NULL) {
if (m_pMasterTalkoverMix->toBool()) {
// Add Talkover to Sidechain output, re-use the talkover buffer
// Add Master and Talkover to Sidechain output, re-use the talkover buffer
SampleUtil::addWithGain(m_pTalkover,
m_pMaster, 1.0,
iBufferSize);
pSidechain = m_pTalkover;
m_ppSidechain = &m_pTalkover;
}
m_pSideChain->writeSamples(pSidechain, iBufferSize);
m_pEngineSideChain->writeSamples(*m_ppSidechain, iBufferSize);
}

// Update VU meter (it does not return anything). Needs to be here so that
// master balance and talkover is reflected in the VU meter.
if (m_pVumeter != NULL) {
m_pVumeter->process(pSidechain, iBufferSize);
m_pVumeter->process(*m_ppSidechain, iBufferSize);
}

// Add master to headphone with appropriate gain
Expand Down Expand Up @@ -675,6 +681,9 @@ const CSAMPLE* EngineMaster::buffer(AudioOutput output) const {
case AudioOutput::DECK:
return getDeckBuffer(output.getIndex());
break;
case AudioOutput::SIDECHAIN:
return getSidechainBuffer();
break;
default:
return NULL;
}
Expand All @@ -695,6 +704,9 @@ void EngineMaster::onOutputConnected(AudioOutput output) {
case AudioOutput::DECK:
// We don't track enabled decks.
break;
case AudioOutput::SIDECHAIN:
// We don't track enabled sidechain.
break;
default:
break;
}
Expand All @@ -707,14 +719,17 @@ void EngineMaster::onOutputDisconnected(AudioOutput output) {
// and recording/broadcasting as well
break;
case AudioOutput::HEADPHONES:
m_pHeadphoneEnabled->set(1.0);
m_pHeadphoneEnabled->set(0.0);
break;
case AudioOutput::BUS:
m_bBusOutputConnected[output.getIndex()] = false;
break;
case AudioOutput::DECK:
// We don't track enabled decks.
break;
case AudioOutput::SIDECHAIN:
// We don't track enabled sidechain.
break;
default:
break;
}
Expand Down
7 changes: 5 additions & 2 deletions src/engine/enginemaster.h
Original file line number Diff line number Diff line change
Expand Up @@ -129,9 +129,10 @@ class EngineMaster : public QObject, public AudioSource {
const CSAMPLE* getOutputBusBuffer(unsigned int i) const;
const CSAMPLE* getDeckBuffer(unsigned int i) const;
const CSAMPLE* getChannelBuffer(QString name) const;
const CSAMPLE* getSidechainBuffer() const;

EngineSideChain* getSideChain() const {
return m_pSideChain;
return m_pEngineSideChain;
}

struct ChannelInfo {
Expand Down Expand Up @@ -291,6 +292,8 @@ class EngineMaster : public QObject, public AudioSource {
CSAMPLE* m_pHead;
CSAMPLE* m_pTalkover;

CSAMPLE** m_ppSidechain; // points to master or to talkover buffer

EngineWorkerScheduler* m_pWorkerScheduler;
EngineSync* m_pMasterSync;

Expand All @@ -308,7 +311,7 @@ class EngineMaster : public QObject, public AudioSource {
EngineDelay* m_pHeadDelay;

EngineVuMeter* m_pVumeter;
EngineSideChain* m_pSideChain;
EngineSideChain* m_pEngineSideChain;

ControlPotmeter* m_pCrossfader;
ControlPotmeter* m_pHeadMix;
Expand Down
142 changes: 142 additions & 0 deletions src/engine/sidechain/enginenetworkstream.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
#ifdef __WINDOWS__
#include <windows.h>
#include <mmsystem.h>
#else
#include <sys/time.h>
#include <unistd.h>
#endif


#include "engine/sidechain/enginenetworkstream.h"

#include "sampleutil.h"

unsigned int kBufferFrames = 32768; // 743 ms @ 44100 Hz

EngineNetworkStream::EngineNetworkStream(double sampleRate,
int numOutputChannels,
int numInputChannels)
: m_pOutputFifo(NULL),
m_pInputFifo(NULL),
m_numOutputChannels(numOutputChannels),
m_numInputChannels(numInputChannels),
m_sampleRate(sampleRate),
m_streamStartTimeMs(-1),
m_streamFramesWritten(0),
m_streamFramesRead(0) {
if (numOutputChannels) {
m_pOutputFifo = new FIFO<CSAMPLE>(numOutputChannels * kBufferFrames);
}
if (numInputChannels) {
m_pInputFifo = new FIFO<CSAMPLE>(numInputChannels * kBufferFrames);
}
}

EngineNetworkStream::~EngineNetworkStream() {
if (m_streamStartTimeMs >= 0) {
stopStream();
}
delete m_pOutputFifo;
delete m_pInputFifo;
}

void EngineNetworkStream::startStream() {
m_streamStartTimeMs = getNetworkTimeMs();
m_streamFramesWritten = 0;
}

void EngineNetworkStream::stopStream() {
m_streamStartTimeMs = -1;
}

int EngineNetworkStream::getWriteExpected() {
return static_cast<int>(getStreamTimeFrames() - m_streamFramesWritten);
}

int EngineNetworkStream::getReadExpected() {
return static_cast<int>(getStreamTimeFrames() - m_streamFramesRead);
}

void EngineNetworkStream::write(const CSAMPLE* buffer, int frames) {
int writeAvailable = m_pOutputFifo->writeAvailable();
int writeRequired = frames * m_numOutputChannels;
if (writeAvailable < writeRequired) {
// Flush outdated frames to free space for writing
int readRequired = writeRequired - writeAvailable;
qDebug() << "EngineNetworkStream::write flushed" << readRequired
<< "samples";
m_pOutputFifo->flushReadData(readRequired);
writeAvailable = m_pOutputFifo->writeAvailable();
}
int copyCount = math_min(writeAvailable, writeRequired);
if (copyCount > 0) {
(void)m_pOutputFifo->write(buffer, copyCount);
}
m_streamFramesWritten += frames;
}

void EngineNetworkStream::writeSilence(int frames) {
int writeAvailable = m_pOutputFifo->writeAvailable();
int writeRequired = frames * m_numOutputChannels;
if (writeAvailable < writeRequired) {
// Flush outdated frames to free space for writing
int readRequired = writeRequired - writeAvailable;
qDebug() << "EngineNetworkStream::writeSilence flushed" << readRequired
<< "samples";
m_pOutputFifo->flushReadData(readRequired);
writeAvailable = m_pOutputFifo->writeAvailable();
}
int clearCount = math_min(writeAvailable, writeRequired);
if (clearCount > 0) {
CSAMPLE* dataPtr1;
ring_buffer_size_t size1;
CSAMPLE* dataPtr2;
ring_buffer_size_t size2;
(void)m_pOutputFifo->aquireWriteRegions(clearCount,
&dataPtr1, &size1, &dataPtr2, &size2);
SampleUtil::clear(dataPtr1,size1);
if (size2 > 0) {
SampleUtil::clear(dataPtr2,size2);
}
m_pOutputFifo->releaseWriteRegions(clearCount);
}
m_streamFramesWritten += frames;
}

void EngineNetworkStream::read(CSAMPLE* buffer, int frames) {
int readAvailable = m_pOutputFifo->readAvailable();
int readRequired = frames * m_numInputChannels;
int copyCount = math_min(readAvailable, readRequired);
if (copyCount > 0) {
(void)m_pOutputFifo->read(buffer, copyCount);
buffer += copyCount;
}
if (readAvailable < readRequired) {
// Fill missing Samples with silence
int silenceCount = readRequired - readAvailable;
qDebug() << "EngineNetworkStream::write flushed" << readRequired
<< "samples";
SampleUtil::clear(buffer, silenceCount);
}
}

qint64 EngineNetworkStream::getStreamTimeMs() {
return getNetworkTimeMs() - m_streamStartTimeMs;
}

qint64 EngineNetworkStream::getStreamTimeFrames() {
return static_cast<double>(getStreamTimeMs()) * m_sampleRate / 1000;
}

// static
qint64 EngineNetworkStream::getNetworkTimeMs() {
// This matches the GPL2 implementation found in
// https://github.com/codders/libshout/blob/a17fb84671d3732317b0353d7281cc47e2df6cf6/src/timing/timing.c
#ifdef __WINDOWS__
return timeGetTime();
#else
struct timeval mtv;
gettimeofday(&mtv, NULL);
return (qint64)(mtv.tv_sec) * 1000 + (qint64)(mtv.tv_usec) / 1000;
#endif
}
40 changes: 40 additions & 0 deletions src/engine/sidechain/enginenetworkstream.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#ifndef ENGINENETWORKSTREAM_H_
#define ENGINENETWORKSTREAM_H_

#include "util/types.h"
#include "util/fifo.h"

class EngineNetworkStream {
public:
EngineNetworkStream(double sampleRate,
int numOutputChannels,
int numInputChannels);
virtual ~EngineNetworkStream();

void startStream();
void stopStream();

int getWriteExpected();
int getReadExpected();

void write(const CSAMPLE* buffer, int frames);
void read(CSAMPLE* buffer, int frames);
void writeSilence(int frames);

qint64 getStreamTimeMs();
qint64 getStreamTimeFrames();

static qint64 getNetworkTimeMs();

private:
FIFO<CSAMPLE>* m_pOutputFifo;
FIFO<CSAMPLE>* m_pInputFifo;
int m_numOutputChannels;
int m_numInputChannels;
double m_sampleRate;
qint64 m_streamStartTimeMs;
qint64 m_streamFramesWritten;
qint64 m_streamFramesRead;
};

#endif /* ENGINENETWORKSTREAM_H_ */
3 changes: 2 additions & 1 deletion src/mixxx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,8 @@ void MixxxMainWindow::initalize(QApplication* pApp, const CmdlineArgs& args) {
m_pEffectsManager = new EffectsManager(this, m_pConfig);

// Starting the master (mixing of the channels and effects):
m_pEngine = new EngineMaster(m_pConfig, "[Master]", m_pEffectsManager, true, true);
m_pEngine = new EngineMaster(m_pConfig, "[Master]", m_pEffectsManager,
true, true);

// Create effect backends. We do this after creating EngineMaster to allow
// effect backends to refer to controls that are produced by the engine.
Expand Down
2 changes: 2 additions & 0 deletions src/playermanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ PlayerManager::PlayerManager(ConfigObject<ConfigValue>* pConfig,
m_pSoundManager->registerOutput(AudioOutput(AudioOutput::BUS, 0, 0, o),
m_pEngine);
}
m_pSoundManager->registerOutput(AudioOutput(AudioOutput::SIDECHAIN),
m_pEngine);
}

PlayerManager::~PlayerManager() {
Expand Down
6 changes: 1 addition & 5 deletions src/sounddevice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,6 @@ int SoundDevice::getNumOutputChannels() const {
return m_iNumOutputChannels;
}

void SoundDevice::setHostAPI(QString api) {
m_hostAPI = api;
}

void SoundDevice::setSampleRate(double sampleRate) {
if (sampleRate <= 0.0) {
// this is the default value used elsewhere in this file
Expand All @@ -72,7 +68,7 @@ void SoundDevice::setFramesPerBuffer(unsigned int framesPerBuffer) {
}

SoundDeviceError SoundDevice::addOutput(const AudioOutputBuffer &out) {
//Check if the output channels are already used
// Check if the output channels are already used
foreach (AudioOutputBuffer myOut, m_audioOutputs) {
if (out.channelsClash(myOut)) {
return SOUNDDEVICE_ERROR_DUPLICATE_OUTPUT_CHANNEL;
Expand Down
Loading

0 comments on commit 3dc4480

Please sign in to comment.