2 Calf Box, an open source musical instrument.
3 Copyright (C) 2010-2013 Krzysztof Foltman
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 3 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
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
19 #include "prefetch_pipe.h"
28 // Don't bother fetching less than 4 (mono) or 8 KB (stereo)
29 #define PIPE_MIN_PREFETCH_SIZE_FRAMES 2048
31 void cbox_prefetch_pipe_init(struct cbox_prefetch_pipe
*pipe
, uint32_t buffer_size
)
33 pipe
->data
= malloc(buffer_size
);
34 pipe
->buffer_size
= buffer_size
;
36 pipe
->state
= pps_free
;
39 gboolean
cbox_prefetch_pipe_openfile(struct cbox_prefetch_pipe
*pipe
)
41 if (pipe
->waveform
->taritem
)
43 int fd
= cbox_tarfile_openitem(pipe
->waveform
->tarfile
, pipe
->waveform
->taritem
);
45 pipe
->sndfile
= sf_open_fd(fd
, SFM_READ
, &pipe
->info
, TRUE
);
50 pipe
->sndfile
= sf_open(pipe
->waveform
->canonical_name
, SFM_READ
, &pipe
->info
);
53 pipe
->file_pos_frame
= sf_seek(pipe
->sndfile
, pipe
->waveform
->preloaded_frames
, SEEK_SET
);
54 if (pipe
->file_loop_end
> pipe
->info
.frames
)
55 pipe
->file_loop_end
= pipe
->info
.frames
;
56 pipe
->buffer_loop_end
= pipe
->buffer_size
/ (sizeof(int16_t) * pipe
->info
.channels
);
57 pipe
->produced
= pipe
->file_pos_frame
;
59 pipe
->state
= pps_active
;
64 void cbox_prefetch_pipe_consumed(struct cbox_prefetch_pipe
*pipe
, uint32_t frames
)
66 pipe
->consumed
+= frames
;
69 void cbox_prefetch_pipe_fetch(struct cbox_prefetch_pipe
*pipe
)
74 // XXXKF take consumption rate into account
76 // How many frames left to consume
77 int32_t supply
= pipe
->produced
- pipe
->consumed
;
80 // Overrun already occurred. Cut the losses by skipping already missed
82 uint32_t overrun
= -supply
;
84 // XXXKF This may or may not be stupid. I didn't put much thought into it.
85 pipe
->produced
+= overrun
;
86 pipe
->file_pos_frame
= sf_seek(pipe
->sndfile
, overrun
, SEEK_CUR
);
87 pipe
->write_ptr
+= overrun
;
88 if (pipe
->write_ptr
>= pipe
->buffer_loop_end
)
89 pipe
->write_ptr
%= pipe
->buffer_loop_end
;
92 if (supply
>= pipe
->buffer_loop_end
)
95 // How many frames to read to fill the full prefetch size
96 int32_t readsize
= pipe
->buffer_loop_end
- supply
;
98 if (readsize
< PIPE_MIN_PREFETCH_SIZE_FRAMES
)
101 if (pipe
->write_ptr
== pipe
->buffer_loop_end
)
104 // If reading across buffer boundary, only read the part up to buffer
105 // end, and then retry from start of the buffer.
106 if (pipe
->write_ptr
+ readsize
> pipe
->buffer_loop_end
)
108 readsize
= pipe
->buffer_loop_end
- pipe
->write_ptr
;
111 // If past the file loop end, restart at file loop start
112 if (pipe
->file_pos_frame
>= pipe
->file_loop_end
)
114 if (pipe
->file_loop_start
== (uint32_t)-1 || (pipe
->loop_count
&& pipe
->play_count
>= pipe
->loop_count
- 1))
116 pipe
->finished
= TRUE
;
117 for (int i
= 0; i
< readsize
* pipe
->info
.channels
; i
++)
118 pipe
->data
[pipe
->write_ptr
* pipe
->info
.channels
+ i
] = rand();
124 pipe
->file_pos_frame
= pipe
->file_loop_start
;
125 sf_seek(pipe
->sndfile
, pipe
->file_loop_start
, SEEK_SET
);
128 // If reading across file loop boundary, read up to loop end and
130 if (pipe
->file_pos_frame
+ readsize
> pipe
->file_loop_end
)
132 readsize
= pipe
->file_loop_end
- pipe
->file_pos_frame
;
136 int32_t actread
= sf_readf_short(pipe
->sndfile
, pipe
->data
+ pipe
->write_ptr
* pipe
->info
.channels
, readsize
);
137 pipe
->produced
+= actread
;
138 pipe
->file_pos_frame
+= actread
;
139 pipe
->write_ptr
+= actread
;
143 void cbox_prefetch_pipe_closefile(struct cbox_prefetch_pipe
*pipe
)
145 assert(pipe
->state
== pps_closing
);
146 assert(pipe
->sndfile
);
147 sf_close(pipe
->sndfile
);
148 pipe
->sndfile
= NULL
;
149 pipe
->state
= pps_free
;
152 void cbox_prefetch_pipe_close(struct cbox_prefetch_pipe
*pipe
)
155 cbox_prefetch_pipe_closefile(pipe
);
163 static void *prefetch_thread(void *user_data
)
165 struct cbox_prefetch_stack
*stack
= user_data
;
167 while(!stack
->finished
)
170 for (int i
= 0; i
< stack
->pipe_count
; i
++)
172 struct cbox_prefetch_pipe
*pipe
= &stack
->pipes
[i
];
180 if (!cbox_prefetch_pipe_openfile(pipe
))
181 pipe
->state
= pps_error
;
182 assert(pipe
->state
!= pps_opening
);
186 pipe
->state
= pps_closing
;
188 cbox_prefetch_pipe_fetch(pipe
);
191 cbox_prefetch_pipe_closefile(pipe
);
201 struct cbox_prefetch_stack
*cbox_prefetch_stack_new(int npipes
, uint32_t buffer_size
)
203 struct cbox_prefetch_stack
*stack
= calloc(1, sizeof(struct cbox_prefetch_stack
));
204 stack
->pipes
= calloc(npipes
, sizeof(struct cbox_prefetch_pipe
));
206 for (int i
= 0; i
< npipes
; i
++)
208 cbox_prefetch_pipe_init(&stack
->pipes
[i
], buffer_size
);
209 stack
->pipes
[i
].next_free_pipe
= i
- 1;
211 stack
->pipe_count
= npipes
;
212 stack
->last_free_pipe
= npipes
- 1;
213 stack
->finished
= FALSE
;
215 if (pthread_create(&stack
->thr_prefetch
, NULL
, prefetch_thread
, stack
))
217 // XXXKF set thread priority
218 g_warning("Cannot create a prefetch thread. Exiting.\n");
225 struct cbox_prefetch_pipe
*cbox_prefetch_stack_pop(struct cbox_prefetch_stack
*stack
, struct cbox_waveform
*waveform
, uint32_t file_loop_start
, uint32_t file_loop_end
, uint32_t loop_count
)
227 // The stack may include some pipes that are already returned but not yet
228 // fully prepared for opening a new file
229 int pos
= stack
->last_free_pipe
;
230 while(pos
!= -1 && stack
->pipes
[pos
].state
!= pps_free
)
231 pos
= stack
->pipes
[pos
].next_free_pipe
;
235 struct cbox_prefetch_pipe
*pipe
= &stack
->pipes
[pos
];
237 stack
->last_free_pipe
= pipe
->next_free_pipe
;
238 pipe
->next_free_pipe
= -1;
240 pipe
->waveform
= waveform
;
241 if (file_loop_start
== (uint32_t)-1 && loop_count
)
243 pipe
->file_loop_start
= file_loop_start
;
244 pipe
->file_loop_end
= file_loop_end
;
245 pipe
->buffer_loop_end
= 0;
246 pipe
->finished
= FALSE
;
247 pipe
->returned
= FALSE
;
248 pipe
->produced
= waveform
->preloaded_frames
;
250 pipe
->play_count
= 0;
251 pipe
->loop_count
= loop_count
;
253 __sync_synchronize();
254 pipe
->state
= pps_opening
;
258 void cbox_prefetch_stack_push(struct cbox_prefetch_stack
*stack
, struct cbox_prefetch_pipe
*pipe
)
267 pipe
->state
= pps_free
;
270 // Close the file as soon as open operation completes
271 pipe
->returned
= TRUE
;
274 pipe
->state
= pps_closing
;
277 __sync_synchronize();
279 assert(pipe
->next_free_pipe
== -1);
280 pipe
->next_free_pipe
= stack
->last_free_pipe
;
281 int pos
= pipe
- stack
->pipes
;
283 __sync_synchronize();
284 stack
->last_free_pipe
= pos
;
287 int cbox_prefetch_stack_get_active_pipe_count(struct cbox_prefetch_stack
*stack
)
290 for (int i
= 0; i
< stack
->pipe_count
; i
++)
292 if (stack
->pipes
[i
].state
!= pps_free
)
298 void cbox_prefetch_stack_destroy(struct cbox_prefetch_stack
*stack
)
301 stack
->finished
= TRUE
;
302 pthread_join(stack
->thr_prefetch
, &result
);
303 for (int i
= 0; i
< stack
->pipe_count
; i
++)
304 cbox_prefetch_pipe_close(&stack
->pipes
[i
]);