int pcmfile_seek_set(PcmFile *pf, uint64_t dest) { FILE *fp = pf->io.fp; int slow_seek = !(pf->seekable); if(pf->seekable) { if(dest <= INT32_MAX) { // destination is within first 2GB if(fseek(fp, (long)dest, SEEK_SET)) return -1; } else { int64_t offset = (int64_t)dest - (int64_t)pf->filepos; if(offset >= INT32_MIN && offset <= INT32_MAX) { // offset is within +/- 2GB of file start if(fseek(fp, (long)offset, SEEK_CUR)) return -1; } else { // absolute offset is more than 2GB if(offset < 0) { fprintf(stderr, "error: backward seeking is limited to 2GB\n"); return -1; } else { fprintf(stderr, "warning: forward seeking more than 2GB will be slow.\n"); } slow_seek = 1; } } byteio_flush(&pf->io); } if(slow_seek) { // do forward-only seek by reading data to temp buffer uint64_t offset; uint8_t buf[1024]; if(dest < pf->filepos) return -1; for(offset = dest - pf->filepos; offset > 1024; offset -= 1024) byteio_read(buf, 1024, &pf->io); byteio_read(buf, offset, &pf->io); } pf->filepos = dest; return 0; }
/** * Reads a 2-byte big-endian word from the input stream */ static inline uint16_t read2be(PcmFile *pf) { uint16_t x; if (byteio_read(&x, 2, &pf->io) != 2) return 0; pf->filepos += 2; return be2me_16(x); }
/** * Reads a 4-byte big-endian word from the input stream */ static inline uint32_t read4be(PcmFile *pf) { uint32_t x; if (byteio_read(&x, 4, &pf->io) != 4) return 0; pf->filepos += 4; return be2me_32(x); }
/** * Reads a 8-byte big-endian word from the input stream */ static inline uint64_t read8be(PcmFile *pf) { uint64_t x; if (byteio_read(&x, 8, &pf->io) != 8) return 0; pf->filepos += 8; return be2me_64(x); }
int pcmfile_read_samples(PcmFile *pf, void *output, int num_samples) { uint8_t *buffer; uint8_t *read_buffer; uint32_t bytes_needed, buffer_size; int nr, i, j, bps, nsmp; // check input and limit number of samples if(pf == NULL || pf->io.fp == NULL || output == NULL || pf->fmt_convert == NULL) { fprintf(stderr, "null input to pcmfile_read_samples()\n"); return -1; } if(pf->block_align <= 0) { fprintf(stderr, "invalid block_align\n"); return -1; } num_samples = MIN(num_samples, PCM_MAX_READ); // calculate number of bytes to read, being careful not to read past // the end of the data chunk bytes_needed = pf->block_align * num_samples; if(!pf->read_to_eof) { if((pf->filepos + bytes_needed) >= (pf->data_start + pf->data_size)) { bytes_needed = (uint32_t)((pf->data_start + pf->data_size) - pf->filepos); num_samples = bytes_needed / pf->block_align; } } if(num_samples <= 0) return 0; // allocate temporary buffer for raw input data bps = pf->block_align / pf->channels; buffer_size = (bps != 3) ? bytes_needed : num_samples * sizeof(int32_t) * pf->channels; buffer = calloc(buffer_size+1, 1); if(!buffer) { fprintf(stderr, "error allocating read buffer\n"); return -1; } read_buffer = buffer + (buffer_size - bytes_needed); // read raw audio samples from input stream into temporary buffer nr = byteio_read(read_buffer, bytes_needed, &pf->io); if (nr <= 0) { free(buffer); return nr; } pf->filepos += nr; nr /= pf->block_align; nsmp = nr * pf->channels; // do any necessary conversion based on source_format and read_format. // also do byte swapping when necessary based on source audio and system // byte orders. switch (bps) { case 2: #ifdef WORDS_BIGENDIAN if(pf->order == PCM_BYTE_ORDER_LE) #else if(pf->order == PCM_BYTE_ORDER_BE) #endif { uint16_t *buf16 = (uint16_t *)buffer; for(i=0; i<nsmp; i++) { buf16[i] = bswap_16(buf16[i]); } } break; case 3: { int32_t *input = (int32_t*)buffer; int unused_bits = 32 - pf->bit_width; int32_t v; for(i=0,j=0; i<nsmp*bps; i+=bps,j++) { #ifdef WORDS_BIGENDIAN if(pf->order == PCM_BYTE_ORDER_LE) #else if(pf->order == PCM_BYTE_ORDER_BE) #endif { v = be2me_32((*(int32_t*)(read_buffer + i)) << 8); } else { v = *(int32_t*)(read_buffer + i); } v <<= unused_bits; // clear unused high bits v >>= unused_bits; // sign extend input[j] = v; } } break; case 4: #ifdef WORDS_BIGENDIAN if(pf->order == PCM_BYTE_ORDER_LE) #else if(pf->order == PCM_BYTE_ORDER_BE) #endif { uint32_t *buf32 = (uint32_t *)buffer; for(i=0; i<nsmp; i++) { buf32[i] = bswap_32(buf32[i]); } } break; default: #ifdef WORDS_BIGENDIAN if(pf->order == PCM_BYTE_ORDER_LE) #else if(pf->order == PCM_BYTE_ORDER_BE) #endif { uint64_t *buf64 = (uint64_t *)buffer; for(i=0; i<nsmp; i++) { buf64[i] = bswap_64(buf64[i]); } } break; } pf->fmt_convert(output, buffer, nsmp); // free temporary buffer free(buffer); return nr; }