diff --git a/LICENSE b/LICENSE index 399fcd1..514597b 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,7 @@ MIT License Copyright (c) 2019 Abdullah Atta +Copyright (c) 2024 Karol Zimmer Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 5b51cb9..e69b50c 100644 --- a/README.md +++ b/README.md @@ -10,12 +10,6 @@ A crazy simple library for reading/writing WAV files written in V! ## Installation: -Install using `vpkg` - -```bash -vpkg get https://github.com/thecodrr/vave -``` - Install using `V`'s builtin `vpm` (you will need to import the module with: `import thecodrr.vave` with this method of installation): ```shell @@ -140,6 +134,7 @@ And **[follow](https://github.com/thecodrr)** me for my next creations! 🤩 MIT License Copyright (c) 2019 Abdullah Atta +Copyright (c) 2024 Karol Zimmer Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/constants.v b/constants.v index a8f44c3..ac61b2a 100644 --- a/constants.v +++ b/constants.v @@ -1,19 +1,18 @@ module vave -const ( - WAV_RIFF_CHUNK_ID = 'RIFF'.str - WAV_FORMAT_CHUNK_ID = 'fmt '.str - WAV_FACT_CHUNK_ID = 'fact'.str - WAV_DATA_CHUNK_ID = 'data'.str - WAVE_ID = 'WAVE'.str - WAV_RIFF_HEADER_SIZE = u32(8) - DEFAULT_SUB_FORMAT = [0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71] -) +const wav_riff_chunk_id = 'RIFF'.str +const wav_format_chunk_id = 'fmt '.str +const wav_fact_chunk_id = 'fact'.str +const wav_data_chunk_id = 'data'.str +const wave_id = 'WAVE'.str +const wav_riff_header_size = u32(8) +const default_sub_format = [0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, + 0x9b, 0x71] pub enum Formats { - pcm = 0x0001, - ieee = 0x0003, - alaw = 0x0006, - mulaw = 0x0007, + pcm = 0x0001 + ieee = 0x0003 + alaw = 0x0006 + mulaw = 0x0007 extensible = 0xfffe -} \ No newline at end of file +} diff --git a/header.v b/header.v index 48f1e6a..a6c1e8b 100644 --- a/header.v +++ b/header.v @@ -51,7 +51,7 @@ struct WavDataChunk{ fn (w &WavFile) parse_format_chunk() WavFormatChunk { chunk := WavFormatChunk{} - if !w.parse_chunk_header(&chunk) && !compare(&chunk.id, WAV_FORMAT_CHUNK_ID) { + if !w.parse_chunk_header(&chunk) && !compare(&chunk.id, wav_format_chunk_id) { panic("Couldn't find the format chunk.") } @@ -67,9 +67,13 @@ fn (w &WavFile) parse_format_chunk() WavFormatChunk { panic("This wav file has more than 2 channels but isn't WAV_FORMAT_EXTENSIBLE.") } - if !(Formats(chunk.format_tag) in [.pcm, .ieee, .alaw, .mulaw]) { + if !(unsafe { + Formats(chunk.format_tag) in [.pcm, .ieee, .alaw, .mulaw] + }) { if chunk.ext_size != 0 { - if !(Formats(chunk.sub_format.format_code) in [.pcm, .ieee, .alaw, .mulaw]) { + if !(unsafe { + Formats(chunk.sub_format.format_code) in [.pcm, .ieee, .alaw, .mulaw] + }) { panic("Only PCM, IEEE float and log-PCM log files are accepted.") } } else { @@ -82,7 +86,7 @@ fn (w &WavFile) parse_format_chunk() WavFormatChunk { fn (w &WavFile) parse_master_chunk() WavMasterChunk { chunk := WavMasterChunk{} - if !w.parse_chunk_header(&chunk) || !compare(&chunk.id, WAV_RIFF_CHUNK_ID) || !w.read_u32(&chunk.wave_id) || !compare(&chunk.wave_id, WAVE_ID) { + if !w.parse_chunk_header(&chunk) || !compare(&chunk.id, wav_riff_chunk_id) || !w.read_u32(&chunk.wave_id) || !compare(&chunk.wave_id, wave_id) { panic("Couldn't find the RIFF chunk. This is probably not a WAVE file.") } return chunk @@ -90,22 +94,22 @@ fn (w &WavFile) parse_master_chunk() WavMasterChunk { fn (w &WavFile) parse_fact_chunk() WavFactChunk { chunk := WavFactChunk{} - if w.parse_chunk_header(&chunk) && compare(&chunk.id, WAV_FACT_CHUNK_ID) && w.parse_chunk_body(&chunk, chunk.size) { + if w.parse_chunk_header(&chunk) && compare(&chunk.id, wav_fact_chunk_id) && w.parse_chunk_body(&chunk, chunk.size) { return chunk } return chunk } -fn (w mut WavFile) parse_header() bool { +fn (mut w WavFile) parse_header() bool { w.chunk = w.parse_master_chunk() w.chunk.format_chunk = w.parse_format_chunk() w.chunk.fact_chunk = w.parse_fact_chunk() - if compare(&w.chunk.fact_chunk.id, WAV_DATA_CHUNK_ID) { + if compare(&w.chunk.fact_chunk.id, wav_data_chunk_id) { w.chunk.data_chunk.id = w.chunk.fact_chunk.id w.chunk.data_chunk.size = w.chunk.fact_chunk.size w.chunk.fact_chunk.size = 0 } else { - for !compare(&w.chunk.data_chunk.id, WAV_DATA_CHUNK_ID) && !w.eof() { + for !compare(&w.chunk.data_chunk.id, wav_data_chunk_id) && !w.eof() { w.read_u32(&w.chunk.data_chunk.id) } if w.eof() { @@ -130,7 +134,9 @@ fn (w &WavFile) parse_chunk_body(chunk voidptr, size u32) bool { fn compare(a voidptr, b byteptr) bool { data := byteptr(a) for i in 0..4 { - if byte(data[i]) != b[i] {return false} + if unsafe { byte(data[i]) != b[i] } { + return false + } } return true } \ No newline at end of file diff --git a/io.v b/io.v index ed181af..0ef9b61 100644 --- a/io.v +++ b/io.v @@ -13,7 +13,7 @@ fn (w &WavFile) read_u16(buf voidptr) bool { } fn (w &WavFile) read_into_struct(c voidptr, skip int, size u32) bool { - return w.read_bytes(*byte(c) + skip, int(size)) + return w.read_bytes(unsafe { voidptr(int(c) + skip) }, int(size)) } /* fn (w &WavFile) pos() i64 { @@ -28,5 +28,5 @@ fn (w &WavFile) read_into_struct(c voidptr, skip int, size u32) bool { } */ fn (w &WavFile) eof() bool { - return feof(w.fp) > -1 || ftell(w.fp) == int(w.get_header_size() + w.chunk.data_chunk.size) -} \ No newline at end of file + return C.feof(w.fp) > -1 || C.ftell(w.fp) == int(w.get_header_size() + w.chunk.data_chunk.size) +} diff --git a/reader.v b/reader.v index bec13d9..6834962 100644 --- a/reader.v +++ b/reader.v @@ -1,35 +1,35 @@ module vave -//TODO implement different reading functions like mono, stereo etc. +// TODO implement different reading functions like mono, stereo etc. -//read_sample reads one sample from the audio stream -//it can be used for streaming etc. The returned data -//must be freed manually or it will cause a memory leak. +// read_sample reads one sample from the audio stream +// it can be used for streaming etc. The returned data +// must be freed manually or it will cause a memory leak. pub fn (w &WavFile) read_sample() byteptr { return w.read_samples(1) } -//read_raw reads all the audio data from the audio stream -//the data is put into w.data instead of returning it -//and automatically frees it on w.close() +// read_raw reads all the audio data from the audio stream +// the data is put into w.data instead of returning it +// and automatically frees it on w.close() pub fn (w &WavFile) read_raw() byteptr { return w.read_samples(int(w.total_samples())) } -//read_samples reads multiple samples from the audio stream -//the returned data must be freed manually or it will cause a memory leak. +// read_samples reads multiple samples from the audio stream +// the returned data must be freed manually or it will cause a memory leak. pub fn (w &WavFile) read_samples(count int) byteptr { if w.mode in ['wb', 'wbx', 'ab'] { - panic("File was opened in wrong mode.") + panic('File was opened in wrong mode.') } if w.chunk.format_chunk.format_tag == u16(Formats.extensible) { - println("warn: EXTENSIBLE format is not supported.") + println('warn: EXTENSIBLE format is not supported.') } total_samples := int(w.num_channels()) * count * int(w.sample_size()) - data := malloc(total_samples) + data := unsafe { malloc(total_samples) } C.fread(data, w.sample_size(), int(w.num_channels()) * count, w.fp) - if ferror(w.fp) > 0 { - panic("Failed to read the data chunk.") - } + if w.fp == C.NULL { + panic('Failed to read the data chunk.') + } return data -} \ No newline at end of file +} diff --git a/v.mod b/v.mod index 0decc8c..34ede65 100644 --- a/v.mod +++ b/v.mod @@ -1,5 +1,5 @@ Module { name: 'vave' - version: '0.0.2' + version: '0.1.0' deps: [] } \ No newline at end of file diff --git a/vave.v b/vave.v index e9594dd..d99f76a 100644 --- a/vave.v +++ b/vave.v @@ -1,35 +1,48 @@ module vave -import os +struct C.FILE {} -struct C.FILE -fn C.feof(f &FILE) int +fn C.feof(f &C.FILE) int fn C.ferror() int struct WavFile { - filename string - mut: - chunk WavMasterChunk - fp &C.FILE - mode string + filename string +mut: + chunk WavMasterChunk + fp &C.FILE + mode string } -//open opens a WAV file for read/write in the specified mode -pub fn open(path, mode string) &WavFile { - os.tmpdir() //hack to include os import - +// open opens a WAV file for read/write in the specified mode +pub fn open(path string, mode string) &WavFile { mut wav := &WavFile{ fp: C.NULL } match mode { - "rb", "r" {wav.mode = "rb"} - "r+", "rb+", "r+b" {wav.mode = "rb+"} - "w", "wb" {wav.mode = "wb"} - "w+", "wb+", "w+b" {wav.mode = "wb+"} - "wx", "wbx" {wav.mode = "wbx"} - "w+x", "wb+x", "w+bx" {wav.mode = "wb+x"} - "a", "ab" {wav.mode = "ab"} - "a+","ab+","ab+b" {wav.mode = "ab+"} + 'rb', 'r' { + wav.mode = 'rb' + } + 'r+', 'rb+', 'r+b' { + wav.mode = 'rb+' + } + 'w', 'wb' { + wav.mode = 'wb' + } + 'w+', 'wb+', 'w+b' { + wav.mode = 'wb+' + } + 'wx', 'wbx' { + wav.mode = 'wbx' + } + 'w+x', 'wb+x', 'w+bx' { + wav.mode = 'wb+x' + } + 'a', 'ab' { + wav.mode = 'ab' + } + 'a+', 'ab+', 'ab+b' { + wav.mode = 'ab+' + } else { panic("init: wrong 'mode' given.") } @@ -37,7 +50,7 @@ pub fn open(path, mode string) &WavFile { $if windows { wav.fp = C._wfopen(path.replace('/', '\\').to_wide(), wav.mode.to_wide()) - } $if linux { + } $else { wav.fp = C.fopen(path.replace('\\', '/').str, wav.mode.str) } @@ -55,9 +68,9 @@ pub fn open(path, mode string) &WavFile { return wav } -pub fn (w mut WavFile) close() int { +pub fn (mut w WavFile) close() int { if !w.finalize() { - panic("Failed to close the file.") + panic('Failed to close the file.') } unsafe { free(w) @@ -66,43 +79,43 @@ pub fn (w mut WavFile) close() int { } pub fn (w &WavFile) bytes_per_sample() u16 { - return w.chunk.format_chunk.bits_per_sample / u16(8) + return w.chunk.format_chunk.bits_per_sample / u16(8) } pub fn (w &WavFile) total_samples() u32 { - return w.chunk.data_chunk.size / u32(w.chunk.format_chunk.block_align) + return w.chunk.data_chunk.size / u32(w.chunk.format_chunk.block_align) } pub fn (w &WavFile) sample_rate() u32 { - return w.chunk.format_chunk.sample_rate + return w.chunk.format_chunk.sample_rate } pub fn (w &WavFile) channel_mask() u32 { - return w.chunk.format_chunk.channel_mask + return w.chunk.format_chunk.channel_mask } pub fn (w &WavFile) sub_format() u16 { - return w.chunk.format_chunk.sub_format.format_code + return w.chunk.format_chunk.sub_format.format_code } pub fn (w &WavFile) sample_size() u16 { - return w.chunk.format_chunk.block_align / w.chunk.format_chunk.n_channels + return w.chunk.format_chunk.block_align / w.chunk.format_chunk.n_channels } pub fn (w &WavFile) format() Formats { - return Formats(w.chunk.format_chunk.format_tag) + return unsafe { Formats(w.chunk.format_chunk.format_tag) } } pub fn (w &WavFile) num_channels() u16 { - return w.chunk.format_chunk.n_channels + return w.chunk.format_chunk.n_channels } pub fn (w &WavFile) valid_bits_per_sample() u16 { - if w.chunk.format_chunk.format_tag != u16(Formats.extensible) { - return w.chunk.format_chunk.bits_per_sample - } else { - return w.chunk.format_chunk.valid_bits_per_sample - } + if w.chunk.format_chunk.format_tag != u16(Formats.extensible) { + return w.chunk.format_chunk.bits_per_sample + } else { + return w.chunk.format_chunk.valid_bits_per_sample + } } pub fn (w &WavFile) duration() u32 { @@ -115,15 +128,15 @@ pub fn (w &WavFile) data_len() int { // Private -fn (w mut WavFile) finalize () bool { - if w.fp == C.NULL { - return false - } +fn (mut w WavFile) finalize() bool { + if w.fp == C.NULL { + return false + } - ret := C.fclose(w.fp) - if (ret != 0) { - panic("Couldn't close the file properly.") - } + ret := C.fclose(w.fp) + if ret != 0 { + panic("Couldn't close the file properly.") + } return true } @@ -133,13 +146,12 @@ fn (w mut WavFile) finalize () bool { } */ fn (w &WavFile) get_header_size() u32 { - mut header_size := WAV_RIFF_HEADER_SIZE + u32(4) + - WAV_RIFF_HEADER_SIZE + w.chunk.format_chunk.size + - WAV_RIFF_HEADER_SIZE + mut header_size := wav_riff_header_size + u32(4) + wav_riff_header_size + + w.chunk.format_chunk.size + wav_riff_header_size - if compare(&w.chunk.fact_chunk.id, WAV_FACT_CHUNK_ID) { - header_size += WAV_RIFF_HEADER_SIZE + w.chunk.fact_chunk.size - } + if compare(&w.chunk.fact_chunk.id, wav_fact_chunk_id) { + header_size += wav_riff_header_size + w.chunk.fact_chunk.size + } - return header_size -} \ No newline at end of file + return header_size +} diff --git a/vpkg.json b/vpkg.json deleted file mode 100644 index 804fa82..0000000 --- a/vpkg.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "name": "vave", - "version": "0.0.2", - "author": ["thecodrr "], - "repo": "https://github.com/thecodrr/vave", - "sources": ["https://v-pkg.github.io/registry/"], - "dependencies": [] -}