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