aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/plugins/Effect/Effect.pro6
-rw-r--r--src/plugins/Effect/srconverter/srconverter.cpp8
-rw-r--r--src/plugins/Effect/srconverter/srconverter.h4
-rw-r--r--src/plugins/Input/Input.pro28
-rw-r--r--src/plugins/Input/flac/decoder_flac.cpp77
-rw-r--r--src/plugins/Input/flac/decoder_flac.h1
-rw-r--r--src/plugins/Input/mad/decoder_mad.cpp7
-rw-r--r--src/plugins/Output/Output.pro10
-rw-r--r--src/plugins/Output/alsa/outputalsa.cpp44
-rw-r--r--src/plugins/Output/alsa/outputalsa.h9
-rw-r--r--src/qmmp/audioconverter.cpp22
-rw-r--r--src/qmmp/audioconverter_p.h4
-rw-r--r--src/qmmp/audioparameters.cpp20
-rw-r--r--src/qmmp/audioparameters.h10
-rw-r--r--src/qmmp/channelconverter.cpp119
-rw-r--r--src/qmmp/channelconverter_p.h47
-rw-r--r--src/qmmp/channelmap.cpp51
-rw-r--r--src/qmmp/channelmap.h5
-rw-r--r--src/qmmp/decoder.cpp6
-rw-r--r--src/qmmp/decoder.h9
-rw-r--r--src/qmmp/effect.cpp22
-rw-r--r--src/qmmp/effect.h20
-rw-r--r--src/qmmp/output.cpp18
-rw-r--r--src/qmmp/output.h17
-rw-r--r--src/qmmp/outputwriter.cpp112
-rw-r--r--src/qmmp/outputwriter_p.h16
-rw-r--r--src/qmmp/qmmp.h21
-rw-r--r--src/qmmp/qmmp.pro6
-rw-r--r--src/qmmp/qmmpaudioengine.cpp35
-rw-r--r--src/qmmp/recycler.cpp5
-rw-r--r--src/qmmp/recycler_p.h5
31 files changed, 588 insertions, 176 deletions
diff --git a/src/plugins/Effect/Effect.pro b/src/plugins/Effect/Effect.pro
index 336647a4b..b073f08f3 100644
--- a/src/plugins/Effect/Effect.pro
+++ b/src/plugins/Effect/Effect.pro
@@ -1,15 +1,15 @@
include (../../../qmmp.pri)
TEMPLATE = subdirs
-SUBDIRS += crossfade stereo
+#SUBDIRS += crossfade stereo
contains(CONFIG, BS2B_PLUGIN){
-SUBDIRS += bs2b
+#SUBDIRS += bs2b
}
unix {
SUBDIRS += srconverter
contains(CONFIG, LADSPA_PLUGIN){
- SUBDIRS += ladspa
+# SUBDIRS += ladspa
}
}
diff --git a/src/plugins/Effect/srconverter/srconverter.cpp b/src/plugins/Effect/srconverter/srconverter.cpp
index fcd77bc04..324dbb051 100644
--- a/src/plugins/Effect/srconverter/srconverter.cpp
+++ b/src/plugins/Effect/srconverter/srconverter.cpp
@@ -1,5 +1,5 @@
/***************************************************************************
- * Copyright (C) 2007-2013 by Ilya Kotov *
+ * Copyright (C) 2007-2014 by Ilya Kotov *
* forkotov02@hotmail.ru *
* *
* This program is free software; you can redistribute it and/or modify *
@@ -115,12 +115,12 @@ void SRConverter::applyEffect(Buffer *b)
}
}
-void SRConverter::configure(quint32 freq, int chan, Qmmp::AudioFormat format)
+void SRConverter::configure(quint32 freq, ChannelMap map, Qmmp::AudioFormat format)
{
freeSRC();
if(freq != m_overSamplingFs && format != Qmmp::PCM_S8)
{
- m_src_state = src_new(m_converter_type, chan, &m_srcError);
+ m_src_state = src_new(m_converter_type, map.count(), &m_srcError);
if (m_src_state)
{
m_src_data.src_ratio = (float)m_overSamplingFs/(float)freq;
@@ -129,7 +129,7 @@ void SRConverter::configure(quint32 freq, int chan, Qmmp::AudioFormat format)
else
qDebug("SRConverter: src_new(): %s", src_strerror(m_srcError));
}
- Effect::configure(m_overSamplingFs, chan, format);
+ Effect::configure(m_overSamplingFs, map, format);
m_sz = audioParameters().sampleSize();
}
diff --git a/src/plugins/Effect/srconverter/srconverter.h b/src/plugins/Effect/srconverter/srconverter.h
index f25ebc491..b214c866f 100644
--- a/src/plugins/Effect/srconverter/srconverter.h
+++ b/src/plugins/Effect/srconverter/srconverter.h
@@ -1,5 +1,5 @@
/***************************************************************************
- * Copyright (C) 2007-2013 by Ilya Kotov *
+ * Copyright (C) 2007-2014 by Ilya Kotov *
* forkotov02@hotmail.ru *
* *
* This program is free software; you can redistribute it and/or modify *
@@ -34,7 +34,7 @@ public:
virtual ~SRConverter();
void applyEffect(Buffer *b);
- void configure(quint32 freq, int chan, Qmmp::AudioFormat format);
+ void configure(quint32 freq, ChannelMap map, Qmmp::AudioFormat format);
private:
void freeSRC();
diff --git a/src/plugins/Input/Input.pro b/src/plugins/Input/Input.pro
index 9b00864e6..0149aa949 100644
--- a/src/plugins/Input/Input.pro
+++ b/src/plugins/Input/Input.pro
@@ -1,53 +1,53 @@
include(../../../qmmp.pri)
-SUBDIRS += mad cue vorbis sndfile wavpack
TEMPLATE = subdirs
+SUBDIRS += mad #cue vorbis sndfile wavpack
contains(CONFIG, FLAC_PLUGIN){
SUBDIRS += flac
}
contains(CONFIG, MUSEPACK_PLUGIN){
- SUBDIRS += mpc
+# SUBDIRS += mpc
}
contains(CONFIG, MODPLUG_PLUGIN){
- SUBDIRS += modplug
+# SUBDIRS += modplug
}
contains(CONFIG, FFMPEG_PLUGIN){
- contains(CONFIG, FFMPEG_LEGACY){
- SUBDIRS += ffmpeg_legacy
- }else{
- SUBDIRS += ffmpeg
- }
+# contains(CONFIG, FFMPEG_LEGACY){
+# SUBDIRS += ffmpeg_legacy
+# }else{
+# SUBDIRS += ffmpeg
+# }
}
contains(CONFIG, GME_PLUGIN){
- SUBDIRS += gme
+# SUBDIRS += gme
}
contains(CONFIG, OPUS_PLUGIN){
- SUBDIRS += opus
+# SUBDIRS += opus
}
contains(CONFIG, CDAUDIO_PLUGIN){
- SUBDIRS += cdaudio
+# SUBDIRS += cdaudio
}
contains(CONFIG, SID_PLUGIN){
- SUBDIRS += sid
+# SUBDIRS += sid
}
unix{
contains(CONFIG, AAC_PLUGIN){
- SUBDIRS += aac
+# SUBDIRS += aac
}
contains(CONFIG, WILDMIDI_PLUGIN){
- SUBDIRS += wildmidi
+# SUBDIRS += wildmidi
}
diff --git a/src/plugins/Input/flac/decoder_flac.cpp b/src/plugins/Input/flac/decoder_flac.cpp
index acd76f159..4dfca6839 100644
--- a/src/plugins/Input/flac/decoder_flac.cpp
+++ b/src/plugins/Input/flac/decoder_flac.cpp
@@ -393,17 +393,25 @@ bool DecoderFLAC::initialize()
data()->ok = 0;
return false;
}
+ ChannelMap channel_map = findChannelMap(data()->channels);
+
+ if(channel_map.isEmpty())
+ {
+ qWarning("DecoderFLAC: unsupported number of channels: %d", data()->channels);
+ return false;
+ }
+
switch(data()->bits_per_sample)
{
case 8:
- configure(data()->sample_rate, data()->channels, Qmmp::PCM_S8);
+ configure(data()->sample_rate, channel_map, Qmmp::PCM_S8);
break;
case 16:
- configure(data()->sample_rate, data()->channels, Qmmp::PCM_S16LE);
+ configure(data()->sample_rate, channel_map, Qmmp::PCM_S16LE);
break;
case 24:
case 32:
- configure(data()->sample_rate, data()->channels, Qmmp::PCM_S32LE);
+ configure(data()->sample_rate, channel_map, Qmmp::PCM_S32LE);
break;
default:
return false;
@@ -555,3 +563,66 @@ uint DecoderFLAC::findID3v2(char *data, ulong size) //retuns ID3v2 tag size
}
return 0;
}
+
+ChannelMap DecoderFLAC::findChannelMap(int channels)
+{
+ ChannelMap map;
+ switch (channels)
+ {
+ case 1:
+ map << Qmmp::CHAN_FRONT_LEFT;
+ break;
+ case 2:
+ map << Qmmp::CHAN_FRONT_LEFT
+ << Qmmp::CHAN_FRONT_RIGHT;
+ break;
+ case 3:
+ map << Qmmp::CHAN_FRONT_LEFT
+ << Qmmp::CHAN_FRONT_RIGHT
+ << Qmmp::CHAN_FRONT_CENTER;
+ break;
+ case 4:
+ map << Qmmp::CHAN_FRONT_LEFT
+ << Qmmp::CHAN_FRONT_RIGHT
+ << Qmmp::CHAN_REAR_LEFT
+ << Qmmp::CHAN_REAR_RIGHT;
+ break;
+ case 5:
+ map << Qmmp::CHAN_FRONT_LEFT
+ << Qmmp::CHAN_FRONT_RIGHT
+ << Qmmp::CHAN_FRONT_CENTER
+ << Qmmp::CHAN_REAR_LEFT
+ << Qmmp::CHAN_REAR_RIGHT;
+ break;
+ case 6:
+ map << Qmmp::CHAN_FRONT_LEFT
+ << Qmmp::CHAN_FRONT_RIGHT
+ << Qmmp::CHAN_FRONT_CENTER
+ << Qmmp::CHAN_LFE
+ << Qmmp::CHAN_REAR_LEFT
+ << Qmmp::CHAN_REAR_RIGHT;
+ break;
+ case 7:
+ map << Qmmp::CHAN_FRONT_LEFT
+ << Qmmp::CHAN_FRONT_RIGHT
+ << Qmmp::CHAN_FRONT_CENTER
+ << Qmmp::CHAN_LFE
+ << Qmmp::CHAN_REAR_CENTER
+ << Qmmp::CHAN_SIDE_LEFT
+ << Qmmp::CHAN_SIDE_RIGHT;
+ break;
+ case 8:
+ map << Qmmp::CHAN_FRONT_LEFT
+ << Qmmp::CHAN_FRONT_RIGHT
+ << Qmmp::CHAN_FRONT_CENTER
+ << Qmmp::CHAN_LFE
+ << Qmmp::CHAN_REAR_LEFT
+ << Qmmp::CHAN_REAR_RIGHT
+ << Qmmp::CHAN_SIDE_LEFT
+ << Qmmp::CHAN_SIDE_RIGHT;
+ break;
+ default:
+ ;
+ }
+ return map;
+}
diff --git a/src/plugins/Input/flac/decoder_flac.h b/src/plugins/Input/flac/decoder_flac.h
index e64e376eb..4b4a2f668 100644
--- a/src/plugins/Input/flac/decoder_flac.h
+++ b/src/plugins/Input/flac/decoder_flac.h
@@ -82,6 +82,7 @@ private:
// helper functions
void deinit();
uint findID3v2(char *data, ulong size); //retuns ID3v2 tag size
+ ChannelMap findChannelMap(int channels);
struct flac_data *m_data;
qint64 length_in_bytes;
diff --git a/src/plugins/Input/mad/decoder_mad.cpp b/src/plugins/Input/mad/decoder_mad.cpp
index b4087c06b..08b2486fb 100644
--- a/src/plugins/Input/mad/decoder_mad.cpp
+++ b/src/plugins/Input/mad/decoder_mad.cpp
@@ -120,7 +120,12 @@ bool DecoderMAD::initialize()
mad_frame_mute (&m_frame);
m_stream.next_frame = 0;
m_stream.sync = 0;
- configure(m_freq, m_channels, Qmmp::PCM_S16LE);
+ ChannelMap map;
+ if(m_channels == 1)
+ map << Qmmp::CHAN_FRONT_LEFT;
+ else
+ map << Qmmp::CHAN_FRONT_LEFT << Qmmp::CHAN_FRONT_RIGHT;
+ configure(m_freq, map, Qmmp::PCM_S16LE);
m_inited = true;
return true;
}
diff --git a/src/plugins/Output/Output.pro b/src/plugins/Output/Output.pro
index d07513257..7dc1f0ddb 100644
--- a/src/plugins/Output/Output.pro
+++ b/src/plugins/Output/Output.pro
@@ -4,20 +4,20 @@ TEMPLATE = subdirs
win32:SUBDIRS += waveout
win32:SUBDIRS += directsound
-SUBDIRS += null
+#SUBDIRS += null
unix{
contains(CONFIG, JACK_PLUGIN){
- SUBDIRS += jack
+# SUBDIRS += jack
}
contains(CONFIG, OSS_PLUGIN){
- SUBDIRS += oss
+# SUBDIRS += oss
}
contains(CONFIG, PULSE_AUDIO_PLUGIN){
- SUBDIRS += pulseaudio
+# SUBDIRS += pulseaudio
}
contains(CONFIG, ALSA_PLUGIN){
@@ -25,7 +25,7 @@ contains(CONFIG, ALSA_PLUGIN){
}
contains(CONFIG, OSS4_PLUGIN){
- SUBDIRS += oss4
+# SUBDIRS += oss4
}
}
diff --git a/src/plugins/Output/alsa/outputalsa.cpp b/src/plugins/Output/alsa/outputalsa.cpp
index 5774579f5..f12f02060 100644
--- a/src/plugins/Output/alsa/outputalsa.cpp
+++ b/src/plugins/Output/alsa/outputalsa.cpp
@@ -44,6 +44,18 @@ OutputALSA::OutputALSA() : m_inited(false)
m_prebuf_size = 0;
m_prebuf_fill = 0;
m_can_pause = false;
+
+ m_alsa_channels[SND_CHMAP_NA] = Qmmp::CHAN_NULL;
+ m_alsa_channels[SND_CHMAP_MONO] = Qmmp::CHAN_FRONT_CENTER;
+ m_alsa_channels[SND_CHMAP_FL] = Qmmp::CHAN_FRONT_LEFT;
+ m_alsa_channels[SND_CHMAP_FR] = Qmmp::CHAN_FRONT_RIGHT;
+ m_alsa_channels[SND_CHMAP_RL] = Qmmp::CHAN_REAR_LEFT;
+ m_alsa_channels[SND_CHMAP_RR] = Qmmp::CHAN_REAR_RIGHT;
+ m_alsa_channels[SND_CHMAP_FC] = Qmmp::CHAN_FRONT_CENTER;
+ m_alsa_channels[SND_CHMAP_LFE] = Qmmp::CHAN_LFE;
+ m_alsa_channels[SND_CHMAP_SL] = Qmmp::CHAN_SIDE_LEFT;
+ m_alsa_channels[SND_CHMAP_SR] = Qmmp::CHAN_SIDE_RIGHT;
+ m_alsa_channels[SND_CHMAP_RC] = Qmmp::CHAN_REAR_CENTER;
}
OutputALSA::~OutputALSA()
@@ -52,7 +64,7 @@ OutputALSA::~OutputALSA()
free (pcm_name);
}
-bool OutputALSA::initialize(quint32 freq, int chan, Qmmp::AudioFormat format)
+bool OutputALSA::initialize(quint32 freq, ChannelMap map, Qmmp::AudioFormat format)
{
m_inited = false;
@@ -141,12 +153,17 @@ bool OutputALSA::initialize(quint32 freq, int chan, Qmmp::AudioFormat format)
qWarning("OutputALSA: The rate %d Hz is not supported by your hardware.\n==> Using %d Hz instead.", rate, exact_rate);
rate = exact_rate;
}
- uint c = chan;
+ uint c = map.count();
if ((err = snd_pcm_hw_params_set_channels_near(pcm_handle, hwparams, &c)) < 0)
{
qWarning("OutputALSA: Error setting channels: %s", snd_strerror(err));
return false;
}
+ if (c != (uint)map.count())
+ {
+ qWarning("OutputALSA: The channel number %d is not supported by your hardware", map.count());
+ qWarning("==> Using %d instead.", c);
+ }
if ((err = snd_pcm_hw_params_set_period_time_near(pcm_handle, hwparams, &period_time ,0)) < 0)
{
qWarning("OutputALSA: Error setting period time: %s", snd_strerror(err));
@@ -190,11 +207,30 @@ bool OutputALSA::initialize(quint32 freq, int chan, Qmmp::AudioFormat format)
m_chunk_size = period_size;
m_can_pause = snd_pcm_hw_params_can_pause(hwparams) && use_pause;
qDebug("OutputALSA: can pause: %d", m_can_pause);
- configure(rate, chan, format); //apply configuration
+
+ //channel map configuration
+ snd_pcm_chmap_t *chmap = snd_pcm_get_chmap(pcm_handle);
+ if(!chmap)
+ {
+ qWarning("OutputALSA: Unable to receive current channel map: %s", snd_strerror(err));
+ return false;
+ }
+ char tmp[256];
+ memset(tmp,0,256);
+ snd_pcm_chmap_print(chmap, 256, tmp);
+ qDebug("OutputALSA: received channel map: %s",tmp);
+ ChannelMap out_map;
+ for(uint i = 0; i < chmap->channels; ++i)
+ {
+ if(m_alsa_channels.keys().contains(chmap->pos[i]))
+ out_map.append(m_alsa_channels.value(chmap->pos[i]));
+ else
+ out_map.append(Qmmp::CHAN_NULL);
+ }
+ configure(exact_rate, out_map, format); //apply configuration
//create alsa prebuffer;
m_prebuf_size = 2 * snd_pcm_frames_to_bytes(pcm_handle, m_chunk_size); //buffer for two periods
m_prebuf = (uchar *)malloc(m_prebuf_size);
-
m_inited = true;
return true;
}
diff --git a/src/plugins/Output/alsa/outputalsa.h b/src/plugins/Output/alsa/outputalsa.h
index 1600e3b29..d378b813c 100644
--- a/src/plugins/Output/alsa/outputalsa.h
+++ b/src/plugins/Output/alsa/outputalsa.h
@@ -18,8 +18,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
***************************************************************************/
-#ifndef OUTPUTALSA_H
-#define OUTPUTALSA_H
+#ifndef OUTPUTALSA_H
+#define OUTPUTALSA_H
class OutputALSA;
@@ -28,6 +28,7 @@ extern "C"
#include <alsa/asoundlib.h>
}
+#include <QHash>
#include <qmmp/output.h>
#include <qmmp/volume.h>
@@ -38,7 +39,7 @@ public:
OutputALSA();
~OutputALSA();
- bool initialize(quint32, int, Qmmp::AudioFormat format);
+ bool initialize(quint32, ChannelMap map, Qmmp::AudioFormat format);
//output api
qint64 latency();
qint64 writeAudio(unsigned char *data, qint64 maxSize);
@@ -63,6 +64,8 @@ private:
qint64 m_prebuf_size;
qint64 m_prebuf_fill;
bool m_can_pause;
+ //channel conversions
+ QHash <quint16, Qmmp::ChannelPosition> m_alsa_channels;
};
class VolumeALSA : public Volume
diff --git a/src/qmmp/audioconverter.cpp b/src/qmmp/audioconverter.cpp
index fc64dc7d5..064e47cec 100644
--- a/src/qmmp/audioconverter.cpp
+++ b/src/qmmp/audioconverter.cpp
@@ -1,5 +1,5 @@
/***************************************************************************
- * Copyright (C) 2010 by Ilya Kotov *
+ * Copyright (C) 2010-2014 by Ilya Kotov *
* forkotov02@hotmail.ru *
* *
* This program is free software; you can redistribute it and/or modify *
@@ -47,10 +47,10 @@ AudioConverter::AudioConverter()
m_format = Qmmp::PCM_UNKNOWM;
}
-void AudioConverter::configure(quint32 srate, int chan, Qmmp::AudioFormat f)
+void AudioConverter::configure(quint32 srate, ChannelMap map, Qmmp::AudioFormat f)
{
m_format = f;
- Effect::configure(srate, chan, Qmmp::PCM_S16LE);
+ Effect::configure(srate, map, Qmmp::PCM_S16LE);
}
void AudioConverter::applyEffect(Buffer *b)
@@ -58,14 +58,14 @@ void AudioConverter::applyEffect(Buffer *b)
switch(m_format)
{
case Qmmp::PCM_S8:
- {
- unsigned char *out = new unsigned char[b->nbytes*2];
- s8_to_s16((qint8 *)b->data, (qint16 *) out, b->nbytes);
- delete [] b->data;
- b->data = out;
- b->nbytes <<= 1;
- break;
- }
+ {
+ unsigned char *out = new unsigned char[b->nbytes*2];
+ s8_to_s16((qint8 *)b->data, (qint16 *) out, b->nbytes);
+ delete [] b->data;
+ b->data = out;
+ b->nbytes <<= 1;
+ break;
+ }
case Qmmp::PCM_S24LE:
s24_to_s16((qint32 *)b->data, (qint16 *)b->data, b->nbytes >> 2);
b->nbytes >>= 1;
diff --git a/src/qmmp/audioconverter_p.h b/src/qmmp/audioconverter_p.h
index 6f59220f1..d01b09e5e 100644
--- a/src/qmmp/audioconverter_p.h
+++ b/src/qmmp/audioconverter_p.h
@@ -1,5 +1,5 @@
/***************************************************************************
- * Copyright (C) 2010 by Ilya Kotov *
+ * Copyright (C) 2010-2014 by Ilya Kotov *
* forkotov02@hotmail.ru *
* *
* This program is free software; you can redistribute it and/or modify *
@@ -30,7 +30,7 @@ class AudioConverter : public Effect
{
public:
AudioConverter();
- void configure(quint32 srate = 44100, int chan = 2, Qmmp::AudioFormat f = Qmmp::PCM_S16LE);
+ void configure(quint32 srate, ChannelMap map, Qmmp::AudioFormat f = Qmmp::PCM_S16LE);
void applyEffect(Buffer *b);
private:
diff --git a/src/qmmp/audioparameters.cpp b/src/qmmp/audioparameters.cpp
index b139b93c5..f1d8b6bdf 100644
--- a/src/qmmp/audioparameters.cpp
+++ b/src/qmmp/audioparameters.cpp
@@ -1,5 +1,5 @@
/***************************************************************************
- * Copyright (C) 2009 by Ilya Kotov *
+ * Copyright (C) 2009-2014 by Ilya Kotov *
* forkotov02@hotmail.ru *
* *
* This program is free software; you can redistribute it and/or modify *
@@ -23,34 +23,33 @@
AudioParameters::AudioParameters()
{
m_srate = 0;
- m_chan = 0;
m_format = Qmmp::PCM_S16LE;
}
AudioParameters::AudioParameters(const AudioParameters &other)
{
m_srate = other.sampleRate();
- m_chan = other.channels();
+ m_chan_map = other.channelMap();
m_format = other.format();
}
-AudioParameters::AudioParameters(quint32 srate, int chan, Qmmp::AudioFormat format)
+AudioParameters::AudioParameters(quint32 srate, const ChannelMap &map, Qmmp::AudioFormat format)
{
m_srate = srate;
- m_chan = chan;
+ m_chan_map = map;
m_format = format;
}
void AudioParameters::operator=(const AudioParameters &p)
{
m_srate = p.sampleRate();
- m_chan = p.channels();
+ m_chan_map = p.channelMap();
m_format = p.format();
}
bool AudioParameters::operator==(const AudioParameters &p) const
{
- return m_srate == p.sampleRate() && m_chan == p.channels() && m_format == p.format();
+ return m_srate == p.sampleRate() && m_chan_map == p.channelMap() && m_format == p.format();
}
bool AudioParameters::operator!=(const AudioParameters &p) const
@@ -65,7 +64,12 @@ quint32 AudioParameters::sampleRate() const
int AudioParameters::channels() const
{
- return m_chan;
+ return m_chan_map.count();
+}
+
+const ChannelMap AudioParameters::channelMap() const
+{
+ return m_chan_map;
}
Qmmp::AudioFormat AudioParameters::format() const
diff --git a/src/qmmp/audioparameters.h b/src/qmmp/audioparameters.h
index dc3620820..94e113eca 100644
--- a/src/qmmp/audioparameters.h
+++ b/src/qmmp/audioparameters.h
@@ -1,5 +1,5 @@
/***************************************************************************
- * Copyright (C) 2009-2012 by Ilya Kotov *
+ * Copyright (C) 2009-2014 by Ilya Kotov *
* forkotov02@hotmail.ru *
* *
* This program is free software; you can redistribute it and/or modify *
@@ -22,6 +22,7 @@
#define AUDIOPARAMETERS_H
#include <QtGlobal>
+#include "channelmap.h"
#include "qmmp.h"
/*! @brief The AudioParameters class keeps information about audio settings.
@@ -37,10 +38,10 @@ public:
/*!
* Constructs audio settings with the given parameters.
* @param srate Sampling rate.
- * @param chan Number of channels.
+ * @param map Channel map.
* @param format PCM data format.
*/
- AudioParameters(quint32 srate, int chan, Qmmp::AudioFormat format);
+ AudioParameters(quint32 srate, const ChannelMap &map, Qmmp::AudioFormat format);
/*!
* Constructs a copy of \b other.
*/
@@ -65,6 +66,7 @@ public:
* Returns number of channels.
*/
int channels() const;
+ const ChannelMap channelMap() const;
/*!
* Returns pcm format.
*/
@@ -80,7 +82,7 @@ public:
private:
quint32 m_srate;
- int m_chan;
+ ChannelMap m_chan_map;
Qmmp::AudioFormat m_format;
};
diff --git a/src/qmmp/channelconverter.cpp b/src/qmmp/channelconverter.cpp
new file mode 100644
index 000000000..622dc73ae
--- /dev/null
+++ b/src/qmmp/channelconverter.cpp
@@ -0,0 +1,119 @@
+/***************************************************************************
+ * Copyright (C) 2014 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+
+#include "channelconverter_p.h"
+
+ChannelConverter::ChannelConverter(ChannelMap out_map)
+{
+ m_disabled = true;
+ m_tmp_buf = 0;
+ m_format = Qmmp::PCM_UNKNOWM;
+ m_frame_size = 0;
+ m_channels = 0;
+ m_out_map = out_map;
+ memset(m_reorder_array, 0, sizeof(m_reorder_array));
+}
+
+ChannelConverter::~ChannelConverter()
+{
+ if(m_tmp_buf)
+ {
+ delete [] m_tmp_buf;
+ m_tmp_buf = 0;
+ }
+}
+
+void ChannelConverter::configure(quint32 srate, ChannelMap in_map, Qmmp::AudioFormat f)
+{
+ Effect::configure(srate, m_out_map, f);
+
+ if((m_disabled = (in_map == m_out_map)))
+ return;
+
+ m_channels = channels();
+ m_frame_size = audioParameters().sampleSize() * channels();
+ m_tmp_buf = new unsigned char[m_frame_size];
+ m_format = f;
+
+ QStringList reorderStringList;
+ for(int i = 0; i < m_channels; ++i)
+ {
+ m_reorder_array[i] = m_out_map.indexOf(in_map.at(i));
+ reorderStringList << QString("%1").arg(m_reorder_array[i]);
+ }
+
+ qDebug("ChannelConverter: {%s} ==> {%s}; {%s}", qPrintable(in_map.toString()),
+ qPrintable(m_out_map.toString()), qPrintable(reorderStringList.join(",")));
+}
+
+void ChannelConverter::applyEffect(Buffer *b)
+{
+ if(m_disabled)
+ return;
+
+ unsigned long i = 0;
+ int j = 0;
+
+ switch(m_format)
+ {
+ case Qmmp::PCM_S8:
+ {
+ unsigned char *data = b->data;
+ unsigned char *prev_data = m_tmp_buf;
+ for(i = 0; i < b->nbytes/m_frame_size; ++i)
+ {
+ memcpy(m_tmp_buf, data, m_frame_size);
+ for(j = 0; j < m_channels; ++j)
+ data[j] = m_reorder_array[j] < 0 ? 0 : prev_data[m_reorder_array[j]];
+ data += m_channels;
+ }
+ break;
+ }
+ case Qmmp::PCM_S16LE:
+ {
+ quint16 *data = (quint16 *) b->data;
+ quint16 *prev_data = (quint16 *) m_tmp_buf;
+ for(i = 0; i < b->nbytes/m_frame_size; ++i)
+ {
+ memcpy(m_tmp_buf, data, m_frame_size);
+ for(j = 0; j < m_channels; ++j)
+ data[j] = m_reorder_array[j] < 0 ? 0 : prev_data[m_reorder_array[j]];
+ data += m_channels;
+ }
+ break;
+ }
+ case Qmmp::PCM_S24LE:
+ case Qmmp::PCM_S32LE:
+ {
+ quint32 *data = (quint32 *) b->data;
+ quint32 *prev_data = (quint32 *) m_tmp_buf;
+ for(i = 0; i < b->nbytes/m_frame_size; ++i)
+ {
+ memcpy(m_tmp_buf, data, m_frame_size);
+ for(j = 0; j < m_channels; ++j)
+ data[j] = m_reorder_array[j] < 0 ? 0 : prev_data[m_reorder_array[j]];
+ data += m_channels;
+ }
+ break;
+ }
+ default:
+ ;
+ }
+}
diff --git a/src/qmmp/channelconverter_p.h b/src/qmmp/channelconverter_p.h
new file mode 100644
index 000000000..7f41570fe
--- /dev/null
+++ b/src/qmmp/channelconverter_p.h
@@ -0,0 +1,47 @@
+/***************************************************************************
+ * Copyright (C) 2014 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+
+#ifndef CHANNELCONVERTER_P_H
+#define CHANNELCONVERTER_P_H
+
+#include "effect.h"
+
+/*! @internal
+ * @author Ilya Kotov <forkotov02@hotmail.ru>
+ */
+class ChannelConverter : public Effect
+{
+public:
+ ChannelConverter(ChannelMap out_map);
+ ~ChannelConverter();
+ void configure(quint32 srate, ChannelMap in_map, Qmmp::AudioFormat f);
+ void applyEffect(Buffer *b);
+
+private:
+ bool m_disabled;
+ int m_reorder_array[9];
+ unsigned char *m_tmp_buf;
+ int m_frame_size;
+ int m_channels;
+ Qmmp::AudioFormat m_format;
+ ChannelMap m_out_map;
+};
+
+#endif // CHANNELCONVERTER_P_H
diff --git a/src/qmmp/channelmap.cpp b/src/qmmp/channelmap.cpp
index 196c69d6b..34fdde17a 100644
--- a/src/qmmp/channelmap.cpp
+++ b/src/qmmp/channelmap.cpp
@@ -18,16 +18,20 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
***************************************************************************/
+#include <QStringList>
+#include <QHash>
#include "channelmap.h"
-Qmmp::AudioChannel ChannelMap::m_internal_map[8] = { Qmmp::CHAN_FRONT_LEFT,
+Qmmp::ChannelPosition ChannelMap::m_internal_map[9] = { Qmmp::CHAN_FRONT_LEFT,
Qmmp::CHAN_FRONT_RIGHT,
Qmmp::CHAN_REAR_LEFT,
Qmmp::CHAN_REAR_RIGHT,
- Qmmp::CHAN_CENTER,
+ Qmmp::CHAN_FRONT_CENTER,
+ Qmmp::CHAN_REAR_CENTER,
Qmmp::CHAN_LFE,
Qmmp::CHAN_SIDE_LEFT,
- Qmmp::CHAN_SIDE_RIGHT };
+ Qmmp::CHAN_SIDE_RIGHT,
+ };
ChannelMap::ChannelMap()
{
@@ -36,7 +40,7 @@ ChannelMap::ChannelMap()
int ChannelMap::mask() const
{
int mask = 0;
- foreach (Qmmp::AudioChannel channel, *this)
+ foreach (Qmmp::ChannelPosition channel, *this)
{
mask |= channel;
}
@@ -46,16 +50,37 @@ int ChannelMap::mask() const
const ChannelMap ChannelMap::remaped() const
{
ChannelMap map;
- for(int i = 0; i < 8; ++i)
+ for(int i = 0; i < 9; ++i)
{
- foreach (Qmmp::AudioChannel channel, *this)
- {
- if(channel == m_internal_map[i])
- {
- map.append(channel);
- break;
- }
- }
+ if(contains(m_internal_map[i]))
+ map.append(m_internal_map[i]);
+ }
+ while (map.count() < count())
+ {
+ map.append(Qmmp::CHAN_NULL);
}
return map;
}
+
+const QString ChannelMap::toString() const
+{
+ QStringList list;
+ QHash <Qmmp::ChannelPosition, QString> names;
+ names.insert(Qmmp::CHAN_NULL, "NA");
+ names.insert(Qmmp::CHAN_FRONT_LEFT, "FL");
+ names.insert(Qmmp::CHAN_FRONT_RIGHT, "FR");
+ names.insert(Qmmp::CHAN_REAR_LEFT, "RL");
+ names.insert(Qmmp::CHAN_REAR_RIGHT, "RR");
+ names.insert(Qmmp::CHAN_FRONT_CENTER, "FC");
+ names.insert(Qmmp::CHAN_REAR_CENTER, "RC");
+ names.insert(Qmmp::CHAN_LFE, "LFE");
+ names.insert(Qmmp::CHAN_SIDE_LEFT, "SL");
+ names.insert(Qmmp::CHAN_SIDE_RIGHT, "SR");
+
+
+ foreach (Qmmp::ChannelPosition channel, *this)
+ {
+ list << names.value(channel);
+ }
+ return list.join(",");
+}
diff --git a/src/qmmp/channelmap.h b/src/qmmp/channelmap.h
index 5a9a96ef5..19196b8f5 100644
--- a/src/qmmp/channelmap.h
+++ b/src/qmmp/channelmap.h
@@ -24,16 +24,17 @@
#include <QList>
#include "qmmp.h"
-class ChannelMap : public QList<Qmmp::AudioChannel>
+class ChannelMap : public QList<Qmmp::ChannelPosition>
{
public:
ChannelMap();
int mask() const;
const ChannelMap remaped() const;
+ const QString toString() const;
private:
- static Qmmp::AudioChannel m_internal_map[8];
+ static Qmmp::ChannelPosition m_internal_map[9];
};
#endif // CHANNELMAP_H
diff --git a/src/qmmp/decoder.cpp b/src/qmmp/decoder.cpp
index 1b9bfff69..1aa9fc4de 100644
--- a/src/qmmp/decoder.cpp
+++ b/src/qmmp/decoder.cpp
@@ -33,9 +33,9 @@ void Decoder::setReplayGainInfo(const QMap<Qmmp::ReplayGainKey, double> &rg, boo
m_hasHeadroom = headroom;
}
-void Decoder::configure(quint32 srate, int chan, Qmmp::AudioFormat format)
+void Decoder::configure(quint32 srate, const ChannelMap &map, Qmmp::AudioFormat format)
{
- m_parameters = AudioParameters(srate, chan, format);
+ m_parameters = AudioParameters(srate, map, format);
}
qint64 Decoder::read(float *data, qint64 samples)
@@ -49,7 +49,7 @@ qint64 Decoder::read(float *data, qint64 samples)
void Decoder::next()
{}
-const QString Decoder::nextURL()
+const QString Decoder::nextURL() const
{
return QString();
}
diff --git a/src/qmmp/decoder.h b/src/qmmp/decoder.h
index 195aefefb..8e76f3a93 100644
--- a/src/qmmp/decoder.h
+++ b/src/qmmp/decoder.h
@@ -14,6 +14,7 @@
#include "fileinfo.h"
#include "qmmp.h"
#include "audioparameters.h"
+#include "channelmap.h"
#include "decoderfactory.h"
class QmmpPluginCache;
@@ -82,7 +83,7 @@ public:
* By default this function does nothing.
* Reimplemet it if your decoder can play next track without stop/start cycle.
*/
- virtual const QString nextURL();
+ virtual const QString nextURL() const;
/*!
* Returns detected audio parameters.
*/
@@ -174,12 +175,10 @@ protected:
/*!
* Use this function inside initialize() reimplementation to tell other plugins about audio parameters.
* @param srate Sample rate.
- * @param chan Number of channels.
+ * @param map Map of channels.
* @param f Audio format.
*/
- void configure(quint32 srate = 44100, int chan = 2, Qmmp::AudioFormat f = Qmmp::PCM_S16LE);
-
- //void configure(quint32 srate = 44100, QList<Qmmp::AudioChannel> channels, Qmmp::AudioFormat f = Qmmp::PCM_S16LE);
+ void configure(quint32 srate, const ChannelMap &map, Qmmp::AudioFormat f = Qmmp::PCM_S16LE);
private:
static void loadPlugins();
diff --git a/src/qmmp/effect.cpp b/src/qmmp/effect.cpp
index 8f797b7ea..99fc4c3b7 100644
--- a/src/qmmp/effect.cpp
+++ b/src/qmmp/effect.cpp
@@ -29,39 +29,45 @@
Effect::Effect()
{
m_freq = 0;
- m_chan = 0;
m_format = Qmmp::PCM_UNKNOWM;
+ m_channels = 0;
m_factory = 0;
}
Effect::~Effect()
{}
-void Effect::configure(quint32 freq, int chan, Qmmp::AudioFormat format)
+void Effect::configure(quint32 freq, ChannelMap map, Qmmp::AudioFormat format)
{
m_freq = freq;
- m_chan = chan;
+ m_chan_map = map;
+ m_channels = map.count();
m_format = format;
}
-quint32 Effect::sampleRate()
+quint32 Effect::sampleRate() const
{
return m_freq;
}
-int Effect::channels()
+int Effect::channels() const
{
- return m_chan;
+ return m_channels;
}
-Qmmp::AudioFormat Effect::format()
+const ChannelMap Effect::channelMap() const
+{
+ return m_chan_map;
+}
+
+Qmmp::AudioFormat Effect::format() const
{
return m_format;
}
const AudioParameters Effect::audioParameters() const
{
- return AudioParameters(m_freq, m_chan, m_format);
+ return AudioParameters(m_freq, m_chan_map, m_format);
}
EffectFactory* Effect::factory() const
diff --git a/src/qmmp/effect.h b/src/qmmp/effect.h
index 3c9262145..b2fc2bf0f 100644
--- a/src/qmmp/effect.h
+++ b/src/qmmp/effect.h
@@ -1,5 +1,5 @@
/***************************************************************************
- * Copyright (C) 2007-2013 by Ilya Kotov *
+ * Copyright (C) 2007-2014 by Ilya Kotov *
* forkotov02@hotmail.ru *
* *
* This program is free software; you can redistribute it and/or modify *
@@ -23,6 +23,7 @@
#include <QList>
#include <QStringList>
#include "audioparameters.h"
+#include "channelmap.h"
#include "buffer.h"
class EffectFactory;
@@ -51,22 +52,26 @@ public:
* Prepares object for usage.
* Subclasses that reimplement this function must call the base implementation.
* @param srate Sample rate.
- * @param chan Number of channels.
+ * @param map Map of channels.
* @param f Audio format.
*/
- virtual void configure(quint32 srate = 44100, int chan = 2, Qmmp::AudioFormat f = Qmmp::PCM_S16LE);
+ virtual void configure(quint32 srate, ChannelMap map, Qmmp::AudioFormat f = Qmmp::PCM_S16LE);
/*!
* Returns samplerate.
*/
- quint32 sampleRate();
+ quint32 sampleRate() const;
/*!
* Returns channels number.
*/
- int channels();
+ int channels() const;
+ /*!
+ * Returns map of channels
+ */
+ const ChannelMap channelMap() const;
/*!
* Returns audio format.
*/
- Qmmp::AudioFormat format();
+ Qmmp::AudioFormat format() const;
/*!
* Returns audio parameters for output data.
*/
@@ -108,7 +113,8 @@ public:
private:
EffectFactory *m_factory;
quint32 m_freq;
- int m_chan;
+ int m_channels;
+ ChannelMap m_chan_map;
Qmmp::AudioFormat m_format;
static void loadPlugins();
static QList<QmmpPluginCache*> *m_cache;
diff --git a/src/qmmp/output.cpp b/src/qmmp/output.cpp
index 7a253047a..81031568e 100644
--- a/src/qmmp/output.cpp
+++ b/src/qmmp/output.cpp
@@ -17,30 +17,34 @@
Output::Output()
{
m_frequency = 0;
- m_channels = 0;
m_format = Qmmp::PCM_UNKNOWM;
}
-void Output::configure(quint32 freq, int chan, Qmmp::AudioFormat format)
+void Output::configure(quint32 freq, ChannelMap map, Qmmp::AudioFormat format)
{
m_frequency = freq;
- m_channels = chan;
+ m_chan_map = map;
m_format = format;
}
AudioParameters Output::audioParameters() const
{
- return AudioParameters(m_frequency, m_channels, m_format);
+ return AudioParameters(m_frequency, m_chan_map, m_format);
}
-quint32 Output::sampleRate()
+quint32 Output::sampleRate() const
{
return m_frequency;
}
-int Output::channels()
+int Output::channels() const
{
- return m_channels;
+ return m_chan_map.count();
+}
+
+const ChannelMap Output::channelMap() const
+{
+ return m_chan_map;
}
Qmmp::AudioFormat Output::format() const
diff --git a/src/qmmp/output.h b/src/qmmp/output.h
index 8b2ab67e4..96949e799 100644
--- a/src/qmmp/output.h
+++ b/src/qmmp/output.h
@@ -12,6 +12,7 @@
#include <QIODevice>
#include "outputfactory.h"
#include "audioparameters.h"
+#include "channelmap.h"
class QTimer;
class QmmpSettings;
@@ -36,11 +37,11 @@ public:
* Prepares object for usage and setups required audio parameters.
* Subclass should reimplement this function.
* @param freq Sample rate.
- * @param chan Number of channels.
+ * @param map Map of channels.
* @param format Audio format
* @return initialization result (\b true - success, \b false - failure)
*/
- virtual bool initialize(quint32 freq, int chan, Qmmp::AudioFormat format) = 0;
+ virtual bool initialize(quint32 freq, ChannelMap map, Qmmp::AudioFormat format) = 0;
/*!
* Returns output interface latency in milliseconds.
*/
@@ -80,11 +81,13 @@ public:
/*!
* Returns samplerate.
*/
- quint32 sampleRate();
+ quint32 sampleRate() const;
/*!
* Returns channels number.
*/
- int channels();
+ int channels() const;
+
+ const ChannelMap channelMap() const;
/*!
* Returns selected audio format.
*/
@@ -120,14 +123,14 @@ protected:
/*!
* Use this function inside initialize() reimplementation to tell about accepted audio parameters.
* @param freq Sample rate.
- * @param chan Number of channels.
+ * @param map Map of channels.
* @param format Audio format.
*/
- void configure(quint32 freq, int chan, Qmmp::AudioFormat format);
+ void configure(quint32 freq, ChannelMap map, Qmmp::AudioFormat format);
private:
quint32 m_frequency;
- int m_channels;
+ ChannelMap m_chan_map;
Qmmp::AudioFormat m_format;
static void loadPlugins();
static QList<QmmpPluginCache*> *m_cache;
diff --git a/src/qmmp/outputwriter.cpp b/src/qmmp/outputwriter.cpp
index 682318880..4199ae93b 100644
--- a/src/qmmp/outputwriter.cpp
+++ b/src/qmmp/outputwriter.cpp
@@ -22,6 +22,8 @@
#include "statehandler.h"
#include "visual.h"
#include "output.h"
+#include "audioconverter_p.h"
+#include "channelconverter_p.h"
#include "volumecontrol_p.h"
#include "outputwriter_p.h"
@@ -61,7 +63,7 @@ OutputWriter::OutputWriter (QObject* parent) : QThread (parent)
m_totalWritten = 0;
m_currentMilliseconds = -1;
m_bytesPerMillisecond = 0;
- m_userStop = false;
+ m_user_stop = false;
m_finish = false;
m_visBuffer = 0;
m_visBufferSize = 0;
@@ -91,8 +93,14 @@ OutputWriter::~OutputWriter()
}
}
-bool OutputWriter::initialize(quint32 freq, int chan, Qmmp::AudioFormat format)
+bool OutputWriter::initialize(quint32 freq, ChannelMap map, Qmmp::AudioFormat format)
{
+ QMap<Qmmp::AudioFormat, QString> formatNames;
+ formatNames.insert(Qmmp::PCM_S8, "s8");
+ formatNames.insert(Qmmp::PCM_S16LE, "s16le");
+ formatNames.insert(Qmmp::PCM_S24LE, "s24le");
+ formatNames.insert(Qmmp::PCM_S32LE, "s32le");
+
m_output = Output::create();
if(!m_output)
{
@@ -100,30 +108,41 @@ bool OutputWriter::initialize(quint32 freq, int chan, Qmmp::AudioFormat format)
return false;
}
- if (!m_output->initialize(freq, chan, format))
+ if (!m_output->initialize(freq, map, format))
{
qWarning("OutputWriter: unable to initialize output");
delete m_output;
m_output = 0;
return false;
}
- m_frequency = m_output->sampleRate();
- m_channels = m_output->channels();
- m_format = m_output->format();
- QMap<Qmmp::AudioFormat, QString> formatNames;
- formatNames.insert(Qmmp::PCM_S8, "s8");
- formatNames.insert(Qmmp::PCM_S16LE, "s16le");
- formatNames.insert(Qmmp::PCM_S24LE, "s24le");
- formatNames.insert(Qmmp::PCM_S32LE, "s32le");
- qDebug("OutputWriter: [%s] %d Hz, %d ch, %s",
+ m_frequency = freq;
+ m_channels = map.count();
+ m_format = format;
+ m_chan_map = map;
+
+ qDebug("OutputWriter: [%s] %u Hz, {%s}, %s ==> %u Hz, {%s}, %s",
qPrintable(Output::currentFactory()->properties().shortName),
- freq, chan, qPrintable(formatNames.value(format)));
- m_bytesPerMillisecond = freq * chan * AudioParameters::sampleSize(format) / 1000;
- m_recycler.configure(freq, chan, format); //calculate output buffer size
+ freq,
+ qPrintable(map.toString()),
+ qPrintable(formatNames.value(format)),
+ m_output->sampleRate(),
+ qPrintable(m_output->channelMap().toString()),
+ qPrintable(formatNames.value(m_output->format())));
+
+ if(!prepareConverters())
+ {
+ qWarning("OutputWriter: unable to convert audio");
+ delete m_output;
+ m_output = 0;
+ return false;
+ }
+
+ m_bytesPerMillisecond = m_frequency * m_channels * AudioParameters::sampleSize(format) / 1000;
+ m_recycler.configure(m_frequency, m_channels, m_format); //calculate output buffer size
//visual buffer
if(m_visBuffer)
delete [] m_visBuffer;
- m_visBufferSize = QMMP_BLOCK_FRAMES * 2 * chan; //16-bit samples
+ m_visBufferSize = QMMP_BLOCK_FRAMES * 2 * m_channels; //16-bit samples
if(m_format != Qmmp::PCM_S16LE)
m_visBuffer = new unsigned char [m_visBufferSize];
m_useEq = m_eqEnabled && m_frequency && m_format == Qmmp::PCM_S16LE;
@@ -146,7 +165,7 @@ void OutputWriter::pause()
void OutputWriter::stop()
{
- m_userStop = true;
+ m_user_stop = true;
}
void OutputWriter::setMuted(bool muted)
@@ -178,7 +197,7 @@ QMutex *OutputWriter::mutex()
AudioParameters OutputWriter::audioParameters() const
{
- return AudioParameters(m_frequency, m_channels, m_format);
+ return AudioParameters(m_frequency, m_chan_map, m_format);
}
quint32 OutputWriter::sampleRate()
@@ -196,6 +215,11 @@ Qmmp::AudioFormat OutputWriter::format() const
return m_format;
}
+const ChannelMap OutputWriter::channelMap() const
+{
+ return m_chan_map;
+}
+
int OutputWriter::sampleSize() const
{
return AudioParameters::sampleSize(m_format);
@@ -243,6 +267,14 @@ void OutputWriter::dispatchVisual (Buffer *buffer)
m_visBuffer = 0;
}
+void OutputWriter::applyConverters(Buffer *buffer)
+{
+ foreach (Effect *e, m_converters)
+ {
+ e->applyEffect(buffer);
+ }
+}
+
void OutputWriter::clearVisuals()
{
foreach (Visual *visual, *Visual::visuals())
@@ -253,6 +285,45 @@ void OutputWriter::clearVisuals()
}
}
+bool OutputWriter::prepareConverters()
+{
+ qDeleteAll(m_converters);
+ m_converters.clear();
+
+ AudioParameters ap = m_output->audioParameters();
+
+ if(channels() != m_output->channels())
+ {
+ qWarning("OutputWriter: unsupported channel number");
+ return false;
+ }
+
+ if(format() != ap.format())
+ {
+ if(m_output->format() == Qmmp::PCM_S16LE)
+ {
+ qDebug("OutputWriter: using 16 bit comverter");
+ m_converters << new AudioConverter();
+ m_converters.last()->configure(ap.sampleRate(), ap.channelMap(), ap.format());
+ ap = m_converters.last()->audioParameters();
+ }
+ else
+ {
+ qWarning("OutputWriter: unsupported audio format");
+ return false;
+ }
+ }
+
+ if(channelMap() != ap.channelMap())
+ {
+ m_converters << new ChannelConverter(ap.channelMap());
+ m_converters.last()->configure(ap.sampleRate(), channelMap(), ap.format());
+ ap = m_converters.last()->audioParameters();
+ }
+
+ return true;
+}
+
void OutputWriter::dispatch(qint64 elapsed,
int bitrate,
int frequency,
@@ -305,7 +376,7 @@ void OutputWriter::run()
m_prev_pause = m_pause;
}
recycler()->mutex()->lock ();
- done = m_userStop || (m_finish && recycler()->empty());
+ done = m_user_stop || (m_finish && recycler()->empty());
while (!done && (recycler()->empty() || m_pause))
{
@@ -313,7 +384,7 @@ void OutputWriter::run()
mutex()->unlock();
recycler()->cond()->wait(recycler()->mutex());
mutex()->lock ();
- done = m_userStop || m_finish;
+ done = m_user_stop || m_finish;
}
status();
@@ -351,6 +422,7 @@ void OutputWriter::run()
SoftwareVolume::instance()->changeVolume(b, m_channels, m_format);
if (m_muted)
memset(b->data, 0, b->nbytes);
+ applyConverters(b);
l = 0;
m = 0;
while (l < b->nbytes && !m_pause && !m_prev_pause)
diff --git a/src/qmmp/outputwriter_p.h b/src/qmmp/outputwriter_p.h
index fcaecfc5f..f4096a9f5 100644
--- a/src/qmmp/outputwriter_p.h
+++ b/src/qmmp/outputwriter_p.h
@@ -1,5 +1,5 @@
/***************************************************************************
- * Copyright (C) 2012 by Ilya Kotov *
+ * Copyright (C) 2012-2014 by Ilya Kotov *
* forkotov02@hotmail.ru *
* *
* This program is free software; you can redistribute it and/or modify *
@@ -25,11 +25,13 @@
#include <QMutex>
#include "recycler_p.h"
#include "audioparameters.h"
+#include "channelmap.h"
class QTimer;
class QmmpSettings;
class StateHandler;
class Output;
+class Effect;
/** @internal
@brief Output thread.
@@ -45,11 +47,11 @@ public:
/*!
* Prepares object for usage and setups required audio parameters.
* @param freq Sample rate.
- * @param chan Number of channels.
+ * @param map Map of channels.
* @param format Audio format
* @return initialization result (\b true - success, \b false - failure)
*/
- bool initialize(quint32 freq, int chan, Qmmp::AudioFormat format);
+ bool initialize(quint32 freq, ChannelMap map, Qmmp::AudioFormat format);
/*!
* Requests playback to pause. If it was paused already, playback should resume.
*/
@@ -97,6 +99,7 @@ public:
* Returns selected audio format.
*/
Qmmp::AudioFormat format() const;
+ const ChannelMap channelMap() const;
/*!
* Returns sample size in bytes.
*/
@@ -115,16 +118,20 @@ private:
int channels);
void dispatch(const Qmmp::State &state);
void dispatchVisual(Buffer *buffer);
+ void applyConverters(Buffer *buffer);
void clearVisuals();
+ bool prepareConverters();
+
bool m_skip;
QMutex m_mutex;
Recycler m_recycler;
StateHandler *m_handler;
quint32 m_frequency;
int m_channels, m_kbps;
+ ChannelMap m_chan_map;
Qmmp::AudioFormat m_format;
qint64 m_bytesPerMillisecond;
- bool m_userStop, m_pause;
+ bool m_user_stop, m_pause;
bool m_prev_pause;
bool m_finish;
bool m_useEq, m_eqEnabled;
@@ -134,6 +141,7 @@ private:
QmmpSettings *m_settings;
Output *m_output;
bool m_muted;
+ QList<Effect *> m_converters;
};
diff --git a/src/qmmp/qmmp.h b/src/qmmp/qmmp.h
index 4103a3d17..a079e2a02 100644
--- a/src/qmmp/qmmp.h
+++ b/src/qmmp/qmmp.h
@@ -87,17 +87,18 @@ public:
PCM_S32LE /*!< Signed 32 bit Little Endian */
};
- enum AudioChannel
+ enum ChannelPosition
{
- CHAN_NULL = 0x00,
- CHAN_FRONT_LEFT = 0x01,
- CHAN_FRONT_RIGHT = 0x02,
- CHAN_REAR_LEFT = 0x04,
- CHAN_REAR_RIGHT = 0x08,
- CHAN_CENTER = 0x10,
- CHAN_LFE = 0x20,
- CHAN_SIDE_LEFT = 0x40,
- CHAN_SIDE_RIGHT = 0x80
+ CHAN_NULL = 0x00,
+ CHAN_FRONT_LEFT = 0x01,
+ CHAN_FRONT_RIGHT = 0x02,
+ CHAN_REAR_LEFT = 0x04,
+ CHAN_REAR_RIGHT = 0x08,
+ CHAN_FRONT_CENTER = 0x10,
+ CHAN_REAR_CENTER = 0x20,
+ CHAN_SIDE_LEFT = 0x40,
+ CHAN_SIDE_RIGHT = 0x80,
+ CHAN_LFE = 0x100,
};
/*!
diff --git a/src/qmmp/qmmp.pro b/src/qmmp/qmmp.pro
index 158a14fd2..4516dff6c 100644
--- a/src/qmmp/qmmp.pro
+++ b/src/qmmp/qmmp.pro
@@ -38,7 +38,8 @@ HEADERS += \
outputwriter_p.h \
recycler_p.h \
qmmpplugincache_p.h \
- channelmap.h
+ channelmap.h \
+ channelconverter_p.h
SOURCES += recycler.cpp \
decoder.cpp \
output.cpp \
@@ -68,7 +69,8 @@ SOURCES += recycler.cpp \
qmmpevents.cpp \
outputwriter.cpp \
qmmpplugincache.cpp \
- channelmap.cpp
+ channelmap.cpp \
+ channelconverter.cpp
FORMS +=
unix:TARGET = ../../lib/qmmp
win32:TARGET = ../../../bin/qmmp
diff --git a/src/qmmp/qmmpaudioengine.cpp b/src/qmmp/qmmpaudioengine.cpp
index d5e47b68d..45ea3bbcd 100644
--- a/src/qmmp/qmmpaudioengine.cpp
+++ b/src/qmmp/qmmpaudioengine.cpp
@@ -1,5 +1,5 @@
/***************************************************************************
- * Copyright (C) 2009-2013 by Ilya Kotov *
+ * Copyright (C) 2009-2014 by Ilya Kotov *
* forkotov02@hotmail.ru *
* *
* This program is free software; you can redistribute it and/or modify *
@@ -32,6 +32,7 @@
#include "statehandler.h"
#include "audioconverter_p.h"
#include "qmmpaudioengine_p.h"
+#include "channelconverter_p.h"
#include "metadatamanager.h"
#define TRANSPORT_TIMEOUT 5000 //ms
@@ -170,7 +171,7 @@ void QmmpAudioEngine::addEffect(EffectFactory *factory)
Effect *effect = Effect::create(factory);
if(!effect)
return;
- effect->configure(m_ap.sampleRate(), m_ap.channels(), m_ap.format());
+ effect->configure(m_ap.sampleRate(), m_ap.channelMap(), m_ap.format());
if(effect->audioParameters() == m_ap)
{
mutex()->lock();
@@ -568,34 +569,16 @@ OutputWriter *QmmpAudioEngine::createOutput()
{
OutputWriter *output = new OutputWriter(0);
output->setMuted(m_muted);
- if (!output->initialize(m_ap.sampleRate(), m_ap.channels(), m_ap.format()))
+ if (!output->initialize(m_ap.sampleRate(), m_ap.channelMap(), m_ap.format()))
{
delete output;
StateHandler::instance()->dispatch(Qmmp::FatalError);
return 0;
}
- if(output->audioParameters() != m_ap)
- {
- if(output->audioParameters().format() == Qmmp::PCM_S16LE) //output supports 16 bit only
- {
- Effect *effect = new AudioConverter();
- effect->configure(m_ap.sampleRate(), m_ap.channels(), m_ap.format());
- m_ap = effect->audioParameters();
- m_effects.append(effect);
- qDebug("QmmpAudioEngine: output plugin requires 16 bit, using 16-bit converter");
- }
- else
- {
- qWarning("QmmpAudioEngine: unsupported audio format");
- delete output;
- StateHandler::instance()->dispatch(Qmmp::FatalError);
- return 0;
- }
- }
if(m_output_buf)
delete [] m_output_buf;
- m_bks = QMMP_BLOCK_FRAMES * m_ap.channels() * m_ap.sampleSize();
+ m_bks = output->recycler()->blockSize();
m_output_size = m_bks * 4;
m_output_buf = new unsigned char[m_output_size];
return output;
@@ -621,10 +604,14 @@ void QmmpAudioEngine::prepareEffects(Decoder *d)
QList <Effect *> tmp_effects = m_effects;
m_effects.clear();
+ m_effects << new ChannelConverter(m_ap.channelMap().remaped());
+ m_effects.at(0)->configure(m_ap.sampleRate(), m_ap.channelMap(), m_ap.format());
+ m_ap = m_effects.at(0)->audioParameters();
+
if(m_settings->use16BitOutput())
{
m_effects << new AudioConverter();
- m_effects.at(0)->configure(m_ap.sampleRate(), m_ap.channels(), m_ap.format());
+ m_effects.at(0)->configure(m_ap.sampleRate(), m_ap.channelMap(), m_ap.format());
m_ap = m_effects.at(0)->audioParameters();
}
@@ -648,7 +635,7 @@ void QmmpAudioEngine::prepareEffects(Decoder *d)
if(!effect)
{
effect = Effect::create(factory);
- effect->configure(m_ap.sampleRate(), m_ap.channels(), m_ap.format());
+ effect->configure(m_ap.sampleRate(), m_ap.channelMap(), m_ap.format());
if (m_ap != effect->audioParameters())
{
m_blockedEffects << effect; //list of effects which require restart
diff --git a/src/qmmp/recycler.cpp b/src/qmmp/recycler.cpp
index 533c453a3..15d31a0e6 100644
--- a/src/qmmp/recycler.cpp
+++ b/src/qmmp/recycler.cpp
@@ -140,3 +140,8 @@ unsigned long Recycler::size() const
{
return m_buffer_count * m_block_size;
}
+
+unsigned long Recycler::blockSize() const
+{
+ return m_block_size;
+}
diff --git a/src/qmmp/recycler_p.h b/src/qmmp/recycler_p.h
index 8ca458643..22443f8de 100644
--- a/src/qmmp/recycler_p.h
+++ b/src/qmmp/recycler_p.h
@@ -75,6 +75,11 @@ public:
*/
unsigned long size() const; // size in bytes
/*!
+ * Returns block size in bytes.
+ */
+ unsigned long blockSize() const; // size in bytes
+
+ /*!
* Returns mutex pointer.
*/
QMutex *mutex()