//------------------------------------------------- // read_flac_sample - read a FLAC file as a sample //------------------------------------------------- static bool read_flac_sample(emu_file file, sample_t sample) { // seek back to the start of the file file.seek(0, SEEK_SET); // create the FLAC decoder and fill in the sample data flac_decoder decoder = new flac_decoder(file.core_file_get()); sample.frequency = decoder.sample_rate(); // error if more than 1 channel or not 16bpp if (decoder.channels() != 1) { return(false); } if (decoder.bits_per_sample() != 16) { return(false); } // resize the array and read sample.data.resize(decoder.total_samples()); if (!decoder.decode_interleaved(sample.data, (uint32_t)sample.data.Count)) { return(false); } // finish up and clean up decoder.finish(); return(true); }
// dynamic control //void set_frequency(UINT8 channel, UINT32 frequency); //void set_volume(UINT8 channel, float volume); // helpers //------------------------------------------------- // read_sample - read a WAV or FLAC file as a // sample //------------------------------------------------- static bool read_sample(emu_file file, sample_t sample) { // read the core header and make sure it's a proper file MemoryU8 buf = new MemoryU8(4, true); //uint8_t buf[4]; uint32_t offset = file.read(new Pointer <uint8_t>(buf), 4); if (offset < 4) { osd_printf_warning("Unable to read {0}, 0-byte file?\n", file.filename()); return(false); } // look for the appropriate RIFF tag if (buf[0] == 'R' && buf[1] == 'I' && buf[2] == 'F' && buf[3] == 'F') // memcmp(&buf[0], "RIFF", 4) == 0) { return(read_wav_sample(file, sample)); } else if (buf[0] == 'f' && buf[1] == 'L' && buf[2] == 'a' && buf[3] == 'C') // memcmp(&buf[0], "fLaC", 4) == 0) { return(read_flac_sample(file, sample)); } // if nothing appropriate, emit a warning osd_printf_warning("Unable to read {0}, corrupt file?\n", file.filename()); return(false); }
//------------------------------------------------- // device_post_load - handle updating after a // restore //------------------------------------------------- protected override void device_post_load() { // loop over channels for (int channel = 0; channel < m_channels; channel++) { // attach any samples that were loaded and playing channel_t chan = m_channel[channel]; if (chan.source_num >= 0 && chan.source_num < m_sample.Count) { sample_t sample = m_sample[chan.source_num]; chan.source = sample.data; chan.source_length = sample.data.Count; if (sample.data.Count == 0) { chan.source_num = -1; } } // validate the position against the length in case the sample is smaller if (chan.source != null && chan.pos >= chan.source_length) { if (chan.loop) { chan.pos %= (UInt32)chan.source_length; } else { chan.source = null; chan.source_num = -1; } } } }
//------------------------------------------------- // device_post_load - handle updating after a // restore //------------------------------------------------- protected override void device_post_load() { // loop over channels for (int channel = 0; channel < m_channels; channel++) { // attach any samples that were loaded and playing channel_t chan = m_channel[channel]; if (chan.source_num >= 0 && chan.source_num < m_sample.Count) { sample_t sample = m_sample[chan.source_num]; chan.source = new Pointer <int16_t>(sample.data); //chan.source = &sample.data[0]; chan.source_len = (u32)sample.data.size(); if (sample.data.Count == 0) { chan.source_num = -1; } } // validate the position against the length in case the sample is smaller double endpos = chan.source_len; if (chan.source != null && chan.pos >= endpos) { if (chan.loop) { double posfloor = std.floor(chan.pos); chan.pos -= posfloor; chan.pos += (double)((int32_t)posfloor % chan.source_len); } else { chan.source = null; chan.source_num = -1; } } } }
// internal helpers //------------------------------------------------- // read_wav_sample - read a WAV file as a sample //------------------------------------------------- static bool read_wav_sample(emu_file file, sample_t sample) { // we already read the opening 'RIFF' tag uint32_t offset = 4; // get the total size uint32_t filesize; MemoryU8 filesizeBuffer = new MemoryU8(4, true); offset += file.read(new Pointer <uint8_t>(filesizeBuffer), 4); if (offset < 8) { osd_printf_warning("Unexpected size offset {0} ({1})\n", offset, file.filename()); return(false); } filesize = filesizeBuffer.GetUInt32(); filesize = little_endianize_int32(filesize); // read the RIFF file type and make sure it's a WAVE file MemoryU8 buf = new MemoryU8(32, true); //char [] buf = new char[32]; offset += file.read(new Pointer <uint8_t>(buf), 4); if (offset < 12) { osd_printf_warning("Unexpected WAVE offset {0} ({1})\n", offset, file.filename()); return(false); } if (!(buf[0] == 'W' && buf[1] == 'A' && buf[2] == 'V' && buf[3] == 'E')) // memcmp(&buf[0], "WAVE", 4) != 0) { osd_printf_warning("Could not find WAVE header ({0})\n", file.filename()); return(false); } // seek until we find a format tag uint32_t length; MemoryU8 lengthBuffer = new MemoryU8(4, true); while (true) { offset += file.read(new Pointer <uint8_t>(buf), 4); offset += file.read(new Pointer <uint8_t>(lengthBuffer), 4); length = lengthBuffer.GetUInt32(); length = little_endianize_int32(length); if (buf[0] == 'f' && buf[1] == 'm' && buf[2] == 't' && buf[3] == ' ') //if (memcmp(&buf[0], "fmt ", 4) == 0) { break; } // seek to the next block file.seek(length, SEEK_CUR); offset += length; if (offset >= filesize) { osd_printf_warning("Could not find fmt tag ({0})\n", file.filename()); return(false); } } // read the format -- make sure it is PCM uint16_t temp16; MemoryU8 temp16Buffer = new MemoryU8(2, true); offset += file.read(new Pointer <uint8_t>(temp16Buffer), 2); temp16 = temp16Buffer.GetUInt16(); temp16 = little_endianize_int16(temp16); if (temp16 != 1) { osd_printf_warning("unsupported format {0} - only PCM is supported ({1})\n", temp16, file.filename()); return(false); } // number of channels -- only mono is supported offset += file.read(new Pointer <uint8_t>(temp16Buffer), 2); temp16 = temp16Buffer.GetUInt16(); temp16 = little_endianize_int16(temp16); if (temp16 != 1) { osd_printf_warning("unsupported number of channels {0} - only mono is supported ({1})\n", temp16, file.filename()); return(false); } // sample rate uint32_t rate; MemoryU8 rateBuffer = new MemoryU8(4, true); offset += file.read(new Pointer <uint8_t>(rateBuffer), 4); rate = rateBuffer.GetUInt32(); rate = little_endianize_int32(rate); // bytes/second and block alignment are ignored offset += file.read(new Pointer <uint8_t>(buf), 6); // bits/sample uint16_t bits; MemoryU8 bitsBuffer = new MemoryU8(2, true); offset += file.read(new Pointer <uint8_t>(bitsBuffer), 2); bits = bitsBuffer.GetUInt16(); bits = little_endianize_int16(bits); if (bits != 8 && bits != 16) { osd_printf_warning("unsupported bits/sample {0} - only 8 and 16 are supported ({1})\n", bits, file.filename()); return(false); } // seek past any extra data file.seek(length - 16, SEEK_CUR); offset += length - 16; // seek until we find a data tag while (true) { offset += file.read(new Pointer <uint8_t>(buf), 4); offset += file.read(new Pointer <uint8_t>(lengthBuffer), 4); length = lengthBuffer.GetUInt32(); length = little_endianize_int32(length); if (buf[0] == 'd' && buf[1] == 'a' && buf[2] == 't' && buf[3] == 'a') //if (memcmp(&buf[0], "data", 4) == 0) { break; } // seek to the next block file.seek(length, SEEK_CUR); offset += length; if (offset >= filesize) { osd_printf_warning("Could not find data tag ({0})\n", file.filename()); return(false); } } // if there was a 0 length data block, we're done if (length == 0) { osd_printf_warning("empty data block ({0})\n", file.filename()); return(false); } // fill in the sample data sample.frequency = rate; // read the data in if (bits == 8) { sample.data.resize(length); MemoryU8 sample_data_8bit = new MemoryU8((int)length, true); file.read(new Pointer <uint8_t>(sample_data_8bit), length); // convert 8-bit data to signed samples Pointer <uint8_t> tempptr = new Pointer <uint8_t>(sample_data_8bit); //uint8_t *tempptr = reinterpret_cast<uint8_t *>(&sample.data[0]); for (int sindex = (int)length - 1; sindex >= 0; sindex--) { sample.data[sindex] = (int16_t)((uint8_t)(tempptr[sindex] ^ 0x80) * 256); } } else { // 16-bit data is fine as-is sample.data.resize(length / 2); MemoryU8 sample_data_8bit = new MemoryU8((int)length, true); file.read(new Pointer <uint8_t>(sample_data_8bit), length); // swap high/low on big-endian systems if (ENDIANNESS_NATIVE != ENDIANNESS_LITTLE) { for (uint32_t sindex = 0; sindex < length / 2; sindex++) { sample.data[sindex] = (int16_t)little_endianize_int16(sample_data_8bit.GetUInt16((int)sindex)); //sample.data[sindex]); } } } return(true); }