Side panel added

This commit is contained in:
IlyaShurupov 2023-11-29 14:36:59 +03:00 committed by Ilya Shurupov
parent dd71eba0ec
commit a233b6a214
9 changed files with 366 additions and 25 deletions

View file

@ -1,17 +1,24 @@
project(LibraryViewer)
find_package(ALSA REQUIRED)
### ---------------------- Externals --------------------- ###
set(BINDINGS_INCLUDE ../Externals/glfw/include ../Externals/glew/include)
set(BINDINGS_LIBS glfw glew_s Imgui Nanovg)
### ---------------------- Static Library --------------------- ###
file(GLOB SOURCES "./private/*.cpp" "./private/*/*.cpp")
file(GLOB HEADERS "./public/*.hpp" "./public/*/*.hpp" "./applications/*.hpp")
add_library(${PROJECT_NAME} STATIC ${SOURCES} ${HEADERS})
target_include_directories(${PROJECT_NAME} PUBLIC ./public/)
target_include_directories(${PROJECT_NAME} PUBLIC ./public/ ${BINDINGS_INCLUDE})
target_link_libraries(${PROJECT_NAME} PUBLIC Graphics Connection)
target_link_libraries(${PROJECT_NAME} PUBLIC ${BINDINGS_LIBS})
file(COPY "library" DESTINATION "${CMAKE_BINARY_DIR}/${PROJECT_NAME}/")
file(COPY "applications/Font.ttf" DESTINATION "${CMAKE_BINARY_DIR}/${PROJECT_NAME}/")
### -------------------------- Applications -------------------------- ###
add_executable(libView ./applications/Entry.cpp)
target_link_libraries(libView ${PROJECT_NAME} LibraryViewer)
target_link_libraries(libView ${PROJECT_NAME} ${ALSA_LIBRARY})

View file

@ -1,9 +1,8 @@
#include "NewPlacement.hpp"
// #include "NewPlacement.hpp"
#include "WavPlayer.hpp"
#include "LibraryGui.hpp"
// events
// animations
// monitor resource usage
// sorting
@ -33,6 +32,8 @@ void runApp() {
}
int main() {
// example();
tp::ModuleManifest* deps[] = { &tp::gModuleLibraryViewer, nullptr };
tp::ModuleManifest binModule("LibViewEntry", nullptr, nullptr, deps);

Binary file not shown.

View file

@ -0,0 +1,138 @@
#include "WavPlayer.hpp"
#include <chrono>
#include <fstream>
#include <iostream>
#include <math.h>
WavPlayer::WavPlayer() :
handle(nullptr),
playbackThread(nullptr),
isPlaying(false),
progress(0.0f) {
if (snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, 0) < 0) {
std::cerr << "Error: Couldn't open ALSA PCM device." << std::endl;
}
}
WavPlayer::~WavPlayer() {
stopSong();
if (handle) {
snd_pcm_close(handle);
}
}
void WavPlayer::loadSong(const char* filePath) {
stopSong();
if (snd_pcm_prepare(handle) < 0) {
std::cerr << "Error: Couldn't prepare PCM." << std::endl;
return;
}
// TODO: Load and parse the WAV file
// For simplicity, assume a 16-bit, mono WAV file with a 44.1 kHz sample rate
// You need to handle loading and parsing of the WAV file here
// Example:
// std::ifstream file(filePath, std::ios::binary);
// if (!file) {
// std::cerr << "Error: Could not open WAV file." << std::endl;
// return;
// }
// ... (load and parse WAV file)
snd_pcm_set_params(handle, SND_PCM_FORMAT_S16_LE, SND_PCM_ACCESS_RW_INTERLEAVED, 1, 44100, 1, 500000);
}
void WavPlayer::playSong() {
if (!isPlaying) {
isPlaying = true;
playbackThread = new std::thread(&WavPlayer::playbackThreadFunction, this);
}
}
void WavPlayer::stopSong() {
if (isPlaying) {
isPlaying = false;
if (playbackThread && playbackThread->joinable()) {
playbackThread->join();
}
delete playbackThread;
playbackThread = nullptr;
snd_pcm_drop(handle);
}
}
void WavPlayer::setVolume(float volume) {
// TODO: Adjust volume using ALSA API
// You might need to use functions like snd_pcm_volume() or other ALSA functions
}
float WavPlayer::getProgress() const { return progress; }
float WavPlayer::getDurationSec() const {
// TODO: Return the duration of the loaded song
// You need information from the loaded WAV file for this
return 0.0f;
}
void WavPlayer::setProgress(float newProgress) {
if (newProgress >= 0.0f && newProgress <= 1.0f) {
progress = newProgress;
// TODO: Adjust playback position based on the progress using ALSA API
// You might need to use functions like snd_pcm_writei() or other ALSA functions
}
}
void WavPlayer::playbackThreadFunction() {
const int buffer_size = 1024; // Adjust the buffer size according to your requirements
short buffer[buffer_size];
while (isPlaying) {
// TODO: Implement actual playback logic using ALSA API
// You might need to use functions like snd_pcm_writei() or other ALSA functions
// For simplicity, we'll fill the buffer with dummy data
for (int i = 0; i < buffer_size; ++i) {
buffer[i] = static_cast<short>(32767 * std::sin(2.0 * M_PI * 440.0 * progress));
}
// Write the buffer to the PCM device
int err = snd_pcm_writei(handle, buffer, buffer_size);
if (err < 0) {
std::cerr << "Error writing to PCM device: " << snd_strerror(err) << std::endl;
stopSong();
return;
}
// Update progress
progress += 0.1f;
if (progress >= 1.0f) {
stopSong();
}
// Sleep for a short duration (adjust as needed)
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
}
void example() {
WavPlayer player;
// Load a WAV file
player.loadSong("path/to/your/file.wav");
// Start playing the song asynchronously
player.playSong();
// Simulate a main application loop (replace this with your actual application logic)
for (int i = 0; i < 30; ++i) {
std::cout << "Progress: " << player.getProgress() << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(1));
}
// Stop the song playback
player.stopSong();
}

