mirror of
https://github.com/m5stack/M5Stack_MicroPython.git
synced 2026-05-20 10:14:44 -07:00
added i2s IN DAC support
This commit is contained in:
@@ -0,0 +1,303 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2018 M5Stack (https://github.com/m5stack)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "py/obj.h"
|
||||
#include "driver/i2s.h"
|
||||
// #include "machine_i2s.h"
|
||||
#include "py/runtime.h"
|
||||
#include "py/mperrno.h"
|
||||
|
||||
typedef enum {
|
||||
I2S, I2S_MERUS, DAC_BUILT_IN, PDM
|
||||
} output_mode_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
mp_obj_base_t base;
|
||||
i2s_port_t port;
|
||||
output_mode_t output_mode;
|
||||
i2s_config_t config;
|
||||
i2s_channel_t num_channels;
|
||||
uint8_t volume;
|
||||
} machine_i2s_obj_t;
|
||||
|
||||
extern const mp_obj_type_t machine_i2s_type;
|
||||
|
||||
|
||||
STATIC void machine_i2s_obj_init_helper(machine_i2s_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
enum { ARG_mode, ARG_bclk, ARG_ws, ARG_data_out, ARG_data_in, ARG_samplerate, ARG_bitdepth, ARG_channel, ARG_volume};
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_mode, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = DAC_BUILT_IN} },
|
||||
{ MP_QSTR_bclk, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 2} },
|
||||
{ MP_QSTR_ws, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 13} },
|
||||
{ MP_QSTR_data_out, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 5} },
|
||||
{ MP_QSTR_data_in, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} },
|
||||
{ MP_QSTR_samplerate, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 48000} },
|
||||
{ MP_QSTR_bitdepth, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 16} },
|
||||
{ MP_QSTR_channel, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 2} },
|
||||
{ MP_QSTR_volume, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 80} },
|
||||
};
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
||||
output_mode_t mode = args[ARG_mode].u_int;
|
||||
self->output_mode = mode;
|
||||
self->config.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX | (mode == DAC_BUILT_IN ? I2S_MODE_DAC_BUILT_IN : 0));
|
||||
self->config.sample_rate = args[ARG_samplerate].u_int;
|
||||
self->config.bits_per_sample = args[ARG_bitdepth].u_int;
|
||||
self->config.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT;
|
||||
self->config.communication_format = (i2s_comm_format_t)((mode == DAC_BUILT_IN ? 0 : I2S_COMM_FORMAT_I2S) | I2S_COMM_FORMAT_I2S_MSB);
|
||||
self->config.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, //Interrupt level 1
|
||||
self->config.dma_buf_count = 4;
|
||||
self->config.dma_buf_len = 1024;
|
||||
self->config.use_apll = 0;
|
||||
self->num_channels = args[ARG_channel].u_int;
|
||||
self->volume = args[ARG_volume].u_int;
|
||||
|
||||
if (i2s_driver_install(self->port, &self->config, 0, NULL) != ESP_OK) {
|
||||
mp_raise_ValueError("I2S: failed to enable");
|
||||
}
|
||||
|
||||
if (mode == DAC_BUILT_IN) {
|
||||
i2s_set_pin(self->port, NULL);
|
||||
i2s_set_dac_mode(I2S_DAC_CHANNEL_RIGHT_EN);
|
||||
} else {
|
||||
i2s_pin_config_t pin_config = {
|
||||
.bck_io_num = args[ARG_bclk].u_int,
|
||||
.ws_io_num = args[ARG_ws].u_int,
|
||||
.data_out_num = args[ARG_data_out].u_int,
|
||||
.data_in_num = args[ARG_data_in].u_int,
|
||||
};
|
||||
if (i2s_set_pin(self->port, &pin_config) != ESP_OK) {
|
||||
i2s_driver_uninstall(self->port);
|
||||
mp_raise_ValueError("I2S: failed to set pins in");
|
||||
}
|
||||
}
|
||||
|
||||
i2s_set_clk(self->port, self->config.sample_rate, self->config.bits_per_sample, self->num_channels);
|
||||
// i2s_zero_dma_buffer(self->port);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
STATIC mp_obj_t machine_i2s_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
|
||||
mp_int_t port = I2S_NUM_0;
|
||||
if (n_args > 0) {
|
||||
port = mp_obj_get_int(args[0]);
|
||||
n_args--;
|
||||
args++;
|
||||
}
|
||||
|
||||
if (port >= I2S_NUM_MAX || port < I2S_NUM_0) {
|
||||
mp_raise_ValueError("invalid I2S peripheral");
|
||||
}
|
||||
|
||||
// Create I2S object
|
||||
machine_i2s_obj_t *self = m_new_obj(machine_i2s_obj_t);
|
||||
self->base.type = &machine_i2s_type;
|
||||
self->port = port;
|
||||
|
||||
mp_map_t kw_args;
|
||||
mp_map_init_fixed_table(&kw_args, n_kw, args + n_args);
|
||||
machine_i2s_obj_init_helper(self, n_args, args, &kw_args);
|
||||
return (machine_i2s_obj_t*)self; // discard const
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
STATIC mp_obj_t machine_i2s_obj_init(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) {
|
||||
machine_i2s_obj_init_helper(args[0], n_args - 1, args + 1, kw_args);
|
||||
return mp_const_none;
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(machine_i2s_init_obj, 1, machine_i2s_obj_init);
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
STATIC mp_obj_t machine_i2s_obj_deinit(mp_obj_t self_in) {
|
||||
const machine_i2s_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
i2s_driver_uninstall(self->port);
|
||||
return mp_const_none;
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(machine_i2s_deinit_obj, machine_i2s_obj_deinit);
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
STATIC mp_obj_t machine_i2s_obj_sample_rates(mp_obj_t self_in, mp_obj_t rate) {
|
||||
machine_i2s_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
|
||||
self->config.sample_rate = mp_obj_get_int(rate);
|
||||
i2s_set_sample_rates(self->port, (uint32_t)(self->config.sample_rate));
|
||||
|
||||
return mp_const_none;
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_2(machine_i2s_set_sample_rates_obj, machine_i2s_obj_sample_rates);
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
STATIC mp_obj_t machine_i2s_obj_bits_per_sample(mp_obj_t self_in, mp_obj_t bits) {
|
||||
machine_i2s_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
|
||||
self->config.bits_per_sample = (i2s_bits_per_sample_t)mp_obj_get_int(bits);
|
||||
i2s_set_clk(self->port, self->config.sample_rate, self->config.bits_per_sample, self->num_channels);
|
||||
|
||||
return mp_const_none;
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_2(machine_i2s_obj_bits_per_sample_obj, machine_i2s_obj_bits_per_sample);
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
STATIC mp_obj_t machine_i2s_obj_set_channel(mp_obj_t self_in, mp_obj_t channel) {
|
||||
machine_i2s_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
|
||||
self->num_channels = (i2s_channel_t)mp_obj_get_int(channel);
|
||||
i2s_set_clk(self->port, self->config.sample_rate, self->config.bits_per_sample, self->num_channels);
|
||||
|
||||
return mp_const_none;
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_2(machine_i2s_obj_set_channel_obj, machine_i2s_obj_set_channel);
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
STATIC mp_obj_t machine_i2s_obj_set_volume(mp_obj_t self_in, mp_obj_t volume) {
|
||||
machine_i2s_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
|
||||
self->volume = mp_obj_get_int(volume);
|
||||
|
||||
return mp_const_none;
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_2(machine_i2s_obj_set_volume_obj, machine_i2s_obj_set_volume);
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
STATIC mp_obj_t machine_i2s_obj_write(const mp_obj_t self_in, mp_obj_t buf_in) {
|
||||
const machine_i2s_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
|
||||
mp_buffer_info_t bufinfo;
|
||||
mp_get_buffer_raise(buf_in, &bufinfo, MP_BUFFER_READ);
|
||||
|
||||
if (i2s_write_bytes(self->port, bufinfo.buf, bufinfo.len, portMAX_DELAY) == ESP_FAIL) {
|
||||
mp_raise_OSError(MP_EIO);
|
||||
}
|
||||
|
||||
return mp_const_none;
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_2(machine_i2s_write_obj, machine_i2s_obj_write);
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
STATIC mp_obj_t machine_i2s_obj_stream_out(const mp_obj_t self_in, mp_obj_t buf_in) {
|
||||
const machine_i2s_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
|
||||
mp_buffer_info_t bufinfo;
|
||||
mp_get_buffer_raise(buf_in, &bufinfo, MP_BUFFER_READ);
|
||||
|
||||
uint8_t buf_bytes_per_sample = (self->config.bits_per_sample / 8);
|
||||
uint32_t num_samples = bufinfo.len / buf_bytes_per_sample / self->num_channels;
|
||||
|
||||
// pointer to left / right sample position
|
||||
char *ptr_l = bufinfo.buf;
|
||||
char *ptr_r = bufinfo.buf + buf_bytes_per_sample;
|
||||
uint8_t stride = buf_bytes_per_sample * 2;
|
||||
|
||||
if (self->num_channels == (i2s_channel_t)1) {
|
||||
ptr_r = ptr_l;
|
||||
}
|
||||
|
||||
int bytes_pushed = 0;
|
||||
TickType_t max_wait = 20 / portTICK_PERIOD_MS; // portMAX_DELAY = bad idea
|
||||
for (int i = 0; i < num_samples; i++) {
|
||||
|
||||
if(self->output_mode == DAC_BUILT_IN) {
|
||||
// assume 16 bit src bit_depth
|
||||
short left = *(short *) ptr_l;
|
||||
short right = *(short *) ptr_r;
|
||||
left = (int)left * self->volume / 100;
|
||||
right = (int)right * self->volume / 100;
|
||||
|
||||
// The built-in DAC wants unsigned samples, so we shift the range
|
||||
// from -32768-32767 to 0-65535.
|
||||
left = left + 0x8000;
|
||||
right = right + 0x8000;
|
||||
|
||||
uint32_t sample = (uint16_t) left;
|
||||
sample = (sample << 16 & 0xffff0000) | ((uint16_t) right);
|
||||
|
||||
bytes_pushed = i2s_push_sample(self->port, (const char*) &sample, max_wait);
|
||||
} else {
|
||||
|
||||
switch (self->config.bits_per_sample)
|
||||
{
|
||||
case I2S_BITS_PER_SAMPLE_16BIT:
|
||||
; // workaround
|
||||
|
||||
/* low - high / low - high */
|
||||
const char samp32[4] = {ptr_l[0], ptr_l[1], ptr_r[0], ptr_r[1]};
|
||||
|
||||
bytes_pushed = i2s_push_sample(self->port, (const char*) &samp32, max_wait);
|
||||
break;
|
||||
|
||||
case I2S_BITS_PER_SAMPLE_32BIT:
|
||||
; // workaround
|
||||
|
||||
const char samp64[8] = {0, 0, ptr_l[0], ptr_l[1], 0, 0, ptr_r[0], ptr_r[1]};
|
||||
bytes_pushed = i2s_push_sample(self->port, (const char*) &samp64, max_wait);
|
||||
break;
|
||||
|
||||
default:
|
||||
// ESP_LOGE(TAG, "bit depth unsupported: %d", self->config.bits_per_sample);
|
||||
mp_raise_ValueError("8 bit depth unsupported");
|
||||
}
|
||||
}
|
||||
|
||||
// DMA buffer full - retry
|
||||
if (bytes_pushed == 0) {
|
||||
i--;
|
||||
} else {
|
||||
ptr_r += stride;
|
||||
ptr_l += stride;
|
||||
}
|
||||
}
|
||||
|
||||
return mp_const_none;
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_2(machine_i2s_obj_stream_out_obj, machine_i2s_obj_stream_out);
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
STATIC const mp_rom_map_elem_t machine_i2s_locals_dict_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&machine_i2s_init_obj)},
|
||||
{ MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&machine_i2s_deinit_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_sample_rate), MP_ROM_PTR(&machine_i2s_set_sample_rates_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_bits_per_sample), MP_ROM_PTR(&machine_i2s_obj_bits_per_sample_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_channel), MP_ROM_PTR(&machine_i2s_obj_set_channel_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_volume), MP_ROM_PTR(&machine_i2s_obj_set_volume_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_stream_out), MP_ROM_PTR(&machine_i2s_obj_stream_out_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&machine_i2s_write_obj) },
|
||||
|
||||
{ MP_ROM_QSTR(MP_QSTR_MODE_IN_DAC), MP_ROM_INT(DAC_BUILT_IN) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_MODE_I2S), MP_ROM_INT(I2S) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_I2S_NUM_0), MP_ROM_INT(I2S_NUM_0) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_I2S_NUM_1), MP_ROM_INT(I2S_NUM_1) },
|
||||
};
|
||||
|
||||
STATIC MP_DEFINE_CONST_DICT(machine_i2s_locals_dict, machine_i2s_locals_dict_table);
|
||||
|
||||
const mp_obj_type_t machine_i2s_type = {
|
||||
{ &mp_type_type },
|
||||
.name = MP_QSTR_I2S,
|
||||
.make_new = machine_i2s_make_new,
|
||||
.locals_dict = (mp_obj_dict_t*)&machine_i2s_locals_dict,
|
||||
};
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2017 Ayke van Laethem
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef MICROPY_INCLUDED_ESP32_MACHINE_I2S_H
|
||||
#define MICROPY_INCLUDED_ESP32_MACHINE_I2S_H
|
||||
|
||||
#include "py/obj.h"
|
||||
#include "driver/i2s.h"
|
||||
|
||||
typedef struct {
|
||||
mp_obj_base_t base;
|
||||
i2s_port_t port;
|
||||
} machine_i2s_obj_t;
|
||||
|
||||
extern const mp_obj_type_t machine_i2s_type;
|
||||
|
||||
#endif // MICROPY_INCLUDED_ESP32_MACHINE_I2S_H
|
||||
@@ -0,0 +1,168 @@
|
||||
"""Simple class to read IFF chunks.
|
||||
|
||||
An IFF chunk (used in formats such as AIFF, TIFF, RMFF (RealMedia File
|
||||
Format)) has the following structure:
|
||||
|
||||
+----------------+
|
||||
| ID (4 bytes) |
|
||||
+----------------+
|
||||
| size (4 bytes) |
|
||||
+----------------+
|
||||
| data |
|
||||
| ... |
|
||||
+----------------+
|
||||
|
||||
The ID is a 4-byte string which identifies the type of chunk.
|
||||
|
||||
The size field (a 32-bit value, encoded using big-endian byte order)
|
||||
gives the size of the whole chunk, including the 8-byte header.
|
||||
|
||||
Usually an IFF-type file consists of one or more chunks. The proposed
|
||||
usage of the Chunk class defined here is to instantiate an instance at
|
||||
the start of each chunk and read from the instance until it reaches
|
||||
the end, after which a new instance can be instantiated. At the end
|
||||
of the file, creating a new instance will fail with a EOFError
|
||||
exception.
|
||||
|
||||
Usage:
|
||||
while True:
|
||||
try:
|
||||
chunk = Chunk(file)
|
||||
except EOFError:
|
||||
break
|
||||
chunktype = chunk.getname()
|
||||
while True:
|
||||
data = chunk.read(nbytes)
|
||||
if not data:
|
||||
pass
|
||||
# do something with data
|
||||
|
||||
The interface is file-like. The implemented methods are:
|
||||
read, close, seek, tell, isatty.
|
||||
Extra methods are: skip() (called by close, skips to the end of the chunk),
|
||||
getname() (returns the name (ID) of the chunk)
|
||||
|
||||
The __init__ method has one required argument, a file-like object
|
||||
(including a chunk instance), and one optional argument, a flag which
|
||||
specifies whether or not chunks are aligned on 2-byte boundaries. The
|
||||
default is 1, i.e. aligned.
|
||||
"""
|
||||
|
||||
class Chunk:
|
||||
def __init__(self, file, align=True, bigendian=True, inclheader=False):
|
||||
import struct
|
||||
self.closed = False
|
||||
self.align = align # whether to align to word (2-byte) boundaries
|
||||
if bigendian:
|
||||
strflag = '>'
|
||||
else:
|
||||
strflag = '<'
|
||||
self.file = file
|
||||
self.chunkname = file.read(4)
|
||||
if len(self.chunkname) < 4:
|
||||
raise EOFError
|
||||
try:
|
||||
data = file.read(4)
|
||||
self.chunksize = struct.unpack(strflag+'L', data)[0]
|
||||
except:# struct.error:
|
||||
raise EOFError
|
||||
if inclheader:
|
||||
self.chunksize = self.chunksize - 8 # subtract header
|
||||
self.size_read = 0
|
||||
try:
|
||||
self.offset = self.file.tell()
|
||||
except:
|
||||
self.seekable = False
|
||||
else:
|
||||
self.seekable = True
|
||||
|
||||
def getname(self):
|
||||
"""Return the name (ID) of the current chunk."""
|
||||
return self.chunkname
|
||||
|
||||
def getsize(self):
|
||||
"""Return the size of the current chunk."""
|
||||
return self.chunksize
|
||||
|
||||
def close(self):
|
||||
if not self.closed:
|
||||
self.skip()
|
||||
self.closed = True
|
||||
|
||||
def isatty(self):
|
||||
if self.closed:
|
||||
raise ValueError("I/O operation on closed file")
|
||||
return False
|
||||
|
||||
def seek(self, pos, whence=0):
|
||||
"""Seek to specified position into the chunk.
|
||||
Default position is 0 (start of chunk).
|
||||
If the file is not seekable, this will result in an error.
|
||||
"""
|
||||
|
||||
if self.closed:
|
||||
raise ValueError("I/O operation on closed file")
|
||||
if not self.seekable:
|
||||
raise OSError("cannot seek")
|
||||
if whence == 1:
|
||||
pos = pos + self.size_read
|
||||
elif whence == 2:
|
||||
pos = pos + self.chunksize
|
||||
if pos < 0 or pos > self.chunksize:
|
||||
raise RuntimeError
|
||||
self.file.seek(self.offset + pos, 0)
|
||||
self.size_read = pos
|
||||
|
||||
def tell(self):
|
||||
if self.closed:
|
||||
raise ValueError("I/O operation on closed file")
|
||||
return self.size_read
|
||||
|
||||
def read(self, size=-1):
|
||||
"""Read at most size bytes from the chunk.
|
||||
If size is omitted or negative, read until the end
|
||||
of the chunk.
|
||||
"""
|
||||
|
||||
if self.closed:
|
||||
raise ValueError("I/O operation on closed file")
|
||||
if self.size_read >= self.chunksize:
|
||||
return ''
|
||||
if size < 0:
|
||||
size = self.chunksize - self.size_read
|
||||
if size > self.chunksize - self.size_read:
|
||||
size = self.chunksize - self.size_read
|
||||
data = self.file.read(size)
|
||||
self.size_read = self.size_read + len(data)
|
||||
if self.size_read == self.chunksize and \
|
||||
self.align and \
|
||||
(self.chunksize & 1):
|
||||
dummy = self.file.read(1)
|
||||
self.size_read = self.size_read + len(dummy)
|
||||
return data
|
||||
|
||||
def skip(self):
|
||||
"""Skip the rest of the chunk.
|
||||
If you are not interested in the contents of the chunk,
|
||||
this method should be called so that the file points to
|
||||
the start of the next chunk.
|
||||
"""
|
||||
|
||||
if self.closed:
|
||||
raise ValueError("I/O operation on closed file")
|
||||
if self.seekable:
|
||||
try:
|
||||
n = self.chunksize - self.size_read
|
||||
# maybe fix alignment
|
||||
if self.align and (self.chunksize & 1):
|
||||
n = n + 1
|
||||
self.file.seek(n, 1)
|
||||
self.size_read = self.size_read + n
|
||||
return
|
||||
except OSError:
|
||||
pass
|
||||
while self.size_read < self.chunksize:
|
||||
n = min(8192, self.chunksize - self.size_read)
|
||||
dummy = self.read(n)
|
||||
if not dummy:
|
||||
raise EOFError
|
||||
@@ -6,7 +6,7 @@ from utime import ticks_ms
|
||||
import display as lcd
|
||||
import utils
|
||||
|
||||
VERSION = "v0.3.5"
|
||||
VERSION = "v0.3.6"
|
||||
|
||||
_BUTTON_A_PIN = const(39)
|
||||
_BUTTON_B_PIN = const(38)
|
||||
@@ -147,6 +147,10 @@ def fimage(x, y, file):
|
||||
lcd.image(x, y, file, 0, lcd.JPG)
|
||||
|
||||
|
||||
def delay(ms):
|
||||
time.sleep_ms(ms)
|
||||
|
||||
|
||||
# -------- M5Stack ---------
|
||||
|
||||
# Node ID
|
||||
@@ -175,4 +179,4 @@ buttonC = Button(_BUTTON_C_PIN)
|
||||
|
||||
|
||||
# SPEAKER
|
||||
speaker = Speaker()
|
||||
# speaker = Speaker()
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user