diff --git a/stuff/profiles/layouts/rooms/Default/menubar_template.xml b/stuff/profiles/layouts/rooms/Default/menubar_template.xml
index 6d797d7..86114c8 100644
--- a/stuff/profiles/layouts/rooms/Default/menubar_template.xml
+++ b/stuff/profiles/layouts/rooms/Default/menubar_template.xml
@@ -35,6 +35,7 @@
MI_OutputSettings
MI_Render
MI_FastRender
+ MI_SoundTrack
MI_PrintXsheet
MI_Print
diff --git a/toonz/sources/toonz/CMakeLists.txt b/toonz/sources/toonz/CMakeLists.txt
index 31d408d..50f369d 100644
--- a/toonz/sources/toonz/CMakeLists.txt
+++ b/toonz/sources/toonz/CMakeLists.txt
@@ -108,6 +108,7 @@ set(MOC_HEADERS
selectionutils.h
shifttracetool.h
shortcutpopup.h
+ soundtrackexport.h
startuppopup.h
subcameramanager.h
subscenecommand.h
@@ -252,6 +253,7 @@ set(SOURCES
scriptconsolepanel.cpp
shifttracetool.cpp
shortcutpopup.cpp
+ soundtrackexport.cpp
startuppopup.cpp
subcameramanager.cpp
timestretchpopup.cpp
diff --git a/toonz/sources/toonz/mainwindow.cpp b/toonz/sources/toonz/mainwindow.cpp
index db3a547..40f334d 100644
--- a/toonz/sources/toonz/mainwindow.cpp
+++ b/toonz/sources/toonz/mainwindow.cpp
@@ -1642,6 +1642,7 @@ void MainWindow::defineActions() {
createMenuFileAction(MI_Render, tr("&Render"), "Ctrl+Shift+R");
createMenuFileAction(MI_FastRender, tr("&Fast Render to MP4"), "Alt+R");
createMenuFileAction(MI_Preview, tr("&Preview"), "Ctrl+R");
+ createMenuFileAction(MI_SoundTrack, tr("&Export Soundtrack"), "");
createRightClickMenuAction(MI_SavePreviewedFrames,
tr("&Save Previewed Frames"), "");
createRightClickMenuAction(MI_RegeneratePreview, tr("&Regenerate Preview"),
diff --git a/toonz/sources/toonz/menubar.cpp b/toonz/sources/toonz/menubar.cpp
index 65d241a..71ddccb 100644
--- a/toonz/sources/toonz/menubar.cpp
+++ b/toonz/sources/toonz/menubar.cpp
@@ -1100,6 +1100,7 @@ QMenuBar *StackedMenuBar::createFullMenuBar() {
addMenuItem(fileMenu, MI_OutputSettings);
addMenuItem(fileMenu, MI_Render);
addMenuItem(fileMenu, MI_FastRender);
+ addMenuItem(fileMenu, MI_SoundTrack);
// addMenuItem(fileMenu, MI_SavePreviewedFrames);
fileMenu->addSeparator();
addMenuItem(fileMenu, MI_PrintXsheet);
diff --git a/toonz/sources/toonz/menubarcommandids.h b/toonz/sources/toonz/menubarcommandids.h
index eea6be2..7e56d69 100644
--- a/toonz/sources/toonz/menubarcommandids.h
+++ b/toonz/sources/toonz/menubarcommandids.h
@@ -48,6 +48,7 @@
#define MI_Render "MI_Render"
#define MI_FastRender "MI_FastRender"
#define MI_Preview "MI_Preview"
+#define MI_SoundTrack "MI_SoundTrack"
#define MI_RegeneratePreview "MI_RegeneratePreview"
#define MI_RegenerateFramePr "MI_RegenerateFramePr"
#define MI_ClonePreview "MI_ClonePreview"
diff --git a/toonz/sources/toonz/soundtrackexport.cpp b/toonz/sources/toonz/soundtrackexport.cpp
new file mode 100644
index 0000000..31bc866
--- /dev/null
+++ b/toonz/sources/toonz/soundtrackexport.cpp
@@ -0,0 +1,121 @@
+
+#include "soundtrackexport.h"
+
+#include "menubarcommandids.h"
+#include "tapp.h"
+#include "tsop.h"
+
+// TnzQt includes
+#include "toonzqt/menubarcommand.h"
+
+// TnzLib includes
+#include "toonz/tscenehandle.h"
+#include "toonz/txsheethandle.h"
+#include "toonz/toonzscene.h"
+#include "toonz/txsheet.h"
+#include "toonz/tstageobjecttree.h"
+
+// TnzCore includes
+#include "tsystem.h"
+#include "toutputproperties.h"
+#include "filebrowserpopup.h"
+#include "tsound_io.h"
+
+//-----------------------------------------------------------------------------
+
+SoundtrackExport::SoundtrackExport() {
+ m_scene = TApp::instance()->getCurrentScene()->getScene();
+}
+
+//-----------------------------------------------------------------------------
+
+bool SoundtrackExport::hasSoundTrack() {
+ TXsheetHandle *xsheetHandle = TApp::instance()->getCurrentXsheet();
+ TXsheet::SoundProperties *prop = new TXsheet::SoundProperties();
+ m_sound = xsheetHandle->getXsheet()->makeSound(prop);
+ if (m_sound == NULL) {
+ m_hasSoundtrack = false;
+ return false;
+ } else {
+ m_hasSoundtrack = true;
+ return true;
+ }
+}
+
+void SoundtrackExport::makeSoundtrack(int r0, int r1, double fps) {
+ TCG_ASSERT(r0 <= r1, return );
+ long m_whiteSample;
+ double samplePerFrame = m_sound->getSampleRate() / fps;
+
+ // Extract the useful part of scene soundtrack
+ TSoundTrackP snd1 = m_sound->extract((TINT32)(r0 * samplePerFrame),
+ (TINT32)(r1 * samplePerFrame));
+
+ assert(!m_st);
+ if (!m_st) {
+ m_whiteSample = 0;
+ // First, add white sound before the 'from' instant
+ m_st = TSoundTrack::create(snd1->getFormat(), m_whiteSample);
+ m_whiteSample = 0;
+ }
+
+ // Then, add the rest
+ TINT32 fromSample = m_st->getSampleCount();
+ TINT32 numSample =
+ std::max(TINT32((r1 - r0 + 1) * samplePerFrame), snd1->getSampleCount());
+
+ m_st = TSop::insertBlank(m_st, fromSample, numSample + m_whiteSample);
+ m_st->copy(snd1, TINT32(fromSample + m_whiteSample));
+
+ m_whiteSample = 0;
+}
+
+void SoundtrackExport::saveSoundtrack() {
+ if (!m_st) {
+ GenericSaveFilePopup *m_saveShortcutsPopup =
+ new GenericSaveFilePopup("Export Soundtrack");
+ m_saveShortcutsPopup->addFilterType("wav");
+ TFilePath fp = m_saveShortcutsPopup->getPath();
+ if (fp == TFilePath()) return;
+
+ TSceneProperties *sprop = m_scene->getProperties();
+
+ int step;
+ int from, to;
+ sprop->getOutputProperties()->getRange(from, to, step);
+ if (to < 0) {
+ TXsheet *xs = m_scene->getXsheet();
+
+ // NOTE: Use of numeric_limits::min is justified since the type is
+ // *INTERGRAL*.
+ from = (std::numeric_limits::max)(),
+ to = (std::numeric_limits::min)();
+
+ for (int k = 0; k < xs->getColumnCount(); ++k) {
+ int r0, r1;
+ xs->getCellRange(k, r0, r1);
+
+ from = std::min(from, r0), to = std::max(to, r1);
+ }
+ }
+ makeSoundtrack(
+ from, to,
+ m_scene->getProperties()->getOutputProperties()->getFrameRate());
+ TSoundTrackWriter::save(fp, m_st);
+ }
+}
+
+//-----------------------------------------------------------------------------
+
+class ExportSoundtrackCommandHandler final : public MenuItemHandler {
+public:
+ ExportSoundtrackCommandHandler() : MenuItemHandler(MI_SoundTrack) {}
+ void execute() override {
+ SoundtrackExport soundSettings;
+ if (soundSettings.hasSoundTrack())
+ soundSettings.saveSoundtrack();
+ else {
+ DVGui::warning("There is no sound to be exported.");
+ }
+ }
+} ExportSoundtrackCommandHandler;
diff --git a/toonz/sources/toonz/soundtrackexport.h b/toonz/sources/toonz/soundtrackexport.h
new file mode 100644
index 0000000..849b26b
--- /dev/null
+++ b/toonz/sources/toonz/soundtrackexport.h
@@ -0,0 +1,34 @@
+#pragma once
+
+#ifndef SOUNDTRACKEXPORT_H
+#define SOUNDTRACKEXPORT_H
+
+#include "toonzqt/dvdialog.h"
+#include "toonz/sceneproperties.h"
+#include "tsound.h"
+
+// forward declaration
+class ToonzScene;
+
+//=============================================================================
+// SoundtrackExport
+//-----------------------------------------------------------------------------
+
+class SoundtrackExport : public QWidget {
+ Q_OBJECT
+
+public:
+ SoundtrackExport();
+ bool hasSoundTrack();
+ void makeSoundtrack(int r0, int r1, double fps);
+ void saveSoundtrack();
+
+protected:
+ bool m_hasSoundtrack;
+ TSoundTrackP m_st;
+ TSoundTrack *m_sound;
+ ToonzScene *m_scene;
+ TFilePath m_fp;
+};
+
+#endif // SOUNDTRACKEXPORT_H