View file

@ -3,10 +3,143 @@
#include "LibraryViewer.hpp"
#include "Rect.hpp"
#include "Animations.hpp"
#include "imgui.h"
template <typename T>
concept DrawableConcept = true;
template <typename Events, typename Canvas> requires(DrawableConcept<Canvas>)
class ResizerWidget {
public:
ResizerWidget() {}
void proc(const Events& events, const tp::RectF& areaParent, const tp::RectF& area) {
if (!areaParent.isOverlap(area)) {
resizing = false;
return;
}
hover = area.isInside(events.getPos());
if (events.isPressed() && hover) {
resizing = true;
} else if (!events.isDown()) {
resizing = false;
}
value = area.x;
if (resizing) {
tp::halnf pos = events.getPos().x;
auto diff = (pos - value - area.z / 2.f);
value += diff;
}
value = tp::clamp(value, min, max);
if (min > max) {
value = min;
}
}
void draw(Canvas& canvas, const tp::RectF& areaParent, const tp::RectF& area) {
if (!areaParent.isOverlap(area)) return;
if (hover || resizing) {
canvas.rect(area, { 0.23f, 0.23f, 0.23f, 1.f}, 0.f);
} else {
canvas.rect(area, { 0.04f, 0.04f, 0.04f, 1.f }, 0.f);
}
}
public:
bool hover = false;
bool resizing = false;
tp::halnf max = 0;
tp::halnf min = 0;
tp::halnf value = 0;
};
template <typename Events, typename Canvas> requires(DrawableConcept<Canvas>)
class TrackInfoWidget {
struct SortType {
tp::String text;
bool dec = false;
bool inc = false;
};
public:
TrackInfoWidget(const Track* track = nullptr) : mTrack(track) {
items.append( { "Date Added" } );
items.append( { "Date Last Played" } );
}
void proc(const Events& events, const tp::RectF& areaParent, const tp::RectF& area) {
if (!mTrack) return;
if (!areaParent.isOverlap(area)) return;
}
void draw(Canvas& canvas, const tp::RectF& areaParent, const tp::RectF& area) {
if (!areaParent.isOverlap(area)) return;
ImGui::SetNextWindowPos( { area.x, area.y } );
ImGui::SetNextWindowSize( { area.z, area.w } );
RenderUI();
canvas.rect(area, { 0.13f, 0.13f, 0.13f, 1.f}, 4.f);
if (!mTrack) return;
}
void RenderUI() {
ImGui::Begin("InfoWindow", 0, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoResize);
ImGui::Text("Song Info:");
if (mTrack) {
ImGui::Text("Name : %s", mTrack->mName.read());
ImGui::Text("Author : %s", mTrack->mArtist.read());
} else {
ImGui::Text("Not Selected");
}
ImGui::Separator();
ImGui::Checkbox("Loved only", &filterLoved);
songFilter.Draw("Song Filter");
sortFilter.Draw("Sorting Type");
for (int i = 0; i < items.size(); ++i) {
if (!sortFilter.PassFilter(items[i].text.read())) continue;
ImGui::PushID(i);
if (ImGui::Button("Inc")) {
items[i].inc = true;
}
ImGui::SameLine();
if (ImGui::Button("Dec")) {
items[i].dec = true;
}
ImGui::AlignTextToFramePadding();
ImGui::SameLine();
ImGui::Text("%s", items[i].text.read());
ImGui::PopID();
}
ImGui::End();
}
public:
ImGuiTextFilter songFilter;
ImGuiTextFilter sortFilter;
tp::Buffer<SortType> items;
const Track* mTrack;
bool filterLoved = false;
};
template <typename Events, typename Canvas> requires(DrawableConcept<Canvas>)
class ScrollBarWidget {
public:
@ -20,7 +153,7 @@ public:
return;
}
if (events.getScrollY() != 0) {
if (events.getScrollY() != 0 && areaParent.isInside(events.getPos())) {
auto offset = events.getScrollY() < 0 ? 1.0f : -1.0f;
if (scrollInertia * offset > 0) {
scrollInertia += offset;
@ -82,12 +215,13 @@ public:
template <typename Events, typename Canvas> requires(DrawableConcept<Canvas>)
class TrackWidget {
public:
TrackWidget(const Track* track) : mTrack(track) {
TrackWidget(const Track* track = nullptr) : mTrack(track) {
col.mColor.setAnimTime(0);
col.mColor.setNoTransition({ 0.15f, 0.15f, 0.15f, 0.f });
};
void proc(const Events& events, const tp::RectF& areaParent, const tp::RectF& area) {
if (!mTrack) return;
if (!areaParent.isOverlap(area)) return;
if (area.isInside(events.getPos())) {
col.set( { 0.15f, 0.15f, 0.15f, 1.f } );
@ -99,6 +233,7 @@ public:
}
void draw(Canvas& canvas, const tp::RectF& areaParent, const tp::RectF& area) {
if (!mTrack) return;
if (!areaParent.isOverlap(area)) return;
canvas.rect(area, col.get(), 4.f);
@ -116,7 +251,7 @@ public:
canvas.text(mTrack->mArtist.read(), textAreaAuthor, 12.f, Canvas::LC, 4.f, { 0.8f, 0.8f, 0.8f, 1.f} );
}
private:
public:
tp::halnf marging = 5.f;
bool insideDebug = false;
tp::AnimColor col;
@ -130,25 +265,39 @@ public:
for (auto track : mLibrary->mTraks) {
mTraks.append(TrackWidget<Events, Canvas>(&track.data()));
}
mResizeWidget.value = 1000;
}
void proc(const Events& events, const tp::RectF& areaParent, const tp::RectF& aArea) {
mResizeWidget.max = aArea.z - trackInfoSize;
mResizeWidget.min = 100;
area = { aArea.x, aArea.y, aArea.z, aArea.w - controllPanelSize };
areaCP = { aArea.x, aArea.y + aArea.w - controllPanelSize, aArea.z, controllPanelSize };
area = { aArea.x, aArea.y, mResizeWidget.value, aArea.w - controllPanelSize };
areaInfo = { mResizeWidget.value + resizeSize, aArea.y, aArea.z - mResizeWidget.value - resizeSize, aArea.w - controllPanelSize } ;
areaResize = { mResizeWidget.value, aArea.y, resizeSize, areaInfo.w };
mScroll.mSizeFraction = area.w / (mLibrary->mTraks.size() * trackSize);
mScroll.mScrollFactor = 1.f / mLibrary->mTraks.size();
mScroll.proc(events, area, { area.x + area.z - scrollSize - 3, area.y + 10, scrollSize - 3.f, area.w - 20 } );
mResizeWidget.proc(events, area, areaResize );
if (area.isInside(events.getPos())) {
mScroll.proc(events, area, { area.x + area.z - scrollSize - 3, area.y + 10, scrollSize - 3.f, area.w - 20 } );
tp::halnf offset = mScroll.mPositionFraction * mLibrary->mTraks.size() * trackSize;
auto idx = 0;
for (auto track : mTraks) {
track->proc(events, area, { area.x + 10, area.y + 10 + idx * trackSize - offset, area.z - 20 - scrollSize, trackSize - 5 } );
auto trackArea = tp::RectF{ area.x + 10, area.y + 10 + idx * trackSize - offset, area.z - 20 - scrollSize, trackSize - 5 };
track->proc(events, area, trackArea);
if (trackArea.isInside(events.getPos()) && events.isPressed()) {
mCurrentTrack.mTrack = track->mTrack;
mCurrentTrackInfo.mTrack = track->mTrack;
}
idx++;
}
}
@ -157,6 +306,7 @@ public:
}
void draw(Canvas& canvas, const tp::RectF& areaParent, const tp::RectF& aArea) {
canvas.rect(aArea, { 0.1f, 0.1f, 0.1f, 1.f });
tp::halnf offset = mScroll.mPositionFraction * mLibrary->mTraks.size() * trackSize;
@ -168,25 +318,39 @@ public:
}
mScroll.draw(canvas, area, { area.x + area.z - scrollSize - 3, area.y + 10, scrollSize - 3.f, area.w - 20 } );
// canvas.rect( areaInfo, { 0.04f, 0.04f, 0.04f, 1.f });
mCurrentTrackInfo.draw(canvas, aArea, areaInfo );
mResizeWidget.draw(canvas, aArea, areaResize );
drawCP(canvas, aArea, areaCP);
}
void drawCP(Canvas& canvas, const tp::RectF& areaParent, const tp::RectF& area) {
canvas.rect( area, { 0.04f, 0.04f, 0.04f, 1.f });
mTraks.first().draw(canvas, area, area);
mCurrentTrack.draw(canvas, area, area);
}
private:
tp::halnf scrollSize = 15;
tp::halnf scrollSize = 20;
tp::halnf trackInfoSize = 200;
tp::halnf controllPanelSize = 70;
tp::halnf trackSize = 60;
tp::Vec2F debugPos;
tp::halnf resizeSize = 10;
tp::RectF area;
tp::RectF areaInfo;
tp::RectF areaCP;
tp::RectF areaResize;
tp::Buffer<TrackWidget<Events, Canvas>> mTraks;
TrackWidget<Events, Canvas> mCurrentTrack;
TrackInfoWidget<Events, Canvas> mCurrentTrackInfo;
ScrollBarWidget<Events, Canvas> mScroll;
ResizerWidget<Events, Canvas> mResizeWidget;
Library* mLibrary;
};

View file

@ -0,0 +1,29 @@
#pragma once
#include <iostream>
#include <thread>
#include <alsa/asoundlib.h>
class WavPlayer {
public:
WavPlayer();
~WavPlayer();
void loadSong(const char* filePath);
void playSong();
void stopSong();
void setVolume(float volume);
float getProgress() const;
float getDurationSec() const;
void setProgress(float newProgress);
private:
void playbackThreadFunction();
snd_pcm_t* handle;
std::thread* playbackThread;
bool isPlaying;
float progress;
};
void example();