~ubuntu-branches/ubuntu/saucy/banshee/saucy-proposed

« back to all changes in this revision

Viewing changes to .pc/Don-t-use-the-new-decoded-pad-signal-of-decodebin.patch/libbanshee/banshee-bpmdetector.c

  • Committer: Package Import Robot
  • Author(s): Chow Loong Jin
  • Date: 2013-06-05 21:05:42 UTC
  • mfrom: (6.4.27 sid)
  • Revision ID: package-import@ubuntu.com-20130605210542-mpm73q57pyx928ml
Tags: 2.6.1-2ubuntu1
* [cce9151] Merge from unstable, remaining changes:
  - Enable and recommend SoundMenu and Disable NotificationArea by default
  - Disable boo and karma extensions
  - Move desktop file for Meego UI to /usr/share/une/applications
  - Change the url for the Amazon store redirector
  - [9b356d6] Add workaround for set_Height exception.
  - [ccbcbbd] Make Banshee translatable in Launchpad
  - [2094ee5] Bump libgpod build-dep version to 0.8.2-7~
  - [03c8cad] Set debian-branch to ubuntu/raring

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
//
 
2
// banshee-bpmdetector.c
 
3
//
 
4
// Author:
 
5
//   Gabriel Burt <gburt@novell.com>
 
6
//
 
7
// Copyright (C) 2008 Novell, Inc.
 
8
//
 
9
// Permission is hereby granted, free of charge, to any person obtaining
 
10
// a copy of this software and associated documentation files (the
 
11
// "Software"), to deal in the Software without restriction, including
 
12
// without limitation the rights to use, copy, modify, merge, publish,
 
13
// distribute, sublicense, and/or sell copies of the Software, and to
 
14
// permit persons to whom the Software is furnished to do so, subject to
 
15
// the following conditions:
 
16
//
 
17
// The above copyright notice and this permission notice shall be
 
18
// included in all copies or substantial portions of the Software.
 
19
//
 
20
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 
21
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 
22
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 
23
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 
24
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 
25
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 
26
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
27
//
 
28
 
 
29
#ifdef HAVE_CONFIG_H
 
30
#  include "config.h"
 
31
#endif
 
32
 
 
33
#include <stdio.h>
 
34
#include <string.h>
 
35
#include <glib/gi18n.h>
 
36
 
 
37
#include "banshee-gst.h"
 
38
#include "banshee-tagger.h"
 
39
 
 
40
typedef struct BansheeBpmDetector BansheeBpmDetector;
 
41
 
 
42
typedef void (* BansheeBpmDetectorFinishedCallback) ();
 
43
typedef void (* BansheeBpmDetectorProgressCallback) (double bpm);
 
44
typedef void (* BansheeBpmDetectorErrorCallback)    (const gchar *error, const gchar *debug);
 
45
 
 
46
// Only analyze 20 seconds of audio per song
 
47
#define BPM_DETECT_ANALYSIS_DURATION_MS 20*1000
 
48
 
 
49
struct BansheeBpmDetector {
 
50
    gboolean is_detecting;
 
51
 
 
52
    /*
 
53
     * You can run this pipeline on the cmd line with:
 
54
     * gst-launch -m filesrc location=/path/to/my.mp3 ! decodebin ! \
 
55
     *    audioconvert ! bpmdetect ! fakesink
 
56
     */
 
57
 
 
58
    GstElement *pipeline;
 
59
    GstElement *filesrc;
 
60
    GstElement *decodebin;
 
61
    GstElement *audioconvert;
 
62
    GstElement *bpmdetect;
 
63
    GstElement *fakesink;
 
64
    
 
65
    BansheeBpmDetectorProgressCallback progress_cb;
 
66
    BansheeBpmDetectorFinishedCallback finished_cb;
 
67
    BansheeBpmDetectorErrorCallback error_cb;
 
68
};
 
69
 
 
70
// ---------------------------------------------------------------------------
 
71
// Private Functions
 
72
// ---------------------------------------------------------------------------
 
73
 
 
74
static void
 
75
bbd_raise_error (BansheeBpmDetector *detector, const gchar *error, const gchar *debug)
 
