#ifndef DEPENDENCY_AUDIO_DECODE // Stubs: //(none required) #else # define DEPENDENCY_AUDIO_DECODE_OGG # define DEPENDENCY_AUDIO_DECODE_MP3 # define DEPENDENCY_AUDIO_DECODE_WAV # ifdef QB64_BACKSLASH_FILESYSTEM # ifdef DEPENDENCY_AUDIO_DECODE_MP3 # include "mp3_mini\\src.c" # endif # ifdef DEPENDENCY_AUDIO_DECODE_WAV # include "wav\\src.c" # endif # ifdef DEPENDENCY_AUDIO_DECODE_OGG # include "ogg\\src.c" # endif # else # ifdef DEPENDENCY_AUDIO_DECODE_MP3 # include "mp3_mini/src.c" # endif # ifdef DEPENDENCY_AUDIO_DECODE_WAV # include "wav/src.c" # endif # ifdef DEPENDENCY_AUDIO_DECODE_OGG # include "ogg/src.c" # endif # endif # include // forward refs: void sub__sndvol(int32 handle, float volume); void sub__sndclose(int32 handle); int32 func__sndopen(qbs *filename, qbs *requirements, int32 passed) { sndsetup(); if (new_error) return 0; static qbs *s1 = NULL; if (!s1) s1 = qbs_new(0, 0); qbs_set(s1, qbs_add(filename, qbs_new_txt_len("\0", 1))); // s1=filename+CHR$(0) // load file if (s1->len == 1) return 0; // return invalid handle if null length string static int32 fh, result; static int64 lof; fh = gfs_open(s1, 1, 0, 0); if (fh < 0) return 0; lof = gfs_lof(fh); static uint8 *content; content = (uint8 *)malloc(lof); if (!content) { gfs_close(fh); return 0; } result = gfs_read(fh, -1, content, lof); gfs_close(fh); if (result < 0) { free(content); return 0; } // identify file format static snd_sequence_struct *seq; // OGG? # ifdef DEPENDENCY_AUDIO_DECODE_OGG if (lof >= 3) { if (content[0] == 79) { if (content[1] == 103) { if (content[2] == 103) { //"Ogg" seq = snd_decode_ogg(content, lof); goto got_seq; } } } } // 3 # endif // WAV? # ifdef DEPENDENCY_AUDIO_DECODE_WAV if (lof >= 12) { if ((*(uint32 *)&content[8]) == 0x45564157) { // WAVE seq = snd_decode_wav(content, lof); goto got_seq; } // WAVE } # endif // assume mp3! // MP3? # ifdef DEPENDENCY_AUDIO_DECODE_MP3 seq = snd_decode_mp3(content, lof); # endif got_seq: free(content); if (seq == NULL) return 0; // convert sequence (includes sample rate conversion etc etc) // just perform sample_rate fix for now... // 1. 8->16bit conversion and/or edian conversion static int32 incorrect_format; incorrect_format = 0; if (seq->bits_per_sample != 16) incorrect_format = 1; if (seq->is_unsigned) incorrect_format = 1; // todo... if (seq->endian==???) // this section does not fix the frequency, only the bits per sample // and signed-ness of the data if (incorrect_format) { static int32 bps; bps = seq->bits_per_sample / 8; static int32 samples; samples = seq->data_size / bps; static uint8 *new_data; if (bps != 2) { new_data = (uint8 *)malloc(samples * 2); } else { new_data = (uint8 *)seq->data; } static int32 i, v; for (i = 0; i < samples; i++) { // read original value v = 0; if (bps == 1) { if (seq->is_unsigned) { v = *(uint8 *)(seq->data + i * 1); v = (v - 128) * 256; } else { v = *(int8 *)(seq->data + i * 1); v = v * 128; } } if (bps == 2) { if (seq->is_unsigned) { v = *(uint16 *)(seq->data + i * 2); v = v - 32768; } else { v = *(int16 *)(seq->data + i * 2); } } // place new value into array ((int16 *)new_data)[i] = v; } // i if (bps != 2) { free(seq->data); seq->data = (uint16 *)new_data; seq->data_size = samples * 2; } // update seq info seq->bits_per_sample = 16; seq->is_unsigned = 0; } // incorrect format // 2. samplerate conversion if (seq->sample_rate != snd_frequency) { // need to resample seq->data // create new resampler SpeexResamplerState *state; state = speex_resampler_init(seq->channels, seq->sample_rate, snd_frequency, SPEEX_RESAMPLER_QUALITY_MIN, NULL); if (!state) { // NULL means failure free(seq->data); return 0; } // allocate new memory for output int32 out_samples_max = ((double)seq->data_size / seq->channels / 2) * ((((double)snd_frequency) / ((double)seq->sample_rate)) + 0.1) + 100; // 10%+100 extra samples as a buffer-zone int16 *resampled = (int16 *)malloc(out_samples_max * seq->channels * sizeof(int16)); if (!resampled) { free(seq->data); return 0; } // establish data sizes // in_len will be set by the resampler to number of samples processed spx_uint32_t in_len = seq->data_size / seq->channels / 2; // divide by 2 because 2byte samples, divide by #channels because function wants it per-channel // out_len will be set to the number of samples written spx_uint32_t out_len = out_samples_max * seq->channels * sizeof(int16); // resample! if (speex_resampler_process_interleaved_int(state, (spx_int16_t *)seq->data, &in_len, (spx_int16_t *)resampled, &out_len) != RESAMPLER_ERR_SUCCESS) { // Error free(resampled); free(seq->data); speex_resampler_destroy(state); return 0; } // destroy the resampler anyway speex_resampler_destroy(state); // establish real size of new data and update seq free(seq->data); // That was the old data seq->data_size = out_len * seq->channels * 2; // remember out_len is perchannel, and each sample is 2 bytes seq->data = (uint16_t *)realloc(resampled, seq->data_size); // we overestimated the array size before, so make it the correct size now if (!seq->data) { // realloc could fail free(resampled); return 0; } seq->sample_rate = snd_frequency; } // Unpack stereo data into separate left/right buffers if (seq->channels == 1) { seq->channels = 1; seq->data_left = seq->data; seq->data_left_size = seq->data_size; seq->data_right = NULL; seq->data_right_size = 0; } else if (seq->channels == 2) { seq->data_left_size = seq->data_right_size = seq->data_size / 2; seq->data_left = (uint16_t *)malloc(seq->data_size / 2); if (!seq->data_left) { free(seq->data); return 0; } seq->data_right = (uint16_t *)malloc(seq->data_size / 2); if (!seq->data_right) { free(seq->data_left); free(seq->data); return 0; } for (int sample = 0; sample < seq->data_size / 4; sample++) { seq->data_left[sample] = seq->data[sample * 2]; seq->data_right[sample] = seq->data[sample * 2 + 1]; } free(seq->data); seq->data = NULL; } else { free(seq->data); return 0; } // attach sequence to handle (& inc. refs) // create snd handle static int32 handle; handle = list_add(snd_handles); static snd_struct *snd; snd = (snd_struct *)list_get(snd_handles, handle); snd->internal = 0; snd->type = 2; snd->seq = seq; snd->volume = 1.0; if (seq->channels == 1) { snd->bal_left_x = snd->bal_left_y = snd->bal_left_z = 0; } else if (seq->channels == 2) { snd->bal_left_x = -0.01; snd->bal_left_y = snd->bal_left_z = 0; snd->bal_right_x = 0.01; snd->bal_right_y = snd->bal_right_z = 0; } snd->bal_update = 1; sndupdate(snd); return handle; } mem_block func__memsound(int32 i, int32 targetChannel) { static mem_block b; if (new_error) goto error; if (i <= 0) goto error; sndsetup(); static snd_struct *sn; sn = (snd_struct *)list_get(snd_handles, i); if (!sn) { goto error; } if (!snd_allow_internal) { if (sn->internal) { goto error; } } if (targetChannel < 1 || targetChannel > sn->seq->channels) goto error; if (sn->lock_id) { b.lock_offset = (ptrszint)sn->lock_offset; b.lock_id = sn->lock_id; // get existing tag } else { new_mem_lock(); mem_lock_tmp->type = 5; // sound b.lock_offset = (ptrszint)mem_lock_tmp; b.lock_id = mem_lock_id; sn->lock_offset = (void *)mem_lock_tmp; sn->lock_id = mem_lock_id; // create tag } if (targetChannel == 1) { b.offset = (ptrszint)sn->seq->data_left; b.size = sn->seq->data_left_size; } if (targetChannel == 2) { b.offset = (ptrszint)sn->seq->data_right; b.size = sn->seq->data_right_size; } b.type = 0; // sn->bytes_per_pixel+128+1024+2048;//integer+unsigned+pixeltype b.elementsize = sn->seq->bits_per_sample / 8; b.sound = i; return b; error: b.offset = 0; b.size = 0; b.lock_offset = (ptrszint)mem_lock_base; b.lock_id = 1073741821; // set invalid lock b.type = 0; b.elementsize = 0; b.sound = 0; return b; } void sub__sndplayfile(qbs *filename, int32 sync, double volume, int32 passed) { if (new_error) return; sndsetup(); int32 handle; handle = func__sndopen(filename, NULL, 0); if (!handle) return; if (passed & 2) { sub__sndvol(handle, volume); } sub__sndplay(handle); sub__sndclose(handle); } #endif