summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apps/playback.c207
-rw-r--r--firmware/pcm_playback.c30
2 files changed, 124 insertions, 113 deletions
diff --git a/apps/playback.c b/apps/playback.c
index f4ec05b369..c6128ca007 100644
--- a/apps/playback.c
+++ b/apps/playback.c
@@ -71,6 +71,7 @@ static volatile bool paused;
71#define CODEC_MPC "/.rockbox/codecs/codecmpc.rock"; 71#define CODEC_MPC "/.rockbox/codecs/codecmpc.rock";
72#define CODEC_WAVPACK "/.rockbox/codecs/codecwavpack.rock"; 72#define CODEC_WAVPACK "/.rockbox/codecs/codecwavpack.rock";
73 73
74#define AUDIO_FILL_CYCLE (1024*256)
74#define AUDIO_DEFAULT_WATERMARK (1024*256) 75#define AUDIO_DEFAULT_WATERMARK (1024*256)
75#define AUDIO_DEFAULT_FILECHUNK (1024*32) 76#define AUDIO_DEFAULT_FILECHUNK (1024*32)
76 77
@@ -84,6 +85,7 @@ static volatile bool paused;
84#define AUDIO_FLUSH_RELOAD 8 85#define AUDIO_FLUSH_RELOAD 8
85#define AUDIO_CODEC_DONE 9 86#define AUDIO_CODEC_DONE 9
86#define AUDIO_FLUSH 10 87#define AUDIO_FLUSH 10
88#define AUDIO_TRACK_CHANGED 11
87 89
88#define CODEC_LOAD 1 90#define CODEC_LOAD 1
89#define CODEC_LOAD_DISK 2 91#define CODEC_LOAD_DISK 2
@@ -125,6 +127,8 @@ static volatile int codecbufused;
125static volatile int buf_ridx; 127static volatile int buf_ridx;
126static volatile int buf_widx; 128static volatile int buf_widx;
127 129
130static int last_peek_offset;
131
128/* Track information (count in file buffer, read/write indexes for 132/* Track information (count in file buffer, read/write indexes for
129 track ring structure. */ 133 track ring structure. */
130static int track_count; 134static int track_count;
@@ -424,6 +428,7 @@ void codec_configure_callback(int setting, void *value)
424void codec_track_changed(void) 428void codec_track_changed(void)
425{ 429{
426 track_changed = true; 430 track_changed = true;
431 // queue_post(&audio_queue, AUDIO_TRACK_CHANGED, 0);
427} 432}
428 433
429void yield_codecs(void) 434void yield_codecs(void)
@@ -438,25 +443,22 @@ void yield_codecs(void)
438 443
439void audio_fill_file_buffer(void) 444void audio_fill_file_buffer(void)
440{ 445{
441 size_t i; 446 size_t i, size;
442 int rc; 447 int rc;
443 448
444 tracks[track_widx].start_pos = ci.curpos; 449 if (current_fd < 0)
445 logf("Filling buffer..."); 450 return ;
451
446 i = 0; 452 i = 0;
447 while ((off_t)i < tracks[track_widx].filerem) { 453 size = MIN(tracks[track_widx].filerem, AUDIO_FILL_CYCLE);
454 while (i < size) {
448 /* Give codecs some processing time. */ 455 /* Give codecs some processing time. */
449 yield_codecs(); 456 yield_codecs();
450 457
451 if (!queue_empty(&audio_queue)) { 458 if (fill_bytesleft == 0)
452 logf("Filling interrupted");
453 return ;
454 }
455
456 if (fill_bytesleft < MIN((unsigned int)conf_filechunk,
457 tracks[track_widx].filerem - i))
458 break ; 459 break ;
459 rc = MIN(conf_filechunk, codecbuflen - buf_widx); 460 rc = MIN(conf_filechunk, codecbuflen - buf_widx);
461 rc = MIN(rc, (off_t)fill_bytesleft);
460 rc = read(current_fd, &codecbuf[buf_widx], rc); 462 rc = read(current_fd, &codecbuf[buf_widx], rc);
461 if (rc <= 0) { 463 if (rc <= 0) {
462 tracks[track_widx].filerem = 0; 464 tracks[track_widx].filerem = 0;
@@ -474,7 +476,8 @@ void audio_fill_file_buffer(void)
474 codecbufused += i; 476 codecbufused += i;
475 tracks[track_widx].filerem -= i; 477 tracks[track_widx].filerem -= i;
476 tracks[track_widx].filepos += i; 478 tracks[track_widx].filepos += i;
477 logf("Done:%d", tracks[track_widx].available); 479 /*logf("Filled:%d/%d", tracks[track_widx].available,
480 tracks[track_widx].filerem);*/
478 if (tracks[track_widx].filerem == 0) { 481 if (tracks[track_widx].filerem == 0) {
479 close(current_fd); 482 close(current_fd);
480 current_fd = -1; 483 current_fd = -1;
@@ -569,6 +572,7 @@ bool loadcodec(const char *trackname, bool start_play)
569 size = filesize(fd); 572 size = filesize(fd);
570 if ((off_t)fill_bytesleft < size + conf_watermark) { 573 if ((off_t)fill_bytesleft < size + conf_watermark) {
571 logf("Not enough space"); 574 logf("Not enough space");
575 fill_bytesleft = 0;
572 close(fd); 576 close(fd);
573 return false; 577 return false;
574 } 578 }
@@ -576,11 +580,6 @@ bool loadcodec(const char *trackname, bool start_play)
576 i = 0; 580 i = 0;
577 while (i < size) { 581 while (i < size) {
578 yield_codecs(); 582 yield_codecs();
579 if (!queue_empty(&audio_queue)) {
580 logf("Buffering interrupted");
581 close(fd);
582 return false;
583 }
584 583
585 copy_n = MIN(conf_filechunk, codecbuflen - buf_widx); 584 copy_n = MIN(conf_filechunk, codecbuflen - buf_widx);
586 rc = read(fd, &codecbuf[buf_widx], copy_n); 585 rc = read(fd, &codecbuf[buf_widx], copy_n);
@@ -609,7 +608,8 @@ bool audio_load_track(int offset, bool start_play, int peek_offset)
609 int rc, i; 608 int rc, i;
610 int copy_n; 609 int copy_n;
611 610
612 if (track_count >= MAX_TRACK) 611 if (track_count >= MAX_TRACK || tracks[track_widx].filesize != 0
612 || current_fd >= 0)
613 return false; 613 return false;
614 614
615 trackname = playlist_peek(peek_offset); 615 trackname = playlist_peek(peek_offset);
@@ -653,13 +653,6 @@ bool audio_load_track(int offset, bool start_play, int peek_offset)
653 //logf("%s", trackname); 653 //logf("%s", trackname);
654 logf("Buffering track:%d/%d", track_widx, track_ridx); 654 logf("Buffering track:%d/%d", track_widx, track_ridx);
655 655
656 if (!queue_empty(&audio_queue)) {
657 logf("Interrupted!");
658 //ci.stop_codec = true;
659 close(fd);
660 return false;
661 }
662
663 if (!get_metadata(&tracks[track_widx],fd,trackname,v1first)) { 656 if (!get_metadata(&tracks[track_widx],fd,trackname,v1first)) {
664 close(fd); 657 close(fd);
665 return false; 658 return false;
@@ -685,22 +678,11 @@ bool audio_load_track(int offset, bool start_play, int peek_offset)
685 } 678 }
686 679
687 i = tracks[track_widx].start_pos; 680 i = tracks[track_widx].start_pos;
681 size = MIN(size, AUDIO_FILL_CYCLE);
688 while (i < size) { 682 while (i < size) {
689 /* Give codecs some processing time to prevent glitches. */ 683 /* Give codecs some processing time to prevent glitches. */
690 yield_codecs(); 684 yield_codecs();
691 685
692 /* Limit buffering size at first run. */
693 if (conf_bufferlimit && (int)fill_bytesleft >= conf_bufferlimit) {
694 fill_bytesleft = conf_bufferlimit;
695 conf_bufferlimit = 0;
696 }
697
698 if (!queue_empty(&audio_queue)) {
699 logf("Buffering interrupted");
700 close(fd);
701 return false;
702 }
703
704 if (fill_bytesleft == 0) 686 if (fill_bytesleft == 0)
705 break ; 687 break ;
706 688
@@ -737,7 +719,6 @@ bool audio_load_track(int offset, bool start_play, int peek_offset)
737 if (tracks[track_widx].filerem != 0) { 719 if (tracks[track_widx].filerem != 0) {
738 current_fd = fd; 720 current_fd = fd;
739 logf("Partially buf:%d", tracks[track_widx].available); 721 logf("Partially buf:%d", tracks[track_widx].available);
740 return false;
741 } else { 722 } else {
742 logf("Completely buf."); 723 logf("Completely buf.");
743 close(fd); 724 close(fd);
@@ -750,20 +731,13 @@ bool audio_load_track(int offset, bool start_play, int peek_offset)
750 return true; 731 return true;
751} 732}
752 733
753void audio_insert_tracks(int offset, bool start_playing, int peek_offset)
754{
755 fill_bytesleft = codecbuflen - codecbufused;
756 filling = true;
757 while (audio_load_track(offset, start_playing, peek_offset)) {
758 start_playing = false;
759 offset = 0;
760 peek_offset++;
761 }
762 filling = false;
763}
764
765void audio_play_start(int offset) 734void audio_play_start(int offset)
766{ 735{
736 if (current_fd >= 0) {
737 close(current_fd);
738 current_fd = -1;
739 }
740
767 memset(&tracks, 0, sizeof(struct track_info) * MAX_TRACK); 741 memset(&tracks, 0, sizeof(struct track_info) * MAX_TRACK);
768 sound_set(SOUND_VOLUME, global_settings.volume); 742 sound_set(SOUND_VOLUME, global_settings.volume);
769 track_count = 0; 743 track_count = 0;
@@ -773,9 +747,17 @@ void audio_play_start(int offset)
773 buf_widx = 0; 747 buf_widx = 0;
774 codecbufused = 0; 748 codecbufused = 0;
775 pcm_set_boost_mode(true); 749 pcm_set_boost_mode(true);
776 audio_insert_tracks(offset, true, 0); 750
751 fill_bytesleft = codecbuflen;
752 filling = true;
753 last_peek_offset = 0;
754 if (audio_load_track(offset, true, 0)) {
755 last_peek_offset++;
756 ata_sleep();
757 } else {
758 logf("Failure");
759 }
777 pcm_set_boost_mode(false); 760 pcm_set_boost_mode(false);
778 ata_sleep();
779} 761}
780 762
781void audio_clear_track_entries(void) 763void audio_clear_track_entries(void)
@@ -791,25 +773,14 @@ void audio_clear_track_entries(void)
791 } 773 }
792} 774}
793 775
794void audio_check_buffer(void) 776void initialize_buffer_fill(void)
795{ 777{
796 int i; 778 int cur_idx, i;
797 int cur_idx;
798
799 /* Fill buffer as full as possible for cross-fader. */
800 if (pcm_is_crossfade_enabled() && cur_ti->id3.length > 0
801 && cur_ti->id3.length - cur_ti->id3.elapsed < 20000 && playing)
802 pcm_set_boost_mode(true);
803
804 /* Start buffer filling as necessary. */
805 if (codecbufused > conf_watermark || !queue_empty(&audio_queue)
806 || !playing || ci.stop_codec || ci.reload_codec)
807 return ;
808 779
809 filling = true;
810 pcm_set_boost_mode(true); 780 pcm_set_boost_mode(true);
811 781
812 fill_bytesleft = codecbuflen - codecbufused; 782 fill_bytesleft = codecbuflen - codecbufused;
783 tracks[track_widx].start_pos = ci.curpos;
813 784
814 /* Calculate real track count after throwing away old tracks. */ 785 /* Calculate real track count after throwing away old tracks. */
815 cur_idx = track_ridx; 786 cur_idx = track_ridx;
@@ -827,6 +798,30 @@ void audio_check_buffer(void)
827 798
828 /* Mark all other entries null. */ 799 /* Mark all other entries null. */
829 audio_clear_track_entries(); 800 audio_clear_track_entries();
801}
802
803void audio_check_buffer(void)
804{
805 /* Fill buffer as full as possible for cross-fader. */
806 if (pcm_is_crossfade_enabled() && cur_ti->id3.length > 0
807 && cur_ti->id3.length - cur_ti->id3.elapsed < 20000 && playing)
808 pcm_set_boost_mode(true);
809
810 /* Start buffer filling as necessary. */
811 if ((codecbufused > conf_watermark || !queue_empty(&audio_queue)
812 || !playing || ci.stop_codec || ci.reload_codec) && !filling)
813 return ;
814
815 if (!filling) {
816 initialize_buffer_fill();
817 filling = true;
818 }
819
820 /* Limit buffering size at first run. */
821 if (conf_bufferlimit && (int)fill_bytesleft >= conf_bufferlimit) {
822 fill_bytesleft = conf_bufferlimit;
823 conf_bufferlimit = 0;
824 }
830 825
831 /* Try to load remainings of the file. */ 826 /* Try to load remainings of the file. */
832 if (tracks[track_widx].filerem > 0) 827 if (tracks[track_widx].filerem > 0)
@@ -836,18 +831,17 @@ void audio_check_buffer(void)
836 if (tracks[track_widx].filerem == 0 && tracks[track_widx].filesize != 0) { 831 if (tracks[track_widx].filerem == 0 && tracks[track_widx].filesize != 0) {
837 if (++track_widx == MAX_TRACK) 832 if (++track_widx == MAX_TRACK)
838 track_widx = 0; 833 track_widx = 0;
839 logf("new ti: %d", track_widx);
840 } 834 }
841 835
842 logf("ti: %d", track_widx);
843 /* Load new files to fill the entire buffer. */ 836 /* Load new files to fill the entire buffer. */
844 if (tracks[track_widx].filerem == 0) 837 if (audio_load_track(0, false, last_peek_offset)) {
845 audio_insert_tracks(0, false, 1); 838 last_peek_offset++;
846 839 } else if (tracks[track_widx].filerem == 0 || fill_bytesleft == 0) {
847 pcm_set_boost_mode(false); 840 filling = false;
848 if (playing) 841 pcm_set_boost_mode(false);
849 ata_sleep(); 842 if (playing)
850 filling = false; 843 ata_sleep();
844 }
851} 845}
852 846
853void audio_update_trackinfo(void) 847void audio_update_trackinfo(void)
@@ -890,6 +884,7 @@ void audio_update_trackinfo(void)
890 884
891void audio_change_track(void) 885void audio_change_track(void)
892{ 886{
887 logf("change track");
893 if (track_ridx == track_widx) { 888 if (track_ridx == track_widx) {
894 logf("No more tracks"); 889 logf("No more tracks");
895 while (pcm_is_playing()) 890 while (pcm_is_playing())
@@ -914,6 +909,9 @@ bool codec_request_next_track_callback(void)
914 909
915 /* Advance to next track. */ 910 /* Advance to next track. */
916 if (ci.reload_codec && new_track > 0) { 911 if (ci.reload_codec && new_track > 0) {
912 last_peek_offset--;
913 if (!playlist_check(1))
914 return false;
917 playlist_next(1); 915 playlist_next(1);
918 if (++track_ridx == MAX_TRACK) 916 if (++track_ridx == MAX_TRACK)
919 track_ridx = 0; 917 track_ridx = 0;
@@ -927,6 +925,9 @@ bool codec_request_next_track_callback(void)
927 925
928 /* Advance to previous track. */ 926 /* Advance to previous track. */
929 else if (ci.reload_codec && new_track < 0) { 927 else if (ci.reload_codec && new_track < 0) {
928 last_peek_offset++;
929 if (!playlist_check(-1))
930 return false;
930 playlist_next(-1); 931 playlist_next(-1);
931 if (--track_ridx < 0) 932 if (--track_ridx < 0)
932 track_ridx = MAX_TRACK-1; 933 track_ridx = MAX_TRACK-1;
@@ -942,6 +943,9 @@ bool codec_request_next_track_callback(void)
942 943
943 /* Codec requested track change (next track). */ 944 /* Codec requested track change (next track). */
944 else { 945 else {
946 last_peek_offset--;
947 if (!playlist_check(1))
948 return false;
945 playlist_next(1); 949 playlist_next(1);
946 if (++track_ridx >= MAX_TRACK) 950 if (++track_ridx >= MAX_TRACK)
947 track_ridx = 0; 951 track_ridx = 0;
@@ -992,19 +996,28 @@ void audio_thread(void)
992 struct event ev; 996 struct event ev;
993 997
994 while (1) { 998 while (1) {
999 yield_codecs();
995 audio_check_buffer(); 1000 audio_check_buffer();
996 queue_wait_w_tmo(&audio_queue, &ev, 100); 1001 queue_wait_w_tmo(&audio_queue, &ev, 0);
997 switch (ev.id) { 1002 switch (ev.id) {
998 case AUDIO_PLAY: 1003 case AUDIO_PLAY:
1004 logf("starting...");
999 ci.stop_codec = true; 1005 ci.stop_codec = true;
1000 ci.reload_codec = false; 1006 ci.reload_codec = false;
1001 ci.seek_time = 0; 1007 ci.seek_time = 0;
1002 pcm_play_stop(); 1008 //pcm_play_stop();
1003 audio_play_start((int)ev.data); 1009 audio_play_start((int)ev.data);
1004 break ; 1010 break ;
1005 1011
1006 case AUDIO_STOP: 1012 case AUDIO_STOP:
1007 paused = false; 1013 paused = false;
1014 filling = false;
1015 playing = false;
1016 ci.stop_codec = true;
1017 if (current_fd >= 0) {
1018 close(current_fd);
1019 current_fd = -1;
1020 }
1008 pcm_play_stop(); 1021 pcm_play_stop();
1009 pcm_play_pause(true); 1022 pcm_play_pause(true);
1010 break ; 1023 break ;
@@ -1022,6 +1035,9 @@ void audio_thread(void)
1022 audio_invalidate_tracks(); 1035 audio_invalidate_tracks();
1023 break ; 1036 break ;
1024 1037
1038 case AUDIO_TRACK_CHANGED:
1039 break ;
1040
1025 case AUDIO_CODEC_DONE: 1041 case AUDIO_CODEC_DONE:
1026 //if (playing) 1042 //if (playing)
1027 // audio_change_track(); 1043 // audio_change_track();
@@ -1156,15 +1172,7 @@ void audio_play(int offset)
1156void audio_stop(void) 1172void audio_stop(void)
1157{ 1173{
1158 logf("audio_stop"); 1174 logf("audio_stop");
1159 playing = false;
1160 paused = false;
1161 ci.stop_codec = true;
1162 if (current_fd >= 0) {
1163 close(current_fd);
1164 current_fd = -1;
1165 }
1166 queue_post(&audio_queue, AUDIO_STOP, 0); 1175 queue_post(&audio_queue, AUDIO_STOP, 0);
1167 pcm_play_pause(true);
1168} 1176}
1169 1177
1170void audio_pause(void) 1178void audio_pause(void)
@@ -1183,37 +1191,36 @@ void audio_resume(void)
1183 //queue_post(&audio_queue, AUDIO_RESUME, 0); 1191 //queue_post(&audio_queue, AUDIO_RESUME, 0);
1184} 1192}
1185 1193
1186void audio_next(void) 1194static void initiate_track_change(int peek_index)
1187{ 1195{
1188 logf("audio_next"); 1196 if (!playlist_check(peek_index))
1189 new_track = 1; 1197 return ;
1198
1199 new_track = peek_index;
1190 ci.reload_codec = true; 1200 ci.reload_codec = true;
1191 1201
1192 /* Detect if disk is spinning.. */ 1202 /* Detect if disk is spinning.. */
1193 if (filling) { 1203 if (filling) {
1194 ci.stop_codec = true; 1204 ci.stop_codec = true;
1195 playlist_next(1); 1205 playlist_next(peek_index);
1196 queue_post(&audio_queue, AUDIO_PLAY, 0); 1206 queue_post(&audio_queue, AUDIO_PLAY, 0);
1197 } 1207 }
1198 1208
1199 else if (!pcm_crossfade_start()) { 1209 else if (!pcm_crossfade_start()) {
1200 pcm_play_stop(); 1210 //pcm_play_stop();
1201 } 1211 }
1202} 1212}
1203 1213
1214void audio_next(void)
1215{
1216 logf("audio_next");
1217 initiate_track_change(1);
1218}
1219
1204void audio_prev(void) 1220void audio_prev(void)
1205{ 1221{
1206 logf("audio_prev"); 1222 logf("audio_prev");
1207 new_track = -1; 1223 initiate_track_change(-1);
1208 ci.reload_codec = true;
1209 pcm_play_stop();
1210
1211 if (filling) {
1212 ci.stop_codec = true;
1213 playlist_next(-1);
1214 queue_post(&audio_queue, AUDIO_PLAY, 0);
1215 }
1216 //queue_post(&audio_queue, AUDIO_PREV, 0);
1217} 1224}
1218 1225
1219void audio_ff_rewind(int newpos) 1226void audio_ff_rewind(int newpos)
diff --git a/firmware/pcm_playback.c b/firmware/pcm_playback.c
index 0ab3d0272f..da7f06fa29 100644
--- a/firmware/pcm_playback.c
+++ b/firmware/pcm_playback.c
@@ -27,6 +27,7 @@
27#include "uda1380.h" 27#include "uda1380.h"
28#include "system.h" 28#include "system.h"
29#endif 29#endif
30#include "logf.h"
30 31
31#include <stdio.h> 32#include <stdio.h>
32#include <string.h> 33#include <string.h>
@@ -225,6 +226,7 @@ void pcm_play_stop(void)
225 if (pcm_playing) { 226 if (pcm_playing) {
226 uda1380_enable_output(false); 227 uda1380_enable_output(false);
227 pcm_boost(false); 228 pcm_boost(false);
229 sleep(HZ/16);
228 dma_stop(); 230 dma_stop();
229 } 231 }
230 pcmbuf_unplayed_bytes = 0; 232 pcmbuf_unplayed_bytes = 0;
@@ -384,36 +386,38 @@ bool pcm_is_lowdata(void)
384 386
385bool pcm_crossfade_start(void) 387bool pcm_crossfade_start(void)
386{ 388{
387 //logf("cf:%d", audiobuffer_free / CHUNK_SIZE); 389 if (PCMBUF_SIZE - audiobuffer_free < CHUNK_SIZE * 4 || !crossfade_enabled) {
388 if (audiobuffer_free > CHUNK_SIZE * 4 || !crossfade_enabled) {
389 return false; 390 return false;
390 } 391 }
392 logf("crossfading!");
391 pcm_boost(true); 393 pcm_boost(true);
392 crossfade_active = true; 394 crossfade_active = true;
393 crossfade_pos = audiobuffer_pos; 395 crossfade_pos = audiobuffer_pos;
394 crossfade_amount = (PCMBUF_SIZE - audiobuffer_free - CHUNK_SIZE * 22)/2; 396 crossfade_amount = (PCMBUF_SIZE - audiobuffer_free - (CHUNK_SIZE * 4))/2;
395 crossfade_rem = crossfade_amount; 397 crossfade_rem = crossfade_amount;
396 audiobuffer_fillpos = 0; 398 audiobuffer_fillpos = 0;
397 399
398 crossfade_pos -= crossfade_amount*2; 400 crossfade_pos -= crossfade_amount*2;
399 if (crossfade_pos < 0) 401 if (crossfade_pos < 0)
400 crossfade_pos = PCMBUF_SIZE + crossfade_pos; 402 crossfade_pos += PCMBUF_SIZE;
401 return true; 403 return true;
402} 404}
403 405
404static __inline 406static __inline
405void crossfade(short *buf, const short *buf2, int length) 407void crossfade(short *buf, const short *buf2, int length)
406{ 408{
407 while (length--) { 409 int i, size;
408 *buf = (int)((*buf * ((crossfade_rem)*1000/crossfade_amount))/1000); 410
409 *buf += (int)((*buf2 * ((crossfade_amount-crossfade_rem)*1000/crossfade_amount))/1000); 411 logf("cfi: %d/%d", length, crossfade_rem);
410 buf++; 412 size = MIN(length, crossfade_rem);
411 buf2++; 413 for (i = 0; i < length; i++) {
412 if (--crossfade_rem <= 0) { 414 /* This is not yet realtime, needs optimizations for crossfade to work. */
413 crossfade_active = false; 415 buf[i] = ((buf[i] * ((crossfade_rem)*1000/crossfade_amount))/1000)
414 break ; 416 + ((buf2[i] * ((crossfade_amount-crossfade_rem)*1000/crossfade_amount))/1000);
415 }
416 } 417 }
418
419 if (--crossfade_rem <= 0)
420 crossfade_active = false;
417} 421}
418 422
419bool audiobuffer_insert(char *buf, size_t length) 423bool audiobuffer_insert(char *buf, size_t length)