76
{
 
77
    printf ("bpm_detect got error: %s %s\n", error, debug);
 
78
    g_return_if_fail (detector != NULL);
 
79
 
 
80
    if (detector->error_cb != NULL) {
 
81
        detector->error_cb (error, debug);
 
82
    }
 
83
}
 
84
 
 
85
static void
 
86
bbd_pipeline_process_tag (const GstTagList *tag_list, const gchar *tag_name, BansheeBpmDetector *detector)
 
87
{
 
88
    const GValue *value;
 
89
    gint value_count;
 
90
    double bpm;
 
91
    
 
92
    g_return_if_fail (detector != NULL);
 
93
 
 
94
    if (detector->progress_cb == NULL) {
 
95
        return;
 
96
    }
 
97
 
 
98
    if (strcmp (tag_name, GST_TAG_BEATS_PER_MINUTE)) {
 
99
        return;
 
100
    }
 
101
 
 
102
    value_count = gst_tag_list_get_tag_size (tag_list, tag_name);
 
103
    if (value_count < 1) {
 
104
        return;
 
105
    }
 
106
    
 
107
    value = gst_tag_list_get_value_index (tag_list, tag_name, 0);
 
108
    if (value != NULL && G_VALUE_HOLDS_DOUBLE (value)) {
 
109
        bpm = g_value_get_double (value);
 
110
        detector->progress_cb (bpm);
 
111
    }
 
112
}
 
113
 
 
114
static gboolean
 
115
bbd_pipeline_bus_callback (GstBus *bus, GstMessage *message, gpointer data)
 
116
{
 
117
    BansheeBpmDetector *detector = (BansheeBpmDetector *)data;
 
118
 
 
119
    g_return_val_if_fail (detector != NULL, FALSE);
 
120
 
 
121
    switch (GST_MESSAGE_TYPE (message)) {
 
122
        case GST_MESSAGE_TAG: {
 
123
            GstTagList *tags;
 
124
            gst_message_parse_tag (message, &tags);
 
125
            if (GST_IS_TAG_LIST (tags)) {
 
126
                gst_tag_list_foreach (tags, (GstTagForeachFunc)bbd_pipeline_process_tag, detector);
 
127
                gst_tag_list_free (tags);
 
128
            }
 
129
            break;
 
130
        }
 
131
 
 
132
        case GST_MESSAGE_ERROR: {
 
133
            GError *error;
 
134
            gchar *debug;
 
135
            
 
136
            gst_message_parse_error (message, &error, &debug);
 
137
            bbd_raise_error (detector, error->message, debug);
 
138
            g_error_free (error);
 
139
            g_free (debug);
 
140
            
 
141
            detector->is_detecting = FALSE;
 
142
            break;
 
143
        }
 
144
 
 
145
        case GST_MESSAGE_EOS: {
 
146
            detector->is_detecting = FALSE;
 
147
            gst_element_set_state (GST_ELEMENT (detector->pipeline), GST_STATE_NULL);
 
148
 
 
149
            if (detector->finished_cb != NULL) {
 
150
                detector->finished_cb ();
 
151
            }
 
152
            break;
 
153
        }
 
154
        
 
155
        default: break;
 
156
    }
 
157
    
 
158
    return TRUE;
 
159
}
 
160
 
 
161
static void
 
162
bbd_new_decoded_pad(GstElement *decodebin, GstPad *pad, 
 
163
    gboolean last, gpointer data)
 
164
{
 
165
    GstCaps *caps;
 
166
    GstStructure *str;
 
167
    GstPad *audiopad;
 
168
    BansheeBpmDetector *detector = (BansheeBpmDetector *)data;
 
169
 
 
170
    g_return_if_fail(detector != NULL);
 
171
 
 
172
    audiopad = gst_element_get_static_pad(detector->audioconvert, "sink");
 
173
    
 
174
    if(GST_PAD_IS_LINKED(audiopad)) {
 
175
        g_object_unref(audiopad);
 
176
        return;
 
177
    }
 
178
 
 
179
    caps = gst_pad_query_caps(pad, NULL);
 
180
    str = gst_caps_get_structure(caps, 0);
 
181
    
 
182
    if(!g_strrstr(gst_structure_get_name(str), "audio")) {
 
183
        gst_caps_unref(caps);
 
184
        gst_object_unref(audiopad);
 
185
        return;
 
186
    }
 
187
   
 
188
    gst_caps_unref(caps);
 
189
    gst_pad_link(pad, audiopad);
 
190
}
 
