2012-05-26 14:49:10 +02:00
|
|
|
//=============================================================================
|
|
|
|
// MusE Score
|
|
|
|
// Linux Music Score Editor
|
|
|
|
// $Id: exportaudio.cpp 5660 2012-05-22 14:17:39Z wschweer $
|
|
|
|
//
|
|
|
|
// Copyright (C) 2009 Werner Schweer and others
|
|
|
|
//
|
|
|
|
// This program is free software; you can redistribute it and/or modify
|
|
|
|
// it under the terms of the GNU General Public License version 2.
|
|
|
|
//
|
|
|
|
// 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
//=============================================================================
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
#include <sndfile.h>
|
|
|
|
#include "libmscore/score.h"
|
|
|
|
#include "libmscore/note.h"
|
|
|
|
#include "libmscore/part.h"
|
|
|
|
#include "libmscore/mscore.h"
|
2013-04-02 20:46:07 +02:00
|
|
|
#include "synthesizer/msynthesizer.h"
|
|
|
|
#include "musescore.h"
|
|
|
|
#include "preferences.h"
|
2012-05-26 14:49:10 +02:00
|
|
|
|
2013-05-13 18:49:17 +02:00
|
|
|
namespace Ms {
|
|
|
|
|
|
|
|
#ifdef HAS_AUDIOFILE
|
|
|
|
|
2012-05-26 14:49:10 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// saveAudio
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
bool MuseScore::saveAudio(Score* score, const QString& name, const QString& ext)
|
|
|
|
{
|
|
|
|
int format;
|
|
|
|
if (ext == "wav")
|
|
|
|
format = SF_FORMAT_WAV | SF_FORMAT_PCM_16;
|
|
|
|
else if (ext == "ogg")
|
|
|
|
format = SF_FORMAT_OGG | SF_FORMAT_VORBIS;
|
|
|
|
else if (ext == "flac")
|
|
|
|
format = SF_FORMAT_FLAC | SF_FORMAT_PCM_16;
|
|
|
|
else {
|
|
|
|
qDebug("unknown audio file type <%s>\n", qPrintable(ext));
|
|
|
|
return false;
|
|
|
|
}
|
2013-05-06 21:35:08 +02:00
|
|
|
|
|
|
|
EventMap events;
|
|
|
|
score->renderMidi(&events);
|
|
|
|
if(events.size() == 0)
|
|
|
|
return false;
|
|
|
|
|
2013-04-02 20:46:07 +02:00
|
|
|
MasterSynthesizer* synti = synthesizerFactory();
|
2013-04-24 15:37:55 +02:00
|
|
|
synti->init();
|
2012-05-26 14:49:10 +02:00
|
|
|
int sampleRate = preferences.exportAudioSampleRate;
|
2013-03-26 19:59:51 +01:00
|
|
|
synti->setSampleRate(sampleRate);
|
2013-04-02 20:46:07 +02:00
|
|
|
synti->setState(score->synthesizerState());
|
2012-05-26 14:49:10 +02:00
|
|
|
|
2013-03-27 17:16:24 +01:00
|
|
|
int oldSampleRate = MScore::sampleRate;
|
2012-06-21 11:21:25 +02:00
|
|
|
MScore::sampleRate = sampleRate;
|
|
|
|
|
2012-05-26 14:49:10 +02:00
|
|
|
SF_INFO info;
|
|
|
|
memset(&info, 0, sizeof(info));
|
|
|
|
info.channels = 2;
|
|
|
|
info.samplerate = sampleRate;
|
|
|
|
info.format = format;
|
|
|
|
SNDFILE* sf = sf_open(qPrintable(name), SFM_WRITE, &info);
|
|
|
|
if (sf == 0) {
|
|
|
|
qDebug("open soundfile failed: %s\n", sf_strerror(sf));
|
|
|
|
delete synti;
|
2012-06-21 11:21:25 +02:00
|
|
|
MScore::sampleRate = oldSampleRate;
|
2012-05-26 14:49:10 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
QProgressBar* pBar = showProgressBar();
|
|
|
|
pBar->reset();
|
|
|
|
|
2013-04-04 12:12:10 +02:00
|
|
|
float peak = 0.0;
|
2012-05-26 14:49:10 +02:00
|
|
|
double gain = 1.0;
|
2013-04-08 10:31:17 +02:00
|
|
|
EventMap::const_iterator endPos = events.cend();
|
2012-05-26 14:49:10 +02:00
|
|
|
--endPos;
|
2013-04-08 10:31:17 +02:00
|
|
|
const int et = (score->utick2utime(endPos->first) + 1) * MScore::sampleRate;
|
2013-04-18 16:54:51 +02:00
|
|
|
pBar->setRange(0, et);
|
|
|
|
|
2012-05-26 14:49:10 +02:00
|
|
|
for (int pass = 0; pass < 2; ++pass) {
|
|
|
|
EventMap::const_iterator playPos;
|
2013-04-08 10:31:17 +02:00
|
|
|
playPos = events.cbegin();
|
2012-05-26 14:49:10 +02:00
|
|
|
|
|
|
|
//
|
|
|
|
// init instruments
|
|
|
|
//
|
|
|
|
foreach(const Part* part, score->parts()) {
|
|
|
|
foreach(const Channel& a, part->instr()->channel()) {
|
|
|
|
a.updateInitList();
|
2013-04-25 09:50:55 +02:00
|
|
|
foreach(MidiCoreEvent e, a.init) {
|
2012-05-26 14:49:10 +02:00
|
|
|
if (e.type() == ME_INVALID)
|
|
|
|
continue;
|
|
|
|
e.setChannel(a.channel);
|
2013-04-04 12:12:10 +02:00
|
|
|
int syntiIdx= synti->index(score->midiMapping(a.channel)->articulation->synti);
|
2012-05-26 14:49:10 +02:00
|
|
|
synti->play(e, syntiIdx);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static const unsigned FRAMES = 512;
|
|
|
|
float buffer[FRAMES * 2];
|
|
|
|
int playTime = 0;
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
unsigned frames = FRAMES;
|
|
|
|
//
|
|
|
|
// collect events for one segment
|
|
|
|
//
|
|
|
|
memset(buffer, 0, sizeof(float) * FRAMES * 2);
|
|
|
|
int endTime = playTime + frames;
|
|
|
|
float* p = buffer;
|
2013-04-08 10:31:17 +02:00
|
|
|
for (; playPos != events.cend(); ++playPos) {
|
|
|
|
int f = score->utick2utime(playPos->first) * MScore::sampleRate;
|
2012-05-26 14:49:10 +02:00
|
|
|
if (f >= endTime)
|
|
|
|
break;
|
|
|
|
int n = f - playTime;
|
2013-04-04 12:12:10 +02:00
|
|
|
if (n) {
|
|
|
|
synti->process(n, p);
|
|
|
|
p += 2 * n;
|
|
|
|
}
|
2012-05-26 14:49:10 +02:00
|
|
|
|
|
|
|
playTime += n;
|
|
|
|
frames -= n;
|
2013-04-25 09:50:55 +02:00
|
|
|
const NPlayEvent& e = playPos->second;
|
2012-05-26 14:49:10 +02:00
|
|
|
if (e.isChannelEvent()) {
|
|
|
|
int channelIdx = e.channel();
|
|
|
|
Channel* c = score->midiMapping(channelIdx)->articulation;
|
|
|
|
if (!c->mute) {
|
2013-04-04 12:12:10 +02:00
|
|
|
synti->play(e, synti->index(c->synti));
|
2012-05-26 14:49:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (frames) {
|
|
|
|
synti->process(frames, p);
|
|
|
|
playTime += frames;
|
|
|
|
}
|
2013-03-27 17:16:24 +01:00
|
|
|
if (pass == 1) {
|
|
|
|
for (unsigned i = 0; i < FRAMES * 2; ++i)
|
|
|
|
buffer[i] *= gain;
|
2012-05-26 14:49:10 +02:00
|
|
|
sf_writef_float(sf, buffer, FRAMES);
|
2013-03-27 17:16:24 +01:00
|
|
|
}
|
2012-05-26 14:49:10 +02:00
|
|
|
else {
|
|
|
|
for (unsigned i = 0; i < FRAMES * 2; ++i)
|
|
|
|
peak = qMax(peak, qAbs(buffer[i]));
|
|
|
|
}
|
|
|
|
playTime = endTime;
|
2013-04-18 16:54:51 +02:00
|
|
|
pBar->setValue((pass * et + playTime) / 2);
|
2013-03-27 17:16:24 +01:00
|
|
|
|
2012-05-26 14:49:10 +02:00
|
|
|
if (playTime >= et)
|
|
|
|
break;
|
|
|
|
}
|
2013-04-04 12:12:10 +02:00
|
|
|
if (pass == 0 && peak == 0.0) {
|
|
|
|
qDebug("song is empty");
|
|
|
|
break;
|
|
|
|
}
|
2012-05-26 14:49:10 +02:00
|
|
|
gain = 0.99 / peak;
|
|
|
|
}
|
|
|
|
|
|
|
|
hideProgressBar();
|
|
|
|
|
2012-06-21 11:21:25 +02:00
|
|
|
MScore::sampleRate = oldSampleRate;
|
2012-05-26 14:49:10 +02:00
|
|
|
delete synti;
|
|
|
|
if (sf_close(sf)) {
|
|
|
|
qDebug("close soundfile failed\n");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif // HAS_AUDIOFILE
|
2013-05-13 18:49:17 +02:00
|
|
|
}
|
2012-05-26 14:49:10 +02:00
|
|
|
|