mirror of
https://github.com/Goldenkrew3000/OSSP_OpenSource.git
synced 2025-12-19 00:04:44 +10:00
Compare commits
6 Commits
3c0c2c3d5a
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 1a4e59346e | |||
| b889db7225 | |||
| 4f4377c837 | |||
| 7c22c0f63f | |||
| 496c5e9acf | |||
| 62c95a5de8 |
@@ -1,5 +1,4 @@
|
||||
# OSSP_OpenSource
|
||||
An effort to open-source OSSP
|
||||
# OSSP (OpenSubsonicPlayer)
|
||||
|
||||
## Notice
|
||||
OSSP is under heavy development and is NOT ready for daily use.<br>
|
||||
@@ -25,6 +24,8 @@ OSSP has been privately tested under:
|
||||
- iOS / macOS Catalyst aarch64
|
||||
- 32-bit platforms have major issues at this time
|
||||
|
||||
OSSP itself is extremely portable, and should be usable on any UNIX®/Unix-like platform with little to no patching, but the Gstreamer player logic is less portable (Specifically LSP Plugins).
|
||||
|
||||
## Information
|
||||
The ```libopensubsonic``` library has been implemented as per the specification located at [OpenSubsonic Netlify](https://opensubsonic.netlify.app/docs/api-reference/)
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include "external/discord-rpc/include/discord_rpc.h"
|
||||
#include "libopensubsonic/logger.h"
|
||||
#include "configHandler.h"
|
||||
@@ -87,11 +88,14 @@ void discordrpc_update(discordrpc_data** discordrpc_struct) {
|
||||
asprintf(&detailsString, "Idle");
|
||||
presence.details = detailsString;
|
||||
} else if ((*discordrpc_struct)->state == DISCORDRPC_STATE_PLAYING) {
|
||||
time_t currentTime = time(NULL);
|
||||
asprintf(&detailsString, "%s", (*discordrpc_struct)->songTitle);
|
||||
asprintf(&stateString, "by %s", (*discordrpc_struct)->songArtist);
|
||||
presence.details = detailsString;
|
||||
presence.state = stateString;
|
||||
presence.largeImageKey = (*discordrpc_struct)->coverArtUrl;
|
||||
presence.startTimestamp = (long)currentTime;
|
||||
presence.endTimestamp = (long)currentTime + (*discordrpc_struct)->songLength;
|
||||
if (configObj->discordrpc_showSysDetails) {
|
||||
presence.largeImageText = discordrpc_osString;
|
||||
}
|
||||
|
||||
@@ -20,10 +20,12 @@
|
||||
extern configHandler_config_t* configObj;
|
||||
bool bLikedSongsShow = false;
|
||||
bool bAudioSettingsShow = false;
|
||||
bool bPlayQueueShow = false;
|
||||
void showLikedSongs();
|
||||
void showAudioSettings();
|
||||
void showPlayQueue();
|
||||
|
||||
int qt_gui_entry(int argc, char** argv) {
|
||||
int gui_entry() {
|
||||
// Initialize SDL
|
||||
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_GAMECONTROLLER) != 0) {
|
||||
printf("SDL could not be initialized: %s\n", SDL_GetError());
|
||||
@@ -104,6 +106,12 @@ int qt_gui_entry(int argc, char** argv) {
|
||||
bAudioSettingsShow = true;
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
if (ImGui::Button("Play Queue")) {
|
||||
bPlayQueueShow = true;
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
@@ -115,6 +123,10 @@ int qt_gui_entry(int argc, char** argv) {
|
||||
showAudioSettings();
|
||||
}
|
||||
|
||||
if (bPlayQueueShow) {
|
||||
showPlayQueue();
|
||||
}
|
||||
|
||||
// Render
|
||||
ImGui::Render();
|
||||
glViewport(0, 0, (int)io.DisplaySize.x, (int)io.DisplaySize.y);
|
||||
@@ -186,9 +198,10 @@ void showLikedSongs() {
|
||||
}
|
||||
|
||||
if (selectedSong != -1) {
|
||||
printf("Song: %s (%s)\n", starredStruct->songs[selectedSong].title,
|
||||
starredStruct->songs[selectedSong].id);
|
||||
OSSPlayer_QueueAppend(starredStruct->songs[selectedSong].id);
|
||||
OSSPlayer_QueueAppend(starredStruct->songs[selectedSong].title,
|
||||
starredStruct->songs[selectedSong].artist,
|
||||
starredStruct->songs[selectedSong].id,
|
||||
starredStruct->songs[selectedSong].duration);
|
||||
selectedSong = -1;
|
||||
}
|
||||
|
||||
@@ -196,20 +209,57 @@ void showLikedSongs() {
|
||||
}
|
||||
|
||||
float in_volume_val = 0;
|
||||
float out_volume_val = 0;
|
||||
float pitch_val = 0;
|
||||
bool hasInVolumeFirstRun = false;
|
||||
void showAudioSettings() {
|
||||
ImGui::Begin("Audio Settings");
|
||||
|
||||
if (!hasInVolumeFirstRun) {
|
||||
in_volume_val = OSSPlayer_GstECont_InVolume_Get();
|
||||
out_volume_val = OSSPlayer_GstECont_OutVolume_Get();
|
||||
pitch_val = configObj->audio_pitch_cents / 100.0f; // Cents to semitones
|
||||
hasInVolumeFirstRun = true;
|
||||
}
|
||||
|
||||
ImGui::Text("In Vol / Out Vol");
|
||||
|
||||
// Idk what that field is, styling?, Size, Storage, Low, High
|
||||
if (ImGui::VSliderFloat("##v", ImVec2(35, 160), &in_volume_val, 0.0f, 1.0f)) {
|
||||
if (ImGui::VSliderFloat("##invol", ImVec2(35, 160), &in_volume_val, 0.0f, 1.0f)) {
|
||||
// Data has changed
|
||||
OSSPlayer_GstECont_InVolume_set(in_volume_val);
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
if (ImGui::VSliderFloat("##outvol", ImVec2(35, 160), &out_volume_val, 0.0f, 1.0f)) {
|
||||
OSSPlayer_GstECont_OutVolume_set(out_volume_val);
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
if (ImGui::VSliderFloat("##pitch", ImVec2(35, 160), &pitch_val, -6.00f, 6.00f)) {
|
||||
OSSPlayer_GstECont_Pitch_Set(pitch_val * 100.0f); // Convert semitones to cents
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
// TODO: go through abstraction
|
||||
#include "../player/playQueue.hpp"
|
||||
|
||||
void showPlayQueue() {
|
||||
ImGui::Begin("Play Queue");
|
||||
|
||||
static int selectedSong = -1;
|
||||
if (ImGui::BeginChild("Play Queue", ImVec2(0, 200), ImGuiChildFlags_Border)) {
|
||||
for (int i = 0; i < internal_OSSPQ_GetItemCount(); i++) {
|
||||
if (ImGui::Selectable(internal_OSSPQ_GetTitleAtIndex(i), selectedSong == i)) {
|
||||
selectedSong = i;
|
||||
}
|
||||
}
|
||||
ImGui::EndChild();
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
extern "C" {
|
||||
#endif // __cplusplus
|
||||
|
||||
int qt_gui_entry(int argc, char** argv);
|
||||
int gui_entry();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ int main(int argc, char** argv) {
|
||||
discordrpc_struct_deinit(&discordrpc);
|
||||
|
||||
// Launch QT frontend
|
||||
qt_gui_entry(argc, argv);
|
||||
gui_entry();
|
||||
|
||||
// Cleanup and exit
|
||||
configHandler_Free(&configObj);
|
||||
|
||||
@@ -5,29 +5,82 @@
|
||||
* Info: Gstreamer Queue Handler
|
||||
*/
|
||||
|
||||
/*
|
||||
* Now you might ask why this even exists in this way, but I thought that it would be easier to jump to C++
|
||||
* to store a song queue with objects instead of some hacked together solution in C
|
||||
* And I was right.
|
||||
* Like yeah, you can store an array of structs easily in C, but to dynamically be able to move those around, no,
|
||||
* std::deque makes that MUCH simpler.
|
||||
*/
|
||||
|
||||
#include <cstddef>
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
#include <deque>
|
||||
#include "playQueue.hpp"
|
||||
|
||||
// NOTE: Acronym is OpenSubsonicPlayerQueue
|
||||
std::deque<std::string> OSSPQ_Items;
|
||||
// C++ interface for storing song queue data (C interface is in the header)
|
||||
class SongObject {
|
||||
public:
|
||||
std::string title;
|
||||
std::string artist;
|
||||
std::string id;
|
||||
long duration;
|
||||
};
|
||||
|
||||
int internal_OSSPQ_AppendToEnd(char* id) {
|
||||
// NOTE: Acronym is OpenSubsonicPlayerQueue
|
||||
std::deque<SongObject> OSSPQ_Items;
|
||||
|
||||
int internal_OSSPQ_AppendToEnd(char* title, char* artist, char* id, long duration) {
|
||||
// Append a new song to the end of the queue
|
||||
printf("Title: %s\nArtist: %s\nID: %s\nDuration: %ld\n", title, artist, id, duration);
|
||||
// TODO: Find a neater way of converting a C string to a C++ string??
|
||||
std::string cpp_title(title);
|
||||
std::string cpp_artist(artist);
|
||||
std::string cpp_id(id);
|
||||
OSSPQ_Items.push_back(cpp_id);
|
||||
SongObject songObject;
|
||||
songObject.title = cpp_title;
|
||||
songObject.artist = cpp_artist;
|
||||
songObject.id = cpp_id;
|
||||
songObject.duration = duration;
|
||||
OSSPQ_Items.push_back(songObject);
|
||||
return 0;
|
||||
}
|
||||
|
||||
char* internal_OSSPQ_PopFromFront() {
|
||||
OSSPQ_SongStruct* internal_OSSPQ_PopFromFront() {
|
||||
if (OSSPQ_Items.empty()) {
|
||||
// No items in play queue
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char* id = strdup(OSSPQ_Items.front().c_str()); // Heap allocate id
|
||||
// Pull the first song off the song queue
|
||||
SongObject songObject = OSSPQ_Items.front();
|
||||
OSSPQ_Items.pop_front();
|
||||
return id;
|
||||
|
||||
// Move song data into a C readable format
|
||||
// NOTE: I am initializing the variables to a known value just in case there is missing information in songObject
|
||||
OSSPQ_SongStruct* playQueueObject = (OSSPQ_SongStruct*)malloc(sizeof(OSSPQ_SongStruct));
|
||||
playQueueObject->title = NULL;
|
||||
playQueueObject->artist = NULL;
|
||||
playQueueObject->id = NULL;
|
||||
playQueueObject->duration = 0;
|
||||
|
||||
playQueueObject->title = strdup(songObject.title.c_str());
|
||||
playQueueObject->artist = strdup(songObject.artist.c_str());
|
||||
playQueueObject->id = strdup(songObject.id.c_str());
|
||||
playQueueObject->duration = songObject.duration;
|
||||
return playQueueObject;
|
||||
}
|
||||
|
||||
void internal_OSSPQ_FreeSongObject(OSSPQ_SongStruct* songObject) {
|
||||
if (songObject->title != NULL) { free(songObject->title); }
|
||||
if (songObject->artist != NULL) { free(songObject->artist); }
|
||||
if (songObject->id != NULL) { free(songObject->id); }
|
||||
if (songObject != NULL) { free(songObject); }
|
||||
}
|
||||
|
||||
char* internal_OSSPQ_GetTitleAtIndex(int idx) {
|
||||
return (char*)OSSPQ_Items[idx].title.c_str();
|
||||
}
|
||||
|
||||
int internal_OSSPQ_GetItemCount() {
|
||||
|
||||
@@ -11,8 +11,18 @@
|
||||
extern "C" {
|
||||
#endif // __cplusplus
|
||||
|
||||
int internal_OSSPQ_AppendToEnd(char* id);
|
||||
char* internal_OSSPQ_PopFromFront();
|
||||
// C interface for sending song queue data (C++ interface is in the C++ file)
|
||||
typedef struct {
|
||||
char* title;
|
||||
char* artist;
|
||||
char* id;
|
||||
long duration;
|
||||
} OSSPQ_SongStruct;
|
||||
|
||||
int internal_OSSPQ_AppendToEnd(char* title, char* artist, char* id, long duration);
|
||||
OSSPQ_SongStruct* internal_OSSPQ_PopFromFront();
|
||||
void internal_OSSPQ_FreeSongObject(OSSPQ_SongStruct* songObject);
|
||||
char* internal_OSSPQ_GetTitleAtIndex(int idx);
|
||||
int internal_OSSPQ_GetItemCount();
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <math.h>
|
||||
#include <stdbool.h>
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
#include "../configHandler.h"
|
||||
#include "../discordrpc.h"
|
||||
#include "../libopensubsonic/logger.h"
|
||||
@@ -18,11 +19,9 @@
|
||||
#include "playQueue.hpp"
|
||||
#include "player.h"
|
||||
|
||||
#include <unistd.h> // For worse sleep TODO
|
||||
|
||||
extern configHandler_config_t* configObj;
|
||||
static int rc = 0;
|
||||
GstElement *pipeline, *playbin, *filter_bin, *conv_in, *conv_out, *in_volume, *equalizer, *pitch, *reverb;
|
||||
GstElement *pipeline, *playbin, *filter_bin, *conv_in, *conv_out, *in_volume, *equalizer, *pitch, *reverb, *out_volume;
|
||||
GstPad *sink_pad, *src_pad;
|
||||
GstBus* bus;
|
||||
guint bus_watch_id;
|
||||
@@ -105,6 +104,8 @@ void* OSSPlayer_GMainLoop(void*) {
|
||||
}
|
||||
|
||||
void* OSSPlayer_ThrdInit(void*) {
|
||||
bool haveIssuedDiscordRPCIdle = true;
|
||||
|
||||
// Player init function for pthread entry
|
||||
logger_log_important(__func__, "Player thread running.");
|
||||
OSSPlayer_GstInit();
|
||||
@@ -116,21 +117,21 @@ void* OSSPlayer_ThrdInit(void*) {
|
||||
// Poll play queue for new items to play
|
||||
while (true) { // TODO use global bool instead
|
||||
if (internal_OSSPQ_GetItemCount() != 0 && isPlaying == false) {
|
||||
printf("IS VALID\n");
|
||||
// Player is not playing and a song is in the song queue
|
||||
char* id = OSSPlayer_QueuePopFront();
|
||||
if (id == NULL) {
|
||||
|
||||
// Pull new song from the song queue
|
||||
OSSPQ_SongStruct* songObject = OSSPlayer_QueuePopFront();
|
||||
if (songObject == NULL) {
|
||||
printf("FUCK\n");
|
||||
// TODO: this
|
||||
}
|
||||
|
||||
// NOTE: Using a few strdup()'s because the cleanup/deinit functions perform free's and to avoid UAFs/Double frees
|
||||
|
||||
// Fetch song information
|
||||
// NOTE: Using a few strdup()'s because the cleanup/deinit functions perform free's so to avoid UAF's/Double free's
|
||||
// Fetch song information from the /getSong endpoint
|
||||
opensubsonic_httpClient_URL_t* song_url = malloc(sizeof(opensubsonic_httpClient_URL_t));
|
||||
opensubsonic_httpClient_URL_prepare(&song_url);
|
||||
song_url->endpoint = OPENSUBSONIC_ENDPOINT_GETSONG;
|
||||
song_url->id = strdup(id);
|
||||
song_url->id = strdup(songObject->id);
|
||||
opensubsonic_httpClient_formUrl(&song_url);
|
||||
opensubsonic_getSong_struct* songStruct;
|
||||
opensubsonic_httpClient_fetchResponse(&song_url, (void**)&songStruct);
|
||||
@@ -139,21 +140,24 @@ void* OSSPlayer_ThrdInit(void*) {
|
||||
opensubsonic_httpClient_URL_t* coverart_url = malloc(sizeof(opensubsonic_httpClient_URL_t));
|
||||
opensubsonic_httpClient_URL_prepare(&coverart_url);
|
||||
coverart_url->endpoint = OPENSUBSONIC_ENDPOINT_GETCOVERART;
|
||||
coverart_url->id = strdup(id);
|
||||
coverart_url->id = strdup(songObject->id);
|
||||
opensubsonic_httpClient_formUrl(&coverart_url);
|
||||
|
||||
// Update Discord RPC
|
||||
// Prepare Discord RPC
|
||||
discordrpc_data* discordrpc = NULL;
|
||||
discordrpc_struct_init(&discordrpc);
|
||||
discordrpc->state = DISCORDRPC_STATE_PLAYING;
|
||||
discordrpc->songLength = songStruct->duration;
|
||||
discordrpc->songTitle = strdup(songStruct->title);
|
||||
discordrpc->songArtist = strdup(songStruct->artist);
|
||||
discordrpc->coverArtUrl = strdup(coverart_url->formedUrl);
|
||||
//discordrpc->coverArtUrl = strdup(coverart_url->formedUrl);
|
||||
discordrpc_update(&discordrpc);
|
||||
discordrpc_struct_deinit(&discordrpc);
|
||||
|
||||
// Free the cover art URL
|
||||
opensubsonic_httpClient_URL_cleanup(&coverart_url);
|
||||
|
||||
// Free the /getSong info
|
||||
opensubsonic_getSong_struct_free(&songStruct);
|
||||
opensubsonic_httpClient_URL_cleanup(&song_url);
|
||||
|
||||
@@ -161,14 +165,39 @@ void* OSSPlayer_ThrdInit(void*) {
|
||||
opensubsonic_httpClient_URL_t* stream_url = malloc(sizeof(opensubsonic_httpClient_URL_t));
|
||||
opensubsonic_httpClient_URL_prepare(&stream_url);
|
||||
stream_url->endpoint = OPENSUBSONIC_ENDPOINT_STREAM;
|
||||
stream_url->id = id;
|
||||
stream_url->id = strdup(songObject->id);
|
||||
opensubsonic_httpClient_formUrl(&stream_url);
|
||||
|
||||
// Free song queue object
|
||||
// TODO: Go through abstraction
|
||||
internal_OSSPQ_FreeSongObject(songObject);
|
||||
|
||||
// Configure discord RPC idle boolean for when a song isn't playing
|
||||
haveIssuedDiscordRPCIdle = false;
|
||||
|
||||
// Configure playbin3, free stream URL, send discord RPC, and start playing
|
||||
g_object_set(playbin, "uri", stream_url->formedUrl, NULL);
|
||||
opensubsonic_httpClient_URL_cleanup(&stream_url);
|
||||
isPlaying = true;
|
||||
gst_element_set_state(pipeline, GST_STATE_PLAYING);
|
||||
}
|
||||
usleep(200 * 1000); // Use futex and signals instead of this TODO
|
||||
|
||||
if (internal_OSSPQ_GetItemCount() == 0 && !isPlaying) {
|
||||
// No song currently playing, and the queue is empty
|
||||
|
||||
// Only send idle Discord RPC if needed to avoid spamming
|
||||
if (!haveIssuedDiscordRPCIdle) {
|
||||
printf("Issuing idle Discord RPC\n");
|
||||
haveIssuedDiscordRPCIdle = true;
|
||||
|
||||
discordrpc_data* discordrpc = NULL;
|
||||
discordrpc_struct_init(&discordrpc);
|
||||
discordrpc->state = DISCORDRPC_STATE_IDLE;
|
||||
discordrpc_update(&discordrpc);
|
||||
discordrpc_struct_deinit(&discordrpc);
|
||||
}
|
||||
}
|
||||
usleep(200 * 1000);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -216,6 +245,7 @@ int OSSPlayer_GstInit() {
|
||||
// Calf Studio Plugins Reverb
|
||||
reverb = gst_element_factory_make(configObj->lv2_reverb_filter_name, "reverb");
|
||||
}
|
||||
out_volume = gst_element_factory_make("volume", "out-volume");
|
||||
// TODO: Make better error messages for here, and exit out early
|
||||
if (!equalizer) {
|
||||
logger_log_error(__func__, "Could not initialize equalizer.");
|
||||
@@ -229,8 +259,8 @@ int OSSPlayer_GstInit() {
|
||||
|
||||
// Add and link elements to the filter bin
|
||||
// TODO: Check creation and dynamic as per config
|
||||
gst_bin_add_many(GST_BIN(filter_bin), conv_in, in_volume, equalizer, conv_out, NULL);
|
||||
gst_element_link_many(conv_in, in_volume, equalizer, conv_out, NULL);
|
||||
gst_bin_add_many(GST_BIN(filter_bin), conv_in, in_volume, equalizer, pitch, out_volume, conv_out, NULL);
|
||||
gst_element_link_many(conv_in, in_volume, equalizer, pitch, out_volume, conv_out, NULL);
|
||||
sink_pad = gst_element_get_static_pad(conv_in, "sink");
|
||||
src_pad = gst_element_get_static_pad(conv_out, "src");
|
||||
gst_element_add_pad(filter_bin, gst_ghost_pad_new("sink", sink_pad));
|
||||
@@ -240,7 +270,7 @@ int OSSPlayer_GstInit() {
|
||||
|
||||
// Setup playbin3 (Configure audio plugins and set user agent)
|
||||
g_object_set(playbin, "audio-filter", filter_bin, NULL);
|
||||
g_signal_connect (playbin, "source-setup", G_CALLBACK(gst_playbin3_sourcesetup_callback), NULL);
|
||||
g_signal_connect(playbin, "source-setup", G_CALLBACK(gst_playbin3_sourcesetup_callback), NULL);
|
||||
|
||||
// Add playbin3 to the pipeline
|
||||
gst_bin_add(GST_BIN(pipeline), playbin);
|
||||
@@ -248,10 +278,11 @@ int OSSPlayer_GstInit() {
|
||||
// Initialize in-volume (Volume before the audio reaches the plugins)
|
||||
g_object_set(in_volume, "volume", 0.175, NULL);
|
||||
|
||||
// Initialize out-volume (Volume after the audio plugins)
|
||||
g_object_set(out_volume, "volume", 1.00, NULL);
|
||||
|
||||
// Initialize equalizer
|
||||
if (configObj->audio_equalizer_enable) {
|
||||
printf("Initializing %d equalizer bands...\n", configObj->audio_equalizer_graphCount);
|
||||
|
||||
// Dynamically append settings to the equalizer to match the config file
|
||||
for (int i = 0; i < configObj->audio_equalizer_graphCount; i++) {
|
||||
char* ftl_name = NULL;
|
||||
@@ -296,7 +327,7 @@ int OSSPlayer_GstInit() {
|
||||
g_object_set(equalizer, fl_name, freq, NULL);
|
||||
g_object_set(equalizer, fr_name, freq, NULL);
|
||||
} else {
|
||||
printf("EQ band %d - F: %.2f / G: %.2f / Q: 4.36\n", i + 1, (float)configObj->audio_equalizer_graph[i].frequency, gain);
|
||||
printf("EQ band %d - F: %.2f(Nfp) / G: %.2f / Q: 4.36\n", i + 1, (float)configObj->audio_equalizer_graph[i].frequency, gain);
|
||||
g_object_set(equalizer, fl_name, (float)configObj->audio_equalizer_graph[i].frequency, NULL);
|
||||
g_object_set(equalizer, fr_name, (float)configObj->audio_equalizer_graph[i].frequency, NULL);
|
||||
}
|
||||
@@ -315,9 +346,11 @@ int OSSPlayer_GstInit() {
|
||||
}
|
||||
|
||||
// Initialize pitch
|
||||
|
||||
|
||||
|
||||
if (configObj->audio_pitch_enable) {
|
||||
float scaleFactor = OSSPlayer_CentsToPSF(configObj->audio_pitch_cents);
|
||||
printf("Pitch Cents: %.2f, Scale factor: %.6f\n", configObj->audio_pitch_cents, scaleFactor);
|
||||
g_object_set(pitch, "pitch", scaleFactor, NULL);
|
||||
}
|
||||
|
||||
// Initialize reverb
|
||||
}
|
||||
@@ -329,25 +362,28 @@ int OSSPlayer_GstDeInit() {
|
||||
/*
|
||||
* Player Queue Control Functions
|
||||
*/
|
||||
int OSSPlayer_QueueAppend(char* id) {
|
||||
int OSSPlayer_QueueAppend(char* title, char* artist, char* id, long duration) {
|
||||
// Call to C++ function
|
||||
internal_OSSPQ_AppendToEnd(id);
|
||||
// Note: I would receive a song struct instead of individual elements, but it would significantly slow down the GUI
|
||||
internal_OSSPQ_AppendToEnd(title, artist, id, duration);
|
||||
}
|
||||
|
||||
char* OSSPlayer_QueuePopFront() {
|
||||
OSSPQ_SongStruct* OSSPlayer_QueuePopFront() {
|
||||
// Call to C++ function
|
||||
// NOTE: 'id' is heap-allocated from C++
|
||||
char* id = internal_OSSPQ_PopFromFront();
|
||||
if (id == NULL) {
|
||||
|
||||
OSSPQ_SongStruct* songObject = internal_OSSPQ_PopFromFront();
|
||||
|
||||
if (songObject == NULL) {
|
||||
// Queue is empty TODO
|
||||
printf("FUCKFUCKFUCK\n");
|
||||
}
|
||||
return id;
|
||||
return songObject;
|
||||
}
|
||||
|
||||
/*
|
||||
* Gstreamer Element Control Functions
|
||||
*/
|
||||
// TODO: Consolidate volume functions?
|
||||
float OSSPlayer_GstECont_InVolume_Get() {
|
||||
gdouble vol;
|
||||
g_object_get(in_volume, "volume", &vol, NULL);
|
||||
@@ -358,6 +394,25 @@ void OSSPlayer_GstECont_InVolume_set(float val) {
|
||||
g_object_set(in_volume, "volume", val, NULL);
|
||||
}
|
||||
|
||||
float OSSPlayer_GstECont_OutVolume_Get() {
|
||||
gdouble vol;
|
||||
g_object_get(out_volume, "volume", &vol, NULL);
|
||||
return (float)vol;
|
||||
}
|
||||
|
||||
void OSSPlayer_GstECont_OutVolume_set(float val) {
|
||||
g_object_set(out_volume, "volume", val, NULL);
|
||||
}
|
||||
|
||||
float OSSPlayer_GstECont_Pitch_Get() {
|
||||
//
|
||||
}
|
||||
|
||||
void OSSPlayer_GstECont_Pitch_Set(float cents) {
|
||||
float psf = OSSPlayer_CentsToPSF(cents);
|
||||
g_object_set(pitch, "pitch", psf, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Utility Functions
|
||||
*/
|
||||
@@ -370,3 +425,9 @@ float OSSPlayer_PitchFollow(float freq, float semitone) {
|
||||
// Calculate new EQ frequency from semitone adjustment
|
||||
return freq * pow(2.0, semitone / 12.0);
|
||||
}
|
||||
|
||||
float OSSPlayer_CentsToPSF(float cents) {
|
||||
// Convert Cents to a Pitch Scale Factor
|
||||
float semitone = cents / 100.0;
|
||||
return pow(2, (semitone / 12.0f));
|
||||
}
|
||||
|
||||
@@ -11,17 +11,24 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "playQueue.hpp"
|
||||
|
||||
void* OSSPlayer_GMainLoop(void*);
|
||||
void* OSSPlayer_ThrdInit(void*);
|
||||
int OSSPlayer_GstInit();
|
||||
int OSSPlayer_QueueAppend(char* id);
|
||||
char* OSSPlayer_QueuePopFront();
|
||||
int OSSPlayer_QueueAppend(char* title, char* artist, char* id, long duration);
|
||||
OSSPQ_SongStruct* OSSPlayer_QueuePopFront();
|
||||
|
||||
float OSSPlayer_GstECont_InVolume_Get();
|
||||
void OSSPlayer_GstECont_InVolume_set(float val);
|
||||
float OSSPlayer_GstECont_OutVolume_Get();
|
||||
void OSSPlayer_GstECont_OutVolume_set(float val);
|
||||
float OSSPlayer_GstECont_Pitch_Get();
|
||||
void OSSPlayer_GstECont_Pitch_Set(float cents);
|
||||
|
||||
float OSSPlayer_DbLinMul(float db);
|
||||
float OSSPlayer_PitchFollow(float freq, float semitone);
|
||||
float OSSPlayer_CentsToPSF(float cents);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user