audacious thread timing the correct way -- texeci's to follow
[monky] / src / audacious.c
1 /* -------------------------------------------------------------------------
2  * audacious.c:  conky support for audacious music player
3  *
4  * Copyright (C) 2005  Philip Kovacs kovacsp3@comcast.net
5  * 
6  * $Id$
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
21  * --------------------------------------------------------------------------- */
22
23 #include <pthread.h>
24 #include <errno.h>
25 #include <stdio.h>
26 #include <string.h>
27 #include <unistd.h>
28
29 #include <glib.h>
30 #include <audacious/beepctrl.h>
31
32 #include "config.h"
33 #include "conky.h"
34 #include "audacious.h"
35
36 /* access to this item array is synchronized */
37 static audacious_t audacious_items;
38
39 /* -----------------------------------------
40  * Conky update function for audacious data.
41  * ----------------------------------------- */
42 void update_audacious(void)
43 {
44     /* 
45       The worker thread is updating audacious_items array asynchronously to the main 
46       conky thread.  We merely copy the audacious_items array into the main thread's 
47       info structure when the main thread's update cycle fires. 
48     */
49     pthread_mutex_lock(&info.audacious.item_mutex);
50     memcpy(&info.audacious.items,audacious_items,sizeof(audacious_items));
51     pthread_mutex_unlock(&info.audacious.item_mutex);
52 }
53
54
55 /* ------------------------------------------------------------
56  * Create a worker thread for audacious media player status.
57  *
58  * Returns 0 on success, -1 on error. 
59  * ------------------------------------------------------------*/
60 int create_audacious_thread(void)
61 {
62     /* Is a worker is thread already running? */
63     if (info.audacious.thread)
64         return(-1);
65
66     /* Joinable thread for audacious activity */
67     pthread_attr_init(&info.audacious.thread_attr);
68     pthread_attr_setdetachstate(&info.audacious.thread_attr, PTHREAD_CREATE_JOINABLE);
69     /* Init mutexes */
70     pthread_mutex_init(&info.audacious.item_mutex, NULL);
71     pthread_mutex_init(&info.audacious.runnable_mutex, NULL);
72     /* Init runnable condition for worker thread */
73     pthread_cond_init(&info.audacious.runnable_cond, NULL);
74     if (pthread_create(&info.audacious.thread, &info.audacious.thread_attr, audacious_thread_func, NULL))
75         return(-1);
76
77     return 0;
78 }
79
80 /* ------------------------------------------------
81  * Destroy audacious player status thread. 
82  *
83  * Returns 0 on success, -1 on error.
84  * ------------------------------------------------ */
85 int destroy_audacious_thread(void)
86 {
87     /* Is a worker is thread running? If not, no error. */
88     if (!info.audacious.thread)
89         return(0);
90
91     /* Signal audacious thread to terminate */
92     pthread_mutex_lock (&info.audacious.runnable_mutex);
93     pthread_cond_signal (&info.audacious.runnable_cond);
94     pthread_mutex_unlock (&info.audacious.runnable_mutex);
95
96     /* Destroy thread attribute and wait for thread */
97     pthread_attr_destroy(&info.audacious.thread_attr);
98     if (pthread_join(info.audacious.thread, NULL))
99         return(-1);
100
101     /* Destroy mutexes and cond */
102     pthread_mutex_destroy(&info.audacious.item_mutex);
103     pthread_mutex_destroy(&info.audacious.runnable_mutex);
104     pthread_cond_destroy(&info.audacious.runnable_cond);
105
106     info.audacious.thread=(pthread_t)0;
107     return 0;
108 }
109
110 /* ---------------------------------------------------
111  * Worker thread function for audacious data sampling.
112  * --------------------------------------------------- */ 
113 void *audacious_thread_func(void *pvoid)
114 {
115     int runnable=1;
116     static audacious_t items;
117     gint session,playpos,frames,length;
118     gint rate,freq,chans;
119     gchar *psong,*pfilename;
120     struct timespec abstime;
121
122     pvoid=(void *)pvoid;  /* avoid warning */
123     session=0;
124     psong=NULL;
125     pfilename=NULL;
126
127     /* Loop until the main thread sets the runnable signal to 0. */
128     while(runnable) {
129
130         for (;;) {  /* convenience loop so we can break below */
131
132             if (!xmms_remote_is_running (session)) {
133                 memset(&items,0,sizeof(items));
134                 strcpy(items[AUDACIOUS_STATUS],"Not running");
135                 break;
136             }
137
138             /* Player status */
139             if (xmms_remote_is_paused (session))
140                 strcpy(items[AUDACIOUS_STATUS],"Paused");
141             else if (xmms_remote_is_playing (session))
142                  strcpy(items[AUDACIOUS_STATUS],"Playing");
143             else
144                  strcpy(items[AUDACIOUS_STATUS],"Stopped");
145
146             /* Current song title */
147             playpos = xmms_remote_get_playlist_pos (session);
148             psong = xmms_remote_get_playlist_title (session, playpos);
149             if (psong) {
150                 strncpy(items[AUDACIOUS_TITLE],psong,sizeof(items[AUDACIOUS_TITLE])-1);
151                 g_free (psong);
152                 psong=NULL;
153             }
154
155             /* Current song length as MM:SS */
156             frames = xmms_remote_get_playlist_time (session,playpos);
157             length = frames / 1000;
158             snprintf(items[AUDACIOUS_LENGTH],sizeof(items[AUDACIOUS_LENGTH])-1,
159                      "%d:%.2d", length / 60, length % 60);
160
161             /* Current song length in seconds */
162             snprintf(items[AUDACIOUS_LENGTH_SECONDS],sizeof(items[AUDACIOUS_LENGTH_SECONDS])-1,
163                      "%d", length);
164
165             /* Current song position as MM:SS */
166             frames = xmms_remote_get_output_time (session);
167             length = frames / 1000;
168             snprintf(items[AUDACIOUS_POSITION],sizeof(items[AUDACIOUS_POSITION])-1,
169                      "%d:%.2d", length / 60, length % 60);
170
171             /* Current song position in seconds */
172             snprintf(items[AUDACIOUS_POSITION_SECONDS],sizeof(items[AUDACIOUS_POSITION_SECONDS])-1,
173                      "%d", length);
174
175             /* Current song bitrate */
176             xmms_remote_get_info (session, &rate, &freq, &chans);
177             snprintf(items[AUDACIOUS_BITRATE],sizeof(items[AUDACIOUS_BITRATE])-1, "%d", rate);
178
179             /* Current song frequency */
180             snprintf(items[AUDACIOUS_FREQUENCY],sizeof(items[AUDACIOUS_FREQUENCY])-1, "%d", freq);
181
182             /* Current song channels */
183             snprintf(items[AUDACIOUS_CHANNELS],sizeof(items[AUDACIOUS_CHANNELS])-1, "%d", chans);
184
185             /* Current song filename */
186             pfilename = xmms_remote_get_playlist_file (session,playpos);
187             if (pfilename) {
188                 strncpy(items[AUDACIOUS_FILENAME],pfilename,sizeof(items[AUDACIOUS_FILENAME])-1);
189                 g_free (pfilename);
190                 pfilename=NULL;
191             }
192
193             /* Length of the Playlist (number of songs) */
194             length = xmms_remote_get_playlist_length (session);
195             snprintf(items[AUDACIOUS_PLAYLIST_LENGTH],sizeof(items[AUDACIOUS_PLAYLIST_LENGTH])-1, "%d", length);
196
197             /* Playlist position (index of song) */
198             snprintf(items[AUDACIOUS_PLAYLIST_POSITION],sizeof(items[AUDACIOUS_PLAYLIST_POSITION])-1, "%d", playpos+1);
199
200             break;
201         }
202
203         /* Deliver the refreshed items array to audacious_items. */
204         pthread_mutex_lock(&info.audacious.item_mutex);
205         memcpy(&audacious_items,items,sizeof(items));
206         pthread_mutex_unlock(&info.audacious.item_mutex);
207
208         /* Get absolute time 1 sec in the future. */
209         clock_gettime (CLOCK_REALTIME, &abstime);
210         abstime.tv_sec += 1;
211
212         /* Wait for a second before looping or until signalled to stop. */
213         if (pthread_cond_timedwait (&info.audacious.runnable_cond, 
214                                      &info.audacious.runnable_mutex, 
215                                      &abstime) != ETIMEDOUT)
216         {
217             runnable=0;
218             (void) pthread_mutex_unlock (&info.audacious.runnable_mutex);
219         }
220
221     }
222
223     pthread_exit(NULL);
224 }