191
 
 
192
static gboolean
 
193
bbd_pipeline_construct (BansheeBpmDetector *detector)
 
194
{
 
195
    g_return_val_if_fail (detector != NULL, FALSE);
 
196
 
 
197
    if (detector->pipeline != NULL) {
 
198
        return TRUE;
 
199
    }
 
200
        
 
201
    detector->pipeline = gst_pipeline_new ("pipeline");
 
202
    if (detector->pipeline == NULL) {
 
203
        bbd_raise_error (detector, _("Could not create pipeline"), NULL);
 
204
        return FALSE;
 
205
    }
 
206
 
 
207
    detector->filesrc = gst_element_factory_make ("filesrc", "filesrc");
 
208
    if (detector->filesrc == NULL) {
 
209
        bbd_raise_error (detector, _("Could not create filesrc element"), NULL);
 
210
        return FALSE;
 
211
    }
 
212
  
 
213
    detector->decodebin = gst_element_factory_make ("decodebin", "decodebin");
 
214
    if (detector->decodebin == NULL) {
 
215
        bbd_raise_error (detector, _("Could not create decodebin plugin"), NULL);
 
216
        return FALSE;
 
217
    }
 
218
 
 
219
    detector->audioconvert = gst_element_factory_make ("audioconvert", "audioconvert");
 
220
    if (detector->audioconvert == NULL) {
 
221
        bbd_raise_error (detector, _("Could not create audioconvert plugin"), NULL);
 
222
        return FALSE;
 
223
    }
 
224
 
 
225
    detector->bpmdetect = gst_element_factory_make ("bpmdetect", "bpmdetect");
 
226
    if (detector->bpmdetect == NULL) {
 
227
        bbd_raise_error (detector, _("Could not create bpmdetect plugin"), NULL);
 
228
        return FALSE;
 
229
    }
 
230
    
 
231
    detector->fakesink = gst_element_factory_make ("fakesink", "bpmfakesink");
 
232
    if (detector->fakesink == NULL) {
 
233
        bbd_raise_error (detector, _("Could not create fakesink plugin"), NULL);
 
234
        return FALSE;
 
235
    }
 
236
 
 
237
    gst_bin_add_many (GST_BIN (detector->pipeline),
 
238
        detector->filesrc, detector->decodebin, detector->audioconvert,
 
239
        detector->bpmdetect, detector->fakesink, NULL);
 
240
 
 
241
    if (!gst_element_link (detector->filesrc, detector->decodebin)) {
 
242
        bbd_raise_error (detector, _("Could not link pipeline elements"), NULL);
 
243
        return FALSE;
 
244
    }
 
245
 
 
246
    // decodebin and audioconvert are linked dynamically when the decodebin creates a new pad
 
247
    g_signal_connect(detector->decodebin, "new-decoded-pad", 
 
248
        G_CALLBACK(bbd_new_decoded_pad), detector);
 
249
 
 
250
    if (!gst_element_link_many (detector->audioconvert, detector->bpmdetect, detector->fakesink, NULL)) {
 
251
        bbd_raise_error (detector, _("Could not link pipeline elements"), NULL);
 
252
        return FALSE;
 
253
    }
 
254
        
 
255
    gst_bus_add_watch (gst_pipeline_get_bus (GST_PIPELINE (detector->pipeline)), bbd_pipeline_bus_callback, detector);
 
256
 
 
257
    return TRUE;
 
258
}
 
259
 
 
260
// ---------------------------------------------------------------------------
 
261
// Internal Functions
 
262
// ---------------------------------------------------------------------------
 
263
 
 
264
BansheeBpmDetector *
 
265
bbd_new ()
 
266
{
 
267
    return g_new0 (BansheeBpmDetector, 1);
 
268
}
 
