1 /* FluidSynth - A Software Synthesizer
2  *
3  * Copyright (C) 2003  Peter Hanappe and others.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public License
7  * as published by the Free Software Foundation; either version 2 of
8  * the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the Free
17  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18  * 02110-1301, USA
19  */
20 
21 
22 #include "fluid_rvoice_handler.h"
23 
24 
new_fluid_rvoice_handler(void)25 fluid_rvoice_handler_t* new_fluid_rvoice_handler(void)
26 {
27   fluid_rvoice_handler_t* handler;
28 
29   handler = FLUID_NEW(fluid_rvoice_handler_t);
30   if (handler == NULL) {
31     FLUID_LOG(FLUID_ERR, "Out of memory");
32     return NULL;
33   }
34   FLUID_MEMSET(handler, 0, sizeof(fluid_rvoice_handler_t));
35 
36   return handler;
37 }
38 
delete_fluid_rvoice_handler(fluid_rvoice_handler_t * handler)39 void delete_fluid_rvoice_handler(fluid_rvoice_handler_t* handler)
40 {
41   if (handler == NULL)
42     return;
43 
44 #if 0
45   FLUID_FREE(handler->finished_voices);
46 #endif
47   FLUID_FREE(handler->voices);
48   FLUID_FREE(handler);
49 }
50 
51 
52 int
fluid_rvoice_handler_add_voice(fluid_rvoice_handler_t * handler,fluid_rvoice_t * voice)53 fluid_rvoice_handler_add_voice(fluid_rvoice_handler_t* handler, fluid_rvoice_t* voice)
54 {
55   if (handler->active_voices >= handler->polyphony) {
56     FLUID_LOG(FLUID_WARN, "Trying to exceed polyphony in fluid_rvoice_handler_add_voice");
57     return FLUID_FAILED;
58   }
59   handler->voices[handler->active_voices++] = voice;
60   return FLUID_OK;
61 }
62 
63 /**
64  * Update polyphony - max number of voices (NOTE: not hard real-time capable)
65  * @return FLUID_OK or FLUID_FAILED
66  */
67 int
fluid_rvoice_handler_set_polyphony(fluid_rvoice_handler_t * handler,int value)68 fluid_rvoice_handler_set_polyphony(fluid_rvoice_handler_t* handler, int value)
69 {
70   void* newptr;
71   if (handler->active_voices > value)
72     return FLUID_FAILED;
73 #if 0
74   if (handler->finished_voice_count > value)
75     return FLUID_FAILED;
76 #endif
77 
78   newptr = FLUID_REALLOC(handler->voices, value * sizeof(fluid_rvoice_t*));
79   if (newptr == NULL)
80     return FLUID_FAILED;
81   handler->voices = newptr;
82 #if 0
83   newptr = FLUID_REALLOC(handler->finished_voices, value * sizeof(fluid_rvoice_t*));
84   if (newptr == NULL)
85     return FLUID_FAILED;
86   handler->finished_voices = newptr;
87 #endif
88 
89   handler->polyphony = value;
90   return FLUID_OK;
91 }
92 
93 static void
fluid_rvoice_handler_remove_voice(fluid_rvoice_handler_t * handler,int index)94 fluid_rvoice_handler_remove_voice(fluid_rvoice_handler_t* handler, int index)
95 {
96 #if 0
97   if (handler->finished_voice_count < handler->polyphony)
98     handler->finished_voices[handler->finished_voice_count++] = handler->voices[index];
99 #endif
100 
101   if (handler->remove_voice_callback != NULL)
102     handler->remove_voice_callback(handler->remove_voice_callback_userdata,
103                                    handler->voices[index]);
104 
105   handler->active_voices--;
106   if (index < handler->active_voices) /* Move the last voice into the "hole" */
107     handler->voices[index] = handler->voices[handler->active_voices];
108 }
109 
110 /**
111  * Synthesize one voice
112  * @return Number of samples written
113  */
114 #if 0
115 static inline int
116 fluid_rvoice_handler_write_one(fluid_rvoice_handler_t* handler, int index,
117                                fluid_real_t* buf, int blockcount)
118 {
119   int i, result = 0;
120   fluid_rvoice_t* voice = handler->voices[index];
121   for (i=0; i < blockcount; i++) {
122     int s = fluid_rvoice_write(voice, buf);
123     if (s == -1) {
124       FLUID_MEMSET(buf, 0, FLUID_BUFSIZE*sizeof(fluid_real_t));
125       s = FLUID_BUFSIZE;
126     }
127     buf += s;
128     result += s;
129   }
130   return result;
131 }
132 #endif
133 
134 /**
135  * Synthesize one voice and add to buffer.
136  * NOTE: If return value is less than blockcount*FLUID_BUFSIZE, that means
137  * voice has been finished, removed and possibly replaced with another voice.
138  * @return Number of samples written
139  */
140 static inline int
fluid_rvoice_handler_mix_one(fluid_rvoice_handler_t * handler,int index,fluid_real_t ** bufs,unsigned int blockcount,unsigned int bufcount)141 fluid_rvoice_handler_mix_one(fluid_rvoice_handler_t* handler, int index,
142                                fluid_real_t** bufs, unsigned int blockcount, unsigned int bufcount)
143 {
144   unsigned int i, j=0, result = 0;
145   fluid_rvoice_t* voice = handler->voices[index];
146 
147   fluid_real_t local_buf[FLUID_BUFSIZE*blockcount];
148 
149   for (i=0; i < blockcount; i++) {
150     int s = fluid_rvoice_write(voice, &local_buf[FLUID_BUFSIZE*i]);
151     if (s == -1) {
152       s = FLUID_BUFSIZE; /* Voice is quiet, TODO: optimize away memset/mix */
153       FLUID_MEMSET(&local_buf[FLUID_BUFSIZE*i], 0, FLUID_BUFSIZE*sizeof(fluid_real_t*));
154     }
155     result += s;
156     if (s < FLUID_BUFSIZE) {
157       j = 1;
158       break;
159     }
160   }
161   fluid_rvoice_buffers_mix(&voice->buffers, local_buf, result, bufs, bufcount);
162 
163   if (j)
164     fluid_rvoice_handler_remove_voice(handler, index);
165 
166   return result;
167 }
168 
169 static inline void
fluid_resetbufs(int blockcount,int bufcount,fluid_real_t ** bufs)170 fluid_resetbufs(int blockcount, int bufcount, fluid_real_t** bufs)
171 {
172   int i;
173   for (i=0; i < bufcount; i++)
174     FLUID_MEMSET(bufs[i], 0, blockcount * FLUID_BUFSIZE * sizeof(fluid_real_t));
175 }
176 
177 /**
178  * Single-threaded scenario, no worker threads
179  */
180 static inline void
fluid_rvoice_handler_render_loop_simple(fluid_rvoice_handler_t * handler,int blockcount,int bufcount,fluid_real_t ** bufs)181 fluid_rvoice_handler_render_loop_simple(fluid_rvoice_handler_t* handler,
182                             int blockcount, int bufcount, fluid_real_t** bufs)
183 {
184   int i;
185   int scount = blockcount * FLUID_BUFSIZE;
186   for (i=0; i < handler->active_voices; i++) {
187     int s = fluid_rvoice_handler_mix_one(handler, i, bufs, blockcount, bufcount);
188     if (s < scount) i--; /* Need to render the moved voice as well */
189   }
190 }
191 
192 
193 /**
194  * @param blockcount number of samples to render is blockcount*FLUID_BUFSIZE
195  * @param bufcount number of buffers to render into
196  * @param bufs array of bufcount buffers, each containing blockcount*FLUID_BUFSIZE samples
197  */
198 void
fluid_rvoice_handler_render(fluid_rvoice_handler_t * handler,int blockcount,int bufcount,fluid_real_t ** bufs)199 fluid_rvoice_handler_render(fluid_rvoice_handler_t* handler,
200                             int blockcount, int bufcount, fluid_real_t** bufs)
201 {
202   fluid_resetbufs(blockcount, bufcount, bufs);
203   fluid_rvoice_handler_render_loop_simple(handler, blockcount, bufcount, bufs);
204 }
205