int SNDWAV_PrepareWAVParams(WAVContainer_t *wav) { assert(wav); //硬件参数赋值 uint16_t channels = DEFAULT_CHANNELS; uint16_t sample_rate = DEFAULT_SAMPLE_RATE; uint16_t sample_length = DEFAULT_SAMPLE_LENGTH; uint32_t duration_time = DEFAULT_DURATION_TIME; wav->header.magic = WAV_RIFF; wav->header.type = WAV_WAVE; wav->format.magic = WAV_FMT; wav->format.fmt_size = LE_INT(16); wav->format.format = LE_SHORT(WAV_FMT_PCM); wav->chunk.type = WAV_DATA; //自定义 wav->format.channels = LE_SHORT(channels); wav->format.sample_rate = LE_INT(sample_rate); wav->format.sample_length = LE_SHORT(sample_length); wav->format.blocks_align = LE_SHORT(channels * sample_length / 8); wav->format.bytes_p_second = LE_INT((uint16_t)(wav->format.blocks_align) * sample_rate); wav->chunk.length = LE_INT(duration_time * (uint32_t)(wav->format.bytes_p_second)); wav->header.length = LE_INT((uint32_t)(wav->chunk.length) + sizeof(wav->chunk) + sizeof(wav->format) + sizeof(wav->header) - 8); return 0; }
static void end_wave(int fd) { /* only close output */ WaveChunkHeader cd; off64_t length_seek; off64_t filelen; u_int rifflen; length_seek = sizeof(WaveHeader) + sizeof(WaveChunkHeader) + sizeof(WaveFmtBody); cd.type = WAV_DATA; cd.length = fdcount > 0x7fffffff ? LE_INT(0x7fffffff) : LE_INT(fdcount); filelen = fdcount + 2*sizeof(WaveChunkHeader) + sizeof(WaveFmtBody) + 4; rifflen = filelen > 0x7fffffff ? LE_INT(0x7fffffff) : LE_INT(filelen); if (lseek64(fd, 4, SEEK_SET) == 4) write(fd, &rifflen, 4); if (lseek64(fd, length_seek, SEEK_SET) == length_seek) write(fd, &cd, sizeof(WaveChunkHeader)); if (fd != 1) close(fd); }
void SNDWAV_Play(SNDPCMContainer_t *sndpcm, WAVContainer_t *wav, int fd) { int load, ret; off64_t written = 0; off64_t c; off64_t count = LE_INT(wav->chunk.length); load = 0; while (written < count) { /* Must read [chunk_bytes] bytes data enough. */ do { c = count - written; if (c > sndpcm->chunk_bytes) c = sndpcm->chunk_bytes; c -= load; if (c == 0) break; ret = SNDWAV_P_SaveRead(fd, sndpcm->data_buf + load, c); if (ret < 0) { fprintf(stderr, "Error safe_read/n"); exit(-1); } if (ret == 0) break; load += ret; } while ((size_t)load < sndpcm->chunk_bytes); /* Transfer to size frame */ load = load * 8 / sndpcm->bits_per_frame; ret = SNDWAV_WritePcm(sndpcm, load); if (ret != load) break; ret = ret * sndpcm->bits_per_frame / 8; written += ret; load = 0; } }
// test, if it's a .WAV file, > 0 if ok (and set the speed, stereo etc.) // == 0 if not // Value returned is bytes to be discarded. static ssize_t test_wavefile(int fd, u_char *_buffer, size_t size) { WaveHeader *h = (WaveHeader *)_buffer; u_char *buffer = NULL; size_t blimit = 0; WaveFmtBody *f; WaveChunkHeader *c; u_int type, len; if (size < sizeof(WaveHeader)) return -1; if (h->magic != WAV_RIFF || h->type != WAV_WAVE) return -1; if (size > sizeof(WaveHeader)) { check_wavefile_space(buffer, size - sizeof(WaveHeader), blimit); memcpy(buffer, _buffer + sizeof(WaveHeader), size - sizeof(WaveHeader)); } size -= sizeof(WaveHeader); while (1) { check_wavefile_space(buffer, sizeof(WaveChunkHeader), blimit); test_wavefile_read(fd, buffer, &size, sizeof(WaveChunkHeader), __LINE__); c = (WaveChunkHeader*)buffer; type = c->type; len = LE_INT(c->length); len += len % 2; if (size > sizeof(WaveChunkHeader)) memmove(buffer, buffer + sizeof(WaveChunkHeader), size - sizeof(WaveChunkHeader)); size -= sizeof(WaveChunkHeader); if (type == WAV_FMT) break; check_wavefile_space(buffer, len, blimit); test_wavefile_read(fd, buffer, &size, len, __LINE__); if (size > len) memmove(buffer, buffer + len, size - len); size -= len; } if (len < sizeof(WaveFmtBody)) { error(_("unknown length of 'fmt ' chunk (read %u, should be %u at least)"), len, (u_int)sizeof(WaveFmtBody)); exit(EXIT_FAILURE); } check_wavefile_space(buffer, len, blimit); test_wavefile_read(fd, buffer, &size, len, __LINE__); f = (WaveFmtBody*) buffer; if (LE_SHORT(f->format) != WAV_PCM_CODE) { error(_("can't play not PCM-coded WAVE-files")); exit(EXIT_FAILURE); } if (LE_SHORT(f->modus) < 1) { error(_("can't play WAVE-files with %d tracks"), LE_SHORT(f->modus)); exit(EXIT_FAILURE); } hwparams.channels = LE_SHORT(f->modus); switch (LE_SHORT(f->bit_p_spl)) { case 8: if (hwparams.format != DEFAULT_FORMAT && hwparams.format != SND_PCM_FORMAT_U8) fprintf(stderr, _("Warning: format is changed to U8\n")); hwparams.format = SND_PCM_FORMAT_U8; break; case 16: if (hwparams.format != DEFAULT_FORMAT && hwparams.format != SND_PCM_FORMAT_S16_LE) fprintf(stderr, _("Warning: format is changed to S16_LE\n")); hwparams.format = SND_PCM_FORMAT_S16_LE; break; case 24: switch (LE_SHORT(f->byte_p_spl) / hwparams.channels) { case 3: if (hwparams.format != DEFAULT_FORMAT && hwparams.format != SND_PCM_FORMAT_S24_3LE) fprintf(stderr, _("Warning: format is changed to S24_3LE\n")); hwparams.format = SND_PCM_FORMAT_S24_3LE; break; case 4: if (hwparams.format != DEFAULT_FORMAT && hwparams.format != SND_PCM_FORMAT_S24_LE) fprintf(stderr, _("Warning: format is changed to S24_LE\n")); hwparams.format = SND_PCM_FORMAT_S24_LE; break; default: error(_(" can't play WAVE-files with sample %d bits in %d bytes wide (%d channels)"), LE_SHORT(f->bit_p_spl), LE_SHORT(f->byte_p_spl), hwparams.channels); exit(EXIT_FAILURE); } break; case 32: hwparams.format = SND_PCM_FORMAT_S32_LE; break; default: error(_(" can't play WAVE-files with sample %d bits wide"), LE_SHORT(f->bit_p_spl)); exit(EXIT_FAILURE); } hwparams.rate = LE_INT(f->sample_fq); if (size > len) memmove(buffer, buffer + len, size - len); size -= len; while (1) { u_int type, len; check_wavefile_space(buffer, sizeof(WaveChunkHeader), blimit); test_wavefile_read(fd, buffer, &size, sizeof(WaveChunkHeader), __LINE__); c = (WaveChunkHeader*)buffer; type = c->type; len = LE_INT(c->length); if (size > sizeof(WaveChunkHeader)) memmove(buffer, buffer + sizeof(WaveChunkHeader), size - sizeof(WaveChunkHeader)); size -= sizeof(WaveChunkHeader); if (type == WAV_DATA) { if (len < pbrec_count && len < 0x7ffffffe) pbrec_count = len; if (size > 0) memcpy(_buffer, buffer, size); free(buffer); return size; } len += len % 2; check_wavefile_space(buffer, len, blimit); test_wavefile_read(fd, buffer, &size, len, __LINE__); if (size > len) memmove(buffer, buffer + len, size - len); size -= len; } // shouldn't be reached return -1; }
/* write a WAVE-header */ static void begin_wave(int fd, size_t cnt) { WaveHeader h; WaveFmtBody f; WaveChunkHeader cf, cd; int bits; u_int tmp; u_short tmp2; /* WAVE cannot handle greater than 32bit (signed?) int */ if (cnt == (size_t)-2) cnt = 0x7fffff00; bits = 8; switch ((unsigned long) hwparams.format) { case SND_PCM_FORMAT_U8: bits = 8; break; case SND_PCM_FORMAT_S16_LE: bits = 16; break; case SND_PCM_FORMAT_S32_LE: case SND_PCM_FORMAT_FLOAT_LE: bits = 32; break; case SND_PCM_FORMAT_S24_LE: case SND_PCM_FORMAT_S24_3LE: bits = 24; break; default: error(_("Wave doesn't support %s format..."), snd_pcm_format_name(hwparams.format)); exit(EXIT_FAILURE); } h.magic = WAV_RIFF; tmp = cnt + sizeof(WaveHeader) + sizeof(WaveChunkHeader) + sizeof(WaveFmtBody) + sizeof(WaveChunkHeader) - 8; h.length = LE_INT(tmp); h.type = WAV_WAVE; cf.type = WAV_FMT; cf.length = LE_INT(16); if (hwparams.format == SND_PCM_FORMAT_FLOAT_LE) f.format = LE_SHORT(WAV_FMT_IEEE_FLOAT); else f.format = LE_SHORT(WAV_FMT_PCM); f.channels = LE_SHORT(hwparams.channels); f.sample_fq = LE_INT(hwparams.rate); #if 0 tmp2 = (samplesize == 8) ? 1 : 2; f.byte_p_spl = LE_SHORT(tmp2); tmp = dsp_speed * hwparams.channels * (u_int) tmp2; #else tmp2 = hwparams.channels * snd_pcm_format_physical_width(hwparams.format) / 8; f.byte_p_spl = LE_SHORT(tmp2); tmp = (u_int) tmp2 * hwparams.rate; #endif f.byte_p_sec = LE_INT(tmp); f.bit_p_spl = LE_SHORT(bits); cd.type = WAV_DATA; cd.length = LE_INT(cnt); if (write(fd, &h, sizeof(WaveHeader)) != sizeof(WaveHeader) || write(fd, &cf, sizeof(WaveChunkHeader)) != sizeof(WaveChunkHeader) || write(fd, &f, sizeof(WaveFmtBody)) != sizeof(WaveFmtBody) || write(fd, &cd, sizeof(WaveChunkHeader)) != sizeof(WaveChunkHeader)) { error(_("write error")); exit(EXIT_FAILURE); } }
int SNDWAV_SetParams(SNDPCMContainer_t *sndpcm, WAVContainer_t *wav) { snd_pcm_hw_params_t *hwparams; snd_pcm_format_t format; uint32_t exact_rate; uint32_t buffer_time, period_time; /* Allocate the snd_pcm_hw_params_t structure on the stack. */ snd_pcm_hw_params_alloca(&hwparams); /* Init hwparams with full configuration space */ if (snd_pcm_hw_params_any(sndpcm->handle, hwparams) < 0) { fprintf(stderr, "Error snd_pcm_hw_params_any/n"); goto ERR_SET_PARAMS; } if (snd_pcm_hw_params_set_access(sndpcm->handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED) < 0) { fprintf(stderr, "Error snd_pcm_hw_params_set_access\n"); goto ERR_SET_PARAMS; } /* Set sample format */ if (SNDWAV_P_GetFormat(wav, &format) < 0) { fprintf(stderr, "Error get_snd_pcm_format\n"); goto ERR_SET_PARAMS; } if (snd_pcm_hw_params_set_format(sndpcm->handle, hwparams, format) < 0) { fprintf(stderr, "Error snd_pcm_hw_params_set_format\n"); goto ERR_SET_PARAMS; } sndpcm->format = format; /* Set number of channels */ if (snd_pcm_hw_params_set_channels(sndpcm->handle, hwparams, LE_SHORT(wav->format.channels)) < 0) { fprintf(stderr, "Error snd_pcm_hw_params_set_channels\n"); goto ERR_SET_PARAMS; } sndpcm->channels = LE_SHORT(wav->format.channels); /* Set sample rate. If the exact rate is not supported */ /* by the hardware, use nearest possible rate. */ exact_rate = LE_INT(wav->format.sample_rate); if (snd_pcm_hw_params_set_rate_near(sndpcm->handle, hwparams, &exact_rate, 0) < 0) { fprintf(stderr, "Error snd_pcm_hw_params_set_rate_near\n"); goto ERR_SET_PARAMS; } if (LE_INT(wav->format.sample_rate) != exact_rate) { fprintf(stderr, "The rate %d Hz is not supported by your hardware.\n ==> Using %d Hz instead.\n", LE_INT(wav->format.sample_rate), exact_rate); } if (snd_pcm_hw_params_get_buffer_time_max(hwparams, &buffer_time, 0) < 0) { fprintf(stderr, "Error snd_pcm_hw_params_get_buffer_time_max\n"); goto ERR_SET_PARAMS; } if (buffer_time > 500000) buffer_time = 500000; period_time = buffer_time / 4; if (snd_pcm_hw_params_set_buffer_time_near(sndpcm->handle, hwparams, &buffer_time, 0) < 0) { fprintf(stderr, "Error snd_pcm_hw_params_set_buffer_time_near\n"); goto ERR_SET_PARAMS; } if (snd_pcm_hw_params_set_period_time_near(sndpcm->handle, hwparams, &period_time, 0) < 0) { fprintf(stderr, "Error snd_pcm_hw_params_set_period_time_near\n"); goto ERR_SET_PARAMS; } /* Set hw params */ if (snd_pcm_hw_params(sndpcm->handle, hwparams) < 0) { fprintf(stderr, "Error snd_pcm_hw_params(handle, params)\n"); goto ERR_SET_PARAMS; } snd_pcm_hw_params_get_period_size(hwparams, &sndpcm->chunk_size, 0); snd_pcm_hw_params_get_buffer_size(hwparams, &sndpcm->buffer_size); if (sndpcm->chunk_size == sndpcm->buffer_size) { fprintf(stderr, ("Can't use period equal to buffer size (%lu == %lu)\n"), sndpcm->chunk_size, sndpcm->buffer_size); goto ERR_SET_PARAMS; } sndpcm->bits_per_sample = snd_pcm_format_physical_width(format); sndpcm->bits_per_frame = sndpcm->bits_per_sample * LE_SHORT(wav->format.channels); sndpcm->chunk_bytes = sndpcm->chunk_size * sndpcm->bits_per_frame / 8; /* Allocate audio data buffer */ sndpcm->data_buf = (uint8_t *)malloc(sndpcm->chunk_bytes); if (!sndpcm->data_buf) { fprintf(stderr, "Error malloc: [data_buf]\n"); goto ERR_SET_PARAMS; } return 0; ERR_SET_PARAMS: return -1; }