mirror of
https://github.com/ddnet/ddnet.git
synced 2024-09-20 01:24:18 +00:00
Merge #6765
6765: Add play/stop button and duration label for editor sounds r=def- a=Robyt3 Add button to play/stop audio preview and label showing selected sound duration in editor sound list and sound file browser. Show error message instead of preview in editor image/sound file browser when selected file cannot be loaded. Screenshots: - ![screenshot_2023-06-25_21-53-14](https://github.com/ddnet/ddnet/assets/23437060/a2eb810c-e132-4fa8-9d0d-730b78088651) - ![screenshot_2023-06-25_21-53-01](https://github.com/ddnet/ddnet/assets/23437060/1a742abc-51f9-4526-aada-d941d42e481d) - ![screenshot_2023-06-25_21-53-05](https://github.com/ddnet/ddnet/assets/23437060/bf500772-ab6b-409f-8cc4-1bfbaeae55eb) ## Checklist - [X] Tested the change ingame - [X] Provided screenshots if it is a visual change - [ ] Tested in combination with possibly related configuration options - [ ] Written a unit test (especially base/) or added coverage to integration test - [X] Considered possible null pointers and out of bounds array indexing - [X] Changed no physics that affect existing maps - [ ] Tested the change with [ASan+UBSan or valgrind's memcheck](https://github.com/ddnet/ddnet/#using-addresssanitizer--undefinedbehavioursanitizer-or-valgrinds-memcheck) (optional) Co-authored-by: Robert Müller <robytemueller@gmail.com>
This commit is contained in:
commit
d840233951
|
@ -576,7 +576,7 @@ int CSound::DecodeWV(int SampleID, const void *pData, unsigned DataSize)
|
|||
return SampleID;
|
||||
}
|
||||
|
||||
int CSound::LoadOpus(const char *pFilename)
|
||||
int CSound::LoadOpus(const char *pFilename, int StorageType)
|
||||
{
|
||||
// don't waste memory on sound when we are stress testing
|
||||
#ifdef CONF_DEBUG
|
||||
|
@ -600,7 +600,7 @@ int CSound::LoadOpus(const char *pFilename)
|
|||
|
||||
void *pData;
|
||||
unsigned DataSize;
|
||||
if(!m_pStorage->ReadFile(pFilename, IStorage::TYPE_ALL, &pData, &DataSize))
|
||||
if(!m_pStorage->ReadFile(pFilename, StorageType, &pData, &DataSize))
|
||||
{
|
||||
dbg_msg("sound/opus", "failed to open file. filename='%s'", pFilename);
|
||||
return -1;
|
||||
|
@ -618,7 +618,7 @@ int CSound::LoadOpus(const char *pFilename)
|
|||
return SampleID;
|
||||
}
|
||||
|
||||
int CSound::LoadWV(const char *pFilename)
|
||||
int CSound::LoadWV(const char *pFilename, int StorageType)
|
||||
{
|
||||
// don't waste memory on sound when we are stress testing
|
||||
#ifdef CONF_DEBUG
|
||||
|
@ -642,7 +642,7 @@ int CSound::LoadWV(const char *pFilename)
|
|||
|
||||
void *pData;
|
||||
unsigned DataSize;
|
||||
if(!m_pStorage->ReadFile(pFilename, IStorage::TYPE_ALL, &pData, &DataSize))
|
||||
if(!m_pStorage->ReadFile(pFilename, StorageType, &pData, &DataSize))
|
||||
{
|
||||
dbg_msg("sound/wv", "failed to open file. filename='%s'", pFilename);
|
||||
return -1;
|
||||
|
|
|
@ -34,9 +34,9 @@ public:
|
|||
|
||||
bool IsSoundEnabled() override { return m_SoundEnabled; }
|
||||
|
||||
int LoadWV(const char *pFilename) override;
|
||||
int LoadWV(const char *pFilename, int StorageType = IStorage::TYPE_ALL) override;
|
||||
int LoadWVFromMem(const void *pData, unsigned DataSize, bool FromEditor) override;
|
||||
int LoadOpus(const char *pFilename) override;
|
||||
int LoadOpus(const char *pFilename, int StorageType = IStorage::TYPE_ALL) override;
|
||||
int LoadOpusFromMem(const void *pData, unsigned DataSize, bool FromEditor) override;
|
||||
void UnloadSample(int SampleID) override;
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include "kernel.h"
|
||||
|
||||
#include <engine/shared/video.h>
|
||||
#include <engine/storage.h>
|
||||
|
||||
class ISound : public IInterface
|
||||
{
|
||||
|
@ -63,8 +64,8 @@ public:
|
|||
|
||||
virtual bool IsSoundEnabled() = 0;
|
||||
|
||||
virtual int LoadWV(const char *pFilename) = 0;
|
||||
virtual int LoadOpus(const char *pFilename) = 0;
|
||||
virtual int LoadWV(const char *pFilename, int StorageType = IStorage::TYPE_ALL) = 0;
|
||||
virtual int LoadOpus(const char *pFilename, int StorageType = IStorage::TYPE_ALL) = 0;
|
||||
virtual int LoadWVFromMem(const void *pData, unsigned DataSize, bool FromEditor = false) = 0;
|
||||
virtual int LoadOpusFromMem(const void *pData, unsigned DataSize, bool FromEditor = false) = 0;
|
||||
virtual void UnloadSample(int SampleID) = 0;
|
||||
|
|
|
@ -875,7 +875,7 @@ bool CEditor::CallbackSaveCopyMap(const char *pFileName, int StorageType, void *
|
|||
}
|
||||
}
|
||||
|
||||
void CEditor::DoToolbar(CUIRect ToolBar)
|
||||
void CEditor::DoToolbarLayers(CUIRect ToolBar)
|
||||
{
|
||||
const bool ModPressed = Input()->ModifierIsPressed();
|
||||
const bool ShiftPressed = Input()->ShiftIsPressed();
|
||||
|
@ -1268,6 +1268,41 @@ void CEditor::DoToolbar(CUIRect ToolBar)
|
|||
}
|
||||
}
|
||||
|
||||
void CEditor::DoToolbarSounds(CUIRect ToolBar)
|
||||
{
|
||||
CUIRect ToolBarTop, ToolBarBottom, Button;
|
||||
ToolBar.HSplitMid(&ToolBarTop, &ToolBarBottom, 5.0f);
|
||||
|
||||
if(m_SelectedSound >= 0 && (size_t)m_SelectedSound < m_Map.m_vpSounds.size())
|
||||
{
|
||||
const CEditorSound *pSelectedSound = m_Map.m_vpSounds[m_SelectedSound];
|
||||
|
||||
// play/stop button
|
||||
{
|
||||
ToolBarBottom.VSplitLeft(ToolBarBottom.h, &Button, &ToolBarBottom);
|
||||
static int s_PlayStopButton;
|
||||
if(DoButton_FontIcon(&s_PlayStopButton, Sound()->IsPlaying(pSelectedSound->m_SoundID) ? FONT_ICON_STOP : FONT_ICON_PLAY, 0, &Button, 0, "Play/stop audio preview", IGraphics::CORNER_ALL) ||
|
||||
(m_Dialog == DIALOG_NONE && m_EditBoxActive == 0 && Input()->KeyPress(KEY_SPACE)))
|
||||
{
|
||||
if(Sound()->IsPlaying(pSelectedSound->m_SoundID))
|
||||
Sound()->Stop(pSelectedSound->m_SoundID);
|
||||
else
|
||||
Sound()->Play(CSounds::CHN_GUI, pSelectedSound->m_SoundID, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// duration
|
||||
{
|
||||
ToolBarBottom.VSplitLeft(5.0f, nullptr, &ToolBarBottom);
|
||||
char aDuration[32];
|
||||
char aDurationLabel[64];
|
||||
str_time_float(Sound()->GetSampleDuration(pSelectedSound->m_SoundID), TIME_HOURS, aDuration, sizeof(aDuration));
|
||||
str_format(aDurationLabel, sizeof(aDurationLabel), "Duration: %s", aDuration);
|
||||
UI()->DoLabel(&ToolBarBottom, aDurationLabel, 12.0f, TEXTALIGN_ML);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void Rotate(const CPoint *pCenter, CPoint *pPoint, float Rotation)
|
||||
{
|
||||
int x = pPoint->x - pCenter->x;
|
||||
|
@ -4575,7 +4610,7 @@ void CEditor::RenderFileDialog()
|
|||
// GUI coordsys
|
||||
UI()->MapScreen();
|
||||
CUIRect View = *UI()->Screen();
|
||||
CUIRect Preview;
|
||||
CUIRect Preview = {0.0f, 0.0f, 0.0f, 0.0f};
|
||||
float Width = View.w, Height = View.h;
|
||||
|
||||
View.Draw(ColorRGBA(0, 0, 0, 0.25f), 0, 0);
|
||||
|
@ -4594,7 +4629,7 @@ void CEditor::RenderFileDialog()
|
|||
View.HSplitBottom(14.0f, &View, &FileBox);
|
||||
FileBox.VSplitLeft(55.0f, &FileBoxLabel, &FileBox);
|
||||
View.HSplitBottom(10.0f, &View, nullptr); // some spacing
|
||||
if(m_FileDialogFileType == CEditor::FILETYPE_IMG)
|
||||
if(m_FileDialogFileType == CEditor::FILETYPE_IMG || m_FileDialogFileType == CEditor::FILETYPE_SOUND)
|
||||
View.VSplitMid(&View, &Preview);
|
||||
|
||||
// title
|
||||
|
@ -4745,7 +4780,7 @@ void CEditor::RenderFileDialog()
|
|||
m_aFilesSelectedName[0] = '\0';
|
||||
UpdateFileNameInput();
|
||||
s_ListBox.ScrollToSelected();
|
||||
m_PreviewImageState = PREVIEWIMAGE_UNLOADED;
|
||||
m_FilePreviewState = PREVIEW_UNLOADED;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4753,46 +4788,97 @@ void CEditor::RenderFileDialog()
|
|||
|
||||
if(m_FilesSelectedIndex > -1)
|
||||
{
|
||||
if(m_FileDialogFileType == CEditor::FILETYPE_IMG && m_PreviewImageState == PREVIEWIMAGE_UNLOADED && m_FilesSelectedIndex > -1)
|
||||
if(m_FilePreviewState == PREVIEW_UNLOADED)
|
||||
{
|
||||
if(str_endswith(m_vpFilteredFileList[m_FilesSelectedIndex]->m_aFilename, ".png"))
|
||||
if(m_FileDialogFileType == CEditor::FILETYPE_IMG && str_endswith(m_vpFilteredFileList[m_FilesSelectedIndex]->m_aFilename, ".png"))
|
||||
{
|
||||
char aBuffer[IO_MAX_PATH_LENGTH];
|
||||
str_format(aBuffer, sizeof(aBuffer), "%s/%s", m_pFileDialogPath, m_vpFilteredFileList[m_FilesSelectedIndex]->m_aFilename);
|
||||
if(Graphics()->LoadPNG(&m_FilePreviewImageInfo, aBuffer, IStorage::TYPE_ALL))
|
||||
if(Graphics()->LoadPNG(&m_FilePreviewImageInfo, aBuffer, m_vpFilteredFileList[m_FilesSelectedIndex]->m_StorageType))
|
||||
{
|
||||
Graphics()->UnloadTexture(&m_FilePreviewImage);
|
||||
m_FilePreviewImage = Graphics()->LoadTextureRaw(m_FilePreviewImageInfo.m_Width, m_FilePreviewImageInfo.m_Height, m_FilePreviewImageInfo.m_Format, m_FilePreviewImageInfo.m_pData, m_FilePreviewImageInfo.m_Format, 0);
|
||||
Graphics()->FreePNG(&m_FilePreviewImageInfo);
|
||||
m_PreviewImageState = PREVIEWIMAGE_LOADED;
|
||||
m_FilePreviewState = PREVIEW_LOADED;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_PreviewImageState = PREVIEWIMAGE_ERROR;
|
||||
m_FilePreviewState = PREVIEW_ERROR;
|
||||
}
|
||||
}
|
||||
else if(m_FileDialogFileType == CEditor::FILETYPE_SOUND && str_endswith(m_vpFilteredFileList[m_FilesSelectedIndex]->m_aFilename, ".opus"))
|
||||
{
|
||||
char aBuffer[IO_MAX_PATH_LENGTH];
|
||||
str_format(aBuffer, sizeof(aBuffer), "%s/%s", m_pFileDialogPath, m_vpFilteredFileList[m_FilesSelectedIndex]->m_aFilename);
|
||||
Sound()->UnloadSample(m_FilePreviewSound);
|
||||
m_FilePreviewSound = Sound()->LoadOpus(aBuffer, m_vpFilteredFileList[m_FilesSelectedIndex]->m_StorageType);
|
||||
m_FilePreviewState = m_FilePreviewSound == -1 ? PREVIEW_ERROR : PREVIEW_LOADED;
|
||||
}
|
||||
}
|
||||
if(m_FileDialogFileType == CEditor::FILETYPE_IMG && m_PreviewImageState == PREVIEWIMAGE_LOADED)
|
||||
{
|
||||
int w = m_FilePreviewImageInfo.m_Width;
|
||||
int h = m_FilePreviewImageInfo.m_Height;
|
||||
if(m_FilePreviewImageInfo.m_Width > Preview.w) // NOLINT(clang-analyzer-core.UndefinedBinaryOperatorResult)
|
||||
{
|
||||
h = m_FilePreviewImageInfo.m_Height * Preview.w / m_FilePreviewImageInfo.m_Width;
|
||||
w = Preview.w;
|
||||
}
|
||||
if(h > Preview.h)
|
||||
{
|
||||
w = w * Preview.h / h;
|
||||
h = Preview.h;
|
||||
}
|
||||
|
||||
Graphics()->TextureSet(m_FilePreviewImage);
|
||||
Graphics()->BlendNormal();
|
||||
Graphics()->QuadsBegin();
|
||||
IGraphics::CQuadItem QuadItem(Preview.x, Preview.y, w, h);
|
||||
Graphics()->QuadsDrawTL(&QuadItem, 1);
|
||||
Graphics()->QuadsEnd();
|
||||
if(m_FileDialogFileType == CEditor::FILETYPE_IMG)
|
||||
{
|
||||
Preview.Margin(10.0f, &Preview);
|
||||
if(m_FilePreviewState == PREVIEW_LOADED)
|
||||
{
|
||||
int w = m_FilePreviewImageInfo.m_Width;
|
||||
int h = m_FilePreviewImageInfo.m_Height;
|
||||
if(m_FilePreviewImageInfo.m_Width > Preview.w)
|
||||
{
|
||||
h = m_FilePreviewImageInfo.m_Height * Preview.w / m_FilePreviewImageInfo.m_Width;
|
||||
w = Preview.w;
|
||||
}
|
||||
if(h > Preview.h)
|
||||
{
|
||||
w = w * Preview.h / h;
|
||||
h = Preview.h;
|
||||
}
|
||||
|
||||
Graphics()->TextureSet(m_FilePreviewImage);
|
||||
Graphics()->BlendNormal();
|
||||
Graphics()->QuadsBegin();
|
||||
IGraphics::CQuadItem QuadItem(Preview.x, Preview.y, w, h);
|
||||
Graphics()->QuadsDrawTL(&QuadItem, 1);
|
||||
Graphics()->QuadsEnd();
|
||||
}
|
||||
else if(m_FilePreviewState == PREVIEW_ERROR)
|
||||
{
|
||||
SLabelProperties Props;
|
||||
Props.m_MaxWidth = Preview.w;
|
||||
UI()->DoLabel(&Preview, "Failed to load the image (check the local console for details).", 12.0f, TEXTALIGN_TL, Props);
|
||||
}
|
||||
}
|
||||
else if(m_FileDialogFileType == CEditor::FILETYPE_SOUND)
|
||||
{
|
||||
Preview.Margin(10.0f, &Preview);
|
||||
if(m_FilePreviewState == PREVIEW_LOADED)
|
||||
{
|
||||
CUIRect Button;
|
||||
Preview.HSplitTop(20.0f, &Preview, nullptr);
|
||||
Preview.VSplitLeft(Preview.h, &Button, &Preview);
|
||||
Preview.VSplitLeft(Preview.h / 4.0f, nullptr, &Preview);
|
||||
|
||||
static int s_PlayStopButton;
|
||||
if(DoButton_FontIcon(&s_PlayStopButton, Sound()->IsPlaying(m_FilePreviewSound) ? FONT_ICON_STOP : FONT_ICON_PLAY, 0, &Button, 0, "Play/stop audio preview", IGraphics::CORNER_ALL))
|
||||
{
|
||||
if(Sound()->IsPlaying(m_FilePreviewSound))
|
||||
Sound()->Stop(m_FilePreviewSound);
|
||||
else
|
||||
Sound()->Play(CSounds::CHN_GUI, m_FilePreviewSound, 0);
|
||||
}
|
||||
|
||||
char aDuration[32];
|
||||
char aDurationLabel[64];
|
||||
str_time_float(Sound()->GetSampleDuration(m_FilePreviewSound), TIME_HOURS, aDuration, sizeof(aDuration));
|
||||
str_format(aDurationLabel, sizeof(aDurationLabel), "Duration: %s", aDuration);
|
||||
UI()->DoLabel(&Preview, aDurationLabel, 12.0f, TEXTALIGN_ML);
|
||||
}
|
||||
else if(m_FilePreviewState == PREVIEW_ERROR)
|
||||
{
|
||||
SLabelProperties Props;
|
||||
Props.m_MaxWidth = Preview.w;
|
||||
UI()->DoLabel(&Preview, "Failed to load the sound (check the local console for details). Make sure you enabled sounds in the settings.", 12.0f, TEXTALIGN_TL, Props);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4858,7 +4944,7 @@ void CEditor::RenderFileDialog()
|
|||
UpdateFileNameInput();
|
||||
if(!WasChanged) // ensure that changed flag is not set if it wasn't previously set, as this would reset the selection after DoEditBox is called
|
||||
m_FileDialogFileNameInput.WasChanged(); // this clears the changed flag
|
||||
m_PreviewImageState = PREVIEWIMAGE_UNLOADED;
|
||||
m_FilePreviewState = PREVIEW_UNLOADED;
|
||||
}
|
||||
|
||||
const float ButtonSpacing = ButtonBar.w > 600.0f ? 40.0f : 10.0f;
|
||||
|
@ -5053,7 +5139,7 @@ void CEditor::FilelistPopulate(int StorageType, bool KeepSelection)
|
|||
else
|
||||
m_aFilesSelectedName[0] = '\0';
|
||||
}
|
||||
m_PreviewImageState = PREVIEWIMAGE_UNLOADED;
|
||||
m_FilePreviewState = PREVIEW_UNLOADED;
|
||||
}
|
||||
|
||||
void CEditor::InvokeFileDialog(int StorageType, int FileType, const char *pTitle, const char *pButtonText,
|
||||
|
@ -5072,7 +5158,7 @@ void CEditor::InvokeFileDialog(int StorageType, int FileType, const char *pTitle
|
|||
m_aFileDialogCurrentLink[0] = 0;
|
||||
m_pFileDialogPath = m_aFileDialogCurrentFolder;
|
||||
m_FileDialogFileType = FileType;
|
||||
m_PreviewImageState = PREVIEWIMAGE_UNLOADED;
|
||||
m_FilePreviewState = PREVIEW_UNLOADED;
|
||||
m_FileDialogOpening = true;
|
||||
|
||||
if(pDefaultName)
|
||||
|
@ -6180,7 +6266,9 @@ void CEditor::Render()
|
|||
|
||||
// do the toolbar
|
||||
if(m_Mode == MODE_LAYERS)
|
||||
DoToolbar(ToolBar);
|
||||
DoToolbarLayers(ToolBar);
|
||||
else if(m_Mode == MODE_SOUNDS)
|
||||
DoToolbarSounds(ToolBar);
|
||||
|
||||
if(m_Dialog == DIALOG_NONE && m_EditBoxActive == 0)
|
||||
{
|
||||
|
|
|
@ -710,11 +710,11 @@ class CEditor : public IEditor
|
|||
|
||||
int GetTextureUsageFlag();
|
||||
|
||||
enum EPreviewImageState
|
||||
enum EPreviewState
|
||||
{
|
||||
PREVIEWIMAGE_UNLOADED,
|
||||
PREVIEWIMAGE_LOADED,
|
||||
PREVIEWIMAGE_ERROR,
|
||||
PREVIEW_UNLOADED,
|
||||
PREVIEW_LOADED,
|
||||
PREVIEW_ERROR,
|
||||
};
|
||||
|
||||
public:
|
||||
|
@ -775,7 +775,8 @@ public:
|
|||
m_FilesSelectedIndex = -1;
|
||||
|
||||
m_FilePreviewImage.Invalidate();
|
||||
m_PreviewImageState = PREVIEWIMAGE_UNLOADED;
|
||||
m_FilePreviewSound = -1;
|
||||
m_FilePreviewState = PREVIEW_UNLOADED;
|
||||
|
||||
m_SelectEntitiesImage = "DDNet";
|
||||
|
||||
|
@ -945,8 +946,10 @@ public:
|
|||
int m_FileDialogFileType;
|
||||
int m_FilesSelectedIndex;
|
||||
CLineInputBuffered<IO_MAX_PATH_LENGTH> m_FileDialogNewFolderNameInput;
|
||||
|
||||
IGraphics::CTextureHandle m_FilePreviewImage;
|
||||
EPreviewImageState m_PreviewImageState;
|
||||
int m_FilePreviewSound;
|
||||
EPreviewState m_FilePreviewState;
|
||||
CImageInfo m_FilePreviewImageInfo;
|
||||
bool m_FileDialogOpening;
|
||||
|
||||
|
@ -1212,7 +1215,8 @@ public:
|
|||
void DoSoundSource(CSoundSource *pSource, int Index);
|
||||
|
||||
void DoMapEditor(CUIRect View);
|
||||
void DoToolbar(CUIRect Toolbar);
|
||||
void DoToolbarLayers(CUIRect Toolbar);
|
||||
void DoToolbarSounds(CUIRect Toolbar);
|
||||
void DoQuad(CQuad *pQuad, int Index);
|
||||
ColorRGBA GetButtonColor(const void *pID, int Checked);
|
||||
|
||||
|
|
Loading…
Reference in a new issue