269
 
 
270
void 
 
271
bbd_cancel (BansheeBpmDetector *detector)
 
272
{
 
273
    g_return_if_fail (detector != NULL);
 
274
    
 
275
    if (detector->pipeline != NULL && GST_IS_ELEMENT (detector->pipeline)) {
 
276
        gst_element_set_state (GST_ELEMENT (detector->pipeline), GST_STATE_NULL);
 
277
        gst_object_unref (GST_OBJECT (detector->pipeline));
 
278
        detector->pipeline = NULL;
 
279
    }
 
280
}
 
281
 
 
282
void
 
283
bbd_destroy (BansheeBpmDetector *detector)
 
284
{
 
285
    g_return_if_fail (detector != NULL);
 
286
    
 
287
    bbd_cancel (detector);
 
288
    
 
289
    g_free (detector);
 
290
    detector = NULL;
 
291
}
 
292
 
 
293
gboolean
 
294
bbd_process_file (BansheeBpmDetector *detector, const gchar *path)
 
295
{
 
296
    //static GstFormat format = GST_FORMAT_TIME;
 
297
    //gint64 duration, duration_ms, start_ms, end_ms;
 
298
 
 
299
    g_return_val_if_fail (detector != NULL, FALSE);
 
300
 
 
301
    if (!bbd_pipeline_construct (detector)) {
 
302
        return FALSE;
 
303
    }
 
304
    
 
305
    detector->is_detecting = TRUE;
 
306
    gst_element_set_state (detector->fakesink, GST_STATE_NULL);
 
307
    g_object_set (G_OBJECT (detector->filesrc), "location", path, NULL);
 
308
 
 
309
    // TODO listen for transition to STATE_PLAYING, then
 
310
    // Determine how long the file is, and set the detector to base its analysis off the middle 30 seconds of the song
 
311
 
 
312
    /*if (gst_element_query_duration (detector->fakesink, &format, &duration)) {
 
313
        duration_ms = duration / GST_MSECOND;
 
314
 
 
315
        start_ms = CLAMP((duration_ms / 2) - (BPM_DETECT_ANALYSIS_DURATION_MS/2), 0, duration_ms);
 
316
        end_ms   = CLAMP(start_ms + BPM_DETECT_ANALYSIS_DURATION_MS, start_ms, duration_ms);
 
317
        printf("Analyzing song %s starting at %d ending at %d\n", path, start_ms/1000, end_ms/1000);
 
318
 
 
319
        if (gst_element_seek (detector->fakesink, 1.0, 
 
320
            //GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_SKIP | GST_SEEK_FLAG_KEY_UNIT,
 
321
            GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT,
 
322
            GST_SEEK_TYPE_SET, start_ms * GST_MSECOND, 
 
323
            GST_SEEK_TYPE_SET, end_ms * GST_MSECOND))
 
324
        {
 
325
        }
 
326
    }*/
 
327
 
 
328
    gst_element_set_state (detector->pipeline, GST_STATE_PLAYING);
 
329
    return TRUE;
 
330
}
 
331
 
 
332
void
 
333
bbd_set_progress_callback (BansheeBpmDetector *detector, BansheeBpmDetectorProgressCallback cb)
 
334
{
 
335
    g_return_if_fail (detector != NULL);
 
336
    detector->progress_cb = cb;
 
337
}
 
338
 
 
339
void
 
340
bbd_set_finished_callback (BansheeBpmDetector *detector, BansheeBpmDetectorFinishedCallback cb)
 
341
{
 
342
    g_return_if_fail (detector != NULL);
 
343
    detector->finished_cb = cb;
 
344
}
 
345
 
 
346
void
 
347
bbd_set_error_callback (BansheeBpmDetector *detector, BansheeBpmDetectorErrorCallback cb)
 
348
{
 
349
    g_return_if_fail (detector != NULL);
 
350
    detector->error_cb = cb;
 
351
}
 
352
 
 
353
gboolean
 
354
bbd_get_is_detecting (BansheeBpmDetector *detector)
 
355
{
 
356
    g_return_val_if_fail (detector != NULL, FALSE);
 
357
    return detector->is_detecting;
 
358
}