Side panel added
This commit is contained in:
parent
c7d3b15758
commit
7b325244c5
9 changed files with 366 additions and 25 deletions
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
// -------- Debug UI -------- //
|
||||
#include <imgui.h>
|
||||
#include <imgui_internal.h>
|
||||
#include <imgui_impl_glfw.h>
|
||||
#include <imgui_impl_opengl3.h>
|
||||
|
||||
|
|
@ -28,6 +29,9 @@ void Graphics::GUI::init(Window* window) {
|
|||
|
||||
// Initialize ImGui with GLFW and OpenGL
|
||||
ImGui_ImplGlfw_InitForOpenGL(window->getContext()->window, true);
|
||||
|
||||
// ImGui_ImplGlfw_GetBackendData();
|
||||
|
||||
ImGui_ImplOpenGL3_Init("#version 330");
|
||||
}
|
||||
|
||||
|
|
@ -45,10 +49,6 @@ void Graphics::GUI::proc() {
|
|||
ImGui::NewFrame();
|
||||
|
||||
tp::HeapAllocGlobal::stopIgnore();
|
||||
|
||||
// ImGui code goes here
|
||||
ImGui::Begin("Window");
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
void Graphics::GUI::draw() {
|
||||
|
|
|
|||
|
|
@ -59,26 +59,26 @@ void Graphics::GL::draw() {
|
|||
|
||||
void Graphics::init(Window* window) {
|
||||
mGl.init();
|
||||
//mGui.init(window);
|
||||
mGui.init(window);
|
||||
mCanvas.init();
|
||||
}
|
||||
|
||||
void Graphics::deinit() {
|
||||
mCanvas.deinit();
|
||||
// mGui.deinit();
|
||||
mGui.deinit();
|
||||
mGl.deinit();
|
||||
}
|
||||
|
||||
void Graphics::proc() {
|
||||
mGl.proc();
|
||||
mCanvas.proc();
|
||||
// mGui.proc();
|
||||
mGui.proc();
|
||||
}
|
||||
|
||||
void Graphics::draw() {
|
||||
mGl.draw();
|
||||
mCanvas.draw();
|
||||
// mGui.draw();
|
||||
mGui.draw();
|
||||
}
|
||||
|
||||
Window::Window(int width, int height, const char* title) {
|
||||
|
|
@ -101,10 +101,9 @@ Window::Window(int width, int height, const char* title) {
|
|||
return;
|
||||
}
|
||||
|
||||
mGraphics.init(this);
|
||||
|
||||
mContext->inputManager.init(mContext->window);
|
||||
glfwSetWindowUserPointer(mContext->window, &mContext->inputManager);
|
||||
|
||||
mGraphics.init(this);
|
||||
|
||||
mEvents.mContext = mContext;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@ namespace tp {
|
|||
void init(GLFWwindow* aWindow) {
|
||||
window = aWindow;
|
||||
// Set up key and mouse button callbacks
|
||||
|
||||
glfwSetWindowUserPointer(window, this);
|
||||
glfwSetKeyCallback(window, keyCallback);
|
||||
glfwSetMouseButtonCallback(window, mouseButtonCallback);
|
||||
glfwSetScrollCallback(window, scrollCallback);
|
||||
|
|
@ -86,6 +88,7 @@ namespace tp {
|
|||
void resetScroll() {
|
||||
scrollX = 0.0;
|
||||
scrollY = 0.0;
|
||||
mouseButtonStates[GLFW_MOUSE_BUTTON_LEFT] = GLFW_REPEAT;
|
||||
}
|
||||
|
||||
double scrollX = 0.0;
|
||||
|
|
|
|||
|
|
@ -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})
|
||||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
BIN
LibraryViewer/library/Example.wav
Normal file
BIN
LibraryViewer/library/Example.wav
Normal file
Binary file not shown.
138
LibraryViewer/private/WavPlayer.cpp
Normal file
138
LibraryViewer/private/WavPlayer.cpp
Normal 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();
|
||||
}
|
||||
|
|
@ -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();
|
||||
|
||||
if (area.isInside(events.getPos())) {
|
||||
mScroll.proc(events, area, { area.x + area.z - scrollSize - 3, area.y + 10, scrollSize - 3.f, area.w - 20 } );
|
||||
mResizeWidget.proc(events, area, areaResize );
|
||||
|
||||
mScroll.proc(events, area, { area.x + area.z - scrollSize - 3, area.y + 10, scrollSize - 3.f, area.w - 20 } );
|
||||
if (area.isInside(events.getPos())) {
|
||||
|
||||
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;
|
||||
|
|
@ -169,24 +319,38 @@ 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;
|
||||
};
|
||||
29
LibraryViewer/public/WavPlayer.hpp
Normal file
29
LibraryViewer/public/WavPlayer.hpp
Normal 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();
|
||||
Loading…
Add table
Add a link
Reference in a new issue