strtok() is recursive by default on win32.
[mpd-mk.git] / src / crossfade.c
blob7989b1dc91cfd1eda0064e5937148935f439d383
1 /*
2 * Copyright (C) 2003-2010 The Music Player Daemon Project
3 * http://www.musicpd.org
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "config.h"
21 #include "crossfade.h"
22 #include "pcm_mix.h"
23 #include "chunk.h"
24 #include "audio_format.h"
25 #include "tag.h"
27 #include <assert.h>
28 #include <string.h>
29 #include <stdlib.h>
30 #include <glib.h>
32 #undef G_LOG_DOMAIN
33 #define G_LOG_DOMAIN "crossfade"
35 #ifdef G_OS_WIN32
36 #define strtok_r(s,d,p) strtok(s,d)
37 #endif
39 static float mixramp_interpolate(char *ramp_list, float required_db)
41 float db, secs, last_db = nan(""), last_secs = 0;
42 char *ramp_str, *save_str = NULL;
44 /* ramp_list is a string of pairs of dBs and seconds that describe the
45 * volume profile. Delimiters are semi-colons between pairs and spaces
46 * between the dB and seconds of a pair.
47 * The dB values must be monotonically increasing for this to work. */
49 while (1) {
50 /* Parse the dB tokens out of the input string. */
51 ramp_str = strtok_r(ramp_list, " ", &save_str);
53 /* Tell strtok to continue next time round. */
54 ramp_list = NULL;
56 /* Parse the dB value. */
57 if (NULL == ramp_str) {
58 return nan("");
60 db = (float)atof(ramp_str);
62 /* Parse the time. */
63 ramp_str = strtok_r(NULL, ";", &save_str);
64 if (NULL == ramp_str) {
65 return nan("");
67 secs = (float)atof(ramp_str);
69 /* Check for exact match. */
70 if (db == required_db) {
71 return secs;
74 /* Save if too quiet. */
75 if (db < required_db) {
76 last_db = db;
77 last_secs = secs;
78 continue;
81 /* If required db < any stored value, use the least. */
82 if (isnan(last_db)) {
83 return secs;
86 /* Finally, interpolate linearly. */
87 secs = last_secs + (required_db - last_db) * (secs - last_secs) / (db - last_db);
88 return secs;
92 unsigned cross_fade_calc(float duration, float total_time,
93 float mixramp_db, float mixramp_delay,
94 char *mixramp_start, char *mixramp_prev_end,
95 const struct audio_format *af,
96 const struct audio_format *old_format,
97 unsigned max_chunks)
99 unsigned int chunks = 0;
100 float chunks_f;
101 float mixramp_overlap;
103 if (duration < 0 || duration >= total_time ||
104 /* we can't crossfade when the audio formats are different */
105 !audio_format_equals(af, old_format))
106 return 0;
108 assert(duration >= 0);
109 assert(audio_format_valid(af));
111 chunks_f = (float)audio_format_time_to_size(af) / (float)CHUNK_SIZE;
113 if (isnan(mixramp_delay) || !(mixramp_start) || !(mixramp_prev_end)) {
114 chunks = (chunks_f * duration + 0.5);
115 } else {
116 /* Calculate mixramp overlap.
117 * FIXME factor in ReplayGain for both songs. */
118 mixramp_overlap = mixramp_interpolate(mixramp_start, mixramp_db)
119 + mixramp_interpolate(mixramp_prev_end, mixramp_db);
120 if (!isnan(mixramp_overlap) && (mixramp_delay <= mixramp_overlap)) {
121 chunks = (chunks_f * (mixramp_overlap - mixramp_delay));
122 g_debug("will overlap %d chunks, %fs", chunks,
123 mixramp_overlap - mixramp_delay);
127 if (chunks > max_chunks) {
128 chunks = max_chunks;
129 g_warning("audio_buffer_size too small for computed MixRamp overlap");
132 return chunks;
135 void cross_fade_apply(struct music_chunk *a, const struct music_chunk *b,
136 const struct audio_format *format,
137 float mix_ratio)
139 size_t size;
141 assert(a != NULL);
142 assert(b != NULL);
143 assert(a->length == 0 || b->length == 0 ||
144 audio_format_equals(&a->audio_format, &b->audio_format));
146 if (a->tag == NULL && b->tag != NULL)
147 /* merge the tag into the destination chunk */
148 a->tag = tag_dup(b->tag);
150 size = b->length > a->length
151 ? a->length
152 : b->length;
154 pcm_mix(a->data,
155 b->data,
156 size,
157 format,
158 mix_ratio);
160 if (b->length > a->length) {
161 /* the second buffer is larger than the first one:
162 there is unmixed rest at the end. Copy it over.
163 The output buffer API guarantees that there is
164 enough room in a->data. */
166 #ifndef NDEBUG
167 if (a->length == 0)
168 a->audio_format = b->audio_format;
169 #endif
171 memcpy(a->data + a->length,
172 b->data + a->length,
173 b->length - a->length);
174 a->length = b->length;